@mudlet/mudlet-web 0.5.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +1 -1
  2. package/dist-lib/analytics.d.ts +27 -0
  3. package/dist-lib/assets/fonts/Vera.woff2 +0 -0
  4. package/dist-lib/assets/fonts/VeraBI.woff2 +0 -0
  5. package/dist-lib/assets/fonts/VeraBd.woff2 +0 -0
  6. package/dist-lib/assets/fonts/VeraIt.woff2 +0 -0
  7. package/dist-lib/assets/mapParser.worker-DrVZ3wg_.js +9 -0
  8. package/dist-lib/backupImport-wt8r7JXI.js +233 -0
  9. package/dist-lib/branding.d.ts +111 -13
  10. package/dist-lib/db/sqliteClient.d.ts +195 -7
  11. package/dist-lib/hooks/useAutoReconnect.d.ts +61 -0
  12. package/dist-lib/hooks/useDocumentFullscreen.d.ts +23 -0
  13. package/dist-lib/hooks/useKeyboardShortcuts.d.ts +37 -0
  14. package/dist-lib/hooks/useRecordLastPlayed.d.ts +10 -0
  15. package/dist-lib/import/applyMudletProfile.d.ts +35 -9
  16. package/dist-lib/import/backupImport.d.ts +118 -0
  17. package/dist-lib/import/collectProfileExport.d.ts +33 -2
  18. package/dist-lib/import/defaultPackages.d.ts +57 -14
  19. package/dist-lib/import/defaults/CF-loader/CF-loader.mpackage +0 -0
  20. package/dist-lib/import/defaults/MedBootstrap/MedBootstrap.mpackage +0 -0
  21. package/dist-lib/import/defaults/deleteOldProfiles/deleteOldProfiles.mpackage +0 -0
  22. package/dist-lib/import/defaults/echo/echo.mpackage +0 -0
  23. package/dist-lib/import/defaults/enable-accessibility/enable-accessibility.mpackage +0 -0
  24. package/dist-lib/import/defaults/generic_mapper/generic_mapper.mpackage +0 -0
  25. package/dist-lib/import/defaults/gui-drop/gui-drop.mpackage +0 -0
  26. package/dist-lib/import/defaults/icesus-loader/icesus-loader.mpackage +0 -0
  27. package/dist-lib/import/defaults/mg-loader/mg-loader.mpackage +0 -0
  28. package/dist-lib/import/defaults/mudlet-base-ui/mudlet-base-ui.mpackage +0 -0
  29. package/dist-lib/import/defaults/mudlet-mapper.xml +1215 -389
  30. package/dist-lib/import/mudletControlChars.d.ts +14 -0
  31. package/dist-lib/import/mudletHost.d.ts +73 -10
  32. package/dist-lib/import/mudletLink.d.ts +53 -2
  33. package/dist-lib/import/mudletProfileExport.d.ts +38 -5
  34. package/dist-lib/import/mudletProfileImport.d.ts +110 -12
  35. package/dist-lib/import/mudletVariables.d.ts +4 -3
  36. package/dist-lib/import/mudletWriteback.d.ts +15 -1
  37. package/dist-lib/import/mudletXmlImport.d.ts +46 -0
  38. package/dist-lib/import/packageFonts.d.ts +33 -0
  39. package/dist-lib/import/packageInstaller.d.ts +111 -7
  40. package/dist-lib/import/qtProfileData.d.ts +10 -0
  41. package/dist-lib/import/remotePackageInstall.d.ts +23 -7
  42. package/dist-lib/import/zipEntries.d.ts +45 -0
  43. package/dist-lib/index.d.ts +7 -3
  44. package/dist-lib/index.js +50508 -29836
  45. package/dist-lib/logging/SessionLogger.d.ts +75 -15
  46. package/dist-lib/map/MapSelectionOverlay.d.ts +2 -2
  47. package/dist-lib/map/MapStore.d.ts +387 -60
  48. package/dist-lib/map/MudletHighlightOverlay.d.ts +2 -2
  49. package/dist-lib/map/{MudixMapReader.d.ts → MudletMapReader.d.ts} +2 -2
  50. package/dist-lib/map/areaGraph.d.ts +120 -0
  51. package/dist-lib/map/environmentKit.d.ts +36 -0
  52. package/dist-lib/map/labelPixmap.d.ts +90 -0
  53. package/dist-lib/map/mapImageExport.d.ts +36 -2
  54. package/dist-lib/map/mapStyle.d.ts +11 -0
  55. package/dist-lib/map/mapVersion.d.ts +66 -0
  56. package/dist-lib/map/pathfinding.d.ts +2 -2
  57. package/dist-lib/mud/CharLoginController.d.ts +230 -0
  58. package/dist-lib/mud/ItemIdSequence.d.ts +1 -1
  59. package/dist-lib/mud/MudSession.d.ts +279 -22
  60. package/dist-lib/mud/PatternEngine.d.ts +153 -14
  61. package/dist-lib/mud/aliases/AliasEngine.d.ts +55 -9
  62. package/dist-lib/mud/autoLogin.d.ts +59 -0
  63. package/dist-lib/mud/commandSplit.d.ts +9 -0
  64. package/dist-lib/mud/connection/LineAssembler.d.ts +38 -11
  65. package/dist-lib/mud/connection/MudClient.d.ts +291 -53
  66. package/dist-lib/mud/connection/SubnegotiationRepair.d.ts +24 -0
  67. package/dist-lib/mud/connection/TelnetNegotiator.d.ts +208 -68
  68. package/dist-lib/mud/connection/telnetDebug.d.ts +8 -8
  69. package/dist-lib/mud/events.d.ts +102 -23
  70. package/dist-lib/mud/games/bundledGames.d.ts +12 -7
  71. package/dist-lib/mud/games/gameLinks.d.ts +40 -0
  72. package/dist-lib/mud/games/icons/3Kingdoms.png +0 -0
  73. package/dist-lib/mud/games/icons/3Scapes.png +0 -0
  74. package/dist-lib/mud/games/icons/achaea.png +0 -0
  75. package/dist-lib/mud/games/icons/aetolia.png +0 -0
  76. package/dist-lib/mud/games/icons/banner_astaria.png +0 -0
  77. package/dist-lib/mud/games/icons/banner_darkmists.png +0 -0
  78. package/dist-lib/mud/games/icons/banner_dreamland.png +0 -0
  79. package/dist-lib/mud/games/icons/banner_petria.jpg +0 -0
  80. package/dist-lib/mud/games/icons/banner_ropmud.png +0 -0
  81. package/dist-lib/mud/games/icons/blackmud.png +0 -0
  82. package/dist-lib/mud/games/icons/carrionfields.png +0 -0
  83. package/dist-lib/mud/games/icons/dragonfire_icon.png +0 -0
  84. package/dist-lib/mud/games/icons/fed2-logo.png +0 -0
  85. package/dist-lib/mud/games/icons/icesus_480x120.png +0 -0
  86. package/dist-lib/mud/games/icons/imperian.png +0 -0
  87. package/dist-lib/mud/games/icons/infinity_480x120.png +0 -0
  88. package/dist-lib/mud/games/icons/lusternia.png +0 -0
  89. package/dist-lib/mud/games/icons/morgengrauen_480x120.png +0 -0
  90. package/dist-lib/mud/games/icons/mudlet-tutorial.png +0 -0
  91. package/dist-lib/mud/games/icons/phoenixmud_480x120.png +0 -0
  92. package/dist-lib/mud/games/icons/starmourn.png +0 -0
  93. package/dist-lib/mud/games/icons/voidmud_icon.png +0 -0
  94. package/dist-lib/mud/games/icons/wotmudicon.png +0 -0
  95. package/dist-lib/mud/games/websiteLinks.d.ts +21 -0
  96. package/dist-lib/mud/keybindings/KeyEngine.d.ts +110 -4
  97. package/dist-lib/mud/keybindings/browserReservedKeys.d.ts +2 -2
  98. package/dist-lib/mud/keybindings/keyEventTarget.d.ts +61 -0
  99. package/dist-lib/mud/keybindings/qtKeys.d.ts +91 -12
  100. package/dist-lib/mud/protocol/atcp.d.ts +19 -0
  101. package/dist-lib/mud/protocol/byteString.d.ts +24 -0
  102. package/dist-lib/mud/protocol/charLoginFlow.d.ts +243 -16
  103. package/dist-lib/mud/protocol/charset.d.ts +111 -46
  104. package/dist-lib/mud/protocol/codePages.d.ts +10 -0
  105. package/dist-lib/mud/protocol/constants.d.ts +17 -10
  106. package/dist-lib/mud/protocol/echo.d.ts +23 -27
  107. package/dist-lib/mud/protocol/gmcp.d.ts +36 -6
  108. package/dist-lib/mud/protocol/index.d.ts +3 -2
  109. package/dist-lib/mud/protocol/luaLiteral.d.ts +12 -0
  110. package/dist-lib/mud/protocol/mccp.d.ts +83 -1
  111. package/dist-lib/mud/protocol/mnes.d.ts +41 -58
  112. package/dist-lib/mud/protocol/msdp.d.ts +11 -4
  113. package/dist-lib/mud/protocol/msp.d.ts +3 -2
  114. package/dist-lib/mud/protocol/mssp.d.ts +9 -4
  115. package/dist-lib/mud/protocol/multiByte.d.ts +51 -0
  116. package/dist-lib/mud/protocol/multiByteEncode.d.ts +42 -0
  117. package/dist-lib/mud/protocol/mxp.d.ts +193 -14
  118. package/dist-lib/mud/protocol/oauthClientFlow.d.ts +128 -0
  119. package/dist-lib/mud/protocol/qtCodePages.generated.d.ts +1 -0
  120. package/dist-lib/mud/protocol/tlsCodes.d.ts +40 -3
  121. package/dist-lib/mud/replay/ReplayPlayer.d.ts +16 -1
  122. package/dist-lib/mud/replay/replayFormat.d.ts +29 -2
  123. package/dist-lib/mud/text/Console.d.ts +255 -49
  124. package/dist-lib/mud/text/FormatState.d.ts +137 -14
  125. package/dist-lib/mud/text/ansiEscapes.d.ts +32 -16
  126. package/dist-lib/mud/text/colorParsers.d.ts +14 -2
  127. package/dist-lib/mud/text/colorSpaceId.d.ts +2 -0
  128. package/dist-lib/mud/text/colors.d.ts +17 -10
  129. package/dist-lib/mud/text/controlCharacterMode.d.ts +1 -1
  130. package/dist-lib/mud/text/hyperlinkVisibility.d.ts +1 -1
  131. package/dist-lib/mud/text/lineBreak.d.ts +5 -0
  132. package/dist-lib/mud/text/serverWrap.d.ts +2 -2
  133. package/dist-lib/mud/text/textAnalyzer.d.ts +79 -0
  134. package/dist-lib/mud/text/unicodeRuns.d.ts +11 -0
  135. package/dist-lib/mud/text/unicodeTables.generated.d.ts +3 -0
  136. package/dist-lib/mud/text/wcwidth.d.ts +27 -22
  137. package/dist-lib/mud/timers/TimerEngine.d.ts +163 -11
  138. package/dist-lib/mud/timers/workerClock.d.ts +67 -0
  139. package/dist-lib/mud/triggers/TriggerEngine.d.ts +449 -47
  140. package/dist-lib/mud/triggers/legacyColorPatterns.d.ts +58 -0
  141. package/dist-lib/mud/triggers/pcre/Pcre2.d.ts +83 -1
  142. package/dist-lib/oauth-callback.html +94 -0
  143. package/dist-lib/pcre2Wasm.d.ts +54 -0
  144. package/dist-lib/pcre2Wasm.js +241 -0
  145. package/dist-lib/scripting/EngineHost.d.ts +45 -21
  146. package/dist-lib/scripting/IScriptingRuntime.d.ts +103 -27
  147. package/dist-lib/scripting/MxpFrameManager.d.ts +10 -1
  148. package/dist-lib/scripting/ScriptingAPI.d.ts +1030 -213
  149. package/dist-lib/scripting/ScriptingEngine.d.ts +727 -106
  150. package/dist-lib/scripting/StopwatchManager.d.ts +97 -36
  151. package/dist-lib/scripting/http/HttpService.d.ts +9 -1
  152. package/dist-lib/scripting/http/userUrl.d.ts +41 -0
  153. package/dist-lib/scripting/lua/LuaRuntime.d.ts +352 -77
  154. package/dist-lib/scripting/lua/argContracts.d.ts +71 -0
  155. package/dist-lib/scripting/lua/bindings/automation.d.ts +1 -1
  156. package/dist-lib/scripting/lua/bindings/commands.d.ts +13 -0
  157. package/dist-lib/scripting/lua/bindings/context.d.ts +20 -2
  158. package/dist-lib/scripting/lua/bindings/diagnostics.d.ts +1 -1
  159. package/dist-lib/scripting/lua/bindings/map.d.ts +1 -1
  160. package/dist-lib/scripting/lua/bindings/media.d.ts +2 -2
  161. package/dist-lib/scripting/lua/bindings/output.d.ts +1 -1
  162. package/dist-lib/scripting/lua/bindings/session.d.ts +2 -2
  163. package/dist-lib/scripting/lua/bindings/textEdit.d.ts +1 -1
  164. package/dist-lib/scripting/lua/bindings/userWindows.d.ts +1 -1
  165. package/dist-lib/scripting/lua/byteArmor.d.ts +36 -0
  166. package/dist-lib/scripting/lua/imageSize.d.ts +7 -0
  167. package/dist-lib/scripting/lua/rex.d.ts +4 -2
  168. package/dist-lib/scripting/lua/sqlRowEncoder.d.ts +6 -1
  169. package/dist-lib/scripting/lua/utf8CaseMap.d.ts +1 -0
  170. package/dist-lib/scripting/lua/utf8Patterns.d.ts +21 -1
  171. package/dist-lib/scripting/lua/wellFormedStrings.d.ts +40 -0
  172. package/dist-lib/scripting/lua/yajl.d.ts +43 -5
  173. package/dist-lib/scripting/profilesPresence.d.ts +20 -2
  174. package/dist-lib/scripting/vfs/ProfileVFS.d.ts +164 -10
  175. package/dist-lib/scripting/vfs/fsErrors.d.ts +28 -0
  176. package/dist-lib/scripting/vfs/idbCommit.d.ts +20 -0
  177. package/dist-lib/storage/appStore.d.ts +24 -9
  178. package/dist-lib/storage/hooks.d.ts +5 -1
  179. package/dist-lib/storage/index.d.ts +2 -2
  180. package/dist-lib/storage/mapStorage.d.ts +2 -0
  181. package/dist-lib/storage/persistentStorage.d.ts +3 -1
  182. package/dist-lib/storage/profileStorage.d.ts +15 -0
  183. package/dist-lib/storage/profileVfsData.d.ts +3 -3
  184. package/dist-lib/storage/schema.d.ts +512 -40
  185. package/dist-lib/storage/storageMigration.d.ts +82 -0
  186. package/dist-lib/storage-g5V1b-c1.js +2039 -0
  187. package/dist-lib/styles.css +1 -1
  188. package/dist-lib/ui/AboutModal.d.ts +1 -1
  189. package/dist-lib/ui/BrandLoginFields.d.ts +1 -1
  190. package/dist-lib/ui/BrowserSignInModal.d.ts +23 -0
  191. package/dist-lib/ui/CmdLineMenuRegistry.d.ts +14 -5
  192. package/dist-lib/ui/CommandBar.d.ts +8 -8
  193. package/dist-lib/ui/ConnectionFormModal.d.ts +20 -1
  194. package/dist-lib/ui/ConnectionScreen.d.ts +9 -1
  195. package/dist-lib/ui/GameLinkPrompt.d.ts +28 -0
  196. package/dist-lib/ui/HelpModal.d.ts +5 -1
  197. package/dist-lib/ui/MapSymbolUsageModal.d.ts +32 -0
  198. package/dist-lib/ui/MouseEventRegistry.d.ts +31 -4
  199. package/dist-lib/ui/NotepadModal.d.ts +22 -0
  200. package/dist-lib/ui/ResizableModal.d.ts +16 -1
  201. package/dist-lib/ui/SettingsModal.d.ts +10 -1
  202. package/dist-lib/ui/TlsAlertBanner.d.ts +3 -1
  203. package/dist-lib/ui/Toolbar.d.ts +22 -1
  204. package/dist-lib/ui/VaultManageButton.d.ts +10 -5
  205. package/dist-lib/ui/VaultModal.d.ts +3 -0
  206. package/dist-lib/ui/buttons/ButtonsBar.d.ts +3 -1
  207. package/dist-lib/ui/buttons/inactiveButtons.d.ts +25 -0
  208. package/dist-lib/ui/cmdline/CommandLineManager.d.ts +19 -0
  209. package/dist-lib/ui/cmdline/plainText.d.ts +16 -0
  210. package/dist-lib/ui/cmdline/subCommandLine.d.ts +78 -0
  211. package/dist-lib/ui/cmdline/useCmdLineSelection.d.ts +26 -0
  212. package/dist-lib/ui/cmdline/useSubCommandLineKeys.d.ts +33 -0
  213. package/dist-lib/ui/codemirror/lineParagraphMarks.d.ts +19 -0
  214. package/dist-lib/ui/codemirror/luaHover.d.ts +7 -0
  215. package/dist-lib/ui/codemirror/options.d.ts +33 -0
  216. package/dist-lib/ui/codemirror/theme.d.ts +29 -5
  217. package/dist-lib/ui/commands/addonCommands.d.ts +134 -0
  218. package/dist-lib/ui/commands/appShortcuts.d.ts +86 -0
  219. package/dist-lib/ui/commands/hostCommands.d.ts +88 -0
  220. package/dist-lib/ui/commands/keyMatch.d.ts +20 -0
  221. package/dist-lib/ui/commands/keySequence.d.ts +57 -0
  222. package/dist-lib/ui/components/ContextMenu.d.ts +21 -1
  223. package/dist-lib/ui/components/FontPicker.d.ts +5 -1
  224. package/dist-lib/ui/components/useModalFocus.d.ts +13 -1
  225. package/dist-lib/ui/helpTopics.d.ts +15 -0
  226. package/dist-lib/ui/labels/LabelManager.d.ts +172 -9
  227. package/dist-lib/ui/labels/LabelOverlay.d.ts +19 -0
  228. package/dist-lib/ui/labels/backgroundImageSize.d.ts +19 -0
  229. package/dist-lib/ui/labels/labelModel.d.ts +64 -0
  230. package/dist-lib/ui/labels/qColor.d.ts +13 -0
  231. package/dist-lib/ui/labels/qtCss.d.ts +12 -4
  232. package/dist-lib/ui/landmarks.d.ts +9 -0
  233. package/dist-lib/ui/launcher/ActionMenu.d.ts +19 -0
  234. package/dist-lib/ui/launcher/GameDetails.d.ts +21 -0
  235. package/dist-lib/ui/launcher/GameSection.d.ts +18 -0
  236. package/dist-lib/ui/launcher/LauncherSearch.d.ts +23 -0
  237. package/dist-lib/ui/launcher/ProfileArt.d.ts +20 -0
  238. package/dist-lib/ui/launcher/ProfileSection.d.ts +43 -0
  239. package/dist-lib/ui/launcher/launcherModel.d.ts +72 -0
  240. package/dist-lib/ui/layout/overlayLayerOrder.d.ts +2 -2
  241. package/dist-lib/ui/menu/MenuBar.d.ts +31 -0
  242. package/dist-lib/ui/menu/SplitButton.d.ts +39 -0
  243. package/dist-lib/ui/menu/anchoredPopup.d.ts +74 -0
  244. package/dist-lib/ui/menu/appMenus.d.ts +65 -0
  245. package/dist-lib/ui/menu/menuModel.d.ts +88 -0
  246. package/dist-lib/ui/menu/toolbarModel.d.ts +56 -0
  247. package/dist-lib/ui/notepad.d.ts +76 -0
  248. package/dist-lib/ui/openSignInPage.d.ts +14 -0
  249. package/dist-lib/ui/output/OutputContextMenu.d.ts +14 -1
  250. package/dist-lib/ui/output/OutputRenderer.d.ts +5 -0
  251. package/dist-lib/ui/output/OutputSearchBar.d.ts +5 -1
  252. package/dist-lib/ui/output/StickyOutputPanel.d.ts +17 -3
  253. package/dist-lib/ui/output/TextAnalyzerModal.d.ts +25 -0
  254. package/dist-lib/ui/output/caretMode.d.ts +3 -2
  255. package/dist-lib/ui/output/clearSplit.d.ts +11 -0
  256. package/dist-lib/ui/output/outputCopy.d.ts +41 -2
  257. package/dist-lib/ui/output/outputRows.d.ts +43 -0
  258. package/dist-lib/ui/output/selectionGesture.d.ts +35 -0
  259. package/dist-lib/ui/output/wordSelection.d.ts +33 -0
  260. package/dist-lib/ui/search/matcher.d.ts +14 -4
  261. package/dist-lib/ui/settings/SettingsShell.d.ts +91 -0
  262. package/dist-lib/ui/settings/ShortcutEditor.d.ts +13 -0
  263. package/dist-lib/ui/sound/SoundManager.d.ts +210 -44
  264. package/dist-lib/ui/sound/closedCaption.d.ts +4 -1
  265. package/dist-lib/ui/sound/mediaDirection.d.ts +35 -0
  266. package/dist-lib/ui/tabCompletion.d.ts +53 -0
  267. package/dist-lib/ui/textedit/TextEditManager.d.ts +11 -2
  268. package/dist-lib/ui/useOpenProfiles.d.ts +1 -1
  269. package/dist-lib/ui/video/VideoManager.d.ts +7 -0
  270. package/dist-lib/ui/windows/WindowManager.d.ts +452 -37
  271. package/dist-lib/ui/windows/panels/ScriptEditorPanel.d.ts +14 -0
  272. package/dist-lib/ui/windows/panels/ScriptSearch.d.ts +6 -1
  273. package/dist-lib/ui/windows/panels/TextPanel.d.ts +4 -1
  274. package/dist-lib/ui/windows/panels/VariablesView.d.ts +37 -7
  275. package/dist-lib/ui/windows/panels/WindowCmdLine.d.ts +4 -2
  276. package/dist-lib/ui/windows/panels/aliasLoop.d.ts +24 -0
  277. package/dist-lib/ui/windows/panels/editorClipboard.d.ts +45 -0
  278. package/dist-lib/ui/windows/panels/editorUndo.d.ts +79 -0
  279. package/dist-lib/ui/windows/panels/mapAreaChange.d.ts +10 -0
  280. package/dist-lib/utils/appShellCache.d.ts +25 -0
  281. package/dist-lib/utils/fontFamilyName.d.ts +13 -0
  282. package/dist-lib/utils/fontLoader.d.ts +31 -0
  283. package/dist-lib/utils/formatBytes.d.ts +4 -0
  284. package/dist-lib/utils/mustBeOneOf.d.ts +9 -0
  285. package/dist-lib/utils/profileLock.d.ts +1 -1
  286. package/dist-lib/utils/systemTheme.d.ts +24 -0
  287. package/dist-lib/utils/timeZone.d.ts +10 -0
  288. package/dist-lib/vault/CredentialVault.d.ts +51 -5
  289. package/dist-lib/vault/deviceUnlock.d.ts +47 -0
  290. package/dist-lib/vault/vaultAccess.d.ts +24 -2
  291. package/dist-lib/vault/vaultHelpTopic.d.ts +9 -0
  292. package/dist-lib/vault/vaultRecord.d.ts +33 -1
  293. package/dist-lib/vfs-sw.js +220 -11
  294. package/dist-lib/vite.d.ts +1 -1
  295. package/dist-lib/vite.js +66 -43
  296. package/package.json +23 -15
  297. package/dist-lib/assets/mapParser.worker-t-3rd1zR.js +0 -9
  298. package/dist-lib/mud/connection/PingTracker.d.ts +0 -41
  299. package/dist-lib/ui/BundledGameGrid.d.ts +0 -21
  300. package/dist-lib/ui/ConnectionGrid.d.ts +0 -21
  301. package/dist-lib/ui/bufferWords.d.ts +0 -38
