8047778a5b
Wellenbasierte Routen-Sim (DefensegeograSimMac) nach dem Tourismustal- Playbook integriert: Sim nach App/sims/tourismusregion (game.html, Sprites 47MB->9.5MB, ohne Eigen-Backend/Editor/Dashboard/Keys), Plattform-Hooks (GGS_LIVE_STATE mit Welle/Budget/Umwelt/Sterne/Lenkung, Submit bei Partie-Ende mit fertigem 0-100-Score, GGS_TUTORIAL, Home- Button, Musik leise). Wrapper + Modul-Detailseite. SQL: module_info (beta, Karte), Glossar-Kernbegriff Besucherlenkung (+Bild, Leichte Sprache) + 12 Zuordnungen, Lehrplan-Mapping auf tourismus- raumentwicklung (primaer) + verkehrserziehung/systemisches-denken. LEHRPLAN.md mit Schwerpunkt Besucherlenkung & Mobilitaet. Lehrer- Backend: Benchmark (Sim-Score direkt), Live-Cockpit-Spalten, needsHelp, Highlights. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
326 lines
16 KiB
JavaScript
326 lines
16 KiB
JavaScript
// Die 5 Wegkarten: je ein vorgerendertes 3D-Diorama (assets/maps/*.png,
|
||
// 1536×1024) mit handvermessenen Routen-Polylinien (Bild-Pixel), festen
|
||
// Bauplatz-Slots und optionalem Lift (Seilbahn zum Berg mit Rundweg oben).
|
||
// Zonen: standard · road (Parkplatz/Bus) · lake (Badesteg) · hang (Skilift)
|
||
// · berg (Almhütte/Aussicht, erst mit Lift erreichbar).
|
||
|
||
export const TILE2PX = 36; // 1 „Feld" Reichweite = 36 Bild-Pixel
|
||
|
||
export const MAPS = {
|
||
talschleife: {
|
||
id: 'talschleife', name: 'Talschleife', icon: '🏞️',
|
||
desc: 'Der Klassiker: ein Weg vom Westrand durchs Dorf zum Talausgang – See und Berg locken abseits.',
|
||
image: 'assets/maps/talschleife.png', w: 1536, h: 1024,
|
||
routes: {
|
||
westost: {
|
||
entry: 'W', exit: 'SE',
|
||
points: [[304, 340], [301, 354], [302, 370], [312, 379], [330, 382],
|
||
[346, 390], [359, 398], [347, 383], [344, 364], [353, 350],
|
||
[368, 338], [384, 333], [400, 335], [419, 347], [430, 357],
|
||
[440, 367], [447, 376], [455, 396], [461, 414], [471, 435],
|
||
[477, 455], [477, 473], [485, 488], [502, 502], [515, 516],
|
||
[532, 532], [549, 541], [564, 544], [572, 531], [579, 544],
|
||
[594, 551], [611, 555], [624, 567], [626, 581], [624, 565],
|
||
[610, 554], [595, 550], [577, 554], [563, 564], [557, 576],
|
||
[549, 591], [552, 611], [562, 627], [568, 640], [576, 658],
|
||
[578, 677], [578, 695], [588, 711], [600, 726], [606, 742],
|
||
[616, 754], [626, 766], [640, 773], [652, 784], [666, 786],
|
||
[686, 784], [705, 788], [716, 800], [715, 816], [728, 816],
|
||
[741, 826], [754, 836], [768, 840], [779, 851], [795, 860],
|
||
[814, 865], [829, 874], [836, 886], [838, 889]],
|
||
},
|
||
gegenzug: {
|
||
entry: 'S', exit: 'E', reverseOf: 'westost',
|
||
},
|
||
},
|
||
entryRoutes: { W: ['westost'], N: ['westost'], S: ['gegenzug'] },
|
||
slots: [
|
||
{ x: 300, y: 292, zone: 'road' },
|
||
{ x: 245, y: 425, zone: 'road' },
|
||
{ x: 430, y: 378, zone: 'standard' },
|
||
{ x: 588, y: 298, zone: 'standard' },
|
||
{ x: 634, y: 372, zone: 'standard' },
|
||
{ x: 697, y: 397, zone: 'standard' },
|
||
{ x: 485, y: 595, zone: 'standard' },
|
||
{ x: 700, y: 713, zone: 'standard' },
|
||
{ x: 776, y: 742, zone: 'standard' },
|
||
{ x: 357, y: 619, zone: 'standard' },
|
||
{ x: 1235, y: 546, zone: 'standard' },
|
||
{ x: 545, y: 655, zone: 'standard' },
|
||
{ x: 900, y: 788, zone: 'standard' },
|
||
{ x: 261, y: 520, zone: 'standard' },
|
||
{ x: 885, y: 615, zone: 'lake' },
|
||
{ x: 1005, y: 782, zone: 'lake' },
|
||
{ x: 987, y: 381, zone: 'hang' },
|
||
{ x: 888, y: 218, zone: 'berg' },
|
||
{ x: 1049, y: 296, zone: 'berg' },
|
||
],
|
||
lift: { topX: 952, topY: 168, loop: [[1000, 212], [935, 242]] },
|
||
},
|
||
|
||
seerunde: {
|
||
id: 'seerunde', name: 'Seerunde', icon: '🏖️',
|
||
desc: 'Ein Rundweg um den großen Bergsee – Nord- oder Südufer? Beide Ströme wollen versorgt sein.',
|
||
image: 'assets/maps/seerunde.png', w: 1536, h: 1024,
|
||
routes: {
|
||
nordufer: {
|
||
entry: 'W', exit: 'E',
|
||
points: [[274, 784], [290, 768], [300, 756], [311, 746], [323, 733],
|
||
[336, 718], [348, 706], [359, 695], [368, 686], [378, 672],
|
||
[371, 658], [358, 646], [348, 636], [336, 629], [325, 622],
|
||
[308, 608], [302, 590], [310, 570], [322, 555], [336, 542],
|
||
[350, 532], [368, 523], [384, 519], [396, 509], [407, 499],
|
||
[420, 493], [436, 480], [445, 467], [451, 452], [454, 433],
|
||
[456, 411], [459, 389], [466, 370], [477, 353], [490, 338],
|
||
[510, 326], [522, 322], [535, 318], [550, 315], [566, 313],
|
||
[584, 312], [603, 311], [620, 311], [636, 311], [648, 311],
|
||
[668, 312], [683, 315], [698, 318], [714, 315], [734, 322],
|
||
[748, 328], [760, 332], [780, 338], [792, 340], [804, 343],
|
||
[818, 346], [830, 348], [850, 353], [866, 358], [882, 366],
|
||
[902, 372], [921, 374], [942, 377], [960, 380], [976, 384],
|
||
[991, 391], [1010, 406], [1020, 414], [1028, 424], [1035, 435],
|
||
[1040, 446], [1044, 459], [1045, 471], [1046, 484], [1050, 502],
|
||
[1063, 514], [1082, 522], [1100, 531], [1118, 543], [1130, 554],
|
||
[1144, 555], [1158, 562], [1167, 574], [1170, 588], [1170, 604],
|
||
[1163, 618], [1151, 633], [1137, 642], [1116, 652], [1100, 662],
|
||
[1086, 670], [1068, 676], [1052, 678], [1032, 684], [1014, 691],
|
||
[1000, 697], [977, 700], [965, 701], [942, 702], [930, 705],
|
||
[914, 712], [897, 720], [880, 723], [866, 728], [852, 738],
|
||
[837, 746], [822, 751], [814, 762], [808, 780], [796, 798],
|
||
[780, 813], [763, 821], [744, 824], [728, 831], [702, 837],
|
||
[689, 838], [664, 837], [649, 836], [636, 834], [624, 834],
|
||
[605, 831], [588, 826], [576, 816], [588, 812], [606, 814],
|
||
[588, 812], [572, 802], [563, 793], [563, 808], [550, 807],
|
||
[536, 799], [522, 785], [506, 770], [488, 756], [474, 746],
|
||
[457, 736], [449, 728], [440, 715], [429, 701], [415, 686],
|
||
[401, 678], [384, 679], [369, 690], [360, 699], [348, 710],
|
||
[336, 722], [323, 735], [311, 747], [300, 758], [292, 766],
|
||
[279, 779], [274, 784]],
|
||
},
|
||
suedufer: {
|
||
entry: 'S', exit: 'SE', lake: true,
|
||
points: [[274, 784], [290, 768], [300, 756], [311, 746], [323, 733],
|
||
[336, 718], [349, 706], [360, 696], [370, 688], [387, 680],
|
||
[403, 686], [416, 696], [427, 702], [443, 715], [462, 732],
|
||
[473, 743], [486, 756], [497, 766], [506, 776], [521, 789],
|
||
[534, 796], [552, 805], [568, 808], [588, 812], [604, 815],
|
||
[589, 809], [579, 817], [596, 828], [614, 832], [636, 834],
|
||
[649, 836], [664, 837], [677, 838], [700, 838], [718, 834],
|
||
[735, 826], [750, 822], [770, 818], [788, 807], [797, 798],
|
||
[808, 780], [812, 763], [830, 753], [845, 745], [860, 735],
|
||
[874, 725], [892, 717], [910, 713], [927, 706], [947, 700],
|
||
[959, 699], [973, 699], [991, 698], [1014, 693], [1026, 688],
|
||
[1038, 684], [1055, 678], [1070, 676], [1086, 664], [1105, 654],
|
||
[1120, 650], [1140, 641], [1157, 626], [1167, 610], [1170, 593],
|
||
[1169, 576], [1158, 565], [1144, 561], [1131, 550], [1114, 538],
|
||
[1104, 532], [1090, 528], [1079, 522], [1060, 513], [1050, 502],
|
||
[1044, 481], [1042, 467], [1039, 450], [1031, 430], [1018, 412],
|
||
[1000, 398], [990, 391], [978, 386], [960, 380], [942, 377],
|
||
[921, 374], [902, 372], [882, 366], [866, 359], [840, 351],
|
||
[827, 348], [806, 345], [785, 340], [766, 334], [748, 327],
|
||
[732, 320], [716, 316], [698, 315], [681, 316], [664, 312],
|
||
[647, 311], [626, 310], [612, 310], [600, 310], [580, 311],
|
||
[562, 312], [545, 315], [534, 318], [518, 325], [503, 334],
|
||
[488, 344], [474, 355], [464, 366], [456, 385], [452, 408],
|
||
[452, 421], [453, 435], [452, 448], [448, 460], [443, 471],
|
||
[436, 481], [420, 494], [402, 502], [388, 512], [372, 522],
|
||
[356, 527], [337, 538], [324, 548], [310, 566], [303, 588],
|
||
[308, 607], [326, 622], [336, 629], [348, 636], [358, 645],
|
||
[368, 655], [381, 669], [368, 687], [357, 697], [347, 708],
|
||
[335, 720], [321, 734], [309, 746], [299, 758], [291, 766],
|
||
[279, 779], [274, 784]],
|
||
},
|
||
},
|
||
entryRoutes: { W: ['nordufer'], N: ['nordufer'], S: ['suedufer'] },
|
||
slots: [
|
||
{ x: 255, y: 641, zone: 'road' },
|
||
{ x: 330, y: 800, zone: 'road' },
|
||
{ x: 395, y: 330, zone: 'standard' },
|
||
{ x: 520, y: 285, zone: 'standard' },
|
||
{ x: 700, y: 245, zone: 'standard' },
|
||
{ x: 966, y: 300, zone: 'standard' },
|
||
{ x: 1075, y: 385, zone: 'standard' },
|
||
{ x: 1230, y: 651, zone: 'standard' },
|
||
{ x: 620, y: 772, zone: 'standard' },
|
||
{ x: 900, y: 792, zone: 'standard' },
|
||
{ x: 1080, y: 720, zone: 'standard' },
|
||
{ x: 598, y: 346, zone: 'lake' },
|
||
{ x: 700, y: 655, zone: 'lake' },
|
||
{ x: 880, y: 635, zone: 'lake' },
|
||
],
|
||
},
|
||
|
||
serpentinen: {
|
||
id: 'serpentinen', name: 'Serpentinen', icon: '⛰️',
|
||
desc: 'Über den Berg ins Tal: langer Abstieg in Kehren – die Seilbahn verbindet Dorf und Gipfelterrasse.',
|
||
image: 'assets/maps/serpentinen.png', w: 1536, h: 1024,
|
||
routes: {
|
||
kehren: {
|
||
entry: 'W', exit: 'SE',
|
||
points: [[355, 229], [357, 244], [362, 268], [367, 280], [379, 299],
|
||
[388, 308], [396, 318], [403, 329], [411, 340], [419, 351],
|
||
[432, 364], [446, 372], [463, 379], [480, 385], [498, 389],
|
||
[515, 392], [532, 394], [546, 396], [564, 401], [584, 406],
|
||
[601, 408], [619, 407], [640, 404], [664, 398], [677, 394],
|
||
[690, 390], [704, 384], [720, 377], [734, 372], [748, 367],
|
||
[761, 364], [774, 361], [786, 360], [798, 358], [819, 360],
|
||
[838, 362], [858, 362], [879, 363], [900, 370], [918, 378],
|
||
[936, 380], [945, 370], [952, 356], [950, 373], [939, 383],
|
||
[923, 385], [904, 375], [920, 378], [936, 378], [926, 393],
|
||
[916, 408], [895, 416], [881, 423], [869, 430], [858, 436],
|
||
[840, 446], [822, 455], [806, 462], [791, 467], [777, 472],
|
||
[757, 484], [747, 491], [732, 506], [724, 520], [716, 532],
|
||
[704, 542], [691, 557], [678, 569], [664, 583], [650, 593],
|
||
[635, 601], [614, 607], [602, 608], [584, 607], [566, 605],
|
||
[550, 604], [536, 604], [515, 608], [499, 617], [494, 630],
|
||
[503, 643], [513, 656], [526, 669], [542, 676], [557, 688],
|
||
[572, 704], [586, 712], [580, 731], [570, 740], [560, 752],
|
||
[549, 770], [544, 787], [548, 802], [561, 819], [558, 833],
|
||
[544, 848], [542, 868], [551, 884], [568, 898], [584, 911],
|
||
[598, 922], [601, 925]],
|
||
},
|
||
talwaerts: { entry: 'S', exit: 'E', reverseOf: 'kehren' },
|
||
},
|
||
entryRoutes: { W: ['kehren'], N: ['kehren'], S: ['talwaerts'] },
|
||
slots: [
|
||
{ x: 548, y: 901, zone: 'road' },
|
||
{ x: 725, y: 890, zone: 'road' },
|
||
{ x: 538, y: 325, zone: 'standard' },
|
||
{ x: 665, y: 436, zone: 'standard' },
|
||
{ x: 835, y: 428, zone: 'standard' },
|
||
{ x: 1006, y: 480, zone: 'standard' },
|
||
{ x: 415, y: 646, zone: 'standard' },
|
||
{ x: 303, y: 578, zone: 'standard' },
|
||
{ x: 695, y: 616, zone: 'standard' },
|
||
{ x: 855, y: 636, zone: 'standard' },
|
||
{ x: 1090, y: 780, zone: 'standard' },
|
||
{ x: 1087, y: 627, zone: 'hang' },
|
||
{ x: 845, y: 292, zone: 'berg' },
|
||
{ x: 951, y: 312, zone: 'berg' },
|
||
],
|
||
lift: { topX: 898, topY: 292, loop: [[858, 300], [938, 315]] },
|
||
},
|
||
|
||
kreuzung: {
|
||
id: 'kreuzung', name: 'Kreuzung', icon: '🛣️',
|
||
desc: 'Zwei Täler kreuzen sich am Dorfplatz: vier Ströme, ein Zentrum – wer deckt alle Richtungen ab?',
|
||
image: 'assets/maps/kreuzung.png', w: 1536, h: 1024,
|
||
routes: {
|
||
nordsued: {
|
||
entry: 'N', exit: 'SE',
|
||
points: [[487, 255], [498, 264], [511, 276], [528, 292], [538, 300],
|
||
[546, 308], [556, 316], [565, 325], [575, 333], [585, 342],
|
||
[595, 350], [605, 360], [615, 368], [624, 376], [642, 391],
|
||
[658, 406], [674, 418], [687, 430], [698, 440], [712, 456],
|
||
[716, 477], [712, 494], [705, 510], [695, 528], [682, 546],
|
||
[668, 565], [660, 575], [650, 585], [642, 595], [633, 604],
|
||
[624, 614], [616, 623], [607, 632], [598, 642], [590, 650],
|
||
[580, 660], [572, 668], [563, 677], [547, 694], [532, 710],
|
||
[516, 727], [504, 740], [493, 752], [490, 755]],
|
||
},
|
||
westost: {
|
||
entry: 'W', exit: 'E',
|
||
points: [[135, 472], [158, 472], [174, 472], [189, 472], [207, 472],
|
||
[228, 472], [248, 472], [270, 472], [290, 472], [312, 472],
|
||
[332, 472], [354, 472], [374, 472], [396, 472], [416, 472],
|
||
[436, 472], [456, 472], [474, 472], [493, 472], [510, 471],
|
||
[527, 471], [543, 471], [559, 471], [575, 470], [591, 470],
|
||
[607, 470], [623, 470], [638, 471], [654, 471], [670, 471],
|
||
[686, 472], [701, 472], [717, 474], [732, 476], [748, 478],
|
||
[763, 481], [779, 484], [795, 488], [811, 492], [828, 495],
|
||
[844, 499], [862, 502], [879, 506], [897, 509], [916, 513],
|
||
[934, 517], [953, 521], [972, 524], [990, 528], [1010, 532],
|
||
[1028, 536], [1047, 539], [1066, 543], [1084, 547], [1103, 551],
|
||
[1122, 554], [1140, 558], [1160, 561], [1178, 564], [1197, 568],
|
||
[1216, 571], [1234, 574], [1253, 577], [1271, 580], [1289, 582],
|
||
[1306, 585], [1324, 587], [1340, 590], [1357, 592], [1373, 593],
|
||
[1387, 595], [1400, 596], [1418, 598], [1430, 600]],
|
||
},
|
||
},
|
||
entryRoutes: { W: ['westost'], N: ['nordsued'], S: ['nordsued'] },
|
||
slots: [
|
||
{ x: 425, y: 285, zone: 'road' },
|
||
{ x: 300, y: 694, zone: 'road' },
|
||
{ x: 645, y: 218, zone: 'standard' },
|
||
{ x: 330, y: 380, zone: 'standard' },
|
||
{ x: 545, y: 435, zone: 'standard' },
|
||
{ x: 933, y: 546, zone: 'standard' },
|
||
{ x: 760, y: 665, zone: 'standard' },
|
||
{ x: 610, y: 782, zone: 'standard' },
|
||
{ x: 890, y: 795, zone: 'standard' },
|
||
{ x: 1140, y: 640, zone: 'standard' },
|
||
{ x: 1180, y: 478, zone: 'standard' },
|
||
{ x: 1405, y: 465, zone: 'standard' },
|
||
{ x: 391, y: 512, zone: 'lake' },
|
||
{ x: 310, y: 611, zone: 'lake' },
|
||
],
|
||
},
|
||
|
||
passstrasse: {
|
||
id: 'passstrasse', name: 'Passstraße', icon: '🛤️',
|
||
desc: 'Ein langer Anstieg zum Pass, kaum Bauplätze: Hier zählt jede Standortentscheidung doppelt.',
|
||
image: 'assets/maps/passstrasse.png', w: 1536, h: 1024,
|
||
routes: {
|
||
bergauf: {
|
||
entry: 'W', exit: 'E',
|
||
points: [[700, 859], [687, 855], [684, 840], [692, 824], [705, 814],
|
||
[707, 798], [697, 789], [686, 774], [670, 758], [656, 748],
|
||
[643, 740], [629, 733], [614, 725], [600, 718], [582, 705],
|
||
[570, 690], [564, 675], [564, 660], [572, 643], [592, 626],
|
||
[603, 619], [624, 606], [638, 599], [649, 592], [669, 582],
|
||
[686, 574], [702, 569], [710, 584], [695, 593], [672, 603],
|
||
[656, 611], [642, 620], [658, 612], [669, 606], [688, 598],
|
||
[710, 590], [723, 585], [743, 578], [757, 571], [766, 559],
|
||
[779, 548], [794, 541], [811, 537], [828, 532], [843, 527],
|
||
[863, 517], [880, 510], [897, 514], [913, 511], [925, 498],
|
||
[928, 479], [927, 460], [922, 444], [910, 432], [888, 418],
|
||
[870, 408], [856, 400], [842, 388], [837, 374], [847, 356],
|
||
[859, 345], [870, 336], [890, 324], [909, 315], [926, 311],
|
||
[937, 304]],
|
||
},
|
||
bergab: { entry: 'S', exit: 'SE', reverseOf: 'bergauf' },
|
||
},
|
||
entryRoutes: { W: ['bergauf'], N: ['bergauf'], S: ['bergab'] },
|
||
slots: [
|
||
{ x: 562, y: 815, zone: 'road' },
|
||
{ x: 760, y: 875, zone: 'road' },
|
||
{ x: 300, y: 565, zone: 'standard' },
|
||
{ x: 415, y: 660, zone: 'standard' },
|
||
{ x: 560, y: 600, zone: 'standard' },
|
||
{ x: 850, y: 548, zone: 'standard' },
|
||
{ x: 905, y: 400, zone: 'standard' },
|
||
{ x: 1016, y: 330, zone: 'standard' },
|
||
],
|
||
},
|
||
};
|
||
|
||
// Editor-Overrides (js/maps-custom.json) über die eingebauten Daten legen:
|
||
// routes ersetzt die Punktlisten (entry/exit/lake-Flags bleiben erhalten,
|
||
// unbekannte Routen-IDs werden neu angelegt), slots ersetzt die Slot-Liste.
|
||
export function applyMapOverrides(overrides) {
|
||
if (!overrides) return;
|
||
for (const [mid, o] of Object.entries(overrides)) {
|
||
const def = MAPS[mid];
|
||
if (!def) continue;
|
||
if (o.routes) {
|
||
for (const [rk, pts] of Object.entries(o.routes)) {
|
||
if (!Array.isArray(pts) || pts.length < 2) continue;
|
||
if (def.routes[rk]) {
|
||
def.routes[rk] = { ...def.routes[rk], points: pts, reverseOf: undefined };
|
||
} else {
|
||
def.routes[rk] = { entry: 'W', exit: 'E', points: pts };
|
||
}
|
||
}
|
||
// reverseOf-Varianten zeigen ggf. auf ersetzte Routen – bleibt gültig,
|
||
// solange die Basisroute existiert (prepMap löst sie auf)
|
||
}
|
||
if (Array.isArray(o.slots) && o.slots.length) {
|
||
def.slots = o.slots.map(s => ({ x: s.x, y: s.y, zone: s.zone || 'standard' }));
|
||
}
|
||
if (o.lift) def.lift = o.lift;
|
||
}
|
||
}
|
||
|
||
// Zubringer-Reihenfolge im Startbildschirm
|
||
export const MAP_ORDER = ['talschleife', 'seerunde', 'serpentinen', 'kreuzung', 'passstrasse'];
|