mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 05:11:35 +00:00
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:
parent
3a6f22d05c
commit
df6b651eeb
15 changed files with 714 additions and 155 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."},
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);*/
|
||||
|
|
Loading…
Reference in a new issue