Compare commits
No commits in common. "36e2b9a8866b98c095ef76e00bf461df76c39a6c" and "629ab9fba383dd40859967275362f5397155da87" have entirely different histories.
36e2b9a886
...
629ab9fba3
@ -3,7 +3,7 @@ plugins {
|
|||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "0.0.3"
|
version = "0.0.2"
|
||||||
group = "net.Chipperfluff"
|
group = "net.Chipperfluff"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|||||||
10
chipper
@ -67,16 +67,6 @@ inject_options() {
|
|||||||
if [[ -f "./config/servers.dat" ]]; then
|
if [[ -f "./config/servers.dat" ]]; then
|
||||||
cp "./config/servers.dat" "$dir/servers.dat"
|
cp "./config/servers.dat" "$dir/servers.dat"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# server.properties
|
|
||||||
if [[ -f "./config/server.properties" ]]; then
|
|
||||||
cp "./config/server.properties" "$dir/server.properties"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# server icon
|
|
||||||
if [[ -f "./config/server-icon.png" ]]; then
|
|
||||||
cp "./config/server-icon.png" "$dir/server-icon.png"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_server_files() {
|
ensure_server_files() {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 417 B |
@ -1,58 +0,0 @@
|
|||||||
#Minecraft server properties
|
|
||||||
#Mon Dec 22 17:38:51 CET 2025
|
|
||||||
allow-flight=false
|
|
||||||
allow-nether=true
|
|
||||||
broadcast-console-to-ops=true
|
|
||||||
broadcast-rcon-to-ops=true
|
|
||||||
difficulty=easy
|
|
||||||
enable-command-block=true
|
|
||||||
enable-jmx-monitoring=false
|
|
||||||
enable-query=false
|
|
||||||
enable-rcon=false
|
|
||||||
enable-status=true
|
|
||||||
enforce-secure-profile=true
|
|
||||||
enforce-whitelist=false
|
|
||||||
entity-broadcast-range-percentage=100
|
|
||||||
force-gamemode=false
|
|
||||||
function-permission-level=2
|
|
||||||
gamemode=survival
|
|
||||||
generate-structures=true
|
|
||||||
generator-settings={}
|
|
||||||
hardcore=false
|
|
||||||
hide-online-players=false
|
|
||||||
initial-disabled-packs=
|
|
||||||
initial-enabled-packs=vanilla,fabric
|
|
||||||
level-name=world
|
|
||||||
level-seed=
|
|
||||||
level-type=minecraft\:normal
|
|
||||||
max-chained-neighbor-updates=1000000
|
|
||||||
max-players=20
|
|
||||||
max-tick-time=60000
|
|
||||||
max-world-size=29999984
|
|
||||||
motd=§6chipi §edev §7server\n§8local test instance
|
|
||||||
network-compression-threshold=256
|
|
||||||
online-mode=false
|
|
||||||
op-permission-level=4
|
|
||||||
player-idle-timeout=0
|
|
||||||
prevent-proxy-connections=false
|
|
||||||
pvp=true
|
|
||||||
query.port=25565
|
|
||||||
rate-limit=0
|
|
||||||
rcon.password=
|
|
||||||
rcon.port=25575
|
|
||||||
require-resource-pack=false
|
|
||||||
resource-pack=
|
|
||||||
resource-pack-prompt=
|
|
||||||
resource-pack-sha1=
|
|
||||||
server-ip=
|
|
||||||
server-port=25565
|
|
||||||
simulation-distance=10
|
|
||||||
spawn-animals=true
|
|
||||||
spawn-monsters=true
|
|
||||||
spawn-npcs=true
|
|
||||||
spawn-protection=16
|
|
||||||
sync-chunk-writes=true
|
|
||||||
text-filtering-config=
|
|
||||||
use-native-transport=true
|
|
||||||
view-distance=10
|
|
||||||
white-list=false
|
|
||||||
@ -9,7 +9,6 @@ import net.Chipperfluff.chipi.effect.ModEffects;
|
|||||||
import net.Chipperfluff.chipi.entity.ModEntities;
|
import net.Chipperfluff.chipi.entity.ModEntities;
|
||||||
import net.Chipperfluff.chipi.entity.SpawnLogic;
|
import net.Chipperfluff.chipi.entity.SpawnLogic;
|
||||||
import net.Chipperfluff.chipi.entity.MepEntity;
|
import net.Chipperfluff.chipi.entity.MepEntity;
|
||||||
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
|
|
||||||
import net.Chipperfluff.chipi.item.ModItemGroups;
|
import net.Chipperfluff.chipi.item.ModItemGroups;
|
||||||
import net.Chipperfluff.chipi.item.ModItems;
|
import net.Chipperfluff.chipi.item.ModItems;
|
||||||
import net.Chipperfluff.chipi.server.ChipiServerEvents;
|
import net.Chipperfluff.chipi.server.ChipiServerEvents;
|
||||||
@ -17,7 +16,6 @@ import net.Chipperfluff.chipi.sound.ModSounds;
|
|||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
|
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
|
||||||
import net.Chipperfluff.chipi.util.TickScheduler;
|
import net.Chipperfluff.chipi.util.TickScheduler;
|
||||||
import net.Chipperfluff.chipi.item.music.ModMusicDiscs;
|
|
||||||
|
|
||||||
public class ChipiMod implements ModInitializer {
|
public class ChipiMod implements ModInitializer {
|
||||||
|
|
||||||
@ -37,15 +35,12 @@ public class ChipiMod implements ModInitializer {
|
|||||||
SpawnLogic.register();
|
SpawnLogic.register();
|
||||||
|
|
||||||
ModSounds.register();
|
ModSounds.register();
|
||||||
ModMusicDiscs.registerAll();
|
|
||||||
|
|
||||||
TickScheduler.init();
|
TickScheduler.init();
|
||||||
CommandHandler.register();
|
CommandHandler.register();
|
||||||
|
|
||||||
FabricDefaultAttributeRegistry.register(ModEntities.MEP, MepEntity.createMepAttributes());
|
FabricDefaultAttributeRegistry.register(ModEntities.MEP, MepEntity.createMepAttributes());
|
||||||
|
|
||||||
FabricDefaultAttributeRegistry.register(ModEntities.PLAYER_JR, PlayerJrEntity.createAttributes());
|
|
||||||
|
|
||||||
ChipiServerEvents.register();
|
ChipiServerEvents.register();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,5 @@ public class ModEntityRenderers {
|
|||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
EntityRendererRegistry.register(ModEntities.MEP, MepRenderer::new);
|
EntityRendererRegistry.register(ModEntities.MEP, MepRenderer::new);
|
||||||
EntityRendererRegistry.register(ModEntities.PLAYER_JR, PlayerJrRenderer::new);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.client.entity;
|
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
|
|
||||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
|
||||||
import net.minecraft.client.render.VertexConsumerProvider;
|
|
||||||
import net.minecraft.client.render.entity.BipedEntityRenderer;
|
|
||||||
import net.minecraft.client.render.entity.EntityRendererFactory;
|
|
||||||
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
|
||||||
import net.minecraft.client.render.entity.model.EntityModelLayers;
|
|
||||||
import net.minecraft.client.util.DefaultSkinHelper;
|
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
|
|
||||||
public class PlayerJrRenderer
|
|
||||||
extends BipedEntityRenderer<PlayerJrEntity, BipedEntityModel<PlayerJrEntity>> {
|
|
||||||
|
|
||||||
public PlayerJrRenderer(EntityRendererFactory.Context ctx) {
|
|
||||||
super(ctx, new BipedEntityModel<>(ctx.getPart(EntityModelLayers.PLAYER)), 0.3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(
|
|
||||||
PlayerJrEntity entity,
|
|
||||||
float yaw,
|
|
||||||
float tickDelta,
|
|
||||||
MatrixStack matrices,
|
|
||||||
VertexConsumerProvider vertices,
|
|
||||||
int light
|
|
||||||
) {
|
|
||||||
matrices.scale(0.5f, 0.5f, 0.5f);
|
|
||||||
super.render(entity, yaw, tickDelta, matrices, vertices, light);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Identifier getTexture(PlayerJrEntity entity) {
|
|
||||||
String dadName = entity.getDadName();
|
|
||||||
|
|
||||||
if (dadName == null || dadName.isBlank()) {
|
|
||||||
return DefaultSkinHelper.getTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* THIS is how vanilla + mods do it:
|
|
||||||
* - stable Identifier
|
|
||||||
* - async skin download
|
|
||||||
* - cached by SkinProvider
|
|
||||||
*/
|
|
||||||
Identifier skin = AbstractClientPlayerEntity.getSkinId(dadName);
|
|
||||||
AbstractClientPlayerEntity.loadSkin(skin, dadName);
|
|
||||||
|
|
||||||
return skin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,6 @@ public final class CommandHandler {
|
|||||||
(dispatcher, registryAccess, environment) -> {
|
(dispatcher, registryAccess, environment) -> {
|
||||||
ChpCommand.register(dispatcher);
|
ChpCommand.register(dispatcher);
|
||||||
CspCommand.register(dispatcher);
|
CspCommand.register(dispatcher);
|
||||||
SpawnJrCommand.register(dispatcher);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.command;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.entity.ModEntities;
|
|
||||||
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
|
|
||||||
|
|
||||||
import net.minecraft.command.argument.EntityArgumentType;
|
|
||||||
import net.minecraft.server.command.CommandManager;
|
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
|
|
||||||
public final class SpawnJrCommand {
|
|
||||||
|
|
||||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
|
||||||
dispatcher.register(
|
|
||||||
CommandManager.literal("spawnjr")
|
|
||||||
.requires(src -> src.hasPermissionLevel(2))
|
|
||||||
.then(
|
|
||||||
CommandManager.argument("dad", EntityArgumentType.player())
|
|
||||||
.executes(SpawnJrCommand::execute)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int execute(CommandContext<ServerCommandSource> ctx) {
|
|
||||||
ServerCommandSource source = ctx.getSource();
|
|
||||||
|
|
||||||
System.out.println("[spawnjr] ================================");
|
|
||||||
System.out.println("[spawnjr] Command invoked");
|
|
||||||
|
|
||||||
try {
|
|
||||||
ServerWorld world = source.getWorld();
|
|
||||||
ServerPlayerEntity dad = EntityArgumentType.getPlayer(ctx, "dad");
|
|
||||||
ServerPlayerEntity spawner = source.getPlayerOrThrow();
|
|
||||||
|
|
||||||
BlockPos pos = spawner.getBlockPos();
|
|
||||||
|
|
||||||
System.out.println("[spawnjr] Dad = " + dad.getName().getString());
|
|
||||||
System.out.println("[spawnjr] Dad UUID = " + dad.getUuidAsString());
|
|
||||||
System.out.println("[spawnjr] World = " + world.getRegistryKey().getValue());
|
|
||||||
System.out.println("[spawnjr] Spawn pos = " + pos);
|
|
||||||
|
|
||||||
PlayerJrEntity jr = new PlayerJrEntity(ModEntities.PLAYER_JR, world);
|
|
||||||
System.out.println("[spawnjr] Entity constructed");
|
|
||||||
|
|
||||||
jr.refreshPositionAndAngles(
|
|
||||||
pos.getX() + 0.5,
|
|
||||||
pos.getY(),
|
|
||||||
pos.getZ() + 0.5,
|
|
||||||
world.random.nextFloat() * 360f,
|
|
||||||
0f
|
|
||||||
);
|
|
||||||
|
|
||||||
// single source of truth
|
|
||||||
jr.setDad(dad);
|
|
||||||
System.out.println("[spawnjr] Dad relationship set");
|
|
||||||
|
|
||||||
boolean spawned = world.spawnEntity(jr);
|
|
||||||
System.out.println("[spawnjr] spawnEntity() returned = " + spawned);
|
|
||||||
|
|
||||||
if (!spawned) {
|
|
||||||
source.sendError(Text.literal("[spawnjr] Spawn failed (entity rejected by world)"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("[spawnjr] SUCCESS");
|
|
||||||
System.out.println("[spawnjr] ================================");
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("[spawnjr] ================================");
|
|
||||||
System.out.println("[spawnjr] CRASH");
|
|
||||||
System.out.println("[spawnjr] hey message Chipperfluff");
|
|
||||||
System.out.println("[spawnjr] Exception: " + e.getClass().getName());
|
|
||||||
System.out.println("[spawnjr] Message: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
source.sendError(Text.literal(
|
|
||||||
"[spawnjr] Internal error. Check logs. (hey message Chipperfluff)"
|
|
||||||
));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,19 +9,8 @@ import net.minecraft.util.Identifier;
|
|||||||
public class ModEffects {
|
public class ModEffects {
|
||||||
|
|
||||||
public static final StatusEffect CHIPI_BLESSING = new ChipiBlessingEffect();
|
public static final StatusEffect CHIPI_BLESSING = new ChipiBlessingEffect();
|
||||||
public static final StatusEffect PREGNANT = new PregnantEffect();
|
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
Registry.register(
|
Registry.register(Registries.STATUS_EFFECT, new Identifier(ChipiMod.MOD_ID, "chipi_blessing"), CHIPI_BLESSING);
|
||||||
Registries.STATUS_EFFECT,
|
|
||||||
new Identifier(ChipiMod.MOD_ID, "chipi_blessing"),
|
|
||||||
CHIPI_BLESSING
|
|
||||||
);
|
|
||||||
|
|
||||||
Registry.register(
|
|
||||||
Registries.STATUS_EFFECT,
|
|
||||||
new Identifier(ChipiMod.MOD_ID, "pregnant"),
|
|
||||||
PREGNANT
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,127 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.effect;
|
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.entity.ModEntities;
|
|
||||||
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
|
|
||||||
import net.minecraft.entity.LivingEntity;
|
|
||||||
import net.minecraft.entity.attribute.EntityAttributeInstance;
|
|
||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
|
||||||
import net.minecraft.entity.attribute.EntityAttributeModifier;
|
|
||||||
import net.minecraft.entity.effect.StatusEffect;
|
|
||||||
import net.minecraft.entity.effect.StatusEffectCategory;
|
|
||||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PregnantEffect extends StatusEffect {
|
|
||||||
|
|
||||||
public static final int TOTAL_DURATION = 20 * 60 * 10; // 10 min
|
|
||||||
private static final double MAX_SLOW = 0.90;
|
|
||||||
|
|
||||||
private static final UUID SPEED_UUID =
|
|
||||||
UUID.fromString("7c8b6b8f-4b6c-4f6f-9b7b-01c8e9f8a111");
|
|
||||||
|
|
||||||
public PregnantEffect() {
|
|
||||||
super(StatusEffectCategory.HARMFUL, 0xFFB6C1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- helpers used by mixin ---------- */
|
|
||||||
|
|
||||||
/** 0.0 → normal jump, 1.0 → no jump */
|
|
||||||
public static double getJumpClamp(ServerPlayerEntity player) {
|
|
||||||
StatusEffectInstance inst = player.getStatusEffect(ModEffects.PREGNANT);
|
|
||||||
if (inst == null) return 0.0;
|
|
||||||
|
|
||||||
double progress =
|
|
||||||
1.0 - (inst.getDuration() / (double) TOTAL_DURATION);
|
|
||||||
|
|
||||||
return Math.min(Math.max(progress, 0.0), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- ticking ---------- */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canApplyUpdateEffect(int duration, int amplifier) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyUpdateEffect(LivingEntity entity, int amplifier) {
|
|
||||||
if (!(entity instanceof ServerPlayerEntity player)) return;
|
|
||||||
|
|
||||||
StatusEffectInstance inst = player.getStatusEffect(this);
|
|
||||||
if (inst == null) return;
|
|
||||||
|
|
||||||
int remaining = inst.getDuration();
|
|
||||||
|
|
||||||
double progress =
|
|
||||||
1.0 - (remaining / (double) TOTAL_DURATION);
|
|
||||||
|
|
||||||
progress = Math.min(Math.max(progress, 0.0), 1.0);
|
|
||||||
|
|
||||||
applySpeedModifier(player, progress * MAX_SLOW);
|
|
||||||
|
|
||||||
if (remaining % 400 == 0 && remaining > 20) {
|
|
||||||
player.sendMessage(
|
|
||||||
Text.literal("Your knees file a formal complaint…"),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining == 1) {
|
|
||||||
finishBirth(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applySpeedModifier(ServerPlayerEntity player, double slowAmount) {
|
|
||||||
EntityAttributeInstance attr =
|
|
||||||
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED);
|
|
||||||
|
|
||||||
if (attr == null) return;
|
|
||||||
|
|
||||||
EntityAttributeModifier old = attr.getModifier(SPEED_UUID);
|
|
||||||
if (old != null) attr.removeModifier(old);
|
|
||||||
|
|
||||||
if (slowAmount > 0.01) {
|
|
||||||
attr.addTemporaryModifier(new EntityAttributeModifier(
|
|
||||||
SPEED_UUID,
|
|
||||||
"Pregnancy slowdown",
|
|
||||||
-slowAmount,
|
|
||||||
EntityAttributeModifier.Operation.MULTIPLY_TOTAL
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finishBirth(ServerPlayerEntity dad) {
|
|
||||||
// cleanup
|
|
||||||
EntityAttributeInstance attr =
|
|
||||||
dad.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED);
|
|
||||||
if (attr != null) {
|
|
||||||
EntityAttributeModifier old = attr.getModifier(SPEED_UUID);
|
|
||||||
if (old != null) attr.removeModifier(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
dad.removeStatusEffect(this);
|
|
||||||
|
|
||||||
PlayerJrEntity jr = ModEntities.PLAYER_JR.create(dad.getWorld());
|
|
||||||
if (jr == null) return;
|
|
||||||
|
|
||||||
jr.refreshPositionAndAngles(
|
|
||||||
dad.getX(), dad.getY(), dad.getZ(),
|
|
||||||
dad.getYaw(), dad.getPitch()
|
|
||||||
);
|
|
||||||
|
|
||||||
jr.setDad(dad);
|
|
||||||
dad.getWorld().spawnEntity(jr);
|
|
||||||
|
|
||||||
dad.sendMessage(
|
|
||||||
Text.literal(
|
|
||||||
"You're a dad now. The name’s " +
|
|
||||||
jr.getName().getString() +
|
|
||||||
". Warning: it might cheat."
|
|
||||||
),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -23,16 +23,6 @@ public final class ModEntities {
|
|||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
public static final EntityType<PlayerJrEntity> PLAYER_JR =
|
|
||||||
Registry.register(
|
|
||||||
Registries.ENTITY_TYPE,
|
|
||||||
new Identifier("chipi", "player_jr"),
|
|
||||||
FabricEntityTypeBuilder
|
|
||||||
.create(SpawnGroup.CREATURE, PlayerJrEntity::new)
|
|
||||||
.dimensions(EntityDimensions.fixed(0.45f, 0.9f)) // small
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
private ModEntities() {
|
private ModEntities() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,389 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.entity;
|
|
||||||
|
|
||||||
import net.minecraft.entity.EntityType;
|
|
||||||
import net.minecraft.entity.ai.goal.Goal;
|
|
||||||
import net.minecraft.entity.ai.goal.LookAroundGoal;
|
|
||||||
import net.minecraft.entity.ai.goal.LookAtEntityGoal;
|
|
||||||
import net.minecraft.entity.ai.goal.SwimGoal;
|
|
||||||
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
|
||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
|
||||||
import net.minecraft.entity.mob.MobEntity;
|
|
||||||
import net.minecraft.entity.mob.PathAwareEntity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PlayerJrEntity extends PathAwareEntity {
|
|
||||||
|
|
||||||
/* ================= RULES (CHEATING FRIEND) ================= */
|
|
||||||
|
|
||||||
private static final double FORCE_FLY_DISTANCE = 50.0; // fly if farther than this (horizontal)
|
|
||||||
private static final int STUCK_TICKS_TO_FLY = 40; // 2 seconds of no horizontal progress
|
|
||||||
private static final double STUCK_MIN_CLOSING = 0.10; // horizontal progress threshold
|
|
||||||
private static final double STUCK_MIN_DISTANCE = 6.0; // don't care if already close
|
|
||||||
private static final double DAD_MOVING_EPS_SQ = 0.02; // if dad is moving, don't call it stuck
|
|
||||||
|
|
||||||
private static final int LAND_RADIUS = 6; // search dry landing within this radius
|
|
||||||
private static final double LAND_HORIZONTAL_DISTANCE = 2.0; // must be this close (horiz) to land
|
|
||||||
private static final double HOVER_HEIGHT = 1.25; // hover above landing block
|
|
||||||
private static final int MIN_FLY_TICKS = 20; // must fly at least this long
|
|
||||||
|
|
||||||
private static final double FLY_SPEED = 0.55;
|
|
||||||
private static final double FLY_ACCEL = 0.25;
|
|
||||||
private static final int MAX_VERTICAL_GAP = 6; // if dad is this high above -> fly
|
|
||||||
|
|
||||||
/* ================= STATE ================= */
|
|
||||||
|
|
||||||
private UUID dadUuid;
|
|
||||||
private String dadName = "";
|
|
||||||
|
|
||||||
private enum Mode { GROUND, FLYING }
|
|
||||||
private Mode mode = Mode.GROUND;
|
|
||||||
|
|
||||||
private int flyTicks = 0;
|
|
||||||
|
|
||||||
// Stuck tracking (ground)
|
|
||||||
private int stuckTicks = 0;
|
|
||||||
private double lastDadHorizDist = -1;
|
|
||||||
|
|
||||||
// Landing / hover state
|
|
||||||
private BlockPos landingSpot = null;
|
|
||||||
private BlockPos forcedLandingSpot = null;
|
|
||||||
|
|
||||||
/* ================= CONSTRUCTOR ================= */
|
|
||||||
|
|
||||||
public PlayerJrEntity(EntityType<? extends PathAwareEntity> type, World world) {
|
|
||||||
super(type, world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DefaultAttributeContainer.Builder createAttributes() {
|
|
||||||
return MobEntity.createMobAttributes()
|
|
||||||
.add(EntityAttributes.GENERIC_MAX_HEALTH, 10.0)
|
|
||||||
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.25)
|
|
||||||
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 64.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= DAD ================= */
|
|
||||||
|
|
||||||
public void setDad(ServerPlayerEntity dad) {
|
|
||||||
dadUuid = dad.getUuid();
|
|
||||||
dadName = dad.getGameProfile().getName();
|
|
||||||
setCustomName(Text.literal(dadName + " jr."));
|
|
||||||
setCustomNameVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ServerPlayerEntity getDad() {
|
|
||||||
if (dadUuid == null || getWorld().isClient) return null;
|
|
||||||
return getWorld().getServer().getPlayerManager().getPlayer(dadUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDadName() { return dadName; }
|
|
||||||
|
|
||||||
/* ================= AI ================= */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initGoals() {
|
|
||||||
goalSelector.add(1, new SwimGoal(this));
|
|
||||||
goalSelector.add(2, new FollowDadGoal(this));
|
|
||||||
goalSelector.add(3, new LookAtEntityGoal(this, PlayerEntity.class, 6f));
|
|
||||||
goalSelector.add(4, new LookAroundGoal(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= TICK ================= */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
super.tick();
|
|
||||||
if (getWorld().isClient) return;
|
|
||||||
|
|
||||||
// Never fall damage ever (you asked this specifically)
|
|
||||||
this.fallDistance = 0.0f;
|
|
||||||
|
|
||||||
ServerPlayerEntity dad = getDad();
|
|
||||||
if (dad == null) return;
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case GROUND -> doGroundBrain(dad);
|
|
||||||
case FLYING -> doFlyingBrain(dad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= CORE BRAIN: GROUND ================= */
|
|
||||||
|
|
||||||
private void doGroundBrain(ServerPlayerEntity dad) {
|
|
||||||
// Water cheat: if land is nearby, prefer walking out; if no path, fly briefly to land.
|
|
||||||
if (this.isTouchingWater()) {
|
|
||||||
BlockPos escape = findLandingSpotNear(this.getBlockPos(), LAND_RADIUS);
|
|
||||||
if (escape != null) {
|
|
||||||
boolean canWalk = getNavigation().startMovingTo(
|
|
||||||
escape.getX() + 0.5,
|
|
||||||
escape.getY(),
|
|
||||||
escape.getZ() + 0.5,
|
|
||||||
1.1
|
|
||||||
);
|
|
||||||
if (canWalk) {
|
|
||||||
resetStuckTracking(dad);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
forcedLandingSpot = escape;
|
|
||||||
startFlying(dad);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update stuck tracking (only on ground mode)
|
|
||||||
updateStuckTracking(dad);
|
|
||||||
if (stuckTicks >= STUCK_TICKS_TO_FLY) {
|
|
||||||
startFlying(dad);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Too far behind (impatient cheat)
|
|
||||||
if (horizontalDistanceTo(dad) > FORCE_FLY_DISTANCE) {
|
|
||||||
startFlying(dad);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dad is way above us (can't reach)
|
|
||||||
double dy = dad.getY() - getY();
|
|
||||||
if (dy > MAX_VERTICAL_GAP && horizontalDistanceTo(dad) > 2.0) {
|
|
||||||
startFlying(dad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateStuckTracking(ServerPlayerEntity dad) {
|
|
||||||
boolean tryingToWalk = !getNavigation().isIdle();
|
|
||||||
double dadDist = horizontalDistanceTo(dad);
|
|
||||||
double dadSpeedSq = dad.getVelocity().horizontalLengthSquared();
|
|
||||||
|
|
||||||
if (!tryingToWalk || dadDist <= STUCK_MIN_DISTANCE || dadSpeedSq > DAD_MOVING_EPS_SQ) {
|
|
||||||
stuckTicks = 0;
|
|
||||||
lastDadHorizDist = dadDist;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastDadHorizDist < 0) {
|
|
||||||
lastDadHorizDist = dadDist;
|
|
||||||
stuckTicks = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double closing = lastDadHorizDist - dadDist; // positive means getting closer
|
|
||||||
if (closing < STUCK_MIN_CLOSING) stuckTicks++;
|
|
||||||
else stuckTicks = 0;
|
|
||||||
|
|
||||||
lastDadHorizDist = dadDist;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetStuckTracking(ServerPlayerEntity dad) {
|
|
||||||
stuckTicks = 0;
|
|
||||||
lastDadHorizDist = horizontalDistanceTo(dad);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= CORE BRAIN: FLYING ================= */
|
|
||||||
|
|
||||||
private void startFlying(ServerPlayerEntity dad) {
|
|
||||||
mode = Mode.FLYING;
|
|
||||||
flyTicks = 0;
|
|
||||||
|
|
||||||
landingSpot = null;
|
|
||||||
|
|
||||||
setNoGravity(true);
|
|
||||||
getNavigation().stop();
|
|
||||||
setVelocity(Vec3d.ZERO);
|
|
||||||
velocityDirty = true;
|
|
||||||
|
|
||||||
dad.sendMessage(Text.literal(getName().getString() + " switched to Game Mode Creative"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doFlyingBrain(ServerPlayerEntity dad) {
|
|
||||||
flyTicks++;
|
|
||||||
|
|
||||||
double horiz = horizontalDistanceTo(dad);
|
|
||||||
|
|
||||||
if (flyTicks >= MIN_FLY_TICKS) {
|
|
||||||
BlockPos candidate = forcedLandingSpot;
|
|
||||||
if (candidate == null) {
|
|
||||||
candidate = findLandingSpotNear(dad.getBlockPos(), LAND_RADIUS);
|
|
||||||
}
|
|
||||||
if (candidate != null && !wouldImmediatelyRefly(dad, candidate)) {
|
|
||||||
landingSpot = candidate;
|
|
||||||
if (horizontalDistanceTo(landingSpot) <= LAND_HORIZONTAL_DISTANCE) {
|
|
||||||
stopFlying(dad);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos targetSpot = forcedLandingSpot != null ? forcedLandingSpot : landingSpot;
|
|
||||||
Vec3d target = targetSpot != null
|
|
||||||
? Vec3d.ofCenter(targetSpot).add(0, HOVER_HEIGHT, 0)
|
|
||||||
: dad.getPos().add(0, 2.2, 0);
|
|
||||||
|
|
||||||
flyTowardSmooth(target);
|
|
||||||
lookAtEntity(dad, 30f, 30f);
|
|
||||||
}
|
|
||||||
private void stopFlying(ServerPlayerEntity dad) {
|
|
||||||
setNoGravity(false);
|
|
||||||
mode = Mode.GROUND;
|
|
||||||
|
|
||||||
landingSpot = null;
|
|
||||||
forcedLandingSpot = null;
|
|
||||||
|
|
||||||
dad.sendMessage(Text.literal(getName().getString() + " switched to Game Mode Survival"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= MOVEMENT HELPERS ================= */
|
|
||||||
|
|
||||||
private void flyTowardSmooth(Vec3d target) {
|
|
||||||
Vec3d delta = target.subtract(getPos());
|
|
||||||
double distSq = delta.lengthSquared();
|
|
||||||
if (distSq < 0.0006) {
|
|
||||||
setVelocity(getVelocity().multiply(0.6));
|
|
||||||
velocityDirty = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3d desired = delta.normalize().multiply(FLY_SPEED);
|
|
||||||
|
|
||||||
// stronger steer when far (prevents drifting)
|
|
||||||
double accel = (distSq > 64.0) ? Math.min(0.55, FLY_ACCEL * 2.0) : FLY_ACCEL;
|
|
||||||
|
|
||||||
Vec3d blended = getVelocity().multiply(1.0 - accel).add(desired.multiply(accel));
|
|
||||||
setVelocity(blended);
|
|
||||||
velocityDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double horizontalDistanceTo(ServerPlayerEntity dad) {
|
|
||||||
double dx = dad.getX() - getX();
|
|
||||||
double dz = dad.getZ() - getZ();
|
|
||||||
return Math.sqrt(dx * dx + dz * dz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double horizontalDistanceTo(BlockPos pos) {
|
|
||||||
double dx = pos.getX() + 0.5 - getX();
|
|
||||||
double dz = pos.getZ() + 0.5 - getZ();
|
|
||||||
return Math.sqrt(dx * dx + dz * dz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= LAND SEARCH (DRY + SAFE) ================= */
|
|
||||||
|
|
||||||
private BlockPos findLandingSpotNear(BlockPos center, int radius) {
|
|
||||||
BlockPos best = null;
|
|
||||||
double bestScore = Double.MAX_VALUE;
|
|
||||||
|
|
||||||
int minY = center.getY() - 3;
|
|
||||||
int maxY = center.getY() + 3;
|
|
||||||
|
|
||||||
for (int dx = -radius; dx <= radius; dx++) {
|
|
||||||
for (int dz = -radius; dz <= radius; dz++) {
|
|
||||||
if ((dx * dx + dz * dz) > radius * radius) continue;
|
|
||||||
|
|
||||||
for (int y = minY; y <= maxY; y++) {
|
|
||||||
BlockPos p = new BlockPos(center.getX() + dx, y, center.getZ() + dz);
|
|
||||||
if (!canStandAt(p)) continue;
|
|
||||||
|
|
||||||
// prefer closer to dad AND closer to us a bit
|
|
||||||
double toCenter = p.getSquaredDistance(center);
|
|
||||||
double toMe = this.getPos().squaredDistanceTo(Vec3d.ofCenter(p));
|
|
||||||
double score = toCenter * 1.0 + toMe * 0.10;
|
|
||||||
|
|
||||||
if (score < bestScore) {
|
|
||||||
bestScore = score;
|
|
||||||
best = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canStandAt(BlockPos pos) {
|
|
||||||
World w = getWorld();
|
|
||||||
|
|
||||||
// feet + head air
|
|
||||||
if (!w.getBlockState(pos).isAir()) return false;
|
|
||||||
if (!w.getBlockState(pos.up()).isAir()) return false;
|
|
||||||
|
|
||||||
// solid below
|
|
||||||
BlockPos below = pos.down();
|
|
||||||
if (!w.getBlockState(below).isSolidBlock(w, below)) return false;
|
|
||||||
|
|
||||||
// not in fluid, not standing on fluid
|
|
||||||
if (!w.getFluidState(pos).isEmpty()) return false;
|
|
||||||
if (!w.getFluidState(below).isEmpty()) return false;
|
|
||||||
|
|
||||||
// also avoid lava blocks around feet area (simple “don’t be stupid”)
|
|
||||||
// (fluid check already catches lava fluid; this catches blocks like magma? optional)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wouldImmediatelyRefly(ServerPlayerEntity dad, BlockPos landPos) {
|
|
||||||
if (horizontalDistanceTo(dad) > FORCE_FLY_DISTANCE) return true;
|
|
||||||
if (dad.getY() - landPos.getY() > MAX_VERTICAL_GAP) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= FALL DAMAGE ================= */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleFallDamage(float fallDistance, float damageMultiplier, DamageSource source) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= SAVE / LOAD ================= */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeCustomDataToNbt(NbtCompound nbt) {
|
|
||||||
if (dadUuid != null) nbt.putUuid("Dad", dadUuid);
|
|
||||||
nbt.putString("DadName", dadName);
|
|
||||||
nbt.putString("Mode", mode.name());
|
|
||||||
nbt.putInt("FlyTicks", flyTicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readCustomDataFromNbt(NbtCompound nbt) {
|
|
||||||
if (nbt.containsUuid("Dad")) dadUuid = nbt.getUuid("Dad");
|
|
||||||
if (nbt.contains("DadName")) dadName = nbt.getString("DadName");
|
|
||||||
if (nbt.contains("Mode")) {
|
|
||||||
mode = Mode.valueOf(nbt.getString("Mode"));
|
|
||||||
setNoGravity(mode != Mode.GROUND);
|
|
||||||
}
|
|
||||||
if (nbt.contains("FlyTicks")) flyTicks = nbt.getInt("FlyTicks");
|
|
||||||
|
|
||||||
// volatile reset
|
|
||||||
landingSpot = null;
|
|
||||||
forcedLandingSpot = null;
|
|
||||||
lastDadHorizDist = -1;
|
|
||||||
stuckTicks = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= FOLLOW GOAL ================= */
|
|
||||||
|
|
||||||
static class FollowDadGoal extends Goal {
|
|
||||||
private final PlayerJrEntity jr;
|
|
||||||
FollowDadGoal(PlayerJrEntity jr) { this.jr = jr; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canStart() {
|
|
||||||
return jr.mode == Mode.GROUND && jr.getDad() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
ServerPlayerEntity dad = jr.getDad();
|
|
||||||
if (dad == null) return;
|
|
||||||
|
|
||||||
if (jr.squaredDistanceTo(dad) > 4) {
|
|
||||||
jr.getNavigation().startMovingTo(dad, 1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,6 @@ package net.Chipperfluff.chipi.item;
|
|||||||
|
|
||||||
import net.Chipperfluff.chipi.ChipiMod;
|
import net.Chipperfluff.chipi.ChipiMod;
|
||||||
import net.Chipperfluff.chipi.block.ModBlocks;
|
import net.Chipperfluff.chipi.block.ModBlocks;
|
||||||
import net.Chipperfluff.chipi.item.music.ModMusicDiscs;
|
|
||||||
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
@ -51,10 +50,6 @@ public class ModItemGroups {
|
|||||||
entries.add(ModItems.CHIPPER_SHOVEL);
|
entries.add(ModItems.CHIPPER_SHOVEL);
|
||||||
entries.add(ModItems.CHIPPER_HOE);
|
entries.add(ModItems.CHIPPER_HOE);
|
||||||
|
|
||||||
// Music discs
|
|
||||||
for (var disc : ModMusicDiscs.getAll().values()) {
|
|
||||||
entries.add(disc);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
package net.Chipperfluff.chipi.item;
|
package net.Chipperfluff.chipi.item;
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.effect.ModEffects;
|
|
||||||
import net.Chipperfluff.chipi.sound.ModSounds;
|
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.ItemUsage;
|
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
import net.minecraft.sound.SoundCategory;
|
|
||||||
import net.minecraft.util.Hand;
|
|
||||||
import net.minecraft.util.UseAction;
|
import net.minecraft.util.UseAction;
|
||||||
import net.minecraft.util.TypedActionResult;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||||
|
import net.Chipperfluff.chipi.effect.ModEffects;
|
||||||
|
import net.Chipperfluff.chipi.sound.ModSounds;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
|
||||||
public class PlayerMilkItem extends Item {
|
public class PlayerMilkItem extends Item {
|
||||||
|
|
||||||
@ -22,30 +18,6 @@ public class PlayerMilkItem extends Item {
|
|||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= DENY DRINKING ================= */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
|
||||||
ItemStack stack = user.getStackInHand(hand);
|
|
||||||
|
|
||||||
// Already pregnant → deny drinking
|
|
||||||
if (user.hasStatusEffect(ModEffects.PREGNANT)) {
|
|
||||||
if (!world.isClient) {
|
|
||||||
user.sendMessage(
|
|
||||||
Text.literal("You already feel something growing..."),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return TypedActionResult.fail(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start drinking normally
|
|
||||||
user.setCurrentHand(hand);
|
|
||||||
return TypedActionResult.consume(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= DRINK ANIMATION ================= */
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UseAction getUseAction(ItemStack stack) {
|
public UseAction getUseAction(ItemStack stack) {
|
||||||
return UseAction.DRINK;
|
return UseAction.DRINK;
|
||||||
@ -56,23 +28,10 @@ public class PlayerMilkItem extends Item {
|
|||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= FINISH DRINK ================= */
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
|
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
|
||||||
|
|
||||||
if (!world.isClient && user instanceof PlayerEntity player) {
|
if (!world.isClient && user instanceof PlayerEntity player) {
|
||||||
|
|
||||||
// Apply pregnancy effect
|
|
||||||
player.addStatusEffect(new StatusEffectInstance(
|
|
||||||
ModEffects.PREGNANT,
|
|
||||||
20 * 60 * 10, // 10 minutes
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
));
|
|
||||||
|
|
||||||
// Play sound
|
|
||||||
world.playSound(
|
world.playSound(
|
||||||
null,
|
null,
|
||||||
player.getBlockPos(),
|
player.getBlockPos(),
|
||||||
@ -83,10 +42,8 @@ public class PlayerMilkItem extends Item {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CORRECT vanilla behavior:
|
if (user instanceof PlayerEntity player && !player.getAbilities().creativeMode) {
|
||||||
// consumes milk and gives bucket
|
return new ItemStack(Items.BUCKET);
|
||||||
if (user instanceof PlayerEntity player) {
|
|
||||||
return ItemUsage.exchangeStack(stack, player, new ItemStack(Items.BUCKET));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack;
|
return stack;
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.item.music;
|
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.ChipiMod;
|
|
||||||
import net.minecraft.item.Item;
|
|
||||||
import net.minecraft.item.MusicDiscItem;
|
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.registry.Registry;
|
|
||||||
import net.minecraft.sound.SoundEvent;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ModMusicDiscs {
|
|
||||||
|
|
||||||
private static final Map<String, Item> DISCS = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
private static final MusicDiscDef[] DEFINITIONS = {
|
|
||||||
new MusicDiscDef("aa9", 7, 5, 0),
|
|
||||||
new MusicDiscDef("abc", 7, 2, 11),
|
|
||||||
new MusicDiscDef("fs", 7, 2, 0),
|
|
||||||
new MusicDiscDef("phone", 7, 2, 11),
|
|
||||||
new MusicDiscDef("wha", 7, 1, 48),
|
|
||||||
new MusicDiscDef("who", 7, 2, 27),
|
|
||||||
new MusicDiscDef("working_as_intented", 7, 3, 45)
|
|
||||||
};
|
|
||||||
|
|
||||||
private static Item registerDisc(MusicDiscDef def) {
|
|
||||||
String id = "chipi_record_" + def.name();
|
|
||||||
Identifier identifier = new Identifier(ChipiMod.MOD_ID, id);
|
|
||||||
|
|
||||||
SoundEvent sound = Registry.register(
|
|
||||||
Registries.SOUND_EVENT,
|
|
||||||
identifier,
|
|
||||||
SoundEvent.of(identifier)
|
|
||||||
);
|
|
||||||
|
|
||||||
Item item = Registry.register(
|
|
||||||
Registries.ITEM,
|
|
||||||
identifier,
|
|
||||||
new MusicDiscItem(
|
|
||||||
def.comparatorOutput(),
|
|
||||||
sound,
|
|
||||||
new Item.Settings().maxCount(1),
|
|
||||||
toTicks(def.minutes(), def.seconds())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
DISCS.put(def.name(), item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, Item> getAll() {
|
|
||||||
return DISCS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerAll() {
|
|
||||||
for (MusicDiscDef def : DEFINITIONS) {
|
|
||||||
registerDisc(def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int toTicks(int minutes, int seconds) {
|
|
||||||
int totalSeconds = Math.max(0, minutes) * 60 + Math.max(0, seconds);
|
|
||||||
return totalSeconds * 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package net.Chipperfluff.chipi.item.music;
|
|
||||||
|
|
||||||
public record MusicDiscDef(
|
|
||||||
String name,
|
|
||||||
int comparatorOutput,
|
|
||||||
int minutes,
|
|
||||||
int seconds
|
|
||||||
) {}
|
|
||||||
@ -1,15 +1,10 @@
|
|||||||
package net.Chipperfluff.chipi.mixin;
|
package net.Chipperfluff.chipi.mixin;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import net.Chipperfluff.chipi.effect.ModEffects;
|
|
||||||
import net.Chipperfluff.chipi.effect.PregnantEffect;
|
|
||||||
import net.Chipperfluff.chipi.util.ChipiTrackedData;
|
import net.Chipperfluff.chipi.util.ChipiTrackedData;
|
||||||
import net.minecraft.entity.data.DataTracker;
|
import net.minecraft.entity.data.DataTracker;
|
||||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
@ -19,63 +14,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
@Mixin(PlayerEntity.class)
|
@Mixin(PlayerEntity.class)
|
||||||
public abstract class PlayerEntityMixin {
|
public abstract class PlayerEntityMixin {
|
||||||
|
|
||||||
/* ============================================================
|
|
||||||
INIT: tracked data
|
|
||||||
============================================================ */
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
private void chipi$initTrackedData(
|
private void chipi$initTrackedData(World world, BlockPos pos, float yaw, GameProfile profile, CallbackInfo ci) {
|
||||||
World world,
|
|
||||||
BlockPos pos,
|
|
||||||
float yaw,
|
|
||||||
GameProfile profile,
|
|
||||||
CallbackInfo ci
|
|
||||||
) {
|
|
||||||
PlayerEntity self = (PlayerEntity)(Object)this;
|
PlayerEntity self = (PlayerEntity)(Object)this;
|
||||||
DataTracker tracker = self.getDataTracker();
|
DataTracker tracker = self.getDataTracker();
|
||||||
|
|
||||||
tracker.startTracking(ChipiTrackedData.CHIPI_ENERGY, 1.0f);
|
tracker.startTracking(ChipiTrackedData.CHIPI_ENERGY, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
|
||||||
JUMP CLAMP: pregnancy logic
|
|
||||||
============================================================ */
|
|
||||||
|
|
||||||
@Inject(
|
|
||||||
method = "jump",
|
|
||||||
at = @At("HEAD"),
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
private void chipi$pregnancyJumpClamp(CallbackInfo ci) {
|
|
||||||
if (!((Object)this instanceof ServerPlayerEntity player)) return;
|
|
||||||
|
|
||||||
StatusEffectInstance inst =
|
|
||||||
player.getStatusEffect(ModEffects.PREGNANT);
|
|
||||||
|
|
||||||
if (inst == null) return; // normal jump
|
|
||||||
|
|
||||||
int total = PregnantEffect.TOTAL_DURATION;
|
|
||||||
int remaining = inst.getDuration();
|
|
||||||
|
|
||||||
// progress 0.0 → 1.0
|
|
||||||
double progress =
|
|
||||||
1.0 - ((double)remaining / total);
|
|
||||||
progress = Math.min(Math.max(progress, 0.0), 1.0);
|
|
||||||
|
|
||||||
// vanilla jump velocity ≈ 0.42
|
|
||||||
double maxJump = 0.42 * (1.0 - progress);
|
|
||||||
|
|
||||||
// End stage: NO jumping at all
|
|
||||||
if (maxJump < 0.05) {
|
|
||||||
ci.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3d vel = player.getVelocity();
|
|
||||||
player.setVelocity(vel.x, maxJump, vel.z);
|
|
||||||
player.velocityDirty = true;
|
|
||||||
|
|
||||||
// stop vanilla jump
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,30 +2,12 @@
|
|||||||
"death.attack.void_block": "§8%1$s stepped beyond safety — the Outside answered.",
|
"death.attack.void_block": "§8%1$s stepped beyond safety — the Outside answered.",
|
||||||
"death.attack.chipi.void_block_fire": "§c%1$s tried to save themselves.§r §8It got worse.",
|
"death.attack.chipi.void_block_fire": "§c%1$s tried to save themselves.§r §8It got worse.",
|
||||||
|
|
||||||
"effect.chipi.chipi_blessing": "§bChipi's Blessing",
|
|
||||||
"effect.chipi.pregnant": "§dyou are Pregnant",
|
|
||||||
|
|
||||||
"block.chipi.void_block": "Void Block",
|
"block.chipi.void_block": "Void Block",
|
||||||
"block.chipi.chipper_frame": "Chipper Frame",
|
"block.chipi.chipper_frame": "Chipper Frame",
|
||||||
"block.chipi.chipper_portal": "Chipper Portal",
|
"block.chipi.chipper_portal": "Chipper Portal",
|
||||||
"block.chipi.chipper_ore": "Chipper Ore",
|
"block.chipi.chipper_ore": "Chipper Ore",
|
||||||
"block.chipi.chipper_alloy_block": "Chipper Alloy Block",
|
"block.chipi.chipper_alloy_block": "Chipper Alloy Block",
|
||||||
|
|
||||||
"item.chipi.chipi_record_aa9": "Chipi Record – Aa9",
|
|
||||||
"item.chipi.chipi_record_aa9.desc": "Chipi Record – Aa9",
|
|
||||||
"item.chipi.chipi_record_abc": "Chipi Record – Abc",
|
|
||||||
"item.chipi.chipi_record_abc.desc": "Chipi Record – Abc",
|
|
||||||
"item.chipi.chipi_record_fs": "Chipi Record – Fs",
|
|
||||||
"item.chipi.chipi_record_fs.desc": "Chipi Record – Fs",
|
|
||||||
"item.chipi.chipi_record_phone": "Chipi Record – Phone",
|
|
||||||
"item.chipi.chipi_record_phone.desc": "Chipi Record – Phone",
|
|
||||||
"item.chipi.chipi_record_wha": "Chipi Record – Wha",
|
|
||||||
"item.chipi.chipi_record_wha.desc": "Chipi Record – Wha",
|
|
||||||
"item.chipi.chipi_record_who": "Chipi Record – Who",
|
|
||||||
"item.chipi.chipi_record_who.desc": "Chipi Record – Who",
|
|
||||||
"item.chipi.chipi_record_working_as_intented": "Chipi Record – Working As Intented",
|
|
||||||
"item.chipi.chipi_record_working_as_intented.desc": "Chipi Record – Working As Intented",
|
|
||||||
|
|
||||||
"itemGroup.chipi.chipi": "Chipi",
|
"itemGroup.chipi.chipi": "Chipi",
|
||||||
|
|
||||||
"item.chipi.void_block": "Void Block",
|
"item.chipi.void_block": "Void Block",
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_aa9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_abc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_fs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_phone"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_wha"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_who"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "minecraft:item/generated",
|
|
||||||
"textures": {
|
|
||||||
"layer0": "chipi:item/chipi_record_working_as_intented"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,26 +8,5 @@
|
|||||||
"sounds": [
|
"sounds": [
|
||||||
"chipi:player_milk"
|
"chipi:player_milk"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"chipi_record_aa9": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_aa9", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_abc": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_abc", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_fs": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_fs", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_phone": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_phone", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_wha": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_wha", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_who": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_who", "stream": true }]
|
|
||||||
},
|
|
||||||
"chipi_record_working_as_intented": {
|
|
||||||
"sounds": [{ "name": "chipi:chipi_record_working_as_intented", "stream": true }]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 312 B |
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "chipi:raw_chipper_ore" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_aa9" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "minecraft:orange_wool" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_abc" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "minecraft:sweet_berries" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_fs" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "minecraft:iron_ingot" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_phone" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "chipi:chipper_alloy" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_wha" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "minecraft:blue_wool" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_who" }
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "minecraft:crafting_shaped",
|
|
||||||
"pattern": [
|
|
||||||
"xxx",
|
|
||||||
"xyx",
|
|
||||||
"xxx"
|
|
||||||
],
|
|
||||||
"key": {
|
|
||||||
"x": { "item": "minecraft:glass" },
|
|
||||||
"y": { "item": "minecraft:emerald" }
|
|
||||||
},
|
|
||||||
"result": { "item": "chipi:chipi_record_working_as_intented" }
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"replace": false,
|
|
||||||
"values": [
|
|
||||||
"chipi:chipi_record_aa9",
|
|
||||||
"chipi:chipi_record_abc",
|
|
||||||
"chipi:chipi_record_fs",
|
|
||||||
"chipi:chipi_record_phone",
|
|
||||||
"chipi:chipi_record_wha",
|
|
||||||
"chipi:chipi_record_who",
|
|
||||||
"chipi:chipi_record_working_as_intented"
|
|
||||||
]
|
|
||||||
}
|
|
||||||