This commit is contained in:
2026-02-23 18:33:18 +01:00
parent a7f256b383
commit fd19e2fce1
12 changed files with 656 additions and 17 deletions

View File

@@ -0,0 +1,142 @@
package dev.tggamesyt.szar;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ai.goal.*;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.mob.PathAwareEntity;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Optional;
import java.util.UUID;
public class KidEntity extends PathAwareEntity {
private static final TrackedData<Integer> AGE =
DataTracker.registerData(KidEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Optional<UUID>> PARENT_A =
DataTracker.registerData(KidEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
private static final TrackedData<Optional<UUID>> PARENT_B =
DataTracker.registerData(KidEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
public static final int MAX_AGE = 360000; // 30 days * 10h
public KidEntity(EntityType<KidEntity> type, World world) {
super(type, world);
}
@Override
protected void initDataTracker() {
super.initDataTracker();
this.dataTracker.startTracking(AGE, 0);
this.dataTracker.startTracking(PARENT_A, Optional.empty());
this.dataTracker.startTracking(PARENT_B, Optional.empty());
}
public float getAgeFraction() {
return Math.min(dataTracker.get(AGE) / (float) MAX_AGE, 1.0f);
}
@Override
protected void initGoals() {
this.goalSelector.add(0, new SwimGoal(this));
this.goalSelector.add(1, new FleeEntityGoal<>(this, PlayerEntity.class, 6F, 1.2, 1.5));
this.goalSelector.add(2, new WanderAroundFarGoal(this, 1.0));
this.goalSelector.add(3, new LookAroundGoal(this));
//this.goalSelector.add(4, new KidGoToBedGoal(this, 1.0));
this.targetSelector.add(1, new RevengeGoal(this));
}
@Override
public void tick() {
super.tick();
if (!getWorld().isClient) {
int age = dataTracker.get(AGE);
age++;
dataTracker.set(AGE, age);
// Remove brutal killing; just let the kid “grow up” visually
if (age > MAX_AGE) {
// Optionally, you can clamp AGE to MAX_AGE to avoid overflow
dataTracker.set(AGE, MAX_AGE);
}
// Forget anger quickly
if (this.getAttacker() != null && age % 60 == 0) {
this.setTarget(null);
this.setAttacker(null);
}
}
}
public static DefaultAttributeContainer.Builder createAttributes() {
return PathAwareEntity.createMobAttributes()
.add(EntityAttributes.GENERIC_MAX_HEALTH, 20.0)
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.25)
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 2);
}
public float getGrowthScale() {
return 0.3f + getAgeFraction() * 0.7f;
}
public UUID getParentA() {
return this.dataTracker.get(PARENT_A).orElse(null);
}
public UUID getParentB() {
return this.dataTracker.get(PARENT_B).orElse(null);
}
// Used for consistent skin generation
public long getHybridSeed() {
UUID a = getParentA();
UUID b = getParentB();
if (a == null || b == null) return 0L;
return a.getMostSignificantBits() ^ b.getLeastSignificantBits();
}
public void setParents(UUID a, UUID b) {
this.dataTracker.set(PARENT_A, Optional.ofNullable(a));
this.dataTracker.set(PARENT_B, Optional.ofNullable(b));
}
@Override
public void writeCustomDataToNbt(NbtCompound nbt) {
super.writeCustomDataToNbt(nbt);
UUID a = getParentA();
UUID b = getParentB();
if (a != null) nbt.putUuid("ParentA", a);
if (b != null) nbt.putUuid("ParentB", b);
nbt.putInt("Age", dataTracker.get(AGE));
}
@Override
public void readCustomDataFromNbt(NbtCompound nbt) {
super.readCustomDataFromNbt(nbt);
if (nbt.containsUuid("ParentA"))
dataTracker.set(PARENT_A, Optional.of(nbt.getUuid("ParentA")));
if (nbt.containsUuid("ParentB"))
dataTracker.set(PARENT_B, Optional.of(nbt.getUuid("ParentB")));
dataTracker.set(AGE, nbt.getInt("Age"));
}
}

View File

@@ -0,0 +1,116 @@
package dev.tggamesyt.szar;
import dev.tggamesyt.szar.KidEntity;
import net.minecraft.block.BedBlock;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import java.util.EnumSet;
public class KidGoToBedGoal extends Goal {
private final KidEntity kid;
private final double speed;
private BlockPos targetBed;
private BlockPos pathTarget;
public KidGoToBedGoal(KidEntity kid, double speed) {
this.kid = kid;
this.speed = speed;
this.setControls(EnumSet.of(Control.MOVE, Control.LOOK));
}
@Override
public boolean canStart() {
if (kid.getWorld().isClient) return false;
long time = kid.getWorld().getTimeOfDay() % 24000L;
if (time < 13000L || time > 23000L) return false; // night only
targetBed = findNearestBed();
if (targetBed == null) return false;
pathTarget = getAdjacentWalkable(targetBed);
return pathTarget != null;
}
private BlockPos findNearestBed() {
ServerWorld world = (ServerWorld) kid.getWorld();
BlockPos pos = kid.getBlockPos();
int radius = 16;
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dz = -radius; dz <= radius; dz++) {
BlockPos check = pos.add(dx, dy, dz);
if (world.getBlockState(check).getBlock() instanceof BedBlock) {
return check;
}
}
}
}
return null;
}
private BlockPos getAdjacentWalkable(BlockPos bed) {
// pick one of the 4 cardinal blocks adjacent to bed that is walkable
ServerWorld world = (ServerWorld) kid.getWorld();
BlockPos[] candidates = {
bed.north(),
bed.south(),
bed.east(),
bed.west()
};
for (BlockPos candidate : candidates) {
if (world.isAir(candidate) && world.isAir(candidate.up())) {
return candidate;
}
}
return null; // no free adjacent block
}
@Override
public boolean shouldContinue() {
return pathTarget != null && !kid.getNavigation().isIdle();
}
@Override
public void start() {
if (pathTarget != null && targetBed != null) {
kid.setSleepingPosition(targetBed);
kid.getNavigation().startMovingTo(
pathTarget.getX() + 0.5,
pathTarget.getY(),
pathTarget.getZ() + 0.5,
speed
);
}
}
@Override
public void tick() {
if (pathTarget != null) {
double dx = pathTarget.getX() + 0.5 - kid.getX();
double dy = pathTarget.getY() - kid.getY();
double dz = pathTarget.getZ() + 0.5 - kid.getZ();
if (dx*dx + dy*dy + dz*dz > 1.0) { // ~1 block distance
kid.getNavigation().startMovingTo(
pathTarget.getX() + 0.5,
pathTarget.getY(),
pathTarget.getZ() + 0.5,
speed
);
}
}
}
@Override
public void stop() {
if (targetBed != null) {
kid.clearSleepingPosition();
targetBed = null;
pathTarget = null;
}
}
}

