Compare commits

...
Author SHA1 Message Date
Licious aeedec0b6d Make starters work from datapacks instead of config. 2024-04-03 12:24:06 +01:00
27 changed files with 352 additions and 345 deletions
@@ -67,24 +67,13 @@ import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFa
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.ServerTickHandler
@@ -105,13 +94,7 @@ import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -132,13 +115,16 @@ import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.collections.filterIsInstance
import kotlin.collections.forEach
import kotlin.collections.listOf
import kotlin.collections.set
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.math.roundToInt
object Cobblemon {
const val MODID = "cobblemon"
@@ -166,7 +152,6 @@ object Cobblemon {
var storage = PokemonStoreManager()
var molangData = NbtMoLangDataStoreFactory
lateinit var playerData: PlayerDataStoreManager
lateinit var starterConfig: StarterConfig
val dataProvider: DataProvider = CobblemonDataProvider
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
var statProvider: StatProvider = CobblemonStatProvider
@@ -477,27 +462,6 @@ object Cobblemon {
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
if (config.exportStarterConfig) {
val file = File("config/cobblemon/starters.json")
file.parentFile.mkdirs()
if (!file.exists()) {
val config = StarterConfig()
val pw = PrintWriter(file)
StarterConfig.GSON.toJson(config, pw)
pw.close()
return config
}
val reader = FileReader(file)
val config = StarterConfig.GSON.fromJson(reader, StarterConfig::class.java)
reader.close()
return config
} else {
return StarterConfig()
}
}
fun saveConfig() {
@@ -6,24 +6,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.config.starter
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.text.Text
data class StarterCategory(
val name: String,
val displayName: String,
val displayName: Text,
val pokemon: List<PokemonProperties>
) {
fun asRenderableStarterCategory() = RenderableStarterCategory(name, displayName, pokemon.map { it.asRenderablePokemon() })
}
)
data class RenderableStarterCategory(
val name: String,
val displayName: String,
val displayName: Text,
val pokemon: List<RenderablePokemon>
) {
val displayNameText = displayName.asTranslated()
}
)
@@ -8,12 +8,12 @@
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.config.starter.StarterCategory
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
interface StarterHandler {
fun getStarterList(player: ServerPlayerEntity): List<StarterCategory>
fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory>
fun handleJoin(player: ServerPlayerEntity)
fun requestStarterChoice(player: ServerPlayerEntity)
fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int)
fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int)
}
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.adapters.PlainJsonTextAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* The registry for starter Pokémon.
*
* The data here is used by the [CobblemonStarterHandler].
*
* 3rd party is not enforced to respect this registry.
*/
object StarterRegistry : JsonDataRegistry<StarterCategory> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(Text::class.java, PlainJsonTextAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
override val typeToken: TypeToken<StarterCategory> = TypeToken.get(StarterCategory::class.java)
override val id: Identifier = cobblemonResource("starter")
override val resourcePath: String = id.path
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable: SimpleObservable<out DataRegistry> = SimpleObservable()
private val categories = hashMapOf<Identifier, StarterCategory>()
// We let the default starter handler sync the client
// This is to prevent unnecessary packets depending on impl
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, StarterCategory>) {
this.categories.clear()
this.categories += data
Cobblemon.LOGGER.info("Loaded {} starters ({} categories)", this.categories.values.sumOf { it.pokemon.size }, this.categories.size)
}
fun all(): Map<Identifier, StarterCategory> = this.categories
fun keys(): Set<Identifier> = this.categories.keys
fun categories(): Collection<StarterCategory> = this.categories.values
fun categoryOf(key: Identifier): StarterCategory? = this.categories[key]
fun categoryOf(key: String): StarterCategory? = this.categories[key.asIdentifierDefaultingNamespace()]
}
@@ -37,7 +37,7 @@ data class PlayerData(
fun defaultData(forPlayer: UUID) = PlayerData(
uuid = forPlayer,
starterPrompted = false,
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
starterLocked = !Cobblemon.config.allowStarterOnJoin,
starterSelected = false,
starterUUID = null,
keyItems = mutableSetOf(),
@@ -27,7 +27,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.type.DualTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.SingleTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.TypeWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
@@ -40,6 +40,7 @@ import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.toast.Toast
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Starterselection Screen Thingy
@@ -47,7 +48,7 @@ import net.minecraft.text.Text
* @author Qu
* @since 2022-06-18
*/
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
companion object {
// Size of UI at scale 1
@@ -64,8 +65,9 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
}
private val categories = categories.toSortedMap { a, b -> a.compareTo(b) }
private var currentSelection = 0
private lateinit var currentCategory: RenderableStarterCategory
private lateinit var currentCategoryId: Identifier
private lateinit var modelWidget: ModelWidget
private lateinit var currentPokemon: RenderablePokemon
private lateinit var typeWidget: TypeWidget
@@ -95,7 +97,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
paneWidth = 71, paneHeight = BASE_HEIGHT - 11,
topOffset = 6, bottomOffset = 5,
entryHeight = 20, entryWidth = 57,
categories = categories,
categories = this.categories,
x = x - 2, y = y + 8,
starterSelectionScreen = this
)
@@ -120,8 +122,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
addDrawableChild(rightButton)
addDrawableChild(leftButton)
currentCategory = categories.first()
currentPokemon = currentCategory.pokemon[currentSelection]
currentCategoryId = categories.firstKey()
currentPokemon = this.currentCategory().pokemon[currentSelection]
with(currentPokemon) {
modelWidget = ModelWidget(
@@ -142,11 +144,11 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
) {
CobblemonNetwork.sendPacketToServer(
SelectStarterPacket(
categoryName = currentCategory.name,
categoryId = currentCategoryId,
selected = currentSelection
)
)
MinecraftClient.getInstance().setScreen(null)
this.close()
}
addDrawableChild(selectionButton)
@@ -161,7 +163,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
starterRoundaboutLeft = StarterRoundabout(
pX = x + 89, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()],
clickAction = { _, _ -> this.left() },
rotationVector = this.modelWidget.rotVec
)
@@ -169,7 +171,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
starterRoundaboutRight = StarterRoundabout(
pX = x + 149, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()],
clickAction = { _, _ -> this.right() },
rotationVector = this.modelWidget.rotVec
)
@@ -276,8 +278,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
super.render(context, mouseX, mouseY, delta)
}
fun changeCategory(category: RenderableStarterCategory) {
currentCategory = category
fun changeCategory(id: Identifier) {
currentCategoryId = id
currentSelection = 0
updateSelection()
}
@@ -288,7 +290,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
updateSelection()
}
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= currentCategory.pokemon.size - 1) currentSelection + 1 else 0
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= this.currentCategory().pokemon.size - 1) currentSelection + 1 else 0
private fun left() {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
@@ -296,16 +298,16 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
updateSelection()
}
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) currentCategory.pokemon.size - 1 else currentSelection - 1
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) this.currentCategory().pokemon.size - 1 else currentSelection - 1
private fun updateSelection() {
currentPokemon = currentCategory.pokemon[currentSelection].also {
currentPokemon = this.currentCategory().pokemon[currentSelection].also {
modelWidget.pokemon = it
typeWidget = typeWidget(it, (width - BASE_WIDTH) / 2, (height - BASE_HEIGHT) / 2)
}
starterRoundaboutLeft.pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
starterRoundaboutLeft.pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()]
starterRoundaboutCenter.pokemon = currentPokemon
starterRoundaboutRight.pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
starterRoundaboutRight.pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()]
}
private fun typeWidget(pokemon: RenderablePokemon, x: Int, y: Int) : TypeWidget {
@@ -324,5 +326,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
)
}
private fun currentCategory(): RenderableStarterCategory = this.categories[this.currentCategoryId]!!
override fun shouldPause() = true
}
@@ -12,14 +12,14 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class CategoryList(
private val paneWidth: Int,
@@ -28,7 +28,7 @@ class CategoryList(
bottomOffset: Int,
private val entryWidth: Int,
entryHeight: Int,
private val categories: List<RenderableStarterCategory>,
private val categories: Map<Identifier, RenderableStarterCategory>,
val x: Int, val y: Int,
private val minecraft: MinecraftClient = MinecraftClient.getInstance(),
private val starterSelectionScreen: StarterSelectionScreen
@@ -44,7 +44,6 @@ class CategoryList(
companion object {
private const val CATEGORY_BUTTON_WIDTH = 51.5f
private const val CATEGORY_BUTTON_HEIGHT = 16f
private const val ENTRY_X_OFFSET = 10f
private val categoryResource = cobblemonResource("textures/gui/starterselection/starterselection_slot.png")
}
@@ -53,19 +52,12 @@ class CategoryList(
this.setRenderHorizontalShadows(false)
this.setRenderBackground(false)
this.setRenderSelection(false)
}
private var entriesCreated = false
private fun createEntries() = categories.map {
Category(it)
this.categories.forEach { (id, category) ->
this.addEntry(Category(id, category))
}
}
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
if (!entriesCreated) {
createEntries().forEach { addEntry(it) }
entriesCreated = true
}
context.enableScissor(
x,
y,
@@ -80,15 +72,13 @@ class CategoryList(
this.updateSize(this.paneWidth, this.paneHeight, this.y, this.y + this.paneHeight)
this.setLeftPos(this.x)
}
private fun scale(n: Int): Int = (this.client.window.scaleFactor * n).toInt()
override fun getRowWidth() = this.entryWidth
override fun getScrollbarPositionX(): Int {
return this.left + this.width - 5
}
inner class Category(private val category: RenderableStarterCategory) : Entry<Category>() {
inner class Category(private val id: Identifier, private val category: RenderableStarterCategory) : Entry<Category>() {
override fun render(
context: DrawContext,
@@ -121,7 +111,7 @@ class CategoryList(
)
drawScaledText(
context = context,
text = category.displayNameText,
text = category.displayName.copy(),
x = x + 28,
y = y + 4.5F,
scale = 1F,
@@ -132,7 +122,7 @@ class CategoryList(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
starterSelectionScreen.changeCategory(category = category)
starterSelectionScreen.changeCategory(this.id)
minecraft.soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
return true
}
@@ -9,11 +9,7 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command.SINGLE_SUCCESS
import com.mojang.brigadier.CommandDispatcher
@@ -38,18 +34,7 @@ object OpenStarterScreenCommand {
private fun execute(context: CommandContext<ServerCommandSource>) : Int {
val player = EntityArgumentType.getPlayer(context, "player")
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
context.source.sendFeedback({ lang("ui.starter.hasalreadychosen", player.name).red() }, true)
return 0
}
if (playerData.starterLocked) {
playerData.starterLocked = false
playerData.sendToPlayer(player)
}
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
player.sendPacket(OpenStarterUIPacket(Cobblemon.starterHandler.getStarterList(player)))
Cobblemon.starterHandler.requestStarterChoice(player)
return SINGLE_SUCCESS
}
@@ -113,6 +113,12 @@ class CobblemonConfig {
@NodeCategory(Category.Starter)
var exportStarterConfig = false
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
var promptStarterOnceOnly = true
@NodeCategory(Category.Battles)
var autoUpdateShowdown = true
@@ -1,128 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.config.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.NodeCategory
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.google.gson.GsonBuilder
class StarterConfig {
companion object {
val GSON = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
}
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
@LastChangedVersion("1.5.0")
var promptStarterOnceOnly = true
@NodeCategory(Category.Starter)
var starters = mutableListOf(
StarterCategory(
name = "Kanto",
displayName = "cobblemon.starterselection.category.kanto",
pokemon = mutableListOf(
PokemonProperties.parse("Bulbasaur level=10"),
PokemonProperties.parse("Charmander level=10"),
PokemonProperties.parse("Squirtle level=10"),
)
),
StarterCategory(
name = "Johto",
displayName = "cobblemon.starterselection.category.johto",
pokemon = mutableListOf(
PokemonProperties.parse("Chikorita level=10"),
PokemonProperties.parse("Cyndaquil level=10"),
PokemonProperties.parse("Totodile level=10"),
)
),
StarterCategory(
name = "Hoenn",
displayName = "cobblemon.starterselection.category.hoenn",
pokemon = mutableListOf(
PokemonProperties.parse("Treecko level=10"),
PokemonProperties.parse("Torchic level=10"),
PokemonProperties.parse("Mudkip level=10"),
)
),
StarterCategory(
name = "Sinnoh",
displayName = "cobblemon.starterselection.category.sinnoh",
pokemon = mutableListOf(
PokemonProperties.parse("Turtwig level=10"),
PokemonProperties.parse("Chimchar level=10"),
PokemonProperties.parse("Piplup level=10"),
)
),
StarterCategory(
name = "Unova",
displayName = "cobblemon.starterselection.category.unova",
pokemon = mutableListOf(
PokemonProperties.parse("Snivy level=10"),
PokemonProperties.parse("Tepig level=10"),
PokemonProperties.parse("Oshawott level=10"),
)
),
StarterCategory(
name = "Kalos",
displayName = "cobblemon.starterselection.category.kalos",
pokemon = mutableListOf(
PokemonProperties.parse("Chespin level=10"),
PokemonProperties.parse("Fennekin level=10"),
PokemonProperties.parse("Froakie level=10"),
)
),
StarterCategory(
name = "Alola",
displayName = "cobblemon.starterselection.category.alola",
pokemon = mutableListOf(
PokemonProperties.parse("Rowlet level=10"),
PokemonProperties.parse("Litten level=10"),
PokemonProperties.parse("Popplio level=10"),
)
),
StarterCategory(
name = "Galar",
displayName = "cobblemon.starterselection.category.galar",
pokemon = mutableListOf(
PokemonProperties.parse("Grookey level=10"),
PokemonProperties.parse("Scorbunny level=10"),
PokemonProperties.parse("Sobble level=10"),
)
),
StarterCategory(
name = "Paldea",
displayName = "cobblemon.starterselection.category.paldea",
pokemon = mutableListOf(
PokemonProperties.parse("Sprigatito level=10"),
PokemonProperties.parse("Fuecoco level=10"),
PokemonProperties.parse("Quaxly level=10"),
)
)
//,
// StarterCategory(
// name = "Johto",
// displayName = lang("starterselection.category.johto"),
// pokemon = mutableListOf(
// PokemonProperties().also { it.level = 5 ; it.species = "Chikorita" },
// PokemonProperties().also { it.level = 5 ; it.species = "Cyndaquil" },
// PokemonProperties().also { it.level = 5 ; it.species = "Totodile" }
// )
// )
)
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
@@ -63,6 +64,7 @@ object CobblemonDataProvider : DataProvider {
this.register(Abilities)
this.register(PokemonSpecies)
this.register(SpeciesAdditions)
this.register(StarterRegistry)
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
@@ -9,48 +9,33 @@
package com.cobblemon.mod.common.net.messages.client.starter
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.config.starter.StarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class OpenStarterUIPacket internal constructor(val categories: List<RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
class OpenStarterUIPacket(val categories: Map<Identifier, RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
override val id = ID
constructor(categories: Collection<StarterCategory>) : this(categories.map { it.asRenderableStarterCategory() })
override fun encode(buffer: PacketByteBuf) {
buffer.writeInt(categories.size)
categories.forEach {
buffer.writeString(it.name)
buffer.writeString(it.displayName)
buffer.writeInt(it.pokemon.size)
it.pokemon.forEach { it.saveToBuffer(buffer) }
buffer.writeMap(this.categories, PacketByteBuf::writeIdentifier) { categoryBuffer, category ->
categoryBuffer.writeText(category.displayName)
categoryBuffer.writeCollection(category.pokemon) { pokemonBuffer, renderable ->
renderable.saveToBuffer(pokemonBuffer)
}
}
}
companion object {
val ID = cobblemonResource("open_starter")
fun decode(buffer: PacketByteBuf): OpenStarterUIPacket {
val numCategories = buffer.readInt()
val categories = arrayListOf<RenderableStarterCategory>()
for (i in 0 until numCategories) {
val name = buffer.readString()
val displayName = buffer.readString()
val numProperties = buffer.readInt()
val renderablePokemon = mutableListOf<RenderablePokemon>()
repeat(times = numProperties) {
renderablePokemon.add(RenderablePokemon.loadFromBuffer(buffer))
}
categories.add(
RenderableStarterCategory(
name = name,
displayName = displayName,
pokemon = renderablePokemon
)
)
val categories = buffer.readMap(PacketByteBuf::readIdentifier) { categoryBuffer ->
val displayName = buffer.readText()
val renderablePokemon = categoryBuffer.readList(RenderablePokemon::loadFromBuffer)
return@readMap RenderableStarterCategory(displayName, renderablePokemon)
}
return OpenStarterUIPacket(categories)
}
@@ -29,7 +29,7 @@ class SetClientPlayerDataPacket(val promptStarter: Boolean, val starterLocked: B
playerData: PlayerData,
resetStarterPrompt: Boolean? = null
) : this(
!playerData.starterPrompted || !Cobblemon.starterConfig.promptStarterOnceOnly,
!playerData.starterPrompted || !Cobblemon.config.promptStarterOnceOnly,
playerData.starterLocked,
playerData.starterSelected,
playerData.starterUUID,
@@ -11,16 +11,17 @@ package com.cobblemon.mod.common.net.messages.server
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class SelectStarterPacket(val categoryName: String, val selected: Int) : NetworkPacket<SelectStarterPacket> {
class SelectStarterPacket(val categoryId: Identifier, val selected: Int) : NetworkPacket<SelectStarterPacket> {
override val id = ID
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(this.categoryName)
buffer.writeIdentifier(this.categoryId)
buffer.writeInt(this.selected)
}
companion object {
val ID = cobblemonResource("select_starter")
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readString(), buffer.readInt())
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readIdentifier(), buffer.readInt())
}
}
@@ -16,6 +16,6 @@ import net.minecraft.server.network.ServerPlayerEntity
object SelectStarterPacketHandler : ServerNetworkPacketHandler<SelectStarterPacket> {
override fun handle(packet: SelectStarterPacket, server: MinecraftServer, player: ServerPlayerEntity) {
Cobblemon.starterHandler.chooseStarter(player, packet.categoryName, packet.selected)
Cobblemon.starterHandler.chooseStarter(player, packet.categoryId, packet.selected)
}
}
@@ -1,73 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
open class CobblemonStarterHandler : StarterHandler {
override fun getStarterList(player: ServerPlayerEntity) = Cobblemon.starterConfig.starters
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
playerData.sendToPlayer(player)
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
} else {
OpenStarterUIPacket(getStarterList(player)).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
}
override fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
return player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
return player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
}
val category = getStarterList(player).find { it.name == categoryName } ?: return
if (index > category.pokemon.size) {
return
}
val properties = category.pokemon[index]
val pokemon = properties.create()
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) {
Cobblemon.storage.getParty(player).add(
it.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class CobblemonStarterHandler : StarterHandler {
override fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory> = StarterRegistry.all()
// No OP the starters are sent out when the open UI packet is sent out instead
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
if (!this.isAbleToSelect(player)) {
return
}
val playerData = Cobblemon.playerData.get(player)
val renderable = this.getStarters(player).entries.associate { (id, category) ->
val renderable = category.pokemon.map {
// Let the actual Pokémon be created and apply the shiny accordingly
// This won't be accurate with random but I mean what else do you want me to do
val pokemon = it.create()
// While they can still be shiny a random shiny from the #create doesn't mean the actual starter will be shiny
// This tries to be realistic
pokemon.shiny = player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)
pokemon.asRenderablePokemon()
}
id to RenderableStarterCategory(category.displayName, renderable)
}
OpenStarterUIPacket(renderable).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
override fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int) {
if (!this.isAbleToSelect(player)) {
return
}
val category = this.getStarters(player)[categoryId] ?: return
val properties = category.pokemon.getOrNull(index) ?: return
val pokemon = properties.create()
val playerData = Cobblemon.playerData.get(player)
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) { event ->
Cobblemon.storage.getParty(player).add(
event.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
/**
* Checks if the [player] can select a starter.
* If not gives them an appropriate feedback message.
*
* @param player The [ServerPlayerEntity] being tested.
* @return If the [player] can pick a starter.
*/
private fun isAbleToSelect(player: ServerPlayerEntity): Boolean {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
return false
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
return false
}
return true
}
}
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters
import com.google.gson.*
import net.minecraft.text.Text
import java.lang.reflect.Type
object PlainJsonTextAdapter : JsonDeserializer<Text>, JsonSerializer<Text> {
override fun deserialize(jElement: JsonElement, type: Type, context: JsonDeserializationContext): Text {
return Text.Serializer.fromJson(jElement) ?: throw IllegalArgumentException("Cannot resolve null text")
}
override fun serialize(text: Text, type: Type, context: JsonSerializationContext): JsonElement = JsonPrimitive(Text.Serializer.toJson(text))
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.kanto"
},
"pokemon": [
"Bulbasaur level=10",
"Charmander level=10",
"Squirtle level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.johto"
},
"pokemon": [
"Chikorita level=10",
"Cyndaquil level=10",
"Totodile level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.hoenn"
},
"pokemon": [
"Treecko level=10",
"Torchic level=10",
"Mudkip level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.sinnoh"
},
"pokemon": [
"Turtwig level=10",
"Chimchar level=10",
"Piplup level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.unova"
},
"pokemon": [
"Snivy level=10",
"Tepig level=10",
"Oshawott level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.kalos"
},
"pokemon": [
"Chespin level=10",
"Fennekin level=10",
"Froakie level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.alola"
},
"pokemon": [
"Rowlet level=10",
"Litten level=10",
"Popplio level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.galar"
},
"pokemon": [
"Grookey level=10",
"Scorbunny level=10",
"Sobble level=10"
]
}
@@ -0,0 +1,10 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.paldea"
},
"pokemon": [
"Sprigatito level=10",
"Fuecoco level=10",
"Quaxly level=10"
]
}