✨ Overview UI
This commit is contained in:
parent
cac20e21ad
commit
f0af12f4bb
BIN
public/fonts/ibm-plex-sans-v19-latin-100.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-100.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-100italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-100italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-200.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-200.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-200italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-200italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-300.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-300.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-300italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-300italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-500.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-500.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-500italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-500italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-600.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-600.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-600italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-600italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-700.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-700.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-700italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-700italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-italic.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-italic.woff2
Executable file
Binary file not shown.
BIN
public/fonts/ibm-plex-sans-v19-latin-regular.woff2
Executable file
BIN
public/fonts/ibm-plex-sans-v19-latin-regular.woff2
Executable file
Binary file not shown.
112
src/assets/fonts.css
Normal file
112
src/assets/fonts.css
Normal file
@ -0,0 +1,112 @@
|
||||
/* ibm-plex-sans-100 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-100.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-100italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-100italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-200 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-200.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-200italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-200italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-300 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-300italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-300italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-regular - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-500 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-500italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-600 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-600italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-600italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-700 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
/* ibm-plex-sans-700italic - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url('/fonts/ibm-plex-sans-v19-latin-700italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
@ -4,4 +4,8 @@
|
||||
|
||||
:root {
|
||||
--color-primary: #49509e;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: 'IBM Plex Sans', sans;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import Stage from "./stage";
|
||||
|
||||
import "./assets/fonts.css";
|
||||
import "./index.css";
|
||||
|
||||
export default function Root() {
|
||||
|
@ -9,6 +9,10 @@ import TimerProvider from "./providers/timer";
|
||||
import CooldownProvider from "./providers/cooldown";
|
||||
import CooldownUI from "./ui/cooldown";
|
||||
|
||||
import "./objects/common.css";
|
||||
import OverviewProvider from "./providers/overview";
|
||||
import OverviewUI from "./ui/overview";
|
||||
|
||||
const StageContainer = styled("div")`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
@ -55,20 +59,23 @@ export default function Stage() {
|
||||
|
||||
return (
|
||||
<TimerProvider>
|
||||
<CooldownProvider>
|
||||
<StageContainer class="stage" ref={container}>
|
||||
<Show when={ready()}>
|
||||
{/* Objects */}
|
||||
<Crystal size={128} position={centerPosition} />
|
||||
<Player size={64} position={playerPosition} />
|
||||
<OverviewProvider>
|
||||
<CooldownProvider>
|
||||
<StageContainer class="stage" ref={container}>
|
||||
<Show when={ready()}>
|
||||
{/* Objects */}
|
||||
<Crystal size={128} position={centerPosition} />
|
||||
<Player size={64} position={playerPosition} />
|
||||
|
||||
<Attacker size={32} position={randomPosition} />
|
||||
<Attacker size={32} position={randomPosition} />
|
||||
|
||||
{/* UI Elements */}
|
||||
<CooldownUI />
|
||||
</Show>
|
||||
</StageContainer>
|
||||
</CooldownProvider>
|
||||
{/* UI Elements */}
|
||||
<CooldownUI />
|
||||
<OverviewUI />
|
||||
</Show>
|
||||
</StageContainer>
|
||||
</CooldownProvider>
|
||||
</OverviewProvider>
|
||||
</TimerProvider>
|
||||
);
|
||||
}
|
||||
|
7
src/stage/objects/common.css
Normal file
7
src/stage/objects/common.css
Normal file
@ -0,0 +1,7 @@
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
svg[data-dispose=yes] {
|
||||
opacity: 0;
|
||||
}
|
@ -3,6 +3,7 @@ import { vector2d } from "../vector";
|
||||
import { createMemo, createSignal } from "solid-js";
|
||||
import { useTimer } from "../providers/timer";
|
||||
import { checkElementOverlap } from "./collision";
|
||||
import { useOverview } from "../providers/overview";
|
||||
|
||||
import "./crystal.css";
|
||||
|
||||
@ -20,6 +21,8 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
|
||||
|
||||
// Health
|
||||
|
||||
const [_, { set }] = useOverview();
|
||||
|
||||
const [health, setHealth] = createSignal(100);
|
||||
const [maxHealth, setMaxHealth] = createSignal(100);
|
||||
|
||||
@ -28,30 +31,32 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
|
||||
const isAlive = createMemo(() => health() > 0);
|
||||
|
||||
function calcHostileCauseDamage() {
|
||||
const units = document.querySelectorAll<HTMLElement>(".hostile");
|
||||
const units = document.querySelectorAll<HTMLElement>(
|
||||
".hostile:not([data-dispose=yes])"
|
||||
);
|
||||
const crystal = document.querySelector<HTMLElement>(".crystal");
|
||||
|
||||
let damage = 0;
|
||||
for (const unit of units) {
|
||||
if (crystal && checkElementOverlap(crystal, unit)) {
|
||||
damage += parseInt(unit.getAttribute("data-damage") ?? "0");
|
||||
unit.setAttribute("data-need-dispose", "yes");
|
||||
unit.setAttribute("data-dispose", "yes");
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
function dealHostileUnderCrystal() {
|
||||
const units = document.querySelectorAll(
|
||||
".attackers[data-need-dispose=yes]"
|
||||
);
|
||||
units.forEach((unit) => unit.remove());
|
||||
const units = document.querySelectorAll(".attackers[data-dispose=yes]");
|
||||
// Delay 175ms to remove that node cuz animation is 150ms plus 25ms for browser rerender gap
|
||||
units.forEach((unit) => setTimeout(() => unit.remove(), 175));
|
||||
}
|
||||
|
||||
function updateHealth() {
|
||||
const damage = calcHostileCauseDamage();
|
||||
if (damage && isAlive()) {
|
||||
setHealth(health() - damage);
|
||||
set("crystal.health", health());
|
||||
console.log("health: ", health());
|
||||
}
|
||||
dealHostileUnderCrystal();
|
||||
|
@ -99,7 +99,7 @@ export default function Attacker(props: {
|
||||
|
||||
transform: rotate(${facingAngle}deg);
|
||||
|
||||
transition-property: transform;
|
||||
transition-property: transform, opacity;
|
||||
transition-timing-function: linear;
|
||||
transition-duration: 150ms;
|
||||
|
||||
|
36
src/stage/providers/overview.tsx
Normal file
36
src/stage/providers/overview.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { createContext, createSignal, useContext } from "solid-js";
|
||||
|
||||
const OverviewContext = createContext();
|
||||
|
||||
export default function OverviewProvider(props: any) {
|
||||
const [stats, setStats] = createSignal<{ [id: string]: any }>({
|
||||
"crystal.health": 100,
|
||||
"crystal.maxHealth": 100,
|
||||
});
|
||||
|
||||
const store = [
|
||||
stats,
|
||||
{
|
||||
set(id: string, value: any) {
|
||||
const original = structuredClone(stats());
|
||||
original[id] = value;
|
||||
setStats(original);
|
||||
},
|
||||
unset(id: string) {
|
||||
const original = structuredClone(stats());
|
||||
delete original[id];
|
||||
setStats(original);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<OverviewContext.Provider value={store}>
|
||||
{props.children}
|
||||
</OverviewContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useOverview(): any[2] {
|
||||
return useContext(OverviewContext);
|
||||
}
|
34
src/stage/ui/overview.tsx
Normal file
34
src/stage/ui/overview.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { styled } from "solid-styled-components";
|
||||
import { useOverview } from "../providers/overview";
|
||||
import { createMemo } from "solid-js";
|
||||
|
||||
export default function OverviewUI() {
|
||||
const [overview] = useOverview();
|
||||
|
||||
const crystalHealth = createMemo(() => overview()["crystal.health"]);
|
||||
const crystalMaxHealth = createMemo(() => overview()["crystal.maxHealth"]);
|
||||
const regenerationProgress = createMemo(
|
||||
() => (crystalHealth() / crystalMaxHealth()) * 100
|
||||
);
|
||||
|
||||
const BottomSideContainer = styled("div")`
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
width: 100vw;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<BottomSideContainer class="healthy-overview">
|
||||
<div class="text-xl">Crystal {crystalHealth()}/{crystalMaxHealth()}</div>
|
||||
<progress class="h-[4px] progress progress-error w-screen" value={regenerationProgress()} max={100} />
|
||||
</BottomSideContainer>
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user