+
- 💵
- 🪵
- 🪨
- 🍟
-
- 🧑🦱
+ 💰
+ 🪵
+ 🪨
+ 🍟
+ 🧑🦱
-
-
- Total
+
+ ∑
{Math.floor(ressources.gold)} ({valueSmoothener(buildingOutputs.gold)})
@@ -49,21 +53,27 @@ export const RessourceMenu = () => {
{Math.floor(ressources.food)} ({valueSmoothener(buildingOutputs.food)})
-
- {Math.floor(ressources.villager)}
-
-
- Remaining
+ {Math.floor(ressources.villager)}
- {remainingRessources(valueSmoothener(buildingOutputs.gold), Math.floor(ressources.gold))}
- {remainingRessources(valueSmoothener(buildingOutputs.wood), Math.floor(ressources.wood))}
- {remainingRessources(valueSmoothener(buildingOutputs.stone), Math.floor(ressources.stone))}
- {remainingRessources(valueSmoothener(buildingOutputs.food), Math.floor(ressources.food))}
-
-
+ <>
+ {selected && (
+
+
+ ⏳
+
+ {remainingRessources(valueSmoothener(buildingOutputs.gold), Math.floor(ressources.gold))}
+ {remainingRessources(valueSmoothener(buildingOutputs.wood), Math.floor(ressources.wood))}
+ {remainingRessources(valueSmoothener(buildingOutputs.stone), Math.floor(ressources.stone))}
+ {remainingRessources(valueSmoothener(buildingOutputs.food), Math.floor(ressources.food))}
+
+
+ )}
+ >
+
+ {selected && (
@@ -71,7 +81,7 @@ export const RessourceMenu = () => {
-
+ )}
);
};
diff --git a/src/components/Tile/Tile.tsx b/src/components/Tile/Tile.tsx
index 41f2e03..828c9a3 100644
--- a/src/components/Tile/Tile.tsx
+++ b/src/components/Tile/Tile.tsx
@@ -107,7 +107,7 @@ const TileComponent = () => {
color={selected ? noColor : isUnlocked ? lightGray : grayScale}
>
{hasBuilding &&
}
-
+
{/* {ref?.current?.position && !hasBuilding && (
)} */}
diff --git a/src/components/TilePropertiesMenu/TilePropertiesMenu.scss b/src/components/TilePropertiesMenu/TilePropertiesMenu.scss
new file mode 100644
index 0000000..9f0862f
--- /dev/null
+++ b/src/components/TilePropertiesMenu/TilePropertiesMenu.scss
@@ -0,0 +1,285 @@
+:root {
+ --pulse-color-start: rgba(33, 73, 77, 0.702);
+ --pulse-color-middle: rgba(33, 100, 77, 0.702);
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ --pulse-color-start: rgba(200, 200, 200, 0.702);
+ --pulse-color-middle: rgba(117, 191, 121, 0.702);
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --pulse-color-start: rgba(33, 73, 77, 0.702);
+ --pulse-color-middle: rgba(33, 100, 77, 0.702);
+ }
+}
+
+@keyframes pulse {
+ 0% {
+ background-color: var(--pulse-color-start);
+ }
+ 50% {
+ background-color: var(--pulse-color-middle);
+ }
+ 100% {
+ background-color: var(--pulse-color-start);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ filter: blur(4px);
+ }
+ to {
+ opacity: 1;
+ filter: blur(0);
+ }
+}
+
+@keyframes fadeOut {
+ from {
+ opacity: 1;
+ filter: blur(0);
+ }
+ to {
+ opacity: 0;
+ filter: blur(4px);
+ }
+}
+
+@keyframes slideIn {
+ from {
+ transform: translate(-50%, -100%);
+ }
+ to {
+ transform: translate(-50%, 0);
+ }
+}
+
+@keyframes shake {
+ 0% {
+ transform: translateX(0);
+ }
+ 25% {
+ transform: translateX(-5px);
+ }
+ 50% {
+ transform: translateX(5px);
+ }
+ 75% {
+ transform: translateX(-5px);
+ }
+ 100% {
+ transform: translateX(0);
+ }
+}
+
+@keyframes successAnimation {
+ 0% {
+ background-color: rgba(33, 73, 77, 0.702);
+ }
+ 50% {
+ background-color: rgba(33, 150, 77, 0.702);
+ }
+ 100% {
+ background-color: rgba(33, 73, 77, 0.702);
+ }
+}
+
+.fade-in {
+ animation: fadeIn 0.5s cubic-bezier(0.25, 0.1, 0.25, 1) both;
+ will-change: opacity, transform, filter;
+}
+
+.fade-out {
+ animation: fadeOut 0.5s cubic-bezier(0.25, 0.1, 0.25, 1) both;
+ will-change: opacity, transform, filter;
+}
+
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+}
+
+.TilePropertiesMenu__button {
+ position: fixed;
+ top: 41cqi;
+ left: 5cqi;
+ backdrop-filter: saturate(180%) blur(30px);
+ -webkit-backdrop-filter: saturate(180%) blur(30px);
+ padding: 10px 10px;
+ border-radius: 25px;
+ border-width: 0px;
+ font-size: 16px;
+ z-index: 1000;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+
+
+
+ @media (prefers-color-scheme: light) {
+ background-color: #d3c57a66;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ background-color: rgba(33, 73, 77, 0.702);
+ }
+
+
+ &:disabled {
+ animation: fadeOut 0.40s forwards ease-in-out;
+ transition: background 0.2s ease-in-out;
+
+ &:active {
+ animation:
+ shake 0.5s ease;
+ }
+ }
+
+ &:not(:disabled):not(.selected) {
+ animation: pulse 4s ease-in-out infinite;
+ }
+}
+
+.TilePropertiesMenu__modal {
+ margin: 0 auto;
+ position: fixed;
+
+ top: 2cqb;
+ left: 50cqi;
+ position: fixed;
+ transform: translate(-50%, 0);
+ width: 339px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ height: auto;
+ min-height: 132px;
+ max-height: 90vh;
+ overflow: hidden;
+ border: 0;
+ background-color: rgb(33, 73, 77);
+ backdrop-filter: saturate(180%) blur(30px);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 20px;
+ z-index: 1001;
+ opacity: 0;
+ animation: fadeIn 0.25s forwards ease-in-out;
+
+ h3 {
+ color: beige;
+ margin-bottom: 3px;
+ font-size: 13px;
+ }
+
+ ul {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ max-height: 50px;
+ overflow-y: scroll;
+ color: beige;
+ flex-grow: 1;
+ }
+
+ li {
+ padding: 5px 0;
+ display: flex;
+ font-size: 11px;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ input {
+ -webkit-autocomplete: off;
+ -webkit-autocorrect: off;
+ -webkit-spellcheck: false;
+ outline: none;
+ width: calc(100% - 80px);
+ padding: 4px;
+ margin-bottom: 10px;
+ border-radius: 15px;
+ border: none;
+ background: rgba(211, 197, 122, 0.4);
+ background-color: rgba(33, 73, 77, 0.702);
+ backdrop-filter: saturate(180%) blur(30px);
+ -webkit-backdrop-filter: saturate(180%) blur(30px);
+ color: whitesmoke;
+ font-size: 12px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ transition:
+ background-color 0.3s ease,
+ filter 0.3s ease;
+
+ &::placeholder {
+ color: #ccc;
+ }
+ }
+
+ button {
+ background: rgba(211, 197, 122, 0.4);
+ background-color: rgba(33, 73, 77, 0.702);
+ backdrop-filter: saturate(180%) blur(30px);
+ -webkit-backdrop-filter: saturate(180%) blur(30px);
+ padding: 5px 5px;
+ border-radius: 25px;
+ border-width: 0;
+ font-size: 10px;
+ z-index: 1000;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+ }
+}
+
+.TilePropertiesMenu__modal-confirm {
+ position: fixed;
+ top: 1cqb;
+ right: 2cqb;
+ width: 50px;
+ border-radius: 15px;
+ border: none;
+ padding: 10px 15px;
+ color: rgb(196, 31, 31);
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+ background-color: rgba(0, 255, 26, 0.4);
+ filter: grayscale(0%);
+
+ &:disabled {
+ box-shadow: 0 0px 0px rgba(0, 0, 0, 0.1);
+ background-color: rgb(140, 36, 36);
+ cursor: not-allowed;
+ filter: grayscale(50%);
+ transition:
+ background-color 0.3s ease-in-out,
+ filter 0.3s ease-in-out;
+
+ &:active {
+ animation: shake 0.5s ease;
+ }
+ }
+}
+
+@media (orientation: landscape) {
+ .TilePropertiesMenu__modal {
+ position: fixed;
+ top: 1cqi;
+ left: 50;
+ max-height: 250px;
+ width: 300px;
+ background-color: rgba(33, 73, 77, 0.9);
+ padding: 10px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 20px;
+ overflow-y: auto;
+ z-index: 1001;
+ }
+
+ .TilePropertiesMenu__button {
+ top: 7cqi;
+ left: 5cqi;
+ }
+}
diff --git a/src/components/TilePropertiesMenu/TilePropertiesMenu.tsx b/src/components/TilePropertiesMenu/TilePropertiesMenu.tsx
new file mode 100644
index 0000000..430a49b
--- /dev/null
+++ b/src/components/TilePropertiesMenu/TilePropertiesMenu.tsx
@@ -0,0 +1,85 @@
+import { useStore } from '../../store/store';
+import { useState } from 'react';
+import './TilePropertiesMenu.scss';
+import { createPortal } from 'react-dom';
+import { useClickOutside } from '../../hooks';
+import { Vector3 } from 'three';
+
+export const TilePropertiesMenu = () => {
+ const [modalOpen, setModalOpen] = useState(false);
+ const [locationTitle, setLocationTitle] = useState('');
+
+ const selected = useStore((state) => state.selected);
+ const modalRef = useClickOutside(() => setModalOpen(false));
+ const setCamera = useStore((state) => state.setCamera);
+
+ const locations = useStore((state) => state.locations);
+
+ const saveLocation = useStore((state) => state.saveLocation);
+
+ const handleAdd = (t: string, x: number, y: number) => {
+ saveLocation(t, x, y);
+ selected!.object.userData.update();
+ setModalOpen(false);
+ setLocationTitle('');
+ };
+
+ return (
+ <>
+
{
+ setModalOpen(true);
+ }}
+ >
+ 📍
+
+
+ {modalOpen &&
+ createPortal(
+
+
setLocationTitle(e.target.value)}
+ placeholder="Enter location title"
+ className="TilePropertiesMenu__input"
+ />
+
{
+ const { x, y } = selected!.object.position;
+ handleAdd(locationTitle, x, y);
+ }}
+ >
+ 💾
+
+
+
Saved Locations
+
+
+ {Object.entries(locations).map(([title, { positionX, positionY }]) => (
+
+ {title}: ({positionX.toFixed(2)}, {positionY.toFixed(2)})
+ {
+ const newPosition = new Vector3(positionX, positionY, 10);
+ setCamera(newPosition as Vector3, new Vector3(positionX, positionY, 0) as Vector3);
+ }}
+ className="TilePropertiesMenu__locations-button"
+ >
+ ▶
+
+
+ ))}
+
+
,
+ document.body,
+ )}
+ >
+ );
+};
+
+export default TilePropertiesMenu;
diff --git a/src/components/building/buildingFinancials.ts b/src/components/building/buildingFinancials.ts
index 0de7395..b76be00 100644
--- a/src/components/building/buildingFinancials.ts
+++ b/src/components/building/buildingFinancials.ts
@@ -9,27 +9,30 @@ type BuildingRessourcesType = {
};
export const SELLFACTOR = 0.5;
+
+const extractAmount = (cost: { amount: number; emoji: string } | undefined) => cost ? Math.ceil(cost.amount * SELLFACTOR) : 0;
+
export const BuildingSellBenefits: BuildingRessourcesType = {
[BuildingType.Outpost]: {
- wood: Math.ceil(getCostsOfBuilding(BuildingType.Outpost).wood! * SELLFACTOR),
- gold: Math.ceil(getCostsOfBuilding(BuildingType.Outpost).gold! * SELLFACTOR),
+ wood: extractAmount(getCostsOfBuilding(BuildingType.Outpost).wood),
+ gold: extractAmount(getCostsOfBuilding(BuildingType.Outpost).gold),
},
[BuildingType.Lumberhut]: {
- wood: Math.ceil(getCostsOfBuilding(BuildingType.Lumberhut).wood! * SELLFACTOR),
- gold: Math.ceil(getCostsOfBuilding(BuildingType.Lumberhut).gold! * SELLFACTOR),
- villager: getCostsOfBuilding(BuildingType.Lumberhut).villager!,
+ wood: extractAmount(getCostsOfBuilding(BuildingType.Lumberhut).wood),
+ gold: extractAmount(getCostsOfBuilding(BuildingType.Lumberhut).gold),
+ villager: getCostsOfBuilding(BuildingType.Lumberhut).villager?.amount!,
},
[BuildingType.House]: {
- wood: Math.ceil(getCostsOfBuilding(BuildingType.House).wood! * SELLFACTOR),
- gold: Math.ceil(getCostsOfBuilding(BuildingType.House).gold! * SELLFACTOR),
- villager: getCostsOfBuilding(BuildingType.House).villager!,
+ wood: extractAmount(getCostsOfBuilding(BuildingType.House).wood),
+ gold: extractAmount(getCostsOfBuilding(BuildingType.House).gold),
+ villager: getCostsOfBuilding(BuildingType.House).villager?.amount!,
},
[BuildingType.CornField]: {
- gold: Math.ceil(getCostsOfBuilding(BuildingType.House).gold! * SELLFACTOR),
+ gold: extractAmount(getCostsOfBuilding(BuildingType.CornField).gold),
},
[BuildingType.StoneQuarry]: {
- wood: Math.ceil(getCostsOfBuilding(BuildingType.House).wood! * SELLFACTOR),
- gold: Math.ceil(getCostsOfBuilding(BuildingType.House).gold! * SELLFACTOR),
- villager: getCostsOfBuilding(BuildingType.House).villager!,
+ wood: extractAmount(getCostsOfBuilding(BuildingType.StoneQuarry).wood),
+ gold: extractAmount(getCostsOfBuilding(BuildingType.StoneQuarry).gold),
+ villager: getCostsOfBuilding(BuildingType.StoneQuarry).villager?.amount!,
},
-};
+};
\ No newline at end of file
diff --git a/src/components/building/buildings.ts b/src/components/building/buildings.ts
index 45a83c9..7a5cfb3 100644
--- a/src/components/building/buildings.ts
+++ b/src/components/building/buildings.ts
@@ -2,6 +2,14 @@ import { Terrain } from '../../helpers/terrain';
import { Ressources } from '../../store/store';
import { BuildingType } from './Building';
+const EMOJIS = {
+ wood: '🪵',
+ gold: '💰',
+ villager: '🧑🦱',
+ food: '🍟',
+ stone: '🪨',
+};
+
export type Building = {
type: BuildingType;
name: string;
@@ -9,11 +17,17 @@ export type Building = {
description: string;
costs: {
// how much of each ressource is consumed when placing the building
- [key in Ressources]?: number;
+ [key in Ressources]?: {
+ amount: number;
+ emoji: string;
+ };
};
output: {
// use negative values for costs (i.e. output 'gold: -10 / 60' means the building costs 10 gold per minute)
- [key in Ressources]?: number;
+ [key in Ressources]?: {
+ amount: number;
+ emoji: string;
+ };
};
terrains: Terrain[]; // the types of terrain the building can be built on
};
@@ -25,13 +39,13 @@ export const allBuildings: Building[] = [
type: BuildingType.Outpost,
name: 'Outpost',
icon: '🗼',
- description: 'Unlocks new areas for your town to grow.',
+ description: 'The Outpost serves as a beacon of expansion, allowing your town to extend its borders and explore new territories. It stands tall, symbolizing the ambition and growth of your community. Establishing an Outpost is the first step towards a thriving and expansive settlement.',
costs: {
- wood: 10,
- gold: 600,
+ wood: { amount: 10, emoji: EMOJIS.wood },
+ gold: { amount: 600, emoji: EMOJIS.gold },
},
output: {
- gold: -20 / 60,
+ gold: { amount: -20 / 60, emoji: EMOJIS.gold },
},
terrains: [Terrain.BEACH, Terrain.MEADOW, Terrain.FOREST],
},
@@ -39,15 +53,15 @@ export const allBuildings: Building[] = [
type: BuildingType.House,
name: 'House',
icon: '🛖',
- description: 'Adds 4 villagers to your town. Villagers pay taxes but require food.',
+ description: 'A cozy House that provides shelter for your villagers, adding 4 new members to your growing town. These villagers contribute to the economy by paying taxes, but they also require sustenance. The House is a cornerstone of any thriving community, ensuring the well-being and growth of its inhabitants.',
costs: {
- wood: 6,
- gold: 100,
- villager: -4,
+ wood: { amount: 6, emoji: EMOJIS.wood },
+ gold: { amount: 100, emoji: EMOJIS.gold },
+ villager: { amount: -4, emoji: EMOJIS.villager },
},
output: {
- gold: 12 / 60,
- food: -1 / 60,
+ gold: { amount: 12 / 60, emoji: EMOJIS.gold },
+ food: { amount: -1 / 60, emoji: EMOJIS.food },
},
terrains: [Terrain.BEACH, Terrain.MEADOW, Terrain.FOREST],
},
@@ -55,15 +69,15 @@ export const allBuildings: Building[] = [
type: BuildingType.Lumberhut,
name: 'Lumberhut',
icon: '🪚',
- description: 'A villager works here to produce wood needed to build new buildings, but costs money to maintain.',
+ description: 'The Lumberhut is a vital structure where a villager works tirelessly to produce the wood necessary for constructing new buildings. Though it requires gold to maintain, the steady supply of wood it provides is indispensable for the expansion and development of your town.',
costs: {
- wood: 2,
- gold: 10,
- villager: 1,
+ wood: { amount: 2, emoji: EMOJIS.wood },
+ gold: { amount: 10, emoji: EMOJIS.gold },
+ villager: { amount: 1, emoji: EMOJIS.villager },
},
output: {
- wood: 2 / 60,
- gold: -6 / 60,
+ wood: { amount: 2 / 60, emoji: EMOJIS.wood },
+ gold: { amount: -6 / 60, emoji: EMOJIS.gold },
},
terrains: [Terrain.FOREST],
},
@@ -71,13 +85,13 @@ export const allBuildings: Building[] = [
type: BuildingType.CornField,
name: 'Cornfield',
icon: '🌽',
- description: 'Produces low amounts of food. Costs money to maintain.',
+ description: 'The Cornfield is a humble yet essential source of food for your villagers. While it incurs maintenance costs, the Cornfield ensures a steady supply of nourishment, supporting the health and productivity of your community. It is a testament to the agricultural prowess of your town.',
costs: {
- gold: 30,
+ gold: { amount: 30, emoji: EMOJIS.gold },
},
output: {
- gold: -8 / 60,
- food: 4 / 60,
+ gold: { amount: -8 / 60, emoji: EMOJIS.gold },
+ food: { amount: 4 / 60, emoji: EMOJIS.food },
},
terrains: [Terrain.MEADOW],
},
@@ -85,16 +99,15 @@ export const allBuildings: Building[] = [
type: BuildingType.StoneQuarry,
name: 'Quarry',
icon: '⛏️',
- description:
- 'Villagers work here to produces stone needed for more advanced buildings, but it costs money to maintain.',
+ description: 'The Quarry is a bustling hub where villagers extract stone, a crucial resource for constructing advanced buildings. Despite the costs associated with its operation, the Quarry plays a key role in the architectural and infrastructural advancements of your settlement, paving the way for future growth.',
costs: {
- wood: 6,
- gold: 40,
- villager: 4,
+ wood: { amount: 6, emoji: EMOJIS.wood },
+ gold: { amount: 40, emoji: EMOJIS.gold },
+ villager: { amount: 4, emoji: EMOJIS.villager },
},
output: {
- gold: -12 / 60,
- stone: 2 / 60,
+ gold: { amount: -12 / 60, emoji: EMOJIS.gold },
+ stone: { amount: 2 / 60, emoji: EMOJIS.stone },
},
terrains: [Terrain.MOUNTAIN],
},
@@ -109,4 +122,4 @@ export const getCostsOfBuilding = (buildingType: BuildingType) =>
allBuildings.find((building) => building.type === buildingType)!.costs;
export const getOutputOfBuilding = (buildingType: BuildingType) =>
- allBuildings.find((building) => building.type === buildingType)!.output;
+ allBuildings.find((building) => building.type === buildingType)!.output;
\ No newline at end of file
diff --git a/src/materials/materials.tsx b/src/materials/materials.tsx
index a0278b2..4f13154 100644
--- a/src/materials/materials.tsx
+++ b/src/materials/materials.tsx
@@ -1,9 +1,9 @@
import { useTexture } from '@react-three/drei';
-import { BoxGeometry, Color, MeshStandardMaterial, NearestFilter } from 'three';
+import { BoxGeometry, Color, LinearFilter, MeshPhysicalMaterial,MeshStandardMaterial } from 'three';
import textureAtlas from '../textures/textureAtlas.jpg';
-export const water = new MeshStandardMaterial({
- color: 'blue',
+export const water = new MeshPhysicalMaterial({
+ color: 'DeepBlue',
emissiveIntensity: 0.2,
});
@@ -11,22 +11,28 @@ export const building = new BoxGeometry(0.5, 0.5, 0.5);
export const Materials = () => {
const [texture_atlas] = useTexture([textureAtlas]);
- texture_atlas.minFilter = NearestFilter;
- texture_atlas.magFilter = NearestFilter;
+ texture_atlas.minFilter = LinearFilter;
+ texture_atlas.magFilter = LinearFilter;
- const water = new MeshStandardMaterial({
+ const water = new MeshPhysicalMaterial({
color: new Color(0x0055ff),
emissive: new Color(0x0055ff),
emissiveIntensity: 0.4,
transparent: true,
- opacity: 0.9,
- metalness: 0.7,
- roughness: 0.5,
- wireframe: false,
+ opacity: 0.8,
+ metalness: 0.1,
+ roughness: 0.1,
+ clearcoat: 1.0,
+ clearcoatRoughness: 0.1,
+ reflectivity: 0.9,
});
+
+
const atlas = new MeshStandardMaterial({
map: texture_atlas,
+ roughness: 0.5,
+ metalness: 0.1,
});
return {
diff --git a/src/store/store.ts b/src/store/store.ts
index 9a6ea96..56515a3 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -24,6 +24,7 @@ export type State = {
};
unlocked: { [key: number]: { [key: number]: number } };
selected: SelectedObject | undefined;
+ locations: { [key: string]: { positionX: number; positionY: number } };
camera:
| {
position: Vector3;
@@ -40,6 +41,8 @@ export type Actions = {
lock: (x: number, y: number, range: number) => void;
unlock: (x: number, y: number, range: number) => void;
setSelected: (object: SelectedObject | undefined) => void;
+ saveLocation: (title: string, x: number, y: number) => void;
+ removeLocation: (title: string) => void;
setCamera: (position: Vector3, lookAt: Vector3) => void;
reset: () => void;
};
@@ -61,6 +64,7 @@ const initialState: State = {
villager: 2,
},
unlocked: {},
+ locations: {},
selected: undefined,
camera: undefined,
};
@@ -72,16 +76,21 @@ export const useStore = create
()(
...initialState,
addBuilding: (x, y, building) =>
set((state) => {
- state.removeRessources(getCostsOfBuilding(building));
+ const costs = getCostsOfBuilding(building);
+ const ressourcesToRemove = Object.fromEntries(
+ Object.entries(costs).map(([key, value]) => [key, value.amount])
+ );
+ state.removeRessources(ressourcesToRemove);
if (building === BuildingType.Outpost) {
state.unlock(x, y, 8);
}
// add building output
- const newBuildingOutputs = state.buildingOutputs;
- Object.entries(getOutputOfBuilding(building)).forEach((ressource) => {
- newBuildingOutputs[ressource[0] as Ressources] += ressource[1];
+ const newBuildingOutputs =state.buildingOutputs;
+ const output = getOutputOfBuilding(building);
+ Object.entries(output).forEach(([key, value]) => {
+ newBuildingOutputs[key as Ressources] += value.amount;
});
// add new building
@@ -94,30 +103,53 @@ export const useStore = create()(
buildingOutputs: newBuildingOutputs,
};
}),
+ // add new saved location
+ saveLocation: (t, x, y) =>
+ set((state) => {
+ const newLocationType = state.locations;
+ newLocationType[t] = { positionX: x, positionY: y };
+ return {
+ locations: newLocationType,
+ };
+ }),
+ // remove saved location
+ removeLocation: (t) =>
+ set((state) => {
+ const newLocationType = state.locations;
+ delete newLocationType[t];
+ return {
+ locations: newLocationType,
+ };
+ }),
removeBuilding: (x, y) =>
set((state) => {
const building = state.buildings[x][y];
- state.addRessources(BuildingSellBenefits[state.buildings[x][y]]);
+ const sellBenefits = BuildingSellBenefits[building];
+ const ressourcesToAdd = Object.fromEntries(
+ Object.entries(sellBenefits).map(([key, value]) => [key, value])
+ );
+ state.addRessources(ressourcesToAdd);
if (building === BuildingType.Outpost) {
state.lock(x, y, 8);
}
- // TODO: remove output froom state.buildingOutputs
+ // remove building output
const newBuildingOutputs = state.buildingOutputs;
- Object.entries(getOutputOfBuilding(building)).forEach((ressource) => {
- newBuildingOutputs[ressource[0] as Ressources] -= ressource[1];
+ const output = getOutputOfBuilding(building);
+ Object.entries(output).forEach(([key, value]) => {
+ newBuildingOutputs[key as Ressources] -= value.amount;
});
- const newBuildingType = state.buildings;
+ const newBuildingType = { ...state.buildings };
delete newBuildingType[x][y];
- return { buildings: newBuildingType };
+ return { buildings: newBuildingType, buildingOutputs: newBuildingOutputs };
}),
addRessources: (ressourcesToAdd: Partial<{ [key in Ressources]: number }>) =>
set((state: State) => {
const newRessources = state.ressources;
- Object.entries(ressourcesToAdd).forEach((ressource) => {
- newRessources[ressource[0] as Ressources] += ressource[1];
+ Object.entries(ressourcesToAdd).forEach(([key, value]) => {
+ newRessources[key as Ressources] += value!;
});
return {
ressources: newRessources,
@@ -125,9 +157,9 @@ export const useStore = create()(
}),
removeRessources: (ressourcesToRemove: Partial<{ [key in Ressources]: number }>) =>
set((state: State) => {
- const newRessources = state.ressources;
- Object.entries(ressourcesToRemove).forEach((ressource) => {
- newRessources[ressource[0] as Ressources] -= ressource[1];
+ const newRessources = state.ressources ;
+ Object.entries(ressourcesToRemove).forEach(([key, value]) => {
+ newRessources[key as Ressources] -= value!;
});
return {
ressources: newRessources,
@@ -177,11 +209,11 @@ export const useStore = create()(
set(() => {
return { camera: { position, lookAt } };
}),
- reset: () => set({ ...initialState }),
+ reset: () => set(initialState),
}),
{
name: 'game-state',
},
),
),
-);
+);
\ No newline at end of file