Quakec Clock

Discuss programming in the QuakeC language.
Post Reply
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Quakec Clock

Post by toneddu2000 »

Hi guys, I'm trying to deepen every day my knowledge in quakec and every step I make I understand I don't know so much! :)
One of the thing I couldn't find explained here is the concept of time in quake.
It's useless asking you to help for the problem I have right now, so I'll try to create a more costructive issue:

How time beats in quakec? I mean, when the "clock counter" starts? When the map is loaded? And when player dies, time stops? Or it continues ticking even after new map is loaded? And, most of all, how to control it?
I tried to use the variable "time + value" a lot of time but with no success.

I created this very simple chart to create an example situation.
Imagine a tipical situation where a main entity, at some time, spawns. This entity, after an X amount of time (time after its spawning), spawns other sub entities. These sub entities could spawn after other X time other sub sub entities or call the main entities in a X time or every Y time.

Is it doable in quakec a structure like this?
There are other threads discussing this argument?

thanks in advance you guys
Meadow Fun!! - my first commercial game, made with FTEQW game engine
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Quakec Clock

Post by taniwha »

This is where you use .think and .nextthink (and, if you want, the state operator (void ()foo = [] {...}).

Most engines require you to set the move-type to something other than MOVETYPE_NONE for think to work (QF allows MOVETYPE_NONE, both nq and qw). NOCLIP might be a good one.

Another thing to watch out for is nextthink: normally one sets it to a time in the future, and the engine will call think after that time (accuracy depends on server frame rate). If you don't want think to be called for the time being (eg, until some other condition is met), set nextthink to 0 (or less). Now comes the fun: in QW, if nexthink is > 0 but <= sv.time (engine name), the think function will be called repeatedly in the same frame. This is useful if your think function is complicated an gets the dreaded "runaway loop" error (you don't want to do this sort of thing often, though). I've recently made it so both QF's QW and NQ do the looping only if nexthink == sv.time (I ran into a problem with the marksman ogres in honey because I'd made NQ work the same as QW).

BTW, watch out for .ltime: it's really weird and updated only for MOVETYPE_PUSH objects (and is a part of the problem I had with marksman ogres).
Leave others their otherness.
http://quakeforge.net/
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Quakec Clock

Post by Cobalt »

Check out world.qc the 2 most important routines there are : Worldspawn() and Startframe().

Startframe is called constantly and I believe that is where time in the game has its early origins as far as QC goes. The global called: FRAMECOUNT is sort of like time, but its the current number of frames rendered, and the sys_ticrate cvar determines the next frame, and the 'frame' is also sometimes caled "PHYSICS". With players / clients you have routines : PlayerPreThink() and PlayerPostThink() that are part of its thinking before and after the "PHYSICS" frame. That is what I have concluded after studying lots of QC.

WorldSpawn() refers directly to the 'frames' where I believe the map is being loaded up. If you call (self) .anything in that routine, you are talking about the WORLD entity, which is the master entity at the top of the entity list, and I guess its the very first entity spawned.



.ltime is usually used to determine how long the entity will last before ite removed, usually via : remove (self);
...but in the end, its just another numeric float, and you can use it to do other things with other entities where maybe you have a use that
is checking the time vs ltime and changing its touch in its think, IE:

Code: Select all

Void () Entitythink = 
{
if (time >= self.ltime)
self.touch = Ent_Newtouch;

};


Other things like .attack_finished , .air_finished are setting when the player runs out of air or has completed an atteck so that it can attack again....and thoise are all directly pointing to a time value.

 


Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Quakec Clock

Post by Spike »

huh? movetype_none has thinks in every single quake engine.

float time; is the time since the map started. players from the previous map will join at about 0.3 secs in or something weird like that.
it isn't system time, because that would break everything. specifically, system time in a float would have too low precision. doubles do not make everything okay. just using long doubles instead is a stupid suggestion, so I'm glad noone suggested it.

.float ltime; has nothing to do with the remove() builtin, at least as origionally designed.
.ltime is only used by the engine for .movetype == MOVETYPE_PUSH, where it is used instead of time for checks against nextthink. it is also incremented ONLY when the pusher actually moves. this allows syncing thinks to specific distances traveled even if something stood in its path and stopped it from moving for 10 mins. like I say, the engine only does that with movetype_push, so with any other movetype its 'free' for reuse. Any such reuse is mod specific.

traditionally you have:
startframe()
foreachclient
{
if self.isaplayer then playerprethink()
if (self.movetype == movetype_push)
{
fractionmoved = trytomove(self.velocity * frametime);
self.ltime = fractionmoved*frametime;
if (self.nextthink && self.nextthink < self.ltime) then {self.nextthink = 0; self.think();}
}
else if (self.nextthink && self.nextthink < time) then {self.nextthink = 0; self.think();}
if self.isaplayer then playerpostthink()
}
ext_endframe();

as taniwha says, QW can think multiple times in a single frame. the time value will be set to self.nextthink before the call. this is to keep nextthinks etc ticking at 10fps if there's only network traffic driving physics at 5 fps. also player entities do not have their physics run as part of the generic physics frame, but rather only on response to network traffic.
fte (and I *think* dp too) uses the qw multiple-thinks-and-players-outside-the-frame behaviour by default, even for nq mods, just with an exit clause if the think function sets nextthink to time. :)
yes, this breaks certain mods (headhunters at least), but fixes physics to give prediction. in fte, you can set sv_nomsec (badly named, I know) to mimic nq physics more precisely. a value of 2 makes it an exact match (assuming there's no bugs!)
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Quakec Clock

Post by taniwha »

Spike wrote:huh? movetype_none has thinks in every single quake engine.
Sorry, I got confused between thinking and linking. Indeed, MOVETYPE_NONE thinks, but it doesn't link the entity.

[edit]Thanks for the explanation of ltime. That one really didn't make sense. The problem in honey is that the marksman ogre's floating ball uses some of the door physics code in its think, and thus nextime = self.ltime+0.1; sneaks in: ouch. Fine with id nq, but...
Leave others their otherness.
http://quakeforge.net/
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Quakec Clock

Post by toneddu2000 »

thanks you guys for all the replies but I'm still a little confused.
If I understood correctly:
1) there's no clock in quake linked to system's clock
2) only value for time used in quake is float time; and it starts to "count" since the map has started
3) float ltime is tied to MOVETYPE_PUSH (so I think it's used by crushing platforms and similar, I guess. Not so useful in game)
4) to make the time flows I need to set think and nextthink for every entity I need to sync with quake time
5) Quake works a 10 FPS (right?)

