Natural Spawns PR2 Improvements to GUI Functionality

This commit is contained in:
Kam committed 2026-02-23 05:21:51 -05:00
1 parent 27e9f98862
commit 5c42308ca3
9 files changed
+378 -33

No files matched your search

+117 -6
View File
@@ -28,8 +28,10 @@ import noppes.npcs.scripted.event.CustomNPCsEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -37,6 +39,10 @@ public class NPCSpawning {
private static Set<ChunkCoordIntPair> eligibleChunksForSpawning = Sets.newHashSet();
private static final byte CHUNK_SPAWN_RADIUS = 7;
private static final int SPAWN_ATTEMPTS_PER_CHUNK = 3;
private static final String NATURAL_SPAWN_ID_TAG = "CNPCNaturalSpawnId";
private static final String NATURAL_SPAWN_NAME_TAG = "CNPCNaturalSpawnName";
private static final String NATURAL_SPAWN_TIME_TAG = "CNPCNaturalSpawnTime";
private static final Map<Long, Long> SPAWN_COOLDOWNS = new HashMap<Long, Long>();
private static boolean animalSpawn;
private static boolean monsterSpawn;
@@ -67,6 +73,7 @@ public class NPCSpawning {
if (npcCount >= cap) {
return;
}
HashMap<Integer, Integer> spawnEntryCounts = getNaturalSpawnEntryCounts(world);
ArrayList<ChunkCoordIntPair> tmp = new ArrayList(eligibleChunksForSpawning);
Collections.shuffle(tmp);
Iterator<ChunkCoordIntPair> iterator = tmp.iterator();
@@ -95,10 +102,10 @@ public class NPCSpawning {
String name = world.getBiomeGenForCoords(x, z).biomeName;
SpawnData data = SpawnController.Instance.getRandomSpawnData(name, world.provider.dimensionId);
if (data == null || y < data.spawnHeightMin || y > data.spawnHeightMax || !canCreatureTypeSpawnAtLocation(data, world, x, y, z) || world.getClosestPlayer(x, y, z, 24.0D) != null)
if (data == null)
continue;
if (spawnData(data, world, x, y, z)) {
if (trySpawnWithEntryConfig(data, world, x, y, z, world.rand, spawnEntryCounts)) {
npcCount++;
}
}
@@ -141,12 +148,108 @@ public class NPCSpawning {
return chunkSet.size();
}
private static HashMap<Integer, Integer> getNaturalSpawnEntryCounts(World world) {
HashMap<Integer, Integer> counts = new HashMap<Integer, Integer>();
List<Entity> list = world.loadedEntityList;
for (Entity entity : list) {
if (entity == null || entity.isDead) {
continue;
}
NBTTagCompound entityData = entity.getEntityData();
if (!entityData.hasKey(NATURAL_SPAWN_ID_TAG)) {
continue;
}
int spawnId = entityData.getInteger(NATURAL_SPAWN_ID_TAG);
Integer count = counts.get(spawnId);
counts.put(spawnId, count == null ? 1 : count + 1);
}
return counts;
}
private static long getSpawnCooldownKey(World world, int spawnId) {
return ((long) world.provider.dimensionId << 32) ^ (spawnId & 0xffffffffL);
}
private static boolean isSpawnOnCooldown(World world, SpawnData data) {
if (data.cooldownTicks <= 0 || data.id < 0) {
return false;
}
long key = getSpawnCooldownKey(world, data.id);
Long lastSpawnTick = SPAWN_COOLDOWNS.get(key);
if (lastSpawnTick == null) {
return false;
}
long tickDelta = world.getWorldInfo().getWorldTotalTime() - lastSpawnTick;
return tickDelta < data.cooldownTicks;
}
private static void markSpawnCooldown(World world, SpawnData data) {
if (data.cooldownTicks <= 0 || data.id < 0) {
return;
}
long key = getSpawnCooldownKey(world, data.id);
SPAWN_COOLDOWNS.put(key, world.getWorldInfo().getWorldTotalTime());
}
private static boolean isSpawnEntryAtCapacity(SpawnData data, Map<Integer, Integer> spawnEntryCounts) {
if (data.maxAlive <= 0 || data.id < 0) {
return false;
}
Integer currentCount = spawnEntryCounts.get(data.id);
return currentCount != null && currentCount >= data.maxAlive;
}
private static void incrementSpawnEntryCount(SpawnData data, Map<Integer, Integer> spawnEntryCounts) {
if (data.id < 0) {
return;
}
Integer currentCount = spawnEntryCounts.get(data.id);
spawnEntryCounts.put(data.id, currentCount == null ? 1 : currentCount + 1);
}
private static boolean hasNearbyPlayer(SpawnData data, World world, int x, int y, int z) {
if (data.playerMinDistance <= 0) {
return false;
}
return world.getClosestPlayer(x, y, z, data.playerMinDistance) != null;
}
private static boolean trySpawnWithEntryConfig(SpawnData data, World world, int x, int y, int z, Random rand, Map<Integer, Integer> spawnEntryCounts) {
if (y < data.spawnHeightMin || y > data.spawnHeightMax) {
return false;
}
if (isSpawnEntryAtCapacity(data, spawnEntryCounts) || isSpawnOnCooldown(world, data)) {
return false;
}
int attempts = Math.max(1, data.attemptsPerCycle);
for (int attempt = 0; attempt < attempts; attempt++) {
int attemptX = x;
int attemptZ = z;
if (attempt > 0) {
attemptX += rand.nextInt(5) - rand.nextInt(5);
attemptZ += rand.nextInt(5) - rand.nextInt(5);
}
if (!canCreatureTypeSpawnAtLocation(data, world, attemptX, y, attemptZ) || hasNearbyPlayer(data, world, attemptX, y, attemptZ)) {
continue;
}
if (spawnData(data, world, attemptX, y, attemptZ)) {
incrementSpawnEntryCount(data, spawnEntryCounts);
markSpawnCooldown(world, data);
return true;
}
}
return false;
}
public static void performWorldGenSpawning(World world, int x, int z, Random rand) {
int cap = getCap(world);
int npcCount = countNPCs(world);
if (npcCount >= cap) {
return;
}
HashMap<Integer, Integer> spawnEntryCounts = getNaturalSpawnEntryCounts(world);
BiomeGenBase biome = world.getBiomeGenForCoords(x + 8, z + 8);
while (rand.nextFloat() < biome.getSpawningChance()) {
if (npcCount >= cap) {
@@ -174,13 +277,13 @@ public class NPCSpawning {
l2 = l2 + (int) ((data.spawnHeightMax - l2) * Math.random());
}
if (!canCreatureTypeSpawnAtLocation(data, world, j1, l2, k1)) {
if (!trySpawnWithEntryConfig(data, world, j1, l2, k1, rand, spawnEntryCounts)) {
j1 += rand.nextInt(5) - rand.nextInt(5);
for (k1 += rand.nextInt(5) - rand.nextInt(5); j1 < x || j1 >= x + size || k1 < z || k1 >= z + size; k1 = i2 + rand.nextInt(5) - rand.nextInt(5)) {
j1 = l1 + rand.nextInt(5) - rand.nextInt(5);
}
} else if (spawnData(data, world, j1, l2, k1)) {
} else {
npcCount++;
break;
}
@@ -226,14 +329,22 @@ public class NPCSpawning {
if (spawnEntity instanceof EntityCustomNpc) {
EntityCustomNpc npc = (EntityCustomNpc) spawnEntity;
npc.stats.spawnCycle = 3;
npc.stats.canDespawn = true;
npc.stats.playerSetCanDespawn = true;
if (data.despawnMode == SpawnData.DESPAWN_FORCE_PERSISTENT) {
npc.stats.canDespawn = false;
npc.stats.playerSetCanDespawn = false;
} else if (data.despawnMode == SpawnData.DESPAWN_FORCE_NATURAL) {
npc.stats.canDespawn = true;
npc.stats.playerSetCanDespawn = true;
}
npc.ais.returnToStart = false;
npc.ais.startPos = new int[]{x, y, z};
npc.updateAI = true;
npc.updateClient = true;
npc.getNavigator().clearPathEntity();
}
entityliving.getEntityData().setInteger(NATURAL_SPAWN_ID_TAG, data.id);
entityliving.getEntityData().setString(NATURAL_SPAWN_NAME_TAG, data.name);
entityliving.getEntityData().setLong(NATURAL_SPAWN_TIME_TAG, world.getWorldInfo().getWorldTotalTime());
spawnEntity.setLocationAndAngles(x + 0.5, y, z + 0.5, world.rand.nextFloat() * 360.0F, 0.0F);
} catch (Exception exception) {
exception.printStackTrace();
@@ -5,6 +5,7 @@ import noppes.npcs.client.gui.util.GuiNpcButton;
import noppes.npcs.client.gui.util.GuiNpcButtonYesNo;
import noppes.npcs.client.gui.util.GuiNpcLabel;
import noppes.npcs.client.gui.util.GuiNpcTextField;
import noppes.npcs.client.gui.util.GuiScrollWindow;
import noppes.npcs.client.gui.util.ITextfieldListener;
import noppes.npcs.client.gui.util.SubGuiInterface;
import noppes.npcs.controllers.data.SpawnData;
@@ -22,30 +23,90 @@ public class SubGuiSpawningOptions extends SubGuiInterface implements ITextfield
public void initGui() {
super.initGui();
int y = guiTop - 15;
int scrollX = guiLeft + 4;
int scrollY = guiTop + 4;
int scrollW = xSize - 8;
int scrollH = ySize - 34;
addButton(new GuiNpcButtonYesNo(10, guiLeft + 120, y += 22, data.animalSpawning));
addLabel(new GuiNpcLabel(10, "spawning.animalSpawning", guiLeft + 4, y + 5));
GuiScrollWindow sw = new GuiScrollWindow(this, scrollX, scrollY, scrollW, scrollH, 0);
sw.drawDefaultBackground = false;
addScrollableGui(0, sw);
addButton(new GuiNpcButtonYesNo(11, guiLeft + 120, y += 22, data.monsterSpawning));
addLabel(new GuiNpcLabel(11, "spawning.monsterSpawning", guiLeft + 4, y + 5));
int labelX = 4;
int controlX = 132;
int y = 6;
addButton(new GuiNpcButtonYesNo(12, guiLeft + 120, y += 22, data.liquidSpawning));
addLabel(new GuiNpcLabel(12, "spawning.liquidSpawning", guiLeft + 4, y + 5));
sw.addButton(new GuiNpcButtonYesNo(10, controlX, y, data.animalSpawning));
sw.addLabel(new GuiNpcLabel(10, "spawning.animalSpawning", labelX, y + 5));
sw.getButton(10).setHoverText("tooltip.naturalspawns.options.animal");
y += 22;
addButton(new GuiNpcButtonYesNo(13, guiLeft + 120, y += 22, data.airSpawning));
addLabel(new GuiNpcLabel(13, "spawning.airSpawning", guiLeft + 4, y + 5));
sw.addButton(new GuiNpcButtonYesNo(11, controlX, y, data.monsterSpawning));
sw.addLabel(new GuiNpcLabel(11, "spawning.monsterSpawning", labelX, y + 5));
sw.getButton(11).setHoverText("tooltip.naturalspawns.options.monster");
y += 22;
addTextField(new GuiNpcTextField(14, this, guiLeft + 120, y += 22, 40, 20, "" + data.spawnHeightMin));
addLabel(new GuiNpcLabel(14, "spawning.minHeight", guiLeft + 4, y + 5));
addTextField(new GuiNpcTextField(15, this, guiLeft + 120, y += 22, 40, 20, "" + data.spawnHeightMax));
addLabel(new GuiNpcLabel(15, "spawning.maxHeight", guiLeft + 4, y + 5));
getTextField(14).integersOnly = true;
getTextField(14).setMinMaxDefault(-Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
getTextField(15).integersOnly = true;
getTextField(15).setMinMaxDefault(-Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
sw.addButton(new GuiNpcButtonYesNo(12, controlX, y, data.liquidSpawning));
sw.addLabel(new GuiNpcLabel(12, "spawning.liquidSpawning", labelX, y + 5));
sw.getButton(12).setHoverText("tooltip.naturalspawns.options.liquid");
y += 22;
addButton(new GuiNpcButton(100, guiLeft + 154, guiTop + 192, 60, 20, "gui.done"));
sw.addButton(new GuiNpcButtonYesNo(13, controlX, y, data.airSpawning));
sw.addLabel(new GuiNpcLabel(13, "spawning.airSpawning", labelX, y + 5));
sw.getButton(13).setHoverText("tooltip.naturalspawns.options.air");
y += 22;
sw.addTextField(new GuiNpcTextField(14, this, controlX, y, 60, 20, "" + data.spawnHeightMin));
sw.addLabel(new GuiNpcLabel(14, "spawning.minHeight", labelX, y + 5));
sw.getTextField(14).integersOnly = true;
sw.getTextField(14).setMinMaxDefault(-Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
sw.getTextField(14).setHoverText("tooltip.naturalspawns.options.minHeight");
y += 22;
sw.addTextField(new GuiNpcTextField(15, this, controlX, y, 60, 20, "" + data.spawnHeightMax));
sw.addLabel(new GuiNpcLabel(15, "spawning.maxHeight", labelX, y + 5));
sw.getTextField(15).integersOnly = true;
sw.getTextField(15).setMinMaxDefault(-Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
sw.getTextField(15).setHoverText("tooltip.naturalspawns.options.maxHeight");
y += 22;
sw.addTextField(new GuiNpcTextField(16, this, controlX, y, 60, 20, "" + data.maxAlive));
sw.addLabel(new GuiNpcLabel(16, "spawning.maxAlive", labelX, y + 5));
sw.getTextField(16).integersOnly = true;
sw.getTextField(16).setMinMaxDefault(0, Integer.MAX_VALUE, 0);
sw.getTextField(16).setHoverText("tooltip.naturalspawns.options.maxAlive");
y += 22;
sw.addTextField(new GuiNpcTextField(17, this, controlX, y, 60, 20, "" + data.cooldownTicks));
sw.addLabel(new GuiNpcLabel(17, "spawning.cooldownTicks", labelX, y + 5));
sw.getTextField(17).integersOnly = true;
sw.getTextField(17).setMinMaxDefault(0, Integer.MAX_VALUE, 0);
sw.getTextField(17).setHoverText("tooltip.naturalspawns.options.cooldownTicks");
y += 22;
sw.addTextField(new GuiNpcTextField(18, this, controlX, y, 60, 20, "" + data.attemptsPerCycle));
sw.addLabel(new GuiNpcLabel(18, "spawning.attemptsPerCycle", labelX, y + 5));
sw.getTextField(18).integersOnly = true;
sw.getTextField(18).setMinMaxDefault(1, Integer.MAX_VALUE, 1);
sw.getTextField(18).setHoverText("tooltip.naturalspawns.options.attemptsPerCycle");
y += 22;
sw.addTextField(new GuiNpcTextField(19, this, controlX, y, 60, 20, "" + data.playerMinDistance));
sw.addLabel(new GuiNpcLabel(19, "spawning.playerMinDistance", labelX, y + 5));
sw.getTextField(19).integersOnly = true;
sw.getTextField(19).setMinMaxDefault(0, Integer.MAX_VALUE, 24);
sw.getTextField(19).setHoverText("tooltip.naturalspawns.options.playerMinDistance");
y += 22;
sw.addButton(new GuiNpcButton(20, controlX, y, 74, 20,
new String[]{"spawning.despawn.forceNatural", "spawning.despawn.preserve", "spawning.despawn.forcePersistent"}, data.despawnMode));
sw.addLabel(new GuiNpcLabel(20, "spawning.despawnMode", labelX, y + 5));
sw.getButton(20).setHoverText("tooltip.naturalspawns.options.despawnMode");
y += 22;
sw.maxScrollY = Math.max(0, y - sw.clipHeight + 8);
addButton(new GuiNpcButton(100, guiLeft + xSize - 64, guiTop + ySize - 24, 60, 20, "gui.done"));
}
@Override
@@ -64,6 +125,9 @@ public class SubGuiSpawningOptions extends SubGuiInterface implements ITextfield
if (button.id == 13) {
data.airSpawning = button.getValue() == 1;
}
if (button.id == 20) {
data.setDespawnMode(button.getValue());
}
if (button.id == 100) {
close();
}
@@ -77,5 +141,17 @@ public class SubGuiSpawningOptions extends SubGuiInterface implements ITextfield
if (textfield.id == 15) {
data.spawnHeightMax = textfield.getInteger();
}
if (textfield.id == 16) {
data.setMaxAlive(textfield.getInteger());
}
if (textfield.id == 17) {
data.setCooldownTicks(textfield.getInteger());
}
if (textfield.id == 18) {
data.setAttemptsPerCycle(textfield.getInteger());
}
if (textfield.id == 19) {
data.setPlayerMinDistance(textfield.getInteger());
}
}
}
@@ -62,6 +62,12 @@ public class GuiNpcNaturalSpawns extends GuiNPCInterface2 implements IGuiData, I
this.addButton(new GuiNpcButton(1, guiLeft + 358, guiTop + 38, 58, 20, "gui.add"));
this.addButton(new GuiNpcButton(2, guiLeft + 358, guiTop + 61, 58, 20, "gui.remove"));
getButton(1).setHoverText("tooltip.naturalspawns.main.addProfile");
getButton(2).setHoverText("tooltip.naturalspawns.main.removeProfile");
if (getTextField(55) != null) {
getTextField(55).setHoverText("tooltip.naturalspawns.main.search");
}
if (this.spawn.id >= 0)
showSpawn();
@@ -74,15 +80,18 @@ public class GuiNpcNaturalSpawns extends GuiNPCInterface2 implements IGuiData, I
int y = guiTop + 30;
addLabel(new GuiNpcLabel(3, "spawning.biomes", guiLeft + 4, y));
addButton(new GuiNpcButton(3, guiLeft + 40, y - 5, 50, 20, "selectServer.edit"));
getButton(3).setHoverText("tooltip.naturalspawns.main.biomes");
addLabel(new GuiNpcLabel(11, StatCollector.translateToLocal("Dimensions"), guiLeft + 95, y));
addButton(new GuiNpcButton(11, guiLeft + 150, y - 5, 50, 20, "selectServer.edit"));
getButton(11).setHoverText("tooltip.naturalspawns.main.dimensions");
addLabel(new GuiNpcLabel(4, "spawning.options", guiLeft + 4, y += 22));
addButton(new GuiNpcButton(4, guiLeft + 90, y - 5, 50, 20, "selectServer.edit"));
getButton(4).setHoverText("tooltip.naturalspawns.main.options");
addSlider(new GuiNpcSlider(this, 5, guiLeft + 4, y += 17, 180, 20, (float) this.spawn.itemWeight / 100));
addExtra(new GuiHoverText(1, "spawning.naturalInfo", guiLeft + 188, y + 5));
addExtra(new GuiHoverText(1, "spawning.naturalInfoPerformance", guiLeft + 188, y + 5));
this.spawnEntryScroll.guiLeft = guiLeft + 4;
this.spawnEntryScroll.guiTop = y + 40;
@@ -96,13 +105,17 @@ public class GuiNpcNaturalSpawns extends GuiNPCInterface2 implements IGuiData, I
addScroll(this.spawnEntryScroll);
addButton(new GuiNpcButton(21, guiLeft + 6, y += 20, 20, 20, "+"));
getButton(21).setHoverText("tooltip.naturalspawns.main.addEntry");
if (this.spawnEntryScroll.hasSelected()) {
int selected = Integer.parseInt(this.spawnEntryScroll.getSelected());
addButton(new GuiNpcButton(22, guiLeft + 32, y, 20, 20, "-"));
getButton(22).setHoverText("tooltip.naturalspawns.main.removeEntry");
GuiNpcTextField num = new GuiNpcTextField(25, this, guiLeft + 60, y += 10, 30, 20, "" + selected);
num.integersOnly = true;
addTextField(num);
getTextField(25).setHoverText("tooltip.naturalspawns.main.slot");
addButton(new GuiNpcButton(26, guiLeft + 92, y, 100, 20, this.getTitle(this.spawn.spawnCompounds.get(selected))));
getButton(26).setHoverText("tooltip.naturalspawns.main.selectEntry");
}
}
@@ -82,7 +82,8 @@ public class GuiNpcButton extends GuiButton {
}
public void setHoverText(String text) {
this.hoverableText = StatCollector.translateToLocal(text);
String translated = StatCollector.translateToLocal(text);
this.hoverableText = translated == null ? "" : translated.replace("\\n", "\n");
}
public int getValue() {
@@ -201,13 +202,30 @@ public class GuiNpcButton extends GuiButton {
Minecraft mc = Minecraft.getMinecraft();
String displayString = StatCollector.translateToLocal(hoverableText);
GL11.glColor4f(1, 1, 1, 1);
List<String> lines = TextSplitter.splitText(displayString, 30);
List<String> lines = splitHoverText(displayString, 30);
drawHoveringText(lines, i, j, mc);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glPopMatrix();
}
}
private List<String> splitHoverText(String text, int maxLineLength) {
List<String> lines = new ArrayList<String>();
if (text == null || text.isEmpty())
return lines;
String normalized = text.replace("\\n", "\n");
String[] explicitLines = normalized.split("\\r?\\n", -1);
for (String line : explicitLines) {
if (line.isEmpty()) {
lines.add("");
continue;
}
lines.addAll(TextSplitter.splitText(line, maxLineLength));
}
return lines;
}
protected void drawHoveringText(List textLines, int x, int y, Minecraft mc) {
if (mc.fontRenderer == null || textLines.isEmpty())
return;
@@ -12,6 +12,7 @@ import net.minecraft.util.StatCollector;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.util.ArrayList;
import java.util.List;
public class GuiNpcTextField extends GuiTextField {
@@ -304,7 +305,8 @@ public class GuiNpcTextField extends GuiTextField {
}
public void setHoverText(String text) {
this.hoverableText = StatCollector.translateToLocal(text);
String translated = StatCollector.translateToLocal(text);
this.hoverableText = translated == null ? "" : translated.replace("\\n", "\n");
}
public boolean hasHoverText() {
@@ -317,10 +319,14 @@ public class GuiNpcTextField extends GuiTextField {
}
public void drawHover(int mouseX, int mouseY, boolean hasSubGui) {
drawHover(mouseX, mouseY, mouseX, mouseY, hasSubGui);
}
public void drawHover(int hitMouseX, int hitMouseY, int tooltipMouseX, int tooltipMouseY, boolean hasSubGui) {
if (hasSubGui || !enabled || hoverableText.isEmpty())
return;
boolean isHovered = isMouseOver(mouseX, mouseY);
boolean isHovered = isMouseOver(hitMouseX, hitMouseY);
if (!isHovered) {
wasHovered = false;
hoverCount = 0;
@@ -338,13 +344,30 @@ public class GuiNpcTextField extends GuiTextField {
GL11.glPushMatrix();
Minecraft mc = Minecraft.getMinecraft();
GL11.glColor4f(1, 1, 1, 1);
List<String> lines = TextSplitter.splitText(hoverableText, 30);
drawHoveringText(lines, mouseX, mouseY, mc);
List<String> lines = splitHoverText(hoverableText, 30);
drawHoveringText(lines, tooltipMouseX, tooltipMouseY, mc);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glPopMatrix();
}
}
private List<String> splitHoverText(String text, int maxLineLength) {
List<String> lines = new ArrayList<String>();
if (text == null || text.isEmpty())
return lines;
String normalized = text.replace("\\n", "\n");
String[] explicitLines = normalized.split("\\r?\\n", -1);
for (String line : explicitLines) {
if (line.isEmpty()) {
lines.add("");
continue;
}
lines.addAll(TextSplitter.splitText(line, maxLineLength));
}
return lines;
}
protected void drawHoveringText(List<String> textLines, int x, int y, Minecraft mc) {
if (mc.fontRenderer == null || textLines.isEmpty())
return;
@@ -300,9 +300,14 @@ public class GuiScrollWindow extends GuiScreen implements ITextfieldListener, IC
button.drawHover(mouseX, mouseY, subGui);
}
}
// Label hover: convert screen mouse to scroll-local coords for hit testing
int localMouseX = mouseX - xPos;
int localMouseY = (int) (mouseY - yPos + scrollY);
for (GuiNpcTextField textField : textfields.values()) {
if (textField.hasHoverText()) {
textField.drawHover(localMouseX, localMouseY, mouseX, mouseY, subGui);
}
}
// Label hover: convert screen mouse to scroll-local coords for hit testing
for (GuiNpcLabel label : labels.values()) {
if (!label.hoverableText.isEmpty()) {
label.drawHover(localMouseX, localMouseY, mouseX, mouseY, subGui, fontRendererObj);
@@ -21,6 +21,10 @@ import java.util.Map;
import java.util.Set;
public class SpawnData extends WeightedRandom.Item implements INaturalSpawn {
public static final int DESPAWN_FORCE_NATURAL = INaturalSpawn.DESPAWN_FORCE_NATURAL;
public static final int DESPAWN_PRESERVE_TEMPLATE = INaturalSpawn.DESPAWN_PRESERVE_TEMPLATE;
public static final int DESPAWN_FORCE_PERSISTENT = INaturalSpawn.DESPAWN_FORCE_PERSISTENT;
public List<String> biomes = new ArrayList<>();
public HashSet<Integer> dimensions = new HashSet<>();
public int id = -1;
@@ -35,6 +39,11 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn {
public int spawnHeightMin;
public int spawnHeightMax = 100;
public int maxAlive = 0;
public int cooldownTicks = 0;
public int attemptsPerCycle = 1;
public int playerMinDistance = 24;
public int despawnMode = DESPAWN_FORCE_NATURAL;
public SpawnData() {
super(10);
@@ -74,6 +83,19 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn {
} else {
spawnHeightMax = compound.getInteger("HeightMax");
}
maxAlive = compound.hasKey("MaxAlive") ? Math.max(0, compound.getInteger("MaxAlive")) : 0;
cooldownTicks = compound.hasKey("CooldownTicks") ? Math.max(0, compound.getInteger("CooldownTicks")) : 0;
attemptsPerCycle = compound.hasKey("AttemptsPerCycle") ? Math.max(1, compound.getInteger("AttemptsPerCycle")) : 1;
playerMinDistance = compound.hasKey("PlayerMinDistance") ? Math.max(0, compound.getInteger("PlayerMinDistance")) : 24;
if (compound.hasKey("DespawnMode")) {
despawnMode = compound.getInteger("DespawnMode");
} else {
despawnMode = DESPAWN_FORCE_NATURAL;
}
if (despawnMode < DESPAWN_FORCE_NATURAL || despawnMode > DESPAWN_FORCE_PERSISTENT) {
despawnMode = DESPAWN_FORCE_NATURAL;
}
}
public NBTTagCompound writeNBT(NBTTagCompound compound) {
@@ -98,6 +120,11 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn {
compound.setInteger("HeightMin", spawnHeightMin);
compound.setInteger("HeightMax", spawnHeightMax);
compound.setInteger("MaxAlive", maxAlive);
compound.setInteger("CooldownTicks", cooldownTicks);
compound.setInteger("AttemptsPerCycle", attemptsPerCycle);
compound.setInteger("PlayerMinDistance", playerMinDistance);
compound.setInteger("DespawnMode", despawnMode);
return compound;
}
@@ -204,4 +231,46 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn {
public void setBiomes(String[] biomes) {
this.biomes = new ArrayList<>(Arrays.asList(biomes));
}
public void setMaxAlive(int maxAlive) {
this.maxAlive = Math.max(0, maxAlive);
}
public int getMaxAlive() {
return this.maxAlive;
}
public void setCooldownTicks(int ticks) {
this.cooldownTicks = Math.max(0, ticks);
}
public int getCooldownTicks() {
return this.cooldownTicks;
}
public void setAttemptsPerCycle(int attempts) {
this.attemptsPerCycle = Math.max(1, attempts);
}
public int getAttemptsPerCycle() {
return this.attemptsPerCycle;
}
public void setPlayerMinDistance(int distance) {
this.playerMinDistance = Math.max(0, distance);
}
public int getPlayerMinDistance() {
return this.playerMinDistance;
}
public void setDespawnMode(int mode) {
if (mode < DESPAWN_FORCE_NATURAL || mode > DESPAWN_FORCE_PERSISTENT)
mode = DESPAWN_FORCE_NATURAL;
this.despawnMode = mode;
}
public int getDespawnMode() {
return this.despawnMode;
}
}