qw support is 99% working
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 26 Feb 2006 03:00:47 +0000 (03:00 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 26 Feb 2006 03:00:47 +0000 (03:00 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6033 d7cf8633-e32d-0410-b094-e92efae38249

15 files changed:
cl_input.c
cl_main.c
cl_parse.c
cl_screen.c
client.h
cmd.c
common.c
host_cmd.c
netconn.c
progs.h
protocol.c
protocol.h
sbar.c
sv_user.c
view.c

index 62451ea..e6d0536 100644 (file)
@@ -858,9 +858,9 @@ void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, qw_usercmd_t *from, qw_usercmd_t *
        if (bits & QW_CM_UP)
                MSG_WriteShort(buf, to->upmove);
        if (bits & QW_CM_BUTTONS)
-               MSG_WriteShort(buf, to->buttons);
+               MSG_WriteByte(buf, to->buttons);
        if (bits & QW_CM_IMPULSE)
-               MSG_WriteShort(buf, to->impulse);
+               MSG_WriteByte(buf, to->impulse);
        MSG_WriteByte(buf, to->msec);
 }
 
@@ -986,6 +986,7 @@ void CL_SendMove(void)
                        // PROTOCOL_DARKPLACES5  clc_move = 19 bytes total
                        // PROTOCOL_DARKPLACES6  clc_move = 52 bytes total
                        // PROTOCOL_DARKPLACES7  clc_move = 56 bytes total
+                       // PROTOCOL_QUAKEWORLD   clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll)
                        if (cls.protocol == PROTOCOL_QUAKEWORLD)
                        {
                                int checksumindex;
@@ -1042,9 +1043,12 @@ void CL_SendMove(void)
                                // request delta compression if appropriate
                                if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
                                {
+                                       cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
                                        MSG_WriteByte(&buf, qw_clc_delta);
                                        MSG_WriteByte(&buf, cl.qw_validsequence & 255);
                                }
+                               else
+                                       cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1;
                        }
                        else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE)
                        {
index db35eac..46db360 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -124,6 +124,7 @@ CL_ClearState
 void CL_ClearState(void)
 {
        int i;
+       entity_t *ent;
 
        if (cl_entities) Mem_Free(cl_entities);cl_entities = NULL;
        if (cl_csqcentities) Mem_Free(cl_csqcentities);cl_csqcentities = NULL;  //[515]: csqc
@@ -138,6 +139,7 @@ void CL_ClearState(void)
        if (cl_brushmodel_entities) Mem_Free(cl_brushmodel_entities);cl_brushmodel_entities = NULL;
        if (cl.entitydatabase) EntityFrame_FreeDatabase(cl.entitydatabase);cl.entitydatabase = NULL;
        if (cl.entitydatabase4) EntityFrame4_FreeDatabase(cl.entitydatabase4);cl.entitydatabase4 = NULL;
+       if (cl.entitydatabaseqw) EntityFrameQW_FreeDatabase(cl.entitydatabaseqw);cl.entitydatabaseqw = NULL;
        if (cl.scores) Mem_Free(cl.scores);cl.scores = NULL;
 
        if (!sv.active)
@@ -145,6 +147,9 @@ void CL_ClearState(void)
 
 // wipe the entire cl structure
        memset (&cl, 0, sizeof(cl));
+
+       S_StopAllSounds();
+
        // reset the view zoom interpolation
        cl.mviewzoom[0] = cl.mviewzoom[1] = 1;
 
@@ -213,6 +218,27 @@ void CL_ClearState(void)
                VectorSet(cl_playercrouchmaxs, 16, 16, 24);
        }
 
+       // disable until we get textures for it
+       R_ResetSkyBox();
+
+       ent = &cl_entities[0];
+       // entire entity array was cleared, so just fill in a few fields
+       ent->state_current.active = true;
+       ent->render.model = cl.worldmodel = NULL; // no world model yet
+       ent->render.scale = 1; // some of the renderer still relies on scale
+       ent->render.alpha = 1;
+       ent->render.colormap = -1; // no special coloring
+       ent->render.flags = RENDER_SHADOW | RENDER_LIGHT;
+       Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1);
+       Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
+       CL_BoundingBoxForEntity(&ent->render);
+
+       // noclip is turned off at start
+       noclip_anglehack = false;
+
+       // mark all frames invalid for delta
+       memset(cl.qw_deltasequence, -1, sizeof(cl.qw_deltasequence));
+
        CL_Screen_NewMap();
        CL_Particles_Clear();
        CL_CGVM_Clear();
@@ -307,13 +333,22 @@ void CL_Disconnect(void)
                if (cls.demorecording)
                        CL_Stop_f();
 
-               // send clc_disconnect 3 times to improve chances of server receiving
-               // it (but it still fails sometimes)
-               Con_DPrint("Sending clc_disconnect\n");
+               // send disconnect message 3 times to improve chances of server
+               // receiving it (but it still fails sometimes)
                memset(&buf, 0, sizeof(buf));
                buf.data = bufdata;
                buf.maxsize = sizeof(bufdata);
-               MSG_WriteByte(&buf, clc_disconnect);
+               if (cls.protocol == PROTOCOL_QUAKEWORLD)
+               {
+                       Con_DPrint("Sending drop command\n");
+                       MSG_WriteByte(&buf, qw_clc_stringcmd);
+                       MSG_WriteString(&buf, "drop");
+               }
+               else
+               {
+                       Con_DPrint("Sending clc_disconnect\n");
+                       MSG_WriteByte(&buf, clc_disconnect);
+               }
                NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
                NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
                NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
@@ -723,7 +758,7 @@ void CL_AddQWCTFFlagModel(entity_t *player, int skin)
        VectorSet(flag->render.colormod, 1, 1, 1);
        // attach the flag to the player matrix
        Matrix4x4_CreateFromQuakeEntity(&flagmatrix, -f, -22, 0, 0, 0, -45, 1);
