Minimal HomeAction and Tabs
This commit is contained in:
parent
e35219c1cf
commit
625000c308
14 changed files with 347 additions and 81 deletions
16
src/components/Game.svelte
Normal file
16
src/components/Game.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { SharkGame } from "../shark/SharkGame";
|
||||
import type { Tabs } from "../shark/Tabs";
|
||||
|
||||
export let currentTab: typeof Tabs.currentTab;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<svelte:component this={SharkGame.Tabs.Tabs[currentTab]} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
height: calc(100% - 2em);
|
||||
}
|
||||
</style>
|
|
@ -1,10 +1,11 @@
|
|||
<svelte:options immutable />
|
||||
|
||||
<script lang="ts">
|
||||
import type { ResourceEntry } from "./ResourceTable.svelte";
|
||||
import type { Resource } from "../../shark/data/Resources";
|
||||
|
||||
export let categoryName: string;
|
||||
export let collapsed: boolean;
|
||||
export let resources: ResourceEntry[];
|
||||
export let resources: [string, Resource][];
|
||||
</script>
|
||||
|
||||
<tr on:click class="subhead" tabindex="0" role="button">
|
||||
|
|
|
@ -1,85 +1,29 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script context="module" lang="ts">
|
||||
export type ResourceEntry = [string, typeof TEST_RESOURCES["Shark"]];
|
||||
const TEST_RESOURCES = {
|
||||
Shark: {
|
||||
amount: 100,
|
||||
category: "Frenzy",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Ray: {
|
||||
amount: 170,
|
||||
category: "Frenzy",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand1: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand2: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand3: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand4: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand5: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand6: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
Sand7: {
|
||||
amount: 12,
|
||||
category: "Garbage",
|
||||
change: (Math.random() - 0.5) * 30,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<svelte:options immutable />
|
||||
|
||||
<script lang="ts">
|
||||
import ResourceGroup from "./ResourceGroup.svelte";
|
||||
let collapsed: string[] = [];
|
||||
$: groups = resourceGroups();
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
function resourceGroups() {
|
||||
import type { Resource } from "../../shark/data/Resources";
|
||||
import ResourceGroup from "./ResourceGroup.svelte";
|
||||
|
||||
let collapsed: string[] = [];
|
||||
let groups: ReturnType<typeof resourceGroups>;
|
||||
export let resources: Writable<Record<string, Resource>>;
|
||||
|
||||
resources.subscribe((val) => {
|
||||
groups = resourceGroups(val);
|
||||
});
|
||||
|
||||
function resourceGroups(resources: Record<string, Resource>) {
|
||||
return Object.entries(
|
||||
Object.entries(TEST_RESOURCES).reduce(
|
||||
(
|
||||
reduced: { [key: string]: ResourceEntry[] },
|
||||
[resourceName, resource]
|
||||
) => {
|
||||
Object.entries(resources).reduce((reduced, [resourceName, resource]) => {
|
||||
if (reduced[resource.category] === undefined) {
|
||||
reduced[resource.category] = [];
|
||||
}
|
||||
reduced[resource.category] = [
|
||||
...reduced[resource.category],
|
||||
[resourceName, resource],
|
||||
];
|
||||
reduced[resource.category].push([resourceName, resource]);
|
||||
|
||||
return reduced;
|
||||
},
|
||||
{}
|
||||
)
|
||||
}, {} as Record<string, [string, Resource][]>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
71
src/components/TabSelector.svelte
Normal file
71
src/components/TabSelector.svelte
Normal file
|
@ -0,0 +1,71 @@
|
|||
<script lang="ts">
|
||||
import type { Tabs } from "../shark/Tabs";
|
||||
|
||||
export let tabs: typeof Tabs.Tabs;
|
||||
export let selectedTab: typeof Tabs.currentTab;
|
||||
$: tabNames = Object.keys(tabs) as typeof selectedTab[];
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
{#each tabNames as tab}
|
||||
<li>
|
||||
<a
|
||||
on:click={() => {
|
||||
selectedTab = tab;
|
||||
}}
|
||||
aria-selected={tab === selectedTab}
|
||||
href={"javascript:;"}>{tab}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
width: 100%;
|
||||
background-color: var(--color-dark);
|
||||
text-shadow: 2px 2px 3px var(--color-darker);
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
> ul {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
|
||||
margin: 0.2em;
|
||||
padding: 0.2em 0.6em;
|
||||
|
||||
> li {
|
||||
pointer-events: initial;
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
user-select: none;
|
||||
|
||||
&:not(:first-child):before {
|
||||
content: " | ";
|
||||
}
|
||||
|
||||
> a {
|
||||
color: var(--color-lighter);
|
||||
}
|
||||
> a:not([aria-selected="true"]):hover {
|
||||
filter: brightness(130%);
|
||||
}
|
||||
> a[aria-selected="true"] {
|
||||
pointer-events: none;
|
||||
text-decoration: none;
|
||||
color: var(--color-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
41
src/components/Tabs/Home.svelte
Normal file
41
src/components/Tabs/Home.svelte
Normal file
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import type { HomeAction } from "../../shark/data/HomeActions";
|
||||
import { HomeActions } from "../../shark/data/HomeActions";
|
||||
import { SharkGame } from "../../shark/SharkGame";
|
||||
|
||||
function homeActionClick(homeAction: HomeAction) {
|
||||
if (homeAction.effect.resource) {
|
||||
const resources = SharkGame.Resources.getResources(
|
||||
Object.keys(homeAction.effect.resource)
|
||||
);
|
||||
|
||||
if (resources !== null) {
|
||||
for (const [resourceName, resource] of Object.entries(resources)) {
|
||||
resource.amount += homeAction.effect.resource[resourceName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (homeAction.outcomes) {
|
||||
SharkGame.Log.addMessage(
|
||||
homeAction.outcomes[
|
||||
Math.floor(Math.random() * homeAction.outcomes.length)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each Object.entries(HomeActions.getActionTable()) as [homeActionId, homeAction]}
|
||||
<button on:click={() => homeActionClick(homeAction)} id={homeActionId}>
|
||||
{homeAction.name}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
1
src/components/Tabs/Lab.svelte
Normal file
1
src/components/Tabs/Lab.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
Haha, Laboratory go <em>BRRRRRRRRRRRRRRR!</em>
|
|
@ -1,14 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { SharkGame } from "../shark/SharkGame";
|
||||
import Game from "./Game.svelte";
|
||||
import Log from "./Log.svelte";
|
||||
import ResourceTable from "./ResourceTable/ResourceTable.svelte";
|
||||
import TabSelector from "./TabSelector.svelte";
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div id="left-column">
|
||||
<ResourceTable />
|
||||
<ResourceTable bind:resources={SharkGame.Resources.Resources} />
|
||||
<Log bind:messages={SharkGame.Log.messages} />
|
||||
</div>
|
||||
<div id="right-column">
|
||||
<TabSelector
|
||||
bind:tabs={SharkGame.Tabs.Tabs}
|
||||
bind:selectedTab={SharkGame.Tabs.currentTab}
|
||||
/>
|
||||
<Game bind:currentTab={SharkGame.Tabs.currentTab} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -30,5 +39,10 @@
|
|||
width: 20%;
|
||||
min-width: 20em;
|
||||
}
|
||||
|
||||
> #right-column {
|
||||
width: 80%;
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
8
src/global.d.ts
vendored
8
src/global.d.ts
vendored
|
@ -1 +1,9 @@
|
|||
/// <reference types="svelte" />
|
||||
|
||||
import type { SharkGame } from "./shark/SharkGame";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
SharkGame: SharkGame;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Resources } from "./data/Resources";
|
||||
import { Log } from "./Log";
|
||||
import { Settings } from "./Settings";
|
||||
import { StaticClass } from "./StaticClass";
|
||||
import { Tabs } from "./Tabs";
|
||||
|
||||
export class SharkGame extends StaticClass {
|
||||
static readonly #GAME_NAMES = [
|
||||
|
@ -55,6 +57,8 @@ export class SharkGame extends StaticClass {
|
|||
static title: string;
|
||||
static readonly Settings = Settings;
|
||||
static readonly Log = Log;
|
||||
static readonly Resources = Resources;
|
||||
static readonly Tabs = Tabs;
|
||||
|
||||
static init(): void {
|
||||
for (const setting of Object.values(Settings)) {
|
||||
|
@ -68,4 +72,4 @@ export class SharkGame extends StaticClass {
|
|||
}
|
||||
}
|
||||
|
||||
globalThis.SharkGame = SharkGame;
|
||||
window.SharkGame = SharkGame;
|
||||
|
|
11
src/shark/Tabs.ts
Normal file
11
src/shark/Tabs.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import Home from "../components/Tabs/Home.svelte";
|
||||
import Lab from "../components/Tabs/Lab.svelte";
|
||||
import { StaticClass } from "./StaticClass";
|
||||
|
||||
export class Tabs extends StaticClass {
|
||||
static readonly Tabs = {
|
||||
Home,
|
||||
Lab,
|
||||
} as const;
|
||||
static currentTab: keyof typeof Tabs.Tabs = "Home";
|
||||
}
|
73
src/shark/data/HomeActions.ts
Normal file
73
src/shark/data/HomeActions.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { StaticClass } from "../StaticClass";
|
||||
|
||||
export type HomeAction = {
|
||||
name: string;
|
||||
effect: {
|
||||
resource: Record<string, number>;
|
||||
};
|
||||
outcomes: string[];
|
||||
multiOutcomes?: string[];
|
||||
helpText: string;
|
||||
};
|
||||
|
||||
export class HomeActions extends StaticClass {
|
||||
static getActionTable(): { [actionName: string]: HomeAction } {
|
||||
return HomeActions.actionTable;
|
||||
}
|
||||
|
||||
static actionTable = {
|
||||
catchFish: {
|
||||
name: "Catch fish",
|
||||
effect: {
|
||||
resource: {
|
||||
fish: 1,
|
||||
},
|
||||
},
|
||||
cost: {},
|
||||
prereq: {},
|
||||
outcomes: [
|
||||
"Dropped the bass.",
|
||||
"Ate a kipper. Wait. Hang on.",
|
||||
"You eat a fish hooray!",
|
||||
"Fish.",
|
||||
"Ate a shark. Wait. No, it wasn't a shark.",
|
||||
"Ate an anchovy.",
|
||||
"Ate a catfish.",
|
||||
"Ate a flounder.",
|
||||
"Ate a haddock.",
|
||||
"Ate a herring.",
|
||||
"Ate a mackerel.",
|
||||
"Ate a mullet.",
|
||||
"Ate a perch.",
|
||||
"Ate a pollock.",
|
||||
"Ate a salmon.",
|
||||
"Ate a sardine.",
|
||||
"Ate a sole.",
|
||||
"Ate a tilapia.",
|
||||
"Ate a trout.",
|
||||
"Ate a whitefish.",
|
||||
"Ate a bass.",
|
||||
"Ate a carp.",
|
||||
"Ate a cod.",
|
||||
"Ate a halibut.",
|
||||
"Ate a mahi mahi.",
|
||||
"Ate a monkfish.",
|
||||
"Ate a perch.",
|
||||
"Ate a snapper.",
|
||||
"Ate a bluefish.",
|
||||
"Ate a grouper.",
|
||||
"Ate a sea bass.",
|
||||
"Ate a yellowfin tuna.",
|
||||
"Ate a marlin.",
|
||||
"Ate an orange roughy.",
|
||||
"Ate a shark.",
|
||||
"Ate a swordfish.",
|
||||
"Ate a tilefish.",
|
||||
"Ate a tuna.",
|
||||
"Ate a swedish fish.",
|
||||
"Ate a goldfish.",
|
||||
],
|
||||
helpText: "Use your natural shark prowess to find and catch a fish.",
|
||||
},
|
||||
};
|
||||
}
|
73
src/shark/data/Resources.ts
Normal file
73
src/shark/data/Resources.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { writable } from "svelte/store";
|
||||
|
||||
import { StaticClass } from "../StaticClass";
|
||||
|
||||
export class Resource {
|
||||
category: string;
|
||||
amount: number;
|
||||
change: number;
|
||||
|
||||
constructor(category: string) {
|
||||
this.category = category;
|
||||
this.amount = 0;
|
||||
this.change = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class Resources extends StaticClass {
|
||||
static readonly Resources = writable<Record<string, Resource>>({
|
||||
shark: new Resource("Frenzy"),
|
||||
ray: new Resource("Frenzy"),
|
||||
fish: new Resource("Animals"),
|
||||
});
|
||||
|
||||
static createResource(name: string, category: string): void {
|
||||
Resources.Resources.update((res) => {
|
||||
res[name] = new Resource(category);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
static setResource(resourceName: string, amount: number): void {
|
||||
Resources.Resources.update((res) => {
|
||||
res[resourceName].amount = amount;
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
static getResource(resourceName: string): Resource | null {
|
||||
let result: null | Resource = null;
|
||||
Resources.Resources.update((res) => {
|
||||
if (Object.hasOwnProperty.call(res, resourceName))
|
||||
result = res[resourceName];
|
||||
return res;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
static getResources(resourceNames: string[]): Record<string, Resource> | null;
|
||||
static getResources(
|
||||
...resourceNames: string[]
|
||||
): Record<string, Resource> | null;
|
||||
static getResources(
|
||||
...resourceNames: string[] | string[][]
|
||||
): Record<string, Resource> | null {
|
||||
if (Array.isArray(resourceNames[0])) {
|
||||
resourceNames = resourceNames[0];
|
||||
}
|
||||
|
||||
let result: null | [string, Resource][] = null;
|
||||
Resources.Resources.update((res) => {
|
||||
for (const resourceName of resourceNames as string[]) {
|
||||
if (Object.hasOwnProperty.call(res, resourceName)) {
|
||||
if (result === null) {
|
||||
result = [];
|
||||
}
|
||||
result.push([resourceName, res[resourceName]]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
return result === null ? null : Object.fromEntries(result);
|
||||
}
|
||||
}
|
|
@ -23,13 +23,18 @@ a {
|
|||
button {
|
||||
background-color: var(--color-light);
|
||||
border: 1px solid var(--color-lighter);
|
||||
border-radius: 10px;
|
||||
border-radius: 5px;
|
||||
text-shadow: 0 0 2px black;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 2px 2px var(--color-lighter);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--color-dark);
|
||||
color: var(--color-light);
|
||||
box-shadow: 0 3px 7px 7px var(--color-darker) inset;
|
||||
}
|
||||
|
||||
&:not(:disabled) {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -4,5 +4,9 @@
|
|||
"target": "es2021",
|
||||
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"],
|
||||
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue