project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m
author Wuzzy <Wuzzy2@mail.ru>
Mon, 16 Sep 2019 17:33:49 +0200
changeset 15431 8504fee3b601
parent 13199 e17efd663125
permissions -rw-r--r--
Racer: Fix weird water splashes after waypoint placement Does not affect official racer, as only waypoint placement is touched. The reason was that the air attack gear sometimes was not deleted fast enough so it might occassionally drop some air bombs (these are deleted now). Also, the airplane position was set to water level, which caused another water splash.

/*
 * Hedgewars-iOS, a Hedgewars port for iOS devices
 * Copyright (c) 2009-2012 Vittorio Giovara <vittorio.giovara@gmail.com>
 *
 * 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; version 2 of the License
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
 */


#import "GameInterfaceBridge.h"
#import "EngineProtocolNetwork.h"
#import "StatsPageViewController.h"


static UIViewController *callingController;

@implementation GameInterfaceBridge
@synthesize blackView, savePath, port;

#pragma mark -
#pragma mark Instance methods for engine interaction
// prepares the controllers for hosting a game
- (void)earlyEngineLaunch:(NSDictionary *)optionsOrNil {
    [[AudioManagerController mainManager] fadeOutBackgroundMusic];

    EngineProtocolNetwork *engineProtocol = [[EngineProtocolNetwork alloc] init];
    self.port = engineProtocol.enginePort;
    engineProtocol.delegate = self;
    [engineProtocol spawnThread:self.savePath withOptions:optionsOrNil];

    // add a black view hiding the background
    UIWindow *thisWindow = [[HedgewarsAppDelegate sharedAppDelegate] uiwindow];
    self.blackView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.blackView.opaque = YES;
    self.blackView.backgroundColor = [UIColor blackColor];
    self.blackView.alpha = 0;
    self.blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [UIView beginAnimations:@"fade out" context:NULL];
    [UIView setAnimationDuration:1];
    self.blackView.alpha = 1;
    [UIView commitAnimations];
    [thisWindow addSubview:self.blackView];

    // keep the point of return for games that completed loading
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:self.savePath forKey:@"savedGamePath"];
    [userDefaults setObject:[NSNumber numberWithBool:NO] forKey:@"saveIsValid"];
    [userDefaults synchronize];

    // let's launch the engine using this -perfomSelector so that the runloop can deal with queued messages first
    [self performSelector:@selector(engineLaunch) withObject:nil afterDelay:0.1f];
}

// cleans up everything
- (void)lateEngineLaunch {
    // notify views below that they are getting the spotlight again
    [[[HedgewarsAppDelegate sharedAppDelegate] uiwindow] makeKeyAndVisible];
    [callingController viewWillAppear:YES];

    // remove completed games notification
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:@"" forKey:@"savedGamePath"];
    [userDefaults synchronize];

    // remove the cover view with a transition
    self.blackView.alpha = 1;
    [UIView beginAnimations:@"fade in" context:NULL];
    [UIView setAnimationDuration:1];
    self.blackView.alpha = 0;
    [UIView commitAnimations];
    [self.blackView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1];

    // can remove the savefile if the replay has ended
    if ([HWUtils gameType] == gtSave)
        [[NSFileManager defaultManager] removeItemAtPath:self.savePath error:nil];

    // restart music and we're done
    [[AudioManagerController mainManager] fadeInBackgroundMusic];
    [HWUtils setGameStatus:gsNone];
    [HWUtils setGameType:gtNone];
}

