Minimal HomeAction and Tabs

This commit is contained in:
Tobias Berger 2021-09-27 18:22:35 +02:00
parent e35219c1cf
commit 625000c308
14 changed files with 347 additions and 81 deletions

View 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>

View file

@ -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">

View file

@ -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 type { Writable } from "svelte/store";
import type { Resource } from "../../shark/data/Resources";
import ResourceGroup from "./ResourceGroup.svelte";
let collapsed: string[] = [];
$: groups = resourceGroups();
let groups: ReturnType<typeof resourceGroups>;
export let resources: Writable<Record<string, Resource>>;
function resourceGroups() {
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]
) => {
if (reduced[resource.category] === undefined) {
reduced[resource.category] = [];
}
reduced[resource.category] = [
...reduced[resource.category],
[resourceName, resource],
];
Object.entries(resources).reduce((reduced, [resourceName, resource]) => {
if (reduced[resource.category] === undefined) {
reduced[resource.category] = [];
}
reduced[resource.category].push([resourceName, resource]);
return reduced;
},
{}
)
return reduced;
}, {} as Record<string, [string, Resource][]>)
);
}

View 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>

View 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>

View file

@ -0,0 +1 @@
Haha, Laboratory go <em>BRRRRRRRRRRRRRRR!</em>

View file

@ -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
View file

@ -1 +1,9 @@
/// <reference types="svelte" />
import type { SharkGame } from "./shark/SharkGame";
declare global {
interface Window {
SharkGame: SharkGame;
}
}

View file

@ -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
View 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";
}

View 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.",
},
};
}

View 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);
}
}

View file

@ -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;

View file

@ -4,5 +4,9 @@
"target": "es2021",
"include": ["src/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
"exclude": ["node_modules/*", "__sapper__/*", "public/*"],
"compilerOptions": {
"strict": true
}
}