Skip to content

Commit

Permalink
[New] Added PlayCase Mode
Browse files Browse the repository at this point in the history
Adjusts joystick behavior and menu positions to be compatible with PlayCase when enabled
  • Loading branch information
LitRitt committed Sep 10, 2024
1 parent 589fa35 commit 54796c4
Show file tree
Hide file tree
Showing 19 changed files with 293 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Cores/DeltaCore
20 changes: 20 additions & 0 deletions Ignited.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
AC8C213529FDAE400044748D /* OptionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8C20FD29FDAAB90044748D /* OptionPickerView.swift */; };
AC8C213629FDAE400044748D /* OptionToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8C20FF29FDAACB0044748D /* OptionToggleView.swift */; };
AC8E5BCA29CCF77700F32D19 /* UIColor+AdjustHSBA.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8E5BC929CCF77700F32D19 /* UIColor+AdjustHSBA.swift */; };
AC96420B2C8F8BF7003D0485 /* PlayCaseOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC96420A2C8F8BF7003D0485 /* PlayCaseOptions.swift */; };
AC9860E62C8FFCA200F05E10 /* GifImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9860E52C8FFCA100F05E10 /* GifImageView.swift */; };
AC9860E92C8FFD2300F05E10 /* playcase.gif in Resources */ = {isa = PBXBuildFile; fileRef = AC9860E82C8FFD2300F05E10 /* playcase.gif */; };
AC99B33D2B254B10001A2A1F /* PauseMenuOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC99B33C2B254B10001A2A1F /* PauseMenuOptions.swift */; };
ACA97B742BD411E8005AF0DA /* PurchaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACA97B732BD411E8005AF0DA /* PurchaseView.swift */; };
ACAAE2FA2A0E337F004E0B3A /* CGColor+RGB.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAAE2F92A0E337F004E0B3A /* CGColor+RGB.swift */; };
Expand Down Expand Up @@ -499,6 +502,9 @@
AC8C211929FDACB30044748D /* EnvironmentValues+FeatureOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+FeatureOption.swift"; sourceTree = "<group>"; };
AC8C211F29FDAD970044748D /* libFeatures.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFeatures.a; sourceTree = BUILT_PRODUCTS_DIR; };
AC8E5BC929CCF77700F32D19 /* UIColor+AdjustHSBA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AdjustHSBA.swift"; sourceTree = "<group>"; };
AC96420A2C8F8BF7003D0485 /* PlayCaseOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayCaseOptions.swift; sourceTree = "<group>"; };
AC9860E52C8FFCA100F05E10 /* GifImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifImageView.swift; sourceTree = "<group>"; };
AC9860E82C8FFD2300F05E10 /* playcase.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = playcase.gif; sourceTree = "<group>"; };
AC99B33C2B254B10001A2A1F /* PauseMenuOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PauseMenuOptions.swift; sourceTree = "<group>"; };
ACA97B732BD411E8005AF0DA /* PurchaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseView.swift; sourceTree = "<group>"; };
ACA97B7B2BD5A624005AF0DA /* Ignited.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ignited.entitlements; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1045,6 +1051,7 @@
isa = PBXGroup;
children = (
AC89DC982A4CACFC000DBD0C /* ControllerFeatures.swift */,
AC96420A2C8F8BF7003D0485 /* PlayCaseOptions.swift */,
ACCD9DBA2A01E48B00B235C4 /* SkinOptions.swift */,
AC89DC9A2A4CADD6000DBD0C /* BackgroundBlurOptions.swift */,
ACBFE6D52A590CA5002DDD04 /* ControllerOptions.swift */,
Expand Down Expand Up @@ -1121,6 +1128,14 @@
path = Features;
sourceTree = "<group>";
};
AC9860E72C8FFD1400F05E10 /* GIFs */ = {
isa = PBXGroup;
children = (
AC9860E82C8FFD2300F05E10 /* playcase.gif */,
);
path = GIFs;
sourceTree = "<group>";
};
ACAB77192B468C50001AB619 /* Protocols */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1303,6 +1318,7 @@
BF5942571E09BB5D0051894B /* Components */ = {
isa = PBXGroup;
children = (
AC9860E52C8FFCA100F05E10 /* GifImageView.swift */,
ACE43DC82B473C0E00AD9287 /* ToastView.swift */,
ACAB773A2B46926F001AB619 /* Keychain.swift */,
BF4828871F90290F00028B97 /* Action.swift */,
Expand Down Expand Up @@ -1601,6 +1617,7 @@
BFEC732F1AAECCBD00650035 /* Resources */ = {
isa = PBXGroup;
children = (
AC9860E72C8FFD1400F05E10 /* GIFs */,
AC53406029E32139009EDF08 /* Button Sounds */,
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */,
BF02D5D91DDEBB3000A5E131 /* openvgdb.sqlite */,
Expand Down Expand Up @@ -1930,6 +1947,7 @@
ACCDD4882A6386020034A6C0 /* beep.mp3 in Resources */,
AC4D519E2B09F881008B245B /* Settings.storyboard in Resources */,
BF1F45AB21AF4B5800EF9895 /* SyncResultsViewController.storyboard in Resources */,
AC9860E92C8FFD2300F05E10 /* playcase.gif in Resources */,
AC4D51992B09F848008B245B /* PauseMenu.storyboard in Resources */,
AC7262EC2C49B98B0009B577 /* AboutPatreonHeaderView.xib in Resources */,
AC4D519C2B09F871008B245B /* PausePresentationControllerContentView.xib in Resources */,
Expand Down Expand Up @@ -2230,6 +2248,7 @@
AC39C7632BAC66F400C8F07D /* GBCoreFeatures.swift in Sources */,
D5864978297756CE0081477E /* CheatBaseView.swift in Sources */,
ACD5FA032A0C4C84005CDCF1 /* GBFeatures.swift in Sources */,
AC9860E62C8FFCA200F05E10 /* GifImageView.swift in Sources */,
BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */,
AC7262F22C49BAB30009B577 /* PremiumPatronsView.swift in Sources */,
ACF7E31F29F75BA0000FE071 /* PHPhotoLibrary+Authorization.swift in Sources */,
Expand Down Expand Up @@ -2274,6 +2293,7 @@
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
AC7262E42C49B4AE0009B577 /* PatreonSecrets.swift in Sources */,
AC0076DE2B509B4200EBC46C /* LowBatteryOptions.swift in Sources */,
AC96420B2C8F8BF7003D0485 /* PlayCaseOptions.swift in Sources */,
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
BF713C0822499ED4004A1A2B /* PreviousHarmony.xcdatamodeld in Sources */,
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
Expand Down
31 changes: 31 additions & 0 deletions Ignited/Components/GifImageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// GifImageView.swift
// Ignited
//
// Created by Chris Rittenhouse on 9/10/24.
// Copyright © 2024 LitRitt. All rights reserved.
//

import SwiftUI
import WebKit

struct GifImageView: UIViewRepresentable {
private let name: String
init(_ name: String) {
self.name = name
}

func makeUIView(context: Context) -> WKWebView {
let webview = WKWebView()
let url = Bundle.main.url(forResource: name, withExtension: "gif")!
let data = try! Data(contentsOf: url)
webview.load(data, mimeType: "image/gif", characterEncodingName: "UTF-8", baseURL: url.deletingLastPathComponent())
webview.isOpaque = false
webview.backgroundColor = .clear
return webview
}

func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.reload()
}
}
9 changes: 6 additions & 3 deletions Ignited/Emulation/GameViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ extension GameViewController
self.performMicrophoneAction()
}

pauseViewController.playCaseItem?.isSelected = Settings.controllerFeatures.playCase.isEnabled

func makeFastForwardSpeedMenu() -> UIMenu
{
var fastForwardOptions: [UIMenuElement] = []
Expand Down Expand Up @@ -1078,7 +1080,7 @@ private extension GameViewController
self.controllerView.updateControllerSkin()
self.updateControllerSkin()

self.controllerView.isRelativeTrackingEnabled = Settings.controllerFeatures.skin.thumbstickMode == .relative
self.controllerView.isRelativeTrackingEnabled = (Settings.controllerFeatures.skin.thumbstickMode == .relative) || Settings.controllerFeatures.playCase.isEnabled

self.updateButtonAudioFeedbackSound()
self.updateBackgroundBlur()
Expand Down Expand Up @@ -2651,12 +2653,13 @@ extension GameViewController
let quickSettingsView = QuickSettingsView.makeViewController(gameViewController: self)

if let sheet = quickSettingsView.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.detents = Settings.controllerFeatures.playCase.isEnabled ? [.large()] : [.medium(), .large()]
sheet.largestUndimmedDetentIdentifier = nil
sheet.prefersScrollingExpandsWhenScrolledToEdge = true
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = false
sheet.prefersGrabberVisible = true
sheet.presentedViewController.additionalSafeAreaInsets.bottom = Settings.controllerFeatures.playCase.isEnabled ? (UIScreen.main.bounds.height * 0.4) : 0
}

self.present(quickSettingsView, animated: true, completion: nil)
Expand Down Expand Up @@ -3413,7 +3416,7 @@ private extension GameViewController
self.updateControllerSkinCustomization()

case Settings.controllerFeatures.skin.$thumbstickMode.settingsKey:
self.controllerView.isRelativeTrackingEnabled = Settings.controllerFeatures.skin.thumbstickMode == .relative
self.controllerView.isRelativeTrackingEnabled = (Settings.controllerFeatures.skin.thumbstickMode == .relative) || Settings.controllerFeatures.playCase.isEnabled

case Settings.touchFeedbackFeatures.touchVibration.$strength.settingsKey:
self.controllerView.hapticFeedbackStrength = Settings.touchFeedbackFeatures.touchVibration.strength
Expand Down
11 changes: 11 additions & 0 deletions Ignited/Extensions/Graphics/UIImage+SymbolTemplates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ extension UIImage
return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: orientation).withRenderingMode(.alwaysTemplate).withTintColor(accentColor)
}

