gist/rtcw_from_7e67f464825cc8d018be2fd8ad13738e0e4e5303.diff

6706 lines
227 KiB
Diff

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 21da71831..9e6b8c5fe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -826,6 +826,16 @@ IF(FTE_PLUG_QUAKE3)
ENDIF()
ENDIF()
+ADD_LIBRARY(plug_rtcwsp MODULE ${FTE_Q3_FILES} plugins/plugin.c)
+SET_TARGET_PROPERTIES(plug_rtcwsp PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON};BOTLIB;BOTLIB_STATIC;FTEPLUGIN;RTCWSP")
+TARGET_LINK_LIBRARIES(plug_rtcwsp m)
+EMBED_PLUGIN_META(rtcwsp "RTCW - Singleplayer" "Provides compatability with RTCW's single-player gamecode.")
+
+ADD_LIBRARY(plug_rtcwmp MODULE ${FTE_Q3_FILES} plugins/plugin.c)
+SET_TARGET_PROPERTIES(plug_rtcwmp PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON};BOTLIB;BOTLIB_STATIC;FTEPLUGIN;RTCWMP")
+TARGET_LINK_LIBRARIES(plug_rtcwmp m)
+EMBED_PLUGIN_META(rtcwmp "RTCW - Multiplayer" "Provides compatability with RTCW's multi-player gamecode.")
+
FILE(STRINGS "${FTE_BUILD_CONFIG}" BULLET_INTERNAL REGEX "^#define[\t ]+USE_INTERNAL_BULLET")
IF(BULLET_INTERNAL)
#Built-in bullet physics plugin...
diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c
index 548fd3b0c..e65a0eb10 100644
--- a/engine/client/cl_main.c
+++ b/engine/client/cl_main.c
@@ -6781,6 +6781,8 @@ void CL_StartCinematicOrMenu(void)
Media_PlayFilm("video/idlogo.roq", true);
else if (idcin_depth != FDEPTH_MISSING)
Media_PlayFilm("video/idlog.cin", true);
+ else if (COM_FCheckExists("video/wolfintro.roq"))
+ Media_PlayFilm("video/wolfintro.roq", true);
#ifdef HAVE_LEGACY
//and for fun (blame spirit):
diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc
index 1ebd3378c..281597e7a 100644
--- a/engine/client/cl_plugin.inc
+++ b/engine/client/cl_plugin.inc
@@ -398,7 +398,8 @@ static model_t *Plug_Scene_ModelFromId(qhandle_t id)
}
static qhandle_t Plug_Scene_ShaderForSkin(qhandle_t modelid, int surfaceidx, int skinnum, float time)
{
- shader_t *s = Mod_ShaderForSkin(Plug_Scene_ModelFromId(modelid), surfaceidx, skinnum, time, NULL);
+ texnums_t *tnums;
+ shader_t *s = Mod_ShaderForSkin(Plug_Scene_ModelFromId(modelid), surfaceidx, skinnum, time, &tnums);
return s->id+1;
}
static void QDECL Plug_Scene_Clear(void)
@@ -857,7 +858,7 @@ static int QDECL Plug_GetWeaponStats(int self, struct wstats_s *result, size_t m
}
#endif
-static qboolean QDECL Plug_Con_SubPrint(const char *name, const char *text)
+static qboolean QDECL Plug_Con_SubPrint(const char *name, const char *text, unsigned int setflags, unsigned int clearflags)
{
console_t *con;
if (!name)
@@ -867,7 +868,7 @@ static qboolean QDECL Plug_Con_SubPrint(const char *name, const char *text)
{
if (!*name)
{
- Con_Printf("%s", text);
+ Con_PrintFlags(text, setflags, clearflags);
return true;
}
return false;
@@ -892,7 +893,7 @@ static qboolean QDECL Plug_Con_SubPrint(const char *name, const char *text)
}
}
- Con_PrintCon(con, text, con->parseflags);
+ Con_PrintCon(con, text, (setflags|con->parseflags)&~clearflags);
return true;
}
diff --git a/engine/client/m_options.c b/engine/client/m_options.c
index fe2cd4192..785f1c2b4 100644
--- a/engine/client/m_options.c
+++ b/engine/client/m_options.c
@@ -3528,6 +3528,8 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
ent.framestate.g[FS_REG].frame[0] = mods->framegroup;
ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime;
ent.framestate.g[FS_REG].endbone = 0x7fffffff;
+ ent.framestate.g[FST_BASE] = ent.framestate.g[FS_REG];
+
if (*mods->skinname)
ent.customskin = Mod_RegisterSkinFile(mods->skinname); //explicit .skin file to use
else
@@ -4284,6 +4286,7 @@ static int QDECL CompleteModelViewerList (const char *name, qofs_t flags, time_t
struct xcommandargcompletioncb_s *ctx = parm;
const char *ext = COM_GetFileExtension(name, NULL);
if (!strcmp(ext, ".mdl") || !strcmp(ext, ".md2") || !strcmp(ext, ".md3")
+ || !strcmp(ext, ".mds") || !strcmp(ext, ".mdc")
|| !strcmp(ext, ".iqm") || !strcmp(ext, ".dpm") || !strcmp(ext, ".zym")
|| !strcmp(ext, ".psk") || !strcmp(ext, ".md5mesh") || !strcmp(ext, ".md5anim")
|| !strcmp(ext, ".bsp") || !strcmp(ext, ".map") || !strcmp(ext, ".hmp")
diff --git a/engine/client/render.h b/engine/client/render.h
index cffe4943d..aeb5cab52 100644
--- a/engine/client/render.h
+++ b/engine/client/render.h
@@ -179,6 +179,12 @@ typedef struct
unsigned int q1upper; //Q1UNSPECIFIED
unsigned int q1lower; //Q1UNSPECIFIED
#endif
+ size_t numprops;
+ struct
+ {
+ char key[64];
+ char value[MAX_QPATH];
+ } *props;
struct
{
char surface[MAX_QPATH];
diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c
index 2cdeb9a6a..33b4de4eb 100644
--- a/engine/client/snd_dma.c
+++ b/engine/client/snd_dma.c
@@ -57,6 +57,17 @@ qboolean snd_initialized = false;
int snd_speed;
float voicevolumemod = 1;
+static struct
+{
+ float volume;
+
+ float startvol;
+ float starttime;
+
+ float endvol;
+ float duration;
+} volfade = {1};
+
static struct listener_s
{
int entnum;
@@ -3954,10 +3965,38 @@ int S_GetMixerTime(soundcardinfo_t *sc)
}
#endif
+//fades non-voice audio to $newvolume over $duration seconds
+void S_FadeSounds(float newvolume, float duration)
+{
+ volfade.startvol = volfade.volume;
+ volfade.endvol = newvolume;
+ volfade.starttime = realtime;
+ volfade.duration = duration;
+}
+
+//respacialises, repaints, etc
void S_Update (void)
{
soundcardinfo_t *sc;
+ //update fades / voice levels
+ if (volfade.duration)
+ {
+ float frac = (realtime - volfade.starttime)/volfade.duration;
+ if (frac >= 1)
+ {
+ frac = 1;
+ volfade.duration = 0;
+ }
+ volfade.volume = volfade.startvol + frac*(volfade.endvol-volfade.startvol);
+ }
+ voicevolumemod = volfade.volume;
+ if (s_voip.lastspoke_any > realtime) //when someone else is speaking, reduce other playback volumes so that we can hear them
+ voicevolumemod *= snd_voip_ducking.value;
+ if (s_voip.wantsend) //when we are speaking, clamp the volume so that others cannot hear our game noise so easily
+ voicevolumemod = min(voicevolumemod, snd_voip_capturingvol.value);
+
+
S_LockMixer();
for (sc = sndcardinfo; sc; sc = sc->next)
S_UpdateCard(sc);
diff --git a/engine/client/sound.h b/engine/client/sound.h
index 1f75f1ecd..88c954b80 100644
--- a/engine/client/sound.h
+++ b/engine/client/sound.h
@@ -205,6 +205,7 @@ void S_StopAllSounds(qboolean clear);
void S_UpdateListener(int seat, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, size_t reverbtype, vec3_t velocity);
qboolean S_UpdateReverb(size_t reverbtype, void *reverb, size_t reverbsize);
void S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up);
+void S_FadeSounds(float newvolume, float duration);
void S_Update (void);
void S_ExtraUpdate (void);
void S_MixerThread(soundcardinfo_t *sc);
diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h
index 728cc74b4..0b7a04473 100644
--- a/engine/common/bothdefs.h
+++ b/engine/common/bothdefs.h
@@ -194,6 +194,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef AVAIL_WASAPI
#endif
+#if defined(RTCWMP) && defined(RTCWSP)
+ #undef RTCW RTCWMP //lame... sorry.
+#endif
+#if defined(RTCWMP) || defined(RTCWSP)
+ #define RTCW //breaks q3... sorry
+#endif
+
#if !(defined(__linux__) || defined(__CYGWIN__)) || defined(ANDROID)
#undef HAVE_GNUTLS
#endif
diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h
index efd006717..9cd2fed2b 100644
--- a/engine/common/bspfile.h
+++ b/engine/common/bspfile.h
@@ -510,20 +510,20 @@ typedef struct
#define FTECONTENTS_EMPTY 0x00000000
#define FTECONTENTS_SOLID 0x00000001
#define FTECONTENTS_WINDOW 0x00000002 //solid to bullets, but not sight/agro
-//q2aux 0x00000004
+//q2aux,rtcwlightgrid 0x00000004
#define FTECONTENTS_LAVA 0x00000008
#define FTECONTENTS_SLIME 0x00000010
#define FTECONTENTS_WATER 0x00000020
#define FTECONTENTS_FLUID (FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA|FTECONTENTS_SKY) //sky is a fluid for q1 code.
-//q2mist 0x00000040
-//q3notteam1 0x00000080
-//q3notteam2 0x00000100
+//q2mist,rtcwfog 0x00000040
+//q3notteam1,rtcwmissileclip 0x00000080
+//q3notteam2,rtcwitem 0x00000100
//q3nobotclip 0x00000200
// 0x00000400
// 0x00000800
-// 0x00001000
-// 0x00002000
-#define FTECONTENTS_LADDER 0x00004000
+//rtcwaiinvis 0x00001000
+//rtcwclipshot 0x00002000
+#define FTECONTENTS_LADDER 0x00004000 //rtcwmover(unused)
//q2areaportal,q3areaportal 0x00008000
#define FTECONTENTS_PLAYERCLIP 0x00010000
#define FTECONTENTS_MONSTERCLIP 0x00020000
diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c
index c66de8cfc..2e92546f8 100644
--- a/engine/common/com_mesh.c
+++ b/engine/common/com_mesh.c
@@ -1010,16 +1010,6 @@ static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float
returns the up-to-8 skeletal bone poses to blend together.
return value is the number of blends that are actually live.
*/
-typedef struct
-{
- skeltype_t skeltype; //the skeletal type of this bone block. all blocks should have the same result or the whole thing is unusable or whatever.
- int firstbone; //first bone of interest
- int endbone; //the first bone of the next group (ie: if first is 0, this is the count)
- int lerpcount; //number of pose+frac entries.
- float frac[FRAME_BLENDS*2]; //weight of this animation (1 if lerpcount is 1)
- float *pose[FRAME_BLENDS*2]; //pointer to the raw frame data for bone 0.
- void *needsfree[FRAME_BLENDS*2];
-} skellerps_t;
static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, const struct framestateregion_s *fs, int numbones, const galiasinfo_t *inf)
{
int frame1; //signed, because frametime might be negative...
@@ -1163,6 +1153,9 @@ static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate,
if (lastbone > inf->numbones)
lastbone = inf->numbones;
+ if (inf->AnimateBones)
+ return inf->AnimateBones(inf, fstate, lerps, firstbone, lastbone);
+
for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++)
{
endbone = fstate->g[bonegroup].endbone;
@@ -1467,12 +1460,17 @@ static void Alias_BuildGPUWeights(model_t *mod, galiasinfo_t *inf, size_t num_tr
basepose = inf->baseframeofs;
if (!basepose)
{
- if (!inf->numanimations || !inf->ofsanimations[0].boneofs)
+ skellerps_t lerps[FS_COUNT];
+ framestate_t fs = {0};
+ fs.g[FST_BASE].lerpweight[0] = 1;
+ fs.g[FS_REG].lerpweight[0] = 1;
+ if (!Alias_FindRawSkelData(inf, &fs, lerps, 0, inf->numbones)) //lets see what we get! we'll just blindly use the first pose.
{
Con_DPrintf("Alias_BuildGPUWeights: no base pose\n");
return; //its fucked jim
}
- basepose = Alias_ConvertBoneData(inf->ofsanimations[0].skeltype, inf->ofsanimations[0].boneofs, inf->numbones, inf->ofsbones, SKEL_ABSOLUTE, buffer, bufferalt, MAX_BONES);
+
+ basepose = Alias_ConvertBoneData(lerps->skeltype, lerps->pose[0], inf->numbones, inf->ofsbones, SKEL_ABSOLUTE, buffer, bufferalt, MAX_BONES);
}
//make sure we have bone inversions
@@ -6327,10 +6325,1027 @@ static qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize)
+#ifdef MODELFMT_MDC
+typedef struct {
+ char name[64]; // tag name
+} mdcTagName_t;
+
+#define MDC_TAG_ANGLE_SCALE ( 360.0 / 32700.0 )
+
+typedef struct {
+ short xyz[3];
+ short angles[3];
+} mdcTag_t;
+
+/*
+** mdcSurface_t
+**
+** CHUNK SIZE
+** header sizeof( md3Surface_t )
+** shaders sizeof( md3Shader_t ) * numShaders
+** triangles[0] sizeof( md3Triangle_t ) * numTriangles
+** st sizeof( md3St_t ) * numVerts
+** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numBaseFrames
+** XyzCompressed sizeof( mdcXyzCompressed ) * numVerts * numCompFrames
+** frameBaseFrames sizeof( short ) * numFrames
+** frameCompFrames sizeof( short ) * numFrames (-1 if frame is a baseFrame)
+*/
+typedef struct {
+ int ident; //
+
+ char name[64]; // polyset name
+
+ int flags;
+ int numCompFrames; // all surfaces in a model should have the same
+ int numBaseFrames; // ditto
+
+ int numShaders; // all surfaces in a model should have the same
+ int numVerts;
+
+ int numTriangles;
+ int ofsTriangles;
+
+ int ofsShaders; // offset from start of md3Surface_t
+ int ofsSt; // texture coords are common for all frames
+ int ofsXyzNormals; // md3XyzNormal_t[numVerts * numBaseFrames]
+ int ofsXyzCompressed; // int[numVerts * numCompFrames]
+
+ int ofsFrameBaseFrames; // short[numFrames], frame index into the xyznormals
+ int ofsFrameCompFrames; // short[numFrames], -1 means ignore, indexes to xyzcompressed.
+
+ int ofsEnd; // next surface follows
+} mdcSurface_t;
+
+typedef struct {
+ int ident;
+ int version;
+
+ char name[64]; // model name
+
+ int flags;
+
+ int numFrames;
+ int numTags;
+ int numSurfaces;
+
+ int numSkins;
+
+ int ofsFrames; // offset for first frame, stores the bounds and localOrigin
+ int ofsTagNames; // numTags
+ int ofsTags; // numFrames * numTags
+ int ofsSurfaces; // first surface, others follow
+
+ int ofsEnd; // end of file
+} mdcHeader_t;
+
+#define MDC_IDENT "IDPC",4
+static qboolean QDECL Mod_LoadMDCModel(model_t *mod, void *buffer, size_t fsize)
+{
+#ifndef SERVERONLY
+ galiasskin_t *skin;
+ skinframe_t *frames;
+ float lat, lng;
+ md3St_t *inst;
+ vec3_t *normals;
+ vec3_t *svector;
+ vec3_t *tvector;
+ vec2_t *st_array;
+ md3Shader_t *inshader;
+ int externalskins;
+#endif
+// int version;
+ int s, i, j, d;
+
+ index_t *indexes;
+
+ vec3_t min, max, tmp;
+
+ galiaspose_t *pose;
+ galiasinfo_t *parent, *root;
+ galiasanimation_t *group;
+
+ vecV_t *verts;
+
+ md3Triangle_t *intris;
+ md3XyzNormal_t *invert;
+ unsigned int *incompressed;
+
+
+ int size;
+
+ mdcHeader_t *header;
+ mdcSurface_t *surf;
+ galiasinfo_t *galias;
+ unsigned int numposes, numverts;
+
+ frameinfo_t *framegroups;
+ unsigned int numgroups;
+
+ header = buffer;
+
+ if (memcmp(&header->ident, MDC_IDENT))
+ return false;
+ if (LittleLong(header->version) != 2) //version with less dupe tag names.
+ return false;
+ parent = NULL;
+ root = NULL;
+#ifndef SERVERONLY
+ externalskins = Mod_CountSkinFiles(mod);
+#endif
+ min[0] = min[1] = min[2] = 0;
+ max[0] = max[1] = max[2] = 0;
+ framegroups = ParseFrameInfo(mod->name, &numgroups);
+
+ surf = (mdcSurface_t *)((qbyte *)header + LittleLong(header->ofsSurfaces));
+ for (s = 0; s < LittleLong(header->numSurfaces); s++)
+ {
+// if (LittleLong(surf->ident) != MDC_IDENT)
+// Con_Printf(CON_WARNING "Warning: mdc sub-surface doesn't match ident\n");
+
+ if (!framegroups)
+ numgroups = LittleLong(header->numFrames);
+ numposes = LittleLong(header->numFrames);
+ numverts = LittleLong(surf->numVerts);
+
+ size = sizeof(galiasinfo_t) + sizeof(galiasanimation_t)*numgroups;
+ galias = ZG_Malloc(&mod->memgroup, size);
+ Mod_DefaultMesh(galias, surf->name, s);
+ galias->ofsanimations = group = (galiasanimation_t*)(galias+1); //frame groups
+ galias->numanimations = numgroups;
+ galias->numverts = numverts;
+ galias->numindexes = LittleLong(surf->numTriangles)*3;
+ galias->shares_verts = s; //with itself, so no sharing.
+ if (parent)
+ parent->nextsurf = galias;
+ else
+ root = galias;
+ parent = galias;
+
+ //load the texcoords
+#ifndef SERVERONLY
+ st_array = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*galias->numindexes);
+ galias->ofs_st_array = st_array;
+ inst = (md3St_t*)((qbyte*)surf + LittleLong(surf->ofsSt));
+ for (i = 0; i < galias->numverts; i++)
+ {
+ st_array[i][0] = LittleFloat(inst[i].s);
+ st_array[i][1] = LittleFloat(inst[i].t);
+ }
+#endif
+
+ //load the index data
+ indexes = ZG_Malloc(&mod->memgroup, sizeof(*indexes)*galias->numindexes);
+ galias->ofs_indexes = indexes;
+ intris = (md3Triangle_t *)((qbyte*)surf + LittleLong(surf->ofsTriangles));
+ for (i = 0; i < LittleLong(surf->numTriangles); i++)
+ {
+ indexes[i*3+0] = LittleLong(intris[i].indexes[0]);
+ indexes[i*3+1] = LittleLong(intris[i].indexes[1]);
+ indexes[i*3+2] = LittleLong(intris[i].indexes[2]);
+ }
+
+ //figure out where we're putting the pose data
+ size = sizeof(galiaspose_t) + sizeof(vecV_t)*numverts;
+#ifndef SERVERONLY
+ size += 3*sizeof(vec3_t)*numverts;
+#endif
+ size *= numposes;
+ pose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size);
+ verts = (vecV_t*)(pose+numposes);
+#ifndef SERVERONLY
+ normals = (vec3_t*)(verts + numverts*numposes);
+ svector = (vec3_t*)(normals + numverts*numposes);
+ tvector = (vec3_t*)(svector + numverts*numposes);
+#endif
+
+ if (framegroups)
+ { //group the poses into animations.
+ for (i = 0; i < numgroups; i++)
+ {
+ int first = framegroups[i].firstpose, count = framegroups[i].posecount;
+ if (first >= numposes) //bound the numbers.
+ first = numposes-1;
+ if (first < 0)
+ first = 0;
+ if (count > numposes-first)
+ count = numposes-first;
+ if (count < 0)
+ count = 0;
+ Q_snprintfz(group->name, sizeof(group->name), "%s", framegroups[i].name);
+ group->numposes = count;
+ group->rate = framegroups[i].fps;
+ group->poseofs = pose + first;
+ group->loop = framegroups[i].loop;
+ group->events = NULL;
+ group++;
+ }
+ }
+ else
+ { //raw poses, no animations.
+ for (i = 0; i < numgroups; i++)
+ {
+ Q_snprintfz(group->name, sizeof(group->name), "frame%i", i);
+ group->numposes = 1;
+ group->rate = 1;
+ group->poseofs = pose + i;
+ group->loop = false;
+ group->events = NULL;
+ group++;
+ }
+ }
+
+ //load in that per-pose data
+ for (i = 0; i < numposes; i++)
+ {
+ invert = (md3XyzNormal_t *)((qbyte*)surf + LittleLong(surf->ofsXyzNormals));
+ invert += numverts * ((unsigned short*)((qbyte*)surf+surf->ofsFrameBaseFrames))[i];
+
+ j = ((short*)((qbyte*)surf+surf->ofsFrameCompFrames))[i];
+ if (j >= 0 && j < LittleLong(surf->numCompFrames))
+ { //we have compressed data for this frame
+ incompressed = (int*)((qbyte*)surf + LittleLong(surf->ofsXyzCompressed)) + numverts*j;
+
+ for (j = 0; j < numverts; j++)
+ {
+ d = incompressed[j];
+ tmp[0] = ( (float)( d & 255 ) - 127 );
+ tmp[1] = ( (float)( (d>>8) & 255 ) - 127 );
+ tmp[2] = ( (float)( (d>>16) & 255 ) - 127 );
+
+#ifndef SERVERONLY
+ #if 0 //FIXME, should be using this instead.
+ VectorCopy( ( r_anormals )[( d >> 24 )], normals[j] );
+ #else
+ lat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
+ lng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
+ normals[j][0] = cos ( lng ) * sin ( lat );
+ normals[j][1] = sin ( lng ) * sin ( lat );
+ normals[j][2] = cos ( lat );
+ #endif
+#endif
+ for (d = 0; d < 3; d++)
+ {
+ verts[j][d] = LittleShort(invert[j].xyz[d])/64.0f + tmp[d]*0.05;
+ if (verts[j][d]<min[d])
+ min[d] = verts[j][d];
+ if (verts[j][d]>max[d])
+ max[d] = verts[j][d];
+ }
+ }
+ }
+ else
+ {
+ for (j = 0; j < numverts; j++)
+ {
+#ifndef SERVERONLY
+ lat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
+ lng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
+ normals[j][0] = cos ( lng ) * sin ( lat );
+ normals[j][1] = sin ( lng ) * sin ( lat );
+ normals[j][2] = cos ( lat );
+#endif
+ for (d = 0; d < 3; d++)
+ {
+ verts[j][d] = LittleShort(invert[j].xyz[d])/64.0f;
+ if (verts[j][d]<min[d])
+ min[d] = verts[j][d];
+ if (verts[j][d]>max[d])
+ max[d] = verts[j][d];
+ }
+ }
+ }
+
+ pose->scale[0] = 1;
+ pose->scale[1] = 1;
+ pose->scale[2] = 1;
+
+ pose->scale_origin[0] = 0;
+ pose->scale_origin[1] = 0;
+ pose->scale_origin[2] = 0;
+
+ invert += LittleLong(surf->numVerts);
+
+ pose->ofsverts = verts;
+ verts += numverts;
+#ifndef SERVERONLY
+ pose->ofsnormals = normals;
+ normals += numverts;
+ pose->ofssvector = svector;
+ svector += numverts;
+ pose->ofstvector = tvector;
+ tvector += numverts;
+#endif
+ pose++;
+ }
+
+#ifndef SERVERONLY
+ if (externalskins<LittleLong(surf->numShaders))
+ externalskins = LittleLong(surf->numShaders);
+ if (externalskins)
+ {
+ skin = ZG_Malloc(&mod->memgroup, (externalskins)*((sizeof(galiasskin_t)+sizeof(skinframe_t))));
+ galias->ofsskins = skin;
+ frames = (skinframe_t *)(skin + externalskins);
+ inshader = (md3Shader_t *)((qbyte *)surf + LittleLong(surf->ofsShaders));
+ for (i = 0; i < externalskins; i++)
+ {
+ skin->numframes = 1;
+ skin->frame = &frames[i];
+ skin->skinwidth = 0;
+ skin->skinheight = 0;
+ skin->skinspeed = 0;
+
+ if (i >= LittleLong(surf->numShaders))
+ Q_strncpyz(frames->shadername, "", sizeof(frames->shadername)); //this shouldn't be possible
+ else
+ Q_strncpyz(frames->shadername, inshader->name, sizeof(frames->shadername));
+
+ inshader++;
+ skin++;
+ }
+ galias->numskins = i;
+ }
+#endif
+
+ VectorCopy(min, mod->mins);
+ VectorCopy(max, mod->maxs);
+
+
+ Mod_CompileTriangleNeighbours (mod, galias);
+ Mod_BuildTextureVectors(galias);
+
+ surf = (mdcSurface_t *)((qbyte *)surf + LittleLong(surf->ofsEnd));
+ }
+
+ if (!root)
+ {
+ root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t));
+ Mod_DefaultMesh(root, mod->name, 0);
+ }
+
+ root->numtagframes = LittleLong(header->numFrames);
+ root->numtags = LittleLong(header->numTags);
+ root->ofstags = ZG_Malloc(&mod->memgroup, root->numtags*sizeof(md3tag_t)*root->numtagframes);
+
+ {
+ mdcTagName_t *srcname;
+ mdcTag_t *src;
+ md3tag_t *dst;
+ int f;
+ vec3_t ang;
+
+ src = (mdcTag_t *)((char*)header+LittleLong(header->ofsTags));
+ dst = root->ofstags;
+ for (f = 0; f < root->numtagframes; f++)
+ {
+ srcname = (mdcTagName_t *)((char*)header+LittleLong(header->ofsTagNames));
+ for(i=0;i<root->numtags;i++)
+ {
+ memcpy(dst->name, srcname->name, sizeof(dst->name));
+ for(j=0;j<3;j++)
+ {
+ dst->org[j] = LittleShort(src->xyz[j])/64.0;
+ ang[j] = LittleShort(src->angles[j])*MDC_TAG_ANGLE_SCALE;
+ }
+ AngleVectorsFLU(ang, dst->ang[0], dst->ang[1], dst->ang[2]);
+
+ srcname++;
+ src++;
+ dst++;
+ }
+ }
+ }
+
+#ifndef SERVERONLY
+ if (mod_md3flags.value)
+ mod->flags = LittleLong(header->flags);
+ else
+#endif
+ mod->flags = 0;
+ if (!mod->flags)
+ mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);
+
+ Mod_ClampModelSize(mod);
+ Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);
+
+ mod->type = mod_alias;
+ mod->numframes = root->numanimations;
+ mod->meshinfo = root;
+
+ mod->funcs.NativeTrace = Mod_Trace;
+
+ return true;
+}
+#endif
+
+
+
+
+#ifdef MODELFMT_MDS
+#define MDS_IDENT "MDSW",4
+#define MDS_VERSION 4
+//#define MDS_MAX_VERTS 6000
+//#define MDS_MAX_TRIANGLES 8192
+//#define MDS_MAX_BONES 128
+//#define MDS_MAX_SURFACES 32
+//#define MDS_MAX_TAGS 128
+
+#define MDS_TRANSLATION_SCALE ( 1.0 / 64 )
+
+typedef struct {
+ int boneIndex; // these are indexes into the boneReferences,
+ float boneWeight; // not the global per-frame bone list
+ vec3_t offset;
+} mdsWeight_t;
+
+typedef struct {
+ vec3_t normal;
+ vec2_t texCoords;
+ int numWeights;
+ int fixedParent; // stay equi-distant from this parent
+ float fixedDist;
+ mdsWeight_t weights[1]; // variable sized
+} mdsVertex_t;
+
+typedef struct {
+ int indexes[3];
+} mdsTriangle_t;
+
+typedef struct {
+ int ident;
+
+ char name[64]; // polyset name
+ char shader[64];
+ int shaderIndex; // for in-game use
+
+ int minLod;
+
+ int ofsHeader; // this will be a negative number
+
+ int numVerts;
+ int ofsVerts;
+
+ int numTriangles;
+ int ofsTriangles;
+
+ int ofsCollapseMap; // numVerts * int
+
+ // Bone references are a set of ints representing all the bones
+ // present in any vertex weights for this surface. This is
+ // needed because a model may have surfaces that need to be
+ // drawn at different sort times, and we don't want to have
+ // to re-interpolate all the bones for each surface.
+ int numBoneReferences;
+ int ofsBoneReferences;
+
+ int ofsEnd; // next surface follows
+} mdsSurface_t;
+
+typedef struct {
+ short angles[3]; // to be converted to axis at run-time (this is also better for lerping)
+ short angles_3; // junk
+ short ofsAngles[2]; // PITCH/YAW, head in this direction from parent to go to the offset position
+} mdsBoneFrameCompressed_t;
+
+typedef struct {
+ vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame
+ vec3_t localOrigin; // midpoint of bounds, used for sphere cull
+ float radius; // dist from localOrigin to corner
+ vec3_t parentOffset; // one bone is an ascendant of all other bones, it starts the hierachy at this position
+ mdsBoneFrameCompressed_t bones[1]; // [numBones]
+} mdsFrame_t;
+
+typedef struct {
+ char name[64]; // name of tag
+ float torsoWeight;
+ int boneIndex; // our index in the bones
+} mdsTag_t;
+
+#define BONEFLAG_TAG 1 // this bone is actually a tag
+
+typedef struct {
+ char name[64]; // name of bone
+ int parent; // not sure if this is required, no harm throwing it in
+ float torsoWeight; // scale torso rotation about torsoParent by this
+ float parentDist;
+ int flags;
+} mdsBoneInfo_t;
+
+typedef struct {
+ int ident;
+ int version;
+
+ char name[64]; // model name
+
+ float lodScale;
+ float lodBias;
+
+ // frames and bones are shared by all levels of detail
+ int numFrames;
+ int numBones;
+ int ofsFrames; // mdsFrame_t[numFrames]
+ int ofsBones; // mdsBoneInfo_t[numBones]
+ int torsoParent; // index of bone that is the parent of the torso
+
+ int numSurfaces;
+ int ofsSurfaces;
+
+ // tag data
+ int numTags;
+ int ofsTags; // mdsTag_t[numTags]
+
+ int ofsEnd; // end of file
+} mdsHeader_t;
+
+typedef float matrix12[12];
+struct mds_bones_s
+{
+ const struct mdsBoneInfo2_s
+ {
+ short parent; //redundant, but simpler.
+ short flags;
+ float parentdist; //anim displacements are expressed as pitch+yaw with fixed distance.
+ float torsoweight; //individual bones decide blend weights.
+ } *bone;
+
+ const mdsFrame_t *bf[2];
+ const mdsFrame_t *tf[2];
+ matrix12 *pose;
+
+ float bfrac;
+ float tfrac;
+};
+static void MDS_CalcRelativeBone(struct mds_bones_s *info, size_t idx)
+{
+// if (calced[bone])
+// return;
+// MDS_CalcRelativeBone(parent);
+
+
+ vec3_t axis[3];
+ short ang1[3], ang2[3];
+ vec3_t ang;
+
+ const struct mdsBoneInfo2_s *bone = info->bone+idx;
+ float *__restrict opose = info->pose[idx];
+
+ const mdsBoneFrameCompressed_t *bf[2] = {info->bf[0]->bones+idx, info->bf[1]->bones+idx};
+ const mdsBoneFrameCompressed_t *tf[2] = {info->tf[0]->bones+idx, info->tf[1]->bones+idx};
+
+#define MDS_AngleBlend(a,f,b,r) r = a + (short)(b-a)*f
+#define MDS_Angle2Blend(a,f,b,r) MDS_AngleBlend(a[0],f,b[0],r[0]),MDS_AngleBlend(a[1],f,b[1],r[1])
+#define MDS_Angle3Blend(a,f,b,r) MDS_AngleBlend(a[0],f,b[0],r[0]),MDS_AngleBlend(a[1],f,b[1],r[1]),MDS_AngleBlend(a[2],f,b[2],r[2])
+
+ //we're blending two frames here, between two regions, so 4-way lerps.
+ //we use base + diff*frac, because it wraps better.
+ if (bone->torsoweight)
+ {
+ if (bone->torsoweight == 1) //torso angles only.
+ MDS_Angle3Blend(tf[0]->angles, info->tfrac,tf[1]->angles, ang);
+ else
+ { //ang = torso
+ MDS_Angle3Blend(bf[0]->angles, info->bfrac,bf[1]->angles, ang1);
+ MDS_Angle3Blend(tf[0]->angles, info->tfrac,tf[1]->angles, ang2);
+ MDS_Angle3Blend(ang1, bone->torsoweight,ang2, ang);
+ }
+ }
+ else //legs angles only.
+ MDS_Angle3Blend(bf[0]->angles, info->bfrac,bf[1]->angles, ang);
+ ang[0] = SHORT2ANGLE(ang[0]);
+ ang[1] = SHORT2ANGLE(ang[1]);
+ ang[2] = SHORT2ANGLE(ang[2]);
+ AngleVectorsFLU(ang, axis[0], axis[1], axis[2]);
+
+ opose[0] = axis[0][0];
+ opose[1] = axis[0][1];
+ opose[2] = axis[0][2];
+ opose[4] = axis[1][0];
+ opose[5] = axis[1][1];
+ opose[6] = axis[1][2];
+ opose[8] = axis[2][0];
+ opose[9] = axis[2][1];
+ opose[10]= axis[2][2];
+
+ if (bone->parent == -1)
+ { //no parent? just use the frame's offset...
+ FloatInterpolate(info->bf[0]->parentOffset[0], info->bfrac, info->bf[1]->parentOffset[0], opose[3]);
+ FloatInterpolate(info->bf[0]->parentOffset[1], info->bfrac, info->bf[1]->parentOffset[1], opose[7]);
+ FloatInterpolate(info->bf[0]->parentOffset[2], info->bfrac, info->bf[1]->parentOffset[2], opose[11]);
+
+ //tfrac? bone->torsoWeight?
+ }
+ else
+ {
+ const float *__restrict ppose = info->pose[bone->parent];
+ ang[2] = 0;
+ if (bone->torsoweight)
+ {
+ if (bone->torsoweight == 1)
+ {
+ ang[0] = SHORT2ANGLE(tf[0]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(tf[0]->ofsAngles[1]);
+ AngleVectors(ang, axis[1], NULL, NULL);
+ ang[0] = SHORT2ANGLE(tf[1]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(tf[1]->ofsAngles[1]);
+ AngleVectors(ang, axis[2], NULL, NULL);
+ VectorInterpolate(axis[1], info->bfrac, axis[2], axis[0]);
+ VectorNormalize(axis[0]);
+ }
+ else
+ {
+ ang[0] = SHORT2ANGLE(tf[0]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(tf[0]->ofsAngles[1]);
+ AngleVectors(ang, axis[1], NULL, NULL);
+ ang[0] = SHORT2ANGLE(tf[1]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(tf[1]->ofsAngles[1]);
+ AngleVectors(ang, axis[2], NULL, NULL);
+ VectorInterpolate(axis[1], info->bfrac, axis[2], axis[0]);
+ VectorNormalize(axis[0]);
+
+ ang[0] = SHORT2ANGLE(bf[0]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(bf[0]->ofsAngles[1]);
+ AngleVectors(ang, axis[1], NULL, NULL);
+ ang[0] = SHORT2ANGLE(bf[1]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(bf[1]->ofsAngles[1]);
+ AngleVectors(ang, axis[2], NULL, NULL);
+ VectorInterpolate(axis[1], info->bfrac, axis[2], axis[0]);
+ VectorNormalize(axis[0]);
+ }
+ }
+ else
+ {
+ ang[0] = SHORT2ANGLE(bf[0]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(bf[0]->ofsAngles[1]);
+ AngleVectors(ang, axis[1], NULL, NULL);
+ ang[0] = SHORT2ANGLE(bf[1]->ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(bf[1]->ofsAngles[1]);
+ AngleVectors(ang, axis[2], NULL, NULL);
+ VectorInterpolate(axis[1], info->bfrac, axis[2], axis[0]);
+ VectorNormalize(axis[0]);
+ }
+ opose[3] = ppose[3] + axis[0][0] * bone->parentdist;
+ opose[7] = ppose[7] + axis[0][1] * bone->parentdist;
+ opose[11] = ppose[11] + axis[0][2] * bone->parentdist;
+ }
+}
+//called from Alias_FindRawSkelData
+static int QDECL MDS_WeirdBoneHack(const galiasinfo_t *inf, const framestate_t *fstate, skellerps_t *lerps, size_t firstbone, size_t lastbone)
+{
+ //this is annoying.
+ //mds bakes which framegroup each bone scales from into the bones themselves.
+ //the bones also have a habit of not properly rotating relative to their parent
+
+ struct mds_bones_s info;
+
+ size_t b;
+ firstbone = 0; //bah...
+
+ lerps->firstbone = firstbone;
+ lerps->endbone = lastbone;
+ lerps->skeltype = SKEL_ABSOLUTE;
+ lerps->lerpcount = 1;
+ lerps->frac[0] = 1;
+ info.pose = (matrix12*)(lerps->pose[0] = lerps->needsfree[0] = Z_Malloc(sizeof(matrix12)*(lastbone-firstbone)));
+ info.bone = inf->userptr;
+ //base animation info that the mod should always be setting.
+ info.bf[0] = inf->ofsanimations[(unsigned)fstate->g[FS_REG].frame[0]%inf->numanimations].boneofs;
+ info.bf[1] = inf->ofsanimations[(unsigned)fstate->g[FS_REG].frame[1]%inf->numanimations].boneofs;
+ info.bfrac = fstate->g[FS_REG].lerpweight[1];
+ //base/torso info is swapped around, because weirdness when the gamecode gets lazy.
+ info.tf[0] = inf->ofsanimations[(unsigned)fstate->g[FST_BASE].frame[0]%inf->numanimations].boneofs;
+ info.tf[1] = inf->ofsanimations[(unsigned)fstate->g[FST_BASE].frame[1]%inf->numanimations].boneofs;
+ info.tfrac = fstate->g[FST_BASE].lerpweight[1];
+
+ for (b = firstbone; b < lastbone; b++)
+ MDS_CalcRelativeBone(&info, b);
+
+ if (inf->meshrootbone != -1)
+ {
+ vec3_t torsoaxis[3];
+ vec3_t parentorg;
+ matrix12 t, w;//, t2;
+// size_t i;
+ float wscale = -2;
+ MDS_CalcRelativeBone(&info, inf->meshrootbone);
+ VectorSet(parentorg, info.pose[inf->meshrootbone][3], info.pose[inf->meshrootbone][7], info.pose[inf->meshrootbone][11]);
+
+ AngleVectorsFLU(fstate->bonecontrols, torsoaxis[0],torsoaxis[1],torsoaxis[2]);
+
+ for (b = firstbone; b < lastbone; b++)
+ {
+ if (!info.bone[b].torsoweight)
+ continue; //bone is fine as it is.
+
+ memcpy(t, info.pose[b], sizeof(t)); //gotta copy it cos matmult requires unaliased dest
+ if (info.bone[b].flags&1)
+ {
+ float s=info.bone[b].torsoweight, is=1-s;
+ //make it relative to the root torso bone. rotations are not a consideration, for some reason.
+ t[3] -= parentorg[0];
+ t[7] -= parentorg[1];
+ t[11]-= parentorg[2];
+
+ info.pose[b][0] = t[ 0]*is + s*(t[ 0]*torsoaxis[0][0] + t[ 1]*torsoaxis[1][0] + t[ 2]*torsoaxis[2][0]);
+ info.pose[b][1] = t[ 1]*is + s*(t[ 0]*torsoaxis[0][1] + t[ 1]*torsoaxis[1][1] + t[ 2]*torsoaxis[2][1]);
+ info.pose[b][2] = t[ 2]*is + s*(t[ 0]*torsoaxis[0][2] + t[ 1]*torsoaxis[1][2] + t[ 2]*torsoaxis[2][2]);
+ info.pose[b][4] = t[ 4]*is + s*(t[ 4]*torsoaxis[0][0] + t[ 5]*torsoaxis[1][0] + t[ 6]*torsoaxis[2][0]);
+ info.pose[b][5] = t[ 5]*is + s*(t[ 4]*torsoaxis[0][1] + t[ 5]*torsoaxis[1][1] + t[ 6]*torsoaxis[2][1]);
+ info.pose[b][6] = t[ 6]*is + s*(t[ 4]*torsoaxis[0][2] + t[ 5]*torsoaxis[1][2] + t[ 6]*torsoaxis[2][2]);
+ info.pose[b][8] = t[ 8]*is + s*(t[ 8]*torsoaxis[0][0] + t[ 9]*torsoaxis[1][0] + t[10]*torsoaxis[2][0]);
+ info.pose[b][9] = t[ 9]*is + s*(t[ 8]*torsoaxis[0][1] + t[ 9]*torsoaxis[1][1] + t[10]*torsoaxis[2][1]);
+ info.pose[b][10]= t[10]*is + s*(t[ 8]*torsoaxis[0][2] + t[ 9]*torsoaxis[1][2] + t[10]*torsoaxis[2][2]);
+ info.pose[b][3] = t[ 3]*is + s*(t[ 3]*torsoaxis[0][0] + t[ 7]*torsoaxis[1][0] + t[11]*torsoaxis[2][0]);
+ info.pose[b][7] = t[ 7]*is + s*(t[ 3]*torsoaxis[0][1] + t[ 7]*torsoaxis[1][1] + t[11]*torsoaxis[2][1]);
+ info.pose[b][11]= t[11]*is + s*(t[ 3]*torsoaxis[0][2] + t[ 7]*torsoaxis[1][2] + t[11]*torsoaxis[2][2]);
+
+ info.pose[b][3] += parentorg[0];
+ info.pose[b][7] += parentorg[1];
+ info.pose[b][11]+= parentorg[2];
+ }
+ else
+ {
+ //make it relative to the root torso bone. rotations are not a consideration, for some reason.
+ t[3] -= parentorg[0];
+ t[7] -= parentorg[1];
+ t[11]-= parentorg[2];
+
+ if (wscale != info.bone[b].torsoweight)
+ { //don't need to rebuild if multiple bones have the same weights.
+ wscale = info.bone[b].torsoweight;
+ w[0] = torsoaxis[0][0]*wscale + (1-wscale);
+ w[1] = torsoaxis[1][0]*wscale;
+ w[2] = torsoaxis[2][0]*wscale;
+ w[3] = parentorg[0]; //base position we're rotating the bone around.
+ w[4] = torsoaxis[0][1]*wscale;
+ w[5] = torsoaxis[1][1]*wscale + (1-wscale);
+ w[6] = torsoaxis[2][1]*wscale;
+ w[7] = parentorg[1];
+ w[8] = torsoaxis[0][2]*wscale;
+ w[9] = torsoaxis[1][2]*wscale;
+ w[10]= torsoaxis[2][2]*wscale + (1-wscale);
+ w[11]= parentorg[2];
+ }
+ Matrix3x4_Multiply(t, w, info.pose[b]);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static qboolean QDECL Mod_LoadMDSModel(model_t *mod, void *buffer, size_t fsize)
+{
+ size_t s, i, j, framesize;
+ mdsHeader_t hdr;
+
+ galiasbone_t *bones;
+ galiasinfo_t *surf;
+#ifndef SERVERONLY
+ galiasskin_t *skins;
+ skinframe_t *skinframes;
+#endif
+ galisskeletaltransforms_t *transforms, *tr;
+ index_t *idx;
+ galiasanimation_t *anims;
+ const mdsVertex_t *v;
+ const mdsSurface_t *isurf;
+ const mdsFrame_t *iframe;
+ mdsFrame_t *mframe;
+ const int *iidx;
+ const mdsBoneInfo_t *ibone;
+ struct mdsBoneInfo2_s *mbone;
+
+ if (memcmp(buffer, MDS_IDENT))
+ {
+ Con_Printf(CON_ERROR "Mod_LoadMDSModel: %s is not an MDS file\n", mod->name);
+ return false;
+ }
+ hdr.version = LittleLong(((mdsHeader_t*)buffer)->version);
+ if (hdr.version != MDS_VERSION)
+ {
+ Con_Printf(CON_ERROR "Mod_LoadMDSModel: %s is unsupported version %i\n", mod->name, hdr.version);
+ return false;
+ }
+
+ hdr.lodScale = LittleFloat(((mdsHeader_t*)buffer)->lodScale);
+ hdr.lodBias = LittleFloat(((mdsHeader_t*)buffer)->lodBias);
+ hdr.numFrames = LittleLong(((mdsHeader_t*)buffer)->numFrames);
+ hdr.numBones = LittleLong(((mdsHeader_t*)buffer)->numBones);
+ hdr.ofsFrames = LittleLong(((mdsHeader_t*)buffer)->ofsFrames);
+ hdr.ofsBones = LittleLong(((mdsHeader_t*)buffer)->ofsBones);
+ hdr.torsoParent = LittleLong(((mdsHeader_t*)buffer)->torsoParent);
+ hdr.numSurfaces = LittleLong(((mdsHeader_t*)buffer)->numSurfaces);
+ hdr.ofsSurfaces = LittleLong(((mdsHeader_t*)buffer)->ofsSurfaces);
+ hdr.numTags = LittleLong(((mdsHeader_t*)buffer)->numTags);
+ hdr.ofsTags = LittleLong(((mdsHeader_t*)buffer)->ofsTags);
+ hdr.ofsEnd = LittleLong(((mdsHeader_t*)buffer)->ofsEnd);
+ if (hdr.ofsEnd != fsize)
+ {
+ Con_Printf(CON_ERROR "Mod_LoadMDSModel: %s truncated\n", mod->name);
+ return false;
+ }
+
+ if (hdr.numSurfaces < 1 || hdr.numFrames < 1 || hdr.numBones < 1)
+ {
+ Con_Printf(CON_ERROR "Mod_LoadMDSModel: %s has %i surfaces, %i frames, %i bones\n", mod->name, hdr.numSurfaces, hdr.numFrames, hdr.numBones);
+ return false;
+ }
+
+ bones = ZG_Malloc(&mod->memgroup, sizeof(*bones)*hdr.numBones);
+ mbone = ZG_Malloc(&mod->memgroup, sizeof(*mbone)*hdr.numBones);
+ ibone = (const mdsBoneInfo_t*)((const qbyte*)buffer + hdr.ofsBones);
+ for (i = 0; i < hdr.numBones; i++)
+ {
+ Q_strncpyz(bones[i].name, ibone[i].name, sizeof(bones[i].name));
+ mbone[i].parent = bones[i].parent = LittleLong(ibone[i].parent);
+ //bones[i].inverse will be calculated by Alias_BuildGPUWeights
+
+ //and our mds-specific bone info, required for generating weird bone matricies.
+ mbone[i].flags = LittleLong(ibone[i].flags);
+ mbone[i].parentdist = LittleFloat(ibone[i].parentDist);
+ mbone[i].torsoweight = LittleFloat(ibone[i].torsoWeight);
+ }
+
+ ClearBounds(mod->mins, mod->maxs);
+ anims = ZG_Malloc(&mod->memgroup, sizeof(*anims)*hdr.numFrames);
+// poses = ZG_Malloc(&mod->memgroup, sizeof(*poses)*12*hdr.numBones*hdr.numFrames);
+ iframe = (const mdsFrame_t*)((const qbyte*)buffer + hdr.ofsFrames);
+ framesize = sizeof(*iframe)-sizeof(iframe->bones) + hdr.numBones * sizeof(iframe->bones[0]);
+ mframe = ZG_Malloc(&mod->memgroup, hdr.numFrames*framesize);
+
+
+ for (i = 0; i < hdr.numFrames; i++, iframe = (const mdsFrame_t*)((const qbyte*)iframe + framesize), mframe = (mdsFrame_t*)((qbyte*)mframe + framesize))
+ {
+ anims[i].skeltype = SKEL_ABSOLUTE;
+ anims[i].loop = false;
+ anims[i].numposes = 1;
+ anims[i].rate = 10;
+ anims[i].events = NULL;
+ Q_snprintfz(anims[i].name, sizeof(anims[i].name), "f%u", (unsigned)i);
+
+ (void)iframe->localOrigin; //for culling
+ (void)iframe->radius; //for culling
+
+ AddPointToBounds(iframe->bounds[0], mod->mins, mod->maxs);
+ AddPointToBounds(iframe->bounds[1], mod->mins, mod->maxs);
+
+ //byteswap the stuff we'll actually need.
+ mframe->parentOffset[0] = LittleFloat(iframe->parentOffset[0]);
+ mframe->parentOffset[1] = LittleFloat(iframe->parentOffset[1]);
+ mframe->parentOffset[2] = LittleFloat(iframe->parentOffset[2]);
+ for (j = 0; j < hdr.numBones; j++)
+ {
+ mframe->bones[j].angles[0] = LittleShort(iframe->bones[j].angles[0]);
+ mframe->bones[j].angles[1] = LittleShort(iframe->bones[j].angles[1]);
+ mframe->bones[j].angles[2] = LittleShort(iframe->bones[j].angles[2]);
+ mframe->bones[j].angles_3 = (i<<8)|j;
+ mframe->bones[j].ofsAngles[0] = LittleShort(iframe->bones[j].ofsAngles[0]);
+ mframe->bones[j].ofsAngles[1] = LittleShort(iframe->bones[j].ofsAngles[1]);
+ }
+ anims[i].boneofs = mframe;
+
+ /*
+ for (j = 0; j < hdr.numBones; j++, poses+=12)
+ {
+ vec3_t axis[3];
+ vec3_t ang;
+ ppose = (float*)anims[i].boneofs + ibone[j].parent*12;
+ ang[0] = SHORT2ANGLE(iframe->bones[j].angles[0]);
+ ang[1] = SHORT2ANGLE(iframe->bones[j].angles[1]);
+ ang[2] = SHORT2ANGLE(iframe->bones[j].angles[2]);
+ (void)iframe->bones[j].angles[3]; //junk...
+ AngleVectorsFLU(ang, axis[0], axis[1], axis[2]);
+
+ poses[0] = axis[0][0];
+ poses[1] = axis[0][1];
+ poses[2] = axis[0][2];
+ poses[4] = axis[1][0];
+ poses[5] = axis[1][1];
+ poses[6] = axis[1][2];
+ poses[8] = axis[2][0];
+ poses[9] = axis[2][1];
+ poses[10]= axis[2][2];
+
+ if (bones[j].parent == -1)
+ { //no parent? just use the frame's offset...
+ poses[3] = iframe->parentOffset[0];
+ poses[7] = iframe->parentOffset[1];
+ poses[11]= iframe->parentOffset[2];
+ }
+ else
+ {
+ ang[0] = SHORT2ANGLE(iframe->bones[j].ofsAngles[0]);
+ ang[1] = SHORT2ANGLE(iframe->bones[j].ofsAngles[1]);
+ ang[2] = 0;
+ AngleVectors(ang, axis[0], NULL, NULL);
+ poses[3] = ppose[3] + axis[0][0] * ibone[j].parentDist;
+ poses[7] = ppose[7] + axis[0][1] * ibone[j].parentDist;
+ poses[11] = ppose[11] + axis[0][2] * ibone[j].parentDist;
+ }
+ }
+ */
+ }
+
+ surf = ZG_Malloc(&mod->memgroup, sizeof(*surf)*hdr.numSurfaces);
+#ifndef SERVERONLY
+ skins = ZG_Malloc(&mod->memgroup, sizeof(*skins)*hdr.numSurfaces);
+ skinframes = ZG_Malloc(&mod->memgroup, sizeof(*skinframes)*hdr.numSurfaces);
+#endif
+ isurf = (const mdsSurface_t*)((const qbyte*)buffer + hdr.ofsSurfaces);
+ for (s = 0; s < hdr.numSurfaces; s++)
+ {
+ if (s)
+ surf[s-1].nextsurf = &surf[s];
+ (void)isurf->ident;
+ (void)isurf->ofsHeader;
+ (void)isurf->minLod;
+ (void)isurf->ofsCollapseMap; //this is some weird table that provides lod by skipping various triangles
+ (void)isurf->numBoneReferences;
+ (void)isurf->ofsBoneReferences; //to skip processing bones that are part of some other mesh.
+
+ surf[s].surfaceid = s;
+ surf[s].shares_bones = 0; //aka: does
+ surf[s].shares_verts = s; //aka: doesn't.
+ Q_strncpyz(surf[s].surfacename, isurf->name, sizeof(surf[s].surfacename));
+ surf[s].contents = FTECONTENTS_BODY;
+#ifndef SERVERONLY
+ surf[s].numskins = 1;
+ surf[s].ofsskins = &skins[s];
+ skins[s].numframes = 1;
+ skins[s].frame = &skinframes[s];
+ Q_strncpyz(skins[s].frame[0].shadername, isurf->shader, sizeof(skins[s].frame[0].shadername));
+#endif
+
+ surf[s].numbones = hdr.numBones;
+ surf[s].ofsbones = bones;
+ surf[s].numanimations = hdr.numFrames;
+ surf[s].ofsanimations = anims;
+
+ surf[s].AnimateBones = MDS_WeirdBoneHack;
+ surf[s].userptr = mbone;
+ surf[s].meshrootbone = hdr.torsoParent;
+
+ surf[s].numverts = LittleLong(isurf->numVerts);
+ //count the transforms needed
+ for (i = 0, j=0, v = (const mdsVertex_t*)((const qbyte*)isurf+LittleLong(isurf->ofsVerts)); i < surf[s].numverts; i++, v = (const mdsVertex_t*)&v->weights[LittleLong(v->numWeights)])
+ j += LittleLong(v->numWeights);
+ //then generate them
+ surf[s].ofs_st_array = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*surf[s].numverts);
+ transforms = tr = Z_Malloc(sizeof(*transforms)*j);
+ for (i = 0, v = (const mdsVertex_t*)((const qbyte*)isurf+LittleLong(isurf->ofsVerts)); i < surf[s].numverts; i++, v = (const mdsVertex_t*)&v->weights[LittleLong(v->numWeights)])
+ {
+ float w;
+ int n = LittleLong(v->numWeights);
+ (void)v->fixedParent; //no idea
+ (void)v->fixedDist; //no idea what that is meant to do
+
+ surf[s].ofs_st_array[i][0] = LittleFloat(v->texCoords[0]);
+ surf[s].ofs_st_array[i][1] = LittleFloat(v->texCoords[1]);
+ for (j = 0; j < n; j++)
+ {
+ tr->vertexindex = i;
+ tr->boneindex = LittleLong(v->weights[j].boneIndex);
+ w = LittleFloat(v->weights[j].boneWeight);
+ tr->org[0] = w*LittleFloat(v->weights[j].offset[0]);
+ tr->org[1] = w*LittleFloat(v->weights[j].offset[1]);
+ tr->org[2] = w*LittleFloat(v->weights[j].offset[2]);
+ tr->org[3] = w;
+#ifndef SERVERONLY
+ tr->normal[0] = w*v->normal[0]; //these are normally relative to only the first weight...
+ tr->normal[1] = w*v->normal[1];
+ tr->normal[2] = w*v->normal[2];
+#endif
+ tr++;
+ }
+ }
+ //and then convert those to gpu-friendly weights (also computes normals)
+ Alias_BuildGPUWeights(mod, &surf[s], tr-transforms, transforms, false);
+ Z_Free(transforms);
+
+ surf[s].numindexes = LittleLong(isurf->numTriangles)*3;
+ iidx = (const int*)((const qbyte*)isurf + LittleLong(isurf->ofsTriangles));
+ surf[s].ofs_indexes = idx = ZG_Malloc(&mod->memgroup, sizeof(*idx)*surf[s].numindexes);
+ for (i = 0; i < surf[s].numindexes; i++)
+ idx[i] = LittleLong(iidx[i]);
+
+ isurf = (const mdsSurface_t*)((const qbyte*)isurf + LittleLong(isurf->ofsEnd));
+ }
+
+ Mod_ClampModelSize(mod);
+ Mod_ParseModelEvents(mod, surf->ofsanimations, surf->numanimations);
+
+ mod->type = mod_alias;
+ mod->numframes = surf->numanimations;
+ mod->meshinfo = surf;
+
+ mod->funcs.NativeTrace = Mod_Trace;
+ return true;
+}
+#endif
@@ -10122,6 +11137,12 @@ void Alias_Register(void)
Mod_RegisterModelFormatText(NULL, "External Anim", "EXTERNALANIM", Mod_LoadCompositeAnim);
#endif
+#ifdef MODELFMT_MDC
+ Mod_RegisterModelFormatMagic(NULL, "RTCW Skeletal Models (mdc)", MDC_IDENT, Mod_LoadMDCModel);
+#endif
+#ifdef MODELFMT_MDS
+ Mod_RegisterModelFormatMagic(NULL, "RTCW Skeletal Models (mds)", MDS_IDENT, Mod_LoadMDSModel);
+#endif
#ifdef MODELFMT_OBJ
Mod_RegisterModelFormatText(NULL, "Wavefront Object (obj)", ".obj", Mod_LoadObjModel);
diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h
index 0bd02f274..000dd3367 100644
--- a/engine/common/com_mesh.h
+++ b/engine/common/com_mesh.h
@@ -93,6 +93,18 @@ typedef struct
vec3_t normal;
#endif
} galisskeletaltransforms_t;
+
+
+typedef struct
+{
+ skeltype_t skeltype; //the skeletal type of this bone block. all blocks should have the same result or the whole thing is unusable or whatever.
+ int firstbone; //first bone of interest
+ int endbone; //the first bone of the next group (ie: if first is 0, this is the count)
+ int lerpcount; //number of pose+frac entries.
+ float frac[FRAME_BLENDS*2]; //weight of this animation (1 if lerpcount is 1)
+ float *pose[FRAME_BLENDS*2]; //pointer to the raw frame data for bone 0.
+ void *needsfree[FRAME_BLENDS*2];
+} skellerps_t;
#endif
//we can't be bothered with animating skins.
@@ -183,7 +195,9 @@ typedef struct galiasinfo_s
unsigned int mappedbones;
unsigned int nummorphs; //extra data after the xyz/norm/stvect arrays
const float *(QDECL *AnimateMorphs)(const struct galiasinfo_s *surf, const framestate_t *framestate);
- int meshrootbone;
+ int (QDECL *AnimateBones)(const struct galiasinfo_s *surf, const framestate_t *fstate, skellerps_t *lerps/*[FS_COUNT]*/, size_t firstbone, size_t lastbone);
+ int meshrootbone; //for private use by callbacks
+ void *userptr; //for private use by callbacks
float *baseframeofs; /*non-heirachical*/
int numbones;
diff --git a/engine/common/common.h b/engine/common/common.h
index 62927ebf1..25be13d78 100644
--- a/engine/common/common.h
+++ b/engine/common/common.h
@@ -769,6 +769,8 @@ typedef struct
char *eula; //when running as an installer, the user will be presented with this as a prompt
char *rtcbroker; //the broker to use for webrtc connections.
char *basedir; //this is where we expect to find the data.
+ char *extrapaks; //additional paks to load straight after pakN.* (eg: sp_pak - loading sp_pak0.pk3 etc)
+ char *ignorepaks; //paks matching this pattern won't be loaded (eg: mp_*.* so that rtcwsp doesn't get confused)
char *iconname; //path we can find the icon (relative to the fmf's location)
char *schemes; //protocol scheme used to connect to a server running this game, use com_parse.
diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h
index 876692fb3..35899b162 100644
--- a/engine/common/config_fteqw.h
+++ b/engine/common/config_fteqw.h
@@ -86,6 +86,8 @@
#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets).
#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
+#define MODELFMT_MDC //RTCW's 'compressed md3' format
+#define MODELFMT_MDS //RTCW's somewhat lame skeletal format
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats
diff --git a/engine/common/fs.c b/engine/common/fs.c
index 6e6cdd52e..bcd5ed4d8 100644
--- a/engine/common/fs.c
+++ b/engine/common/fs.c
@@ -230,6 +230,8 @@ void FS_Manifest_Free(ftemanifest_t *man)
Z_Free(man->defaultoverrides);
Z_Free(man->rtcbroker);
Z_Free(man->basedir);
+ Z_Free(man->extrapaks);
+ Z_Free(man->ignorepaks);
Z_Free(man->iconname);
for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)
{
@@ -280,6 +282,10 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
newm->iconname = Z_StrDup(oldm->iconname);
if (oldm->basedir)
newm->basedir = Z_StrDup(oldm->basedir);
+ if (oldm->extrapaks)
+ newm->extrapaks = Z_StrDup(oldm->extrapaks);
+ if (oldm->ignorepaks)
+ newm->ignorepaks = Z_StrDup(oldm->ignorepaks);
if (oldm->mainconfig)
newm->mainconfig = Z_StrDup(oldm->mainconfig);
newm->homedirtype = oldm->homedirtype;
@@ -376,6 +382,10 @@ static void FS_Manifest_Print(ftemanifest_t *man)
Con_Printf("icon %s\n", COM_QuotedString(man->iconname, buffer, sizeof(buffer), false));
if (man->basedir)
Con_Printf("basedir %s\n", COM_QuotedString(man->basedir, buffer, sizeof(buffer), false));
+ if (man->extrapaks)
+ Con_Printf("extrapaks %s\n", COM_QuotedString(man->extrapaks, buffer, sizeof(buffer), false));
+ if (man->ignorepaks)
+ Con_Printf("ignorepaks %s\n", COM_QuotedString(man->ignorepaks, buffer, sizeof(buffer), false));
for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)
{
@@ -805,6 +815,10 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
Con_Printf("Too many game paths specified in manifest\n");
}
}
+ else if (!Q_strcasecmp(cmd, "extrapaks"))
+ Z_StrDupPtr(&man->extrapaks, Cmd_Argv(1));
+ else if (!Q_strcasecmp(cmd, "ignorepaks"))
+ Z_StrDupPtr(&man->ignorepaks, Cmd_Argv(1));
//FIXME: these should generate package-manager entries.
#ifdef HAVE_LEGACY
else if (!Q_strcasecmp(cmd, "filedependancies") || !Q_strcasecmp(cmd, "archiveddependancies"))
@@ -3134,6 +3148,11 @@ static void FS_AddSingleDataFile (const char *descriptor, wildpaks_t *param, sea
flocation_t loc;
unsigned int keptflags = 0;
+ //let manifest files specify files to ignore (required for rtcw)
+ if (fs_manifest->ignorepaks)
+ if (wildcmp(fs_manifest->ignorepaks, descriptor))
+ return;
+
Q_snprintfz (pakfile, sizeof(pakfile), "%s%s", param->parentdesc, descriptor);
for (search = com_searchpaths; search; search = search->next)
@@ -3418,7 +3437,7 @@ static void FS_AddDownloadManifestPackages(searchpath_t **oldpaths, unsigned int
static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpath_t *search, unsigned int pflags, unsigned int loadstuff)
{
//search is the parent
- int i, j;
+ int i, j, k;
searchpath_t *existing;
searchpathfuncs_t *handle;
char pakfile[MAX_OSPATH];
@@ -3487,50 +3506,56 @@ static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const
continue;
if (loadstuff & (1<<j))
{
+ const char *prefix[] = {"pak", fs_manifest->extrapaks};
qboolean okay = true;
const char *extension = searchpathformats[j].extension;
//first load all the numbered pak files
- for (i=0 ; okay ; i++)
+ for (k=0 ; k<countof(prefix); k++)
{
- snprintf (pakfile, sizeof(pakfile), "pak%i.%s", i, extension);
- fs_finds++;
- if (search->handle->FindFile(search->handle, &loc, pakfile, NULL))
+ if (!prefix[k])
+ continue;
+ for (i=0 ; okay; i++)
{
- snprintf (logicalfile, sizeof(logicalfile), "%spak%i.%s", logicalpaths, i, extension);
- snprintf (purefile, sizeof(purefile), "%s/pak%i.%s", purepath, i, extension);
-
- for (existing = com_searchpaths; existing; existing = existing->next)
- {
- if (!Q_strcasecmp(existing->logicalpath, logicalfile)) //assumption: first member of structure is a char array
- break; //already loaded (base paths?)
- }
- if (!existing)
+ snprintf (pakfile, sizeof(pakfile), "%s%i.%s", prefix[k], i, extension);
+ fs_finds++;
+ if (search->handle->FindFile(search->handle, &loc, pakfile, NULL))
{
- handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
- if (!handle)
+ snprintf (logicalfile, sizeof(logicalfile), "%s%s%i.%s", logicalpaths, prefix[k], i, extension);
+ snprintf (purefile, sizeof(purefile), "%s/%s%i.%s", purepath, prefix[k], i, extension);
+
+ for (existing = com_searchpaths; existing; existing = existing->next)
{
- vfs = search->handle->OpenVFS(search->handle, &loc, "rb");
- if (!vfs)
- break;
- handle = searchpathformats[j].OpenNew (vfs, search->handle, pakfile, logicalfile, "");
+ if (!Q_strcasecmp(existing->logicalpath, logicalfile)) //assumption: first member of structure is a char array
+ break; //already loaded (base paths?)
+ }
+ if (!existing)
+ {
+ handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
if (!handle)
- break;
+ {
+ vfs = search->handle->OpenVFS(search->handle, &loc, "rb");
+ if (!vfs)
+ break;
+ handle = searchpathformats[j].OpenNew (vfs, search->handle, pakfile, logicalfile, "");
+ if (!handle)
+ break;
+ }
+ FS_AddPathHandle(oldpaths, purefile, logicalfile, handle, "", SPF_COPYPROTECTED|pflags|keptflags, (unsigned int)-1);
}
- FS_AddPathHandle(oldpaths, purefile, logicalfile, handle, "", SPF_COPYPROTECTED|pflags|keptflags, (unsigned int)-1);
}
- }
- else
- okay = false;
+ else
+ okay = false;
- if (i == 0 && qshack)
- {
- snprintf (pakfile, sizeof(pakfile), "quakespasm.%s", extension);
- handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
- if (!handle)
- handle = FS_OpenPackByExtension(VFSOS_Open(pakfile, "rb"), NULL, pakfile, pakfile);
- if (handle) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth
- FS_AddPathHandle(oldpaths, "", pakfile, handle, "", SPF_COPYPROTECTED|SPF_PRIVATE, (unsigned int)-1);
+ if (i == 0 && k == 0 && qshack)
+ {
+ snprintf (pakfile, sizeof(pakfile), "quakespasm.%s", extension);
+ handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
+ if (!handle)
+ handle = FS_OpenPackByExtension(VFSOS_Open(pakfile, "rb"), NULL, pakfile, pakfile);
+ if (handle) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth
+ FS_AddPathHandle(oldpaths, "", pakfile, handle, "", SPF_COPYPROTECTED|SPF_PRIVATE, (unsigned int)-1);
+ }
}
}
}
@@ -4010,6 +4035,10 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 1\nset r_clearcolour 0 0 0\nset com_parseutf8 0\ngl_overbright "FORWEB("0","2")"\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_gamedirnativecode 1\nsv_port "STRINGIFY(PORT_Q3SERVER)"\ncl_defaultport "STRINGIFY(PORT_Q3SERVER)"\ncom_protocolversion 68\n"
//#define RMQCFG "sv_bigcoords 1\n"
+#define RTCWCFG "set v_gammainverted 0\nset com_parseutf8 0\ngl_overbright 2\nscr_loadingscreen_aspect 2\nr_shadow_realtime_dlight_shadows 0\nr_coronas 1\nr_coronas_occlusion 1\nsetfl sex male ua\nsetfl head default ua\nsetfl cg_autoactivate 1 ua\nsetfl cg_autoactivate 1 ua\nsetfl cg_predictItems 1 ua\nsetfl cg_emptyswitch 0 ua\nseta handicap 100\ncom_gamedirnativecode 1\nr_replacemodels iqm mds md3 mdc\nsv_port "STRINGIFY(PORT_Q3SERVER)"\ncl_defaultport "STRINGIFY(PORT_Q3SERVER)"\n"
+#define RTCWSPCFG RTCWCFG "seta model bj2\nset g_sp_botcount 64\ncom_protocolversion 49\n"
+#define RTCWMPCFG RTCWCFG "seta model multi\ncon_notifytime 7\nsetfl g_gametype 5 sa\nsetfl cg_autoReload 1 ua\ncom_protocolversion 60\n"
+
#ifndef UPDATEURL
#ifdef HAVE_SSL
#define UPDATEURL(g) "/downloadables.php?game=" #g
@@ -4117,8 +4146,13 @@ static const gamemode_info_t gamemode_info[] = {
#endif
#if defined(Q3CLIENT) || defined(Q3SERVER)
- {"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3), "quake3"},
- {"-quake3demo", "q3demo", "Quake3Demo", {"demoq3/pak0.pk3"}, Q3CFG, {"demoq3", "*fteq3"}, "Quake III Arena Demo", NULL, "quake3"},
+ //rtcw is logically two-games-in-one-gamedir, using different pk3 names and config names to isolate each other. each has a mutually exclusive plugin, and we can't depend upon downloadable meta info to get that right so list needed packages here. :(
+ //(the ! blocks plugins from being loaded, unloading them instead, allowing both to be installed at once without issues, and doing so more gracefully allows switching between.)
+ {"-rtcwsp", "rtcwsp", "WolfSP", {"main/pak0.pk3"}, RTCWSPCFG,{"main", "*ftertcw"},"Return To Castle Wolfenstein (SP)",UPDATEURL(RTCW), "rtcwsp;rtcwmp;!rtcwmp;!quake3"},
+ {"-rtcwmp", "rtcwmp", "WolfMP", {"main/pak0.pk3"}, RTCWMPCFG,{"main", "*ftertcw"},"Return To Castle Wolfenstein (MP)",UPDATEURL(RTCW), "rtcwmp;rtcwsp;!rtcwsp;!quake3"},
+ {"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3), "quake3;!rtcwmp;!rtcwsp"},
+ {"-quake3demo", "q3demo", "Quake3Demo", {"demoq3/pak0.pk3"}, Q3CFG, {"demoq3", "*fteq3"}, "Quake III Arena Demo", NULL, "quake3;!rtcwmp;!rtcwsp"},
+
//the rest are not supported in any real way. maps-only mostly, if that
// {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"},
// {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},
@@ -5635,6 +5669,25 @@ static ftemanifest_t *FS_GenerateLegacyManifest(int game, const char *basedir)
FS_Manifest_ParseTokens(man);
}
+ if (!strncmp(gamemode_info[game].argname + strlen(gamemode_info[game].argname)-2, "mp", 2))
+ {
+ Cmd_TokenizeString(va("ignorepaks \"sp_*\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ Cmd_TokenizeString(va("extrapaks \"mp_pak\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ Cmd_TokenizeString(va("mainconfig \"ftemp.cfg\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ }
+ else if (!strncmp(gamemode_info[game].argname + strlen(gamemode_info[game].argname)-2, "sp", 2))
+ {
+ Cmd_TokenizeString(va("ignorepaks \"mp_*\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ Cmd_TokenizeString(va("extrapaks \"sp_pak\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ Cmd_TokenizeString(va("mainconfig \"ftesp.cfg\""), false, false);
+ FS_Manifest_ParseTokens(man);
+ }
+
//if there's no base dirs, edit the manifest to give it its default ones.
for (j = 0; j < sizeof(man->gamepath) / sizeof(man->gamepath[0]); j++)
{
diff --git a/engine/common/plugin.c b/engine/common/plugin.c
index 7dc43b329..486d56402 100644
--- a/engine/common/plugin.c
+++ b/engine/common/plugin.c
@@ -2205,6 +2205,7 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
S_StartSound,
S_GetChannelLevel,
S_Voip_ClientLoudness,
+ S_FadeSounds,
Media_NamedTrack
};
if (structsize == sizeof(funcs))
diff --git a/engine/common/ui_public.h b/engine/common/ui_public.h
index 3305ff19d..d374a2776 100644
--- a/engine/common/ui_public.h
+++ b/engine/common/ui_public.h
@@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
-
//these structures are shared with the exe.
#define UIMAX_SCOREBOARDNAME 16
@@ -117,6 +116,10 @@ typedef enum {
UI_SET_ACTIVE_MENU,
// void UI_SetActiveMenu( uiMenuCommand_t menu );
+#ifdef RTCW
+ UI_GET_ACTIVE_MENU,
+#endif
+
UI_CONSOLE_COMMAND,
// qboolean UI_ConsoleCommand( int realTime );
@@ -129,6 +132,10 @@ typedef enum {
} uiExport_t;
+#if defined(RTCW) && (!defined(RTCWMP) && !defined(RTCWSP))
+#error RTCW defined without RTCWMP nor RTCWSP
+#endif
+
typedef enum {
UI_ERROR,
UI_PRINT,
@@ -145,16 +152,28 @@ typedef enum {
UI_CMD_EXECUTETEXT,
UI_FS_FOPENFILE,
UI_FS_READ,
+#ifdef RTCWSP
+ UI_FS_SEEK,
+#endif
UI_FS_WRITE,
UI_FS_FCLOSEFILE,
UI_FS_GETFILELIST,
+#ifdef RTCW
+ UI_FS_DELETEFILE,
+#endif
UI_R_REGISTERMODEL,
UI_R_REGISTERSKIN,
UI_R_REGISTERSHADERNOMIP,//20
UI_R_CLEARSCENE,
UI_R_ADDREFENTITYTOSCENE,
UI_R_ADDPOLYTOSCENE,
+#ifdef RTCW
+ UI_R_ADDPOLYSTOSCENE,
+#endif
UI_R_ADDLIGHTTOSCENE,
+#ifdef RTCW
+ UI_R_ADDCORONATOSCENE,
+#endif
UI_R_RENDERSCENE,
UI_R_SETCOLOR,
UI_R_DRAWSTRETCHPIC,
@@ -163,6 +182,10 @@ typedef enum {
UI_CM_LOADMODEL,//30
UI_S_REGISTERSOUND,
UI_S_STARTLOCALSOUND,
+#ifdef RTCWSP
+ UI_S_FADESTREAMINGSOUND,
+ UI_S_FADEALLSOUNDS,
+#endif
UI_KEY_KEYNUMTOSTRINGBUF,
UI_KEY_GETBINDINGBUF,
UI_KEY_SETBINDING,
@@ -176,6 +199,12 @@ typedef enum {
UI_GETGLCONFIG,
UI_GETCLIENTSTATE,
UI_GETCONFIGSTRING,
+#ifdef RTCW
+ UI_LAN_GETLOCALSERVERCOUNT,
+ UI_LAN_GETLOCALSERVERADDRESSSTRING,
+ UI_LAN_GETGLOBALSERVERCOUNT,
+ UI_LAN_GETGLOBALSERVERADDRESSSTRING,
+#endif
UI_LAN_GETPINGQUEUECOUNT,
UI_LAN_CLEARPING,
UI_LAN_GETPING,
@@ -216,9 +245,21 @@ typedef enum {
UI_LAN_GETSERVERPING,
UI_LAN_SERVERISVISIBLE,
UI_LAN_COMPARESERVERS,
+#ifdef RTCW
+ UI_CL_GETLIMBOSTRING,
+ #ifdef RTCWMP
+ UI_SET_PBCLSTATUS,
+ UI_CHECKAUTOUPDATE,
+ UI_GET_AUTOUPDATE,
+ UI_CL_TRANSLATE_STRING,
+ UI_OPENURL,
+ UI_SET_PBSVSTATUS,
+ #endif
+#else
// 1.32
UI_FS_SEEK,
UI_SET_PBCLSTATUS,//87
+#endif
UI_MEMSET = 100,
UI_MEMCPY,
@@ -237,4 +278,7 @@ typedef enum {
UI_GETVIDINFO = 504,
UI_GET_STRING = 510,
*/
+#ifdef RTCWSP
+ UI_ALLOC = 900,
+#endif
} uiImport_t;
diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c
index 845297780..ee014dd65 100644
--- a/engine/gl/gl_alias.c
+++ b/engine/gl/gl_alias.c
@@ -96,6 +96,7 @@ void Mod_WipeSkin(skinid_t id, qboolean force)
}
R_UnloadShader(sk->mappings[i].shader);
}
+ Z_Free(sk->props);
Z_Free(sk);
}
static void Mod_WipeAllSkins(qboolean final)
@@ -275,6 +276,16 @@ skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext)
skin->q1upper = Q1UNSPECIFIED;
#endif
+#ifdef RTCW
+ //in rtcw, h_blink surfaces are drawn only when RF_BLINK is used, otherwise they're ignored.
+ //we don't support RF_BLINK, so just block them here.
+ Q_strncpyz(skin->mappings[skin->nummappings].surface, "h_blink", sizeof(skin->mappings[skin->nummappings].surface));
+ Q_strncpyz(shadername, com_token, sizeof(shadername));
+ skin->mappings[skin->nummappings].shader = R_RegisterShader ("nodraw", 0, "{\nsurfaceparm nodraw\n}\n");
+ R_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader, 0);
+ skin->mappings[skin->nummappings].texnums = *skin->mappings[skin->nummappings].shader->defaulttextures;
+ skin->nummappings++;
+#endif
while(skintext)
{
@@ -365,6 +376,28 @@ skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext)
{
//ignore it. matches q3.
}
+ else if (!strcmp(com_token, "playerscale")) //For RTCW
+ {
+ float scale;
+ size_t i = skin->numprops;
+ Z_ReallocElements((void**)&skin->props, &skin->numprops, skin->numprops+1, sizeof(*skin->props));
+ Q_strncpyz(skin->props[i].key, "playerscale", sizeof(skin->props[i].key));
+ //yes, xyz are all the same value. rtcw sucks
+ skintext = COM_ParseToken(skintext, NULL);
+ scale = atof(com_token);
+ Q_snprintfz(skin->props[i].value, sizeof(skin->props[i].value), "%f %f %f", scale,scale,scale);
+ }
+ else if (!strncmp(com_token, "md3_", 4)) //For RTCW
+ {
+ size_t i = skin->numprops;
+ Z_ReallocElements((void**)&skin->props, &skin->numprops, skin->numprops+1, sizeof(*skin->props));
+ Q_strncpyz(skin->props[i].key, com_token, sizeof(skin->props[i].key));
+ skintext = COM_ParseToken(skintext, ",");
+ if (!skintext)
+ skin->numprops = i;
+ skintext = COM_ParseToken(skintext, NULL);
+ Q_strncpyz(skin->props[i].value, com_token, sizeof(skin->props[i].value));
+ }
#ifdef QWSKINS
else if (!strcmp(com_token, "qwskin"))
{
diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c
index 4a8b63539..19f2514b3 100644
--- a/engine/gl/gl_shader.c
+++ b/engine/gl/gl_shader.c
@@ -839,7 +839,7 @@ static int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, ch
flags |= IF_MIPCAP;
if (pass)
- {
+ { //d3d needs some extra hints for its samplers (can't just use the texture's state)
if (flags & IF_CLAMP)
pass->flags |= SHADER_PASS_CLAMP;
if (flags & IF_LINEAR)
@@ -2929,6 +2929,13 @@ static shaderkey_t shaderkeys[] =
{"nofragment", NULL, "doom3"},
/*RTCW compat*/
+ /*r_ext_compressed_textures:
+ 0: compress nothing
+ 1: compress everything that isn't nocompress
+ 2: compress only allow compress
+ 3: unconditionally compress everything.
+ FIXME: we don't support these hints, so they end up being treated as 'unconditionally compress everything'
+ */
{"nocompress", NULL, "rtcw"},
{"allowcompress", NULL, "rtcw"},
{"nofog", NULL, "rtcw"},
@@ -4025,15 +4032,15 @@ static shaderkey_t shaderpasskeys[] =
//RTCW
- //fancy map lines use the map if that mode is active.
+ //fancy map lines use the map only if that mode is active.
//FIXME: actually check these to ensure there's no issues with any shaders overriding the pass's previously specified map
// (hopefully no shaders would actually do that due to the engine loading both textures, which would be wasteful)
{"map16", Shaderpass_RTCW_Map_16bit, "rtcw"},
{"map32", Shaderpass_RTCW_Map_32bit, "rtcw"},
{"mapcomp", Shaderpass_RTCW_Map_s3tc, "rtcw"},
{"mapnocomp", Shaderpass_RTCW_Map_nos3tc, "rtcw"},
- {"animcompmap", Shaderpass_RTCW_AnimMap_s3tc, "rtcw"},
- {"animnocompmap",Shaderpass_RTCW_AnimMap_nos3tc,"rtcw"},
+ {"animmapcomp", Shaderpass_RTCW_AnimMap_s3tc, "rtcw"},
+ {"animmapnocomp",Shaderpass_RTCW_AnimMap_nos3tc,"rtcw"},
//qfusion/warsow compat
{"material", Shaderpass_QF_Material, "qf"},
diff --git a/engine/server/savegame.c b/engine/server/savegame.c
index 85ab02a0c..e2a771a8d 100644
--- a/engine/server/savegame.c
+++ b/engine/server/savegame.c
@@ -1470,6 +1470,12 @@ void SV_Savegame_f (void)
{
if (sv.state == ss_clustermode && MSV_ForwardToAutoServer())
;
+#ifdef Q3SERVER
+ //we can't save the game when using q3 gamecode.
+ //however, RTCW gamecode does implement its own 'savegame' command, so give the gamecode the choice to handle it.
+ else if (q3->sv.ConsoleCommand())
+ return;
+#endif
else if (Cmd_Argc() <= 2)
{
const char *savename = Cmd_Argv(1);
@@ -2068,6 +2074,7 @@ qboolean SV_Loadgame (const char *unsafe_savename)
#ifndef QUAKETC
{"%s.sav"},
#endif
+ {"save/%s.svg"},
{"%s"}
};
int bestd=0x7fffffff,best=0;
@@ -2116,6 +2123,15 @@ qboolean SV_Loadgame (const char *unsafe_savename)
}
Q_snprintfz (filename, sizeof(filename), savefiles[best].pattern, savename);
+#ifdef Q3SERVER
+ if (!!Q_strcasecmp(COM_GetFileExtension(filename, NULL), "svg"))
+ { //RTCW saved games are handled by the gamecode itself.
+ //let the map command handle it.
+ extern void SV_Map_f(void);
+ SV_Map_f();
+ return true;
+ }
+#endif
f = FS_OpenReadLocation(filename, &savefiles[best].loc);
if (!f)
{
diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c
index 3333f914a..1a0d018f2 100644
--- a/engine/server/sv_ccmds.c
+++ b/engine/server/sv_ccmds.c
@@ -534,6 +534,7 @@ variations:
'devmap' forces sv_cheats 1, otherwise map
'map_restart' restarts the current map. Name is needed for q3 compat.
'restart' is an alias for 'map_restart'. Exists for NQ compat, but as an alias for QW mods that tried to use it for mod-specific things.
+'loadgame' is for rtcw (as an internal part of the loadgame command). simply reads an svg file to get the real map name then sets up some cvars to tell the gamecode which file to read, otherwise like spmap.
hexen2 fixme:
'restart restore' restarts the map, reloading from a saved game if applicable.
@@ -674,6 +675,54 @@ void SV_Map_f (void)
#endif
mapeditor = !strcmp(Cmd_Argv(0), "mapedit");
+ if (!strncmp(cmd, "loadgame", 4))
+ { //for RTCW: if we're loading a gamecode-controlled saved game then we need to figure out which map we're actually meant to be loading.
+ size_t sz;
+ char *f = NULL;
+ if (!f)
+ {
+ snprintf (expanded, sizeof(expanded), "%s", level);
+ f = FS_MallocFile(expanded, FS_GAME, &sz);
+ }
+ if (!f)
+ {
+ snprintf (expanded, sizeof(expanded), "save/%s.svg", level);
+ f = FS_MallocFile(expanded, FS_GAME, &sz);
+ }
+ *level = 0;
+ if (f)
+ {
+ if (sz > 4)
+ {
+ Q_strncpyz(level, f+4, sizeof(level));
+ //write the loaded game to current.svg, for map_restart (gamecode will also write it on map loads)
+ if (strcmp(expanded, "save/current.svg"))
+ FS_WriteFile("save/current.svg", f, sz, FS_GAMEONLY);
+ }
+ FS_FreeFile(f);
+ }
+ if (!*level)
+ {
+ Con_Printf ("Unable to determine which level to load\n");
+ return;
+ }
+ q3singleplayer = true;
+ flushparms = true;
+
+ Cvar_Set(Cvar_Get2("savegame_loading", "", CVAR_NOSAVE, "Used to signal between engine and gamecode.", "rtcw compat"), "1");
+ Cvar_Set(Cvar_Get2("savegame_filename", "", CVAR_NOSAVE, "Used to signal between engine and gamecode.", "rtcw compat"), expanded);
+ }
+ else if (!isrestart)
+ { //and make sure they're cleared if its not actually a loadgame.
+ cvar_t *c;
+ c = Cvar_FindVar("savegame_loading");
+ if (c)
+ Cvar_Set(c, "");
+ c = Cvar_FindVar("savegame_filename");
+ if (c)
+ Cvar_Set(c, "");
+ }
+
sv.mapchangelocked = false;
if (!strcmp(level, "."))
@@ -899,7 +948,13 @@ void SV_Map_f (void)
if (!isrestart)
{
if (q3singleplayer)
+ {
+ extern cvar_t maxclients;
+ var = Cvar_Get("g_sp_botcount", "8", CVAR_MAPLATCH|CVAR_SERVERINFO, "Q3 compatability");
+ if (maxclients.ival < var->ival) //rtcw uses bots for monsters, so needs quite a few player slots in single player... while q3 needs 8
+ Cvar_SetValue(&maxclients, var->ival);
Cvar_ForceSet(gametype, "2");//singleplayer
+ }
else if (gametype->value == 2)
Cvar_ForceSet(gametype, "");//force to ffa deathmatch
}
diff --git a/plugins/irc/ircclient.c b/plugins/irc/ircclient.c
index 597e0ac2d..8f0cb2c14 100644
--- a/plugins/irc/ircclient.c
+++ b/plugins/irc/ircclient.c
@@ -69,13 +69,6 @@ static void QDECL IRC_UpdateVideo(int width, int height)
pvid.height = height;
}
-static qboolean (*Con_TrySubPrint)(const char *subname, const char *text);
-static qboolean Con_FakeSubPrint(const char *subname, const char *text)
-{
- plugfuncs->Print(text);
- return true;
-}
-
//porting zone:
@@ -354,7 +347,10 @@ static void IRC_Printf(ircclient_t *irc, const char *subname, const char *format
if (!*string)
confuncs->SetActive(lwr);
- Con_TrySubPrint(lwr, string);
+ if (confuncs)
+ confuncs->SubPrint(subname, string, 0, 0);
+ else
+ plugfuncs->Print(string);
}
}
@@ -399,9 +395,7 @@ qboolean Plug_Init(void)
plugfuncs->ExportFunction("ConsoleLink", IRC_ConsoleLink);
if (!confuncs || !plugfuncs->ExportFunction("ConExecuteCommand", IRC_ConExecuteCommand))
- Con_TrySubPrint = Con_FakeSubPrint;
- else
- Con_TrySubPrint = confuncs->SubPrint;
+ confuncs = NULL;
reloadconfig = true;
IRC_InitCvars();
@@ -446,8 +440,10 @@ int IRC_ConExecuteCommand(qboolean isinsecure)
{
if (*buffer == '/')
IRC_Command(NULL, "", imsg);
+ else if (confuncs)
+ confuncs->SubPrint(buffer, "You were disconnected\n", 0, 0);
else
- Con_TrySubPrint(buffer, "You were disconnected\n");
+ plugfuncs->Print("You were disconnected\n");
return true;
}
diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c
index 1aebbbb1a..9cb7337ed 100644
--- a/plugins/jabber/jabberclient.c
+++ b/plugins/jabber/jabberclient.c
@@ -112,13 +112,6 @@ static enum
#define Q_strncpyz(o, i, l) do {strncpy(o, i, l-1);o[l-1]='\0';}while(0)
-static qboolean (*Con_TrySubPrint)(const char *conname, const char *message);
-qboolean Fallback_ConPrint(const char *conname, const char *message)
-{
- plugfuncs->Print(message);
- return true;
-}
-
void Con_SubPrintf(const char *subname, const char *format, ...)
{
va_list argptr;
@@ -128,7 +121,10 @@ void Con_SubPrintf(const char *subname, const char *format, ...)
Q_vsnprintf (string, sizeof(string), format,argptr);
va_end (argptr);
- Con_TrySubPrint(subname, string);
+ if (confuncs)
+ confuncs->SubPrint(subname, string, 0, 0);
+ else
+ plugfuncs->Print(string);
}
@@ -923,11 +919,9 @@ qboolean Plug_Init(void)
if (!confuncs || !plugfuncs->ExportFunction("ConExecuteCommand", JCL_ConExecuteCommand))
{
+ confuncs = NULL;
Con_Printf("XMPP plugin in single-console mode\n");
- Con_TrySubPrint = Fallback_ConPrint;
}
- else
- Con_TrySubPrint = confuncs->SubPrint;
cmdfuncs->AddCommand(COMMANDPREFIX, JCL_ExecuteCommand_f, cmddesc);
cmdfuncs->AddCommand(COMMANDPREFIX2, JCL_ExecuteCommand_f, cmddesc);
@@ -4485,7 +4479,10 @@ void XMPP_ConversationPrintf(const char *context, const char *title, qboolean ta
confuncs->SetConsoleFloat(context, "wnd_h", 320);
confuncs->SetConsoleString(context, "title", title);
}
- Con_TrySubPrint(context, string);
+ if (confuncs)
+ confuncs->SubPrint(context, string, 0, 0);
+ else
+ plugfuncs->Print(string);
}
void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree)
{
@@ -6848,7 +6845,7 @@ void JCL_Command(int accid, char *console)
jid = roomnick;
else
{
- Con_TrySubPrint(console, "Unable to translate roomnicks. Please use their bare jid.\n");
+ Con_SubPrintf(console, "Unable to translate roomnicks. Please use their bare jid.\n");
return; //FIXME: translate from roomnick to real barejid
}
@@ -6980,7 +6977,7 @@ void JCL_Command(int accid, char *console)
if (jcl)
{
- Con_TrySubPrint(console, "You are already connected\nPlease /quit first\n");
+ Con_SubPrintf(console, "You are already connected\nPlease /quit first\n");
return;
}
if (!strncmp(arg[0]+1, "tls", 3))
@@ -6994,45 +6991,45 @@ void JCL_Command(int accid, char *console)
jcl = jclients[accid] = JCL_Connect(accid, arg[3], tls, arg[1], arg[2]);
if (!jclients[accid])
{
- Con_TrySubPrint(console, "Connect failed\n");
+ Con_SubPrintf(console, "Connect failed\n");
return;
}
jclient_configdirty = true;
}
else if (!strcmp(arg[0]+1, "help"))
{
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /connect USERNAME@DOMAIN/RESOURCE [PASSWORD] [XMPPSERVER]^]\n");
- Con_TrySubPrint(console, "eg for gmail: ^[/" COMMANDPREFIX " /connect myusername@gmail.com^] (using oauth2)\n");
- Con_TrySubPrint(console, "eg for gmail: ^[/" COMMANDPREFIX " /connect myusername@gmail.com mypassword^] (warning: password will be saved locally in plain text)\n");
-// Con_TrySubPrint(console, "eg for facebook: ^[/" COMMANDPREFIX " /connect myusername@chat.facebook.com mypassword chat.facebook.com^]\n");
-// Con_TrySubPrint(console, "eg for msn: ^[/" COMMANDPREFIX " /connect myusername@messanger.live.com mypassword^]\n");
- Con_TrySubPrint(console, "Note that this info will be used the next time you start quake.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /connect USERNAME@DOMAIN/RESOURCE [PASSWORD] [XMPPSERVER]^]\n");
+ Con_SubPrintf(console, "eg for gmail: ^[/" COMMANDPREFIX " /connect myusername@gmail.com^] (using oauth2)\n");
+ Con_SubPrintf(console, "eg for gmail: ^[/" COMMANDPREFIX " /connect myusername@gmail.com mypassword^] (warning: password will be saved locally in plain text)\n");
+// Con_SubPrintf(console, "eg for facebook: ^[/" COMMANDPREFIX " /connect myusername@chat.facebook.com mypassword chat.facebook.com^]\n");
+// Con_SubPrintf(console, "eg for msn: ^[/" COMMANDPREFIX " /connect myusername@messanger.live.com mypassword^]\n");
+ Con_SubPrintf(console, "Note that this info will be used the next time you start quake.\n");
//small note:
//for the account 'me@example.com' the server to connect to can be displayed with:
//nslookup -querytype=SRV _xmpp-client._tcp.example.com
//srv resolving seems to be non-standard on each system, I don't like having to special case things.
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /help^]\n"
- "This text...\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /raw <XML STANZAS/>^]\n"
- "For debug hackery.\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /friend accountname@domain friendlyname^]\n"
- "Befriends accountname, and shows them in your various lists using the friendly name. Can also be used to rename friends.\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /unfriend accountname@domain^]\n"
- "Ostracise your new best enemy. You will no longer see them and they won't be able to contact you.\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /blist^]\n"
- "Show all your friends! Names are clickable and will begin conversations.\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /quit^]\n"
- "Disconnect from the XMPP server, noone will be able to hear you scream.\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /join accountname@domain^]\n"
- "Joins your friends game (they will be prompted).\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /invite accountname@domain^]\n"
- "Invite someone to join your game (they will be prompted).\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /voice accountname@domain^]\n"
- "Begin a bi-directional peer-to-peer voice conversation with someone (they will be prompted).\n");
- Con_TrySubPrint(console, "^[/" COMMANDPREFIX " /msg ACCOUNTNAME@domain \"your message goes here\"^]\n"
- "Sends a message to the named person. If given a resource postfix, your message will be sent only to that resource.\n");
- Con_TrySubPrint(console, "If no arguments, will print out your friends list. If no /command is used, the arguments will be sent as a message to the person you last sent a message to.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /help^]\n"
+ "This text...\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /raw <XML STANZAS/>^]\n"
+ "For debug hackery.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /friend accountname@domain friendlyname^]\n"
+ "Befriends accountname, and shows them in your various lists using the friendly name. Can also be used to rename friends.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /unfriend accountname@domain^]\n"
+ "Ostracise your new best enemy. You will no longer see them and they won't be able to contact you.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /blist^]\n"
+ "Show all your friends! Names are clickable and will begin conversations.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /quit^]\n"
+ "Disconnect from the XMPP server, noone will be able to hear you scream.\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /join accountname@domain^]\n"
+ "Joins your friends game (they will be prompted).\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /invite accountname@domain^]\n"
+ "Invite someone to join your game (they will be prompted).\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /voice accountname@domain^]\n"
+ "Begin a bi-directional peer-to-peer voice conversation with someone (they will be prompted).\n");
+ Con_SubPrintf(console, "^[/" COMMANDPREFIX " /msg ACCOUNTNAME@domain \"your message goes here\"^]\n"
+ "Sends a message to the named person. If given a resource postfix, your message will be sent only to that resource.\n");
+ Con_SubPrintf(console, "If no arguments, will print out your friends list. If no /command is used, the arguments will be sent as a message to the person you last sent a message to.\n");
}
else if (!strcmp(arg[0]+1, "clear"))
{
@@ -7284,7 +7281,7 @@ void JCL_Command(int accid, char *console)
if (!*msg && confuncs)
JCL_RegenerateBuddyList(true);
else
- Con_TrySubPrint(console, "Not connected. For help, type \"^[/" COMMANDPREFIX " /help^]\"\n");
+ Con_SubPrintf(console, "Not connected. For help, type \"^[/" COMMANDPREFIX " /help^]\"\n");
}
}
}
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 68eadb62e..156643f04 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -221,7 +221,7 @@ typedef struct //core stuff
typedef struct //subconsole handling
{
F(qhandle_t,POpen, (const char *conname));
- F(qboolean, SubPrint, (const char *subname, const char *text)); //on to sub console.
+ F(qboolean, SubPrint, (const char *subname, const char *text, unsigned int setflags, unsigned int clearflags)); //on to sub console.
F(qboolean, RenameSub, (const char *oldname, const char *newname)); //rename a console.
F(qboolean, IsActive, (const char *conname));
F(qboolean, SetActive, (const char *conname));
@@ -277,6 +277,7 @@ typedef struct
F(void, StartSound, (int entnum, int entchannel, struct sfx_s *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags));
F(float, GetChannelLevel, (int entnum, int entchannel));
F(int, Voip_ClientLoudness,(unsigned int plno));
+ F(void, FadeSounds, (float newvolume, float duration));
F(qboolean, ChangeMusicTrack, (const char *initialtrack, const char *looptrack));
#define plugaudiofuncs_name "Audio"
diff --git a/plugins/qi/qi.c b/plugins/qi/qi.c
index db188ecb8..800b2a0ea 100644
--- a/plugins/qi/qi.c
+++ b/plugins/qi/qi.c
@@ -63,7 +63,7 @@ void Con_SubPrintf(const char *subname, char *format, ...)
Q_vsnprintf (string, sizeof(string), format,argptr);
va_end (argptr);
- confuncs->SubPrint(subname, string);
+ confuncs->SubPrint(subname, string, 0, 0);
}
diff --git a/plugins/quake3/botlib/aasfile.h b/plugins/quake3/botlib/aasfile.h
index 8f2fbf629..146b10f9c 100644
--- a/plugins/quake3/botlib/aasfile.h
+++ b/plugins/quake3/botlib/aasfile.h
@@ -25,8 +25,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// default long
#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E')
+#ifdef RTCW
+#define AASVERSION 8 //adds steepness.
+#else
#define AASVERSION_OLD 4
#define AASVERSION 5
+#endif
//presence types
#define PRESENCE_NONE 1
@@ -80,9 +84,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define AREACONTENTS_JUMPPAD 128
#define AREACONTENTS_DONOTENTER 256
#define AREACONTENTS_VIEWPORTAL 512
+#ifdef RTCW
+#define AREACONTENTS_DONOTENTER_LARGE 1024
+#define AREACONTENTS_MOVER 2048
+#else
#define AREACONTENTS_MOVER 1024
#define AREACONTENTS_NOTTEAM1 2048
#define AREACONTENTS_NOTTEAM2 4096
+#endif
//number of model of the mover inside this area
#define AREACONTENTS_MODELNUMSHIFT 24
#define AREACONTENTS_MAXMODELNUM 0xFF
@@ -94,6 +103,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define AREA_LIQUID 4 //area contains a liquid
#define AREA_DISABLED 8 //area is disabled for routing when set
#define AREA_BRIDGE 16 //area ontop of a bridge
+#define AREA_USEFORROUTING 1024
//aas file header lumps
#define AAS_LUMPS 14
@@ -147,6 +157,10 @@ typedef struct aas_areasettings_s
int clusterareanum; //number of the area in the cluster
int numreachableareas; //number of reachable areas from this one
int firstreachablearea; //first reachable area in the reachable area index
+
+#ifdef RTCW
+ float groundsteepness; //0 flat, 1 steep
+#endif
} aas_areasettings_t;
//cluster portal
diff --git a/plugins/quake3/botlib/be_aas.h b/plugins/quake3/botlib/be_aas.h
index dbf24bc12..9d07c8086 100644
--- a/plugins/quake3/botlib/be_aas.h
+++ b/plugins/quake3/botlib/be_aas.h
@@ -59,7 +59,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define TFL_LAVA 0x00400000 //travel through lava
#define TFL_DONOTENTER 0x00800000 //travel through donotenter area
#define TFL_FUNCBOB 0x01000000 //func bobbing
+#ifdef RTCW
+#define TFL_DONOTENTER_LARGE 0x02000000
+#else
#define TFL_FLIGHT 0x02000000 //flight
+#endif
#define TFL_BRIDGE 0x04000000 //move over a bridge
//
#define TFL_NOTTEAM1 0x08000000 //not team 1
diff --git a/plugins/quake3/botlib/be_aas_bspq3.c b/plugins/quake3/botlib/be_aas_bspq3.c
index 31c9f66a5..b278f65c4 100644
--- a/plugins/quake3/botlib/be_aas_bspq3.c
+++ b/plugins/quake3/botlib/be_aas_bspq3.c
@@ -405,6 +405,7 @@ void AAS_ParseBSPEntities(void)
ent = &bspworld.entities[bspworld.numentities];
bspworld.numentities++;
ent->epairs = NULL;
+
while(PS_ReadToken(script, &token))
{
if (!strcmp(token.string, "}")) break;
diff --git a/plugins/quake3/botlib/be_aas_def.h b/plugins/quake3/botlib/be_aas_def.h
index eb995d6c6..2a7480a3e 100644
--- a/plugins/quake3/botlib/be_aas_def.h
+++ b/plugins/quake3/botlib/be_aas_def.h
@@ -32,10 +32,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//debugging on
#define AAS_DEBUG
-#define MAX_CLIENTS 64
+//#define MAX_CLIENTS 64
#define MAX_MODELS 256 // these are sent over the net as 8 bits
#define MAX_SOUNDS 256 // so they cannot be blindly increased
-#define MAX_CONFIGSTRINGS 1024
+//#define MAX_CONFIGSTRINGS 1024
#define CS_SCORES 32
#define CS_MODELS (CS_SCORES+MAX_CLIENTS)
@@ -284,6 +284,8 @@ typedef struct aas_s
//areas the reachabilities go through
int *reachabilityareaindex;
aas_reachabilityareas_t *reachabilityareas;
+
+ unsigned short int *hidetraveltimes; //private to AAS_NearestHideArea
} aas_t;
#define AASINTERN
diff --git a/plugins/quake3/botlib/be_aas_file.c b/plugins/quake3/botlib/be_aas_file.c
index b331f8c94..e87484d5a 100644
--- a/plugins/quake3/botlib/be_aas_file.c
+++ b/plugins/quake3/botlib/be_aas_file.c
@@ -370,7 +370,11 @@ int AAS_LoadAASFile(char *filename)
//check the version
header.version = LittleLong(header.version);
//
+#ifdef AASVERSION_OLD
if (header.version != AASVERSION_OLD && header.version != AASVERSION)
+#else
+ if (header.version != AASVERSION)
+#endif
{
AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION);
botimport.FS_FCloseFile(fp);
diff --git a/plugins/quake3/botlib/be_aas_main.c b/plugins/quake3/botlib/be_aas_main.c
index a07149471..b8f6fd796 100644
--- a/plugins/quake3/botlib/be_aas_main.c
+++ b/plugins/quake3/botlib/be_aas_main.c
@@ -50,7 +50,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif
#endif
+#if MAX_AAS_WORLDS > 1
+#define aasworld (*aasworldptr)
+aas_t aasworlds[MAX_AAS_WORLDS];
+aas_t *aasworldptr = &aasworlds[0];
+#else
aas_t aasworld;
+#endif
libvar_t *saveroutingcache;
@@ -243,42 +249,46 @@ void AAS_ContinueInit(float time)
//===========================================================================
int AAS_StartFrame(float time)
{
- aasworld.time = time;
- //unlink all entities that were not updated last frame
- AAS_UnlinkInvalidEntities();
- //invalidate the entities
- AAS_InvalidateEntities();
- //initialize AAS
- AAS_ContinueInit(time);
- //
- aasworld.frameroutingupdates = 0;
- //
- if (botDeveloper)
+ int i;
+ for (i = 0; i < MAX_AAS_WORLDS; i++)
{
- if (LibVarGetValue("showcacheupdates"))
+ aasworld.time = time;
+ //unlink all entities that were not updated last frame
+ AAS_UnlinkInvalidEntities();
+ //invalidate the entities
+ AAS_InvalidateEntities();
+ //initialize AAS
+ AAS_ContinueInit(time);
+ //
+ aasworld.frameroutingupdates = 0;
+ //
+ if (botDeveloper)
{
- AAS_RoutingInfo();
- LibVarSet("showcacheupdates", "0");
+ if (LibVarGetValue("showcacheupdates"))
+ {
+ AAS_RoutingInfo();
+ LibVarSet("showcacheupdates", "0");
+ } //end if
+ if (LibVarGetValue("showmemoryusage"))
+ {
+ PrintUsedMemorySize();
+ LibVarSet("showmemoryusage", "0");
+ } //end if
+ if (LibVarGetValue("memorydump"))
+ {
+ PrintMemoryLabels();
+ LibVarSet("memorydump", "0");
+ } //end if
} //end if
- if (LibVarGetValue("showmemoryusage"))
+ //
+ if (saveroutingcache->value)
{
- PrintUsedMemorySize();
- LibVarSet("showmemoryusage", "0");
+ AAS_WriteRouteCache();
+ LibVarSet("saveroutingcache", "0");
} //end if
- if (LibVarGetValue("memorydump"))
- {
- PrintMemoryLabels();
- LibVarSet("memorydump", "0");
- } //end if
- } //end if
- //
- if (saveroutingcache->value)
- {
- AAS_WriteRouteCache();
- LibVarSet("saveroutingcache", "0");
- } //end if
- //
- aasworld.numframes++;
+ //
+ aasworld.numframes++;
+ }
return BLERR_NOERROR;
} //end of the function AAS_StartFrame
//===========================================================================
@@ -346,38 +356,56 @@ int AAS_LoadFiles(const char *mapname)
//===========================================================================
int AAS_LoadMap(const char *mapname)
{
+ int i;
int errnum;
+ int ret = BLERR_NOERROR;
+ qboolean gotone = qfalse;
//if no mapname is provided then the string indexes are updated
if (!mapname)
{
return 0;
} //end if
- //
- aasworld.initialized = qfalse;
- //NOTE: free the routing caches before loading a new map because
- // to free the caches the old number of areas, number of clusters
- // and number of areas in a clusters must be available
- AAS_FreeRoutingCaches();
- //load the map
- errnum = AAS_LoadFiles(mapname);
- if (errnum != BLERR_NOERROR)
+
+ for (i = 0; i < MAX_AAS_WORLDS; i++)
{
- aasworld.loaded = qfalse;
- return errnum;
- } //end if
- //
- AAS_InitSettings();
- //initialize the AAS link heap for the new map
- AAS_InitAASLinkHeap();
- //initialize the AAS linked entities for the new map
- AAS_InitAASLinkedEntities();
- //initialize reachability for the new map
- AAS_InitReachability();
- //initialize the alternative routing
- AAS_InitAlternativeRouting();
- //everything went ok
- return 0;
+ AAS_SetCurrentWorld(i);
+ aasworld.initialized = qfalse;
+ //NOTE: free the routing caches before loading a new map because
+ // to free the caches the old number of areas, number of clusters
+ // and number of areas in a clusters must be available
+ AAS_FreeRoutingCaches();
+ //load the map
+#if MAX_AAS_WORLDS > 1
+ {
+ char hullname[256];
+ Com_sprintf (hullname, sizeof(hullname), "%s_b%i", mapname, i);
+ errnum = AAS_LoadFiles(hullname);
+ }
+#else
+ errnum = AAS_LoadFiles(mapname);
+#endif
+ if (errnum != BLERR_NOERROR)
+ {
+ ret = errnum;
+ aasworld.loaded = qfalse;
+ continue;
+ } //end if
+ //
+ AAS_InitSettings();
+ //initialize the AAS link heap for the new map
+ AAS_InitAASLinkHeap();
+ //initialize the AAS linked entities for the new map
+ AAS_InitAASLinkedEntities();
+ //initialize reachability for the new map
+ AAS_InitReachability();
+ //initialize the alternative routing
+ AAS_InitAlternativeRouting();
+ gotone = qtrue;
+ }
+ if (gotone)
+ return 0; //success, if at least one hull worked
+ return ret;
} //end of the function AAS_LoadMap
//===========================================================================
// called when the library is first loaded
@@ -388,6 +416,8 @@ int AAS_LoadMap(const char *mapname)
//===========================================================================
int AAS_Setup(void)
{
+ AAS_SetCurrentWorld(0);
+
aasworld.maxclients = (int) LibVarValue("maxclients", "128");
aasworld.maxentities = (int) LibVarValue("maxentities", "1024");
// as soon as it's set to 1 the routing cache will be saved
@@ -411,25 +441,38 @@ int AAS_Setup(void)
//===========================================================================
void AAS_Shutdown(void)
{
- AAS_ShutdownAlternativeRouting();
- //
- AAS_DumpBSPData();
- //free routing caches
- AAS_FreeRoutingCaches();
- //free aas link heap
- AAS_FreeAASLinkHeap();
- //free aas linked entities
- AAS_FreeAASLinkedEntities();
- //free the aas data
- AAS_DumpAASData();
- //free the entities
- if (aasworld.entities) FreeMemory(aasworld.entities);
- //clear the aasworld structure
- Com_Memset(&aasworld, 0, sizeof(aas_t));
- //aas has not been initialized
- aasworld.initialized = qfalse;
+ int i;
+ for (i = 0; i < MAX_AAS_WORLDS; i++)
+ {
+ AAS_SetCurrentWorld(i);
+
+ AAS_ShutdownAlternativeRouting();
+ //
+ AAS_DumpBSPData();
+ //free routing caches
+ AAS_FreeRoutingCaches();
+ //free aas link heap
+ AAS_FreeAASLinkHeap();
+ //free aas linked entities
+ AAS_FreeAASLinkedEntities();
+ //free the aas data
+ AAS_DumpAASData();
+ //free the entities
+ if (aasworld.entities) FreeMemory(aasworld.entities);
+ //clear the aasworld structure
+ Com_Memset(&aasworld, 0, sizeof(aas_t));
+ //aas has not been initialized
+ aasworld.initialized = qfalse;
+ }
//NOTE: as soon as a new .bsp file is loaded the .bsp file memory is
// freed an reallocated, so there's no need to free that memory here
//print shutdown
botimport.Print(PRT_MESSAGE, "AAS shutdown.\n");
} //end of the function AAS_Shutdown
+
+void AAS_SetCurrentWorld(int idx)
+{
+#if MAX_AAS_WORLDS > 1
+ aasworldptr = &aasworlds[idx];
+#endif
+}
\ No newline at end of file
diff --git a/plugins/quake3/botlib/be_aas_main.h b/plugins/quake3/botlib/be_aas_main.h
index 1d0bef105..85a71b739 100644
--- a/plugins/quake3/botlib/be_aas_main.h
+++ b/plugins/quake3/botlib/be_aas_main.h
@@ -31,7 +31,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifdef AASINTERN
+#ifdef RTCW
+#define MAX_AAS_WORLDS 2
+#define aasworld (*aasworldptr)
+extern aas_t *aasworldptr;
+#else
+#define MAX_AAS_WORLDS 1
extern aas_t aasworld;
+#endif
//AAS error message
void QDECL AAS_Error(char *fmt, ...);
@@ -59,3 +66,5 @@ int AAS_IndexFromModel(char *modelname);
float AAS_Time(void);
//
void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj );
+
+void AAS_SetCurrentWorld(int i);
\ No newline at end of file
diff --git a/plugins/quake3/botlib/be_aas_route.c b/plugins/quake3/botlib/be_aas_route.c
index 8e1c556e4..dfa99a56b 100644
--- a/plugins/quake3/botlib/be_aas_route.c
+++ b/plugins/quake3/botlib/be_aas_route.c
@@ -287,6 +287,8 @@ void AAS_RemoveRoutingCacheUsingArea( int areanum )
AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster );
AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster );
} //end else
+ if (!aasworld.initialized)
+ return; //not yet ready... don't crash!
// remove all portal cache
for (i = 0; i < aasworld.numareas; i++)
{
@@ -363,12 +365,18 @@ int AAS_GetAreaContentsTravelFlags(int areanum)
tfl |= TFL_LAVA;
else
tfl |= TFL_AIR;
+#ifdef RTCW
+ if ( contents & AREACONTENTS_DONOTENTER_LARGE )
+ tfl |= TFL_DONOTENTER_LARGE;
+#endif
if (contents & AREACONTENTS_DONOTENTER)
tfl |= TFL_DONOTENTER;
+#ifndef RTCW
if (contents & AREACONTENTS_NOTTEAM1)
tfl |= TFL_NOTTEAM1;
if (contents & AREACONTENTS_NOTTEAM2)
tfl |= TFL_NOTTEAM2;
+#endif
if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE)
tfl |= TFL_BRIDGE;
return tfl;
@@ -871,6 +879,11 @@ void AAS_InitRoutingUpdate(void)
maxreachabilityareas = aasworld.clusters[i].numreachabilityareas;
} //end if
} //end for
+#ifdef RTCW
+ //I don't really know what I'm doing. I'm just trying to fix random segfaults.
+ if (maxreachabilityareas < aasworld.numareas)
+ maxreachabilityareas = aasworld.numareas;
+#endif
//allocate memory for the routing update fields
aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory(
maxreachabilityareas * sizeof(aas_routingupdate_t));
@@ -923,12 +936,19 @@ typedef struct routecacheheader_s
int numclusters;
int areacrc;
int clustercrc;
+#ifdef RTCW
+ int reachcrc;
+#endif
int numportalcache;
int numareacache;
} routecacheheader_t;
#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M')
+#ifdef RTCW
+#define RCVERSION 9915
+#else
#define RCVERSION 2
+#endif
//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed);
//int AAS_CompressVis(byte *vis, int numareas, byte *dest);
@@ -977,6 +997,9 @@ void AAS_WriteRouteCache(void)
routecacheheader.numclusters = aasworld.numclusters;
routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas );
routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters );
+#ifdef RTCW
+ routecacheheader.reachcrc = CRC_ProcessString( (unsigned char *)aasworld.reachability, sizeof(aas_reachability_t) * aasworld.reachabilitysize );
+#endif
routecacheheader.numportalcache = numportalcache;
routecacheheader.numareacache = numareacache;
//write the header
@@ -1066,36 +1089,51 @@ int AAS_ReadRouteCache(void)
botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp );
if (routecacheheader.ident != RCID)
{
+ botimport.FS_FCloseFile(fp);
AAS_Error("%s is not a route cache dump\n");
return qfalse;
} //end if
if (routecacheheader.version != RCVERSION)
{
- AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION);
+ botimport.FS_FCloseFile(fp);
+ AAS_Error("route cache dump has wrong version %d, should be %d\n", routecacheheader.version, RCVERSION);
return qfalse;
} //end if
if (routecacheheader.numareas != aasworld.numareas)
{
+ botimport.FS_FCloseFile(fp);
//AAS_Error("route cache dump has wrong number of areas\n");
return qfalse;
} //end if
if (routecacheheader.numclusters != aasworld.numclusters)
{
+ botimport.FS_FCloseFile(fp);
//AAS_Error("route cache dump has wrong number of clusters\n");
return qfalse;
} //end if
if (routecacheheader.areacrc !=
CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ))
{
+ botimport.FS_FCloseFile(fp);
//AAS_Error("route cache dump area CRC incorrect\n");
return qfalse;
} //end if
if (routecacheheader.clustercrc !=
CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ))
{
+ botimport.FS_FCloseFile(fp);
+ //AAS_Error("route cache dump cluster CRC incorrect\n");
+ return qfalse;
+ } //end if
+#ifdef RTCW
+ if (routecacheheader.reachcrc !=
+ CRC_ProcessString( (unsigned char *)aasworld.reachability, sizeof(aas_reachability_t) * aasworld.reachabilitysize ))
+ {
+ botimport.FS_FCloseFile(fp);
//AAS_Error("route cache dump cluster CRC incorrect\n");
return qfalse;
} //end if
+#endif
//read all the portal cache
for (i = 0; i < routecacheheader.numportalcache; i++)
{
@@ -1117,6 +1155,7 @@ int AAS_ReadRouteCache(void)
aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache;
aasworld.clusterareacache[cache->cluster][clusterareanum] = cache;
} //end for
+#ifdef RTCW
// read the visareas
/*
aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *));
@@ -1130,7 +1169,8 @@ int AAS_ReadRouteCache(void)
}
}
*/
- //
+ // rtcw also has some extra waypoint thingies here
+#endif
botimport.FS_FCloseFile(fp);
return qtrue;
} //end of the function AAS_ReadRouteCache
@@ -2050,13 +2090,14 @@ int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t go
} //end for
return qfalse;
} //end of the function AAS_RandomGoalArea
+#ifdef RTCW
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
-int AAS_AreaVisible(int srcarea, int destarea)
+static int AAS_AreaVisible(int srcarea, int destarea)
{
return qfalse;
} //end of the function AAS_AreaVisible
@@ -2066,25 +2107,24 @@ int AAS_AreaVisible(int srcarea, int destarea)
// Returns: -
// Changes Globals: -
//===========================================================================
-float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)
+/*static float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)
{
vec3_t vec, p2;
AAS_ProjectPointOntoVector(point, v1, v2, p2);
VectorSubtract(point, p2, vec);
return VectorLength(vec);
-} //end of the function DistancePointToLine
+} //end of the function DistancePointToLine*/
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
-int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)
+static int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)
{
int i, j, nextareanum, badtravelflags, numreach, bestarea;
unsigned short int t, besttraveltime;
- static unsigned short int *hidetraveltimes;
aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
aas_reachability_t *reach;
float dist1, dist2;
@@ -2092,13 +2132,13 @@ int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, ve
qboolean startVisible;
//
- if (!hidetraveltimes)
+ if (!aasworld.hidetraveltimes)
{
- hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));
+ aasworld.hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));
} //end if
else
{
- Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));
+ Com_Memset(aasworld.hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));
} //end else
besttraveltime = 0;
bestarea = 0;
@@ -2179,8 +2219,8 @@ int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, ve
//
if (besttraveltime && t >= besttraveltime) continue;
//
- if (!hidetraveltimes[nextareanum] ||
- hidetraveltimes[nextareanum] > t)
+ if (!aasworld.hidetraveltimes[nextareanum] ||
+ aasworld.hidetraveltimes[nextareanum] > t)
{
//if the nextarea is not visible from the enemy area
if (!AAS_AreaVisible(enemyareanum, nextareanum))
@@ -2188,7 +2228,7 @@ int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, ve
besttraveltime = t;
bestarea = nextareanum;
} //end if
- hidetraveltimes[nextareanum] = t;
+ aasworld.hidetraveltimes[nextareanum] = t;
nextupdate = &aasworld.areaupdate[nextareanum];
nextupdate->areanum = nextareanum;
nextupdate->tmptraveltime = t;
@@ -2210,3 +2250,28 @@ int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, ve
} //end while
return bestarea;
} //end of the function AAS_NearestHideArea
+
+void AAS_SetAASBlockingEntity(vec3_t mins, vec3_t maxs, qboolean blocking)
+{
+ int arealist[256];
+ int w, a, c;
+ for (w = 0; w < MAX_AAS_WORLDS; w++)
+ {
+ AAS_SetCurrentWorld(w);
+ if (aasworld.loaded)
+ {
+ c = AAS_BBoxAreas(mins, maxs, arealist, sizeof(arealist)/sizeof(arealist[0]));
+ for (a = 0; a < c; a++)
+ AAS_EnableRoutingArea(arealist[a], !blocking);
+ }
+ }
+}
+qboolean AAS_RT_GetHidePos(vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, vec3_t returnPos)
+{
+ int area = AAS_NearestHideArea(srcnum, srcpos, srcarea, destnum, destpos, destarea, TFL_WALK|TFL_SWIM|TFL_CROUCH|TFL_WATER);
+ if (!area)
+ return qfalse;
+ VectorCopy(aasworld.areas[area].center, returnPos);
+ return qtrue;
+}
+#endif
diff --git a/plugins/quake3/botlib/be_aas_route.h b/plugins/quake3/botlib/be_aas_route.h
index 8805c66f6..5137f9086 100644
--- a/plugins/quake3/botlib/be_aas_route.h
+++ b/plugins/quake3/botlib/be_aas_route.h
@@ -64,4 +64,7 @@ int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origi
int goalareanum, int travelflags, int maxareas, int maxtime,
int stopevent, int stopcontents, int stoptfl, int stopareanum);
-
+#ifdef RTCW
+void AAS_SetAASBlockingEntity(vec3_t mins, vec3_t maxs, qboolean blocking);
+qboolean AAS_RT_GetHidePos(vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, vec3_t returnPos);
+#endif
diff --git a/plugins/quake3/botlib/be_ai_char.c b/plugins/quake3/botlib/be_ai_char.c
index 0754eb62f..2f173b966 100644
--- a/plugins/quake3/botlib/be_ai_char.c
+++ b/plugins/quake3/botlib/be_ai_char.c
@@ -222,7 +222,7 @@ bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
source = LoadSourceFile(charfile);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", charfile);
return NULL;
} //end if
ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
diff --git a/plugins/quake3/botlib/be_ai_chat.c b/plugins/quake3/botlib/be_ai_chat.c
index 6d222100a..dac403114 100644
--- a/plugins/quake3/botlib/be_ai_chat.c
+++ b/plugins/quake3/botlib/be_ai_chat.c
@@ -630,7 +630,7 @@ bot_synonymlist_t *BotLoadSynonyms(char *filename)
source = LoadSourceFile(filename);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", filename);
return NULL;
} //end if
//
@@ -986,7 +986,7 @@ bot_randomlist_t *BotLoadRandomStrings(char *filename)
source = LoadSourceFile(filename);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", filename);
return NULL;
} //end if
//
@@ -1289,7 +1289,7 @@ bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile)
source = LoadSourceFile(matchfile);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", matchfile);
return NULL;
} //end if
//
@@ -1863,7 +1863,7 @@ bot_replychat_t *BotLoadReplyChat(char *filename)
source = LoadSourceFile(filename);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", filename);
return NULL;
} //end if
//
@@ -2062,7 +2062,7 @@ bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname)
source = LoadSourceFile(chatfile);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", chatfile);
return NULL;
} //end if
//chat structure
@@ -2824,15 +2824,16 @@ void BotEnterChat(int chatstate, int clientto, int sendto)
botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
}
else {
+ int clientfrom = (cs->client>=0)?cs->client:clientto; //rtcw doesn't have cs->client state, and uses the 'clientto' arg for that instead.
switch(sendto) {
case CHAT_TEAM:
- EA_Command(cs->client, va("say_team %s", cs->chatmessage));
+ EA_Command(clientfrom, va("say_team %s", cs->chatmessage));
break;
case CHAT_TELL:
- EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage));
+ EA_Command(clientfrom, va("tell %d %s", clientto, cs->chatmessage));
break;
default: //CHAT_ALL
- EA_Command(cs->client, va("say %s", cs->chatmessage));
+ EA_Command(clientfrom, va("say %s", cs->chatmessage));
break;
}
}
diff --git a/plugins/quake3/botlib/be_ai_goal.c b/plugins/quake3/botlib/be_ai_goal.c
index df24c1129..1895c6026 100644
--- a/plugins/quake3/botlib/be_ai_goal.c
+++ b/plugins/quake3/botlib/be_ai_goal.c
@@ -189,9 +189,9 @@ maplocation_t *maplocations = NULL;
//camp spots
campspot_t *campspots = NULL;
//the game type
-int g_gametype = 0;
+static int g_gametype = 0;
//additional dropped item weight
-libvar_t *droppedweight = NULL;
+static libvar_t *droppedweight = NULL;
//========================================================================
//
@@ -288,7 +288,7 @@ itemconfig_t *LoadItemConfig(char *filename)
PC_SetBaseFolder(BOTFILESBASEFOLDER);
source = LoadSourceFile( path );
if( !source ) {
- botimport.Print( PRT_ERROR, "counldn't load %s\n", path );
+ botimport.Print( PRT_ERROR, "couldn't load %s\n", path );
return NULL;
} //end if
//initialize item config
diff --git a/plugins/quake3/botlib/be_ai_weap.c b/plugins/quake3/botlib/be_ai_weap.c
index 227c377e9..6467e872c 100644
--- a/plugins/quake3/botlib/be_ai_weap.c
+++ b/plugins/quake3/botlib/be_ai_weap.c
@@ -225,7 +225,7 @@ weaponconfig_t *LoadWeaponConfig(char *filename)
source = LoadSourceFile(path);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", path);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", path);
return NULL;
} //end if
//initialize weapon config
diff --git a/plugins/quake3/botlib/be_ai_weight.c b/plugins/quake3/botlib/be_ai_weight.c
index d8b986caa..74570b8a3 100644
--- a/plugins/quake3/botlib/be_ai_weight.c
+++ b/plugins/quake3/botlib/be_ai_weight.c
@@ -328,7 +328,7 @@ weightconfig_t *ReadWeightConfig(char *filename)
source = LoadSourceFile(filename);
if (!source)
{
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
+ botimport.Print(PRT_ERROR, "couldn't load %s\n", filename);
return NULL;
} //end if
//
diff --git a/plugins/quake3/botlib/be_ea.c b/plugins/quake3/botlib/be_ea.c
index 315866886..30ae79ce8 100644
--- a/plugins/quake3/botlib/be_ea.c
+++ b/plugins/quake3/botlib/be_ea.c
@@ -73,13 +73,14 @@ void EA_Tell(int client, int clientto, char *str)
{
botimport.BotClientCommand(client, va("tell %d, %s", clientto, str));
} //end of the function EA_SayTeam
+#ifdef RTCW
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
-void EA_UseItem(int client, char *it)
+void EA_UseItem(int client, const char *it)
{
botimport.BotClientCommand(client, va("use %s", it));
} //end of the function EA_UseItem
@@ -89,7 +90,7 @@ void EA_UseItem(int client, char *it)
// Returns: -
// Changes Globals: -
//===========================================================================
-void EA_DropItem(int client, char *it)
+void EA_DropItem(int client, const char *it)
{
botimport.BotClientCommand(client, va("drop %s", it));
} //end of the function EA_DropItem
@@ -99,7 +100,7 @@ void EA_DropItem(int client, char *it)
// Returns: -
// Changes Globals: -
//===========================================================================
-void EA_UseInv(int client, char *inv)
+void EA_UseInv(int client, const char *inv)
{
botimport.BotClientCommand(client, va("invuse %s", inv));
} //end of the function EA_UseInv
@@ -109,10 +110,16 @@ void EA_UseInv(int client, char *inv)
// Returns: -
// Changes Globals: -
//===========================================================================
-void EA_DropInv(int client, char *inv)
+void EA_DropInv(int client, const char *inv)
{
botimport.BotClientCommand(client, va("invdrop %s", inv));
} //end of the function EA_DropInv
+void EA_Reload(int client)
+{
+ bot_input_t *bi = &botinputs[client];
+ bi->actionflags |= ACTION_RELOAD;
+}
+#endif
//===========================================================================
//
// Parameter: -
@@ -436,7 +443,7 @@ void EA_GetInput(int client, float thinktime, bot_input_t *input)
// Returns: -
// Changes Globals: -
//===========================================================================
-void EA_ResetInput(int client)
+void EA_ResetInput(int client, bot_input_t *init)
{
bot_input_t *bi;
int jumped = qfalse;
@@ -449,6 +456,9 @@ void EA_ResetInput(int client)
jumped = bi->actionflags & ACTION_JUMP;
bi->actionflags = 0;
if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME;
+
+ if (init)
+ *bi = *init;
} //end of the function EA_ResetInput
//===========================================================================
//
diff --git a/plugins/quake3/botlib/be_ea.h b/plugins/quake3/botlib/be_ea.h
index 4fb37046c..fc19a61dc 100644
--- a/plugins/quake3/botlib/be_ea.h
+++ b/plugins/quake3/botlib/be_ea.h
@@ -60,7 +60,15 @@ void EA_View(int client, vec3_t viewangles);
//send regular input to the server
void EA_EndRegular(int client, float thinktime);
void EA_GetInput(int client, float thinktime, bot_input_t *input);
-void EA_ResetInput(int client);
+void EA_ResetInput(int client, bot_input_t *init);
//setup and shutdown routines
int EA_Setup(void);
void EA_Shutdown(void);
+
+#ifdef RTCW
+void EA_UseItem(int client, const char *it);
+void EA_DropItem(int client, const char *it);
+void EA_UseInv(int client, const char *inv);
+void EA_DropInv(int client, const char *inv);
+void EA_Reload(int client);
+#endif
\ No newline at end of file
diff --git a/plugins/quake3/botlib/be_interface.c b/plugins/quake3/botlib/be_interface.c
index 73d537d49..72f6ba89f 100644
--- a/plugins/quake3/botlib/be_interface.c
+++ b/plugins/quake3/botlib/be_interface.c
@@ -175,12 +175,14 @@ int Export_BotLibSetup(void)
if (errnum != BLERR_NOERROR) return errnum;
errnum = EA_Setup(); //be_ea.c
if (errnum != BLERR_NOERROR) return errnum;
+#ifndef RTCW
errnum = BotSetupWeaponAI(); //be_ai_weap.c
if (errnum != BLERR_NOERROR)return errnum;
errnum = BotSetupGoalAI(); //be_ai_goal.c
if (errnum != BLERR_NOERROR) return errnum;
errnum = BotSetupChatAI(); //be_ai_chat.c
if (errnum != BLERR_NOERROR) return errnum;
+#endif
errnum = BotSetupMoveAI(); //be_ai_move.c
if (errnum != BLERR_NOERROR) return errnum;
@@ -197,7 +199,8 @@ int Export_BotLibSetup(void)
//===========================================================================
int Export_BotLibShutdown(void)
{
- if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP;
+ int ret = BLERR_NOERROR;
+ if (!BotLibSetup("BotLibShutdown")) ret = BLERR_LIBRARYNOTSETUP;
#ifndef DEMO
//DumpFileCRCs();
#endif //DEMO
@@ -230,7 +233,7 @@ int Export_BotLibShutdown(void)
// print any files still open
PC_CheckOpenSourceHandles();
//
- return BLERR_NOERROR;
+ return ret;
} //end of the function Export_BotLibShutdown
//===========================================================================
//
@@ -722,6 +725,16 @@ static void Init_AAS_Export( aas_export_t *aas ) {
//--------------------------------------------
aas->AAS_Swimming = AAS_Swimming;
aas->AAS_PredictClientMovement = AAS_PredictClientMovement;
+
+#ifdef RTCW
+ //rtcw
+// aas->AAS_RT_ShowRoute = AAS_RT_ShowRoute;
+ aas->AAS_RT_GetHidePos = AAS_RT_GetHidePos;
+// aas->AAS_FindAttackSpotWithinRange = AAS_FindAttackSpotWithinRange;
+// aas->AAS_GetRouteFirstVisPos = AAS_GetRouteFirstVisPos;
+ aas->AAS_SetAASBlockingEntity = AAS_SetAASBlockingEntity;
+ aas->AAS_SetCurrentWorld = AAS_SetCurrentWorld;
+#endif
}
@@ -758,6 +771,15 @@ static void Init_EA_Export( ea_export_t *ea ) {
ea->EA_GetInput = EA_GetInput;
ea->EA_EndRegular = EA_EndRegular;
ea->EA_ResetInput = EA_ResetInput;
+
+#ifdef RTCW
+ //rtcw
+ ea->EA_UseItem = EA_UseItem;
+ ea->EA_DropItem = EA_DropItem;
+ ea->EA_UseInv = EA_UseInv;
+ ea->EA_DropInv = EA_DropInv;
+ ea->EA_Reload = EA_Reload;
+#endif
}
diff --git a/plugins/quake3/botlib/botlib.h b/plugins/quake3/botlib/botlib.h
index 37eabeaa8..fe6a332e4 100644
--- a/plugins/quake3/botlib/botlib.h
+++ b/plugins/quake3/botlib/botlib.h
@@ -29,7 +29,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
+#ifdef RTCW
+#define BOTLIB_API_VERSION 0x00010002
+#else
#define BOTLIB_API_VERSION 2
+#endif
struct aas_clientmove_s;
struct aas_entityinfo_s;
@@ -79,10 +83,30 @@ struct weaponinfo_s;
#define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config
//action flags
+#ifdef RTCW
+#define ACTION_ATTACK 0x00000001
+#define ACTION_USE 0x00000002
+#define ACTION_RESPAWN 4
+#define ACTION_JUMP 8
+#define ACTION_MOVEUP ACTION_JUMP
+#define ACTION_CROUCH 16
+#define ACTION_MOVEDOWN ACTION_CROUCH
+#define ACTION_MOVEFORWARD 32
+#define ACTION_MOVEBACK 64
+#define ACTION_MOVELEFT 128
+#define ACTION_MOVERIGHT 256
+#define ACTION_DELAYEDJUMP 512
+#define ACTION_TALK 1024
+#define ACTION_GESTURE 2048
+#define ACTION_WALK 4096
+#define ACTION_RELOAD 8192
+
+#define ACTION_JUMPEDLASTFRAME 0x10000000
+#else
#define ACTION_ATTACK 0x00000001
-#define ACTION_USE 0x00000002
+#define ACTION_USE 0x00000002
#define ACTION_RESPAWN 0x00000008
-#define ACTION_JUMP 0x00000010
+#define ACTION_JUMP 0x00000010
#define ACTION_MOVEUP 0x00000020
#define ACTION_CROUCH 0x00000080
#define ACTION_MOVEDOWN 0x00000100
@@ -91,16 +115,17 @@ struct weaponinfo_s;
#define ACTION_MOVELEFT 0x00001000
#define ACTION_MOVERIGHT 0x00002000
#define ACTION_DELAYEDJUMP 0x00008000
-#define ACTION_TALK 0x00010000
+#define ACTION_TALK 0x00010000
#define ACTION_GESTURE 0x00020000
-#define ACTION_WALK 0x00080000
+#define ACTION_WALK 0x00080000
#define ACTION_AFFIRMATIVE 0x00100000
#define ACTION_NEGATIVE 0x00200000
#define ACTION_GETFLAG 0x00800000
#define ACTION_GUARDBASE 0x01000000
#define ACTION_PATROL 0x02000000
#define ACTION_FOLLOWME 0x08000000
-#define ACTION_JUMPEDLASTFRAME 0x10000000
+#define ACTION_JUMPEDLASTFRAME 0x10000000
+#endif
//the bot input, will be converted to an usercmd_t
typedef struct bot_input_s
@@ -172,39 +197,39 @@ typedef struct botlib_import_s
//print messages from the bot library
void (QDECL *Print)(int type, char *fmt, ...);
//trace a bbox through the world
- void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask);
+ void (QDECL *Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask);
//trace a bbox against a specific entity
- void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask);
+ void (QDECL *EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask);
//retrieve the contents at the given point
- int (*PointContents)(vec3_t point);
+ int (QDECL *PointContents)(vec3_t point);
//check if the point is in potential visible sight
- int (*inPVS)(vec3_t p1, vec3_t p2);
+ int (QDECL *inPVS)(vec3_t p1, vec3_t p2);
//retrieve the BSP entity data lump
- char *(*BSPEntityData)(void);
+ const char *(QDECL *BSPEntityData)(void);
//
- void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);
+ void (QDECL *BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);
//send a bot client command
- void (*BotClientCommand)(int client, char *command);
+ void (QDECL *BotClientCommand)(int client, char *command);
//memory allocation
- void *(*GetMemory)(int size); // allocate from Zone
- void (*FreeMemory)(void *ptr); // free memory from Zone
- int (*AvailableMemory)(void); // available Zone memory
- void *(*HunkAlloc)(int size); // allocate from hunk
+ void *(QDECL *GetMemory)(int size); // allocate from Zone
+ void (QDECL *FreeMemory)(void *ptr); // free memory from Zone
+ int (QDECL *AvailableMemory)(void); // available Zone memory
+ void *(QDECL *HunkAlloc)(int size); // allocate from hunk
//file system access
- int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode );
- int (*FS_Read)( void *buffer, int len, fileHandle_t f );
- int (*FS_Write)( const void *buffer, int len, fileHandle_t f );
- void (*FS_FCloseFile)( fileHandle_t f );
- int (*FS_Seek)( fileHandle_t f, long offset, int origin );
+ int (QDECL *FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode );
+ int (QDECL *FS_Read)( void *buffer, int len, fileHandle_t f );
+ int (QDECL *FS_Write)( const void *buffer, int len, fileHandle_t f );
+ void (QDECL *FS_FCloseFile)( fileHandle_t f );
+ int (QDECL *FS_Seek)( fileHandle_t f, long offset, int origin );
//debug visualisation stuff
- int (*DebugLineCreate)(void);
- void (*DebugLineDelete)(int line);
- void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color);
+ int (QDECL *DebugLineCreate)(void);
+ void (QDECL *DebugLineDelete)(int line);
+ void (QDECL *DebugLineShow)(int line, vec3_t start, vec3_t end, int color);
//
- int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points);
- void (*DebugPolygonDelete)(int id);
+ int (QDECL *DebugPolygonCreate)(int color, int numPoints, vec3_t *points);
+ void (QDECL *DebugPolygonDelete)(int id);
- void (*Error)(const char *msg); //for unrecoverable errors only. Will quit out.
+ void (QDECL *Error)(const char *msg); //for unrecoverable errors only. Will quit out.
} botlib_import_t;
typedef struct aas_export_s
@@ -212,91 +237,109 @@ typedef struct aas_export_s
//-----------------------------------
// be_aas_entity.h
//-----------------------------------
- void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info);
+ void (QDECL *AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info);
//-----------------------------------
// be_aas_main.h
//-----------------------------------
- int (*AAS_Initialized)(void);
- void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs);
- float (*AAS_Time)(void);
+ int (QDECL *AAS_Initialized)(void);
+ void (QDECL *AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs);
+ float (QDECL *AAS_Time)(void);
//--------------------------------------------
// be_aas_sample.c
//--------------------------------------------
- int (*AAS_PointAreaNum)(vec3_t point);
- int (*AAS_PointReachabilityAreaIndex)( vec3_t point );
- int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);
- int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);
- int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info );
+ int (QDECL *AAS_PointAreaNum)(vec3_t point);
+ int (QDECL *AAS_PointReachabilityAreaIndex)( vec3_t point );
+ int (QDECL *AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);
+ int (QDECL *AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);
+ int (QDECL *AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info );
//--------------------------------------------
// be_aas_bspq3.c
//--------------------------------------------
- int (*AAS_PointContents)(vec3_t point);
- int (*AAS_NextBSPEntity)(int ent);
- int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size);
- int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v);
- int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value);
- int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value);
+ int (QDECL *AAS_PointContents)(vec3_t point);
+ int (QDECL *AAS_NextBSPEntity)(int ent);
+ int (QDECL *AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size);
+ int (QDECL *AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v);
+ int (QDECL *AAS_FloatForBSPEpairKey)(int ent, char *key, float *value);
+ int (QDECL *AAS_IntForBSPEpairKey)(int ent, char *key, int *value);
//--------------------------------------------
// be_aas_reach.c
//--------------------------------------------
- int (*AAS_AreaReachability)(int areanum);
+ int (QDECL *AAS_AreaReachability)(int areanum);
//--------------------------------------------
// be_aas_route.c
//--------------------------------------------
- int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags);
- int (*AAS_EnableRoutingArea)(int areanum, int enable);
- int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin,
+ int (QDECL *AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags);
+ int (QDECL *AAS_EnableRoutingArea)(int areanum, int enable);
+ int (QDECL *AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin,
int goalareanum, int travelflags, int maxareas, int maxtime,
int stopevent, int stopcontents, int stoptfl, int stopareanum);
//--------------------------------------------
// be_aas_altroute.c
//--------------------------------------------
- int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,
+ int (QDECL *AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,
struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals,
int type);
//--------------------------------------------
// be_aas_move.c
//--------------------------------------------
- int (*AAS_Swimming)(vec3_t origin);
- int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move,
+ int (QDECL *AAS_Swimming)(vec3_t origin);
+ int (QDECL *AAS_PredictClientMovement)(struct aas_clientmove_s *move,
int entnum, vec3_t origin,
int presencetype, int onground,
vec3_t velocity, vec3_t cmdmove,
int cmdframes,
int maxframes, float frametime,
int stopevent, int stopareanum, int visualize);
+
+#ifdef RTCW
+ //rtcw additions
+// void (QDECL *AAS_RT_ShowRoute)(vec3_t srcpos, int srcnum, int destnum);
+ qboolean (QDECL *AAS_RT_GetHidePos)(vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, vec3_t returnPos);
+ int (QDECL *AAS_FindAttackSpotWithinRange)(int srcnum, int rangenum, int enemynum, float rangedist, int travelflags, float *outpos);
+ qboolean (QDECL *AAS_GetRouteFirstVisPos)(vec3_t srcpos, vec3_t destpos, int travelflags, vec3_t retpos);
+ void (QDECL *AAS_SetAASBlockingEntity)(vec3_t absmin, vec3_t absmax, qboolean blocking);
+ void (QDECL *AAS_SetCurrentWorld)(int index);
+#endif
} aas_export_t;
typedef struct ea_export_s
{
//ClientCommand elementary actions
- void (*EA_Command)(int client, char *command );
- void (*EA_Say)(int client, char *str);
- void (*EA_SayTeam)(int client, char *str);
+ void (QDECL *EA_Command)(int client, char *command );
+ void (QDECL *EA_Say)(int client, char *str);
+ void (QDECL *EA_SayTeam)(int client, char *str);
//
- void (*EA_Action)(int client, int action);
- void (*EA_Gesture)(int client);
- void (*EA_Talk)(int client);
- void (*EA_Attack)(int client);
- void (*EA_Use)(int client);
- void (*EA_Respawn)(int client);
- void (*EA_MoveUp)(int client);
- void (*EA_MoveDown)(int client);
- void (*EA_MoveForward)(int client);
- void (*EA_MoveBack)(int client);
- void (*EA_MoveLeft)(int client);
- void (*EA_MoveRight)(int client);
- void (*EA_Crouch)(int client);
-
- void (*EA_SelectWeapon)(int client, int weapon);
- void (*EA_Jump)(int client);
- void (*EA_DelayedJump)(int client);
- void (*EA_Move)(int client, vec3_t dir, float speed);
- void (*EA_View)(int client, vec3_t viewangles);
+ void (QDECL *EA_Action)(int client, int action);
+ void (QDECL *EA_Gesture)(int client);
+ void (QDECL *EA_Talk)(int client);
+ void (QDECL *EA_Attack)(int client);
+ void (QDECL *EA_Use)(int client);
+ void (QDECL *EA_Respawn)(int client);
+ void (QDECL *EA_MoveUp)(int client);
+ void (QDECL *EA_MoveDown)(int client);
+ void (QDECL *EA_MoveForward)(int client);
+ void (QDECL *EA_MoveBack)(int client);
+ void (QDECL *EA_MoveLeft)(int client);
+ void (QDECL *EA_MoveRight)(int client);
+ void (QDECL *EA_Crouch)(int client);
+
+ void (QDECL *EA_SelectWeapon)(int client, int weapon);
+ void (QDECL *EA_Jump)(int client);
+ void (QDECL *EA_DelayedJump)(int client);
+ void (QDECL *EA_Move)(int client, vec3_t dir, float speed);
+ void (QDECL *EA_View)(int client, vec3_t viewangles);
//send regular input to the server
- void (*EA_EndRegular)(int client, float thinktime);
- void (*EA_GetInput)(int client, float thinktime, bot_input_t *input);
- void (*EA_ResetInput)(int client);
+ void (QDECL *EA_EndRegular)(int client, float thinktime);
+ void (QDECL *EA_GetInput)(int client, float thinktime, bot_input_t *input);
+ void (QDECL *EA_ResetInput)(int client, bot_input_t *init);
+
+#ifdef RTCW
+ void (QDECL *EA_UseItem)(int client, const char *it);
+ void (QDECL *EA_DropItem)(int client, const char *it);
+ void (QDECL *EA_UseInv)(int client, const char *inv);
+ void (QDECL *EA_DropInv)(int client, const char *inv);
+ void (QDECL *EA_Reload)(int client);
+#endif
} ea_export_t;
typedef struct ai_export_s
@@ -304,97 +347,97 @@ typedef struct ai_export_s
//-----------------------------------
// be_ai_char.h
//-----------------------------------
- int (*BotLoadCharacter)(char *charfile, float skill);
- void (*BotFreeCharacter)(int character);
- float (*Characteristic_Float)(int character, int index);
- float (*Characteristic_BFloat)(int character, int index, float min, float max);
- int (*Characteristic_Integer)(int character, int index);
- int (*Characteristic_BInteger)(int character, int index, int min, int max);
- void (*Characteristic_String)(int character, int index, char *buf, int size);
+ int (QDECL *BotLoadCharacter)(char *charfile, float skill);
+ void (QDECL *BotFreeCharacter)(int character);
+ float (QDECL *Characteristic_Float)(int character, int index);
+ float (QDECL *Characteristic_BFloat)(int character, int index, float min, float max);
+ int (QDECL *Characteristic_Integer)(int character, int index);
+ int (QDECL *Characteristic_BInteger)(int character, int index, int min, int max);
+ void (QDECL *Characteristic_String)(int character, int index, char *buf, int size);
//-----------------------------------
// be_ai_chat.h
//-----------------------------------
- int (*BotAllocChatState)(void);
- void (*BotFreeChatState)(int handle);
- void (*BotQueueConsoleMessage)(int chatstate, int type, char *message);
- void (*BotRemoveConsoleMessage)(int chatstate, int handle);
- int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm);
- int (*BotNumConsoleMessages)(int chatstate);
- void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
- int (*BotNumInitialChats)(int chatstate, char *type);
- int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
- int (*BotChatLength)(int chatstate);
- void (*BotEnterChat)(int chatstate, int client, int sendto);
- void (*BotGetChatMessage)(int chatstate, char *buf, int size);
- int (*StringContains)(char *str1, char *str2, int casesensitive);
- int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context);
- void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size);
- void (*UnifyWhiteSpaces)(char *string);
- void (*BotReplaceSynonyms)(char *string, unsigned long int context);
- int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname);
- void (*BotSetChatGender)(int chatstate, int gender);
- void (*BotSetChatName)(int chatstate, char *name, int client);
+ int (QDECL *BotAllocChatState)(void);
+ void (QDECL *BotFreeChatState)(int handle);
+ void (QDECL *BotQueueConsoleMessage)(int chatstate, int type, char *message);
+ void (QDECL *BotRemoveConsoleMessage)(int chatstate, int handle);
+ int (QDECL *BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm);
+ int (QDECL *BotNumConsoleMessages)(int chatstate);
+ void (QDECL *BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
+ int (QDECL *BotNumInitialChats)(int chatstate, char *type);
+ int (QDECL *BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
+ int (QDECL *BotChatLength)(int chatstate);
+ void (QDECL *BotEnterChat)(int chatstate, int client, int sendto);
+ void (QDECL *BotGetChatMessage)(int chatstate, char *buf, int size);
+ int (QDECL *StringContains)(char *str1, char *str2, int casesensitive);
+ int (QDECL *BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context);
+ void (QDECL *BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size);
+ void (QDECL *UnifyWhiteSpaces)(char *string);
+ void (QDECL *BotReplaceSynonyms)(char *string, unsigned long int context);
+ int (QDECL *BotLoadChatFile)(int chatstate, char *chatfile, char *chatname);
+ void (QDECL *BotSetChatGender)(int chatstate, int gender);
+ void (QDECL *BotSetChatName)(int chatstate, char *name, int client);
//-----------------------------------
// be_ai_goal.h
//-----------------------------------
- void (*BotResetGoalState)(int goalstate);
- void (*BotResetAvoidGoals)(int goalstate);
- void (*BotRemoveFromAvoidGoals)(int goalstate, int number);
- void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal);
- void (*BotPopGoal)(int goalstate);
- void (*BotEmptyGoalStack)(int goalstate);
- void (*BotDumpAvoidGoals)(int goalstate);
- void (*BotDumpGoalStack)(int goalstate);
- void (*BotGoalName)(int number, char *name, int size);
- int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal);
- int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal);
- int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags);
- int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags,
+ void (QDECL *BotResetGoalState)(int goalstate);
+ void (QDECL *BotResetAvoidGoals)(int goalstate);
+ void (QDECL *BotRemoveFromAvoidGoals)(int goalstate, int number);
+ void (QDECL *BotPushGoal)(int goalstate, struct bot_goal_s *goal);
+ void (QDECL *BotPopGoal)(int goalstate);
+ void (QDECL *BotEmptyGoalStack)(int goalstate);
+ void (QDECL *BotDumpAvoidGoals)(int goalstate);
+ void (QDECL *BotDumpGoalStack)(int goalstate);
+ void (QDECL *BotGoalName)(int number, char *name, int size);
+ int (QDECL *BotGetTopGoal)(int goalstate, struct bot_goal_s *goal);
+ int (QDECL *BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal);
+ int (QDECL *BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags);
+ int (QDECL *BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags,
struct bot_goal_s *ltg, float maxtime);
- int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal);
- int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal);
- int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal);
- int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal);
- int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal);
- float (*BotAvoidGoalTime)(int goalstate, int number);
- void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime);
- void (*BotInitLevelItems)(void);
- void (*BotUpdateEntityItems)(void);
- int (*BotLoadItemWeights)(int goalstate, char *filename);
- void (*BotFreeItemWeights)(int goalstate);
- void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child);
- void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename);
- void (*BotMutateGoalFuzzyLogic)(int goalstate, float range);
- int (*BotAllocGoalState)(int client);
- void (*BotFreeGoalState)(int handle);
+ int (QDECL *BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal);
+ int (QDECL *BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal);
+ int (QDECL *BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal);
+ int (QDECL *BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal);
+ int (QDECL *BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal);
+ float (QDECL *BotAvoidGoalTime)(int goalstate, int number);
+ void (QDECL *BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime);
+ void (QDECL *BotInitLevelItems)(void);
+ void (QDECL *BotUpdateEntityItems)(void);
+ int (QDECL *BotLoadItemWeights)(int goalstate, char *filename);
+ void (QDECL *BotFreeItemWeights)(int goalstate);
+ void (QDECL *BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child);
+ void (QDECL *BotSaveGoalFuzzyLogic)(int goalstate, char *filename);
+ void (QDECL *BotMutateGoalFuzzyLogic)(int goalstate, float range);
+ int (QDECL *BotAllocGoalState)(int client);
+ void (QDECL *BotFreeGoalState)(int handle);
//-----------------------------------
// be_ai_move.h
//-----------------------------------
- void (*BotResetMoveState)(int movestate);
- void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags);
- int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type);
- void (*BotResetAvoidReach)(int movestate);
- void (*BotResetLastAvoidReach)(int movestate);
- int (*BotReachabilityArea)(vec3_t origin, int testground);
- int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target);
- int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target);
- int (*BotAllocMoveState)(void);
- void (*BotFreeMoveState)(int handle);
- void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove);
- void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type);
+ void (QDECL *BotResetMoveState)(int movestate);
+ void (QDECL *BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags);
+ int (QDECL *BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type);
+ void (QDECL *BotResetAvoidReach)(int movestate);
+ void (QDECL *BotResetLastAvoidReach)(int movestate);
+ int (QDECL *BotReachabilityArea)(vec3_t origin, int testground);
+ int (QDECL *BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target);
+ int (QDECL *BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target);
+ int (QDECL *BotAllocMoveState)(void);
+ void (QDECL *BotFreeMoveState)(int handle);
+ void (QDECL *BotInitMoveState)(int handle, struct bot_initmove_s *initmove);
+ void (QDECL *BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type);
//-----------------------------------
// be_ai_weap.h
//-----------------------------------
- int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory);
- void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo);
- int (*BotLoadWeaponWeights)(int weaponstate, char *filename);
- int (*BotAllocWeaponState)(void);
- void (*BotFreeWeaponState)(int weaponstate);
- void (*BotResetWeaponState)(int weaponstate);
+ int (QDECL *BotChooseBestFightWeapon)(int weaponstate, int *inventory);
+ void (QDECL *BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo);
+ int (QDECL *BotLoadWeaponWeights)(int weaponstate, char *filename);
+ int (QDECL *BotAllocWeaponState)(void);
+ void (QDECL *BotFreeWeaponState)(int weaponstate);
+ void (QDECL *BotResetWeaponState)(int weaponstate);
//-----------------------------------
// be_ai_gen.h
//-----------------------------------
- int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child);
+ int (QDECL *GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child);
} ai_export_t;
//bot AI library imported functions
@@ -407,29 +450,29 @@ typedef struct botlib_export_s
//AI functions
ai_export_t ai;
//setup the bot library, returns BLERR_
- int (*BotLibSetup)(void);
+ int (QDECL *BotLibSetup)(void);
//shutdown the bot library, returns BLERR_
- int (*BotLibShutdown)(void);
+ int (QDECL *BotLibShutdown)(void);
//sets a library variable returns BLERR_
- int (*BotLibVarSet)(char *var_name, char *value);
+ int (QDECL *BotLibVarSet)(char *var_name, char *value);
//gets a library variable returns BLERR_
- int (*BotLibVarGet)(char *var_name, char *value, int size);
+ int (QDECL *BotLibVarGet)(char *var_name, char *value, int size);
//sets a C-like define returns BLERR_
- int (*PC_AddGlobalDefine)(char *string);
- int (*PC_LoadSourceHandle)(const char *filename);
- int (*PC_FreeSourceHandle)(int handle);
- int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token);
- int (*PC_SourceFileAndLine)(int handle, char *filename, int *line);
+ int (QDECL *PC_AddGlobalDefine)(char *string);
+ int (QDECL *PC_LoadSourceHandle)(const char *filename);
+ int (QDECL *PC_FreeSourceHandle)(int handle);
+ int (QDECL *PC_ReadTokenHandle)(int handle, pc_token_t *pc_token);
+ int (QDECL *PC_SourceFileAndLine)(int handle, char *filename, int *line);
//start a frame in the bot library
- int (*BotLibStartFrame)(float time);
+ int (QDECL *BotLibStartFrame)(float time);
//load a new map in the bot library
- int (*BotLibLoadMap)(const char *mapname);
+ int (QDECL *BotLibLoadMap)(const char *mapname);
//entity updates
- int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state);
+ int (QDECL *BotLibUpdateEntity)(int ent, bot_entitystate_t *state);
//just for testing
- int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3);
+ int (QDECL *Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3);
} botlib_export_t;
//linking of bot library
diff --git a/plugins/quake3/botlib/l_memory.c b/plugins/quake3/botlib/l_memory.c
index 4fc0a5163..5486e020b 100644
--- a/plugins/quake3/botlib/l_memory.c
+++ b/plugins/quake3/botlib/l_memory.c
@@ -41,12 +41,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MEM_ID 0x12345678l
#define HUNK_ID 0x87654321l
-int allocatedmemory;
-int totalmemorysize;
-int numblocks;
-
#ifdef MEMORYMANEGER
+static int allocatedmemory;
+static int totalmemorysize;
+static int numblocks;
+
typedef struct memoryblock_s
{
unsigned long int id;
@@ -424,13 +424,18 @@ void *GetClearedHunkMemory(unsigned long size)
void FreeMemory(void *ptr)
{
unsigned long int *memid;
+ if (!ptr)
+ return;
memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int));
if (*memid == MEM_ID)
{
+ *memid = 0;
botimport.FreeMemory(memid);
} //end if
+ else if (*memid != HUNK_ID)
+ botimport.Print(PRT_FATAL, "invalid memory sentinal\n");
} //end of the function FreeMemory
//===========================================================================
//
diff --git a/plugins/quake3/botlib/q_shared.h b/plugins/quake3/botlib/q_shared.h
index 55b5a7c9e..2bccd4b91 100644
--- a/plugins/quake3/botlib/q_shared.h
+++ b/plugins/quake3/botlib/q_shared.h
@@ -23,6 +23,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef __Q_SHARED_H
#define __Q_SHARED_H
+#if defined(RTCWMP) || defined(RTCWSP)
+ #define RTCW
+#endif
+
// q_shared.h -- included first by ALL program modules.
// A user mod should never modify this file
@@ -213,6 +217,13 @@ typedef int clipHandle_t;
#define YAW 1 // left / right
#define ROLL 2 // fall over
+#define MAX_QPATH 64 // max length of a quake game pathname
+#ifdef PATH_MAX
+#define MAX_OSPATH PATH_MAX
+#else
+#define MAX_OSPATH 256 // max length of a filesystem pathname
+#endif
+
// the game guarantees that no string from the network will ever
// exceed MAX_STRING_CHARS
#define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString
@@ -227,14 +238,6 @@ typedef int clipHandle_t;
#define BIG_INFO_KEY 8192
#define BIG_INFO_VALUE 8192
-
-#define MAX_QPATH 64 // max length of a quake game pathname
-#ifdef PATH_MAX
-#define MAX_OSPATH PATH_MAX
-#else
-#define MAX_OSPATH 256 // max length of a filesystem pathname
-#endif
-
#define MAX_NAME_LENGTH 32 // max length of a client name
#define MAX_SAY_TEXT 150
@@ -1013,7 +1016,6 @@ typedef enum {
CHAN_ANNOUNCER // announcer voices, etc
} soundChannel_t;
-
/*
========================================================================
@@ -1032,7 +1034,11 @@ typedef enum {
//
// per-level limits
//
+#ifdef RTCW
+#define MAX_CLIENTS 128 // absolute limit
+#else
#define MAX_CLIENTS 64 // absolute limit
+#endif
#define MAX_LOCATIONS 64
#define GENTITYNUM_BITS 10 // don't need to send any more
@@ -1049,8 +1055,13 @@ typedef enum {
#define MAX_MODELS 256 // these are sent over the net as 8 bits
#define MAX_SOUNDS 256 // so they cannot be blindly increased
-
+#ifdef RTCW
+#define MAX_CONFIGSTRINGS 2048
+#else
#define MAX_CONFIGSTRINGS 1024
+#endif
+
+#if 0
// these are the only configstrings that the system reserves, all the
// other ones are strictly for servergame to clientgame communication
@@ -1186,14 +1197,26 @@ typedef struct playerState_s {
#define MOVE_RUN 120 // if forwardmove or rightmove are >= MOVE_RUN,
// then BUTTON_WALKING should be set
-
+#endif
// usercmd_t is sent to the server each client frame
typedef struct usercmd_s {
+#ifdef RTCW
+ int serverTime;
+ byte buttons;
+ byte wbuttons;
+ byte weapon;
+ byte holdable;
+ int angles[3];
+ signed char forwardmove, rightmove, upmove;
+ signed char wolfkick;
+ unsigned short cld;
+#else
int serverTime;
int angles[3];
int buttons;
byte weapon; // weapon
signed char forwardmove, rightmove, upmove;
+#endif
} usercmd_t;
//===================================================================
diff --git a/plugins/quake3/clq3_cg.c b/plugins/quake3/clq3_cg.c
index 6043905d7..34ad145d5 100644
--- a/plugins/quake3/clq3_cg.c
+++ b/plugins/quake3/clq3_cg.c
@@ -49,26 +49,65 @@ typedef enum {
CG_CM_TRANSFORMEDPOINTCONTENTS,
CG_CM_BOXTRACE,
CG_CM_TRANSFORMEDBOXTRACE,
+#ifdef RTCW
+ CG_CM_CAPSULETRACE,
+ CG_CM_TRANSFORMEDCAPSULETRACE,
+ CG_CM_TEMPCAPSULEMODEL,
+#endif
CG_CM_MARKFRAGMENTS,
CG_S_STARTSOUND,
+#ifdef RTCW
+ CG_S_STARTSOUNDEX,
+#endif
CG_S_STARTLOCALSOUND,
CG_S_CLEARLOOPINGSOUNDS, //30
CG_S_ADDLOOPINGSOUND,
CG_S_UPDATEENTITYPOSITION,
+#ifdef RTCW
+ CG_S_GETVOICEAMPLITUDE,
+#endif
CG_S_RESPATIALIZE,
CG_S_REGISTERSOUND,
CG_S_STARTBACKGROUNDTRACK,
+#ifdef RTCWSP
+ CG_S_FADESTREAMINGSOUND,
+ CG_S_FADEALLSOUNDS,
+#endif
+#ifdef RTCW
+ CG_S_STARTSTREAMINGSOUND,
+#endif
CG_R_LOADWORLDMAP,
CG_R_REGISTERMODEL,
CG_R_REGISTERSKIN,
CG_R_REGISTERSHADER,
+#ifdef RTCW
+ CG_R_GETSKINMODEL,
+ CG_R_GETMODELSHADER,
+ CG_R_REGISTERFONT,
+#endif
CG_R_CLEARSCENE, //40
CG_R_ADDREFENTITYTOSCENE,
+#ifdef RTCW
+ CG_GET_ENTITY_TOKEN,
+#endif
CG_R_ADDPOLYTOSCENE,
+#ifdef RTCW
+ CG_R_ADDPOLYSTOSCENE,
+#endif
+#ifdef RTCWSP
+ CG_RB_ZOMBIEFXADDNEWHIT,
+#endif
CG_R_ADDLIGHTTOSCENE,
+#ifdef RTCW
+ CG_R_ADDCORONATOSCENE,
+ CG_R_SETFOG,
+#endif
CG_R_RENDERSCENE,
CG_R_SETCOLOR,
CG_R_DRAWSTRETCHPIC,
+#ifdef RTCW
+ CG_R_DRAWSTRETCHPIC_GRADIENT,
+#endif
CG_R_MODELBOUNDS,
CG_R_LERPTAG,
CG_GETGLCONFIG,
@@ -79,9 +118,14 @@ typedef enum {
CG_GETCURRENTCMDNUMBER,
CG_GETUSERCMD,
CG_SETUSERCMDVALUE,
+#ifdef RTCWMP
+ CG_SETCLIENTLERPORIGIN,
+#endif
CG_R_REGISTERSHADERNOMIP,
CG_MEMORY_REMAINING,
+#ifndef RTCW
CG_R_REGISTERFONT,
+#endif
CG_KEY_ISDOWN, //60
CG_KEY_GETCATCHER,
CG_KEY_SETCATCHER,
@@ -95,15 +139,35 @@ typedef enum {
CG_REAL_TIME, //70
CG_SNAPVECTOR,
CG_REMOVECOMMAND,
+#ifndef RTCWSP
CG_R_LIGHTFORPOINT, //73
+#endif
+#ifdef RTCW
+ CG_SENDMOVESPEEDSTOGAME,
+#endif
CG_CIN_PLAYCINEMATIC, //74
CG_CIN_STOPCINEMATIC, //75
CG_CIN_RUNCINEMATIC, //76
CG_CIN_DRAWCINEMATIC, //77
CG_CIN_SETEXTENTS, //78
CG_R_REMAP_SHADER, //79
+#ifndef RTCWSP
CG_S_ADDREALLOOPINGSOUND, //80
+#endif
CG_S_STOPLOOPINGSOUND, //81
+#ifdef RTCWSP
+ CG_S_STOPSTREAMINGSOUND,
+#endif
+#ifdef RTCW
+ CG_LOADCAMERA,
+ CG_STARTCAMERA,
+#endif
+#ifdef RTCWSP
+ CG_STOPCAMERA,
+#endif
+#ifdef RTCW
+ CG_GETCAMERAINFO,
+#else
CG_CM_TEMPCAPSULEMODEL, //82
CG_CM_CAPSULETRACE, //83
@@ -114,8 +178,13 @@ typedef enum {
CG_R_INPVS, //88
// 1.32
CG_FS_SEEK, //89
+#endif
+#ifdef RTCWSP
+ CG_MEMSET = 110,
+#else
CG_MEMSET = 100,
+#endif
CG_MEMCPY,
CG_STRNCPY,
CG_SIN,
@@ -128,6 +197,21 @@ typedef enum {
CG_TESTPRINTFLOAT,
CG_ACOS,
+#ifdef RTCW
+ CG_INGAME_POPUP,
+ CG_INGAME_CLOSEPOPUP,
+ CG_LIMBOCHAT,
+#ifdef RTCWMP
+ CG_R_DRAWROTATEDPIC,
+ CG_KEY_GETBINDINGBUF,
+ CG_KEY_SETBINDING,
+ CG_KEY_KEYNUMTOSTRINGBUF,
+ CG_TRANSLATE_STRING,
+#else
+ CG_GETMODELINFO,
+ CG_ALLOC = 900,
+#endif
+#endif
/*CG_FTE_FINDPARTICLEEFFECT = 200,
CG_FTE_SPAWNPARTICLEEFFECT,
@@ -340,6 +424,17 @@ static qboolean CGQ3_GetUserCmd(int cmdNumber, q3usercmd_t *ucmd)
ucmd->buttons = cmd->buttons;
ucmd->weapon = cmd->weapon;
+#ifdef RTCW
+ ucmd->wbuttons = cmd->buttons>>8;
+ ucmd->wolfkick = cmd->buttons>>16;
+ ucmd->holdable = cmd->buttons>>24;
+ #ifdef RTCWMP
+ ucmd->mpSetup = cmd->weapon>>16;
+ ucmd->identClient = cmd->weapon>>24;
+ #else
+ ucmd->cld = cmd->weapon>>16;
+ #endif
+#endif
return true;
}
@@ -577,8 +672,660 @@ static void CG_ClearLoopingSounds(qboolean clearall)
}
}
+#ifdef RTCW
+static qboolean CG_SkinGetModel(skinid_t skinid, const char *key, char *result, size_t resultsize)
+{
+ skinfile_t *skin = scenefuncs->LookupSkin(skinid);
+ int i;
+ if (skin)
+ {
+ for (i = 0; i < skin->numprops; i++)
+ {
+ if (!Q_strcasecmp(skin->props[i].key, key))
+ {
+ Q_strncpyz(result, skin->props[i].value, resultsize);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#include "ui_public.h"
+typedef struct
+{
+ qboolean started;
+ float starttime;
+ float basetime;
+ float totaltime; //basetime + wait times
+ struct spline_s
+ {
+ float time;
+ int type;
+ char name[MAX_QPATH];
+ float speed;
+ size_t numcp;
+ struct
+ {
+ vec3_t pos;
+ } *cp;
+ } camera;
+ float targetbias;
+ size_t numtargets;
+ struct spline_s *targets, *curtarget;
+ struct
+ {
+ float time;
+ float fov;
+ float startfov;
+ float endfov;
+ } fov;
+
+ size_t numevents;
+ struct camevent_s
+ {
+ qboolean fired;
+ float time; //timestamp of the event
+ enum {
+ CE_INVALID=0,
+ CE_TARGET=4,
+ CE_FADEOUT=11,
+ CE_FADEIN=12
+ } type;
+ char *param;
+ } *events;
+} camdata_t;
+struct cam_parsestate_s
+{
+ const char *str;
+ char token[256];
+};
+static void Cam_Free(camdata_t *cam)
+{
+ size_t i;
+ Z_Free(cam->camera.cp);
+ for (i = 0; i < cam->numtargets; i++)
+ Z_Free(cam->targets[i].cp);
+ Z_Free(cam->targets);
+ for (i = 0; i < cam->numevents; i++)
+ Z_Free(cam->events[i].param);
+ Z_Free(cam->events);
+ memset(cam, 0, sizeof(*cam));
+}
+static qboolean Com_ParseNoLineBreak(struct cam_parsestate_s *ctx)
+{
+ char *e;
+ com_tokentype_t toktype;
+ e = cmdfuncs->ParsePunctuation(ctx->str, NULL, ctx->token, sizeof(ctx->token), &toktype);
+ if (toktype == TTP_LINEENDING)
+ {
+ *ctx->token = 0; //read nothing, and don't progress the read either.
+ return false;
+ }
+ else
+ {
+ ctx->str = e;
+ return true;
+ }
+}
+static void Com_ParseIgnoreLineBreak(struct cam_parsestate_s *ctx)
+{
+ com_tokentype_t toktype;
+ do
+ {
+ ctx->str = cmdfuncs->ParsePunctuation(ctx->str, NULL, ctx->token, sizeof(ctx->token), &toktype);
+ } while (toktype == TTP_LINEENDING);
+}
+static qboolean Cam_ParseSplineCoords(struct spline_s *spline, struct cam_parsestate_s *ctx)
+{
+ size_t c;
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!Q_strcasecmp(ctx->token, "{"))
+ {
+ for(;;)
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!ctx->str)
+ return false; //failure
+ if (!Q_strcasecmp(ctx->token, "}"))
+ break;
+ if (!Q_strcasecmp(ctx->token, "granularity"))
+ {
+ Com_ParseNoLineBreak(ctx);
+// spline->time = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "name"))
+ {
+ Com_ParseNoLineBreak(ctx);
+// spline->time = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "("))
+ {
+ vec3_t xyz;
+ Com_ParseIgnoreLineBreak(ctx);
+ xyz[0] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ xyz[1] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ xyz[2] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ if (Q_strcasecmp(ctx->token, ")"))
+ return false;
+
+ c = spline->numcp;
+ Z_ReallocElements((void**)&spline->cp, &spline->numcp, spline->numcp+1, sizeof(*spline->cp));
+ VectorCopy(xyz, spline->cp[c].pos);
+ }
+ else
+ {
+ Con_Printf("Cam_ParseSplineCoords: unknown property %s\n", ctx->token);
+ while (Com_ParseNoLineBreak(ctx))
+ ;
+ }
+ }
+ }
+ return true;
+}
+static qboolean Cam_ParseSpline(struct spline_s *spline, struct cam_parsestate_s *ctx, int parsetype)
+{
+ size_t c;
+ spline->time = 0;
+ spline->speed = 0;
+ *spline->name = 0;
+ spline->type = parsetype;
+ switch(parsetype)
+ {
+ case 0: //fixed, so only one point
+ Z_ReallocElements((void**)&spline->cp, &spline->numcp, 1, sizeof(*spline->cp));
+ VectorClear(spline->cp[0].pos);
+ break;
+ case 1: //interpolated, two points
+ Z_ReallocElements((void**)&spline->cp, &spline->numcp, 2, sizeof(*spline->cp));
+ VectorClear(spline->cp[0].pos);
+ VectorClear(spline->cp[1].pos);
+ break;
+ case 2: //spline, don't know needed point count yet, but lets make sure cached state doesn't break anything.
+ spline->numcp = 0;
+ break;
+ default:
+ return false;
+ }
+
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!Q_strcasecmp(ctx->token, "{"))
+ {
+ for(;;)
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!ctx->str)
+ return false; //failure
+ if (!Q_strcasecmp(ctx->token, "}"))
+ break;
+ if (!Q_strcasecmp(ctx->token, "time"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ spline->time = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "type"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ if (parsetype != atoi(ctx->token))
+ return false; //*sigh*
+ }
+ else if (!Q_strcasecmp(ctx->token, "name"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ Q_strncpyz(spline->name, ctx->token, sizeof(spline->name));
+ }
+ else if (!Q_strcasecmp(ctx->token, "baseVelocity"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ spline->speed = atof(ctx->token);
+ }
+ else if (!parsetype && !Q_strcasecmp(ctx->token, "pos"))
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (Q_strcasecmp(ctx->token, "("))
+ return false;
+
+ c = 0;
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[0] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[1] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[2] = atof(ctx->token);
+
+ Com_ParseIgnoreLineBreak(ctx);
+ if (Q_strcasecmp(ctx->token, ")"))
+ return false;
+ }
+ else if (parsetype == 1 && (!Q_strcasecmp(ctx->token, "startPos") || !Q_strcasecmp(ctx->token, "endPos")))
+ {
+ if (!Q_strcasecmp(ctx->token, "startPos"))
+ c = 0;
+ else if (!Q_strcasecmp(ctx->token, "endPos"))
+ c = 1;
+ else
+ return false;
+ Com_ParseIgnoreLineBreak(ctx);
+ if (Q_strcasecmp(ctx->token, "("))
+ return false;
+
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[0] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[1] = atof(ctx->token);
+ Com_ParseIgnoreLineBreak(ctx);
+ spline->cp[c].pos[2] = atof(ctx->token);
+
+ Com_ParseIgnoreLineBreak(ctx);
+ if (Q_strcasecmp(ctx->token, ")"))
+ return false;
+ }
+ else if (parsetype == 2 && !Q_strcasecmp(ctx->token, "target"))
+ {
+ if (!Cam_ParseSplineCoords(spline, ctx))
+ return false;
+ }
+ else
+ {
+ Con_Printf("Cam_ParseSpline: unknown property %s\n", ctx->token);
+ while (Com_ParseNoLineBreak(ctx))
+ ;
+ }
+ }
+ }
+ return true;
+}
+static qboolean Cam_ParseTargetSpline(camdata_t *cam, struct cam_parsestate_s *ctx, int parsetype)
+{
+ struct spline_s *spline;
+ int i = cam->numtargets;
+ Z_ReallocElements((void**)&cam->targets, &cam->numtargets, cam->numtargets+1, sizeof(*cam->targets));
+ spline = &cam->targets[i];
+ if (!cam->curtarget)
+ cam->curtarget = spline;
+ return Cam_ParseSpline(spline, ctx, parsetype);
+}
+static qboolean Cam_ParseEvent(camdata_t *cam, struct cam_parsestate_s *ctx)
+{
+ struct camevent_s *ev;
+ int i = cam->numevents;
+ Z_ReallocElements((void**)&cam->events, &cam->numevents, cam->numevents+1, sizeof(*cam->events));
+ ev = &cam->events[i];
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!Q_strcasecmp(ctx->token, "{"))
+ {
+ for(;;)
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!ctx->str)
+ return false; //failure
+ if (!Q_strcasecmp(ctx->token, "}"))
+ break;
+ if (!Q_strcasecmp(ctx->token, "time"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ ev->time = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "type"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ ev->type = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "name"))
+ {
+ Com_ParseNoLineBreak(ctx);
+// Q_strncpyz(ev->name, ctx->token, sizeof(ev->name));
+ }
+ else if (!Q_strcasecmp(ctx->token, "param"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ ev->param = Z_StrDup(ctx->token);
+ }
+ else
+ {
+ Con_Printf("Cam_ParseEvent: unknown property %s\n", ctx->token);
+ while (Com_ParseNoLineBreak(ctx))
+ ;
+ }
+ }
+ }
+ return true;
+}
+static qboolean Cam_ParseFov(camdata_t *cam, struct cam_parsestate_s *ctx)
+{
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!Q_strcasecmp(ctx->token, "{"))
+ {
+ for(;;)
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!ctx->str)
+ return false; //failure
+ if (!Q_strcasecmp(ctx->token, "}"))
+ break;
+ if (!Q_strcasecmp(ctx->token, "time"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ cam->fov.time = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "fov"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ cam->fov.fov = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "startFOV"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ cam->fov.startfov = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "endFOV"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ cam->fov.endfov = atof(ctx->token);
+ }
+ else
+ {
+ Con_Printf("Cam_ParseFov: unknown property %s\n", ctx->token);
+ while (Com_ParseNoLineBreak(ctx))
+ ;
+ }
+ }
+ }
+ return true;
+}
+static qboolean Cam_Parse(camdata_t *cam, struct cam_parsestate_s *ctx)
+{
+ qboolean ret = false;
+
+ Cam_Free(cam);
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!Q_strcasecmp(ctx->token, "{"))
+ {
+ for(ret = true; ret;)
+ {
+ Com_ParseIgnoreLineBreak(ctx);
+ if (!ctx->str)
+ return false; //failure
+ if (!Q_strcasecmp(ctx->token, "}"))
+ break;
+ if (!Q_strcasecmp(ctx->token, "time"))
+ {
+ Com_ParseNoLineBreak(ctx);
+ cam->basetime = atof(ctx->token);
+ }
+ else if (!Q_strcasecmp(ctx->token, "camera_Fixed"))
+ ret &= Cam_ParseSpline(&cam->camera, ctx, 0);
+ else if (!Q_strcasecmp(ctx->token, "camera_Interpolated"))
+ ret &= Cam_ParseSpline(&cam->camera, ctx, 1);
+ else if (!Q_strcasecmp(ctx->token, "camera_Spline"))
+ ret &= Cam_ParseSpline(&cam->camera, ctx, 2);
+ else if (!Q_strcasecmp(ctx->token, "target_Fixed"))
+ ret &= Cam_ParseTargetSpline(cam, ctx, 0);
+ else if (!Q_strcasecmp(ctx->token, "target_Interpolated"))
+ ret &= Cam_ParseTargetSpline(cam, ctx, 1);
+ else if (!Q_strcasecmp(ctx->token, "target_Spline"))
+ ret &= Cam_ParseTargetSpline(cam, ctx, 2);
+ else if (!Q_strcasecmp(ctx->token, "event"))
+ ret &= Cam_ParseEvent(cam, ctx);
+ else if (!Q_strcasecmp(ctx->token, "fov"))
+ ret &= Cam_ParseFov(cam, ctx);
+ else
+ {
+ Con_Printf("Cam_Parse: unknown property %s\n", ctx->token);
+ while (Com_ParseNoLineBreak(ctx))
+ ;
+ }
+ }
+ }
+ return ret;
+}
+static qboolean Cam_Load(camdata_t *cam, const char *filename)
+{
+ qboolean ret = false;
+ vfsfile_t *f;
+ f = fsfuncs->OpenVFS(filename, "rb", FS_GAME);
+ if (f)
+ {
+ char buffer[65536];
+ struct cam_parsestate_s ctx = {buffer};
+ int s;
+ s = VFS_READ(f, buffer, sizeof(buffer)-1);
+ if (s<0)
+ s = 0;
+ buffer[s] = 0;
+
+ ctx.str = cmdfuncs->ParseToken(buffer, ctx.token, sizeof(ctx.token), NULL);
+ if (!Q_strcasecmp(ctx.token, "cameraPathDef"))
+ ret = !!Cam_Parse(cam, &ctx);
+
+ VFS_CLOSE(f);
+ }
+
+ cam->totaltime = cam->basetime;
+ return ret;
+}
+static void Cam_Start(camdata_t *cam, double time)
+{
+ cam->started = true;
+ cam->starttime = time;
+ cam->targetbias = 0;
+ cam->curtarget = cam->numtargets?&cam->targets[0]:NULL;
+}
+#ifdef RTCWSP
+static void Cam_Stop(camdata_t *cam)
+{
+ cam->started = false;
+}
+#endif
+static void Cam_CalcSpline(const struct spline_s *spline, double time, float *out)
+{
+ float frac;
+ vec3_t move;
+ int i;
+
+ if (!spline->numcp)
+ VectorClear(out);
+ else
+ {
+ switch(spline->type)
+ {
+ case 0:
+ VectorCopy(spline->cp[0].pos, out);
+ break;
+ case 1:
+ VectorSubtract(spline->cp[1].pos, spline->cp[0].pos, move);
+ frac = (time) * spline->speed / sqrt(DotProduct(move,move));
+ frac = bound(0, frac, 1);
+
+ VectorInterpolate(spline->cp[0].pos, frac, spline->cp[1].pos, out);
+ break;
+ case 2:
+ for (i = 0; i < spline->numcp-1; i++)
+ {
+ VectorSubtract(spline->cp[i+1].pos, spline->cp[i+0].pos, move);
+ frac = (time) * spline->speed / sqrt(DotProduct(move,move));
+ if (frac > 1)
+ {
+ time -= 1/(spline->speed / sqrt(DotProduct(move,move)));
+ continue;
+ }
+ break;
+ }
+ frac = bound(0, frac, 1);
+
+ VectorInterpolate(spline->cp[i+0].pos, frac, spline->cp[i+1].pos, out);
+ break;
+ }
+ }
+}
+static float Cam_CalcFov(camdata_t *cam, double time)
+{
+ if (cam->fov.time)
+ { //interpolating fov
+ float frac = time / cam->fov.time;
+ float ret;
+ frac = bound(0, frac, 1);
+ FloatInterpolate(cam->fov.startfov, frac, cam->fov.endfov, ret);
+ return ret;
+ }
+ return cam->fov.fov;
+}
+static qboolean Cam_Calculate(camdata_t *cam, double time, vec3_t pos, vec3_t angle, float *fov)
+{
+ size_t i, j;
+ vec3_t targ;
+ time -= cam->starttime;
+ if (time > cam->totaltime)
+ return false;
+
+ //process any new events
+ for (i = 0; i < cam->numevents; i++)
+ {
+ struct camevent_s *ev = &cam->events[i];
+ if (ev->fired)
+ continue;
+ if (time >= ev->time)
+ {
+ ev->fired = true;
+ switch(ev->type)
+ {
+ case CE_TARGET:
+ for (j = 0; j < cam->numtargets; j++)
+ {
+ if (!Q_strcasecmp(ev->param, cam->targets[j].name))
+ {
+ cam->curtarget = &cam->targets[j];
+ cam->targetbias = ev->time; //new target starts now.
+ break;
+ }
+ }
+ if (j == cam->numtargets)
+ Con_DPrintf("Cam_Calculate: target \"%s\" not known\n", ev->param);
+ break;
+ case CE_FADEIN:
+ cmdfuncs->AddText(va("fade 0 0 0 0 %f\n", atof(ev->param)), true);
+ break;
+ case CE_FADEOUT:
+ cmdfuncs->AddText(va("fade 0 0 0 255 %f\n", atof(ev->param)), true);
+ break;
+ default:
+ Con_Printf("Cam_Calculate: event type %i not supported\n", ev->type);
+ break;
+ }
+ }
+ }
+
+ Cam_CalcSpline(&cam->camera, time, pos);
+ if (cam->curtarget)
+ Cam_CalcSpline(cam->curtarget, time-cam->targetbias, targ);
+ else
+ VectorCopy(pos, targ); //really some sort of error
+
+ *fov = Cam_CalcFov(cam, time);
+
+ //the gamecode wants angles, not coords.
+ VectorSubtract(targ, pos, targ);
+ VectorAngles(targ, NULL, angle, true); //and with screwed pitch.
+ return true;
+}
+
+static camdata_t cams[64];
+qboolean CG_Cam_IsActive(void)
+{
+ return cams[0].started;
+}
+
+//this stuff is crap
+static void CG_R_SetFog(int cmd, int arg1, int arg2, float r, float g, float b, float density)
+{
+ static fogstate_t fogtypes[10];
+ if (cmd == 10/*SWITCHFOG*/)
+ {
+ if (arg1 >= countof(fogtypes))
+ return; //invalid index...
+// CL_ResetFog(0);
+// cl.fog[0] = fogtypes[arg1];
+// cl.fog[0].time = realtime + (arg2/1000.);
+ return;
+ }
+ else if (cmd == 2/*PORTALVIEW*/)
+ ; //skybox fog
+ else if (cmd == 6/*SERVER*/)
+ ; //server-controlled global fog
+ else
+ {
+ Con_Printf("CG_R_SetFog: unsupported fog cmd %i\n", cmd);
+ return;
+ }
+
+ if (density >= 1)
+ density = 0; //FIXME: linear fog not supported
+ fogtypes[cmd].density = density;
+ VectorSet(fogtypes[cmd].colour, r, g, b);
+ fogtypes[cmd].alpha = 1;
+}
+
+//this 'limbo' stuff is used for persistent chat in the class selection screen, sent from cgame to ui
+static char *limbostrings[7];
+qboolean CG_GetLimboString(int index, char *outbuf)
+{
+ if (index >= countof(limbostrings))
+ {
+ *outbuf = 0;
+ return false;
+ }
+ else if (limbostrings[index])
+ Q_strncpyz(outbuf, limbostrings[index], 140);
+ else
+ *outbuf = 0;
+ return true;
+}
+static void CG_AddLimboString(const char *line)
+{
+ size_t i;
+ Z_Free(limbostrings[countof(limbostrings)-1]);
+ for (i = countof(limbostrings)-1; i > 0; i--)
+ limbostrings[i] = limbostrings[i-1];
+ limbostrings[0] = Z_StrDup(line);
+}
+#endif
+#ifdef RTCWMP
+static void CG_ImageRotated(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t pic, float angle)
+{ //everyone does rotation differently...
+ vec2_t pivot, saxis, taxis, points[4], tcoords[4];
+ w /= 2;
+ h /= 2;
+ pivot[0] = x+w;
+ pivot[1] = y+h;
+
+ angle *= M_PI*2; //input angle is 0-1
+ saxis[0] = cos(angle);
+ saxis[1] = sin(angle);
+ taxis[0] = -sin(angle);
+ taxis[1] = cos(angle);
+
+ //this line sucks. simply rotating the image is lame when q3 stuff so blatently ignores aspect
+ //this is typically used for the compass, so it actually being round is better than an oval rotating, even if its frame remains an oval
+ w = h = sqrt((w*w+h*h)/2);
+
+ Vector2MA(pivot, -w, saxis, points[0]); Vector2MA(points[0], -h, taxis, points[0]);
+ Vector2MA(pivot, +w, saxis, points[1]); Vector2MA(points[1], -h, taxis, points[1]);
+ Vector2MA(pivot, +w, saxis, points[2]); Vector2MA(points[2], +h, taxis, points[2]);
+ Vector2MA(pivot, -w, saxis, points[3]); Vector2MA(points[3], +h, taxis, points[3]);
+
+ Vector2Set(tcoords[0], s1, s1);
+ Vector2Set(tcoords[1], s2, s1);
+ Vector2Set(tcoords[2], s2, s2);
+ Vector2Set(tcoords[3], s1, s2);
+
+ drawfuncs->Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, pic);
+}
+#endif
+int VM_LerpTagRTCW(float *out, q3refEntity_t *ent, char *tagname, int startindex);
int VM_LerpTag(float *out, model_t *model, int f1, int f2, float l2, char *tagname);
#define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) plugfuncs->EndGame("Call to cgame trap %u passes invalid pointer\n", (unsigned int)fn); //out of bounds.
@@ -602,6 +1349,11 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case CG_PRINT:
{
const char *text = VM_POINTER(arg[0]);
+#ifdef RTCWMP
+ if (!strncmp(text, "[skipnotify]", 12))
+ confuncs->SubPrint(NULL, text+12, PFS_NONOTIFY, 0);
+ else
+#endif
Con_Printf("%s", text);
}
break;
@@ -679,8 +1431,10 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case CG_FS_WRITE: //fwrite
Con_DPrintf("CG_FS_WRITE: not implemented\n");
break;
+#ifndef RTCW
case CG_FS_SEEK:
return VM_FSeek(arg[0], arg[1], arg[2], 1);
+#endif
case CG_FS_FCLOSEFILE: //fclose
VM_fclose(VM_LONG(arg[0]), 1);
break;
@@ -873,6 +1627,9 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
if (ccs.worldmodel->loadstate != MLS_LOADED)
plugfuncs->EndGame("Couldn't load map %s", mapname);
+#ifdef RTCW
+ mapentspointer = worldfuncs->GetEntitiesString(ccs.worldmodel);
+#endif
for (i=1 ; i<=ccs.worldmodel->numsubmodels && i < countof(ccs.model_precache); i++)
ccs.model_precache[i] = worldfuncs->LoadModel(worldfuncs->FixName(va("*%i", i), mapname), MLV_SILENTSYNC);
@@ -957,7 +1714,9 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case CG_R_ADDREFENTITYTOSCENE: //add ent to scene
VQ3_AddEntity(VM_POINTER(arg[0]));
break;
+#ifndef RTCW
case CG_R_ADDADDITIVELIGHTTOSCENE:
+#endif
case CG_R_ADDLIGHTTOSCENE: //add light to scene.
{
dlight_t *dl = scenefuncs->AllocDlightOrg(-1, VM_POINTER(arg[0]));
@@ -987,7 +1746,11 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case CG_R_LERPTAG: //Lerp tag...
VALIDATEPOINTER(arg[0], sizeof(float)*12);
+#ifdef RTCW
+ VM_LONG(ret) = VM_LerpTagRTCW(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));
+#else
VM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), scenefuncs->ModelFromId(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5]));
+#endif
break;
case CG_S_REGISTERSOUND:
@@ -1011,13 +1774,20 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
break;
case CG_S_ADDLOOPINGSOUND:
+#ifdef RTCW
+ //entnum, origin, velocity, range, sfx, volume
+ CG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), arg[3], VM_FROMSTRCACHE(arg[4]), VM_LONG(arg[5])/255, false);
+#else
//entnum, origin, velocity, sfx
CG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), -1, VM_FROMSTRCACHE(arg[3]), 1, false);
+#endif
break;
+#ifndef RTCWSP
case CG_S_ADDREALLOOPINGSOUND:
//entnum, origin, velocity, sfx
CG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), -1, VM_FROMSTRCACHE(arg[3]), 1, true);
break;
+#endif
case CG_S_STOPLOOPINGSOUND:
//entnum
CG_StopLoopingSounds(VM_LONG(arg[0])+1);
@@ -1086,6 +1856,12 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
cfg->depthBits = 24;
cfg->stencilBits = 8;//sh_config.stencilbits;
cfg->textureCompression = true;//!!sh_config.hw_bc;
+#ifdef RTCW
+ //cfg->cananisrophic = gl_config.ext_texture_filter_anisotropic!=0;
+ //cfg->maxanistrophy = gl_config.ext_texture_filter_anisotropic;
+ //cfg->maxtexturesize = sh_config.texture2d_maxsize;
+ //cfg->numtextureunits = be_maxpasses;
+#endif
cfg->textureEnvAddAvailable = true;//sh_config.env_add;
//these are the only three that really matter.
@@ -1124,8 +1900,20 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
VM_LONG(ret) = CGQ3_GetUserCmd(VM_LONG(arg[0]), VM_POINTER(arg[1]));
break;
case CG_SETUSERCMDVALUE: //weaponselect, zoomsensitivity.
+#ifdef RTCW
+ ccs.selected_weapon = VM_LONG(arg[0]);
+ ccs.sendholdable = VM_LONG(arg[1]);
+ inputfuncs->SetSensitivityScale(VM_FLOAT(arg[2]));
+ #ifdef RTCWMP
+ ccs.sendcld = VM_LONG(arg[3])&0xff; //mpsetup
+ ccs.sendcld |= (VM_LONG(arg[4])&0xff)<<8; //identclient
+ #else
+ ccs.sendcld = VM_LONG(arg[3]);
+ #endif
+#else
ccs.selected_weapon = VM_LONG(arg[0]);
inputfuncs->SetSensitivityScale(VM_FLOAT(arg[1]));
+#endif
break;
case CG_GETSERVERCOMMAND:
@@ -1230,6 +2018,204 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
mapentspointer = cmdfuncs->ParseToken(mapentspointer, VM_POINTER(arg[0]), arg[1], NULL);
return !!mapentspointer;
+#ifdef RTCW
+ case CG_R_GETSKINMODEL:
+ VALIDATEPOINTER(arg[1], 64);
+ ret = CG_SkinGetModel(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), 64);
+ break;
+ case CG_R_GETMODELSHADER:
+ (void)arg[2];
+ ret = scenefuncs->ShaderForSkin(VM_LONG(arg[0]), VM_LONG(arg[1]), 0, 0);
+ break;
+
+ case CG_S_STARTSOUNDEX: Con_DPrintf("RTCWCG: Unsupported system trap: %s\n", "CG_S_STARTSOUNDEX"); break;
+ case CG_S_GETVOICEAMPLITUDE:
+ {
+ //query mixer for sound file levels, for cutscenes.
+ float level = audiofuncs->GetChannelLevel(VM_LONG(arg[0])+1, 3/*RTCW_CHAN_VOICE*/);
+#ifdef VOICECHAT
+ //query voip levels, for players.
+ float voip = audiofuncs->Voip_ClientLoudness(VM_LONG(arg[0]));
+ level = max(0,level) + max(0, voip);
+#else
+ level = max(0,level);
+#endif
+ //our GetChannelLevel doesn't quite match, so I'm just fudging these too
+ level = pow(cvarfuncs->GetNVFDG("cg_VoiceRawScalep","1",0,NULL,"RTCW Compat")->value*level, cvarfuncs->GetNVFDG("cg_VoiceRawExp","0.8",0,NULL,"RTCW Compat")->value);
+ ret = cvarfuncs->GetNVFDG("cg_VoiceScale","1024",0,NULL,"RTCW Compat")->value*level; //greater range will be moduloed away, which keeps the mouth moving more than it otherwise would.
+ if (ret < cvarfuncs->GetNVFDG("cg_VoiceMin","25",0,NULL,"RTCW Compat")->value) //this is used for mouths. don't open them if there's just a small amount of noise there.
+ ret = 0;
+ }
+ break;
+
+#ifdef RTCWSP
+ case CG_S_FADEALLSOUNDS:
+ audiofuncs->FadeSounds(VM_FLOAT(arg[0]), VM_LONG(arg[1])/1000.0);
+ break;
+ case CG_S_FADESTREAMINGSOUND: Con_DPrintf("RTCWCG: Unsupported system trap: %s (ent=%i, time=%f, ssNum=%i)\n", "CG_S_FADESTREAMINGSOUND", VM_LONG(arg[0])+1, VM_LONG(arg[1])/1000.0, VM_LONG(arg[2])); break;
+ case CG_S_STOPSTREAMINGSOUND: Con_DPrintf("RTCWCG: Unsupported system trap: %s (ent=%i)\n", "CG_S_STOPSTREAMINGSOUND", VM_LONG(arg[0])+1); break;
+ case CG_RB_ZOMBIEFXADDNEWHIT: Con_Printf("RTCWCG: Unsupported system trap: %s\n", "CG_RB_ZOMBIEFXADDNEWHIT"); break;
+#endif
+#ifdef RTCWMP
+ case CG_R_DRAWROTATEDPIC:
+ CG_ImageRotated(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_LONG(arg[8]), VM_FLOAT(arg[9]));
+ break;
+ case CG_SETCLIENTLERPORIGIN: break; //simply tells the client where the player entity is, for clientside anti-wallhack.
+ case CG_TRANSLATE_STRING: Q_strncpyz(VM_POINTER(arg[1]), VM_POINTER(arg[0]), 256); break;
+#endif
+ case CG_S_STARTSTREAMINGSOUND: Con_Printf("RTCWCG: Unsupported system trap: %s (initial=%s,loop=%s,ent=%i,chan=%i,atten=%f)\n", "CG_S_STARTSTREAMINGSOUND", (char*)VM_POINTER(arg[0]), (char*)VM_POINTER(arg[1]), VM_LONG(arg[2])+1,VM_LONG(arg[3]),VM_FLOAT(arg[4])); break;
+
+ case CG_R_ADDCORONATOSCENE:
+ {
+ float *org = VM_POINTER(arg[0]);
+ float r = VM_FLOAT(arg[1]);
+ float g = VM_FLOAT(arg[2]);
+ float b = VM_FLOAT(arg[3]);
+ float scale = VM_FLOAT(arg[4]);
+// int id = VM_LONG(arg[5]);
+// int flags = VM_LONG(arg[6]); /*&1:visible,&2:spotlight*/
+ dlight_t *dl = scenefuncs->AllocDlightOrg(-2, org);
+ dl->radius = 200;
+ dl->die = ccs.time+0.1;
+ VectorSet(dl->color, r, g, b);
+ VectorSet(dl->lightcolourscales, 0, 0, 0); //no ambient/diffuse/specular. not a real light.
+ dl->flags = LFLAG_NORMALMODE|LFLAG_REALTIMEMODE;
+ dl->corona = scale*0.25;
+// dl->coronascale = scale;
+ }
+ break;
+ case CG_R_SETFOG:
+ CG_R_SetFog(arg[0], arg[1], arg[2], VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]));
+ break;
+ case CG_R_DRAWSTRETCHPIC_GRADIENT: Con_Printf("RTCWCG: Unsupported system trap: %s\n", "CG_R_DRAWSTRETCHPIC_GRADIENT");break;
+ case CG_SENDMOVESPEEDSTOGAME:
+#ifdef Q3SERVER
+ {
+ extern vm_t *q3gamevm;
+ if (q3gamevm && vmfuncs->NonNative(q3gamevm))
+ return false;
+ else if (q3gamevm)
+ return vmfuncs->Call(q3gamevm, 13/*GAME_RETRIEVE_MOVESPEEDS_FROM_CLIENT*/, VM_LONG(arg[0]), VM_POINTER(arg[1]));
+ }
+#endif
+ break;
+
+ case CG_LOADCAMERA:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= countof(cams))
+ return false; //shoo!
+ return Cam_Load(&cams[VM_LONG(arg[0])], VM_POINTER(arg[1]));
+ case CG_STARTCAMERA:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= countof(cams))
+ return false; //shoo!
+ Cam_Start(&cams[VM_LONG(arg[0])], VM_LONG(arg[1])/1000.0);
+ break;
+#ifdef RTCWSP
+ case CG_STOPCAMERA:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= countof(cams))
+ return false; //shoo!
+ Cam_Stop(&cams[VM_LONG(arg[0])]);
+ break;
+#endif
+ case CG_GETCAMERAINFO:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= countof(cams))
+ return false; //shoo!
+ VALIDATEPOINTER(arg[2], sizeof(vec3_t));
+ VALIDATEPOINTER(arg[3], sizeof(vec3_t));
+ VALIDATEPOINTER(arg[4], sizeof(float));
+ return Cam_Calculate(&cams[VM_LONG(arg[0])], VM_LONG(arg[1])/1000.0, VM_POINTER(arg[2]), VM_POINTER(arg[3]), VM_POINTER(arg[4]));
+
+ case CG_LIMBOCHAT: CG_AddLimboString(VM_POINTER(arg[0])); break;
+#ifdef RTCWSP
+ case CG_GETMODELINFO:
+#ifdef Q3SERVER
+ {
+ extern vm_t *q3gamevm;
+ if (q3gamevm && vmfuncs->NonNative(q3gamevm))
+ return false;
+ else if (q3gamevm)
+ return vmfuncs->Call(q3gamevm, 14/*GAME_GETMODELINFO*/, VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));
+ }
+#endif
+ return 0;//returning 0 here 'should' be okay, as that will happen with QVMs, demos, and multiplayer. should invoke the game.dll's vmMain with GAME_GETMODELINFO
+ case CG_ALLOC: Con_Printf("RTCWCG: Bad system trap: %s\n", "CG_ALLOC"); break;
+#endif
+
+#ifdef RTCWMP
+ case CG_KEY_KEYNUMTOSTRINGBUF:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
+ break; //out of bounds.
+
+ Q_strncpyz(VM_POINTER(arg[1]), inputfuncs->GetKeyName(VM_LONG(arg[0]), 0), VM_LONG(arg[2]));
+ break;
+
+ case CG_KEY_GETBINDINGBUF:
+ if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
+ break; //out of bounds.
+
+ {
+ const char *binding = inputfuncs->GetKeyBind(0, VM_LONG(arg[0]), 0);
+ if (binding)
+ Q_strncpyz(VM_POINTER(arg[1]), binding, VM_LONG(arg[2]));
+ else
+ *(char *)VM_POINTER(arg[1]) = '\0';
+ }
+ break;
+
+ case CG_KEY_SETBINDING:
+ inputfuncs->SetKeyBind(0, VM_LONG(arg[0]), ~0, VM_POINTER(arg[1]));
+ break;
+#endif
+
+ case CG_INGAME_POPUP:
+ {
+ const char *name = VM_POINTER(arg[0]);
+ vm_t *uivm = UI_GetUIVM();
+ int i, cmd = 10/*CLIPBOARD, the default value if none match*/;
+ static struct { const char *popupname; int popval;} popups[] =
+ {
+#ifdef RTCWMP
+ {"briefing#", -1},
+ {"hbook1", 10},
+ {"hbook2", 11},
+ {"hbook3", 12},
+ {"UIMENU_WM_PICKTEAM", 13},
+ {"UIMENU_WM_PICKPLAYER", 14},
+ {"UIMENU_WM_QUICKMESSAGE", 15},
+ {"UIMENU_WM_QUICKMESSAGEALT", 16},
+ {"UIMENU_WM_LIMBO", 17},
+ {"UIMENU_WM_AUTOUPDATE", 18},
+#else
+ {"briefing", 19},
+ {"hbook1", 12},
+ {"hbook2", 13},
+ {"hbook3", 14},
+ {"pregame", 7},
+ {"UIMENU_WM_PICKTEAM", 15},
+ {"UIMENU_WM_PICKPLAYER", 16},
+ {"UIMENU_WM_QUICKMESSAGE", 17},
+ {"UIMENU_WM_LIMBO", 18},
+#endif
+ };
+ if (name)
+ for (i = 0; i < countof(popups); i++)
+ if (!Q_strcasecmp(name, popups[i].popupname))
+ {
+ cmd = popups[i].popval;
+ break;
+ }
+ //only briefing may actually work before we're active.
+ if (cmd == popups[0].popval || ccs.state == ca_active)
+ vmfuncs->Call( uivm, UI_SET_ACTIVE_MENU, cmd);
+ }
+ return 0;
+ case CG_INGAME_CLOSEPOPUP:
+ {
+ vm_t *uivm = UI_GetUIVM();
+ vmfuncs->Call(uivm, UI_KEY_EVENT, K_ESCAPE, qtrue);
+ vmfuncs->Call(uivm, UI_KEY_EVENT, K_ESCAPE, qfalse);
+ }
+ return 0;
+#endif
case CG_CIN_PLAYCINEMATIC:
return UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5]));
@@ -1256,8 +2242,12 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case CG_REMOVECOMMAND:
case CG_TESTPRINTINT:
case CG_TESTPRINTFLOAT:
- case CG_R_INPVS:
+#ifndef RTCWSP
case CG_R_LIGHTFORPOINT:
+#endif
+#ifndef RTCW
+ case CG_R_INPVS:
+#endif
// default:
Con_Printf("Q3CG: Bad system trap: %i\n", (int)fn);
}
@@ -1316,9 +2306,15 @@ int CG_Refresh(double time)
if (!cgvm)
return false;
+#ifdef RTCW
+ skyroom_forced = false;
+#endif
ccs.time = time;
vmfuncs->Call(cgvm, CG_DRAW_ACTIVE_FRAME, (int)(ccs.time*1000), 0, false);
drawfuncs->Colour4f(1, 1, 1, 1);
+#ifdef RTCW
+ skyroom_forced = false;
+#endif
return true;
}
@@ -1334,6 +2330,13 @@ void CG_Stop (void)
VM_fcloseall(1);
cgvm = NULL;
+#ifdef RTCW
+ {
+ int i;
+ for (i = 0; i < countof(cams); i++)
+ Cam_Free(&cams[i]);
+ }
+#endif
}
}
@@ -1354,10 +2357,50 @@ void CG_Start (void)
box_model = worldfuncs->TempBoxModel(vec3_origin, vec3_origin); //just in case.
clientfuncs->SetLoadingState(true);
+#ifdef RTCW
+ cvarfuncs->GetNVFDG("r_mapFogColor", "0", 0, "Required to avoid saved games breaking.", "RTCW compat");
+
+ #ifdef RTCWMP
+ cgvm = vmfuncs->Create("cgame.mp.", cvarfuncs->GetFloat("com_gamedirnativecode")?CG_SystemCallsNative:NULL, "vm/cgame.mp.", CG_SystemCallsVM);
+ #else
+ cgvm = vmfuncs->Create("cgame.sp.", cvarfuncs->GetFloat("com_gamedirnativecode")?CG_SystemCallsNative:NULL, "vm/cgame.sp.", CG_SystemCallsVM);
+ #endif
+#else
cgvm = vmfuncs->Create("cgame", cvarfuncs->GetFloat("com_gamedirnativecode")?CG_SystemCallsNative:NULL, "vm/cgame", CG_SystemCallsVM);
+#endif
clientfuncs->SetLoadingState(false);
if (cgvm)
{ //hu... cgame doesn't appear to have a query version call!
+#ifdef RTCW
+ {
+ static const char *buttonnames[] = {
+ /*0 attack*/NULL,
+ /*1 talking*/NULL,
+ /*2*/"useitem",
+ /*3*/"salute",
+ /*4walking*/NULL,
+ /*5*/"sprint",
+ /*6*/"activate",
+ /*7anykey*/NULL,
+ /*8*/"attack2",
+ /*9*/"zoom",
+ /*10*/"quickgren",
+ /*11*/"reload",
+ /*12*/"leanleft",
+ /*13*/"leanright",
+ /*14*/"wbutton6",
+ /*15*/"wbutton7",
+ /*16*/"kick"};
+ int i;
+ for (i = 0; i < countof(buttonnames); i++)
+ {
+ if (!buttonnames[i])
+ continue;
+ cmdfuncs->AddText(va("alias +%s +button%i\n", buttonnames[i],i), true);
+ cmdfuncs->AddText(va("alias -%s -button%i\n", buttonnames[i],i), true);
+ }
+ }
+#endif
vmfuncs->Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, ccs.playernum);
}
else
@@ -1386,6 +2429,14 @@ qboolean CG_ConsoleCommand(void)
qboolean CG_KeyPressed(int key, int unicode, int down)
{
+#ifdef RTCW
+ if (down && CG_Cam_IsActive())
+ {
+ if (key == K_ESCAPE || key == K_SPACE || key == K_ENTER)
+ cmdfuncs->AddText ("cmd cameraInterrupt\n", false);
+ return true;
+ }
+#endif
if (!cgvm || !(Q3_GetKeyCatcher()&8))
return false;
diff --git a/plugins/quake3/clq3_parse.c b/plugins/quake3/clq3_parse.c
index 7d8f70c93..a41e2cb64 100644
--- a/plugins/quake3/clq3_parse.c
+++ b/plugins/quake3/clq3_parse.c
@@ -507,7 +507,11 @@ qboolean CLQ3_SystemInfoChanged(const char *str)
if (!*value)
{
+#ifdef RTCW
+ value = "main";
+#else
value = "baseq3";
+#endif
}
rc = worldfuncs->GetInfoKey(str, "sv_referencedPaks"); //the ones that we should download.
@@ -829,8 +833,23 @@ void MSG_Q3_WriteDeltaUsercmd( sizebuf_t *msg, int key, const usercmd_t *from, c
MSG_WriteDeltaKey(msg, key, from->forwardmove, to->forwardmove, 8);
MSG_WriteDeltaKey(msg, key, from->sidemove, to->sidemove, 8);
MSG_WriteDeltaKey(msg, key, from->upmove, to->upmove, 8);
+#ifdef RTCW
+ MSG_WriteDeltaKey(msg, key, from->buttons, to->buttons, 8);
+ MSG_WriteDeltaKey(msg, key, from->buttons>>8, to->buttons>>8, 8); //wbuttons
+#else
MSG_WriteDeltaKey(msg, key, from->buttons, to->buttons, 16);
+#endif
MSG_WriteDeltaKey(msg, key, from->weapon, to->weapon, 8);
+#ifdef RTCW
+ MSG_WriteDeltaKey(msg, key, from->buttons>>24, to->buttons>>24, 8); //holdable
+ MSG_WriteDeltaKey(msg, key, from->buttons>>16, to->buttons>>16, 8); //wolfkick
+ #ifdef RTCWMP
+ MSG_WriteDeltaKey(msg, key, from->weapon>>16, to->weapon>>16, 8); //mpsetup
+ MSG_WriteDeltaKey(msg, key, from->weapon>>24, to->weapon>>24, 8); //ident
+ #else
+ MSG_WriteDeltaKey(msg, key, from->weapon>>16, to->weapon>>16, 16); //cld
+ #endif
+#endif
}
@@ -871,6 +890,10 @@ void CLQ3_SendCmd(struct ftenet_connections_s *socket, usercmd_t *cmd, unsigned
cmd->servertime = gametime*1000;
cmd->weapon = ccs.selected_weapon;
+#ifdef RTCW
+ cmd->buttons |= (ccs.sendholdable&0xff)<<24;
+ cmd->weapon |= (ccs.sendcld&0xffff)<<16;
+#endif
cmd->forwardmove *= 127/400.0f;
cmd->sidemove *= 127/400.0f;
diff --git a/plugins/quake3/clq3_ui.c b/plugins/quake3/clq3_ui.c
index 4920eab65..9294fad44 100644
--- a/plugins/quake3/clq3_ui.c
+++ b/plugins/quake3/clq3_ui.c
@@ -326,14 +326,29 @@ struct q3refEntity_s {
float shadowPlane; // projection shadows go here, stencils go slightly lower
vec3_t axis[3]; // rotation vectors
+#ifdef RTCW
+ vec3_t torsoaxis[3]; // gah wtf
+#endif
qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale
float origin[3]; // also used as MODEL_BEAM's "from"
int frame; // also used as MODEL_BEAM's diameter
+#ifdef RTCW
+ int torsoFrame;
+#ifdef RTCWSP
+ vec3_t scale;
+#endif
+#endif
// previous data for frame interpolation
float oldorigin[3]; // also used as MODEL_BEAM's "to"
int oldframe;
+#ifdef RTCW
+ int oldTorsoFrame;
+#endif
float backlerp; // 0.0 = current, 1.0 = old
+#ifdef RTCW
+ float torsoBacklerp;
+#endif
// texturing
int skinNum; // inline skin index
@@ -348,6 +363,14 @@ struct q3refEntity_s {
// extra sprite information
float radius;
float rotation;
+
+#ifdef RTCW
+ vec3_t fireRiseDir;
+ float fadeStartTime, fadeEndTime;
+ float hilightIntensity;
+ int reFlags;
+ int entityNum;
+#endif
};
struct q3polyvert_s
@@ -423,6 +446,16 @@ void VQ3_AddEntity(const q3refEntity_t *q3)
memcpy(ent.axis, q3->axis, sizeof(q3->axis));
ent.framestate.g[FS_REG].lerpweight[1] = q3->backlerp;
ent.framestate.g[FS_REG].lerpweight[0] = 1 - ent.framestate.g[FS_REG].lerpweight[1];
+
+#ifdef RTCW
+ //this is backwards, but oh well, its only relevant for mds and mds has the same torso/base swap (ignoring basebone).
+ ent.framestate.g[FST_BASE].frame[0] = q3->torsoFrame;
+ ent.framestate.g[FST_BASE].frame[1] = q3->oldTorsoFrame;
+ VectorAngles(q3->torsoaxis[0], q3->torsoaxis[2], ent.framestate.bonecontrols, false);
+ ent.framestate.g[FST_BASE].lerpweight[1] = q3->torsoBacklerp;
+ ent.framestate.g[FST_BASE].lerpweight[0] = 1 - ent.framestate.g[FST_BASE].lerpweight[1];
+#endif
+
if (q3->reType == RT_SPRITE)
{
ent.scale = q3->radius;
@@ -460,6 +493,15 @@ void VQ3_AddEntity(const q3refEntity_t *q3)
ent.bottomcolour = BOTTOM_DEFAULT;
ent.playerindex = -1;
+#ifdef RTCW
+ if (q3->hilightIntensity)
+ {
+#ifdef HEXEN2
+ ent.drawflags = MLS_ADDLIGHT;
+ ent.abslight = q3->hilightIntensity * 255;
+#endif
+ }
+#endif
if ((q3->renderfx & Q3RF_LIGHTING_ORIGIN) && ent.model)
{
@@ -501,6 +543,7 @@ void VQ3_AddPolys(shader_t *s, int numverts, q3polyvert_t *verts, size_t polycou
}
}
+#ifndef RTCW
int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagname)
{
int tagnum;
@@ -563,6 +606,90 @@ int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagnam
return false;
}
}
+#else
+int VM_LerpTagRTCW(float *out, q3refEntity_t *ent, char *tagname, int startindex)
+{ //returns
+ int tagnum;
+ float *ang;
+ float *org;
+
+ float tr[12];
+ qboolean found;
+ framestate_t fstate;
+ model_t *model = scenefuncs->ModelFromId(ent->hModel);
+
+ org = out;
+ ang = out+3;
+
+ memset(&fstate, 0, sizeof(fstate));
+ fstate.g[FS_REG].frame[0] = ent->frame;
+ fstate.g[FS_REG].frame[1] = ent->oldframe;
+ if (ent->frame == ent->oldframe)
+ { //explicit check, to avoid a plethora of valgrind warnings.
+ fstate.g[FS_REG].lerpweight[0] = 1;
+ }
+ else
+ {
+ fstate.g[FS_REG].lerpweight[0] = 1 - ent->backlerp;
+ fstate.g[FS_REG].lerpweight[1] = ent->backlerp;
+ }
+
+ fstate.g[FST_BASE].frame[0] = ent->torsoFrame;
+ fstate.g[FST_BASE].frame[1] = ent->oldTorsoFrame;
+ VectorAngles(ent->torsoaxis[0], ent->torsoaxis[2], fstate.bonecontrols, false);
+ if (ent->torsoFrame == ent->oldTorsoFrame)
+ fstate.g[FST_BASE].lerpweight[0] = 1;
+ else
+ {
+ fstate.g[FST_BASE].lerpweight[1] = ent->torsoBacklerp;
+ fstate.g[FST_BASE].lerpweight[0] = 1 - fstate.g[FST_BASE].lerpweight[1];
+ }
+
+
+ tagnum = scenefuncs->TagNumForName(model, tagname, startindex);
+ found = scenefuncs->GetTag(model, tagnum, &fstate, tr);
+
+ if (found && tagnum)
+ {
+ ang[0] = tr[0];
+ ang[1] = tr[1];
+ ang[2] = tr[2];
+ org[0] = tr[3];
+
+ ang[3] = tr[4];
+ ang[4] = tr[5];
+ ang[5] = tr[6];
+ org[1] = tr[7];
+
+ ang[6] = tr[8];
+ ang[7] = tr[9];
+ ang[8] = tr[10];
+ org[2] = tr[11];
+
+ return tagnum-1;
+ }
+ else
+ {
+ org[0] = 0;
+ org[1] = 0;
+ org[2] = 0;
+
+ ang[0] = 1;
+ ang[1] = 0;
+ ang[2] = 0;
+
+ ang[3] = 0;
+ ang[4] = 1;
+ ang[5] = 0;
+
+ ang[6] = 0;
+ ang[7] = 0;
+ ang[8] = 1;
+
+ return -1;
+ }
+}
+#endif
#define MAX_RENDER_STRINGS 8
#define MAX_RENDER_STRING_LENGTH 32
@@ -585,12 +712,29 @@ struct q3refdef_s {
char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH];
};
+#ifdef RTCW
+vec3_t skyroom_origin;
+qboolean skyroom_forced;
+#endif
void VQ3_RenderView(const q3refdef_t *ref)
{
plugrefdef_t scene;
scene.flags = 0;
+#ifdef RTCW
+ if (ref->rdflags & 8/*RDF_SKYBOXPORTAL*/)
+ { //evil hack. just take the position and make sense of it ourselves.
+ VectorCopy(ref->vieworg, skyroom_origin);
+ skyroom_forced = true;
+ return;
+ }
+ else if (skyroom_forced && !(ref->rdflags & 0x10/*RDF_DRAWSKYBOX*/))
+ {
+ VectorCopy(skyroom_origin, scene.skyroom_org);
+ scene.flags |= RDF_SKYROOMENABLED;
+ }
+#endif
scene.rect.x = ref->x;
scene.rect.y = ref->y;
@@ -963,8 +1107,10 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
break;
case UI_FS_WRITE: //fwrite
break;
+#ifndef RTCWMP
case UI_FS_SEEK:
return VM_FSeek(arg[0], arg[1], arg[2], 0);
+#endif
case UI_FS_FCLOSEFILE: //fclose
VM_fclose(VM_LONG(arg[0]), 0);
break;
@@ -1048,7 +1194,11 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
case UI_CM_LERPTAG: //Lerp tag...
VALIDATEPOINTER(arg[0], sizeof(float)*12);
+#ifdef RTCW
+ VM_LONG(ret) = VM_LerpTagRTCW(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));
+#else
VM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), scenefuncs->ModelFromId(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5]));
+#endif
break;
case UI_S_REGISTERSOUND:
@@ -1154,6 +1304,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
cfg->depthBits = 24;
cfg->stencilBits = 8;//sh_config.stencilbits;
cfg->textureCompression = true;//!!sh_config.texfmt[PTI_BC1_RGBA];
+/*#ifdef RTCW
+ cfg->cananisrophic = gl_config.ext_texture_filter_anisotropic!=0;
+ cfg->maxanistrophy = gl_config.ext_texture_filter_anisotropic;
+ cfg->maxtexturesize = sh_config.texture2d_maxsize;
+ cfg->numtextureunits = be_maxpasses;
+#endif*/
cfg->textureEnvAddAvailable = true;//sh_config.env_add;
//these are the only three that really matter.
@@ -1458,8 +1614,10 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
VM_LONG(ret) = true;
break;
+#ifndef RTCW
case UI_SET_PBCLSTATUS:
break;
+#endif
// standard Q3
case UI_MEMSET:
@@ -1606,12 +1764,37 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
return UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]));
+#ifdef RTCW
+ case UI_FS_DELETEFILE: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_FS_DELETEFILE"); return 0;
+ case UI_R_ADDPOLYSTOSCENE: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_R_ADDPOLYSTOSCENE"); return 0;
+ case UI_R_ADDCORONATOSCENE: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_R_ADDCORONATOSCENE"); return 0;
+ case UI_LAN_GETLOCALSERVERCOUNT: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_LAN_GETLOCALSERVERCOUNT"); return 0;
+ case UI_LAN_GETLOCALSERVERADDRESSSTRING: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_LAN_GETLOCALSERVERADDRESSSTRING"); return 0;
+ case UI_LAN_GETGLOBALSERVERCOUNT: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_LAN_GETGLOBALSERVERCOUNT"); return 0;
+ case UI_LAN_GETGLOBALSERVERADDRESSSTRING: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_LAN_GETGLOBALSERVERADDRESSSTRING"); return 0;
+ case UI_CL_GETLIMBOSTRING: return CG_GetLimboString(arg[0], VM_POINTER(arg[1]));
+#endif
+#ifdef RTCWMP
+ case UI_SET_PBCLSTATUS: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_SET_PBCLSTATUS"); return 0;
+ case UI_SET_PBSVSTATUS: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_SET_PBSVSTATUS"); return 0;
+ case UI_GET_AUTOUPDATE: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_GET_AUTOUPDATE"); return 0;
+ case UI_CL_TRANSLATE_STRING: Q_strncpyz(VM_POINTER(arg[1]), VM_POINTER(arg[0]), 256); break;
+ case UI_CHECKAUTOUPDATE: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_CHECKAUTOUPDATE"); return 0;
+ case UI_OPENURL: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_OPENURL"); return 0;
+#endif
+#ifdef RTCWSP
+ case UI_S_FADESTREAMINGSOUND: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_S_FADESTREAMINGSOUND"); return 0;
+ case UI_S_FADEALLSOUNDS: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_S_FADEALLSOUNDS"); return 0;
+#endif
+#ifdef RTCWSP
+ case UI_ALLOC: Con_Printf("RTCWUI: Not implemented system trap: %s\n", "UI_ALLOC"); return 0;
+#endif
#ifndef _DEBUG
default:
@@ -1838,7 +2021,15 @@ void UI_Start (void)
uimenu.lowpriority = true;
SV_InitBotLib();
+#ifdef RTCW
+ #ifdef RTCWMP
+ uivm = vmfuncs->Create("ui.mp.", cvarfuncs->GetFloat("com_gamedirnativecode")?UI_SystemCallsNative:NULL, "vm/ui.mp", UI_SystemCallsVM);
+ #else
+ uivm = vmfuncs->Create("ui.sp.", cvarfuncs->GetFloat("com_gamedirnativecode")?UI_SystemCallsNative:NULL, "vm/ui.sp", UI_SystemCallsVM);
+ #endif
+#else
uivm = vmfuncs->Create("ui", cvarfuncs->GetFloat("com_gamedirnativecode")?UI_SystemCallsNative:NULL, "vm/ui", UI_SystemCallsVM);
+#endif
if (uivm)
{
apiversion = vmfuncs->Call(uivm, UI_GETAPIVERSION, 6);
@@ -1909,6 +2100,16 @@ vm_t *UI_GetUIVM(void)
return uivm;
}
+#ifdef RTCW
+static void UI_StartMultiplayer_f(void)
+{
+ cmdfuncs->AddText("fs_changegame rtcwmp\n", true);
+}
+static void UI_StartSingleplayer_f(void)
+{
+ cmdfuncs->AddText("fs_changegame rtcwsp\n", true);
+}
+#endif
void UI_Init (void)
{
@@ -1917,6 +2118,12 @@ void UI_Init (void)
cl_nodelta_ptr = cvarfuncs->GetNVFDG("cl_nodelta", "", 0, NULL, "Q3 Compat");
cmdfuncs->AddCommand("ui_restart", UI_Restart_f, "Reload the Q3-based User Interface module");
+
+#ifdef RTCW
+ //we don't do multiple binaries...
+ cmdfuncs->AddCommand("startMultiplayer", UI_StartMultiplayer_f, "Stub function");
+ cmdfuncs->AddCommand("startSingleplayer", UI_StartSingleplayer_f, "Stub function");
+#endif
}
#endif
diff --git a/plugins/quake3/clq3defs.h b/plugins/quake3/clq3defs.h
index f3fc10480..29d0f1536 100644
--- a/plugins/quake3/clq3defs.h
+++ b/plugins/quake3/clq3defs.h
@@ -20,8 +20,13 @@ typedef struct {
#define MAX_Q3_STATS 16
#define MAX_Q3_PERSISTANT 16
#define MAX_Q3_POWERUPS 16
+#ifdef RTCW
+#define MAX_Q3_WEAPONS 64
+#define MAX_PS_EVENTS 4
+#else
#define MAX_Q3_WEAPONS 16
#define MAX_PS_EVENTS 2
+#endif
typedef struct q3playerState_s {
int commandTime; // cmd->serverTime of last executed command
int pm_type;
@@ -32,7 +37,14 @@ typedef struct q3playerState_s {
vec3_t origin;
vec3_t velocity;
int weaponTime;
+#ifdef RTCW
+ int weaponDelay;
+ int grenadeTimeLeft;
+#endif
int gravity;
+#ifdef RTCW
+ float leanf;
+#endif
int speed;
int delta_angles[3]; // add to command angles to get view direction
// changed by spawns, rotating objects, and teleporters
@@ -50,13 +62,18 @@ typedef struct q3playerState_s {
// when at rest, the value will remain unchanged
// used to twist the legs during strafing
+#ifndef RTCW
vec3_t grapplePoint; // location of grapple to pull towards if PMF_GRAPPLE_PULL
+#endif
int eFlags; // copied to entityState_t->eFlags
int eventSequence; // pmove generated events
int events[MAX_PS_EVENTS];
int eventParms[MAX_PS_EVENTS];
+#ifdef RTCW
+ int oldEventSequence;
+#endif
int externalEvent; // events set on player from another source
int externalEventParm;
@@ -65,6 +82,9 @@ typedef struct q3playerState_s {
int clientNum; // ranges from 0 to MAX_CLIENTS-1
int weapon; // copied to entityState_t->weapon
int weaponstate;
+#ifdef RTCW
+ int item;
+#endif
vec3_t viewangles; // for fixed views
int viewheight;
@@ -79,15 +99,65 @@ typedef struct q3playerState_s {
int persistant[MAX_Q3_PERSISTANT]; // stats that aren't cleared on death
int powerups[MAX_Q3_POWERUPS]; // level.time that the powerup runs out
int ammo[MAX_Q3_WEAPONS];
+#ifdef RTCW
+ int ammoclip[MAX_Q3_WEAPONS];
+ int holdable[16];
+ int holding;
+ int weapons[MAX_Q3_WEAPONS/32];
+ vec3_t mins, maxs;
+ float crouchMaxZ;
+ float crouchViewHeight, standViewHeight, deadViewHeight;
+ float runSpeedScale, sprintSpeedScale, crouchSpeedScale;
+ int viewlocked;
+ int viewlocked_entNum;
+ float friction;
+ int aiChar;
+ int teamNum;
+ int gunfx;
+ int onFireStart;
+ int serverCursorHint;
+ int serverCursorHintVal;
+ q3trace_t serverCursorHintTrace;
+#else
int generic1;
int loopSound;
int jumppad_ent; // jumppad entity hit this frame
+#endif
// not communicated over the net at all
int ping; // server to game info for scoreboard
int pmove_framecount; // FIXME: don't transmit over the network
+#ifndef RTCW
int jumppad_frame;
+#endif
int entityEventSequence;
+#ifdef RTCW
+ int sprintTime;
+ int sprintExertTime;
+ int classWeaponTime;
+ int jumpTime;
+ int weapAnimTimer;
+ int weapAnim;
+ qboolean releasedFire;
+ float aimSpreadScaleFloat;
+ int aimSpreadScale;
+ int lastFireTime;
+ int quickGrenTime;
+ int leanStopDebounceTime;
+ int weapHeat[MAX_Q3_WEAPONS];
+ int curWeapHeat;
+ int venomTime;
+#ifndef RTCWMP
+ int accShowBits;
+ int accHideBits;
+#endif
+ int aiState;
+#ifdef RTCWMP
+ int identifyClient;
+#else
+ float footstepCount;
+#endif
+#endif
} q3playerState_t;
@@ -130,6 +200,9 @@ typedef struct q3entityState_s {
int groundEntityNum; // -1 = in air
int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24)
+#ifdef RTCW
+ int dl_intensity; // used for coronas
+#endif
int loopSound; // constantly loop this sound
int modelindex;
@@ -142,6 +215,11 @@ typedef struct q3entityState_s {
int event; // impulse events -- muzzle flashes, footsteps, etc
int eventParm;
+#ifdef RTCW
+ int eventSequence;
+ int events[4];
+ int eventParms[4];
+#endif
// for players
int powerups; // bit flags
@@ -149,7 +227,18 @@ typedef struct q3entityState_s {
int legsAnim; // mask off ANIM_TOGGLEBIT
int torsoAnim; // mask off ANIM_TOGGLEBIT
+#ifdef RTCW
+ int density;
+ int dmgFlags;
+ int onFireStart;
+ int onFireEnd;
+ int aiChar, teamNum;
+ int effect1Time, effect2Time, effect3Time;
+ int aiState;
+ int animMovetype;
+#else
int generic1;
+#endif
} q3entityState_t;
@@ -201,11 +290,29 @@ typedef struct frame_s {
} q3frame_t;
typedef struct {
+#ifdef RTCW
+ int serverTime;
+ qbyte buttons;
+ qbyte wbuttons;
+ qbyte weapon;
+ qbyte holdable;
+ int angles[3];
+ signed char forwardmove, rightmove, upmove;
+ signed char wolfkick;
+
+ #ifdef RTCWMP
+ char mpSetup;
+ char identClient;
+ #else
+ unsigned short cld;
+ #endif
+#else
int serverTime;
int angles[3];
int buttons;
qbyte weapon; // weapon
signed char forwardmove, rightmove, upmove;
+#endif
} q3usercmd_t;
#define Q3CMD_BACKUP 64 //number of q3usercmd_ts that the client can queue before acks
#define Q3CMD_MASK (Q3CMD_BACKUP-1)
@@ -215,13 +322,22 @@ typedef struct {
#define MAX_Q3_GAMESTATE_CHARS 16000
#define MAX_STRING_CHARS 1024
+#ifdef RTCW
+ #define MAX_Q3_CONFIGSTRINGS 2048
+ #define Q3TEXTCMD_BACKUP 256
+#else
#define MAX_Q3_CONFIGSTRINGS 1024
#define Q3TEXTCMD_BACKUP 64 //number of reliable text commands that can be queued, must be power of two
+#endif
#define Q3TEXTCMD_MASK (Q3TEXTCMD_BACKUP-1)
#define CFGSTR_SYSINFO 1
+#ifdef RTCW
+ #define MODELINDEX_BITS 9
+#else
#define MODELINDEX_BITS 8
+#endif
#define GENTITYNUM_BITS 10
#define MAX_GENTITIES (1<<GENTITYNUM_BITS)
#define ENTITYNUM_NONE (MAX_GENTITIES-1)
@@ -247,6 +363,10 @@ typedef struct {
//this stuff gets inserted into usercmd_t messages.
int selected_weapon;
+#ifdef RTCW
+ int sendholdable;
+ int sendcld;
+#endif
qdownload_t *download;
model_t *worldmodel;
@@ -332,6 +452,38 @@ int UI_Cin_Draw(int idx);
int UI_Cin_SetExtents(int idx, int x, int y, int w, int h);
typedef struct {
+#ifdef RTCW
+ char renderer_string[MAX_STRING_CHARS];
+ char vendor_string[MAX_STRING_CHARS];
+ char version_string[MAX_STRING_CHARS];
+ char extensions_string[4*MAX_STRING_CHARS];
+
+ int maxtexturesize;
+ int numtextureunits;
+ int colorBits, depthBits, stencilBits;
+ int driverType;
+ int hardwareType;
+ qboolean supportsgamma;
+ int textureCompression;
+ qboolean textureEnvAddAvailable;
+ qboolean cananisrophic;
+ float maxanistrophy;
+
+ qboolean nvfogavail;
+ int nvfogmode;
+ int atitruform;
+ int atinormalmode;
+ int atipointmode;
+
+ int vidWidth, vidHeight;
+ float windowAspect;
+ int displayFrequency;
+
+ qboolean isFullscreen;
+ qboolean stereoEnabled;
+ qboolean smpActive;
+ qboolean anistrophy;
+#else
char renderer_string[MAX_STRING_CHARS];
char vendor_string[MAX_STRING_CHARS];
char version_string[MAX_STRING_CHARS];
@@ -357,6 +509,7 @@ typedef struct {
qboolean isFullscreen;
qboolean stereoEnabled;
qboolean smpActive;
+#endif
} q3glconfig_t;
diff --git a/plugins/quake3/q3common.c b/plugins/quake3/q3common.c
index fb0433c37..e7f20f937 100644
--- a/plugins/quake3/q3common.c
+++ b/plugins/quake3/q3common.c
@@ -1,14 +1,15 @@
#include "q3common.h"
-plug2dfuncs_t *drawfuncs;
-plug3dfuncs_t *scenefuncs;
-plugaudiofuncs_t *audiofuncs;
-plugq3vmfuncs_t *vmfuncs;
-plugfsfuncs_t *fsfuncs;
-pluginputfuncs_t *inputfuncs;
-plugclientfuncs_t *clientfuncs;
-plugmsgfuncs_t *msgfuncs;
-plugworldfuncs_t *worldfuncs;
-plugmasterfuncs_t *masterfuncs;
+plugsubconsolefuncs_t *confuncs;
+plug2dfuncs_t *drawfuncs;
+plug3dfuncs_t *scenefuncs;
+plugaudiofuncs_t *audiofuncs;
+plugq3vmfuncs_t *vmfuncs;
+plugfsfuncs_t *fsfuncs;
+pluginputfuncs_t *inputfuncs;
+plugclientfuncs_t *clientfuncs;
+plugmsgfuncs_t *msgfuncs;
+plugworldfuncs_t *worldfuncs;
+plugmasterfuncs_t *masterfuncs;
#ifndef STATIC_Q3
double realtime;
@@ -796,6 +797,87 @@ typedef struct {
// entityState_t
//
static const q3field_t esFieldTable[] = {
+#ifdef RTCW
+ ES_FIELD(eType, 8),
+ #ifdef RTCWMP
+ ES_FIELD(eFlags, 24),
+ #else
+ ES_FIELD(eFlags, 32),
+ #endif
+ ES_FIELD(pos.trType, 8),
+ ES_FIELD(pos.trTime, 32),
+ ES_FIELD(pos.trDuration, 32),
+ ES_FIELD(pos.trBase[0], 0),
+ ES_FIELD(pos.trBase[1], 0),
+ ES_FIELD(pos.trBase[2], 0),
+ ES_FIELD(pos.trDelta[0], 0),
+ ES_FIELD(pos.trDelta[1], 0),
+ ES_FIELD(pos.trDelta[2], 0),
+ ES_FIELD(apos.trType, 8),
+ ES_FIELD(apos.trTime, 32),
+ ES_FIELD(apos.trDuration, 32),
+ ES_FIELD(apos.trBase[0], 0),
+ ES_FIELD(apos.trBase[1], 0),
+ ES_FIELD(apos.trBase[2], 0),
+ ES_FIELD(apos.trDelta[0], 0),
+ ES_FIELD(apos.trDelta[1], 0),
+ ES_FIELD(apos.trDelta[2], 0),
+ ES_FIELD(time, 32),
+ ES_FIELD(time2, 32),
+ ES_FIELD(origin[0], 0),
+ ES_FIELD(origin[1], 0),
+ ES_FIELD(origin[2], 0),
+ ES_FIELD(origin2[0], 0),
+ ES_FIELD(origin2[1], 0),
+ ES_FIELD(origin2[2], 0),
+ ES_FIELD(angles[0], 0),
+ ES_FIELD(angles[1], 0),
+ ES_FIELD(angles[2], 0),
+ ES_FIELD(angles2[0], 0),
+ ES_FIELD(angles2[1], 0),
+ ES_FIELD(angles2[2], 0),
+ ES_FIELD(otherEntityNum, GENTITYNUM_BITS),
+ ES_FIELD(otherEntityNum2, GENTITYNUM_BITS),
+ ES_FIELD(groundEntityNum, GENTITYNUM_BITS),
+ ES_FIELD(loopSound, 8),
+ ES_FIELD(constantLight, 32),
+ ES_FIELD(dl_intensity, 32),
+ ES_FIELD(modelindex, MODELINDEX_BITS),
+ ES_FIELD(modelindex2, MODELINDEX_BITS),
+ ES_FIELD(frame, 16),
+ ES_FIELD(clientNum, 8),
+ ES_FIELD(solid, 24),
+ ES_FIELD(event, 10),
+ ES_FIELD(eventParm, 8),
+ ES_FIELD(eventSequence, 8),
+ ES_FIELD(events[0], 8),
+ ES_FIELD(events[1], 8),
+ ES_FIELD(events[2], 8),
+ ES_FIELD(events[3], 8),
+ ES_FIELD(eventParms[0], 8),
+ ES_FIELD(eventParms[1], 8),
+ ES_FIELD(eventParms[2], 8),
+ ES_FIELD(eventParms[3], 8),
+ ES_FIELD(powerups, MAX_Q3_POWERUPS),
+ ES_FIELD(weapon, 8),
+ ES_FIELD(legsAnim, 10),
+ ES_FIELD(torsoAnim, 10),
+ ES_FIELD(density, 10),
+ ES_FIELD(dmgFlags, 32),
+ ES_FIELD(onFireStart, 32),
+ ES_FIELD(onFireEnd, 32),
+ ES_FIELD(aiChar, 8),
+ ES_FIELD(teamNum, 8),
+ ES_FIELD(effect1Time, 32),
+ ES_FIELD(effect2Time, 32),
+ ES_FIELD(effect3Time, 32),
+ ES_FIELD(aiState, 2),
+ #ifdef RTCWMP
+ ES_FIELD(animMovetype, 4)
+ #else
+ ES_FIELD(animMovetype, 6)
+ #endif
+#else
ES_FIELD( pos.trTime, 32 ),
ES_FIELD( pos.trBase[0], 0 ),
ES_FIELD( pos.trBase[1], 0 ),
@@ -847,6 +929,7 @@ static const q3field_t esFieldTable[] = {
ES_FIELD( angles2[2], 0 ),
ES_FIELD( constantLight, 32 ),
ES_FIELD( frame, 16 )
+#endif
};
static const int esTableSize = sizeof( esFieldTable ) / sizeof( esFieldTable[0] );
@@ -1141,6 +1224,92 @@ void MSGQ3_WriteDeltaEntity(sizebuf_t *msg, const q3entityState_t *from, const q
// playerState_t
//
static const q3field_t psFieldTable[] = {
+#ifdef RTCW
+ PS_FIELD(commandTime, 32),
+ PS_FIELD(pm_type, 8),
+ PS_FIELD(bobCycle, 8),
+ PS_FIELD(pm_flags, 16),
+ PS_FIELD(pm_time, -16),
+ PS_FIELD(origin[0], 0),
+ PS_FIELD(origin[1], 0),
+ PS_FIELD(origin[2], 0),
+ PS_FIELD(velocity[0], 0),
+ PS_FIELD(velocity[1], 0),
+ PS_FIELD(velocity[2], 0),
+ PS_FIELD(weaponTime, -16),
+ PS_FIELD(weaponDelay, -16),
+ PS_FIELD(grenadeTimeLeft, -16),
+ PS_FIELD(gravity, 16),
+ PS_FIELD(leanf, 0),
+ PS_FIELD(speed, 16),
+ PS_FIELD(delta_angles[0], 16),
+ PS_FIELD(delta_angles[1], 16),
+ PS_FIELD(delta_angles[2], 16),
+ PS_FIELD(groundEntityNum, GENTITYNUM_BITS),
+ PS_FIELD(legsTimer, 16),
+ PS_FIELD(torsoTimer, 16),
+ PS_FIELD(legsAnim, 10),
+ PS_FIELD(torsoAnim, 10),
+ PS_FIELD(movementDir, 8),
+#ifdef RTCWMP
+ PS_FIELD(eFlags, 24),
+#else
+ PS_FIELD(eFlags, 32),
+#endif
+ PS_FIELD(eventSequence, 8),
+ PS_FIELD(events[0], 8),
+ PS_FIELD(events[1], 8),
+ PS_FIELD(events[2], 8),
+ PS_FIELD(events[3], 8),
+ PS_FIELD(eventParms[0], 8),
+ PS_FIELD(eventParms[1], 8),
+ PS_FIELD(eventParms[2], 8),
+ PS_FIELD(eventParms[3], 8),
+ PS_FIELD(clientNum, 8),
+ PS_FIELD(weapons[0], 32),
+ PS_FIELD(weapons[1], 32),
+ PS_FIELD(weapon, 7),
+ PS_FIELD(weaponstate, 4),
+ PS_FIELD(weapAnim, 10),
+ PS_FIELD(viewangles[0], 0),
+ PS_FIELD(viewangles[1], 0),
+ PS_FIELD(viewangles[2], 0),
+ PS_FIELD(viewheight, -8),
+ PS_FIELD(damageEvent, 8),
+ PS_FIELD(damageYaw, 8),
+ PS_FIELD(damagePitch, 8),
+ PS_FIELD(damageCount, 8),
+ PS_FIELD(mins[0], 0),
+ PS_FIELD(mins[1], 0),
+ PS_FIELD(mins[2], 0),
+ PS_FIELD(maxs[0], 0),
+ PS_FIELD(maxs[1], 0),
+ PS_FIELD(maxs[2], 0),
+ PS_FIELD(crouchMaxZ, 0),
+ PS_FIELD(crouchViewHeight, 0),
+ PS_FIELD(standViewHeight, 0),
+ PS_FIELD(deadViewHeight, 0),
+ PS_FIELD(runSpeedScale, 0),
+ PS_FIELD(sprintSpeedScale, 0),
+ PS_FIELD(crouchSpeedScale, 0),
+ PS_FIELD(friction, 0),
+ PS_FIELD(viewlocked, 8),
+ PS_FIELD(viewlocked_entNum, 16),
+ PS_FIELD(aiChar, 8),
+ PS_FIELD(teamNum, 8),
+ PS_FIELD(gunfx, 8),
+ PS_FIELD(onFireStart, 32),
+ PS_FIELD(curWeapHeat, 8),
+ PS_FIELD(sprintTime, 16),
+ PS_FIELD(aimSpreadScale, 8),
+ PS_FIELD(aiState, 2),
+ PS_FIELD(serverCursorHint, 8),
+ PS_FIELD(serverCursorHintVal, 8),
+ PS_FIELD(classWeaponTime, 32),
+#ifndef RTCWMP
+ PS_FIELD(footstepCount, 0),
+#endif
+#else
PS_FIELD( commandTime, 32 ),
PS_FIELD( origin[0], 0 ),
PS_FIELD( origin[1], 0 ),
@@ -1189,6 +1358,7 @@ static const q3field_t psFieldTable[] = {
PS_FIELD( grapplePoint[2], 0 ),
PS_FIELD( jumppad_ent, 10 ),
PS_FIELD( loopSound, 16 )
+#endif
};
static const int psTableSize = sizeof( psFieldTable ) / sizeof( psFieldTable[0] );
q3playerState_t nullPlayerState;
@@ -1211,7 +1381,13 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
int maxFieldNum;
unsigned int statsMask;
unsigned int persistantMask;
+#ifdef RTCW
+ unsigned int holdableMask;
+ unsigned short ammoMask[MAX_Q3_WEAPONS/16];
+ unsigned short ammoClipMask[MAX_Q3_WEAPONS/16];
+#else
unsigned int ammoMask;
+#endif
unsigned int powerupsMask;
int i, j;
@@ -1303,12 +1479,38 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
persistantMask |= (1u << i);
}
+#ifdef RTCW
+ for (j = 0; j < MAX_Q3_WEAPONS/16; j++)
+ {
+ ammoMask[j] = 0;
+ for(i=0 ; i<16 ; i++ )
+ {
+ if(from->ammo[j*16+i] != to->ammo[j*16+i])
+ ammoMask[j] |= (1u << i);
+ }
+
+ ammoClipMask[j] = 0;
+ for(i=0 ; i<MAX_Q3_WEAPONS ; i++ )
+ {
+ if(from->ammoclip[j*16+i] != to->ammoclip[j*16+i])
+ ammoClipMask[j] |= (1u << i);
+ }
+ }
+
+ holdableMask = 0;
+ for(i=0 ; i<countof(to->holdable) ; i++ )
+ {
+ if(from->holdable[i] != to->holdable[i])
+ holdableMask |= (1u << i);
+ }
+#else
ammoMask = 0;
for(i=0 ; i<MAX_Q3_WEAPONS ; i++ )
{
if(from->ammo[i] != to->ammo[i])
ammoMask |= (1u << i);
}
+#endif
powerupsMask = 0;
for( i=0 ; i<MAX_Q3_POWERUPS ; i++ )
@@ -1318,7 +1520,11 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
}
if(statsMask || persistantMask
+#ifdef RTCW
+ || holdableMask
+#else
|| ammoMask
+#endif
|| powerupsMask)
{
//
@@ -1350,6 +1556,21 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
else
msgfuncs->WriteBits(msg, 0, 1); // unchanged
+#ifdef RTCW
+ // PS_HOLDABLE
+ if(holdableMask)
+ {
+ msgfuncs->WriteBits(msg, 1, 1);
+ msgfuncs->WriteBits(msg, holdableMask, 16);
+ for(i=0; i<countof(to->holdable); i++)
+ {
+ if(holdableMask & (1 << i))
+ msgfuncs->WriteBits(msg, to->holdable[i], -16);
+ }
+ }
+ else
+ msgfuncs->WriteBits( msg, 0, 1 ); // unchanged
+#else
for (j = 0; j < MAX_Q3_WEAPONS/16; j++)
{
// PS_AMMO
@@ -1364,6 +1585,7 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
else
msgfuncs->WriteBits(msg, 0, 1); // unchanged
}
+#endif
// PS_POWERUPS
if(powerupsMask)
@@ -1382,6 +1604,43 @@ void MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, co
else
msgfuncs->WriteBits(msg, 0, 1);
+#ifdef RTCW
+ if (ammoMask[0] || ammoMask[1] || ammoMask[2] || ammoMask[3])
+ {
+ msgfuncs->WriteBits(msg, 1, 1);
+ // PS_AMMO
+ for (j = 0; j < MAX_Q3_WEAPONS/16; j++)
+ {
+ if (ammoMask[j])
+ {
+ msgfuncs->WriteBits(msg, 1, 1);
+ msgfuncs->WriteBits(msg, ammoMask[j], 16);
+ for(i=0; i<16; i++)
+ if(ammoMask[j] & (1u << i))
+ msgfuncs->WriteBits(msg, to->ammo[j*16+i], 16);
+ }
+ else
+ msgfuncs->WriteBits(msg, 0, 1); // unchanged
+ }
+ }
+ else
+ msgfuncs->WriteBits(msg, 0, 1); //saved 3 bits. woo.
+
+ // PS_AMMOCLIP
+ for (j = 0; j < MAX_Q3_WEAPONS/16; j++)
+ {
+ if (ammoClipMask[j])
+ {
+ msgfuncs->WriteBits(msg, 1, 1);
+ msgfuncs->WriteBits(msg, ammoClipMask[j], 16);
+ for(i=0; i<16; i++)
+ if(ammoClipMask[j] & (1u << i))
+ msgfuncs->WriteBits(msg, to->ammoclip[j*16+i], 16);
+ }
+ else
+ msgfuncs->WriteBits(msg, 0, 1); // unchanged
+ }
+#endif
}
#endif
@@ -1516,6 +1775,26 @@ void MSG_Q3_ReadDeltaPlayerstate( const q3playerState_t *from, q3playerState_t *
}
}
+#ifdef RTCW
+ // PS_HOLDABLE
+ if( msgfuncs->ReadBits(1) )
+ {
+#ifdef MSG_SHOWNET
+ if( moredump ) {
+ Com_Printf( "PS_HOLDABLE " );
+ }
+#endif
+
+ bitmask = msgfuncs->ReadBits(16);
+ for( i=0 ; i<countof(to->holdable) ; i++ )
+ {
+ if( bitmask & (1u << i) )
+ {
+ to->holdable[i] = (signed short)msgfuncs->ReadBits(-16);
+ }
+ }
+ }
+#else
// PS_AMMO
for (j=0; j < MAX_Q3_WEAPONS/16; j++)
{
@@ -1537,6 +1816,7 @@ void MSG_Q3_ReadDeltaPlayerstate( const q3playerState_t *from, q3playerState_t *
}
}
}
+#endif
// PS_POWERUPS
if( msgfuncs->ReadBits(1) )
{
@@ -1557,6 +1837,53 @@ void MSG_Q3_ReadDeltaPlayerstate( const q3playerState_t *from, q3playerState_t *
}
}
+#ifdef RTCW
+ // PS_AMMO (moved out of the above block for some reason)
+ if( msgfuncs->ReadBits(1) )
+ {
+ for (j=0; j < MAX_Q3_WEAPONS/16; j++)
+ {
+ if( msgfuncs->ReadBits(1) )
+ {
+#ifdef MSG_SHOWNET
+ if( moredump )
+ {
+ Com_Printf( "PS_AMMO " );
+ }
+#endif
+ bitmask = msgfuncs->ReadBits(16);
+ for( i=0 ; i<16 ; i++ )
+ {
+ if( bitmask & (1u << i) )
+ {
+ to->ammo[j*16+i] = (signed short)msgfuncs->ReadBits(16);
+ }
+ }
+ }
+ }
+ }
+ // PS_AMMOCLIP (also moved out, for some reason)
+ for (j=0; j < MAX_Q3_WEAPONS/16; j++)
+ {
+ if( msgfuncs->ReadBits(1) )
+ {
+#ifdef MSG_SHOWNET
+ if( moredump )
+ {
+ Com_Printf( "PS_AMMOCLIP " );
+ }
+#endif
+ bitmask = msgfuncs->ReadBits(16);
+ for( i=0 ; i<16 ; i++ )
+ {
+ if( bitmask & (1u << i) )
+ {
+ to->ammoclip[j*16+i] = (signed short)msgfuncs->ReadBits(16);
+ }
+ }
+ }
+ }
+#endif
#ifdef MSG_SHOWNET
@@ -1619,9 +1946,24 @@ void MSG_Q3_ReadDeltaUsercmd(int key, const usercmd_t *from, usercmd_t *to)
to->forwardmove = (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->forwardmove, 8);
to->sidemove = (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->sidemove, 8);
to->upmove = (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->upmove, 8);
+#ifdef RTCW
+ to->buttons = MSG_ReadDeltaKey(key, from->buttons&255, 8);
+ to->buttons |= MSG_ReadDeltaKey(key, (from->buttons>>8)&255, 8)<<8; //wbuttons
+#else
to->buttons = MSG_ReadDeltaKey(key, from->buttons, 16);
+#endif
to->weapon = MSG_ReadDeltaKey(key, from->weapon, 8);
+#ifdef RTCW
+ to->buttons |= MSG_ReadDeltaKey(key, (from->buttons>>24)&255, 8)<<24; //holdable
+ to->buttons |= MSG_ReadDeltaKey(key, (from->buttons>>16)&255, 8)<<16; //wolfkick
+#ifdef RTCWMP
+ to->weapon |= MSG_ReadDeltaKey(key, (from->weapon>>16)&255, 8)<<16; //cld
+ to->weapon |= MSG_ReadDeltaKey(key, (from->weapon>>24)&255, 8)<<24; //cld
+#else
+ to->weapon |= MSG_ReadDeltaKey(key, (from->weapon>>16)&255, 16)<<16; //cld
+#endif
+#endif
}
}
@@ -1739,6 +2081,7 @@ qboolean Plug_Init(void)
plugfuncs->ExportFunction("Tick", Q3_Frame);
#endif
+ confuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));
drawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));
scenefuncs = plugfuncs->GetEngineInterface(plug3dfuncs_name, sizeof(*scenefuncs));
inputfuncs = plugfuncs->GetEngineInterface(pluginputfuncs_name, sizeof(*inputfuncs));
diff --git a/plugins/quake3/q3common.h b/plugins/quake3/q3common.h
index 2e5755692..199d8aea9 100644
--- a/plugins/quake3/q3common.h
+++ b/plugins/quake3/q3common.h
@@ -6,18 +6,23 @@
//#define Q3_NOENCRYPT //a debugging property, makes it incompatible with q3
-extern plug2dfuncs_t *drawfuncs;
-extern plug3dfuncs_t *scenefuncs;
-extern pluginputfuncs_t *inputfuncs;
-extern plugaudiofuncs_t *audiofuncs;
-extern plugmasterfuncs_t*masterfuncs;
-extern plugclientfuncs_t*clientfuncs;
-
-extern plugq3vmfuncs_t *vmfuncs;
-extern plugfsfuncs_t *fsfuncs;
-extern plugmsgfuncs_t *msgfuncs;
-extern plugworldfuncs_t *worldfuncs;
-
+extern plugsubconsolefuncs_t*confuncs;
+extern plug2dfuncs_t *drawfuncs;
+extern plug3dfuncs_t *scenefuncs;
+extern pluginputfuncs_t *inputfuncs;
+extern plugaudiofuncs_t *audiofuncs;
+extern plugmasterfuncs_t *masterfuncs;
+extern plugclientfuncs_t *clientfuncs;
+
+extern plugq3vmfuncs_t *vmfuncs;
+extern plugfsfuncs_t *fsfuncs;
+extern plugmsgfuncs_t *msgfuncs;
+extern plugworldfuncs_t *worldfuncs;
+
+#ifdef RTCW
+extern vec3_t skyroom_origin;
+extern qboolean skyroom_forced;
+#endif
extern cvar_t *sv_maxclients;
extern cvar_t *cl_shownet_ptr, *cl_c2sdupe_ptr, *cl_nodelta_ptr;
diff --git a/plugins/quake3/q3g_public.h b/plugins/quake3/q3g_public.h
index 12b63fde6..25163fb70 100644
--- a/plugins/quake3/q3g_public.h
+++ b/plugins/quake3/q3g_public.h
@@ -86,6 +86,12 @@ typedef struct {
// ent->s.ownerNum = passEntityNum (don't interact with your own missiles)
// entity[ent->s.ownerNum].ownerNum = passEntityNum (don't interact with other missiles from owner)
int ownerNum;
+#ifdef RTCW
+ int eventTime;
+#endif
+#ifdef RTCWMP
+ int worldflags;
+#endif
} q3entityShared_t;
@@ -110,6 +116,9 @@ typedef enum {
G_ERROR, // ( const char *string );
// abort the game
+#ifdef RTCWSP
+ G_ENDGAME,
+#endif
G_MILLISECONDS, // ( void );
// get current time for profiling reasons
@@ -132,6 +141,9 @@ typedef enum {
G_FS_FOPEN_FILE, // ( const char *qpath, fileHandle_t *file, fsMode_t mode );
G_FS_READ, // ( void *buffer, int len, fileHandle_t f );
G_FS_WRITE, // ( const void *buffer, int len, fileHandle_t f );
+#ifdef RTCW
+ G_FS_RENAME,
+#endif
G_FS_FCLOSE_FILE, // ( fileHandle_t f );
G_SEND_CONSOLE_COMMAND, // ( const char *text );
@@ -226,8 +238,12 @@ typedef enum {
G_TRACECAPSULE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
G_ENTITY_CONTACTCAPSULE, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );
+#ifdef RTCW
+ G_GETTAG,
+#else
// 1.32
G_FS_SEEK,
+#endif
G_MEMSET = 100,
G_MEMCPY,
@@ -256,15 +272,24 @@ typedef enum {
BOTLIB_GET_CONSOLE_MESSAGE, // ( int client, char *message, int size );
BOTLIB_USER_COMMAND, // ( int client, usercmd_t *ucmd );
+#ifdef RTCW
+ BOTLIB_AAS_ENTITY_VISIBLE = 300,
+ BOTLIB_AAS_IN_FIELD_OF_VISION,
+ BOTLIB_AAS_VISIBLE_CLIENTS,
+#else
BOTLIB_AAS_ENABLE_ROUTING_AREA = 300,
BOTLIB_AAS_BBOX_AREAS,
BOTLIB_AAS_AREA_INFO,
+#endif
BOTLIB_AAS_ENTITY_INFO,
BOTLIB_AAS_INITIALIZED,
BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX,
BOTLIB_AAS_TIME,
+#ifdef RTCW
+ BOTLIB_AAS_SETCURRENTWORLD,
+#endif
BOTLIB_AAS_POINT_AREA_NUM,
BOTLIB_AAS_TRACE_AREAS,
@@ -282,9 +307,42 @@ typedef enum {
BOTLIB_AAS_SWIMMING,
BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT,
+#ifdef RTCW
+ BOTLIB_AAS_RT_SHOWROUTE,
+ BOTLIB_AAS_RT_GETHIDEPOS,
+ BOTLIB_AAS_FINDATTACKSPOTWITHINRANGE,
+#ifdef RTCWSP
+ BOTLIB_AAS_GETROUTEFIRSTVISPOS,
+#endif
+ BOTLIB_AAS_SETAASBLOCKINGENTITY,
+#endif
BOTLIB_EA_SAY = 400,
BOTLIB_EA_SAY_TEAM,
+#ifdef RTCW
+ BOTLIB_EA_USE_ITEM,
+ BOTLIB_EA_DROP_ITEM,
+ BOTLIB_EA_USE_INV,
+ BOTLIB_EA_DROP_INV,
+ BOTLIB_EA_GESTURE,
+ BOTLIB_EA_COMMAND,
+
+ BOTLIB_EA_SELECT_WEAPON,
+ BOTLIB_EA_TALK,
+ BOTLIB_EA_ATTACK,
+ BOTLIB_EA_RELOAD,
+ BOTLIB_EA_USE,
+ BOTLIB_EA_RESPAWN,
+ BOTLIB_EA_JUMP,
+ BOTLIB_EA_DELAYED_JUMP,
+ BOTLIB_EA_CROUCH,
+ BOTLIB_EA_MOVE_UP,
+ BOTLIB_EA_MOVE_DOWN,
+ BOTLIB_EA_MOVE_FORWARD,
+ BOTLIB_EA_MOVE_BACK,
+ BOTLIB_EA_MOVE_LEFT,
+ BOTLIB_EA_MOVE_RIGHT,
+#else
BOTLIB_EA_COMMAND,
BOTLIB_EA_ACTION,
@@ -304,6 +362,7 @@ typedef enum {
BOTLIB_EA_SELECT_WEAPON,
BOTLIB_EA_JUMP,
BOTLIB_EA_DELAYED_JUMP,
+#endif
BOTLIB_EA_MOVE,
BOTLIB_EA_VIEW,
@@ -373,6 +432,9 @@ typedef enum {
BOTLIB_AI_ALLOC_MOVE_STATE,
BOTLIB_AI_FREE_MOVE_STATE,
BOTLIB_AI_INIT_MOVE_STATE,
+#ifdef RTCW
+ BOTLIB_AI_INIT_AVOID_REACH,
+#endif
BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON,
BOTLIB_AI_GET_WEAPON_INFO,
BOTLIB_AI_LOAD_WEAPON_WEIGHTS,
@@ -401,6 +463,10 @@ typedef enum {
BOTLIB_PC_READ_TOKEN,
BOTLIB_PC_SOURCE_FILE_AND_LINE,
+#ifdef RTCWSP
+ G_FS_COPY_FILE,
+ G_ALLOC = 900, //for iortcw compat
+#endif
G_DEFAULTCASEWARNINGDISABLE //note: not an allowed index, just exists to prevent clang from warning about the default case.
} q3ggameImport_t;
diff --git a/plugins/quake3/svq3_game.c b/plugins/quake3/svq3_game.c
index 97eae787e..e07e78e1c 100644
--- a/plugins/quake3/svq3_game.c
+++ b/plugins/quake3/svq3_game.c
@@ -688,6 +688,17 @@ static qboolean SVQ3_GetUserCmd(int clientnumber, q3usercmd_t *ucmd)
ucmd->buttons = cmd->buttons;
ucmd->weapon = cmd->weapon;
+#ifdef RTCW
+ ucmd->wbuttons=cmd->buttons>>8;
+ ucmd->wolfkick=cmd->buttons>>16;
+ ucmd->holdable=cmd->buttons>>24;
+ #ifdef RTCWMP
+ ucmd->mpSetup = cmd->weapon>>16;
+ ucmd->identClient = cmd->weapon>>24;
+ #else
+ ucmd->cld = cmd->weapon>>16;
+ #endif
+#endif
return true;
}
@@ -924,8 +935,10 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
case G_FS_WRITE: //fwrite
ret = VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);
break;
+#ifndef RTCW
case G_FS_SEEK:
return VM_FSeek(arg[0], arg[1], arg[2], 0);
+#endif
case G_FS_FCLOSE_FILE: //fclose
VM_fclose(VM_LONG(arg[0]), 0);
break;
@@ -1148,6 +1161,7 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
}
return 0;
+#ifndef RTCW
case BOTLIB_AAS_ENABLE_ROUTING_AREA:
return botlib->aas.AAS_EnableRoutingArea(VM_LONG(arg[0]), VM_LONG(arg[1]));
case BOTLIB_AAS_BBOX_AREAS:
@@ -1155,6 +1169,7 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
return botlib->aas.AAS_BBoxAreas(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));
case BOTLIB_AAS_AREA_INFO:
return botlib->aas.AAS_AreaInfo(VM_LONG(arg[0]), VM_POINTER(arg[1]));
+#endif
case BOTLIB_AAS_ENTITY_INFO:
botlib->aas.AAS_EntityInfo(VM_LONG(arg[0]), VM_POINTER(arg[1]));
return 0;
@@ -1216,9 +1231,11 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
botlib->ea.EA_Command(VM_LONG(arg[0]), VM_POINTER(arg[1]));
return 0;
+#ifndef RTCW
case BOTLIB_EA_ACTION:
botlib->ea.EA_Action(VM_LONG(arg[0]), VM_LONG(arg[1]));
return 0;
+#endif
case BOTLIB_EA_GESTURE:
botlib->ea.EA_Gesture(VM_LONG(arg[0]));
return 0;
@@ -1279,12 +1296,22 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
botlib->ea.EA_GetInput(VM_LONG(arg[0]), VM_FLOAT(arg[1]), VM_POINTER(arg[2]));
return 0;
case BOTLIB_EA_RESET_INPUT:
- botlib->ea.EA_ResetInput(VM_LONG(arg[0]));
+ botlib->ea.EA_ResetInput(VM_LONG(arg[0]),
+#ifdef RTCW
+ VM_POINTER(arg[1])
+#else
+ NULL
+#endif
+ );
return 0;
case BOTLIB_AI_LOAD_CHARACTER:
+#ifdef RTCW
+ return botlib->ai.BotLoadCharacter(VM_POINTER(arg[0]), VM_LONG(arg[1]));
+#else
return botlib->ai.BotLoadCharacter(VM_POINTER(arg[0]), VM_FLOAT(arg[1]));
+#endif
case BOTLIB_AI_FREE_CHARACTER:
botlib->ai.BotFreeCharacter(arg[0]);
return 0;
@@ -1347,7 +1374,11 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
return 0;
case BOTLIB_AI_SET_CHAT_NAME:
botlib->ai.BotSetChatName(arg[0], VM_POINTER(arg[1]),
+#ifdef RTCW
+ -1/*defers to BOTLIB_AI_ENTER_CHAT*/
+#else
arg[2]
+#endif
);
return 0;
@@ -1532,6 +1563,69 @@ static qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, co
#endif
+#ifdef RTCWSP
+ case G_ENDGAME:
+ plugfuncs->EndGame("endgame\r");
+ break;
+ case G_FS_COPY_FILE: Con_Printf("RTCW: builtin %s is not implemented\n", "G_FS_COPY_FILE"); return ret;
+ case G_ALLOC: Con_Printf("RTCW: builtin %s is not implemented\n", "G_ALLOC"); return ret;
+#endif
+#ifdef RTCW
+ case G_FS_RENAME:
+ return fsfuncs->Rename(VM_POINTER(arg[0]), VM_POINTER(arg[1]), FS_GAMEONLY);
+ case G_GETTAG:
+#ifdef VM_CG
+ if (cgvm && vmfuncs->NonNative(cgvm))
+ return false;
+ else if (cgvm)
+ return vmfuncs->Call(cgvm, 9/*CG_GET_TAG*/, VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));
+#endif
+ break;
+
+#ifdef USEBOTLIB
+ case BOTLIB_AAS_SETCURRENTWORLD:
+ if (botlib)
+ botlib->aas.AAS_SetCurrentWorld(VM_LONG(arg[0]));
+ break;
+
+ case BOTLIB_AAS_ENTITY_VISIBLE: Con_Printf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_ENTITY_VISIBLE"); return ret; //unused
+ case BOTLIB_AAS_IN_FIELD_OF_VISION: Con_Printf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_IN_FIELD_OF_VISION"); return ret; //unused
+ case BOTLIB_AAS_VISIBLE_CLIENTS: Con_Printf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_VISIBLE_CLIENTS"); return ret; //unused
+ case BOTLIB_AAS_RT_SHOWROUTE: Con_DPrintf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_RT_SHOWROUTE"); return ret; //debugging only
+// botlib->aas.AAS_RT_ShowRoute(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]));
+ break;
+ case BOTLIB_AAS_RT_GETHIDEPOS:
+ return botlib->aas.AAS_RT_GetHidePos(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_POINTER(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5]), VM_POINTER(arg[6]));
+ case BOTLIB_AAS_FINDATTACKSPOTWITHINRANGE: Con_DPrintf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_FINDATTACKSPOTWITHINRANGE");return ret;//dumb ai
+ return botlib->aas.AAS_FindAttackSpotWithinRange(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_FLOAT(arg[3]), VM_LONG(arg[4]), VM_POINTER(arg[5]));
+#ifdef RTCWSP
+ case BOTLIB_AAS_GETROUTEFIRSTVISPOS: Con_DPrintf("RTCW: builtin %s is not implemented\n", "BOTLIB_AAS_GETROUTEFIRSTVISPOS"); return ret; //fixme!
+ return botlib->aas.AAS_GetRouteFirstVisPos(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), VM_POINTER(arg[3]));
+#endif
+ case BOTLIB_AAS_SETAASBLOCKINGENTITY:
+ if (botlib)
+ botlib->aas.AAS_SetAASBlockingEntity(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));
+ break;
+ case BOTLIB_EA_USE_ITEM:
+ botlib->ea.EA_UseItem(VM_LONG(arg[0]), VM_POINTER(arg[1]));
+ break;
+ case BOTLIB_EA_DROP_ITEM:
+ botlib->ea.EA_DropItem(VM_LONG(arg[0]), VM_POINTER(arg[1]));
+ break;
+ case BOTLIB_EA_USE_INV:
+ botlib->ea.EA_UseInv(VM_LONG(arg[0]), VM_POINTER(arg[1]));
+ break;
+ case BOTLIB_EA_DROP_INV:
+ botlib->ea.EA_DropInv(VM_LONG(arg[0]), VM_POINTER(arg[1]));
+ break;
+ case BOTLIB_EA_RELOAD:
+ botlib->ea.EA_Reload(VM_LONG(arg[0]));
+ break;
+ case BOTLIB_AI_INIT_AVOID_REACH:
+ botlib->ai.BotResetAvoidReach(VM_LONG(arg[0]));
+ break;
+#endif
+#endif
case G_IN_PVS:
return SV_InPVS(VM_POINTER(arg[0]), VM_POINTER(arg[1]));
@@ -1706,9 +1800,9 @@ static int QDECL BL_Seek(fileHandle_t f, long offset, int seektype)
{ // on success, apparently returns 0
return VM_FSeek((int)f, offset, seektype, Z_TAG_BOTLIB)?0:-1;
}
-static char *QDECL BL_BSPEntityData(void)
+static const char *QDECL BL_BSPEntityData(void)
{
- return (char*)worldfuncs->GetEntitiesString(sv3.world->worldmodel);
+ return worldfuncs->GetEntitiesString(sv3.world->worldmodel);
}
static void QDECL BL_Trace(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)
{
@@ -1882,6 +1976,20 @@ static void SVQ3_UpdateServerinfo(void)
Q_strncatz(buffer, va("\\sv_maxclients\\%s", sv_maxclients->string), sizeof(buffer));
SVQ3_SetConfigString(0, buffer);
+#ifdef RTCWMP
+ {
+ int i;
+ char *wolfinfo[] = {"gamestate", "g_currentRound", "g_nextTimeLimit"};
+ *buffer = 0;
+ for (i = 0; i < countof(wolfinfo); i++)
+ {
+ cvar_t *v = cvarfuncs->GetNVFDG(wolfinfo[i], NULL, 0, NULL, "Q3-Game-Code created");
+ if (v)
+ worldfuncs->SetInfoKey(buffer, wolfinfo[i], v->string, sizeof(buffer));
+ }
+ }
+ SVQ3_SetConfigString(36, buffer);
+#endif
q3_serverinfo_dirty = false;
}
@@ -1914,7 +2022,17 @@ qboolean SVQ3_InitGame(server_static_t *server_state_static, server_t *server_st
sv_maxclients = cvarfuncs->GetNVFDG("sv_maxclients", "", 0, NULL, "Q3 Compat");
+#ifdef RTCW
+ #ifdef RTCWMP
+ if (!restart)
+ cvarfuncs->SetString("gamestate", "-1"); //set to -1 on new map
+ q3gamevm = vmfuncs->Create("qagame.mp.", cvarfuncs->GetFloat("com_gamedirnativecode")?Q3G_SystemCallsNative:NULL, "vm/qagame.mp", Q3G_SystemCallsVM);
+ #else
+ q3gamevm = vmfuncs->Create("qagame.sp.", cvarfuncs->GetFloat("com_gamedirnativecode")?Q3G_SystemCallsNative:NULL, "vm/qagame.sp", Q3G_SystemCallsVM);
+ #endif
+#else
q3gamevm = vmfuncs->Create("qagame", cvarfuncs->GetFloat("com_gamedirnativecode")?Q3G_SystemCallsNative:NULL, "vm/qagame", Q3G_SystemCallsVM);
+#endif
if (!q3gamevm)
return false;
@@ -2036,6 +2154,9 @@ void SVQ3_CreateBaseline(void)
if(!ent->r.linked)
continue;
+#ifdef RTCW
+ ent->s.number = entnum;
+#endif
// FIXME - is this check correct?
if(ent->r.svFlags & (SVF_NOCLIENT|/*SVF_CLIENTMASK|*/SVF_SINGLECLIENT))
@@ -3248,6 +3369,49 @@ qboolean SVQ3_HandleClient(netadr_t *from, sizebuf_t *msg)
return true;
}
+#ifdef RTCWMP
+typedef enum {
+ GS_INITIALIZE = -1,
+ GS_PLAYING,
+ GS_WARMUP_COUNTDOWN,
+ GS_WARMUP,
+ GS_INTERMISSION,
+ GS_WAITING_FOR_PLAYERS,
+ GS_RESET
+} rtcwgamestate_t;
+//called from inside map_restart
+static qboolean RTCWMP_AllowRestart(void)
+{
+ rtcwgamestate_t oldstate = cvarfuncs->GetFloat("gamestate");
+ rtcwgamestate_t newstate;
+ char newstatebuf[64];
+ cmdfuncs->Argv(2, newstatebuf, sizeof(newstatebuf));
+ newstate = atoi(newstatebuf);
+
+ //always do warmups on new matches
+ if (oldstate == GS_INTERMISSION && newstate == GS_PLAYING)
+ newstate = GS_WARMUP;
+ if (oldstate == newstate && newstate != GS_PLAYING)
+ return false;
+ if (oldstate==GS_WAITING_FOR_PLAYERS && newstate == GS_WARMUP)
+ return false;
+ if (oldstate == GS_INTERMISSION && newstate != GS_WARMUP)
+ return false;
+ if (oldstate == GS_RESET && (newstate != GS_WAITING_FOR_PLAYERS && newstate != GS_WARMUP))
+ return false;
+ if (newstate == GS_RESET)
+ {
+ cvar_t *nts = cvarfuncs->GetNVFDG("g_noTeamSwitching", "0", 0, NULL, "rtcw compat");
+ if (nts && nts->ival)
+ newstate = GS_WAITING_FOR_PLAYERS;
+ else
+ newstate = GS_WARMUP;
+ }
+ cvarfuncs->SetFloat("gamestate", newstate);
+ return true;
+}
+#endif
+
//Q3 gamecode does map_restart weirdly.
//it simply reloads the gamecode without changing any maps/models/sounds
@@ -3260,6 +3424,11 @@ qboolean SVQ3_RestartGamecode(void)
if (sv.allocated_client_slots != newmaxclients)
return false; //can't do it if maxclients needs to change.
+#ifdef RTCWMP
+ if (!RTCWMP_AllowRestart())
+ return true; //if restart is not allowed, just pretend that we did it without doing anything.
+#endif
+
//reload the gamecode
sv.state = ss_loading;
if (!SVQ3_InitGame(sv3.server_state_static, sv3.server_state, true))