func_train, one point at a time?
Moderator: InsideQC Admins
11 posts
• Page 1 of 1
func_train, one point at a time?
hi,
is there presently any way in the plain generic quake engine to instead of having a trigger_once or trigger_multiple start a func_train in motion and not be able to stop it instead have some kind of trigger once to make the train simply increment one point along its path?
is there presently any way in the plain generic quake engine to instead of having a trigger_once or trigger_multiple start a func_train in motion and not be able to stop it instead have some kind of trigger once to make the train simply increment one point along its path?
-

dayfive - Posts: 77
- Joined: Fri Nov 10, 2006 9:48 pm
This can be handled in the QuakeC side, and I think mods like Quoth already do it.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC
(LordHavoc)
-

frag.machine - Posts: 2090
- Joined: Sat Nov 25, 2006 1:49 pm
I'm pretty sure this code does what you're asking. It's a func_train adaptation that will stop at any path_corner that has a wait of -1. When you trigger it again, it will move to the next one in the path.
If this doesn't work, I apologize, it may have rotted since I last used it.
You'll have to add some variables to def.qc like "isMoving". I hope this at least gets you on the right track.
Or use Quoth.
If this doesn't work, I apologize, it may have rotted since I last used it.
You'll have to add some variables to def.qc like "isMoving". I hope this at least gets you on the right track.
Or use Quoth.
- Code: Select all
// ================================================================
// FUNC_MOVER
// ================================================================
// Forward declarations
void() func_mover_next;
void() func_mover_use;
void() func_mover_wait;
void() func_mover_blocked =
{
if( time < self.attack_finished )
{
return;
}
self.attack_finished = time + 0.5;
T_Damage (other, self, self, self.dmg);
};
// Triggered.
void() func_mover_use =
{
// Ignore triggers if we are already moving
if( self.isMoving )
{
return;
}
func_mover_next();
};
void() func_mover_wait =
{
sound( self, CHAN_VOICE, self.noise, 1, ATTN_NORM );
if( self.wait )
{
self.nextthink = self.ltime + self.wait;
}
else
{
self.nextthink = self.ltime + 0.01;
}
self.isMoving = FALSE;
self.think = func_mover_next;
};
void() func_mover_next =
{
local entity targ;
targ = find( world, targetname, self.target );
self.target = targ.target;
if( !self.target )
{
objerror( "mover_next: no next target" );
}
if( targ.wait )
{
self.wait = targ.wait;
}
else
{
self.wait = 0;
}
self.isMoving = TRUE;
sound( self, CHAN_VOICE, self.noise1, 1, ATTN_NORM );
SUB_CalcMove( targ.origin - self.mins, self.speed, func_mover_wait );
};
void() func_mover_find =
{
local entity targ;
// Find the first path_corner we are targeting
targ = find( world, targetname, self.target );
self.target = targ.target;
// Move to the target
setorigin( self, targ.origin - self.mins );
// If this mover isn't being targetted, make it start moving immediately
if( !self.targetname )
{
// not triggered, so start immediately
self.nextthink = self.ltime + 0.1;
self.think = func_mover_next;
}
};
/*QUAKED func_mover (.8 .5 .0) ?
A mover is a brush entity that can move to path_corners.
This works a lot like a func_train except that each time the mover stops, it can be triggered again.
Set up a series of path_corners with a wait of -1 to create a mover that moves and stops at waypoints.
"sounds =
0) None
1) Train
2) Base
3) Medieval
*/
void() func_mover =
{
if( !self.speed )
{
self.speed = 100;
}
if (!self.target)
{
objerror ("func_mover without a target");
}
if( !self.dmg )
{
self.dmg = 2;
}
self.isMoving = FALSE;
self.cnt = 1;
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
self.blocked = func_mover_blocked;
self.use = func_mover_use;
self.classname = "mover";
if (self.sounds == 0)
{
precache_sound ("misc/null.wav");
self.noise = ("misc/null.wav");
precache_sound ("misc/null.wav");
self.noise1 = ("misc/null.wav");
}
else if (self.sounds == 1)
{
precache_sound ("plats/train2.wav");
self.noise = ("plats/train2.wav");
precache_sound ("plats/train1.wav");
self.noise1 = ("plats/train1.wav");
}
else if (self.sounds == 2)
{
precache_sound ("plats/plat2.wav");
self.noise = "plats/plat2.wav";
precache_sound ("plats/plat1.wav");
self.noise1 = "plats/plat1.wav";
}
else if (self.sounds == 3)
{
precache_sound ("plats/medplat2.wav");
self.noise = "plats/medplat2.wav";
precache_sound ("plats/medplat1.wav");
self.noise1 = "plats/medplat1.wav";
}
setmodel( self, self.model );
setsize( self, self.mins, self.maxs );
setorigin( self, self.origin );
// Start movers on the second frame, to make sure their targets have had a chance to spawn
self.nextthink = self.ltime + 0.1;
self.think = func_mover_find;
};
- Willem
- Posts: 73
- Joined: Wed Jan 23, 2008 10:58 am
Can this function be used to make a (TF2 style) payload map for Quake1? ie to make the 'cart' stop and start along a fixed path?
ie
If there are Blue players near the cart it moves (slowly)along its path. If there are no Blue players near the cart for (eg) 20 secs, it slowly moves backwards. The cart gives health and ammo to the Blue team.
Possible?
OneManClan
ie
If there are Blue players near the cart it moves (slowly)along its path. If there are no Blue players near the cart for (eg) 20 secs, it slowly moves backwards. The cart gives health and ammo to the Blue team.
Possible?
OneManClan
- OneManClan
- Posts: 243
- Joined: Sat Feb 28, 2009 2:38 pm
Dear Gurus,
I've started work on a TF2 style 'payload' adaptation for Q1, and have been studying Willem's func_mover concept but I'm still confused as to exactly *how* it works. If anyone can answer my 5 questions (in the comments below) then that would be great! I've put 'OMC' before my comments, and added 'Willem' to his original ones.
thanks,
OneManClan
Note: This is an excerpt from the complete Willem's func_mover which he posted above.
I've started work on a TF2 style 'payload' adaptation for Q1, and have been studying Willem's func_mover concept but I'm still confused as to exactly *how* it works. If anyone can answer my 5 questions (in the comments below) then that would be great! I've put 'OMC' before my comments, and added 'Willem' to his original ones.
thanks,
OneManClan
Note: This is an excerpt from the complete Willem's func_mover which he posted above.
- Code: Select all
// OMC the 'func_mover' is sitting there waiting to be triggered (IIUC)
void() func_mover_wait =
{
sound( self, CHAN_VOICE, self.noise, 1, ATTN_NORM );
if( self.wait )
{
/* OMC Q1: defs.qc says that .ltime is something that FUNC_PLATS use instead of time, but how do they work, and where is self.ltime set? */
self.nextthink = self.ltime + self.wait;
}
else
{
self.nextthink = self.ltime + 0.01;
}
self.isMoving = FALSE;
self.think = func_mover_next;
};
void() func_mover_next =
{
local entity targ;
targ = find( world, targetname, self.target );
self.target = targ.target;
// OMC Q2: Waypoint ents start with 'wait = -1', so why/when would targ ever not be -1?
if( targ.wait )
{
self.wait = targ.wait;
}
else
{
self.wait = 0;
}
self.isMoving = TRUE;
sound( self, CHAN_VOICE, self.noise1, 1, ATTN_NORM );
// OMC Q3: This function does not run every frame, so how does the func_mover move?
SUB_CalcMove( targ.origin - self.mins, self.speed, func_mover_wait );
};
// OMC I think this only runs once, when the map is loaded
void() func_mover_find =
{
local entity targ;
// Willem Find the first path_corner we are targeting
targ = find( world, targetname, self.target );
self.target = targ.target;
// Willem Move to the target
// OMC Q4: IIUC self.mins is used in 'setsize' - what is it doing here?
setorigin( self, targ.origin - self.mins );
// If this mover isn't being targetted, make it start moving immediately
if( !self.targetname )
{
// OMC Q5. Why would it move it if doesnt have a self.targetname?
// not triggered, so start immediately
self.nextthink = self.ltime + 0.1;
self.think = func_mover_next;
}
};
- OneManClan
- Posts: 243
- Joined: Sat Feb 28, 2009 2:38 pm
A1: the engine increases self.ltime by as much as time increases, *except* if the object was blocked by something, so thinks will be delayed if the object was blocked. This is special behavior only for movetype_push.
A2: negative wait generally means 'infinite'. looks like that value isn't supported. certainly the extra logic to test if its zero is useless.
A3: movetype_push objects move by setting velocity, and should not be moved using setorigins, other than to attempt to enforce a safe starting point for a move.
A4: inline bsp objects tend to have a default origin of '0 0 0'. Their positioning is typically based upon their mins coord rather than their center.
That is, a 64*64*64 cube object centered '256 256 256' will have origin/mins/max of '0 0 0'/'224 224 224'/'288 288 288', once a setmodel has been used on it, anyway.
Waypoints are positioned at the absmin point that the object would have if it was waiting at that waypoint.
Or in other words, pusher.origin + pusher.mins == waypoint.origin
Rearrange to calculate pusher.origin relative to pusher.mins and the waypoint.origin.
A5: objects without a targetname cannot be targetted. such objects thus tend to automatically begin moving, because they're useless otherwise - you might as well have used func_wall.
A2: negative wait generally means 'infinite'. looks like that value isn't supported. certainly the extra logic to test if its zero is useless.
A3: movetype_push objects move by setting velocity, and should not be moved using setorigins, other than to attempt to enforce a safe starting point for a move.
A4: inline bsp objects tend to have a default origin of '0 0 0'. Their positioning is typically based upon their mins coord rather than their center.
That is, a 64*64*64 cube object centered '256 256 256' will have origin/mins/max of '0 0 0'/'224 224 224'/'288 288 288', once a setmodel has been used on it, anyway.
Waypoints are positioned at the absmin point that the object would have if it was waiting at that waypoint.
Or in other words, pusher.origin + pusher.mins == waypoint.origin
Rearrange to calculate pusher.origin relative to pusher.mins and the waypoint.origin.
A5: objects without a targetname cannot be targetted. such objects thus tend to automatically begin moving, because they're useless otherwise - you might as well have used func_wall.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Thanks for the great info Spike. Much to think about. Meanwhile re:
I had another look at the code, and just realised that 'if()' returns TRUE, if given a negative number!!! ie:
In other words:
Ugh, so many hours wasted being (needlessly) confused! Still, at least this bit makes sense now.
Thanks again Spike.
Spike wrote:A2: negative wait generally means 'infinite'. looks like that value isn't supported. certainly the extra logic to test if its zero is useless.
I had another look at the code, and just realised that 'if()' returns TRUE, if given a negative number!!! ie:
- Code: Select all
local float foo;
foo = -1.0;
if(foo)
dprint("This prints!!\n");
In other words:
- Code: Select all
if( self.wait ) // this is TRUE if self.wait == -1
{
self.nextthink = self.ltime + self.wait;
// we just gave .nextthink a negative value, which (apparantly) is 'valid'! IIUC, it just means that the .think will run when (and if) the .nextthink value is ever changed
}
Ugh, so many hours wasted being (needlessly) confused! Still, at least this bit makes sense now.
Thanks again Spike.
- OneManClan
- Posts: 243
- Joined: Sat Feb 28, 2009 2:38 pm
'truth' is any value which is not 0. sadly in QC that also includes -0, which can result in some confusion...
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
11 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest
