Compare commits

..
Author SHA1 Message Date
JPAK 922ee95fd2 Camera changes for riding. Need to find a better system for being "unlocked" (in third person only) to make the animations feel better. 2025-03-03 16:22:13 -06:00
Apion 0fb5d4f69b Merge branch 'riding-sheet-scraper' into 'main'
Added scraper for riding sheet

See merge request cable-mc/cobblemon!1546
2025-03-01 08:34:14 +00:00
Paul e4346687fc Added scraper for riding sheet 2025-03-01 10:51:04 +10:00
Polymeta e0f26923b7 Merge branch 'moe-eats-bugs' into 'main'
Fix logic for LevelUpCriterion

See merge request cable-mc/cobblemon!1544
2025-02-26 13:08:30 +00:00
MoeBoy76andPolymeta 52cff9472a Fix logic for LevelUpCriterion 2025-02-26 13:08:30 +00:00
Polymeta c4935c3128 Merge branch 'feat/animated-party-portraits' into 'main'
Add support for animated portraits in the party overlay

See merge request cable-mc/cobblemon!1302
2025-02-23 09:26:59 +00:00
Hiroku 7831421399 Merge branch 'feature/cosmetic-slot' into 'main'
Cosmetic item

See merge request cable-mc/cobblemon!1538
2025-02-23 09:11:15 +00:00
TestandHiroku a059979220 Cosmetic item 2025-02-23 09:11:15 +00:00
Hiroku 09bab5b2aa Merge branch 'means-of-transportation' into 'main'
Rhydon the Pokemon

See merge request cable-mc/cobblemon!1534
2025-02-21 22:45:15 +00:00
Paul 358d2604fb imports 2025-02-22 08:34:17 +10:00
Paul bbd0415c92 Removed unused packet 2025-02-22 08:33:47 +10:00
Paul 9f553fc6c1 Remove blank mixin 2025-02-22 08:22:25 +10:00
Polymeta 51f8bb6ad5 Merge branch 'fix-pokeball-spewpa-evo' into 'main'
Fixed field name in evolution requirements for Spewpa Pokeball.

See merge request cable-mc/cobblemon!1541
2025-02-21 11:40:23 +00:00
Deltric cf93c0d544 Update changelog 2025-02-21 05:25:12 -06:00
Paul 2e6bef608b Merge remote-tracking branch 'origin/main' 2025-02-21 20:22:54 +10:00
Paul dcba7a5ab7 Removed unnecessary berry validation now that resultant yields are capped 2025-02-21 20:22:27 +10:00
Hiroku 4e3b1eb641 Merge branch 'feature/fix-ghost-pokemon' into 'main'
remove owned pokemon immediately when player stops seeing them, instead of queuing them

Closes #1127

See merge request cable-mc/cobblemon!1528
2025-02-21 10:21:43 +00:00
Paul 6f949ce858 Fixed no sounds playing for status curing berries being eaten or battle-interacted healing berries being eaten 2025-02-21 20:20:38 +10:00
Paul a04cd3162a Merge remote-tracking branch 'origin/main' into means-of-transportation
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
2025-02-21 17:48:24 +10:00
Paul 8b551159a6 Removed things that shouldn't have been there, client-packaged various client mixins 2025-02-21 17:46:46 +10:00
Hiroku 05b8621a81 Merge branch 'gradle/reposilite' into 'main'
Reposilite maven publication endpoint

See merge request cable-mc/cobblemon!1537
2025-02-21 03:14:39 +00:00
Paul 1068d3e078 Fixed berry yield exceeding growth points, closes #1435 2025-02-21 12:51:20 +10:00
Hiroku c1d6a9563c Merge branch 'feature/modfile-versions' into 'main'
Modfile tweaks and small mixin fixes

Closes #1460 and #1456

See merge request cable-mc/cobblemon!1539
2025-02-21 00:28:37 +00:00
PolymetaandHiroku f426b16f1d Modfile tweaks and small mixin fixes 2025-02-21 00:28:37 +00:00
Hiroku c944fc1ac6 Merge branch 'fix/form-change-illegal-moves' into 'main'
fix: Pokemon will now properly update their moveset after form changes

See merge request cable-mc/cobblemon!1540
2025-02-21 00:27:48 +00:00
Deltric c68df830a7 Fixed field name in evolution requirements for Spewpa Pokeball. 2025-02-19 19:37:44 -06:00
Ina 0432441868 fix: Pokemon will now properly update their moveset after form changes 2025-02-19 22:52:23 +00:00
Ina dcd0959600 chore: MR renamed feedback 2025-02-19 20:47:12 +00:00
Nick 7f631174d7 Use native snapshot extension 2025-02-17 14:27:05 -08:00
Nick 379344036f Reposilite maven publication endpoint 2025-02-17 14:24:20 -08:00
Paul 759f0800cd Added MoLang flows for poke_ball_capture_calculated, evolution_tested, evolution_accepted, evolution_completed, cleaned up a collection of MoLang hooks from PokemonProperties and spawning 2025-02-17 20:08:49 +10:00
Paul 0ed0270d18 Merge remote-tracking branch 'origin/main' 2025-02-17 16:42:05 +10:00
Hiroku 478e714a2d Merge branch 'held-item-rending-fix' into 'main'
Visual Held Items Rending Fix

See merge request cable-mc/cobblemon!1536
2025-02-17 03:13:38 +00:00
Josh 6005c04e64 There was an issue with rendering items in the original visual held items branch that wasn't properly reverted. If multiple pokemon where being rendered with different item display contexts it would prioritize one, ignoring the other. This fixes that. 2025-02-16 21:16:48 -05:00
Polymeta 75f76ae228 Merge branch 'fix/chestboat-inventory-persistence' into 'main'
Fix 1444 (Apricorn boat with chest voiding inventory)

Closes #1444

See merge request cable-mc/cobblemon!1533
2025-02-16 01:09:56 +00:00
JustAHuman-xDandPolymeta 31a955fed1 Fix 1444 (Apricorn boat with chest voiding inventory) 2025-02-16 01:09:55 +00:00
Paul 4f1517e336 Adding molang context and functions to poke ball capture event 2025-02-16 10:07:14 +10:00
Polymeta 6772d95d25 fix example json [skip-ci] 2025-02-15 23:47:51 +00:00
Hiroku a29f9d7bce Merge branch 'visually-held-items' into 'main'
Visible Held Items Implementation

