Added a gamerule to force players to select a new tribe after death

This commit is contained in:
Sollace 2023-01-27 18:06:06 +00:00
parent e052eb534f
commit 2b227eee1a
13 changed files with 91 additions and 49 deletions

View file

@ -0,0 +1,10 @@
package com.minelittlepony.unicopia;
import net.minecraft.world.GameRules;
import net.minecraft.world.GameRules.BooleanRule;
public interface UGameRules {
GameRules.Key<BooleanRule> SWAP_TRIBE_ON_DEATH = GameRules.register("swapTribeOnDeath", GameRules.Category.SPAWNING, BooleanRule.create(false));
static void bootstrap() { }
}

View file

@ -84,6 +84,7 @@ public class Unicopia implements ModInitializer {
Abilities.bootstrap(); Abilities.bootstrap();
UScreenHandlers.bootstrap(); UScreenHandlers.bootstrap();
UTreeGen.bootstrap(); UTreeGen.bootstrap();
UGameRules.bootstrap();
} }
public interface SidedAccess { public interface SidedAccess {

View file

@ -14,6 +14,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgMarkTraitRead; import com.minelittlepony.unicopia.network.MsgMarkTraitRead;
import com.minelittlepony.unicopia.network.MsgUnlockTraits; import com.minelittlepony.unicopia.network.MsgUnlockTraits;
import com.minelittlepony.unicopia.util.Copyable;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
@ -31,7 +32,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.world.World; import net.minecraft.world.World;
public class TraitDiscovery implements NbtSerialisable { public class TraitDiscovery implements NbtSerialisable, Copyable<TraitDiscovery> {
private final Set<Trait> unreadTraits = new HashSet<>(); private final Set<Trait> unreadTraits = new HashSet<>();
private final Set<Trait> traits = new HashSet<>(); private final Set<Trait> traits = new HashSet<>();
@ -152,7 +153,8 @@ public class TraitDiscovery implements NbtSerialisable {
return SpellTraits.fromNbt(nbt); return SpellTraits.fromNbt(nbt);
} }
public void copyFrom(TraitDiscovery old) { @Override
public void copyFrom(TraitDiscovery old, boolean alive) {
clear(); clear();
unreadTraits.addAll(old.unreadTraits); unreadTraits.addAll(old.unreadTraits);
traits.addAll(old.traits); traits.addAll(old.traits);

View file

@ -151,8 +151,10 @@ public class EntityPhysics<T extends Entity> implements Physics, Copyable<Entity
} }
@Override @Override
public void copyFrom(EntityPhysics<T> other) { public void copyFrom(EntityPhysics<T> other, boolean alive) {
setBaseGravityModifier(other.getBaseGravityModifier()); if (alive) {
setBaseGravityModifier(other.getBaseGravityModifier());
}
} }
@Override @Override

View file

@ -123,9 +123,11 @@ public class ItemTracker implements NbtSerialisable, Copyable<ItemTracker>, Tick
} }
@Override @Override
public void copyFrom(ItemTracker other) { public void copyFrom(ItemTracker other, boolean alive) {
items.clear(); items.clear();
items.putAll(other.items); if (alive) {
items.putAll(other.items);
}
} }
public interface Trackable extends ItemConvertible { public interface Trackable extends ItemConvertible {

View file

@ -70,6 +70,10 @@ public class RaceChangeStatusEffect extends StatusEffect {
return; return;
} }
if (eq instanceof Pony pony) {
pony.setRespawnRace(race);
}
int ticks = Math.max(0, MAX_DURATION - state.getDuration()); int ticks = Math.max(0, MAX_DURATION - state.getDuration());
Stage stage = Stage.forDuration(ticks / STAGE_DURATION); Stage stage = Stage.forDuration(ticks / STAGE_DURATION);
@ -80,23 +84,20 @@ public class RaceChangeStatusEffect extends StatusEffect {
int progression = ticks % (stage.ordinal() * STAGE_DURATION); int progression = ticks % (stage.ordinal() * STAGE_DURATION);
if ((eq instanceof Pony pony ? pony.getActualSpecies() : eq.getSpecies()) == race || !race.isPermitted(entity instanceof PlayerEntity ? (PlayerEntity)entity : null)) { if ((eq instanceof Pony pony ? pony.getActualSpecies() : eq.getSpecies()) == race || !race.isPermitted(entity instanceof PlayerEntity player ? player : null)) {
if (progression == 0 && entity instanceof PlayerEntity && stage == Stage.CRAWLING) { if (progression == 0 && entity instanceof PlayerEntity player && stage == Stage.CRAWLING) {
((PlayerEntity)entity).sendMessage(Stage.INITIAL.getMessage(race), true); player.sendMessage(Stage.INITIAL.getMessage(race), true);
} }
return; return;
} }
if (progression == 0) { if (progression == 0 && stage != Stage.DEATH && entity instanceof PlayerEntity player) {
if (stage != Stage.DEATH && entity instanceof PlayerEntity) { player.sendMessage(stage.getMessage(race), true);
((PlayerEntity)entity).sendMessage(stage.getMessage(race), true);
}
} }
entity.setHealth(1); entity.setHealth(1);
if (entity instanceof PlayerEntity) { if (eq instanceof Pony pony) {
Pony pony = (Pony)eq;
MagicReserves magic = pony.getMagicalReserves(); MagicReserves magic = pony.getMagicalReserves();
magic.getExertion().add(50); magic.getExertion().add(50);
magic.getEnergy().add(3); magic.getEnergy().add(3);
@ -108,8 +109,6 @@ public class RaceChangeStatusEffect extends StatusEffect {
} }
if (stage == Stage.DEATH) { if (stage == Stage.DEATH) {
eq.setSpecies(race);
if (eq instanceof Caster) { if (eq instanceof Caster) {
((Caster<?>)eq).getSpellSlot().clear(); ((Caster<?>)eq).getSpellSlot().clear();
} }
@ -119,11 +118,10 @@ public class RaceChangeStatusEffect extends StatusEffect {
magic.getEnergy().set(0.6F); magic.getEnergy().set(0.6F);
magic.getExhaustion().set(0); magic.getExhaustion().set(0);
magic.getExertion().set(0); magic.getExertion().set(0);
pony.setDirty(); entity.damage(MagicalDamageSource.TRIBE_SWAP, Float.MAX_VALUE);
} else {
eq.setSpecies(race);
} }
entity.damage(MagicalDamageSource.TRIBE_SWAP, Float.MAX_VALUE);
entity.setHealth(0);
} }
} }

View file

@ -21,10 +21,8 @@ import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.entity.effect.UEffects;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgOtherPlayerCapabilities;
import com.minelittlepony.unicopia.network.MsgPlayerAnimationChange;
import com.minelittlepony.unicopia.util.*; import com.minelittlepony.unicopia.util.*;
import com.minelittlepony.unicopia.network.*;
import com.minelittlepony.unicopia.network.datasync.EffectSync.UpdateCallback; import com.minelittlepony.unicopia.network.datasync.EffectSync.UpdateCallback;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate; import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import com.minelittlepony.common.util.animation.LinearInterpolator; import com.minelittlepony.common.util.animation.LinearInterpolator;
@ -82,6 +80,8 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
private final Interpolator interpolator = new LinearInterpolator(); private final Interpolator interpolator = new LinearInterpolator();
private Race respawnRace = Race.UNSET;
private boolean dirty; private boolean dirty;
private int ticksHanging; private int ticksHanging;
@ -151,6 +151,10 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return advancementProgress; return advancementProgress;
} }
public void setRespawnRace(Race race) {
respawnRace = race;
}
@Override @Override
public Race getSpecies() { public Race getSpecies() {
if (AmuletSelectors.ALICORN_AMULET.test(entity)) { if (AmuletSelectors.ALICORN_AMULET.test(entity)) {
@ -621,21 +625,41 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
} }
@Override @Override
public void copyFrom(Pony oldPlayer) { public void copyFrom(Pony oldPlayer, boolean alive) {
if (!oldPlayer.asEntity().isRemoved()) {
boolean forcedSwap = !alive
&& entity instanceof ServerPlayerEntity
&& entity.world.getGameRules().getBoolean(UGameRules.SWAP_TRIBE_ON_DEATH)
&& oldPlayer.asEntity().getDamageTracker().getMostRecentDamage().getDamageSource() != MagicalDamageSource.TRIBE_SWAP;
if (alive) {
oldPlayer.getSpellSlot().stream(true).forEach(getSpellSlot()::put); oldPlayer.getSpellSlot().stream(true).forEach(getSpellSlot()::put);
} else { } else {
oldPlayer.getSpellSlot().stream(true).filter(SpellPredicate.IS_PLACED).forEach(getSpellSlot()::put); if (forcedSwap) {
Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(entity), Text.translatable("gui.unicopia.tribe_selection.respawn")), (ServerPlayerEntity)entity);
} else {
oldPlayer.getSpellSlot().stream(true).filter(SpellPredicate.IS_PLACED).forEach(getSpellSlot()::put);
}
} }
oldPlayer.getSpellSlot().put(null); oldPlayer.getSpellSlot().put(null);
getArmour().copyFrom(oldPlayer.getArmour()); setSpecies(oldPlayer.respawnRace != Race.UNSET && !alive ? oldPlayer.respawnRace : oldPlayer.getActualSpecies());
setSpecies(oldPlayer.getActualSpecies()); getDiscoveries().copyFrom(oldPlayer.getDiscoveries(), alive);
getDiscoveries().copyFrom(oldPlayer.getDiscoveries()); getPhysics().copyFrom(oldPlayer.getPhysics(), alive);
getCharms().equipSpell(Hand.MAIN_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.MAIN_HAND)); if (!forcedSwap) {
getCharms().equipSpell(Hand.OFF_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.OFF_HAND)); getArmour().copyFrom(oldPlayer.getArmour(), alive);
corruption.set(oldPlayer.getCorruption().get()); getCharms().equipSpell(Hand.MAIN_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.MAIN_HAND));
levels.set(oldPlayer.getLevel().get()); getCharms().equipSpell(Hand.OFF_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.OFF_HAND));
mana.getXp().set(oldPlayer.getMagicalReserves().getXp().get()); corruption.set(oldPlayer.getCorruption().get());
levels.set(oldPlayer.getLevel().get());
mana.getXp().set(oldPlayer.getMagicalReserves().getXp().get());
} else {
mana.getEnergy().set(0.6F);
mana.getExhaustion().set(0);
mana.getExertion().set(0);
}
advancementProgress.putAll(oldPlayer.getAdvancementProgress()); advancementProgress.putAll(oldPlayer.getAdvancementProgress());
setDirty(); setDirty();
onSpawn(); onSpawn();

