Better compat with QE. EX_PROMPT now supported serverside (emulated for non-qe clients). Per-client localisation now works. Scoreboards are now a little nicer when running mods with well-defined teams (eg NQ ssqc).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6309 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2023-01-09 05:11:34 +00:00
parent 3a6f22d05c
commit df6b651eeb
15 changed files with 714 additions and 155 deletions

View file

@ -4166,12 +4166,14 @@ void CL_LinkPacketEntities (void)
if (state->effects & EF_BRIGHTLIGHT)
{
radius = max(radius,r_brightlight_colour.vec4[3]);
VectorAdd(colour, r_brightlight_colour.vec4, colour);
if (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))
VectorAdd(colour, r_brightlight_colour.vec4, colour);
}
if (state->effects & EF_DIMLIGHT)
{
radius = max(radius,r_dimlight_colour.vec4[3]);
VectorAdd(colour, r_dimlight_colour.vec4, colour);
if (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))
VectorAdd(colour, r_dimlight_colour.vec4, colour);
}
if (state->effects & EF_BLUE)
{
@ -5398,12 +5400,14 @@ void CL_LinkPlayers (void)
if (state->effects & EF_BRIGHTLIGHT)
{
radius = max(radius,r_brightlight_colour.vec4[3]);
VectorAdd(colour, r_brightlight_colour.vec4, colour);
if (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))
VectorAdd(colour, r_brightlight_colour.vec4, colour);
}
if (state->effects & EF_DIMLIGHT)
{
radius = max(radius,r_dimlight_colour.vec4[3]);
VectorAdd(colour, r_dimlight_colour.vec4, colour);
if (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))
VectorAdd(colour, r_dimlight_colour.vec4, colour);
}
if (state->effects & EF_BLUE)
{

View file

@ -1837,7 +1837,7 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports!
if (cls.qex)
MSG_WriteByte(buf, 4);
MSG_WriteByte(buf, 1);
for (i=0 ; i<3 ; i++)
{

View file

@ -4048,8 +4048,8 @@ void CLNQ_ConnectionlessPacket(void)
return;
}
if (length == 5 && net_from.prot == NP_DTLS)
{
if (length == 5)
{ //QE strips the port entirely.
cls.proquake_angles_hack = false;
cls.protocol_nq = CPNQ_ID;
Con_DPrintf("QuakeEx server...\n");

View file

@ -4136,11 +4136,11 @@ static void CLQEX_ParsePrompt(void)
SCR_CenterPrint(0, NULL, true);
return;
}
*message = 0;
s = MSG_ReadString();
Q_strncatz(message+ofs, "/S/C/.", sizeof(message)-ofs);
ofs += strlen(message+ofs);
TL_Reformat(message+ofs, sizeof(message)-ofs, 1, &s);
TL_Reformat(com_language, message+ofs, sizeof(message)-ofs, 1, &s);
ofs += strlen(message+ofs);
Q_strncatz(message+ofs, "\n", sizeof(message)-ofs);
@ -4151,7 +4151,7 @@ static void CLQEX_ParsePrompt(void)
imp = MSG_ReadByte();
Q_strncatz(message+ofs, "^[[", sizeof(message)-ofs);
ofs += strlen(message+ofs);
TL_Reformat(message+ofs, sizeof(message)-ofs, 1, &s);
TL_Reformat(com_language, message+ofs, sizeof(message)-ofs, 1, &s);
ofs += strlen(message+ofs);
Q_strncatz(message+ofs, va("]\\impulse\\%i^]\n", imp), sizeof(message)-ofs);
ofs += strlen(message+ofs);
@ -4175,7 +4175,7 @@ static char *CLQEX_ReadStrings(void)
for (; a < count; a++)
MSG_ReadString(); //don't lose space, though we can't buffer it.
TL_Reformat(formatted, sizeof(formatted), a, arg);
TL_Reformat(com_language, formatted, sizeof(formatted), a, arg);
return formatted;
}
@ -7606,7 +7606,7 @@ void CLQW_ParseServerMessage (void)
cl.completed_time = cl.gametime;
}
cl.intermissionmode = IM_NQFINALE;
SCR_CenterPrint (destsplit, TL_Translate(MSG_ReadString ()), false);
SCR_CenterPrint (destsplit, TL_Translate(com_language, MSG_ReadString ()), false);
break;
case svc_sellscreen:
@ -8784,8 +8784,8 @@ void CLNQ_ParseServerMessage (void)
//FIXME: figure out the player index so we can kickban/mute/etc them.
qbyte plcolour = MSG_ReadByte();
qbyte chatcolour = MSG_ReadByte();
char *pcols[] = {S_COLOR_WHITE,S_COLOR_RED,S_COLOR_YELLOW, S_COLOR_CYAN};
char *ccols[] = {S_COLOR_WHITE,S_COLOR_CYAN};
char *pcols[] = {S_COLOR_WHITE,S_COLOR_GREEN,S_COLOR_CYAN, S_COLOR_YELLOW};
char *ccols[] = {S_COLOR_WHITE,S_COLOR_CYAN}; //say, say_team
Con_Printf("^[%s%s"/*"\\player\\%i"*/"^]: ", pcols[plcolour%countof(pcols)], MSG_ReadString());
Con_Printf("%s%s\n", ccols[chatcolour%countof(ccols)], MSG_ReadString());
break;

View file

