diff --git a/DouShouQi_App/DouShouQi_App.xcodeproj/project.pbxproj b/DouShouQi_App/DouShouQi_App.xcodeproj/project.pbxproj index f7a8c5a..cbeefb6 100644 --- a/DouShouQi_App/DouShouQi_App.xcodeproj/project.pbxproj +++ b/DouShouQi_App/DouShouQi_App.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 6437FF132C25846F009D0EAF /* PlayingGameVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6437FF122C25846F009D0EAF /* PlayingGameVM.swift */; }; + 6437FF142C25870C009D0EAF /* DSQ.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC62C5322C1C188F0048CD0B /* DSQ.xcframework */; }; 6458345C2BF5F92300E18321 /* DouShouQi_AppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6458345B2BF5F92300E18321 /* DouShouQi_AppApp.swift */; }; 6458345E2BF5F92300E18321 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6458345D2BF5F92300E18321 /* ContentView.swift */; }; 645834602BF5F92500E18321 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6458345F2BF5F92500E18321 /* Assets.xcassets */; }; @@ -30,6 +32,7 @@ 649B59AE2BF64EAB002BAE38 /* AppImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649B59AD2BF64EAB002BAE38 /* AppImages.swift */; }; 649B59B22BF65392002BAE38 /* TextStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649B59B12BF65392002BAE38 /* TextStyles.swift */; }; 649B59B42BF653E1002BAE38 /* ViewTitleTextStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649B59B32BF653E1002BAE38 /* ViewTitleTextStyle.swift */; }; + 64D2D74E2C25D380009BD010 /* Animals.Symbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D2D74D2C25D380009BD010 /* Animals.Symbols.swift */; }; 64D992722C06281B002ACBC6 /* SystemIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D992712C06281B002ACBC6 /* SystemIcons.swift */; }; 64FC4D692C09C78000D08B8B /* SelectPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FC4D682C09C78000D08B8B /* SelectPlayerView.swift */; }; 64FC4D6B2C09C7C900D08B8B /* SelectPlayerButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FC4D6A2C09C7C900D08B8B /* SelectPlayerButtonView.swift */; }; @@ -56,7 +59,6 @@ EC62C5252C0F68830048CD0B /* PlayerVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC62C5242C0F68830048CD0B /* PlayerVM.swift */; }; EC62C5292C1974000048CD0B /* PlayersVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC62C5282C1974000048CD0B /* PlayersVM.swift */; }; EC62C52D2C197ED10048CD0B /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC62C52C2C197ED10048CD0B /* Player.swift */; }; - EC62C5332C1C188F0048CD0B /* DSQ.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC62C5322C1C188F0048CD0B /* DSQ.xcframework */; }; EC62C5382C1C64EE0048CD0B /* CoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC62C5372C1C64EE0048CD0B /* CoreManager.swift */; }; EC62C53D2C1C69200048CD0B /* DouShouQi_App.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC62C53B2C1C69200048CD0B /* DouShouQi_App.xcdatamodeld */; }; EC62C53F2C1C6D1A0048CD0B /* CDPlayerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC62C53E2C1C6D1A0048CD0B /* CDPlayerExtension.swift */; }; @@ -98,6 +100,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 6437FF122C25846F009D0EAF /* PlayingGameVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayingGameVM.swift; sourceTree = ""; }; 645834582BF5F92300E18321 /* DouShouQi_App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DouShouQi_App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6458345B2BF5F92300E18321 /* DouShouQi_AppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DouShouQi_AppApp.swift; sourceTree = ""; }; 6458345D2BF5F92300E18321 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -125,6 +128,7 @@ 649B59AD2BF64EAB002BAE38 /* AppImages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppImages.swift; sourceTree = ""; }; 649B59B12BF65392002BAE38 /* TextStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStyles.swift; sourceTree = ""; }; 649B59B32BF653E1002BAE38 /* ViewTitleTextStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTitleTextStyle.swift; sourceTree = ""; }; + 64D2D74D2C25D380009BD010 /* Animals.Symbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animals.Symbols.swift; sourceTree = ""; }; 64D992712C06281B002ACBC6 /* SystemIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemIcons.swift; sourceTree = ""; }; 64FC4D682C09C78000D08B8B /* SelectPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPlayerView.swift; sourceTree = ""; }; 64FC4D6A2C09C7C900D08B8B /* SelectPlayerButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPlayerButtonView.swift; sourceTree = ""; }; @@ -174,7 +178,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EC62C5332C1C188F0048CD0B /* DSQ.xcframework in Frameworks */, + 6437FF142C25870C009D0EAF /* DSQ.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -322,6 +326,7 @@ children = ( 646FA83D2C072340001466BA /* GameVM.swift */, 646FA8422C0730D6001466BA /* HistoricVM.swift */, + 6437FF122C25846F009D0EAF /* PlayingGameVM.swift */, ); path = Game; sourceTree = ""; @@ -455,6 +460,7 @@ children = ( EC62C53E2C1C6D1A0048CD0B /* CDPlayerExtension.swift */, ECF3FD302C2722C600F5E62B /* CDHistoriqueExtension.swift */, + 64D2D74D2C25D380009BD010 /* Animals.Symbols.swift */, ); path = Extensions; sourceTree = ""; @@ -672,6 +678,7 @@ 649B59B22BF65392002BAE38 /* TextStyles.swift in Sources */, EC99D5C12C089F6B00731B62 /* GameResumeFrame.swift in Sources */, EC05BFCB2C05F470000F7B19 /* GameScene.swift in Sources */, + 64D2D74E2C25D380009BD010 /* Animals.Symbols.swift in Sources */, 6458345C2BF5F92300E18321 /* DouShouQi_AppApp.swift in Sources */, ECB636512C046379007CD5E2 /* MusicPlayer.swift in Sources */, ECB6364E2C04628E007CD5E2 /* SpriteMeeple.swift in Sources */, @@ -709,6 +716,7 @@ 646F04BE2C0F54C0003C8600 /* SettingsVM.swift in Sources */, EC62C52D2C197ED10048CD0B /* Player.swift in Sources */, EC62C5292C1974000048CD0B /* PlayersVM.swift in Sources */, + 6437FF132C25846F009D0EAF /* PlayingGameVM.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DouShouQi_App/DouShouQi_App/AppModelAndExtension/Extensions/Animals.Symbols.swift b/DouShouQi_App/DouShouQi_App/AppModelAndExtension/Extensions/Animals.Symbols.swift new file mode 100644 index 0000000..4ddbdb3 --- /dev/null +++ b/DouShouQi_App/DouShouQi_App/AppModelAndExtension/Extensions/Animals.Symbols.swift @@ -0,0 +1,34 @@ +// +// Animals.Symbols.swift +// DouShouQi_App +// +// Created by Rémi REGNAULT on 21/06/2024. +// + +import Foundation +import DouShouQiModel + +extension Animal { + public var symbol: String { + switch self{ + case .rat: + return "🐭" + case .cat: + return "🐱" + case .dog: + return "🐶" + case .wolf: + return "🐺" + case .leopard: + return "🐆" + case .tiger: + return "🐯" + case .lion: + return "🦁" + case .elephant: + return "🐘" + @unknown default: + return "🐥" + } + } +} diff --git a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.swift b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.swift index 33e70ff..3b7bb34 100644 --- a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.swift +++ b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.swift @@ -13,4 +13,6 @@ public struct Colors { static let Button = Color("ButtonColor") static let MeepleP1 = UIColor(named: "MeepleP1") static let TextButton = Color("ButtonTextColor") + static let MeepleP2 = UIColor(named: "MeepleP2") + static let WinnerBackground = Color("WinnerBackground") } diff --git a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP1.colorset/Contents.json b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP1.colorset/Contents.json index d1b0235..1a21e5e 100644 --- a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP1.colorset/Contents.json +++ b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP1.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" + "blue" : "0.765", + "green" : "0.855", + "red" : "0.000" } }, "idiom" : "universal" diff --git a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP2.colorset/Contents.json b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP2.colorset/Contents.json new file mode 100644 index 0000000..8159a8e --- /dev/null +++ b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/MeepleP2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.150", + "red" : "0.150" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.150", + "red" : "0.150" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/WinnerBackground.colorset/Contents.json b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/WinnerBackground.colorset/Contents.json new file mode 100644 index 0000000..927f6cf --- /dev/null +++ b/DouShouQi_App/DouShouQi_App/Assets/Colors/Colors.xcassets/WinnerBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DouShouQi_App/DouShouQi_App/Components/Game/TopGameBoard.swift b/DouShouQi_App/DouShouQi_App/Components/Game/TopGameBoard.swift index 284b3cf..b510902 100644 --- a/DouShouQi_App/DouShouQi_App/Components/Game/TopGameBoard.swift +++ b/DouShouQi_App/DouShouQi_App/Components/Game/TopGameBoard.swift @@ -6,20 +6,23 @@ // import SwiftUI +import DouShouQiModel struct TopGameBoard: View { + @ObservedObject var gameVM: PlayingGameVM + var body: some View { VStack { HStack { Circle() .fill(Color.white) .frame(width: 40, height: 40) - Text("Player 1 Turn") + Text("\(gameVM.currentPlayerName)'s Turn") .font(.title) .foregroundColor(.white) } .padding() - .background(Color.red) + .background(Color(gameVM.currentPlayerColor)) .cornerRadius(10) Spacer().frame(height: 20) @@ -37,14 +40,14 @@ struct TopGameBoard: View { Spacer() HStack { - Text("Last moove :") + Text("Last move :") .font(.title2) .foregroundColor(.black) /*Image(systemName: "cheetah") .resizable() .frame(width: 30, height: 30)*/ - Text("🦁") - Text("2.3") + Text(gameVM.lastPieceMoved) + Text(gameVM.lastMove) .font(.title2) .foregroundColor(.black) } @@ -60,8 +63,8 @@ struct TopGameBoard: View { } } -struct TopGameBoard_Previews: PreviewProvider { - static var previews: some View { - TopGameBoard() - } -} +//struct TopGameBoard_Previews: PreviewProvider { +// static var previews: some View { +// TopGameBoard(gameVM: vm) +// } +//} diff --git a/DouShouQi_App/DouShouQi_App/Components/Scene/GameScene.swift b/DouShouQi_App/DouShouQi_App/Components/Scene/GameScene.swift index 6f955a2..9d24af2 100644 --- a/DouShouQi_App/DouShouQi_App/Components/Scene/GameScene.swift +++ b/DouShouQi_App/DouShouQi_App/Components/Scene/GameScene.swift @@ -6,43 +6,43 @@ // import Foundation +import SwiftUI import SpriteKit import DouShouQiModel class GameScene : SKScene { - var player1: HumanPlayer = HumanPlayer(withName: "Rémi", andId: .player1)! - var player2: HumanPlayer = HumanPlayer(withName: "Rayhan", andId: .player2)! + var player1: DouShouQiModel.Player? + var player2: DouShouQiModel.Player? - var game: Game + var game: Game? let pieces: [Owner : [Animal:SpriteMeeple]] = [ .player1: [ - .rat: SpriteMeeple(imageNamed: AppImages.Rat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .cat: SpriteMeeple(imageNamed: AppImages.Cat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .dog: SpriteMeeple(imageNamed: AppImages.Dog, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .wolf: SpriteMeeple(imageNamed: AppImages.Wolf, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .leopard: SpriteMeeple(imageNamed: AppImages.Leopard, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .lion: SpriteMeeple(imageNamed: AppImages.Lion, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .tiger: SpriteMeeple(imageNamed: AppImages.Tigger, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), - .elephant: SpriteMeeple(imageNamed: AppImages.Elephant, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!), + .rat: SpriteMeeple(imageNamed: AppImages.Rat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .cat: SpriteMeeple(imageNamed: AppImages.Cat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .dog: SpriteMeeple(imageNamed: AppImages.Dog, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .wolf: SpriteMeeple(imageNamed: AppImages.Wolf, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .leopard: SpriteMeeple(imageNamed: AppImages.Leopard, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .lion: SpriteMeeple(imageNamed: AppImages.Lion, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .tiger: SpriteMeeple(imageNamed: AppImages.Tigger, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), + .elephant: SpriteMeeple(imageNamed: AppImages.Elephant, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP1!, imageRotation: 180), ], .player2: [ - .rat: SpriteMeeple(imageNamed: AppImages.Rat, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .cat: SpriteMeeple(imageNamed: AppImages.Cat, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .dog: SpriteMeeple(imageNamed: AppImages.Dog, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .wolf: SpriteMeeple(imageNamed: AppImages.Wolf, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .leopard: SpriteMeeple(imageNamed: AppImages.Leopard, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .lion: SpriteMeeple(imageNamed: AppImages.Lion, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .tiger: SpriteMeeple(imageNamed: AppImages.Tigger, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), - .elephant: SpriteMeeple(imageNamed: AppImages.Elephant, size: CGSize(width: 100, height: 100), backgroundColor: .blue, imageRotation: 180), + .rat: SpriteMeeple(imageNamed: AppImages.Rat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .cat: SpriteMeeple(imageNamed: AppImages.Cat, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .dog: SpriteMeeple(imageNamed: AppImages.Dog, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .wolf: SpriteMeeple(imageNamed: AppImages.Wolf, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .leopard: SpriteMeeple(imageNamed: AppImages.Leopard, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .lion: SpriteMeeple(imageNamed: AppImages.Lion, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .tiger: SpriteMeeple(imageNamed: AppImages.Tigger, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), + .elephant: SpriteMeeple(imageNamed: AppImages.Elephant, size: CGSize(width: 100, height: 100), backgroundColor: Colors.MeepleP2!), ], ] let imageBoard: SKSpriteNode = SKSpriteNode(imageNamed: AppImages.boardGame) override init(size: CGSize) { - game = try! Game(withRules: ClassicRules(), andPlayer1: player1, andPlayer2: player2) super.init(size: size) imageBoard.size = size //self.scaleMode = .aspectFit @@ -50,48 +50,41 @@ class GameScene : SKScene { self.backgroundColor = .white self.addChild(imageBoard) + } + + convenience init(size: CGSize, gameVM: PlayingGameVM) { + self.init(size: size) + self.game = gameVM.game + self.player1 = self.game?.players[.player1] + self.player2 = self.game?.players[.player2] for piece in pieces.flatMap({owner, pieces in pieces.values}) { self.addChild(piece) - //piece.setOnMove(onMove: onMeepleMove) + piece.setOnMove(onMove: gameVM.onMeepleMove) } - initializeBoard(game.board) + initializeBoard(game!.board) + + gameVM.game.addPieceRemovedListener { _, _, piece in + self.pieces[piece.owner]![piece.animal]?.removeFromParent() + } + gameVM.start() } func initializeBoard(_ board: Board) { - for (lineIndex, currentLine) in game.board.grid.enumerated() { + for (lineIndex, currentLine) in game!.board.grid.enumerated() { for (cellIndex, currentCell) in currentLine.enumerated() { if let piece = currentCell.piece { - pieces[piece.owner]?[piece.animal]?.cellPosition = CGPoint(x: cellIndex-3, y: lineIndex-4) + pieces[piece.owner]?[piece.animal]?.cellPosition = CGPoint(x: cellIndex, y: lineIndex) pieces[piece.owner]?[piece.animal]?.cellPosition = CGPoint(x: 0, y: 0) - print("line :", lineIndex, " cologne: ", cellIndex) } } } } required init?(coder aDecoder: NSCoder) { - game = try! Game(withRules: ClassicRules(), andPlayer1: player1, andPlayer2: player2) + game = try! Game(withRules: ClassicRules(), andPlayer1: DouShouQiModel.Player(withName: "P1", andId: .player1)!, andPlayer2: DouShouQiModel.Player(withName: "P2", andId: .player2)!) super.init(coder: aDecoder); } - - func onMeepleMove(_ start: CGPoint, _ end: CGPoint) async { - - let owner = game.rules.getNextPlayer() - let player = game.players[owner] - - let move = Move(of: owner, fromRow: Int(start.x), andFromColumn: Int(start.y), toRow: Int(end.x), andToColumn: Int(end.y)) - - print("Meeple moved") - - try! await (player as! HumanPlayer).chooseMove(move) - - - } - - public func start() async throws { - try await game.start() - } } diff --git a/DouShouQi_App/DouShouQi_App/Components/Scene/SpriteMeeple.swift b/DouShouQi_App/DouShouQi_App/Components/Scene/SpriteMeeple.swift index 0a74775..359bc48 100644 --- a/DouShouQi_App/DouShouQi_App/Components/Scene/SpriteMeeple.swift +++ b/DouShouQi_App/DouShouQi_App/Components/Scene/SpriteMeeple.swift @@ -10,8 +10,8 @@ import SpriteKit class SpriteMeeple : SKNode { - static let offset = CGPoint(x: 0, y: 0) - static let direction = CGVector(dx: 100, dy: 100) + static let offset = CGPoint(x: -300, y: 400) + static let direction = CGVector(dx: 100, dy: -100) let imageNode : SKSpriteNode var ellipseNode : SKShapeNode @@ -19,15 +19,21 @@ class SpriteMeeple : SKNode { var originalSize: CGSize var originalEllipseSize: CGSize - var onMove: ((CGPoint, CGPoint) async -> ())? + var onMove: ((SpriteMeeple, CGPoint, CGPoint) -> ())? var cellPosition: CGPoint{ didSet(cellPosition){ self.position.x = SpriteMeeple.offset.x + SpriteMeeple.direction.dx*cellPosition.x - self.position.y = SpriteMeeple.offset.y + SpriteMeeple.direction.dx*cellPosition.y + self.position.y = SpriteMeeple.offset.y + SpriteMeeple.direction.dy*cellPosition.y } } + private var oldCellPosition: CGPoint? + + func getCellPosition() -> CGPoint { + return CGPoint(x: Int((self.position.x - SpriteMeeple.offset.x)/SpriteMeeple.direction.dx), y: Int((self.position.y - SpriteMeeple.offset.y)/SpriteMeeple.direction.dy)) + } + init(imageNamed imageName: String, size: CGSize, backgroundColor: UIColor, imageRotation : Double = 0){ imageNode = SKSpriteNode(imageNamed: imageName) originalSize = CGSize(width: 80, height: 65) @@ -59,7 +65,7 @@ class SpriteMeeple : SKNode { super.init(coder: aDecoder) } - public func setOnMove(onMove: @escaping (CGPoint, CGPoint) -> ()) { self.onMove = onMove } + public func setOnMove(onMove: @escaping (SpriteMeeple, CGPoint, CGPoint) -> ()) { self.onMove = onMove } override var isUserInteractionEnabled: Bool{ set { } @@ -67,6 +73,8 @@ class SpriteMeeple : SKNode { } override func touchesBegan(_ touches: Set, with event: UIEvent?){ + self.oldCellPosition = getCellPosition() + imageNode.size = CGSize(width: imageNode.size.width * 1.1, height: imageNode.size.height * 1.1) ellipseNode.path = SKShapeNode(ellipseOf: CGSize(width: originalEllipseSize.width*1.1, height: originalEllipseSize.height*1.1)).path self.zPosition = 1 @@ -88,13 +96,12 @@ class SpriteMeeple : SKNode { imageNode.size = originalSize ellipseNode.path = SKShapeNode(ellipseOf: originalEllipseSize).path - if let onMove: (CGPoint, CGPoint) async -> () = onMove { - //onMove(CGPoint(x: 100, y: 100), CGPoint(x: 200, y: 100)) + if let onMove: (SpriteMeeple, CGPoint, CGPoint) -> () = onMove { + onMove(self, self.oldCellPosition!, self.getCellPosition()) } self.zPosition = 0 } - override func touchesMoved(_ touches: Set, with event: UIEvent?){ self.position = touches.first?.location(in: parent!) ?? CGPoint(x: 0, y: 0) diff --git a/DouShouQi_App/DouShouQi_App/ViewModel/Game/PlayingGameVM.swift b/DouShouQi_App/DouShouQi_App/ViewModel/Game/PlayingGameVM.swift new file mode 100644 index 0000000..104303a --- /dev/null +++ b/DouShouQi_App/DouShouQi_App/ViewModel/Game/PlayingGameVM.swift @@ -0,0 +1,136 @@ +// +// PlayingGameVM.swift +// DouShouQi_App +// +// Created by Rémi REGNAULT on 21/06/2024. +// + +import Foundation +import SwiftUI +import DouShouQiModel + +class PlayingGameVM: ObservableObject { + + // Properties + @Published var game: Game + + // Computed properties + public var currentPlayerName: String { game.players[game.rules.getNextPlayer()]?.name ?? (game.rules.getNextPlayer() == .player1 ? "P1" : "P2") } + + public var lastMove: String { + if let move: Move = game.rules.historic.last { + return "\(move.columnDestination).\(move.rowDestination)" + } + else { + return "" + } + } + + public var lastPieceMoved: String { + if let move: Move = game.rules.historic.last { + return game.board.grid[move.rowDestination][move.columnDestination].piece?.animal.symbol ?? "" + } else { + return "" + } + } + + public var currentPlayerColor: UIColor { + return game.rules.getNextPlayer() == .player1 ? Colors.MeepleP1! : Colors.MeepleP2! + } + + public var winner: String { + guard self.game.isOver else { + return "" + } + + guard let lastMove: Move = self.game.rules.historic.last else { + return "" + } + + guard let winner: DouShouQiModel.Player = game.players[lastMove.owner] else { + return "" + } + return winner.name + } + + // Inits + init(withGame game: Game) { + self.game = game + } + + init?(withRules rules: Rules, andPlayer1 p1: DouShouQiModel.Player, andPlayer2 p2: DouShouQiModel.Player) { + do { + self.game = try Game(withRules: rules, andPlayer1: p1, andPlayer2: p2) + + self.game.addInvalidMoveCallbacksListener { _, move, player, result in + if result { + return + } + print("**************************************") + print("⚠️⚠️⚠️⚠️ Invalid Move detected: \(move) by \(player.name) (\(player.id))") + print("**************************************") + + } + + self.game.addGameOverListener { board, result, player in + switch(result){ + case .notFinished: + print("⏳ Game is not over yet!") + case .winner(winner: let o, reason: let r): + print(board) + print("**************************************") + print("Game Over!!!") + print("🥇🏆 and the winner is... \(o == .player1 ? "🟡" : "🔴") \(player?.name ?? "")!") + switch(r){ + case .denReached: + print("🪺 the opponent's den has been reached.") + case .noMorePieces: + print("🐭🐱🐯🦁🐘 all the opponent's animals have been eaten...") + case .noMovesLeft: + print("⛔️ the opponent can not move any piece!") + case .tooManyOccurences: + print("🔄 the opponent seem to like this situation... but enough is enough. Sorry...") + default: + print("Reason unknown...") + } + print("**************************************") + default: + break + } + + } + + } catch { + print("Error") + return nil + } + } + + // Customs funcs + func onMeepleMove(_ sender: SpriteMeeple, _ start: CGPoint, _ end: CGPoint) { + let owner = game.rules.getNextPlayer() + let player: DouShouQiModel.Player = game.players[owner]! + + let move = Move(of: owner, fromRow: Int(start.y), andFromColumn: Int(start.x), toRow: Int(end.y), andToColumn: Int(end.x)) + + if (game.rules.isMoveValid(onBoard: game.board, withMove: move)) { + print("Meeple moved = ", start, " -> ", end) + + Task { + try! await (player as! HumanPlayer).chooseMove(move) + } + self.objectWillChange.send() + } else { + sender.cellPosition = start + sender.cellPosition = start + } + + } + + public func start() { + Task { + try await self.game.start() + } + } + +} diff --git a/DouShouQi_App/DouShouQi_App/Views/Game/GameView.swift b/DouShouQi_App/DouShouQi_App/Views/Game/GameView.swift index 23f94be..fe14168 100644 --- a/DouShouQi_App/DouShouQi_App/Views/Game/GameView.swift +++ b/DouShouQi_App/DouShouQi_App/Views/Game/GameView.swift @@ -7,21 +7,53 @@ import SwiftUI import SpriteKit +import DouShouQiModel struct GameView: View { var gameScene: GameScene = GameScene(size: CGSize(width: 700, height: 900)) + + @StateObject var gameVM: PlayingGameVM + + @State private var showHome = false + var body: some View { - VStack { - TopGameBoard().frame(maxHeight: 200) - SpriteView(scene: gameScene) - } - .onAppear { - playSound(named: "Fight") - - //MusicPlayer.shared.playBackgroundMusic(music: "GameMusic") + ZStack { + VStack { + TopGameBoard(gameVM: gameVM).frame(maxHeight: 200) + SpriteView(scene: GameScene(size: CGSize(width: 700, height: 900), gameVM: gameVM)) + } + .onAppear { + playSound(named: "Fight") + + //MusicPlayer.shared.playBackgroundMusic(music: "GameMusic") + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { + MusicPlayer.shared.playBackgroundMusic(music: "GameMusic") + } + } + .onDisappear { + MusicPlayer.shared.stopBackgroundMusic() + } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { - MusicPlayer.shared.playBackgroundMusic(music: "GameMusic") + if (gameVM.game.isOver) { + HStack { + Spacer() + + VStack(alignment: .center) { + Text("Game Over") + .font(.title2) + Text("Winner: \(gameVM.winner)") + } + .background(Colors.WinnerBackground) + + Spacer() + } + .onAppear { + Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { _ in + + // Il faut retourner au home + } + } } } .onDisappear { @@ -31,8 +63,8 @@ struct GameView: View { } } -struct GameView_Previews: PreviewProvider { +/*struct GameView_Previews: PreviewProvider { static var previews: some View { GameView() } -} +}*/ diff --git a/DouShouQi_App/DouShouQi_App/Views/Player/SelectPlayerView.swift b/DouShouQi_App/DouShouQi_App/Views/Player/SelectPlayerView.swift index 1a87600..72da9a0 100644 --- a/DouShouQi_App/DouShouQi_App/Views/Player/SelectPlayerView.swift +++ b/DouShouQi_App/DouShouQi_App/Views/Player/SelectPlayerView.swift @@ -7,6 +7,7 @@ import SwiftUI import SpriteKit +import DouShouQiModel struct SelectPlayerView: View { @@ -26,8 +27,9 @@ struct SelectPlayerView: View { HStack { VStack(alignment: .trailing) { - MainMenuButton(text: "Start", destination: GameView(), sound: "Start", horizontalAlignment: .trailing, topLeftCorner: 10, bottomLeftCorner: 10) + MainMenuButton(text: "Start", destination: GameView(gameVM: PlayingGameVM(withRules: ClassicRules(), andPlayer1: HumanPlayer(withName: player1.player.name, andId: .player1)!, andPlayer2: HumanPlayer(withName: player2.player.name, andId: .player2)!)!), sound: "Start", horizontalAlignment: .trailing, topLeftCorner: 10, bottomLeftCorner: 10) MainMenuButton(text: "Settings", destination: ContentView(), sound: "TitleScreenButtonSound", horizontalAlignment: .trailing, topLeftCorner: 10, bottomLeftCorner: 10) + } }