static func customSymbolWithTemplate(name: String, pointSize: CGFloat = 30, accentColor: UIColor = UIColor.themeColor, orientation: Orientation = .up) -> UIImage
{
guard let symbolImage = UIImage(named: name)?.withConfiguration(SymbolConfiguration(pointSize: pointSize)),
let cgImage = symbolImage.cgImage else
{
return UIImage()
}

return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: orientation).withRenderingMode(.alwaysTemplate).withTintColor(accentColor)
}

static func makePlaceholder(size: CGFloat = 100) -> UIImage
{
let format = UIGraphicsImageRendererFormat()
Expand Down
2 changes: 1 addition & 1 deletion Ignited/Features/Advanced/PowerUserOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ extension PowerUserOptions
Settings.n64Features.openGLES2.enabledGames = []

case .pauseMenu:
Settings.gameplayFeatures.pauseMenu.buttonOrder = ["Save State", "Load State", "Restart", "Screenshot", "Status Bar", "Sustain Buttons", "Rewind", "Fast Forward", "Microphone", "Rotation Lock", "Palettes", "Quick Settings", "Backgroud Blur", "Overscan Editor", "Cheat Codes", "Alt Skin", "Debug Mode"]
Settings.gameplayFeatures.pauseMenu.buttonOrder = ["Save State", "Load State", "Restart", "Screenshot", "Status Bar", "Sustain Buttons", "Rewind", "Fast Forward", "Microphone", "PlayCase Mode", "Rotation Lock", "Palettes", "Quick Settings", "Backgroud Blur", "Overscan Editor", "Cheat Codes", "Alt Skin", "Debug Mode"]

case .gameScreenshot:
Settings.gameplayFeatures.screenshots.saveLocation = .photos
Expand Down
7 changes: 7 additions & 0 deletions Ignited/Features/Controllers/ControllerFeatures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2023 LitRitt. All rights reserved.
//

import UIKit
import Features

struct ControllerFeatures: FeatureContainer
Expand Down Expand Up @@ -34,6 +35,12 @@ struct ControllerFeatures: FeatureContainer
options: BackgroundBlurOptions())
var backgroundBlur