// main routine for calling the actual game engine
- (void)engineLaunch {
    CGFloat width, height;
    CGFloat screenScale = [[UIScreen mainScreen] safeScale];
    NSString *ipcString = [[NSString alloc] initWithFormat:@"%d",self.port];
    
    NSString *localeString = [[NSString alloc] initWithFormat:@"%@.txt", [HWUtils languageID]];
    
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];

    CGRect screenBounds = [[UIScreen mainScreen] safeBounds];
    width = screenBounds.size.width;
    height = screenBounds.size.height;

    NSString *horizontalSize = [[NSString alloc] initWithFormat:@"%d", (int)(width * screenScale)];
    NSString *verticalSize = [[NSString alloc] initWithFormat:@"%d", (int)(height * screenScale)];
    NSString *resourcePath = [[NSString alloc] initWithFormat:@"%@/Data", [[NSBundle mainBundle] resourcePath]];

    NSString *modelId = [HWUtils modelType];
    NSInteger tmpQuality;
    if ([modelId hasPrefix:@"iPhone1"] || [modelId hasPrefix:@"iPod1,1"] || [modelId hasPrefix:@"iPod2,1"])     // = iPhone and iPhone 3G or iPod Touch or iPod Touch 2G
        tmpQuality = 0x00000001 | 0x00000002 | 0x00000008 | 0x00000040;                 // rqLowRes | rqBlurryLand | rqSimpleRope | rqKillFlakes
    else if ([modelId hasPrefix:@"iPhone2"] || [modelId hasPrefix:@"iPod3"])                                    // = iPhone 3GS or iPod Touch 3G
        tmpQuality = 0x00000002 | 0x00000040;                                           // rqBlurryLand | rqKillFlakes
    else if ([modelId hasPrefix:@"iPad1"] || [modelId hasPrefix:@"iPod4"])                                      // = iPad 1G or iPod Touch 4G
        tmpQuality = 0x00000002;                                                        // rqBlurryLand
    else                                                                                                        // = everything else
        tmpQuality = 0;                                                                 // full quality

    // disable ammomenu animation
    tmpQuality = tmpQuality | 0x00000080;
    // disable tooltips on iPhone
    if (IS_IPAD() == NO)
        tmpQuality = tmpQuality | 0x00000400;
    NSString *rawQuality = [NSString stringWithFormat:@"%d",tmpQuality];
    NSString *documentsDirectory = DOCUMENTS_FOLDER();

    NSMutableArray *gameParameters = [[NSMutableArray alloc] initWithObjects:
                                      @"--internal",
                                      @"--port", ipcString,
                                      @"--width", horizontalSize,
                                      @"--height", verticalSize,
                                      @"--raw-quality", rawQuality,
                                      @"--locale", localeString,
                                      @"--prefix", resourcePath,
                                      @"--user-prefix", documentsDirectory,
                                      nil];

    NSString *username = [settings objectForKey:@"username"];
    if ([username length] > 0) {
        [gameParameters addObject:@"--nick"];
        [gameParameters addObject: username];
    }

    if ([[settings objectForKey:@"sound"] boolValue] == NO)
        [gameParameters addObject:@"--nosound"];

    if ([[settings objectForKey:@"music"] boolValue] == NO)
        [gameParameters addObject:@"--nomusic"];

    if([[settings objectForKey:@"alternate"] boolValue] == YES)
        [gameParameters addObject:@"--altdmg"];

#ifdef DEBUG
    [gameParameters addObject:@"--showfps"];
#endif

    if ([HWUtils gameType] == gtSave)
        [gameParameters addObject:self.savePath];

    [HWUtils setGameStatus:gsLoading];

    int argc = [gameParameters count];
    const char **argv = (const char **)malloc(sizeof(const char*)*argc);
    for (int i = 0; i < argc; i++)
        argv[i] = strdup([[gameParameters objectAtIndex:i] UTF8String]);

    // this is the pascal function that starts the game
    RunEngine(argc, argv);

    // cleanup
    for (int i = 0; i < argc; i++)
        free((void *)argv[i]);
    free(argv);

    // moar cleanup
    [self lateEngineLaunch];
}


#pragma mark -
#pragma mark EngineProtocolDelegate methods
- (void)gameEndedWithStatistics:(NSArray *)stats {
    [self performSelectorOnMainThread:@selector(presentStats:) withObject:stats waitUntilDone:NO];
}

- (void)presentStats:(NSArray *)stats {
    if (stats != nil) {
        StatsPageViewController *statsPage = [[StatsPageViewController alloc] init];
        statsPage.statsArray = stats;
        statsPage.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

        [callingController presentViewController:statsPage animated:YES completion:nil];
    }
}

#pragma mark -
#pragma mark Class methods for setting up the engine from outsite
+ (void)registerCallingController:(UIViewController *)controller {
    callingController = controller;
}

+ (void)startGame:(TGameType)type atPath:(NSString *)path withOptions:(NSDictionary *)config {
    [HWUtils setGameType:type];
    id bridge = [[self alloc] init];
    [bridge setSavePath:path];
    [bridge earlyEngineLaunch:config];
}

+ (void)startLocalGame:(NSDictionary *)withOptions {
    NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
    [outputFormatter setDateFormat:@"yyyy-MM-dd '@' HH.mm"];
    NSString *savePath = [[NSString alloc] initWithFormat:@"%@%@.hws",SAVES_DIRECTORY(),[outputFormatter stringFromDate:[NSDate date]]];

    // in the rare case in which a savefile with the same name exists the older one must be removed (otherwise it gets corrupted)
    if ([[NSFileManager defaultManager] fileExistsAtPath:savePath])
        [[NSFileManager defaultManager] removeItemAtPath:savePath error:nil];

    [self startGame:gtLocal atPath:savePath withOptions:withOptions];
}

+ (void)startSaveGame:(NSString *)atPath {
    [self startGame:gtSave atPath:atPath withOptions:nil];
}

+ (void)startMissionGame:(NSString *)withSubPath {
    NSString *seedCmd = [self seedCommand];
    NSString *missionPath = [[NSString alloc] initWithFormat:@"escript Missions/%@.lua", withSubPath];
    NSDictionary *missionDict = [[NSDictionary alloc] initWithObjectsAndKeys:missionPath, @"mission_command", seedCmd, @"seed_command", nil];

    [self startGame:gtMission atPath:nil withOptions:missionDict];
}

+ (NSString *)seedCommand {
    // generate a seed
    NSString *seed = [HWUtils seed];
    NSString *seedCmd = [[NSString alloc] initWithFormat:@"eseed {%@}", seed];
    return seedCmd;
}

+ (void)startCampaignMissionGameWithScript:(NSString *)missionScriptName forCampaign:(NSString *)campaignName {
    NSString *seedCmd = [self seedCommand];
    NSString *campaignMissionPath = [[NSString alloc] initWithFormat:@"escript Missions/Campaign/%@/%@", campaignName, missionScriptName];
    NSDictionary *campaignMissionDict = [[NSDictionary alloc] initWithObjectsAndKeys:campaignMissionPath, @"mission_command", seedCmd, @"seed_command", nil];
    
    [self startGame:gtCampaign atPath:nil withOptions:campaignMissionDict];
}

+ (void)startSimpleGame {
    NSString *seedCmd = [self seedCommand];

    // pick a random static map
    NSArray *listOfMaps = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MAPS_DIRECTORY() error:NULL];
    NSString *mapName = [listOfMaps objectAtIndex:arc4random_uniform((int)[listOfMaps count])];
    NSString *fileCfg = [[NSString alloc] initWithFormat:@"%@/%@/map.cfg",MAPS_DIRECTORY(),mapName];
    NSString *contents = [[NSString alloc] initWithContentsOfFile:fileCfg encoding:NSUTF8StringEncoding error:NULL];
    NSArray *split = [contents componentsSeparatedByString:@"\n"];
    NSString *themeCommand = [[NSString alloc] initWithFormat:@"etheme %@", [split objectAtIndex:0]];
    NSString *staticMapCommand = [[NSString alloc] initWithFormat:@"emap %@", mapName];

    // select teams with two different colors
    NSArray *colorArray = [HWUtils teamColors];
    NSInteger firstColorIndex, secondColorIndex;
    do {
        firstColorIndex = arc4random_uniform((int)[colorArray count]);
        secondColorIndex = arc4random_uniform((int)[colorArray count]);
    } while (firstColorIndex == secondColorIndex);
    unsigned int firstColor = [[colorArray objectAtIndex:firstColorIndex] intValue];
    unsigned int secondColor = [[colorArray objectAtIndex:secondColorIndex] intValue];

    NSDictionary *firstTeam = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:4],@"number",
                                                                           [NSNumber numberWithUnsignedInt:firstColor],@"color",
                                                                           @"Ninjas.plist",@"team",nil];
    NSDictionary *secondTeam = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:4],@"number",
                                                                            [NSNumber numberWithUnsignedInt:secondColor],@"color",
                                                                            @"Robots.plist",@"team",nil];
    NSArray *listOfTeams = [[NSArray alloc] initWithObjects:firstTeam,secondTeam,nil];

    // create the configuration
    NSDictionary *gameDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
                                    seedCmd,@"seed_command",
                                    @"e$template_filter 0",@"templatefilter_command",
                                    @"e$mapgen 0",@"mapgen_command",
                                    @"e$maze_size 0",@"mazesize_command",
                                    themeCommand,@"theme_command",
                                    staticMapCommand,@"staticmap_command",
                                    listOfTeams,@"teams_list",
                                    @"Default.plist",@"scheme",
                                    @"Default.plist",@"weapon",
                                    @"",@"mission_command",
                                    nil];

    // launch game
    [GameInterfaceBridge startLocalGame:gameDictionary];
}

@end