This commit is contained in:
Yeray 2024-07-14 19:24:52 +02:00
commit cb95512840
21 changed files with 7928 additions and 0 deletions

24
.gitignore vendored Normal file
View 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
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View 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
View File

@ -0,0 +1,47 @@
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](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
View 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

File diff suppressed because it is too large Load Diff

25
package.json Normal file
View 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
View 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
View 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>

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

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

@ -0,0 +1 @@
/// <reference types="astro/client" />

25
src/pages/index.astro Normal file
View 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
View 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
View 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
View File

29
src/services/players.ts Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/strict"
}