74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
import { getUserIdFromSession } from '../../../utils/auth';
|
|
import { VILLAGE_GRID_SIZE, MOVE_COST, isCropGrown } from '../../../utils/village';
|
|
|
|
// --- Handler ---
|
|
export default defineEventHandler(async (event) => {
|
|
const userId = await getUserIdFromSession(event);
|
|
const objectId = parseInt(event.context.params?.id || '', 10);
|
|
const { x, y } = await readBody(event);
|
|
|
|
// 1. --- Validation ---
|
|
if (isNaN(objectId)) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Invalid object ID.' });
|
|
}
|
|
if (typeof x !== 'number' || typeof y !== 'number') {
|
|
throw createError({ statusCode: 400, statusMessage: 'Invalid request body. "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: 'Cannot move object outside of village bounds.' });
|
|
}
|
|
|
|
// 2. --- Fetch state and enforce rules ---
|
|
const village = await prisma.village.findUnique({
|
|
where: { userId },
|
|
include: { objects: true },
|
|
});
|
|
|
|
if (!village) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Village not found.' });
|
|
}
|
|
|
|
const objectToMove = village.objects.find(obj => obj.id === objectId);
|
|
|
|
// Rule: Object must exist and belong to the user (implicit via village)
|
|
if (!objectToMove) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Object not found.' });
|
|
}
|
|
|
|
// Rule: Cannot move obstacles
|
|
if (objectToMove.type === 'OBSTACLE') {
|
|
throw createError({ statusCode: 400, statusMessage: 'Cannot move obstacles. They must be cleared.' });
|
|
}
|
|
|
|
// Rule: Target cell must be empty (and not the same cell)
|
|
if (village.objects.some(obj => obj.x === x && obj.y === y)) {
|
|
throw createError({ statusCode: 409, statusMessage: 'Target cell is already occupied.' });
|
|
}
|
|
|
|
// 3. --- Perform atomic transaction ---
|
|
try {
|
|
const [, updatedObject] = await prisma.$transaction([
|
|
prisma.user.update({
|
|
where: { id: userId, coins: { gte: MOVE_COST } },
|
|
data: { coins: { decrement: MOVE_COST } },
|
|
}),
|
|
prisma.villageObject.update({
|
|
where: { id: objectId },
|
|
data: { x, y },
|
|
}),
|
|
]);
|
|
|
|
return {
|
|
id: updatedObject.id,
|
|
type: updatedObject.type,
|
|
x: updatedObject.x,
|
|
y: updatedObject.y,
|
|
cropType: updatedObject.cropType,
|
|
isGrown: isCropGrown(updatedObject.plantedAt, updatedObject.cropType),
|
|
};
|
|
|
|
} catch (e) {
|
|
throw createError({ statusCode: 402, statusMessage: 'Insufficient coins to move object.' });
|
|
}
|
|
});
|