Very Broken Lasers

This commit is contained in:
TheBrokenRail 2020-07-15 18:44:49 -04:00
parent 83c922dd59
commit 4983948e3b
12 changed files with 555 additions and 1 deletions

View File

@ -11,4 +11,8 @@ public class Config {
public static final int REACTOR_TIME = 2400;
public static final int REACTOR_ENERGY_OUTPUT = 250;
public static final int DEFENSIVE_LASER_RANGE = 18;
public static final int DEFENSIVE_LASER_IDLE_ENERGY_REQUIRED = 25;
public static final int DEFENSIVE_LASER_FIRE_ENERGY_REQUIRED = 64;
}

View File

@ -1,5 +1,6 @@
package com.thebrokenrail.energonrelics;
import com.thebrokenrail.energonrelics.block.DefensiveLaserBlock;
import com.thebrokenrail.energonrelics.block.battery.ActiveBatteryControllerBlock;
import com.thebrokenrail.energonrelics.block.reactor.ReactorControllerBlock;
import com.thebrokenrail.energonrelics.block.reactor.ReactorCoreBlock;
@ -58,6 +59,9 @@ public class EnergonRelics implements ModInitializer {
public static final Item CIRCUIT_BOARD_ITEM = new Item(new Item.Settings().group(ITEM_GROUP));
public static final Item DEFENSIVE_LASER_CORE_ITEM = new Item(new Item.Settings().group(ITEM_GROUP));;
public static final DefensiveLaserBlock DEFENSIVE_LASER_BLOCK = new DefensiveLaserBlock();
@Override
public void onInitialize() {
NETWORK_CHIP_ITEM = Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "network_chip"), new NetworkChipItem());
@ -82,5 +86,8 @@ public class EnergonRelics implements ModInitializer {
VERIDIUM_ORE_BLOCK.register("veridium_ore");
VERIDIUM_BLOCK_BLOCK.register("veridium_block");
Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, new Identifier(NAMESPACE, "veridium_ore"), VERIDIUM_ORE_FEATURE);
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "defensive_laser_core"), DEFENSIVE_LASER_CORE_ITEM);
DEFENSIVE_LASER_BLOCK.register("defensive_laser");
}
}

View File

@ -0,0 +1,69 @@
package com.thebrokenrail.energonrelics.block;
import com.thebrokenrail.energonrelics.block.entity.DefensiveLaserBlockEntity;
import com.thebrokenrail.energonrelics.block.util.EnergyProviderBlock;
import com.thebrokenrail.energonrelics.client.render.DefensiveLaserBlockEntityRenderer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.Direction;
import java.util.function.Function;
@SuppressWarnings("deprecation")
public class DefensiveLaserBlock extends EnergyProviderBlock {
public static final BooleanProperty POWERED = Properties.POWERED;
public static final DirectionProperty FACING = Properties.FACING;
public DefensiveLaserBlock() {
super(FabricBlockSettings.of(Material.GLASS).sounds(BlockSoundGroup.GLASS).nonOpaque().lightLevel(state -> state.get(POWERED) ? 15 : 0).strength(0.3f));
setDefaultState(getDefaultState().with(POWERED, false).with(FACING, Direction.NORTH));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(POWERED);
builder.add(FACING);
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.rotate(mirror.getRotation(state.get(FACING)));
}
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return getDefaultState().with(FACING, ctx.getSide().getOpposite());
}
@Override
protected Function<BlockEntityType<BlockEntity>, BlockEntity> getFactory() {
return DefensiveLaserBlockEntity::new;
}
@Override
@Environment(EnvType.CLIENT)
protected Function<BlockEntityRenderDispatcher, BlockEntityRenderer<BlockEntity>> getRenderer() {
return DefensiveLaserBlockEntityRenderer::new;
}
}

View File

