Added score tracking, implemented zone drag-and-drop🐉
This commit is contained in:
@ -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
|
||||
|
||||
83
src/game.rs
83
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<Uuid, String> {
|
||||
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<Uuid, String> {
|
||||
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(
|
||||
|
||||
160
src/main.rs
160
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("/<uuid>/transfer_ownership/<play_id>")]
|
||||
fn transfer_ownership(
|
||||
uuid: Uuid,
|
||||
play_id: Uuid,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/play/<index>")]
|
||||
fn play(
|
||||
uuid: Uuid,
|
||||
@ -156,6 +168,24 @@ fn play(
|
||||
respond!(game::play_from_hand(&mut game_state, index, player.clone()))
|
||||
}
|
||||
|
||||
#[post("/<uuid>/play_at/<index>/<x>/<y>")]
|
||||
fn play_at(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
x: u8,
|
||||
y: u8,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/unkill/<index>")]
|
||||
fn unkill(
|
||||
uuid: Uuid,
|
||||
@ -180,6 +210,42 @@ fn unbanish(
|
||||
respond!(game::unkill(&mut game_state, player.clone(), index, true))
|
||||
}
|
||||
|
||||
#[post("/<uuid>/unkill_at/<index>/<x>/<y>")]
|
||||
fn unkill_at(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
x: u8,
|
||||
y: u8,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/unbanish_at/<index>/<x>/<y>")]
|
||||
fn unbanish_at(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
x: u8,
|
||||
y: u8,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/discard/<index>")]
|
||||
fn discard(
|
||||
uuid: Uuid,
|
||||
@ -240,6 +306,54 @@ fn unshadow(
|
||||
respond!(game::transfer_dead_card(&mut game_state, index, true))
|
||||
}
|
||||
|
||||
#[post("/<uuid>/fade_from_discard/<index>")]
|
||||
fn fade_from_discard(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/fade_bottom_from_discard/<index>")]
|
||||
fn fade_bottom_from_discard(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/fade_from_shadow/<index>")]
|
||||
fn fade_from_shadow(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/fade_bottom_from_shadow/<index>")]
|
||||
fn fade_bottom_from_shadow(
|
||||
uuid: Uuid,
|
||||
index: usize,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/yoink/<index>")]
|
||||
fn yoink(
|
||||
uuid: Uuid,
|
||||
@ -414,16 +528,40 @@ fn fade_bottom(
|
||||
))
|
||||
}
|
||||
|
||||
#[post("/<uuid>/life/<count>")]
|
||||
fn life(
|
||||
#[post("/<uuid>/fade_from_play/<play_id>")]
|
||||
fn fade_from_play(
|
||||
uuid: Uuid,
|
||||
count: isize,
|
||||
play_id: Uuid,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/fade_bottom_from_play/<play_id>")]
|
||||
fn fade_bottom_from_play(
|
||||
uuid: Uuid,
|
||||
play_id: Uuid,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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("/<uuid>/life_diff/<diff>")]
|
||||
fn life_diff(
|
||||
uuid: Uuid,
|
||||
diff: isize,
|
||||
game_state_arc: &State<ArcMutexGameState>,
|
||||
player_uuids: &State<PlayerUuids>,
|
||||
) -> (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 {
|
||||
|
||||
@ -11,9 +11,11 @@
|
||||
<main>
|
||||
<div class="hand" id="opponent-hand"></div>
|
||||
<div id="border"></div>
|
||||
<div id="play"></div>
|
||||
<div id="play" data-border-text=" "></div>
|
||||
<div class="hand" id="own-hand"></div>
|
||||
<div id="extra" class="hidden"></div>
|
||||
<div id="extra" class="hidden">
|
||||
<div id="extra-list"></div>
|
||||
</div>
|
||||
</main>
|
||||
<aside id="view-card-container">
|
||||
<div id="param-container">
|
||||
@ -38,5 +40,9 @@
|
||||
<span id="shadow-realm-count"></span>
|
||||
</div>
|
||||
</aside>
|
||||
<aside id="counts">
|
||||
<div class="count" id="opponent-score"></div>
|
||||
<div class="count" id="own-score"></div>
|
||||
</aside>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -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
|
||||
)!
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user