79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
import { getUserIdFromSession } from '../../utils/auth';
|
|
import { VILLAGE_GRID_SIZE, ITEM_COSTS } from '../../utils/village';
|
|
import { VillageObjectType } from '@prisma/client';
|
|
|
|
// --- Handler ---
|
|
export default defineEventHandler(async (event) => {
|
|
const userId = await getUserIdFromSession(event);
|
|
const { type, x, y } = await readBody(event);
|
|
|
|
// 1. --- Validation ---
|
|
if (!type || typeof x !== 'number' || typeof y !== 'number') {
|
|
throw createError({ statusCode: 400, statusMessage: 'Invalid request body. "type", "x", and "y" are required.' });
|
|
}
|
|
if (x < 0 || x >= VILLAGE_GRID_SIZE.width || y < 0 || y >= VILLAGE_GRID_SIZE.height) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Object placed outside of village bounds.' });
|
|
}
|
|
const cost = ITEM_COSTS[type as VillageObjectType];
|
|
if (cost === undefined) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Cannot place objects of this type.' });
|
|
}
|
|
|
|
// 2. --- Fetch current state and enforce rules ---
|
|
const village = await prisma.village.findUnique({
|
|
where: { userId },
|
|
include: { objects: true },
|
|
});
|
|
|
|
if (!village) {
|
|
// This should not happen if GET /village is called first, but as a safeguard:
|
|
throw createError({ statusCode: 404, statusMessage: 'Village not found.' });
|
|
}
|
|
|
|
// Rule: Cell must be empty
|
|
if (village.objects.some(obj => obj.x === x && obj.y === y)) {
|
|
throw createError({ statusCode: 409, statusMessage: 'A building already exists on this cell.' });
|
|
}
|
|
|
|
// Rule: Fields require available workers
|
|
if (type === 'FIELD') {
|
|
const houseCount = village.objects.filter(obj => obj.type === 'HOUSE').length;
|
|
const fieldCount = village.objects.filter(obj => obj.type === 'FIELD').length;
|
|
if (fieldCount >= houseCount) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Not enough available workers to build a new field. Build more houses first.' });
|
|
}
|
|
}
|
|
|
|
// 3. --- Perform atomic transaction ---
|
|
try {
|
|
const [, newObject] = await prisma.$transaction([
|
|
prisma.user.update({
|
|
where: { id: userId, coins: { gte: cost } },
|
|
data: { coins: { decrement: cost } },
|
|
}),
|
|
prisma.villageObject.create({
|
|
data: {
|
|
villageId: village.id,
|
|
type,
|
|
x,
|
|
y,
|
|
},
|
|
}),
|
|
]);
|
|
|
|
setResponseStatus(event, 201);
|
|
return {
|
|
id: newObject.id,
|
|
type: newObject.type,
|
|
x: newObject.x,
|
|
y: newObject.y,
|
|
cropType: null,
|
|
isGrown: null,
|
|
};
|
|
|
|
} catch (e) {
|
|
// Catches failed transactions, likely from the user.update 'where' clause (insufficient funds)
|
|
throw createError({ statusCode: 402, statusMessage: 'Insufficient coins to build.' });
|
|
}
|
|
});
|