✨ 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 {
|
:root {
|
||||||
--color-primary: #49509e;
|
--color-primary: #49509e;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
font-family: 'IBM Plex Sans', sans;
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import Stage from "./stage";
|
import Stage from "./stage";
|
||||||
|
|
||||||
|
import "./assets/fonts.css";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
|
||||||
export default function Root() {
|
export default function Root() {
|
||||||
|
@ -9,6 +9,10 @@ import TimerProvider from "./providers/timer";
|
|||||||
import CooldownProvider from "./providers/cooldown";
|
import CooldownProvider from "./providers/cooldown";
|
||||||
import CooldownUI from "./ui/cooldown";
|
import CooldownUI from "./ui/cooldown";
|
||||||
|
|
||||||
|
import "./objects/common.css";
|
||||||
|
import OverviewProvider from "./providers/overview";
|
||||||
|
import OverviewUI from "./ui/overview";
|
||||||
|
|
||||||
const StageContainer = styled("div")`
|
const StageContainer = styled("div")`
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@ -55,20 +59,23 @@ export default function Stage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TimerProvider>
|
<TimerProvider>
|
||||||
<CooldownProvider>
|
<OverviewProvider>
|
||||||
<StageContainer class="stage" ref={container}>
|
<CooldownProvider>
|
||||||
<Show when={ready()}>
|
<StageContainer class="stage" ref={container}>
|
||||||
{/* Objects */}
|
<Show when={ready()}>
|
||||||
<Crystal size={128} position={centerPosition} />
|
{/* Objects */}
|
||||||
<Player size={64} position={playerPosition} />
|
<Crystal size={128} position={centerPosition} />
|
||||||
|
<Player size={64} position={playerPosition} />
|
||||||
|
|
||||||
<Attacker size={32} position={randomPosition} />
|
<Attacker size={32} position={randomPosition} />
|
||||||
|
|
||||||
{/* UI Elements */}
|
{/* UI Elements */}
|
||||||
<CooldownUI />
|
<CooldownUI />
|
||||||
</Show>
|
<OverviewUI />
|
||||||
</StageContainer>
|
</Show>
|
||||||
</CooldownProvider>
|
</StageContainer>
|
||||||
|
</CooldownProvider>
|
||||||
|
</OverviewProvider>
|
||||||
</TimerProvider>
|
</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 { createMemo, createSignal } from "solid-js";
|
||||||
import { useTimer } from "../providers/timer";
|
import { useTimer } from "../providers/timer";
|
||||||
import { checkElementOverlap } from "./collision";
|
import { checkElementOverlap } from "./collision";
|
||||||
|
import { useOverview } from "../providers/overview";
|
||||||
|
|
||||||
import "./crystal.css";
|
import "./crystal.css";
|
||||||
|
|
||||||
@ -20,6 +21,8 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
|
|||||||
|
|
||||||
// Health
|
// Health
|
||||||
|
|
||||||
|
const [_, { set }] = useOverview();
|
||||||
|
|
||||||
const [health, setHealth] = createSignal(100);
|
const [health, setHealth] = createSignal(100);
|
||||||
const [maxHealth, setMaxHealth] = 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);
|
const isAlive = createMemo(() => health() > 0);
|
||||||
|
|
||||||
function calcHostileCauseDamage() {
|
function calcHostileCauseDamage() {
|
||||||
const units = document.querySelectorAll<HTMLElement>(".hostile");
|
const units = document.querySelectorAll<HTMLElement>(
|
||||||
|
".hostile:not([data-dispose=yes])"
|
||||||
|
);
|
||||||
const crystal = document.querySelector<HTMLElement>(".crystal");
|
const crystal = document.querySelector<HTMLElement>(".crystal");
|
||||||
|
|
||||||
let damage = 0;
|
let damage = 0;
|
||||||
for (const unit of units) {
|
for (const unit of units) {
|
||||||
if (crystal && checkElementOverlap(crystal, unit)) {
|
if (crystal && checkElementOverlap(crystal, unit)) {
|
||||||
damage += parseInt(unit.getAttribute("data-damage") ?? "0");
|
damage += parseInt(unit.getAttribute("data-damage") ?? "0");
|
||||||
unit.setAttribute("data-need-dispose", "yes");
|
unit.setAttribute("data-dispose", "yes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dealHostileUnderCrystal() {
|
function dealHostileUnderCrystal() {
|
||||||
const units = document.querySelectorAll(
|
const units = document.querySelectorAll(".attackers[data-dispose=yes]");
|
||||||
".attackers[data-need-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));
|
||||||
units.forEach((unit) => unit.remove());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHealth() {
|
function updateHealth() {
|
||||||
const damage = calcHostileCauseDamage();
|
const damage = calcHostileCauseDamage();
|
||||||
if (damage && isAlive()) {
|
if (damage && isAlive()) {
|
||||||
setHealth(health() - damage);
|
setHealth(health() - damage);
|
||||||
|
set("crystal.health", health());
|
||||||
console.log("health: ", health());
|
console.log("health: ", health());
|
||||||
}
|
}
|
||||||
dealHostileUnderCrystal();
|
dealHostileUnderCrystal();
|
||||||
|
@ -99,7 +99,7 @@ export default function Attacker(props: {
|
|||||||
|
|
||||||
transform: rotate(${facingAngle}deg);
|
transform: rotate(${facingAngle}deg);
|
||||||
|
|
||||||
transition-property: transform;
|
transition-property: transform, opacity;
|
||||||
transition-timing-function: linear;
|
transition-timing-function: linear;
|
||||||
transition-duration: 150ms;
|
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