Skip to content

Commit

Permalink
feat(ui): handle new blind mechanisms
Browse files Browse the repository at this point in the history
  • Loading branch information
kleinfreund committed Mar 2, 2024
1 parent ecbab32 commit 8975679
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 71 deletions.
88 changes: 51 additions & 37 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,42 +59,50 @@ <h2>Round data</h2>
</div>

<div class="card">
<label class="r-blind text-control">
<span class="label truncate">Blind</span>

<select class="r-blind-input select" data-r-blind>
<option value="Small Blind" selected>Small Blind</option>
<option value="Big Blind">Big Blind</option>
<option value="The Hook">The Hook</option>
<option value="The Ox">The Ox</option>
<option value="The House">The House</option>
<option value="The Wall">The Wall</option>
<option value="The Wheel">The Wheel</option>
<option value="The Arm">The Arm</option>
<option value="The Club">The Club</option>
<option value="The Fish">The Fish</option>
<option value="The Psychic">The Psychic</option>
<option value="The Goad">The Goad</option>
<option value="The Water">The Water</option>
<option value="The Window">The Window</option>
<option value="The Manacle">The Manacle</option>
<option value="The Eye">The Eye</option>
<option value="The Mouth">The Mouth</option>
<option value="The Plant">The Plant</option>
<option value="The Serpent">The Serpent</option>
<option value="The Pillar">The Pillar</option>
<option value="The Needle">The Needle</option>
<option value="The Head">The Head</option>
<option value="The Tooth">The Tooth</option>
<option value="The Flint">The Flint</option>
<option value="The Mark">The Mark</option>
<option value="Amber Acorn">Amber Acorn</option>
<option value="Unknown One">Unknown One</option>
<option value="Violet Vessel">Violet Vessel</option>
<option value="Unknown Two">Unknown Two</option>
<option value="Cerulean Bell">Cerulean Bell</option>
</select>
</label>
<div class="stack">
<label class="r-blind-name text-control">
<span class="label truncate">Blind</span>

<select class="r-blind-name-input select" data-r-blind-name>
<option value="Small Blind" selected>Small Blind</option>
<option value="Big Blind">Big Blind</option>
<option value="The Hook">The Hook</option>
<option value="The Ox">The Ox</option>
<option value="The House">The House</option>
<option value="The Wall">The Wall</option>
<option value="The Wheel">The Wheel</option>
<option value="The Arm">The Arm</option>
<option value="The Club">The Club</option>
<option value="The Fish">The Fish</option>
<option value="The Psychic">The Psychic</option>
<option value="The Goad">The Goad</option>
<option value="The Water">The Water</option>
<option value="The Window">The Window</option>
<option value="The Manacle">The Manacle</option>
<option value="The Eye">The Eye</option>
<option value="The Mouth">The Mouth</option>
<option value="The Plant">The Plant</option>
<option value="The Serpent">The Serpent</option>
<option value="The Pillar">The Pillar</option>
<option value="The Needle">The Needle</option>
<option value="The Head">The Head</option>
<option value="The Tooth">The Tooth</option>
<option value="The Flint">The Flint</option>
<option value="The Mark">The Mark</option>
<option value="Amber Acorn">Amber Acorn</option>
<option value="Verdant Leaf">Verdant Leaf</option>
<option value="Violet Vessel">Violet Vessel</option>
<option value="Crimson Heart">Crimson Heart</option>
<option value="Cerulean Bell">Cerulean Bell</option>
</select>
</label>

<label class="r-blind-is-active checkbox-control">
<input class="r-blind-is-active-input checkbox" type="checkbox" value="is-active" checked data-r-blind-is-active>

<span class="label">Active?</span>
</label>
</div>
</div>

<div class="card">
Expand Down Expand Up @@ -678,7 +686,7 @@ <h2>Hand</h2>
</template>

<template id="card">
<div class="card playing-card" data-card>
<div class="card playing-card" data-playing-card>
<div class="stack">
<div class="actions">
<button class="c-move-left-button --icon" type="button" data-move-left-button>
Expand All @@ -703,6 +711,12 @@ <h2>Hand</h2>
<span class="label">Play?</span>
</label>

<label class="c-is-debuffed checkbox-control">
<input class="c-is-debuffed-input checkbox" type="checkbox" value="is-debuffed" data-c-is-debuffed>

<span class="label">Debuffed?</span>
</label>

<div class="input-list">
<label class="c-rank text-control">
<span class="visually-hidden">Rank</span>
Expand Down
4 changes: 4 additions & 0 deletions src/ui/css/_components.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
margin-top: 1.5rem;
}

.playing-card:not(.--has-verdant-leaf) .c-is-debuffed {
display: none;
}

