mirror of
https://github.com/id-Software/Wolf3D-iOS.git
synced 2024-11-10 07:22:02 +00:00
1024 lines
21 KiB
C
1024 lines
21 KiB
C
|
/*
|
||
|
|
||
|
Copyright (C) 2004 Michael Liebscher
|
||
|
Copyright (C) 2000-2002 by DarkOne the Hacker
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* wolf_player.c: Wolfenstein3-D player management.
|
||
|
*
|
||
|
* Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
|
||
|
* Date: 2004
|
||
|
*
|
||
|
* Acknowledgement:
|
||
|
* Portion of this code was derived from NewWolf, and was originally
|
||
|
* written by DarkOne the Hacker.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "../wolfiphone.h"
|
||
|
|
||
|
player_t Player; // player struct (pos, health etc...)
|
||
|
|
||
|
|
||
|
#define PLAYERSIZE MINDIST // player radius
|
||
|
|
||
|
|
||
|
|
||
|
struct atkinf
|
||
|
{
|
||
|
char tics, attack, frame; // attack is 1 for gun, 2 for knife
|
||
|
|
||
|
} attackinfo[ 4 ][ 14 ] = // 4 guns, 14 frames max for every gun!
|
||
|
{
|
||
|
{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },
|
||
|
{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },
|
||
|
{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },
|
||
|
{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns: true if player can change weapons, otherwise false.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE _boolean PL_ChangeWeapon( player_t *self, int weapon )
|
||
|
{
|
||
|
unsigned itemflag;
|
||
|
|
||
|
itemflag = ITEM_WEAPON_1 << weapon;
|
||
|
|
||
|
if( self->ammo[ AMMO_BULLETS ] == 0 && weapon != WEAPON_KNIFE )
|
||
|
{
|
||
|
Com_Printf("Not enough ammo.\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if( ! (self->items & itemflag) )
|
||
|
{
|
||
|
Com_Printf( "No weapon.\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
self->weapon =
|
||
|
self->pendingweapon = weapon;
|
||
|
|
||
|
self->attackframe =
|
||
|
self->attackcount =
|
||
|
self->weaponframe = 0;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function: Called if player pressed USE button
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns: returns true if player used something
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE _boolean PL_Use( player_t *self, LevelData_t *lvl )
|
||
|
{
|
||
|
int x, y, dir;
|
||
|
|
||
|
dir = Get4dir( FINE2RAD( self->position.angle ) );
|
||
|
x = self->tilex + dx4dir[ dir ];
|
||
|
y = self->tiley + dy4dir[ dir ];
|
||
|
|
||
|
if( lvl->tilemap[ x ][ y ] & DOOR_TILE )
|
||
|
{
|
||
|
return Door_TryUse( &lvl->Doors.DoorMap[ x ][ y ], Player.items );
|
||
|
}
|
||
|
|
||
|
if( lvl->tilemap[ x ][ y ] & SECRET_TILE )
|
||
|
{
|
||
|
return PushWall_Push( x, y, dir );
|
||
|
}
|
||
|
|
||
|
if( lvl->tilemap[ x ][ y ] & ELEVATOR_TILE )
|
||
|
{
|
||
|
int newtex;
|
||
|
|
||
|
switch( dir )
|
||
|
{
|
||
|
case dir4_east:
|
||
|
case dir4_west:
|
||
|
newtex = lvl->wall_tex_x[ x ][ y ] += 2;
|
||
|
break;
|
||
|
|
||
|
case dir4_north:
|
||
|
case dir4_south:
|
||
|
return false; // don't allow to press elevator rails
|
||
|
}
|
||
|
|
||
|
if( lvl->tilemap[ self->tilex ][ self->tiley ] & SECRETLEVEL_TILE )
|
||
|
{
|
||
|
self->playstate = ex_secretlevel;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->playstate = ex_complete;
|
||
|
}
|
||
|
Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/040.wav" ), 1, ATTN_NORM, 0 );
|
||
|
|
||
|
iphoneStartIntermission( 0 );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/020.wav" ), 1, ATTN_NORM, 0 );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#define STOPSPEED 0x0D00
|
||
|
#define FRICTION 0.25f
|
||
|
#define MAXMOVE (MINDIST*2-1)
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns: returns true if move ok
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE _boolean PL_TryMove( player_t *self, LevelData_t *lvl )
|
||
|
{
|
||
|
int xl, yl, xh, yh, x, y;
|
||
|
int d, n;
|
||
|
|
||
|
xl = POS2TILE( Player.position.origin[ 0 ] - PLAYERSIZE );
|
||
|
yl = POS2TILE( Player.position.origin[ 1 ] - PLAYERSIZE );
|
||
|
xh = POS2TILE( Player.position.origin[ 0 ] + PLAYERSIZE );
|
||
|
yh = POS2TILE( Player.position.origin[ 1 ] + PLAYERSIZE );
|
||
|
|
||
|
// Cheching for solid walls:
|
||
|
for( y = yl ; y <= yh ; ++y )
|
||
|
for( x = xl ; x <= xh ; ++x )
|
||
|
{
|
||
|
if( lvl->tilemap[ x ][ y ] & SOLID_TILE )
|
||
|
return 0;
|
||
|
|
||
|
if( lvl->tilemap[ x ][ y ] & DOOR_TILE &&
|
||
|
Door_Opened( &lvl->Doors, x, y) != DOOR_FULLOPEN ) {
|
||
|
// iphone hack to allow player to move halfway into door tiles
|
||
|
// if the player bounds doesn't cross the middle of the tile, let the move continue
|
||
|
if ( abs( Player.position.origin[0] - TILE2POS( x ) ) <= 0x9000
|
||
|
&& abs( Player.position.origin[1] - TILE2POS( y ) ) <= 0x9000 ) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check for actors
|
||
|
for( n = 0 ; n < NumGuards ; ++n )
|
||
|
{
|
||
|
if( Guards[ n ].state >= st_die1 )
|
||
|
continue;
|
||
|
|
||
|
d = self->position.origin[ 0 ] - Guards[ n ].x;
|
||
|
|
||
|
if( d < -MINACTORDIST || d > MINACTORDIST )
|
||
|
continue;
|
||
|
|
||
|
d = self->position.origin[ 1 ] - Guards[ n ].y;
|
||
|
|
||
|
if( d < -MINACTORDIST || d > MINACTORDIST)
|
||
|
continue;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void PL_ClipMove( player_t *self, int xmove, int ymove )
|
||
|
{
|
||
|
int basex, basey;
|
||
|
|
||
|
basex = self->position.origin[ 0 ];
|
||
|
basey = self->position.origin[ 1 ];
|
||
|
|
||
|
self->position.origin[ 0 ] += xmove;
|
||
|
self->position.origin[ 1 ] += ymove;
|
||
|
if( PL_TryMove( self, r_world ) )
|
||
|
{
|
||
|
return; // we moved as we wanted
|
||
|
}
|
||
|
|
||
|
//Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/000.wav" ), 1, ATTN_NORM, 0 );
|
||
|
|
||
|
if( xmove ) // don't bother if we don't move x!
|
||
|
{
|
||
|
self->position.origin[ 0 ] = basex + xmove;
|
||
|
self->position.origin[ 1 ] = basey;
|
||
|
if( PL_TryMove( self, r_world ) )
|
||
|
{
|
||
|
return; // May be we'll move only X direction?
|
||
|
}
|
||
|
}
|
||
|
if( ymove ) // don't bother if we don't move y!
|
||
|
{
|
||
|
self->position.origin[ 0 ] = basex;
|
||
|
self->position.origin[ 1 ] = basey + ymove;
|
||
|
if( PL_TryMove( self, r_world ) )
|
||
|
{
|
||
|
return; // May be we'll move only Y direction?
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// movement blocked; we must stay on one place... :(
|
||
|
self->position.origin[ 0 ] = basex;
|
||
|
self->position.origin[ 1 ] = basey;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function: Changes player's angle and position
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void PL_ControlMovement( player_t *self, LevelData_t *lvl )
|
||
|
{
|
||
|
int angle, speed;
|
||
|
|
||
|
// rotation
|
||
|
angle = self->position.angle;
|
||
|
|
||
|
// if(cmd->forwardmove || cmd->sidemove)
|
||
|
self->movx = self->movy = 0; // clear accumulated movement
|
||
|
|
||
|
if( Player.cmd.forwardmove )
|
||
|
{
|
||
|
speed = tics * Player.cmd.forwardmove;
|
||
|
self->movx+=(int)(speed * CosTable[ angle ] );
|
||
|
self->movy+=(int)(speed * SinTable[ angle ] );
|
||
|
}
|
||
|
if( Player.cmd.sidemove )
|
||
|
{
|
||
|
speed = tics * Player.cmd.sidemove;
|
||
|
self->movx += (int)( speed * SinTable[ angle ] );
|
||
|
self->movy -= (int)( speed * CosTable[ angle ] );
|
||
|
}
|
||
|
|
||
|
if( ! self->movx && ! self->movy )
|
||
|
return;
|
||
|
|
||
|
#ifdef SPEAR
|
||
|
|
||
|
funnyticount = 0; // ZERO FUNNY COUNTER IF MOVED! // FIXME!
|
||
|
|
||
|
#endif
|
||
|
self->speed = self->movx + self->movy;
|
||
|
|
||
|
// bound movement
|
||
|
if( self->movx > MAXMOVE )
|
||
|
self->movx = MAXMOVE;
|
||
|
else if( self->movx < -MAXMOVE )
|
||
|
self->movx = -MAXMOVE;
|
||
|
|
||
|
if( self->movy > MAXMOVE )
|
||
|
self->movy = MAXMOVE;
|
||
|
else if( self->movy < -MAXMOVE )
|
||
|
self->movy = -MAXMOVE;
|
||
|
|
||
|
// move player and clip movement to walls (check for no-clip mode here)
|
||
|
PL_ClipMove( self, self->movx, self->movy );
|
||
|
self->tilex = POS2TILE( self->position.origin[ 0 ] );
|
||
|
self->tiley = POS2TILE( self->position.origin[ 1 ] );
|
||
|
|
||
|
// pick up items easier -- any tile you touch, instead of
|
||
|
// just the midpoint tile
|
||
|
{
|
||
|
int x, y;
|
||
|
|
||
|
for ( x = -1 ; x <= 1 ; x+= 2 ) {
|
||
|
int tilex = POS2TILE( self->position.origin[0] + x * PLAYERSIZE );
|
||
|
for ( y = -1 ; y <= 1 ; y+= 2 ) {
|
||
|
int tiley = POS2TILE( self->position.origin[1] + y * PLAYERSIZE );
|
||
|
Powerup_PickUp( tilex, tiley );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Powerup_PickUp( self->tilex, self->tiley );
|
||
|
|
||
|
// Checking for area change, ambush tiles and doors will have negative values
|
||
|
if( lvl->areas[ self->tilex ][ self->tiley ] >= 0 &&
|
||
|
lvl->areas[ self->tilex ][ self->tiley ] != Player.areanumber )
|
||
|
{
|
||
|
Player.areanumber = lvl->areas[ self->tilex ][ self->tiley ];
|
||
|
assert( Player.areanumber >= 0 && Player.areanumber < NUMAREAS );
|
||
|
Areas_ConnectAreas( Player.areanumber );
|
||
|
}
|
||
|
|
||
|
if( lvl->tilemap[ self->tilex ][ self->tiley ] & EXIT_TILE )
|
||
|
{
|
||
|
iphoneStartIntermission( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void PL_PlayerAttack( player_t *self, _boolean re_attack )
|
||
|
{
|
||
|
struct atkinf *cur;
|
||
|
|
||
|
self->attackcount -= tics;
|
||
|
while( self->attackcount <= 0 )
|
||
|
{
|
||
|
cur = &attackinfo[ self->weapon ][ self->attackframe ];
|
||
|
switch( cur->attack )
|
||
|
{
|
||
|
case -1:
|
||
|
self->flags &= ~PL_FLAG_ATTCK;
|
||
|
if( ! self->ammo[ AMMO_BULLETS ] )
|
||
|
{
|
||
|
self->weapon = WEAPON_KNIFE;
|
||
|
}
|
||
|
else if( self->weapon != self->pendingweapon )
|
||
|
{
|
||
|
self->weapon = self->pendingweapon;
|
||
|
}
|
||
|
self->attackframe = self->weaponframe = 0;
|
||
|
return;
|
||
|
|
||
|
case 4:
|
||
|
if( ! self->ammo[ AMMO_BULLETS ] )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( re_attack )
|
||
|
{
|
||
|
self->attackframe -= 2;
|
||
|
}
|
||
|
|
||
|
case 1:
|
||
|
if( ! self->ammo[ AMMO_BULLETS ] ) // can only happen with chain gun
|
||
|
{
|
||
|
self->attackframe++;
|
||
|
break;
|
||
|
}
|
||
|
fire_lead( self );
|
||
|
self->ammo[ AMMO_BULLETS ]--;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
fire_hit( self );
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
if(self->ammo[AMMO_BULLETS] && re_attack)
|
||
|
self->attackframe-=2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
self->attackcount += cur->tics;
|
||
|
self->attackframe++;
|
||
|
self->weaponframe = attackinfo[ self->weapon ][ self->attackframe ].frame;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_Process( player_t *self, LevelData_t *lvl )
|
||
|
{
|
||
|
int n;
|
||
|
|
||
|
self->madenoise = false;
|
||
|
|
||
|
PL_ControlMovement( self, lvl );
|
||
|
|
||
|
if( self->flags & PL_FLAG_ATTCK )
|
||
|
{
|
||
|
PL_PlayerAttack( self, Player.cmd.buttons & BUTTON_ATTACK );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( Player.cmd.buttons & BUTTON_USE )
|
||
|
{
|
||
|
if(!(self->flags & PL_FLAG_REUSE) && PL_Use( self, lvl ) )
|
||
|
{
|
||
|
self->flags|=PL_FLAG_REUSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->flags &= ~PL_FLAG_REUSE;
|
||
|
}
|
||
|
|
||
|
if( Player.cmd.buttons & BUTTON_ATTACK )
|
||
|
{
|
||
|
self->flags |= PL_FLAG_ATTCK;
|
||
|
|
||
|
self->attackframe = 0;
|
||
|
self->attackcount = attackinfo[ self->weapon ][ 0 ].tics;
|
||
|
self->weaponframe = attackinfo[ self->weapon ][ 0 ].frame;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// process impulses
|
||
|
switch( Player.cmd.impulse )
|
||
|
{
|
||
|
case 0:
|
||
|
break; // no impulse
|
||
|
|
||
|
case 1:
|
||
|
case 2:
|
||
|
case 3:
|
||
|
case 4:
|
||
|
PL_ChangeWeapon( self, Player.cmd.impulse - 1 );
|
||
|
break;
|
||
|
|
||
|
case 10: // next weapon /like in Quake/ FIXME: weapprev, weapnext
|
||
|
self->pendingweapon=self->weapon;
|
||
|
for( n = 0 ; n < 4; ++n )
|
||
|
{
|
||
|
if( ++self->weapon > WEAPON_CHAIN )
|
||
|
{
|
||
|
self->weapon = WEAPON_KNIFE;
|
||
|
}
|
||
|
|
||
|
if( PL_ChangeWeapon( self, self->weapon ) )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
self->weapon = self->pendingweapon;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Com_Printf( "Unknown Impulse: %d\n", Player.cmd.impulse );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: Nothing.
|
||
|
|
||
|
Returns: Nothing.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_Reset(void)
|
||
|
{
|
||
|
memset( &Player, 0, sizeof( Player ) );
|
||
|
Player.playstate = ex_notingame;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: Nothing.
|
||
|
|
||
|
Returns: Nothing.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_Spawn( placeonplane_t location, LevelData_t *lvl )
|
||
|
{
|
||
|
Player.position = location;
|
||
|
Player.tilex = POS2TILE( location.origin[ 0 ] );
|
||
|
Player.tiley = POS2TILE( location.origin[ 1 ] );
|
||
|
Player.areanumber = lvl->areas[ Player.tilex ][ Player.tiley ];
|
||
|
assert( Player.areanumber >= 0 && Player.areanumber < NUMAREAS );
|
||
|
if( Player.areanumber < 0 )
|
||
|
{
|
||
|
Player.areanumber = 36;
|
||
|
}
|
||
|
|
||
|
Areas_ConnectAreas( Player.areanumber );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: Nothing.
|
||
|
|
||
|
Returns: Nothing.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void Cmd_Give_f( void )
|
||
|
{
|
||
|
PL_GiveHealth( &Player, 999, 0 );
|
||
|
PL_GiveAmmo( &Player, AMMO_BULLETS, 99 );
|
||
|
PL_GiveWeapon( &Player, WEAPON_AUTO );
|
||
|
PL_GiveWeapon( &Player, WEAPON_CHAIN );
|
||
|
PL_GiveKey( &Player, KEY_GOLD );
|
||
|
PL_GiveKey( &Player, KEY_SILVER );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: Nothing.
|
||
|
|
||
|
Returns: Nothing.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void Cmd_God_f( void )
|
||
|
{
|
||
|
Player.flags ^= FL_GODMODE;
|
||
|
|
||
|
Com_Printf( "God mode %s\n", Player.flags & FL_GODMODE ? "ON":"OFF" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: Nothing.
|
||
|
|
||
|
Returns: Nothing.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PRIVATE void PL_notarget_f( void )
|
||
|
{
|
||
|
Player.flags ^= FL_NOTARGET;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_Init(void)
|
||
|
{
|
||
|
PL_Reset();
|
||
|
PL_NewGame( &Player );
|
||
|
|
||
|
Cmd_AddCommand( "god", Cmd_God_f );
|
||
|
Cmd_AddCommand( "notarget", PL_notarget_f );
|
||
|
|
||
|
Cmd_AddCommand( "give", Cmd_Give_f );
|
||
|
}
|
||
|
|
||
|
// ------------------------- * environment interraction * -------------------------
|
||
|
#define EXTRAPOINTS 40000 // points for an extra life
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
#ifdef IPHONE
|
||
|
void vibrateDevice();
|
||
|
#else
|
||
|
void vibrateDevice() {}
|
||
|
#endif
|
||
|
|
||
|
PUBLIC void PL_Damage( player_t *self, entity_t *attacker, int points )
|
||
|
{
|
||
|
|
||
|
if( self->playstate == ex_dead )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self->LastAttacker = attacker;
|
||
|
|
||
|
if( skill->value == gd_baby )
|
||
|
{
|
||
|
points >>= 2;
|
||
|
}
|
||
|
|
||
|
// vibe the phone
|
||
|
vibrateDevice();
|
||
|
|
||
|
// note the direction of the last hit for the directional blends
|
||
|
{
|
||
|
int dx = attacker->x - self->position.origin[0];
|
||
|
int dy = attacker->y - self->position.origin[1];
|
||
|
|
||
|
// probably won't ever have damage from self, but check anyway
|
||
|
if ( dx != 0 || dy != 0 ) {
|
||
|
float angle = atan2f( dy, dx );
|
||
|
float playerAngle = self->position.angle * 360.0f / (float)ANG_360;
|
||
|
float deltaAngle;
|
||
|
angle = angle * 180.0f / M_PI;
|
||
|
if ( angle < 0 ) {
|
||
|
angle = 360 + angle;
|
||
|
}
|
||
|
deltaAngle = angle - playerAngle;
|
||
|
if ( deltaAngle > 180 ) {
|
||
|
deltaAngle = deltaAngle - 360;
|
||
|
}
|
||
|
if ( deltaAngle < -180 ) {
|
||
|
deltaAngle = 360 + deltaAngle;
|
||
|
}
|
||
|
// Com_Printf( "damage: player angle: %4.0f shotAngle: %4.0f deltaAngle:%4.0f\n", playerAngle, angle, deltaAngle );
|
||
|
if ( deltaAngle > 40 ) {
|
||
|
iphoneSetAttackDirection( 1 );
|
||
|
} else if ( deltaAngle < -40 ) {
|
||
|
iphoneSetAttackDirection( -1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// do everything else but subtract health in god mode, to ease
|
||
|
// testing of damage feedback
|
||
|
if( !(self->flags & FL_GODMODE) )
|
||
|
{
|
||
|
self->health -= points;
|
||
|
}
|
||
|
|
||
|
if( self->health <= 0 )
|
||
|
{
|
||
|
// dead
|
||
|
self->health = 0;
|
||
|
self->playstate = ex_dead;
|
||
|
|
||
|
Sound_StartSound( NULL, 0, CHAN_BODY, Sound_RegisterSound( "lsfx/009.wav" ), 1, ATTN_NORM, 0 );
|
||
|
}
|
||
|
|
||
|
// red screen flash
|
||
|
iphoneStartDamageFlash( points );
|
||
|
|
||
|
// stop the happy grin face if shot before it times out
|
||
|
Player.face_gotgun = false;
|
||
|
|
||
|
// make BJ's eyes bulge on huge hits
|
||
|
if( points > 30 && Player.health != 0 )
|
||
|
{
|
||
|
Player.face_ouch = true;
|
||
|
Player.facecount = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns: returns true if player needs this health.
|
||
|
|
||
|
Notes:
|
||
|
gives player some HP
|
||
|
max can be:
|
||
|
0 - natural player's health limit (100 or 150 with augment)
|
||
|
>0 - indicates the limit
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC _boolean PL_GiveHealth( player_t *self, int points, int max )
|
||
|
{
|
||
|
if( max == 0 )
|
||
|
{
|
||
|
max = (self->items & ITEM_AUGMENT) ? 150 : 100;
|
||
|
}
|
||
|
|
||
|
if( self->health >= max )
|
||
|
{
|
||
|
return false; // doesn't need this health
|
||
|
}
|
||
|
|
||
|
self->health += points;
|
||
|
|
||
|
if( self->health > max )
|
||
|
{
|
||
|
self->health = max;
|
||
|
}
|
||
|
|
||
|
Player.face_gotgun = false;
|
||
|
|
||
|
return true; // took it
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns: returns true if player needs this ammo
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC _boolean PL_GiveAmmo( player_t *self, int type, int ammo )
|
||
|
{
|
||
|
int max_ammo[ AMMO_TYPES ] = { 99 };
|
||
|
int max;
|
||
|
|
||
|
max = max_ammo[ type ];
|
||
|
if( self->items & ITEM_BACKPACK )
|
||
|
{
|
||
|
max *= 2;
|
||
|
}
|
||
|
|
||
|
if( self->ammo[ type ] >= max )
|
||
|
{
|
||
|
return false; // don't need
|
||
|
}
|
||
|
|
||
|
if( ! self->ammo[ type ] && ! self->attackframe ) // knife was out
|
||
|
{
|
||
|
self->weapon = self->pendingweapon;
|
||
|
}
|
||
|
|
||
|
self->ammo[ type ] += ammo;
|
||
|
if( self->ammo[ type ] > max )
|
||
|
{
|
||
|
self->ammo[ type ] = max;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_GiveWeapon( player_t *self, int weapon )
|
||
|
{
|
||
|
unsigned itemflag;
|
||
|
|
||
|
PL_GiveAmmo( self, AMMO_BULLETS, 6 ); // give some ammo with a weapon
|
||
|
|
||
|
itemflag = ITEM_WEAPON_1 << weapon;
|
||
|
if( self->items & itemflag )
|
||
|
{
|
||
|
return; // player owns this weapon
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->items |= itemflag;
|
||
|
if ( self->weapon < weapon ) { // don't switch if already using better weapon
|
||
|
self->weapon = self->pendingweapon = weapon;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_GiveLife( player_t *self )
|
||
|
{
|
||
|
if( self->lives < 9 )
|
||
|
{
|
||
|
self->lives++;
|
||
|
}
|
||
|
|
||
|
Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "lsfx/044.wav" ), 1, ATTN_NORM, 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_GivePoints( player_t *self, W32 points )
|
||
|
{
|
||
|
self->score += points;
|
||
|
while( self->score >= self->next_extra )
|
||
|
{
|
||
|
self->next_extra += EXTRAPOINTS;
|
||
|
PL_GiveLife( self );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_GiveKey( player_t *self, int key )
|
||
|
{
|
||
|
self->items |= ITEM_KEY_1 << key;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function: Set up player for the new game
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_NewGame( player_t *self )
|
||
|
{
|
||
|
memset( self, 0, sizeof( player_t ) );
|
||
|
|
||
|
self->health = 100;
|
||
|
self->ammo[ AMMO_BULLETS ] = 16; // JDC: changed for iphone 8;
|
||
|
self->lives = 3;
|
||
|
|
||
|
self->weapon = self->pendingweapon = WEAPON_PISTOL;
|
||
|
self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2;
|
||
|
self->next_extra = EXTRAPOINTS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function: Set up player for level transition
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC void PL_NextLevel( player_t *self )
|
||
|
{
|
||
|
self->old_score = self->score;
|
||
|
self->attackcount = self->attackframe = self->weaponframe = 0;
|
||
|
self->flags = 0;
|
||
|
|
||
|
self->items &= ~(ITEM_KEY_1 | ITEM_KEY_2 | ITEM_KEY_3 | ITEM_KEY_4);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-----------------------------------------------------------------------------
|
||
|
Function:
|
||
|
|
||
|
Parameters: self -[in] Player to respawn in game world.
|
||
|
|
||
|
Returns: returns false if no lives left
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
PUBLIC _boolean PL_Reborn( player_t *self )
|
||
|
{
|
||
|
#if 0 // removed game over from iphone version
|
||
|
if( --self->lives < 1 )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
self->health = 100;
|
||
|
self->ammo[ AMMO_BULLETS ] = 16; // JDC: changed for iphone 8;
|
||
|
self->score = self->old_score;
|
||
|
self->attackcount = 0;
|
||
|
self->attackframe = 0;
|
||
|
self->weaponframe = 0;
|
||
|
self->flags = 0;
|
||
|
|
||
|
self->weapon = self->pendingweapon = WEAPON_PISTOL;
|
||
|
self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2;
|
||
|
|
||
|
self->playstate = ex_playing;
|
||
|
|
||
|
return true;
|
||
|
}
|