racer skip

pull/8432/head
Thibault Duplessis 2021-03-20 13:56:40 +01:00
parent b044873a4d
commit f0f11c1083
7 changed files with 66 additions and 10 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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) =>

View File

@ -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);

View File

@ -31,7 +31,7 @@
@include breakpoint($mq-col2) {
position: absolute;
bottom: -2vh;
bottom: -3em;
margin-left: 0;
}
}