iphone_loop.c 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797
  1. /*
  2. Copyright (C) 2009-2011 id Software LLC, a ZeniMax Media company.
  3. Copyright (C) 2009 Id Software, Inc.
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include "../doomiphone.h"
  17. playState_t playState;
  18. #define OWNER_AUTOMAP (void *)0x123
  19. int iphoneFrameNum;
  20. int levelLoadFrameNum;
  21. boolean iphoneTimeDemo;
  22. char timeDemoResultString[80];
  23. int timeDemoFrames;
  24. int timeDemoStart;
  25. // set to 1 when app is exiting to cause game thread to do a save game,
  26. // which would not be safe to do from the event thread
  27. volatile int saveOnExitState;
  28. // console mode
  29. int consoleActive;
  30. // current touches latched from the system touches
  31. int numTouches;
  32. int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
  33. // so we can detect button releases
  34. int numPrevTouches;
  35. int prevTouches[5][2];
  36. float tilt; // -1.0 to 1.0
  37. float tiltPitch;
  38. #define MAX_TILT_HISTORY 64
  39. float tiltHistory[MAX_TILT_HISTORY][4];
  40. int tiltHistoryNum;
  41. // pressing on the weapon brings up the weaponSelect overlay
  42. boolean drawWeaponSelect;
  43. int weaponSelected = -1;
  44. pkTexture_t *arialFontTexture;
  45. logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
  46. int gameSocket;
  47. int gameID;
  48. int playerID;
  49. int packetSequence; // for logging dropped packets and estimating latency
  50. netFail_t netGameFailure; // set by asyncThread
  51. netPlayer_t netPlayers[MAXPLAYERS];
  52. netPeer_t netServer;
  53. sem_t * ticSemaphore;
  54. // set after each game tic if a usable line is in front of the player
  55. boolean autoUseActive;
  56. boolean respawnActive;
  57. // if we haven't processed a game tic in a half second, draw the net problem icon
  58. int lastGameProcessedTime;
  59. // this flag lets us give a shotgun and some ammo after the player has respawned
  60. boolean addGear;
  61. extern bool inBackgroundProcess;
  62. /*
  63. =================================================================================
  64. Touch Handling
  65. With multiple draggable controls on screen, it is important to track touches
  66. as continuous events, rather than discrete points, because we want to allow
  67. a finger to start on a dragable control, but still function when it has
  68. been dragged outside the original hot area, and not trigger anything else
  69. as it wanders around.
  70. I considered a Down / Dragged / Released interface, but especially with
  71. threading, it was better to keep them as a set of structures that could
  72. be looked at at different points in the game loop.
  73. if touch goes down on a dragable control, it will be owned by that control until it
  74. is released, even if it is dragged outside the original bounds.
  75. If we support pinch for anything, a single control will own multiple touches.
  76. Touches that aren't owned by a dragable control are free-roaming, and can hit buttons.
  77. =================================================================================
  78. */
  79. /*
  80. Does not claim ownership or play any sounds.
  81. The touch can be dragged in if it isn't owned by
  82. another control.
  83. */
  84. touch_t *TouchInBounds( int x, int y, int w, int h ) {
  85. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  86. touch_t *t = &gameTouches[i];
  87. if ( t->controlOwner ) {
  88. continue; // already claimed
  89. }
  90. if ( !t->down ) {
  91. continue; // not pressed
  92. }
  93. if ( t->x >= x && t->x < x + w
  94. && t->y >= y && t->y < y + h ) {
  95. return t;
  96. }
  97. }
  98. return NULL;
  99. }
  100. // even a touch claimed by another control will count (fire button)
  101. touch_t *AnyTouchInBounds( int x, int y, int w, int h ) {
  102. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  103. touch_t *t = &gameTouches[i];
  104. if ( !t->down ) {
  105. continue; // not pressed
  106. }
  107. if ( t->x >= x && t->x < x + w
  108. && t->y >= y && t->y < y + h ) {
  109. return t;
  110. }
  111. }
  112. return NULL;
  113. }
  114. touch_t *UpdateHudTouch( ibutton_t *hud ) {
  115. if ( hud->buttonFlags & ( BF_IGNORE | BF_HUDBUTTON ) ) {
  116. // hud element isn't active
  117. return NULL;
  118. }
  119. if ( !hud->touch ) {
  120. // see if a free touch was just made in, or dragged into the bounds
  121. // make the active boxes twice as large as the drawing bounds
  122. int x = hud->x - ( hud->drawWidth >> 1 );
  123. int y = hud->y - ( hud->drawHeight >> 1 );
  124. int w = hud->drawWidth << 1;
  125. int h = hud->drawHeight << 1;
  126. hud->touch = TouchInBounds( x, y, w, h );
  127. if ( hud->touch ) {
  128. // claim this touch so it won't activate anything else
  129. hud->touch->controlOwner = hud;
  130. if ( touchClick->value ) {
  131. Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value );
  132. }
  133. // save the initial touch position for auto-centering stcks
  134. hud->downX = hud->touch->x;
  135. hud->downY = hud->touch->y;
  136. // Clamp it so that you are guaranteed to have a full range of motion.
  137. // This means that a touch at the edge of the screen won't center it,
  138. // but will instead cause immediate movement. This prevents the main
  139. // drawback of the centering controls -- if you pressed down too close
  140. // to the side, you wouldn't have full mobility that direction.
  141. int w = hud->drawWidth / 2;
  142. int h = hud->drawHeight / 2;
  143. if ( hud->downX < w ) {
  144. hud->downX = w;
  145. }
  146. if ( hud->downX + w > displaywidth ) {
  147. hud->downX = displaywidth - w;
  148. }
  149. if ( hud->downY < h ) {
  150. hud->downY = h;
  151. }
  152. if ( hud->downY > displayheight - h ) {
  153. hud->downY = displayheight - h;
  154. }
  155. }
  156. }
  157. if ( hud->touch ) {
  158. // see if the touch was released
  159. if ( !hud->touch->down ) {
  160. if ( touchClick->value ) {
  161. Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value );
  162. }
  163. hud->touch = NULL;
  164. }
  165. }
  166. return hud->touch;
  167. }
  168. void SetButtonPics( ibutton_t *button, const char *picBase, const char *title, int x, int y ) {
  169. button->texture = PK_FindTexture( picBase );
  170. button->scale = 1.0f;
  171. button->title = title;
  172. button->x = x * ((float)displaywidth) / 480.0f;
  173. button->y = y * ((float)displayheight) / 320.0f;
  174. float xRatio = ((float)displaywidth) / 480.0f;
  175. float yRatio = ((float)displayheight) / 320.0f;
  176. float themin = MIN( xRatio, yRatio );
  177. button->drawWidth = button->texture->textureData->srcWidth * themin;
  178. button->drawHeight = button->texture->textureData->srcHeight * themin;
  179. }
  180. void SetButtonPicsAndSizes( ibutton_t *button, const char *picBase, const char *title, int x, int y, int w, int h ) {
  181. SetButtonPics( button, picBase, title, x, y );
  182. float xRatio = ((float)displaywidth) / 480.0f;
  183. float yRatio = ((float)displayheight) / 320.0f;
  184. float themin = MIN( xRatio, yRatio );
  185. button->drawWidth = w * themin;
  186. button->drawHeight = h * themin;
  187. }
  188. /*
  189. ==================
  190. HandleButton
  191. Plays enter / exit / action sounds, returns true if the
  192. touch was released inside the bounds.
  193. Touches can slide onto a button, they aren't required
  194. to tap initially inside it.
  195. Main menu buttons and the small in-game buttons are
  196. done with this. Because these handle both drawing and
  197. decision making, there is a frame of latency involved
  198. versus splitting the decision making and drawing.
  199. Returns true if the button should perform its action.
  200. ==================
  201. */
  202. float buttonScaleStep = 0.01f;
  203. float buttonScaleMin = 0.95f;
  204. boolean HandleButton( ibutton_t *button ) {
  205. if ( button->buttonFlags & BF_IGNORE ) {
  206. return false;
  207. }
  208. // Hack
  209. button->drawHeight = button->drawWidth;
  210. if ( ( button->buttonFlags & BF_TRANSPARENT ) && !button->touch ) {
  211. // draw half-transparent
  212. glColor4f( 1, 1, 1, 0.5 );
  213. } else if ( button->buttonFlags & BF_DIMMED ) {
  214. // draw half-bright
  215. glColor4f( 0.5, 0.5, 0.5, 1 );
  216. } else {
  217. glColor4f( 1, 1, 1, 1 );
  218. }
  219. if ( button->touch && !button->touch->down ) {
  220. button->touch->controlOwner = NULL;
  221. button->touch = NULL;
  222. }
  223. bool released = false;
  224. if ( !(button->buttonFlags & BF_INACTIVE) ) {
  225. if ( button->touch ) {
  226. // see if the touch was dragged outside the button bounds
  227. if ( button->touch->x < button->x || button->touch->x >= button->x + button->drawWidth
  228. || button->touch->y < button->y || button->touch->y >= button->y + button->drawHeight ) {
  229. // dragged outside, don't trigger on release now
  230. if ( button->buttonFlags & BF_SMALL_CLICK ) {
  231. Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value );
  232. } else {
  233. Sound_StartLocalSound( "iphone/baborted_01.wav" );
  234. }
  235. button->touch->controlOwner = NULL;
  236. button->touch = NULL;
  237. button->pressed = false;
  238. }
  239. }
  240. if ( !button->touch ) {
  241. if ( button->pressed ) {
  242. // released inside the button, so do the action
  243. if ( button->buttonFlags & BF_SMALL_CLICK ) {
  244. Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value );
  245. } else {
  246. Sound_StartLocalSound( "iphone/baction_01.wav" );
  247. }
  248. button->pressed = false;
  249. released = true;
  250. }
  251. }
  252. // see if a new touch went down or moved into the button
  253. if ( !released && !button->touch ) {
  254. button->twoFingerPress = false;
  255. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  256. touch_t *t = &gameTouches[i];
  257. if ( t->controlOwner ) {
  258. continue; // already claimed
  259. }
  260. if ( !t->down ) {
  261. continue;
  262. }
  263. if ( t->x >= button->x && t->x < button->x + button->drawWidth
  264. && t->y >= button->y && t->y < button->y + button->drawHeight ) {
  265. if ( button->buttonFlags & BF_SMALL_CLICK ) {
  266. Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value );
  267. } else {
  268. Sound_StartLocalSound( "iphone/bdown_01.wav" );
  269. }
  270. button->touch = t;
  271. button->pressed = true;
  272. t->controlOwner = &button;
  273. break;
  274. }
  275. }
  276. }
  277. }
  278. // animate scale
  279. if ( button->frameNum != iphoneFrameNum - 1 ) {
  280. button->scale = 1.0f; // just came back to a menu
  281. }
  282. button->frameNum = iphoneFrameNum;
  283. if ( button->touch && button->touch->down ) {
  284. // check for a two-finger touch for alternate modes
  285. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  286. touch_t *t = &gameTouches[i];
  287. if ( t->controlOwner ) {
  288. continue; // already claimed
  289. }
  290. if ( t == button->touch ) {
  291. continue; // the primary touch
  292. }
  293. if ( !t->down ) {
  294. continue;
  295. }
  296. if ( t->x >= button->x && t->x < button->x + button->drawWidth
  297. && t->y >= button->y && t->y < button->y + button->drawHeight ) {
  298. button->twoFingerPress = true;
  299. break;
  300. }
  301. }
  302. // adjust the animated scale
  303. button->scale -= buttonScaleStep;
  304. if ( button->scale < buttonScaleMin ) {
  305. button->scale = buttonScaleMin;
  306. }
  307. } else {
  308. button->scale += buttonScaleStep;
  309. if ( button->scale > 1.0f ) {
  310. button->scale = 1.0f;
  311. }
  312. }
  313. if ( button->buttonFlags & BF_GLOW ) {
  314. // cycle through double-bright
  315. float f = 0.75 + 0.25 * sin( 3.14159 * 2 * ( iphoneFrameNum & 31 ) / 32.0 );
  316. glColor4f( f, f, f, 1 );
  317. glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
  318. glTexEnvf( GL_TEXTURE_ENV, GL_RGB_SCALE, 2.0 );
  319. }
  320. PK_StretchTexture( button->texture, button->x+button->drawWidth/2 - button->drawWidth/2 * button->scale,
  321. button->y + button->drawHeight/2 - button->drawHeight/2 * button->scale,
  322. button->drawWidth * button->scale, button->drawHeight * button->scale );
  323. if ( button->buttonFlags & BF_GLOW ) {
  324. glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
  325. glColor4f( 1, 1, 1, 1 );
  326. }
  327. if ( button->title ) {
  328. float length = StringFontWidth( button->title ) * 0.75;
  329. float x = button->x + button->drawWidth/2 - length/2;
  330. // don't push the text off the edge of the screen
  331. if ( x < 0 ) {
  332. x = 0;
  333. } else if ( x + length > displaywidth ) {
  334. x = displaywidth - length;
  335. }
  336. float y;
  337. float textScale = 0.75;
  338. if ( button->buttonFlags & BF_CENTERTEXT ) {
  339. glColor4f( 1, 1, 1, 1 ); // !@# remove when we get a button background that doesn't need dimming
  340. y = button->y + button->drawHeight / 2 + 8;
  341. textScale *= button->scale; // animate text scale when centered
  342. } else {
  343. y = button->y + button->drawHeight + 16;
  344. }
  345. iphoneDrawText( x, y, textScale, button->title );
  346. }
  347. glColor4f( 1, 1, 1, 1 );
  348. return released;
  349. }
  350. //=========================================================================
  351. typedef struct {
  352. unsigned short x0, y0, x1, y1;
  353. float xoff, yoff, xadvance;
  354. } GlyphRect;
  355. #include "arialGlyphRects.h" // precalculated offsets in the font image
  356. float StringFontWidth( const char *str ) {
  357. float len = 0;
  358. while ( *str ) {
  359. int i = *str;
  360. if ( i >= ' ' && i < 128 ) {
  361. len += glyphRects[i-32].xadvance;
  362. }
  363. str++;
  364. }
  365. return len;
  366. }
  367. /*
  368. ==================
  369. iphoneDrawText
  370. Returns the width in pixels
  371. ==================
  372. */
  373. float iphoneDrawText( float x, float y, float scale, const char *str ) {
  374. float fx = x;
  375. float fy = y;
  376. PK_BindTexture( arialFontTexture );
  377. glBegin( GL_QUADS );
  378. while ( *str ) {
  379. int i = *str;
  380. if ( i >= ' ' && i < 128 ) {
  381. GlyphRect *glyph = &glyphRects[i-32];
  382. // the glyphRects don't include the shadow outline
  383. float x0 = ( glyph->x0 - 1 ) / 256.0;
  384. float y0 = ( glyph->y0 - 1 ) / 256.0;
  385. float x1 = ( glyph->x1 + 2 ) / 256.0;
  386. float y1 = ( glyph->y1 + 2 ) / 256.0;
  387. float width = ( x1 - x0 ) * 256 * scale;
  388. float height = ( y1 - y0 ) * 256 * scale;
  389. float xoff = ( glyph->xoff - 1 ) * scale;
  390. float yoff = ( glyph->yoff - 1 ) * scale;
  391. glTexCoord2f( x0, y0 );
  392. glVertex2f( fx + xoff, fy + yoff );
  393. glTexCoord2f( x1, y0 );
  394. glVertex2f( fx + xoff + width, fy + yoff );
  395. glTexCoord2f( x1, y1 );
  396. glVertex2f( fx + xoff + width, fy + yoff + height );
  397. glTexCoord2f( x0, y1 );
  398. glVertex2f( fx + xoff, fy + yoff + height );
  399. // with our default texture, the difference is negligable
  400. fx += glyph->xadvance * scale;
  401. // fx += ceil(glyph->xadvance); // with the outline, ceil is probably the right thing
  402. }
  403. str++;
  404. }
  405. glEnd();
  406. return fx - x;
  407. }
  408. /*
  409. ==================
  410. iphoneCenterText
  411. Returns the width in pixels
  412. ==================
  413. */
  414. float iphoneCenterText( float x, float y, float scale, const char *str ) {
  415. float l = StringFontWidth( str );
  416. x *= ((float)displaywidth) / 480.0f;
  417. y *= ((float)displayheight) / 320.0f;
  418. x -= l * scale * 0.5;
  419. return iphoneDrawText( x, y, scale, str );
  420. }
  421. /*
  422. ==================
  423. TouchDown
  424. Checks all touches against a square
  425. ==================
  426. */
  427. int TouchDown( int x, int y, int w, int h ) {
  428. int i;
  429. for ( i = 0 ; i < numTouches ; i++ ) {
  430. if ( touches[i][0] >= x && touches[i][1] >= y
  431. && touches[i][0] < x + w && touches[i][1] < y + h ) {
  432. return 1;
  433. }
  434. }
  435. return 0;
  436. }
  437. /*
  438. ==================
  439. TouchPressed
  440. Requires that the touch be inside the bounds, and that it didn't seem to be
  441. dragged in from out of the bounds in the previous frame.
  442. ==================
  443. */
  444. int TouchPressed( int x, int y, int w, int h ) {
  445. x *= ((float)displaywidth) / 480.0f;
  446. y *= ((float)displayheight) / 320.0f;
  447. w *= ((float)displaywidth) / 480.0f;
  448. h *= ((float)displayheight) / 320.0f;
  449. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  450. touch_t *t = &gameTouches[i];
  451. if ( !t->down ) {
  452. continue;
  453. }
  454. if ( t->controlOwner ) {
  455. continue;
  456. }
  457. if ( t->stateCount != 1 ) {
  458. // not just pressed
  459. continue;
  460. }
  461. if ( t->x < x || t->x >= x + w
  462. || t->y < y || t->y >= y + h ) {
  463. continue;
  464. }
  465. // just pressed this frame
  466. return 1;
  467. }
  468. return 0;
  469. }
  470. /*
  471. ==================
  472. TouchReleased
  473. Perform an action when released in the box.
  474. If not down this frame, but down the previous frame, it is released
  475. ==================
  476. */
  477. int TouchReleased( int x, int y, int w, int h ) {
  478. #if 0
  479. // only check when the touch count just went down by one
  480. if ( numTouches != numPrevTouches - 1 ) {
  481. return 0;
  482. }
  483. int i;
  484. int downPrev = 0;
  485. int downNow = 0;
  486. for ( i = 0 ; i < numPrevTouches ; i++ ) {
  487. if ( prevTouches[i][0] >= x && prevTouches[i][1] >= y
  488. && prevTouches[i][0] < x + w && prevTouches[i][1] < y + h ) {
  489. downPrev = 1;
  490. break;
  491. }
  492. }
  493. // see if not down this frame
  494. for ( i = 0 ; i < numTouches ; i++ ) {
  495. if ( touches[i][0] >= x && touches[i][1] >= y
  496. && touches[i][0] < x + w && touches[i][1] < y + h ) {
  497. downNow = 1;
  498. break;
  499. }
  500. }
  501. if ( !downPrev ) {
  502. if ( downNow ) {
  503. Sound_StartLocalSound( "iphone/bdown_01.wav" );
  504. }
  505. // wasn't down the previous frame
  506. return 0;
  507. }
  508. if ( downNow ) {
  509. // still down
  510. return 0;
  511. }
  512. if ( numTouches == numPrevTouches ) {
  513. // finger dragged off
  514. Sound_StartLocalSound( "iphone/baborted_01.wav" );
  515. return 0;
  516. }
  517. // released
  518. Sound_StartLocalSound( "iphone/baction_01.wav" );
  519. return 1;
  520. #else
  521. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  522. touch_t *t = &gameTouches[i];
  523. if ( t->down ) {
  524. continue; // still pressed
  525. }
  526. if ( t->x >= x && t->x < x + w
  527. && t->y >= y && t->y < y + h ) {
  528. if ( t->stateCount <= 1 ) {
  529. // just released
  530. Sound_StartLocalSound( "iphone/baction_01.wav" );
  531. return 1;
  532. }
  533. }
  534. }
  535. return 0;
  536. #endif
  537. }
  538. /*
  539. ==================
  540. iphoneRotateForLandscape
  541. ==================
  542. */
  543. int iphoneRotateForLandscape() {
  544. if ( revLand->value ) {
  545. // reverse landscape mode
  546. glRotatef( -90, 0, 0, 1 );
  547. return true;
  548. } else {
  549. glRotatef( 90, 0, 0, 1 );
  550. }
  551. return false;
  552. }
  553. /*
  554. ==================
  555. iphoneSet2D
  556. ==================
  557. */
  558. void iphoneSet2D( void ) {
  559. // note that GL thinks the iphone is always
  560. // in portrait mode as far as the framebuffer
  561. // is concerned.
  562. glViewport( 0,0, displayheight, displaywidth );
  563. glMatrixMode( GL_MODELVIEW );
  564. glLoadIdentity();
  565. glEnable( GL_TEXTURE_2D );
  566. glDisable( GL_DEPTH_TEST );
  567. glDisable( GL_SCISSOR_TEST );
  568. glDisable( GL_FOG );
  569. glDisable( GL_CULL_FACE );
  570. glEnable( GL_BLEND );
  571. glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  572. glDisable( GL_ALPHA_TEST );
  573. glColor4f( 1,1,1,1 );
  574. glMatrixMode( GL_PROJECTION );
  575. glLoadIdentity();
  576. // now get into the landscape we want
  577. iphoneRotateForLandscape();
  578. glOrthof( 0, displaywidth, displayheight, 0, -99999, 99999 );
  579. }
  580. void iphoneCheckForLandscapeReverse() {
  581. static int reverseCount;
  582. // if we stay significantly negative for half a second, flip orientation
  583. if ( tiltPitch < -0.35 ) {
  584. if ( ++reverseCount > 10 ) {
  585. Cvar_SetValue( revLand->name, !revLand->value );
  586. SysIPhoneSetUIKitOrientation( revLand->value );
  587. }
  588. } else {
  589. reverseCount = 0;
  590. }
  591. }
  592. void iphoneTiltEvent( float *tilts ) {
  593. int i;
  594. int j;
  595. int c;
  596. float sum[3];
  597. static float prevTime;
  598. // we probably should mutex this, but I doubt it causes any problems
  599. if ( revLand->value ) {
  600. tilts[1] = -tilts[1];
  601. tilts[0] = -tilts[0];
  602. }
  603. c = tiltAverages->value;
  604. if ( c < 1 ) {
  605. c = 1;
  606. } else if ( c > MAX_TILT_HISTORY ) {
  607. c = MAX_TILT_HISTORY;
  608. }
  609. // acc[0] - [2] are accelerometer values, ax[3] is the timestamp
  610. for ( i = 0 ; i < 3 ; i++ ) {
  611. tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][i] = tilts[i];
  612. sum[i] = 0;
  613. for ( j = 0 ; j < c ; j++ ) {
  614. sum[i] += tiltHistory[(tiltHistoryNum-j)&(MAX_TILT_HISTORY-1)][i];
  615. }
  616. sum[i] /= c;
  617. }
  618. // save the timestamp for analysis and tap detection
  619. tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][3] = tilts[3] - prevTime;
  620. prevTime = tilts[3];
  621. tiltHistoryNum++;
  622. tilt = sum[1];
  623. tiltPitch = sum[0];
  624. // Com_Printf( "%4.2f %4.2f %4.2f\n", tilts[0], tilts[1], tilts[2] );
  625. }
  626. void ShowTilt() {
  627. int i;
  628. int axis = (int)showTilt->value - 1;
  629. color4_t fillColor = { 255, 0, 0, 255 };
  630. color4_t whiteColor = { 255, 255, 255, 255 };
  631. color4_t nowColor = { 0, 255, 0, 255 };
  632. float x;
  633. if ( axis < 0 || axis > 2 ) {
  634. return;
  635. }
  636. for ( i = 0 ; i < MAX_TILT_HISTORY ; i++ ) {
  637. x = tiltHistory[(tiltHistoryNum-1-i)&(MAX_TILT_HISTORY-1)][axis] * ( 10 / 0.018168604 );
  638. if ( x < 0 ) {
  639. R_Draw_Fill( 240 + x, i*4, -x, 4, fillColor );
  640. } else if ( x > 0 ) {
  641. R_Draw_Fill( 240, i*4, x, 4, fillColor );
  642. }
  643. }
  644. x = tilt * ( 10 / 0.018168604 );
  645. if ( x < 0 ) {
  646. R_Draw_Fill( 240 + x, i*4, -x, 4, nowColor );
  647. } else if ( x > 0 ) {
  648. R_Draw_Fill( 240, i*4, x, 4, nowColor );
  649. }
  650. R_Draw_Fill( 240, 0, 1, MAX_TILT_HISTORY*4, whiteColor );
  651. }
  652. void ShowTime() {
  653. if ( !showTime->value ) {
  654. return;
  655. }
  656. color4_t sleepColor = { 255, 0, 0, 255 };
  657. color4_t activeColor = { 0, 255, 0, 255 };
  658. color4_t swapColor = { 0, 0, 255, 255 };
  659. color4_t ticColor = { 255, 255, 255, 255 };
  660. for ( int i = 1 ; i < 30 ; i++ ) {
  661. logTime_t *lt = &loggedTimes[(iphoneFrameNum - i ) & (MAX_LOGGED_TIMES-1)];
  662. int sleepTime = ( lt->afterSleep - lt->enterFrame ) >> 7;
  663. int activeTime = ( lt->beforeSwap - lt->afterSleep ) >> 7;
  664. int swapTime = ( lt->afterSwap - lt->beforeSwap ) >> 7;
  665. R_Draw_Fill( 0, i * 4, activeTime, 2, activeColor );
  666. R_Draw_Fill( activeTime, i * 4, swapTime, 2, swapColor );
  667. R_Draw_Fill( activeTime + swapTime, i * 4, sleepTime, 2, sleepColor );
  668. R_Draw_Fill( 480 - lt->numGameTics * 10, i * 4, lt->numGameTics * 10, 2, ticColor );
  669. R_Draw_Fill( 480 - lt->numPingTics * 10, i * 4+2, lt->numPingTics * 10, 2, swapColor );
  670. }
  671. }
  672. /*
  673. ==================
  674. iphoneHighlightPicWhenTouched
  675. Draw transparent except when touched
  676. =================
  677. */
  678. void iphoneHighlightPicWhenTouched( pkTexture_t *texture, int x, int y, int w, int h ) {
  679. if ( TouchDown( x, y, w, h ) ) {
  680. glColor4f(1,1,1,1);
  681. } else {
  682. glColor4f(1,1,1,0.5);
  683. }
  684. PK_StretchTexture( texture, x, y, w, h );
  685. glColor4f(1,1,1,1);
  686. }
  687. /*
  688. ==================
  689. iphoneSetNotifyText
  690. Notify text is a single centered line for "got a key", "found a secret", etc
  691. ==================
  692. */
  693. char notifyText[128];
  694. int notifyFrameNum;
  695. void iphoneSetNotifyText( const char *str, ... ) {
  696. va_list argptr;
  697. if ( !messages->value ) {
  698. // option to disable all the message prints
  699. return;
  700. }
  701. va_start( argptr, str );
  702. (void)vsnprintf( notifyText, sizeof( notifyText )-1, str, argptr );
  703. va_end( argptr );
  704. notifyFrameNum = iphoneFrameNum;
  705. }
  706. void iphoneDrawNotifyText() {
  707. if ( notifyFrameNum == 0 ) {
  708. return;
  709. }
  710. // display for three seconds, then fade over 0.3
  711. float f = iphoneFrameNum - notifyFrameNum - 80;
  712. if ( f < 0 ) {
  713. f = 1.0;
  714. } else {
  715. f = 1.0 - f * 0.1f;
  716. if ( f < 0 ) {
  717. notifyFrameNum = 0;
  718. return;
  719. }
  720. }
  721. glColor4f( 1, 1, 1, f );
  722. iphoneCenterText( 240, 16, 0.75, notifyText );
  723. glColor4f( 1, 1, 1, 1 );
  724. }
  725. /*
  726. ==================
  727. Rotor control
  728. ==================
  729. */
  730. void iphoneDrawRotorControl( ibutton_t *hud ) {
  731. if ( hud->buttonFlags & BF_IGNORE ) {
  732. return;
  733. }
  734. pkTexture_t *tex = hud->texture;
  735. PK_BindTexture( tex );
  736. float cx = hud->x + hud->drawWidth / 2;
  737. float cy = hud->y + hud->drawHeight / 2;
  738. float as = sin( hud->drawState );
  739. float ac = cos( hud->drawState );
  740. float sz = hud->drawWidth / 2;
  741. float xv[2] = { sz*ac, sz*as };
  742. float yv[2] = { -sz*as, sz*ac };
  743. glColor4f( 1, 1, 1, 1 );
  744. glBegin( GL_TRIANGLE_STRIP );
  745. glTexCoord2f( 0.0f, 0.0f ); glVertex2f( cx - xv[0] - yv[0], cy - xv[1] - yv[1] );
  746. glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2f( cx + xv[0] - yv[0], cy + xv[1] - yv[1] );
  747. glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2f( cx - xv[0] + yv[0], cy - xv[1] + yv[1] );
  748. glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2f( cx + xv[0] + yv[0], cy + xv[1] + yv[1] );
  749. glEnd();
  750. }
  751. //===================================================================================
  752. void iphoneDrawHudControl( ibutton_t *hud ) {
  753. if ( hud->buttonFlags & BF_IGNORE ) {
  754. return;
  755. }
  756. if ( !hud->texture ) {
  757. return;
  758. }
  759. if ( hud->drawAsLimit ) {
  760. // green tint when at maximum displacement
  761. glColor4f(0.5,1,0.5,1);
  762. } else if ( hud->touch || ( hud->buttonFlags & BF_DRAW_ACTIVE ) ) {
  763. // red tint when active
  764. glColor4f(1,0.5,0.5,1);
  765. } else {
  766. glColor4f(1,1,1,1);
  767. }
  768. if ( hud->scale <= 0 ) {
  769. hud->scale = 1.0f;
  770. }
  771. float w = hud->drawWidth * hud->scale;
  772. float h = hud->drawHeight * hud->scale;
  773. float x = hud->x + ( hud->drawWidth - w ) * 0.5f;
  774. float y = hud->y + ( hud->drawHeight - w ) * 0.5f;
  775. if ( centerSticks->value && hud->touch ) {
  776. // reposiition the control after each touch
  777. x = hud->touch->x - w*0.5f;
  778. y = hud->touch->y - h*0.5f;
  779. }
  780. PK_StretchTexture( hud->texture, x, y, w, h );
  781. glColor4f(1,1,1,1);
  782. }
  783. bool NewTextButton( ibutton_t *b, const char *title, int x, int y, int w, int h ) {
  784. if ( !b->texture ) {
  785. const char *pic = (w>128 ? "iphone/long_string_box.tga" : "iphone/short_string_box.tga" );
  786. SetButtonPicsAndSizes( b, pic, "", x, y, w, h );
  787. b->buttonFlags = BF_DIMMED | BF_CENTERTEXT;
  788. }
  789. b->title = title;
  790. return HandleButton( b );
  791. }
  792. /*
  793. =====================================================================
  794. Smart USE test -- determine if there is a usable line within range
  795. of the player. This is done by the main thread after running a game tic,
  796. the async thread just checks the flag, because calling the traverse
  797. functions is not thread safe.
  798. =====================================================================
  799. */
  800. static boolean usableInRange;
  801. boolean PTR_UseTestTraverse (intercept_t* in)
  802. {
  803. if (!in->d.line->special)
  804. {
  805. P_LineOpening (in->d.line);
  806. if (openrange <= 0)
  807. {
  808. // can't use through a wall
  809. return false;
  810. }
  811. // not a special line, but keep checking
  812. return true;
  813. }
  814. player_t* player = &players[consoleplayer];
  815. int side = 0;
  816. if (P_PointOnLineSide (player->mo->x, player->mo->y, in->d.line) == 1)
  817. side = 1;
  818. // e6y
  819. // b.m. side test was broken in boom201
  820. if ((demoplayback ? (demover != 201) : (compatibility_level != boom_201_compatibility)))
  821. if (side) //jff 6/1/98 fix inadvertent deletion of side test
  822. return false;
  823. if ( in->d.line->special == 76 ) {
  824. // The button that opens the final door at E1M6 has a trigger line
  825. // right in front of it, which causes the use button to go down without
  826. // doing anything, and it gets held down as you close to the actual
  827. // button, so it doesn't get activated. You could turn to face the wall
  828. // to get the use button up, then turn back towards the button, but it
  829. // feels totally broken. The correct solution would be to return false
  830. // for all line specials that won't actually be player-usable, but I can't
  831. // tell if that is a simple range or a huge scattered list. For now, this
  832. // hack to ignore this particular line type solves the problem. We'll
  833. // see if other levels have similar issues...
  834. return false;
  835. }
  836. if ( in->d.line->special == 88 ) { // yellow door above plat on E1M6
  837. return false;
  838. }
  839. // this is a reasonable target for use
  840. usableInRange = true;
  841. //WAS can't use for than one special line in a row
  842. //jff 3/21/98 NOW multiple use allowed with enabling line flag
  843. return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))?
  844. true : false;
  845. }
  846. boolean P_TestUseLines()
  847. {
  848. int angle;
  849. fixed_t x1;
  850. fixed_t y1;
  851. fixed_t x2;
  852. fixed_t y2;
  853. player_t* player = &players[consoleplayer];
  854. if ( !player->mo ) {
  855. return false; // at intermission
  856. }
  857. angle = player->mo->angle >> ANGLETOFINESHIFT;
  858. x1 = player->mo->x;
  859. y1 = player->mo->y;
  860. x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
  861. y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
  862. // itterate over the lines and run the callback function
  863. usableInRange = false;
  864. P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTestTraverse );
  865. return usableInRange;
  866. }
  867. //=====================================================================
  868. /*
  869. ==================
  870. AutomapControls
  871. This is strictly client-side, done in the game thread instead of the async thread
  872. ==================
  873. */
  874. void AutomapControls() {
  875. //------------------------
  876. // automap controls
  877. //------------------------
  878. extern fixed_t m_x, m_y; // LL x,y window location on the map (map coords)
  879. extern fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords)
  880. // width/height of window on map (map coords)
  881. extern fixed_t m_w;
  882. extern fixed_t m_h;
  883. // used by MTOF to scale from map-to-frame-buffer coords
  884. extern fixed_t scale_mtof;
  885. // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
  886. extern fixed_t scale_ftom;
  887. extern fixed_t min_scale_mtof; // used to tell when to stop zooming out
  888. extern fixed_t max_scale_mtof; // used to tell when to stop zooming in
  889. static int prevX = -1, prevY = -1;
  890. // any touch not down in another control will
  891. // drag-scroll and be claimed by the automap
  892. int touchCount = 0;
  893. touch_t *mapTouch[MAX_TOUCHES];
  894. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  895. touch_t *t = &gameTouches[i];
  896. if ( t->down ) {
  897. if ( t->controlOwner == NULL || t->controlOwner == OWNER_AUTOMAP ) {
  898. // claim it so dragging onto another control won't
  899. // cause it to activate
  900. t->controlOwner = OWNER_AUTOMAP;
  901. mapTouch[touchCount] = t;
  902. touchCount++;
  903. }
  904. }
  905. }
  906. if ( touchCount != 1 ) {
  907. prevX = -1;
  908. }
  909. static int pinching;
  910. if ( touchCount != 2 ) {
  911. pinching = 0;
  912. }
  913. if ( touchCount == 1 ) {
  914. // adjust the automap values, assume square aspect ratio
  915. touch_t *t = mapTouch[0];
  916. // drag position
  917. if ( prevX == -1 ) {
  918. prevX = t->x;
  919. prevY = t->y;
  920. }
  921. m_x -= ( t->x - prevX ) * (float)m_w / displaywidth;
  922. m_y += ( t->y - prevY ) * (float)m_w / displaywidth;
  923. m_x2 = m_x + m_w;
  924. m_y2 = m_y + m_h;
  925. prevX = t->x;
  926. prevY = t->y;
  927. } else if ( touchCount == 2 ) {
  928. // pinch scale
  929. touch_t *t1 = mapTouch[0];
  930. touch_t *t2 = mapTouch[1];
  931. static float baseDist;
  932. static float baseMtoF;
  933. static int basem_w;
  934. static int basem_h;
  935. float dist = sqrt( (t2->x-t1->x)*(t2->x-t1->x)+(t2->y-t1->y)*(t2->y-t1->y) );
  936. if ( !pinching ) {
  937. pinching = 1;
  938. baseDist = dist;
  939. baseMtoF = scale_mtof;
  940. basem_w = m_w;
  941. basem_h = m_h;
  942. }
  943. scale_mtof = baseMtoF * dist / baseDist;
  944. if ( scale_mtof < min_scale_mtof ) {
  945. scale_mtof = min_scale_mtof;
  946. dist = (float)min_scale_mtof * baseDist / baseMtoF;
  947. } else if ( scale_mtof > max_scale_mtof ) {
  948. scale_mtof = max_scale_mtof;
  949. dist = (float)max_scale_mtof * baseDist / baseMtoF;
  950. }
  951. scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
  952. float midx = (t2->x+t1->x)*0.5;
  953. float midy = (t2->y+t1->y)*0.5;
  954. float midxDoom = m_x + m_w * midx / displaywidth;
  955. float midyDoom = m_y + m_w * midy / displaywidth;
  956. m_w = basem_w * baseDist / dist;
  957. m_h = basem_h * baseDist / dist;
  958. m_x = midxDoom - m_w * midx / displaywidth;
  959. m_y = midyDoom - m_w * midy / displaywidth;
  960. m_x2 = m_x + m_w;
  961. m_y2 = m_y + m_h;
  962. }
  963. }
  964. void SwapBuffersAndTouches() {
  965. // debug graphs
  966. ShowTilt();
  967. ShowTime();
  968. ShowNet();
  969. ShowSound();
  970. pthread_mutex_lock( &eventMutex );
  971. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  972. if ( sysTouches[i].down && gameTouches[i].down ) {
  973. sysTouches[i].controlOwner = gameTouches[i].controlOwner;
  974. }
  975. }
  976. pthread_mutex_unlock( &eventMutex );
  977. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].beforeSwap = SysIphoneMicroseconds();
  978. SysIPhoneSwapBuffers();
  979. int now = SysIphoneMicroseconds();
  980. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSwap = now;
  981. }
  982. float weaponSelectDrawScale = 1.25f;
  983. void DrawWeapon(int weaponlump, int x, int y, int w, int h, int lightlevel)
  984. {
  985. GLTexture *gltexture;
  986. float fU1,fU2,fV1,fV2;
  987. int x1,y1,x2,y2;
  988. if( displaywidth >= 960 ) {
  989. weaponSelectDrawScale = 1.25f;
  990. } else {
  991. weaponSelectDrawScale = 0.75f;
  992. }
  993. x *= ((float)displaywidth) / 480.0f;
  994. y *= ((float)displayheight) / 320.0f;
  995. w *= ((float)displaywidth) / 480.0f;
  996. h *= ((float)displayheight) / 320.0f;
  997. // force doom to rebind, since we have changed the active GL_TEXTURE_2D
  998. last_gltexture = NULL;
  999. gltexture=gld_RegisterPatch(firstspritelump+weaponlump, CR_DEFAULT);
  1000. if (!gltexture)
  1001. return;
  1002. float scaledWidth = gltexture->width * weaponSelectDrawScale;
  1003. float scaledHeight = gltexture->height * weaponSelectDrawScale;
  1004. // pin the middle bottom of the patch to the middle bottom of
  1005. // the draw rectangle, then let everything else scale as needed
  1006. fU1=0;
  1007. fV1=0;
  1008. fU2=(float)gltexture->width/(float)gltexture->tex_width;
  1009. fV2=(float)gltexture->height/(float)gltexture->tex_height;
  1010. x1=x+(w-scaledWidth)*0.5;
  1011. x2=x1 + scaledWidth;
  1012. y1=y+h-scaledHeight;
  1013. y2=y+h;;
  1014. gld_BindPatch(gltexture, CR_DEFAULT);
  1015. glColor4f( lightlevel, lightlevel, lightlevel, 1 );
  1016. glBegin(GL_TRIANGLE_STRIP);
  1017. glTexCoord2f(fU1, fV1); glVertex2f((float)(x1),(float)(y1));
  1018. glTexCoord2f(fU1, fV2); glVertex2f((float)(x1),(float)(y2));
  1019. glTexCoord2f(fU2, fV1); glVertex2f((float)(x2),(float)(y1));
  1020. glTexCoord2f(fU2, fV2); glVertex2f((float)(x2),(float)(y2));
  1021. glEnd();
  1022. glColor4f(1.0f,1.0f,1.0f,1.0f);
  1023. }
  1024. /*
  1025. ==================
  1026. DrawWeaponSelect
  1027. ==================
  1028. */
  1029. static const char *weaponNames[9] = {
  1030. "fist",
  1031. "pistol",
  1032. "shotgun",
  1033. "chaingun",
  1034. "rockets",
  1035. "plasma",
  1036. "BFG",
  1037. "chainsaw",
  1038. "dblshotgun"
  1039. };
  1040. int weaponSprites[9] = {
  1041. SPR_PUNG,
  1042. SPR_PISG,
  1043. SPR_SHTG,
  1044. SPR_CHGG,
  1045. SPR_MISG,
  1046. SPR_PLSG,
  1047. SPR_BFGG,
  1048. SPR_SAWG,
  1049. SPR_SHT2
  1050. };
  1051. void DrawWeaponSelect() {
  1052. player_t *player = &players[consoleplayer];
  1053. for ( int i = wp_fist ; i <= wp_supershotgun ; i++ ) {
  1054. int bx = i % 3;
  1055. int by = i / 3;
  1056. color4_t color = { 0, 0, 255, 200 };
  1057. color4_t textColor = { 255, 255, 255, 255 };
  1058. boolean selectable = false;
  1059. int ammo = -1;
  1060. switch ( i ) {
  1061. case wp_pistol: ammo = player->ammo[am_clip]; break;
  1062. case wp_shotgun: ammo = player->ammo[am_shell]; break;
  1063. case wp_chaingun: ammo = player->ammo[am_clip]; break;
  1064. case wp_missile: ammo = player->ammo[am_misl]; break;
  1065. case wp_plasma: ammo = player->ammo[wp_plasma]; break;
  1066. case wp_bfg: ammo = player->ammo[wp_plasma]; if ( ammo < 40 ) ammo = 0; break;
  1067. case wp_supershotgun: ammo = player->ammo[wp_plasma]; if ( ammo < 2 ) ammo = 0; break;
  1068. }
  1069. if ( !player->weaponowned[i] ) {
  1070. // don't have the weapon
  1071. color[0] = color[1] = color[2] = 50;
  1072. textColor[3] = 128;
  1073. } else {
  1074. // selectable
  1075. color[0] = 255; color[1] = 255; color[2] = 255; color[3] = 255;
  1076. selectable = true;
  1077. if ( ammo == 0 ) {
  1078. // have it, but out of ammo
  1079. color[0] = 255; color[1] = color[2] = 0;
  1080. textColor[3] = 128;
  1081. }
  1082. }
  1083. int x = bx * 160 + 20;
  1084. int y = by * 88;
  1085. int w = 120;
  1086. int h = 80;
  1087. float nx = x * ((float)displaywidth) / 480.0f;
  1088. float ny = y * ((float)displayheight) / 320.0f;
  1089. float nw = w * ((float)displaywidth) / 480.0f;
  1090. float nh = h * ((float)displayheight) / 320.0f;
  1091. if ( selectable && TouchDown( nx, ny, nw, nh ) ) {
  1092. color[0] = 128;
  1093. color[1] = color[2] = 128;
  1094. color[3] = 200;
  1095. }
  1096. glColor4ubv( color );
  1097. PK_StretchTexture( PK_FindTexture( "iphone/multi_backdrop.tga" ), nx, ny, nw, nh );
  1098. // R_Draw_Blend( x, y, w, h, color );
  1099. glColor4ubv( textColor );
  1100. iphoneCenterText( x + w/2, y+16, 0.75, weaponNames[i] );
  1101. // draw the weapon sprite full color if available or black if not
  1102. spritedef_t *sprdef = &sprites[weaponSprites[i]];
  1103. if ( sprdef->spriteframes ) { // restricted wads won't have all weapons
  1104. spriteframe_t *sprframe = &sprdef->spriteframes[0];
  1105. DrawWeapon( sprframe->lump[0] , x, y - 2, w, h, player->weaponowned[i] );
  1106. if ( selectable && TouchReleased( nx, ny, nw, nh ) ) {
  1107. drawWeaponSelect = false;
  1108. weaponSelected = i;
  1109. }
  1110. }
  1111. }
  1112. }
  1113. /*
  1114. ==================
  1115. iphoneFrame
  1116. This is continuously called by the game thread main loop, any sleeping
  1117. is done explicitly. If the game isn't holding 30hz, it will be running
  1118. flat out with no sleeping at all.
  1119. ==================
  1120. */
  1121. void iphoneFrame() {
  1122. iphoneFrameNum++;
  1123. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numGameTics = 0;
  1124. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSleep =
  1125. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].enterFrame = SysIphoneMicroseconds();
  1126. //-------------------------------------------------
  1127. // grabs the console command under mutex.
  1128. //-------------------------------------------------
  1129. pthread_mutex_lock( &eventMutex );
  1130. // execute a console command if one was typed
  1131. if ( consoleCommand[0] ) {
  1132. // send it a character at a time to the classic dooom cheat processing
  1133. // for idkfa, idclev, etc
  1134. for ( int i = 0 ; consoleCommand[i] != 0 ; i++ ) {
  1135. M_FindCheats( consoleCommand[i] );
  1136. }
  1137. // send it to the new concole command processing
  1138. Com_Printf( "%s\n", consoleCommand );
  1139. Cmd_ExecuteString( consoleCommand );
  1140. consoleCommand[0] = 0;
  1141. }
  1142. pthread_mutex_unlock( &eventMutex );
  1143. // move touches to prevTouches (old style use, remove...)
  1144. numPrevTouches = numTouches;
  1145. memcpy( prevTouches, touches, sizeof( prevTouches ) );
  1146. // process old style touches
  1147. numTouches = 0;
  1148. for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
  1149. touch_t *t = &gameTouches[i];
  1150. if ( t->down ) {
  1151. touches[numTouches][0] = t->x;
  1152. touches[numTouches][1] = t->y;
  1153. numTouches++;
  1154. }
  1155. }
  1156. // check for flipping the phone orientation
  1157. iphoneCheckForLandscapeReverse();
  1158. // go to the next demo if needed
  1159. if ( advancedemo ) {
  1160. if ( iphoneTimeDemo && timeDemoStart ) {
  1161. // go back to the menu after a timedemo
  1162. menuState = IPM_MAIN;
  1163. timeDemoStart = 0;
  1164. return;
  1165. }
  1166. static int demoState;
  1167. players[consoleplayer].playerstate = PST_LIVE; /* not reborn */
  1168. advancedemo = usergame = paused = false;
  1169. gameaction = ga_nothing;
  1170. gamestate = GS_DEMOSCREEN;
  1171. static const char *demoNames[4] = { "demo1", "demo2", "demo3", "demo4" };
  1172. G_DeferedPlayDemo( demoNames[demoState] );
  1173. if ( ++demoState == 4 ) {
  1174. demoState = 0;
  1175. }
  1176. }
  1177. if ( saveOnExitState == 1 ) {
  1178. printf( "SaveOnExitState == 1\n" );
  1179. if ( !netgame && !demoplayback && usergame && gamestate == GS_LEVEL ) {
  1180. G_SaveGame( 0, "quicksave" );
  1181. G_DoSaveGame(true);
  1182. }
  1183. saveOnExitState = 2;
  1184. return;
  1185. }
  1186. if ( saveOnExitState == 2 ) {
  1187. // the app is exiting
  1188. return;
  1189. }
  1190. //--------------------------------------------------------------------------------------
  1191. // game tic processing
  1192. //--------------------------------------------------------------------------------------
  1193. boolean runGame = false;
  1194. if( inBackgroundProcess ) {
  1195. return;
  1196. }
  1197. if ( menuState == IPM_GAME ) {
  1198. // don't run the game when in the menus
  1199. runGame = true;
  1200. }
  1201. if ( automapmode & am_active ) {
  1202. // Unlike PC Doom, don't run time when in the automap, since
  1203. // drawing the controls clutters the screen too much.
  1204. runGame = false;
  1205. }
  1206. if ( netgame ) {
  1207. // even when in the menus or automap, the tics must be processed if it is a net game
  1208. runGame = true;
  1209. }
  1210. if ( netGameFailure ) {
  1211. // consistancy failure or interruption
  1212. runGame = false;
  1213. }
  1214. // since we don't allow movement control in the automap,
  1215. // don't advance time.
  1216. if ( runGame ) {
  1217. int stopTic;
  1218. // block until the AsyncTic() has said we can run at least one frame,
  1219. // unless we are doing a flat-out timedemo run
  1220. if ( iphoneTimeDemo ) {
  1221. stopTic = gametic+1;
  1222. maketic = stopTic+1;
  1223. } else {
  1224. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSleep = SysIphoneMicroseconds();
  1225. if ( localGameID == gameID ) {
  1226. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numPingTics = netPlayers[1].peer.currentPingTics;
  1227. } else {
  1228. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numPingTics = netServer.currentPingTics;
  1229. }
  1230. // On the server, we always want to execute all available tics.
  1231. // For a remote client, that would also give the minimum lag, but things are much
  1232. // smoother if they instead try to leave one buffer tic, unless that would
  1233. // leave the frame without running a single gametic.
  1234. stopTic = maketic;
  1235. if ( consoleplayer != 0 ) {
  1236. // we are a client, so try to leave a buffer frame
  1237. stopTic = maketic - 1;
  1238. if ( gametic == stopTic ) {
  1239. stopTic++;
  1240. }
  1241. }
  1242. }
  1243. //---------------------------------
  1244. // run game tics
  1245. //---------------------------------
  1246. while( gametic < stopTic ) {
  1247. // run the gametic with all the player and monster logic
  1248. // this will extract netcmds[player][gametic%BACKUPTICS] for each player
  1249. // Com_Printf( "gametic %i\n", gametic );
  1250. G_Ticker();
  1251. // if we just respawned with add-gear, give items now
  1252. if ( addGear ) {
  1253. players[0].weaponowned[wp_shotgun] = true;
  1254. players[0].ammo[am_shell] = 20;
  1255. players[0].pendingweapon = wp_shotgun;
  1256. addGear = false;
  1257. }
  1258. // show the network trouble icon if we haven't run a game tic in a long time
  1259. lastGameProcessedTime = SysIphoneMilliseconds();
  1260. // see if there is a usable line in front of the player right now,
  1261. // which can be picked up by the asyncTic
  1262. autoUseActive = P_TestUseLines();
  1263. // generate the checksum for consistency failure testing
  1264. P_Checksum(gametic);
  1265. // on to the next tic
  1266. loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numGameTics++;
  1267. gametic++;
  1268. // this probably doesn't need to be tic-synced, but it doesn't hurt
  1269. if (players[displayplayer].mo) {
  1270. // move positional sounds and free up channels that have completed
  1271. S_UpdateSounds(players[displayplayer].mo);
  1272. }
  1273. }
  1274. }
  1275. if ( consoleActive ) {
  1276. iphoneSet2D();
  1277. // FIXME: actually draw a console...
  1278. // Console_Draw();
  1279. SwapBuffersAndTouches();
  1280. return;
  1281. }
  1282. if ( menuState != IPM_GAME ) {
  1283. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1284. iphoneSet2D();
  1285. iphoneDrawMenus();
  1286. SwapBuffersAndTouches();
  1287. return;
  1288. }
  1289. //------------------
  1290. // any touch release during demo playback goes to main menu
  1291. //------------------
  1292. if ( !usergame && !iphoneTimeDemo ) {
  1293. if ( numTouches == 0 && numPrevTouches == 1 ) {
  1294. menuState = IPM_MAIN;
  1295. }
  1296. }
  1297. if( inBackgroundProcess ) {
  1298. return;
  1299. }
  1300. // Draw the game screen. This can also be called by the pacifier update
  1301. // during level loading.
  1302. iphoneDrawScreen();
  1303. // If we just loaded a level, do the texture precaching after we
  1304. // have drawn and displayed the first frame, so the user has
  1305. // something to look at while it is loading.
  1306. if ( iphoneFrameNum == levelLoadFrameNum + 1 ) {
  1307. int start = SysIphoneMilliseconds();
  1308. gld_Precache();
  1309. int end = SysIphoneMilliseconds();
  1310. Com_Printf( "%3.1f seconds to gld_Precache()\n", (end-start)*0.001f );
  1311. timeDemoStart = end;
  1312. timeDemoFrames = 0;
  1313. }
  1314. }
  1315. int pacifierCycle;
  1316. int pacifierTime;
  1317. void iphonePacifierUpdate() {
  1318. // Only update a few times a second so it doesn't actually make it
  1319. // take longer to load.
  1320. int now = SysIphoneMilliseconds();
  1321. if ( now < pacifierTime + 200 ) {
  1322. return;
  1323. }
  1324. pacifierTime = now;
  1325. pacifierCycle = ( pacifierCycle + 1 ) & 7;
  1326. iphoneDrawScreen();
  1327. }
  1328. /*
  1329. ==================
  1330. iphoneDrawScreen
  1331. Called by the main loop and also during pacifier update when preloading textures
  1332. ==================
  1333. */
  1334. void iphoneDrawScreen() {
  1335. // tell the classic code about turning the status bar on or off
  1336. if ( statusBar->modified ) {
  1337. statusBar->modified = false;
  1338. if ( statusBar->value ) {
  1339. R_SetViewSize( 10 );
  1340. } else {
  1341. R_SetViewSize( 11 );
  1342. }
  1343. }
  1344. //------------------------------------------------
  1345. // Update display with current state.
  1346. //------------------------------------------------
  1347. // force doom to rebind, since we have changed the active GL_TEXTURE_2D
  1348. last_gltexture = NULL;
  1349. D_Display();
  1350. iphoneSet2D();
  1351. //-----------------------------------
  1352. // draw 2D overlays for game screen
  1353. //-----------------------------------
  1354. if ( automapmode & am_active ) {
  1355. if ( HandleButton( &huds.map ) ) {
  1356. AM_Stop();
  1357. }
  1358. if ( !netgame ) { // no save game option during net play
  1359. static ibutton_t btnSave;
  1360. if ( NewTextButton( &btnSave, "SAVE", 480-64, 0, 64, 32 ) ) {
  1361. G_SaveGame( 0, "ManualSave" );
  1362. G_DoSaveGame(true);
  1363. AM_Stop();
  1364. }
  1365. }
  1366. // update scroll and zoom after the buttons have potentially claimed a touch
  1367. AutomapControls();
  1368. } else if ( iphoneFrameNum == levelLoadFrameNum + 1 ) {
  1369. // don't draw hud elements during the precache
  1370. // draw rotating pacifier icon
  1371. PK_BindTexture( PK_FindTexture( "iphone/loading.tga" ) );
  1372. glColor4f( 1, 1, 1, 1 );
  1373. float cx = 240 * ((float)displaywidth) / 480.0f;
  1374. float cy = 160 * ((float)displayheight) / 320.0f;
  1375. float as = sin( pacifierCycle * M_PI / 4 );
  1376. float ac = cos( pacifierCycle * M_PI / 4 );
  1377. float sz = 64;
  1378. float xv[2] = { sz*ac, sz*as };
  1379. float yv[2] = { -sz*as, sz*ac };
  1380. glBegin( GL_TRIANGLE_STRIP );
  1381. glTexCoord2f( 0, 0 ); glVertex2f( cx - xv[0] - yv[0], cy - xv[1] - yv[1] );
  1382. glTexCoord2f( 1, 0 ); glVertex2f( cx + xv[0] - yv[0], cy + xv[1] - yv[1] );
  1383. glTexCoord2f( 0, 1 ); glVertex2f( cx - xv[0] + yv[0], cy - xv[1] + yv[1] );
  1384. glTexCoord2f( 1, 1 ); glVertex2f( cx + xv[0] + yv[0], cy + xv[1] + yv[1] );
  1385. glEnd();
  1386. } else {
  1387. // normal gameplay
  1388. if ( gamestate == GS_FINALE ) {
  1389. // leave the main menu button on the screen so they can start the
  1390. // next episode
  1391. if ( HandleButton( &huds.menu ) ) {
  1392. iphonePauseMusic();
  1393. menuState = IPM_MAIN;
  1394. }
  1395. } else if ( !menuactive && !demoplayback && usergame && gamestate == GS_LEVEL ) {
  1396. if ( players[consoleplayer].playerstate == PST_DEAD ) {
  1397. // when dead, only show the main menu con and the
  1398. // respawn / load game icons
  1399. if ( HandleButton( &huds.menu ) ) {
  1400. iphonePauseMusic();
  1401. menuState = IPM_MAIN;
  1402. }
  1403. if ( !deathmatch && !netgame ) {
  1404. static ibutton_t btnSaved;
  1405. static ibutton_t btnRespawn;
  1406. static ibutton_t btnGear;
  1407. if ( !btnSaved.texture ) {
  1408. // initial setup
  1409. SetButtonPicsAndSizes( &btnSaved, "iphone/load_saved.tga", "Saved game", 240 - 48 - 96 - 48, 80, 96, 96 );
  1410. SetButtonPicsAndSizes( &btnRespawn, "iphone/respawn.tga", "Restart", 240 - 48, 80, 96, 96 );
  1411. SetButtonPicsAndSizes( &btnGear, "iphone/respawn_gear.tga", "Add gear", 240 + 48 + 48, 80, 96, 96 );
  1412. }
  1413. if ( HandleButton( &btnSaved ) ) {
  1414. StartSaveGame();
  1415. }
  1416. if ( HandleButton( &btnRespawn ) ) {
  1417. players[consoleplayer].playerstate = PST_REBORN;
  1418. }
  1419. if ( HandleButton( &btnGear ) ) {
  1420. players[consoleplayer].playerstate = PST_REBORN;
  1421. addGear = true;
  1422. }
  1423. } else {
  1424. static ibutton_t btnNetRespawn;
  1425. if ( !btnNetRespawn.texture ) {
  1426. // initial setup
  1427. SetButtonPicsAndSizes( &btnNetRespawn, "iphone/respawn.tga", "Respawn", 240 - 96/2, 90, 96, 96 );
  1428. }
  1429. if ( HandleButton( &btnNetRespawn ) ) {
  1430. // this will cause the next command sent to include a use action,
  1431. // then clear this flag
  1432. respawnActive = true;
  1433. }
  1434. }
  1435. } else if ( drawWeaponSelect ) {
  1436. DrawWeaponSelect();
  1437. } else {
  1438. if ( drawControls->value ) {
  1439. iphoneDrawHudControl( &huds.forwardStick );
  1440. iphoneDrawHudControl( &huds.sideStick );
  1441. iphoneDrawHudControl( &huds.turnStick );
  1442. iphoneDrawRotorControl( &huds.turnRotor );
  1443. // iphoneDrawHudControl( &huds.fire );
  1444. }
  1445. if ( HandleButton( &huds.menu ) ) {
  1446. iphonePauseMusic();
  1447. menuState = IPM_MAIN;
  1448. }
  1449. if ( HandleButton( &huds.map ) ) {
  1450. AM_Start();
  1451. }
  1452. if ( HandleButton( &huds.fire ) ) {
  1453. }
  1454. if ( netgame ) {
  1455. #if 0
  1456. static ibutton_t btnPlayer;
  1457. if ( NewTextButton( &btnPlayer, "PLAYER", 0, 48, 100, 32 ) ) {
  1458. displayplayer ^= 1;
  1459. }
  1460. static ibutton_t btnNet;
  1461. if ( NewTextButton( &btnNet, "NET", 240-32, 0, 80, 32 ) ) {
  1462. showNet->value = !showNet->value;
  1463. }
  1464. static ibutton_t btnThrottle;
  1465. const char *title = throttle->value ? "Throttle:ON" : "Throttle:OFF";
  1466. if ( NewTextButton( &btnThrottle, title, 0, 0, 128, 32 ) ) {
  1467. throttle->value = !throttle->value;
  1468. }
  1469. #endif
  1470. }
  1471. #if 0
  1472. static ibutton_t btnSpeeds;
  1473. if ( NewTextButton( &btnSpeeds, "SPEEDS", 240-32, 0, 80, 32 ) ) {
  1474. showTime->value = !showTime->value;
  1475. }
  1476. static ibutton_t btnTest;
  1477. if ( NewTextButton( &btnTest, "TEST", 0, 48, 70, 32 ) ) {
  1478. testNewRenderer = !testNewRenderer;
  1479. }
  1480. #endif
  1481. }
  1482. // notify text last, so it is always on top and legible
  1483. iphoneDrawNotifyText();
  1484. // not getting network tics
  1485. if ( SysIphoneMilliseconds() - lastGameProcessedTime > 500 ) {
  1486. PK_StretchTexture( PK_FindTexture("iphone/multiplay.tga"), 240 - 96/2, 90, 96, 96 );
  1487. }
  1488. // draw the little graph in the upper right corner
  1489. if ( netgame ) {
  1490. ShowMiniNet();
  1491. }
  1492. }
  1493. }
  1494. // update timedemo display
  1495. if ( iphoneTimeDemo ) {
  1496. if ( iphoneFrameNum > levelLoadFrameNum + 1 ) {
  1497. timeDemoFrames++;
  1498. float fps = timeDemoFrames * 1000.0f / ( SysIphoneMilliseconds() - timeDemoStart );
  1499. sprintf( timeDemoResultString, "%5.1f fps", fps );
  1500. } else {
  1501. strcpy( timeDemoResultString, "TIMEDEMO" );
  1502. }
  1503. iphoneCenterText( 240, 80, 0.75, timeDemoResultString );
  1504. }
  1505. // time how long the GPU takes to render the entire frame
  1506. if ( glfinish->value ) {
  1507. int start = SysIphoneMicroseconds();
  1508. glFinish();
  1509. int end = SysIphoneMicroseconds();
  1510. Com_Printf( "%4.1f msec for glFinish()\n", ( end - start ) * 0.001f );
  1511. }
  1512. SwapBuffersAndTouches();
  1513. }