View file

@ -24,7 +24,7 @@ abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHan
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Inject(method = "copyFrom(Lnet/minecraft/server/network/ServerPlayerEntity;Z)V", at = @At("HEAD")) @Inject(method = "copyFrom(Lnet/minecraft/server/network/ServerPlayerEntity;Z)V", at = @At("HEAD"))
private void onCopyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo info) { private void onCopyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo info) {
get().copyFrom(((Equine.Container<Pony>)oldPlayer).get()); get().copyFrom(((Equine.Container<Pony>)oldPlayer).get(), alive);
} }
} }

View file

@ -7,6 +7,7 @@ import com.sollace.fabwork.api.packets.*;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public interface Channel { public interface Channel {
C2SPacketType<MsgPlayerAbility<?>> CLIENT_PLAYER_ABILITY = SimpleNetworking.clientToServer(Unicopia.id("player_ability"), MsgPlayerAbility::read); C2SPacketType<MsgPlayerAbility<?>> CLIENT_PLAYER_ABILITY = SimpleNetworking.clientToServer(Unicopia.id("player_ability"), MsgPlayerAbility::read);
@ -39,7 +40,7 @@ public interface Channel {
race = Race.UNSET; race = Race.UNSET;
} }
if (race.isUnset()) { if (race.isUnset()) {
sender.sendPacket(SERVER_SELECT_TRIBE.id(), new MsgTribeSelect(Race.allPermitted(handler.player)).toBuffer()); sender.sendPacket(SERVER_SELECT_TRIBE.id(), new MsgTribeSelect(Race.allPermitted(handler.player), Text.translatable("gui.unicopia.tribe_selection.journey")).toBuffer());
} else { } else {
pony.setSpecies(race); pony.setSpecies(race);
Unicopia.LOGGER.info("Setting {}'s race to {} due to host setting", handler.player.getDisplayName().getString(), Race.REGISTRY.getId(race).toString()); Unicopia.LOGGER.info("Setting {}'s race to {} due to host setting", handler.player.getDisplayName().getString(), Race.REGISTRY.getId(race).toString());

View file

@ -8,19 +8,19 @@ import com.sollace.fabwork.api.packets.Packet;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
public record MsgTribeSelect (Set<Race> availableRaces) implements Packet<PlayerEntity> { public record MsgTribeSelect (Set<Race> availableRaces, Text serverMessage) implements Packet<PlayerEntity> {
public MsgTribeSelect(PacketByteBuf buffer) { public MsgTribeSelect(PacketByteBuf buffer) {
this(new HashSet<>()); this(
int len = buffer.readInt(); buffer.readCollection(HashSet::new, buf -> buf.readRegistryValue(Race.REGISTRY)),
while (len-- > 0) { buffer.readText()
availableRaces.add(buffer.readRegistryValue(Race.REGISTRY)); );
}
} }
@Override @Override
public void toBuffer(PacketByteBuf buffer) { public void toBuffer(PacketByteBuf buffer) {
buffer.writeInt(availableRaces.size()); buffer.writeCollection(availableRaces, (buf, race) -> buf.writeRegistryValue(Race.REGISTRY, race));
availableRaces.forEach(race -> buffer.writeRegistryValue(Race.REGISTRY, race)); buffer.writeText(serverMessage);
} }
} }

View file

@ -1,5 +1,5 @@
package com.minelittlepony.unicopia.util; package com.minelittlepony.unicopia.util;
public interface Copyable<T extends Copyable<T>> { public interface Copyable<T extends Copyable<T>> {
void copyFrom(T other); void copyFrom(T other, boolean alive);
} }

View file

@ -18,7 +18,7 @@ public class MagicalDamageSource extends EntityDamageSource {
public static final DamageSource EXHAUSTION = new MagicalDamageSource("magical_exhaustion", null, true, true); public static final DamageSource EXHAUSTION = new MagicalDamageSource("magical_exhaustion", null, true, true);
public static final DamageSource ALICORN_AMULET = new MagicalDamageSource("alicorn_amulet", null, true, true); public static final DamageSource ALICORN_AMULET = new MagicalDamageSource("alicorn_amulet", null, true, true);
public static final DamageSource FOOD_POISONING = new DamageSource("food_poisoning"); public static final DamageSource FOOD_POISONING = new DamageSource("food_poisoning");
public static final DamageSource TRIBE_SWAP = new DamageSource("tribe_swap"); public static final DamageSource TRIBE_SWAP = new DamageSource("tribe_swap").setOutOfWorld().setUnblockable();
public static final DamageSource ZAP_APPLE = create("zap"); public static final DamageSource ZAP_APPLE = create("zap");
public static final DamageSource KICK = create("kick"); public static final DamageSource KICK = create("kick");
public static final DamageSource SUN = new DamageSource("sun").setBypassesArmor().setFire(); public static final DamageSource SUN = new DamageSource("sun").setBypassesArmor().setFire();

View file

@ -1,4 +1,6 @@
accessWidener v1 named accessWidener v1 named
accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase;
accessible class net/minecraft/client/render/item/HeldItemRenderer$HandRenderType accessible class net/minecraft/client/render/item/HeldItemRenderer$HandRenderType
accessible method net/minecraft/world/GameRules register (Ljava/lang/String;Lnet/minecraft/world/GameRules$Category;Lnet/minecraft/world/GameRules$Type;)Lnet/minecraft/world/GameRules$Key;
accessible method net/minecraft/world/GameRules$BooleanRule create (Z)Lnet/minecraft/world/GameRules$Type;