Skip to content

Commit

Permalink
添加新的女仆攻击列表设置功能
Browse files Browse the repository at this point in the history
- 修改 SynchedEntityData 定义,避免可能导致的问题(实体丢失)
  • Loading branch information
TartaricAcid committed Oct 28, 2024
1 parent b494edd commit 844dc9f
Show file tree
Hide file tree
Showing 20 changed files with 433 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package com.github.tartaricacid.touhoulittlemaid.api.task;

import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MaidConfig;
import com.github.tartaricacid.touhoulittlemaid.entity.data.inner.AttackListData;
import com.github.tartaricacid.touhoulittlemaid.entity.item.AbstractEntityFromItem;
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.InitTaskData;
import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer;
import com.github.tartaricacid.touhoulittlemaid.inventory.container.task.AttackTaskConfigContainer;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.MenuProvider;
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.decoration.ArmorStand;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;

import java.util.Optional;
Expand Down Expand Up @@ -53,12 +60,32 @@ default boolean canAttack(EntityMaid maid, LivingEntity target) {
return false;
}

// TODO 获取女仆 Task Data
MonsterType monsterType;
AttackListData attackListData = maid.getData(InitTaskData.ATTACK_LIST);
if (attackListData != null && attackListData.attackGroups().containsKey(id)) {
// 获取女仆 Task Data 里设置的
monsterType = attackListData.attackGroups().get(id);
} else {
// 那如果没有呢?走默认配置
monsterType = DefaultMonsterType.getMonsterType(target);
}
return DefaultMonsterType.canAttack(maid, target, monsterType);
}

@Override
default MenuProvider getTaskConfigGuiProvider(EntityMaid maid) {
final int entityId = maid.getId();
return new MenuProvider() {
@Override
public Component getDisplayName() {
return Component.literal("Maid Attack Config Container");
}

// 那如果没有呢?走默认配置
MonsterType monsterType = DefaultMonsterType.getMonsterType(target);
return DefaultMonsterType.canAttack(maid, target, monsterType);
@Override
public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) {
return new AttackTaskConfigContainer(index, playerInventory, entityId);
}
};
}

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

import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid;
import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.MonsterListButton;
import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.TouhouImageButton;
import com.github.tartaricacid.touhoulittlemaid.entity.data.inner.AttackListData;
import com.github.tartaricacid.touhoulittlemaid.entity.misc.MonsterType;
import com.github.tartaricacid.touhoulittlemaid.init.InitTaskData;
import com.github.tartaricacid.touhoulittlemaid.inventory.container.task.TaskConfigContainer;
import com.github.tartaricacid.touhoulittlemaid.network.message.SetAttackListPackage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Inventory;
import net.neoforged.neoforge.network.PacketDistributor;
import org.anti_ad.mc.ipn.api.IPNButton;
import org.anti_ad.mc.ipn.api.IPNGuiHint;
import org.anti_ad.mc.ipn.api.IPNPlayerSideOnly;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.lwjgl.glfw.GLFW;

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

