Add basic building support
This commit is contained in:
		
							parent
							
								
									2fdb719df8
								
							
						
					
					
						commit
						6c64e770b4
					
				| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
[
 | 
			
		||||
    {
 | 
			
		||||
        "category": "mines",
 | 
			
		||||
        "buildings": [
 | 
			
		||||
            {
 | 
			
		||||
                "id": "iron-mine",
 | 
			
		||||
                "cost": {
 | 
			
		||||
                    "iron": 1,
 | 
			
		||||
                    "gold": 1,
 | 
			
		||||
                    "coal": 1
 | 
			
		||||
                },
 | 
			
		||||
                "energy": 11,
 | 
			
		||||
                "multiplier": 2
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "id": "gold-mine",
 | 
			
		||||
                "cost": {
 | 
			
		||||
                    "iron": 2,
 | 
			
		||||
                    "gold": 2,
 | 
			
		||||
                    "coal": 2
 | 
			
		||||
                },
 | 
			
		||||
                "energy": 11,
 | 
			
		||||
                "multiplier": 2.5
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }, {
 | 
			
		||||
        "category": "utilities",
 | 
			
		||||
        "buildings": [
 | 
			
		||||
            {
 | 
			
		||||
                "id": "research-lab",
 | 
			
		||||
                "cost": {
 | 
			
		||||
                    "iron": 10,
 | 
			
		||||
                    "gold": 11,
 | 
			
		||||
                    "coal": 12
 | 
			
		||||
                },
 | 
			
		||||
                "energy": 100,
 | 
			
		||||
                "multiplier": 3
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
import { Users } from '../db/mongodb';
 | 
			
		||||
import type User from '../../types/User';
 | 
			
		||||
import type Resources from '../../types/Resources';
 | 
			
		||||
import type Building from '../../types/Building';
 | 
			
		||||
 | 
			
		||||
export const getAllUsers = async () => {
 | 
			
		||||
    const users = await Users();
 | 
			
		||||
| 
						 | 
				
			
			@ -42,4 +43,34 @@ export const updateUserResources = async (username: string, resources: any) => {
 | 
			
		|||
            resources
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getUserBuildings = async (username: string): Promise<Array<Building>> => {
 | 
			
		||||
    const users = await Users();
 | 
			
		||||
    const user = await users.findOne({ username });
 | 
			
		||||
 | 
			
		||||
    const buildings: Array<Building> = [];
 | 
			
		||||
 | 
			
		||||
    if (user?.buildings !== undefined) {
 | 
			
		||||
        user?.buildings.forEach((building: Building) => {
 | 
			
		||||
            buildings.push(building);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return buildings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const createOrUpgradeBuilding = async (username: string, building: Building) => {
 | 
			
		||||
    const users = await Users();
 | 
			
		||||
 | 
			
		||||
    const result = await users.updateOne(
 | 
			
		||||
        { username, "buildings.name": building.id },
 | 
			
		||||
        { $set: { "buildings.$.level": building.level } }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (result.modifiedCount === 0) {
 | 
			
		||||
        await users.updateOne({ username },
 | 
			
		||||
            { $push: { buildings: building } }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
{
 | 
			
		||||
    "Label": {
 | 
			
		||||
        "mines": "Mines",
 | 
			
		||||
        "iron-mine": "Iron mine",
 | 
			
		||||
        "gold-mine": "Gold mine",
 | 
			
		||||
 | 
			
		||||
        "utilities": "Utilities",
 | 
			
		||||
        "research-lab": "Research lab"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
import type Resources from "../../types/Resources";
 | 
			
		||||
 | 
			
		||||
export default function calculateAvailableResources(userResources: Resources, buildingCost: Resources) {
 | 
			
		||||
    let canBuild = true;
 | 
			
		||||
    const resources = {} as Resources;
 | 
			
		||||
    
 | 
			
		||||
    let resource: keyof Resources;
 | 
			
		||||
    for (resource in buildingCost) {
 | 
			
		||||
        resources[resource] = userResources[resource] - buildingCost[resource];
 | 
			
		||||
        if (userResources[resource] < buildingCost[resource]) {
 | 
			
		||||
            canBuild = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        canBuild,
 | 
			
		||||
        resources
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
import { type APIRoute } from "astro";
 | 
			
		||||
import validateAccessToken from "../../../lib/utils/validateAccessToken";
 | 
			
		||||
import { getAccessToken } from "../../../lib/db/accessTokens";
 | 
			
		||||
import { getUserResources } from "../../../lib/db/users";
 | 
			
		||||
import buildings from '../../../lib/data/buildings.json';
 | 
			
		||||
import calculateAvailableResources from "../../../lib/utils/calculateAvailableResources";
 | 
			
		||||
 | 
			
		||||
export const POST: APIRoute = async({ request }) => {
 | 
			
		||||
    const response = await validateAccessToken(request);
 | 
			
		||||
    if(response instanceof Response) return response;
 | 
			
		||||
 | 
			
		||||
    const user = (await getAccessToken(response))?.username ?? "";
 | 
			
		||||
    const resources = await getUserResources(user);
 | 
			
		||||
    const buildingId = (await request.json()).building;
 | 
			
		||||
    const building = buildings.map(cat => cat.buildings.filter(b => b.id === buildingId))[0][0];
 | 
			
		||||
    const balance = calculateAvailableResources(resources, building.cost);
 | 
			
		||||
    
 | 
			
		||||
    return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 200,
 | 
			
		||||
            message: "OK",
 | 
			
		||||
            accessToken: response,
 | 
			
		||||
            data: {
 | 
			
		||||
                resources,
 | 
			
		||||
                building,
 | 
			
		||||
                balance
 | 
			
		||||
            }
 | 
			
		||||
        }), { status: 200 }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
import type { APIRoute } from "astro";
 | 
			
		||||
import { getAccessToken } from "../../../lib/db/accessTokens";
 | 
			
		||||
import validateAccessToken from "../../../lib/utils/validateAccessToken";
 | 
			
		||||
 | 
			
		||||
export const GET: APIRoute = async({ request }) => {
 | 
			
		||||
    const response = await validateAccessToken(request);
 | 
			
		||||
    if(response instanceof Response) return response;
 | 
			
		||||
 | 
			
		||||
    const buildings = [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Iron Mine",
 | 
			
		||||
            "level": 1,
 | 
			
		||||
            "production": 100
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Coal Mine",
 | 
			
		||||
            "level": 2,
 | 
			
		||||
            "production": 150
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 200,
 | 
			
		||||
            message: "OK",
 | 
			
		||||
            data: {
 | 
			
		||||
                buildings
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
---
 | 
			
		||||
import Layout from '../../layouts/Layout.astro';
 | 
			
		||||
import NavBar from '../../components/NavBar.astro';
 | 
			
		||||
import { getUserResources } from '../../lib/db/users';
 | 
			
		||||
import { getHighestWeightedLanguage, getLocales } from '../../lib/lang/langDriver';
 | 
			
		||||
import ResourceBar from '../../components/ResourceBar.astro';
 | 
			
		||||
 | 
			
		||||
const buildingsList = (await import('../../lib/data/buildings.json')).default;
 | 
			
		||||
 | 
			
		||||
const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
 | 
			
		||||
const username = Astro.cookies.get('username')?.value ?? "";
 | 
			
		||||
 | 
			
		||||
if(loggedToken === null || username === "") return Astro.redirect('/');
 | 
			
		||||
 | 
			
		||||
const resources = await getUserResources(username);
 | 
			
		||||
 | 
			
		||||
const locale = getHighestWeightedLanguage(Astro.request.headers.get('accept-language'));
 | 
			
		||||
 | 
			
		||||
const langResources = await getLocales(locale, 'resources');
 | 
			
		||||
const langGame = await getLocales(locale, 'game');
 | 
			
		||||
const langBuildings = await getLocales(locale, 'buildings');
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<Layout title="Buildings">
 | 
			
		||||
	<NavBar loggedIn="true" active="buildings" />
 | 
			
		||||
	<ResourceBar loggedIn="true" />
 | 
			
		||||
    <ul>
 | 
			
		||||
        <li>{langResources['Label_coal']}: <span id="coal">{resources.coal * 2}</span></li>
 | 
			
		||||
        <li>{langResources['Label_iron']}: <span id="iron">{resources.iron * 3}</span></li>
 | 
			
		||||
        <li>{langResources['Label_gold']}: <span id="gold">{resources.gold * 4}</span></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
 | 
			
		||||
    {buildingsList.map(cat => (
 | 
			
		||||
		<div class="building-card">
 | 
			
		||||
			<h3>{langBuildings[`Label_${cat.category}`]}</h3>
 | 
			
		||||
			{cat.buildings.map(building => (
 | 
			
		||||
				<div>{langBuildings[`Label_${building.id}`]} - {langResources['Label_iron']}: {building.cost['iron']}, {langResources['Label_gold']}: {building.cost['gold']}, {langResources['Label_coal']}: {building.cost['coal']} | <a id={`build_${building.id}`} href="#" class="a-button">{langGame['Link_build']}</a></div>
 | 
			
		||||
			))}
 | 
			
		||||
		</div>
 | 
			
		||||
	))}
 | 
			
		||||
</Layout>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
	* {
 | 
			
		||||
		color: white;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	main {
 | 
			
		||||
		margin: auto;
 | 
			
		||||
		padding: 1rem;
 | 
			
		||||
		width: 800px;
 | 
			
		||||
		max-width: calc(100% - 2rem);
 | 
			
		||||
		color: white;
 | 
			
		||||
		font-size: 20px;
 | 
			
		||||
		line-height: 1.6;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.astro-a {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: -32px;
 | 
			
		||||
		left: 50%;
 | 
			
		||||
		transform: translatex(-50%);
 | 
			
		||||
		width: 220px;
 | 
			
		||||
		height: auto;
 | 
			
		||||
		z-index: -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h3 {
 | 
			
		||||
		font-size: 2rem;
 | 
			
		||||
		font-weight: 700;
 | 
			
		||||
		line-height: 1;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		margin-bottom: 1em;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.text-gradient {
 | 
			
		||||
		background-image: var(--accent-gradient);
 | 
			
		||||
		-webkit-background-clip: text;
 | 
			
		||||
		-webkit-text-fill-color: transparent;
 | 
			
		||||
		background-size: 400%;
 | 
			
		||||
		background-position: 0%;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions {
 | 
			
		||||
		margin-bottom: 2rem;
 | 
			
		||||
		border: 1px solid rgba(var(--accent-light), 25%);
 | 
			
		||||
		background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
 | 
			
		||||
		padding: 1.5rem;
 | 
			
		||||
		border-radius: 8px;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions code {
 | 
			
		||||
		font-size: 0.8em;
 | 
			
		||||
		font-weight: bold;
 | 
			
		||||
		background: rgba(var(--accent-light), 12%);
 | 
			
		||||
		color: rgb(var(--accent-light));
 | 
			
		||||
		border-radius: 4px;
 | 
			
		||||
		padding: 0.3em 0.4em;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions strong {
 | 
			
		||||
		color: rgb(var(--accent-light));
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.link-card-grid {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
 | 
			
		||||
		gap: 2rem;
 | 
			
		||||
		padding: 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.a-button {
 | 
			
		||||
		text-decoration: none;
 | 
			
		||||
    	color: green;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.a-button:hover {
 | 
			
		||||
		color: lime;
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
	setInterval(() => {
 | 
			
		||||
		const coal = document.querySelector('#coal');
 | 
			
		||||
		const iron = document.querySelector('#iron');
 | 
			
		||||
		const gold = document.querySelector('#gold');
 | 
			
		||||
 | 
			
		||||
		if(!coal || !iron || !gold) return;
 | 
			
		||||
 | 
			
		||||
		coal.innerHTML = String(parseInt(coal?.innerHTML ?? "0") + 1);
 | 
			
		||||
		iron.innerHTML = String(parseInt(iron?.innerHTML ?? "0") + 2);
 | 
			
		||||
		gold.innerHTML = String(parseInt(gold?.innerHTML ?? "0") + 3);
 | 
			
		||||
	}, 1_000);
 | 
			
		||||
 | 
			
		||||
	const allButtons = document.getElementsByClassName("a-button");
 | 
			
		||||
 | 
			
		||||
	for(const buildingButton of allButtons) {
 | 
			
		||||
		buildingButton.addEventListener("click", async () => {
 | 
			
		||||
			const id = buildingButton.id.split("_")[1];
 | 
			
		||||
 | 
			
		||||
			await fetch('/api/build/createBuilding', {
 | 
			
		||||
				method: 'POST',
 | 
			
		||||
				headers: {
 | 
			
		||||
					'Content-Type': 'application/json'
 | 
			
		||||
				},
 | 
			
		||||
				body: JSON.stringify({
 | 
			
		||||
					building: id
 | 
			
		||||
				})
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
export default interface Building {
 | 
			
		||||
    id: string;
 | 
			
		||||
    level: number;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import type { ObjectId } from "mongodb";
 | 
			
		||||
import type Resources from "./Resources";
 | 
			
		||||
import type Building from "./Building";
 | 
			
		||||
 | 
			
		||||
export default interface User {
 | 
			
		||||
    _id?: ObjectId;
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +9,7 @@ export default interface User {
 | 
			
		|||
    password: string;
 | 
			
		||||
    lastLogin: Date;
 | 
			
		||||
    resources: Resources;
 | 
			
		||||
    buildings: Array<Building>;
 | 
			
		||||
    createdAt: Date;
 | 
			
		||||
    updatedAt: Date;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue