racer skip
parent
b044873a4d
commit
f0f11c1083
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0"></path><g class="" transform="translate(0,0)" style=""><path d="M177.3 26.41c-3.6.05-7.3.36-11.2.96-19.5 2.98-32.7 8.95-40.2 16.45-7.5 7.51-10.5 16.58-8.9 30 1.3 10.49 3.2 15.8 5.3 19.24 2.2 3.45 5.1 5.87 10.5 9.94 10.4 7.8 27.4 20.7 49.9 55.4 8.1 2.4 14.8 2.1 21.6-.1 6-1.9 12-5.4 18-9.9-3.2-13.2-1.2-25.7 1.5-36.8 3.2-13.03 7-24.74 7-35.91 0-24.85-17.7-48.1-50-49.25-1.1-.04-2.3-.05-3.5-.03zm45.1 143.49c-4 2.2-8.1 4.2-12.6 5.6-5.1 1.6-10.6 2.5-16.2 2.5l46.3 165.6 29.1-7.3-46.6-166.4zm121.4 167.3c-1.5 0-3.1.2-4.7.6l-158.8 36.6c-17 3.9-20.1 21.9-13 32.5-16.6 4.9-20.7 23.6-13.9 35.9-29.5 5.4-27.4 47.6 6.6 47.6h217.4c36.2 0 36.2-48.2 0-48.2H275.6l93.2-10.9c31.9-3.7 27.7-51.6-4.3-47.9l-79.8 9.3-.3-1.1 61.6-14.1c24.4-5.6 20.3-39.8-1.2-40.3h-1z" fill="#fff" fill-opacity="1"></path></g></svg>
|
After Width: | Height: | Size: 952 B |
|
@ -2,16 +2,15 @@
|
|||
@extend %flex-center-nowrap;
|
||||
|
||||
position: relative;
|
||||
margin-bottom: -1em;
|
||||
|
||||
&__time {
|
||||
font-family: 'storm';
|
||||
font-size: 6em;
|
||||
font-size: 5em;
|
||||
transition: color 0.3s;
|
||||
margin: 2vh 0;
|
||||
|
||||
@include breakpoint($mq-col2) {
|
||||
margin: 0;
|
||||
margin: 0 0 -0.2em 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
.puz-clock {
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__skip {
|
||||
@extend %flex-column;
|
||||
align-items: center;
|
||||
@include transition;
|
||||
&.disabled {
|
||||
opacity: 0;
|
||||
cursor: default;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
background-image: img-url('racer/gear-stick.svg');
|
||||
width: 4ch;
|
||||
height: 4ch;
|
||||
margin-bottom: 0.6ch;
|
||||
}
|
||||
&:hover::before {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
&__spectating .puz-clock__time {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ export default class StormCtrl {
|
|||
promotion: Promotion;
|
||||
countdown: Countdown;
|
||||
boost: Boost = new Boost();
|
||||
skipAvailable = true;
|
||||
ground = prop<CgApi | false>(false) as Prop<CgApi | false>;
|
||||
|
||||
constructor(opts: RacerOpts, redraw: (data: RacerData) => void) {
|
||||
|
@ -111,15 +112,27 @@ export default class StormCtrl {
|
|||
this.redrawSlow();
|
||||
};
|
||||
|
||||
canSkip = () => this.skipAvailable;
|
||||
|
||||
skip = () => {
|
||||
if (this.skipAvailable) {
|
||||
this.skipAvailable = false;
|
||||
sound.good();
|
||||
this.playUci(this.run.current.expectedMove());
|
||||
}
|
||||
};
|
||||
|
||||
userMove = (orig: Key, dest: Key): void => {
|
||||
if (!this.promotion.start(orig, dest, this.playUserMove)) this.playUserMove(orig, dest);
|
||||
};
|
||||
|
||||
playUserMove = (orig: Key, dest: Key, promotion?: Role): void => {
|
||||
playUserMove = (orig: Key, dest: Key, promotion?: Role): void =>
|
||||
this.playUci(`${orig}${dest}${promotion ? (promotion == 'knight' ? 'n' : promotion[0]) : ''}`);
|
||||
|
||||
playUci = (uci: Uci): void => {
|
||||
this.run.moves++;
|
||||
this.promotion.cancel();
|
||||
const puzzle = this.run.current;
|
||||
const uci = `${orig}${dest}${promotion ? (promotion == 'knight' ? 'n' : promotion[0]) : ''}`;
|
||||
const pos = puzzle.position();
|
||||
const move = parseUci(uci)!;
|
||||
let captureSound = pos.board.occupied.has(move.to);
|
||||
|
|
|
@ -46,12 +46,15 @@ const selectScreen = (ctrl: RacerCtrl): MaybeVNodes => {
|
|||
comboZone(ctrl),
|
||||
];
|
||||
case 'racing':
|
||||
const clock = h('div.puz-clock', [renderClock(ctrl.run, ctrl.end, false)]);
|
||||
const clock = renderClock(ctrl.run, ctrl.end, false);
|
||||
return ctrl.isPlayer()
|
||||
? [playerScore(ctrl), clock, comboZone(ctrl)]
|
||||
? [playerScore(ctrl), h('div.puz-clock', [clock, renderSkip(ctrl)]), comboZone(ctrl)]
|
||||
: [
|
||||
spectating(noarg),
|
||||
h('div.racer__spectating', [clock, ctrl.race.lobby ? lobbyNext(ctrl) : waitForRematch(noarg)]),
|
||||
h('div.racer__spectating', [
|
||||
h('div.puz-clock', clock),
|
||||
ctrl.race.lobby ? lobbyNext(ctrl) : waitForRematch(noarg),
|
||||
]),
|
||||
comboZone(ctrl),
|
||||
];
|
||||
case 'post':
|
||||
|
@ -63,6 +66,21 @@ const selectScreen = (ctrl: RacerCtrl): MaybeVNodes => {
|
|||
}
|
||||
};
|
||||
|
||||
const renderSkip = (ctrl: RacerCtrl) =>
|
||||
h(
|
||||
'button.racer__skip.button.button-red',
|
||||
{
|
||||
class: {
|
||||
disabled: !ctrl.canSkip(),
|
||||
},
|
||||
attrs: {
|
||||
title: 'Skip this move to preserve your combo! Only works once per race.',
|
||||
},
|
||||
hook: bind('click', ctrl.skip),
|
||||
},
|
||||
'skip'
|
||||
);
|
||||
|
||||
const puzzleRacer = () => h('strong', 'Puzzle Racer');
|
||||
|
||||
const waitingToStart = (noarg: TransNoArg) =>
|
||||
|
|
|
@ -11,7 +11,7 @@ const trackHeight = 25;
|
|||
export const renderRace = (ctrl: RacerCtrl) => {
|
||||
const players = ctrl.players();
|
||||
const minMoves = players.reduce((m, p) => (p.score < m ? p.score : m), 130) / 3;
|
||||
const maxMoves = players.reduce((m, p) => (p.score > m ? p.score : m), 30);
|
||||
const maxMoves = players.reduce((m, p) => (p.score > m ? p.score : m), 35);
|
||||
const delta = maxMoves - minMoves;
|
||||
const relative: RelativeScore = score => (score - minMoves) / delta;
|
||||
const bestScore = players.reduce((b, p) => (p.score > b ? p.score : b), 0);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
@include breakpoint($mq-col2) {
|
||||
position: absolute;
|
||||
bottom: -2vh;
|
||||
bottom: -3em;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue