diff --git a/README.md b/README.md index 64d933c..8439212 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ specifically designed to be used when players share a deck definitely not a shameless untap clone # todo -+ Add hotkeys for every zone (*Really* almost done!) ++ Add hotkeys for every zone (Basically done!) + ~~Add untap all hotkey~~ + Add viewing top of deck + ~~Add searching deck~~ -+ Add score values (usable for life counters) ++ ~~Add score values (usable for life counters)~~ + Make events visible # maybe todo -+ Add drag-and-drop from hand and piles ++ ~~Add drag-and-drop from hand and piles~~ + Make deck information, piles, and score info json-configurable + Use cookies instead of hardcoded url uuid diff --git a/src/game.rs b/src/game.rs index 3c0c7e0..14a7345 100644 --- a/src/game.rs +++ b/src/game.rs @@ -48,10 +48,13 @@ enum Event { Draw(Player, usize), Pass(Player, Player), ChangeLifeTotal(Player, isize, isize), + FadeFromPlay(Uuid, bool), + FadeFromPile(Uuid, bool), FadeFromHand(Player), PlayFromHand(Player), Bounce(Player), Tap(Uuid), + TransferOwnership(Uuid), Discard(Player), Undiscard(Player), Kill(Uuid, bool), @@ -253,6 +256,59 @@ pub fn change_life(game_state: &mut GameState, diff: isize, player: Player) { }; } +pub fn fade_from_play( + game_state: &mut GameState, + play_id: Uuid, + bottom: bool, +) -> Result<(), String> { + let played_card = match game_state.play.remove(&play_id) { + Some(played_card) => played_card, + None => return Err(format!("Nonexistant play id: {}", play_id)), + }; + gen_event!( + game_state.events, + Event::FadeFromPlay(played_card.id.clone(), bottom) + ); + if bottom { + game_state.deck.push_front(played_card.id); + } else { + game_state.deck.push_back(played_card.id); + } + Ok(()) +} + +pub fn fade_from_pile( + game_state: &mut GameState, + pile_index: usize, + shadow: bool, + bottom: bool, +) -> Result<(), String> { + let pile = if shadow { + &mut game_state.shadow_realm + } else { + &mut game_state.discard_pile + }; + if None == pile.get(pile_index) { + let pile_name = if shadow { + "shadow realm" + } else { + "discard pile" + }; + return Err(format!( + "Unkill index out of bounds: {} ({})", + pile_index, pile_name + )); + } + let card = pile.remove(pile_index); + gen_event!(game_state.events, Event::FadeFromPile(card.clone(), shadow)); + if bottom { + game_state.deck.push_front(card); + } else { + game_state.deck.push_back(card); + } + Ok(()) +} + pub fn fade_from_hand( game_state: &mut GameState, hand_index: usize, @@ -281,7 +337,7 @@ pub fn play_from_hand( game_state: &mut GameState, hand_index: usize, player: Player, -) -> Result<(), String> { +) -> Result { let player_hand = match player { Player::A => &mut game_state.hands[0], Player::B => &mut game_state.hands[1], @@ -292,6 +348,7 @@ pub fn play_from_hand( } let card = player_hand.cards.remove(hand_index); let play_uuid = Uuid::new_v4(); + let return_uuid = play_uuid.clone(); game_state.play.insert( play_uuid, InPlay { @@ -303,7 +360,7 @@ pub fn play_from_hand( play_id: play_uuid, }, ); - Ok(()) + Ok(return_uuid) } pub fn bounce(game_state: &mut GameState, play_id: Uuid) -> Result<(), String> { @@ -330,6 +387,21 @@ pub fn tap(game_state: &mut GameState, play_id: Uuid) -> Result<(), String> { } } +pub fn transfer_ownership(game_state: &mut GameState, play_id: Uuid) -> Result<(), String> { + gen_event!(game_state.events, Event::TransferOwnership(play_id.clone())); + match game_state.play.get_mut(&play_id) { + Some(played_card) => { + if played_card.owner == Player::A { + played_card.owner = Player::B; + } else { + played_card.owner = Player::A; + } + Ok(()) + } + None => return Err(format!("Nonexistant play id: {}", play_id)), + } +} + pub fn move_played_card( game_state: &mut GameState, play_id: Uuid, @@ -415,7 +487,7 @@ pub fn unkill( player: Player, pile_index: usize, shadow: bool, -) -> Result<(), String> { +) -> Result { let pile = if shadow { &mut game_state.shadow_realm } else { @@ -428,13 +500,14 @@ pub fn unkill( "discard pile" }; return Err(format!( - "Transfer index out of bounds: {} ({})", + "Unkill index out of bounds: {} ({})", pile_index, pile_name )); } let card = pile.remove(pile_index); gen_event!(game_state.events, Event::Unkill(card, shadow)); let play_uuid = Uuid::new_v4(); + let return_uuid = play_uuid.clone(); game_state.play.insert( play_uuid, InPlay { @@ -446,7 +519,7 @@ pub fn unkill( play_id: play_uuid, }, ); - Ok(()) + Ok(return_uuid) } pub fn transfer_dead_card( diff --git a/src/main.rs b/src/main.rs index cb10482..b494fc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,7 +48,7 @@ macro_rules! respond_ok { macro_rules! respond { ($state:expr) => { match $state { - Ok(()) => respond_ok!(), + Ok(_) => respond_ok!(), Err(err_string) => (Status::BadRequest, (ContentType::Plain, err_string)), } }; @@ -144,6 +144,18 @@ fn tap( respond!(game::tap(&mut game_state, play_id)) } +#[post("//transfer_ownership/")] +fn transfer_ownership( + uuid: Uuid, + play_id: Uuid, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::transfer_ownership(&mut game_state, play_id)) +} + #[post("//play/")] fn play( uuid: Uuid, @@ -156,6 +168,24 @@ fn play( respond!(game::play_from_hand(&mut game_state, index, player.clone())) } +#[post("//play_at///")] +fn play_at( + uuid: Uuid, + index: usize, + x: u8, + y: u8, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + let player = get_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + let res = game::play_from_hand(&mut game_state, index, player.clone()); + match res { + Ok(play_id) => respond!(game::move_played_card(&mut game_state, play_id, x, y)), + Err(_) => respond!(res), + } +} + #[post("//unkill/")] fn unkill( uuid: Uuid, @@ -180,6 +210,42 @@ fn unbanish( respond!(game::unkill(&mut game_state, player.clone(), index, true)) } +#[post("//unkill_at///")] +fn unkill_at( + uuid: Uuid, + index: usize, + x: u8, + y: u8, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + let player = get_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + let res = game::unkill(&mut game_state, player.clone(), index, false); + match res { + Ok(play_id) => respond!(game::move_played_card(&mut game_state, play_id, x, y)), + Err(_) => respond!(res), + } +} + +#[post("//unbanish_at///")] +fn unbanish_at( + uuid: Uuid, + index: usize, + x: u8, + y: u8, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + let player = get_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + let res = game::unkill(&mut game_state, player.clone(), index, true); + match res { + Ok(play_id) => respond!(game::move_played_card(&mut game_state, play_id, x, y)), + Err(_) => respond!(res), + } +} + #[post("//discard/")] fn discard( uuid: Uuid, @@ -240,6 +306,54 @@ fn unshadow( respond!(game::transfer_dead_card(&mut game_state, index, true)) } +#[post("//fade_from_discard/")] +fn fade_from_discard( + uuid: Uuid, + index: usize, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_pile(&mut game_state, index, false, false)) +} + +#[post("//fade_bottom_from_discard/")] +fn fade_bottom_from_discard( + uuid: Uuid, + index: usize, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_pile(&mut game_state, index, false, true)) +} + +#[post("//fade_from_shadow/")] +fn fade_from_shadow( + uuid: Uuid, + index: usize, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_pile(&mut game_state, index, true, false)) +} + +#[post("//fade_bottom_from_shadow/")] +fn fade_bottom_from_shadow( + uuid: Uuid, + index: usize, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_pile(&mut game_state, index, true, true)) +} + #[post("//yoink/")] fn yoink( uuid: Uuid, @@ -414,16 +528,40 @@ fn fade_bottom( )) } -#[post("//life/")] -fn life( +#[post("//fade_from_play/")] +fn fade_from_play( uuid: Uuid, - count: isize, + play_id: Uuid, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_play(&mut game_state, play_id, false)) +} + +#[post("//fade_bottom_from_play/")] +fn fade_bottom_from_play( + uuid: Uuid, + play_id: Uuid, + game_state_arc: &State, + player_uuids: &State, +) -> (Status, (ContentType, String)) { + assert_player!(player_uuids, uuid); + unlock_state_mut!(game_state_arc, mutex, game_state); + respond!(game::fade_from_play(&mut game_state, play_id, true)) +} + +#[post("//life_diff/")] +fn life_diff( + uuid: Uuid, + diff: isize, game_state_arc: &State, player_uuids: &State, ) -> (Status, (ContentType, String)) { let player = get_player!(player_uuids, uuid); unlock_state_mut!(game_state_arc, mutex, game_state); - game::change_life(&mut game_state, count, player.clone()); + game::change_life(&mut game_state, diff, player.clone()); respond_ok!() } @@ -473,12 +611,14 @@ fn rocket() -> _ { draw, shuffle, pass, - life, + life_diff, fade, fade_bottom, play, + play_at, bounce, tap, + transfer_ownership, move_card, discard, kill, @@ -486,6 +626,8 @@ fn rocket() -> _ { banish, unkill, unbanish, + unkill_at, + unbanish_at, shadow, unshadow, undiscard, @@ -495,6 +637,12 @@ fn rocket() -> _ { yoink, yoink_mill, yoink_xmill, + fade_from_play, + fade_bottom_from_play, + fade_from_discard, + fade_bottom_from_discard, + fade_from_shadow, + fade_bottom_from_shadow, ], ) .manage(ArcMutexGameState { diff --git a/web/dynamic/index.html b/web/dynamic/index.html index 569d3ca..f2e0af1 100644 --- a/web/dynamic/index.html +++ b/web/dynamic/index.html @@ -11,9 +11,11 @@
-
+
- +
+ diff --git a/web/src/script.ls b/web/src/script.ls index 165a603..4d938d7 100644 --- a/web/src/script.ls +++ b/web/src/script.ls @@ -1,28 +1,41 @@ (->> - const err = -> console.error it + const err = -> + console.error it + $play.set-attribute \data-border-text it + set-timeout (-> $play.set-attribute \data-border-text ' '), 3000 const fetch-post = -> const req = new Request it, { method: \POST } - fetch req .then (-> + (fetch req .then ->> + throw await it.text! unless it.ok console.log it it ) .catch err + const life-diff$ = -> + fetch-post "./life_diff/#it" + life-index = +('B' == state.player-state.you) + current-life = state.player-state.life_totals[life-index] + $own-score.inner-text = current-life + it window.fetch-post = fetch-post - const $opponent-hand = document.query-selector '#opponent-hand' - const $own-hand = document.query-selector '#own-hand' - const $view-card = document.query-selector '#view-card' - const $view-card-container = document.query-selector '#view-card-container' - const $param = document.query-selector '#param' - const $play = document.query-selector '#play' - const $discard-pile = document.query-selector '#discard-pile' - const $discard-pile-img = document.query-selector '#discard-pile-img' - const $discard-pile-count = document.query-selector '#discard-pile-count' - const $shadow-realm = document.query-selector '#shadow-realm' - const $shadow-realm-img = document.query-selector '#shadow-realm-img' - const $shadow-realm-count = document.query-selector '#shadow-realm-count' - const $deck = document.query-selector '#deck' - const $deck-img = document.query-selector '#deck-img' - const $deck-count = document.query-selector '#deck-count' - const $extra = document.query-selector '#extra' + const $ = -> document.query-selector it + const $opponent-hand = $ \#opponent-hand + const $own-hand = $ \#own-hand + const $view-card = $ \#view-card + const $view-card-container = $ \#view-card-container + const $param = $ \#param + const $play = $ \#play + const $discard-pile = $ \#discard-pile + const $discard-pile-img = $ \#discard-pile-img + const $discard-pile-count = $ \#discard-pile-count + const $shadow-realm = $ \#shadow-realm + const $shadow-realm-img = $ \#shadow-realm-img + const $shadow-realm-count = $ \#shadow-realm-count + const $deck = $ \#deck + const $deck-img = $ \#deck-img + const $deck-count = $ \#deck-count + const $extra = $ \#extra + const $extra-list = $ \#extra-list + const $opponent-score = $ \#opponent-score + const $own-score = $ \#own-score const [push-param, pop-param] = (-> param = "" clear-param-timer = 0 @@ -52,7 +65,7 @@ const get-play-size = -> $play.get-bounding-client-rect! const gen-array = -> Array.from { length: it }, (_, i) -> i const gen-array-of = (it, val) -> Array.from { length: it }, -> val - const cards = await fetch \/cards.json .then (.json!) + const cards = await fetch \/cards.json .then (.json!) window.cards = cards const $make-card = -> $img = document.create-element \img @@ -70,9 +83,9 @@ | _ void const get-state = ->> - player-state-json = await fetch \./get_state .then (.text!) + player-state-json = await fetch \./get_state .then (.text!) player-state = JSON.parse player-state-json - events-json = await fetch \/get_events .then (.text!) + events-json = await fetch \/get_events .then (.text!) events = JSON.parse events-json play-map = new Map player-state.play.map -> [it.id, it] return { player-state-json, player-state, events-json, events, play-map } @@ -87,6 +100,10 @@ state = await get-state! const apply-state$ = ->> { player-state } = it + if player-state.you == \A + [$own-score.inner-text, $opponent-score.inner-text] = player-state.life_totals + else + [$opponent-score.inner-text, $own-score.inner-text] = player-state.life_totals gen-opponent-cards$ player-state.opponent_cards_in_hand if document.body.class-list.contains \my-turn document.body.class-list.remove \my-turn if player-state.you != player-state.turn_player @@ -124,6 +141,23 @@ ..set-attribute \data-hand-index index ..set-attribute \data-text card-data.txt ..set-attribute \data-png card-data.png + ..set-attribute \draggable \true + ..add-event-listener \dragend -> + console.log $drop-zone + switch $drop-zone + | $play + const x-percent = Math.round (100 * it.client-x / document.document-element.client-width) + const height = document.document-element.client-height + const y-top-offset = 0.14 * height + y-bottom-offset = y-top-offset + y-bottom-offset *= 2 unless $extra.class-list.contains \hidden + const y-percent = Math.round (100 * (it.client-y - y-top-offset) / (height - y-bottom-offset - y-top-offset)) + const x = clamp 5 x-percent, 95 + const y = clamp 5 y-percent, 95 + fetch-post "./play_at/#index/#x/#y" + | $discard-pile => fetch-post "./discard/#index" + | $shadow-realm => fetch-post "./forget/#index" + | $deck => fetch-post "./fade/#index" ..add-event-listener \mouseenter show-card$ $own-hand.append-child card next-play-id-list = [...player-state.play.map (.play_id)].sort!join \| @@ -135,6 +169,7 @@ relative-y = it.position_y else relative-y = 100 - it.position_y + { play_id } = it # for closure purposes img = $make-card card-data.jpg img ..class-list.add \in-play-card @@ -149,6 +184,12 @@ ..set-attribute \data-owner it.owner ..set-attribute \data-owned it.owner == player-state.you ..set-attribute \data-tapped it.tapped + ..add-event-listener \dblclick (_) -> + if \true == img.get-attribute \data-tapped + img.set-attribute \data-tapped \false + else + img.set-attribute \data-tapped \true + fetch-post "./tap/#play_id" ..add-event-listener \mouseenter show-card$ ..add-event-listener \dragstart -> it.target.set-attribute \data-start-x it.target.offset-left @@ -156,26 +197,33 @@ it.target.set-attribute \data-client-x it.client-x it.target.set-attribute \data-client-y it.client-y ..add-event-listener \dragend -> - const start-x = +it.target.get-attribute \data-start-x - const start-y = +it.target.get-attribute \data-start-y - const start-client-x = +it.target.get-attribute \data-client-x - const start-client-y = +it.target.get-attribute \data-client-y - const new-x = it.client-x + (start-x - start-client-x) - const new-y = it.client-y + (start-y - start-client-y) - const scale-x = +start-x / +(it.target.get-attribute \data-play-x) - const scale-y = +start-y / +(it.target.get-attribute \data-play-y) - const x = clamp 5 Math.round(new-x / scale-x), 95 - const y = clamp 5 Math.round(new-y / scale-y), 95 - it.target.style - ..left = x + \% - ..top = y + \% - it.target.set-attribute \data-play-x x - it.target.set-attribute \data-play-y y - if state.player-state.you == it.target.get-attribute \data-owner - absolute-y = y - else - absolute-y = 100 - y - fetch-post "./move/#{it.target.get-attribute \data-play-id}/#x/#absolute-y" + switch $drop-zone + | $discard-pile => fetch-post "./kill/#play_id" + | $shadow-realm => fetch-post "./banish/#play_id" + | $deck => fetch-post "./fade/#index" + | $play => + const start-x = +it.target.get-attribute \data-start-x + const start-y = +it.target.get-attribute \data-start-y + const start-client-x = +it.target.get-attribute \data-client-x + const start-client-y = +it.target.get-attribute \data-client-y + const new-x = it.client-x + (start-x - start-client-x) + const new-y = it.client-y + (start-y - start-client-y) + const scale-x = +start-x / +(it.target.get-attribute \data-play-x) + const scale-y = +start-y / +(it.target.get-attribute \data-play-y) + const x = clamp 5 Math.round(new-x / scale-x), 95 + const y = clamp 5 Math.round(new-y / scale-y), 95 + if y > 90 && \true == img.get-attribute \data-owned + return fetch-post "./bounce/#play_id" + it.target.style + ..left = x + \% + ..top = y + \% + it.target.set-attribute \data-play-x x + it.target.set-attribute \data-play-y y + if state.player-state.you == it.target.get-attribute \data-owner + absolute-y = y + else + absolute-y = 100 - y + fetch-post "./move/#{it.target.get-attribute \data-play-id}/#x/#absolute-y" $play.append-child img play-id-list := next-play-id-list else @@ -189,39 +237,102 @@ $element.style ..left = it.position_x + \% ..top = relative-y + \% + $element.set-attribute \data-owner it.owner + $element.set-attribute \data-owned it.owner == player-state.you $element.set-attribute \data-play-x it.position_x $element.set-attribute \data-play-y relative-y apply-state$ state - const open-list = (list, list-type) --> + const open-list = (list, list-type, dragend-callback) --> const $card-list = list.map (card-id, index) -> if card-id ret = $make-card cards[card-id].jpg + ret.add-event-listener \dragend -> dragend-callback it, index + ret.set-attribute \draggable \true ret.set-attribute \data-png cards[card-id].png - ret.set-attribute "data-index" index - ret.set-attribute "data-list-type" list-type + ret.set-attribute \data-index index + ret.set-attribute \data-list-type list-type ret.add-event-listener \mouseenter show-card$ ret else $make-blank-card! - $extra.innerHTML = "" - $card-list.for-each -> $extra.append it + $extra-list.innerHTML = "" + $card-list.for-each -> $extra-list.append it $extra.set-attribute \data-pile-type list-type $extra.class-list.remove \hidden $discard-pile-img.add-event-listener \mouseenter show-card$ $shadow-realm-img.add-event-listener \mouseenter show-card$ - $play.add-event-listener \dragover (.prevent-default!) - - $discard-pile-img.add-event-listener \click -> open-list state.player-state.discard_pile, \discard - $discard-pile-count.add-event-listener \click -> open-list state.player-state.discard_pile, \discard - $shadow-realm-img.add-event-listener \click -> open-list state.player-state.shadow_realm, \shadow - $shadow-realm-count.add-event-listener \click -> open-list state.player-state.shadow_realm, \shadow - $deck-img.add-event-listener \click -> open-list (gen-array-of state.player-state.deck_size, null), \deck - $deck-count.add-event-listener \click -> open-list (gen-array-of state.player-state.deck_size, null), \deck + $discard-pile.add-event-listener \click -> open-list state.player-state.discard_pile, \discard (it, index) -> + switch $drop-zone + | $play + const x-percent = Math.round (100 * it.client-x / document.document-element.client-width) + const height = document.document-element.client-height + const y-top-offset = 0.14 * height + y-bottom-offset = y-top-offset + y-bottom-offset *= 2 unless $extra.class-list.contains \hidden + const y-percent = Math.round (100 * (it.client-y - y-top-offset) / (height - y-bottom-offset - y-top-offset)) + const x = clamp 5 x-percent, 95 + const y = clamp 5 y-percent, 95 + fetch-post "./unkill_at/#index/#x/#y" + | $discard-pile => return + | $shadow-realm => fetch-post "./shadow/#index" + | $deck => fetch-post "./fade_from_discard/#index" + | $own-hand => "./undiscard/#index" + it.target.parent-element.remove-child it.target + clear-extra-on-empty$! + $shadow-realm.add-event-listener \click -> open-list state.player-state.shadow_realm, \shadow (it, index) -> + switch $drop-zone + | $play + const x-percent = Math.round (100 * it.client-x / document.document-element.client-width) + const height = document.document-element.client-height + const y-top-offset = 0.14 * height + y-bottom-offset = y-top-offset + y-bottom-offset *= 2 unless $extra.class-list.contains \hidden + const y-percent = Math.round (100 * (it.client-y - y-top-offset) / (height - y-bottom-offset - y-top-offset)) + const x = clamp 5 x-percent, 95 + const y = clamp 5 y-percent, 95 + fetch-post "./unbanish_at/#index/#x/#y" + | $discard-pile => fetch-post "./unshadow/#index" + | $shadow-realm => return + | $deck => fetch-post "./fade_from_shadow/#index" + | $own-hand => "./remember/#index" + it.target.parent-element.remove-child it.target + clear-extra-on-empty$! + $deck.add-event-listener \click -> open-list (gen-array-of state.player-state.deck_size, null), \deck + + $drop-zone = $play + const set-drop-zone$ = -> + it.prevent-default! + $drop-zone := it.target + #console.log "New drop zone: ", $drop-zone + $play.add-event-listener \dragover set-drop-zone$ + $discard-pile.add-event-listener \dragover set-drop-zone$ + $shadow-realm.add-event-listener \dragover set-drop-zone$ + $deck.add-event-listener \dragover set-drop-zone$ + $own-hand.add-event-listener \dragover set-drop-zone$ + + wheel-wait = false + $own-score.add-event-listener \wheel -> + return if wheel-wait + diff = switch + | it.delta-x < 0 => -5 + | it.delta-x > 0 => 5 + | it.delta-y > 0 => -10 + | it.delta-y < 0 => 10 + diff *= 2 if it.shift-key + diff *= -1 if it.alt-key + life-diff$ diff + wheel-wait := true + set-timeout (-> wheel-wait := false), 250 + const clear-extra-on-empty$ = -> + if !$extra-list.child-element-count + $extra.class-list.add \hidden + $extra.remove-attribute \data-pile-type + set-interval (->> const new-state = await get-state! unless new-state.player-state-json == state.player-state-json @@ -230,6 +341,19 @@ window.state = state ), 1000 mouse-x = mouse-y = 0 + $own-score.add-event-listener \click -> + diff = 1 + diff *= 10 if it.shift-key + diff *= 2 if it.ctrl-key + diff *= -1 if it.alt-key + life-diff$ diff + $own-score.add-event-listener \contextmenu -> + it.prevent-default! + diff = -1 + diff *= 10 if it.shift-key + diff *= 2 if it.ctrl-key + diff *= -1 if it.alt-key + life-diff$ diff document ..add-event-listener \mousemove -> mouse-x = it.client-x @@ -240,20 +364,45 @@ $view-card-container.class-list.add \right ..add-event-listener \keyup -> $current-mouse-node = [...document.query-selector-all \:hover][* - 1] - switch it.key.to-lower-case! + key = it.key.to-lower-case! + key = "!#key" if it.alt-key + key = "+#key" if it.shift-key + key = "^#key" if it.ctrl-key + switch key | \v => fetch-post \./shuffle if $extra.has-attribute \data-pile-type and \deck-revealed == $extra.get-attribute \data-pile-type $extra.class-list.add \hidden - | \c => fetch-post "./draw/#{pop-param!}" + | \c , \^d => fetch-post "./draw/#{pop-param!}" | \e => fetch-post \./pass | \0 \1 \2 \3 \4 \5 \6 \7 \8 \9 => push-param that | \t => if $current-mouse-node?.has-attribute \data-hand-index fetch-post "./fade/#{$current-mouse-node.get-attribute \data-hand-index}" + else if $current-mouse-node?.has-attribute \data-list-type + switch $current-mouse-node.get-attribute \data-list-type + | \discard + fetch-post "./fade_from_discard/#{$current-mouse-node.get-attribute \data-index}" + | \deck , \deck-revealed => return + | \shadow + fetch-post "./fade_from_shadow/#{$current-mouse-node.get-attribute \data-index}" + $current-mouse-node.parent-element.remove-child $current-mouse-node + clear-extra-on-empty$! | \y => if $current-mouse-node?.has-attribute \data-hand-index fetch-post "./fade_bottom/#{$current-mouse-node.get-attribute \data-hand-index}" + else if $current-mouse-node?.has-attribute \data-list-type + switch $current-mouse-node.get-attribute \data-list-type + | \discard + fetch-post "./fade_bottom_from_discard/#{$current-mouse-node.get-attribute \data-index}" + | \deck , \deck-revealed => return + | \shadow + fetch-post "./fade_bottom_from_shadow/#{$current-mouse-node.get-attribute \data-index}" + $current-mouse-node.parent-element.remove-child $current-mouse-node + clear-extra-on-empty$! + | \b => + if $current-mouse-node?.has-attribute \data-play-id + fetch-post "./transfer_ownership/#{$current-mouse-node.get-attribute \data-play-id}" | ' ' => if $current-mouse-node?.has-attribute \data-hand-index fetch-post "./play/#{$current-mouse-node.get-attribute \data-hand-index}" @@ -272,9 +421,7 @@ | \shadow fetch-post "./unbanish/#{$current-mouse-node.get-attribute \data-index}" $current-mouse-node.parent-element.remove-child $current-mouse-node - if !$extra.child-element-count - $extra.class-list.add \hidden - $extra.remove-attribute \data-pile-type + clear-extra-on-empty$! | \r => if $current-mouse-node?.has-attribute \data-play-id fetch-post "./bounce/#{$current-mouse-node.get-attribute \data-play-id}" @@ -287,9 +434,7 @@ | \shadow fetch-post "./remember/#{$current-mouse-node.get-attribute \data-index}" $current-mouse-node.parent-element.remove-child $current-mouse-node - if !$extra.child-element-count - $extra.class-list.add \hidden - $extra.remove-attribute \data-pile-type + clear-extra-on-empty$! | \d => if $current-mouse-node?.has-attribute \data-hand-index fetch-post "./discard/#{$current-mouse-node.get-attribute \data-hand-index}" @@ -304,9 +449,7 @@ | \shadow fetch-post "./unshadow/#{$current-mouse-node.get-attribute \data-index}" $current-mouse-node.parent-element.remove-child $current-mouse-node - if !$extra.child-element-count - $extra.class-list.add \hidden - $extra.remove-attribute \data-pile-type + clear-extra-on-empty$! | \s => if $current-mouse-node?.has-attribute \data-hand-index fetch-post "./forget/#{$current-mouse-node.get-attribute \data-hand-index}" @@ -320,14 +463,12 @@ fetch-post "./yoink_xmill/#{$current-mouse-node.get-attribute \data-index}" | \shadow return + clear-extra-on-empty$! $current-mouse-node.parent-element.remove-child $current-mouse-node - if !$extra.child-element-count - $extra.class-list.add \hidden - $extra.remove-attribute \data-pile-type | \q => $extra.class-list.add \hidden $extra.remove-attribute \data-pile-type - | \x => + | \x , \+x => if it.shift-key || state.player-state.turn_player == state.player-state.you fetch-post \./untap_all [...$play.children] @@ -337,4 +478,18 @@ fetch-post \./get_deck .then ->> const deck = await it.json! open-list deck, \deck-revealed + | \^e => + alert 1 + count = +prompt "Number to draw:" + if count != count or count < 0 + return err "Invalid number: #count" + fetch-post "./draw/#count" + | \l => + it.prevent-default! + count = +prompt "New life total:" + if count != count or count < 0 + return err "Invalid number: #count" + life-index = if state.player-state.you == \a then 0 else 1 + diff = count - state.player-state.life_totals[life-index] + life-diff$ diff )! diff --git a/web/src/style.scss b/web/src/style.scss index aa34275..393c1a7 100644 --- a/web/src/style.scss +++ b/web/src/style.scss @@ -35,7 +35,6 @@ main { display: flex; flex-direction: row; justify-content: center; - gap: 0.2%; align-items: center; img { display: flex; @@ -49,7 +48,7 @@ main { #extra { transition: height 0.5s; - overflow: auto; + overflow: scroll; background-color: #222d; } @@ -57,6 +56,14 @@ main { height: 0; } +#extra-list { + height: 100%; + display: flex; + flex-direction: row; + gap: 0.2vw; + overflow: scroll; +} + aside { position: absolute; height: 100%; @@ -129,13 +136,15 @@ aside { } #play::before { - content: " "; + content: attr(data-border-text); position: absolute; top: 48%; bottom: 48%; left: 0; right: 0; background-color: inherit; + color: #fff; + text-align: center; } .my-turn #play { @@ -170,3 +179,33 @@ aside { img { border-radius: 10%; } + +#counts { + position: absolute; + right: 0; + width: 7vw; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.count { + font-size: 2.5rem; + color: #fff; + width: 100%; + text-align: center; + height: 14vh; + display: flex; + flex-direction: column; + justify-content: center; + user-select: none; +} + +#opponent-score { + background-color: #f002; +} + +#own-score { + background-color: #0ff2; +}