Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"base_mobs": 2,
"entity_tables": [
{
"attribute_modifiers": [
{
"amount": 0.5,
"attribute": "minecraft:generic.max_health",
"id": "mia:trial_spawner_health",
"operation": "add_multiplied_total"
}
],
"effects": [
{
"amplifier": 0,
"duration": -1,
"effect": "minecraft:fire_resistance"
}
],
"entity": "minecraft:zombie",
"equipment": {
"chest": "minecraft:iron_chestplate",
"head": "minecraft:iron_helmet",
"mainhand": "minecraft:diamond_sword"
},
"weight": 10
},
{
"attribute_modifiers": [
{
"amount": 2.0,
"attribute": "minecraft:generic.attack_damage",
"id": "mia:trial_spawner_damage",
"operation": "add_value"
}
],
"effects": [
{
"amplifier": 1,
"duration": -1,
"effect": "minecraft:speed"
}
],
"entity": "minecraft:skeleton",
"equipment": {
"head": "minecraft:chainmail_helmet",
"mainhand": "minecraft:bow"
},
"weight": 5
}
],
"loot_tables": [
{
"loot_table": "minecraft:chests/trial_chambers/reward_rare",
"weight": 10
},
{
"loot_table": "minecraft:chests/trial_chambers/reward_unique",
"weight": 3
}
],
"mobs_per_player": 1,
"spawn_per_tick": 1,
"spawn_range": 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package com.altnoir.mia.block.entity.renderer;

import com.altnoir.mia.block.entity.AbyssSpawnerBlockEntity;
import com.altnoir.mia.client.render.MiaRenderTypes;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.util.FastColor;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import org.joml.Matrix4f;
import org.joml.Vector3f;

