diff --git a/TODO.md b/TODO.md index 1460a2f..8f53db3 100644 --- a/TODO.md +++ b/TODO.md @@ -79,6 +79,22 @@ - Selected - Cant select - Stack Top view + +back + idle + hover + click + flip + drag + +front + idle + wait + hover + click + flip + drag + - Stock - Deck Stack @@ -99,3 +115,6 @@ - card to foundation highlight anim ~ several design settings + + + diff --git a/_pack.js b/_pack.js index a6a1212..39ae114 100644 --- a/_pack.js +++ b/_pack.js @@ -3,7 +3,7 @@ import { ImageSave, Rect, Packer, aseprite } from 'aset' export default async function pack() { - let packer = new Packer() + let packer = new Packer(4) let sprites = [] @@ -48,10 +48,10 @@ function ase_files(folder) { .map(file => new Promise(_resolve => { fs.readFile([folder, file].join('/'), (err, data) => { if (err) { - console.error(err) - return + throw err } let name = file.split('.')[0] + console.log(name, data.length) _resolve({ name, ase: aseprite(data)}) }) }))).then(resolve) diff --git a/content.js b/content.js index cc3d4b6..e24b58c 100644 --- a/content.js +++ b/content.js @@ -11,7 +11,7 @@ const ase_content = () => { chokidar.watch(['./content/sprites/*.ase' -], { ignoreInitial: true }) +], { ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 500 } }) .on('all', (event, path) => ase_content()) ase_content() diff --git a/content/fx/click_card.ase b/content/fx.ase similarity index 100% rename from content/fx/click_card.ase rename to content/fx.ase diff --git a/content/out_0.json b/content/out_0.json index da58297..3765004 100755 --- a/content/out_0.json +++ b/content/out_0.json @@ -1 +1 @@ -{"sprites":[{"name":"click_card","tags":[{"from":0,"to":3,"name":"idle"}],"packs":[{"frame":{"x":0,"y":0,"w":64,"h":64},"packed":{"x":444,"y":1065,"w":64,"h":64}},{"frame":{"x":0,"y":0,"w":64,"h":64},"packed":{"x":508,"y":1065,"w":64,"h":64}},{"frame":{"x":0,"y":0,"w":64,"h":64},"packed":{"x":572,"y":1065,"w":64,"h":64}},{"frame":{"x":0,"y":0,"w":64,"h":64},"packed":{"x":636,"y":1065,"w":64,"h":64}}]},{"name":"card","tags":[{"from":0,"to":0,"name":"idle"},{"from":1,"to":1,"name":"hover"},{"from":2,"to":2,"name":"click"},{"from":3,"to":3,"name":"back"},{"from":4,"to":6,"name":"flip"}],"packs":[{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":480,"y":520,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":684,"y":520,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":0,"y":820,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":204,"y":820,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":408,"y":820,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":612,"y":820,"w":204,"h":245}},{"frame":{"x":0,"y":0,"w":204,"h":245},"packed":{"x":0,"y":1065,"w":204,"h":245}}]},{"name":"main_card_bg","tags":[{"from":0,"to":0,"name":"idle"}],"packs":[{"frame":{"x":0,"y":0,"w":420,"h":520},"packed":{"x":0,"y":0,"w":420,"h":520}}]},{"name":"menu_bar","tags":[{"from":0,"to":0,"name":"idle"}],"packs":[{"frame":{"x":0,"y":0,"w":240,"h":160},"packed":{"x":204,"y":1065,"w":240,"h":160}}]},{"name":"recycle","tags":[{"from":0,"to":0,"name":"idle"},{"from":1,"to":3,"name":"hover"}],"packs":[{"frame":{"x":0,"y":0,"w":240,"h":300},"packed":{"x":420,"y":0,"w":240,"h":300}},{"frame":{"x":0,"y":0,"w":240,"h":300},"packed":{"x":660,"y":0,"w":240,"h":300}},{"frame":{"x":0,"y":0,"w":240,"h":300},"packed":{"x":0,"y":520,"w":240,"h":300}},{"frame":{"x":0,"y":0,"w":240,"h":300},"packed":{"x":240,"y":520,"w":240,"h":300}}]}]} \ No newline at end of file +{"sprites":[{"name":"card","tags":[{"from":0,"to":0,"name":"idle"},{"from":1,"to":1,"name":"hover"},{"from":2,"to":4,"name":"click"},{"from":5,"to":7,"name":"flip"},{"from":8,"to":8,"name":"back_idle"},{"from":9,"to":9,"name":"back_hover"},{"from":10,"to":10,"name":"back_click"},{"from":11,"to":13,"name":"back_flip"},{"from":14,"to":15,"name":"drag"},{"from":16,"to":17,"name":"back_drag"},{"from":18,"to":18,"name":"wait"},{"from":19,"to":19,"name":"shadow"}],"packs":[{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":248,"y":528,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":460,"y":528,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":672,"y":528,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":884,"y":528,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":1096,"y":528,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":0,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":212,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":424,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":636,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":848,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":1060,"y":836,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":0,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":212,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":424,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":636,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":848,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":1060,"y":1089,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":0,"y":1342,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":212,"y":1342,"w":212,"h":253}},{"frame":{"x":4,"y":4,"w":204,"h":245},"packed":{"x":424,"y":1342,"w":212,"h":253}}]},{"name":"main_card_bg","tags":[{"from":0,"to":0,"name":"idle"}],"packs":[{"frame":{"x":4,"y":4,"w":420,"h":520},"packed":{"x":0,"y":0,"w":428,"h":528}}]},{"name":"menu_bar","tags":[{"from":0,"to":0,"name":"idle"}],"packs":[{"frame":{"x":4,"y":4,"w":240,"h":160},"packed":{"x":636,"y":1342,"w":248,"h":168}}]},{"name":"recycle","tags":[{"from":0,"to":0,"name":"idle"},{"from":1,"to":3,"name":"hover"}],"packs":[{"frame":{"x":4,"y":4,"w":240,"h":300},"packed":{"x":428,"y":0,"w":248,"h":308}},{"frame":{"x":4,"y":4,"w":240,"h":300},"packed":{"x":676,"y":0,"w":248,"h":308}},{"frame":{"x":4,"y":4,"w":240,"h":300},"packed":{"x":924,"y":0,"w":248,"h":308}},{"frame":{"x":4,"y":4,"w":240,"h":300},"packed":{"x":0,"y":528,"w":248,"h":308}}]},{"name":"suit","tags":[{"from":0,"to":0,"name":"spades"},{"from":1,"to":1,"name":"hearts"},{"from":2,"to":2,"name":"diamonds"},{"from":3,"to":3,"name":"clubs"}],"packs":[{"frame":{"x":4,"y":4,"w":64,"h":64},"packed":{"x":884,"y":1342,"w":72,"h":72}},{"frame":{"x":4,"y":4,"w":64,"h":64},"packed":{"x":956,"y":1342,"w":72,"h":72}},{"frame":{"x":4,"y":4,"w":64,"h":64},"packed":{"x":1028,"y":1342,"w":72,"h":72}},{"frame":{"x":4,"y":4,"w":64,"h":64},"packed":{"x":1100,"y":1342,"w":72,"h":72}}]}]} \ No newline at end of file diff --git a/content/out_0.png b/content/out_0.png index e725c4a..8bafae7 100755 Binary files a/content/out_0.png and b/content/out_0.png differ diff --git a/content/sprites/card.ase b/content/sprites/card.ase index eb614a0..9b6ca11 100755 Binary files a/content/sprites/card.ase and b/content/sprites/card.ase differ diff --git a/content/sprites/suit.ase b/content/sprites/suit.ase new file mode 100755 index 0000000..1da3cea Binary files /dev/null and b/content/sprites/suit.ase differ diff --git a/screens/concept.ase b/screens/concept.ase new file mode 100755 index 0000000..16d78be Binary files /dev/null and b/screens/concept.ase differ diff --git a/src/game.ts b/src/game.ts index eac6878..4524b87 100644 --- a/src/game.ts +++ b/src/game.ts @@ -80,8 +80,9 @@ class MainTitle extends Play { type ClickableData = { debug?: true, rect: Rect, - on_hover?: () => void, + on_hover?: () => boolean, on_hover_end?: () => void, + on_click_begin?: () => void, on_click?: () => void, on_drag_begin?: (e: Vec2) => void, on_drag_end?: (e: Vec2) => void, @@ -109,6 +110,18 @@ export class Clickable extends Play { let _hovering = false let self = this this.unbindable_input({ + on_click_begin(_e: EventPosition, right: boolean) { + if (right) { + return false + } + let e = _e.mul(Game.v_screen) + let point = Rect.make(e.x - 4, e.y - 4, 8, 8) + let rect = Rect.make(self.g_position.x, self.g_position.y, self.width, self.height) + if (rect.overlaps(point)) { + return self.data.on_click_begin?.() || false + } + return false + }, on_drag(d: DragEvent, d0?: DragEvent) { if (d._right) { return false @@ -166,7 +179,7 @@ export class Clickable extends Play { if (rect.overlaps(point)) { if (!_hovering) { _hovering = true - self.data.on_hover?.() + return self.data.on_hover?.() || false } } else { if (_hovering) { @@ -174,7 +187,13 @@ export class Clickable extends Play { self.data.on_hover_end?.() } } - return false + return _hovering + }, + on_hover_clear() { + if (_hovering) { + _hovering = false + self.data.on_hover_end?.() + } }, on_click(_e: EventPosition, right: boolean) { let e = _e.mul(Game.v_screen) diff --git a/src/input.ts b/src/input.ts index 1ff6a04..5392c50 100644 --- a/src/input.ts +++ b/src/input.ts @@ -12,8 +12,10 @@ export type DragEvent = { export type Hooks = { priority?: number, on_hover?: (e: EventPosition) => boolean, + on_hover_clear?: () => void, on_up?: (e: EventPosition, right: boolean, m?: Vec2) => boolean, on_click?: (e: EventPosition, right: boolean) => boolean, + on_click_begin?: (e: EventPosition, right: boolean) => boolean, on_drag?: (d: DragEvent, d0?: DragEvent) => boolean, on_context?: () => boolean } @@ -42,7 +44,14 @@ class Input { } _on_hover(e: EventPosition) { - this.hooks.find(_ => _.on_hover?.(e)) + let hooks = this.hooks + + let j = hooks.findIndex(_ => _.on_hover?.(e)) + if (j !== -1) { + for (let i = j + 1; i < this.hooks.length; i++) { + hooks[i].on_hover_clear?.() + } + } } _on_up(e: EventPosition, right: boolean, m?: EventPosition) { this.hooks.find(_ => _.on_up?.(e, right, m)) @@ -50,6 +59,9 @@ class Input { _on_click(e: EventPosition, right: boolean) { this.hooks.find(_ => _.on_click?.(e, right)) } + _on_click_begin(e: EventPosition, right: boolean) { + this.hooks.find(_ => _.on_click_begin?.(e, right)) + } _on_drag(d: DragEvent, d0?: DragEvent) { this.hooks.find(_ => _.on_drag?.(d, d0)) } @@ -108,6 +120,8 @@ class Input { self._on_update = undefined } } + + self._on_click_begin(e, _right) }, _onDragMove(_e) { let e = map_e(_e) diff --git a/src/showcase.ts b/src/showcase.ts index 1e086b2..ea7b956 100644 --- a/src/showcase.ts +++ b/src/showcase.ts @@ -15,49 +15,525 @@ import { bg1, link_color, Play, PlayType} from './play' import { ticks } from './shared' import { Anim } from './anim' +import { Tween } from './tween' import { Text, RectView, Clickable, Background, MainMenu } from './game' +import { Button } from './ui' +import { Suit, Cards as OCards, CardPov, hidden_card } from 'lsolitaire' + +import { arr_random } from './util' + +type DragHook = (e: Vec2) => void +type DropHook = () => void + +const suit_long: Record = { 's': 'spades', 'd': 'diamonds', 'h': 'hearts', 'c': 'clubs' } export class Card extends Play { + _card!: CardPov + + get waiting() { + return this._card === hidden_card + } + + set card(card: CardPov) { + this._card = card + this.suit.play_now(suit_long[card[0] as Suit]) + if (this.waiting) { + if (this.anim._animation === 'idle') { + this.anim.play('wait') + this.suit.visible = false + } + } else { + if (this.anim._animation === 'wait') { + this.anim.play('idle') + this.suit.visible = true + } + } + } + + + get easing() { + return !!this._tx || !!this._ty || !!this._tr + } + + _will_lerp_t?: number + _will_lerp_position?: Vec2 + lerp_position(v?: Vec2, t?: number) { + + if (this._tx) { + this.cancel(this._tx) + this._tx = undefined + } + if (this._ty) { + this.cancel(this._ty) + this._ty = undefined + } + + this._will_lerp_position = v + this._will_lerp_t = t + this._target_speed = (1-(t || 0.5)) * 0.1 + } + + _dragging!: boolean + + get drag_decay() { + return this._drag_decay + } + _drag_decay: Vec2 = Vec2.zero + _on_drag?: DragHook + bind_drag(e: DragHook) { + this._on_drag = e + } + + _on_drop?: DropHook + bind_drop(e?: DropHook) { + this._on_drop = e + } + + _on_hover?: [DropHook, DropHook] + bind_hover(e?: [DropHook, DropHook]) { + this._on_hover = e + } + + facing!: number + + suit!: Anim anim!: Anim - fx!: Array + shadow!: Anim + + _will_hover!: boolean + _will_hover_end!: boolean + _will_flip_back!: boolean + _will_flip_front!: boolean + + lerp_hover_y!: number + + _tr?: Tween + _tx?: Tween + _ty?: Tween + + _target_speed!: number + _speed!: number + + _hover_time?: number + get hover_time() { + return this._hover_time ?? 0 + } + + ease_position(v: Vec2, duration: number = ticks.half) { + if (v.equals(this.position)) { + return + } + this._target_speed = (duration / ticks.half) * 0.2 + this._tx = this.tween_single(this._tx, [this.position.x, v.x], (v) => { + this.position.x = v + }, duration, 0, () => { + this._tx = undefined + this._target_speed = 0 + }) + + this._ty = this.tween_single(this._ty, [this.position.y, v.y], (v) => { + this.position.y = v + }, duration, 0, () => { this._ty = undefined }) + } + + _init() { + this._card = hidden_card + + this.shadow = this.make(Anim, Vec2.make(0, 0), { name: 'card' }) + this.shadow.origin = Vec2.make(88, 120) + this.shadow.play_now('shadow') + this.anim = this.make(Anim, Vec2.make(0, 0), { name: 'card' }) this.anim.origin = Vec2.make(88, 120) + this.facing = -1 + this.anim.play_now('back_idle') + + this.suit = this.make(Anim, Vec2.make(-44, -86), { name: 'suit' }) + this.suit.origin = Vec2.make(32, 32) + this.suit.play_now('spades') + this.suit.scale = Vec2.make(0.6, 0.6) + this.suit.visible = false + + this._will_hover = false + this._will_hover_end = false + + this._will_flip_back = false + this._will_flip_front = false + + this.lerp_hover_y = 0 + + this._dragging = false + + this._speed = 0 + this._target_speed = 0 + + let self = this + this.make(Clickable, Vec2.make(16, 16).sub(this.anim.origin), { + rect: Rect.make(0, 0, 170, 210), + on_hover() { + if (self._on_hover) { + self._on_hover[0]() + } + if (self._on_drag) { + self._will_hover = true + return true + } + return false + }, + on_hover_end() { + if (self._on_hover) { + self._on_hover[1]() + } + self._will_hover_end = true + }, + on_drag_begin(e: Vec2) { + if (self._on_drag) { + self._dragging = true + self._drag_decay = e.sub(self.position) + } + }, + on_drag_end() { + self._dragging = false + }, + on_drag(e: Vec2) { + if (self._on_drag) { + self._on_drag(e) + } + }, + on_drop() { + if (self._on_drop) { + self._on_drop() + } + } + }) + + } + + _update() { + + this._speed = lerp(this._speed, this._target_speed, 0.2) + let n = ease(Math.abs(Math.sin(Time.seconds * 3))) * this._speed + this.scale = Vec2.make(1-n, 1+n) + + + if (this._will_lerp_position) { + this.position = Vec2.lerp(this.position, this._will_lerp_position, this._will_lerp_t ?? 0.5) + } + + + + this.anim.position.y = lerp(this.anim.position.y, this.lerp_hover_y, 0.2) + this.suit.position.y = lerp(this.suit.position.y, this.lerp_hover_y - 86, 0.16) + + if (this._will_hover) { + this._will_hover = false + this.anim.play(this.facing === 1 ? 'hover' : 'back_hover') + this.lerp_hover_y = -6 + this._hover_time = 0 + } + if (this._will_hover_end) { + this._will_hover_end = false + let idle_wait = this.waiting ? 'wait': 'idle' + this.anim.play(this.facing === 1 ? idle_wait: 'back_idle') + this.lerp_hover_y = 0 + this._hover_time = undefined + } + + if (this._hover_time !== undefined && this._hover_time >= 0) { + this._hover_time += Time.delta + } + + if (this._will_flip_back) { + if (!this.easing) { + this._will_flip_back = false + this.shadow.visible = false + this.suit.visible = false + this.anim.play('flip', () => { + this.facing = -1 + this.anim.play('back_idle') + this.shadow.visible = true + }) + } + } + + if (this._will_flip_front) { + if (!this.easing) { + this._will_flip_front = false + this.shadow.visible = false + this.suit.visible = false + this.anim.play('back_flip', () => { + this.facing = 1 + this.anim.play(this.waiting ? 'wait' : 'idle') + this.shadow.visible = true + + if (!this.waiting) { + this.suit.visible = true + } + }) + } + } } - hover() { - this.anim.play('hover') + flip_back() { + this._will_flip_back = this.facing !== -1 } - hover_end() { - this.anim.play('idle') + flip_front() { + this._will_flip_front = this.facing !== 1 } +} + + + + +export class Cards extends Play { + + frees!: Array + used!: Array - click() { - this.routine(this._click()) + + borrow() { + let card = this.frees.shift()! + this.used.push(card) + + card.visible = true + return card + } + + release(card: Card) { + card.visible = false + this.used.splice(this.used.indexOf(card), 1) + this.frees.push(card) } - *_click() { - this.anim.play('click') - yield * this.wait_for(ticks.half) - this.anim.play('idle') + + _init() { + this.frees = OCards.deck.map(card => { + let _ = this.make(Card, Vec2.zero, {}) + _.visible = false + return _ + }) + + this.used = [] } +} + +type StackData = { + h?: number } +class Stack extends Play { + get data() { + return this._data as StackData + } -export class CardShowcase extends Play { + get length() { + return this.cards.length + } + + get top_card() { + return this.cards[this.cards.length - 1] + } + + get h() { + let n = 55 * (1 - this.cards.length / 50) + return this.data.h ?? n + } + + get top_position() { + return this.position.add(Vec2.make(0, this.cards.length * this.h)) + } + + add_cards(cards: Array) { + this.cards.push(...cards) + this._reposition() + } + + remove_cards(n: number) { + let cards = this.cards.splice(-n) + this._reposition() + return cards + } + + _i_gap?: number + set i_gap(_: number | undefined) { + if (this._i_gap === _) { + return + } + this._i_gap = _ + this._reposition() + } + + _reposition() { + this.cards.forEach((card, i) => { + let i_gap = (this._i_gap !== undefined && i > this._i_gap) ? i + 0.5 : i + if (card.easing) { + return + } + card.ease_position(this.p_position.add(Vec2.make(0, i_gap * this.h))) + }) + } + + ease_position(v: Vec2) { + this.position = v + this._reposition() + } + + cards!: Array + + + _init() { + this.cards = [] + } + + +} + +function sigmoid(x: number) { + return 1 / (1 + Math.exp(-x)); +} + +class DragStack extends Play { + + get waiting() { + return this._waiting + } + _waiting: boolean = false + wait_drop() { + this._waiting = true + } + + _cards!: Array + set cards(cards: Array) { + this._cards = cards + this._cards.forEach(_ => _.send_front()) + } - hovering!: Card - clicking!: Card + get drag_decay() { + return this._cards[0].drag_decay + } + + get h() { + return 50 + } + + drag(v: Vec2) { + this._cards.forEach((_, i) => { + let _v = v.add(Vec2.make(0, this.h * i).sub(this.drag_decay)) + let t = 1-sigmoid((i/this._cards.length) * 2) + _.lerp_position(_v, t) + }) + } - i_clicking = 0 + release() { + let cards = this._cards.splice(0) + + cards.forEach(_=> _.lerp_position()) + return cards + } + + _init() { + this._cards = [] + } + +} + + + +type TableuData = { + on_front_drag: (i: number, v: Vec2) => void, + on_front_drop: () => void +} + +export class Tableu extends Play { + + get data() { + return this._data as TableuData + } + + get top_front_position() { + return this.fronts.top_position + } + + get top_back_position() { + return this.backs.top_position + } + + add_backs(cards: Array) { + cards.forEach(_ => _.flip_back()) + this.backs.add_cards(cards) + this.fronts.ease_position(this.top_back_position) + } + + add_fronts(cards: Array) { + + this.fronts.add_cards(cards) + cards.forEach(_ => _.flip_front()) + + let self = this + let l = this.fronts.cards.length + this.fronts.cards.forEach((_, i) => { + _.bind_drop(undefined) + _.bind_drag((e: Vec2) => { + self.data.on_front_drag(l - i, e) + }) + }) + this.fronts.top_card.bind_drop(() => { + self.data.on_front_drop() + }) + } + + remove_fronts(i: number) { + let cards = this.fronts.remove_cards(i) + cards.forEach(_ => { + _.bind_drop(undefined) + }) + let self = this + let l = this.fronts.cards.length + this.fronts.cards.forEach((_, i) => { + _.bind_drop(undefined) + _.bind_drag((e: Vec2) => { + self.data.on_front_drag(l - i, e) + }) + }) + this.fronts.top_card?.bind_drop(() => { + self.data.on_front_drop() + }) + return cards + } + + backs!: Stack + fronts!: Stack + + _init() { + this.backs = this.make(Stack, Vec2.make(0, 0), { h: 33 }) + this.fronts = this.make(Stack, Vec2.make(0, 0), {}) + } + + + _update() { + let i = this.fronts.cards.findIndex(_ => _.hover_time > ticks.half) + if (i !== -1) { + this.fronts.i_gap = i + } else { + this.fronts.i_gap = undefined + } + } +} + + +export class CardShowcase extends Play { + + dragging?: DragStack + card!: Card _init() { @@ -67,39 +543,159 @@ export class CardShowcase extends Play { let c_off = Vec2.make(100, 200) let w = 300 - this.make(Text, Vec2.make(200, 200), { - text: 'idle' + let h = 100 + this.make(Text, Vec2.make(100, 200), { + text: 'front' }) - _ = this.make(Card, Vec2.make(200, 200).add(c_off), {}) + - this.make(Text, Vec2.make(200 + w, 200), { - text: 'hover' + this.make(Text, Vec2.make(100, 200 + h), { + text: ' - wait' }) - this.hovering = this.make(Card, Vec2.make(200 + w, 200).add(c_off), {}) - this.routine(this._hover()) - this.make(Text, Vec2.make(200 + w * 2, 200), { - text: 'click' + this.make(Text, Vec2.make(100, 200 + h * 2), { + text: ' - idle' }) - this.clicking = this.make(Card, Vec2.make(200 + w * 2, 200).add(c_off), {}) - this.routine(this._click()) - } + this.make(Text, Vec2.make(100, 200 + h * 3), { + text: ' - hover' + }) + + this.make(Text, Vec2.make(100, 200 + h * 4), { + text: ' - click' + }) + + this.make(Text, Vec2.make(100, 200 + h * 5), { + text: ' - flip' + }) + + this.make(Text, Vec2.make(450, 200), { + text: 'back' + }) + + this.make(Text, Vec2.make(450, 200 + h * 2), { + text: ' - idle' + }) + + this.make(Text, Vec2.make(450, 200 + h * 3), { + text: ' - hover' + }) + + this.make(Text, Vec2.make(450, 200 + h * 4), { + text: ' - click' + }) + + this.make(Text, Vec2.make(450, 200 + h * 5), { + text: ' - flip' + }) + + let self = this + this.make(Button, Vec2.make(400, 200 + h * 7), { + text: 'flip', + w: 200, + h: 100, + on_click() { + if (self.card.facing === 1) { + self.card.flip_back() + } else { + self.card.flip_front() + } + } + }) + + this.make(Button, Vec2.make(80, 200 + h * 7), { + text: 'set card', + w: 300, + h: 100, + on_click() { + if (self.card._card === hidden_card) { + self.card.card = arr_random(OCards.deck) + } else { + self.card.card = hidden_card + } + } + }) + + let cards = this.make(Cards, Vec2.make(500, 0), {}) + + + this.card = cards.borrow() + this.card.ease_position(Vec2.make(500, 500)) + this.card.bind_drag(() => {}) + + this.make(Clickable, Vec2.make(0, 0), { + rect: Rect.make(0, 0, 0, 0), + on_up() { + if (self.dragging) { + let cards = self.dragging.release() + tableu3.add_fronts(cards) + self.dragging = undefined + } + + } + }) + + let tableu = this.make(Tableu, Vec2.make(1000, 500), { + + on_front_drag(i: number, v: Vec2) { + if (self.dragging) { + self.dragging.drag(v) + } else { + let cards = tableu.remove_fronts(i) + self.dragging = self.make(DragStack, Vec2.zero, {}) + self.dragging.cards = cards + } + }, + on_front_drop() { + } + }) + tableu.add_backs([...Array(5).keys()].map(() => cards.borrow())) + tableu.add_fronts([...Array(12).keys()].map(() => cards.borrow())) + tableu.fronts.cards.forEach(_ => _.card = arr_random(OCards.deck)) + + + + + let tableu2 = this.make(Tableu, Vec2.make(1000 - 200, 500), { + + on_front_drag(i: number, v: Vec2) { + if (self.dragging) { + self.dragging.drag(v) + } else { + let cards = tableu2.remove_fronts(i) + self.dragging = self.make(DragStack, Vec2.zero, {}) + self.dragging.cards = cards + } + }, + on_front_drop() { + } + }) + tableu2.add_backs([...Array(5).keys()].map(() => cards.borrow())) + tableu2.add_fronts([...Array(5).keys()].map(() => cards.borrow())) + tableu2.fronts.cards.forEach(_ => _.card = arr_random(OCards.deck)) + + + let tableu3 = this.make(Tableu, Vec2.make(1000 + 200, 500), { + + on_front_drag(i: number, v: Vec2) { + if (self.dragging) { + self.dragging.drag(v) + } else { + let cards = tableu3.remove_fronts(i) + self.dragging = self.make(DragStack, Vec2.zero, {}) + self.dragging.cards = cards + } + }, + on_front_drop() { + } + }) + tableu3.add_backs([...Array(5).keys()].map(() => cards.borrow())) + tableu3.add_fronts([...Array(2).keys()].map(() => cards.borrow())) + tableu3.fronts.cards.forEach(_ => _.card = arr_random(OCards.deck)) + + - *_hover(): Generator { - this.hovering.hover() - yield * this.wait_for(ticks.half) - this.hovering.hover_end() - yield * this.wait_for(ticks.seconds) - yield * this._hover() - } - *_click(): Generator { - this.clicking.hover() - yield * this.wait_for(ticks.sixth) - this.clicking.click() - yield * this.wait_for(ticks.seconds) - yield * this._click() } _update() { diff --git a/src/ui.ts b/src/ui.ts new file mode 100644 index 0000000..1c3f305 --- /dev/null +++ b/src/ui.ts @@ -0,0 +1,55 @@ +import { TextureFilter, TextureSampler } from 'blah' +import { Color } from 'blah' +import { Rect, Vec2, Mat3x2 } from 'blah' +import { Time, App, batch, Batch, Target } from 'blah' + +import Content from './content' +import Input, { Hooks, EventPosition, DragEvent } from './input' +import { howtos } from './howtos' +import { Transition, transition } from './transition' + +import { rmap, ease, lerp, appr } from './lerp' +import { InfiniteScrollableList } from './scrollable' + +import { bg1, link_color, Play, PlayType} from './play' + +import { ticks } from './shared' +import { Anim } from './anim' + +import { Text, RectView, Clickable, Background, MainMenu } from './game' + +type ButtonData = { + text: string, + w: number, + h: number, + on_click: () => void +} + +export class Button extends Play { + + get data() { + return this._data as ButtonData + } + + _init() { + + let r = this.make(RectView, Vec2.make(0, 0), { + w: this.data.w, + h: this.data.h, + color: Color.black + }) + + let _ = this.make(Text, Vec2.make(this.data.w / 2, 0), { + text: this.data.text, + center: true + }) + + let self = this + this.make(Clickable, Vec2.make(4, 4), { + rect: Rect.make(0, 0, this.data.w - 8, this.data.h - 8), + on_click() { + self.data.on_click() + } + }) + } +} diff --git a/src/util.ts b/src/util.ts index e14485a..b8b9141 100644 --- a/src/util.ts +++ b/src/util.ts @@ -20,8 +20,8 @@ export function rnd_h(rng: RNG = random) { return rng() * 2 - 1 } -export function arr_random(arr: Array) { - return arr[int_random(arr.length)] +export function arr_random(arr: Array, rng: RNG = random) { + return arr[int_random(arr.length, rng)] } export const v_random = (rng: RNG = random) => Vec2.make(rng(), rng())