-       Matrix4x4_Concat(&flag->render.matrix, &flagmatrix, &player->render.matrix);
+       Matrix4x4_Concat(&flag->render.matrix, &player->render.matrix, &flagmatrix);
        Matrix4x4_Invert_Simple(&flag->render.inversematrix, &flag->render.matrix);
        R_LerpAnimation(&flag->render);
        CL_BoundingBoxForEntity(&flag->render);
@@ -1604,6 +1639,7 @@ int CL_ReadFromServer(void)
                        CL_RelinkStaticEntities();
                        CL_RelinkBeams();
                        CL_RelinkEffects();
+                       CL_RelinkQWNails();
                }
                else
                        csqc_frame = true;
index d7e0f0f..d100914 100644 (file)
@@ -388,6 +388,7 @@ static qboolean QW_CL_CheckOrDownloadFile(const char *filename)
        return false;
 }
 
+static void QW_CL_ProcessUserInfo(int slot);
 static void QW_CL_RequestNextDownload(void)
 {
        int i;
@@ -400,7 +401,31 @@ static void QW_CL_RequestNextDownload(void)
        case dl_single:
                break;
        case dl_skin:
-               // TODO
+               if (cls.qw_downloadnumber == 0)
+                       Con_Printf("Checking skins...\n");
+               for (;cls.qw_downloadnumber < cl.maxclients;cls.qw_downloadnumber++)
+               {
+                       if (!cl.scores[cls.qw_downloadnumber].name[0])
+                               continue;
+                       // check if we need to download the file, and return if so
+                       if (!QW_CL_CheckOrDownloadFile(va("skins/%s.pcx", cl.scores[cls.qw_downloadnumber].qw_skin)))
+                               return;
+               }
+
+               cls.qw_downloadtype = dl_none;
+
+               // load any newly downloaded skins
+               for (i = 0;i < cl.maxclients;i++)
+                       QW_CL_ProcessUserInfo(i);
+
+               // if we're still in signon stages, request the next one
+               if (cls.signon != SIGNONS)
+               {
+                       cls.signon = SIGNONS-1;
+                       // we'll go to SIGNONS when the first entity update is received
+                       MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+                       MSG_WriteString(&cls.netcon->message, va("begin %i", cl.qw_servercount));
+               }
                break;
        case dl_model:
                if (cls.qw_downloadnumber == 0)
@@ -409,7 +434,6 @@ static void QW_CL_RequestNextDownload(void)
                        cls.qw_downloadnumber = 1;
                }
 
-               cls.qw_downloadtype = dl_model;
                for (;cls.qw_downloadnumber < MAX_MODELS && cl.model_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
                {
                        // skip submodels
@@ -428,6 +452,8 @@ static void QW_CL_RequestNextDownload(void)
                                return;
                }
 
+               cls.qw_downloadtype = dl_none;
+
                // touch all of the precached models that are still loaded so we can free
                // anything that isn't needed
                Mod_ClearUsed();
@@ -452,9 +478,20 @@ static void QW_CL_RequestNextDownload(void)
                        if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL)
                                Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
 
+               // check memory integrity
+               Mem_CheckSentinelsGlobal();
+
+               // now that we have a world model, set up the world entity, renderer
+               // modules and csqc
+               cl_entities[0].render.model = cl.worldmodel = cl.model_precache[1];
+               CL_BoundingBoxForEntity(&cl_entities[0].render);
+
+               R_Modules_NewMap();
+               CL_CGVM_Start();
+
                // done checking sounds and models, send a prespawn command now
                MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
-               MSG_WriteString(&cls.netcon->message, va("prespawn %i 0 %i", cl.qw_servercount, cl.worldmodel ? cl.worldmodel->brush.qw_md4sum2 : 0));
+               MSG_WriteString(&cls.netcon->message, va("prespawn %i 0 %i", cl.qw_servercount, cl.model_precache[1]->brush.qw_md4sum2));
 
                if (cls.qw_downloadmemory)
                {
@@ -469,17 +506,15 @@ static void QW_CL_RequestNextDownload(void)
                        cls.qw_downloadnumber = 1;
                }
 
-               cls.qw_downloadtype = dl_sound;
                for (;cl.sound_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
                {
-                       // skip subsounds
-                       if (cl.sound_name[cls.qw_downloadnumber][0] == '*')
-                               continue;
                        // check if we need to download the file, and return if so
                        if (!QW_CL_CheckOrDownloadFile(va("sound/%s", cl.sound_name[cls.qw_downloadnumber])))
                                return;
                }
 
+               cls.qw_downloadtype = dl_none;
+
                // load new sounds and unload old ones
                // FIXME: S_ServerSounds does not know about cl.sfx_ sounds
                S_ServerSounds(cl.sound_name, cls.qw_downloadnumber);
@@ -500,6 +535,9 @@ static void QW_CL_RequestNextDownload(void)
                        cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, false);
                }
 
+               // check memory integrity
+               Mem_CheckSentinelsGlobal();
+
                // done with sound downloads, next we check models
                MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
                MSG_WriteString(&cls.netcon->message, va("modellist %i %i", cl.qw_servercount, 0));
@@ -512,9 +550,11 @@ static void QW_CL_RequestNextDownload(void)
 
 static void QW_CL_ParseDownload(void)
 {
-       int size = MSG_ReadShort();
+       int size = (signed short)MSG_ReadShort();
        int percent = MSG_ReadByte();
 
+       //Con_Printf("download %i %i%% (%i/%i)\n", size, percent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize);
+
        // skip the download fragment if playing a demo
        if (!cls.netcon)
        {
@@ -602,6 +642,7 @@ static void QW_CL_ParseModelList(void)
                return;
        }
 
+       cls.signon = 2;
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_model;
        QW_CL_RequestNextDownload();
@@ -636,11 +677,30 @@ static void QW_CL_ParseSoundList(void)
                return;
        }
 
