31 Commits

Author SHA1 Message Date
adrien f87af4fa7e swap stats th and td 2026-03-18 01:56:27 +01:00
adrien 980715c72f save both skins 2026-03-18 01:38:43 +01:00
adrien 81b243bd85 hard drop rotation 2026-03-16 13:30:20 +01:00
adrien 4320b46e37 group tetr.io skins by authors 2026-03-14 17:53:56 +01:00
adrien b2738f6923 game over sound 2026-03-12 09:32:37 +01:00
adrien 7730d72315 improve sounds 2026-03-12 09:25:51 +01:00
adrien 56ed3b5c13 if (sfxVolumeRange.value) 2026-03-12 03:13:28 +01:00
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
123 changed files with 15735 additions and 929 deletions
+56 -41
View File
@@ -1,17 +1,17 @@
:root {
--cell-side: 25px;
--rX: -15;
--rY: 0;
--tZ: 25px;
--cell-size: 25px;
--rX: -15;
--rY: 0;
--tZ: 25px;
}
body {
background: linear-gradient(#212529, #14171a) fixed;
background: linear-gradient(#212529, #14171a) fixed;
}
@supports (backdrop-filter: blur()) {
.modal::before {
content: "";
content: '';
position: absolute;
top: 0;
bottom: 0;
@@ -30,8 +30,12 @@ body {
background-color: #25292d;
}
#statsTable th {
text-align: right;
}
#matrixCard {
background-image: radial-gradient(#222, #25292d)
background-image: radial-gradient(#222, #25292d);
}
.card-header {
@@ -43,18 +47,13 @@ body {
font-weight: 600;
}
#statsTable td,
#statsModal td {
text-align: right;
}
td#timeCell {
min-width: 10ch;
min-width: 10ch;
}
.minoes-table {
--piece-column: 0;
--piece-row : 0;
--piece-column: 0;
--piece-row: 0;
table-layout: fixed;
border-collapse: separate;
border-spacing: 0;
@@ -62,16 +61,16 @@ td#timeCell {
}
#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 {
25% {
transform: translateY(3px);
50% {
transform: translate(0, 5px);
}
}
.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) {
@@ -79,16 +78,16 @@ tr.buffer-zone td:not(.mino) {
}
tr.matrix td:not(.mino) {
border-left : 1px solid #333;
border-right : 1px solid #333;
border-top : 1px solid #303030;
border-left: 1px solid #333;
border-right: 1px solid #333;
border-top: 1px solid #303030;
border-bottom: 1px solid #303030;
}
td {
overflow: hidden;
width: var(--cell-side);
height: var(--cell-side);
width: var(--cell-size);
height: var(--cell-size);
box-sizing: border-box;
}
@@ -113,33 +112,39 @@ td {
}
td.trail-animation {
animation: trail-animation ease-out .3s;
animation: trail-animation ease-out 0.3s;
}
@keyframes cleared-line-animation {
from {
background-color: white;
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% {
box-shadow: -60px 0 2px #adb5bd66, 60px 0 2px #adb5bd66;
box-shadow:
-60px 0 2px #adb5bd66,
60px 0 2px #adb5bd66;
}
to {
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 {
animation: cleared-line-animation ease-out .3s;
animation: cleared-line-animation ease-out 0.3s;
}
#holdTable .J,
#holdTable .L,
#holdTable .S,
#holdTable .T,
#holdTable .Z,
#holdTable .Z,
#nextTable .J,
#nextTable .L,
#nextTable .S,
@@ -157,6 +162,7 @@ tr.cleared-line-animation {
text-shadow: 1px 1px #000c;
font-size: 3vmin;
text-align: center;
z-index: 200;
}
#messagesSpan div {
@@ -192,7 +198,6 @@ tr.cleared-line-animation {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
line-height: var(--bs-body-line-height);
}
30% {
opacity: 1;
@@ -212,13 +217,13 @@ tr.cleared-line-animation {
@keyframes rotate-in-animation {
0% {
opacity:0;
transform:rotate(200deg);
opacity: 0;
transform: rotate(200deg);
line-height: var(--bs-body-line-height);
}
30% {
opacity:1;
transform:translateZ(0);
opacity: 1;
transform: translateZ(0);
transform: scale3d(1, 1, 1);
}
80% {
@@ -234,15 +239,15 @@ tr.cleared-line-animation {
}
#messagesSpan div.rotate-in-animation {
animation-name: rotate-in-animation;
animation-timing-function: cubic-bezier(.25,.46,.45,.94);
animation-name: rotate-in-animation;
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
animation-duration: 1s;
}
#messagesSpan div.zoom-in-animation {
animation-name: zoom-in-animation;
animation-timing-function: cubic-bezier(.25,.46,.45,.94);
transform-origin:center;
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
transform-origin: center;
animation-duration: 1s;
}
@@ -268,23 +273,33 @@ tr.cleared-line-animation {
border-collapse: collapse;
}
#statsModal th {
#statsModal td {
padding-left: 0;
padding-right: 0.5rem;
padding-bottom: 0.2rem;
border-left: 0.5rem solid transparent;
border-right: 0;
width: max-content;
}
#statsModal td {
#statsModal th {
padding-left: 0.5rem;
padding-right: 0;
padding-bottom: 0.2rem;
border-left: 0;
border-right: 0.5rem solid transparent;
text-align: right;
}
#statsModal tr:last-child th,
#statsModal tr:last-child td {
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%);
--light: hsl(var(--hue), calc(0.66 * var(--saturation)), 84%);
--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-image:
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,.12),
0 -4px 0 var(--top),
2px -3px 0 var(--right),
3px 2px 6px #0004;
0 2px 4px #0008;
filter: saturate(1.1) contrast(1.05);
}
@@ -82,7 +80,7 @@ tr.matrix td:not(.mino) {
.ghost {
border: 3px solid #fff2;
padding: 2px;
padding: 3px;
background-color: #fff2;
background-clip: content-box;
background-image: none;
+15 -14
View File
@@ -1,14 +1,14 @@
body {
background-image: url("electro/bg.jpg");
background-image: url('electro/bg.jpg');
background-size: cover;
}
body[data-bs-theme="dark"] {
--bs-body-bg: #2125296b;
body[data-bs-theme='dark'] {
--bs-body-bg: #2125296b;
}
.btn-dark {
--bs-btn-bg: #2125296b;
--bs-btn-bg: #2125296b;
}
.card {
@@ -16,7 +16,7 @@ body[data-bs-theme="dark"] {
}
tr.matrix td:not(.mino) {
border-left : none;
border-left: none;
border-bottom: none;
}
@@ -30,29 +30,30 @@ tr.matrix td:not(.mino) {
}
.mino:not(.ghost):not(.locking):before {
content: "";
content: '';
position: absolute;
z-index: -1;
inset: 0;
margin: 1px 1px 0px 0px;
padding: 2px;
border-radius: 4px;
--glint-x: calc(50% + 50% * (var(--piece-column) - var(--column))/10);
--glint-y: calc(50% + 50% * (var(--piece-row) - var(--row))/25);
--glint-x: calc(50% + 50% * (var(--piece-column) - var(--column)) / 10);
--glint-y: calc(50% + 50% * (var(--piece-row) - var(--row)) / 25);
background: radial-gradient(
at var(--glint-x) var(--glint-y),
rgba(204, 238, 247, 0.9) 0%,
rgba(10, 159, 218, 0.9) 150%);
mask:
linear-gradient(#666 0 0) content-box,
linear-gradient(#fff 0 0);
rgba(204, 238, 247, 0.9) 0%,
rgba(10, 159, 218, 0.9) 150%
);
mask:
linear-gradient(#666 0 0) content-box,
linear-gradient(#fff 0 0);
mask-mode: luminance;
mask-composite: intersect;
}
.ghost.mino {
background: rgba(242, 255, 255, 10%);
border : 2px solid rgba(255, 255, 255, 0.3);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 3px;
box-shadow: 0px 0px 10px rgba(242, 255, 255, 75%);
}
+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

+21 -25
View File
@@ -5,12 +5,8 @@
.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%));
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 {
@@ -26,8 +22,8 @@ tr.matrix td:not(.mino) {
.minoes-table td {
display: inline-block;
width: var(--cell-side);
height: var(--cell-side);
width: var(--cell-size);
height: var(--cell-size);
padding: 0 !important;
z-index: calc(200 - var(--row));
}
@@ -41,38 +37,38 @@ tr.matrix td:not(.mino) {
}
.I.mino {
--background-color: #42AFE1;
--box-shadow-color: #6CEAFF;
--background-color: #42afe1;
--box-shadow-color: #6ceaff;
}
.J.mino {
--background-color: #1165B5;
--box-shadow-color: #339BFF;
--background-color: #1165b5;
--box-shadow-color: #339bff;
}
.L.mino {
--background-color: #F38927;
--box-shadow-color: #FFBA59;
--background-color: #f38927;
--box-shadow-color: #ffba59;
}
.O.mino {
--background-color: #F6D03C;
--box-shadow-color: #FFFF7F;
--background-color: #f6d03c;
--box-shadow-color: #ffff7f;
}
.S.mino {
--background-color: #51B84D;
--box-shadow-color: #84F880;
--background-color: #51b84d;
--box-shadow-color: #84f880;
}
.T.mino {
--background-color: #9739A2;
--box-shadow-color: #D958E9;
--background-color: #9739a2;
--box-shadow-color: #d958e9;
}
.Z.mino {
--background-color: #EB4F65;
--box-shadow-color: #FF7F79;
--background-color: #eb4f65;
--box-shadow-color: #ff7f79;
}
.ghost.mino {
@@ -86,8 +82,8 @@ tr.matrix td:not(.mino) {
}
.locking.mino {
--background-color: white;
--box-shadow-color: #DDD;
--background-color: white;
--box-shadow-color: #ddd;
}
.locked.mino {
@@ -122,4 +118,4 @@ tr.matrix td:not(.mino) {
to {
background-color: transparent;
}
}
}
+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

+19 -19
View File
@@ -1,5 +1,5 @@
body {
--bs-gutter-x: 0;
--bs-gutter-x: 0;
background: black !important;
}
@@ -7,11 +7,11 @@ body {
gap: 0 !important;
margin: 0;
text-transform: uppercase;
letter-spacing: .1em;
letter-spacing: 0.1em;
}
#screenRow {
--bs-gutter-x: 0;
--bs-gutter-x: 0;
}
.card {
@@ -22,7 +22,7 @@ body {
}
.card-header,
.card-header th{
.card-header th {
background: transparent;
font-weight: 400 !important;
font-size: 1.3em;
@@ -30,7 +30,7 @@ body {
}
#screenRow .table {
--bs-border-width: 0;
--bs-border-width: 0;
}
#holdTable {
@@ -76,7 +76,7 @@ body {
}
td#timeCell {
text-align: center;
text-align: center;
}
#matrixCard {
@@ -95,38 +95,38 @@ td#timeCell {
}
.I.mino {
background-color: #42AFE1;
border-color: #6CEAFF;
background-color: #42afe1;
border-color: #6ceaff;
}
.J.mino {
background-color: #1165B5;
border-color: #339BFF;
background-color: #1165b5;
border-color: #339bff;
}
.L.mino {
background-color: #F38927;
border-color: #FFBA59;
background-color: #f38927;
border-color: #ffba59;
}
.O.mino {
background-color: #F6D03C;
border-color: #FFFF7F;
background-color: #f6d03c;
border-color: #ffff7f;
}
.S.mino {
background-color: #32ee3e;
border-color: #84F880;
border-color: #84f880;
}
.T.mino {
background-color: #9739A2;
border-color: #D958E9;
background-color: #9739a2;
border-color: #d958e9;
}
.Z.mino {
background-color: #EB4F65;
border-color: #FF7F79;
background-color: #eb4f65;
border-color: #ff7f79;
}
.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

+280 -207
View File
@@ -1,72 +1,90 @@
: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 {
background-image: url(stereo/bg.jpg),
radial-gradient(circle at center,
#39444f 0%,
#2c323b 25%,
#293036 28%,
#252b32 34%,
#242930 38%,
#1a1d22 52%,
#191c22 53%,
#151519 63%,
#141418 65%,
#0f0f12 74%,
#0a0c0d 100%);
background-repeat: space;
background-position: center;
background-size: cover;
background-image:
url(stereo/bg.jpg),
radial-gradient(
circle at center,
#39444f 0%,
#2c323b 25%,
#293036 28%,
#252b32 34%,
#242930 38%,
#1a1d22 52%,
#191c22 53%,
#151519 63%,
#141418 65%,
#0f0f12 74%,
#0a0c0d 100%
);
background-repeat: space;
background-position: center;
background-size: cover;
}
#sceneDiv {
perspective: 500px;
cursor: grab;
perspective: 500px;
cursor: grab;
}
#sceneDiv:active {
cursor: grabbing;
}
#sceneDiv * {
transform-style: preserve-3d;
cursor: grabbing;
}
#screenRow {
display: block;
transform: translateZ(var(--tZ)) rotateX(calc((var(--rX)) * 1deg)) rotateY(calc((var(--rY)) * 1deg));
--light-rX: calc(-1 * var(--rY) / 30);
--light-rY: calc(var(--rX) / 20);
display: block;
transform: translateZ(var(--tZ)) rotateX(calc((var(--rX)) * 1deg))
rotateY(calc((var(--rY)) * 1deg));
}
#screenRow * {
display: block;
#sceneDiv,
#screenRow,
#screenRow .col,
#screenRow .card,
#screenRow .card-body,
.minoes-table,
.minoes-table tbody,
.minoes-table tr,
.minoes-table td {
display: block;
transform-style: preserve-3d;
}
#screenRow .col {
display: inline-block !important;
width: max-content;
height: 100%;
vertical-align: top;
display: inline-block !important;
width: max-content;
height: 100%;
vertical-align: top;
}
.card {
background: #36394180;
background: #36394180;
}
#matrixCard {
background-image: none;
background-image: none;
transform-origin: bottom;
}
#screenRow .card>* {
transform: translateZ(var(--cell-side));
#screenRow .card > * {
transform: translateZ(var(--cell-size));
}
#screenRow .card-header {
background-color: transparent;
border: none;
background-color: transparent;
border: none;
}
.card,
.card-header {
text-shadow:
calc(-0.3px * var(--rY)) calc(0.4px * var(--rX)) 5px #0008;
text-shadow: calc(-0.3px * var(--rY)) calc(0.4px * var(--rX)) 5px #0008;
}
#holdTable .mino {
@@ -81,157 +99,204 @@ body {
.minoes-table th,
.minoes-table td {
display: inline-block !important;
width: max-content;
display: inline-block !important;
width: max-content;
}
.minoes-table tr {
width: max-content;
height: var(--cell-side);
width: max-content;
height: var(--cell-size);
}
#statsTable tr {
display: table;
width: 100%;
display: table;
width: 100%;
}
#statsTable th,
#statsTable td {
display: table-cell;
border: 0;
display: table-cell;
border: 0;
}
tr.matrix td:not(.mino) {
border: 0;
border: 0;
}
.minoes-table td {
width: var(--cell-side) !important;
height: var(--cell-side) !important;
overflow: visible;
width: var(--cell-size);
height: var(--cell-size);
overflow: visible;
position: relative;
}
.minoes-table .mino,
.minoes-table .mino::before,
.minoes-table .mino + :not(.mino)::before,
.minoes-table .mino::after {
--light-x: calc(-0.5 - var(--rY) / 30 - var(--column) / 10 + 1);
--light-y: calc(-0.5 + var(--rX) / 20 - var(--row) / 6 + 4);
.mino,
.mino::before,
.mino + :not(.mino)::before,
.mino::after {
--light-pX: calc(0.5 - var(--column) / 10);
--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));
--edge-color: hsla(var(--h), var(--s), calc(var(--l) * (var(--light) * 0.9)), var(--a));
background: var(--center-color);
border-radius: 2px;
border: 2px outset var(--center-color);
background: var(--center-color);
border-radius: 4px;
border: 2px outset var(--center-color);
}
.minoes-table .mino::before,
.minoes-table .mino + :not(.mino)::before,
.minoes-table .mino::after,
.mino::before,
.mino + :not(.mino)::before,
.mino::after,
td.trail-animation::before,
td.trail-animation::after,
tr.cleared-line-animation td::before,
tr.cleared-line-animation td::after {
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: var(--cell-side);
height: var(--cell-side);
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: var(--cell-size);
height: var(--cell-size);
}
/* Front face */
.minoes-table .mino,
.mino,
td.trail-animation {
--light: calc(
1
+ (var(--light-y) * 0.3)
+ (var(--light-x) * 0.2)
);
--center-x: calc(35% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%);
background: radial-gradient(
circle at var(--center-x) var(--center-y),
var(--center-color) 15%,
var(--edge-color) 100%
);
box-shadow: 0 0 7px hsla(var(--h), var(--s), calc(var(--l) * var(--light) * 1.3), 20%);
--light: calc(1 + (var(--light-y) * 0.3) + (var(--light-x) * 0.2));
--center-x: calc(35% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%);
background: radial-gradient(
circle at var(--center-x) var(--center-y),
var(--center-color) 15%,
var(--edge-color) 100%
);
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 */
.minoes-table .mino::before,
.mino::before,
td.trail-animation::before,
tr.cleared-line-animation td::before,
.left .minoes-table .mino + .mino::before {
--light: calc(
1.1
+ (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;
.mino + .mino::before {
--light: calc(1.1 + (var(--light-x) * -0.2) + (var(--light-y) * 0.15));
transform: var(--t3d) rotateY(-90deg);
transform-origin: left;
}
/* Right face */
.right .minoes-table .mino + .mino::before,
.minoes-table .mino + :not(.mino)::before,
.mino + :not(.mino)::before,
.right .mino + .mino::before,
.right td.trail-animation::before,
.right tr.cleared-line-animation td::before {
--light: calc(
0.85
+ (var(--light-x) * -0.2)
+ (var(--light-y) * -0.15)
);
--center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%);
filter: saturate(0.95);
transform: translate3d(0, 0, calc(-1 * var(--cell-side))) rotateY(-90deg);
transform-origin: left;
--light: calc(0.85 + (var(--light-x) * -0.2) + (var(--light-y) * -0.15));
--center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(35% + var(--light-y) * 10%);
filter: saturate(0.95);
transform: translate3d(0, 0, var(--cell-size-opposite)) rotateY(-90deg);
transform-origin: left;
}
.right .minoes-table .mino:last-child::before {
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateY(90deg) !important;
transform-origin: right !important;
.right .mino:last-child::before {
transform: var(--t3d) rotateY(90deg) !important;
transform-origin: right !important;
}
/* Top face */
.minoes-table .mino::after,
.mino::after,
td.trail-animation::after,
tr.cleared-line-animation td::after {
--light: calc(
1.5
+ (var(--light-y) * 0.2)
);
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateX(90deg);
transform-origin: top;
--light: calc(1.5 + (var(--light-y) * 0.2));
transform: var(--t3d) rotateX(90deg);
transform-origin: top;
}
/* Bottom face */
.bottom .minoes-table .mino::after,
.bottom .mino::after,
.bottom td.trail-animation::after,
.bottom tr.cleared-line-animation td::after {
--light: calc(
1.1
+ (var(--light-y) * -0.3)
);
--center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(65% + var(--light-y) * 10%);
filter: saturate(0.95);
transform: translate3d(-1.5px, -1.5px, calc(-1 * var(--cell-side))) rotateX(-90deg);
transform-origin: bottom;
--light: calc(1.1 + (var(--light-y) * -0.3));
--center-x: calc(65% + var(--light-x) * 10%);
--center-y: calc(65% + var(--light-y) * 10%);
filter: saturate(0.95);
transform: var(--t3d) rotateX(-90deg);
transform-origin: bottom;
}
.J.mino, .J.mino + :not(.mino) { --h: 210deg; --s: 78%; --l: 52%; --a: 0.75; }
.L.mino, .L.mino + :not(.mino) { --h: 28deg; --s: 85%; --l: 52%; --a: 0.75; }
.O.mino, .O.mino + :not(.mino) { --h: 48deg; --s: 88%; --l: 52%; --a: 0.75; }
.I.mino, .I.mino + :not(.mino) { --h: 200deg; --s: 70%; --l: 52%; --a: 0.75; }
.S.mino, .S.mino + :not(.mino) { --h: 118deg; --s: 45%; --l: 52%; --a: 0.75; }
.T.mino, .T.mino + :not(.mino) { --h: 293deg; --s: 48%; --l: 52%; --a: 0.75; }
.Z.mino, .Z.mino + :not(.mino) { --h: 352deg; --s: 75%; --l: 52%; --a: 0.75; }
.J,
.J + :not(.mino) {
--h: 210deg;
--s: 78%;
--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; }
.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; }
.ghost.mino,
.ghost.mino + :not(.mino) {
--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 .L + :not(.mino),
@@ -243,13 +308,13 @@ tr.cleared-line-animation td::after {
#nextTable .S + :not(.mino),
#nextTable .T + :not(.mino),
#nextTable .Z + :not(.mino) {
transform: translateX(50%);
transform: translateX(50%);
}
@keyframes trail-animation {
from {
background-color: hsla(180, 100%, 100%, 0.1);
border-color: hsla(180, 100%, 100%, 0.1);
border-color: hsla(180, 100%, 100%, 0.1);
}
to {
background-color: transparent;
@@ -259,112 +324,120 @@ tr.cleared-line-animation td::after {
td.trail-animation::before,
td.trail-animation::after {
animation: trail-animation ease-out .3s;
animation: trail-animation ease-out 0.3s;
}
@keyframes hard-dropped-table-animation {
50% {
transform: translateY(10px) rotateX(-3deg);
}
}
@keyframes locked-animation {
from {
--h: 0deg; --s: 0%; --l: 100%; --a: 1;
box-shadow: 0 0 10px hsla(180, 100%, 100%, 0.2);
--h: 0deg;
--s: 0%;
--l: 100%;
--a: 1;
box-shadow: 0 0 10px hsla(180, 100%, 100%, 0.2);
}
}
.locked.mino::before,
.locked.mino::after {
animation: locked-animation;
animation-duration: .2s;
animation-duration: 0.2s;
}
@keyframes cleared-line-animation {
from {
background-color: white !important;
box-shadow: 0 0 0 white;
}
to {
background-color: #fff0;
box-shadow: 0 0 100px transparent;
}
from {
background-color: white !important;
box-shadow: 0 0 0 white;
}
to {
background-color: #fff0;
box-shadow: 0 0 100px transparent;
}
}
tr.cleared-line-animation td::before,
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 {
from {
opacity: 1;
transform: translateY(200%);
}
from {
opacity: 1;
transform: translateY(200%);
}
50% {
transform: translateY(0) scaleY(1);
line-height: var(--bs-body-line-height);
}
50% {
transform: translateY(0) scaleY(1);
line-height: var(--bs-body-line-height);
}
to {
opacity: 1;
transform: translateY(-100%) scaleY(0);
line-height: 0;
}
to {
opacity: 1;
transform: translateY(-100%) scaleY(0);
line-height: 0;
}
}
@keyframes zoom-in-animation {
from {
opacity: 1;
transform: scale3d(0.3, 0.3, 0.3);
line-height: var(--bs-body-line-height);
}
from {
opacity: 1;
transform: scale3d(0.3, 0.3, 0.3);
line-height: var(--bs-body-line-height);
}
30% {
transform: scale3d(1, 1, 1);
}
30% {
transform: scale3d(1, 1, 1);
}
80% {
transform: scale3d(1, 1, 1);
line-height: var(--bs-body-line-height);
}
80% {
transform: scale3d(1, 1, 1);
line-height: var(--bs-body-line-height);
}
to {
opacity: 1;
transform: scale3d(1.5, 0, 1);
line-height: 0;
}
to {
opacity: 1;
transform: scale3d(1.5, 0, 1);
line-height: 0;
}
}
@keyframes rotate-in-animation {
0% {
opacity: 1;
transform: rotate(200deg);
line-height: var(--bs-body-line-height);
}
0% {
opacity: 1;
transform: rotate(200deg);
line-height: var(--bs-body-line-height);
}
30% {
transform: translateZ(0);
transform: scale3d(1, 1, 1);
}
30% {
transform: translateZ(0) scale3d(1, 1, 1);
}
80% {
transform: scale3d(1, 1, 1);
line-height: var(--bs-body-line-height);
}
80% {
transform: scale3d(1, 1, 1);
line-height: var(--bs-body-line-height);
}
to {
opacity: 1;
transform: scale3d(1.5, 0, 1);
line-height: 0;
}
to {
opacity: 1;
transform: scale3d(1.5, 0, 1);
line-height: 0;
}
}
@keyframes game-over-animation {
from {
opacity: 1;
transform: translateY(200%);
}
from {
opacity: 1;
transform: translateY(200%);
}
to {
opacity: 1;
transform: translateY(0) scaleY(1);
line-height: var(--bs-body-line-height);
}
to {
opacity: 1;
transform: translateY(0) scaleY(1);
line-height: var(--bs-body-line-height);
}
}
+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);
}
+27 -14
View File
@@ -1,38 +1,51 @@
body {
background-image: url(synthwave/bg.png);
background-image: url(synthwave/wtCSusF.jpeg);
background-size: cover;
}
body[data-bs-theme="dark"] {
--bs-body-bg: #2125296b;
body[data-bs-theme='dark'] {
--bs-body-bg: #2125296b;
}
.btn-dark {
--bs-btn-bg: #2125296b;
--bs-btn-bg: #2125296b;
}
.card {
background: #25292d66;
background: #25292d66;
}
#matrixCard {
background-image: radial-gradient(#2226, #25292d66);
background-image: radial-gradient(#2226, #25292d66);
}
.card,
#matrixCard {
background: repeating-linear-gradient(transparent, #111 1px);
background: repeating-linear-gradient(transparent, transparent 2px, #1114 2px, #1114 5px);
backdrop-filter: blur(3px);
}
.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 {
background: var(--color);
border: 3px solid var(--border);
box-shadow: 0 0 8px var(--border);
background: var(--color);
border: 3px solid var(--border);
border-radius: 6px;
box-shadow: 0 0 8px var(--border);
filter: blur(0.5px);
}
.I {
@@ -66,11 +79,11 @@ body[data-bs-theme="dark"] {
}
.Z {
--color: #E67D8666;
--border: #E67D86;
--color: #e67d8666;
--border: #e67d86;
}
.ghost {
--color: #fff4;
--border: #fff5;
}
}
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(tetrio-skin/a_forest.png);
}
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);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+114 -41
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="fr">
<html lang="fr" data-bs-theme="dark">
<head>
<meta charset="utf-8" />
@@ -8,15 +8,24 @@
<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-icons@1.10.4/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/classic.css" title="Thème sélectionné" id="selectedStyleSheet">
<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/electro.css" title="Électro">
<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/opera.css" title="Opéra">
<link rel="alternate stylesheet" href="css/stereo.css" title="Stéréo">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css"/>
<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/tetrio-skin.css" title="Thème sélectionné" id="selectedStyleSheet">
<link rel="alternate stylesheet" href="css/tetrio-skin.css" title="Sample Tetr.io...">
<link rel="alternate stylesheet" href="css/jstris-skin.css" title="Sample JStris...">
<link rel="alternate stylesheet" href="css/classic.css" title="Classique">
<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/synthwave.css" title="Synthwave">
<link rel="alternate stylesheet" href="css/heavy-metal.css" title="Heavy metal">
<link rel="alternate stylesheet" href="css/jazz.css" title="Jazz">
<link rel="alternate stylesheet" href="css/old-school.css" title="Old School">
<link rel="alternate stylesheet" href="css/stereo.css" title="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="icon" type="image/png" sizes="32x32" href="favicons/T-2.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicons/favicon-16x16.png">
@@ -27,12 +36,12 @@
<meta property="og:image" content="https://adrien.malingrey.fr/jeux/quatuor/thumbnail.png"/>
<meta property="og:image:width" 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:site_name" content="adrien.malingrey.fr"/>
</head>
<body data-bs-theme="dark">
<body>
<div class="modal fade" id="settingsModal" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
@@ -60,7 +69,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>
<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 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>
<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>
@@ -68,15 +78,30 @@
</fieldset>
<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>
<div class="col-4"><select name="stylesheet" id="stylesheetSelect" class="form-select" oninput="selectedStyleSheet.href = this.value">
<option value="css/classic.css" selected>Classique</option>
<option value="css/minimal.css">Minimal</option>
<option value="css/synthwave.css">Synthwave</option>
<option value="css/electro.css">Électro</option>
<option value="css/retro.css">Rétro</option>
<option value="css/opera.css">Opéra</option>
<option value="css/stereo.css">Stéréo</option>
</select></div>
<div class="col-4">
<select name="stylesheet" id="stylesheetSelect" class="form-select">
<option value="css/tetrio-skin.css" selected>Sample Tetr.io...</option>
<option value="css/jstris-skin.css">Sample JStris...</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/electro.css">Électro</option>
<option value="css/heavy-metal.css">Heavy metal</option>
<option value="css/jazz.css">Jazz</option>
<option value="css/old-school.css">Old School</option>
<option value="css/stereo.css">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"><abbr title="Auteur des effets sonores : 25Pi25">Volume</abbr></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="form-check form-switch text-start">
<input id="fullscreenCheckbox" type="checkbox" role="switch" class="form-check-input" tabindex="0">
@@ -84,8 +109,6 @@
</div>
</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 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>
@@ -120,11 +143,11 @@
</div>
<div class="card shadow">
<table id="statsTable" class="table mb-0">
<tr class="card-header fw-bold text-uppercase"><th>Score</th><td id="scoreCell">0</td> </tr>
<tr><th>Meilleur<br/>score</th><td id="highScoreCell"><script>document.write(Number(localStorage["highScore"]) || 0)</script></td></tr>
<tr><th>Niveau</th> <td id="levelCell">0</td> </tr>
<tr><th>But</th> <td id="goalCell">0</td> </tr>
<tr><th>Temps</th> <td id="timeCell">00:00:00</td> </tr>
<tr class="card-header fw-bold text-uppercase"><td>Score</td><th id="scoreCell">0</th> </tr>
<tr><td>Meilleur<br/>score</td><th id="highScoreCell"><script>document.write(Number(localStorage["highScore"]) || 0)</script></th></tr>
<tr><td>Niveau</td><th id="levelCell">0</th> </tr>
<tr><td>But</td> <th id="goalCell">0</th> </tr>
<tr><td>Temps</td> <th id="timeCell">00:00:00</th></tr>
</table>
</div>
</div>
@@ -193,7 +216,7 @@
</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">
<div class="modal-content">
@@ -203,11 +226,16 @@
</div>
<div class="modal-body p-0">
<table class="table mb-0">
<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>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>Lignes</th> <td id="statsModaltotalClearedLines"></td><th>Lignes par minute</th> <td id="statsModaltotalClearedLinesPM"></td></tr>
<tr><td>Score</td> <th id="statsModalScoreCell"></th> </tr>
<tr><td>Meilleur score</td> <th id="statsModalHighScoreCell"></th> </tr>
<tr><td>Niveau</td> <th id="statsModalLevelCell"></th> </tr>
<tr><td>Temps</td> <th id="statsModalTimeCell"></th> </tr>
<tr><td>Lignes</td> <th id="statsModaltotalClearedLines"></th> </tr>
<tr><td>Lignes par minute</td> <th id="statsModaltotalClearedLinesPM"><htd></tr>
<tr><td>Quatuors</td> <th id="statsModalNbQuatuors"></th> </tr>
<tr><td>Pirouettes</td> <th id="statsModalNbTSpin"></th> </tr>
<tr><td>Plus long combo</td> <th id="statsModalMaxCombo"></th> </tr>
<tr><td>Plus long bout à bout</td><th id="statsModalMaxB2B"></th> </tr>
</table>
</div>
<div class="modal-footer">
@@ -227,14 +255,59 @@
<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"/>
<audio id="wallSound" src="sounds/808K_A.wav" preload="auto" type="audio/wav"></audio>
<audio id="hardDropSound" src="sounds/909S.wav" preload="auto" type="audio/wav"></audio>
<audio id="lineClearSound" src="sounds/808COW.wav" preload="auto" type="audio/wav"></audio>
<audio id="tSpinSound" src="sounds/78GUIR.wav" preload="auto" type="audio/wav"></audio>
<audio id="quatuorSound" src="sounds/BRRDC1.wav" preload="auto" type="audio/wav"></audio>
<audio id="btb_1" src="snd/btb_1.ogg" preload="auto" type="audio/ogg"></audio>
<audio id="btb_2" src="snd/btb_2.ogg" preload="auto" type="audio/ogg"></audio>
<audio id="btb_3" src="snd/btb_3.ogg" preload="auto" type="audio/ogg"></audio>
<audio id="btb_break" src="snd/btb_break.ogg" preload="auto" type="audio/ogg"></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="gameover" src="snd/topout.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="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="spin" src="snd/spin.ogg" preload="auto" type="audio/ogg"></audio>
</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/interface.js" language="Javascript" type="text/javascript"></script>
<script src="js/app.js" language="Javascript" type="text/javascript"></script>
+323 -202
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) {
fullscreenCheckbox.checked = true
fullscreenCheckbox.checked = true;
} else {
fullscreenCheckbox.checked = false
if (playing) pauseSettings()
fullscreenCheckbox.checked = false;
if (playing) pauseSettings();
}
}
document.onfullscreenerror = function() {
fullscreenCheckbox.checked = false
}
};
document.onfullscreenerror = function () {
fullscreenCheckbox.checked = false;
};
function restart() {
stats.modal.hide()
holdQueue.init()
holdQueue.redraw()
stats.init()
matrix.init()
nextQueue.init()
settings.init()
pauseSettings()
stats.modal.hide();
holdQueue.init();
holdQueue.redraw();
stats.init();
matrix.init();
nextQueue.init();
settings.init();
pauseSettings();
}
function pauseSettings() {
scheduler.clearInterval(fall)
scheduler.clearTimeout(lockDown)
scheduler.clearTimeout(repeat)
scheduler.clearInterval(autorepeat)
scheduler.clearInterval(ticktack)
stats.pauseTime = stats.time
scheduler.clearInterval(fall);
scheduler.clearTimeout(lockDown);
scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat);
scheduler.clearInterval(ticktack);
stats.pauseTime = stats.time;
document.onkeydown = null
document.onkeydown = null;
settings.show()
settings.show();
playSound(menuhover)
}
function newGame(event) {
if (!settings.form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
settings.form.reportValidity()
settings.form.classList.add('was-validated')
event.preventDefault();
event.stopPropagation();
settings.form.reportValidity();
settings.form.classList.add('was-validated');
} else {
const audioContext = new AudioContext()
for(const sound of document.getElementsByTagName("audio")) {
sound.preservesPitch = false
audioContext.createMediaElementSource(sound).connect(audioContext.destination)
const audioContext = new AudioContext();
for (const sound of document.getElementsByTagName('audio')) {
sound.preservesPitch = false;
audioContext.createMediaElementSource(sound).connect(audioContext.destination);
}
levelInput.name = "level"
levelInput.disabled = true
titleHeader.innerHTML = "PAUSE"
resumeButton.innerHTML = "Reprendre"
event.target.onsubmit = resume
stats.level = levelInput.valueAsNumber
localStorage["startLevel"] = levelInput.value
playing = true
onblur = pauseSettings
resume(event)
levelInput.name = 'level';
levelInput.disabled = true;
titleHeader.innerHTML = 'PAUSE';
resumeButton.innerHTML = 'Reprendre';
event.target.onsubmit = resume;
stats.level = levelInput.valueAsNumber;
localStorage['startLevel'] = levelInput.value;
playing = true;
onblur = pauseSettings;
resume(event);
}
}
function resume(event) {
event.preventDefault()
event.stopPropagation()
event.preventDefault();
event.stopPropagation();
settings.form.reportValidity()
settings.form.classList.add('was-validated')
settings.form.reportValidity();
settings.form.classList.add('was-validated');
if (settings.form.checkValidity()) {
for(const sound of document.getElementsByTagName("audio"))
sound.volume = sfxVolumeRange.value
for (const sound of document.getElementsByTagName('audio'))
sound.volume = sfxVolumeRange.value;
settings.modal.hide()
settings.getInputs()
settings.modal.hide();
settings.getInputs();
document.onkeydown = onkeydown
document.onkeyup = onkeyup
stats.time = stats.pauseTime
scheduler.setInterval(ticktack, 1000)
document.onkeydown = onkeydown;
document.onkeyup = onkeyup;
if (matrix.piece) scheduler.setInterval(fall, stats.fallPeriod)
else generate()
stats.time = stats.pauseTime;
scheduler.setInterval(ticktack, 1000);
if (matrix.piece) scheduler.setInterval(fall, stats.fallPeriod);
else generate();
playSound(menuconfirm)
}
}
function ticktack() {
timeCell.innerText = stats.timeFormat.format(stats.time)
timeCell.innerText = stats.timeFormat.format(stats.time);
}
function generate(piece) {
matrix.piece = piece || nextQueue.shift()
if (!piece && holdQueue.piece) holdQueue.drawPiece()
matrix.piece = piece || nextQueue.shift();
if (!piece && holdQueue.piece) holdQueue.drawPiece();
//lastActionSucceded = true
favicon.href = matrix.piece.favicon_href
favicon.href = matrix.piece.favicon_href;
if (matrix.piece.canMove(TRANSLATION.NONE)) {
scheduler.setInterval(fall, stats.fallPeriod)
scheduler.setInterval(fall, stats.fallPeriod);
} else {
gameOver() // block out
gameOver(); // block out
}
}
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() {
scheduler.clearTimeout(lockDown)
playSound(hardDropSound)
while (matrix.piece.move(TRANSLATION.DOWN, ROTATION.NONE, true)) stats.score += 2
matrixCard.classList.remove("hard-dropped-table-animation")
hardDrop: function () {
scheduler.clearTimeout(lockDown);
playSound(harddrop);
while (matrix.piece.move(TRANSLATION.DOWN, ROTATION.NONE, true)) stats.score += 2;
matrixCard.classList.remove('hard-dropped-table-animation');
matrixCard.offsetHeight;
matrixCard.classList.add("hard-dropped-table-animation") // restart animation
lockDown()
return true
matrixCard.classList.add('hard-dropped-table-animation'); // restart animation
lockDown();
return true;
},
hold: function() {
hold: function () {
if (matrix.piece.holdEnabled) {
scheduler.clearInterval(fall)
scheduler.clearTimeout(lockDown)
scheduler.clearInterval(fall);
scheduler.clearTimeout(lockDown);
playSound(hold)
let piece = matrix.piece
piece.facing = FACING.NORTH
piece.locked = false
generate(holdQueue.piece)
matrix.piece.holdEnabled = false
holdQueue.piece = piece
let piece = matrix.piece;
piece.facing = FACING.NORTH;
piece.locked = false;
generate(holdQueue.piece);
matrix.piece.holdEnabled = false;
holdQueue.piece = piece;
}
},
pause: pauseSettings,
}
};
// Handle player inputs
const REPEATABLE_ACTIONS = [
playerActions.moveLeft,
playerActions.moveRight,
playerActions.softDrop
]
pressedKeys = new Set()
actionsQueue = []
playerActions.softDrop,
];
pressedKeys = new Set();
actionsQueue = [];
function onkeydown(event) {
if (event.key in settings.keyBind) {
event.preventDefault()
event.preventDefault();
if (!pressedKeys.has(event.key)) {
pressedKeys.add(event.key)
action = settings.keyBind[event.key]
pressedKeys.add(event.key);
action = settings.keyBind[event.key];
/*if (action()) {
lastActionSucceded = true
} else if (lastActionSucceded || !(action in REPEATABLE_ACTIONS)) {
playSound(wallSound)
lastActionSucceded = false
}*/
action()
action();
if (REPEATABLE_ACTIONS.includes(action)) {
actionsQueue.unshift(action)
scheduler.clearTimeout(repeat)
scheduler.clearInterval(autorepeat)
if (action == playerActions.softDrop) scheduler.setInterval(autorepeat, settings.fallPeriod/20)
else scheduler.setTimeout(repeat, settings.das)
actionsQueue.unshift(action);
scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat);
if (action == playerActions.softDrop)
scheduler.setInterval(autorepeat, settings.fallPeriod / 20);
else scheduler.setTimeout(repeat, settings.das);
}
matrix.drawPiece()
matrix.drawPiece();
}
}
}
function repeat() {
if (actionsQueue.length) {
actionsQueue[0]()
scheduler.setInterval(autorepeat, settings.arr)
actionsQueue[0]();
scheduler.setInterval(autorepeat, settings.arr);
}
}
@@ -216,121 +195,263 @@ function autorepeat() {
wallSound.play()
lastActionSucceded = false
}*/
actionsQueue[0]()
}
else scheduler.clearInterval(autorepeat)
actionsQueue[0]();
} else scheduler.clearInterval(autorepeat);
}
function onkeyup(event) {
if (event.key in settings.keyBind) {
event.preventDefault()
pressedKeys.delete(event.key)
action = settings.keyBind[event.key]
event.preventDefault();
pressedKeys.delete(event.key);
action = settings.keyBind[event.key];
if (actionsQueue.includes(action)) {
actionsQueue.splice(actionsQueue.indexOf(action), 1)
scheduler.clearTimeout(repeat)
scheduler.clearInterval(autorepeat)
actionsQueue.splice(actionsQueue.indexOf(action), 1);
scheduler.clearTimeout(repeat);
scheduler.clearInterval(autorepeat);
if (actionsQueue.length) {
if (actionsQueue[0] == playerActions.softDrop) scheduler.setInterval(autorepeat, settings.fallPeriod/20)
else scheduler.setTimeout(repeat, settings.das)
if (actionsQueue[0] == playerActions.softDrop)
scheduler.setInterval(autorepeat, settings.fallPeriod / 20);
else scheduler.setTimeout(repeat, settings.das);
} else {
matrix.drawPiece()
matrix.drawPiece();
}
}
}
}
function fall() {
matrix.piece.move(TRANSLATION.DOWN)
matrix.piece.move(TRANSLATION.DOWN);
}
function lockDown() {
scheduler.clearTimeout(lockDown)
scheduler.clearInterval(fall)
scheduler.clearTimeout(lockDown);
scheduler.clearInterval(fall);
if (matrix.lock()) {
stats.lockDown(matrix.piece.tSpin, matrix.clearLines())
generate()
stats.lockDown(matrix.piece.tSpin, matrix.clearLines());
generate();
} else {
gameOver() // lock out
gameOver(); // lock out
}
}
onanimationend = function (event) {
event.target.classList.remove(event.animationName)
}
event.target.classList.remove(event.animationName);
};
messagesSpan.onanimationend = function(event) {
event.target.remove()
}
messagesSpan.onanimationend = function (event) {
event.target.remove();
};
function gameOver() {
matrix.piece.locked = true
matrix.drawPiece()
matrix.piece.locked = true;
matrix.drawPiece();
document.onkeydown = null
onblur = null
scheduler.clearInterval(ticktack)
playing = false
document.onkeydown = null;
onblur = null;
scheduler.clearInterval(ticktack);
playing = false;
stats.show()
stats.show();
playSound(gameover)
}
window.onbeforeunload = function(event) {
stats.save()
settings.save()
window.onbeforeunload = function (event) {
stats.save();
settings.save();
if (playing) return false;
}
};
// Play with 3D
let mousedown = false
let rX0 = -15
let rY0 = 0
let clientX0 = 0
let clientY0 = 0
let mousedown = false;
let rX0 = -15;
let rY0 = 0;
let clientX0 = 0;
let clientY0 = 0;
sceneDiv.onmousedown = function(event) {
mousedown = true
rX0 = parseInt(getComputedStyle(screenRow).getPropertyValue("--rX"))
dy0 = parseInt(getComputedStyle(screenRow).getPropertyValue("--rY"))
clientX0 = event.clientX
clientY0 = event.clientY
}
sceneDiv.onmousedown = function (event) {
mousedown = true;
rX0 = parseInt(getComputedStyle(screenRow).getPropertyValue('--rX'));
dy0 = parseInt(getComputedStyle(screenRow).getPropertyValue('--rY'));
clientX0 = event.clientX;
clientY0 = event.clientY;
};
sceneDiv.onmousemove = function(event) {
if (mousedown) {
event.preventDefault()
event.stopPropagation()
rX = (rX0 - 0.5 * (event.clientY - clientY0)) % 360
screenRow.style.setProperty("--rX", rX)
sceneDiv.onmousemove = function (event) {
if (mousedown) {
event.preventDefault();
event.stopPropagation();
rX = (rX0 - 0.5 * (event.clientY - clientY0)) % 360;
screenRow.style.setProperty('--rX', rX);
if (rX >= 0) {
screenRow.classList.remove("top")
screenRow.classList.add("bottom")
screenRow.classList.remove('top');
screenRow.classList.add('bottom');
} else {
screenRow.classList.add("top")
screenRow.classList.remove("bottom")
screenRow.classList.add('top');
screenRow.classList.remove('bottom');
}
rY = (rY0 + 0.5 * (event.clientX - clientX0)) % 360
screenRow.style.setProperty("--rY", rY)
rY = (rY0 + 0.5 * (event.clientX - clientX0)) % 360;
screenRow.style.setProperty('--rY', rY);
if (rY <= 0) {
screenRow.classList.remove("left")
screenRow.classList.add("right")
screenRow.classList.remove('left');
screenRow.classList.add('right');
} else {
screenRow.classList.add("left")
screenRow.classList.remove("right")
screenRow.classList.add('left');
screenRow.classList.remove('right');
}
}
};
sceneDiv.onmouseup = document.onmouseleave = function (event) {
mousedown = false;
};
fullscreenCheckbox.onchange = function () {
if (this.checked) {
document.documentElement.requestFullscreen();
} else {
document.exitFullscreen();
}
};
sceneDiv.onwheel = function (event) {
event.preventDefault();
event.stopPropagation();
let tZ = parseInt(getComputedStyle(screenRow).getPropertyValue('--tZ'));
tZ -= event.deltaY;
screenRow.style.setProperty('--tZ', tZ + 'px');
};
$.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 (/^(https?:\/\/.*\.(?:png|jpg|jpeg|gif|bmp|webp|svg))$/i.test(url)) {
return {
id: url,
text: 'Source externe',
newTag: true,
};
}
});
stylesheetSelect.oninput = function (event) {
selectedStyleSheet.href = this.value;
var skinURL = skinURLSelect.value
switch (this.value) {
case 'css/tetrio-skin.css':
localStorage[skinURLSelect.name] = skinURLSelect.value
skinURLSelect.name = "tetrioSkinURL"
$("#skinURLSelect").empty();
const baseURL = "https://you.have.fail/tetrioplus/data"
fetch(`${baseURL}/data.json`)
.then((resp) => resp.json())
.then((json) => {
json = json.filter((item) => (item.type == "skin" && item.format == "tetrioraster" && /\.(?:png|jpg|jpeg|gif|bmp|webp|svg)$/i.test(item.path)))
const groups = Map.groupBy(json, (skin) => skin.author)
var data = groups.entries().map(([author, skins]) => {
return {
text: author,
children: skins.map((skin) => {
return {
id: `${baseURL}/${encodeURI(skin.path)}`,
text: skin.description? `${skin.name}\n${skin.description}` : `${skin.name}`
}
})
}
}).toArray()
data.push({
text: "AdrienMalin",
children: [{
id: `${document.location.href}/css/tetrio-skin/a_forest.png`,
text: "A forest"
}]
})
data.sort((group1, group2) => group1.text > group2.text)
skinURLSelect.disabled = false;
$('#skinURLSelect').select2({data: data})
if (skinURL = localStorage['tetrioSkinURL']) {
if ($('#skinURLSelect').find(`option[value='${skinURL}']`).length) {
$('#skinURLSelect').val(skinURL).trigger('change');
} else {
var option = new Option('Sample sauvegardé', skinURL);
$('#skinURLSelect').append(option).trigger('change');
}
skinURLSelect.oninput();
}
})
break;
case 'css/jstris-skin.css':
localStorage[skinURLSelect.name] = skinURLSelect.value
skinURLSelect.name = "jstrisSkinURL"
$("#skinURLSelect").empty();
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} by ${skin.author}`,
})),
};
data.push(groupData);
}
skinURLSelect.disabled = false;
$('#skinURLSelect').select2({data: data})
if (skinURL = localStorage['jstrisSkinURL']) {
if ($('#skinURLSelect').find(`option[value='${skinURL}']`).length) {
$('#skinURLSelect').val(skinURL).trigger('change');
} else {
var option = new Option('Sample sauvegardé', skinURL);
$('#skinURLSelect').append(option).trigger('change');
}
skinURLSelect.oninput();
}
});
break;
default:
skinURLSelect.disabled = true;
break;
}
}
sceneDiv.onmouseup = document.onmouseleave = function(event) {
mousedown = false
}
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']");
sceneDiv.onwheel = function(event) {
event.preventDefault()
event.stopPropagation()
let tZ = parseInt(getComputedStyle(screenRow).getPropertyValue("--tZ"))
tZ -= event.deltaY
screenRow.style.setProperty("--tZ", tZ + "px")
}
window.onload = function (event) {
restart();
};
+4 -4
View File
@@ -17,7 +17,7 @@ const T_SPIN = {
T_SPIN: "PIROUETTE"
}
// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
// score = AWARDED_LINE_CLEARS[tSpin][clearedLines]
const AWARDED_LINE_CLEARS = {
[T_SPIN.NONE]: [0, 1, 3, 5, 8],
[T_SPIN.MINI]: [1, 2],
@@ -282,18 +282,18 @@ class Matrix extends MinoesTable {
}
clearLines() {
let nbClearedLines = 0
let clearedLines = 0
for (let y=0; y<this.rows; y++) {
let row = this.blocks[y]
if (row.filter(lockedMino => lockedMino).length == this.columns) {
nbClearedLines++
clearedLines++
this.blocks.splice(y, 1)
this.blocks.unshift(Array(matrix.columns))
this.table.rows[y].classList.add("cleared-line-animation")
}
}
this.redraw()
return nbClearedLines
return clearedLines
}
}
Matrix.prototype.init_center = [4, 4]
+260 -205
View File
@@ -1,335 +1,390 @@
const KEY_NAMES = new Proxy({
["ArrowLeft"] : "←",
["←"] : "ArrowLeft",
["ArrowRight"] : "→",
["→"] : "ArrowRight",
["ArrowUp"] : "↑",
["↑"] : "ArrowUp",
["ArrowDown"] : "↓",
["↓"] : "ArrowDown",
[" "] : "Espace",
["Espace"] : " ",
["Escape"] : "Échap.",
["Échap."] : "Escape",
["Backspace"] : "Ret. arrière",
["Ret. arrière"]: "Backspace",
["Enter"] : "Entrée",
["Entrée"] : "Enter",
}, {
get(target, key) {
return key in target? target[key] : key
}
})
const KEY_NAMES = new Proxy(
{
['ArrowLeft']: '←',
['←']: 'ArrowLeft',
['ArrowRight']: '→',
['→']: 'ArrowRight',
['ArrowUp']: '↑',
['↑']: 'ArrowUp',
['ArrowDown']: '↓',
['↓']: 'ArrowDown',
[' ']: 'Espace',
['Espace']: ' ',
['Escape']: 'Échap.',
['Échap.']: 'Escape',
['Backspace']: 'Ret. arrière',
['Ret. arrière']: 'Backspace',
['Enter']: 'Entrée',
['Entrée']: 'Enter',
},
{
get(target, key) {
return key in target ? target[key] : key;
},
},
);
class Settings {
constructor() {
this.form = settingsForm
this.load()
this.modal = new bootstrap.Modal('#settingsModal')
settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus())
this.form = settingsForm;
this.load();
this.modal = new bootstrap.Modal('#settingsModal');
settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus());
}
load() {
this.form.querySelectorAll("[name]").forEach(element => {
if (element.name in localStorage)
element.value = localStorage[element.name]
})
window.document.selectedStyleSheetSet = stylesheetSelect.value
this.form.querySelectorAll('[name]').forEach(element => {
if (element.name in localStorage) element.value = localStorage[element.name];
});
stylesheetSelect.oninput();
}
save() {
this.form.querySelectorAll("[name]").forEach(element => localStorage[element.name] = element.value)
this.form
.querySelectorAll('[name]')
.forEach(element => (localStorage[element.name] = element.value));
}
init() {
this.form.onsubmit = newGame
levelInput.name = "startLevel"
levelInput.disabled = false
titleHeader.innerHTML = "QUATUOR"
resumeButton.innerHTML = "Jouer"
this.form.onsubmit = newGame;
levelInput.name = 'startLevel';
levelInput.disabled = false;
titleHeader.innerHTML = 'QUATUOR';
resumeButton.innerHTML = 'Jouer';
}
show() {
resumeButton.disabled = false
settings.form.classList.remove('was-validated')
settings.modal.show()
settings.form.reportValidity()
resumeButton.disabled = false;
settings.form.classList.remove('was-validated');
settings.modal.show();
settings.form.reportValidity();
}
getInputs() {
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']")) {
this[input.name] = input.valueAsNumber
this[input.name] = input.valueAsNumber;
}
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({}, {
get: (target, key) => target[key.toLowerCase()],
set: (target, key, value) => target[key.toLowerCase()] = value,
has: (target, key) => key.toLowerCase() in target
})
this.keyBind = new Proxy(
{},
{
get: (target, key) => target[key.toLowerCase()],
set: (target, key, value) => (target[key.toLowerCase()] = value),
has: (target, key) => key.toLowerCase() in target,
},
);
for (let actionName in playerActions) {
this.keyBind[settings[actionName]] = playerActions[actionName]
this.keyBind[settings[actionName]] = playerActions[actionName];
}
}
}
function changeKey(input) {
prevValue = input.value
input.value = ""
keyInputs = Array.from(input.form.querySelectorAll("input[type='text']"))
prevValue = input.value;
input.value = '';
keyInputs = Array.from(input.form.querySelectorAll("input[type='text']"));
input.onkeydown = function (event) {
event.preventDefault()
input.value = KEY_NAMES[event.key]
event.preventDefault();
input.value = KEY_NAMES[event.key];
keyInputs.forEach(input => {
input.setCustomValidity("")
input.classList.remove("is-invalid")
})
input.setCustomValidity('');
input.classList.remove('is-invalid');
});
keyInputs.sort((input1, input2) => {
if(input1.value == input2.value) {
input1.setCustomValidity("Touche déjà utilisée")
input1.classList.add("is-invalid")
input2.setCustomValidity("Touche déjà utilisée")
input2.classList.add("is-invalid")
if (input1.value == input2.value) {
input1.setCustomValidity('Touche déjà utilisée');
input1.classList.add('is-invalid');
input2.setCustomValidity('Touche déjà utilisée');
input2.classList.add('is-invalid');
}
return input1.value > input2.value
})
return input1.value > input2.value;
});
if (input.checkValidity()) {
input.blur()
input.blur();
}
}
};
input.onblur = function (event) {
if (!input.value) input.value = prevValue
input.onkeydown = null
input.onblur = null
}
if (!input.value) input.value = prevValue;
input.onkeydown = null;
input.onblur = null;
};
}
class Stats {
constructor() {
this.modal = new bootstrap.Modal('#statsModal')
this.load()
this.modal = new bootstrap.Modal('#statsModal');
this.load();
}
load() {
this.highScore = Number(localStorage["highScore"]) || 0
this.highScore = Number(localStorage['highScore']) || 0;
}
init() {
levelInput.value = localStorage["startLevel"] || 1
this.score = 0
this.goal = 0
this.combo = 0
this.b2b = 0
this.startTime = new Date()
this.lockDelay = DELAY.LOCK
this.totalClearedLines = 0
this.nbQuatuors = 0
this.nbTSpin = 0
this.maxCombo = 0
this.maxB2B = 0
levelInput.value = localStorage['startLevel'] || 1;
this.score = 0;
this.goal = 0;
this.combo = -1;
this.b2b = -1;
this.startTime = new Date();
this.lockDelay = DELAY.LOCK;
this.totalClearedLines = 0;
this.nbQuatuors = 0;
this.nbTSpin = 0;
this.maxCombo = 0;
this.maxB2B = 0;
}
set score(score) {
this._score = score
scoreCell.innerText = score.toLocaleString()
this._score = score;
scoreCell.innerText = score.toLocaleString();
if (score > this.highScore) {
this.highScore = score
this.highScore = score;
}
}
get score() {
return this._score
return this._score;
}
set highScore(highScore) {
this._highScore = highScore
highScoreCell.innerText = highScore.toLocaleString()
this._highScore = highScore;
highScoreCell.innerText = highScore.toLocaleString();
}
get highScore() {
return this._highScore
return this._highScore;
}
set level(level) {
this._level = level
this.goal += level * 5
if (level <= 20){
this.fallPeriod = 1000 * Math.pow(0.8 - ((level - 1) * 0.007), level - 1)
this._level = level;
this.goal += level * 5;
if (level <= 20) {
this.fallPeriod = 1000 * Math.pow(0.8 - (level - 1) * 0.007, level - 1);
}
if (level > 15)
this.lockDelay = 500 * Math.pow(0.9, level - 15)
levelInput.value = level
levelCell.innerText = level
messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>` })
if (level > 15) this.lockDelay = 500 * Math.pow(0.9, level - 15);
levelInput.value = level;
levelCell.innerText = level;
messagesSpan.addNewChild('div', {
className: 'show-level-animation',
innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>`,
});
}
get level() {
return this._level
return this._level;
}
set goal(goal) {
this._goal = goal
goalCell.innerText = goal
this._goal = goal;
goalCell.innerText = goal;
}
get goal() {
return this._goal
return this._goal;
}
set combo(combo) {
this._combo = combo
if (combo > this.maxCombo) this.maxCombo = combo
this._combo = combo;
if (combo > this.maxCombo) this.maxCombo = combo;
}
get combo() {
return this._combo
return this._combo;
}
set b2b(b2b) {
this._b2b = b2b
if (b2b > this.maxB2B) this.maxB2B = b2b
this._b2b = b2b;
if (b2b > this.maxB2B) this.maxB2B = b2b;
}
get b2b() {
return this._b2b
return this._b2b;
}
set time(time) {
this.startTime = new Date() - time
ticktack()
this.startTime = new Date() - time;
ticktack();
}
get time() {
return new Date() - this.startTime
return new Date() - this.startTime;
}
lockDown(tSpin, nbClearedLines) {
this.totalClearedLines += nbClearedLines
if (nbClearedLines == 4) this.nbQuatuors++
if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++
lockDown(tSpin, clearedLines) {
this.totalClearedLines += clearedLines;
if (clearedLines == 4) this.nbQuatuors++;
if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++;
// Cleared lines & T-Spin
let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
let patternScore = 100 * this.level * awardedLineClears
if (tSpin) messagesSpan.addNewChild("div", {
className: "rotate-in-animation",
innerHTML: tSpin
})
if (nbClearedLines) messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
innerHTML: CLEARED_LINES_NAMES[nbClearedLines]
})
let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][clearedLines];
let patternScore = 100 * this.level * awardedLineClears;
if (tSpin)
messagesSpan.addNewChild('div', {
className: 'rotate-in-animation',
innerHTML: tSpin,
});
if (clearedLines)
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
innerHTML: CLEARED_LINES_NAMES[clearedLines],
});
if (patternScore) {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .2s",
innerHTML: patternScore
})
this.score += patternScore
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .2s',
innerHTML: patternScore,
});
this.score += patternScore;
}
// Combo
if (nbClearedLines) {
this.combo++
if (clearedLines) {
this.combo++;
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) {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .4s",
innerHTML: `COMBO<br/>${comboScore}`
})
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .4s',
innerHTML: `COMBO<br/>${comboScore}`,
});
} else {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .4s",
innerHTML: `COMBO x${this.combo}<br/>${comboScore}`
})
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .4s',
innerHTML: `COMBO x${this.combo}<br/>${comboScore}`,
});
}
this.score += comboScore
this.score += comboScore;
}
} else {
this.combo = -1
if (this.combo >= 1) playSound(combobreak)
this.combo = -1;
}
// Back to back sequence
if ((nbClearedLines == 4) || (tSpin && nbClearedLines)) {
this.b2b++
if (clearedLines == 4 || (tSpin && clearedLines)) {
this.b2b++;
if (this.b2b >= 1) {
let b2bScore = patternScore / 2
let b2bScore = patternScore / 2;
if (this.b2b == 1) {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .4s",
innerHTML: `BOUT À BOUT<br/>${b2bScore}`
})
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .4s',
innerHTML: `BOUT À BOUT<br/>${b2bScore}`,
});
} else {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .4s",
innerHTML: `BOUT À BOUT x${this.b2b}<br/>${b2bScore}`
})
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .4s',
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) {
messagesSpan.addNewChild("div", {
className: "zoom-in-animation",
style: "animation-delay: .4s",
innerHTML: `FIN DU BOUT À BOUT`
})
messagesSpan.addNewChild('div', {
className: 'zoom-in-animation',
style: 'animation-delay: .4s',
innerHTML: `FIN DU BOUT À BOUT`,
});
playSound(btb_break)
}
this.b2b = -1
this.b2b = -1;
}
// Sound
// Sounds
if (sfxVolumeRange.value) {
if (nbClearedLines == 4) playSound(quatuorSound, this.combo)
else if (nbClearedLines) playSound(lineClearSound, this.combo)
if (tSpin) playSound(tSpinSound, this.combo)
if (clearedLines == 4 || (tSpin && clearedLines)) switch(this.b2b) {
case -1:
case 0: playSound(clearbtb); break
case 1: playSound(btb_1); break
case 2: playSound(btb_2); break
default: playSound(btb_3)
} else if (tSpin) switch(this.combo) {
case -1: playSound(spin); break
case 0: playSound(clearspin); break
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 switch(this.combo) {
case -1: break;
case 0: playSound(clearline); break
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)
}
}
this.goal -= awardedLineClears
if (this.goal <= 0) this.level++
this.goal -= awardedLineClears;
if (this.goal <= 0) this.level++;
}
show() {
let time = stats.time
statsModalScoreCell.innerText = this.score.toLocaleString()
statsModalHighScoreCell.innerText = this.highScore.toLocaleString()
statsModalLevelCell.innerText = this.level
statsModalTimeCell.innerText = this.timeFormat.format(time)
statsModaltotalClearedLines.innerText = this.totalClearedLines
statsModaltotalClearedLinesPM.innerText = (stats.totalClearedLines * 60000 / time).toFixed(2)
statsModalNbQuatuors.innerText = this.nbQuatuors
statsModalNbTSpin.innerText = this.nbTSpin
statsModalMaxCombo.innerText = this.maxCombo
statsModalMaxB2B.innerText = this.maxB2B
this.modal.show()
let time = stats.time;
statsModalScoreCell.innerText = this.score.toLocaleString();
statsModalHighScoreCell.innerText = this.highScore.toLocaleString();
statsModalLevelCell.innerText = this.level;
statsModalTimeCell.innerText = this.timeFormat.format(time);
statsModaltotalClearedLines.innerText = this.totalClearedLines;
statsModaltotalClearedLinesPM.innerText = (
(stats.totalClearedLines * 60000) /
time
).toFixed(2);
statsModalNbQuatuors.innerText = this.nbQuatuors;
statsModalNbTSpin.innerText = this.nbTSpin;
statsModalMaxCombo.innerText = this.maxCombo;
statsModalMaxB2B.innerText = this.maxB2B;
this.modal.show();
}
save() {
localStorage["highScore"] = this.highScore
localStorage['highScore'] = this.highScore;
}
}
Stats.prototype.timeFormat = new Intl.DateTimeFormat("fr-FR", {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
timeZone: "UTC"
})
Stats.prototype.timeFormat = new Intl.DateTimeFormat('fr-FR', {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
timeZone: 'UTC',
});
function playSound(sound, note=0) {
sound.currentTime = 0
sound.playbackRate = Math.pow(5/4, note)
sound.play()
}
function playSound(sound, note = 0) {
sound.currentTime = 0;
sound.playbackRate = note? Math.pow(5 / 4, note): 1;
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.

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