View File

@@ -0,0 +1,50 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.AttributeContainer;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectCategory;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import java.util.UUID;
public class PregnantEffect extends StatusEffect {
public PregnantEffect() {
super(StatusEffectCategory.BENEFICIAL, 0xFF66CC); // pink color
}
@Override
public boolean canApplyUpdateEffect(int duration, int amplifier) {
return false; // no ticking needed, only care about end
}
@Override
public void onRemoved(LivingEntity entity, AttributeContainer attributes, int amplifier) {
if (!entity.getWorld().isClient && entity instanceof ServerPlayerEntity player) {
ServerWorld world = (ServerWorld) player.getWorld();
UUID partnerUuid = Szar.pregnantPartners.remove(player.getUuid());
ServerPlayerEntity partner;
if (partnerUuid != null) {
partner = (ServerPlayerEntity) world.getPlayerByUuid(partnerUuid);
} else {
partner = world.getPlayers().stream()
.filter(p -> p != player)
.min((a, b) -> Double.compare(player.squaredDistanceTo(a), player.squaredDistanceTo(b)))
.orElse(player);
}
KidEntity kid = Szar.Kid.create(world);
if (kid != null) {
kid.refreshPositionAndAngles(player.getX(), player.getY(), player.getZ(),
player.getYaw(), player.getPitch());
kid.setParents(player.getUuid(), partner.getUuid());
world.spawnEntity(kid);
}
}
super.onRemoved(entity, attributes, amplifier);
}
}

View File