+       cls.signon = 2;
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_sound;
        QW_CL_RequestNextDownload();
 }
 
+static void QW_CL_Skins_f(void)
+{
+       cls.qw_downloadnumber = 0;
+       cls.qw_downloadtype = dl_skin;
+       QW_CL_RequestNextDownload();
+}
+
+static void QW_CL_Changing_f(void)
+{
+       if (cls.qw_downloadmemory)  // don't change when downloading
+               return;
+
+       S_StopAllSounds();
+       cl.intermission = 0;
+       cls.signon = 1; // not active anymore, but not disconnected
+       Con_Printf("\nChanging map...\n");
+}
+
 void QW_CL_NextUpload(void)
 {
        int r, percent, size;
@@ -716,9 +776,11 @@ static void QW_CL_ProcessUserInfo(int slot)
        cl.scores[slot].colors = topcolor * 16 + bottomcolor;
        InfoString_GetValue(cl.scores[slot].qw_userinfo, "*spectator", temp, sizeof(temp));
        cl.scores[slot].qw_spectator = temp[0] != 0;
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "team", cl.scores[slot].qw_team, sizeof(cl.scores[slot].qw_team));
        InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin));
-       // LordHavoc: abusing Draw_CachePic for caching skins...
-       cl.scores[slot].qw_skin_cachepic = Draw_CachePic(cl.scores[slot].qw_skin, true);
+       if (!cl.scores[slot].qw_skin[0])
+               strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin));
+       // TODO: skin cache
 }
 
 static void QW_CL_UpdateUserInfo(void)
@@ -760,10 +822,13 @@ static void QW_CL_ServerInfo(void)
 {
        char key[2048];
        char value[2048];
+       char temp[32];
        strlcpy(key, MSG_ReadString(), sizeof(key));
        strlcpy(value, MSG_ReadString(), sizeof(value));
        Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
        InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value);
+       InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
+       cl.qw_teamplay = atoi(temp);
 }
 
 static void QW_CL_ParseNails(void)
@@ -772,16 +837,17 @@ static void QW_CL_ParseNails(void)
        int numnails = MSG_ReadByte();
        vec_t *v;
        unsigned char bits[6];
-       cl.qw_num_nails = 0;
        for (i = 0;i < numnails;i++)
        {
-               v = cl.qw_nails[cl.qw_num_nails++];
                for (j = 0;j < 6;j++)
                        bits[j] = MSG_ReadByte();
+               if (cl.qw_num_nails > 255)
+                       continue;
+               v = cl.qw_nails[cl.qw_num_nails++];
                v[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
                v[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
                v[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
-               v[3] = 360*(bits[4]>>4)/16;
+               v[3] = -360*(bits[4]>>4)/16;
                v[4] = 360*bits[5]/256;
                v[5] = 0;
        }
@@ -888,6 +954,10 @@ void CL_ParseServerInfo (void)
 
        Con_DPrint("Serverinfo packet received.\n");
 
+       // if server is active, we already began a loading plaque
+       if (!sv.active)
+               SCR_BeginLoadingPlaque();
+
        // check memory integrity
        Mem_CheckSentinelsGlobal();
 
@@ -910,6 +980,8 @@ void CL_ParseServerInfo (void)
        cls.protocol = protocol;
        Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol));
 
+       cl_num_entities = 1;
+
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
                cl.qw_servercount = MSG_ReadLong();
@@ -963,23 +1035,15 @@ void CL_ParseServerInfo (void)
                // check memory integrity
                Mem_CheckSentinelsGlobal();
 
-               S_StopAllSounds();
-               // if server is active, we already began a loading plaque
-               if (!sv.active)
-                       SCR_BeginLoadingPlaque();
-
-               // disable until we get textures for it
-               R_ResetSkyBox();
-
-               memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));      //[515]: csqc
-               memset(cl.model_precache, 0, sizeof(cl.model_precache));
-               memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
-
                MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
                MSG_WriteString(&cls.netcon->message, va("soundlist %i %i", cl.qw_servercount, 0));
 
                cls.state = ca_connected;
                cls.signon = 1;
+
+               // note: on QW protocol we can't set up the gameworld until after
+               // downloads finish...
+               // (we don't even know the name of the map yet)
        }
        else
        {
@@ -1006,18 +1070,6 @@ void CL_ParseServerInfo (void)
                // check memory integrity
                Mem_CheckSentinelsGlobal();
 
-               S_StopAllSounds();
-               // if server is active, we already began a loading plaque
-               if (!sv.active)
-                       SCR_BeginLoadingPlaque();
-
-               // disable until we get textures for it
-               R_ResetSkyBox();
-
-               memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));      //[515]: csqc
-               memset(cl.model_precache, 0, sizeof(cl.model_precache));
-               memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
-
                // parse model precache list
                for (nummodels=1 ; ; nummodels++)
                {
@@ -1088,32 +1140,16 @@ void CL_ParseServerInfo (void)
                for (i=1 ; i<numsounds ; i++)
                {
                        CL_KeepaliveMessage();
-
                        // Don't lock the sfx here, S_ServerSounds already did that
                        cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i], true, false);
                }
-       }
 
-       // local state
-       ent = &cl_entities[0];
-       // entire entity array was cleared, so just fill in a few fields
-       ent->state_current.active = true;
-       ent->render.model = cl.worldmodel = cl.model_precache[1];
-       ent->render.scale = 1; // some of the renderer still relies on scale
-       ent->render.alpha = 1;
-       ent->render.colormap = -1; // no special coloring
-       ent->render.flags = RENDER_SHADOW | RENDER_LIGHT;
-       Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1);
-       Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
-       CL_BoundingBoxForEntity(&ent->render);
-
-       cl_num_entities = 1;
-
-       R_Modules_NewMap();
-       CL_CGVM_Start();
-
-       // noclip is turned off at start
-       noclip_anglehack = false;
+               // we now have the worldmodel so we can set up the game world
+               ent->render.model = cl.worldmodel = cl.model_precache[1];
+               CL_BoundingBoxForEntity(&ent->render);
+               R_Modules_NewMap();
+               CL_CGVM_Start();
+       }
 
        // check memory integrity
        Mem_CheckSentinelsGlobal();