@IPNPlayerSideOnly
@IPNGuiHint(button = IPNButton.SORT, horizontalOffset = -36, bottom = -12)
@IPNGuiHint(button = IPNButton.SORT_COLUMNS, horizontalOffset = -24, bottom = -24)
@IPNGuiHint(button = IPNButton.SORT_ROWS, horizontalOffset = -12, bottom = -36)
@IPNGuiHint(button = IPNButton.SHOW_EDITOR, horizontalOffset = -5)
@IPNGuiHint(button = IPNButton.SETTINGS, horizontalOffset = -5)
public class AttackTaskConfigGui extends MaidTaskConfigGui<TaskConfigContainer> {
private static final ResourceLocation BG = ResourceLocation.fromNamespaceAndPath(TouhouLittleMaid.MOD_ID, "textures/gui/attack_task_config.png");

private final Map<ResourceLocation, MonsterType> attackGroups;
private final List<ResourceLocation> attackGroupsKey;
private EditBox inputField;
private int page = 0;

public AttackTaskConfigGui(TaskConfigContainer screenContainer, Inventory inv, Component titleIn) {
super(screenContainer, inv, titleIn);
this.attackGroups = Objects.requireNonNullElse(this.getMaid().getData(InitTaskData.ATTACK_LIST), AttackListData.empty()).attackGroups();
this.attackGroupsKey = Lists.newArrayList();
this.sortKey();
}

private void sortKey() {
this.attackGroupsKey.clear();

List<ResourceLocation> hostile = Lists.newArrayList();
List<ResourceLocation> neutral = Lists.newArrayList();
List<ResourceLocation> friendly = Lists.newArrayList();

for (ResourceLocation id : attackGroups.keySet()) {
if (attackGroups.get(id) == MonsterType.HOSTILE) {
hostile.add(id);
}
if (attackGroups.get(id) == MonsterType.NEUTRAL) {
neutral.add(id);
}
if (attackGroups.get(id) == MonsterType.FRIENDLY) {
friendly.add(id);
}
}

attackGroupsKey.addAll(hostile);
attackGroupsKey.addAll(neutral);
attackGroupsKey.addAll(friendly);

this.page = Mth.clamp(this.page, 0, (this.attackGroupsKey.size() - 1) / 7);
}

@Override
protected void initAdditionWidgets() {
int startLeft = leftPos + 87;
int startTop = topPos + 36;

this.inputField = new EditBox(this.font, startLeft, startTop, 117, 16, Component.literal("Monster List"));
this.inputField.setMaxLength(256);
this.addWidget(this.inputField);

this.addRenderableWidget(Button.builder(Component.translatable("gui.touhou_little_maid.monster_type.add"), b -> addMonsterType())
.pos(startLeft + 119, startTop - 1).size(44, 18).build());

this.addRenderableWidget(new TouhouImageButton(startLeft + 121, startTop + 20, 5, 9, 0, 176, 9, BG, b -> {
this.page = this.page - 1;
this.page = Mth.clamp(this.page, 0, (this.attackGroupsKey.size() - 1) / 7);
this.init();
}));
this.addRenderableWidget(new TouhouImageButton(startLeft + 156, startTop + 20, 5, 9, 5, 176, 9, BG, b -> {
this.page = this.page + 1;
this.page = Mth.clamp(this.page, 0, (this.attackGroupsKey.size() - 1) / 7);
this.init();
}));

for (int i = 0; i < 7; i++) {
int index = page * 7 + i;
if (index >= attackGroupsKey.size()) {
return;
}
ResourceLocation id = attackGroupsKey.get(index);
EntityType<?> type = BuiltInRegistries.ENTITY_TYPE.get(id);
Component name = type.getDescription();
int yOffset = startTop + 31 + 13 * i;
this.addRenderableWidget(new MonsterListButton(name, startLeft - 1, yOffset, id, this));
}
}

private void addMonsterType() {
String value = this.inputField.getValue();
if (StringUtils.isBlank(value)) {
return;
}
if (!ResourceLocation.isValidNamespace(value)) {
return;
}
ResourceLocation id = ResourceLocation.parse(value);
if (BuiltInRegistries.ENTITY_TYPE.containsKey(id)) {
this.attackGroups.put(id, MonsterType.NEUTRAL);
this.sortKey();
super.init();
}
}

public void removeMonsterType(ResourceLocation id) {
this.attackGroups.remove(id);
this.sortKey();
super.init();
}

@Override
public void resize(Minecraft minecraft, int width, int height) {
String value = this.inputField.getValue();
super.resize(minecraft, width, height);
this.inputField.setValue(value);
}

@Override
protected void renderAddition(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
this.inputField.render(graphics, mouseX, mouseY, partialTicks);

MutableComponent pageText = Component.literal(String.format("%d/%d", this.page + 1, (this.attackGroupsKey.size() - 1) / 7 + 1));
graphics.drawCenteredString(font, pageText, leftPos + 228, topPos + 57, 0xFFFFFF);
graphics.drawCenteredString(font, Component.translatable("gui.touhou_little_maid.monster_type.title"), leftPos + 147, topPos + 57, 0xFFFFFF);
}

@Override
protected void renderBg(GuiGraphics graphics, float partialTicks, int x, int y) {
super.renderBg(graphics, partialTicks, x, y);
graphics.blit(BG, leftPos + 80, topPos + 28, 0, 0, imageWidth, 137);
}

@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_ESCAPE && this.getMinecraft().player != null) {
this.getMinecraft().player.closeContainer();
}
return this.inputField.keyPressed(keyCode, scanCode, modifiers) || this.inputField.canConsumeInput() || super.keyPressed(keyCode, scanCode, modifiers);
}

@Override
public void onClose() {
PacketDistributor.sendToServer(new SetAttackListPackage(this.getMaid().getId(), this.attackGroups));
super.onClose();
}

public Map<ResourceLocation, MonsterType> getAttackGroups() {
return attackGroups;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button;

import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid;
import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.task.AttackTaskConfigGui;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;

public class MonsterListButton extends Button {
private static final ResourceLocation ICON = ResourceLocation.fromNamespaceAndPath(TouhouLittleMaid.MOD_ID, "textures/gui/attack_task_config.png");
private final AttackTaskConfigGui parents;
private final ResourceLocation entityId;

public MonsterListButton(Component entityName, int x, int y, ResourceLocation entityId, AttackTaskConfigGui parents) {
super(Button.builder(entityName, b -> {
}).pos(x, y).size(164, 13));
this.parents = parents;
this.entityId = entityId;
}

@Override
protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float pPartialTick) {
Minecraft mc = Minecraft.getInstance();
RenderSystem.enableDepthTest();
if (deleteClick(mouseX, mouseY)) {
graphics.blit(ICON, this.getX(), this.getY(), 0, 163, this.width, this.height, 256, 256);
} else if (leftClick(mouseX, mouseY) || rightClick(mouseX, mouseY)) {
graphics.blit(ICON, this.getX(), this.getY(), 0, 150, this.width, this.height, 256, 256);
} else {
graphics.blit(ICON, this.getX(), this.getY(), 0, 137, this.width, this.height, 256, 256);
}
graphics.drawString(mc.font, this.getMessage(), this.getX() + 5, this.getY() + 3, 0x444444, false);
graphics.drawCenteredString(mc.font, this.parents.getAttackGroups().get(entityId).getComponent(), this.getX() + 142, this.getY() + 3, 0xFFFFFF);
}

@Override
public void onClick(double mouseX, double mouseY) {
if (deleteClick(mouseX, mouseY)) {
this.parents.removeMonsterType(this.entityId);
} else if (leftClick(mouseX, mouseY)) {
this.parents.getAttackGroups().computeIfPresent(this.entityId, (k, monsterType) -> monsterType.getPrevious());
} else if (rightClick(mouseX, mouseY)) {
this.parents.getAttackGroups().computeIfPresent(this.entityId, (k, monsterType) -> monsterType.getNext());
}
}

private boolean deleteClick(double mouseX, double mouseY) {
boolean clickY = this.getY() <= mouseY && mouseY <= (this.getY() + this.getHeight());
boolean deleteClickX = (this.getX() + 107) <= mouseX && mouseX <= (this.getX() + 120);
return clickY && deleteClickX;
}

private boolean leftClick(double mouseX, double mouseY) {
boolean clickY = this.getY() <= mouseY && mouseY <= (this.getY() + this.getHeight());
boolean leftClickX = (this.getX() + 120) <= mouseX && mouseX <= (this.getX() + 130);
return clickY && leftClickX;
}

private boolean rightClick(double mouseX, double mouseY) {
boolean clickY = this.getY() <= mouseY && mouseY <= (this.getY() + this.getHeight());
boolean rightClickX = (this.getX() + 154) <= mouseX && mouseX <= (this.getX() + 164);
return clickY && rightClickX;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack.*;
import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.config.MaidConfigContainerGui;
import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.task.AttackTaskConfigGui;
import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.task.DefaultMaidTaskConfigGui;
import com.github.tartaricacid.touhoulittlemaid.client.gui.item.PicnicBasketContainerScreen;
import com.github.tartaricacid.touhoulittlemaid.client.gui.item.WirelessIOContainerGui;
Expand Down Expand Up @@ -29,5 +30,6 @@ public static void clientSetup(RegisterMenuScreensEvent event) {
event.register(InitContainer.PICNIC_BASKET_CONTAINER.get(), PicnicBasketContainerScreen::new);

event.register(InitContainer.DEFAULT_MAIK_TASK_CONFIG.get(), DefaultMaidTaskConfigGui::new);
event.register(InitContainer.ATTACK_TASK_CONFIG.get(), AttackTaskConfigGui::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.tartaricacid.touhoulittlemaid.entity.data.inner;

import com.github.tartaricacid.touhoulittlemaid.entity.misc.MonsterType;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.resources.ResourceLocation;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public record AttackListData(Map<ResourceLocation, MonsterType> attackGroups) {
private static final Codec<Map<ResourceLocation, MonsterType>> LIST_CODEC = Codec.unboundedMap(ResourceLocation.CODEC, MonsterType.CODEC)
.xmap(HashMap::new, Function.identity());
public static final Codec<AttackListData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
LIST_CODEC.fieldOf("attack_groups").forGetter(AttackListData::attackGroups)
).apply(instance, AttackListData::new));

public static AttackListData empty() {
return new AttackListData(Maps.newHashMap());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public static MonsterType getTypeByIndex(int index) {
return MonsterType.values()[Math.min(index, length - 1)];
}

public MonsterType getPrevious() {
int index = this.ordinal() - 1;
if (index < 0) {
index = values().length - 1;
}
return values()[index % values().length];
}

public MonsterType getNext() {
int ordinal = this.ordinal();
int length = MonsterType.values().length;
Expand Down
Loading

0 comments on commit 844dc9f

Please sign in to comment.