diff --git a/src/main/java/mc/duzo/timeless/Timeless.java b/src/main/java/mc/duzo/timeless/Timeless.java index 4d4afcc8..aab3123a 100644 --- a/src/main/java/mc/duzo/timeless/Timeless.java +++ b/src/main/java/mc/duzo/timeless/Timeless.java @@ -2,6 +2,7 @@ import dev.amble.lib.container.RegistryContainer; import net.fabricmc.api.ModInitializer; +import net.minecraft.util.Identifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,4 +39,8 @@ public void onInitialize() { // Networking Network.init(); } + + public static Identifier id(String path) { + return new Identifier(MOD_ID, path); + } } \ No newline at end of file diff --git a/src/main/java/mc/duzo/timeless/mixin/client/PlayerEntityRendererMixin.java b/src/main/java/mc/duzo/timeless/mixin/client/PlayerEntityRendererMixin.java index c8e5aa90..c43c9ab8 100644 --- a/src/main/java/mc/duzo/timeless/mixin/client/PlayerEntityRendererMixin.java +++ b/src/main/java/mc/duzo/timeless/mixin/client/PlayerEntityRendererMixin.java @@ -1,5 +1,6 @@ package mc.duzo.timeless.mixin.client; +import mc.duzo.timeless.suit.client.render.SuitModel; import net.minecraft.entity.EquipmentSlot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -35,6 +36,20 @@ public PlayerEntityRendererMixin(EntityRendererFactory.Context ctx, PlayerEntity } @Inject(method = "renderArm" , at = @At("HEAD"), cancellable = true) + private void timeless$stopArmRender(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo ci){ + Suit suit = Suit.findSuit(player, EquipmentSlot.CHEST).orElse(null); + if (suit == null) return; + ClientSuit clientSuit = suit.toClient(); + if (!(clientSuit.hasModel())) return; + + boolean isRight = player.getMainArm() == Arm.RIGHT; + // todo - this will create a new model every frame, which is bad + SuitModel model = clientSuit.model().get(); + + if (!model.shouldRenderOriginalArm(isRight, player)) ci.cancel(); + } + + @Inject(method = "renderArm" , at = @At("TAIL")) private void timeless$renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo ci){ Suit suit = Suit.findSuit(player, EquipmentSlot.CHEST).orElse(null); if (suit == null) return; @@ -43,7 +58,7 @@ public PlayerEntityRendererMixin(EntityRendererFactory.Context ctx, PlayerEntity boolean isRight = player.getMainArm() == Arm.RIGHT; // todo - this will create a new model every frame, which is bad - clientSuit.model().get().renderArm(isRight, player, 0, matrices, vertexConsumers.getBuffer(RenderLayer.getEntityTranslucent(clientSuit.texture())), light, 1, 1, 1, 1); - ci.cancel(); + SuitModel model = clientSuit.model().get(); + model.renderArm(isRight, player, 0, matrices, vertexConsumers.getBuffer(RenderLayer.getEntityTranslucent(clientSuit.texture())), light, 1, 1, 1, 1); } } \ No newline at end of file diff --git a/src/main/java/mc/duzo/timeless/power/BasicTogglePower.java b/src/main/java/mc/duzo/timeless/power/BasicTogglePower.java new file mode 100644 index 00000000..9eab453c --- /dev/null +++ b/src/main/java/mc/duzo/timeless/power/BasicTogglePower.java @@ -0,0 +1,67 @@ +package mc.duzo.timeless.power; + +import mc.duzo.timeless.suit.api.EquipSoundSupplier; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Consumer; + +public class BasicTogglePower extends TogglePower { + @Nullable + protected final Consumer toggleAction; + @Nullable + protected final Consumer enabledTick; + @Nullable + protected final Consumer disabledTick; + + public BasicTogglePower(String modid, String key, + @Nullable Consumer enabled, + @Nullable Consumer toggle, + @Nullable Consumer disabledTick) { + super(modid, key); + + this.toggleAction = toggle; + this.enabledTick = enabled; + this.disabledTick = disabledTick; + } + + @Override + public boolean run(ServerPlayerEntity player) { + if (toggleAction != null) { + toggleAction.accept(player); + } + + return super.run(player); + } + + @Override + public void tick(ServerPlayerEntity player) { + boolean value = getValue(player); + + if (value && enabledTick != null) { + enabledTick.accept(player); + } else if (!value && disabledTick != null) { + disabledTick.accept(player); + } + + super.tick(player); + } + + @Override + protected Class getSuitClass() { + return EquipSoundSupplier.class; + } + + @Override + public Identifier getAnimation(boolean isOpening, EquipSoundSupplier suit) { + return null; + } + + @Override + public Optional getSound(EquipSoundSupplier suit) { + return suit.getEquipSound(); + } +} diff --git a/src/main/java/mc/duzo/timeless/power/PowerRegistry.java b/src/main/java/mc/duzo/timeless/power/PowerRegistry.java index dd96e931..28a3ea3f 100644 --- a/src/main/java/mc/duzo/timeless/power/PowerRegistry.java +++ b/src/main/java/mc/duzo/timeless/power/PowerRegistry.java @@ -54,6 +54,10 @@ public static T register(T entry) { }) .build().register(); public static Power SWIFT_SNEAK = Power.Builder.create(new Identifier(Timeless.MOD_ID, "swift_sneak")).build().register(); + public static BasicTogglePower INVISIBILITY = new BasicTogglePower(Timeless.MOD_ID, "invisibility", (player) -> { + if (player.getServer().getTicks() % 20 != 0) return; // Run every second + player.addStatusEffect(new StatusEffectInstance(StatusEffects.INVISIBILITY, 22, 3, false, false)); + }, null, null).register(); public static void init() {} } diff --git a/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitItem.java b/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitItem.java new file mode 100644 index 00000000..784a7a1b --- /dev/null +++ b/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitItem.java @@ -0,0 +1,14 @@ +package mc.duzo.timeless.suit.api; + +import mc.duzo.timeless.core.items.SuitItem; +import mc.duzo.timeless.core.items.SuitMaterial; +import mc.duzo.timeless.datagen.provider.lang.AutomaticSuitEnglish; +import mc.duzo.timeless.datagen.provider.model.AutomaticModel; +import mc.duzo.timeless.suit.Suit; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +public class SimpleSuitItem extends SuitItem implements AutomaticModel, AutomaticSuitEnglish { + public SimpleSuitItem(Suit suit, Type type, SuitMaterial material) { + super(suit, material, type, new FabricItemSettings().maxCount(1)); + } +} diff --git a/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitMaterial.java b/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitMaterial.java new file mode 100644 index 00000000..3d0fe868 --- /dev/null +++ b/src/main/java/mc/duzo/timeless/suit/api/SimpleSuitMaterial.java @@ -0,0 +1,21 @@ +package mc.duzo.timeless.suit.api; + +import mc.duzo.timeless.core.items.SuitMaterial; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Util; + +import java.util.EnumMap; + +public class SimpleSuitMaterial extends SuitMaterial { + public SimpleSuitMaterial(Item repair, String name) { + super(name, 33, Util.make(new EnumMap(ArmorItem.Type.class), map -> { + map.put(ArmorItem.Type.BOOTS, 3); + map.put(ArmorItem.Type.LEGGINGS, 6); + map.put(ArmorItem.Type.CHESTPLATE, 8); + map.put(ArmorItem.Type.HELMET, 3); + }), 10, SoundEvents.ITEM_ARMOR_EQUIP_CHAIN, 2.0F, 0.0F, () -> Ingredient.ofItems(repair)); + } +} diff --git a/src/main/java/mc/duzo/timeless/suit/client/render/SuitModel.java b/src/main/java/mc/duzo/timeless/suit/client/render/SuitModel.java index 21f05faf..e0cef766 100644 --- a/src/main/java/mc/duzo/timeless/suit/client/render/SuitModel.java +++ b/src/main/java/mc/duzo/timeless/suit/client/render/SuitModel.java @@ -1,6 +1,7 @@ package mc.duzo.timeless.suit.client.render; import mc.duzo.animation.generic.AnimationInfo; +import mc.duzo.animation.generic.VisiblePart; import mc.duzo.timeless.suit.client.ClientSuit; import mc.duzo.timeless.suit.client.animation.SuitAnimationHolder; import mc.duzo.timeless.suit.client.animation.SuitAnimationTracker; @@ -26,6 +27,12 @@ public abstract class SuitModel extends EntityModel { public abstract void render(LivingEntity entity, float tickDelta, MatrixStack matrices, VertexConsumer vertexConsumers, int light, float r, float g, float b, float alpha); public abstract void renderArm(boolean isRight, AbstractClientPlayerEntity player, int i, MatrixStack matrices, VertexConsumer buffer, int light, int i1, int i2, int i3, int i4); + public boolean shouldRenderOriginalArm(boolean isRight, AbstractClientPlayerEntity player) { + VisiblePart requiredPart = isRight ? VisiblePart.RIGHT_ARM : VisiblePart.LEFT_ARM; + + return this.getSuit().getVisibleParts(player).contains(requiredPart); + } + /** * Sets the visibility of the model parts for the given slot. */ diff --git a/src/main/java/mc/duzo/timeless/suit/client/render/generic/AlexSuitModel.java b/src/main/java/mc/duzo/timeless/suit/client/render/generic/AlexSuitModel.java index d1b1af60..9f8698f2 100644 --- a/src/main/java/mc/duzo/timeless/suit/client/render/generic/AlexSuitModel.java +++ b/src/main/java/mc/duzo/timeless/suit/client/render/generic/AlexSuitModel.java @@ -80,13 +80,17 @@ public void render(LivingEntity entity, float tickDelta, MatrixStack matrices, V } } + float factor = 1.01f; + matrices.scale(factor, factor, factor); + matrices.translate(0.0F, 1 - factor, 1 - factor); + this.getPart().render(matrices, vertexConsumers, light, OverlayTexture.DEFAULT_UV, r, g, b, alpha); matrices.pop(); } @Override public void renderArm(boolean isRight, AbstractClientPlayerEntity player, int i, MatrixStack matrices, VertexConsumer buffer, int light, int i1, int i2, int i3, int i4) { - if (isRight) this.renderRightArm(player, i, matrices, buffer, light, i1, i2, i3, i4); + if (isRight) this.renderRightArm(player, i, matrices, buffer, light, i1, i2, i3, i4); else this.renderLeftArm(player, i, matrices, buffer, light, i1, i2, i3, i4); } @@ -94,7 +98,8 @@ private void renderRightArm(AbstractClientPlayerEntity player, int i, MatrixStac matrices.push(); this.rightArm.resetTransform(); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-0.5f)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(5f)); + matrices.translate(0.015F, -0.015F, -0.055F); this.rightArm.render(matrices, buffer, light, OverlayTexture.DEFAULT_UV, 1f, 1f, 1f, 1f); matrices.pop(); @@ -104,6 +109,8 @@ private void renderLeftArm(AbstractClientPlayerEntity player, int i, MatrixStack matrices.push(); this.leftArm.resetTransform(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-5f)); + matrices.translate(0.015F, -0.015F, -0.055F); this.leftArm.render(matrices, buffer, light, OverlayTexture.DEFAULT_UV, 1f, 1f, 1f, 1f); matrices.pop(); diff --git a/src/main/java/mc/duzo/timeless/suit/client/render/generic/SteveSuitModel.java b/src/main/java/mc/duzo/timeless/suit/client/render/generic/SteveSuitModel.java index 8c54ade4..173328a8 100644 --- a/src/main/java/mc/duzo/timeless/suit/client/render/generic/SteveSuitModel.java +++ b/src/main/java/mc/duzo/timeless/suit/client/render/generic/SteveSuitModel.java @@ -80,6 +80,10 @@ public void render(LivingEntity entity, float tickDelta, MatrixStack matrices, V } } + float factor = 1.01f; + matrices.scale(factor, factor, factor); + matrices.translate(0.0F, 1 - factor, 1 - factor); + this.getPart().render(matrices, vertexConsumers, light, OverlayTexture.DEFAULT_UV, r, g, b, alpha); matrices.pop(); } @@ -94,7 +98,8 @@ private void renderRightArm(AbstractClientPlayerEntity player, int i, MatrixStac matrices.push(); this.rightArm.resetTransform(); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-0.5f)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(5f)); + matrices.translate(0.015F, -0.015F, -0.055F); this.rightArm.render(matrices, buffer, light, OverlayTexture.DEFAULT_UV, 1f, 1f, 1f, 1f); matrices.pop(); @@ -104,6 +109,8 @@ private void renderLeftArm(AbstractClientPlayerEntity player, int i, MatrixStack matrices.push(); this.leftArm.resetTransform(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-5f)); + matrices.translate(0.015F, -0.015F, -0.055F); this.leftArm.render(matrices, buffer, light, OverlayTexture.DEFAULT_UV, 1f, 1f, 1f, 1f); matrices.pop(); diff --git a/src/main/java/mc/duzo/timeless/suit/fantastic/FantasticFourSuit.java b/src/main/java/mc/duzo/timeless/suit/fantastic/FantasticFourSuit.java new file mode 100644 index 00000000..28ee52a1 --- /dev/null +++ b/src/main/java/mc/duzo/timeless/suit/fantastic/FantasticFourSuit.java @@ -0,0 +1,144 @@ +package mc.duzo.timeless.suit.fantastic; + +import mc.duzo.animation.generic.VisibilityList; +import mc.duzo.animation.generic.VisiblePart; +import mc.duzo.timeless.Timeless; +import mc.duzo.timeless.datagen.provider.lang.AutomaticSuitEnglish; +import mc.duzo.timeless.power.PowerList; +import mc.duzo.timeless.suit.Suit; +import mc.duzo.timeless.suit.api.FlightSuit; +import mc.duzo.timeless.suit.client.ClientSuit; +import mc.duzo.timeless.suit.client.render.SuitModel; +import mc.duzo.timeless.suit.client.render.generic.SteveSuitModel; +import mc.duzo.timeless.suit.set.SuitSet; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.entity.LivingEntity; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.sound.SoundEvent; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.random.Random; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +public abstract class FantasticFourSuit extends Suit implements AutomaticSuitEnglish, FlightSuit { + private final Identifier id; + + protected FantasticFourSuit(Identifier id) { + this.id = id; + } + + protected FantasticFourSuit(String mark, String modid) { + this(new Identifier(modid, "fantastic_" + mark)); + } + + /** + * For Timeless heroes ONLY + * Addon mods should use other constructor + */ + protected FantasticFourSuit(String mark) { + this(mark, Timeless.MOD_ID); + } + + public static FantasticFourSuit create(Identifier id, PowerList powers, Supplier set) { + return new FantasticFourSuit(id) { + @Override + public PowerList getPowers() { + return powers; + } + + @Override + public SuitSet getSet() { + return set.get(); + } + + @Override + public int getFlightSpeed(boolean hasBoost) { + return 50; + } + }; + } + + @Override + public boolean isBinding() { + return true; + } + + @Override + public Identifier id() { + return this.id; + } + + @Override + public SoundEvent getStepSound() { + return SoundEvents.BLOCK_WOOL_STEP; + } + + @Override + public Optional getEquipSound() { + return Optional.of(SoundEvents.ITEM_ARMOR_EQUIP_LEATHER); + } + + @Override + public Optional getUnequipSound() { + return Optional.of(SoundEvents.ITEM_ARMOR_EQUIP_LEATHER); + } + + + @Override + public void createFlightParticles(ServerPlayerEntity player) { + // taken from the old forge archive, will need reworking + ServerWorld world = player.getServerWorld(); + Random random = world.getRandom(); + + int i = MathHelper.clamp(0, 0, 64); + double f2 = Math.cos(player.getBodyYaw() * ((float) Math.PI / 180F)) * (0.1F + 0.21F * (float) i); + double f3 = Math.sin(player.getBodyYaw() * ((float) Math.PI / 180F)) * (0.1F + 0.21F * (float) i); + double f4 = Math.cos(player.getBodyYaw() * ((float) Math.PI / 180F)) * (0.4F + 0.21F * (float) i); + double f5 = Math.sin(player.getBodyYaw() * ((float) Math.PI / 180F)) * (0.4F + 0.21F * (float) i); + float f6 = (0.3F * 0.45F) * ((float) i * 0.2F + 1F); + float f7 = (0.3F * 0.45F) * ((float) i * 0.2F + 6F); + + world.spawnParticles(ParticleTypes.SMOKE, player.getX() + f2, player.getY() + (double) f6, player.getZ() + f3, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMOKE, player.getX() - f2, player.getY() + (double) f6, player.getZ() - f3, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMOKE, player.getX() + f4, player.getY() + (double) f7, player.getZ() + f5, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMOKE, player.getX() - f4, player.getY() + (double) f7, player.getZ() - f5, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMALL_FLAME, player.getX() + f2, player.getY() + (double) f6, player.getZ() + f3, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMALL_FLAME, player.getX() - f2, player.getY() + (double) f6, player.getZ() - f3, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMALL_FLAME, player.getX() + f4, player.getY() + (double) f7, player.getZ() + f5, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + world.spawnParticles(ParticleTypes.SMALL_FLAME, player.getX() - f4, player.getY() + (double) f7, player.getZ() - f5, 1, random.nextGaussian() * 0.05D, -0.25, random.nextGaussian() * 0.05D, 0.0D); + } + + @Override + public boolean isAlwaysVisible() { + return false; + } + + @Environment(EnvType.CLIENT) + @Override + protected ClientSuit createClient() { + return new ClientSuit(this) { + @Override + public Supplier model() { + return () -> new SteveSuitModel(this); + } + + @Override + public Identifier texture() { + return new Identifier(this.id().getNamespace(), "textures/suit/fantastic_four.png"); + } + + @Override + public List getVisibleParts(LivingEntity entity) { + // Head and arms. + return VisibilityList.of(VisiblePart.LEFT_ARM, VisiblePart.RIGHT_ARM, VisiblePart.HEAD, VisiblePart.HAT); + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/mc/duzo/timeless/suit/set/SetRegistry.java b/src/main/java/mc/duzo/timeless/suit/set/SetRegistry.java index dfea107e..e6df74a7 100644 --- a/src/main/java/mc/duzo/timeless/suit/set/SetRegistry.java +++ b/src/main/java/mc/duzo/timeless/suit/set/SetRegistry.java @@ -1,11 +1,17 @@ package mc.duzo.timeless.suit.set; +import mc.duzo.timeless.power.PowerList; +import mc.duzo.timeless.power.PowerRegistry; +import mc.duzo.timeless.suit.api.SimpleSuitItem; +import mc.duzo.timeless.suit.api.SimpleSuitMaterial; +import mc.duzo.timeless.suit.fantastic.FantasticFourSuit; import mc.duzo.timeless.suit.moonknight.item.MoonKnightSuitItem; import mc.duzo.timeless.suit.moonknight.jake.JakeSuit; import mc.duzo.timeless.suit.moonknight.marc.MarcSuit; import mc.duzo.timeless.suit.moonknight.steven.StevenSuit; import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.minecraft.item.Items; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.SimpleRegistry; @@ -35,6 +41,7 @@ public static T register(T suit) { public static SuitSet MOON_KNIGHT_MARC; public static SuitSet MOON_KNIGHT_JAKE; public static SuitSet MOON_KNIGHT_STEVEN; + public static SuitSet FANTASTIC_INVISIBLE; public static void init() { // Iron Man @@ -45,8 +52,14 @@ public static void init() { // Batman BATMAN_66 = register(new RegisteringSuitSet(new Batman66Suit(), BatmanSuitItem::new)); + + // Moon Knight MOON_KNIGHT_MARC = register(new RegisteringSuitSet(new MarcSuit(), MoonKnightSuitItem::new)); MOON_KNIGHT_JAKE = register(new RegisteringSuitSet(new JakeSuit(), MoonKnightSuitItem::new)); MOON_KNIGHT_STEVEN = register(new RegisteringSuitSet(new StevenSuit(), MoonKnightSuitItem::new)); + + // Fantastic Four + FANTASTIC_INVISIBLE = register(new RegisteringSuitSet(FantasticFourSuit.create(Timeless.id("fantastic_invisible"), PowerList.of(PowerRegistry.INVISIBILITY), () -> SetRegistry.FANTASTIC_INVISIBLE), + (suit, type) -> new SimpleSuitItem(suit, type, new SimpleSuitMaterial(Items.COPPER_INGOT, "fantastic")))); } } diff --git a/src/main/resources/assets/timeless/textures/suit/fantastic_four.png b/src/main/resources/assets/timeless/textures/suit/fantastic_four.png new file mode 100644 index 00000000..8efae86a Binary files /dev/null and b/src/main/resources/assets/timeless/textures/suit/fantastic_four.png differ