public class AbyssSpawnerRenderer implements BlockEntityRenderer<AbyssSpawnerBlockEntity> {
private static final int SPHERE_SEGMENTS = 16;
private static final int SPHERE_RINGS = 12;
private static final float BASE_RADIUS = 0.25f;

private static final int CORE_COLOR = FastColor.ARGB32.color(230, 80, 20, 120);
private static final int OUTER_COLOR = FastColor.ARGB32.color(180, 120, 60, 180);

public AbyssSpawnerRenderer(BlockEntityRendererProvider.Context context) {
}

@Override
public void render(AbyssSpawnerBlockEntity blockEntity, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) {
var level = blockEntity.getLevel();
if (level == null) {
return;
}

if (!blockEntity.hasValidPattern()) {
return;
}

var spawner = blockEntity.getAbyssSpawner();
float time = (level.getGameTime() + partialTick) * 0.05f;

poseStack.pushPose();
poseStack.translate(0.5, 0.5, 0.5);

double spin = Mth.lerp(partialTick, spawner.getOSpin(), spawner.getSpin());
poseStack.mulPose(Axis.YP.rotationDegrees((float) spin));

float bob = Mth.sin(time * 0.5f) * 0.05f;
poseStack.translate(0, bob, 0);

float pulse = 1.0f + Mth.sin(time * 2.0f) * 0.1f;
float radius = BASE_RADIUS * pulse;

renderDistortedSphere(poseStack, bufferSource, radius, time, packedLight);

poseStack.scale(1.3f, 1.3f, 1.3f);
renderGlowingSphere(poseStack, bufferSource, radius * 0.8f, time);

poseStack.popPose();
}

private void renderDistortedSphere(PoseStack poseStack, MultiBufferSource bufferSource, float radius, float time, int packedLight) {
VertexConsumer buffer = bufferSource.getBuffer(MiaRenderTypes.ABYSS_ORB);
Matrix4f matrix = poseStack.last().pose();

RandomSource random = RandomSource.create(42L);

for (int ring = 0; ring < SPHERE_RINGS; ring++) {
float theta1 = (float) Math.PI * ring / SPHERE_RINGS;
float theta2 = (float) Math.PI * (ring + 1) / SPHERE_RINGS;

for (int seg = 0; seg < SPHERE_SEGMENTS; seg++) {
float phi1 = 2.0f * (float) Math.PI * seg / SPHERE_SEGMENTS;
float phi2 = 2.0f * (float) Math.PI * (seg + 1) / SPHERE_SEGMENTS;

float distort1 = getDistortion(theta1, phi1, time, random);
float distort2 = getDistortion(theta2, phi1, time, random);
float distort3 = getDistortion(theta2, phi2, time, random);
float distort4 = getDistortion(theta1, phi2, time, random);

Vector3f p1 = spherePoint(theta1, phi1, radius * distort1);
Vector3f p2 = spherePoint(theta2, phi1, radius * distort2);
Vector3f p3 = spherePoint(theta2, phi2, radius * distort3);
Vector3f p4 = spherePoint(theta1, phi2, radius * distort4);

float v1 = (float) ring / SPHERE_RINGS;
float v2 = (float) (ring + 1) / SPHERE_RINGS;

int color1 = lerpColor(CORE_COLOR, OUTER_COLOR, v1);
int color2 = lerpColor(CORE_COLOR, OUTER_COLOR, v2);

addVertex(buffer, matrix, p1, color1, packedLight);
addVertex(buffer, matrix, p2, color2, packedLight);
addVertex(buffer, matrix, p3, color2, packedLight);

addVertex(buffer, matrix, p1, color1, packedLight);
addVertex(buffer, matrix, p3, color2, packedLight);
addVertex(buffer, matrix, p4, color1, packedLight);
}
}
}

private void renderGlowingSphere(PoseStack poseStack, MultiBufferSource bufferSource, float radius, float time) {
VertexConsumer buffer = bufferSource.getBuffer(MiaRenderTypes.ABYSS_ORB_GLOW);
Matrix4f matrix = poseStack.last().pose();

float glowPulse = Mth.sin(time * 3.0f) * 0.5f + 0.5f;
int glowAlpha = (int) (80 + glowPulse * 100);
int glowColor = FastColor.ARGB32.color(glowAlpha, 180, 80, 220);

for (int ring = 0; ring < SPHERE_RINGS / 2; ring++) {
float theta1 = (float) Math.PI * ring / (SPHERE_RINGS / 2);
float theta2 = (float) Math.PI * (ring + 1) / (SPHERE_RINGS / 2);

for (int seg = 0; seg < SPHERE_SEGMENTS / 2; seg++) {
float phi1 = 2.0f * (float) Math.PI * seg / (SPHERE_SEGMENTS / 2);
float phi2 = 2.0f * (float) Math.PI * (seg + 1) / (SPHERE_SEGMENTS / 2);

Vector3f p1 = spherePoint(theta1, phi1, radius);
Vector3f p2 = spherePoint(theta2, phi1, radius);
Vector3f p3 = spherePoint(theta2, phi2, radius);
Vector3f p4 = spherePoint(theta1, phi2, radius);

addVertex(buffer, matrix, p1, glowColor, LightTexture.FULL_BRIGHT);
addVertex(buffer, matrix, p2, glowColor, LightTexture.FULL_BRIGHT);
addVertex(buffer, matrix, p3, glowColor, LightTexture.FULL_BRIGHT);

addVertex(buffer, matrix, p1, glowColor, LightTexture.FULL_BRIGHT);
addVertex(buffer, matrix, p3, glowColor, LightTexture.FULL_BRIGHT);
addVertex(buffer, matrix, p4, glowColor, LightTexture.FULL_BRIGHT);
}
}
}

private float getDistortion(float theta, float phi, float time, RandomSource random) {
float noise = Mth.sin(theta * 5 + time) * Mth.cos(phi * 3 + time * 0.7f);
float secondary = Mth.sin(theta * 8 - time * 1.3f) * Mth.sin(phi * 6 + time * 0.5f);
return 1.0f + noise * 0.15f + secondary * 0.08f;
}

private Vector3f spherePoint(float theta, float phi, float radius) {
float x = radius * Mth.sin(theta) * Mth.cos(phi);
float y = radius * Mth.cos(theta);
float z = radius * Mth.sin(theta) * Mth.sin(phi);
return new Vector3f(x, y, z);
}

private void addVertex(VertexConsumer buffer, Matrix4f matrix, Vector3f pos, int color, int light) {
buffer.addVertex(matrix, pos.x, pos.y, pos.z)
.setColor(color)
.setLight(light);
}

private int lerpColor(int color1, int color2, float t) {
int a1 = FastColor.ARGB32.alpha(color1);
int r1 = FastColor.ARGB32.red(color1);
int g1 = FastColor.ARGB32.green(color1);
int b1 = FastColor.ARGB32.blue(color1);

int a2 = FastColor.ARGB32.alpha(color2);
int r2 = FastColor.ARGB32.red(color2);
int g2 = FastColor.ARGB32.green(color2);
int b2 = FastColor.ARGB32.blue(color2);

int a = (int) Mth.lerp(t, a1, a2);
int r = (int) Mth.lerp(t, r1, r2);
int g = (int) Mth.lerp(t, g1, g2);
int b = (int) Mth.lerp(t, b1, b2);

return FastColor.ARGB32.color(a, r, g, b);
}

@Override
public boolean shouldRenderOffScreen(AbyssSpawnerBlockEntity blockEntity) {
return true;
}

@Override
public int getViewDistance() {
return 64;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.altnoir.mia.client.event;

import com.altnoir.mia.block.entity.renderer.AbyssSpawnerRenderer;
import com.altnoir.mia.block.entity.renderer.EndlessCupBlockRenderer;
import com.altnoir.mia.block.entity.renderer.PedestalBlockRenderer;
import com.altnoir.mia.block.entity.renderer.SunStoneBlockRenderer;
Expand All @@ -14,5 +15,6 @@ public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderer
event.registerBlockEntityRenderer(MiaBlockEntities.PEDESTAL.get(), PedestalBlockRenderer::new);
event.registerBlockEntityRenderer(MiaBlockEntities.ENDLESS_CUP_BLOCK_ENTITY.get(), EndlessCupBlockRenderer::new);
event.registerBlockEntityRenderer(MiaBlockEntities.SUN_STONE_BLOCK_ENTITY.get(), SunStoneBlockRenderer::new);
event.registerBlockEntityRenderer(MiaBlockEntities.ABYSS_SPAWNER.get(), AbyssSpawnerRenderer::new);
}
}
44 changes: 44 additions & 0 deletions src/main/java/com/altnoir/mia/client/render/MiaRenderTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.altnoir.mia.client.render;

