Compare commits

...
114 Commits
Author SHA1 Message Date
Hiroku 16007d3c1b Merge branch 'main' into '1.2.0'
Update Changelog

See merge request cable-mc/cobblemon!246
2023-01-01 04:20:56 +00:00
TestandHiroku ccee0065c2 Update Changelog [skip-ci] 2023-01-01 04:20:55 +00:00
Paul 30137a9308 When referring to a discretely counted thing, 'less' is actually meant to be 'fewer'. [skip-ci] 2023-01-01 14:04:26 +10:00
Paul c853644c16 Changelog for 1.2.0 [skip-ci] 2023-01-01 13:59:52 +10:00
Paul 794d1b391d Non-snapshot 1.2.0 2023-01-01 13:44:51 +10:00
Paul 3ac2687f1e Merge remote-tracking branch 'origin/main' 2023-01-01 11:19:29 +10:00
Paul 511fd00652 Add last saved Cobblemon version to Pokemon data so we can do data migrations 2023-01-01 11:19:18 +10:00
Test 405d3fd774 fix framing for Beedrill/Butterfree line 2022-12-31 15:20:55 +00:00
Paul 7a26d613fd Fixed zero PP turning into max-maxPP when entering battle 2022-12-31 20:16:34 +10:00
Paul 0a02edf6f3 Fixed hitboxes not changing when a Pokémon evolves 2022-12-31 19:22:30 +10:00
Paul e8c7b31144 Added explicit dependency on Architectury so we get fewer crash reports 2022-12-31 18:59:35 +10:00
Paul d6f6742658 Fixed Eevee positioning on shoulders 2022-12-31 13:16:35 +10:00
Paul c76dfe9e96 Doubled default charge rate of healers 2022-12-31 13:16:06 +10:00
Paul 3ba88265eb Fixed variations not showing in battle tiles, start battles with choice menu open 2022-12-31 13:04:57 +10:00
Hiroku dcc8975757 Fixed pyu size 2022-12-31 02:22:44 +00:00
Hiroku e824234057 Merge branch 'hit-count-interpret' into 'main'
Add support for '-hitcount' to ShowdownInterpreter

See merge request cable-mc/cobblemon!244
2022-12-30 23:32:41 +00:00
Licious 0cc8bd6e0f Remove unused import 2022-12-30 18:22:52 +00:00
Licious17 1e45ff34f0 Added support for '-hitcount' to ShowdownInterpreter 2022-12-30 17:20:38 +00:00
Hiroku 2532511d53 Merge branch 'fix-capture-effects' into 'main'
Fix capture effects

See merge request cable-mc/cobblemon!243
2022-12-30 12:39:12 +00:00
Licious17 14f7c5ecc3 Fixed CaptureEffect not triggering after a successful capture 2022-12-30 12:14:09 +00:00
Hiroku 1ec6af6d34 Merge branch 'pokemon-cry-fixes' into 'main'
Fix connection to server with cries

See merge request cable-mc/cobblemon!242
2022-12-29 21:04:19 +00:00
Licious17 e472ee5fbd Fixed S2C crash due to missing cry sound events, made the sound event identifier adapt to the namespace of the registered species for better expected datapack functionality, added the ability to create sounds for every form. 2022-12-29 20:50:11 +00:00
Test 7b4f43fd12 update labels 2022-12-28 21:45:36 +00:00
Hiroku 8a49b88c7a Merge branch 'spawn-pos-change' into 'main'
Fix ModifiableWorld#spawnEntity and Entity#setPosition inconsistencies

See merge request cable-mc/cobblemon!241
2022-12-28 13:02:15 +00:00
Licious17 2138022f6d Fixed usages of ModifiableWorld#spawnEntity before Entity#setPosition 2022-12-28 12:38:31 +00:00
Paul fd3bc667a9 Changelog for cry change [skip-ci] 2022-12-28 17:46:21 +10:00
Paul 4db5e54d88 Added blank sounds for Pokémon ambient cries so they can be set via RP 2022-12-28 17:41:07 +10:00
Paul 62748437de Corrected evo displays I think 2022-12-28 14:17:27 +10:00
Paul 163b2df7f6 Fixed many bugged forms that didn't have aspect conditions 2022-12-28 14:17:04 +10:00
Paul 386bad2c1c More missed stuff from last commit (illegal abilities) 2022-12-28 14:15:32 +10:00
Paul d304028389 Missed stuff from last commit (illegal abilities) 2022-12-28 14:14:53 +10:00
Paul 964894f376 Fixed abilities being illegal when they shouldn't be illegal 2022-12-28 14:14:33 +10:00
Paul ec8dfd746d Fixed moves that only work in double battles not being selectable at all 2022-12-28 13:47:09 +10:00
Hiroku 732547213a Merge branch 'mod-menu-compat' into 'main'
Added necessary data

See merge request cable-mc/cobblemon!239
2022-12-27 22:03:01 +00:00
Hiroku df888a7f36 Merge branch 'graal-relocation' into 'main'
Relocate IBM package, add pipes for Graal logging

See merge request cable-mc/cobblemon!240
2022-12-27 22:02:29 +00:00
Nick bd01b9d682 Relocate IBM package, add pipes for Graal logging 2022-12-27 13:10:38 -08:00
Licious17 ab390703da Updated mods.toml and fabric.mod.json with our new website. 2022-12-27 17:38:09 +00:00
Licious17 28cd8f0265 Added necessary data 2022-12-27 17:33:46 +00:00
Paul ca47636ffe Merge remote-tracking branch 'origin/main' into snowstorm-particles 2022-12-27 21:14:31 +10:00
Paul a852b8e535 Fixed multiple variant JSON issues 2022-12-27 21:14:13 +10:00
Test fa6f52d305 update click.ogg 2022-12-26 22:27:20 +00:00
Paul 3e8d8daa9b Merge remote-tracking branch 'origin/main' into snowstorm-particles 2022-12-26 21:32:21 +10:00
Paul 8196a686df Fixed arbok variants, other variants need restoring ): 2022-12-26 21:31:59 +10:00
Veraxiel da157a1b28 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2022-12-26 02:17:14 -05:00
Veraxiel 167191a135 Added spawn files for Natu, Murkrow, and Wailord families. Updated spawn files of the Gen 1 fossil Pokémon. 2022-12-26 02:16:31 -05:00
Paul e6f881df6e Merge remote-tracking branch 'origin/main' into snowstorm-particles
# Conflicts:
#	CHANGELOG.md
2022-12-26 14:35:35 +10:00
Paul 53f82b1971 Fixed form and alolan stuff 2022-12-26 14:25:18 +10:00
Hiroku 9f775f2c92 Merge branch 'model-fix-balls-healmach' into 'main'
Fixes In Hand Pokeball and Healing Machine Display Settings

See merge request cable-mc/cobblemon!238
2022-12-25 08:57:15 +00:00
dhi_holo 07f89dc691 Update CHANGELOG.md 2022-12-25 18:33:19 +10:00
dhi_holo 0c70a5cbac Fixes In Hand Pokeball and Healing Machine Display Settings
The rendering of the pokeball and healing machine items, mainly when dropped but in other places, were not matching to how other things in Minecraft work, so were brought slightly more inline. Slight model tweak to the Pokeball In Hand model to make the visuals cleaner, but every change directly links to the display settings.
2022-12-25 18:26:15 +10:00
Paul 3ceda765c9 Updated framing for starters, pichu line, zubat 2022-12-25 17:16:51 +10:00
Paul e3360e2d18 Updated various models and textures, fixed Rattata + Nidoqueen texture, updated Sableye animations 2022-12-25 14:33:19 +10:00
Paul b4e2ecc8f1 Added Wailmer, Wailord, Murkrow, Honchkrow, and fixed various framings 2022-12-24 22:04:45 +10:00
Paul bdd6ea48a0 Added scissoring of pokemon renders in PC GUI, update click sound to be a bit quieter 2022-12-24 11:44:26 +10:00
Paul 84244c0376 Added Natu and Xatu 2022-12-23 19:03:38 +10:00
Paul 6fec1417b1 Don't despawn a pokemon that has a vehicle 2022-12-23 16:13:04 +10:00
Paul 142c47de1c Changelog for pokemon look change during battle 2022-12-23 13:13:35 +10:00
Paul e796883660 Merge remote-tracking branch 'origin/main' into pc 2022-12-23 13:13:03 +10:00
Paul c6779385a7 Added emissive texture support to Pokémon render layers 2022-12-23 13:12:44 +10:00
Hiroku 1a0ea5db72 Merge branch 'pc' into 'main'
PC

See merge request cable-mc/cobblemon!237
2022-12-23 03:07:36 +00:00
TestandHiroku 342a1ceb6d PC 2022-12-23 03:07:36 +00:00
Hiroku 0e7d3438a5 Merge branch 'main' into 'main'
Pokémon look at eachother in battle & halt all other goals.

Closes #131

See merge request cable-mc/cobblemon!233
2022-12-23 02:55:25 +00:00
amo cadcdab39b fix bug 2022-12-23 02:52:08 +00:00
Paul 0bc7614809 Merge remote-tracking branch 'origin/pc' into pc 2022-12-23 12:42:56 +10:00
Paul ecbde40aa6 Fixed production issue on Forge caused by Graal 2022-12-23 12:42:46 +10:00
Village 672ed74309 rm unused imports 2022-12-22 21:25:12 -05:00
Village 469e9e9065 set preview on hover, remove debounce for grab 2022-12-22 21:20:50 -05:00
Paul 2dba103c14 Fixed text glitching on ability and nature 2022-12-23 10:37:15 +10:00
Village be7c0220d4 update sounds 2022-12-22 15:01:23 -05:00
Village 14f9640556 Merge branch 'main' of https://gitlab.com/cable-mc/pokemon-cobbled into pc 2022-12-22 02:59:05 -05:00
Village 3b2940ac59 rm unused imports 2022-12-22 02:58:46 -05:00
Village 036d9c5e6b PC 2022-12-22 02:34:25 -05:00
Paul 76cdecbb94 Fixed Mewtwo always being Mega Mewtwo X actually 2022-12-21 22:15:02 +10:00
Paul 1d6317b5d6 Fixed an instance of using CheckedRandom unsafely 2022-12-21 22:13:07 +10:00
Paul 26f963f9b9 Improved initializing logic so we don't get nasty invis pokemon again 2022-12-21 22:06:20 +10:00
Paul 9ee90508ed Changelog update [skip-ci] 2022-12-21 17:44:03 +10:00
Hiroku 9b6da631ee Merge branch 'stat-calculation-fix' into 'main'
Apply level multiplier in correct location, fixes invalid stats outside of HP calculation

See merge request cable-mc/cobblemon!236
2022-12-21 07:38:00 +00:00
Hiroku c26edc55f5 Merge branch 'spawn-validator' into 'main'
Fix Magmortar spawn definitions and add validator for future default configurations

See merge request cable-mc/cobblemon!235
2022-12-21 07:37:50 +00:00
Nick f1c306051c Apply level multiplier in correct location, fixes invalid stats outside of HP calculation 2022-12-20 19:18:32 -08:00
Nick dacb16044d License 2022-12-20 18:04:51 -08:00
Nick 68478f84d8 Fix Magmortar spawn definitions and add validator for future default configurations 2022-12-20 18:01:59 -08:00
Hiroku 6272bfe2d6 Merge branch 'battle-engine' into 'main'
Replaced Javet with Graal

See merge request cable-mc/cobblemon!234
2022-12-20 19:52:14 +00:00
Paul e561bfd148 Fixed bad directory reference in PokemonSpecies showdown export 2022-12-20 07:06:59 +10:00
amo 2f9cc2b41c pokemon look at eachother in battle & dont move 2022-12-19 16:08:12 +00:00
Paul 8d39e2536b Fixed graal issue in production jars 2022-12-19 22:08:45 +10:00
Paul 17fa80e752 Upped compile-time RAM requisition to 4gb 2022-12-19 21:59:10 +10:00
Paul 4d2641308a licenses 2022-12-19 21:02:34 +10:00
Paul 27e02963eb Implemented Showdown messaging using Graal 2022-12-19 21:01:12 +10:00
Paul d71c03e48a Merge remote-tracking branch 'origin/main' into battle-engine 2022-12-19 18:28:09 +10:00
Paul 43b69ba860 Merge remote-tracking branch 'origin/main' 2022-12-17 10:08:36 +10:00
Paul 64bfb72c53 Documentation correction 2022-12-17 10:08:25 +10:00
Paul 6810232183 Updated licenses 2022-12-17 10:08:03 +10:00
Licious17 6e25c2591e Changed the default Apricorn seed chance config value from 6% to 10% 2022-12-16 12:42:22 +00:00
Paul 0be43ec28c Added waning and waxing moonphase ranges 2022-12-16 21:08:20 +10:00
Paul 78f0f572b2 Added range support to moon phase conditions and allowed single-value interpretation for time range 2022-12-16 21:05:10 +10:00
Paul 6d99c7e643 Fixed showdown shutting down after logging out of singleplayer 2022-12-16 12:56:03 +10:00
Paul c6f4fc8992 Prevent all motion on dying pokemon 2022-12-16 12:37:40 +10:00
Paul 320c471b4e Changelog for chunk load fix 2022-12-16 12:33:10 +10:00
Paul 0ea8d8932e Merge branch '1.1.1-log-world-gen' of gitlab.com:cable-mc/pokemon-cobbled
# Conflicts:
#	gradle.properties
2022-12-16 12:32:27 +10:00
Hiroku 7325f4b6ac Merge branch 'surface-spawning' into 'main'
Added surface spawning context

See merge request cable-mc/cobblemon!232
2022-12-16 02:21:57 +00:00
Paul 4fd035aa16 Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into surface-spawning 2022-12-16 00:21:15 +10:00
Licious 5f1038d7a4 Merge branch 'animations-in-resource-packs' into 'main'
Animations in resource packs

See merge request cable-mc/cobblemon!230
2022-12-15 13:36:17 +00:00
HirokuandLicious 6449cb7295 Animations in resource packs 2022-12-15 13:36:17 +00:00
Paul 1fa0e81b8e Added surface spawning context 2022-12-15 22:34:09 +10:00
Hiroku b243a0a098 Merge branch 'starter-config-maintainability' into 'main'
Starter config maintainability

