This commit is contained in:
2026-02-27 13:52:05 +01:00
parent 3a78b7dc37
commit c19c9931bb
5 changed files with 305 additions and 8 deletions

View File

@@ -1,9 +1,7 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.TntEntity;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.TargetPredicate;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.ai.goal.LookAroundGoal;
@@ -15,13 +13,24 @@ import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.PathAwareEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class IslamTerrorist extends PathAwareEntity implements Arrestable{
private BlockPos targetCoreBlock = null; // the core block this mob is attacking
private Vec3d taxiDirection;
private Vec3d currentDirection = null; // direction plane is moving
private BlockPos taxiTarget;
private int flyStraightTicks = 0;
private int planeTaxiTicks = 0;
public static boolean arrestable = false;
private int BlowUpCooldown = 0;
private int panicTicks = 0;
@@ -56,6 +65,108 @@ public class IslamTerrorist extends PathAwareEntity implements Arrestable{
super.tick();
if (BlowUpCooldown > 0) BlowUpCooldown--;
Entity vehicle = this.getVehicle();
if (!(vehicle instanceof PlaneEntity plane) || targetCoreBlock == null) return;
Vec3d vel = plane.getVelocity();
// -------------------------
// TAXI PHASE: ground, random direction
// -------------------------
if (planeTaxiTicks > 0) {
planeTaxiTicks--;
if (taxiTarget != null) {
Vec3d toTarget = Vec3d.ofCenter(taxiTarget).subtract(plane.getPos());
if (toTarget.length() > 0.5) {
Vec3d desired = toTarget.normalize().multiply(0.4);
plane.setVelocity(vel.lerp(desired, 0.1));
}
}
// small lift near end
if (planeTaxiTicks < 20) {
plane.setVelocity(plane.getVelocity().x, 0.08, plane.getVelocity().z);
}
// Face movement
Vec3d look = plane.getVelocity().normalize();
if (look.length() > 0) {
plane.setYaw((float) Math.toDegrees(Math.atan2(-look.x, look.z)));
plane.setPitch((float) -Math.toDegrees(Math.asin(look.y)));
}
return;
}
// -------------------------
// FLY STRAIGHT PHASE
// -------------------------
if (flyStraightTicks > 0) {
flyStraightTicks--;
plane.setVelocity(currentDirection.x * 1.4, 0.25, currentDirection.z * 1.4);
Vec3d look = plane.getVelocity().normalize();
if (look.length() > 0) {
plane.setYaw((float) Math.toDegrees(Math.atan2(-look.x, look.z)));
plane.setPitch((float) -Math.toDegrees(Math.asin(look.y)));
}
return;
}
// -------------------------
// HOMING PHASE
// -------------------------
Vec3d target = Vec3d.ofCenter(targetCoreBlock);
Vec3d toTarget = target.subtract(plane.getPos());
Vec3d desired = toTarget.normalize().multiply(1.8);
// Add small upward lift if below target
if (plane.getY() < target.y - 10) {
desired = desired.add(0, 0.2, 0);
}
// Smooth turning toward target
currentDirection = currentDirection.lerp(desired.normalize(), 0.03).normalize();
plane.setVelocity(currentDirection.multiply(1.8));
// Face movement
Vec3d look = plane.getVelocity().normalize();
if (look.length() > 0) {
plane.setYaw((float) Math.toDegrees(Math.atan2(-look.x, look.z)));
plane.setPitch((float) -Math.toDegrees(Math.asin(look.y)));
}
// -------------------------
// IMPACT
// -------------------------
if (!getWorld().isClient &&
(plane.horizontalCollision || plane.verticalCollision)) {
getWorld().createExplosion(
plane,
plane.getX(),
plane.getY(),
plane.getZ(),
7.0f,
World.ExplosionSourceType.TNT
);
plane.discard();
this.discard();
BlockEntity be = getWorld().getBlockEntity(targetCoreBlock);
if (be instanceof ObeliskCoreBlockEntity core) {
core.setHasPlaneMob(false);
core.markDirty();
}
targetCoreBlock = null;
}
}
// ================= VISIBILITY =================
@@ -96,6 +207,19 @@ public class IslamTerrorist extends PathAwareEntity implements Arrestable{
this.velocityDirty = true;
}
@Override
public void onDeath(DamageSource source) {
super.onDeath(source);
if (targetCoreBlock == null) return;
BlockEntity be = getWorld().getBlockEntity(targetCoreBlock);
if (be instanceof ObeliskCoreBlockEntity core) {
core.setHasPlaneMob(false);
core.markDirty();
}
}
// ================= DAMAGE =================
@@ -119,6 +243,70 @@ public class IslamTerrorist extends PathAwareEntity implements Arrestable{
}
}
@Override
public EntityData initialize(
ServerWorldAccess worldAccess,
LocalDifficulty difficulty,
SpawnReason spawnReason,
@Nullable EntityData entityData,
@Nullable NbtCompound entityNbt
) {
EntityData data = super.initialize(worldAccess, difficulty, spawnReason, entityData, entityNbt);
if (!(worldAccess instanceof ServerWorld world)) return data;
BlockPos corePos = findNearbyCoreBlock(this.getBlockPos(), 100);
if (corePos == null) return data;
BlockEntity be = world.getBlockEntity(corePos);
if (!(be instanceof ObeliskCoreBlockEntity core)) return data;
if (!core.hasPlaneMob() && world.random.nextInt(100) == 0) {
PlaneEntity plane = new PlaneEntity(Szar.PLANE_ENTITY_TYPE, world);
plane.refreshPositionAndAngles(getX(), getY(), getZ(), getYaw(), getPitch());
world.spawnEntity(plane);
this.startRiding(plane, true);
core.setHasPlaneMob(true);
core.markDirty();
this.targetCoreBlock = corePos;
// Taxi + straight flight setup
this.planeTaxiTicks = 60; // 3 seconds ground taxi
this.flyStraightTicks = 40; // 2 seconds straight flight before homing
// Random horizontal direction
float randomYaw = world.random.nextFloat() * 360f;
this.currentDirection = Vec3d.fromPolar(0, randomYaw).normalize();
// Taxi target 20 blocks ahead in random direction
this.taxiTarget = this.getBlockPos().add(
(int)(currentDirection.x * 20),
0,
(int)(currentDirection.z * 20)
);
}
return data;
}
private BlockPos findNearbyCoreBlock(BlockPos pos, int radius) {
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 (getWorld().getBlockState(check).getBlock() == Szar.OBELISK_CORE) {
return check;
}
}
}
}
return null;
}
// 🔴 Flee from only specific victim, hide behind others
private static class FleeSpecificPlayerGoal extends Goal {
private final IslamTerrorist mob;

View File

@@ -1,17 +1,28 @@
package dev.tggamesyt.szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable;
public class ObeliskCoreBlock extends Block {
public class ObeliskCoreBlock extends Block implements BlockEntityProvider {
public ObeliskCoreBlock(Settings settings) {
super(settings);
}
@Nullable
@Override
public ObeliskCoreBlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new ObeliskCoreBlockEntity(pos, state);
}
@Override
public void onDestroyedByExplosion(World world, BlockPos pos, Explosion explosion) {
super.onDestroyedByExplosion(world, pos, explosion);
@@ -19,5 +30,12 @@ public class ObeliskCoreBlock extends Block {
if (!(world instanceof ServerWorld serverWorld)) return;
TwoTowersUtil.grantNearbyAdvancement(serverWorld, pos, 100);
BlockEntity be = world.getBlockEntity(pos);
if (be instanceof ObeliskCoreBlockEntity core) {
core.setHasPlaneMob(false); // reset in case a plane was active
core.markDirty();
}
}
}

View File

@@ -0,0 +1,31 @@
package dev.tggamesyt.szar;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.BlockPos;
public class ObeliskCoreBlockEntity extends BlockEntity {
private boolean hasPlaneMob = false;
public ObeliskCoreBlockEntity(BlockPos pos, BlockState state) {
super(Szar.OBELISK_CORE_ENTITY, pos, state);
}
// NBT serialization
@Override
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
nbt.putBoolean("hasPlaneMob", hasPlaneMob);
}
@Override
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);
hasPlaneMob = nbt.getBoolean("hasPlaneMob");
}
public boolean hasPlaneMob() { return hasPlaneMob; }
public void setHasPlaneMob(boolean value) { hasPlaneMob = value; }
}

