строительство работает в окне деревне. не работает мув, делит, есть баги, когда дровосек рубит много деревьев сразу
This commit is contained in:
parent
3e35a25601
commit
4b83e8339f
|
|
@ -33,12 +33,14 @@ To run the development server with hot-reloading:
|
|||
```bash
|
||||
npm run dev
|
||||
```
|
||||
Не запускай npm run dev сам. Скажи пользователю, что бы сделал это сам.
|
||||
|
||||
### Building for Production
|
||||
To build the application for production:
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
Не запускай npm run build сам. Скажи пользователю, что бы сделал это сам.
|
||||
|
||||
## 3. Development Conventions
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
<div class="actions-list">
|
||||
<div v-for="(action, index) in selectedTile.availableActions" :key="index" class="action-item">
|
||||
<button
|
||||
:disabled="!action.isEnabled"
|
||||
:disabled="!action.isEnabled || isSubmitting"
|
||||
@click="handleActionClick(action)"
|
||||
>
|
||||
{{ getActionLabel(action) }}
|
||||
|
|
@ -91,9 +91,37 @@ const getActionLabel = (action) => {
|
|||
return action.type;
|
||||
};
|
||||
|
||||
const handleActionClick = (action) => {
|
||||
console.log('Action clicked:', action);
|
||||
// In a future task, this will dispatch the action to the backend.
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
const handleActionClick = async (action) => {
|
||||
if (isSubmitting.value) return;
|
||||
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
const response = await useFetch('/api/village/action', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
tileId: selectedTile.value.id,
|
||||
actionType: action.type,
|
||||
payload: {
|
||||
...(action.type === 'BUILD' && { buildingType: action.buildingType }),
|
||||
...(action.type === 'MOVE' && { toTileId: action.toTileId }), // Assuming action.toTileId will be present for MOVE
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (response.error.value) {
|
||||
alert(response.error.value.data?.statusMessage || 'An unknown error occurred.');
|
||||
} else {
|
||||
villageData.value = response.data.value;
|
||||
selectedTile.value = null;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to perform action:', e);
|
||||
alert('An unexpected error occurred. Please check the console.');
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "dummy" TEXT;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `dummy` on the `User` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"nickname" TEXT,
|
||||
"avatar" TEXT DEFAULT '/avatars/default.png',
|
||||
"coins" INTEGER NOT NULL DEFAULT 0,
|
||||
"exp" INTEGER NOT NULL DEFAULT 0,
|
||||
"soundOn" BOOLEAN NOT NULL DEFAULT true,
|
||||
"confettiOn" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_User" ("avatar", "coins", "confettiOn", "createdAt", "email", "exp", "id", "nickname", "password", "soundOn", "updatedAt") SELECT "avatar", "coins", "confettiOn", "createdAt", "email", "exp", "id", "nickname", "password", "soundOn", "updatedAt" FROM "User";
|
||||
DROP TABLE "User";
|
||||
ALTER TABLE "new_User" RENAME TO "User";
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
|
@ -14,6 +14,9 @@ datasource db {
|
|||
enum VillageObjectType {
|
||||
HOUSE
|
||||
FIELD
|
||||
LUMBERJACK
|
||||
QUARRY
|
||||
WELL
|
||||
}
|
||||
|
||||
enum TerrainType {
|
||||
|
|
|
|||
45
server/api/village/action.post.ts
Normal file
45
server/api/village/action.post.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// server/api/village/action.post.ts
|
||||
import { getUserIdFromSession } from '../../utils/auth';
|
||||
import { buildOnTile, clearTile, moveObject, removeObject } from '../../services/villageService';
|
||||
import { getVillageState } from '../../services/villageService';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const userId = await getUserIdFromSession(event);
|
||||
const body = await readBody(event);
|
||||
|
||||
const { tileId, actionType, payload } = body;
|
||||
|
||||
if (!tileId || !actionType) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing tileId or actionType' });
|
||||
}
|
||||
|
||||
switch (actionType) {
|
||||
case 'BUILD':
|
||||
if (!payload?.buildingType) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing buildingType for BUILD action' });
|
||||
}
|
||||
await buildOnTile(userId, tileId, payload.buildingType);
|
||||
break;
|
||||
|
||||
case 'CLEAR':
|
||||
await clearTile(userId, tileId);
|
||||
break;
|
||||
|
||||
case 'MOVE':
|
||||
if (!payload?.toTileId) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing toTileId for MOVE action' });
|
||||
}
|
||||
await moveObject(userId, tileId, payload.toTileId);
|
||||
break;
|
||||
|
||||
case 'REMOVE':
|
||||
await removeObject(userId, tileId);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid actionType' });
|
||||
}
|
||||
|
||||
// Return the full updated village state
|
||||
return getVillageState(userId);
|
||||
});
|
||||
|
|
@ -296,4 +296,205 @@ export async function getVillageState(userId: number): Promise<FullVillage> {
|
|||
});
|
||||
|
||||
return { ...finalVillageState, tiles: tilesWithActions } as any;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Action Service Functions ---
|
||||
|
||||
|
||||
|
||||
export async function buildOnTile(userId: number, tileId: number, buildingType: string) {
|
||||
|
||||
const { VillageObjectType } = await import('@prisma/client');
|
||||
|
||||
const validBuildingTypes = Object.keys(VillageObjectType);
|
||||
|
||||
if (!validBuildingTypes.includes(buildingType)) {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: `Invalid building type: ${buildingType}` });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
|
||||
// 1. Fetch all necessary data
|
||||
|
||||
const user = await tx.user.findUniqueOrThrow({ where: { id: userId } });
|
||||
|
||||
const tile = await tx.villageTile.findUniqueOrThrow({ where: { id: tileId }, include: { village: true } });
|
||||
|
||||
|
||||
|
||||
// Ownership check
|
||||
|
||||
if (tile.village.userId !== userId) {
|
||||
|
||||
throw createError({ statusCode: 403, statusMessage: "You don't own this tile" });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Business logic validation
|
||||
|
||||
if (tile.terrainType !== 'EMPTY' || tile.object) {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: 'Tile is not empty' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const cost = BUILDING_COSTS[buildingType];
|
||||
|
||||
if (user.coins < cost) {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: 'Not enough coins' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (PRODUCING_BUILDINGS.includes(buildingType)) {
|
||||
|
||||
const villageObjects = await tx.villageObject.findMany({ where: { villageId: tile.villageId } });
|
||||
|
||||
const housesCount = villageObjects.filter(o => o.type === 'HOUSE').length;
|
||||
|
||||
const producingCount = villageObjects.filter(o => PRODUCING_BUILDINGS.includes(o.type)).length;
|
||||
|
||||
if (producingCount >= housesCount) {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: 'Not enough workers (houses)' });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 2. Perform mutations
|
||||
|
||||
await tx.user.update({
|
||||
|
||||
where: { id: userId },
|
||||
|
||||
data: { coins: { decrement: cost } },
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await tx.villageObject.create({
|
||||
|
||||
data: {
|
||||
|
||||
type: buildingType as keyof typeof VillageObjectType,
|
||||
|
||||
villageId: tile.villageId,
|
||||
|
||||
tileId: tileId,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function clearTile(userId: number, tileId: number) {
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
|
||||
const tile = await tx.villageTile.findUniqueOrThrow({
|
||||
|
||||
where: { id: tileId },
|
||||
|
||||
include: { village: { include: { objects: true } } },
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (tile.village.userId !== userId) {
|
||||
|
||||
throw createError({ statusCode: 403, statusMessage: "You don't own this tile" });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tile.terrainState !== 'IDLE') {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: 'Tile is not idle' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tile.terrainType === 'BLOCKED_TREE') {
|
||||
|
||||
const hasLumberjack = tile.village.objects.some(o => o.type === 'LUMBERJACK');
|
||||
|
||||
if (!hasLumberjack) throw createError({ statusCode: 400, statusMessage: 'Requires a Lumberjack to clear trees' });
|
||||
|
||||
} else if (tile.terrainType === 'BLOCKED_STONE') {
|
||||
|
||||
const hasQuarry = tile.village.objects.some(o => o.type === 'QUARRY');
|
||||
|
||||
if (!hasQuarry) throw createError({ statusCode: 400, statusMessage: 'Requires a Quarry to clear stones' });
|
||||
|
||||
} else {
|
||||
|
||||
throw createError({ statusCode: 400, statusMessage: 'Tile is not blocked by trees or stones' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
await tx.villageTile.update({
|
||||
|
||||
where: { id: tileId },
|
||||
|
||||
data: {
|
||||
|
||||
terrainState: 'CLEARING',
|
||||
|
||||
clearingStartedAt: new Date(),
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function removeObject(userId: number, tileId: number) {
|
||||
|
||||
// As requested, this is a stub for now.
|
||||
|
||||
throw createError({ statusCode: 501, statusMessage: 'Remove action not implemented yet' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function moveObject(userId: number, fromTileId: number, toTileId: number) {
|
||||
|
||||
// As requested, this is a stub for now.
|
||||
|
||||
throw createError({ statusCode: 501, statusMessage: 'Move action not implemented yet' });
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user