Compare commits
44
Commits
fixgnu23
...
mmenucleanup
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
097cd5e2ca | ||
|
|
cd4836ae99 | ||
|
|
f0acf179c2 | ||
|
|
836ad12c40 | ||
|
|
a147fa3100 | ||
|
|
48d9b2bc98 | ||
|
|
939bf588b3 | ||
|
|
b0653416f6 | ||
|
|
f2dc344bf8 | ||
|
|
c0654448f0 | ||
|
|
2bf3912633 | ||
|
|
4d71fb8409 | ||
|
|
8543a099f4 | ||
|
|
9c6ec02b18 | ||
|
|
2800685cfc | ||
|
|
5d8bb3223f | ||
|
|
5e1f943a38 | ||
|
|
fca7b4a837 | ||
|
|
d2bab80b29 | ||
|
|
78ab90c1a6 | ||
|
|
102e944fd5 | ||
|
|
9bd4a933cf | ||
|
|
e233635783 | ||
|
|
da24ba20c7 | ||
|
|
74bdabc636 | ||
|
|
4b129314d3 | ||
|
|
3a55972997 | ||
|
|
6e04e4171e | ||
|
|
d1a6394e52 | ||
|
|
ccc90842a7 | ||
|
|
dc069d7bb5 | ||
|
|
f4a827dfc2 | ||
|
|
4f3232b6cc | ||
|
|
3685502edf | ||
|
|
f87bcef079 | ||
|
|
c3458afe2b | ||
|
|
17bc8a4a36 | ||
|
|
b04f017ab6 | ||
|
|
083ddb6396 | ||
|
|
c3129742c3 | ||
|
|
877bd640f3 | ||
|
|
3233df9c6c | ||
|
|
9ad5f5189c | ||
|
|
c7718dea79 |
No files matched your search
@@ -29,6 +29,7 @@ m_cond.c
|
||||
m_fixed.c
|
||||
m_menu.c
|
||||
m_textinput.c
|
||||
m_emotes.cpp
|
||||
m_misc.c
|
||||
m_perfstats.c
|
||||
m_queue.c
|
||||
|
||||
+833
-701
File diff suppressed because it is too large.
Load diff
+2
-3
@@ -42,8 +42,8 @@ applications may follow different packet versions.
|
||||
#define VANILLA_GT_MATCH 3
|
||||
|
||||
// Networking and tick handling related.
|
||||
#define BACKUPTICS 32
|
||||
#define TICQUEUE 512 // more than enough for most timeouts....
|
||||
#define BACKUPTICS 512 // more than enough for most timeouts....
|
||||
#define CLIENTBACKUPTICS 32
|
||||
#define MAXTEXTCMD 256
|
||||
|
||||
// No. of tics your controls can be delayed by.
|
||||
@@ -533,7 +533,6 @@ extern UINT32 serverlistultimatecount;
|
||||
extern INT32 mapchangepending;
|
||||
|
||||
// Points inside doomcom
|
||||
extern doomdata_t *netbuffer;
|
||||
extern consvar_t cv_stunserver;
|
||||
extern consvar_t cv_httpsource;
|
||||
extern consvar_t cv_kicktime;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "g_game.h"
|
||||
#include "g_input.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "m_emotes.h"
|
||||
#include "i_sound.h"
|
||||
#include "i_system.h"
|
||||
#include "i_time.h"
|
||||
@@ -2036,6 +2037,8 @@ void D_SRB2Main(void)
|
||||
|
||||
S_InitMusicDefs();
|
||||
|
||||
M_InitEmotes();
|
||||
|
||||
CONS_Printf("ST_Init(): Init status bar.\n");
|
||||
ST_Init();
|
||||
|
||||
|
||||
+34
-29
@@ -48,12 +48,6 @@ tic_t connectiontimeout = (10*TICRATE);
|
||||
|
||||
/// \brief network packet
|
||||
doomcom_t *doomcom = NULL;
|
||||
/// \brief network packet data, points inside doomcom
|
||||
doomdata_t *netbuffer = NULL;
|
||||
/// \brief hole punching packet, also points inside doomcom
|
||||
#ifdef HOLEPUNCH
|
||||
holepunch_t *holepunchpacket = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
FILE *debugfile = NULL; // put some net info in a file during the game
|
||||
@@ -209,13 +203,9 @@ static node_t nodes[MAXNETNODES];
|
||||
// 0 if a = n (mod 256)
|
||||
// >0 if a > b (mod 256)
|
||||
// mnemonic: to use it compare to 0: cmpack(a,b)<0 is "a < b" ...
|
||||
FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
|
||||
FUNCMATH static inline INT32 cmpack(UINT8 a, UINT8 b)
|
||||
{
|
||||
register INT32 d = a - b;
|
||||
|
||||
if (d >= 127 || d < -128)
|
||||
return -d;
|
||||
return d;
|
||||
return (SINT8)(a - b);
|
||||
}
|
||||
|
||||
/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
|
||||
@@ -229,6 +219,8 @@ static boolean GetFreeAcknum(UINT8 *freeack)
|
||||
node_t *node = &nodes[doomcom->remotenode];
|
||||
INT32 i, numfreeslot = 0;
|
||||
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
|
||||
{
|
||||
DEBFILE(va("too fast %d %d\n",node->remotefirstack,node->nextacknum));
|
||||
@@ -298,6 +290,7 @@ static boolean Processackpak(void)
|
||||
INT32 i;
|
||||
boolean goodpacket = true;
|
||||
node_t *node = &nodes[doomcom->remotenode];
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
// Received an ack return, so remove the ack in the list
|
||||
if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
|
||||
@@ -307,7 +300,7 @@ static boolean Processackpak(void)
|
||||
// Search the ackbuffer and free it
|
||||
for (i = 0; i < MAXACKPACKETS; i++)
|
||||
{
|
||||
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
|
||||
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode
|
||||
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
|
||||
{
|
||||
RemoveAck(i);
|
||||
@@ -423,6 +416,7 @@ void Net_SendAcks(INT32 node)
|
||||
#ifdef NONET
|
||||
(void)node;
|
||||
#else
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
netbuffer->packettype = PT_NOTHING;
|
||||
M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND);
|
||||
HSendPacket(node, false, 0, MAXACKTOSEND);
|
||||
@@ -434,6 +428,8 @@ static void GotAcks(void)
|
||||
{
|
||||
INT32 i, j;
|
||||
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
for (j = 0; j < MAXACKTOSEND; j++)
|
||||
{
|
||||
if (netbuffer->u.textcmd[j])
|
||||
@@ -487,6 +483,8 @@ void Net_AckTicker(void)
|
||||
#ifndef NONET
|
||||
INT32 i;
|
||||
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
for (i = 0; i < MAXACKPACKETS; i++)
|
||||
{
|
||||
const INT32 nodei = ackpak[i].destinationnode;
|
||||
@@ -542,6 +540,7 @@ void Net_UnAcknowledgePacket(INT32 node)
|
||||
#ifdef NONET
|
||||
(void)node;
|
||||
#else
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
|
||||
DEBFILE(va("UnAcknowledge node %d\n", node));
|
||||
|
||||
@@ -736,6 +735,7 @@ void Net_CloseConnection(INT32 node)
|
||||
//
|
||||
static UINT32 NetbufferChecksum(void)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
UINT32 c = 0x1234567;
|
||||
const INT32 l = doomcom->datalength - 4;
|
||||
const UINT8 *buf = (UINT8 *)netbuffer + 4;
|
||||
@@ -852,6 +852,8 @@ const char *Net_GetPacketName(UINT8 packettype)
|
||||
|
||||
static void DebugPrintpacket(const char *header)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
fprintf(debugfile, "%-12s (node %d,ack %d,ackret %d,size %d) type(%d) : %s\n",
|
||||
header, doomcom->remotenode, netbuffer->ack, netbuffer->ackreturn, doomcom->datalength,
|
||||
netbuffer->packettype, Net_GetPacketName(netbuffer->packettype));
|
||||
@@ -1021,6 +1023,7 @@ void Command_Droprate(void)
|
||||
#ifndef NONET
|
||||
static boolean ShouldDropPacket(void)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
return (packetdropquantity[netbuffer->packettype])
|
||||
|| (packetdroprate != 0 && rand() < (((double)RAND_MAX) * (packetdroprate / 100.f))) || packetdroprate == 100;
|
||||
}
|
||||
@@ -1032,6 +1035,7 @@ static boolean ShouldDropPacket(void)
|
||||
//
|
||||
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
|
||||
|
||||
if (node == 0) // Packet is to go back to us
|
||||
@@ -1132,7 +1136,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
|
||||
//
|
||||
boolean HGetPacket(void)
|
||||
{
|
||||
//boolean nodejustjoined;
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
// Get a packet from self
|
||||
if (rebound_tail != rebound_head)
|
||||
@@ -1220,6 +1224,18 @@ static void Internal_FreeNodenum(INT32 nodenum)
|
||||
(void)nodenum;
|
||||
}
|
||||
|
||||
static char *I_NetSplitAddress(char *host, char **port)
|
||||
{
|
||||
boolean v4 = (host[0] != '[');
|
||||
|
||||
host = strtok(host, v4 ? ":" : "[]");
|
||||
|
||||
if (port)
|
||||
*port = strtok(NULL, ":");
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
SINT8 I_NetMakeNode(const char *hostname)
|
||||
{
|
||||
SINT8 newnode = -1;
|
||||
@@ -1227,20 +1243,14 @@ SINT8 I_NetMakeNode(const char *hostname)
|
||||
if (I_NetMakeNodewPort)
|
||||
{
|
||||
char *localhostname = strdup(hostname);
|
||||
char *t = localhostname;
|
||||
const char *port;
|
||||
char *port;
|
||||
if (!localhostname)
|
||||
return newnode;
|
||||
|
||||
// retrieve portnum from address!
|
||||
strtok(localhostname, ":");
|
||||
port = strtok(NULL, ":");
|
||||
hostname = I_NetSplitAddress(localhostname, &port);
|
||||
|
||||
// remove the port in the hostname as we've it already
|
||||
while ((*t != ':') && (*t != '\0'))
|
||||
t++;
|
||||
*t = '\0';
|
||||
|
||||
newnode = I_NetMakeNodewPort(localhostname, port);
|
||||
newnode = I_NetMakeNodewPort(hostname, port);
|
||||
free(localhostname);
|
||||
}
|
||||
return newnode;
|
||||
@@ -1332,11 +1342,6 @@ boolean D_CheckNetGame(void)
|
||||
if (doomcom->numnodes > MAXNETNODES)
|
||||
I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
|
||||
|
||||
netbuffer = (doomdata_t *)(void *)&doomcom->data;
|
||||
#ifdef HOLEPUNCH
|
||||
holepunchpacket = (holepunch_t *)(void *)&doomcom->data;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
if (M_CheckParm("-debugfile"))
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "i_system.h"
|
||||
#include "g_game.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "m_emotes.h"
|
||||
#include "g_input.h"
|
||||
#include "m_menu.h"
|
||||
#include "r_local.h"
|
||||
@@ -912,6 +913,8 @@ void D_RegisterClientCommands(void)
|
||||
|
||||
CV_RegisterVar(&cv_keyboardlayout);
|
||||
|
||||
CV_RegisterVar(&cv_emotes);
|
||||
|
||||
// time attack ghost options are also saved to config
|
||||
CV_RegisterVar(&cv_ghost_besttime);
|
||||
CV_RegisterVar(&cv_ghost_bestlap);
|
||||
|
||||
@@ -142,6 +142,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
|
||||
{
|
||||
size_t i;
|
||||
UINT8 count = 0;
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
UINT8 *p_start = netbuffer->packettype == PT_MOREFILESNEEDED ? netbuffer->u.filesneededcfg.files : netbuffer->u.serverinfo.fileneeded;
|
||||
UINT8 *p = p_start;
|
||||
char wadfilename[MAX_WADPATH] = "";
|
||||
@@ -323,6 +324,7 @@ boolean CL_SendRequestFile(void)
|
||||
#ifdef MORELEGACYDOWNLOADER
|
||||
boolean firstloop = true;
|
||||
#endif
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
#ifdef PARANOIA
|
||||
if (M_CheckParm("-nodownload"))
|
||||
@@ -451,9 +453,11 @@ tryagain:
|
||||
// returns false if a requested file was not found or cannot be sent
|
||||
boolean Got_RequestFilePak(INT32 node)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
char wad[MAX_WADPATH+1];
|
||||
UINT8 *p = netbuffer->u.textcmd;
|
||||
UINT8 id;
|
||||
|
||||
while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow
|
||||
{
|
||||
id = READUINT8(p);
|
||||
@@ -817,6 +821,7 @@ void SV_FileSendTicker(void)
|
||||
size_t size;
|
||||
filetx_t *f;
|
||||
INT32 packetsent, ram, i, j;
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
||||
if (!filestosend) // No file to send
|
||||
return;
|
||||
@@ -945,6 +950,7 @@ void SV_FileSendTicker(void)
|
||||
|
||||
void Got_Filetxpak(void)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
INT32 filenum = netbuffer->u.filetxpak.fileid;
|
||||
fileneeded_t *file = &fileneeded[filenum];
|
||||
char *filename = file->filename;
|
||||
|
||||
+1
-1
@@ -575,7 +575,7 @@ extern consvar_t cv_downloading; // allow clients to downloading WADs.
|
||||
extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu
|
||||
extern consvar_t cv_jointimeout;
|
||||
extern consvar_t cv_maxping;
|
||||
extern ticcmd_t netcmds[TICQUEUE][MAXPLAYERS];
|
||||
extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
|
||||
extern INT32 serverplayer;
|
||||
extern INT32 adminplayers[MAXPLAYERS];
|
||||
|
||||
|
||||
+1
-1
@@ -1787,7 +1787,7 @@ void G_Ticker(boolean run)
|
||||
}
|
||||
}
|
||||
|
||||
buf = gametic % TICQUEUE;
|
||||
buf = gametic % BACKUPTICS;
|
||||
|
||||
if (!demo.playback)
|
||||
{
|
||||
|
||||
+5
-1
@@ -220,7 +220,11 @@ HMS_connect (const char *format, ...)
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, (long)CURL_IPRESOLVE_V4);
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (!M_CheckParm("-ipv6"))
|
||||
#endif
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, (long)CURL_IPRESOLVE_V4);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)cv_masterserver_timeout.value);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
|
||||
|
||||
+100
-16
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "m_menu.h" // gametype_cons_t
|
||||
#include "m_cond.h" // emblems
|
||||
#include "m_emotes.h"
|
||||
|
||||
#include "d_clisrv.h"
|
||||
|
||||
@@ -94,6 +95,9 @@ static textinput_t w_chat;
|
||||
static boolean headsupactive = false;
|
||||
boolean hu_showscores; // draw rankings
|
||||
static char hu_tick;
|
||||
static tic_t hu_emoteanim = 0;
|
||||
#define MAXEMOTESUGGESTIONS 8
|
||||
static emote_t *emote_suggestions[MAXEMOTESUGGESTIONS] = {0};
|
||||
|
||||
static huddrawlist_h luahuddrawlist_scores;
|
||||
|
||||
@@ -1106,6 +1110,7 @@ void HU_Ticker(void)
|
||||
return;
|
||||
|
||||
hu_tick++;
|
||||
hu_emoteanim++;
|
||||
hu_tick &= 7; // currently only to blink chat input cursor
|
||||
|
||||
if (PLAYER1INPUTDOWN(gc_scores))
|
||||
@@ -1399,7 +1404,7 @@ boolean HU_Responder(event_t *ev)
|
||||
&& ev->data1 != gamecontrol[0][gc_talkkey][1]))
|
||||
return false;
|
||||
|
||||
M_TextInputHandle(&w_chat, c);
|
||||
M_TextInputHandleEmotes(&w_chat, c, emote_suggestions, MAXEMOTESUGGESTIONS);
|
||||
|
||||
if (c == KEY_ENTER)
|
||||
{
|
||||
@@ -1439,6 +1444,8 @@ boolean HU_Responder(event_t *ev)
|
||||
|
||||
#ifndef NONET
|
||||
|
||||
#define HU_DrawEmote(x, y, emote, flags) M_DrawEmote((x), (y), (emote), hu_emoteanim, (flags))
|
||||
|
||||
// Precompile a wordwrapped string to any given width.
|
||||
// This is a muuuch better method than V_WORDWRAP.
|
||||
// again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day.
|
||||
@@ -1450,6 +1457,8 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
|
||||
size_t slen;
|
||||
char *newstring = Z_StrDup(string);
|
||||
INT32 spacewidth = (vid.width < 640) ? 8 : 4, charwidth = (vid.width < 640) ? 8 : 4;
|
||||
emote_t *emote = NULL;
|
||||
int emotelen = 0;
|
||||
|
||||
slen = strlen(string);
|
||||
x = 0;
|
||||
@@ -1471,7 +1480,12 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
|
||||
c = toupper(c);
|
||||
c -= HU_FONTSTART;
|
||||
|
||||
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
|
||||
if ((emote = M_VerifyEmote(string+i, &emotelen)))
|
||||
{
|
||||
chw = EMOTEWIDTH;
|
||||
i += emotelen-1; // Will be incremented on next loop iteration, hence -1
|
||||
}
|
||||
else if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
|
||||
{
|
||||
chw = spacewidth;
|
||||
lastusablespace = i;
|
||||
@@ -1493,7 +1507,6 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
|
||||
return newstring;
|
||||
}
|
||||
|
||||
|
||||
// 30/7/18: chaty is now the distance at which the lowest point of the chat will be drawn if that makes any sense.
|
||||
|
||||
INT16 chatx = 13, chaty = 169; // let's use this as our coordinates, shh
|
||||
@@ -1526,6 +1539,8 @@ static void HU_drawMiniChat(void)
|
||||
char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]);
|
||||
size_t j = 0;
|
||||
INT32 linescount = 0;
|
||||
emote_t *emote = NULL;
|
||||
int emotelen = 0;
|
||||
|
||||
while(msg[j]) // iterate through msg
|
||||
{
|
||||
@@ -1550,6 +1565,11 @@ static void HU_drawMiniChat(void)
|
||||
|
||||
++j;
|
||||
}
|
||||
else if ((emote = M_VerifyEmote(msg+j, &emotelen)))
|
||||
{
|
||||
dx += EMOTEWIDTH - charwidth;
|
||||
j += emotelen;
|
||||
}
|
||||
else
|
||||
{
|
||||
j++;
|
||||
@@ -1595,6 +1615,8 @@ static void HU_drawMiniChat(void)
|
||||
size_t j = 0;
|
||||
char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it.
|
||||
UINT8 *colormap = NULL;
|
||||
emote_t *emote = NULL;
|
||||
int emotelen = 0;
|
||||
|
||||
while(msg[j]) // iterate through msg
|
||||
{
|
||||
@@ -1621,6 +1643,16 @@ static void HU_drawMiniChat(void)
|
||||
|
||||
++j;
|
||||
}
|
||||
else if ((emote = M_VerifyEmote(msg+j, &emotelen)))
|
||||
{
|
||||
if (cv_chatbacktint.value) // on request of wolfy
|
||||
V_DrawFillConsoleMap(x + dx + 2, y+dy, EMOTEWIDTH, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
|
||||
HU_DrawEmote(x+dx+2, y+dy, emote, V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag);
|
||||
dx += EMOTEWIDTH - charwidth;
|
||||
|
||||
j += emotelen;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cv_chatbacktint.value) // on request of wolfy
|
||||
@@ -1698,6 +1730,9 @@ static void HU_drawChatLog(INT32 offset)
|
||||
INT32 j = 0;
|
||||
char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]); // get the current message, and word wrap it.
|
||||
UINT8 *colormap = NULL;
|
||||
emote_t *emote = NULL;
|
||||
int emotelen = 0;
|
||||
|
||||
while(msg[j]) // iterate through msg
|
||||
{
|
||||
if (msg[j] < HU_FONTSTART) // don't draw
|
||||
@@ -1719,6 +1754,15 @@ static void HU_drawChatLog(INT32 offset)
|
||||
|
||||
++j;
|
||||
}
|
||||
else if ((emote = M_VerifyEmote(msg+j, &emotelen)))
|
||||
{
|
||||
if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy)))
|
||||
{
|
||||
HU_DrawEmote(x+dx+2, y+dy, emote, V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
dx += EMOTEWIDTH - charwidth;
|
||||
}
|
||||
j += emotelen;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy)))
|
||||
@@ -1844,7 +1888,7 @@ static void HU_DrawChat(void)
|
||||
typelines = 1;
|
||||
|
||||
if ((w_chat.cursor == 0 || w_chat.length == 0) && hu_tick < 4)
|
||||
V_DrawChatCharacter(chatx+2+c+charwidth*w_chat.cursor, y+1, '_'|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
|
||||
V_DrawChatCharacter(chatx+2+c, y+1, '_'|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
|
||||
|
||||
if (w_chat.select != w_chat.cursor)
|
||||
{
|
||||
@@ -1855,30 +1899,40 @@ static void HU_DrawChat(void)
|
||||
while (w_chat_buf[i])
|
||||
{
|
||||
boolean skippedline = false;
|
||||
if (w_chat.cursor == (i+1))
|
||||
emote_t *emote = NULL;
|
||||
int emotelen = 0;
|
||||
int drawwidth = charwidth; // if we've drawn emote, selection needs to account for that
|
||||
|
||||
if ((emote = M_VerifyEmote(w_chat_buf+i, &emotelen)))
|
||||
{
|
||||
HU_DrawEmote(chatx + c + 2, y-1, emote, V_SNAPTOBOTTOM|V_SNAPTOLEFT|t);
|
||||
c += EMOTEWIDTH - charwidth;
|
||||
i += emotelen-1;
|
||||
drawwidth = EMOTEWIDTH;
|
||||
}
|
||||
else if (w_chat_buf[i] >= HU_FONTSTART) //Hurdler: isn't it better like that?
|
||||
V_DrawChatCharacter(chatx + c + 2, y, w_chat_buf[i] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
|
||||
|
||||
// Draw selection
|
||||
if (i >= select_start && i < select_end)
|
||||
V_DrawFill(chatx + c + 2 - (drawwidth-charwidth), y-1, drawwidth, charheight, 103|V_TRANSLUCENT|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t);
|
||||
|
||||
++i;
|
||||
|
||||
if (w_chat.cursor == i)
|
||||
{
|
||||
INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down.
|
||||
INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
|
||||
if (hu_tick < 4)
|
||||
V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
|
||||
|
||||
if (cursorx == chatx+1 && saylen == i) // a weirdo hack
|
||||
if (cursorx == chatx+1 && saylen == i-1) // a weirdo hack
|
||||
{
|
||||
typelines += 1;
|
||||
skippedline = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Hurdler: isn't it better like that?
|
||||
if (w_chat_buf[i] >= HU_FONTSTART)
|
||||
V_DrawChatCharacter(chatx + c + 2, y, w_chat_buf[i] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
|
||||
|
||||
// Draw selection
|
||||
if (i >= select_start && i < select_end)
|
||||
V_DrawFill(chatx + c + 2, y-1, charwidth, charheight, 103|V_TRANSLUCENT|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t);
|
||||
|
||||
++i;
|
||||
|
||||
c += charwidth;
|
||||
if (c > boxw-(charwidth*2) && !skippedline)
|
||||
{
|
||||
@@ -1888,6 +1942,36 @@ static void HU_DrawChat(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (emote_suggestions[0])
|
||||
{
|
||||
// A bit of copy-paste from /pm code :p
|
||||
INT32 suggesty = chaty - charheight - 1;
|
||||
size_t longest_suggestion_length = 0;
|
||||
|
||||
for (i = 0; i < MAXEMOTESUGGESTIONS && emote_suggestions[i]; ++i)
|
||||
{
|
||||
longest_suggestion_length = max(longest_suggestion_length, strlen(emote_suggestions[i]->name));
|
||||
}
|
||||
|
||||
#ifdef NETSPLITSCREEN
|
||||
if (splitscreen)
|
||||
{
|
||||
suggesty -= BASEVIDHEIGHT/2;
|
||||
if (splitscreen > 1)
|
||||
suggesty += 16;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
suggesty -= (cv_kartspeedometer.value ? 16 : 0);
|
||||
|
||||
for (i = 0; i < MAXEMOTESUGGESTIONS && emote_suggestions[i]; ++i)
|
||||
{
|
||||
V_DrawFillConsoleMap(chatx + boxw + 2, suggesty - (7*i), (longest_suggestion_length+2)*4 + EMOTEWIDTH, 6, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
V_DrawSmallString(chatx + boxw + 4 + EMOTEWIDTH, suggesty - (7*i), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va(":%s:", emote_suggestions[i]->name));
|
||||
HU_DrawEmote(chatx + boxw + 2, suggesty - i*7, emote_suggestions[i], V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
}
|
||||
}
|
||||
|
||||
// handle /pm list. It's messy, horrible and I don't care.
|
||||
if (strnicmp(w_chat_buf, "/pm", 3) == 0 && vid.width >= 400 && !teamtalk) // 320x200 unsupported kthxbai
|
||||
{
|
||||
|
||||
+4
-4
@@ -82,6 +82,8 @@ typedef struct
|
||||
char data[MAXPACKETLENGTH];
|
||||
} ATTRPACK doomcom_t;
|
||||
|
||||
#define DOOMCOM_DATA(d) (doomdata_t *)&(d)->data
|
||||
|
||||
#ifdef HOLEPUNCH
|
||||
typedef struct
|
||||
{
|
||||
@@ -89,6 +91,8 @@ typedef struct
|
||||
INT32 addr;
|
||||
INT16 port;
|
||||
} ATTRPACK holepunch_t;
|
||||
|
||||
#define HOLEPUNCH_DATA(d) (holepunch_t *)&(d)->data
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@@ -97,10 +101,6 @@ typedef struct
|
||||
|
||||
extern doomcom_t *doomcom;
|
||||
|
||||
#ifdef HOLEPUNCH
|
||||
extern holepunch_t *holepunchpacket;
|
||||
#endif
|
||||
|
||||
/** \brief return packet in doomcom struct
|
||||
*/
|
||||
extern boolean (*I_NetGet)(void);
|
||||
|
||||
+77
-21
@@ -371,8 +371,16 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
|
||||
|
||||
static const char *SOCK_AddrToStr(mysockaddr_t *sk)
|
||||
{
|
||||
static char s[64]; // 255.255.255.255:65535 or IPv6:65535
|
||||
static char s[64]; // 255.255.255.255:65535 or
|
||||
// [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
|
||||
|
||||
#ifdef HAVE_NTOP
|
||||
#ifdef HAVE_IPV6
|
||||
int v6 = (sk->any.sa_family == AF_INET6);
|
||||
#else
|
||||
int v6 = 0;
|
||||
#endif
|
||||
|
||||
void *addr;
|
||||
int e = 0; // save error code so it can't be modified later code and avoid calling WSAGetLastError() more then once
|
||||
|
||||
@@ -387,14 +395,20 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
|
||||
|
||||
if(addr == NULL)
|
||||
sprintf(s, "No address");
|
||||
else if(inet_ntop(sk->any.sa_family, addr, s, sizeof (s)) == NULL)
|
||||
else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
|
||||
{
|
||||
e = errno;
|
||||
sprintf(s, "Unknown family type, error #%u: %s", e, strerror(e));
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if(sk->any.sa_family == AF_INET6 && sk->ip6.sin6_port != 0)
|
||||
strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
|
||||
else if(sk->any.sa_family == AF_INET6)
|
||||
{
|
||||
s[0] = '[';
|
||||
strcat(s, "]");
|
||||
|
||||
if (sk->ip6.sin6_port != 0)
|
||||
strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
|
||||
}
|
||||
#endif
|
||||
else if(sk->any.sa_family == AF_INET && sk->ip4.sin_port != 0)
|
||||
strcat(s, va(":%d", ntohs(sk->ip4.sin_port)));
|
||||
@@ -486,10 +500,30 @@ static time_t SOCK_GetUnbanTime(size_t ban)
|
||||
}
|
||||
|
||||
#ifndef NONET
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
|
||||
{
|
||||
UINT8 bitmask;
|
||||
I_Assert(mask <= 128);
|
||||
if (mask == 0)
|
||||
mask = 128;
|
||||
if (memcmp(&a->ip6.sin6_addr.s6_addr, &b->ip6.sin6_addr.s6_addr, mask / 8) != 0)
|
||||
return false;
|
||||
if (mask % 8 == 0)
|
||||
return true;
|
||||
bitmask = 255 << (mask % 8);
|
||||
return (a->ip6.sin6_addr.s6_addr[mask / 8] & bitmask) == (b->ip6.sin6_addr.s6_addr[mask / 8] & bitmask);
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
|
||||
{
|
||||
UINT32 bitmask = INADDR_NONE;
|
||||
|
||||
if (a->any.sa_family != b->any.sa_family)
|
||||
return false;
|
||||
|
||||
if (mask && mask < 32)
|
||||
bitmask = htonl((UINT32)(-1) << (32 - mask));
|
||||
|
||||
@@ -498,13 +532,14 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
|
||||
&& (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port));
|
||||
#ifdef HAVE_IPV6
|
||||
else if (b->any.sa_family == AF_INET6)
|
||||
return memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
|
||||
return SOCK_cmpipv6(a, b, mask)
|
||||
&& (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port));
|
||||
#endif
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// This is a hack. For some reason, nodes aren't being freed properly.
|
||||
// This goes through and cleans up what nodes were supposed to be freed.
|
||||
/** \warning This function causes the file downloading to stop if someone joins.
|
||||
@@ -620,6 +655,7 @@ is_external_address (UINT32 p)
|
||||
|
||||
static boolean hole_punch(ssize_t c)
|
||||
{
|
||||
holepunch_t *holepunchpacket = HOLEPUNCH_DATA(doomcom);
|
||||
|
||||
/* See ../doc/Holepunch-Protocol.txt */
|
||||
if (cv_rendezvousserver.string[0] &&
|
||||
@@ -659,6 +695,7 @@ static boolean SOCK_Get(void)
|
||||
fromlen = (socklen_t)sizeof(fromaddress);
|
||||
c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0,
|
||||
(void *)&fromaddress, &fromlen);
|
||||
|
||||
if (c > 0)
|
||||
{
|
||||
#ifdef USE_STUN
|
||||
@@ -868,11 +905,11 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
unsigned long trueval = true;
|
||||
#endif
|
||||
mysockaddr_t straddr;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
socklen_t len = sizeof(straddr);
|
||||
|
||||
if (s == (SOCKET_TYPE)ERRSOCKET)
|
||||
return (SOCKET_TYPE)ERRSOCKET;
|
||||
|
||||
#ifdef USE_WINSOCK
|
||||
{ // Alam_GBC: disable the new UDP connection reset behavior for Win2k and up
|
||||
#ifdef USE_WINSOCK2
|
||||
@@ -892,14 +929,12 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
}
|
||||
#endif
|
||||
|
||||
straddr.any = *addr;
|
||||
memcpy(&straddr, addr, addrlen);
|
||||
I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr));
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
mysockaddr_t tmpaddr;
|
||||
tmpaddr.any = *addr ;
|
||||
if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
|
||||
if (straddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
|
||||
{
|
||||
opt = true;
|
||||
opts = (socklen_t)sizeof(opt);
|
||||
@@ -924,7 +959,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
#ifdef HAVE_IPV6
|
||||
else if (family == AF_INET6)
|
||||
{
|
||||
if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
|
||||
if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
|
||||
{
|
||||
opt = true;
|
||||
opts = (socklen_t)sizeof(opt);
|
||||
@@ -939,7 +974,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
// make it IPv6 ony
|
||||
opt = true;
|
||||
opts = (socklen_t)sizeof(opt);
|
||||
rc = setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts);
|
||||
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts);
|
||||
if (rc <= -1)
|
||||
{
|
||||
e = errno;
|
||||
@@ -969,7 +1004,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
|
||||
inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr);
|
||||
maddr.ipv6mr_interface = 0;
|
||||
rc = setsockopt(s, SOL_SOCKET, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr));
|
||||
rc = setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr));
|
||||
if (rc <= -1)
|
||||
{
|
||||
e = errno;
|
||||
@@ -1032,7 +1067,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
|
||||
}
|
||||
|
||||
rc = getsockname(s, (struct sockaddr *)&sin, &len);
|
||||
rc = getsockname(s, &straddr.any, &len);
|
||||
if (rc != 0)
|
||||
{
|
||||
e = errno;
|
||||
@@ -1040,7 +1075,14 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
|
||||
I_OutputMsg("getsockname failed: #%u, %s\n", e, strerror(e));
|
||||
}
|
||||
else
|
||||
current_port = (UINT16)ntohs(sin.sin_port);
|
||||
{
|
||||
if (family == AF_INET)
|
||||
current_port = (UINT16)ntohs(straddr.ip4.sin_port);
|
||||
#ifdef HAVE_IPV6
|
||||
else if (family == AF_INET6)
|
||||
current_port = (UINT16)ntohs(straddr.ip6.sin6_port);
|
||||
#endif
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1121,6 +1163,7 @@ static boolean UDP_Socket(void)
|
||||
I_freeaddrinfo(ai);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (b_ipv6)
|
||||
{
|
||||
@@ -1319,12 +1362,13 @@ static boolean SOCK_GetAddr(struct sockaddr_in *sin, const char *address, const
|
||||
{
|
||||
struct my_addrinfo *ai = NULL, *runp, hints;
|
||||
int gaie;
|
||||
size_t i;
|
||||
|
||||
if (!port || !port[0])
|
||||
port = DEFAULTPORT;
|
||||
|
||||
memset (&hints, 0x00, sizeof (hints));
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
@@ -1343,10 +1387,19 @@ static boolean SOCK_GetAddr(struct sockaddr_in *sin, const char *address, const
|
||||
{
|
||||
while (runp != NULL)
|
||||
{
|
||||
if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
|
||||
break;
|
||||
// test ip address of server
|
||||
for (i = 0; i < mysocketses; ++i)
|
||||
{
|
||||
if (runp->ai_addr->sa_family == myfamily[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
runp = runp->ai_next;
|
||||
if (i >= mysocketses)
|
||||
runp = runp->ai_next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,6 +1443,8 @@ static void rendezvous(int size)
|
||||
|
||||
tic_t tic = I_GetTime();
|
||||
|
||||
holepunch_t *holepunchpacket = HOLEPUNCH_DATA(doomcom);
|
||||
|
||||
if (tic != refreshtic)
|
||||
{
|
||||
if (SOCK_GetAddr(&rzv.ip4, host, (port ? port : "7777"), false))
|
||||
@@ -1415,6 +1470,7 @@ static void rendezvous(int size)
|
||||
static void SOCK_RequestHolePunch(INT32 node)
|
||||
{
|
||||
mysockaddr_t * addr = &clientaddress[node];
|
||||
holepunch_t *holepunchpacket = HOLEPUNCH_DATA(doomcom);
|
||||
|
||||
holepunchpacket->addr = addr->ip4.sin_addr.s_addr;
|
||||
holepunchpacket->port = addr->ip4.sin_port;
|
||||
@@ -1736,7 +1792,7 @@ boolean I_InitTcpNetwork(void)
|
||||
|
||||
// server address only in ip
|
||||
if (serverhostname[0])
|
||||
{
|
||||
{
|
||||
CONS_Printf("SERVERNAME: %s\n", serverhostname);
|
||||
COM_BufAddText("connect \"");
|
||||
COM_BufAddText(serverhostname);
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
#include "m_emotes.h"
|
||||
|
||||
extern "C" {
|
||||
#include "w_wad.h"
|
||||
#include "z_zone.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "v_video.h"
|
||||
}
|
||||
|
||||
consvar_t cv_emotes = {"emotes", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};;
|
||||
|
||||
static std::map<std::string, emote_t> emotes;
|
||||
|
||||
// TODO - Would be really nice to generalize soc-like file parsing and not reimplement it 100 times :)))))
|
||||
void M_LoadEmotes(UINT16 wadnum)
|
||||
{
|
||||
UINT16 lumpnum = W_CheckNumForNamePwad("EMOTES", wadnum, 0);
|
||||
|
||||
if (lumpnum == INT16_MAX)
|
||||
return;
|
||||
|
||||
char *lump = static_cast<char*>(W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE));
|
||||
size_t size = W_LumpLengthPwad(wadnum, lumpnum);
|
||||
|
||||
std::istringstream file(std::string(lump, size));
|
||||
std::string line;
|
||||
|
||||
emote_t *emote = nullptr;
|
||||
std::string emote_name;
|
||||
|
||||
// Just smol lambda to remove all the trailing chars we might not want
|
||||
auto trim = [](std::string &str) {
|
||||
str.erase(0, str.find_first_not_of(" \t\n\r\f\v"));
|
||||
str.erase(str.find_last_not_of(" \t\n\r\f\v")+1);
|
||||
};
|
||||
|
||||
int numemotes = 0;
|
||||
int linenum = 0;
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
++linenum;
|
||||
|
||||
trim(line);
|
||||
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
size_t split = line.find('=');
|
||||
|
||||
// Didn't find the =, means we're about to parse `Emote <name>`
|
||||
if (split == line.npos)
|
||||
{
|
||||
split = line.find(' ');
|
||||
|
||||
if (split == line.npos)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Unrecognized line. (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strnicmp(line.c_str(), "emote", 5))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: 'Emote' expected. (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum);
|
||||
continue;
|
||||
}
|
||||
|
||||
emote_name = line.substr(split+1);
|
||||
|
||||
if (emote_name.find_first_of(" \t\n\r\f\v:") != emote_name.npos)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Emote name cannot contain spaces or ':' symbols. (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (emote_name.size() > MAXEMOTENAME)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Emote name is too long, truncating. (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum);
|
||||
emote_name = emote_name.substr(0, MAXEMOTENAME);
|
||||
}
|
||||
|
||||
emote = &emotes[emote_name];
|
||||
strlcpy(emote->name, emote_name.c_str(), MAXEMOTENAME);
|
||||
|
||||
emote->timeperframe = 1;
|
||||
emote->numframes = 0;
|
||||
std::memset(emote->frames, 0, sizeof(emote->frames));
|
||||
|
||||
++numemotes;
|
||||
}
|
||||
else if (emote)
|
||||
{
|
||||
std::string field = line.substr(0, split);
|
||||
std::string value = line.substr(split+1);
|
||||
|
||||
trim(field);
|
||||
trim(value);
|
||||
|
||||
if (stricmp(field.c_str(), "frames") == 0)
|
||||
{
|
||||
UINT8 numframes = 0;
|
||||
|
||||
auto copy_frame = [&] {
|
||||
if (numframes == MAXEMOTEFRAMES)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Too many frames. (file %s, line %d)", wadfiles[wadnum]->filename, linenum);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t list_split = value.find(',');
|
||||
std::string framelumpname = value.substr(0, list_split);
|
||||
trim(framelumpname);
|
||||
|
||||
if (list_split == value.npos)
|
||||
value = "";
|
||||
else
|
||||
value = value.substr(list_split+1, value.npos);
|
||||
|
||||
// End of list of frames
|
||||
if (framelumpname.size() == 0)
|
||||
return false;
|
||||
|
||||
if (framelumpname.size() > 8)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Frame %d name is too long. (file %s, line %d)", numframes, wadfiles[wadnum]->filename, linenum);
|
||||
framelumpname = framelumpname.substr(0, 8);
|
||||
}
|
||||
|
||||
std::strncpy(emote->frames[numframes], framelumpname.c_str(), framelumpname.size()+1);
|
||||
|
||||
++numframes;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
while (copy_frame()) {}
|
||||
|
||||
if (numframes == 0)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Expected list of frames. (file %s, line %d)", wadfiles[wadnum]->filename, linenum);
|
||||
}
|
||||
|
||||
emote->numframes = numframes;
|
||||
}
|
||||
else if (stricmp(field.c_str(), "timeperframe") == 0)
|
||||
{
|
||||
emote->timeperframe = std::stoi(value);
|
||||
|
||||
if (emote->timeperframe <= 0)
|
||||
{
|
||||
emote->timeperframe = 1;
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Bad value for 'timeperframe'. (file %s, line %d)", wadfiles[wadnum]->filename, linenum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Unrecognized field '%s'. (file %s, line %d)", field.c_str(), wadfiles[wadnum]->filename, linenum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "EMOTES: Unrecognized line. (file %s, line %d)\n", wadfiles[wadnum]->filename, linenum);
|
||||
}
|
||||
}
|
||||
|
||||
CONS_Printf("Added %d emotes\n", numemotes);
|
||||
}
|
||||
|
||||
void M_InitEmotes(void)
|
||||
{
|
||||
UINT16 i;
|
||||
for (i = 0; i < numwadfiles; i++)
|
||||
M_LoadEmotes(i);
|
||||
}
|
||||
|
||||
emote_t *M_FindEmote(const char *name, int len, int skip)
|
||||
{
|
||||
if (!cv_emotes.value)
|
||||
return nullptr;
|
||||
|
||||
char query[MAXEMOTENAME+1] = {0};
|
||||
std::strncpy(query, name, std::min(len, MAXEMOTENAME));
|
||||
|
||||
for (auto &pair: emotes)
|
||||
{
|
||||
if (pair.first.rfind(query, 0, std::min(len, MAXEMOTENAME)) != 0)
|
||||
continue;
|
||||
|
||||
if (skip > 0)
|
||||
{
|
||||
--skip;
|
||||
continue;
|
||||
}
|
||||
|
||||
return &pair.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
emote_t *M_VerifyEmote(const char *name, int *emotelen)
|
||||
{
|
||||
if (*name != ':')
|
||||
return nullptr;
|
||||
|
||||
if (!cv_emotes.value)
|
||||
return nullptr;
|
||||
|
||||
const char *p = name+1; // skip ':'
|
||||
emote_t *match = nullptr;
|
||||
|
||||
while (*p && (p - name) < MAXEMOTENAME)
|
||||
{
|
||||
if (*p == ':')
|
||||
{
|
||||
std::string checkname = std::string(name+1, (p-name)-1);
|
||||
auto it = emotes.find(checkname);
|
||||
|
||||
if (it != emotes.end())
|
||||
match = &it->second;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
if (match && emotelen)
|
||||
*emotelen = p-name+1;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void M_DrawEmote(INT32 x, INT32 y, emote_t *emote, tic_t anim, INT32 flags)
|
||||
{
|
||||
if (emote->numframes == 0)
|
||||
return;
|
||||
|
||||
const char *lumpname = emote->frames[(anim/emote->timeperframe) % emote->numframes];
|
||||
patch_t *emotepatch = (patch_t*)W_CachePatchName(lumpname, PU_CACHE);
|
||||
|
||||
const int CHARHEIGHT = 6;
|
||||
|
||||
fixed_t scale = FRACUNIT;
|
||||
|
||||
x *= FRACUNIT;
|
||||
y *= FRACUNIT;
|
||||
|
||||
if (emotepatch->width > EMOTEWIDTH)
|
||||
scale = (FRACUNIT/emotepatch->width)*EMOTEWIDTH;
|
||||
else if (emotepatch->width < EMOTEWIDTH)
|
||||
x += (EMOTEWIDTH-emotepatch->width)*FRACUNIT/2;
|
||||
|
||||
y -= (scale*emotepatch->height-CHARHEIGHT*FRACUNIT)/2;
|
||||
|
||||
V_DrawFixedPatch(x, y, scale, flags, emotepatch, NULL);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef __M_EMOTES__
|
||||
#define __M_EMOTES__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "command.h"
|
||||
|
||||
extern consvar_t cv_emotes;
|
||||
|
||||
#define MAXEMOTENAME 32
|
||||
#define MAXEMOTEFRAMES 32
|
||||
#define EMOTEWIDTH 6
|
||||
|
||||
typedef struct emote_s {
|
||||
char name[MAXEMOTENAME+1];
|
||||
char frames[MAXEMOTEFRAMES][9];
|
||||
UINT8 numframes;
|
||||
tic_t timeperframe;
|
||||
} emote_t;
|
||||
|
||||
// Reads and adds emotes from EMOTES lump, with syntax similar to soc
|
||||
void M_LoadEmotes(UINT16 wadnum);
|
||||
|
||||
// Load emotes from all base wads
|
||||
void M_InitEmotes(void);
|
||||
|
||||
// Finds first matching emote, ignoring first few matches
|
||||
emote_t *M_FindEmote(const char *name, int len, int skips);
|
||||
|
||||
// If first character is not ':', instantly returns null
|
||||
// Starts from ':' character, goes until it finds ':' or end of string
|
||||
// If end of string is found, or closing ':' is found but resulting emote name doesn't exist,
|
||||
// it returns null, otherwise it returns emote and stores into *emotelen how much characters
|
||||
// needs to be skipped to get past emote name
|
||||
emote_t *M_VerifyEmote(const char *name, int *emotelen);
|
||||
|
||||
// Draw the emote, anim should be some kind of timer ticking every game tic
|
||||
// for animated emotes
|
||||
void M_DrawEmote(INT32 x, INT32 y, emote_t *emote, tic_t anim, INT32 flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __M_EMOTES__
|
||||
+285
-3591
File diff suppressed because it is too large.
Load diff
+2883
File diff suppressed because it is too large.
Load diff
+169
-5
@@ -130,6 +130,82 @@ static void M_TextInputPaste(textinput_t *input)
|
||||
M_TextInputAddString(input, paste);
|
||||
}
|
||||
|
||||
static void M_TextInputLeft(textinput_t *input, boolean emotes)
|
||||
{
|
||||
// Just do it simple way if possible
|
||||
if (!emotes || input->cursor < 2 || input->buffer[input->cursor-1] != ':')
|
||||
{
|
||||
if (input->cursor != 0)
|
||||
--input->cursor;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we need to check if we have to skip emote
|
||||
size_t start = input->cursor-2;
|
||||
|
||||
while (input->buffer[start] != ':')
|
||||
{
|
||||
// We reached start of string and didn't find ':' symbol, thats definitely not an emote
|
||||
// so just do it simple way
|
||||
if (start == 0)
|
||||
{
|
||||
M_TextInputLeft(input, false);
|
||||
return;
|
||||
}
|
||||
|
||||
--start;
|
||||
}
|
||||
|
||||
int emotelen = 0;
|
||||
if (M_VerifyEmote(input->buffer+start, &emotelen))
|
||||
input->cursor -= emotelen; // also skip the :
|
||||
else
|
||||
M_TextInputLeft(input, false); // Not a valid emote, do the simple thing
|
||||
}
|
||||
|
||||
static void M_TextInputRight(textinput_t *input, boolean emotes)
|
||||
{
|
||||
// Just do it simple way if possible
|
||||
if (!emotes || input->cursor == input->length || input->buffer[input->cursor] != ':')
|
||||
{
|
||||
if (input->cursor < input->length)
|
||||
++input->cursor;
|
||||
return;
|
||||
}
|
||||
|
||||
int emotelen = 0;
|
||||
if (M_VerifyEmote(input->buffer+input->cursor, &emotelen))
|
||||
input->cursor += emotelen; // Also skip the :
|
||||
else
|
||||
M_TextInputRight(input, false); // Not a valid emote, do the simple thing
|
||||
}
|
||||
|
||||
// Check if we're inside a valid emote
|
||||
static boolean M_TextInputCheckEmote(textinput_t *input)
|
||||
{
|
||||
int start = input->cursor;
|
||||
|
||||
// Definitely not an emote
|
||||
if (start == 0)
|
||||
return false;
|
||||
|
||||
// Might be closing :, need to skip it just in case
|
||||
if (input->buffer[start] == ':')
|
||||
--start;
|
||||
|
||||
while (input->buffer[start] != ':')
|
||||
{
|
||||
// No ':' found, definitely not inside emote
|
||||
if (start == 0)
|
||||
return false;
|
||||
|
||||
--start;
|
||||
}
|
||||
|
||||
// Found what may be emote start, now we can check
|
||||
return M_VerifyEmote(input->buffer+start, NULL) != NULL;
|
||||
}
|
||||
|
||||
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size)
|
||||
{
|
||||
input->buffer = buffer;
|
||||
@@ -156,7 +232,7 @@ void M_TextInputSetString(textinput_t *input, const char *c)
|
||||
input->cursor = input->select = input->length = strlen(c);
|
||||
}
|
||||
|
||||
boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
||||
static boolean M_TextInputHandleBase(textinput_t *input, INT32 key, boolean emotes)
|
||||
{
|
||||
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|
||||
|| key == KEY_LCTRL || key == KEY_RCTRL
|
||||
@@ -241,16 +317,14 @@ boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
||||
|
||||
if (key == KEY_LEFTARROW)
|
||||
{
|
||||
if (input->cursor != 0)
|
||||
--input->cursor;
|
||||
M_TextInputLeft(input, emotes);
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_RIGHTARROW)
|
||||
{
|
||||
if (input->cursor < input->length)
|
||||
++input->cursor;
|
||||
M_TextInputRight(input, emotes);
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
@@ -320,6 +394,96 @@ boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Just an alias, more or less
|
||||
boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
||||
{
|
||||
return M_TextInputHandleBase(input, key, false);
|
||||
}
|
||||
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_t *suggestions[], int maxsuggestions)
|
||||
{
|
||||
boolean ret = M_TextInputHandleBase(input, key, true);
|
||||
|
||||
// After this handled key we ended up inside an emote, lets fix that
|
||||
if (M_TextInputCheckEmote(input))
|
||||
M_TextInputToWordEnd(input, !shiftdown);
|
||||
|
||||
// Always clear the first entry
|
||||
suggestions[0] = NULL;
|
||||
|
||||
// For autocomplete
|
||||
int emotestart = 0;
|
||||
|
||||
// Try find suggestions for emote names
|
||||
for (int i = input->cursor-1; i >= 0 && input->cursor-i <= MAXEMOTENAME; --i)
|
||||
{
|
||||
// Space can't be part of emote name
|
||||
if (isspace(input->buffer[i]))
|
||||
break;
|
||||
|
||||
// Found a :, try suggest emotes
|
||||
if (input->buffer[i] == ':')
|
||||
{
|
||||
emotestart = i+1;
|
||||
// ...But only if we typed at least something
|
||||
if ((int)input->cursor-(i-1) < 3)
|
||||
break;
|
||||
|
||||
for (int skip = 0; skip < maxsuggestions; ++skip)
|
||||
{
|
||||
suggestions[skip] = M_FindEmote(input->buffer+i+1, input->cursor-i-1, skip);
|
||||
|
||||
// No more suggestions
|
||||
if (!suggestions[skip])
|
||||
break;
|
||||
}
|
||||
|
||||
// In any case, we found what we wanted, can exit the loop now
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we suggest emotes, try autocomplete
|
||||
if (key == '\t' && suggestions[0])
|
||||
{
|
||||
int pos = (input->cursor-emotestart);
|
||||
boolean autocomplete = true;
|
||||
|
||||
while (autocomplete)
|
||||
{
|
||||
// Check if current character matches for all suggestions
|
||||
char c = suggestions[0]->name[pos];
|
||||
|
||||
// End of string reached
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
for (int i = 1; i < maxsuggestions && suggestions[i]; ++i)
|
||||
{
|
||||
if (suggestions[i]->name[pos] != c)
|
||||
{
|
||||
autocomplete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++pos;
|
||||
|
||||
if (autocomplete)
|
||||
M_TextInputAddChar(input, c);
|
||||
}
|
||||
|
||||
// This was the only suggestion, finish autocomplete with a ':' and clear suggestions
|
||||
if (maxsuggestions == 1 || !suggestions[1])
|
||||
{
|
||||
M_TextInputAddChar(input, ':');
|
||||
suggestions[0] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void M_DrawTextInputScroll(INT32 x, INT32 y, textinput_t *input, INT32 flags, INT32 MAXINPUTWIDTH)
|
||||
{
|
||||
if (renderisnewtic)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __M_TEXTINPUT__
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "m_emotes.h" // M_VerifyEmote, M_FindEmote
|
||||
|
||||
typedef struct textinput_s {
|
||||
size_t cursor;
|
||||
@@ -22,6 +23,10 @@ void M_TextInputSetString(textinput_t *input, const char *c);
|
||||
|
||||
boolean M_TextInputHandle(textinput_t *input, INT32 key);
|
||||
|
||||
// Does everything same as M_TextInputHandle, but also handles emotes in input string
|
||||
// If currently typing something that looks like an existing emote, return suggestions for it
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_t *suggestions[], int maxsuggestions);
|
||||
|
||||
#define M_DrawTextInput(x, y, input, flags) M_DrawTextInputScroll((x), (y), (input), (flags), ((input)->buffer_size-1)*8)
|
||||
void M_DrawTextInputScroll(INT32 x, INT32 y, textinput_t *input, INT32 flags, INT32 MAXINPUTWIDTH);
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ typedef union
|
||||
typedef struct
|
||||
{
|
||||
msg_header_t header;
|
||||
#ifndef HAVE_IPV6
|
||||
char ip[16];
|
||||
#else
|
||||
char ip[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
|
||||
#endif
|
||||
char port[8];
|
||||
char contact[32];
|
||||
char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31)
|
||||
|
||||
+7
-1
@@ -34,6 +34,7 @@
|
||||
#include "r_fps.h" // R_ResetViewInterpolation in level load
|
||||
|
||||
#include "s_sound.h"
|
||||
#include "m_emotes.h"
|
||||
#include "st_stuff.h"
|
||||
#include "w_wad.h"
|
||||
#include "z_zone.h"
|
||||
@@ -3071,7 +3072,7 @@ boolean P_SetupLevel(boolean fromnetsave, boolean reloadinggamestate)
|
||||
|
||||
if (!fromnetsave) // uglier hack
|
||||
{ // to make a newly loaded level start on the second frame.
|
||||
INT32 buf = gametic % TICQUEUE;
|
||||
INT32 buf = gametic % BACKUPTICS;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
@@ -3337,6 +3338,11 @@ UINT16 P_PartialAddWadFile(const char *wadfilename, boolean local)
|
||||
//
|
||||
S_LoadMusicDefs(wadnum);
|
||||
|
||||
//
|
||||
// look for emotes
|
||||
//
|
||||
M_LoadEmotes(wadnum);
|
||||
|
||||
// TODO: Experimental SPRTINFO support, test first
|
||||
R_LoadSpriteInfoLumps(wadnum, wadfiles[wadnum]->numlumps);
|
||||
|
||||
|
||||
Reference in new issue
Block a user