puzzle racer WIP
parent
d31d9fa864
commit
13151fb96d
|
@ -19,7 +19,7 @@ final class RacerJson(stormJson: StormJson, sign: StormSign, lightUserSync: Ligh
|
|||
Json
|
||||
.obj("name" -> p.name, "moves" -> p.moves)
|
||||
.add("userId" -> p.userId)
|
||||
.add("title" -> user.map(_.title))
|
||||
.add("title", user.map(_.title))
|
||||
}
|
||||
|
||||
// full race data
|
||||
|
|
|
@ -40,7 +40,7 @@ case class RacerRace(
|
|||
|
||||
def startsInMillis = startsAt.map(d => d.getMillis - nowMillis)
|
||||
|
||||
def hasStarted = startsInMillis.pp.exists(_ <= 0)
|
||||
def hasStarted = startsInMillis.exists(_ <= 0)
|
||||
|
||||
lazy val moves = puzzles.foldLeft(0) { case (m, p) =>
|
||||
m + p.line.size / 2
|
||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
|
@ -1,16 +1,31 @@
|
|||
$car-width: 4rem;
|
||||
$car-ratio: 0.328;
|
||||
|
||||
@font-face {
|
||||
font-family: 'racer-car';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
||||
src: url('../images/racer/racer.woff2') format('woff2');
|
||||
}
|
||||
|
||||
.racer__race {
|
||||
@extend %box-neat;
|
||||
|
||||
background: $c-bg-box;
|
||||
// background-image: url(../images/grain.png);
|
||||
background-image: url(../images/racer/road1.jpg);
|
||||
background-size: 102px;
|
||||
padding: 2vh 2vw;
|
||||
font-size: 0.9em;
|
||||
|
||||
.racer--ongoing & {
|
||||
animation: road-scroll 2000s linear infinite;
|
||||
}
|
||||
|
||||
&__track {
|
||||
display: flex;
|
||||
border-bottom: 2px solid $c-border;
|
||||
// border-bottom: 2px solid $c-border;
|
||||
margin-right: 10ch;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
@ -19,26 +34,37 @@ $car-ratio: 0.328;
|
|||
@extend %flex-center-nowrap;
|
||||
|
||||
flex: 0 0 auto;
|
||||
transition: padding 1s;
|
||||
transition: padding 2s;
|
||||
margin-left: -0.5rem;
|
||||
|
||||
&__name {
|
||||
margin-left: 1ch;
|
||||
color: $c-font-dim;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__car {
|
||||
flex: 0 0 $car-width;
|
||||
font-family: 'racer-car';
|
||||
height: #{$car-width * $car-ratio};
|
||||
background-image: img-url('racer/race-car2.svg');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom right;
|
||||
font-size: $car-width;
|
||||
line-height: 0;
|
||||
text-shadow: -1px 1px 2px #000;
|
||||
animation: lightSpeedInLeft 0.8s ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$c-bg-position-y: 3px;
|
||||
@keyframes road-scroll {
|
||||
from {
|
||||
background-position: 0 $c-bg-position-y;
|
||||
}
|
||||
to {
|
||||
background-position: -10000% $c-bg-position-y;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lightSpeedInLeft {
|
||||
from {
|
||||
transform: translateX(-100%) skewX(30deg);
|
||||
|
@ -59,12 +85,29 @@ $car-ratio: 0.328;
|
|||
}
|
||||
}
|
||||
|
||||
@for $i from 0 through 9 {
|
||||
.car-#{$i} {
|
||||
@if $theme-dark {
|
||||
filter: hue-rotate(#{$i * 36 * 7 % 360}deg) brightness(2);
|
||||
} @else {
|
||||
filter: hue-rotate(#{$i * 36 * 7 % 360}deg);
|
||||
}
|
||||
$car-colors: (
|
||||
0: hsl(274, 100%, 59%),
|
||||
1: hsl(240, 100%, 67%),
|
||||
2: #008000,
|
||||
3: #b22222,
|
||||
4: hsl(16, 80%, 62%),
|
||||
5: #9acd32,
|
||||
6: #ff4500,
|
||||
7: #2e8b57,
|
||||
8: #daa520,
|
||||
9: hsl(32, 75%, 47%),
|
||||
);
|
||||
@each $index, $color in $car-colors {
|
||||
.car-#{$index} {
|
||||
color: #{$color};
|
||||
}
|
||||
}
|
||||
// @for $i from 0 through 9 {
|
||||
// .car-#{$i} {
|
||||
// @if $theme-dark {
|
||||
// filter: hue-rotate(#{$i * 36 * 7 % 360}deg) brightness(2);
|
||||
// } @else {
|
||||
// filter: hue-rotate(#{$i * 36 * 7 % 360}deg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -3,7 +3,8 @@ import { Config } from 'puz/interfaces';
|
|||
const config: Config = {
|
||||
// all times in seconds
|
||||
clock: {
|
||||
initial: 1 * 60,
|
||||
initial: 99 * 60,
|
||||
// initial: 1 * 60,
|
||||
// initial: 10,
|
||||
malus: 5,
|
||||
},
|
||||
|
|
|
@ -27,12 +27,6 @@ export default class StormCtrl {
|
|||
|
||||
constructor(opts: RacerOpts, redraw: (data: RacerData) => void) {
|
||||
this.data = opts.data;
|
||||
// this.data.players = [];
|
||||
// for (let i = 0; i < 10; i++)
|
||||
// this.data.players.push({
|
||||
// name: `Player${i}`,
|
||||
// moves: Math.round((this.data.race.moves / 9) * i),
|
||||
// });
|
||||
this.race = this.data.race;
|
||||
this.pref = opts.pref;
|
||||
this.redraw = () => redraw(this.data);
|
||||
|
@ -57,6 +51,7 @@ export default class StormCtrl {
|
|||
lichess.socket = new lichess.StrongSocket(`/racer/${this.race.id}`, false);
|
||||
lichess.pubsub.on('socket.in.racerState', this.serverUpdate);
|
||||
this.startCountdown();
|
||||
this.simulate();
|
||||
console.log(this.race);
|
||||
}
|
||||
|
||||
|
@ -180,4 +175,17 @@ export default class StormCtrl {
|
|||
const g = this.ground();
|
||||
return g && f(g);
|
||||
};
|
||||
|
||||
private simulate = () => {
|
||||
this.data.players = [];
|
||||
for (let i = 0; i < 10; i++)
|
||||
this.data.players.push({
|
||||
name: `Player${i}`,
|
||||
moves: 0,
|
||||
});
|
||||
setInterval(() => {
|
||||
if (this.isRacing()) this.data.players[Math.floor(Math.random() * 10)].moves++;
|
||||
this.redraw();
|
||||
}, 150);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@ export default function (ctrl: RacerCtrl): VNode {
|
|||
return h(
|
||||
'div.racer.racer-app.racer--play',
|
||||
{
|
||||
class: playModifiers(ctrl.run),
|
||||
class: {
|
||||
...playModifiers(ctrl.run),
|
||||
'racer--ongoing': ctrl.isRacing(),
|
||||
},
|
||||
},
|
||||
renderPlay(ctrl)
|
||||
);
|
||||
|
|
|
@ -2,19 +2,29 @@ import { h } from 'snabbdom';
|
|||
import RacerCtrl from '../ctrl';
|
||||
import { Player, Race } from '../interfaces';
|
||||
|
||||
export const renderRace = (ctrl: RacerCtrl) => h('div.racer__race', ctrl.players().map(renderTrack(ctrl.race)));
|
||||
// to [0,1]
|
||||
type RelativeMoves = (moves: number) => number;
|
||||
|
||||
const renderTrack = (race: Race) => (player: Player, index: number) =>
|
||||
export const renderRace = (ctrl: RacerCtrl) => {
|
||||
const players = ctrl.players();
|
||||
const minMoves = players.reduce((m, p) => (p.moves < m ? p.moves : m), 999) / 2;
|
||||
const maxMoves = players.reduce((m, p) => (p.moves > m ? p.moves : m), 30);
|
||||
const delta = maxMoves - minMoves;
|
||||
const relative: RelativeMoves = moves => (moves - minMoves) / delta;
|
||||
return h('div.racer__race', players.map(renderTrack(relative)));
|
||||
};
|
||||
|
||||
const renderTrack = (relative: RelativeMoves) => (player: Player, index: number) =>
|
||||
h(
|
||||
'div.racer__race__track',
|
||||
h(
|
||||
'div.racer__race__player',
|
||||
{
|
||||
attrs: {
|
||||
style: `padding-left:${(95 * player.moves) / race.moves}%`,
|
||||
style: `padding-left:${relative(player.moves) * 95}%`,
|
||||
},
|
||||
},
|
||||
[h(`div.racer__race__player__car.car-${index}`), h('span.racer__race__player__name', playerLink(player))]
|
||||
[h(`div.racer__race__player__car.car-${index}`, [0]), h('span.racer__race__player__name', playerLink(player))]
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@import 'font';
|
||||
@import 'play-again';
|
||||
@import 'high';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@import '../../../common/css/plugin';
|
||||
@import '../../../common/css/component/slist';
|
||||
@import '../../../puz/css/font';
|
||||
|
||||
@import '../dashboard';
|
||||
|
|
|
@ -765,7 +765,7 @@ chessground@^4.4:
|
|||
merge "1.2.0"
|
||||
mithril "github:ornicar/mithril.js#v1.0.1"
|
||||
|
||||
chessground@^7.11.1:
|
||||
chessground@^7.11.0, chessground@^7.11.1:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.yarnpkg.com/chessground/-/chessground-7.11.1.tgz#7e126d660d63025f9bf27371a8778e2baeeb1b57"
|
||||
integrity sha512-IwGSEZBsQlquHyudv1Y5EwlfTXrz0CEWisJuEim2W3eX07IBpYUsr0ChTD35du+Cr0x1+e6GbCLY3hVejKG7ow==
|
||||
|
|
Loading…
Reference in New Issue