My remained doubts are:

Is it impossible to start a timer when the map starts and keep checking it to inject events at specific time (like a scripted game - the player walk in an area map and, after 2 min, a monsters horde spawns at his back or whatever)
float time is a builtin variable? Or it was created by id just in the quakec? I mean, using DP, it could be possible to recreate a clock system (with timers and stuff) just with blank quakec code?

PS: I didn't understand the "thinking and linking" matter said by taniwha. When an entity is meant to be "linked"?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
andrewj
Posts: 133
Joined: Mon Aug 30, 2010 3:29 pm
Location: Australia

Re: Quakec Clock

Post by andrewj »

toneddu2000 wrote:Is it impossible to start a timer when the map starts and keep checking it to inject events at specific time (like a scripted game - the player walk in an area map and, after 2 min, a monsters horde spawns at his back or whatever)
You can think of every entity in the map as having a built-in timer. You set 'nextthink' to the time in the future when you want its 'think' code to be called, and the engine will automatically call that think function when that time rolls around.

So what you just described is actually very easy. There will be a trigger entity whose 'touch' code is run when the player enters that room, and that 'touch' code can set nextthink to 'time + 120' for 2 minutes in the future, and set a new 'think' function which has the code for spawning in the monsters.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Quakec Clock

Post by Cobalt »

There is a way to bring the PC system clock into QC using DP. According th LordHavoc:

See extension DP_QC_GETTIME, gettime(GETTIME_UPTIME) will give you seconds since engine start, but this degrades rather significantly if the engine is left running for more than a day (precision loss
due to floating point precision on big numbers) so be careful what you wish for.



toneddu2000 wrote:1) there's no clock in quake linked to system's clock
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Quakec Clock

Post by toneddu2000 »

@andrewj: that's exactly what I needed to know: if time was relative to entity which call it. Thanks a lot

@Cobalt: thanks for the hint, I'll try that
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Quakec Clock

Post by Cobalt »

Related - I am trying this to get the date and time in the game via DP:

string(float uselocaltime, string format, ...) strftime = #478;
//description:
//provides the ability to get the local (in your timezone) or world (Universal Coordinated Time) time as a string using the formatting of your choice:
//example: "%Y-%m-%d %H:%M:%S" (result looks like: 2007-02-08 01:03:15)
//note: "%F %T" gives the same result as "%Y-%m-%d %H:%M:%S" (ISO 8601 date format and 24-hour time)
//for more format codes please do a web search for strftime 3 and you should find the man(3) pages for this standard C function.
//

My (not working) code:

Code: Select all

 local string s;
      local float hour;
       s = (strftime (TRUE, "%Y-%m-%d %H:%M:%S"));
	hour = stof(s);
      bprint(" Current time is: ");
     bprint (s);
      bprint("\n");

...will not compile - says type mismatch on "s" , I have tried floating s, no help?
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Quakec Clock

Post by frag.machine »

Cobalt wrote:Related - I am trying this to get the date and time in the game via DP:

string(float uselocaltime, string format, ...) strftime = #478;
//description:
//provides the ability to get the local (in your timezone) or world (Universal Coordinated Time) time as a string using the formatting of your choice:
//example: "%Y-%m-%d %H:%M:%S" (result looks like: 2007-02-08 01:03:15)
//note: "%F %T" gives the same result as "%Y-%m-%d %H:%M:%S" (ISO 8601 date format and 24-hour time)
//for more format codes please do a web search for strftime 3 and you should find the man(3) pages for this standard C function.
//

My (not working) code:

Code: Select all

 local string s;
      local float hour;
       s = (strftime (TRUE, "%Y-%m-%d %H:%M:%S"));
	hour = stof(s);
      bprint(" Current time is: ");
     bprint (s);
      bprint("\n");

...will not compile - says type mismatch on "s" , I have tried floating s, no help?

Never used those DP extensions. Have you tried a redux like this ?

Code: Select all

    local string s;
    s = strftime (TRUE, "%Y-%m-%d %H:%M:%S");
    bprint(" Current time is: ");
    bprint (s);
    bprint("\n");
EDIT: Ok, the following code compiled (using frikqcc here, YMMV with other compilers) and executed OK:

Code: Select all

void () dp_gettime_test =
{
    bprint (" Current time is: ");
    bprint (strftime (TRUE, "%Y-%m-%d %H:%M:%S"));
    bprint ("\n");
}
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Quakec Clock

Post by Cobalt »

Thanks FM, worked nice!
Post Reply