Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
1package net.lerariemann.infinity.entity.custom;
2
3import net.lerariemann.infinity.InfinityMod;
4import net.lerariemann.infinity.registry.core.ModEntities;
5import net.lerariemann.infinity.util.VersionMethods;
6import net.lerariemann.infinity.util.core.ConfigType;
7import net.lerariemann.infinity.util.core.NbtUtils;
8import net.lerariemann.infinity.util.platform.InfinityPlatform;
9import net.minecraft.core.Holder;
10import net.minecraft.core.particles.ParticleTypes;
11import net.minecraft.core.registries.BuiltInRegistries;
12import net.minecraft.core.registries.Registries;
13import net.minecraft.nbt.CompoundTag;
14import net.minecraft.network.chat.Component;
15import net.minecraft.network.syncher.EntityDataAccessor;
16import net.minecraft.network.syncher.EntityDataSerializers;
17import net.minecraft.network.syncher.SynchedEntityData;
18import net.minecraft.server.level.ServerLevel;
19import net.minecraft.sounds.SoundEvents;
20import net.minecraft.world.DifficultyInstance;
21import net.minecraft.world.InteractionHand;
22import net.minecraft.world.InteractionResult;
23import net.minecraft.world.damagesource.DamageSource;
24import net.minecraft.world.effect.MobEffect;
25import net.minecraft.world.effect.MobEffectInstance;
26import net.minecraft.world.entity.EntityType;
27import net.minecraft.world.entity.LivingEntity;
28import net.minecraft.world.entity.SpawnGroupData;
29import net.minecraft.world.entity.monster.Skeleton;
30import net.minecraft.world.entity.player.Player;
31import net.minecraft.world.item.ItemStack;
32import net.minecraft.world.item.ItemUtils;
33import net.minecraft.world.item.Items;
34//? if >1.21 {
35import net.minecraft.world.item.alchemy.PotionContents;
36import net.minecraft.core.component.DataComponents;
37//?} else {
38/*import net.minecraft.world.item.alchemy.PotionUtils;
39*///?}
40import net.minecraft.world.item.enchantment.Enchantment;
41import net.minecraft.world.item.enchantment.Enchantments;
42import net.minecraft.world.level.Level;
43import net.minecraft.world.level.ServerLevelAccessor;
44//? if >1.21.4 {
45import net.minecraft.world.level.storage.ValueInput;
46import net.minecraft.world.level.storage.ValueOutput;
47import static net.minecraft.world.entity.EntitySpawnReason.CONVERSION;
48import net.minecraft.client.resources.language.I18n;
49//?}
50import org.jspecify.annotations.Nullable;
51
52import java.util.*;
53
54public class ChaosSkeleton extends Skeleton implements TintableEntity {
55 private static final EntityDataAccessor<String> effect = SynchedEntityData.defineId(ChaosSkeleton.class, EntityDataSerializers.STRING);
56 private static final EntityDataAccessor<Integer> color = SynchedEntityData.defineId(ChaosSkeleton.class, EntityDataSerializers.INT);
57 private static final EntityDataAccessor<Integer> duration = SynchedEntityData.defineId(ChaosSkeleton.class, EntityDataSerializers.INT);
58 public static Map<String, String> effect_lookup = Map.ofEntries(Map.entry("minecraft:unluck", "minecraft:luck"),
59 Map.entry("minecraft:bad_omen", "minecraft:hero_of_the_village"),
60 Map.entry("minecraft:darkness", "minecraft:night_vision"),
61 Map.entry("minecraft:blindness", "minecraft:night_vision"),
62 Map.entry("minecraft:glowing", "minecraft:invisibility"),
63 Map.entry("minecraft:hunger", "minecraft:saturation"),
64 Map.entry("minecraft:levitation", "minecraft:slow_falling"),
65 Map.entry("minecraft:mining_fatigue", "minecraft:haste"),
66 Map.entry("minecraft:poison", "minecraft:regeneration"),
67 Map.entry("minecraft:slowness", "minecraft:speed"),
68 Map.entry("minecraft:weakness", "minecraft:strength"),
69 Map.entry("minecraft:wither", "minecraft:regeneration"),
70 Map.entry("minecraft:instant_damage", "minecraft:instant_health"),
71 Map.entry("minecraft:instant_health", "minecraft:instant_damage"),
72 Map.entry("minecraft:luck", "minecraft:unluck"),
73 Map.entry("minecraft:hero_of_the_village", "minecraft:bad_omen"),
74 Map.entry("minecraft:night_vision", "minecraft:darkness"),
75 Map.entry("minecraft:invisibility", "minecraft:glowing"),
76 Map.entry("minecraft:saturation", "minecraft:hunger"),
77 Map.entry("minecraft:slow_falling", "minecraft:levitation"),
78 Map.entry("minecraft:haste", "minecraft:mining_fatigue"),
79 Map.entry("minecraft:regeneration", "minecraft:poison"),
80 Map.entry("minecraft:speed", "minecraft:slowness"),
81 Map.entry("minecraft:strength", "minecraft:weakness"));
82
83 public ChaosSkeleton(EntityType<? extends Skeleton> entityType, Level world) {
84 super(entityType, world);
85 }
86
87 @Override
88 public int getColorNamed() {
89 return hasCustomName() ? TintableEntity.getColorNamed(getName().getString(), tickCount, getId()) : -1;
90 }
91
92 @Override
93 @Nullable
94 public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty,
95 //? if >1.21.2 {
96 net.minecraft.world.entity.EntitySpawnReason
97 //?} else {
98 /*net.minecraft.world.entity.MobSpawnType
99 *///?}
100 spawnReason, @Nullable SpawnGroupData entityData
101 //? if <1.21 {
102 /*,CompoundTag tag
103 *///?}
104 ) {
105 Random r = new Random();
106 CompoundTag effect = InfinityMod.provider.randomElement(r, ConfigType.EFFECTS);
107 this.setEffect(effect);
108 this.setDuration(r.nextInt(600));
109 return super.finalizeSpawn(world, difficulty, spawnReason, entityData
110 //? if <1.21
111 /*,tag*/
112 );
113 }
114
115 @Override
116 protected void defineSynchedData(
117 //? if >1.21 {
118 SynchedEntityData.Builder builder
119 //?}
120 ) {
121 super.defineSynchedData(
122 //? if >1.21 {
123 builder
124 //?}
125 );
126 //? if <1.21 {
127 /*var builder = this.entityData;
128 *///?}
129 builder.define(effect, "luck");
130 builder.define(duration, 200);
131 builder.define(color, 0x00FF00);
132 }
133 @Override
134 public boolean isSunBurnTick() {
135 return false;
136 }
137
138 @Override
139 public InteractionResult mobInteract(Player player, InteractionHand hand) {
140 ItemStack itemStack = player.getItemInHand(hand);
141 if (itemStack.is(Items.FERMENTED_SPIDER_EYE) && effect_lookup.containsKey(this.getEffect())) {
142 if (!player.getAbilities().instabuild) {
143 itemStack.shrink(1);
144 }
145 if (player.level().getRandom().nextFloat() < 0.5) {
146 String i = effect_lookup.get(this.getEffect());
147 MobEffect newEffect = VersionMethods.getFromRegistry(BuiltInRegistries.MOB_EFFECT, VersionMethods.id(i));
148 if (newEffect != null) {
149 ChaosSkeleton newSkeleton;
150 if (!this.level().isClientSide() && (newSkeleton = ModEntities.CHAOS_SKELETON.get().create(this.level()
151 //? if >1.21.4 {
152 ,CONVERSION
153 //?}
154 )) != null) {
155 ((ServerLevel) this.level()).sendParticles(ParticleTypes.HEART, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0);
156 this.discard();
157 ModEntities.copy(this, newSkeleton);
158 newSkeleton.setDuration(this.getDuration());
159 newSkeleton.setEffect(i, newEffect.getColor());
160 this.level().addFreshEntity(newSkeleton);
161 return InteractionResult.SUCCESS;
162 }
163 }
164 }
165 }
166 if (itemStack.is(Items.GLASS_BOTTLE)) {
167 ItemStack itemStack2 = setPotion(Items.POTION.getDefaultInstance(), this.getColorForRender(), this.getEffect(), this.getDuration() * 20);
168 ItemStack itemStack3 = ItemUtils.createFilledResult(itemStack, player, itemStack2, false);
169 player.setItemInHand(hand, itemStack3);
170 this.playSound(SoundEvents.BOTTLE_FILL, 1.0f, 1.0f);
171 Skeleton newSkeleton;
172 if (!this.level().isClientSide() && (newSkeleton = EntityType.SKELETON.create(this.level()
173 //? if >1.21.4 {
174 , CONVERSION
175 //?}
176 )) != null) {
177 this.discard();
178 ModEntities.copy(this, newSkeleton);
179 this.level().addFreshEntity(newSkeleton);
180 return InteractionResult.SUCCESS;
181 }
182 }
183 return super.mobInteract(player, hand);
184 }
185
186 public void setEffect(CompoundTag eff) {
187 setEffect(NbtUtils.getString(eff, "Name"), NbtUtils.getInt(eff, "Color"));
188 }
189 public void setEffect(String eff, int c) {
190 if (eff.isBlank()) {
191 CompoundTag newEffect = InfinityMod.provider.randomElement(random, ConfigType.EFFECTS);
192 eff = NbtUtils.getString(newEffect, "Name");
193 c = NbtUtils.getInt(newEffect, "Color");
194 }
195 this.entityData.set(effect, eff);
196 this.entityData.set(color, c);
197 }
198 public String getEffect() {
199 return this.entityData.get(effect);
200 }
201 @Override
202 public int getColor() {
203 return this.entityData.get(color);
204 }
205
206 public void setDuration(int i) {
207 this.entityData.set(duration, i);
208 }
209 public int getDuration() {
210 return this.entityData.get(duration);
211 }
212 //? if >1.21.4 {
213
214 @Override
215 protected void addAdditionalSaveData(ValueOutput output) {
216 super.addAdditionalSaveData(output);
217 output.putString("effect", this.getEffect());
218 output.putInt("duration", this.getDuration());
219 output.putInt("color", this.getColor());
220 }
221
222 //?} else {
223 /*@Override
224 public void addAdditionalSaveData(CompoundTag nbt) {
225 super.addAdditionalSaveData(nbt);
226 nbt.putString("effect", this.getEffect());
227 nbt.putInt("duration", this.getDuration());
228 nbt.putInt("color", this.getColor());
229 }
230 *///?}
231
232 @Override
233 public void readAdditionalSaveData(
234 //? if >1.21.2 {
235 ValueInput
236 //?} else {
237 /*CompoundTag
238 *///?}
239 nbt) {
240 super.readAdditionalSaveData(nbt);
241 this.setEffect(NbtUtils.getString(nbt, "effect"), NbtUtils.getInt(nbt, "color"));
242 this.setDuration(NbtUtils.getInt(nbt,"duration"));
243 }
244
245 public static ItemStack setPotion(ItemStack stack, int color, String effect, int duration) {
246 List<MobEffectInstance> customEffects = new ArrayList<>();
247 //? if >1.21.4 {
248 customEffects.add(new MobEffectInstance(VersionMethods.getFromId(BuiltInRegistries.MOB_EFFECT, VersionMethods.id(effect)), duration));
249 //?} else {
250 /*CompoundTag potionEffect = new CompoundTag();
251 potionEffect.putString("id", effect);
252 potionEffect.putInt("duration", duration);
253 customEffects.add(MobEffectInstance.load(potionEffect));
254 *///?}
255 //? if >1.21.4 {
256 stack.set(DataComponents.POTION_CONTENTS, new PotionContents(Optional.empty(), Optional.of(color), customEffects, Optional.of(I18n.get("potion.infinity.skeleton"))));
257 //?} else if >1.21 {
258 /*stack.set(DataComponents.POTION_CONTENTS, new PotionContents(Optional.empty(), Optional.of(color), customEffects));
259 stack.set(DataComponents.ITEM_NAME, Component.translatable("potion.infinity.skeleton"));
260 *///?} else {
261 /*PotionUtils.setCustomEffects(stack, customEffects);
262 stack.setHoverName(Component.translatable("potion.infinity.skeleton"));
263 *///?}
264 return stack;
265 }
266
267 @Override
268 public ItemStack getProjectile(ItemStack stack) {
269 return getProjectileType();
270 }
271
272 public ItemStack getProjectileType() {
273 return setPotion(Items.TIPPED_ARROW.getDefaultInstance(), this.getColor(), this.getEffect(), this.getDuration());
274 }
275
276 //? if >1.21 {
277 @Override
278 protected void dropCustomDeathLoot(ServerLevel world, DamageSource source, boolean causedByPlayer) {
279 super.dropCustomDeathLoot(world, source, causedByPlayer);
280 ItemStack weapon = source.getWeaponItem();
281 Holder<Enchantment> looting = VersionMethods.getRegistry(world.getServer().registryAccess(), Registries.ENCHANTMENT).
282 //? if >1.21.4 {
283 getOrThrow
284 //?} else {
285 /*getHolderOrThrow
286 *///?}
287 (Enchantments.LOOTING);
288 int lootingLevel = weapon == null ? 0 : InfinityPlatform.INSTANCE.getEnchantments(weapon, world.getServer()).getLevel(looting);
289 dropEquipment(lootingLevel, world);
290 }
291 //?} else {
292 /*@Override
293 protected void dropCustomDeathLoot(DamageSource source, int lootingLevel, boolean allowDrops) {
294 super.dropCustomDeathLoot(source, lootingLevel, allowDrops);
295 dropEquipment(lootingLevel, (ServerLevel) this.level());
296 }
297 *///?}
298
299 private void dropEquipment(int lootingLevel, ServerLevel world) {
300 int count = world.random.nextIntBetweenInclusive(0, 2 + lootingLevel);
301 this.spawnAtLocation(
302 //? if >1.21.4
303 world,
304 getProjectileType().copyWithCount(count));
305 }
306}