WIP: fork patch/collider/codec stubs, 464->135 compile errors

Phase 0 compile progress (70% reduction). Core data model compile :

Refs yesman.epicfight strippées (hors 4 javadocs) :
- AnimationProperty : combat properties EXTRA_DAMAGE, STUN_TYPE, PARTICLE
- ClientAnimator : playAnimationAt(..., AnimatorControlPacket.Layer, Priority)
- ClothSimulator : OBBCollider -> fork geometry-only dans rig/collider/
- InstantiateInvoker : Collider, ColliderPreset, Armatures, DatapackEditScreen
- MoveCoordFunctions : GrapplingAttackAnimation
- SimulationTypes : InverseKinematicsSimulator (path rewrite)

Stubs patch/ :
- EntityPatch<T> abstract — getOriginal, isLogicalClient, getMatrix, getAngleTo
- LivingEntityPatch<T> abstract — getAnimator, getArmature, getTarget, getYRot*
- MobPatch<T extends Mob> — instanceof check only
- item/CapabilityItem — type marker

Forks utilitaires :
- rig/collider/OBBCollider — geometry only (strip Entity collision, drawInternal)
- anim/types/StateSpectrum — identique EF, imports rewrités
- util/PacketBufferCodec — StreamCodec backport
- util/TimePairList — identique EF
- util/HitEntityList — shell pour Priority enum uniquement
- util/ExtendableEnum + ExtendableEnumManager — register/assign enum

Fix package declarations :
- armature/Joint.java + JointTransform.java : package rig.anim -> rig.armature
- Imports JointTransform ajoutés dans anim/{Pose,Keyframe,TransformSheet}

Residu 135 errors = cluster rendering (Phase 2) :
- render/TiedUpRenderTypes (17) : CompositeState package-private MC
- event/PatchedRenderersEvent (11) : missing PatchedEntityRenderer
- mesh/SkinnedMesh (13) : VanillaMeshPartDefinition, compute shader fields
- asset/JsonAssetLoader (6), anim/LivingMotion (5)
This commit is contained in:
NotEvil
2026-04-22 02:45:18 +02:00
parent 06678199be
commit b034184f8a
46 changed files with 1574 additions and 2260 deletions

View File

