Skip to content

Commit

Permalink
添加妖怪名单,可以控制女仆的攻击目标
Browse files Browse the repository at this point in the history
  • Loading branch information
TartaricAcid committed Sep 7, 2024
1 parent be5b244 commit 951a2f7
Show file tree
Hide file tree
Showing 15 changed files with 483 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.github.tartaricacid.touhoulittlemaid.api.task;

import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MaidConfig;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.DefaultMonsterType;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.MonsterType;
import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid;
import com.github.tartaricacid.touhoulittlemaid.init.InitItems;
import com.github.tartaricacid.touhoulittlemaid.item.ItemMonsterList;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

import java.util.Map;
import java.util.Optional;

public interface IAttackTask extends IMaidTask {
Expand All @@ -25,18 +30,6 @@ static Optional<? extends LivingEntity> findFirstValidAttackTarget(EntityMaid ma
mobs -> mobs.findClosest((e) -> maid.canAttack(e) && maid.isWithinRestriction(e.blockPosition())));
}

private static boolean checkCanAttackEntity(LivingEntity target) {
// 不能攻击玩家
if (target instanceof Player) {
return false;
}
// 有主的宠物也不攻击
if (target instanceof TamableAnimal tamableAnimal) {
return tamableAnimal.getOwnerUUID() == null;
}
return true;
}

