SWF_TextInstance.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "../renderer/Font.h"
  23. idSWFScriptObject_TextInstancePrototype textInstanceScriptObjectPrototype;
  24. idCVar swf_textScrollSpeed( "swf_textScrollSpeed", "80", CVAR_INTEGER, "scroll speed for text" );
  25. idCVar swf_textRndLetterSpeed( "swf_textRndLetterSpeed", "8", CVAR_INTEGER, "scroll speed for text" );
  26. idCVar swf_textRndLetterDelay( "swf_textRndLetterDelay", "100", CVAR_INTEGER, "scroll speed for text" );
  27. idCVar swf_textParagraphSpeed( "swf_textParagraphSpeed", "15", CVAR_INTEGER, "scroll speed for text" );
  28. idCVar swf_textParagraphInc( "swf_textParagraphInc", "1.3", CVAR_FLOAT, "scroll speed for text" );
  29. idCVar swf_subtitleExtraTime( "swf_subtitleExtraTime", "3500", CVAR_INTEGER, "time after subtitles vo is complete" );
  30. idCVar swf_subtitleEarlyTrans( "swf_subtitleEarlyTrans", "3500", CVAR_INTEGER, "early time out to switch the line" );
  31. idCVar swf_subtitleLengthGuess( "swf_subtitleLengthGuess", "10000", CVAR_INTEGER, "early time out to switch the line" );
  32. idCVar swf_textMaxInputLength( "swf_textMaxInputLength", "104", CVAR_INTEGER, "max number of characters that can go into the input line" );
  33. idCVar swf_textStrokeSize( "swf_textStrokeSize", "1.65f", CVAR_FLOAT, "size of font glyph stroke", 0.0f, 2.0f );
  34. idCVar swf_textStrokeSizeGlyphSpacer( "swf_textStrokeSizeGlyphSpacer", "1.5f", CVAR_FLOAT, "additional space for spacing glyphs using stroke" );
  35. /*
  36. ========================
  37. idSWFTextInstance::idSWFTextInstance
  38. ========================
  39. */
  40. idSWFTextInstance::idSWFTextInstance() {
  41. swf = NULL;
  42. }
  43. /*
  44. ========================
  45. idSWFTextInstance::~idSWFTextInstance
  46. ================== ======
  47. */
  48. idSWFTextInstance::~idSWFTextInstance() {
  49. scriptObject.SetText( NULL );
  50. scriptObject.Clear();
  51. scriptObject.Release();
  52. subtitleTimingInfo.Clear();
  53. }
  54. /*
  55. ========================
  56. idSWFTextInstance::Init
  57. ========================
  58. */
  59. void idSWFTextInstance::Init( idSWFEditText * _editText, idSWF * _swf ) {
  60. editText = _editText;
  61. swf = _swf;
  62. text = idLocalization::GetString( editText->initialText );
  63. lengthCalculated = false;
  64. variable = editText->variable;
  65. color = editText->color;
  66. visible = true;
  67. selectionStart = -1;
  68. selectionEnd = -1;
  69. scroll = 0;
  70. scrollTime = 0;
  71. maxscroll = 0;
  72. maxLines = 0;
  73. linespacing = 0;
  74. glyphScale = 1.0f;
  75. shiftHeld = false;
  76. tooltip = false;
  77. renderMode = SWF_TEXT_RENDER_NORMAL;
  78. generatingText = false;
  79. triggerGenerate = false;
  80. rndSpotsVisible = 0;
  81. textSpotsVisible = 0;
  82. startRndTime = 0;
  83. charMultiplier = 0;
  84. prevReplaceIndex = 0;
  85. scrollUpdate = false;
  86. ignoreColor = false;
  87. isSubtitle = false;
  88. subLength = 0;
  89. subAlign = 0;
  90. subUpdating = false;
  91. subCharStartIndex = 0;
  92. subNextStartIndex = 0;
  93. subCharEndIndex = 0;
  94. subDisplayTime = 0;
  95. subStartTime = -1;
  96. subSourceID = -1;
  97. subNeedsSwitch = false;
  98. subForceKill = false;
  99. subKillTimeDelay = 0;
  100. subSwitchTime = 0;
  101. subLastWordIndex = 0;
  102. subPrevLastWordIndex = 0;
  103. subInitialLine = true;
  104. textLength = 0;
  105. inputTextStartChar = 0;
  106. renderDelay = swf_textRndLetterDelay.GetInteger();
  107. needsSoundUpdate = false;
  108. useDropShadow = false;
  109. useStroke = false;
  110. strokeStrength = 1.0f;
  111. strokeWeight = swf_textStrokeSize.GetFloat();
  112. scriptObject.SetPrototype( &textInstanceScriptObjectPrototype );
  113. scriptObject.SetText( this );
  114. scriptObject.SetNoAutoDelete( true );
  115. }
  116. /*
  117. ========================
  118. idSWFTextInstance::GetTextLength
  119. ========================
  120. */
  121. float idSWFTextInstance::GetTextLength() {
  122. // CURRENTLY ONLY WORKS FOR SINGLE LINE TEXTFIELDS
  123. if ( lengthCalculated && variable.IsEmpty() ) {
  124. return textLength;
  125. }
  126. idStr txtLengthCheck = "";
  127. float len = 0.0f;
  128. if ( verify( swf != NULL ) ) {
  129. if ( !variable.IsEmpty() ) {
  130. idSWFScriptVar var = swf->GetGlobal( variable );
  131. if ( var.IsUndefined() ) {
  132. txtLengthCheck = text;
  133. } else {
  134. txtLengthCheck = var.ToString();
  135. }
  136. txtLengthCheck = idLocalization::GetString( txtLengthCheck );
  137. } else {
  138. txtLengthCheck = idLocalization::GetString( text );
  139. }
  140. const idSWFEditText * shape = editText;
  141. idSWFDictionaryEntry * fontEntry = swf->FindDictionaryEntry( shape->fontID, SWF_DICT_FONT );
  142. idSWFFont * swfFont = fontEntry->font;
  143. float width = fabs( shape->bounds.br.x - shape->bounds.tl.x );
  144. float postTrans = SWFTWIP( shape->fontHeight );
  145. const idFont * fontInfo = swfFont->fontID;
  146. float glyphScale = postTrans / 48.0f;
  147. int tlen = txtLengthCheck.Length();
  148. int index = 0;
  149. while ( index < tlen ) {
  150. scaledGlyphInfo_t glyph;
  151. fontInfo->GetScaledGlyph( glyphScale, txtLengthCheck.UTF8Char( index ), glyph );
  152. len += glyph.xSkip;
  153. if ( useStroke ) {
  154. len += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * strokeWeight * glyphScale );
  155. }
  156. if ( !( shape->flags & SWF_ET_AUTOSIZE ) && len >= width ) {
  157. len = width;
  158. break;
  159. }
  160. }
  161. }
  162. lengthCalculated = true;
  163. textLength = len;
  164. return textLength;
  165. }
  166. /*
  167. ========================
  168. idSWFTextInstance::StartParagraphText
  169. ========================
  170. */
  171. void idSWFTextInstance::StartParagraphText( int time ) {
  172. generatingText = true;
  173. textSpotsVisible = 0;
  174. randomtext = "";
  175. triggerGenerate = false;
  176. startRndTime = time;
  177. rndTime = time;
  178. rnd.SetSeed( time );
  179. prevReplaceIndex = 0;
  180. rndSpotsVisible = text.Length();
  181. indexArray.Clear();
  182. charMultiplier = 0;
  183. text = idLocalization::GetString( text );
  184. lengthCalculated = false;
  185. for( int index = 0; index < text.Length(); ++index ) {
  186. randomtext.Append( " " );
  187. indexArray.Append( index );
  188. }
  189. for( int index = 0; index < indexArray.Num(); ++index ) {
  190. int swapIndex = rnd.RandomInt( indexArray.Num() );
  191. int val = indexArray[index];
  192. indexArray[index] = indexArray[swapIndex];
  193. indexArray[swapIndex] = val;
  194. }
  195. }
  196. /*
  197. ========================
  198. idSWFTextInstance::GetParagraphText
  199. ========================
  200. */
  201. idStr idSWFTextInstance::GetParagraphText( int time ) {
  202. if ( triggerGenerate ) {
  203. return " ";
  204. } else if ( time - startRndTime < renderDelay ) {
  205. return " ";
  206. } else if ( generatingText ) {
  207. if ( time - rndTime >= renderDelay ) {
  208. rndTime = time;
  209. needsSoundUpdate = true;
  210. if ( prevReplaceIndex >= text.Length() ) {
  211. generatingText = false;
  212. return text;
  213. }
  214. randomtext[prevReplaceIndex] = text[prevReplaceIndex];
  215. prevReplaceIndex++;
  216. }
  217. } else {
  218. scrollUpdate = false;
  219. return text;
  220. }
  221. return randomtext;
  222. }
  223. /*
  224. ========================
  225. idSWFTextInstance::StartRandomText
  226. ========================
  227. */
  228. bool idSWFTextInstance::NeedsSoundPlayed() {
  229. if ( soundClip.IsEmpty() ) {
  230. return false;
  231. }
  232. return needsSoundUpdate;
  233. }
  234. /*
  235. ========================
  236. idSWFTextInstance::StartRandomText
  237. ========================
  238. */
  239. void idSWFTextInstance::StartRandomText( int time ) {
  240. generatingText = true;
  241. textSpotsVisible = 0;
  242. randomtext = "";
  243. triggerGenerate = false;
  244. startRndTime = time;
  245. rndTime = time;
  246. rnd.SetSeed( time );
  247. rndSpotsVisible = 0;
  248. text = idLocalization::GetString( text );
  249. lengthCalculated = false;
  250. for( int index = 0; index < text.Length(); ++index ) {
  251. if ( text[index] == ' ' ) {
  252. randomtext.Append( " " );
  253. } else {
  254. randomtext.Append( "." );
  255. rndSpotsVisible++;
  256. }
  257. }
  258. }
  259. /*
  260. ========================
  261. idSWFTextInstance::GetRandomText
  262. ========================
  263. */
  264. idStr idSWFTextInstance::GetRandomText( int time ) {
  265. if ( triggerGenerate ) {
  266. return " ";
  267. } else if ( time - startRndTime < renderDelay ) {
  268. return " ";
  269. } else if ( generatingText ) {
  270. if ( rndSpotsVisible > 0 ) {
  271. int waitTime = swf_textRndLetterSpeed.GetInteger();
  272. if ( randomtext.Length() >= 10 ) {
  273. waitTime = waitTime / 3;
  274. }
  275. if ( time - rndTime >= waitTime ) {
  276. rndTime = time;
  277. int spotIndex = rnd.RandomInt( rndSpotsVisible );
  278. int cIndex = 0;
  279. for( int c = 0; c < randomtext.Length(); ++ c ) {
  280. if ( c >= text.Length() ) {
  281. rndSpotsVisible = 0;
  282. break;
  283. }
  284. if ( randomtext[c] == '.' ) {
  285. cIndex++;
  286. }
  287. if ( cIndex == spotIndex ) {
  288. bool useCaps = false;
  289. if ( c - 1 >= 0 && text[c-1] == ' ' ) {
  290. useCaps = true;
  291. } else if ( c == 0 ) {
  292. useCaps = true;
  293. }
  294. if ( useCaps || renderMode == SWF_TEXT_RENDER_RANDOM_APPEAR_CAPS ) {
  295. randomtext[c] = rnd.RandomInt( 'Z' - 'A' ) + 'A';
  296. } else {
  297. randomtext[c] = rnd.RandomInt( 'z' - 'a' ) + 'a';
  298. }
  299. rndSpotsVisible--;
  300. if ( !soundClip.IsEmpty() ) {
  301. needsSoundUpdate = true;
  302. }
  303. break;
  304. }
  305. }
  306. }
  307. } else if ( rndSpotsVisible == 0 && textSpotsVisible < text.Length() ) {
  308. if ( textSpotsVisible >= randomtext.Length() ) {
  309. textSpotsVisible++;
  310. } else {
  311. if ( time - rndTime >= swf_textRndLetterSpeed.GetInteger() ) {
  312. rndTime = time;
  313. randomtext[textSpotsVisible] = text[textSpotsVisible];
  314. textSpotsVisible++;
  315. if ( !soundClip.IsEmpty() ) {
  316. needsSoundUpdate = true;
  317. }
  318. }
  319. }
  320. } else {
  321. return " ";
  322. }
  323. } else {
  324. return text;
  325. }
  326. if ( rndSpotsVisible == 0 && textSpotsVisible == text.Length() ) {
  327. generatingText = false;
  328. }
  329. return randomtext;
  330. }
  331. /*
  332. ==============================================
  333. SUBTITLE FUNCTIONALITY
  334. ==============================================
  335. */
  336. /*
  337. ==============================================
  338. idSWFTextInstance::SwitchSubtitleText
  339. ==============================================
  340. */
  341. void idSWFTextInstance::SwitchSubtitleText( int time ) {
  342. subNeedsSwitch = false;
  343. }
  344. void idSWFTextInstance::SetSubNextStartIndex( int value ) {
  345. subNextStartIndex = value;
  346. }
  347. /*
  348. ==============================================
  349. idSWFTextInstance::UpdateSubtitle
  350. ==============================================
  351. */
  352. bool idSWFTextInstance::UpdateSubtitle( int time ) {
  353. if ( subForceKillQueued ) {
  354. subForceKillQueued = false;
  355. subForceKill = true;
  356. subKillTimeDelay = time + swf_subtitleExtraTime.GetInteger();
  357. }
  358. if ( subUpdating && !subForceKill ) {
  359. if ( ( time >= subSwitchTime && !subNeedsSwitch ) || ( !subNeedsSwitch && subInitialLine ) ) {
  360. //idLib::Printf( "SWITCH TIME %d / %d \n", time, subSwitchTime );
  361. if ( subInitialLine && subtitleTimingInfo.Num() > 0 ) {
  362. if ( subStartTime == -1 ) {
  363. subStartTime = time - 600;
  364. }
  365. if ( time < subStartTime + subtitleTimingInfo[0].startTime ) {
  366. return true;
  367. } else {
  368. text = subtitleText;//subtitleText = "";
  369. subInitialLine = false;
  370. }
  371. }
  372. if ( subNextStartIndex + 1 >= text.Length( ) ) {
  373. subForceKillQueued = true;
  374. } else {
  375. subCharStartIndex = subNextStartIndex;
  376. //subtitleText.CopyRange( text, subCharStartIndex, subCharEndIndex );
  377. subNeedsSwitch = true;
  378. }
  379. }
  380. }
  381. if ( subForceKill ) {
  382. if ( time >= subKillTimeDelay ) {
  383. subForceKill = false;
  384. return false;
  385. }
  386. }
  387. return true;
  388. }
  389. /*
  390. ==============================================
  391. idSWFTextInstance::SubtitleComplete
  392. ==============================================
  393. */
  394. void idSWFTextInstance::SetSubEndIndex( int endChar, int time ) {
  395. subCharEndIndex = endChar;
  396. if ( subCharEndIndex + 1 >= text.Length() ) {
  397. LastWordChanged( subtitleTimingInfo.Num(), time );
  398. }
  399. }
  400. /*
  401. ==============================================
  402. idSWFTextInstance::SubtitleComplete
  403. ==============================================
  404. */
  405. void idSWFTextInstance::SubtitleComplete() {
  406. subInitialLine = true;
  407. subUpdating = false;
  408. isSubtitle = false;
  409. subNeedsSwitch = false;
  410. subCharDisplayTime = 0;
  411. subForceKillQueued = false;
  412. subForceKill = false;
  413. subKillTimeDelay = 0;
  414. subSwitchTime = 0;
  415. subLastWordIndex = 0;
  416. subPrevLastWordIndex = 0;
  417. subStartTime = -1;
  418. subSpeaker = "";
  419. subtitleText = "";
  420. text = "";
  421. subtitleTimingInfo.Clear();
  422. }
  423. /*
  424. ==============================================
  425. idSWFTextInstance::LastWordChanged
  426. ==============================================
  427. */
  428. void idSWFTextInstance::LastWordChanged( int wordCount, int time ) {
  429. if ( subPrevLastWordIndex + wordCount >= subtitleTimingInfo.Num() ) {
  430. subLastWordIndex = subtitleTimingInfo.Num() - 1;
  431. } else {
  432. subLastWordIndex = subPrevLastWordIndex + wordCount - 1;
  433. }
  434. if ( subStartTime == -1 ) {
  435. if ( subtitleTimingInfo.Num() > 0 ) {
  436. subStartTime = time + subtitleTimingInfo[0].startTime;
  437. } else {
  438. subStartTime = time;
  439. }
  440. }
  441. subSwitchTime = subStartTime + subtitleTimingInfo[subLastWordIndex].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  442. //idLib::Printf( "switchtime set 1 %d last word %d\n", subSwitchTime, subLastWordIndex );
  443. }
  444. /*
  445. ==============================================
  446. idSWFTextInstance::GetSubtitleBreak
  447. ==============================================
  448. */
  449. int idSWFTextInstance::GetApporoximateSubtitleBreak( int time ) {
  450. int wordIndex = subLastWordIndex;
  451. bool setSwitchTime = false;
  452. if ( subStartTime == -1 ) {
  453. subStartTime = time;
  454. }
  455. if ( time >= subSwitchTime ) {
  456. subPrevLastWordIndex = subLastWordIndex;
  457. for( int i = wordIndex; i < subtitleTimingInfo.Num(); ++i ) {
  458. if ( subtitleTimingInfo[i].forceBreak ) {
  459. if ( i + 1 < subtitleTimingInfo.Num() ) {
  460. subSwitchTime = subStartTime + subtitleTimingInfo[i+1].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  461. //idLib::Printf( "switchtime set 2 %d\n", subSwitchTime );
  462. subLastWordIndex = i;
  463. setSwitchTime = true;
  464. break;
  465. } else {
  466. subSwitchTime = subStartTime + subtitleTimingInfo[i].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  467. //idLib::Printf( "switchtime set 3 %d\n", subSwitchTime );
  468. subLastWordIndex = i;
  469. setSwitchTime = true;
  470. break;
  471. }
  472. } else {
  473. int timeSpan = subtitleTimingInfo[i].startTime - subtitleTimingInfo[wordIndex].startTime;
  474. if ( timeSpan > swf_subtitleLengthGuess.GetInteger() ) {
  475. if ( i - 1 >= 0 ) {
  476. subSwitchTime = subStartTime + subtitleTimingInfo[i].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  477. //idLib::Printf( "switchtime set 4 %d\n", subSwitchTime );
  478. subLastWordIndex = i - 1;
  479. } else {
  480. subSwitchTime = subStartTime + subtitleTimingInfo[i].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  481. //idLib::Printf( "switchtime set 5 %d\n", subSwitchTime );
  482. subLastWordIndex = i;
  483. }
  484. setSwitchTime = true;
  485. break;
  486. }
  487. }
  488. }
  489. if ( !setSwitchTime && subtitleTimingInfo.Num() > 0 ) {
  490. subSwitchTime = subStartTime + subtitleTimingInfo[ subtitleTimingInfo.Num() - 1 ].startTime;// - swf_subtitleEarlyTrans.GetInteger();
  491. //idLib::Printf( "switchtime set 6 %d\n", subSwitchTime );
  492. subLastWordIndex = subtitleTimingInfo.Num();
  493. }
  494. }
  495. return subLastWordIndex;
  496. }
  497. /*
  498. ==============================================
  499. idSWFTextInstance::SubtitleComplete
  500. ==============================================
  501. */
  502. void idSWFTextInstance::SubtitleCleanup() {
  503. subSourceID = -1;
  504. subAlign = -1;
  505. text = "";
  506. }
  507. /*
  508. ==============================================
  509. idSWFTextInstance::SetStrokeInfo
  510. ==============================================
  511. */
  512. void idSWFTextInstance::SetStrokeInfo( bool use, float strength, float weight ) {
  513. useStroke = use;
  514. if ( use ) {
  515. strokeWeight = weight;
  516. strokeStrength = strength;
  517. }
  518. }
  519. /*
  520. ==============================================
  521. idSWFTextInstance::CalcMaxScroll
  522. ==============================================
  523. */
  524. int idSWFTextInstance::CalcMaxScroll( int numLines ) {
  525. if ( numLines != -1 ) {
  526. if ( numLines < 0 ) {
  527. numLines = 0;
  528. }
  529. maxscroll = numLines;
  530. return maxscroll;
  531. }
  532. const idSWFEditText * shape = editText;
  533. if ( !( shape->flags & SWF_ET_MULTILINE ) ) {
  534. return 0;
  535. }
  536. if ( swf == NULL ) {
  537. return 0;
  538. }
  539. idSWFDictionaryEntry * fontEntry = swf->FindDictionaryEntry( shape->fontID, SWF_DICT_FONT );
  540. if ( fontEntry == NULL ) {
  541. return 0;
  542. }
  543. idSWFFont * swfFont = fontEntry->font;
  544. if ( swfFont == NULL ) {
  545. return 0;
  546. }
  547. const idFont * fontInfo = swfFont->fontID;
  548. if ( fontInfo == NULL ) {
  549. return 0;
  550. }
  551. idStr textCheck;
  552. if ( variable.IsEmpty() ) {
  553. textCheck = idLocalization::GetString( text );
  554. } else {
  555. textCheck = idLocalization::GetString( variable );
  556. }
  557. if ( textCheck.IsEmpty() ) {
  558. return 0;
  559. }
  560. float x = bounds.tl.x;
  561. float y = bounds.tl.y;
  562. idList< idStr > textLines;
  563. idStr * currentLine = &textLines.Alloc();
  564. // tracks the last breakable character we found
  565. int lastbreak = 0;
  566. float lastbreakX = 0;
  567. int charIndex = 0;
  568. if ( IsSubtitle() ) {
  569. charIndex = GetSubStartIndex();
  570. }
  571. while ( charIndex < textCheck.Length() ) {
  572. if ( textCheck[ charIndex ] == '\n' ) {
  573. if ( shape->flags & SWF_ET_MULTILINE ) {
  574. currentLine->Append( '\n' );
  575. x = bounds.tl.x;
  576. y += linespacing;
  577. currentLine = &textLines.Alloc();
  578. lastbreak = 0;
  579. charIndex++;
  580. continue;
  581. } else {
  582. break;
  583. }
  584. }
  585. int glyphStart = charIndex;
  586. uint32 tc = textCheck.UTF8Char( charIndex );
  587. scaledGlyphInfo_t glyph;
  588. fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
  589. float glyphSkip = glyph.xSkip;
  590. if ( HasStroke() ) {
  591. glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * GetStrokeWeight() * glyphScale );
  592. }
  593. if ( x + glyphSkip > bounds.br.x ) {
  594. if ( shape->flags & ( SWF_ET_MULTILINE | SWF_ET_WORDWRAP ) ) {
  595. if ( lastbreak > 0 ) {
  596. int curLineIndex = currentLine - &textLines[0];
  597. idStr * newline = &textLines.Alloc();
  598. currentLine = &textLines[ curLineIndex ];
  599. if ( maxLines == 1 ) {
  600. currentLine->CapLength( currentLine->Length() - 3 );
  601. currentLine->Append( "..." );
  602. break;
  603. } else {
  604. *newline = currentLine->c_str() + lastbreak;
  605. currentLine->CapLength( lastbreak );
  606. currentLine = newline;
  607. x -= lastbreakX;
  608. }
  609. } else {
  610. currentLine = &textLines.Alloc();
  611. x = bounds.tl.x;
  612. }
  613. lastbreak = 0;
  614. } else {
  615. break;
  616. }
  617. }
  618. while ( glyphStart < charIndex && glyphStart < text.Length() ) {
  619. currentLine->Append( text[ glyphStart++ ] );
  620. }
  621. x += glyphSkip;
  622. if ( tc == ' ' || tc == '-' ) {
  623. lastbreak = currentLine->Length();
  624. lastbreakX = x;
  625. }
  626. }
  627. maxscroll = textLines.Num() - maxLines;
  628. if ( maxscroll < 0 ) {
  629. maxscroll = 0;
  630. }
  631. return maxscroll;
  632. }
  633. int idSWFTextInstance::CalcNumLines() {
  634. const idSWFEditText * shape = editText;
  635. if ( !( shape->flags & SWF_ET_MULTILINE ) ) {
  636. return 1;
  637. }
  638. idSWFDictionaryEntry * fontEntry = swf->FindDictionaryEntry( shape->fontID, SWF_DICT_FONT );
  639. if ( fontEntry == NULL ) {
  640. return 1;
  641. }
  642. idStr textCheck;
  643. if ( variable.IsEmpty() ) {
  644. textCheck = idLocalization::GetString( text );
  645. } else {
  646. textCheck = idLocalization::GetString( variable );
  647. }
  648. if ( textCheck.IsEmpty() ) {
  649. return 1;
  650. }
  651. if ( swf == NULL ) {
  652. return 1;
  653. }
  654. idSWFFont * swfFont = fontEntry->font;
  655. float postTransformHeight = SWFTWIP( shape->fontHeight );
  656. const idFont * fontInfo = swfFont->fontID;
  657. float glyphScale = postTransformHeight / 48.0f;
  658. swfRect_t bounds;
  659. bounds.tl.x = ( shape->bounds.tl.x + SWFTWIP( shape->leftMargin ) );
  660. bounds.br.x = ( shape->bounds.br.x - SWFTWIP( shape->rightMargin ) );
  661. bounds.tl.y = ( shape->bounds.tl.y + ( 1.15f * glyphScale ) );
  662. bounds.br.y = ( shape->bounds.br.y );
  663. float linespacing = fontInfo->GetAscender( 1.15f * glyphScale );
  664. if ( shape->leading != 0 ) {
  665. linespacing += ( glyphScale * SWFTWIP( shape->leading ) );
  666. }
  667. float x = bounds.tl.x;
  668. int maxLines = idMath::Ftoi( ( bounds.br.y - bounds.tl.y ) / linespacing );
  669. if ( maxLines == 0 ) {
  670. maxLines = 1;
  671. }
  672. // tracks the last breakable character we found
  673. int numLines = 1;
  674. int lastbreak = 0;
  675. int charIndex = 0;
  676. while ( charIndex < textCheck.Length() ) {
  677. if ( textCheck[ charIndex ] == '\n' ) {
  678. if ( numLines == maxLines ) {
  679. return maxLines;
  680. }
  681. numLines++;
  682. lastbreak = 0;
  683. x = bounds.tl.x;
  684. charIndex++;
  685. } else {
  686. uint32 tc = textCheck[ charIndex++ ];
  687. scaledGlyphInfo_t glyph;
  688. fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
  689. float glyphSkip = glyph.xSkip;
  690. if ( useStroke ) {
  691. glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * strokeWeight * glyphScale );
  692. }
  693. if ( x + glyphSkip > bounds.br.x ) {
  694. if ( numLines == maxLines ) {
  695. return maxLines;
  696. } else {
  697. numLines++;
  698. if ( lastbreak != 0 ) {
  699. charIndex = charIndex - ( charIndex - lastbreak );
  700. }
  701. x = bounds.tl.x;
  702. lastbreak = 0;
  703. }
  704. } else {
  705. x += glyphSkip;
  706. if ( tc == ' ' || tc == '-' ) {
  707. lastbreak = charIndex;
  708. }
  709. }
  710. }
  711. }
  712. return numLines;
  713. }
  714. /*
  715. ========================
  716. idSWFScriptObject_TextInstancePrototype
  717. ========================
  718. */
  719. #define SWF_TEXT_FUNCTION_DEFINE( x ) idSWFScriptVar idSWFScriptObject_TextInstancePrototype::idSWFScriptFunction_##x::Call( idSWFScriptObject * thisObject, const idSWFParmList & parms )
  720. #define SWF_TEXT_NATIVE_VAR_DEFINE_GET( x ) idSWFScriptVar idSWFScriptObject_TextInstancePrototype::idSWFScriptNativeVar_##x::Get( class idSWFScriptObject * object )
  721. #define SWF_TEXT_NATIVE_VAR_DEFINE_SET( x ) void idSWFScriptObject_TextInstancePrototype::idSWFScriptNativeVar_##x::Set( class idSWFScriptObject * object, const idSWFScriptVar & value )
  722. #define SWF_TEXT_PTHIS_FUNC( x ) idSWFTextInstance * pThis = thisObject ? thisObject->GetText() : NULL; if ( !verify( pThis != NULL ) ) { idLib::Warning( "SWF: tried to call " x " on NULL edittext" ); return idSWFScriptVar(); }
  723. #define SWF_TEXT_PTHIS_GET( x ) idSWFTextInstance * pThis = object ? object->GetText() : NULL; if ( pThis == NULL ) { return idSWFScriptVar(); }
  724. #define SWF_TEXT_PTHIS_SET( x ) idSWFTextInstance * pThis = object ? object->GetText() : NULL; if ( pThis == NULL ) { return; }
  725. #define SWF_TEXT_FUNCTION_SET( x ) scriptFunction_##x.AddRef(); Set( #x, &scriptFunction_##x );
  726. #define SWF_TEXT_NATIVE_VAR_SET( x ) SetNative( #x, &swfScriptVar_##x );
  727. idSWFScriptObject_TextInstancePrototype::idSWFScriptObject_TextInstancePrototype() {
  728. SWF_TEXT_FUNCTION_SET( onKey );
  729. SWF_TEXT_FUNCTION_SET( onChar );
  730. SWF_TEXT_FUNCTION_SET( generateRnd );
  731. SWF_TEXT_FUNCTION_SET( calcNumLines );
  732. SWF_TEXT_FUNCTION_SET( clearTimingInfo );
  733. SWF_TEXT_NATIVE_VAR_SET( text );
  734. SWF_TEXT_NATIVE_VAR_SET( _textLength ); // only works on single lines of text not multiline
  735. SWF_TEXT_NATIVE_VAR_SET( autoSize );
  736. SWF_TEXT_NATIVE_VAR_SET( dropShadow );
  737. SWF_TEXT_NATIVE_VAR_SET( _stroke );
  738. SWF_TEXT_NATIVE_VAR_SET( _strokeStrength );
  739. SWF_TEXT_NATIVE_VAR_SET( _strokeWeight );
  740. SWF_TEXT_NATIVE_VAR_SET( variable );
  741. SWF_TEXT_NATIVE_VAR_SET( _alpha );
  742. SWF_TEXT_NATIVE_VAR_SET( textColor );
  743. SWF_TEXT_NATIVE_VAR_SET( _visible );
  744. SWF_TEXT_NATIVE_VAR_SET( selectionStart );
  745. SWF_TEXT_NATIVE_VAR_SET( selectionEnd );
  746. SWF_TEXT_NATIVE_VAR_SET( scroll );
  747. SWF_TEXT_NATIVE_VAR_SET( maxscroll );
  748. SWF_TEXT_NATIVE_VAR_SET( isTooltip );
  749. SWF_TEXT_NATIVE_VAR_SET( mode );
  750. SWF_TEXT_NATIVE_VAR_SET( delay );
  751. SWF_TEXT_NATIVE_VAR_SET( renderSound );
  752. SWF_TEXT_NATIVE_VAR_SET( updateScroll );
  753. SWF_TEXT_NATIVE_VAR_SET( subtitle );
  754. SWF_TEXT_NATIVE_VAR_SET( subtitleAlign );
  755. SWF_TEXT_NATIVE_VAR_SET( subtitleSourceID );
  756. SWF_TEXT_NATIVE_VAR_SET( subtitleSpeaker );
  757. SWF_TEXT_FUNCTION_SET( subtitleSourceCheck );
  758. SWF_TEXT_FUNCTION_SET( subtitleStart );
  759. SWF_TEXT_FUNCTION_SET( subtitleLength );
  760. SWF_TEXT_FUNCTION_SET( killSubtitle );
  761. SWF_TEXT_FUNCTION_SET( forceKillSubtitle );
  762. SWF_TEXT_FUNCTION_SET( subLastLine );
  763. SWF_TEXT_FUNCTION_SET( addSubtitleInfo );
  764. SWF_TEXT_FUNCTION_SET( terminateSubtitle );
  765. }
  766. SWF_TEXT_NATIVE_VAR_DEFINE_GET( text ) {
  767. SWF_TEXT_PTHIS_GET( "text" );
  768. return pThis->text;
  769. }
  770. SWF_TEXT_NATIVE_VAR_DEFINE_SET( text ) {
  771. SWF_TEXT_PTHIS_SET( "text " );
  772. pThis->text = idLocalization::GetString( value.ToString() );
  773. if ( pThis->text.IsEmpty() ) {
  774. pThis->selectionEnd = -1;
  775. pThis->selectionStart = -1;
  776. pThis->inputTextStartChar = 0;
  777. }
  778. pThis->lengthCalculated = false;
  779. }
  780. SWF_TEXT_NATIVE_VAR_DEFINE_GET( autoSize ) {
  781. SWF_TEXT_PTHIS_GET( "autoSize" );
  782. return ( pThis->editText->flags & SWF_ET_AUTOSIZE ) != 0;
  783. }
  784. SWF_TEXT_NATIVE_VAR_DEFINE_SET( autoSize ) {
  785. SWF_TEXT_PTHIS_SET( "autoSize" );
  786. if ( value.ToBool() ) {
  787. pThis->editText->flags |= SWF_ET_AUTOSIZE;
  788. } else {
  789. pThis->editText->flags &= ~SWF_ET_AUTOSIZE;
  790. }
  791. pThis->lengthCalculated = false;
  792. }
  793. SWF_TEXT_NATIVE_VAR_DEFINE_GET( dropShadow ) { SWF_TEXT_PTHIS_GET( "dropShadow" ); return pThis->useDropShadow; }
  794. SWF_TEXT_NATIVE_VAR_DEFINE_SET( dropShadow ) { SWF_TEXT_PTHIS_SET( "dropShadow" ); pThis->useDropShadow = value.ToBool(); }
  795. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _stroke ) { SWF_TEXT_PTHIS_GET( "_stroke" ); return pThis->useStroke; }
  796. SWF_TEXT_NATIVE_VAR_DEFINE_SET( _stroke ) { SWF_TEXT_PTHIS_SET( "_stroke" ); pThis->useStroke = value.ToBool(); }
  797. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _strokeStrength ) { SWF_TEXT_PTHIS_GET( "_strokeStrength" ); return pThis->strokeStrength; }
  798. SWF_TEXT_NATIVE_VAR_DEFINE_SET( _strokeStrength ) { SWF_TEXT_PTHIS_SET( "_strokeStrength" ); pThis->strokeStrength = value.ToFloat(); }
  799. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _strokeWeight ) { SWF_TEXT_PTHIS_GET( "_strokeWeight" ); return pThis->strokeWeight; }
  800. SWF_TEXT_NATIVE_VAR_DEFINE_SET( _strokeWeight ) { SWF_TEXT_PTHIS_SET( "_strokeWeight" ); pThis->strokeWeight = value.ToFloat(); }
  801. SWF_TEXT_NATIVE_VAR_DEFINE_GET( variable ) { SWF_TEXT_PTHIS_GET( "variable" ); return pThis->variable; }
  802. SWF_TEXT_NATIVE_VAR_DEFINE_SET( variable ) { SWF_TEXT_PTHIS_SET( "variable" ); pThis->variable = value.ToString(); }
  803. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _alpha ) { SWF_TEXT_PTHIS_GET( "_alpha" ); return pThis->color.a / 255.0f; }
  804. SWF_TEXT_NATIVE_VAR_DEFINE_SET( _alpha ) { SWF_TEXT_PTHIS_SET( "_alpha" ); pThis->color.a = idMath::Ftob( value.ToFloat() * 255.0f ); }
  805. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _visible ) { SWF_TEXT_PTHIS_GET( "_visible" ); return pThis->visible; }
  806. SWF_TEXT_NATIVE_VAR_DEFINE_SET( _visible ) { SWF_TEXT_PTHIS_SET( "_visible" ); pThis->visible = value.ToBool(); }
  807. SWF_TEXT_NATIVE_VAR_DEFINE_GET( selectionStart ) { SWF_TEXT_PTHIS_GET( "selectionStart" ); return pThis->selectionStart; }
  808. SWF_TEXT_NATIVE_VAR_DEFINE_SET( selectionStart ) { SWF_TEXT_PTHIS_SET( "selectionStart" ); pThis->selectionStart = value.ToInteger(); }
  809. SWF_TEXT_NATIVE_VAR_DEFINE_GET( selectionEnd ) { SWF_TEXT_PTHIS_GET( "selectionEnd" ); return pThis->selectionEnd; }
  810. SWF_TEXT_NATIVE_VAR_DEFINE_SET( selectionEnd ) { SWF_TEXT_PTHIS_SET( "selectionEnd" ); pThis->selectionEnd = value.ToInteger(); }
  811. SWF_TEXT_NATIVE_VAR_DEFINE_SET( isTooltip ) { SWF_TEXT_PTHIS_SET( "isTooltip" ); pThis->tooltip = value.ToBool(); }
  812. SWF_TEXT_NATIVE_VAR_DEFINE_GET( isTooltip ) { SWF_TEXT_PTHIS_GET( "isTooltip" ); return pThis->tooltip; }
  813. SWF_TEXT_NATIVE_VAR_DEFINE_SET( delay ) { SWF_TEXT_PTHIS_SET( "delay" ); pThis->renderDelay = value.ToInteger(); }
  814. SWF_TEXT_NATIVE_VAR_DEFINE_GET( delay ) { SWF_TEXT_PTHIS_GET( "delay" ); return pThis->renderDelay; }
  815. SWF_TEXT_NATIVE_VAR_DEFINE_SET( renderSound ) { SWF_TEXT_PTHIS_SET( "renderSound" ); pThis->soundClip = value.ToString(); }
  816. SWF_TEXT_NATIVE_VAR_DEFINE_GET( renderSound ) { SWF_TEXT_PTHIS_GET( "renderSound" ); return pThis->soundClip; }
  817. SWF_TEXT_NATIVE_VAR_DEFINE_SET( updateScroll ) { SWF_TEXT_PTHIS_SET( "updateScroll" ); pThis->scrollUpdate = value.ToBool(); }
  818. SWF_TEXT_NATIVE_VAR_DEFINE_GET( updateScroll ) { SWF_TEXT_PTHIS_GET( "updateScroll" ); return pThis->scrollUpdate; }
  819. SWF_TEXT_NATIVE_VAR_DEFINE_GET( mode ) { SWF_TEXT_PTHIS_GET( "mode" ); return pThis->renderMode; }
  820. SWF_TEXT_NATIVE_VAR_DEFINE_GET( scroll ) { SWF_TEXT_PTHIS_GET( "scroll" ); return pThis->scroll; }
  821. SWF_TEXT_NATIVE_VAR_DEFINE_GET( maxscroll ) { SWF_TEXT_PTHIS_GET( "maxscroll" ); return pThis->maxscroll; }
  822. SWF_TEXT_NATIVE_VAR_DEFINE_GET( _textLength ) {
  823. SWF_TEXT_PTHIS_GET( "_textLength" );
  824. return pThis->GetTextLength();
  825. }
  826. SWF_TEXT_NATIVE_VAR_DEFINE_SET( mode ) {
  827. SWF_TEXT_PTHIS_SET( "mode" );
  828. int mode = value.ToInteger();
  829. if ( mode >= (int)SWF_TEXT_RENDER_MODE_COUNT || mode < 0 ) {
  830. mode = SWF_TEXT_RENDER_NORMAL;
  831. }
  832. pThis->renderMode = swfTextRenderMode_t(mode);
  833. }
  834. SWF_TEXT_NATIVE_VAR_DEFINE_SET( scroll ) {
  835. SWF_TEXT_PTHIS_SET( "scroll" );
  836. int time = Sys_Milliseconds();
  837. if ( time >= pThis->scrollTime ) {
  838. pThis->scrollTime = Sys_Milliseconds() + swf_textScrollSpeed.GetInteger();
  839. pThis->scroll = value.ToInteger();
  840. }
  841. }
  842. SWF_TEXT_NATIVE_VAR_DEFINE_SET( maxscroll ) {
  843. SWF_TEXT_PTHIS_SET( "maxscroll" );
  844. pThis->maxscroll = value.ToInteger();
  845. }
  846. SWF_TEXT_NATIVE_VAR_DEFINE_GET( textColor ) {
  847. SWF_TEXT_PTHIS_GET( "textColor" );
  848. int r = ( pThis->color.r << 16 );
  849. int g = ( pThis->color.g << 8 );
  850. int b = pThis->color.b;
  851. int textColor = r | g | b;
  852. return textColor;
  853. }
  854. SWF_TEXT_NATIVE_VAR_DEFINE_SET( textColor ) {
  855. SWF_TEXT_PTHIS_SET( "textColor" );
  856. int textColor = value.ToInteger();
  857. int r = ( textColor >> 16 ) & 0xFF;
  858. int g = ( textColor >> 8 ) & 0x00FF;
  859. int b = textColor & 0x0000FF;
  860. pThis->color.r = r;
  861. pThis->color.g = g;
  862. pThis->color.b = b;
  863. }
  864. SWF_TEXT_FUNCTION_DEFINE( clearTimingInfo ) {
  865. SWF_TEXT_PTHIS_FUNC( "clearTimingInfo" );
  866. pThis->subtitleTimingInfo.Clear();
  867. return idSWFScriptVar();
  868. }
  869. SWF_TEXT_FUNCTION_DEFINE( generateRnd ) {
  870. SWF_TEXT_PTHIS_FUNC( "generateRnd" );
  871. pThis->triggerGenerate = true;
  872. pThis->rndSpotsVisible = -1;
  873. pThis->generatingText = false;
  874. return idSWFScriptVar();
  875. }
  876. SWF_TEXT_FUNCTION_DEFINE( calcNumLines ) {
  877. SWF_TEXT_PTHIS_FUNC( "calcNumLines" );
  878. return pThis->CalcNumLines();
  879. }
  880. SWF_TEXT_FUNCTION_DEFINE( onKey ) {
  881. SWF_TEXT_PTHIS_FUNC( "onKey" );
  882. int keyCode = parms[0].ToInteger();
  883. bool keyDown = parms[1].ToBool();
  884. if ( keyDown ) {
  885. switch ( keyCode ) {
  886. case K_LSHIFT:
  887. case K_RSHIFT: {
  888. pThis->shiftHeld = true;
  889. break;
  890. }
  891. case K_BACKSPACE:
  892. case K_DEL: {
  893. if ( pThis->selectionStart == pThis->selectionEnd ) {
  894. if ( keyCode == K_BACKSPACE ) {
  895. pThis->selectionStart = pThis->selectionEnd - 1;
  896. } else {
  897. pThis->selectionEnd = pThis->selectionStart + 1;
  898. }
  899. }
  900. int start = Min( pThis->selectionStart, pThis->selectionEnd );
  901. int end = Max( pThis->selectionStart, pThis->selectionEnd );
  902. idStr left = pThis->text.Left( Max( start, 0 ) );
  903. idStr right = pThis->text.Right( Max( pThis->text.Length() - end, 0 ) );
  904. pThis->text = left + right;
  905. pThis->selectionStart = start;
  906. pThis->selectionEnd = start;
  907. break;
  908. }
  909. case K_LEFTARROW: {
  910. if ( pThis->selectionEnd > 0 ) {
  911. pThis->selectionEnd--;
  912. if ( !pThis->shiftHeld ) {
  913. pThis->selectionStart = pThis->selectionEnd;
  914. }
  915. }
  916. break;
  917. }
  918. case K_RIGHTARROW: {
  919. if ( pThis->selectionEnd < pThis->text.Length() ) {
  920. pThis->selectionEnd++;
  921. if ( !pThis->shiftHeld ) {
  922. pThis->selectionStart = pThis->selectionEnd;
  923. }
  924. }
  925. break;
  926. }
  927. case K_HOME: {
  928. pThis->selectionEnd = 0;
  929. if ( !pThis->shiftHeld ) {
  930. pThis->selectionStart = pThis->selectionEnd;
  931. }
  932. break;
  933. }
  934. case K_END: {
  935. pThis->selectionEnd = pThis->text.Length();
  936. if ( !pThis->shiftHeld ) {
  937. pThis->selectionStart = pThis->selectionEnd;
  938. }
  939. break;
  940. }
  941. }
  942. } else {
  943. if ( keyCode == K_LSHIFT || keyCode == K_RSHIFT ) {
  944. pThis->shiftHeld = false;
  945. }
  946. }
  947. return true;
  948. }
  949. SWF_TEXT_FUNCTION_DEFINE( onChar ) {
  950. SWF_TEXT_PTHIS_FUNC( "onChar" );
  951. int keyCode = parms[0].ToInteger();
  952. if ( keyCode < 32 || keyCode == 127 ) {
  953. return false;
  954. }
  955. char letter = ( char )keyCode;
  956. // assume ` is meant for the console
  957. if ( letter == '`' ) {
  958. return false;
  959. }
  960. if ( pThis->selectionStart != pThis->selectionEnd ) {
  961. int start = Min( pThis->selectionStart, pThis->selectionEnd );
  962. int end = Max( pThis->selectionStart, pThis->selectionEnd );
  963. idStr left = pThis->text.Left( Max( start, 0 ) );
  964. idStr right = pThis->text.Right( Max( pThis->text.Length() - end, 0 ) );
  965. pThis->text = left + right;
  966. pThis->selectionStart = start;
  967. pThis->text.Clear();
  968. pThis->text.Append( left );
  969. pThis->text.Append( letter );
  970. pThis->text.Append( right );
  971. pThis->selectionStart++;
  972. } else if ( pThis->selectionStart < swf_textMaxInputLength.GetInteger() ) {
  973. if ( pThis->selectionStart < 0 ) {
  974. pThis->selectionStart = 0;
  975. }
  976. pThis->text.Insert( letter, pThis->selectionStart++ );
  977. }
  978. pThis->selectionEnd = pThis->selectionStart;
  979. return true;
  980. }
  981. SWF_TEXT_NATIVE_VAR_DEFINE_GET( subtitle ) { SWF_TEXT_PTHIS_GET( "subtitle" ); return pThis->isSubtitle; }
  982. SWF_TEXT_NATIVE_VAR_DEFINE_SET( subtitle ) { SWF_TEXT_PTHIS_SET( "subtitle" ); pThis->isSubtitle = value.ToBool(); }
  983. SWF_TEXT_NATIVE_VAR_DEFINE_GET( subtitleAlign ) { SWF_TEXT_PTHIS_GET( "subtitleAlign" ); return pThis->subAlign; }
  984. SWF_TEXT_NATIVE_VAR_DEFINE_SET( subtitleAlign ) { SWF_TEXT_PTHIS_SET( "subtitleAlign" ); pThis->subAlign = value.ToInteger(); }
  985. SWF_TEXT_NATIVE_VAR_DEFINE_GET( subtitleSourceID ) { SWF_TEXT_PTHIS_GET( "subtitleSourceID" ); return pThis->subSourceID; }
  986. SWF_TEXT_NATIVE_VAR_DEFINE_SET( subtitleSourceID ) { SWF_TEXT_PTHIS_SET( "subtitleSourceID" ); pThis->subSourceID = value.ToInteger(); }
  987. SWF_TEXT_NATIVE_VAR_DEFINE_GET( subtitleSpeaker ) { SWF_TEXT_PTHIS_GET( "subtitleSpeaker" ); return pThis->subSpeaker.c_str(); }
  988. SWF_TEXT_NATIVE_VAR_DEFINE_SET( subtitleSpeaker ) { SWF_TEXT_PTHIS_SET( "subtitleSpeaker" ); pThis->subSpeaker = value.ToString(); }
  989. SWF_TEXT_FUNCTION_DEFINE( subtitleLength ) {
  990. SWF_TEXT_PTHIS_FUNC( "subtitleLength" );
  991. pThis->subLength = parms[0].ToInteger();
  992. return idSWFScriptVar();
  993. }
  994. SWF_TEXT_FUNCTION_DEFINE( subtitleSourceCheck ) {
  995. SWF_TEXT_PTHIS_FUNC( "subtitleSourceCheck" );
  996. int idCheck = parms[0].ToInteger();
  997. if ( pThis->subSourceID == -1 ) {
  998. pThis->subSourceID = idCheck;
  999. return 1;
  1000. }
  1001. if ( idCheck == pThis->subSourceID ) { // || pThis->subForceKill ) {
  1002. pThis->SubtitleComplete();
  1003. pThis->subSourceID = idCheck;
  1004. return -1;
  1005. }
  1006. return 0;
  1007. }
  1008. SWF_TEXT_FUNCTION_DEFINE( subtitleStart ) {
  1009. SWF_TEXT_PTHIS_FUNC( "subtitleStart" );
  1010. pThis->subUpdating = true;
  1011. pThis->subNeedsSwitch = false;
  1012. pThis->subForceKillQueued = false;
  1013. pThis->subForceKill = false;
  1014. pThis->subKillTimeDelay = 0;
  1015. // trickery to swap the text so subtitles don't show until they should
  1016. pThis->subtitleText = pThis->text;
  1017. pThis->text = "";
  1018. pThis->subCharStartIndex = 0;
  1019. pThis->subNextStartIndex = 0;
  1020. pThis->subCharEndIndex = 0;
  1021. pThis->subSwitchTime = 0;
  1022. pThis->subLastWordIndex = 0;
  1023. pThis->subPrevLastWordIndex = 0;
  1024. pThis->subStartTime = -1;
  1025. pThis->subInitialLine = true;
  1026. return idSWFScriptVar();
  1027. }
  1028. SWF_TEXT_FUNCTION_DEFINE( forceKillSubtitle ) {
  1029. SWF_TEXT_PTHIS_FUNC( "forceKillSubtitle" );
  1030. pThis->subForceKill = true;
  1031. pThis->subKillTimeDelay = 0;
  1032. return idSWFScriptVar();
  1033. }
  1034. SWF_TEXT_FUNCTION_DEFINE( killSubtitle ) {
  1035. SWF_TEXT_PTHIS_FUNC( "killSubtitle" );
  1036. pThis->subForceKillQueued = true;
  1037. //pThis->SubtitleComplete();
  1038. return idSWFScriptVar();
  1039. }
  1040. SWF_TEXT_FUNCTION_DEFINE( terminateSubtitle ) {
  1041. SWF_TEXT_PTHIS_FUNC( "terminateSubtitle" );
  1042. pThis->SubtitleComplete();
  1043. pThis->SubtitleCleanup();
  1044. return idSWFScriptVar();
  1045. }
  1046. SWF_TEXT_FUNCTION_DEFINE( subLastLine ) {
  1047. SWF_TEXT_PTHIS_FUNC( "subLastLine" );
  1048. idStr lastLine;
  1049. int len = pThis->subCharEndIndex - pThis->subCharStartIndex;
  1050. pThis->text.Mid( pThis->subCharStartIndex, len, lastLine );
  1051. return lastLine;
  1052. }
  1053. SWF_TEXT_FUNCTION_DEFINE( addSubtitleInfo ) {
  1054. SWF_TEXT_PTHIS_FUNC( "addSubtitleInfo" );
  1055. if ( parms.Num() != 3 ) {
  1056. return idSWFScriptVar();
  1057. }
  1058. subTimingWordData_t info;
  1059. info.phrase = parms[0].ToString();
  1060. info.startTime = parms[1].ToInteger();
  1061. info.forceBreak = parms[2].ToBool();
  1062. pThis->subtitleTimingInfo.Append( info );
  1063. return idSWFScriptVar();
  1064. }