Add essence upgrade recipes

This commit is contained in:
Linnea Gräf
2024-07-24 02:22:30 +02:00
parent 4585a11434
commit c7143936d7
10 changed files with 242 additions and 21 deletions

View File

@@ -31,6 +31,7 @@ import moe.nea.firmament.events.HandledScreenPushREIEvent
import moe.nea.firmament.features.inventory.CraftingOverlay import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
import moe.nea.firmament.rei.recipes.SBCraftingRecipe import moe.nea.firmament.rei.recipes.SBCraftingRecipe
import moe.nea.firmament.rei.recipes.SBEssenceUpgradeRecipe
import moe.nea.firmament.rei.recipes.SBForgeRecipe import moe.nea.firmament.rei.recipes.SBForgeRecipe
import moe.nea.firmament.rei.recipes.SBKatRecipe import moe.nea.firmament.rei.recipes.SBKatRecipe
import moe.nea.firmament.rei.recipes.SBMobDropRecipe import moe.nea.firmament.rei.recipes.SBMobDropRecipe
@@ -72,6 +73,7 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.add(SBForgeRecipe.Category) registry.add(SBForgeRecipe.Category)
registry.add(SBMobDropRecipe.Category) registry.add(SBMobDropRecipe.Category)
registry.add(SBKatRecipe.Category) registry.add(SBKatRecipe.Category)
registry.add(SBEssenceUpgradeRecipe.Category)
} }
override fun registerExclusionZones(zones: ExclusionZones) { override fun registerExclusionZones(zones: ExclusionZones) {
@@ -92,6 +94,10 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.registerDisplayGenerator( registry.registerDisplayGenerator(
SBKatRecipe.Category.categoryIdentifier, SBKatRecipe.Category.categoryIdentifier,
SkyblockKatRecipeDynamicGenerator) SkyblockKatRecipeDynamicGenerator)
registry.registerDisplayGenerator(
SBEssenceUpgradeRecipe.Category.categoryIdentifier,
SkyblockEssenceRecipeDynamicGenerator
)
} }
override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) { override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {

View File

@@ -22,6 +22,7 @@ import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.registry.tag.TagKey import net.minecraft.registry.tag.TagKey
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Formatting
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.repo.ExpLadders import moe.nea.firmament.repo.ExpLadders
@@ -30,10 +31,13 @@ import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.HypixelPetInfo import moe.nea.firmament.util.HypixelPetInfo
import moe.nea.firmament.util.LegacyFormattingCode
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.appendLore import moe.nea.firmament.util.appendLore
import moe.nea.firmament.util.item.displayNameAccordingToNbt
import moe.nea.firmament.util.petData import moe.nea.firmament.util.petData
import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyBlockId
import moe.nea.firmament.util.withColor
// TODO: add in extra data like pet info, into this structure // TODO: add in extra data like pet info, into this structure
data class PetData( data class PetData(
@@ -46,6 +50,7 @@ data class PetData(
fun fromHypixel(petInfo: HypixelPetInfo) = PetData( fun fromHypixel(petInfo: HypixelPetInfo) = PetData(
petInfo.tier, petInfo.type, petInfo.exp, petInfo.tier, petInfo.type, petInfo.exp,
) )
fun forLevel(petId: String, rarity: Rarity, level: Int) = PetData( fun forLevel(petId: String, rarity: Rarity, level: Int) = PetData(
rarity, petId, ExpLadders.getExpLadder(petId, rarity).getPetExpForLevel(level).toDouble() rarity, petId, ExpLadders.getExpLadder(petId, rarity).getPetExpForLevel(level).toDouble()
) )
@@ -60,6 +65,8 @@ data class SBItemStack(
private var stackSize: Int, private var stackSize: Int,
private var petData: PetData?, private var petData: PetData?,
val extraLore: List<Text> = emptyList(), val extraLore: List<Text> = emptyList(),
// TODO: grab this star data from nbt if possible
val stars: Int = 0,
) { ) {
fun getStackSize() = stackSize fun getStackSize() = stackSize
@@ -67,6 +74,7 @@ data class SBItemStack(
this.stackSize = stackSize this.stackSize = stackSize
this.itemStack_ = null this.itemStack_ = null
} }
fun getPetData() = petData fun getPetData() = petData
fun setPetData(petData: PetData?) { fun setPetData(petData: PetData?) {
this.petData = petData this.petData = petData
@@ -132,12 +140,42 @@ data class SBItemStack(
injectReplacementDataForPets(replacementData) injectReplacementDataForPets(replacementData)
return@run neuItem.asItemStack(idHint = skyblockId, replacementData).copyWithCount(stackSize) return@run neuItem.asItemStack(idHint = skyblockId, replacementData).copyWithCount(stackSize)
.also { it.appendLore(extraLore) } .also { it.appendLore(extraLore) }
.also { enhanceStatsByStars(it, stars) }
} }
if (itemStack_ == null) if (itemStack_ == null)
itemStack_ = itemStack itemStack_ = itemStack
return itemStack return itemStack
} }
private fun starString(stars: Int): Text {
if (stars <= 0) return Text.empty()
val tiers = listOf(
LegacyFormattingCode.GOLD,
LegacyFormattingCode.LIGHT_PURPLE,
LegacyFormattingCode.AQUA,
)
val maxStars = 5
if (stars > tiers.size * maxStars) return Text.literal(" ${stars}").withColor(Formatting.RED)
val starBaseTier = (stars - 1) / maxStars
val starBaseColor = tiers[starBaseTier]
val starsInCurrentTier = stars - starBaseTier * maxStars
val starString = Text.literal(" " + "".repeat(starsInCurrentTier)).withColor(starBaseColor.modern)
if (starBaseTier > 0) {
val starLastTier = tiers[starBaseTier - 1]
val starsInLastTier = 5 - starsInCurrentTier
starString.append(Text.literal("".repeat(starsInLastTier)).withColor(starLastTier.modern))
}
return starString
}
private fun enhanceStatsByStars(itemStack: ItemStack, stars: Int) {
if (stars == 0) return
// TODO: increase stats and add the star level into the nbt data so star displays work
itemStack.displayNameAccordingToNbt = itemStack.displayNameAccordingToNbt.copy()
.append(starString(stars))
}
fun asImmutableItemStack(): ItemStack { fun asImmutableItemStack(): ItemStack {
return itemStack return itemStack
} }
@@ -179,11 +217,11 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
} }
override fun wildcard(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack { override fun wildcard(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack {
return value.copy(stackSize = 1) return value.copy(stackSize = 1, petData = null, stars = 0, extraLore = listOf())
} }
override fun normalize(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack { override fun normalize(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack {
return value.copy(stackSize = 1) return wildcard(entry, value)
} }
override fun copy(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack { override fun copy(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack {

View File

@@ -18,9 +18,11 @@ import me.shedaniel.rei.api.client.view.ViewSearchBuilder
import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.display.Display
import me.shedaniel.rei.api.common.entry.EntryStack import me.shedaniel.rei.api.common.entry.EntryStack
import moe.nea.firmament.rei.recipes.SBCraftingRecipe import moe.nea.firmament.rei.recipes.SBCraftingRecipe
import moe.nea.firmament.rei.recipes.SBEssenceUpgradeRecipe
import moe.nea.firmament.rei.recipes.SBForgeRecipe import moe.nea.firmament.rei.recipes.SBForgeRecipe
import moe.nea.firmament.rei.recipes.SBKatRecipe import moe.nea.firmament.rei.recipes.SBKatRecipe
import moe.nea.firmament.rei.recipes.SBMobDropRecipe import moe.nea.firmament.rei.recipes.SBMobDropRecipe
import moe.nea.firmament.repo.EssenceRecipeProvider
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
@@ -35,6 +37,8 @@ val SkyblockMobDropRecipeDynamicGenerator =
val SkyblockKatRecipeDynamicGenerator = val SkyblockKatRecipeDynamicGenerator =
neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) } neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) }
val SkyblockEssenceRecipeDynamicGenerator =
neuDisplayGenerator<SBEssenceUpgradeRecipe, EssenceRecipeProvider.EssenceUpgradeRecipe> { SBEssenceUpgradeRecipe(it) }
inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(crossinline mapper: (T) -> D) = inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(crossinline mapper: (T) -> D) =
object : DynamicDisplayGenerator<D> { object : DynamicDisplayGenerator<D> {

View File

@@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.rei.recipes
import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle
import me.shedaniel.rei.api.client.gui.Renderer
import me.shedaniel.rei.api.client.gui.widgets.Widget
import me.shedaniel.rei.api.client.gui.widgets.Widgets
import me.shedaniel.rei.api.client.registry.display.DisplayCategory
import me.shedaniel.rei.api.common.category.CategoryIdentifier
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
import moe.nea.firmament.rei.SBItemEntryDefinition
import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.repo.EssenceRecipeProvider
import moe.nea.firmament.util.SkyblockId
class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() {
object Category : DisplayCategory<SBEssenceUpgradeRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade")
override fun getTitle(): Text {
return Text.literal("Essence Upgrades")
}
override fun getIcon(): Renderer {
return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER"))
}
override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> {
val recipe = display.neuRecipe
val list = mutableListOf<Widget>()
list.add(Widgets.createRecipeBase(bounds))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2))
.markInput()
.entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1))))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2))
.markInput()
.entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient)))
list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8))
.markOutput()
.entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter))))
val extraItems = recipe.extraItems
list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2,
if (extraItems.isEmpty()) bounds.centerY - 17 / 2
else bounds.centerY + 18 / 2)))
for ((index, item) in extraItems.withIndex()) {
list.add(Widgets.createSlot(
Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18,
bounds.centerY - 18 / 2))
.markInput()
.entry(SBItemEntryDefinition.getEntry(item)))
}
return list
}
}
override fun getCategoryIdentifier(): CategoryIdentifier<*> {
return Category.categoryIdentifier
}
}

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.repo
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEURecipe
import moe.nea.firmament.util.SkyblockId
class BetterRepoRecipeCache(val essenceRecipeProvider: EssenceRecipeProvider) : IReloadable {
var usages: Map<SkyblockId, Set<NEURecipe>> = mapOf()
var recipes: Map<SkyblockId, Set<NEURecipe>> = mapOf()
override fun reload(repository: NEURepository) {
val usages = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
val recipes = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
val baseRecipes = repository.items.items.values
.asSequence()
.flatMap { it.recipes }
val extraRecipes = essenceRecipeProvider.recipes
(baseRecipes + extraRecipes)
.forEach { recipe ->
recipe.allInputs.forEach { usages.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
recipe.allOutputs.forEach { recipes.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
}
this.usages = usages
this.recipes = recipes
}
}

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.repo
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEURecipe
import moe.nea.firmament.util.SkyblockId
class EssenceRecipeProvider : IReloadable {
data class EssenceUpgradeRecipe(
val itemId: SkyblockId,
val starCountAfter: Int,
val essenceCost: Int,
val essenceType: String, // TODO: replace with proper type
val extraItems: List<NEUIngredient>,
) : NEURecipe {
val essenceIngredient= NEUIngredient.fromString("${essenceType}:$essenceCost")
val allUpgradeComponents = listOf(essenceIngredient) + extraItems
override fun getAllInputs(): Collection<NEUIngredient> {
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1")) + allUpgradeComponents
}
override fun getAllOutputs(): Collection<NEUIngredient> {
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1"))
}
}
var recipes = listOf<EssenceUpgradeRecipe>()
private set
override fun reload(repository: NEURepository) {
val recipes = mutableListOf<EssenceUpgradeRecipe>()
for ((neuId, costs) in repository.constants.essenceCost.costs) {
for ((starCountAfter, essenceCost) in costs.essenceCosts.entries) {
val items = costs.itemCosts[starCountAfter] ?: emptyList()
recipes.add(
EssenceUpgradeRecipe(
SkyblockId(neuId),
starCountAfter,
essenceCost,
"ESSENCE_" + costs.type.uppercase(), // how flimsy
items.map { NEUIngredient.fromString(it) }))
}
}
this.recipes = recipes
}
}

View File

@@ -1,28 +1,34 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
* *
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
package moe.nea.firmament.repo package moe.nea.firmament.repo
import io.ktor.client.call.* import io.ktor.client.call.body
import io.ktor.client.request.* import io.ktor.client.request.get
import io.ktor.client.statement.* import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.nio.* import io.ktor.utils.io.jvm.nio.copyTo
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.util.iterate
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.io.path.* import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlin.io.path.createDirectories
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.outputStream
import kotlin.io.path.readText
import kotlin.io.path.writeText
import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.util.iterate
object RepoDownloadManager { object RepoDownloadManager {
@@ -55,6 +61,9 @@ object RepoDownloadManager {
private class GithubCommitsResponse(val sha: String) private class GithubCommitsResponse(val sha: String)
private suspend fun requestLatestGithubSha(): String? { private suspend fun requestLatestGithubSha(): String? {
if (RepoManager.Config.branch == "prerelease") {
RepoManager.Config.branch = "master"
}
val response = val response =
Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${RepoManager.Config.branch}") Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${RepoManager.Config.branch}")
if (response.status.value != 200) { if (response.status.value != 200) {

View File

@@ -1,5 +1,6 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
* *
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
@@ -28,12 +29,12 @@ object RepoManager {
object Config : ManagedConfig("repo") { object Config : ManagedConfig("repo") {
var username by string("username") { "NotEnoughUpdates" } var username by string("username") { "NotEnoughUpdates" }
var reponame by string("reponame") { "NotEnoughUpdates-REPO" } var reponame by string("reponame") { "NotEnoughUpdates-REPO" }
var branch by string("branch") { "prerelease" } var branch by string("branch") { "master" }
val autoUpdate by toggle("autoUpdate") { true } val autoUpdate by toggle("autoUpdate") { true }
val reset by button("reset") { val reset by button("reset") {
username = "NotEnoughUpdates" username = "NotEnoughUpdates"
reponame = "NotEnoughUpdates-REPO" reponame = "NotEnoughUpdates-REPO"
branch = "prerelease" branch = "master"
save() save()
} }
@@ -66,12 +67,17 @@ object RepoManager {
} }
} }
private val recipeCache = NEURecipeCache.forRepo(neuRepo) val essenceRecipeProvider = EssenceRecipeProvider()
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider)
init {
neuRepo.registerReloadListener(essenceRecipeProvider)
neuRepo.registerReloadListener(recipeCache)
}
fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes }
fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId.neuItem] ?: setOf() fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId] ?: setOf()
fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId.neuItem] ?: setOf() fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId] ?: setOf()
private fun trySendClientboundUpdateRecipesPacket(): Boolean { private fun trySendClientboundUpdateRecipesPacket(): Boolean {
return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes(

View File

@@ -18,8 +18,11 @@ var ItemStack.loreAccordingToNbt
set(DataComponentTypes.LORE, LoreComponent(value)) set(DataComponentTypes.LORE, LoreComponent(value))
} }
val ItemStack.displayNameAccordingToNbt var ItemStack.displayNameAccordingToNbt: Text
get() = get(DataComponentTypes.CUSTOM_NAME) ?: get(DataComponentTypes.ITEM_NAME) ?: item.name get() = get(DataComponentTypes.CUSTOM_NAME) ?: get(DataComponentTypes.ITEM_NAME) ?: item.name
set(value) {
set(DataComponentTypes.CUSTOM_NAME, value)
}
fun ItemStack.setCustomName(text: Text) { fun ItemStack.setCustomName(text: Text) {
set(DataComponentTypes.CUSTOM_NAME, text) set(DataComponentTypes.CUSTOM_NAME, text)

View File

@@ -9,6 +9,7 @@ package moe.nea.firmament.util
import net.minecraft.text.MutableText import net.minecraft.text.MutableText
import net.minecraft.text.PlainTextContent import net.minecraft.text.PlainTextContent
import net.minecraft.text.Style
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.text.TranslatableTextContent import net.minecraft.text.TranslatableTextContent
import net.minecraft.util.Formatting import net.minecraft.util.Formatting
@@ -98,7 +99,7 @@ val Text.unformattedString: String
get() = string.removeColorCodes() get() = string.removeColorCodes()
fun MutableText.withColor(formatting: Formatting) = this.styled { it.withColor(formatting) } fun MutableText.withColor(formatting: Formatting) = this.styled { it.withColor(formatting).withItalic(false) }
fun Text.transformEachRecursively(function: (Text) -> Text): Text { fun Text.transformEachRecursively(function: (Text) -> Text): Text {
val c = this.content val c = this.content