See merge request cable-mc/cobblemon!1531
2025-02-15 23:28:56 +00:00
Josh 2ff6559b5e Merge remote-tracking branch 'origin/visually-held-items' into visually-held-items 2025-02-15 16:49:51 -05:00
Josh fc6739f640 Revert "Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame"
This reverts commit 6ff4eef7dc.
2025-02-15 16:48:12 -05:00
Josh 6ff4eef7dc Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame 2025-02-15 16:40:11 -05:00
Josh 82151c3944 Updated HeldItemRender to only update modifiers once when the item changes, rather than every frame 2025-02-15 16:38:50 -05:00
Hiroku 0af4588a14 README change [skip-ci] 2025-02-15 20:26:29 +00:00
Josh 3c07ca973b Updated CHANGELOG.md 2025-02-15 15:14:33 -05:00
Josh 60f3393f3f HeldItemRenderer is no longer instantiated every frame 2025-02-15 02:16:25 -05:00
Josh c6a1518e0d A HeldItemRenderer is no longer instantiated every frame, oops 2025-02-14 22:16:44 -05:00
Josh e816e41df1 Removed unused/unnecessary methods 2025-02-14 01:34:20 -05:00
Josh 96a9844fa4 Changed heldItemVisible to optional 2025-02-13 22:02:35 -05:00
Josh 8febe77edb Made item visibility value savable 2025-02-13 21:21:29 -05:00
Josh ef89d8fcd8 renamed isItemHidden to heldItemVisible and reversed the bool in places where needed 2025-02-13 21:20:40 -05:00
Josh 0253a4dd7a Items with components no longer break rendering 2025-02-13 19:46:07 -05:00
Josh 5259ea0455 Merge branch 'main' into visually-held-items 2025-02-13 17:53:08 -05:00
Josh f637768e3d Rendering items for shoulder pokemon 2025-02-13 17:38:02 -05:00
Josh ef867a02d5 Refactors and renamed item_glasses to item_face 2025-02-13 17:37:45 -05:00
Josh e0456939a9 ModelWidget can render held items 2025-02-12 19:04:59 -05:00
Ina 7210063026 feat: Add config support for this system 2025-02-12 20:10:16 +00:00
Ina 94e52f6f82 Merge branch 'main' into feat/animated-party-portraits
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt
2025-02-12 18:49:15 +00:00
Josh cba0c138b0 Merge branch 'main' into visually-held-items 2025-02-12 11:17:13 -05:00
Josh b748e39877 Removed unused comment 2025-02-12 11:15:27 -05:00
Polymeta 0d311593cc remove owned pokemon immediately when player stops seeing them, instead of queuing them
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-11 11:12:13 +00:00
Josh a1031f3dc9 Logic to decide when held items or hand items are shown. 2025-02-11 00:13:35 -05:00
Josh e753e05c67 Created new C2S packet for changing item visibility 2025-02-11 00:07:51 -05:00
Josh ed292bcaea Changed item tag names 2025-02-10 23:05:59 -05:00
Josh c2f1176aca Merge branch 'main' into visually-held-items 2025-02-08 19:18:00 -05:00
Josh 637ad2e510 Created a Renderer Class for held items. Using Null objects to modify scale and displayContext for now, but this is planned to change 2025-02-08 17:17:13 -05:00
Josh e08ddc4fc4 Started implementing renderable held items. Only working client side at the moment. 2025-02-08 00:02:25 -05:00
Ina 0da8243f05 fix: Missing return 2024-11-17 19:38:03 +00:00
Ina 2774360d4c feat: Add system to draw animated (or not) portraits based on graphics settings. 2024-11-17 19:34:49 +00:00
226 changed files with 4405 additions and 4755 deletions
+16 -1
View File
@@ -6,6 +6,10 @@
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
- Pokémon now follow the mouse cursor on the Summary screen, with an option to disable this in the settings.
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
@@ -21,6 +25,15 @@
- Fixed fling not using Item Names for minecraft held items that substitute Pokémon items
- Fixed evolutions sometimes preventing players from logging in to servers
- Fixed rendering of shoulder-mounted Pokémon desyncing between clients
- Fixed apricorn chest boats forgetting their inventories when being unloaded
- Fixed moves not updating correctly between form changes resulting in illegal movesets
- Fixed crash sometimes occurring with the "Oritech" mod
- Fixed crashes due to an incorrect Java version handing out an obscure crash.
- Fixed some berries being able to rarely get too many berries and cause a crash.
- Fixed status curing berries not playing the berry eating sound, same for healing berries used mid-battle.
- Fixed owned Pokémon sometimes being un-interactable after the player relogs fast
- Fixed field name in evolution requirements for Spewpa Pokeball.
- Fixed LevelUpCriterion logic to correctly check that the Pokémon is a preEvo.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -34,11 +47,13 @@
### Changes
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
- The `requiredContext` for an item interaction evolution
- The `itemCondition` for a held item evolution requirement
- The `fossils` for a fossil entry
- Added MoLang flows for `poke_ball_capture_calculated`, `evolution_tested`, `evolution_accepted`, `evolution_completed`
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -2159,4 +2174,4 @@ Thank you so much to all of our community translators that bring the mod to the
- 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.
- Initial release.
+1 -1
View File
@@ -1,6 +1,6 @@
# Cobblemon
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and Forge.
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and NeoForge.
## Getting started
-81
View File
@@ -1,81 +0,0 @@
{
"lastSavedVersion": "1.7.0",
"maxPokemonLevel": 100,
"maxPokemonFriendship": 255,
"announceDropItems": true,
"defaultDropItemMethod": "on-entity",
"ambientPokemonCryTicks": 1080,
"defaultBoxCount": 30,
"pokemonSaveIntervalSeconds": 30,
"storageFormat": "nbt",
"preventCompletePartyDeposit": false,
"mongoDBConnectionString": "mongodb://localhost:27017",
"mongoDBDatabaseName": "cobblemon",
"maxVerticalCorrectionBlocks": 64,
"minimumLevelRangeMax": 10,
"enableSpawning": true,
"minimumDistanceBetweenEntities": 8.0,
"maxNearbyBlocksHorizontalRange": 4,
"maxNearbyBlocksVerticalRange": 2,
"maxVerticalSpace": 8,
"worldSliceDiameter": 8,
"worldSliceHeight": 16,
"ticksBetweenSpawnAttempts": 20.0,
"minimumSliceDistanceFromPlayer": 16.0,
"maximumSliceDistanceFromPlayer": 64.0,
"exportSpawnConfig": false,
"savePokemonToWorld": true,
"exportStarterConfig": false,
"autoUpdateShowdown": true,
"defaultFleeDistance": 32.0,
"allowExperienceFromPvP": true,
"experienceShareMultiplier": 0.5,
"luckyEggMultiplier": 1.5,
"allowSpectating": true,
"experienceMultiplier": 2.0,
"pokemonPerChunk": 1.0,
"passiveStatuses": {
"cobblemon:poison": "180-300",
"cobblemon:poisonbadly": "180-300",
"cobblemon:paralysis": "180-300",
"cobblemon:frozen": "180-300",
"cobblemon:sleep": "180-300",
"cobblemon:burn": "180-300"
},
"infiniteHealerCharge": false,
"maxHealerCharge": 6.0,
"secondsToChargeHealingMachine": 900.0,
"defaultFaintTimer": 300,
"faintAwakenHealthPercent": 0.2,
"healPercent": 0.05,
"healTimer": 60,
"baseApricornTreeGenerationChance": 0.1,
"displayEntityLevelLabel": true,
"displayEntityNameLabel": true,
"displayNameForUnknownPokemon": false,
"displayEntityLabelsWhenCrouchingOnly": false,
"shinyRate": 8192.0,
"shinyNoticeParticlesDistance": 24.0,
"captureCalculator": "cobblemon",
"playerDamagePokemon": true,
"appleLeftoversChance": 0.025,
"maxRootsInArea": 5,
"bigRootPropagationChance": 0.1,
"energyRootChance": 0.25,
"maxDynamaxLevel": 10,
"teraTypeRate": 20.0,
"defaultPasturedPokemonLimit": 16,
"pastureBlockUpdateTicks": 40,
"pastureMaxWanderDistance": 64,
"pastureMaxPerChunk": 4.0,
"maxInsertedFossilItems": 2,
"walkingInBattleAnimations": false,
"battleWildMaxDistance": 12.0,
"tradeMaxDistance": 12.0,
"battlePvPMaxDistance": 32.0,
"battleSpectateMaxDistance": 64.0,
"maxPokedexScanningDetectionRange": 10.0,
"hideUnimplementedPokemonInThePokedex": false,
"summaryPokemonFollowCursor": true,
"enableDebugKeys": false
}
@@ -9,9 +9,6 @@
package com.cobblemon.mod.common;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
@@ -120,22 +117,8 @@ public interface Rollable {
return (float) Mth.wrapDegrees(-Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
}
/**
* Get the angular velocity in degrees per second as a Vec3
*/
Vec3 angRollVel( double deltaTime );
public Vec3 rotationOnMouseXY(double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime );
boolean shouldRoll();
boolean useAngVelSmoothing();
void clearRotation();
}
@@ -12,15 +12,9 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
@@ -34,70 +28,4 @@ public abstract class LivingEntityMixin {
}
}
/*
@Inject(method = "travelRidden", at = @At(value = "HEAD"))
private void cobblemon$altTravelRidden(Player player, Vec3 travelVector)
{
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
}
/*
// Shadow the original method so we can call it.
@Shadow
protected abstract boolean isControlledByLocalInstance();
/**
* Redirect the call to isControlledByLocalInstance() within travelRidden.
* When travelRidden calls isControlledByLocalInstance(), it will get our modified result.
*/
/*
@Redirect(
method = "travelRidden(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/Vec3;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/LivingEntity;isControlledByLocalInstance()Z"
)
)
private boolean redirectIsControlledByLocalInstance(LivingEntity instance) {
// Return true if either the original method returns true or our custom condition is true.
return this.isControlledByLocalInstance() || this.b;
}
@Overwrite
private void travelRidden(Player player, Vec3 travelVector) {
Vec3 vec3 = this.getRiddenInput(player, travelVector);
this.tickRidden(player, vec3);
if (this.isControlledByLocalInstance()) {
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
} else {
this.calculateEntityAnimation(false);
this.setDeltaMovement(Vec3.ZERO);
this.tryCheckInsideBlocks();
}
}
@ModifyArg(
method = "travelRidden",
at = @At(
value = "INVOKE",
target = "setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V"
),
index = 0 // the first (and only) argument
)
private Vec3 modifyDeltaMovementArgument(Vec3 original) {
// Optionally, check if original is Vec3.ZERO, to only replace that call:
if (original.equals(Vec3.ZERO)) {
// Return your custom vector instead of Vec3.ZERO
return original;
}
// Otherwise, return the original value
return original;
}
*/
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import net.minecraft.client.player.LocalPlayer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin {
@Shadow
private float jumpRidingScale;
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//ride.getRiding().canJump()
if( !ride.getRiding().canJump(ride, player))
{
cir.setReturnValue(0.5f);
}
}
// Check if the player is riding your custom entity (for example, a Pokemon)
/*
if (player.isRiding() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity rideable = (PokemonEntity) player.getVehicle();
// Get your custom fuel ratio (a value from 0.0 to 1.0)
float fuelRatio = rideable.getRidingFuelRatio();
cir.setReturnValue(fuelRatio);
}
*/
}
}
@@ -38,7 +38,6 @@ import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -48,7 +47,6 @@ import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.objectweb.asm.Opcodes;
@@ -57,7 +55,6 @@ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -181,8 +178,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Inject(method = "isInvulnerableTo", at = @At("HEAD"), cancellable = true)
public void isInvulnerableTo(DamageSource source, CallbackInfoReturnable<Boolean> ci) {
if (!level().isClientSide) {
ServerPlayer player = (ServerPlayer)(Object)this;
if (!level().isClientSide && (Object) this instanceof ServerPlayer player) {
boolean invulnerableInBattle = this.level().getGameRules().getBoolean(CobblemonGameRules.BATTLE_INVULNERABILITY);
boolean inBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (invulnerableInBattle && inBattle) {
@@ -275,7 +271,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Override
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
//Disabled to allow non mouse inputs to modify roll
if (!shouldRoll()) return this;
if (this.orientation == null) {
@@ -301,61 +296,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public Vec3 rotationOnMouseXY(double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime )
{
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
//this is ridiculous. There likely needs to be some sort of mouseInfo class.
return pokemonEntity.getRiding().rotationOnMouseXY
(
pokemonEntity,
player,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime );
}
@Override
public boolean useAngVelSmoothing() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return true;
if (player.isHolding(Items.DIAMOND)) return false;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
@Override
public Vec3 angRollVel( double deltaTime ) {
var ret = new Vec3(0.0, 0.0, 0.0);
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return ret;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return ret;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return ret;
return pokemonEntity.getRiding().angRollVel( pokemonEntity, player, deltaTime );
}
@Override
public void clearRotation() {
this.orientation = null;
@@ -6,12 +6,21 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -36,9 +45,12 @@ public abstract class CameraMixin {
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Shadow private Vec3 position;
@Shadow private float eyeHeight;
@Shadow private float eyeHeightOld;
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@@ -80,6 +92,48 @@ public abstract class CameraMixin {
ci.cancel();
}
//If you want to move this to a delagate you need an AW for position
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
public void positionCamera(Camera instance, double x, double y, double z, Operation<Void> original){
Entity entity = instance.getEntity();
Entity vehicle = entity.getVehicle();
if(vehicle instanceof Rideable){
//RidingCameraDelagate.INSTANCE.positionCamera(instance, x, y, z, original);
if(!(vehicle instanceof PokemonEntity)){
original.call(instance, x, y, z);
return;
}
PokemonEntity pokemon = (PokemonEntity) vehicle;
int seatIndex = pokemon.getPassengers().indexOf(entity);
Seat seat = pokemon.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemon.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
if(locator == null){
original.call(instance, x, y, z);
return;
}
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
Vec3 entityPos = new Vec3(
Mth.lerp(instance.getPartialTickTime(), pokemon.xOld, pokemon.getX()),
Mth.lerp(instance.getPartialTickTime(), pokemon.yOld, pokemon.getY()),
Mth.lerp(instance.getPartialTickTime(), pokemon.zOld, pokemon.getZ())
);
Rollable rollable = (Rollable) entity;
float currEyeHeight = Mth.lerp(instance.getPartialTickTime(), eyeHeightOld, eyeHeight);
Matrix3f orientation = rollable.shouldRoll() && rollable.getOrientation() != null ? rollable.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight, 0f)));
position = locatorOffset.add(entityPos).add(rotatedEyeHeight);
} else {
original.call(instance, x, y, z);
}
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import net.minecraft.client.DeltaTracker;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
@@ -6,12 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.ModAPI;
@@ -40,7 +40,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
*/
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Unique private final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
// ignore the hint saying implmentation is never null, if something crashes then it is null and causes red herrings
@Unique private final String MODEL_PATH = (Cobblemon.implementation != null && Cobblemon.implementation.getModAPI() == ModAPI.FABRIC) ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import java.util.List;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.client.CobblemonClient;
@@ -14,13 +14,11 @@ import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.Blaze3D;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -35,11 +33,8 @@ public class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Unique SmoothDouble xMouseSmoother = new SmoothDouble();
@Unique SmoothDouble yMouseSmoother = new SmoothDouble();
@Unique SmoothDouble pitchSmoother = new SmoothDouble();
@Unique SmoothDouble rollSmoother = new SmoothDouble();
@Unique SmoothDouble yawSmoother = new SmoothDouble();
@Shadow @Final private SmoothDouble smoothTurnY;
@@ -49,8 +44,6 @@ public class MouseHandlerMixin {
@Shadow private double accumulatedDY;
@Shadow private double accumulatedScrollX;
@Inject(
method = "onScroll",
at = @At(
@@ -102,12 +95,7 @@ public class MouseHandlerMixin {
target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V"
)
)
public boolean cobblemon$modifyRotation(LocalPlayer player,
double cursorDeltaX,
double cursorDeltaY,
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
public boolean cobblemon$modifyRotation(LocalPlayer player, double cursorDeltaX, double cursorDeltaY, @Local(argsOnly = true) double d) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -121,92 +109,16 @@ public class MouseHandlerMixin {
}
if (!(player instanceof Rollable rollable)) return true;
if (!rollable.shouldRoll()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
}
//maybe have a rollable x mouse and rollable y mouse that take the xmousesmoother
//and y mouse smoother ( which are right now named pichsmoother and roll smoother)
// values and feed it to those rollable functions which will be called by the
//specific ride controller where rollable.rotate can then be called and used
//controller specific.
//return a vec3 of the yaw pitch and roll that are wanted.
//multiply by sensitivity before passing it in
//var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.05f, d);
//var yMouse = yMouseSmoother.getNewDeltaValue(cursorDeltaY * 0.05f, d);
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var ridingSensitivity = Math.pow(defaultSensitivity, 3);
Vec3 angVecMouse = rollable.rotationOnMouseXY
( cursorDeltaY,
cursorDeltaX ,
yMouseSmoother,
xMouseSmoother,
ridingSensitivity,
movementTime );
//make this configurable maybe?
//var mRoll = rollSmoother.getNewDeltaValue(angVecMouse.z * 0.15f, d);
rollable.rotate((float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z);
//var yMouse = yMouseSmoother.getNewDeltaValue(angVecMouse.y * 0.15f, d);
//var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
//get the current angular velocity requested to be applied
//by the ride controller
var angRot = rollable.angRollVel(movementTime);
if( rollable.useAngVelSmoothing() )
{
//get the current angular velocity requested to be applied
//by the ride controller
//Multiply against deltatime to get a consistent turn
//independent of framerate
if( angRot != null ) {
//var yaw = angRot.x * d;
//Add the yaw to the mouse here... this all needs changed. It should
//just map which 0 1 or 2 of the three it is going to modulate with
//what
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
rollable.rotate((float) yaw, (float) pitch, (float) roll);
}
}
else
{
//Multiply against deltatime to get a consistent turn
//independent of framerate
if( angRot != null ) {
//var yaw = angRot.x * d;
//Add the yaw to the mouse here... this all needs changed. It should
//just map which 0 1 or 2 of the three it is going to modulate with
//what
rollable.rotate((float) (angRot.x * 10 * d),
(float) (angRot.y * 10 * d),
(float) (angRot.z * 10 * d));
}
}
var pitch = pitchSmoother.getNewDeltaValue(cursorDeltaY * 0.15f, d);
var roll = rollSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
rollable.rotate(0.0F, (float)pitch, (float)roll);
return false;
}
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.renderer;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
/**
* @author landonjw
*/
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {
// @Redirect(
// method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V",
// at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;hasVehicle()Z"),
// slice = @Slice(
// from = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerpAngleDegrees(FFF)F"),
// to = @At(value = "TAIL")
// )
// )
// private boolean dodgeRenderer(LivingEntity instance) {
// return false;
// }
/*@WrapOperation(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;wrapDegrees(F)F")
)
private float replaceWrapValue(float angle, Operation<Float> original){
if(origin){
return 0f;
} else {
return original.call(angle);
}
}*/
}
@@ -12,14 +12,6 @@ object CobblemonBuildDetails {
const val BRANCH = "{{ branch | default("Not Specified") }}"
fun smallCommitHash(): String {
if(this.GIT_COMMIT.equals("Not Specified")) {
return "Unknown"
}
return this.GIT_COMMIT.substring(0, 7)
}
fun version(): String {
return this.VERSION + if(this.SNAPSHOT) "+${this.smallCommitHash()}" else ""
}
}
@@ -80,6 +80,7 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
import com.cobblemon.mod.common.permission.LaxPermissionValidator
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
@@ -278,6 +279,7 @@ object Cobblemon {
SHINY_ASPECT.register()
GENDER_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
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.net.messages.client.data.CosmeticItemAssignmentSyncPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.cosmetic.CosmeticItemAssignment
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.PokemonPropertiesAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.core.RegistryAccess
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
object CobblemonCosmeticItems : JsonDataRegistry<CosmeticItemAssignment> {
override val id = cobblemonResource("cosmetic_items")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonCosmeticItems>()
override val typeToken = TypeToken.get(CosmeticItemAssignment::class.java)
override val resourcePath = "cosmetic_items"
override val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(PokemonProperties::class.java, PokemonPropertiesAdapter(saveLong = false))
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
val cosmeticItems = mutableListOf<CosmeticItemAssignment>()
override fun reload(data: Map<ResourceLocation, CosmeticItemAssignment>) {
cosmeticItems.clear()
data.entries.forEach { (id, value) -> value.id = id }
cosmeticItems.addAll(data.values)
}
override fun sync(player: ServerPlayer) {
CosmeticItemAssignmentSyncPacket(cosmeticItems).sendToPlayer(player)
}
fun findValidForPokemon(pokemon: Pokemon) = cosmeticItems.filter { it.pokemon.any { it.matches(pokemon) } }
fun findValidCosmeticForPokemonAndItem(registryAccess: RegistryAccess, pokemon: Pokemon, itemStack: ItemStack) = cosmeticItems
.filter { it.pokemon.any { it.matches(pokemon) } }
.flatMap { it.cosmeticItems }
.firstOrNull { it.consumedItem.fits(itemStack.item, registryAccess.registryOrThrow(Registries.ITEM)) }
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
@@ -86,13 +87,20 @@ object CobblemonFlows : DataRegistry {
observable.emit(this)
}
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>, cancelable: Cancelable? = null) {
fun run(
eventResourceLocation: ResourceLocation,
context: Map<String, MoValue>,
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
runtime.environment.query.addFunction("cancel") { cancelable }
}
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
flows[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
@@ -146,6 +146,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
@@ -187,6 +188,7 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
@@ -259,6 +261,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(TeraTypeUpdatePacket.ID, TeraTypeUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(DmaxLevelUpdatePacket.ID, DmaxLevelUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(GmaxFactorUpdatePacket.ID, GmaxFactorUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemUpdatePacket.ID, CosmeticItemUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -349,6 +352,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -408,6 +412,7 @@ object CobblemonNetwork {
val list = mutableListOf<PacketRegisterInfo<*>>()
// Pokemon Update Packets
list.add(PacketRegisterInfo(SetNicknamePacket.ID, SetNicknamePacket::decode, SetNicknameHandler))
list.add(PacketRegisterInfo(SetItemHiddenPacket.ID, SetItemHiddenPacket::decode, SetItemHiddenHandler))
// Evolution Packets
list.add(PacketRegisterInfo(AcceptEvolutionPacket.ID, AcceptEvolutionPacket::decode, AcceptEvolutionHandler))
@@ -33,7 +33,7 @@ class LevelUpCriterion(
}
override fun matches(player: ServerPlayer, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val preEvo = context.pokemon.preEvolution != null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
if (preEvo || hasEvolution) {
@@ -43,27 +43,3 @@ class LevelUpCriterion(
}
}
/*class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
json.addProperty("level", level)
json.addProperty("has_evolved", evolved)
}
override fun fromJson(json: JsonObject) {
level = json.get("level")?.asInt ?: 0
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayer, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
if (preEvo || hasEvolution) {
evolutionCheck = !(preEvo == hasEvolution)
}
return level == context.level && evolutionCheck == evolved
}
}*/
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.battles.interpreter
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
/**
@@ -30,6 +33,13 @@ interface BattleContext {
/** The [BattlePokemon] that directly or indirectly caused this [BattleContext]. */
val origin: BattlePokemon?
fun getStruct() = queryStructOf(
"type" to { StringValue(type.name) },
"origin" to { origin?.struct ?: DoubleValue(0.0) },
"id" to { StringValue(id) },
"turn_created" to { DoubleValue(turn.toDouble()) }
)
/**
* The type of [BattleContext].
*
@@ -99,7 +99,6 @@ class Berry(
val weight: Float,
val boneMealChance: Float
) {
@Transient
var identifier: ResourceLocation = identifier
internal set
@@ -159,7 +158,7 @@ class Berry(
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
* Triggers [BerryYieldCalculationEvent].
*
* @param world The [World] the tree is present in.
* @param world The [Level] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @param placer The [LivingEntity] tending to the tree, if any.
@@ -173,9 +172,10 @@ class Berry(
if (BerryBlock.getMulch(treeEntity) == MulchVariant.RICH) {
//I hope the upper bound isnt exclusive, especially when there's a method called nextBetweenExclusive
// update: it was meant to be exclusive, which caused #1124. I've lowered the upper bound from 3 to 2.
yield += world.random.nextIntBetweenInclusive(1, 2)
yield += world.random.nextIntBetweenInclusive(RICH_MULCH_MIN, RICH_MULCH_MAX)
treeEntity.decrementMulchDuration(world, pos, state)
}
yield = yield.coerceAtMost(growthPoints.size)
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
return yield
@@ -193,7 +193,7 @@ class Berry(
*
* @return The maximum possible yield.
*/
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() } + RICH_MULCH_MAX
/**
* Checks if this berry can mutate with the given [partner].
@@ -231,6 +231,7 @@ class Berry(
}
// A cheat since gson doesn't invoke init block
// well I actually suspect it does but
internal fun validate() {
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
throw IllegalArgumentException("A berry base yield must be a positive range")
@@ -247,10 +248,6 @@ class Berry(
}
*/
this.growthFactors.forEach { it.validateArguments() }
val maxYield = this.maxYield()
if (this.growthPoints.size < maxYield) {
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
}
this.shapedFlower = hashMapOf()
this.shapedFruit = hashMapOf()
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
@@ -339,6 +336,8 @@ class Berry(
}
companion object {
const val RICH_MULCH_MIN = 1
const val RICH_MULCH_MAX = 2
internal fun decode(buffer: RegistryFriendlyByteBuf): Berry {
val identifier = buffer.readIdentifier()
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.conditional
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.mojang.serialization.Codec
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.tags.TagKey
val ITEM_REGISTRY_LIKE_CODEC = makeCodecForRegistryLikeCondition("item", Registries.ITEM)
fun <T : Any> makeCodecForRegistryLikeCondition(name: String, registryKey: ResourceKey<Registry<T>>): Codec<RegistryLikeCondition<T>> {
return CodecUtils.createByStringCodec(
from = { str ->
if (str.startsWith("#")) {
return@createByStringCodec RegistryLikeTagCondition<T>(TagKey.create(registryKey, str.substring(1).asIdentifierDefaultingNamespace()))
} else {
return@createByStringCodec RegistryLikeIdentifierCondition<T>(str.asIdentifierDefaultingNamespace())
}
},
to = { condition ->
if (condition is RegistryLikeTagCondition) {
"#${condition.tag.location}"
} else {
(condition as RegistryLikeIdentifierCondition).identifier.toString()
}
},
errorSupplier = { "Unknown $name condition: $it. Should be # followed by a tag, or $name resource location e.g. #minecraft:ladders or minecraft:grass_block." }
)
}
@@ -26,13 +26,13 @@ open class RegistryLikeTagCondition<T : Any>(val tag: TagKey<T>) : RegistryLikeC
companion object {
const val PREFIX = "#"
fun <T : Any> resolver(
ResourceKey: ResourceKey<Registry<T>>,
resourceKey: ResourceKey<Registry<T>>,
constructor: (TagKey<T>) -> RegistryLikeTagCondition<T>
): (JsonElement) -> RegistryLikeTagCondition<T>? = {
val firstSymbol = it.asString.substring(0, 1)
if (firstSymbol == PREFIX) {
val identifier = ResourceLocation.parse(it.asString.substring(1))
constructor(TagKey.create(ResourceKey, identifier))
constructor(TagKey.create(resourceKey, identifier))
} else {
null
}
@@ -8,6 +8,10 @@
package com.cobblemon.mod.common.api.events
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
/**
* Something that can be canceled. This is a highly complex class and should only be read by professional engineers.
*
@@ -21,4 +25,9 @@ abstract class Cancelable {
fun cancel() {
isCanceled = true
}
val cancelFunc: Pair<String, (MoParams) -> MoValue> = "cancel" to {
cancel()
DoubleValue.ONE
}
}
@@ -212,6 +212,10 @@ object CobblemonEvents {
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val COSMETIC_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val COSMETIC_ITEM_POST = EventObservable<HeldItemEvent.Post>()
@JvmField
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@@ -8,16 +8,12 @@
package com.cobblemon.mod.common.api.events.battles
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
import java.util.function.Function
/**
* Event fired when a [BattlePokemon] faints. Exposes the [BattlePokemon] that fainted and the [BattleContext]
@@ -37,13 +33,6 @@ data class BattleFaintedEvent(
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to killed.struct,
"context" to QueryStruct(
hashMapOf(
"type" to Function { StringValue(context.type.name) },
"origin" to Function { context.origin?.struct ?: DoubleValue(0.0) },
"id" to Function { StringValue(context.id) },
"turn_created" to Function { DoubleValue(context.turn.toDouble()) },
)
)
"context" to context.getStruct()
)
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.events.battles
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.util.asArrayValue
import net.minecraft.network.chat.MutableComponent
@@ -30,6 +31,8 @@ data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
val functions = moLangFunctionMap(cancelFunc)
}
/**
@@ -26,7 +26,7 @@ data class TerastallizationEvent(
val battle: PokemonBattle,
val pokemon: BattlePokemon,
val teraType: TeraType
) {
) {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
@@ -8,9 +8,15 @@
package com.cobblemon.mod.common.api.events.pokeball
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getIntOrNull
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -32,4 +38,34 @@ class PokeBallCaptureCalculatedEvent(
action(this, thrower)
}
}
val context = mutableMapOf<String, MoValue>(
"thrower" to thrower.asMostSpecificMoLangValue(),
"pokemon" to pokemonEntity.struct,
"poke_ball" to pokeBallEntity.struct,
"is_successful_capture" to DoubleValue(captureResult.isSuccessfulCapture),
"is_critical_capture" to DoubleValue(captureResult.isCriticalCapture),
)
val functions = moLangFunctionMap(
"set_shakes" to { params ->
val numShakes = params.getInt(0)
val successful = params.getBooleanOrNull(1) ?: (numShakes == 4)
captureResult = CaptureContext(
numberOfShakes = numShakes,
isSuccessfulCapture = successful,
isCriticalCapture = false
)
DoubleValue.ONE
},
"set_critical_capture" to { params ->
val numShakes = params.getIntOrNull(0) ?: 1
captureResult = CaptureContext(
numberOfShakes = numShakes,
isSuccessfulCapture = true,
isCriticalCapture = true
)
DoubleValue.ONE
}
)
}
@@ -20,8 +20,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class FriendshipUpdatedEvent(
val pokemon: Pokemon,
val newFriendshipInitial: Int
)
{
) {
var newFriendship: Int = newFriendshipInitial
set(value) {
field = value.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
@@ -9,13 +9,11 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.events.pokemon.HeldItemEvent.Post
import com.cobblemon.mod.common.api.events.pokemon.HeldItemEvent.Pre
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.item.ItemStack
/**
* The base for all the events related to held items.
* The base for all the events related to held items and cosmetic items.
*
* @see [Pre]
* @see [Post]
@@ -28,7 +26,7 @@ interface HeldItemEvent {
val pokemon: Pokemon
/**
* Fired at the start of [Pokemon.swapHeldItem].
* Fired at the start of [Pokemon.swapHeldItem] and [Pokemon.swapCosmeticItem].
*
* This event should be used to mutate the results of this transaction.
*
@@ -44,7 +42,7 @@ interface HeldItemEvent {
data class Pre(override val pokemon: Pokemon, var receiving: ItemStack, var returning: ItemStack, var decrement: Boolean) : HeldItemEvent, Cancelable()
/**
* Fired at the end of [Pokemon.swapHeldItem].
* Fired at the end of [Pokemon.swapHeldItem] and [Pokemon.swapCosmeticItem].
*
* @property pokemon The [Pokemon] triggering this event.
* @property received The [ItemStack] considered as received and set to the [pokemon]. This is a copy and mutation will not be taken into account.
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -23,4 +24,11 @@ class LevelUpEvent(val pokemon: Pokemon, val oldLevel: Int, var newLevel: Int) {
"old_level" to DoubleValue(oldLevel.toDouble()),
"new_level" to DoubleValue(newLevel.toDouble())
)
val functions = moLangFunctionMap(
"set_new_level" to {
newLevel = it.getInt(0)
DoubleValue.ONE
}
)
}
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
@@ -47,7 +48,9 @@ sealed interface PokedexDataChangedEvent {
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
) : PokedexDataChangedEvent, Cancelable()
) : PokedexDataChangedEvent, Cancelable() {
val functions = moLangFunctionMap(cancelFunc)
}
class Post(
override val dataSource: PokedexEntityData,
@@ -8,8 +8,12 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.server
import java.util.UUID
data class PokemonGainedEvent(
@@ -20,4 +24,8 @@ data class PokemonGainedEvent(
"player_id" to StringValue(playerId.toString()),
"pokemon" to pokemon.struct
)
val functions = moLangFunctionMap(
"player" to { server()?.playerList?.getPlayer(playerId)?.asMoLangValue() ?: DoubleValue.ZERO },
)
}
@@ -8,7 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -25,4 +28,11 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class EvolutionAcceptedEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : Cancelable(), EvolutionEvent
) : Cancelable(), EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue()
)
val functions = moLangFunctionMap(cancelFunc)
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -23,4 +25,9 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class EvolutionCompleteEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : EvolutionEvent
) : EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue()
)
}
@@ -8,8 +8,13 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getBoolean
/**
* An event fired when [Evolution.test] has been performed.
@@ -25,4 +30,18 @@ data class EvolutionTestedEvent(
override val evolution: Evolution,
val originalResult: Boolean,
var result: Boolean
) : EvolutionEvent
) : EvolutionEvent {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"evolution" to evolution.asMoLangValue(),
"original_result" to DoubleValue(originalResult),
)
val functions = moLangFunctionMap(
"result" to { DoubleValue(result) },
"set_result" to {
result = it.getBoolean(0)
DoubleValue.ONE
}
)
}
@@ -16,6 +16,9 @@ import java.util.UUID
data class PokemonInteractionGUICreationEvent(
val pokemonID: UUID,
val mountShoulder: Boolean,
val giveHeld: Boolean,
val giveCosmetic: Boolean,
val canRide: Boolean,
val options: Multimap<Orientation, InteractWheelOption>
) {
fun addFillingOption(option: InteractWheelOption) {
@@ -36,6 +36,8 @@ import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.PokedexManager
import com.cobblemon.mod.common.api.pokedex.SeenCount
import com.cobblemon.mod.common.api.pokedex.SeenPercent
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -60,8 +62,12 @@ import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimati
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import java.util.UUID
@@ -1002,6 +1008,82 @@ object MoLangFunctions {
}
)
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("level") { DoubleValue(props.level?.toDouble() ?: 0) }
map.put("set_level") { params ->
props.level = params.getIntOrNull(0)
return@put DoubleValue.ONE
}
map.put("shiny") { DoubleValue(props.shiny) }
map.put("set_shiny") { params ->
props.shiny = params.getBooleanOrNull(0)
return@put DoubleValue.ONE
}
map.put("species") { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
map.put("set_species") { params ->
props.species = params.getStringOrNull(0)
return@put DoubleValue.ONE
}
map.put("gender") { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
map.put("set_gender") { params ->
props.gender = params.getStringOrNull(0)?.let { Gender.valueOf(it) }
return@put DoubleValue.ONE
}
map.put("form") { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
map.put("ivs") {
val ivs = props.ivs
if (ivs == null) {
return@put DoubleValue.ZERO
} else {
return@put ivs.struct
}
}
map.put("evs") {
val evs = props.evs
if (evs == null) {
return@put DoubleValue.ZERO
} else {
return@put evs.struct
}
}
map.put("friendship") { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
map.put("set_friendship") { params ->
props.friendship = params.getIntOrNull(0)
return@put DoubleValue.ONE
}
map.put("create") { params ->
val pokemon = props.create()
return@put pokemon.struct
}
map
}
)
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ evolution ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("result") { evolution.result.asMoLangValue() }
if (evolution is LevelUpEvolution) {
map.put("is_level_up") { DoubleValue.ONE }
} else if (evolution is TradeEvolution) {
map.put("is_trade") { DoubleValue.ONE }
} else if (evolution is ItemInteractionEvolution) {
map.put("is_item") { DoubleValue.ONE }
}
map.put("is_optional") { DoubleValue(evolution.optional) }
map.put("consumes_held_item") { DoubleValue(evolution.consumeHeldItem) }
map
}
)
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
@@ -1034,6 +1116,24 @@ object MoLangFunctions {
return value
}
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
val value = ObjectValue(
obj = this,
stringify = { it.asString() }
)
value.addFunctions(pokemonPropertiesFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun Evolution.asMoLangValue(): ObjectValue<Evolution> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
value.addFunctions(evolutionFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
val value = ObjectValue(
obj = this,
@@ -1170,6 +1270,24 @@ object MoLangFunctions {
return this
}
fun moLangFunctionMap(
vararg functions: Pair<String, (MoParams) -> MoValue>
): Map<String, (MoParams) -> MoValue> {
return functions.toMap()
}
fun queryStructOf(
vararg functions: Pair<String, (MoParams) -> MoValue>
): QueryStruct {
return QueryStruct(
hashMapOf<String, java.util.function.Function<MoParams, Any>>(
*functions.map { (name, func) ->
name to java.util.function.Function<MoParams, Any> { params -> func(params) }
}.toTypedArray()
)
)
}
fun MoLangRuntime.setup(): MoLangRuntime {
environment.query.addStandardFunctions()
return this
@@ -320,7 +320,7 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
"pokemon": "weedle",
"weight": "10",
"selectableTimes": "2",
"levelVariation": "2"
"levelVariation": "2",
"npcLevels": "1-8"
},
{
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
@@ -775,17 +772,6 @@ open class PokemonProperties {
return pieces.joinToString(separator)
}
fun asStruct(): VariableStruct {
val struct = VariableStruct()
species?.let { struct.setDirectly("species", StringValue(it)) }
level?.let { struct.setDirectly("level", DoubleValue(it)) }
shiny?.let { struct.setDirectly("shiny", DoubleValue(it)) }
// add more of the optional properties to the struct as doubles or strings
gender?.let { struct.setDirectly("gender", StringValue(it.name)) }
friendship?.let { struct.setDirectly("friendship", DoubleValue(it)) }
return struct
}
fun updateAspects() {
val aspects = mutableSetOf<String>()
AspectProvider.providers.forEach { aspects.addAll(it.provide(this)) }
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
@@ -47,7 +48,7 @@ import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementA
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
@@ -106,6 +107,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(RideController::class.java, RideControllerAdapter)
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
@@ -13,7 +13,7 @@ import net.minecraft.world.entity.PlayerRideableJumping
/**
* Represents an entity that supports riding.
*
* @since 1.5.0
* @since 1.7.0
*/
interface Rideable : PlayerRideableJumping {
@@ -10,20 +10,15 @@ package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import kotlin.math.sin
data class RidingManager(val entity: PokemonEntity) {
var lastSpeed = 0F
@@ -63,12 +58,8 @@ data class RidingManager(val entity: PokemonEntity) {
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
//val speedPlayer = driver.deltaMovement.horizontalDistance()
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
//println(speedEntity)
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
// driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$lastSpeed b/t")), true)
}
fun speed(entity: PokemonEntity, driver: Player): Float {
@@ -100,11 +91,6 @@ data class RidingManager(val entity: PokemonEntity) {
return controller.velocity(entity, driver, input)
}
fun fuelBar(entity: PokemonEntity, driver: Player): Float {
val controller = getController(entity) ?: return 0.0f
return controller.fuelBar(entity, driver)
}
fun canJump(entity: PokemonEntity, driver: Player): Boolean {
val controller = getController(entity) ?: return false
return controller.canJump(entity, driver)
@@ -115,24 +101,11 @@ data class RidingManager(val entity: PokemonEntity) {
return controller.jumpForce(entity, driver, jumpStrength)
}
/**
* Used to supply the dela for the lerp() between velocity
* vectors. Called inertia for now as the way it feels when
* affecting the ride is somewhat akin to inertia.
*/
fun inertia(entity: PokemonEntity): Double {
val controller = getController(entity) ?: return 0.5
return controller.inertia(entity)
}
//Can be accomplished through the velocity controls
fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
val controller = getController(entity) ?: return null
return controller.gravity(entity, regularGravity)
}
//Maybe should be changed to be a function that conveys:
// "turn on use our camera system function"
fun shouldRoll(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.shouldRoll(entity)
@@ -148,50 +121,8 @@ data class RidingManager(val entity: PokemonEntity) {
return controller.shouldRotatePokemonHead()
}
//this is called just as often as
fun rotationOnMouseXY
( entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
val controller = getController(entity) ?: return Vec3.ZERO
return controller.rotationOnMouseXY( entity,
driver,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime )
}
/**
* Determines whether mouse input will affect the rotation of
* the rollable object. Need to remove this as it's not used anymore
*/
//I think this might better serve as a "use smoothing on angular velocity"useAngVelSmoothing
fun useAngVelSmoothing(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return true
return controller.useAngVelSmoothing(entity)
}
fun turnOffOnGround(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.turnOffOnGround(entity)
}
fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val controller = getController(entity) ?: return Vec3(0.0, 0.0, 0.0)
return controller.angRollVel(entity, driver, deltaTime)
}
//FIXME: Make this not just be false for all controllers lol
fun dismountOnShift(): Boolean {
val controller = getController(entity) ?: return false
return controller.dismountOnShift(entity)
return false
}
}
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinitions
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readIdentifier
@@ -21,16 +21,16 @@ import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
class RidingProperties(
val stats: Map<RidingStat, RidingStatDefinitions> = mapOf(),
val stats: Map<RidingStat, RidingStatDefinition> = mapOf(),
val seats: List<Seat> = listOf(),
val conditions: List<Expression> = listOf(),
val controller: RideController? = null
) {
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
val stats: Map<RidingStat, RidingStatDefinitions> = buffer.readMap(
val stats: Map<RidingStat, RidingStatDefinition> = buffer.readMap(
{ buffer.readEnum<RidingStat>(RidingStat::class.java) },
{ RidingStatDefinitions.decode(buffer) }
{ RidingStatDefinition.decode(buffer) }
)
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
val conditions = buffer.readList { buffer.readString().asExpression() }
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.riding.controller
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingState
@@ -18,7 +17,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.writeIdentifier
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
@@ -31,7 +29,7 @@ import net.minecraft.world.phys.Vec3
* the pokemon experience drifting after movement, or generic movement. It's up to the
* controller to decide how the pokemon behaves.
*
* @since x.x.x
* @since 1.7.0
*/
interface RideController : Encodable, Decodable {
@@ -73,14 +71,6 @@ interface RideController : Encodable, Decodable {
*/
fun rotation(entity: PokemonEntity, driver: LivingEntity) : Vec2
/**
* Sets the change current change in rotation for that tick in three
* dimensions
*/
fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
return Vec3(0.0, 0.0, 0.0)
}
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
@@ -94,43 +84,13 @@ interface RideController : Encodable, Decodable {
*/
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3) : Vec3
fun fuelBar(entity: PokemonEntity, driver: Player): Float = 0.0f
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) : Vec3
fun gravity(entity: PokemonEntity, regularGravity: Double) : Double? = null
fun inertia(entity: PokemonEntity ) : Double = 0.5
fun shouldRoll(entity: PokemonEntity): Boolean = false
fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
//rolling function which is roll on mousex and pitch on mousey
fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
//Might need to add the somothing here for default.
val rollable = driver as Rollable
return Vec3(0.0, yMouse, xMouse )
}
fun turnOffOnGround(entity: PokemonEntity): Boolean = false
fun dismountOnShift(entity: PokemonEntity): Boolean = false
fun getRuntime(entity: PokemonEntity) = entity.riding.runtime
fun shouldRotatePokemonHead(): Boolean = false
@@ -23,7 +23,9 @@ import net.minecraft.network.chat.Component
* @author Hiroku
* @since February 17th, 2025
*/
class RidingStatDefinitions {
class RidingStatDefinition {
// This object gets read from a JSON adapter manually (field-by-field) so don't add fields without updating the adapter.
/** A map from [RidingStyle] to a minimum and maximum value for this stat. */
val ranges = mutableMapOf<RidingStyle, IntRange>()
/** Display name for the stat, if null falls back to [RidingStat.displayName]. */
@@ -51,14 +53,14 @@ class RidingStatDefinitions {
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingStatDefinitions {
fun decode(buffer: RegistryFriendlyByteBuf): RidingStatDefinition {
val displayName = buffer.readNullable { buffer.readText() }
val description = buffer.readNullable { buffer.readText() }
val ranges = buffer.readMap<RidingStyle, IntRange>(
{ buffer.readEnum<RidingStyle>(RidingStyle::class.java) },
{ IntRange(buffer.readSizedInt(IntSize.U_BYTE), buffer.readSizedInt(IntSize.U_BYTE)) }
)
return RidingStatDefinitions().apply {
return RidingStatDefinition().apply {
this.displayName = displayName
this.description = description
this.ranges.putAll(ranges)
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon.config
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -20,8 +22,8 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import com.google.gson.annotations.SerializedName
import net.minecraft.network.chat.MutableComponent
import kotlin.math.ceil
import net.minecraft.network.chat.MutableComponent
/**
* A [SpawnDetail] for spawning a [PokemonEntity].
@@ -69,7 +71,9 @@ class PokemonSpawnDetail : SpawnDetail() {
}
override fun autoLabel() {
val pokemonStruct = pokemon.asStruct()
struct.addFunction("pokemon") { pokemon.asMoLangValue() }
struct.addFunction("min_level") { DoubleValue(getDerivedLevelRange().start.toDouble()) }
struct.addFunction("max_level") { DoubleValue(getDerivedLevelRange().endInclusive.toDouble()) }
if (pokemon.species != null) {
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
if (species != null) {
@@ -88,7 +92,6 @@ class PokemonSpawnDetail : SpawnDetail() {
}
}
struct.setDirectly("pokemon", pokemonStruct)
super.autoLabel()
}
@@ -8,12 +8,12 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.bedrockk.molang.runtime.struct.ArrayStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.ModDependant
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.asTranslated
/**
@@ -55,21 +56,21 @@ abstract class SpawnDetail : ModDependant {
var labels = mutableListOf<String>()
val struct: VariableStruct = VariableStruct()
val struct: QueryStruct = queryStructOf(
"weight" to { DoubleValue(weight) },
"percentage" to { DoubleValue(percentage) },
"id" to { StringValue(id) },
"bucket" to { StringValue(bucket.name) },
"width" to { DoubleValue(width.toDouble()) },
"height" to { DoubleValue(height.toDouble()) },
"context" to { StringValue(context.name) },
"labels" to { labels.asArrayValue { StringValue(it) } }
)
override var neededInstalledMods = listOf<String>()
override var neededUninstalledMods = listOf<String>()
open fun autoLabel() {
struct.setDirectly("weight", DoubleValue(weight.toDouble()))
struct.setDirectly("percentage", DoubleValue(percentage.toDouble()))
struct.setDirectly("id", StringValue(id))
struct.setDirectly("bucket", StringValue(bucket.name))
struct.setDirectly("width", DoubleValue(width.toDouble()))
struct.setDirectly("height", DoubleValue(height.toDouble()))
struct.setDirectly("context", StringValue(context.name))
struct.setDirectly("labels", ArrayStruct(labels.mapIndexed { index, s -> "$index" to StringValue(s) }.toMap()))
}
open fun autoLabel() {}
open fun getName() = displayName?.asTranslated() ?: id.text()
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.rules.component
import com.bedrockk.molang.Expression
import com.bedrockk.molang.ast.BooleanExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -34,7 +33,7 @@ class FilterRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val allow: Expression = BooleanExpression(false)
val allow: Expression = BooleanExpression(true)
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return if (spawnSelector.selects(detail) && contextSelector.selects(ctx)) {
@@ -124,7 +124,10 @@ object CobblemonItemTags {
@JvmField val POWER_LENS = create("held/power_lens")
@JvmField val POWER_WEIGHT = create("held/power_weight")
@JvmField val TERRAIN_SEEDS = create("held/terrain_seeds")
//Held Item Visibility Tags
@JvmField val WEARABLE_FACE_ITEMS = create("held/visibility/face")
@JvmField val WEARABLE_HAT_ITEMS = create("held/visibility/hat")
@JvmField val HIDDEN_ITEMS = create("held/visibility/hidden")
private fun create(path: String) = TagKey.create(Registries.ITEM, cobblemonResource(path))
@@ -109,7 +109,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
determineMutation(world, random, pos, newState)
}
world.setBlock(pos, newState, Block.UPDATE_CLIENTS)
world.setBlock(pos, newState, UPDATE_CLIENTS)
convertMulchToEntity(world, newState, pos)
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
treeEntity.setChanged()
@@ -102,7 +102,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
mulchDuration = if (newDuration <= 0) {
mulchVariant = MulchVariant.NONE
this.setChanged()
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
world.sendBlockUpdated(pos, state, state, UPDATE_CLIENTS)
0
} else {
newDuration
@@ -313,6 +313,10 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
val berryPoints = arrayListOf<Pair<Berry, GrowthPoint>>()
val sequenceIndices = growthPointSequence.toCharArray().filter { it.digitToInt(16) < baseBerry.growthPoints.size }
for ((index, identifier) in this.growthPoints.withIndex()) {
// Don't brick old broken worlds that had too many
if (index >= sequenceIndices.size) {
break
}
val berry = Berries.getByIdentifier(identifier) ?: continue
val sequenceIndexHex = sequenceIndices[index].digitToInt(16)
berryPoints.add(berry to baseBerry.growthPoints[sequenceIndexHex])
@@ -8,22 +8,25 @@
package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
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.battle.BattleGUI
import com.cobblemon.mod.common.client.gui.portrait.FullyAnimatedPortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.SelectedAnimatedPortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.InanimatePortraitDrawer
import com.cobblemon.mod.common.client.gui.portrait.PortraitStyle
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
@@ -55,7 +58,10 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
private val genderIconMale = cobblemonResource("textures/gui/party/party_gender_male.png")
private val genderIconFemale = cobblemonResource("textures/gui/party/party_gender_female.png")
private val portraitBackground = cobblemonResource("textures/gui/party/party_slot_portrait_background.png")
val state = FloatingState()
private val inanimatePortraitDrawer = InanimatePortraitDrawer()
private val selectedAnimatedPortraitDrawer = SelectedAnimatedPortraitDrawer()
private val fullyAnimatedPortraitDrawer = FullyAnimatedPortraitDrawer()
}
private val screenExemptions: List<Class<out Screen>> = listOf(
@@ -84,6 +90,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
override fun render(context: GuiGraphics, tickCounter: DeltaTracker) {
val partialDeltaTicks = tickCounter.realtimeDeltaTicks
val minecraft = Minecraft.getInstance()
// Hiding if a Screen is open and not exempt
@@ -153,15 +160,14 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
0.0
)
state.currentAspects = pokemon.aspects
val portraitDrawer = when (Cobblemon.config.partyPortraitAnimations) {
PortraitStyle.NEVER_ANIMATE -> inanimatePortraitDrawer
PortraitStyle.ANIMATE_SELECTED -> selectedAnimatedPortraitDrawer
PortraitStyle.ALWAYS_ANIMATE -> fullyAnimatedPortraitDrawer
}
portraitDrawer.draw(pokemon, matrices, partialDeltaTicks, selectedSlot == index, index)
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = matrices,
partialTicks = 0F, // partialDeltaTicks, //Before you get any funny ideas about party animated pokemon, make sure they each get their own state instead of sharing.
contextScale = pokemon.form.baseScale,
state = state
)
matrices.popPose()
context.disableScissor()
}
@@ -0,0 +1,16 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.interact.wheel
enum class InteractTypePokemon {
SHOULDER,
HELD_ITEM,
COSMETIC_ITEM,
RIDE;
}
@@ -36,7 +36,7 @@ class InteractWheelButton(
const val BUTTON_SIZE = 69
const val TEXTURE_HEIGHT = BUTTON_SIZE * 2
const val ICON_SIZE = 32
const val ICON_SCALE = 0.5f
const val ICON_SCALE = 0.5F
const val ICON_OFFSET = 26.5
}
@@ -55,10 +55,10 @@ class InteractWheelButton(
height = BUTTON_SIZE,
vOffset = if (isHovered(mouseX.toFloat(), mouseY.toFloat()) && isEnabled) BUTTON_SIZE else 0,
textureHeight = TEXTURE_HEIGHT,
alpha = if (isEnabled) 1f else 0.4f
alpha = if (isEnabled) 1f else 0.4F
)
if(isHovered(mouseX.toFloat(), mouseY.toFloat())){
if(isEnabled && isHovered(mouseX.toFloat(), mouseY.toFloat())){
tooltipText?.let {
context.renderTooltip(Minecraft.getInstance().font, Component.translatable(it), mouseX, mouseY)
}
@@ -74,7 +74,7 @@ class InteractWheelButton(
y = iconY,
width = ICON_SIZE,
height = ICON_SIZE,
alpha = if (isEnabled) 1f else 0.4f,
alpha = if (isEnabled) 1F else 0.2F,
red = colour.x,
green = colour.y,
blue = colour.z,
@@ -93,7 +93,7 @@ class InteractWheelButton(
y = (iconY.toFloat() - ICON_SIZE),
width = ICON_SIZE,
height = ICON_SIZE,
alpha = if (isEnabled) 1f else 0.4f,
alpha = if (isEnabled) 1F else 0.4F,
red = colour.x,
green = colour.y,
blue = colour.z,
@@ -104,7 +104,7 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
tooltipText = option?.tooltipText,
x = x,
y = y,
isEnabled = option != null,
isEnabled = option != null && option.enabled,
colour = option?.colour ?: { null },
onPress = { option?.onPress?.invoke() },
canHover = { a: Double, b: Double -> !isMouseInCenter(a, b)}
@@ -26,48 +26,65 @@ import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import org.joml.Vector3f
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canRide: Boolean): InteractWheelGUI {
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canGiveHeld: Boolean, canGiveCosmetic: Boolean, canRide: Boolean): InteractWheelGUI {
val mountShoulder = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_shoulder.png"),
tooltipText = "cobblemon.ui.interact.mount.shoulder",
enabled = canMountShoulder,
onPress = {
if (canMountShoulder) {
InteractPokemonPacket(pokemonID, true, false).sendToServer()
InteractPokemonPacket(pokemonID, InteractTypePokemon.SHOULDER).sendToServer()
closeGUI()
}
}
)
val giveItem = InteractWheelOption(
val giveHeldItem = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
tooltipText = "cobblemon.ui.interact.give.item",
enabled = canGiveHeld,
onPress = {
InteractPokemonPacket(pokemonID, false, false).sendToServer()
closeGUI()
if (canGiveHeld) {
InteractPokemonPacket(pokemonID, InteractTypePokemon.HELD_ITEM).sendToServer()
closeGUI()
}
}
)
val giveCosmeticItem = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_cosmetic_item.png"),
tooltipText = "cobblemon.ui.interact.give.cosmetic_item",
enabled = canGiveCosmetic,
onPress = {
if (canGiveCosmetic) {
InteractPokemonPacket(pokemonID, InteractTypePokemon.COSMETIC_ITEM).sendToServer()
closeGUI()
}
}
)
val ride = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
iconResource = cobblemonResource("textures/gui/interact/icon_ride.png"),
tooltipText = "cobblemon.ui.interact.ride",
onPress = {
if (canRide) {
InteractPokemonPacket(pokemonID, false, true).sendToServer()
InteractPokemonPacket(pokemonID, InteractTypePokemon.RIDE).sendToServer()
closeGUI()
}
}
)
val options: Multimap<Orientation, InteractWheelOption> = ArrayListMultimap.create()
options.put(Orientation.TOP_RIGHT, giveItem)
if(canRide) {
options.put(Orientation.BOTTOM_LEFT, ride)
}
if (canMountShoulder) {
options.put(Orientation.TOP_LEFT, mountShoulder)
}
CobblemonEvents.POKEMON_INTERACTION_GUI_CREATION.post(PokemonInteractionGUICreationEvent(pokemonID, canMountShoulder, options))
options.put(Orientation.TOP_RIGHT, giveHeldItem)
options.put(Orientation.BOTTOM_RIGHT, giveCosmeticItem)
options.put(Orientation.BOTTOM_LEFT, ride)
options.put(Orientation.TOP_LEFT, mountShoulder)
CobblemonEvents.POKEMON_INTERACTION_GUI_CREATION.post(PokemonInteractionGUICreationEvent(
pokemonID = pokemonID,
mountShoulder = canMountShoulder,
giveHeld = canGiveHeld,
giveCosmetic = canGiveCosmetic,
canRide = canRide,
options = options
))
return InteractWheelGUI(options, Component.translatable("cobblemon.ui.interact.pokemon"))
}
@@ -14,6 +14,7 @@ import org.joml.Vector3f
data class InteractWheelOption(
val iconResource: ResourceLocation,
val secondaryIconResource: ResourceLocation? = null,
val enabled: Boolean = true,
val tooltipText: String?,
val colour: () -> Vector3f? = { null },
val onPress: () -> Unit
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
import java.util.UUID
/**
* Base logic for a [PortraitDrawer] that animates a portrait based on conditions.
*/
abstract class AnimatedPortraitDrawer : PortraitDrawer {
private val state = FloatingState()
private val stateAtIndex = hashMapOf<Int, Pair<UUID, FloatingState>>()
override fun draw(pokemon: Pokemon, poseStack: PoseStack, partialTicks: Float, isSelected: Boolean, index: Int) {
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = poseStack,
partialTicks = this.ticksFor(pokemon, isSelected, partialTicks),
contextScale = pokemon.form.baseScale,
state = this.stateFor(pokemon, isSelected, index)
)
}
protected abstract fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean
protected open fun ticksFor(pokemon: Pokemon, isSelected: Boolean, partialTicks: Float): Float {
return if (this.shouldAnimate(pokemon, isSelected)) partialTicks else 0F
}
protected open fun stateFor(pokemon: Pokemon, isSelected: Boolean, index: Int): FloatingState {
if (!this.shouldAnimate(pokemon, isSelected)) {
return this.state
}
val state = this.stateAtIndex.getOrPut(index) { pokemon.uuid to this.createState(pokemon) }
if (state.first != pokemon.uuid) {
val newState = this.createState(pokemon)
this.stateAtIndex[index] = pokemon.uuid to newState
return newState
}
return state.second
}
protected open fun createState(pokemon: Pokemon): FloatingState {
val state = FloatingState()
state.currentAspects = pokemon.aspects
return state
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.GraphicsStatus
/**
* Draws portraits if the Pokemon is not fainted.
*
* Meant for client with [GraphicsStatus.FABULOUS].
*/
class FullyAnimatedPortraitDrawer : AnimatedPortraitDrawer() {
override fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean {
return !pokemon.isFainted()
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.GraphicsStatus
/**
* The simplest implementation, the Pokemon is drawn staying still.
*
* Meant for client with [GraphicsStatus.FAST].
*/
class InanimatePortraitDrawer : PortraitDrawer {
private val state = FloatingState()
override fun draw(pokemon: Pokemon, poseStack: PoseStack, partialTicks: Float, isSelected: Boolean, index: Int) {
state.currentAspects = pokemon.aspects
drawPosablePortrait(
identifier = pokemon.species.resourceIdentifier,
matrixStack = poseStack,
partialTicks = 0F,
contextScale = pokemon.form.baseScale,
state = this.state
)
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.blaze3d.vertex.PoseStack
/**
* Responsible for drawing a Pokemon portrait.
*
* Keep in mind any scaling and handling of the [PoseStack] must be done on the implementations using this system.
*/
fun interface PortraitDrawer {
/**
* Draws the portrait.
*
* @param pokemon The [Pokemon] being drawn.
* @param poseStack The [PoseStack] being used.
* @param partialTicks The partial ticks of the screen using this.
* @param isSelected If the [pokemon] is considered in a selected state for example hovered or the current party member.
* @param index The index of the [pokemon] in the screen using this.
*/
fun draw(
pokemon: Pokemon,
poseStack: PoseStack,
partialTicks: Float,
isSelected: Boolean,
index: Int
)
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.mojang.serialization.Codec
import net.minecraft.util.StringRepresentable
enum class PortraitStyle : StringRepresentable {
NEVER_ANIMATE,
ANIMATE_SELECTED,
ALWAYS_ANIMATE;
override fun getSerializedName(): String = this.name
companion object {
@JvmStatic
val CODEC: Codec<PortraitStyle> = StringRepresentable.fromEnum(PortraitStyle::values)
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.portrait
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.GraphicsStatus
/**
* Draws portraits if the Pokemon is not fainted and selected.
*
* Meant for client with [GraphicsStatus.FANCY].
*/
class SelectedAnimatedPortraitDrawer : AnimatedPortraitDrawer() {
override fun shouldAnimate(pokemon: Pokemon, isSelected: Boolean): Boolean {
return !pokemon.isFainted() && isSelected
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.summary
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Move
@@ -36,6 +37,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWi
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.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.pokemon.Gender
@@ -89,10 +91,14 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
private val typeSpacerDoubleResource = cobblemonResource("textures/gui/summary/type_spacer_double.png")
private val sideSpacerResource = cobblemonResource("textures/gui/summary/summary_side_spacer.png")
private val evolveButtonResource = cobblemonResource("textures/gui/summary/summary_evolve_button.png")
private val itemVisibleResource = cobblemonResource("textures/gui/summary/item_visible.png")
private val itemHiddenResource = cobblemonResource("textures/gui/summary/item_hidden.png")
private val tabIconInfo = cobblemonResource("textures/gui/summary/summary_tab_icon_info.png")
private val tabIconMoves = cobblemonResource("textures/gui/summary/summary_tab_icon_moves.png")
private val tabIconStats = cobblemonResource("textures/gui/summary/summary_tab_icon_stats.png")
val iconShinyResource = cobblemonResource("textures/gui/summary/icon_shiny.png")
val iconHeldItemResource = cobblemonResource("textures/gui/summary/icon_item_held.png")
val iconCosmeticItemResource = cobblemonResource("textures/gui/summary/icon_item_cosmetic.png")
/**
* Attempts to open this screen for a client.
@@ -120,6 +126,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
private lateinit var modelWidget: ModelWidget
private lateinit var nicknameEntryWidget: NicknameEntryWidget
private val summaryTabs = mutableListOf<SummaryTab>()
private var showCosmeticItem = false
private var mainScreenIndex = INFO
var sideScreenIndex = PARTY
private val party = ArrayList(party)
@@ -168,6 +175,45 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
)
)
//Item Visibility Button
val HeldItemVisibilityButton = SummaryButton(
buttonX = x + 3F,
buttonY = y + 104F,
buttonWidth = 32,
buttonHeight = 32,
scale = 0.5F,
resource = itemVisibleResource,
activeResource = itemHiddenResource,
clickAction = {
selectedPokemon.heldItemVisible = !selectedPokemon.heldItemVisible
(it as SummaryButton).buttonActive = !selectedPokemon.heldItemVisible
modelWidget.heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
// Send item visibility update to server
sendToServer(SetItemHiddenPacket(selectedPokemon.uuid, selectedPokemon.heldItemVisible))
},
renderRequirement = { !selectedPokemon.heldItemNoCopy().isEmpty && !showCosmeticItem },
clickRequirement = { !selectedPokemon.heldItemNoCopy().isEmpty && !showCosmeticItem }
)
addRenderableWidget(HeldItemVisibilityButton)
// Held/Cosmetic Item Button
addRenderableWidget(
SummaryButton(
buttonX = x + 67F,
buttonY = y + 113F,
buttonWidth = 12,
buttonHeight = 12,
scale = 0.5F,
resource = iconCosmeticItemResource,
activeResource = iconHeldItemResource,
clickAction = {
showCosmeticItem = !showCosmeticItem
(it as SummaryButton).buttonActive = showCosmeticItem
}
)
)
// Init Tabs
summaryTabs.clear()
summaryTabs.add(
@@ -237,6 +283,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
rotationY = 325F,
offsetY = -10.0,
shouldFollowCursor = true,
heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
)
addRenderableOnly(this.modelWidget)
}
@@ -278,6 +325,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
if (::modelWidget.isInitialized) {
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
modelWidget.heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
}
if (this::nicknameEntryWidget.isInitialized) {
@@ -541,19 +589,19 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
scale = SCALE
)
// Held Item
val heldItem = selectedPokemon.heldItemNoCopy()
// Held/Cosmetic Item
val displayedItem = if (showCosmeticItem) selectedPokemon.cosmeticItem else selectedPokemon.heldItemNoCopy()
val itemX = x + 3
val itemY = y + 104
if (!heldItem.isEmpty) {
context.renderItem(heldItem, itemX, itemY)
context.renderItemDecorations(Minecraft.getInstance().font, heldItem, itemX, itemY)
if (!displayedItem.isEmpty) {
context.renderItem(displayedItem, itemX, itemY)
context.renderItemDecorations(Minecraft.getInstance().font, displayedItem, itemX, itemY)
}
drawScaledText(
context = context,
text = lang("held_item"),
x = x + 27,
text = lang("${if (showCosmeticItem) "cosmetic" else "held"}_item"),
x = x + 24,
y = y + 114.5,
scale = SCALE
)
@@ -576,14 +624,19 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
scale = SCALE
)
matrices.pushPose()
// Prevent widgets from being overlapped by other components
matrices.translate(0.0, 0.0, 1000.0)
// Render all added Widgets
super.render(context, mouseX, mouseY, delta)
// Render Item Tooltip
if (!heldItem.isEmpty) {
if (!displayedItem.isEmpty) {
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, heldItem, mouseX, mouseY)
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, displayedItem, mouseX, mouseY)
}
matrices.popPose()
}
/**
@@ -38,7 +38,8 @@ class SummaryButton(
private val silent: Boolean = false,
private val boldText: Boolean = true,
private val largeText: Boolean = true,
private val textScale: Float = 1F
private val textScale: Float = 1F,
private val scale: Float = 1F
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), text, clickAction, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
@@ -62,12 +63,13 @@ class SummaryButton(
blitk(
matrixStack = matrices,
texture = if (buttonActive && activeResource != null) activeResource else resource,
x = buttonX,
y = buttonY,
x = buttonX / scale,
y = buttonY / scale,
width = buttonWidth,
height = buttonHeight,
vOffset = if (hoverTexture && isHovered) buttonHeight else 0,
vOffset = if (hoverTexture && isButtonHovered(pMouseX, pMouseY)) buttonHeight else 0,
textureHeight = if (hoverTexture) (buttonHeight.toFloat() * 2) else buttonHeight,
scale = scale
)
// Render Text
@@ -84,7 +86,7 @@ class SummaryButton(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (this.clickRequirement.invoke(this)) {
if (this.clickRequirement.invoke(this) && isButtonHovered(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
@@ -102,4 +104,9 @@ class SummaryButton(
this.buttonX = x
this.buttonY = y
}
fun isButtonHovered(mouseX: Number, mouseY: Number): Boolean {
return mouseX.toFloat() in (buttonX..(buttonX + (buttonWidth.toFloat() * scale)))
&& mouseY.toFloat() in (buttonY..(buttonY + (buttonHeight.toFloat() * scale)))
}
}
@@ -12,10 +12,12 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.calculateHeadYawAndPitch
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import org.joml.Quaternionf
import org.joml.Vector3f
@@ -28,6 +30,7 @@ class ModelWidget(
var offsetY: Double = 0.0,
val playCryOnClick: Boolean = false,
val shouldFollowCursor: Boolean = false,
var heldItem: ItemStack? = null
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Summary - ModelWidget")) {
companion object {
@@ -39,8 +42,11 @@ class ModelWidget(
set (value) {
field = value
currentYawAndPitch = Pair(0f, 0f)
state = FloatingState()
}
private val heldItemRenderer = HeldItemRenderer()
var state = FloatingState()
var lookStartTime: Long? = null
var currentYawAndPitch: Pair<Float, Float> = Pair(0f, 0f)
@@ -106,6 +112,13 @@ class ModelWidget(
headPitch = currentYawAndPitch.second,
)
heldItemRenderer.renderOnModel(
heldItem?: ItemStack.EMPTY,
state.locatorStates,
matrices,
context.bufferSource()
)
matrices.popPose()
context.disableScissor()
@@ -15,6 +15,12 @@ import net.minecraft.client.Minecraft
object InteractPokemonUIPacketHandler: ClientNetworkPacketHandler<InteractPokemonUIPacket> {
override fun handle(packet: InteractPokemonUIPacket, client: Minecraft) {
client.setScreen(createPokemonInteractGui(packet.pokemonID, packet.canMountShoulder, packet.canRide))
client.setScreen(createPokemonInteractGui(
packet.pokemonID,
packet.canMountShoulder,
packet.canGiveHeld,
packet.canGiveCosmetic,
packet.canRide
))
}
}
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.item
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_FACE_ITEMS
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_HAT_ITEMS
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.NullObjectParser
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.item.ItemStack
class HeldItemRenderer() {
private val itemRenderer = Minecraft.getInstance().itemRenderer
private var displayContext = ItemDisplayContext.FIXED
private var scale = 1.0f
fun render(
entity: PokemonEntity?,
item: ItemStack,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
seed: Int
) {
if (item.isEmpty) return
displayContext = ItemDisplayContext.FIXED
poseStack.pushPose()
when {
(locators.containsKey("item_face") && item.`is`(WEARABLE_FACE_ITEMS)) -> {
displayContext = ItemDisplayContext.HEAD
poseStack.mulPose(locators["item_face"]!!.matrix)
poseStack.translate(0f, 0f, .28f * scale)
poseStack.scale(0.7f * scale, 0.7f * scale, 0.7f * scale)
}
(locators.containsKey("item_hat") && item.`is`(WEARABLE_HAT_ITEMS)) -> {
displayContext = ItemDisplayContext.HEAD
poseStack.mulPose(locators["item_hat"]!!.matrix)
poseStack.translate(0f, -0.26f * scale, 0f)
poseStack.scale(.68f * scale, .68f * scale, .68f * scale)
}
(locators.containsKey("item")) -> {
updateModifiers("item",locators)
poseStack.mulPose(locators["item"]!!.matrix)
when(displayContext) {
ItemDisplayContext.FIXED -> {
poseStack.translate(0f, 0.01666f * scale, 0f)
poseStack.scale(.5f * scale, .5f * scale, .5f * scale)
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
}
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND -> {
poseStack.translate(0.075f* scale, 0f * scale, -0.05f* scale)
poseStack.scale(scale, scale, scale)
poseStack.mulPose(Axis.XP.rotationDegrees(-90f))
poseStack.mulPose(Axis.YP.rotationDegrees(-90f))
}
else -> {
poseStack.scale(scale, scale, scale)
}
}
}
else -> { // Don't render any item
poseStack.popPose()
return
}
}
itemRenderer.renderStatic(entity, item, displayContext, false, poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
poseStack.popPose()
}
fun renderOnModel(
item: ItemStack,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int = LightTexture.pack(11, 7)
){
if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) render(null, item, locators, poseStack, buffer, light, 0)
}
fun renderOnEntity(
entity: PokemonEntity,
delegate: PokemonClientDelegate,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int
) {
val shownItem = entity.shownItem
val locators: Map<String, MatrixWrapper> = delegate.locatorStates
render(entity, shownItem, locators, poseStack, buffer, light, 0)
}
private fun updateModifiers(name: String, locators: Map<String, MatrixWrapper>) {
locators.forEach { (locator: String, m: MatrixWrapper) ->
val modifiers: Map<String, Float>
if (locator.startsWith("_null_$name[")) {
modifiers = NullObjectParser.parseNullObject(locator).modifiers
if (modifiers.containsKey("scale")) scale = modifiers["scale"]!!
if (modifiers.containsKey("mode")) displayContext = ItemDisplayContext.entries[((modifiers["mode"]!!).toInt()) % 9]
return
}
}
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.render.layer
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
@@ -35,6 +36,7 @@ import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>) : RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
@@ -42,6 +44,8 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
}
private val heldItemRenderer = HeldItemRenderer()
var leftState = FloatingState()
var lastRenderedLeft: ShoulderData? = null
var rightState = FloatingState()
@@ -155,6 +159,14 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
model.withLayerContext(buffer, state, VaryingModelRepository.getLayers(shoulderData.species.resourceIdentifier, state)) {
model.render(context, matrixStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
}
heldItemRenderer.renderOnModel(
shoulderData.shownItem,
state.locatorStates,
matrixStack,
buffer
)
model.setDefault()
matrixStack.popPose()
livingEntity.level().profiler.pop()
@@ -177,7 +189,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
if (!shoulderNbt.contains(DataKeys.SHOULDER_SPECIES)) {
return Pokemon.CLIENT_CODEC.decode(NbtOps.INSTANCE, shoulderNbt.getCompound(DataKeys.POKEMON))
.map { it.first }
.mapOrElse({ ShoulderData(pokemonUUID, it.species, it.form, it.aspects, it.scaleModifier) }, { null })
.mapOrElse({ ShoulderData(pokemonUUID, it.species, it.form, it.aspects, it.scaleModifier, it.heldItem) }, { null })
}
val species = PokemonSpecies.getByIdentifier(ResourceLocation.parse(shoulderNbt.getString(DataKeys.SHOULDER_SPECIES)))
?: return null
@@ -185,7 +197,10 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
val form = species.forms.firstOrNull { it.name == formName } ?: species.standardForm
val aspects = shoulderNbt.getList(DataKeys.SHOULDER_ASPECTS, Tag.TAG_STRING.toInt()).map { it.asString }.toSet()
val scaleModifier = shoulderNbt.getFloat(DataKeys.SHOULDER_SCALE_MODIFIER)
return ShoulderData(pokemonUUID, species, form, aspects, scaleModifier)
val shownItem = Minecraft.getInstance().level?.registryAccess()
?.let { ItemStack.parseOptional(it, shoulderNbt.getCompound(DataKeys.SHOULDER_ITEM)) }
?: ItemStack.EMPTY
return ShoulderData(pokemonUUID, species, form, aspects, scaleModifier, shownItem)
}
private data class ShoulderCache(
@@ -198,7 +213,8 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
val species: Species,
val form: FormData,
val aspects: Set<String>,
val scaleModifier: Float
val scaleModifier: Float,
val shownItem: ItemStack
)
companion object {
@@ -69,11 +69,11 @@ class LocatorAccess(
* Updates all of the locator states with the position at this current frame.
* This is the same logic as ModelPart uses, that's why we reuse ModelPart#rotate.
*/
fun update(matrixStack: PoseStack, entity: Entity, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
fun update(matrixStack: PoseStack, entity: Entity?, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
matrixStack.pushPose()
joint.transform(matrixStack)
if (isRoot) {
if (isRoot && null!=entity) {
matrixStack.pushPose()
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.last().pose())
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench
/**
* Uses the name of null objects in a model to get modifier values for a locator.
* @author Josh
*/
object NullObjectParser {
fun parseNullObject(input: String): NullObjectModifier {
// Extract modifiedLocator
val modifiedLocator = input.replaceFirst("^_null_".toRegex(), "").replace("\\[.*\\]$".toRegex(), "")
// Extract modifiers
val modifiers: MutableMap<String, Float> = HashMap()
val start = input.indexOf('[')
val end = input.indexOf(']')
if (start != -1 && end != -1 && end > start) {
val modifierPart = input.substring(start + 1, end)
val modifierPairs = modifierPart.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (pair in modifierPairs) {
val keyValue = pair.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (keyValue.size == 2) {
val key = keyValue[0].trim { it <= ' ' }
try {
val value = keyValue[1].trim { it <= ' ' }.toFloat()
modifiers[key] = value
} catch (e: NumberFormatException) {
System.err.println("Invalid number format for: " + keyValue[1])
}
}
}
}
return NullObjectModifier(modifiedLocator, modifiers)
}
/**
* Stores modifiers for a particular locator.
*/
class NullObjectModifier(val locatorName: String, val modifiers: Map<String, Float>)
}
@@ -676,7 +676,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
* found and used from other client-side systems.
*/
fun updateLocators(entity: Entity?, state: PosableState) {
entity ?: return
val matrixStack = PoseStack()
var scale = 1F
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class WhiscashModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("whiscash")
override var portraitScale = 0.8F
override var portraitTranslation = Vec3(-0.35, 0.4, 0.0)
override var profileScale = 0.6F
override var profileTranslation = Vec3(-0.1, 0.6, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("whiscash", "cry") }
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
animations = arrayOf(
bedrock("whiscash", "water_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
bedrock("whiscash", "water_idle")
)
)
}
}
@@ -533,6 +533,7 @@ object VaryingModelRepository {
inbuilt("spewpa", ::SpewpaModel)
inbuilt("vivillon", ::VivillonModel)
inbuilt("barboach", ::BarboachModel)
inbuilt("whiscash", ::WhiscashModel)
inbuilt("combee", ::CombeeModel)
inbuilt("vespiquen", ::VespiquenModel)
inbuilt("lillipup", ::LillipupModel)
@@ -45,14 +45,13 @@ object MountedPlayerRenderer {
val locatorOffset = locator.matrix.getTranslation(Vector3f())
val center = Vector3f(0f, entity.bbHeight/2, 0f)
var locatorOffsetToCenter = locatorOffset.sub(center, Vector3f())
//locatorOffsetToCenter.y -= 0.5f
val locatorOffsetToCenter = locatorOffset.sub(center, Vector3f())
val transformationMatrix = Matrix4f()
if (player is Rollable && player.shouldRoll()){
//if (player is Rollable && player.shouldRoll()){
//transformationMatrix.rotate(Axis.YP.rotationDegrees(-yBodyRot))
//transformationMatrix.mulLocal(player.orientation)
}
//}
val rotatedOffset = transformationMatrix.transformPosition(locatorOffsetToCenter, Vector3f()).add(center).sub(Vector3f(0f, player.bbHeight/2, 0f))
matrix.translate(rotatedOffset)
@@ -114,7 +113,6 @@ object MountedPlayerRenderer {
state.runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing", DoubleValue(limbSwing.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing_amount", DoubleValue(limbSwingAmount.toDouble()))
state.runtime.environment.setSimpleVariable("velocity", DoubleValue(pokemonEntity.deltaMovement.horizontalDistance()))
// TODO add the q.player reference, requires a cached thing or something on the client idk
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BE
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BEAM_SHRINK_TIME
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PosablePokemonEntityModel
@@ -85,6 +86,8 @@ class PokemonRenderer(
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
}
private val heldItemRenderer = HeldItemRenderer()
override fun getTextureLocation(entity: PokemonEntity): ResourceLocation {
return VaryingModelRepository.getTexture(entity.pokemon.species.resourceIdentifier, entity.delegate as PokemonClientDelegate)
}
@@ -153,6 +156,15 @@ class PokemonRenderer(
this.renderNameTag(entity, entity.effectiveName(), poseMatrix, buffer, packedLight, partialTicks)
}
// Minecraft.getInstance().bufferBuilders.entityVertexConsumers.draw()
//Render Held Item
heldItemRenderer.renderOnEntity(
entity,
clientDelegate,
poseMatrix,
buffer,
packedLight
)
}
fun renderRiding(
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.config
import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.client.gui.portrait.PortraitStyle
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.pokeball.catching.calculators.CobblemonCaptureCalculator
import com.cobblemon.mod.common.util.adapters.CaptureCalculatorAdapter
@@ -255,6 +256,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Interface, lang = "summary_pokemon_follow_cursor")
var summaryPokemonFollowCursor = true
@CobblemonConfigField(Category.Interface, lang = "party_portrait_animations")
var partyPortraitAnimations = PortraitStyle.NEVER_ANIMATE
@CobblemonConfigField(Category.Debug, lang = "enable_debug_keys")
var enableDebugKeys = false
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonFlows
import com.cobblemon.mod.common.CobblemonBrainConfigs
import com.cobblemon.mod.common.CobblemonMechanics
@@ -92,6 +93,7 @@ object CobblemonDataProvider : DataProvider {
this.register(DexEntryAdditions)
this.register(Dexes)
this.register(DexAdditions)
this.register(CobblemonCosmeticItems)
this.register(CobblemonFlows)
CobblemonSpawnPools.load()
@@ -9,16 +9,7 @@
package com.cobblemon.mod.common.datafixer
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.datafixer.fix.BlockPosUpdateFix
import com.cobblemon.mod.common.datafixer.fix.EvolutionProxyNestingFix
import com.cobblemon.mod.common.datafixer.fix.FeatureFix
import com.cobblemon.mod.common.datafixer.fix.IvEvToIdentifierFix
import com.cobblemon.mod.common.datafixer.fix.MovesetJsonFix
import com.cobblemon.mod.common.datafixer.fix.NicknameFix
import com.cobblemon.mod.common.datafixer.fix.RaisedPPStagesFix
import com.cobblemon.mod.common.datafixer.fix.ShoulderStateJsonFix
import com.cobblemon.mod.common.datafixer.fix.TeraTypeFix
import com.cobblemon.mod.common.datafixer.fix.TradeableMissingFix
import com.cobblemon.mod.common.datafixer.fix.*
import com.cobblemon.mod.common.datafixer.schema.CobblemonRootSchema
import com.cobblemon.mod.common.datafixer.schema.CobblemonSchemaV2
import com.google.common.util.concurrent.ThreadFactoryBuilder
@@ -26,7 +26,6 @@ enum class PoseType {
FLY,
FLOAT,
SWIM,
GLIDE,
/** A pose for rendering on the left shoulder. Pose animations are given the player head yaw, pitch, and ageInTicks. */
SHOULDER_LEFT,
/** A pose for rendering on the right shoulder. Pose animations are given the player head yaw, pitch, and ageInTicks. */
@@ -42,7 +41,7 @@ enum class PoseType {
companion object {
val ALL_POSES = EnumSet.allOf(PoseType::class.java)
val FLYING_POSES = EnumSet.of(FLY, HOVER, GLIDE)
val FLYING_POSES = EnumSet.of(FLY, HOVER)
val SWIMMING_POSES = EnumSet.of(SWIM, FLOAT)
val STANDING_POSES = EnumSet.of(STAND, WALK)
val SHOULDER_POSES = EnumSet.of(SHOULDER_LEFT, SHOULDER_RIGHT)
@@ -10,11 +10,15 @@ package com.cobblemon.mod.common.entity.boat
import com.cobblemon.mod.common.CobblemonEntities
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceKey
import net.minecraft.world.Containers
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.HasCustomInventoryScreen
import net.minecraft.world.entity.SlotAccess
import net.minecraft.world.entity.monster.piglin.PiglinAi
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
@@ -52,6 +56,31 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
}
}
override fun getMaxPassengers() = 1
override fun addAdditionalSaveData(compound: CompoundTag) {
super.addAdditionalSaveData(compound)
this.addChestVehicleSaveData(compound, this.registryAccess())
}
override fun readAdditionalSaveData(compound: CompoundTag) {
super.readAdditionalSaveData(compound)
this.readChestVehicleSaveData(compound, this.registryAccess())
}
public override fun destroy(source: DamageSource) {
this.destroy(this.getDropItem())
this.chestVehicleDestroyed(source, this.level(), this)
}
override fun remove(reason: RemovalReason) {
if (!this.level().isClientSide && reason.shouldDestroy()) {
Containers.dropContents(this.level(), this, this)
}
super.remove(reason)
}
override fun interact(player: Player, hand: InteractionHand): InteractionResult {
if (!player.isSecondaryUseActive) {
val interactionResult = super.interact(player, hand)
@@ -86,6 +115,8 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
override fun setItem(slot: Int, stack: ItemStack) = this.setChestVehicleItem(slot, stack)
override fun getSlot(slot: Int): SlotAccess? = this.getChestVehicleSlot(slot)
override fun setChanged() {}
override fun stillValid(player: Player): Boolean = this.isChestVehicleStillValid(player)
@@ -120,6 +151,10 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
private fun emptyInventory(): NonNullList<ItemStack> = NonNullList.withSize(INVENTORY_SLOTS, ItemStack.EMPTY)
override fun stopOpen(player: Player) {
this.level().gameEvent(GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of(player))
}
companion object {
private const val INVENTORY_SLOTS = 27
@@ -103,7 +103,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
var capturingPokemon: PokemonEntity? = null
val captureFuture = CompletableFuture<Boolean>()
var captureState: CaptureState
get() = CaptureState.values()[entityData.get(CAPTURE_STATE).toInt()]
get() = CaptureState.entries[entityData.get(CAPTURE_STATE).toInt()]
set(value) { entityData.set(CAPTURE_STATE, value.ordinal.toByte()) }
var aspects: Set<String>
get() = entityData.get(ASPECTS)
+66 -177
View File
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.entity.PokemonSender
@@ -34,10 +36,10 @@ import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.*
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.api.riding.RidingManager
import com.cobblemon.mod.common.api.riding.Seat
import com.cobblemon.mod.common.api.riding.events.SelectDriverEvent
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinitions
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnServer
@@ -80,8 +82,6 @@ import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.feature.StashHandler
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MAXTOPSPEED
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MINTOPSPEED
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -121,7 +121,15 @@ import net.minecraft.world.InteractionResult
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.*
import net.minecraft.world.entity.AgeableMob
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Pose
import net.minecraft.world.entity.Shearable
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.ai.control.MoveControl
@@ -143,7 +151,6 @@ import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Vector3f
import java.util.*
@Suppress("unused")
open class PokemonEntity(
@@ -174,6 +181,7 @@ open class PokemonEntity(
@JvmStatic val FREEZE_FRAME = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.FLOAT)
@JvmStatic val CAUGHT_BALL = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
@JvmStatic val EVOLUTION_STARTED = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
@JvmStatic var SHOWN_HELD_ITEM = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.ITEM_STACK)
const val BATTLE_LOCK = "battle"
const val EVOLUTION_LOCK = "evolving"
@@ -230,15 +238,16 @@ open class PokemonEntity(
get() = entityData.get(FRIENDSHIP)
val seats: List<Seat>
get() = form.riding.seats
val rideProp: RidingProperties
get() = form.riding
var shownItem: ItemStack
get() = entityData.get(SHOWN_HELD_ITEM)
set(value) = entityData.set(SHOWN_HELD_ITEM, value)
var drops: DropTable? = null
var tethering: PokemonPastureBlockEntity.Tethering? = null
// TODO review if we still want this
var queuedToDespawn = false
var enablePoseTypeRecalculation = true
@@ -302,7 +311,6 @@ open class PokemonEntity(
.addPokemonEntityFunctions(this)
}
init {
delegate.initialize(this)
delegate.changePokemon(pokemon)
@@ -334,6 +342,7 @@ open class PokemonEntity(
builder.define(FREEZE_FRAME, -1F)
builder.define(CAUGHT_BALL, "")
builder.define(EVOLUTION_STARTED, false)
builder.define(SHOWN_HELD_ITEM, ItemStack.EMPTY)
}
override fun onSyncedDataUpdated(data: EntityDataAccessor<*>) {
@@ -934,10 +943,15 @@ open class PokemonEntity(
}
if (hand == InteractionHand.MAIN_HAND && player is ServerPlayer && pokemon.getOwnerPlayer() == player) {
val cosmeticItemDefinition = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(player.level().registryAccess(), pokemon, itemStack)
if (player.isShiftKeyDown) {
InteractPokemonUIPacket(this.getUUID(), canSitOnShoulder() && pokemon in player.party(), this.canRide(player)).sendToPlayer(
player
)
InteractPokemonUIPacket(
this.getUUID(),
canSitOnShoulder() && pokemon in player.party(),
!(pokemon.heldItemNoCopy().isEmpty && itemStack.isEmpty),
(!pokemon.cosmeticItem.isEmpty && itemStack.isEmpty) || cosmeticItemDefinition != null,
this.canRide(player) && pokemon.riding.canRide
).sendToPlayer(player)
} else {
// TODO #105
if (this.attemptItemInteraction(player, player.getItemInHand(hand))) return InteractionResult.SUCCESS
@@ -1106,37 +1120,56 @@ open class PokemonEntity(
}
fun offerHeldItem(player: Player, stack: ItemStack): Boolean {
return offerItem(player, stack, isCosmetic = false)
}
fun offerCosmeticItem(player: Player, stack: ItemStack): Boolean {
return offerItem(player, stack, isCosmetic = true)
}
fun offerItem(
player: Player,
stack: ItemStack,
isCosmetic: Boolean
): Boolean {
if (player !is ServerPlayer || this.isBusy || this.pokemon.getOwnerPlayer() != player) {
return false
}
if (!stack.isEmpty && (isBlacklisted(stack) || !isWhitelisted(stack))) {
if (!stack.isEmpty && !isCosmetic && (isBlacklisted(stack) || !isWhitelisted(stack))) {
player.sendSystemMessage(lang("held_item.forbidden", stack.hoverName, this.pokemon.getDisplayName()))
return false
}
// We want the count of 1 in order to match the ItemStack#areEqual
val possibleReturn = if (isCosmetic) this.pokemon.cosmeticItem.copy() else this.pokemon.heldItemNoCopy()
val giving = stack.copy().apply { count = 1 }
val possibleReturn = this.pokemon.heldItemNoCopy()
if (stack.isEmpty && possibleReturn.isEmpty) {
if (ItemStack.isSameItem(giving, possibleReturn)) {
val message = if (isCosmetic) {
lang("cosmetic_item.already_wearing", this.pokemon.getDisplayName(), stack.hoverName)
} else {
lang("held_item.already_holding", this.pokemon.getDisplayName(), stack.hoverName)
}
player.sendSystemMessage(message)
return false
}
if (ItemStack.isSameItem(giving, possibleReturn)) {
player.sendSystemMessage(lang("held_item.already_holding", this.pokemon.getDisplayName(), stack.hoverName))
return true
val returned = if (isCosmetic) {
this.pokemon.swapCosmeticItem(stack = stack, decrement = !player.isCreative)
} else {
this.pokemon.swapHeldItem(stack = stack, decrement = !player.isCreative)
}
val returned = this.pokemon.swapHeldItem(stack = stack, decrement = !player.isCreative)
val text = when {
giving.isEmpty -> lang("held_item.take", returned.hoverName, this.pokemon.getDisplayName())
returned.isEmpty -> lang("held_item.give", this.pokemon.getDisplayName(), giving.hoverName)
else -> lang("held_item.replace", returned.hoverName, this.pokemon.getDisplayName(), giving.hoverName)
isCosmetic && giving.isEmpty -> lang("cosmetic_item.take", returned.hoverName, this.pokemon.getDisplayName())
isCosmetic && returned.isEmpty -> lang("cosmetic_item.give", this.pokemon.getDisplayName(), returned.hoverName)
!isCosmetic && giving.isEmpty -> lang("held_item.take", returned.hoverName, this.pokemon.getDisplayName())
!isCosmetic && returned.isEmpty -> lang("held_item.give", this.pokemon.getDisplayName(), returned.hoverName)
isCosmetic -> lang("cosmetic_item.replace", returned.hoverName, this.pokemon.getDisplayName(), returned.hoverName)
else -> lang("held_item.replace", returned.hoverName, this.pokemon.getDisplayName(), returned.hoverName)
}
player.giveOrDropItemStack(returned)
player.sendSystemMessage(text)
this.level().playSoundServer(
position = this.position(),
sound = SoundEvents.ITEM_PICKUP,
@@ -1215,6 +1248,7 @@ open class PokemonEntity(
nbt.putString(DataKeys.SHOULDER_FORM, this.pokemon.form.name)
nbt.put(DataKeys.SHOULDER_ASPECTS, this.pokemon.aspects.map(StringTag::valueOf).toNbtList())
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, this.pokemon.scaleModifier)
nbt.put(DataKeys.SHOULDER_ITEM, this.level().registryAccess().let { if (this.shownItem.isEmpty) CompoundTag() else this.shownItem.saveOptional(it) } as CompoundTag)
if (isLeft) player.shoulderEntityLeft = nbt else player.shoulderEntityRight = nbt
return true
}
@@ -1376,75 +1410,10 @@ open class PokemonEntity(
}
}
//Converted to kotin from the same named private function in entity.
private fun getInputVector(relative: Vec3, motionScaler: Float, facing: Float): Vec3 {
val d = relative.lengthSqr()
if (d < 1.0E-7) {
return Vec3.ZERO
}
// If the squared length is greater than 1, normalize; otherwise, use the vector as-is.
// May not want to normalize this
//val vec3 = (if (d > 1.0) relative.normalize() else relative).scale(motionScaler.toDouble())
val vec3 = relative
// Convert the facing angle (in degrees) to radians using the factor 0.017453292F.
val f = Mth.sin(facing * 0.017453292F)
val g = Mth.cos(facing * 0.017453292F)
// Rotate the vector by the facing angle.
return Vec3(
vec3.x * g - vec3.z * f,
vec3.y,
vec3.z * g + vec3.x * f
)
}
//override fun moveRelative(amount: Float, relative: Vec3 ) {
//super.moveRelative(amount, relative)
//val v = this.getInputVector(relative, 1.0f, this.getYRot());
//this.setDeltaMovement(v);
//}
override fun handleRelativeFrictionAndCalculateMovement(deltaMovement: Vec3 , friction: Float ): Vec3 {
val riders = this.passengers.filterIsInstance<LivingEntity>()
if ( riders.isEmpty() ) {
super.handleRelativeFrictionAndCalculateMovement(deltaMovement, friction)
}
//ignore this for now
else if( false )
{
val v = this.getInputVector(deltaMovement, 1.0f, this.getYRot());
//changing this will give the ride more or less inertia/handling/drift?
//A bit of overlap with other things but.
this.setDeltaMovement(this.deltaMovement.lerp(v, riding.inertia(this)));
//this.setDeltaMovement(this.deltaMovement.scale(this.speed.toDouble()));
//maybe move this to the travel function. The jump below never gets used.
this.move( MoverType.SELF, this.deltaMovement );
}
return this.deltaMovement
}
override fun travel(movementInput: Vec3) {
val prevBlockPos = this.blockPosition()
if (beamMode != 3) { // Don't let Pokémon move during recall
//this.deltaMovement = movementInput
//val riders = this.passengers.filterIsInstance<LivingEntity>()
//if ( !riders.isEmpty() ) {
//if ( false ) {
//val v = this.handleRelativeFrictionAndCalculateMovement(movementInput, 0.6f)
//this.moveRelative(this.speed, v);
//}
val riders = this.passengers.filterIsInstance<LivingEntity>()
if ( riders.isEmpty() ) {
super.travel(movementInput)
}
//super.travel(movementInput)
super.travel(movementInput)
this.updateBlocksTraveled(prevBlockPos)
}
if (isBattling && this.isInWater) {
@@ -1496,7 +1465,7 @@ open class PokemonEntity(
/**
* If this Pokémon has a nickname, then the nickname is returned.
* Otherwise, [getDefaultName] is returned
* Otherwise, [getDisplayName] is returned
*
* @return The current display name of this entity.
*/
@@ -1635,21 +1604,11 @@ open class PokemonEntity(
}
override fun stopSeenByPlayer(player: ServerPlayer) {
if (player == null) {
return
}
if (this.ownerUUID == player.uuid && tethering == null) {
queuedToDespawn = true
// queuedToDespawn = true
this.remove(RemovalReason.DISCARDED)
return
}
//
// val chunkPos = ChunkPos(BlockPos(x.toInt(), y.toInt(), z.toInt()))
// (world as ServerWorld).chunkManager
// .addTicket(ChunkTicketType.POST_TELEPORT, chunkPos, 0, id)
// this.goalSelector.tick()
// if(distanceTo(player.blockPos) > 100) pokemon.recall()
// }
}
override fun canBeLeashed() = true
@@ -1678,17 +1637,6 @@ open class PokemonEntity(
return platform == PlatformType.NONE && super.canRide(entity)
}
// Takes in a requested stat type with a base minimum and base maximum and returns the interpolated
// stat based on the boost of that pokemons stat
fun getRideStat( stat: RidingStat, style: RidingStyle, baseMin: Double, baseMax: Double ): Double {
//Currenlty zero boost since aprijuice isn't implemented yet
val stat = this.rideProp.calculate(stat, style, 0 )
val statVal = (((baseMax - baseMin) / 100) * stat) + baseMin
return statVal
}
override fun canAddPassenger(passenger: Entity): Boolean {
return passengers.size < seats.size
}
@@ -1698,11 +1646,6 @@ open class PokemonEntity(
jumping = value
}
// override fun moveRelative(amount: Float, relative: Vec3) {
// val vec3 = this.getInputVector(relative, amount, this.yRot)
// this.deltaMovement = deltaMovement.add(vec3)
// }
override fun tickRidden(driver: Player, movementInput: Vec3) {
super.tickRidden(driver, movementInput)
this.riding.tick(this, driver, movementInput)
@@ -1712,23 +1655,6 @@ open class PokemonEntity(
this.yBodyRot = this.yRot
this.yRotO = this.yRot
val riders = this.passengers.filterIsInstance<LivingEntity>()
//These are the only functions that matter and I have isolated them.
//I think using speed to scale the movement vector will not be working.
if ( !riders.isEmpty() ) {
val v = this.getInputVector(riding.velocity(this, driver, Vec3.ZERO), 1.0f, this.getYRot());
//changing this will give the ride more or less inertia/handling/drift?
//A bit of overlap with other things but.
this.setDeltaMovement(this.deltaMovement.lerp(v, riding.inertia(this)));
//this.setDeltaMovement(this.deltaMovement.scale(this.speed.toDouble()));
//maybe move this to the travel function. The jump below never gets used.
this.move( MoverType.SELF, this.deltaMovement );
}
if (this.riding.canJump(this, driver)) {
if (this.onGround()) {
if (this.jumpInputStrength > 0) {
@@ -1881,45 +1807,8 @@ open class PokemonEntity(
// Set back to land pose type?
}
/*
These two functions (fluids and ground) need to become riding configurable and
dependent. Also, the onGround() function seems to affect quite a few spots in
code and would likely need to be changed to be something more robust instead of
just overriding this method.
*/
override fun isAffectedByFluids(): Boolean {
var fluidAffected = true
if( this.hasControllingPassenger() )
{
//Change this so it calls something from the controller to check
//if the specific controller wants to ignore fluid physics since
//not every single one will want to
fluidAffected = false
}
return fluidAffected
}
//this seems a bit hacky to me seeing as how many spots in the base classes its used.
//However, there are odd interactions with the controllers when they are meant to be
//flying or swimming but they are touching the ground and this needs to be prevented.
//Having it be able to be turned off by the flying or swimming controllers is the
//temp solution I have found.
override fun onGround() : Boolean
{
if( riding.turnOffOnGround( this ) )
{
return false
}
return super.onGround()
}
//I think already mentioned but should maybe be riding controller configurable
override fun dismountsUnderwater(): Boolean {
return false
return true
}
override fun getDefaultGravity(): Double {
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.entity.PlatformType
import com.cobblemon.mod.common.entity.PoseType
@@ -122,6 +123,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
entity.entityData.set(PokemonEntity.FRIENDSHIP, entity.pokemon.friendship)
entity.entityData.set(PokemonEntity.CAUGHT_BALL, trackedBall)
updateShownItem()
updatePoseType()
}
@@ -198,6 +200,21 @@ class PokemonServerDelegate : PokemonSideDelegate {
updateTrackedValues()
}
fun updateShownItem() {
val trackedShownItem = when {
// Show Hand Item if Held item is hidden
!entity.pokemon.heldItemVisible -> entity.mainHandItem
// Show Held Item unless it is empty
else -> (entity as PokemonEntity?)?.pokemon?.heldItemNoCopy()?.takeUnless { it.isEmpty }
// Show Hand Item if Held item is empty
?: entity.mainHandItem
}.copy()
/* Hide items tagged as hidden (If the item is in this list, it will not render) */
.let { if (it.`is`(CobblemonItemTags.HIDDEN_ITEMS)) ItemStack.EMPTY else it}
entity.entityData.set(PokemonEntity.SHOWN_HELD_ITEM, trackedShownItem)
}
fun updatePoseType() {
if (!entity.enablePoseTypeRecalculation || entity.passengers.isNotEmpty()) {
return
@@ -19,7 +19,7 @@ object FlowHandler {
fun setup() {
CobblemonEvents.POKEMON_CAPTURED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_captured"), it.context) }
CobblemonEvents.BATTLE_VICTORY.subscribe { CobblemonFlows.run(cobblemonResource("battle_victory"), it.context) }
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.subscribe { CobblemonFlows.run(cobblemonResource("pokedex_data_changed_pre"), it.getContext(), it) }
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.subscribe { CobblemonFlows.run(cobblemonResource("pokedex_data_changed_pre"), it.getContext(), it.functions) }
CobblemonEvents.POKEDEX_DATA_CHANGED_POST.subscribe { CobblemonFlows.run(cobblemonResource("pokedex_data_changed_post"), it.getContext()) }
CobblemonEvents.FORME_CHANGE.subscribe { CobblemonFlows.run(cobblemonResource("forme_change"), it.context) }
CobblemonEvents.MEGA_EVOLUTION.subscribe { CobblemonFlows.run(cobblemonResource("mega_evolution"), it.context) }
@@ -27,13 +27,17 @@ object FlowHandler {
CobblemonEvents.TERASTALLIZATION.subscribe { CobblemonFlows.run(cobblemonResource("terastallization"), it.context) }
CobblemonEvents.BATTLE_FAINTED.subscribe { CobblemonFlows.run(cobblemonResource("battle_fainted"), it.structContext) }
CobblemonEvents.BATTLE_FLED.subscribe { CobblemonFlows.run(cobblemonResource("battle_fled"), it.context) }
CobblemonEvents.BATTLE_STARTED_PRE.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_pre"), it.context, it) }
CobblemonEvents.BATTLE_STARTED_PRE.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_pre"), it.context, it.functions) }
CobblemonEvents.BATTLE_STARTED_POST.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_post"), it.context) }
CobblemonEvents.APRICORN_HARVESTED.subscribe { CobblemonFlows.run(cobblemonResource("apricorn_harvested"), it.context) }
CobblemonEvents.THROWN_POKEBALL_HIT.subscribe { CobblemonFlows.run(cobblemonResource("thrown_pokeball_hit"), it.context) }
CobblemonEvents.LEVEL_UP_EVENT.subscribe { CobblemonFlows.run(cobblemonResource("level_up"), it.context) }
CobblemonEvents.LEVEL_UP_EVENT.subscribe { CobblemonFlows.run(cobblemonResource("level_up"), it.context, it.functions) }
CobblemonEvents.POKEMON_FAINTED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_fainted"), it.context) }
CobblemonEvents.POKEMON_GAINED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_gained"), it.context) }
CobblemonEvents.POKEMON_GAINED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_gained"), it.context, it.functions) }
CobblemonEvents.POKE_BALL_CAPTURE_CALCULATED.subscribe { CobblemonFlows.run(cobblemonResource("poke_ball_capture_calculated"), it.context, it.functions) }
CobblemonEvents.EVOLUTION_TESTED.subscribe { CobblemonFlows.run(cobblemonResource("evolution_tested"), it.context, it.functions) }
CobblemonEvents.EVOLUTION_ACCEPTED.subscribe { CobblemonFlows.run(cobblemonResource("evolution_accepted"), it.context, it.functions) }
CobblemonEvents.EVOLUTION_COMPLETE.subscribe { CobblemonFlows.run(cobblemonResource("evolution_completed"), it.context) }
}
}
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.gametest
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.api.pokemon.PokemonProperties.Companion.parse
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
class CobblemonGameTests {
@GameTest(template = "")
fun spawnBidoof(context: GameTestHelper) {
context.succeedWhen {
val bidoof = parse("bidoof").create()
bidoof.ivs[Stats.ATTACK] = 31
val target = BlockPos(context.relativePos(BlockPos(0, 0, 0)))
context.assertEntityPresent(CobblemonEntities.POKEMON)
context.assertEntityData(target, CobblemonEntities.POKEMON, PokemonEntity::pokemon, bidoof)
}
}
}
@@ -64,7 +64,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -48,7 +48,7 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
val currentStatus = pokemon.status?.status
return if (currentStatus != null && (status.isEmpty() || currentStatus in status)) {
pokemon.status = null
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
pokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
if (!player.isCreative) {
stack.shrink(1)
}
@@ -60,7 +60,7 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -27,7 +27,7 @@ class PlayerInteractOptionsPacket(
val targetId: UUID,
val numericTargetId: Int,
val selectedPokemonId: UUID,
) : NetworkPacket<PlayerInteractOptionsPacket> {
) : NetworkPacket<PlayerInteractOptionsPacket> {
companion object {
val ID = cobblemonResource("player_interactions")
fun decode(buffer: RegistryFriendlyByteBuf) = PlayerInteractOptionsPacket(
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.pokemon.cosmetic.CosmeticItemAssignment
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class CosmeticItemAssignmentSyncPacket(
assignments: Collection<CosmeticItemAssignment>
) : DataRegistrySyncPacket<CosmeticItemAssignment, CosmeticItemAssignmentSyncPacket>(assignments) {
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: CosmeticItemAssignment) {
CosmeticItemAssignment.PACKET_CODEC.encode(buffer, entry)
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): CosmeticItemAssignment? {
return CosmeticItemAssignment.PACKET_CODEC.decode(buffer)
}
override fun synchronizeDecoded(entries: Collection<CosmeticItemAssignment>) {
CobblemonCosmeticItems.reload(entries.associateBy { it.id })
}
companion object {
val ID = cobblemonResource("cosmetic_item_assignment_sync")
fun decode(buffer: RegistryFriendlyByteBuf) = CosmeticItemAssignmentSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Matrix3f
class S2CUpdateOrientationPacket internal constructor(val entityId: Int, val orientation: Matrix3f?) : NetworkPacket<S2CUpdateOrientationPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeInt(entityId)
buffer.writeBoolean(orientation != null)
if (orientation != null) {
buffer.writeMatrix3f(orientation)
}
}
companion object {
val ID = cobblemonResource("s2c_update_orientation")
fun decode(buffer: RegistryFriendlyByteBuf): S2CUpdateOrientationPacket {
val entityId = buffer.readInt()
val orientation = if (buffer.readBoolean()) buffer.readMatrix3f() else null
return S2CUpdateOrientationPacket(entityId, orientation)
}
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.pokemon.update
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readItemStack
import com.cobblemon.mod.common.util.writeItemStack
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.item.ItemStack
class CosmeticItemUpdatePacket(pokemon: () -> Pokemon, value: ItemStack) : SingleUpdatePacket<ItemStack, CosmeticItemUpdatePacket>(pokemon, value) {
override val id = ID
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
buffer.writeItemStack(this.value)
}
override fun set(pokemon: Pokemon, value: ItemStack) {
pokemon.cosmeticItem = value
}
companion object {
val ID = cobblemonResource("cosmetic_item_update")
fun decode(buffer: RegistryFriendlyByteBuf): CosmeticItemUpdatePacket {
val pokemon = decodePokemon(buffer)
val cosmeticItem = buffer.readItemStack()
return CosmeticItemUpdatePacket(pokemon, cosmeticItem)
}
}
}
@@ -24,18 +24,32 @@ import java.util.UUID
* @author Village
* @since January 7th, 2023
*/
class InteractPokemonUIPacket(val pokemonID: UUID, val canMountShoulder: Boolean, val canRide: Boolean): NetworkPacket<InteractPokemonUIPacket> {
class InteractPokemonUIPacket(
val pokemonID: UUID,
val canMountShoulder: Boolean,
val canGiveHeld: Boolean,
val canGiveCosmetic: Boolean,
val canRide: Boolean
): NetworkPacket<InteractPokemonUIPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonID)
buffer.writeBoolean(canMountShoulder)
buffer.writeBoolean(canGiveHeld)
buffer.writeBoolean(canGiveCosmetic)
buffer.writeBoolean(canRide)
}
companion object {
val ID = cobblemonResource("interact_pokemon_ui")
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonUIPacket(buffer.readUUID(), buffer.readBoolean(), buffer.readBoolean())
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonUIPacket(
buffer.readUUID(),
buffer.readBoolean(),
buffer.readBoolean(),
buffer.readBoolean(),
buffer.readBoolean()
)
}
}
@@ -9,10 +9,11 @@
package com.cobblemon.mod.common.net.messages.server.pokemon.interact
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.gui.interact.wheel.InteractTypePokemon
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readUUID
import com.cobblemon.mod.common.util.writeUUID
import com.cobblemon.mod.common.util.readEnumConstant
import com.cobblemon.mod.common.util.writeEnumConstant
import net.minecraft.network.RegistryFriendlyByteBuf
import java.util.UUID
@@ -24,15 +25,14 @@ import java.util.UUID
* @author Village
* @since January 7th, 2023
*/
class InteractPokemonPacket(val pokemonID: UUID, val mountShoulder: Boolean, val ride: Boolean) : NetworkPacket<InteractPokemonPacket> {
class InteractPokemonPacket(val pokemonID: UUID, val interactType: InteractTypePokemon) : NetworkPacket<InteractPokemonPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonID)
buffer.writeBoolean(mountShoulder)
buffer.writeBoolean(ride)
buffer.writeEnumConstant(interactType)
}
companion object {
val ID = cobblemonResource("interact_pokemon")
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonPacket(buffer.readUUID(), buffer.readBoolean(), buffer.readBoolean())
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonPacket(buffer.readUUID(), buffer.readEnumConstant(InteractTypePokemon::class.java))
}
}

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