1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447 |
- /*=============================================================================
- Name : Subtitle.c
- Purpose : Functions for printing subtitles in time with the speech
- Created 2/3/1999 by lmoloney
- Copyright Relic Entertainment, Inc. All rights reserved.
- =============================================================================*/
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <stdio.h>
- #include <string.h>
- #include "glinc.h"
- #include "debug.h"
- #include "memory.h"
- #include "font.h"
- #include "color.h"
- #include "statscript.h"
- #include "font.h"
- #include "fontReg.h"
- #include "main.h"
- #include "key.h"
- #include "universe.h"
- #include "teams.h"
- #include "mainrgn.h"
- #include "sensors.h"
- #include "nis.h"
- #include "render.h"
- #include "fereg.h"
- #include "strings.h"
- #include "subtitle.h"
- /*=============================================================================
- Data:
- =============================================================================*/
- //strings for testing
- #if SUB_MODULE_TEST
- char *subTestSubtitle[NINEKEY - ONEKEY + 1] =
- {
- "A short string",
- "This page was originally made to tell what I saw and what had occured to me during my 1994-1995 trip to the U.S. Amundsen-Scott South Pole Station. However the process of writing the text and preparing the pictures took so much time that I had to halt for a while so that I can pass all those courses that I need in order to stay in the Graduate School. The result is that now I have come back from the second (now the third, fourth .. ) trip which I have some thing new to tell, but I still have unfinished first-year stuff to write. So here are some stories from the 1995 trip and writings based on later ones. \n\nPhotographic notes: Most photographic information are not directly put in the text. A lot of pictures are not serious shots. Here are some photographic related information. #n#nView them the right way: There are a lot of pictures in my web pages. If you find pictures on your screen too dark or too bright, probably you need to adjust gamma of your monitor. I created all images that are used here with the gamma set to 2.2.The best viewing environment is when there is no light directly shining on your screen. Adjust your monitor so that the center block in this image is indistinguishable against the grey part when you look at it from 6 to 8 feet away; and the border is as dark as possible. This will make your monitor have a closer presentation to mine. However I was told it could be hard to achieve on Macs. \n\nAnyway, enjoy the stories! ",
- "A meduim-length string withSomeReallyLongWordsToReallyMessWithTheWordWrappingLogic.ThisOutghToMessThemUp!",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
- };
- real32 subTestSubtitleTime[NINEKEY - ONEKEY + 1] =
- {
- 1.0f,
- 12.0f,
- 2.0f,
- 1.0f,
- 1.0f,
- 1.0f,
- 1.0f,
- 1.0f,
- 1.0f
- };
- #endif //SUB_MODULE_TEST
- //semaphore for communicating between stream thread and main game thread
- HANDLE subSemaphore;
- //pointer to timer to use currently. May be different depending on NIS's etc.
- real32 *subTimeElapsed = NULL;
- //set to TRUE when a fleet intel event ends for the benifit of KAS
- sdword subMessageEnded = FALSE;
- //retained copy of the player's race and all team colors so we know when to
- //re-load the speech icon textures.
- ShipRace subLastRace;
- trcolorinfo subLastTeamColors[TE_NumberPlayers];
- /*-----------------------------------------------------------------------------
- This next section of data should only be accessed under the protection
- of the previous semaphore.
- -----------------------------------------------------------------------------*/
- subnewsubtitle subNewSubtitles[SUB_NumberNewSubtitles];
- sdword subNumberNewSubtitles;
- #if SUB_VERBOSE_LEVEL >= 1
- sdword subMissedSubtitles;
- #endif
- /*-----------------------------------------------------------------------------
- Subtitle themes and printing regions.
- -----------------------------------------------------------------------------*/
- subregion subRegion[SUB_NumberRegions];
- subtheme subTheme[SUB_NumberThemes];
- /*-----------------------------------------------------------------------------
- General subtitle stuff
- -----------------------------------------------------------------------------*/
- real32 subScrollDwellStart = SUB_ScrollDwellStart;
- real32 subScrollDwellEnd = SUB_ScrollDwellEnd;
- real32 subScrollShortest = SUB_ScrollShortest;
- real32 subTitleShortest = SUB_TitleShortest;
- bool8 subCloseCaptionsEnabled = TRUE;
- /*-----------------------------------------------------------------------------
- Script parsing table for subtitle.script.
- -----------------------------------------------------------------------------*/
- void subThemeColorSet(char *directory, char *field, void *dataToFillIn);
- void subThemeDropshadowSet(char *directory, char *field, void *dataToFillIn);
- void subThemeMarginSet(char *directory, char *field, void *dataToFillIn);
- void subThemeFontSet(char *directory, char *field, void *dataToFillIn);
- void subThemeFadeSet(char *directory, char *field, void *dataToFillIn);
- void subRegionBoundsSet(char *directory, char *field, void *dataToFillIn);
- void subRegionScaleRezSet(char *directory, char *field, void *dataToFillIn);
- void subRegionMaxLinesSet(char *directory, char *field, void *dataToFillIn);
- void subThemeCentredSet(char *directory, char *field, void *dataToFillIn);
- void subThemePictureSet(char *directory, char *field, void *dataToFillIn);
- void subRegionIconOffsetSet(char *directory, char *field, void *dataToFillIn);
- scriptEntry subScriptTable[] =
- {
- //define theme parameters
- { "themeColor", subThemeColorSet, NULL},
- { "themeDropshadow", subThemeDropshadowSet, NULL},
- { "themeMargin", subThemeMarginSet, NULL},
- { "themeFont", subThemeFontSet, NULL},
- { "themeFadeIn", subThemeFadeSet, (void *)FALSE},
- { "themeFadeOut", subThemeFadeSet, (void *)TRUE},
- { "themePicture", subThemePictureSet, (void *)TRUE},
- { "themeCentred", subThemeCentredSet, (void *)TRUE},
- //define printing regions
- { "regionBounds", subRegionBoundsSet, NULL},
- { "regionScaleRez", subRegionScaleRezSet, NULL},
- { "regionMaxLines", subRegionMaxLinesSet, NULL},
- { "regionIconOffset", subRegionIconOffsetSet, NULL},
- //general stuff
- { "scrollDwellStart", scriptSetReal32CB, &subScrollDwellStart},
- { "scrollDwellEnd", scriptSetReal32CB, &subScrollDwellEnd},
- { "scrollShortest", scriptSetReal32CB, &subScrollShortest},
- { "subtitleShortest", scriptSetReal32CB, &subTitleShortest},
- { NULL, NULL, 0 }
- };
- /*=============================================================================
- Functions:
- =============================================================================*/
- /*-----------------------------------------------------------------------------
- Name : subAnyCardsOnScreen
- Description : returns TRUE if any subtitle cards are on the screen
- Inputs :
- Outputs :
- Return : returns TRUE if any subtitle cards are on the screen
- ----------------------------------------------------------------------------*/
- bool subAnyCardsOnScreen(void)
- {
- sdword index;
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- if (subRegion[index].bEnabled && subRegion[index].cardIndex > 0)
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeColorSet
- Description : Script-oarsing callback for setting a theme's color
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeColorSet(char *directory, char *field, void *dataToFillIn)
- {
- udword nScanned, iTheme, red, green, blue;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d %d %d", &iTheme, &red, &green, &blue);
- #if SUB_ERROR_CHECKING
- if (nScanned != 4)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 4, nScanned);
- }
- if (iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- if (red > UBYTE_Max || green > UBYTE_Max || blue > UBYTE_Max)
- {
- dbgFatalf(DBG_Loc, "Invalid color in string '%s'", field);
- }
- #endif
- subTheme[iTheme].textColor = colRGB(red, green, blue);
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeDropshadowSet
- Description : Script parse callback for setting the dropshadow flag on a theme.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeDropshadowSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme;
- char boolString[UBYTE_Max];
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %s", &iTheme, boolString);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- #endif
- subTheme[iTheme].bDropShadow = (bool8)scriptStringToBool(boolString);
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeCentredSet
- Description : Script parse callback for setting the centred flag on a theme.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeCentredSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme;
- char boolString[UBYTE_Max];
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %s", &iTheme, boolString);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- #endif
- subTheme[iTheme].bCentred = (bool8)scriptStringToBool(boolString);
- }
- /*-----------------------------------------------------------------------------
- Name : subThemePictureSet
- Description : Script parse callback for setting the picture for a theme.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemePictureSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme, iColorScheme;
- char pictureString[UBYTE_Max];
- char fileName[UBYTE_Max];
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d %s", &iTheme, &iColorScheme, pictureString);
- #if SUB_ERROR_CHECKING
- if (nScanned != 3)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 3, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- if (iColorScheme < 0 || iColorScheme >= TE_NumberPlayers)
- {
- dbgFatalf(DBG_Loc, "Invalid color scheme %d for '%s'", iColorScheme, pictureString);
- }
- #endif
- if (universe.players[0].race == R1)
- {
- strcpy(fileName, "SpeechIcons\\R1\\");
- }
- else
- {
- strcpy(fileName, "SpeechIcons\\R2\\");
- }
- strcat(fileName, pictureString);
- subTheme[iTheme].picture = trTextureRegister(fileName, &teColorSchemes[iColorScheme].textureColor, (void *)&subTheme[iTheme]);
- subTheme[iTheme].pictureColorScheme = iColorScheme;
- subTheme[iTheme].bPicture = TRUE;
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeMarginSet
- Description : Script parse callback for setting the margin of a theme
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeMarginSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme, margin;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d", &iTheme, &margin);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- #endif
- subTheme[iTheme].margin = margin;
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeFadeSet
- Description : Script parse callback for setting a fade time
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeFadeSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme;
- real32 fadeTime;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %f", &iTheme, &fadeTime);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- dbgAssert(fadeTime >= 0.0f && fadeTime < 80.0f);
- #endif
- if ((bool)dataToFillIn == FALSE)
- {
- subTheme[iTheme].fadeIn = fadeTime;
- }
- else
- {
- subTheme[iTheme].fadeOut = fadeTime;
- }
- }
- /*-----------------------------------------------------------------------------
- Name : subThemeFontSet
- Description : Script parse callback for setting the font of a theme
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subThemeFontSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iTheme;
- char fontName[1024];
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %s", &iTheme, fontName);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iTheme < 0 || iTheme >= SUB_NumberThemes)
- {
- dbgFatalf(DBG_Loc, "Invalid theme number %d. Must be between 0 and %d.", iTheme, SUB_NumberThemes);
- }
- #endif
- subTheme[iTheme].font = frFontRegister(fontName);
- }
- /*-----------------------------------------------------------------------------
- Name : subRegionBoundsSet
- Description : Script parse callback to set the bounds of a subtitle region.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subRegionBoundsSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iRegion;
- rectangle rect;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d %d %d %d", &iRegion, &rect.x0, &rect.y0, &rect.x1, &rect.y1);
- #if SUB_ERROR_CHECKING
- if (nScanned != 5)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 5, nScanned);
- }
- if (iRegion < 0 || iRegion >= SUB_NumberRegions)
- {
- dbgFatalf(DBG_Loc, "Invalid region number %d. Must be between 0 and %d.", iRegion, SUB_NumberRegions);
- }
- dbgAssert(rect.x0 < 640);
- dbgAssert(rect.x1 > 0);
- dbgAssert(rect.y0 < 480);
- dbgAssert(rect.y1 > 0);
- dbgAssert(rect.x0 < rect.x1);
- dbgAssert(rect.y0 < rect.y1);
- #endif
- subRegion[iRegion].defaultRect = rect;
- }
- /*-----------------------------------------------------------------------------
- Name : subRegionScaleRezSet
- Description : script parse callback to set the resolution scale flags for a
- subtitle region
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subRegionScaleRezSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iRegion;
- char bScaleRezX[UBYTE_Max], bScaleRezY[UBYTE_Max];
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %s %s", &iRegion, bScaleRezX, bScaleRezY);
- #if SUB_ERROR_CHECKING
- if (nScanned != 3)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 3, nScanned);
- }
- if (iRegion < 0 || iRegion >= SUB_NumberRegions)
- {
- dbgFatalf(DBG_Loc, "Invalid region number %d. Must be between 0 and %d.", iRegion, SUB_NumberRegions);
- }
- #endif
- subRegion[iRegion].bScaleRezX = (bool8)scriptStringToBool(bScaleRezX);
- subRegion[iRegion].bScaleRezY = (bool8)scriptStringToBool(bScaleRezY);
- }
- /*-----------------------------------------------------------------------------
- Name : subRegionMaxLinesSet
- Description : Script parse callback for setting the max number of lines in
- a region.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subRegionMaxLinesSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iRegion, maxLines;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d", &iRegion, &maxLines);
- #if SUB_ERROR_CHECKING
- if (nScanned != 2)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 2, nScanned);
- }
- if (iRegion < 0 || iRegion >= SUB_NumberRegions)
- {
- dbgFatalf(DBG_Loc, "Invalid region number %d. Must be between 0 and %d.", iRegion, SUB_NumberRegions);
- }
- dbgAssert(maxLines > 0 && maxLines < UDWORD_Max);
- #endif
- subRegion[iRegion].numberCards = maxLines;
- }
- /*-----------------------------------------------------------------------------
- Name : subRegionIconOffsetSet
- Description : Script parse callback for setting the speech icon offset (if any) for the region
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subRegionIconOffsetSet(char *directory, char *field, void *dataToFillIn)
- {
- sdword nScanned, iRegion, x, y;
- RemoveCommasFromString(field);
- nScanned = sscanf(field, "%d %d %d", &iRegion, &x, &y);
- #if SUB_ERROR_CHECKING
- if (nScanned != 3)
- {
- dbgFatalf(DBG_Loc, "Wrong number of fields in '%s': expected %d found %d", field, 3, nScanned);
- }
- if (iRegion < 0 || iRegion >= SUB_NumberRegions)
- {
- dbgFatalf(DBG_Loc, "Invalid region number %d. Must be between 0 and %d.", iRegion, SUB_NumberRegions);
- }
- #endif
- subRegion[iRegion].iconOffsetX = x;
- subRegion[iRegion].iconOffsetY = y;
- }
- /*-----------------------------------------------------------------------------
- Name : subTextureInfoRemember
- Description : Remembers the player's race and team color information.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subTextureInfoRemember(void)
- {
- sdword index;
- subLastRace = universe.players[0].race;
- for (index = 0; index < TE_NumberPlayers; index++)
- {
- subLastTeamColors[index] = teColorSchemes[index].textureColor;
- }
- }
- /*-----------------------------------------------------------------------------
- Name : subRegionsRescale
- Description : Rescales all region rectangles for a new screen resolution.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subRegionsRescale(void)
- {
- sdword index;
- sdword diffX = (MAIN_WindowWidth - 640) / 2;
- sdword diffY = (MAIN_WindowHeight - 480) / 2;
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- if (subRegion[index].defaultRect.x1 != 0)
- { //if a region was defined
- if (subRegion[index].bScaleRezX)
- { //if x-scale in resolution
- subRegion[index].rect.x0 = subRegion[index].defaultRect.x0 * MAIN_WindowWidth / 640;
- subRegion[index].rect.x1 = subRegion[index].defaultRect.x1 * MAIN_WindowWidth / 640;
- }
- else
- {
- subRegion[index].rect.x0 = subRegion[index].defaultRect.x0 + diffX;
- subRegion[index].rect.x1 = subRegion[index].defaultRect.x1 + diffX;
- }
- if (subRegion[index].bScaleRezY)
- { //if y-scale in resolution
- subRegion[index].rect.y0 = subRegion[index].defaultRect.y0 * MAIN_WindowHeight / 480;
- subRegion[index].rect.y1 = subRegion[index].defaultRect.y1 * MAIN_WindowHeight / 480;
- }
- else
- {
- subRegion[index].rect.y0 = subRegion[index].defaultRect.y0 + diffY;
- subRegion[index].rect.y1 = subRegion[index].defaultRect.y1 + diffY;
- }
- }
- }
- }
- /*-----------------------------------------------------------------------------
- Name : subStartup
- Description : Starts the subtitle module, reading in the subtitle scripts
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subStartup(void)
- {
- sdword index;
- #if SUB_VERBOSE_LEVEL >= 1
- dbgMessage("\nStartup subtitle module");
- #endif
- subNumberNewSubtitles = 0;
- #if SUB_VERBOSE_LEVEL >= 1
- subMissedSubtitles = 0;
- #endif
- subSemaphore = CreateSemaphore(NULL, 1, 1, SUB_SemaphoreName);
- dbgAssert(subSemaphore != NULL);
- memset(&subRegion, 0, sizeof(subRegion)); //clear out the themes and printing regions
- memset(&subTheme, 0, sizeof(subTheme));
- scriptSet(NULL, SUB_ScriptFile, subScriptTable); //load in the script file
- //post-setup of the output regions
- subRegionsRescale();
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- if (subRegion[index].defaultRect.x1 != 0)
- { //if a region was defined
- dbgAssert(subRegion[index].numberCards > 0 && subRegion[index].numberCards < UWORD_Max);
- subRegion[index].card = memAlloc(sizeof(subcard) * subRegion[index].numberCards, "subTitleCards", NonVolatile);
- subRegion[index].bEnabled = TRUE;
- subRegion[index].bAborted = FALSE;
- }
- }
- subRegion[STR_CloseCaptioned].bEnabled = subCloseCaptionsEnabled;//special case because these are enabled with a command-line switch
- subTextureInfoRemember();
- }
- /*-----------------------------------------------------------------------------
- Name : subReset
- Description : To be called between levels, clears out any leftover subtitles
- from all regions.
- Inputs : void
- Outputs :
- Return : void
- ----------------------------------------------------------------------------*/
- void subReset(void)
- {
- sdword index;
- //clear all text cards out of all regions
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- subRegion[index].cardIndex = 0;
- }
- }
- /*-----------------------------------------------------------------------------
- Name : subTexturesReset
- Description : Resets the subtitle textures, re-coloring and ensureing the
- correct race of textures is loaded.
- Inputs :
- Outputs :
- Return : void
- ----------------------------------------------------------------------------*/
- void subTexturesReset(void)
- {
- sdword index;
- char fileName[UBYTE_Max];
- texreg *reg;
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- subRegion[index].cardIndex = 0;
- }
- //clear all text cards out of all regions
- for (index = 0; index < SUB_NumberThemes; index++)
- {
- //recolorize the textures
- #if TR_NIL_TEXTURE
- if (subTheme[index].bPicture && (!GLOBAL_NO_TEXTURES))
- #else
- if (subTheme[index].bPicture)
- #endif
- {
- //trTextureColorsUpdate(subTheme[index].picture, &teColorSchemes[subTheme[index].pictureColorScheme].textureColor);
- //replace the image with the image for the correct race.
- reg = trStructureGet(subTheme[index].picture);
- strcpy(fileName, reg->fileName);
- if (universe.players[0].race == R1)
- {
- //Name if texture is:"SpeechIcons\RX\BlahBlah"
- // replace this number: ^
- #define SUB_NUMBER_INDEX 13
- fileName[SUB_NUMBER_INDEX] = '1';
- }
- else
- {
- fileName[SUB_NUMBER_INDEX] = '2';
- }
- if (universe.players[0].race != subLastRace ||
- memcmp(&subLastTeamColors[subTheme[index].pictureColorScheme], &teColorSchemes[subTheme[index].pictureColorScheme].textureColor, sizeof(trcolorinfo)))
- {
- //trTextureUnregister(subTheme[index].picture);
- trRegisterRemoval(subTheme[index].picture);
- subTheme[index].picture = trTextureRegister(fileName, &teColorSchemes[subTheme[index].pictureColorScheme].textureColor, (void *)&subTheme[index]);
- }
- }
- }
- subTextureInfoRemember();
- }
- /*-----------------------------------------------------------------------------
- Name : subShutdown
- Description : Shuts down subtitle module, freeing memory as needed.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subShutdown(void)
- {
- sdword index;
- #if SUB_VERBOSE_LEVEL >= 1
- dbgMessage("\nShutdown subtitle module");
- #endif
- CloseHandle(subSemaphore);
- //free any allocated memory
- for (index = 0; index < SUB_NumberRegions; index++)
- {
- if (subRegion[index].card != NULL)
- {
- memFree(subRegion[index].card);
- }
- }
- }
- /*-----------------------------------------------------------------------------
- Name : subStringsChop
- Description : Chop up a long string to make it fit in the specified region
- using the specified type theme.
- Inputs : region - screen size to print into
- font - what font to print in?
- longLength - length of string we're chopping up.
- longString - string we are to be chopping up
- Outputs : chopBuffer - buffer they'll be chopped up into (in case
- NULL-terminators need to be added)
- choppedStrings - list of pointers to chopped lines.
- Return : number of chopped strings
- ----------------------------------------------------------------------------*/
- sdword subStringsChop(rectangle *rect, fonthandle font, sdword longLength, char *longString, char *chopBuffer, char **choppedStrings)
- {
- sdword nChopped = 0;
- sdword nBytesUsed = 0;
- sdword width = rect->x1 - rect->x0;
- char *chopStart, *chopEnd, *nextChopStart, *wrapPtr;
- fonthandle current = fontCurrentGet();
- sdword state;
- fontMakeCurrent(font);
- chopStart = longString;
- // while (chopStart - longString < longLength)
- // { //while there is still some string to chop up
- for (chopEnd = chopStart; *chopStart != 0; chopEnd++)
- { //find the string to fit on this line
- //see if there is a newline or end-of-string
- if ((chopEnd[0] == '#' || chopEnd[0] == '\\') && chopEnd[1] == 'n')
- { //"#n" or "\\n" - newline
- for (nextChopStart = chopEnd + 2; *nextChopStart; nextChopStart++)
- { //find next non-whitespace, non-newline character
- if (!strchr(" \t\n\r", *nextChopStart))
- {
- break;
- }
- }
- }
- else if (chopEnd[0] == '\n' || chopEnd[0] == '\r')
- { // '\n' or '\r'
- for (nextChopStart = chopEnd + 1; *nextChopStart; nextChopStart++)
- { //find next non-whitespace, non-newline character
- if (!strchr(" \t\r", *nextChopStart))
- {
- break;
- }
- }
- }
- else if (*chopEnd == 0)
- { //end of string
- nextChopStart = chopEnd; //last time through this loop
- }
- //!!! no support for dashes yet
- //note: this method of fontWidth'ing is rather inefficient because
- //fontWidth will continuously be parsing the same piece of string. Oh well.
- else if (fontWidthN(chopStart, chopEnd - chopStart + 1) > width)
- { //if string too long for the region it's in
- //scan backwards to find some whitespace to cut out
- state = 0; //search for whitespace state
- for (wrapPtr = chopEnd; wrapPtr > chopStart; wrapPtr--)
- {
- if (state == 0)
- { //search for whitespace state
- if (strchr("\t ", *wrapPtr))
- { //if this is whitespace
- state = 1; //in the whitespace state
- nextChopStart = wrapPtr + 1; //this will be start of next line
- }
- }
- else
- { //in the whitespace state
- if (!strchr("\t ", *wrapPtr))
- {
- wrapPtr++; //point to first of the whitespace
- break;
- }
- }
- }
- //did we find a whitespace break to wrap on?
- if (wrapPtr > chopStart)
- { //yes, we found some whitespace to wrap on
- chopEnd = wrapPtr;
- }
- else
- { //else no whitespace was found
- nextChopStart = chopEnd; //next line will start right at the character that went too long
- }
- }
- else
- { //still fits, keep going
- continue;
- }
- //now, the string from chopStart to chopEnd will be a line.
- memcpy(chopBuffer + nBytesUsed, chopStart, chopEnd - chopStart);//copy the string into the buffer
- chopBuffer[nBytesUsed + chopEnd - chopStart] = 0;//NULL-terminate it
- choppedStrings[nChopped] = chopBuffer + nBytesUsed;//set pointer to the string
- dbgAssert(nBytesUsed < SUB_SubtitleLength + SUB_MaxLinesPerSubtitle);
- dbgAssert(nChopped < SUB_MaxLinesPerSubtitle);
- nBytesUsed += chopEnd - chopStart + 1; //update usage count
- nChopped++; //and number of strings
- //chop the next line
- chopStart = nextChopStart; //set to start scanning next line
- chopEnd = chopStart - 1;
- }
- // }
- fontMakeCurrent(current); //restore the old font
- return(nChopped); //return number of strings chopped up
- }
- /*-----------------------------------------------------------------------------
- Name : subCreateScrollyLines
- Description : Compute scroll and fade information for these strings.
- Inputs : region - what scrolly text region we're adding them to
- theme - what type theme we're using
- nStrings - number of strings we're adding
- strings - array of string pointers
- speechTime - length of speech event, in seconds
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subCreateScrollyLines(subregion *region, subtheme *theme, sdword nStrings, char **strings, real32 speechTime)
- {
- sdword index, linesToScroll, linesInRegion;
- sdword x, y;
- real32 scrollDistance;
- real32 scrollDuration;
- real32 delay, duration, scrollSpeed;
- real32 totalHeight;
- fonthandle fhSave = fontCurrentGet();
- fontMakeCurrent(theme->font);
- //figure out how far we'll have to scroll
- linesInRegion = (region->rect.y1 - region->rect.y0) / (fontHeight(" ") + SUB_InterlineSpacing);
- linesToScroll = nStrings - linesInRegion;
- totalHeight = (real32)(nStrings * (fontHeight(" ") + SUB_InterlineSpacing));
- scrollDistance = (real32)(linesToScroll * (fontHeight(" ") + SUB_InterlineSpacing));
- if (scrollDistance < 0.0f)
- {
- scrollDistance = 0.0f; //no scrolling needed
- }
- //how long will the scroll take?
- scrollDuration = speechTime - subScrollDwellStart - subScrollDwellEnd;
- //prep the region to scroll and display new text
- region->cardIndex = 0; //kill all previous cards
- if (scrollDistance > 0.0f)
- { //if we are to scroll at all
- region->textScrollStart = *subTimeElapsed + subScrollDwellStart;
- if (scrollDuration < 0)
- { //does that make 0 scroll time after all the dwelling?
- region->textScrollEnd = region->textScrollStart + subScrollShortest;//set some minimum scroll duration (only needed if somebody speaks really fast :)
- //!!! this is weird... fix me please!!!
- }
- else
- { //else we can scroll as normal
- region->textScrollEnd = region->textScrollStart + scrollDuration;
- }
- region->scrollDistance = -scrollDistance; //upward scroll is negative
- scrollSpeed = scrollDistance / (region->textScrollEnd - region->textScrollStart);
- }
- else
- { //else no scrolling
- scrollDuration = speechTime - subScrollDwellStart - subScrollDwellEnd;//just stay around for the duration of string
- region->scrollDistance = 0.0f;
- scrollSpeed = 0.0f; //should never be divided by anyhow
- }
- //now that we've computed our scroll time, let's actually add the lines
- y = 0;
- delay = 0.0f;
- for (index = 0; index < nStrings; index++)
- {
- #if SUB_ERROR_CHECKING
- if (index >= region->numberCards)
- {
- dbgFatalf(DBG_Loc, "Exceeded %d lines with the text '%s'.", region->numberCards, strings[index]);
- }
- #endif
- if (index < linesInRegion)
- { //if first lines to appear
- delay = 0; //make it show up right now
- if (index >= nStrings - linesInRegion)
- { //string is starting and ending string
- duration = subScrollDwellStart + subScrollDwellEnd + scrollDuration;
- }
- else
- { //else it'll scroll off the top
- duration = subScrollDwellStart + (real32)y / scrollSpeed;
- }
- }
- else
- { //else it will scroll onto screen
- //delay = (real32)y / scrollSpeed; //this is when it scrolls into view
- delay = subScrollDwellStart + (real32)((index - linesInRegion) * (fontHeight(" ") + SUB_InterlineSpacing)) / scrollSpeed;
- if (index >= nStrings - linesInRegion)
- { //if it is an ending string
- duration = (totalHeight - (real32)y) / scrollSpeed + subScrollDwellEnd;
- }
- else
- { //else it will scroll on the bottom and off the top
- duration = (real32)(linesInRegion * (fontHeight(" ") + SUB_InterlineSpacing)) / scrollSpeed;
- }
- }
- if (theme->bCentred)
- {
- x = ((region->rect.x1 - region->rect.x0) - fontWidth(strings[index])) / 2;
- }
- else
- {
- x = 0;
- }
- region->card[index].x = x;
- region->card[index].y = y;
- region->card[index].creationTime = *subTimeElapsed + delay;
- region->card[index].duration = duration;
- region->card[index].fadeIn = theme->fadeIn;
- region->card[index].fadeOut = theme->fadeOut;
- region->card[index].c = theme->textColor;
- region->card[index].bDropShadow = theme->bDropShadow;
- region->card[index].font = theme->font;
- region->card[index].text = strings[index];
- y += fontHeight(" ") + SUB_InterlineSpacing; //move the next line down
- }
- region->cardIndex = index;
- dbgAssert(region->cardIndex < region->numberCards);
- if (theme->bPicture)
- {
- region->picture = theme->picture;
- }
- else
- {
- region->picture = TR_InvalidHandle;
- }
- fontMakeCurrent(fhSave);
- }
- /*-----------------------------------------------------------------------------
- Name : subCreateNonScrollyLines
- Description : Same as above, but the lines won't scroll; intead consecutive
- pages will fade in and out.
- Inputs :
- Outputs :
- Return :
- ----------------------------------------------------------------------------*/
- void subCreateNonScrollyLines(subregion *region, subtheme *theme, sdword nStrings, char **strings, real32 speechTime)
- {
- sdword index, linesInRegion;
- sdword x, y;
- real32 delay;
- sdword nPages, iPage;
- real32 timePerPage;
- fonthandle fhSave = fontCurrentGet();
- dbgAssert(nStrings != 0);
- fontMakeCurrent(theme->font);
- speechTime = max(speechTime, subTitleShortest);
- //prep the region
- region->cardIndex = 0; //kill all previous cards
- region->scrollDistance = 0;
- //figure out how many pages there will be
- linesInRegion = (region->rect.y1 - region->rect.y0) / (fontHeight(" ") + SUB_InterlineSpacing);
- if (linesInRegion == 0)
- {
- linesInRegion = 1;
- }
- nPages = nStrings / linesInRegion;
- if (nStrings % linesInRegion)
- {
- nPages++;
- }
- timePerPage = speechTime / (real32)nPages;
- //now that we've computed our scroll time, let's actually add the lines
- for (index = 0; index < nStrings; index++)
- {
- #if SUB_ERROR_CHECKING
- if (index >= region->numberCards)
- {
- dbgFatalf(DBG_Loc, "Exceeded %d lines with the text '%s'.", region->numberCards, strings[index]);
- }
- #endif
- if (index < linesInRegion)
- { //if first lines to appear
- delay = 0; //make it show up right now
- }
- else
- { //else it will scroll onto screen
- iPage = index / linesInRegion; //what page am I on?
- delay = (real32)iPage * timePerPage;
- }
- if (theme->bCentred)
- {
- x = ((region->rect.x1 - region->rect.x0) - fontWidth(strings[index])) / 2;
- }
- else
- {
- x = 0;
- }
- y = (index % linesInRegion) * (fontHeight(" ") + SUB_InterlineSpacing);
- region->card[index].x = x;
- region->card[index].y = y;
- region->card[index].creationTime = *subTimeElapsed + delay;
- region->card[index].duration = timePerPage;
- region->card[index].fadeIn = theme->fadeIn;
- region->card[index].fadeOut = theme->fadeOut;
- region->card[index].c = theme->textColor;
- region->card[index].bDropShadow = theme->bDropShadow;
- region->card[index].font = theme->font;
- region->card[index].text = strings[index];
- }
- region->cardIndex = index;
- dbgAssert(region->cardIndex < region->numberCards);
- if (theme->bPicture)
- {
- region->picture = theme->picture;
- }
- else
- {
- region->picture = TR_InvalidHandle;
- }
- fontMakeCurrent(fhSave);
- region->bAborted = FALSE;
- }
- /*-----------------------------------------------------------------------------
- Name : subControlsScan
- Description : Scan for control escape sequences at the start of a string.
- Inputs : start - start of string to parse
- Outputs : region - region pointer to modify on #r<n> or #c
- theme - theme pointer to modify on #t<n> or #c
- bContinuous - set to TRUE on \+ or #+
- Return : start of actual string
- ----------------------------------------------------------------------------*/
- char *subControlsScan(char *start, subregion **region, subtheme **theme, bool8 *bContinuous)
- {
- sdword scannedIndex;
- while ((*start == '#' || *start == '\\') && *start != 0)
- {
- start++;
- switch (*start)
- {
- case '+':
- *bContinuous = TRUE;
- break;
- case 'r':
- start++;
- scannedIndex = *start - '0';
- dbgAssert(scannedIndex >= 0 && scannedIndex < SUB_NumberRegions);
- *region = &subRegion[scannedIndex];
- break;
- case 't':
- start++;
- if (*start >= '0' && *start <= '9')
- {
- scannedIndex = *start - '0';
- }
- else
- {
- scannedIndex = 0xa + *start - 'a';
- }
- dbgAssert(scannedIndex >= 0 && scannedIndex < SUB_NumberThemes);
- *theme = &subTheme[scannedIndex];
- break;
- case 'c':
- *region = &subRegion[STR_CloseCaptioned];
- *theme = &subTheme[STT_CloseCaptioned];
- break;
- #if SUB_ERROR_CHECKING
- default:
- dbgFatalf(DBG_Loc, "Invalid format character '%c' in '%s'", *start, start);
- #endif
- }
- start++;
- }
- return(start);
- }
- /*-----------------------------------------------------------------------------
- Name : subTitlesUpdate
- Description : Called every frame, this function updates subtitles,
- handling asynchronous data transfer from the stream thread.
- Inputs : void
- Outputs :
- Return : void
- ----------------------------------------------------------------------------*/
- void subTitlesUpdate(void)
- {
- sdword lastActor, lastSpeechEvent, totalLength, index;
- real32 totalTime;
- char fullNewStringBuffer[SUB_SubtitleLength], *fullNewString;
- char *choppedStrings[SUB_MaxLinesPerSubtitle];
- sdword nChoppedStrings;
- subregion *region;
- subtheme *theme;
- char *newString;
- bool8 bContinuous = FALSE, bContinuousIgnore;
- subTimeElapsed = &universe.totaltimeelapsed;
- #if SUB_MODULE_TEST
- {
- sdword key;
- for (key = ONEKEY; key <= NINEKEY; key++)
- {
- if (keyIsStuck(key))
- {
- keyClearSticky(key);
- if (subTestSubtitle[key - ONEKEY] != NULL)
- {
- subTitleAdd(0, subTestSubtitle[key - ONEKEY],
- strlen(subTestSubtitle[key - ONEKEY]),
- subTestSubtitleTime[key - ONEKEY]);
- }
- }
- }
- }
- #endif //SUB_MODULE_TEST
- WaitForSingleObject(subSemaphore, INFINITE); //make sure nobody else is working with the subtitle transfer buffers
- if (subNumberNewSubtitles > 0)
- {
- //because subtitles are delivered as a series of phrases, we must "stitch"
- //them together here. To do this, we compare the actor index of adjacent
- //new subtitles. We may later want to add a more accurate method of
- //concatenation, such as the speech event index.
- for (index = 0; index < subNumberNewSubtitles; index++)
- {
- strcpy(fullNewStringBuffer, subNewSubtitles[index].text);//intialize the concatenation string
- lastActor = subNewSubtitles[index].actor; //!!! this is the concatenation criteria
- region = &subRegion[STR_LetterboxBar]; //default region to print to
- theme = &subTheme[lastActor]; //default theme for this actor
- lastSpeechEvent = subNewSubtitles[index].speechEvent;
- fullNewString = subControlsScan(fullNewStringBuffer, ®ion, &theme, &bContinuous);
- totalLength = strlen(fullNewString);
- totalTime = subNewSubtitles[index].time;
- while (index + 1 < subNumberNewSubtitles &&
- subNewSubtitles[index + 1].actor == lastActor &&
- subNewSubtitles[index + 1].speechEvent == lastSpeechEvent)
- {
- index++;
- dbgAssert(totalLength + strlen(subNewSubtitles[index].text) < SUB_SubtitleLength);
- strcat(fullNewString, " "); //put a space between phrases.
- totalLength++;
- newString = subNewSubtitles[index].text;
- newString = subControlsScan(subNewSubtitles[index].text, ®ion, &theme, &bContinuousIgnore);
- strcpy(fullNewString + totalLength, newString);//append the new string
- totalLength = strlen(fullNewString); //get new total length
- totalTime += subNewSubtitles[index].time; //add all the times together
- }
- }
- #if SUB_VERBOSE_LEVEL >= 1
- dbgMessagef("\nSubtitle: actor %d says 0x%x '%s' (%.2f seconds).", lastActor, lastSpeechEvent, fullNewString, totalTime);
- #endif
- //now that we have a concatenated string; let's chop it up to wrap it around
- nChoppedStrings = subStringsChop(®ion->rect, theme->font, totalLength, fullNewString, region->chopBuffer, choppedStrings);
- #if SUB_VERBOSE_LEVEL >= 2
- if (nChoppedStrings > 1)
- {
- sdword index;
- dbgMessage("\nChopped up like this:");
- for (index = 0; index < nChoppedStrings; index++)
- {
- dbgMessagef("\n'%s'", choppedStrings[index]);
- }
- }
- #endif
- //string is all chopped up. Let's figure out how long/far to scroll and how long to stick around at the ends.
- if (strCurLanguage >= 1)
- { //use the standard timing for English; scrolly timing for forign
- subCreateScrollyLines(region, theme, nChoppedStrings, choppedStrings, totalTime);
- }
- else
- {
- subCreateNonScrollyLines(region, theme, nChoppedStrings, choppedStrings, totalTime);
- }
- region->bContinuousEvent = bContinuous;
- subNumberNewSubtitles = 0; //discard all processed new subtitles
- }
- //notify by debug message if we missed any phrases
- #if SUB_VERBOSE_LEVEL >= 1
- if (subMissedSubtitles > 0)
- {
- dbgMessagef("\nsubtitle: %d subtitles were missed.", subMissedSubtitles);
- subMissedSubtitles = 0;
- }
- #endif
- ReleaseSemaphore(subSemaphore, 1, NULL);
- }
- /*-----------------------------------------------------------------------------
- Name : subTitleAdd
- Description : Display a subtitle. This function called from the speech
- streamer when a speech event is streamed in.
- Inputs : actor - integer for an actor. This is used to determine where
- to print the speech even, plus fonts and whanot.
- speechEvnt - what speechEvent it was, straight out of speechevent.h
- text - text to print. Must be in proper language when passed
- here.
- length - length of text string, same as what you would get
- from a strlen() call.
- time - length of text
- Outputs :
- Return : ?
- ----------------------------------------------------------------------------*/
- sdword subTitleAdd(sdword actor, sdword speechEvent, char *text, sdword length, real32 time)
- {
- /*
- #if SUB_VERBOSE_LEVEL >= 1
- dbgMessagef("\nSubtitle: actor %d says '%s'.", actor, text);
- #endif
- */
- WaitForSingleObject(subSemaphore, INFINITE); //make sure nobody else is working with the subtitle transfer buffers
- if (subNumberNewSubtitles < SUB_NumberNewSubtitles - 1)
- {
- dbgAssert(length < SUB_SubtitleLength);
- subNewSubtitles[subNumberNewSubtitles].actor = actor;
- subNewSubtitles[subNumberNewSubtitles].speechEvent = speechEvent;
- subNewSubtitles[subNumberNewSubtitles].length = length;
- subNewSubtitles[subNumberNewSubtitles].time = time;
- //strcpy(subNewSubtitles[subNumberNewSubtitles].text, text);
- memcpy(subNewSubtitles[subNumberNewSubtitles].text, text, length);
- subNewSubtitles[subNumberNewSubtitles].text[length] = 0;
- subNumberNewSubtitles++;
- }
- else
- {
- #if SUB_VERBOSE_LEVEL >= 1
- subMissedSubtitles++;
- #endif
- }
- ReleaseSemaphore(subSemaphore, 1, NULL);
- return(OKAY);
- }
- /*-----------------------------------------------------------------------------
- Name : subTitlesDraw
- Description : Render the subtitles in a given region.
- Inputs : region - what region to render the subtitles for.
- Outputs : may delete one or more of the text cards in the region
- because they time out.
- Return :void
- ----------------------------------------------------------------------------*/
- void subTitlesDraw(subregion *region)
- {
- sdword index;
- fonthandle fhSave = fontCurrentGet();
- char *lineStart;//, *newLine;
- sdword x, y;
- subcard *card;
- color c;
- real32 fadeValue;
- udword multiplier;
- sdword scroll;
- real32 scrollFloat;
- #if SUB_VISUALIZE_REGION
- primRectSolid2(®ion->rect, colBlack);
- primRectOutline2(®ion->rect, 1, colWhite);
- #endif
- if ((!mrRenderMainScreen && !smFleetIntel) && region == &subRegion[STR_LetterboxBar])
- { //if some full screen gui is up
- rndTextureEnable(FALSE);
- glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
- glBegin(GL_QUADS);
- //top scissor part
- glVertex2f(primScreenToGLX(-1), primScreenToGLY(0));
- glVertex2f(primScreenToGLX(-1), primScreenToGLY(NIS_LetterHeight));
- glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(NIS_LetterHeight));
- glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(0));
- glEnd();
- //ferDrawBoxRegion(region->rect, ferMediumTextures, ferInternalGlow, NULL, FALSE);
- }
- //scroll the text, if applicable
- scroll = region->rect.y0;
- if (region->scrollDistance != 0.0f)
- { //if there is a scroll to go on
- if (*subTimeElapsed >= region->textScrollStart)
- { //if the scroll has started
- if (*subTimeElapsed <= region->textScrollEnd)
- { //if currently scrolling
- scrollFloat = region->scrollDistance * (*subTimeElapsed - region->textScrollStart) / (region->textScrollEnd - region->textScrollStart);
- }
- else
- { //else scroll has ended
- scrollFloat = region->scrollDistance;
- }
- scroll = (sdword)scrollFloat + region->rect.y0;
- }
- }
- for (index = 0, card = region->card; index < region->cardIndex; index++, card++)
- {
- if (*subTimeElapsed >= card->creationTime)
- { //if card has started up properly
- if (*subTimeElapsed - card->creationTime > card->duration)
- { //if card elapses
- // dbgMessagef("XXXX%10s", region->card[index].text);
- if (region->cardIndex > 1)
- {
- region->card[index] = region->card[region->cardIndex - 1];
- }
- region->cardIndex--;
- index--;
- card = ®ion->card[index];
- continue;
- }
- fontMakeCurrent(card->font);
- lineStart = card->text;
- //newLine = strstr(lineStart, NIS_NewLine);
- x = card->x + region->rect.x0;
- y = card->y + scroll;
- //scroll the text, if applicable
- /*
- if (card->scroll != 0.0f)
- { //if the text is scrolling
- y += (sdword)(card->scroll * //move to scrolled-to position
- (card->NIS->timeElapsed - card->creationTime) /
- card->duration);
- }
- */
- c = card->c;
- //fade the text in, if applicable
- if (card->fadeIn != 0.0f)
- {
- fadeValue = (*subTimeElapsed - card->creationTime) / card->fadeIn;
- if (fadeValue >= 1.0f)
- {
- card->fadeIn = 0.0f;
- fadeValue = 1.0f;
- }
- multiplier = (udword)(fadeValue * 256.0f);
- c = colRGB(colRed(c) * multiplier / 256, colGreen(c) * multiplier / 256, colBlue(c) * multiplier / 256);
- }
- //fade the the text out, if applicable
- if (card->fadeOut != 0.0f)
- { //if there's a fadeout
- if (*subTimeElapsed >= card->creationTime + card->duration - card->fadeOut)
- { //if it in the fading part
- fadeValue = (*subTimeElapsed - (card->creationTime + card->duration - card->fadeOut)) / card->fadeOut;
- fadeValue = max(0.0, 1.0f - fadeValue);
- multiplier = (udword)(fadeValue * 256.0f);
- c = colRGB(colRed(c) * multiplier / 256, colGreen(c) * multiplier / 256, colBlue(c) * multiplier / 256);
- // dbgMessagef("<0x%2x>", multiplier);
- }
- }
- //draw the text
- /*
- while (newLine != NULL)
- {
- fontPrintN(x, y, card->c, lineStart, newLine - lineStart);
- y += fontHeight(" ") + 1;
- x = card->margin;
- newLine += NIS_NewLineLength;
- lineStart = newLine;
- newLine = strstr(newLine, NIS_NewLine);
- }
- */
- if (card->bDropShadow)
- {
- fontShadowSet(FS_SE, colBlack);
- }
- fontPrint(x, y, c, lineStart);
- }
- }
- if (region->picture != TR_InvalidHandle)
- {
- rectangle rect;
- rect.x0 = region->rect.x0 + region->iconOffsetX;
- rect.y0 = region->rect.y0 + region->iconOffsetY;
- rect.x1 = rect.x0 + SUB_PictureWidth;
- rect.y1 = rect.y0 + SUB_PictureHeight;
- trMakeCurrent(region->picture);
- glDisable(GL_ALPHA_TEST);
- glEnable(GL_BLEND);
- if (RGLtype == SWtype)
- {
- glTexCoord2f((real32)(-rect.x0), (real32)(-rect.y1));
- glDrawPixels(SUB_PictureWidth, SUB_PictureHeight, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- }
- else
- {
- primRectSolidTextured2(&rect);
- }
- glDisable(GL_BLEND);
- }
- if (region->cardIndex == 0 && region == &subRegion[STR_LetterboxBar] && (!region->bAborted) && !region->bContinuousEvent)
- { //if all fleet intel/command cards expired
- subMessageEnded = 1;
- }
- fontShadowSet(FS_NONE, colBlack);
- fontMakeCurrent(fhSave);
- }
- /*-----------------------------------------------------------------------------
- Name : subTitlesFadeOut
- Description : Causes all remaining subtitles in a region to fade out over
- a specified period.
- Inputs : region - what region to fade teh subtitles out of
- fadeTime - duration of fadeout
- Outputs :
- Return : void
- ----------------------------------------------------------------------------*/
- void subTitlesFadeOut(subregion *region, real32 fadeTime)
- {
- sdword index;
- subcard *card;
- dbgAssert(region != NULL);
- for (index = 0, card = region->card; index < region->cardIndex; index++, card++)
- {
- if (card->creationTime > *subTimeElapsed)
- { //if card not started created yet
- card->creationTime = *subTimeElapsed;
- card->duration = -1; //make sure it never gets drawn
- continue;
- }
- if (*subTimeElapsed - card->creationTime + fadeTime < card->duration)
- { //if the card isn't going to fade out before the time specified on it's own
- card->fadeOut = fadeTime; //start fading it out
- card->duration = *subTimeElapsed - card->creationTime + fadeTime;//kill it when it's faded
- }
- }
- region->bAborted = TRUE;
- }
|