Skip to content

Commit

Permalink
Weapon Cards (#92)
Browse files Browse the repository at this point in the history
* DefaultEndTurnOnEliminated

* Simulate 3 players

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
  • Loading branch information
stephtelolahy authored Dec 20, 2024
1 parent 4feaf53 commit 697b506
Show file tree
Hide file tree
Showing 73 changed files with 1,155 additions and 1,586 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ jobs:
platform: ${{ 'iOS Simulator' }}
run: |
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
# device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
device='iPhone 16'
if [ $scheme = default ]; then scheme=$(cat default); fi
if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
Expand All @@ -48,7 +49,8 @@ jobs:
platform: ${{ 'iOS Simulator' }}
run: |
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
# device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
device='iPhone 16'
if [ $scheme = default ]; then scheme=$(cat default); fi
if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
Expand Down
113 changes: 22 additions & 91 deletions WildWestOnline/Core/Bang/Sources/Game/Card.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,39 @@
/// not currently in the game and see how they play.
/// A `card` is just a collection of effects and attributes
/// ℹ️ Inspired by https://github.com/danielyule/hearthbreaker/wiki/Tag-Format
/// ℹ️ Before dispatching resolved action, verify initial event is still confirmed as state
/// ℹ️ All effects of the same source share the resolved arguments
/// ℹ️ All effects of the same source share the resolved arguments
///
public struct Card: Equatable, Codable, Sendable {
public let name: String
public let desc: String
public let canPlay: [StateReq]
public let onPlay: [Effect]
public let canTrigger: [EventReq]
public let shouldTrigger: [EventReq]
public let onTrigger: [Effect]
public let passive: [Effect]
public let onActive: [Effect]
public let onDeactive: [Effect]
public let counterShot: Bool

public init(
name: String,
desc: String = "",
canPlay: [StateReq] = [],
onPlay: [Effect] = [],
canTrigger: [EventReq] = [],
shouldTrigger: [EventReq] = [],
onTrigger: [Effect] = [],
passive: [Effect] = [],
onActive: [Effect] = [],
onDeactive: [Effect] = [],
counterShot: Bool = false
) {
self.name = name
self.desc = desc
self.canPlay = canPlay
self.onPlay = onPlay
self.counterShot = counterShot
self.canTrigger = canTrigger
self.shouldTrigger = shouldTrigger
self.onTrigger = onTrigger
self.passive = passive
self.onActive = onActive
self.onDeactive = onDeactive
self.counterShot = counterShot
}

/// Occurred action when card is played
Expand All @@ -60,30 +62,32 @@ public struct Card: Equatable, Codable, Sendable {
/// Required state conditions to play a card
public enum StateReq: Equatable, Codable, Sendable {
case playersAtLeast(Int)
case playedThisTurnAtMost([String: Int])
case playLimitPerTurn([String: Int])
case healthZero
case gameOver
case currentTurn
}

/// Required event conditions to trigger a card
public struct EventReq: Equatable, Codable, Sendable {
public let actionKind: GameAction.Kind
public let stateConditions: [StateReq]
public let stateReqs: [StateReq]

public init(
actionKind: GameAction.Kind,
stateConditions: [StateReq] = []
stateReqs: [StateReq] = []
) {
self.actionKind = actionKind
self.stateConditions = stateConditions
self.stateReqs = stateReqs
}
}

/// Selectors are used to specify which objects an aura or effect should affect.
/// Selectors are used to specify which objects an effect should affect.
/// Choice is performed by {actor}
public enum Selector: Equatable, Codable, Sendable {
case `repeat`(Number)
case setAmount(Int)
case setAmountPerCard([String: Int])
case setTarget(TargetGroup)
case setCard(CardGroup)
case chooseOne(ChooseOneElement, resolved: ChooseOneResolved? = nil, selection: String? = nil)
Expand All @@ -106,7 +110,10 @@ public struct Card: Equatable, Codable, Sendable {
}

public enum CardGroup: String, Codable, Sendable {
case all
case allHand
case allInPlay
case played
case equipedWeapon
}

public enum ChooseOneElement: Equatable, Codable, Sendable {
Expand Down Expand Up @@ -154,79 +161,3 @@ public extension String {
/// Pass when asked a counter card
static let pass = "pass"
}

/*

/// Selectors are used to specify which objects an aura or effect should affect.
/// Choice is performed by {actor}
public enum Selector: Equatable, Codable {
/// must `discard` hand card
case chooseCostHandCard(CardCondition? = nil, count: Int = 1)

/// can `discard` hand card to counter the effect
case chooseEventuallyCounterHandCard(CardCondition? = nil, count: Int = 1)

/// can `discard` hand card to reverse effect
case chooseEventuallyReverseHandCard(CardCondition)

/// apply effect x times
case `repeat`(Number)

/// must match given condition
case verify(Card.StateReq)

public enum Target: String, Codable {
case actor // who is playing the card
case next // next player after actor
case offender // actor of previous attack
case eliminated // just eliminated player
case targeted // target of previous attack
}

public enum TargetCondition: Equatable, Codable {
case atDistance(Int)
case atDistanceReachable
case neighbourToTarget
case havingCard
case havingHandCard
case havingInPlayCard
}

public enum Card: Equatable, Codable {
case played
case all
case inPlayWithAttr(PlayerAttribute)
}

public enum CardCondition: Equatable, Codable {
case fromHand
case inPlay
case isBlue
case suits(String)
case named(String)
case action(ActionType)
case discovered
case discarded
}

public enum Number: Equatable, Codable {
case activePlayers
case excessHand
case wound
case damage
case value(Int)
}

public indirect enum Card.StateReq: Equatable, Codable {
case playersAtLeast(Int)
case limitPerTurn(Int)
case draw(String)
case actorTurn
case discardedCardsNotAce
case hasNoBlueCardsInPlay
case targetHealthIs1
case not(Self)
}
}
}
*/
110 changes: 93 additions & 17 deletions WildWestOnline/Core/Bang/Sources/Game/GameAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

public struct GameAction: Action, Equatable, Codable, Sendable {
public let kind: Kind
public var kind: Kind
public var payload: Payload

public enum Kind: String, Codable, Sendable {
Expand All @@ -16,23 +16,30 @@ public struct GameAction: Action, Equatable, Codable, Sendable {
case drawDiscard
case drawDiscovered
case discover
@available(*, deprecated, message: "use .discardHand or .discardInPlay instead")
case discard
case discardHand
case discardInPlay
case heal
case damage
case choose
case steal
case shoot
case endTurn
case startTurn
case queue
case eliminate
case endGame
case setMaxHealth
case activate
case setWeapon
case setMagnifying
case setRemoteness
case choose
case queue
case discardPlayed
case equip
case handicap
case setMaxHealth
case increaseMagnifying
case increaseRemoteness
case setHandLimit
case activate
case setPlayLimitPerTurn
}

public struct Payload: Equatable, Codable, Sendable {
Expand All @@ -45,6 +52,7 @@ public struct GameAction: Action, Equatable, Codable, Sendable {
public var selectors: [Card.Selector]
public var children: [GameAction]
public var cards: [String]
public var amountPerCard: [String: Int]

public init(
actor: String = "",
Expand All @@ -55,7 +63,8 @@ public struct GameAction: Action, Equatable, Codable, Sendable {
selection: String? = nil,
selectors: [Card.Selector] = [],
children: [GameAction] = [],
cards: [String] = []
cards: [String] = [],
amountPerCard: [String: Int] = [:]
) {
self.actor = actor
self.source = source
Expand All @@ -66,6 +75,7 @@ public struct GameAction: Action, Equatable, Codable, Sendable {
self.selectors = selectors
self.children = children
self.cards = cards
self.amountPerCard = amountPerCard
}
}

Expand Down Expand Up @@ -165,10 +175,21 @@ public extension GameAction {
)
}

/// Discard a player's hand or inPlay card
static func discard(_ card: String, player: String) -> Self {
/// Discard a player's hand card
static func discardHand(_ card: String, player: String) -> Self {
.init(
kind: .discardHand,
payload: .init(
target: player,
card: card
)
)
}

/// Discard a player's inPlay card
static func discardInPlay(_ card: String, player: String) -> Self {
.init(
kind: .discard,
kind: .discardInPlay,
payload: .init(
target: player,
card: card
Expand Down Expand Up @@ -246,16 +267,67 @@ public extension GameAction {
payload: .init(target: player, cards: cards)
)
}

/// Discard just played card
static func discardPlayed(_ card: String, player: String) -> Self {
.init(
kind: .discardPlayed,
payload: .init(
target: player,
card: card
)
)
}

/// Equip a card
static func equip(_ card: String, player: String) -> Self {
.init(
kind: .equip,
payload: .init(
target: player,
card: card
)
)
}

/// Handicap a target with a card
static func handicap(_ card: String, target: String, player: String) -> Self {
.init(
kind: .handicap,
payload: .init(
actor: player,
target: target,
card: card
)
)
}

/// Set Weapon
static func setWeapon(_ weapon: Int, player: String) -> Self {
.init(
kind: .setWeapon,
payload: .init(target: player, amount: weapon)
)
}

static func setPlayLimitPerTurn(_ limit: [String: Int], player: String) -> Self {
.init(
kind: .setPlayLimitPerTurn,
payload: .init(
target: player,
amountPerCard: limit
)
)
}
}

extension GameAction: CustomStringConvertible {
public var description: String {
var parts: [String] = []
if payload.selectors.isEmpty {
parts.append(kind.emoji)
} else {
if payload.selectors.isNotEmpty {
parts.append("..")
}
parts.append(kind.emoji)
parts.append(payload.target)

if let card = payload.card {
Expand Down Expand Up @@ -286,14 +358,15 @@ private extension GameAction.Kind {
}

static let dict: [GameAction.Kind: String] = [
.play: "🟑",
.play: "βšͺ️",
.heal: "❀️",
.damage: "πŸ₯΅",
.drawDeck: "πŸ’°",
.drawDiscard: "πŸ’°",
.drawDiscovered: "πŸ’°",
.steal: "‼️",
.discard: "❌",
.discardHand: "❌",
.discardInPlay: "❌",
.draw: "🎲",
.discover: "🎁",
.shoot: "πŸ”«",
Expand All @@ -303,6 +376,9 @@ private extension GameAction.Kind {
.endGame: "πŸŽ‰",
.choose: "🎯",
.activate: "🟒",
.queue: "βž•"
.discardPlayed: "🟠",
.equip: "πŸ”΅",
.queue: "βž•",
.setWeapon: "😎"
]
}
Loading

0 comments on commit 697b506

Please sign in to comment.