Skip to content

Commit

Permalink
Merge branch 'website'
Browse files Browse the repository at this point in the history
  • Loading branch information
anafro committed Apr 15, 2024
2 parents 2802fe8 + aed49f8 commit 33af876
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 101 deletions.
59 changes: 30 additions & 29 deletions website/src/components/Board.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ref } from 'vue';
import { generateUUIDv4 } from '../crypto/uuids';
import { byClass, closest, indexOfChild, locate, locateMouse, measure, move } from '../dom/dom';
import { firecracker } from '../particles/particles';
import { MoveType, useBoard } from '../stores/board-store';
import { useBoard } from '../stores/board-store';
import { Coordinate } from '../thunderlight/coordinate';
import { EngineMode } from '../thunderlight/engine-mode';
import { jukebox } from '../utils/jukebox';
Expand All @@ -13,7 +13,7 @@ import Inventory from './Inventory.vue';
import ModeSwitch from './ModeSwitch.vue';
let hand: HTMLElement | undefined = undefined;
const hand = ref<HTMLElement | undefined>(undefined);
const board = useBoard();
const mode = ref<EngineMode>('board');
Expand All @@ -22,11 +22,11 @@ function getCells(): HTMLElement[] {
}
function locateHand(): Coordinate {
if (hand === undefined) {
if (hand.value === undefined) {
throw new Error("Cannot locate an empty hand");
}
const cell = hand.parentElement;
const cell = hand.value.parentElement;
if (cell === null) {
throw new Error("Cannot locate a hand with no parent")
Expand Down Expand Up @@ -55,33 +55,37 @@ function highlight(cell: HTMLElement) {
function onPieceGrab(element: HTMLElement) {
jukebox.play("piece.grab", 0.2);
hand = element;
hand.style.position = 'absolute';
hand.value = element;
hand.value.style.position = 'absolute';
console.info("piece.grab")
}
async function onPieceDrop(piece: HTMLElement) {
if (hand === undefined) {
async function onPieceDrop(_: HTMLElement) {
if (hand.value === undefined) {
return;
}
console.info("piece.drop")
const cells = getCells();
const underlyingCell = closest(cells, hand);
const underlyingCell = closest(cells, hand.value);
const source = locateHand();
const destination = locateCell(underlyingCell);
hand.style.position = '';
hand.style.top = '';
hand.style.left = '';
hand.value.style.position = '';
hand.value.style.top = '';
hand.value.style.left = '';
const move = board.move(source, destination);
switch (move) {
case MoveType.TRAVEL:
case 'travel':
jukebox.play("piece.drop", 0.3);
break;
case MoveType.ATTACK:
hand = undefined;
case 'attack':
hand.value = undefined;
await sleep(100);
jukebox.play("piece.attack", 0.8, 0.3);
Expand All @@ -90,35 +94,33 @@ async function onPieceDrop(piece: HTMLElement) {
firecracker.splash("piece.shred", locate(underlyingCell), 30, 25);
break;
case MoveType.BACK:
case 'back':
jukebox.play("piece.back", 0.8, 0.3);
break;
case MoveType.PROHIBITED:
case 'prohibited':
jukebox.play("piece.prohibited");
break;
}
fadeCells();
hand = undefined;
hand.value = undefined;
}
function onPieceMove(event: MouseEvent): void {
if(hand === undefined) {
if(hand.value === undefined) {
return;
}
const mouse = locateMouse(event).shift(measure(hand).shorten(3));
const mouse = locateMouse(event).shift(measure(hand.value).shorten(3));
const cells = getCells();
const underlyingCell = closest(cells, hand);
console.info(`${mouse}`, hand);
const underlyingCell = closest(cells, hand.value);
if(!underlyingCell.classList.contains('highlighted')) {
fadeCells();
}
move(hand, mouse);
move(hand.value, mouse);
highlight(underlyingCell);
}
Expand All @@ -129,16 +131,15 @@ function onPieceMove(event: MouseEvent): void {
<ModeSwitch v-model="mode"></ModeSwitch>

<div id="board">
<Inventory player="gote"></Inventory>
<Inventory player="gote" v-model="hand"></Inventory>

<div id="cells" @mousemove="onPieceMove">
<div class="cell" v-for="piece of board.cells">
<DraggablePiece
v-if="piece !== undefined"
:key="piece?.id ?? generateUUIDv4()"
:kanji="piece.type.kanji"
:promoted="piece?.type.promoted"
:grabbable="piece?.player === board.player"
:type="piece.type"
:grabbable="piece?.player === board.turn"
:enemy="piece?.player === 'gote'"
:state="piece?.state"
ref="pieces"
Expand All @@ -148,7 +149,7 @@ function onPieceMove(event: MouseEvent): void {
</div>
</div>
<Inventory player="sente"></Inventory>
<Inventory player="sente" v-model="hand"></Inventory>
</div>
</div>
Expand Down
37 changes: 11 additions & 26 deletions website/src/components/DraggablePiece.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
<script setup lang="ts">
import { Ref, ref } from 'vue';
import { shake } from '../utils/numbers';
import { PieceState } from '../thunderlight/piece-type';
import { PieceState, PieceType } from '../thunderlight/piece-type';
const emit = defineEmits([
'grab',
'drop',
]);
const props = defineProps<{
kanji: string,
type: PieceType,
grabbable: boolean,
enemy: boolean,
promoted: boolean,
state: PieceState
}>();
const piece: Ref<HTMLElement | null> = ref(null);
function onPieceGrab(event: MouseEvent): void {
// if (!props.grabbable) {
// return;
// }
function onPieceGrab(_: MouseEvent): void {
if (!piece.value) {
return;
}
// const element: HTMLElement = event.currentTarget as HTMLElement;
piece.value.style.transition = '';
piece.value.style.zIndex = '99';
emit("grab", piece.value);
emit("grab", piece.value, props.type);
}
function onPieceDrop(event: MouseEvent) {
// if (!props.grabbable) {
// return;
// }
// const element: HTMLElement = event.currentTarget as HTMLElement;
function onPieceDrop(_: MouseEvent) {
if (!piece.value) {
return;
}
Expand All @@ -49,24 +37,24 @@ function onPieceDrop(event: MouseEvent) {
piece.value.style.transition = '200ms ease-in-out';
piece.value.style.transform = `rotate(${shake(0, 15) + (props.enemy ? 180 : 0)}deg)`;
emit("drop", piece.value);
emit("drop", piece.value, props.type);
}
</script>

<template>
<div
class="piece"
ref="piece"
:class="{grabbable, promoted, enemy, [state]: true }"
:title="grabbable ? `😈⚔️ Grab this ${kanji} to move!` : `⛔🤚 This is not your ${kanji}!`"
:class="{grabbable, promoted: type.promoted, enemy, [state]: true }"
:title="enemy ? `That's not this player's turn!` : `Grab this piece to make a move.`"
@mousedown="onPieceGrab"
@mouseup="onPieceDrop"
>
<svg class="piece-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 59.97 62.8">
<polygon class="piece-body" points="54.67 14.25 29.99 0 29.99 0 29.99 0 5.3 14.25 0 62.8 29.99 62.8 59.97 62.8 54.67 14.25"/>
</svg>

<span class="piece-kanji">{{ kanji }}</span>
<span class="piece-kanji">{{ type.kanji }}</span>
</div>

</template>
Expand Down Expand Up @@ -108,7 +96,7 @@ function onPieceDrop(event: MouseEvent) {
cursor: not-allowed
transform-style: preserve-3d
transition: 75ms
filter: hue-rotate(0deg)
opacity: 0.3
&.drop
animation: piece-drop 200ms ease-in
Expand All @@ -119,16 +107,13 @@ function onPieceDrop(event: MouseEvent) {
&.grabbable
opacity: 1
cursor: grab
filter: hue-rotate(0)
filter: hue-rotate(0deg)
&:active
scale: 1.5
cursor: grabbing
filter: drop-shadow(10px 10px 5px transparentize($background, 0.60))
&:not(.grabbable)
opacity: 0.3
& > *
grid-row: 1
grid-column: 1
Expand Down
99 changes: 94 additions & 5 deletions website/src/components/Inventory.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,101 @@
<script setup lang="ts">
import { generateUUIDv4 } from '../crypto/uuids';
import { byClass, closest, indexOfChild } from '../dom/dom';
import { useBoard } from '../stores/board-store';
import { TYPES } from '../thunderlight/piece-type';
import { Coordinate } from '../thunderlight/coordinate';
import { PieceType } from '../thunderlight/piece-type';
import { Player } from '../thunderlight/player';
import { jukebox } from '../utils/jukebox';
import DraggablePiece from './DraggablePiece.vue';
defineProps<{
player: Player,
}>();
const hand = defineModel<HTMLElement | undefined>(undefined);
const board = useBoard();
function getCells(): HTMLElement[] {
return byClass("cell");
}
function locateHand(): Coordinate {
return new Coordinate(4, 4);
// if (hand.value === undefined) {
// throw new Error("Cannot locate an empty hand");
// }
// const cell = hand.value.parentElement;
// if (cell === null) {
// console.error("The hand is ", hand.value, hand.value.parentElement)
// throw new Error("Cannot locate a hand with no parent")
// }
// return locateCell(cell);
}
function locateCell(cell: HTMLElement): Coordinate {
const childIndex = indexOfChild(cell);
const x = childIndex % 9;
const y = Math.floor(childIndex / 9);
return new Coordinate(x, y);
}
function fadeCells() {
getCells().forEach(cell => {
cell.classList.remove('highlighted')
});
}
function highlight(cell: HTMLElement) {
cell.classList.add('highlighted');
}
function onPieceGrab(element: HTMLElement) {
console.info(element, hand.value);
jukebox.play("piece.grab", 0.2);
hand.value = element;
hand.value.style.position = 'absolute';
}
async function onPieceDrop(_: HTMLElement, type: PieceType) {
if (hand.value === undefined) {
return;
}
const cells = getCells();
const underlyingCell = closest(cells, hand.value);
const destination = locateCell(underlyingCell);
hand.value.style.position = '';
hand.value.style.top = '';
hand.value.style.left = '';
board.drop(destination, type);
jukebox.play("piece.drop", 0.3);
fadeCells();
hand.value = undefined;
}
</script>

<template>
<div class="inventory">
<div class="inventory-cell" v-for="type of TYPES.filter(type => !type.promoted)">
<div class="inventory-slot" v-for="slot of board.getInventoryOf(player).slots">
<DraggablePiece
:key="generateUUIDv4()"
:kanji="type.kanji"
:promoted="type.promoted"
:grabbable="player === board.player"
:type="slot.type"
:grabbable="player === board.player && slot.count !== 0"
:enemy="player === 'gote'"
@drop="onPieceDrop"
@grab="onPieceGrab"
state="idle"
></DraggablePiece>
<span class="inventory-slot-count">{{ slot.count }}</span>
</div>
</div>
</template>
Expand All @@ -36,4 +109,20 @@ const board = useBoard();
background: $gray
flex-direction: column
gap: 1em
.inventory-slot
display: flex
align-items: center
justify-content: center
.inventory-slot-count
color: white
background: $background
width: 1.5em
aspect-ratio: 1 / 1
text-align: center
border-radius: 3em
margin-top: auto
margin-left: -1em
z-index: 1
</style>
Loading

0 comments on commit 33af876

Please sign in to comment.