package moe.nea.firmament.features.world import com.mojang.brigadier.arguments.StringArgumentType import kotlinx.serialization.serializer import net.minecraft.text.Text import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.DefaultSource import moe.nea.firmament.commands.RestArgumentType import moe.nea.firmament.commands.get import moe.nea.firmament.commands.suggestsList import moe.nea.firmament.commands.thenArgument import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.commands.thenLiteral import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.util.ClipboardUtils import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.MC import moe.nea.firmament.util.TemplateUtil import moe.nea.firmament.util.data.MultiFileDataHolder import moe.nea.firmament.util.tr object FirmWaypointManager { object DataHolder : MultiFileDataHolder(serializer(), "waypoints") val SHARE_PREFIX = "FIRM_WAYPOINTS/" val ENCODED_SHARE_PREFIX = TemplateUtil.getPrefixComparisonSafeBase64Encoding(SHARE_PREFIX) fun createExportableCopy( waypoints: FirmWaypoints, ): FirmWaypoints { val copy = waypoints.copy(waypoints = waypoints.waypoints.toMutableList()) if (waypoints.isRelativeTo != null) { val origin = waypoints.lastRelativeImport if (origin != null) { copy.waypoints.replaceAll { it.copy( x = it.x - origin.x, y = it.y - origin.y, z = it.z - origin.z, ) } } else { TODO("Add warning!") } } return copy } fun loadWaypoints(waypoints: FirmWaypoints, sendFeedback: (Text) -> Unit) { val copy = waypoints.deepCopy() if (copy.isRelativeTo != null) { val origin = MC.player!!.blockPos copy.waypoints.replaceAll { it.copy( x = it.x + origin.x, y = it.y + origin.y, z = it.z + origin.z, ) } copy.lastRelativeImport = origin.toImmutable() sendFeedback(tr("firmament.command.waypoint.import.ordered.success", "Imported ${copy.size} relative waypoints. Make sure you stand in the correct spot while loading the waypoints: ${copy.isRelativeTo}.")) } else { sendFeedback(tr("firmament.command.waypoint.import.success", "Imported ${copy.size} waypoints.")) } Waypoints.waypoints = copy } fun setOrigin(source: DefaultSource, text: String?) { val waypoints = Waypoints.useEditableWaypoints() waypoints.isRelativeTo = text ?: waypoints.isRelativeTo ?: "" val pos = MC.player!!.blockPos waypoints.lastRelativeImport = pos source.sendFeedback(tr("firmament.command.waypoint.originset", "Set the origin of waypoints to ${FirmFormatters.formatPosition(pos)}. Run /firm waypoints export to save the waypoints relative to this position.")) } @Subscribe fun onCommands(event: CommandEvent.SubCommand) { event.subcommand(Waypoints.WAYPOINTS_SUBCOMMAND) { thenLiteral("setorigin") { thenExecute { setOrigin(source, null) } thenArgument("hint", RestArgumentType) { text -> thenExecute { setOrigin(source, this[text]) } } } thenLiteral("clearorigin") { thenExecute { val waypoints = Waypoints.useEditableWaypoints() waypoints.lastRelativeImport = null waypoints.isRelativeTo = null source.sendFeedback(tr("firmament.command.waypoint.originunset", "Unset the origin of the waypoints. Run /firm waypoints export to save the waypoints with absolute coordinates.")) } } thenLiteral("save") { thenArgument("name", StringArgumentType.string()) { name -> suggestsList { DataHolder.list().keys } thenExecute { val waypoints = Waypoints.useNonEmptyWaypoints() if (waypoints == null) { source.sendError(Waypoints.textNothingToExport()) return@thenExecute } waypoints.id = get(name) val exportableWaypoints = createExportableCopy(waypoints) DataHolder.insert(get(name), exportableWaypoints) DataHolder.save() source.sendFeedback(tr("firmament.command.waypoint.saved", "Saved waypoints locally as ${get(name)}. Use /firm waypoints load to load them again.")) } } } thenLiteral("load") { thenArgument("name", StringArgumentType.string()) { name -> suggestsList { DataHolder.list().keys } thenExecute { val name = get(name) val waypoints = DataHolder.list()[name] if (waypoints == null) { source.sendError( tr("firmament.command.waypoint.nosaved", "No saved waypoint for ${name}. Use tab completion to see available names.")) return@thenExecute } loadWaypoints(waypoints, source::sendFeedback) } } } thenLiteral("export") { thenExecute { val waypoints = Waypoints.useNonEmptyWaypoints() if (waypoints == null) { source.sendError(Waypoints.textNothingToExport()) return@thenExecute } val exportableWaypoints = createExportableCopy(waypoints) val data = TemplateUtil.encodeTemplate(SHARE_PREFIX, exportableWaypoints) ClipboardUtils.setTextContent(data) source.sendFeedback(tr("firmament.command.waypoint.export", "Copied ${exportableWaypoints.size} waypoints to clipboard in Firmament format.")) } } thenLiteral("import") { thenExecute { val text = ClipboardUtils.getTextContents() if (text.startsWith("[")) { source.sendError(tr("firmament.command.waypoint.import.lookslikecw", "The waypoints in your clipboard look like they might be ColeWeight waypoints. If so, use /firm waypoints importcw or /firm waypoints importrelativecw.")) return@thenExecute } val waypoints = TemplateUtil.maybeDecodeTemplate(SHARE_PREFIX, text) if (waypoints == null) { source.sendError(tr("firmament.command.waypoint.import.error", "Could not import Firmament waypoints from your clipboard. Make sure they are Firmament compatible waypoints.")) return@thenExecute } loadWaypoints(waypoints, source::sendFeedback) } } } } }