11package de .erethon .aether .creature ;
22
3- import com .magmaguy .freeminecraftmodels .customentity .DynamicEntity ;
3+ import com .magmaguy .freeminecraftmodels .animation .AnimationStateType ;
4+ import com .magmaguy .freeminecraftmodels .customentity .ModeledEntity ;
45import com .magmaguy .freeminecraftmodels .utils .DataMappings ;
56import de .erethon .aether .Aether ;
6- import net .minecraft .server .level .ServerPlayer ;
7+ import de .erethon .hecate .Hecate ;
8+ import de .erethon .hecate .ui .EntityStatusDisplay ;
9+ import de .erethon .papyrus .CraftPDamageType ;
10+ import net .minecraft .server .level .ServerLevel ;
11+ import net .minecraft .world .damagesource .DamageSource ;
12+ import net .minecraft .world .entity .Entity ;
713import net .minecraft .world .entity .EntityType ;
814import net .minecraft .world .entity .Mob ;
15+ import net .minecraft .world .entity .ai .attributes .Attributes ;
916import net .minecraft .world .level .Level ;
17+ import org .bukkit .Location ;
18+ import org .bukkit .NamespacedKey ;
1019import org .bukkit .World ;
20+ import org .bukkit .entity .TextDisplay ;
21+ import org .bukkit .event .entity .EntityRemoveEvent ;
22+ import org .bukkit .persistence .PersistentDataType ;
23+ import org .jetbrains .annotations .Nullable ;
1124
1225public class ModelledMob extends AetherBaseMob {
1326
14- private DynamicEntity model ;
27+ private static final NamespacedKey MARKER_KEY = new NamespacedKey ("erethon" , "no_nametag" );
28+
29+ private ModeledEntity model ;
30+
31+ private EntityStatusDisplay display ;
32+ private TextDisplay healthDisplay ;
33+ private TextDisplay nameTag ;
34+ private TextDisplay statusDisplay ;
35+ private double hitboxHeight ;
36+
37+ private AnimationStateType currentMovementState = AnimationStateType .IDLE ;
1538
1639 public ModelledMob (EntityType <? extends Mob > type , Level world ) {
1740 super (type , world );
@@ -23,53 +46,108 @@ public ModelledMob(NPCData data, World world, Integer overrideLevel) {
2346
2447 public ModelledMob (NPCData data , World world ) {
2548 super (data , world );
26- displayType = EntityType .PILLAGER ; // Hardcode this for now
49+ displayType = EntityType .PIG ; // Hardcode this for now
2750 String id = data .cfg .getString ("modelId" );
2851 if (id == null ) {
2952 Aether .addException ("ModelledMob " + data .getID (), "Missing modelId" , "Add a modelId or remove the ModelledMob base class" , null );
3053 return ;
3154 }
3255 entityData = DataMappings .getSynchedEntityData (displayType );
33- this .model = DynamicEntity .create (id , getBukkitLivingEntity ());
34- setInvisible (true );
56+ this .model = new ModeledEntity (id , getBukkitEntity ().getLocation ());
57+ hitboxHeight = model .getSkeletonBlueprint ().getHitbox ().getHeight ();
58+ getBukkitEntity ().setVisibleByDefault (false );
59+ getBukkitEntity ().getPersistentDataContainer ().set (MARKER_KEY , PersistentDataType .BOOLEAN , true );
60+ onLoad ();
61+ onFirstSpawn ();
3562 }
3663
3764 @ Override
3865 public void addToWorld () {
3966 super .addToWorld ();
67+ model .spawn (getBukkitEntity ());
68+ model .setUnderlyingEntity (getBukkitEntity ());
69+ Hecate hecate = Hecate .getInstance ();
70+ display = new EntityStatusDisplay (getBukkitLivingEntity ());
71+ healthDisplay = display .healthDisplay ;
72+ nameTag = display .entityNameTag ;
73+ statusDisplay = display .statusDisplay ;
74+ if (healthDisplay == null || nameTag == null || statusDisplay == null ) {
75+ return ;
76+ }
77+ hecate .getStatusDisplayManager ().addStatusDisplay (getBukkitLivingEntity (), display );
78+ // We will handle positioning ourselves to account for the entity changes
79+ getBukkitEntity ().removePassenger (nameTag );
80+ getBukkitEntity ().removePassenger (healthDisplay );
81+ getBukkitEntity ().removePassenger (statusDisplay );
82+ healthDisplay .setTeleportDuration (1 );
83+ statusDisplay .setTeleportDuration (1 );
84+ nameTag .setTeleportDuration (1 );
4085 }
4186
42- private void syncSkeletonWithEntity () {
43- if ( isDeadOrDying ()) return ;
44- model .getSkeleton ().setCurrentHeadPitch ( getBukkitLivingEntity (). getEyeLocation (). getPitch () );
45- model . getSkeleton (). setCurrentHeadYaw ( getBukkitLivingEntity (). getEyeLocation (). getYaw () );
87+ @ Override
88+ public boolean hurtServer ( ServerLevel level , DamageSource source , float amount , CraftPDamageType type ) {
89+ model .getSkeleton ().tint ( );
90+ return super . hurtServer ( level , source , amount , type );
4691 }
4792
4893 @ Override
49- public void startSeenByPlayer (ServerPlayer serverPlayer ) {
50- super .startSeenByPlayer (serverPlayer );
51- refreshEntityData (serverPlayer );
94+ public boolean doHurtTarget (ServerLevel level , Entity target ) {
95+ if (model .hasAnimation ("attack" )) {
96+ model .playAnimation ("attack" , false , false );
97+ }
98+ return super .doHurtTarget (level , target );
5299 }
53100
54101 @ Override
55- public void stopSeenByPlayer (ServerPlayer serverPlayer ) {
56- super .stopSeenByPlayer (serverPlayer );
102+ public void die (DamageSource damageSource ) {
103+ super .die (damageSource );
104+ if (model .hasAnimation ("death" )) {
105+ model .removeWithDeathAnimation ();
106+ } else {
107+ model .removeWithMinimizedAnimation ();
108+ }
109+ }
110+
111+ @ Override
112+ public void onRemoval (RemovalReason reason ) {
113+ super .onRemoval (reason );
114+ model .setMarkForRemoval (true );
115+ }
116+
117+ private void syncSkeletonWithEntity () {
118+ if (isDeadOrDying ()) return ;
119+ model .getSkeleton ().setCurrentHeadPitch (getBukkitLivingEntity ().getEyeLocation ().getPitch ());
120+ model .getSkeleton ().setCurrentHeadYaw (getBukkitLivingEntity ().getEyeLocation ().getYaw ());
57121 }
58122
59123 @ Override
60124 protected void onFirstSpawn () {
125+ super .onFirstSpawn ();
61126 setInvisible (true );
62127 }
63128
64129 @ Override
65130 public void tick () {
66131 super .tick ();
67132 syncSkeletonWithEntity ();
68- }
133+ model .tick ();
134+ // Determine movement state based on velocity
135+ boolean isMoving = getDeltaMovement ().horizontalDistanceSqr () > 0.0001 && !isDeadOrDying ();
136+ AnimationStateType targetState = isMoving ? AnimationStateType .WALK : AnimationStateType .IDLE ;
69137
70- @ Override
71- public void remove (RemovalReason reason ) {
72- super .remove (reason );
73- model .remove ();
138+ if (targetState != currentMovementState ) {
139+ model .getAnimationComponent ().playAnimation (targetState .name ().toLowerCase (), false , true );
140+ currentMovementState = targetState ;
141+ }
142+ if (nameTag == null || healthDisplay == null || statusDisplay == null ) {
143+ return ;
144+ }
145+ // Update displays
146+ Location loc = getBukkitEntity ().getLocation ().add (0 , hitboxHeight + 0.18f , 0 );
147+ loc .setPitch (0 );
148+ loc .setYaw (0 );
149+ nameTag .teleport (loc );
150+ healthDisplay .teleport (loc );
151+ statusDisplay .teleport (loc );
74152 }
75153}
0 commit comments