Skip to content

Commit

Permalink
Rework mother tree structure code.
Browse files Browse the repository at this point in the history
Remove versatile connected block and replace with version specifically tailored to the mother
  • Loading branch information
maxanier committed Jul 30, 2023
1 parent cfc589b commit 8d884c0
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 326 deletions.
177 changes: 116 additions & 61 deletions src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package de.teamlapen.vampirism.blockentity;

import de.teamlapen.vampirism.VampirismMod;
import de.teamlapen.vampirism.blocks.DarkSpruceLogs;
import de.teamlapen.vampirism.blocks.connected.ConnectedBlock;
import de.teamlapen.vampirism.blocks.mother.IRemainsBlock;
import de.teamlapen.vampirism.blocks.mother.MotherBlock;
import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure;
import de.teamlapen.vampirism.core.ModParticles;
import de.teamlapen.vampirism.core.ModSounds;
import de.teamlapen.vampirism.core.ModTiles;
import de.teamlapen.vampirism.network.ClientboundPlayEventPacket;
import de.teamlapen.vampirism.particle.FlyingBloodParticleOptions;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
Expand All @@ -26,77 +24,111 @@
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;

import java.util.List;
import java.util.Optional;
import java.util.Set;


public class MotherBlockEntity extends BlockEntity {

private final ServerBossEvent bossEvent = new ServerBossEvent(Component.translatable("block.vampirism.mother"), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
private boolean canBeDestroyed = false;

private boolean isFrozen = false;
private int freezeTimer = 0;
private final ConnectedBlock.Connector<DarkSpruceLogs> connector = new ConnectedBlock.Connector<>(DarkSpruceLogs.class);
/**
* Indicate whether a mother block is loaded in the world.
* Should be acceptably accurate as there is only every one mother nearby. But don't use for anything important for gameplay
*/
public static boolean IS_A_MOTHER_LOADED_UNRELIABLE = false;

public MotherBlockEntity(BlockPos pos, BlockState state) {
super(ModTiles.MOTHER.get(), pos, state);
bossEvent.setProgress(1);
bossEvent.setPlayBossMusic(true);
}

public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, MotherBlockEntity e) {

IS_A_MOTHER_LOADED_UNRELIABLE = true;
//Handle fight ---------------------------------
if (e.isFrozen && e.freezeTimer-- <= 0) {
e.unFreezeFight(level, blockPos, blockState);
}
if (!e.isFrozen && e.level != null) {
if (e.level.getRandom().nextInt(50) == 0) {
e.updateFightStatus();
if (!e.bossEvent.getPlayers().isEmpty()) {
List<Pair<BlockPos, BlockState>> vul = ((IRemainsBlock) e.getBlockState().getBlock()).getConnector().getVulnerabilities(level, blockPos).filter(state -> ((IRemainsBlock) (state.getRight().getBlock())).isVulnerable(state.getRight())).toList();
if (!vul.isEmpty()) {

List<Triple<BlockPos, BlockState, IRemainsBlock>> vuls = e.getTreeStructure(false).getVerifiedVulnerabilities(level).filter(t -> t.getRight().isVulnerable(t.getMiddle())).toList();
if (!vuls.isEmpty()) {
for (ServerPlayer player : e.bossEvent.getPlayers()) {
BlockPos p = vul.get(e.level.getRandom().nextInt(vul.size())).getLeft();
BlockPos p = vuls.get(e.level.getRandom().nextInt(vuls.size())).getLeft();
ModParticles.spawnParticlesServer(player.level(), new FlyingBloodParticleOptions(100, false, p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5), player.getX(), player.getY() + player.getEyeHeight() / 2, player.getZ(), 10, 0.1f, 0.1f, 0.1f, 0);
}
}
}

}
}
//Handle destruction --------------------------------
if (e.level != null && e.destructionTimer > 0) {
if (e.destructionTimer++ % 3 == 0) {
MotherTreeStructure structure = e.getTreeStructure(false);
Optional<Set<BlockPos>> hierarchy = structure.popHierarchy();
if (hierarchy.isPresent()) {
for (BlockPos p : hierarchy.get()) {
if (level.getBlockState(p).getBlock() instanceof IRemainsBlock) {
level.setBlock(p, Blocks.AIR.defaultBlockState(), 3);
ModParticles.spawnParticlesServer(level, new DustParticleOptions(new Vector3f(0.7f, 0.7f, 0.7f), 1), p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5f, 20, 0.3, 0.3, 0.3, 0.01);
}
}
} else {
//Destruction complete
e.destructionTimer = -1;
}
}
}
}

public void updateFightStatus() {
List<Pair<BlockPos, BlockState>> vulnerabilities = ((IRemainsBlock) this.getBlockState().getBlock()).getConnector().getVulnerabilities(this.level, this.worldPosition).toList();
long remainingVulnerabilities = vulnerabilities.stream().filter(state -> ((IRemainsBlock) (state.getRight().getBlock())).isVulnerable(state.getRight())).count();
if (remainingVulnerabilities > 0) {
this.bossEvent.setProgress(remainingVulnerabilities / (float) vulnerabilities.size());
} else {
this.bossEvent.setProgress(0);
this.endFight();
}

//Destruction
/**
* Caches structure. Should be mostly valid because blocks cannot be destroyed by survival player. Is (unreliably) invalidated when a block is destroyed nonetheless.
*/
private MotherTreeStructure cachedStructure;

/**
* Indicate whether a mother block is loaded in the world.
* Should be acceptably accurate as there is only every one mother nearby. But don't use for anything important for gameplay
*/
public static boolean IS_A_MOTHER_LOADED_UNRELIABLE = false;

public MotherBlockEntity(BlockPos pos, BlockState state) {
super(ModTiles.MOTHER.get(), pos, state);
bossEvent.setProgress(1);
bossEvent.setPlayBossMusic(true);
}
/**
* Describes destruction process
* == 0: Intact
* > 0: Increasing, destruction in progress
* == -1: Structure, destroyed, mother is vulnerable
*/
private int destructionTimer = 0;

private void endFight() {
this.bossEvent.getPlayers().forEach(p -> VampirismMod.dispatcher.sendTo(new ClientboundPlayEventPacket(2, getBlockPos(), 0), p));
this.bossEvent.removeAllPlayers();
this.canBeDestroyed = true;
this.connector.foreachFacing(this.level, this.worldPosition, (level, pos, state) -> {
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
ModParticles.spawnParticlesServer(level, ParticleTypes.POOF, pos.getX(), pos.getY(), pos.getZ(), 5, 0.5, 0.5, 0.5, 0.05);
});
this.setChanged();
if (this.level != null) {
this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3);
this.level.playSound(null, worldPosition, ModSounds.MOTHER_DEATH.get(), SoundSource.BLOCKS, 1f, 1f);
}
public boolean isCanBeBroken() {
return this.destructionTimer == -1;
}

public boolean isIntact() {
return destructionTimer == 0;
}

@Override
public void load(@NotNull CompoundTag tag) {
super.load(tag);
this.destructionTimer = tag.getInt("destruction_timer");
}

/**
* Call this when a block that belongs to this structure is removed
*/
public void onStructureBlockRemoved() {
this.cachedStructure = null;
}

@Override
Expand All @@ -123,47 +155,70 @@ private void addPlayer(Player player) {
return saveWithoutMetadata();
}


@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}

public void onVulnerabilityHit(ServerPlayer p, boolean destroyed) {
addPlayer(p);
updateFightStatus();
if (destroyed && isIntact()) {
freezeFight();
}
}

public void updateFightStatus() {
List<Triple<BlockPos, BlockState, IRemainsBlock>> vuls = getTreeStructure(false).getVerifiedVulnerabilities(level).toList();
long remainingVulnerabilities = vuls.stream().filter(vul -> vul.getRight().isVulnerable(vul.getMiddle())).count();
if (remainingVulnerabilities > 0) {
this.bossEvent.setProgress(remainingVulnerabilities / (float) vuls.size());
} else {
this.bossEvent.setProgress(0);
this.endFight();
}
}

@Override
protected void saveAdditional(@NotNull CompoundTag tag) {
super.saveAdditional(tag);
tag.putBoolean("isVulnerable", this.canBeDestroyed);
tag.putInt("destruction_timer", this.destructionTimer);
}

@Override
public void load(@NotNull CompoundTag tag) {
super.load(tag);
this.canBeDestroyed = tag.getBoolean("isVulnerable");
private void endFight() {
this.bossEvent.getPlayers().forEach(p -> VampirismMod.dispatcher.sendTo(new ClientboundPlayEventPacket(2, getBlockPos(), 0), p));
this.bossEvent.removeAllPlayers();
this.initiateDestruction();
}


public boolean isCanBeDestroyed() {
return this.canBeDestroyed;
private void freezeFight() {
this.isFrozen = true;
getTreeStructure(false).getVerifiedVulnerabilities(this.level).forEach(vul -> vul.getRight().freeze(level, vul.getLeft(), vul.getMiddle()));
this.bossEvent.setColor(BossEvent.BossBarColor.WHITE);
}

public void onVulnerabilityHit(ServerPlayer p, boolean destroyed) {
addPlayer(p);
updateFightStatus();
if (destroyed && !this.canBeDestroyed) {
freezeFight();
@NotNull
private MotherTreeStructure getTreeStructure(boolean forceRefresh) {
if (forceRefresh || this.cachedStructure == null) {
this.cachedStructure = MotherTreeStructure.getTreeView(this.level, this.worldPosition);
}
return cachedStructure;
}

private void freezeFight() {
this.isFrozen = true;
((MotherBlock) getBlockState().getBlock()).getConnector().foreach(this.level, this.worldPosition, (level, pos, state) -> ((IRemainsBlock) state.getBlock()).freeze(level, pos, state));
this.bossEvent.setColor(BossEvent.BossBarColor.WHITE);
private void initiateDestruction() {
this.getTreeStructure(true);
this.destructionTimer = 1;
if (this.level != null) {
this.level.playSound(null, worldPosition, ModSounds.MOTHER_DEATH.get(), SoundSource.BLOCKS, 1f, 1f);
}
}

private void unFreezeFight(Level level, BlockPos blockPos, BlockState blockState) {
this.isFrozen = false;
this.freezeTimer = 20 * 10;
((MotherBlock) getBlockState().getBlock()).getConnector().foreach(level, blockPos, (level1, pos, state) -> ((IRemainsBlock) state.getBlock()).unFreeze(level1, pos, state));
getTreeStructure(false).getVerifiedVulnerabilities(this.level).forEach(vul -> vul.getRight().unFreeze(level, vul.getLeft(), vul.getMiddle()));
this.bossEvent.setColor(BossEvent.BossBarColor.RED);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.teamlapen.vampirism.blockentity;

import de.teamlapen.vampirism.blocks.mother.ActiveVulnerableRemainsBlock;
import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure;
import de.teamlapen.vampirism.core.ModBlocks;
import de.teamlapen.vampirism.core.ModEntities;
import de.teamlapen.vampirism.core.ModSounds;
Expand Down Expand Up @@ -31,30 +31,23 @@ public VulnerableRemainsBlockEntity(BlockPos pos, BlockState state) {
}


private Optional<MotherBlockEntity> getMother() {
if (motherPos == null) {
((ActiveVulnerableRemainsBlock) getBlockState().getBlock()).getConnector().getMother(this.level, this.worldPosition).ifPresentOrElse(pos -> {
motherPos = pos.getKey();
}, () -> {
this.level.setBlockAndUpdate(this.worldPosition, ModBlocks.REMAINS.get().defaultBlockState());
public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, VulnerableRemainsBlockEntity e) {
if (e.firstTick) {
e.firstTick = false;
e.getMother().ifPresent(mother -> {
mother.updateFightStatus();
e.checkDummyEntity();
});
} else if (level.getRandom().nextInt(100) == 3) {
e.checkDummyEntity();
}
return Optional.ofNullable(motherPos).map(pos -> {
var blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof MotherBlockEntity mother) {
return mother;
}
return null;
});
}

private void destroyVulnerability() {
this.level.setBlockAndUpdate(this.worldPosition, ModBlocks.INCAPACITATED_VULNERABLE_REMAINS.get().defaultBlockState());
}

public void attacked(@NotNull BlockState state, @NotNull ServerPlayer player) {

}

public void onDamageDealt(DamageSource src, double damage) {
this.health -= damage;
Expand Down Expand Up @@ -109,17 +102,11 @@ private VulnerableRemainsDummyEntity checkDummyEntity() {

private boolean firstTick = true;

public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, VulnerableRemainsBlockEntity e) {
if (e.firstTick) {
e.firstTick = false;
Optional<MotherBlockEntity> motherOpt = e.getMother();
//motherOpt.ifPresent(MotherBlockEntity::updateFightStatus);
motherOpt.ifPresent(mother -> {
mother.updateFightStatus();
Entity e2 = e.checkDummyEntity();
//level.getNearbyPlayers(TargetingConditions.DEFAULT, e2, AABB.ofSize(blockPos.getCenter(), 5, 5, 5)).forEach(mother::addPlayer);
});
private Optional<MotherBlockEntity> getMother() {
if (this.level != null) {
return MotherTreeStructure.findMother(this.level, this.getBlockPos()).map(p -> this.level.getBlockEntity(p.getKey())).filter(e -> e instanceof MotherBlockEntity).map(e -> (MotherBlockEntity) e);
}
return Optional.empty();
}

}
Loading

0 comments on commit 8d884c0

Please sign in to comment.