├── AmongKey.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── andresavic.xcuserdatad │ │ └── IDEFindNavigatorScopes.plist └── xcuserdata │ └── andresavic.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── AmongKey ├── AmongKeyApp.swift ├── AmongUsClassifier.mlmodel ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── Contents.json │ └── UI.imageset │ │ ├── Contents.json │ │ └── UI.png ├── ContentView.swift ├── Info.plist ├── Keymapping.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── Update.swift ├── LICENSE ├── README.md └── preview.gif /AmongKey.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A100B2EB2575110800719402 /* Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = A100B2EA2575110800719402 /* Update.swift */; }; 11 | A100B2EF25751C4900719402 /* Keymapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = A100B2EE25751C4900719402 /* Keymapping.swift */; }; 12 | A156D92C256D78F50057805F /* AmongKeyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A156D92B256D78F50057805F /* AmongKeyApp.swift */; }; 13 | A156D92E256D78F50057805F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A156D92D256D78F50057805F /* ContentView.swift */; }; 14 | A156D930256D78F60057805F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A156D92F256D78F60057805F /* Assets.xcassets */; }; 15 | A156D933256D78F60057805F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A156D932256D78F60057805F /* Preview Assets.xcassets */; }; 16 | A1E56DA025954D64007FCCDB /* AmongUsClassifier.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = A1E56D9F25954D64007FCCDB /* AmongUsClassifier.mlmodel */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | A100B2EA2575110800719402 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = ""; }; 21 | A100B2EE25751C4900719402 /* Keymapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keymapping.swift; sourceTree = ""; }; 22 | A156D928256D78F50057805F /* AmongKey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AmongKey.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | A156D92B256D78F50057805F /* AmongKeyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmongKeyApp.swift; sourceTree = ""; }; 24 | A156D92D256D78F50057805F /* ContentView.swift */ = {isa = PBXFileReference; indentWidth = 3; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 25 | A156D92F256D78F60057805F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | A156D932256D78F60057805F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 27 | A156D934256D78F60057805F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | A1E56D9F25954D64007FCCDB /* AmongUsClassifier.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = AmongUsClassifier.mlmodel; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | A156D925256D78F50057805F /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | A156D91F256D78F50057805F = { 43 | isa = PBXGroup; 44 | children = ( 45 | A156D92A256D78F50057805F /* AmongKey */, 46 | A156D929256D78F50057805F /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | A156D929256D78F50057805F /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | A156D928256D78F50057805F /* AmongKey.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | A156D92A256D78F50057805F /* AmongKey */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | A1E56D9F25954D64007FCCDB /* AmongUsClassifier.mlmodel */, 62 | A156D92B256D78F50057805F /* AmongKeyApp.swift */, 63 | A156D92D256D78F50057805F /* ContentView.swift */, 64 | A156D92F256D78F60057805F /* Assets.xcassets */, 65 | A156D934256D78F60057805F /* Info.plist */, 66 | A156D931256D78F60057805F /* Preview Content */, 67 | A100B2EA2575110800719402 /* Update.swift */, 68 | A100B2EE25751C4900719402 /* Keymapping.swift */, 69 | ); 70 | path = AmongKey; 71 | sourceTree = ""; 72 | }; 73 | A156D931256D78F60057805F /* Preview Content */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | A156D932256D78F60057805F /* Preview Assets.xcassets */, 77 | ); 78 | path = "Preview Content"; 79 | sourceTree = ""; 80 | }; 81 | /* End PBXGroup section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | A156D927256D78F50057805F /* AmongKey */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = A156D938256D78F60057805F /* Build configuration list for PBXNativeTarget "AmongKey" */; 87 | buildPhases = ( 88 | A156D924256D78F50057805F /* Sources */, 89 | A156D925256D78F50057805F /* Frameworks */, 90 | A156D926256D78F50057805F /* Resources */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = AmongKey; 97 | productName = AmongKey; 98 | productReference = A156D928256D78F50057805F /* AmongKey.app */; 99 | productType = "com.apple.product-type.application"; 100 | }; 101 | /* End PBXNativeTarget section */ 102 | 103 | /* Begin PBXProject section */ 104 | A156D920256D78F50057805F /* Project object */ = { 105 | isa = PBXProject; 106 | attributes = { 107 | LastSwiftUpdateCheck = 1220; 108 | LastUpgradeCheck = 1220; 109 | TargetAttributes = { 110 | A156D927256D78F50057805F = { 111 | CreatedOnToolsVersion = 12.2; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = A156D923256D78F50057805F /* Build configuration list for PBXProject "AmongKey" */; 116 | compatibilityVersion = "Xcode 9.3"; 117 | developmentRegion = en; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | Base, 122 | ); 123 | mainGroup = A156D91F256D78F50057805F; 124 | productRefGroup = A156D929256D78F50057805F /* Products */; 125 | projectDirPath = ""; 126 | projectRoot = ""; 127 | targets = ( 128 | A156D927256D78F50057805F /* AmongKey */, 129 | ); 130 | }; 131 | /* End PBXProject section */ 132 | 133 | /* Begin PBXResourcesBuildPhase section */ 134 | A156D926256D78F50057805F /* Resources */ = { 135 | isa = PBXResourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | A156D933256D78F60057805F /* Preview Assets.xcassets in Resources */, 139 | A156D930256D78F60057805F /* Assets.xcassets in Resources */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | /* End PBXResourcesBuildPhase section */ 144 | 145 | /* Begin PBXSourcesBuildPhase section */ 146 | A156D924256D78F50057805F /* Sources */ = { 147 | isa = PBXSourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | A1E56DA025954D64007FCCDB /* AmongUsClassifier.mlmodel in Sources */, 151 | A156D92E256D78F50057805F /* ContentView.swift in Sources */, 152 | A156D92C256D78F50057805F /* AmongKeyApp.swift in Sources */, 153 | A100B2EB2575110800719402 /* Update.swift in Sources */, 154 | A100B2EF25751C4900719402 /* Keymapping.swift in Sources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXSourcesBuildPhase section */ 159 | 160 | /* Begin XCBuildConfiguration section */ 161 | A156D936256D78F60057805F /* Debug */ = { 162 | isa = XCBuildConfiguration; 163 | buildSettings = { 164 | ALWAYS_SEARCH_USER_PATHS = NO; 165 | CLANG_ANALYZER_NONNULL = YES; 166 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 167 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 168 | CLANG_CXX_LIBRARY = "libc++"; 169 | CLANG_ENABLE_MODULES = YES; 170 | CLANG_ENABLE_OBJC_ARC = YES; 171 | CLANG_ENABLE_OBJC_WEAK = YES; 172 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 173 | CLANG_WARN_BOOL_CONVERSION = YES; 174 | CLANG_WARN_COMMA = YES; 175 | CLANG_WARN_CONSTANT_CONVERSION = YES; 176 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 177 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 178 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 179 | CLANG_WARN_EMPTY_BODY = YES; 180 | CLANG_WARN_ENUM_CONVERSION = YES; 181 | CLANG_WARN_INFINITE_RECURSION = YES; 182 | CLANG_WARN_INT_CONVERSION = YES; 183 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 184 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 185 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 186 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 187 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 188 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 189 | CLANG_WARN_STRICT_PROTOTYPES = YES; 190 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 191 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 192 | CLANG_WARN_UNREACHABLE_CODE = YES; 193 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 194 | COPY_PHASE_STRIP = NO; 195 | DEBUG_INFORMATION_FORMAT = dwarf; 196 | ENABLE_STRICT_OBJC_MSGSEND = YES; 197 | ENABLE_TESTABILITY = YES; 198 | GCC_C_LANGUAGE_STANDARD = gnu11; 199 | GCC_DYNAMIC_NO_PIC = NO; 200 | GCC_NO_COMMON_BLOCKS = YES; 201 | GCC_OPTIMIZATION_LEVEL = 0; 202 | GCC_PREPROCESSOR_DEFINITIONS = ( 203 | "DEBUG=1", 204 | "$(inherited)", 205 | ); 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | MACOSX_DEPLOYMENT_TARGET = 11.0; 213 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 214 | MTL_FAST_MATH = YES; 215 | ONLY_ACTIVE_ARCH = YES; 216 | SDKROOT = macosx; 217 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 218 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 219 | }; 220 | name = Debug; 221 | }; 222 | A156D937256D78F60057805F /* Release */ = { 223 | isa = XCBuildConfiguration; 224 | buildSettings = { 225 | ALWAYS_SEARCH_USER_PATHS = NO; 226 | CLANG_ANALYZER_NONNULL = YES; 227 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_ENABLE_OBJC_WEAK = YES; 233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_COMMA = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 239 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 240 | CLANG_WARN_EMPTY_BODY = YES; 241 | CLANG_WARN_ENUM_CONVERSION = YES; 242 | CLANG_WARN_INFINITE_RECURSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 246 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 248 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 249 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 250 | CLANG_WARN_STRICT_PROTOTYPES = YES; 251 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 252 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | COPY_PHASE_STRIP = NO; 256 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 257 | ENABLE_NS_ASSERTIONS = NO; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu11; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | MACOSX_DEPLOYMENT_TARGET = 11.0; 268 | MTL_ENABLE_DEBUG_INFO = NO; 269 | MTL_FAST_MATH = YES; 270 | SDKROOT = macosx; 271 | SWIFT_COMPILATION_MODE = wholemodule; 272 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 273 | }; 274 | name = Release; 275 | }; 276 | A156D939256D78F60057805F /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 280 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 281 | CODE_SIGN_IDENTITY = "Apple Development"; 282 | CODE_SIGN_STYLE = Automatic; 283 | COMBINE_HIDPI_IMAGES = YES; 284 | DEVELOPMENT_ASSET_PATHS = "\"AmongKey/Preview Content\""; 285 | DEVELOPMENT_TEAM = XQFTHXDY4Y; 286 | ENABLE_HARDENED_RUNTIME = YES; 287 | ENABLE_PREVIEWS = YES; 288 | INFOPLIST_FILE = AmongKey/Info.plist; 289 | LD_RUNPATH_SEARCH_PATHS = ( 290 | "$(inherited)", 291 | "@executable_path/../Frameworks", 292 | ); 293 | MACOSX_DEPLOYMENT_TARGET = 11.0; 294 | MARKETING_VERSION = 1.5; 295 | PRODUCT_BUNDLE_IDENTIFIER = at.andresavic.AmongKey; 296 | PRODUCT_NAME = "$(TARGET_NAME)"; 297 | SWIFT_VERSION = 5.0; 298 | }; 299 | name = Debug; 300 | }; 301 | A156D93A256D78F60057805F /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 305 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 306 | CODE_SIGN_IDENTITY = "Apple Development"; 307 | CODE_SIGN_STYLE = Automatic; 308 | COMBINE_HIDPI_IMAGES = YES; 309 | DEVELOPMENT_ASSET_PATHS = "\"AmongKey/Preview Content\""; 310 | DEVELOPMENT_TEAM = XQFTHXDY4Y; 311 | ENABLE_HARDENED_RUNTIME = YES; 312 | ENABLE_PREVIEWS = YES; 313 | INFOPLIST_FILE = AmongKey/Info.plist; 314 | LD_RUNPATH_SEARCH_PATHS = ( 315 | "$(inherited)", 316 | "@executable_path/../Frameworks", 317 | ); 318 | MACOSX_DEPLOYMENT_TARGET = 11.0; 319 | MARKETING_VERSION = 1.5; 320 | PRODUCT_BUNDLE_IDENTIFIER = at.andresavic.AmongKey; 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | SWIFT_VERSION = 5.0; 323 | }; 324 | name = Release; 325 | }; 326 | /* End XCBuildConfiguration section */ 327 | 328 | /* Begin XCConfigurationList section */ 329 | A156D923256D78F50057805F /* Build configuration list for PBXProject "AmongKey" */ = { 330 | isa = XCConfigurationList; 331 | buildConfigurations = ( 332 | A156D936256D78F60057805F /* Debug */, 333 | A156D937256D78F60057805F /* Release */, 334 | ); 335 | defaultConfigurationIsVisible = 0; 336 | defaultConfigurationName = Release; 337 | }; 338 | A156D938256D78F60057805F /* Build configuration list for PBXNativeTarget "AmongKey" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | A156D939256D78F60057805F /* Debug */, 342 | A156D93A256D78F60057805F /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | /* End XCConfigurationList section */ 348 | }; 349 | rootObject = A156D920256D78F50057805F /* Project object */; 350 | } 351 | -------------------------------------------------------------------------------- /AmongKey.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AmongKey.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AmongKey.xcodeproj/project.xcworkspace/xcuserdata/andresavic.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AmongKey.xcodeproj/xcuserdata/andresavic.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /AmongKey.xcodeproj/xcuserdata/andresavic.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AmongKey.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /AmongKey/AmongKeyApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AmongKeyApp.swift 3 | // AmongKey 4 | // 5 | // Created by Andre Savic on 24.11.20. 6 | // 7 | 8 | import SwiftUI 9 | import CoreML 10 | import Vision 11 | import ImageIO 12 | 13 | @main 14 | struct AmongKeyApp: App { 15 | var body: some Scene { 16 | WindowGroup { 17 | ContentView().onAppear(perform: { 18 | setup() 19 | }) 20 | } 21 | } 22 | } 23 | 24 | var up: Int = 0 25 | var down: Int = 0 26 | var left: Int = 0 27 | var right: Int = 0 28 | 29 | var mouseIsDown: Bool = false 30 | var mouseIsClicking: Bool = false 31 | var mouseClickLock = NSLock() 32 | 33 | 34 | func calculateUIPosition(ui_position: (ui_x: Int, ui_y: Int)) -> (x: Double, y: Double) { 35 | let bottom_right_button = (x: 2536.0, y: 1873.0) 36 | let vertical_spacing = 400.0; 37 | let horizontal_spacing = 350.0; 38 | return (x: bottom_right_button.x - horizontal_spacing * Double(ui_position.ui_x), 39 | y: bottom_right_button.y - vertical_spacing * Double(ui_position.ui_y)) 40 | } 41 | 42 | 43 | let JOYSTICK = (x: 227.589, y: 1873.375) 44 | let USE_BUTTON = calculateUIPosition(ui_position: (ui_x: 0, ui_y: 0)) 45 | let KILL_BUTTON = calculateUIPosition(ui_position: (ui_x: 0, ui_y: 1)) 46 | let REPORT_BUTTON = calculateUIPosition(ui_position: (ui_x: 1, ui_y: 0)) 47 | let SABOTAGE_BUTTON = calculateUIPosition(ui_position: (ui_x: 1, ui_y: 1)) 48 | let VENT_BUTTON = calculateUIPosition(ui_position: (ui_x: 2, ui_y: 1)) 49 | let ROLE_BUTTON = calculateUIPosition(ui_position: (ui_x: 2, ui_y: 0)) 50 | 51 | let ESC_BUTTON = (x: 120.0, y: 420.0) 52 | let MAP_BUTTON = (x: 2665.0, y: 421.0) 53 | let LOBBY_CHAT_BUTTON = (x: 2395.0, y: 110.0) 54 | let MEETING_CHAT_BUTTON = (x: 2411.0, y: 301.0) 55 | let LOBBY_CHAT_SEND_BUTTON = (x: 2105.0, y: 1585.0) 56 | let MEETING_CHAT_SEND_BUTTON = (x: 1850.0, y: 1650.0) 57 | 58 | var originalPosition = (x: 0.0, y: 0.0) 59 | var originalSize = (height: 0.0, width: 0.0) 60 | 61 | var X: Double = -1.0 //X Position of the Among Us Window 62 | var Y: Double = -1.0 //Y Position of the Among Us Window 63 | 64 | var Height: Double = -1 //Height of the Among Us Window 65 | var Width: Double = -1 //Width of the Among Us Window 66 | 67 | var topmost: Bool = false // Is Among Us in focus 68 | 69 | var gamestate: String = "Menu" //Curent game state 70 | 71 | var storeScreenshot: String = "" 72 | 73 | var lastX: Double = 0.0 //Last down X position of cursor 74 | var lastY: Double = 0.0 //Last down Y position of cursor 75 | 76 | func setup() { 77 | Update.checkForUpdate(completion: { download in 78 | DispatchQueue.main.async { 79 | globaleState.shared.download = download 80 | } 81 | }) 82 | 83 | let permission = AXIsProcessTrustedWithOptions( 84 | [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary) 85 | 86 | if (permission) { 87 | createKeybinding() 88 | } 89 | //Run movement all 25ms 90 | Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true) { _ in 91 | movement() 92 | } 93 | 94 | //Caputre Among Us window all 250ms 95 | Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { _ in 96 | amongusWindow() 97 | } 98 | } 99 | 100 | 101 | func movement() { 102 | if (X == -1 && Y == -1) { rescueMouse(); return } 103 | 104 | if (topmost == false) { rescueMouse(); return } 105 | 106 | if (gamestate != "Ingame" && gamestate != "Lobby") { rescueMouse(); return } 107 | 108 | if ((up + down + left + right) == 0) { rescueMouse(); return } 109 | 110 | let lockSuccess = mouseClickLock.try() 111 | if (!lockSuccess){ return } 112 | var joystickY: Double = calcPos(pos: JOYSTICK).y 113 | var joystickX: Double = calcPos(pos: JOYSTICK).x 114 | 115 | if (mouseIsDown == false) { 116 | //Set cursor in the center of the Joystick 117 | mouseDown(pos: CGPoint(x: X + calcPos(pos: JOYSTICK).x, y: Y + calcPos(pos: JOYSTICK).y)); 118 | } 119 | 120 | //Set the cursor to the movemment direction 121 | joystickY = joystickY - (joysticDistance() * Double(up)); 122 | joystickY = joystickY + (joysticDistance() * Double(down)); 123 | joystickX = joystickX - (joysticDistance() * Double(left)); 124 | joystickX = joystickX + (joysticDistance() * Double(right)); 125 | 126 | lastX = X + joystickX 127 | lastY = Y + joystickY 128 | 129 | mouseDown(pos: CGPoint(x: lastX, y: lastY)); 130 | mouseIsDown = true 131 | mouseClickLock.unlock() 132 | } 133 | 134 | func calcPos(pos: (x: Double, y: Double)) -> (x: Double, y: Double) { 135 | return (x: pos.x / (2800.0 / Width), y: pos.y / (2100 / Height)) 136 | } 137 | 138 | func joysticDistance() -> Double { 139 | return 120 / (2800.0 / Width) 140 | } 141 | 142 | func amongusWindow() { 143 | let options = CGWindowListOption(arrayLiteral: CGWindowListOption.optionAll) 144 | let cgWindowListInfo = CGWindowListCopyWindowInfo(options, CGWindowID(0)) 145 | let cgWindowListInfo2 = cgWindowListInfo as NSArray? as? [[String: AnyObject]] 146 | let frontMostAppId = NSWorkspace.shared.frontmostApplication!.processIdentifier 147 | var image: CGImage 148 | for windowDic in cgWindowListInfo2! { 149 | //Determine the Among Us window 150 | if (windowDic["kCGWindowOwnerName"] as! String == "Among Us" && windowDic["kCGWindowStoreType"] as! Int == 1 && windowDic["kCGWindowAlpha"] as! Int == 1) { 151 | let ownerProcessID = windowDic["kCGWindowOwnerPID"] as! Int 152 | let bounds = windowDic["kCGWindowBounds"] as! [String: Double] 153 | 154 | if (bounds["Height"]! <= 500 || bounds["Width"]! <= 500) { return } //Fix for Macs with Touchbar 155 | 156 | originalPosition = (x: bounds["X"]!, y: bounds["Y"]!) 157 | originalSize = (height: bounds["Height"]!, width: bounds["Width"]!) 158 | 159 | X = originalPosition.x 160 | Y = originalPosition.y 161 | Height = originalSize.height 162 | Width = originalSize.width 163 | 164 | if (isFullscreen()) { 165 | let f = 2800.0 / 2100.0 166 | X = (Width - (Height * f)) / 2.0 167 | Y = 0.167 //I dont know why but there are wered 168 | Width = (Height * f) 169 | }else{ 170 | Y = Y + 29 // Window Topbar 171 | Height = Height - 29 // Window Topbar 172 | } 173 | 174 | topmost = (frontMostAppId == ownerProcessID) 175 | 176 | if (topmost == false) { return } //Only capture Among Us window when it is in focus 177 | 178 | //Create capture of Window 179 | guard let windowImage: CGImage = 180 | CGWindowListCreateImage(.null, .optionIncludingWindow, (windowDic["kCGWindowNumber"] as! NSNumber).uint32Value, 181 | [.boundsIgnoreFraming, .nominalResolution]) else { return } 182 | 183 | //Push the capture into the Image Classifier model 184 | //Source: https://developer.apple.com/documentation/createml/creating_an_image_classifier_model 185 | do { 186 | 187 | if (isFullscreen()){ 188 | //Crop when Among Us runs in Fullscreen 189 | let cropZone = CGRect(x: X, y: 0, width: Width, height: Height) 190 | image = windowImage.cropping(to: cropZone)! 191 | }else{ 192 | image = windowImage 193 | } 194 | 195 | let model = try VNCoreMLModel(for: AmongUsClassifier(configuration: MLModelConfiguration()).model) 196 | let request = VNCoreMLRequest(model: model, completionHandler: AmongUsClassifierResult) 197 | let handler = VNImageRequestHandler(cgImage: image) 198 | try handler.perform([request]) 199 | } catch { 200 | print(error) 201 | } 202 | 203 | //Write capture to disk as image for training data purposes 204 | if (storeScreenshot != "") { 205 | let picturesDirectory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)[0] 206 | 207 | let imageUrl = picturesDirectory.appendingPathComponent("/Training Data/" + storeScreenshot + "/among" + UUID().uuidString + ".png", isDirectory: false) 208 | try? image.png!.write(to: imageUrl) 209 | 210 | storeScreenshot = "" 211 | } 212 | } 213 | } 214 | } 215 | 216 | func isFullscreen() -> Bool { 217 | print("------") 218 | print(originalPosition) 219 | print(originalSize) 220 | return (originalSize.height > 619 && originalPosition.x == 0.0 && originalPosition.y == 0.0) 221 | } 222 | 223 | func AmongUsClassifierResult(request: VNRequest, error: Error?) { 224 | guard let results = request.results as? [VNClassificationObservation] else { fatalError("Error") } 225 | 226 | //Ignore results with a confidence smaller than 25% 227 | if results[0].confidence < 0.25 { return } 228 | 229 | globaleState.shared.score = Int(results[0].confidence * 100) 230 | globaleState.shared.scene = results[0].identifier 231 | 232 | gamestate = results[0].identifier 233 | } 234 | 235 | func rescueMouse() { 236 | if (mouseIsDown == true) { 237 | mouseUp(pos: CGPoint(x: lastX, y: lastY)); 238 | mouseIsDown = false; 239 | } 240 | } 241 | 242 | func simulateClick(pos: (x: Double, y: Double)) { 243 | if (X == -1 && Y == -1) { 244 | return; 245 | } 246 | let clickEventWaitTime : useconds_t = 10000; 247 | DispatchQueue.global(qos:.background).async { 248 | let pos = CGPoint(x: X + calcPos(pos: pos).x, y: Y + calcPos(pos: pos).y) 249 | mouseClickLock.lock() 250 | rescueMouse() 251 | usleep(clickEventWaitTime) 252 | mouseDown(pos: pos) 253 | usleep(clickEventWaitTime) 254 | mouseUp(pos: pos) 255 | usleep(clickEventWaitTime) 256 | mouseClickLock.unlock() 257 | } 258 | } 259 | 260 | func simulateKeypress(key: UInt16) { 261 | print("Keypress: " + String(key)) 262 | let keyDownEvent = CGEvent(keyboardEventSource: nil, virtualKey: key, keyDown: true) 263 | keyDownEvent?.flags = CGEventFlags.maskCommand 264 | keyDownEvent?.post(tap: CGEventTapLocation.cghidEventTap) 265 | 266 | usleep(10000) 267 | let keyUpEvent = CGEvent(keyboardEventSource: nil, virtualKey: key, keyDown: false) 268 | keyUpEvent?.flags = CGEventFlags.maskCommand 269 | keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap) 270 | } 271 | 272 | func mouseDown(pos: CGPoint) { 273 | let mouseDown = CGEvent(mouseEventSource: CGEventSource.init(stateID: .hidSystemState), mouseType: .leftMouseDown, 274 | mouseCursorPosition: pos, mouseButton: .left) 275 | 276 | mouseDown?.post(tap: .cghidEventTap) 277 | } 278 | 279 | func mouseUp(pos: CGPoint) { 280 | let mouseEventUp = CGEvent(mouseEventSource: CGEventSource.init(stateID: .hidSystemState), mouseType: .leftMouseUp, 281 | mouseCursorPosition: pos, mouseButton: .left) 282 | 283 | mouseEventUp?.post(tap: .cghidEventTap) 284 | } 285 | 286 | 287 | //Source: https://stackoverflow.com/a/48312429 288 | extension CGImage { 289 | var png: Data? { 290 | guard let mutableData = CFDataCreateMutable(nil, 0), 291 | let destination = CGImageDestinationCreateWithData(mutableData, "public.png" as CFString, 1, nil) else { return nil } 292 | CGImageDestinationAddImage(destination, self, nil) 293 | guard CGImageDestinationFinalize(destination) else { return nil } 294 | return mutableData as Data 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /AmongKey/AmongUsClassifier.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/AmongUsClassifier.mlmodel -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "platform" : "osx", 6 | "reference" : "findHighlightColor" 7 | }, 8 | "idiom" : "universal" 9 | } 10 | ], 11 | "info" : { 12 | "author" : "xcode", 13 | "version" : 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/UI.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "UI.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AmongKey/Assets.xcassets/UI.imageset/UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/AmongKey/Assets.xcassets/UI.imageset/UI.png -------------------------------------------------------------------------------- /AmongKey/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // AmongKey 4 | // 5 | // Created by Andre Savic on 24.11.20. 6 | // 7 | import SwiftUI 8 | 9 | class globaleState: ObservableObject { 10 | static let shared = globaleState() 11 | private init() {} 12 | @Published var score: Int = 0 13 | @Published var scene: String = "" 14 | @Published var download: String = "" 15 | } 16 | 17 | struct ContentView: View { 18 | @Environment(\.openURL) var openURL 19 | @ObservedObject var state = globaleState.shared 20 | 21 | var body: some View { 22 | VStack { 23 | Image("UI") 24 | .resizable() 25 | .frame(width: 400, height: 520) 26 | 27 | Text((self.state.scene != "" ? self.state.scene : "Nothing") + " - Confidence: " + String(self.state.score) + "%") 28 | .foregroundColor(Color.white) 29 | .frame(maxWidth: .infinity, alignment: .leading) 30 | .padding(.leading, 25).padding(.bottom, 25) 31 | .background(Color.black) 32 | 33 | Button("Update available"){ 34 | openURL(URL(string: self.state.download)!) 35 | } 36 | .isHidden(self.state.download == "", remove: true) 37 | .padding(.bottom, 25) 38 | 39 | }.frame(minWidth: 400, maxWidth: 400, alignment: .topLeading).background(Color.black) 40 | } 41 | } 42 | 43 | struct ContentView_Previews: PreviewProvider { 44 | static var previews: some View { 45 | ContentView() 46 | } 47 | } 48 | 49 | //Source: https://stackoverflow.com/a/59228385 50 | extension View { 51 | @ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View { 52 | if hidden { 53 | if !remove { 54 | self.hidden() 55 | } 56 | } else { 57 | self 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AmongKey/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSApplicationCategoryType 22 | public.app-category.utilities 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | 26 | 27 | -------------------------------------------------------------------------------- /AmongKey/Keymapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Keymapping.swift 3 | // AmongKey 4 | // 5 | // Created by Andre Savic on 30.11.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | func createKeybinding() { 11 | NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) { (event) in 12 | 13 | if (topmost == false) { return } //Only process inputs when Among Us is in focus 14 | 15 | switch (event.keyCode, gamestate) { 16 | case (13, "Ingame"), (13, "Lobby"), (126, "Ingame"), (126, "Lobby"): 17 | up = 1 18 | break 19 | case (1, "Ingame"), (1, "Lobby"), (125, "Ingame"), (125, "Lobby"): 20 | down = 1 21 | break 22 | case (0, "Ingame"), (0, "Lobby"), (123, "Ingame"), (123, "Lobby"): 23 | left = 1 24 | break 25 | case (2, "Ingame"), (2, "Lobby"), (124, "Ingame"), (124, "Lobby"): 26 | right = 1 27 | break 28 | default: 29 | break 30 | } 31 | } 32 | 33 | NSEvent.addGlobalMonitorForEvents(matching: [.keyUp]) { (event) in 34 | 35 | if (topmost == false){ return } //Only process inputs when Among Us is in focus 36 | 37 | switch (event.keyCode, gamestate) { 38 | case (13, "Ingame"), (13, "Lobby"), (126, "Ingame"), (126, "Lobby"): 39 | up = 0 40 | break 41 | case (1, "Ingame"), (1, "Lobby"), (125, "Ingame"), (125, "Lobby"): 42 | down = 0 43 | break 44 | case (0, "Ingame"), (0, "Lobby"), (123, "Ingame"), (123, "Lobby"): 45 | left = 0 46 | break 47 | case (2, "Ingame"), (2, "Lobby"), (124, "Ingame"), (124, "Lobby"): 48 | right = 0 49 | break 50 | case (36, "Lobbychat"): // Return to send message 51 | if (!isFullscreen()) { 52 | print("PRESS KEY") 53 | simulateKeypress(key: 53) //Escape to blur input field 54 | } 55 | amongusWindow() 56 | simulateClick(pos: LOBBY_CHAT_SEND_BUTTON) 57 | usleep(10000) 58 | simulateClick(pos: (x: LOBBY_CHAT_SEND_BUTTON.x - 350, y: LOBBY_CHAT_SEND_BUTTON.y)) 59 | break; 60 | case (36, "Meeting"): // Return to send message 61 | if (!isFullscreen()) { 62 | simulateKeypress(key: 53) //Escape to blur input field 63 | } 64 | amongusWindow() 65 | simulateClick(pos: MEETING_CHAT_SEND_BUTTON) 66 | usleep(10000) 67 | simulateClick(pos: (x: MEETING_CHAT_SEND_BUTTON.x - 350, y: MEETING_CHAT_SEND_BUTTON.y)) 68 | break; 69 | case (49, "Ingame"), (14, "Ingame"), (49, "Lobby"), (14, "Lobby"): // Space button 70 | simulateClick(pos: USE_BUTTON) 71 | break; 72 | case (53, "Ingame"), (53, "Lobby"): 73 | simulateClick(pos: ESC_BUTTON) 74 | break; 75 | case (12, "Ingame"): // Q - Kill 76 | simulateClick(pos: KILL_BUTTON) 77 | break; 78 | case (9, "Ingame"): // V - Vent 79 | simulateClick(pos: VENT_BUTTON) 80 | break; 81 | case (3, "Ingame"): // F - Role Ability 82 | simulateClick(pos: ROLE_BUTTON) 83 | break; 84 | case (15, "Ingame"): // R - Report 85 | simulateClick(pos: REPORT_BUTTON) 86 | break; 87 | case (48, "Ingame"): // Tab - Map 88 | simulateClick(pos: MAP_BUTTON) 89 | break; 90 | case (48, "Lobby"): // Tab to open chat and focus input field 91 | simulateClick(pos: LOBBY_CHAT_BUTTON) 92 | usleep(100000) 93 | simulateClick(pos: (x: LOBBY_CHAT_SEND_BUTTON.x - 250, y: LOBBY_CHAT_SEND_BUTTON.y)) 94 | break; 95 | case (48, "Meeting"): // Tab to open chat and focus input field 96 | simulateClick(pos: MEETING_CHAT_BUTTON) 97 | usleep(100000) 98 | simulateClick(pos: (x: MEETING_CHAT_SEND_BUTTON.x - 250, y: MEETING_CHAT_SEND_BUTTON.y)) 99 | break; 100 | case (18, _): //1 - Store AI Training Data Ingame 101 | storeScreenshot = "Ingame" 102 | break; 103 | case (19, _): //2 - Store AI Training Data Lobby 104 | storeScreenshot = "Lobby" 105 | break; 106 | case (20, _): //3 - Store AI Training Data Chat 107 | storeScreenshot = "Lobbychat" 108 | break; 109 | case (21, _): //4 - Store AI Training Data Meeting 110 | storeScreenshot = "Meeting" 111 | break; 112 | case (23, _): //5 - Store AI Training Data Menu 113 | storeScreenshot = "Menu" 114 | break; 115 | default: 116 | break 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /AmongKey/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AmongKey/Update.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Update.swift 3 | // AmongKey 4 | // 5 | // Created by Andre Savic on 30.11.20. 6 | // 7 | 8 | import Foundation 9 | 10 | class Update { 11 | static let url = URL(string: "https://andresavic.at/amongkey/update.json") 12 | 13 | struct UpdateJson: Codable { 14 | let version: Double 15 | let download: String 16 | } 17 | 18 | static func checkForUpdate(completion: @escaping (String)->()) { 19 | guard let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { 20 | return; 21 | } 22 | 23 | let request = URLRequest(url: self.url!, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 60.0) 24 | URLSession.shared.dataTask(with: request) { data, response, error in 25 | if let data = data { 26 | do { 27 | let res = try JSONDecoder().decode(UpdateJson.self, from: data) 28 | if (res.version > Double(currentVersion)!) { 29 | completion(res.download) 30 | } 31 | } catch let error { 32 | print(error) 33 | } 34 | } 35 | }.resume() 36 | 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AmongKey 2 | Keyboard controls for Among Us on Apple Silicon Macs 3 | 4 | [![AmongKey Demo](preview.gif)](https://youtu.be/GfeaROHHPgs) 5 | ## About The Project 6 | 7 | Among Us got featured on the last Apple Event for the new Macs with Apple Silicon. Steve Jobs himself would turn in his grave if he knew how horrible the UX of Among Us is on M1 Macs. You can only control the game with touch👆🏼, but there are no touch screens on Mac. 8 | 9 | So I created AmongKey! 10 | 11 | AmongKey uses the Neural Engine of the M1 processor to recognize the current gamestate and translates keyboard input signals into mouse events matching the current situation. The ML model is trained on over 1300 screenshots of Among Us gameplays. 12 | 13 | Yes, it is a hacky workaround(and a very funny side project)... but until the makers of Among Us will implement official keyboard support, you can enjoy Among Us like on a Window machine. 14 | 15 | ## Why ML for such a simple task? 16 | 17 | Among Us is heavily based on the in-game chat during the game. So in one second you are runnning through the spaceship and in the next second you have to chat with your crewmates. 18 | The chat on the Among Us mobile version is on the Mac even more broken, because you have to manually focus the inputfield and Return to send is not supported. 19 | 20 | Keeping the keymapping static would be a very bad UX because the mouse cursor would always jump on the screen when you are typing(W A S D Space). 21 | 22 | My first tries used simple color checking of specific UI elements. But after testing there was so many game situations in which this not worked out very well or the effort was way to large to catch all edge cases. For example specific tasks(security cameras) or emergency crisis tints the whole screen red. Even color values changed when changing the screen resolution. 23 | 24 | After over 15 ML model generation and collecting(playing the game 🤓) a lot of training data the results are very reliable. Even for game situations that the model was not trained on. Just mindblowing. 🤯 25 | 26 | ## Train the Machine Learning Model 27 | 28 | Download the Trainig Data: 29 | https://drive.google.com/drive/folders/1VP8d5Rle5NWI-30EeXDdZ-mcO3Q3CSVI 30 | 31 | Follow the instructions on: 32 | https://developer.apple.com/documentation/createml/creating_an_image_classifier_model -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresavic/AmongKey/882c97d41f80570cb2d9e5cacf19929c9fcf990a/preview.gif --------------------------------------------------------------------------------