Spawn farm

This commit is contained in:
LittleSheep 2024-02-16 16:23:23 +08:00
parent f0af12f4bb
commit f64f3bd544
6 changed files with 98 additions and 23 deletions

13
src/signal.ts Normal file
View File

@ -0,0 +1,13 @@
export type SignalSubscriber = (props?: any) => void;
export default class SignalBus {
subscribers: SignalSubscriber[] = [];
emit(props?: any) {
this.subscribers.forEach((item) => item(props));
}
subscribe(callback: SignalSubscriber) {
this.subscribers.push(callback);
}
}

View File

@ -1,10 +1,10 @@
import { styled } from "solid-styled-components"; import { styled } from "solid-styled-components";
import { Show, createSignal, onMount } from "solid-js"; import { Show, createSignal, onMount } from "solid-js";
import { vector2d } from "./vector"; import { vector2d } from "./vector";
import SignalBus from "../signal";
import Player from "./objects/player"; import Player from "./objects/player";
import Crystal from "./objects/crystal"; import Crystal from "./objects/crystal";
import Attacker from "./objects/enemies/attacker";
import TimerProvider from "./providers/timer"; 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";
@ -12,6 +12,7 @@ import CooldownUI from "./ui/cooldown";
import "./objects/common.css"; import "./objects/common.css";
import OverviewProvider from "./providers/overview"; import OverviewProvider from "./providers/overview";
import OverviewUI from "./ui/overview"; import OverviewUI from "./ui/overview";
import SpawnFarm, { EnemiesTemplates } from "./objects/spawn-farm";
const StageContainer = styled("div")` const StageContainer = styled("div")`
width: 100vw; width: 100vw;
@ -25,32 +26,20 @@ const StageContainer = styled("div")`
export default function Stage() { export default function Stage() {
let container: HTMLDivElement = document.createElement("div"); let container: HTMLDivElement = document.createElement("div");
let stageSize = new vector2d(0, 0);
let centerPosition = new vector2d(0, 0); let centerPosition = new vector2d(0, 0);
let playerPosition = new vector2d(0, 0); let playerPosition = new vector2d(0, 0);
let randomPosition = new vector2d(0, 0);
let [ready, setReady] = createSignal(false); let [ready, setReady] = createSignal(false);
function calcRandomPosition(maxX: number, maxY: number) { const enemiesSpawnSignal = new SignalBus();
return new vector2d(
Math.floor(Math.random() * maxX),
Math.floor(Math.random() * maxY)
);
}
function initializeStage(element: HTMLDivElement) { function initializeStage(element: HTMLDivElement) {
centerPosition = new vector2d( stageSize = new vector2d(element.clientWidth, element.clientHeight);
element.clientWidth / 2, centerPosition = new vector2d(stageSize.x / 2, stageSize.y / 2);
element.clientHeight / 2
);
playerPosition = centerPosition.clone(); playerPosition = centerPosition.clone();
randomPosition = calcRandomPosition( setInterval(() => enemiesSpawnSignal.emit(), 1000)
element.clientWidth,
element.clientHeight
);
console.log("example pos:", randomPosition);
setReady(true); setReady(true);
} }
@ -67,7 +56,13 @@ export default function Stage() {
<Crystal size={128} position={centerPosition} /> <Crystal size={128} position={centerPosition} />
<Player size={64} position={playerPosition} /> <Player size={64} position={playerPosition} />
<Attacker size={32} position={randomPosition} /> {/* Object Sets */}
<SpawnFarm
templates={EnemiesTemplates}
signal={enemiesSpawnSignal}
borderX={stageSize.x}
borderY={stageSize.y}
/>
{/* UI Elements */} {/* UI Elements */}
<CooldownUI /> <CooldownUI />

View File

@ -10,8 +10,8 @@ export default function Attacker(props: {
damage?: number; damage?: number;
position?: vector2d; position?: vector2d;
}) { }) {
const width = props.size ? props.size / 6 : 2; const width = props.size ? props.size / 6 : 5;
const height = props.size ?? 20; const height = props.size ?? 32;
// Just some random comment // Just some random comment
// https://youtube.com/ishowspeed // https://youtube.com/ishowspeed

View File

@ -0,0 +1,67 @@
import { createSignal, type Component, JSX, For, createMemo } from "solid-js";
import { vector2d } from "../vector";
import SignalBus from "../../signal";
import Attacker from "./enemies/attacker";
import { TimerContext, useTimer } from "../providers/timer";
export const EnemiesTemplates = [
{ element: Attacker, weight: 10 },
{ element: Attacker, weight: 10 },
];
export default function SpawnFarm({
templates,
signal,
borderX,
borderY,
}: {
templates: { element: Component; weight: number }[];
signal: SignalBus;
borderX: number;
borderY: number;
}) {
const [objects, setObjects] = createSignal<JSX.Element[]>([]);
const timer = useTimer();
function createObject(template: Component, props?: any) {
return (
<TimerContext.Provider value={timer}>
{template(props)}
</TimerContext.Provider>
);
}
function pickTemplate() {
const totalWeight = templates.reduce((sum, { weight }) => sum + weight, 0);
const randomNumber = Math.random() * totalWeight;
let cumulativeWeight = 0;
for (let i = 0; i < templates.length; i++) {
cumulativeWeight += templates[i].weight;
if (randomNumber < cumulativeWeight) {
return templates[i].element;
}
}
}
function pickPosition() {
return new vector2d(
Math.floor(Math.random() * borderX),
Math.floor(Math.random() * borderY)
);
}
function generate() {
const template = pickTemplate();
if (template) {
const object = createObject(template, { position: pickPosition() });
setObjects(objects().concat([object]));
}
}
signal.subscribe(() => generate());
return <For each={objects()}>{(item) => item}</For>;
}

View File

@ -6,7 +6,7 @@ import {
useContext, useContext,
} from "solid-js"; } from "solid-js";
const TimerContext = createContext(); export const TimerContext = createContext();
export const TickDuration = 100; export const TickDuration = 100;

View File

@ -26,7 +26,7 @@ export default function OverviewUI() {
return ( return (
<> <>
<BottomSideContainer class="healthy-overview"> <BottomSideContainer class="healthy-overview">
<div class="text-xl">Crystal {crystalHealth()}/{crystalMaxHealth()}</div> <div class="text-lg">Crystal {crystalHealth()}/{crystalMaxHealth()}</div>
<progress class="h-[4px] progress progress-error w-screen" value={regenerationProgress()} max={100} /> <progress class="h-[4px] progress progress-error w-screen" value={regenerationProgress()} max={100} />
</BottomSideContainer> </BottomSideContainer>
</> </>