Themed piece colors

main
Mathieu GROUSSEAU 1 week ago
parent 13a9189e8b
commit 7ae16d7cc1

@ -23,6 +23,7 @@
F05DA2112E002AA00094A4A8 /* BoardNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05DA2102E002AA00094A4A8 /* BoardNode.swift */; };
F05DA2132E01BA270094A4A8 /* PlayerVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05DA2122E01BA270094A4A8 /* PlayerVM.swift */; };
F05DA2152E02D1850094A4A8 /* UIUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05DA2142E02D1850094A4A8 /* UIUtilities.swift */; };
F05DA2172E082F5B0094A4A8 /* Owner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05DA2162E082F5B0094A4A8 /* Owner.swift */; };
F0F59E492DD4958800BE32D6 /* C4.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E432DD492B400BE32D6 /* C4.xcframework */; };
F0F59E4A2DD4958800BE32D6 /* C4Persistance.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */; };
F0F59E4B2DD4958800BE32D6 /* C4Players.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E422DD492B400BE32D6 /* C4Players.xcframework */; };
@ -74,6 +75,7 @@
F05DA2102E002AA00094A4A8 /* BoardNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardNode.swift; sourceTree = "<group>"; };
F05DA2122E01BA270094A4A8 /* PlayerVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerVM.swift; sourceTree = "<group>"; };
F05DA2142E02D1850094A4A8 /* UIUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIUtilities.swift; sourceTree = "<group>"; };
F05DA2162E082F5B0094A4A8 /* Owner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Owner.swift; sourceTree = "<group>"; };
F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Persistance.xcframework; path = ../precompiled/xcframeworks/C4Persistance.xcframework; sourceTree = "<group>"; };
F0F59E422DD492B400BE32D6 /* C4Players.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Players.xcframework; path = ../precompiled/xcframeworks/C4Players.xcframework; sourceTree = "<group>"; };
F0F59E432DD492B400BE32D6 /* C4.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4.xcframework; path = ../precompiled/xcframeworks/C4.xcframework; sourceTree = "<group>"; };
@ -215,6 +217,7 @@
children = (
F0143C332DF987490086CAAA /* PlayerType.swift */,
F0143C352DFA9A000086CAAA /* RulesType.swift */,
F05DA2162E082F5B0094A4A8 /* Owner.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -380,6 +383,7 @@
F0F59E5B2DE6F68800BE32D6 /* ScoreboardView.swift in Sources */,
F0143C2B2DF018F20086CAAA /* GameScene.swift in Sources */,
F0F59E572DE6D6E600BE32D6 /* SavedGamesView.swift in Sources */,
F05DA2172E082F5B0094A4A8 /* Owner.swift in Sources */,
F0143C342DF987490086CAAA /* PlayerType.swift in Sources */,
F0F59E552DDDED1D00BE32D6 /* IngameView.swift in Sources */,
);

