wolf_player.c 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. /*
  2. Copyright (C) 2004 Michael Liebscher
  3. Copyright (C) 2000-2002 by DarkOne the Hacker
  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. /*
  17. * wolf_player.c: Wolfenstein3-D player management.
  18. *
  19. * Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
  20. * Date: 2004
  21. *
  22. * Acknowledgement:
  23. * Portion of this code was derived from NewWolf, and was originally
  24. * written by DarkOne the Hacker.
  25. *
  26. */
  27. #include "../wolfiphone.h"
  28. player_t Player; // player struct (pos, health etc...)
  29. #define PLAYERSIZE MINDIST // player radius
  30. struct atkinf
  31. {
  32. char tics, attack, frame; // attack is 1 for gun, 2 for knife
  33. } attackinfo[ 4 ][ 14 ] = // 4 guns, 14 frames max for every gun!
  34. {
  35. { {6,0,1},{6,2,2},{6,0,3},{6,-1,0} },
  36. { {6,0,1},{6,1,2},{6,0,3},{6,-1,0} },
  37. { {6,0,1},{6,1,2},{6,3,3},{6,-1,0} },
  38. { {6,0,1},{6,1,2},{6,4,3},{6,-1,0} },
  39. };
  40. /*
  41. -----------------------------------------------------------------------------
  42. Function:
  43. Parameters:
  44. Returns: true if player can change weapons, otherwise false.
  45. Notes:
  46. -----------------------------------------------------------------------------
  47. */
  48. PRIVATE _boolean PL_ChangeWeapon( player_t *self, int weapon )
  49. {
  50. unsigned itemflag;
  51. itemflag = ITEM_WEAPON_1 << weapon;
  52. if( self->ammo[ AMMO_BULLETS ] == 0 && weapon != WEAPON_KNIFE )
  53. {
  54. // Com_Printf("Not enough ammo.\n");
  55. return false;
  56. }
  57. if( ! (self->items & itemflag) )
  58. {
  59. // Com_Printf( "No weapon.\n" );
  60. return false;
  61. }
  62. self->weapon =
  63. self->pendingweapon = weapon;
  64. self->attackframe =
  65. self->attackcount =
  66. self->weaponframe = 0;
  67. return true;
  68. }
  69. /*
  70. -----------------------------------------------------------------------------
  71. Function: Called if player pressed USE button
  72. Parameters:
  73. Returns: returns true if player used something
  74. Notes:
  75. -----------------------------------------------------------------------------
  76. */
  77. PRIVATE _boolean PL_Use( player_t *self, LevelData_t *lvl )
  78. {
  79. int x, y, dir;
  80. dir = Get4dir( FINE2RAD( self->position.angle ) );
  81. x = self->tilex + dx4dir[ dir ];
  82. y = self->tiley + dy4dir[ dir ];
  83. if( lvl->tilemap[ x ][ y ] & DOOR_TILE )
  84. {
  85. return Door_TryUse( &lvl->Doors.DoorMap[ x ][ y ], Player.items );
  86. }
  87. if( lvl->tilemap[ x ][ y ] & SECRET_TILE )
  88. {
  89. return PushWall_Push( x, y, dir );
  90. }
  91. if( lvl->tilemap[ x ][ y ] & ELEVATOR_TILE )
  92. {
  93. int newtex;
  94. switch( dir )
  95. {
  96. case dir4_east:
  97. case dir4_west:
  98. newtex = lvl->wall_tex_x[ x ][ y ] += 2;
  99. break;
  100. case dir4_north:
  101. case dir4_south:
  102. return false; // don't allow to press elevator rails
  103. }
  104. if( lvl->tilemap[ self->tilex ][ self->tiley ] & SECRETLEVEL_TILE )
  105. {
  106. self->playstate = ex_secretlevel;
  107. }
  108. else
  109. {
  110. self->playstate = ex_complete;
  111. }
  112. Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/040.wav" ), 1, ATTN_NORM, 0 );
  113. iphoneStartIntermission( 0 );
  114. return true;
  115. }
  116. //Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/020.wav" ), 1, ATTN_NORM, 0 );
  117. return false;
  118. }
  119. #define STOPSPEED 0x0D00
  120. #define FRICTION 0.25f
  121. #define MAXMOVE (MINDIST*2-1)
  122. /*
  123. -----------------------------------------------------------------------------
  124. Function:
  125. Parameters:
  126. Returns: returns true if move ok
  127. Notes:
  128. -----------------------------------------------------------------------------
  129. */
  130. PRIVATE _boolean PL_TryMove( player_t *self, LevelData_t *lvl )
  131. {
  132. int xl, yl, xh, yh, x, y;
  133. int d, n;
  134. xl = POS2TILE( Player.position.origin[ 0 ] - PLAYERSIZE );
  135. yl = POS2TILE( Player.position.origin[ 1 ] - PLAYERSIZE );
  136. xh = POS2TILE( Player.position.origin[ 0 ] + PLAYERSIZE );
  137. yh = POS2TILE( Player.position.origin[ 1 ] + PLAYERSIZE );
  138. // Cheching for solid walls:
  139. for( y = yl ; y <= yh ; ++y )
  140. for( x = xl ; x <= xh ; ++x )
  141. {
  142. if( lvl->tilemap[ x ][ y ] & SOLID_TILE )
  143. return 0;
  144. if( lvl->tilemap[ x ][ y ] & DOOR_TILE &&
  145. Door_Opened( &lvl->Doors, x, y) != DOOR_FULLOPEN ) {
  146. // iphone hack to allow player to move halfway into door tiles
  147. // if the player bounds doesn't cross the middle of the tile, let the move continue
  148. if ( abs( Player.position.origin[0] - TILE2POS( x ) ) <= 0x9000
  149. && abs( Player.position.origin[1] - TILE2POS( y ) ) <= 0x9000 ) {
  150. return 0;
  151. }
  152. }
  153. }
  154. // check for actors
  155. for( n = 0 ; n < NumGuards ; ++n )
  156. {
  157. if( Guards[ n ].state >= st_die1 )
  158. continue;
  159. d = self->position.origin[ 0 ] - Guards[ n ].x;
  160. if( d < -MINACTORDIST || d > MINACTORDIST )
  161. continue;
  162. d = self->position.origin[ 1 ] - Guards[ n ].y;
  163. if( d < -MINACTORDIST || d > MINACTORDIST)
  164. continue;
  165. return false;
  166. }
  167. return true;
  168. }
  169. /*
  170. -----------------------------------------------------------------------------
  171. Function:
  172. Parameters:
  173. Returns:
  174. Notes:
  175. -----------------------------------------------------------------------------
  176. */
  177. PRIVATE void PL_ClipMove( player_t *self, int xmove, int ymove )
  178. {
  179. int basex, basey;
  180. basex = self->position.origin[ 0 ];
  181. basey = self->position.origin[ 1 ];
  182. self->position.origin[ 0 ] += xmove;
  183. self->position.origin[ 1 ] += ymove;
  184. if( PL_TryMove( self, r_world ) )
  185. {
  186. return; // we moved as we wanted
  187. }
  188. //Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/000.wav" ), 1, ATTN_NORM, 0 );
  189. if( xmove ) // don't bother if we don't move x!
  190. {
  191. self->position.origin[ 0 ] = basex + xmove;
  192. self->position.origin[ 1 ] = basey;
  193. if( PL_TryMove( self, r_world ) )
  194. {
  195. return; // May be we'll move only X direction?
  196. }
  197. }
  198. if( ymove ) // don't bother if we don't move y!
  199. {
  200. self->position.origin[ 0 ] = basex;
  201. self->position.origin[ 1 ] = basey + ymove;
  202. if( PL_TryMove( self, r_world ) )
  203. {
  204. return; // May be we'll move only Y direction?
  205. }
  206. }
  207. // movement blocked; we must stay on one place... :(
  208. self->position.origin[ 0 ] = basex;
  209. self->position.origin[ 1 ] = basey;
  210. }
  211. /*
  212. -----------------------------------------------------------------------------
  213. Function: Changes player's angle and position
  214. Parameters:
  215. Returns:
  216. Notes:
  217. -----------------------------------------------------------------------------
  218. */
  219. PRIVATE void PL_ControlMovement( player_t *self, LevelData_t *lvl )
  220. {
  221. int angle, speed;
  222. // rotation
  223. angle = self->position.angle;
  224. // if(cmd->forwardmove || cmd->sidemove)
  225. self->movx = self->movy = 0; // clear accumulated movement
  226. if( Player.cmd.forwardmove )
  227. {
  228. speed = tics * Player.cmd.forwardmove;
  229. self->movx+=(int)(speed * CosTable[ angle ] );
  230. self->movy+=(int)(speed * SinTable[ angle ] );
  231. }
  232. if( Player.cmd.sidemove )
  233. {
  234. speed = tics * Player.cmd.sidemove;
  235. self->movx += (int)( speed * SinTable[ angle ] );
  236. self->movy -= (int)( speed * CosTable[ angle ] );
  237. }
  238. if( ! self->movx && ! self->movy )
  239. return;
  240. #ifdef SPEAR
  241. funnyticount = 0; // ZERO FUNNY COUNTER IF MOVED! // FIXME!
  242. #endif
  243. self->speed = self->movx + self->movy;
  244. // bound movement
  245. if( self->movx > MAXMOVE )
  246. self->movx = MAXMOVE;
  247. else if( self->movx < -MAXMOVE )
  248. self->movx = -MAXMOVE;
  249. if( self->movy > MAXMOVE )
  250. self->movy = MAXMOVE;
  251. else if( self->movy < -MAXMOVE )
  252. self->movy = -MAXMOVE;
  253. // move player and clip movement to walls (check for no-clip mode here)
  254. PL_ClipMove( self, self->movx, self->movy );
  255. self->tilex = POS2TILE( self->position.origin[ 0 ] );
  256. self->tiley = POS2TILE( self->position.origin[ 1 ] );
  257. // pick up items easier -- any tile you touch, instead of
  258. // just the midpoint tile
  259. {
  260. int x, y;
  261. for ( x = -1 ; x <= 1 ; x+= 2 ) {
  262. int tilex = POS2TILE( self->position.origin[0] + x * PLAYERSIZE );
  263. for ( y = -1 ; y <= 1 ; y+= 2 ) {
  264. int tiley = POS2TILE( self->position.origin[1] + y * PLAYERSIZE );
  265. Powerup_PickUp( tilex, tiley );
  266. }
  267. }
  268. }
  269. // Powerup_PickUp( self->tilex, self->tiley );
  270. // Checking for area change, ambush tiles and doors will have negative values
  271. if( lvl->areas[ self->tilex ][ self->tiley ] >= 0 &&
  272. lvl->areas[ self->tilex ][ self->tiley ] != Player.areanumber )
  273. {
  274. Player.areanumber = lvl->areas[ self->tilex ][ self->tiley ];
  275. assert( Player.areanumber >= 0 && Player.areanumber < NUMAREAS );
  276. Areas_ConnectAreas( Player.areanumber );
  277. }
  278. if( lvl->tilemap[ self->tilex ][ self->tiley ] & EXIT_TILE )
  279. {
  280. iphoneStartIntermission( 0 );
  281. }
  282. }
  283. /*
  284. -----------------------------------------------------------------------------
  285. Function:
  286. Parameters:
  287. Returns:
  288. Notes:
  289. -----------------------------------------------------------------------------
  290. */
  291. PRIVATE void PL_PlayerAttack( player_t *self, _boolean re_attack )
  292. {
  293. struct atkinf *cur;
  294. self->attackcount -= tics;
  295. while( self->attackcount <= 0 )
  296. {
  297. cur = &attackinfo[ self->weapon ][ self->attackframe ];
  298. switch( cur->attack )
  299. {
  300. case -1:
  301. self->flags &= ~PL_FLAG_ATTCK;
  302. if( ! self->ammo[ AMMO_BULLETS ] )
  303. {
  304. self->weapon = WEAPON_KNIFE;
  305. }
  306. else if( self->weapon != self->pendingweapon )
  307. {
  308. self->weapon = self->pendingweapon;
  309. }
  310. self->attackframe = self->weaponframe = 0;
  311. return;
  312. case 4:
  313. if( ! self->ammo[ AMMO_BULLETS ] )
  314. {
  315. break;
  316. }
  317. if( re_attack )
  318. {
  319. self->attackframe -= 2;
  320. }
  321. case 1:
  322. if( ! self->ammo[ AMMO_BULLETS ] ) // can only happen with chain gun
  323. {
  324. self->attackframe++;
  325. break;
  326. }
  327. fire_lead( self );
  328. self->ammo[ AMMO_BULLETS ]--;
  329. break;
  330. case 2:
  331. fire_hit( self );
  332. break;
  333. case 3:
  334. if(self->ammo[AMMO_BULLETS] && re_attack)
  335. self->attackframe-=2;
  336. break;
  337. }
  338. self->attackcount += cur->tics;
  339. self->attackframe++;
  340. self->weaponframe = attackinfo[ self->weapon ][ self->attackframe ].frame;
  341. }
  342. }
  343. /*
  344. -----------------------------------------------------------------------------
  345. Function:
  346. Parameters:
  347. Returns:
  348. Notes:
  349. -----------------------------------------------------------------------------
  350. */
  351. PUBLIC void PL_Process( player_t *self, LevelData_t *lvl )
  352. {
  353. int n;
  354. self->madenoise = false;
  355. PL_ControlMovement( self, lvl );
  356. if( self->flags & PL_FLAG_ATTCK )
  357. {
  358. PL_PlayerAttack( self, Player.cmd.buttons & BUTTON_ATTACK );
  359. }
  360. else
  361. {
  362. if( Player.cmd.buttons & BUTTON_USE )
  363. {
  364. if(!(self->flags & PL_FLAG_REUSE) && PL_Use( self, lvl ) )
  365. {
  366. self->flags|=PL_FLAG_REUSE;
  367. }
  368. }
  369. else
  370. {
  371. self->flags &= ~PL_FLAG_REUSE;
  372. }
  373. if( Player.cmd.buttons & BUTTON_ATTACK )
  374. {
  375. self->flags |= PL_FLAG_ATTCK;
  376. //gsh
  377. if (self->previousweapon != WEAPON_KNIFE && self->previousweapon)
  378. {
  379. //self->weapon = self->previousweapon;
  380. PL_ChangeWeapon(self, self->previousweapon);
  381. }
  382. self->attackframe = 0;
  383. self->attackcount = attackinfo[ self->weapon ][ 0 ].tics;
  384. self->weaponframe = attackinfo[ self->weapon ][ 0 ].frame;
  385. }
  386. else if ( Player.cmd.buttons & BUTTON_ALTERNATE_ATTACK ) //gsh
  387. {
  388. self->flags |= PL_FLAG_ATTCK;
  389. // PL_ChangeWeapon(self, WEAPON_KNIFE);
  390. if (self->weapon != WEAPON_KNIFE)
  391. {
  392. self->previousweapon = self->weapon;
  393. self->weapon = WEAPON_KNIFE;
  394. }
  395. self->attackframe = 0;
  396. self->attackcount = attackinfo[ self->weapon ][ 0 ].tics;
  397. self->weaponframe = attackinfo[ self->weapon ][ 0 ].frame;
  398. }
  399. else if ( Player.cmd.buttons & BUTTON_CHANGE_WEAPON ) {
  400. self->pendingweapon=self->weapon;
  401. for( n = 0 ; n < 4; ++n )
  402. {
  403. if( ++self->weapon > WEAPON_CHAIN )
  404. {
  405. self->weapon = WEAPON_KNIFE;
  406. }
  407. if( PL_ChangeWeapon( self, self->weapon ) )
  408. {
  409. break;
  410. }
  411. }
  412. self->weapon = self->pendingweapon;
  413. }
  414. }
  415. }
  416. /*
  417. -----------------------------------------------------------------------------
  418. Function:
  419. Parameters: Nothing.
  420. Returns: Nothing.
  421. Notes:
  422. -----------------------------------------------------------------------------
  423. */
  424. PUBLIC void PL_Reset(void)
  425. {
  426. memset( &Player, 0, sizeof( Player ) );
  427. Player.playstate = ex_notingame;
  428. }
  429. /*
  430. -----------------------------------------------------------------------------
  431. Function:
  432. Parameters: Nothing.
  433. Returns: Nothing.
  434. Notes:
  435. -----------------------------------------------------------------------------
  436. */
  437. PUBLIC void PL_Spawn( placeonplane_t location, LevelData_t *lvl )
  438. {
  439. Player.position = location;
  440. Player.tilex = POS2TILE( location.origin[ 0 ] );
  441. Player.tiley = POS2TILE( location.origin[ 1 ] );
  442. Player.areanumber = lvl->areas[ Player.tilex ][ Player.tiley ];
  443. assert( Player.areanumber >= 0 && Player.areanumber < NUMAREAS );
  444. if( Player.areanumber < 0 )
  445. {
  446. Player.areanumber = 36;
  447. }
  448. Areas_ConnectAreas( Player.areanumber );
  449. //gsh
  450. iphoneSetLevelNotifyText();
  451. #if 0
  452. char str[128];
  453. //sprintf( str, "Entering level E%iM%i", currentMap.episode + 1, currentMap.map + 1 );
  454. //gsh
  455. if (currentMap.episode < 6)
  456. sprintf( str, "Entering level E%iM%i", currentMap.episode+1, currentMap.map+1 );
  457. else if (currentMap.episode < 10) {
  458. int currentLevel = currentMap.episode * 10 + currentMap.map;
  459. switch (currentLevel) {
  460. case 60: case 61: case 62: case 63: case 64:
  461. sprintf( str, "Entering Tunnels %i", currentLevel-60+1);
  462. break;
  463. case 78:
  464. sprintf( str, "Entering Tunnels %i", 6);
  465. break;
  466. case 65: case 66: case 67: case 68: case 69:
  467. sprintf( str, "Entering Dungeons %i", currentLevel-65+1);
  468. break;
  469. case 79:
  470. sprintf( str, "Entering Dungeons %i", 6);
  471. break;
  472. case 70: case 71: case 72: case 73: case 74: case 75:
  473. sprintf( str, "Entering Castle");
  474. break;
  475. case 76:
  476. sprintf( str, "Entering Ramparts");
  477. break;
  478. case 77:
  479. sprintf( str, "Entering Death Knight");
  480. break;
  481. case 80:
  482. sprintf( str, "Entering Dungeon Dimension");
  483. break;
  484. default:
  485. sprintf( str, " ");
  486. break;
  487. }
  488. }
  489. else
  490. sprintf( str, "Entering level custom %i", /*currentMap.episode+1,*/ currentMap.map+1 );
  491. iphoneSetNotifyText( str );
  492. #endif
  493. }
  494. /*
  495. -----------------------------------------------------------------------------
  496. Function:
  497. Parameters: Nothing.
  498. Returns: Nothing.
  499. Notes:
  500. -----------------------------------------------------------------------------
  501. */
  502. PRIVATE void Cmd_Give_f( void )
  503. {
  504. Com_Printf( "Giving stuff.\n" );
  505. PL_GiveHealth( &Player, 999, 0 );
  506. PL_GiveAmmo( &Player, AMMO_BULLETS, 99 );
  507. PL_GiveWeapon( &Player, WEAPON_AUTO );
  508. PL_GiveWeapon( &Player, WEAPON_CHAIN );
  509. PL_GiveKey( &Player, KEY_GOLD );
  510. PL_GiveKey( &Player, KEY_SILVER );
  511. }
  512. /*
  513. -----------------------------------------------------------------------------
  514. Function:
  515. Parameters: Nothing.
  516. Returns: Nothing.
  517. Notes:
  518. -----------------------------------------------------------------------------
  519. */
  520. PRIVATE void Cmd_God_f( void )
  521. {
  522. Player.flags ^= FL_GODMODE;
  523. Com_Printf( "God mode %s\n", Player.flags & FL_GODMODE ? "ON":"OFF" );
  524. }
  525. /*
  526. -----------------------------------------------------------------------------
  527. Function:
  528. Parameters: Nothing.
  529. Returns: Nothing.
  530. Notes:
  531. -----------------------------------------------------------------------------
  532. */
  533. PRIVATE void PL_notarget_f( void )
  534. {
  535. Player.flags ^= FL_NOTARGET;
  536. Com_Printf( "Notarget mode %s\n", Player.flags & FL_NOTARGET ? "ON":"OFF" );
  537. }
  538. /*
  539. -----------------------------------------------------------------------------
  540. Function:
  541. Parameters:
  542. Returns:
  543. Notes:
  544. -----------------------------------------------------------------------------
  545. */
  546. PUBLIC void PL_Init(void)
  547. {
  548. PL_Reset();
  549. PL_NewGame( &Player );
  550. Cmd_AddCommand( "god", Cmd_God_f );
  551. Cmd_AddCommand( "notarget", PL_notarget_f );
  552. Cmd_AddCommand( "give", Cmd_Give_f );
  553. }
  554. // ------------------------- * environment interraction * -------------------------
  555. #define EXTRAPOINTS 40000 // points for an extra life
  556. /*
  557. -----------------------------------------------------------------------------
  558. Function:
  559. Parameters:
  560. Returns:
  561. Notes:
  562. -----------------------------------------------------------------------------
  563. */
  564. PUBLIC void PL_Damage( player_t *self, entity_t *attacker, int points )
  565. {
  566. if( self->playstate == ex_dead )
  567. {
  568. return;
  569. }
  570. self->LastAttacker = attacker;
  571. if( skill->value == gd_baby )
  572. {
  573. points >>= 2;
  574. }
  575. // vibe the phone
  576. SysIPhoneVibrate();
  577. // note the direction of the last hit for the directional blends
  578. {
  579. int dx = attacker->x - self->position.origin[0];
  580. int dy = attacker->y - self->position.origin[1];
  581. // probably won't ever have damage from self, but check anyway
  582. if ( dx != 0 || dy != 0 ) {
  583. float angle = atan2f( dy, dx );
  584. float playerAngle = self->position.angle * 360.0f / (float)ANG_360;
  585. float deltaAngle;
  586. angle = angle * 180.0f / M_PI;
  587. if ( angle < 0 ) {
  588. angle = 360 + angle;
  589. }
  590. deltaAngle = angle - playerAngle;
  591. if ( deltaAngle > 180 ) {
  592. deltaAngle = deltaAngle - 360;
  593. }
  594. if ( deltaAngle < -180 ) {
  595. deltaAngle = 360 + deltaAngle;
  596. }
  597. // Com_Printf( "damage: player angle: %4.0f shotAngle: %4.0f deltaAngle:%4.0f\n", playerAngle, angle, deltaAngle );
  598. if ( deltaAngle > 40 ) {
  599. iphoneSetAttackDirection( 1 );
  600. } else if ( deltaAngle < -40 ) {
  601. iphoneSetAttackDirection( -1 );
  602. }
  603. }
  604. }
  605. // do everything else but subtract health in god mode, to ease
  606. // testing of damage feedback
  607. if( !(self->flags & FL_GODMODE) )
  608. {
  609. self->health -= points;
  610. }
  611. if( self->health <= 0 )
  612. {
  613. // dead
  614. self->health = 0;
  615. self->playstate = ex_dead;
  616. Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/009.wav" ), 1, ATTN_NORM, 0 );
  617. }
  618. // red screen flash
  619. iphoneStartDamageFlash( points );
  620. // stop the happy grin face if shot before it times out
  621. Player.face_gotgun = false;
  622. // make BJ's eyes bulge on huge hits
  623. if( points > 30 && Player.health != 0 )
  624. {
  625. Player.face_ouch = true;
  626. Player.facecount = 0;
  627. }
  628. }
  629. /*
  630. -----------------------------------------------------------------------------
  631. Function:
  632. Parameters:
  633. Returns: returns true if player needs this health.
  634. Notes:
  635. gives player some HP
  636. max can be:
  637. 0 - natural player's health limit (100 or 150 with augment)
  638. >0 - indicates the limit
  639. -----------------------------------------------------------------------------
  640. */
  641. PUBLIC _boolean PL_GiveHealth( player_t *self, int points, int max )
  642. {
  643. if( max == 0 )
  644. {
  645. max = (self->items & ITEM_AUGMENT) ? 150 : 100;
  646. }
  647. if( self->health >= max )
  648. {
  649. return false; // doesn't need this health
  650. }
  651. self->health += points;
  652. if( self->health > max )
  653. {
  654. self->health = max;
  655. }
  656. Player.face_gotgun = false;
  657. return true; // took it
  658. }
  659. /*
  660. -----------------------------------------------------------------------------
  661. Function:
  662. Parameters:
  663. Returns: returns true if player needs this ammo
  664. Notes:
  665. -----------------------------------------------------------------------------
  666. */
  667. PUBLIC _boolean PL_GiveAmmo( player_t *self, int type, int ammo )
  668. {
  669. int max_ammo[ AMMO_TYPES ] = { 99 };
  670. int max;
  671. max = max_ammo[ type ];
  672. if( self->items & ITEM_BACKPACK )
  673. {
  674. max *= 2;
  675. }
  676. if( self->ammo[ type ] >= max )
  677. {
  678. return false; // don't need
  679. }
  680. if( ! self->ammo[ type ] && ! self->attackframe ) // knife was out
  681. {
  682. self->weapon = self->pendingweapon;
  683. }
  684. self->ammo[ type ] += ammo;
  685. if( self->ammo[ type ] > max )
  686. {
  687. self->ammo[ type ] = max;
  688. }
  689. return true;
  690. }
  691. /*
  692. -----------------------------------------------------------------------------
  693. Function:
  694. Parameters:
  695. Returns:
  696. Notes:
  697. -----------------------------------------------------------------------------
  698. */
  699. PUBLIC void PL_GiveWeapon( player_t *self, int weapon )
  700. {
  701. unsigned itemflag;
  702. PL_GiveAmmo( self, AMMO_BULLETS, 6 ); // give some ammo with a weapon
  703. itemflag = ITEM_WEAPON_1 << weapon;
  704. if( self->items & itemflag )
  705. {
  706. return; // player owns this weapon
  707. }
  708. else
  709. {
  710. self->items |= itemflag;
  711. if ( self->weapon < weapon ) { // don't switch if already using better weapon
  712. self->weapon = self->pendingweapon = weapon;
  713. }
  714. }
  715. }
  716. /*
  717. -----------------------------------------------------------------------------
  718. Function:
  719. Parameters:
  720. Returns:
  721. Notes:
  722. -----------------------------------------------------------------------------
  723. */
  724. PUBLIC void PL_GivePoints( player_t *self, W32 points )
  725. {
  726. #if 0 // no score on iphone
  727. self->score += points;
  728. while( self->score >= self->next_extra )
  729. {
  730. self->next_extra += EXTRAPOINTS;
  731. PL_GiveLife( self );
  732. }
  733. #endif
  734. }
  735. /*
  736. -----------------------------------------------------------------------------
  737. Function:
  738. Parameters:
  739. Returns:
  740. Notes:
  741. -----------------------------------------------------------------------------
  742. */
  743. PUBLIC void PL_GiveKey( player_t *self, int key )
  744. {
  745. self->items |= ITEM_KEY_1 << key;
  746. }
  747. /*
  748. -----------------------------------------------------------------------------
  749. Function: Set up player for the new game
  750. Parameters:
  751. Returns:
  752. Notes:
  753. -----------------------------------------------------------------------------
  754. */
  755. PUBLIC void PL_NewGame( player_t *self )
  756. {
  757. memset( self, 0, sizeof( player_t ) );
  758. self->health = 100;
  759. self->ammo[ AMMO_BULLETS ] = 16; // JDC: changed for iphone 8;
  760. self->lives = 3;
  761. self->previousweapon = WEAPON_KNIFE; //gsh
  762. self->weapon = self->pendingweapon = WEAPON_PISTOL;
  763. self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2;
  764. self->next_extra = EXTRAPOINTS;
  765. }
  766. /*
  767. -----------------------------------------------------------------------------
  768. Function: Set up player for level transition
  769. Parameters:
  770. Returns:
  771. Notes:
  772. -----------------------------------------------------------------------------
  773. */
  774. PUBLIC void PL_NextLevel( player_t *self )
  775. {
  776. self->old_score = self->score;
  777. self->attackcount = self->attackframe = self->weaponframe = 0;
  778. self->flags = 0;
  779. self->items &= ~(ITEM_KEY_1 | ITEM_KEY_2 | ITEM_KEY_3 | ITEM_KEY_4);
  780. }
  781. /*
  782. -----------------------------------------------------------------------------
  783. Function:
  784. Parameters: self -[in] Player to respawn in game world.
  785. Returns: returns false if no lives left
  786. Notes:
  787. -----------------------------------------------------------------------------
  788. */
  789. PUBLIC _boolean PL_Reborn( player_t *self )
  790. {
  791. #if 0 // removed game over from iphone version
  792. if( --self->lives < 1 )
  793. {
  794. return false;
  795. }
  796. #endif
  797. self->health = 100;
  798. self->ammo[ AMMO_BULLETS ] = 16; // JDC: changed for iphone 8;
  799. self->score = self->old_score;
  800. self->attackcount = 0;
  801. self->attackframe = 0;
  802. self->weaponframe = 0;
  803. self->flags = 0;
  804. self->previousweapon = WEAPON_KNIFE; //gsh
  805. self->weapon = self->pendingweapon = WEAPON_PISTOL;
  806. self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2;
  807. self->playstate = ex_playing;
  808. return true;
  809. }