@Feature(name: "PlayCase Mode",
description: "Enable features to enhance you gameplay experience while using PlayCase.",
options: PlayCaseOptions(),
attributes: [.hidden(when: { UIDevice.current.userInterfaceIdiom != .phone })])
var playCase

private init()
{
self.prepareFeatures()
Expand Down
55 changes: 55 additions & 0 deletions Ignited/Features/Controllers/PlayCaseOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// PlayCaseOptions.swift
// Ignited
//
// Created by Chris Rittenhouse on 9/9/24.
// Copyright © 2024 LitRitt. All rights reserved.
//

import SwiftUI

import Features

struct PlayCaseOptions
{
@Option(name: "Logo",
description: "PlayCase is a phone case designed to enhance your mobile emulation experience by giving you physical buttons on top of your screen.",
detailView: { _ in
HStack {
Image("PlayCaseLogo")
.resizable()
.aspectRatio(contentMode: .fit)
Spacer()
GifImageView("playcase")
.frame(width: 70, height: 100, alignment: .center)
}
.frame(height: 100)
.padding()
.displayInline()
})
var logo: Bool = false

@Option(name: "Visit PlayCase",
description: "Consider buying a PlayCase to support its creator, who strives to continue improving the product and provides excellent customer support!.",
detailView: { _ in
Button("Visit PlayCase") {
UIApplication.shared.openWebpage(site: "https://playcase.gg")
}
.font(.system(size: 17, weight: .bold, design: .default))
.foregroundColor(.accentColor)
.displayInline()
})
var visitButton: Bool = false

@Option(name: "Download Skins",
description: "Tap here to download compatible skins from the PlayCase website.",
detailView: { _ in
Button("Download Skins") {
UIApplication.shared.openWebpage(site: "https://playcase.gg/skins/")
}
.font(.system(size: 17, weight: .bold, design: .default))
.foregroundColor(.accentColor)
.displayInline()
})
var downloadSkins: Bool = false
}
3 changes: 2 additions & 1 deletion Ignited/Features/Controllers/SkinOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ struct SkinOptions

@Option(name: "Thumbstick Mode",
description: "Change the way thumbsticks on skins behave.\n\nRelative: The middle of the stick is where you first touch. Use this for touchscreen input.\n\nAbsolute: The middle of the stick is where the skin shows it. Use this for controller cases like PlayCase.",
values: SkinThumbstickMode.allCases)
values: SkinThumbstickMode.allCases,
attributes: [.hidden(when: { Settings.controllerFeatures.playCase.isEnabled })])
var thumbstickMode: SkinThumbstickMode = .relative