@ -285,6 +285,38 @@
}
}
},
"inGame.ppButton.pause" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Pause"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Pause"
}
}
}
},
"inGame.ppButton.resume" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Resume"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Reprendre"
}
}
}
},
"mainMenu.button.newGame" : {
"extractionState" : "manual",
"localizations" : {
@ -470,6 +502,7 @@
}
},
"newGame.timeLimit" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {

@ -39,6 +39,13 @@ public class GameScene: SKScene {
self.draggablePiece.piece = .init(withOwner: $0)
})
self.cancellables.append(vm.$currentBoard.sink(receiveValue: self.updateBoard))
self.cancellables.append(vm.$paused.sink { paused in
self.draggablePiece.onDragHandler = if paused {
nil
} else {
self.onPieceDragEnd
}
})
vm.game.addGameStartedListener { _ in
DispatchQueue.main.async {
@ -77,7 +84,6 @@ public class GameScene: SKScene {
) }
private func onPlayerTurn(player: Player) {
print("player \(player)")
self.draggablePiece.onDragHandler = if player is HumanPlayer {
self.onPieceDragEnd
} else {

@ -13,19 +13,8 @@ class PieceNode : SKNode {
var piece: Piece = .init(withOwner: .noOne) {
didSet {
switch (piece.owner) {
case .player1:
circle.fillColor = .yellow
circle.strokeColor = .black
case .player2:
circle.fillColor = .red
circle.strokeColor = .black
default:
// NOTE: should be unreachable
circle.fillColor = .lightGray
circle.strokeColor = .clear
}
circle.fillColor = UIColor(piece.owner.pieceColor)
circle.strokeColor = UIColor(.pieceBorder)
}
}
var boardPosition: (x: Int, y: Int)? = nil {
@ -49,7 +38,7 @@ class PieceNode : SKNode {
override init() {
circle = SKShapeNode(circleOfRadius: Self.pieceSize / 2 * Self.borderFactor)
super.init()
addChild(circle)

@ -0,0 +1,19 @@
//
// Owner.swift
// App
//
// Created by etudiant2 on 22/06/2025.
//
import SwiftUI
import Connect4Core
extension Owner {
var pieceColor: Color {
switch (self) {
case .player1: .piece1
case .player2: .piece2
default: .gray
}
}
}

@ -42,11 +42,28 @@ struct IngameView: View {
Spacer()
// Button("", systemImage: "globe") {
//
// let style: (key: LocalizedStringKey, img: String) = if vm.paused {
// ("inGame.ppButton.resume", "play.circle")
// } else {
// ("inGame.ppButton.pause", "pause.circle")
// }
// Button(style.key, systemImage: style.img) {
// vm.paused.toggle()
// }
// .`if`(vm.paused, { $0.buttonStyle(.bordered) })
// .`if`(!vm.paused, { $0.buttonStyle(.borderedProminent) })
// //For some reason the following crashes the compiler:
// // .either(vm.paused,
// // `true`: { $0.buttonStyle(.borderedProminent) },
// // `false`: { $0.buttonStyle(.bordered) }
// // )
// .tint(.accentColor)
//
// Spacer()
Text("inGame.currentRules \(vm.rulesName)")
}.onAppear {
vm.start()
}
}
@ -56,10 +73,6 @@ struct IngameView: View {
self._vm = StateObject(wrappedValue: vm)
self.scene = GameScene(viewModel: vm)
vm.start()
// TODO actual game initialization
}
}
@ -116,8 +129,9 @@ private struct PlayerView: View {
}
}
}.padding(.all, 5)
// .background(color, ignoresSafeAreaEdges: Edge.Set())
// .containerShape(RoundedRectangle(cornerRadius: 5))
.border(who.id.pieceColor)
.containerShape(RoundedRectangle(cornerRadius: 5))
.padding(.all, 5)
}
init(who owner: PlayerVM, dock_left dockLeft: Bool, isTurn: Binding<Bool>) {

@ -19,13 +19,8 @@ struct NewGameView: View {
var body: some View {
VStack {
Form {
Section(header: Label("generic.player1.name", systemImage: "person")) {
PlayerSectionView(settings: p1)
}
Section(header: Label("generic.player2.name", systemImage: "person")) {
PlayerSectionView(settings: p2)
}
PlayerSectionView("generic.player1.name", color: .piece1, settings: p1)
PlayerSectionView("generic.player2.name", color: .piece2, settings: p2)
HStack {
Picker("newGame.rules.title", systemImage: "slider.horizontal.3", selection: $vm.rulesType) {
@ -48,14 +43,13 @@ struct NewGameView: View {
Stepper("newGame.dimensions.alignedTokens \(vm.alignedTokens)", value: $vm.alignedTokens)
}
Section(header: Label("newGame.timeLimit", systemImage: "stopwatch")) {
}
// Section(header: Label("newGame.timeLimit", systemImage: "stopwatch")) {
//
// }
}
}.toolbar {
NavigationLink {
NavigationLazyView(IngameView(settings: vm, player1: p1, player2: p2))
// .navigationTitle(Text("Titre" as String))
} label: {
Label("newGame.play", systemImage: "play")
}
@ -75,35 +69,46 @@ struct NavigationLazyView<Content: View>: View {
}
private struct PlayerSectionView: View {
private let sectionLabel: LocalizedStringKey
private let pieceColor: Color
@ObservedObject
private var settings: PlayerSettingsVM
@State private var photo: PhotosPickerItem? = nil
var body: some View {
Picker("newGame.player.type", selection: $settings.type) {
ForEach(PlayerType.allCases) {
Text(LocalizedStringKey($0.baseTranslationKey)).tag($0)
Section(header: Label {
Text(sectionLabel)
} icon: {
Circle().fill(self.pieceColor)
}.fixedSize(horizontal: true, vertical: false)) {
Picker("newGame.player.type", selection: $settings.type) {
ForEach(PlayerType.allCases) {
Text(LocalizedStringKey($0.baseTranslationKey)).tag($0)
}
}
if (settings.type == .Human) {
TextField("newGame.player.name", text: $settings.name)
// TODO: MacOS
//.textInputSuggestions(isEnabled: true) {
//
//}
}
// // TODO: actual photo support
// Image(systemName: "camera.viewfinder").overlay {
// PhotosPicker(selection: $photo) {
// Text("newGame.player.photo.picker")
// }
// }
}
if (settings.type == .Human) {
TextField("newGame.player.name", text: $settings.name)
// TODO: MacOS
//.textInputSuggestions(isEnabled: true) {
//
//}
}
// // TODO: actual photo support
// Image(systemName: "camera.viewfinder").overlay {
// PhotosPicker(selection: $photo) {
// Text("newGame.player.photo.picker")
// }
// }
}
init(settings: PlayerSettingsVM) {
init(_ sectionLabel: LocalizedStringKey, color pieceColor: Color, settings: PlayerSettingsVM) {
self.sectionLabel = sectionLabel
self.pieceColor = pieceColor
self.settings = settings
}
}

@ -9,6 +9,14 @@ extension View {
self
}
}
@ViewBuilder func either<Content: View>(_ condition: Bool, `true`: (Self) -> Content, `false`: (Self) -> Content) -> some View {
if condition {
`true`(self)
} else {
`false`(self)
}
}
}
extension Binding {

@ -2,8 +2,11 @@ import Foundation
import Connect4Core
import Connect4Players
import Connect4Rules
import Connect4Persistance
class IngameVM: ObservableObject {
let gameName: String
let game: Game
// @Published
@ -25,9 +28,26 @@ class IngameVM: ObservableObject {
@Published
var currentBoard: Board
@Published
var paused: Bool = false {
didSet {
Task.detached(priority: .userInitiated) {
if (self.paused) {
// TODO
} else {
// TODO
}
}
}
}
private var running: Bool = false
init?(settings: NewGameVM, player1: PlayerSettingsVM, player2: PlayerSettingsVM) {
let fmt = DateFormatter()
fmt.locale = Locale(identifier: "EN") // No "root" locale?
self.gameName = fmt.string(from: Date.now)
let rulesInit: (Int, Int, Int) -> (any Rules)? = switch (settings.rulesType) {
case .Classic: Connect4Rules.init
case .TicTacToe: TicTacToeRules.init

@ -7,6 +7,7 @@ class PlayerVM: ObservableObject {
var name: String { inner.name }
var type: PlayerType
var id: Owner { inner.id }
init(inner: Player) {
self.inner = inner

@ -0,0 +1,56 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.313",
"green" : "0.943",
"red" : "0.942"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.274",
"green" : "0.825",
"red" : "0.825"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.125",
"green" : "0.178",
"red" : "0.811"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.859",
"green" : "0.859",
"red" : "0.851"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading…
Cancel
Save