extension_timescale.txt
来自「quake1 dos源代码最新版本」· 文本 代码 · 共 278 行
TXT
278 行
================================================================
Title : Tutorial: TIMESCALE extension
Date : 2000-10-23
Filename : extension_timescale.TXT
Author : Matthias "Maddes" Buecher
Tomas "Tomaz" Jakobsson
Email Address : maddes@go.to
tompsson@hotmail.com
Homepage : Quake Info Pool
http://www.quake-info-pool.net/
Tomaz Homepage
http://tomaz.quakesrc.org/
Quake Standards Group (short QSG)
http://www.quakesrc.org/
Complexity : Moderate
================================================================
The extension is called "TIMESCALE" (replaces the older "TQ_SLOWMO") and allows
to slow down or speed up the game.
If you want to add this extension to your own engine, you should add Lord Havoc's
Extension System before (tutorial here: EXTENSION_SYSTEM.TXT).
It's mainly adding a new console variable called "HOST_TIMESCALE" and using its
value when calculating the frametime for the game. For example by setting the
console variable to 0.5 the game runs only at half the speed, this means 1
second realtime results in 0.5 second gametime.
As a lot of non-gameplay functions on the client side are affected by the
frametime, those functions have to made independent of the modified gamespeed.
The best way to do this is to have three(!) frametimes, one that holds the
frametime of your system/cpu, one for the regular game frametime and the last
one to keep the modified game frametime.
The QuakeC code should set the "HOST_TIMESCALE" cvar to an initial value in
worlspawn() and stuff the value of "HOST_TIMESCALE" to all clients, when they
connect or when the value is changed.
First add the new extension to the array pr_extensions[] in PR_CMDS.C...
"TIMESCALE", // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
In HOST.C add these definitions at the top...
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
double host_cpu_frametime;
double host_org_frametime;
cvar_t host_timescale = {"host_timescale", "1.0"};
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
... register the console variable in Host_InitLocal() ...
Cvar_RegisterVariable (&host_timescale); // 2001-10-20 HOST_TIMERATE extension by Tomaz/Maddes
... and replace or modify the function Host_FilterTime() as following ...
qboolean Host_FilterTime (float time)
{
realtime += time;
if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)
return false; // framerate is too high
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
// host_frametime = realtime - oldrealtime;
host_cpu_frametime = realtime - oldrealtime;
host_org_frametime = host_cpu_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
oldrealtime = realtime;
if (host_framerate.value > 0)
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
{
// host_frametime = host_framerate.value;
host_org_frametime = host_framerate.value;
}
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
else
{ // don't allow really long or short frames
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
/*
if (host_frametime > 0.1)
host_frametime = 0.1;
if (host_frametime < 0.001)
host_frametime = 0.001;
*/
if (host_org_frametime > 0.1)
host_org_frametime = 0.1;
if (host_org_frametime < 0.001)
host_org_frametime = 0.001;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
}
host_frametime = host_org_frametime * host_timescale.value; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
return true;
}
If your engine supports rangechecks on console variables, I suggest to limit the
value of HOST_TIMERATE to 0 - 10.0 to avoid negative values.
Declare the new variables for other source files in QUAKEDEF.H...
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
extern double host_cpu_frametime;
extern double host_org_frametime;
extern cvar_t host_timescale;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
In SV_SpawnServer() of SV_MAIN.C change...
host_frametime = 0.1;
... into ...
host_org_frametime = host_frametime = 0.1; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
The server side is done and you can control the game speed with the console variable.
But we also have to change the client side too:
The console roll-in/out, the centerprint screentime and the turtle display have
to be changed. Change the usage of "host_time" with "host_cpu_frametime" in
SCR_CheckDrawCenterString(), SCR_DrawTurtle() and SCR_SetUpToDrawConsole() of
SCREEN.C and GL_SCREEN.C.
A minor problem is the slow rotating Quake symbol in the menus. To fix this
replace the usage of "host_time" with "realtime" in M_Main_Draw(),
M_SinglePlayer_Draw(), M_MultiPlayer_Draw() and M_Net_Draw() of MENU.C.
A more serious problem is that keyboard and joystick turning are affected by the
new frametime, but not mouse looking. To make keyboard and joystick turning
independent of the modified frametime, replace the usage of "host_frametime"
with "host_org_frametime" in IN_JoyMove() of IN_WIN.C, CL_AdjustAngles() of
CL_INPUT.C and IN_JoyMove() of IN_DOS.C.
Unfortunately I do not know a way to modify the speed of sounds in the original
Quake source, but the volume fade uses the frametime. So replace the usage of
"host_frametime" with "host_org_frametime" in S_UpdateAmbientSounds() of
SND_DMA.C.
If you use the QSG's FMOD sound replacement tutorial then add
the following console variable to SND_FMOD.C...
cvar_t SND_NoTimeScaleEffect = {"SND_NoTimeScaleEffect", "0"}; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
... register the console variable in S_Init() ...
Cvar_RegisterVariable(&SND_NoTimeScaleEffect); // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
... and change the end of S_StartSound() to this ...
if(SND_LastContent <= CONTENTS_WATER && !SND_NoWaterEffect.value && entchannel != -2)
{
rate = Channel->samplerate * SND_WaterPitch.value;
}
else
{
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
if (SND_NoTimeScaleEffect.value)
rate = Channel->samplerate * host_timescale.value;
else
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
rate = Channel->samplerate;
}
Channel->channel = FSOUND_PlaySound3DAttrib(Channel->channel, sfx->sample, rate, rvol, Channel->pan, NULL, NULL);
If you use the same model interpolations like in TomazQuake, then multiply the
value of "host_timescale" for the blend value. The following lines show how it
is done in TomazQuake:
R_BlendedRotateForEntity() of GL_RMAIN.C: "blend = (timepassed * 10) * host_timescale.value;"
GL_DrawAliasBlendedFrame() of GL_MDL.C: "blend = ((realtime - e->frame_start_time)*host_timescale.value) / e->frame_interval;"
There can be other things which have to be adapated too, depending on the features of your engine.
At last we will pass the two new frametimes to the QuakeC code.
When a PROGS.DAT is loaded we check if the two float globals "cpu_frametime" and
"org_frametime" are defined. If so, we will set them at the start of every frame.
The loading is done in PR_EDICT.C, so add these definitions at top of it...
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
ddef_t *pr_global_cpu_frametime;
ddef_t *pr_global_org_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
... at top of PR_LoadProgs() add this definition ...
etype_t type; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes
... and at the end of PR_LoadProgs() add this code ...
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
pr_global_cpu_frametime = ED_FindGlobal ("cpu_frametime");
if (pr_global_cpu_frametime)
{
type = pr_global_cpu_frametime->type;
type &= ~DEF_SAVEGLOBAL;
if (type != ev_float)
{
pr_global_cpu_frametime = NULL;
}
}
pr_global_org_frametime = ED_FindGlobal ("org_frametime");
if (pr_global_org_frametime)
{
type = pr_global_org_frametime->type;
type &= ~DEF_SAVEGLOBAL;
if (type != ev_float)
{
pr_global_org_frametime = NULL;
}
}
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
Declare the new variables for other source files in PROGS.H...
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
extern ddef_t *pr_global_cpu_frametime;
extern ddef_t *pr_global_org_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
In HOST.C change the beginning of Host_ServerFrame() (there are 3!!!) as
following...
void Host_ServerFrame (void)
{
// run the world state
pr_global_struct->frametime = host_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes start
if (pr_global_cpu_frametime)
{
G_FLOAT(pr_global_cpu_frametime->ofs) = host_cpu_frametime;
}
if (pr_global_org_frametime)
{
G_FLOAT(pr_global_org_frametime->ofs) = host_org_frametime;
}
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes end
Done.
You might say that this functionality must not be added as an extension, as the
QuakeC code could use the new builtin function "cvar_find" to check for the
console variables, or that the QuakeC code could always set the console variables.
That's right, but this is just a tutorial.
Now it's up to you to provide another extension which allows an entity to act in
normal speed while the game is slowed down. This could be handled by an entity
field called ".no_timescale", so QuakeC coders can allow players to act faster in
slowmo while their slowmo account isn't zero. If the entity field is set you
have to do the physics with host_org_frametime and not with host_frametime.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?