doom-ios/code/iphone/iphone_loop.c

1850 lines
51 KiB
C
Raw Normal View History

/*
Copyright (C) 2009 Id Software, Inc.
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.
*/
#include "doomiphone.h"
2018-05-09 09:03:58 -05:00
#include "TargetConditionals.h"
playState_t playState;
#define OWNER_AUTOMAP (void *)0x123
int iphoneFrameNum;
int levelLoadFrameNum;
boolean iphoneTimeDemo;
char timeDemoResultString[80];
int timeDemoFrames;
int timeDemoStart;
// set to 1 when app is exiting to cause game thread to do a save game,
// which would not be safe to do from the event thread
volatile int saveOnExitState;
// console mode
int consoleActive;
// current touches latched from the system touches
int numTouches;
int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
// so we can detect button releases
int numPrevTouches;
int prevTouches[5][2];
float tilt; // -1.0 to 1.0
float tiltPitch;
#define MAX_TILT_HISTORY 64
float tiltHistory[MAX_TILT_HISTORY][4];
int tiltHistoryNum;
// pressing on the weapon brings up the weaponSelect overlay
boolean drawWeaponSelect;
int weaponSelected = -1;
pkTexture_t *arialFontTexture;
logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
int gameSocket;
int gameID;
int playerID;
int packetSequence; // for logging dropped packets and estimating latency
netFail_t netGameFailure; // set by asyncThread
netPlayer_t netPlayers[MAXPLAYERS];
netPeer_t netServer;
// set after each game tic if a usable line is in front of the player
boolean autoUseActive;
boolean respawnActive;
// if we haven't processed a game tic in a half second, draw the net problem icon
int lastGameProcessedTime;
// this flag lets us give a shotgun and some ammo after the player has respawned
boolean addGear;
extern bool inBackgroundProcess;
/*
=================================================================================
Touch Handling
With multiple draggable controls on screen, it is important to track touches
as continuous events, rather than discrete points, because we want to allow
a finger to start on a dragable control, but still function when it has
been dragged outside the original hot area, and not trigger anything else
as it wanders around.
I considered a Down / Dragged / Released interface, but especially with
threading, it was better to keep them as a set of structures that could
be looked at at different points in the game loop.
if touch goes down on a dragable control, it will be owned by that control until it
is released, even if it is dragged outside the original bounds.
If we support pinch for anything, a single control will own multiple touches.
Touches that aren't owned by a dragable control are free-roaming, and can hit buttons.
=================================================================================
*/
/*
Does not claim ownership or play any sounds.
The touch can be dragged in if it isn't owned by
another control.
*/
touch_t *TouchInBounds( int x, int y, int w, int h ) {
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->controlOwner ) {
continue; // already claimed
}
if ( !t->down ) {
continue; // not pressed
}
if ( t->x >= x && t->x < x + w
&& t->y >= y && t->y < y + h ) {
// JDS Debug
//printf("Touch HIT! %d vs. %d / %d vs. %d (%d %d)\n",x,t->x,y,t->y,w,h);
return t;
}
// JDS Debug
else {
//printf("Touch missed %d vs. %d / %d vs. %d (%d %d)\n",x,t->x,y,t->y,w,h);
}
}
return NULL;
}
// even a touch claimed by another control will count (fire button)
touch_t *AnyTouchInBounds( int x, int y, int w, int h ) {
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( !t->down ) {
continue; // not pressed
}
if ( t->x >= x && t->x < x + w
&& t->y >= y && t->y < y + h ) {
return t;
}
}
return NULL;
}
touch_t *UpdateHudTouch( ibutton_t *hud ) {
if ( hud->buttonFlags & ( BF_IGNORE | BF_HUDBUTTON ) ) {
// hud element isn't active
return NULL;
}
if ( !hud->touch ) {
// see if a free touch was just made in, or dragged into the bounds
// make the active boxes twice as large as the drawing bounds
int x = hud->x - ( hud->drawWidth >> 1 );
int y = hud->y - ( hud->drawHeight >> 1 );
int w = hud->drawWidth << 1;
int h = hud->drawHeight << 1;
hud->touch = TouchInBounds( x, y, w, h );
//printf("TIB: %d %d %d %d -> %s\n",x,y,w,h,(hud->touch == NULL) ? "none" : "found" );
if ( hud->touch ) {
// claim this touch so it won't activate anything else
hud->touch->controlOwner = hud;
if ( touchClick->value ) {
Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value ? 0.15f : 0.0f );
}
// save the initial touch position for auto-centering stcks
hud->downX = hud->touch->x;
hud->downY = hud->touch->y;
// Clamp it so that you are guaranteed to have a full range of motion.
// This means that a touch at the edge of the screen won't center it,
// but will instead cause immediate movement. This prevents the main
// drawback of the centering controls -- if you pressed down too close
// to the side, you wouldn't have full mobility that direction.
int width = hud->drawWidth / 2;
int height = hud->drawHeight / 2;
if ( hud->downX < width ) {
hud->downX = width;
}
if ( hud->downX + width > displaywidth ) {
hud->downX = displaywidth - width;
}
if ( hud->downY < height ) {
hud->downY = height;
}
if ( hud->downY > displayheight - height ) {
hud->downY = displayheight - height;
}
}
}
if ( hud->touch ) {
// see if the touch was released
if ( !hud->touch->down ) {
if ( touchClick->value ) {
Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value ? 0.15f : 0.0f );
}
hud->touch = NULL;
}
}
return hud->touch;
}
void SetButtonPics( ibutton_t *button, const char *picBase, const char *title, int x, int y ) {
button->texture = PK_FindTexture( picBase );
button->scale = 1.0f;
button->title = title;
2020-10-19 18:59:43 +02:00
button->x = x * ((float)SCREENWIDTH) / 480.0f;
button->y = y * ((float)SCREENHEIGHT) / 320.0f;
2020-10-19 18:59:43 +02:00
float xRatio = ((float)SCREENWIDTH) / 480.0f;
float yRatio = ((float)SCREENHEIGHT) / 320.0f;
float themin = MIN( xRatio, yRatio );
button->drawWidth = button->texture->textureData->srcWidth * themin;
button->drawHeight = button->texture->textureData->srcHeight * themin;
}
void SetButtonPicsAndSizes( ibutton_t *button, const char *picBase, const char *title, int x, int y, int w, int h ) {
SetButtonPics( button, picBase, title, x, y );
2020-10-19 18:59:43 +02:00
float xRatio = ((float)SCREENWIDTH) / 480.0f;
float yRatio = ((float)SCREENHEIGHT) / 320.0f;
float themin = MIN( xRatio, yRatio );
button->drawWidth = w * themin;
button->drawHeight = h * themin;
}
/*
==================
HandleButton
Plays enter / exit / action sounds, returns true if the
touch was released inside the bounds.
Touches can slide onto a button, they aren't required
to tap initially inside it.
Main menu buttons and the small in-game buttons are
done with this. Because these handle both drawing and
decision making, there is a frame of latency involved
versus splitting the decision making and drawing.
Returns true if the button should perform its action.
==================
*/
float buttonScaleStep = 0.01f;
float buttonScaleMin = 0.95f;
boolean HandleButton( ibutton_t *button ) {
if ( button->buttonFlags & BF_IGNORE ) {
return false;
}
// Hack
button->drawHeight = button->drawWidth;
if ( ( button->buttonFlags & BF_TRANSPARENT ) && !button->touch ) {
// draw half-transparent
glColor4f( 1, 1, 1, 0.5 );
} else if ( button->buttonFlags & BF_DIMMED ) {
// draw half-bright
glColor4f( 0.5, 0.5, 0.5, 1 );
} else {
glColor4f( 1, 1, 1, 1 );
}
if ( button->touch && !button->touch->down ) {
button->touch->controlOwner = NULL;
button->touch = NULL;
}
bool released = false;
if ( !(button->buttonFlags & BF_INACTIVE) ) {
if ( button->touch ) {
// see if the touch was dragged outside the button bounds
if ( button->touch->x < button->x || button->touch->x >= button->x + button->drawWidth
|| button->touch->y < button->y || button->touch->y >= button->y + button->drawHeight ) {
// dragged outside, don't trigger on release now
if ( button->buttonFlags & BF_SMALL_CLICK ) {
Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value ? 0.15f : 0.0f );
} else {
Sound_StartLocalSound( "iphone/baborted_01.wav" );
}
button->touch->controlOwner = NULL;
button->touch = NULL;
button->pressed = false;
}
}
if ( !button->touch ) {
if ( button->pressed ) {
// released inside the button, so do the action
if ( button->buttonFlags & BF_SMALL_CLICK ) {
Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value ? 0.15f : 0.0f );
} else {
Sound_StartLocalSound( "iphone/baction_01.wav" );
}
button->pressed = false;
released = true;
}
}
// see if a new touch went down or moved into the button
if ( !released && !button->touch ) {
button->twoFingerPress = false;
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->controlOwner ) {
continue; // already claimed
}
if ( !t->down ) {
continue;
}
if ( t->x >= button->x && t->x < button->x + button->drawWidth
&& t->y >= button->y && t->y < button->y + button->drawHeight ) {
if ( button->buttonFlags & BF_SMALL_CLICK ) {
Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value ? 0.15f : 0.0f );
} else {
Sound_StartLocalSound( "iphone/bdown_01.wav" );
}
button->touch = t;
button->pressed = true;
t->controlOwner = &button;
break;
}
}
}
}
// animate scale
if ( button->frameNum != iphoneFrameNum - 1 ) {
button->scale = 1.0f; // just came back to a menu
}
button->frameNum = iphoneFrameNum;
if ( button->touch && button->touch->down ) {
// check for a two-finger touch for alternate modes
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->controlOwner ) {
continue; // already claimed
}
if ( t == button->touch ) {
continue; // the primary touch
}
if ( !t->down ) {
continue;
}
if ( t->x >= button->x && t->x < button->x + button->drawWidth
&& t->y >= button->y && t->y < button->y + button->drawHeight ) {
button->twoFingerPress = true;
break;
}
}
// adjust the animated scale
button->scale -= buttonScaleStep;
if ( button->scale < buttonScaleMin ) {
button->scale = buttonScaleMin;
}
} else {
button->scale += buttonScaleStep;
if ( button->scale > 1.0f ) {
button->scale = 1.0f;
}
}
if ( button->buttonFlags & BF_GLOW ) {
// cycle through double-bright
float f = 0.75 + 0.25 * sin( 3.14159 * 2 * ( iphoneFrameNum & 31 ) / 32.0 );
glColor4f( f, f, f, 1 );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvf( GL_TEXTURE_ENV, GL_RGB_SCALE, 2.0 );
}
PK_StretchTexture( button->texture, button->x+button->drawWidth/2 - button->drawWidth/2 * button->scale,
button->y + button->drawHeight/2 - button->drawHeight/2 * button->scale,
button->drawWidth * button->scale, button->drawHeight * button->scale );
if ( button->buttonFlags & BF_GLOW ) {
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glColor4f( 1, 1, 1, 1 );
}
if ( button->title ) {
float length = StringFontWidth( button->title ) * 0.75;
float x = button->x + button->drawWidth/2 - length/2;
// don't push the text off the edge of the screen
if ( x < 0 ) {
x = 0;
} else if ( x + length > displaywidth ) {
x = displaywidth - length;
}
float y;
float textScale = 0.75;
if ( button->buttonFlags & BF_CENTERTEXT ) {
glColor4f( 1, 1, 1, 1 ); // !@# remove when we get a button background that doesn't need dimming
y = button->y + button->drawHeight / 2 + 8;
textScale *= button->scale; // animate text scale when centered
} else {
y = button->y + button->drawHeight + 16;
}
textScale *= screenResolutionScale * 2;
iphoneDrawText( x, y, textScale, button->title );
}
glColor4f( 1, 1, 1, 1 );
return released;
}
//=========================================================================
typedef struct {
unsigned short x0, y0, x1, y1;
float xoff, yoff, xadvance;
} GlyphRect;
#include "arialGlyphRects.h" // precalculated offsets in the font image
float StringFontWidth( const char *str ) {
float len = 0;
while ( *str ) {
int i = *str;
if ( i >= ' ' && i < 128 ) {
len += glyphRects[i-32].xadvance;
}
str++;
}
return len;
}
/*
==================
iphoneDrawText
Returns the width in pixels
==================
*/
float iphoneDrawText( float x, float y, float scale, const char *str ) {
float fx = x;
float fy = y;
PK_BindTexture( arialFontTexture );
glBegin( GL_QUADS );
while ( *str ) {
int i = *str;
if ( i >= ' ' && i < 128 ) {
GlyphRect *glyph = &glyphRects[i-32];
// the glyphRects don't include the shadow outline
float x0 = ( glyph->x0 - 1 ) / 256.0;
float y0 = ( glyph->y0 - 1 ) / 256.0;
float x1 = ( glyph->x1 + 2 ) / 256.0;
float y1 = ( glyph->y1 + 2 ) / 256.0;
float width = ( x1 - x0 ) * 256 * scale;
float height = ( y1 - y0 ) * 256 * scale;
float xoff = ( glyph->xoff - 1 ) * scale;
float yoff = ( glyph->yoff - 1 ) * scale;
2018-10-13 14:37:25 -05:00
// red would be awesome here but it's kinda hard to read
// glColor4f( 1, 0, 0, 1 );
glTexCoord2f( x0, y0 );
glVertex2f( fx + xoff, fy + yoff );
glTexCoord2f( x1, y0 );
glVertex2f( fx + xoff + width, fy + yoff );
glTexCoord2f( x1, y1 );
glVertex2f( fx + xoff + width, fy + yoff + height );
glTexCoord2f( x0, y1 );
glVertex2f( fx + xoff, fy + yoff + height );
// with our default texture, the difference is negligable
fx += glyph->xadvance * scale;
// fx += ceil(glyph->xadvance); // with the outline, ceil is probably the right thing
}
str++;
}
glEnd();
return fx - x;
}
/*
==================
iphoneCenterText
Returns the width in pixels
==================
*/
float iphoneCenterText( float x, float y, float scale, const char *str ) {
float l = StringFontWidth( str );
2020-10-19 18:59:43 +02:00
x *= ((float)SCREENWIDTH) / 480.0f;
y *= ((float)SCREENHEIGHT) / 320.0f;
x -= l * scale * 0.5 * screenResolutionScale * 2;
return iphoneDrawText( x, y, scale * screenResolutionScale * 2, str );
}
/*
==================
TouchDown
Checks all touches against a square
==================
*/
int TouchDown( int x, int y, int w, int h ) {
int i;
for ( i = 0 ; i < numTouches ; i++ ) {
if ( touches[i][0] >= x && touches[i][1] >= y
&& touches[i][0] < x + w && touches[i][1] < y + h ) {
return 1;
}
}
return 0;
}
/*
==================
TouchPressed
Requires that the touch be inside the bounds, and that it didn't seem to be
dragged in from out of the bounds in the previous frame.
==================
*/
int TouchPressed( int x, int y, int w, int h ) {
x *= ((float)displaywidth) / 480.0f;
y *= ((float)displayheight) / 320.0f;
w *= ((float)displaywidth) / 480.0f;
h *= ((float)displayheight) / 320.0f;
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( !t->down ) {
continue;
}
if ( t->controlOwner ) {
continue;
}
if ( t->stateCount != 1 ) {
// not just pressed
continue;
}
if ( t->x < x || t->x >= x + w
|| t->y < y || t->y >= y + h ) {
continue;
}
// just pressed this frame
return 1;
}
return 0;
}
/*
==================
TouchReleased
Perform an action when released in the box.
If not down this frame, but down the previous frame, it is released
==================
*/
int TouchReleased( int x, int y, int w, int h ) {
#if 0
// only check when the touch count just went down by one
if ( numTouches != numPrevTouches - 1 ) {
return 0;
}
int i;
int downPrev = 0;
int downNow = 0;
for ( i = 0 ; i < numPrevTouches ; i++ ) {
if ( prevTouches[i][0] >= x && prevTouches[i][1] >= y
&& prevTouches[i][0] < x + w && prevTouches[i][1] < y + h ) {
downPrev = 1;
break;
}
}
// see if not down this frame
for ( i = 0 ; i < numTouches ; i++ ) {
if ( touches[i][0] >= x && touches[i][1] >= y
&& touches[i][0] < x + w && touches[i][1] < y + h ) {
downNow = 1;
break;
}
}
if ( !downPrev ) {
if ( downNow ) {
Sound_StartLocalSound( "iphone/bdown_01.wav" );
}
// wasn't down the previous frame
return 0;
}
if ( downNow ) {
// still down
return 0;
}
if ( numTouches == numPrevTouches ) {
// finger dragged off
Sound_StartLocalSound( "iphone/baborted_01.wav" );
return 0;
}
// released
Sound_StartLocalSound( "iphone/baction_01.wav" );
return 1;
#else
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->down ) {
continue; // still pressed
}
if ( t->x >= x && t->x < x + w
&& t->y >= y && t->y < y + h ) {
if ( t->stateCount <= 1 ) {
// just released
Sound_StartLocalSound( "iphone/baction_01.wav" );
return 1;
}
}
}
return 0;
#endif
}
/*
==================
iphoneSet2D
==================
*/
void iphoneSet2D( void ) {
// note that GL thinks the iphone is always
// in portrait mode as far as the framebuffer
// is concerned.
2020-10-14 21:18:19 +02:00
int x = 0;
if (displaywidth > SCREENWIDTH) {
x = (displaywidth - SCREENWIDTH) / 2;
}
/* JDS proper fix for landscape orientation */
2020-10-14 21:18:19 +02:00
glViewport( x,0, displaywidth, displayheight );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnable( GL_TEXTURE_2D );
glDisable( GL_DEPTH_TEST );
glDisable( GL_SCISSOR_TEST );
glDisable( GL_FOG );
glDisable( GL_CULL_FACE );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glDisable( GL_ALPHA_TEST );
glColor4f( 1,1,1,1 );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrthof( 0, displaywidth, displayheight, 0, -99999, 99999 );
}
void iphoneTiltEvent( float *tilts ) {
int i;
int j;
int c;
float sum[3];
static float prevTime;
// we probably should mutex this, but I doubt it causes any problems
/*
if ( revLand->value ) {
tilts[1] = -tilts[1];
tilts[0] = -tilts[0];
}
*/
c = tiltAverages->value;
if ( c < 1 ) {
c = 1;
} else if ( c > MAX_TILT_HISTORY ) {
c = MAX_TILT_HISTORY;
}
// acc[0] - [2] are accelerometer values, ax[3] is the timestamp
for ( i = 0 ; i < 3 ; i++ ) {
tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][i] = tilts[i];
sum[i] = 0;
for ( j = 0 ; j < c ; j++ ) {
sum[i] += tiltHistory[(tiltHistoryNum-j)&(MAX_TILT_HISTORY-1)][i];
}
sum[i] /= c;
}
// save the timestamp for analysis and tap detection
tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][3] = tilts[3] - prevTime;
prevTime = tilts[3];
tiltHistoryNum++;
tilt = sum[1];
tiltPitch = sum[0];
// Com_Printf( "%4.2f %4.2f %4.2f\n", tilts[0], tilts[1], tilts[2] );
}
void ShowTilt() {
int i;
int axis = (int)showTilt->value - 1;
color4_t fillColor = { 255, 0, 0, 255 };
color4_t whiteColor = { 255, 255, 255, 255 };
color4_t nowColor = { 0, 255, 0, 255 };
float x;
if ( axis < 0 || axis > 2 ) {
return;
}
for ( i = 0 ; i < MAX_TILT_HISTORY ; i++ ) {
x = tiltHistory[(tiltHistoryNum-1-i)&(MAX_TILT_HISTORY-1)][axis] * ( 10 / 0.018168604 );
if ( x < 0 ) {
R_Draw_Fill( 240 + x, i*4, -x, 4, fillColor );
} else if ( x > 0 ) {
R_Draw_Fill( 240, i*4, x, 4, fillColor );
}
}
x = tilt * ( 10 / 0.018168604 );
if ( x < 0 ) {
R_Draw_Fill( 240 + x, i*4, -x, 4, nowColor );
} else if ( x > 0 ) {
R_Draw_Fill( 240, i*4, x, 4, nowColor );
}
R_Draw_Fill( 240, 0, 1, MAX_TILT_HISTORY*4, whiteColor );
}
void ShowTime() {
if ( !showTime->value ) {
return;
}
color4_t sleepColor = { 255, 0, 0, 255 };
color4_t activeColor = { 0, 255, 0, 255 };
color4_t swapColor = { 0, 0, 255, 255 };
color4_t ticColor = { 255, 255, 255, 255 };
for ( int i = 1 ; i < 30 ; i++ ) {
logTime_t *lt = &loggedTimes[(iphoneFrameNum - i ) & (MAX_LOGGED_TIMES-1)];
int sleepTime = ( lt->afterSleep - lt->enterFrame ) >> 7;
int activeTime = ( lt->beforeSwap - lt->afterSleep ) >> 7;
int swapTime = ( lt->afterSwap - lt->beforeSwap ) >> 7;
R_Draw_Fill( 0, i * 4, activeTime, 2, activeColor );
R_Draw_Fill( activeTime, i * 4, swapTime, 2, swapColor );
R_Draw_Fill( activeTime + swapTime, i * 4, sleepTime, 2, sleepColor );
R_Draw_Fill( 480 - lt->numGameTics * 10, i * 4, lt->numGameTics * 10, 2, ticColor );
R_Draw_Fill( 480 - lt->numPingTics * 10, i * 4+2, lt->numPingTics * 10, 2, swapColor );
}
}
/*
==================
iphoneHighlightPicWhenTouched
Draw transparent except when touched
=================
*/
void iphoneHighlightPicWhenTouched( pkTexture_t *texture, int x, int y, int w, int h ) {
if ( TouchDown( x, y, w, h ) ) {
glColor4f(1,1,1,1);
} else {
glColor4f(1,1,1,0.5);
}
PK_StretchTexture( texture, x, y, w, h );
glColor4f(1,1,1,1);
}
/*
==================
iphoneSetNotifyText
Notify text is a single centered line for "got a key", "found a secret", etc
==================
*/
char notifyText[128];
int notifyFrameNum;
void iphoneSetNotifyText( const char *str, ... ) {
va_list argptr;
if ( !messages->value ) {
// option to disable all the message prints
return;
}
va_start( argptr, str );
(void)vsnprintf( notifyText, sizeof( notifyText )-1, str, argptr );
va_end( argptr );
notifyFrameNum = iphoneFrameNum;
}
void iphoneDrawNotifyText() {
if ( notifyFrameNum == 0 ) {
return;
}
// display for three seconds, then fade over 0.3
float f = iphoneFrameNum - notifyFrameNum - 80;
if ( f < 0 ) {
f = 1.0;
} else {
f = 1.0 - f * 0.1f;
if ( f < 0 ) {
notifyFrameNum = 0;
return;
}
}
glColor4f( 1, 1, 1, f );
iphoneCenterText( 240, 16, 0.75, notifyText );
glColor4f( 1, 1, 1, 1 );
}
/*
==================
Rotor control
==================
*/
void iphoneDrawRotorControl( ibutton_t *hud ) {
if ( hud->buttonFlags & BF_IGNORE ) {
return;
}
pkTexture_t *tex = hud->texture;
PK_BindTexture( tex );
float cx = (hud->x + hud->drawWidth / 2);
float cy = (hud->y + hud->drawHeight / 2);
float as = sin( hud->drawState );
float ac = cos( hud->drawState );
float sz = (hud->drawWidth / 2);
float xv[2] = { sz*ac, sz*as };
float yv[2] = { -sz*as, sz*ac };
glColor4f( 1, 1, 1, 1 );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( 0.0f, 0.0f ); glVertex2f( (cx - xv[0] - yv[0]), (cy - xv[1] - yv[1]) );
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2f( (cx + xv[0] - yv[0]), (cy + xv[1] - yv[1]) );
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2f( (cx - xv[0] + yv[0]), (cy - xv[1] + yv[1]) );
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2f( (cx + xv[0] + yv[0]), (cy + xv[1] + yv[1]) );
/*
glTexCoord2f( 0.0f, 0.0f ); glVertex2f( cx - xv[0] - yv[0], cy - xv[1] - yv[1] );
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2f( cx + xv[0] - yv[0], cy + xv[1] - yv[1] );
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2f( cx - xv[0] + yv[0], cy - xv[1] + yv[1] );
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2f( cx + xv[0] + yv[0], cy + xv[1] + yv[1] );
*/
glEnd();
}
//===================================================================================
void iphoneDrawHudControl( ibutton_t *hud ) {
2018-05-09 09:03:58 -05:00
if (TARGET_OS_TV) {
return;
}
if ( hud->buttonFlags & BF_IGNORE ) {
return;
}
if ( !hud->texture ) {
return;
}
if ( hud->drawAsLimit ) {
// green tint when at maximum displacement
glColor4f(0.5,1,0.5,1);
} else if ( hud->touch || ( hud->buttonFlags & BF_DRAW_ACTIVE ) ) {
// red tint when active
glColor4f(1,0.5,0.5,1);
} else {
glColor4f(1,1,1,1);
}
if ( hud->scale <= 0 ) {
hud->scale = 1.0f;
}
float w = hud->drawWidth * hud->scale;
float h = hud->drawHeight * hud->scale;
float x = hud->x + ( hud->drawWidth - w ) * 0.5f;
float y = hud->y + ( hud->drawHeight - w ) * 0.5f;
if ( centerSticks->value && hud->touch ) {
// reposiition the control after each touch
x = hud->touch->x - w*0.5f;
y = hud->touch->y - h*0.5f;
}
PK_StretchTexture( hud->texture, x, y, w, h );
glColor4f(1,1,1,1);
}
bool NewTextButton( ibutton_t *b, const char *title, int x, int y, int w, int h ) {
if ( !b->texture ) {
const char *pic = (w>128 ? "iphone/long_string_box.tga" : "iphone/short_string_box.tga" );
SetButtonPicsAndSizes( b, pic, "", x, y, w, h );
b->buttonFlags = BF_DIMMED | BF_CENTERTEXT;
}
b->title = title;
return HandleButton( b );
}
/*
=====================================================================
Smart USE test -- determine if there is a usable line within range
of the player. This is done by the main thread after running a game tic,
the async thread just checks the flag, because calling the traverse
functions is not thread safe.
=====================================================================
*/
static boolean usableInRange;
boolean PTR_UseTestTraverse (intercept_t* in)
{
if (!in->d.line->special)
{
P_LineOpening (in->d.line);
if (openrange <= 0)
{
// can't use through a wall
return false;
}
// not a special line, but keep checking
return true;
}
player_t* player = &players[consoleplayer];
int side = 0;
if (P_PointOnLineSide (player->mo->x, player->mo->y, in->d.line) == 1)
side = 1;
// e6y
// b.m. side test was broken in boom201
if ((demoplayback ? (demover != 201) : (compatibility_level != boom_201_compatibility)))
if (side) //jff 6/1/98 fix inadvertent deletion of side test
return false;
if ( in->d.line->special == 76 ) {
// The button that opens the final door at E1M6 has a trigger line
// right in front of it, which causes the use button to go down without
// doing anything, and it gets held down as you close to the actual
// button, so it doesn't get activated. You could turn to face the wall
// to get the use button up, then turn back towards the button, but it
// feels totally broken. The correct solution would be to return false
// for all line specials that won't actually be player-usable, but I can't
// tell if that is a simple range or a huge scattered list. For now, this
// hack to ignore this particular line type solves the problem. We'll
// see if other levels have similar issues...
return false;
}
if ( in->d.line->special == 88 ) { // yellow door above plat on E1M6
return false;
}
// this is a reasonable target for use
usableInRange = true;
//WAS can't use for than one special line in a row
//jff 3/21/98 NOW multiple use allowed with enabling line flag
return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))?
true : false;
}
boolean P_TestUseLines()
{
int angle;
fixed_t x1;
fixed_t y1;
fixed_t x2;
fixed_t y2;
player_t* player = &players[consoleplayer];
if ( !player->mo ) {
return false; // at intermission
}
angle = player->mo->angle >> ANGLETOFINESHIFT;
x1 = player->mo->x;
y1 = player->mo->y;
x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
// itterate over the lines and run the callback function
usableInRange = false;
P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTestTraverse );
return usableInRange;
}
//=====================================================================
/*
==================
AutomapControls
This is strictly client-side, done in the game thread instead of the async thread
==================
*/
void AutomapControls() {
//------------------------
// automap controls
//------------------------
extern fixed_t m_x, m_y; // LL x,y window location on the map (map coords)
extern fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords)
// width/height of window on map (map coords)
extern fixed_t m_w;
extern fixed_t m_h;
// used by MTOF to scale from map-to-frame-buffer coords
extern fixed_t scale_mtof;
// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
extern fixed_t scale_ftom;
extern fixed_t min_scale_mtof; // used to tell when to stop zooming out
extern fixed_t max_scale_mtof; // used to tell when to stop zooming in
static int prevX = -1, prevY = -1;
// any touch not down in another control will
// drag-scroll and be claimed by the automap
int touchCount = 0;
touch_t *mapTouch[MAX_TOUCHES];
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->down ) {
if ( t->controlOwner == NULL || t->controlOwner == OWNER_AUTOMAP ) {
// claim it so dragging onto another control won't
// cause it to activate
t->controlOwner = OWNER_AUTOMAP;
mapTouch[touchCount] = t;
touchCount++;
}
}
}
if ( touchCount != 1 ) {
prevX = -1;
}
static int pinching;
if ( touchCount != 2 ) {
pinching = 0;
}
if ( touchCount == 1 ) {
// adjust the automap values, assume square aspect ratio
touch_t *t = mapTouch[0];
// drag position
if ( prevX == -1 ) {
prevX = t->x;
prevY = t->y;
}
m_x -= ( t->x - prevX ) * (float)m_w / displaywidth;
m_y += ( t->y - prevY ) * (float)m_w / displaywidth;
m_x2 = m_x + m_w;
m_y2 = m_y + m_h;
prevX = t->x;
prevY = t->y;
} else if ( touchCount == 2 ) {
// pinch scale
touch_t *t1 = mapTouch[0];
touch_t *t2 = mapTouch[1];
static float baseDist;
static float baseMtoF;
static int basem_w;
static int basem_h;
float dist = sqrt( (t2->x-t1->x)*(t2->x-t1->x)+(t2->y-t1->y)*(t2->y-t1->y) );
if ( !pinching ) {
pinching = 1;
baseDist = dist;
baseMtoF = scale_mtof;
basem_w = m_w;
basem_h = m_h;
}
scale_mtof = baseMtoF * dist / baseDist;
if ( scale_mtof < min_scale_mtof ) {
scale_mtof = min_scale_mtof;
dist = (float)min_scale_mtof * baseDist / baseMtoF;
} else if ( scale_mtof > max_scale_mtof ) {
scale_mtof = max_scale_mtof;
dist = (float)max_scale_mtof * baseDist / baseMtoF;
}
scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
float midx = (t2->x+t1->x)*0.5;
float midy = (t2->y+t1->y)*0.5;
float midxDoom = m_x + m_w * midx / displaywidth;
float midyDoom = m_y + m_w * midy / displaywidth;
m_w = basem_w * baseDist / dist;
m_h = basem_h * baseDist / dist;
m_x = midxDoom - m_w * midx / displaywidth;
m_y = midyDoom - m_w * midy / displaywidth;
m_x2 = m_x + m_w;
m_y2 = m_y + m_h;
}
}
void SwapBuffersAndTouches() {
// debug graphs
ShowTilt();
ShowTime();
ShowNet();
ShowSound();
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
if ( sysTouches[i].down && gameTouches[i].down ) {
sysTouches[i].controlOwner = gameTouches[i].controlOwner;
}
}
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].beforeSwap = SysIphoneMicroseconds();
SysIPhoneSwapBuffers();
int now = SysIphoneMicroseconds();
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSwap = now;
}
float weaponSelectDrawScale = 1.25f;
void DrawWeapon(int weaponlump, int x, int y, int w, int h, int lightlevel)
{
GLTexture *gltexture;
float fU1,fU2,fV1,fV2;
int x1,y1,x2,y2;
x *= ((float)displaywidth) / 480.0f;
y *= ((float)displayheight) / 320.0f;
w *= ((float)displaywidth) / 480.0f;
h *= ((float)displayheight) / 320.0f;
// force doom to rebind, since we have changed the active GL_TEXTURE_2D
last_gltexture = NULL;
gltexture=gld_RegisterPatch(firstspritelump+weaponlump, CR_DEFAULT);
if (!gltexture)
return;
float scaledWidth = gltexture->width * weaponSelectDrawScale;
float scaledHeight = gltexture->height * weaponSelectDrawScale; // JDS Hack
// pin the middle bottom of the patch to the middle bottom of
// the draw rectangle, then let everything else scale as needed
fU1=0;
fV1=0;
fU2=(float)gltexture->width/(float)gltexture->tex_width;
fV2=(float)gltexture->height/(float)gltexture->tex_height;
x1=x+(w-scaledWidth)*0.5;
x2=x1 + scaledWidth;
y1=y+h-scaledHeight;
y2=y+h;;
gld_BindPatch(gltexture, CR_DEFAULT);
glColor4f( lightlevel, lightlevel, lightlevel, 1 );
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(fU1, fV1); glVertex2f((float)(x1),(float)(y1));
glTexCoord2f(fU1, fV2); glVertex2f((float)(x1),(float)(y2));
glTexCoord2f(fU2, fV1); glVertex2f((float)(x2),(float)(y1));
glTexCoord2f(fU2, fV2); glVertex2f((float)(x2),(float)(y2));
glEnd();
glColor4f(1.0f,1.0f,1.0f,1.0f);
}
/*
==================
DrawWeaponSelect
==================
*/
static const char *weaponNames[9] = {
"fist",
"pistol",
"shotgun",
"chaingun",
"rockets",
"plasma",
"BFG",
"chainsaw",
"dblshotgun"
};
int weaponSprites[9] = {
SPR_PUNG,
SPR_PISG,
SPR_SHTG,
SPR_CHGG,
SPR_MISG,
SPR_PLSG,
SPR_BFGG,
SPR_SAWG,
SPR_SHT2
};
void DrawWeaponSelect() {
player_t *player = &players[consoleplayer];
for ( int i = wp_fist ; i <= wp_supershotgun ; i++ ) {
int bx = i % 3;
int by = i / 3;
color4_t color = { 0, 0, 255, 200 };
color4_t textColor = { 255, 255, 255, 255 };
boolean selectable = false;
int ammo = -1;
switch ( i ) {
case wp_pistol: ammo = player->ammo[am_clip]; break;
case wp_shotgun: ammo = player->ammo[am_shell]; break;
case wp_chaingun: ammo = player->ammo[am_clip]; break;
case wp_missile: ammo = player->ammo[am_misl]; break;
case wp_plasma: ammo = player->ammo[am_cell]; break;
case wp_bfg: ammo = player->ammo[am_cell]; if ( ammo < 40 ) ammo = 0; break;
case wp_supershotgun: ammo = player->ammo[am_shell]; if ( ammo < 2 ) ammo = 0; break;
// These cases were here originally - but they feel buggy.
/*
case wp_plasma: ammo = player->ammo[wp_plasma]; break;
case wp_bfg: ammo = player->ammo[wp_plasma]; if ( ammo < 40 ) ammo = 0; break;
case wp_supershotgun: ammo = player->ammo[wp_plasma]; if ( ammo < 2 ) ammo = 0; break;
*/
}
if ( !player->weaponowned[i] ) {
// don't have the weapon
color[0] = color[1] = color[2] = 50;
textColor[3] = 128;
} else {
// selectable
color[0] = 255; color[1] = 255; color[2] = 255; color[3] = 255;
selectable = true;
if ( ammo == 0 ) {
// have it, but out of ammo
color[0] = 255; color[1] = color[2] = 0;
textColor[3] = 128;
}
}
int x = bx * 160 + 20;
int y = by * 88;
int w = 120;
int h = 80;
float nx = x * ((float)displaywidth) / 480.0f;
float ny = y * ((float)displayheight) / 320.0f;
float nw = w * ((float)displaywidth) / 480.0f;
float nh = h * ((float)displayheight) / 320.0f;
if ( selectable && TouchDown( nx, ny, nw, nh ) ) {
color[0] = 128;
color[1] = color[2] = 128;
color[3] = 200;
}
glColor4ubv( color );
PK_StretchTexture( PK_FindTexture( "iphone/multi_backdrop.tga" ), nx, ny, nw, nh );
// R_Draw_Blend( x, y, w, h, color );
glColor4ubv( textColor );
iphoneCenterText( x + w/2, y+16, 0.75, weaponNames[i] );
// draw the weapon sprite full color if available or black if not
spritedef_t *sprdef = &sprites[weaponSprites[i]];
if ( sprdef->spriteframes ) { // restricted wads won't have all weapons
spriteframe_t *sprframe = &sprdef->spriteframes[0];
DrawWeapon( sprframe->lump[0] , x, y - 2, w, h, player->weaponowned[i] );
if ( selectable && TouchReleased( nx, ny, nw, nh ) ) {
drawWeaponSelect = false;
weaponSelected = i;
}
}
}
}
/*
==================
iphoneFrame
This is continuously called by the game thread main loop, any sleeping
is done explicitly. If the game isn't holding 30hz, it will be running
flat out with no sleeping at all.
==================
*/
void iphoneFrame() {
iphoneFrameNum++;
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numGameTics = 0;
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSleep =
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].enterFrame = SysIphoneMicroseconds();
//-------------------------------------------------
// grabs the console command under mutex.
//-------------------------------------------------
// execute a console command if one was typed
if ( consoleCommand[0] ) {
// send it a character at a time to the classic dooom cheat processing
// for idkfa, idclev, etc
for ( int i = 0 ; consoleCommand[i] != 0 ; i++ ) {
M_FindCheats( consoleCommand[i] );
}
// send it to the new concole command processing
Com_Printf( "%s\n", consoleCommand );
Cmd_ExecuteString( consoleCommand );
consoleCommand[0] = 0;
}
// move touches to prevTouches (old style use, remove...)
numPrevTouches = numTouches;
memcpy( prevTouches, touches, sizeof( prevTouches ) );
// process old style touches
numTouches = 0;
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
touch_t *t = &gameTouches[i];
if ( t->down ) {
touches[numTouches][0] = t->x;
touches[numTouches][1] = t->y;
numTouches++;
}
}
// go to the next demo if needed
if ( advancedemo ) {
if ( iphoneTimeDemo && timeDemoStart ) {
// go back to the menu after a timedemo
menuState = IPM_MAIN;
timeDemoStart = 0;
iphoneMainMenu();
return;
}
static int demoState;
players[consoleplayer].playerstate = PST_LIVE; /* not reborn */
advancedemo = usergame = paused = false;
gameaction = ga_nothing;
gamestate = GS_DEMOSCREEN;
static const char *demoNames[3] = { "demo1", "demo2", "demo3" };
G_DeferedPlayDemo( demoNames[demoState] );
if ( ++demoState == 3 ) {
demoState = 0;
}
}
if ( saveOnExitState == 1 ) {
printf( "SaveOnExitState == 1\n" );
if ( !netgame && !demoplayback && usergame && gamestate == GS_LEVEL ) {
G_SaveGame( 0, "quicksave" );
G_DoSaveGame(true);
}
saveOnExitState = 2;
return;
}
if ( saveOnExitState == 2 ) {
// the app is exiting
return;
}
//--------------------------------------------------------------------------------------
// game tic processing
//--------------------------------------------------------------------------------------
boolean runGame = false;
if( inBackgroundProcess ) {
return;
}
if ( menuState == IPM_GAME ) {
// don't run the game when in the menus
runGame = true;
}
if ( automapmode & am_active ) {
// Unlike PC Doom, don't run time when in the automap, since
// drawing the controls clutters the screen too much.
runGame = false;
}
if ( netgame ) {
// even when in the menus or automap, the tics must be processed if it is a net game
runGame = true;
}
if ( netGameFailure ) {
// consistancy failure or interruption
runGame = false;
}
// since we don't allow movement control in the automap,
// don't advance time.
if ( runGame ) {
int stopTic;
// block until the AsyncTic() has said we can run at least one frame,
// unless we are doing a flat-out timedemo run
if ( iphoneTimeDemo ) {
stopTic = gametic+1;
maketic = stopTic+1;
} else {
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].afterSleep = SysIphoneMicroseconds();
if ( localGameID == gameID ) {
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numPingTics = netPlayers[1].peer.currentPingTics;
} else {
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numPingTics = netServer.currentPingTics;
}
// On the server, we always want to execute all available tics.
// For a remote client, that would also give the minimum lag, but things are much
// smoother if they instead try to leave one buffer tic, unless that would
// leave the frame without running a single gametic.
stopTic = maketic;
if ( consoleplayer != 0 ) {
// we are a client, so try to leave a buffer frame
static const int COMMAND_BUFFER_SIZE = 1;
stopTic = maketic - COMMAND_BUFFER_SIZE;
if ( gametic >= stopTic ) {
// Ideally, clients should run 1 gametic per frame. But if the client is
// waiting on the server to deliver more tics, the client has to stall
// if it runs out of buffered tics.
const int idealTic = gametic + 1;
if ( idealTic < maketic ) {
stopTic = idealTic;
} else {
stopTic = maketic;
}
}
}
}
//---------------------------------
// run game tics
//---------------------------------
while( gametic < stopTic ) {
// run the gametic with all the player and monster logic
// this will extract netcmds[player][gametic%BACKUPTICS] for each player
// Com_Printf( "gametic %i\n", gametic );
G_Ticker();
// if we just respawned with add-gear, give items now
if ( addGear ) {
players[0].weaponowned[wp_shotgun] = true;
players[0].ammo[am_shell] = 20;
players[0].pendingweapon = wp_shotgun;
addGear = false;
}
// show the network trouble icon if we haven't run a game tic in a long time
lastGameProcessedTime = SysIphoneMilliseconds();
// see if there is a usable line in front of the player right now,
// which can be picked up by the asyncTic
autoUseActive = P_TestUseLines();
// generate the checksum for consistency failure testing
P_Checksum(gametic);
// on to the next tic
loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].numGameTics++;
gametic++;
// this probably doesn't need to be tic-synced, but it doesn't hurt
if (players[displayplayer].mo) {
// move positional sounds and free up channels that have completed
2018-05-09 09:03:58 -05:00
//GUS temporarily removed
//S_UpdateSounds(players[displayplayer].mo);
}
}
}
if ( consoleActive ) {
iphoneSet2D();
// FIXME: actually draw a console...
// Console_Draw();
SwapBuffersAndTouches();
return;
}
if ( menuState != IPM_GAME ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
iphoneSet2D();
if ( menuState == IPM_HUDEDIT ) {
HudEditFrame();
}
SwapBuffersAndTouches();
return;
}
//------------------
// any touch release during demo playback goes to main menu
//------------------
if ( !usergame && !iphoneTimeDemo ) {
if ( numTouches == 0 && numPrevTouches == 1 ) {
menuState = IPM_MAIN;
iphoneMainMenu();
}
}
if( inBackgroundProcess ) {
return;
}
// Draw the game screen. This can also be called by the pacifier update
// during level loading.
iphoneDrawScreen();
// If we just loaded a level, do the texture precaching after we
// have drawn and displayed the first frame, so the user has
// something to look at while it is loading.
// if ( false ) { // iphoneFrameNum == levelLoadFrameNum + 1 ) {
// int start = SysIphoneMilliseconds();
// gld_Precache();
// int end = SysIphoneMilliseconds();
// Com_Printf( "%3.1f seconds to gld_Precache()\n", (end-start)*0.001f );
// timeDemoStart = end;
// timeDemoFrames = 0;
// }
}
int pacifierCycle;
int pacifierTime;
void iphonePacifierUpdate() {
// Only update a few times a second so it doesn't actually make it
// take longer to load.
int now = SysIphoneMilliseconds();
if ( now < pacifierTime + 200 ) {
return;
}
pacifierTime = now;
pacifierCycle = ( pacifierCycle + 1 ) & 7;
// Is this causing a massive slowdown while precaching?
iphoneDrawScreen();
}
/*
==================
iphoneDrawScreen
Called by the main loop and also during pacifier update when preloading textures
==================
*/
void iphoneDrawScreen() {
// tell the classic code about turning the status bar on or off
if ( statusBar->modified ) {
statusBar->modified = false;
2018-05-09 09:03:58 -05:00
if ( statusBar->value ) {
R_SetViewSize( 10 );
hud_displayed = 0;
hud_active = 0;
hud_distributed = 0;
} else {
R_SetViewSize( 11 );
hud_displayed = 1;
hud_active = 2;
hud_distributed = 1;
}
}
//------------------------------------------------
// Update display with current state.
//------------------------------------------------
// force doom to rebind, since we have changed the active GL_TEXTURE_2D
last_gltexture = NULL;
D_Display();
iphoneSet2D();
//-----------------------------------
// draw 2D overlays for game screen
//-----------------------------------
if ( automapmode & am_active ) {
if ( HandleButton( &huds.map ) ) {
AM_Stop();
}
if ( !netgame ) { // no save game option during net play
static ibutton_t btnSave;
if ( NewTextButton( &btnSave, "SAVE", 480-64, 0, 64, 32 ) ) {
G_SaveGame( 0, "ManualSave" );
G_DoSaveGame(true);
AM_Stop();
}
}
// update scroll and zoom after the buttons have potentially claimed a touch
AutomapControls();
} else if ( iphoneFrameNum == levelLoadFrameNum + 1 ) {
// don't draw hud elements during the precache
color4_t black = { 0, 0, 0, 255 };
R_Draw_Fill( 0, 0, 480, 320, black );
// draw rotating pacifier icon
PK_BindTexture( PK_FindTexture( "iphone/loading.tga" ) );
glColor4f( 1, 1, 1, 1 );
float cx = 240 * ((float)displaywidth) / 480.0f;
float cy = 160 * ((float)displayheight) / 320.0f;
float as = sin( pacifierCycle * M_PI / 4 );
float ac = cos( pacifierCycle * M_PI / 4 );
float sz = 64;
float xv[2] = { sz*ac, sz*as };
float yv[2] = { -sz*as, sz*ac };
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( 0, 0 ); glVertex2f( cx - xv[0] - yv[0], cy - xv[1] - yv[1] );
glTexCoord2f( 1, 0 ); glVertex2f( cx + xv[0] - yv[0], cy + xv[1] - yv[1] );
glTexCoord2f( 0, 1 ); glVertex2f( cx - xv[0] + yv[0], cy - xv[1] + yv[1] );
glTexCoord2f( 1, 1 ); glVertex2f( cx + xv[0] + yv[0], cy + xv[1] + yv[1] );
glEnd();
} else {
// normal gameplay
if ( gamestate == GS_FINALE ) {
// leave the main menu button on the screen so they can start the
// next episode
if ( HandleButton( &huds.menu ) ) {
iphonePauseMusic();
menuState = IPM_MAIN;
iphoneMainMenu();
}
} else if ( !menuactive && !demoplayback && usergame && gamestate == GS_LEVEL ) {
if ( players[consoleplayer].playerstate == PST_DEAD ) {
// when dead, only show the main menu con and the
// respawn / load game icons
2018-05-09 09:03:58 -05:00
#if !TARGET_OS_TV
if ( HandleButton( &huds.menu ) ) {
iphonePauseMusic();
menuState = IPM_MAIN;
iphoneMainMenu();
}
2018-05-09 09:03:58 -05:00
#endif
if ( !deathmatch && !netgame ) {
2018-05-09 09:03:58 -05:00
// for now we're going to not draw these on the screen for the TV version
// all you can do is respawn with the USE button (A)
#if TARGET_OS_TV
static ibutton_t btnRespawn;
SetButtonPicsAndSizes( &btnRespawn, "iphone/respawn.tga", "Press A to restart", 240 - 48, 80, 96, 96 );
if ( HandleButton( &btnRespawn ) ) {
// players[consoleplayer].playerstate = PST_REBORN;
}
#else
static ibutton_t btnSaved;
static ibutton_t btnRespawn;
static ibutton_t btnGear;
if ( !btnSaved.texture ) {
// initial setup
SetButtonPicsAndSizes( &btnSaved, "iphone/load_saved.tga", "Saved game", 240 - 48 - 96 - 48, 80, 96, 96 );
SetButtonPicsAndSizes( &btnRespawn, "iphone/respawn.tga", "Restart", 240 - 48, 80, 96, 96 );
SetButtonPicsAndSizes( &btnGear, "iphone/respawn_gear.tga", "Add gear", 240 + 48 + 48, 80, 96, 96 );
}
if ( HandleButton( &btnSaved ) ) {
StartSaveGame();
}
if ( HandleButton( &btnRespawn ) ) {
players[consoleplayer].playerstate = PST_REBORN;
}
if ( HandleButton( &btnGear ) ) {
players[consoleplayer].playerstate = PST_REBORN;
addGear = true;
}
2018-05-09 09:03:58 -05:00
#endif
} else {
static ibutton_t btnNetRespawn;
if ( !btnNetRespawn.texture ) {
// initial setup
SetButtonPicsAndSizes( &btnNetRespawn, "iphone/respawn.tga", "Respawn", 240 - 96/2, 90, 96, 96 );
}
if ( HandleButton( &btnNetRespawn ) ) {
// this will cause the next command sent to include a use action,
// then clear this flag
respawnActive = true;
}
}
} else if ( drawWeaponSelect ) {
DrawWeaponSelect();
} else {
if ( drawControls->value ) {
iphoneDrawHudControl( &huds.forwardStick );
iphoneDrawHudControl( &huds.sideStick );
iphoneDrawHudControl( &huds.turnStick );
iphoneDrawRotorControl( &huds.turnRotor );
// iphoneDrawHudControl( &huds.fire );
}
2018-05-09 09:03:58 -05:00
if (!TARGET_OS_TV) {
if ( HandleButton( &huds.menu ) ) {
iphonePauseMusic();
menuState = IPM_MAIN;
iphoneMainMenu();
}
if ( HandleButton( &huds.map ) ) {
AM_Start();
}
if ( HandleButton( &huds.fire ) ) {
}
}
if ( netgame ) {
#if 0
static ibutton_t btnPlayer;
if ( NewTextButton( &btnPlayer, "PLAYER", 0, 48, 100, 32 ) ) {
displayplayer ^= 1;
}
static ibutton_t btnNet;
if ( NewTextButton( &btnNet, "NET", 240-32, 0, 80, 32 ) ) {
showNet->value = !showNet->value;
}
static ibutton_t btnThrottle;
const char *title = throttle->value ? "Throttle:ON" : "Throttle:OFF";
if ( NewTextButton( &btnThrottle, title, 0, 0, 128, 32 ) ) {
throttle->value = !throttle->value;
}
#endif
}
#if 0
static ibutton_t btnSpeeds;
if ( NewTextButton( &btnSpeeds, "SPEEDS", 240-32, 0, 80, 32 ) ) {
showTime->value = !showTime->value;
}
static ibutton_t btnTest;
if ( NewTextButton( &btnTest, "TEST", 0, 48, 70, 32 ) ) {
testNewRenderer = !testNewRenderer;
}
#endif
}
// notify text last, so it is always on top and legible
iphoneDrawNotifyText();
// not getting network tics
const int timeSinceLastProcessed = SysIphoneMilliseconds() - lastGameProcessedTime;
if ( timeSinceLastProcessed > 800 ) { // Origianlly was 500, let's be a little more lenient for internet play.
PK_StretchTexture( PK_FindTexture("iphone/multiplay.tga"), 240 - 96/2, 90, 96, 96 );
}
// draw the little graph in the upper right corner
#ifndef NDEBUG
if ( netgame ) {
ShowMiniNet();
}
#endif
}
}
// update timedemo display
if ( iphoneTimeDemo ) {
if ( iphoneFrameNum > levelLoadFrameNum + 1 ) {
timeDemoFrames++;
float fps = timeDemoFrames * 1000.0f / ( SysIphoneMilliseconds() - timeDemoStart );
sprintf( timeDemoResultString, "%5.1f fps", fps );
} else {
strcpy( timeDemoResultString, "TIMEDEMO" );
}
iphoneCenterText( 240, 80, 0.75, timeDemoResultString );
}
// time how long the GPU takes to render the entire frame
if ( glfinish->value ) {
int start = SysIphoneMicroseconds();
glFinish();
int end = SysIphoneMicroseconds();
Com_Printf( "%4.1f msec for glFinish()\n", ( end - start ) * 0.001f );
}
SwapBuffersAndTouches();
}