import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;

public class MiaRenderTypes extends RenderType {

public MiaRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, int bufferSize, boolean affectsCrumbling, boolean sortOnUpload, Runnable setupState, Runnable clearState) {
super(name, format, mode, bufferSize, affectsCrumbling, sortOnUpload, setupState, clearState);
}

public static final RenderType ABYSS_ORB = create(
"mia_abyss_orb",
DefaultVertexFormat.POSITION_COLOR_LIGHTMAP,
VertexFormat.Mode.TRIANGLES,
1536,
false,
true,
CompositeState.builder()
.setShaderState(POSITION_COLOR_LIGHTMAP_SHADER)
.setTransparencyState(TRANSLUCENT_TRANSPARENCY)
.setWriteMaskState(COLOR_DEPTH_WRITE)
.setCullState(NO_CULL)
.setDepthTestState(LEQUAL_DEPTH_TEST)
.createCompositeState(false)
);

public static final RenderType ABYSS_ORB_GLOW = create(
"mia_abyss_orb_glow",
DefaultVertexFormat.POSITION_COLOR_LIGHTMAP,
VertexFormat.Mode.TRIANGLES,
1536,
false,
true,
CompositeState.builder()
.setShaderState(POSITION_COLOR_LIGHTMAP_SHADER)
.setTransparencyState(ADDITIVE_TRANSPARENCY)
.setWriteMaskState(COLOR_WRITE)
.setCullState(NO_CULL)
.setDepthTestState(LEQUAL_DEPTH_TEST)
.createCompositeState(false)
);
}
43 changes: 30 additions & 13 deletions src/main/java/com/altnoir/mia/core/spawner/AbyssTrialSpawner.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ private boolean trySpawnMob(ServerLevel level, BlockPos pos, AbyssTrialSpawnerPa
return false;
}

var entityType = selectedEntity.get().getEntityType();
var entityInstance = selectedEntity.get();
var entityType = entityInstance.getEntityType();

int range = pattern.spawnRange();
for (int attempts = 0; attempts < 20; attempts++) {
Expand All @@ -258,6 +259,8 @@ private boolean trySpawnMob(ServerLevel level, BlockPos pos, AbyssTrialSpawnerPa
if (entity != null) {
if (entity instanceof Mob mob) {
mob.finalizeSpawn(level, level.getCurrentDifficultyAt(spawnPos), MobSpawnType.TRIAL_SPAWNER, null);

applyEntityConfiguration(mob, entityInstance);
}

level.addFreshEntity(entity);
Expand All @@ -280,6 +283,32 @@ private boolean trySpawnMob(ServerLevel level, BlockPos pos, AbyssTrialSpawnerPa
return false;
}

private void applyEntityConfiguration(Mob mob, EntityTableInstance entityInstance) {
if (entityInstance.hasEquipment()) {
for (var entry : entityInstance.getEquipment().entrySet()) {
mob.setItemSlot(entry.getKey(), entry.getValue().copy());
mob.setDropChance(entry.getKey(), 0.0F);
}
}

if (entityInstance.hasEffects()) {
for (var effect : entityInstance.getEffects()) {
mob.addEffect(new net.minecraft.world.effect.MobEffectInstance(effect));
}
}

if (entityInstance.hasAttributeModifiers()) {
for (var entry : entityInstance.getAttributeModifiers().entrySet()) {
var attributeInstance = mob.getAttribute(entry.getKey());
if (attributeInstance != null) {
attributeInstance.addPermanentModifier(entry.getValue());
}
}
// Ensure mob's health matches new max health after modifiers
mob.setHealth(mob.getMaxHealth());
}
}

private void collectRewardsForPlayer(ServerLevel level, BlockPos pos, Player player, AbyssTrialSpawnerPattern pattern) {
var selectedLoot = WeightedRandom.getRandomItem(this.random, pattern.lootTables().unwrap());
if (selectedLoot.isEmpty()) {
Expand Down Expand Up @@ -426,18 +455,6 @@ public double getOSpin() {
return this.oSpin;
}

@Nullable
public Entity getOrCreateDisplayEntity(Level level, BlockPos pos) {
if (this.displayEntity == null && hasValidPattern() && level instanceof ServerLevel serverLevel) {
var pattern = getPattern();
var selected = WeightedRandom.getRandomItem(this.random, pattern.entityTables().unwrap());
if (selected.isPresent()) {
this.displayEntity = selected.get().getEntityType().create(serverLevel, null, pos, MobSpawnType.TRIAL_SPAWNER, false, false);
}
}
return this.displayEntity;
}

public interface StateAccessor {
TrialSpawnerState getState();
void setState(Level level, TrialSpawnerState state);
Expand Down
Loading