import { getItemTypesFromMapItems, getPropertiesFromMapItems } from "../../utils"; import AppDataSource from "../dbConnecter"; import { Map as GameMap } from "../entities/map"; import { unlinkSync } from "fs"; import path from "path"; import { deleteFiles } from "../../utils/file-uploader"; import { Model } from "../entities/model"; import { In } from "typeorm"; import { Street } from "../entities/street"; import { Property } from "../entities/property"; import { MapItem } from "../entities/mapItem"; const mapRepository = AppDataSource.getRepository(GameMap); const modelRepository = AppDataSource.getRepository(Model); export const createMap = async (name: string) => { const map = new GameMap(); map.name = name; map.indexList = []; return await mapRepository.save(map); }; export const deleteMap = async (id: string) => { const map = await mapRepository.findOne({ where: { id }, }); if (map) { await mapRepository.remove(map); return; } else { throw new Error("无效的id"); } }; export const updateMapName = async (mapId: string, name: string) => { await mapRepository.createQueryBuilder().update(GameMap).set({ name }).where("id = :id", { id: mapId }).execute(); }; export const updateMapUseState = async (mapId: string, inUse: boolean) => { await mapRepository.createQueryBuilder().update(GameMap).set({ inUse }).where("id = :id", { id: mapId }).execute(); if (inUse) { const map = await loadGameMapInfo(mapId); if (map) mapInfoCache.set(mapId, map); } }; export const setBackground = async (mapId: string, backgroundUrl: string) => { const { background: oldBg } = (await mapRepository.findOne({ where: { id: mapId }, select: ["background"] })) || { background: "", }; if (oldBg) { const filePathArr = oldBg.split("/"); const fileName = filePathArr[filePathArr.length - 1 >= 0 ? filePathArr.length - 1 : 0]; try { await deleteFiles([`monopoly/backgrounds/${fileName}`]); } catch (e: any) { throw new Error(`在删除原有Background时发生错误:${e.message}`); } } await mapRepository .createQueryBuilder() .update(GameMap) .set({ background: backgroundUrl }) .where("id = :id", { id: mapId }) .execute(); }; export const updateHouseModelList = async (mapId: string, houseModels: { lv0: string; lv1: string; lv2: string }) => { const houseModel_lv0 = await modelRepository.findOne({ where: { id: houseModels.lv0 } }); const houseModel_lv1 = await modelRepository.findOne({ where: { id: houseModels.lv1 } }); const houseModel_lv2 = await modelRepository.findOne({ where: { id: houseModels.lv2 } }); const map = await mapRepository.findOne({ where: { id: mapId } }); if (map && houseModel_lv0 && houseModel_lv1 && houseModel_lv2) { map.houseModel_lv0 = houseModel_lv0; map.houseModel_lv1 = houseModel_lv1; map.houseModel_lv2 = houseModel_lv2; mapRepository.save(map); } else { throw new Error("获取Map或者Model时发生错误"); } }; export const updateIndexList = async (id: string, indexList: string[]) => { mapRepository.createQueryBuilder().update(GameMap).set({ indexList }).where("id = :id", { id }).execute(); }; const mapInfoCache: Map = new Map(); export const getMapById = async (id: string, isAdmin: boolean) => { if (!mapInfoCache.get(id) || isAdmin) { const map = await loadGameMapInfo(id); if (!map) return null; if (!isAdmin) mapInfoCache.set(id, map); //如果是管理员访问则不更新缓存, 只会在管理员在管理端开启地图时才会更新缓存 return map; } else { return mapInfoCache.get(id) || null; } }; async function loadGameMapInfo(id: string) { const map = await mapRepository.findOne({ where: { id }, relations: [ "mapItems", "mapItems.linkto", "mapItems.linkto.type", "mapItems.linkto.property", "mapItems.linkto.property.street", "mapItems.type", "mapItems.type.model", "mapItems.arrivedEvent", "mapItems.property", "mapItems.property.street", // "properties", // "properties.street", "chanceCards", "streets", "houseModel_lv0", "houseModel_lv1", "houseModel_lv2", ], }); if (map) { map.itemTypes = getItemTypesFromMapItems(map.mapItems) as any; map.properties = getPropertiesFromMapItems(map.mapItems) as any; return map; } } export const getMapsList = async (page: number, size: number, isAdmin: boolean) => { const total = await mapRepository.count(); const shouldPage = page > 0 && size > 0 && size < total; let mapsList = await mapRepository.find({ relations: ["mapItems", "mapItems.type", "mapItems.type.model", "chanceCards"], skip: shouldPage ? (page - 1) * size : undefined, take: shouldPage ? size : undefined, }); mapsList.map((map) => { map.itemTypes = getItemTypesFromMapItems(map.mapItems) as any; return map; }); const deduped = new Map(); mapsList.forEach((map) => { if (!deduped.has(map.id)) deduped.set(map.id, map); }); mapsList = Array.from(deduped.values()); const showAllMaps = process.env.MAP_LIST_SHOW_ALL !== "false"; if (!isAdmin && !showAllMaps) { mapsList = mapsList.filter((m) => m.inUse); } if (process.env.MAP_LIST_DEBUG === "true") { const summary = mapsList.map((m) => `${m.id}:${m.name}:${m.inUse ? "on" : "off"}`).join(", "); console.log(`[map-list] total=${total} returned=${mapsList.length} page=${page} size=${size} showAll=${showAllMaps} :: ${summary}`); } return { mapsList, total }; }; export const getMapIndexsByMapId = async (id: string) => { const map = await mapRepository.findOne({ where: { id }, select: ["indexList"] }); if (map) { return map.indexList; } else { throw new Error("地图不存在"); } }; export const cloneMapWithVariant = async ( sourceMapId: string, newName: string, options?: { propertyCostScale?: number; tollScale?: number; streetScale?: number } ) => { const source = await mapRepository.findOne({ where: { id: sourceMapId }, relations: [ "mapItems", "mapItems.type", "mapItems.linkto", "mapItems.property", "mapItems.property.street", "mapItems.arrivedEvent", "chanceCards", "itemTypes", "streets", "houseModel_lv0", "houseModel_lv1", "houseModel_lv2", ], }); if (!source) { throw new Error("原始地图不存在"); } if (!newName?.trim()) { throw new Error("新地图名称不能为空"); } const propertyCostScale = Math.max(0.1, Number(options?.propertyCostScale || 1)); const tollScale = Math.max(0.1, Number(options?.tollScale || 1)); const streetScale = Math.max(0.1, Number(options?.streetScale || 1)); const map = new GameMap(); map.name = newName.trim(); map.background = source.background; map.inUse = false; map.indexList = []; map.itemTypes = source.itemTypes || []; map.chanceCards = source.chanceCards || []; map.houseModel_lv0 = source.houseModel_lv0; map.houseModel_lv1 = source.houseModel_lv1; map.houseModel_lv2 = source.houseModel_lv2; const streetMap = new Map(); map.streets = (source.streets || []).map((street) => { const s = new Street(); s.name = street.name; s.increase = Number((street.increase * streetScale).toFixed(4)); s.map = map; streetMap.set(street.id, s); return s; }); const sourceProperties = (source.mapItems || []) .filter((item) => Boolean(item.property)) .map((item) => item.property as Property) .filter((p, index, arr) => arr.findIndex((x) => x.id === p.id) === index); const propertyMap = new Map(); map.properties = sourceProperties.map((property) => { const p = new Property(); p.name = property.name; p.sellCost = Math.max(1, Math.round(property.sellCost * propertyCostScale)); p.buildCost = Math.max(1, Math.round(property.buildCost * propertyCostScale)); p.cost_lv0 = Math.max(1, Math.round(property.cost_lv0 * tollScale)); p.cost_lv1 = Math.max(1, Math.round(property.cost_lv1 * tollScale)); p.cost_lv2 = Math.max(1, Math.round(property.cost_lv2 * tollScale)); p.effectCode = property.effectCode; p.map = map; p.street = property.street ? streetMap.get(property.street.id) || map.streets[0] : map.streets[0]; propertyMap.set(property.id, p); return p; }); const mapItemMap = new Map(); map.mapItems = (source.mapItems || []).map((item) => { const m = new MapItem(); m._id = item._id; m.x = item.x; m.y = item.y; m.rotation = item.rotation; m.type = item.type; m.arrivedEvent = item.arrivedEvent; m.map = map; if (item.property) { m.property = propertyMap.get(item.property.id); } mapItemMap.set(item.id, m); return m; }); (source.mapItems || []).forEach((item) => { if (!item.linkto) return; const clonedItem = mapItemMap.get(item.id); const clonedTarget = mapItemMap.get(item.linkto.id); if (clonedItem && clonedTarget) { clonedItem.linkto = clonedTarget; } }); const sourceIndexList = [...(source.indexList || [])]; map.indexList = []; const saved = await mapRepository.save(map); const savedWithItems = await mapRepository.findOne({ where: { id: saved.id }, relations: ["mapItems"], }); if (savedWithItems && sourceIndexList.length > 0) { const sourceIdToItemKey = new Map((source.mapItems || []).map((item) => [item.id, item._id])); const savedItemKeyToId = new Map((savedWithItems.mapItems || []).map((item) => [item._id, item.id])); savedWithItems.indexList = sourceIndexList .map((oldId) => { const key = sourceIdToItemKey.get(oldId) || ""; return savedItemKeyToId.get(key) || ""; }) .filter(Boolean); await mapRepository.save(savedWithItems); return savedWithItems; } return saved; };