@@ -1,20 +1,16 @@
package dev.tggamesyt.szar;
import com.google.common.collect.ImmutableSet;
import com.mojang.serialization.Codec;
import dev.tggamesyt.szar.PlaneAnimation;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
@@ -29,6 +25,7 @@ import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.*;
@@ -44,21 +41,16 @@ import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.structure.rule.RuleTest;
import net.minecraft.structure.rule.RuleTestType;
import net.minecraft.structure.rule.TagMatchRuleTest;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
import net.minecraft.util.collection.DataPool;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.math.BlockPos;
import net.minecraft.village.TradeOffer;
import net.minecraft.village.VillagerProfession;
import net.minecraft.world.Heightmap;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.YOffset;
@@ -67,13 +59,10 @@ import net.minecraft.world.gen.placementmodifier.BiomePlacementModifier;
import net.minecraft.world.gen.placementmodifier.CountPlacementModifier;
import net.minecraft.world.gen.placementmodifier.HeightRangePlacementModifier;
import net.minecraft.world.gen.placementmodifier.SquarePlacementModifier;
import net.minecraft.world.gen.stateprovider.BlockStateProvider;
import net.minecraft.world.gen.stateprovider.WeightedBlockStateProvider;
import net.minecraft.world.gen.structure.StructureType;
import net.minecraft.world.poi.PointOfInterestType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.jmx.Server;
import java.util.HashMap;
import java.util.List;
@@ -161,6 +150,15 @@ public class Szar implements ModInitializer {
.dimensions(EntityDimensions.fixed(0.6F, 1.8F)) // player-sized
.build()
);
public static final EntityType<KidEntity> Kid =
Registry.register(
Registries.ENTITY_TYPE,
new Identifier(MOD_ID, "kid"),
FabricEntityTypeBuilder.create(SpawnGroup.CREATURE,
KidEntity::new) // ✅ matches EntityType<KidEntity>
.dimensions(EntityDimensions.fixed(0.6F, 1.8F))
.build()
);
public static final EntityType<EpsteinEntity> EpsteinEntityType =
Registry.register(
Registries.ENTITY_TYPE,
@@ -284,6 +282,7 @@ public class Szar implements ModInitializer {
})
.build()
);
private final Map<UUID, BlockPos> sleepingPlayers = new HashMap<>();
@Override
public void onInitialize() {
ServerCosmetics.init();
@@ -435,6 +434,10 @@ public class Szar implements ModInitializer {
NiggerEntityType,
NiggerEntity.createAttributes()
);
FabricDefaultAttributeRegistry.register(
Kid,
KidEntity.createAttributes()
);
FabricDefaultAttributeRegistry.register(
EpsteinEntityType,
NiggerEntity.createAttributes()
@@ -588,7 +591,20 @@ public class Szar implements ModInitializer {
}
});
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
if (player.isSleeping()) {
BlockPos bedPos = player.getSleepingPosition().orElse(null);
if (bedPos != null && !sleepingPlayers.containsKey(player.getUuid())) {
sleepingPlayers.put(player.getUuid(), bedPos);
checkSleepPairs(server, player, bedPos);
}
} else {
// remove on wakeup
sleepingPlayers.remove(player.getUuid());
}
}
});
}
public static final StructurePieceType TNT_OBELISK_PIECE =
Registry.register(
@@ -635,6 +651,10 @@ public class Szar implements ModInitializer {
new Identifier(MOD_ID, "radiation"),
new RadiationStatusEffect()
);
public static final StatusEffect PREGNANT =
Registry.register(Registries.STATUS_EFFECT,
new Identifier(Szar.MOD_ID, "pregnant"),
new PregnantEffect());
public static final RegistryKey<DamageType> RADIATION_DAMAGE =
RegistryKey.of(RegistryKeys.DAMAGE_TYPE, new Identifier(MOD_ID, "radiation"));
public static final Item AK_AMMO = Registry.register(
@@ -1105,5 +1125,36 @@ public class Szar implements ModInitializer {
));
}
}
public static final Map<UUID, UUID> pregnantPartners = new HashMap<>();
private void checkSleepPairs(MinecraftServer server, ServerPlayerEntity sleeper, BlockPos bedPos) {
double maxDist = 2.0;
for (ServerPlayerEntity other : server.getPlayerManager().getPlayerList()) {
if (other == sleeper) continue;
if (other.isSleeping()) {
BlockPos otherPos = other.getSleepingPosition().orElse(null);
if (otherPos != null && otherPos.isWithinDistance(bedPos, maxDist)) {
// Determine who is holding the special item
if (isHoldingSpecial(sleeper)) {
// The OTHER player gets the effect
givePregnantEffect(other, sleeper);
} else if (isHoldingSpecial(other)) {
givePregnantEffect(sleeper, other);
}
}
}
}
}
private boolean isHoldingSpecial(ServerPlayerEntity p) {
return p.getMainHandStack().getItem() == FASZITEM;
}
private void givePregnantEffect(ServerPlayerEntity player, ServerPlayerEntity partner) {
player.addStatusEffect(new StatusEffectInstance(PREGNANT, 20 * 60 * 20, 0, false, false, true));
pregnantPartners.put(player.getUuid(), partner.getUuid());
}
}

View File

@@ -70,5 +70,7 @@
"death.attack.radiation": "%1$s radiated away",
"death.attack.radiation.player": "%1$s was lethally irradiated by %2$s",
"entity.szar.merl": "Merl",
"item.szar.merl_spawn_egg": "Merl Spawn Egg"
"item.szar.merl_spawn_egg": "Merl Spawn Egg",
"effect.szar.pregnant": "Pregnant",
"entity.szar.kid": "Kid"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB