Persists customized input mappings between app launches
This commit is contained in:
parent
d70105e30e
commit
94cbdbe159
@ -1 +1 @@
|
||||
Subproject commit f33f8bd91a9e0b41ba55b9aa8370397dd7e7f809
|
||||
Subproject commit 7681a93515aea5c7642a56c888fe5da3aa4ea09f
|
||||
@ -79,6 +79,8 @@
|
||||
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */; };
|
||||
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */; };
|
||||
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */; };
|
||||
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */; };
|
||||
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */; };
|
||||
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; };
|
||||
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; };
|
||||
@ -109,6 +111,7 @@
|
||||
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; };
|
||||
BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFF6452E1F7CC5060056533E /* GameControllerInputMappingTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */; };
|
||||
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */; };
|
||||
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */; };
|
||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
|
||||
@ -202,6 +205,7 @@
|
||||
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+ParentViewController.swift"; sourceTree = "<group>"; };
|
||||
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControllerSkin+Configuring.swift"; sourceTree = "<group>"; };
|
||||
BF696B7F1D9B2B02009639E0 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = Theming/Theme.swift; sourceTree = "<group>"; };
|
||||
BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GameControllerInputMappingTransformer.swift; path = Database/Model/Transformers/GameControllerInputMappingTransformer.swift; sourceTree = "<group>"; };
|
||||
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
BF6BF3121EB7E47F008E83CD /* ImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImportOption.swift; path = Importing/ImportOption.swift; sourceTree = "<group>"; };
|
||||
BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = iTunesImportOption.swift; path = "Importing/Import Options/iTunesImportOption.swift"; sourceTree = "<group>"; };
|
||||
@ -209,6 +213,8 @@
|
||||
BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesDatabaseImportOption.swift; path = "Importing/Import Options/GamesDatabaseImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF6BF3201EB82362008E83CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/GamesDatabase.storyboard; sourceTree = "<group>"; };
|
||||
BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PhotoLibraryImportOption.swift; path = "Importing/Import Options/PhotoLibraryImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = _GameControllerInputMapping.swift; path = Database/Model/Machine/_GameControllerInputMapping.swift; sourceTree = "<group>"; };
|
||||
BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GameControllerInputMapping.swift; path = Database/Model/Human/GameControllerInputMapping.swift; sourceTree = "<group>"; };
|
||||
BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
|
||||
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridMenuViewController.swift; path = "Pause Menu/GridMenuViewController.swift"; sourceTree = "<group>"; };
|
||||
@ -378,6 +384,7 @@
|
||||
BF5942721E09BC700051894B /* Model.xcdatamodel */,
|
||||
BF5942741E09BC740051894B /* Human */,
|
||||
BF5942751E09BC780051894B /* Machine */,
|
||||
BF6B82A31F7CC29A00042BFB /* Transformers */,
|
||||
BF5942761E09BC7C0051894B /* Misc */,
|
||||
);
|
||||
name = Model;
|
||||
@ -390,6 +397,7 @@
|
||||
BF5942781E09BC830051894B /* ControllerSkin.swift */,
|
||||
BF5942791E09BC830051894B /* Game.swift */,
|
||||
BF59427A1E09BC830051894B /* GameCollection.swift */,
|
||||
BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */,
|
||||
BF59427B1E09BC830051894B /* SaveState.swift */,
|
||||
);
|
||||
name = Human;
|
||||
@ -402,6 +410,7 @@
|
||||
BF5942821E09BC8B0051894B /* _ControllerSkin.swift */,
|
||||
BF5942831E09BC8B0051894B /* _Game.swift */,
|
||||
BF5942841E09BC8B0051894B /* _GameCollection.swift */,
|
||||
BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */,
|
||||
BF5942851E09BC8B0051894B /* _SaveState.swift */,
|
||||
);
|
||||
name = Machine;
|
||||
@ -433,6 +442,14 @@
|
||||
name = Theming;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF6B82A31F7CC29A00042BFB /* Transformers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */,
|
||||
);
|
||||
name = Transformers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF6BF3161EB820F4008E83CD /* Import Options */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -811,6 +828,7 @@
|
||||
BFC6F7B81F435BC500221B96 /* Input+Display.swift in Sources */,
|
||||
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */,
|
||||
BF6BF3181EB82111008E83CD /* iTunesImportOption.swift in Sources */,
|
||||
BFF6452E1F7CC5060056533E /* GameControllerInputMappingTransformer.swift in Sources */,
|
||||
BF59427C1E09BC830051894B /* Cheat.swift in Sources */,
|
||||
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
|
||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||
@ -859,9 +877,11 @@
|
||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
|
||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
|
||||
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */,
|
||||
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
|
||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */,
|
||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */,
|
||||
|
||||
77
Delta/Database/Model/Human/GameControllerInputMapping.swift
Normal file
77
Delta/Database/Model/Human/GameControllerInputMapping.swift
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// GameControllerInputMapping.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 8/30/16.
|
||||
// Copyright (c) 2016 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
|
||||
@objc(GameControllerInputMapping)
|
||||
public class GameControllerInputMapping: _GameControllerInputMapping
|
||||
{
|
||||
fileprivate var inputMapping: DeltaCore.GameControllerInputMapping {
|
||||
get { return self.deltaCoreInputMapping as! DeltaCore.GameControllerInputMapping }
|
||||
set { self.deltaCoreInputMapping = newValue }
|
||||
}
|
||||
|
||||
public convenience init(inputMapping: DeltaCore.GameControllerInputMapping, context: NSManagedObjectContext)
|
||||
{
|
||||
self.init(entity: GameControllerInputMapping.entity(), insertInto: context)
|
||||
|
||||
self.inputMapping = inputMapping
|
||||
}
|
||||
}
|
||||
|
||||
extension GameControllerInputMapping
|
||||
{
|
||||
class func inputMapping(for gameController: GameController, gameType: GameType, in managedObjectContext: NSManagedObjectContext) -> GameControllerInputMapping?
|
||||
{
|
||||
guard let playerIndex = gameController.playerIndex else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let fetchRequest: NSFetchRequest<GameControllerInputMapping> = GameControllerInputMapping.fetchRequest()
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@ AND %K == %d", #keyPath(GameControllerInputMapping.gameControllerInputType), gameController.inputType.rawValue, #keyPath(GameControllerInputMapping.gameType), gameType.rawValue, #keyPath(GameControllerInputMapping.playerIndex), playerIndex)
|
||||
|
||||
do
|
||||
{
|
||||
let inputMappings = try managedObjectContext.fetch(fetchRequest)
|
||||
|
||||
let inputMapping = inputMappings.first
|
||||
return inputMapping
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension GameControllerInputMapping: GameControllerInputMappingProtocol
|
||||
{
|
||||
var name: String? {
|
||||
get { return self.inputMapping.name }
|
||||
set { self.inputMapping.name = newValue }
|
||||
}
|
||||
|
||||
var supportedControllerInputs: [Input] {
|
||||
return self.inputMapping.supportedControllerInputs
|
||||
}
|
||||
|
||||
public func input(forControllerInput controllerInput: Input) -> Input?
|
||||
{
|
||||
return self.inputMapping.input(forControllerInput: controllerInput)
|
||||
}
|
||||
|
||||
func set(_ input: Input?, forControllerInput controllerInput: Input)
|
||||
{
|
||||
self.inputMapping.set(input, forControllerInput: controllerInput)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
// DO NOT EDIT. This file is machine-generated and constantly overwritten.
|
||||
// Make changes to GameControllerInputMapping.swift instead.
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
import DeltaCore
|
||||
|
||||
public class _GameControllerInputMapping: NSManagedObject
|
||||
{
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<GameControllerInputMapping> {
|
||||
return NSFetchRequest<GameControllerInputMapping>(entityName: "GameControllerInputMapping")
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@NSManaged public var deltaCoreInputMapping: Any
|
||||
|
||||
@NSManaged public var gameControllerInputType: GameControllerInputType
|
||||
|
||||
@NSManaged public var gameType: GameType
|
||||
|
||||
@NSManaged public var playerIndex: Int16
|
||||
|
||||
// MARK: - Relationships
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16C68" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13240" systemVersion="16G29" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
@ -79,6 +79,31 @@
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameControllerInputMapping" representedClassName="GameControllerInputMapping" syncable="YES">
|
||||
<attribute name="deltaCoreInputMapping" attributeType="Transformable" valueTransformerName="GameControllerInputMappingTransformer" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="Any"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameControllerInputType" attributeType="Transformable" valueTransformerName="" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameControllerInputType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="playerIndex" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="gameControllerInputType"/>
|
||||
<constraint value="gameType"/>
|
||||
<constraint value="playerIndex"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="SaveState" representedClassName="SaveState" syncable="YES">
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
@ -107,6 +132,7 @@
|
||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="180"/>
|
||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
||||
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="105"/>
|
||||
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||
</elements>
|
||||
</model>
|
||||
@ -0,0 +1,61 @@
|
||||
//
|
||||
// GameControllerInputMappingTransformer.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 9/27/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
|
||||
@objc(GameControllerInputMappingTransformer)
|
||||
class GameControllerInputMappingTransformer: ValueTransformer
|
||||
{
|
||||
override class func transformedValueClass() -> AnyClass {
|
||||
return NSData.self
|
||||
}
|
||||
|
||||
override class func allowsReverseTransformation() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func transformedValue(_ value: Any?) -> Any?
|
||||
{
|
||||
guard let inputMapping = value as? DeltaCore.GameControllerInputMapping else { return nil }
|
||||
|
||||
let plistEncoder = PropertyListEncoder()
|
||||
|
||||
do
|
||||
{
|
||||
let data = try plistEncoder.encode(inputMapping)
|
||||
return data
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func reverseTransformedValue(_ value: Any?) -> Any?
|
||||
{
|
||||
guard let inputMappingData = value as? Data else { return nil }
|
||||
|
||||
let plistDecoder = PropertyListDecoder()
|
||||
|
||||
do
|
||||
{
|
||||
let inputMapping = try plistDecoder.decode(DeltaCore.GameControllerInputMapping.self, from: inputMappingData)
|
||||
return inputMapping
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32,12 +32,15 @@ private extension GameViewController
|
||||
|
||||
struct SustainInputsMapping: GameControllerInputMappingProtocol
|
||||
{
|
||||
let gameControllerInputType: GameControllerInputType
|
||||
let previousInputMapping: GameControllerInputMappingProtocol?
|
||||
let gameController: GameController
|
||||
|
||||
var gameControllerInputType: GameControllerInputType {
|
||||
return self.gameController.inputType
|
||||
}
|
||||
|
||||
func input(forControllerInput controllerInput: Input) -> Input?
|
||||
{
|
||||
if let mappedInput = self.previousInputMapping?.input(forControllerInput: controllerInput), mappedInput == StandardGameControllerInput.menu
|
||||
if let mappedInput = self.gameController.defaultInputMapping?.input(forControllerInput: controllerInput), mappedInput == StandardGameControllerInput.menu
|
||||
{
|
||||
return mappedInput
|
||||
}
|
||||
@ -355,6 +358,8 @@ private extension GameViewController
|
||||
{
|
||||
@objc func updateControllers()
|
||||
{
|
||||
guard let emulatorCore = self.emulatorCore, let game = self.game else { return }
|
||||
|
||||
let controllers = [self.controllerView as GameController] + ExternalGameControllerManager.shared.connectedControllers
|
||||
|
||||
if let index = Settings.localControllerPlayerIndex
|
||||
@ -368,28 +373,25 @@ private extension GameViewController
|
||||
self.controllerView.isHidden = true
|
||||
}
|
||||
|
||||
// Removing all game controllers from EmulatorCore will reset each controller's playerIndex to nil
|
||||
// We temporarily cache their playerIndexes, and then we reset them after removing all controllers
|
||||
var controllerIndexes = [ObjectIdentifier: Int?]()
|
||||
controllers.forEach { controllerIndexes[ObjectIdentifier($0)] = $0.playerIndex }
|
||||
|
||||
self.emulatorCore?.removeAllGameControllers()
|
||||
|
||||
// Reset each controller's playerIndex to what it was before removing all controllers from EmulatorCore
|
||||
controllers.forEach { $0.playerIndex = controllerIndexes[ObjectIdentifier($0)] ?? nil }
|
||||
|
||||
for controller in controllers
|
||||
for gameController in controllers
|
||||
{
|
||||
if let index = controller.playerIndex
|
||||
if gameController.playerIndex != nil
|
||||
{
|
||||
// We need to place the underscore here to silence erroneous unused result warning despite annotating function with @discardableResult
|
||||
// Hopefully this bug won't be around for too long...
|
||||
_ = self.emulatorCore?.setGameController(controller, at: index)
|
||||
controller.addReceiver(self)
|
||||
if let inputMapping = GameControllerInputMapping.inputMapping(for: gameController, gameType: game.type, in: DatabaseManager.shared.viewContext)
|
||||
{
|
||||
gameController.addReceiver(self, inputMapping: inputMapping)
|
||||
gameController.addReceiver(emulatorCore, inputMapping: inputMapping)
|
||||
}
|
||||
else
|
||||
{
|
||||
gameController.addReceiver(self)
|
||||
gameController.addReceiver(emulatorCore)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controller.removeReceiver(self)
|
||||
gameController.removeReceiver(self)
|
||||
gameController.removeReceiver(emulatorCore)
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,8 +625,8 @@ private extension GameViewController
|
||||
|
||||
self.isSelectingSustainedButtons = true
|
||||
|
||||
self.sustainInputsMapping = SustainInputsMapping(gameControllerInputType: gameController.inputType, previousInputMapping: gameController.inputMapping)
|
||||
gameController.inputMapping = self.sustainInputsMapping
|
||||
let sustainInputsMapping = SustainInputsMapping(gameController: gameController)
|
||||
gameController.addReceiver(self, inputMapping: sustainInputsMapping)
|
||||
|
||||
let blurEffect = self.sustainButtonsBlurView.effect
|
||||
self.sustainButtonsBlurView.effect = nil
|
||||
@ -643,7 +645,7 @@ private extension GameViewController
|
||||
|
||||
self.isSelectingSustainedButtons = false
|
||||
|
||||
gameController.inputMapping = self.sustainInputsMapping?.previousInputMapping
|
||||
self.updateControllers()
|
||||
self.sustainInputsMapping = nil
|
||||
|
||||
// Reactivate all sustained inputs, since they will now be mapped to game inputs.
|
||||
|
||||
@ -17,7 +17,7 @@ class ControllerInputsViewController: UIViewController
|
||||
{
|
||||
var gameController: GameController! {
|
||||
didSet {
|
||||
self.prepareGameController()
|
||||
self.gameController.addReceiver(self, inputMapping: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ class ControllerInputsViewController: UIViewController
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var inputMapping: GameControllerInputMapping!
|
||||
fileprivate var previousInputMapping: GameControllerInputMappingProtocol?
|
||||
fileprivate lazy var managedObjectContext: NSManagedObjectContext = DatabaseManager.shared.newBackgroundContext()
|
||||
fileprivate var inputMappings = [System: GameControllerInputMapping]()
|
||||
|
||||
fileprivate let supportedActionInputs: [ActionInput] = [.saveState, .loadState, .fastForward]
|
||||
|
||||
@ -91,8 +91,11 @@ extension ControllerInputsViewController
|
||||
self.actionsMenuViewController = segue.destination as! GridMenuViewController
|
||||
self.prepareActionsMenuViewController()
|
||||
|
||||
case "cancelControllerControls": self.gameController.inputMapping = self.previousInputMapping
|
||||
case "saveControllerControls": self.gameController.inputMapping = self.inputMapping
|
||||
case "cancelControllerInputs": break
|
||||
case "saveControllerInputs":
|
||||
self.managedObjectContext.performAndWait {
|
||||
self.managedObjectContext.saveWithErrorLogging()
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
@ -105,6 +108,7 @@ private extension ControllerInputsViewController
|
||||
{
|
||||
guard self.isViewLoaded else { return }
|
||||
|
||||
// Update popoverMenuButton to display correctly on iOS 10.
|
||||
if let popoverMenuButton = self.navigationItem.popoverMenuController?.popoverMenuButton
|
||||
{
|
||||
popoverMenuButton.title = self.system.localizedShortName
|
||||
@ -113,8 +117,31 @@ private extension ControllerInputsViewController
|
||||
self.navigationController?.navigationBar.layoutIfNeeded()
|
||||
}
|
||||
|
||||
// Update controller view's controller skin.
|
||||
self.gameViewController.controllerView.controllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: self.system.gameType)
|
||||
|
||||
// Fetch input mapping if it hasn't already been fetched.
|
||||
if let gameController = self.gameController, let playerIndex = gameController.playerIndex, self.inputMappings[self.system] == nil
|
||||
{
|
||||
self.managedObjectContext.performAndWait {
|
||||
let inputMapping = GameControllerInputMapping.inputMapping(for: gameController, gameType: self.system.gameType, in: self.managedObjectContext) ?? {
|
||||
let deltaCoreInputMapping = gameController.defaultInputMapping as? DeltaCore.GameControllerInputMapping ?? DeltaCore.GameControllerInputMapping(gameControllerInputType: gameController.inputType)
|
||||
|
||||
let inputMapping = GameControllerInputMapping(inputMapping: deltaCoreInputMapping, context: self.managedObjectContext)
|
||||
inputMapping.gameControllerInputType = gameController.inputType
|
||||
inputMapping.gameType = self.system.gameType
|
||||
inputMapping.playerIndex = Int16(playerIndex)
|
||||
|
||||
return inputMapping
|
||||
}()
|
||||
|
||||
inputMapping.name = String.localizedStringWithFormat("Custom %@", gameController.name)
|
||||
|
||||
self.inputMappings[self.system] = inputMapping
|
||||
}
|
||||
}
|
||||
|
||||
// Update callouts, if view is already on screen.
|
||||
if self.view.window != nil
|
||||
{
|
||||
self.calloutViews.forEach { $1.dismissCallout(animated: true) }
|
||||
@ -126,18 +153,6 @@ private extension ControllerInputsViewController
|
||||
}
|
||||
}
|
||||
|
||||
func prepareGameController()
|
||||
{
|
||||
self.gameController.addReceiver(self)
|
||||
|
||||
self.previousInputMapping = self.gameController.inputMapping
|
||||
|
||||
self.inputMapping = self.gameController.inputMapping as? GameControllerInputMapping ?? GameControllerInputMapping(gameControllerInputType: self.gameController.inputType)
|
||||
self.inputMapping.name = String.localizedStringWithFormat("Custom %@", self.gameController.name)
|
||||
|
||||
self.gameController.inputMapping = nil
|
||||
}
|
||||
|
||||
func preparePopoverMenuController()
|
||||
{
|
||||
let listMenuViewController = ListMenuViewController()
|
||||
@ -186,7 +201,7 @@ private extension ControllerInputsViewController
|
||||
text = NSLocalizedString("Fast Forward", comment: "")
|
||||
}
|
||||
|
||||
let item = MenuItem(text: text, image: image) { (item) in
|
||||
let item = MenuItem(text: text, image: image) { [unowned self] (item) in
|
||||
guard let calloutView = self.calloutViews[AnyInput(input)] else { return }
|
||||
self.toggle(calloutView)
|
||||
}
|
||||
@ -204,7 +219,8 @@ private extension ControllerInputsViewController
|
||||
let controllerView = self.gameViewController.controllerView,
|
||||
let traits = controllerView.controllerSkinTraits,
|
||||
let items = controllerView.controllerSkin?.items(for: traits),
|
||||
let controllerViewInputMapping = controllerView.inputMapping
|
||||
let controllerViewInputMapping = controllerView.defaultInputMapping,
|
||||
let inputMapping = self.inputMappings[self.system]
|
||||
else { return }
|
||||
|
||||
// Implicit assumption that all skins used for controller input mapping don't have multiple items with same input.
|
||||
@ -218,21 +234,23 @@ private extension ControllerInputsViewController
|
||||
self.calloutViews[AnyInput(input)] = calloutView
|
||||
}
|
||||
|
||||
// Update callout views with controller inputs that map to callout views' associated controller skin inputs.
|
||||
for input in self.inputMapping.supportedControllerInputs
|
||||
{
|
||||
let mappedInput = self.mappedInput(for: input)
|
||||
|
||||
if let calloutView = self.calloutViews[mappedInput]
|
||||
self.managedObjectContext.performAndWait {
|
||||
// Update callout views with controller inputs that map to callout views' associated controller skin inputs.
|
||||
for input in inputMapping.supportedControllerInputs
|
||||
{
|
||||
if let previousInput = calloutView.input
|
||||
let mappedInput = self.mappedInput(for: input)
|
||||
|
||||
if let calloutView = self.calloutViews[mappedInput]
|
||||
{
|
||||
// Ensure the input we display has a higher priority.
|
||||
calloutView.input = (input.displayPriority > previousInput.displayPriority) ? input : previousInput
|
||||
}
|
||||
else
|
||||
{
|
||||
calloutView.input = input
|
||||
if let previousInput = calloutView.input
|
||||
{
|
||||
// Ensure the input we display has a higher priority.
|
||||
calloutView.input = (input.displayPriority > previousInput.displayPriority) ? input : previousInput
|
||||
}
|
||||
else
|
||||
{
|
||||
calloutView.input = input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,6 +270,8 @@ private extension ControllerInputsViewController
|
||||
{
|
||||
func updateActiveCalloutView(with controllerInput: Input?)
|
||||
{
|
||||
guard let inputMapping = self.inputMappings[self.system] else { return }
|
||||
|
||||
guard let activeCalloutView = self.activeCalloutView else { return }
|
||||
|
||||
guard let input = self.calloutViews.first(where: { $0.value == activeCalloutView })?.key else { return }
|
||||
@ -271,20 +291,22 @@ private extension ControllerInputsViewController
|
||||
}
|
||||
}
|
||||
|
||||
for supportedInput in self.inputMapping.supportedControllerInputs
|
||||
{
|
||||
let mappedInput = self.mappedInput(for: supportedInput)
|
||||
|
||||
if mappedInput == input
|
||||
self.managedObjectContext.performAndWait {
|
||||
for supportedInput in inputMapping.supportedControllerInputs
|
||||
{
|
||||
// Set all existing controller inputs that currently map to "input" to instead map to nil.
|
||||
self.inputMapping.set(nil, forControllerInput: supportedInput)
|
||||
let mappedInput = self.mappedInput(for: supportedInput)
|
||||
|
||||
if mappedInput == input
|
||||
{
|
||||
// Set all existing controller inputs that currently map to "input" to instead map to nil.
|
||||
inputMapping.set(nil, forControllerInput: supportedInput)
|
||||
}
|
||||
}
|
||||
|
||||
if let controllerInput = controllerInput
|
||||
{
|
||||
inputMapping.set(input, forControllerInput: controllerInput)
|
||||
}
|
||||
}
|
||||
|
||||
if let controllerInput = controllerInput
|
||||
{
|
||||
self.inputMapping.set(input, forControllerInput: controllerInput)
|
||||
}
|
||||
|
||||
activeCalloutView.input = controllerInput
|
||||
@ -352,7 +374,11 @@ private extension ControllerInputsViewController
|
||||
{
|
||||
func mappedInput(for input: Input) -> AnyInput
|
||||
{
|
||||
guard let mappedInput = self.inputMapping.input(forControllerInput: input) else {
|
||||
guard let inputMapping = self.inputMappings[self.system] else {
|
||||
fatalError("Input mapping for current system does not exist.")
|
||||
}
|
||||
|
||||
guard let mappedInput = inputMapping.input(forControllerInput: input) else {
|
||||
fatalError("Mapped input for provided input does not exist.")
|
||||
}
|
||||
|
||||
@ -450,6 +476,8 @@ extension ControllerInputsViewController: GameControllerReceiver
|
||||
{
|
||||
func gameController(_ gameController: GameController, didActivate controllerInput: DeltaCore.Input)
|
||||
{
|
||||
guard self.isViewLoaded else { return }
|
||||
|
||||
switch gameController
|
||||
{
|
||||
case self.gameViewController.controllerView:
|
||||
|
||||
@ -36,7 +36,7 @@ private class LocalDeviceController: NSObject, GameController
|
||||
|
||||
let inputType: GameControllerInputType = .standard
|
||||
|
||||
var inputMapping: GameControllerInputMappingProtocol?
|
||||
var defaultInputMapping: GameControllerInputMappingProtocol?
|
||||
}
|
||||
|
||||
class ControllersSettingsViewController: UITableViewController
|
||||
|
||||
@ -286,12 +286,12 @@
|
||||
<navigationItem key="navigationItem" id="UeP-Yr-9jA">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="QfC-sf-WbP">
|
||||
<connections>
|
||||
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="cancelControllerControls" unwindAction="unwindFromControllerControlsViewController:" id="AkD-Lu-h5b"/>
|
||||
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="cancelControllerInputs" unwindAction="unwindFromControllerControlsViewController:" id="AkD-Lu-h5b"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" style="done" systemItem="save" id="WHh-7W-jpl">
|
||||
<connections>
|
||||
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="saveControllerControls" unwindAction="unwindFromControllerControlsViewController:" id="4xr-OB-4dx"/>
|
||||
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="saveControllerInputs" unwindAction="unwindFromControllerControlsViewController:" id="4xr-OB-4dx"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user