@Option(name: "Diagonal D-Pad Inputs",
Expand Down
2 changes: 1 addition & 1 deletion Ignited/Features/Gameplay/PauseMenuOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct PauseMenuOptions
}
}
})
var buttonOrder: [String] = ["Save State", "Load State", "Restart", "Screenshot", "Status Bar", "Sustain Buttons", "Rewind", "Fast Forward", "Microphone", "Rotation Lock", "Palettes", "Quick Settings", "Backgroud Blur", "Overscan Editor", "Cheat Codes", "Alt Skin", "Debug Mode"]
var buttonOrder: [String] = ["Save State", "Load State", "Restart", "Screenshot", "Status Bar", "Sustain Buttons", "Rewind", "Fast Forward", "Microphone", "PlayCase Mode", "Rotation Lock", "Palettes", "Quick Settings", "Backgroud Blur", "Overscan Editor", "Cheat Codes", "Alt Skin", "Debug Mode"]

@Option(name: "Shake to Open",
description: "Allows you to shake your device to open the pause menu. Only works when all other device shake features are disabled. This feature acts as a failsafe to ensure you can always access the pause menu even if you are unable to via the controller or skin. Disable at your own risk.",
Expand Down
22 changes: 12 additions & 10 deletions Ignited/Pause Menu/PauseViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class PauseViewController: UIViewController, PauseInfoProviding
var cheatCodesItem: MenuItem?
var fastForwardItem: MenuItem?
var sustainButtonsItem: MenuItem?
var playCaseItem: MenuItem?
var rewindItem: MenuItem?
var microphoneItem: MenuItem?
var rotationLockItem: MenuItem?
Expand Down Expand Up @@ -107,20 +108,11 @@ extension PauseViewController
self.pauseNavigationController.navigationBar.tintColor = UIColor.themeColor
self.pauseNavigationController.view.backgroundColor = UIColor.clear

let gridMenuViewController = self.pauseNavigationController.topViewController as! GridMenuViewController

let navigationBarAppearance = self.pauseNavigationController.navigationBar.standardAppearance.copy()
navigationBarAppearance.backgroundEffect = UIBlurEffect(style: .systemUltraThinMaterial)
// navigationBarAppearance.backgroundColor = UIColor.systemBackground.withAlphaComponent(0.2)

// navigationBarAppearance.shadowColor = UIColor.label.withAlphaComponent(0.2)
self.pauseNavigationController.navigationBar.standardAppearance = navigationBarAppearance

let transparentBarAppearance = navigationBarAppearance.copy()
transparentBarAppearance.backgroundColor = nil
transparentBarAppearance.backgroundEffect = nil
gridMenuViewController.navigationItem.standardAppearance = transparentBarAppearance

let gridMenuViewController = self.pauseNavigationController.topViewController as! GridMenuViewController
gridMenuViewController.items = self.pauseItems

case "saveStates":
Expand Down Expand Up @@ -175,6 +167,7 @@ private extension PauseViewController
self.cheatCodesItem = nil
self.fastForwardItem = nil
self.sustainButtonsItem = nil
self.playCaseItem = nil
self.rewindItem = nil
self.microphoneItem = nil
self.rotationLockItem = nil
Expand Down Expand Up @@ -227,6 +220,12 @@ private extension PauseViewController
image: UIImage.symbolWithTemplate(name: "forward.fill"),
action: { _ in })