View File

@@ -17,12 +17,15 @@ import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
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.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper;
import net.fabricmc.fabric.api.object.builder.v1.world.poi.PointOfInterestHelper;
import net.minecraft.advancement.Advancement;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.*;
import net.minecraft.entity.damage.DamageType;
import net.minecraft.entity.data.DataTracker;
@@ -649,6 +652,26 @@ public class Szar implements ModInitializer {
return count;
})
);
dispatcher.register(
LiteralArgumentBuilder.<ServerCommandSource>literal("getnearestobeliskcore")
.requires(context -> context.hasPermissionLevel(2))
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld world = source.getWorld();
assert source.getEntity() != null;
ObeliskCoreBlockEntity nearest = findNearestObelisk(world, source.getEntity().getBlockPos(), 100);
if (nearest != null) {
boolean hasPlane = nearest.hasPlaneMob();
source.sendMessage(Text.literal(
"HasPlane: " + hasPlane
));
return 1;
}
return 0;
})
);
});
Registry.register(
Registries.ITEM,
@@ -656,6 +679,36 @@ public class Szar implements ModInitializer {
new BlockItem(OBELISK_CORE, new Item.Settings())
);
}
public static ObeliskCoreBlockEntity findNearestObelisk(ServerWorld world, BlockPos center, int radius) {
ObeliskCoreBlockEntity closest = null;
double closestDistance = Double.MAX_VALUE;
BlockPos.Mutable mutable = new BlockPos.Mutable();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
mutable.set(center.getX() + x,
center.getY() + y,
center.getZ() + z);
BlockEntity be = world.getBlockEntity(mutable);
if (be instanceof ObeliskCoreBlockEntity obelisk) {
double distance = center.getSquaredDistance(mutable);
if (distance < closestDistance) {
closestDistance = distance;
closest = obelisk;
}
}
}
}
}
return closest;
}
public static final Item CNDM = Registry.register(
Registries.ITEM,
new Identifier(MOD_ID, "cndm"),
@@ -691,8 +744,15 @@ public class Szar implements ModInitializer {
.copy(Blocks.DIRT) // soft block
.strength(0.5f, 1.0f) // very easy to break, low blast resistance
)
);
public static final BlockEntityType<ObeliskCoreBlockEntity> OBELISK_CORE_ENTITY = Registry.register(
Registries.BLOCK_ENTITY_TYPE,
new Identifier(MOD_ID, "obelisk_core"),
FabricBlockEntityTypeBuilder.create(
ObeliskCoreBlockEntity::new,
OBELISK_CORE // block(s) this BE is linked to
).build(null)
);