2018-01-24 08:16:52 +01:00
/*
Copyright ( C ) 2018 Parallel Realities
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 "hub.h"
static void unlockAllLevels ( void ) ;
static void unlockMission ( char * id ) ;
static void loadMissions ( void ) ;
static void unlockNeighbouringMission ( HubMission * sourceMission ) ;
static int missionComparator ( const void * a , const void * b ) ;
2018-02-16 23:50:55 +01:00
static HubMission * getMissionAt ( int x , int y ) ;
2018-02-17 08:56:29 +01:00
static void drawMissions ( void ) ;
static void drawInfoBar ( void ) ;
static void drawMissionInfo ( void ) ;
2018-02-16 23:50:55 +01:00
static void logic ( void ) ;
static void draw ( void ) ;
2018-02-17 17:52:51 +01:00
static void startMission ( void ) ;
static void cancel ( void ) ;
static void options ( void ) ;
static void stats ( void ) ;
static void trophies ( void ) ;
static void quit ( void ) ;
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats ( void ) ;
2018-02-18 08:59:14 +01:00
static void doCursor ( void ) ;
static void doMissionSelect ( void ) ;
static void doMissionInfo ( void ) ;
2018-02-18 23:22:05 +01:00
static void drawHudWidgets ( void ) ;
2018-02-19 20:13:29 +01:00
static void awardMissionTrophies ( void ) ;
2018-02-21 09:13:28 +01:00
static void returnFromOptions ( void ) ;
2018-02-22 20:01:16 +01:00
void destroyHub ( void ) ;
2018-01-24 08:16:52 +01:00
static HubMission hubMissionHead ;
static HubMission * hubMissionTail ;
2018-02-16 23:50:55 +01:00
static HubMission * selectedMission ;
static Atlas * worldMap ;
static Atlas * alert ;
static Atlas * clouds ;
2018-02-17 17:52:51 +01:00
static Sprite * cursorSpr ;
2018-02-17 08:56:29 +01:00
static Sprite * keySprites [ MAX_KEY_TYPES ] ;
2018-02-16 23:50:55 +01:00
static Texture * atlasTexture ;
static int completedMissions ;
static int numMissions ;
2018-02-18 12:25:14 +01:00
static int unlockedMissions ;
2018-02-17 17:52:51 +01:00
static PointF cursor ;
2018-02-16 23:50:55 +01:00
static float blipSize ;
static float blipValue ;
2018-02-19 09:28:42 +01:00
static int showing ;
2018-02-18 15:22:45 +01:00
static PointF cloudPos ;
2018-01-24 08:16:52 +01:00
void initHub ( void )
{
2018-02-18 12:25:14 +01:00
int unlockTeeka , i ;
2018-02-16 23:50:55 +01:00
HubMission * mission , * teeka ;
2018-01-24 08:16:52 +01:00
Tuple * t ;
2018-02-18 08:59:14 +01:00
startSectionTransition ( ) ;
2018-01-24 08:16:52 +01:00
memset ( & hubMissionHead , 0 , sizeof ( HubMission ) ) ;
hubMissionTail = & hubMissionHead ;
2018-02-17 08:56:29 +01:00
memset ( & keySprites , 0 , sizeof ( Sprite * ) * MAX_KEY_TYPES ) ;
2018-02-18 10:29:37 +01:00
selectedMission = NULL ;
2018-02-18 08:59:14 +01:00
loadMusic ( " music/61321__mansardian__news-background.ogg " ) ;
2018-02-16 23:50:55 +01:00
atlasTexture = getTexture ( " gfx/atlas/atlas.png " ) ;
worldMap = getImageFromAtlas ( " gfx/hub/worldMap.jpg " ) ;
alert = getImageFromAtlas ( " gfx/hub/alert.png " ) ;
clouds = getImageFromAtlas ( " gfx/hub/clouds.png " ) ;
2018-02-17 17:52:51 +01:00
cursorSpr = getSprite ( " Cursor " ) ;
2018-02-16 23:50:55 +01:00
2018-02-17 08:56:29 +01:00
for ( i = 0 ; i < MAX_KEY_TYPES ; i + + )
{
if ( game . keys [ i ] . value . i > 0 )
{
keySprites [ i ] = getSprite ( game . keys [ i ] . key ) ;
}
}
2018-02-17 17:52:51 +01:00
cursor . x = SCREEN_WIDTH / 2 ;
cursor . y = SCREEN_HEIGHT / 2 ;
getWidget ( " startMission " , " mission " ) - > action = startMission ;
getWidget ( " cancel " , " mission " ) - > action = cancel ;
getWidget ( " options " , " hub " ) - > action = options ;
getWidget ( " stats " , " hub " ) - > action = stats ;
getWidget ( " trophies " , " hub " ) - > action = trophies ;
getWidget ( " quit " , " hub " ) - > action = quit ;
2018-02-17 08:56:29 +01:00
2018-02-19 23:32:14 +01:00
getWidget ( " ok " , " stats " ) - > action = returnFromTrophyStats ;
getWidget ( " ok " , " trophies " ) - > action = returnFromTrophyStats ;
2018-02-19 09:28:42 +01:00
2018-01-24 08:16:52 +01:00
loadMissions ( ) ;
if ( dev . cheatLevels )
{
unlockAllLevels ( ) ;
}
2018-02-16 23:50:55 +01:00
numMissions = 0 ;
2018-01-24 08:16:52 +01:00
unlockedMissions = 0 ;
2018-02-16 23:50:55 +01:00
completedMissions = 0 ;
unlockTeeka = 1 ;
blipValue = 0 ;
2018-02-19 09:28:42 +01:00
showing = SHOW_NONE ;
2018-02-18 12:25:14 +01:00
cursor . x = SCREEN_WIDTH / 2 ;
cursor . y = SCREEN_HEIGHT / 2 ;
SDL_WarpMouseInWindow ( app . window , cursor . x , cursor . y ) ;
2018-01-24 08:16:52 +01:00
for ( t = game . missionStatusHead . next ; t ! = NULL ; t = t - > next )
{
if ( t - > value . i ! = MS_INCOMPLETE )
{
unlockedMissions + + ;
}
2018-02-16 23:50:55 +01:00
if ( t - > value . i = = MS_COMPLETE )
{
completedMissions + + ;
}
2018-01-24 08:16:52 +01:00
}
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
2018-02-18 12:25:14 +01:00
if ( mission - > unlockCount = = 0 | | dev . cheatLevels )
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
unlockMission ( mission - > id ) ;
}
mission - > status = getMissionStatus ( mission - > id ) ;
if ( strcmp ( mission - > id , " teeka " ) = = 0 )
{
teeka = mission ;
2018-01-24 08:16:52 +01:00
}
2018-02-16 23:50:55 +01:00
else if ( mission - > status = = MS_LOCKED )
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
unlockTeeka = 0 ;
2018-01-24 08:16:52 +01:00
}
2018-02-16 23:50:55 +01:00
numMissions + + ;
2018-01-24 08:16:52 +01:00
}
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
if ( mission - > status = = MS_COMPLETE | | mission - > status = = MS_MISSING_HEART_CELL | | mission - > status = = MS_PARTIAL )
{
unlockNeighbouringMission ( mission ) ;
}
if ( mission - > status = = MS_MISSING_HEART_CELL )
{
2018-01-28 17:14:17 +01:00
STRNCPY ( mission - > description , _ ( " All objectives for this misson have been completed. However, there is a Cell or a Heart left to find. See if you can locate it. " ) , MAX_DESCRIPTION_LENGTH ) ;
2018-01-24 08:16:52 +01:00
}
}
2018-02-18 12:25:14 +01:00
/* keep our unlock count in sync */
unlockedMissions = 0 ;
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
if ( mission - > status ! = MS_LOCKED )
{
unlockedMissions + + ;
}
}
2018-02-17 17:52:51 +01:00
if ( ! unlockTeeka )
2018-02-16 23:50:55 +01:00
{
2018-02-17 17:52:51 +01:00
teeka - > status = MS_LOCKED ;
2018-02-16 23:50:55 +01:00
}
2018-02-19 20:13:29 +01:00
awardMissionTrophies ( ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 15:22:45 +01:00
cloudPos . x = randF ( ) - randF ( ) ;
cloudPos . y = randF ( ) - randF ( ) ;
2018-02-16 23:50:55 +01:00
app . delegate . logic = & logic ;
app . delegate . draw = & draw ;
2018-02-18 08:59:14 +01:00
2018-02-20 13:31:41 +01:00
app . restrictTrophyAlert = 0 ;
2018-02-18 08:59:14 +01:00
playMusic ( 1 ) ;
endSectionTransition ( ) ;
2018-02-16 23:50:55 +01:00
}
static void logic ( void )
{
blipValue + = 0.1 ;
blipSize = 64 + ( sin ( blipValue ) * 16 ) ;
2018-02-18 15:22:45 +01:00
scrollBackground ( cloudPos . x , cloudPos . y ) ;
2018-02-17 08:56:29 +01:00
animateSprites ( ) ;
2018-02-16 23:50:55 +01:00
2018-02-19 09:28:42 +01:00
switch ( showing )
2018-02-17 08:56:29 +01:00
{
2018-02-19 09:28:42 +01:00
case SHOW_NONE :
doCursor ( ) ;
if ( selectedMission = = NULL )
{
doMissionSelect ( ) ;
}
else
{
doMissionInfo ( ) ;
}
break ;
2018-02-18 23:22:05 +01:00
2018-02-19 09:28:42 +01:00
case SHOW_WIDGETS :
doWidgets ( ) ;
if ( app . keyboard [ SDL_SCANCODE_ESCAPE ] )
{
2018-02-25 13:23:00 +01:00
playSound ( SND_MENU_BACK , 0 ) ;
2018-02-19 09:28:42 +01:00
showing = SHOW_NONE ;
app . keyboard [ SDL_SCANCODE_ESCAPE ] = 0 ;
}
break ;
case SHOW_STATS :
doStats ( ) ;
if ( app . keyboard [ SDL_SCANCODE_ESCAPE ] )
{
2018-02-25 13:23:00 +01:00
playSound ( SND_MENU_BACK , 0 ) ;
2018-02-19 23:32:14 +01:00
returnFromTrophyStats ( ) ;
2018-02-19 09:28:42 +01:00
}
break ;
case SHOW_TROPHIES :
2018-02-19 23:32:14 +01:00
doTrophies ( ) ;
if ( app . keyboard [ SDL_SCANCODE_ESCAPE ] )
{
2018-02-25 13:23:00 +01:00
playSound ( SND_MENU_BACK , 0 ) ;
2018-02-19 23:32:14 +01:00
returnFromTrophyStats ( ) ;
}
2018-02-19 09:28:42 +01:00
break ;
default :
break ;
2018-02-16 23:50:55 +01:00
}
2018-02-18 08:59:14 +01:00
}
2018-02-17 17:52:51 +01:00
2018-02-18 08:59:14 +01:00
static void doCursor ( void )
{
2018-02-17 17:52:51 +01:00
if ( app . mouse . dx ! = 0 | | app . mouse . dy ! = 0 )
{
cursor . x = app . mouse . x ;
cursor . y = app . mouse . y ;
}
if ( isControl ( CONTROL_UP ) | | app . keyboard [ SDL_SCANCODE_UP ] )
{
cursor . y - = CURSOR_SPEED ;
2018-02-18 12:25:14 +01:00
SDL_WarpMouseInWindow ( app . window , cursor . x , cursor . y ) ;
2018-02-17 17:52:51 +01:00
}
if ( isControl ( CONTROL_DOWN ) | | app . keyboard [ SDL_SCANCODE_DOWN ] )
{
cursor . y + = CURSOR_SPEED ;
2018-02-18 12:25:14 +01:00
SDL_WarpMouseInWindow ( app . window , cursor . x , cursor . y ) ;
2018-02-17 17:52:51 +01:00
}
if ( isControl ( CONTROL_LEFT ) | | app . keyboard [ SDL_SCANCODE_LEFT ] )
{
cursor . x - = CURSOR_SPEED ;
2018-02-18 12:25:14 +01:00
SDL_WarpMouseInWindow ( app . window , cursor . x , cursor . y ) ;
2018-02-17 17:52:51 +01:00
}
if ( isControl ( CONTROL_RIGHT ) | | app . keyboard [ SDL_SCANCODE_RIGHT ] )
{
cursor . x + = CURSOR_SPEED ;
2018-02-18 12:25:14 +01:00
SDL_WarpMouseInWindow ( app . window , cursor . x , cursor . y ) ;
2018-02-17 17:52:51 +01:00
}
2018-02-16 23:50:55 +01:00
}
2018-02-18 08:59:14 +01:00
static void doMissionSelect ( void )
{
HubMission * m ;
if ( app . keyboard [ SDL_SCANCODE_ESCAPE ] )
{
2018-02-25 13:23:00 +01:00
playSound ( SND_MENU_BACK , 0 ) ;
2018-02-18 08:59:14 +01:00
showWidgetGroup ( " hub " ) ;
2018-02-19 09:28:42 +01:00
showing = SHOW_WIDGETS ;
2018-02-18 23:22:05 +01:00
app . keyboard [ SDL_SCANCODE_ESCAPE ] = 0 ;
2018-02-18 08:59:14 +01:00
}
else if ( isControl ( CONTROL_FIRE ) | | app . mouse . button [ SDL_BUTTON_LEFT ] )
{
m = getMissionAt ( cursor . x , cursor . y ) ;
if ( m ! = NULL )
{
selectedMission = m ;
app . mouse . button [ SDL_BUTTON_LEFT ] = 0 ;
clearControl ( CONTROL_FIRE ) ;
showWidgetGroup ( " mission " ) ;
}
}
}
static void doMissionInfo ( void )
{
Widget * w ;
w = selectWidgetAt ( cursor . x , cursor . y ) ;
if ( ( w ! = NULL ) & & ( isControl ( CONTROL_FIRE ) | | app . mouse . button [ SDL_BUTTON_LEFT ] ) )
{
w - > action ( ) ;
app . mouse . button [ SDL_BUTTON_LEFT ] = 0 ;
clearControl ( CONTROL_FIRE ) ;
}
if ( app . keyboard [ SDL_SCANCODE_ESCAPE ] )
{
cancel ( ) ;
}
}
2018-02-16 23:50:55 +01:00
static void draw ( void )
{
blitRectScaled ( atlasTexture - > texture , 0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT , & worldMap - > rect , 0 ) ;
2018-01-24 08:16:52 +01:00
2018-02-18 15:22:45 +01:00
drawBackground ( atlasTexture - > texture , & clouds - > rect ) ;
2018-02-17 08:56:29 +01:00
drawMissions ( ) ;
drawInfoBar ( ) ;
2018-02-19 09:28:42 +01:00
switch ( showing )
2018-02-17 08:56:29 +01:00
{
2018-02-19 09:28:42 +01:00
case SHOW_NONE :
if ( selectedMission ! = NULL )
{
2018-02-21 09:43:45 +01:00
drawMissionInfo ( ) ;
2018-02-19 09:28:42 +01:00
drawWidgets ( ) ;
}
blitRect ( atlasTexture - > texture , cursor . x , cursor . y , getCurrentFrame ( cursorSpr ) , 1 ) ;
break ;
2018-02-18 23:22:05 +01:00
2018-02-19 09:28:42 +01:00
case SHOW_WIDGETS :
drawHudWidgets ( ) ;
break ;
case SHOW_STATS :
drawStats ( ) ;
break ;
case SHOW_TROPHIES :
2018-02-19 23:32:14 +01:00
drawTrophies ( ) ;
2018-02-19 09:28:42 +01:00
break ;
2018-02-17 08:56:29 +01:00
}
}
static void drawMissions ( void )
{
HubMission * mission ;
2018-01-24 08:16:52 +01:00
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
2018-02-16 23:50:55 +01:00
switch ( mission - > status )
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
case MS_INCOMPLETE :
SDL_SetTextureColorMod ( atlasTexture - > texture , 255 , 0 , 0 ) ;
blitRectScaled ( atlasTexture - > texture , mission - > x , mission - > y , blipSize , blipSize , & alert - > rect , 1 ) ;
drawText ( mission - > x , mission - > y - 32 , 18 , TA_CENTER , colors . white , mission - > name ) ;
break ;
case MS_PARTIAL :
case MS_MISSING_HEART_CELL :
SDL_SetTextureColorMod ( atlasTexture - > texture , 255 , 255 , 0 ) ;
blitRectScaled ( atlasTexture - > texture , mission - > x , mission - > y , blipSize , blipSize , & alert - > rect , 1 ) ;
drawText ( mission - > x , mission - > y - 32 , 18 , TA_CENTER , colors . white , mission - > name ) ;
break ;
2018-02-18 08:59:14 +01:00
default :
break ;
2018-01-24 08:16:52 +01:00
}
}
2018-02-16 23:50:55 +01:00
SDL_SetTextureColorMod ( atlasTexture - > texture , 255 , 255 , 255 ) ;
2018-02-17 08:56:29 +01:00
}
static void drawInfoBar ( void )
{
2018-02-16 23:50:55 +01:00
drawRect ( 0 , 0 , SCREEN_WIDTH , 32 , 0 , 0 , 0 , 192 ) ;
2018-02-18 12:25:14 +01:00
drawText ( 10 , 5 , 18 , TA_LEFT , colors . white , " Missions : %d / %d " , completedMissions , unlockedMissions ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 12:25:14 +01:00
drawText ( 210 , 5 , 18 , TA_LEFT , colors . white , " MIAs : %d / %d " , game . stats [ STAT_MIAS_RESCUED ] , game . totalMIAs ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 12:25:14 +01:00
drawText ( 410 , 5 , 18 , TA_LEFT , colors . white , " Targets : %d / %d " , game . stats [ STAT_TARGETS_DEFEATED ] , game . totalTargets ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 12:25:14 +01:00
drawText ( 610 , 5 , 18 , TA_LEFT , colors . white , " Keys : %d / %d " , game . stats [ STAT_KEYS_FOUND ] , game . totalKeys ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 12:25:14 +01:00
drawText ( 810 , 5 , 18 , TA_LEFT , colors . white , " Hearts : %d / %d " , game . stats [ STAT_HEARTS_FOUND ] , game . totalHearts ) ;
2018-02-16 23:50:55 +01:00
2018-02-18 12:25:14 +01:00
drawText ( 1010 , 5 , 18 , TA_LEFT , colors . white , " Cells : %d / %d " , game . stats [ STAT_CELLS_FOUND ] , game . totalCells ) ;
2018-01-24 08:16:52 +01:00
}
2018-02-18 23:22:05 +01:00
static void drawHudWidgets ( void )
{
2018-02-19 20:13:29 +01:00
drawRect ( 0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT , 0 , 0 , 0 , 128 ) ;
2018-02-18 23:22:05 +01:00
2018-02-25 13:23:00 +01:00
drawWidgetFrame ( ) ;
2018-02-18 23:22:05 +01:00
drawWidgets ( ) ;
}
2018-02-17 08:56:29 +01:00
static void drawMissionInfo ( void )
{
int w , h , x , y , size , mid , i ;
w = 800 ;
h = 550 ;
x = ( SCREEN_WIDTH - w ) / 2 ;
y = ( SCREEN_HEIGHT - h ) / 2 ;
drawRect ( 0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT , 0 , 0 , 0 , 128 ) ;
drawRect ( x , y , w , h , 0 , 0 , 0 , 192 ) ;
drawOutlineRect ( x , y , w , h , 255 , 255 , 255 , 255 ) ;
drawText ( SCREEN_WIDTH / 2 , y + 25 , 32 , TA_CENTER , colors . white , selectedMission - > name ) ;
limitTextWidth ( w - 150 ) ;
drawText ( x + 15 , y + 100 , 22 , TA_LEFT , colors . white , selectedMission - > description ) ;
limitTextWidth ( 0 ) ;
size = 65 ;
mid = size / 2 ;
2018-02-18 08:59:14 +01:00
y = ( ( ( SCREEN_HEIGHT - h ) / 2 ) + h ) - 225 ;
drawText ( SCREEN_WIDTH / 2 , y , 24 , TA_CENTER , colors . white , " Keys " ) ;
y + = 64 ;
2018-02-17 08:56:29 +01:00
x = ( ( SCREEN_WIDTH - w ) / 2 ) + 30 ;
for ( i = 0 ; i < MAX_KEY_TYPES ; i + + )
{
drawRect ( x , y , size , size , 0 , 0 , 0 , 128 ) ;
drawOutlineRect ( x , y , size , size , 255 , 255 , 255 , 255 ) ;
if ( game . keys [ i ] . value . i > 0 )
{
2018-02-17 17:52:51 +01:00
blitRect ( atlasTexture - > texture , x + mid , y + mid + 7 , getCurrentFrame ( keySprites [ i ] ) , 1 ) ;
2018-02-17 08:56:29 +01:00
drawText ( x + size - 5 , y , 18 , TA_RIGHT , colors . white , " %d " , game . keys [ i ] . value . i ) ;
}
x + = ( size + 30 ) ;
}
}
2018-01-24 08:16:52 +01:00
static void unlockMission ( char * id )
{
Tuple * t ;
for ( t = game . missionStatusHead . next ; t ! = NULL ; t = t - > next )
{
if ( strcmp ( t - > key , id ) = = 0 )
{
2018-01-28 17:14:17 +01:00
if ( t - > value . i = = MS_LOCKED )
2018-01-24 08:16:52 +01:00
{
t - > value . i = MS_INCOMPLETE ;
2018-01-28 17:14:17 +01:00
SDL_LogMessage ( SDL_LOG_CATEGORY_APPLICATION , SDL_LOG_PRIORITY_DEBUG , " Unlocked mission %s " , id ) ;
2018-01-24 08:16:52 +01:00
}
2018-01-28 17:14:17 +01:00
2018-01-24 08:16:52 +01:00
return ;
}
}
t = malloc ( sizeof ( Tuple ) ) ;
memset ( t , 0 , sizeof ( Tuple ) ) ;
game . missionStatusTail - > next = t ;
game . missionStatusTail = t ;
STRNCPY ( t - > key , id , MAX_NAME_LENGTH ) ;
t - > value . i = MS_INCOMPLETE ;
2018-01-28 17:14:17 +01:00
SDL_LogMessage ( SDL_LOG_CATEGORY_APPLICATION , SDL_LOG_PRIORITY_DEBUG , " Unlocked mission %s " , id ) ;
2018-01-24 08:16:52 +01:00
}
static void unlockAllLevels ( void )
{
HubMission * mission ;
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
2018-01-28 17:14:17 +01:00
if ( mission - > status = = MS_LOCKED | | mission - > status = = MS_INCOMPLETE )
2018-01-24 08:16:52 +01:00
{
mission - > status = MS_INCOMPLETE ;
unlockMission ( mission - > id ) ;
}
}
}
static void unlockNeighbouringMission ( HubMission * sourceMission )
{
2018-02-16 23:50:55 +01:00
HubMission * mission , * missions [ numMissions ] ;
2018-01-24 08:16:52 +01:00
int i ;
i = 0 ;
2018-02-16 23:50:55 +01:00
memset ( missions , 0 , sizeof ( HubMission * ) * numMissions ) ;
2018-01-24 08:16:52 +01:00
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
mission - > distance = 99999 ;
2018-02-18 12:25:14 +01:00
if ( mission - > unlockCount > unlockedMissions | | mission - > status = = MS_COMPLETE | | mission - > status = = MS_MISSING_HEART_CELL | | mission - > status = = MS_PARTIAL )
2018-01-24 08:16:52 +01:00
{
continue ;
}
mission - > distance = getDistance ( mission - > x , mission - > y , sourceMission - > x , sourceMission - > y ) ;
missions [ i + + ] = mission ;
}
qsort ( missions , i , sizeof ( HubMission * ) , missionComparator ) ;
mission = missions [ 0 ] ;
if ( mission ! = NULL )
{
2018-02-18 12:25:14 +01:00
if ( mission - > status = = MS_LOCKED )
2018-01-24 08:16:52 +01:00
{
mission - > status = MS_INCOMPLETE ;
unlockMission ( mission - > id ) ;
}
mission = missions [ 1 ] ;
if ( mission ! = NULL )
{
2018-02-18 12:25:14 +01:00
if ( mission - > status = = MS_LOCKED )
2018-01-24 08:16:52 +01:00
{
mission - > status = MS_INCOMPLETE ;
unlockMission ( mission - > id ) ;
}
}
}
}
2018-02-16 23:50:55 +01:00
HubMission * getMissionAt ( int x , int y )
2018-01-24 08:16:52 +01:00
{
HubMission * rtn ;
HubMission * mission ;
float distance , dist ;
rtn = NULL ;
distance = 32 ;
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
2018-02-16 23:50:55 +01:00
if ( mission - > status = = MS_INCOMPLETE | | mission - > status = = MS_MISSING_HEART_CELL | | mission - > status = = MS_PARTIAL )
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
dist = getDistance ( x , y , mission - > x , mission - > y ) ;
if ( dist < distance )
{
rtn = mission ;
distance = dist ;
}
2018-01-24 08:16:52 +01:00
}
}
return rtn ;
}
2018-02-17 17:52:51 +01:00
static void startMission ( void )
{
STRNCPY ( game . worldId , selectedMission - > id , MAX_NAME_LENGTH ) ;
saveGame ( ) ;
2018-02-18 08:59:14 +01:00
stopMusic ( ) ;
2018-02-18 10:29:37 +01:00
2018-02-22 20:01:16 +01:00
destroyHub ( ) ;
2018-02-18 10:29:37 +01:00
initWorld ( ) ;
2018-02-17 17:52:51 +01:00
}
static void cancel ( void )
{
hideAllWidgets ( ) ;
2018-02-19 09:28:42 +01:00
showing = SHOW_NONE ;
2018-02-17 17:52:51 +01:00
selectedMission = NULL ;
app . keyboard [ SDL_SCANCODE_ESCAPE ] = 0 ;
}
static void options ( void )
{
2018-02-21 09:13:28 +01:00
initOptions ( returnFromOptions ) ;
2018-02-17 17:52:51 +01:00
}
static void stats ( void )
{
2018-02-19 09:28:42 +01:00
showing = SHOW_STATS ;
showWidgetGroup ( " stats " ) ;
2018-02-17 17:52:51 +01:00
}
static void trophies ( void )
{
2018-02-19 23:32:14 +01:00
showing = SHOW_TROPHIES ;
showWidgetGroup ( " trophies " ) ;
2018-02-17 17:52:51 +01:00
}
static void quit ( void )
{
}
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats ( void )
2018-02-19 09:28:42 +01:00
{
showWidgetGroup ( " hub " ) ;
showing = SHOW_WIDGETS ;
app . keyboard [ SDL_SCANCODE_ESCAPE ] = 0 ;
}
2018-02-21 09:13:28 +01:00
static void returnFromOptions ( void )
{
app . delegate . logic = logic ;
app . delegate . draw = draw ;
returnFromTrophyStats ( ) ;
}
2018-02-16 23:50:55 +01:00
static void loadMissions ( void )
{
cJSON * root , * node ;
char * text ;
HubMission * mission ;
double ratioX , ratioY ;
/* the original Attrition is based on 800x600, so multiply up */
ratioX = SCREEN_WIDTH / 800.0 ;
ratioY = SCREEN_HEIGHT / 600.0 ;
text = readFile ( " data/hub/missions.json " ) ;
root = cJSON_Parse ( text ) ;
for ( node = cJSON_GetObjectItem ( root , " missions " ) - > child ; node ! = NULL ; node = node - > next )
{
mission = malloc ( sizeof ( HubMission ) ) ;
memset ( mission , 0 , sizeof ( HubMission ) ) ;
hubMissionTail - > next = mission ;
hubMissionTail = mission ;
STRNCPY ( mission - > id , cJSON_GetObjectItem ( node , " id " ) - > valuestring , MAX_NAME_LENGTH ) ;
STRNCPY ( mission - > name , cJSON_GetObjectItem ( node , " name " ) - > valuestring , MAX_NAME_LENGTH ) ;
STRNCPY ( mission - > description , cJSON_GetObjectItem ( node , " description " ) - > valuestring , MAX_DESCRIPTION_LENGTH ) ;
mission - > status = MS_LOCKED ;
mission - > unlockCount = cJSON_GetObjectItem ( node , " unlockCount " ) - > valueint ;
mission - > x = cJSON_GetObjectItem ( node , " x " ) - > valuedouble * ratioX ;
mission - > y = cJSON_GetObjectItem ( node , " y " ) - > valuedouble * ratioY ;
}
cJSON_Delete ( root ) ;
free ( text ) ;
}
2018-02-19 20:13:29 +01:00
static void awardMissionTrophies ( void )
{
int beach , greenlands , underground , outpost ;
HubMission * mission ;
beach = greenlands = underground = outpost = 1 ;
for ( mission = hubMissionHead . next ; mission ! = NULL ; mission = mission - > next )
{
if ( mission - > status ! = MS_COMPLETE )
{
if ( strstr ( mission - > id , " beach " ) )
{
beach = 0 ;
}
else if ( strstr ( mission - > id , " greenlands " ) )
{
greenlands = 0 ;
}
else if ( strstr ( mission - > id , " underground " ) )
{
underground = 0 ;
}
else if ( strstr ( mission - > id , " outpost " ) )
{
outpost = 0 ;
}
}
}
if ( beach )
{
awardTrophy ( " BEACH " ) ;
}
if ( greenlands )
{
awardTrophy ( " GREENLANDS " ) ;
}
if ( underground )
{
awardTrophy ( " UNDERGROUND " ) ;
}
if ( outpost )
{
awardTrophy ( " OUTPOST " ) ;
}
/* ignore training mission */
if ( completedMissions = = 2 )
{
awardTrophy ( " CLEAN " ) ;
}
}
2018-01-24 08:16:52 +01:00
static int missionComparator ( const void * a , const void * b )
{
HubMission * m1 = * ( ( HubMission * * ) a ) ;
HubMission * m2 = * ( ( HubMission * * ) b ) ;
2018-02-18 12:25:14 +01:00
return m1 - > distance - m2 - > distance ;
2018-01-24 08:16:52 +01:00
}
2018-02-16 23:50:55 +01:00
void destroyHub ( void )
{
2018-02-17 17:52:51 +01:00
HubMission * m ;
while ( hubMissionHead . next )
{
m = hubMissionHead . next ;
hubMissionHead . next = m - > next ;
free ( m ) ;
}
2018-02-22 20:01:16 +01:00
memset ( & hubMissionHead , 0 , sizeof ( HubMission ) ) ;
hubMissionTail = & hubMissionHead ;
2018-02-16 23:50:55 +01:00
}