Help: Moving a func_train via an array of vectors

Discuss programming in the QuakeC language.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Help: Moving a func_train via an array of vectors

Post by OneManClan »

SHORT VERSION:
Q: Is there some inherent conflict with replacing the normal navigation system func_trains use (the interconnected path_corner entities) with an array of vectors (where we use the index to decide which vector the func_train follows)?

thanks,

OneManClan
====================

LONG VERSION:
Dear Gurus of QuakeC,

I’m on to v2 of the TF2 style 'payload cart’, this time using an array of vectors to provide the waypoints, rather than the normal set of path_corner entities, BUT.. I've have gotten stuck. I'm not sure if the fault lies in my code, or in some quirk of QuakeC. Sorry bout the longish post, but I couldn't condense further without leaving out a potentially crucial detail.

OBJECTIVE
To control the movement of the func_cart (a func_train) by using an array of vectors, which are loaded from a .txt file when the map begins. [EDIT: This is actually an 'implementation' idea, NOT the 'objective'. The actual 'objective' is to make a TF2 style payload 'cart': When blue touches it it moves forward along a fixed path, if they don't it stops, and (after a few seconds) it goes backwards. I'm happy to scrap this implementation if someone can suggest a better method.]

HISTORY
I did get a previous version of this 'reversible cart' working using multiple path_corner entities, but was advised that using an array make more sense, and which is supported by FTE (which I'm using).

PROBLEM
Lots of little weird things including: The func_cart insists on starting at [7], and seems to constantly skip array vector [0]. Strategic dprints also show that the vectors seem to be changing value, ie ones are displayed are not part of the set, and new ones are being generated (?). The cart does stick to the basic path though, except for not going to position[0]. Hopefully the following description of my method will reveal something basic I've overlooked:

CURRENT ATTEMPT

1. We make a .txt file w the vectors the func_cart will follow(agrpl.txt):

Code: Select all

-383 383 200
-383 383 40
-383 -384 40
447 -384 40
448 383 40
0 384 40
1 -1 40

[EDIT: I just realised that I've been assuming that the cart has been starting at [7], but [7] is very close to '0 0 0', so its possible that [0] is not being read, and that the cart is positioning itself at '0 0 0', will test asap]


2. We remove all the waypoint entities from the original map via an ent edit, leaving only the cart and the first target. The map simply does NOT load if the first target isn’t there. [Theory 1: this is the problem]. An excerpt from agrpl.ent:

Code: Select all

{
"model" "*1"
"sounds" "1"
"dmg" "0"
"target" "point1"
"targetname" "train"
"classname" "func_cart"
}
{
"origin" "-383 383 200"
"target" "point2"
"targetname" "point1"
"classname" "path_corner"
}

3. In the QuakeC, we make some new variables:

Code: Select all

.vector current_target; // where the cart is going
.float array_index; // keeps track of which array element the cart is at
.float final_array_element; // keeps track of the number of elements in the array, and also detects the final element
4. We load an array of vectors:

Code: Select all

/*************************************************
fills the global vector array 'cart_target'
returns the number of elements.
************************************************/

#define MAX_WAYPOINTS 20
vector cart_target[MAX_WAYPOINTS];

float () WaypointLoad =
{

local float	file, i;
local string str;


	// mapname is a global
	str = strcat(mapname,".txt");
	
	file = fopen(str, FILE_READ);
	
	if(file < 0)
		{
			dprint("Problem opening ");
			dprint(str);
			dprint("\n");
			return; 
		}
	
	
	for( i = 0; str; i++)
		{
			str = fgets(file);
			
			if (str == "") break;
			
			cart_target[i] = stov(str);
			
			// Note: this DOES print the correct vectors!
			dprint("WaypointLoad loading vector ");
			dprint(ftos(i));
			dprint(": ");
			dprint(vtos(cart_target[i]));
			dprint("\n");
	
		}
	
	fclose(file);
	
	// return the number of elements in the array
	return i;

};

5. We setup the func_cart via:

Code: Select all

/*************************************************
********** 1. defining the cart entity
************************************************/
void() func_cart =
{ 

   //self is the cart
   self.speed = CARTSPEED; // the carts speed
      
   if (!self.target)
      {
         objerror ("func_cart without a target");
      }
     
     
   
   self.solid = SOLID_BSP;
   self.movetype = #MOVETYPE_PUSH;
   self.blocked = fc_blocked;  // defined elsewhere
   self.touch = fc_touch;  // defined elsewhere
   self.classname = "cart";
   // self.impulse gets set to (time+10) every time Blue touches the cart
   // if (self.impulse is > time) ... the cart moves
   self.impulse = time + DELAY_BEFORE_GAME_BEGINS;
   
   // What the cart looks like
   setmodel( self, self.model );
   setsize( self, self.mins, self.maxs );
   
      
   // we load the waypoints, and .final_array_element is the number of waypoints
   self.final_array_element = WaypointLoad();
   
   // array_index stores the current vector index
   self.array_index = 0;
   
   // .current_target stores the current vector
   self.current_target = cart_target[self.array_index];
         
   //  appear at target 1
   //  The following doesn't work, the cart does NOT appear at
   //  cart_target[0] (which is '-383 383 200')
   //  It appears at a point 1/2 way between [0], and [1]
   //  Possibly there's a conflict w what's in the .ent

   setorigin( self, self.current_target );
   
   
   self.nextthink = self.ltime + DELAY_BEFORE_GAME_BEGINS;
   self.think = fc_next;

};
5. Where self.current_target loads the next vector in the array, which it does, but it skips over 0.

Code: Select all

/*************************************************
**********3. finds next path_corner and moves cart
*************************************************/
void() fc_next =
{
// this dprint reveals that the vector values are constantly increasing(?)
dprint("\n fc_next we are targeting: self.origin -self.mins: ");      
dprint(vtos(self.origin - self.mins));      

// this dprint reveals that the indexes [i]are[/i] all traversed (ie 'the nodes are walked') but when it gets to (and displays that we're at)[0], the cart (almost)instantly skips to [1] 
dprint(", current array_index: ");      
dprint(ftos(self.array_index));   
dprint("\n");      


   
   // self is the cart
   self.speed = CARTSPEED; // restore speed in case Red blocked it
   

         if(self.array_index > self.final_array_element)
            {
               self.array_index = 0;
               self.current_target = cart_target[self.array_index];
            }
         else
            {
               self.array_index = self.array_index + 1;
               self.current_target = cart_target[self.array_index];
            }            
            
            if(!self.target )
               {
                  objerror( "cart_next: no next target" );
               }
          
            
                  SUB_CalcMove( self.current_target - self.mins, self.speed, fc_next );

};

I've got the cart to loop through the whole path, BUT a bunch of weird things are happening that are hard to describe:

1. I put some dprints at the start of fc_next, and Also, every time the actual .origin vector of the cart seems to keep changing, I can't see a pattern, and the path seems the same.

2. The cart does not go through [0].

I could post more details, including results of tests Ive done, but to keep this is simple as possible, could someone please confirm:

Q1: Is there some error I've made?
Q2: Is this method is somehow fundamentally flawed?
Q3: Is there some conflict in the method I'm using, and the basic definition/concept of a func_train?


thanks,


OneManClan
Last edited by OneManClan on Sat Aug 20, 2011 12:10 am, edited 7 times in total.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Help: Moving a func_train via an array of vectors

Post by frag.machine »

OneManClan wrote:HISTORY
I did get a previous version of this 'reversible cart' working using multiple path_corner entities, but was advised that using an array make more sense, and which is supported by FTE (which I'm using).
I must confess I fail to see where is the advantage if you already have a perfectly working version using path_corners. Are you running out of entities or bumping in some QuakeC/ engine limitation forcing you to resort to DP/FTE only extensions ?
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by OneManClan »

Hey frag.machine!
frag.machine wrote:
OneManClan wrote:HISTORY
I did get a previous version of this 'reversible cart' working using multiple path_corner entities, but was advised that using an array make more sense, and which is supported by FTE (which I'm using).
I must confess I fail to see where is the advantage if you already have a perfectly working version using path_corners. Are you running out of entities or bumping in some QuakeC/ engine limitation forcing you to resort to DP/FTE only extensions ?
Well, the previous version used Willem's idea of giving every waypoint a "wait" "-1", to allow the func_cart to move one point at a time. My version moves cart to the next waypoint ONLY if Blue has touched it recently, otherwise (one of the highlights of my QuakeC career!) .. it goes in Reverse(!) :)

The problem is that this system required a waypoint every few 'feet', so that the cart would stop if Blue stopped touching it - potentially 50+ waypoint entities. The array idea is meant to solve that.

Otoh....... another option might be.... : instead of checking (and potentially stopping) at every waypoint (every few feet) to see if Blue has stopped touching the cart... make the cart NOT stop at all ... and simply adjust its self.speed. This would allow the cart to come to a complete 'stop' 1/2 way between waypoints... so we only need waypoints to change a direction..

I've been assuming that the carts speed cannot be changed whilst SUB_CalcMove is still running, but this might not be the case..

Thanks for the feedback.. I'll try modifying the earlier version... though
I'm still curious as to what the prob is w the array implementation, and a nerdy part of me is excited about using arrays and reading/storing information in txt files via QuakeC
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by OneManClan »

OneManClan wrote: I've been assuming that the carts speed cannot be changed whilst SUB_CalcMove is still running, but this might not be the case..
I'm testing this; loaded the previous func_cart v1 (which used waypoints not the array), and tried to change self.speed of the cart 1/2 way between waypoints, whilst the cart was moving via:

Code: Select all

SUB_CalcMove( targ.origin - self.mins, self.speed, fc_next );
.. No go. :( The cart stayed at the same speed whilst travellinbg in between waypoints, and only changed speed after the SUB_CalcMove 'move' was complete, ie in the split second before the next SUB_CalcMove began. At this point, unless Ive missed something, or someone suggests another (better!) idea, I'm gonna go back to the array idea... pending more feedback from the Gurus. Bear in mind, this is all newbie guesswork, and I'd happily scrap this implementation and do it 'properly', if anyone has a better idea.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by Spike »

You do realise that you can call SUB_CalcMove even when its already moving somewhere, right? Even if its to the same point but with a different speed.
Like I've said previously, SUB_CalcMove only calculates the velocity and nextthink of the movement. Assuming the think function that you set for when it reaches the point its meant to be moving to is correct, then you can make it move around in circles by changing the destination in a circular pattern at regular intervals and all sorts of crazy stuff. It really is a simple function containing only a little block of reusable maths.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by OneManClan »

Spike wrote:You do realise that you can call SUB_CalcMove even when its already moving somewhere, right? Even if its to the same point but with a different speed.
... no, I had no idea! I assumed that any extra calls to SUB_CalcMove would cause a conflict with one already in progress (because does its thing in 'ltime'). If SUB_CalcMove can be called w a speed of '0', this might be a method of stopping the cart between waypoints!
Spike wrote:Like I've said previously, SUB_CalcMove only calculates the velocity and nextthink of the movement. Assuming the think function that you set for when it reaches the point its meant to be moving to is correct, then you can make it move around in circles by changing the destination in a circular pattern at regular intervals and all sorts of crazy stuff. It really is a simple function containing only a little block of reusable maths.
VERY interesting! Not sure what you mean by 'reusable maths', but will experiment, and report back.

Thank you!
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Check this video: http://www.quaketastic.com/upload/files ... d_test.zip

Later I'll upload the code + demo map + BSP model I made. Tested in Darkplaces, but uses only vanilla QuakeC, so it should work on any engine. Also, not shown in the video is the fact that the payload cart heals players pushing it (exactly in the same way the TF2 payload cart). The values of stop time (5 sec in this demo) and speed (150 units) can be changed in 2 globals. Uses regular path_corners to define the cart trajectory, and you can also make the cart fire alternative targets when reaches a path_corner (just add a "target2" to the path_corner) and/or print messages (set the "message" field). In both cases, the path_corner fires/prints the message only in the first time the cart passes. The only different behavior from the TF2 payload cart is that this one will go back all the way to the first path_corner (the TF2 cart stops when reaches the last conquered checkpoint), but this can be easily changed.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

frag.machine wrote:Check this video: http://www.quaketastic.com/upload/files ... d_test.zip
Hey frag.machine - Wow, I didn't know someone had already coded it!! :o This is perfect!! Love the way that thing pops up to indicate that the waypoint has been passed.
frag.machine wrote: Later I'll upload the code + demo map + BSP model I made.
Yes please! :)
frag.machine wrote:Tested in Darkplaces, but uses only vanilla QuakeC, so it should work on any engine. Also, not shown in the video is the fact that the payload cart heals players pushing it (exactly in the same way the TF2 payload cart). The values of stop time (5 sec in this demo) and speed (150 units) can be changed in 2 globals. Uses regular path_corners to define the cart trajectory, and you can also make the cart fire alternative targets when reaches a path_corner (just add a "target2" to the path_corner)
!!! <expletive> - Of course! Add a "target2" field which stores a string holding the name of the previous waypoint! So simple, so logical, and (now that you've explained it).. obvious. I'm a little embarrassed that I'm still too newbie to have even considered that as an solution.
frag.machine wrote:and/or print messages (set the "message" field). In both cases, the path_corner fires/prints the message only in the first time the cart passes. The only different behavior from the TF2 payload cart is that this one will go back all the way to the first path_corner (the TF2 cart stops when reaches the last conquered checkpoint), but this can be easily changed.
Excellent! After many days of analysis, coding, and newbie guesswork, I'm happy to admit defeat, and gratefully accept something that actually works! :)

thanks,

OneManClan
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

OneManClan wrote:
frag.machine wrote:Uses regular path_corners to define the cart trajectory, and you can also make the cart fire alternative targets when reaches a path_corner (just add a "target2" to the path_corner)
!!! <expletive> - Of course! Add a "target2" field which stores a string holding the name of the previous waypoint! So simple, so logical, and (now that you've explained it).. obvious. I'm a little embarrassed that I'm still too newbie to have even considered that as an solution.
Huh... No :) You don't need to set the previous path to make the cart return; actually, this can be done simply looking for an entity that has a "target" equals to the current path_corner "targetname". You only need to set "target2" if you want to fire some event when the cart touches the path-corner for the first time (like the yellow pole in the video). Likewise, if you want a message to be show, just set "message". As I said, after fired these fields are cleared and won't be referred again.

I'll package everything in a .zip and put on quaketastic.com. Check it later. Things will be easier to understand when you check the map source.

EDIT: here is the whole thing, kitchen sink include. Have fun.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Electro
Posts: 312
Joined: Wed Dec 29, 2004 11:25 pm
Location: Brisbane, Australia
Contact:

Post by Electro »

He's gonna blow a load over that. I know how hard he's been trying to get his to work.
Benjamin Darling
http://www.bendarling.net/

Reflex - In development competitive arena fps combining modern tech with the speed, precision and freedom of 90's shooters.
http://www.reflexfps.net/
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re:

Post by OneManClan »

Hi all!
UPDATE:
Ok, I've revisited frag.machines version of 'Payload' (which unlike mine, works!), have studied it carefully (thanks again f.m.!) and (with some minor fiddling), have finally managed to get it working in AGR CustomTF!! :)

Now... the map this functionality is intended for is town4 ... for which the .map file is unavailable. I've written to the author ('Karl'), no go (invalid email). TeamXlink had a go at decompiling town4.bsp about a year ago, and put some tracks and a cart, and it loaded and 'kind of' worked using FTE as a client, but not EZquake. IIUC this is something to do with the decompilation creating too many entities (or something) and IIUC although it looked almost perfect (to my eyes) there was some 'issue' post decompilation, which would have required a complete rebuild of the map (or something).. anyway.. I had an idea:

Q: Is it possible to implement this entire 'payload' concept on an existing .bsp without needing to decompile/recompile ... just using QuakeC and an ent edit?

Could the entire track and cart be added to the map in this way? The cart functionality is working, I know how to precache and 'initialise' the cart, and as for the track.. i assume each section would be a seperate entity.. joined together... or perhaps the whole track could be one huge entity...

Mapping Gurus, your thoughts please

OneManClan
ps ...if this proves too difficult/impossible.. I suppose we could dispense with the track entirely, and just use a 'floating' cart..
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Help: Moving a func_train via an array of vectors

Post by frag.machine »

You could create a huge customized .BSP model simulating the track and add it to the map (BIG YUCK). HOWEVER, the light won't look right in most engines (dunno if FTE is able to correctly light BSP models). This MAY or MAY NOT be an issue.

I suggest you to scrap the original map and recreate it with the tracks. Easier, more elegant and I would say wiser, because payload maps have unique balancing issues (see TF2's pl_goldrush for an example of poorly implemented gameplay).
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by OneManClan »

frag.machine wrote:You could create a huge customized .BSP model simulating the track and add it to the map (BIG YUCK). HOWEVER, the light won't look right in most engines (dunno if FTE is able to correctly light BSP models). This MAY or MAY NOT be an issue.
Do you mean 'BIG YUCK' as in 'too difficult', or as in 'it will look weird, lighting wise'?
frag.machine wrote:I suggest you to scrap the original map and recreate it with the tracks. Easier, more elegant and I would say wiser, because payload maps have unique balancing issues (see TF2's pl_goldrush for an example of poorly implemented gameplay).
How do you mean 'poorly implemented'? IIRC goldrush was really popular.

Btw. I did the following last night:
1. Walked the path in town4 where the cart would go, and outputted the coordinates (ie everytime there was a change in direction)
2. Added these coordinates as path_corners to the town4 ent.
3. At this point I was stuck, as I couldn't add the following:

Code: Select all

{
"model" "*1" // this line assumes the mdl/bsp is embedded in the map!
"sounds" "1"
"dmg" "0"
"target" "point2"
"targetname" "train"
"classname" "func_cart"
}
So I changed the qc, by adding to the 'func_cart':

Code: Select all

local entity cart;

	cart = spawn();
	self = cart;
	self.target= "point2";
	self.targetname= "train";
	self.classname ="func_cart";
And it worked!!!! :) The cart appeared, and behaved normally/correctly! I'm still not sure:

(1) how all this works
(2) why the functions related to the cart aren't declared before use in the qc, and
(3) why the cart related functions don't have ';' at the end of the {}

IIUC:
1. A new level is started.
2. The Engine reads and loads the entities from the bsp into memory (world.qc)
3. Somewhere, somehow func_cart is run (?) and this starts the Payload code.
4. (guessing) Since the map had no cart, I spawned one, gave it the info the cart would have had if it were in the maps ent.

The only bit of 'weirdness' was: At one point the path goes uphill, and since the cart is square, the edges touched/clipped/went through the 'ground'. This (mostly) didn't affect the cart, except for one section where it was going backwards and ... something happened.. and a second cart came out of the first.. and suddenly there were TWO carts, both functioning normally.. I kept making them go forward and backwards.. and eventually one kind of went into a different path and disappeared into the map.. any idea what would have caused this?
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Help: Moving a func_train via an array of vectors

Post by frag.machine »

OneManClan wrote:Do you mean 'BIG YUCK' as in 'too difficult', or as in 'it will look weird, lighting wise'?
Both.
OneManClan wrote:
frag.machine wrote:I suggest you to scrap the original map and recreate it with the tracks. Easier, more elegant and I would say wiser, because payload maps have unique balancing issues (see TF2's pl_goldrush for an example of poorly implemented gameplay).
How do you mean 'poorly implemented'? IIRC goldrush was really popular.
A bit off-topic so I wont get deep on this, but despite the popularity (likely due to the fact it was the first payload map for a long time), goldrush has a serious path choke in the first stage, just after the first check point. In about 90% of times a decent red team can hold this spot and stale the cart even if outnumbered. The same doesn't happen in others pl maps like badwater. Bad map design.
OneManClan wrote:"model" "*1" // this line assumes the mdl/bsp is embedded in the map!
Replacing it with "maps/cart.bsp" (or whatever it is called in your version) should work.
OneManClan wrote:I'm still not sure:
(1) how all this works
(2) why the functions related to the cart aren't declared before use in the qc, and
(3) why the cart related functions don't have ';' at the end of the {}
Because this is just entity data stored in the map that's used by the engine to spawn entities with the same name in the QuakeC.
When Quake finds a "func_cart" into the entity lump of the BSP, it looks into the QuakeC code for a function with similar name, and this function is called, much like an object constructor in OOP (although it's not exactly the same).

OneManClan wrote:The only bit of 'weirdness' was: At one point the path goes uphill, and since the cart is square, the edges touched/clipped/went through the 'ground'. This (mostly) didn't affect the cart, except for one section where it was going backwards and ... something happened.. and a second cart came out of the first.. and suddenly there were TWO carts, both functioning normally.. I kept making them go forward and backwards.. and eventually one kind of went into a different path and disappeared into the map.. any idea what would have caused this?
I suppose you ended up creating two carts (one via info stored into the entity lump of the map, other via QuakeC). You don't need to spawn a new func_cart by hand, just replace the model name in the map and this should be enough.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Moving a func_train via an array of vectors

Post by OneManClan »

frag.machine wrote:
OneManClan wrote:The only bit of 'weirdness' was: At one point the path goes uphill, and since the cart is square, the edges touched/clipped/went through the 'ground'. This (mostly) didn't affect the cart, except for one section where it was going backwards and ... something happened.. and a second cart came out of the first.. and suddenly there were TWO carts, both functioning normally.. I kept making them go forward and backwards.. and eventually one kind of went into a different path and disappeared into the map.. any idea what would have caused this?
I suppose you ended up creating two carts (one via info stored into the entity lump of the map, other via QuakeC). You don't need to spawn a new func_cart by hand, just replace the model name in the map and this should be enough.
Done, and it WORKS, thanks! :) (the reason for the second cart is because I (for some stupid reason) had two carts in the ent file :oops: )

Btw, I notice the cart is MOVETYPE_PUSH, but (IIUC) moves via :

Code: Select all

setorigin (self, self.goalentity.origin - self.mins);
Q: Is there a reason you didn't use SUB_CalcMove() (which I thought was the standard way to move MOVETYPE_PUSH?). IIUC, Spike mentions (above) that SUB_CalcMove would make it possible for the cart to 'swerve' around corners in a semi circle, rather than a sudden/sharp change in direction (though I have no idea how complex this would be to implement).
Post Reply