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