@ -0,0 +1,354 @@
package com.thebrokenrail.energonrelics.block.entity;
import com.thebrokenrail.energonrelics.Config;
import com.thebrokenrail.energonrelics.EnergonRelics;
import com.thebrokenrail.energonrelics.block.DefensiveLaserBlock;
import com.thebrokenrail.energonrelics.energy.core.EnergyReceiverBlockEntity;
import com.thebrokenrail.energonrelics.energy.core.util.Action;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
public class DefensiveLaserBlockEntity extends EnergyReceiverBlockEntity {
private static final float MAX_PITCH = 60;
private static final float MIN_PITCH = -20;
private static final float ROTATION_INCREMENT = 2;
private static final int COUNTDOWN = 6;
public final Rotation rotation = new Rotation(0, 0, "Rotation");
private LivingEntity target;
private int countdown = 0;
private final Predicate<LivingEntity> predicate = entity -> {
if (entity.getBlockPos().isWithinDistance(getPos(), Config.DEFENSIVE_LASER_RANGE)) {
Vec3d start = getPosVec().add(rotation.getRayVector());
Vec3d end = entity.getPos();
HitResult result = entity.getEntityWorld().rayTrace(new RayTraceContext(start, end, RayTraceContext.ShapeType.COLLIDER, RayTraceContext.FluidHandling.ANY, entity));
return result.getType() == HitResult.Type.MISS;
} else {
return false;
}
};
private Vec3d getPosVec() {
return new Vec3d(getPos().getX() + 0.5d, getPos().getY() + 0.5d, getPos().getZ() + 0.5d);
}
@Override
public BlockState getCachedState() {
if (hasWorld()) {
return super.getCachedState();
} else {
return EnergonRelics.DEFENSIVE_LASER_BLOCK.getDefaultState();
}
}
private interface SwapFunction {
class SwapData {
private final float yaw;
private final float pitch;
public SwapData(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
}
Vec3d swap(SwapData data);
SwapData line(Vec3d data);
static SwapFunction upDown(boolean up) {
return new SwapFunction() {
@Override
public Vec3d swap(SwapData data) {
return new Vec3d(-data.yaw, data.pitch, 0);
}
private SwapData reverseSwap(Vec3d data) {
return new SwapData((float) -data.getX(), (float) data.getY());
}
@Override
public SwapData line(Vec3d data) {
SwapData reverse = reverseSwap(data);
return new SwapData(-reverse.yaw, reverse.pitch);
}
};
}
static SwapFunction northSouth(boolean north) {
return new SwapFunction() {
@Override
public Vec3d swap(SwapData data) {
if (north) {
return new Vec3d(0, data.pitch, data.yaw);
} else {
return new Vec3d(0, -data.pitch, data.yaw);
}
}
private SwapData reverseSwap(Vec3d data) {
if (north) {
return new SwapData((float) data.getZ(), (float) data.getY());
} else {
return new SwapData((float) data.getZ(), (float) -data.getY());
}
}
@Override
public SwapData line(Vec3d data) {
SwapData reverse = reverseSwap(data);
if (north) {
return new SwapData(reverse.pitch + 90, reverse.yaw + 90);
} else {
return new SwapData(reverse.pitch - 180, reverse.yaw - 90);
}
}
};
}
SwapFunction SWAP_PITCH_ROLL = new SwapFunction() {
@Override
public Vec3d swap(SwapData data) {
return new Vec3d(data.yaw, data.pitch, 0);
}
private SwapData reverseSwap(Vec3d data) {
return new SwapData((float) data.getX(), (float) data.getY());
}
@Override
public SwapData line(Vec3d data) {
SwapData reverse = reverseSwap(data);
return new SwapData(-reverse.pitch, reverse.yaw);
}
};
}
public class Rotation {
private float yaw = 0;
private float pitch = 0;
private final String key;
private Rotation(float yaw, float pitch, String key) {
setRaw(yaw, pitch);
this.key = key;
}
private void fromTag(CompoundTag tag) {
CompoundTag rotation = tag.getCompound(key);
setRaw(rotation.getInt("Yaw"), rotation.getInt("Pitch"));
}
private void toTag(CompoundTag tag) {
CompoundTag rotation = new CompoundTag();
rotation.putFloat("Yaw", yaw);
rotation.putFloat("Pitch", pitch);
tag.put(key, rotation);
}
private void change(float yaw, float pitch) {
setRaw(this.yaw + yaw, this.pitch + pitch);
markDirty();
}
private void setRaw(float yaw, float pitch) {
setYaw(yaw);
setPitch(pitch);
}
private void setYaw(float yaw) {
this.yaw = yaw % 360;
}
private void setPitch(float pitch) {
this.pitch = Math.max(MIN_PITCH, Math.min(MAX_PITCH, pitch % 360));
}
public float getYaw(boolean display) {
return (float) getBaseVector(display).getX();
}
public float getPitch(boolean display) {
return (float) getBaseVector(display).getY();
}
public float getRoll(boolean display) {
return (float) getBaseVector(display).getZ();
}
private Vec3d getBaseModifier() {
Direction facing = getCachedState().get(DefensiveLaserBlock.FACING);
switch (facing) {
case DOWN: {
return new Vec3d(0, 0, 0);
}
case UP: {
return new Vec3d(0, 180, 0);
}
case NORTH: {
return new Vec3d(0, 0, 90);
}
case SOUTH: {
return new Vec3d(180, 90, 90);
}
case EAST: {
return new Vec3d(180, 0, 90);
}
case WEST: {
return new Vec3d(0, 0, -90);
}
default: {
throw new UnsupportedOperationException();
}
}
}
public SwapFunction getSwapFunction() {
Direction facing = getCachedState().get(DefensiveLaserBlock.FACING);
switch (facing) {
case DOWN: {
return SwapFunction.upDown(false);
}
case UP: {
return SwapFunction.upDown(true);
}
case EAST:
case WEST: {
return SwapFunction.SWAP_PITCH_ROLL;
}
case NORTH: {
return SwapFunction.northSouth(true);
}
case SOUTH: {
return SwapFunction.northSouth(false);
}
default: {
throw new UnsupportedOperationException();
}
}
}
private Vec3d getBaseVector(boolean display) {
Vec3d modifier = getBaseModifier();
return getSwapFunction().swap(new SwapFunction.SwapData(yaw, -(pitch - (display ? 45 : 0)))).add(modifier);
}
private Vec3d getRayVector() {
SwapFunction.SwapData data = getSwapFunction().line(getBaseVector(false));
float pitch = data.pitch * (float) DEG2RAD;
float yaw = data.yaw * (float) DEG2RAD;
float cosYaw = MathHelper.cos(yaw);
float sinYaw = MathHelper.sin(yaw);
float cosPitch = MathHelper.cos(pitch);
float sinPitch = MathHelper.sin(pitch);
return new Vec3d(sinYaw * cosPitch, -sinPitch, cosYaw * cosPitch);
}
private float getAngleChange(float from, float to) {
float diff = MathHelper.subtractAngles(from, to);
return MathHelper.clamp(diff, -ROTATION_INCREMENT, ROTATION_INCREMENT);
}
private void target(float targetYaw, float targetPitch) {
SwapFunction function = getSwapFunction();
SwapFunction.SwapData data = function.line(getBaseVector(false));
Vec3d vec = new Vec3d(targetYaw, targetPitch, 0);
float yawChange = getAngleChange(data.yaw, (float) vec.getX());
float pitchChange = getAngleChange(data.pitch, (float) vec.getY());
change(yawChange, pitchChange);
}
}
public DefensiveLaserBlockEntity(BlockEntityType<?> type) {
super(type);
}
public static final double DEG2RAD = Math.PI / 180d;
private static final double RAD2DEG = 180d / Math.PI;
private float getTargetPitch(double x, double y, double z) {
Vec3d pos = getPosVec();
double diffX = x - pos.getX();
double diffY = y - pos.getY();
double diffZ = z - pos.getZ();
double g = MathHelper.sqrt(diffX * diffX + diffZ * diffZ);
return (float) -(MathHelper.atan2(diffY, g) * RAD2DEG);
}
private float getTargetYaw(double x, double z) {
Vec3d pos = getPosVec();
double diffX = x - pos.getX();
double diffZ = z - pos.getZ();
return (float) (MathHelper.atan2(diffZ, diffX) * RAD2DEG) - 90.0F;
}
@Override
protected void tickEnergy() {
assert getWorld() != null;
Vec3d vec = rotation.getRayVector().add(getPosVec());
((ServerWorld) getWorld()).spawnParticles(ParticleTypes.END_ROD, vec.getX(), vec.getY(), vec.getZ(), 1, 0, 0, 0, 0);
addAction(Action.createBlockStatePropertyAction(Config.DEFENSIVE_LASER_IDLE_ENERGY_REQUIRED, DefensiveLaserBlock.POWERED, true, false));
rotation.target(45, 45);
if (getCachedState().get(DefensiveLaserBlock.POWERED)) {
if (countdown > 0) {
countdown--;
}
if (countdown == 1) {
// Fire!
target = null;
} else if (countdown < 1) {
if (target == null) {
List<LivingEntity> entities = getWorld().getEntities(LivingEntity.class, new Box(getPos()).expand(Config.DEFENSIVE_LASER_RANGE), predicate);
if (entities.size() > 0) {
Collections.shuffle(entities);
target = entities.get(0);
}
} else {
if (predicate.test(target)) {
Optional<Vec3d> optional = target.getBoundingBox().rayTrace(getPosVec(), getPosVec().add(rotation.getRayVector().multiply(Config.DEFENSIVE_LASER_RANGE)));
if (optional.isPresent()) {
countdown = COUNTDOWN;
} else {
Vec3d targetPos = target.getPos();
float targetYaw = getTargetYaw(targetPos.getX(), targetPos.getZ());
float targetPitch = getTargetPitch(targetPos.getX(), targetPos.getY(), targetPos.getZ());
rotation.target(targetYaw, targetPitch);
}
} else {
target = null;
}
}
}
}
}
@Override
public CompoundTag toTag(CompoundTag tag) {
super.toTag(tag);
rotation.toTag(tag);
tag.putInt("Countdown", countdown);
return tag;
}
@Override
public void fromTag(BlockState state, CompoundTag tag) {
super.fromTag(state, tag);
rotation.fromTag(tag);
countdown = tag.getInt("Countdown");
}
}

View File

@ -0,0 +1,45 @@
package com.thebrokenrail.energonrelics.client.render;
import com.thebrokenrail.energonrelics.EnergonRelics;
import com.thebrokenrail.energonrelics.block.entity.DefensiveLaserBlockEntity;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack;
public class DefensiveLaserBlockEntityRenderer extends HighlightBlockEntityRenderer {
public DefensiveLaserBlockEntityRenderer(BlockEntityRenderDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void render(BlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
matrices.push();
matrices.translate(0.5d, 0.5d, 0.5d);
if (entity instanceof DefensiveLaserBlockEntity) {
DefensiveLaserBlockEntity laser = (DefensiveLaserBlockEntity) entity;
float roll = (float) (laser.rotation.getRoll(true) * DefensiveLaserBlockEntity.DEG2RAD);
if (roll != 0) {
matrices.multiply(Vector3f.POSITIVE_Z.getRadialQuaternion(roll));
}
float yaw = (float) (laser.rotation.getYaw(true) * DefensiveLaserBlockEntity.DEG2RAD);
if (yaw != 0) {
matrices.multiply(Vector3f.POSITIVE_Y.getRadialQuaternion(yaw));
}
float pitch = (float) (laser.rotation.getPitch(true) * DefensiveLaserBlockEntity.DEG2RAD);
if (pitch != 0) {
matrices.multiply(Vector3f.POSITIVE_X.getRadialQuaternion(pitch));
}
matrices.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(90));
MinecraftClient.getInstance().getItemRenderer().renderItem(new ItemStack(EnergonRelics.DEFENSIVE_LASER_CORE_ITEM), ModelTransformation.Mode.GROUND, light, overlay, matrices, vertexConsumers);
}
matrices.pop();
super.render(entity, tickDelta, matrices, vertexConsumers, light, overlay);
}
}

View File

@ -0,0 +1,31 @@
{
"variants": {
"facing=down": {
"model": "energonrelics:block/defensive_laser"
},
"facing=east": {
"model": "energonrelics:block/defensive_laser",
"x": 90,
"y": 270
},
"facing=north": {
"model": "energonrelics:block/defensive_laser",
"x": 90,
"y": 180
},
"facing=south": {
"model": "energonrelics:block/defensive_laser",
"y": 180,
"x": 270
},
"facing=up": {
"model": "energonrelics:block/defensive_laser",
"x": 180
},
"facing=west": {
"model": "energonrelics:block/defensive_laser",
"y": 90,
"x": 90
}
}
}

View File

@ -18,5 +18,7 @@
"block.energonrelics.reactor_core": "Reactor Core",
"item.energonrelics.veridium_ingot": "Veridium Ingot",
"block.energonrelics.veridium_ore": "Veridium Ore",
"block.energonrelics.veridium_block": "Veridium Block"
"block.energonrelics.veridium_block": "Veridium Block",
"item.energonrelics.defensive_laser_core": "Defensive laser Core",
"block.energonrelics.defensive_laser": "Defensive Laser"
}