See merge request cable-mc/cobblemon!229
2022-12-14 21:58:09 +00:00
Paul 139253454c Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into starter-config-maintainability
# Conflicts:
#	CHANGELOG.md
2022-12-15 07:42:26 +10:00
Paul 7224ac123f Better protect save file loading using the .temps 2022-12-14 22:29:28 +10:00
Paul c53018942b Changelog [skip-ci] 2022-12-13 19:50:34 +10:00
Paul 45eaf6fb3b Likely fix for world chunk gen hanging 2022-12-10 17:00:26 +10:00
Paul 8e4141ccc1 Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into starter-config-maintainability 2022-12-10 15:23:58 +10:00
Paul 41c6accc16 Improved starter config handling so it is opt-in for it to export to a file 2022-12-10 15:23:50 +10:00
Paul 2302d6e59f 1.1.1 non-snapshot 2022-11-28 13:35:08 +10:00
Paul 5848e7b21c Added more stuff 2022-11-13 13:57:45 +10:00
Paul 2a2c0da200 Starting some global type definitions for engine 2022-11-11 19:50:41 +10:00
1526 changed files with 11571 additions and 6197 deletions
+2 -1
View File
@@ -31,4 +31,5 @@ forge*changelog.txt
common/src/main/resources/assets/cobblemon/showdown
# Generated files
*/generated/resources/*
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
+1 -1
View File
@@ -10,7 +10,7 @@ stages:
build-job:
stage: build
script:
- export GRADLE_OPTS="-Xmx3G -Xms3G"
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew build
cache:
+46 -12
View File
@@ -1,30 +1,63 @@
# Changelog
## 1.1.2
## [1.2.0 - The Customization Update (January 1st, 2023)](#1-2-0)
### Additions
- Reworked customisation of models, textures, and animations to make fakemon very easy to create.
- Added 'nature' and 'pokeball' options to commands such as /spawnpokemon and /givepokemon.
- Added support for datapack spawn files and spawn file presets.
- Added models for Natu and Xatu, Murkrow and Honchkrow, Wailmer and Wailord.
- Added new PC interface and it is beautiful.
- Reworked the battle system so that battles load faster, cause fewer bugs, and can run on shared server hosts. This is a very big change that also reduced the size of the mod by 50mb!
- Added full resource pack / data pack customization of models, textures, animations, spawning, and spawn file presets to make custom Pokémon species and variations very easy to create. You can find the guide for creating custom Pokémon on [our website](https://cobblemon.com/guides/custompokemon.html)!
- Added water surface spawning for Pokémon like Lapras.
- Added emissive texture support to Pokémon render layers.
- Added compatibility for Mod Menu ([CurseForge](https://www.curseforge.com/minecraft/mc-mods/modmenu), [Modrinth](https://modrinth.com/mod/modmenu)).
- Added blank ambient Pokémon cries so cries can be added via resource packs.
- Added new sounds for GUIs and item uses.
- Added `nature` and `pokeball` options to commands such as `/spawnpokemon` and `/givepokemon`.
### Changes
- Reinforced party and PC saving to make data corruption from crashes less bad.
- Added a config option for whether the starter config will be exported, making it more maintainable as we add starter Pokémon.
- Battles now start with the options menu open for convenience.
- Doubled the default charge rate of healers. You'd need to reset your config under `./config/cobblemon/main.json` to see this change!
- Changed the default Apricorn seed chance config value from 6% to 10%.
- The mod now correctly reports our dependency on Architectury API so people don't get super confused when things don't work.
- Pokémon now look at their opponents during battle.
- Updated Sableye's animations to be super freaky.
- Changed the healer advancements to make the healing machine's mechanics more obvious.
### Fixes
- Fixed an incompatibility with [Porting Lib](https://github.com/Fabricators-of-Create/Porting-Lib) used by Fabric ports of mods such as Create or Twilight Forest.
- Fixed HP and max HP values in the battle GUI not being correct.
- Fixed some animations on snake-type Pokémon being mega slow.
- Fixed some animations on snake-type Pokémon being super slow.
- Fixed a typo in the English name for Calcium. Calcuim.
- Fixed Pokémon gradually becoming rarer around you if you move long distances.
- Fixed a shoulder mount crash on Fabric.
- Fixed a rare issue where chunks would take a really long time to generate.
- Fixed a singleplayer bug where battles wouldn't work after leaving then rejoining a world.
- Fixed stat calculations for everything except HP. HP was fine though :).
- Fixed a randomized Pokémon spawning in mountains that corrupted the data of whatever party or PC it got into. Yikes.
- Fixed a rare crash involving literally random number generation. A random crash involving random numbers.
- Fixed all regular Mewtwo having the stats and types of Mega Mewtwo X - same deal with many other Pokémon.
- Fixed the framing of many Pokémon in GUIs.
- Fixed texture glitches with Rattata and Nidoqueen (again!).
- Fixed dropped item forms of all Pokéballs and the Healing Machine, and slightly adjusted all other display settings
- Fixed issues with forms not showing the correct evolution in the evolution menu.
- Fixed some alternate forms not having the alternate stats and types.
- Fixed moves that only work in double battles not being selectable at all (such as Helping Hand and Aromatic Mist).
- Fixed abilities not remaining legal in some forms.
- Fixed Poké Ball capture effects not triggering after a successful capture, such as the Heal Ball's healing effect.
- Fixed multiple-hit moves sending gibberish into the battle chat.
- Fixed Pyukumuku not being appropriately scaled.
- Fixed shiny and other variations of Pokémon not showing in the battle GUI.
- Fixed Eevee being poorly positioned and un-animated on shoulders.
- Fixed a Pokémon's hitbox not updating when it evolves while sent out.
- Fixed a Pokémon's PP going from zero to above the maximum when entering another battle.
## 1.1.1 (November 27th, 2022)
## [1.1.1 (November 27th, 2022)](#1-1-1)
### Fixes
- Fixed a critical issue with servers where Pokémon data didn't properly synchronize and so you couldn't see any.
## 1.1.0 - The Swim and Sleep Update (November 27th, 2022)
## [1.1.0 - The Swim and Sleep Update (November 27th, 2022)](#1-1-0)
#### "Ideally not at the same time."
### Additions
- Added a new GUI for viewing party information, rearranging moves, and evolving Pokémon. It looks too good.
- Starter Pokémon will sleep on top of you if sent out when you get on a bed.
@@ -78,6 +111,7 @@
- Statuses like sleep and frozen no longer last forever if it was carried over from a previous battle.
### Localization
- Added species, ability, and move translations for ko_ko, jp_jp, fr_fr, es_es, it_it, and zh_cn.
## 1.0.0 (2022-11-12)
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
@@ -19,6 +19,7 @@ tasks {
archiveClassifier.set("dev-shadow")
archiveBaseName.set("Cobblemon-${project.name}")
configurations = listOf(bundle)
mergeServiceFiles()
}
remapJar {
+1 -7
View File
@@ -21,8 +21,7 @@ dependencies {
modApi(libs.mclib)
// For Showdown
modCompileOnly(libs.javet) // Linux or Windows
modCompileOnly(libs.javetMac) // Mac OS (x86_64 Only)
modCompileOnly(libs.graal)
//shadowCommon group: 'commons-io', name: 'commons-io', version: '2.6'
@@ -33,11 +32,6 @@ dependencies {
testImplementation(libs.mockk)
compileOnly("net.luckperms:api:${rootProject.property("luckperms_version")}")
// For Showdown
// modCompileOnly 'com.caoccao.javet:javet:1.0.6' // Linux or Windows
// modCompileOnly 'com.caoccao.javet:javet-macos:1.0.6' // Mac OS (x86_64 Only)
// modCompileOnly group: 'commons-io', name: 'commons-io', version: '2.6'
}
publishing {
@@ -48,7 +48,6 @@ import com.cobblemon.mod.common.api.reactive.Observable.Companion.map
import com.cobblemon.mod.common.api.reactive.Observable.Companion.takeFirst
import com.cobblemon.mod.common.api.scheduling.ScheduledTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
@@ -68,7 +67,6 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownConnection
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
@@ -128,7 +126,6 @@ object Cobblemon {
val LOGGER = LogManager.getLogger()
lateinit var implementation: CobblemonImplementation
lateinit var showdown: ShowdownConnection
var captureCalculator: CaptureCalculator = CobblemonGen348CaptureCalculator
var experienceCalculator: ExperienceCalculator = StandardExperienceCalculator
var evYieldCalculator: EvCalculator = Generation8EvCalculator
@@ -261,7 +258,6 @@ object Cobblemon {
SERVER_STOPPED.subscribe {
storage.unregisterAll()
playerData.saveAll()
showdown.close()
}
SERVER_STARTED.subscribe {
bestSpawner.onServerStarted()
@@ -351,19 +347,23 @@ object Cobblemon {
}
fun loadStarterConfig(): StarterConfig {
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()
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()
}
val reader = FileReader(file)
val config = StarterConfig.GSON.fromJson(reader, StarterConfig::class.java)
reader.close()
return config
}
fun saveConfig() {
@@ -52,6 +52,7 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.Add
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
@@ -207,6 +208,9 @@ object CobblemonNetwork {
buildClientMessage<SpeciesRegistrySyncPacket>()
buildClientMessage<PropertiesCompletionRegistrySyncPacket>()
// Hax
buildClientMessage<UnvalidatedPlaySoundS2CPacket>()
/**
* Server Packets
*/
@@ -16,6 +16,14 @@ import net.minecraft.util.registry.Registry
object CobblemonSounds : CompletableRegistry<SoundEvent>(Registry.SOUND_EVENT_KEY) {
private fun queue(name: String) = queue(name) { SoundEvent(cobblemonResource(name)) }
val GUI_CLICK = queue("gui.click")
val PC_ON = queue("pc.on")
val PC_OFF = queue("pc.off")
val PC_GRAB = queue("pc.grab")
val PC_DROP = queue("pc.drop")
val PC_RELEASE = queue("pc.release")
val POKE_BALL_CAPTURE_STARTED = queue("poke_ball.capture_started")
val POKE_BALL_CAPTURE_SUCCEEDED = queue("poke_ball.capture_succeeded")
val POKE_BALL_SHAKE = queue("poke_ball.shake")
@@ -23,5 +31,6 @@ object CobblemonSounds : CompletableRegistry<SoundEvent>(Registry.SOUND_EVENT_KE
val POKE_BALL_HIT = queue("poke_ball.hit")
val POKE_BALL_SEND_OUT = queue("poke_ball.send_out")
val POKE_BALL_RECALL = queue("poke_ball.recall")
val ITEM_USE = queue("item.use")
}
@@ -22,7 +22,6 @@ import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import kotlin.io.path.Path
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
@@ -83,6 +82,6 @@ object Abilities : JsonDataRegistry<AbilityTemplate> {
fun all() = allAbilities.toList()
fun first() = allAbilities.first()
fun get(name: String) = abilityMap[name.lowercase()]
fun getOrException(name: String) = get(name)!!
fun getOrException(name: String) = get(name) ?: throw IllegalArgumentException("Unable to find ability of name: $name")
fun count() = allAbilities.size
}
@@ -21,27 +21,35 @@ import net.minecraft.text.MutableText
* @author Qu
* @since January 9th, 2022
*/
open class Ability internal constructor(var template: AbilityTemplate) {
open class Ability internal constructor(var template: AbilityTemplate, var forced: Boolean) {
val name: String
get() = template.name
val displayName: MutableText
val displayName: String
get() = template.displayName
val description: MutableText
val description: String
get() = template.description
open fun saveToNBT(nbt: NbtCompound): NbtCompound {
nbt.putString(DataKeys.POKEMON_ABILITY_NAME, name)
nbt.putBoolean(DataKeys.POKEMON_ABILITY_FORCED, forced)
return nbt
}
open fun saveToJSON(json: JsonObject): JsonObject {
json.addProperty(DataKeys.POKEMON_ABILITY_NAME, name)
json.addProperty(DataKeys.POKEMON_ABILITY_FORCED, forced)
return json
}
open fun loadFromNBT(nbt: NbtCompound) = this
open fun loadFromJSON(json: JsonObject) = this
open fun loadFromNBT(nbt: NbtCompound): Ability {
forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
return this
}
open fun loadFromJSON(json: JsonObject): Ability {
forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
return this
}
}
@@ -8,10 +8,8 @@
package com.cobblemon.mod.common.api.abilities
import com.cobblemon.mod.common.util.lang
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.text.MutableText
/**
* This represents the base of an Ability.
@@ -21,14 +19,14 @@ import net.minecraft.text.MutableText
*/
class AbilityTemplate(
val name: String = "",
var builder: (AbilityTemplate) -> Ability = { Ability(it) },
val displayName: MutableText = lang("ability.$name"),
val description: MutableText = lang("ability.$name.desc")
var builder: (AbilityTemplate, forced: Boolean) -> Ability = { template, forced -> Ability(template, forced) },
val displayName: String = "cobblemon.ability.$name",
val description: String = "cobblemon.ability.$name.desc"
) {
/**
* Returns the Ability or if applicable the extension connected to this template
*/
fun create() = builder(this)
fun create(forced: Boolean = false) = builder(this, forced)
/**
@@ -36,12 +34,12 @@ class AbilityTemplate(
*
* Ability extensions need to write and read their needed data from here.
*/
fun create(nbt: NbtCompound) = builder(this).loadFromNBT(nbt)
fun create(nbt: NbtCompound) = create().loadFromNBT(nbt)
/**
* Returns the Ability and loads the given JSON object into it.
*
* Ability extensions need to write and read their needed data from here.
*/
fun create(json: JsonObject) = builder(this).loadFromJSON(json)
fun create(json: JsonObject) = create().loadFromJSON(json)
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.battles.model
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon.showdown
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -26,13 +25,11 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.runner.GraalShowdown
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.pokemon.feature.BattleCriticalHitsFeature
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.text.Text
@@ -50,6 +47,7 @@ open class PokemonBattle(
) {
/** Whether or not logging will be silenced for this battle. */
var mute = true
init {
side1.battle = this
side2.battle = this
@@ -148,17 +146,8 @@ open class PokemonBattle(
}
fun writeShowdownAction(vararg messages: String) {
val jsonArray = JsonArray()
for (message in messages) {
jsonArray.add(message)
}
val request = JsonObject()
request.addProperty(DataKeys.REQUEST_TYPE, DataKeys.REQUEST_BATTLE_SEND_MESSAGE)
request.addProperty(DataKeys.REQUEST_BATTLE_ID, battleId.toString())
request.add(DataKeys.REQUEST_MESSAGES, jsonArray)
val json = BattleRegistry.gson.toJson(request)
log(json)
showdown.write(json)
log(messages.joinToString("\n"))
GraalShowdown.sendToShowdown(battleId, messages.toList().toTypedArray())
}
fun turn(newTurnNumber: Int) {
@@ -9,8 +9,11 @@
package com.cobblemon.mod.common.api.net
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.util.getServer
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.registry.RegistryKey
import net.minecraft.world.World
/*
* Simple packet interface to make a more traditional layout for netcode
@@ -30,4 +33,17 @@ interface NetworkPacket {
}
fun sendToAllPlayers() = CobblemonNetwork.sendToAllPlayers(this)
fun sendToServer() = CobblemonNetwork.sendToServer(this)
// A copy from PlayerManager#sendToAround to work with our packets
fun sendToPlayersAround(x: Double, y: Double, z: Double, distance: Double, worldKey: RegistryKey<World>, exclusionCondition: (ServerPlayerEntity) -> Boolean = { false }) {
val server = getServer() ?: return
server.playerManager.playerList.filter { player ->
if (exclusionCondition.invoke(player))
return@filter false
val xDiff = x - player.x
val yDiff = y - player.y
val zDiff = z - player.z
return@filter (xDiff * xDiff + yDiff * yDiff + zDiff) < distance * distance
}
.forEach { player -> CobblemonNetwork.sendToPlayer(player, this) }
}
}
@@ -26,127 +26,127 @@ object Natures {
private val allNatures = mutableListOf<Nature>()
val HARDY = registerNature(
Nature(cobblemonResource("hardy"), lang("nature.hardy"),
Nature(cobblemonResource("hardy"), "cobblemon.nature.hardy",
null, null, null, null)
)
val LONELY = registerNature(
Nature(cobblemonResource("lonely"), lang("nature.lonely"),
Nature(cobblemonResource("lonely"), "cobblemon.nature.lonely",
Stats.ATTACK, Stats.DEFENCE, Flavor.SPICY, Flavor.SOUR)
)
val BRAVE = registerNature(
Nature(cobblemonResource("brave"), lang("nature.brave"),
Nature(cobblemonResource("brave"), "cobblemon.nature.brave",
Stats.ATTACK, Stats.SPEED, Flavor.SPICY, Flavor.SWEET)
)
val ADAMANT = registerNature(
Nature(cobblemonResource("adamant"), lang("nature.adamant"),
Nature(cobblemonResource("adamant"), "cobblemon.nature.adamant",
Stats.ATTACK, Stats.SPECIAL_ATTACK, Flavor.SPICY, Flavor.DRY)
)
val NAUGHTY = registerNature(
Nature(cobblemonResource("naughty"), lang("nature.naughty"),
Nature(cobblemonResource("naughty"), "cobblemon.nature.naughty",
Stats.ATTACK, Stats.SPECIAL_DEFENCE, Flavor.SPICY, Flavor.BITTER)
)
val BOLD = registerNature(
Nature(cobblemonResource("bold"), lang("nature.bold"),
Nature(cobblemonResource("bold"), "cobblemon.nature.bold",
Stats.DEFENCE, Stats.ATTACK, Flavor.SOUR, Flavor.SPICY)
)
val DOCILE = registerNature(
Nature(cobblemonResource("docile"), lang("nature.docile"),
Nature(cobblemonResource("docile"), "cobblemon.nature.docile",
null, null, null, null)
)
val RELAXED = registerNature(
Nature(cobblemonResource("relaxed"), lang("nature.relaxed"),
Nature(cobblemonResource("relaxed"), "cobblemon.nature.relaxed",
Stats.DEFENCE, Stats.SPEED, Flavor.SOUR, Flavor.SWEET)
)
val IMPISH = registerNature(
Nature(cobblemonResource("impish"), lang("nature.impish"),
Nature(cobblemonResource("impish"), "cobblemon.nature.impish",
Stats.DEFENCE, Stats.SPECIAL_ATTACK, Flavor.SOUR, Flavor.DRY)
)
val LAX = registerNature(
Nature(cobblemonResource("lax"), lang("nature.lax"),
Nature(cobblemonResource("lax"), "cobblemon.nature.lax",
Stats.DEFENCE, Stats.SPECIAL_DEFENCE, Flavor.SOUR, Flavor.BITTER)
)
val TIMID = registerNature(
Nature(cobblemonResource("timid"), lang("nature.timid"),
Nature(cobblemonResource("timid"), "cobblemon.nature.timid",
Stats.SPEED, Stats.ATTACK, Flavor.SWEET, Flavor.SPICY)
)
val HASTY = registerNature(
Nature(cobblemonResource("hasty"), lang("nature.hasty"),
Nature(cobblemonResource("hasty"), "cobblemon.nature.hasty",
Stats.SPEED, Stats.DEFENCE, Flavor.SWEET, Flavor.SOUR)
)
val SERIOUS = registerNature(
Nature(cobblemonResource("serious"), lang("nature.serious"),
Nature(cobblemonResource("serious"), "cobblemon.nature.serious",
null, null, null, null)
)
val JOLLY = registerNature(
Nature(cobblemonResource("jolly"), lang("nature.jolly"),
Nature(cobblemonResource("jolly"), "cobblemon.nature.jolly",
Stats.SPEED, Stats.SPECIAL_ATTACK, Flavor.SWEET, Flavor.DRY)
)
val NAIVE = registerNature(
Nature(cobblemonResource("naive"), lang("nature.naive"),
Nature(cobblemonResource("naive"), "cobblemon.nature.naive",
Stats.SPEED, Stats.SPECIAL_DEFENCE, Flavor.SWEET, Flavor.BITTER)
)
val MODEST = registerNature(
Nature(cobblemonResource("modest"), lang("nature.modest"),
Nature(cobblemonResource("modest"), "cobblemon.nature.modest",
Stats.SPECIAL_ATTACK, Stats.ATTACK, null, null)
)
val MILD = registerNature(
Nature(cobblemonResource("mild"), lang("nature.mild"),
Nature(cobblemonResource("mild"), "cobblemon.nature.mild",
Stats.SPECIAL_ATTACK, Stats.DEFENCE, Flavor.DRY, Flavor.SOUR)
)
val QUIET = registerNature(
Nature(cobblemonResource("quiet"), lang("nature.quiet"),
Nature(cobblemonResource("quiet"), "cobblemon.nature.quiet",
Stats.SPECIAL_ATTACK, Stats.SPEED, Flavor.DRY, Flavor.SWEET)
)
val BASHFUL = registerNature(
Nature(cobblemonResource("bashful"), lang("nature.bashful"),
Nature(cobblemonResource("bashful"), "cobblemon.nature.bashful",
null, null, null, null)
)
val RASH = registerNature(
Nature(cobblemonResource("rash"), lang("nature.rash"),
Nature(cobblemonResource("rash"), "cobblemon.nature.rash",
Stats.SPECIAL_ATTACK, Stats.SPECIAL_DEFENCE, Flavor.DRY, Flavor.BITTER)
)
val CALM = registerNature(
Nature(cobblemonResource("calm"), lang("nature.calm"),
Nature(cobblemonResource("calm"), "cobblemon.nature.calm",
Stats.SPECIAL_DEFENCE, Stats.ATTACK, Flavor.BITTER, Flavor.SPICY)
)
val GENTLE = registerNature(
Nature(cobblemonResource("gentle"), lang("nature.gentle"),
Nature(cobblemonResource("gentle"), "cobblemon.nature.gentle",
Stats.SPECIAL_DEFENCE, Stats.DEFENCE, Flavor.BITTER, Flavor.SOUR)
)
val SASSY = registerNature(
Nature(cobblemonResource("sassy"), lang("nature.sassy"),
Nature(cobblemonResource("sassy"), "cobblemon.nature.sassy",
Stats.SPECIAL_DEFENCE, Stats.SPEED, Flavor.BITTER, Flavor.SWEET)
)
val CAREFUL = registerNature(
Nature(cobblemonResource("careful"), lang("nature.careful"),
Nature(cobblemonResource("careful"), "cobblemon.nature.careful",
Stats.SPECIAL_DEFENCE, Stats.SPECIAL_ATTACK, Flavor.BITTER, Flavor.DRY)
)
val QUIRKY = registerNature(
Nature(cobblemonResource("quirky"), lang("nature.quirky"),
Nature(cobblemonResource("quirky"), "cobblemon.nature.quirky",
null, null, null, null)
)
@@ -311,7 +311,14 @@ open class PokemonProperties {
}
fun create(): Pokemon {
return Pokemon().also { apply(it) }.also { if (it.moveSet.none { it != null }) it.initializeMoveset() }
val pokemon = Pokemon()
apply(pokemon)
// Force the setter to initialize it
pokemon.species = pokemon.species
if (pokemon.moveSet.none { it != null }) {
pokemon.initializeMoveset()
}
return pokemon
}
fun createEntity(world: World): PokemonEntity {
@@ -366,6 +373,7 @@ open class PokemonProperties {
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
return json
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.pokemon
import com.caoccao.javet.interop.V8Host
import com.caoccao.javet.interop.V8Runtime
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
@@ -32,10 +30,11 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroupAdapter
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.condition.TimeRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.battles.runner.GraalShowdown
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -51,11 +50,11 @@ import com.cobblemon.mod.common.util.adapters.DropEntryAdapter
import com.cobblemon.mod.common.util.adapters.EggGroupAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.LazySetAdapter
import com.cobblemon.mod.common.util.adapters.LearnsetAdapter
import com.cobblemon.mod.common.util.adapters.NbtCompoundAdapter
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.HashBasedTable
@@ -64,7 +63,6 @@ import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.io.File
import java.nio.file.Files
import kotlin.io.path.Path
import kotlin.reflect.KProperty
import net.minecraft.block.Block
import net.minecraft.entity.EntityDimensions
@@ -99,7 +97,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
@@ -218,45 +216,40 @@ object PokemonSpecies : JsonDataRegistry<Species> {
this.speciesByDex.remove(species.resourceIdentifier.namespace, species.nationalPokedexNumber)
}
}
V8Host.getNodeInstance().createV8Runtime<V8Runtime>().use { runtime ->
// Showdown loads mods by reading existing files as such we cannot dynamically add to the Pokédex, instead, we will overwrite the existing file and force a mod reload.
val pokedexFile = File("node_modules/pokemon-showdown/.data-dist/mods/Cobblemon/pokedex.js")
Files.createDirectories(pokedexFile.toPath().parent)
pokedexFile.bufferedWriter().use { writer ->
writer.write(
"""
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
const Pokedex = {
$pokedexDataHolder
};
exports.Pokedex = Pokedex;
""".trimIndent()
)
}
val formatsDataFile = File("node_modules/pokemon-showdown/.data-dist/mods/Cobblemon/formats-data.js")
formatsDataFile.bufferedWriter().use { writer ->
writer.write(
"""
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
const FormatsData = {
$formatsDataHolder
};
exports.FormatsData = FormatsData;
""".trimIndent()
)
}
val executor = runtime.getExecutor(
// Showdown loads mods by reading existing files as such we cannot dynamically add to the Pokédex, instead, we will overwrite the existing file and force a mod reload.
val pokedexFile = File("showdown/node_modules/pokemon-showdown/.data-dist/mods/cobblemon/pokedex.js")
Files.createDirectories(pokedexFile.toPath().parent)
pokedexFile.bufferedWriter().use { writer ->
writer.write(
"""
const PokemonShowdown = require('pokemon-showdown');
PokemonShowdown.Dex.modsLoaded = false;
PokemonShowdown.Dex.includeMods();
""".trimIndent()
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
const Pokedex = {
$pokedexDataHolder
};
exports.Pokedex = Pokedex;
""".trimIndent()
)
executor.resourceName = "./node_modules"
executor.executeVoid()
}
val formatsDataFile = File("showdown/node_modules/pokemon-showdown/.data-dist/mods/cobblemon/formats-data.js")
formatsDataFile.bufferedWriter().use { writer ->
writer.write(
"""
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
const FormatsData = {
$formatsDataHolder
};
exports.FormatsData = FormatsData;
""".trimIndent()
)
}
GraalShowdown.context.eval("js", """
PokemonShowdown.Dex.modsLoaded = false;
PokemonShowdown.Dex.includeMods();
""".trimIndent())
Cobblemon.LOGGER.info("Finished creating showdown data for species")
}
@@ -122,7 +122,7 @@ class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, C
}
override fun provide(properties: PokemonProperties): Set<String> {
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
setOf(keys.first())
} else {
emptySet()
@@ -37,6 +37,12 @@ object CobblemonPokemonLabels {
*/
const val PSEUDO_LEGENDARY = "pseudo_legendary"
/**
* Represents a baby Pokémon, this is not just a first stage Pokémon species, it is also unable to breed.
* For more information see this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Baby_Pok%C3%A9mon) page.
*/
const val BABY = "baby"
/**
* Represents a Pokémon that has multiple forms depending on the region they're from.
* In Cobblemon/Minecraft terms there are no regions, but we follow the official concept.
@@ -93,12 +99,6 @@ object CobblemonPokemonLabels {
*/
const val REGIONAL_OF_PALDEA = "paldea_regional"
/**
* Represents a Pokémon "baby", this is not just a first stage Pokémon species, it is also unable to breed.
* For more information see this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Baby_Pok%C3%A9mon) page.
*/
const val BABY = "baby"
/**
* Represents a mega evolution.
* For more information see the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Mega_Evolution) page.
@@ -158,13 +158,12 @@ object CobblemonPokemonLabels {
const val GENERATION_8 = "gen8"
/**
* Official Pokémon changed by a datapack, there is no guarantee authors will adhere to this principle
* Official Pokémon changed by a data pack, there is no guarantee authors will adhere to this principle
*/
const val CUSTOMIZED_OFFICIAL = "customized_official"
/**
* Unofficial Pokémon created by a datapack, there is no guarantee authors will adhere to this principle
* Unofficial Pokémon created by a data pack, there is no guarantee authors will adhere to this principle
*/
const val FAKEMON = "fakemon"
}
const val CUSTOM = "custom"
}
@@ -15,17 +15,20 @@ import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SubmergedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -33,7 +36,6 @@ import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
import com.cobblemon.mod.common.api.spawning.preset.PokemonSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import com.cobblemon.mod.common.api.spawning.spawner.AreaSpawner
@@ -44,7 +46,6 @@ import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
import com.cobblemon.mod.common.entity.pokemon.CobblemonAgingDespawner
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.util.Identifier
/**
* A grouping of all the overarching behaviours of the Best Spawner system. This is a convenient accessor to
@@ -89,17 +90,20 @@ object BestSpawner {
SpawningCondition.register(AreaSpawningCondition.NAME, AreaSpawningCondition::class.java)
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
SpawningContextCalculator.register(SeafloorSpawningContextCalculator)
SpawningContextCalculator.register(LavafloorSpawningContextCalculator)
SpawningContextCalculator.register(SubmergedSpawningContextCalculator)
SpawningContextCalculator.register(SurfaceSpawningContextCalculator)
SpawningContext.register(name = "grounded", clazz = GroundedSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2022 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.spawning
/**
* A simple utility for combining multiple integer ranges.
*
* @author Hiroku
* @since December 16th, 2022
*/
open class IntRanges() {
constructor(vararg ranges: IntRange) : this() {
this.ranges = ranges.toMutableList()
}
var ranges = mutableListOf<IntRange>()
operator fun contains(value: Int) = ranges.any { value in it }
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 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.spawning
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange.Companion.moonPhaseRanges
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
/**
* A range of moon phases for a world mainly to be used in [SpawningCondition]s. A moon phase range
* is defined as any number of phase integer ranges (0-7).
*
* There are a series of in-built moon phase ranges that you can replace if they don't suit
* your definitions.
*
* If you want a moon phase range to be referenced by name in [SpawningCondition]s, you need
* to register it, by name, in [moonPhaseRanges].
*
* @author Hiroku
* @since December 16th, 2022
*/
class MoonPhaseRange : IntRanges {
companion object {
val moonPhaseRanges = mutableMapOf(
"crescent" to MoonPhaseRange(3..3, 5..5),
"gibbous" to MoonPhaseRange(1..1, 7..7),
"full" to MoonPhaseRange(0..0),
"new" to MoonPhaseRange(4..4),
"quarter" to MoonPhaseRange(2..2, 6..6),
"waxing" to MoonPhaseRange(5..5, 7..7),
"waning" to MoonPhaseRange(1..1, 3..3)
)
}
constructor() : super()
constructor(vararg ranges: IntRange) : super(*ranges)
}
@@ -10,28 +10,28 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
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.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.TimeRange
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.FluidLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
import com.cobblemon.mod.common.util.adapters.RegisteredSpawningContextAdapter
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailPresetAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
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.block.Block
import net.minecraft.fluid.Fluid
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
@@ -54,10 +54,12 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(SpawnDetailPreset::class.java, SpawnDetailPresetAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
@@ -8,46 +8,30 @@
package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon.config
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.TimeRange
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.util.AssetLoading
import com.cobblemon.mod.common.util.AssetLoading.toPath
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.DropEntryAdapter
import com.cobblemon.mod.common.util.adapters.FluidLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
import com.cobblemon.mod.common.util.adapters.NbtCompoundAdapter
import com.cobblemon.mod.common.util.adapters.RegisteredSpawningContextAdapter
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.fromJson
import com.cobblemon.mod.common.util.isHigherVersion
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.reflect.TypeToken
import java.io.File
import java.io.FileOutputStream
import java.io.FileReader
import java.io.InputStream
import java.io.InputStreamReader
import java.nio.file.Path
import kotlin.io.path.pathString
import net.minecraft.block.Block
import net.minecraft.fluid.Fluid
import net.minecraft.nbt.NbtCompound
import net.minecraft.util.Identifier
import net.minecraft.world.biome.Biome
@@ -66,12 +50,14 @@ object SpawnLoader {
.setLenient()
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(SpawnDetail::class.java, SpawnDetailAdapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
@@ -80,146 +66,4 @@ object SpawnLoader {
.create()
var deserializingConditionClass: Class<out SpawningCondition<*>>? = null
// fun load(folder: String): SpawnPool {
// val pool = SpawnPool()
//
// val external = getExternalSets(folder)
// val internal = loadInternalFolder(folder)
//
// external.forEach { set ->
// val matchingInternal = internal.find { it.id == set.id }
// if (set.preventOverwrite) {
// if (matchingInternal != null) {
// internal.remove(matchingInternal)
// }
// if (set.isEnabled()) {
// pool.details.addAll(set.filter { it.isModDependencySatisfied() })
// }
// } else {
// if (matchingInternal != null && matchingInternal.version.isHigherVersion(set.version)) {
// if (matchingInternal.isEnabled()) {
// pool.details.addAll(matchingInternal.filter { it.isModDependencySatisfied() })
// }
// internal.remove(matchingInternal)
// exportSet(folder, matchingInternal)
// }
// }
// }
//
// internal.forEach { set ->
// if (set.isEnabled()) {
// pool.details.addAll(set.filter { it.isModDependencySatisfied() })
// }
// if (config.exportSpawnsToConfig) {
// exportSet(folder, set)
// }
// }
//
// pool.precalculate()
//
// return pool
// }
//
// fun exportSet(folder: String, set: SpawnSet) {
// val file = File("./config/cobblemon/spawning/spawns/$folder/${set.id.lowercase()}.set.json")
// file.parentFile.mkdirs()
// file.createNewFile()
//
// val internalStream = Cobblemon::class.java.getResourceAsStream(set.path.toAbsolutePath().toString())!!
// val bytes = internalStream.readAllBytes()
// internalStream.close()
//
// val externalStream = FileOutputStream(file)
// externalStream.write(bytes)
// externalStream.close()
// }
//
// fun getExternalSets(folder: String): MutableList<SpawnSet> {
// val sets = mutableListOf<SpawnSet>()
// val files = mutableListOf<File>()
// AssetLoading.searchFor(
// dir = "config/cobblemon/spawning/spawns/$folder",
// suffix = ".json",
// list = files
// )
// val paths = files.map { it.toPath() }
//
// sets.addAll(
// paths.mapNotNull {
// try {
// val file = it.toFile()
// val reader = FileReader(file)
// val set = gson.fromJson<SpawnSet>(reader)
// reader.close()
// return@mapNotNull set
// } catch (exception : Exception) {
// LOGGER.error("Unable to load spawn set from ${it.pathString}")
// exception.printStackTrace()
// null
// }
// }
// )
//
// return sets
// }
//
// fun loadSetFromPath(path: Path): SpawnSet {
// val pathString = if (path.toString().startsWith("/")) path.toString() else "/$path"
// val inputStream = Cobblemon.javaClass.getResourceAsStream(pathString)
// ?: throw IllegalArgumentException("Unable to open resource stream for: $pathString")
// val reader = InputStreamReader(inputStream)
// val set = gson.fromJson<SpawnSet>(reader)
// reader.close()
// return set
// }
//
// fun loadInternalFolder(folder: String): MutableList<SpawnSet> {
// val internalFolder = cobblemonResource("spawning/spawns/$folder")
// val path = internalFolder.toPath() ?: throw IllegalArgumentException("There is no valid, internal path for $internalFolder")
// val internalPaths = AssetLoading.fileSearch(path, { it.toString().endsWith(".json") }, recursive = true)
// val sets = mutableListOf<SpawnSet>()
// sets.addAll(internalPaths.flatMap { filePath -> loadInternalFile(filePath) })
// return sets
// }
//
// fun loadInternalFile(path: Path): MutableList<SpawnSet> {
// if (path.toString().endsWith(".packed.json")) {
// return loadPackedSets(path).onEach { it.path = path }
// } else if (path.toString().endsWith(".json")) {
// return mutableListOf(loadSetFromPath(path)).onEach { it.path = path }
// }
//
// return mutableListOf()
// }
//
// fun loadPackedSets(path: Path): MutableList<SpawnSet> {
// val fileStream = Cobblemon::class.java.getResourceAsStream(path.toAbsolutePath().toString())
// ?: throw IllegalArgumentException("Unable to create input stream for $path")
//
// return try {
// loadPackedSetsFromStream(fileStream)
// } catch (e: Exception) {
// LOGGER.error(e.message)
// return mutableListOf()
// } finally {
// fileStream.close()
// }
// }
//
//
// fun loadPackedSetsFromStream(inputStream: InputStream): MutableList<SpawnSet> {
// val sets = mutableListOf<SpawnSet>()
// val stream = InputStreamReader(inputStream)
// val jsonArray = gson.fromJson(stream, JsonArray::class.java)
// stream.close()
// jsonArray.forEach { jsonElement ->
// try {
// sets.add(gson.fromJson<SpawnSet>(jsonElement))
// } catch (exception: Exception) {
// exception.printStackTrace()
// }
// }
// return sets
// }
}
@@ -6,10 +6,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
/**
* A range of time ticks for a world to be used in [SpawningCondition]s. A time range
* A range of time ticks for a world mainly to be used in [SpawningCondition]s. A time range
* is defined as any number of tick ranges. For example, you can define a time range
* as being between 0 and 12000 ticks or 13000 and 14000 ticks just for a single time
* range. This is to deal with the limitation that Minecraft days go from 0 ticks to
@@ -19,14 +21,14 @@ package com.cobblemon.mod.common.api.spawning.condition
* your definitions.
*
* If you want a time range to be referenced by name in [SpawningCondition]s, you need
* to register it, by name, in [ranges].
* to register it, by name, in [timeRanges].
*
* @author Hiroku
* @since January 26th, 2022
*/
class TimeRange() {
class TimeRange : IntRanges {
companion object {
val ranges = mutableMapOf(
val timeRanges = mutableMapOf(
"any" to TimeRange(0..23999),
"day" to TimeRange(23460..23999, 0..12541),
"night" to TimeRange(12542..23459),
@@ -40,11 +42,6 @@ class TimeRange() {
)
}
constructor(vararg ranges: IntRange) : this() {
this.ranges = ranges.toMutableList()
}
var ranges = mutableListOf<IntRange>()
operator fun contains(timeTicks: Int) = ranges.any { timeTicks in it }
}
constructor() : super()
constructor(vararg ranges: IntRange) : super(*ranges)
}
@@ -9,6 +9,8 @@
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ALL
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ANY
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -37,7 +39,7 @@ abstract class SpawningCondition<T : SpawningContext> {
var dimensions: MutableList<Identifier>? = null
var biomes: MutableSet<RegistryLikeCondition<Biome>>? = null
var moonPhase: Int? = null
var moonPhase: MoonPhaseRange? = null
var canSeeSky: Boolean? = null
var minX: Float? = null
var minY: Float? = null
@@ -76,7 +78,7 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimension.effects !in dimensions!!) {
return false
} else if (moonPhase != null && moonPhase != ctx.moonPhase) {
} else if (moonPhase != null && ctx.moonPhase in moonPhase!!) {
return false
} else if (biomes != null && biomes!!.isNotEmpty() && biomes!!.none { condition -> condition.fits(ctx.biome, ctx.biomeRegistry) }) {
return false
@@ -8,10 +8,11 @@
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.Merger
import net.minecraft.util.Identifier
import net.minecraft.fluid.Fluid
/**
* Base type for a spawning condition that applies to some kind of [SubmergedSpawningContext]. This
@@ -24,7 +25,7 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
var minDepth: Int? = null
var maxDepth: Int? = null
var fluidIsSource: Boolean? = null
var fluidBlock: Identifier? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
@@ -33,9 +34,9 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
false
} else if (maxDepth != null && ctx.depth > maxDepth!!) {
false
} else if (fluidIsSource != null && ctx.fluidState.isStill != fluidIsSource!!) {
} else if (fluidIsSource != null && ctx.fluid.isStill != fluidIsSource!!) {
false
} else !(fluidBlock != null && ctx.blockRegistry.getKey(ctx.fluidBlock).get().value != fluidBlock!!)
} else !(ctx.fluid.isEmpty || (fluid != null && !fluid!!.fits(ctx.fluid.fluid, ctx.fluidRegistry)))
}
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
@@ -44,7 +45,7 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
minDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
fluidIsSource = merger.mergeSingle(fluidIsSource, other.fluidIsSource)
fluidBlock = merger.mergeSingle(fluidBlock, other.fluidBlock)
fluid = merger.mergeSingle(fluid, other.fluid)
}
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 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.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.Merger
import net.minecraft.fluid.Fluid
/**
* Base type for a spawning condition that applies to some kind of [SurfaceSpawningContext]. This
* can be extended for subclasses of [SurfaceSpawningContext].
*
* @author Hiroku
* @since December 15th, 2022
*/
abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawningContext> : AreaTypeSpawningCondition<T>() {
var minDepth: Int? = null
var maxDepth: Int? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
false
} else if (minDepth != null && ctx.depth < minDepth!!) {
false
} else if (maxDepth != null && ctx.depth > maxDepth!!) {
false
} else !(ctx.baseBlock.fluidState.isEmpty || (fluid != null && !fluid!!.fits(ctx.baseBlock.fluidState.fluid, ctx.fluidRegistry)))
}
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
super.copyFrom(other, merger)
if (other is SurfaceTypeSpawningCondition) {
minDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
fluid = merger.mergeSingle(fluid, other.fluid)
}
}
}
/**
* A spawning condition for a [SurfaceSpawningContext].
*
* @author Hiroku
* @since December 15th, 2022
*/
open class SurfaceSpawningCondition : SurfaceTypeSpawningCondition<SurfaceSpawningContext>() {
override fun contextClass() = SurfaceSpawningContext::class.java
companion object {
const val NAME = "surface"
}
}
@@ -92,4 +92,18 @@ open class LavafloorSpawningContext(
height: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, width, height, nearbyBlocks, slice)
open class SurfaceSpawningContext(
cause: SpawnCause,
world: World,
position: BlockPos,
light: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
width: Int,
height: Int,
val depth: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, width, height, nearbyBlocks, slice)
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.block.Block
import net.minecraft.entity.Entity
import net.minecraft.fluid.Fluid
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.registry.Registry
@@ -65,6 +66,7 @@ abstract class SpawningContext {
val biomeRegistry: Registry<Biome> by lazy { world.registryManager.get(Registry.BIOME_KEY) }
val blockRegistry: Registry<Block> by lazy { world.registryManager.get(Registry.BLOCK_KEY) }
val fluidRegistry: Registry<Fluid> by lazy { world.registryManager.get(Registry.FLUID_KEY)}
val biomeName: Identifier
get() = this.biomeRegistry.getId(biome)!!
@@ -34,6 +34,5 @@ open class SubmergedSpawningContext(
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : AreaSpawningContext(cause, world, position, light, canSeeSky, influences, width, height, nearbyBlocks, slice) {
val fluidBlock = slice.getBlockState(position.x, position.y, position.z).block
val fluidState = slice.getBlockState(position.x, position.y, position.z).fluidState
val fluid = slice.getBlockState(position.x, position.y, position.z).fluidState
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.spawning.context.FlooredSpawningContext
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator.Companion.isAirCondition
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator.Companion.isLavaCondition
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator.Companion.isSolidCondition
@@ -120,4 +121,34 @@ object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Lav
nearbyBlocks = getNearbyBlocks(input)
)
}
}
}
/**
* The context calculator used for [SurfaceSpawningContext]s. Requires a fluid block below it and
* air blocks in its surroundings.
*
* @author Hiroku
* @since December 15th, 2022
*/
object SurfaceSpawningContextCalculator : FlooredSpawningContextCalculator<SurfaceSpawningContext> {
override val baseCondition: (BlockState) -> Boolean = { !it.fluidState.isEmpty }
override val surroundingCondition: (BlockState) -> Boolean = isAirCondition
override fun calculate(input: AreaSpawningInput): SurfaceSpawningContext {
return SurfaceSpawningContext(
cause = input.cause,
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
width = getHorizontalSpace(input, surroundingCondition, config.maxHorizontalSpace, offsetY = 1),
height = getHeight(input, surroundingCondition, config.maxVerticalSpace / 2, offsetY = 1),
depth = getDepth(input, baseCondition, config.maxVerticalSpace / 2),
slice = input.slice,
nearbyBlocks = getNearbyBlocks(input)
)
}
}
@@ -33,7 +33,8 @@ class BestSpawnerConfig {
val replaceWithNewVersion = true
val contextWeights = mutableMapOf(
"grounded" to 1F,
"submerged" to 0.7F
"submerged" to 0.95F,
"surface" to 0.05F
)
val buckets = mutableListOf(
SpawnBucket("common", 94.4F),
@@ -13,15 +13,15 @@ import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.nextBetween
import java.util.UUID
import kotlin.math.atan
import kotlin.math.cos
import kotlin.math.sin
import kotlin.random.Random
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.math.MathHelper.PI
import net.minecraft.util.math.MathHelper.ceil
import net.minecraft.util.math.MathHelper.nextBetween
import net.minecraft.util.math.random.Random
/**
* A spawner that works around a single player. It will do basic tracking of a player's speed
@@ -39,12 +39,12 @@ class PlayerSpawner(player: ServerPlayerEntity, spawns: SpawnPool, manager: Spaw
val sliceDiameter = config.worldSliceDiameter
val sliceHeight = config.worldSliceHeight
val rand = Random.create()
val rand = Random.Default
val center = player.pos
val r = nextBetween(rand, config.minimumSliceDistanceFromPlayer, config.maximumSliceDistanceFromPlayer)
val thetatemp = atan(player.velocity.z / player.velocity.x) + nextBetween(rand, -PI/2, PI/2 )
val r = rand.nextBetween(config.minimumSliceDistanceFromPlayer, config.maximumSliceDistanceFromPlayer)
val thetatemp = atan(player.velocity.z / player.velocity.x) + rand.nextBetween(-PI/2, PI/2 )
val theta = if (player.velocity.horizontalLength() < 0.1) {
rand.nextDouble() * 2 * PI
} else if (player.velocity.x < 0) {
@@ -44,11 +44,15 @@ open class JSONStoreAdapter(
}
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
val br = BufferedReader(FileReader(file))
val json = gson.fromJson<JsonObject>(br)
br.close()
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
store.loadFromJSON(json)
return store
return try {
val br = BufferedReader(FileReader(file))
val json = gson.fromJson<JsonObject>(br)
br.close()
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
store.loadFromJSON(json)
store
} catch (e: Exception) {
null
}
}
}
@@ -38,9 +38,9 @@ open class NBTStoreAdapter(
val nbt = NbtIo.readCompressed(file)
store.loadFromNBT(nbt)
store
} catch (eof: EOFException) {
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
store
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.storage.adapter.flatifle
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.adapter.CobblemonAdapterParent
@@ -67,6 +68,7 @@ abstract class OneToOneFileStoreAdapter<S>(
try {
val tempLoaded = load(tempFile, storeClass, uuid)
if (tempLoaded != null) {
save(file, serialize(tempLoaded))
return tempLoaded
}
} finally {
@@ -76,6 +78,10 @@ abstract class OneToOneFileStoreAdapter<S>(
return if (file.exists()) {
load(file, storeClass, uuid)
?: let {
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
} else {
null
}
@@ -20,7 +20,7 @@ import net.minecraft.network.PacketByteBuf
* @since March 9th, 2022
*/
data class BattleFormat(
val mod: String = "Cobblemon",
val mod: String = "cobblemon",
val battleType: BattleType = BattleTypes.SINGLES,
val ruleSet: Set<String> = setOf()
) {
@@ -8,15 +8,12 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon.showdown
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.battles.runner.GraalShowdown
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.time.Instant
import java.util.Optional
import java.util.UUID
@@ -113,10 +110,6 @@ object BattleRegistry {
return team.joinToString("]")
}
/**
* Temporary starting method for a battle.
* TODO: Replace with a builder for battle definition and then a starting method that takes the built result?
*/
fun startBattle(
battleFormat: BattleFormat,
side1: BattleSide,
@@ -126,8 +119,8 @@ object BattleRegistry {
battleMap[battle.battleId] = battle
// Build request message
val jsonArray = JsonArray()
jsonArray.add(">start { \"format\": ${battleFormat.toFormatJSON()} }")
val messages = mutableListOf<String>()
messages.add(">start { \"format\": ${battleFormat.toFormatJSON()} }")
/*
* Showdown IDs are like p1, p2, p3, etc. Showdown uses these keys to identify who is doing what to whom.
@@ -167,21 +160,16 @@ object BattleRegistry {
// -> Add the players and team
for (actor in battle.actors.sortedBy { it.showdownId }) {
jsonArray.add(""">player ${actor.showdownId} {"name":"${actor.uuid}","team":"${actor.pokemonList.packTeam()}"}""")
messages.add(""">player ${actor.showdownId} {"name":"${actor.uuid}","team":"${actor.pokemonList.packTeam()}"}""")
}
// -> Set team size
for (actor in battle.actors.sortedBy { it.showdownId }) {
jsonArray.add(">${actor.showdownId} team ${actor.pokemonList.count()}")
messages.add(">${actor.showdownId} team ${actor.pokemonList.count()}")
}
// Compiles the request and sends it off
val request = JsonObject()
request.addProperty(DataKeys.REQUEST_TYPE, DataKeys.REQUEST_BATTLE_START)
request.addProperty(DataKeys.REQUEST_BATTLE_ID, battle.battleId.toString())
request.add(DataKeys.REQUEST_MESSAGES, jsonArray)
showdown.write(gson.toJson(request))
GraalShowdown.startBattle(battle, messages.toTypedArray())
return battle
}
@@ -99,7 +99,7 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
// No PP or disabled or something
return false
}
val availableTargets = move.target.targetList(activeBattlePokemon) ?: return true
val availableTargets = move.target.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
@@ -28,7 +28,6 @@ import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownConnection
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleInitializePacket
@@ -96,6 +95,7 @@ object ShowdownInterpreter {
updateInstructions["|-status|"] = this::handleStatusInstruction
updateInstructions["|-end|"] = this::handleEndInstruction
updateInstructions["|-miss|"] = this::handleMissInstruction
updateInstructions["|-hitcount|"] = this::handleHitCountInstruction
sideUpdateInstructions["|request|"] = this::handleRequestInstruction
splitUpdateInstructions["|switch|"] = this::handleSwitchInstruction
@@ -149,18 +149,15 @@ object ShowdownInterpreter {
else -> "HUH!?"
}
fun interpretMessage(message: String) {
fun interpretMessage(battleId: UUID, message: String) {
// Check key map and use function if matching
val battleId = message.split(ShowdownConnection.LINE_START)[0]
val rawMessage = message.replace(battleId + ShowdownConnection.LINE_START, "")
if (rawMessage.startsWith("{\"winner\":\"")) {
if (message.startsWith("{\"winner\":\"")) {
// The post-win message is something we don't care about just yet. It's basically a summary of what happened in the battle.
// Check /docs/example-post-win-message.json for its format.
return
}
val battle = BattleRegistry.getBattle(UUID.fromString(battleId))
val battle = BattleRegistry.getBattle(battleId)
if (battle == null) {
LOGGER.info("No battle could be found with the id: $battleId")
@@ -169,7 +166,7 @@ object ShowdownInterpreter {
runOnServer {
battle.showdownMessages.add(message)
interpret(battle, rawMessage)
interpret(battle, message)
}
}
@@ -904,4 +901,14 @@ object ShowdownInterpreter {
}
}
// |-hitcount|POKEMON|NUM
fun handleHitCountInstruction(battle: PokemonBattle, message: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
val hitCount = message.substringAfterLast("|").toIntOrNull() ?: -1
val lang = if (hitCount == 1) battleLang("hit_count_singular") else battleLang("hit_count", hitCount)
battle.broadcastChatMessage(lang)
}
}
}
@@ -11,19 +11,17 @@ package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon.config
import com.cobblemon.mod.common.Cobblemon.showdown
import com.cobblemon.mod.common.battles.runner.JavetShowdownConnection
import com.cobblemon.mod.common.battles.runner.GraalShowdown
import com.cobblemon.mod.common.util.FileUtils
import com.cobblemon.mod.common.util.extractTo
import com.cobblemon.mod.common.util.fromJson
import com.google.gson.GsonBuilder
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStreamReader
import java.util.concurrent.CompletableFuture
import net.minecraft.client.MinecraftClient
import net.minecraft.util.Identifier
class ShowdownThread : Thread("Cobblemon Showdown") {
var showdownStarted = CompletableFuture<Unit>()
@@ -32,7 +30,8 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
.create()
override fun run() {
var showdownDir = File(".")
var showdownDir = File("showdown")
showdownDir.mkdir()
val showdownMetadata = loadShowdownMetadata()
@@ -63,73 +62,13 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
}
}
// Initialize showdown connection
showdown = JavetShowdownConnection()
(showdown as JavetShowdownConnection).initializeServer()
GraalShowdown.createContext()
GraalShowdown.boot()
// Sleep for two seconds before attempting connection
sleep(2000)
val maxTries = 15
var tries = 0
// If connection fails, wait another two seconds
while (!attemptConnection() && tries < maxTries) {
tries++
sleep(3000)
}
// Max attempts
if (tries == maxTries) {
LOGGER.error("Failed to connect to showdown after 5 tries.")
MinecraftClient.getInstance().close()
}
LOGGER.info("Showdown has been connected!")
LOGGER.info("Showdown has been started!")
showdownStarted.complete(Unit)
// Reset tries as this will be used by reconnection attempts
tries = 0
// While showdown is not closed, continue to check connection and read messages
while (!showdown.isClosed()) {
// Attempt reconnection if not connected
if (!showdown.isConnected()) {
while (!attemptConnection() && tries < maxTries) {
tries++
sleep(3000)
}
// Max attempts
if (tries == maxTries) {
LOGGER.error("Failed to connect to showdown after 5 tries.")
MinecraftClient.getInstance().close()
}
tries = 0
LOGGER.info("Showdown has been reconnected!")
}
// Reads messages and don't destroy the connection if there is an error
try {
showdown.read(ShowdownInterpreter::interpretMessage)
} catch (e: Exception) {
e.printStackTrace()
}
// Read messages every half a second
sleep(500)
}
}
private fun attemptConnection() : Boolean {
try {
showdown.open()
return true
} catch (exception: IOException) {
return false
}
}
private fun loadShowdownMetadata() : ShowdownMetadata? {
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2022 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.battles.runner
import com.cobblemon.mod.common.Cobblemon
import java.util.logging.Handler
import java.util.logging.Level
import java.util.logging.LogRecord
object GraalLogger : Handler() {
override fun publish(record: LogRecord?) {
if(record == null) {
return
}
when (record.level) {
Level.INFO -> Cobblemon.LOGGER.info(record.message)
Level.WARNING -> Cobblemon.LOGGER.warn(record.message)
Level.SEVERE -> Cobblemon.LOGGER.error(record.message)
else -> Cobblemon.LOGGER.debug(record.message)
}
}
override fun flush() {}
override fun close() {}
}
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2022 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.battles.runner
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import java.io.File
import java.util.UUID
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.HostAccess
import org.graalvm.polyglot.PolyglotAccess
import org.graalvm.polyglot.Value
object GraalShowdown {
@Transient
lateinit var context: Context
@Transient
lateinit var sendBattleMessageFunction: Value
fun createContext() {
val access = HostAccess.newBuilder(HostAccess.ALL).build()
context = Context.newBuilder("js")
.allowIO(true)
.allowExperimentalOptions(true)
.allowPolyglotAccess(PolyglotAccess.ALL)
.allowHostAccess(access)
.allowAllAccess(true)
.allowCreateThread(true)
.logHandler(GraalLogger)
.option("engine.WarnInterpreterOnly", "false")
.option("js.commonjs-require", "true")
.option("js.commonjs-require-cwd", "showdown")
.option(
"js.commonjs-core-modules-replacements",
"buffer:buffer/,crypto:crypto-browserify,path:path-browserify"
)
.allowHostClassLoading(true)
.allowNativeAccess(true)
.allowCreateProcess(true)
.build()
context.eval("js", """
globalThis.process = {
cwd: function() {
return '';
}
}
""".trimIndent())
}
fun boot() {
context.eval("js", File("showdown/index.js").readText())
sendBattleMessageFunction = context.getBindings("js").getMember("sendBattleMessage")
}
fun startBattle(battle: PokemonBattle, messages: Array<String>) {
val startBattleFunction = context.getBindings("js").getMember("startBattle")
startBattleFunction.execute(GraalShowdown, battle.battleId.toString(), messages)
}
fun sendFromShowdown(battleId: String, message: String) {
ShowdownInterpreter.interpretMessage(UUID.fromString(battleId), message)
}
fun sendToShowdown(battleId: UUID, messages: Array<String>) {
sendBattleMessageFunction.execute(battleId.toString(), messages)
}
}
@@ -1,72 +0,0 @@
/*
* Copyright (C) 2022 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.battles.runner
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.InetAddress
import java.net.Socket
import java.nio.charset.Charset
class JavetShowdownConnection : ShowdownConnection {
private lateinit var socket: Socket
private lateinit var writer: OutputStreamWriter
private lateinit var reader: BufferedReader
private var data = ""
private var closed = false
private val serverThread = Thread({ ShowdownServer.start() }, "Cobblemon Showdown Server")
fun initializeServer() {
serverThread.start()
}
override fun open() {
socket = Socket(InetAddress.getLocalHost(), ShowdownServer.port, InetAddress.getLocalHost(), 0)
socket.keepAlive = true
writer = socket.getOutputStream().writer(charset = Charset.forName("ascii"))
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
}
override fun close() {
socket.close()
closed = true
}
override fun write(input: String) {
writer.write(input + ShowdownConnection.LINE_END)
writer.flush()
}
override fun read(messageHandler: (String) -> Unit) {
try {
while (reader.ready()) {
val char = reader.read()
if (char > -1) {
data += char.toChar()
if (data.endsWith(ShowdownConnection.LINE_END)) {
messageHandler(data.replace(ShowdownConnection.LINE_END, ""))
data = ""
}
}
}
} catch (exception: IOException) {
exception.printStackTrace()
}
}
override fun isClosed() : Boolean {
return closed
}
override fun isConnected(): Boolean {
return socket.isConnected
}
}
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2022 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.battles.runner
import java.io.File
import java.io.IOException
/**
* Modified from https://lankydan.dev/running-a-kotlin-class-as-a-subprocess
*
* Spawns a process and returns it
*/
@Throws(IOException::class, InterruptedException::class)
fun exec(clazz: Class<*>, args: List<String> = emptyList(), jvmArgs: List<String> = emptyList()): Process {
val javaHome = System.getProperty("java.home")
val javaBin = javaHome + File.separator + "bin" + File.separator + "java"
val classpath = System.getProperty("java.class.path")
val className = clazz.name
val command = ArrayList<String>()
command.add(javaBin)
command.addAll(jvmArgs)
command.add("-cp")
command.add(classpath)
command.add(className)
command.addAll(args)
val builder = ProcessBuilder(command)
return builder.inheritIO().start()
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2022 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.battles.runner
interface ShowdownConnection {
fun open()
fun close()
fun write(input: String)
fun read(messageHandler: (String) -> Unit)
fun isClosed(): Boolean
fun isConnected(): Boolean
companion object {
const val LINE_END = "{EOT}"
const val LINE_START = "{SOT}"
}
}
@@ -1,180 +0,0 @@
/*
* Copyright (C) 2022 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.battles.runner
import com.caoccao.javet.interop.V8Host
import com.caoccao.javet.interop.V8Runtime
import java.net.DatagramSocket
import java.net.ServerSocket
object ShowdownServer {
var minPort = 25650
var maxPort = 25700
var port = 0
fun findOpenPort(): Int? {
for (port in minPort..maxPort) {
if (isOpenPort(port)) {
return port
}
}
return null
}
fun isOpenPort(port: Int): Boolean {
var ss: ServerSocket? = null
var ds: DatagramSocket? = null
try {
ss = ServerSocket(port)
ss.reuseAddress = true
ds = DatagramSocket(port)
ds.reuseAddress = true
return true
} catch (_: Exception) {
} finally {
try { ss?.close() } catch (_: Exception) {}
try { ds?.close() } catch (_: Exception) {}
}
return false
}
fun start() {
val port = findOpenPort() ?: throw IllegalStateException("No port open in range $minPort-$maxPort for Showdown to start on!")
this.port = port
val runtime = V8Host.getNodeInstance().createV8Runtime<V8Runtime>()
runtime.use {
val executor = it.getExecutor(
"""
const PokemonShowdown = require('pokemon-showdown');
var Net = require('net')
const port = $port;
const battleMap = new Map();
// Use net.createServer() in your code. This is just for illustration purpose.
// Create a new TCP server.
const server = Net.createServer();
// The server listens to a socket for a client to make a connection request.
// Think of a socket as an end point.
server.listen(port, function () {
console.log('Server listening for connection requests on socket localhost: ' + port);
});
// When a client requests a connection with the server, the server creates a new
// socket dedicated to that client.
server.on('connection', function (socket) {
console.log('A new connection has been established.');
// The server can also receive data from the client by reading from its socket.
// The end of the message is the tag {EOT}
let messageBuffer = new MessageBuffer("{EOT}")
socket.on('data', function (chunk) {
messageBuffer.push(chunk);
// When is a message is fully formed from chunking, we handle the data
while (!messageBuffer.isFinished()) {
const message = messageBuffer.handleData()
try {
// Parses the request and goes over message types
const request = JSON.parse(message);
switch(request.RequestType) {
case 'StartBattle': {
const battleStream = new PokemonShowdown.BattleStream();
battleMap.set(request.RequestBattleId, battleStream);
// Join messages with new line
for(const element of request.RequestMessages) {
battleStream.write(element);
}
// Any battle output then gets written to the socket
// TODO: Send the battle id with this
// TODO: Add a way to cancel battle earlier and close stream
(async () => {
for await (const output of battleStream) {
socket.write(request.RequestBattleId + "{SOT}" + output + "{EOT}")
}
})();
break;
}
case 'SendMessage': {
const battleStream = battleMap.get(request.RequestBattleId)
for(const element of request.RequestMessages) {
battleStream.write(element);
}
break;
}
}
} catch (error) {
console.error(error);
}
}
});
// When the client requests to end the TCP connection with the server, the server
// ends the connection.
socket.on('end', function () {
console.log('Closing connection with the client');
server.close();
});
// Don't forget to catch error, for your own sake.
socket.on('error', function (err) {
console.log(`Error: ` + err);
});
});
class MessageBuffer {
constructor(delimiter) {
this.delimiter = delimiter
this.buffer = ""
}
isFinished() {
if (
this.buffer.length === 0 ||
this.buffer.indexOf(this.delimiter) === -1
) {
return true
}
return false
}
push(data) {
this.buffer += data
}
getMessage() {
const delimiterIndex = this.buffer.indexOf(this.delimiter)
if (delimiterIndex !== -1) {
const message = this.buffer.slice(0, delimiterIndex)
this.buffer = this.buffer.replace(message + this.delimiter, "")
return message
}
return null
}
handleData() {
/**
* Try to accumulate the buffer with messages
*
* If the server isn't sending delimiters for some reason
* then nothing will ever come back for these requests
*/
const message = this.getMessage()
return message
}
}
""".trimIndent()
)
executor.resourceName = "./node_modules"
executor.executeVoid()
}
}
}
@@ -1,67 +0,0 @@
/*
* Copyright (C) 2022 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.battles.runner
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.InetAddress
import java.net.Socket
import java.nio.charset.Charset
class StandardShowdownConnection(host: InetAddress, port: Int): ShowdownConnection {
private lateinit var socket: Socket
private lateinit var writer: OutputStreamWriter
private lateinit var reader: BufferedReader
private var data = ""
private var closed: Boolean = false
override fun open() {
socket = Socket(InetAddress.getLocalHost(), 25567, InetAddress.getLocalHost(), 25566)
socket.keepAlive = true
writer = socket.getOutputStream().writer(charset = Charset.forName("ascii"))
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
}
override fun close() {
socket.close()
closed = true
}
override fun write(input: String) {
writer.write(input + ShowdownConnection.LINE_END)
writer.flush()
}
override fun read(messageHandler: (String) -> Unit) {
try {
while (reader.ready()) {
val char = reader.read()
if (char > -1) {
data += char.toChar()
if (data.endsWith(ShowdownConnection.LINE_END)) {
messageHandler(data.replace(ShowdownConnection.LINE_END, ""))
data = ""
}
}
}
} catch (exception: IOException) {
exception.printStackTrace()
}
}
override fun isClosed(): Boolean {
return closed
}
override fun isConnected(): Boolean {
return socket.isConnected
}
}
@@ -151,7 +151,7 @@ object CobblemonClient {
fun reloadCodedAssets(resourceManager: ResourceManager) {
LOGGER.info("Reloading assets")
BedrockAnimationRepository.clear()
BedrockAnimationRepository.loadAnimations(resourceManager)
PokemonModelRepository.reload(resourceManager)
LOGGER.info("Loaded assets")
// PokeBallModelRepository.reload(resourceManager)
@@ -22,6 +22,7 @@ class ClientBattlePokemon(
val uuid: UUID,
var displayName: MutableText,
var properties: PokemonProperties,
var aspects: Set<String>,
var hpRatio: Float,
var status: PersistentStatus?,
var statChanges: MutableMap<Stat, Int>
@@ -6,39 +6,53 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary
package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class ExitButton(
pX: Int, pY: Int,
onPress: PressAction
): ButtonWidget(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress) {
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Text.literal("Exit"), onPress) {
companion object {
private const val WIDTH = 52F
private const val HEIGHT = 26F
private const val WIDTH = 26F
private const val HEIGHT = 13F
private const val SCALE = 0.5F
private val exitButtonResource = cobblemonResource("ui/summary/summary_back_button.png")
private val buttonResource = cobblemonResource("ui/common/back_button.png")
private val iconResource = cobblemonResource("ui/common/back_button_icon.png")
}
override fun renderButton(pMatrixStack: MatrixStack, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
blitk(
matrixStack = pMatrixStack,
texture = exitButtonResource,
x = x * 2,
y = y * 2,
texture = buttonResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2,
textureHeight = HEIGHT * 2
)
blitk(
matrixStack = pMatrixStack,
texture = iconResource,
x = (x + 7) / SCALE,
y = (y + 4) / SCALE,
width = 21,
height = 11,
scale = SCALE
)
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + (WIDTH * SCALE))) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + (HEIGHT * SCALE)))
override fun playDownSound(soundManager: SoundManager) {
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -163,7 +163,7 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
reversed = !left,
species = battlePokemon.species,
level = battlePokemon.level,
aspects = battlePokemon.properties.aspects,
aspects = battlePokemon.aspects,
displayName = battlePokemon.displayName,
gender = battlePokemon.gender,
status = battlePokemon.status,
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.SingleActionRequest
@@ -17,6 +18,8 @@ import com.cobblemon.mod.common.util.battleLang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.Selectable
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
@@ -94,5 +97,9 @@ class BattleGeneralActionSelection(
override fun appendNarrations(builder: NarrationMessageBuilder) {
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
override fun getType() = Selectable.SelectionType.NONE
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.text.bold
@@ -26,6 +27,8 @@ import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.toRGB
import net.minecraft.client.MinecraftClient
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.math.MathHelper.floor
@@ -156,7 +159,7 @@ class BattleMoveSelection(
}
moveSelection.playDownSound(MinecraftClient.getInstance().soundManager)
val targets = move.target.targetList(moveSelection.request.activePokemon)
if (targets == null) {
if (targets.isNullOrEmpty()) {
moveSelection.battleGUI.selectAction(moveSelection.request, MoveActionResponse(move.id, null))
} else if (targets.size == 1) {
moveSelection.battleGUI.selectAction(moveSelection.request, MoveActionResponse(move.id, targets[0].getPNX()))
@@ -185,4 +188,8 @@ class BattleMoveSelection(
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.battles.ShowdownPokemon
import com.cobblemon.mod.common.battles.SwitchActionResponse
import com.cobblemon.mod.common.client.CobblemonClient
@@ -19,6 +20,8 @@ import com.cobblemon.mod.common.util.battleLang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.Selectable
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.math.MathHelper.ceil
class BattleSwitchPokemonSelection(
@@ -120,5 +123,9 @@ class BattleSwitchPokemonSelection(
override fun appendNarrations(builder: NarrationMessageBuilder) {
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
override fun getType() = Selectable.SelectionType.HOVERED
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.pokemon.Pokemon
class BoxStorageSlot(
x: Int, y: Int,
private val parent: StorageWidget,
private val pc: ClientPC,
val position: PCPosition,
onPress: PressAction
) : StorageSlot(x, y, parent, onPress) {
override fun getPokemon(): Pokemon? {
return pc.get(position)
}
override fun shouldRender(): Boolean {
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
} else {
grabbedSlot.getPokemon() != getPokemon()
}
}
}
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.TexturedButtonWidget
import net.minecraft.client.util.math.MatrixStack
class ExitButton(
pX: Int, pY: Int,
val pWidth: Int, val pHeight: Int,
pXTexStart: Int, pYTexStart: Int, pYDiffText: Int,
onPress: PressAction
): TexturedButtonWidget(pX, pY, pWidth, pHeight, pXTexStart, pYTexStart, pYDiffText, exitButtonResource, onPress) {
companion object {
private val exitButtonResource = cobblemonResource("ui/pc/pc_exit.png")
}
override fun renderButton(pMatrixStack: MatrixStack, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
hovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
if (isHovered) {
blitk(
matrixStack = pMatrixStack,
x = x + 0.1, y = y - 1F,
texture = exitButtonResource,
width = pWidth, height = pHeight
)
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.util.math.MatrixStack
class GrabbedStorageSlot(
x: Int, y: Int,
parent: StorageWidget,
private val pokemon: Pokemon
) : StorageSlot(x, y, parent, {}) {
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderSlot(matrices, mouseX - (width / 2), mouseY - (height / 2))
}
override fun getPokemon(): Pokemon? {
return pokemon
}
override fun isSelected(): Boolean {
return true
}
}
@@ -8,33 +8,47 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.TexturedButtonWidget
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class NavigationButton(
pX: Int, pY: Int,
val pWidth: Int, val pHeight: Int,
pXTexStart: Int, pYTexStart: Int, pYDiffText: Int,
private val forward: Boolean,
onPress: PressAction,
): TexturedButtonWidget(pX, pY, pWidth, pHeight, pXTexStart, pYTexStart, pYDiffText, forwardButtonResource, onPress) {
onPress: PressAction
): ButtonWidget(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal("Navigation"), onPress) {
companion object {
private val forwardButtonResource = cobblemonResource("ui/pc/pc_box_right.png")
private val backwardsButtonResource = cobblemonResource("ui/pc/pc_box_left.png")
private const val WIDTH = 8F
private const val HEIGHT = 10F
private const val SCALE = 0.5F
private val forwardButtonResource = cobblemonResource("ui/pc/pc_arrow_next.png")
private val backwardsButtonResource = cobblemonResource("ui/pc/pc_arrow_previous.png")
}
override fun renderButton(pMatrixStack: MatrixStack, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
hovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
if (isHovered) {
blitk(
matrixStack = pMatrixStack,
x = x + 0.25, y = y - 0.5F,
texture = if (forward) forwardButtonResource else backwardsButtonResource,
width = pWidth, height = pHeight
)
}
val hovered = (isHovered(pMouseX.toDouble(), pMouseY.toDouble()))
blitk(
matrixStack = pMatrixStack,
x = x / SCALE,
y = y / SCALE,
texture = if (forward) forwardButtonResource else backwardsButtonResource,
width = WIDTH,
height = HEIGHT,
vOffset = if (hovered) HEIGHT else 0,
textureHeight = HEIGHT * 2,
scale = SCALE
)
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + (WIDTH * SCALE))) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + (HEIGHT * SCALE)))
}
@@ -1,81 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.util.scaleIt
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.math.Quaternion
import net.minecraft.util.math.Vec3f
class PCBoxMemberWidget(
x: Int, y: Int,
private val parent: PCWidget,
private val pcGui: PCGui,
private val pc: ClientPC,
val position: PCPosition,
onPress: PressAction
) : ButtonWidget(x - PC_BOX_DIMENSION, y, PC_BOX_DIMENSION, PC_BOX_DIMENSION, Text.literal("PCBoxMember"), onPress) {
companion object {
// Box slot
private const val PC_BOX_DIMENSION = 32
// Portrait
private const val PORTRAIT_DIMENSIONS = 27
}
var playingDownSound = false
override fun playDownSound(soundManager: SoundManager) {
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
val pokemon = pc.get(position) ?: return
matrices.push()
val minecraft = MinecraftClient.getInstance()
RenderSystem.enableScissor(
this.scaleIt(this.x + 3),
minecraft.window.height - this.scaleIt(this.y + PORTRAIT_DIMENSIONS + 2),
this.scaleIt(PORTRAIT_DIMENSIONS),
this.scaleIt(PORTRAIT_DIMENSIONS)
)
matrices.translate(this.x + (PORTRAIT_DIMENSIONS / 2.0) + 4, this.y + 4.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
drawProfilePokemon(
renderablePokemon = pokemon.asRenderablePokemon(),
matrixStack = matrices,
rotation = Quaternion.fromEulerXyzDegrees(Vec3f(13F, 35F, 0F)),
state = null,
scale = 6F
)
RenderSystem.disableScissor()
matrices.pop()
if (position == parent.selectedPosition) {
blitk(
matrixStack = matrices,
texture = PCWidget.selectedResource,
x = x,
y = y,
height = PC_BOX_DIMENSION,
width = PC_BOX_DIMENSION
)
}
}
}
@@ -0,0 +1,410 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvent
import net.minecraft.text.Text
class PCGUI(
private val pc: ClientPC,
private val party: ClientParty
) : Screen(Text.translatable("cobblemon.ui.pc.title")) {
companion object {
const val BASE_WIDTH = 349
const val BASE_HEIGHT = 205
const val TYPE_SPACER_WIDTH = 128
const val TYPE_SPACER_HEIGHT = 12
const val PC_SPACER_WIDTH = 342
const val PC_SPACER_HEIGHT = 14
const val PORTRAIT_SIZE = 66
const val SCALE = 0.5F
private val baseResource = cobblemonResource("ui/pc/pc_base.png")
private val portraitBackgroundResource = cobblemonResource("ui/pc/portrait_background.png")
private val topSpacerResource = cobblemonResource("ui/pc/pc_spacer_top.png")
private val bottomSpacerResource = cobblemonResource("ui/pc/pc_spacer_bottom.png")
private val rightSpacerResource = cobblemonResource("ui/pc/pc_spacer_right.png")
private val typeSpacerResource = cobblemonResource("ui/pc/type_spacer.png")
private val typeSpacerSingleResource = cobblemonResource("ui/pc/type_spacer_single.png")
private val typeSpacerDoubleResource = cobblemonResource("ui/pc/type_spacer_double.png")
}
private lateinit var storageWidget: StorageWidget
private var modelWidget: ModelWidget? = null
internal var previewPokemon: Pokemon? = null
var ticksElapsed = 0
var selectPointerOffsetY = 0
var selectPointerOffsetIncrement = false
override fun init() {
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
// Add Exit Button
this.addDrawableChild(
ExitButton(pX = x + 320, pY = y + 186) {
playSound(CobblemonSounds.PC_OFF.get())
MinecraftClient.getInstance().setScreen(null)
}
)
// Add Forward Button
this.addDrawableChild(
NavigationButton(
pX = x + 221,
pY = y + 17,
forward = true
) { storageWidget.box += 1 }
)
// Add Backwards Button
this.addDrawableChild(
NavigationButton(
pX = x + 119,
pY = y + 17,
forward = false
) { storageWidget.box -= 1 }
)
// Add Storage
this.storageWidget = StorageWidget(
pX = x + 85,
pY = y + 27,
pcGui = this,
pc = pc,
party = party
)
this.setPreviewPokemon(null)
this.addDrawableChild(storageWidget)
super.init()
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrices)
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
// Render Portrait Background
blitk(
matrixStack = matrices,
texture = portraitBackgroundResource,
x = x + 6,
y = y + 27,
width = PORTRAIT_SIZE,
height = PORTRAIT_SIZE
)
// Render Model Portrait
modelWidget?.render(matrices, mouseX, mouseY, delta)
// Render Base Resource
blitk(
matrixStack = matrices,
texture = baseResource,
x = x, y = y,
width = BASE_WIDTH,
height = BASE_HEIGHT
)
// Render Info Labels
drawScaledText(
matrixStack = matrices,
text = lang("ui.info.nature").bold(),
x = x + 39,
y = y + 129.5,
centered = true,
scale = SCALE
)
drawScaledText(
matrixStack = matrices,
text = lang("ui.info.ability").bold(),
x = x + 39,
y = y + 146.5,
centered = true,
scale = SCALE
)
drawScaledText(
matrixStack = matrices,
text = lang("ui.moves").bold(),
x = x + 39,
y = y + 163.5,
centered = true,
scale = SCALE
)
// Render Pokemon Info
val pokemon = previewPokemon
if (pokemon != null) {
// Status
val status = pokemon.status?.status
if (pokemon.isFainted() || status != null) {
val statusName = if (pokemon.isFainted()) "fnt" else status?.showdownName
blitk(
matrixStack = matrices,
texture = cobblemonResource("ui/battle/battle_status_$statusName.png"),
x = x + 34,
y = y + 1,
height = 7,
width = 39,
uOffset = 35,
textureWidth = 74
)
blitk(
matrixStack = matrices,
texture = cobblemonResource("ui/summary/status_trim.png"),
x = x + 34,
y = y + 2,
height = 6,
width = 3
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.status.$statusName").bold(),
x = x + 39,
y = y
)
}
// Level
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.lv").bold(),
x = x + 6,
y = y + 1.5,
shadow = true
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = pokemon.level.toString().text().bold(),
x = x + 19,
y = y + 1.5,
shadow = true
)
// Poké Ball
val ballResource = cobblemonResource("textures/items/poke_balls/" + pokemon.caughtBall.name.path + ".png")
blitk(
matrixStack = matrices,
texture = ballResource,
x = (x + 3.5) / SCALE,
y = (y + 12) / SCALE,
width = 16,
height = 16,
scale = SCALE
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = pokemon.displayName.bold(),
x = x + 12,
y = y + 11.5,
shadow = true
)
if (pokemon.gender != Gender.GENDERLESS) {
val isMale = pokemon.gender == Gender.MALE
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = textSymbol,
x = x + 69, // 64 when tag icon is implemented
y = y + 11.5,
colour = if (isMale) 0x32CBFF else 0xFC5454,
shadow = true
)
}
blitk(
matrixStack = matrices,
texture = if (pokemon.secondaryType != null) typeSpacerDoubleResource else typeSpacerSingleResource,
x = (x + 7) / SCALE,
y = (y + 118.5) / SCALE,
width = TYPE_SPACER_WIDTH,
height = TYPE_SPACER_HEIGHT,
scale = SCALE
)
TypeIcon(
x = x + 39,
y = y + 117,
type = pokemon.primaryType,
secondaryType = pokemon.secondaryType,
doubleCenteredOffset = 5F,
secondaryOffset = 10F,
small = true,
centeredX = true
).render(matrices)
// Nature
drawScaledText(
matrixStack = matrices,
text = pokemon.nature.displayName.asTranslated(),
x = x + 39,
y = y + 137,
centered = true,
shadow = true,
scale = SCALE
)
// Ability
drawScaledText(
matrixStack = matrices,
text = pokemon.ability.displayName.asTranslated(),
x = x + 39,
y = y + 154,
centered = true,
shadow = true,
scale = SCALE
)
// Moves
val moves = pokemon.moveSet.getMoves()
for (i in moves.indices) {
drawScaledText(
matrixStack = matrices,
text = moves[i].displayName,
x = x + 39,
y = y + 170.5 + (7 * i),
centered = true,
shadow = true,
scale = SCALE
)
}
} else {
blitk(
matrixStack = matrices,
texture = typeSpacerResource,
x = (x + 7) / SCALE,
y = (y + 118.5) / SCALE,
width = TYPE_SPACER_WIDTH,
height = TYPE_SPACER_HEIGHT,
scale = SCALE
)
}
// Box Label
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = Text.translatable("cobblemon.ui.pc.box.title", (this.storageWidget.box + 1).toString()).bold(),
x = x + 172,
y = y + 15,
centered = true
)
blitk(
matrixStack = matrices,
texture = topSpacerResource,
x = (x + 86.5) / SCALE,
y = (y + 13) / SCALE,
width = PC_SPACER_WIDTH,
height = PC_SPACER_HEIGHT,
scale = SCALE
)
blitk(
matrixStack = matrices,
texture = bottomSpacerResource,
x = (x + 86.5) / SCALE,
y = (y + 189) / SCALE,
width = PC_SPACER_WIDTH,
height = PC_SPACER_HEIGHT,
scale = SCALE
)
blitk(
matrixStack = matrices,
texture = rightSpacerResource,
x = (x + 275.5) / SCALE,
y = (y + 184) / SCALE,
width = 64,
height = 24,
scale = SCALE
)
super.render(matrices, mouseX, mouseY, delta)
}
/**
* Whether this Screen should pause the Game in SinglePlayer
*/
override fun shouldPause(): Boolean {
return false
}
override fun tick() {
ticksElapsed++
// Calculate select pointer offset
var delayFactor = 3
if (ticksElapsed % (2 * delayFactor) == 0) selectPointerOffsetIncrement = !selectPointerOffsetIncrement
if (ticksElapsed % delayFactor == 0) selectPointerOffsetY += if (selectPointerOffsetIncrement) 1 else -1
}
private fun playSound(soundEvent: SoundEvent) {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
}
fun setPreviewPokemon(pokemon: Pokemon?) {
if (pokemon != null) {
previewPokemon = pokemon
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
modelWidget = ModelWidget(
pX = x + 6,
pY = y + 27,
pWidth = PORTRAIT_SIZE,
pHeight = PORTRAIT_SIZE,
pokemon = pokemon.asRenderablePokemon(),
baseScale = 2F,
rotationY = 325F,
offsetY = -10.0
)
} else {
previewPokemon = null;
modelWidget = null;
}
}
}
@@ -1,128 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.ColourLibrary
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class PCGui(
private val pc: ClientPC,
private val party: ClientParty
) : Screen(Text.translatable("cobblemon.ui.pc.title")) {
companion object {
// Size of Background
private const val backgroundHeight = 200
private const val backgroundWidth = 300
// Textures
private val background = cobblemonResource("ui/pc/pc_base.png")
private val underlay = cobblemonResource("ui/pc/pc_underlay.png")
}
private lateinit var pcWidget: PCWidget
override fun init() {
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
// Add Exit Button
this.addDrawableChild(
ExitButton(
pX = x + 249, pY = y + 4,
pWidth = 16, pHeight = 12,
pXTexStart = 0, pYTexStart = 0, pYDiffText = 0
) {
MinecraftClient.getInstance().setScreen(null)
})
// Add Forward Button
this.addDrawableChild(
NavigationButton(
pX = x + 230, pY = y + 9,
pWidth = 9, pHeight = 14,
pXTexStart = 0, pYTexStart = 0, pYDiffText = 0,
forward = true
) {
pcWidget.box += 1
})
// Add Backwards Button
this.addDrawableChild(
NavigationButton(
pX = x + 108, pY = y + 9,
pWidth = 9, pHeight = 14,
pXTexStart = 0, pYTexStart = 0, pYDiffText = 0,
forward = false
) {
pcWidget.box -= 1
})
// Add PC
this.pcWidget = PCWidget(
pX = x + 116, pY = y + 32,
pWidth = 175, pHeight = 145,
pcGui = this,
pc = pc,
party = party
)
this.addDrawableChild(pcWidget)
super.init()
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrices)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
// Rendering PC Underlay
blitk(
matrixStack = matrices,
texture = underlay,
x = x + 7, y = y + 25,
width = 65, height = 65
)
// Rendering UI Background
blitk(
matrixStack = matrices,
texture = background,
x = x, y = y,
width = backgroundWidth, height = backgroundHeight
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = Text.translatable("cobblemon.ui.box.title", (this.pcWidget.box + 1).toString()),
x = (x + 173.5), y = (y + 8.75),
colour = ColourLibrary.WHITE, shadow = false,
centered = true,
scale = 1.5f,
)
super.render(matrices, mouseX, mouseY, delta)
}
/**
* Whether this Screen should pause the Game in SinglePlayer
*/
override fun shouldPause(): Boolean {
return false
}
}
@@ -1,80 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonFloatingState
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.math.Quaternion
import net.minecraft.util.math.Vec3f
class PCPreviewSelectedWidget(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
val baseScale: Float = 2.7F,
private val parent: PCWidget,
private val pc: ClientPC,
private val party: ClientParty,
): SoundlessWidget(pX, pY, pWidth, pHeight, Text.literal("PC - PreviewSelectedWidget")) {
companion object {
var render = true
}
var state = PokemonFloatingState()
private val minecraft = MinecraftClient.getInstance()
private var rotVec = Vec3f(13F, 35F, 0F)
override fun render(pMatrixStack: MatrixStack, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
if (!render) {
return
}
hovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
renderPKM(pMatrixStack)
}
private fun renderPKM(poseStack: MatrixStack) {
val pokemon = parent.getSelectedPokemon() ?: return
poseStack.push()
RenderSystem.enableScissor(
(x * minecraft.window.scaleFactor).toInt(),
(minecraft.window.height - (y * minecraft.window.scaleFactor) - (height * minecraft.window.scaleFactor)).toInt(),
(width * minecraft.window.scaleFactor).toInt(),
(height * minecraft.window.scaleFactor).toInt()
)
poseStack.translate(x + width * 0.5, y.toDouble(), 0.0)
poseStack.scale(baseScale, baseScale, baseScale)
poseStack.push()
drawProfilePokemon(
renderablePokemon = pokemon.asRenderablePokemon(),
matrixStack = poseStack,
rotation = Quaternion.fromEulerXyzDegrees(rotVec),
state = state
)
poseStack.pop()
RenderSystem.disableScissor()
poseStack.pop()
}
override fun onClick(pMouseX: Double, pMouseY: Double) {
}
}
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class PCTrashWidget(
x: Int, y: Int,
private val parent: PCWidget,
private val pc: ClientPC,
onPress: PressAction
) : ButtonWidget(x, y, WIDTH, HEIGHT, Text.literal("PCTrash"), onPress) {
companion object {
private const val WIDTH = 13
private const val HEIGHT = 14
val trashIcon = cobblemonResource("ui/pc/pc_trash.png")
val trashIconHovered = cobblemonResource("ui/pc/pc_trash_hovered.png")
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (!parent.canDeleteSelected()) {
return
}
val isHovered = mouseX in x..(x + WIDTH) && mouseY in y..(y + HEIGHT)
val icon = if (isHovered) trashIconHovered else trashIcon
blitk(
matrixStack = matrices,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
texture = icon
)
}
}
@@ -1,261 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvents
import net.minecraft.text.Text
class PCWidget(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
private val pcGui: PCGui,
private val pc: ClientPC,
private val party: ClientParty
) : SoundlessWidget(pX, pY, pWidth, pHeight, Text.literal("PCWidget")) {
companion object {
val selectedResource = cobblemonResource("ui/pc/pc_selected.png")
}
var selectedPosition: StorePosition? = null
var box = 0
set(value) {
// If value is within min and max
field = if (value > 0 && value < pc.boxes.size) {
value
}
// If value is less than zero, wrap around to end.
else if (value < 0) {
pc.boxes.size - 1
}
// Else it's greater than max, wrap around to start.
else {
0
}
this.setupMemberWidgets()
}
private val partyWidgets = arrayListOf<PartyMemberWidget>()
private val pcWidgets = arrayListOf<PCBoxMemberWidget>()
private val previewWidget: PCPreviewSelectedWidget
private val trashWidget: PCTrashWidget
fun canDeleteSelected(): Boolean {
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1) && selectedPosition != null
}
init {
this.setupMemberWidgets()
this.previewWidget = PCPreviewSelectedWidget(
pX = x - 105,
pY = y,
pWidth = 60,
pHeight = 70,
baseScale = 1.5f,
parent = this,
pc = pc,
party = party
).also {
this.addWidget(it)
}
this.trashWidget = PCTrashWidget(
x = x - 22,
y = y + 150,
parent = this,
pc = pc,
onPress = {
if (canDeleteSelected()) {
val position = selectedPosition ?: return@PCTrashWidget
val pokemon = getSelectedPokemon() ?: return@PCTrashWidget
val packet = when (position) {
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
else -> return@PCTrashWidget
}
CobblemonNetwork.sendToServer(packet)
selectedPosition = null
}
}
).also {
addWidget(it)
}
}
private fun setupMemberWidgets() {
this.resetWidgets()
var index = 0
// Box members
for (row in 1..5) {
for (col in 1..6) {
PCBoxMemberWidget(
x = x + (col-1) * 29,
y = y + (row-1) * 29,
parent = this,
pcGui = pcGui,
pc = pc,
position = PCPosition(box, index),
onPress = { this.onPokemonPressed(it) }
).also { widget ->
this.addWidget(widget)
this.pcWidgets.add(widget)
}
index++
}
}
// Party slots
for (partySlot in 0..5) {
val texture = when(partySlot) {
0 -> PartyMemberWidget.slotOneResource
5 -> PartyMemberWidget.slotSixResource
else -> PartyMemberWidget.slotTwoThroughFiveResource
}
PartyMemberWidget(
x = x + 185,
y = (y - 19) + partySlot * 31,
parent = this,
pcGui = pcGui,
pc = pc,
party = party,
position = PartyPosition(partySlot),
texture = texture,
onPress = { this.onPokemonPressed(it) }
).also { widget ->
this.addWidget(widget)
this.partyWidgets.add(widget)
}
}
}
fun getSelectedPokemon(): Pokemon? {
val selectedPosition = this.selectedPosition ?: return null
return when (selectedPosition) {
is PCPosition -> pc.get(selectedPosition)
is PartyPosition -> party.get(selectedPosition)
else -> null
}
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
this.previewWidget.render(matrices, mouseX, mouseY, delta)
this.pcWidgets.forEach { widget -> widget.render(matrices, mouseX, mouseY, delta) }
this.partyWidgets.forEach { widget -> widget.render(matrices, mouseX, mouseY, delta) }
this.trashWidget.render(matrices, mouseX, mouseY, delta)
}
private fun resetWidgets() {
this.partyWidgets.forEach(this::removeWidget)
this.partyWidgets.clear()
this.pcWidgets.forEach(this::removeWidget)
this.pcWidgets.clear()
}
fun clickSound() {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f))
}
private fun onPokemonPressed(button: ButtonWidget) {
// Only use on member widgets
val clickedPosition = when(button) {
is PCBoxMemberWidget -> button.position
is PartyMemberWidget -> button.position
else -> return
}
// Clicking on itself, so unselect position.
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
this.selectedPosition = null
clickSound()
return
}
// Check for selecting a Pokémon.
val clickedPokemon = when(button) {
is PCBoxMemberWidget -> pc.get(clickedPosition as PCPosition)
is PartyMemberWidget -> party.get(clickedPosition as PartyPosition)
else -> null
}
if (this.selectedPosition == null && clickedPokemon != null) {
this.selectedPosition = clickedPosition
clickSound()
return
}
// Handle movement within the PC.
val selectedPokemon = when(this.selectedPosition) {
is PCPosition -> pc.get(this.selectedPosition as PCPosition)
is PartyPosition -> party.get(this.selectedPosition as PartyPosition)
else -> null
} ?: return
// PC -> PC
if (this.selectedPosition is PCPosition && clickedPosition is PCPosition) {
val packet = clickedPokemon?.let { SwapPCPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonPacket(selectedPokemon.uuid, selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
clickSound()
this.selectedPosition = null
}
// PC -> Party
else if (this.selectedPosition is PCPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(clickedPokemon.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonToPartyPacket(selectedPokemon.uuid, this.selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
this.selectedPosition = null
}
// Party -> PC
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
return
}
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
packet.sendToServer()
this.selectedPosition = null
}
// Party -> Party
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
packet.sendToServer()
this.selectedPosition = null
}
}
}
@@ -1,107 +0,0 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.scaleIt
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.Quaternion
import net.minecraft.util.math.Vec3f
class PartyMemberWidget(
x: Int, y: Int,
private val parent: PCWidget,
private val pcGui: PCGui,
private val pc: ClientPC,
private val party: ClientParty,
val position: PartyPosition,
private val texture: Identifier,
onPress: PressAction
) : ButtonWidget(x - PC_BOX_DIMENSION, y, PC_BOX_DIMENSION, PC_BOX_DIMENSION, Text.literal("PartyMember"), onPress) {
companion object {
// Box slot
private const val PC_BOX_DIMENSION = 32
// Portrait
private const val PORTRAIT_DIMENSIONS = 29
// Slot Textures
val slotOneResource = cobblemonResource("ui/pc/pc_party_1.png")
val slotTwoThroughFiveResource = cobblemonResource("ui/pc/pc_party_2-5.png")
val slotSixResource = cobblemonResource("ui/pc/pc_party_6.png")
}
override fun playDownSound(soundManager: SoundManager) {
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
matrices.push()
val xOffset = if (texture == slotSixResource) -2f else 0f
val slotWidth = if (texture == slotSixResource) 35 else 33
blitk(
matrixStack = matrices,
x = x + xOffset + -0.9f, y = y - 1F,
texture = this.texture,
width = slotWidth, height = 33
)
val pokemon = party.get(position)
if (pokemon != null) {
val pokemonX = this.x - 3
val pokemonY = this.y - 1
val minecraft = MinecraftClient.getInstance()
RenderSystem.enableScissor(
this.scaleIt(pokemonX + 3),
minecraft.window.height - this.scaleIt(pokemonY + PORTRAIT_DIMENSIONS + 2),
this.scaleIt(PORTRAIT_DIMENSIONS),
this.scaleIt(PORTRAIT_DIMENSIONS)
)
matrices.push()
matrices.translate(pokemonX + (PORTRAIT_DIMENSIONS / 2.0) + 4, pokemonY + 4.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
drawProfilePokemon(
renderablePokemon = pokemon.asRenderablePokemon(),
matrixStack = matrices,
rotation = Quaternion.fromEulerXyzDegrees(Vec3f(13F, 35F, 0F)),
state = null,
scale = 6F
)
RenderSystem.disableScissor()
matrices.pop()
if (position == parent.selectedPosition) {
blitk(
matrixStack = matrices,
texture = PCWidget.selectedResource,
x = x,
y = y + 1,
height = PORTRAIT_DIMENSIONS,
width = PORTRAIT_DIMENSIONS
)
}
}
matrices.pop()
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.pokemon.Pokemon
class PartyStorageSlot(
x: Int, y: Int,
private val parent: StorageWidget,
private val party: ClientParty,
val position: PartyPosition,
onPress: PressAction
) : StorageSlot(x, y, parent, onPress) {
override fun getPokemon(): Pokemon? {
return party.get(position)
}
override fun shouldRender(): Boolean {
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
} else {
grabbedSlot.getPokemon() != getPokemon()
}
}
}
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class ReleaseButton(
x: Int, y: Int,
private val parent: StorageWidget,
onPress: PressAction
) : ButtonWidget(x, y, WIDTH, HEIGHT, Text.literal("Release"), onPress) {
companion object {
private const val WIDTH = 58
private const val HEIGHT = 16
private val buttonResource = cobblemonResource("ui/pc/pc_release_button.png")
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (parent.canDeleteSelected() && !parent.displayConfirmRelease) {
blitk(
matrixStack = matrices,
texture = buttonResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
x = x + (WIDTH / 2),
y = y + 3.5,
centered = true,
shadow = true
)
}
}
override fun playDownSound(pHandler: SoundManager) {
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class ReleaseConfirmButton(
x: Int, y: Int,
private val parent: StorageWidget,
private val subKey: String,
onPress: PressAction
) : ButtonWidget(x, y, WIDTH, HEIGHT, Text.literal("ReleaseConfirm"), onPress) {
companion object {
private const val WIDTH = 30
private const val HEIGHT = 13
private val buttonResource = cobblemonResource("ui/pc/pc_release_button_confirm.png")
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (parent.canDeleteSelected() && parent.displayConfirmRelease) {
blitk(
matrixStack = matrices,
texture = buttonResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang(subKey).bold(),
x = x + (WIDTH / 2),
y = y + 2,
centered = true,
shadow = true
)
}
}
override fun playDownSound(soundManager: SoundManager) {
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -0,0 +1,128 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.math.Quaternion
import net.minecraft.util.math.Vec3f
open class StorageSlot(
x: Int, y: Int,
private val parent: StorageWidget,
onPress: PressAction
) : ButtonWidget(x, y, SIZE, SIZE, Text.literal("StorageSlot"), onPress) {
companion object {
const val SIZE = 25
private val genderIconMale = cobblemonResource("ui/pc/gender_icon_male.png")
private val genderIconFemale = cobblemonResource("ui/pc/gender_icon_female.png")
private val selectPointerResource = cobblemonResource("ui/pc/pc_pointer.png")
}
override fun playDownSound(soundManager: SoundManager) {
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (shouldRender()) {
renderSlot(matrices, x, y)
}
}
fun renderSlot(matrices: MatrixStack, posX: Int, posY: Int) {
val pokemon = getPokemon() ?: return
val minecraft = MinecraftClient.getInstance()
RenderSystem.enableScissor(
((posX - 2) * minecraft.window.scaleFactor).toInt(),
(minecraft.window.height - (posY + SIZE + 2) * minecraft.window.scaleFactor).toInt(),
((SIZE + 4) * minecraft.window.scaleFactor).toInt(),
((SIZE + 4) * minecraft.window.scaleFactor).toInt()
)
// Render Pokémon
matrices.push()
matrices.translate(posX + (SIZE / 2.0), posY + 3.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
drawProfilePokemon(
renderablePokemon = pokemon.asRenderablePokemon(),
matrixStack = matrices,
rotation = Quaternion.fromEulerXyzDegrees(Vec3f(13F, 35F, 0F)),
state = null,
scale = 4.5F
)
matrices.pop()
RenderSystem.disableScissor()
// Ensure labels are not hidden behind Pokémon render
matrices.push()
matrices.translate(0.0, 0.0, 100.0)
// Level
drawScaledText(
matrixStack = matrices,
text = lang("ui.lv.number", pokemon.level),
x = posX + 1,
y = posY + 1,
shadow = true,
scale = PCGUI.SCALE
)
if (pokemon.gender != Gender.GENDERLESS) {
blitk(
matrixStack = matrices,
texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
x = (posX + 21) / PCGUI.SCALE,
y = (posY + 1) / PCGUI.SCALE,
width = 6,
height = 8,
scale = PCGUI.SCALE
)
}
matrices.pop()
if (isSelected()) {
blitk(
matrixStack = matrices,
texture = selectPointerResource,
x = (posX + 10) / PCGUI.SCALE,
y = ((posY - 3) / PCGUI.SCALE) - parent.pcGui.selectPointerOffsetY,
width = 11,
height = 8,
scale = PCGUI.SCALE
)
}
}
open fun getPokemon(): Pokemon? {
return null
}
open fun isSelected(): Boolean {
return getPokemon() == parent.pcGui.previewPokemon
}
open fun shouldRender(): Boolean {
return true
}
fun isHovered(mouseX: Int, mouseY: Int) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + SIZE)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + SIZE))
}
@@ -0,0 +1,379 @@
/*
* Copyright (C) 2022 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.client.gui.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvent
import net.minecraft.text.Text
class StorageWidget(
pX: Int, pY: Int,
val pcGui: PCGUI,
private val pc: ClientPC,
private val party: ClientParty
) : SoundlessWidget(pX, pY, WIDTH, HEIGHT, Text.literal("PCWidget")) {
companion object {
const val WIDTH = 263
const val HEIGHT = 155
const val BOX_SLOT_START_OFFSET_X = 7
const val BOX_SLOT_START_OFFSET_Y = 11
const val PARTY_SLOT_START_OFFSET_X = 193
const val PARTY_SLOT_START_OFFSET_Y = 8
const val BOX_SLOT_PADDING = 2
const val PARTY_SLOT_PADDING = 6
private val screenOverlayResource = cobblemonResource("ui/pc/pc_screen_overlay.png")
}
private val partySlots = arrayListOf<PartyStorageSlot>()
private val boxSlots = arrayListOf<BoxStorageSlot>()
private val releaseButton: ReleaseButton
private val releaseYesButton: ReleaseConfirmButton
private val releaseNoButton: ReleaseConfirmButton
var displayConfirmRelease = false
var screenLoaded = false
var selectedPosition: StorePosition? = null
var grabbedSlot: GrabbedStorageSlot? = null
var box = 0
set(value) {
// If value is within min and max
field = if (value > 0 && value < pc.boxes.size) value
// If value is less than zero, wrap around to end
else if (value < 0) pc.boxes.size - 1
// Else it's greater than max, wrap around to start
else 0
this.setupStorageSlots()
}
init {
this.setupStorageSlots()
this.releaseButton = ReleaseButton(
x = x + 194,
y = y + 124,
parent = this,
onPress = {
if (!displayConfirmRelease) {
displayConfirmRelease = true
playSound(CobblemonSounds.GUI_CLICK.get())
}
}
)
this.releaseYesButton = ReleaseConfirmButton(
x = x + 190,
y = y + 131,
parent = this,
subKey = "ui.generic.yes",
onPress = {
if (canDeleteSelected() && displayConfirmRelease) {
val position = selectedPosition ?: return@ReleaseConfirmButton
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
val packet = when (position) {
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
else -> return@ReleaseConfirmButton
}
CobblemonNetwork.sendToServer(packet)
playSound(CobblemonSounds.PC_RELEASE.get())
resetSelected()
displayConfirmRelease = false
}
}
)
this.releaseNoButton = ReleaseConfirmButton(
x = x + 226,
y = y + 131,
parent = this,
subKey = "ui.generic.no",
onPress = {
if (displayConfirmRelease) {
displayConfirmRelease = false
playSound(CobblemonSounds.GUI_CLICK.get())
}
}
)
}
fun canDeleteSelected(): Boolean {
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1)
&& selectedPosition != null
&& grabbedSlot != null
}
private fun setupStorageSlots() {
this.resetStorageSlots()
var index = 0
// Box Slots
val boxStartX = x + BOX_SLOT_START_OFFSET_X
val boxStartY = y + BOX_SLOT_START_OFFSET_Y
for (row in 1..5) {
for (col in 1..6) {
BoxStorageSlot(
x = boxStartX + ((col - 1) * (StorageSlot.SIZE + BOX_SLOT_PADDING)),
y = boxStartY + ((row - 1) * (StorageSlot.SIZE + BOX_SLOT_PADDING)),
parent = this,
pc = pc,
position = PCPosition(box, index),
onPress = { this.onStorageSlotClicked(it) }
).also { widget ->
this.addWidget(widget)
this.boxSlots.add(widget)
}
index++
}
}
// Party Slots
for (partyIndex in 0..5) {
var partyX = x + PARTY_SLOT_START_OFFSET_X
var partyY = y + PARTY_SLOT_START_OFFSET_Y
if (partyIndex > 0) {
val isEven = partyIndex % 2 == 0
val offsetIndex = (partyIndex - (if (isEven) 0 else 1)) / 2
val offsetX = if (isEven) 0 else (StorageSlot.SIZE + PARTY_SLOT_PADDING)
val offsetY = if (isEven) 0 else 8
partyX += offsetX
partyY += ((StorageSlot.SIZE + PARTY_SLOT_PADDING) * offsetIndex) + offsetY
}
PartyStorageSlot(
x = partyX,
y = partyY,
parent = this,
party = party,
position = PartyPosition(partyIndex),
onPress = { this.onStorageSlotClicked(it) }
).also { widget ->
this.addWidget(widget)
this.partySlots.add(widget)
}
}
}
private fun getSelectedPokemon(): Pokemon? {
val selectedPosition = this.selectedPosition ?: return null
return when (selectedPosition) {
is PCPosition -> pc.get(selectedPosition)
is PartyPosition -> party.get(selectedPosition)
else -> null
}
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
// Party Label
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.party").bold(),
x = x + 213,
y = y - 15.5,
centered = true,
shadow = true
)
if (canDeleteSelected() && displayConfirmRelease) {
drawScaledText(
matrixStack = matrices,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
x = x + 223,
y = y + 119,
centered = true
)
}
this.releaseButton.render(matrices, mouseX, mouseY, delta)
this.releaseYesButton.render(matrices, mouseX, mouseY, delta)
this.releaseNoButton.render(matrices, mouseX, mouseY, delta)
// Screen Overlay
blitk(
matrixStack = matrices,
texture = screenOverlayResource,
x = x - 17,
y = y - 17,
width = 208,
height = 189,
alpha = if (screenLoaded) 1F else ((pcGui.ticksElapsed).toFloat() / 10F).coerceIn(0F, 1F)
)
if (screenLoaded) {
this.boxSlots.forEach { slot ->
slot.render(matrices, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& pokemon != null && pokemon != pcGui.previewPokemon) pcGui.setPreviewPokemon(pokemon)
}
} else {
if (pcGui.ticksElapsed >= 10) screenLoaded = true
}
this.partySlots.forEach { slot ->
slot.render(matrices, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& pokemon != null && pokemon != pcGui.previewPokemon) pcGui.setPreviewPokemon(pokemon)
}
grabbedSlot?.render(matrices, mouseX, mouseY, delta)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (displayConfirmRelease) {
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
if (releaseNoButton.isHovered(pMouseX, pMouseY)) releaseNoButton.mouseClicked(pMouseX, pMouseY, pButton)
} else {
if (releaseButton.isHovered(pMouseX, pMouseY)) releaseButton.mouseClicked(pMouseX, pMouseY, pButton)
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
private fun resetStorageSlots() {
this.partySlots.forEach(this::removeWidget)
this.partySlots.clear()
this.boxSlots.forEach(this::removeWidget)
this.boxSlots.clear()
}
private fun playSound(soundEvent: SoundEvent) {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
}
private fun onStorageSlotClicked(button: ButtonWidget) {
// Check if storage slot
val clickedPosition = when(button) {
is BoxStorageSlot -> button.position
is PartyStorageSlot -> button.position
else -> return
}
// Reset release confirmation
displayConfirmRelease = false
// Clicking on itself, so unselect position
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP.get())
resetSelected()
return
}
// Check for selecting a Pokémon
val clickedPokemon = when(button) {
is BoxStorageSlot -> pc.get(clickedPosition as PCPosition)
is PartyStorageSlot -> party.get(clickedPosition as PartyPosition)
else -> null
}
if (grabbedSlot == null) {
if (clickedPokemon != null) {
this.selectedPosition = clickedPosition
this.pcGui.setPreviewPokemon(clickedPokemon)
grabbedSlot = GrabbedStorageSlot(
x = button.x,
y = button.y,
parent = this,
pokemon = clickedPokemon
)
playSound(CobblemonSounds.PC_GRAB.get())
}
} else {
// Handle movement within the Box
val selectedPokemon = when(this.selectedPosition) {
is PCPosition -> pc.get(this.selectedPosition as PCPosition)
is PartyPosition -> party.get(this.selectedPosition as PartyPosition)
else -> null
} ?: return
// Box to Box
if (this.selectedPosition is PCPosition && clickedPosition is PCPosition) {
val packet = clickedPokemon?.let { SwapPCPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonPacket(selectedPokemon.uuid, selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP.get())
resetSelected()
}
// Box to Party
else if (this.selectedPosition is PCPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(clickedPokemon.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonToPartyPacket(selectedPokemon.uuid, this.selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP.get())
resetSelected()
}
// Party to Box
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
return
}
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP.get())
resetSelected()
}
// Party to Party
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP.get())
resetSelected()
}
}
}
private fun resetSelected() {
selectedPosition = null
grabbedSlot = null
pcGui.setPreviewPokemon(null)
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.summary
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.api.moves.MoveSet
@@ -19,6 +20,7 @@ import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.widgets.EvolutionSelectScreen
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
@@ -26,7 +28,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.PartyWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.SummaryTab
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.info.InfoWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.change.MoveSwapScreen
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSwapScreen
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.storage.ClientParty
@@ -43,7 +45,9 @@ import net.minecraft.client.gui.Element
import net.minecraft.client.gui.Selectable
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.widget.ClickableWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvent
import net.minecraft.text.Text
class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summary.title")) {
@@ -154,7 +158,12 @@ class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summ
pX = x + 78,
pY = y - 1,
label = lang("ui.info")
) { if (mainScreenIndex != INFO) displayMainScreen(INFO) }
) {
if (mainScreenIndex != INFO) {
displayMainScreen(INFO)
playSound(CobblemonSounds.GUI_CLICK.get())
}
}
)
summaryTabs.add(
@@ -162,7 +171,12 @@ class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summ
pX = x + 119,
pY = y - 1,
label = lang("ui.moves")
) { if (mainScreenIndex != MOVES) displayMainScreen(MOVES) }
) {
if (mainScreenIndex != MOVES) {
displayMainScreen(MOVES)
playSound(CobblemonSounds.GUI_CLICK.get())
}
}
)
summaryTabs.add(
@@ -170,7 +184,12 @@ class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summ
pX = x + 160,
pY = y - 1,
label = lang("ui.stats")
) { if (mainScreenIndex != STATS) displayMainScreen(STATS) }
) {
if (mainScreenIndex != STATS) {
displayMainScreen(STATS)
playSound(CobblemonSounds.GUI_CLICK.get())
}
}
)
summaryTabs[mainScreenIndex].toggleTab()
@@ -182,6 +201,7 @@ class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summ
pX = x + 302,
pY = y + 145
) {
playSound(CobblemonSounds.GUI_CLICK.get())
MinecraftClient.getInstance().setScreen(null)
}
)
@@ -512,4 +532,8 @@ class Summary private constructor(): Screen(Text.translatable("cobblemon.ui.summ
override fun mouseClicked(d: Double, e: Double, i: Int): Boolean {
return children().any { it.mouseClicked(d, e, i) }
}
fun playSound(soundEvent: SoundEvent) {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
}
}
@@ -8,12 +8,14 @@
package com.cobblemon.mod.common.client.gui.summary
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.MutableText
@@ -86,9 +88,9 @@ class SummaryButton(
return false
}
override fun playDownSound(soundManager: SoundManager?) {
override fun playDownSound(soundManager: SoundManager) {
if (!this.silent) {
super.playDownSound(soundManager)
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
@@ -197,6 +198,7 @@ class PartySlotWidget(
} else {
if ((index > -1) && (summary.selectedPokemon.uuid != pokemon?.uuid)) {
summary.switchSelection(index)
if (pokemon != null) partyWidget.playSound(CobblemonSounds.GUI_CLICK.get())
return true
}
}
@@ -213,6 +215,7 @@ class PartySlotWidget(
if (partyWidget.swapEnabled && partyWidget.isWithinScreen(mouseX, mouseY) && index < 0) {
repositionSlot(mouseX, mouseY)
} else {
if (partyWidget.swapSource != null) partyWidget.playSound(CobblemonSounds.PC_DROP.get())
toggleDrag(false)
partyWidget.swapSource = null
partyWidget.draggedSlot = null
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
@@ -17,8 +18,11 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.sound.PositionedSoundInstance
import java.security.InvalidParameterException
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvent
import net.minecraft.text.Text
class PartyWidget(
@@ -46,7 +50,7 @@ class PartyWidget(
private val partySize = partyList.size
private val partySlots = arrayListOf<PartySlotWidget>()
val swapButton: SummaryButton = SummaryButton(
private val swapButton: SummaryButton = SummaryButton(
buttonX = x + 80F,
buttonY = y - 9F,
buttonWidth = 26,
@@ -159,6 +163,7 @@ class PartyWidget(
index = -1,
isClientPartyMember = isParty
)
playSound(CobblemonSounds.PC_GRAB.get())
}
}
}
@@ -174,6 +179,7 @@ class PartyWidget(
}
swapSource = null
draggedSlot = null
playSound(CobblemonSounds.PC_DROP.get())
}
}
return super.mouseReleased(pMouseX, pMouseY, pButton)
@@ -205,6 +211,10 @@ class PartyWidget(
return -1
}
fun playSound(soundEvent: SoundEvent) {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
}
fun isWithinScreen(mouseX: Double, mouseY: Double): Boolean = mouseX.toInt() in x..(x + WIDTH)
&& mouseY.toInt() in y..(y + HEIGHT)
}
@@ -8,12 +8,15 @@
package com.cobblemon.mod.common.client.gui.summary.widgets.screens
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.MutableText
@@ -47,6 +50,9 @@ class SummaryTab(
)
}
override fun playDownSound(soundManager: SoundManager) {
}
fun toggleTab(state: Boolean = true) {
isActive = state
}
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.MinecraftClient
@@ -147,7 +148,7 @@ class InfoWidget(
drawScaledText(
matrixStack = pMatrixStack,
font = CobblemonResources.DEFAULT_LARGE,
text = pokemon.nature.displayName.bold(),
text = pokemon.nature.displayName.asTranslated().bold(),
x = x + 53,
y = y + 66,
shadow = true
@@ -166,7 +167,7 @@ class InfoWidget(
drawScaledText(
matrixStack = pMatrixStack,
font = CobblemonResources.DEFAULT_LARGE,
text = pokemon.ability.displayName.bold(),
text = pokemon.ability.displayName.asTranslated().bold(),
x = x + 53,
y = y + 81,
shadow = true
@@ -177,7 +178,7 @@ class InfoWidget(
pMatrixStack.push()
pMatrixStack.scale(smallTextScale, smallTextScale, 1F)
MultiLineLabelK.create(
component = pokemon.ability.description,
component = pokemon.ability.description.asTranslated(),
width = 117 / smallTextScale,
maxLines = 3
).renderLeftAligned(
@@ -6,9 +6,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.change
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.api.moves.MoveTemplate
@@ -18,7 +19,6 @@ import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.Summary
import com.cobblemon.mod.common.client.gui.summary.widgets.common.SummaryScrollList
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.util.cobblemonResource
@@ -195,6 +195,7 @@ class MoveSwapScreen(
newMove = move
)
)
pane.movesWidget.summary.playSound(CobblemonSounds.GUI_CLICK.get())
pane.movesWidget.summary.displaySideScreen(Summary.PARTY)
return true
}
@@ -8,9 +8,12 @@
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
@@ -63,5 +66,9 @@ class ReorderMoveButton(
}
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
fun isHovered(mouseX: Double, mouseY: Double, offsetY: Float) = mouseX.toFloat() in ((pX - OFFSET_X)..((pX - OFFSET_X) + (WIDTH * SCALE))) && mouseY.toFloat() in ((pY + offsetY)..((pY + offsetY) + (HEIGHT * SCALE) - 0.5F))
}
@@ -8,11 +8,13 @@
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.change.MoveSwapScreen
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
@@ -57,5 +59,9 @@ class SwapMoveButton(
)
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK.get(), 1.0F))
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in ((pX + OFFSET_X)..((pX + OFFSET_X) + (WIDTH * SCALE))) && mouseY.toFloat() in ((pY + OFFSET_Y)..((pY + OFFSET_Y) + (HEIGHT * SCALE) - 0.5F))
}
@@ -32,6 +32,7 @@ import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.pokemon.update.EvolutionUpdatePacketHandler
import com.cobblemon.mod.common.client.net.pokemon.update.SingleUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
import com.cobblemon.mod.common.client.net.storage.RemoveClientPokemonHandler
import com.cobblemon.mod.common.client.net.storage.SwapClientPokemonHandler
@@ -155,6 +156,9 @@ object ClientPacketRegistrar : SidedPacketRegistrar() {
registerHandler(DataRegistrySyncPacketHandler<Species, SpeciesRegistrySyncPacket>())
registerHandler(DataRegistrySyncPacketHandler<PropertiesCompletionProvider.SuggestionHolder, PropertiesCompletionRegistrySyncPacket>())
// Hax
registerHandler(UnvalidatedPlaySoundS2CPacketHandler)
CobblemonNetwork.clientHandlersRegistered.complete(Unit)
}
}
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.battle.ClientBattleActor
import com.cobblemon.mod.common.client.battle.ClientBattlePokemon
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.net.ClientPacketHandler
import com.cobblemon.mod.common.net.messages.client.battle.BattleInitializePacket
import net.minecraft.client.MinecraftClient
@@ -45,7 +46,10 @@ object BattleInitializeHandler : ClientPacketHandler<BattleInitializePacket> {
}
}
}
minimised = false
}
MinecraftClient.getInstance().setScreen(BattleGUI())
}
fun actorFromDTO(actorDTO: BattleInitializePacket.BattleActorDTO): ClientBattleActor {
@@ -60,6 +64,7 @@ object BattleInitializeHandler : ClientPacketHandler<BattleInitializePacket> {
ClientBattlePokemon(
uuid = it.uuid,
properties = it.properties,
aspects = it.aspects,
displayName = it.displayName,
hpRatio = it.hpRatio,
status = it.status,
@@ -34,6 +34,7 @@ object BattleSwitchPokemonHandler : ClientPacketHandler<BattleSwitchPokemonPacke
uuid = uuid,
displayName = displayName,
properties = properties,
aspects = aspects,
hpRatio = hpRatio,
status = status,
statChanges = statChanges
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2022 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.client.net.sound
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.client.net.ClientPacketHandler
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import net.minecraft.client.MinecraftClient
import net.minecraft.sound.SoundEvent
internal object UnvalidatedPlaySoundS2CPacketHandler : ClientPacketHandler<UnvalidatedPlaySoundS2CPacket> {
override fun invokeOnClient(packet: UnvalidatedPlaySoundS2CPacket, ctx: CobblemonNetwork.NetworkContext) {
val client = MinecraftClient.getInstance()
client.executeSync {
// This prevents clients that might not have the required resources installed from getting spammed about missing sound
if (client.soundManager.get(packet.sound) != null) {
client.world?.playSound(client.player, packet.x, packet.y, packet.z, SoundEvent(packet.sound), packet.category, packet.volume, packet.pitch)
}
}
}
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.client.net.storage.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.pc.PCGui
import com.cobblemon.mod.common.client.gui.pc.PCGUI
import com.cobblemon.mod.common.client.net.ClientPacketHandler
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import net.minecraft.client.MinecraftClient
@@ -18,6 +18,6 @@ import net.minecraft.client.MinecraftClient
object OpenPCHandler : ClientPacketHandler<OpenPCPacket> {
override fun invokeOnClient(packet: OpenPCPacket, ctx: CobblemonNetwork.NetworkContext) {
val pc = CobblemonClient.storage.pcStores[packet.storeID] ?: return
MinecraftClient.getInstance().setScreen(PCGui(pc, CobblemonClient.storage.myParty))
MinecraftClient.getInstance().setScreen(PCGUI(pc, CobblemonClient.storage.myParty))
}
}
@@ -274,23 +274,21 @@ abstract class PoseableEntityModel<T : Entity>(
)
fun bedrock(
file: String,
animationGroup: String,
animation: String,
fileSuffix: String = ".animation.json",
animationPrefix: String = "animation.${file.substringAfterLast("/")}"
animationPrefix: String = "animation.$animationGroup"
) = BedrockStatelessAnimation<T>(
this,
BedrockAnimationRepository.getAnimation(file + fileSuffix, "$animationPrefix.$animation")
BedrockAnimationRepository.getAnimation(animationGroup, "$animationPrefix.$animation")
)
fun bedrockStateful(
file: String,
animationGroup: String,
animation: String,
fileSuffix: String = ".animation.json",
animationPrefix: String = "animation.${file.substringAfterLast("/")}",
animationPrefix: String = "animation.$animationGroup",
preventsIdleCheck: (T?, PoseableEntityState<T>, StatelessAnimation<T, *>) -> Boolean = { _, _, _ -> true }
) = BedrockStatefulAnimation(
BedrockAnimationRepository.getAnimation(file + fileSuffix, "$animationPrefix.$animation"),
BedrockAnimationRepository.getAnimation(animationGroup, "$animationPrefix.$animation"),
preventsIdleCheck
)
@@ -28,6 +28,6 @@ class RotationFunctionStatelessAnimation<T : Entity>(
) : StatelessAnimation<T, ModelFrame>(frame) {
override val targetFrame = ModelFrame::class.java
override fun setAngles(entity: T?, model: PoseableEntityModel<T>, state: PoseableEntityState<T>?, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float) {
part.addRotation(axis, function(timeVariable(entity?.let { model.getState(it) }, limbSwing, ageInTicks) ?: 0F))
part.addRotation(axis, function(timeVariable(state, limbSwing, ageInTicks) ?: 0F))
}
}
@@ -8,10 +8,11 @@
package com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.util.fromJson
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
import net.minecraft.resource.ResourceManager
/**
@@ -28,22 +29,26 @@ object BedrockAnimationRepository {
.registerTypeAdapter(BedrockAnimation::class.java, BedrockAnimationAdapter)
.create()
private val animations: MutableMap<String, BedrockAnimation> = mutableMapOf()
private val animationGroups = mutableMapOf<String, BedrockAnimationGroup>()
fun loadAnimations(resourceManager: ResourceManager) {
LOGGER.info("Loading animations...")
var animationCount = 0
animationGroups.clear()
resourceManager.findResources("bedrock/animations", { it.path.endsWith(".animation.json") }).forEach { identifier, resource ->
val animationGroup = gson.fromJson<BedrockAnimationGroup>(resource.inputStream.reader())
val animationGroupName = identifier.path.substringAfterLast("/").replace(".animation.json", "")
animationGroups[animationGroupName] = animationGroup
animationCount += animationGroup.animations.size
}
LOGGER.info("Loaded $animationCount animations from ${animationGroups.size} animation groups.")
}
fun getAnimation(fileName: String, animationName: String): BedrockAnimation {
if (animations[animationName] == null) {
loadAnimationsFromFile(fileName)
}
return animations[animationName] ?: throw IllegalArgumentException("animation is not in specified file")
}
val animationGroup = animationGroups[fileName]
?: throw IllegalArgumentException("Unknown animation group: $fileName")
fun loadAnimationsFromFile(fileName: String) {
val stream = Cobblemon::class.java.getResourceAsStream("/assets/${Cobblemon.MODID}/bedrock/animations/$fileName") ?: throw IllegalStateException("animation file $fileName could not be found")
val animationGroup = gson.fromJson<BedrockAnimationGroup>(stream.reader())
animationGroup.animations.forEach { (name, animation) -> animations[name] = animation }
}
fun clear() {
animations.clear()
return animationGroup.animations[animationName]
?: throw IllegalArgumentException("Animation $animationName not found in animation group $fileName")
}
}
@@ -150,7 +150,7 @@ class JsonPokemonPoseableModel(override val rootPart: ModelPart) : PokemonPoseab
return@mapNotNull JsonPokemonPoseableModelAdapter.model!!.singleBoneLook<PokemonEntity>()
} else if (animString.startsWith("bedrock")) {
val split = animString.replace("bedrock(", "").replace(")", "").split(",").map(String::trim)
return@mapNotNull model.bedrock(file = split[0], animation = split[1])
return@mapNotNull model.bedrock(animationGroup = split[0], animation = split[1])
}
return@mapNotNull null
}.toTypedArray()
@@ -89,7 +89,11 @@ abstract class PokemonPoseableModel : PoseableEntityModel<PokemonEntity>() {
if (provider != null) {
for (layer in currentLayers) {
val texture = layer.texture ?: continue
val consumer = provider.getBuffer(RenderLayer.getEntityTranslucent(texture))
val consumer = if (layer.emissive) {
provider.getBuffer(RenderLayer.getEntityTranslucentEmissive(texture))
} else {
provider.getBuffer(RenderLayer.getEntityTranslucent(texture))
}
stack.push()
stack.scale(layer.scale.x, layer.scale.y, layer.scale.z)
super.render(stack, consumer, packedLight, OverlayTexture.DEFAULT_UV, layer.tint.x, layer.tint.y, layer.tint.z, layer.tint.w)
@@ -26,7 +26,7 @@ class AbraModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val portraitTranslation = Vec3d(0.0, 0.2, 0.0)
override val profileScale = 1.0F
override val profileTranslation = Vec3d(0.0, 0.0, 0.0)
override val profileTranslation = Vec3d(0.0, 0.4, 0.0)
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
@@ -38,7 +38,7 @@ class AbraModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0063_abra/abra", "ground_idle")
bedrock("abra", "ground_idle")
)
)
@@ -48,8 +48,8 @@ class AbraModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0063_abra/abra", "ground_idle")
//bedrock("0063_abra/abra", "ground_walk")
bedrock("abra", "ground_idle")
//bedrock("abra", "ground_walk")
)
)
}
@@ -57,5 +57,5 @@ class AbraModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("0063_abra/abra", "faint") else null
) = if (state.isPosedIn(standing, walk)) bedrockStateful("abra", "faint") else null
}
@@ -36,7 +36,7 @@ class AerodactylModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseType = PoseType.STAND,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0142_aerodactyl/aerodactyl", "ground_idle")
bedrock("aerodactyl", "ground_idle")
)
)
@@ -45,7 +45,7 @@ class AerodactylModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseTypes = UI_POSES + PoseType.HOVER + PoseType.FLOAT,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0142_aerodactyl/aerodactyl", "air_idle")
bedrock("aerodactyl", "air_idle")
)
)
@@ -54,7 +54,7 @@ class AerodactylModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0142_aerodactyl/aerodactyl", "air_fly")
bedrock("aerodactyl", "air_fly")
)
)
}
@@ -62,5 +62,5 @@ class AerodactylModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("0142_aerodactyl/aerodactyl", "faint") else null
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("aerodactyl", "faint") else null
}
@@ -36,7 +36,7 @@ class AlakazamModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook() ,
bedrock("0065_alakazam/alakazam", "ground_idle")
bedrock("alakazam", "ground_idle")
)
)
@@ -46,8 +46,8 @@ class AlakazamModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0065_alakazam/alakazam", "ground_idle")
//bedrock("0065_alakazam/alakazam", "ground_walk")
bedrock("alakazam", "ground_idle")
//bedrock("alakazam", "ground_walk")
)
)
}
@@ -55,5 +55,5 @@ class AlakazamModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("0065_alakazam/alakazam", "faint") else null
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("alakazam", "faint") else null
}
@@ -79,7 +79,7 @@ class ArbokModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0024_arbok/arbok", "ground_idle"),
bedrock("arbok", "ground_idle"),
wave
)
)
@@ -90,7 +90,7 @@ class ArbokModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0024_arbok/arbok", "ground_walk"),
bedrock("arbok", "ground_walk"),
wave
)
)
@@ -100,7 +100,7 @@ class ArbokModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseTypes = UI_POSES,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0024_arbok/arbok", "summary_idle")
bedrock("arbok", "summary_idle")
)
)
}
@@ -108,5 +108,5 @@ class ArbokModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("0024_arbok/arbok", "faint") else null
) = if (state.isPosedIn(standing, walk)) bedrockStateful("arbok", "faint") else null
}
@@ -43,7 +43,7 @@ class ArcanineModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Quad
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook()
//bedrock("0059_arcanine/arcanine", "ground_idle")
//bedrock("arcanine", "ground_idle")
)
)
@@ -54,7 +54,7 @@ class ArcanineModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Quad
idleAnimations = arrayOf(
singleBoneLook(),
QuadrupedWalkAnimation(this, periodMultiplier = 0.75F, amplitudeMultiplier = 1F)
//bedrock("0059_arcanine/arcanine", "ground_walk")
//bedrock("arcanine", "ground_walk")
)
)
}
@@ -62,5 +62,5 @@ class ArcanineModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Quad
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("0059_arcanine/arcanine", "faint") else null
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("arcanine", "faint") else null
}
@@ -37,7 +37,7 @@ class ArticunoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseType = PoseType.STAND,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0144_articuno/articuno", "ground_idle")
bedrock("articuno", "ground_idle")
)
)
@@ -46,7 +46,7 @@ class ArticunoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseType = PoseType.WALK,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0144_articuno/articuno", "ground_idle")
bedrock("articuno", "ground_idle")
)
)
@@ -55,7 +55,7 @@ class ArticunoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseTypes = UI_POSES + PoseType.HOVER + PoseType.FLOAT,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0144_articuno/articuno", "air_idle")
bedrock("articuno", "air_idle")
)
)
@@ -64,7 +64,7 @@ class ArticunoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0144_articuno/articuno", "air_fly")
bedrock("articuno", "air_fly")
)
)
}
@@ -72,5 +72,5 @@ class ArticunoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("0144_articuno/articuno", "faint") else null
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("articuno", "faint") else null
}
@@ -25,11 +25,11 @@ class BeedrillModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BiWi
override val leftWing = getPart("wing_left")
override val rightWing = getPart("wing_right")
override val portraitScale = 1.5F
override val portraitTranslation = Vec3d(0.1, 0.2, 0.0)
override val portraitScale = 1.9F
override val portraitTranslation = Vec3d(0.05, 0.12, 0.0)
override val profileScale = 1.0F
override val profileTranslation = Vec3d(0.0, 0.0, 0.0)
override val profileScale = 0.9F
override val profileTranslation = Vec3d(0.0, 0.3, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
@@ -38,7 +38,7 @@ class BeedrillModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BiWi
override fun registerPoses() {
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("0015_beedrill/beedrill", "sleep")),
idleAnimations = arrayOf(bedrock("beedrill", "sleep")),
transformedParts = arrayOf(
rootPart.asTransformed().addPosition(Y_AXIS, -3F)
)
@@ -49,7 +49,7 @@ class BeedrillModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BiWi
poseTypes = STATIONARY_POSES,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0015_beedrill/beedrill", "air_idle")
bedrock("beedrill", "air_idle")
),
transformedParts = arrayOf(rootPart.asTransformed().addPosition(Y_AXIS, -10F))
)
@@ -59,7 +59,7 @@ class BeedrillModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BiWi
poseTypes = MOVING_POSES,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0015_beedrill/beedrill", "air_fly")
bedrock("beedrill", "air_fly")
),
transformedParts = arrayOf(rootPart.asTransformed().addPosition(Y_AXIS, -5F))
)
@@ -36,7 +36,7 @@ class BellsproutModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0069_bellsprout/bellsprout", "ground_idle")
bedrock("bellsprout", "ground_idle")
)
)
@@ -46,8 +46,8 @@ class BellsproutModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0069_bellsprout/bellsprout", "ground_idle")
//bedrock("0069_bellsprout/bellsprout", "ground_walk")
bedrock("bellsprout", "ground_idle")
//bedrock("bellsprout", "ground_walk")
)
)
}
@@ -55,5 +55,5 @@ class BellsproutModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("0069_bellsprout/bellsprout", "faint") else null
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("bellsprout", "faint") else null
}
@@ -51,7 +51,7 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
idleAnimations = arrayOf(bedrock("0009_blastoise/blastoise", "sleep"))
idleAnimations = arrayOf(bedrock("blastoise", "sleep"))
)
standing = registerPose(
@@ -60,7 +60,7 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0009_blastoise/blastoise", "ground_idle")
bedrock("blastoise", "ground_idle")
)
)
@@ -69,7 +69,7 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
poseTypes = setOf(PoseType.FLOAT),
transformTicks = 10,
idleAnimations = arrayOf(
bedrock("0009_blastoise/blastoise", "water_idle"),
bedrock("blastoise", "water_idle"),
singleBoneLook()
)
)
@@ -80,7 +80,7 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0009_blastoise/blastoise", "water_swim")
bedrock("blastoise", "water_swim")
)
)
@@ -89,7 +89,7 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0009_blastoise/blastoise", "ground_walk")
bedrock("blastoise", "ground_walk")
)
)
}
@@ -97,5 +97,5 @@ class BlastoiseModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("0009_blastoise/blastoise", "faint") else null
) = if (state.isPosedIn(standing, walk)) bedrockStateful("blastoise", "faint") else null
}
@@ -38,7 +38,7 @@ class BulbasaurModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Qua
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
idleAnimations = arrayOf(bedrock("0001_bulbasaur/bulbasaur", "sleep"))
idleAnimations = arrayOf(bedrock("bulbasaur", "sleep"))
)
standing = registerPose(
@@ -47,7 +47,7 @@ class BulbasaurModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Qua
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0001_bulbasaur/bulbasaur", "ground_idle")
bedrock("bulbasaur", "ground_idle")
)
)
@@ -57,7 +57,7 @@ class BulbasaurModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Qua
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("0001_bulbasaur/bulbasaur", "ground_walk")
bedrock("bulbasaur", "ground_walk")
)
)
}
@@ -65,5 +65,5 @@ class BulbasaurModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Qua
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("0001_bulbasaur/bulbasaur", "faint") else null
) = if (state.isPosedIn(standing, walk)) bedrockStateful("bulbasaur", "faint") else null
}

Some files were not shown because too many files have changed in this diff Show More