mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-07 22:16:44 +01:00
Add broken alicorn amulet, and further tweaks the sombra
This commit is contained in:
parent
30c91050db
commit
0320976d47
20 changed files with 284 additions and 71 deletions
|
@ -83,7 +83,13 @@ public class SombraEntityModel extends EntityModel<SombraEntity> {
|
|||
|
||||
@Override
|
||||
public void animateModel(SombraEntity entity, float limbAngle, float limbDistance, float tickDelta) {
|
||||
float jawsOpenAmount = entity.getBiteAmount(tickDelta); //(1 - Math.max(0, MathHelper.sin(ageInTicks * 0.1F)));
|
||||
float jawsOpenAmount = entity.getBiteAmount(tickDelta);
|
||||
float scale = entity.getScaleFactor(tickDelta) * 1.7F;
|
||||
|
||||
part.pivotY = scale * -20;
|
||||
part.xScale = scale;
|
||||
part.yScale = scale;
|
||||
part.zScale = scale;
|
||||
|
||||
lowerJaw.resetTransform();
|
||||
lowerJaw.pivotY -= jawsOpenAmount * 3;
|
||||
|
@ -96,14 +102,8 @@ public class SombraEntityModel extends EntityModel<SombraEntity> {
|
|||
|
||||
@Override
|
||||
public void setAngles(SombraEntity entity, float limbAngle, float limbDistance, float animationProgress, float netHeadYaw, float headPitch) {
|
||||
float scale = 1.6F;
|
||||
|
||||
part.xScale = scale;
|
||||
part.yScale = scale;
|
||||
part.zScale = scale;
|
||||
|
||||
part.yaw = -MathHelper.HALF_PI;
|
||||
part.pivotY = MathHelper.sin(animationProgress * 0.05F) - 12;
|
||||
part.pivotY += MathHelper.sin(animationProgress * 0.05F);
|
||||
part.pivotZ = MathHelper.cos(animationProgress * 0.045F);
|
||||
|
||||
head.pitch = headPitch * MathHelper.RADIANS_PER_DEGREE;
|
||||
|
|
|
@ -9,6 +9,7 @@ import net.minecraft.entity.LivingEntity;
|
|||
public interface AmuletSelectors {
|
||||
Predicate<LivingEntity> ALICORN_AMULET = UItems.ALICORN_AMULET::isApplicable;
|
||||
Predicate<LivingEntity> PEGASUS_AMULET = UItems.PEGASUS_AMULET::isApplicable;
|
||||
Predicate<LivingEntity> UNICORN_AMULET = UItems.UNICORN_AMULET::isApplicable;
|
||||
|
||||
Predicate<LivingEntity> ALICORN_AMULET_AFTER_1_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(ItemTracker.DAYS)));
|
||||
Predicate<LivingEntity> ALICORN_AMULET_AFTER_2_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(2 * ItemTracker.DAYS)));
|
||||
|
|
|
@ -120,6 +120,11 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
|||
}
|
||||
|
||||
public void initAi(GoalSelector goals, GoalSelector targets) {
|
||||
|
||||
if (entity instanceof SombraEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.goals = Optional.of(goals);
|
||||
this.targets = Optional.of(targets);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public class CrystalShardsEntity extends Entity {
|
|||
}
|
||||
|
||||
public static Set<Direction> getOccupiedFaces(World world, BlockPos pos) {
|
||||
return world.getEntitiesByClass(CrystalShardsEntity.class, new Box(pos).expand(1), EntityPredicates.VALID_ENTITY)
|
||||
return world.getEntitiesByClass(CrystalShardsEntity.class, new Box(pos).expand(4), EntityPredicates.VALID_ENTITY)
|
||||
.stream()
|
||||
.map(e -> e.getAttachmentFace())
|
||||
.distinct()
|
||||
|
|
|
@ -10,19 +10,17 @@ import com.minelittlepony.unicopia.USounds;
|
|||
import com.minelittlepony.unicopia.entity.ai.ArenaAttackGoal;
|
||||
import com.minelittlepony.unicopia.item.AmuletItem;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.particle.ParticleHandle;
|
||||
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
|
||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import com.minelittlepony.unicopia.util.VecHelper;
|
||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.ExperienceOrbEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.MovementType;
|
||||
import net.minecraft.entity.ai.goal.ActiveTargetGoal;
|
||||
import net.minecraft.entity.ai.goal.FlyGoal;
|
||||
import net.minecraft.entity.ai.goal.LongDoorInteractGoal;
|
||||
|
@ -40,6 +38,7 @@ import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
|||
import net.minecraft.entity.attribute.EntityAttributes;
|
||||
import net.minecraft.entity.boss.BossBar;
|
||||
import net.minecraft.entity.boss.ServerBossBar;
|
||||
import net.minecraft.entity.boss.BossBar.Style;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
|
@ -67,7 +66,9 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameRules;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldEvents;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
public class SombraEntity extends HostileEntity implements ArenaCombatant, ParticleSource<SombraEntity> {
|
||||
|
@ -76,18 +77,17 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
static final Predicate<Entity> EFFECT_TARGET_PREDICATE = EntityPredicates.VALID_LIVING_ENTITY.and(EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR);
|
||||
|
||||
private static final TrackedData<Optional<BlockPos>> HOME_POS = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS);
|
||||
private static final TrackedData<Float> TARGET_SIZE = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
||||
|
||||
private final ServerBossBar bossBar = (ServerBossBar)new ServerBossBar(getDisplayName(), BossBar.Color.PURPLE, BossBar.Style.PROGRESS)
|
||||
.setDarkenSky(true)
|
||||
.setThickenFog(true);
|
||||
|
||||
private final ParticleHandle shroud = new ParticleHandle();
|
||||
|
||||
private final ServerBossBar bossBar;
|
||||
final EntityReference<StormCloudEntity> stormCloud = new EntityReference<>();
|
||||
|
||||
private int prevBiteTime;
|
||||
private int biteTime;
|
||||
|
||||
private float prevSize;
|
||||
private float currentSize;
|
||||
|
||||
public static void startEncounter(World world, BlockPos pos) {
|
||||
StormCloudEntity cloud = UEntities.STORM_CLOUD.create(world);
|
||||
cloud.setPosition(pos.up(10).toCenterPos());
|
||||
|
@ -97,8 +97,18 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
}
|
||||
|
||||
public SombraEntity(EntityType<SombraEntity> type, World world) {
|
||||
this(type, world, null);
|
||||
}
|
||||
|
||||
public SombraEntity(EntityType<SombraEntity> type, World world, @Nullable ServerBossBar bossBar) {
|
||||
super(type, world);
|
||||
bossBar.setStyle(BossBar.Style.NOTCHED_10);
|
||||
this.bossBar = bossBar == null ? createBossBar(getDisplayName()) : bossBar;
|
||||
this.bossBar.setName(getDisplayName());
|
||||
this.bossBar.setStyle(Style.NOTCHED_10);
|
||||
}
|
||||
|
||||
public static ServerBossBar createBossBar(Text name) {
|
||||
return new SombraBossBar(name);
|
||||
}
|
||||
|
||||
public static DefaultAttributeContainer.Builder createMobAttributes() {
|
||||
|
@ -137,6 +147,7 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
protected void initDataTracker() {
|
||||
super.initDataTracker();
|
||||
dataTracker.startTracking(HOME_POS, Optional.empty());
|
||||
dataTracker.startTracking(TARGET_SIZE, 1F);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -182,7 +193,21 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
|
||||
public float getBiteAmount(float tickDelta) {
|
||||
float progress = (MathHelper.lerp(tickDelta, prevBiteTime, biteTime) / (float)MAX_BITE_TIME);
|
||||
return 1 - Math.abs(MathHelper.sin(progress * MathHelper.PI));
|
||||
return 1 - Math.abs(MathHelper.sin(progress * MathHelper.PI * 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScaleFactor() {
|
||||
return Math.max(1, dataTracker.get(TARGET_SIZE));
|
||||
}
|
||||
|
||||
public float getScaleFactor(float tickDelta) {
|
||||
return MathHelper.lerp(tickDelta, prevSize, currentSize);
|
||||
}
|
||||
|
||||
public void setScaleFactor(float targetSize) {
|
||||
dataTracker.set(TARGET_SIZE, targetSize);
|
||||
calculateDimensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -191,8 +216,8 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
Optional<BlockPos> homePos = getHomePos();
|
||||
|
||||
if (homePos.isEmpty() && !isRemoved()) {
|
||||
remove(RemovalReason.DISCARDED);
|
||||
return;
|
||||
setHomePos(getBlockPos());
|
||||
homePos = getHomePos();
|
||||
}
|
||||
|
||||
if (getBlockPos().getSquaredDistance(homePos.get()) > MathHelper.square(getAreaRadius())) {
|
||||
|
@ -205,19 +230,34 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
biteTime--;
|
||||
}
|
||||
|
||||
float targetSize = getScaleFactor();
|
||||
boolean sizeChanging = prevSize != currentSize;
|
||||
prevSize = currentSize;
|
||||
tickGrowth(targetSize, sizeChanging);
|
||||
|
||||
if (!hasPositionTarget() && homePos.isPresent()) {
|
||||
setPositionTarget(homePos.get(), (int)getAreaRadius());
|
||||
}
|
||||
|
||||
setVelocity(Vec3d.ZERO);
|
||||
super.tick();
|
||||
|
||||
if (getTarget() == null && getVelocity().y < 0) {
|
||||
setVelocity(getVelocity().multiply(1, 0.4, 1));
|
||||
}
|
||||
|
||||
addVelocity(0, 0.0242F, 0);
|
||||
|
||||
if (isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubmergedInWater()) {
|
||||
jump();
|
||||
}
|
||||
|
||||
if (random.nextInt(1200) == 0) {
|
||||
playSound(USounds.ENTITY_SOMBRA_LAUGH, 1, 1);
|
||||
getWorld().sendEntityStatus(this, BITE);
|
||||
laugh();
|
||||
}
|
||||
|
||||
if (random.nextInt(340) == 0) {
|
||||
|
@ -235,8 +275,10 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
}
|
||||
}
|
||||
|
||||
for (BlockPos p : BlockPos.iterateRandomly(random, 3, getBlockPos(), 20)) {
|
||||
CrystalShardsEntity.infestBlock((ServerWorld)getWorld(), p);
|
||||
if (random.nextInt(200) == 0) {
|
||||
for (BlockPos p : BlockPos.iterateRandomly(random, 3, getBlockPos(), 20)) {
|
||||
CrystalShardsEntity.infestBlock((ServerWorld)getWorld(), p);
|
||||
}
|
||||
}
|
||||
|
||||
if (getTarget() == null && getNavigation().isIdle()) {
|
||||
|
@ -247,6 +289,29 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
getHomePos().ifPresent(this::generateArenaEffects);
|
||||
}
|
||||
|
||||
protected void tickGrowth(float targetSize, boolean changing) {
|
||||
if (currentSize != targetSize) {
|
||||
float sizeDifference = (dataTracker.get(TARGET_SIZE) - currentSize);
|
||||
currentSize = Math.abs(sizeDifference) < 0.01F ? targetSize : currentSize + (sizeDifference * 0.2F);
|
||||
calculateDimensions();
|
||||
}
|
||||
|
||||
if (currentSize == targetSize && changing) {
|
||||
laugh();
|
||||
}
|
||||
|
||||
if (currentSize == targetSize && isDead()) {
|
||||
setScaleFactor(currentSize + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void laugh() {
|
||||
if (!getWorld().isClient) {
|
||||
playSound(USounds.ENTITY_SOMBRA_LAUGH, 1, 1);
|
||||
getWorld().sendEntityStatus(this, BITE);
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyAreaEffects(Entity target) {
|
||||
if (this.age % 150 == 0) {
|
||||
target.playSound(
|
||||
|
@ -297,16 +362,6 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
ParticleUtils.spawnParticle(getWorld(), type, pos, Vec3d.ZERO);
|
||||
ParticleUtils.spawnParticle(getWorld(), type, pos, pos.subtract(getPos()).add(0, 0.1, 0).multiply(-0.013));
|
||||
});
|
||||
|
||||
shroud.update(getUuid(), this, spawner -> {
|
||||
var radius = getAreaRadius();
|
||||
var center = home.toCenterPos();
|
||||
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0xFF000000, 1, radius - 0.2F), center, Vec3d.ZERO);
|
||||
}).ifPresent(attachment -> {
|
||||
attachment.setAttribute(Attachment.ATTR_BOUND, 1);
|
||||
attachment.setAttribute(Attachment.ATTR_COLOR, 0xFF000000);
|
||||
attachment.setAttribute(Attachment.ATTR_OPACITY, 2.5F);
|
||||
});
|
||||
} else {
|
||||
for (Entity target : VecHelper.findInRange(this, getWorld(), home.toCenterPos(), getAreaRadius() - 0.2F, EFFECT_TARGET_PREDICATE)) {
|
||||
applyAreaEffects(target);
|
||||
|
@ -352,8 +407,15 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
}
|
||||
boolean damaged = super.damage(source, amount);
|
||||
|
||||
if (source.getAttacker() instanceof PlayerEntity player) {
|
||||
teleportRandomly(16);
|
||||
if (!getWorld().isClient) {
|
||||
if (source.getAttacker() instanceof PlayerEntity player) {
|
||||
teleportRandomly(16);
|
||||
}
|
||||
|
||||
float targetSize = getScaleFactor();
|
||||
if (targetSize > 1) {
|
||||
setScaleFactor(Math.max(1, targetSize * 0.9F));
|
||||
}
|
||||
}
|
||||
|
||||
return damaged;
|
||||
|
@ -376,6 +438,39 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
super.onDeath(damageSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updatePostDeath() {
|
||||
if (++deathTime >= 180 && deathTime <= 200) {
|
||||
getWorld().addParticle(ParticleTypes.EXPLOSION_EMITTER,
|
||||
random.nextTriangular(getX(), 4F),
|
||||
random.nextTriangular(getY() + 2, 2F),
|
||||
random.nextTriangular(getZ(), 4F), 0, 0, 0);
|
||||
}
|
||||
|
||||
move(MovementType.SELF, new Vec3d(0, 0.3F, 0));
|
||||
|
||||
if (getWorld() instanceof ServerWorld sw) {
|
||||
final boolean dropLoot = this.getWorld().getGameRules().getBoolean(GameRules.DO_MOB_LOOT);
|
||||
final int experience = 500;
|
||||
|
||||
if (deathTime > 150 && deathTime % 5 == 0 && dropLoot) {
|
||||
ExperienceOrbEntity.spawn(sw, getPos(), MathHelper.floor(experience * 0.08f));
|
||||
}
|
||||
|
||||
if (deathTime == 1 && !isSilent()) {
|
||||
getWorld().syncGlobalEvent(WorldEvents.ENDER_DRAGON_DIES, this.getBlockPos(), 0);
|
||||
}
|
||||
|
||||
if (deathTime == 200) {
|
||||
if (dropLoot) {
|
||||
ExperienceOrbEntity.spawn(sw, getPos(), MathHelper.floor(experience * 0.2f));
|
||||
}
|
||||
remove(RemovalReason.KILLED);
|
||||
emitGameEvent(GameEvent.ENTITY_DIE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fall(double y, boolean onGroundIn, BlockState state, BlockPos pos) {
|
||||
}
|
||||
|
@ -385,9 +480,16 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
return super.canTarget(target) && getHomePos().filter(home -> target.getPos().isInRange(home.toCenterPos(), getAreaRadius())).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInWalkTargetRange(BlockPos pos) {
|
||||
BlockPos center = getHomePos().orElse(getBlockPos());
|
||||
double distance = pos.getSquaredDistanceFromCenter(center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5);
|
||||
return distance < MathHelper.square(getAreaRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAttack(Entity target) {
|
||||
getWorld().sendEntityStatus(this, BITE);
|
||||
laugh();
|
||||
return super.tryAttack(target);
|
||||
}
|
||||
|
||||
|
@ -458,6 +560,7 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
nbt.put("homePos", pos);
|
||||
});
|
||||
nbt.put("cloud", stormCloud.toNBT());
|
||||
nbt.putFloat("size", getScaleFactor());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -469,6 +572,24 @@ public class SombraEntity extends HostileEntity implements ArenaCombatant, Parti
|
|||
if (hasCustomName()) {
|
||||
bossBar.setName(getDisplayName());
|
||||
}
|
||||
setScaleFactor(nbt.getFloat("size"));
|
||||
stormCloud.fromNBT(nbt.getCompound("cloud"));
|
||||
}
|
||||
|
||||
private static class SombraBossBar extends ServerBossBar {
|
||||
public SombraBossBar(Text displayName) {
|
||||
super(displayName, BossBar.Color.PURPLE, BossBar.Style.PROGRESS);
|
||||
setDarkenSky(true);
|
||||
setThickenFog(true);
|
||||
setDragonMusic(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPercent(float percent) {
|
||||
super.setPercent(percent);
|
||||
if (percent > 0.6F && getColor() == Color.PURPLE) {
|
||||
setColor(Color.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package com.minelittlepony.unicopia.entity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import com.minelittlepony.unicopia.server.world.WeatherConditions;
|
||||
|
@ -9,13 +13,17 @@ import com.minelittlepony.unicopia.server.world.WeatherConditions;
|
|||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LightningEntity;
|
||||
import net.minecraft.entity.boss.ServerBossBar;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
@ -29,6 +37,10 @@ public class StormCloudEntity extends Entity {
|
|||
private static final TrackedData<Float> TARGET_SIZE = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
||||
private static final TrackedData<Boolean> DISSIPATING = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
||||
|
||||
static final float MAX_SIZE = 30;
|
||||
static final int CLEAR_TARGET_ALTITUDE = 90;
|
||||
static final int STORMY_TARGET_ALTITUDE = 20;
|
||||
|
||||
private float prevSize;
|
||||
private float currentSize;
|
||||
|
||||
|
@ -37,6 +49,10 @@ public class StormCloudEntity extends Entity {
|
|||
private int phase;
|
||||
private int nextPhase;
|
||||
|
||||
@Nullable
|
||||
private ServerBossBar bossBar;
|
||||
private final Set<ServerPlayerEntity> trackingPlayers = new HashSet<>();
|
||||
|
||||
public StormCloudEntity(EntityType<StormCloudEntity> type, World world) {
|
||||
super(type, world);
|
||||
setSize(1 + random.nextInt(4));
|
||||
|
@ -88,7 +104,7 @@ public class StormCloudEntity extends Entity {
|
|||
}
|
||||
|
||||
public float getSize(float tickDelta) {
|
||||
return MathHelper.clamp(MathHelper.lerp(tickDelta, prevSize, currentSize), 1, 30);
|
||||
return MathHelper.clamp(MathHelper.lerp(tickDelta, prevSize, currentSize), 1, MAX_SIZE);
|
||||
}
|
||||
|
||||
public void setSize(float size) {
|
||||
|
@ -103,12 +119,11 @@ public class StormCloudEntity extends Entity {
|
|||
public void tick() {
|
||||
setFireTicks(1);
|
||||
|
||||
|
||||
prevSize = currentSize;
|
||||
float targetSize = dataTracker.get(TARGET_SIZE);
|
||||
if (currentSize != targetSize) {
|
||||
float sizeDifference = (dataTracker.get(TARGET_SIZE) - currentSize);
|
||||
currentSize = Math.abs(sizeDifference) < 0.01F ? targetSize : currentSize + (sizeDifference * 0.2F);
|
||||
currentSize = Math.abs(sizeDifference) < 0.01F ? targetSize : currentSize + (sizeDifference * 0.02F);
|
||||
}
|
||||
|
||||
if (isStormy()) {
|
||||
|
@ -120,7 +135,7 @@ public class StormCloudEntity extends Entity {
|
|||
|
||||
if (isLogicalSideForUpdatingMovement()) {
|
||||
float groundY = getWorld().getTopY(Type.MOTION_BLOCKING_NO_LEAVES, (int)getX(), (int)getZ());
|
||||
float cloudY = (float)getY() - (isStormy() ? 20 : 90);
|
||||
float cloudY = (float)getY() - (isStormy() ? STORMY_TARGET_ALTITUDE : CLEAR_TARGET_ALTITUDE);
|
||||
|
||||
addVelocity(0, 0.03F * (groundY - cloudY), 0);
|
||||
|
||||
|
@ -169,6 +184,11 @@ public class StormCloudEntity extends Entity {
|
|||
}
|
||||
}
|
||||
|
||||
if (cursed) {
|
||||
float percent = Math.min(1, (size + 1) / MAX_SIZE);
|
||||
getBossBar().setPercent(percent);
|
||||
}
|
||||
|
||||
if (currentSize == targetSize) {
|
||||
if (isDissipating()) {
|
||||
if (size < 2) {
|
||||
|
@ -181,10 +201,11 @@ public class StormCloudEntity extends Entity {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (size < 30) {
|
||||
if (size < MAX_SIZE) {
|
||||
if (cursed) {
|
||||
setSize(size + 1);
|
||||
setStormTicks(1);
|
||||
setStormTicks(-1);
|
||||
|
||||
spawnLightningStrike(getBlockPos(), false, false);
|
||||
playSound(USounds.AMBIENT_WIND_GUST, 1, 1);
|
||||
}
|
||||
|
@ -192,7 +213,7 @@ public class StormCloudEntity extends Entity {
|
|||
}
|
||||
}
|
||||
|
||||
if (size >= 30) {
|
||||
if (size >= MAX_SIZE) {
|
||||
if (cursed) {
|
||||
setStormTicks(-1);
|
||||
|
||||
|
@ -208,9 +229,10 @@ public class StormCloudEntity extends Entity {
|
|||
pickRandomPoints(13, pos -> spawnLightningStrike(pos, true, false));
|
||||
|
||||
cursed = false;
|
||||
SombraEntity sombra = UEntities.SOMBRA.create(getWorld());
|
||||
SombraEntity sombra = new SombraEntity(UEntities.SOMBRA, getWorld(), getBossBar());
|
||||
sombra.setPosition(getPos());
|
||||
sombra.setHomePos(getWorld().getTopPosition(Type.MOTION_BLOCKING_NO_LEAVES, getBlockPos()));
|
||||
sombra.setScaleFactor(8);
|
||||
sombra.stormCloud.set(this);
|
||||
getWorld().spawnEntity(sombra);
|
||||
|
||||
|
@ -299,6 +321,15 @@ public class StormCloudEntity extends Entity {
|
|||
super.handleStatus(status);
|
||||
}
|
||||
|
||||
private ServerBossBar getBossBar() {
|
||||
if (this.bossBar == null) {
|
||||
this.bossBar = SombraEntity.createBossBar(Text.translatable("unicopia.entity.sombra").formatted(Formatting.OBFUSCATED));
|
||||
trackingPlayers.removeIf(Entity::isRemoved);
|
||||
trackingPlayers.forEach(this.bossBar::addPlayer);
|
||||
}
|
||||
return this.bossBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCustomDataToNbt(NbtCompound nbt) {
|
||||
nbt.putInt("stormTicks", getStormTicks());
|
||||
|
@ -314,4 +345,25 @@ public class StormCloudEntity extends Entity {
|
|||
setSize(currentSize = nbt.getFloat("size"));
|
||||
cursed = nbt.getBoolean("cursed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartedTrackingBy(ServerPlayerEntity player) {
|
||||
super.onStartedTrackingBy(player);
|
||||
trackingPlayers.removeIf(Entity::isRemoved);
|
||||
trackingPlayers.add(player);
|
||||
if (bossBar != null) {
|
||||
bossBar.addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStoppedTrackingBy(ServerPlayerEntity player) {
|
||||
super.onStoppedTrackingBy(player);
|
||||
trackingPlayers.removeIf(Entity::isRemoved);
|
||||
trackingPlayers.remove(player);
|
||||
if (bossBar != null) {
|
||||
bossBar.removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,15 +46,15 @@ public interface UEntities {
|
|||
EntityType<SpellbookEntity> SPELLBOOK = register("spellbook", FabricEntityTypeBuilder.create(SpawnGroup.MISC, SpellbookEntity::new)
|
||||
.trackRangeBlocks(200)
|
||||
.dimensions(EntityDimensions.fixed(0.9F, 0.5F)));
|
||||
EntityType<SombraEntity> SOMBRA = register("sombra", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, SombraEntity::new)
|
||||
.trackRangeBlocks(200)
|
||||
.dimensions(EntityDimensions.fixed(1F, 2F)));
|
||||
EntityType<SombraEntity> SOMBRA = register("sombra", FabricEntityTypeBuilder.<SombraEntity>create(SpawnGroup.MONSTER, SombraEntity::new)
|
||||
.trackRangeBlocks(20)
|
||||
.dimensions(EntityDimensions.changing(2F, 4F)));
|
||||
EntityType<CrystalShardsEntity> CRYSTAL_SHARDS = register("crystal_shards", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CrystalShardsEntity::new)
|
||||
.trackRangeBlocks(100)
|
||||
.dimensions(EntityDimensions.fixed(1F, 1F)));
|
||||
.dimensions(EntityDimensions.changing(1F, 1F)));
|
||||
EntityType<StormCloudEntity> STORM_CLOUD = register("storm_cloud", FabricEntityTypeBuilder.create(SpawnGroup.MISC, StormCloudEntity::new)
|
||||
.trackRangeBlocks(200)
|
||||
.dimensions(EntityDimensions.fixed(20F, 20F)));
|
||||
.dimensions(EntityDimensions.changing(20F, 20F)));
|
||||
EntityType<AirBalloonEntity> AIR_BALLOON = register("air_balloon", FabricEntityTypeBuilder.create(SpawnGroup.MISC, AirBalloonEntity::new)
|
||||
.trackRangeBlocks(1000)
|
||||
.dimensions(EntityDimensions.changing(2.5F, 0.1F)));
|
||||
|
|
|
@ -223,7 +223,10 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
|
|||
*/
|
||||
public Race.Composite getCompositeRace() {
|
||||
Race observed = getObservedSpecies();
|
||||
return new Race.Composite(observed, AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN : observed);
|
||||
return new Race.Composite(observed,
|
||||
AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN
|
||||
: AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN
|
||||
: observed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -142,6 +142,11 @@ public interface UItems {
|
|||
.maxCount(1)
|
||||
.maxDamage(1000)
|
||||
.rarity(Rarity.RARE)), ItemGroups.TOOLS);
|
||||
Item BROKEN_ALICORN_AMULET = register("broken_alicorn_amulet", new Item(new Item.Settings()), ItemGroups.TOOLS);
|
||||
AmuletItem UNICORN_AMULET = register("unicorn_amulet", new AmuletItem(new FabricItemSettings()
|
||||
.maxCount(1)
|
||||
.maxDamage(890)
|
||||
.rarity(Rarity.UNCOMMON), 900), ItemGroups.TOOLS);
|
||||
|
||||
GlassesItem SUNGLASSES = register("sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);
|
||||
GlassesItem BROKEN_SUNGLASSES = register("broken_sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);
|
||||
|
|
|
@ -123,6 +123,10 @@
|
|||
|
||||
"item.unicopia.alicorn_amulet": "Alicorn Amulet",
|
||||
"item.unicopia.alicorn_amulet.lore": "Time worn: %d",
|
||||
|
||||
"item.unicopia.broken_alicorn_amulet": "Broken Alicorn Amulet",
|
||||
"item.unicopia.unicorn_amulet": "Unicorn Amulet",
|
||||
"item.unicopia.unicorn_amulet.lore": "Grants magical abilities to whoever wears it",
|
||||
|
||||
"item.unicopia.grogars_bell": "Grogar's Bell",
|
||||
"item.unicopia.grogars_bell.charges": "Charges: %d / %d",
|
||||
|
@ -197,8 +201,12 @@
|
|||
"entity.unicopia.cast_spell": "Cast Spell",
|
||||
"entity.unicopia.cast_spell.by": "a spell cast by %s",
|
||||
"entity.unicopia.spellbook": "Spellbook",
|
||||
"entity.unicopia.air_balloon": "Hot Air Balloon",
|
||||
"entity.unicopia.sombra": "King Sombra",
|
||||
"entity.unicopia.sombra.taunt": "That's not going to work on me!",
|
||||
"entity.unicopia.storm_cloud": "Storm Cloud",
|
||||
"entity.unicopia.sombra": "King Sombra",
|
||||
"entity.unicopia.crystal_shards": "Crystal Shards",
|
||||
|
||||
"player.reachDistance": "Reach Distance",
|
||||
"player.miningSpeed": "Mining Speed",
|
||||
|
|
|
@ -1,18 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"parent": "unicopia:item/amulet",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/alicorn_amulet"
|
||||
},
|
||||
"display": {
|
||||
"thirdperson": {
|
||||
"rotation": [ -90, 0, 0 ],
|
||||
"translation": [ 0, 1, -3 ],
|
||||
"scale": [ 0.55, 0.55, 0.55 ]
|
||||
},
|
||||
"firstperson": {
|
||||
"rotation": [ 0, -135, 25 ],
|
||||
"translation": [ 0, 4, 2 ],
|
||||
"scale": [ 1.7, 1.7, 1.7 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/main/resources/assets/unicopia/models/item/amulet.json
Normal file
15
src/main/resources/assets/unicopia/models/item/amulet.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"display": {
|
||||
"thirdperson": {
|
||||
"rotation": [ -90, 0, 0 ],
|
||||
"translation": [ 0, 1, -3 ],
|
||||
"scale": [ 0.55, 0.55, 0.55 ]
|
||||
},
|
||||
"firstperson": {
|
||||
"rotation": [ 0, -135, 25 ],
|
||||
"translation": [ 0, 4, 2 ],
|
||||
"scale": [ 1.7, 1.7, 1.7 ]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/broken_alicorn_amulet"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"parent": "unicopia:item/amulet",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/pegasus_amulet"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "unicopia:item/amulet",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/unicorn_amulet"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 549 B After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -2,6 +2,7 @@
|
|||
"replace": false,
|
||||
"values": [
|
||||
"unicopia:alicorn_amulet",
|
||||
"unicopia:pegasus_amulet"
|
||||
"unicopia:pegasus_amulet",
|
||||
"unicopia:unicorn_amulet"
|
||||
]
|
||||
}
|
|
@ -9,9 +9,11 @@
|
|||
"unicopia:magic_staff",
|
||||
"unicopia:dragon_breath_scroll",
|
||||
"unicopia:spellbook",
|
||||
"unicopia:meadowbrooks_staff",
|
||||
"unicopia:grogars_bell",
|
||||
"unicopia:pegasus_amulet",
|
||||
"unicopia:alicorn_amulet",
|
||||
"unicopia:meadowbrooks_staff",
|
||||
"unicopia:grogars_bell"
|
||||
"unicopia:broken_alicorn_amulet",
|
||||
"unicopia:unicorn_amulet"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue