24 Commits

Author SHA1 Message Date
adrien 6706d60086 more sounds 2026-03-12 02:41:08 +01:00
adrien 040dc5d5e9 round 2026-03-11 19:52:19 +01:00
adrien b8b743871e defaults, round 2026-03-11 18:10:20 +01:00
adrien 018d623143 fix space in url 2026-03-11 16:18:32 +01:00
adrien 54cb17fb88 use https://you.have.fail/tetrioplus/ skins 2026-03-11 15:05:34 +01:00
adrien 7bdb9524ad sanitize input 2026-03-10 03:23:30 +01:00
adrien 923ec9f95d improve jstris skin 2026-03-10 02:06:05 +01:00
adrien 1d9a521595 escapeMarkup 2026-03-09 21:49:02 +01:00
adrien 3bb9f302ed improve jstris skin 2026-03-09 21:03:45 +01:00
adrien 4bac3face1 use https://konsola5.github.io/jstris-customization-database/ 2026-03-09 19:06:48 +01:00
adrien b2601e9c24 tweaks 2026-03-09 01:50:18 +01:00
adrien 89b08832d1 ghost opacity 2026-03-08 05:35:43 +01:00
adrien 7ed61e135b support for jstris skin as well 2026-03-08 05:28:16 +01:00
adrien a49c95f1aa no overlay 2026-03-08 04:08:48 +01:00
adrien d0a2f1b7f9 use external skins 2026-03-07 23:13:45 +01:00
adrien aed0601933 a bit of 3D 2026-03-05 21:14:34 +01:00
adrien 896b26684a sprites 2026-03-05 09:03:17 +01:00
adrien bf18998caa fix paste error 2026-03-05 08:46:10 +01:00
adrien f261f01684 less blinks 2026-03-05 02:01:38 +01:00
adrien 49e56427b1 optimization 2026-03-04 23:54:27 +01:00
adrien b72276b76c neo-classic theme 2026-03-04 19:50:44 +01:00
adrien d4d0dfaf30 merge and rename themes 2026-03-04 08:52:24 +01:00
adrien 5ba379d968 ridge 2026-03-04 00:17:41 +01:00
adrien f6ebe87434 shadow 2026-03-03 16:50:32 +01:00
120 changed files with 15686 additions and 918 deletions
+31 -17
View File
@@ -1,5 +1,5 @@
:root { :root {
--cell-side: 25px; --cell-size: 25px;
--rX: -15; --rX: -15;
--rY: 0; --rY: 0;
--tZ: 25px; --tZ: 25px;
@@ -11,7 +11,7 @@ body {
@supports (backdrop-filter: blur()) { @supports (backdrop-filter: blur()) {
.modal::before { .modal::before {
content: ""; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
@@ -31,7 +31,7 @@ body {
} }
#matrixCard { #matrixCard {
background-image: radial-gradient(#222, #25292d) background-image: radial-gradient(#222, #25292d);
} }
.card-header { .card-header {
@@ -62,16 +62,16 @@ td#timeCell {
} }
#matrixTable { #matrixTable {
margin-top: calc(-1 * var(--buffer-zone-rows) * var(--cell-side)); margin-top: calc(-1 * var(--buffer-zone-rows) * var(--cell-size));
} }
@keyframes hard-dropped-table-animation { @keyframes hard-dropped-table-animation {
25% { 50% {
transform: translateY(3px); transform: translate(0, 5px);
} }
} }
.hard-dropped-table-animation { .hard-dropped-table-animation {
animation: hard-dropped-table-animation .2s; animation: hard-dropped-table-animation ease-in-out 0.2s;
} }
tr.buffer-zone td:not(.mino) { tr.buffer-zone td:not(.mino) {
@@ -87,8 +87,8 @@ tr.matrix td:not(.mino) {
td { td {
overflow: hidden; overflow: hidden;
width: var(--cell-side); width: var(--cell-size);
height: var(--cell-side); height: var(--cell-size);
box-sizing: border-box; box-sizing: border-box;
} }
@@ -113,26 +113,32 @@ td {
} }
td.trail-animation { td.trail-animation {
animation: trail-animation ease-out .3s; animation: trail-animation ease-out 0.3s;
} }
@keyframes cleared-line-animation { @keyframes cleared-line-animation {
from { from {
background-color: white; background-color: white;
filter: saturate(50%) brightness(300%); filter: saturate(50%) brightness(300%);
box-shadow: 0 0 0 #adb5bd, 0 0 0 #adb5bd; box-shadow:
0 0 0 #adb5bd,
0 0 0 #adb5bd;
} }
60% { 60% {
box-shadow: -60px 0 2px #adb5bd66, 60px 0 2px #adb5bd66; box-shadow:
-60px 0 2px #adb5bd66,
60px 0 2px #adb5bd66;
} }
to { to {
background-color: transparent; background-color: transparent;
box-shadow: -100px 0 5px transparent, 100px 0 5px transparent; box-shadow:
-100px 0 5px transparent,
100px 0 5px transparent;
} }
} }
tr.cleared-line-animation { tr.cleared-line-animation {
animation: cleared-line-animation ease-out .3s; animation: cleared-line-animation ease-out 0.3s;
} }
#holdTable .J, #holdTable .J,
@@ -157,6 +163,7 @@ tr.cleared-line-animation {
text-shadow: 1px 1px #000c; text-shadow: 1px 1px #000c;
font-size: 3vmin; font-size: 3vmin;
text-align: center; text-align: center;
z-index: 200;
} }
#messagesSpan div { #messagesSpan div {
@@ -192,7 +199,6 @@ tr.cleared-line-animation {
opacity: 0; opacity: 0;
transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3);
line-height: var(--bs-body-line-height); line-height: var(--bs-body-line-height);
} }
30% { 30% {
opacity: 1; opacity: 1;
@@ -235,13 +241,13 @@ tr.cleared-line-animation {
#messagesSpan div.rotate-in-animation { #messagesSpan div.rotate-in-animation {
animation-name: rotate-in-animation; animation-name: rotate-in-animation;
animation-timing-function: cubic-bezier(.25,.46,.45,.94); animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
animation-duration: 1s; animation-duration: 1s;
} }
#messagesSpan div.zoom-in-animation { #messagesSpan div.zoom-in-animation {
animation-name: zoom-in-animation; animation-name: zoom-in-animation;
animation-timing-function: cubic-bezier(.25,.46,.45,.94); animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
transform-origin: center; transform-origin: center;
animation-duration: 1s; animation-duration: 1s;
} }
@@ -288,3 +294,11 @@ tr.cleared-line-animation {
#statsModal tr:last-child td { #statsModal tr:last-child td {
border-bottom: none; border-bottom: none;
} }
.select2-dropdown {
width: min-content !important;
}
.select2-results__group {
display: inline-block;
}
+204
View File
@@ -0,0 +1,204 @@
body .select2-container--bootstrap-5 .select2-selection {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border: var(--bs-border-width) solid var(--bs-border-color);
}
body
.select2-container--bootstrap-5.select2-container--focus
.select2-selection,
body
.select2-container--bootstrap-5.select2-container--open
.select2-selection {
border-color: var(--bs-link-hover-color);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
body
.select2-container--bootstrap-5
.select2-selection--multiple
.select2-selection__clear,
body
.select2-container--bootstrap-5
.select2-selection--single
.select2-selection__clear {
background: transparent
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e")
50%/0.75rem auto no-repeat;
}
body
.select2-container--bootstrap-5
.select2-selection--multiple
.select2-selection__clear:hover,
body
.select2-container--bootstrap-5
.select2-selection--single
.select2-selection__clear:hover {
background: transparent
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e")
50%/0.75rem auto no-repeat;
}
body .select2-container--bootstrap-5 .select2-dropdown {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-link-hover-color);
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-search
.select2-search__field {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
background-clip: padding-box;
border: var(--bs-border-width) solid var(--bs-border-color);
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-search
.select2-search__field:focus {
border-color: var(--bs-link-hover-color);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option.select2-results__message {
color: #6c757d;
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option.select2-results__option--highlighted {
color: var(--bs-body-color);
background-color: var(--bs-light-bg-subtle) !important;
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option.select2-results__option--selected,
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option[aria-selected="true"]:not(
.select2-results__option--highlighted
) {
color: var(--bs-body-color);
background-color: var(--bs-dark-bg-subtle);
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option.select2-results__option--disabled,
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option[aria-disabled="true"] {
color: #6c757d;
}
body
.select2-container--bootstrap-5
.select2-dropdown
.select2-results__options
.select2-results__option[role="group"]
.select2-results__group {
color: #6c757d;
}
body
.select2-container--bootstrap-5
.select2-selection--single
.select2-selection__rendered {
color: var(--bs-body-color);
}
body
.select2-container--bootstrap-5
.select2-selection--single
.select2-selection__rendered
.select2-selection__placeholder {
color: #6c757d;
}
body
.select2-container--bootstrap-5
.select2-selection--multiple
.select2-selection__rendered
.select2-selection__choice {
color: var(--bs-body-color);
border: var(--bs-border-width) solid var(--bs-border-color);
}
body
.select2-container--bootstrap-5.select2-container--disabled
.select2-selection,
body
.select2-container--bootstrap-5.select2-container--disabled.select2-container--focus
.select2-selection {
color: #6c757d;
background-color: var(--bs-light-bg-subtle);
border-color: var(--bs-dark-bg-subtle);
}
.is-valid + body .select2-container--bootstrap-5 .select2-selection,
.was-validated
select:valid
+ body
.select2-container--bootstrap-5
.select2-selection {
border-color: #198754;
}
.is-valid
+ body
.select2-container--bootstrap-5.select2-container--focus
.select2-selection,
.is-valid
+ body
.select2-container--bootstrap-5.select2-container--open
.select2-selection,
.was-validated
select:valid
+ body
.select2-container--bootstrap-5.select2-container--focus
.select2-selection,
.was-validated
select:valid
+ body
.select2-container--bootstrap-5.select2-container--open
.select2-selection {
border-color: #198754;
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
}
.is-invalid + body .select2-container--bootstrap-5 .select2-selection,
.was-validated
select:invalid
+ body
.select2-container--bootstrap-5
.select2-selection {
border-color: #dc3545;
}
.is-invalid
+ body
.select2-container--bootstrap-5.select2-container--focus
.select2-selection,
.is-invalid
+ body
.select2-container--bootstrap-5.select2-container--open
.select2-selection,
.was-validated
select:invalid
+ body
.select2-container--bootstrap-5.select2-container--focus
.select2-selection,
.was-validated
select:invalid
+ body
.select2-container--bootstrap-5.select2-container--open
.select2-selection {
border-color: #dc3545;
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
}
+2 -4
View File
@@ -12,7 +12,6 @@ tr.matrix td:not(.mino) {
--color: hsl(var(--hue), var(--saturation), 40%); --color: hsl(var(--hue), var(--saturation), 40%);
--light: hsl(var(--hue), calc(0.66 * var(--saturation)), 84%); --light: hsl(var(--hue), calc(0.66 * var(--saturation)), 84%);
--top: hsl(var(--hue), calc(0.6 * var(--saturation)), 68%); --top: hsl(var(--hue), calc(0.6 * var(--saturation)), 68%);
--right: hsl(var(--hue), calc(0.4 * var(--saturation)), 20%);
background-color: var(--color); background-color: var(--color);
background-image: background-image:
radial-gradient( radial-gradient(
@@ -40,8 +39,7 @@ tr.matrix td:not(.mino) {
inset 2px 0 4px rgba(0,0,0,.06), inset 2px 0 4px rgba(0,0,0,.06),
inset -2px 0 4px rgba(0,0,0,.12), inset -2px 0 4px rgba(0,0,0,.12),
0 -4px 0 var(--top), 0 -4px 0 var(--top),
2px -3px 0 var(--right), 0 2px 4px #0008;
3px 2px 6px #0004;
filter: saturate(1.1) contrast(1.05); filter: saturate(1.1) contrast(1.05);
} }
@@ -82,7 +80,7 @@ tr.matrix td:not(.mino) {
.ghost { .ghost {
border: 3px solid #fff2; border: 3px solid #fff2;
padding: 2px; padding: 3px;
background-color: #fff2; background-color: #fff2;
background-clip: content-box; background-clip: content-box;
background-image: none; background-image: none;
+5 -4
View File
@@ -1,9 +1,9 @@
body { body {
background-image: url("electro/bg.jpg"); background-image: url('electro/bg.jpg');
background-size: cover; background-size: cover;
} }
body[data-bs-theme="dark"] { body[data-bs-theme='dark'] {
--bs-body-bg: #2125296b; --bs-body-bg: #2125296b;
} }
@@ -30,7 +30,7 @@ tr.matrix td:not(.mino) {
} }
.mino:not(.ghost):not(.locking):before { .mino:not(.ghost):not(.locking):before {
content: ""; content: '';
position: absolute; position: absolute;
z-index: -1; z-index: -1;
inset: 0; inset: 0;
@@ -42,7 +42,8 @@ tr.matrix td:not(.mino) {
background: radial-gradient( background: radial-gradient(
at var(--glint-x) var(--glint-y), at var(--glint-x) var(--glint-y),
rgba(204, 238, 247, 0.9) 0%, rgba(204, 238, 247, 0.9) 0%,
rgba(10, 159, 218, 0.9) 150%); rgba(10, 159, 218, 0.9) 150%
);
mask: mask:
linear-gradient(#666 0 0) content-box, linear-gradient(#666 0 0) content-box,
linear-gradient(#fff 0 0); linear-gradient(#fff 0 0);
+61
View File
@@ -0,0 +1,61 @@
#sceneDiv {
perspective: 500px;
}
#screenRow {
transform: rotateX(15deg);
}
#screenRow .card {
box-shadow:
0 10px 3px #25292d,
0 15px 0 var(--bs-card-border-color) !important;;
}
tr.matrix td:not(.mino) {
border-left: none;
border-bottom: none;
}
.mino {
background-color: hsl(var(--hue), 55%, 55%);
background-image: linear-gradient(30deg, #fff4, transparent);
border: 1px outset hsl(var(--hue), 55%, 45%);
border-radius: 2px;
box-shadow:
0 10px 3px hsl(var(--hue), 70%, 10%),
0 15px 0 hsla(var(--hue), 90%, 40%, 70%);
opacity: 80%;
backdrop-filter: blur(6px);
}
.I {
--hue: 197;
}
.J {
--hue: 217;
}
.L {
--hue: 36;
}
.O {
--hue: 60;
}
.S {
--hue: 113;
}
.T {
--hue: 268;
}
.Z {
--hue: 0;
}
.ghost {
opacity: 30%;
}
+217
View File
@@ -0,0 +1,217 @@
body {
--bs-gutter-x: 0;
background: black !important;
}
#screenRow {
gap: 0 !important;
margin: 0;
text-transform: uppercase;
letter-spacing: 0.1em;
}
#screenRow {
--bs-gutter-x: 0;
}
.card {
background: black;
border: none;
border-radius: 0;
margin-bottom: 0.5em !important;
}
.card:first-of-type {
border-bottom: 3px solid white;
}
.card-header,
.card-header th {
background: transparent;
font-weight: 400 !important;
font-size: 1.3em;
border: none;
}
#screenRow .table {
--bs-border-width: 0;
}
.minoes-table {
display: flex;
flex-direction: column;
filter: drop-shadow(-2px 0 0 white) drop-shadow(2px 0 0 white) drop-shadow(0 -2px 0 white)
drop-shadow(0 2px 0 white) drop-shadow(5px 8px 0 rgba(9, 9, 9, 22%));
}
.minoes-table tr {
display: flex;
position: relative;
flex-direction: row;
z-index: calc(100 - var(--row));
}
#holdTable {
margin: 0 !important;
}
#statsTable {
width: 10rem;
}
#statsTable tr {
display: flex;
flex-flow: column;
align-items: center;
}
#statsTable td,
#statsTable th {
margin: 0;
padding: 0 !important;
width: auto;
height: auto;
text-transform: uppercase;
}
#statsTable th {
display: inline;
flex-flow: row;
font-size: 0.8em;
text-align: center;
width: 200%;
}
#statsTable td {
font-size: 1.3em;
font-weight: 600;
color: white;
}
td#timeCell {
text-align: center;
}
#matrixCard {
background: transparent;
border-top: none;
border-left: 3px solid white;
border-right: 3px solid white;
border-bottom: 3px solid white;
}
tr.matrix td:not(.mino) {
border: 0;
}
.minoes-table td {
display: inline-block;
width: var(--cell-size);
height: var(--cell-size);
padding: 0 !important;
z-index: calc(200 - var(--row));
}
.mino {
width: inherit;
height: inherit;
display: block;
padding: 0;
opacity: 100%;
border-width: 1px;
border-style: solid;
box-shadow: 0 -6px 0 var(--box-shadow-color);
}
.I.mino {
background-color: #42afe1;
border-color: #6ceaff;
--box-shadow-color: #6ceaff;
}
.J.mino {
background-color: #1165b5;
border-color: #339bff;
--box-shadow-color: #339bff;
}
.L.mino {
background-color: #f38927;
border-color: #ffba59;
--box-shadow-color: #ffba59;
}
.O.mino {
background-color: #f6d03c;
border-color: #ffff7f;
--box-shadow-color: #ffff7f;
}
.S.mino {
background-color: #32ee3e;
border-color: #84f880;
--box-shadow-color: #84f880;
}
.T.mino {
background-color: #9739a2;
border-color: #d958e9;
--box-shadow-color: #d958e9;
}
.Z.mino {
background-color: #eb4f65;
border-color: #ff7f79;
--box-shadow-color: #ff7f79;
}
.ghost.mino {
background-color: #fff4;
border: none;
opacity: 5%;
box-shadow: none;
transform: translateY(-3px);
}
.moving.mino {
filter: saturate(80%) brightness(130%);
}
.locking.mino {
filter: saturate(20%) brightness(300%);
}
.locked.mino {
animation: locked-animation;
animation-duration: 0.2s;
}
.disabled.mino {
filter: brightness(50%) contrast(65%);
opacity: 70%;
}
@keyframes locked-animation {
from {
filter: saturate(50%) brightness(300%);
}
}
@keyframes cleared-line-animation {
from {
background-color: #eeeeee;
}
to {
background-color: transparent;
}
}
@keyframes trail-animation {
from {
background-color: #ceffff05;
filter: saturate(50%) brightness(110%);
}
to {
background-color: transparent;
}
}
+118
View File
@@ -0,0 +1,118 @@
.minoes-table {
display: flex;
flex-direction: column;
filter: drop-shadow(0 8px 1px #0008);
}
.minoes-table tr {
display: flex;
position: relative;
flex-direction: row;
z-index: calc(100 - var(--row));
}
tr.matrix td:not(.mino) {
border: none;
border-right: 1px solid #8884;
}
.minoes-table td {
display: inline-block;
width: var(--cell-size);
height: var(--cell-size);
padding: 0 !important;
z-index: calc(200 - var(--row));
}
.mino {
width: inherit;
height: inherit;
display: block;
padding: 0;
opacity: 100%;
border: none;
box-shadow: 0 -6px 0 var(--box-shadow-color);
}
.I.mino {
background-color: #0293b0;
--box-shadow-color: #05d2f2;
}
.J.mino {
background-color: #2c69c2;
--box-shadow-color: #7bb7f6;
}
.L.mino {
background-color: #fa9b23;
--box-shadow-color: #ffba59;
}
.O.mino {
background-color: #f9d92c;
--box-shadow-color: #fff194;
}
.S.mino {
background-color: #01a493;
--box-shadow-color: #03e7d3;
}
.T.mino {
background-color: #6830d1;
--box-shadow-color: #bb88fc;
}
.Z.mino {
background-color: #ee2b58;
--box-shadow-color: #fd4487;
}
.ghost.mino {
background-color: #fff8;
box-shadow: none;
transform: blue(4px);
}
.moving.mino {
filter: saturate(80%) brightness(130%);
}
.locking.mino {
filter: saturate(20%) brightness(300%);
}
.locked.mino {
animation: locked-animation;
animation-duration: 0.2s;
}
.disabled.mino {
filter: brightness(50%) contrast(65%);
}
@keyframes locked-animation {
from {
filter: saturate(50%) brightness(300%);
}
}
@keyframes cleared-line-animation {
from {
background-color: #eeeeee;
}
to {
background-color: transparent;
}
}
@keyframes trail-animation {
from {
background-color: #ceffff05;
filter: saturate(50%) brightness(110%);
}
to {
background-color: transparent;
}
}
+81
View File
@@ -0,0 +1,81 @@
:root {
--cell-size: 24px;
--sprite-size: round(100% / 8, 1px);
--skin-url: url(https://i.imgur.com/HqGYC5G.png);
}
.card {
background-color: #1c1c1c;
}
.card-body {
background-color: black;
}
#matrixCard {
background-image: url(jstris-skin/jstris-grid.png);
background-position: bottom;
background-repeat: no-repeat;
}
tr.matrix td:not(.mino) {
border: none;
}
.mino {
background-image: var(--skin-url);
background-size: cover;
background-repeat: no-repeat;
background-position-x: calc(var(--sprite-pos) * var(--sprite-size));
}
.I {
--sprite-pos: 6;
}
.J {
--sprite-pos: 7;
}
.L {
--sprite-pos: 3;
}
.O {
--sprite-pos: 4;
}
.S {
--sprite-pos: 5;
}
.T {
--sprite-pos: 8;
}
.Z {
--sprite-pos: 2;
}
.ghost {
--sprite-pos: 1;
opacity: 50%;
}
.disabled {
--sprite-pos: 0;
}
.locking.mino {
filter: saturate(60%) brightness(180%);
}
#holdTable .mino,
#nextTable .mino {
box-shadow: 4px 4px 10px #0002;
}
.preview {
--cell-size: 24px;
height: var(--cell-size);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

+19 -23
View File
@@ -5,12 +5,8 @@
.minoes-table { .minoes-table {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
filter: filter: drop-shadow(-2px 0 0 white) drop-shadow(2px 0 0 white) drop-shadow(0 -2px 0 white)
drop-shadow(-2px 0 0 white) drop-shadow(0 2px 0 white) drop-shadow(5px 8px 0 rgba(9, 9, 9, 22%));
drop-shadow(2px 0 0 white)
drop-shadow(0 -2px 0 white)
drop-shadow(0 2px 0 white)
drop-shadow(5px 8px 0 rgba(9, 9, 9, 22%));
} }
.minoes-table tr { .minoes-table tr {
@@ -26,8 +22,8 @@ tr.matrix td:not(.mino) {
.minoes-table td { .minoes-table td {
display: inline-block; display: inline-block;
width: var(--cell-side); width: var(--cell-size);
height: var(--cell-side); height: var(--cell-size);
padding: 0 !important; padding: 0 !important;
z-index: calc(200 - var(--row)); z-index: calc(200 - var(--row));
} }
@@ -41,38 +37,38 @@ tr.matrix td:not(.mino) {
} }
.I.mino { .I.mino {
--background-color: #42AFE1; --background-color: #42afe1;
--box-shadow-color: #6CEAFF; --box-shadow-color: #6ceaff;
} }
.J.mino { .J.mino {
--background-color: #1165B5; --background-color: #1165b5;
--box-shadow-color: #339BFF; --box-shadow-color: #339bff;
} }
.L.mino { .L.mino {
--background-color: #F38927; --background-color: #f38927;
--box-shadow-color: #FFBA59; --box-shadow-color: #ffba59;
} }
.O.mino { .O.mino {
--background-color: #F6D03C; --background-color: #f6d03c;
--box-shadow-color: #FFFF7F; --box-shadow-color: #ffff7f;
} }
.S.mino { .S.mino {
--background-color: #51B84D; --background-color: #51b84d;
--box-shadow-color: #84F880; --box-shadow-color: #84f880;
} }
.T.mino { .T.mino {
--background-color: #9739A2; --background-color: #9739a2;
--box-shadow-color: #D958E9; --box-shadow-color: #d958e9;
} }
.Z.mino { .Z.mino {
--background-color: #EB4F65; --background-color: #eb4f65;
--box-shadow-color: #FF7F79; --box-shadow-color: #ff7f79;
} }
.ghost.mino { .ghost.mino {
@@ -87,7 +83,7 @@ tr.matrix td:not(.mino) {
.locking.mino { .locking.mino {
--background-color: white; --background-color: white;
--box-shadow-color: #DDD; --box-shadow-color: #ddd;
} }
.locked.mino { .locked.mino {
+89
View File
@@ -0,0 +1,89 @@
.minoes-table tr {
z-index: calc(100 - var(--row));
position: sticky;
}
tr.matrix td:not(.mino) {
border-left: none;
border-bottom: none;
}
.mino {
--color: hsl(var(--hue), var(--saturation), 60%);
--dark: hsl(var(--hue), var(--saturation), 28%);
--light: hsl(var(--hue), calc(0.66 * var(--saturation)), 90%);
--border: hsl(var(--hue), var(--saturation), 40%);
--top: hsl(var(--hue), calc(0.6 * var(--saturation)), 75%);
position: relative;
background-color: var(--dark);
background-image: radial-gradient(
ellipse 200% 120% at 50% 75%,
#fff8 10%,
var(--dark) 28%,
#fff8 38%,
var(--dark) 48%
);
border: 1px outset var(--border);
border-radius: 3px;
box-shadow:
inset 3px 0 4px rgba(0,0,0,.06),
inset -3px 0 4px rgba(0,0,0,.12),
0 -3px 0 var(--top),
0 2px 2px #0004;
}
.mino:after {
--size: calc(var(--cell-size) - 12px);
position: absolute;
content: "";
box-sizing: content-box;
width: var(--size);
height: var(--size);
opacity: 40%;
background: var(--color);
left: 3px;
top: 3px;
border: 2px solid var(--border);
border-top-color: white;
}
.I {
--hue: 193;
--saturation: 100%;
}
.J {
--hue: 215;
--saturation: 100%;
}
.L {
--hue: 25;
--saturation: 100%;
}
.O {
--hue: 42;
--saturation: 100%;
}
.S {
--hue: 95;
--saturation: 100%;
}
.T {
--hue: 300;
--saturation: 56%;
}
.Z {
--hue: 357;
--saturation: 84%;
}
.ghost {
--hue: 0;
--saturation: 0%;
opacity: 30%;
}
+171
View File
@@ -0,0 +1,171 @@
:root {
--cell-size: 20px;
--sprite-size: 40px;
}
@font-face {
font-family: 'Early GameBoy';
src: url('old-school/Early GameBoy.ttf');
}
#screenRow {
background-image: url('old-school/bg.png');
background-size: 10px;
padding: 40px 20px;
border: 3px inset black;
border-radius: 10px;
}
.col {
gap: 8px;
}
.card {
background: #8d8e04;
border-radius: 0;
border-image-source: url('old-school/border-sm.png');
border-image-slice: 25;
border-image-width: 13px;
border-image-repeat: repeat;
border-image-outset: 12px;
box-shadow: unset;
width: 100%;
}
.card-header {
background: #8d8e04;
}
#matrixCard {
background: #808302;
border-image-source: url('old-school/border-lg.png');
border-image-slice: 30;
border-image-width: 15px;
border-image-outset: 15px;
}
#statsTable,
.card,
.card-header,
#messagesSpan div {
font-family: 'Early GameBoy', monospace;
font-smooth: never;
-webkit-font-smoothing: none;
color: #254806;
text-shadow:
-1px -1px 3px rgba(0, 0, 0, 40%),
1px 1px 1px rgba(0, 0, 0, 40%);
}
#statsTable {
font-size: 0.7em;
letter-spacing: -0.1em;
}
#statsTable tr {
display: flex;
flex-flow: column;
}
#statsTable th,
#statsTable td {
border: 0;
padding: 0 0.2rem;
}
#statsTable td {
width: auto;
text-align: end;
}
#messagesSpan {
text-shadow:
-2px -2px #808302,
-2px 2px #808302,
2px -2px #808302,
2px 2px #808302;
}
.minoes-table td {
border: 0 !important;
}
.mino {
box-shadow:
-2px -2px 5px rgba(0, 0, 0, 40%),
1px 1px 2px rgba(0, 0, 0, 40%);
background-size: cover;
background-image: url('old-school/sprites.png');
background-position: calc(var(--sprite-pos) * var(--sprite-size)) 0px;
}
.I.mino {
--sprite-pos: 0;
}
.J.mino {
--sprite-pos: 1;
}
.L.mino {
--sprite-pos: 2;
}
.O.mino {
--sprite-pos: 3;
}
.S.mino {
--sprite-pos: 4;
}
.T.mino {
--sprite-pos: 5;
}
.Z.mino {
--sprite-pos: 6;
}
@keyframes blinker {
35% {
opacity: 50%;
}
}
.locking.mino {
filter: brightness(135%) contrast(70%);
}
.ghost.mino,
.disabled.mino {
opacity: 50%;
}
.locked.mino {
animation: none;
}
.hard-dropped-table-animation {
animation: hard-dropped-table-animation steps(1) 0.2s;
}
@keyframes cleared-line-animation {
10%,
30%,
50%,
70%,
90% {
opacity: 0;
}
20%,
40%,
60%,
80% {
opacity: 100%;
}
}
tr.cleared-line-animation {
animation: cleared-line-animation ease-out 0.4s;
}

Before

Width:  |  Height:  |  Size: 870 B

After

Width:  |  Height:  |  Size: 870 B

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+14 -14
View File
@@ -7,7 +7,7 @@ body {
gap: 0 !important; gap: 0 !important;
margin: 0; margin: 0;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: .1em; letter-spacing: 0.1em;
} }
#screenRow { #screenRow {
@@ -95,38 +95,38 @@ td#timeCell {
} }
.I.mino { .I.mino {
background-color: #42AFE1; background-color: #42afe1;
border-color: #6CEAFF; border-color: #6ceaff;
} }
.J.mino { .J.mino {
background-color: #1165B5; background-color: #1165b5;
border-color: #339BFF; border-color: #339bff;
} }
.L.mino { .L.mino {
background-color: #F38927; background-color: #f38927;
border-color: #FFBA59; border-color: #ffba59;
} }
.O.mino { .O.mino {
background-color: #F6D03C; background-color: #f6d03c;
border-color: #FFFF7F; border-color: #ffff7f;
} }
.S.mino { .S.mino {
background-color: #32ee3e; background-color: #32ee3e;
border-color: #84F880; border-color: #84f880;
} }
.T.mino { .T.mino {
background-color: #9739A2; background-color: #9739a2;
border-color: #D958E9; border-color: #d958e9;
} }
.Z.mino { .Z.mino {
background-color: #EB4F65; background-color: #eb4f65;
border-color: #FF7F79; border-color: #ff7f79;
} }
.ghost.mino { .ghost.mino {
-153
View File
@@ -1,153 +0,0 @@
:root {
--cell-side: 20px;
}
@font-face {
font-family: "Early GameBoy";
src: url("retro/Early GameBoy.ttf");
}
#screenRow {
background-image: url("retro/bg.png");
background-size: 10px;
padding: 40px 20px;
border: 3px inset black;
border-radius: 10px;
}
.col {
gap: 8px;
}
.card {
background: #8D8E04;
border-radius: 0;
border-image-source: url("retro/border-sm.png");
border-image-slice: 25;
border-image-width: 13px;
border-image-repeat: repeat;
border-image-outset: 12px;
box-shadow: unset;
width: 100%;
}
.card-header {
background: #8D8E04;
}
#matrixCard {
background: #808302;
border-image-source: url("retro/border-lg.png");
border-image-slice: 30;
border-image-width: 15px;
border-image-outset: 15px;
}
#statsTable,
.card,
.card-header,
#messagesSpan {
font-family: "Early GameBoy", monospace;
font-smooth: never;
-webkit-font-smoothing: none;
color: #254806;
text-shadow: -1px -1px 3px rgba(0, 0, 0, 40%), 1px 1px 1px rgba(0, 0, 0, 40%);
}
#statsTable {
font-size: .7em;
letter-spacing: -.1em;
}
#statsTable tr {
display: flex;
flex-flow: column;
}
#statsTable th,
#statsTable td {
border: 0;
padding: 0 .2rem;
}
#statsTable td {
width: auto;
text-align: end;
}
#messagesSpan {
text-shadow: -2px -2px #808302, -2px 2px #808302, 2px -2px #808302, 2px 2px #808302;
}
.minoes-table td {
border: 0 !important;
}
.mino {
box-shadow: -2px -2px 5px rgba(0, 0, 0, 40%), 1px 1px 2px rgba(0, 0, 0, 40%);
background-size: 100%;
}
.I.mino {
background-image: url("retro/I-mino.png")
}
.J.mino {
background-image: url("retro/J-mino.png")
}
.L.mino {
background-image: url("retro/L-mino.png")
}
.O.mino {
background-image: url("retro/O-mino.png")
}
.S.mino {
background-image: url("retro/S-mino.png")
}
.T.mino {
background-image: url("retro/T-mino.png")
}
.Z.mino {
background-image: url("retro/Z-mino.png")
}
@keyframes blinker {
35% {
opacity: 50%;
}
}
.locking.mino {
animation: blinker 0.08s step-start infinite;
}
.ghost.mino,
.disabled.mino {
opacity: 50%;
}
.locked.mino {
animation: none;
}
.hard-dropped-table-animation {
animation: hard-dropped-table-animation steps(1) .2s;
}
@keyframes cleared-line-animation {
10%, 30%, 50%, 70%, 90% {
opacity: 0;
}
20%, 40%, 60%, 80% {
opacity: 100%;
}
}
tr.cleared-line-animation {
animation: cleared-line-animation ease-out .4s;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

+146 -80
View File
@@ -1,6 +1,15 @@
:root {
--rbw: 4px;
--tl: calc(-1 * var(--rbw));
--cell-size-opposite: calc(-1 * var(--cell-size));
--t3d: translate3d(var(--tl), var(--tl), var(--cell-size-opposite));
}
body { body {
background-image: url(stereo/bg.jpg), background-image:
radial-gradient(circle at center, url(stereo/bg.jpg),
radial-gradient(
circle at center,
#39444f 0%, #39444f 0%,
#2c323b 25%, #2c323b 25%,
#293036 28%, #293036 28%,
@@ -11,7 +20,8 @@ body {
#151519 63%, #151519 63%,
#141418 65%, #141418 65%,
#0f0f12 74%, #0f0f12 74%,
#0a0c0d 100%); #0a0c0d 100%
);
background-repeat: space; background-repeat: space;
background-position: center; background-position: center;
background-size: cover; background-size: cover;
@@ -26,17 +36,25 @@ body {
cursor: grabbing; cursor: grabbing;
} }
#sceneDiv * {
transform-style: preserve-3d;
}
#screenRow { #screenRow {
--light-rX: calc(-1 * var(--rY) / 30);
--light-rY: calc(var(--rX) / 20);
display: block; display: block;
transform: translateZ(var(--tZ)) rotateX(calc((var(--rX)) * 1deg)) rotateY(calc((var(--rY)) * 1deg)); transform: translateZ(var(--tZ)) rotateX(calc((var(--rX)) * 1deg))
rotateY(calc((var(--rY)) * 1deg));
} }
#screenRow * { #sceneDiv,
#screenRow,
#screenRow .col,
#screenRow .card,
#screenRow .card-body,
.minoes-table,
.minoes-table tbody,
.minoes-table tr,
.minoes-table td {
display: block; display: block;
transform-style: preserve-3d;
} }
#screenRow .col { #screenRow .col {
@@ -55,7 +73,7 @@ body {
} }
#screenRow .card > * { #screenRow .card > * {
transform: translateZ(var(--cell-side)); transform: translateZ(var(--cell-size));
} }
#screenRow .card-header { #screenRow .card-header {
@@ -65,8 +83,7 @@ body {
.card, .card,
.card-header { .card-header {
text-shadow: text-shadow: calc(-0.3px * var(--rY)) calc(0.4px * var(--rX)) 5px #0008;
calc(-0.3px * var(--rY)) calc(0.4px * var(--rX)) 5px #0008;
} }
#holdTable .mino { #holdTable .mino {
@@ -87,7 +104,7 @@ body {
.minoes-table tr { .minoes-table tr {
width: max-content; width: max-content;
height: var(--cell-side); height: var(--cell-size);
} }
#statsTable tr { #statsTable tr {
@@ -106,27 +123,30 @@ tr.matrix td:not(.mino) {
} }
.minoes-table td { .minoes-table td {
width: var(--cell-side) !important; width: var(--cell-size);
height: var(--cell-side) !important; height: var(--cell-size);
overflow: visible; overflow: visible;
position: relative;
} }
.minoes-table .mino, .mino,
.minoes-table .mino::before, .mino::before,
.minoes-table .mino + :not(.mino)::before, .mino + :not(.mino)::before,
.minoes-table .mino::after { .mino::after {
--light-x: calc(-0.5 - var(--rY) / 30 - var(--column) / 10 + 1); --light-pX: calc(0.5 - var(--column) / 10);
--light-y: calc(-0.5 + var(--rX) / 20 - var(--row) / 6 + 4); --light-pY: calc(3.5 - var(--row) / 6);
--light-x: calc(var(--light-rX) + var(--light-pX));
--light-y: calc(var(--light-rY) + var(--light-pY));
--center-color: hsla(var(--h), var(--s), calc(var(--l) * var(--light) * 1.1), var(--a)); --center-color: hsla(var(--h), var(--s), calc(var(--l) * var(--light) * 1.1), var(--a));
--edge-color: hsla(var(--h), var(--s), calc(var(--l) * (var(--light) * 0.9)), var(--a)); --edge-color: hsla(var(--h), var(--s), calc(var(--l) * (var(--light) * 0.9)), var(--a));
background: var(--center-color); background: var(--center-color);
border-radius: 2px; border-radius: 4px;
border: 2px outset var(--center-color); border: 2px outset var(--center-color);
} }
.minoes-table .mino::before, .mino::before,
.minoes-table .mino + :not(.mino)::before, .mino + :not(.mino)::before,
.minoes-table .mino::after, .mino::after,
td.trail-animation::before, td.trail-animation::before,
td.trail-animation::after, td.trail-animation::after,
tr.cleared-line-animation td::before, tr.cleared-line-animation td::before,
@@ -136,18 +156,14 @@ tr.cleared-line-animation td::after {
top: 0; top: 0;
left: 0; left: 0;
display: block; display: block;
width: var(--cell-side); width: var(--cell-size);
height: var(--cell-side); height: var(--cell-size);
} }
/* Front face */ /* Front face */
.minoes-table .mino, .mino,
td.trail-animation { td.trail-animation {
--light: calc( --light: calc(1 + (var(--light-y) * 0.3) + (var(--light-x) * 0.2));
1
+ (var(--light-y) * 0.3)
+ (var(--light-x) * 0.2)
);
--center-x: calc(35% + var(--light-x) * 10%); --center-x: calc(35% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%); --center-y: calc(35% + var(--light-y) * 10%);
background: radial-gradient( background: radial-gradient(
@@ -155,83 +171,131 @@ td.trail-animation {
var(--center-color) 15%, var(--center-color) 15%,
var(--edge-color) 100% var(--edge-color) 100%
); );
box-shadow: 0 0 7px hsla(var(--h), var(--s), calc(var(--l) * var(--light) * 1.3), 20%); box-shadow: 0 0 6px hsla(var(--h), var(--s), calc(var(--l) * var(--light) * 1.3), 40%);
border-style: ridge;
border-width: var(--rbw);
} }
/* Left face */ /* Left face */
.minoes-table .mino::before, .mino::before,
td.trail-animation::before, td.trail-animation::before,
tr.cleared-line-animation td::before, tr.cleared-line-animation td::before,
.left .minoes-table .mino + .mino::before { .mino + .mino::before {
--light: calc( --light: calc(1.1 + (var(--light-x) * -0.2) + (var(--light-y) * 0.15));
1.1 transform: var(--t3d) rotateY(-90deg);
+ (var(--light-x) * -0.2)
+ (var(--light-y) * 0.15)
);
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateY(-90deg);
transform-origin: left; transform-origin: left;
} }
/* Right face */ /* Right face */
.right .minoes-table .mino + .mino::before, .mino + :not(.mino)::before,
.minoes-table .mino + :not(.mino)::before, .right .mino + .mino::before,
.right td.trail-animation::before, .right td.trail-animation::before,
.right tr.cleared-line-animation td::before { .right tr.cleared-line-animation td::before {
--light: calc( --light: calc(0.85 + (var(--light-x) * -0.2) + (var(--light-y) * -0.15));
0.85
+ (var(--light-x) * -0.2)
+ (var(--light-y) * -0.15)
);
--center-x: calc(65% + var(--light-x) * 10%); --center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%); --center-y: calc(35% + var(--light-y) * 10%);
filter: saturate(0.95); filter: saturate(0.95);
transform: translate3d(0, 0, calc(-1 * var(--cell-side))) rotateY(-90deg); transform: translate3d(0, 0, var(--cell-size-opposite)) rotateY(-90deg);
transform-origin: left; transform-origin: left;
} }
.right .minoes-table .mino:last-child::before { .right .mino:last-child::before {
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateY(90deg) !important; transform: var(--t3d) rotateY(90deg) !important;
transform-origin: right !important; transform-origin: right !important;
} }
/* Top face */ /* Top face */
.minoes-table .mino::after, .mino::after,
td.trail-animation::after, td.trail-animation::after,
tr.cleared-line-animation td::after { tr.cleared-line-animation td::after {
--light: calc( --light: calc(1.5 + (var(--light-y) * 0.2));
1.5 transform: var(--t3d) rotateX(90deg);
+ (var(--light-y) * 0.2)
);
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateX(90deg);
transform-origin: top; transform-origin: top;
} }
/* Bottom face */ /* Bottom face */
.bottom .minoes-table .mino::after, .bottom .mino::after,
.bottom td.trail-animation::after, .bottom td.trail-animation::after,
.bottom tr.cleared-line-animation td::after { .bottom tr.cleared-line-animation td::after {
--light: calc( --light: calc(1.1 + (var(--light-y) * -0.3));
1.1
+ (var(--light-y) * -0.3)
);
--center-x: calc(65% + var(--light-x) * 10%); --center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(65% + var(--light-y) * 10%); --center-y: calc(65% + var(--light-y) * 10%);
filter: saturate(0.95); filter: saturate(0.95);
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateX(-90deg); transform: var(--t3d) rotateX(-90deg);
transform-origin: bottom; transform-origin: bottom;
} }
.J.mino, .J.mino + :not(.mino) { --h: 210deg; --s: 78%; --l: 52%; --a: 0.75; } .J,
.L.mino, .L.mino + :not(.mino) { --h: 28deg; --s: 85%; --l: 52%; --a: 0.75; } .J + :not(.mino) {
.O.mino, .O.mino + :not(.mino) { --h: 48deg; --s: 88%; --l: 52%; --a: 0.75; } --h: 210deg;
.I.mino, .I.mino + :not(.mino) { --h: 200deg; --s: 70%; --l: 52%; --a: 0.75; } --s: 78%;
.S.mino, .S.mino + :not(.mino) { --h: 118deg; --s: 45%; --l: 52%; --a: 0.75; } --l: 52%;
.T.mino, .T.mino + :not(.mino) { --h: 293deg; --s: 48%; --l: 52%; --a: 0.75; } --a: 0.75;
.Z.mino, .Z.mino + :not(.mino) { --h: 352deg; --s: 75%; --l: 52%; --a: 0.75; } }
.L,
.L + :not(.mino) {
--h: 28deg;
--s: 85%;
--l: 52%;
--a: 0.75;
}
.O,
.O + :not(.mino) {
--h: 48deg;
--s: 88%;
--l: 52%;
--a: 0.75;
}
.I,
.I + :not(.mino) {
--h: 200deg;
--s: 70%;
--l: 52%;
--a: 0.75;
}
.S,
.S + :not(.mino) {
--h: 118deg;
--s: 45%;
--l: 52%;
--a: 0.75;
}
.T,
.T + :not(.mino) {
--h: 293deg;
--s: 48%;
--l: 52%;
--a: 0.75;
}
.Z,
.Z + :not(.mino) {
--h: 352deg;
--s: 75%;
--l: 52%;
--a: 0.75;
}
.ghost.mino, .ghost.mino + :not(.mino) { --h: 0deg; --s: 0%; --l: 55%; --a: 0.40; } .ghost.mino,
.locking.mino, .locking.mino + :not(.mino) { --h: 0deg; --s: 0%; --l: 92%; --a: 0.72; } .ghost.mino + :not(.mino) {
.disabled.mino, .disabled.mino + :not(.mino) { --h: 0deg; --s: 0%; --l: 45%; --a: 0.72; } --h: 0deg;
--s: 0%;
--l: 55%;
--a: 0.4;
}
.locking.mino,
.locking.mino + :not(.mino) {
--h: 0deg;
--s: 0%;
--l: 92%;
--a: 0.72;
}
.disabled.mino,
.disabled.mino + :not(.mino) {
--h: 0deg;
--s: 0%;
--l: 45%;
--a: 0.72;
}
#holdTable .J + :not(.mino), #holdTable .J + :not(.mino),
#holdTable .L + :not(.mino), #holdTable .L + :not(.mino),
@@ -259,12 +323,15 @@ tr.cleared-line-animation td::after {
td.trail-animation::before, td.trail-animation::before,
td.trail-animation::after { td.trail-animation::after {
animation: trail-animation ease-out .3s; animation: trail-animation ease-out 0.3s;
} }
@keyframes locked-animation { @keyframes locked-animation {
from { from {
--h: 0deg; --s: 0%; --l: 100%; --a: 1; --h: 0deg;
--s: 0%;
--l: 100%;
--a: 1;
box-shadow: 0 0 10px hsla(180, 100%, 100%, 0.2); box-shadow: 0 0 10px hsla(180, 100%, 100%, 0.2);
} }
} }
@@ -272,7 +339,7 @@ td.trail-animation::after {
.locked.mino::before, .locked.mino::before,
.locked.mino::after { .locked.mino::after {
animation: locked-animation; animation: locked-animation;
animation-duration: .2s; animation-duration: 0.2s;
} }
@keyframes cleared-line-animation { @keyframes cleared-line-animation {
@@ -288,7 +355,7 @@ td.trail-animation::after {
tr.cleared-line-animation td::before, tr.cleared-line-animation td::before,
tr.cleared-line-animation td::after { tr.cleared-line-animation td::after {
animation: cleared-line-animation ease-out .3s; animation: cleared-line-animation ease-out 0.3s;
} }
@keyframes show-level-animation { @keyframes show-level-animation {
@@ -340,8 +407,7 @@ tr.cleared-line-animation td::after {
} }
30% { 30% {
transform: translateZ(0); transform: translateZ(0) scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
} }
80% { 80% {
+43
View File
@@ -0,0 +1,43 @@
tr.matrix td:not(.mino),
tr.matrix td.mino {
border-left: none;
border-right: 1px solid #333;
border-top: 1px solid #333;
border-bottom: none;
}
.mino {
background-size: cover
}
.I {
background-image: url(https://www.svgrepo.com/show/395902/blue-square.svg)
}
.J {
background-image: url(https://www.svgrepo.com/show/395944/brown-square.svg);
}
.L {
background-image: url(https://www.svgrepo.com/show/397681/orange-square.svg);
}
.O {
background-image: url(https://www.svgrepo.com/show/398716/yellow-square.svg);
}
.S {
background-image: url(https://www.svgrepo.com/show/396582/green-square.svg);
}
.T {
background-image: url(https://www.svgrepo.com/show/398143/purple-square.svg);
}
.Z {
background-image: url(https://www.svgrepo.com/show/397699/red-square.svg);
}
.ghost {
background-image: url(https://www.svgrepo.com/show/398610/white-large-square.svg);
}
+18 -5
View File
@@ -1,9 +1,9 @@
body { body {
background-image: url(synthwave/bg.png); background-image: url(synthwave/wtCSusF.jpeg);
background-size: cover; background-size: cover;
} }
body[data-bs-theme="dark"] { body[data-bs-theme='dark'] {
--bs-body-bg: #2125296b; --bs-body-bg: #2125296b;
} }
@@ -21,18 +21,31 @@ body[data-bs-theme="dark"] {
.card, .card,
#matrixCard { #matrixCard {
background: repeating-linear-gradient(transparent, #111 1px); background: repeating-linear-gradient(transparent, transparent 2px, #1114 2px, #1114 5px);
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
} }
.minoes-table { .minoes-table {
background: transparent; background: transparent;
border-spacing: 2px;
}
#matrixTable {
margin-top: calc(-1 * var(--buffer-zone-rows) * var(--cell-size) - 8px);
}
#matrixTable td:not(.mino) {
border-color: #8881;
border-top: none;
border-bottom: none;
} }
.mino { .mino {
background: var(--color); background: var(--color);
border: 3px solid var(--border); border: 3px solid var(--border);
border-radius: 6px;
box-shadow: 0 0 8px var(--border); box-shadow: 0 0 8px var(--border);
filter: blur(0.5px);
} }
.I { .I {
@@ -66,8 +79,8 @@ body[data-bs-theme="dark"] {
} }
.Z { .Z {
--color: #E67D8666; --color: #e67d8666;
--border: #E67D86; --border: #e67d86;
} }
.ghost { .ghost {
Binary file not shown.

After

Width:  |  Height:  |  Size: 972 KiB

+68
View File
@@ -0,0 +1,68 @@
:root {
--cell-size: 30px;
--sprite-size: round(100% / 11, 1px);
--skin-url: url(https://you.have.fail/ed/at/tetrioplus/data/content/skin/Haley%20Halcyon/tetrio_gameboy_plus.svg);
}
tr.matrix td:not(.mino) {
border-left: none;
border-bottom: none;
}
.mino {
background-image: var(--skin-url);
background-size: cover;
background-repeat: no-repeat;
background-position-x: calc(var(--sprite-pos) * var(--sprite-size));
}
.I {
--sprite-pos: 4;
}
.J {
--sprite-pos: 5;
}
.L {
--sprite-pos: 1;
}
.O {
--sprite-pos: 2;
}
.S {
--sprite-pos: 3;
}
.T {
--sprite-pos: 6;
}
.Z {
--sprite-pos: 0;
}
.ghost {
--sprite-pos: 7;
opacity: 40%;
}
.disabled {
--sprite-pos: 8;
}
.locking.mino {
filter: saturate(60%) brightness(180%);
}
#holdTable .mino,
#nextTable .mino {
box-shadow: 4px 4px 10px #0002;
}
.preview {
--cell-size: 24px;
height: var(--cell-size);
}
+93 -27
View File
@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="fr"> <html lang="fr" data-bs-theme="dark">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -8,15 +8,23 @@
<meta name="color-scheme" content="dark"> <meta name="color-scheme" content="dark">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css"/>
<link rel="stylesheet" href="css/classic.css" title="Thème sélectionné" id="selectedStyleSheet"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
<link rel="stylesheet" href="css/_select2-dark.css">
<link rel="stylesheet" href="css/_common.css">
<link rel="stylesheet" href="css/jstris-skin.css" title="Thème sélectionné" id="selectedStyleSheet">
<link rel="alternate stylesheet" href="css/jstris-skin.css" title="Sample...">
<link rel="alternate stylesheet" href="css/classic.css" title="Classique"> <link rel="alternate stylesheet" href="css/classic.css" title="Classique">
<link rel="alternate stylesheet" href="css/minimal.css" title="Minimal"> <link rel="alternate stylesheet" href="css/neo-classic.css" title="Néo-classique">
<link rel="alternate stylesheet" href="css/electro.css" title="Électro"> <link rel="alternate stylesheet" href="css/electro.css" title="Électro">
<link rel="alternate stylesheet" href="css/synthwave.css" title="Synthwave"> <link rel="alternate stylesheet" href="css/synthwave.css" title="Synthwave">
<link rel="alternate stylesheet" href="css/retro.css" title="Rétro"> <link rel="alternate stylesheet" href="css/heavy-metal.css" title="Heavy metal">
<link rel="alternate stylesheet" href="css/opera.css" title="Opéra"> <link rel="alternate stylesheet" href="css/jazz.css" title="Jazz">
<link rel="alternate stylesheet" href="css/stereo.css" title="Stéréo"> <link rel="alternate stylesheet" href="css/old-school.css" title="Old School">
<link rel="alternate stylesheet" href="css/stereo.css" title="Stéréo (3D)">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.1.0-rc.0/js/select2.min.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="favicons/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicons/T-2.png"> <link rel="icon" type="image/png" sizes="32x32" href="favicons/T-2.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicons/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="favicons/favicon-16x16.png">
@@ -27,12 +35,12 @@
<meta property="og:image" content="https://adrien.malingrey.fr/jeux/quatuor/thumbnail.png"/> <meta property="og:image" content="https://adrien.malingrey.fr/jeux/quatuor/thumbnail.png"/>
<meta property="og:image:width" content="288"/> <meta property="og:image:width" content="288"/>
<meta property="og:image:height" content="288"/> <meta property="og:image:height" content="288"/>
<meta property="og:description" content="Un jeu avec un quatuor de blocs qui tombent."/> <meta property="og:description" content="Un jeu de quatuors de blocs qui tombent."/>
<meta property="og:locale" content="fr_FR"/> <meta property="og:locale" content="fr_FR"/>
<meta property="og:site_name" content="adrien.malingrey.fr"/> <meta property="og:site_name" content="adrien.malingrey.fr"/>
</head> </head>
<body data-bs-theme="dark"> <body>
<div class="modal fade" id="settingsModal" data-bs-backdrop="static" data-bs-keyboard="false"> <div class="modal fade" id="settingsModal" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
@@ -60,7 +68,8 @@
<div class="col-4"><input name="pause" id="pauseInput" type="text" class="form-control text-center" value="Échap." onfocus="changeKey(this)" placeholder="Touche ?" title="Modifier la touche" required></div> <div class="col-4"><input name="pause" id="pauseInput" type="text" class="form-control text-center" value="Échap." onfocus="changeKey(this)" placeholder="Touche ?" title="Modifier la touche" required></div>
<label for="pauseInput" title="Pause" class="col-2 col-form-label d-flex align-items-center justify-content-center"><i class="bi bi-pause"></i></label> <label for="pauseInput" title="Pause" class="col-2 col-form-label d-flex align-items-center justify-content-center"><i class="bi bi-pause"></i></label>
</fieldset> </fieldset>
<fieldset id="autorepeatFieldset" class="row g-2 mb-3 align-items-center text-center"><!--<legend class="text-start">Répétition automatique</legend>--> <fieldset id="autorepeatFieldset" class="row g-2 mb-3 align-items-center text-center">
<!--<legend class="text-start">Répétition automatique</legend>-->
<label for="arrInput" class="col-2 col-form-label"><abbr title="Automatic Repeat Rate : période de répétition de l'action">ARR</abbr></label> <label for="arrInput" class="col-2 col-form-label"><abbr title="Automatic Repeat Rate : période de répétition de l'action">ARR</abbr></label>
<div class="col-4"><div class="input-group"><input name="arr" id="arrInput" type="number" class="form-control text-center" value="50" min="2" max="200" step="1"><div class="input-group-text">ms</div></div></div> <div class="col-4"><div class="input-group"><input name="arr" id="arrInput" type="number" class="form-control text-center" value="50" min="2" max="200" step="1"><div class="input-group-text">ms</div></div></div>
<div class="col-4"><div class="input-group"><input name="das" id="dasInput" type="number" class="form-control text-center" value="300" min="100" max="500" step="5"><div class="input-group-text">ms</div></div></div> <div class="col-4"><div class="input-group"><input name="das" id="dasInput" type="number" class="form-control text-center" value="300" min="100" max="500" step="5"><div class="input-group-text">ms</div></div></div>
@@ -68,15 +77,28 @@
</fieldset> </fieldset>
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Interface</legend> <fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Interface</legend>
<label for="stylesheetSelect" class="col-2 col-form-label">Thème</label> <label for="stylesheetSelect" class="col-2 col-form-label">Thème</label>
<div class="col-4"><select name="stylesheet" id="stylesheetSelect" class="form-select" oninput="selectedStyleSheet.href = this.value"> <div class="col-4">
<option value="css/classic.css" selected>Classique</option> <select name="stylesheet" id="stylesheetSelect" class="form-select">
<option value="css/minimal.css">Minimal</option> <option value="css/jstris-skin.css" selected>Sample JStris...</option>
<option value="css/tetrio-skin.css">Sample Tetr.io...</option>
<option value="css/classic.css">Classique</option>
<option value="css/neo-classic.css">Néo-classique</option>
<option value="css/synthwave.css">Synthwave</option> <option value="css/synthwave.css">Synthwave</option>
<option value="css/electro.css">Électro</option> <option value="css/electro.css">Électro</option>
<option value="css/retro.css">Rétro</option> <option value="css/heavy-metal.css">Heavy metal</option>
<option value="css/opera.css">Opéra</option> <option value="css/jazz.css">Jazz</option>
<option value="css/stereo.css">Stéréo</option> <option value="css/old-school.css">Old School</option>
</select></div> <option value="css/stereo.css">Stéréo (3D)</option>
</select>
</div>
<div class="col-4">
<select name="skinURL" id="skinURLSelect" class="form-select"
oninput="document.documentElement.style.setProperty('--skin-url', `url(${this.value})`)">
</select>
</div>
<label for="skinURLSelect" class="col-2 col-form-label">Sample</label>
<label for="sfxVolumeRange" class="col-2 col-form-label">Volume</label>
<div class="col-4 d-flex align-items-baseline"><input name="sfxVolumeRange" id="sfxVolumeRange" class="form-range" type="range" min="0" max="1" step="any" value="0.7"></div>
<div class="col-4"> <div class="col-4">
<div class="form-check form-switch text-start"> <div class="form-check form-switch text-start">
<input id="fullscreenCheckbox" type="checkbox" role="switch" class="form-check-input" tabindex="0"> <input id="fullscreenCheckbox" type="checkbox" role="switch" class="form-check-input" tabindex="0">
@@ -84,8 +106,6 @@
</div> </div>
</div> </div>
<div class="col-2"></div> <div class="col-2"></div>
<label for="sfxVolumeRange" class="col-2 col-form-label">Volume</label>
<div class="col-4 d-flex align-items-baseline"><input name="sfxVolumeRange" id="sfxVolumeRange" class="form-range" type="range" min="0" max="1" step="any" value="0.7"></div>
</fieldset> </fieldset>
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Partie</legend> <fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Partie</legend>
<label for="levelInput" class="col-2 col-form-label text-center">Niveau</label> <label for="levelInput" class="col-2 col-form-label text-center">Niveau</label>
@@ -193,7 +213,7 @@
</div> </div>
<div class="modal fade" id="statsModal" data-bs-backdrop="static" tabindex="-1"> <div class="modal fade" id="statsModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
@@ -204,7 +224,7 @@
<div class="modal-body p-0"> <div class="modal-body p-0">
<table class="table mb-0"> <table class="table mb-0">
<tr><th>Score</th> <td id="statsModalScoreCell"></td> <th>Quatuors</th> <td id="statsModalNbQuatuors"></td> </tr> <tr><th>Score</th> <td id="statsModalScoreCell"></td> <th>Quatuors</th> <td id="statsModalNbQuatuors"></td> </tr>
<tr><th>Meilleur score</th><td id="statsModalHighScoreCell"></td> <th>Pirouettes</th> <td id="statsModalNbTSpin"></td></td> </tr> <tr><th>Meilleur score</th><td id="statsModalHighScoreCell"></td> <th>Pirouettes</th> <td id="statsModalNbTSpin"></td> </tr>
<tr><th>Temps</th> <td id="statsModalTimeCell"></td> <th>Plus long combo</th> <td id="statsModalMaxCombo"></td> </tr> <tr><th>Temps</th> <td id="statsModalTimeCell"></td> <th>Plus long combo</th> <td id="statsModalMaxCombo"></td> </tr>
<tr><th>Niveau</th> <td id="statsModalLevelCell"></td> <th>Plus long bout à bout</th><td id="statsModalMaxB2B"></td> </tr> <tr><th>Niveau</th> <td id="statsModalLevelCell"></td> <th>Plus long bout à bout</th><td id="statsModalMaxB2B"></td> </tr>
<tr><th>Lignes</th> <td id="statsModaltotalClearedLines"></td><th>Lignes par minute</th> <td id="statsModaltotalClearedLinesPM"></td></tr> <tr><th>Lignes</th> <td id="statsModaltotalClearedLines"></td><th>Lignes par minute</th> <td id="statsModaltotalClearedLinesPM"></td></tr>
@@ -227,14 +247,60 @@
<img src="favicons/T-0.png"/><img src="favicons/T-1.png"/><img src="favicons/T-2.png"/><img src="favicons/T-3.png"/> <img src="favicons/T-0.png"/><img src="favicons/T-1.png"/><img src="favicons/T-2.png"/><img src="favicons/T-3.png"/>
<img src="favicons/Z-0.png"/><img src="favicons/Z-1.png"/><img src="favicons/Z-2.png"/><img src="favicons/Z-3.png"/> <img src="favicons/Z-0.png"/><img src="favicons/Z-1.png"/><img src="favicons/Z-2.png"/><img src="favicons/Z-3.png"/>
<audio id="wallSound" src="sounds/808K_A.wav" preload="auto" type="audio/wav"></audio> <audio id = "btb_1" src = "snd/btb_1.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id="hardDropSound" src="sounds/909S.wav" preload="auto" type="audio/wav"></audio> <audio id = "btb_2" src = "snd/btb_2.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id="lineClearSound" src="sounds/808COW.wav" preload="auto" type="audio/wav"></audio> <audio id = "btb_3" src = "snd/btb_3.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id="tSpinSound" src="sounds/78GUIR.wav" preload="auto" type="audio/wav"></audio> <audio id = "btb_break" src = "snd/btb_break.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id="quatuorSound" src="sounds/BRRDC1.wav" preload="auto" type="audio/wav"></audio> <audio id = "clearbtb" src = "snd/clearbtb.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "clearline" src = "snd/clearline.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "clearquad" src = "snd/clearquad.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "clearspin" src = "snd/clearspin.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "combo_1_power" src = "snd/combo_1_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_1" src = "snd/combo_1.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_10_power" src = "snd/combo_10_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_10" src = "snd/combo_10.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_11_power" src = "snd/combo_11_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_11" src = "snd/combo_11.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_12_power" src = "snd/combo_12_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_12" src = "snd/combo_12.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_13_power" src = "snd/combo_13_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_13" src = "snd/combo_13.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_14_power" src = "snd/combo_14_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_14" src = "snd/combo_14.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_15_power" src = "snd/combo_15_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_15" src = "snd/combo_15.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_16_power" src = "snd/combo_16_power.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "combo_16" src = "snd/combo_16.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "combo_2_power" src = "snd/combo_2_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_2" src = "snd/combo_2.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_3_power" src = "snd/combo_3_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_3" src = "snd/combo_3.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_4_power" src = "snd/combo_4_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_4" src = "snd/combo_4.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_5_power" src = "snd/combo_5_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_5" src = "snd/combo_5.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_6_power" src = "snd/combo_6_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_6" src = "snd/combo_6.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_7_power" src = "snd/combo_7_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_7" src = "snd/combo_7.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_8_power" src = "snd/combo_8_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_8" src = "snd/combo_8.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_9_power" src = "snd/combo_9_power.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combo_9" src = "snd/combo_9.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "combobreak" src = "snd/combobreak.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "floor" src = "snd/floor.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "harddrop" src = "snd/harddrop.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "hit" src = "snd/hit.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "hold" src = "snd/hold.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "hyperalert" src = "snd/hyperalert.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "menuconfirm" src = "snd/menuconfirm.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "menuhover" src = "snd/menuhover.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "move" src = "snd/move.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "rotate" src = "snd/rotate.ogg" preload = "auto" type = "audio/ogg"></audio>
<audio id = "shatter" src = "snd/shatter.mp3" preload = "auto" type = "audio/mp3"></audio>
<audio id = "spin" src = "snd/spin.ogg" preload = "auto" type = "audio/ogg"></audio>
</span> </span>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<script src="js/game_logic.js" language="Javascript" type="text/javascript"></script> <script src="js/game_logic.js" language="Javascript" type="text/javascript"></script>
<script src="js/interface.js" language="Javascript" type="text/javascript"></script> <script src="js/interface.js" language="Javascript" type="text/javascript"></script>
<script src="js/app.js" language="Javascript" type="text/javascript"></script> <script src="js/app.js" language="Javascript" type="text/javascript"></script>
+273 -187
View File
@@ -1,210 +1,189 @@
let scheduler = new Scheduler()
let settings = new Settings()
let stats = new Stats()
let holdQueue = new HoldQueue()
let matrix = new Matrix()
let nextQueue = new NextQueue()
let playing = false
//let lastActionSucceded = true
let favicon
window.onload = function(event) {
document.selectedStyleSheetSet = selectedStyleSheet.title
selectedStyleSheet.href = stylesheetSelect.value
favicon = document.querySelector("link[rel~='icon']")
fullscreenCheckbox.onchange = function() {
if (this.checked) {
document.documentElement.requestFullscreen()
} else {
document.exitFullscreen()
}
}
restart()
}
document.onfullscreenchange = function () { document.onfullscreenchange = function () {
if (document.fullscreenElement) { if (document.fullscreenElement) {
fullscreenCheckbox.checked = true fullscreenCheckbox.checked = true;
} else { } else {
fullscreenCheckbox.checked = false fullscreenCheckbox.checked = false;
if (playing) pauseSettings() if (playing) pauseSettings();
}
} }
};
document.onfullscreenerror = function () { document.onfullscreenerror = function () {
fullscreenCheckbox.checked = false fullscreenCheckbox.checked = false;
} };
function restart() { function restart() {
stats.modal.hide() stats.modal.hide();
holdQueue.init() holdQueue.init();
holdQueue.redraw() holdQueue.redraw();
stats.init() stats.init();
matrix.init() matrix.init();
nextQueue.init() nextQueue.init();
settings.init() settings.init();
pauseSettings() pauseSettings();
} }
function pauseSettings() { function pauseSettings() {
scheduler.clearInterval(fall) scheduler.clearInterval(fall);
scheduler.clearTimeout(lockDown) scheduler.clearTimeout(lockDown);
scheduler.clearTimeout(repeat) scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat) scheduler.clearInterval(autorepeat);
scheduler.clearInterval(ticktack) scheduler.clearInterval(ticktack);
stats.pauseTime = stats.time stats.pauseTime = stats.time;
document.onkeydown = null document.onkeydown = null;
settings.show() settings.show();
playSound(menuhover)
} }
function newGame(event) { function newGame(event) {
if (!settings.form.checkValidity()) { if (!settings.form.checkValidity()) {
event.preventDefault() event.preventDefault();
event.stopPropagation() event.stopPropagation();
settings.form.reportValidity() settings.form.reportValidity();
settings.form.classList.add('was-validated') settings.form.classList.add('was-validated');
} else { } else {
const audioContext = new AudioContext() const audioContext = new AudioContext();
for(const sound of document.getElementsByTagName("audio")) { for (const sound of document.getElementsByTagName('audio')) {
sound.preservesPitch = false sound.preservesPitch = false;
audioContext.createMediaElementSource(sound).connect(audioContext.destination) audioContext.createMediaElementSource(sound).connect(audioContext.destination);
} }
levelInput.name = "level" levelInput.name = 'level';
levelInput.disabled = true levelInput.disabled = true;
titleHeader.innerHTML = "PAUSE" titleHeader.innerHTML = 'PAUSE';
resumeButton.innerHTML = "Reprendre" resumeButton.innerHTML = 'Reprendre';
event.target.onsubmit = resume event.target.onsubmit = resume;
stats.level = levelInput.valueAsNumber stats.level = levelInput.valueAsNumber;
localStorage["startLevel"] = levelInput.value localStorage['startLevel'] = levelInput.value;
playing = true playing = true;
onblur = pauseSettings onblur = pauseSettings;
resume(event) resume(event);
} }
} }
function resume(event) { function resume(event) {
event.preventDefault() event.preventDefault();
event.stopPropagation() event.stopPropagation();
settings.form.reportValidity() settings.form.reportValidity();
settings.form.classList.add('was-validated') settings.form.classList.add('was-validated');
if (settings.form.checkValidity()) { if (settings.form.checkValidity()) {
for(const sound of document.getElementsByTagName("audio")) for (const sound of document.getElementsByTagName('audio'))
sound.volume = sfxVolumeRange.value sound.volume = sfxVolumeRange.value;
settings.modal.hide() settings.modal.hide();
settings.getInputs() settings.getInputs();
document.onkeydown = onkeydown document.onkeydown = onkeydown;
document.onkeyup = onkeyup document.onkeyup = onkeyup;
stats.time = stats.pauseTime stats.time = stats.pauseTime;
scheduler.setInterval(ticktack, 1000) scheduler.setInterval(ticktack, 1000);
if (matrix.piece) scheduler.setInterval(fall, stats.fallPeriod) if (matrix.piece) scheduler.setInterval(fall, stats.fallPeriod);
else generate() else generate();
playSound(menuconfirm)
} }
} }
function ticktack() { function ticktack() {
timeCell.innerText = stats.timeFormat.format(stats.time) timeCell.innerText = stats.timeFormat.format(stats.time);
} }
function generate(piece) { function generate(piece) {
matrix.piece = piece || nextQueue.shift() matrix.piece = piece || nextQueue.shift();
if (!piece && holdQueue.piece) holdQueue.drawPiece() if (!piece && holdQueue.piece) holdQueue.drawPiece();
//lastActionSucceded = true //lastActionSucceded = true
favicon.href = matrix.piece.favicon_href favicon.href = matrix.piece.favicon_href;
if (matrix.piece.canMove(TRANSLATION.NONE)) { if (matrix.piece.canMove(TRANSLATION.NONE)) {
scheduler.setInterval(fall, stats.fallPeriod) scheduler.setInterval(fall, stats.fallPeriod);
} else { } else {
gameOver() // block out gameOver(); // block out
} }
} }
let playerActions = { let playerActions = {
moveLeft: () => matrix.piece.move(TRANSLATION.LEFT), moveLeft: () => matrix.piece.move(TRANSLATION.LEFT)? playSound(move) : playSound(hit),
moveRight: () => matrix.piece.move(TRANSLATION.RIGHT), moveRight: () => matrix.piece.move(TRANSLATION.RIGHT)? playSound(move) : playSound(hit),
rotateClockwise: () => matrix.piece.rotate(ROTATION.CW), rotateClockwise: () => matrix.piece.rotate(ROTATION.CW)? playSound(rotate) : playSound(hit),
rotateCounterclockwise: () => matrix.piece.rotate(ROTATION.CCW), rotateCounterclockwise: () => matrix.piece.rotate(ROTATION.CCW)? playSound(rotate) : playSound(hit),
softDrop: () => matrix.piece.move(TRANSLATION.DOWN) && ++stats.score, softDrop: () => (matrix.piece.move(TRANSLATION.DOWN) && ++stats.score)? playSound(move) : playSound(floor),
hardDrop: function () { hardDrop: function () {
scheduler.clearTimeout(lockDown) scheduler.clearTimeout(lockDown);
playSound(hardDropSound) playSound(harddrop);
while (matrix.piece.move(TRANSLATION.DOWN, ROTATION.NONE, true)) stats.score += 2 while (matrix.piece.move(TRANSLATION.DOWN, ROTATION.NONE, true)) stats.score += 2;
matrixCard.classList.remove("hard-dropped-table-animation") matrixCard.classList.remove('hard-dropped-table-animation');
matrixCard.offsetHeight; matrixCard.offsetHeight;
matrixCard.classList.add("hard-dropped-table-animation") // restart animation matrixCard.classList.add('hard-dropped-table-animation'); // restart animation
lockDown() lockDown();
return true return true;
}, },
hold: function () { hold: function () {
if (matrix.piece.holdEnabled) { if (matrix.piece.holdEnabled) {
scheduler.clearInterval(fall) scheduler.clearInterval(fall);
scheduler.clearTimeout(lockDown) scheduler.clearTimeout(lockDown);
playSound(hold)
let piece = matrix.piece let piece = matrix.piece;
piece.facing = FACING.NORTH piece.facing = FACING.NORTH;
piece.locked = false piece.locked = false;
generate(holdQueue.piece) generate(holdQueue.piece);
matrix.piece.holdEnabled = false matrix.piece.holdEnabled = false;
holdQueue.piece = piece holdQueue.piece = piece;
} }
}, },
pause: pauseSettings, pause: pauseSettings,
} };
// Handle player inputs // Handle player inputs
const REPEATABLE_ACTIONS = [ const REPEATABLE_ACTIONS = [
playerActions.moveLeft, playerActions.moveLeft,
playerActions.moveRight, playerActions.moveRight,
playerActions.softDrop playerActions.softDrop,
] ];
pressedKeys = new Set() pressedKeys = new Set();
actionsQueue = [] actionsQueue = [];
function onkeydown(event) { function onkeydown(event) {
if (event.key in settings.keyBind) { if (event.key in settings.keyBind) {
event.preventDefault() event.preventDefault();
if (!pressedKeys.has(event.key)) { if (!pressedKeys.has(event.key)) {
pressedKeys.add(event.key) pressedKeys.add(event.key);
action = settings.keyBind[event.key] action = settings.keyBind[event.key];
/*if (action()) { /*if (action()) {
lastActionSucceded = true lastActionSucceded = true
} else if (lastActionSucceded || !(action in REPEATABLE_ACTIONS)) { } else if (lastActionSucceded || !(action in REPEATABLE_ACTIONS)) {
playSound(wallSound) playSound(wallSound)
lastActionSucceded = false lastActionSucceded = false
}*/ }*/
action() action();
if (REPEATABLE_ACTIONS.includes(action)) { if (REPEATABLE_ACTIONS.includes(action)) {
actionsQueue.unshift(action) actionsQueue.unshift(action);
scheduler.clearTimeout(repeat) scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat) scheduler.clearInterval(autorepeat);
if (action == playerActions.softDrop) scheduler.setInterval(autorepeat, settings.fallPeriod/20) if (action == playerActions.softDrop)
else scheduler.setTimeout(repeat, settings.das) scheduler.setInterval(autorepeat, settings.fallPeriod / 20);
else scheduler.setTimeout(repeat, settings.das);
} }
matrix.drawPiece() matrix.drawPiece();
} }
} }
} }
function repeat() { function repeat() {
if (actionsQueue.length) { if (actionsQueue.length) {
actionsQueue[0]() actionsQueue[0]();
scheduler.setInterval(autorepeat, settings.arr) scheduler.setInterval(autorepeat, settings.arr);
} }
} }
@@ -216,121 +195,228 @@ function autorepeat() {
wallSound.play() wallSound.play()
lastActionSucceded = false lastActionSucceded = false
}*/ }*/
actionsQueue[0]() actionsQueue[0]();
} } else scheduler.clearInterval(autorepeat);
else scheduler.clearInterval(autorepeat)
} }
function onkeyup(event) { function onkeyup(event) {
if (event.key in settings.keyBind) { if (event.key in settings.keyBind) {
event.preventDefault() event.preventDefault();
pressedKeys.delete(event.key) pressedKeys.delete(event.key);
action = settings.keyBind[event.key] action = settings.keyBind[event.key];
if (actionsQueue.includes(action)) { if (actionsQueue.includes(action)) {
actionsQueue.splice(actionsQueue.indexOf(action), 1) actionsQueue.splice(actionsQueue.indexOf(action), 1);
scheduler.clearTimeout(repeat) scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat) scheduler.clearInterval(autorepeat);
if (actionsQueue.length) { if (actionsQueue.length) {
if (actionsQueue[0] == playerActions.softDrop) scheduler.setInterval(autorepeat, settings.fallPeriod/20) if (actionsQueue[0] == playerActions.softDrop)
else scheduler.setTimeout(repeat, settings.das) scheduler.setInterval(autorepeat, settings.fallPeriod / 20);
else scheduler.setTimeout(repeat, settings.das);
} else { } else {
matrix.drawPiece() matrix.drawPiece();
} }
} }
} }
} }
function fall() { function fall() {
matrix.piece.move(TRANSLATION.DOWN) matrix.piece.move(TRANSLATION.DOWN);
} }
function lockDown() { function lockDown() {
scheduler.clearTimeout(lockDown) scheduler.clearTimeout(lockDown);
scheduler.clearInterval(fall) scheduler.clearInterval(fall);
if (matrix.lock()) { if (matrix.lock()) {
stats.lockDown(matrix.piece.tSpin, matrix.clearLines()) stats.lockDown(matrix.piece.tSpin, matrix.clearLines());
generate() generate();
} else { } else {
gameOver() // lock out gameOver(); // lock out
} }
} }
onanimationend = function (event) { onanimationend = function (event) {
event.target.classList.remove(event.animationName) event.target.classList.remove(event.animationName);
} };
messagesSpan.onanimationend = function (event) { messagesSpan.onanimationend = function (event) {
event.target.remove() event.target.remove();
} };
function gameOver() { function gameOver() {
matrix.piece.locked = true matrix.piece.locked = true;
matrix.drawPiece() matrix.drawPiece();
document.onkeydown = null document.onkeydown = null;
onblur = null onblur = null;
scheduler.clearInterval(ticktack) scheduler.clearInterval(ticktack);
playing = false playing = false;
stats.show() stats.show();
} }
window.onbeforeunload = function (event) { window.onbeforeunload = function (event) {
stats.save() stats.save();
settings.save() settings.save();
if (playing) return false; if (playing) return false;
} };
// Play with 3D // Play with 3D
let mousedown = false let mousedown = false;
let rX0 = -15 let rX0 = -15;
let rY0 = 0 let rY0 = 0;
let clientX0 = 0 let clientX0 = 0;
let clientY0 = 0 let clientY0 = 0;
sceneDiv.onmousedown = function (event) { sceneDiv.onmousedown = function (event) {
mousedown = true mousedown = true;
rX0 = parseInt(getComputedStyle(screenRow).getPropertyValue("--rX")) rX0 = parseInt(getComputedStyle(screenRow).getPropertyValue('--rX'));
dy0 = parseInt(getComputedStyle(screenRow).getPropertyValue("--rY")) dy0 = parseInt(getComputedStyle(screenRow).getPropertyValue('--rY'));
clientX0 = event.clientX clientX0 = event.clientX;
clientY0 = event.clientY clientY0 = event.clientY;
} };
sceneDiv.onmousemove = function (event) { sceneDiv.onmousemove = function (event) {
if (mousedown) { if (mousedown) {
event.preventDefault() event.preventDefault();
event.stopPropagation() event.stopPropagation();
rX = (rX0 - 0.5 * (event.clientY - clientY0)) % 360 rX = (rX0 - 0.5 * (event.clientY - clientY0)) % 360;
screenRow.style.setProperty("--rX", rX) screenRow.style.setProperty('--rX', rX);
if (rX >= 0) { if (rX >= 0) {
screenRow.classList.remove("top") screenRow.classList.remove('top');
screenRow.classList.add("bottom") screenRow.classList.add('bottom');
} else { } else {
screenRow.classList.add("top") screenRow.classList.add('top');
screenRow.classList.remove("bottom") screenRow.classList.remove('bottom');
} }
rY = (rY0 + 0.5 * (event.clientX - clientX0)) % 360 rY = (rY0 + 0.5 * (event.clientX - clientX0)) % 360;
screenRow.style.setProperty("--rY", rY) screenRow.style.setProperty('--rY', rY);
if (rY <= 0) { if (rY <= 0) {
screenRow.classList.remove("left") screenRow.classList.remove('left');
screenRow.classList.add("right") screenRow.classList.add('right');
} else { } else {
screenRow.classList.add("left") screenRow.classList.add('left');
screenRow.classList.remove("right") screenRow.classList.remove('right');
}
} }
} }
};
sceneDiv.onmouseup = document.onmouseleave = function (event) { sceneDiv.onmouseup = document.onmouseleave = function (event) {
mousedown = false mousedown = false;
};
fullscreenCheckbox.onchange = function () {
if (this.checked) {
document.documentElement.requestFullscreen();
} else {
document.exitFullscreen();
} }
};
sceneDiv.onwheel = function (event) { sceneDiv.onwheel = function (event) {
event.preventDefault() event.preventDefault();
event.stopPropagation() event.stopPropagation();
let tZ = parseInt(getComputedStyle(screenRow).getPropertyValue("--tZ")) let tZ = parseInt(getComputedStyle(screenRow).getPropertyValue('--tZ'));
tZ -= event.deltaY tZ -= event.deltaY;
screenRow.style.setProperty("--tZ", tZ + "px") screenRow.style.setProperty('--tZ', tZ + 'px');
};
const ImageURLPattern = /^(https?:\/\/.*\.(?:png|jpg|jpeg|gif|bmp|webp|svg))$/i
$.fn.select2.defaults.set("templateResult", (state) =>
state.id
? $(`<img class="preview" src="${state.id}" title="${state.text}" loading="lazy"/>`)
: state.text
)
$.fn.select2.defaults.set("templateSelection", (state) =>
state.id
? $(`
<table class="minoes-table preview" style="--skin-url: url(${state.id});">
<tr><td class="Z mino"></td><td class="O mino"></td><td class="T mino"></td><td class="I mino"></td></tr>
</table>
`)
: state.text
)
$.fn.select2.defaults.set("theme", "bootstrap-5")
$.fn.select2.defaults.set("selectionCssClass", 'form-select')
$.fn.select2.defaults.set("dropdownParent", $('#settingsModal'))
$.fn.select2.defaults.set("dropdownAutoWidth", true)
$.fn.select2.defaults.set("placeholder", "URL de l'image")
$.fn.select2.defaults.set("tags", true)
$.fn.select2.defaults.set("createTag", function (params) {
const url = encodeURI(params.term);
if (ImageURLPattern.test(url)) {
return {
id: url,
text: 'Source externe',
newTag: true,
};
} }
});
stylesheetSelect.oninput = function (event) {
selectedStyleSheet.href = this.value;
$("#skinURLSelect").empty();
switch (this.value) {
case 'css/jstris-skin.css':
skinURLSelect.disabled = false;
fetch('https://konsola5.github.io/jstris-customization-database/jstrisCustomizationDatabase.json')
.then(response => response.json())
.then(json => {
const data = [];
for (const group in json) {
const groupData = {
text: group,
children: json[group].map(skin => ({
id: skin.link,
text: `${skin.name} (${skin.author})`,
})),
};
data.push(groupData);
}
$('#skinURLSelect').select2({data: data});
});
break;
case 'css/tetrio-skin.css':
skinURLSelect.disabled = false;
fetch("https://you.have.fail/tetrioplus/data/data.json")
.then((resp) => resp.json())
.then((json) => {
const data = json
.filter((item) => (item.type == "skin" && item.format == "tetrioraster"))
.map((skin) => {
return {
id: encodeURI(`https://you.have.fail/tetrioplus/data/${skin.path}`),
text:`${skin.name} (${skin.author})\n${skin.description}`
}
})
.filter((option) => ImageURLPattern.test(option.id))
$('#skinURLSelect').select2({data: data});
})
break;
default:
skinURLSelect.disabled = true;
break;
}
}
let scheduler = new Scheduler();
let settings = new Settings();
let stats = new Stats();
let holdQueue = new HoldQueue();
let matrix = new Matrix();
let nextQueue = new NextQueue();
let playing = false;
//let lastActionSucceded = true
let favicon = document.querySelector("link[rel~='icon']");
window.onload = function (event) {
restart();
};
+4 -4
View File
@@ -17,7 +17,7 @@ const T_SPIN = {
T_SPIN: "PIROUETTE" T_SPIN: "PIROUETTE"
} }
// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines] // score = AWARDED_LINE_CLEARS[tSpin][clearedLines]
const AWARDED_LINE_CLEARS = { const AWARDED_LINE_CLEARS = {
[T_SPIN.NONE]: [0, 1, 3, 5, 8], [T_SPIN.NONE]: [0, 1, 3, 5, 8],
[T_SPIN.MINI]: [1, 2], [T_SPIN.MINI]: [1, 2],
@@ -282,18 +282,18 @@ class Matrix extends MinoesTable {
} }
clearLines() { clearLines() {
let nbClearedLines = 0 let clearedLines = 0
for (let y=0; y<this.rows; y++) { for (let y=0; y<this.rows; y++) {
let row = this.blocks[y] let row = this.blocks[y]
if (row.filter(lockedMino => lockedMino).length == this.columns) { if (row.filter(lockedMino => lockedMino).length == this.columns) {
nbClearedLines++ clearedLines++
this.blocks.splice(y, 1) this.blocks.splice(y, 1)
this.blocks.unshift(Array(matrix.columns)) this.blocks.unshift(Array(matrix.columns))
this.table.rows[y].classList.add("cleared-line-animation") this.table.rows[y].classList.add("cleared-line-animation")
} }
} }
this.redraw() this.redraw()
return nbClearedLines return clearedLines
} }
} }
Matrix.prototype.init_center = [4, 4] Matrix.prototype.init_center = [4, 4]
+267 -200
View File
@@ -1,335 +1,402 @@
const KEY_NAMES = new Proxy({ const KEY_NAMES = new Proxy(
["ArrowLeft"] : "←", {
["←"] : "ArrowLeft", ['ArrowLeft']: '←',
["ArrowRight"] : "→", ['←']: 'ArrowLeft',
["→"] : "ArrowRight", ['ArrowRight']: '→',
["ArrowUp"] : "↑", ['→']: 'ArrowRight',
["↑"] : "ArrowUp", ['ArrowUp']: '↑',
["ArrowDown"] : "↓", ['↑']: 'ArrowUp',
["↓"] : "ArrowDown", ['ArrowDown']: '↓',
[" "] : "Espace", ['↓']: 'ArrowDown',
["Espace"] : " ", [' ']: 'Espace',
["Escape"] : "Échap.", ['Espace']: ' ',
["Échap."] : "Escape", ['Escape']: 'Échap.',
["Backspace"] : "Ret. arrière", ['Échap.']: 'Escape',
["Ret. arrière"]: "Backspace", ['Backspace']: 'Ret. arrière',
["Enter"] : "Entrée", ['Ret. arrière']: 'Backspace',
["Entrée"] : "Enter", ['Enter']: 'Entrée',
}, { ['Entrée']: 'Enter',
},
{
get(target, key) { get(target, key) {
return key in target? target[key] : key return key in target ? target[key] : key;
} },
}) },
);
class Settings { class Settings {
constructor() { constructor() {
this.form = settingsForm this.form = settingsForm;
this.load() this.load();
this.modal = new bootstrap.Modal('#settingsModal') this.modal = new bootstrap.Modal('#settingsModal');
settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus()) settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus());
} }
load() { load() {
this.form.querySelectorAll("[name]").forEach(element => { this.form.querySelectorAll('[name]').forEach(element => {
if (element.name in localStorage) if (element.name in localStorage) element.value = localStorage[element.name];
element.value = localStorage[element.name] });
}) stylesheetSelect.oninput();
window.document.selectedStyleSheetSet = stylesheetSelect.value if (localStorage['skinURL']) {
if ($('#skinURLSelect').find("option[value='" + localStorage['skinURL'] + "']").length) {
$('#skinURLSelect').val(localStorage['skinURL']).trigger('change');
} else {
var option = new Option(
'Source externe',
localStorage['skinURL'],
true,
true,
);
$('#skinURLSelect').append(option).trigger('change');
}
skinURLSelect.oninput();
}
} }
save() { save() {
this.form.querySelectorAll("[name]").forEach(element => localStorage[element.name] = element.value) this.form
.querySelectorAll('[name]')
.forEach(element => (localStorage[element.name] = element.value));
} }
init() { init() {
this.form.onsubmit = newGame this.form.onsubmit = newGame;
levelInput.name = "startLevel" levelInput.name = 'startLevel';
levelInput.disabled = false levelInput.disabled = false;
titleHeader.innerHTML = "QUATUOR" titleHeader.innerHTML = 'QUATUOR';
resumeButton.innerHTML = "Jouer" resumeButton.innerHTML = 'Jouer';
} }
show() { show() {
resumeButton.disabled = false resumeButton.disabled = false;
settings.form.classList.remove('was-validated') settings.form.classList.remove('was-validated');
settings.modal.show() settings.modal.show();
settings.form.reportValidity() settings.form.reportValidity();
} }
getInputs() { getInputs() {
for (let input of this.form.querySelectorAll("input[type='text']")) { for (let input of this.form.querySelectorAll("input[type='text']")) {
this[input.name] = KEY_NAMES[input.value] this[input.name] = KEY_NAMES[input.value];
} }
for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) { for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) {
this[input.name] = input.valueAsNumber this[input.name] = input.valueAsNumber;
} }
for (let input of this.form.querySelectorAll("input[type='checkbox']")) { for (let input of this.form.querySelectorAll("input[type='checkbox']")) {
this[input.name] = input.checked == true this[input.name] = input.checked == true;
} }
this.keyBind = new Proxy({}, { this.keyBind = new Proxy(
{},
{
get: (target, key) => target[key.toLowerCase()], get: (target, key) => target[key.toLowerCase()],
set: (target, key, value) => target[key.toLowerCase()] = value, set: (target, key, value) => (target[key.toLowerCase()] = value),
has: (target, key) => key.toLowerCase() in target has: (target, key) => key.toLowerCase() in target,
},
}) );
for (let actionName in playerActions) { for (let actionName in playerActions) {
this.keyBind[settings[actionName]] = playerActions[actionName] this.keyBind[settings[actionName]] = playerActions[actionName];
} }
} }
} }
function changeKey(input) { function changeKey(input) {
prevValue = input.value prevValue = input.value;
input.value = "" input.value = '';
keyInputs = Array.from(input.form.querySelectorAll("input[type='text']")) keyInputs = Array.from(input.form.querySelectorAll("input[type='text']"));
input.onkeydown = function (event) { input.onkeydown = function (event) {
event.preventDefault() event.preventDefault();
input.value = KEY_NAMES[event.key] input.value = KEY_NAMES[event.key];
keyInputs.forEach(input => { keyInputs.forEach(input => {
input.setCustomValidity("") input.setCustomValidity('');
input.classList.remove("is-invalid") input.classList.remove('is-invalid');
}) });
keyInputs.sort((input1, input2) => { keyInputs.sort((input1, input2) => {
if (input1.value == input2.value) { if (input1.value == input2.value) {
input1.setCustomValidity("Touche déjà utilisée") input1.setCustomValidity('Touche déjà utilisée');
input1.classList.add("is-invalid") input1.classList.add('is-invalid');
input2.setCustomValidity("Touche déjà utilisée") input2.setCustomValidity('Touche déjà utilisée');
input2.classList.add("is-invalid") input2.classList.add('is-invalid');
} }
return input1.value > input2.value return input1.value > input2.value;
}) });
if (input.checkValidity()) { if (input.checkValidity()) {
input.blur() input.blur();
}
} }
};
input.onblur = function (event) { input.onblur = function (event) {
if (!input.value) input.value = prevValue if (!input.value) input.value = prevValue;
input.onkeydown = null input.onkeydown = null;
input.onblur = null input.onblur = null;
};
} }
}
class Stats { class Stats {
constructor() { constructor() {
this.modal = new bootstrap.Modal('#statsModal') this.modal = new bootstrap.Modal('#statsModal');
this.load() this.load();
} }
load() { load() {
this.highScore = Number(localStorage["highScore"]) || 0 this.highScore = Number(localStorage['highScore']) || 0;
} }
init() { init() {
levelInput.value = localStorage["startLevel"] || 1 levelInput.value = localStorage['startLevel'] || 1;
this.score = 0 this.score = 0;
this.goal = 0 this.goal = 0;
this.combo = 0 this.combo = -1;
this.b2b = 0 this.b2b = -1;
this.startTime = new Date() this.startTime = new Date();
this.lockDelay = DELAY.LOCK this.lockDelay = DELAY.LOCK;
this.totalClearedLines = 0 this.totalClearedLines = 0;
this.nbQuatuors = 0 this.nbQuatuors = 0;
this.nbTSpin = 0 this.nbTSpin = 0;
this.maxCombo = 0 this.maxCombo = 0;
this.maxB2B = 0 this.maxB2B = 0;
} }
set score(score) { set score(score) {
this._score = score this._score = score;
scoreCell.innerText = score.toLocaleString() scoreCell.innerText = score.toLocaleString();
if (score > this.highScore) { if (score > this.highScore) {
this.highScore = score this.highScore = score;
} }
} }
get score() { get score() {
return this._score return this._score;
} }
set highScore(highScore) { set highScore(highScore) {
this._highScore = highScore this._highScore = highScore;
highScoreCell.innerText = highScore.toLocaleString() highScoreCell.innerText = highScore.toLocaleString();
} }
get highScore() { get highScore() {
return this._highScore return this._highScore;
} }
set level(level) { set level(level) {
this._level = level this._level = level;
this.goal += level * 5 this.goal += level * 5;
if (level <= 20) { if (level <= 20) {
this.fallPeriod = 1000 * Math.pow(0.8 - ((level - 1) * 0.007), level - 1) this.fallPeriod = 1000 * Math.pow(0.8 - (level - 1) * 0.007, level - 1);
} }
if (level > 15) if (level > 15) this.lockDelay = 500 * Math.pow(0.9, level - 15);
this.lockDelay = 500 * Math.pow(0.9, level - 15) levelInput.value = level;
levelInput.value = level levelCell.innerText = level;
levelCell.innerText = level messagesSpan.addNewChild('div', {
messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>` }) className: 'show-level-animation',
innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>`,
});
} }
get level() { get level() {
return this._level return this._level;
} }
set goal(goal) { set goal(goal) {
this._goal = goal this._goal = goal;
goalCell.innerText = goal goalCell.innerText = goal;
} }
get goal() { get goal() {
return this._goal return this._goal;
} }
set combo(combo) { set combo(combo) {
this._combo = combo this._combo = combo;
if (combo > this.maxCombo) this.maxCombo = combo if (combo > this.maxCombo) this.maxCombo = combo;
} }
get combo() { get combo() {
return this._combo return this._combo;
} }
set b2b(b2b) { set b2b(b2b) {
this._b2b = b2b this._b2b = b2b;
if (b2b > this.maxB2B) this.maxB2B = b2b if (b2b > this.maxB2B) this.maxB2B = b2b;
} }
get b2b() { get b2b() {
return this._b2b return this._b2b;
} }
set time(time) { set time(time) {
this.startTime = new Date() - time this.startTime = new Date() - time;
ticktack() ticktack();
} }
get time() { get time() {
return new Date() - this.startTime return new Date() - this.startTime;
} }
lockDown(tSpin, nbClearedLines) { lockDown(tSpin, clearedLines) {
this.totalClearedLines += nbClearedLines this.totalClearedLines += clearedLines;
if (nbClearedLines == 4) this.nbQuatuors++ if (clearedLines == 4) this.nbQuatuors++;
if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++ if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++;
// Cleared lines & T-Spin // Cleared lines & T-Spin
let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines] let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][clearedLines];
let patternScore = 100 * this.level * awardedLineClears let patternScore = 100 * this.level * awardedLineClears;
if (tSpin) messagesSpan.addNewChild("div", { if (tSpin)
className: "rotate-in-animation", messagesSpan.addNewChild('div', {
innerHTML: tSpin className: 'rotate-in-animation',
}) innerHTML: tSpin,
if (nbClearedLines) messagesSpan.addNewChild("div", { });
className: "zoom-in-animation", if (clearedLines)
innerHTML: CLEARED_LINES_NAMES[nbClearedLines] messagesSpan.addNewChild('div', {
}) className: 'zoom-in-animation',
innerHTML: CLEARED_LINES_NAMES[clearedLines],
});
if (patternScore) { if (patternScore) {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .2s", style: 'animation-delay: .2s',
innerHTML: patternScore innerHTML: patternScore,
}) });
this.score += patternScore this.score += patternScore;
} }
// Combo // Combo
if (nbClearedLines) { if (clearedLines) {
this.combo++ this.combo++;
if (this.combo >= 1) { if (this.combo >= 1) {
let comboScore = (nbClearedLines == 1 ? 20 : 50) * this.combo * this.level let comboScore = (clearedLines == 1 ? 20 : 50) * this.combo * this.level;
if (this.combo == 1) { if (this.combo == 1) {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .4s", style: 'animation-delay: .4s',
innerHTML: `COMBO<br/>${comboScore}` innerHTML: `COMBO<br/>${comboScore}`,
}) });
} else { } else {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .4s", style: 'animation-delay: .4s',
innerHTML: `COMBO x${this.combo}<br/>${comboScore}` innerHTML: `COMBO x${this.combo}<br/>${comboScore}`,
}) });
} }
this.score += comboScore this.score += comboScore;
} }
} else { } else {
this.combo = -1 if (this.combo >= 1) playSound(combobreak)
this.combo = -1;
} }
// Back to back sequence // Back to back sequence
if ((nbClearedLines == 4) || (tSpin && nbClearedLines)) { if (clearedLines == 4 || (tSpin && clearedLines)) {
this.b2b++ this.b2b++;
if (this.b2b >= 1) { if (this.b2b >= 1) {
let b2bScore = patternScore / 2 let b2bScore = patternScore / 2;
if (this.b2b == 1) { if (this.b2b == 1) {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .4s", style: 'animation-delay: .4s',
innerHTML: `BOUT À BOUT<br/>${b2bScore}` innerHTML: `BOUT À BOUT<br/>${b2bScore}`,
}) });
} else { } else {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .4s", style: 'animation-delay: .4s',
innerHTML: `BOUT À BOUT x${this.b2b}<br/>${b2bScore}` innerHTML: `BOUT À BOUT x${this.b2b}<br/>${b2bScore}`,
}) });
} }
this.score += b2bScore this.score += b2bScore;
} }
} else if (nbClearedLines && !tSpin ) { } else if (clearedLines && !tSpin) {
if (this.b2b >= 1) { if (this.b2b >= 1) {
messagesSpan.addNewChild("div", { messagesSpan.addNewChild('div', {
className: "zoom-in-animation", className: 'zoom-in-animation',
style: "animation-delay: .4s", style: 'animation-delay: .4s',
innerHTML: `FIN DU BOUT À BOUT` innerHTML: `FIN DU BOUT À BOUT`,
}) });
playSound(btb_break)
} }
this.b2b = -1 this.b2b = -1;
} }
// Sound // Sounds
if (sfxVolumeRange.value) { if (clearedLines == 4 || (tSpin && clearedLines)) {
if (nbClearedLines == 4) playSound(quatuorSound, this.combo) if (this.b2b >= 1) switch(this.b2b) {
else if (nbClearedLines) playSound(lineClearSound, this.combo) case 1: playSound(btb_1); break
if (tSpin) playSound(tSpinSound, this.combo) case 2: playSound(btb_2); break
} default: playSound(btb_3)
} else if (this.combo >= 1) switch(this.combo) {
case 1: playSound(combo_1_power); break
case 2: playSound(combo_2_power); break
case 3: playSound(combo_3_power); break
case 4: playSound(combo_4_power); break
case 5: playSound(combo_5_power); break
case 6: playSound(combo_6_power); break
case 7: playSound(combo_7_power); break
case 8: playSound(combo_8_power); break
case 9: playSound(combo_9_power); break
case 10: playSound(combo_10_power); break
case 11: playSound(combo_11_power); break
case 12: playSound(combo_12_power); break
case 13: playSound(combo_13_power); break
case 14: playSound(combo_14_power); break
case 15: playSound(combo_15_power); break
default: playSound(combo_16_power)
} else if (clearedLines == 4) playSound(clearbtb);
else playSound(clearspin);
} else if (this.combo >= 1) switch(this.combo) {
case 1: playSound(combo_1); break
case 2: playSound(combo_2); break
case 3: playSound(combo_3); break
case 4: playSound(combo_4); break
case 5: playSound(combo_5); break
case 6: playSound(combo_6); break
case 7: playSound(combo_7); break
case 8: playSound(combo_8); break
case 9: playSound(combo_9); break
case 10: playSound(combo_10); break
case 11: playSound(combo_11); break
case 12: playSound(combo_12); break
case 13: playSound(combo_13); break
case 14: playSound(combo_14); break
case 15: playSound(combo_15); break
default: playSound(combo_16)
} else if (tSpin) {
if (clearedLines) playSound(clearspin);
else playSound(spin);
} else if (clearedLines) playSound(clearline);
this.goal -= awardedLineClears this.goal -= awardedLineClears;
if (this.goal <= 0) this.level++ if (this.goal <= 0) this.level++;
} }
show() { show() {
let time = stats.time let time = stats.time;
statsModalScoreCell.innerText = this.score.toLocaleString() statsModalScoreCell.innerText = this.score.toLocaleString();
statsModalHighScoreCell.innerText = this.highScore.toLocaleString() statsModalHighScoreCell.innerText = this.highScore.toLocaleString();
statsModalLevelCell.innerText = this.level statsModalLevelCell.innerText = this.level;
statsModalTimeCell.innerText = this.timeFormat.format(time) statsModalTimeCell.innerText = this.timeFormat.format(time);
statsModaltotalClearedLines.innerText = this.totalClearedLines statsModaltotalClearedLines.innerText = this.totalClearedLines;
statsModaltotalClearedLinesPM.innerText = (stats.totalClearedLines * 60000 / time).toFixed(2) statsModaltotalClearedLinesPM.innerText = (
statsModalNbQuatuors.innerText = this.nbQuatuors (stats.totalClearedLines * 60000) /
statsModalNbTSpin.innerText = this.nbTSpin time
statsModalMaxCombo.innerText = this.maxCombo ).toFixed(2);
statsModalMaxB2B.innerText = this.maxB2B statsModalNbQuatuors.innerText = this.nbQuatuors;
this.modal.show() statsModalNbTSpin.innerText = this.nbTSpin;
statsModalMaxCombo.innerText = this.maxCombo;
statsModalMaxB2B.innerText = this.maxB2B;
this.modal.show();
} }
save() { save() {
localStorage["highScore"] = this.highScore localStorage['highScore'] = this.highScore;
} }
} }
Stats.prototype.timeFormat = new Intl.DateTimeFormat("fr-FR", { Stats.prototype.timeFormat = new Intl.DateTimeFormat('fr-FR', {
hour: "numeric", hour: 'numeric',
minute: "2-digit", minute: '2-digit',
second: "2-digit", second: '2-digit',
timeZone: "UTC" timeZone: 'UTC',
}) });
function playSound(sound, note = 0) { function playSound(sound, note = 0) {
sound.currentTime = 0 sound.currentTime = 0;
sound.playbackRate = Math.pow(5/4, note) sound.playbackRate = Math.pow(5 / 4, note);
sound.play() sound.play();
} }
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More