906 lines
25 KiB
C++
906 lines
25 KiB
C++
|
// cg_text.c --
|
|||
|
|
|||
|
// this line must stay at top so the whole PCH thing works...
|
|||
|
#include "cg_headers.h"
|
|||
|
|
|||
|
#include "cg_media.h"
|
|||
|
|
|||
|
|
|||
|
//int precacheWav_i; // Current high index of precacheWav array
|
|||
|
//precacheWav_t precacheWav[MAX_PRECACHEWAV];
|
|||
|
|
|||
|
|
|||
|
//int precacheText_i; // Current high index of precacheText array
|
|||
|
//precacheText_t precacheText[MAX_PRECACHETEXT];
|
|||
|
|
|||
|
|
|||
|
extern vec4_t textcolor_caption;
|
|||
|
extern vec4_t textcolor_center;
|
|||
|
extern vec4_t textcolor_scroll;
|
|||
|
|
|||
|
|
|||
|
#define GAMETEXT_X_START 75.0f
|
|||
|
#define GAMETEXT_X_END 600.0f
|
|||
|
|
|||
|
#define MAX_NUM_GAMELINES 4
|
|||
|
|
|||
|
void CG_GameText(int y )
|
|||
|
{
|
|||
|
CG_Printf("CG_GameText() being called. Tell Ste\n");
|
|||
|
|
|||
|
/* const char *s,*holds;
|
|||
|
int i, len;
|
|||
|
float x, w;
|
|||
|
int numChars;
|
|||
|
int text_i;
|
|||
|
char str[MAX_QPATH];
|
|||
|
int holdCnt,playingTime;
|
|||
|
int totalLength,sound,max;
|
|||
|
|
|||
|
Q_strncpyz (str, CG_Argv( 1 ), MAX_QPATH );
|
|||
|
|
|||
|
cg.gameTextSpeaker = atoi(CG_Argv(2));
|
|||
|
cg.gameTextEntNum = atoi(CG_Argv(3));
|
|||
|
|
|||
|
sound = cgs.sound_precache[atoi(CG_Argv(4))];
|
|||
|
|
|||
|
text_i = CG_SearchTextPrecache(str);
|
|||
|
//ensure we found a match
|
|||
|
if (text_i == -1)
|
|||
|
{
|
|||
|
Com_Printf("WARNING: CG_GameText given invalid text key :'%s'",str);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
cg.gameTextTime = cg.time;
|
|||
|
cg.printTextY = 5 + SMALLCHAR_HEIGHT;
|
|||
|
|
|||
|
cg.gameTextCurrentLine = 0;
|
|||
|
|
|||
|
// count the number of lines for centering
|
|||
|
cg.scrollTextLines = 1;
|
|||
|
|
|||
|
|
|||
|
memset (cg.printText, 0, sizeof(cg.printText));
|
|||
|
|
|||
|
// Break into individual lines
|
|||
|
i = 0;
|
|||
|
len = 0;
|
|||
|
s = precacheText[text_i].text;
|
|||
|
holds = s;
|
|||
|
|
|||
|
playingTime = cgi_S_GetSampleLength(sound);
|
|||
|
totalLength = strlen(s);
|
|||
|
if (totalLength == 0)
|
|||
|
{
|
|||
|
totalLength = 1;
|
|||
|
}
|
|||
|
cg.gameLetterTime = playingTime / totalLength;
|
|||
|
|
|||
|
//We start at column 75 according to DrawGameText
|
|||
|
x = GAMETEXT_X_START;
|
|||
|
w = GAMETEXT_X_END - GAMETEXT_X_START;
|
|||
|
numChars = floor(w/SMALLCHAR_WIDTH);
|
|||
|
|
|||
|
while( *s )
|
|||
|
{
|
|||
|
len++;
|
|||
|
if (*s == '\n')
|
|||
|
{//Being told explicitly to start a new line
|
|||
|
Q_strncpyz( cg.printText[i], holds, len);
|
|||
|
i++;
|
|||
|
len = 0;
|
|||
|
holds = s;
|
|||
|
holds++;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
else if ( len == numChars )
|
|||
|
{//Reached max length of this line
|
|||
|
//step back until we find a space
|
|||
|
while( len && *s != ' ' )
|
|||
|
{
|
|||
|
s--;
|
|||
|
len--;
|
|||
|
}
|
|||
|
//break the line here
|
|||
|
Q_strncpyz( cg.printText[i], holds, len);
|
|||
|
i++;
|
|||
|
len = 0;
|
|||
|
holds = s;
|
|||
|
holds++;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
s++;
|
|||
|
}
|
|||
|
|
|||
|
len++; // So the NULL will be properly placed at the end of the string of Q_strncpyz
|
|||
|
Q_strncpyz( cg.printText[i], holds, len); // To get the last line
|
|||
|
|
|||
|
//NOTE: This might be able to use the VoiceVolume or TID_VOICE info from the cg.gameTextEntNum
|
|||
|
// to decide when to drop the text...
|
|||
|
max = MAX_NUM_GAMELINES;
|
|||
|
if (max >cg.scrollTextLines)
|
|||
|
{
|
|||
|
max = cg.scrollTextLines;
|
|||
|
}
|
|||
|
|
|||
|
holdCnt = 0;
|
|||
|
for (i=0;i<max;++i)
|
|||
|
{
|
|||
|
holdCnt += strlen(cg.printText[i]);
|
|||
|
}
|
|||
|
cg.gameNextTextTime = cg.time + (holdCnt * cg.gameLetterTime);
|
|||
|
|
|||
|
|
|||
|
cg.scrollTextTime = 0; // No scrolling during captions
|
|||
|
*/
|
|||
|
}
|
|||
|
|
|||
|
void CG_DrawGameText(void)
|
|||
|
{
|
|||
|
if ( !cg.gameTextTime )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
CG_Printf("CG_DrawGameText() being called. Tell Ste\n");
|
|||
|
/*
|
|||
|
char *start;
|
|||
|
int l;
|
|||
|
int i,max;
|
|||
|
int x, y;
|
|||
|
char linebuffer[1024], string[1024];
|
|||
|
int holdCnt;
|
|||
|
vec4_t color;
|
|||
|
|
|||
|
|
|||
|
// Advance to next line (if there are any) and calculate time to show
|
|||
|
if ( cg.gameNextTextTime < cg.time )
|
|||
|
{
|
|||
|
cg.gameTextCurrentLine += MAX_NUM_GAMELINES;
|
|||
|
|
|||
|
if (cg.gameTextCurrentLine >= cg.scrollTextLines)
|
|||
|
{
|
|||
|
cg.gameTextTime = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
max = MAX_NUM_GAMELINES;
|
|||
|
if ((cg.scrollTextLines - cg.gameTextCurrentLine) < max)
|
|||
|
{
|
|||
|
max = cg.scrollTextLines - cg.gameTextCurrentLine;
|
|||
|
}
|
|||
|
|
|||
|
// Loop through next lines to calc how long to show 'em
|
|||
|
holdCnt = 0;
|
|||
|
for (i=cg.gameTextCurrentLine;i<(cg.gameTextCurrentLine + max);++i)
|
|||
|
{
|
|||
|
holdCnt += strlen(cg.printText[i]);
|
|||
|
}
|
|||
|
cg.gameNextTextTime = cg.time + (holdCnt * cg.gameLetterTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Give a color if one wasn't given
|
|||
|
if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) &&
|
|||
|
(textcolor_caption[2] == 0) && (textcolor_caption[3] == 0))
|
|||
|
{
|
|||
|
Vector4Copy( colorTable[CT_WHITE], textcolor_caption );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
color[0] = colorTable[CT_BLACK][0];
|
|||
|
color[1] = colorTable[CT_BLACK][1];
|
|||
|
color[2] = colorTable[CT_BLACK][2];
|
|||
|
color[3] = 0.350f;
|
|||
|
|
|||
|
// Set Y of the first line
|
|||
|
y = cg.printTextY;
|
|||
|
x = GAMETEXT_X_START;
|
|||
|
|
|||
|
// Background
|
|||
|
cgi_R_SetColor(color); // Background, CLAMP TO 4 LINES
|
|||
|
CG_DrawPic( x - 4, y - SMALLCHAR_HEIGHT - 2, (70 * SMALLCHAR_WIDTH),(( ((cg.scrollTextLines>MAX_NUM_GAMELINES)?MAX_NUM_GAMELINES:cg.scrollTextLines) + 1) * SMALLCHAR_HEIGHT) + 4, cgs.media.ammoslider );
|
|||
|
|
|||
|
sprintf(string, "%s:", speakerTable[cg.gameTextSpeaker].stringID);
|
|||
|
|
|||
|
CG_DrawStringExt( x, y - SMALLCHAR_HEIGHT, string, colorTable[CT_LTPURPLE1], qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT );
|
|||
|
|
|||
|
for (i= cg.gameTextCurrentLine;i< cg.gameTextCurrentLine + MAX_NUM_GAMELINES;++i)
|
|||
|
{
|
|||
|
start = cg.printText[i];
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
|
|||
|
for ( l = 0; l < 80; l++ )
|
|||
|
{
|
|||
|
if ( !start[l] || start[l] == '\n' )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
linebuffer[l] = start[l];
|
|||
|
}
|
|||
|
linebuffer[l] = 0;
|
|||
|
|
|||
|
|
|||
|
CG_DrawStringExt( x, y, linebuffer, textcolor_caption, qfalse, qtrue,
|
|||
|
SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT );
|
|||
|
|
|||
|
y += SMALLCHAR_HEIGHT;
|
|||
|
|
|||
|
while ( *start && ( *start != '\n' ) )
|
|||
|
{
|
|||
|
start++;
|
|||
|
}
|
|||
|
|
|||
|
if ( !*start )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
start++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cgi_R_SetColor( NULL );
|
|||
|
*/
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// display text in a supplied box, start at top left and going down by however many pixels I feel like internally,
|
|||
|
// return value is NULL if all fitted, else char * of next char to continue from that didn't fit.
|
|||
|
//
|
|||
|
// (coords are in the usual 640x480 virtual space)...
|
|||
|
//
|
|||
|
// ( if you get the same char * returned as what you passed in, then none of it fitted at all (box too small) )
|
|||
|
//
|
|||
|
const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
|
|||
|
const char *psText, int iFontHandle, float fScale,
|
|||
|
const vec4_t v4Color)
|
|||
|
{
|
|||
|
cgi_R_SetColor( v4Color );
|
|||
|
|
|||
|
// Setup a reasonable vertical spacing (taiwanese needs 1.5 fontheight, so use that for all)...
|
|||
|
//
|
|||
|
const int iFontHeight = cgi_R_Font_HeightPixels(iFontHandle, fScale);
|
|||
|
const int iFontHeightAdvance = (int) (1.5f * (float) iFontHeight);
|
|||
|
int iYpos = iBoxY; // start print pos
|
|||
|
|
|||
|
// findmeste // test stuff, remove later
|
|||
|
// psText = "<22>ɨ<EFBFBD><C9A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Õ¤h<D5A4>w<EFBFBD>g<EFBFBD>w<EFBFBD><77><EFBFBD>F<EFBFBD>A<EFBFBD>Ú¤]<5D><><EFBFBD>Ò¦<EFBFBD><D2A6>o<EFBFBD>{<7B><><EFBFBD>i<EFBFBD><69><EFBFBD>u<EFBFBD>Ó©<EFBFBD><D3A9>v<EFBFBD>C<EFBFBD>ܤ<EFBFBD><DCA4><EFBFBD><EFBFBD>a<EFBFBD>A<EFBFBD><41><EFBFBD>hĵ<68><C4B5><EFBFBD><EFBFBD><EFBFBD>Ç¥<EFBFBD>è<EFBFBD>o<EFBFBD>{<7B>F<EFBFBD>@<40>Ǫ<EFBFBD><C7AA>p<EFBFBD>A<EFBFBD>dzƦb<C6A6><62><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD><65><EFBFBD>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>E<EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>C<EFBFBD>L<EFBFBD><4C><EFBFBD>˦<EFBFBD><CBA6>~<7E><><EFBFBD>ϸ`<60>A<EFBFBD><41><EFBFBD>L<EFBFBD>F<EFBFBD>h<EFBFBD>h<EFBFBD><68><EFBFBD>Æ¡C<C6A1>{<7B>b<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD>H<EFBFBD><48><EFBFBD>A<EFBFBD>Ã¥B<C3A5>¯Ùn<D9AD><6E><EFBFBD><EFBFBD><EFBFBD>f<EFBFBD>r<EFBFBD>C<EFBFBD>ھڳ̷psCurrentTextReadPos<6F><73><EFBFBD><EFBFBD><EFBFBD>i<EFBFBD>A<EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>H<EFBFBD>Î¥L<CEA5><4C><EFBFBD>ҦФw<D0A4>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ú¤F<DAA4><46><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>Ú¨<EFBFBD><DAA8>R<EFBFBD>Ó°l<D3B0><6C><EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>H<EFBFBD>αϥX<CFA5>Ò¦<EFBFBD><D2A6>H<EFBFBD><48><EFBFBD>C<EFBFBD>o<EFBFBD>ä<EFBFBD><C3A4>e<EFBFBD><65><EFBFBD>C";
|
|||
|
|
|||
|
// this could probably be simplified now, but it was converted from something else I didn't originally write,
|
|||
|
// and it works anyway so wtf...
|
|||
|
//
|
|||
|
const char *psCurrentTextReadPos = psText;
|
|||
|
const char *psReadPosAtLineStart = psCurrentTextReadPos;
|
|||
|
const char *psBestLineBreakSrcPos = psCurrentTextReadPos;
|
|||
|
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
|
|||
|
while( *psCurrentTextReadPos && (iYpos + iFontHeight < (iBoxY + iBoxHeight)) )
|
|||
|
{
|
|||
|
char sLineForDisplay[2048]; // ott
|
|||
|
|
|||
|
// construct a line...
|
|||
|
//
|
|||
|
psCurrentTextReadPos = psReadPosAtLineStart;
|
|||
|
sLineForDisplay[0] = '\0';
|
|||
|
while ( *psCurrentTextReadPos )
|
|||
|
{
|
|||
|
psLastGood_s = psCurrentTextReadPos;
|
|||
|
|
|||
|
// read letter...
|
|||
|
//
|
|||
|
qboolean bIsTrailingPunctuation;
|
|||
|
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(&psCurrentTextReadPos, &bIsTrailingPunctuation);
|
|||
|
|
|||
|
// concat onto string so far...
|
|||
|
//
|
|||
|
if (uiLetter > 255)
|
|||
|
{
|
|||
|
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c",uiLetter & 0xFF));
|
|||
|
}
|
|||
|
|
|||
|
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
|
|||
|
//
|
|||
|
if (bIsTrailingPunctuation || uiLetter == ' ')
|
|||
|
{
|
|||
|
psBestLineBreakSrcPos = psCurrentTextReadPos;
|
|||
|
}
|
|||
|
|
|||
|
if (uiLetter == '\n')
|
|||
|
{
|
|||
|
// explicit new line...
|
|||
|
//
|
|||
|
sLineForDisplay[ strlen(sLineForDisplay)-1 ] = '\0'; // kill the CR
|
|||
|
psReadPosAtLineStart = psCurrentTextReadPos;
|
|||
|
break; // print this line
|
|||
|
}
|
|||
|
else
|
|||
|
if ( cgi_R_Font_StrLenPixels(sLineForDisplay, iFontHandle, fScale) >= (iBoxWidth - 16) )
|
|||
|
{
|
|||
|
// reached screen edge, so cap off string at bytepos after last good position...
|
|||
|
//
|
|||
|
if (psBestLineBreakSrcPos == psReadPosAtLineStart)
|
|||
|
{
|
|||
|
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
|
|||
|
// since it doesn't have a single space or punctuation mark right the way across one line
|
|||
|
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
|
|||
|
// string into this function while the game was running in english (which should NEVER happen
|
|||
|
// normally). On the other hand I suppose it'psCurrentTextReadPos entirely possible that some taiwanese string
|
|||
|
// might have no punctuation at all, so...
|
|||
|
//
|
|||
|
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
|
|||
|
}
|
|||
|
|
|||
|
sLineForDisplay[ psBestLineBreakSrcPos - psReadPosAtLineStart ] = '\0';
|
|||
|
psReadPosAtLineStart = psCurrentTextReadPos = psBestLineBreakSrcPos;
|
|||
|
break; // print this line
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ... and print it...
|
|||
|
//
|
|||
|
// int iWidth = cgi_R_Font_StrLenPixels( sLineForDisplay, iFontHandle, iFontScale );
|
|||
|
// if (iWidth)
|
|||
|
// {
|
|||
|
// int x = BoxX; // ignore this for now -----> (SCREEN_WIDTH-iWidth) / 2;
|
|||
|
cgi_R_Font_DrawString(iBoxX, iYpos, sLineForDisplay, v4Color, iFontHandle, -1, fScale);
|
|||
|
iYpos += iFontHeightAdvance;
|
|||
|
// }
|
|||
|
|
|||
|
// and echo to console in dev mode...
|
|||
|
//
|
|||
|
if ( cg_developer.integer )
|
|||
|
{
|
|||
|
Com_Printf( "%psCurrentTextReadPos\n", sLineForDisplay );
|
|||
|
}
|
|||
|
}
|
|||
|
return psReadPosAtLineStart;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
===============================================================================
|
|||
|
|
|||
|
CAPTION TEXT
|
|||
|
|
|||
|
===============================================================================
|
|||
|
*/
|
|||
|
void CG_CaptionTextStop(void)
|
|||
|
{
|
|||
|
cg.captionTextTime = 0;
|
|||
|
}
|
|||
|
|
|||
|
// slightly confusingly, the char arg for this function is an audio filename of the form "path/path/filename",
|
|||
|
// the "filename" part of which should be the same as the StripEd reference we're looking for in the current
|
|||
|
// level's string package...
|
|||
|
//
|
|||
|
void CG_CaptionText( const char *str, int sound, int y )
|
|||
|
{
|
|||
|
const char *s, *holds;
|
|||
|
int i;
|
|||
|
int holdTime;
|
|||
|
char text[8192]={0};
|
|||
|
|
|||
|
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
|
|||
|
|
|||
|
holds = strrchr(str,'/');
|
|||
|
if (!holds)
|
|||
|
{
|
|||
|
#ifndef FINAL_BUILD
|
|||
|
Com_Printf("WARNING: CG_CaptionText given audio filename with no '/':'%s'\n",str);
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
i = cgi_SP_GetStringTextString( va("%s_%s",cgs.stripLevelName,holds+1), text, sizeof(text) );
|
|||
|
//ensure we found a match
|
|||
|
if (!i)
|
|||
|
{
|
|||
|
#ifndef FINAL_BUILD
|
|||
|
Com_Printf("WARNING: CG_CaptionText given invalid text key :'%s'\n",str);
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale)); // taiwanese needs 1.5 fontheight spacing
|
|||
|
|
|||
|
cg.captionTextTime = cg.time;
|
|||
|
if (in_camera) {
|
|||
|
cg.captionTextY = SCREEN_HEIGHT - (client_camera.bar_height_dest/2); // ths is now a centre'd Y, not a start Y
|
|||
|
} else { //get above the hud
|
|||
|
cg.captionTextY = (int) (0.75f * ((float)SCREEN_HEIGHT - (float)fontHeight * 1.5f));
|
|||
|
}
|
|||
|
cg.captionTextCurrentLine = 0;
|
|||
|
|
|||
|
// count the number of lines for centering
|
|||
|
cg.scrollTextLines = 1;
|
|||
|
|
|||
|
memset (cg.captionText, 0, sizeof(cg.captionText));
|
|||
|
|
|||
|
// Break into individual lines
|
|||
|
i = 0; // this could be completely removed and replace by "cg.scrollTextLines-1", but wtf?
|
|||
|
|
|||
|
// findmeste // test stuff, remove later
|
|||
|
s=(const char*)&text;
|
|||
|
// tai...
|
|||
|
// s="<22>ɨ<EFBFBD><C9A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Õ¤h<D5A4>w<EFBFBD>g<EFBFBD>w<EFBFBD><77><EFBFBD>F<EFBFBD>A<EFBFBD>Ú¤]<5D><><EFBFBD>Ò¦<EFBFBD><D2A6>o<EFBFBD>{<7B><><EFBFBD>i<EFBFBD><69><EFBFBD>u<EFBFBD>Ó©<EFBFBD><D3A9>v<EFBFBD>C<EFBFBD>ܤ<EFBFBD><DCA4><EFBFBD><EFBFBD>a<EFBFBD>A<EFBFBD><41><EFBFBD>hĵ<68><C4B5><EFBFBD><EFBFBD><EFBFBD>Ç¥<EFBFBD>è<EFBFBD>o<EFBFBD>{<7B>F<EFBFBD>@<40>Ǫ<EFBFBD><C7AA>p<EFBFBD>A<EFBFBD>dzƦb<C6A6><62><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD><65><EFBFBD>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>E<EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>C<EFBFBD>L<EFBFBD><4C><EFBFBD>˦<EFBFBD><CBA6>~<7E><><EFBFBD>ϸ`<60>A<EFBFBD><41><EFBFBD>L<EFBFBD>F<EFBFBD>h<EFBFBD>h<EFBFBD><68><EFBFBD>Æ¡C<C6A1>{<7B>b<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD>H<EFBFBD><48><EFBFBD>A<EFBFBD>Ã¥B<C3A5>¯Ùn<D9AD><6E><EFBFBD><EFBFBD><EFBFBD>f<EFBFBD>r<EFBFBD>C<EFBFBD>ھڳ̷s<CCB7><73><EFBFBD><EFBFBD><EFBFBD>i<EFBFBD>A<EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>H<EFBFBD>Î¥L<CEA5><4C><EFBFBD>ҦФw<D0A4>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ú¤F<DAA4><46><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>Ú¨<EFBFBD><DAA8>R<EFBFBD>Ó°l<D3B0><6C><EFBFBD>ǧJ<C7A7><4A><EFBFBD>o<EFBFBD>H<EFBFBD>αϥX<CFA5>Ò¦<EFBFBD><D2A6>H<EFBFBD><48><EFBFBD>C<EFBFBD>o<EFBFBD>ä<EFBFBD><C3A4>e<EFBFBD><65><EFBFBD>C";
|
|||
|
// kor...
|
|||
|
// s="Wp:<3A><>Ÿ<EFBFBD><C5B8><EFBFBD>̴<EFBFBD> <20>ָ<EFBFBD>. <20>׵<EFBFBD><D7B5><EFBFBD> <20><><EFBFBD>Ѵ<EFBFBD><D1B4><EFBFBD> <20>װ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϰڴ<CFB0>.<2E><>Ÿ<EFBFBD><C5B8><EFBFBD>̴<EFBFBD> <20>ָ<EFBFBD>. <20>׵<EFBFBD><D7B5><EFBFBD> <20><><EFBFBD>Ѵ<EFBFBD><D1B4><EFBFBD> <20>װ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϰڴ<CFB0>.";
|
|||
|
holds = s;
|
|||
|
|
|||
|
int iPlayingTimeMS = cgi_S_GetSampleLength(sound);
|
|||
|
int iLengthInChars = strlen(s);//cgi_R_Font_StrLenChars(s); // strlen is also good for MBCS in this instance, since it's for timing
|
|||
|
if (iLengthInChars == 0)
|
|||
|
{
|
|||
|
iLengthInChars = 1;
|
|||
|
}
|
|||
|
cg.captionLetterTime = iPlayingTimeMS / iLengthInChars;
|
|||
|
|
|||
|
const char *psBestLineBreakSrcPos = s;
|
|||
|
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
|
|||
|
while( *s )
|
|||
|
{
|
|||
|
psLastGood_s = s;
|
|||
|
|
|||
|
// read letter...
|
|||
|
//
|
|||
|
qboolean bIsTrailingPunctuation;
|
|||
|
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(&s, &bIsTrailingPunctuation);
|
|||
|
|
|||
|
// concat onto string so far...
|
|||
|
//
|
|||
|
if (uiLetter > 255)
|
|||
|
{
|
|||
|
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c",uiLetter & 0xFF));
|
|||
|
}
|
|||
|
|
|||
|
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
|
|||
|
//
|
|||
|
if (bIsTrailingPunctuation || uiLetter == ' ')
|
|||
|
{
|
|||
|
psBestLineBreakSrcPos = s;
|
|||
|
}
|
|||
|
|
|||
|
if (uiLetter == '\n')
|
|||
|
{
|
|||
|
// explicit new line...
|
|||
|
//
|
|||
|
cg.captionText[i][ strlen(cg.captionText[i])-1 ] = '\0'; // kill the CR
|
|||
|
i++;
|
|||
|
holds = s;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
else
|
|||
|
if ( cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale) >= SCREEN_WIDTH - 16)
|
|||
|
{
|
|||
|
// reached screen edge, so cap off string at bytepos after last good position...
|
|||
|
//
|
|||
|
if (psBestLineBreakSrcPos == holds)
|
|||
|
{
|
|||
|
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
|
|||
|
// since it doesn't have a single space or punctuation mark right the way across one line
|
|||
|
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
|
|||
|
// string into this function while the game was running in english (which should NEVER happen
|
|||
|
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
|
|||
|
// might have no punctuation at all, so...
|
|||
|
//
|
|||
|
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
|
|||
|
}
|
|||
|
|
|||
|
cg.captionText[i][ psBestLineBreakSrcPos - holds ] = '\0';
|
|||
|
holds = s = psBestLineBreakSrcPos;
|
|||
|
i++;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// calc the length of time to hold each 2 lines of text on the screen.... presumably this works?
|
|||
|
//
|
|||
|
holdTime = strlen(cg.captionText[0]);
|
|||
|
if (cg.scrollTextLines > 1)
|
|||
|
{
|
|||
|
holdTime += strlen(cg.captionText[1]); // strlen is also good for MBCS in this instance, since it's for timing
|
|||
|
}
|
|||
|
cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);
|
|||
|
|
|||
|
cg.scrollTextTime = 0; // No scrolling during captions
|
|||
|
|
|||
|
//Echo to console in dev mode
|
|||
|
if ( cg_developer.integer )
|
|||
|
{
|
|||
|
Com_Printf( "%s\n", cg.captionText[0] ); // ste: was [i], but surely sentence 0 is more useful than last?
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CG_DrawCaptionText(void)
|
|||
|
{
|
|||
|
int i;
|
|||
|
int x, y, w;
|
|||
|
int holdTime;
|
|||
|
|
|||
|
if ( !cg.captionTextTime )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
|
|||
|
|
|||
|
if (cg_skippingcin.value != 0.0f)
|
|||
|
{
|
|||
|
cg.captionTextTime = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( cg.captionNextTextTime < cg.time )
|
|||
|
{
|
|||
|
cg.captionTextCurrentLine += 2;
|
|||
|
|
|||
|
if (cg.captionTextCurrentLine >= cg.scrollTextLines)
|
|||
|
{
|
|||
|
cg.captionTextTime = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
holdTime = strlen(cg.captionText[cg.captionTextCurrentLine]);
|
|||
|
if (cg.scrollTextLines >= cg.captionTextCurrentLine)
|
|||
|
{
|
|||
|
// ( strlen is also good for MBCS in this instance, since it's for timing -ste)
|
|||
|
//
|
|||
|
holdTime += strlen(cg.captionText[cg.captionTextCurrentLine + 1]);
|
|||
|
}
|
|||
|
|
|||
|
cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);//50);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Give a color if one wasn't given
|
|||
|
if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) &&
|
|||
|
(textcolor_caption[2] == 0) && (textcolor_caption[3] == 0))
|
|||
|
{
|
|||
|
Vector4Copy( colorTable[CT_WHITE], textcolor_caption );
|
|||
|
}
|
|||
|
|
|||
|
cgi_R_SetColor(textcolor_caption);
|
|||
|
|
|||
|
// Set Y of the first line (varies if only printing one line of text)
|
|||
|
// (this all works, please don't mess with it)
|
|||
|
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale));
|
|||
|
const bool bPrinting2Lines = !!(cg.captionText[ cg.captionTextCurrentLine+1 ][0]);
|
|||
|
y = cg.captionTextY - ( (float)fontHeight * (bPrinting2Lines ? 1 : 0.5f)); // captionTextY was a centered Y pos, not a top one
|
|||
|
y -= cgi_Language_IsAsian() ? 0 : 4;
|
|||
|
|
|||
|
for (i= cg.captionTextCurrentLine;i< cg.captionTextCurrentLine + 2;++i)
|
|||
|
{
|
|||
|
w = cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale);
|
|||
|
if (w)
|
|||
|
{
|
|||
|
x = (SCREEN_WIDTH-w) / 2;
|
|||
|
cgi_R_Font_DrawString(x, y, cg.captionText[i], textcolor_caption, cgs.media.qhFontMedium, -1, fFontScale);
|
|||
|
y += fontHeight;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cgi_R_SetColor( NULL );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
===============================================================================
|
|||
|
|
|||
|
SCROLL TEXT
|
|||
|
|
|||
|
===============================================================================
|
|||
|
|
|||
|
CG_ScrollText - split text up into seperate lines
|
|||
|
|
|||
|
'str' arg is StripEd string reference, eg "CREDITS_RAVEN"
|
|||
|
|
|||
|
*/
|
|||
|
int giScrollTextPixelWidth = SCREEN_WIDTH;
|
|||
|
void CG_ScrollText( const char *str, int iPixelWidth )
|
|||
|
{
|
|||
|
const char *s,*holds;
|
|||
|
int i;//, len;//, numChars;
|
|||
|
|
|||
|
giScrollTextPixelWidth = iPixelWidth;
|
|||
|
|
|||
|
// first, ask the strlen of the final string...
|
|||
|
//
|
|||
|
i = cgi_SP_GetStringTextString( str, NULL,0 );
|
|||
|
//ensure we found a match
|
|||
|
if (!i)
|
|||
|
{
|
|||
|
#ifndef FINAL_BUILD
|
|||
|
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
//
|
|||
|
// malloc space to hold it...
|
|||
|
//
|
|||
|
char *psText = (char *) cgi_Z_Malloc( i+1, TAG_STRING );
|
|||
|
//
|
|||
|
// now get the string...
|
|||
|
//
|
|||
|
i = cgi_SP_GetStringTextString( str, psText, i+1 );
|
|||
|
//ensure we found a match
|
|||
|
if (!i)
|
|||
|
{
|
|||
|
assert(0); // should never get here now, but wtf?
|
|||
|
cgi_Z_Free(psText);
|
|||
|
#ifndef FINAL_BUILD
|
|||
|
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
cg.scrollTextTime = cg.time;
|
|||
|
cg.printTextY = SCREEN_HEIGHT;
|
|||
|
cg.scrollTextLines = 1;
|
|||
|
|
|||
|
s = psText;
|
|||
|
i = 0;
|
|||
|
holds = s;
|
|||
|
|
|||
|
const char *psBestLineBreakSrcPos = s;
|
|||
|
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
|
|||
|
while( *s )
|
|||
|
{
|
|||
|
psLastGood_s = s;
|
|||
|
|
|||
|
// read letter...
|
|||
|
//
|
|||
|
qboolean bIsTrailingPunctuation;
|
|||
|
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(&s, &bIsTrailingPunctuation);
|
|||
|
|
|||
|
// concat onto string so far...
|
|||
|
//
|
|||
|
if (uiLetter > 255)
|
|||
|
{
|
|||
|
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c",uiLetter & 0xFF));
|
|||
|
}
|
|||
|
|
|||
|
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
|
|||
|
//
|
|||
|
if (bIsTrailingPunctuation || uiLetter == ' ')
|
|||
|
{
|
|||
|
psBestLineBreakSrcPos = s;
|
|||
|
}
|
|||
|
|
|||
|
if (uiLetter == '\n')
|
|||
|
{
|
|||
|
// explicit new line...
|
|||
|
//
|
|||
|
cg.printText[i][ strlen(cg.printText[i])-1 ] = '\0'; // kill the CR
|
|||
|
i++;
|
|||
|
holds = s;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
else
|
|||
|
if ( cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f) >= iPixelWidth)
|
|||
|
{
|
|||
|
// reached screen edge, so cap off string at bytepos after last good position...
|
|||
|
//
|
|||
|
if (psBestLineBreakSrcPos == holds)
|
|||
|
{
|
|||
|
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
|
|||
|
// since it doesn't have a single space or punctuation mark right the way across one line
|
|||
|
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
|
|||
|
// string into this function while the game was running in english (which should NEVER happen
|
|||
|
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
|
|||
|
// might have no punctuation at all, so...
|
|||
|
//
|
|||
|
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
|
|||
|
}
|
|||
|
|
|||
|
cg.printText[i][ psBestLineBreakSrcPos - holds ] = '\0';
|
|||
|
holds = s = psBestLineBreakSrcPos;
|
|||
|
i++;
|
|||
|
cg.scrollTextLines++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cg.captionTextTime = 0; // No captions during scrolling
|
|||
|
cgi_Z_Free(psText);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// draws using [textcolor_scroll]...
|
|||
|
//
|
|||
|
#define SCROLL_LPM (1/50.0) // 1 line per 50 ms
|
|||
|
void CG_DrawScrollText(void)
|
|||
|
{
|
|||
|
char *start;
|
|||
|
int i;
|
|||
|
int x,y;
|
|||
|
const int fontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f)); // taiwanese needs 1.5 fontheight spacing
|
|||
|
|
|||
|
if ( !cg.scrollTextTime )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
cgi_R_SetColor( textcolor_scroll );
|
|||
|
|
|||
|
y = cg.printTextY - (cg.time - cg.scrollTextTime) * SCROLL_LPM;
|
|||
|
|
|||
|
// cgi_R_Font_DrawString(320, 200, va("Scrolltext printing @ %d",y), colorTable[CT_LTGOLD1], cgs.media.qhFontMedium, -1, 1.0f);
|
|||
|
|
|||
|
// See if text has finished scrolling off screen
|
|||
|
if ((y + cg.scrollTextLines * fontHeight) < 1)
|
|||
|
{
|
|||
|
cg.scrollTextTime = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
for (i=0;i<cg.scrollTextLines;++i)
|
|||
|
{
|
|||
|
|
|||
|
// Is this line off top of screen?
|
|||
|
if ((y + ((i +1) * fontHeight)) < 1)
|
|||
|
{
|
|||
|
y += fontHeight;
|
|||
|
continue;
|
|||
|
}
|
|||
|
// or past bottom of screen?
|
|||
|
else if (y > SCREEN_HEIGHT)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
start = cg.printText[i];
|
|||
|
|
|||
|
// w = cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f);
|
|||
|
// if (w)
|
|||
|
{
|
|||
|
x = (SCREEN_WIDTH - giScrollTextPixelWidth) / 2;
|
|||
|
cgi_R_Font_DrawString(x,y, cg.printText[i], textcolor_scroll, cgs.media.qhFontMedium, -1, 1.0f);
|
|||
|
y += fontHeight;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cgi_R_SetColor( NULL );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
===============================================================================
|
|||
|
|
|||
|
CENTER PRINTING
|
|||
|
|
|||
|
===============================================================================
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
==============
|
|||
|
CG_CenterPrint
|
|||
|
|
|||
|
Called for important messages that should stay in the center of the screen
|
|||
|
for a few moments
|
|||
|
==============
|
|||
|
*/
|
|||
|
void CG_CenterPrint( const char *str, int y) {
|
|||
|
char *s;
|
|||
|
|
|||
|
// Find text to match the str given
|
|||
|
if (*str == '@')
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
i = cgi_SP_GetStringTextString( va("%s_%s", cgs.stripLevelName, str+1), cg.centerPrint, sizeof(cg.centerPrint) );
|
|||
|
if (!i)
|
|||
|
{
|
|||
|
Com_Printf ("CG_CenterPrint: bad key for precached Text '%s'\n",str);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
|
|||
|
}
|
|||
|
|
|||
|
cg.centerPrintTime = cg.time;
|
|||
|
cg.centerPrintY = y;
|
|||
|
|
|||
|
// count the number of lines for centering
|
|||
|
cg.centerPrintLines = 1;
|
|||
|
s = cg.centerPrint;
|
|||
|
while( *s ) {
|
|||
|
if (*s == '\n')
|
|||
|
cg.centerPrintLines++;
|
|||
|
s++;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
===================
|
|||
|
CG_DrawCenterString
|
|||
|
===================
|
|||
|
*/
|
|||
|
void CG_DrawCenterString( void )
|
|||
|
{
|
|||
|
char *start;
|
|||
|
int l;
|
|||
|
int x, y, w;
|
|||
|
float *color;
|
|||
|
|
|||
|
if ( !cg.centerPrintTime ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
|
|||
|
if ( !color ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if((textcolor_center[0] == 0) && (textcolor_center[1] == 0) &&
|
|||
|
(textcolor_center[2] == 0) && (textcolor_center[3] == 0))
|
|||
|
{
|
|||
|
Vector4Copy( colorTable[CT_WHITE], textcolor_center );
|
|||
|
}
|
|||
|
|
|||
|
start = cg.centerPrint;
|
|||
|
|
|||
|
const int fontHeight = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
|
|||
|
y = cg.centerPrintY - (cg.centerPrintLines * fontHeight) / 2;
|
|||
|
|
|||
|
while ( 1 ) {
|
|||
|
char linebuffer[1024];
|
|||
|
|
|||
|
// this is kind of unpleasant when dealing with MBCS, but...
|
|||
|
//
|
|||
|
const char *psString = start;
|
|||
|
int iOutIndex = 0;
|
|||
|
for ( l = 0; l < 40; l++ ) {
|
|||
|
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(&psString);
|
|||
|
if (!uiLetter || uiLetter == '\n'){
|
|||
|
break;
|
|||
|
}
|
|||
|
if (uiLetter > 255)
|
|||
|
{
|
|||
|
linebuffer[iOutIndex++] = uiLetter >> 8;
|
|||
|
linebuffer[iOutIndex++] = uiLetter & 0xFF;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
linebuffer[iOutIndex++] = uiLetter & 0xFF;
|
|||
|
}
|
|||
|
}
|
|||
|
linebuffer[iOutIndex++] = '\0';
|
|||
|
|
|||
|
w = cgi_R_Font_StrLenPixels(linebuffer, cgs.media.qhFontMedium, 1.0f);
|
|||
|
|
|||
|
x = ( SCREEN_WIDTH - w ) / 2;
|
|||
|
|
|||
|
cgi_R_Font_DrawString(x,y,linebuffer, textcolor_center, cgs.media.qhFontMedium, -1, 1.0f);
|
|||
|
|
|||
|
y += fontHeight;
|
|||
|
|
|||
|
while ( *start && ( *start != '\n' ) ) {
|
|||
|
start++;
|
|||
|
}
|
|||
|
if ( !*start ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
start++;
|
|||
|
}
|
|||
|
|
|||
|
}
|