@@ -366,9 +366,15 @@ disableTrigger"Mudlet Mapper prompt trigger"</script>
366
366
  <Trigger isActive="yes" isFolder="no" isTempTrigger="no" isMultiline="no" isPerlSlashGOption="no" isColorizerTrigger="no" isFilterTrigger="no" isSoundTrigger="no" isColorTrigger="no" isColorTriggerFg="no" isColorTriggerBg="no">
367
367
  <name>No wormhole</name>
368
368
  <script>if mmp.speedWalkDir and mmp.speedWalkDir[mmp.speedWalkCounter] and mmp.speedWalkDir[mmp.speedWalkCounter]=="worm warp" then
369
- mmp.echo("Missing wormhole detected, locking wormhole and trying new path...")
369
+ mmp.echo("Missing wormhole detected, removing wormhole and trying new path...")
370
370
 
371
- lockSpecialExit(mmp.currentroom, mmp.speedWalkPath[mmp.speedWalkCounter], "worm warp", true)
371
+ local wormholeDestination = mmp.speedWalkPath[mmp.speedWalkCounter]
372
+ removeSpecialExit(mmp.currentroom, "worm warp")
373
+ local reverseExits = getSpecialExits(wormholeDestination) or {}
374
+ local reverseExit = reverseExits[mmp.currentroom]
375
+ if reverseExit == "worm warp" or (type(reverseExit) == "table" and reverseExit["worm warp"]) then
376
+ removeSpecialExit(wormholeDestination, "worm warp")
377
+ end
372
378
 
373
379
  local destination = mmp.speedWalkPath[#mmp.speedWalkPath]
374
380
 
@@ -5640,76 +5646,39 @@ tempTimer(10, [[disableTrigger'Parse wholist']])</script>
5640
5646
  </Alias>
5641
5647
  <Alias isActive="yes" isFolder="no">
5642
5648
  <name>Add Room Mark</name>
5643
- <script>local tmp = getRoomUserData(1, "gotoMapping")
5644
- local maptable = {}
5645
-
5646
- if tmp ~= "" then
5647
- maptable = yajl.to_value(tmp)
5648
- end
5649
-
5649
+ <script>local visibility, first, second = matches[2], matches[3], matches[4]
5650
+ visibility = visibility ~= "" and visibility or "private"
5651
+ second = second ~= "" and second or nil
5650
5652
  local location, markname
5651
- if not matches[3] then
5652
- markname = matches[2]
5653
- location = mmp.currentroom
5654
- elseif tonumber(matches[2]) then
5655
- location = matches[2]; markname = matches[3]
5653
+ if not second then
5654
+ markname, location = first, mmp.currentroom
5655
+ elseif tonumber(first) then
5656
+ location, markname = first, second
5656
5657
  else
5657
- location = matches[3]; markname = matches[2]
5658
- end
5659
-
5660
- -- can't allow mark name to ne a number - yajl then generates a giant table of null's
5661
- if tonumber(markname) then
5662
- mmp.echo("The mark name can't be a number.") return
5658
+ location, markname = second, first
5663
5659
  end
5664
-
5665
- maptable[markname] = location
5666
- local tmp2 = yajl.to_string(maptable)
5667
-
5668
- if not mmp.roomexists(1) then
5669
- addRoom(1)
5670
- end
5671
-
5672
- setRoomUserData(1, "gotoMapping", tmp2)
5673
- mmp.echo(string.format("Room mark for '%s' set to room %s.", markname, location))</script>
5660
+ mmp.setRoomMark(markname, location, visibility)</script>
5674
5661
  <command></command>
5675
5662
  <packageName></packageName>
5676
- <regex>^room mark (\w+)(?: (\w+))?$</regex>
5663
+ <regex>^room mark (?:(private|public) )?(\w+)(?: (\w+))?$</regex>
5677
5664
  </Alias>
5678
5665
  <Alias isActive="yes" isFolder="no">
5679
5666
  <name>Remove Room Mark</name>
5680
- <script>local tmp= getRoomUserData(1, "gotoMapping")
5681
- if tmp~="" then
5682
- local maptable=yajl.to_value(tmp)
5683
- if not maptable[matches[2]] then mmp.echo("Don't have such a mark in the db.") return end
5684
-
5685
- maptable[matches[2]]=nil
5686
- local tmp2=yajl.to_string(maptable)
5687
- setRoomUserData(1, "gotoMapping", tmp2)
5688
- mmp.echo("Removed the "..matches[2].." mark.")
5689
- else
5690
- mmp.echo("We don't have any marks stored anyway.")
5691
- end</script>
5667
+ <script>mmp.removeRoomMark(matches[3], matches[2] ~= "" and matches[2] or "private")</script>
5692
5668
  <command></command>
5693
5669
  <packageName></packageName>
5694
- <regex>^room unmark (\w+)$</regex>
5670
+ <regex>^room unmark (?:(private|public) )?(\w+)$</regex>
5695
5671
  </Alias>
5696
5672
  <Alias isActive="yes" isFolder="no">
5697
5673
  <name>Display Room Marks</name>
5698
- <script>local tmp= getRoomUserData(1, "gotoMapping")
5699
- if tmp~="" then
5700
- local maptable = yajl.to_value(tmp) or {}
5701
- local sortedkeys = {}
5702
- for k in pairs(maptable) do sortedkeys[#sortedkeys+1] = k end
5703
- table.sort(sortedkeys)
5704
-
5705
- mmp.echo("Known marks in this map:")
5706
- if next(maptable) then
5707
- for i = 1, #sortedkeys do echo(string.format(" %-21s %s\n", tostring(sortedkeys[i]), tostring(maptable[sortedkeys[i]]))) end
5708
- else
5709
- echo(" (none)\n")
5710
- end
5674
+ <script>local marks = mmp.getAllRoomMarks()
5675
+ mmp.echo("Known marks in this map:")
5676
+ if #marks == 0 then
5677
+ echo(" (none)\n")
5711
5678
  else
5712
- mmp.echo("No marks are recorded in this map.")
5679
+ for _, mark in ipairs(marks) do
5680
+ echo(string.format(" %-21s %s (%s)\n", tostring(mark.name), tostring(mark.location), mark.visibility))
5681
+ end
5713
5682
  end</script>
5714
5683
  <command></command>
5715
5684
  <packageName></packageName>
@@ -5857,17 +5826,19 @@ end
5857
5826
  if not gmcp.Room.Info.area:find("(manse)") then
5858
5827
  mmp.echo("You are not in a manse")
5859
5828
  else
5860
- if searchRoom(gmcp.Room.Info.num):sub(0,11)=="searchRoom:" then
5861
- addRoom(gmcp.Room.Info.num)
5862
- end
5863
- if getRoomArea(gmcp.Room.Info.num)==-1 then
5864
- setRoomArea(gmcp.Room.Info.num,addAreaName(manse:title().." Manse"))
5865
- end
5866
- setRoomChar(gmcp.Room.Info.num,"A")
5867
- addSpecialExit(portalRooms[city],gmcp.Room.Info.num,"Portal enter "..manse)
5868
- addSpecialExit(portalRooms["aetherplex"],gmcp.Room.Info.num,"Portal enter "..manse)
5869
- addSpecialExit(gmcp.Room.Info.num,portalRooms["aetherplex"],"portal exit aetherplex")
5870
- addSpecialExit(gmcp.Room.Info.num,portalRooms[city],"portal exit "..city)
5829
+ mmp.localMapChanges(function()
5830
+ if searchRoom(gmcp.Room.Info.num):sub(0,11)=="searchRoom:" then
5831
+ addRoom(gmcp.Room.Info.num)
5832
+ end
5833
+ if getRoomArea(gmcp.Room.Info.num)==-1 then
5834
+ setRoomArea(gmcp.Room.Info.num,addAreaName(manse:title().." Manse"))
5835
+ end
5836
+ setRoomChar(gmcp.Room.Info.num,"A")
5837
+ addSpecialExit(portalRooms[city],gmcp.Room.Info.num,"Portal enter "..manse)
5838
+ addSpecialExit(portalRooms["aetherplex"],gmcp.Room.Info.num,"Portal enter "..manse)
5839
+ addSpecialExit(gmcp.Room.Info.num,portalRooms["aetherplex"],"portal exit aetherplex")
5840
+ addSpecialExit(gmcp.Room.Info.num,portalRooms[city],"portal exit "..city)
5841
+ end)
5871
5842
  mmp.echo(city:title().." has been linked to the Aetherplex through "..manse:title())
5872
5843
  end</script>
5873
5844
  <command></command>
@@ -5910,6 +5881,17 @@ mmp =
5910
5881
  ferry_rooms = {},
5911
5882
  }
5912
5883
  mmp.speedWalkWatch = createStopWatch()
5884
+
5885
+ function mmp.roomDetails()
5886
+ local info = gmcp and gmcp.Room and gmcp.Room.Info
5887
+ return info and type(info.details) == "table" and info.details or nil
5888
+ end
5889
+
5890
+ function mmp.hasRoomDetail(detail)
5891
+ local details = mmp.roomDetails()
5892
+ return details and table.contains(details, detail) or false
5893
+ end
5894
+
5913
5895
  -- speedWalkPath and speedWalkDir populated by Mudlet from getPath() and gotoRoom()
5914
5896
  speedWalkPath = speedWalkPath or {}
5915
5897
  speedWalkDir = speedWalkDir or {}
@@ -5927,7 +5909,7 @@ mmp.lagtable =
5927
5909
  [4] = {description = "Bad. Terrible. Terribad.", time = 5},
5928
5910
  [5] = {description = "Carrier Pigeon", time = 10},
5929
5911
  }
5930
- local newversion = "25.5.4"
5912
+ local newversion = "26.10.1"
5931
5913
  if mmp.version and mmp.version ~= newversion then
5932
5914
  if not mmp.game then
5933
5915
  mmp.echo("Mapper script updated - Thanks! I don't know what game are you connected to, though - so please reconnect, if you could.")
@@ -5938,15 +5920,24 @@ end
5938
5920
  mmp.version = newversion
5939
5921
 
5940
5922
  function mmp.startup()
5941
- if not mmp.firstRun then
5942
- return
5943
- end
5923
+ local firstRun = mmp.firstRun
5924
+ local previousSettings = mmp.settings
5925
+ local previousValues = previousSettings and previousSettings.getAllOptions and previousSettings:getAllOptions() or nil
5926
+ local customOptions = previousSettings and rawget(previousSettings, "_customOptions") or nil
5944
5927
  local private_settings = {}
5928
+ local crowdmapGames = {achaea = true, starmourn = true, lusternia = true, stickmud = true, ashyria = true, asteria = true, imperian = true, aetolia = true}
5945
5929
 
5946
5930
  --General settings
5947
5931
 
5948
5932
  private_settings["echocolour"] = mmp.createOption("cyan", mmp.changeEchoColour, {"string"}, "Set the color for room number echos?", function(newSetting) return color_table[newSetting] ~= nil end)
5949
- private_settings["crowdmap"] = mmp.createOption(false, mmp.changeMapSource, {"boolean"}, "Use a crowd-sourced map instead of games default?", nil, {achaea = true, starmourn = true, lusternia = true, stickmud = true, ashyria = true, asteria = true, imperian = true, aetolia = true})
5933
+ private_settings["mapsource"] = mmp.createOption("game", mmp.changeMapSource, {"string"}, "Map source: game, published, or service.", function(value) return value == "game" or value == "published" or value == "service" end, crowdmapGames)
5934
+ private_settings["crowdmapservicesend"] = mmp.createOption(false, function(name, option)
5935
+ mmp.changeBoolFunc(name, option)
5936
+ if option and mmp.crowdmapService then mmp.crowdmapService.sendNext() end
5937
+ end, {"boolean"}, "Send local map changes to the service?", nil, crowdmapGames, function() return mmp.settings.mapsource == "service" end)
5938
+ private_settings["gmcpmapupdates"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Apply safe GMCP room information to existing map rooms, even when mapping mode is off?")
5939
+ private_settings["crowdmapserviceurl"] = mmp.createOption("https://&lt;game&gt;.mudmaps.community", mmp.changeCrowdmapServiceMapOption, {"string"}, "Crowdmap service endpoint URL. Use &lt;game&gt; to derive the subdomain from the connected game.", function(value) return value:match("^https?://") ~= nil end, crowdmapGames, function() return mmp.settings.mapsource == "service" end)
5940
+ private_settings["crowdmapservicereports"] = mmp.createOption(2, mmp.changeCrowdmapServiceMapOption, {"number"}, "Required independent reports for changes sourced from the service.", function(value) return value >= 0 and value == math.floor(value) end, crowdmapGames, function() return mmp.settings.mapsource == "service" end)
5950
5941
  private_settings["showcmds"] = mmp.createOption(true, mmp.changeBoolFunc, {"boolean"}, "Show walking commands?")
5951
5942
  private_settings["laglevel"] = mmp.createOption(1, mmp.changeLaglevel, {"number"}, "How laggy is your connection, (fast 1&lt;-&gt;5 slow)?", mmp.verifyLaglevel)
5952
5943
  private_settings["slowwalk"] = mmp.createOption(false, mmp.setSlowWalk, {"boolean"}, "Walk slowly instead of as quick as possible?")
@@ -5974,9 +5965,13 @@ function mmp.startup()
5974
5965
  private_settings["removewings"] = mmp.createOption(true, mmp.setWingsRemoval, {"boolean"}, "Remove Wings after using them?", nil, {achaea = true, imperian = true})
5975
5966
  private_settings["harness"] = mmp.createOption(false, mmp.setHarness, {"boolean"}, "Use Stratospheric Harness?", nil, {achaea = true})
5976
5967
  private_settings["duantahar"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Chenubian Wings?", nil, {achaea = true})
5968
+ private_settings["duantaharid"] = mmp.createOption("", nil, {"string", "number"}, "Item ID of your Chenubian Wings, if you own more than one pair of wings?", nil, {achaea = true})
5977
5969
  private_settings["duanatharan"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Atavian Wings?", nil, {achaea = true, aetolia = true})
5970
+ private_settings["duanatharanid"] = mmp.createOption("", nil, {"string", "number"}, "Item ID of your Atavian Wings, if you own more than one pair of wings?", nil, {achaea = true, aetolia = true})
5978
5971
  private_settings["duanathar"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Eagle Wings?", nil, {achaea = true, aetolia = true})
5972
+ private_settings["duanatharid"] = mmp.createOption("", nil, {"string", "number"}, "Item ID of your Eagle Wings, if you own more than one pair of wings?", nil, {achaea = true, aetolia = true})
5979
5973
  private_settings["duanatharic"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Island Wings?", nil, {achaea = true})
5974
+ private_settings["duanatharicid"] = mmp.createOption("", nil, {"string", "number"}, "Item ID of your Island Wings, if you own more than one pair of wings?", nil, {achaea = true})
5980
5975
  private_settings["soar"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Aero Soar?", nil, {achaea = true})
5981
5976
  private_settings["shackle"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Take off shackle?", nil, {achaea = true})
5982
5977
  private_settings["universe"] = mmp.createOption(false, mmp.changeBoolFunc, {"boolean"}, "Use Universe Tarot?", nil, {achaea = true})
@@ -6064,9 +6059,15 @@ function mmp.startup()
6064
6059
 
6065
6060
 
6066
6061
 
6062
+ if previousValues and mmp.migrateOptions then mmp.migrateOptions(previousValues) end
6067
6063
  mmp.settings = mmp.createOptionsTable(private_settings)
6064
+ for name, value in pairs(previousValues or {}) do
6065
+ if private_settings[name] then mmp.settings:setOption(name, value, true) end
6066
+ end
6067
+ for name, option in pairs(customOptions or {}) do
6068
+ if not private_settings[name] then mmp.settings[name] = option end
6069
+ end
6068
6070
  mmp.settings.disp = mmp.echo
6069
- mmp.game = false
6070
6071
  mmp.settings.dispOption =
6071
6072
  function(opt, val)
6072
6073
  cecho(
@@ -6084,6 +6085,8 @@ function mmp.startup()
6084
6085
  function()
6085
6086
  mmp.echo("Please use the mconfig alias to set options!")
6086
6087
  end
6088
+ if not firstRun then return end
6089
+ mmp.game = false
6087
6090
  raiseEvent("mmp areas changed")
6088
6091
  mmp.firstRun = false
6089
6092
  mmp.echon("Mudlet Mapper script for IREs (" .. tostring(mmp.version) .. ") loaded! (")
@@ -6097,6 +6100,673 @@ function mmp.startup()
6097
6100
  end</script>
6098
6101
  <eventHandlerList />
6099
6102
  </Script>
6103
+ <Script isActive="yes" isFolder="no">
6104
+ <name>Crowdmap service</name>
6105
+ <packageName></packageName>
6106
+ <script>mmp.crowdmapService = mmp.crowdmapService or {}
6107
+ local service = mmp.crowdmapService
6108
+ service.pendingChanges = service.pendingChanges or {}
6109
+ service.maxAttempts = 6
6110
+ service.maxPersistSeconds = 24 * 60 * 60
6111
+ service.unsentChanges = service.unsentChanges or 0
6112
+ service.retryPending = false
6113
+
6114
+ local function endpoint(path)
6115
+ local url = mmp.settings.crowdmapserviceurl
6116
+ if url:find("&lt;game&gt;", 1, true) then
6117
+ if type(mmp.game) ~= "string" then error("Cannot derive the crowdmap service URL before the game is known.") end
6118
+ url = url:gsub("&lt;game&gt;", mmp.game)
6119
+ end
6120
+ return url:gsub("/+$", "") .. path
6121
+ end
6122
+
6123
+ function service.baseUrl()
6124
+ local ok, url = pcall(endpoint, "")
6125
+ return ok and url or nil
6126
+ end
6127
+
6128
+ function service.reporter()
6129
+ if gmcp and gmcp.Char and gmcp.Char.Status and gmcp.Char.Status.name and gmcp.Char.Status.name ~= "" then
6130
+ return gmcp.Char.Status.name
6131
+ end
6132
+ end
6133
+
6134
+ local function queryValue(value)
6135
+ return tostring(value):gsub("([^%w%-_%.~])", function(character)
6136
+ return string.format("%%%02X", string.byte(character))
6137
+ end)
6138
+ end
6139
+
6140
+ function service.sendNext()
6141
+ if service.inFlight or service.retryPending or #service.pendingChanges == 0 then return end
6142
+ -- Retain queued reports while sending is disabled or another map source is
6143
+ -- selected; a later submission or enabling service sending retries them.
6144
+ if service.suspended or not mmp.settings or mmp.settings.mapsource ~= "service" or not mmp.settings.crowdmapservicesend then return end
6145
+ local reporterName = service.reporter()
6146
+ if not reporterName then return end
6147
+ local entry = service.pendingChanges[1]
6148
+ -- Normalize entries that were queued by an earlier script load.
6149
+ if not entry.change then
6150
+ entry = { change = entry, attempts = 0 }
6151
+ service.pendingChanges[1] = entry
6152
+ end
6153
+ entry.attempts = (entry.attempts or 0) + 1
6154
+ local change = entry.change
6155
+ change.reporter = reporterName
6156
+ service.requestId = (service.requestId or 0) + 1
6157
+ local requestId = service.requestId
6158
+ local ok, url = pcall(endpoint, "/change?request=" .. requestId)
6159
+ if not ok then
6160
+ table.remove(service.pendingChanges, 1)
6161
+ service.handleFailure(entry, "Could not send map change to crowdmap service: " .. url)
6162
+ return
6163
+ end
6164
+ local posted, requestUrl = postHTTP(yajl.to_string(change), url, { ["Content-Type"] = "application/json" })
6165
+ if not posted then
6166
+ table.remove(service.pendingChanges, 1)
6167
+ service.handleFailure(entry, "Could not send map change to crowdmap service.")
6168
+ return
6169
+ end
6170
+ table.remove(service.pendingChanges, 1)
6171
+ service.inFlight, service.requestUrl = true, requestUrl or url
6172
+ service.inFlightEntry = entry
6173
+ tempTimer(30, function()
6174
+ if service.inFlight and service.requestId == requestId then
6175
+ local timedOutEntry = service.inFlightEntry
6176
+ service.inFlight, service.requestUrl, service.inFlightEntry = false, nil, nil
6177
+ service.handleFailure(timedOutEntry, "Timed out sending map change to crowdmap service.")
6178
+ end
6179
+ end)
6180
+ end
6181
+
6182
+ function service.submit(change)
6183
+ if mmp.settings.mapsource ~= "service" or not mmp.settings.crowdmapservicesend or service.suspended then return end
6184
+ table.insert(service.pendingChanges, { change = change, attempts = 0, expiresAt = os.time() + service.maxPersistSeconds })
6185
+ service.sendNext()
6186
+ end
6187
+
6188
+ function service.pendingForSave()
6189
+ local result = {}
6190
+ local function append(entry)
6191
+ if type(entry) ~= "table" or type(entry.change) ~= "table" then return end
6192
+ entry.expiresAt = tonumber(entry.expiresAt) or (os.time() + service.maxPersistSeconds)
6193
+ table.insert(result, {
6194
+ change = entry.change,
6195
+ attempts = tonumber(entry.attempts) or 0,
6196
+ expiresAt = entry.expiresAt
6197
+ })
6198
+ end
6199
+ append(service.inFlightEntry)
6200
+ for _, entry in ipairs(service.pendingChanges) do append(entry) end
6201
+ return result
6202
+ end
6203
+
6204
+ function service.restorePendingChanges(entries)
6205
+ if type(entries) ~= "table" or service.inFlightEntry or #service.pendingChanges &gt; 0 then return end
6206
+ local now = os.time()
6207
+ for _, entry in ipairs(entries) do
6208
+ local attempts = type(entry) == "table" and tonumber(entry.attempts) or nil
6209
+ local expiresAt = type(entry) == "table" and tonumber(entry.expiresAt) or nil
6210
+ if type(entry) == "table" and type(entry.change) == "table" and attempts and attempts &gt;= 0
6211
+ and attempts &lt; service.maxAttempts and expiresAt and expiresAt &gt; now then
6212
+ table.insert(service.pendingChanges, { change = entry.change, attempts = attempts, expiresAt = expiresAt })
6213
+ elseif type(entry) == "table" and type(entry.change) == "table" then
6214
+ service.unsentChanges = service.unsentChanges + 1
6215
+ end
6216
+ end
6217
+ end
6218
+
6219
+ function service.promoteHashOnlyRoom(roomNumber)
6220
+ service.submit({ type = "create-room", roomNumber = tonumber(roomNumber) })
6221
+ local hash = getRoomHashByID(roomNumber)
6222
+ if hash and hash ~= "" then
6223
+ service.submit({ type = "set-room-hash", roomNumber = tonumber(roomNumber), hash = hash })
6224
+ end
6225
+ end
6226
+
6227
+ function service.localOnly(callback)
6228
+ local wasSuspended = service.suspended
6229
+ service.suspended = true
6230
+ local result = {pcall(callback)}
6231
+ service.suspended = wasSuspended
6232
+ if not result[1] then error(result[2]) end
6233
+ return unpack(result, 2)
6234
+ end
6235
+
6236
+ mmp.localMapChanges = service.localOnly
6237
+
6238
+ service.temporarySpecialExits = service.temporarySpecialExits or {}
6239
+
6240
+ local function specialExitKey(destination, command)
6241
+ return tostring(destination) .. "\0" .. tostring(command)
6242
+ end
6243
+
6244
+ function service.trackTemporarySpecialExit(roomNumber, destination, command)
6245
+ local roomKey = tostring(roomNumber)
6246
+ service.temporarySpecialExits[roomKey] = service.temporarySpecialExits[roomKey] or {}
6247
+ service.temporarySpecialExits[roomKey][specialExitKey(destination, command)] = true
6248
+ end
6249
+
6250
+ function service.isTemporarySpecialExit(roomNumber, destination, command)
6251
+ local roomExits = service.temporarySpecialExits[tostring(roomNumber)]
6252
+ return roomExits and roomExits[specialExitKey(destination, command)] or false
6253
+ end
6254
+
6255
+ function service.untrackTemporarySpecialExit(roomNumber, destination, command)
6256
+ local roomKey = tostring(roomNumber)
6257
+ local roomExits = service.temporarySpecialExits[roomKey]
6258
+ if not roomExits then return end
6259
+ roomExits[specialExitKey(destination, command)] = nil
6260
+ if not next(roomExits) then service.temporarySpecialExits[roomKey] = nil end
6261
+ end
6262
+
6263
+ function service.untrackTemporarySpecialExitCommand(roomNumber, command)
6264
+ local roomKey = tostring(roomNumber)
6265
+ local roomExits = service.temporarySpecialExits[roomKey]
6266
+ if not roomExits then return false end
6267
+ local suffix = "\0" .. tostring(command)
6268
+ local found = false
6269
+ for key in pairs(roomExits) do
6270
+ if key:sub(-#suffix) == suffix then
6271
+ roomExits[key] = nil
6272
+ found = true
6273
+ end
6274
+ end
6275
+ if not next(roomExits) then service.temporarySpecialExits[roomKey] = nil end
6276
+ return found
6277
+ end
6278
+
6279
+ function service.versionUrl()
6280
+ local reporterName = service.reporter()
6281
+ if not reporterName then return end
6282
+ return endpoint("/map/version?timesSeen=" .. mmp.settings.crowdmapservicereports .. "&amp;reporter=" .. queryValue(reporterName))
6283
+ end
6284
+
6285
+ function service.mapUrl()
6286
+ local reporterName = service.reporter()
6287
+ if not reporterName then return end
6288
+ return endpoint("/map?format=binary&amp;timesSeen=" .. mmp.settings.crowdmapservicereports .. "&amp;reporter=" .. queryValue(reporterName))
6289
+ end
6290
+
6291
+ registerNamedEventHandler("mapper", "crowdmapServiceReporterReady", "gmcp.Char.Status", function()
6292
+ local reporterName = service.reporter()
6293
+ if reporterName and reporterName ~= service.lastReporter then
6294
+ service.lastReporter = reporterName
6295
+ service.sendNext()
6296
+ if service.waitingForReporter and mmp.checkforupdate then
6297
+ service.waitingForReporter = nil
6298
+ mmp.checkforupdate()
6299
+ end
6300
+ end
6301
+ end)
6302
+ registerNamedEventHandler("mapper", "crowdmapServiceReporterReset", "sysConnectionEvent", function()
6303
+ service.lastReporter = nil
6304
+ end)
6305
+
6306
+ local directions = {
6307
+ [1] = "north", [2] = "northeast", [3] = "northwest", [4] = "east",
6308
+ [5] = "west", [6] = "south", [7] = "southeast", [8] = "southwest",
6309
+ [9] = "up", [10] = "down", [11] = "in", [12] = "out",
6310
+ n = "north", ne = "northeast", nw = "northwest", e = "east",
6311
+ w = "west", s = "south", se = "southeast", sw = "southwest",
6312
+ u = "up", d = "down", north = "north", northeast = "northeast",
6313
+ northwest = "northwest", east = "east", west = "west", south = "south",
6314
+ southeast = "southeast", southwest = "southwest", up = "up", down = "down",
6315
+ ["in"] = "in", out = "out"
6316
+ }
6317
+
6318
+ local function pack(...)
6319
+ return { n = select("#", ...), ... }
6320
+ end
6321
+
6322
+ local function unpackResults(results)
6323
+ return unpack(results, 1, results.n)
6324
+ end
6325
+
6326
+ do
6327
+ -- Keep the native functions across module reloads and always rebuild wrappers
6328
+ -- against them, never against wrappers installed by a previous load.
6329
+ if not service.original then
6330
+ service.original = {
6331
+ addAreaName = addAreaName, setAreaName = setAreaName, deleteArea = deleteArea,
6332
+ addRoom = addRoom, deleteRoom = deleteRoom, setRoomName = setRoomName,
6333
+ setRoomCoordinates = setRoomCoordinates, setRoomArea = setRoomArea,
6334
+ setRoomWeight = setRoomWeight, setRoomChar = setRoomChar, setRoomEnv = setRoomEnv,
6335
+ setRoomIDbyHash = setRoomIDbyHash,
6336
+ setRoomUserData = setRoomUserData, clearRoomUserDataItem = clearRoomUserDataItem,
6337
+ addSpecialExit = addSpecialExit, removeSpecialExit = removeSpecialExit,
6338
+ clearSpecialExits = clearSpecialExits, lockSpecialExit = lockSpecialExit,
6339
+ setExitWeight = setExitWeight, setDoor = setDoor,
6340
+ setMapUserData = setMapUserData, clearMapUserDataItem = clearMapUserDataItem,
6341
+ createMapLabel = createMapLabel, deleteMapLabel = deleteMapLabel
6342
+ }
6343
+ end
6344
+ service.wrappersInstalled = true
6345
+ local original = service.original
6346
+
6347
+ function addAreaName(name, ...)
6348
+ local results = pack(original.addAreaName(name, ...))
6349
+ local areaId = results[1]
6350
+ if areaId then service.submit({ type = "create-area", areaId = areaId, name = name }) end
6351
+ return unpackResults(results)
6352
+ end
6353
+
6354
+ function setAreaName(areaId, name, ...)
6355
+ local results = pack(original.setAreaName(areaId, name, ...))
6356
+ if results[1] then service.submit({ type = "rename-area", areaId = areaId, name = name }) end
6357
+ return unpackResults(results)
6358
+ end
6359
+
6360
+ function deleteArea(areaId, ...)
6361
+ local results = pack(original.deleteArea(areaId, ...))
6362
+ if results[1] then service.submit({ type = "delete-area", areaId = tonumber(areaId) }) end
6363
+ return unpackResults(results)
6364
+ end
6365
+
6366
+ function addRoom(roomNumber, ...)
6367
+ local results = pack(original.addRoom(roomNumber, ...))
6368
+ if results[1] then
6369
+ service.submit({ type = "create-room", roomNumber = tonumber(roomNumber) })
6370
+ local areaId = select(1, ...)
6371
+ if areaId ~= nil then
6372
+ service.submit({ type = "set-room-area", roomNumber = tonumber(roomNumber), areaId = tonumber(areaId) })
6373
+ end
6374
+ end
6375
+ return unpackResults(results)
6376
+ end
6377
+
6378
+ function deleteRoom(roomNumber, ...)
6379
+ local results = pack(original.deleteRoom(roomNumber, ...))
6380
+ if results[1] then service.submit({ type = "delete-room", roomNumber = tonumber(roomNumber) }) end
6381
+ return unpackResults(results)
6382
+ end
6383
+
6384
+ function setRoomName(roomNumber, name, ...)
6385
+ local results = pack(original.setRoomName(roomNumber, name, ...))
6386
+ if results[1] then service.submit({ type = "room-name", roomNumber = tonumber(roomNumber), name = name }) end
6387
+ return unpackResults(results)
6388
+ end
6389
+
6390
+ function setRoomCoordinates(roomNumber, x, y, z, ...)
6391
+ local results = pack(original.setRoomCoordinates(roomNumber, x, y, z, ...))
6392
+ if results[1] then service.submit({ type = "set-room-coordinates", roomNumber = tonumber(roomNumber), x = x, y = y, z = z }) end
6393
+ return unpackResults(results)
6394
+ end
6395
+
6396
+ function setRoomArea(roomNumber, areaId, ...)
6397
+ local results = pack(original.setRoomArea(roomNumber, areaId, ...))
6398
+ if results[1] then service.submit({ type = "set-room-area", roomNumber = tonumber(roomNumber), areaId = tonumber(areaId) }) end
6399
+ return unpackResults(results)
6400
+ end
6401
+
6402
+ function setRoomWeight(roomNumber, weight, ...)
6403
+ local results = pack(original.setRoomWeight(roomNumber, weight, ...))
6404
+ if results[1] then service.submit({ type = "set-room-weight", roomNumber = tonumber(roomNumber), weight = weight }) end
6405
+ return unpackResults(results)
6406
+ end
6407
+
6408
+ function setRoomChar(roomNumber, symbol, ...)
6409
+ local results = pack(original.setRoomChar(roomNumber, symbol, ...))
6410
+ if results[1] then service.submit({ type = "set-room-symbol", roomNumber = tonumber(roomNumber), symbol = symbol }) end
6411
+ return unpackResults(results)
6412
+ end
6413
+
6414
+ function setRoomEnv(roomNumber, environmentId, ...)
6415
+ local results = pack(original.setRoomEnv(roomNumber, environmentId, ...))
6416
+ if results[1] then service.submit({ type = "set-room-environment", roomNumber = tonumber(roomNumber), environmentId = environmentId }) end
6417
+ return unpackResults(results)
6418
+ end
6419
+
6420
+ function setRoomIDbyHash(roomNumber, hash, ...)
6421
+ local results = pack(original.setRoomIDbyHash(roomNumber, hash, ...))
6422
+ local applied = results[1]
6423
+ -- Older Mudlet versions return no values on success.
6424
+ if results.n == 0 then
6425
+ applied = tonumber(roomNumber) and tonumber(roomNumber) &gt; 0
6426
+ and getRoomIDbyHash(hash) == tonumber(roomNumber)
6427
+ end
6428
+ if applied then
6429
+ service.submit({ type = "set-room-hash", roomNumber = tonumber(roomNumber), hash = hash })
6430
+ end
6431
+ return unpackResults(results)
6432
+ end
6433
+
6434
+ function setRoomUserData(roomNumber, key, value, ...)
6435
+ local results = pack(original.setRoomUserData(roomNumber, key, value, ...))
6436
+ -- hashonly is local bookkeeping for a placeholder room created to bind a
6437
+ -- hash before the room itself is known; it is not map data.
6438
+ if results[1] and key ~= "hashonly" then service.submit({ type = "modify-room-user-data", roomNumber = tonumber(roomNumber), key = key, value = value }) end
6439
+ return unpackResults(results)
6440
+ end
6441
+
6442
+ function clearRoomUserDataItem(roomNumber, key, ...)
6443
+ local results = pack(original.clearRoomUserDataItem(roomNumber, key, ...))
6444
+ if results[1] and key ~= "hashonly" then service.submit({ type = "delete-room-user-data", roomNumber = tonumber(roomNumber), key = key }) end
6445
+ return unpackResults(results)
6446
+ end
6447
+
6448
+ function addSpecialExit(roomNumber, destination, command, ...)
6449
+ local results = pack(original.addSpecialExit(roomNumber, destination, command, ...))
6450
+ if results[1] and command ~= mmp.whirlString then service.submit({ type = "modify-special-exit", roomNumber = tonumber(roomNumber), destination = tonumber(destination), exitCommand = command }) end
6451
+ return unpackResults(results)
6452
+ end
6453
+
6454
+ function removeSpecialExit(roomNumber, command, ...)
6455
+ local results = pack(original.removeSpecialExit(roomNumber, command, ...))
6456
+ local wasTemporary = service.untrackTemporarySpecialExitCommand(roomNumber, command)
6457
+ if results[1] and command ~= mmp.whirlString and not wasTemporary then
6458
+ service.submit({ type = "delete-special-exit", roomNumber = tonumber(roomNumber), exitCommand = command })
6459
+ end
6460
+ return unpackResults(results)
6461
+ end
6462
+
6463
+ function clearSpecialExits(roomNumber, ...)
6464
+ local exits = getSpecialExits(roomNumber) or {}
6465
+ local results = pack(original.clearSpecialExits(roomNumber, ...))
6466
+ if not next(getSpecialExits(roomNumber) or {}) then
6467
+ local function submitExit(destination, command)
6468
+ if command ~= mmp.whirlString and not service.isTemporarySpecialExit(roomNumber, destination, command) then
6469
+ service.submit({ type = "delete-special-exit", roomNumber = tonumber(roomNumber), exitCommand = command })
6470
+ end
6471
+ end
6472
+ for destination, commands in pairs(exits) do
6473
+ if type(commands) == "table" then
6474
+ for command in pairs(commands) do submitExit(destination, command) end
6475
+ else
6476
+ submitExit(destination, commands)
6477
+ end
6478
+ end
6479
+ service.temporarySpecialExits[tostring(roomNumber)] = nil
6480
+ end
6481
+ return unpackResults(results)
6482
+ end
6483
+
6484
+ function lockSpecialExit(roomNumber, destination, command, locked, ...)
6485
+ local results = pack(original.lockSpecialExit(roomNumber, destination, command, locked, ...))
6486
+ if results[1] then
6487
+ service.submit({ type = locked and "lock-special-exit" or "unlock-special-exit", roomNumber = tonumber(roomNumber), destination = tonumber(destination), exitCommand = command })
6488
+ end
6489
+ return unpackResults(results)
6490
+ end
6491
+
6492
+ function setExitWeight(roomNumber, exit, weight, ...)
6493
+ local results = pack(original.setExitWeight(roomNumber, exit, weight, ...))
6494
+ if results[1] then
6495
+ local direction = directions[exit]
6496
+ local change = { roomNumber = tonumber(roomNumber), weight = weight }
6497
+ if direction then change.type, change.direction = "modify-exit-weight", direction
6498
+ else change.type, change.exitCommand = "modify-special-exit-weight", tostring(exit) end
6499
+ service.submit(change)
6500
+ end
6501
+ return unpackResults(results)
6502
+ end
6503
+
6504
+ if original.setDoor then
6505
+ function setDoor(roomNumber, exit, status, ...)
6506
+ local results = pack(original.setDoor(roomNumber, exit, status, ...))
6507
+ local direction = directions[exit]
6508
+ if results[1] and direction then
6509
+ service.submit({ type = "set-exit-door", roomNumber = tonumber(roomNumber), direction = direction, status = tonumber(status) })
6510
+ end
6511
+ return unpackResults(results)
6512
+ end
6513
+ end
6514
+
6515
+ if original.setMapUserData then
6516
+ function setMapUserData(key, value, ...)
6517
+ local results = pack(original.setMapUserData(key, value, ...))
6518
+ if results[1] then service.submit({ type = "set-map-user-data", key = key, value = value }) end
6519
+ return unpackResults(results)
6520
+ end
6521
+ end
6522
+
6523
+ if original.clearMapUserDataItem then
6524
+ function clearMapUserDataItem(key, ...)
6525
+ local results = pack(original.clearMapUserDataItem(key, ...))
6526
+ if results[1] then service.submit({ type = "delete-map-user-data", key = key }) end
6527
+ return unpackResults(results)
6528
+ end
6529
+ end
6530
+
6531
+ if original.createMapLabel and getMapLabel then
6532
+ function createMapLabel(...)
6533
+ local arguments = pack(...)
6534
+ local areaId = arguments[1]
6535
+ local results = pack(original.createMapLabel(unpack(arguments, 1, arguments.n)))
6536
+ local labelId = results[1]
6537
+ if labelId ~= false and labelId ~= nil then
6538
+ local label = getMapLabel(areaId, labelId)
6539
+ if label and label.FgColor and label.BgColor and not label.Temporary then
6540
+ service.submit({
6541
+ type = "set-map-label", areaId = tonumber(areaId), labelId = labelId,
6542
+ label = {
6543
+ text = label.Text, x = label.X, y = label.Y, z = label.Z,
6544
+ width = label.Width, height = label.Height,
6545
+ fgColor = { alpha = label.FgColor.alpha or 255, r = label.FgColor.r, g = label.FgColor.g, b = label.FgColor.b },
6546
+ bgColor = { alpha = label.BgColor.alpha or 50, r = label.BgColor.r, g = label.BgColor.g, b = label.BgColor.b },
6547
+ noScaling = label.Scaling == false,
6548
+ showOnTop = label.OnTop == true
6549
+ }
6550
+ })
6551
+ end
6552
+ end
6553
+ return unpackResults(results)
6554
+ end
6555
+ end
6556
+
6557
+ if original.deleteMapLabel then
6558
+ function deleteMapLabel(areaId, labelId, ...)
6559
+ local label = getMapLabel and getMapLabel(areaId, labelId)
6560
+ local results = pack(original.deleteMapLabel(areaId, labelId, ...))
6561
+ local remainingLabel = getMapLabel and getMapLabel(areaId, labelId)
6562
+ if type(label) == "table" and next(label) and not label.Temporary and (type(remainingLabel) ~= "table" or not next(remainingLabel)) then
6563
+ service.submit({ type = "delete-map-label", areaId = tonumber(areaId), labelId = tonumber(labelId) })
6564
+ end
6565
+ return unpackResults(results)
6566
+ end
6567
+ end
6568
+
6569
+ function service.wrapSetExit(originalSetExit)
6570
+ service.originalMmpSetExit = service.originalMmpSetExit or originalSetExit
6571
+ function mmp.setExit(roomNumber, destination, direction, ...)
6572
+ local results = pack(service.originalMmpSetExit(roomNumber, destination, direction, ...))
6573
+ if results[1] then
6574
+ local change = { roomNumber = tonumber(roomNumber), direction = directions[direction] }
6575
+ if tonumber(destination) == -1 then change.type = "delete-exit"
6576
+ else change.type, change.destination = "modify-exit", tonumber(destination) end
6577
+ service.submit(change)
6578
+ end
6579
+ return unpackResults(results)
6580
+ end
6581
+ end
6582
+ end
6583
+
6584
+ function service.handleFailure(entry, message)
6585
+ if entry.attempts &lt; service.maxAttempts then
6586
+ table.insert(service.pendingChanges, 1, entry)
6587
+ local delay = math.min(2 ^ (entry.attempts - 1), 30)
6588
+ mmp.echo(message .. " Retrying in " .. delay .. " seconds (" .. entry.attempts .. "/" .. service.maxAttempts .. ").")
6589
+ service.retryPending = true
6590
+ tempTimer(delay, function()
6591
+ service.retryPending = false
6592
+ service.sendNext()
6593
+ end)
6594
+ else
6595
+ service.unsentChanges = service.unsentChanges + 1
6596
+ mmp.echo(message .. " Discarded after " .. service.maxAttempts .. " attempts. The local edit remains, but a later map download may overwrite it.")
6597
+ tempTimer(0, function() service.sendNext() end)
6598
+ end
6599
+ end
6600
+
6601
+ function service.warnBeforeMapLoad()
6602
+ local count = service.unsentCount()
6603
+ if count &gt; 0 then
6604
+ mmp.echo(string.format("Warning: %d local map edit%s have not been confirmed by the crowdmap service and may be overwritten by this map download.", count, count == 1 and "" or "s"))
6605
+ end
6606
+ end
6607
+
6608
+ function service.unsentCount()
6609
+ return service.unsentChanges + #service.pendingChanges + (service.inFlightEntry and 1 or 0)
6610
+ end
6611
+
6612
+ function service.mapLoaded()
6613
+ service.unsentChanges = 0
6614
+ end
6615
+
6616
+ local function completeRequest(url, error)
6617
+ if service.inFlight and url == service.requestUrl then
6618
+ local entry = service.inFlightEntry
6619
+ service.inFlight, service.requestUrl, service.inFlightEntry = false, nil, nil
6620
+ if error then
6621
+ service.handleFailure(entry, "Could not send map change to crowdmap service: " .. error)
6622
+ return
6623
+ end
6624
+ service.sendNext()
6625
+ end
6626
+ end
6627
+
6628
+ registerNamedEventHandler("mapper", "crowdmapServicePostError", "sysPostHttpError", function(_, error, url)
6629
+ completeRequest(url, error)
6630
+ end)
6631
+ registerNamedEventHandler("mapper", "crowdmapServicePostDone", "sysPostHttpDone", function(_, url)
6632
+ completeRequest(url)
6633
+ end)
6634
+ registerNamedEventHandler("mapper", "crowdmapServiceGetDone", "sysGetHttpDone", function(_, url)
6635
+ completeRequest(url)
6636
+ end)</script>
6637
+ <eventHandlerList />
6638
+ </Script>
6639
+ <Script isActive="yes" isFolder="no">
6640
+ <name>Room marks</name>
6641
+ <packageName></packageName>
6642
+ <script>mmp.roomMarkKeys = { public = "gotoMapping", private = "gotoMapping-private" }
6643
+
6644
+ function mmp.getRoomMarks(visibility)
6645
+ local key = mmp.roomMarkKeys[visibility]
6646
+ assert(key, "Unknown room mark visibility: " .. tostring(visibility))
6647
+ local raw = getRoomUserData(1, key)
6648
+ if raw == "" then return {} end
6649
+ local ok, marks = pcall(yajl.to_value, raw)
6650
+ if not ok or type(marks) ~= "table" then
6651
+ mmp.echo("Could not read " .. visibility .. " room marks because their map data is invalid.")
6652
+ return {}
6653
+ end
6654
+ return marks
6655
+ end
6656
+
6657
+ function mmp.getAllRoomMarks()
6658
+ local marks = {}
6659
+ for _, visibility in ipairs({"public", "private"}) do
6660
+ for name, location in pairs(mmp.getRoomMarks(visibility)) do
6661
+ marks[#marks + 1] = { name = name, location = location, visibility = visibility }
6662
+ end
6663
+ end
6664
+ table.sort(marks, function(a, b) return a.name &lt; b.name end)
6665
+ return marks
6666
+ end
6667
+
6668
+ function mmp.setRoomMark(markname, location, visibility)
6669
+ visibility = visibility or "private"
6670
+ if tonumber(markname) then
6671
+ return mmp.echo("The mark name can't be a number.")
6672
+ end
6673
+ local marks = mmp.getRoomMarks(visibility)
6674
+ marks[markname] = location
6675
+ local function writeMark()
6676
+ if not mmp.roomexists(1) then addRoom(1) end
6677
+ setRoomUserData(1, mmp.roomMarkKeys[visibility], yajl.to_string(marks))
6678
+ end
6679
+ if visibility == "private" then mmp.localMapChanges(writeMark) else writeMark() end
6680
+ mmp.echo(string.format("%s room mark for '%s' set to room %s.", visibility:title(), markname, location))
6681
+ end
6682
+
6683
+ function mmp.removeRoomMark(markname, visibility)
6684
+ visibility = visibility or "private"
6685
+ local marks = mmp.getRoomMarks(visibility)
6686
+ if not marks[markname] then
6687
+ return mmp.echo("Don't have such a " .. visibility .. " mark in the db.")
6688
+ end
6689
+ marks[markname] = nil
6690
+ local function removeMark()
6691
+ setRoomUserData(1, mmp.roomMarkKeys[visibility], yajl.to_string(marks))
6692
+ end
6693
+ if visibility == "private" then mmp.localMapChanges(removeMark) else removeMark() end
6694
+ mmp.echo("Removed the " .. visibility .. " " .. markname .. " mark.")
6695
+ end</script>
6696
+ <eventHandlerList />
6697
+ </Script>
6698
+ <Script isActive="yes" isFolder="no">
6699
+ <name>Sync safe Room.Info fields</name>
6700
+ <packageName></packageName>
6701
+ <script>function mmp.syncSafeRoomInfo(roomNumber, deferFeedback)
6702
+ if not gmcp.Room or not gmcp.Room.Info then
6703
+ return
6704
+ end
6705
+
6706
+ local info = gmcp.Room.Info
6707
+ roomNumber = roomNumber or tonumber(info.num)
6708
+ if not roomNumber and info.roomhash then
6709
+ roomNumber = getRoomIDbyHash(info.roomhash)
6710
+ end
6711
+ if not roomNumber or roomNumber == -1 or not mmp.roomexists(roomNumber) then
6712
+ return
6713
+ end
6714
+ local updates = {}
6715
+
6716
+ if info.name then
6717
+ local roomName = mmp.cleanroomname(info.name)
6718
+ if getRoomName(roomNumber) ~= roomName then
6719
+ setRoomName(roomNumber, roomName)
6720
+ unHighlightRoom(roomNumber)
6721
+ updates[#updates + 1] = "Updated room name to '" .. roomName .. "'."
6722
+ end
6723
+ end
6724
+
6725
+ if mmp.envids and info.environment and mmp.envids[info.environment] and mmp.envids[info.environment] ~= getRoomEnv(roomNumber) then
6726
+ setRoomEnv(roomNumber, mmp.envids[info.environment])
6727
+ updates[#updates + 1] = "Updated environment name to " .. info.environment .. "."
6728
+ end
6729
+
6730
+ local details = mmp.roomDetails()
6731
+ if details and mmp.game ~= "asteria" and mmp.game ~= "ashyria" then
6732
+ local indoors = table.contains(details, "indoors")
6733
+ if indoors and (getRoomUserData(roomNumber, "indoors") == "" or getRoomUserData(roomNumber, "outdoors") ~= "") then
6734
+ setRoomUserData(roomNumber, "indoors", "y")
6735
+ clearRoomUserDataItem(roomNumber, "outdoors")
6736
+ updates[#updates + 1] = "Updated room to be indoors."
6737
+ elseif not indoors and (getRoomUserData(roomNumber, "indoors") ~= "" or getRoomUserData(roomNumber, "outdoors") == "") then
6738
+ clearRoomUserDataItem(roomNumber, "indoors")
6739
+ setRoomUserData(roomNumber, "outdoors", "y")
6740
+ updates[#updates + 1] = "Updated room to be outdoors."
6741
+ end
6742
+ end
6743
+
6744
+ if mmp.game == "achaea" and info.area and info.area ~= getRoomUserData(roomNumber, "Game Area") then
6745
+ setRoomUserData(roomNumber, "Game Area", info.area)
6746
+ updates[#updates + 1] = "Updated game area to " .. info.area .. "."
6747
+ end
6748
+
6749
+ if details and getRoomChar(roomNumber) ~= "W" and table.contains(details, "wilderness") then
6750
+ setRoomChar(roomNumber, "W")
6751
+ updates[#updates + 1] = "Added the wilderness mark."
6752
+ end
6753
+ if #updates > 0 then
6754
+ local message = table.concat(updates, " ")
6755
+ if not deferFeedback then
6756
+ mmp.echo(message)
6757
+ centerview(mmp.currentroom)
6758
+ end
6759
+ return message
6760
+ end
6761
+ end
6762
+
6763
+ registerNamedEventHandler("mapper", "syncSafeRoomInfo", "gmcp.Room.Info", function()
6764
+ if not mmp.editing and mmp.settings and mmp.settings.gmcpmapupdates then
6765
+ mmp.syncSafeRoomInfo()
6766
+ end
6767
+ end)</script>
6768
+ <eventHandlerList />
6769
+ </Script>
6100
6770
  <Script isActive="yes" isFolder="no">
6101
6771
  <name>Create Option Table</name>
6102
6772
  <packageName></packageName>
@@ -6123,7 +6793,7 @@ local spairs = spairs or function(tbl, order)
6123
6793
  end
6124
6794
 
6125
6795
 
6126
- function mmp.createOption(startingValue, onChangeFunc, allowedVarTypes, use, checkOption, games)
6796
+ function mmp.createOption(startingValue, onChangeFunc, allowedVarTypes, use, checkOption, games, visibleWhen)
6127
6797
  if allowedVarTypes then -- make sure our starting Value follows type rules
6128
6798
  if not table.contains(allowedVarTypes, type(startingValue)) then
6129
6799
  echo("Starting type is not of allowed type!\n")
@@ -6139,6 +6809,7 @@ function mmp.createOption(startingValue, onChangeFunc, allowedVarTypes, use, che
6139
6809
  allowedVarTypes = allowedVarTypes,
6140
6810
  use = use or "",
6141
6811
  games = games,
6812
+ visibleWhen = visibleWhen,
6142
6813
  checkOption = checkOption or function() return true end
6143
6814
  }
6144
6815
 
@@ -6167,7 +6838,7 @@ function mmp.createOptionsTable(defaultTable)
6167
6838
  function proxyTable:showAllOptions(game)
6168
6839
  proxyTable.disp("Available options: \n")
6169
6840
  for k, v in spairs(self[index]) do
6170
- if not game or not v.games or v.games[game] then
6841
+ if (not game or not v.games or v.games[game]) and (not v.visibleWhen or v.visibleWhen()) then
6171
6842
  self.dispOption(k, v)
6172
6843
  end
6173
6844
  end
@@ -6187,7 +6858,25 @@ function mmp.createOptionsTable(defaultTable)
6187
6858
  return t
6188
6859
  end
6189
6860
 
6190
- function proxyTable:setOption(option, value, silent)
6861
+ function proxyTable:setOption(option, value, silent)
6862
+
6863
+ if option:lower() == "crowdmap" then
6864
+ option = "mapsource"
6865
+ if value == true then
6866
+ value = "published"
6867
+ elseif value == false then
6868
+ value = "game"
6869
+ else
6870
+ proxyTable.disp("'crowdmap' has been replaced by 'mapsource'. "..
6871
+ "Possible values: game | published | service ")
6872
+ return
6873
+ end
6874
+ if not silent then
6875
+ proxyTable.disp("'crowdmap' has been replaced by 'mapsource'. Using '" .. value .. "'. " ..
6876
+ "(mconfig mapsource " .. value .. ")")
6877
+ end
6878
+ end
6879
+
6191
6880
  if self[option] == nil then
6192
6881
  proxyTable.disp("No such option!\n")
6193
6882
  return
@@ -6292,13 +6981,11 @@ function mmp.gotoArea(where, number, dashtype, exact)
6292
6981
  end
6293
6982
  local where = where:lower()
6294
6983
  number = tonumber(number)
6295
- local tmp = getRoomUserData(1, "gotoMapping")
6296
- if not tmp or tmp == '' then
6297
- tmp = "[]"
6298
- end
6299
- local temp, maptable = yajl.to_value(tmp), {}
6300
- for k, v in pairs(temp) do
6301
- maptable[k:lower()] = v
6984
+ local maptable = {}
6985
+ for _, visibility in ipairs({"public", "private"}) do
6986
+ for markname, roomNumber in pairs(mmp.getRoomMarks(visibility)) do
6987
+ maptable[markname:lower()] = roomNumber
6988
+ end
6302
6989
  end
6303
6990
  local destinationRoom = maptable[where]
6304
6991
  if destinationRoom then
@@ -6674,6 +7361,7 @@ function mmp.speedwalking(event, num)
6674
7361
  if num == nil and gmcp.Room.Info.roomhash then
6675
7362
  num = mmp.roomidFromHash(gmcp.Room.Info.roomhash)
6676
7363
  end
7364
+ if num == -1 then return end
6677
7365
 
6678
7366
  if num ~= mmp.currentroom then
6679
7367
  mmp.previousroom = mmp.currentroom
@@ -6684,7 +7372,7 @@ function mmp.speedwalking(event, num)
6684
7372
  -- Try to track if we're flying or not, for Imperian wings
6685
7373
  -- This is to avoid being "off path" if we FLY due to wings.
6686
7374
  local madeflight = false
6687
- if gmcp.Room then
7375
+ if gmcp.Room and gmcp.Room.Info then
6688
7376
  local flying = false
6689
7377
  if string.find(gmcp.Room.Info.name, "^flying above") then
6690
7378
  flying = true
@@ -6703,13 +7391,14 @@ function mmp.speedwalking(event, num)
6703
7391
  mmp.flying = false
6704
7392
  end
6705
7393
  -- track if we're inside or outside, if possible
6706
- if gmcp.Room then
7394
+ local details = mmp.roomDetails()
7395
+ if gmcp.Room and gmcp.Room.Info and details then
6707
7396
  local areaID = getRoomArea(mmp.currentroom)
6708
7397
  if
6709
7398
  mmp.inside and
6710
7399
  not (
6711
- table.contains(gmcp.Room.Info.details, "indoors") or
6712
- table.contains(gmcp.Room.Info.details, "considered indoors")
7400
+ table.contains(details, "indoors") or
7401
+ table.contains(details, "considered indoors")
6713
7402
  ) and
6714
7403
  not mmp.orbed()
6715
7404
  then
@@ -6718,8 +7407,8 @@ function mmp.speedwalking(event, num)
6718
7407
  elseif
6719
7408
  not mmp.inside and
6720
7409
  (
6721
- table.contains(gmcp.Room.Info.details, "indoors") or
6722
- table.contains(gmcp.Room.Info.details, "considered indoors") or
7410
+ table.contains(details, "indoors") or
7411
+ table.contains(details, "considered indoors") or
6723
7412
  mmp.orbed()
6724
7413
  )
6725
7414
  then
@@ -6772,7 +7461,7 @@ function mmp.speedwalking(event, num)
6772
7461
  elseif
6773
7462
  #mmp.speedWalkPath &gt; 0 and
6774
7463
  not mmp.ferry_rooms[num] and
6775
- not (gmcp.Room.Info.details and table.contains(gmcp.Room.Info.details, "ferry"))
7464
+ not mmp.hasRoomDetail("ferry")
6776
7465
  then
6777
7466
  -- ended up somewhere we didn't want to be, and this isn't a ferry room?
6778
7467
  speedWalkMoved = false
@@ -7109,11 +7798,13 @@ end</script>
7109
7798
  <script>--genericized version of the went_outside event handlers.
7110
7799
  --when you go outside, it sees if a shorter path can be found using external links. If so, routes you on the new path instead.
7111
7800
  function mmp.wentOutside()
7801
+ local details = mmp.roomDetails()
7112
7802
  if
7113
7803
  not mmp.autowalking or
7114
7804
  (mmp.currentroom == mmp.speedWalkPath[#mmp.speedWalkPath]) or
7115
- table.contains(gmcp.Room.Info.details, "indoors") or
7116
- table.contains(gmcp.Room.Info.details, "considered indoors") or
7805
+ not details or
7806
+ table.contains(details, "indoors") or
7807
+ table.contains(details, "considered indoors") or
7117
7808
  mmp.orbed(mmp.currentroom)
7118
7809
  then
7119
7810
  return
@@ -7165,18 +7856,25 @@ function mmp.tempSpecialExit(start, destination, command, weight)
7165
7856
  -- no start room given, so shift all arguments one to the right
7166
7857
  start, destination, command, weight = mmp.currentroom, start, destination, command
7167
7858
  end
7168
- table.insert(exitsCreated, {room = start, command = command})
7169
- addSpecialExit(start, destination, command)
7170
- if weight then
7171
- setExitWeight(start, command, weight)
7859
+ local added
7860
+ mmp.localMapChanges(function()
7861
+ added = addSpecialExit(start, destination, command)
7862
+ if weight then setExitWeight(start, command, weight) end
7863
+ end)
7864
+ if added then
7865
+ table.insert(exitsCreated, {room = start, destination = destination, command = command})
7866
+ mmp.crowdmapService.trackTemporarySpecialExit(start, destination, command)
7172
7867
  end
7173
7868
  end
7174
7869
 
7175
7870
  function mmp.removeWings()
7176
7871
  --remove all special exits that were created by addWings, then resets the table.
7177
- for _, exit in ipairs(exitsCreated) do
7178
- removeSpecialExit(exit.room, exit.command)
7179
- end
7872
+ mmp.localMapChanges(function()
7873
+ for _, exit in ipairs(exitsCreated) do
7874
+ removeSpecialExit(exit.room, exit.command)
7875
+ mmp.crowdmapService.untrackTemporarySpecialExit(exit.room, exit.destination, exit.command)
7876
+ end
7877
+ end)
7180
7878
  exitsCreated = {}
7181
7879
  end
7182
7880
 
@@ -7216,13 +7914,16 @@ local universeLocations = {
7216
7914
  function mmp.addWingsAchaea()
7217
7915
  --Trimmed down version of the original function, meant to only be used for wings. (Where the command varies based on several MCONFIGS)
7218
7916
 
7219
- local function getcmd(word)
7917
+ local function getcmd(word, id)
7918
+ local target = (id and id ~= "") and ("wings." .. tostring(id)) or "wings"
7220
7919
  return
7221
7920
  mmp.settings.removewings and
7222
7921
  string.format(
7223
- [[script:sendAll("wear wings", "say *%s %s", "remove wings", false)]],
7922
+ [[script:sendAll("wear %s", "say *%s %s", "remove %s", false)]],
7923
+ target,
7224
7924
  (mmp.settings.winglanguage and mmp.settings.winglanguage or ""),
7225
- word
7925
+ word,
7926
+ target
7226
7927
  ) or
7227
7928
  string.format(
7228
7929
  [[script:send("say *%s %s", false)]],
@@ -7244,24 +7945,28 @@ function mmp.addWingsAchaea()
7244
7945
  --Sarapis wings.
7245
7946
  if mmp.oncontinent(area, "Main") then
7246
7947
  if mmp.settings.duanatharan then
7247
- mmp.tempSpecialExit(4882, getcmd("duanatharan"))
7248
- mmp.tempSpecialExit(3885, getcmd("duanathar"))
7948
+ local wid = mmp.settings.duanatharanid
7949
+ mmp.tempSpecialExit(4882, getcmd("duanatharan", wid))
7950
+ mmp.tempSpecialExit(3885, getcmd("duanathar", wid))
7249
7951
  elseif mmp.settings.duantahar then
7250
- mmp.tempSpecialExit(42319, getcmd("duanathar"))
7251
- mmp.tempSpecialExit(4882, getcmd("duanatharan"))
7952
+ local wid = mmp.settings.duantaharid
7953
+ mmp.tempSpecialExit(42319, getcmd("duanathar", wid))
7954
+ mmp.tempSpecialExit(4882, getcmd("duanatharan", wid))
7252
7955
  else
7253
- mmp.tempSpecialExit(3885, getcmd("duanathar"))
7956
+ mmp.tempSpecialExit(3885, getcmd("duanathar", mmp.settings.duanatharid))
7254
7957
  end
7255
7958
  --Meropis Wings
7256
7959
  elseif mmp.oncontinent(area, "Meropis") then
7257
7960
  if mmp.settings.duanatharan then
7258
- mmp.tempSpecialExit(51603, getcmd("duanatharan"))
7259
- mmp.tempSpecialExit(51188, getcmd("duanathar"))
7961
+ local wid = mmp.settings.duanatharanid
7962
+ mmp.tempSpecialExit(51603, getcmd("duanatharan", wid))
7963
+ mmp.tempSpecialExit(51188, getcmd("duanathar", wid))
7260
7964
  elseif mmp.settings.duantahar then
7261
- mmp.tempSpecialExit(42319, getcmd("duanathar"))
7262
- mmp.tempSpecialExit(51603, getcmd("duanatharan"))
7965
+ local wid = mmp.settings.duantaharid
7966
+ mmp.tempSpecialExit(42319, getcmd("duanathar", wid))
7967
+ mmp.tempSpecialExit(51603, getcmd("duanatharan", wid))
7263
7968
  else
7264
- mmp.tempSpecialExit(51188, getcmd("duanathar"))
7969
+ mmp.tempSpecialExit(51188, getcmd("duanathar", mmp.settings.duanatharid))
7265
7970
  end
7266
7971
  end
7267
7972
  end
@@ -7275,7 +7980,7 @@ function mmp.addWingsAchaea()
7275
7980
  mmp.oncontinent(area, "Island") or
7276
7981
  mmp.oncontinent(area, "North")
7277
7982
  then
7278
- mmp.tempSpecialExit(47571, getcmd("duanatharic"))
7983
+ mmp.tempSpecialExit(47571, getcmd("duanatharic", mmp.settings.duanatharicid))
7279
7984
  end
7280
7985
  end
7281
7986
  -- Stratospheric Harness support -- PapaGuacamole
@@ -7500,13 +8205,16 @@ end
7500
8205
  --of searching for a valid outdoors room.
7501
8206
 
7502
8207
  local function linkWingsRemote(room)
7503
- local function getcmd(word)
8208
+ local function getcmd(word, id)
8209
+ local target = (id and id ~= "") and ("wings." .. tostring(id)) or "wings"
7504
8210
  return
7505
8211
  mmp.settings.removewings and
7506
8212
  string.format(
7507
- [[script:sendAll("wear wings", "say *%s %s", "remove wings", false)]],
8213
+ [[script:sendAll("wear %s", "say *%s %s", "remove %s", false)]],
8214
+ target,
7508
8215
  (mmp.settings.winglanguage and mmp.settings.winglanguage or ""),
7509
- word
8216
+ word,
8217
+ target
7510
8218
  ) or
7511
8219
  string.format(
7512
8220
  [[script:send("say *%s %s", false)]],
@@ -7521,24 +8229,28 @@ local function linkWingsRemote(room)
7521
8229
  if (mmp.settings.duanathar or mmp.settings.duanatharan or mmp.settings.duantahar) then
7522
8230
  if mmp.oncontinent(area, "Main") then
7523
8231
  if mmp.settings.duanatharan then
7524
- mmp.tempSpecialExit(room, 4882, getcmd("duanatharan"))
7525
- mmp.tempSpecialExit(room, 3885, getcmd("duanathar"))
8232
+ local wid = mmp.settings.duanatharanid
8233
+ mmp.tempSpecialExit(room, 4882, getcmd("duanatharan", wid))
8234
+ mmp.tempSpecialExit(room, 3885, getcmd("duanathar", wid))
7526
8235
  elseif mmp.settings.duantahar then
7527
- mmp.tempSpecialExit(room, 42319, getcmd("duanathar"))
7528
- mmp.tempSpecialExit(room, 4882, getcmd("duanatharan"))
8236
+ local wid = mmp.settings.duantaharid
8237
+ mmp.tempSpecialExit(room, 42319, getcmd("duanathar", wid))
8238
+ mmp.tempSpecialExit(room, 4882, getcmd("duanatharan", wid))
7529
8239
  else
7530
- mmp.tempSpecialExit(room, 3885, getcmd("duanathar"))
8240
+ mmp.tempSpecialExit(room, 3885, getcmd("duanathar", mmp.settings.duanatharid))
7531
8241
  end
7532
8242
  --Meropis Wings
7533
8243
  elseif mmp.oncontinent(area, "Meropis") then
7534
8244
  if mmp.settings.duanatharan then
7535
- mmp.tempSpecialExit(room, 51603, getcmd("duanatharan"))
7536
- mmp.tempSpecialExit(room, 51188, getcmd("duanathar"))
8245
+ local wid = mmp.settings.duanatharanid
8246
+ mmp.tempSpecialExit(room, 51603, getcmd("duanatharan", wid))
8247
+ mmp.tempSpecialExit(room, 51188, getcmd("duanathar", wid))
7537
8248
  elseif mmp.settings.duantahar then
7538
- mmp.tempSpecialExit(room, 42319, getcmd("duanathar"))
7539
- mmp.tempSpecialExit(room, 51603, getcmd("duanatharan"))
8249
+ local wid = mmp.settings.duantaharid
8250
+ mmp.tempSpecialExit(room, 42319, getcmd("duanathar", wid))
8251
+ mmp.tempSpecialExit(room, 51603, getcmd("duanatharan", wid))
7540
8252
  else
7541
- mmp.tempSpecialExit(room, 51188, getcmd("duanathar"))
8253
+ mmp.tempSpecialExit(room, 51188, getcmd("duanathar", mmp.settings.duanatharid))
7542
8254
  end
7543
8255
  end
7544
8256
  end
@@ -7554,7 +8266,7 @@ local function linkWingsRemote(room)
7554
8266
  mmp.oncontinent(area, "North")
7555
8267
  )
7556
8268
  then
7557
- mmp.tempSpecialExit(room, 47571, getcmd("duanatharic"))
8269
+ mmp.tempSpecialExit(room, 47571, getcmd("duanatharic", mmp.settings.duanatharicid))
7558
8270
  end
7559
8271
  if mmp.settings.harness then
7560
8272
  -- eastern isles
@@ -7607,7 +8319,7 @@ function mmp.achaeaBetterWings()
7607
8319
  if
7608
8320
  (mmp.game ~= "achaea") or
7609
8321
  (not mmp.settings.betterwings) or
7610
- (not mmp.settings.crowdmap) or
8322
+ (mmp.settings.mapsource == "game") or
7611
8323
  (getRoomUserData(mmp.currentroom, "outdoors") == "y" and orbedBlackList()(mmp.currentroom))
7612
8324
  then
7613
8325
  return
@@ -7760,7 +8472,7 @@ function mmp.addWingsLusternia()
7760
8472
  if mmp.usebubblix() then
7761
8473
  if table.contains(bubblix.areas, gmcp.Room.Info.area) then
7762
8474
  mmp.tempSpecialExit(mmp.currentroom, 6831, command)
7763
- elseif not table.contains(gmcp.Room.Info.details, "an Aetherbubble") then
8475
+ elseif mmp.roomDetails() and not mmp.hasRoomDetail("an Aetherbubble") then
7764
8476
  mmp.tempSpecialExit(mmp.currentroom, bubblix.room, command)
7765
8477
  end
7766
8478
  end
@@ -7784,8 +8496,8 @@ function mmp.addWingsLusternia()
7784
8496
  }
7785
8497
  local exitScript = [[script:sendAll("outr %sbutton", "touch %sbutton", false)]]
7786
8498
  if
7787
- table.contains(gmcp.Room.Info.details, "the Prime Material Plane") and
7788
- table.contains(gmcp.Room.Info.details, "outdoors")
8499
+ mmp.hasRoomDetail("the Prime Material Plane") and
8500
+ mmp.hasRoomDetail("outdoors")
7789
8501
  then
7790
8502
  for color, room in pairs(colors) do
7791
8503
  mmp.tempSpecialExit(mmp.currentroom, room, exitScript:format(color, color), 20)
@@ -7839,8 +8551,9 @@ function mmp.addWingsImperian()
7839
8551
  if
7840
8552
  (mmp.settings.shekinah or mmp.settings.suriel) and
7841
8553
  gmcp.Room and
7842
- not table.contains(gmcp.Room.Info.details, "indoors") and
7843
- not table.contains(gmcp.Room.Info.details, "considered indoors") and
8554
+ mmp.roomDetails() and
8555
+ not mmp.hasRoomDetail("indoors") and
8556
+ not mmp.hasRoomDetail("considered indoors") and
7844
8557
  table.contains(mmp.imperian.wingAbleAreas, getRoomArea(mmp.currentroom))
7845
8558
  then
7846
8559
  if mmp.settings.shekinah then
@@ -8156,19 +8869,19 @@ end
8156
8869
  function mmp.usebix()
8157
8870
  if mmp.game ~= "lusternia" then return end
8158
8871
 
8159
- if (table.contains(gmcp.Room.Info.details, "the Prime Material Plane") or
8160
- table.contains(gmcp.Room.Info.details, "the Ethereal Plane") or
8161
- table.contains(gmcp.Room.Info.details, "the Water Elemental Plane") or
8162
- table.contains(gmcp.Room.Info.details, "the Air Elemental Plane") or
8163
- table.contains(gmcp.Room.Info.details, "the Fire Elemental Plane") or
8164
- table.contains(gmcp.Room.Info.details, "the Earth Elemental Plane") or
8165
- table.contains(gmcp.Room.Info.details, "Celestia, Plane of Light") or
8166
- table.contains(gmcp.Room.Info.details, "the Tainted Plane of Nil") or
8167
- table.contains(gmcp.Room.Info.details, "the Cosmic Plane of Continuum") or
8168
- table.contains(gmcp.Room.Info.details, "the Cosmic Plane of Vortex") or
8169
- table.contains(gmcp.Room.Info.details, "the Astral Plane")) and
8872
+ if (mmp.hasRoomDetail("the Prime Material Plane") or
8873
+ mmp.hasRoomDetail("the Ethereal Plane") or
8874
+ mmp.hasRoomDetail("the Water Elemental Plane") or
8875
+ mmp.hasRoomDetail("the Air Elemental Plane") or
8876
+ mmp.hasRoomDetail("the Fire Elemental Plane") or
8877
+ mmp.hasRoomDetail("the Earth Elemental Plane") or
8878
+ mmp.hasRoomDetail("Celestia, Plane of Light") or
8879
+ mmp.hasRoomDetail("the Tainted Plane of Nil") or
8880
+ mmp.hasRoomDetail("the Cosmic Plane of Continuum") or
8881
+ mmp.hasRoomDetail("the Cosmic Plane of Vortex") or
8882
+ mmp.hasRoomDetail("the Astral Plane")) and
8170
8883
  gmcp.Room.Info.area ~= "the City of Climanti" and
8171
- table.contains(gmcp.Room.Info.details, "outdoors") then
8884
+ mmp.hasRoomDetail("outdoors") then
8172
8885
  return true
8173
8886
  end
8174
8887
  return false
@@ -8176,14 +8889,14 @@ end
8176
8889
 
8177
8890
  function mmp.useprism()
8178
8891
  if mmp.game ~= "lusternia" then return end
8179
- if (table.contains(gmcp.Room.Info.details, "the Prime Material Plane") or
8180
- table.contains(gmcp.Room.Info.details, "the Ethereal Plane") or
8181
- table.contains(gmcp.Room.Info.details, "the Water Elemental Plane") or
8182
- table.contains(gmcp.Room.Info.details, "the Air Elemental Plane") or
8183
- table.contains(gmcp.Room.Info.details, "the Fire Elemental Plane") or
8184
- table.contains(gmcp.Room.Info.details, "the Earth Elemental Plane")) and
8892
+ if (mmp.hasRoomDetail("the Prime Material Plane") or
8893
+ mmp.hasRoomDetail("the Ethereal Plane") or
8894
+ mmp.hasRoomDetail("the Water Elemental Plane") or
8895
+ mmp.hasRoomDetail("the Air Elemental Plane") or
8896
+ mmp.hasRoomDetail("the Fire Elemental Plane") or
8897
+ mmp.hasRoomDetail("the Earth Elemental Plane")) and
8185
8898
  gmcp.Room.Info.area ~= "the City of Climanti" and
8186
- table.contains(gmcp.Room.Info.details, "outdoors") then
8899
+ mmp.hasRoomDetail("outdoors") then
8187
8900
  return true
8188
8901
  end
8189
8902
  return false
@@ -8191,15 +8904,15 @@ end
8191
8904
  function mmp.usebubblix()
8192
8905
  if mmp.game ~= "lusternia" then return end
8193
8906
 
8194
- if ((table.contains(gmcp.Room.Info.details, "the Prime Material Plane") or
8195
- table.contains(gmcp.Room.Info.details, "the Ethereal Plane") or
8196
- table.contains(gmcp.Room.Info.details, "the Water Elemental Plane") or
8197
- table.contains(gmcp.Room.Info.details, "the Air Elemental Plane") or
8198
- table.contains(gmcp.Room.Info.details, "the Fire Elemental Plane") or
8199
- table.contains(gmcp.Room.Info.details, "the Earth Elemental Plane")) and
8907
+ if ((mmp.hasRoomDetail("the Prime Material Plane") or
8908
+ mmp.hasRoomDetail("the Ethereal Plane") or
8909
+ mmp.hasRoomDetail("the Water Elemental Plane") or
8910
+ mmp.hasRoomDetail("the Air Elemental Plane") or
8911
+ mmp.hasRoomDetail("the Fire Elemental Plane") or
8912
+ mmp.hasRoomDetail("the Earth Elemental Plane")) and
8200
8913
  gmcp.Room.Info.area ~= "the City of Climanti" and
8201
- table.contains(gmcp.Room.Info.details, "outdoors")) or
8202
- table.contains(gmcp.Room.Info.details, "an Aetherbubble") then
8914
+ mmp.hasRoomDetail("outdoors")) or
8915
+ mmp.hasRoomDetail("an Aetherbubble") then
8203
8916
  return true
8204
8917
  end
8205
8918
  return false
@@ -9382,48 +10095,63 @@ end
9382
10095
 
9383
10096
  function mmp.lockPathways()
9384
10097
  local lock = mmp.settings.lockpathways and true or false
9385
- mmp.doLock("pathways", lock, "pathfind")
10098
+ mmp.localMapChanges(function() mmp.doLock("pathways", lock, "pathfind") end)
9386
10099
  end
9387
10100
 
9388
10101
  function mmp.lockSewers()
9389
10102
  local lock = mmp.settings.locksewers and true or false
9390
- return mmp.doLock("sewer grates", lock, "enter grate")
10103
+ return mmp.localMapChanges(function() return mmp.doLock("sewer grates", lock, "enter grate") end)
9391
10104
  end
9392
10105
 
9393
10106
  function mmp.lockWormholes()
9394
10107
  local lock = mmp.settings.lockwormholes and true or false
9395
- return mmp.doLock("wormholes", lock, "worm warp")
10108
+ return mmp.localMapChanges(function() return mmp.doLock("wormholes", lock, "worm warp") end)
9396
10109
  end
9397
10110
  tempTimer(0, function() if mmp.firstRun then mmp.lockWormholes() end end)
9398
10111
 
9399
10112
  function mmp.lockPebble()
9400
10113
  local disabled = (not mmp.settings.pebble) and true or false
9401
- mmp.doLock(nil, disabled, "touch 116998")
9402
- mmp.doLock(nil, disabled, "touch 277930")
10114
+ mmp.localMapChanges(function()
10115
+ mmp.doLock(nil, disabled, "touch 116998")
10116
+ mmp.doLock(nil, disabled, "touch 277930")
10117
+ end)
9403
10118
 
9404
10119
  mmp.echo(string.format("Use of pebble %s.", disabled and "disabled" or "enabled"))
9405
10120
  end
9406
10121
 
9407
10122
  function mmp.lockSpecials()
9408
10123
  local lock = mmp.settings.lockspecials and true or false
9409
- mmp.doLock("special exits", lock)
10124
+ mmp.localMapChanges(function() mmp.doLock("special exits", lock) end)
9410
10125
  end
9411
10126
 
9412
10127
 
10128
+ function mmp.changeCrowdmapServiceMapOption()
10129
+ if mmp.settings.mapsource ~= "service" then return end
10130
+ mmp.mapSourceGeneration = (mmp.mapSourceGeneration or 0) + 1
10131
+ mmp.checkforupdate()
10132
+ end
10133
+
9413
10134
  function mmp.changeMapSource()
9414
- local use = mmp.settings.crowdmap and true or false
9415
- if use and not (mmp.game == "achaea" or mmp.game == "starmourn" or mmp.game == "lusternia" or mmp.game == "stickmud" or mmp.game == "ashyria" or mmp.game == "asteria" or mmp.game == "imperian" or mmp.game == "aetolia") then
9416
- mmp.echo("Sorry - the crowdsourced map is only available for use in Achaea, Starmourn, Lusternia, StickMUD, Ashyria and Asteria. If you'd like to help start one for your game, please post at http://forums.mudlet.org/viewtopic.php?f=13&amp;t=1696. If you are playing one of the games, then it is likely that you just downloaded the script - and it doesn't know what you are playing. Reconnect and it'll know.")
9417
- mmp.settings.crowdmap = false
9418
- elseif use and not loadMap then
10135
+ local source = mmp.settings.mapsource
10136
+ if source ~= mmp.lastMapSource then
10137
+ mmp.lastMapSource = source
10138
+ mmp.mapSourceGeneration = (mmp.mapSourceGeneration or 0) + 1
10139
+ end
10140
+ if source ~= "game" and not (mmp.game == "achaea" or mmp.game == "starmourn" or mmp.game == "lusternia" or mmp.game == "stickmud" or mmp.game == "ashyria" or mmp.game == "asteria" or mmp.game == "imperian" or mmp.game == "aetolia") then
10141
+ mmp.echo("Sorry - the crowdsourced map is only available for use in Achaea, Aetolia, Imperian, Starmourn, Lusternia, StickMUD, Ashyria and Asteria. If you'd like to help start one for your game, please post at http://forums.mudlet.org/viewtopic.php?f=13&amp;t=1696. If you are playing one of the games, then it is likely that you just downloaded the script - and it doesn't know what you are playing. Reconnect and it'll know.")
10142
+ mmp.settings:setOption("mapsource", "game", true)
10143
+ elseif source ~= "game" and not loadMap then
9419
10144
  mmp.echo("Sorry - your Mudlet is too old and can't load maps. Please update: http://forums.mudlet.org/viewtopic.php?f=5&amp;t=1874")
9420
- mmp.settings.crowdmap = false
9421
- elseif use then
9422
- mmp.echo("Will use the crowdsourced map for updates instead!")
9423
- mmp.checkforupdate()
10145
+ mmp.settings:setOption("mapsource", "game", true)
10146
+ elseif source == "published" then
10147
+ mmp.echo("Will use the published crowdsourced map for updates instead!")
10148
+ elseif source == "service" then
10149
+ mmp.echo("Will use the live crowdmap service for updates instead!")
10150
+ mmp.crowdmapService.sendNext()
9424
10151
  else
9425
10152
  mmp.echo("Will use the default game map for updates.")
9426
10153
  end
10154
+ mmp.checkforupdate()
9427
10155
  end
9428
10156
 
9429
10157
  function mmp.setWingsLanguage()
@@ -9443,6 +10171,7 @@ function mmp.setSlowWalk()
9443
10171
  end
9444
10172
 
9445
10173
  function mmp.disableWaterWalk()
10174
+ return mmp.localMapChanges(function()
9446
10175
  local c = 0
9447
10176
  local getRoomEnv, setRoomWeight = getRoomEnv, setRoomWeight
9448
10177
  for roomid, roomname in pairs(getRooms()) do
@@ -9453,9 +10182,11 @@ function mmp.disableWaterWalk()
9453
10182
  end
9454
10183
 
9455
10184
  return c
10185
+ end)
9456
10186
  end
9457
10187
 
9458
10188
  function mmp.enableWaterWalk()
10189
+ return mmp.localMapChanges(function()
9459
10190
  local c = 0
9460
10191
  local getRoomEnv, setRoomWeight = getRoomEnv, setRoomWeight
9461
10192
  for roomid, roomname in pairs(getRooms()) do
@@ -9466,6 +10197,7 @@ function mmp.enableWaterWalk()
9466
10197
  end
9467
10198
 
9468
10199
  return c
10200
+ end)
9469
10201
  end
9470
10202
 
9471
10203
  function mmp.setWaterWalk()
@@ -10042,6 +10774,7 @@ end</script>
10042
10774
  <name>mmp.wormholesfix</name>
10043
10775
  <packageName></packageName>
10044
10776
  <script>function mmp.wormholesfix()
10777
+ return mmp.localMapChanges(function()
10045
10778
  local c = 0
10046
10779
  local weight = 10
10047
10780
  for _, area in pairs(getAreaTable()) do
@@ -10063,6 +10796,7 @@ end</script>
10063
10796
  end
10064
10797
 
10065
10798
  if c &gt; 0 then mmp.echo(string.format("%s wormholes weighted to %s (so we don't take them over too short distances).", c, weight)) end
10799
+ end)
10066
10800
  end</script>
10067
10801
  <eventHandlerList>
10068
10802
  <string>mmapper updated map</string>
@@ -10188,6 +10922,7 @@ function mmp.setExit(from, to, direction)
10188
10922
 
10189
10923
  return oldsetExit(from, to, type(direction) == "string" and exitmap[direction] or direction)
10190
10924
  end
10925
+ mmp.crowdmapService.wrapSetExit(mmp.setExit)
10191
10926
  end</script>
10192
10927
  <eventHandlerList />
10193
10928
  </Script>
@@ -10394,7 +11129,8 @@ end
10394
11129
  if gmcp.Room.Info.num then
10395
11130
  centerview(gmcp.Room.Info.num)
10396
11131
  elseif gmcp.Room.Info.roomhash then
10397
- centerview(mmp.roomidFromHash(gmcp.Room.Info.roomhash))
11132
+ local id = mmp.roomidFromHash(gmcp.Room.Info.roomhash)
11133
+ if id ~= -1 then centerview(id) end
10398
11134
  end
10399
11135
  end
10400
11136
  end</script>
@@ -10422,11 +11158,15 @@ local function makeroom(oldid, newid, x, y, z)
10422
11158
  assert(oldid, "makeroom: need the ID of the original room")
10423
11159
  assert(newid, "makeroom: need the ID of the room to create")
10424
11160
  assert(x and y and z, "makeroom: need all 3 coordinates")
10425
- addRoom(newid)
11161
+ local wasHashOnly = roomExists and roomExists(newid) and getRoomUserData(newid, "hashonly") == "true"
11162
+ if wasHashOnly then
11163
+ clearRoomUserDataItem(newid, "hashonly")
11164
+ mmp.crowdmapService.promoteHashOnlyRoom(newid)
11165
+ else
11166
+ addRoom(newid)
11167
+ end
10426
11168
  setRoomCoordinates(newid, x, y, z)
10427
11169
  setRoomArea(newid, getRoomArea(oldid))
10428
- -- in case we are in a hash-based game and the room ID already got created to store a hash, clear the flag
10429
- clearRoomUserDataItem(newid, "hashonly")
10430
11170
  local fgr, fgg, fgb = unpack(color_table.red)
10431
11171
  local bgr, bgg, bgb = unpack(color_table.blue)
10432
11172
  highlightRoom(newid, fgr, fgg, fgb, bgr, bgg, bgb, 1, 100, 100)
@@ -10510,12 +11250,15 @@ end
10510
11250
  -- at the same time, it'll mark the userdata "hashonly" as "true" so mmp.roomExists won't say this room exists
10511
11251
  -- this preserves the same logic in the code as non-hash games
10512
11252
  function mmp.roomidFromHash(hash)
11253
+ if hash == "" then return -1 end
10513
11254
  local id = getRoomIDbyHash(hash)
10514
11255
  if id == -1 then
10515
11256
  id = createRoomID()
10516
- addRoom(id)
10517
- setRoomIDbyHash(id, hash)
10518
- setRoomUserData(id, "hashonly", "true")
11257
+ mmp.localMapChanges(function()
11258
+ addRoom(id)
11259
+ setRoomIDbyHash(id, hash)
11260
+ setRoomUserData(id, "hashonly", "true")
11261
+ end)
10519
11262
  end
10520
11263
 
10521
11264
  return id
@@ -10546,6 +11289,7 @@ function mmp.mappingNewroom(_, num)
10546
11289
  if not num and gmcp.Room.Info.roomhash then
10547
11290
  num = mmp.roomidFromHash(gmcp.Room.Info.roomhash)
10548
11291
  end
11292
+ if num == -1 then return end
10549
11293
  local currentexits = gmcp.Room.Info.exits
10550
11294
  local s = ""
10551
11295
  if not mmp.roomexists(num) then
@@ -10559,7 +11303,7 @@ function mmp.mappingNewroom(_, num)
10559
11303
  id = mmp.roomidFromHash(id)
10560
11304
  end
10561
11305
 
10562
- if mmp.roomexists(id) then
11306
+ if id ~= -1 and mmp.roomexists(id) then
10563
11307
  s = makeroom(id, num, getshiftedcoords(exit, getRoomCoordinates(id)))
10564
11308
  end
10565
11309
  end
@@ -10571,14 +11315,6 @@ function mmp.mappingNewroom(_, num)
10571
11315
  end
10572
11316
  -- if we created it, and some data could be filled in
10573
11317
  if mmp.roomexists(num) then
10574
- -- cleanup the room name
10575
- local rootroomname = mmp.cleanroomname(gmcp.Room.Info.name)
10576
- -- match exact case, so mappers alertness' works properly
10577
- if getRoomName(num) ~= rootroomname then
10578
- setRoomName(num, rootroomname)
10579
- unHighlightRoom(num)
10580
- s = s .. (#s &gt; 0 and " " or "") .. "Updated room name to '" .. rootroomname .. "'."
10581
- end
10582
11318
  -- autolink exits
10583
11319
  if not inwilderness() then
10584
11320
  local x = getRoomExits(num) or {}
@@ -10598,7 +11334,7 @@ function mmp.mappingNewroom(_, num)
10598
11334
  exit ..
10599
11335
  ", it leads to a room with ID 0 (and that's not supported yet)."
10600
11336
  else
10601
- if not x[mmp.anytolong(exit)] then
11337
+ if id ~= -1 and not x[mmp.anytolong(exit)] then
10602
11338
  if not mmp.roomexists(id) then
10603
11339
  s =
10604
11340
  makeroom(
@@ -10716,45 +11452,9 @@ function mmp.mappingNewroom(_, num)
10716
11452
  end
10717
11453
  end
10718
11454
  end
10719
- -- check for environment update, if we have environments mapped out
10720
- if
10721
- mmp.envids[gmcp.Room.Info.environment] and
10722
- mmp.envids[gmcp.Room.Info.environment] ~= getRoomEnv(num)
10723
- then
10724
- setRoomEnv(num, mmp.envids[gmcp.Room.Info.environment])
10725
- s =
10726
- s ..
10727
- (#s &gt; 0 and " " or "") ..
10728
- "Updated environment name to " ..
10729
- gmcp.Room.Info.environment ..
10730
- "."
10731
- end
10732
- -- check indoors status
10733
- if mmp.game ~= "asteria" or mmp.game ~= "ashyria" then
10734
- local indoors = table.contains(gmcp.Room.Info.details, "indoors")
10735
- if indoors and (getRoomUserData(num, "indoors") == '' or getRoomUserData(num,"outdoors") ~= '') then
10736
- setRoomUserData(num, "indoors", "y")
10737
- clearRoomUserDataItem(num, "outdoors")
10738
- s = s .. (#s &gt; 0 and " " or "") .. "Updated room to be indoors."
10739
- elseif not indoors and (getRoomUserData(num, "indoors") ~= '' or getRoomUserData(num, "outdoors") == '') then
10740
- clearRoomUserDataItem(num, "indoors")
10741
- setRoomUserData(num, "outdoors", "y")
10742
- s = s .. (#s &gt; 0 and " " or "") .. "Updated room to be outdoors."
10743
- end
10744
- end
10745
-
10746
- -- check server area name (Achaea only for now)
10747
- if mmp.game == "achaea" then
10748
- local serverArea = gmcp.Room.Info.area
10749
- if serverArea ~= getRoomUserData(num, "Game Area") then
10750
- setRoomUserData(num, "Game Area", serverArea)
10751
- s = s .. (#s &gt; 0 and " " or "") .. "Updated game area to " .. serverArea .. "."
10752
- end
10753
- end
10754
- -- check for wilderness exits
10755
- if getRoomChar(num) ~= "W" and table.contains(gmcp.Room.Info.details, "wilderness") then
10756
- setRoomChar(num, "W")
10757
- s = s .. (#s &gt; 0 and " " or "") .. "Added the wilderness mark."
11455
+ local safeRoomUpdates = mmp.syncSafeRoomInfo(num, true)
11456
+ if safeRoomUpdates then
11457
+ s = s .. (#s > 0 and " " or "") .. safeRoomUpdates
10758
11458
  end
10759
11459
  end
10760
11460
  if #s &gt; 0 then
@@ -10787,7 +11487,9 @@ end</script>
10787
11487
  <script>function mmp.saveOptions()
10788
11488
  local saveTable = {
10789
11489
  locked_areas = mmp.locked,
10790
- options = mmp.settings:getAllOptions()
11490
+ options = mmp.settings:getAllOptions(),
11491
+ unsent_crowdmap_changes = mmp.crowdmapService and mmp.crowdmapService.unsentChanges or 0,
11492
+ pending_crowdmap_changes = mmp.crowdmapService and mmp.crowdmapService.pendingForSave() or {}
10791
11493
  }
10792
11494
  local _sep
10793
11495
  if string.char(getMudletHomeDir():byte()) == "/" then _sep = "/" else _sep = "\\" end
@@ -10802,14 +11504,46 @@ end</script>
10802
11504
  <Script isActive="yes" isFolder="no">
10803
11505
  <name>mmp.loadOptions</name>
10804
11506
  <packageName></packageName>
10805
- <script>function mmp.loadOptions()
11507
+ <script>function mmp.migrateOptions(options)
11508
+ if type(options) ~= "table" then return end
11509
+
11510
+ -- Pre-PR profiles only know the published Crowdmap toggle. PR #202 added a
11511
+ -- second service-source toggle. Fold both historical settings into one enum;
11512
+ -- the live service wins if both were enabled, matching the former download
11513
+ -- precedence. Remove old keys so later loads remain quiet and deterministic.
11514
+ if options.mapsource == nil then
11515
+ if options.crowdmapservicesource then
11516
+ options.mapsource = "service"
11517
+ elseif options.crowdmap then
11518
+ options.mapsource = "published"
11519
+ else
11520
+ options.mapsource = "game"
11521
+ end
11522
+ end
11523
+ options.crowdmap = nil
11524
+ options.crowdmapservicesource = nil
11525
+
11526
+ -- PR #202 originally used this Achaea-specific endpoint as its default. Only
11527
+ -- replace that exact value: every other value is a user-selected endpoint.
11528
+ if options.crowdmapserviceurl == "https://achaea-crowdmap.schova.de" then
11529
+ options.crowdmapserviceurl = "https://&lt;game&gt;.mudmaps.community"
11530
+ end
11531
+ end
11532
+
11533
+ function mmp.loadOptions()
10806
11534
  local loadTable = mmp.loadLocks()
11535
+ if mmp.crowdmapService then
11536
+ mmp.crowdmapService.unsentChanges = tonumber(loadTable.unsent_crowdmap_changes) or 0
11537
+ mmp.crowdmapService.restorePendingChanges(loadTable.pending_crowdmap_changes)
11538
+ end
10807
11539
 
10808
11540
  if loadTable.options then
11541
+ mmp.migrateOptions(loadTable.options)
10809
11542
  for k, v in pairs(loadTable.options) do
10810
11543
  mmp.settings:setOption(k, v, true)
10811
11544
  end
10812
11545
  end
11546
+ if mmp.crowdmapService then mmp.crowdmapService.sendNext() end
10813
11547
  end
10814
11548
 
10815
11549
  function mmp.loadLocks()
@@ -10934,10 +11668,12 @@ end</script>
10934
11668
  <packageName></packageName>
10935
11669
  <script>function mmp.achaeaGaiaWhirlpoolReset(event, game)
10936
11670
  if game == "Achaea" then
10937
- mmp.whirlString = 'script: send("enter whirlpool") if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end if tempWhirlTimer then killTimer(killWhirlTimer) tempWhirlTimer = nil end tempWhirlTrigger = tempTrigger("What do you wish to enter?", [[if tempWhirlTimer then killTimer(tempWhirlTimer) tempWhirlTimer = nil end mmp.echo("Whirlpool not found here. Trying another room.") local x = {45817, 45780, 45903, 45868, 46254, 45866} if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end clearSpecialExits(]]..mmp.currentroom..[[) local y = table.index_of(x, ]]..mmp.currentroom..[[) + 1 if y &gt; #x then y = 1 end addSpecialExit(x[y], 793, mmp.whirlString) mmp.clearpathcache() mmp.gotoRoom(mmp.speedWalkPath[#mmp.speedWalkPath]) mmp.echo("Moved whirlpool to "..x[y]..".")]]) tempWhirlTimer = tempTimer(1, [[if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end if tempWhirlTimer then killTimer(tempWhirlTimer) tempWhirlTimer = nil end]])'
11671
+ mmp.whirlString = 'script: send("enter whirlpool") if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end if tempWhirlTimer then killTimer(killWhirlTimer) tempWhirlTimer = nil end tempWhirlTrigger = tempTrigger("What do you wish to enter?", [[if tempWhirlTimer then killTimer(tempWhirlTimer) tempWhirlTimer = nil end mmp.echo("Whirlpool not found here. Trying another room.") local x = {45817, 45780, 45903, 45868, 46254, 45866} if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end local y = table.index_of(x, ]]..mmp.currentroom..[[) + 1 if y &gt; #x then y = 1 end mmp.localMapChanges(function() clearSpecialExits(]]..mmp.currentroom..[[) addSpecialExit(x[y], 793, mmp.whirlString) end) mmp.clearpathcache() mmp.gotoRoom(mmp.speedWalkPath[#mmp.speedWalkPath]) mmp.echo("Moved whirlpool to "..x[y]..".")]]) tempWhirlTimer = tempTimer(1, [[if tempWhirlTrigger then killTrigger(tempWhirlTrigger) tempWhirlTrigger = nil end if tempWhirlTimer then killTimer(tempWhirlTimer) tempWhirlTimer = nil end]])'
10938
11672
  local x = {45817, 45780, 45903, 45868, 46254, 45866}
10939
- for i, v in ipairs(x) do clearSpecialExits(v) end
10940
- addSpecialExit(45868, 793, mmp.whirlString)
11673
+ mmp.localMapChanges(function()
11674
+ for i, v in ipairs(x) do clearSpecialExits(v) end
11675
+ addSpecialExit(45868, 793, mmp.whirlString)
11676
+ end)
10941
11677
  end
10942
11678
  end</script>
10943
11679
  <eventHandlerList>
@@ -10979,7 +11715,7 @@ registerAnonymousEventHandler("started speedwalk", "mmp.unwearShackle")</script>
10979
11715
  function mmp.getAchaeaOrbTable()
10980
11716
  return
10981
11717
  --first table is areas in each org for the crowdmap, second is for the game provided map.
10982
- mmp.settings.crowdmap and
11718
+ mmp.settings.mapsource ~= "game" and
10983
11719
  {
10984
11720
  ashtan = {49, 53},
10985
11721
  cyrene = {57, 59, 62, 63, 68, 70},
@@ -11565,32 +12301,36 @@ function mmp.astroboots()
11565
12301
  local option = mmp.settings.astrojump
11566
12302
  mmp.echo("&lt;green&gt;" .. (option and "Set up" or "Removed") .. " &lt;white&gt;Astrojump &lt;green&gt;exits")
11567
12303
  local exitString = [[script:if gmcp.Room.Info.exits.%s then send("%s", false) else send("astrojump", false) end]]
11568
- for k, v in pairs(astralExits) do
11569
- if option then
11570
- addSpecialExit(v[1], v[2], string.format(exitString, v[3], v[3]))
11571
- addSpecialExit(v[2], v[1], string.format(exitString, mirror[v[3]], mirror[v[3]]))
11572
- else
11573
- removeSpecialExit(v[1], "astrojump")
11574
- removeSpecialExit(v[2], "astrojump")
11575
- removeSpecialExit(v[1], string.format(exitString, v[3], v[3]))
11576
- removeSpecialExit(v[2], string.format(exitString, mirror[v[3]], mirror[v[3]]))
12304
+ mmp.localMapChanges(function()
12305
+ for k, v in pairs(astralExits) do
12306
+ if option then
12307
+ addSpecialExit(v[1], v[2], string.format(exitString, v[3], v[3]))
12308
+ addSpecialExit(v[2], v[1], string.format(exitString, mirror[v[3]], mirror[v[3]]))
12309
+ else
12310
+ removeSpecialExit(v[1], "astrojump")
12311
+ removeSpecialExit(v[2], "astrojump")
12312
+ removeSpecialExit(v[1], string.format(exitString, v[3], v[3]))
12313
+ removeSpecialExit(v[2], string.format(exitString, mirror[v[3]], mirror[v[3]]))
12314
+ end
11577
12315
  end
11578
- end
12316
+ end)
11579
12317
  end
11580
12318
 
11581
12319
  function mmp.wildnodes(create)
11582
12320
  if mmp.game ~= "lusternia" then
11583
12321
  return
11584
12322
  end
11585
- for k, v in pairs(astralExits) do
11586
- if create then
11587
- mmp.setExit(v[1], v[2], v[3])
11588
- mmp.setExit(v[2], v[1], mirrorExits[v[3]])
11589
- else
11590
- mmp.setExit(v[1], -1, v[3])
11591
- mmp.setExit(v[2], -1, mirrorExits[v[3]])
12323
+ mmp.localMapChanges(function()
12324
+ for k, v in pairs(astralExits) do
12325
+ if create then
12326
+ mmp.setExit(v[1], v[2], v[3])
12327
+ mmp.setExit(v[2], v[1], mirrorExits[v[3]])
12328
+ else
12329
+ mmp.setExit(v[1], -1, v[3])
12330
+ mmp.setExit(v[2], -1, mirrorExits[v[3]])
12331
+ end
11592
12332
  end
11593
- end
12333
+ end)
11594
12334
  end</script>
11595
12335
  <eventHandlerList />
11596
12336
  </Script>
@@ -11617,7 +12357,8 @@ function mmp.transverse(_, command)
11617
12357
  if command:sub(0, 11) == "transverse " or command == "pathfind" then
11618
12358
  transverseCMD = command
11619
12359
  transversedFrom = gmcp.Room.Info.num
11620
- lastPlane = planes[gmcp.Room.Info.details[1]]
12360
+ local details = mmp.roomDetails()
12361
+ lastPlane = details and planes[details[1]] or nil
11621
12362
  validTransverse = true
11622
12363
  end
11623
12364
  end
@@ -11626,29 +12367,33 @@ registerAnonymousEventHandler("sysDataSendRequest", "mmp.transverse")
11626
12367
 
11627
12368
  function mmp.registerTransverseExit()
11628
12369
  if transversedFrom ~= gmcp.Room.Info.num then
11629
- addSpecialExit(transversedFrom, gmcp.Room.Info.num, transverseCMD)
11630
- setExitWeight(transversedFrom, transverseCMD, 20)
11631
- if lastPlane then
11632
- addSpecialExit(gmcp.Room.Info.num, transversedFrom, "transverse " .. lastPlane)
11633
- setExitWeight(gmcp.Room.Info.num, "transverse " .. lastPlane, 20)
11634
- end
12370
+ mmp.localMapChanges(function()
12371
+ addSpecialExit(transversedFrom, gmcp.Room.Info.num, transverseCMD)
12372
+ setExitWeight(transversedFrom, transverseCMD, 20)
12373
+ if lastPlane then
12374
+ addSpecialExit(gmcp.Room.Info.num, transversedFrom, "transverse " .. lastPlane)
12375
+ setExitWeight(gmcp.Room.Info.num, "transverse " .. lastPlane, 20)
12376
+ end
12377
+ end)
11635
12378
  validTransverse = false
11636
12379
  end
11637
12380
  end
11638
12381
 
11639
12382
  function mmp.clearTransverse()
11640
- local otherSide = 0
11641
- for room, command in pairs(getSpecialExits(gmcp.Room.Info.num)) do
11642
- if next(command) == transverseCMD then
11643
- otherSide = room
12383
+ mmp.localMapChanges(function()
12384
+ local otherSide = 0
12385
+ for room, command in pairs(getSpecialExits(gmcp.Room.Info.num)) do
12386
+ if next(command) == transverseCMD then
12387
+ otherSide = room
12388
+ end
11644
12389
  end
11645
- end
11646
- for room, command in pairs(getSpecialExits(otherSide) or {}) do
11647
- if room == gmcp.Room.Info.num and string.sub(next(command), 0, 11) == "transverse " then
11648
- removeSpecialExit(otherSide, next(command))
12390
+ for room, command in pairs(getSpecialExits(otherSide) or {}) do
12391
+ if room == gmcp.Room.Info.num and string.sub(next(command), 0, 11) == "transverse " then
12392
+ removeSpecialExit(otherSide, next(command))
12393
+ end
11649
12394
  end
11650
- end
11651
- removeSpecialExit(gmcp.Room.Info.num, transverseCMD)
12395
+ removeSpecialExit(gmcp.Room.Info.num, transverseCMD)
12396
+ end)
11652
12397
  if mmp.autowalking then
11653
12398
  mmp.autowalking = false
11654
12399
  mmp.gotoRoom(string.split(next(mmp.showpathcache()), "_")[2])
@@ -11656,26 +12401,30 @@ function mmp.clearTransverse()
11656
12401
  end
11657
12402
 
11658
12403
  function mmp.registerPathfind()
11659
- addSpecialExit(transversedFrom, gmcp.Room.Info.num, "pathfind")
11660
- setExitWeight(transversedFrom, "pathfind", 15)
11661
- addSpecialExit(gmcp.Room.Info.num, transversedFrom, "pathfind")
11662
- setExitWeight(gmcp.Room.Info.num, "pathfind", 15)
12404
+ mmp.localMapChanges(function()
12405
+ addSpecialExit(transversedFrom, gmcp.Room.Info.num, "pathfind")
12406
+ setExitWeight(transversedFrom, "pathfind", 15)
12407
+ addSpecialExit(gmcp.Room.Info.num, transversedFrom, "pathfind")
12408
+ setExitWeight(gmcp.Room.Info.num, "pathfind", 15)
12409
+ end)
11663
12410
  validTransverse = false
11664
12411
  end
11665
12412
 
11666
12413
  function mmp.clearPathfind()
11667
- local otherSide = 0
11668
- for room, command in pairs(getSpecialExits(gmcp.Room.Info.num)) do
11669
- if next(command) == "pathfind" then
11670
- otherSide = room
12414
+ mmp.localMapChanges(function()
12415
+ local otherSide = 0
12416
+ for room, command in pairs(getSpecialExits(gmcp.Room.Info.num)) do
12417
+ if next(command) == "pathfind" then
12418
+ otherSide = room
12419
+ end
11671
12420
  end
11672
- end
11673
- for room, command in pairs(getSpecialExits(otherSide) or {}) do
11674
- if room == gmcp.Room.Info.num and next(command) == "pathfind" then
11675
- removeSpecialExit(otherSide, next(command))
12421
+ for room, command in pairs(getSpecialExits(otherSide) or {}) do
12422
+ if room == gmcp.Room.Info.num and next(command) == "pathfind" then
12423
+ removeSpecialExit(otherSide, next(command))
12424
+ end
11676
12425
  end
11677
- end
11678
- removeSpecialExit(gmcp.Room.Info.num, "pathfind")
12426
+ removeSpecialExit(gmcp.Room.Info.num, "pathfind")
12427
+ end)
11679
12428
  if mmp.autowalking then
11680
12429
  mmp.autowalking = false
11681
12430
  mmp.gotoRoom(string.split(next(mmp.showpathcache()), "_")[2])
@@ -11684,18 +12433,20 @@ end
11684
12433
 
11685
12434
  function mmp.lockpaths(lock)
11686
12435
  local count = 0
11687
- for area, areaname in pairs(mmp.areatabler) do
11688
- local rooms = getAreaRooms(area) or {}
11689
- for i = 0, #rooms do
11690
- local exits = getSpecialExits(rooms[i] or 0)
11691
- for room, command in pairs(exits or {}) do
11692
- if next(command) == "pathfind" then
11693
- count = count + 1
11694
- lockSpecialExit(rooms[i], room, "pathfind", lock)
12436
+ mmp.localMapChanges(function()
12437
+ for area, areaname in pairs(mmp.areatabler) do
12438
+ local rooms = getAreaRooms(area) or {}
12439
+ for i = 0, #rooms do
12440
+ local exits = getSpecialExits(rooms[i] or 0)
12441
+ for room, command in pairs(exits or {}) do
12442
+ if next(command) == "pathfind" then
12443
+ count = count + 1
12444
+ lockSpecialExit(rooms[i], room, "pathfind", lock)
12445
+ end
11695
12446
  end
11696
12447
  end
11697
12448
  end
11698
- end
12449
+ end)
11699
12450
  mmp.echo((lock and "Locked " or "Unlocked ") .. count .. " pathfinding exits.")
11700
12451
  end</script>
11701
12452
  <eventHandlerList />
@@ -12314,7 +13065,74 @@ end</script>
12314
13065
  <Script isActive="yes" isFolder="no">
12315
13066
  <name>mmp.downloadedFile</name>
12316
13067
  <packageName></packageName>
12317
- <script>function mmp.downloadedFile(_, filename)
13068
+ <script>mmp.crowdmapLoadRetries = mmp.crowdmapLoadRetries or {}
13069
+
13070
+ local function crowdmapDownloadIsCurrent(context)
13071
+ return context.filename == mmp.crowdmapfile
13072
+ and context.source == mmp.settings.mapsource
13073
+ and context.generation == (mmp.mapSourceGeneration or 0)
13074
+ and context.identity == mmp.mapSourceIdentity(mmp.settings.mapsource)
13075
+ end
13076
+
13077
+ local function finishCrowdmapLoad(context, successMessage)
13078
+ os.remove(context.filename)
13079
+ mmp.crowdmapService.mapLoaded()
13080
+ if mmp.game ~= "starmourn" then
13081
+ local wc = mmp.lockWormholes()
13082
+ local ws = mmp.lockSewers()
13083
+ mmp.lockPebble()
13084
+ if mmp.game == "achaea" and wc == 0 and ws == 0 then
13085
+ mmp.echo("Sorry, it seems your Mudlet isn't compatible with the new crowdmap format - and the map cannot be loaded fully. Please update your Mudlet:\n http://www.mudlet.org/download/")
13086
+ return
13087
+ end
13088
+ end
13089
+
13090
+ if mmp.settings.waterwalk then mmp.enableWaterWalk() else mmp.disableWaterWalk() end
13091
+
13092
+ mmp.echo(successMessage)
13093
+ mmp.recordCrowdmapVersion(context.version, context.identity)
13094
+
13095
+ local newPublicMarks = mmp.getRoomMarks("public")
13096
+ for markname, location in pairs(context.publicMarks) do
13097
+ if context.privateMarks[markname] == nil and tostring(newPublicMarks[markname]) ~= tostring(location) then
13098
+ context.privateMarks[markname] = location
13099
+ end
13100
+ end
13101
+ mmp.localMapChanges(function()
13102
+ setRoomUserData(1, "gotoMapping-private", yajl.to_string(context.privateMarks))
13103
+ end)
13104
+ mmp.echo("Private marks from the old map migrated successfully.")
13105
+ raiseEvent("mmapper updated map")
13106
+ end
13107
+
13108
+ local function loadCrowdmapDownload(context, successMessage)
13109
+ mmp.crowdmapService.warnBeforeMapLoad()
13110
+ if not loadMap(context.filename) then return false end
13111
+ finishCrowdmapLoad(context, successMessage)
13112
+ return true
13113
+ end
13114
+
13115
+ function mmp.retryCrowdmapLoad(filename)
13116
+ local context = mmp.crowdmapLoadRetries[filename]
13117
+ if not context then return mmp.echo("That map download is no longer available to retry.") end
13118
+ if not crowdmapDownloadIsCurrent(context) then
13119
+ mmp.crowdmapLoadRetries[filename] = nil
13120
+ os.remove(filename)
13121
+ mmp.echo("That map download is stale; checking for the current map instead.")
13122
+ return mmp.checkforupdate()
13123
+ end
13124
+ if not io.exists(filename) then
13125
+ mmp.crowdmapLoadRetries[filename] = nil
13126
+ return mmp.echo("That map download is no longer available to retry.")
13127
+ end
13128
+ if loadCrowdmapDownload(context, "Map loaded successfully!") then
13129
+ mmp.crowdmapLoadRetries[filename] = nil
13130
+ else
13131
+ mmp.echo("Nope, didn't work. Open the map and try again?")
13132
+ end
13133
+ end
13134
+
13135
+ function mmp.downloadedFile(_, filename)
12318
13136
  if not io.exists(filename) then return end
12319
13137
 
12320
13138
  -- workaround for https://bugs.launchpad.net/mudlet/+bug/1092769
@@ -12346,6 +13164,10 @@ end</script>
12346
13164
  echo("\n\n")
12347
13165
 
12348
13166
  elseif filename == mmp.crowdchangelogfile then -- changelog for the crowdmap
13167
+ if mmp.crowdmapDownloadSource ~= mmp.settings.mapsource or mmp.crowdmapDownloadGeneration ~= (mmp.mapSourceGeneration or 0) or mmp.crowdmapDownloadIdentity ~= mmp.mapSourceIdentity(mmp.settings.mapsource) then
13168
+ os.remove(filename)
13169
+ return
13170
+ end
12349
13171
  local f, s = io.open(filename)
12350
13172
  if f then s = f:read("*a"); io.close(f) end
12351
13173
 
@@ -12376,79 +13198,25 @@ end</script>
12376
13198
  mmp.downloadcrowdmap(mmp.newversion)
12377
13199
 
12378
13200
  elseif filename == mmp.crowdmapfile then -- crowdmap map
12379
- mmp.echo("Map downloaded, loading it in...")
12380
-
12381
- local tmp = getRoomUserData(1, "gotoMapping")
12382
- local oldmaptable = {}
12383
-
12384
- if tmp ~= "" then
12385
- oldmaptable = yajl.to_value(tmp)
13201
+ if mmp.crowdmapDownloadSource ~= mmp.settings.mapsource or mmp.crowdmapDownloadGeneration ~= (mmp.mapSourceGeneration or 0) or mmp.crowdmapDownloadIdentity ~= mmp.mapSourceIdentity(mmp.settings.mapsource) then
13202
+ os.remove(filename)
13203
+ return mmp.checkforupdate()
12386
13204
  end
13205
+ mmp.echo("Map downloaded, loading it in...")
13206
+ local context = {
13207
+ filename = filename,
13208
+ version = mmp.crowdmapDownloadVersion,
13209
+ source = mmp.crowdmapDownloadSource,
13210
+ generation = mmp.crowdmapDownloadGeneration,
13211
+ identity = mmp.crowdmapDownloadIdentity,
13212
+ publicMarks = mmp.getRoomMarks("public"),
13213
+ privateMarks = mmp.getRoomMarks("private")
13214
+ }
12387
13215
 
12388
- local ok = loadMap(filename)
12389
-
12390
- if ok then
12391
- if mmp.game ~= "starmourn" then
12392
- local wc = mmp.lockWormholes()
12393
- local ws = mmp.lockSewers()
12394
- mmp.lockPebble()
12395
- if mmp.game == "achaea" and wc == 0 and ws == 0 then -- in achaea, using crowdmap, if we didn't get any special exits - it means Mudlet needs to be updated
12396
- mmp.echo("Sorry, it seems your Mudlet isn't compatible with the new crowdmap format - and the map cannot be loaded fully. Please update your Mudlet:\n http://www.mudlet.org/download/")
12397
- return
12398
- end
12399
- end
12400
-
12401
- if mmp.settings.waterwalk then mmp.enableWaterWalk() else mmp.disableWaterWalk() end
12402
-
12403
- mmp.echo("Map loaded fine - enjoy!")
12404
-
12405
- tmp = getRoomUserData(1, "gotoMapping")
12406
- local newmaptable = {}
12407
-
12408
- if tmp ~= "" then
12409
- newmaptable = yajl.to_value(tmp)
12410
- end
12411
-
12412
- for k,v in pairs(oldmaptable) do newmaptable[k] = v end
12413
- setRoomUserData(1, "gotoMapping", yajl.to_string(newmaptable))
12414
- mmp.echo("Marks from the old map migrated successfully.")
12415
-
12416
- raiseEvent("mmapper updated map")
12417
- else
13216
+ if not loadCrowdmapDownload(context, "Map loaded fine - enjoy!") then
13217
+ mmp.crowdmapLoadRetries[filename] = context
12418
13218
  mmp.echon("Map failed to load - you need to have the mapper open. Please open it, and then ")
12419
- echoLink("click here", [[
12420
- local tmp = getRoomUserData(1, "gotoMapping")
12421
- local oldmaptable = {}
12422
- if tmp ~= "" then
12423
- oldmaptable = yajl.to_value(tmp)
12424
- end
12425
-
12426
- local ok = loadMap(']]..filename..[[')
12427
- if ok then
12428
- local wc = mmp.lockWormholes()
12429
- local ws = mmp.lockSewers()
12430
- mmp.lockPebble()
12431
- if mmp.game == "achaea" and wc == 0 and ws == 0 then -- in achaea, using crowdmap, if we didn't get any special exits - it means Mudlet needs to be updated
12432
- mmp.echo("Sorry, it seems your Mudlet isn't compatible with the new crowdmap format - and the map cannot be loaded fully. Please update your Mudlet:\n http://www.mudlet.org/download/")
12433
- return
12434
- end
12435
-
12436
- if mmp.settings.waterwalk then mmp.enableWaterWalk() else mmp.disableWaterWalk() end
12437
-
12438
- mmp.echo("Map loaded successfully!")
12439
-
12440
- tmp = getRoomUserData(1, "gotoMapping")
12441
- local newmaptable = {}
12442
- if tmp ~= "" then
12443
- newmaptable = yajl.to_value(tmp)
12444
- end
12445
- for k,v in pairs(oldmaptable) do newmaptable[k] = v end
12446
- setRoomUserData(1, "gotoMapping", yajl.to_string(newmaptable))
12447
- mmp.echo("Marks from the old map migrated successfully.")
12448
- raiseEvent("mmapper updated map")
12449
- else mmp.echo("Nope, didn't work. Open the map and try again?") end
12450
- ]],
12451
- "Click here to try loading the map again")
13219
+ echoLink("click here", string.format("mmp.retryCrowdmapLoad(%q)", filename), "Click here to try loading the map again")
12452
13220
  echo(" to try loading it in again.\n")
12453
13221
  end
12454
13222
 
@@ -12458,12 +13226,16 @@ end</script>
12458
13226
  elseif filename == tostring(mmp.mapfile) then -- map version #, either IRE's or crowd
12459
13227
  mmp.checkingupdates = false
12460
13228
 
13229
+ if mmp.mapDownloadSource ~= mmp.settings.mapsource or mmp.mapDownloadGeneration ~= (mmp.mapSourceGeneration or 0) or mmp.mapDownloadIdentity ~= mmp.mapSourceIdentity(mmp.settings.mapsource) then
13230
+ return mmp.checkforupdate()
13231
+ end
13232
+
12461
13233
  local function needupdate(currentmd5, oldmd5)
12462
- if not mmp.settings.crowdmap then
13234
+ if mmp.mapDownloadSource == "game" then
12463
13235
  mmp.echon("The games map was ")
12464
13236
  echoLink("updated", '', "New MD5: "..tostring(currentmd5)..", previous MD5: "..(oldmd5 or "(none)"), true)
12465
13237
  echo(" - you should update yours! Go to Settings -&gt; Mapper tab and click on the 'Download' button there. Once you've updated, ")
12466
- echoLink("click here", "mmp.updatedmap('"..currentmd5.."')", "Click here to quiet the update reminder")
13238
+ echoLink("click here", string.format("mmp.updatedmap(%q, %q)", currentmd5, mmp.mapDownloadIdentity), "Click here to quiet the update reminder")
12467
13239
  echo(" to remove the reminder.")
12468
13240
  else
12469
13241
  mmp.newversion, mmp.oldversion = currentmd5, oldmd5
@@ -12473,6 +13245,10 @@ end</script>
12473
13245
 
12474
13246
  local f, s = io.open(filename)
12475
13247
  if f then s = f:read("*a"); io.close(f) end
13248
+ if mmp.mapDownloadSource == "service" and s:sub(1, 1) == '"' then
13249
+ local ok, decoded = pcall(yajl.to_value, s)
13250
+ if ok and type(decoded) == "string" then s = decoded end
13251
+ end
12476
13252
  local currentmd5 = string.match(s, "([a-z0-9]+) map%.xml")
12477
13253
 
12478
13254
  -- using crowdsourced map
@@ -12489,7 +13265,21 @@ end</script>
12489
13265
  local f, s = io.open(getMudletHomeDir().."/map downloads/current")
12490
13266
  if f then s = f:read("*a"); io.close(f) end
12491
13267
 
12492
- if s ~= currentmd5 then needupdate(currentmd5, s) end
13268
+ local storedVersion, storedIdentity, structuredMarker = s, nil, false
13269
+ local decodedOk, storedMarker = pcall(yajl.to_value, s)
13270
+ if decodedOk and type(storedMarker) == "table" then
13271
+ storedVersion = tostring(storedMarker.version or "")
13272
+ storedIdentity = storedMarker.identity
13273
+ structuredMarker = true
13274
+ end
13275
+
13276
+ if storedVersion ~= currentmd5 or (structuredMarker and storedIdentity ~= mmp.mapDownloadIdentity) then
13277
+ needupdate(currentmd5, storedVersion)
13278
+ elseif not structuredMarker then
13279
+ -- A matching legacy marker is already current. Add its source identity
13280
+ -- silently instead of showing a false one-time update reminder.
13281
+ mmp.writeMapVersion(currentmd5, mmp.mapDownloadIdentity)
13282
+ end
12493
13283
  end
12494
13284
  end</script>
12495
13285
  <eventHandlerList>
@@ -12501,14 +13291,38 @@ end</script>
12501
13291
  <packageName></packageName>
12502
13292
  <script>local downloadfolder = getMudletHomeDir() .. "/map downloads/"
12503
13293
 
13294
+ function mmp.mapSourceIdentity(source)
13295
+ if source == "service" then
13296
+ return "service:" .. tostring(mmp.game) .. ":" .. tostring(mmp.crowdmapService.mapUrl() or "")
13297
+ end
13298
+ return tostring(source) .. ":" .. tostring(mmp.game)
13299
+ end
13300
+
13301
+ function mmp.writeMapVersion(version, identity)
13302
+ local f, err = io.open(downloadfolder .. "current", "w")
13303
+ if not f then
13304
+ return mmp.echo("Couldn't write to the update file, because: " .. err)
13305
+ end
13306
+ f:write(yajl.to_string({ version = tostring(version), identity = identity }))
13307
+ f:close()
13308
+ end
13309
+
12504
13310
  -- this should get called at start and every hour after that
12505
13311
 
12506
13312
  function mmp.checkforupdate()
12507
13313
  if not mmp.game or mmp.checkingupdates then
12508
13314
  return
12509
13315
  end
13316
+ if mmp.settings.mapsource == "service" and not mmp.crowdmapService.reporter() then
13317
+ mmp.crowdmapService.waitingForReporter = true
13318
+ return
13319
+ end
12510
13320
  local game = mmp.game
12511
- mmp.mapfile = downloadfolder .. "MD5"
13321
+ mmp.mapDownloadId = (mmp.mapDownloadId or 0) + 1
13322
+ mmp.mapfile = downloadfolder .. "MD5-" .. mmp.mapDownloadId
13323
+ mmp.mapDownloadSource = mmp.settings.mapsource
13324
+ mmp.mapDownloadGeneration = mmp.mapSourceGeneration or 0
13325
+ mmp.mapDownloadIdentity = mmp.mapSourceIdentity(mmp.mapDownloadSource)
12512
13326
  mmp.mapperfile = downloadfolder .. "mapper"
12513
13327
  if not downloadFile then
12514
13328
  mmp.echo("Your version of Mudlet doesn't support downloading files - please upgrade to 2.0+")
@@ -12529,7 +13343,9 @@ function mmp.checkforupdate()
12529
13343
  return
12530
13344
  end
12531
13345
  end
12532
- if mmp.settings.crowdmap then
13346
+ if mmp.mapDownloadSource == "service" then
13347
+ downloadFile(mmp.mapfile, mmp.crowdmapService.versionUrl())
13348
+ elseif mmp.mapDownloadSource == "published" then
12533
13349
  if game == "achaea" then
12534
13350
  downloadFile(
12535
13351
  mmp.mapfile, "http://ire-mudlet-mapping.github.io/AchaeaCrowdmap/Map/version.txt"
@@ -12575,14 +13391,9 @@ end
12575
13391
 
12576
13392
  -- called by the user when the map is updated to register the fact that it was
12577
13393
 
12578
- function mmp.updatedmap(currentmd5)
13394
+ function mmp.updatedmap(currentmd5, identity)
12579
13395
  assert(currentmd5, "need md5 sum to write to file")
12580
- local f, err = io.open(downloadfolder .. "current", "w")
12581
- if not f then
12582
- return mmp.echo("Couldn't write to the update file, because: " .. err)
12583
- end
12584
- f:write(currentmd5)
12585
- f:close()
13396
+ mmp.writeMapVersion(currentmd5, identity or mmp.mapSourceIdentity(mmp.settings.mapsource))
12586
13397
  local t = {"Go you for updating!", "Thanks for updating the map!", "Alright, map updated!"}
12587
13398
  mmp.echo(t[math.random(1, #t)])
12588
13399
  end
@@ -12597,8 +13408,14 @@ function mmp.retrievechangelog()
12597
13408
  end
12598
13409
 
12599
13410
  function mmp.retrievecrowdchangelog()
12600
- mmp.crowdchangelogfile = downloadfolder .. "crowdchangelogfile"
12601
- if mmp.game == "achaea" then
13411
+ mmp.crowdmapDownloadId = (mmp.crowdmapDownloadId or 0) + 1
13412
+ mmp.crowdchangelogfile = downloadfolder .. "crowdchangelogfile-" .. mmp.crowdmapDownloadId
13413
+ mmp.crowdmapDownloadSource = mmp.settings.mapsource
13414
+ mmp.crowdmapDownloadGeneration = mmp.mapSourceGeneration or 0
13415
+ mmp.crowdmapDownloadIdentity = mmp.mapSourceIdentity(mmp.crowdmapDownloadSource)
13416
+ if mmp.settings.mapsource == "service" then
13417
+ mmp.downloadcrowdmap(mmp.newversion)
13418
+ elseif mmp.game == "achaea" then
12602
13419
  downloadFile(
12603
13420
  mmp.crowdchangelogfile, "http://ire-mudlet-mapping.github.io/AchaeaCrowdmap/Map/changelog.txt"
12604
13421
  )
@@ -12664,14 +13481,19 @@ function mmp.downloadmapperscript()
12664
13481
  end
12665
13482
 
12666
13483
  function mmp.downloadcrowdmap(newversion)
12667
- mmp.crowdmapfile = downloadfolder .. "crowdmap"
12668
- local f, err = io.open(downloadfolder .. "current", "w")
12669
- if not f then
12670
- return mmp.echo("Couldn't write to the update file, because: " .. err)
13484
+ if mmp.settings.mapsource == "service" and not mmp.crowdmapService.reporter() then
13485
+ mmp.crowdmapService.waitingForReporter = true
13486
+ return
12671
13487
  end
12672
- f:write(newversion)
12673
- f:close()
12674
- if mmp.game == "achaea" then
13488
+ mmp.crowdmapDownloadId = (mmp.crowdmapDownloadId or 0) + 1
13489
+ mmp.crowdmapfile = downloadfolder .. "crowdmap-" .. mmp.crowdmapDownloadId
13490
+ mmp.crowdmapDownloadSource = mmp.settings.mapsource
13491
+ mmp.crowdmapDownloadGeneration = mmp.mapSourceGeneration or 0
13492
+ mmp.crowdmapDownloadIdentity = mmp.mapSourceIdentity(mmp.crowdmapDownloadSource)
13493
+ mmp.crowdmapDownloadVersion = newversion
13494
+ if mmp.settings.mapsource == "service" then
13495
+ downloadFile(mmp.crowdmapfile, mmp.crowdmapService.mapUrl())
13496
+ elseif mmp.game == "achaea" then
12675
13497
  downloadFile(mmp.crowdmapfile, "http://ire-mudlet-mapping.github.io/AchaeaCrowdmap/Map/map")
12676
13498
  elseif mmp.game == "starmourn" then
12677
13499
  downloadFile(mmp.crowdmapfile, "http://ire-mudlet-mapping.github.io/StarmournCrowdmap/Map/map")
@@ -12691,6 +13513,10 @@ function mmp.downloadcrowdmap(newversion)
12691
13513
  mmp.echo("Downloading the latest crowdmap...")
12692
13514
  end
12693
13515
 
13516
+ function mmp.recordCrowdmapVersion(version, identity)
13517
+ mmp.writeMapVersion(version, identity or mmp.crowdmapDownloadIdentity)
13518
+ end
13519
+
12694
13520
  function mmp.showcrowdchangelog()
12695
13521
  mmp.echo("Public map changelog:")
12696
13522
  if not mmp.crowdchangelog then