mirror of
https://github.com/Aviortheking/games.git
synced 2025-08-11 21:51:58 +00:00
4
src/client/styl/stylus.d.ts
vendored
Normal file
4
src/client/styl/stylus.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
declare module '*.styl' {
|
||||
const content: any
|
||||
export = content
|
||||
}
|
13
src/pages/_app.tsx
Normal file
13
src/pages/_app.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import App from 'next/app'
|
||||
|
||||
import '@dzeio/components/style.css'
|
||||
|
||||
export default class CApp extends App {
|
||||
|
||||
public render() {
|
||||
const { Component, pageProps } = this.props
|
||||
|
||||
return(<Component {...pageProps} />)
|
||||
}
|
||||
}
|
19
src/pages/_document.tsx
Normal file
19
src/pages/_document.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
class MyDocument extends Document {
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
13
src/pages/index.tsx
Normal file
13
src/pages/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Link, Text } from '@dzeio/components'
|
||||
import React from 'react'
|
||||
|
||||
export default class Index extends React.Component {
|
||||
|
||||
public render = () => (
|
||||
<main>
|
||||
<Text>
|
||||
<Link href="/pokemon-shuffle">Pokémon Shuffle</Link>
|
||||
</Text>
|
||||
</main>
|
||||
)
|
||||
}
|
292
src/pages/pokemon-shuffle/index.tsx
Normal file
292
src/pages/pokemon-shuffle/index.tsx
Normal file
@@ -0,0 +1,292 @@
|
||||
import { Button, Table, Text, Util } from '@dzeio/components'
|
||||
import React from 'react'
|
||||
import css from './pokemon-shuffle.styl'
|
||||
|
||||
interface Cell {
|
||||
id: number
|
||||
horizontalCombo?: true
|
||||
verticalCombo?: true
|
||||
justSpawned?: true
|
||||
isFalling?: true
|
||||
}
|
||||
|
||||
interface States {
|
||||
items: Array<Array<Cell | undefined>>
|
||||
loading?: true
|
||||
movingItem?: {x: number, y: number, cell: Cell}
|
||||
points: number
|
||||
turn: number
|
||||
combo: number
|
||||
comboMax: number
|
||||
cursorPos: {x: number, y: number}
|
||||
}
|
||||
|
||||
const ITEM_COUNT = 4
|
||||
const BOARD_SIZE = 6
|
||||
let n = BOARD_SIZE
|
||||
|
||||
export default class PokemonShuffle extends React.Component<unknown, States> {
|
||||
|
||||
public state: States = {
|
||||
items: [[]],
|
||||
points: 0,
|
||||
turn: 0,
|
||||
combo: 0,
|
||||
comboMax: 0,
|
||||
cursorPos: {x: 0, y: 0}
|
||||
}
|
||||
|
||||
public render = () => (
|
||||
<main>
|
||||
<ul>
|
||||
<li><Text>Tour: {this.state.turn}</Text></li>
|
||||
<li><Text>Combo: {this.state.combo}, Max: {this.state.comboMax}</Text></li>
|
||||
<li><Text>Points: {this.state.points}</Text></li>
|
||||
</ul>
|
||||
<Table >
|
||||
<tbody className={`${css.table} ${this.state.loading ? css.loading : ''}`}>
|
||||
{this.state.items.map((row, y) => (
|
||||
<tr key={y}>
|
||||
{row.map((cell, x) => (
|
||||
<td
|
||||
key={cell?.isFalling ? n++ : x}
|
||||
onClick={this.onCellClick(x, y)}
|
||||
className={css.cellParent}
|
||||
>
|
||||
{cell && (
|
||||
<Text className={Util.buildClassName(
|
||||
css[`icon-${cell.id}`],
|
||||
css.cell,
|
||||
[css.isFalling, cell.isFalling],
|
||||
[css.justSpawned, cell.justSpawned],
|
||||
[css.explode, cell.horizontalCombo || cell.verticalCombo]
|
||||
)}>
|
||||
<div></div>
|
||||
</Text>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<Button onClick={() => this.calculate()}>Calculate!</Button>
|
||||
<Button onClick={() => this.start()}>Start!</Button>
|
||||
{/* <Input block type="textarea" value={JSON.stringify(this.state.items)}/> */}
|
||||
{this.state.movingItem && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
left: this.state.cursorPos.x,
|
||||
top: this.state.cursorPos.y,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
pointerEvents: 'none'
|
||||
}}>
|
||||
<Text className={Util.buildClassName(css[`icon-${this.state.movingItem.cell?.id}`], css.cell)}>
|
||||
<div></div>
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
<Text>
|
||||
TODO list:
|
||||
</Text>
|
||||
<ul>
|
||||
<li><Text>Lancement Initial sans combo possible</Text></li>
|
||||
<li><Text>Meilleurs Animation de destruction</Text></li>
|
||||
<li><Text>Annuler le mouvement si rien n'est claim</Text></li>
|
||||
</ul>
|
||||
</main>
|
||||
)
|
||||
|
||||
private mouveMove = (ev: MouseEvent) => {
|
||||
this.setState({cursorPos: {
|
||||
x: ev.pageX,
|
||||
y: ev.pageY
|
||||
}})
|
||||
}
|
||||
|
||||
private start() {
|
||||
if (this.state.loading) {return}
|
||||
this.setState({
|
||||
loading: true,
|
||||
items: Array
|
||||
.from(Array(BOARD_SIZE))
|
||||
.map(
|
||||
() => Array.from(Array(BOARD_SIZE))
|
||||
.map(() => ({id: random(0, ITEM_COUNT)}))
|
||||
)
|
||||
}, () => this.calculate())
|
||||
}
|
||||
|
||||
private onCellClick = (x: number, y: number) => async () => {
|
||||
// console.log(x, y)
|
||||
if (this.state.loading) {
|
||||
return window.alert('Cant play while Calculating')
|
||||
}
|
||||
if (!this.state.movingItem) {
|
||||
const cell = this.state.items[y][x]
|
||||
if (!cell) {
|
||||
return window.alert('Cant move nothing')
|
||||
}
|
||||
document.addEventListener('mousemove', this.mouveMove)
|
||||
this.setState({movingItem: {x,y,cell}})
|
||||
this.state.items[y][x] = undefined
|
||||
return
|
||||
} else {
|
||||
document.removeEventListener('mousemove', this.mouveMove)
|
||||
const items = this.state.items
|
||||
const temp = items[y][x]
|
||||
items[y][x] = this.state.movingItem.cell
|
||||
items[this.state.movingItem.y][this.state.movingItem.x] = temp
|
||||
this.setState({
|
||||
movingItem: undefined,
|
||||
loading: true,
|
||||
items
|
||||
}, () => this.calculate())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private asyncSetState = (states: Partial<States>) => new Promise<void>(
|
||||
(res) => this.setState(states as States, () => res())
|
||||
)
|
||||
|
||||
private async calculate() {
|
||||
const items = this.state.items.map((r) => r.map((c) => {
|
||||
if (!c) {
|
||||
return c
|
||||
}
|
||||
c.horizontalCombo = undefined
|
||||
c.verticalCombo = undefined
|
||||
return c
|
||||
}))
|
||||
|
||||
let newPoints = 0
|
||||
|
||||
let checkupCount = 0
|
||||
// Checkup horizontal
|
||||
for (let y = 0; y < items.length; y++) {
|
||||
const row = items[y]
|
||||
for (let x = 0; x < row.length; x++) {
|
||||
const cell = row[x]
|
||||
if (!cell || cell.horizontalCombo) {continue}
|
||||
const id = cell.id
|
||||
let sameCount = 0
|
||||
while((x + ++sameCount) < items.length) {
|
||||
console.log(y + sameCount, x)
|
||||
const tmp = row[x + sameCount]
|
||||
if (!tmp || tmp.id !== id) {break}
|
||||
}
|
||||
if (sameCount >= 3) {
|
||||
checkupCount += 1
|
||||
for (let i = x; i < (x + sameCount); i++) {
|
||||
const tmp = items[y][i]
|
||||
if (!tmp) {continue}
|
||||
tmp.horizontalCombo = true
|
||||
newPoints++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check vertical
|
||||
for (let y = 0; y < items.length; y++) {
|
||||
const row = items[y]
|
||||
for (let x = 0; x < row.length; x++) {
|
||||
const cell = row[x]
|
||||
if (!cell || cell.verticalCombo) {continue}
|
||||
const id = cell.id
|
||||
let sameCount = 0
|
||||
while((y + ++sameCount) < items.length) {
|
||||
// console.log(y + sameCount, x)
|
||||
const tmp = items[y + sameCount][x]
|
||||
if (!tmp || tmp.id !== id) {break}
|
||||
}
|
||||
// if ((y + sameCount) > items.length) {
|
||||
// sameCount++
|
||||
// }
|
||||
if (sameCount >= 3) {
|
||||
checkupCount += 1
|
||||
for (let i = y; i < (y + sameCount); i++) {
|
||||
const tmp = items[i][x]
|
||||
if (!tmp) {continue}
|
||||
tmp.verticalCombo = true
|
||||
|
||||
newPoints++
|
||||
}
|
||||
// console.log(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkupCount) {
|
||||
await this.asyncSetState({
|
||||
items,
|
||||
points: this.state.points + newPoints,
|
||||
combo: this.state.combo+checkupCount,
|
||||
comboMax: Math.max(this.state.comboMax, this.state.combo+checkupCount)
|
||||
})
|
||||
await new Promise((res) => setTimeout(res, 500))
|
||||
}
|
||||
|
||||
// return
|
||||
|
||||
// Clear items
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let y = 0; y < items.length; y++) {
|
||||
const row = items[y]
|
||||
for (let x = 0; x < row.length; x++) {
|
||||
const cell = row[x]
|
||||
if (!cell || (!cell.horizontalCombo && !cell.verticalCombo)) {continue}
|
||||
items[y][x] = undefined
|
||||
}
|
||||
}
|
||||
|
||||
let itemHasFallen = false
|
||||
let needNewTurn = false
|
||||
do {
|
||||
// Make items fall
|
||||
itemHasFallen = false
|
||||
for (let y = (items.length - 1); y >= 0; y--) {
|
||||
const row = items[y]
|
||||
for (let x = 0; x < row.length; x++) {
|
||||
const cell = row[x]
|
||||
if (cell) {
|
||||
cell.justSpawned = undefined
|
||||
cell.isFalling = undefined
|
||||
}
|
||||
if (cell && y+1 < row.length && !items[y+1][x]) {
|
||||
cell.isFalling = true
|
||||
needNewTurn = true
|
||||
itemHasFallen = true
|
||||
items[y+1][x] = cell
|
||||
items[y][x] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill to top lane
|
||||
for (let x = 0; x < items[0].length; x++) {
|
||||
const cell = items[0][x]
|
||||
if (!cell) {
|
||||
itemHasFallen = true
|
||||
items[0][x] = {id: random(0, ITEM_COUNT), justSpawned: true}
|
||||
}
|
||||
}
|
||||
if (itemHasFallen) {
|
||||
await this.asyncSetState({items})
|
||||
await new Promise((res) => setTimeout(res, 300))
|
||||
}
|
||||
} while (itemHasFallen)
|
||||
|
||||
// If an item has fallen re calculate
|
||||
if (needNewTurn) {
|
||||
this.setState({items}, () => this.calculate())
|
||||
return
|
||||
}
|
||||
this.setState({items, loading: undefined, turn: this.state.turn+1, combo: 0})
|
||||
}
|
||||
}
|
||||
|
||||
function random(min = 0, max = 100) {
|
||||
return Math.floor(Math.random() * (max - min) + min)
|
||||
}
|
129
src/pages/pokemon-shuffle/pokemon-shuffle.styl
Normal file
129
src/pages/pokemon-shuffle/pokemon-shuffle.styl
Normal file
@@ -0,0 +1,129 @@
|
||||
$iconSize = 121px
|
||||
|
||||
.table
|
||||
position relative
|
||||
transition filter .5s ease-in-out
|
||||
|
||||
&.loading .cell
|
||||
filter brightness(0.5)
|
||||
|
||||
.cellParent
|
||||
position relative
|
||||
width $iconSize
|
||||
padding 0 !important
|
||||
height $iconSize
|
||||
|
||||
.cell
|
||||
width $iconSize
|
||||
transition filter .5s ease-in-out
|
||||
height $iconSize
|
||||
background-image url('/assets/pokemon-shuffle/icons.png')
|
||||
animation idleAnimation ease-in-out 1s
|
||||
animation-iteration-count infinite
|
||||
transform-origin 50% 50%
|
||||
&.icon-0
|
||||
background-position (1*$iconSize) 0px
|
||||
&.icon-1
|
||||
background-position (2*$iconSize) 0px
|
||||
&.icon-2
|
||||
background-position (3*$iconSize) 0px
|
||||
&.icon-3
|
||||
background-position (4*$iconSize) 0px
|
||||
&.icon-4
|
||||
background-position (5*$iconSize) 0px
|
||||
&.icon-5
|
||||
background-position (6*$iconSize) 0px
|
||||
&.icon-6
|
||||
background-position (7*$iconSize) 0px
|
||||
&.icon-7
|
||||
background-position (8*$iconSize) 0px
|
||||
&.icon-8
|
||||
background-position (9*$iconSize) 0px
|
||||
&.icon-9
|
||||
background-position (10*$iconSize) 0px
|
||||
&.icon-10
|
||||
background-position (11*$iconSize) 0px
|
||||
|
||||
&.isFalling
|
||||
position absolute
|
||||
animation fallingAnimation linear .3s
|
||||
animation-iteration-count 1
|
||||
transform-origin 50% 50%
|
||||
animation-fill-mode forwards
|
||||
|
||||
&.explode
|
||||
animation destroyAnimation ease-in-out .5s
|
||||
animation-iteration-count 1
|
||||
transform-origin 50% 50%
|
||||
animation-fill-mode forwards
|
||||
|
||||
&::before
|
||||
content " "
|
||||
position absolute
|
||||
bottom 0
|
||||
width 100%
|
||||
height 100%
|
||||
border white solid 4px
|
||||
border-radius 100%
|
||||
|
||||
animation destroyCircleAnimation ease-in-out .3s
|
||||
animation-iteration-count 1
|
||||
transform-origin 50% 50%
|
||||
animation-fill-mode forwards
|
||||
|
||||
&.justSpawned
|
||||
animation spawnAnimation ease-in-out 0.5s
|
||||
animation-iteration-count 1
|
||||
transform-origin 50% 50%
|
||||
|
||||
@keyframes idleAnimation
|
||||
0%
|
||||
transform rotate(0)
|
||||
20%
|
||||
transform rotate(-10deg)
|
||||
40%
|
||||
transform rotate(10deg)
|
||||
60%
|
||||
transform rotate(-5deg)
|
||||
80%
|
||||
transform rotate(5deg)
|
||||
100%
|
||||
transform rotate(0)
|
||||
|
||||
@keyframes fallingAnimation
|
||||
from
|
||||
top -117px // revoir précisément
|
||||
to
|
||||
top 3px
|
||||
|
||||
@keyframes destroyCircleAnimation
|
||||
0%
|
||||
opacity 0
|
||||
transform scale(0)
|
||||
100%
|
||||
opacity 1
|
||||
transform scale(1.2)
|
||||
|
||||
|
||||
@keyframes destroyAnimation
|
||||
0%
|
||||
transform scale(1)
|
||||
filter brightness(1)
|
||||
|
||||
20%
|
||||
transform scale(1.3)
|
||||
filter brightness(1.7)
|
||||
|
||||
100%
|
||||
transform scale(0)
|
||||
filter brightness(1.5)
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes spawnAnimation
|
||||
from
|
||||
transform: scale(0)
|
||||
|
||||
to
|
||||
transform: scale(1)
|
14
src/pages/sitemap.xml.ts
Normal file
14
src/pages/sitemap.xml.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { GetServerSideProps } from 'next'
|
||||
import Sitemap from 'easy-sitemap'
|
||||
|
||||
export default class SitemapXml {}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async ({res}) => {
|
||||
const sitemap = new Sitemap('https://games.avior.me', {response: res})
|
||||
sitemap.addEntry('/')
|
||||
sitemap.addEntry('/pokemon-shuffle')
|
||||
sitemap.build()
|
||||
return {
|
||||
notFound: true
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user