View File

@ -0,0 +1,33 @@
{
"parent": "minecraft:block/block",
"textures": {
"0": "energonrelics:block/defensive_laser_base",
"particle": "energonrelics:block/defensive_laser_base"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 4, 16],
"faces": {
"north": {"uv": [0, 0, 16, 4], "texture": "#0"},
"east": {"uv": [0, 0, 16, 4], "texture": "#0"},
"south": {"uv": [0, 0, 16, 4], "texture": "#0"},
"west": {"uv": [0, 0, 16, 4], "texture": "#0"},
"up": {"uv": [0, 0, 16, 16], "texture": "#0"},
"down": {"uv": [0, 0, 16, 16], "texture": "#0"}
}
},
{
"from": [7, 4, 7],
"to": [9, 8, 9],
"faces": {
"north": {"uv": [7, 6, 9, 10], "texture": "#0"},
"east": {"uv": [7, 6, 9, 10], "texture": "#0"},
"south": {"uv": [7, 6, 9, 10], "texture": "#0"},
"west": {"uv": [7, 6, 9, 10], "texture": "#0"},
"up": {"uv": [7, 7, 9, 9], "texture": "#0"},
"down": {"uv": [7, 7, 9, 9], "texture": "#0"}
}
}
]
}

View File

@ -0,0 +1,3 @@
{
"parent": "minecraft:builtin/entity"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "energonrelics:item/defensive_laser_core"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B