@@ -2172,6 +2208,17 @@ void CL_ParseServerMessage(void)
        {
                cl.mtime[1] = cl.mtime[0];
                cl.mtime[0] = realtime; // qw has no clock
+               cl.movement_needupdate = true;
+
+               // slightly kill qw player entities each frame
+               for (i = 1;i < cl.maxclients;i++)
+                       cl_entities_active[i] = false;
+
+               // kill all qw nails
+               cl.qw_num_nails = 0;
+
+               // fade weapon view kick
+               cl.qw_weaponkick = min(cl.qw_weaponkick + 10 * cl.frametime, 0);
 
                while (1)
                {
@@ -2236,7 +2283,7 @@ void CL_ParseServerMessage(void)
                                        CL_NextDemo();
                                else
                                        CL_Disconnect();
-                               break;
+                               return;
 
                        case qw_svc_print:
                                i = MSG_ReadByte();
@@ -2389,10 +2436,10 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case qw_svc_smallkick:
-                               Con_Printf("TODO: qw_svc_smallkick\n");
+                               cl.qw_weaponkick = -2;
                                break;
                        case qw_svc_bigkick:
-                               Con_Printf("TODO: qw_svc_bigkick\n");
+                               cl.qw_weaponkick = -4;
                                break;
 
                        case qw_svc_muzzleflash:
@@ -2476,6 +2523,12 @@ void CL_ParseServerMessage(void)
                                break;
                        }
                }
+
+               // fully kill the still slightly dead qw player entities each frame
+               for (i = 1;i < cl.maxclients;i++)
+                       if (!cl_entities_active[i])
+                               cl_entities[i].state_current.active = false;
+
                QW_CL_UpdateItemsAndWeapon();
        }
        else
@@ -2918,6 +2971,8 @@ void CL_Parse_Init(void)
 
        Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)");
        Cmd_AddCommand("stopul", QW_CL_StopUpload, "aborts current upload (screenshot for example)");
+       Cmd_AddCommand("skins", QW_CL_Skins_f, "downloads missing qw skins from server");
+       Cmd_AddCommand("changing", QW_CL_Changing_f, "sent by qw servers to tell client to wait for level change");
 }
 
 void CL_Parse_Shutdown(void)
index f4e11ba..5b5c622 100644 (file)
@@ -391,6 +391,27 @@ void SCR_DrawBrand (void)
        DrawQ_Pic (x, y, "gfx/brand", 0, 0, 1, 1, 1, 1, 0);
 }
 
+/*
+==============
+SCR_DrawDownload
+==============
+*/
+static void SCR_DrawDownload(void)
+{
+       int len;
+       float x, y;
+       float size = 8;
+       char temp[256];
+       if (!cls.qw_downloadname[0])
+               return;
+       dpsnprintf(temp, sizeof(temp), "Downloading %s ...  %3i%%\n", cls.qw_downloadname, cls.qw_downloadpercent);
+       len = strlen(temp);
+       x = (vid_conwidth.integer - len*size) / 2;
+       y = vid_conheight.integer - size;
+       DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 0.5, 0);
+       DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0);
+}
+
 //=============================================================================
 
 
@@ -1530,6 +1551,8 @@ void CL_UpdateScreen(void)
 
        SCR_DrawBrand();
 
+       SCR_DrawDownload();
+
        SCR_UpdateScreen();
 }
 
index b87be42..5b485c0 100644 (file)
--- a/client.h
+++ b/client.h
@@ -376,8 +376,8 @@ typedef struct scoreboard_s
        int             qw_ping;
        int             qw_packetloss;
        int             qw_spectator;
+       char    qw_team[8];
        char    qw_skin[MAX_QPATH];
-       cachepic_t *qw_skin_cachepic; // skins are loaded as cachepics
 } scoreboard_t;
 
 typedef struct cshift_s
@@ -712,9 +712,15 @@ typedef struct client_state_s
        // local copy of the server infostring
        char qw_serverinfo[MAX_SERVERINFO_STRING];
 
+       // time of last qw "pings" command sent to server while showing scores
+       double last_ping_request;
+
        // used during connect
        int qw_servercount;
 
+       // updated from serverinfo
+       int qw_teamplay;
+
        // indicates whether the player is spectating
        qboolean qw_spectator;
 
@@ -743,9 +749,13 @@ typedef struct client_state_s
        int qw_num_nails;
        vec_t qw_nails[255][6];
 
+       float qw_weaponkick;
+
        int qw_validsequence;
 
        qw_usercmd_t qw_moves[QW_UPDATE_BACKUP];
+
+       int qw_deltasequence[QW_UPDATE_BACKUP];
 }
 client_state_t;
 
diff --git a/cmd.c b/cmd.c
index 2af8f84..b042cd2 100644 (file)
--- a/cmd.c
+++ b/cmd.c
@@ -1036,7 +1036,10 @@ void Cmd_ForwardStringToServer (const char *s)
        // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
        // attention, it has been eradicated from here, its only (former) use in
        // all of darkplaces.
-       MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+       else
+               MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
        SZ_Write(&cls.netcon->message, (const unsigned char *)s, (int)strlen(s) + 1);
 }
 