@@ -56,15 +56,11 @@ import com.tiedup.remake.rig.anim.types.StaticAnimation;
import com.tiedup.remake.rig.asset.AssetAccessor; import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.asset.JsonAssetLoader; import com.tiedup.remake.rig.asset.JsonAssetLoader;
import com.tiedup.remake.rig.anim.client.AnimationSubFileReader; import com.tiedup.remake.rig.anim.client.AnimationSubFileReader;
import yesman.epicfight.api.data.reloader.SkillManager;
import com.tiedup.remake.rig.exception.AssetLoadingException; import com.tiedup.remake.rig.exception.AssetLoadingException;
import com.tiedup.remake.rig.util.InstantiateInvoker; import com.tiedup.remake.rig.util.InstantiateInvoker;
import com.tiedup.remake.rig.util.MutableBoolean; import com.tiedup.remake.rig.util.MutableBoolean;
import com.tiedup.remake.rig.TiedUpRigRegistry; import com.tiedup.remake.rig.TiedUpRigRegistry;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.network.EpicFightNetworkManager;
import yesman.epicfight.network.client.CPCheckAnimationRegistryMatches;
import yesman.epicfight.network.server.SPDatapackSync;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class AnimationManager extends SimplePreparableReloadListener<List<ResourceLocation>> { public class AnimationManager extends SimplePreparableReloadListener<List<ResourceLocation>> {
@@ -212,7 +208,7 @@ public class AnimationManager extends SimplePreparableReloadListener<List<Resour
} }
}); });
SkillManager.reloadAllSkillsAnimations(); // RIG : SkillManager.reloadAllSkillsAnimations() strippé (combat skills).
this.animations.entrySet().stream() this.animations.entrySet().stream()
.reduce( .reduce(
@@ -301,88 +297,13 @@ public class AnimationManager extends SimplePreparableReloadListener<List<Resour
* custom weapon types & mob capabilities won't be created because they won't be able to find the animations from the server * custom weapon types & mob capabilities won't be created because they won't be able to find the animations from the server
* dummy animations will be automatically removed right after reloading resourced as the server forces using resource pack * dummy animations will be automatically removed right after reloading resourced as the server forces using resource pack
*/ */
@OnlyIn(Dist.CLIENT) // RIG : processServerPacket + validateClientAnimationRegistry strippés.
public void processServerPacket(SPDatapackSync packet, boolean mandatoryPack) { // C'était le protocole datapack-sync EF pour valider que le client a les
if (mandatoryPack) { // mêmes animations que le serveur au login (important pour les animations
for (CompoundTag tag : packet.getTags()) { // combat stockées en data/). TiedUp utilise resource pack uniquement
String invocationCommand = tag.getString("invoke_command"); // (assets/) côté client, pas de sync datapack nécessaire.
ResourceLocation registryName = ResourceLocation.parse(tag.getString("registry_name")); // Ré-introduire Phase 2+ si on veut un warning quand un pack d'animations
int id = tag.getInt("id"); // custom diverge.
if (this.animationByName.containsKey(registryName)) {
continue;
}
AnimationAccessor<? extends StaticAnimation> accessor = AnimationAccessorImpl.create(registryName, getResourcepackAnimationCount(), false, (accessor$2) -> {
try {
return InstantiateInvoker.invoke(invocationCommand, StaticAnimation.class).getResult();
} catch (Exception e) {
TiedUpRigConstants.LOGGER.warn("Failed at creating animation from server resource pack");
e.printStackTrace();
return TiedUpRigRegistry.EMPTY_ANIMATION;
}
});
this.animationById.put(id, accessor);
this.animationByName.put(registryName, accessor);
}
}
int animationCount = this.animations.size();
String[] registryNames = new String[animationCount];
for (int i = 0; i < animationCount; i++) {
String registryName = this.animationById.get(i + 1).registryName().toString();
registryNames[i] = registryName;
}
CPCheckAnimationRegistryMatches registrySyncPacket = new CPCheckAnimationRegistryMatches(animationCount, registryNames);
EpicFightNetworkManager.sendToServer(registrySyncPacket);
}
public void validateClientAnimationRegistry(CPCheckAnimationRegistryMatches msg, ServerGamePacketListenerImpl connection) {
StringBuilder messageBuilder = new StringBuilder();
int count = 0;
Set<String> clientAnimationRegistry = new HashSet<> (Set.of(msg.registryNames));
for (String registryName : this.animations.keySet().stream().map((rl) -> rl.toString()).toList()) {
if (!clientAnimationRegistry.contains(registryName)) {
// Animations that don't exist in client
if (count < 10) {
messageBuilder.append(registryName);
messageBuilder.append("\n");
}
count++;
} else {
clientAnimationRegistry.remove(registryName);
}
}
// Animations that don't exist in server
for (String registryName : clientAnimationRegistry) {
if (registryName.equals("empty")) {
continue;
}
if (count < 10) {
messageBuilder.append(registryName);
messageBuilder.append("\n");
}
count++;
}
if (count >= 10) {
messageBuilder.append(Component.translatable("gui.epicfight.warn.animation_unsync.etc", (count - 9)).getString());
messageBuilder.append("\n");
}
if (!messageBuilder.isEmpty()) {
connection.disconnect(Component.translatable("gui.epicfight.warn.animation_unsync", messageBuilder.toString()));
}
}
private static final Set<String> NO_WARNING_MODID = Sets.newHashSet(); private static final Set<String> NO_WARNING_MODID = Sets.newHashSet();

View File

@@ -0,0 +1,31 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.anim;
/**
* RIG stub. Upstream EF : packet commun client/serveur pour sync des
* animation variables (variables partagées entre deux sides pendant une
* animation combat).
*
* <p>TiedUp Phase 0 : la classe est conservée en stub juste pour l'enum
* {@link Action} utilisé par {@code AnimationVariables.put/remove} et
* {@code SynchedAnimationVariableKey.sync}. Le vrai packet réseau n'est
* pas implémenté — les `sync()` calls sont no-ops côté runtime pour
* l'instant (cf. {@code SynchedAnimationVariableKey.sync}).</p>
*
* <p>Phase 2+ : si on a besoin de sync d'animation variables entre
* serveur et client (cas d'usage non identifié en TiedUp), implémenter
* un vrai packet. Sinon garder le stub et stripper {@code sync()} plus
* tard.</p>
*/
public class AnimationVariablePacket {
public enum Action {
PUT,
REMOVE,
}
}

View File

@@ -22,7 +22,7 @@ import com.tiedup.remake.rig.util.ParseUtil;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap; import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap.TypeKey; import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap.TypeKey;
import com.tiedup.remake.rig.TiedUpRigRegistry; import com.tiedup.remake.rig.TiedUpRigRegistry;
import yesman.epicfight.network.common.AnimationVariablePacket; import com.tiedup.remake.rig.anim.AnimationVariablePacket;
public class AnimationVariables { public class AnimationVariables {
protected final Animator animator; protected final Animator animator;

View File

@@ -6,6 +6,8 @@
package com.tiedup.remake.rig.anim; package com.tiedup.remake.rig.anim;
import com.tiedup.remake.rig.armature.JointTransform;
public class Keyframe { public class Keyframe {
private float timeStamp; private float timeStamp;
private final JointTransform transform; private final JointTransform transform;

View File

@@ -14,6 +14,8 @@ import java.util.function.BiConsumer;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.tiedup.remake.rig.armature.JointTransform;
public class Pose { public class Pose {
public static final Pose EMPTY_POSE = new Pose(); public static final Pose EMPTY_POSE = new Pose();

View File

@@ -22,10 +22,9 @@ import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.util.PacketBufferCodec; import com.tiedup.remake.rig.util.PacketBufferCodec;
import com.tiedup.remake.rig.util.datastruct.ClearableIdMapper; import com.tiedup.remake.rig.util.datastruct.ClearableIdMapper;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.network.EpicFightNetworkManager; import com.tiedup.remake.rig.anim.AnimationVariablePacket;
import yesman.epicfight.network.client.CPAnimationVariablePacket; import com.tiedup.remake.rig.anim.AnimationVariablePacket;
import yesman.epicfight.network.common.AnimationVariablePacket; import com.tiedup.remake.rig.anim.AnimationVariablePacket;
import yesman.epicfight.network.server.SPAnimationVariablePacket;
import com.tiedup.remake.rig.patch.LivingEntityPatch; import com.tiedup.remake.rig.patch.LivingEntityPatch;
public interface SynchedAnimationVariableKey<T> { public interface SynchedAnimationVariableKey<T> {
@@ -83,11 +82,9 @@ public interface SynchedAnimationVariableKey<T> {
} }
default void sync(LivingEntityPatch<?> entitypatch, @Nullable AssetAccessor<? extends StaticAnimation> animation, T value, AnimationVariablePacket.Action action) { default void sync(LivingEntityPatch<?> entitypatch, @Nullable AssetAccessor<? extends StaticAnimation> animation, T value, AnimationVariablePacket.Action action) {
if (entitypatch.isLogicalClient()) { // RIG : sync réseau des animation variables strippé.
EpicFightNetworkManager.sendToServer(new CPAnimationVariablePacket<> (this, animation, value, action)); // Pas d'usage bondage identifié — ré-implémenter Phase 2 avec packet
} else { // dédié si besoin. Voir AnimationVariablePacket stub.
entitypatch.sendToAllPlayersTrackingMe(new SPAnimationVariablePacket<> (entitypatch, this, animation, value, action));
}
} }
public static class SynchedSharedAnimationVariableKey<T> extends SharedAnimationVariableKey<T> implements SynchedAnimationVariableKey<T> { public static class SynchedSharedAnimationVariableKey<T> extends SharedAnimationVariableKey<T> implements SynchedAnimationVariableKey<T> {

View File

@@ -14,6 +14,7 @@ import org.joml.Quaternionf;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import com.tiedup.remake.rig.armature.JointTransform;
import com.tiedup.remake.rig.math.MathUtils; import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f; import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f; import com.tiedup.remake.rig.math.Vec3f;

View File

@@ -15,7 +15,6 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.jetbrains.annotations.ApiStatus;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@@ -46,7 +45,6 @@ import com.tiedup.remake.rig.anim.client.property.JointMaskEntry;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap; import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap;
import com.tiedup.remake.rig.TiedUpRigRegistry; import com.tiedup.remake.rig.TiedUpRigRegistry;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.network.common.AnimatorControlPacket;
import com.tiedup.remake.rig.patch.LivingEntityPatch; import com.tiedup.remake.rig.patch.LivingEntityPatch;
public class ClientAnimator extends Animator { public class ClientAnimator extends Animator {
@@ -85,14 +83,10 @@ public class ClientAnimator extends Animator {
layer.playAnimation(nextAnimation, this.entitypatch, transitionTimeModifier); layer.playAnimation(nextAnimation, this.entitypatch, transitionTimeModifier);
} }
/** Play an animation with specifying layer and priority **/ // RIG : playAnimationAt(..., AnimatorControlPacket.Layer, AnimatorControlPacket.Priority)
@ApiStatus.Internal // strippé — re-implem Phase 2 avec packet dédié. Voir AnimationVariablePacket.
public void playAnimationAt(AssetAccessor<? extends StaticAnimation> nextAnimation, float transitionTimeModifier, AnimatorControlPacket.Layer layerType, AnimatorControlPacket.Priority priority) {
Layer layer = layerType == AnimatorControlPacket.Layer.BASE_LAYER ? this.baseLayer : this.baseLayer.compositeLayers.get(AnimatorControlPacket.getPriority(priority));
layer.paused = false;
layer.playAnimation(nextAnimation, this.entitypatch, transitionTimeModifier);
}
@Override @Override
public void playAnimationInstantly(AssetAccessor<? extends StaticAnimation> nextAnimation) { public void playAnimationInstantly(AssetAccessor<? extends StaticAnimation> nextAnimation) {
Layer layer = nextAnimation.get().getLayerType() == Layer.LayerType.BASE_LAYER ? this.baseLayer : this.baseLayer.compositeLayers.get(nextAnimation.get().getPriority()); Layer layer = nextAnimation.get().getLayerType() == Layer.LayerType.BASE_LAYER ? this.baseLayer : this.baseLayer.compositeLayers.get(nextAnimation.get().getPriority());

View File

@@ -24,7 +24,6 @@ import net.minecraft.sounds.SoundEvent;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.registries.RegistryObject;
import com.tiedup.remake.rig.anim.AnimationManager.AnimationAccessor; import com.tiedup.remake.rig.anim.AnimationManager.AnimationAccessor;
import com.tiedup.remake.rig.anim.LivingMotion; import com.tiedup.remake.rig.anim.LivingMotion;
import com.tiedup.remake.rig.anim.Pose; import com.tiedup.remake.rig.anim.Pose;
@@ -36,18 +35,14 @@ import com.tiedup.remake.rig.anim.types.ActionAnimation;
import com.tiedup.remake.rig.anim.types.DynamicAnimation; import com.tiedup.remake.rig.anim.types.DynamicAnimation;
import com.tiedup.remake.rig.anim.types.LinkAnimation; import com.tiedup.remake.rig.anim.types.LinkAnimation;
import com.tiedup.remake.rig.anim.types.StaticAnimation; import com.tiedup.remake.rig.anim.types.StaticAnimation;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulator.BakedInverseKinematicsDefinition; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.BakedInverseKinematicsDefinition;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulator.InverseKinematicsDefinition; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.InverseKinematicsDefinition;
import com.tiedup.remake.rig.util.HitEntityList.Priority; import com.tiedup.remake.rig.util.HitEntityList.Priority;
import com.tiedup.remake.rig.util.TimePairList; import com.tiedup.remake.rig.util.TimePairList;
import com.tiedup.remake.rig.math.ValueModifier; import com.tiedup.remake.rig.math.ValueModifier;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.particle.HitParticleType;
import yesman.epicfight.skill.BasicAttack;
import com.tiedup.remake.rig.patch.LivingEntityPatch; import com.tiedup.remake.rig.patch.LivingEntityPatch;
import com.tiedup.remake.rig.patch.item.CapabilityItem; import com.tiedup.remake.rig.patch.item.CapabilityItem;
import yesman.epicfight.world.damagesource.ExtraDamageInstance;
import yesman.epicfight.world.damagesource.StunType;
public abstract class AnimationProperty<T> { public abstract class AnimationProperty<T> {
private static final Map<String, AnimationProperty<?>> SERIALIZABLE_ANIMATION_PROPERTY_KEYS = Maps.newHashMap(); private static final Map<String, AnimationProperty<?>> SERIALIZABLE_ANIMATION_PROPERTY_KEYS = Maps.newHashMap();
@@ -250,10 +245,11 @@ public abstract class AnimationProperty<T> {
public static final ActionAnimationProperty<TimePairList> COORD_UPDATE_TIME = new ActionAnimationProperty<TimePairList> (); public static final ActionAnimationProperty<TimePairList> COORD_UPDATE_TIME = new ActionAnimationProperty<TimePairList> ();
/** /**
* This property determines if it reset the player basic attack combo counter or not {@link BasicAttack} * This property determines if it reset the player basic attack combo counter or not.
* RIG : BasicAttack strippé, flag conservé pour compat JSON.
*/ */
public static final ActionAnimationProperty<Boolean> RESET_PLAYER_COMBO_COUNTER = new ActionAnimationProperty<Boolean> ("reset_combo_attack_counter", Codec.BOOL); public static final ActionAnimationProperty<Boolean> RESET_PLAYER_COMBO_COUNTER = new ActionAnimationProperty<Boolean> ("reset_combo_attack_counter", Codec.BOOL);
/** /**
* Provide destination of action animation {@link MoveCoordFunctions} * Provide destination of action animation {@link MoveCoordFunctions}
*/ */
@@ -333,11 +329,9 @@ public abstract class AnimationProperty<T> {
public static final AttackPhaseProperty<ValueModifier> DAMAGE_MODIFIER = new AttackPhaseProperty<ValueModifier> ("damage", ValueModifier.CODEC); public static final AttackPhaseProperty<ValueModifier> DAMAGE_MODIFIER = new AttackPhaseProperty<ValueModifier> ("damage", ValueModifier.CODEC);
public static final AttackPhaseProperty<ValueModifier> ARMOR_NEGATION_MODIFIER = new AttackPhaseProperty<ValueModifier> ("armor_negation", ValueModifier.CODEC); public static final AttackPhaseProperty<ValueModifier> ARMOR_NEGATION_MODIFIER = new AttackPhaseProperty<ValueModifier> ("armor_negation", ValueModifier.CODEC);
public static final AttackPhaseProperty<ValueModifier> IMPACT_MODIFIER = new AttackPhaseProperty<ValueModifier> ("impact", ValueModifier.CODEC); public static final AttackPhaseProperty<ValueModifier> IMPACT_MODIFIER = new AttackPhaseProperty<ValueModifier> ("impact", ValueModifier.CODEC);
public static final AttackPhaseProperty<Set<ExtraDamageInstance>> EXTRA_DAMAGE = new AttackPhaseProperty<Set<ExtraDamageInstance>> (); // RIG : EXTRA_DAMAGE, STUN_TYPE, PARTICLE strippés (combat).
public static final AttackPhaseProperty<StunType> STUN_TYPE = new AttackPhaseProperty<StunType> ();
public static final AttackPhaseProperty<SoundEvent> SWING_SOUND = new AttackPhaseProperty<SoundEvent> (); public static final AttackPhaseProperty<SoundEvent> SWING_SOUND = new AttackPhaseProperty<SoundEvent> ();
public static final AttackPhaseProperty<SoundEvent> HIT_SOUND = new AttackPhaseProperty<SoundEvent> (); public static final AttackPhaseProperty<SoundEvent> HIT_SOUND = new AttackPhaseProperty<SoundEvent> ();
public static final AttackPhaseProperty<RegistryObject<HitParticleType>> PARTICLE = new AttackPhaseProperty<RegistryObject<HitParticleType>> ();
public static final AttackPhaseProperty<Priority> HIT_PRIORITY = new AttackPhaseProperty<Priority> (); public static final AttackPhaseProperty<Priority> HIT_PRIORITY = new AttackPhaseProperty<Priority> ();
public static final AttackPhaseProperty<Set<TagKey<DamageType>>> SOURCE_TAG = new AttackPhaseProperty<Set<TagKey<DamageType>>> (); public static final AttackPhaseProperty<Set<TagKey<DamageType>>> SOURCE_TAG = new AttackPhaseProperty<Set<TagKey<DamageType>>> ();
public static final AttackPhaseProperty<Function<LivingEntityPatch<?>, Vec3>> SOURCE_LOCATION_PROVIDER = new AttackPhaseProperty<Function<LivingEntityPatch<?>, Vec3>> (); public static final AttackPhaseProperty<Function<LivingEntityPatch<?>, Vec3>> SOURCE_LOCATION_PROVIDER = new AttackPhaseProperty<Function<LivingEntityPatch<?>, Vec3>> ();

View File

@@ -33,7 +33,7 @@ import com.tiedup.remake.rig.anim.types.AttackAnimation;
import com.tiedup.remake.rig.anim.types.AttackAnimation.Phase; import com.tiedup.remake.rig.anim.types.AttackAnimation.Phase;
import com.tiedup.remake.rig.anim.types.DynamicAnimation; import com.tiedup.remake.rig.anim.types.DynamicAnimation;
import com.tiedup.remake.rig.anim.types.EntityState; import com.tiedup.remake.rig.anim.types.EntityState;
import com.tiedup.remake.rig.anim.types.grappling.GrapplingAttackAnimation; // RIG : GrapplingAttackAnimation strippé (combat grappling hook), ref javadoc conservée
import com.tiedup.remake.rig.math.MathUtils; import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f; import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f; import com.tiedup.remake.rig.math.Vec3f;

View File

@@ -6,400 +6,32 @@
package com.tiedup.remake.rig.anim.types; package com.tiedup.remake.rig.anim.types;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ForgeMod;
import com.tiedup.remake.rig.anim.AnimationManager.AnimationAccessor;
import com.tiedup.remake.rig.anim.AnimationPlayer;
import com.tiedup.remake.rig.anim.AnimationVariables;
import com.tiedup.remake.rig.anim.AnimationVariables.IndependentAnimationVariableKey;
import com.tiedup.remake.rig.anim.AnimationVariables.SharedAnimationVariableKey;
import com.tiedup.remake.rig.armature.JointTransform;
import com.tiedup.remake.rig.anim.Keyframe;
import com.tiedup.remake.rig.anim.Pose;
import com.tiedup.remake.rig.anim.TransformSheet;
import com.tiedup.remake.rig.anim.property.AnimationProperty.ActionAnimationProperty; import com.tiedup.remake.rig.anim.property.AnimationProperty.ActionAnimationProperty;
import com.tiedup.remake.rig.anim.property.AnimationProperty.AttackAnimationProperty;
import com.tiedup.remake.rig.anim.property.AnimationProperty.PlaybackSpeedModifier;
import com.tiedup.remake.rig.anim.property.AnimationProperty.StaticAnimationProperty;
import com.tiedup.remake.rig.anim.property.MoveCoordFunctions;
import com.tiedup.remake.rig.anim.property.MoveCoordFunctions.MoveCoordGetter;
import com.tiedup.remake.rig.anim.property.MoveCoordFunctions.MoveCoordSetter;
import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.anim.client.Layer;
import com.tiedup.remake.rig.anim.client.property.ClientAnimationProperties;
import com.tiedup.remake.rig.anim.client.property.JointMaskEntry;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import com.tiedup.remake.rig.util.TimePairList; import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f;
import com.tiedup.remake.rig.patch.LocalPlayerPatch;
import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.network.EpicFightNetworkManager;
import yesman.epicfight.network.client.CPSyncPlayerAnimationPosition;
import yesman.epicfight.network.server.SPSyncAnimationPosition;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
/**
* RIG stub. Upstream EF : ActionAnimation ajoute un système de COORD
* TransformSheet pour les mouvements d'attaque en espace monde + des
* packets sync serveur/client via {@code CPSyncPlayerAnimationPosition}.
* Strippé pour TiedUp — garde juste l'API {@code addProperty} utilisée par
* {@link com.tiedup.remake.rig.asset.JsonAssetLoader} (ligne 621).
*/
public class ActionAnimation extends MainFrameAnimation { public class ActionAnimation extends MainFrameAnimation {
public static final SharedAnimationVariableKey<TransformSheet> ACTION_ANIMATION_COORD = AnimationVariables.shared((animator) -> new TransformSheet(), false);
public static final IndependentAnimationVariableKey<Vec3> BEGINNING_LOCATION = AnimationVariables.independent((animator) -> animator.getEntityPatch().getOriginal().position(), true); public ActionAnimation(float transitionTime, boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
public static final IndependentAnimationVariableKey<Float> INITIAL_LOOK_VEC_DOT = AnimationVariables.independent((animator) -> 1.0F, true); super(transitionTime, isRepeat, registryName, armature);
public ActionAnimation(float transitionTime, AnimationAccessor<? extends ActionAnimation> accessor, AssetAccessor<? extends Armature> armature) {
this(transitionTime, Float.MAX_VALUE, accessor, armature);
}
public ActionAnimation(float transitionTime, float postDelay, AnimationAccessor<? extends ActionAnimation> accessor, AssetAccessor<? extends Armature> armature) {
super(transitionTime, accessor, armature);
this.stateSpectrumBlueprint.clear()
.newTimePair(0.0F, postDelay)
.addState(EntityState.MOVEMENT_LOCKED, true)
.addState(EntityState.UPDATE_LIVING_MOTION, false)
.addState(EntityState.CAN_BASIC_ATTACK, false)
.addState(EntityState.CAN_SKILL_EXECUTION, false)
.addState(EntityState.TURNING_LOCKED, true)
.newTimePair(0.0F, Float.MAX_VALUE)
.addState(EntityState.INACTION, true);
this.addProperty(StaticAnimationProperty.FIXED_HEAD_ROTATION, true);
}
/**
* For resourcepack animations
*/
public ActionAnimation(float transitionTime, float postDelay, String path, AssetAccessor<? extends Armature> armature) {
super(transitionTime, path, armature);
this.stateSpectrumBlueprint.clear()
.newTimePair(0.0F, postDelay)
.addState(EntityState.MOVEMENT_LOCKED, true)
.addState(EntityState.UPDATE_LIVING_MOTION, false)
.addState(EntityState.CAN_BASIC_ATTACK, false)
.addState(EntityState.CAN_SKILL_EXECUTION, false)
.addState(EntityState.TURNING_LOCKED, true)
.newTimePair(0.0F, Float.MAX_VALUE)
.addState(EntityState.INACTION, true);
this.addProperty(StaticAnimationProperty.FIXED_HEAD_ROTATION, true);
}
@Override
public void putOnPlayer(AnimationPlayer animationPlayer, LivingEntityPatch<?> entitypatch) {
if (entitypatch.shouldMoveOnCurrentSide(this)) {
MoveCoordSetter moveCoordSetter = this.getProperty(ActionAnimationProperty.COORD_SET_BEGIN).orElse(MoveCoordFunctions.RAW_COORD);
moveCoordSetter.set(this, entitypatch, entitypatch.getAnimator().getVariables().getOrDefaultSharedVariable(ACTION_ANIMATION_COORD));
}
super.putOnPlayer(animationPlayer, entitypatch);
}
protected void initCoordVariables(LivingEntityPatch<?> entitypatch) {
Vec3 start = entitypatch.getOriginal().position();
if (entitypatch.getTarget() != null) {
Vec3 targetTracePosition = entitypatch.getTarget().position();
Vec3 toDestWorld = targetTracePosition.subtract(start);
float dot = Mth.clamp((float)toDestWorld.normalize().dot(MathUtils.getVectorForRotation(0.0F, entitypatch.getYRot())), 0.0F, 1.0F);
entitypatch.getAnimator().getVariables().put(INITIAL_LOOK_VEC_DOT, this.getAccessor(), dot);
}
entitypatch.getAnimator().getVariables().put(BEGINNING_LOCATION, this.getAccessor(), start);
}
@Override
public void begin(LivingEntityPatch<?> entitypatch) {
entitypatch.cancelItemUse();
super.begin(entitypatch);
if (entitypatch.shouldMoveOnCurrentSide(this)) {
entitypatch.beginAction(this);
this.initCoordVariables(entitypatch);
if (this.getProperty(ActionAnimationProperty.STOP_MOVEMENT).orElse(false)) {
entitypatch.getOriginal().setDeltaMovement(0.0D, entitypatch.getOriginal().getDeltaMovement().y, 0.0D);
entitypatch.getOriginal().xxa = 0.0F;
entitypatch.getOriginal().yya = 0.0F;
entitypatch.getOriginal().zza = 0.0F;
}
}
}
@Override
public void tick(LivingEntityPatch<?> entitypatch) {
super.tick(entitypatch);
if (this.getProperty(ActionAnimationProperty.REMOVE_DELTA_MOVEMENT).orElse(false)) {
double gravity = this.getProperty(ActionAnimationProperty.MOVE_VERTICAL).orElse(false) ? 0.0D : entitypatch.getOriginal().getDeltaMovement().y;
entitypatch.getOriginal().setDeltaMovement(0.0D, gravity, 0.0D);
}
this.move(entitypatch, this.getAccessor());
}
@Override
public void linkTick(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> linkAnimation) {
if (this.getProperty(ActionAnimationProperty.REMOVE_DELTA_MOVEMENT).orElse(false)) {
double gravity = this.getProperty(ActionAnimationProperty.MOVE_VERTICAL).orElse(false) ? 0.0D : entitypatch.getOriginal().getDeltaMovement().y;
entitypatch.getOriginal().setDeltaMovement(0.0D, gravity, 0.0D);
}
this.move(entitypatch, linkAnimation);
} }
protected void move(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> animation) { public ActionAnimation(boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
if (!this.validateMovement(entitypatch, animation)) { super(isRepeat, registryName, armature);
return;
}
float elapsedTime = entitypatch.getAnimator().getPlayerFor(this.getAccessor()).getElapsedTime();
if (this.getState(EntityState.INACTION, entitypatch, elapsedTime)) {
LivingEntity livingentity = entitypatch.getOriginal();
Vec3 vec3 = this.getCoordVector(entitypatch, animation);
livingentity.move(MoverType.SELF, vec3);
if (entitypatch.isLogicalClient()) {
EpicFightNetworkManager.sendToServer(new CPSyncPlayerAnimationPosition(livingentity.getId(), elapsedTime, livingentity.position(), animation.get().isLinkAnimation() ? 2 : 1));
} else {
EpicFightNetworkManager.sendToAllPlayerTrackingThisEntity(new SPSyncAnimationPosition(livingentity.getId(), elapsedTime, livingentity.position(), animation.get().isLinkAnimation() ? 2 : 1), livingentity);
}
}
} }
protected boolean validateMovement(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> animation) {
if (!entitypatch.shouldMoveOnCurrentSide(this)) {
return false;
}
if (animation.get().isLinkAnimation()) {
if (!this.getProperty(ActionAnimationProperty.MOVE_ON_LINK).orElse(true)) {
return false;
} else {
return this.shouldMove(0.0F);
}
} else {
return this.shouldMove(entitypatch.getAnimator().getPlayerFor(animation).getElapsedTime());
}
}
protected boolean shouldMove(float currentTime) {
if (this.properties.containsKey(ActionAnimationProperty.MOVE_TIME)) {
TimePairList moveTimes = this.getProperty(ActionAnimationProperty.MOVE_TIME).get();
return moveTimes.isTimeInPairs(currentTime);
} else {
return true;
}
}
@Override
public void modifyPose(DynamicAnimation animation, Pose pose, LivingEntityPatch<?> entitypatch, float time, float partialTicks) {
if (this.getProperty(ActionAnimationProperty.COORD).isEmpty()) {
this.correctRootJoint(animation, pose, entitypatch, time, partialTicks);
}
super.modifyPose(animation, pose, entitypatch, time, partialTicks);
}
public void correctRootJoint(DynamicAnimation animation, Pose pose, LivingEntityPatch<?> entitypatch, float time, float partialTicks) {
JointTransform jt = pose.orElseEmpty("Root");
Vec3f jointPosition = jt.translation();
OpenMatrix4f toRootTransformApplied = entitypatch.getArmature().searchJointByName("Root").getLocalTransform().removeTranslation();
OpenMatrix4f toOrigin = OpenMatrix4f.invert(toRootTransformApplied, null);
Vec3f worldPosition = OpenMatrix4f.transform3v(toRootTransformApplied, jointPosition, null);
worldPosition.x = 0.0F;
worldPosition.y = (this.getProperty(ActionAnimationProperty.MOVE_VERTICAL).orElse(false) && worldPosition.y > 0.0F) ? 0.0F : worldPosition.y;
worldPosition.z = 0.0F;
OpenMatrix4f.transform3v(toOrigin, worldPosition, worldPosition);
jointPosition.x = worldPosition.x;
jointPosition.y = worldPosition.y;
jointPosition.z = worldPosition.z;
}
@Override
public void setLinkAnimation(AssetAccessor<? extends DynamicAnimation> fromAnimation, Pose startPose, boolean isOnSameLayer, float transitionTimeModifier, LivingEntityPatch<?> entitypatch, LinkAnimation dest) {
dest.resetNextStartTime();
float playTime = this.getPlaySpeed(entitypatch, dest);
PlaybackSpeedModifier playSpeedModifier = this.getRealAnimation().get().getProperty(StaticAnimationProperty.PLAY_SPEED_MODIFIER).orElse(null);
if (playSpeedModifier != null) {
playTime = playSpeedModifier.modify(this, entitypatch, playTime, 0.0F, playTime);
}
playTime = Math.abs(playTime) * TiedUpRigConstants.A_TICK;
float linkTime = (transitionTimeModifier > 0.0F) ? transitionTimeModifier + this.transitionTime : this.transitionTime;
float totalTime = playTime * (int)Math.ceil(linkTime / playTime);
float nextStartTime = Math.max(0.0F, -transitionTimeModifier);
nextStartTime += totalTime - linkTime;
dest.setNextStartTime(nextStartTime);
dest.getAnimationClip().reset();
dest.setTotalTime(totalTime);
dest.setConnectedAnimations(fromAnimation, this.getAccessor());
Pose nextStartPose = this.getPoseByTime(entitypatch, nextStartTime, 1.0F);
if (entitypatch.shouldMoveOnCurrentSide(this) && this.getProperty(ActionAnimationProperty.MOVE_ON_LINK).orElse(true)) {
this.correctRawZCoord(entitypatch, nextStartPose, nextStartTime);
}
Map<String, JointTransform> data1 = startPose.getJointTransformData();
Map<String, JointTransform> data2 = nextStartPose.getJointTransformData();
Set<String> joint1 = new HashSet<> (isOnSameLayer ? data1.keySet() : Set.of());
Set<String> joint2 = new HashSet<> (data2.keySet());
if (entitypatch.isLogicalClient()) {
JointMaskEntry entry = fromAnimation.get().getJointMaskEntry(entitypatch, false).orElse(null);
JointMaskEntry entry2 = this.getJointMaskEntry(entitypatch, true).orElse(null);
if (entry != null && entitypatch.isLogicalClient()) {
joint1.removeIf((jointName) -> entry.isMasked(fromAnimation.get().getProperty(ClientAnimationProperties.LAYER_TYPE).orElse(Layer.LayerType.BASE_LAYER) == Layer.LayerType.BASE_LAYER ?
entitypatch.getClientAnimator().currentMotion() : entitypatch.getClientAnimator().currentCompositeMotion(), jointName));
}
if (entry2 != null && entitypatch.isLogicalClient()) {
joint2.removeIf((jointName) -> entry2.isMasked(this.getProperty(ClientAnimationProperties.LAYER_TYPE).orElse(Layer.LayerType.BASE_LAYER) == Layer.LayerType.BASE_LAYER ?
entitypatch.getCurrentLivingMotion() : entitypatch.currentCompositeMotion, jointName));
}
}
joint1.addAll(joint2);
if (linkTime != totalTime) {
Pose pose = this.getPoseByTime(entitypatch, 0.0F, 0.0F);
Map<String, JointTransform> poseData = pose.getJointTransformData();
if (entitypatch.shouldMoveOnCurrentSide(this) && this.getProperty(ActionAnimationProperty.MOVE_ON_LINK).orElse(true)) {
this.correctRawZCoord(entitypatch, pose, 0.0F);
}
for (String jointName : joint1) {
Keyframe[] keyframes = new Keyframe[3];
keyframes[0] = new Keyframe(0.0F, data1.getOrDefault(jointName, JointTransform.empty()));
keyframes[1] = new Keyframe(linkTime, poseData.getOrDefault(jointName, JointTransform.empty()));
keyframes[2] = new Keyframe(totalTime, data2.getOrDefault(jointName, JointTransform.empty()));
TransformSheet sheet = new TransformSheet(keyframes);
dest.getAnimationClip().addJointTransform(jointName, sheet);
}
} else {
for (String jointName : joint1) {
Keyframe[] keyframes = new Keyframe[2];
keyframes[0] = new Keyframe(0.0F, data1.getOrDefault(jointName, JointTransform.empty()));
keyframes[1] = new Keyframe(totalTime, data2.getOrDefault(jointName, JointTransform.empty()));
TransformSheet sheet = new TransformSheet(keyframes);
dest.getAnimationClip().addJointTransform(jointName, sheet);
}
}
dest.loadCoord(null);
this.getProperty(ActionAnimationProperty.COORD).ifPresent((coord) -> {
Keyframe[] keyframes = new Keyframe[2];
keyframes[0] = new Keyframe(0.0F, JointTransform.empty());
keyframes[1] = new Keyframe(totalTime, coord.getKeyframes()[0].transform());
TransformSheet sheet = new TransformSheet(keyframes);
dest.loadCoord(sheet);
});
if (entitypatch.shouldMoveOnCurrentSide(this)) {
MoveCoordSetter moveCoordSetter = this.getProperty(ActionAnimationProperty.COORD_SET_BEGIN).orElse(MoveCoordFunctions.RAW_COORD);
moveCoordSetter.set(dest, entitypatch, entitypatch.getAnimator().getVariables().getOrDefaultSharedVariable(ACTION_ANIMATION_COORD));
}
}
public void correctRawZCoord(LivingEntityPatch<?> entitypatch, Pose pose, float poseTime) {
JointTransform jt = pose.orElseEmpty("Root");
if (this.getProperty(ActionAnimationProperty.COORD).isEmpty()) {
TransformSheet coordTransform = this.getTransfroms().get("Root");
jt.translation().add(0.0F, 0.0F, coordTransform.getInterpolatedTranslation(poseTime).z);
}
}
/** /**
* Get an expected movement vector in specific time * Ajoute une propriété action-specific (COORD TransformSheet typiquement).
* * Version stub : no-op, juste pour que les refs JsonAssetLoader compilent.
* @param entitypatch
* @param elapseTime
* @return
*/ */
public Vec3 getExpectedMovement(LivingEntityPatch<?> entitypatch, float elapseTime) { public <V> ActionAnimation addProperty(ActionAnimationProperty<V> property, V value) {
this.initCoordVariables(entitypatch); return this;
TransformSheet coordTransform = new TransformSheet();
MoveCoordSetter moveCoordSetter = this.getProperty(ActionAnimationProperty.COORD_SET_BEGIN).orElse(MoveCoordFunctions.RAW_COORD);
moveCoordSetter.set(this, entitypatch, coordTransform);
MoveCoordGetter moveGetter = this.getProperty(ActionAnimationProperty.COORD_GET).orElse(MoveCoordFunctions.MODEL_COORD);
Vec3f move = moveGetter.get(this, entitypatch, coordTransform, 0.0F, elapseTime);
return move.toDoubleVector();
}
protected Vec3 getCoordVector(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> animation) {
AnimationPlayer player = entitypatch.getAnimator().getPlayerFor(animation);
TimePairList coordUpdateTime = this.getProperty(ActionAnimationProperty.COORD_UPDATE_TIME).orElse(null);
boolean inUpdateTime = coordUpdateTime == null || coordUpdateTime.isTimeInPairs(player.getElapsedTime());
boolean getRawCoord = this.getProperty(AttackAnimationProperty.FIXED_MOVE_DISTANCE).orElse(!inUpdateTime);
TransformSheet transformSheet = entitypatch.getAnimator().getVariables().getOrDefaultSharedVariable(ACTION_ANIMATION_COORD);
MoveCoordSetter moveCoordsetter = getRawCoord ? MoveCoordFunctions.RAW_COORD : this.getProperty(ActionAnimationProperty.COORD_SET_TICK).orElse(null);
if (moveCoordsetter != null) {
moveCoordsetter.set(animation.get(), entitypatch, transformSheet);
}
boolean hasNoGravity = entitypatch.getOriginal().isNoGravity();
boolean moveVertical = this.getProperty(ActionAnimationProperty.MOVE_VERTICAL).orElse(this.getProperty(ActionAnimationProperty.COORD).isPresent());
MoveCoordGetter moveGetter = getRawCoord ? MoveCoordFunctions.MODEL_COORD : this.getProperty(ActionAnimationProperty.COORD_GET).orElse(MoveCoordFunctions.MODEL_COORD);
Vec3f move = moveGetter.get(animation.get(), entitypatch, transformSheet, player.getPrevElapsedTime(), player.getElapsedTime());
LivingEntity livingentity = entitypatch.getOriginal();
Vec3 motion = livingentity.getDeltaMovement();
this.getProperty(ActionAnimationProperty.NO_GRAVITY_TIME).ifPresentOrElse((noGravityTime) -> {
if (noGravityTime.isTimeInPairs(animation.get().isLinkAnimation() ? 0.0F : player.getElapsedTime())) {
livingentity.setDeltaMovement(motion.x, 0.0D, motion.z);
} else {
move.y = 0.0F;
}
}, () -> {
if (moveVertical && move.y > 0.0F && !hasNoGravity) {
double gravity = livingentity.getAttribute(ForgeMod.ENTITY_GRAVITY.get()).getValue();
livingentity.setDeltaMovement(motion.x, motion.y < 0.0D ? motion.y + gravity : 0.0D, motion.z);
}
});
if (!moveVertical) {
move.y = 0.0F;
}
if (inUpdateTime) {
this.getProperty(ActionAnimationProperty.ENTITY_YROT_PROVIDER).ifPresent((entityYRotProvider) -> {
float yRot = entityYRotProvider.get(animation.get(), entitypatch);
entitypatch.setYRot(yRot);
});
}
return move.toDoubleVector();
}
@OnlyIn(Dist.CLIENT)
public boolean shouldPlayerMove(LocalPlayerPatch playerpatch) {
return playerpatch.isLogicalClient();
} }
} }

View File

@@ -6,565 +6,51 @@
package com.tiedup.remake.rig.anim.types; package com.tiedup.remake.rig.anim.types;
import java.util.ArrayList; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.entity.PartEntity;
import net.minecraftforge.registries.RegistryObject;
import com.tiedup.remake.rig.anim.AnimationManager.AnimationAccessor;
import com.tiedup.remake.rig.anim.AnimationPlayer;
import com.tiedup.remake.rig.anim.AnimationVariables;
import com.tiedup.remake.rig.anim.AnimationVariables.SharedAnimationVariableKey;
import com.tiedup.remake.rig.armature.Joint;
import com.tiedup.remake.rig.anim.property.AnimationProperty.ActionAnimationProperty;
import com.tiedup.remake.rig.anim.property.AnimationProperty.AttackAnimationProperty;
import com.tiedup.remake.rig.anim.property.AnimationProperty.AttackPhaseProperty;
import com.tiedup.remake.rig.anim.property.MoveCoordFunctions;
import com.tiedup.remake.rig.anim.types.EntityState.StateFactor;
import com.tiedup.remake.rig.asset.AssetAccessor;
import yesman.epicfight.api.collider.Collider;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import com.tiedup.remake.rig.util.AttackResult; import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.util.HitEntityList;
import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.ValueModifier;
import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.particle.HitParticleType;
import com.tiedup.remake.rig.patch.HumanoidMobPatch;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
import com.tiedup.remake.rig.patch.PlayerPatch;
import com.tiedup.remake.rig.patch.ServerPlayerPatch;
import yesman.epicfight.world.damagesource.EpicFightDamageSource;
import yesman.epicfight.world.damagesource.EpicFightDamageSources;
import yesman.epicfight.world.entity.eventlistener.AttackEndEvent;
import yesman.epicfight.world.entity.eventlistener.AttackPhaseEndEvent;
import yesman.epicfight.world.entity.eventlistener.PlayerEventListener.EventType;
/**
* RIG stub. Upstream EF : AttackAnimation = système d'attaque combat (570L,
* Phase timings, hitboxes, damage source, attack events, colliders). 100%
* combat, strippé en stub minimal pour TiedUp.
*
* <p>On conserve :</p>
* <ul>
* <li>Le type {@code AttackAnimation} pour les {@code instanceof} dans
* {@link com.tiedup.remake.rig.asset.JsonAssetLoader}:584</li>
* <li>Le field {@code phases} (liste vide) pour la boucle d'itération
* en JsonAssetLoader:591</li>
* <li>La classe interne {@link Phase} avec un {@code getColliders()}
* no-op pour JsonAssetLoader:592</li>
* </ul>
*/
public class AttackAnimation extends ActionAnimation { public class AttackAnimation extends ActionAnimation {
/** Entities that collided **/
public static final SharedAnimationVariableKey<List<Entity>> ATTACK_TRIED_ENTITIES = AnimationVariables.shared((animator) -> Lists.newArrayList(), false);
/** Entities that actually hurt **/
public static final SharedAnimationVariableKey<List<LivingEntity>> ACTUALLY_HIT_ENTITIES = AnimationVariables.shared((animator) -> Lists.newArrayList(), false);
public final Phase[] phases;
public AttackAnimation(float transitionTime, float antic, float preDelay, float contact, float recovery, @Nullable Collider collider, Joint colliderJoint, AnimationAccessor<? extends AttackAnimation> accessor, AssetAccessor<? extends Armature> armature) {
this(transitionTime, accessor, armature, new Phase(0.0F, antic, preDelay, contact, recovery, Float.MAX_VALUE, colliderJoint, collider));
}
public AttackAnimation(float transitionTime, float antic, float preDelay, float contact, float recovery, InteractionHand hand, @Nullable Collider collider, Joint colliderJoint, AnimationAccessor<? extends AttackAnimation> accessor, AssetAccessor<? extends Armature> armature) {
this(transitionTime, accessor, armature, new Phase(0.0F, antic, preDelay, contact, recovery, Float.MAX_VALUE, hand, colliderJoint, collider));
}
public AttackAnimation(float transitionTime, AnimationAccessor<? extends AttackAnimation> accessor, AssetAccessor<? extends Armature> armature, Phase... phases) {
super(transitionTime, accessor, armature);
this.addProperty(ActionAnimationProperty.COORD_SET_BEGIN, MoveCoordFunctions.TRACE_TARGET_DISTANCE);
this.addProperty(ActionAnimationProperty.COORD_SET_TICK, MoveCoordFunctions.TRACE_TARGET_DISTANCE);
this.addProperty(ActionAnimationProperty.COORD_GET, MoveCoordFunctions.MODEL_COORD);
this.addProperty(ActionAnimationProperty.DEST_LOCATION_PROVIDER, MoveCoordFunctions.ATTACK_TARGET_LOCATION);
this.addProperty(ActionAnimationProperty.ENTITY_YROT_PROVIDER, MoveCoordFunctions.MOB_ATTACK_TARGET_LOOK);
this.addProperty(ActionAnimationProperty.STOP_MOVEMENT, true);
this.phases = phases;
this.stateSpectrumBlueprint.clear();
for (Phase phase : phases) {
if (!phase.noStateBind) {
this.bindPhaseState(phase);
}
}
}
/** /**
* For resourcepack animation * Liste des phases d'attaque. Toujours vide en TiedUp — on ne joue
* jamais d'animation attaque. Mais le field doit exister pour que
* JsonAssetLoader.java ligne 591 puisse itérer dessus sans NPE.
*/ */
public AttackAnimation(float convertTime, float antic, float preDelay, float contact, float recovery, InteractionHand hand, @Nullable Collider collider, Joint colliderJoint, String path, AssetAccessor<? extends Armature> armature) { public final List<Phase> phases = Collections.emptyList();
this(convertTime, path, armature, new Phase(0.0F, antic, preDelay, contact, recovery, Float.MAX_VALUE, hand, colliderJoint, collider));
public AttackAnimation(float transitionTime, boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
super(transitionTime, isRepeat, registryName, armature);
} }
public AttackAnimation(boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
super(isRepeat, registryName, armature);
}
/** /**
* For resourcepack animation * Phase d'attaque. Stub pour satisfaire {@code phase.getColliders()}
* en JsonAssetLoader.
*/ */
public AttackAnimation(float convertTime, String path, AssetAccessor<? extends Armature> armature, Phase... phases) {
super(convertTime, 0.0F, path, armature);
this.addProperty(ActionAnimationProperty.COORD_SET_BEGIN, MoveCoordFunctions.TRACE_TARGET_DISTANCE);
this.addProperty(ActionAnimationProperty.COORD_SET_TICK, MoveCoordFunctions.TRACE_TARGET_DISTANCE);
this.addProperty(ActionAnimationProperty.COORD_GET, MoveCoordFunctions.MODEL_COORD);
this.addProperty(ActionAnimationProperty.DEST_LOCATION_PROVIDER, MoveCoordFunctions.ATTACK_TARGET_LOCATION);
this.addProperty(ActionAnimationProperty.ENTITY_YROT_PROVIDER, MoveCoordFunctions.MOB_ATTACK_TARGET_LOOK);
this.addProperty(ActionAnimationProperty.STOP_MOVEMENT, true);
this.phases = phases;
this.stateSpectrumBlueprint.clear();
for (Phase phase : phases) {
if (!phase.noStateBind) {
this.bindPhaseState(phase);
}
}
}
protected void bindPhaseState(Phase phase) {
float preDelay = phase.preDelay;
this.stateSpectrumBlueprint
.newTimePair(phase.start, preDelay)
.addState(EntityState.PHASE_LEVEL, 1)
.newTimePair(phase.start, phase.contact)
.addState(EntityState.CAN_SKILL_EXECUTION, false)
.newTimePair(phase.start, phase.recovery)
.addState(EntityState.MOVEMENT_LOCKED, true)
.addState(EntityState.UPDATE_LIVING_MOTION, false)
.addState(EntityState.CAN_BASIC_ATTACK, false)
.newTimePair(phase.start, phase.end)
.addState(EntityState.INACTION, true)
.newTimePair(phase.antic, phase.end)
.addState(EntityState.TURNING_LOCKED, true)
.newTimePair(preDelay, phase.contact)
.addState(EntityState.ATTACKING, true)
.addState(EntityState.PHASE_LEVEL, 2)
.newTimePair(phase.contact, phase.end)
.addState(EntityState.PHASE_LEVEL, 3)
;
}
@Override
public void begin(LivingEntityPatch<?> entitypatch) {
super.begin(entitypatch);
entitypatch.setLastAttackSuccess(false);
}
@Override
public void linkTick(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> linkAnimation) {
super.linkTick(entitypatch, linkAnimation);
if (!entitypatch.isLogicalClient()) {
this.attackTick(entitypatch, linkAnimation);
}
}
@Override
public void tick(LivingEntityPatch<?> entitypatch) {
super.tick(entitypatch);
if (!entitypatch.isLogicalClient()) {
this.attackTick(entitypatch, this.getAccessor());
}
}
@Override
public void end(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> nextAnimation, boolean isEnd) {
super.end(entitypatch, nextAnimation, isEnd);
if (entitypatch instanceof ServerPlayerPatch playerpatch) {
if (isEnd) {
playerpatch.getEventListener().triggerEvents(EventType.ATTACK_ANIMATION_END_EVENT, new AttackEndEvent(playerpatch, this.getAccessor()));
}
AnimationPlayer player = entitypatch.getAnimator().getPlayerFor(this.getAccessor());
float elapsedTime = player.getElapsedTime();
EntityState state = this.getState(entitypatch, elapsedTime);
if (!isEnd && state.attacking()) {
playerpatch.getEventListener().triggerEvents(EventType.ATTACK_PHASE_END_EVENT, new AttackPhaseEndEvent(playerpatch, this.getAccessor(), this.getPhaseByTime(elapsedTime), this.getPhaseOrderByTime(elapsedTime)));
}
}
if (entitypatch instanceof HumanoidMobPatch<?> mobpatch && entitypatch.isLogicalClient()) {
Mob entity = mobpatch.getOriginal();
if (entity.getTarget() != null && !entity.getTarget().isAlive()) {
entity.setTarget(null);
}
}
}
protected void attackTick(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> animation) {
AnimationPlayer player = entitypatch.getAnimator().getPlayerFor(this.getAccessor());
float prevElapsedTime = player.getPrevElapsedTime();
float elapsedTime = player.getElapsedTime();
EntityState prevState = animation.get().getState(entitypatch, prevElapsedTime);
EntityState state = animation.get().getState(entitypatch, elapsedTime);
Phase phase = this.getPhaseByTime(animation.get().isLinkAnimation() ? 0.0F : elapsedTime);
if (prevState.attacking() || state.attacking() || (prevState.getLevel() <= 2 && state.getLevel() > 2)) {
if (!prevState.attacking() || (phase != this.getPhaseByTime(prevElapsedTime) && (state.attacking() || (prevState.getLevel() <= 2 && state.getLevel() > 2)))) {
entitypatch.onStrike(this, phase.hand);
entitypatch.playSound(this.getSwingSound(entitypatch, phase), 0.0F, 0.0F);
entitypatch.removeHurtEntities();
}
this.hurtCollidingEntities(entitypatch, prevElapsedTime, elapsedTime, prevState, state, phase);
if ((!state.attacking() || elapsedTime >= this.getTotalTime()) && entitypatch instanceof ServerPlayerPatch playerpatch) {
playerpatch.getEventListener().triggerEvents(EventType.ATTACK_PHASE_END_EVENT, new AttackPhaseEndEvent(playerpatch, this.getAccessor(), phase, this.getPhaseOrderByTime(elapsedTime)));
}
}
}
protected void hurtCollidingEntities(LivingEntityPatch<?> entitypatch, float prevElapsedTime, float elapsedTime, EntityState prevState, EntityState state, Phase phase) {
LivingEntity entity = entitypatch.getOriginal();
float prevPoseTime = prevState.attacking() ? prevElapsedTime : phase.preDelay;
float poseTime = state.attacking() ? elapsedTime : phase.contact;
List<Entity> list = this.getPhaseByTime(elapsedTime).getCollidingEntities(entitypatch, this, prevPoseTime, poseTime, this.getPlaySpeed(entitypatch, this));
if (!list.isEmpty()) {
HitEntityList hitEntities = new HitEntityList(entitypatch, list, phase.getProperty(AttackPhaseProperty.HIT_PRIORITY).orElse(HitEntityList.Priority.DISTANCE));
int maxStrikes = this.getMaxStrikes(entitypatch, phase);
while (entitypatch.getCurrentlyActuallyHitEntities().size() < maxStrikes && hitEntities.next()) {
Entity target = hitEntities.getEntity();
LivingEntity trueEntity = this.getTrueEntity(target);
if (trueEntity != null && trueEntity.isAlive() && !entitypatch.getCurrentlyAttackTriedEntities().contains(trueEntity) && !entitypatch.isTargetInvulnerable(target)) {
if (target instanceof LivingEntity || target instanceof PartEntity) {
AABB aabb = target.getBoundingBox();
if (MathUtils.canBeSeen(target, entity, target.position().distanceTo(entity.getEyePosition()) + aabb.getCenter().distanceTo(new Vec3(aabb.maxX, aabb.maxY, aabb.maxZ)))) {
EpicFightDamageSource damagesource = this.getEpicFightDamageSource(entitypatch, target, phase);
int prevInvulTime = target.invulnerableTime;
target.invulnerableTime = 0;
AttackResult attackResult = entitypatch.attack(damagesource, target, phase.hand);
target.invulnerableTime = prevInvulTime;
if (attackResult.resultType.dealtDamage()) {
SoundEvent hitSound = this.getHitSound(entitypatch, phase);
if (hitSound != null) {
target.level().playSound(null, target.getX(), target.getY(), target.getZ(), this.getHitSound(entitypatch, phase), target.getSoundSource(), 1.0F, 1.0F);
}
this.spawnHitParticle((ServerLevel)target.level(), entitypatch, target, phase);
}
entitypatch.getCurrentlyAttackTriedEntities().add(trueEntity);
if (attackResult.resultType.shouldCount()) {
entitypatch.getCurrentlyActuallyHitEntities().add(trueEntity);
}
}
}
}
}
}
}
public LivingEntity getTrueEntity(Entity entity) {
if (entity instanceof LivingEntity livingEntity) {
return livingEntity;
} else if (entity instanceof PartEntity<?> partEntity) {
Entity parentEntity = partEntity.getParent();
if (parentEntity instanceof LivingEntity livingEntity) {
return livingEntity;
}
}
return null;
}
protected int getMaxStrikes(LivingEntityPatch<?> entitypatch, Phase phase) {
return phase.getProperty(AttackPhaseProperty.MAX_STRIKES_MODIFIER)
.map(valueModifier -> (int)ValueModifier.calculator().attach(valueModifier).getResult(entitypatch.getMaxStrikes(phase.hand)))
.orElse(entitypatch.getMaxStrikes(phase.hand));
}
protected SoundEvent getSwingSound(LivingEntityPatch<?> entitypatch, Phase phase) {
return phase.getProperty(AttackPhaseProperty.SWING_SOUND).orElse(entitypatch.getSwingSound(phase.hand));
}
protected SoundEvent getHitSound(LivingEntityPatch<?> entitypatch, Phase phase) {
return phase.getProperty(AttackPhaseProperty.HIT_SOUND).orElse(entitypatch.getWeaponHitSound(phase.hand));
}
public EpicFightDamageSource getEpicFightDamageSource(LivingEntityPatch<?> entitypatch, Entity target, Phase phase) {
return this.getEpicFightDamageSource(entitypatch.getDamageSource(this.getAccessor(), phase.hand), entitypatch, target, phase);
}
public EpicFightDamageSource getEpicFightDamageSource(DamageSource originalSource, LivingEntityPatch<?> entitypatch, Entity target, Phase phase) {
if (phase == null) {
phase = this.getPhaseByTime(entitypatch.getAnimator().getPlayerFor(this.getAccessor()).getElapsedTime());
}
EpicFightDamageSource epicfightSource;
if (originalSource instanceof EpicFightDamageSource epicfightDamageSource) {
epicfightSource = epicfightDamageSource;
} else {
epicfightSource = EpicFightDamageSources.fromVanillaDamageSource(originalSource).setAnimation(this.getAccessor());
}
phase.getProperty(AttackPhaseProperty.DAMAGE_MODIFIER).ifPresent(opt -> {
epicfightSource.attachDamageModifier(opt);
});
phase.getProperty(AttackPhaseProperty.ARMOR_NEGATION_MODIFIER).ifPresent(opt -> {
epicfightSource.attachArmorNegationModifier(opt);
});
phase.getProperty(AttackPhaseProperty.IMPACT_MODIFIER).ifPresent(opt -> {
epicfightSource.attachImpactModifier(opt);
});
phase.getProperty(AttackPhaseProperty.STUN_TYPE).ifPresent(opt -> {
epicfightSource.setStunType(opt);
});
phase.getProperty(AttackPhaseProperty.SOURCE_TAG).ifPresent(opt -> {
opt.forEach(epicfightSource::addRuntimeTag);
});
phase.getProperty(AttackPhaseProperty.EXTRA_DAMAGE).ifPresent(opt -> {
opt.forEach(epicfightSource::addExtraDamage);
});
phase.getProperty(AttackPhaseProperty.SOURCE_LOCATION_PROVIDER).ifPresentOrElse(opt -> {
epicfightSource.setInitialPosition(opt.apply(entitypatch));
}, () -> {
epicfightSource.setInitialPosition(entitypatch.getOriginal().position());
});
return epicfightSource;
}
protected void spawnHitParticle(ServerLevel world, LivingEntityPatch<?> attacker, Entity hit, Phase phase) {
Optional<RegistryObject<HitParticleType>> particleOptional = phase.getProperty(AttackPhaseProperty.PARTICLE);
HitParticleType particle = particleOptional.isPresent() ? particleOptional.get().get() : attacker.getWeaponHitParticle(phase.hand);
particle.spawnParticleWithArgument(world, null, null, hit, attacker.getOriginal());
}
@Override
public float getPlaySpeed(LivingEntityPatch<?> entitypatch, DynamicAnimation animation) {
if (entitypatch instanceof PlayerPatch<?> playerpatch) {
Phase phase = this.getPhaseByTime(playerpatch.getAnimator().getPlayerFor(this.getAccessor()).getElapsedTime());
float speedFactor = this.getProperty(AttackAnimationProperty.ATTACK_SPEED_FACTOR).orElse(1.0F);
Optional<Float> property = this.getProperty(AttackAnimationProperty.BASIS_ATTACK_SPEED);
float correctedSpeed = property.map((value) -> playerpatch.getAttackSpeed(phase.hand) / value).orElse(this.getTotalTime() * playerpatch.getAttackSpeed(phase.hand));
correctedSpeed = Math.round(correctedSpeed * 1000.0F) / 1000.0F;
return 1.0F + (correctedSpeed - 1.0F) * speedFactor;
}
return 1.0F;
}
@SuppressWarnings("unchecked")
public <V, A extends AttackAnimation> A addProperty(AttackPhaseProperty<V> propertyType, V value) {
return (A)this.addProperty(propertyType, value, 0);
}
@SuppressWarnings("unchecked")
public <V, A extends AttackAnimation> A addProperty(AttackPhaseProperty<V> propertyType, V value, int index) {
this.phases[index].addProperty(propertyType, value);
return (A)this;
}
public <A extends AttackAnimation> A removeProperty(AttackPhaseProperty<?> propertyType) {
return this.removeProperty(propertyType, 0);
}
@SuppressWarnings("unchecked")
public <A extends AttackAnimation> A removeProperty(AttackPhaseProperty<?> propertyType, int index) {
this.phases[index].removeProperty(propertyType);
return (A)this;
}
public Phase getPhaseByTime(float elapsedTime) {
Phase currentPhase = null;
for (Phase phase : this.phases) {
currentPhase = phase;
if (phase.end > elapsedTime) {
break;
}
}
return currentPhase;
}
public int getPhaseOrderByTime(float elapsedTime) {
int i = 0;
for (Phase phase : this.phases) {
if (phase.end > elapsedTime) {
break;
}
i++;
}
return i;
}
@Override
public Object getModifiedLinkState(StateFactor<?> factor, Object val, LivingEntityPatch<?> entitypatch, float elapsedTime) {
if (factor == EntityState.ATTACKING && elapsedTime < this.getPlaySpeed(entitypatch, this) * TiedUpRigConstants.A_TICK) {
return false;
}
return val;
}
@Override
@OnlyIn(Dist.CLIENT)
public void renderDebugging(PoseStack poseStack, MultiBufferSource buffer, LivingEntityPatch<?> entitypatch, float playbackTime, float partialTicks) {
AnimationPlayer animPlayer = entitypatch.getAnimator().getPlayerFor(this.getAccessor());
float prevElapsedTime = animPlayer.getPrevElapsedTime();
float elapsedTime = animPlayer.getElapsedTime();
Phase phase = this.getPhaseByTime(playbackTime);
for (Pair<Joint, Collider> colliderInfo : phase.colliders) {
Collider collider = colliderInfo.getSecond();
if (collider == null) {
collider = entitypatch.getColliderMatching(phase.hand);
}
collider.draw(poseStack, buffer, entitypatch, this, colliderInfo.getFirst(), prevElapsedTime, elapsedTime, partialTicks, this.getPlaySpeed(entitypatch, this));
}
}
public static class JointColliderPair extends Pair<Joint, Collider> {
public JointColliderPair(Joint first, Collider second) {
super(first, second);
}
public static JointColliderPair of(Joint joint, Collider collider) {
return new JointColliderPair(joint, collider);
}
}
public static class Phase { public static class Phase {
private final Map<AttackPhaseProperty<?>, Object> properties = Maps.newHashMap(); public Object[] getColliders() {
public final float start; return new Object[0];
public final float antic;
public final float preDelay;
public final float contact;
public final float recovery;
public final float end;
public final InteractionHand hand;
public JointColliderPair[] colliders;
//public final Joint first;
//public final Collider second;
public final boolean noStateBind;
public Phase(float start, float antic, float contact, float recovery, float end, Joint joint, Collider collider) {
this(start, antic, contact, recovery, end, InteractionHand.MAIN_HAND, joint, collider);
}
public Phase(float start, float antic, float contact, float recovery, float end, InteractionHand hand, Joint joint, Collider collider) {
this(start, antic, antic, contact, recovery, end, hand, joint, collider);
}
public Phase(float start, float antic, float preDelay, float contact, float recovery, float end, Joint joint, Collider collider) {
this(start, antic, preDelay, contact, recovery, end, InteractionHand.MAIN_HAND, joint, collider);
}
public Phase(float start, float antic, float preDelay, float contact, float recovery, float end, InteractionHand hand, Joint joint, Collider collider) {
this(start, antic, preDelay, contact, recovery, end, false, hand, joint, collider);
}
public Phase(InteractionHand hand, Joint joint, Collider collider) {
this(0, 0, 0, 0, 0, 0, true, hand, joint, collider);
}
public Phase(float start, float antic, float preDelay, float contact, float recovery, float end, boolean noStateBind, InteractionHand hand, Joint joint, Collider collider) {
this(start, antic, preDelay, contact, recovery, end, noStateBind, hand, JointColliderPair.of(joint, collider));
}
public Phase(float start, float antic, float preDelay, float contact, float recovery, float end, InteractionHand hand, JointColliderPair... colliders) {
this(start, antic, preDelay, contact, recovery, end, false, hand, colliders);
}
public Phase(float start, float antic, float preDelay, float contact, float recovery, float end, boolean noStateBind, InteractionHand hand, JointColliderPair... colliders) {
if (start > end) {
throw new IllegalArgumentException("Phase create exception: Start time is bigger than end time");
}
this.start = start;
this.antic = antic;
this.preDelay = preDelay;
this.contact = contact;
this.recovery = recovery;
this.end = end;
this.colliders = colliders;
this.hand = hand;
this.noStateBind = noStateBind;
}
public <V> Phase addProperty(AttackPhaseProperty<V> propertyType, V value) {
this.properties.put(propertyType, value);
return this;
}
public Phase removeProperty(AttackPhaseProperty<?> propertyType) {
this.properties.remove(propertyType);
return this;
}
public void addProperties(Set<Map.Entry<AttackPhaseProperty<?>, Object>> set) {
for(Map.Entry<AttackPhaseProperty<?>, Object> entry : set) {
this.properties.put(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
public <V> Optional<V> getProperty(AttackPhaseProperty<V> propertyType) {
return (Optional<V>) Optional.ofNullable(this.properties.get(propertyType));
}
public List<Entity> getCollidingEntities(LivingEntityPatch<?> entitypatch, AttackAnimation animation, float prevElapsedTime, float elapsedTime, float attackSpeed) {
Set<Entity> entities = Sets.newHashSet();
for (Pair<Joint, Collider> colliderInfo : this.colliders) {
Collider collider = colliderInfo.getSecond();
if (collider == null) {
collider = entitypatch.getColliderMatching(this.hand);
}
entities.addAll(collider.updateAndSelectCollideEntity(entitypatch, animation, prevElapsedTime, elapsedTime, colliderInfo.getFirst(), attackSpeed));
}
return new ArrayList<>(entities);
}
public JointColliderPair[] getColliders() {
return this.colliders;
}
public InteractionHand getHand() {
return this.hand;
} }
} }
} }

View File

@@ -6,88 +6,23 @@
package com.tiedup.remake.rig.anim.types; package com.tiedup.remake.rig.anim.types;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import com.tiedup.remake.rig.anim.AnimationManager.AnimationAccessor;
import com.tiedup.remake.rig.anim.property.AnimationProperty.StaticAnimationProperty;
import com.tiedup.remake.rig.anim.types.EntityState.StateFactor;
import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.anim.client.Layer;
import com.tiedup.remake.rig.anim.client.property.ClientAnimationProperties;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap; import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
import com.tiedup.remake.rig.patch.PlayerPatch;
import com.tiedup.remake.rig.patch.ServerPlayerPatch;
import yesman.epicfight.world.entity.eventlistener.ActionEvent;
import yesman.epicfight.world.entity.eventlistener.PlayerEventListener.EventType;
/**
* RIG stub. Upstream EF : MainFrameAnimation ajoute du scheduling combat
* via {@code ActionEvent}/{@code PlayerEventListener} (combat tick hooks).
* Strippé pour TiedUp — garde juste le type pour satisfaire les
* instanceof checks de {@link com.tiedup.remake.rig.asset.JsonAssetLoader}
* et la hiérarchie {@link ActionAnimation} / {@link AttackAnimation}.
*/
public class MainFrameAnimation extends StaticAnimation { public class MainFrameAnimation extends StaticAnimation {
public MainFrameAnimation(float convertTime, AnimationAccessor<? extends MainFrameAnimation> accessor, AssetAccessor<? extends Armature> armature) {
super(convertTime, false, accessor, armature); public MainFrameAnimation(float transitionTime, boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
super(transitionTime, isRepeat, registryName, armature);
} }
public MainFrameAnimation(float convertTime, String path, AssetAccessor<? extends Armature> armature) { public MainFrameAnimation(boolean isRepeat, String registryName, AssetAccessor<? extends Armature> armature) {
super(convertTime, false, path, armature); super(isRepeat, registryName, armature);
}
@Override
public void begin(LivingEntityPatch<?> entitypatch) {
if (entitypatch.getAnimator().getPlayerFor(null).getAnimation().get() == this) {
TypeFlexibleHashMap<StateFactor<?>> stateMap = this.stateSpectrum.getStateMap(entitypatch, 0.0F);
TypeFlexibleHashMap<StateFactor<?>> modifiedStateMap = new TypeFlexibleHashMap<> (false);
stateMap.forEach((k, v) -> modifiedStateMap.put(k, this.getModifiedLinkState(k, v, entitypatch, 0.0F)));
entitypatch.updateEntityState(new EntityState(modifiedStateMap));
}
if (entitypatch.isLogicalClient()) {
entitypatch.updateMotion(false);
this.getProperty(StaticAnimationProperty.RESET_LIVING_MOTION).ifPresentOrElse(livingMotion -> {
entitypatch.getClientAnimator().forceResetBeforeAction(livingMotion, livingMotion);
}, () -> {
entitypatch.getClientAnimator().resetMotion(true);
entitypatch.getClientAnimator().resetCompositeMotion();
});
entitypatch.getClientAnimator().getPlayerFor(this.getAccessor()).setReversed(false);
}
super.begin(entitypatch);
if (entitypatch instanceof PlayerPatch<?> playerpatch) {
if (playerpatch.isLogicalClient()) {
if (playerpatch.getOriginal().isLocalPlayer()) {
playerpatch.getEventListener().triggerEvents(EventType.ACTION_EVENT_CLIENT, new ActionEvent<>(playerpatch, this.getAccessor()));
}
} else {
ActionEvent<ServerPlayerPatch> actionEvent = new ActionEvent<>(playerpatch, this.getAccessor());
playerpatch.getEventListener().triggerEvents(EventType.ACTION_EVENT_SERVER, actionEvent);
if (actionEvent.shouldResetActionTick()) {
playerpatch.resetActionTick();
}
}
}
}
@Override
public void tick(LivingEntityPatch<?> entitypatch) {
super.tick(entitypatch);
if (entitypatch.getEntityState().movementLocked()) {
entitypatch.getOriginal().walkAnimation.setSpeed(0);
}
}
@Override
public boolean isMainFrameAnimation() {
return true;
}
@Override
@OnlyIn(Dist.CLIENT)
public Layer.Priority getPriority() {
return this.getProperty(ClientAnimationProperties.PRIORITY).orElse(Layer.Priority.HIGHEST);
} }
} }

View File

@@ -0,0 +1,288 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.anim.types;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.tiedup.remake.rig.anim.types.EntityState.StateFactor;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
public class StateSpectrum {
private final Set<StatesInTime> timePairs = Sets.newHashSet();
void readFrom(StateSpectrum.Blueprint blueprint) {
this.timePairs.clear();
this.timePairs.addAll(blueprint.timePairs);
}
@SuppressWarnings("unchecked")
public <T> T getSingleState(StateFactor<T> stateFactor, LivingEntityPatch<?> entitypatch, float time) {
for (StatesInTime state : this.timePairs) {
if (state.isIn(entitypatch, time)) {
for (Map.Entry<StateFactor<?>, ?> timeEntry : state.getStates(entitypatch)) {
if (timeEntry.getKey() == stateFactor) {
return (T) timeEntry.getValue();
}
}
}
}
return stateFactor.defaultValue();
}
public TypeFlexibleHashMap<StateFactor<?>> getStateMap(LivingEntityPatch<?> entitypatch, float time) {
TypeFlexibleHashMap<StateFactor<?>> stateMap = new TypeFlexibleHashMap<>(true);
for (StatesInTime state : this.timePairs) {
if (state.isIn(entitypatch, time)) {
for (Map.Entry<StateFactor<?>, ?> timeEntry : state.getStates(entitypatch)) {
stateMap.put(timeEntry.getKey(), timeEntry.getValue());
}
}
}
return stateMap;
}
abstract static class StatesInTime {
public abstract Set<Map.Entry<StateFactor<?>, Object>> getStates(LivingEntityPatch<?> entitypatch);
public abstract void removeState(StateFactor<?> state);
public abstract boolean hasState(StateFactor<?> state);
public abstract boolean isIn(LivingEntityPatch<?> entitypatch, float time);
}
static class SimpleStatesInTime extends StatesInTime {
float start;
float end;
Map<StateFactor<?>, Object> states = Maps.newHashMap();
public SimpleStatesInTime(float start, float end) {
this.start = start;
this.end = end;
}
@Override
public boolean isIn(LivingEntityPatch<?> entitypatch, float time) {
return this.start <= time && this.end > time;
}
public <T> StatesInTime addState(StateFactor<T> factor, T val) {
this.states.put(factor, val);
return this;
}
@Override
public Set<Map.Entry<StateFactor<?>, Object>> getStates(LivingEntityPatch<?> entitypatch) {
return this.states.entrySet();
}
@Override
public boolean hasState(StateFactor<?> state) {
return this.states.containsKey(state);
}
@Override
public void removeState(StateFactor<?> state) {
this.states.remove(state);
}
@Override
public String toString() {
return String.format("Time: %.2f ~ %.2f, States: %s", this.start, this.end, this.states);
}
}
static class ConditionalStatesInTime extends StatesInTime {
float start;
float end;
Int2ObjectMap<Map<StateFactor<?>, Object>> conditionalStates = new Int2ObjectOpenHashMap<>();
Function<LivingEntityPatch<?>, Integer> condition;
public ConditionalStatesInTime(Function<LivingEntityPatch<?>, Integer> condition, float start, float end) {
this.start = start;
this.end = end;
this.condition = condition;
}
public <T> StatesInTime addConditionalState(int metadata, StateFactor<T> factor, T val) {
Map<StateFactor<?>, Object> states = this.conditionalStates.computeIfAbsent(metadata, (key) -> Maps.newHashMap());
states.put(factor, val);
return this;
}
@SuppressWarnings("deprecation")
@Override
public Set<Map.Entry<StateFactor<?>, Object>> getStates(LivingEntityPatch<?> entitypatch) {
return this.conditionalStates.get(this.condition.apply(entitypatch)).entrySet();
}
@Override
public boolean isIn(LivingEntityPatch<?> entitypatch, float time) {
return this.start <= time && this.end > time;
}
@Override
public boolean hasState(StateFactor<?> state) {
boolean hasState = false;
for (Map<StateFactor<?>, Object> states : this.conditionalStates.values()) {
hasState |= states.containsKey(state);
}
return hasState;
}
@Override
public void removeState(StateFactor<?> state) {
for (Map<StateFactor<?>, Object> states : this.conditionalStates.values()) {
states.remove(state);
}
}
@SuppressWarnings("deprecation")
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Time: %.2f ~ %.2f, ", this.start, this.end));
int entryCnt = 0;
for (Map.Entry<Integer, Map<StateFactor<?>, Object>> entry : this.conditionalStates.entrySet()) {
sb.append(String.format("States %d: %s", entry.getKey(), entry.getValue()));
entryCnt++;
if (entryCnt < this.conditionalStates.size()) {
sb.append(", ");
}
}
return sb.toString();
}
}
static class VariableStatesInTime extends StatesInTime {
Function<LivingEntityPatch<?>, Float> variableStart;
Function<LivingEntityPatch<?>, Float> variableEnd;
Map<StateFactor<?>, Object> states = Maps.newHashMap();
public VariableStatesInTime(Function<LivingEntityPatch<?>, Float> variableStart, Function<LivingEntityPatch<?>, Float> variableEnd) {
this.variableStart = variableStart;
this.variableEnd = variableEnd;
}
@Override
public boolean isIn(LivingEntityPatch<?> entitypatch, float time) {
return this.variableStart.apply(entitypatch) <= time && this.variableEnd.apply(entitypatch) > time;
}
public <T> StatesInTime addState(StateFactor<T> factor, T val) {
this.states.put(factor, val);
return this;
}
@Override
public Set<Map.Entry<StateFactor<?>, Object>> getStates(LivingEntityPatch<?> entitypatch) {
return this.states.entrySet();
}
@Override
public boolean hasState(StateFactor<?> state) {
return this.states.containsKey(state);
}
@Override
public void removeState(StateFactor<?> state) {
this.states.remove(state);
}
@Override
public String toString() {
return String.format("States: %s", this.states);
}
}
public static class Blueprint {
StatesInTime currentState;
Set<StatesInTime> timePairs = Sets.newHashSet();
public Blueprint newTimePair(float start, float end) {
this.currentState = new SimpleStatesInTime(start, end);
this.timePairs.add(this.currentState);
return this;
}
public Blueprint newConditionalTimePair(Function<LivingEntityPatch<?>, Integer> condition, float start, float end) {
this.currentState = new ConditionalStatesInTime(condition, start, end);
this.timePairs.add(this.currentState);
return this;
}
public Blueprint newVariableTimePair(Function<LivingEntityPatch<?>, Float> variableStart, Function<LivingEntityPatch<?>, Float> variableEnd) {
this.currentState = new VariableStatesInTime(variableStart, variableEnd);
this.timePairs.add(this.currentState);
return this;
}
public <T> Blueprint addState(StateFactor<T> factor, T val) {
if (this.currentState instanceof SimpleStatesInTime simpleState) {
simpleState.addState(factor, val);
}
if (this.currentState instanceof VariableStatesInTime variableState) {
variableState.addState(factor, val);
}
return this;
}
public <T> Blueprint addConditionalState(int metadata, StateFactor<T> factor, T val) {
if (this.currentState instanceof ConditionalStatesInTime conditionalState) {
conditionalState.addConditionalState(metadata, factor, val);
}
return this;
}
public <T> Blueprint removeState(StateFactor<T> factor) {
for (StatesInTime timePair : this.timePairs) {
timePair.removeState(factor);
}
return this;
}
public <T> Blueprint addStateRemoveOld(StateFactor<T> factor, T val) {
this.removeState(factor);
return this.addState(factor, val);
}
public <T> Blueprint addStateIfNotExist(StateFactor<T> factor, T val) {
for (StatesInTime timePair : this.timePairs) {
if (timePair.hasState(factor)) {
return this;
}
}
return this.addState(factor, val);
}
public Blueprint clear() {
this.currentState = null;
this.timePairs.clear();
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (StatesInTime state : this.timePairs) {
sb.append(state + "\n");
}
return sb.toString();
}
}
}

View File

@@ -52,25 +52,19 @@ import com.tiedup.remake.rig.anim.client.property.JointMaskEntry;
import com.tiedup.remake.rig.anim.client.property.TrailInfo; import com.tiedup.remake.rig.anim.client.property.TrailInfo;
import com.tiedup.remake.rig.exception.AssetLoadingException; import com.tiedup.remake.rig.exception.AssetLoadingException;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import yesman.epicfight.api.physics.ik.InverseKinematicsProvider; import com.tiedup.remake.rig.physics.ik.InverseKinematicsProvider;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulatable; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulatable;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulator; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulator.BakedInverseKinematicsDefinition; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.BakedInverseKinematicsDefinition;
import yesman.epicfight.api.physics.ik.InverseKinematicsSimulator.InverseKinematicsObject; import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.InverseKinematicsObject;
import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap; import com.tiedup.remake.rig.util.datastruct.TypeFlexibleHashMap;
import com.tiedup.remake.rig.math.OpenMatrix4f; import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f; import com.tiedup.remake.rig.math.Vec3f;
import yesman.epicfight.client.ClientEngine;
import com.tiedup.remake.rig.render.TiedUpRenderTypes; import com.tiedup.remake.rig.render.TiedUpRenderTypes;
import yesman.epicfight.client.renderer.RenderingTool;
import com.tiedup.remake.rig.render.item.RenderItemBase;
import com.tiedup.remake.rig.TiedUpRigRegistry; import com.tiedup.remake.rig.TiedUpRigRegistry;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;
import com.tiedup.remake.rig.patch.LivingEntityPatch; import com.tiedup.remake.rig.patch.LivingEntityPatch;
import com.tiedup.remake.rig.patch.PlayerPatch; import com.tiedup.remake.rig.patch.PlayerPatch;
import yesman.epicfight.world.entity.eventlistener.AnimationBeginEvent;
import yesman.epicfight.world.entity.eventlistener.AnimationEndEvent;
import yesman.epicfight.world.entity.eventlistener.PlayerEventListener.EventType;
public class StaticAnimation extends DynamicAnimation implements InverseKinematicsProvider { public class StaticAnimation extends DynamicAnimation implements InverseKinematicsProvider {
public static final IndependentAnimationVariableKey<Boolean> HAD_NO_PHYSICS = AnimationVariables.independent((animator) -> false, true); public static final IndependentAnimationVariableKey<Boolean> HAD_NO_PHYSICS = AnimationVariables.independent((animator) -> false, true);
@@ -271,13 +265,9 @@ public class StaticAnimation extends DynamicAnimation implements InverseKinemati
double jointId = Double.longBitsToDouble((long)this.armature.get().searchJointByName(trailInfo.joint()).getId()); double jointId = Double.longBitsToDouble((long)this.armature.get().searchJointByName(trailInfo.joint()).getId());
double index = Double.longBitsToDouble((long)idx++); double index = Double.longBitsToDouble((long)idx++);
if (trailInfo.hand() != null) { // RIG : RenderItemBase (combat weapon item render) strippé —
RenderItemBase renderitembase = ClientEngine.getInstance().renderEngine.getItemRenderer(entitypatch.getAdvancedHoldingItemStack(trailInfo.hand())); // TiedUp n'a pas d'items "actifs" porteurs de trails comme
// les weapons EF. Le trailInfo reste tel quel de la définition.
if (renderitembase != null && renderitembase.trailInfo() != null) {
trailInfo = renderitembase.trailInfo().overwrite(trailInfo);
}
}
if (!trailInfo.playable()) { if (!trailInfo.playable()) {
continue; continue;
@@ -294,16 +284,13 @@ public class StaticAnimation extends DynamicAnimation implements InverseKinemati
} }
}); });
if (entitypatch instanceof PlayerPatch<?> playerpatch) { // RIG : PlayerEventListener (combat animation events) strippé.
playerpatch.getEventListener().triggerEvents(EventType.ANIMATION_BEGIN_EVENT, new AnimationBeginEvent(playerpatch, this)); // Les ON_BEGIN_EVENTS SimpleEvent continuent de fonctionner ci-dessus.
}
} }
@Override @Override
public void end(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> nextAnimation, boolean isEnd) { public void end(LivingEntityPatch<?> entitypatch, AssetAccessor<? extends DynamicAnimation> nextAnimation, boolean isEnd) {
if (entitypatch instanceof PlayerPatch<?> playerpatch) { // RIG : ANIMATION_END_EVENT fire strippé (PlayerEventListener combat).
playerpatch.getEventListener().triggerEvents(EventType.ANIMATION_END_EVENT, new AnimationEndEvent(playerpatch, this, isEnd));
}
this.getProperty(StaticAnimationProperty.ON_END_EVENTS).ifPresent((events) -> { this.getProperty(StaticAnimationProperty.ON_END_EVENTS).ifPresent((events) -> {
for (SimpleEvent<?> event : events) { for (SimpleEvent<?> event : events) {
@@ -604,38 +591,9 @@ public class StaticAnimation extends DynamicAnimation implements InverseKinemati
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public void renderDebugging(PoseStack poseStack, MultiBufferSource buffer, LivingEntityPatch<?> entitypatch, float playTime, float partialTicks) { public void renderDebugging(PoseStack poseStack, MultiBufferSource buffer, LivingEntityPatch<?> entitypatch, float playTime, float partialTicks) {
if (entitypatch instanceof InverseKinematicsSimulatable ikSimulatable) { // RIG : debug render des targets IK (RenderingTool.drawQuad) strippé.
this.getProperty(StaticAnimationProperty.BAKED_IK_DEFINITION).ifPresent((ikDefinitions) -> { // Pas d'IK en TiedUp. Reactivable Phase 2+ avec un helper drawQuad
OpenMatrix4f modelmat = ikSimulatable.getModelMatrix(partialTicks); // simple si on veut debug les joints.
LivingEntity originalEntity = entitypatch.getOriginal();
Vec3 entitypos = originalEntity.position();
float x = (float)entitypos.x;
float y = (float)entitypos.y;
float z = (float)entitypos.z;
float xo = (float)originalEntity.xo;
float yo = (float)originalEntity.yo;
float zo = (float)originalEntity.zo;
OpenMatrix4f toModelPos = OpenMatrix4f.mul(OpenMatrix4f.createTranslation(xo + (x - xo) * partialTicks, yo + (y - yo) * partialTicks, zo + (z - zo) * partialTicks), modelmat, null).invert();
for (BakedInverseKinematicsDefinition bakedIKInfo : this.getProperty(StaticAnimationProperty.BAKED_IK_DEFINITION).orElse(null)) {
ikSimulatable.getIKSimulator().getRunningObject(bakedIKInfo.endJoint()).ifPresent((ikObjet) -> {
VertexConsumer vertexBuilder = buffer.getBuffer(TiedUpRenderTypes.debugQuads());
Vec3f worldtargetpos = ikObjet.getDestination();
Vec3f modeltargetpos = OpenMatrix4f.transform3v(toModelPos, worldtargetpos, null).multiply(-1.0F, 1.0F, -1.0F);
RenderingTool.drawQuad(poseStack, vertexBuilder, modeltargetpos, 0.5F, 1.0F, 0.0F, 0.0F);
Vec3f jointWorldPos = ikObjet.getTipPosition(partialTicks);
Vec3f jointModelpos = OpenMatrix4f.transform3v(toModelPos, jointWorldPos, null);
RenderingTool.drawQuad(poseStack, vertexBuilder, jointModelpos.multiply(-1.0F, 1.0F, -1.0F), 0.4F, 0.0F, 0.0F, 1.0F);
Pose pose = new Pose();
for (String jointName : this.getTransfroms().keySet()) {
pose.putJointData(jointName, this.getTransfroms().get(jointName).getInterpolatedTransform(playTime));
}
});
}
});
}
} }
@Override @Override

View File

@@ -4,7 +4,7 @@
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3. * Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/ */
package com.tiedup.remake.rig.anim; package com.tiedup.remake.rig.armature;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;

View File

@@ -4,7 +4,7 @@
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3. * Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/ */
package com.tiedup.remake.rig.anim; package com.tiedup.remake.rig.armature;
import java.util.Map; import java.util.Map;

View File

@@ -15,10 +15,10 @@ import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import yesman.epicfight.api.physics.PhysicsSimulator; import com.tiedup.remake.rig.physics.PhysicsSimulator;
import yesman.epicfight.api.physics.SimulationObject; import com.tiedup.remake.rig.physics.SimulationObject;
import yesman.epicfight.api.physics.SimulationObject.SimulationObjectBuilder; import com.tiedup.remake.rig.physics.SimulationObject.SimulationObjectBuilder;
import yesman.epicfight.api.physics.SimulationProvider; import com.tiedup.remake.rig.physics.SimulationProvider;
public abstract class AbstractSimulator<KEY, B extends SimulationObjectBuilder, PV extends SimulationProvider<O, SO, B, PV>, O, SO extends SimulationObject<B, PV, O>> implements PhysicsSimulator<KEY, B, PV, O, SO> { public abstract class AbstractSimulator<KEY, B extends SimulationObjectBuilder, PV extends SimulationProvider<O, SO, B, PV>, O, SO extends SimulationObject<B, PV, O>> implements PhysicsSimulator<KEY, B, PV, O, SO> {
protected Map<KEY, ObjectWrapper> simulationObjects = Maps.newHashMap(); protected Map<KEY, ObjectWrapper> simulationObjects = Maps.newHashMap();

View File

@@ -11,7 +11,7 @@ import javax.annotation.Nullable;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import com.tiedup.remake.rig.anim.Animator; import com.tiedup.remake.rig.anim.Animator;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import yesman.epicfight.api.physics.SimulatableObject; import com.tiedup.remake.rig.physics.SimulatableObject;
public interface ClothSimulatable extends SimulatableObject { public interface ClothSimulatable extends SimulatableObject {
@Nullable @Nullable

View File

@@ -48,9 +48,9 @@ import com.tiedup.remake.rig.mesh.SoftBodyTranslatable;
import com.tiedup.remake.rig.mesh.VertexBuilder; import com.tiedup.remake.rig.mesh.VertexBuilder;
import com.tiedup.remake.rig.cloth.AbstractSimulator; import com.tiedup.remake.rig.cloth.AbstractSimulator;
import com.tiedup.remake.rig.cloth.ClothSimulator.ClothObjectBuilder; import com.tiedup.remake.rig.cloth.ClothSimulator.ClothObjectBuilder;
import yesman.epicfight.api.collider.OBBCollider; import com.tiedup.remake.rig.collider.OBBCollider;
import com.tiedup.remake.rig.armature.Armature; import com.tiedup.remake.rig.armature.Armature;
import yesman.epicfight.api.physics.SimulationObject; import com.tiedup.remake.rig.physics.SimulationObject;
import com.tiedup.remake.rig.math.MathUtils; import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f; import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f; import com.tiedup.remake.rig.math.Vec3f;

View File

@@ -0,0 +1,81 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.collider;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f;
/**
* OBB géométrique forké depuis EF, strippé de la logique combat
* (isCollide(Entity), drawInternal, updateAndSelectCollideEntity).
* Ne garde que les données + transform() utilisés par ClothOBBCollider.
*/
public class OBBCollider {
protected final Vec3 modelCenter;
protected final AABB outerAABB;
protected Vec3 worldCenter;
protected final Vec3[] modelVertices;
protected final Vec3[] modelNormals;
protected Vec3[] rotatedVertices;
protected Vec3[] rotatedNormals;
protected Vec3f scale = new Vec3f(1.0F, 1.0F, 1.0F);
public OBBCollider(double vertexX, double vertexY, double vertexZ, double centerX, double centerY, double centerZ) {
this(getInitialAABB(vertexX, vertexY, vertexZ, centerX, centerY, centerZ), vertexX, vertexY, vertexZ, centerX, centerY, centerZ);
}
protected OBBCollider(AABB outerAABB, double vertexX, double vertexY, double vertexZ, double centerX, double centerY, double centerZ) {
this.modelCenter = new Vec3(centerX, centerY, centerZ);
this.outerAABB = outerAABB;
this.worldCenter = new Vec3(0.0D, 0.0D, 0.0D);
this.modelVertices = new Vec3[4];
this.modelNormals = new Vec3[3];
this.rotatedVertices = new Vec3[4];
this.rotatedNormals = new Vec3[3];
this.modelVertices[0] = new Vec3(vertexX, vertexY, -vertexZ);
this.modelVertices[1] = new Vec3(vertexX, vertexY, vertexZ);
this.modelVertices[2] = new Vec3(-vertexX, vertexY, vertexZ);
this.modelVertices[3] = new Vec3(-vertexX, vertexY, -vertexZ);
this.modelNormals[0] = new Vec3(1, 0, 0);
this.modelNormals[1] = new Vec3(0, 1, 0);
this.modelNormals[2] = new Vec3(0, 0, 1);
this.rotatedVertices[0] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedVertices[1] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedVertices[2] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedVertices[3] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedNormals[0] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedNormals[1] = new Vec3(0.0D, 0.0D, 0.0D);
this.rotatedNormals[2] = new Vec3(0.0D, 0.0D, 0.0D);
}
static AABB getInitialAABB(double posX, double posY, double posZ, double center_x, double center_y, double center_z) {
double xLength = Math.abs(posX) + Math.abs(center_x);
double yLength = Math.abs(posY) + Math.abs(center_y);
double zLength = Math.abs(posZ) + Math.abs(center_z);
double maxLength = Math.max(xLength, Math.max(yLength, zLength));
return new AABB(maxLength, maxLength, maxLength, -maxLength, -maxLength, -maxLength);
}
public void transform(OpenMatrix4f modelMatrix) {
OpenMatrix4f noTranslation = modelMatrix.removeTranslation();
for (int i = 0; i < this.modelVertices.length; i++) {
this.rotatedVertices[i] = OpenMatrix4f.transform(noTranslation, this.modelVertices[i]);
}
for (int i = 0; i < this.modelNormals.length; i++) {
this.rotatedNormals[i] = OpenMatrix4f.transform(noTranslation, this.modelNormals[i]);
}
this.scale = noTranslation.toScaleVector();
this.worldCenter = OpenMatrix4f.transform(modelMatrix, this.modelCenter);
}
}

View File

@@ -34,8 +34,8 @@ import com.tiedup.remake.rig.util.ParseUtil;
import com.tiedup.remake.rig.math.OpenMatrix4f; import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec4f; import com.tiedup.remake.rig.math.Vec4f;
import com.tiedup.remake.rig.render.TiedUpRenderTypes; import com.tiedup.remake.rig.render.TiedUpRenderTypes;
import yesman.epicfight.client.renderer.shader.compute.ComputeShaderSetup; import com.tiedup.remake.rig.render.compute.ComputeShaderSetup;
import yesman.epicfight.client.renderer.shader.compute.loader.ComputeShaderProvider; import com.tiedup.remake.rig.render.compute.ComputeShaderProvider;
import com.tiedup.remake.rig.TiedUpAnimationConfig; import com.tiedup.remake.rig.TiedUpAnimationConfig;
import com.tiedup.remake.rig.TiedUpRigConstants; import com.tiedup.remake.rig.TiedUpRigConstants;

View File

@@ -15,7 +15,7 @@ import com.tiedup.remake.rig.mesh.Meshes.MeshAccessor;
import com.tiedup.remake.rig.cloth.ClothSimulatable; import com.tiedup.remake.rig.cloth.ClothSimulatable;
import com.tiedup.remake.rig.cloth.ClothSimulator; import com.tiedup.remake.rig.cloth.ClothSimulator;
import com.tiedup.remake.rig.cloth.ClothSimulator.ClothObject.ClothPart.ConstraintType; import com.tiedup.remake.rig.cloth.ClothSimulator.ClothObject.ClothPart.ConstraintType;
import yesman.epicfight.api.physics.SimulationProvider; import com.tiedup.remake.rig.physics.SimulationProvider;
public interface SoftBodyTranslatable extends SimulationProvider<ClothSimulatable, ClothSimulator.ClothObject, ClothSimulator.ClothObjectBuilder, SoftBodyTranslatable> { public interface SoftBodyTranslatable extends SimulationProvider<ClothSimulatable, ClothSimulator.ClothObject, ClothSimulator.ClothObjectBuilder, SoftBodyTranslatable> {
public static final List<ClothSimulatable> TRACKING_SIMULATION_SUBJECTS = Lists.newArrayList(); public static final List<ClothSimulatable> TRACKING_SIMULATION_SUBJECTS = Lists.newArrayList();

View File

@@ -1,479 +0,0 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch;
import java.util.Optional;
import org.joml.Vector4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PlayerRideableJumping;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingEvent;
import com.tiedup.remake.rig.anim.Animator;
import com.tiedup.remake.rig.armature.JointTransform;
import com.tiedup.remake.rig.anim.LivingMotion;
import com.tiedup.remake.rig.anim.LivingMotions;
import com.tiedup.remake.rig.anim.Pose;
import com.tiedup.remake.rig.anim.property.AnimationProperty.StaticAnimationProperty;
import com.tiedup.remake.rig.anim.types.ActionAnimation;
import com.tiedup.remake.rig.anim.types.DynamicAnimation;
import com.tiedup.remake.rig.anim.client.ClientAnimator;
import com.tiedup.remake.rig.anim.client.Layer;
import com.tiedup.remake.rig.event.RenderEpicFightPlayerEvent;
import com.tiedup.remake.rig.event.UpdatePlayerMotionEvent;
import yesman.epicfight.client.online.EpicSkins;
import com.tiedup.remake.rig.cloth.ClothSimulatable;
import com.tiedup.remake.rig.cloth.ClothSimulator;
import yesman.epicfight.api.physics.PhysicsSimulator;
import yesman.epicfight.api.physics.SimulationTypes;
import com.tiedup.remake.rig.util.EntitySnapshot;
import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f;
import com.tiedup.remake.rig.math.Vec3f;
import com.tiedup.remake.rig.TiedUpAnimationConfig;
import yesman.epicfight.gameasset.EpicFightSounds;
import yesman.epicfight.network.EntityPairingPacketTypes;
import yesman.epicfight.network.server.SPEntityPairingPacket;
import yesman.epicfight.particle.EpicFightParticles;
import com.tiedup.remake.rig.patch.EntityDecorations;
import com.tiedup.remake.rig.patch.EntityDecorations.RenderAttributeModifier;
import com.tiedup.remake.rig.patch.PlayerPatch;
import com.tiedup.remake.rig.patch.item.CapabilityItem;
import yesman.epicfight.world.entity.eventlistener.PlayerEventListener.EventType;
public class AbstractClientPlayerPatch<T extends AbstractClientPlayer> extends PlayerPatch<T> implements ClothSimulatable {
private Item prevHeldItem;
private Item prevHeldItemOffHand;
protected EpicSkins epicSkinsInformation;
@Override
public void onJoinWorld(T entity, EntityJoinLevelEvent event) {
super.onJoinWorld(entity, event);
this.prevHeldItem = Items.AIR;
this.prevHeldItemOffHand = Items.AIR;
EpicSkins.initEpicSkins(this);
}
@Override
public void updateMotion(boolean considerInaction) {
if (this.original.getHealth() <= 0.0F) {
currentLivingMotion = LivingMotions.DEATH;
} else if (!this.state.updateLivingMotion() && considerInaction) {
currentLivingMotion = LivingMotions.INACTION;
} else {
if (original.isFallFlying() || original.isAutoSpinAttack()) {
currentLivingMotion = LivingMotions.FLY;
} else if (original.getVehicle() != null) {
if (original.getVehicle() instanceof PlayerRideableJumping)
currentLivingMotion = LivingMotions.MOUNT;
else
currentLivingMotion = LivingMotions.SIT;
} else if (original.isVisuallySwimming()) {
currentLivingMotion = LivingMotions.SWIM;
} else if (original.isSleeping()) {
currentLivingMotion = LivingMotions.SLEEP;
} else if (!original.onGround() && original.onClimbable()) {
currentLivingMotion = LivingMotions.CLIMB;
} else if (!original.getAbilities().flying) {
ClientAnimator animator = this.getClientAnimator();
if (original.isUnderWater() && (original.getY() - this.yo) < -0.005)
currentLivingMotion = LivingMotions.FLOAT;
else if (original.getY() - this.yo < -0.4F || this.isAirborneState())
currentLivingMotion = LivingMotions.FALL;
else if (this.isMoving()) {
if (original.isCrouching())
currentLivingMotion = LivingMotions.SNEAK;
else if (original.isSprinting())
currentLivingMotion = LivingMotions.RUN;
else
currentLivingMotion = LivingMotions.WALK;
animator.baseLayer.animationPlayer.setReversed(this.dz < 0);
} else {
animator.baseLayer.animationPlayer.setReversed(false);
if (original.isCrouching())
currentLivingMotion = LivingMotions.KNEEL;
else
currentLivingMotion = LivingMotions.IDLE;
}
} else {
if (this.isMoving())
currentLivingMotion = LivingMotions.CREATIVE_FLY;
else
currentLivingMotion = LivingMotions.CREATIVE_IDLE;
}
}
UpdatePlayerMotionEvent.BaseLayer baseLayerEvent = new UpdatePlayerMotionEvent.BaseLayer(this, this.currentLivingMotion, !this.state.updateLivingMotion() && considerInaction);
this.eventListeners.triggerEvents(EventType.UPDATE_BASE_LIVING_MOTION_EVENT, baseLayerEvent);
MinecraftForge.EVENT_BUS.post(baseLayerEvent);
this.currentLivingMotion = baseLayerEvent.getMotion();
if (!this.state.updateLivingMotion() && considerInaction) {
this.currentCompositeMotion = LivingMotions.NONE;
} else {
CapabilityItem mainhandItemCap = this.getHoldingItemCapability(InteractionHand.MAIN_HAND);
CapabilityItem offhandItemCap = this.getHoldingItemCapability(InteractionHand.OFF_HAND);
LivingMotion customLivingMotion = mainhandItemCap.getLivingMotion(this, InteractionHand.MAIN_HAND);
if (customLivingMotion == null) customLivingMotion = offhandItemCap.getLivingMotion(this, InteractionHand.OFF_HAND);
// When item capabilities has custom living motion
if (customLivingMotion != null)
currentCompositeMotion = customLivingMotion;
else if (this.original.isUsingItem()) {
UseAnim useAnim = this.original.getUseItem().getUseAnimation();
if (useAnim == UseAnim.BLOCK)
currentCompositeMotion = LivingMotions.BLOCK_SHIELD;
else if (useAnim == UseAnim.CROSSBOW)
currentCompositeMotion = LivingMotions.RELOAD;
else if (useAnim == UseAnim.DRINK)
currentCompositeMotion = LivingMotions.DRINK;
else if (useAnim == UseAnim.EAT)
currentCompositeMotion = LivingMotions.EAT;
else if (useAnim == UseAnim.SPYGLASS)
currentCompositeMotion = LivingMotions.SPECTATE;
else
currentCompositeMotion = currentLivingMotion;
} else {
if (this.getClientAnimator().getCompositeLayer(Layer.Priority.MIDDLE).animationPlayer.getRealAnimation().get().isReboundAnimation())
currentCompositeMotion = LivingMotions.SHOT;
else if (this.original.swinging && this.original.getSleepingPos().isEmpty())
currentCompositeMotion = LivingMotions.DIGGING;
else
currentCompositeMotion = currentLivingMotion;
}
UpdatePlayerMotionEvent.CompositeLayer compositeLayerEvent = new UpdatePlayerMotionEvent.CompositeLayer(this, this.currentCompositeMotion);
this.eventListeners.triggerEvents(EventType.UPDATE_COMPOSITE_LIVING_MOTION_EVENT, compositeLayerEvent);
MinecraftForge.EVENT_BUS.post(compositeLayerEvent);
this.currentCompositeMotion = compositeLayerEvent.getMotion();
}
}
@Override
public void onOldPosUpdate() {
this.modelYRotO2 = this.modelYRotO;
this.xPosO2 = (float)this.original.xOld;
this.yPosO2 = (float)this.original.yOld;
this.zPosO2 = (float)this.original.zOld;
}
@Override
protected void clientTick(LivingEvent.LivingTickEvent event) {
this.xCloakO2 = this.original.xCloakO;
this.yCloakO2 = this.original.yCloakO;
this.zCloakO2 = this.original.zCloakO;
super.clientTick(event);
if (!this.getEntityState().updateLivingMotion()) {
this.original.yBodyRot = this.original.yHeadRot;
}
boolean isMainHandChanged = this.prevHeldItem != this.original.getInventory().getSelected().getItem();
boolean isOffHandChanged = this.prevHeldItemOffHand != this.original.getInventory().offhand.get(0).getItem();
if (isMainHandChanged || isOffHandChanged) {
this.updateHeldItem(this.getHoldingItemCapability(InteractionHand.MAIN_HAND), this.getHoldingItemCapability(InteractionHand.OFF_HAND));
if (isMainHandChanged) {
this.prevHeldItem = this.original.getInventory().getSelected().getItem();
}
if (isOffHandChanged) {
this.prevHeldItemOffHand = this.original.getInventory().offhand.get(0).getItem();
}
}
/** {@link LivingDeathEvent} never fired for client players **/
if (this.original.deathTime == 1) {
this.getClientAnimator().playDeathAnimation();
}
this.clothSimulator.tick(this);
}
protected boolean isMoving() {
return Math.abs(this.dx) > 0.01F || Math.abs(this.dz) > 0.01F;
}
public void updateHeldItem(CapabilityItem mainHandCap, CapabilityItem offHandCap) {
this.cancelItemUse();
this.getClientAnimator().iterAllLayers((layer) -> {
if (layer.isOff()) {
return;
}
layer.animationPlayer.getRealAnimation().get().getProperty(StaticAnimationProperty.ON_ITEM_CHANGE_EVENT).ifPresent((event) -> {
event.params(mainHandCap, offHandCap);
event.execute(this, layer.animationPlayer.getRealAnimation(), layer.animationPlayer.getPrevElapsedTime(), layer.animationPlayer.getElapsedTime());
});
});
}
// RIG : entityPairing() EF = hook combat skills (Technician, Adrenaline,
// Emergency Escape). Strippé intégralement — ces skills n'existent pas
// en TiedUp. Le super.entityPairing() de base reste disponible si jamais
// une extension TiedUp veut hooker des paires custom.
@Override
public boolean overrideRender() {
RenderEpicFightPlayerEvent renderepicfightplayerevent = new RenderEpicFightPlayerEvent(this, !TiedUpAnimationConfig.enableOriginalModel || this.isEpicFightMode());
MinecraftForge.EVENT_BUS.post(renderepicfightplayerevent);
return renderepicfightplayerevent.getShouldRender();
}
@Override
public boolean shouldMoveOnCurrentSide(ActionAnimation actionAnimation) {
return false;
}
@Override
public void poseTick(DynamicAnimation animation, Pose pose, float elapsedTime, float partialTick) {
if (pose.hasTransform("Head") && this.armature.hasJoint("Head")) {
if (animation.doesHeadRotFollowEntityHead()) {
float headRelativeRot = Mth.rotLerp(partialTick, Mth.wrapDegrees(this.modelYRotO - this.original.yHeadRotO), Mth.wrapDegrees(this.modelYRot - this.original.yHeadRot));
OpenMatrix4f headTransform = this.armature.getBoundTransformFor(pose, this.armature.searchJointByName("Head"));
OpenMatrix4f toOriginalRotation = headTransform.removeScale().removeTranslation().invert();
Vec3f xAxis = OpenMatrix4f.transform3v(toOriginalRotation, Vec3f.X_AXIS, null);
Vec3f yAxis = OpenMatrix4f.transform3v(toOriginalRotation, Vec3f.Y_AXIS, null);
OpenMatrix4f headRotation = OpenMatrix4f.createRotatorDeg(headRelativeRot, yAxis).rotateDeg(-Mth.rotLerp(partialTick, this.original.xRotO, this.original.getXRot()), xAxis);
pose.orElseEmpty("Head").frontResult(JointTransform.fromMatrix(headRotation), OpenMatrix4f::mul);
}
}
}
@Override
public OpenMatrix4f getModelMatrix(float partialTick) {
if (this.original.isAutoSpinAttack()) {
OpenMatrix4f mat = MathUtils.getModelMatrixIntegral(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0, 0, 0, 0, partialTick, PLAYER_SCALE, PLAYER_SCALE, PLAYER_SCALE);
float yRot = MathUtils.lerpBetween(this.original.yRotO, this.original.getYRot(), partialTick);
float xRot = MathUtils.lerpBetween(this.original.xRotO, this.original.getXRot(), partialTick);
mat.rotateDeg(-yRot, Vec3f.Y_AXIS)
.rotateDeg(-xRot, Vec3f.X_AXIS)
.rotateDeg((this.original.tickCount + partialTick) * -55.0F, Vec3f.Z_AXIS)
.translate(0F, -0.39F, 0F);
return mat;
} else if (this.original.isFallFlying()) {
OpenMatrix4f mat = MathUtils.getModelMatrixIntegral(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0, 0, 0, 0, partialTick, PLAYER_SCALE, PLAYER_SCALE, PLAYER_SCALE);
float f1 = (float)this.original.getFallFlyingTicks() + partialTick;
float f2 = Mth.clamp(f1 * f1 / 100.0F, 0.0F, 1.0F);
mat.rotateDeg(-Mth.rotLerp(partialTick, this.original.yBodyRotO, this.original.yBodyRot), Vec3f.Y_AXIS).rotateDeg(f2 * (-this.original.getXRot()), Vec3f.X_AXIS);
Vec3 vec3d = this.original.getViewVector(partialTick);
Vec3 vec3d1 = this.original.getDeltaMovementLerped(partialTick);
double d0 = vec3d1.horizontalDistanceSqr();
double d1 = vec3d.horizontalDistanceSqr();
if (d0 > 0.0D && d1 > 0.0D) {
double d2 = (vec3d1.x * vec3d.x + vec3d1.z * vec3d.z) / (Math.sqrt(d0) * Math.sqrt(d1));
double d3 = vec3d1.x * vec3d.z - vec3d1.z * vec3d.x;
mat.rotate((float)-((Math.signum(d3) * Math.acos(d2))), Vec3f.Z_AXIS);
}
return mat;
} else if (this.original.isSleeping()) {
BlockState blockstate = this.original.getFeetBlockState();
float yRot = 0.0F;
if (blockstate.isBed(this.original.level(), this.original.getSleepingPos().orElse(null), this.original)) {
if (blockstate.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) {
switch(blockstate.getValue(BlockStateProperties.HORIZONTAL_FACING)) {
case EAST:
yRot = 90.0F;
break;
case WEST:
yRot = -90.0F;
break;
case SOUTH:
yRot = 180.0F;
break;
default:
break;
}
}
}
return MathUtils.getModelMatrixIntegral(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, yRot, yRot, 0, PLAYER_SCALE, PLAYER_SCALE, PLAYER_SCALE);
} else {
float yRotO;
float yRot;
float xRotO = 0;
float xRot = 0;
if (this.original.getVehicle() instanceof LivingEntity ridingEntity) {
yRotO = ridingEntity.yBodyRotO;
yRot = ridingEntity.yBodyRot;
} else {
yRotO = this.modelYRotO;
yRot = this.modelYRot;
}
if (!this.getEntityState().inaction() && this.original.getPose() == net.minecraft.world.entity.Pose.SWIMMING) {
float f = this.original.getSwimAmount(partialTick);
float f3 = this.original.isInWater() ? this.original.getXRot() : 0;
float f4 = Mth.lerp(f, 0.0F, f3);
xRotO = f4;
xRot = f4;
}
return MathUtils.getModelMatrixIntegral(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, xRotO, xRot, yRotO, yRot, partialTick, PLAYER_SCALE, PLAYER_SCALE, PLAYER_SCALE);
}
}
public void setEpicSkinsInformation(EpicSkins epicSkinsInformation) {
this.epicSkinsInformation = epicSkinsInformation;
}
public EpicSkins getEpicSkinsInformation() {
return this.epicSkinsInformation;
}
public boolean isEpicSkinsLoaded() {
return this.epicSkinsInformation != null;
}
@Override
public EntitySnapshot<?> captureEntitySnapshot() {
return EntitySnapshot.capturePlayer(this);
}
private final ClothSimulator clothSimulator = new ClothSimulator();
public float modelYRotO2;
public double xPosO2;
public double yPosO2;
public double zPosO2;
public double xCloakO2;
public double yCloakO2;
public double zCloakO2;
@SuppressWarnings("unchecked")
@Override
public <SIM extends PhysicsSimulator<?, ?, ?, ?, ?>> Optional<SIM> getSimulator(SimulationTypes<?, ?, ?, ?, ?, SIM> simulationType) {
if (simulationType == SimulationTypes.CLOTH) {
return Optional.of((SIM)this.clothSimulator);
}
return Optional.empty();
}
@Override
public ClothSimulator getClothSimulator() {
return this.clothSimulator;
}
@Override
public Vec3 getAccurateCloakLocation(float partialFrame) {
if (partialFrame < 0.0F) {
partialFrame = 1.0F - partialFrame;
double x = Mth.lerp((double)partialFrame, this.xCloakO2, this.original.xCloakO) - Mth.lerp((double)partialFrame, this.xPosO2, this.original.xo);
double y = Mth.lerp((double)partialFrame, this.yCloakO2, this.original.yCloakO) - Mth.lerp((double)partialFrame, this.yPosO2, this.original.yo);
double z = Mth.lerp((double)partialFrame, this.zCloakO2, this.original.zCloakO) - Mth.lerp((double)partialFrame, this.zPosO2, this.original.zo);
return new Vec3(x, y, z);
} else {
double x = Mth.lerp((double)partialFrame, this.original.xCloakO, this.original.xCloak) - Mth.lerp((double)partialFrame, this.original.xo, this.original.getX());
double y = Mth.lerp((double)partialFrame, this.original.yCloakO, this.original.yCloak) - Mth.lerp((double)partialFrame, this.original.yo, this.original.getY());
double z = Mth.lerp((double)partialFrame, this.original.zCloakO, this.original.zCloak) - Mth.lerp((double)partialFrame, this.original.zo, this.original.getZ());
return new Vec3(x, y, z);
}
}
@Override
public Vec3 getAccuratePartialLocation(float partialFrame) {
if (partialFrame < 0.0F) {
partialFrame = 1.0F + partialFrame;
double x = Mth.lerp((double)partialFrame, this.xPosO2, this.original.xOld);
double y = Mth.lerp((double)partialFrame, this.yPosO2, this.original.yOld);
double z = Mth.lerp((double)partialFrame, this.zPosO2, this.original.zOld);
return new Vec3(x, y, z);
} else {
double x = Mth.lerp((double)partialFrame, this.original.xOld, this.original.getX());
double y = Mth.lerp((double)partialFrame, this.original.yOld, this.original.getY());
double z = Mth.lerp((double)partialFrame, this.original.zOld, this.original.getZ());
return new Vec3(x, y, z);
}
}
@Override
public Vec3 getObjectVelocity() {
return new Vec3(this.original.getX() - this.original.xOld, this.original.getY() - this.original.yOld, this.original.getZ() - this.original.zOld);
}
@Override
public float getAccurateYRot(float partialFrame) {
if (partialFrame < 0.0F) {
partialFrame = 1.0F + partialFrame;
return Mth.rotLerp(partialFrame, this.modelYRotO2, this.getYRotO());
} else {
return Mth.rotLerp(partialFrame, this.getYRotO(), this.getYRot());
}
}
@Override
public float getYRotDelta(float partialFrame) {
if (partialFrame < 0.0F) {
partialFrame = 1.0F + partialFrame;
return Mth.rotLerp(partialFrame, this.modelYRotO2, this.getYRotO()) - this.modelYRotO2;
} else {
return Mth.rotLerp(partialFrame, this.getYRotO(), this.getYRot()) - this.getYRotO();
}
}
@Override
public boolean invalid() {
return this.original.isRemoved();
}
@Override
public float getScale() {
return PLAYER_SCALE;
}
@Override
public Animator getSimulatableAnimator() {
return this.animator;
}
@Override
public float getGravity() {
return this.getOriginal().isUnderWater() ? 0.98F : 9.8F;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import com.tiedup.remake.rig.math.MathUtils;
import com.tiedup.remake.rig.math.OpenMatrix4f;
/**
* Stub RIG Phase 0 — patch de capability Forge attaché à une entité pour porter
* armature/animator/état. Version strippée des références combat EF
* (SPEntityPairingPacket, ProcessEntityPairingPacketEvent).
*/
public abstract class EntityPatch<T extends Entity> {
protected T original;
protected boolean initialized = false;
public void onOldPosUpdate() {
}
public void onAddedToWorld() {
}
public abstract boolean overrideRender();
public void onStartTracking(ServerPlayer trackingPlayer) {
}
public void onStopTracking(ServerPlayer trackingPlayer) {
}
public void onConstructed(T entity) {
this.original = entity;
}
public void onJoinWorld(T entity, EntityJoinLevelEvent event) {
this.initialized = true;
}
public void onDeath(LivingDeathEvent event) {
}
public final T getOriginal() {
return this.original;
}
public boolean isInitialized() {
return this.initialized;
}
public boolean isLogicalClient() {
return this.original.level().isClientSide();
}
public OpenMatrix4f getMatrix(float partialTick) {
return MathUtils.getModelMatrixIntegral(0, 0, 0, 0, 0, 0, this.original.xRotO, this.original.getXRot(), this.original.yRotO, this.original.getYRot(), partialTick, 1, 1, 1);
}
public double getAngleTo(Entity entity) {
Vec3 a = this.original.getLookAngle();
Vec3 b = new Vec3(entity.getX() - this.original.getX(), entity.getY() - this.original.getY(), entity.getZ() - this.original.getZ()).normalize();
double cos = (a.x * b.x + a.y * b.y + a.z * b.z);
return Math.toDegrees(Math.acos(cos));
}
public double getAngleToHorizontal(Entity entity) {
Vec3 a = this.original.getLookAngle();
Vec3 b = new Vec3(entity.getX() - this.original.getX(), 0.0D, entity.getZ() - this.original.getZ()).normalize();
double cos = (a.x * b.x + a.y * b.y + a.z * b.z);
return Math.toDegrees(Math.acos(cos));
}
public abstract OpenMatrix4f getModelMatrix(float partialTick);
}

View File

@@ -0,0 +1,96 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
import com.tiedup.remake.rig.anim.Animator;
import com.tiedup.remake.rig.anim.LivingMotion;
import com.tiedup.remake.rig.anim.LivingMotions;
import com.tiedup.remake.rig.anim.Pose;
import com.tiedup.remake.rig.anim.client.ClientAnimator;
import com.tiedup.remake.rig.anim.types.DynamicAnimation;
import com.tiedup.remake.rig.armature.Armature;
/**
* Stub RIG Phase 0 — patch de capability attaché à un {@link LivingEntity}.
* Expose l'animator, l'armature, la motion courante + quelques helpers.
* La version finale sera étoffée en Phase 2 (NPCs + player) avec les données
* d'entraînement/bondage spécifiques.
*/
public abstract class LivingEntityPatch<T extends LivingEntity> extends EntityPatch<T> {
public LivingMotion currentLivingMotion = LivingMotions.IDLE;
public LivingMotion currentCompositeMotion = LivingMotions.IDLE;
public abstract void updateMotion(boolean considerInaction);
public abstract Armature getArmature();
public Animator getAnimator() {
return null;
}
@Nullable
public ClientAnimator getClientAnimator() {
return null;
}
public LivingMotion getCurrentLivingMotion() {
return this.currentLivingMotion;
}
public LivingMotion getCurrentCompositeMotion() {
return this.currentCompositeMotion;
}
@Nullable
public LivingEntity getTarget() {
return this.original != null ? this.original.getLastHurtMob() : null;
}
@Nullable
public LivingEntity getGrapplingTarget() {
return null;
}
public float getReach() {
return 1.0F;
}
public float getYRot() {
return this.original.getYRot();
}
public void setYRot(float yRot) {
this.original.setYRot(yRot);
}
public float getYRotLimit() {
return 180.0F;
}
public float getYRotDeltaTo(Entity entity) {
Vec3 delta = entity.position().subtract(this.original.position());
float yaw = (float) Math.toDegrees(Math.atan2(delta.z, delta.x)) - 90.0F;
return net.minecraft.util.Mth.wrapDegrees(yaw - this.original.getYRot());
}
public void poseTick(DynamicAnimation animation, Pose pose, float elapsedTime, float partialTick) {
}
public void updateEntityState() {
}
@Override
public boolean overrideRender() {
return false;
}
}

View File

@@ -1,579 +0,0 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import net.minecraftforge.entity.PartEntity;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingEvent;
import com.tiedup.remake.rig.armature.JointTransform;
import com.tiedup.remake.rig.anim.Keyframe;
import com.tiedup.remake.rig.anim.Pose;
import com.tiedup.remake.rig.anim.TransformSheet;
import com.tiedup.remake.rig.anim.property.AnimationProperty.ActionAnimationProperty;
import com.tiedup.remake.rig.anim.types.ActionAnimation;
import com.tiedup.remake.rig.anim.types.AttackAnimation;
import com.tiedup.remake.rig.anim.types.DirectStaticAnimation;
import com.tiedup.remake.rig.anim.types.StaticAnimation;
import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.anim.client.AnimationSubFileReader;
import com.tiedup.remake.rig.anim.client.AnimationSubFileReader.PovSettings;
import com.tiedup.remake.rig.anim.client.AnimationSubFileReader.PovSettings.ViewLimit;
import com.tiedup.remake.rig.anim.client.Layer;
import com.tiedup.remake.rig.anim.client.property.ClientAnimationProperties;
import yesman.epicfight.api.client.camera.EpicFightCameraAPI;
import yesman.epicfight.api.client.input.InputManager;
import yesman.epicfight.api.client.input.action.MinecraftInputAction;
import com.tiedup.remake.rig.math.MathUtils;
import yesman.epicfight.client.ClientEngine;
import yesman.epicfight.client.events.engine.RenderEngine;
import yesman.epicfight.client.gui.screen.SkillBookScreen;
import com.tiedup.remake.rig.TiedUpAnimationConfig;
import com.tiedup.remake.rig.TiedUpRigRegistry;
import com.tiedup.remake.rig.TiedUpRigConstants;
import yesman.epicfight.network.EpicFightNetworkManager;
import yesman.epicfight.network.client.CPAnimatorControl;
import yesman.epicfight.network.client.CPChangePlayerMode;
import yesman.epicfight.network.client.CPModifyEntityModelYRot;
import yesman.epicfight.network.client.CPSetStamina;
import yesman.epicfight.network.common.AnimatorControlPacket;
import yesman.epicfight.skill.modules.ChargeableSkill;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
import com.tiedup.remake.rig.patch.PlayerPatch;
import com.tiedup.remake.rig.patch.item.CapabilityItem;
import yesman.epicfight.world.entity.eventlistener.PlayerEventListener.EventType;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class LocalPlayerPatch extends AbstractClientPlayerPatch<LocalPlayer> {
private static final UUID ACTION_EVENT_UUID = UUID.fromString("d1a1e102-1621-11ed-861d-0242ac120002");
private Minecraft minecraft;
private float staminaO;
private int prevChargingAmount;
private AnimationSubFileReader.PovSettings povSettings;
private FirstPersonLayer firstPersonLayer = new FirstPersonLayer();
@Override
public void onConstructed(LocalPlayer entity) {
super.onConstructed(entity);
this.minecraft = Minecraft.getInstance();
}
@Override
public void onJoinWorld(LocalPlayer player, EntityJoinLevelEvent event) {
super.onJoinWorld(player, event);
this.eventListeners.addEventListener(EventType.ACTION_EVENT_CLIENT, ACTION_EVENT_UUID, (playerEvent) -> {
ClientEngine.getInstance().controlEngine.unlockHotkeys();
});
}
public void onRespawnLocalPlayer(ClientPlayerNetworkEvent.Clone event) {
this.onJoinWorld(event.getNewPlayer(), new EntityJoinLevelEvent(event.getNewPlayer(), event.getNewPlayer().level()));
}
@Override
public void tick(LivingEvent.LivingTickEvent event) {
this.staminaO = this.getStamina();
if (this.isHoldingAny() && this.getHoldingSkill() instanceof ChargeableSkill) {
this.prevChargingAmount = this.getChargingAmount();
} else {
this.prevChargingAmount = 0;
}
super.tick(event);
}
@Override
public void clientTick(LivingEvent.LivingTickEvent event) {
this.staminaO = this.getStamina();
super.clientTick(event);
// Handle first person animation
final AssetAccessor<? extends StaticAnimation> currentPlaying = this.firstPersonLayer.animationPlayer.getRealAnimation();
boolean noPovAnimation = this.getClientAnimator().iterVisibleLayersUntilFalse(layer -> {
if (layer.isOff()) {
return true;
}
Optional<DirectStaticAnimation> optPovAnimation = layer.animationPlayer.getRealAnimation().get().getProperty(ClientAnimationProperties.POV_ANIMATION);
Optional<PovSettings> optPovSettings = layer.animationPlayer.getRealAnimation().get().getProperty(ClientAnimationProperties.POV_SETTINGS);
optPovAnimation.ifPresent(povAnimation -> {
if (!povAnimation.equals(currentPlaying.get())) {
this.firstPersonLayer.playAnimation(povAnimation, layer.animationPlayer.getRealAnimation(), this, 0.0F);
this.povSettings = optPovSettings.get();
}
});
return !optPovAnimation.isPresent();
});
if (noPovAnimation && !currentPlaying.equals(TiedUpRigRegistry.EMPTY_ANIMATION)) {
this.firstPersonLayer.off();
}
this.firstPersonLayer.update(this);
if (this.firstPersonLayer.animationPlayer.getAnimation().equals(TiedUpRigRegistry.EMPTY_ANIMATION)) {
this.povSettings = null;
}
}
@Override
public boolean overrideRender() {
// Disable rendering the player when animated first person model disabled
if (this.original.is(this.minecraft.player)) {
if (this.minecraft.options.getCameraType().isFirstPerson() && !TiedUpAnimationConfig.enableAnimatedFirstPersonModel) {
return false;
}
}
return super.overrideRender();
}
@Override
public LivingEntity getTarget() {
return EpicFightCameraAPI.getInstance().getFocusingEntity();
}
@Override
public void toVanillaMode(boolean synchronize) {
if (this.playerMode != PlayerMode.VANILLA) {
ClientEngine.getInstance().renderEngine.downSlideSkillUI();
if (TiedUpAnimationConfig.autoSwitchCamera) {
this.minecraft.options.setCameraType(CameraType.FIRST_PERSON);
}
if (synchronize) {
EpicFightNetworkManager.sendToServer(new CPChangePlayerMode(PlayerMode.VANILLA));
}
}
super.toVanillaMode(synchronize);
}
@Override
public void toEpicFightMode(boolean synchronize) {
if (this.playerMode != PlayerMode.EPICFIGHT) {
ClientEngine.getInstance().renderEngine.upSlideSkillUI();
if (TiedUpAnimationConfig.autoSwitchCamera) {
this.minecraft.options.setCameraType(CameraType.THIRD_PERSON_BACK);
}
if (synchronize) {
EpicFightNetworkManager.sendToServer(new CPChangePlayerMode(PlayerMode.EPICFIGHT));
}
}
super.toEpicFightMode(synchronize);
}
@Override
public boolean isFirstPerson() {
return this.minecraft.options.getCameraType() == CameraType.FIRST_PERSON;
}
@Override
public boolean shouldBlockMoving() {
return InputManager.isActionActive(MinecraftInputAction.MOVE_BACKWARD) || InputManager.isActionActive(MinecraftInputAction.SNEAK);
}
@Override
public boolean shouldMoveOnCurrentSide(ActionAnimation actionAnimation) {
if (!this.isLogicalClient()) {
return false;
}
return actionAnimation.shouldPlayerMove(this);
}
public float getStaminaO() {
return this.staminaO;
}
public int getPrevChargingAmount() {
return this.prevChargingAmount;
}
public FirstPersonLayer getFirstPersonLayer() {
return this.firstPersonLayer;
}
public AnimationSubFileReader.PovSettings getPovSettings() {
return this.povSettings;
}
public boolean hasCameraAnimation() {
return this.povSettings != null && this.povSettings.cameraTransform() != null;
}
@Override
public void setStamina(float value) {
EpicFightNetworkManager.sendToServer(new CPSetStamina(value, true));
}
@Override
public void setModelYRot(float amount, boolean sendPacket) {
super.setModelYRot(amount, sendPacket);
if (sendPacket) {
EpicFightNetworkManager.sendToServer(new CPModifyEntityModelYRot(amount));
}
}
public float getModelYRot() {
return this.modelYRot;
}
public void setModelYRotInGui(float rotDeg) {
this.useModelYRot = true;
this.modelYRot = rotDeg;
}
public void disableModelYRotInGui(float originalDeg) {
this.useModelYRot = false;
this.modelYRot = originalDeg;
}
@Override
public void disableModelYRot(boolean sendPacket) {
super.disableModelYRot(sendPacket);
if (sendPacket) {
EpicFightNetworkManager.sendToServer(new CPModifyEntityModelYRot());
}
}
@Override
public double checkXTurn(double xRot) {
if (xRot == 0.0D) {
return xRot;
}
if (TiedUpAnimationConfig.enablePovAction && this.minecraft.options.getCameraType().isFirstPerson() && this.isEpicFightMode() && !this.getFirstPersonLayer().isOff()) {
ViewLimit viewLimit = this.getPovSettings().viewLimit();
if (viewLimit != null) {
float xRotDest = this.original.getXRot() + (float)xRot * 0.15F;
if (xRotDest <= viewLimit.xRotMin() || xRotDest >= viewLimit.xRotMax()) {
return 0.0D;
}
}
}
return xRot;
}
@Override
public double checkYTurn(double yRot) {
if (yRot == 0.0D) {
return yRot;
}
if (TiedUpAnimationConfig.enablePovAction && this.minecraft.options.getCameraType().isFirstPerson() && this.isEpicFightMode() && !this.getFirstPersonLayer().isOff()) {
ViewLimit viewLimit = this.getPovSettings().viewLimit();
if (viewLimit != null) {
float yCamera = Mth.wrapDegrees(this.original.getYRot());
float yBody = MathUtils.findNearestRotation(yCamera, this.getYRot());
float yRotDest = yCamera + (float)yRot * 0.15F;
float yRotClamped = Mth.clamp(yRotDest, yBody + viewLimit.yRotMin(), yBody + viewLimit.yRotMax());
if (yRotDest != yRotClamped) {
return 0.0D;
}
}
}
return yRot;
}
@Override
public void beginAction(ActionAnimation animation) {
EpicFightCameraAPI cameraApi = EpicFightCameraAPI.getInstance();
if (cameraApi.isTPSMode()) {
if (cameraApi.getFocusingEntity() != null && animation instanceof AttackAnimation) {
cameraApi.alignPlayerLookToCrosshair(false, true, true);
} else {
cameraApi.alignPlayerLookToCameraRotation(false, true, true);
}
}
if (!this.useModelYRot || animation.getProperty(ActionAnimationProperty.SYNC_CAMERA).orElse(false)) {
this.modelYRot = this.original.getYRot();
}
if (cameraApi.getFocusingEntity() != null && cameraApi.isLockingOnTarget() && !cameraApi.getFocusingEntity().isRemoved()) {
Vec3 playerPosition = this.original.position();
Vec3 targetPosition = cameraApi.getFocusingEntity().position();
Vec3 toTarget = targetPosition.subtract(playerPosition);
this.original.setYRot((float)MathUtils.getYRotOfVector(toTarget));
}
}
/**
* Play an animation after the current animation is finished
* @param animation
*/
@Override
public void reserveAnimation(AssetAccessor<? extends StaticAnimation> animation) {
this.animator.reserveAnimation(animation);
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.RESERVE, animation, 0.0F, false, false, false));
}
/**
* Play an animation without convert time
* @param animation
*/
@Override
public void playAnimationInstantly(AssetAccessor<? extends StaticAnimation> animation) {
this.animator.playAnimationInstantly(animation);
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.PLAY_INSTANTLY, animation, 0.0F, false, false, false));
}
/**
* Play a shooting animation to end aim pose
* This method doesn't send packet from client to server
*/
@Override
public void playShootingAnimation() {
this.animator.playShootingAnimation();
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.SHOT, -1, 0.0F, false, true, false));
}
/**
* Stop playing an animation
* @param animation
* @param transitionTimeModifier
*/
@Override
public void stopPlaying(AssetAccessor<? extends StaticAnimation> animation) {
this.animator.stopPlaying(animation);
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.STOP, animation, -1.0F, false, false, false));
}
/**
* Play an animation ensuring synchronization between client-server
* Plays animation when getting response from server if it called in client side.
* Do not call this in client side for non-player entities.
*
* @param animation
* @param transitionTimeModifier
*/
@Override
public void playAnimationSynchronized(AssetAccessor<? extends StaticAnimation> animation, float transitionTimeModifier) {
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.PLAY, animation, transitionTimeModifier, false, false, true));
}
/**
* Play an animation only in client side, including all clients tracking this entity
* @param animation
* @param convertTimeModifier
*/
@Override
public void playAnimationInClientSide(AssetAccessor<? extends StaticAnimation> animation, float transitionTimeModifier) {
this.animator.playAnimation(animation, transitionTimeModifier);
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(AnimatorControlPacket.Action.PLAY, animation, transitionTimeModifier, false, true, false));
}
/**
* Pause an animator until it receives a proper order
* @param action SOFT_PAUSE: resume when next animation plays
* HARD_PAUSE: resume when hard pause is set false
* @param pause
**/
@Override
public void pauseAnimator(AnimatorControlPacket.Action action, boolean pause) {
super.pauseAnimator(action, pause);
EpicFightNetworkManager.sendToServer(new CPAnimatorControl(action, -1, 0.0F, pause, false, false));
}
@Override
public void openSkillBook(ItemStack itemstack, InteractionHand hand) {
if (itemstack.hasTag() && itemstack.getTag().contains("skill")) {
Minecraft.getInstance().setScreen(new SkillBookScreen(this.original, itemstack, hand));
}
}
@Override
public void resetHolding() {
if (this.holdingSkill != null) {
ClientEngine.getInstance().controlEngine.releaseAllServedKeys();
}
super.resetHolding();
}
@Override
public void updateHeldItem(CapabilityItem mainHandCap, CapabilityItem offHandCap) {
super.updateHeldItem(mainHandCap, offHandCap);
if (!TiedUpAnimationConfig.preferenceWork.checkHitResult()) {
if (TiedUpAnimationConfig.combatPreferredItems.contains(this.original.getMainHandItem().getItem())) {
this.toEpicFightMode(true);
} else if (TiedUpAnimationConfig.miningPreferredItems.contains(this.original.getMainHandItem().getItem())) {
this.toVanillaMode(true);
}
}
}
/**
* Judge the next behavior depending on player's item preference and where he's looking at
* @return true if the next action is swing a weapon, false if the next action is breaking a block
*/
public boolean canPlayAttackAnimation() {
if (this.isVanillaMode()) {
return false;
}
EpicFightCameraAPI cameraApi = EpicFightCameraAPI.getInstance();
HitResult hitResult =
(EpicFightCameraAPI.getInstance().isTPSMode() && cameraApi.getCrosshairHitResult() != null && cameraApi.getCrosshairHitResult().getLocation().distanceToSqr(this.original.getEyePosition()) < this.original.getBlockReach() * this.original.getBlockReach())
? cameraApi.getCrosshairHitResult() : this.minecraft.hitResult;
if (hitResult == null) {
return true;
}
EntityHitResult entityHitResult = RenderEngine.asEntityHitResult(hitResult);
if (entityHitResult != null) {
Entity hitEntity = entityHitResult.getEntity();
if (!(hitEntity instanceof LivingEntity) && !(hitEntity instanceof PartEntity)) {
return false;
}
}
if (EpicFightCameraAPI.getInstance().isLockingOnTarget()) {
return true;
}
if (TiedUpAnimationConfig.preferenceWork.checkHitResult()) {
if (TiedUpAnimationConfig.combatPreferredItems.contains(this.original.getMainHandItem().getItem())) {
BlockHitResult blockHitResult = RenderEngine.asBlockHitResult(this.minecraft.hitResult);
if (blockHitResult != null && this.minecraft.level != null) {
BlockPos bp = blockHitResult.getBlockPos();
BlockState bs = this.minecraft.level.getBlockState(bp);
return !this.original.getMainHandItem().getItem().canAttackBlock(bs, this.original.level(), bp, this.original) || !this.original.getMainHandItem().isCorrectToolForDrops(bs);
}
} else {
return RenderEngine.hitResultNotEquals(this.minecraft.hitResult, HitResult.Type.BLOCK);
}
return true;
} else {
return this.getPlayerMode() == PlayerPatch.PlayerMode.EPICFIGHT;
}
}
public class FirstPersonLayer extends Layer {
private TransformSheet linkCameraTransform = new TransformSheet(List.of(new Keyframe(0.0F, JointTransform.empty()), new Keyframe(Float.MAX_VALUE, JointTransform.empty())));
public FirstPersonLayer() {
super(null);
}
public void playAnimation(AssetAccessor<? extends StaticAnimation> nextFirstPersonAnimation, AssetAccessor<? extends StaticAnimation> originalAnimation, LivingEntityPatch<?> entitypatch, float transitionTimeModifier) {
Optional<PovSettings> povSettings = originalAnimation.get().getProperty(ClientAnimationProperties.POV_SETTINGS);
boolean hasPrevCameraAnimation = LocalPlayerPatch.this.povSettings != null && LocalPlayerPatch.this.povSettings.cameraTransform() != null;
boolean hasNextCameraAnimation = povSettings.isPresent() && povSettings.get().cameraTransform() != null;
// Activate pov animation
if (hasPrevCameraAnimation || hasNextCameraAnimation) {
if (hasPrevCameraAnimation) {
this.linkCameraTransform.getKeyframes()[0].transform().copyFrom(LocalPlayerPatch.this.povSettings.cameraTransform().getInterpolatedTransform(this.animationPlayer.getElapsedTime()));
} else {
this.linkCameraTransform.getKeyframes()[0].transform().copyFrom(JointTransform.empty());
}
if (hasNextCameraAnimation) {
this.linkCameraTransform.getKeyframes()[1].transform().copyFrom(povSettings.get().cameraTransform().getKeyframes()[0].transform());
} else {
this.linkCameraTransform.getKeyframes()[1].transform().clearTransform();
}
this.linkCameraTransform.getKeyframes()[1].setTime(nextFirstPersonAnimation.get().getTransitionTime());
}
super.playAnimation(nextFirstPersonAnimation, entitypatch, transitionTimeModifier);
}
public void off() {
// Off camera animation
if (LocalPlayerPatch.this.povSettings != null && LocalPlayerPatch.this.povSettings.cameraTransform() != null) {
this.linkCameraTransform.getKeyframes()[0].transform().copyFrom(LocalPlayerPatch.this.povSettings.cameraTransform().getInterpolatedTransform(this.animationPlayer.getElapsedTime()));
this.linkCameraTransform.getKeyframes()[1].transform().copyFrom(JointTransform.empty());
this.linkCameraTransform.getKeyframes()[1].setTime(TiedUpRigConstants.GENERAL_ANIMATION_TRANSITION_TIME);
}
super.off(LocalPlayerPatch.this);
}
@Override
protected Pose getCurrentPose(LivingEntityPatch<?> entitypatch) {
return this.animationPlayer.isEmpty() ? super.getCurrentPose(entitypatch) : this.animationPlayer.getCurrentPose(entitypatch, 0.0F);
}
public TransformSheet getLinkCameraTransform() {
return this.linkCameraTransform;
}
}
/**
* @deprecated Use {@link EpicFightCameraAPI#isLockingOnTarget()} instead
*/
@Deprecated(forRemoval = true)
public boolean isTargetLockedOn() {
return EpicFightCameraAPI.getInstance().isLockingOnTarget();
}
/**
* @deprecated Use {@link EpicFightCameraAPI#setLockOn(boolean)} instead
*/
@Deprecated(forRemoval = true)
public void setLockOn(boolean targetLockedOn) {
EpicFightCameraAPI.getInstance().setLockOn(targetLockedOn);
}
/**
* @deprecated Use {@link EpicFightCameraAPI#toggleLockOn()} instead
*/
@Deprecated(forRemoval = true)
public void toggleLockOn() {
this.setLockOn(!EpicFightCameraAPI.getInstance().isLockingOnTarget());
}
}

View File

@@ -0,0 +1,17 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch;
import net.minecraft.world.entity.Mob;
/**
* Stub RIG Phase 0 — patch générique pour {@link Mob} (NPCs). Utilisé par
* {@code instanceof MobPatch<?>} dans les YRotProvider pour rediriger la tête
* et stopper la navigation pendant une animation. Version complète Phase 2.
*/
public abstract class MobPatch<T extends Mob> extends LivingEntityPatch<T> {
}

View File

@@ -0,0 +1,15 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.patch.item;
/**
* Stub RIG Phase 0 — capability item (côté combat EF). Ne sert ici que de type
* pour ON_ITEM_CHANGE_EVENT ; logique métier à re-implémenter Phase 3 si
* besoin (probablement via ItemStack + AnimationSet directement).
*/
public class CapabilityItem {
}

View File

@@ -0,0 +1,28 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import com.tiedup.remake.rig.physics.SimulationObject.SimulationObjectBuilder;
public interface PhysicsSimulator<KEY, B extends SimulationObjectBuilder, PV extends SimulationProvider<O, T, B, PV>, O, T extends SimulationObject<B, PV, O>> {
public void tick(O object);
public boolean isRunning(KEY key);
public void runUntil(KEY key, PV provider, B builder, BooleanSupplier when);
public void runWhen(KEY key, PV provider, B builder, BooleanSupplier when);
public void restart(KEY key);
public void stop(KEY key);
public Optional<T> getRunningObject(KEY key);
}

View File

@@ -0,0 +1,13 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics;
import java.util.Optional;
public interface SimulatableObject {
<SIM extends PhysicsSimulator<?, ?, ?, ?, ?>> Optional<SIM> getSimulator(SimulationTypes<?, ?, ?, ?, ?, SIM> simulationType);
}

View File

@@ -0,0 +1,17 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics;
import com.tiedup.remake.rig.physics.SimulationObject.SimulationObjectBuilder;
public interface SimulationObject<B extends SimulationObjectBuilder, PV extends SimulationProvider<O, ?, B, PV>, O> {
/**
* An abstract method to specify the parameters needed to build SimulationObject
*/
public abstract class SimulationObjectBuilder {
}
}

View File

@@ -0,0 +1,15 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics;
import javax.annotation.Nullable;
import com.tiedup.remake.rig.physics.SimulationObject.SimulationObjectBuilder;
public interface SimulationProvider<OWN, OBJ extends SimulationObject<?, ?, ?>, BUILDER extends SimulationObjectBuilder, P extends SimulationProvider<OWN, OBJ, BUILDER, P>> {
public OBJ createSimulationData(@Nullable P provider, OWN simOwner, BUILDER simBuilder);
}

View File

@@ -0,0 +1,24 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import com.tiedup.remake.rig.armature.Joint;
import com.tiedup.remake.rig.mesh.SoftBodyTranslatable;
import com.tiedup.remake.rig.cloth.ClothSimulatable;
import com.tiedup.remake.rig.cloth.ClothSimulator;
import com.tiedup.remake.rig.physics.ik.InverseKinematicsProvider;
import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulatable;
import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator;
public interface SimulationTypes<KEY, O, PV extends SimulationProvider<O, DATA, B, PV>, B extends SimulationObject.SimulationObjectBuilder, DATA extends SimulationObject<B, PV, O>, SIM extends PhysicsSimulator<KEY, B, PV, O, DATA>> {
@OnlyIn(Dist.CLIENT)
public static final SimulationTypes<ResourceLocation, ClothSimulatable, SoftBodyTranslatable, ClothSimulator.ClothObjectBuilder, ClothSimulator.ClothObject, ClothSimulator> CLOTH = new SimulationTypes<> () {};
public static final SimulationTypes<Joint, InverseKinematicsSimulatable, InverseKinematicsProvider, InverseKinematicsSimulator.InverseKinematicsBuilder, InverseKinematicsSimulator.InverseKinematicsObject, InverseKinematicsSimulator> INVERSE_KINEMATICS = new SimulationTypes<> () {};
}

View File

@@ -0,0 +1,35 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics.ik;
import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.InverseKinematicsBuilder;
import com.tiedup.remake.rig.physics.ik.InverseKinematicsSimulator.InverseKinematicsObject;
/**
* RIG stub. Upstream EF : interface implémentée par les providers
* d'inverse kinematics pour attacher une IK chain à une animation
* (typiquement pour forcer les mains à rester sur la poignée d'arme
* pendant une attaque, ou les pieds au sol pendant un coup de pied).
*
* <p>Non utilisé en TiedUp (pas de combat, pas de weapon IK).
* Conservé comme interface vide pour que {@code StaticAnimation
* implements InverseKinematicsProvider} compile.</p>
*/
public interface InverseKinematicsProvider {
/**
* Crée les données de simulation IK. Stub par défaut : no-op.
* Surchargé dans les classes qui auraient réellement de l'IK (aucune
* en TiedUp).
*/
default InverseKinematicsObject createSimulationData(
InverseKinematicsProvider provider,
InverseKinematicsSimulatable simOwner,
InverseKinematicsBuilder simBuilder) {
return new InverseKinematicsObject(simBuilder);
}
}

View File

@@ -0,0 +1,15 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics.ik;
/**
* RIG stub. Upstream EF : marker interface pour les entities/patches qui
* peuvent porter une simulation IK. Non utilisé en TiedUp — stub vide
* pour satisfaire les {@code instanceof} dans StaticAnimation.
*/
public interface InverseKinematicsSimulatable {
}

View File

@@ -0,0 +1,47 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.physics.ik;
/**
* RIG stub. Upstream EF : simulateur d'inverse kinematics (solveur CCD
* ou FABRIK) utilisé pour attacher des membres à des points cibles
* pendant les animations d'attaque combat. Non utilisé en TiedUp.
*
* <p>Conservé comme shell vide pour que les imports StaticAnimation +
* AnimationProperty compilent. Les nested types sont des stubs sans
* logique.</p>
*/
public class InverseKinematicsSimulator {
public static class InverseKinematicsObject {
public InverseKinematicsObject(InverseKinematicsBuilder builder) {
// no-op
}
}
public static class InverseKinematicsBuilder {
// no-op stub
}
/**
* Définition d'une chaîne IK (armature joint + cible). Stub avec
* juste la méthode {@code bake()} appelée par StaticAnimation.
*/
public static class InverseKinematicsDefinition {
public BakedInverseKinematicsDefinition bake(
Object armature,
Object jointTransforms,
float correctY,
float correctZ) {
return new BakedInverseKinematicsDefinition();
}
}
public static class BakedInverseKinematicsDefinition {
// no-op stub
}
}

View File

@@ -0,0 +1,27 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.render.compute;
/**
* RIG stub. Upstream EF : registry des ComputeShaderSetup par mesh.
* Non utilisé en TiedUp Phase 0 — classe vide pour satisfaire les refs
* dans SkinnedMesh.
*/
public final class ComputeShaderProvider {
private ComputeShaderProvider() {}
/** Stub no-op : retourne toujours null → chemin CPU skinning. */
public static ComputeShaderSetup getComputeShaderSetup(Object key) {
return null;
}
/** Stub no-op pour Iris init (référence pour IRISCompat éventuel). */
public static void initIris() {
// no-op Phase 0
}
}

View File

@@ -0,0 +1,43 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.render.compute;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import com.tiedup.remake.rig.mesh.Mesh;
import com.tiedup.remake.rig.armature.Armature;
import com.tiedup.remake.rig.math.OpenMatrix4f;
/**
* RIG stub. Upstream EF : ComputeShaderSetup = binding OpenGL 4.3+ compute
* shader pour le GPU skinning. Non utilisé en TiedUp Phase 0 (CPU skinning
* via VertexConsumer suffit). Classe vide, référencée par SkinnedMesh pour
* le chemin GPU optionnel.
*
* <p>Réactivation future (hors scope V1) : réintroduire l'implémentation
* complète EF + gérer IrisComputeShaderSetup pour compat shader packs.</p>
*/
public abstract class ComputeShaderSetup {
/** No-op stub : le chemin GPU compute n'est jamais emprunté. */
public void drawWithShader(
Object mesh,
PoseStack poseStack,
MultiBufferSource bufferSources,
RenderType renderType,
int packedLight,
float r, float g, float b, float a,
int overlay,
Armature armature,
OpenMatrix4f[] poses) {
// no-op — le renderer bascule sur le CPU path si ComputeShaderSetup
// est null sur le mesh. Cette implémentation ne devrait jamais être
// appelée (SkinnedMesh.computerShaderSetup = null par défaut).
}
}

View File

@@ -0,0 +1,11 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
public interface ExtendableEnum {
int universalOrdinal();
}

View File

@@ -0,0 +1,116 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.network.chat.Component;
import com.tiedup.remake.rig.TiedUpRigConstants;
public class ExtendableEnumManager<T extends ExtendableEnum> {
private final Int2ObjectMap<T> enumMapByOrdinal = new Int2ObjectLinkedOpenHashMap<>();
private final Map<String, T> enumMapByName = Maps.newLinkedHashMap();
private final Map<String, Class<?>> enums = Maps.newConcurrentMap();
private final String enumName;
private int lastOrdinal = 0;
public ExtendableEnumManager(String enumName) {
this.enumName = enumName;
}
public void registerEnumCls(String modid, Class<? extends ExtendableEnum> cls) {
if (this.enums.containsKey(modid)) {
TiedUpRigConstants.LOGGER.error(modid + " is already registered in " + this.enumName);
}
TiedUpRigConstants.LOGGER.debug("Registered Extendable Enum " + cls + " in " + this.enumName);
this.enums.put(modid, cls);
}
public void loadEnum() {
List<String> orderByModid = new ArrayList<>(this.enums.keySet());
Collections.sort(orderByModid);
Class<?> cls = null;
try {
for (String modid : orderByModid) {
cls = this.enums.get(modid);
Method m = cls.getMethod("values");
m.invoke(null);
TiedUpRigConstants.LOGGER.debug("Loaded enums in " + cls);
}
} catch (ClassCastException e) {
TiedUpRigConstants.LOGGER.error(cls.getCanonicalName() + " is not an ExtendableEnum!");
e.printStackTrace();
} catch (NoSuchMethodException e) {
TiedUpRigConstants.LOGGER.error(cls.getCanonicalName() + " is not an Enum class!");
e.printStackTrace();
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
TiedUpRigConstants.LOGGER.warn("Error while loading extendable enum " + cls.getCanonicalName());
e.printStackTrace();
}
TiedUpRigConstants.LOGGER.debug("All enums are loaded: " + this.enumName + " " + this.enumMapByName.values());
}
public int assign(T value) {
int lastOrdinal = this.lastOrdinal;
String enumName = ParseUtil.toLowerCase(value.toString());
if (this.enumMapByName.containsKey(enumName)) {
throw new IllegalArgumentException("Enum name " + enumName + " already exists in " + this.enumName);
}
this.enumMapByOrdinal.put(lastOrdinal, value);
this.enumMapByName.put(enumName, value);
++this.lastOrdinal;
return lastOrdinal;
}
public T getOrThrow(int id) throws NoSuchElementException {
if (!this.enumMapByOrdinal.containsKey(id)) {
throw new NoSuchElementException("Enum id " + id + " does not exist in " + this.enumName);
}
return this.enumMapByOrdinal.get(id);
}
public T getOrThrow(String name) throws NoSuchElementException {
String key = ParseUtil.toLowerCase(name);
if (!this.enumMapByName.containsKey(key)) {
throw new NoSuchElementException("Enum name " + key + " does not exist in " + this.enumName);
}
return this.enumMapByName.get(key);
}
public T get(int id) {
return this.enumMapByOrdinal.get(id);
}
public T get(String name) {
return this.enumMapByName.get(ParseUtil.toLowerCase(name));
}
public Collection<T> universalValues() {
return this.enumMapByOrdinal.values();
}
public String toTranslated(ExtendableEnum e) {
return Component.translatable(TiedUpRigConstants.MODID + "." + this.enumName + "." + ParseUtil.toLowerCase(e.toString())).getString();
}
}

View File

@@ -0,0 +1,19 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
/**
* Stub RIG Phase 0 — utilisé uniquement pour exposer {@link Priority} au
* type {@code AttackPhaseProperty<Priority> HIT_PRIORITY}. Logique combat
* (ordonner les entités touchées par une attack animation) non portée.
*/
public class HitEntityList {
public enum Priority {
DISTANCE, TARGET;
}
}

View File

@@ -0,0 +1,254 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.minecraft.world.InteractionHand;
import com.tiedup.remake.rig.armature.Joint;
import com.tiedup.remake.rig.asset.AssetAccessor;
import com.tiedup.remake.rig.exception.AnimationInvokeException;
import com.tiedup.remake.rig.armature.Armature;
public class InstantiateInvoker {
private static final BiMap<String, Class<?>> PRIMITIVE_KEYWORDS = HashBiMap.create();
private static final BiMap<Class<?>, Class<?>> RETURN_TYPE_MAPPER = HashBiMap.create();
private static final Map<Class<?>, Function<String, Object>> STRING_TO_OBJECT_PARSER = Maps.newHashMap();
static {
registerPrimitive("B", byte.class, Byte::parseByte);
registerPrimitive("C", char.class, (s) -> s.charAt(0));
registerPrimitive("D", double.class, Double::parseDouble);
registerPrimitive("F", float.class, Float::parseFloat);
registerPrimitive("I", int.class, Integer::parseInt);
registerPrimitive("J", long.class, Long::parseLong);
registerPrimitive("S", short.class, Short::parseShort);
registerPrimitive("Z", boolean.class, Boolean::parseBoolean);
registerReturnTypeMapper(Armature.class, AssetAccessor.class);
registerKeyword(String.class, (s) -> s);
// RIG : Collider.class (combat) + ColliderPreset strippés — re-implem Phase 2
// si besoin pour les hitboxes de menottes. Joint/Armature stubbés le temps
// que TiedUpRigRegistry expose un armature registry (Phase 2).
registerKeyword(Joint.class, (s) -> {
String[] armature$joint = s.split("\\.");
AssetAccessor<? extends Armature> armature = getArmature(armature$joint[0]);
return armature.get().searchJointByName(armature$joint[1]);
});
registerKeyword(Armature.class, (s) -> getArmature(s));
registerKeyword(InteractionHand.class, InteractionHand::valueOf);
}
private static AssetAccessor<? extends Armature> getArmature(String id) {
throw new AnimationInvokeException("Armature registry not yet implemented — Phase 2 (lookup for '" + id + "')");
}
public static void registerPrimitive(String keyword, Class<?> clz, Function<String, Object> decoder) {
PRIMITIVE_KEYWORDS.put(keyword, clz);
STRING_TO_OBJECT_PARSER.put(clz, decoder);
}
public static void registerReturnTypeMapper(Class<?> clz, Class<?> returnClz) {
RETURN_TYPE_MAPPER.put(clz, returnClz);
}
public static void registerKeyword(Class<?> clz, Function<String, Object> decoder) {
STRING_TO_OBJECT_PARSER.put(clz, decoder);
}
@SuppressWarnings("unchecked")
public static <T> Result<T> invoke(String invocationCommand, @Nullable Class<T> hint) throws Exception {
if (invocationCommand.matches("\\(.+\\)") || invocationCommand.matches("\\(.+\\)\\#.+")) { // invoke instance
return (Result<T>)invokeInstance(invocationCommand, hint);
} else if (invocationCommand.matches("\\[.+\\]") || invocationCommand.matches("\\[.+\\]\\#.+")) { // invoke array
return (Result<T>)invokeArray(invocationCommand, hint);
} else {
String[] param = splitExceptWrapper(invocationCommand, '#', true);
String sValue = param[0];
String sType = param[1];
if ("IMeshRenderBoost".equals(sType)) {
sType = "I"; // Bad Implementation: I > IMeshRenderBoost is caused by replace all but kept to preserve backward compatibility
}
if (PRIMITIVE_KEYWORDS.containsKey(sType)) {
Class<T> type = (Class<T>)PRIMITIVE_KEYWORDS.get(sType);
return Result.of(type, (T)STRING_TO_OBJECT_PARSER.get(type).apply(sValue));
}
Class<T> type = (Class<T>)Class.forName(sType);
if (STRING_TO_OBJECT_PARSER.containsKey(type)) {
if (RETURN_TYPE_MAPPER.containsKey(type)) {
return Result.of((Class<T>)RETURN_TYPE_MAPPER.get(type), (T)STRING_TO_OBJECT_PARSER.get(type).apply(sValue));
}
return Result.of(type, (T)STRING_TO_OBJECT_PARSER.get(type).apply(sValue));
}
throw new AnimationInvokeException("Can't find the matching type for the command " + invocationCommand);
}
}
/**
* @param hint has a higher priority than class specified in invocation command
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> Result<T> invokeInstance(String invocationCommand, @Nullable Class<?> hint) throws Exception {
if (!invocationCommand.matches("\\(.+\\)") && !invocationCommand.matches("\\(.+\\)\\#.+")) {
throw new IllegalStateException("Invalid instantiate invocation command: " + invocationCommand);
}
String[] param$type = splitExceptWrapper(invocationCommand, '#', true);
String sParams = param$type[0];
Class<?> type = param$type.length > 1 ? Class.forName(param$type[1]) : hint;
if (type == null) {
throw new AnimationInvokeException("Can't find the type in command " + invocationCommand);
}
String[] params = splitExceptWrapper(sParams, ',', false);
Object[] oArgs = new Object[params.length];
Class[] oArgClss = new Class[params.length];
for (int i = 0; i < params.length; i++) {
Result<?> result = invoke(params[i], null);
oArgs[i] = result.result;
oArgClss[i] = result.type;
for (Class<?> reservedClass : STRING_TO_OBJECT_PARSER.keySet()) {
if (!result.type.equals(reservedClass) && reservedClass.isAssignableFrom(result.type)) {
oArgClss[i] = reservedClass;
}
}
}
Constructor constructor = null;
try {
if (hint == null) {
throw new NoSuchMethodException();
}
constructor = hint.getConstructor(oArgClss);
} catch (NoSuchMethodException e) {
//EpicFightMod.LOGGER.debug("Can't find the matching constructor for the hint class " + hint + ". Use the given class " + type);
constructor = type.getConstructor(oArgClss);
}
return Result.of((Class<T>)type, (T)constructor.newInstance(oArgs));
}
/**
* @param hint has a higher priority than class specified in invocation command
*/
@SuppressWarnings("unchecked")
public static <T> Result<T[]> invokeArray(String invocationCommand, @Nullable Class<?> hint) throws Exception {
if (!invocationCommand.matches("\\[.+\\]") && !invocationCommand.matches("\\[.+\\]\\#.+")) {
throw new AnimationInvokeException("Invalid array invocation command: " + invocationCommand);
}
String[] param$type = splitExceptWrapper(invocationCommand, '#', true);
String sParams = param$type[0];
Class<?> type = param$type.length > 1 ? Class.forName(param$type[1]) : hint;
if (type == null) {
throw new AnimationInvokeException("Can't find the type in command " + invocationCommand);
}
String[] params = splitExceptWrapper(sParams, ',', false);
List<T> resultArray = Lists.newArrayList();
T[] result = (T[]) Array.newInstance(type, params.length);
for (int i = 0; i < params.length; i++) {
T obj = (T)invoke(params[i], type).result;
if (obj.getClass() != type) {
throw new AnimationInvokeException("Heterogeneous array elements for the command " + invocationCommand);
}
resultArray.add(obj);
result[i] = obj;
}
return Result.of((Class<T[]>)type.arrayType(), resultArray.toArray(result));
}
private static String[] splitExceptWrapper(String sArgs, char keyword, boolean skipWrapper) {
List<String> sArgsList = Lists.newArrayList();
int arrayNestCounter = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sArgs.length(); i++) {
char c = sArgs.charAt(i);
if (c == keyword) {
if (arrayNestCounter < 1) {
sArgsList.add(sb.toString());
sb.setLength(0);
} else {
sb.append(c);
}
} else if (c == '[' || c == '(') {
if (!skipWrapper || arrayNestCounter > 0) {
sb.append(c);
}
arrayNestCounter++;
} else if (c == ']' || c == ')') {
arrayNestCounter--;
if (!skipWrapper || arrayNestCounter > 0) {
sb.append(c);
}
} else {
sb.append(c);
}
}
if (!sb.isEmpty()) {
sArgsList.add(sb.toString());
}
return sArgsList.toArray(new String[0]);
}
public static class Result<T> {
final Class<T> type;
final T result;
Result(Class<T> type, T result) {
this.type = type;
this.result = result;
}
public static <T> Result<T> of(Class<T> type, T value) {
return new Result<>(type, value);
}
public Class<T> getType() {
return this.type;
}
public T getResult() {
return this.result;
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
import org.apache.commons.lang3.BooleanUtils;
public class MutableBoolean implements Comparable<MutableBoolean> {
boolean val;
public MutableBoolean() {
this(false);
}
public MutableBoolean(boolean init) {
this.val = init;
}
public boolean value() {
return this.val;
}
public void set(boolean val) {
this.val = val;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableBoolean) {
return this.val == ((MutableBoolean)obj).val;
}
return false;
}
@Override
public int hashCode() {
return this.val ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode();
}
@Override
public int compareTo(final MutableBoolean other) {
return BooleanUtils.compare(this.val, other.val);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.phys.Vec3;
import com.tiedup.remake.rig.math.Vec3f;
/**
* Backport de StreamCodec (MC 1.21.1) — encode/decode pour FriendlyByteBuf.
* Forké depuis EF, identique.
*/
public interface PacketBufferCodec<T> {
void encode(T obj, FriendlyByteBuf buffer);
T decode(FriendlyByteBuf buffer);
public static final PacketBufferCodec<Boolean> BOOLEAN = new PacketBufferCodec<>() {
@Override public void encode(Boolean obj, FriendlyByteBuf buffer) { buffer.writeBoolean(obj); }
@Override public Boolean decode(FriendlyByteBuf buffer) { return buffer.readBoolean(); }
};
public static final PacketBufferCodec<Integer> INTEGER = new PacketBufferCodec<>() {
@Override public void encode(Integer obj, FriendlyByteBuf buffer) { buffer.writeInt(obj); }
@Override public Integer decode(FriendlyByteBuf buffer) { return buffer.readInt(); }
};
public static final PacketBufferCodec<Float> FLOAT = new PacketBufferCodec<>() {
@Override public void encode(Float obj, FriendlyByteBuf buffer) { buffer.writeFloat(obj); }
@Override public Float decode(FriendlyByteBuf buffer) { return buffer.readFloat(); }
};
public static final PacketBufferCodec<Double> DOUBLE = new PacketBufferCodec<>() {
@Override public void encode(Double obj, FriendlyByteBuf buffer) { buffer.writeDouble(obj); }
@Override public Double decode(FriendlyByteBuf buffer) { return buffer.readDouble(); }
};
public static final PacketBufferCodec<Vec3> VEC3 = new PacketBufferCodec<>() {
@Override public void encode(Vec3 obj, FriendlyByteBuf buffer) {
buffer.writeDouble(obj.x); buffer.writeDouble(obj.y); buffer.writeDouble(obj.z);
}
@Override public Vec3 decode(FriendlyByteBuf buffer) {
return new Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
}
};
public static final PacketBufferCodec<Vec3f> VEC3F = new PacketBufferCodec<>() {
@Override public void encode(Vec3f obj, FriendlyByteBuf buffer) {
buffer.writeFloat(obj.x); buffer.writeFloat(obj.y); buffer.writeFloat(obj.z);
}
@Override public Vec3f decode(FriendlyByteBuf buffer) {
return new Vec3f(buffer.readFloat(), buffer.readFloat(), buffer.readFloat());
}
};
public static <T> PacketBufferCodec<TagKey<T>> tagKey(ResourceKey<Registry<T>> registry) {
return new PacketBufferCodec<>() {
@Override public void encode(TagKey<T> tagKey, FriendlyByteBuf buffer) {
buffer.writeResourceLocation(tagKey.registry().location());
buffer.writeResourceLocation(tagKey.location());
}
@Override public TagKey<T> decode(FriendlyByteBuf buffer) {
ResourceLocation reg = buffer.readResourceLocation();
ResourceLocation tagName = buffer.readResourceLocation();
return TagKey.create(ResourceKey.createRegistryKey(reg), tagName);
}
};
}
}

View File

@@ -0,0 +1,49 @@
/*
* Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
* by the Epic Fight Team, licensed under GPLv3.
* Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.rig.util;
public class TimePairList {
private final TimePair[] timePairs;
private TimePairList(TimePair[] timePairs) {
this.timePairs = timePairs;
}
public boolean isTimeInPairs(float time) {
for (TimePair timePair : this.timePairs) {
if (timePair.isTimeIn(time)) {
return true;
}
}
return false;
}
private static class TimePair {
public final float begin;
public final float end;
private TimePair(float begin, float end) {
this.begin = begin;
this.end = end;
}
private boolean isTimeIn(float time) {
return time >= this.begin && time < end;
}
}
public static TimePairList create(float... times) {
if (times.length % 2 != 0) {
throw new IllegalArgumentException("Time pair exception : number of given times is not an even number");
}
TimePair[] timePairs = new TimePair[times.length / 2];
for (int i = 0; i < times.length / 2; i++) {
timePairs[i] = new TimePair(times[i * 2], times[i * 2 + 1]);
}
return new TimePairList(timePairs);
}
}