import { TMRRencontres } from "./tmr-rencontres.js"; import { Misc } from "./misc.js"; import { Grammar } from "./grammar.js"; import { RdDDice } from "./rdd-dice.js"; /* -------------------------------------------- */ const TMRMapping = { A1: { type: "cite", label: "Cité Vide" }, B1: { type: "plaines", label: "Plaines d’Assorh" }, C1: { type: "necropole", label: "Nécropole de Kroak" }, D1: { type: "fleuve", label: "Fleuve de l'Oubli" }, E1: { type: "monts", label: "Monts de Kanaï" }, F1: { type: "cite", label: "Cité Glauque" }, G1: { type: "desolation", label: "Désolation de Demain" }, H1: { type: "lac", label: "Lac d’Anticalme" }, I1: { type: "plaines", label: "Plaines Grises" }, J1: { type: "monts", label: "Monts Fainéants" }, K1: { type: "cite", label: "Cité d’Onkause" }, L1: { type: "fleuve", label: "Fleuve de l'Oubli" }, M1: { type: "cite", label: "Cité Jalouse" }, A2: { type: "desert", label: "Désert de Mieux" }, B2: { type: "collines", label: "Collines de Dawell" }, C2: { type: "marais", label: "Marais Glignants" }, D2: { type: "cite", label: "Cité de Frost" }, E2: { type: "plaines", label: "Plaines de Fiask" }, F2: { type: "lac", label: "Lac de Misère" }, G2: { type: "marais", label: "Marais Nuisants" }, H2: { type: "collines", label: "Collines de Parta" }, I2: { type: "foret", label: "Forêt Fade" }, J2: { type: "desert", label: "Désert de Poly" }, K2: { type: "foret", label: "Forêt Tamée" }, L2: { type: "fleuve", label: "Fleuve de l'Oubli" }, M2: { type: "necropole", label: "Nécropole de Logos" }, A3: { type: "desolation", label: "Désolation de Demain" }, B3: { type: "plaines", label: "Plaines de Rubéga" }, C3: { type: "fleuve", label: "Fleuve de l'Oubli" }, D3: { type: "gouffre", label: "Gouffre d’Oki" }, E3: { type: "foret", label: "Forêt d’Estoubh" }, F3: { type: "fleuve", label: "Fleuve de l'Oubli" }, G3: { type: "gouffre", label: "Gouffre de Sun" }, H3: { type: "foret", label: "Forêt de Ganna" }, I3: { type: "monts", label: "Monts Grinçants" }, J3: { type: "cite", label: "Cité Venin" }, K3: { type: "plaines", label: "Plaines de Dois" }, L3: { type: "lac", label: "Lac Laineux" }, M3: { type: "monts", label: "Monts de Vdah" }, A4: { type: "foret", label: "Forêt de Falconax" }, B4: { type: "monts", label: "Monts Crâneurs" }, C4: { type: "pont", label: "Pont de Giolii" }, D4: { type: "lac", label: "Lac de Foam" }, E4: { type: "plaines", label: "Plaines d’Orti" }, F4: { type: "fleuve", label: "Fleuve de l'Oubli" }, G4: { type: "sanctuaire", label: "Sanctuaire Blanc" }, H4: { type: "plaines", label: "Plaines de Psark" }, I4: { type: "plaines", label: "Plaines de Xiax" }, J4: { type: "collines", label: "Collines d’Encre" }, K4: { type: "pont", label: "Pont de Fah" }, L4: { type: "sanctuaire", label: "Sanctuaire Mauve" }, M4: { type: "gouffre", label: "Gouffre Grisant" }, A5: { type: "plaines", label: "Plaines de Trilkh" }, B5: { type: "collines", label: "Collines de Tanegy" }, C5: { type: "marais", label: "Marais Flouants" }, D5: { type: "fleuve", label: "Fleuve de l'Oubli" }, E5: { type: "monts", label: "Monts Brûlants" }, F5: { type: "cite", label: "Cité de Panople" }, G5: { type: "pont", label: "Pont d’Ik" }, H5: { type: "desert", label: "Désert de Krane" }, I5: { type: "desolation", label: "Désolation de Demain" }, J5: { type: "marais", label: "Marais de Jab" }, K5: { type: "fleuve", label: "Fleuve de l'Oubli" }, L5: { type: "collines", label: "Collines Suaves" }, M5: { type: "cite", label: "Cité Rimarde" }, A6: { type: "necropole", label: "Nécropole de Zniak" }, B6: { type: "foret", label: "Forêt de Bust" }, C6: { type: "cite", label: "Cité Pavois" }, D6: { type: "fleuve", label: "Fleuve de l'Oubli" }, E6: { type: "sanctuaire", label: "Sanctuaire de Plaine" }, F6: { type: "fleuve", label: "Fleuve de l'Oubli" }, G6: { type: "marais", label: "Marais Glutants" }, H6: { type: "monts", label: "Monts Gurdes" }, I6: { type: "necropole", label: "Nécropole de Xotar" }, J6: { type: "lac", label: "Lac d’Iaupe" }, K6: { type: "desolation", label: "Désolation de Demain" }, L6: { type: "foret", label: "Forêt Gueuse" }, M6: { type: "desolation", label: "Désolation de Demain" }, A7: { type: "plaines", label: "Plaines de l’Arc" }, B7: { type: "marais", label: "Marais Bluants" }, C7: { type: "fleuve", label: "Fleuve de l'Oubli" }, D7: { type: "plaines", label: "Plaines d’Affa" }, E7: { type: "foret", label: "Forêt de Glusks" }, F7: { type: "fleuve", label: "Fleuve de l'Oubli" }, G7: { type: "cite", label: "Cité de Terwa" }, H7: { type: "gouffre", label: "Gouffre de Kapfa" }, I7: { type: "plaines", label: "Plaines de Troo" }, J7: { type: "fleuve", label: "Fleuve de l'Oubli" }, K7: { type: "cite", label: "Cité de Kolix" }, L7: { type: "gouffre", label: "Gouffre d’Episophe" }, M7: { type: "desert", label: "Désert de Lave" }, A8: { type: "gouffre", label: "Gouffre de Shok" }, B8: { type: "fleuve", label: "Fleuve de l'Oubli" }, C8: { type: "foret", label: "Forêt Turmide" }, D8: { type: "cite", label: "Cité d’Olak" }, E8: { type: "plaines", label: "Plaines d’Iolise" }, F8: { type: "lac", label: "Lac des Chats" }, G8: { type: "plaines", label: "Plaines Sans Joie" }, H8: { type: "foret", label: "Forêt d’Ourf" }, I8: { type: "fleuve", label: "Fleuve de l'Oubli" }, J8: { type: "monts", label: "Monts Barask" }, K8: { type: "desert", label: "Désert de Fumée" }, L8: { type: "monts", label: "Monts Tavelés" }, M8: { type: "plaines", label: "Plaines Lavées" }, A9: { type: "collines", label: "Collines de Korrex" }, B9: { type: "lac", label: "Lac de Lucre" }, C9: { type: "monts", label: "Monts Tuméfiés" }, D9: { type: "pont", label: "Pont d’Orx" }, E9: { type: "fleuve", label: "Fleuve de l'Oubli" }, F9: { type: "plaines", label: "Plaines de Foe" }, G9: { type: "desolation", label: "Désolation de Demain" }, H9: { type: "collines", label: "Collines de Noirseul" }, I9: { type: "fleuve", label: "Fleuve de l'Oubli" }, J9: { type: "marais", label: "Marais Gronchants" }, K9: { type: "sanctuaire", label: "Sanctuaire Noir" }, L9: { type: "collines", label: "Collines Cornues" }, M9: { type: "necropole", label: "Nécropole de Zonar" }, A10: { type: "sanctuaire", label: "Sanctuaire d’Olis" }, B10: { type: "monts", label: "Monts Salés" }, C10: { type: "marais", label: "Marais de Dom" }, D10: { type: "fleuve", label: "Fleuve de l'Oubli" }, E10: { type: "gouffre", label: "Gouffre de Junk" }, F10: { type: "marais", label: "Marais Zultants" }, G10: { type: "cite", label: "Cité de Sergal" }, H10: { type: "plaines", label: "Plaines Noires" }, I10: { type: "lac", label: "Lac Wanito" }, J10: { type: "fleuve", label: "Fleuve de l'Oubli" }, K10: { type: "plaines", label: "Plaines Jaunes" }, L10: { type: "desert", label: "Désert de Nicrop" }, M10: { type: "foret", label: "Forêt de Jajou" }, A11: { type: "desolation", label: "Désolation de Demain" }, B11: { type: "cite", label: "Cité de Brilz" }, C11: { type: "pont", label: "Pont de Roï" }, D11: { type: "desolation", label: "Désolation de Demain" }, E11: { type: "lac", label: "Lac de Glinster" }, F11: { type: "cite", label: "Cité de Noape" }, G11: { type: "fleuve", label: "Fleuve de l'Oubli" }, H11: { type: "fleuve", label: "Fleuve de l'Oubli" }, I11: { type: "pont", label: "Pont de Yalm" }, J11: { type: "plaines", label: "Plaines de Miltiar" }, K11: { type: "cite", label: "Cité Tonnerre" }, L11: { type: "collines", label: "Collines de Kol" }, M11: { type: "cite", label: "Cité Crapaud" }, A12: { type: "plaines", label: "Plaines Sages" }, B12: { type: "fleuve", label: "Fleuve de l'Oubli" }, C12: { type: "lac", label: "Lac de Fricassa" }, D12: { type: "collines", label: "Collines d’Huaï" }, E12: { type: "monts", label: "Monts Ajourés" }, F12: { type: "necropole", label: "Nécropole de Troat" }, G12: { type: "plaines", label: "Plaines de Lufmil" }, H12: { type: "collines", label: "Collines de Tooth" }, I12: { type: "gouffre", label: "Gouffre Abimeux" }, J12: { type: "cite", label: "Cité Folle" }, K12: { type: "desolation", label: "Désolation de Demain" }, L12: { type: "plaines", label: "Plaines Venteuses" }, M12: { type: "collines", label: "Collines Révulsantes" }, A13: { type: "fleuve", label: "Fleuve de l'Oubli" }, B13: { type: "gouffre", label: "Gouffre des Litiges" }, C13: { type: "desert", label: "Désert de Neige" }, D13: { type: "cite", label: "Cité Sordide" }, E13: { type: "plaines", label: "Plaines de Xnez" }, F13: { type: "foret", label: "Forêt des Cris" }, G13: { type: "plaines", label: "Plaines Calcaires" }, H13: { type: "desolation", label: "Désolation de Demain" }, I13: { type: "monts", label: "Monts Bigleux" }, J13: { type: "gouffre", label: "Gouffre de Gromph" }, K13: { type: "foret", label: "Forêt de Kluth" }, L13: { type: "monts", label: "Monts Dormants" }, M13: { type: "plaines", label: "Plaines d’Anjou" }, A14: { type: "collines", label: "Collines de Stolis" }, B14: { type: "necropole", label: "Nécropole de Gorlo" }, C14: { type: "foret", label: "Forêt de Bissam" }, D14: { type: "sanctuaire", label: "Sanctuaire Plat" }, E14: { type: "monts", label: "Monts de Quath" }, F14: { type: "plaines", label: "Plaines Brisées" }, G14: { type: "desert", label: "Désert de Sek" }, H14: { type: "plaines", label: "Plaines Blanches" }, I14: { type: "cite", label: "Cité Destituée" }, J14: { type: "desert", label: "Désert de Sank" }, K14: { type: "necropole", label: "Nécropole d’Antinéar" }, L14: { type: "plaines", label: "Plaines de Jislith" }, M14: { type: "desolation", label: "Désolation de Demain" }, A15: { type: "cite", label: "Cité de Mielh" }, C15: { type: "plaines", label: "Plaines de Toué" }, E15: { type: "foret", label: "Forêt des Furies" }, G15: { type: "plaines", label: "Plaines des Soupirs" }, I15: { type: "monts", label: "Monts des Dragées" }, K15: { type: "collines", label: "Collines Pourpres" }, M15: { type: "cite", label: "Cité de Klana" } } export const TMRType = { cite: { name: "cité", genre: "f" }, sanctuaire: { name: "sanctuaire", genre: 'm' }, plaines: { name: "plaines", genre: "fp" }, pont: { name: "pont", genre: "m" }, collines: { name: "collines", genre: "p" }, foret: { name: "forêt", genre: "f" }, monts: { name: "monts", genre: "p" }, desert: { name: "désert", genre: "m" }, fleuve: { name: "fleuve", genre: "m" }, lac: { name: "lac", genre: "m" }, marais: { name: "marais", genre: "m" }, gouffre: { name: "gouffre", genre: "m" }, necropole: { name: "nécropole", genre: "f" }, desolation: { name: "désolation", genre: "f" } } /* -------------------------------------------- */ const caseSpecificModes = ["attache", "trounoir", "debordement", "reserve_extensible", "maitrisee"]; /* -------------------------------------------- */ const tmrRandomMovePatten = [{ name: 'top', x: 0, y: -1 }, { name: 'topright', x: 1, y: -1 }, { name: 'botright', x: 1, y: 1 }, { name: 'bot', x: 0, y: 1 }, { name: 'botleft', x: -1, y: 1 }, { name: 'topleft', x: -1, y: -1 } ] /* -------------------------------------------- */ export const tmrConstants = { col1_y: 30, col2_y: 55, cellw: 55, cellh: 55, gridx: 28, gridy: 28, // tailles third: 18, half: 27.5, twoThird: 36, full: 55, // decallages center: { x: 0, y: 0 }, top: { x: 0, y: -11.5 }, topLeft: { x: -11.5, y: -11.5 }, left: { x: -11.5, y: 0 }, bottomLeft: { x: -11.5, y: 11.5 }, bottom: { x: 0, y: 11.5 }, bottomRight: { x: 11.5, y: 11.5 }, right: { x: 11.5, y: 0 }, topRight: { x: 11.5, y: -11.5 }, } // couleurs export const tmrColors = { sort: 0xFF8800, tetes: 0xA000FF, souffle: 0x804040, queues: 0xAA4040, trounoir: 0x401060, demireve: 0x00FFEE, rencontre: 0xFF0000, casehumide: 0x1050F0, } export const tmrTokenZIndex = { sort: 40, tetes: 20, casehumide: 10, conquete: 30, rencontre: 50, trounoir: 60, demireve: 70, tooltip: 100, } /* -------------------------------------------- */ /* -------------------------------------------- */ export class TMRUtility { static init() { for (let coord in TMRMapping) { const tmr = TMRMapping[coord]; tmr.coord = coord; tmr.genre = TMRType[tmr.type].genre; } let tmrByType = Misc.classify(Object.values(TMRMapping)); for (const [type, list] of Object.entries(tmrByType)) { TMRType[type].list = list; } } /* -------------------------------------------- */ static convertToTMRCoord(pos) { let letterX = String.fromCharCode(65 + (pos.x)); return letterX + (pos.y + 1) } /* -------------------------------------------- */ static verifyTMRCoord(coord) { let TMRregexp = new RegExp(/([A-M])(\d+)/g); let res = TMRregexp.exec(coord); if (res && res[1] && res[2]) { if (res[2] > 0 && res[2] < 16) { return true; } } return false; } /* -------------------------------------------- */ static convertToCellPos(coordTMR) { let x = coordTMR.charCodeAt(0) - 65; let y = coordTMR.substr(1) - 1; return { x: x, y: y } } /* -------------------------------------------- */ static getTMR(coord) { return TMRMapping[coord]; } static getTMRLabel(coord) { return TMRMapping[coord]?.label ?? (coord + ": case inconnue"); } static getTMRType(coord) { const tmr = TMRMapping[coord]; return Misc.upperFirst(TMRType[tmr.type].name); } static getTMRDescr(coord) { const tmr = TMRMapping[coord]; return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label; } static typeTmrName(type) { return Misc.upperFirst(TMRType[Grammar.toLowerCaseNoAccent(type)].name); } static listSelectedTMR(typesTMR) { return Object.values(TMRType).map(value => Misc.upperFirst(value.name)) .sort() .map(name => { return { name: name, selected: typesTMR.includes(name) } }); } static isCaseHumide(tmr) { return tmr.type == 'fleuve' || tmr.type == 'lac' || tmr.type == 'marais'; } /* -------------------------------------------- */ /** Some debug functions */ static async setForceRencontre(index, force = undefined) { this.prochaineRencontre = TMRRencontres.getRencontre(index); if (this.prochaineRencontre) { if (force) { this.prochaineRencontre.force = force; } else { await TMRRencontres.evaluerForceRencontre(this.prochaineRencontre); } console.log("La prochaine rencontre sera:", this.prochaineRencontre.name, " force:", this.prochaineRencontre.force); } else { ui.notifications.warn("Pas de prochaine rencontre valide pour " + index); } } /* -------------------------------------------- */ static isForceRencontre() { return this.prochaineRencontre; } /* -------------------------------------------- */ static utiliseForceRencontre() { const rencontre = this.prochaineRencontre; this.prochaineRencontre = undefined; return rencontre; } /* -------------------------------------------- */ static async getDirectionPattern() { return await RdDDice.rollOneOf(tmrRandomMovePatten); } /* -------------------------------------------- */ static async deplaceTMRAleatoire(actor, coord) { return TMRUtility.deplaceTMRSelonPattern(actor, coord, await TMRUtility.getDirectionPattern(), 1); } /* -------------------------------------------- */ static async deplaceTMRSelonPattern(actor, coord, direction, nTime) { for (let i = 0; i < nTime; i++) { let currentPos = TMRUtility.convertToCellPos(coord); currentPos.x = currentPos.x + direction.x; currentPos.y = currentPos.y + direction.y; if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Sortie de carte ! Ré-insertion aléatoire coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos)); } else { coord = await actor.reinsertionAleatoire('Sortie de carte'); } console.log("Nouvelle case iteration !!!", i, coord); } return coord; } /* -------------------------------------------- */ static getListTMR(terrain) { return TMRType[terrain].list; } static filterTMR(filter) { return Object.values(TMRMapping).filter(filter); } static findTMR(search) { const labelSearch = Grammar.toLowerCaseNoAccent(search) return TMRUtility.filterTMR(it => Grammar.toLowerCaseNoAccent(it.label).match(labelSearch) || it.coord == search); } static filterTMRCoord(filter) { return TMRUtility.filterTMR(filter).map(it => it.coord); } static async getTMRAleatoire(filter = it => true) { return await RdDDice.rollOneOf(TMRUtility.filterTMR(filter)) } /* -------------------------------------------- */ static _checkTMRCoord(x, y) { if (x >= 0 && x < 13 && y >= 0 && y < 14) return true; if (x >= 0 && x < 13 && x % 2 == 0 && y == 14) return true; return false; } /* -------------------------------------------- */ static computeRealPictureCoordinates(coordXY, tmrConstants) { let decallagePairImpair = (coordXY.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y; return { x: tmrConstants.gridx + (coordXY.x * tmrConstants.cellw), y: tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) + decallagePairImpair } } /* -------------------------------------------- */ static getSortsReserve(reserveList, coord) { // TODO : Gérer les têtes spéciales réserve! let tmrDescr = this.getTMR(coord); //console.log("Sort réserve : ", tmrDescr); if (tmrDescr.type == 'fleuve') { // Gestion de la reserve en Fleuve return reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve'); } // Reserve sur un case "normale" return reserveList.filter(it => it.coord == coord); } /* -------------------------------------------- */ /** Returns a list of case inside a given distance * */ static getTMRPortee(coord, portee) { let centerPos = this.convertToCellPos(coord); let posPic = this.computeRealPictureCoordinates(centerPos, tmrConstants); let caseList = []; for (let dx = -portee; dx <= portee; dx++) { // Loop thru lines for (let dy = -portee; dy <= portee; dy++) { // Loop thru lines const currentPos = { x: centerPos.x + dx, y: centerPos.y + dy }; if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Coordinate is valie let dist = this.distancePosTMR(centerPos, currentPos); if (dist <= portee) { caseList.push(this.convertToTMRCoord(currentPos)); // Inside the area } } } } return caseList; } /* -------------------------------------------- */ static distanceTMR(coord1, coord2) { let pos1 = this.convertToCellPos(coord1); let pos2 = this.convertToCellPos(coord2); return this.distancePosTMR(pos1, pos2); } /* -------------------------------------------- */ static distancePosTMR(pos1, pos2) { const dx = pos2.x - pos1.x; const dy = pos2.y - pos1.y; const abs_dx = Math.abs(dx); const abs_dy = Math.abs(dy); const distance = Math.sign(dx) == Math.sign(dy) ? Math.max(abs_dx, abs_dy) : (abs_dx + abs_dy); return distance; } }