.c-rank-input {
width: 5rem;
}
Expand Down
84 changes: 50 additions & 34 deletions src/ui/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { calculateScore } from '#lib/balatro.js'
import { JOKER_DEFINITIONS, JOKER_NAMES, RANKS, SUITS } from '#lib/data.js'
import type { BlindName, DeckName, Edition, Enhancement, HandName, InitialCard, InitialJoker, InitialState, JokerDefinition, JokerEdition, JokerName, Rank, Seal, Suit } from '#lib/types.js'
import type { BlindName, DeckName, Edition, Enhancement, HandName, InitialCard, InitialJoker, InitialState, JokerEdition, JokerName, Rank, Seal, Suit } from '#lib/types.js'
import { getRandomInt } from '#utilities/getRandomInt.js'
import { log } from '#utilities/log.js'
import { notNullish } from '#utilities/notNullish.js'
Expand All @@ -11,7 +11,8 @@ const form = document.querySelector('[data-form]') as HTMLFormElement
const handsEl = form.querySelector('[data-r-hands]') as HTMLInputElement
const discardsEl = form.querySelector('[data-r-discards]') as HTMLInputElement
const moneyEl = form.querySelector('[data-r-money]') as HTMLInputElement
const blindEl = form.querySelector('[data-r-blind]') as HTMLSelectElement
const blindNameEl = form.querySelector('[data-r-blind-name]') as HTMLSelectElement
const blindIsActiveEl = form.querySelector('[data-r-blind-is-active]') as HTMLInputElement
const deckEl = form.querySelector('[data-r-deck]') as HTMLSelectElement
const jokerSlotsEl = form.querySelector('[data-r-joker-slots]') as HTMLInputElement

Expand All @@ -26,6 +27,16 @@ const addCardButton = document.querySelector('[data-c-add-button]') as HTMLButto
const cardTemplate = document.querySelector('template#card') as HTMLTemplateElement