@ -327,6 +327,8 @@ typedef struct {
float time_off;
int erase_lines;
int erase_center;
int oldmousex, oldmousey; //so the cursorchar can be changed by keyboard without constantly getting stomped on.
} cprint_t;
cprint_t scr_centerprint[MAX_SPLITS];
@ -682,7 +684,7 @@ int SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)
int remaining;
shader_t *pic;
int ch;
int mousex,mousey;
int mousex,mousey, mousemoved;
conchar_t *line_start[MAX_CPRINT_LINES];
conchar_t *line_end[MAX_CPRINT_LINES];
@ -741,6 +743,10 @@ int SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)
Font_BeginString(font, rect.x+rect.width, rect.y+rect.height, &right, &bottom);
linecount = Font_LineBreaks(p->string, p->string + p->charcount, (p->flags & CPRINT_NOWRAP)?0x7fffffff:(right - left), MAX_CPRINT_LINES, line_start, line_end);
mousemoved = mousex != p->oldmousex || mousey != p->oldmousey;
p->oldmousex = mousex;
p->oldmousey = mousey;
ch = Font_CharHeight();
if (p->flags & CPRINT_TALIGN)
@ -791,9 +797,27 @@ int SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)
else
x = left + (right - left - Font_LineWidth(line_start[l], line_end[l]))/2;
if (mousey >= y && mousey < y+ch)
if (mousemoved && mousey >= y && mousey < y+ch)
{
p->cursorchar = Font_CharAt(mousex - x, line_start[l], line_end[l]);
conchar_t *linkstart;
linkstart = Font_CharAt(mousex - x, line_start[l], line_end[l]);
//scan backwards to find any link enclosure
if (linkstart)
for(linkstart = linkstart-1; linkstart >= line_start[l]; linkstart--)
{
if (*linkstart == CON_LINKSTART)
{
//found one
p->cursorchar = linkstart;
break;
}
if (*linkstart == CON_LINKEND)
{
//some other link ended here. don't use its start.
break;
}
}
}
remaining -= line_end[l]-line_start[l];
@ -803,6 +827,25 @@ int SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)
if (line_end[l] <= line_start[l])
break;
}
if (p->cursorchar && p->cursorchar >= line_start[l] && p->cursorchar < line_end[l] && *p->cursorchar == CON_LINKSTART)
{
conchar_t *linkend;
int s, e;
for (linkend = p->cursorchar; linkend < line_end[l]; linkend++)
if (*linkend == CON_LINKEND)
break;
s = x+Font_LineWidth(line_start[l], p->cursorchar);
e = x+Font_LineWidth(line_start[l], linkend);
//draw a 2-pixel underscore (behind the text).
R2D_ImageColours(SRGBA(0.3,0.3,0.3, 1)); //mouseover.
R2D_FillBlock((s*vid.width)/(float)vid.rotpixelwidth, ((y+Font_CharHeight()-2)*vid.height)/(float)vid.rotpixelheight, ((e - s)*vid.width)/(float)vid.rotpixelwidth, (2*vid.height)/(float)vid.rotpixelheight);
R2D_Flush();
R2D_ImageColours(SRGBA(1, 1, 1, 1));
}
Font_LineDraw(x, y, line_start[l], line_end[l]);
}
@ -811,11 +854,59 @@ int SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)
return linecount;
}
static void Key_CenterPrintActivate(int pnum)
{
char *link;
cprint_t *p = &scr_centerprint[pnum];
link = SCR_CopyCenterPrint(p);
if (link)
{
if (link[0] == '^' && link[1] == '[')
{
//looks like it might be a link!
char *end = NULL;
char *info;
for (info = link + 2; *info; )
{
if (info[0] == '^' && info[1] == ']')
break; //end of tag, with no actual info, apparently
if (*info == '\\')
break;
else if (info[0] == '^' && info[1] == '^')
info+=2;
else
info++;
}
for(end = info; *end; )
{
if (end[0] == '^' && end[1] == ']')
{
//okay, its a valid link that they clicked
*end = 0;
#ifdef PLUGINS
if (!Plug_ConsoleLink(link+2, info, ""))
#endif
#ifdef CSQC_DAT
if (!CSQC_ConsoleLink(link+2, info))
#endif
Key_DefaultLinkClicked(NULL, link+2, info);
break;
}
if (end[0] == '^' && end[1] == '^')
end+=2;
else
end++;
}
}
}
}
qboolean Key_Centerprint(int key, int unicode, unsigned int devid)
{
int pnum;
cprint_t *p;
char *link;
if (key == K_MOUSE1)
{
@ -824,52 +915,7 @@ qboolean Key_Centerprint(int key, int unicode, unsigned int devid)
{
p = &scr_centerprint[pnum];
if (cl.playerview[pnum].gamerectknown == cls.framecount)
{
link = SCR_CopyCenterPrint(p);
if (link)
{
if (link[0] == '^' && link[1] == '[')
{
//looks like it might be a link!
char *end = NULL;
char *info;
for (info = link + 2; *info; )
{
if (info[0] == '^' && info[1] == ']')
break; //end of tag, with no actual info, apparently
if (*info == '\\')
break;
else if (info[0] == '^' && info[1] == '^')
info+=2;
else
info++;
}
for(end = info; *end; )
{
if (end[0] == '^' && end[1] == ']')
{
//okay, its a valid link that they clicked
*end = 0;
#ifdef PLUGINS
if (!Plug_ConsoleLink(link+2, info, ""))
#endif
#ifdef CSQC_DAT
if (!CSQC_ConsoleLink(link+2, info))
#endif
Key_DefaultLinkClicked(NULL, link+2, info);
break;
}
if (end[0] == '^' && end[1] == '^')
end+=2;
else
end++;
}
}
}
}
Key_CenterPrintActivate(pnum);
}
return true; //handled
}
@ -882,6 +928,52 @@ qboolean Key_Centerprint(int key, int unicode, unsigned int devid)
}
return true;
}
else if ((key == K_ENTER ||
key == K_KP_ENTER ||
key == K_GP_DIAMOND_RIGHT) && devid < countof(scr_centerprint))
{
p = &scr_centerprint[devid];
if (p->cursorchar)
Key_CenterPrintActivate(devid);
return true;
}
else if ((key == K_UPARROW ||
key == K_LEFTARROW ||
key == K_KP_UPARROW ||
key == K_KP_LEFTARROW ||
key == K_GP_DPAD_UP ||
key == K_GP_DPAD_LEFT) && devid < countof(scr_centerprint))
{
p = &scr_centerprint[devid];
if (!p->cursorchar)
p->cursorchar = p->string + p->charcount;
while (--p->cursorchar >= p->string)
{
if (*p->cursorchar == CON_LINKSTART)
return true; //found one
}
p->cursorchar = NULL;
return true;
}
else if ((key == K_DOWNARROW ||
key == K_RIGHTARROW ||
key == K_KP_DOWNARROW ||
key == K_KP_RIGHTARROW ||
key == K_GP_DPAD_DOWN ||
key == K_GP_DPAD_RIGHT) && devid < countof(scr_centerprint))
{
p = &scr_centerprint[devid];
if (!p->cursorchar)
p->cursorchar = p->string-1;
while (++p->cursorchar < p->string + p->charcount)
{
if (*p->cursorchar == CON_LINKSTART)
return true; //found one
}
p->cursorchar = NULL; //hit the end
return true;
}
return false;
}
@ -895,7 +987,6 @@ void SCR_CheckDrawCenterString (void)
for (pnum = 0; pnum < cl.splitclients; pnum++)
{
p = &scr_centerprint[pnum];
p->cursorchar = NULL;
#ifdef QUAKESTATS
if (IN_DrawWeaponWheel(pnum))

View file

@ -126,6 +126,9 @@ static qboolean sbarfailed;
#ifdef HEXEN2
static qboolean sbar_hexen2;
#endif
#ifdef NQPROT
static void Sbar_CTFScores_f(void);
#endif
vrect_t sbar_rect; //screen area that the sbar must fit.
float sbar_rect_left;
@ -803,15 +806,6 @@ void Sbar_ShowScores (void)
sb_updates = 0;
}
#ifdef NQPROT
static void Sbar_CTFScores_f(void)
{ //issued via stuffcmds.
// int red = atoi(Cmd_Argv(1));
// int blue = atoi(Cmd_Argv(2));
// int flags = atoi(Cmd_Argv(3)); //base|carried|dropped | base|carried|dropped
}
#endif
#ifdef HEXEN2
static void Sbar_Hexen2InvLeft_f(void)
{
@ -1455,9 +1449,69 @@ static void Sbar_Hexen2DrawNum (float x, float y, int num, int digits)
}
#endif
#ifdef NQPROT
//this stuff was added to the rerelease's ctf mod.
int cl_ctfredscore;
int cl_ctfbluescore;
int cl_ctfflags;
static void Sbar_CTFScores_f(void)
{ //issued via stuffcmds.
cl_ctfredscore = atoi(Cmd_Argv(1));
cl_ctfbluescore = atoi(Cmd_Argv(2));
cl_ctfflags = atoi(Cmd_Argv(3)) | 0x100; //base|carried|dropped | base|carried|dropped
}
static void Sbar_DrawCTFScores(playerview_t *pv)
{
if (cl_ctfflags)
{
int i, x, y, sy;
static struct
{
int colour;
int *score;
const char *base, *held, *dropped; //at base, held, dropped.
int shift;
} team[] =
{
{4, &cl_ctfredscore, "gfx/redf1.lmp", "gfx/redf2.lmp", "gfx/redf3.lmp", 0},
{13, &cl_ctfbluescore, "gfx/bluef1.lmp", "gfx/bluef2.lmp", "gfx/bluef3.lmp", 3},
};
x = sbar_rect.x + sbar_rect.width - (48+2);
sy = sbar_rect.y + sbar_rect.height-sb_lines;
if (!cl_sbar.value && sb_lines > 24 && scr_viewsize.value>=100 && !cl_hudswap.value)
x -= 42; //QW hud nudges it in by 24 to not cover ammo.
for (i = 0, y=sy-17; i < countof(team); i++, y -= 18)
{
if (cl.players[pv->playernum].rbottomcolor == team[i].colour)
Sbar_FillPC (x-1, y-1, 48+2, 16+2, 12);
Sbar_FillPC (x+16, y, 32, 16, team[i].colour);
}
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
for (i = 0, y=sy-17; i < countof(team); i++, y -= 18)
{
int fl = (cl_ctfflags>>team[i].shift)&7;
mpic_t *pic = NULL;
if (fl == 1)
pic = R2D_SafeCachePic(team[i].base);
else if (fl == 2)
pic = R2D_SafeCachePic(team[i].held);
else if (fl == 4)
pic = R2D_SafeCachePic(team[i].dropped);
if (pic)
R2D_ScalePic(x, y, 16, 16, pic);
}
for (i = 0, y=sy-17; i < countof(team); i++, y -= 18)
Draw_FunStringWidth (x+16, y+((16-8)/2), va("%i", *team[i].score), 32, 2, false);
}
}
#endif
//=============================================================================
int playerteam[MAX_CLIENTS];
typedef struct {
char team[16+1];
int frags;
@ -1466,11 +1520,13 @@ typedef struct {
int topcolour, bottomcolour;
qboolean ownteam;
} team_t;
team_t teams[MAX_CLIENTS];
int teamsort[MAX_CLIENTS];
int scoreboardteams;
static int playerteam[MAX_CLIENTS];
static team_t teams[MAX_CLIENTS];
static int teamsort[MAX_CLIENTS];
static int scoreboardteams;
static qboolean consistentteams; //can hide the 'team' displays when colours are enough.
struct
static struct
{
unsigned char upper;
short frags;
@ -1567,9 +1623,12 @@ void Sbar_SortTeams (playerview_t *pv)
player_info_t *s;
char t[16+1];
int ownnum;
unsigned int seen;
char *end;
// request new ping times every two second
scoreboardteams = 0;
consistentteams = false;
if (!cl.teamplay)
return;
@ -1652,6 +1711,40 @@ void Sbar_SortTeams (playerview_t *pv)
teamsort[i] = teamsort[j];
teamsort[j] = k;
}
seen = 0;
for (i = 0; i < scoreboardteams; i++)
{
//make sure the colour is one of quake's valid non-fullbright colour ranges.
if (teams[i].bottomcolour < 0 || teams[i].bottomcolour > 13)
return;
//don't allow multiple teams with the same colour but different names.
if (seen & (1<<teams[i].bottomcolour))
return;
seen |= (1<<teams[i].bottomcolour);
if (*teams[i].team == 't')
{ //fte servers use t%i for nq team names
if (teams[i].bottomcolour != strtoul(teams[i].team+1, &end, 10) || *end)
return;
}
else
{
if (teams[i].bottomcolour != strtoul(teams[i].team, &end, 10) || *end)
{
if (teams[i].bottomcolour == 4 && !strcasecmp(teams[i].team, "red"))
;
else if (teams[i].bottomcolour == 13 && !strcasecmp(teams[i].team, "blue"))
;
else
return;
}
}
}
//if we got this far, we had no dupe colours and every name checked out.
consistentteams = true;
}
/*
@ -2604,6 +2697,10 @@ static void Sbar_DrawTeamStatus(playerview_t *pv)
int y;
int track;
#ifdef NQPROT
Sbar_DrawCTFScores(pv);
#endif
if (!sbar_teamstatus.ival)
return;
y = -32;
@ -3045,14 +3142,14 @@ void Sbar_IntermissionNumber (float x, float y, int num, int digits, int color,
}
#define COL_TEAM_LOWAVGHIGH COLUMN("low/avg/high", 12*8, {sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); Draw_FunString ( x, y, num); })
#define COL_TEAM_TEAM COLUMN("team", 4*8, {Draw_FunStringWidth ( x, y, tm->team, 4*8, false, false); \
#define COL_TEAM_TEAM if (!consistentteams){COLUMN("team", 4*8, {Draw_FunStringWidth ( x, y, tm->team, 4*8, false, false); })}
#define COL_TEAM_TOTAL COLUMN("total", 5*8, {Draw_FunString ( x, y, va("%5i", tm->frags)); \
if (ourteam)\
{\
Draw_FunString ( x - 1*8, y, "^Ue010");\
Draw_FunString ( x + 4*8, y, "^Ue011");\
Draw_FunString ( x + 5*8, y, "^Ue011");\
}\
})
#define COL_TEAM_TOTAL COLUMN("total", 5*8, {Draw_FunString ( x, y, va("%5i", tm->frags)); })
#define COL_TEAM_PLAYERS COLUMN("players", 7*8, {Draw_FunString ( x, y, va("%5i", tm->players)); })
#define ALL_TEAM_COLUMNS COL_TEAM_LOWAVGHIGH COL_TEAM_TEAM COL_TEAM_TOTAL COL_TEAM_PLAYERS
@ -3092,6 +3189,9 @@ void Sbar_TeamOverlay (playerview_t *pv)
return;
}
// sort the teams
Sbar_SortTeams(pv);
y = gr.y;
if (scr_scoreboard_drawtitle.ival)
@ -3157,9 +3257,6 @@ void Sbar_TeamOverlay (playerview_t *pv)
#undef COLUMN
}
// sort the teams
Sbar_SortTeams(pv);
if (pv->spectator)
trackplayer = Cam_TrackNum(pv);
else
@ -3326,7 +3423,7 @@ ping time frags name
Sbar_FillPC(x, y+4, 40, 4, bottom); \
} \
})
#define COLUMN_TEAMNAME COLUMN(team, 4*8, \
#define COLUMN_TEAMNAME COLUMN(team, (consistentteams?0:(4*8)), \
{ \
if (!s->spectator) \
{ \
@ -3454,7 +3551,7 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
rank_width = 0;
#define COLUMN(title, cwidth, code, fill) if (rank_width+(cwidth)+8 <= gr.width) {showcolumns |= (1<<COLUMN##title); rank_width += cwidth+8;}
#define COLUMN(title, cwidth, code, fill) if (rank_width+(cwidth)+8 <= gr.width && cwidth) {showcolumns |= (1<<COLUMN##title); rank_width += cwidth+8;}
//columns are listed here in priority order (if the screen is too narrow, later ones will be hidden)
COLUMN_NAME
COLUMN_PING
@ -3529,7 +3626,7 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
}
x = startx;
#define COLUMN(title, width, code, fill) if (width && (showcolumns & (1<<COLUMN##title))) {Draw_FunString(x, y, #title); x += width+8;}
#define COLUMN(title, width, code, fill) if (showcolumns & (1<<COLUMN##title)) {Draw_FunString(x, y, #title); x += width+8;}
ALLCOLUMNS
#undef COLUMN
@ -3696,8 +3793,8 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv)
// scores
Sbar_SortFrags (false, false);
if (sbar_rect.width >= 640)
Sbar_SortTeams(pv);
// if (sbar_rect.width >= 640)
Sbar_SortTeams(pv);
if (!scoreboardlines)
return; // no one there?
@ -3769,7 +3866,7 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv)
Q_strncpyz(name, s->name, sizeof(name));
// team and name
if (cl.teamplay)
if (cl.teamplay && !consistentteams)
{
Draw_FunStringWidth (x+48, y, s->team, 32, false, false);
Draw_FunStringWidth (x+48+40, y, name, MAX_DISPLAYEDNAME*8, false, false);
@ -3778,38 +3875,67 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv)
Draw_FunStringWidth (x+48, y, name, MAX_DISPLAYEDNAME*8, false, false);
y += 8;
}
x += 48;
if (cl.teamplay && !consistentteams)
x += 40;
x += MAX_DISPLAYEDNAME*8;
x += 8;
// draw teams if room
if (sbar_rect.width < 640 || !cl.teamplay)
if (x + (consistentteams?40:80) > sbar_rect.x+sbar_rect.width || !cl.teamplay)
return;
y = sbar_rect.y + sbar_rect.height - sb_lines;
// draw seperator
x += 208;
R2D_ImageColours(0.3, 0.3, 0.3, 1);
R2D_FillBlock(x, y, 2, sb_lines);
R2D_ImageColours(1,1,1,1);
x += 2;
// for (y = sbar_rect.height - sb_lines; y < sbar_rect.height - 6; y += 2)
// Draw_ColouredCharacter(x, y, CON_WHITEMASK|14);
x += 16;
x += 8;
y = sbar_rect.height - sb_lines;
for (i=0 ; i < scoreboardteams && y <= sbar_rect.height; i++)
for (i=0 ; i < scoreboardteams && y <= sbar_rect.y+sbar_rect.height; i++)
{
k = teamsort[i];
tm = teams + k;
// draw pings
Draw_FunStringWidth (x, y, tm->team, 32, false, false);
// draw total
sprintf (num, "%5i", tm->frags);
Draw_FunString(x + 40, y, num);
if (!strncmp(cl.players[pv->playernum].team, tm->team, 16))
if (consistentteams)
{
Font_BeginString(font_default, x-8, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 16|0xe000);
Font_BeginString(font_default, x+32, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 17|0xe000);
Font_EndString(font_default);
// draw total
Sbar_FillPC ( x, y+1, 48, 3, tm->topcolour);
Sbar_FillPC ( x, y+4, 48, 4, tm->bottomcolour);
R2D_ImageColours(1,1,1,1);
sprintf (num, "%i", tm->frags);
Draw_FunStringWidth(x,y,num, 40, true, false);
if (!strncmp(cl.players[pv->playernum].team, tm->team, 16))
{
Font_BeginString(font_default, x, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 16|0xe000);
Font_BeginString(font_default, x+40, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 17|0xe000);
Font_EndString(font_default);
}
}
else
{
// draw name
Draw_FunStringWidth (x, y, tm->team, 32, false, false);
// draw total
sprintf (num, "%5i", tm->frags);
Draw_FunString(x + 40, y, num);
if (!strncmp(cl.players[pv->playernum].team, tm->team, 16))
{
Font_BeginString(font_default, x-8, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 16|0xe000);
Font_BeginString(font_default, x+32, y, &px, &py);
Font_DrawChar(px, py, CON_WHITEMASK, 17|0xe000);
Font_EndString(font_default);
}
}
y += 8;

View file

@ -981,8 +981,8 @@ struct po_s *PO_Create(void);
void PO_Merge(struct po_s *po, vfsfile_t *file);
const char *PO_GetText(struct po_s *po, const char *msg);
void PO_Close(struct po_s *po);
const char *TL_Translate(const char *src); //$foo translations.
void TL_Reformat(char *out, size_t outsize, size_t numargs, const char **arg); //"{0} died\n" formatting (with $foo translation, on each arg)
const char *TL_Translate(int language, const char *src); //$foo translations.
void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const char **arg); //"{0} died\n" formatting (with $foo translation, on each arg)
//
// log.c

View file

@ -7996,7 +7996,7 @@ qc_extension_t QSG_Extensions[] = {
{"EXT_DIMENSION_GHOST"},
// {"EX_EXTENDED_EF", NULL, 0,{NULL}, "Provides EF_CANDLELIGHT..."},
// {"EX_MOVETYPE_GIB", NULL, 0,{NULL}, "Adds MOVETYPE_GIB - basically MOVETYPE_BOUNCE with gravity controlled by a cvar instead of just setting the .gravity field..."},
// {"EX_PROMPT", NULL, 3,{"ex_prompt", "ex_promptchoice", "ex_clearprompt"}, "Engine-driven alternative to centerprint menus."},
{"EX_PROMPT", NULL, 3,{"ex_prompt", "ex_promptchoice", "ex_clearprompt"}, "Engine-driven alternative to centerprint menus."},
{"FRIK_FILE", check_notrerelease, 11,{"stof", "fopen","fclose","fgets","fputs","strlen","strcat","substring","stov","strzone","strunzone"}},
{"FTE_CALLTIMEOFDAY", NULL, 1,{"calltimeofday"}, "Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use."},
{"FTE_CSQC_ALTCONSOLES", NULL, 4,{"con_getset", "con_printf", "con_draw", "con_input"}, "The engine tracks multiple consoles. These may or may not be directly visible to the user."},

View file

@ -15,15 +15,9 @@ int com_language;
char sys_language[64] = "";
static char langpath[MAX_OSPATH] = "";
struct language_s languages[MAX_LANGUAGES];
static struct po_s *com_translations;
static void QDECL TL_LanguageChanged(struct cvar_s *var, char *oldvalue)
{
if (com_translations)
{
PO_Close(com_translations);
com_translations = NULL;
}
com_language = TL_FindLanguage(var->string);
}
@ -46,6 +40,9 @@ void TL_Shutdown(void)
languages[j].name = NULL;
PO_Close(languages[j].po);
languages[j].po = NULL;
PO_Close(languages[j].po_qex);
languages[j].po_qex = NULL;
}
}
@ -75,10 +72,10 @@ static int TL_LoadLanguage(char *lang)
//keep truncating until we can find a name that works
u = strrchr(lang, '_');
if (u)
{
*u = 0;
else
lang = "";
return TL_LoadLanguage(lang);
return TL_LoadLanguage(lang);
}
}
languages[j].name = strdup(lang);
languages[j].po = NULL;
@ -503,7 +500,7 @@ const char *PO_GetText(struct po_s *po, const char *msg)
}
static void PO_Merge_Rerelease(struct po_s *po, const char *fmt)
static void PO_Merge_Rerelease(struct po_s *po, const char *langname, const char *fmt)
{
//FOO <plat,plat> = "CString"
char line[32768];
@ -512,22 +509,22 @@ static void PO_Merge_Rerelease(struct po_s *po, const char *fmt)
char *s;
vfsfile_t *file = NULL;
if (!file && *language.string) //use system locale names
file = FS_OpenVFS(va(fmt, language.string), "rb", FS_GAME);
if (!file && *langname) //use system locale names
file = FS_OpenVFS(va(fmt, langname), "rb", FS_GAME);
if (!file) //make a guess
{
s = NULL;
if (language.string[0] && language.string[1] && (!language.string[2] || language.string[2] == '-' || language.string[2] == '_'))
if (langname[0] && langname[1] && (!langname[2] || langname[2] == '-' || langname[2] == '_'))
{ //try to map the user's formal locale to the rerelease's arbitrary names (at least from the perspective of anyone who doesn't speak english).
if (!strncmp(language.string, "fr", 2))
if (!strncmp(langname, "fr", 2))
s = "french";
else if (!strncmp(language.string, "de", 2))
else if (!strncmp(langname, "de", 2))
s = "german";
else if (!strncmp(language.string, "it", 2))
else if (!strncmp(langname, "it", 2))
s = "italian";
else if (!strncmp(language.string, "ru", 2))
else if (!strncmp(langname, "ru", 2))
s = "russian";
else if (!strncmp(language.string, "es", 2))
else if (!strncmp(langname, "es", 2))
s = "spanish";
}
if (s)
@ -553,18 +550,18 @@ static void PO_Merge_Rerelease(struct po_s *po, const char *fmt)
}
}
const char *TL_Translate(const char *src)
const char *TL_Translate(int language, const char *src)
{
if (*src == '$')
{
if (!com_translations)
if (!languages[language].po_qex)
{
char lang[64], *h;
vfsfile_t *f = NULL;
com_translations = PO_Create();
PO_Merge_Rerelease(com_translations, "localization/loc_%s.txt");
languages[language].po_qex = PO_Create();
PO_Merge_Rerelease(languages[language].po_qex, languages[language].name, "localization/loc_%s.txt");
Q_strncpyz(lang, language.string, sizeof(lang));
Q_strncpyz(lang, languages[language].name, sizeof(lang));
while ((h = strchr(lang, '-')))
*h = '_'; //standardise it
if (*lang)
@ -579,20 +576,20 @@ const char *TL_Translate(const char *src)
}
}
if (f)
PO_Merge(com_translations, f);
PO_Merge(languages[language].po_qex, f);
}
src = PO_GetText(com_translations, src);
src = PO_GetText(languages[language].po_qex, src);
}
return src;
}
void TL_Reformat(char *out, size_t outsize, size_t numargs, const char **arg)
void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const char **arg)
{
const char *fmt;
const char *a;
size_t alen;
fmt = (numargs>0&&arg[0])?arg[0]:"";
fmt = TL_Translate(fmt);
fmt = TL_Translate(language, fmt);
outsize--;
while (outsize > 0)
@ -623,7 +620,7 @@ void TL_Reformat(char *out, size_t outsize, size_t numargs, const char **arg)
if (index >= numargs || !arg[index])
a = "";
else
a = TL_Translate(arg[index]);
a = TL_Translate(language, arg[index]);
alen = strlen(a);
if (alen > outsize)

View file

@ -12,6 +12,7 @@ struct language_s
{
char *name;
struct po_s *po;
struct po_s *po_qex;
};
extern struct language_s languages[MAX_LANGUAGES];
extern int com_language;

View file

@ -10857,11 +10857,15 @@ static void QCBUILTIN PF_centerprint_qex(pubprogfuncs_t *prinst, struct globalva
int args;
char s[1024];
int entnum;
int lang = com_language;
entnum = G_EDICTNUM(prinst, OFS_PARM0);
for (args = 0; args+1 < prinst->callargc; args++)
arg[args] = PR_GetStringOfs(prinst, OFS_PARM1+args*(OFS_PARM1-OFS_PARM0));
TL_Reformat(s, sizeof(s), args, arg);
if (entnum >= 1 && entnum <= sv.allocated_client_slots)
lang = svs.clients[entnum-1].language;
TL_Reformat(lang, s, sizeof(s), args, arg);
PF_centerprint_Internal(entnum, false, s);
}
@ -10893,7 +10897,6 @@ static void QCBUILTIN PF_sprint_qex(pubprogfuncs_t *prinst, struct globalvars_s
for (args = 0; args+2 < prinst->callargc; args++)
arg[args] = PR_GetStringOfs(prinst, OFS_PARM2+args*(OFS_PARM1-OFS_PARM0));
}
TL_Reformat(s, sizeof(s), args, arg);
if (entnum < 1 || entnum > sv.allocated_client_slots)
{
@ -10903,6 +10906,7 @@ static void QCBUILTIN PF_sprint_qex(pubprogfuncs_t *prinst, struct globalvars_s
client = &svs.clients[entnum-1];
TL_Reformat(client->language, s, sizeof(s), args, arg);
SV_ClientPrintf (client, level, "%s", s);
if (sv_specprint.ival & SPECPRINT_SPRINT)
@ -10929,7 +10933,6 @@ static void QCBUILTIN PF_bprint_qex(pubprogfuncs_t *prinst, struct globalvars_s
{ //TODO: send the strings to the client for localisation+reordering
const char *arg[8];
int args;
char formatted[1024];
int level;
if (progstype == PROG_QW)
@ -10945,8 +10948,178 @@ static void QCBUILTIN PF_bprint_qex(pubprogfuncs_t *prinst, struct globalvars_s
arg[args] = PR_GetStringOfs(prinst, OFS_PARM0+args*(OFS_PARM1-OFS_PARM0));
}
TL_Reformat(formatted, sizeof(formatted), args, arg);
SV_BroadcastPrintf (level, "%s", formatted);
SV_BroadcastPrint_QexLoc (0, level, arg, args);
}
void SV_Prompt_Input(client_t *cl, usercmd_t *ucmd)
{
if (cl->prompt.active && !cl->qex)
{ //with this serverside, we don't get autorepeat etc.
//we also can't prevent movement beyond the menu closure.
if (ucmd->forwardmove >= 100 && cl->prompt.oldmove[0] < 100)
{ //up
if (cl->prompt.selected > 0)
cl->prompt.selected--;
else if (cl->prompt.numopts)
cl->prompt.selected = cl->prompt.numopts-1; //wrap.
cl->prompt.nextsend = realtime;
}
else if (ucmd->forwardmove <= -100 && cl->prompt.oldmove[0] > -100)
{ //down
cl->prompt.selected++;
if (cl->prompt.selected >= cl->prompt.numopts)
cl->prompt.selected = 0; //wrap.
cl->prompt.nextsend = realtime;
}
else if (ucmd->sidemove >= 100 && cl->prompt.oldmove[1] < 100)
{ //right (use)
if (cl->prompt.selected < cl->prompt.numopts)
cl->edict->v->impulse = cl->prompt.opt[cl->prompt.selected].impulse;
}
cl->prompt.oldmove[0] = ucmd->forwardmove;
cl->prompt.oldmove[1] = ucmd->sidemove;
ucmd->forwardmove = 0;
ucmd->sidemove = 0;
}
else
{
cl->prompt.oldmove[0] = ucmd->forwardmove;
cl->prompt.oldmove[1] = ucmd->sidemove;
}
}
void SV_Prompt_Resend(client_t *client)
{
Z_Free(client->centerprintstring);
client->centerprintstring = NULL;
if (client->prompt.nextsend > realtime)
return; //still good.
if (client->qex)
{ //can depend on the client doing any translation stuff.
size_t sz;
sizebuf_t *msg;
size_t i;
sz = 2;
if (client->prompt.numopts)
{
sz += strlen(client->prompt.header)+1;
for (i = 0; i < client->prompt.numopts; i++)
sz += strlen(client->prompt.opt[i].text)+2;
}
msg = ClientReliable_StartWrite(client, sz);
MSG_WriteByte(msg, svcqex_prompt);
MSG_WriteByte(msg, client->prompt.numopts);
if (client->prompt.numopts)
{
MSG_WriteString(msg, client->prompt.header);
for (i = 0; i < client->prompt.numopts; i++)
{
MSG_WriteString(msg, client->prompt.opt[i].text);
MSG_WriteByte(msg, client->prompt.opt[i].impulse);
}
}
ClientReliable_FinishWrite(client);
client->prompt.nextsend = DBL_MAX; //don't let it time out.
}
else
{ //emulate via centerprints
const char *txt[4];
size_t i;
client->prompt.nextsend = realtime + 1; //don't let it time out.
if (client->prompt.numopts)
{
txt[0] = client->prompt.header?client->prompt.header:"";
for (i = 0; i < 3; i++)
{
if (client->prompt.selected + i - 1u < client->prompt.numopts)
txt[1+i] = client->prompt.opt[client->prompt.selected + i - 1u].text;
else
txt[1+i] = " ";
}
//need to translate it too.
for (i = 0; i < countof(txt); i++)
txt[i] = TL_Translate(client->language, txt[i]);
client->centerprintstring = va("%s\n%s\n^a[[ %s ]]^a\n%s", txt[0], txt[1], txt[2], txt[3]);
}
else
client->centerprintstring = client->prompt.header?client->prompt.header:"";
client->centerprintstring = Z_StrDup(client->centerprintstring);
}
}
void SV_Prompt_Clear(client_t *cl)
{
int i;
cl->prompt.active = false;
Z_Free(cl->prompt.header);
cl->prompt.header = NULL;
cl->prompt.selected = 0;
cl->prompt.nextsend = realtime;
for (i = 0; i < cl->prompt.numopts; i++)
Z_Free(cl->prompt.opt[i].text);
cl->prompt.numopts = cl->prompt.maxopts = 0;
Z_Free(cl->prompt.opt);
cl->prompt.opt = NULL;
}
static void QCBUILTIN PF_prompt_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int p = G_EDICT(prinst, OFS_PARM0)->entnum - 1;
client_t *cl;
const char *text = (prinst->callargc >= 2)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;
unsigned int opts = (prinst->callargc >= 3)?G_FLOAT(OFS_PARM2):0;
if (p < 0 || p >= sv.allocated_client_slots)
{
PR_BIError (prinst, "PF_clearprompt_qex: not a player\n");
return;
}
cl = &svs.clients[p];
SV_Prompt_Clear(cl);
if (!text)
{
SV_Prompt_Resend(cl);
return;
}
cl->prompt.active = true;
cl->prompt.maxopts = opts;
cl->prompt.numopts = 0;
Z_StrDupPtr(&cl->prompt.header, text);
cl->prompt.opt = Z_Malloc(sizeof(*cl->prompt.opt) * cl->prompt.maxopts);
}
static void QCBUILTIN PF_promptchoice_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int p = G_EDICT(prinst, OFS_PARM0)->entnum - 1;
client_t *cl;
const char *text = PR_GetStringOfs(prinst, OFS_PARM1);
int impulse = G_FLOAT(OFS_PARM2);
size_t opt;
if (p < 0 || p >= sv.allocated_client_slots)
{
PR_BIError (prinst, "PF_clearprompt_qex: not a player\n");
return;
}
cl = &svs.clients[p];
if (!cl->prompt.active)
{
PR_BIError (prinst, "PF_clearprompt_qex: too many options\n");
return;
}
opt = cl->prompt.numopts++;
if (opt >= cl->prompt.maxopts)
Z_ReallocElements((void**)&cl->prompt.opt, &cl->prompt.maxopts, opt+1, sizeof(*cl->prompt.opt));
Z_StrDupPtr(&cl->prompt.opt[opt].text, text);
cl->prompt.opt[opt].impulse = impulse;
}
#define STUB ,NULL,true
@ -11215,9 +11388,9 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"ex_bot_followentity",PF_Fixme, 0, 0, 0,0, D("float(entity bot, entity goal)", "Behaviour is undocumented.")},
{"ex_CheckPlayerEXFlags",PF_CheckPlayerEXFlags_qex,0,0, 0,0, D("float(entity playerEnt)", "Behaviour is undocumented.")},
{"ex_walkpathtogoal",PF_walkpathtogoal_qex,0, 0, 0,0, D("float(float movedist, vector goal)", "Behaviour is undocumented.")},
// {"ex_prompt", PF_prompt_qex, 0, 0, 0,0, D("void(entity player, string text, float numchoices)", "Behaviour is undocumented.")},
// {"ex_promptchoice", PF_promptchoice_qex,0, 0, 0,0, D("void(entity player, string text, float impulse)", "Behaviour is undocumented.")},
// {"ex_clearprompt", PF_clearprompt_qex, 0, 0, 0,0, D("void(entity player)", "Behaviour is undocumented.")},
{"ex_prompt", PF_prompt_qex, 0, 0, 0,0, D("void(entity player, string text, float numchoices)", "Behaviour is undocumented.")},
{"ex_promptchoice", PF_promptchoice_qex,0, 0, 0,0, D("void(entity player, string text, float impulse)", "Behaviour is undocumented.")},
{"ex_clearprompt", PF_prompt_qex, 0, 0, 0,0, D("void(entity player)", "Behaviour is undocumented.")},
//End QuakeEx, for now. :(
// Tomaz - QuakeC String Manipulation Begin

View file

@ -559,6 +559,21 @@ typedef struct client_s
char *statss[MAX_CL_STATS];
char *centerprintstring;
struct
{
qboolean active;
char *header;
double nextsend; //qex is a one-off, other clients need spam.
size_t numopts, maxopts, selected;
struct
{
char *text;
int impulse;
} *opt;
int oldmove[2];
} prompt;
union{ //save space
client_frame_t *frames; // updates can be deltad from here
#ifdef Q2SERVER
@ -1303,6 +1318,7 @@ void SV_StuffcmdToClient_Unreliable(client_t *cl, const char *string);
void VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) LIKEPRINTF(3);
void VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t text, ...);
void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...) LIKEPRINTF(2);
void SV_BroadcastPrint_QexLoc (unsigned int flags, int level, const char **arg, int args);
void SV_BroadcastPrint (unsigned int flags, int level, const char *text);
//flags exposed to ktx.
#define BPRINT_IGNOREINDEMO (1<<0) // broad cast print will be not put in demo
@ -1317,6 +1333,10 @@ void SV_SendFixAngle(client_t *client, sizebuf_t *msg, int fixtype, qboolean rol
void SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *key, const char *newval);
void SV_Prompt_Resend(client_t *cl);
void SV_Prompt_Clear(client_t *cl);
void SV_Prompt_Input(client_t *cl, usercmd_t *ucmd);
//
// sv_user.c
//

View file

@ -613,6 +613,7 @@ void SV_DropClient (client_t *drop)
break;
}
SV_Prompt_Clear(drop);
if (drop->centerprintstring)
Z_Free(drop->centerprintstring);
drop->centerprintstring = NULL;
@ -1084,6 +1085,19 @@ void SV_FullClientUpdate (client_t *client, client_t *to)
MSG_WriteByte(buf, playercolor);
ClientReliable_FinishWrite(to);
if (to->qex)
{
unsigned int s1, s2;
if (client->netchan.remote_address.type == NA_LOOPBACK)
s1 = s2 = 0; //host
else
s1 = s2 = -1; //non-playfab connection
MSG_WriteByte(buf, svcqex_updatesocial);
MSG_WriteByte(buf, i);
MSG_WriteLong(buf, s1);
MSG_WriteLong(buf, s2);
}
if (to->fteprotocolextensions2 & PEXT2_PREDINFO)
{
char *s;
@ -1961,11 +1975,18 @@ void SV_AcceptMessage(client_t *newcl)
MSG_WriteLong(&sb, 0);
MSG_WriteByte(&sb, CCREP_ACCEPT);
if (newcl->qex)
; //skip any port info (as well as any proquake ident stuff.
; //skip any port info (as well as any proquake ident stuff).
else
{
NET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0);
MSG_WriteLong(&sb, ShortSwap(localaddr.port));
if (net_from.prot == NP_DTLS
#ifdef SUPPORT_ICE
|| net_from.type == NA_ICE
#endif
)
MSG_WriteLong(&sb, 0); //send a port of 0 if we expect the client to be sane enough and/or otherwise problematic.
else
MSG_WriteLong(&sb, ShortSwap(localaddr.port));
if (newcl->proquake_angles_hack)
{
MSG_WriteByte(&sb, MOD_PROQUAKE);

View file

@ -409,6 +409,69 @@ void SV_BroadcastPrint (unsigned int flags, int level, const char *string)
#endif
}
void SV_BroadcastPrint_QexLoc (unsigned int flags, int level, const char **arg, int args)
{
client_t *cl;
int i;
char string[1024];
if (!(flags & BPRINT_IGNORECONSOLE))
{
//pretend to print on the server, but not to the client's console
TL_Reformat(com_language, string, sizeof(string), args, arg);
Sys_Printf ("%s", string); // print to the system console
Log_String(LOG_CONSOLE, string); //dump into log
}
if (!(flags & BPRINT_IGNORECLIENTS))
{
for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)
{
if (level < cl->messagelevel)
continue;
if (!cl->state)
continue;
if (cl->protocol == SCP_BAD)
continue;
if (cl == sv.skipbprintclient) //silence bprints about the player in ClientConnect. NQ completely wipes the buffer after clientconnect, which is what traditionally hides it.
continue;
if (cl->controller)
continue;
if (cl->qex)
{ //get the damn client to do it.
int size = 3;
for (i = 0; i < args; i++)
size += strlen(arg[i])+1;
ClientReliableWrite_Begin (cl, svcqex_locprint, size);
ClientReliableWrite_Short (cl, args);
for (i = 0; i < args; i++)
ClientReliableWrite_String (cl, arg[i]);
}
else
{
TL_Reformat(cl->language, string, sizeof(string), args, arg);
SV_PrintToClient(cl, level, string);
}
}
}
#ifdef MVD_RECORDING
if (sv.mvdrecording && !(flags&BPRINT_IGNOREINDEMO))
{
sizebuf_t *msg;
TL_Reformat(com_language, string, sizeof(string), args, arg);
msg = MVDWrite_Begin (dem_all, 0, strlen(string)+3);
MSG_WriteByte (msg, svc_print);
MSG_WriteByte (msg, level);
MSG_WriteString (msg, string);
}
#endif
}
void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...)
{
va_list argptr;
@ -1640,6 +1703,9 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
{
SV_WriteEntityDataToMessage(split, msg, pnum);
if (split->prompt.active)
SV_Prompt_Resend(split);
if (split->centerprintstring && ! client->num_backbuf)
{
SV_WriteCenterPrint(split, split->centerprintstring);

View file

@ -4068,6 +4068,35 @@ void SV_PushFloodProt(client_t *client)
client->lastspoke = realtime;
}
#ifdef NQPROT
static void SV_SendQEXChat(client_t *to, int clcolour, int chatcolour, const char *sendername, const char *message)
{
if (to->controller)
to = to->controller;
switch (to->protocol)
{
case SCP_BAD: //bot
break;
case SCP_QUAKE2:
case SCP_QUAKE3:
case SCP_QUAKEWORLD:
break; //doesn't make sense.
case SCP_DARKPLACES6:
case SCP_DARKPLACES7:
case SCP_NETQUAKE:
case SCP_BJP3:
case SCP_FITZ666:
ClientReliableWrite_Begin (to, svcqex_chat, 3 + strlen(sendername)+strlen(message));
ClientReliableWrite_Byte (to, clcolour);
ClientReliableWrite_Byte (to, chatcolour);
ClientReliableWrite_String (to, sendername);
ClientReliableWrite_String (to, message);
break;
}
}
#endif
/*
==================
SV_Say
@ -4100,7 +4129,7 @@ void SV_Say (qboolean team)
memset(sent, 0, sizeof(sent));
if (team)
if (1)//team)
{
Q_strncpyz (t1, InfoBuf_ValueForKey(&host_client->userinfo, "team"), sizeof(t1));
}
@ -4223,7 +4252,25 @@ void SV_Say (qboolean team)
else
sent[cln] = true;
SV_ClientPrintf(client, PRINT_CHAT, "%s", text);
#ifdef NQPROT
if (client->qex)
{ //white, green, cyan, yellow
int c = 0;
if (client == host_client)
c = 3; //yellow for yourself.
else
{
t2 = InfoBuf_ValueForKey (&client->userinfo, "team");
if (strcmp(t1, t2))
c = 1; //green for other team. should probably be red but that's not an option.
else
c = 2; //cyan for same team.
}
SV_SendQEXChat(client, c, !!team, host_client->name, p);
}
else
#endif
SV_ClientPrintf(client, PRINT_CHAT, "%s", text);
}
#ifdef MVD_RECORDING
sv.mvdrecording = mvdrecording;
@ -8113,6 +8160,7 @@ static double SVFTE_ExecuteClientMove(client_t *controller)
ran=true;
}
SV_Prompt_Input(split, &split->lastcmd);
SV_RunCmd (&split->lastcmd, false);
split->lastruncmd = split->lastcmd.servertime;
}
@ -8123,6 +8171,7 @@ static double SVFTE_ExecuteClientMove(client_t *controller)
if (split->lastcmd.impulse)
split->edict->v->impulse = split->lastcmd.impulse;
SV_Prompt_Input(split, &split->lastcmd);
SV_SetEntityButtons(split->edict, split->lastcmd.buttons);
split->lastcmd.buttons = 0;
}
@ -8376,6 +8425,7 @@ void SV_ExecuteClientMessage (client_t *cl)
if (newcmd.impulse)// && SV_FilterImpulse(newcmd.impulse, host_client->trustlevel))
split->edict->v->impulse = newcmd.impulse;
SV_Prompt_Input(split, &newcmd);
SV_SetEntityButtons(split->edict, newcmd.buttons);
}
else
@ -8388,18 +8438,26 @@ void SV_ExecuteClientMessage (client_t *cl)
{
while (net_drop > 2)
{
SV_Prompt_Input(split, &split->lastcmd);
SV_RunCmd (&split->lastcmd, false);
net_drop--;
}
if (net_drop > 1)
{
SV_Prompt_Input(split, &oldest);
SV_RunCmd (&oldest, false);
}
if (net_drop > 0)
{
SV_Prompt_Input(split, &oldcmd);
SV_RunCmd (&oldcmd, false);
}
}
SV_Prompt_Input(split, &newcmd);
SV_RunCmd (&newcmd, false);
host_client->lastruncmd = sv.time*1000;
split->lastruncmd = sv.time*1000;
if (!SV_PlayerPhysicsQC || host_client->spectator)
if (!SV_PlayerPhysicsQC || split->spectator)
SV_PostRunCmd();
}
@ -8741,7 +8799,7 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
frame = &host_client->frameunion.frames[host_client->netchan.incoming_acknowledged & UPDATE_MASK];
if (quakeex)
;
; //sequence is a separate clc (should have already been sent)
else if (host_client->protocol == SCP_DARKPLACES7)
host_client->last_sequence = MSG_ReadLong ();
else if (host_client->fteprotocolextensions2 & PEXT2_PREDINFO)
@ -8888,6 +8946,8 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
}
}
SV_Prompt_Input(host_client, &cmd);
/*host_client->edict->v->v_angle[0] = SHORT2ANGLE(cmd.angles[0]);
host_client->edict->v->v_angle[1] = SHORT2ANGLE(cmd.angles[1]);
host_client->edict->v->v_angle[2] = SHORT2ANGLE(cmd.angles[2]);*/