/**
* 能否攻击该对象
*
Expand All @@ -45,25 +38,36 @@ private static boolean checkCanAttackEntity(LivingEntity target) {
* @return 能否攻击?
*/
default boolean canAttack(EntityMaid maid, LivingEntity target) {
if (maid.getOwner() instanceof Player player) {
LivingEntity lastHurtByMob = player.getLastHurtByMob();
if (target.equals(lastHurtByMob) && checkCanAttackEntity(lastHurtByMob)) {
return true;
}
LivingEntity lastHurtMob = player.getLastHurtMob();
if (target.equals(lastHurtMob) && checkCanAttackEntity(lastHurtMob)) {
return true;
}
// 获取实体 ID
ResourceLocation id = BuiltInRegistries.ENTITY_TYPE.getKey(target.getType());

// 模组自身强制指定的
if (target instanceof Player) {
return false;
}
LivingEntity maidLastHurtByMob = maid.getLastHurtByMob();
if (target.equals(maidLastHurtByMob) && checkCanAttackEntity(maidLastHurtByMob)) {
return true;
if (target instanceof TamableAnimal tamableAnimal) {
// 有主的宠物也不攻击
return tamableAnimal.getOwnerUUID() == null;
}
ResourceLocation key = BuiltInRegistries.ENTITY_TYPE.getKey(target.getType());
if (key != null && MaidConfig.MAID_ATTACK_IGNORE.get().contains(key.toString())) {

// 判断配置文件的
if (MaidConfig.MAID_ATTACK_IGNORE.get().contains(id.toString())) {
return false;
}
return target instanceof Enemy;

// 获取女仆副手是否有妖怪名单
ItemStack offhandItem = maid.getOffhandItem();
if (offhandItem.is(InitItems.MONSTER_LIST.get())) {
Map<ResourceLocation, MonsterType> monsterList = ItemMonsterList.getMonsterList(offhandItem);
if (monsterList.containsKey(id)) {
MonsterType monsterType = monsterList.get(id);
return DefaultMonsterType.canAttack(maid, target, monsterType);
}
}

// 那如果没有呢?走默认配置
MonsterType monsterType = DefaultMonsterType.getMonsterType(target.getType());
return DefaultMonsterType.canAttack(maid, target, monsterType);
}

default boolean hasExtraAttack(EntityMaid maid, Entity target) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.github.tartaricacid.touhoulittlemaid.client.gui.item;

import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.FlatColorButton;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.DefaultMonsterType;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.MonsterType;
import com.github.tartaricacid.touhoulittlemaid.network.message.SetMonsterListMessage;
import com.google.common.collect.Maps;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.network.PacketDistributor;

import java.util.List;
import java.util.Map;

public class MonsterListScreen extends Screen {
private static final int PER_MAX_COUNT = 10;

private final Map<EntityType<?>, MonsterType> monsterList;
private final List<EntityType<?>> monsterListIndex;

private int page = 0;
private int posX;
private int posY;

public MonsterListScreen(ItemStack stack) {
super(Component.literal("Monster List Screen"));
this.monsterList = DefaultMonsterType.getMonsterList(stack, Minecraft.getInstance().level);
this.monsterListIndex = this.monsterList.keySet().stream().toList();
}

@Override
protected void init() {
this.clearWidgets();

this.posX = this.width / 2;
this.posY = this.height / 2;

int startY = posY - 16 * PER_MAX_COUNT / 2 - 8;
int size = this.monsterListIndex.size();
for (int i = 0; i < PER_MAX_COUNT; i++) {
final int index = page * PER_MAX_COUNT + i;
if (index >= size) {
break;
}

EntityType<?> entityType = this.monsterListIndex.get(index);
MonsterType monsterType = this.monsterList.get(entityType);
Component text = monsterType.getComponent()
.copy()
.append(CommonComponents.SPACE)
.append(entityType.getDescription());

FlatColorButton button = new FlatColorButton(posX - 100, startY, 200, 14, text, b -> {
MonsterType next = monsterType.getNext();
this.monsterList.put(entityType, next);
this.init();
});

this.addRenderableWidget(button);
startY += 16;
}

startY = posY + 16 * PER_MAX_COUNT / 2 - 8;
this.addRenderableWidget(new FlatColorButton(posX - 100, startY, 28, 14, Component.literal("<"), b -> {
if (this.page > 0) {
this.page--;
this.init();
}
}));

this.addRenderableWidget(new FlatColorButton(posX + 100 - 28, startY, 28, 14, Component.literal(">"), b -> {
int nextStartIndex = (this.page + 1) * PER_MAX_COUNT;
if (nextStartIndex < this.monsterListIndex.size()) {
this.page++;
this.init();
}
}));
}

@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
super.render(graphics, mouseX, mouseY, partialTick);

String pageText = String.format("%d/%d", page + 1, this.monsterListIndex.size() / PER_MAX_COUNT + 1);
int startY = posY + 16 * PER_MAX_COUNT / 2 - 5;
graphics.drawCenteredString(font, pageText, this.posX, startY, 0xFFFFFF);
}

@Override
public void onClose() {
Map<ResourceLocation, MonsterType> monsterListOutput = Maps.newHashMap();
this.monsterList.forEach((type, monsterType) -> {
ResourceLocation key = BuiltInRegistries.ENTITY_TYPE.getKey(type);
monsterListOutput.put(key, monsterType);
});
PacketDistributor.sendToServer(new SetMonsterListMessage(monsterListOutput));
super.onClose();
}

@Override
public boolean isPauseScreen() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.github.tartaricacid.touhoulittlemaid.entity.misc;

import com.github.tartaricacid.touhoulittlemaid.entity.item.AbstractEntityFromItem;
import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid;
import com.github.tartaricacid.touhoulittlemaid.init.InitItems;
import com.github.tartaricacid.touhoulittlemaid.item.ItemMonsterList;
import com.google.common.collect.Maps;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.npc.Npc;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

import javax.annotation.Nullable;
import java.util.Map;

public final class DefaultMonsterType {
private final static Map<EntityType<?>, MonsterType> DEFAULT = Maps.newHashMap();

public static void initDefault(Level level) {
// 只允许初始化一次
if (!DEFAULT.isEmpty()) {
return;
}

BuiltInRegistries.ENTITY_TYPE.forEach(type -> {
Entity entity = type.create(level);
if (!(entity instanceof LivingEntity livingEntity)) {
return;
}

// 排除一些盔甲架,还有本模组的实体,以及玩家
if (livingEntity instanceof ArmorStand || livingEntity instanceof AbstractEntityFromItem || livingEntity instanceof Player) {
return;
}

// 如果继承了 Enemy 接口,那就是敌对生物
if (livingEntity instanceof Enemy) {
DEFAULT.putIfAbsent(type, MonsterType.HOSTILE);
return;
}

// 如果是玩家、宠物、NPC、归为友好
if (livingEntity instanceof TamableAnimal || livingEntity instanceof Npc) {
DEFAULT.putIfAbsent(type, MonsterType.FRIENDLY);
return;
}

// 否则归为中立
DEFAULT.putIfAbsent(type, MonsterType.NEUTRAL);
});
}

public static Map<EntityType<?>, MonsterType> getMonsterList(ItemStack stack, @Nullable Level level) {
Map<EntityType<?>, MonsterType> output = Maps.newHashMap();

if (level == null || stack.getItem() != InitItems.MONSTER_LIST.get()) {
return output;
}

// 先从物品里读取数据
Map<ResourceLocation, MonsterType> monsterList = ItemMonsterList.getMonsterList(stack);
monsterList.forEach((key, monsterType) -> {
EntityType<?> entityType = BuiltInRegistries.ENTITY_TYPE.get(key);
output.put(entityType, monsterType);
});

// 最后补齐默认数据
DEFAULT.forEach(output::putIfAbsent);

return output;
}

public static MonsterType getMonsterType(EntityType<?> entityType) {
return DEFAULT.getOrDefault(entityType, MonsterType.NEUTRAL);
}

public static boolean canAttack(EntityMaid maid, LivingEntity target, MonsterType monsterType) {
// 友好生物,无论什么情况不攻击
if (monsterType == MonsterType.FRIENDLY) {
return true;
}

// 中立生物,只有玩家攻击的,或者攻击过玩家的才会攻击
if (monsterType == MonsterType.NEUTRAL) {
return checkNeutral(maid, target);
}

// 其他的,那只有敌对了,攻击
return true;
}

private static boolean checkNeutral(EntityMaid maid, LivingEntity target) {
// 先判断主人
if (maid.getOwner() instanceof Player player) {
// 获取攻击主人的生物
LivingEntity lastHurtByMob = player.getLastHurtByMob();
if (target.equals(lastHurtByMob)) {
return true;
}
// 获取主人攻击过的生物
LivingEntity lastHurtMob = player.getLastHurtMob();
if (target.equals(lastHurtMob)) {
return true;
}
}

// 再判断女仆自身被攻击的
LivingEntity maidLastHurtByMob = maid.getLastHurtByMob();
return target.equals(maidLastHurtByMob);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.github.tartaricacid.touhoulittlemaid.entity.misc;

import com.mojang.serialization.Codec;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.util.StringRepresentable;

import java.util.Locale;

public enum MonsterType implements StringRepresentable {
FRIENDLY,
NEUTRAL,
HOSTILE;

public static final Codec<MonsterType> CODEC = StringRepresentable.fromEnum(MonsterType::values);
public static final StreamCodec<ByteBuf, MonsterType> STREAM_CODEC = StreamCodec.of(
(byteBuf, type) -> byteBuf.writeInt(type.ordinal()),
byteBuf -> getTypeByIndex(byteBuf.readInt())
);

private final MutableComponent component;

MonsterType() {
this.component = Component.translatable("gui.touhou_little_maid.monster_type." + this.name().toLowerCase(Locale.ENGLISH));
}

public static MonsterType getTypeByIndex(int index) {
int length = MonsterType.values().length;
return MonsterType.values()[Math.min(index, length - 1)];
}

public MonsterType getNext() {
int ordinal = this.ordinal();
int length = MonsterType.values().length;
return MonsterType.values()[(ordinal + 1) % length];
}

public MutableComponent getComponent() {
return component;
}

@Override
public String getSerializedName() {
return this.name().toLowerCase(Locale.ENGLISH);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.tartaricacid.touhoulittlemaid.event;

import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.DefaultMonsterType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.level.LevelEvent;

@EventBusSubscriber(modid = TouhouLittleMaid.MOD_ID)
public class LevelLoadEvent {
@SubscribeEvent
public static void onLevelLoadEvent(LevelEvent.Load event) {
LevelAccessor levelAccessor = event.getLevel();
if (levelAccessor instanceof Level level) {
DefaultMonsterType.initDefault(level);
}
}
}
Loading

0 comments on commit 951a2f7

Please sign in to comment.