form.addEventListener('submit', handleSubmit)
// Quick and dirty way to update the state whenever necessary
form.addEventListener('change', function () {
for (const cardEl of cardContainer.children) {
updateCardState(cardEl)
}

for (const jokerEl of jokerContainer.children) {
updateJokerState(jokerEl)
}
})
addJokerButton.addEventListener('click', () => addJoker())
addCardButton.addEventListener('click', () => addCard())
window.addEventListener('popstate', () => {
Expand Down Expand Up @@ -81,15 +92,19 @@ function readStateFromUi (): InitialState {
const hands = Number(handsEl.value)
const discards = Number(discardsEl.value)
const money = Number(moneyEl.value)
const blind = blindEl.value as BlindName
const blindName = blindNameEl.value as BlindName
const blindIsActive = blindIsActiveEl.checked
const deck = deckEl.value as DeckName
const jokerSlots = Number(jokerSlotsEl.value)

const initialState: Required<InitialState> = {
hands,
discards,
money,
blind,
blind: {
name: blindName,
isActive: blindIsActive,
},
deck,
handLevels: {},
jokers: [],
Expand Down Expand Up @@ -148,20 +163,23 @@ function readStateFromUi (): InitialState {
const enhancementEl = card.querySelector('[data-c-enhancement]') as HTMLSelectElement
const sealEl = card.querySelector('[data-c-seal]') as HTMLSelectElement
const isPlayedEl = card.querySelector('[data-c-is-played]') as HTMLInputElement
const isDebuffedEl = card.querySelector('[data-c-is-debuffed]') as HTMLInputElement

const rank = rankEl.value as Rank
const suit = suitEl.value as Suit
const edition = editionEl.value as Edition
const enhancement = enhancementEl.value as Enhancement
const seal = sealEl.value as Seal
const isPlayed = isPlayedEl.checked
const isDebuffed = isDebuffedEl.checked

initialState[isPlayed ? 'playedCards' : 'heldCards'].push({
rank,
suit,
edition,
enhancement,
seal,
isDebuffed,
})
}

Expand All @@ -183,7 +201,7 @@ function populateUiWithState () {
hands = 0,
discards = 0,
money = 0,
blind = 'Small Blind',
blind: initialBlind,
deck = 'Red Deck',
handLevels = {},
jokers = [],
Expand All @@ -192,10 +210,16 @@ function populateUiWithState () {
heldCards = [],
} = initialState

const blind = {
name: initialBlind?.name ?? 'Small Blind',
isActive: initialBlind?.isActive ?? true,
}

handsEl.value = String(hands)
discardsEl.value = String(discards)
moneyEl.value = String(money)
blindEl.value = blind
blindNameEl.value = blind.name
blindIsActiveEl.checked = blind.isActive
deckEl.value = deck
jokerSlotsEl.value = String(jokerSlots)

Expand Down Expand Up @@ -249,8 +273,6 @@ function addJoker (initialJoker?: InitialJoker) {
rankInput.name = `joker-rank-${index}`
suitInput.name = `joker-suit-${index}`

nameInput.addEventListener('change', handleJokerNameChange)

const removeButton = jokerEl.querySelector('[data-remove-button]') as HTMLButtonElement
removeButton.addEventListener('click', handleRemoveJokerClick)

Expand Down Expand Up @@ -286,7 +308,7 @@ function addJoker (initialJoker?: InitialJoker) {
jokerContainer.appendChild(template)

setButtonDisabledStates(jokerContainer)
applyJokerState(jokerEl, initialJoker ? JOKER_DEFINITIONS[initialJoker.name] : undefined)
updateJokerState(jokerEl)
}

function setButtonDisabledStates (container: Element) {
Expand Down Expand Up @@ -319,13 +341,13 @@ function handleMoveJokerRightClick (event: Event) {

function handleMoveCardLeftClick (event: Event) {
if (event.currentTarget instanceof HTMLElement) {
move(cardContainer, event.currentTarget.closest('[data-card]')!, -1)
move(cardContainer, event.currentTarget.closest('[data-playing-card]')!, -1)
}
}

function handleMoveCardRightClick (event: Event) {
if (event.currentTarget instanceof HTMLElement) {
move(cardContainer, event.currentTarget.closest('[data-card]')!, 1)
move(cardContainer, event.currentTarget.closest('[data-playing-card]')!, 1)
}
}

Expand All @@ -347,16 +369,10 @@ function move (container: Element, currentEl: Element, direction: 1 | -1) {
setButtonDisabledStates(container)
}

function handleJokerNameChange (event: Event) {
const input = event.currentTarget as HTMLInputElement
const jokerName = input.value as JokerName
function updateJokerState (el: Element) {
const jokerNameEl = el.querySelector('[data-j-name]') as HTMLInputElement
const jokerName = jokerNameEl.value as JokerName
const definition = JOKER_DEFINITIONS[jokerName]
const jokerEl = input.closest('.joker')!

applyJokerState(jokerEl, definition)
}

function applyJokerState (el: Element, definition?: JokerDefinition) {
if (!definition) {
return
}
Expand All @@ -382,17 +398,19 @@ function applyJokerState (el: Element, definition?: JokerDefinition) {

function addCard (initialCard?: InitialCard, isPlayed?: boolean) {
const template = cardTemplate.content.cloneNode(true) as HTMLElement
const cardEl = template.querySelector('[data-card]') as HTMLElement
const cardEl = template.querySelector('[data-playing-card]') as HTMLElement
const index = cardContainer.children.length

const isPlayedInput = cardEl.querySelector('.c-is-played-input') as HTMLInputElement
const isDebuffedInput = cardEl.querySelector('.c-is-debuffed-input') as HTMLInputElement
const rankInput = cardEl.querySelector('.c-rank-input') as HTMLInputElement
const suitInput = cardEl.querySelector('.c-suit-input') as HTMLInputElement
const editionInput = cardEl.querySelector('.c-edition-input') as HTMLSelectElement
const enhancementInput = cardEl.querySelector('.c-enhancement-input') as HTMLSelectElement
const sealInput = cardEl.querySelector('.c-seal-input') as HTMLSelectElement

isPlayedInput.name = `card-isPlayed-${index}`
isPlayedInput.name = `card-is-played-${index}`
isDebuffedInput.name = `card-is-debuffed-${index}`
rankInput.name = `card-rank-${index}`
suitInput.name = `card-suit-${index}`
editionInput.name = `card-edition-${index}`
Expand All @@ -405,8 +423,6 @@ function addCard (initialCard?: InitialCard, isPlayed?: boolean) {
}
}, { capture: true })

isPlayedInput.addEventListener('change', handleIsPlayedChange)

const removeButton = cardEl.querySelector('[data-remove-button]') as HTMLButtonElement
removeButton.addEventListener('click', handleRemoveCardClick)

Expand Down Expand Up @@ -436,7 +452,7 @@ function addCard (initialCard?: InitialCard, isPlayed?: boolean) {
cardContainer.appendChild(template)

setButtonDisabledStates(cardContainer)
applyCardState(cardEl, Boolean(isPlayed))
updateCardState(cardEl)
}

function isInteractive (event: Event): boolean {
Expand All @@ -459,25 +475,25 @@ function isInteractive (event: Event): boolean {
return false
}

function handleIsPlayedChange (event: Event) {
const checkbox = event.currentTarget as HTMLInputElement
const cardEl = checkbox.closest('.playing-card')!

applyCardState(cardEl, checkbox.checked)
}
function updateCardState (el: Element) {
const isPlayedEl = el.querySelector('[data-c-is-played]') as HTMLInputElement
const isDebuffedEl = el.querySelector('[data-c-is-debuffed]') as HTMLInputElement

function applyCardState (el: Element, isPlayed: boolean) {
el.classList.remove(
'--is-played',
'--is-debuffed',
'--has-verdant-leaf'
)

;[
isPlayed ? '--is-played' : null,
isPlayedEl.checked ? '--is-played' : null,
isDebuffedEl.checked ? '--is-debuffed' : null,
blindNameEl.value === 'Verdant Leaf' && blindIsActiveEl.checked ? '--has-verdant-leaf' : undefined,
].filter(notNullish).forEach((className) => el.classList.add(className))
}

function handleRemoveCardClick (event: Event) {
if (event.currentTarget instanceof HTMLElement) {
event.currentTarget.closest('[data-card]')!.remove()
event.currentTarget.closest('[data-playing-card]')!.remove()
}
}

0 comments on commit 8975679

Please sign in to comment.