self.playCaseItem = MenuItem(text: NSLocalizedString("PlayCase Mode", comment: ""),
image: UIImage.customSymbolWithTemplate(name: "PlayCase"),
action: { _ in
Settings.controllerFeatures.playCase.isEnabled.toggle()
})

self.microphoneItem = MenuItem(text: NSLocalizedString("Microphone", comment: ""),
image: UIImage.symbolWithTemplate(name: "mic.fill"),
action: { _ in })
Expand Down Expand Up @@ -292,6 +291,8 @@ private extension PauseViewController
self.additionalSafeAreaInsets.left = 0
self.additionalSafeAreaInsets.right = 0
}

self.additionalSafeAreaInsets.bottom = Settings.controllerFeatures.playCase.isEnabled ? (UIScreen.main.bounds.height * 0.4) : 0
}
}

Expand All @@ -309,6 +310,7 @@ private extension PauseViewController
case "Sustain Buttons": return self.sustainButtonsItem
case "Rewind": return self.rewindItem
case "Fast Forward": return self.fastForwardItem
case "PlayCase Mode": return self.playCaseItem
case "Microphone": return self.microphoneItem
case "Rotation Lock": return self.rotationLockItem
case "Palettes": return self.paletteItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PausePresentationController: UIPresentationController
guard let containerView = self.containerView else { return super.frameOfPresentedViewInContainerView }

var frame: CGRect
let contentHeight = self.presentedViewController.preferredContentSize.height
let contentHeight = Settings.controllerFeatures.playCase.isEnabled ? 0 : self.presentedViewController.preferredContentSize.height

if contentHeight == 0
{
Expand Down
Loading

0 comments on commit 54796c4

Please sign in to comment.