cleaner presentation and adding a second experience
This commit is contained in:
@@ -1,15 +1,9 @@
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
background: #000;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import Router from 'svelte-spa-router';
|
||||
|
||||
// importing all the different screens
|
||||
import Home from './routes/Home.svelte';
|
||||
import LevelSelect from './routes/LevelSelect.svelte';
|
||||
import Game from './routes/Game.svelte';
|
||||
@@ -15,9 +15,103 @@ const routes = {
|
||||
'/gameover': GameOver,
|
||||
'/win': Win,
|
||||
};
|
||||
|
||||
const COLORS = [
|
||||
'#970505', '#CF8917', '#E3D214', '#39BD1C',
|
||||
'#12B6C8', '#170CB7', '#6613BA', '#C71287',
|
||||
'#753F16', '#FFD700',
|
||||
];
|
||||
|
||||
let stars = [];
|
||||
|
||||
onMount(() => {
|
||||
stars = Array.from({ length: 90 }, () => ({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
size: Math.random() * 2 + 1,
|
||||
color: COLORS[Math.floor(Math.random() * COLORS.length)],
|
||||
delay: -(Math.random() * 6), // negative = start mid-animation so they don't all blink at once
|
||||
duration: Math.random() * 3 + 2,
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
|
||||
<!-- star background -->
|
||||
<div class="starfield" aria-hidden="true">
|
||||
{#each stars as s}
|
||||
<span
|
||||
class="star"
|
||||
style="
|
||||
left: {s.x}%;
|
||||
top: {s.y}%;
|
||||
width: {s.size}px;
|
||||
height: {s.size}px;
|
||||
background: {s.color};
|
||||
box-shadow: 0 0 {s.size * 2}px {s.color};
|
||||
animation-duration: {s.duration}s;
|
||||
animation-delay: {s.delay}s;
|
||||
"
|
||||
></span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- content column -->
|
||||
<div class="stage">
|
||||
<p class="game-title">the full hue</p>
|
||||
<Router {routes} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── starfield ── */
|
||||
.starfield {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.star {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
animation: blink linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,100% { opacity: 0.9; }
|
||||
50% { opacity: 0.08; }
|
||||
}
|
||||
|
||||
/* ── content ── */
|
||||
.stage {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.game-title {
|
||||
margin: 0;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.35em;
|
||||
text-transform: lowercase;
|
||||
color: rgba(255, 255, 255, 0.28);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -134,19 +134,20 @@
|
||||
fragments.push(new Fragment(p, fragData.x, fragData.y, fragData.color));
|
||||
}
|
||||
|
||||
// create enemies from level data
|
||||
// create enemies and tar only when the game hasn't been completed —
|
||||
// after beating all levels the player can explore freely
|
||||
enemies = [];
|
||||
tarPuddles = [];
|
||||
if (!get(gameCompleted)) {
|
||||
for (const enemyData of levelData.enemies) {
|
||||
const e = new Enemy(p, enemyData.x, enemyData.y);
|
||||
e.patrolDistance = enemyData.patrol;
|
||||
enemies.push(e);
|
||||
}
|
||||
|
||||
// create tar puddles from level data
|
||||
tarPuddles = [];
|
||||
for (const tarData of levelData.tar) {
|
||||
tarPuddles.push(new TarPuddle(p, tarData.x, tarData.y));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.draw = () => {
|
||||
@@ -228,14 +229,17 @@
|
||||
}
|
||||
|
||||
// ── 8. FELL OFF SCREEN ────────────────────────────────────────
|
||||
// if player falls below the canvas, lose a life and respawn
|
||||
if (player.sprite.y > p.height + 100 && gameState === 'playing') {
|
||||
if (get(gameCompleted)) {
|
||||
player.respawn(); // NG+: no damage, just put them back
|
||||
} else {
|
||||
const dead = player.loseLife(lives);
|
||||
if (dead) {
|
||||
gameState = 'gameover';
|
||||
setTimeout(() => push(`/gameover?level=${levelNumber}`), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 9. DRAW SPRITES LAST ──────────────────────────────────────
|
||||
// p5play renders all sprites after everything else is drawn
|
||||
|
||||
@@ -9,7 +9,7 @@ export class Enemy{
|
||||
this.speed = 1.5;
|
||||
this.direction = 1 // 1 is right, -1 is left
|
||||
this.startX = x;
|
||||
this.patrolDisctance = 100; // how far it can walk
|
||||
this.patrolDistance = 100; // how far it can walk
|
||||
}
|
||||
|
||||
update(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { push } from 'svelte-spa-router';
|
||||
import {unlockedColors} from '../stores/colorStore.js';
|
||||
import { gameCompleted } from '../stores/colorStore.js';
|
||||
|
||||
function startGame() {
|
||||
push('/levelselect');
|
||||
@@ -8,31 +8,43 @@
|
||||
</script>
|
||||
|
||||
<div class="title-screen">
|
||||
<!--<img src="/backgrounds/title_bg.png" class="bg" alt="title background"/>-->
|
||||
|
||||
{#if $gameCompleted}
|
||||
|
||||
<!-- ── NG+ home screen ── -->
|
||||
<div class="content">
|
||||
<h1 class="rainbow-title">The Full Hue</h1>
|
||||
<p class="ng-lead">you've brought all the color back.</p>
|
||||
<p class="ng-body">
|
||||
go through the world again — now in full color and without sorrows.<br>
|
||||
take your time. view the scenery. learn about your hues.
|
||||
</p>
|
||||
<button on:click={startGame}>go again</button>
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
|
||||
<!-- ── original home screen ── -->
|
||||
<div class="content">
|
||||
<h1>The Full Hue</h1>
|
||||
<p>Bring back your color</p>
|
||||
<p class="tagline">Bring back your color</p>
|
||||
<button on:click={startGame}>begin</button>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.title-screen {
|
||||
width: 800px;
|
||||
height: 450;
|
||||
height: 450px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #111;
|
||||
height: 450px;
|
||||
}
|
||||
.bg{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -42,20 +54,64 @@
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
color: white;
|
||||
padding: 0 80px;
|
||||
}
|
||||
|
||||
/* ── shared heading ── */
|
||||
h1 {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 64px;
|
||||
font-weight: 400;
|
||||
text-shadow: 0 2px 12px rgba(0,0,0,0.7);
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 12px rgba(0,0,0,0.7);
|
||||
}
|
||||
p{
|
||||
|
||||
/* ── original tagline ── */
|
||||
.tagline {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 22px;
|
||||
opacity: 0.8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── NG+ rainbow title ── */
|
||||
.rainbow-title {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#FF4136, #FF851B, #FFDC00, #2ECC40,
|
||||
#0074D9, #B10DC9, #FF69B4, #FF4136
|
||||
);
|
||||
background-size: 200% auto;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
animation: shimmer 4s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
from { background-position: 0% center; }
|
||||
to { background-position: 200% center; }
|
||||
}
|
||||
|
||||
/* ── NG+ text ── */
|
||||
.ng-lead {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 18px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
margin: 4px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ng-body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
line-height: 1.75;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ── shared button ── */
|
||||
button {
|
||||
margin-top: 20px;
|
||||
padding: 12px 40px;
|
||||
@@ -68,5 +124,6 @@
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
button:hover { background: rgba(255, 255, 255, 0.3); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user