22
33import net .minecraft .core .BlockPos ;
44import net .minecraft .core .HolderLookup ;
5+ import net .minecraft .core .particles .ItemParticleOption ;
6+ import net .minecraft .core .particles .ParticleTypes ;
57import net .minecraft .nbt .CompoundTag ;
68import net .minecraft .nbt .ListTag ;
79import net .minecraft .nbt .NbtOps ;
810import net .minecraft .nbt .Tag ;
911import net .minecraft .server .level .ServerLevel ;
12+ import net .minecraft .sounds .SoundEvents ;
13+ import net .minecraft .sounds .SoundSource ;
1014import net .minecraft .world .entity .EntityType ;
1115import net .minecraft .world .entity .animal .chicken .Chicken ;
16+ import net .minecraft .world .item .ItemStack ;
17+ import net .minecraft .world .item .Items ;
1218import net .minecraft .world .level .Level ;
1319import net .minecraft .world .level .block .entity .BlockEntity ;
1420import net .minecraft .world .level .block .state .BlockState ;
@@ -35,6 +41,7 @@ public class ChickenCoopEntity extends BlockEntity {
3541 private final List <CompoundTag > storedChickensNbt = new ArrayList <>();
3642 private boolean isNightMode = false ;
3743 private int eggCount = 0 ;
44+ private int eggsLaidThisNight = 0 ;
3845 private static final int MAX_EGGS = 16 ; // Limit storage so it's not infinite
3946
4047 public ChickenCoopEntity (BlockPos pos , BlockState state ) {
@@ -45,38 +52,98 @@ public static void tick(Level level, BlockPos pos, BlockState state, ChickenCoop
4552 if (level .isClientSide ()) return ;
4653
4754 long time = level .getDayTime () % 24000 ;
48- boolean isLate = time >= 13000 && time < 23000 ; // Monster spawn window
55+ boolean isLate = time >= 13000 && time < 23000 ;
4956
57+ // Transition to Night
5058 if (isLate && !be .isNightMode ) {
51- System .out .println ("Chicken Coop: Attempting to collect chickens!" );
5259 be .collectChickens ((ServerLevel ) level , pos );
5360 be .isNightMode = true ;
61+ be .eggsLaidThisNight = 0 ; // Reset the counter for the new night
5462 be .setChanged ();
55- } else if (!isLate && be .isNightMode ) {
63+ }
64+ // Transition to Day
65+ else if (!isLate && be .isNightMode ) {
5666 be .releaseChickens ((ServerLevel ) level , pos );
5767 be .isNightMode = false ;
5868 be .setChanged ();
5969 }
6070
61- // If it's night and we have chickens inside, try to lay eggs
62- if (be .isNightMode && !be .storedChickensNbt .isEmpty () && be .eggCount < MAX_EGGS ) {
63- // Minecraft chickens lay eggs every 6000-12000 ticks.
64- // With up to 6 chickens, a 1 in 1000 chance per tick is roughly realistic.
65- if (level .random .nextInt (1000 ) < be .storedChickensNbt .size ()) {
66- be .eggCount ++;
67- be .setChanged ();
68- // Optional: Play a muffled chicken sound from inside the coop
69- level .playSound (null , pos , SoundEvents .CHICKEN_EGG , SoundSource .BLOCKS , 0.5f , 1.0f );
71+ // Egg Laying Logic
72+ if (be .isNightMode && !be .storedChickensNbt .isEmpty ()) {
73+ int chickensInside = be .storedChickensNbt .size ();
74+
75+ // Condition 1: Total coop storage isn't full (MAX_EGGS = 16)
76+ // Condition 2: This specific night hasn't exceeded the chicken count
77+ if (be .eggCount < MAX_EGGS && be .eggsLaidThisNight < chickensInside ) {
78+
79+ // 1 in 1000 chance per tick is balanced for a full night
80+ if (level .random .nextInt (1000 ) == 0 ) {
81+ be .eggCount ++;
82+ be .eggsLaidThisNight ++; // This ensures this specific chicken is "done" for the night
83+ be .setChanged ();
84+
85+ level .playSound (null , pos , SoundEvents .CHICKEN_EGG , SoundSource .BLOCKS , 0.5f , 1.2f );
86+ }
7087 }
7188 }
7289 }
7390
74- // Helper for the player to interact
75- public int takeEgg () {
76- if (eggCount > 0 ) {
77- eggCount --;
78- setChanged ();
79- return 1 ;
91+ private void spawnFeatherParticles (ServerLevel level , BlockPos pos , int count ) {
92+ // Define the particle type using the Feather item texture
93+ ItemParticleOption particleData =
94+ new ItemParticleOption (
95+ ParticleTypes .ITEM ,
96+ new ItemStack (Items .FEATHER )
97+ );
98+
99+ // Spawn the particles
100+ // Parameters: particle, pos.x, pos.y, pos.z, count, speedX, speedY, speedZ, velocityScale
101+ level .sendParticles (particleData ,
102+ pos .getX () + 0.5 , pos .getY () + 0.5 , pos .getZ () + 0.5 ,
103+ count , // amount of feathers
104+ 0.3 , 0.3 , 0.3 , // spread (delta)
105+ 0.15 // speed/velocity
106+ );
107+ }
108+
109+ @ Override
110+ protected void saveAdditional (ValueOutput valueOutput ) {
111+ super .saveAdditional (valueOutput );
112+
113+ // Save the simple primitives
114+ valueOutput .putInt ("EggCount" , this .eggCount );
115+ valueOutput .putBoolean ("IsNightMode" , this .isNightMode );
116+
117+ // Save the list of chickens using your TypedOutputList logic
118+ // We use the CompoundTag.CODEC to store the raw NBT of each chicken
119+ ValueOutput .TypedOutputList <CompoundTag > chickenList = valueOutput .list ("StoredChickens" , CompoundTag .CODEC );
120+ for (CompoundTag chickenNbt : storedChickensNbt ) {
121+ chickenList .add (chickenNbt );
122+ }
123+ }
124+
125+ @ Override
126+ protected void loadAdditional (ValueInput valueInput ) {
127+ super .loadAdditional (valueInput );
128+
129+ this .eggCount = valueInput .getIntOr ("EggCount" , 0 );
130+ this .isNightMode = valueInput .getBooleanOr ("IsNightMode" , false );
131+
132+ // Clear current chickens
133+ this .storedChickensNbt .clear ();
134+
135+ // Use map to transform the Optional<TypedInputList> into a Stream of NBT
136+ valueInput .list ("StoredChickens" , CompoundTag .CODEC )
137+ .map (ValueInput .TypedInputList ::stream )
138+ .ifPresent (stream -> stream .forEach (this .storedChickensNbt ::add ));
139+ }
140+
141+ public int takeAllEggs () {
142+ int total = this .eggCount ;
143+ if (total > 0 ) {
144+ this .eggCount = 0 ;
145+ this .setChanged ();
146+ return total ;
80147 }
81148 return 0 ;
82149 }
@@ -91,10 +158,11 @@ private void collectChickens(ServerLevel level, BlockPos pos) {
91158
92159 // Wrap our CompoundTag in the ValueOutput implementation
93160 ValueOutput output = new ChickenValueOutput (chickenData , level .registryAccess ());
161+
162+ spawnFeatherParticles (level , chicken .blockPosition (), 15 );
94163
95164 // Satisfies the method signature perfectly!
96165 chicken .saveWithoutId (output );
97-
98166 // Store the result
99167 storedChickensNbt .add (chickenData );
100168 chicken .discard ();
@@ -127,6 +195,8 @@ private void releaseChickens(ServerLevel level, BlockPos pos) {
127195 );
128196
129197 level .addFreshEntity (chicken );
198+
199+ spawnFeatherParticles (level , chicken .blockPosition (), 15 );
130200 }
131201
132202 storedChickensNbt .clear ();
@@ -336,8 +406,31 @@ public ValueInput childOrEmpty(String key) {
336406
337407 @ Override public HolderLookup .Provider lookup () { return registries ; }
338408
339- // Minimal implementation for Lists (can be expanded if chickens store list data)
340- @ Override public <T > Optional <TypedInputList <T >> list (String key , Codec <T > codec ) { return Optional .empty (); }
409+ @ Override
410+ public <T > Optional <TypedInputList <T >> list (String key , Codec <T > codec ) {
411+ // Check if the key exists and is a List
412+ if (!tag .contains (key )) return Optional .empty ();
413+
414+ // In modern mappings, getList only takes the Key.
415+ // It returns the list if found, or an empty one if not.
416+ Optional <ListTag > listTag = tag .getList (key );
417+
418+ // Map the NBT tags to objects using the codec
419+ List <T > items = listTag .stream ()
420+ .map (nbt -> codec .parse (registries .createSerializationContext (NbtOps .INSTANCE ), nbt )
421+ .resultOrPartial (System .err ::println ))
422+ .flatMap (Optional ::stream ) // Flattens Optional<T> into the stream
423+ .toList ();
424+
425+ return Optional .of (new ChickenTypedInputList <>(items ));
426+ }
427+
428+ // Ensure the helper record implements stream()
429+ private record ChickenTypedInputList <T >(List <T > items ) implements TypedInputList <T > {
430+ @ Override public boolean isEmpty () { return items .isEmpty (); }
431+ @ Override public Stream <T > stream () { return items .stream (); }
432+ @ Override public java .util .Iterator <T > iterator () { return items .iterator (); }
433+ }
341434 @ Override public <T > TypedInputList <T > listOrEmpty (String key , Codec <T > codec ) {
342435 return new TypedInputList <T >() {
343436 @ Override public boolean isEmpty () { return true ; }
0 commit comments