wip
This commit is contained in:
commit
cb95512840
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
4
.vscode/extensions.json
vendored
Normal file
4
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
47
README.md
Normal file
47
README.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Astro Starter Kit: Minimal
|
||||
|
||||
```sh
|
||||
npm create astro@latest -- --template minimal
|
||||
```
|
||||
|
||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
|
||||
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
|
||||
[](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```text
|
||||
/
|
||||
├── public/
|
||||
├── src/
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
8
astro.config.mjs
Normal file
8
astro.config.mjs
Normal file
@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [tailwind()]
|
||||
});
|
7455
package-lock.json
generated
Normal file
7455
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "substance-arbitrager",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.7.0",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@orbitdb/core": "^2.2.0",
|
||||
"astro": "^4.11.5",
|
||||
"dexie": "^4.0.7",
|
||||
"nanostores": "^0.10.3",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"daisyui": "^4.12.10"
|
||||
}
|
||||
}
|
9
public/favicon.svg
Normal file
9
public/favicon.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
After Width: | Height: | Size: 749 B |
48
src/components/Game.astro
Normal file
48
src/components/Game.astro
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div id="game" class="screen container mx-auto hidden gap-4">
|
||||
<h1 class="text-5xl text-center text-primary">Substance Arbitrager</h1>
|
||||
<h2 class="text-2xl text-center text-primary"></h2>
|
||||
<div class="w-full flex flex-col justify-center items-center gap-4 mt-16">
|
||||
<div class="w-full flex flex-row justify-center gap-8">
|
||||
<div id="playerMoney"></div><div id="playerLocation"></div>
|
||||
</div>
|
||||
<div id="drugsList" class="w-full flex flex-row justify-center gap-8"></div>
|
||||
</div>
|
||||
<button id="btn-g-back" class="btn btn-outline w-96"
|
||||
>Volver al menu principal</button
|
||||
>
|
||||
</div>
|
||||
<script>
|
||||
import { currentPlayer, loadCurrentPlayer } from "../services/players";
|
||||
import { getCity } from "../services/cities";
|
||||
import screen from "../services/screens";
|
||||
import type { Drug } from "../services/db";
|
||||
import type { City } from "../services/db";
|
||||
document.querySelector("#btn-g-back")!.addEventListener("click", () => {
|
||||
screen.goTo("start-menu");
|
||||
});
|
||||
|
||||
function createDrugElement(drug: Drug, city: City) {
|
||||
const drugElement = document.createElement("div");
|
||||
drugElement.classList.add("drug");
|
||||
drugElement.innerHTML = `
|
||||
<h3>${drug.name}</h3>
|
||||
<p>${drug.basePrice + (drug.basePrice * city.population) / 10000}€</p>
|
||||
`;
|
||||
return drugElement;
|
||||
}
|
||||
|
||||
currentPlayer.subscribe(async (player) => {
|
||||
if (!player) return;
|
||||
document.querySelector("#game > h2")!.textContent = `Hola, ${player.name}`;
|
||||
document.querySelector("#playerMoney")!.textContent =
|
||||
`Dinero: ${player.money}€`;
|
||||
const city = await getCity(player.cityId);
|
||||
document.querySelector("#playerLocation")!.textContent =
|
||||
`Ubicación: ${city!.name}`;
|
||||
});
|
||||
await loadCurrentPlayer();
|
||||
</script>
|
37
src/components/NewGame.astro
Normal file
37
src/components/NewGame.astro
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div id="new-game" class="screen container mx-auto hidden">
|
||||
<h1 class="text-5xl text-center text-primary">Substance Arbitrager</h1>
|
||||
<h2 class="text-2xl text-center text-primary">Nueva partida</h2>
|
||||
<div class="w-full flex flex-col justify-center items-center gap-4 mt-16">
|
||||
<label for="playerName" class="text-primary">Nombre del jugador</label>
|
||||
<input
|
||||
type="text"
|
||||
id="playerName"
|
||||
name="playerName"
|
||||
class="input input-bordered"
|
||||
/>
|
||||
<button id="btn-ng-start" class="btn btn-outline w-96">Comenzar</button>
|
||||
<button id="btn-ng-back" class="btn btn-outline w-96">Volver</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
import screen from "../services/screens";
|
||||
import { createPlayer, selectCurrentPlayer } from "../services/players";
|
||||
|
||||
document.querySelector("#btn-ng-back")!.addEventListener("click", () => {
|
||||
screen.goTo("start-menu");
|
||||
});
|
||||
document
|
||||
.querySelector("#btn-ng-start")!
|
||||
.addEventListener("click", async () => {
|
||||
const playerName = (
|
||||
document.querySelector("#playerName") as HTMLInputElement
|
||||
).value;
|
||||
const player = await createPlayer(playerName);
|
||||
await selectCurrentPlayer(player.id!);
|
||||
screen.goTo("game");
|
||||
});
|
||||
</script>
|
31
src/components/StartMenu.astro
Normal file
31
src/components/StartMenu.astro
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div id="start-menu" class="screen container mx-auto hidden">
|
||||
<h1 class="text-5xl text-center text-primary">Substance Arbitrager</h1>
|
||||
<div class="w-full flex flex-col justify-center items-center gap-4 mt-16">
|
||||
<button id="btn-continue" class="btn btn-outline w-96">Continuar</button>
|
||||
<button id="btn-new-game" class="btn btn-outline w-96">Nueva partida</button
|
||||
>
|
||||
<button id="btn-load-game" class="btn btn-outline w-96"
|
||||
>Cargar partida</button
|
||||
>
|
||||
<button id="btn-config" class="btn btn-outline w-96">Configuración</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
import screen from "../services/screens";
|
||||
document.querySelector("#btn-continue")!.addEventListener("click", () => {
|
||||
screen.goTo("game");
|
||||
});
|
||||
document.querySelector("#btn-new-game")!.addEventListener("click", () => {
|
||||
screen.goTo("new-game");
|
||||
});
|
||||
document.querySelector("#btn-load-game")!.addEventListener("click", () => {
|
||||
screen.goTo("load-game");
|
||||
});
|
||||
document.querySelector("#btn-config")!.addEventListener("click", () => {
|
||||
screen.goTo("config");
|
||||
});
|
||||
</script>
|
1
src/env.d.ts
vendored
Normal file
1
src/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="astro/client" />
|
25
src/pages/index.astro
Normal file
25
src/pages/index.astro
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
import StartMenu from "../components/StartMenu.astro";
|
||||
import NewGame from "../components/NewGame.astro";
|
||||
import Game from "../components/Game.astro";
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Substance Arbitrager</title>
|
||||
</head>
|
||||
<body data-theme="black">
|
||||
<StartMenu />
|
||||
<NewGame />
|
||||
<Game />
|
||||
<script>
|
||||
import screen from "../services/screens";
|
||||
|
||||
screen.loadCurrentScreen();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
5
src/services/cities.ts
Normal file
5
src/services/cities.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { db, type City } from "./db";
|
||||
|
||||
export async function getCity(id: number): Promise<City | undefined> {
|
||||
return await db.cities.get(id);
|
||||
}
|
112
src/services/db.ts
Normal file
112
src/services/db.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import Dexie, { type EntityTable } from "dexie";
|
||||
|
||||
export interface Setting {
|
||||
id?: number;
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface City {
|
||||
id?: number;
|
||||
name: string;
|
||||
population: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface Drug {
|
||||
id?: number;
|
||||
name: string;
|
||||
basePrice: number;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export interface DrugStock {
|
||||
id?: number;
|
||||
drugId: number;
|
||||
cityId: number;
|
||||
amount: number;
|
||||
price: number;
|
||||
}
|
||||
|
||||
export interface Vehicle {
|
||||
id?: number;
|
||||
name: string;
|
||||
image: string;
|
||||
capacity: number;
|
||||
speed: number;
|
||||
price: number;
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
id?: number;
|
||||
name: string;
|
||||
money: number;
|
||||
cityId: number;
|
||||
vehicleId?: number;
|
||||
}
|
||||
|
||||
export interface PlayerInventory {
|
||||
id?: number;
|
||||
playerId: number;
|
||||
drugId: number;
|
||||
amount: number;
|
||||
}
|
||||
|
||||
export interface SavedGame {
|
||||
id?: number;
|
||||
playerId: number;
|
||||
}
|
||||
|
||||
export const db = new Dexie("substanceArbitrager") as Dexie & {
|
||||
settings: EntityTable<Setting, "id">;
|
||||
cities: EntityTable<City, "id">;
|
||||
drugs: EntityTable<Drug, "id">;
|
||||
drugStock: EntityTable<DrugStock, "id">;
|
||||
vehicles: EntityTable<Vehicle, "id">;
|
||||
players: EntityTable<Player, "id">;
|
||||
playerInventory: EntityTable<PlayerInventory, "id">;
|
||||
};
|
||||
|
||||
db.version(1).stores({
|
||||
settings: "++id, &key, value",
|
||||
cities: "++id, name, population, x, y",
|
||||
drugs: "++id, name, basePrice, image",
|
||||
drugStock: "++id, drugId, cityId, amount, price",
|
||||
vehicles: "++id, name, image, capacity, speed, price",
|
||||
players: "++id, name, money, cityId, vehicleId",
|
||||
playerInventory: "++id, playerId, drugId, amount",
|
||||
});
|
||||
|
||||
function initialMigration() {
|
||||
db.settings.bulkPut([
|
||||
{ key: "test", value: "test" },
|
||||
{ key: "test2", value: "test2" },
|
||||
{ key: "freeSlots", value: "3" },
|
||||
]);
|
||||
db.cities.bulkPut([
|
||||
{ name: "Bigastro", population: 1000, x: 12, y: 12 },
|
||||
{ name: "Orihuela", population: 2000, x: 9, y: 11 },
|
||||
{ name: "Torrevieja", population: 3000, x: 14, y: 8 },
|
||||
{ name: "Elche", population: 4000, x: 10, y: 5 },
|
||||
{ name: "Alicante", population: 5000, x: 7, y: 7 },
|
||||
]);
|
||||
db.drugs.bulkPut([
|
||||
{ name: "Cocaine", basePrice: 50, image: "cocaine.png" },
|
||||
{ name: "Marijuana", basePrice: 5, image: "marijuana.png" },
|
||||
{ name: "Heroin", basePrice: 100, image: "heroin.png" },
|
||||
]);
|
||||
db.vehicles.bulkPut([
|
||||
{ name: "Car", image: "car.png", capacity: 100, speed: 60, price: 1000 },
|
||||
{ name: "Van", image: "van.png", capacity: 200, speed: 50, price: 2000 },
|
||||
{
|
||||
name: "Truck",
|
||||
image: "truck.png",
|
||||
capacity: 300,
|
||||
speed: 40,
|
||||
price: 3000,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
db.on("populate", initialMigration);
|
0
src/services/drugs.ts
Normal file
0
src/services/drugs.ts
Normal file
29
src/services/players.ts
Normal file
29
src/services/players.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { db, type Player } from "./db";
|
||||
import { getSetting, setSetting } from "./settings";
|
||||
import { atom } from "nanostores";
|
||||
|
||||
export const currentPlayer = atom<Player | undefined>();
|
||||
|
||||
export async function createPlayer(name: string): Promise<Player> {
|
||||
const player = { name, money: 30, cityId: 1 };
|
||||
const id = await db.players.add(player);
|
||||
return { id, ...player };
|
||||
}
|
||||
|
||||
export async function selectCurrentPlayer(id: number) {
|
||||
await setSetting("currentPlayer", id.toString());
|
||||
currentPlayer.set(await db.players.get(id));
|
||||
}
|
||||
|
||||
export async function loadCurrentPlayer() {
|
||||
const id = await getSetting("currentPlayer");
|
||||
if (id) {
|
||||
currentPlayer.set(await db.players.get(Number(id.value)));
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentPlayer(): Promise<Player | undefined> {
|
||||
const id = await getSetting("currentPlayer");
|
||||
if (!id) return;
|
||||
return db.players.get(Number(id.value));
|
||||
}
|
26
src/services/screens.ts
Normal file
26
src/services/screens.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { getSetting, setSetting } from "./settings";
|
||||
import { atom } from "nanostores";
|
||||
|
||||
export const currentScreen = atom<string | undefined>("start-game");
|
||||
|
||||
async function getCurrentScreen() {
|
||||
return (await getSetting("currentScreen"))?.value ?? "start-menu";
|
||||
}
|
||||
|
||||
async function goTo(screen: string) {
|
||||
await setSetting("currentScreen", screen);
|
||||
const screens = document.querySelectorAll(".screen");
|
||||
screens.forEach((s) => {
|
||||
s.classList.add("hidden");
|
||||
});
|
||||
const currentScreen = document.querySelector(`#${screen}`);
|
||||
if (currentScreen) {
|
||||
currentScreen.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCurrentScreen() {
|
||||
await goTo(await getCurrentScreen());
|
||||
}
|
||||
|
||||
export default { getCurrentScreen, goTo, loadCurrentScreen };
|
17
src/services/settings.ts
Normal file
17
src/services/settings.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { db, type Setting } from "./db";
|
||||
|
||||
export async function getSetting(key: string): Promise<Setting | undefined> {
|
||||
return await db.settings.where("key").equals(key).first();
|
||||
}
|
||||
|
||||
export async function setSetting(key: string, value: string): Promise<void> {
|
||||
if (!(await getSetting(key))) {
|
||||
await addSetting(key, value);
|
||||
} else {
|
||||
await db.settings.where("key").equals(key).modify({ value });
|
||||
}
|
||||
}
|
||||
|
||||
export async function addSetting(key: string, value: string): Promise<void> {
|
||||
await db.settings.add({ key, value });
|
||||
}
|
11
tailwind.config.mjs
Normal file
11
tailwind.config.mjs
Normal file
@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require("daisyui")],
|
||||
daisyui: {
|
||||
themes: ["black"],
|
||||
},
|
||||
};
|
3
tsconfig.json
Normal file
3
tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user