diff --git a/engine/Makefile b/engine/Makefile index 8f00eb22f..3267ea4be 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -1368,7 +1368,8 @@ endif # This is for linking the FTE icon to the MinGW target $(OUT_DIR)/resources.o : winquake.rc @$(WINDRES) $(BRANDFLAGS) -I$(CLIENT_DIR) -O coff $< $@ - +$(OUT_DIR)/fteqcc.o : fteqcc.rc + @$(WINDRES) $(BRANDFLAGS) -I$(PROGS_DIR) -O coff $< $@ #npAPI stuff requires some extra resources $(OUT_DIR)/npplug.o : ftequake/npplug.rc @$(WINDRES) $(BRANDFLAGS) -I$(CLIENT_DIR) -O coff $< $@ @@ -1553,11 +1554,11 @@ _qcc-tmp: $(REQDIR) qcc-rel: @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qcctui.o" qccgui-rel: - @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" + @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" qcc-dbg: @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqcc" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qcctui.o" qccgui-dbg: - @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqccgui" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" + @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqccgui" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" #scintilla is messy as fuck when building statically. but at least we can strip out the lexers we don't use this way. diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 4132a14cc..18e0464b3 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1456,8 +1456,13 @@ qboolean CLQ2_SendCmd (sizebuf_t *buf) MSG_WriteByte (buf, clcq2_move); // save the position for a checksum qbyte - checksumIndex = buf->cursize; - MSG_WriteByte (buf, 0); + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + checksumIndex = -1; + else + { + checksumIndex = buf->cursize; + MSG_WriteByte (buf, 0); + } if (!cl.q2frame.valid || cl_nodelta.ival) MSG_WriteLong (buf, -1); // no compression @@ -1484,11 +1489,14 @@ qboolean CLQ2_SendCmd (sizebuf_t *buf) // calculate a checksum over the move commands dontdrop = CL_WriteDeltas(0, buf); - buf->data[checksumIndex] = Q2COM_BlockSequenceCRCByte( - buf->data + checksumIndex + 1, buf->cursize - checksumIndex - 1, - seq_hash); + if (checksumIndex >= 0) + { + buf->data[checksumIndex] = Q2COM_BlockSequenceCRCByte( + buf->data + checksumIndex + 1, buf->cursize - checksumIndex - 1, + seq_hash); + } - if (cl.sendprespawn) + if (cl.sendprespawn || !cls.protocol_q2) buf->cursize = 0; //tastyspleen.net is alergic. return dontdrop; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 9a4d4cf5f..e10300973 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -242,7 +242,11 @@ static struct netadr_t adr; //address that we're trying to transfer to. int mtu; unsigned int compresscrc; - int protocol; //tracked as part of guesswork based upon what replies we get. + int protocol; //nq/qw/q2/q3. guessed based upon server replies + int subprotocol; //the monkeys are trying to eat me. + unsigned int fteext1; + unsigned int fteext2; + int qport; int challenge; //tracked as part of guesswork based upon what replies we get. double time; //for connection retransmits int defaultport; @@ -541,8 +545,8 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, fteprotextsupported = 0; #endif - cls.fteprotocolextensions = fteprotextsupported; - cls.fteprotocolextensions2 = fteprotextsupported2; + connectinfo.fteext1 = fteprotextsupported; + connectinfo.fteext2 = fteprotextsupported2; #endif t1 = Sys_DoubleTime (); @@ -574,8 +578,12 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, connectinfo.time = realtime+t2-t1; // for retransmit requests - cls.qport = qport.value; - Cvar_SetValue(&qport, (cls.qport+1)&0xffff); + //fixme: we shouldn't cycle these so much + connectinfo.qport = qport.value; + Cvar_SetValue(&qport, (connectinfo.qport+1)&0xffff); + + if (connectinfo.protocol == CP_QUAKE2 && (connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2 || connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO)) + connectinfo.qport &= 0xff; // Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING); @@ -601,8 +609,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, #ifdef Q3CLIENT if (connectinfo.protocol == CP_QUAKE3) { //q3 requires some very strange things. - cls.challenge = connectinfo.challenge; - CLQ3_SendConnectPacket(to); + CLQ3_SendConnectPacket(to, connectinfo.challenge, connectinfo.qport); return; } #endif @@ -612,15 +619,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, if (clients>1) //splitscreen 'connect' command specifies the number of userinfos sent. Q_strncatz(data, va("%i", clients), sizeof(data)); -#ifdef Q2CLIENT - if (connectinfo.protocol == CP_QUAKE2) - Q_strncatz(data, va(" %i", PROTOCOL_VERSION_Q2), sizeof(data)); - else -#endif - Q_strncatz(data, va(" %i", PROTOCOL_VERSION_QW), sizeof(data)); - - - Q_strncatz(data, va(" %i %i", cls.qport, connectinfo.challenge), sizeof(data)); + Q_strncatz(data, va(" %i %i %i", connectinfo.subprotocol, connectinfo.qport, connectinfo.challenge), sizeof(data)); //userinfo0 has some twiddles for extensions from other qw engines. Q_strncatz(data, " \"", sizeof(data)); @@ -638,9 +637,12 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_strncatz(data, "\"", sizeof(data)); for (c = 1; c < clients; c++) - { Q_strncatz(data, va(" \"%s\"", cls.userinfo[c]), sizeof(data)); - } + + if (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2) + Q_strncatz(data, va(" %d %d", mtu, 1905), sizeof(data)); //mti, sub-sub-version + else if (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO) + Q_strncatz(data, va(" %d 0 0 %d", mtu, 1021), sizeof(data)); //mtu, netchan-fragmentation, zlib, sub-sub-version Q_strncatz(data, "\n", sizeof(data)); @@ -681,8 +683,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_strncatz(data, va("0x%x \"%s\"\n", PROTOCOL_INFO_GUID, info), sizeof(data)); NET_SendPacket (NS_CLIENT, strlen(data), data, to); - - cl.splitclients = 0; } char *CL_TryingToConnect(void) @@ -718,9 +718,7 @@ void CL_CheckForResend (void) if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) { extern cvar_t dpcompat_nopreparse; - unsigned int pext1, pext2; - pext1 = 0; - pext2 = 0; + memset(&connectinfo, 0, sizeof(connectinfo)); connectinfo.trying = true; connectinfo.istransfer = false; @@ -735,28 +733,29 @@ void CL_CheckForResend (void) { #ifdef Q3CLIENT case GT_QUAKE3: - pext1 = 0; - pext2 = 0; - cls.protocol = CP_QUAKE3; + connectinfo.protocol = CP_QUAKE3; break; #endif #ifdef Q2CLIENT case GT_QUAKE2: - pext1 = 0; - pext2 = 0; - cls.protocol = CP_QUAKE2; + connectinfo.protocol = CP_QUAKE2; + connectinfo.subprotocol = PROTOCOL_VERSION_Q2; break; #endif default: cl.movesequence = 0; if (!strcmp(cl_loopbackprotocol.string, "qw")) { //qw with all supported extensions -default - pext1 = Net_PextMask(1, false); - pext2 = Net_PextMask(2, false); - cls.protocol = CP_QUAKEWORLD; + connectinfo.fteext1 = Net_PextMask(1, false); + connectinfo.fteext2 = Net_PextMask(2, false); + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; } else if (!strcmp(cl_loopbackprotocol.string, "qwid") || !strcmp(cl_loopbackprotocol.string, "idqw")) - cls.protocol = CP_QUAKEWORLD; + { + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + } #ifdef Q3CLIENT else if (!strcmp(cl_loopbackprotocol.string, "q3")) cls.protocol = CP_QUAKE3; @@ -766,53 +765,55 @@ void CL_CheckForResend (void) { //for debugging. if (rand() & 1) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_FITZ666; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_FITZ666; } else { - cls.protocol = CP_QUAKEWORLD; - pext1 = Net_PextMask(1, false); - pext2 = Net_PextMask(2, false); + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + connectinfo.fteext1 = Net_PextMask(1, false); + connectinfo.fteext2 = Net_PextMask(2, false); } } else if (!strcmp(cl_loopbackprotocol.string, "fitz") || !strcmp(cl_loopbackprotocol.string, "666") || !strcmp(cl_loopbackprotocol.string, "999")) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_FITZ666; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_FITZ666; } else if (!strcmp(cl_loopbackprotocol.string, "nq")) //actually proquake, because we might as well use the extra angles { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_PROQUAKE3_4; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_PROQUAKE3_4; } else if (!strcmp(cl_loopbackprotocol.string, "nqid") || !strcmp(cl_loopbackprotocol.string, "idnq")) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_ID; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_ID; } else if (!strcmp(cl_loopbackprotocol.string, "dp6") || !strcmp(cl_loopbackprotocol.string, "dpp6")) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_DP6; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_DP6; } else if (!strcmp(cl_loopbackprotocol.string, "dp7") || !strcmp(cl_loopbackprotocol.string, "dpp7")) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_DP7; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_DP7; } else if (progstype != PROG_QW && progstype != PROG_H2) //h2 depends on various extensions and doesn't really match either protocol, but we go for qw because that gives us all sorts of extensions. { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_FITZ666; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_FITZ666; //FIXME: pext } #endif else { //protocol wasn't recognised, and we didn't take the nq fallback, so that must mean we're going for qw. - cls.protocol = CP_QUAKEWORLD; - pext1 = Net_PextMask(1, false); - pext2 = Net_PextMask(2, false); + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + connectinfo.fteext1 = Net_PextMask(1, false); + connectinfo.fteext2 = Net_PextMask(2, false); } #ifdef NETPREPARSE @@ -824,28 +825,30 @@ void CL_CheckForResend (void) Con_Printf("dpcompat_nopreparse is unsupported with hexen2\n"); else if (progstype == PROG_QW && cls.protocol != CP_QUAKEWORLD) { - cls.protocol = CP_QUAKEWORLD; - pext1 = Net_PextMask(1, false); - pext2 = Net_PextMask(2, false); + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + connectinfo.fteext1 = Net_PextMask(1, false); + connectinfo.fteext2 = Net_PextMask(2, false); } else if (progstype != PROG_QW && cls.protocol == CP_QUAKEWORLD) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_DP7; //dpcompat_nopreparse is only really needed for DP mods that send unknowable svc_tempentity messages to the client. + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_DP7; //dpcompat_nopreparse is only really needed for DP mods that send unknowable svc_tempentity messages to the client. } } //make sure the protocol within demos is actually correct/sane if (cls.demorecording == 1 && cls.protocol != CP_QUAKEWORLD) { - cls.protocol = CP_QUAKEWORLD; - pext1 = Net_PextMask(1, false); - pext2 = Net_PextMask(2, false); + connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + connectinfo.fteext1 = Net_PextMask(1, false); + connectinfo.fteext2 = Net_PextMask(2, false); } else if (cls.demorecording == 2 && cls.protocol != CP_NETQUAKE) { - cls.protocol = CP_NETQUAKE; - cls.protocol_nq = CPNQ_FITZ666; + connectinfo.protocol = CP_NETQUAKE; + connectinfo.subprotocol = CPNQ_FITZ666; //FIXME: use pext. } break; @@ -853,7 +856,6 @@ void CL_CheckForResend (void) CL_FlushClientCommands(); //clear away all client->server clientcommands. - connectinfo.protocol = cls.protocol; #ifdef NQPROT if (connectinfo.protocol == CP_NETQUAKE) { @@ -873,21 +875,21 @@ void CL_CheckForResend (void) net_message.packing = SZ_RAWBYTES; net_message.cursize = 0; - if (cls.protocol_nq == CPNQ_ID) + if (connectinfo.subprotocol == CPNQ_ID) { net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } - else if (cls.protocol_nq == CPNQ_FITZ666) + else if (connectinfo.subprotocol == CPNQ_FITZ666) { net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\666\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } - else if (cls.protocol_nq == CPNQ_PROQUAKE3_4) + else if (connectinfo.subprotocol == CPNQ_PROQUAKE3_4) { net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\1\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); @@ -903,8 +905,7 @@ void CL_CheckForResend (void) { if (!connectinfo.challenge) connectinfo.challenge = rand(); - cls.challenge = connectinfo.challenge; - CL_SendConnectPacket (NULL, 8192-16, pext1, pext2, false); + CL_SendConnectPacket (NULL, 8192-16, connectinfo.fteext1, connectinfo.fteext2, false); } return; } @@ -2797,7 +2798,10 @@ void CL_ConnectionlessPacket (void) #ifdef Q2CLIENT if (connectinfo.protocol == CP_QUAKE2 || connectinfo.protocol == CP_UNKNOWN) + { connectinfo.protocol = CP_QUAKE2; + connectinfo.subprotocol = PROTOCOL_VERSION_Q2; + } else { Con_Printf("\nChallenge from another protocol, ignoring Q2 challenge\n"); @@ -2812,7 +2816,10 @@ void CL_ConnectionlessPacket (void) /*no idea, assume a QuakeWorld challenge response ('c' packet)*/ else if (connectinfo.protocol == CP_QUAKEWORLD || connectinfo.protocol == CP_UNKNOWN) + { connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; + } else { Con_Printf("\nChallenge from another protocol, ignoring QW challenge\n"); @@ -2825,7 +2832,30 @@ void CL_ConnectionlessPacket (void) lasttime = curtime; lastadr = net_from; - connectinfo.challenge = atoi(s); + s = COM_Parse(s); + connectinfo.challenge = atoi(com_token); + + while((s = COM_Parse(s))) + { + if (connectinfo.protocol == CP_QUAKE2 && !strncmp(com_token, "p=", 2)) + { + char *p = com_token+2; + do + { + switch(strtoul(p, &p, 0)) + { + case PROTOCOL_VERSION_R1Q2: + if (connectinfo.subprotocol < PROTOCOL_VERSION_R1Q2) + connectinfo.subprotocol = PROTOCOL_VERSION_R1Q2; + break; + case PROTOCOL_VERSION_Q2PRO: + if (connectinfo.subprotocol < PROTOCOL_VERSION_Q2PRO) + connectinfo.subprotocol = PROTOCOL_VERSION_Q2PRO; + break; + } + } while (*p++ == ','); + } + } for(;;) { @@ -2917,7 +2947,7 @@ void CL_ConnectionlessPacket (void) { Con_Printf ("accept\n"); Validation_Apply_Ruleset(); - Netchan_Setup(NS_CLIENT, &cls.netchan, &net_from, cls.qport); + Netchan_Setup(NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); CL_ParseEstablished(); Con_DPrintf ("CL_EstablishConnection: connected to %s\n", cls.servername); @@ -2986,6 +3016,7 @@ void CL_ConnectionlessPacket (void) if (c == S2C_CONNECTION) { connectinfo.protocol = CP_QUAKEWORLD; + connectinfo.subprotocol = PROTOCOL_VERSION_QW; #if defined(Q2CLIENT) || defined(Q3CLIENT) client_connect: //fixme: make function #endif @@ -3018,9 +3049,18 @@ client_connect: //fixme: make function } } connectinfo.trying = false; + cl.splitclients = 0; cls.protocol = connectinfo.protocol; + cls.fteprotocolextensions = connectinfo.fteext1; + cls.fteprotocolextensions2 = connectinfo.fteext2; cls.challenge = connectinfo.challenge; - Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, cls.qport); + Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); + if (cls.protocol == CP_QUAKE2) + { + cls.protocol_q2 = connectinfo.subprotocol; + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + cls.netchan.qportsize = 1; + } cls.netchan.fragmentsize = connectinfo.mtu; if (connectinfo.mtu >= 64) cls.netchan.message.maxsize = sizeof(cls.netchan.message_buf); @@ -3184,7 +3224,9 @@ void CLNQ_ConnectionlessPacket(void) Validation_Apply_Ruleset(); - Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, cls.qport); + cls.fteprotocolextensions = connectinfo.fteext1; + cls.fteprotocolextensions2 = connectinfo.fteext2; + Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); CL_ParseEstablished(); cls.netchan.isnqprotocol = true; cls.netchan.compresstable = NULL; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 7d93ded3e..84b0b3053 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -3102,9 +3102,9 @@ void CLQ2_ParseServerData (void) int svcnt; // int cflag; + memset(&cls.netchan.netprim, 0, sizeof(cls.netchan.netprim)); cls.netchan.netprim.coordsize = 2; cls.netchan.netprim.anglesize = 1; - MSG_ChangePrimitives(cls.netchan.netprim); Con_DPrintf ("Serverdata packet received.\n"); // @@ -3119,8 +3119,12 @@ void CLQ2_ParseServerData (void) i = MSG_ReadLong (); cls.protocol_q2 = i; - if (i > PROTOCOL_VERSION_Q2 || i < (cls.demoplayback?PROTOCOL_VERSION_Q2_DEMO_MIN:PROTOCOL_VERSION_Q2_MIN)) - Host_EndGame ("Server returned version %i, not %i", i, PROTOCOL_VERSION_Q2); + if (i == PROTOCOL_VERSION_R1Q2) + Con_DPrintf("Using R1Q2 protocol\n"); + else if (i == PROTOCOL_VERSION_Q2PRO) + Con_DPrintf("Using Q2PRO protocol\n"); + else if (i > PROTOCOL_VERSION_Q2 || i < (cls.demoplayback?PROTOCOL_VERSION_Q2_DEMO_MIN:PROTOCOL_VERSION_Q2_MIN)) + Host_EndGame ("Q2 Server returned version %i, not %i", i, PROTOCOL_VERSION_Q2); svcnt = MSG_ReadLong (); /*cl.attractloop =*/ MSG_ReadByte (); @@ -3160,6 +3164,40 @@ void CLQ2_ParseServerData (void) str = MSG_ReadString (); Q_strncpyz (cl.levelname, str, sizeof(cl.levelname)); + + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2) + { + unsigned short r1q2ver; + qboolean isenhanced = MSG_ReadByte(); + if (isenhanced) + Host_EndGame ("R1Q2 server is running an unsupported mod"); + r1q2ver = MSG_ReadShort(); //protocol version... limit... yeah, buggy. + if (r1q2ver < 1903 || r1q2ver > 1905) + Host_EndGame ("R1Q2 server version %i not supported", r1q2ver); + MSG_ReadByte(); //'used to be advanced deltas' + MSG_ReadByte(); //strafejump hack + + if (r1q2ver >= 1905) + cls.netchan.netprim.q2flags |= NPQ2_SIZE32; + } + else if (cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + { + unsigned short q2prover = MSG_ReadShort(); //q2pro protocol version + if (q2prover < 1011 || q2prover > 1021) + Host_EndGame ("Q2PRO server version %i not supported", q2prover); + MSG_ReadByte(); //server state (ie: demo playback vs actual game) + MSG_ReadByte(); //strafejump hack + MSG_ReadByte(); //q2pro qw-mode. kinda silly for us tbh. + if (q2prover >= 1014) + cls.netchan.netprim.q2flags |= NPQ2_SIZE32; + if (q2prover >= 1018) + cls.netchan.netprim.q2flags |= NPQ2_ANG16; + if (q2prover >= 1015) + MSG_ReadByte(); //some kind of waterjump hack enable + } + + MSG_ChangePrimitives(cls.netchan.netprim); + if (cl.playerview[0].playernum == -1) { // playing a cinematic or showing a pic, not a level SCR_EndLoadingPlaque(); @@ -3801,6 +3839,9 @@ void CLQ2_ParseClientinfo(int i, char *s) player_info_t *player; //s contains "name\model/skin" + if (i >= MAX_CLIENTS) + return; + player = &cl.players[i]; *player->userinfo = '\0'; @@ -3857,7 +3898,11 @@ void CLQ2_ParseConfigString (void) // do something apropriate - if (i == Q2CS_SKY) + if (i == Q2CS_NAME) + { + Q_strncpyz (cl.levelname, s, sizeof(cl.levelname)); + } + else if (i == Q2CS_SKY) { Q_strncpyz (cl.skyname, s, sizeof(cl.skyname)); } @@ -3921,9 +3966,16 @@ void CLQ2_ParseConfigString (void) Z_Free(cl.item_name[i-Q2CS_ITEMS]); cl.item_name[i-Q2CS_ITEMS] = Z_StrDup(s); } + else if (i >= Q2CS_GENERAL && i < Q2CS_GENERAL+Q2MAX_GENERAL) + { + Z_Free(cl.configstring_general[i-Q2CS_PLAYERSKINS]); + cl.configstring_general[i-Q2CS_PLAYERSKINS] = Z_StrDup(s); + } else if (i >= Q2CS_PLAYERSKINS && i < Q2CS_PLAYERSKINS+Q2MAX_CLIENTS) { CLQ2_ParseClientinfo (i-Q2CS_PLAYERSKINS, s); + Z_Free(cl.configstring_general[i-Q2CS_PLAYERSKINS]); + cl.configstring_general[i-Q2CS_PLAYERSKINS] = Z_StrDup(s); } else if (i == Q2CS_MAPCHECKSUM) { @@ -6715,6 +6767,19 @@ void CLQ2_ParseServerMessage (void) switch (cmd) { default: + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + { + switch(cmd & 0x1f) + { + case svcq2_frame: //20 (the bastard to implement.) + CLQ2_ParseFrame(cmd>>5); + break; + default: + Host_EndGame ("CLQ2_ParseServerMessage: Illegible server message (%i)", cmd); + return; + } + break; + } Host_EndGame ("CLQ2_ParseServerMessage: Illegible server message (%i)", cmd); return; @@ -6750,10 +6815,16 @@ void CLQ2_ParseServerMessage (void) else Host_EndGame ("Server disconnected"); return; - case svcq2_reconnect: //8 + case svcq2_reconnect: //8. this is actually kinda weird to have Con_TPrintf ("reconnecting...\n"); +#if 1 + CL_Disconnect(); + CL_BeginServerReconnect(); + return; +#else CL_SendClientCommand(true, "new"); break; +#endif case svcq2_sound: //9 // CLQ2_ParseStartSoundPacket(); break; @@ -6804,7 +6875,7 @@ void CLQ2_ParseServerMessage (void) Host_EndGame ("CL_ParseServerMessage: svcq2_deltapacketentities not as part of svcq2_frame"); return; case svcq2_frame: //20 (the bastard to implement.) - CLQ2_ParseFrame(); + CLQ2_ParseFrame(0); break; } } diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index fe29dcae3..1e8bc0e78 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -130,9 +130,18 @@ void CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t en } else { // encoded bbox - x = 8*(ent->solid & 31); - zd = 8*((ent->solid>>5) & 31); - zu = 8*((ent->solid>>10) & 63) - 32; + if (cls.netchan.netprim.q2flags & NPQ2_SIZE32) + { + x = ent->solid & 255; + zd = (ent->solid >> 8) & 255; + zu = ((ent->solid >> 16) & 65535) - 32768; + } + else + { + x = 8*(ent->solid & 31); + zd = 8*((ent->solid>>5) & 31); + zu = 8*((ent->solid>>10) & 63) - 32; + } bmins[0] = bmins[1] = -x; bmaxs[0] = bmaxs[1] = x; diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 5f3853154..a8b270b31 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2562,8 +2562,7 @@ void CLQ2_ParseTEnt (void) MSG_ReadPos (pos); MSG_ReadDir (dir); color = MSG_ReadByte (); - //FIXME: should use q2's vertical puff thing - P_RunParticleEffect (pos, dir, color, cnt); + P_RunParticleEffectPalette("q2part.TEQ2_LASER_SPARKS", pos, dir, color, cnt); break; /* @@ -3660,15 +3659,29 @@ void CL_UpdateExplosions (void) for (i=0, ex=cl_explosions; i < explosions_running; i++, ex++) { - if (!ex->model && !ex->flags) + if (!ex->model && !(ex->flags&Q2RF_BEAM)) continue; lastrunningexplosion = i; + if (ex->model->loadstate == MLS_LOADING) + continue; + if (ex->model->loadstate != MLS_LOADED) + { + ex->model = NULL; + ex->flags = 0; + P_DelinkTrailstate(&(ex->trailstate)); + continue; + } + f = ex->framerate*(cl.time - ex->start); + if (ex->firstframe >= 0) { firstframe = ex->firstframe; numframes = ex->numframes; + + if (!numframes) + numframes = ex->model->numframes - firstframe; } else { diff --git a/engine/client/client.h b/engine/client/client.h index b670848fe..2b898d1c7 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -470,8 +470,6 @@ typedef struct char servername[MAX_OSPATH]; // name of server from original connect - int qport; - struct ftenet_connections_s *sockets; qdownload_t *download; @@ -797,6 +795,7 @@ typedef struct char sound_name[MAX_PRECACHE_SOUNDS][MAX_QPATH]; char *particle_ssname[MAX_SSPARTICLESPRE]; #ifdef Q2CLIENT + char *configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL]; char *image_name[Q2MAX_IMAGES]; char *item_name[Q2MAX_ITEMS]; short inventory[Q2MAX_ITEMS]; @@ -1288,7 +1287,7 @@ qboolean CL_MayLerp(void); #ifdef Q3CLIENT void VARGS CLQ3_SendClientCommand(const char *fmt, ...) LIKEPRINTF(1); void CLQ3_SendAuthPacket(netadr_t *gameserver); -void CLQ3_SendConnectPacket(netadr_t *to); +void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport); void CLQ3_SendCmd(usercmd_t *cmd); qboolean CLQ3_Netchan_Process(void); void CLQ3_ParseServerMessage (void); @@ -1475,7 +1474,7 @@ void CLQ2_ParseTEnt (void); void CLQ2_AddEntities (void); void CLQ2_ParseBaseline (void); void CLQ2_ClearParticleState(void); -void CLQ2_ParseFrame (void); +void CLQ2_ParseFrame (int extrabits); void CLQ2_RunMuzzleFlash2 (int ent, int flash_number); int CLQ2_RegisterTEntModels (void); #endif diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 03c03e405..07a0d9a83 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -708,13 +708,25 @@ void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int to->origin[1] = MSG_ReadCoord (); if (bits & Q2U_ORIGIN3) to->origin[2] = MSG_ReadCoord (); - - if (bits & Q2U_ANGLE1) - to->angles[0] = MSG_ReadAngle(); - if (bits & Q2U_ANGLE2) - to->angles[1] = MSG_ReadAngle(); - if (bits & Q2U_ANGLE3) - to->angles[2] = MSG_ReadAngle(); + + if ((bits & Q2UX_ANGLE16) && (net_message.prim.q2flags & NPQ2_ANG16)) + { + if (bits & Q2U_ANGLE1) + to->angles[0] = MSG_ReadAngle16(); + if (bits & Q2U_ANGLE2) + to->angles[1] = MSG_ReadAngle16(); + if (bits & Q2U_ANGLE3) + to->angles[2] = MSG_ReadAngle16(); + } + else + { + if (bits & Q2U_ANGLE1) + to->angles[0] = MSG_ReadAngle(); + if (bits & Q2U_ANGLE2) + to->angles[1] = MSG_ReadAngle(); + if (bits & Q2U_ANGLE3) + to->angles[2] = MSG_ReadAngle(); + } if (bits & Q2U_OLDORIGIN) MSG_ReadPos (to->u.q2.old_origin); @@ -728,7 +740,12 @@ void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int to->u.q2.event = 0; if (bits & Q2U_SOLID) - to->solid = MSG_ReadShort (); + { + if (net_message.prim.q2flags & NPQ2_SIZE32) + to->solid = MSG_ReadLong(); + else + to->solid = MSG_ReadShort (); + } } void CLQ2_ClearParticleState(void) @@ -956,7 +973,7 @@ void CLQ2_ParseBaseline (void) CL_ParsePlayerstate =================== */ -void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe) +void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe, int extflags) { int flags; q2player_state_t *state; @@ -983,15 +1000,21 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe) { state->pmove.origin[0] = MSG_ReadShort (); state->pmove.origin[1] = MSG_ReadShort (); - state->pmove.origin[2] = MSG_ReadShort (); + if (extflags & Q2PSX_OLD) + state->pmove.origin[2] = MSG_ReadShort (); } + if (extflags & Q2PSX_M_ORIGIN2) + state->pmove.origin[2] = MSG_ReadShort (); if (flags & Q2PS_M_VELOCITY) { state->pmove.velocity[0] = MSG_ReadShort (); state->pmove.velocity[1] = MSG_ReadShort (); - state->pmove.velocity[2] = MSG_ReadShort (); + if (extflags & Q2PSX_OLD) + state->pmove.velocity[2] = MSG_ReadShort (); } + if (extflags & Q2PSX_M_VELOCITY2) + state->pmove.velocity[2] = MSG_ReadShort (); if (flags & Q2PS_M_TIME) state->pmove.pm_time = MSG_ReadByte (); @@ -1026,8 +1049,11 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe) { state->viewangles[0] = MSG_ReadAngle16 (); state->viewangles[1] = MSG_ReadAngle16 (); - state->viewangles[2] = MSG_ReadAngle16 (); + if (extflags & Q2PSX_OLD) + state->viewangles[2] = MSG_ReadAngle16 (); } + if (extflags & Q2PSX_VIEWANGLE2) + state->viewangles[2] = MSG_ReadAngle16 (); if (flags & Q2PS_KICKANGLES) { @@ -1044,9 +1070,24 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe) if (flags & Q2PS_WEAPONFRAME) { state->gunframe = MSG_ReadByte (); + if (extflags & Q2PSX_OLD) + { + state->gunoffset[0] = MSG_ReadChar ()*0.25; + state->gunoffset[1] = MSG_ReadChar ()*0.25; + state->gunoffset[2] = MSG_ReadChar ()*0.25; + state->gunangles[0] = MSG_ReadChar ()*0.25; + state->gunangles[1] = MSG_ReadChar ()*0.25; + state->gunangles[2] = MSG_ReadChar ()*0.25; + } + } + if (extflags & Q2PSX_GUNOFFSET) + { state->gunoffset[0] = MSG_ReadChar ()*0.25; state->gunoffset[1] = MSG_ReadChar ()*0.25; state->gunoffset[2] = MSG_ReadChar ()*0.25; + } + if (extflags & Q2PSX_GUNANGLES) + { state->gunangles[0] = MSG_ReadChar ()*0.25; state->gunangles[1] = MSG_ReadChar ()*0.25; state->gunangles[2] = MSG_ReadChar ()*0.25; @@ -1067,10 +1108,19 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe) state->rdflags = MSG_ReadByte (); // parse stats - statbits = MSG_ReadLong (); - for (i=0 ; istats[i] = MSG_ReadShort(); + if (extflags & (Q2PSX_OLD|Q2PSX_STATS)) + statbits = MSG_ReadLong (); + else + statbits = 0; + if (statbits) + { + for (i=0 ; istats[i] = MSG_ReadShort(); + } + + if (extflags & Q2PSX_CLIENTNUM) + /*state->viewent =*/ MSG_ReadByte(); } @@ -1104,12 +1154,12 @@ void CLQ2_FireEntityEvents (q2frame_t *frame) CL_ParseFrame ================ */ -void CLQ2_ParseFrame (void) +void CLQ2_ParseFrame (int extrabits) { int cmd; int len; q2frame_t *old; - int i,j; + int i,j, chokecount; memset (&cl.q2frame, 0, sizeof(cl.q2frame)); @@ -1117,8 +1167,31 @@ void CLQ2_ParseFrame (void) CLQ2_ClearProjectiles(); // clear projectiles for new frame #endif - cl.q2frame.serverframe = MSG_ReadLong (); - cl.q2frame.deltaframe = MSG_ReadLong (); + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + { + unsigned int bits = MSG_ReadLong(); + cl.q2frame.serverframe = bits & 0x07ffffff; + i = bits >> 27; + if (i == 31) + cl.q2frame.deltaframe = -1; + else + cl.q2frame.deltaframe = cl.q2frame.serverframe - i; + bits = MSG_ReadByte(); + chokecount = bits & 0xf; + extrabits = (extrabits<<4) | (bits>>4); + } + else + { + cl.q2frame.serverframe = MSG_ReadLong (); + cl.q2frame.deltaframe = MSG_ReadLong (); + if (cls.protocol_q2 > 26) + chokecount = MSG_ReadByte (); + else + chokecount = 0; + + extrabits = Q2PSX_OLD; + } + cl.q2frame.servertime = cl.q2frame.serverframe*100; cl.oldgametime = cl.gametime; @@ -1126,12 +1199,8 @@ void CLQ2_ParseFrame (void) cl.gametime = cl.q2frame.servertime/1000.f; cl.gametimemark = realtime; - if (cls.protocol_q2 > 26) - { - i = MSG_ReadByte (); - for (j=0 ; jlerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL2], ent.keynum, NULL, ¢->trailstate)) + { P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 0xd0, 1, ¢->trailstate); - V_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0); + V_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0); + } } else { diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 8168506dc..bdb487dd6 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -1042,7 +1042,7 @@ void CLQ3_SendAuthPacket(netadr_t *gameserver) } } -void CLQ3_SendConnectPacket(netadr_t *to) +void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport) { char data[2048]; char adrbuf[MAX_ADR_SIZE]; @@ -1056,7 +1056,7 @@ void CLQ3_SendConnectPacket(netadr_t *to) msg.overflowed = msg.allowoverflow = 0; msg.maxsize = sizeof(data); MSG_WriteLong(&msg, -1); - MSG_WriteString(&msg, va("connect \"\\challenge\\%i\\qport\\%i\\protocol\\%i\\ip\\%s%s\"", cls.challenge, cls.qport, PROTOCOL_VERSION_Q3, NET_AdrToString (adrbuf, sizeof(adrbuf), &net_local_cl_ipadr), cls.userinfo[0])); + MSG_WriteString(&msg, va("connect \"\\challenge\\%i\\qport\\%i\\protocol\\%i\\ip\\%s%s\"", challenge, qport, PROTOCOL_VERSION_Q3, NET_AdrToString (adrbuf, sizeof(adrbuf), &net_local_cl_ipadr), cls.userinfo[0])); #ifdef HUFFNETWORK Huff_EncryptPacket(&msg, 12); if (!Huff_CompressionCRC(HUFFCRC_QUAKE3)) diff --git a/engine/client/image.c b/engine/client/image.c index 8522c2ca9..a9dafc1a7 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -3616,7 +3616,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag rgbadata = BZ_Malloc(imgwidth * imgheight*4); for (i = 0, valid = false; i < imgwidth * imgheight; i++) { - if (((qbyte*)rawdata)[i] < 256-vid.fullbright) + if (((qbyte*)rawdata)[i] == 255 || ((qbyte*)rawdata)[i] < 256-vid.fullbright) rgbadata[i] = 0; else { diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index 29c75fdb4..7efa2a1fc 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -1110,7 +1110,10 @@ static void PClassic_RunParticleEffect (vec3_t org, vec3_t dir, int color, int c { Classic_RunParticleEffect(org, dir, color, count); } - +static void PClassic_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count) +{ + Classic_RunParticleEffect(org, dir, color, count); +} particleengine_t pe_classic = { @@ -1129,6 +1132,7 @@ particleengine_t pe_classic = PClassic_RunParticleEffect2, PClassic_RunParticleEffect3, PClassic_RunParticleEffect4, + PClassic_RunParticleEffectPalette, PClassic_ParticleTrailIndex, PClassic_EmitSkyEffectTris, diff --git a/engine/client/p_null.c b/engine/client/p_null.c index 4ebc3a863..b53590e37 100644 --- a/engine/client/p_null.c +++ b/engine/client/p_null.c @@ -20,6 +20,7 @@ static void PNULL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int coun static void PNULL_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count){} static void PNULL_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int effect, int count){} static void PNULL_RunParticleEffect4 (vec3_t org, float radius, int color, int effect, int count){} +static void PNULL_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count){} static void PNULL_ParticleTrailIndex (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk){} static void PNULL_EmitSkyEffectTris(model_t *mod, msurface_t *fa, int ptype){} @@ -65,6 +66,7 @@ particleengine_t pe_null = PNULL_RunParticleEffect2, PNULL_RunParticleEffect3, PNULL_RunParticleEffect4, + PNULL_RunParticleEffectPalette, PNULL_ParticleTrailIndex, PNULL_EmitSkyEffectTris, diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 90c735a8e..3cfade34e 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -71,6 +71,8 @@ extern particleengine_t pe_classic; particleengine_t *fallback = NULL; //does this really need to be 'extern'? #define FALLBACKBIAS 0x1000000 +#define PART_VALID(part) ((part) >= 0 && (part) < FALLBACKBIAS) //copes with fallback indexes + static int pe_default = P_INVALID; static int pe_size2 = P_INVALID; static int pe_size3 = P_INVALID; @@ -80,6 +82,7 @@ static qboolean pe_script_enabled; static float psintable[256]; static qboolean P_LoadParticleSet(char *name, qboolean implicit); +static void R_Particles_KillAllEffects(void); static void buildsintable(void) { @@ -223,11 +226,13 @@ typedef struct part_type_s { float scale; //initial scale float scalerand; //with up to this much extra float die, randdie; //how long it lasts (plus some rand) - float randomvel, randomvelvert, randomvelvertbias; //random velocity (unaligned=worldspace) float veladd, randomveladd; //scale the incoming velocity by this much float orgadd, randomorgadd; //spawn the particle this far along its velocity direction float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards) vec3_t orgbias; //static 3d world-coord bias + vec3_t velbias; + vec3_t orgwrand; //3d world-coord randomisation without relation to spawn mode + vec3_t velwrand; //3d world-coord randomisation without relation to spawn mode float viewspacefrac; float s1, t1, s2, t2; //texture coords @@ -306,18 +311,19 @@ typedef struct part_type_s { struct part_type_s *nexttorun; unsigned int flags; -#define PT_VELOCITY 0x0001 -#define PT_FRICTION 0x0002 +#define PT_VELOCITY 0x0001 // has velocity modifiers +#define PT_FRICTION 0x0002 // has friction modifiers #define PT_CHANGESCOLOUR 0x0004 -#define PT_CITRACER 0x0008 // Q1-style tracer behavior for colorindex -#define PT_INVFRAMETIME 0x0010 // apply inverse frametime to count (causes emits to be per frame) -#define PT_AVERAGETRAIL 0x0020 // average trail points from start to end, useful with t_lightning, etc -#define PT_NOSTATE 0x0040 // don't use trailstate for this emitter (careful with assoc...) -#define PT_NOSPREADFIRST 0x0080 // don't randomize org/vel for first generated particle -#define PT_NOSPREADLAST 0x0100 // don't randomize org/vel for last generated particle -#define PT_TROVERWATER 0x0200 // don't spawn if underwater -#define PT_TRUNDERWATER 0x0400 // don't spawn if overwater -#define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows. +#define PT_CITRACER 0x0008 // Q1-style tracer behavior for colorindex +#define PT_INVFRAMETIME 0x0010 // apply inverse frametime to count (causes emits to be per frame) +#define PT_AVERAGETRAIL 0x0020 // average trail points from start to end, useful with t_lightning, etc +#define PT_NOSTATE 0x0040 // don't use trailstate for this emitter (careful with assoc...) +#define PT_NOSPREADFIRST 0x0080 // don't randomize org/vel for first generated particle +#define PT_NOSPREADLAST 0x0100 // don't randomize org/vel for last generated particle +#define PT_TROVERWATER 0x0200 // don't spawn if underwater +#define PT_TRUNDERWATER 0x0400 // don't spawn if overwater +#define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows. +#define PT_WORLDSPACERAND 0x1000 // effect has orgwrand or velwrand properties unsigned int fluidmask; unsigned int state; @@ -688,6 +694,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc blend\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -706,6 +713,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_SRC_COLOR GL_ONE_MINUS_SRC_COLOR\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -725,6 +733,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_SRC_ALPHA GL_ONE\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -744,6 +753,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_SRC_COLOR GL_ONE\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -763,6 +773,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_ONE GL_ONE_MINUS_SRC_ALPHA\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -782,6 +793,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_ZERO GL_ONE_MINUS_SRC_ALPHA\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -801,6 +813,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_ZERO GL_ONE_MINUS_SRC_COLOR\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -819,6 +832,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "blendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_COLOR\n" "rgbgen vertex\n" "alphagen vertex\n" + "nodepth\n" "}\n" "polygonoffset\n" "surfaceparm noshadows\n" @@ -1245,6 +1259,8 @@ void P_ParticleEffect_f(void) else if (!strcmp(var, "alpha")) ptype->alpha = atof(value); + else if (!strcmp(var, "alpharand")) + ptype->alpharand = atof(value); #ifndef NOLEGACY else if (!strcmp(var, "alphachange")) { @@ -1282,24 +1298,25 @@ void P_ParticleEffect_f(void) else if (!strcmp(var, "randomvel")) { - ptype->randomvel = atof(value); + ptype->velbias[0] = ptype->velbias[1] = 0; + ptype->velwrand[0] = ptype->velwrand[1] = atof(value); if (Cmd_Argc()>3) { - ptype->randomvelvertbias = atof(Cmd_Argv(2)); - ptype->randomvelvert = atof(Cmd_Argv(3)); - ptype->randomvelvert -= ptype->randomvelvertbias; /*make vert be the total range*/ - ptype->randomvelvert /= 2; /*vert is actually +/- 1, not 0 to 1, so rescale it*/ - ptype->randomvelvertbias += ptype->randomvelvert; /*and bias must be centered to the range*/ + ptype->velbias[2] = atof(Cmd_Argv(2)); + ptype->velwrand[2] = atof(Cmd_Argv(3)); + ptype->velwrand[2] -= ptype->velbias[2]; /*make vert be the total range*/ + ptype->velwrand[2] /= 2; /*vert is actually +/- 1, not 0 to 1, so rescale it*/ + ptype->velbias[2] += ptype->velwrand[2]; /*and bias must be centered to the range*/ } else if (Cmd_Argc()>2) { - ptype->randomvelvert = atof(Cmd_Argv(2)); - ptype->randomvelvertbias = 0; + ptype->velwrand[2] = atof(Cmd_Argv(2)); + ptype->velbias[2] = 0; } else { - ptype->randomvelvert = ptype->randomvel; - ptype->randomvelvertbias = 0; + ptype->velwrand[2] = ptype->velwrand[0]; + ptype->velbias[2] = 0; } } else if (!strcmp(var, "veladd")) @@ -1316,6 +1333,32 @@ void P_ParticleEffect_f(void) if (Cmd_Argc()>2) ptype->randomorgadd = atof(Cmd_Argv(2)) - ptype->orgadd; } + + else if (!strcmp(var, "orgbias")) + { + ptype->orgbias[0] = atof(value); + ptype->orgbias[1] = atof(Cmd_Argv(2)); + ptype->orgbias[2] = atof(Cmd_Argv(3)); + } + else if (!strcmp(var, "velbias")) + { + ptype->velbias[0] = atof(value); + ptype->velbias[1] = atof(Cmd_Argv(2)); + ptype->velbias[2] = atof(Cmd_Argv(3)); + } + else if (!strcmp(var, "orgwrand")) + { + ptype->orgwrand[0] = atof(value); + ptype->orgwrand[1] = atof(Cmd_Argv(2)); + ptype->orgwrand[2] = atof(Cmd_Argv(3)); + } + else if (!strcmp(var, "velwrand")) + { + ptype->velwrand[0] = atof(value); + ptype->velwrand[1] = atof(Cmd_Argv(2)); + ptype->velwrand[2] = atof(Cmd_Argv(3)); + } + else if (!strcmp(var, "friction")) { ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(value); @@ -1380,6 +1423,8 @@ parsefluid: ptype->fluidmask |= FTECONTENTS_SOLID; else if (!strcmp(value, "playerclip")) ptype->fluidmask |= FTECONTENTS_PLAYERCLIP; + else if (!strcmp(value, "none")) + ptype->fluidmask |= 0; else Con_Printf("%s.%s: unknown contents: %s\n", ptype->config, ptype->name, value); } @@ -1617,6 +1662,7 @@ parsefluid: ptype->stainonimpact = atof(value); else if (!strcmp(var, "blend")) { + ptype->looks.premul = false; if (!strcmp(value, "adda") || !strcmp(value, "add")) ptype->looks.blendmode = BM_ADDA; else if (!strcmp(value, "addc")) @@ -1631,6 +1677,21 @@ parsefluid: ptype->looks.blendmode = BM_BLENDCOLOUR; else if (!strcmp(value, "blendalpha")) ptype->looks.blendmode = BM_BLEND; + else if (!strcmp(value, "premul_subtract")) + { + ptype->looks.premul = 1; + ptype->looks.blendmode = BM_INVMODC; + } + else if (!strcmp(value, "premul_add")) + { + ptype->looks.premul = 2; + ptype->looks.blendmode = BM_PREMUL; + } + else if (!strcmp(value, "premul_blend")) + { + ptype->looks.premul = 1; + ptype->looks.blendmode = BM_PREMUL; + } else ptype->looks.blendmode = BM_BLEND; //fallback } @@ -1782,10 +1843,10 @@ parsefluid: } /* else if (!strcmp(var, "spawnparam3")) ptype->spawnparam3 = atof(value); */ -#endif - else if (!strcmp(var, "up")) ptype->orgbias[2] = atof(value); +#endif + else if (!strcmp(var, "rampmode")) { if (!strcmp(value, "none")) @@ -1953,8 +2014,10 @@ parsefluid: ptype->clipcount = 1; //if there is a chance that it moves - if (ptype->randomvel || ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert) + if (ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->velbias,ptype->velbias)) ptype->flags |= PT_VELOCITY; + if (DotProduct(ptype->velbias,ptype->velbias) || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->orgwrand,ptype->orgwrand)) + ptype->flags |= PT_WORLDSPACERAND; //if it has friction if (ptype->friction[0] || ptype->friction[1] || ptype->friction[2]) ptype->flags |= PT_FRICTION; @@ -2032,13 +2095,14 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) return true; } - if (body == 1) + if (body == 1 || body == 2) { + qboolean all = body==2; *outstr = 0; if (!ptype->loaded) return true; - Q_strncatz(outstr, va("//this functionality is incomplete\n"), outstrlen); +// Q_strncatz(outstr, va("//this functionality is incomplete\n"), outstrlen); switch (ptype->looks.type) { @@ -2125,51 +2189,52 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("sound \"%s\" %g %g %g %g %g\n", ptype->sounds[i].name, ptype->sounds[i].vol, ptype->sounds[i].atten, ptype->sounds[i].pitch, ptype->sounds[i].delay, ptype->sounds[i].weight), outstrlen); } - if (*ptype->texname) + if (*ptype->texname || all) { //note: particles don't really know if the shader was embedded or not. the shader system handles all that. //this means that you'll really need to use external shaders for this to work. Q_strncatz(outstr, va("texture \"%s\"\n", ptype->texname), outstrlen); Q_strncatz(outstr, va("tcoords %g %g %g %g %g %i %g\n", ptype->s1, ptype->t1, ptype->s2, ptype->t2, 1.0f, ptype->randsmax, ptype->texsstride), outstrlen); } - if (ptype->count) + if (ptype->count || all) Q_strncatz(outstr, va("count %g\n", ptype->count), outstrlen); - if (ptype->rgb[0] || ptype->rgb[1] || ptype->rgb[2]) + if (ptype->rgb[0] || ptype->rgb[1] || ptype->rgb[2] || all) Q_strncatz(outstr, va("rgbf %g %g %g\n", ptype->rgb[0], ptype->rgb[1], ptype->rgb[2]), outstrlen); - if (ptype->rgbrand[0] || ptype->rgbrand[1] || ptype->rgbrand[2]) + if (ptype->rgbrand[0] || ptype->rgbrand[1] || ptype->rgbrand[2] || all) Q_strncatz(outstr, va("rgbrandf %g %g %g\n", ptype->rgbrand[0], ptype->rgbrand[1], ptype->rgbrand[2]), outstrlen); - if (ptype->rgbrandsync[0] || ptype->rgbrandsync[1] || ptype->rgbrandsync[2]) + if (ptype->rgbrandsync[0] || ptype->rgbrandsync[1] || ptype->rgbrandsync[2] || all) Q_strncatz(outstr, va("rgbrandsync %g %g %g\n", ptype->rgbrandsync[0], ptype->rgbrandsync[1], ptype->rgbrandsync[2]), outstrlen); - if (ptype->rgbchange[0] || ptype->rgbchange[1] || ptype->rgbchange[2]) + if (ptype->rgbchange[0] || ptype->rgbchange[1] || ptype->rgbchange[2] || all) Q_strncatz(outstr, va("rgbdeltaf %g %g %g\n", ptype->rgbchange[0], ptype->rgbchange[1], ptype->rgbchange[2]), outstrlen); - if (ptype->rgbchangetime) + if (ptype->rgbchangetime || all) Q_strncatz(outstr, va("rgbchangetime %g\n", ptype->rgbchangetime), outstrlen); - if (ptype->colorindex != -1) + if (ptype->colorindex != -1 || all) Q_strncatz(outstr, va("colorindex %i\n", ptype->colorindex), outstrlen); - if (ptype->colorrand) + if (ptype->colorrand || all) Q_strncatz(outstr, va("colorrand %i\n", ptype->colorrand), outstrlen); - if (ptype->alpha) +// if (ptype->alpha || all) Q_strncatz(outstr, va("alpha %g\n", ptype->alpha), outstrlen); - if (ptype->alpharand) + if (ptype->alpharand || all) Q_strncatz(outstr, va("alpharand %g\n", ptype->alpharand), outstrlen); - if (ptype->alphachange) +// if (ptype->alphachange || all) Q_strncatz(outstr, va("alphadelta %g\n", ptype->alphachange), outstrlen); - if (ptype->scale || ptype->scalerand) + if (ptype->scale || ptype->scalerand || all) Q_strncatz(outstr, va("scale %g %g\n", ptype->scale, ptype->scale+ptype->scalerand), outstrlen); -// if (ptype->looks.scalefactor) //always write scalefactor, to avoid issues with defaults. +// if (ptype->looks.scalefactor || all) //always write scalefactor, to avoid issues with defaults. Q_strncatz(outstr, va("scalefactor %g\n", ptype->looks.scalefactor), outstrlen); - if (ptype->scaledelta) + if (ptype->scaledelta || all) Q_strncatz(outstr, va("scaledelta %g\n", ptype->scaledelta), outstrlen); - if (ptype->looks.stretch) + if (ptype->looks.stretch || all) Q_strncatz(outstr, va("stretchfactor %g\n", ptype->looks.stretch), outstrlen); - if (ptype->die || ptype->randdie) + if (ptype->die || ptype->randdie || all) Q_strncatz(outstr, va("die %g %g\n", ptype->die, ptype->die+ptype->randdie), outstrlen); + if (ptype->spawnmode != SM_BOX || ptype->spawnparam1 || ptype->spawnparam2 || all) switch(ptype->spawnmode) { case SM_CIRCLE: @@ -2203,40 +2268,89 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("spawnmode box %g %g\n", ptype->spawnparam1, ptype->spawnparam2), outstrlen); break; } - if (ptype->spawnvel || ptype->spawnvelvert) + if (ptype->spawnvel || ptype->spawnvelvert || all) Q_strncatz(outstr, va("spawnvel %g %g\n", ptype->spawnvel, ptype->spawnvelvert), outstrlen); - if (ptype->areaspread || ptype->areaspreadvert) + if (ptype->areaspread || ptype->areaspreadvert || all) Q_strncatz(outstr, va("spawnorg %g %g\n", ptype->areaspread, ptype->areaspreadvert), outstrlen); - if (ptype->viewspacefrac) + if (ptype->viewspacefrac || all) Q_strncatz(outstr, ((ptype->viewspacefrac==1)?"viewspace\n":va("viewspace %g\n", ptype->viewspacefrac)), outstrlen); - if (ptype->veladd || ptype->randomveladd) + if (ptype->veladd || ptype->randomveladd || all) { if (ptype->randomveladd) Q_strncatz(outstr, va("veladd %g %g\n", ptype->veladd, ptype->veladd+ptype->randomveladd), outstrlen); else Q_strncatz(outstr, va("veladd %g\n", ptype->veladd), outstrlen); } - if (ptype->orgadd || ptype->randomorgadd) + if (ptype->orgadd || ptype->randomorgadd || all) { if (ptype->randomorgadd) Q_strncatz(outstr, va("orgadd %g %g\n", ptype->orgadd, ptype->orgadd+ptype->randomorgadd), outstrlen); else Q_strncatz(outstr, va("orgadd %g\n", ptype->orgadd), outstrlen); } - if (ptype->randomvel || ptype->randomvelvert || ptype->randomvelvertbias) - Q_strncatz(outstr, va("randomvel %g %g %g\n", ptype->randomvel, ptype->randomvelvertbias - ptype->randomvelvert, ptype->randomvelvertbias + ptype->randomvelvert), outstrlen); + + if (ptype->gravity || all) + Q_strncatz(outstr, va("gravity %g\n", ptype->gravity), outstrlen); + + //these are more for dp-compat than anything else. + if (DotProduct(ptype->orgbias,ptype->orgbias) || all) + Q_strncatz(outstr, va("orgbias %g %g %g\n", ptype->orgbias[0], ptype->orgbias[1], ptype->orgbias[2]), outstrlen); + if (DotProduct(ptype->orgwrand,ptype->orgwrand) || all) + Q_strncatz(outstr, va("orgwrand %g %g %g\n", ptype->orgwrand[0], ptype->orgwrand[1], ptype->orgwrand[2]), outstrlen); + if (ptype->velbias[0] == 0 && ptype->velbias[1] == 0 && ptype->velwrand[0] == ptype->velwrand[1]) + Q_strncatz(outstr, va("randomvel %g %g %g\n", ptype->velwrand[0], ptype->velbias[2] - ptype->velwrand[2], ptype->velbias[2]*2-(ptype->velbias[2]- ptype->velwrand[2])), outstrlen); + else + { + if (DotProduct(ptype->velbias,ptype->velbias) || all) + Q_strncatz(outstr, va("velbias %g %g %g\n", ptype->velbias[0], ptype->velbias[1], ptype->velbias[2]), outstrlen); + if (DotProduct(ptype->velwrand,ptype->velwrand) || all) + Q_strncatz(outstr, va("velwrand %g %g %g\n", ptype->velwrand[0], ptype->velwrand[1], ptype->velwrand[2]), outstrlen); + } if (ptype->assoc != P_INVALID) Q_strncatz(outstr, va("assoc \"%s\"\n", part_type[ptype->assoc].name), outstrlen); - Q_strncatz(outstr, va("rotationstart %g %g\n", ptype->rotationstartmin*180/M_PI, (ptype->rotationstartmin+ptype->rotationstartrand)*180/M_PI), outstrlen); - Q_strncatz(outstr, va("rotationspeed %g %g\n", ptype->rotationmin*180/M_PI, (ptype->rotationmin+ptype->rotationrand)*180/M_PI), outstrlen); + if (ptype->rotationstartmin != -M_PI || ptype->rotationstartrand != 2*M_PI || all) + Q_strncatz(outstr, va("rotationstart %g %g\n", ptype->rotationstartmin*180/M_PI, (ptype->rotationstartmin+ptype->rotationstartrand)*180/M_PI), outstrlen); + if (ptype->rotationmin || ptype->rotationrand || all) + Q_strncatz(outstr, va("rotationspeed %g %g\n", ptype->rotationmin*180/M_PI, (ptype->rotationmin+ptype->rotationrand)*180/M_PI), outstrlen); - if (ptype->dl_radius) + if (ptype->flags & (PT_TROVERWATER | PT_TRUNDERWATER)) { - Q_strncatz(outstr, va("lightradius %g\n", ptype->dl_radius[0]), outstrlen); + if (ptype->flags & PT_TRUNDERWATER) + Q_strncatz(outstr, "underwater", outstrlen); + else + Q_strncatz(outstr, "notunderwater", outstrlen); + if (ptype->fluidmask == 0) + Q_strncatz(outstr, " none", outstrlen); + else if (ptype->fluidmask != FTECONTENTS_FLUID) + { + if ((ptype->fluidmask & FTECONTENTS_FLUID) == FTECONTENTS_FLUID) + Q_strncatz(outstr, " fluid", outstrlen); + else + { + if (ptype->fluidmask & FTECONTENTS_WATER) + Q_strncatz(outstr, " water", outstrlen); + if (ptype->fluidmask & FTECONTENTS_SLIME) + Q_strncatz(outstr, " slime", outstrlen); + if (ptype->fluidmask & FTECONTENTS_LAVA) + Q_strncatz(outstr, " lava", outstrlen); + if (ptype->fluidmask & FTECONTENTS_SKY) + Q_strncatz(outstr, " sky", outstrlen); + } + if (ptype->fluidmask & FTECONTENTS_SOLID) + Q_strncatz(outstr, " solid", outstrlen); + if (ptype->fluidmask & FTECONTENTS_PLAYERCLIP) + Q_strncatz(outstr, " playerclip", outstrlen); + } + Q_strncatz(outstr, "\n", outstrlen); + } + + if (ptype->dl_radius[0] || ptype->dl_radius[1] || all) + { + Q_strncatz(outstr, va("lightradius %g %g\n", ptype->dl_radius[0], ptype->dl_radius[0]+ptype->dl_radius[1]), outstrlen); Q_strncatz(outstr, va("lightradiusfade %g\n", ptype->dl_decay[3]), outstrlen); Q_strncatz(outstr, va("lightrgb %g %g %g\n", ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]), outstrlen); Q_strncatz(outstr, va("lightrgbfade %g %g %g\n", ptype->dl_decay[0], ptype->dl_decay[1], ptype->dl_decay[2]), outstrlen); @@ -2246,9 +2360,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("lightcorona %g %g\n", ptype->dl_corona_intensity, ptype->dl_corona_scale), outstrlen); Q_strncatz(outstr, va("lightscales %g %g %g\n", ptype->dl_scales[0], ptype->dl_scales[1], ptype->dl_scales[2]), outstrlen); } - if (ptype->stain_radius) + if (ptype->stain_radius || all) Q_strncatz(outstr, va("spawnstain %g %g %g %g\n", ptype->stain_radius, ptype->stain_rgb[0], ptype->stain_rgb[1], ptype->stain_rgb[2]), outstrlen); - if (ptype->stainonimpact) + if (ptype->stainonimpact || all) Q_strncatz(outstr, va("stains %g\n", ptype->stainonimpact), outstrlen); return true; @@ -2458,8 +2572,10 @@ static void P_PartInfo_f (void) void FinishParticleType(part_type_t *ptype) { //if there is a chance that it moves - if (ptype->randomvel || ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert) + if (ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->velbias,ptype->velbias)) ptype->flags |= PT_VELOCITY; + if (DotProduct(ptype->velbias,ptype->velbias) || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->orgwrand,ptype->orgwrand)) + ptype->flags |= PT_WORLDSPACERAND; //if it has friction if (ptype->friction[0] || ptype->friction[1] || ptype->friction[2]) ptype->flags |= PT_FRICTION; @@ -2485,6 +2601,7 @@ void FinishParticleType(part_type_t *ptype) ptype->looks.stretch *= 0.04; } +#ifndef NOLEGACY static void P_ImportEffectInfo(char *config, char *line) { part_type_t *ptype = NULL; @@ -2492,6 +2609,45 @@ static void P_ImportEffectInfo(char *config, char *line) char arg[8][1024]; int args = 0; + float teximages[256][4]; + + { + int i; + vfsfile_t *file; + char *line, linebuf[1024]; + //default assumes 8*8 grid, but we allow more + for (i = 0; i < 256; i++) + { + teximages[i][0] = 1/8.0 * (i & 7); + teximages[i][1] = 1/8.0 * (1+(i & 7)); + teximages[i][2] = 1/8.0 * (i>>3); + teximages[i][3] = 1/8.0 * (1+(i>>3)); + } + + file = FS_OpenVFS("particles/particlefont.txt", "rb", FS_GAME); + if (file) + { + while (VFS_GETS(file, linebuf, sizeof(linebuf))) + { + line = COM_StringParse(linebuf, arg[0], sizeof(arg[0]), false, false); + line = COM_StringParse(line, arg[1], sizeof(arg[1]), false, false); + line = COM_StringParse(line, arg[2], sizeof(arg[2]), false, false); + line = COM_StringParse(line, arg[3], sizeof(arg[3]), false, false); + line = COM_StringParse(line, arg[4], sizeof(arg[4]), false, false); + + if (line) + { + i = atoi(arg[0]); + teximages[i][0] = atof(arg[1]); + teximages[i][1] = atof(arg[3]); + teximages[i][2] = atof(arg[2]); + teximages[i][3] = atof(arg[4]); + } + } + VFS_CLOSE(file); + } + } + for (;;) { if (!*line) @@ -2534,8 +2690,6 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->scalerand *= 4; ptype->scaledelta *= 4; } - if (!ptype->areaspreadvert) - ptype->areaspreadvert = 0.001; FinishParticleType(ptype); } @@ -2571,7 +2725,7 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->rgb[1] = 1; ptype->rgb[2] = 1; - ptype->spawnmode = SM_BALL; +// ptype->spawnmode = SM_BALL; ptype->colorindex = -1; ptype->spawnchance = 1; @@ -2667,12 +2821,11 @@ static void P_ImportEffectInfo(char *config, char *line) { int mini = atoi(arg[1]); int maxi = atoi(arg[2]); - /*number range between 0 and 63*/ - ptype->s1 = 1/8.0 * (mini & 7); - ptype->s2 = 1/8.0 * (1+(mini & 7)); - ptype->t1 = 1/8.0 * (mini>>3); - ptype->t2 = 1/8.0 * (1+(mini>>3)); - ptype->texsstride = 1/8.0; + ptype->s1 = teximages[mini][0]; + ptype->s2 = teximages[mini][1]; + ptype->t1 = teximages[mini][2]; + ptype->t2 = teximages[mini][3]; + ptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)][0] - teximages[mini][0]; ptype->randsmax = (maxi - mini); if (ptype->randsmax < 1) ptype->randsmax = 1; @@ -2704,11 +2857,16 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->alphachange = -f/256; } else if (!strcmp(arg[0], "velocityoffset") && args == 4) - ; /*a 3d world-coord addition*/ + { /*a 3d world-coord addition*/ + ptype->velbias[0] = atof(arg[1]); + ptype->velbias[1] = atof(arg[2]); + ptype->velbias[2] = atof(arg[3]); + } else if (!strcmp(arg[0], "velocityjitter") && args == 4) { - ptype->spawnvel = (atof(arg[1]) + atof(arg[2]))*0.5; - ptype->spawnvelvert = atof(arg[3]); + ptype->velwrand[0] = atof(arg[1]); + ptype->velwrand[1] = atof(arg[2]); + ptype->velwrand[2] = atof(arg[3]); } else if (!strcmp(arg[0], "originoffset") && args == 4) { /*a 3d world-coord addition*/ @@ -2718,8 +2876,9 @@ static void P_ImportEffectInfo(char *config, char *line) } else if (!strcmp(arg[0], "originjitter") && args == 4) { - ptype->areaspread = (atof(arg[1]) + atof(arg[2]))*0.5; - ptype->areaspreadvert = atof(arg[3]); + ptype->orgwrand[0] = atof(arg[1]); + ptype->orgwrand[1] = atof(arg[2]); + ptype->orgwrand[2] = atof(arg[3]); } else if (!strcmp(arg[0], "gravity") && args == 2) { @@ -2816,20 +2975,27 @@ static void P_ImportEffectInfo(char *config, char *line) } #if 1 else if (!strcmp(arg[0], "staincolor") && args == 3) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); + Con_Printf("Particle effect token %s not supported\n", arg[0]); else if (!strcmp(arg[0], "stainalpha") && args == 3) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); + Con_Printf("Particle effect token %s not supported\n", arg[0]); else if (!strcmp(arg[0], "stainsize") && args == 3) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); + Con_Printf("Particle effect token %s not supported\n", arg[0]); else if (!strcmp(arg[0], "staintex") && args == 3) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); + Con_Printf("Particle effect token %s not supported\n", arg[0]); else if (!strcmp(arg[0], "stainless") && args == 2) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); - else if (!strcmp(arg[0], "rotate") && args == 3) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); - else if (!strcmp(arg[0], "rotate") && args == 5) - Con_DPrintf("Particle effect %s not supported\n", arg[0]); + Con_Printf("Particle effect token %s not supported\n", arg[0]); #endif + else if (!strcmp(arg[0], "rotate") && args == 5) + { + ptype->rotationstartmin = atof(arg[1]); + ptype->rotationstartrand = atof(arg[2]) - ptype->rotationstartmin; + ptype->rotationmin = atof(arg[3]); + ptype->rotationrand = atof(arg[4]) - ptype->rotationmin; + ptype->rotationstartmin *= M_PI/180; + ptype->rotationstartrand *= M_PI/180; + ptype->rotationmin *= M_PI/180; + ptype->rotationrand *= M_PI/180; + } else Con_Printf("Particle effect token not recognised, or invalid args: %s %s %s %s %s %s\n", arg[0], args<2?"":arg[1], args<3?"":arg[2], args<4?"":arg[3], args<5?"":arg[4], args<6?"":arg[5]); args = 0; @@ -2850,29 +3016,82 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->scalerand *= 4; ptype->scaledelta *= 4; } - if (!ptype->areaspreadvert) - ptype->areaspreadvert = 0.001; FinishParticleType(ptype); } r_plooksdirty = true; } -static void P_ImportEffectInfo_f(void) +static qboolean P_ImportEffectInfo_Name(char *config) { char *file; - char *config = "effectinfo"; FS_LoadFile(va("%s.txt", config), (void**)&file); if (!file) { - Con_Printf("effectinfo.txt not found\n"); - return; + Con_Printf("%s not found\n", config); + return false; } P_ImportEffectInfo(config, file); FS_FreeFile(file); + return true; } +static void P_ConvertEffectInfo_f(void) +{ + int i, assoc, n; + vfsfile_t *outf; + char effect[1024]; + char fname[64] = "particles/effectinfo.cfg"; + + R_Particles_KillAllEffects(); + P_ImportEffectInfo_Name("effectinfo"); + + FS_CreatePath("particles/", FS_GAMEONLY); + outf = FS_OpenVFS(fname, "wb", FS_GAMEONLY); + if (!outf) + { + FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + Con_Printf("Unable to open file %s\n", effect); + return; + } + for (i = 0; i < numparticletypes; i++) + { + part_type_t *ptype = &part_type[i]; + if (strcmp(ptype->config, "effectinfo")) + continue; //skip any which are not relevant. + + if (strchr(ptype->name, '+')) + continue; //skip assoc chains + Q_strncpyz(effect, ptype->name, sizeof(effect)); + + assoc = i; + for(;;) + { + n = part_type[assoc].assoc; + part_type[assoc].assoc = P_INVALID; + VFS_PUTS(outf, "r_part "); + VFS_PUTS(outf, effect); + VFS_PUTS(outf, "\n{\n"); + PScript_Query(assoc, 1, effect, sizeof(effect)); + VFS_PUTS(outf, effect); + VFS_PUTS(outf, "}\n"); + part_type[assoc].assoc = n; + assoc = n; + + if (assoc == P_INVALID) + break; + + if (strchr(part_type[assoc].name, '+')) + Q_snprintfz(effect, sizeof(effect), "+%s", part_type[i].name); + } + } + VFS_CLOSE(outf); + + FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + Con_Printf("Written %s\n", effect); +} +#endif /* =============== @@ -2923,9 +3142,11 @@ static qboolean PScript_InitParticles (void) pe_script_enabled = true; +#ifndef NOLEGACY Cmd_AddCommand("r_exportbuiltinparticles", P_ExportBuiltinSet_f); - Cmd_AddCommand("r_importeffectinfo", P_ImportEffectInfo_f); + Cmd_AddCommandD("r_converteffectinfo", P_ConvertEffectInfo_f, "Attempt to convert particle effects made for a certain other engine."); Cmd_AddCommand("r_exportalleffects", P_ExportAllEffects_f); +#endif //#if _DEBUG Cmd_AddCommand("r_partinfo", P_PartInfo_f); @@ -3152,13 +3373,6 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit) } } - if (!strcmp(name, "effectinfo")) - { - P_ImportEffectInfo_f(); - return true; - } - - FS_LoadFile(va("particles/%s.cfg", name), (void**)&file); if (!file) FS_LoadFile(va("%s.cfg", name), (void**)&file); @@ -3172,6 +3386,12 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit) else { #ifndef NOLEGACY + if (!strcmp(name, "effectinfo")) + { + P_ImportEffectInfo_Name(name); + return true; + } + if (P_LoadParticleSet("high", true)) Con_Printf(CON_WARNING "Couldn't find particle description %s, loading 'high' instead\n", name); else @@ -3567,7 +3787,7 @@ static vec2_t avelocities[NUMVERTEXNORMALS]; -static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t efdir, int pno, int pmax, part_type_t *ptype) +static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t axis[3], int pno, int pmax, part_type_t *ptype) { vec3_t ofsvec, arsvec; @@ -3627,11 +3847,9 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t e } - - // randomvel - ovel[0] = crandom()*ptype->randomvel; - ovel[1] = crandom()*ptype->randomvel; - ovel[2] = crandom()*ptype->randomvelvert + ptype->randomvelvertbias; + ovel[0] = 0; + ovel[1] = 0; + ovel[2] = 0; // handle spawn modes (org/vel) switch (ptype->spawnmode) @@ -3770,13 +3988,22 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t e break; } + k = ptype->orgadd + frandom()*ptype->randomorgadd; + l = ptype->veladd + frandom()*ptype->randomveladd; + +#if 1 + VectorMA(ovel, ofsvec[0]*ptype->spawnvel, axis[0], ovel); + VectorMA(ovel, ofsvec[1]*ptype->spawnvel, axis[1], ovel); + VectorMA(ovel, l+ofsvec[2]*ptype->spawnvelvert, axis[2], ovel); + + VectorMA(eforg, arsvec[0], axis[0], oorg); + VectorMA(oorg, arsvec[1], axis[1], oorg); + VectorMA(oorg, k+arsvec[2], axis[2], oorg); +#else oorg[0] = eforg[0] + arsvec[0]; oorg[1] = eforg[1] + arsvec[1]; oorg[2] = eforg[2] + arsvec[2]; - k = ptype->orgadd + frandom()*ptype->randomorgadd; - l = ptype->veladd + frandom()*ptype->randomveladd; - // apply arsvec+ofsvec if (efdir) { @@ -3796,6 +4023,18 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t e oorg[2] -= k; } +#endif + + if (ptype->flags & PT_WORLDSPACERAND) + { + oorg[0] += crand() * ptype->orgwrand[0]; + oorg[1] += crand() * ptype->orgwrand[1]; + oorg[2] += crand() * ptype->orgwrand[2]; + ovel[0] += crand() * ptype->velwrand[0]; + ovel[1] += crand() * ptype->velwrand[1]; + ovel[2] += crand() * ptype->velwrand[2]; + VectorAdd(ovel, ptype->velbias, ovel); + } VectorAdd(oorg, ptype->orgbias, oorg); } @@ -3815,11 +4054,11 @@ static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3] mod = &ptype->models[rand() % ptype->nummodels]; if (!mod->model) mod->model = Mod_ForName(mod->name, MLV_WARN); - if (mod->model && mod->model->loadstate == MLS_LOADED) + if (mod->model) { vec3_t morg, mdir; - PScript_ApplyOrgVel(morg, mdir, org, axis[0], i, count, ptype); - CL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?axis[2]:NULL, mod->model, mod->framestart, (mod->framecount?mod->framecount:(mod->model->numframes - mod->framestart)), mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, mod->rflags & ~RF_USEORIENTATION, mod->skin); + PScript_ApplyOrgVel(morg, mdir, org, axis, i, count, ptype); + CL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?axis[2]:NULL, mod->model, mod->framestart, mod->framecount, mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, mod->rflags & ~RF_USEORIENTATION, mod->skin); } } } @@ -3964,7 +4203,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, { part_type_t *ptype = &part_type[typenum]; int i, j, k, l, spawnspc; - float m, pcount, orgadd, veladd; + float m, pcount;//, orgadd, veladd; vec3_t axis[3]={{1,0,0},{0,1,0},{0,0,-1}}; particle_t *p; beamseg_t *b, *bfirst; @@ -4279,6 +4518,9 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die; p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die; +#if 1 + PScript_ApplyOrgVel(p->org, p->vel, org, axis, i, pcount, ptype); +#else // randomvel p->vel[0] = crandom()*ptype->randomvel; p->vel[1] = crandom()*ptype->randomvel; @@ -4460,7 +4702,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, } #endif VectorAdd(p->org, ptype->orgbias, p->org); - +#endif p->die = particletime + ptype->die - p->die; } @@ -4477,7 +4719,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, arsvec[0] = cos(m*(i-2)); arsvec[1] = sin(m*(i-2)); arsvec[2] = 0; - VectorSubtract(ofsvec, arsvec, bfirst->dir); + VectorSubtract(b->p->org, arsvec, bfirst->dir); VectorNormalize(bfirst->dir); break; default: @@ -4558,19 +4800,19 @@ static void PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int co ptype = P_FindParticleType(va("pe_%i", color)); if (P_RunParticleEffectType(org, dir, count, ptype)) { - if (count > 130 && pe_size3 != P_INVALID) + if (count > 130 && PART_VALID(pe_size3)) { part_type[pe_size3].colorindex = color & ~0x7; part_type[pe_size3].colorrand = 8; P_RunParticleEffectType(org, dir, count, pe_size3); } - else if (count > 20 && pe_size2 != P_INVALID) + else if (count > 20 && PART_VALID(pe_size2)) { part_type[pe_size2].colorindex = color & ~0x7; part_type[pe_size2].colorrand = 8; P_RunParticleEffectType(org, dir, count, pe_size2); } - else if (pe_default != P_INVALID) + else if (PART_VALID(pe_default)) { part_type[pe_default].colorindex = color & ~0x7; part_type[pe_default].colorrand = 8; @@ -4590,12 +4832,20 @@ static void PScript_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, in vec3_t nvel; int ptype = P_FindParticleType(va("pe2_%i_%i", effect, color)); - if (ptype < 0) + if (!PART_VALID(ptype)) { ptype = P_FindParticleType(va("pe2_%i", effect)); - if (ptype < 0) + if (!PART_VALID(ptype)) + { + if (fallback) + { + fallback->RunParticleEffect2(org, dmin, dmax, color, effect, count); + return; + } ptype = pe_default; - + } + if (!PART_VALID(ptype)) + return; part_type[ptype].colorindex = color; } @@ -4632,12 +4882,20 @@ static void PScript_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int e float invcount; int ptype = P_FindParticleType(va("pe3_%i_%i", effect, color)); - if (ptype < 0) + if (!PART_VALID(ptype)) { ptype = P_FindParticleType(va("pe3_%i", effect)); - if (ptype < 0) + if (!PART_VALID(ptype)) + { +// if (fallback) +// { +// fallback->RunParticleEffect3(org, box, color, effect, count); +// return; +// } ptype = pe_default; - + } + if (!PART_VALID(ptype)) + return; part_type[ptype].colorindex = color; } @@ -4674,12 +4932,20 @@ static void PScript_RunParticleEffect4 (vec3_t org, float radius, int color, int float invcount; int ptype = P_FindParticleType(va("pe4_%i_%i", effect, color)); - if (ptype < 0) + if (!PART_VALID(ptype)) { ptype = P_FindParticleType(va("pe4_%i", effect)); - if (ptype < 0) + if (!PART_VALID(ptype)) + { +// if (fallback) +// { +// fallback->RunParticleEffect4(org, radius, color, effect, count); +// return; +// } ptype = pe_default; - + } + if (!PART_VALID(ptype)) + return; part_type[ptype].colorindex = color; } @@ -4707,14 +4973,15 @@ static void PScript_RunParticleCube(int ptype, vec3_t minb, vec3_t maxb, vec3_t float num; float invcount; - if (ptype < 0) + if (!PART_VALID(ptype)) ptype = P_FindParticleType(va("te_cube%s_%i", gravity?"_g":"", colour)); - if (ptype < 0) + if (!PART_VALID(ptype)) { ptype = P_FindParticleType(va("te_cube%s", gravity?"_g":"")); - if (ptype < 0) + if (!PART_VALID(ptype)) ptype = pe_default; - + if (!PART_VALID(ptype)) + return; part_type[ptype].colorindex = colour; } @@ -4743,12 +5010,13 @@ static void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, flo float invcount; int ptype = P_FindParticleType(va("te_%s_%i", efname, colour)); - if (ptype < 0) + if (!PART_VALID(ptype)) { ptype = P_FindParticleType(va("te_%s", efname)); - if (ptype < 0) + if (!PART_VALID(ptype)) ptype = pe_default; - + if (!PART_VALID(ptype)) + return; part_type[ptype].colorindex = colour; } @@ -4769,6 +5037,24 @@ static void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, flo } } +static void PScript_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count) +{ + int ptype; + + ptype = P_FindParticleType(va("%s_%i", nameprefix, color)); + if (ptype != P_INVALID) + P_RunParticleEffectType(org, dir, count, ptype); + else + { + ptype = P_FindParticleType(nameprefix); + if (!PART_VALID(ptype)) + { + part_type[ptype].colorindex = color; + P_RunParticleEffectType(org, dir, count, ptype); + } + } +} + static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3]) { vec3_t vec, vstep, right, up, start; @@ -4781,8 +5067,6 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype int count; float veladd = -ptype->veladd; - float randvel = ptype->randomvel; - float randvelvert = ptype->randomvelvert; float step; float stop; float tdegree = 2.0*M_PI/256; /* MSVC whine */ @@ -5044,9 +5328,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype if (len < nrfirst || len >= nrlast) { // no offset or areaspread for these particles... - p->vel[0] = vec[0]*veladd+crandom()*randvel; - p->vel[1] = vec[1]*veladd+crandom()*randvel; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert; + p->vel[0] = vec[0]*veladd; + p->vel[1] = vec[1]*veladd; + p->vel[2] = vec[2]*veladd; VectorCopy(start, p->org); } @@ -5070,9 +5354,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype p->org[1] = vec[0]*ptype->areaspread; } - p->vel[0] += vec[0]*veladd+crandom()*randvel; - p->vel[1] += vec[1]*veladd+crandom()*randvel; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert; + p->vel[0] += vec[0]*veladd; + p->vel[1] += vec[1]*veladd; + p->vel[2] = vec[2]*veladd; p->org[0] += start[0]; p->org[1] += start[1]; @@ -5096,9 +5380,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype tright = tcos*ptype->spawnvel; tup = tsin*ptype->spawnvel; - p->vel[0] = vec[0]*veladd+crandom()*randvel + right[0]*tright + up[0]*tup; - p->vel[1] = vec[1]*veladd+crandom()*randvel + right[1]*tright + up[1]*tup; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert + right[2]*tright + up[2]*tup; + p->vel[0] = vec[0]*veladd + right[0]*tright + up[0]*tup; + p->vel[1] = vec[1]*veladd + right[1]*tright + up[1]*tup; + p->vel[2] = vec[2]*veladd + right[2]*tright + up[2]*tup; } break; // TODO: directionalize SM_BALL/SM_CIRCLE/SM_DISTBALL @@ -5109,9 +5393,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype VectorNormalize(p->org); VectorScale(p->org, frandom(), p->org); - p->vel[0] = vec[0]*veladd+crandom()*randvel + p->org[0]*ptype->spawnvel; - p->vel[1] = vec[1]*veladd+crandom()*randvel + p->org[1]*ptype->spawnvel; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert + p->org[2]*ptype->spawnvelvert; + p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel; + p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel; + p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert; p->org[0] = p->org[0]*ptype->areaspread + start[0]; p->org[1] = p->org[1]*ptype->areaspread + start[1]; @@ -5132,9 +5416,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype tcos = cos(len*tdegree)*ptype->spawnvel; tsin = sin(len*tdegree)*ptype->spawnvel; - p->vel[0] = vec[0]*veladd+crandom()*randvel + right[0]*tcos + up[0]*tsin; - p->vel[1] = vec[1]*veladd+crandom()*randvel + right[1]*tcos + up[1]*tsin; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert + right[2]*tcos + up[2]*tsin; + p->vel[0] = vec[0]*veladd + right[0]*tcos + up[0]*tsin; + p->vel[1] = vec[1]*veladd + right[1]*tcos + up[1]*tsin; + p->vel[2] = vec[2]*veladd + right[2]*tcos + up[2]*tsin; } break; @@ -5154,9 +5438,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype VectorNormalize(p->org); VectorScale(p->org, rdist, p->org); - p->vel[0] = vec[0]*veladd+crandom()*randvel + p->org[0]*ptype->spawnvel; - p->vel[1] = vec[1]*veladd+crandom()*randvel + p->org[1]*ptype->spawnvel; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert + p->org[2]*ptype->spawnvelvert; + p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel; + p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel; + p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert; p->org[0] = p->org[0]*ptype->areaspread + start[0]; p->org[1] = p->org[1]*ptype->areaspread + start[1]; @@ -5168,9 +5452,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype p->org[1] = crandom(); p->org[2] = crandom(); - p->vel[0] = vec[0]*veladd+crandom()*randvel + p->org[0]*ptype->spawnvel; - p->vel[1] = vec[1]*veladd+crandom()*randvel + p->org[1]*ptype->spawnvel; - p->vel[2] = vec[2]*veladd+crandom()*randvelvert + p->org[2]*ptype->spawnvelvert; + p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel; + p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel; + p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert; p->org[0] = p->org[0]*ptype->areaspread + start[0]; p->org[1] = p->org[1]*ptype->areaspread + start[1]; @@ -5184,7 +5468,18 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype p->org[1] += vec[1]*ptype->orgadd; p->org[2] += vec[2]*ptype->orgadd; } -// VectorAdd(p->org, ptype->orgbias, p->org); + + if (ptype->flags & PT_WORLDSPACERAND) + { + p->org[0] += crand() * ptype->orgwrand[0]; + p->org[1] += crand() * ptype->orgwrand[1]; + p->org[2] += crand() * ptype->orgwrand[2]; + p->vel[0] += crand() * ptype->velwrand[0]; + p->vel[1] += crand() * ptype->velwrand[1]; + p->vel[2] += crand() * ptype->velwrand[2]; + VectorAdd(p->vel, ptype->velbias, p->vel); + } + VectorAdd(p->org, ptype->orgbias, p->org); } VectorAdd (start, vstep, start); @@ -5292,7 +5587,7 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlk static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk) { - if (pe_defaulttrail != P_INVALID) + if (PART_VALID(pe_defaulttrail)) { part_type[pe_defaulttrail].colorindex = color; part_type[pe_defaulttrail].colorrand = crnd; @@ -6675,6 +6970,7 @@ particleengine_t pe_script = PScript_RunParticleEffect2, PScript_RunParticleEffect3, PScript_RunParticleEffect4, + PScript_RunParticleEffectPalette, PScript_ParticleTrailIndex, PScript_EmitSkyEffectTris, diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 59f8933f3..af497d62b 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -4593,24 +4593,24 @@ static void QCBUILTIN PF_getentity(pubprogfuncs_t *prinst, struct globalvars_s * G_FLOAT(OFS_RETURN) = es->skinnum; break; case GE_MINS: - G_FLOAT(OFS_RETURN+0) = -(es->solid & 31); - G_FLOAT(OFS_RETURN+1) = -(es->solid & 31); - G_FLOAT(OFS_RETURN+2) = -((es->solid>>5) & 31); + G_FLOAT(OFS_RETURN+0) = -(int)(es->solid & 31); + G_FLOAT(OFS_RETURN+1) = -(int)(es->solid & 31); + G_FLOAT(OFS_RETURN+2) = -(int)((es->solid>>5) & 31); break; case GE_MAXS: G_FLOAT(OFS_RETURN+0) = (es->solid & 31); G_FLOAT(OFS_RETURN+1) = (es->solid & 31); - G_FLOAT(OFS_RETURN+1) = ((es->solid>>10) & 63) - 32; + G_FLOAT(OFS_RETURN+2) = ((es->solid>>10) & 63) - 32; break; case GE_ABSMIN: - G_FLOAT(OFS_RETURN+0) = le->origin[0] + -(es->solid & 31); - G_FLOAT(OFS_RETURN+1) = le->origin[1] + -(es->solid & 31); - G_FLOAT(OFS_RETURN+2) = le->origin[2] + -((es->solid>>5) & 31); + G_FLOAT(OFS_RETURN+0) = le->origin[0] + -(int)(es->solid & 31); + G_FLOAT(OFS_RETURN+1) = le->origin[1] + -(int)(es->solid & 31); + G_FLOAT(OFS_RETURN+2) = le->origin[2] + -(int)((es->solid>>5) & 31); break; case GE_ABSMAX: G_FLOAT(OFS_RETURN+0) = le->origin[0] + (es->solid & 31); G_FLOAT(OFS_RETURN+1) = le->origin[1] + (es->solid & 31); - G_FLOAT(OFS_RETURN+1) = le->origin[2] + ((es->solid>>10) & 63) - 32; + G_FLOAT(OFS_RETURN+2) = le->origin[2] + ((es->solid>>10) & 63) - 32; break; case GE_ORIGINANDVECTORS: VectorCopy(le->origin, G_VECTOR(OFS_RETURN)); @@ -5408,7 +5408,7 @@ static struct { {"setmodelindex", PF_cs_SetModelIndex, 333}, // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC) {"modelnameforindex", PF_cs_ModelnameForIndex, 334}, // #334 string(float mdlindex) modelnameforindex (EXT_CSQC) - {"particleeffectnum", PF_cs_particleeffectnum, 335}, // #335 float(string effectname) particleeffectnum (EXT_CSQC) + {"particleeffectnum", PF_cs_particleeffectnum, 335}, // #335 float(string effectname) particleeffectnum (EXT_CSQC) {"trailparticles", PF_cs_trailparticles, 336}, // #336 void(float effectnum, entity ent, vector start, vector end) trailparticles (EXT_CSQC), {"trailparticles_dp", PF_cs_trailparticles, 336}, // #336 DP sucks {"pointparticles", PF_cs_pointparticles, 337}, // #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC) diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 3eeef6113..306b43b48 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -83,7 +83,7 @@ void QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvar G_FLOAT(OFS_RETURN) = 1; } -#define FONT_SLOTS 16 +#define FONT_SLOTS 32 #define FONT_SIZES 16 struct { unsigned int owner; //kdm_foo. whoever has an interest in this font. font is purged when this becomes 0. @@ -153,6 +153,17 @@ int PR_findnamedfont(const char *name, qboolean isslotname) } return -1; } +int PR_findunusedfont(void) +{ + int i; + //don't find slot 0. + for (i = FONT_SLOTS; i-- > 1; ) + { + if (!*fontslot[i].slotname && !*fontslot[i].facename) + return i; + } + return -1; +} //purgeowner is the bitmask of owners that are getting freed. //if purgeowner is 0, fonts will get purged void PR_ReleaseFonts(unsigned int purgeowner) @@ -232,7 +243,7 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g else if (slotnum < 0) slotnum = PR_findnamedfont(facename, false); if (slotnum < 0) - slotnum = PR_findnamedfont("", true); + slotnum = PR_findunusedfont(); if (slotnum < 0) return; //eep. diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 68a93bc0d..1226b4935 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -3442,6 +3442,21 @@ char *particle_set_q2part = "scalefactor 0.8\n" "}\n" +"r_part TEQ2_LASER_SPARKS\n" +"{\n" +"texture \"classicparticle\"\n" +"tcoords 0 0 16 16 32\n" +"count 1\n" +"scale 1\n" +"alpha 1\n" +"die 0.3 0.8\n" +"randomvel 20\n" +"orgadd 0 7\n" +"spawnorg 4\n" +"gravity 40\n" +"scalefactor 0.8\n" +"}\n" + "r_part te_splashsparks\n" "{\n" "texture \"classicparticle\"\n" @@ -3692,7 +3707,7 @@ char *particle_set_q2part = "lightradius 150\n" "lightradiusfade 400\n" "lightrgb 1 1 0\n" -"lightshadows 0\n" +"lightshadows 1\n" "sound \"weapons/lashit.wav\" 1 1 0 0\n" "}\n" "r_part teq2_blaster2\n" @@ -3714,7 +3729,7 @@ char *particle_set_q2part = "lightradius 150\n" "lightradiusfade 400\n" "lightrgb 0.05 1.0 0.05\n" -"lightshadows 0\n" +"lightshadows 1\n" "sound \"weapons/lashit.wav\" 1 1 0 0\n" "}\n" "r_part TR_BLASTERTRAIL\n" @@ -3729,6 +3744,10 @@ char *particle_set_q2part = "randomvel 5\n" "die 0.3 0.5\n" "colorindex 0xe0\n" +"lightradius 200\n" +"lightradiusfade 400\n" +"lightrgb 1.0 1.0 0.0\n" +"lightshadows 1\n" "}\n" //green version @@ -3744,6 +3763,10 @@ char *particle_set_q2part = "randomvel 5\n" "die 0.3 0.5\n" "colorindex 0xd0\n" +"lightradius 200\n" +"lightradiusfade 400\n" +"lightrgb 0.0 1.0 0.0\n" +"lightshadows 1\n" "}\n" @@ -3914,12 +3937,25 @@ char *particle_set_q2part = "}\n" "r_part trq2_gib\n" "{\n" -"assoc tr_gib\n" +"texture \"particles/quake\"\n" +"step 3\n" +"scale 4\n" +"die 1.0 1.4\n" +"colorindex 0xe8 7\n" +"spawnorg 1\n" +"spawnvel 5\n" +"gravity -20\n" "}\n" -//FIXME: implement "r_part trq2_greengib\n" "{\n" -"assoc tr_gib\n" +"texture \"particles/quake\"\n" +"step 3\n" +"scale 4\n" +"die 1.0 1.4\n" +"colorindex 0xdb 7\n" +"spawnorg 1\n" +"spawnvel 5\n" +"gravity -20\n" "}\n" "r_part TR_PLASMA\n" @@ -3951,6 +3987,14 @@ char *particle_set_q2part = "lightrgb 1.0 1.0 0.0\n" "}\n" +//FIXME: add particles +"r_part tr_trap\n" +"{\n" +"lighttime 0\n" +"lightradius 100 200\n" +"lightrgb 1.0 0.8 0.25\n" +"}\n" + //flags do NOT use coronas, because it obscures the holding player's skin colour "r_part tr_flag1\n" "{\n" @@ -3989,15 +4033,6 @@ char *particle_set_q2part = "lightrgb 0.25 0.25 1.0\n" "}\n" - -//FIXME: add particles -"r_part tr_trap\n" -"{\n" -"lighttime 0\n" -"lightradius 100 200\n" -"lightrgb 1.0 0.8 0.25\n" -"}\n" - "r_part EF_FLIES\n" "{\n" "texture \"classicparticle\"\n" @@ -4095,6 +4130,28 @@ char *particle_set_q2part = "sound \"weapons/xpld_wat.wav\" 1 1 0 0\n" "model \"sprites/s_bfg2.sp2\" framestart=0 frameend=4 alpha=0.3 transparent fullbright noshadow\n" "}\n" + + +//31qu cylinder, 8-98 high +//should look like its sucked up into some thingie above +"r_part TEQ2_BOSSTPORT\n" +"{\n" +"texture \"classicparticle\"\n" +"tcoords 0 0 16 16 32\n" +"count 800\n" +"scale 1\n" +"alpha 1\n" +"die 0.5 0.8\n" +"orgadd 8 -98\n" +"veladd 100 200\n" +"spawnmode circle\n" +"spawnorg 48 0\n" +"spawnvel -50 30\n" +"randomvel 32 31\n" +"gravity -800\n" +"rgbf 1 1 1\n" +"scalefactor 0.8\n" +"}\n" ; #endif diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index bdaa09d8f..eec1fa4ce 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -2683,10 +2683,12 @@ void R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_) webogeneratingstate = 0; mod = webostate->wmodel; + GL_DeselectVAO(); + qglGenBuffersARB(1, &webostate->ebo); for (i = 0, idxcount = 0; i < webostate->numbatches; i++) idxcount += webostate->batches[i].numidx; - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, webostate->ebo); + GL_SelectEBO(webostate->ebo); qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, idxcount*sizeof(index_t), NULL, GL_STATIC_DRAW_ARB); for (i = 0, idxcount = 0; i < webostate->numbatches; i++) { @@ -2755,7 +2757,7 @@ static void Surf_SimpleWorld(struct webostate_s *es, qbyte *pvs) { //FIXME: pre-allocate // continue; - eb->maxidx = eb->numidx + surf->mesh->numindexes; + eb->maxidx = eb->numidx + surf->mesh->numindexes + 512; eb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t)); } for (i = 0; i < mesh->numindexes; i++) diff --git a/engine/client/renderer.c b/engine/client/renderer.c index f60492720..942d68591 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -1025,7 +1025,7 @@ void D3DSucks(void) Sys_Error("Failed to reload content after mode switch\n"); } -void R_ShutdownRenderer(qboolean videotoo) +void R_ShutdownRenderer(qboolean devicetoo) { //make sure the worker isn't still loading stuff COM_WorkerFullSync(); @@ -1048,7 +1048,7 @@ void R_ShutdownRenderer(qboolean videotoo) if (Draw_Shutdown) Draw_Shutdown(); - if (VID_DeInit && videotoo) + if (VID_DeInit && devicetoo) { TRACE(("dbg: R_ApplyRenderer: VID_DeInit\n")); VID_DeInit(); @@ -1070,7 +1070,10 @@ void R_ShutdownRenderer(qboolean videotoo) RQ_Shutdown(); - S_Shutdown(false); + if (devicetoo) + S_Shutdown(false); + else + S_StopAllSounds (true); } void R_GenPaletteLookup(void) @@ -1363,7 +1366,7 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n")); TRACE(("dbg: R_ApplyRenderer: S_Restart_f\n")); if (!isDedicated) - S_DoRestart(); + S_DoRestart(true); #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) diff --git a/engine/client/sbar.c b/engine/client/sbar.c index f7b540e29..5c6a5f8a5 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -335,6 +335,8 @@ char *Get_Q2ConfigString(int i) return cl.item_name[i-Q2CS_ITEMS]?cl.item_name[i-Q2CS_ITEMS]:""; if (i == Q2CS_STATUSBAR) return cl.q2statusbar; + if (i == Q2CS_NAME) + return cl.levelname; if (i >= Q2CS_MODELS && i < Q2CS_MODELS + Q2MAX_MODELS) return cl.model_name [i-Q2CS_MODELS]; @@ -342,6 +344,8 @@ char *Get_Q2ConfigString(int i) return cl.model_name [i-Q2CS_SOUNDS]; if (i == Q2CS_AIRACCEL) return "4"; + if (i >= Q2CS_PLAYERSKINS && i < Q2CS_GENERAL+Q2MAX_GENERAL) + return cl.configstring_general[i-Q2CS_PLAYERSKINS]?cl.configstring_general[i-Q2CS_PLAYERSKINS]:""; //#define Q2CS_LIGHTS (Q2CS_IMAGES +Q2MAX_IMAGES) //#define Q2CS_ITEMS (Q2CS_LIGHTS +Q2MAX_LIGHTSTYLES) //#define Q2CS_PLAYERSKINS (Q2CS_ITEMS +Q2MAX_ITEMS) @@ -763,6 +767,10 @@ void Sbar_ShowScores (void) void Sbar_Hexen2InvLeft_f(void) { +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif if (cls.protocol == CP_QUAKE2) { CL_SendClientCommand(true, "invprev"); @@ -786,6 +794,10 @@ void Sbar_Hexen2InvLeft_f(void) } void Sbar_Hexen2InvRight_f(void) { +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif if (cls.protocol == CP_QUAKE2) { CL_SendClientCommand(true, "invnext"); @@ -809,6 +821,11 @@ void Sbar_Hexen2InvRight_f(void) } void Sbar_Hexen2InvUse_f(void) { +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif + if (cls.protocol == CP_QUAKE2) { CL_SendClientCommand(true, "invuse"); @@ -823,21 +840,37 @@ void Sbar_Hexen2InvUse_f(void) void Sbar_Hexen2ShowInfo_f(void) { playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif pv->sb_hexen2_extra_info = true; } void Sbar_Hexen2DontShowInfo_f(void) { playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif pv->sb_hexen2_extra_info = false; } void Sbar_Hexen2PInfoPlaque_f(void) { playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif pv->sb_hexen2_infoplaque = true; } void Sbar_Hexen2MInfoPlaque_f(void) { playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; +#ifdef CSQC_DAT + if (CSQC_ConsoleCommand(Cmd_Argv(0))) + return; +#endif pv->sb_hexen2_infoplaque = false; } @@ -2326,7 +2359,7 @@ static void Sbar_Hexen2DrawExtra (playerview_t *pv) pclass = cl.players[pv->playernum].h2playerclass; if (pclass >= sizeof(pclassname)/sizeof(pclassname[0])) - pclass = sizeof(pclassname)/sizeof(pclassname[0]) - 1; + pclass = 0; //adjust it so there's space diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 0942200db..df4090175 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -1785,9 +1785,11 @@ void S_Startup (void) //why isn't this part of S_Restart_f anymore? //so that the video code can call it directly without flushing the models it's just loaded. -void S_DoRestart (void) +void S_DoRestart (qboolean onlyifneeded) { int i; + if (onlyifneeded && sound_started) + return; //don't need to if its already running. S_StopAllSounds (true); S_Shutdown(false); @@ -1810,7 +1812,7 @@ void S_DoRestart (void) void S_Restart_f (void) { - S_DoRestart(); + S_DoRestart(false); } void S_Control_f (void) @@ -2114,7 +2116,14 @@ void S_Purge(qboolean retaintouched) sfx = &known_sfx[i]; /*don't hurt sounds if they're being processed by a worker thread*/ if (sfx->loadstate == SLS_LOADING) - continue; + { + if (retaintouched) + continue; //don't bother waiting + + //trying to shut down or something. + //make sure there's no worker about to write to sfx after the memory is freed + COM_WorkerPartialSync(sfx, &sfx->loadstate, SLS_LOADING); + } /*don't purge the file if its still relevent*/ if (retaintouched && sfx->touched) diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index b47d15fb7..34c556d0c 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -890,7 +890,6 @@ void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { if (AudioInputPlugins[i](s, data, filesize, snd_speed)) { - s->loadstate = SLS_LOADED; //wake up the main thread in case it decided to wait for us. COM_AddWork(0, S_LoadedOrFailed, s, NULL, SLS_LOADED, 0); BZ_Free(data); diff --git a/engine/client/sound.h b/engine/client/sound.h index 6788f9a6d..bc96016f7 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -194,7 +194,7 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch); void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int insamps, void *out, int outrate, int outwidth, int outchannels, int resampstyle); // restart entire sound subsystem (doesn't flush old sounds, so make sure that happens) -void S_DoRestart (void); +void S_DoRestart (qboolean onlyifneeded); void S_Restart_f (void); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 54ee1e5e1..3f4550fea 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -786,7 +786,7 @@ STAT_H2_CNT_INVINCIBILITY, // changes stat bar STAT_H2_ARTIFACT_ACTIVE, STAT_H2_ARTIFACT_LOW, STAT_H2_MOVETYPE, -STAT_H2_CAMERAMODE, +STAT_H2_CAMERAMODE, //entity STAT_H2_HASTED, STAT_H2_INVENTORY, STAT_H2_RINGS_ACTIVE, @@ -800,21 +800,21 @@ STAT_H2_FLIGHT_T, STAT_H2_WATER_T, STAT_H2_TURNING_T, STAT_H2_REGEN_T, -STAT_H2_PUZZLE1, -STAT_H2_PUZZLE2, -STAT_H2_PUZZLE3, -STAT_H2_PUZZLE4, -STAT_H2_PUZZLE5, -STAT_H2_PUZZLE6, -STAT_H2_PUZZLE7, -STAT_H2_PUZZLE8, +STAT_H2_PUZZLE1, //string +STAT_H2_PUZZLE2, //string +STAT_H2_PUZZLE3, //string +STAT_H2_PUZZLE4, //string +STAT_H2_PUZZLE5, //string +STAT_H2_PUZZLE6, //string +STAT_H2_PUZZLE7, //string +STAT_H2_PUZZLE8, //string STAT_H2_MAXHEALTH, STAT_H2_MAXMANA, STAT_H2_FLAGS, STAT_H2_PLAYERCLASS, -STAT_H2_OBJECTIVE1, -STAT_H2_OBJECTIVE2, +STAT_H2_OBJECTIVE1, //integer +STAT_H2_OBJECTIVE2, //integer STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220, // DP diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 6bb08aab6..15da37411 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1778,6 +1778,36 @@ void Cmd_RestrictCommand_f (void) return; } +void Cmd_EnumerateLevel(int level, char *buf, size_t bufsize) +{ + cmdalias_t *a; + cmd_function_t *cmds; + int cmdlevel; + *buf = 0; + for (cmds = cmd_functions; cmds; cmds=cmds->next) + { + cmdlevel = cmds->restriction?cmds->restriction:rcon_level.ival; + + if (level == cmdlevel) + { + if (*buf) + Q_strncatz(buf, "\t", bufsize); + Q_strncatz(buf, cmds->name, bufsize); + } + } + for (a=cmd_alias ; a ; a=a->next) + { + cmdlevel = a->restriction?a->restriction:rcon_level.ival; + + if (level == cmdlevel) + { + if (*buf) + Q_strncatz(buf, "\t", bufsize); + Q_strncatz(buf, cmds->name, bufsize); + } + } +} + int Cmd_Level(char *name) { cmdalias_t *a; @@ -1793,7 +1823,7 @@ int Cmd_Level(char *name) { if (!strcmp(a->name, name)) { - return a->restriction?a->restriction:Cmd_ExecLevel; + return a->restriction?a->restriction:rcon_level.ival; } } return -1; diff --git a/engine/common/cmd.h b/engine/common/cmd.h index 282392b81..bf5a77668 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -73,6 +73,7 @@ then searches for a command or variable that matches the first token. typedef void (*xcommand_t) (void); int Cmd_Level(char *name); +void Cmd_EnumerateLevel(int level, char *buf, size_t bufsize); void Cmd_Init (void); void Cmd_Shutdown(void); diff --git a/engine/common/common.h b/engine/common/common.h index b7db76f8c..5228c25f0 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -108,8 +108,12 @@ typedef enum {false, true} qboolean; struct netprim_s { - int coordsize; - int anglesize; + qbyte coordsize; + qbyte anglesize; +#define NPQ2_ANG16 (1<<0) +#define NPQ2_SIZE32 (1<<0) + qbyte q2flags; + qbyte pad; }; //============================================================================ diff --git a/engine/common/fs.c b/engine/common/fs.c index 291b3d392..c4ab2a85c 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -2631,7 +2631,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) FS_ChangeGame(man, cfg_reload_on_gamedir.ival, false); } -#define QCFG "set com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" +#define QCFG "set com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\nsv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" /*stuff that makes dp-only mods work a bit better*/ #define DPCOMPAT QCFG "set _cl_playermodel \"\"\n set dpcompat_set 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n" /*nexuiz/xonotic has a few quirks/annoyances...*/ diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 507d995de..a9bcf9354 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -83,6 +83,7 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s) mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; + int idx; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; @@ -92,10 +93,13 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s) for (i=0 ; inumedges ; i++) { e = mod->surfedges[s->firstedge+i]; - if (e >= 0) - v = &mod->vertexes[mod->edges[e].v[0]]; + idx = e < 0; + if (idx) + e = -e; + if (e < 0 || e >= mod->numedges) + v = &mod->vertexes[0]; else - v = &mod->vertexes[mod->edges[-e].v[1]]; + v = &mod->vertexes[mod->edges[e].v[idx]]; for (j=0 ; j<2 ; j++) { diff --git a/engine/common/net.h b/engine/common/net.h index 96ad9df43..c039372c0 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -167,6 +167,7 @@ typedef struct netadr_t remote_address; netsrc_t sock; int qport; + int qportsize; // bandwidth estimator double cleartime; // if realtime > nc->cleartime, free to go diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index bd51ab8a1..fc7557eeb 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -342,6 +342,8 @@ void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t *adr, int qport) chan->message.maxsize = MAX_QWMSGLEN; chan->qport = qport; + + chan->qportsize = 2; } @@ -701,7 +703,12 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) // send the qport if we are a client #ifndef SERVERONLY if (chan->sock == NS_CLIENT) - MSG_WriteShort (&send, cls.qport); + { + if (chan->qportsize == 2) + MSG_WriteShort (&send, chan->qport); + else if (chan->qportsize == 1) + MSG_WriteByte (&send, chan->qport&0xff); + } #endif if (chan->fragmentsize) @@ -742,7 +749,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (!cls.demoplayback) #endif { - int hsz = 10 + ((chan->sock == NS_CLIENT)?2:0); /*header size, if fragmentation is in use*/ + int hsz = 10 + ((chan->sock == NS_CLIENT)?chan->qportsize:0); /*header size, if fragmentation is in use*/ if ((!chan->fragmentsize) || send.cursize-hsz < ((chan->fragmentsize - hsz)&~7)) { @@ -778,7 +785,12 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) *(int*)&send.data[(offset) + 4] = LittleLong(w2); #ifndef SERVERONLY if (chan->sock == NS_CLIENT) - *(short*)&send.data[offset + hsz-4] = LittleShort(cls.qport); + { + if (chan->qportsize == 2) + *(short*)&send.data[offset + hsz-4] = LittleShort(chan->qport); + else if (chan->qportsize == 1) + *(qbyte*)&send.data[offset + hsz-3] = chan->qport&0xff; + } #endif *(short*)&send.data[offset + hsz-2] = LittleShort((offset>>2) | (more?1:0)); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 3c7112c6a..2f66348c7 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -2089,60 +2089,86 @@ static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char } return count > 0; } -qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, const char *address, netadrtype_t addrtype, qboolean islisten) +qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, const char *addresslist, netadrtype_t addrtype, qboolean islisten) { - ftenet_generic_connection_t *(*establish)(qboolean isserver, const char *address, netadr_t adr) = NULL; - netadr_t adr; + netadr_t adr[8]; + ftenet_generic_connection_t *(*establish[countof(adr)])(qboolean isserver, const char *address, netadr_t adr); + char address[countof(adr)][256]; + unsigned int i, j; + qboolean success; - //resolve the address to something sane so we can determine the address type and thus the connection type to use - if (!address || !*address) - adr.type = NA_INVALID; - else if (islisten) - NET_PortToAdr(addrtype, address, &adr); - else - NET_StringToAdr(address, 0, &adr); + if (strchr(name, ':')) + return false; - switch(adr.type) + for (i = 0; addresslist && *addresslist && i < countof(adr); i++) { - default: establish = NULL; break; + addresslist = COM_ParseStringSet(addresslist, address[i], sizeof(address[i])); + //resolve the address to something sane so we can determine the address type and thus the connection type to use + if (!*address[i]) + adr[i].type = NA_INVALID; + else if (islisten) + NET_PortToAdr(addrtype, address[i], &adr[i]); + else + NET_StringToAdr(address[i], 0, &adr[i]); + + switch(adr[i].type) + { + default: establish[i] = NULL; break; #ifdef HAVE_NATPMP - case NA_NATPMP: establish = FTENET_NATPMP_EstablishConnection; break; + case NA_NATPMP: establish[i] = FTENET_NATPMP_EstablishConnection; break; #endif #if !defined(CLIENTONLY) && !defined(SERVERONLY) - case NA_LOOPBACK: establish = FTENET_Loop_EstablishConnection; break; + case NA_LOOPBACK: establish[i] = FTENET_Loop_EstablishConnection; break; #endif #ifdef HAVE_IPV4 - case NA_IP: establish = FTENET_UDP4_EstablishConnection; break; + case NA_IP: establish[i] = FTENET_UDP4_EstablishConnection; break; #endif #ifdef IPPROTO_IPV6 - case NA_IPV6: establish = FTENET_UDP6_EstablishConnection; break; + case NA_IPV6: establish[i] = FTENET_UDP6_EstablishConnection; break; #endif #ifdef USEIPX - case NA_IPX: establish = FTENET_IPX_EstablishConnection; break; + case NA_IPX: establish[i] = FTENET_IPX_EstablishConnection; break; #endif - case NA_WEBSOCKET: + case NA_WEBSOCKET: #ifdef HAVE_WEBSOCKCL - if (!islisten) - establish = FTENET_WebSocket_EstablishConnection; + if (!islisten) + establish[i] = FTENET_WebSocket_EstablishConnection; #endif #ifdef TCPCONNECT - establish = FTENET_TCP4Connect_EstablishConnection; + establish[i] = FTENET_TCP4Connect_EstablishConnection; #endif - break; + break; #ifdef IRCCONNECT - case NA_IRC: establish = FTENET_IRCConnect_EstablishConnection; break; + case NA_IRC: establish[i] = FTENET_IRCConnect_EstablishConnection; break; #endif #ifdef TCPCONNECT - case NA_TCP: establish = FTENET_TCP4Connect_EstablishConnection; break; - case NA_TLSV4: establish = FTENET_TLS4Connect_EstablishConnection; break; + case NA_TCP: establish[i] = FTENET_TCP4Connect_EstablishConnection; break; + case NA_TLSV4: establish[i] = FTENET_TLS4Connect_EstablishConnection; break; #endif #if defined(TCPCONNECT) && defined(IPPROTO_IPV6) - case NA_TCPV6: establish = FTENET_TCP6Connect_EstablishConnection; break; - case NA_TLSV6: establish = FTENET_TLS6Connect_EstablishConnection; break; + case NA_TCPV6: establish[i] = FTENET_TCP6Connect_EstablishConnection; break; + case NA_TLSV6: establish[i] = FTENET_TLS6Connect_EstablishConnection; break; #endif + } } - return FTENET_AddToCollection_Ptr(col, name, establish, islisten, address, &adr); + if (i == 1) + { + success |= FTENET_AddToCollection_Ptr(col, name, establish[0], islisten, address[0], &adr[0]); + i = 0; + } + else + success |= FTENET_AddToCollection_Ptr(col, name, NULL, islisten, NULL, NULL); + + for (j = 0; j < i; j++) + { + success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), establish[j], islisten, address[j], &adr[j]); + } + for (; j < countof(adr); j++) + { + success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), NULL, islisten, NULL, NULL); + } + return success; } void FTENET_CloseCollection(ftenet_connections_t *col) @@ -2589,7 +2615,11 @@ qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a) char *e; if (net_enabled.ival || adrfamily == NA_LOOPBACK) { - int port = strtoul(s, &e, 10); + int port; + if (!strncmp(s, "natpmp:", 7)) + return NET_StringToAdr2(s, 0, a, 1); + + port = strtoul(s, &e, 10); if (*e) //if *e then its not just a single number in there, so treat it as a proper address. return NET_StringToAdr(s, 0, a); else if (e != s) //if we actually read something (even a 0) diff --git a/engine/common/particles.h b/engine/common/particles.h index bb5729725..79f25af58 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -217,6 +217,7 @@ int P_FindParticleType(const char *efname); #define P_RunParticleEffect2 pe->RunParticleEffect2 #define P_RunParticleEffect3 pe->RunParticleEffect3 #define P_RunParticleEffect4 pe->RunParticleEffect4 +#define P_RunParticleEffectPalette pe->RunParticleEffectPalette #define P_ParticleTrailIndex pe->ParticleTrailIndex #define P_EmitSkyEffectTris pe->EmitSkyEffectTris @@ -241,6 +242,7 @@ typedef struct { void (*RunParticleEffect2) (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count); void (*RunParticleEffect3) (vec3_t org, vec3_t box, int color, int effect, int count); void (*RunParticleEffect4) (vec3_t org, float radius, int color, int effect, int count); + void (*RunParticleEffectPalette) (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count); void (*ParticleTrailIndex) (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk); void (*EmitSkyEffectTris) (struct model_s *mod, struct msurface_s *fa, int ptype); diff --git a/engine/common/pmove.c b/engine/common/pmove.c index 88795962d..9e84e9d10 100644 --- a/engine/common/pmove.c +++ b/engine/common/pmove.c @@ -93,6 +93,7 @@ static qboolean PM_PortalTransform(world_t *w, int portalnum, vec3_t org, vec3_t void *pr_globals = PR_globals(w->progs, PR_CURRENT); int i; int tmp; + float f; *w->g.self = EDICT_TO_PROG(w->progs, portal); //transform origin+velocity etc @@ -121,6 +122,16 @@ static qboolean PM_PortalTransform(world_t *w, int portalnum, vec3_t org, vec3_t VectorCopy(w->g.v_right, move); // VectorCopy(w->g.v_up, pmove.gravitydir); + //floor+floor, ish + if (DotProduct(w->g.v_up, pmove.gravitydir) < 0.7) + { + f = DotProduct(newvel, newvel); + if (f < 200*200) + { + VectorScale(newvel, 200 / sqrt(f), newvel); + } + } + //transform the angles too VectorCopy(org, G_VECTOR(OFS_PARM0)); diff --git a/engine/common/pmovetst.c b/engine/common/pmovetst.c index 532a1d2c4..7605def39 100644 --- a/engine/common/pmovetst.c +++ b/engine/common/pmovetst.c @@ -220,7 +220,7 @@ static qboolean PM_TransformedHullCheck (model_t *model, vec3_t start, vec3_t en VectorSubtract (end, origin, end_l); // sweep the box through the model - if (model) + if (model && model->funcs.NativeTrace) { if (angles[0] || angles[1] || angles[2]) { diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 724552d8d..3d96557d5 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -807,6 +807,8 @@ void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_ if (!model || model->type != mod_brush) return; + bestdist = 256; + if (model->fromgame == fg_quake || model->fromgame == fg_quake2) { //all polies, we can skip parts. special case. @@ -5823,6 +5825,7 @@ lh_extension_t QSG_Extensions[] = { // {"DP_ENT_COLORMOD"}, {"DP_ENT_CUSTOMCOLORMAP"}, {"DP_ENT_EXTERIORMODELTOCLIENT"}, + {"DP_ENT_TRAILEFFECTNUM", 1, NULL, {"particleeffectnum"}, "self.traileffectnum=particleeffectnum(\"myeffectname\n\"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame."}, //only in dp6 currently {"DP_ENT_GLOW"}, {"DP_ENT_VIEWMODEL"}, {"DP_GECKO_SUPPORT", 7, NULL, {"gecko_create", "gecko_destroy", "gecko_navigate", "gecko_keyevent", "gecko_mousemove", "gecko_resize", "gecko_get_texture_extent"}}, @@ -5832,6 +5835,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_HALFLIFE_MAP_CVAR"}, //to an extend {"DP_HALFLIFE_SPRITE"}, {"DP_INPUTBUTTONS"}, + {"DP_LIGHTSTYLE_STATICVALUE"}, {"DP_LITSUPPORT"}, {"DP_MD3_TAGSINFO", 2, NULL, {"gettagindex", "gettaginfo"}}, {"DP_MONSTERWALK", 0, NULL, {NULL}, "MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself."}, @@ -5858,7 +5862,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_QC_MINMAXBOUND", 3, NULL, {"min", "max", "bound"}}, {"DP_QC_MULTIPLETEMPSTRINGS", 0, NULL, {NULL}, "Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine)."}, {"DP_QC_RANDOMVEC", 1, NULL, {"randomvec"}}, - {"DP_QC_RENDER_SCENE"}, //clear+addentity+setviewprop+renderscene+setmodel are available to menuqc. + {"DP_QC_RENDER_SCENE", 0, NULL, {NULL}, "clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it."}, {"DP_QC_SINCOSSQRTPOW", 4, NULL, {"sin", "cos", "sqrt", "pow"}}, {"DP_QC_STRFTIME", 1, NULL, {"strftime"}}, {"DP_QC_STRING_CASE_FUNCTIONS", 2, NULL, {"strtolower", "strtoupper"}}, @@ -5871,7 +5875,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_QC_TRACE_MOVETYPE_HITMODEL"}, {"DP_QC_TRACE_MOVETYPE_WORLDONLY"}, {"DP_QC_TRACE_MOVETYPES"}, //this one is just a lame excuse to add annother extension... - {"DP_QC_UNLIMITEDTEMPSTRINGS", 0, NULL, {NULL}, "Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. All temp strings will be valid at least until the QCVM returns."}, + {"DP_QC_UNLIMITEDTEMPSTRINGS", 0, NULL, {NULL}, "Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns."}, {"DP_QC_URI_ESCAPE", 2, NULL, {"uri_escape", "uri_unescape"}}, #ifdef WEBCLIENT {"DP_QC_URI_GET", 1, NULL, {"uri_get"}}, @@ -5889,23 +5893,23 @@ lh_extension_t QSG_Extensions[] = { {"DP_SOLIDCORPSE"}, {"DP_SPRITE32"}, //hmm... is it legal to advertise this one? {"DP_SV_BOTCLIENT", 2, NULL, {"spawnclient", "clienttype"}}, - {"DP_SV_CLIENTCOLORS"}, - {"DP_SV_CLIENTNAME"}, + {"DP_SV_CLIENTCOLORS", 0, NULL, {NULL}, "Provided only for compatibility with DP."}, + {"DP_SV_CLIENTNAME", 0, NULL, {NULL}, "Provided only for compatibility with DP."}, {"DP_SV_DRAWONLYTOCLIENT"}, - {"DP_SV_DROPCLIENT", 1, NULL, {"dropclient"}}, + {"DP_SV_DROPCLIENT", 1, NULL, {"dropclient"}, "Equivelent to quakeworld's stuffcmd(self,\"disconnect\\n\"); hack"}, {"DP_SV_EFFECT", 1, NULL, {"effect"}}, {"DP_SV_EXTERIORMODELFORCLIENT"}, {"DP_SV_NODRAWTOCLIENT"}, //I prefer my older system. Guess I might as well remove that older system at some point. - {"DP_SV_PLAYERPHYSICS"}, - //FTE cannot implement this one, because dp's arguments are the wrong way around. its otherwise implemented. {"DP_SV_POINTPARTICLES"}, + {"DP_SV_PLAYERPHYSICS", 0, NULL, {NULL}, "Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction."}, + {"DP_SV_POINTPARTICLES", 3, NULL, {"particleeffectnum", "pointparticles", "trailparticles"}, "Specifies that pointparticles (and trailparticles) exists in ssqc as well as csqc (and that dp's trailparticles argument fuckup will normally work). ssqc values can be passed to csqc for use, the reverse is not true. Does NOT mean that DP's effectinfo.txt is supported, only that ssqc has functionality equivelent to csqc."}, {"DP_SV_POINTSOUND", 1, NULL, {"pointsound"}}, - {"DP_SV_PRECACHEANYTIME"}, + {"DP_SV_PRECACHEANYTIME", 0, NULL, {NULL}, "Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...)"}, {"DP_SV_SETCOLOR"}, {"DP_SV_SPAWNFUNC_PREFIX"}, {"DP_SV_WRITEPICTURE", 1, NULL, {"WritePicture"}}, {"DP_SV_WRITEUNTERMINATEDSTRING", 1, NULL, {"WriteUnterminatedString"}}, {"DP_TE_BLOOD", 1, NULL, {"te_blood"}}, - {"DP_TE_BLOODSHOWER", 1, NULL, {"te_bloodshower"}}, + {"_DP_TE_BLOODSHOWER", 1, NULL, {"te_bloodshower"}}, {"DP_TE_CUSTOMFLASH", 1, NULL, {"te_customflash"}}, {"DP_TE_EXPLOSIONRGB", 1, NULL, {"te_explosionrgb"}}, {"_DP_TE_FLAMEJET", 1, NULL, {"te_flamejet"}}, @@ -5937,7 +5941,7 @@ lh_extension_t QSG_Extensions[] = { "skel_get_bonerel", "skel_get_boneabs", "skel_set_bone", "skel_mul_bone", "skel_mul_bones", "skel_copybones", "skel_delete", "frameforname", "frameduration"}}, {"FTE_CSQC_RENDERTARGETS_WIP", 0, NULL, {NULL}, "VF_DESTCOLOUR etc exist and are supported"}, - {"FTE_ENT_SKIN_CONTENTS"}, //self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. + {"FTE_ENT_SKIN_CONTENTS", 0, NULL, {NULL}, "self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder."}, {"FTE_ENT_UNIQUESPAWNID"}, {"FTE_EXTENDEDTEXTCODES"}, {"FTE_FORCESHADER", 1, NULL, {"shaderforname"}}, //I'd rename this to _CSQC_ but it does technically provide this builtin to menuqc too, not that the forceshader entity field exists there... but whatever. @@ -5946,9 +5950,11 @@ lh_extension_t QSG_Extensions[] = { {"FTE_ISBACKBUFFERED", 1, NULL, {"isbackbuffered"}, "Allows you to check if a client has too many reliable messages pending."}, {"FTE_MEMALLOC", 4, NULL, {"memalloc", "memfree", "memcpy", "memfill8"}, "Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games."}, #ifndef NOMEDIA - {"FTE_MEDIA_AVI"}, //playfilm supports avi files. - {"FTE_MEDIA_CIN"}, //playfilm command supports q2 cin files. - {"FTE_MEDIA_ROQ"}, //playfilm command supports q3 roq files + #if defined(_WIN32) && !defined(WINRT) + {"FTE_MEDIA_AVI", 0, NULL, {NULL}, "playfilm command supports avi files."}, + #endif + {"FTE_MEDIA_CIN", 0, NULL, {NULL}, "playfilm command supports q2 cin files."}, + {"FTE_MEDIA_ROQ", 0, NULL, {NULL}, "playfilm command supports q3 roq files."}, #endif {"FTE_MULTIPROGS", 5, NULL, {"externcall", "addprogs", "externvalue", "externset", "instr"}, "Multiple progs.dat files can be loaded inside the same qcvm."}, //multiprogs functions are available. {"FTE_MULTITHREADED", 3, NULL, {"sleep", "fork", "abort"}}, @@ -5958,7 +5964,7 @@ lh_extension_t QSG_Extensions[] = { #ifdef SVCHAT {"FTE_NPCCHAT", 1, NULL, {"chat"}}, //server looks at chat files. It automagically branches through calling qc functions as requested. #endif - {"FTE_QC_CHECKCOMMAND", 1, NULL, {"checkcommand"}}, + {"FTE_QC_CHECKCOMMAND", 1, NULL, {"checkcommand"}, "Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values."}, {"FTE_QC_CHECKPVS", 1, NULL, {"checkpvs"}}, {"FTE_QC_HARDWARECURSORS", 0, NULL, {NULL}, "setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors."}, {"FTE_QC_HASHTABLES", 6, NULL, {"hash_createtab", "hash_destroytab", "hash_add", "hash_get", "hash_delete", "hash_getkey"}}, @@ -5971,6 +5977,18 @@ lh_extension_t QSG_Extensions[] = { {"FTE_QC_RAGDOLL_WIP", 1, NULL, {"ragupdate", "skel_set_bone_world", "skel_mmap"}}, {"FTE_QC_SENDPACKET", 1, NULL, {"sendpacket"}}, //includes the SV_ParseConnectionlessPacket event. {"FTE_QC_TRACETRIGGER"}, +#ifdef Q2CLIENT + {"FTE_QUAKE2_CLIENT", 0, NULL, {NULL}, "This engine is able to act as a quake2 client"}, +#endif +#ifdef Q2SERVER + {"FTE_QUAKE2_SERVER", 0, NULL, {NULL}, "This engine is able to act as a quake2 server"}, +#endif +#ifdef Q3CLIENT + {"FTE_QUAKE3_CLIENT", 0, NULL, {NULL}, "This engine is able to act as a quake3 client"}, +#endif +#ifdef Q3SERVER + {"FTE_QUAKE3_SERVER", 0, NULL, {NULL}, "This engine is able to act as a quake3 server"}, +#endif {"FTE_SOLID_LADDER"}, //Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS #ifdef SQL @@ -5986,6 +6004,10 @@ lh_extension_t QSG_Extensions[] = { {"FTE_SV_REENTER"}, {"FTE_TE_STANDARDEFFECTBUILTINS", 14, NULL, {"te_gunshot", "te_spike", "te_superspike", "te_explosion", "te_tarexplosion", "te_wizspike", "te_knightspike", "te_lavasplash", "te_teleport", "te_lightning1", "te_lightning2", "te_lightning3", "te_lightningblood", "te_bloodqw"}}, +#ifdef TERRAIN + {"FTE_TERRAIN_MAP", 0, NULL, {NULL}, "This engine supports .hmp files, as well as terrain embedded within bsp files."}, + {"FTE_RAW_MAP", 0, NULL, {NULL}, "This engine supports directly loading .map files, as well as realtime editing of the various brushes."}, +#endif {"KRIMZON_SV_PARSECLIENTCOMMAND", 3, NULL, {"clientcommand", "tokenize", "argv"}}, //very very similar to the mvdsv system. {"NEH_CMD_PLAY2"}, diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 495883b29..60191f854 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -105,10 +105,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PROTOCOL_INFO_GUID (('G'<<0) + ('U'<<8) + ('I'<<16) + ('D' << 24)) //globally 'unique' client id info. -#define PROTOCOL_VERSION_QW 28 -#define PROTOCOL_VERSION_Q2_DEMO_MIN 26 -#define PROTOCOL_VERSION_Q2_MIN 31 -#define PROTOCOL_VERSION_Q2 34 +#define PROTOCOL_VERSION_QW 28 + +#define PROTOCOL_VERSION_Q2_DEMO_MIN 26 +#define PROTOCOL_VERSION_Q2_MIN 31 +#define PROTOCOL_VERSION_Q2 34 +#define PROTOCOL_VERSION_R1Q2 35 +#define PROTOCOL_VERSION_Q2PRO 36 //========================================= @@ -392,7 +395,13 @@ enum svcq2_ops_e svcq2_playerinfo, //17 // variable svcq2_packetentities,//18 // [...] svcq2_deltapacketentities,//19 // [...] - svcq2_frame //20 (the bastard to implement.) + svcq2_frame, //20 (the bastard to implement.) + + + svcr1q2_zpacket = 21, + svcr1q2_zdownload = 22, + svcq2pro_gamestate = 23, // q2pro specific, means svc_playerupdate in r1q2 + svcq2pro_setting = 24, }; enum clcq2_ops_e @@ -766,6 +775,7 @@ enum clcq2_ops_e #define Q2U_ANGLE1 (1<<10) #define Q2U_MODEL (1<<11) #define Q2U_RENDERFX8 (1<<12) // fullbright, etc +#define Q2UX_ANGLE16 (1<<13) #define Q2U_EFFECTS8 (1<<14) // autorotate, trails, etc #define Q2U_MOREBITS2 (1<<15) // read one additional qbyte @@ -784,6 +794,12 @@ enum clcq2_ops_e #define Q2U_SKIN16 (1<<25) #define Q2U_SOUND (1<<26) #define Q2U_SOLID (1<<27) +#define Q2UX_UNUSED4 (1<<28) +#define Q2UX_UNUSED3 (1<<29) +#define Q2UX_UNUSED2 (1<<30) +#define Q2UX_UNUSED1 (1<<31) + +#define Q2UX_UNUSED (Q2UX_UNUSED1|Q2UX_UNUSED2|Q2UX_UNUSED3|Q2UX_UNUSED4) //============================================== @@ -1009,6 +1025,7 @@ typedef struct entity_state_s vec3_t predorg; } q1; } u; + unsigned short modelindex2; //q2/vweps unsigned short frame; @@ -1031,13 +1048,13 @@ typedef struct entity_state_s qbyte lightstyle; qbyte lightpflags; - unsigned short solid; + unsigned short tagindex; + unsigned int tagentity; + + unsigned int solid; #define ES_SOLID_BSP 31 unsigned short light[4]; - - unsigned short tagentity; - unsigned short tagindex; } entity_state_t; extern entity_state_t nullentitystate; @@ -1178,6 +1195,14 @@ typedef struct q1usercmd_s #define Q2PS_WEAPONFRAME (1<<13) #define Q2PS_RDFLAGS (1<<14) +#define Q2PSX_GUNOFFSET (1<<0) +#define Q2PSX_GUNANGLES (1<<1) +#define Q2PSX_M_VELOCITY2 (1<<2) +#define Q2PSX_M_ORIGIN2 (1<<3) +#define Q2PSX_VIEWANGLE2 (1<<4) +#define Q2PSX_STATS (1<<5) +#define Q2PSX_CLIENTNUM (1<<6) +#define Q2PSX_OLD (1<<8) //not part of the protocol, just lazy handling. // entity_state_t->renderfx flags diff --git a/engine/common/q3common.c b/engine/common/q3common.c index 2d493f72e..e288399a3 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -734,7 +734,7 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) // Send the qport if we are a client if( chan->sock == NS_CLIENT ) { - MSG_WriteShort( &send, cls.qport); + MSG_WriteShort( &send, chan->qport); } #endif fragmentLength = chan->reliable_length - chan->reliable_start; @@ -833,7 +833,7 @@ void Netchan_TransmitQ3( netchan_t *chan, int length, const qbyte *data ) // Send the qport if we are a client if( chan->sock == NS_CLIENT ) { - MSG_WriteShort( &send, cls.qport); + MSG_WriteShort( &send, chan->qport); } #endif // Copy the message to the packet diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 5662bef7c..e9dc926e5 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -2657,9 +2657,8 @@ qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, lump_t * void ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *cookie) { unsigned int vertidx; - int i, lindex; + int i, lindex, edgevert; mesh_t *mesh = surf->mesh; - medge_t *pedge; float *vec; float s, t, d; int sty; @@ -2692,17 +2691,13 @@ void ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *co for (i=0 ; inumvertexes ; i++) { lindex = mod->surfedges[surf->firstedge + i]; - - if (lindex > 0) - { - pedge = &mod->edges[lindex]; - vertidx = pedge->v[0]; - } + edgevert = lindex <= 0; + if (edgevert) + lindex = -lindex; + if (lindex < 0 || lindex >= mod->numedges) + vertidx = 0; else - { - pedge = &mod->edges[-lindex]; - vertidx = pedge->v[1]; - } + vertidx = mod->edges[lindex].v[edgevert]; vec = mod->vertexes[vertidx].position; s = DotProduct (vec, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]; @@ -2973,6 +2968,7 @@ static int Mod_Batches_Generate(model_t *mod) mod->lightmaps.count /= merge; mod->lightmaps.height *= merge; + mod->numbatches = 0; //for each surface, find a suitable batch to insert it into. //we use 'firstmesh' to avoid chucking out too many verts in a single vbo (gl2 hardware tends to have a 16bit limit) diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 29eb2171a..4ff7abc8c 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -2878,18 +2878,12 @@ void DumpGLState(void) // qglGetPointerv(GL_FOG_COORD_ARRAY_POINTER, &ptr); // Sys_Printf("GL_FOG_COORDINATE_ARRAY_EXT: %i (%lx)\n", (int) qglIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT), (int) ptr); // } -// if (qglIsEnabled(GL_INDEX_ARRAY)) { if (qglBindBufferARB) qglGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &rval); else rval = 0; -#ifndef GL_INDEX_ARRAY_POINTER Sys_Printf("GL_ELEMENT_ARRAY_BUFFER_BINDING: %i:%p\n", rval, (void*)0); -#else - qglGetPointerv(GL_INDEX_ARRAY_POINTER, &ptr); - Sys_Printf("GL_INDEX_ARRAY: %s %i:%p\n", qglIsEnabled(GL_INDEX_ARRAY)?"en":"dis", rval, ptr); -#endif } if (qglIsEnabled(GL_NORMAL_ARRAY)) { diff --git a/engine/partcfgs/q2part.cfg b/engine/partcfgs/q2part.cfg index 40b8af4e6..39ca83470 100644 --- a/engine/partcfgs/q2part.cfg +++ b/engine/partcfgs/q2part.cfg @@ -18,6 +18,21 @@ r_part pe_default scalefactor 0.8 } +r_part TEQ2_LASER_SPARKS +{ + texture "classicparticle" + tcoords 0 0 16 16 32 + count 1 + scale 1 + alpha 1 + die 0.3 0.8 + randomvel 20 + orgadd 0 7 + spawnorg 4 + gravity 40 + scalefactor 0.8 +} + r_part te_splashsparks { texture "classicparticle" @@ -268,7 +283,7 @@ r_part teq2_blaster lightradius 150 lightradiusfade 400 lightrgb 1 1 0 - lightshadows 0 + lightshadows 1 sound "weapons/lashit.wav" 1 1 0 0 } r_part teq2_blaster2 @@ -290,7 +305,7 @@ r_part teq2_blaster2 lightradius 150 lightradiusfade 400 lightrgb 0.05 1.0 0.05 - lightshadows 0 + lightshadows 1 sound "weapons/lashit.wav" 1 1 0 0 } r_part TR_BLASTERTRAIL @@ -305,6 +320,10 @@ r_part TR_BLASTERTRAIL randomvel 5 die 0.3 0.5 colorindex 0xe0 + lightradius 200 + lightradiusfade 400 + lightrgb 1.0 1.0 0.0 + lightshadows 1 } //green version @@ -320,6 +339,10 @@ r_part TR_BLASTERTRAIL2 randomvel 5 die 0.3 0.5 colorindex 0xd0 + lightradius 200 + lightradiusfade 400 + lightrgb 0.0 1.0 0.0 + lightshadows 1 } @@ -490,12 +513,25 @@ r_part trq2_grenade } r_part trq2_gib { - assoc tr_gib + texture "particles/quake" + step 3 + scale 4 + die 1.0 1.4 + colorindex 0xe8 7 + spawnorg 1 + spawnvel 5 + gravity -20 } -//FIXME: implement r_part trq2_greengib { - assoc tr_gib + texture "particles/quake" + step 3 + scale 4 + die 1.0 1.4 + colorindex 0xdb 7 + spawnorg 1 + spawnvel 5 + gravity -20 } r_part TR_PLASMA @@ -527,6 +563,14 @@ r_part tr_tagtrail lightrgb 1.0 1.0 0.0 } +//FIXME: add particles +r_part tr_trap +{ + lighttime 0 + lightradius 100 200 + lightrgb 1.0 0.8 0.25 +} + //flags do NOT use coronas, because it obscures the holding player's skin colour r_part tr_flag1 { @@ -565,15 +609,6 @@ r_part tr_flag2 lightrgb 0.25 0.25 1.0 } - -//FIXME: add particles -r_part tr_trap -{ - lighttime 0 - lightradius 100 200 - lightrgb 1.0 0.8 0.25 -} - r_part EF_FLIES { texture "classicparticle" @@ -670,4 +705,26 @@ r_part teq2_bfg_explosion lightrgbfade 0.0 0.0 0.0 sound "weapons/xpld_wat.wav" 1 1 0 0 model "sprites/s_bfg2.sp2" framestart=0 frameend=4 alpha=0.3 transparent fullbright noshadow -} \ No newline at end of file +} + + +//31qu cylinder, 8-98 high +//should look like its sucked up into some thingie above +r_part TEQ2_BOSSTPORT +{ + texture "classicparticle" + tcoords 0 0 16 16 32 + count 800 + scale 1 + alpha 1 + die 0.5 0.8 + orgadd 8 -98 + veladd 100 200 + spawnmode circle + spawnorg 48 0 + spawnvel -50 30 + randomvel 32 31 + gravity -800 + rgbf 1 1 1 + scalefactor 0.8 +} diff --git a/engine/qclib/byshpuld.ico b/engine/qclib/byshpuld.ico new file mode 100644 index 000000000..511de7965 Binary files /dev/null and b/engine/qclib/byshpuld.ico differ diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 2ed447076..855cdd4fd 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -1232,17 +1232,17 @@ reeval: return s; */ } break; -/* case OP_PUSH: - OPC->_int = ENGINEPOINTER(&localstack[localstack_used+pr_spushed]); - pr_spushed += OPA->_int; - if (pr_spushed + localstack_used >= LOCALSTACK_SIZE) + case OP_PUSH: + OPC->_int = ENGINEPOINTER(&prinst.localstack[prinst.localstack_used+prinst.spushed]); + prinst.spushed += OPA->_int; + if (prinst.spushed + prinst.localstack_used >= LOCALSTACK_SIZE) { - pr_spushed = 0; + prinst.spushed = 0; pr_xstatement = st-pr_statements; - PR_RunError(progfuncs, "Progs pushed too much"); + PR_RunError(&progfuncs->funcs, "Progs pushed too much"); } break; - case OP_POP: +/* case OP_POP: pr_spushed -= OPA->_int; if (pr_spushed < 0) { diff --git a/engine/qclib/fteqcc.rc b/engine/qclib/fteqcc.rc new file mode 100644 index 000000000..3f819c87c --- /dev/null +++ b/engine/qclib/fteqcc.rc @@ -0,0 +1 @@ +101 ICON "byshpuld.ico" diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index c8f49244c..769b603f1 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -464,6 +464,7 @@ struct QCC_function_s const char *file; int line; char *name; //internal name of function + struct QCC_function_s *parentscope; //for nested functions struct QCC_type_s *type; //same as the def's type struct QCC_def_s *def; struct QCC_def_s *firstlocal; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 4f8e57de8..149098f0d 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -220,6 +220,8 @@ const QCC_sref_t nullsref = {0}; struct QCC_function_s *pr_scope; // the function being parsed, or NULL QCC_type_t *pr_classtype; // the class that the current function is part of. QCC_type_t *pr_assumetermtype; //undefined things get this time, with no warning about being undeclared (used for the state function, so prototypes are not needed) +QCC_function_t *pr_assumetermscope; +unsigned int pr_assumetermflags; //GDF_ pbool pr_dumpasm; QCC_string_t s_file, s_file2; // filename for function definition @@ -1626,6 +1628,7 @@ static QCC_sref_t QCC_GetTemp(QCC_type_t *type); void QCC_FreeTemp(QCC_sref_t t); void QCC_FreeDef(QCC_def_t *def); QCC_sref_t QCC_MakeSRefForce(QCC_def_t *def, unsigned int ofs, QCC_type_t *type); +QCC_sref_t QCC_MakeSRef(QCC_def_t *def, unsigned int ofs, QCC_type_t *type); //we're about to overwrite the given def, so if there's any aliases to it, we need to clear them out. static void QCC_ClobberDef(QCC_def_t *def) @@ -2916,11 +2919,39 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ QCC_FreeTemp(var_a); optres_assignments++; + if (flags&STFL_DISCARDRESULT) + { + QCC_FreeTemp(var_b); + var_b = nullsref; + } return var_b; } } } } + else if (op - pr_opcodes == OP_ADD_I && statements[numstatements-1].op == OP_MUL_I && opt_assignments) + { + //mul_i idx 4i tmp + //add_i tmp 2i out + //becomes add_piw 2i idx out + +// const QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a); + const QCC_eval_t *eval_b = QCC_SRef_EvalConst(statements[numstatements-1].b); + + if (eval_b && eval_b->_int == 4) + { + if (opt_assignments && var_a.cast && var_a.sym == statements[numstatements-1].c.sym && var_a.ofs == statements[numstatements-1].c.ofs) + if (var_a.sym && var_b.sym && var_a.sym->temp && var_a.sym->refcount==1) + { + op = &pr_opcodes[OP_ADD_PIW]; + QCC_FreeDef(var_a.sym); + var_a = var_b; + var_b = statements[numstatements-1].a; + numstatements--; + QCC_ForceUnFreeDef(var_b.sym); + } + } + } } if (!QCC_OPCodeValid(op)) @@ -3156,10 +3187,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ var_a.cast = type_float; } if (var_b.cast) - { - QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], var_a, var_b, NULL, 0)); - return var_b; - } + return QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], var_a, var_b, NULL, flags&STFL_DISCARDRESULT); } return var_a; case OP_CONV_FTOI: @@ -3185,10 +3213,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ var_a.cast = type_integer; } if (var_b.cast) - { - QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], var_a, var_b, NULL, 0)); - return var_b; - } + return QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], var_a, var_b, NULL, flags&STFL_DISCARDRESULT); } return var_a; case OP_STORE_P: @@ -5008,13 +5033,24 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou } } if (!strcmp(funcname, "alloca")) - { //FIXME: half of these functions with regular expression arguments should be handled later or something - QCC_sref_t sz; + { //FIXME: half of these functions with known arguments should be handled later or something + QCC_sref_t sz, ret; + + if (!func.sym->initialized) + func.sym->initialized = 3; + func.sym->referenced = true; + QCC_FreeTemp(func); + sz = QCC_PR_Expression(TOP_PRIORITY, 0); QCC_PR_Expect(")"); sz = QCC_SupplyConversion(sz, ev_integer, true); - sz = QCC_PR_Statement(&pr_opcodes[OP_PUSH], sz, nullsref, NULL); - return sz; + //result = push_words((sz+3)/4); + sz = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], sz, QCC_MakeIntConst(3), NULL); + sz = QCC_PR_Statement(&pr_opcodes[OP_DIV_I], sz, QCC_MakeIntConst(4), NULL); + QCC_FreeTemp(sz); + ret = QCC_GetTemp(QCC_PointerTypeTo(type_variant)); + QCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], sz, nullsref, ret, false); //push *(int*)&a elements + return ret; } if (!strcmp(funcname, "_")) { @@ -6386,12 +6422,20 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a } arraysize = 0; + //convert it to ints if that makes sense + if (QCC_OPCodeValid(&pr_opcodes[OP_ADD_I])) + { + if (idx.cast) + idx = QCC_SupplyConversion(idx, ev_integer, true); + tmp = QCC_SupplyConversion(tmp, ev_integer, true); + } + if (t->size != 1) /*don't multiply by type size if the instruction/emulation will do that instead*/ { if (tmp.cast->type == ev_float) - tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], tmp, QCC_MakeFloatConst(t->size), NULL); + tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_SupplyConversion(tmp, ev_float, true), QCC_MakeFloatConst(t->size), NULL); else - tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], tmp, QCC_MakeIntConst(t->size), NULL); + tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(tmp, ev_integer, true), QCC_MakeIntConst(t->size), NULL); } //legacy opcodes needs to stay using floats even if an int was specified @@ -6404,9 +6448,9 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a /*calc the new index*/ if (idx.cast && idx.cast->type == ev_float && tmp.cast->type == ev_float) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], idx, QCC_SupplyConversion(tmp, ev_float, true), NULL); + idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], QCC_SupplyConversion(idx, ev_float, true), QCC_SupplyConversion(tmp, ev_float, true), NULL); else if (idx.cast) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, QCC_SupplyConversion(tmp, ev_integer, true), NULL); + idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_SupplyConversion(tmp, ev_integer, true), NULL); else idx = tmp; } @@ -6453,7 +6497,7 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a { tmp = QCC_MakeIntConst(t->params[i].ofs); if (idx.cast) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, tmp, NULL); + idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_SupplyConversion(tmp, ev_integer, true), NULL); else idx = tmp; } @@ -6461,7 +6505,7 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a { tmp = QCC_MakeFloatConst(t->params[i].ofs); if (idx.cast) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], idx, tmp, NULL); + idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], QCC_SupplyConversion(idx, ev_float, true), QCC_SupplyConversion(tmp, ev_float, true), NULL); else idx = tmp; } @@ -6617,22 +6661,19 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo d = QCC_GetTemp(type_vector); d.cast = type_float; if (x.cast->type == ev_float) - x=QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, x, d, NULL, STFL_PRESERVEB); + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); else - x=QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, x, d, NULL, STFL_PRESERVEB); - QCC_FreeTemp(x); + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); d.ofs++; if (y.cast->type == ev_float) - y=QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, y, d, NULL, STFL_PRESERVEB); + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); else - y=QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, y, d, NULL, STFL_PRESERVEB); - QCC_FreeTemp(y); + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); d.ofs++; if (z.cast->type == ev_float) - z=QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, z, d, NULL, STFL_PRESERVEB); + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); else - z=QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, z, d, NULL, STFL_PRESERVEB); - QCC_FreeTemp(z); + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); d.ofs++; d.ofs -= 3; d.cast = type_vector; @@ -6719,6 +6760,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo d = QCC_MakeIntConst(0); else if ( (!strcmp(name, "randomv")) || (!strcmp(name, "sizeof")) || + (!strcmp(name, "alloca")) || (!strcmp(name, "entnum")) || (!strcmp(name, "autocvar")) || (!strcmp(name, "used_model")) || @@ -6753,7 +6795,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo } else if (pr_assumetermtype) { - d = QCC_PR_GetSRef (pr_assumetermtype, name, NULL, true, 0, false); + d = QCC_PR_GetSRef (pr_assumetermtype, name, pr_assumetermscope, true, 0, pr_assumetermflags); if (!d.cast) QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", name); } @@ -6942,7 +6984,7 @@ QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit) char typeb[256]; TypeName(src.cast, typea, sizeof(typea)); TypeName(cast, typeb, sizeof(typeb)); - QCC_PR_ParseWarning(0, "Implicit cast from %s to %s\n", typea, typeb); + QCC_PR_ParseWarning(0, "Implicit cast from %s to %s", typea, typeb); } } src.cast = cast; @@ -6968,7 +7010,7 @@ QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit) char typeb[256]; TypeName(src.cast, typea, sizeof(typea)); TypeName(cast, typeb, sizeof(typeb)); - QCC_PR_ParseWarning(0, "Implicit cast from %s to %s\n", typea, typeb); + QCC_PR_ParseWarning(0, "Implicit cast from %s to %s", typea, typeb); } src.cast = cast; } @@ -8008,7 +8050,9 @@ QCC_sref_t QCC_StoreToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable, pb { addr = dest->base; } - QCC_PR_Statement(&pr_opcodes[OP_STOREP_C], addr, source, NULL); +// if (readable) //if we're returning source, make sure it can't get freed +// QCC_UnFreeTemp(source); + QCC_PR_StatementFlags(&pr_opcodes[OP_STOREP_C], source, addr, NULL, STFL_DISCARDRESULT|(preservedest?STFL_PRESERVEB:0)|(readable?STFL_PRESERVEA:0)); } break; case REF_ACCESSOR: @@ -9839,6 +9883,7 @@ void QCC_PR_ParseState (void) { QCC_sref_t s1, def; + //FIXME: this is ambiguous with pre-inc and post-inc logic. if (QCC_PR_CheckToken("++") || QCC_PR_CheckToken("--")) { s1 = QCC_PR_ParseImmediate (); @@ -9920,6 +9965,8 @@ void QCC_PR_ParseState (void) QCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, "missing comma in state definition"); pr_assumetermtype = type_function; + pr_assumetermscope = pr_scope->parentscope; + pr_assumetermflags = GDF_CONST | (pr_assumetermscope?GDF_STATIC:0); def = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); if (typecmp(def.cast, type_function)) { @@ -10863,6 +10910,7 @@ QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type) func->firstlocal = NULL; func->def = def; func->type = type; + func->parentscope = pr_scope; return func; } @@ -11816,6 +11864,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s { if (scope) { + //FIXME: should we be scanning the locals list instead, and remove the localstable? def = pHash_Get(&localstable, name); while(def) @@ -11829,23 +11878,38 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s if ( def->scope && def->scope != scope) { - def = pHash_GetNext(&localstable, name, def); - continue; // in a different function + struct QCC_function_s *pscope = NULL; + if (def->isstatic) + { + for (pscope = scope->parentscope; pscope; pscope = pscope->parentscope) + if (def->scope == pscope) + break; + } + if (!pscope) + { + def = pHash_GetNext(&localstable, name, def); + continue; // in a different function + } } if (type && typecmp(def->type, type)) QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type, typebuf1, sizeof(typebuf1)), TypeName(def->type, typebuf2, sizeof(typebuf2))); if (def->arraysize != arraysize && arraysize>=0) QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) + if (allocate && scope && !(flags & GDF_STATIC)) { if (allocate == 2) QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Duplicate definition of %s.", name); - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + if (def->isstatic) + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "nonstatic redeclaration of %s ignored", name); + else + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); // if (!scope) // QCC_PR_ParsePrintDef(def); } + else if (allocate && (flags & GDF_STATIC) && !def->isstatic) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "static redefinition of %s follows non-static definition.", name); QCC_ForceUnFreeDef(def); return def; @@ -11919,7 +11983,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s } if (def->arraysize != arraysize && arraysize>=0) QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) + if (allocate && scope && !(flags & GDF_STATIC)) { if (allocate == 2) QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Duplicate definition of %s.", name); @@ -11929,7 +11993,10 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s continue; // in a different function } - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + if (def->isstatic) + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "nonstatic redeclaration of %s ignored", name); + else + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); // if (!scope) // QCC_PR_ParsePrintDef(def); @@ -13060,17 +13127,21 @@ void QCC_PR_ParseDefs (char *classname) else if (forceused) //FIXME: make proper pragma(used) thingie gd_flags |= GDF_USED; -#if IAMNOTLAZY if (dynlength.cast) { +#if 1//IAMNOTLAZY def = QCC_PR_GetDef (QCC_PR_PointerType(type), name, pr_scope, allocatenew, 0, gd_flags); dynlength = QCC_SupplyConversion(dynlength, ev_integer, true); if (type->size != 1) dynlength = QCC_PR_Statement(pr_opcodes+OP_MUL_I, dynlength, QCC_MakeIntConst(type->size), NULL); - QCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], dynlength, nullsref, def, false); //push *(int*)&a elements + QCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], dynlength, nullsref, QCC_MakeSRef(def, 0, def->type), false); //push *(int*)&a elements + QCC_FreeTemp(dynlength); + QCC_FreeDef(def); +#else + QCC_PR_ParseError(ERR_NOTANAME, "%s is dynamically sized", name); +#endif } else -#endif def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, gd_flags); if (!def) diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index d4f69a4e2..dd7871cd1 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -14,6 +14,8 @@ #define EMBEDDEBUG +#define IDI_ICON_FTEQCC MAKEINTRESOURCE(101) + void AddSourceFile(const char *parentsrc, const char *filename); void GUI_ParseCommandLine(char *args); void GUI_RevealOptions(void); @@ -2145,7 +2147,7 @@ void EditFile(char *name, int line, pbool setcontrol) wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; + wndclass.hIcon = LoadIcon(ghInstance, IDI_ICON_FTEQCC); wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); wndclass.hbrBackground = (void *)COLOR_WINDOW; wndclass.lpszMenuName = 0; @@ -3429,7 +3431,7 @@ void OptionsDialog(void) wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; + wndclass.hIcon = LoadIcon(ghInstance, IDI_ICON_FTEQCC); wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); wndclass.hbrBackground = (void *)COLOR_WINDOW; wndclass.lpszMenuName = 0; @@ -4719,7 +4721,7 @@ void CreateOutputWindow(pbool doannoates) wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; + wndclass.hIcon = LoadIcon(ghInstance, IDI_ICON_FTEQCC); wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); wndclass.hbrBackground = (void *)COLOR_WINDOW; wndclass.lpszMenuName = 0; @@ -5076,7 +5078,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; + wndclass.hIcon = LoadIcon(ghInstance, IDI_ICON_FTEQCC); wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); wndclass.hbrBackground = (void *)COLOR_WINDOW; wndclass.lpszMenuName = 0; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 9e26c5b12..095bb52d2 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -34,6 +34,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PF_sqlescape PF_Fixme #define PF_sqlversion PF_Fixme #define PF_sqlreadfloat PF_Fixme +#define PF_sqlreadblob PF_Fixme +#define PF_sqlescapeblob PF_Fixme #endif #ifndef CLIENTONLY @@ -3752,14 +3754,18 @@ void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars */ int i; + G_FLOAT(OFS_RETURN) = 0; + if (s[0] <= ' ') { - PR_BIError (prinst, "PF_precache_particles: Bad string"); + /*if (!ssqc_deprecated_warned) + { + PR_RunWarning(prinst, "PF_precache_particles: Bad string"); + ssqc_deprecated_warned = true; + }*/ return; } - G_FLOAT(OFS_RETURN) = 0; - for (i=1 ; idimension_send, PEXT_CSQC, 0); } } if (!strcmp(sv.strings.particle_precache[i], s)) @@ -5869,6 +5877,83 @@ void QCBUILTIN PF_sqlreadfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_ G_FLOAT(OFS_RETURN) = 0; } +void QCBUILTIN PF_sqlreadblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + sqlserver_t *server; + queryresult_t *qres; + char *data; + + int serveridx = G_FLOAT(OFS_PARM0); + int queryidx = G_FLOAT(OFS_PARM1); + int row = G_FLOAT(OFS_PARM2); + int column = G_FLOAT(OFS_PARM3); + int dst = G_INT(OFS_PARM4); + int dstsize = G_INT(OFS_PARM5); + + if (dst <= 0 || dst+dstsize >= prinst->stringtablesize) + { //FIXME: this check should be some utility function. + PR_BIError(prinst, "PF_sqlreadblob: invalid dest\n"); + return; + } + + if (SQL_Available()) + { + server = SQL_GetServer(serveridx, false); + if (server) + { + qres = SQL_GetQueryResult(server, queryidx, row); + if (qres) + { + size_t blobsize; + data = SQL_ReadField(server, qres, row, column, true, &blobsize); + if (data) + { //unsure how to handle overflows. we truncate for now. + blobsize = min(blobsize, dstsize); + G_INT(OFS_RETURN) = blobsize; + memcpy(prinst->stringtable + dst, data, blobsize); + return; + } + } + else + { + Con_Printf("Invalid sql request/row\n"); + PR_StackTrace(prinst, false); + } + } + } + // else we failed to get anything + G_INT(OFS_RETURN) = 0; +} +void QCBUILTIN PF_sqlescapeblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char hex[16] = "0123456789abcdef"; +// int serveridx = G_FLOAT(OFS_PARM0); //fixme + int qcptr = G_INT(OFS_PARM1); + int size = G_INT(OFS_PARM2); + char *out; + + qbyte *blob = prinst->stringtable + qcptr; + + if (qcptr <= 0 || qcptr+size >= prinst->stringtablesize) + { //FIXME: this check should be some utility function. + PR_BIError(prinst, "PF_sqlescapeblob: invalid blob\n"); + return; + } + + G_INT(OFS_RETURN) = prinst->AllocTempString(prinst, &out, size*2+4); + + //"x'DEADBEEF'" + *out++ = 'x'; + *out++ = '\''; + for (; size > 0; size--, blob++) + { + *out++ = hex[*blob>>4]; + *out++ = hex[*blob&15]; + } + *out++ = '\''; + *out++ = 0; +} + void QCBUILTIN PF_sqlerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { sqlserver_t *server; @@ -7806,6 +7891,16 @@ void QCBUILTIN PF_sv_trailparticles(pubprogfuncs_t *prinst, struct globalvars_s ednum = G_EDICTNUM(prinst, OFS_PARM1); } + if (efnum <= 0) + { +// if (!ssqc_deprecated_warned) +// { +// PR_RunWarning(prinst, "PF_sv_trailparticles: invalid effect"); +// ssqc_deprecated_warned = true; +// } + return; + } + MSG_WriteByte(&sv.multicast, svcfte_trailparticles); MSG_WriteEntity(&sv.multicast, ednum); MSG_WriteShort(&sv.multicast, efnum); @@ -7840,6 +7935,16 @@ void QCBUILTIN PF_sv_pointparticles(pubprogfuncs_t *prinst, struct globalvars_s float *vel = (prinst->callargc < 3)?vec3_origin:G_VECTOR(OFS_PARM2); int count = (prinst->callargc < 4)?1:G_FLOAT(OFS_PARM3); + if (efnum <= 0) + { +// if (!ssqc_deprecated_warned) +// { +// PR_RunWarning(prinst, "PF_sv_pointparticles: invalid effect"); +// ssqc_deprecated_warned = true; +// } + return; + } + if (count > 65535) count = 65535; @@ -8648,10 +8753,10 @@ static void ParamNegateFix ( float * xx, float * yy, int Zone ) static void QCBUILTIN PF_ShowPic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *slot = PR_GetStringOfs(prinst, OFS_PARM0); - const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); - float x = G_FLOAT(OFS_PARM2); - float y = G_FLOAT(OFS_PARM3); - float zone = G_FLOAT(OFS_PARM4); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); + float x = G_FLOAT(OFS_PARM2); + float y = G_FLOAT(OFS_PARM3); + unsigned int zone = G_FLOAT(OFS_PARM4); int entnum; client_t *cl; @@ -9090,9 +9195,18 @@ qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd) if (delta[0] || delta[1] || delta[2]) { //eular angle changes suck - client_t *cl = ClientReliableWrite_BeginSplit(client, svcfte_setangledelta, 7); - for (i=0 ; i < 3 ; i++) - ClientReliableWrite_Angle16 (cl, delta[i]); + if (client->fteprotocolextensions2 & PEXT2_SETANGLEDELTA) + { + client_t *cl = ClientReliableWrite_BeginSplit(client, svcfte_setangledelta, 7); + for (i=0 ; i < 3 ; i++) + ClientReliableWrite_Angle16 (cl, delta[i]); + } + else + { + client_t *cl = ClientReliableWrite_BeginSplit(client, svc_setangle, 7); + for (i=0 ; i < 3 ; i++) + ClientReliableWrite_Angle (cl, sv_player->v->v_angle[i]); + } } } @@ -9729,6 +9843,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"sqlescape", PF_sqlescape, 0, 0, 0, 256, "string(float serveridx, string data)"}, // sqlescape (FTE_SQL) {"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx)"}, // sqlversion (FTE_SQL) {"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column)"}, // sqlreadfloat (FTE_SQL) + {"sqlreadblob", PF_sqlreadblob, 0, 0, 0, 0, "int(float serveridx, float queryidx, float row, float column, _variant *ptr, int maxsize)"}, + {"sqlescapeblob", PF_sqlescapeblob, 0, 0, 0, 0, "string(float serveridx, _variant *ptr, int maxsize)"}, {"stoi", PF_stoi, 0, 0, 0, 259, D("int(string)", "Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string.")}, {"itos", PF_itos, 0, 0, 0, 260, D("string(int)", "Converts the passed true integer into a base10 string.")}, @@ -11017,10 +11133,10 @@ void PR_DumpPlatform_f(void) {"CHAN_BODY", "const float", QW|NQ|CS, NULL, CHAN_BODY}, {"CHANF_RELIABLE", "const float", QW, NULL, 8}, - {"SOUNDFLAG_RELIABLE", "const float", QW|NQ, NULL, CF_RELIABLE}, - {"SOUNDFLAG_ABSVOLUME", "const float", /*QW|NQ|*/CS,NULL, CF_ABSVOLUME}, - {"SOUNDFLAG_FORCELOOP", "const float", /*QW|NQ|*/CS,NULL, CF_FORCELOOP}, - {"SOUNDFLAG_NOSPACIALISE", "const float", /*QW|NQ|*/CS,NULL, CF_NOSPACIALISE}, + {"SOUNDFLAG_RELIABLE", "const float", QW|NQ, "The sound will be sent reliably, and without regard to phs.", CF_RELIABLE}, + {"SOUNDFLAG_ABSVOLUME", "const float", /*QW|NQ|*/CS,"The sample's volume is not scaled by the volume cvar. Use with caution", CF_ABSVOLUME}, + {"SOUNDFLAG_FORCELOOP", "const float", /*QW|NQ|*/CS,"The sound will restart once it reaches the end of the sample.", CF_FORCELOOP}, + {"SOUNDFLAG_NOSPACIALISE", "const float", /*QW|NQ|*/CS,"The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation.", CF_NOSPACIALISE}, {"ATTN_NONE", "const float", QW|NQ|CS, "Sounds with this attenuation can be heard throughout the map", ATTN_NONE}, {"ATTN_NORM", "const float", QW|NQ|CS, "Standard attenuation", ATTN_NORM}, @@ -11103,11 +11219,11 @@ void PR_DumpPlatform_f(void) {"STUFFCMD_IGNOREINDEMO","const float", QW|NQ, "The protocol we are connected to the server with.", STUFFCMD_IGNOREINDEMO}, {"STUFFCMD_DEMOONLY", "const float", QW|NQ, "The protocol we are connected to the server with.", STUFFCMD_DEMOONLY}, - {"SOUND_RELIABLE", "const float", QW|NQ, "The sound will be sent reliably, and without regard to phs.", CF_RELIABLE}, +/* {"SOUND_RELIABLE", "const float", QW|NQ, "The sound will be sent reliably, and without regard to phs.", CF_RELIABLE}, {"SOUND_FORCELOOP", "const float", QW|NQ|CS,"The sound will restart once it reaches the end of the sample.", CF_FORCELOOP}, {"SOUND_NOSPACIALISE", "const float", QW|NQ|CS,"The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation.", CF_NOSPACIALISE}, {"SOUND_ABSVOLUME", "const float", QW|NQ|CS,"The sample's volume is not scaled by the volume cvar. Use with caution", CF_ABSVOLUME}, - +*/ // edict.flags {"FL_FLY", "const float", QW|NQ|CS, NULL, FL_FLY}, {"FL_SWIM", "const float", QW|NQ|CS, NULL, FL_SWIM}, @@ -11171,6 +11287,17 @@ void PR_DumpPlatform_f(void) {"MF_TRACER2", "const float", QW|NQ|CS, NULL, EF_MF_TRACER2>>24}, {"MF_TRACER3", "const float", QW|NQ|CS, NULL, EF_MF_TRACER3>>24}, + + {"SL_ORG_TL", "const float", QW|NQ, NULL, SL_ORG_TL}, + {"SL_ORG_TR", "const float", QW|NQ, NULL, SL_ORG_TR}, + {"SL_ORG_BL", "const float", QW|NQ, NULL, SL_ORG_BL}, + {"SL_ORG_BR", "const float", QW|NQ, NULL, SL_ORG_BR}, + {"SL_ORG_MM", "const float", QW|NQ, NULL, SL_ORG_MM}, + {"SL_ORG_TM", "const float", QW|NQ, NULL, SL_ORG_TM}, + {"SL_ORG_BM", "const float", QW|NQ, NULL, SL_ORG_BM}, + {"SL_ORG_ML", "const float", QW|NQ, NULL, SL_ORG_ML}, + {"SL_ORG_MR", "const float", QW|NQ, NULL, SL_ORG_MR}, + {"PFLAGS_NOSHADOW", "const float", QW|NQ|CS, "Associated RT lights attached will not cast shadows, making them significantly faster to draw.", PFLAGS_NOSHADOW}, {"PFLAGS_CORONA", "const float", QW|NQ|CS, "Enables support of coronas on the associated rtlights.", PFLAGS_CORONA}, {"PFLAGS_FULLDYNAMIC", "const float", QW|NQ, "When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used.", PFLAGS_FULLDYNAMIC}, diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index d75445ef0..8f1a349fe 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -540,6 +540,7 @@ void SV_Map_f (void) if (strlen(level) > 4 && (!strcmp(level + strlen(level)-4, ".cin") || !strcmp(level + strlen(level)-4, ".roq") || + !strcmp(level + strlen(level)-4, ".pcx") || !strcmp(level + strlen(level)-4, ".avi"))) { cinematic = true; @@ -2590,7 +2591,7 @@ static void SV_SendGameCommand_f(void) } else #endif - Con_Printf("This command requires a Q2 sever\n"); + Con_Printf("Mod-specific command not known\n"); } diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index ae0233fce..f59208c51 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -2705,10 +2705,10 @@ int glowsize=0, glowcolor=0, colourmod=0; else MSG_WriteByte (msg,ent->number); - if (bits & NQU_MODEL) MSG_WriteByte (msg, ent->modelindex); - if (bits & NQU_FRAME) MSG_WriteByte (msg, ent->frame); - if (bits & NQU_COLORMAP) MSG_WriteByte (msg, ent->colormap); - if (bits & NQU_SKIN) MSG_WriteByte (msg, ent->skinnum); + if (bits & NQU_MODEL) MSG_WriteByte (msg, ent->modelindex & 0xff); + if (bits & NQU_FRAME) MSG_WriteByte (msg, ent->frame & 0xff); + if (bits & NQU_COLORMAP) MSG_WriteByte (msg, ent->colormap & 0xff); + if (bits & NQU_SKIN) MSG_WriteByte (msg, ent->skinnum & 0xff); if (bits & NQU_EFFECTS) MSG_WriteByte (msg, eff & 0x00ff); if (bits & NQU_ORIGIN1) MSG_WriteCoord (msg, ent->origin[0]); if (bits & NQU_ANGLE1) MSG_WriteAngle(msg, ent->angles[0]); @@ -2723,18 +2723,18 @@ int glowsize=0, glowcolor=0, colourmod=0; if (bits & RMQU_SCALE) MSG_WriteByte(msg, ent->scale); if (bits & FITZU_FRAME2) MSG_WriteByte(msg, ent->frame>>8); if (bits & FITZU_MODEL2) MSG_WriteByte(msg, ent->modelindex>>8); - if (bits & FITZU_LERPFINISH)MSG_WriteByte(msg, (ed->v->nextthink - sv.world.physicstime) * 255); + if (bits & FITZU_LERPFINISH)MSG_WriteByte(msg, bound(0, (int)((ed->v->nextthink - sv.world.physicstime) * 255), 255)); } else { - if (bits & DPU_ALPHA) MSG_WriteByte(msg, ent->trans*255); - if (bits & DPU_SCALE) MSG_WriteByte(msg, ent->scale*16); + if (bits & DPU_ALPHA) MSG_WriteByte(msg, ent->trans); + if (bits & DPU_SCALE) MSG_WriteByte(msg, ent->scale); if (bits & DPU_EFFECTS2) MSG_WriteByte(msg, eff >> 8); if (bits & DPU_GLOWSIZE) MSG_WriteByte(msg, glowsize); if (bits & DPU_GLOWCOLOR) MSG_WriteByte(msg, glowcolor); if (bits & DPU_COLORMOD) MSG_WriteByte(msg, colourmod); - if (bits & DPU_FRAME2) MSG_WriteByte(msg, (int)ent->frame >> 8); - if (bits & DPU_MODEL2) MSG_WriteByte(msg, (int)ent->modelindex >> 8); + if (bits & DPU_FRAME2) MSG_WriteByte(msg, ent->frame >> 8); + if (bits & DPU_MODEL2) MSG_WriteByte(msg, ent->modelindex >> 8); } } #endif diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 15de31ab2..119508761 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -5290,7 +5290,10 @@ void SV_Init (quakeparms_t *parms) #endif if (sv.state == ss_dead) - SV_Error ("Couldn't load a map"); + { + Cmd_ExecuteString("path", RESTRICT_LOCAL); + SV_Error ("Couldn't load a map. You may need to use the -basedir argument."); + } } } diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index d0fd1eddf..872cd6c54 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -196,7 +196,10 @@ void Sys_Error (const char *error, ...) tcsetattr(STDIN_FILENO, TCSADRAIN, &orig); -*(int*)-3 = 0; + //we used to fire sigsegv. this resulted in people reporting segfaults and not the error message that appeared above. resulting in wasted debugging. + //abort should trigger a SIGABRT and still give us the same stack trace. should be more useful that way. + abort(); + exit (1); } @@ -616,6 +619,7 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) case SIGILL: strcpy(signame, "SIGILL"); break; case SIGFPE: strcpy(signame, "SIGFPE"); break; case SIGBUS: strcpy(signame, "SIGBUS"); break; + case SIGABRT: strcpy(signame, "SIGABRT"); break; case SIGSEGV: Q_snprintfz(signame, sizeof(signame), "SIGSEGV (%p)", info->si_addr); break; default: Q_snprintfz(signame, sizeof(signame), "%i", sig); break; } @@ -707,6 +711,7 @@ int main(int argc, char *argv[]) sigaction(SIGILL, &act, NULL); sigaction(SIGFPE, &act, NULL); sigaction(SIGSEGV, &act, NULL); + sigaction(SIGABRT, &act, NULL); sigaction(SIGBUS, &act, NULL); } #endif diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 4850dbfaf..28deeaf3c 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -81,10 +81,10 @@ cvar_t sv_floodprotect_suicide = CVAR("sv_floodprotect_suicide", "1"); cvar_t sv_floodprotect_sendmessage = CVARAF("sv_floodprotect_sendmessage", "", "floodprotmsg", 0); -cvar_t votelevel = SCVAR("votelevel", "0"); -cvar_t voteminimum = SCVAR("voteminimum", "4"); -cvar_t votepercent = SCVAR("votepercent", "-1"); -cvar_t votetime = SCVAR("votetime", "10"); +cvar_t votelevel = CVARD("votelevel", "0", "This is the restriction level of commands that players may vote for. You can reconfigure commands, cvars, or aliases individually. Additionally, aliases can be configured via aliaslevel to be executed at a different level from their restriction level. This can be used to indirectly allow voting for 'map dm4' for instance, without allowing people to vote for every map."); +cvar_t voteminimum = CVARD("voteminimum", "4", "At least this many players must vote the same way for the vote to pass."); +cvar_t votepercent = CVARD("votepercent", "-1", "At least this percentage of players must vote the same way for the vote to pass."); +cvar_t votetime = CVARD("votetime", "10", "Votes will be discarded after this many minutes"); cvar_t pr_allowbutton1 = CVARFD("pr_allowbutton1", "1", CVAR_LATCH, "The button1 field is believed to have been intended to work with the +use command, but it was never hooked up. In NetQuake, this field was often repurposed for other things as it was not otherwise used (and cannot be removed without breaking the crc), while third-party QuakeWorld engines did decide to implement it as believed was intended. As a result, this cvar only applies to QuakeWorld mods and a value of 1 is only likely to cause issues with NQ mods that were ported to QW."); extern cvar_t sv_minping; @@ -445,13 +445,15 @@ void SV_New_f (void) void SVNQ_New_f (void) { extern cvar_t coop; - char message[2048]; - int i; - int maxplayers = 0; - int op; + char message[2048]; + char build[256], mapname[128]; + int i; + int maxplayers = 0; + int op; unsigned int protext1 = 0, protext2 = 0, protmain = 0, protfl = 0; char *protoname; extern cvar_t sv_listen_nq; + const char *gamedir; host_client->prespawn_stage = PRESPAWN_INVALID; host_client->prespawn_idx = 0; @@ -554,8 +556,28 @@ void SVNQ_New_f (void) break; } +#ifdef OFFICIAL_RELEASE + Q_snprintfz(build, sizeof(build), "v%i.%02i", FTE_VER_MAJOR, FTE_VER_MINOR); +#else +#if defined(SVNREVISION) + if (strcmp(STRINGIFY(SVNREVISION), "-")) + Q_snprintfz(build, sizeof(build), "SVN %s", STRINGIFY(SVNREVISION)); + else +#endif + Q_snprintfz(build, sizeof(build), "%s", __DATE__); +#endif + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + { + gamedir = FS_GetGamedir(true); + } + COM_FileBase(sv.modelname, mapname, sizeof(mapname)); + + Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (%s%s%s%s %s) - %s", 2, gamedir, + protoname,(protext1||(protext2&~(PEXT2_REPLACEMENTDELTAS|PEXT2_VOICECHAT)))?"+":"",(protext2&PEXT2_REPLACEMENTDELTAS)?"F":"",(protext2&PEXT2_VOICECHAT)?"V":"", + build, mapname); MSG_WriteByte (&host_client->netchan.message, svc_print); - Q_snprintfz (message, sizeof(message), "%c\n%s %s%s%s%s server\n", 2, version_string(), (protext2&PEXT2_REPLACEMENTDELTAS)?"FTE":"", protoname, (protext1||(protext2&~(PEXT2_REPLACEMENTDELTAS|PEXT2_VOICECHAT)))?"+":"", (protext2&PEXT2_VOICECHAT)?"V":""); MSG_WriteString (&host_client->netchan.message,message); if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7) @@ -4145,12 +4167,23 @@ void SV_Vote_f (void) if (!votelevel.value || ((host_client->penalties & (BAN_MUTE|BAN_DEAF)) == (BAN_MUTE|BAN_DEAF))) { - SV_ClientTPrintf(host_client, PRINT_HIGH, "Voting was dissallowed\n"); + SV_ClientTPrintf(host_client, PRINT_HIGH, "Voting is dissallowed on this server\n"); + return; + } + if (!*command) + { + char cmds[900]; + Cmd_EnumerateLevel(votelevel.value, cmds, sizeof(cmds)); + SV_ClientTPrintf(host_client, PRINT_HIGH, "Allowed commands:\n%s\n", cmds); return; } if (host_client->penalties & BAN_MUTE) { - SV_ClientTPrintf(host_client, PRINT_HIGH, "Sorry, you cannot vote when muted as it may allow you to send a message.\n"); + //pretend to vote for it + if (host_client->penalties & BAN_STEALTH) + SV_ClientTPrintf(host_client, PRINT_HIGH, "%s casts a vote for '%s'\n", host_client->name, command); + else + SV_ClientTPrintf(host_client, PRINT_HIGH, "Sorry, you cannot vote when muted as it may allow you to send a message.\n"); return; } @@ -5591,7 +5624,7 @@ ucmd_t ucmdsq2[] = { {"nextserver", SVQ2_NextServer_f, true}, - {"vote", SV_Vote_f, true}, + {"ftevote", SV_Vote_f, true}, //#ifdef SVRANKING // {"topten", Rank_ListTop10_f, true}, @@ -6930,10 +6963,18 @@ done: args[i] = 0; rname = MSG_ReadString(); if (i) - rname = va("Cmd_%s_%s", rname, args); + rname = va("CSEv_%s_%s", rname, args); else - rname = va("Cmd_%s", rname); + rname = va("CSEv_%s", rname); f = PR_FindFunction(svprogfuncs, rname, PR_ANY); + if (!f) + { + if (i) + rname = va("Cmd_%s_%s", rname, args); + else + rname = va("Cmd_%s", rname); + f = PR_FindFunction(svprogfuncs, rname, PR_ANY); + } if (f) { pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); diff --git a/plugins/ezhud/hud.c b/plugins/ezhud/hud.c index 02c969f0f..6a9c0a5d3 100644 --- a/plugins/ezhud/hud.c +++ b/plugins/ezhud/hud.c @@ -75,7 +75,6 @@ void HUD_Plus_f(void) { char *t; hud_t *hud; - char buf[64]; if (Cmd_Argc() < 1) return; diff --git a/plugins/plugin.c b/plugins/plugin.c index f2c3aec8d..3620c3627 100644 --- a/plugins/plugin.c +++ b/plugins/plugin.c @@ -436,7 +436,9 @@ void Plug_InitStandardBuiltins(void) CHECKBUILTIN(GetPlayerInfo); CHECKBUILTIN(LocalPlayerNumber); CHECKBUILTIN(GetLocalPlayerNumbers); +#ifdef FTEPLUGIN CHECKBUILTIN(GetLastInputFrame); +#endif CHECKBUILTIN(GetTrackerOwnFrags); CHECKBUILTIN(GetServerInfo); CHECKBUILTIN(SetUserInfo);