index 570b6f4..efea485 100644 (file)
--- a/common.c
+++ b/common.c
@@ -338,7 +338,7 @@ void MSG_WriteCoord32f (sizebuf_t *sb, float f)
 
 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
 {
-       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
+       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
                MSG_WriteCoord13i (sb, f);
        else if (protocol == PROTOCOL_DARKPLACES1)
                MSG_WriteCoord32f (sb, f);
@@ -379,7 +379,7 @@ void MSG_WriteAngle32f (sizebuf_t *sb, float f)
 
 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
 {
-       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
+       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
                MSG_WriteAngle8i (sb, f);
        else
                MSG_WriteAngle16i (sb, f);
@@ -479,7 +479,7 @@ char *MSG_ReadString (void)
 {
        static char string[MAX_INPUTLINE];
        int l,c;
-       for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
+       for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
                string[l] = c;
        string[l] = 0;
        return string;
@@ -488,7 +488,7 @@ char *MSG_ReadString (void)
 int MSG_ReadBytes (int numbytes, unsigned char *out)
 {
        int l, c;
-       for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
+       for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
                out[l] = c;
        return l;
 }
@@ -510,7 +510,7 @@ float MSG_ReadCoord32f (void)
 
 float MSG_ReadCoord (protocolversion_t protocol)
 {
-       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
+       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
                return MSG_ReadCoord13i();
        else if (protocol == PROTOCOL_DARKPLACES1)
                return MSG_ReadCoord32f();
@@ -545,7 +545,7 @@ float MSG_ReadAngle32f (void)
 
 float MSG_ReadAngle (protocolversion_t protocol)
 {
-       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
+       if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
                return MSG_ReadAngle8i ();
        else
                return MSG_ReadAngle16i ();
index a329242..b0d8686 100644 (file)
@@ -371,22 +371,39 @@ This is sent just before a server changes levels
 */
 void Host_Reconnect_f (void)
 {
-       if (cmd_source == src_command)
-       {
-               Con_Print("reconnect is not valid from the console\n");
-               return;
-       }
-       if (Cmd_Argc() != 1)
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
        {
-               Con_Print("reconnect : wait for signon messages again\n");
-               return;
+               if (cls.qw_downloadmemory)  // don't change when downloading
+                       return;
+
+               S_StopAllSounds();
+
+               if (cls.netcon)
+               {
+                       if (cls.state == ca_connected && cls.signon < SIGNONS)
+                       {
+                               Con_Printf("reconnecting...\n");
+                               MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
+                               MSG_WriteString(&cls.netcon->message, "new");
+                       }
+                       else
+                               Con_Printf("Please use connect instead (reconnect not implemented)\n");
+               }
        }
-       if (!cls.signon)
+       else
        {
-               //Con_Print("reconnect: no signon, ignoring reconnect\n");
-               return;
+               if (Cmd_Argc() != 1)
+               {
+                       Con_Print("reconnect : wait for signon messages again\n");
+                       return;
+               }
+               if (!cls.signon)
+               {
+                       Con_Print("reconnect: no signon, ignoring reconnect\n");
+                       return;
+               }
+               cls.signon = 0;         // need new connection messages
        }
-       cls.signon = 0;         // need new connection messages
 }
 
 /*
@@ -2077,6 +2094,7 @@ Sent by server when serverinfo changes
 // TODO: shouldn't this be a cvar instead?
 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
 {
+       char temp[512];
        if (Cmd_Argc() != 2)
        {
                Con_Printf ("usage: fullserverinfo <complete info string>\n");
@@ -2084,6 +2102,8 @@ void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
        }
 
        strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
+       InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
+       cl.qw_teamplay = atoi(temp);
 }
 
 /*
index 0bb3246..6612978 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -456,34 +456,44 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        sendreliable = true;
                }
                // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
-               *((int *)(sendbuffer + 0)) = LittleLong(conn->qw.outgoing_sequence | (sendreliable<<31));
+               *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->qw.outgoing_sequence | ((unsigned int)sendreliable<<31));
                // last received unreliable packet number, and last received reliable packet number (0 or 1)
-               *((int *)(sendbuffer + 4)) = LittleLong(conn->qw.incoming_sequence | (conn->qw.incoming_reliable_sequence<<31));
+               *((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
                packetLen = 8;
+               conn->qw.outgoing_sequence++;
                // client sends qport in every packet
                if (conn == cls.netcon)
                {
                        *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
                        packetLen += 2;
                }
-               if (packetLen + (sendreliable ? conn->sendMessageLength : 0) + data->cursize > (int)sizeof(sendbuffer))
+               if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
                {
                        Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
                        return -1;
                }
+               // add the reliable message if there is one
                if (sendreliable)
                {
                        memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
                        packetLen += conn->sendMessageLength;
+                       conn->qw.last_reliable_sequence = conn->qw.outgoing_sequence;
+               }
+               // add the unreliable message if possible
+               if (packetLen + data->cursize <= 1400)
+               {
+                       memcpy(sendbuffer + packetLen, data->data, data->cursize);
+                       packetLen += data->cursize;
                }
-               memcpy(sendbuffer + packetLen, data->data, data->cursize);
-               packetLen += data->cursize;
-               conn->qw.outgoing_sequence++;
 
                NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
                packetsSent++;
                unreliableMessagesSent++;
+
+               // delay later packets to obey rate limit
+               conn->qw.cleartime = max(conn->qw.cleartime, realtime) + packetLen * conn->qw.rate;
+
                return 0;
        }
        else
diff --git a/progs.h b/progs.h
index 63f4057..7005dee 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -53,7 +53,7 @@ typedef struct edict_engineprivate_s
        // we should avoid extensive checking on entities already encountered
        int areagridmarknumber;
 
-       // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE
+       // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE, PROTOCOL_QUAKEWORLD
        // baseline values
        entity_state_t baseline;
 
@@ -186,7 +186,7 @@ typedef struct edict_engineprivate_s
        // we should avoid extensive checking on entities already encountered
        int areagridmarknumber;
 
-       // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE
+       // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE, PROTOCOL_QUAKEWORLD
        // baseline values
        entity_state_t baseline;
 
index 69b1c52..91e0b7c 100644 (file)
@@ -2340,6 +2340,44 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num
 }
 
 
+static int QW_TranslateEffects(int qweffects, int number)
+{
+       int effects = 0;
+       if (qweffects & QW_EF_BRIGHTFIELD)
+               effects |= EF_BRIGHTFIELD;
+       if (qweffects & QW_EF_MUZZLEFLASH)
+               effects |= EF_MUZZLEFLASH;
+       if (qweffects & QW_EF_FLAG1)
+       {
+               // mimic FTEQW's interpretation of EF_FLAG1 as EF_NODRAW on non-player entities
+               if (number > cl.maxclients)
+                       effects |= EF_NODRAW;
+               else
+                       effects |= EF_FLAG1QW;
+       }
+       if (qweffects & QW_EF_FLAG2)
+       {
+               // mimic FTEQW's interpretation of EF_FLAG2 as EF_ADDITIVE on non-player entities
+               if (number > cl.maxclients)
+                       effects |= EF_ADDITIVE;
+               else
+                       effects |= EF_FLAG2QW;
+       }
+       if (qweffects & QW_EF_RED)
+       {
+               if (qweffects & QW_EF_BLUE)
+                       effects |= EF_RED | EF_BLUE;
+               else
+                       effects |= EF_RED;
+       }
+       else if (qweffects & QW_EF_BLUE)
+               effects |= EF_BLUE;
+       else if (qweffects & QW_EF_BRIGHTLIGHT)
+               effects |= EF_BRIGHTLIGHT;
+       else if (qweffects & QW_EF_DIMLIGHT)
+               effects |= EF_DIMLIGHT;
+       return effects;
+}
 
 void EntityStateQW_ReadPlayerUpdate(void)
 {
@@ -2362,9 +2400,14 @@ void EntityStateQW_ReadPlayerUpdate(void)
        s = &ent->state_current;
        *s = defaultstate;
        s->active = true;
+       s->colormap = enumber;
        playerflags = MSG_ReadShort();
        MSG_ReadVector(s->origin, cls.protocol);
        s->frame = MSG_ReadByte();
+
+       VectorClear(viewangles);
+       VectorClear(velocity);
+
        if (playerflags & QW_PF_MSEC)
        {
                // time difference between last update this player sent to the server,
@@ -2379,23 +2422,23 @@ void EntityStateQW_ReadPlayerUpdate(void)
        {
                bits = MSG_ReadByte();
                if (bits & QW_CM_ANGLE1)
-                       viewangles[0] = MSG_ReadAngle16i();
+                       viewangles[0] = MSG_ReadAngle16i(); // cmd->angles[0]
                if (bits & QW_CM_ANGLE2)
-                       viewangles[1] = MSG_ReadAngle16i();
+                       viewangles[1] = MSG_ReadAngle16i(); // cmd->angles[1]
                if (bits & QW_CM_ANGLE3)
-                       viewangles[2] = MSG_ReadAngle16i();
+                       viewangles[2] = MSG_ReadAngle16i(); // cmd->angles[2]
                if (bits & QW_CM_FORWARD)
-                       MSG_ReadShort();
+                       MSG_ReadShort(); // cmd->forwardmove
                if (bits & QW_CM_SIDE)
-                       MSG_ReadShort();
+                       MSG_ReadShort(); // cmd->sidemove
                if (bits & QW_CM_UP)
-                       MSG_ReadShort();
+                       MSG_ReadShort(); // cmd->upmove
                if (bits & QW_CM_BUTTONS)
-                       MSG_ReadByte();
+                       MSG_ReadByte(); // cmd->buttons
                if (bits & QW_CM_IMPULSE)
-                       MSG_ReadByte();
+                       MSG_ReadByte(); // cmd->impulse
+               MSG_ReadByte(); // cmd->msec
        }
-       VectorClear(velocity);
        if (playerflags & QW_PF_VELOCITY1)
                velocity[0] = MSG_ReadShort();
        if (playerflags & QW_PF_VELOCITY2)
@@ -2409,9 +2452,17 @@ void EntityStateQW_ReadPlayerUpdate(void)
        if (playerflags & QW_PF_SKINNUM)
                s->skin = MSG_ReadByte();
        if (playerflags & QW_PF_EFFECTS)
-               s->effects = MSG_ReadByte();
+               s->effects = QW_TranslateEffects(MSG_ReadByte(), enumber);
        if (playerflags & QW_PF_WEAPONFRAME)
                weaponframe = MSG_ReadByte();
+       else
+               weaponframe = 0;
+
+       if (enumber == cl.playerentity)
+       {
+               // if this is an update on our player, update the angles
+               VectorCopy(cl.viewangles, viewangles);
+       }
 
        // calculate the entity angles from the viewangles
        s->angles[0] = viewangles[0] * -0.0333;
@@ -2441,6 +2492,12 @@ void EntityStateQW_ReadPlayerUpdate(void)
 
                VectorCopy(velocity, cl.mvelocity[0]);
                cl.stats[STAT_WEAPONFRAME] = weaponframe;
+               if (playerflags & QW_PF_GIB)
+                       cl.stats[STAT_VIEWHEIGHT] = 8;
+               else if (playerflags & QW_PF_DEAD)
+                       cl.stats[STAT_VIEWHEIGHT] = -16;
+               else
+                       cl.stats[STAT_VIEWHEIGHT] = 22;
        }
 
        // set the cl_entities_active flag
@@ -2474,43 +2531,7 @@ static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits)
        if (bits & QW_U_SKIN)
                s->skin = MSG_ReadByte();
        if (bits & QW_U_EFFECTS)
-       {
-               s->effects = 0;
-               qweffects = MSG_ReadByte();
-               if (qweffects & QW_EF_BRIGHTFIELD)
-                       s->effects |= EF_BRIGHTFIELD;
-               if (qweffects & QW_EF_MUZZLEFLASH)
-                       s->effects |= EF_MUZZLEFLASH;
-               if (qweffects & QW_EF_FLAG1)
-               {
-                       // mimic FTEQW's interpretation of EF_FLAG1 as EF_NODRAW on non-player entities
-                       if (s->number > cl.maxclients)
-                               s->effects |= EF_NODRAW;
-                       else
-                               s->effects |= EF_FLAG1QW;
-               }
-               if (qweffects & QW_EF_FLAG2)
-               {
-                       // mimic FTEQW's interpretation of EF_FLAG2 as EF_ADDITIVE on non-player entities
-                       if (s->number > cl.maxclients)
-                               s->effects |= EF_ADDITIVE;
-                       else
-                               s->effects |= EF_FLAG2QW;
-               }
-               if (qweffects & QW_EF_RED)
-               {
-                       if (qweffects & QW_EF_BLUE)
-                               s->effects |= EF_RED | EF_BLUE;
-                       else
-                               s->effects |= EF_RED;
-               }
-               else if (qweffects & QW_EF_BLUE)
-                       s->effects |= EF_BLUE;
-               else if (qweffects & QW_EF_BRIGHTLIGHT)
-                       s->effects |= EF_BRIGHTLIGHT;
-               else if (qweffects & QW_EF_DIMLIGHT)
-                       s->effects |= EF_DIMLIGHT;
-       }
+               s->effects = QW_TranslateEffects(qweffects = MSG_ReadByte(), s->number);
        if (bits & QW_U_ORIGIN1)
                s->origin[0] = MSG_ReadCoord13i();
        if (bits & QW_U_ANGLE1)
@@ -2538,49 +2559,70 @@ static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits)
                if (bits & QW_U_EFFECTS)
                        Con_Printf(" U_EFFECTS %i", qweffects);
                if (bits & QW_U_ORIGIN1)
-                       Con_Printf(" U_ORIGIN1 %i", s->origin[0]);
+                       Con_Printf(" U_ORIGIN1 %f", s->origin[0]);
                if (bits & QW_U_ANGLE1)
-                       Con_Printf(" U_ANGLE1 %i", s->angles[0]);
+                       Con_Printf(" U_ANGLE1 %f", s->angles[0]);
                if (bits & QW_U_ORIGIN2)
-                       Con_Printf(" U_ORIGIN2 %i", s->origin[1]);
+                       Con_Printf(" U_ORIGIN2 %f", s->origin[1]);
                if (bits & QW_U_ANGLE2)
-                       Con_Printf(" U_ANGLE2 %i", s->angles[1]);
+                       Con_Printf(" U_ANGLE2 %f", s->angles[1]);
                if (bits & QW_U_ORIGIN3)
-                       Con_Printf(" U_ORIGIN3 %i", s->origin[2]);
+                       Con_Printf(" U_ORIGIN3 %f", s->origin[2]);
                if (bits & QW_U_ANGLE3)
-                       Con_Printf(" U_ANGLE3 %i", s->angles[2]);
+                       Con_Printf(" U_ANGLE3 %f", s->angles[2]);
                if (bits & QW_U_SOLID)
                        Con_Printf(" U_SOLID");
                Con_Print("\n");
        }
 }
 
+entityframeqw_database_t *EntityFrameQW_AllocDatabase(mempool_t *pool)
+{
+       entityframeqw_database_t *d;
+       d = (entityframeqw_database_t *)Mem_Alloc(pool, sizeof(*d));
+       return d;
+}
+
+void EntityFrameQW_FreeDatabase(entityframeqw_database_t *d)
+{
+       Mem_Free(d);
+}
+
 void EntityFrameQW_CL_ReadFrame(qboolean delta)
 {
        qboolean invalid = false;
        int number, oldsnapindex, newsnapindex, oldindex, newindex, oldnum, newnum;
        entity_t *ent;
-       entityframeqw_database_t *d = cl.entitydatabaseqw;
+       entityframeqw_database_t *d;
        entityframeqw_snapshot_t *oldsnap, *newsnap;
 
+       if (!cl.entitydatabaseqw)
+               cl.entitydatabaseqw = EntityFrameQW_AllocDatabase(cl_mempool);
+       d = cl.entitydatabaseqw;
+
        newsnapindex = cls.netcon->qw.incoming_sequence & QW_UPDATE_MASK;
        newsnap = d->snapshot + newsnapindex;
        memset(newsnap, 0, sizeof(*newsnap));
+       oldsnapindex = -1;
+       oldsnap = NULL;
        if (delta)
        {
-               oldsnapindex = MSG_ReadByte() & QW_UPDATE_MASK;
-               oldsnap = d->snapshot + oldsnapindex;
-               if (cls.netcon->qw.outgoing_sequence - oldsnapindex >= QW_UPDATE_BACKUP-1)
+               number = MSG_ReadByte();
+               oldsnapindex = cl.qw_deltasequence[newsnapindex];
+               if ((number & QW_UPDATE_MASK) != (oldsnapindex & QW_UPDATE_MASK))
+                       Con_DPrintf("WARNING: from mismatch\n");
+               if (oldsnapindex != -1)
                {
-                       Con_DPrintf("delta update too old\n");
-                       newsnap->invalid = invalid = true; // too old
-                       delta = false;
+                       if (cls.netcon->qw.outgoing_sequence - oldsnapindex >= QW_UPDATE_BACKUP-1)
+                       {
+                               Con_DPrintf("delta update too old\n");
+                               newsnap->invalid = invalid = true; // too old
+                               delta = false;
+                       }
+                       oldsnap = d->snapshot + (oldsnapindex & QW_UPDATE_MASK);
                }
-       }
-       else
-       {
-               oldsnapindex = -1;
-               oldsnap = NULL;
+               else
+                       delta = false;
        }
 
        // read the number of this frame to echo back in next input packet
@@ -2638,10 +2680,13 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta)
                {
                        if (newsnap->num_entities >= QW_MAX_PACKET_ENTITIES)
                                Host_Error("EntityFrameQW_CL_ReadFrame: newsnap->num_entities == MAX_PACKETENTITIES");
-                       newsnap->entities[newsnap->num_entities] = (newnum == oldnum) ? oldsnap->entities[oldindex++] : cl_entities[newnum].state_baseline;
+                       newsnap->entities[newsnap->num_entities] = (newnum == oldnum) ? oldsnap->entities[oldindex] : cl_entities[newnum].state_baseline;
                        EntityStateQW_ReadEntityUpdate(newsnap->entities + newsnap->num_entities, word);
                        newsnap->num_entities++;
                }
+
+               if (newnum == oldnum)
+                       oldindex++;
        }
 
        // expand cl_num_entities to include every entity we've seen this game
@@ -2653,8 +2698,8 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta)
                        CL_ExpandEntities(newnum);
        }
 
-       // now update the entities from the snapshot states
-       number = 1;
+       // now update the non-player entities from the snapshot states
+       number = cl.maxclients + 1;
        for (newindex = 0;;newindex++)
        {
                newnum = newindex >= newsnap->num_entities ? cl_num_entities : newsnap->entities[newindex].number;
@@ -2673,6 +2718,7 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta)
                ent = &cl_entities[number];
                ent->state_previous = ent->state_current;
                ent->state_current = newsnap->entities[newindex];
+               ent->state_current.time = cl.mtime[0];
                CL_MoveLerpEntityStates(ent);
                // the entity lives again...
                cl_entities_active[number] = true;
index e92337b..46a9ea5 100644 (file)
@@ -931,6 +931,8 @@ typedef struct entityframeqw_database_s
 }
 entityframeqw_database_t;
 
+entityframeqw_database_t *EntityFrameQW_AllocDatabase(mempool_t *pool);
+void EntityFrameQW_FreeDatabase(entityframeqw_database_t *d);
 void EntityStateQW_ReadPlayerUpdate(void);
 void EntityFrameQW_CL_ReadFrame(qboolean delta);
 
diff --git a/sbar.c b/sbar.c
index f2d1da5..79b0cf9 100644 (file)
--- a/sbar.c
+++ b/sbar.c
@@ -564,7 +564,7 @@ void Sbar_SortFrags (void)
                                        strcpy(teams[teamlines-1].name, "^3Yellow Team");
                                else
                                        strcpy(teams[teamlines-1].name, "Total Team Score");
-                               
+
                                teams[teamlines-1].frags = 0;
                                teams[teamlines-1].colors = color + 16 * color;
                        }
@@ -1391,16 +1391,31 @@ Sbar_DeathmatchOverlay
 */
 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
 {
+       int minutes;
        unsigned char *c;
-       // draw colors behind score
-       c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8];
-       DrawQ_Fill(x + 8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
-       c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8];
-       DrawQ_Fill(x + 8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
-       // print the text
-       //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
-       // FIXME: use a constant for this color tag instead
-       DrawQ_ColoredString(x, y, va("%c%4i %s" STRING_COLOR_DEFAULT_STR, (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL );
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+       {
+               // draw colors behind score
+               c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8];
+               DrawQ_Fill(x + 14*8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
+               c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8];
+               DrawQ_Fill(x + 14*8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
+               // print the text
+               //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
+               DrawQ_ColoredString(x, y, va("%c%4i %2i %4i %4i %-4s %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL );
+       }
+       else
+       {
+               minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : realtime - s->qw_entertime) / 60.0);
+               // draw colors behind score
+               c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8];
+               DrawQ_Fill(x + 1*8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
+               c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8];
+               DrawQ_Fill(x + 1*8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0);
+               // print the text
+               //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
+               DrawQ_ColoredString(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL );
+       }
        return 8;
 }
 
@@ -1409,13 +1424,27 @@ void Sbar_DeathmatchOverlay (void)
        int i, x, y;
        cachepic_t *pic;
 
+       // request new ping times every two second
+       if (cl.last_ping_request < realtime - 2)
+       {
+               cl.last_ping_request = realtime;
+               if (cls.protocol == PROTOCOL_QUAKEWORLD)
+               {
+                       MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+                       MSG_WriteString(&cls.netcon->message, "pings");
+               }
+       }
+
        pic = Draw_CachePic ("gfx/ranking", true);
        DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, 8, "gfx/ranking", 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
 
        // scores
        Sbar_SortFrags ();
        // draw the text
-       x = (vid_conwidth.integer - (6 + 15) * 8) / 2;
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+               x = (vid_conwidth.integer - (6 + 17 + 15) * 8) / 2;
+       else
+               x = (vid_conwidth.integer - (6 + 15) * 8) / 2;
        y = 40;
 
        if (Sbar_IsTeammatch ())
index 966c055..e152d42 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -815,7 +815,7 @@ void SV_ReadClientMessage(void)
                        return;
                }
 
-               cmd = MSG_ReadChar ();
+               cmd = MSG_ReadByte ();
                if (cmd == -1)
                {
                        // end of message
diff --git a/view.c b/view.c
index be9bea7..2a9629c 100644 (file)
--- a/view.c
+++ b/view.c
@@ -334,10 +334,14 @@ void V_CalcRefdef (void)
                if (cl.intermission)
                {
                        // entity is a fixed camera, just copy the matrix
-                       Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix);
-                       Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix);
-                       r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
-                       viewmodelmatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
+                       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+                               Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1);
+                       else
+                       {
+                               r_refdef.viewentitymatrix = ent->render.matrix;
+                               r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
+                       }
+                       viewmodelmatrix = r_refdef.viewentitymatrix;
                }
                else
                {
@@ -346,6 +350,10 @@ void V_CalcRefdef (void)
                        Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
                        VectorCopy(cl.viewangles, viewangles);
 
+                       // apply qw weapon recoil effect (this did not work in QW)
+                       // TODO: add a cvar to disable this
+                       viewangles[PITCH] += cl.qw_weaponkick;
+
                        if (cl.onground)
                        {
                                if (!cl.oldonground)