Skip to content

Commit

Permalink
应用女仆对栅栏门的交互 (#567)
Browse files Browse the repository at this point in the history
* 完成栅栏门的交互

* 应用门的交互(记忆)机制到栅栏门上

* 简化栅栏门的设计

---------

Co-authored-by: tartaric_acid <[email protected]>
  • Loading branch information
Azumic and TartaricAcid authored Sep 8, 2024
1 parent d8fda99 commit aed8342
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task;

import com.github.tartaricacid.touhoulittlemaid.mixin.FenceGateBlockAccessor;
import com.google.common.collect.Sets;
import com.mojang.datafixers.kinds.OptionalBox;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.behavior.BehaviorControl;
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder;
import net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.Path;
import org.apache.commons.lang3.mutable.MutableInt;
Expand All @@ -34,24 +42,33 @@ public static BehaviorControl<LivingEntity> create() {
instance.registered(MemoryModuleType.NEAREST_LIVING_ENTITIES)).apply(instance, (pathMemory, doorToCloseMemory, livingEntityMemory) -> (serverLevel, entity, time) -> {
Path path = instance.get(pathMemory);
Optional<Set<GlobalPos>> doorToClosePos = instance.tryGet(doorToCloseMemory);

if (!path.notStarted() && !path.isDone()) {
if (Objects.equals(mutableObject.getValue(), path.getNextNode())) {
mutableInt.setValue(COOLDOWN_BEFORE_RERUNNING_IN_SAME_NODE);
} else if (mutableInt.decrementAndGet() > 0) {
return false;
}

mutableObject.setValue(path.getNextNode());
Node previousNode = path.getPreviousNode();
Node nextNode = path.getNextNode();
BlockPos previousNodeBlockPos = previousNode.asBlockPos();
BlockState previousNodeBlockState = serverLevel.getBlockState(previousNodeBlockPos);

if (previousNodeBlockState.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
DoorBlock doorBlock = (DoorBlock) previousNodeBlockState.getBlock();
if (!doorBlock.isOpen(previousNodeBlockState)) {
doorBlock.setOpen(entity, serverLevel, previousNodeBlockState, previousNodeBlockPos, true);
}
doorToClosePos = rememberDoorToClose(doorToCloseMemory, doorToClosePos, serverLevel, previousNodeBlockPos);
} else if (previousNodeBlockState.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock)) {
if (!previousNodeBlockState.getValue(FenceGateBlock.OPEN)) {
setFenceGate(entity, serverLevel, previousNodeBlockState, previousNodeBlockPos, true);
}
doorToClosePos = rememberDoorToClose(doorToCloseMemory, doorToClosePos, serverLevel, previousNodeBlockPos);
}

BlockPos nextNodeBlockPos = nextNode.asBlockPos();
BlockState nextNodeBlockState = serverLevel.getBlockState(nextNodeBlockPos);
if (nextNodeBlockState.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
Expand All @@ -60,7 +77,13 @@ public static BehaviorControl<LivingEntity> create() {
doorBlock.setOpen(entity, serverLevel, nextNodeBlockState, nextNodeBlockPos, true);
doorToClosePos = rememberDoorToClose(doorToCloseMemory, doorToClosePos, serverLevel, nextNodeBlockPos);
}
} else if (nextNodeBlockState.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock)) {
if (!nextNodeBlockState.getValue(FenceGateBlock.OPEN)) {
setFenceGate(entity, serverLevel, nextNodeBlockState, nextNodeBlockPos, true);
doorToClosePos = rememberDoorToClose(doorToCloseMemory, doorToClosePos, serverLevel, nextNodeBlockPos);
}
}

doorToClosePos.ifPresent((doorPos) -> closeDoorsThatIHaveOpenedOrPassedThrough(serverLevel, entity, previousNode, nextNode, doorPos, instance.tryGet(livingEntityMemory)));
return true;
} else {
Expand All @@ -75,14 +98,13 @@ public static void closeDoorsThatIHaveOpenedOrPassedThrough(ServerLevel serverLe
GlobalPos globalPos = doorPosIterator.next();
BlockPos blockPos = globalPos.pos();
if ((previous == null || !previous.asBlockPos().equals(blockPos) || (next != null && entity.blockPosition().equals(next.asBlockPos())))
&& (next == null || !next.asBlockPos().equals(blockPos))) {
&& (next == null || !next.asBlockPos().equals(blockPos))) {
if (isDoorTooFarAway(serverLevel, entity, globalPos)) {
doorPosIterator.remove();
} else {
BlockState blockstate = serverLevel.getBlockState(blockPos);
if (!blockstate.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
doorPosIterator.remove();
} else {

if (blockstate.is(BlockTags.WOODEN_DOORS, (stateBase) -> stateBase.getBlock() instanceof DoorBlock)) {
DoorBlock doorblock = (DoorBlock) blockstate.getBlock();
if (!doorblock.isOpen(blockstate)) {
doorPosIterator.remove();
Expand All @@ -92,6 +114,16 @@ public static void closeDoorsThatIHaveOpenedOrPassedThrough(ServerLevel serverLe
doorblock.setOpen(entity, serverLevel, blockstate, blockPos, false);
doorPosIterator.remove();
}
} else if ((blockstate.is(BlockTags.FENCE_GATES, (stateBase) -> stateBase.getBlock() instanceof FenceGateBlock))) {
if (!blockstate.getValue(FenceGateBlock.OPEN)) {
doorPosIterator.remove();
} else if (areOtherMobsComingThroughDoor(entity, blockPos, nearestLivingEntities)) {
doorPosIterator.remove();
} else {
setFenceGate(entity, serverLevel, blockstate, blockPos, false);
}
} else {
doorPosIterator.remove();
}
}
}
Expand Down Expand Up @@ -140,4 +172,14 @@ private static Optional<Set<GlobalPos>> rememberDoorToClose(MemoryAccessor<Optio
return posSet;
}));
}

private static void setFenceGate(@Nullable Entity entity, Level serverLevel, BlockState blockstate, BlockPos blockPos, boolean isOpen) {
serverLevel.setBlock(blockPos, blockstate.setValue(FenceGateBlock.OPEN, isOpen), Block.UPDATE_CLIENTS | Block.UPDATE_IMMEDIATE);
if (blockstate.getBlock() instanceof FenceGateBlockAccessor fenceGateBlock) {
SoundEvent openSound = fenceGateBlock.tlmOpenSound();
SoundEvent closeSound = fenceGateBlock.tlmCloseSound();
serverLevel.playSound(entity, blockPos, isOpen ? openSound : closeSound, SoundSource.BLOCKS, 1.0F, serverLevel.getRandom().nextFloat() * 0.1F + 0.9F);
}
serverLevel.gameEvent(entity, isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, blockPos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;

/**
* 该方法仅修改了栅栏门的寻路判断
*/
public class MaidNodeEvaluator extends WalkNodeEvaluator {
@Override
public BlockPathTypes getBlockPathType(BlockGetter level, int pX, int pY, int pZ) {
return getMaidBlockPathTypeStatic(level, new BlockPos.MutableBlockPos(pX, pY, pZ));
}

private static BlockPathTypes getMaidBlockPathTypeStatic(BlockGetter level, BlockPos.MutableBlockPos pos) {
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();

BlockPathTypes type = getMaidBlockPathTypeRaw(level, pos);
if (type == BlockPathTypes.OPEN && y >= level.getMinBuildHeight() + 1) {
BlockPathTypes typeBelow = getMaidBlockPathTypeRaw(level, pos.set(x, y - 1, z));

type = typeBelow != BlockPathTypes.WALKABLE
&& typeBelow != BlockPathTypes.OPEN
&& typeBelow != BlockPathTypes.WATER
&& typeBelow != BlockPathTypes.LAVA ? BlockPathTypes.WALKABLE : BlockPathTypes.OPEN;

if (typeBelow == BlockPathTypes.DAMAGE_FIRE) {
type = BlockPathTypes.DAMAGE_FIRE;
}

if (typeBelow == BlockPathTypes.DAMAGE_OTHER) {
type = BlockPathTypes.DAMAGE_OTHER;
}

if (typeBelow == BlockPathTypes.STICKY_HONEY) {
type = BlockPathTypes.STICKY_HONEY;
}

if (typeBelow == BlockPathTypes.POWDER_SNOW) {
type = BlockPathTypes.DANGER_POWDER_SNOW;
}

if (typeBelow == BlockPathTypes.DAMAGE_CAUTIOUS) {
type = BlockPathTypes.DAMAGE_CAUTIOUS;
}
}

if (type == BlockPathTypes.WALKABLE) {
type = checkNeighbourBlocks(level, pos.set(x, y, z), type);
}

return type;
}

private static BlockPathTypes getMaidBlockPathTypeRaw(BlockGetter level, BlockPos pos) {
BlockState blockState = level.getBlockState(pos);
BlockPathTypes pathType = blockState.getBlockPathType(level, pos, null);
if (pathType != null) {
return pathType;
} else if (blockState.isAir()) {
return BlockPathTypes.OPEN;
} else if (blockState.getBlock() instanceof FenceGateBlock) {
return blockState.getValue(FenceGateBlock.OPEN) ? BlockPathTypes.DOOR_OPEN : BlockPathTypes.DOOR_WOOD_CLOSED;
} else {
return WalkNodeEvaluator.getBlockPathTypeRaw(level, pos);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.PathFinder;

public class MaidPathNavigation extends GroundPathNavigation {
public MaidPathNavigation(Mob mob, Level level) {
super(mob, level);
this.mob.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
}

@Override
protected PathFinder createPathFinder(int range) {
this.nodeEvaluator = new MaidNodeEvaluator();
this.nodeEvaluator.setCanOpenDoors(true);
this.nodeEvaluator.setCanPassDoors(true);
this.nodeEvaluator.setCanFloat(true);
return new PathFinder(this.nodeEvaluator, range);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MaidConfig;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidBrain;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidSchedule;
import com.github.tartaricacid.touhoulittlemaid.entity.ai.navigation.MaidPathNavigation;
import com.github.tartaricacid.touhoulittlemaid.entity.backpack.*;
import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatBubbleManger;
import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatText;
Expand Down Expand Up @@ -81,7 +82,7 @@
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.monster.CrossbowAttackMob;
import net.minecraft.world.entity.player.Player;
Expand All @@ -102,7 +103,6 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
Expand Down Expand Up @@ -204,9 +204,6 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob, IMai

protected EntityMaid(EntityType<EntityMaid> type, Level world) {
super(type, world);
((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
this.getNavigation().setCanFloat(true);
this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
this.favorabilityManager = new FavorabilityManager(this);
this.scriptBookManager = new MaidScriptBookManager();
this.schedulePos = new SchedulePos(BlockPos.ZERO, world.dimension().location());
Expand Down Expand Up @@ -255,6 +252,11 @@ protected void defineSynchedData() {
this.entityData.define(GAME_SKILL, new CompoundTag());
}

@Override
protected PathNavigation createNavigation(Level levelIn) {
return new MaidPathNavigation(this, levelIn);
}

@Override
@SuppressWarnings("all")
public Brain<EntityMaid> getBrain() {
Expand Down Expand Up @@ -1591,7 +1593,7 @@ public void setEntityInvulnerable(boolean isInvulnerable) {
this.entityData.set(DATA_INVULNERABLE, isInvulnerable);
}


@Override
public IMaidTask getTask() {
ResourceLocation uid = new ResourceLocation(entityData.get(DATA_TASK));
return TaskManager.findTask(uid).orElse(TaskManager.getIdleTask());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.github.tartaricacid.touhoulittlemaid.mixin;

import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.block.FenceGateBlock;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

/**
* 因为某些奇怪的原因,FenceGateBlock 类下的 openSound 和 closeSound 没有对应的 SRG 名;
* 只能用 mixin 进行访问
*/
@Mixin(FenceGateBlock.class)
public interface FenceGateBlockAccessor {
@Final
@Accessor("openSound")
SoundEvent tlmOpenSound();

@Final
@Accessor("closeSound")
SoundEvent tlmCloseSound();
}
6 changes: 1 addition & 5 deletions src/main/resources/touhou_little_maid.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
"package": "com.github.tartaricacid.touhoulittlemaid.mixin",
"compatibilityLevel": "JAVA_17",
"refmap": "touhou_little_maid.refmap.json",
"mixins": [
"EntityMixin",
"MixinArrowEntity",
"MixinStructureTemplate"
],
"mixins": ["EntityMixin", "FenceGateBlockAccessor", "MixinArrowEntity", "MixinStructureTemplate"],
"client": [
"client.HumanoidModelMixin",
"client.LanguageMixin",
Expand Down

0 comments on commit aed8342

Please sign in to comment.