1.20 progres
king and queen balancing Antmail Blockle
This commit is contained in:
1 parent
c1d60d735d
commit
fc5028a15b
430 files changed
+30569
-1684
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy;
|
||||
|
||||
import com.craisinlord.antarchy.content.util.AttributeRangeInit;
|
||||
import com.craisinlord.antarchy.content.worldgen.VillagePoolAdditions;
|
||||
import com.craisinlord.integrated_api.events.lifecycle.ServerGoingToStartEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -16,5 +17,6 @@ public class Antarchy {
|
||||
ServerGoingToStartEvent.EVENT.addListener(Antarchy::serverAboutToStart);
|
||||
}
|
||||
private static void serverAboutToStart(final ServerGoingToStartEvent event) {
|
||||
VillagePoolAdditions.apply(event.getServer());
|
||||
}
|
||||
}
|
||||
@@ -247,8 +247,8 @@ public final class AntarchySettings {
|
||||
private static double princessArmor = 8.0D;
|
||||
private static double princessKnockbackResistance = 0.5D;
|
||||
private static double princessFollowRange = 48.0D;
|
||||
private static double kingHealth = 6000.0D;
|
||||
private static double queenHealth = 6000.0D;
|
||||
private static double kingHealth = 3400.0D;
|
||||
private static double queenHealth = 4300.0D;
|
||||
private static double kingAttackDamage = 50.0D;
|
||||
private static double queenAttackDamage = 45.0D;
|
||||
private static double royalBossSoundVolume = 4.5D;
|
||||
@@ -256,10 +256,6 @@ public final class AntarchySettings {
|
||||
private static int queenBlackHoleActiveTicks = 120;
|
||||
private static double queenBlackHolePullStrength = 0.16D;
|
||||
private static int queenBlackHoleBlockSuctionCap = 160;
|
||||
private static double queenFinalTimeFieldRadius = 20.0D;
|
||||
private static double queenFinalTimeFieldRate = 0.05D;
|
||||
private static int queenFinalTimeFieldDurationTicks = 45;
|
||||
private static int queenFinalTimeFieldCooldownTicks = 80;
|
||||
private static double kingFireballDamage = 32.0D;
|
||||
private static double kingFireballRadius = 4.5D;
|
||||
private static int kingFireballCooldownTicks = 150;
|
||||
@@ -270,6 +266,7 @@ public final class AntarchySettings {
|
||||
private static int kingIceSpikeCooldownTicks = 240;
|
||||
private static int kingElementalTerrainCap = 256;
|
||||
private static double royalBossArmor = 20.0D;
|
||||
private static double kingArmor = 30.0D;
|
||||
private static double royalBossFollowRange = 128.0D;
|
||||
private static double royalBossMovementSpeed = 0.22D;
|
||||
private static double royalBossKnockbackResistance = 1.0D;
|
||||
@@ -278,6 +275,13 @@ public final class AntarchySettings {
|
||||
private static double royalBossBiteReach = 12.0D;
|
||||
private static double royalBossBiteDamageMultiplier = 1.6D;
|
||||
private static int royalBossBiteCooldownTicks = 45;
|
||||
private static boolean royalBossMultiplayerScalingEnabled = true;
|
||||
private static int royalBossScalingMaxPlayers = 4;
|
||||
private static double royalBossHealthPerAdditionalPlayer = 0.35D;
|
||||
private static double royalBossDamagePerAdditionalPlayer = 0.10D;
|
||||
private static double royalBoundaryRadius = 192.0D;
|
||||
private static double royalBoundaryWarningRadius = 160.0D;
|
||||
private static int royalBoundaryGraceTicks = 60;
|
||||
private static boolean basiliskPetrifyingGazeEnabled = true;
|
||||
private static int basiliskSpawnMaxLightLevel = 5;
|
||||
private static double basiliskHealth = 320.0D;
|
||||
@@ -546,7 +550,7 @@ public final class AntarchySettings {
|
||||
private static double manticoreHealth = 40.0D;
|
||||
private static double manticoreAttackDamage = 7.0D;
|
||||
private static int manticoreStingPoisonTicks = 100;
|
||||
private static int queenManticoreCap = 6;
|
||||
private static int queenManticoreCap = 15;
|
||||
private static int queenManticoreSummonCooldownTicks = 800;
|
||||
private static int queenManticoreSummonCount = 2;
|
||||
private static double queenManticoreSummonRange = 24.0D;
|
||||
@@ -2067,14 +2071,6 @@ public final class AntarchySettings {
|
||||
public static void setQueenBlackHolePullStrength(double v) { queenBlackHolePullStrength = v; }
|
||||
public static int queenBlackHoleBlockSuctionCap() { return queenBlackHoleBlockSuctionCap; }
|
||||
public static void setQueenBlackHoleBlockSuctionCap(int v) { queenBlackHoleBlockSuctionCap = v; }
|
||||
public static double queenFinalTimeFieldRadius() { return queenFinalTimeFieldRadius; }
|
||||
public static void setQueenFinalTimeFieldRadius(double v) { queenFinalTimeFieldRadius = v; }
|
||||
public static double queenFinalTimeFieldRate() { return queenFinalTimeFieldRate; }
|
||||
public static void setQueenFinalTimeFieldRate(double v) { queenFinalTimeFieldRate = v; }
|
||||
public static int queenFinalTimeFieldDurationTicks() { return queenFinalTimeFieldDurationTicks; }
|
||||
public static void setQueenFinalTimeFieldDurationTicks(int v) { queenFinalTimeFieldDurationTicks = v; }
|
||||
public static int queenFinalTimeFieldCooldownTicks() { return queenFinalTimeFieldCooldownTicks; }
|
||||
public static void setQueenFinalTimeFieldCooldownTicks(int v) { queenFinalTimeFieldCooldownTicks = v; }
|
||||
public static double kingFireballDamage() { return kingFireballDamage; }
|
||||
public static void setKingFireballDamage(double v) { kingFireballDamage = v; }
|
||||
public static double kingFireballRadius() { return kingFireballRadius; }
|
||||
@@ -2096,6 +2092,8 @@ public final class AntarchySettings {
|
||||
|
||||
public static double royalBossArmor() { return royalBossArmor; }
|
||||
public static void setRoyalBossArmor(double v) { royalBossArmor = v; }
|
||||
public static double kingArmor() { return kingArmor; }
|
||||
public static void setKingArmor(double v) { kingArmor = v; }
|
||||
|
||||
public static double royalBossFollowRange() { return royalBossFollowRange; }
|
||||
public static void setRoyalBossFollowRange(double v) { royalBossFollowRange = v; }
|
||||
@@ -2120,6 +2118,20 @@ public final class AntarchySettings {
|
||||
|
||||
public static int royalBossBiteCooldownTicks() { return royalBossBiteCooldownTicks; }
|
||||
public static void setRoyalBossBiteCooldownTicks(int v) { royalBossBiteCooldownTicks = v; }
|
||||
public static boolean royalBossMultiplayerScalingEnabled() { return royalBossMultiplayerScalingEnabled; }
|
||||
public static void setRoyalBossMultiplayerScalingEnabled(boolean v) { royalBossMultiplayerScalingEnabled = v; }
|
||||
public static int royalBossScalingMaxPlayers() { return royalBossScalingMaxPlayers; }
|
||||
public static void setRoyalBossScalingMaxPlayers(int v) { royalBossScalingMaxPlayers = v; }
|
||||
public static double royalBossHealthPerAdditionalPlayer() { return royalBossHealthPerAdditionalPlayer; }
|
||||
public static void setRoyalBossHealthPerAdditionalPlayer(double v) { royalBossHealthPerAdditionalPlayer = v; }
|
||||
public static double royalBossDamagePerAdditionalPlayer() { return royalBossDamagePerAdditionalPlayer; }
|
||||
public static void setRoyalBossDamagePerAdditionalPlayer(double v) { royalBossDamagePerAdditionalPlayer = v; }
|
||||
public static double royalBoundaryRadius() { return royalBoundaryRadius; }
|
||||
public static void setRoyalBoundaryRadius(double v) { royalBoundaryRadius = v; }
|
||||
public static double royalBoundaryWarningRadius() { return royalBoundaryWarningRadius; }
|
||||
public static void setRoyalBoundaryWarningRadius(double v) { royalBoundaryWarningRadius = v; }
|
||||
public static int royalBoundaryGraceTicks() { return royalBoundaryGraceTicks; }
|
||||
public static void setRoyalBoundaryGraceTicks(int v) { royalBoundaryGraceTicks = v; }
|
||||
|
||||
public static double nightmareSwordBaseDamage() { return nightmareSwordBaseDamage; }
|
||||
public static void setNightmareSwordBaseDamage(double v) { nightmareSwordBaseDamage = v; }
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.craisinlord.antarchy.content.antmail;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class AntmailEventData extends SimplePreparableReloadListener<Map<ResourceLocation, AntmailEventData.Definition>> {
|
||||
private static final AntmailEventData INSTANCE = new AntmailEventData();
|
||||
private static volatile Map<ResourceLocation, Definition> definitions = Map.of();
|
||||
private AntmailEventData() {
|
||||
}
|
||||
|
||||
public static AntmailEventData instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static void onTick(MinecraftServer server) {
|
||||
long dayTime = server.overworld().getDayTime();
|
||||
long day = dayTime / 24000L;
|
||||
if (dayTime % 24000L >= 20L) return;
|
||||
AntmailServerData data = AntmailServerData.access(server);
|
||||
if (!data.beginRandomMailDay(day)) return;
|
||||
for (AntmailAddress address : data.registeredAddresses()) {
|
||||
for (Map.Entry<ResourceLocation, Definition> entry : definitions.entrySet()) {
|
||||
Definition definition = entry.getValue();
|
||||
if (definition.trigger().type().equals("random") && server.overworld().random.nextDouble() < definition.trigger().chance()) deliver(server, address, entry.getKey(), definition, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void onAdvancement(ServerPlayer player, AdvancementHolder advancement) {
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailAddress address = data.lastUsedAddress(player);
|
||||
if (address == null) return;
|
||||
for (Map.Entry<ResourceLocation, Definition> entry : definitions.entrySet()) {
|
||||
Trigger trigger = entry.getValue().trigger();
|
||||
if (trigger.type().equals("advancement") && trigger.value().equals(advancement.id().toString())) deliver(player.server, address, entry.getKey(), entry.getValue(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deliver(MinecraftServer server, AntmailAddress recipient, ResourceLocation id, Definition definition, boolean permanentClaim) {
|
||||
AntmailServerData data = AntmailServerData.access(server);
|
||||
try {
|
||||
AntmailAddress sender = AntmailAddress.parse(definition.sender());
|
||||
AntmailMessage message = AntmailMessage.create(sender, recipient, definition.subject(), definition.body(), server.overworld().getGameTime(), List.of());
|
||||
AntmailDeliveryResult result = data.deliver(server, message);
|
||||
if (permanentClaim && result.status() != AntmailDeliveryResult.Status.MAILBOX_FULL
|
||||
&& result.status() != AntmailDeliveryResult.Status.FAILED
|
||||
&& result.status() != AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND) {
|
||||
data.claimTrigger(recipient, id);
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<ResourceLocation, Definition> prepare(ResourceManager manager, ProfilerFiller profiler) {
|
||||
Map<ResourceLocation, Definition> loaded = new HashMap<>();
|
||||
for (Map.Entry<ResourceLocation, net.minecraft.server.packs.resources.Resource> resource : manager.listResources("antmail/message", path -> path.getPath().endsWith(".json")).entrySet()) {
|
||||
try {
|
||||
JsonElement element = JsonParser.parseReader(resource.getValue().openAsReader());
|
||||
if (!element.isJsonObject()) continue;
|
||||
var object = element.getAsJsonObject();
|
||||
var trigger = object.getAsJsonObject("trigger");
|
||||
String type = trigger.get("type").getAsString();
|
||||
String value = trigger.has("value") ? trigger.get("value").getAsString() : String.valueOf(trigger.get("day").getAsInt());
|
||||
ResourceLocation id = ResourceLocation.fromNamespaceAndPath(resource.getKey().getNamespace(), resource.getKey().getPath().substring("antmail/message/".length(), resource.getKey().getPath().length() - 5));
|
||||
loaded.put(id, new Definition(object.get("sender").getAsString(), object.get("subject").getAsString(), object.get("body").getAsString(), new Trigger(type, value)));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return Map.copyOf(loaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<ResourceLocation, Definition> loaded, ResourceManager manager, ProfilerFiller profiler) {
|
||||
definitions = loaded;
|
||||
}
|
||||
|
||||
public record Definition(String sender, String subject, String body, Trigger trigger) {
|
||||
}
|
||||
|
||||
public record Trigger(String type, String value) {
|
||||
double chance() {
|
||||
try {
|
||||
return Math.max(0.0D, Math.min(1.0D, Double.parseDouble(value)));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0.0D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-5
@@ -8,10 +8,12 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class AntmailMailbox {
|
||||
public static final int PAGE_SIZE = 20;
|
||||
private final AntmailAddress address;
|
||||
private final List<AntmailMessage> inbox = new ArrayList<>();
|
||||
private final List<AntmailMessage> sent = new ArrayList<>();
|
||||
private final List<AntmailDraft> drafts = new ArrayList<>();
|
||||
private int reportedUnreadCount = -1;
|
||||
|
||||
public AntmailMailbox(AntmailAddress address) {
|
||||
this.address = address;
|
||||
@@ -21,7 +23,7 @@ public final class AntmailMailbox {
|
||||
public List<AntmailMessage> inbox() { return List.copyOf(inbox); }
|
||||
public List<AntmailMessage> sent() { return List.copyOf(sent); }
|
||||
public List<AntmailDraft> drafts() { return List.copyOf(drafts); }
|
||||
public int unreadCount() { return (int) inbox.stream().filter(message -> !message.read()).count(); }
|
||||
public int unreadCount() { return reportedUnreadCount >= 0 ? reportedUnreadCount : (int) inbox.stream().filter(message -> !message.read()).count(); }
|
||||
|
||||
public boolean addIncoming(AntmailMessage message) {
|
||||
if (!address.equals(message.recipient()) || inbox.size() >= AntmailValidation.MAX_MAILBOX_MESSAGES || contains(inbox, message.id())) return false;
|
||||
@@ -45,6 +47,8 @@ public final class AntmailMailbox {
|
||||
public boolean removeDraft(UUID id) { return drafts.removeIf(draft -> draft.id().equals(id)); }
|
||||
public boolean removeInbox(UUID id) { return inbox.removeIf(message -> message.id().equals(id)); }
|
||||
public boolean removeSent(UUID id) { return sent.removeIf(message -> message.id().equals(id)); }
|
||||
public AntmailMessage findSent(UUID id) { return sent.stream().filter(message -> message.id().equals(id)).findFirst().orElse(null); }
|
||||
public AntmailMessage findInbox(UUID id) { return inbox.stream().filter(message -> message.id().equals(id)).findFirst().orElse(null); }
|
||||
|
||||
public boolean markRead(UUID id) {
|
||||
for (AntmailMessage message : inbox) if (message.id().equals(id)) { message.markRead(); return true; }
|
||||
@@ -54,18 +58,47 @@ public final class AntmailMailbox {
|
||||
public AntmailDraft findDraft(UUID id) { return drafts.stream().filter(draft -> draft.id().equals(id)).findFirst().orElse(null); }
|
||||
|
||||
public CompoundTag toTag() {
|
||||
return toTag(true);
|
||||
}
|
||||
|
||||
public CompoundTag toTag(boolean includeMessageAttachments) {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString("Address", address.fullAddress());
|
||||
tag.put("Inbox", messagesToTag(inbox));
|
||||
tag.put("Sent", messagesToTag(sent));
|
||||
tag.put("Inbox", messagesToTag(inbox, includeMessageAttachments));
|
||||
tag.put("Sent", messagesToTag(sent, includeMessageAttachments));
|
||||
ListTag draftTags = new ListTag();
|
||||
for (AntmailDraft draft : drafts) draftTags.add(draft.toTag());
|
||||
tag.put("Drafts", draftTags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public CompoundTag toPageTag(int folder, int page) {
|
||||
int safePage = Math.max(0, page);
|
||||
int start = safePage * PAGE_SIZE;
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString("Address", address.fullAddress());
|
||||
tag.putInt("Folder", folder);
|
||||
tag.putInt("Page", safePage);
|
||||
tag.putInt("PageSize", PAGE_SIZE);
|
||||
tag.putInt("TotalInbox", inbox.size());
|
||||
tag.putInt("TotalSent", sent.size());
|
||||
tag.putInt("UnreadCount", (int) inbox.stream().filter(message -> !message.read()).count());
|
||||
tag.put("Inbox", folder == 0 ? messagesToTag(page(inbox, start), false) : new ListTag());
|
||||
tag.put("Sent", folder == 1 ? messagesToTag(page(sent, start), false) : new ListTag());
|
||||
ListTag draftTags = new ListTag();
|
||||
for (AntmailDraft draft : drafts) draftTags.add(draft.toTag());
|
||||
tag.put("Drafts", draftTags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static <T> List<T> page(List<T> values, int start) {
|
||||
if (start >= values.size()) return List.of();
|
||||
return values.subList(start, Math.min(start + PAGE_SIZE, values.size()));
|
||||
}
|
||||
|
||||
public static AntmailMailbox fromTag(CompoundTag tag) {
|
||||
AntmailMailbox mailbox = new AntmailMailbox(AntmailAddress.parse(tag.getString("Address")));
|
||||
if (tag.contains("UnreadCount")) mailbox.reportedUnreadCount = Math.max(0, tag.getInt("UnreadCount"));
|
||||
readMessages(tag.getList("Inbox", 10), mailbox.inbox);
|
||||
readMessages(tag.getList("Sent", 10), mailbox.sent);
|
||||
ListTag draftTags = tag.getList("Drafts", 10);
|
||||
@@ -75,9 +108,9 @@ public final class AntmailMailbox {
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
private static ListTag messagesToTag(List<AntmailMessage> messages) {
|
||||
private static ListTag messagesToTag(List<AntmailMessage> messages, boolean includeMessageAttachments) {
|
||||
ListTag tags = new ListTag();
|
||||
for (AntmailMessage message : messages) tags.add(message.toTag());
|
||||
for (AntmailMessage message : messages) tags.add(message.toTag(includeMessageAttachments));
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
||||
+17
-4
@@ -16,6 +16,7 @@ public final class AntmailMessage {
|
||||
private static final String CREATED_TAG = "Created";
|
||||
private static final String READ_TAG = "Read";
|
||||
private static final String ATTACHMENTS_TAG = "Attachments";
|
||||
private static final String DELIVERY_STATUS_TAG = "DeliveryStatus";
|
||||
|
||||
private final UUID id;
|
||||
private final AntmailAddress sender;
|
||||
@@ -25,6 +26,7 @@ public final class AntmailMessage {
|
||||
private final long createdAt;
|
||||
private final List<AntmailAttachment> attachments;
|
||||
private boolean read;
|
||||
private String deliveryStatus = "DELIVERED";
|
||||
|
||||
public AntmailMessage(UUID id, AntmailAddress sender, AntmailAddress recipient, String subject, String body, long createdAt, List<AntmailAttachment> attachments, boolean read) {
|
||||
AntmailValidation.Result result = AntmailValidation.validateMessage(sender, recipient, subject, body, attachments);
|
||||
@@ -53,10 +55,16 @@ public final class AntmailMessage {
|
||||
public long createdAt() { return createdAt; }
|
||||
public List<AntmailAttachment> attachments() { return attachments; }
|
||||
public boolean read() { return read; }
|
||||
public String deliveryStatus() { return deliveryStatus; }
|
||||
public void setDeliveryStatus(String status) { deliveryStatus = status == null || status.isBlank() ? "DELIVERED" : status; }
|
||||
public void markRead() { read = true; }
|
||||
public void markUnread() { read = false; }
|
||||
|
||||
public CompoundTag toTag() {
|
||||
return toTag(true);
|
||||
}
|
||||
|
||||
public CompoundTag toTag(boolean includeAttachments) {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString(ID_TAG, id.toString());
|
||||
tag.putString(FROM_TAG, sender.fullAddress());
|
||||
@@ -65,9 +73,12 @@ public final class AntmailMessage {
|
||||
tag.putString(BODY_TAG, body);
|
||||
tag.putLong(CREATED_TAG, createdAt);
|
||||
tag.putBoolean(READ_TAG, read);
|
||||
ListTag attachmentTags = new ListTag();
|
||||
for (AntmailAttachment attachment : attachments) attachmentTags.add(attachment.toTag());
|
||||
tag.put(ATTACHMENTS_TAG, attachmentTags);
|
||||
tag.putString(DELIVERY_STATUS_TAG, deliveryStatus);
|
||||
if (includeAttachments) {
|
||||
ListTag attachmentTags = new ListTag();
|
||||
for (AntmailAttachment attachment : attachments) attachmentTags.add(attachment.toTag());
|
||||
tag.put(ATTACHMENTS_TAG, attachmentTags);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
@@ -75,6 +86,8 @@ public final class AntmailMessage {
|
||||
List<AntmailAttachment> attachments = new ArrayList<>();
|
||||
ListTag attachmentTags = tag.getList(ATTACHMENTS_TAG, 10);
|
||||
for (int index = 0; index < attachmentTags.size(); index++) attachments.add(AntmailAttachment.fromTag(attachmentTags.getCompound(index)));
|
||||
return new AntmailMessage(UUID.fromString(tag.getString(ID_TAG)), AntmailAddress.parse(tag.getString(FROM_TAG)), AntmailAddress.parse(tag.getString(TO_TAG)), tag.getString(SUBJECT_TAG), tag.getString(BODY_TAG), tag.getLong(CREATED_TAG), attachments, tag.getBoolean(READ_TAG));
|
||||
AntmailMessage message = new AntmailMessage(UUID.fromString(tag.getString(ID_TAG)), AntmailAddress.parse(tag.getString(FROM_TAG)), AntmailAddress.parse(tag.getString(TO_TAG)), tag.getString(SUBJECT_TAG), tag.getString(BODY_TAG), tag.getLong(CREATED_TAG), attachments, tag.getBoolean(READ_TAG));
|
||||
message.setDeliveryStatus(tag.getString(DELIVERY_STATUS_TAG));
|
||||
return message;
|
||||
}
|
||||
}
|
||||
+161
-3
@@ -7,18 +7,27 @@ import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class AntmailServerData extends SavedData {
|
||||
private static final String ID = "antarchy_antmail";
|
||||
private static final int MAX_PENDING_MESSAGES = AntmailValidation.MAX_MAILBOX_MESSAGES;
|
||||
private final Map<AntmailAddress, Endpoint> endpoints = new LinkedHashMap<>();
|
||||
private final AntmailMailboxStore mailboxes = new AntmailMailboxStore();
|
||||
private final List<PendingMessage> pending = new ArrayList<>();
|
||||
private final Map<AntmailAddress, java.util.Set<ResourceLocation>> triggered = new LinkedHashMap<>();
|
||||
private final Map<AntmailAddress, Long> mailboxVersions = new LinkedHashMap<>();
|
||||
private final Map<UUID, AntmailAddress> lastUsedAddresses = new LinkedHashMap<>();
|
||||
private long randomMailDay = Long.MIN_VALUE;
|
||||
private int retryTicks;
|
||||
|
||||
public static AntmailServerData create() {
|
||||
return new AntmailServerData();
|
||||
@@ -48,6 +57,35 @@ public final class AntmailServerData extends SavedData {
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
ListTag triggeredTags = tag.getList("Triggered", 10);
|
||||
for (int i = 0; i < triggeredTags.size(); i++) {
|
||||
CompoundTag triggeredTag = triggeredTags.getCompound(i);
|
||||
try {
|
||||
AntmailAddress address = AntmailAddress.parse(triggeredTag.getString("Address"));
|
||||
java.util.Set<ResourceLocation> ids = new java.util.LinkedHashSet<>();
|
||||
ListTag idsTag = triggeredTag.getList("Ids", 8);
|
||||
for (int j = 0; j < idsTag.size(); j++) ids.add(ResourceLocation.parse(idsTag.getString(j)));
|
||||
data.triggered.put(address, ids);
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
ListTag lastUsedTags = tag.getList("LastUsedAddresses", 10);
|
||||
for (int i = 0; i < lastUsedTags.size(); i++) {
|
||||
try {
|
||||
CompoundTag lastUsedTag = lastUsedTags.getCompound(i);
|
||||
data.lastUsedAddresses.put(UUID.fromString(lastUsedTag.getString("Player")), AntmailAddress.parse(lastUsedTag.getString("Address")));
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
data.randomMailDay = tag.contains("RandomMailDay") ? tag.getLong("RandomMailDay") : Long.MIN_VALUE;
|
||||
ListTag versionTags = tag.getList("MailboxVersions", 10);
|
||||
for (int i = 0; i < versionTags.size(); i++) {
|
||||
try {
|
||||
CompoundTag versionTag = versionTags.getCompound(i);
|
||||
data.mailboxVersions.put(AntmailAddress.parse(versionTag.getString("Address")), Math.max(1L, versionTag.getLong("Version")));
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@@ -71,6 +109,33 @@ public final class AntmailServerData extends SavedData {
|
||||
pendingTags.add(pendingTag);
|
||||
}
|
||||
tag.put("Pending", pendingTags);
|
||||
ListTag triggeredTags = new ListTag();
|
||||
for (Map.Entry<AntmailAddress, java.util.Set<ResourceLocation>> entry : triggered.entrySet()) {
|
||||
CompoundTag triggeredTag = new CompoundTag();
|
||||
triggeredTag.putString("Address", entry.getKey().fullAddress());
|
||||
ListTag idsTag = new ListTag();
|
||||
for (ResourceLocation id : entry.getValue()) idsTag.add(net.minecraft.nbt.StringTag.valueOf(id.toString()));
|
||||
triggeredTag.put("Ids", idsTag);
|
||||
triggeredTags.add(triggeredTag);
|
||||
}
|
||||
tag.put("Triggered", triggeredTags);
|
||||
ListTag lastUsedTags = new ListTag();
|
||||
for (Map.Entry<UUID, AntmailAddress> entry : lastUsedAddresses.entrySet()) {
|
||||
CompoundTag lastUsedTag = new CompoundTag();
|
||||
lastUsedTag.putString("Player", entry.getKey().toString());
|
||||
lastUsedTag.putString("Address", entry.getValue().fullAddress());
|
||||
lastUsedTags.add(lastUsedTag);
|
||||
}
|
||||
tag.put("LastUsedAddresses", lastUsedTags);
|
||||
tag.putLong("RandomMailDay", randomMailDay);
|
||||
ListTag versionTags = new ListTag();
|
||||
for (Map.Entry<AntmailAddress, Long> entry : mailboxVersions.entrySet()) {
|
||||
CompoundTag versionTag = new CompoundTag();
|
||||
versionTag.putString("Address", entry.getKey().fullAddress());
|
||||
versionTag.putLong("Version", entry.getValue());
|
||||
versionTags.add(versionTag);
|
||||
}
|
||||
tag.put("MailboxVersions", versionTags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@@ -123,6 +188,40 @@ public final class AntmailServerData extends SavedData {
|
||||
return endpoints.get(address);
|
||||
}
|
||||
|
||||
public synchronized List<AntmailAddress> registeredAddresses() {
|
||||
return List.copyOf(endpoints.keySet());
|
||||
}
|
||||
|
||||
public synchronized AntmailAddress addressFor(ServerPlayer player) {
|
||||
AntmailAddress address = lastUsedAddresses.get(player.getUUID());
|
||||
return address != null && endpoints.containsKey(address) ? address : null;
|
||||
}
|
||||
|
||||
public synchronized AntmailAddress lastUsedAddress(ServerPlayer player) {
|
||||
AntmailAddress address = lastUsedAddresses.get(player.getUUID());
|
||||
return address != null && endpoints.containsKey(address) ? address : null;
|
||||
}
|
||||
|
||||
public synchronized void setLastUsedAddress(ServerPlayer player, AntmailAddress address) {
|
||||
if (address == null) return;
|
||||
lastUsedAddresses.put(player.getUUID(), address);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
public synchronized boolean claimTrigger(AntmailAddress address, ResourceLocation triggerId) {
|
||||
java.util.Set<ResourceLocation> ids = triggered.computeIfAbsent(address, ignored -> new java.util.LinkedHashSet<>());
|
||||
if (!ids.add(triggerId)) return false;
|
||||
setDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean beginRandomMailDay(long day) {
|
||||
if (randomMailDay == day) return false;
|
||||
randomMailDay = day;
|
||||
setDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized AntmailMailbox mailbox(AntmailAddress address) {
|
||||
return mailboxes.get(address);
|
||||
}
|
||||
@@ -133,34 +232,93 @@ public final class AntmailServerData extends SavedData {
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
public synchronized long mailboxVersion(AntmailAddress address) {
|
||||
return mailboxVersions.getOrDefault(address, 1L);
|
||||
}
|
||||
|
||||
public synchronized void touchMailbox(AntmailAddress address) {
|
||||
if (address == null) return;
|
||||
mailboxVersions.put(address, mailboxVersion(address) + 1L);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
public synchronized void drainAddress(MinecraftServer server, ServerLevel level, BlockPos position) {
|
||||
AntmailAddress address = addressAt(level.dimension().location(), position);
|
||||
if (address != null) drain(server, address);
|
||||
}
|
||||
|
||||
public synchronized void drainAvailable(MinecraftServer server) {
|
||||
if (pending.isEmpty() || ++retryTicks < 20) return;
|
||||
retryTicks = 0;
|
||||
List<AntmailAddress> available = new ArrayList<>();
|
||||
for (Map.Entry<AntmailAddress, Endpoint> entry : endpoints.entrySet()) {
|
||||
Endpoint endpoint = entry.getValue();
|
||||
ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, ResourceLocation.parse(endpoint.dimension())));
|
||||
if (level != null && level.hasChunkAt(BlockPos.of(endpoint.position()))) available.add(entry.getKey());
|
||||
}
|
||||
for (AntmailAddress address : available) drain(server, address);
|
||||
}
|
||||
|
||||
public synchronized AntmailDeliveryResult deliver(MinecraftServer server, AntmailMessage message) {
|
||||
if (endpoint(message.recipient()) == null) return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND, message, "address_not_found");
|
||||
if (message == null) return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.MESSAGE_INVALID, null, "missing_message");
|
||||
if (endpoint(message.recipient()) == null) return failedAndRecorded(message, AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND, "address_not_found");
|
||||
AntmailMailbox destination = mailboxes.getOrCreate(message.recipient());
|
||||
if (destination.inbox().size() >= AntmailValidation.MAX_MAILBOX_MESSAGES) return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.MAILBOX_FULL, message, "mailbox_full");
|
||||
if (destination.inbox().size() >= AntmailValidation.MAX_MAILBOX_MESSAGES) return failedAndRecorded(message, AntmailDeliveryResult.Status.MAILBOX_FULL, "mailbox_full");
|
||||
Endpoint target = endpoint(message.recipient());
|
||||
ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, ResourceLocation.parse(target.dimension())));
|
||||
if (level == null || !level.hasChunkAt(BlockPos.of(target.position()))) {
|
||||
if (pending.stream().anyMatch(item -> item.message().id().equals(message.id()))) {
|
||||
return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.DUPLICATE, message, "already_pending");
|
||||
}
|
||||
if (pending.size() >= MAX_PENDING_MESSAGES) {
|
||||
return failedAndRecorded(message, AntmailDeliveryResult.Status.FAILED, "pending_queue_full");
|
||||
}
|
||||
message.setDeliveryStatus("QUEUED");
|
||||
pending.add(new PendingMessage(message.recipient(), message));
|
||||
mailboxes.getOrCreate(message.sender()).addSent(message);
|
||||
touchMailbox(message.recipient());
|
||||
touchMailbox(message.sender());
|
||||
setDirty();
|
||||
return AntmailDeliveryResult.queued(message);
|
||||
}
|
||||
message.setDeliveryStatus("DELIVERED");
|
||||
destination.addIncoming(message);
|
||||
mailboxes.getOrCreate(message.sender()).addSent(message);
|
||||
touchMailbox(message.recipient());
|
||||
touchMailbox(message.sender());
|
||||
setDirty();
|
||||
return AntmailDeliveryResult.delivered(message);
|
||||
}
|
||||
|
||||
private AntmailDeliveryResult failedAndRecorded(AntmailMessage message, AntmailDeliveryResult.Status status, String detail) {
|
||||
message.setDeliveryStatus("FAILED // " + detail.toUpperCase());
|
||||
mailboxes.getOrCreate(message.sender()).addSent(message);
|
||||
touchMailbox(message.sender());
|
||||
setDirty();
|
||||
return AntmailDeliveryResult.failed(status, message, detail);
|
||||
}
|
||||
|
||||
public synchronized void drain(MinecraftServer server, AntmailAddress address) {
|
||||
AntmailMailbox mailbox = mailboxes.getOrCreate(address);
|
||||
final boolean[] delivered = {false};
|
||||
pending.removeIf(pendingMessage -> {
|
||||
if (!pendingMessage.recipient().equals(address) || mailbox.inbox().size() >= AntmailValidation.MAX_MAILBOX_MESSAGES) return false;
|
||||
mailbox.addIncoming(pendingMessage.message());
|
||||
if (!mailbox.addIncoming(pendingMessage.message())) return false;
|
||||
pendingMessage.message().setDeliveryStatus("DELIVERED");
|
||||
delivered[0] = true;
|
||||
return true;
|
||||
});
|
||||
if (delivered[0]) touchMailbox(address);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
public synchronized AntmailDeliveryResult retry(MinecraftServer server, AntmailAddress sender, UUID messageId) {
|
||||
AntmailMessage message = mailboxes.getOrCreate(sender).findSent(messageId);
|
||||
if (message == null) return null;
|
||||
pending.removeIf(item -> item.message().id().equals(messageId));
|
||||
return deliver(server, message);
|
||||
}
|
||||
|
||||
public record Endpoint(String dimension, long position) {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailServerData;
|
||||
import com.craisinlord.antarchy.content.client.AntarchyClientHooks;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -99,6 +100,9 @@ public final class ComputerBlock extends BaseEntityBlock {
|
||||
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof ComputerBlockEntity computer) {
|
||||
computer.dropDisks(level, pos);
|
||||
if (level instanceof net.minecraft.server.level.ServerLevel serverLevel) {
|
||||
AntmailServerData.access(serverLevel.getServer()).unregister(serverLevel.dimension().location(), pos);
|
||||
}
|
||||
}
|
||||
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||
}
|
||||
|
||||
+78
@@ -41,6 +41,12 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
private static final String AUTHENTICATED_TAG = "Authenticated";
|
||||
private static final String AUTHENTICATED_UNTIL_TAG = "AuthenticatedUntil";
|
||||
private static final String DESKTOP_TAG = "Desktop";
|
||||
private static final String BASILISK_SCORE_TAG = "BasiliskScore";
|
||||
private static final String BASILISK_HIGH_SCORE_TAG = "BasiliskHighScore";
|
||||
private static final String ANTMAN_SCORE_TAG = "AntmanScore";
|
||||
private static final String ANTMAN_HIGH_SCORE_TAG = "AntmanHighScore";
|
||||
private static final String BLOCKLE_DAY_TAG = "BlockleDay";
|
||||
private static final String BLOCKLE_GUESSES_TAG = "BlockleGuesses";
|
||||
private static final long AUTHENTICATION_TIMEOUT = 6000L;
|
||||
private final Set<ResourceLocation> physicalDiskIds = new LinkedHashSet<>();
|
||||
private final ComputerFileSystem fileSystem = new ComputerFileSystem();
|
||||
@@ -56,6 +62,12 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
private long authenticatedUntil;
|
||||
private java.util.UUID activeUser;
|
||||
private boolean lastActive;
|
||||
private int basiliskScore;
|
||||
private int basiliskHighScore;
|
||||
private int antmanScore;
|
||||
private int antmanHighScore;
|
||||
private long blockleDay = -1L;
|
||||
private final List<String> blockleGuesses = new ArrayList<>();
|
||||
|
||||
public ComputerBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<ComputerBlockEntity>> type) {
|
||||
super(type.get(), pos, state);
|
||||
@@ -256,6 +268,50 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
return desktopState;
|
||||
}
|
||||
|
||||
public int basiliskScore() {
|
||||
return basiliskScore;
|
||||
}
|
||||
|
||||
public int basiliskHighScore() {
|
||||
return basiliskHighScore;
|
||||
}
|
||||
|
||||
public void setBasiliskScore(int score) {
|
||||
basiliskScore = Math.max(0, score);
|
||||
basiliskHighScore = Math.max(basiliskHighScore, basiliskScore);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public int antmanScore() { return antmanScore; }
|
||||
public int antmanHighScore() { return antmanHighScore; }
|
||||
public void setAntmanScore(int score) {
|
||||
antmanScore = Math.max(0, score);
|
||||
antmanHighScore = Math.max(antmanHighScore, antmanScore);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public long blockleDay() {
|
||||
return blockleDay;
|
||||
}
|
||||
|
||||
public List<String> blockleGuesses() {
|
||||
return List.copyOf(blockleGuesses);
|
||||
}
|
||||
|
||||
public void resetBlockle(long day) {
|
||||
if (blockleDay == day) return;
|
||||
blockleDay = day;
|
||||
blockleGuesses.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public boolean addBlockleGuess(String guess) {
|
||||
if (blockleGuesses.size() >= 6) return false;
|
||||
blockleGuesses.add(guess);
|
||||
setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unlockWallpaper(ResourceLocation id) {
|
||||
return serverDesktopMutation(() -> desktopState.unlockWallpaper(id));
|
||||
}
|
||||
@@ -346,6 +402,17 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
password = tag.getString(PASSWORD_TAG);
|
||||
fileSystem.load(tag, registries);
|
||||
desktopState.load(tag.getCompound(DESKTOP_TAG));
|
||||
basiliskScore = tag.getInt(BASILISK_SCORE_TAG);
|
||||
basiliskHighScore = tag.getInt(BASILISK_HIGH_SCORE_TAG);
|
||||
antmanScore = tag.getInt(ANTMAN_SCORE_TAG);
|
||||
antmanHighScore = tag.getInt(ANTMAN_HIGH_SCORE_TAG);
|
||||
blockleDay = tag.contains(BLOCKLE_DAY_TAG) ? tag.getLong(BLOCKLE_DAY_TAG) : -1L;
|
||||
blockleGuesses.clear();
|
||||
ListTag blockle = tag.getList(BLOCKLE_GUESSES_TAG, 8);
|
||||
for (int index = 0; index < blockle.size(); index++) {
|
||||
String guess = blockle.getString(index);
|
||||
if (guess.length() == 5 && !blockleGuesses.contains(guess) && blockleGuesses.size() < 6) blockleGuesses.add(guess);
|
||||
}
|
||||
authenticated = false;
|
||||
authenticatedUntil = 0L;
|
||||
activeUser = null;
|
||||
@@ -363,6 +430,14 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
tag.putString(PASSWORD_TAG, password);
|
||||
fileSystem.save(tag, registries);
|
||||
tag.put(DESKTOP_TAG, desktopState.save());
|
||||
tag.putInt(BASILISK_SCORE_TAG, basiliskScore);
|
||||
tag.putInt(BASILISK_HIGH_SCORE_TAG, basiliskHighScore);
|
||||
tag.putInt(ANTMAN_SCORE_TAG, antmanScore);
|
||||
tag.putInt(ANTMAN_HIGH_SCORE_TAG, antmanHighScore);
|
||||
tag.putLong(BLOCKLE_DAY_TAG, blockleDay);
|
||||
ListTag blockle = new ListTag();
|
||||
for (String guess : blockleGuesses) blockle.add(StringTag.valueOf(guess));
|
||||
tag.put(BLOCKLE_GUESSES_TAG, blockle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -370,6 +445,9 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putBoolean("HasPassword", hasPassword());
|
||||
tag.putBoolean(AUTHENTICATED_TAG, isAuthenticated());
|
||||
ListTag disks = new ListTag();
|
||||
for (ResourceLocation diskId : physicalDiskIds) disks.add(StringTag.valueOf(diskId.toString()));
|
||||
tag.put(DISKS_TAG, disks);
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.block.entity;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.block.UnderVaultBlock;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.particles.SimpleParticleType;
|
||||
@@ -22,6 +23,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -44,6 +46,14 @@ public final class UnderVaultBlockEntity extends BlockEntity {
|
||||
|
||||
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, UnderVaultBlockEntity blockEntity) {
|
||||
blockEntity.vaultData.setLevel(level);
|
||||
// The vanilla vault detector can miss this ceiling-mounted wrapper because its
|
||||
// block state has a separate render-facing property. Keep the state synchronized
|
||||
// with the same four-block activation range before running vanilla's state machine.
|
||||
if (state.getValue(UnderVaultBlock.STATE) == VaultState.INACTIVE
|
||||
&& !level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.0D), player -> !player.isSpectator()).isEmpty()) {
|
||||
state = state.setValue(UnderVaultBlock.STATE, VaultState.ACTIVE);
|
||||
level.setBlockAndUpdate(pos, state);
|
||||
}
|
||||
VaultBlockEntity.Server.tick(level, pos, state, blockEntity.vaultData.getConfig(), blockEntity.vaultData.getServerData(), blockEntity.vaultData.getSharedData());
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -40,6 +40,7 @@ import java.util.function.Supplier;
|
||||
|
||||
public final class UndertrialSpawnerBlockEntity extends BlockEntity implements TrialSpawner.StateAccessor {
|
||||
private static final String SPAWNER_TAG = "trial_spawner";
|
||||
private static final int DEFAULT_TARGET_COOLDOWN_LENGTH = 36000;
|
||||
private static final int EFFECT_DURATION = 3600;
|
||||
private TrialSpawner trialSpawner;
|
||||
private TrialSpawnerState previousClientState;
|
||||
@@ -194,12 +195,17 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T
|
||||
|
||||
private TrialSpawner createRewardlessSpawner(TrialSpawner source) {
|
||||
TrialSpawner rewardless = new TrialSpawner(withoutLootTables(source.getNormalConfig()), withoutLootTables(source.getOminousConfig()),
|
||||
source.getData(), source.getRequiredPlayerRange(), source.getTargetCooldownLength(), this,
|
||||
source.getData(), source.getRequiredPlayerRange(), targetCooldownLength(source), this,
|
||||
source.getPlayerDetector(), source.getEntitySelector());
|
||||
rewardless.overridePeacefulAndMobSpawnRule();
|
||||
return rewardless;
|
||||
}
|
||||
|
||||
private static int targetCooldownLength(TrialSpawner source) {
|
||||
int targetCooldownLength = source.getTargetCooldownLength();
|
||||
return targetCooldownLength > 0 ? targetCooldownLength : DEFAULT_TARGET_COOLDOWN_LENGTH;
|
||||
}
|
||||
|
||||
private static TrialSpawnerConfig withoutLootTables(TrialSpawnerConfig config) {
|
||||
return new TrialSpawnerConfig(config.spawnRange(), config.totalMobs(), config.simultaneousMobs(),
|
||||
config.totalMobsAddedPerPlayer(), config.simultaneousMobsAddedPerPlayer(), config.ticksBetweenSpawn(),
|
||||
|
||||
+48
@@ -1,22 +1,70 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.network.AntmailResultPayload;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailMessage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class AntmailClientState {
|
||||
private static final int MAX_CACHED_DETAILS_PER_MAILBOX = 8;
|
||||
private static final Map<BlockPos, AntmailResultPayload> RESULTS = new ConcurrentHashMap<>();
|
||||
private static final Map<BlockPos, AntmailResultPayload> MAILBOXES = new ConcurrentHashMap<>();
|
||||
private static final Map<BlockPos, Long> VERSIONS = new ConcurrentHashMap<>();
|
||||
private static final Map<BlockPos, Map<java.util.UUID, AntmailMessage>> DETAILS = new ConcurrentHashMap<>();
|
||||
|
||||
private AntmailClientState() {
|
||||
}
|
||||
|
||||
public static void update(AntmailResultPayload payload) {
|
||||
RESULTS.put(payload.pos(), payload);
|
||||
if ("unchanged".equals(payload.detail())) {
|
||||
VERSIONS.put(payload.pos(), payload.version());
|
||||
return;
|
||||
} else if ("message_detail".equals(payload.detail()) && !payload.data().isBlank()) {
|
||||
try {
|
||||
AntmailMessage message = AntmailMessage.fromTag(com.craisinlord.antarchy.content.antmail.AntmailWire.decodeTag(payload.data()));
|
||||
Map<java.util.UUID, AntmailMessage> details = DETAILS.computeIfAbsent(payload.pos(), ignored -> new ConcurrentHashMap<>());
|
||||
details.put(message.id(), message);
|
||||
while (details.size() > MAX_CACHED_DETAILS_PER_MAILBOX) {
|
||||
java.util.UUID oldest = details.keySet().iterator().next();
|
||||
details.remove(oldest);
|
||||
}
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
} else if (!payload.data().isBlank()) {
|
||||
MAILBOXES.put(payload.pos(), payload);
|
||||
VERSIONS.put(payload.pos(), payload.version());
|
||||
} else {
|
||||
// A failed or unconfigured response is authoritative; never show a
|
||||
// mailbox from a previous address/computer at this position.
|
||||
MAILBOXES.remove(payload.pos());
|
||||
VERSIONS.remove(payload.pos());
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear(BlockPos pos) {
|
||||
RESULTS.remove(pos);
|
||||
MAILBOXES.remove(pos);
|
||||
VERSIONS.remove(pos);
|
||||
DETAILS.remove(pos);
|
||||
}
|
||||
|
||||
public static AntmailResultPayload get(BlockPos pos) {
|
||||
return RESULTS.get(pos);
|
||||
}
|
||||
|
||||
public static AntmailResultPayload getMailbox(BlockPos pos) {
|
||||
return MAILBOXES.get(pos);
|
||||
}
|
||||
|
||||
public static long getVersion(BlockPos pos) {
|
||||
return VERSIONS.getOrDefault(pos, 0L);
|
||||
}
|
||||
|
||||
public static AntmailMessage getMessage(BlockPos pos, java.util.UUID id) {
|
||||
Map<java.util.UUID, AntmailMessage> details = DETAILS.get(pos);
|
||||
return details == null ? null : details.get(id);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class BlockleClientState {
|
||||
private static final Map<BlockPos, ComputerAccessResultPayload> RESULTS = new ConcurrentHashMap<>();
|
||||
|
||||
private BlockleClientState() {
|
||||
}
|
||||
|
||||
public static void update(ComputerAccessResultPayload result) {
|
||||
RESULTS.put(result.pos(), result);
|
||||
}
|
||||
|
||||
public static ComputerAccessResultPayload get(BlockPos pos) {
|
||||
return RESULTS.get(pos);
|
||||
}
|
||||
|
||||
public static void clear(BlockPos pos) {
|
||||
RESULTS.remove(pos);
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -13,10 +13,19 @@ public final class ComputerAccessClientState {
|
||||
}
|
||||
|
||||
public static void update(ComputerAccessResultPayload result) {
|
||||
RESULTS.put(result.pos(), result);
|
||||
if (result.data().startsWith("18\u0000") || result.data().startsWith("19\u0000")) {
|
||||
BlockleClientState.update(result);
|
||||
}
|
||||
// File, terminal, desktop, and wallpaper responses carry their own operation
|
||||
// status. They must not replace the last authoritative login state.
|
||||
if (result.data().isBlank()) RESULTS.put(result.pos(), result);
|
||||
ComputerFileSystemClientState.update(result);
|
||||
}
|
||||
|
||||
public static void clear(BlockPos pos) {
|
||||
RESULTS.remove(pos);
|
||||
}
|
||||
|
||||
public static ComputerAccessResultPayload get(BlockPos pos) {
|
||||
return RESULTS.get(pos);
|
||||
}
|
||||
|
||||
+15
@@ -29,6 +29,13 @@ public final class ComputerFileSystemClientState {
|
||||
state.success = payload.result() == ComputerAccessResultPayload.SUCCESS;
|
||||
state.error = envelope[1];
|
||||
if (state.success) state.error = "";
|
||||
if (action == ComputerAccessPayload.FILE_CREATE || action == ComputerAccessPayload.FILE_SAVE
|
||||
|| action == ComputerAccessPayload.FILE_DELETE || action == ComputerAccessPayload.FILE_MOVE) {
|
||||
state.lastMutationAction = action;
|
||||
state.lastMutationPath = envelope[2];
|
||||
state.lastMutationSuccess = state.success;
|
||||
state.lastMutationError = envelope[1];
|
||||
}
|
||||
if (action == ComputerAccessPayload.FILE_LIST) {
|
||||
state.files = new ArrayList<>();
|
||||
if (state.success && !envelope[2].isEmpty()) state.files.addAll(List.of(envelope[2].split("\\n")));
|
||||
@@ -59,11 +66,19 @@ public final class ComputerFileSystemClientState {
|
||||
private String openedContents = "";
|
||||
private String error = "";
|
||||
private boolean success;
|
||||
private int lastMutationAction = -1;
|
||||
private String lastMutationPath = "";
|
||||
private boolean lastMutationSuccess;
|
||||
private String lastMutationError = "";
|
||||
|
||||
public List<String> files() { return List.copyOf(files); }
|
||||
public String openedPath() { return openedPath; }
|
||||
public String openedContents() { return openedContents; }
|
||||
public String error() { return error; }
|
||||
public boolean success() { return success; }
|
||||
public int lastMutationAction() { return lastMutationAction; }
|
||||
public String lastMutationPath() { return lastMutationPath; }
|
||||
public boolean lastMutationSuccess() { return lastMutationSuccess; }
|
||||
public String lastMutationError() { return lastMutationError; }
|
||||
}
|
||||
}
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class AntFarmGame {
|
||||
public enum Phase { READY, PLAYING, WON, LOST }
|
||||
public enum Direction {
|
||||
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0), NONE(0, 0);
|
||||
|
||||
private final int dx;
|
||||
private final int dy;
|
||||
|
||||
Direction(int dx, int dy) {
|
||||
this.dx = dx;
|
||||
this.dy = dy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] MAP = {
|
||||
"###################",
|
||||
"#.................#",
|
||||
"#.###.#####.#####.#",
|
||||
"#.................#",
|
||||
"#.#####.###.#####.#",
|
||||
"#.................#",
|
||||
"#.#####.###.#####.#",
|
||||
"#.................#",
|
||||
"#.#####.###.#####.#",
|
||||
"#.................#",
|
||||
"###################"
|
||||
};
|
||||
private static final int TICKS_PER_TILE = 4;
|
||||
private static final int PLAYER_START_X = 1;
|
||||
private static final int PLAYER_START_Y = 1;
|
||||
private static final int ENEMY_START_X = 17;
|
||||
private static final int ENEMY_START_Y = 9;
|
||||
|
||||
private final boolean[][] pellets = new boolean[MAP.length][MAP[0].length()];
|
||||
private Phase phase = Phase.READY;
|
||||
private Direction playerDirection = Direction.NONE;
|
||||
private Direction requestedDirection = Direction.NONE;
|
||||
private Direction enemyDirection = Direction.LEFT;
|
||||
private int playerX;
|
||||
private int playerY;
|
||||
private int enemyX;
|
||||
private int enemyY;
|
||||
private int movementTicks;
|
||||
private int score;
|
||||
private int remainingFood;
|
||||
|
||||
public AntFarmGame() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
phase = Phase.READY;
|
||||
playerDirection = Direction.NONE;
|
||||
requestedDirection = Direction.NONE;
|
||||
enemyDirection = Direction.LEFT;
|
||||
playerX = PLAYER_START_X;
|
||||
playerY = PLAYER_START_Y;
|
||||
enemyX = ENEMY_START_X;
|
||||
enemyY = ENEMY_START_Y;
|
||||
movementTicks = 0;
|
||||
score = 0;
|
||||
remainingFood = 0;
|
||||
for (int y = 0; y < MAP.length; y++) {
|
||||
for (int x = 0; x < MAP[y].length(); x++) {
|
||||
pellets[y][x] = MAP[y].charAt(x) == '.';
|
||||
if (pellets[y][x]) remainingFood++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (phase == Phase.READY) phase = Phase.PLAYING;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (phase != Phase.PLAYING) return;
|
||||
movementTicks++;
|
||||
if (movementTicks < TICKS_PER_TILE) return;
|
||||
movementTicks = 0;
|
||||
movePlayer();
|
||||
if (phase != Phase.PLAYING) return;
|
||||
moveEnemy();
|
||||
if (playerX == enemyX && playerY == enemyY) phase = Phase.LOST;
|
||||
}
|
||||
|
||||
public void setRequestedDirection(Direction direction) {
|
||||
if (direction != null && direction != Direction.NONE) requestedDirection = direction;
|
||||
}
|
||||
|
||||
public Phase phase() { return phase; }
|
||||
public int score() { return score; }
|
||||
public int remainingFood() { return remainingFood; }
|
||||
public int width() { return MAP[0].length(); }
|
||||
public int height() { return MAP.length; }
|
||||
public int playerX() { return playerX; }
|
||||
public int playerY() { return playerY; }
|
||||
public int enemyX() { return enemyX; }
|
||||
public int enemyY() { return enemyY; }
|
||||
public boolean hasPellet(int x, int y) { return pellets[y][x]; }
|
||||
public boolean isWall(int x, int y) { return MAP[y].charAt(x) == '#'; }
|
||||
|
||||
private void movePlayer() {
|
||||
if (canMove(playerX, playerY, requestedDirection)) playerDirection = requestedDirection;
|
||||
if (canMove(playerX, playerY, playerDirection)) {
|
||||
playerX += playerDirection.dx;
|
||||
playerY += playerDirection.dy;
|
||||
}
|
||||
if (pellets[playerY][playerX]) {
|
||||
pellets[playerY][playerX] = false;
|
||||
remainingFood--;
|
||||
score += 10;
|
||||
if (remainingFood == 0) phase = Phase.WON;
|
||||
}
|
||||
}
|
||||
|
||||
private void moveEnemy() {
|
||||
List<Direction> directions = legalDirections(enemyX, enemyY);
|
||||
if (directions.isEmpty()) return;
|
||||
Direction reverse = opposite(enemyDirection);
|
||||
if (directions.size() > 1) directions.remove(reverse);
|
||||
Direction best = directions.get(0);
|
||||
int bestDistance = distanceAfter(best, playerX, playerY, enemyX, enemyY);
|
||||
for (Direction direction : directions) {
|
||||
int distance = distanceAfter(direction, playerX, playerY, enemyX, enemyY);
|
||||
if (distance < bestDistance) {
|
||||
best = direction;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
enemyDirection = best;
|
||||
enemyX += best.dx;
|
||||
enemyY += best.dy;
|
||||
}
|
||||
|
||||
private int distanceAfter(Direction direction, int targetX, int targetY, int originX, int originY) {
|
||||
return Math.abs(originX + direction.dx - targetX) + Math.abs(originY + direction.dy - targetY);
|
||||
}
|
||||
|
||||
private List<Direction> legalDirections(int x, int y) {
|
||||
List<Direction> result = new ArrayList<>(4);
|
||||
for (Direction direction : new Direction[]{Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT}) {
|
||||
if (canMove(x, y, direction)) result.add(direction);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean canMove(int x, int y, Direction direction) {
|
||||
if (direction == Direction.NONE) return false;
|
||||
int nextX = x + direction.dx;
|
||||
int nextY = y + direction.dy;
|
||||
return nextY >= 0 && nextY < MAP.length && nextX >= 0 && nextX < MAP[nextY].length() && !isWall(nextX, nextY);
|
||||
}
|
||||
|
||||
private Direction opposite(Direction direction) {
|
||||
return switch (direction) {
|
||||
case UP -> Direction.DOWN;
|
||||
case DOWN -> Direction.UP;
|
||||
case LEFT -> Direction.RIGHT;
|
||||
case RIGHT -> Direction.LEFT;
|
||||
case NONE -> Direction.NONE;
|
||||
};
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
public final class AntFarmInput {
|
||||
private AntFarmInput() {
|
||||
}
|
||||
|
||||
public static AntFarmGame.Direction directionForKey(int keyCode) {
|
||||
return switch (keyCode) {
|
||||
case GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_UP -> AntFarmGame.Direction.UP;
|
||||
case GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_DOWN -> AntFarmGame.Direction.DOWN;
|
||||
case GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_LEFT -> AntFarmGame.Direction.LEFT;
|
||||
case GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_RIGHT -> AntFarmGame.Direction.RIGHT;
|
||||
default -> AntFarmGame.Direction.NONE;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isStartOrRetry(int keyCode) {
|
||||
return keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER;
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
|
||||
public final class AntFarmProgram {
|
||||
private final AntFarmGame game = new AntFarmGame();
|
||||
private boolean gameInstalled;
|
||||
|
||||
public AntFarmProgram(boolean gameInstalled) {
|
||||
this.gameInstalled = gameInstalled;
|
||||
}
|
||||
|
||||
public void setGameInstalled(boolean gameInstalled) {
|
||||
this.gameInstalled = gameInstalled;
|
||||
if (!gameInstalled) game.reset();
|
||||
}
|
||||
|
||||
public boolean isGameInstalled() { return gameInstalled; }
|
||||
public AntFarmGame game() { return game; }
|
||||
|
||||
public void tick() {
|
||||
if (gameInstalled) game.tick();
|
||||
}
|
||||
|
||||
public boolean keyPressed(int keyCode) {
|
||||
if (!gameInstalled) return false;
|
||||
AntFarmGame.Direction direction = AntFarmInput.directionForKey(keyCode);
|
||||
if (direction != AntFarmGame.Direction.NONE) {
|
||||
if (game.phase() == AntFarmGame.Phase.READY) game.start();
|
||||
game.setRequestedDirection(direction);
|
||||
return true;
|
||||
}
|
||||
if (AntFarmInput.isStartOrRetry(keyCode)) {
|
||||
if (game.phase() == AntFarmGame.Phase.READY) game.start();
|
||||
else if (game.phase() == AntFarmGame.Phase.WON || game.phase() == AntFarmGame.Phase.LOST) {
|
||||
game.reset();
|
||||
game.start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void render(GuiGraphics graphics, Font font, int x, int y, int width, int height) {
|
||||
if (!gameInstalled) {
|
||||
graphics.fill(x, y, x + width, y + height, 0xFF020602);
|
||||
graphics.drawString(font, "ANT FARM NOT INSTALLED", x + 12, y + height / 2, 0xFF65FF65, false);
|
||||
return;
|
||||
}
|
||||
AntFarmRenderer.render(graphics, font, game, x, y, width, height);
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
public final class AntFarmRenderer {
|
||||
private static final int BLACK = 0xFF020602;
|
||||
private static final int DARK_GREEN = 0xFF123012;
|
||||
private static final int GREEN = 0xFF65FF65;
|
||||
private static final int PALE_GREEN = 0xFFB8FFB8;
|
||||
|
||||
private AntFarmRenderer() {
|
||||
}
|
||||
|
||||
public static void render(GuiGraphics graphics, Font font, AntFarmGame game, int x, int y, int width, int height) {
|
||||
graphics.fill(x, y, x + width, y + height, BLACK);
|
||||
int hudHeight = 18;
|
||||
int boardX = x + 5;
|
||||
int boardY = y + hudHeight + 4;
|
||||
int boardWidth = width - 10;
|
||||
int boardHeight = height - hudHeight - 9;
|
||||
int tileSize = Math.max(1, Math.min(boardWidth / game.width(), boardHeight / game.height()));
|
||||
int mazeWidth = tileSize * game.width();
|
||||
int mazeHeight = tileSize * game.height();
|
||||
int mazeX = boardX + Math.max(0, (boardWidth - mazeWidth) / 2);
|
||||
int mazeY = boardY + Math.max(0, (boardHeight - mazeHeight) / 2);
|
||||
graphics.drawString(font, Component.literal("SCORE " + String.format("%06d", game.score())), x + 6, y + 4, GREEN, false);
|
||||
graphics.drawString(font, Component.literal("FOOD " + String.format("%03d", game.remainingFood())), x + width - 76, y + 4, PALE_GREEN, false);
|
||||
for (int gridY = 0; gridY < game.height(); gridY++) {
|
||||
for (int gridX = 0; gridX < game.width(); gridX++) {
|
||||
int cellX = mazeX + gridX * tileSize;
|
||||
int cellY = mazeY + gridY * tileSize;
|
||||
if (game.isWall(gridX, gridY)) {
|
||||
graphics.fill(cellX, cellY, cellX + tileSize, cellY + tileSize, DARK_GREEN);
|
||||
} else if (game.hasPellet(gridX, gridY)) {
|
||||
int pelletSize = Math.max(1, tileSize / 4);
|
||||
int offset = (tileSize - pelletSize) / 2;
|
||||
graphics.fill(cellX + offset, cellY + offset, cellX + offset + pelletSize, cellY + offset + pelletSize, PALE_GREEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
drawAnt(graphics, mazeX + game.playerX() * tileSize, mazeY + game.playerY() * tileSize, tileSize, GREEN);
|
||||
drawAnt(graphics, mazeX + game.enemyX() * tileSize, mazeY + game.enemyY() * tileSize, tileSize, PALE_GREEN);
|
||||
if (game.phase() != AntFarmGame.Phase.PLAYING) {
|
||||
String message = switch (game.phase()) {
|
||||
case READY -> "ANT FARM // PRESS ENTER TO START";
|
||||
case WON -> "COLONY FED // ENTER TO PLAY AGAIN";
|
||||
case LOST -> "COLONY COMPROMISED // ENTER TO RETRY";
|
||||
case PLAYING -> "";
|
||||
};
|
||||
int messageWidth = font.width(message);
|
||||
graphics.fill(x + 8, y + height / 2 - 12, x + width - 8, y + height / 2 + 12, BLACK);
|
||||
graphics.drawString(font, Component.literal(message), x + Math.max(8, (width - messageWidth) / 2), y + height / 2 - 4, GREEN, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawAnt(GuiGraphics graphics, int x, int y, int tileSize, int color) {
|
||||
int inset = Math.max(1, tileSize / 4);
|
||||
int center = x + tileSize / 2;
|
||||
graphics.fill(center - inset, y + inset, center + inset + 1, y + tileSize - inset, color);
|
||||
graphics.fill(x + inset, y + tileSize / 2 - inset, x + tileSize - inset, y + tileSize / 2 + inset + 1, color);
|
||||
if (tileSize >= 5) graphics.fill(center - inset, y + inset, center + inset + 1, y + inset + 1, BLACK);
|
||||
}
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public final class AntmanGame {
|
||||
public enum Phase { READY, PLAYING, LOST }
|
||||
public enum Direction { UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
|
||||
final int dx;
|
||||
final int dy;
|
||||
Direction(int dx, int dy) { this.dx = dx; this.dy = dy; }
|
||||
Direction opposite() { return switch (this) { case UP -> DOWN; case DOWN -> UP; case LEFT -> RIGHT; case RIGHT -> LEFT; }; }
|
||||
}
|
||||
public enum EnemyKind { CREEPER, ZOMBIE }
|
||||
public record Enemy(EnemyKind kind, double x, double y, Direction direction) { }
|
||||
|
||||
private static final int WIDTH = 28;
|
||||
private static final int HEIGHT = 20;
|
||||
private static final double SPEED = 0.075D;
|
||||
private final Random random = new Random();
|
||||
private final boolean[][] walls = new boolean[HEIGHT][WIDTH];
|
||||
private final List<Enemy> enemies = new ArrayList<>();
|
||||
private Direction direction = Direction.RIGHT;
|
||||
private Direction requested = Direction.RIGHT;
|
||||
private Phase phase = Phase.READY;
|
||||
private double playerX;
|
||||
private double playerY;
|
||||
private int diamondX;
|
||||
private int diamondY;
|
||||
private int cornDogX;
|
||||
private int cornDogY;
|
||||
private int cornDogTicks;
|
||||
private int cornDogSpawnTicks;
|
||||
private int score;
|
||||
private int highScore;
|
||||
private int lives = 3;
|
||||
private int ticks;
|
||||
private boolean collectedDiamond;
|
||||
|
||||
public AntmanGame(int highScore, int score) {
|
||||
this.highScore = Math.max(0, highScore);
|
||||
reset(score);
|
||||
}
|
||||
|
||||
public void reset() { reset(0); }
|
||||
|
||||
public void reset(int currentScore) {
|
||||
generateMaze();
|
||||
playerX = 1.5D;
|
||||
playerY = 1.5D;
|
||||
direction = Direction.RIGHT;
|
||||
requested = Direction.RIGHT;
|
||||
phase = Phase.READY;
|
||||
score = Math.max(0, currentScore);
|
||||
lives = 3;
|
||||
ticks = 0;
|
||||
cornDogTicks = 0;
|
||||
cornDogSpawnTicks = 0;
|
||||
placeDiamond();
|
||||
cornDogX = -1;
|
||||
cornDogY = -1;
|
||||
enemies.clear();
|
||||
enemies.add(new Enemy(EnemyKind.CREEPER, WIDTH - 2.5D, HEIGHT - 2.5D, Direction.LEFT));
|
||||
enemies.add(new Enemy(EnemyKind.ZOMBIE, WIDTH - 2.5D, 1.5D, Direction.DOWN));
|
||||
}
|
||||
|
||||
public void start() { if (phase == Phase.READY) phase = Phase.PLAYING; }
|
||||
|
||||
public boolean tick() {
|
||||
collectedDiamond = false;
|
||||
if (phase != Phase.PLAYING) return false;
|
||||
ticks++;
|
||||
if (cornDogTicks > 0) cornDogTicks--;
|
||||
if (cornDogX < 0 && ++cornDogSpawnTicks >= 240) {
|
||||
placeCornDog();
|
||||
cornDogSpawnTicks = 0;
|
||||
}
|
||||
updatePlayer();
|
||||
updateEnemies();
|
||||
if (near(playerX, playerY, diamondX + 0.5D, diamondY + 0.5D)) {
|
||||
score += 10;
|
||||
highScore = Math.max(highScore, score);
|
||||
collectedDiamond = true;
|
||||
placeDiamond();
|
||||
}
|
||||
if (cornDogX >= 0 && near(playerX, playerY, cornDogX + 0.5D, cornDogY + 0.5D)) {
|
||||
cornDogTicks = 200;
|
||||
cornDogX = -1;
|
||||
cornDogY = -1;
|
||||
cornDogSpawnTicks = 0;
|
||||
}
|
||||
for (int index = 0; index < enemies.size(); index++) {
|
||||
Enemy enemy = enemies.get(index);
|
||||
if (!near(playerX, playerY, enemy.x(), enemy.y())) continue;
|
||||
if (cornDogTicks > 0) {
|
||||
score += 100;
|
||||
highScore = Math.max(highScore, score);
|
||||
enemies.set(index, new Enemy(enemy.kind(), WIDTH - 2.5D, enemy.kind() == EnemyKind.CREEPER ? HEIGHT - 2.5D : 1.5D, Direction.LEFT));
|
||||
} else {
|
||||
lives--;
|
||||
if (lives <= 0) phase = Phase.LOST;
|
||||
else {
|
||||
playerX = 1.5D;
|
||||
playerY = 1.5D;
|
||||
direction = Direction.RIGHT;
|
||||
requested = Direction.RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction) { if (direction != null) requested = direction; }
|
||||
public Phase phase() { return phase; }
|
||||
public int score() { return score; }
|
||||
public int highScore() { return highScore; }
|
||||
public int lives() { return lives; }
|
||||
public int width() { return WIDTH; }
|
||||
public int height() { return HEIGHT; }
|
||||
public double playerX() { return playerX; }
|
||||
public double playerY() { return playerY; }
|
||||
public int diamondX() { return diamondX; }
|
||||
public int diamondY() { return diamondY; }
|
||||
public int cornDogX() { return cornDogX; }
|
||||
public int cornDogY() { return cornDogY; }
|
||||
public int cornDogTicks() { return cornDogTicks; }
|
||||
public int animationFrame() { return (ticks / 5) % 2; }
|
||||
public Direction direction() { return direction; }
|
||||
public List<Enemy> enemies() { return Collections.unmodifiableList(enemies); }
|
||||
public boolean collectedDiamond() { return collectedDiamond; }
|
||||
public boolean isWall(int x, int y) { return x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT || walls[y][x]; }
|
||||
|
||||
private void updatePlayer() {
|
||||
if (atCenter(playerX) && atCenter(playerY) && canMove(playerX, playerY, requested)) {
|
||||
playerX = Math.round(playerX - 0.5D) + 0.5D;
|
||||
playerY = Math.round(playerY - 0.5D) + 0.5D;
|
||||
direction = requested;
|
||||
}
|
||||
double nextX = playerX + direction.dx * SPEED;
|
||||
double nextY = playerY + direction.dy * SPEED;
|
||||
if (canMove(nextX, nextY, direction)) { playerX = nextX; playerY = nextY; }
|
||||
else { playerX = Math.round(playerX - 0.5D) + 0.5D; playerY = Math.round(playerY - 0.5D) + 0.5D; }
|
||||
}
|
||||
|
||||
private void updateEnemies() {
|
||||
for (int index = 0; index < enemies.size(); index++) {
|
||||
Enemy enemy = enemies.get(index);
|
||||
Direction next = enemy.direction();
|
||||
if (atCenter(enemy.x()) && atCenter(enemy.y())) next = enemy.kind() == EnemyKind.CREEPER ? chaseDirection(enemy.x(), enemy.y(), next) : zombieDirection(enemy.x(), enemy.y(), next);
|
||||
double nextX = enemy.x() + next.dx * (enemy.kind() == EnemyKind.CREEPER ? 0.06D : 0.05D);
|
||||
double nextY = enemy.y() + next.dy * (enemy.kind() == EnemyKind.CREEPER ? 0.06D : 0.05D);
|
||||
if (!canMove(nextX, nextY, next)) { next = randomDirection(enemy.x(), enemy.y(), next); nextX = enemy.x() + next.dx * 0.05D; nextY = enemy.y() + next.dy * 0.05D; }
|
||||
if (canMove(nextX, nextY, next)) enemies.set(index, new Enemy(enemy.kind(), nextX, nextY, next));
|
||||
}
|
||||
}
|
||||
|
||||
private Direction chaseDirection(double x, double y, Direction current) {
|
||||
Direction best = current;
|
||||
double distance = Double.MAX_VALUE;
|
||||
for (Direction direction : Direction.values()) {
|
||||
if (isOpposite(direction, current) || !canMove(x, y, direction)) continue;
|
||||
double nx = x + direction.dx;
|
||||
double ny = y + direction.dy;
|
||||
double value = Math.abs(playerX - nx) + Math.abs(playerY - ny);
|
||||
if (value < distance) { distance = value; best = direction; }
|
||||
}
|
||||
if (distance == Double.MAX_VALUE && canMove(x, y, current.opposite())) best = current.opposite();
|
||||
return best;
|
||||
}
|
||||
|
||||
private Direction zombieDirection(double x, double y, Direction current) {
|
||||
if (random.nextInt(4) != 0 && canMove(x, y, current)) return current;
|
||||
return randomDirection(x, y, current);
|
||||
}
|
||||
|
||||
private Direction randomDirection(double x, double y, Direction current) {
|
||||
List<Direction> options = new ArrayList<>();
|
||||
for (Direction direction : Direction.values()) if (!isOpposite(direction, current) && canMove(x, y, direction)) options.add(direction);
|
||||
if (options.isEmpty() && canMove(x, y, current.opposite())) return current.opposite();
|
||||
if (options.isEmpty()) return current;
|
||||
return options.get(random.nextInt(options.size()));
|
||||
}
|
||||
|
||||
private boolean canMove(double x, double y, Direction direction) {
|
||||
double nx = x + direction.dx * SPEED;
|
||||
double ny = y + direction.dy * SPEED;
|
||||
return !isWall((int) Math.floor(nx), (int) Math.floor(ny));
|
||||
}
|
||||
|
||||
private boolean atCenter(double value) { return Math.abs((value - 0.5D) - Math.rint(value - 0.5D)) < 0.07D; }
|
||||
private boolean near(double firstX, double firstY, double secondX, double secondY) { return Math.abs(firstX - secondX) < 0.45D && Math.abs(firstY - secondY) < 0.45D; }
|
||||
private boolean isOpposite(Direction first, Direction second) { return first.dx + second.dx == 0 && first.dy + second.dy == 0; }
|
||||
|
||||
private void generateMaze() {
|
||||
for (int y = 0; y < HEIGHT; y++) for (int x = 0; x < WIDTH; x++) walls[y][x] = true;
|
||||
carve(1, 1);
|
||||
}
|
||||
|
||||
private void carve(int x, int y) {
|
||||
walls[y][x] = false;
|
||||
List<Direction> directions = new ArrayList<>(List.of(Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT));
|
||||
Collections.shuffle(directions, random);
|
||||
for (Direction direction : directions) {
|
||||
int nx = x + direction.dx * 2;
|
||||
int ny = y + direction.dy * 2;
|
||||
if (nx > 0 && ny > 0 && nx < WIDTH - 1 && ny < HEIGHT - 1 && walls[ny][nx]) {
|
||||
walls[y + direction.dy][x + direction.dx] = false;
|
||||
carve(nx, ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void placeDiamond() {
|
||||
int[] cell = randomOpenCell();
|
||||
diamondX = cell[0];
|
||||
diamondY = cell[1];
|
||||
}
|
||||
|
||||
private void placeCornDog() {
|
||||
int[] cell = randomOpenCell();
|
||||
cornDogX = cell[0];
|
||||
cornDogY = cell[1];
|
||||
}
|
||||
|
||||
private int[] randomOpenCell() {
|
||||
int x;
|
||||
int y;
|
||||
do { x = random.nextInt(WIDTH); y = random.nextInt(HEIGHT); } while (isWall(x, y) || near(playerX, playerY, x + 0.5D, y + 0.5D));
|
||||
return new int[]{x, y};
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public final class AntmanProgram {
|
||||
private final AntmanGame game;
|
||||
private final IntConsumer scoreSaver;
|
||||
private boolean installed;
|
||||
private int savedScore;
|
||||
private int savedHighScore;
|
||||
|
||||
public AntmanProgram(boolean installed, int highScore, int score, IntConsumer scoreSaver) {
|
||||
this.installed = installed;
|
||||
this.savedScore = score;
|
||||
this.savedHighScore = highScore;
|
||||
this.game = new AntmanGame(highScore, score);
|
||||
this.scoreSaver = scoreSaver;
|
||||
}
|
||||
|
||||
public void setInstalled(boolean installed) { this.installed = installed; if (!installed) game.reset(); }
|
||||
public boolean installed() { return installed; }
|
||||
public AntmanGame game() { return game; }
|
||||
public void setStoredScores(int score, int highScore) { savedScore = score; savedHighScore = highScore; game.reset(score); }
|
||||
public void tick() {
|
||||
if (!game.tick()) return;
|
||||
if (game.collectedDiamond() && Minecraft.getInstance().player != null) Minecraft.getInstance().player.playSound(SoundEvents.AMETHYST_BLOCK_CHIME, 0.6F, 1.4F);
|
||||
if (game.score() != savedScore || game.highScore() != savedHighScore) { savedScore = game.score(); savedHighScore = game.highScore(); scoreSaver.accept(savedScore); }
|
||||
}
|
||||
public boolean keyPressed(int keyCode) {
|
||||
if (!installed) return false;
|
||||
AntmanGame.Direction direction = switch (keyCode) {
|
||||
case 265, 87 -> AntmanGame.Direction.UP;
|
||||
case 264, 83 -> AntmanGame.Direction.DOWN;
|
||||
case 263, 65 -> AntmanGame.Direction.LEFT;
|
||||
case 262, 68 -> AntmanGame.Direction.RIGHT;
|
||||
default -> null;
|
||||
};
|
||||
if (direction != null) { game.start(); game.setDirection(direction); return true; }
|
||||
if (keyCode == 257 || keyCode == 335) { if (game.phase() != AntmanGame.Phase.PLAYING) { game.reset(); game.start(); savedScore = 0; scoreSaver.accept(0); } return true; }
|
||||
return false;
|
||||
}
|
||||
public void render(GuiGraphics graphics, Font font, int x, int y, int width, int height) { AntmanRenderer.render(graphics, font, game, x, y, width, height); }
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
public final class AntmanRenderer {
|
||||
private static final int BLACK = 0xFF020602;
|
||||
private static final int DARK_GREEN = 0xFF123012;
|
||||
private static final int GREEN = 0xFF65FF65;
|
||||
private static final int PALE_GREEN = 0xFFB8FFB8;
|
||||
private static final int ANT_BODY = 0xFFC98A3D;
|
||||
private static final int GHOST_RED = 0xFFFF4A4A;
|
||||
private static final int GHOST_PINK = 0xFFFF8FD2;
|
||||
private static final int GHOST_FRIGHTENED = 0xFF4A8CFF;
|
||||
private static final int GHOST_EYE = 0xFFF8FFFF;
|
||||
private AntmanRenderer() { }
|
||||
|
||||
public static void render(GuiGraphics graphics, Font font, AntmanGame game, int x, int y, int width, int height) {
|
||||
graphics.fill(x, y, x + width, y + height, BLACK);
|
||||
graphics.drawString(font, "SCORE " + String.format("%04d", game.score()), x + 5, y + 4, GREEN, false);
|
||||
graphics.drawString(font, "HI " + String.format("%04d", game.highScore()), x + width - 55, y + 4, PALE_GREEN, false);
|
||||
graphics.drawString(font, "LIVES " + game.lives(), x + 5, y + height - 13, PALE_GREEN, false);
|
||||
int tile = Math.max(1, Math.min((width - 8) / game.width(), (height - 34) / game.height()));
|
||||
int boardWidth = tile * game.width();
|
||||
int boardHeight = tile * game.height();
|
||||
int boardX = x + (width - boardWidth) / 2;
|
||||
int boardY = y + 20 + (height - 34 - boardHeight) / 2;
|
||||
for (int cellY = 0; cellY < game.height(); cellY++) for (int cellX = 0; cellX < game.width(); cellX++) if (game.isWall(cellX, cellY)) graphics.fill(boardX + cellX * tile, boardY + cellY * tile, boardX + (cellX + 1) * tile, boardY + (cellY + 1) * tile, DARK_GREEN);
|
||||
renderItem(graphics, new ItemStack(Items.DIAMOND), boardX + game.diamondX() * tile, boardY + game.diamondY() * tile, tile);
|
||||
if (game.cornDogX() >= 0) renderItem(graphics, new ItemStack(AntarchyObjects.COOKED_CORNDOG.get()), boardX + game.cornDogX() * tile, boardY + game.cornDogY() * tile, tile);
|
||||
int playerX = boardX + (int) (game.playerX() * tile);
|
||||
int playerY = boardY + (int) (game.playerY() * tile);
|
||||
drawAnt(graphics, playerX, playerY, tile, game.animationFrame(), ANT_BODY, game.direction());
|
||||
for (AntmanGame.Enemy enemy : game.enemies()) {
|
||||
int enemyX = boardX + (int) (enemy.x() * tile);
|
||||
int enemyY = boardY + (int) (enemy.y() * tile);
|
||||
int color = game.cornDogTicks() > 0 ? GHOST_FRIGHTENED : (enemy.kind() == AntmanGame.EnemyKind.CREEPER ? GHOST_RED : GHOST_PINK);
|
||||
drawGhost(graphics, enemyX, enemyY, tile, game.animationFrame(), color);
|
||||
}
|
||||
if (game.phase() != AntmanGame.Phase.PLAYING) {
|
||||
String title = game.phase() == AntmanGame.Phase.READY ? "ANTMAN.EXE" : "GAME OVER";
|
||||
String prompt = game.phase() == AntmanGame.Phase.READY ? "ENTER TO START" : "ENTER TO RETRY";
|
||||
int centerY = y + height / 2;
|
||||
graphics.fill(x + 4, centerY - 16, x + width - 4, centerY + 16, BLACK);
|
||||
graphics.drawString(font, title, x + (width - font.width(title)) / 2, centerY - 12, GREEN, false);
|
||||
graphics.drawString(font, prompt, x + (width - font.width(prompt)) / 2, centerY + 2, PALE_GREEN, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawAnt(GuiGraphics graphics, int centerX, int centerY, int tile, int frame, int color, AntmanGame.Direction direction) {
|
||||
int size = Math.max(4, tile - 1);
|
||||
int left = centerX - size / 2;
|
||||
int top = centerY - size / 2;
|
||||
int segment = Math.max(1, size / 4);
|
||||
graphics.fill(left + segment * 2, top + segment, left + segment * 3, top + segment * 3, color);
|
||||
graphics.fill(left + segment, top + segment * 2, left + segment * 4, top + segment * 4, color);
|
||||
graphics.fill(left + segment * 3, top + segment * 2, left + segment * 5, top + segment * 4, color);
|
||||
int legOffset = frame == 0 ? 1 : -1;
|
||||
graphics.fill(left + segment, top + segment * 3, left + segment * 2, top + segment * 4 + legOffset, color);
|
||||
graphics.fill(left + segment * 3, top + segment * 3, left + segment * 4, top + segment * 4 - legOffset, color);
|
||||
if (direction == AntmanGame.Direction.LEFT || direction == AntmanGame.Direction.RIGHT) {
|
||||
int headX = direction == AntmanGame.Direction.LEFT ? left : left + segment * 4;
|
||||
graphics.fill(headX, top + segment, headX + segment, top + segment * 2, BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawGhost(GuiGraphics graphics, int centerX, int centerY, int tile, int frame, int color) {
|
||||
int size = Math.max(4, tile - 1);
|
||||
int left = centerX - size / 2;
|
||||
int top = centerY - size / 2;
|
||||
int eyeWidth = Math.max(1, size / 5);
|
||||
int bodyBottom = top + size - 1;
|
||||
graphics.fill(left + 1, top, left + size - 1, bodyBottom - 1, color);
|
||||
graphics.fill(left, top + 2, left + size, bodyBottom - 2, color);
|
||||
int wave = frame == 0 ? 0 : 1;
|
||||
graphics.fill(left, bodyBottom - 1 - wave, left + size / 3, bodyBottom + 1, color);
|
||||
graphics.fill(left + size / 3, bodyBottom - wave, left + size * 2 / 3, bodyBottom + 1, color);
|
||||
graphics.fill(left + size * 2 / 3, bodyBottom - 1 - wave, left + size, bodyBottom + 1, color);
|
||||
int eyeY = top + Math.max(1, size / 3);
|
||||
graphics.fill(left + size / 3 - eyeWidth / 2, eyeY, left + size / 3 + eyeWidth, eyeY + eyeWidth + 1, GHOST_EYE);
|
||||
graphics.fill(left + size * 2 / 3 - eyeWidth / 2, eyeY, left + size * 2 / 3 + eyeWidth, eyeY + eyeWidth + 1, GHOST_EYE);
|
||||
graphics.fill(left + size / 3, eyeY + 1, left + size / 3 + 1, eyeY + eyeWidth + 1, BLACK);
|
||||
graphics.fill(left + size * 2 / 3, eyeY + 1, left + size * 2 / 3 + 1, eyeY + eyeWidth + 1, BLACK);
|
||||
}
|
||||
|
||||
private static void renderItem(GuiGraphics graphics, ItemStack stack, int x, int y, int tile) {
|
||||
float scale = Math.min(1.0F, Math.max(0.45F, tile / 16.0F));
|
||||
graphics.pose().pushPose();
|
||||
graphics.pose().translate(x + tile / 2.0F, y + tile / 2.0F, 0.0F);
|
||||
graphics.pose().scale(scale, scale, 1.0F);
|
||||
RenderSystem.setShaderColor(0.38F, 1.0F, 0.38F, 1.0F);
|
||||
graphics.renderItem(stack, -8, -8);
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
graphics.pose().popPose();
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Random;
|
||||
|
||||
public final class BasiliskGame {
|
||||
public enum Phase { READY, PLAYING, LOST }
|
||||
public enum Direction { UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
|
||||
final int dx;
|
||||
final int dy;
|
||||
Direction(int dx, int dy) { this.dx = dx; this.dy = dy; }
|
||||
}
|
||||
|
||||
private final Deque<Part> body = new ArrayDeque<>();
|
||||
private final Random random = new Random();
|
||||
private Direction direction = Direction.RIGHT;
|
||||
private Direction requested = Direction.RIGHT;
|
||||
private Phase phase = Phase.READY;
|
||||
private int foodX;
|
||||
private int foodY;
|
||||
private int score;
|
||||
private int highScore;
|
||||
private int ticks;
|
||||
|
||||
public BasiliskGame(int highScore, int score) {
|
||||
this.highScore = Math.max(0, highScore);
|
||||
reset(score);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
reset(0);
|
||||
}
|
||||
|
||||
public void reset(int currentScore) {
|
||||
body.clear();
|
||||
body.addFirst(new Part(9, 7));
|
||||
body.addLast(new Part(8, 7));
|
||||
body.addLast(new Part(7, 7));
|
||||
direction = Direction.RIGHT;
|
||||
requested = Direction.RIGHT;
|
||||
phase = Phase.READY;
|
||||
score = Math.max(0, currentScore);
|
||||
ticks = 0;
|
||||
placeFood();
|
||||
}
|
||||
|
||||
public void start() { if (phase == Phase.READY) phase = Phase.PLAYING; }
|
||||
|
||||
public boolean tick() {
|
||||
if (phase != Phase.PLAYING || ++ticks < 5) return false;
|
||||
ticks = 0;
|
||||
if (!isOpposite(requested, direction)) direction = requested;
|
||||
Part head = body.peekFirst();
|
||||
int nextX = head.x() + direction.dx;
|
||||
int nextY = head.y() + direction.dy;
|
||||
if (nextX < 0 || nextX >= 20 || nextY < 0 || nextY >= 14 || body.contains(new Part(nextX, nextY))) {
|
||||
phase = Phase.LOST;
|
||||
return true;
|
||||
}
|
||||
body.addFirst(new Part(nextX, nextY));
|
||||
if (nextX == foodX && nextY == foodY) {
|
||||
score += 10;
|
||||
highScore = Math.max(highScore, score);
|
||||
placeFood();
|
||||
} else {
|
||||
body.removeLast();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction) { if (direction != null && !isOpposite(direction, this.direction)) requested = direction; }
|
||||
public Phase phase() { return phase; }
|
||||
public int score() { return score; }
|
||||
public int highScore() { return highScore; }
|
||||
public int width() { return 20; }
|
||||
public int height() { return 14; }
|
||||
public int foodX() { return foodX; }
|
||||
public int foodY() { return foodY; }
|
||||
public Deque<Part> body() { return body; }
|
||||
|
||||
private void placeFood() {
|
||||
do {
|
||||
foodX = random.nextInt(width());
|
||||
foodY = random.nextInt(height());
|
||||
} while (body.contains(new Part(foodX, foodY)));
|
||||
}
|
||||
|
||||
private boolean isOpposite(Direction first, Direction second) {
|
||||
return first.dx + second.dx == 0 && first.dy + second.dy == 0;
|
||||
}
|
||||
|
||||
public record Part(int x, int y) { }
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public final class BasiliskProgram {
|
||||
private final BasiliskGame game;
|
||||
private boolean installed;
|
||||
private int savedScore;
|
||||
private int savedHighScore;
|
||||
private final IntConsumer scoreSaver;
|
||||
|
||||
public BasiliskProgram(boolean installed, int highScore, int score, IntConsumer scoreSaver) {
|
||||
this.installed = installed;
|
||||
this.savedHighScore = highScore;
|
||||
this.savedScore = score;
|
||||
this.game = new BasiliskGame(highScore, score);
|
||||
this.scoreSaver = scoreSaver;
|
||||
}
|
||||
|
||||
public void setInstalled(boolean installed) { this.installed = installed; if (!installed) game.reset(); }
|
||||
public boolean installed() { return installed; }
|
||||
public BasiliskGame game() { return game; }
|
||||
public void setStoredScores(int score, int highScore) { savedScore = score; savedHighScore = highScore; game.reset(score); }
|
||||
public void tick() { if (game.tick() && (game.score() != savedScore || game.highScore() != savedHighScore)) { if (game.score() > savedScore) Minecraft.getInstance().player.playSound(AntarchySoundEvents.BASILISK_BITE.get(), 0.5F, 1.2F); savedScore = game.score(); savedHighScore = game.highScore(); scoreSaver.accept(savedScore); } }
|
||||
public boolean keyPressed(int keyCode) {
|
||||
if (!installed) return false;
|
||||
BasiliskGame.Direction direction = switch (keyCode) {
|
||||
case 265, 87 -> BasiliskGame.Direction.UP;
|
||||
case 264, 83 -> BasiliskGame.Direction.DOWN;
|
||||
case 263, 65 -> BasiliskGame.Direction.LEFT;
|
||||
case 262, 68 -> BasiliskGame.Direction.RIGHT;
|
||||
default -> null;
|
||||
};
|
||||
if (direction != null) { game.start(); game.setDirection(direction); return true; }
|
||||
if (keyCode == 257 || keyCode == 335) { if (game.phase() != BasiliskGame.Phase.PLAYING) { game.reset(); game.start(); savedScore = 0; scoreSaver.accept(0); } return true; }
|
||||
return false;
|
||||
}
|
||||
public void render(GuiGraphics graphics, Font font, int x, int y, int width, int height) { BasiliskRenderer.render(graphics, font, game, x, y, width, height); }
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
|
||||
public final class BasiliskRenderer {
|
||||
private static final int BLACK = 0xFF020602;
|
||||
private static final int DARK_GREEN = 0xFF123012;
|
||||
private static final int GREEN = 0xFF65FF65;
|
||||
private static final int PALE_GREEN = 0xFFB8FFB8;
|
||||
private BasiliskRenderer() { }
|
||||
|
||||
public static void render(GuiGraphics graphics, Font font, BasiliskGame game, int x, int y, int width, int height) {
|
||||
graphics.fill(x, y, x + width, y + height, BLACK);
|
||||
graphics.drawString(font, "SCORE " + String.format("%04d", game.score()), x + 6, y + 4, GREEN, false);
|
||||
graphics.drawString(font, "HI " + String.format("%04d", game.highScore()), x + width - 56, y + 4, PALE_GREEN, false);
|
||||
int tile = Math.max(1, Math.min((width - 10) / game.width(), (height - 28) / game.height()));
|
||||
int boardWidth = tile * game.width();
|
||||
int boardHeight = tile * game.height();
|
||||
int boardX = x + (width - boardWidth) / 2;
|
||||
int boardY = y + 22 + (height - 22 - boardHeight) / 2;
|
||||
graphics.fill(boardX, boardY, boardX + boardWidth, boardY + boardHeight, DARK_GREEN);
|
||||
graphics.fill(boardX + 1, boardY + 1, boardX + boardWidth - 1, boardY + boardHeight - 1, BLACK);
|
||||
int appleX = boardX + game.foodX() * tile;
|
||||
int appleY = boardY + game.foodY() * tile;
|
||||
int appleInset = Math.max(1, tile / 4);
|
||||
graphics.fill(appleX + appleInset, appleY + appleInset + 1, appleX + tile - appleInset, appleY + tile - appleInset, PALE_GREEN);
|
||||
if (tile >= 5) graphics.fill(appleX + tile / 2, appleY + 1, appleX + tile / 2 + 1, appleY + tile / 3 + 1, GREEN);
|
||||
for (BasiliskGame.Part part : game.body()) graphics.fill(boardX + part.x() * tile + 1, boardY + part.y() * tile + 1, boardX + (part.x() + 1) * tile - 1, boardY + (part.y() + 1) * tile - 1, GREEN);
|
||||
if (game.phase() != BasiliskGame.Phase.PLAYING) {
|
||||
String title = game.phase() == BasiliskGame.Phase.READY ? "BASILISK" : "GAME OVER";
|
||||
String prompt = game.phase() == BasiliskGame.Phase.READY ? "ENTER TO START" : "ENTER TO RETRY";
|
||||
int centerY = y + height / 2;
|
||||
graphics.fill(x + 4, centerY - 16, x + width - 4, centerY + 16, BLACK);
|
||||
graphics.drawString(font, title, x + (width - font.width(title)) / 2, centerY - 12, GREEN, false);
|
||||
graphics.drawString(font, prompt, x + (width - font.width(prompt)) / 2, centerY + 2, PALE_GREEN, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.BlockleClientState;
|
||||
import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload;
|
||||
import com.craisinlord.antarchy.content.network.ComputerNetworking;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class BlockleProgram {
|
||||
private final BlockPos position;
|
||||
private final List<String> guesses = new ArrayList<>();
|
||||
private final List<String> colors = new ArrayList<>();
|
||||
private String input = "";
|
||||
private String status = "LOADING...";
|
||||
private String phase = "PLAYING";
|
||||
private String answer = "";
|
||||
private ResourceLocation answerId;
|
||||
private long day = -1L;
|
||||
private boolean installed;
|
||||
private boolean pending;
|
||||
private boolean received;
|
||||
private String lastResponse = "";
|
||||
|
||||
public BlockleProgram(BlockPos position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public void setInstalled(boolean installed) {
|
||||
this.installed = installed;
|
||||
if (!installed) clear();
|
||||
}
|
||||
|
||||
public boolean installed() {
|
||||
return installed;
|
||||
}
|
||||
|
||||
public void requestState() {
|
||||
if (installed) ComputerNetworking.requestBlockleState(position);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
ComputerAccessResultPayload result = BlockleClientState.get(position);
|
||||
if (result == null) return;
|
||||
if (result.data().equals(lastResponse)) return;
|
||||
lastResponse = result.data();
|
||||
String[] fields = result.data().split("\u0000", 3);
|
||||
if (fields.length < 2 || (!fields[0].equals("18") && !fields[0].equals("19"))) return;
|
||||
if (!fields[1].isBlank()) {
|
||||
status = fields[1].toUpperCase();
|
||||
pending = false;
|
||||
return;
|
||||
}
|
||||
if (fields.length == 3) {
|
||||
parse(fields[2]);
|
||||
pending = false;
|
||||
input = "";
|
||||
received = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean keyPressed(int keyCode) {
|
||||
if (!installed) return false;
|
||||
if (keyCode == 259) {
|
||||
if (!input.isEmpty()) input = input.substring(0, input.length() - 1);
|
||||
return true;
|
||||
}
|
||||
if (keyCode == 257 || keyCode == 335) {
|
||||
if (!received) return true;
|
||||
if (input.length() == 5 && phase.equals("PLAYING") && !pending) {
|
||||
ComputerNetworking.submitBlockleGuess(position, input);
|
||||
pending = true;
|
||||
status = "SUBMITTING";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean charTyped(char codePoint) {
|
||||
if (!installed || !received || phase.equals("SOLVED") || phase.equals("FAILED")) return false;
|
||||
if (codePoint >= 'A' && codePoint <= 'Z') codePoint = Character.toLowerCase(codePoint);
|
||||
if (codePoint >= 'a' && codePoint <= 'z' && input.length() < 5) {
|
||||
input += codePoint;
|
||||
status = "";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void render(GuiGraphics graphics, Font font, int x, int y, int width, int height) {
|
||||
BlockleRenderer.render(graphics, font, x, y, width, height, guesses, colors, input, status, phase, answerId);
|
||||
}
|
||||
|
||||
private void parse(String encoded) {
|
||||
String[] values = encoded.split("\\|");
|
||||
if (values.length == 0) return;
|
||||
try { day = Long.parseLong(values[0]); } catch (NumberFormatException ignored) { return; }
|
||||
guesses.clear();
|
||||
colors.clear();
|
||||
phase = "PLAYING";
|
||||
answer = "";
|
||||
for (int index = 1; index < values.length; index++) {
|
||||
String value = values[index];
|
||||
if (value.equals("PLAYING") || value.equals("SOLVED") || value.equals("FAILED")) {
|
||||
phase = value;
|
||||
if (index + 1 < values.length && (phase.equals("SOLVED") || phase.equals("FAILED"))) {
|
||||
answer = values[index + 1];
|
||||
if (index + 2 < values.length) {
|
||||
try { answerId = ResourceLocation.parse(values[index + 2]); } catch (IllegalArgumentException ignored) { answerId = null; }
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
String[] guess = value.split(",", 2);
|
||||
if (guess.length == 2 && guess[0].length() == 5 && guess[1].length() == 5) {
|
||||
guesses.add(guess[0]);
|
||||
colors.add(guess[1]);
|
||||
}
|
||||
}
|
||||
status = phase.equals("FAILED") && !answer.isBlank() ? "ANSWER: " + answer.toUpperCase() : phase.equals("PLAYING") ? "" : phase;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
guesses.clear();
|
||||
colors.clear();
|
||||
input = "";
|
||||
status = "LOADING...";
|
||||
phase = "PLAYING";
|
||||
answer = "";
|
||||
answerId = null;
|
||||
day = -1L;
|
||||
pending = false;
|
||||
received = false;
|
||||
lastResponse = "";
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.craisinlord.antarchy.content.client.game;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class BlockleRenderer {
|
||||
private static final int EMPTY = 0xFF102010;
|
||||
private static final int GREY = 0xFF3A403A;
|
||||
private static final int YELLOW = 0xFFB59F3B;
|
||||
private static final int GREEN = 0xFF538D4E;
|
||||
private static final int PALE = 0xFFB8FFB8;
|
||||
|
||||
private BlockleRenderer() {
|
||||
}
|
||||
|
||||
public static void render(GuiGraphics graphics, Font font, int x, int y, int width, int height,
|
||||
List<String> guesses, List<String> colors, String input, String status, String phase,
|
||||
ResourceLocation answerId) {
|
||||
graphics.drawString(font, Component.literal("BLOCKLE"), x, y, 0xFF65FF65, false);
|
||||
int cell = 18;
|
||||
int gap = 3;
|
||||
int boardX = x + Math.max(0, (width - cell * 5 - gap * 4) / 2);
|
||||
int boardY = y + 18;
|
||||
for (int row = 0; row < 6; row++) {
|
||||
String guess = row < guesses.size() ? guesses.get(row) : row == guesses.size() ? input : "";
|
||||
String rowColors = row < colors.size() ? colors.get(row) : "";
|
||||
for (int column = 0; column < 5; column++) {
|
||||
int left = boardX + column * (cell + gap);
|
||||
int top = boardY + row * (cell + gap);
|
||||
int fill = EMPTY;
|
||||
if (column < rowColors.length()) fill = switch (rowColors.charAt(column)) {
|
||||
case 'G' -> GREEN;
|
||||
case 'Y' -> YELLOW;
|
||||
default -> GREY;
|
||||
};
|
||||
graphics.fill(left, top, left + cell, top + cell, fill);
|
||||
if (column < guess.length()) {
|
||||
String letter = Character.toString(Character.toUpperCase(guess.charAt(column)));
|
||||
graphics.drawCenteredString(font, letter, left + cell / 2, top + 5, PALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
String message = status.isBlank() ? "ENTER TO SUBMIT" : status;
|
||||
if (phase.equals("PLAYING") && input.length() < 5) message = input.length() + "/5 LETTERS";
|
||||
graphics.drawString(font, Component.literal(message), x, y + 145, PALE, false);
|
||||
if (!phase.equals("PLAYING") && answerId != null) {
|
||||
ItemStack answerStack = BuiltInRegistries.ITEM.getOptional(answerId).map(ItemStack::new).orElse(ItemStack.EMPTY);
|
||||
if (!answerStack.isEmpty()) {
|
||||
graphics.setColor(0.35F, 1.0F, 0.35F, 1.0F);
|
||||
graphics.renderItem(answerStack, x + width - 28, y + 137);
|
||||
graphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1257
-204
File diff suppressed because it is too large.
Load diff
+29
-7
@@ -80,7 +80,7 @@ public final class ComputerFileSystem {
|
||||
if (!validation.successful()) return validation;
|
||||
ComputerFile current = nodes.get(normalized);
|
||||
if (current == null) return Result.failure("not_found");
|
||||
if (current.type() != ComputerFile.Type.TEXT) return Result.failure("not_text");
|
||||
if (current.type() != ComputerFile.Type.TEXT && current.type() != ComputerFile.Type.IMAGE) return Result.failure("not_file");
|
||||
int replacement = totalBytes - current.contents().length() + contents.length();
|
||||
if (replacement > MAX_TOTAL_SIZE) return Result.failure("storage_limit");
|
||||
nodes.put(normalized, ComputerFile.text(normalized, contents));
|
||||
@@ -102,8 +102,29 @@ public final class ComputerFileSystem {
|
||||
int slash = to.lastIndexOf('/');
|
||||
if (slash > 0 && !nodesDirectory(to.substring(0, slash))) return Result.failure("missing_directory");
|
||||
if (file.type() == ComputerFile.Type.DIRECTORY && to.startsWith(from + "/")) return Result.failure("invalid_path");
|
||||
nodes.remove(from);
|
||||
nodes.put(to, file.type() == ComputerFile.Type.DIRECTORY ? ComputerFile.directory(to) : ComputerFile.text(to, file.contents()));
|
||||
if (file.type() == ComputerFile.Type.DIRECTORY) {
|
||||
List<String> descendants = nodes.keySet().stream()
|
||||
.filter(path -> path.startsWith(from + "/"))
|
||||
.toList();
|
||||
for (String descendant : descendants) {
|
||||
String movedPath = to + descendant.substring(from.length());
|
||||
if (nodes.containsKey(movedPath)) return Result.failure("already_exists");
|
||||
}
|
||||
Map<String, ComputerFile> moved = new LinkedHashMap<>();
|
||||
moved.put(to, ComputerFile.directory(to));
|
||||
for (String descendant : descendants) {
|
||||
ComputerFile child = nodes.get(descendant);
|
||||
String movedPath = to + descendant.substring(from.length());
|
||||
moved.put(movedPath, child.type() == ComputerFile.Type.DIRECTORY
|
||||
? ComputerFile.directory(movedPath)
|
||||
: ComputerFile.text(movedPath, child.contents()));
|
||||
}
|
||||
nodes.keySet().removeIf(path -> path.equals(from) || path.startsWith(from + "/"));
|
||||
nodes.putAll(moved);
|
||||
} else {
|
||||
nodes.remove(from);
|
||||
nodes.put(to, ComputerFile.text(to, file.contents()));
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@@ -141,7 +162,7 @@ public final class ComputerFileSystem {
|
||||
CompoundTag stored = new CompoundTag();
|
||||
stored.putString(PATH_TAG, file.path());
|
||||
stored.putString(TYPE_TAG, file.type().name());
|
||||
if (file.type() == ComputerFile.Type.TEXT) stored.putString(CONTENTS_TAG, file.contents());
|
||||
if (file.type() == ComputerFile.Type.TEXT || file.type() == ComputerFile.Type.IMAGE) stored.putString(CONTENTS_TAG, file.contents());
|
||||
files.add(stored);
|
||||
}
|
||||
tag.put(FILES_TAG, files);
|
||||
@@ -157,10 +178,10 @@ public final class ComputerFileSystem {
|
||||
String type = stored.getString(TYPE_TAG);
|
||||
if ("DIRECTORY".equals(type)) {
|
||||
if (nodes.size() < MAX_NODES) nodes.put(path, ComputerFile.directory(path));
|
||||
} else if ("TEXT".equals(type)) {
|
||||
} else if ("TEXT".equals(type) || "IMAGE".equals(type)) {
|
||||
String contents = stored.getString(CONTENTS_TAG);
|
||||
if (contents.length() <= MAX_FILE_SIZE && nodes.size() < MAX_NODES && totalBytes + contents.length() <= MAX_TOTAL_SIZE) {
|
||||
nodes.put(path, ComputerFile.text(path, contents));
|
||||
nodes.put(path, "IMAGE".equals(type) ? ComputerFile.image(path, contents) : ComputerFile.text(path, contents));
|
||||
totalBytes += contents.length();
|
||||
}
|
||||
}
|
||||
@@ -192,7 +213,8 @@ public final class ComputerFileSystem {
|
||||
|
||||
public record ComputerFile(String path, Type type, String contents) {
|
||||
public static ComputerFile directory(String path) { return new ComputerFile(path, Type.DIRECTORY, ""); }
|
||||
public static ComputerFile text(String path, String contents) { return new ComputerFile(path, Type.TEXT, contents); }
|
||||
public static ComputerFile text(String path, String contents) { return path.endsWith(".antpaint") ? image(path, contents) : new ComputerFile(path, Type.TEXT, contents); }
|
||||
public static ComputerFile image(String path, String contents) { return new ComputerFile(path, Type.IMAGE, contents); }
|
||||
public enum Type { DIRECTORY, TEXT, IMAGE }
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.craisinlord.antarchy.content.computer.blockle;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class BlockleAnswers {
|
||||
public static final int VERSION = 1;
|
||||
private static final int SALT = 0x4A17;
|
||||
private static final List<Answer> ANSWERS = List.of(
|
||||
new Answer("anvil", id("anvil")),
|
||||
new Answer("apple", id("apple")),
|
||||
new Answer("arrow", id("arrow")),
|
||||
new Answer("bread", id("bread")),
|
||||
new Answer("brick", id("brick")),
|
||||
new Answer("chain", id("chain")),
|
||||
new Answer("chest", id("chest")),
|
||||
new Answer("clock", id("clock")),
|
||||
new Answer("cocoa", id("cocoa")),
|
||||
new Answer("flint", id("flint")),
|
||||
new Answer("glass", id("glass")),
|
||||
new Answer("melon", id("melon")),
|
||||
new Answer("paper", id("paper")),
|
||||
new Answer("poppy", id("poppy")),
|
||||
new Answer("sculk", id("sculk")),
|
||||
new Answer("stone", id("stone")),
|
||||
new Answer("stick", id("stick")),
|
||||
new Answer("sugar", id("sugar")),
|
||||
new Answer("torch", id("torch")),
|
||||
new Answer("vault", id("vault")),
|
||||
new Answer("wheat", id("wheat")),
|
||||
new Answer("allay", id("allay_spawn_egg")),
|
||||
new Answer("blaze", id("blaze_spawn_egg")),
|
||||
new Answer("camel", id("camel_spawn_egg")),
|
||||
new Answer("drown", id("drowned_spawn_egg")),
|
||||
new Answer("ghast", id("ghast_spawn_egg")),
|
||||
new Answer("horse", id("horse_spawn_egg")),
|
||||
new Answer("llama", id("llama_spawn_egg")),
|
||||
new Answer("panda", id("panda_spawn_egg")),
|
||||
new Answer("sheep", id("sheep_spawn_egg")),
|
||||
new Answer("slime", id("slime_spawn_egg")),
|
||||
new Answer("squid", id("squid_spawn_egg")),
|
||||
new Answer("stray", id("stray_spawn_egg")),
|
||||
new Answer("witch", id("witch_spawn_egg")),
|
||||
new Answer("lever", id("lever")),
|
||||
new Answer("sword", id("iron_sword")),
|
||||
new Answer("cheep", antarchy("cheep_spawn_egg")),
|
||||
new Answer("jerry", antarchy("jerry_spawn_egg")),
|
||||
new Answer("lucid", antarchy("lucid_spawn_egg")),
|
||||
new Answer("queen", antarchy("queen_spawn_egg")),
|
||||
new Answer("ichor", antarchy("ichor")),
|
||||
new Answer("lotus", antarchy("lotus")),
|
||||
new Answer("mucus", antarchy("mucus")),
|
||||
new Answer("peach", antarchy("peach"))
|
||||
);
|
||||
|
||||
private BlockleAnswers() {
|
||||
}
|
||||
|
||||
public static Answer answerForDay(long day) {
|
||||
int index = (int) Math.floorMod(day + SALT, ANSWERS.size());
|
||||
return ANSWERS.get(index);
|
||||
}
|
||||
|
||||
public static int size() {
|
||||
return ANSWERS.size();
|
||||
}
|
||||
|
||||
public record Answer(String word, ResourceLocation itemId) {
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String path) {
|
||||
return ResourceLocation.withDefaultNamespace(path);
|
||||
}
|
||||
|
||||
private static ResourceLocation antarchy(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.craisinlord.antarchy.content.computer.blockle;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public final class BlockleDictionary {
|
||||
private static final String[] CHUNKS = {
|
||||
"rossa,jetty,wizzo,cuppa,cohoe,gurks,squad,beisa,shrug,fossa,fluyt,camus,speed,mamil,array,polio,barns,panes,souts,limas,fetch,queck,twink,graze,crock,almud,oohed,colog,wisht,beard,samel,ahind,brung,barca,mahal,jambe,plush,bruja,howre,middy,kabab,zeals,mazel,sputa,goory,pails,scogs,snool,poboy,doest,orixa,quipo,burbs,goads,blaud,flocs,harim,mamey,primp,lathy,lunes,staps,salad,agony,gusli,damps,tocks,hoick,marry,amply,glisk,pulis,apter,shell,capas,saunf,kutis,teggs,halsh,gamma,tardy,tease,towts,abuts,allee,chins,rolly,situs,bards,phyma,torrs,chace,bikky,stoor,quink,wedgy,vises,swore,upsee,gyves,heapy,gilas,gairs,yonic,wagon,rance,swail,laxed,panne,tabus,anomy,swarm,sword,pharm,penes,snoek,razet,nahal,kapus,lokey,gobos,jails,afars,nerka,cundy,snark,intra,bundh,tanga,baaed,solus,senor,umami,wared,sharp,pewed,tuktu,knowd,dawds,flurr,inert,taxus,ruana,sulci,jaded,othyl,redig,manky,ovism,matlo,cease,perae,sixes,pubco,yappy,famed,hexed,bawrs,decry,iddah,dixit,cased,rupia,toidy,hodad,yukes,gaffe,annoy,leugh,howay,pixel,linga,bayou,lurgy,lears,scrat,prize,homes,scroo,egest,mopus,verbs,maple,barfs,judge,salle,shine,zineb,flexo,gismo,rears,khads,begum,glade,imams,strad,venae,skene,moten,rugby,goels,khazi,belit,smolt,morin,ofays,sural,janny,short,swish,haets,owned,musky,gimps,nodes,lefty,furca,exome,dribs,gouks,smews,munts,chiro,learn,mocus,slime,mokis,nevis,arcus,prigs,rente,yfere,hiker,crame,irked,naira,stire,nagas,peisy,miser,vivda,dolie,weave,umbel,barmy,louns,didos,viold,fowth,guqin,kayos,finca,taths,eliad,dorts,cooey,dicty,bobak,lowse,tuile,laked,tarts,reefy,agama,thing,shore,nicks,nunny,akoya,snaws,writs,doubt,alive,beaut,ibrik,hemps,towed,crout,mogul,halid,pecky,eched,musit,couth,acron,dynes,gothy,bundy,seeps,deeps,plene,kukus,petre,putto,darls,warps,shoji,emoji,hiked,omits,rainy,fills,zines,bores,algor,oliva,midge,vizor,hafta,mulla,paled,kneed,fibre,ptish,redan,exode,zorro,droll,niece,segno,sceat,tunes,mompe,radon,akkas,jowly,gandy,medii,knelt,evoke,lowts,teaks,bucko,groks,leafy,meses,synes,laths,zedas,blank,japed,voled,toxic,reach,nazzy,pubsy,pedro,cucks,baith,magas,gompa,bilby,popes,hents,auras,giddy,tacos,onned,bowed,tsars,slogs,grind,morel,asada,weeke,winna,amman,sabji,runts,slops,tread,loofa,fever,glove,trist,creps,pheon,beres,corso,sofar,reeze,pisos,lames,treck,boabs,apply,grata,daals,soyas,amour,gerne,dudes,nifle,again,slurp,ulnar,ettin,noles,gobar,bohos,kawas,radgy,coati,scail,helot,motel,tides,coven,jasey,flown,urubu,kiddo,nrtya,pinna,curbs,botas,cycle,yugas,ugali,opter,laver,ousts,ootid,easel,lewis,renos,paned,cunit,muils,jehus,ebike,cotch,upper,theor,unais,idiot,manes,sawer,gazoo,edged,pikey,meech,adyts,boite,ixias,alios,padle,lotah,ragas,pudus,meare,graip,quack,noise,livre,pheme,chest,lined,azlon,waves,achoo,valve,mawks,hydro,muxed,etude,smith,mends,pisco,cilia,algos,nenes,karma,neant,visie,volar,burro,doeth,ecrus,dekes,seepy,facts,pours,gambo,mensa,swept,oafos,shock,roams,potts,piker,tings,fount,daunt,battu,licit,cider,fords,palms,wheep,copes,whelm,usnea,staid,dosai,patka,gamas,drome,licks,aldea,snowy,quods,waker,syphs,stumm,chore,silts,topee,zeins,likin,topic,waved,worse,anear,rayah,clipe,teloi,thesp,dusts,pooch,lunts,vinny,purda,zonda,widen,jonty,mosed,hussy,escar,appay,tonic,tavah,budos,cures,retia,maile,undee,quois,vangs,kytes,rends,taiga,abets,licht,potae,funks,repas,sicht,mobes,ceorl,torte,snarl,might,genas,washy,etats,boink,perog,coden,crown,moity,helos,murky,gaitt,dance,grail,deads,mivvy,fugio,ursid,burin,mimsy,lotus,frons,durst,devis,parae,mands,sarin,ethic,whirs,bribe,rorty,nymph,billy,gighe,kalif,breds,cutey,mincy,modoc,allin,yeses,dwalm,balms,yucca,cyder,loped,kagus,smeek,ylems,peags,comps,nobly,cocoa,pedon,hithe,etrog,dumps,magic,bendy,seely,spawn,oxids,hakus,grads,abacs,focus,hides,hayed,stony,slims,tonka,ident,tansu,whirr,glike,votes,sigri,cades,layed,snail,fykes,ousia,nevus,wings,toile,junta,chair,regia,lavvy,fatty,bippy,hoody,bunco,zupan,spend,phubs,sluse,dwine,piety,pyrus,mogue,glows,kutch,ngana,piton,spalt,qipao,pakka,redly,furzy,arhat,fumer,addin,cardy,zooea,clegs,dhals,deshi,meany,lenos,cines,sneap,busti,belts,weans,globe,decad,tinks,bally,yokes,onsen,lover,iddat,terry,pogey,planh,lotte,clies,femes,deedy,acnes,rubli,lolos,rapes,manse,wiped,sabha,gamer,afear,adraw,outta,slaps,tarka,goyle,tomia,misdo,sayee,popsy,watch,riped,baths,tails,dinna,conin,bunya,skool,khors,hulks,lacet,lazes,seams,slyly,joins,doats,proke,kingy,ranny,raker,swoun,gular,sedum,opera,perns,redia,tekke,appel,waist,bolds,steer,podgy,humfs,stagy,opgaf,skeos,swerf,rhyta,pians,ulans,diver,penny,nanua,onlap,loyal,bohea,lirks,senti,pleon,daman,syned,mools,baked,rungs,brace,melba,sotto,dealt,bikes,fecit,flyer,sidhe,rengs,alkyd,veney,strag,apode,aigas,torts,sooth,stean,algin,gummy,nduja,ratan,durum,acies,unios,punts,filos,blype,molal,tiara,ratal,lowen,dawts,iddut,indew,surfs,brims,abaci,troke,mimed,rosha,elate,tilak,cycas,ornis,jives,mercy,dimes,cries,affix,ought,ansas,besti,romer,gisms,which,logon,weels,leftLine truncated
|
||||
"puttu,anlas,torch,homed,serow,blogs,agist,tanka,prima,vobla,flosh,tamas,raver,vegas,roves,ommel,godet,sujee,cerne,halon,axils,eaten,dorms,chiva,sonne,kings,royst,album,groof,buddy,dinic,erhus,quoit,vitta,coxib,tithi,bemad,gouts,chiru,wodgy,gucks,ricin,liman,owner,spoil,trews,gawds,ownio,grams,vaxes,trigs,leets,queme,laxly,dhoni,sider,tabls,spuer,cored,merit,whews,treif,elfed,corgi,newsy,idyls,prays,wight,garri,chado,nonno,glazy,tenge,paves,plink,naffs,nonas,pipid,maiks,smees,sudds,plump,cered,moons,tarse,bewet,abaca,saved,scrab,byded,malic,sadhu,karos,trans,pokie,douce,elegy,conto,obeys,nilas,merry,fixer,escot,poise,soyuz,raise,pashm,rorts,bongo,atuas,asker,azuki,falsy,cubeb,aquas,wolfs,annul,luged,smugs,draft,lited,pichi,weils,grate,whins,sieve,boked,pollo,blunk,esker,eyres,padas,ileac,lawny,cymas,gutsy,bombs,zawns,crier,acers,guppy,lusty,dated,ranke,rusty,omdeh,tamal,remap,wersh,torcs,proas,dholl,enarm,loled,whear,cunei,coset,rafty,javel,fifes,grice,apish,idant,locie,hillo,slaws,cents,plain,volta,gundy,rines,keros,condo,asana,spink,psion,yawns,coyer,thews,carls,odyle,chals,darks,fates,pongs,hoyed,halal,sorgo,icons,bafta,blate,pares,rowel,helps,vakas,thali,llano,wamus,dozes,moble,myxos,yearn,ragga,lilty,unpay,niger,slank,bifid,gaums,laces,bekah,fient,nowds,vitro,shyly,helve,murrs,firry,mairs,lindy,tutti,karee,mobos,labis,pluke,insee,basin,winch,about,tizes,genes,probe,umiac,spaer,moots,sewen,grued,shist,perks,tolus,afire,pomas,mapou,gulag,ariki,jupes,false,nelly,gites,burry,sends,canes,bever,knawe,wonga,limen,pungo,staig,twerk,thumb,resid,copay,strew,oomph,saggy,kaids,ruers,naves,virls,sever,pupus,ettle,tipup,ollas,wally,bully,swang,doser,urent,pocan,spile,occur,risus,makaf,sabot,yamun,matai,durzi,vouge,macka,grrrl,avels,sorts,glide,menes,crane,azine,nosed,xoana,uteri,obeli,trots,simba,wirer,party,potai,reges,gombo,grume,hawed,wagyu,upbow,mares,nused,synod,hemic,twerp,tryma,teeny,uveal,crept,hyphy,perst,lords,began,thrum,nocks,ornee,krais,iglus,sloes,apols,cyclo,backy,richt,moist,droid,stand,bonie,fetus,teras,blare,napoo,diced,malts,shend,boars,charm,mayos,suets,antsy,breve,pinas,sprug,recit,halts,dater,mogos,afoam,ploce,inula,setae,raddy,lawns,genip,xrays,raked,maxim,reeaf,swale,wills,midis,scire,stonn,kinks,zeros,vatas,dogie,serry,pepsi,viand,acene,aloft,beals,kaneh,yirks,piled,bajra,lesos,renin,bonus,wauff,crink,lyses,knack,recon,canns,devel,spayd,throb,gaper,arise,moder,bluid,berko,agile,kamas,assai,slits,matte,spads,stool,ashet,uraei,pulls,firms,moule,rober,bydes,modii,ealed,tache,bebop,swapt,guava,curly,solon,argue,covet,skoal,mosso,haded,shorl,doups,hives,bulls,truce,katal,thugs,arles,drown,skags,babel,revet,novel,alays,codas,rache,punji,douks,potsy,diols,amnic,evict,bauds,hosta,dowie,wizen,micks,showy,zoeae,toxin,noded,bleat,kiaat,toyed,azurn,pupae,grees,oopak,yahoo,wists,ovoli,pines,foams,neves,puris,serve,lovie,ysame,womas,relos,sepia,trade,butes,micra,weise,recut,riser,mbari,dower,sakai,octas,danny,divot,cooly,scapi,salsa,exuls,scudi,leges,visit,farle,crest,faced,bukes,mouth,basse,basts,alkin,mausy,forks,dosha,reely,junks,cubed,plots,drent,curfs,goban,snide,taros,funts,niffy,vairs,noggy,toges,lever,egers,rokos,inrun,platy,antre,sabal,cocco,pouce,fugis,ranes,obang,thrip,thoft,baste,stats,momes,ocote,gloop,admin,oozle,slips,boord,hilus,smarm,aloha,natak,knubs,avert,dowak,bruck,bison,onkus,libor,gives,hardy,greet,craft,narra,tetri,ample,awave,thane,hales,acara,braks,mowra,lassy,kerma,shlep,words,raths,paans,aftos,along,fresh,negro,nuked,gated,manis,exons,poeps,joual,milko,azide,poake,mises,culet,pized,dykon,crunk,whose,bogus,moved,gyppy,oscar,lisps,danda,misch,ngwee,ryoti,swayl,snoke,awork,sopra,alley,kideo,crema,spaed,mired,idees,cuffs,baned,styre,poire,lacky,akita,clime,curst,wades,savvy,musks,stell,satis,wussy,zings,ernes,cluck,susus,beach,saola,rifte,tabun,crons,infer,sypes,build,unfed,pomps,mohur,hoser,autos,unset,neath,macte,whist,acrid,ecads,mamba,chocs,smush,cytes,thanx,rings,vinos,woful,awash,bayer,boody,alans,birzz,campy,retch,motus,taish,scots,panim,ethyl,knurs,coots,dacks,munge,ungag,eland,equip,elite,unlay,nonyl,rouky,varve,milts,soare,gecko,filly,jills,sakti,scrob,trope,seral,atocs,sarvo,nonic,masse,picra,dhaba,sensi,fiber,darns,bouge,rayls,tints,phuts,choil,vivre,osone,kemps,holds,bends,datil,hoten,slums,amrit,gouge,pikis,eensy,clans,dough,scads,blame,ghyll,bliss,tumps,budes,senza,gyres,verte,fluff,dhaks,huzzy,omber,piony,leams,amove,unbar,porus,ooaas,janty,leear,trull,droil,nitta,savey,qaids,caphs,brawn,wokka,mumph,ratas,hexer,lucid,oobit,pleat,holks,youks,dashi,leaps,kills,skein,shoat,hurra,juves,strow,shalt,lollo,duxes,chias,tined,plead,pricy,vanes,hazes,remen,rushy,abyss,vares,gally,dizen,wedge,kanga,haham,nuque,spurn,until,laree,midst,naker,mened,karsy,masks,pucer,goors,knoop,ticky,sargo,split,skeen,stood,togae,slush,prise,eales,eater,glaur,prexy,raffs,wekas,snout,court,ileus,palsy,sevir,rynds,ducal,ovels,juice,plets,woofy,panty,talak,vinas,bapus,weber,olein,gamed,tolaLine truncated
|
||||
"built,mawrs,lagar,marid,grebo,spare,pieta,noxal,abase,kinky,priss,hacek,vicar,marka,temse,serir,blaze,ngaka,heeds,belar,qophs,noted,brews,hafts,ratti,firma,carbo,wrawl,adrip,mvuli,voici,fetor,recks,perne,dowls,mutic,fetts,wetly,exine,kauru,bubby,stope,haram,herry,dacha,saice,theow,awing,frugs,swoln,odium,parti,gaily,dwelt,money,laned,murry,skits,rowme,tatou,frere,ukase,dolci,rhomb,tynes,corer,nerdy,peats,fondu,okole,close,afoul,swole,scena,talaq,paces,senas,faiks,pneus,rubai,jibba,would,patly,fused,crena,doley,mesca,eches,serai,tinge,saran,glout,solei,tinny,noter,upful,venue,natch,panic,curia,yomps,diode,binks,stear,bring,pekau,peace,humps,ixnay,maunt,balot,cosec,purao,armed,liars,hifis,docks,wuxia,phooo,russe,gutty,urena,aidas,vials,posse,chaos,admen,pilum,gunks,wowed,inurn,trugo,skulk,tutee,wrier,pyxes,sonce,tsubo,floss,algid,allen,nolos,stoss,weamb,aspro,mucho,snebs,talcs,pekes,waxen,marls,clavi,titin,swing,uncut,entia,hwyls,vanga,sayne,based,knurr,raiah,pings,vegan,mound,menta,plowt,gaids,pipal,deets,caaed,sayed,hates,debes,masts,resus,unarm,chays,rowns,babus,lapse,alter,bruin,furol,loord,swits,majoe,faves,albas,lysis,shark,peach,vades,owlet,brods,lyams,rater,moves,mynas,arris,hiems,quags,roans,reamy,tanti,heady,pirns,casts,dress,enews,amido,kinin,adits,puffa,pucks,shuts,mural,mered,whens,gowls,darer,helix,mirly,womyn,raggs,wilja,monie,neram,nyams,slams,dirke,tehrs,skens,tavas,table,horal,oupas,navar,hippy,rawly,diets,surgy,lysol,seths,panny,arums,mulls,mazut,drony,virga,ratha,lunet,rowet,dancy,defat,splat,roger,nucha,plout,mungs,sloan,usque,sakia,guido,hissy,jigot,tyran,drapy,tight,puses,droop,pepla,deter,gleby,primo,haves,pinda,rieve,zabra,lieus,parky,fanum,yoyos,styte,faros,heald,gases,samas,scapa,teary,ginge,lossy,embed,genal,burps,fitly,mythy,purge,bauks,kwink,bawty,ferox,bazar,almah,hains,peaky,plops,noris,brink,kirns,sordo,boomy,dufus,nards,galea,corps,speil,deary,flyby,kerky,unlid,drums,undos,cools,conge,dodos,pavis,patas,pands,tipis,puree,statu,rotte,beads,evert,teals,vined,hamba,gopik,crans,wiled,weedy,pacha,parge,perdu,vaunt,pequi,piccy,leany,marly,whiff,neive,homie,rests,aware,riads,somas,lytta,dorps,idyll,mulsh,pence,vatus,zoril,semes,argot,drouk,linac,cloke,nimbi,anger,mojos,bozos,choof,avale,viewy,mazas,canon,abune,south,slobs,moler,ships,souls,boded,waspy,nowls,trice,nisse,teats,caned,biner,tronc,cutty,truss,diyas,merse,waffs,koala,agate,ezine,nudes,rigmo,evhoe,slick,wadas,olate,oleic,woons,typey,oncer,gumma,sweet,heavy,psych,khyal,cates,brens,socle,katis,frisk,withy,sauba,heyed,rosti,feuds,niced,kyles,rawns,gadje,jabot,crise,tacet,lines,esses,grope,swash,ngati,wised,garda,locks,gelid,horst,attas,sycon,telia,algal,frais,fango,yetis,marcs,addio,anils,bunce,wirra,gnarr,ludes,glary,coxed,beaky,ylids,added,patta,veles,curie,unces,ledum,sansa,adust,aurei,spitz,logie,unget,thema,yowls,coifs,surge,softa,gyoza,putas,blaes,blaws,celly,butte,stamp,grain,bursa,ligge,sagos,sheal,atrip,emeer,degus,stush,kynde,cured,pfftt,mills,minxy,tykes,atmos,suite,ramis,merks,coyau,twats,bussu,mooth,terfs,fests,enmew,nould,hoosh,nylon,alefs,pikas,zoons,busks,qapik,remou,cawed,pally,muntu,teugh,slimy,bassi,almes,outgo,batta,ranks,lanks,roguy,zippy,foids,welks,rowan,psias,yowes,allow,rants,faker,dells,rumbo,titir,combi,winds,rubby,deify,quena,crape,jokey,wootz,wites,koses,nizam,pratt,pukka,fixes,bobby,ruffs,magot,argal,ammas,scoog,aweel,gilia,payer,touns,rupee,marge,colle,pates,doles,rappe,lakhs,carve,grana,wytes,macks,nerve,mitry,crips,bewig,melic,andic,bands,mugil,quoin,pools,omiai,drips,eared,yippy,kexes,carol,lores,bises,obias,thurl,tajes,emmew,chiko,orbit,jimmy,vexer,begob,askos,liwaa,burgs,demob,imbed,lahal,chats,laves,cohos,jowar,nurdy,iodic,whoso,broth,soops,tried,crins,jaune,coarb,cocas,mugho,smoko,scaup,feart,skets,hebes,doums,korat,briki,soars,oaths,abeys,brome,appro,tarot,lymes,quich,amend,doers,duded,hushy,grogs,cleik,audit,kutai,skyre,milky,peens,cooze,rusks,meiko,boost,booze,jeans,neeps,kobos,dazes,bayes,spims,pegma,maddy,bwazi,xylic,agaty,rawdy,soree,dinge,assam,stoic,nyaff,rowed,ledes,polls,olive,strim,taras,piner,steno,seems,notum,cripe,stoai,nsima,buyer,gluey,poohs,hovea,jocky,kinas,fangs,yawed,tungs,ephas,povos,scram,camos,ympes,spacy,nucin,looed,ambit,stile,palay,roche,rices,diota,gatch,vasty,otium,flory,crues,petri,sycee,nippy,ludic,sayid,welke,links,uptie,clone,ploat,tiyns,humic,atopy,basan,boney,drill,oaten,mucro,seals,cavil,rimer,odyls,naked,maqam,kreng,sards,simis,geoid,butoh,aboma,oyers,sheas,laich,imine,ficus,ducky,mobey,eaves,warez,hiper,laund,punim,jouks,route,roods,wushu,mokos,ovens,ebons,paths,eidos,glute,abies,tiles,unsew,bidon,teams,muffs,reave,vigil,dutch,japes,spear,dules,caneh,lakin,arnut,fados,tonus,flied,serge,rives,scorn,orbat,aline,genre,hiant,quorl,cream,pebas,coala,prodd,lamps,wroth,shill,penal,spine,citee,milds,choir,bilat,betty,culpa,dilly,perfs,malik,kondo,surds,slice,ablet,omasa,hanch,vilde,fayne,tense,stare,disas,dowds,braza,dooks,verts,intro,mirah,karks,noneLine truncated
|
||||
"pahus,goldy,unbox,dowts,copse,snuck,haars,bello,nebek,slipt,tokes,hewgh,alose,bucks,paips,sewar,mieux,madal,ahint,plish,nudgy,tuque,inane,galas,neeld,urdee,amyls,keyed,laccy,slide,adoon,kaugh,naice,vamps,twier,glebe,borms,krunk,buaze,imari,sepad,vertu,giant,repos,courd,resin,flubs,unbid,felty,decaf,razoo,hibas,pouts,kliks,eyras,fogey,aleem,papey,fiers,motet,tulip,steek,scaff,queen,tango,reput,rorie,monas,ponks,deros,venal,piped,alaps,fitts,besee,fauld,moley,apods,circs,plook,asyla,waite,wound,laddu,tices,bleys,tuffe,kains,kunds,seeds,alala,mawns,neddy,mengs,ewhow,doucs,tools,wacks,kypes,bunjy,braid,spifs,wyled,choli,quint,panto,clown,cokey,syver,coths,yokul,vogue,olden,vardo,vibed,cires,fonds,adaws,amuse,sisal,phone,kwaai,bruhs,runty,wonks,psoas,alowe,knows,azido,raike,sidey,sades,tared,culch,poted,afoot,kerel,kakis,devos,metol,avize,saids,kilts,punas,copal,prial,angry,sooty,slane,quest,goopy,plore,dubby,himbo,dogal,appal,dorrs,cando,rewan,kagos,cloop,adays,gulet,porte,clept,haiks,miyas,yeard,mifty,oater,liner,manos,rower,shura,quirl,jafas,clack,pikel,menad,fluky,reans,enate,mates,abray,thans,kuyas,songy,incog,gauds,hokum,dogma,blend,areca,dirge,sarge,ryijy,latus,gambe,shack,renal,reuse,slish,pagan,stirp,kylie,calif,hands,kirks,docus,uveas,shaps,gonzo,mummy,thawt,actin,hoard,howls,bundt,blush,supes,crogs,seron,solve,chert,totty,aborn,vinho,agree,snush,danks,proud,racon,shaly,paged,pulse,shank,onset,spain,regex,ureas,orlop,mushy,cronk,latch,blive,honds,gamay,heath,arnis,purau,tofus,hinds,mewed,cowks,birls,goyim,stend,alant,nolle,fulth,tasar,luvvy,dives,lunge,snick,derth,cosey,disci,uncos,yukos,tansy,metal,barde,saucy,smear,frore,kneel,grama,crios,chedi,endue,bafts,ralli,smore,tenor,pawns,lodge,resol,caron,tiare,poove,undam,kohas,vapes,river,caffs,blahs,vapor,brach,retax,molls,beans,kempy,ceric,scody,ditty,porth,tarte,faqir,jagra,ogive,bezel,malae,tapis,acryl,dully,trave,clads,rhime,skirr,snoot,prowk,satai,embus,colza,affly,dashy,bugan,wahay,shent,hodja,valli,dauby,oracy,beano,corms,hukou,corni,laxes,noisy,shtum,canny,outer,flaff,fuels,sieur,rehab,gleet,disco,mammy,aevum,hiped,moors,agley,ebene,coady,rerun,tynde,fetwa,garba,troys,kapai,calix,dites,ments,praus,loofs,troth,abore,rimae,early,carrs,salet,miens,joeys,quirt,embar,coked,leirs,cozey,flask,benis,guise,patsy,kirri,brava,ridge,jotas,reaps,varus,twits,freak,chads,plaud,oskin,oiran,manks,adopt,triol,logan,decos,glams,sadhe,cirls,theme,siver,quats,mbube,drole,mhorr,garbs,quyte,kells,mitch,adage,ozeki,vrils,mohos,leeps,skivy,plaps,clied,saith,saiga,swain,klutz,varan,ahing,louse,clink,peery,burke,scold,kunas,forze,syboe,pagod,wicca,infos,guimp,kails,evils,hosed,vaned,charr,muras,matzo,draff,range,buffo,alist,epoch,roral,yucko,crack,filet,pouff,biggy,festa,moyle,ramee,bajan,margs,gored,copha,dangs,bardo,micas,hiree,vegie,beers,aalii,yoghs,navel,heare,fease,jorum,exeme,upped,nepit,cover,smack,poney,preif,cauls,gecks,burka,dates,bonce,ruffy,layer,arbah,mirkn,train,enact,fremd,drave,feare,erect,sweal,pring,batts,thees,nudge,antas,doved,pelsh,pigmy,muiry,flora,bonne,oping,nauch,pawaw,golly,muirs,minae,agloo,faked,jamon,bowne,souly,funsy,claro,whids,hubba,flong,snath,howes,lomas,quass,aboil,nabis,shako,shoos,pooks,huger,totes,glial,atigi,mirth,loans,fagin,phpht,china,toter,fleek,bocce,henge,sunis,nenta,booky,miles,steez,pions,rapin,wolve,popup,unode,gymps,stane,naids,pleas,orgia,dames,abjad,peece,lurgi,frill,gitch,feued,lowps,stong,price,holed,eaved,humas,dered,swags,imide,hydel,annus,trons,moppy,rello,bents,bubal,dinlo,kiwis,urine,namer,feens,idler,lanes,scrog,mased,hider,ruffe,fakey,zaris,perts,satyr,suped,ganch,tibia,ceded,rabbi,stuns,stoop,abysm,aulic,carpe,moste,signs,steak,mixes,panir,grids,saddy,chode,viola,gigue,xerox,mirid,manor,strut,curet,bonze,winks,morts,geals,amlas,stubs,kembo,subah,daled,hells,apres,lifer,jhils,kerry,gauss,dayal,films,kophs,suede,unlap,valid,wayed,mechs,picot,halva,funic,zexes,pitas,nondo,waddy,lobar,rowth,threw,efits,nosir,iller,dobes,kabar,toffs,rehem,reign,lenti,hucks,knock,lovey,lants,skies,grouf,smuts,rifts,mauve,retro,tikia,chomp,nazar,clonk,reget,ovoid,sechs,mukim,kyars,amaze,valor,woald,fisks,giber,cames,round,leese,pubic,barbs,issue,razor,myths,kranz,baaps,poxed,vadas,jowls,twill,frita,naiad,ureal,gusla,lipid,sojas,nempt,huers,puler,whoop,obied,litho,debus,panax,befog,kilig,nites,carex,logic,swage,frati,edict,dalle,iliad,boots,scour,bacon,tares,sexer,masha,monal,pappi,flume,abord,chivy,parol,neons,mucko,event,yapon,fusks,sownd,frows,paxes,whaur,skean,owing,gilet,coops,abbed,onlay,akees,zouks,biers,mooni,kafir,lysin,xviii,rusas,reeds,yohay,salon,hones,urupa,smirs,shirr,meter,agrin,paved,lipas,ensky,trust,gippy,enjoy,appuy,fitna,moues,waxes,koiwi,lungi,bunch,goier,frape,keirs,hertz,nairu,promo,sunup,refis,scudo,olent,bromo,soole,conch,tepid,menge,aught,yecch,burns,bogie,carpi,arete,braky,naled,boned,talon,gonia,deray,ceres,inker,ranga,panga,reune,slomo,trema,ycled,heder,narco,quake,mixie,belle,genro,hurty,smeke,gossLine truncated
|
||||
"ooids,gully,ramen,suber,sutra,preak,drere,bowel,hedgy,deles,refer,tofts,milty,frosh,manas,octan,kerfs,enorm,rebel,guaco,nisei,pelma,nasis,goeth,oared,segar,aflow,skims,wited,sough,hokku,pardy,teems,michi,decks,twonk,fales,ideas,dirls,snive,clows,leses,wakas,wahoo,memic,eniac,sexor,kimbo,oases,derms,pulka,sutor,botch,unaus,vughs,resto,kimet,malwa,rioty,enemy,ruder,ahent,yoker,shmoe,grame,lytic,potty,odism,acini,mules,soler,antae,lound,nicky,brags,doeks,ulnas,dagga,cadre,coypu,cotts,galis,lowes,bezzy,skran,iliac,pases,halve,flesh,runer,wefts,rebbe,isnae,siroc,gules,clomb,amaut,newts,ladle,gibes,taggy,haply,kalpa,bilks,blown,yomim,attap,educt,ingan,linos,gooey,fibro,maise,ngoma,adzed,flawy,toran,jades,offal,twilt,wager,booby,koban,yules,irone,azygy,tryke,sweys,neist,nunks,rogue,riato,mulai,obeah,emule,mocos,girns,sorta,cooky,causa,ayelp,mitta,emote,pelau,thang,appui,momus,asity,taunt,damme,ulzie,suids,cymes,muzzy,anata,spyre,roate,boppy,kelep,pilaf,caver,pheer,derat,peeps,tania,murva,surer,yatra,kapur,udons,coate,thelf,robbo,chynd,noups,brize,texes,bosky,aargh,alert,shews,lirot,lofty,jolly,palea,geode,suety,ponga,fores,voila,beech,cumin,polyp,murid,laufs,alarm,tyros,ragee,berry,ytost,welts,tinct,cacks,snuff,ecigs,plein,reded,dived,kests,mucky,grays,berth,adore,raksi,whift,pirks,roven,syncs,nomad,medal,cerge,gruff,macho,rooty,yabby,linty,delft,dawah,resow,blink,salty,sowff,lweis,tufts,gipon,veale,tigon,jumps,crick,pacos,porae,doter,rewax,doofs,sarus,vales,douse,cheka,gores,crusy,weals,pause,cours,kakas,nixer,scran,cumec,metes,wifie,eleet,heart,coste,duped,aloin,drags,nouny,vacas,riffs,locos,botty,catch,burnt,belli,swack,thole,stimy,holos,tapet,recti,fasts,musty,keech,moult,hella,tends,rugas,reede,spyal,blitz,octad,fiery,using,robot,unsaw,lipes,methy,gived,faces,touse,bikie,stulm,palla,toled,whies,herls,write,kanat,perch,shirk,roofy,skeps,beamy,rully,swops,hauns,horse,giust,minis,sized,zizit,crump,hulas,haika,verve,mooch,velum,techy,fouds,manty,mesal,alkie,roons,capos,grump,nooit,afanc,packs,mores,ambry,rugal,parly,alifs,legge,wawls,nadir,tenny,erned,solde,yerks,eagly,abeer,reply,gybes,cabre,vifda,vivid,gyver,psoai,pacas,piums,emits,touts,jotun,regal,sheen,tesla,unmix,grace,peasy,sohur,ganja,pylon,sente,gumbo,gruel,tanty,edile,rhumb,tilde,slyer,lasts,carry,kaifs,leche,bunks,moats,woose,mists,nihil,buffy,cabby,sarod,quoys,sered,livid,attar,craig,chave,macro,blech,steik,umiaq,donee,yonks,nopal,tells,gluts,bdays,flics,metas,eagre,spoon,hogos,pinol,fezzy,doody,alaap,rugae,upsey,chyme,raile,urbia,booth,manga,preen,hilum,luffs,baits,raven,preux,gauze,galed,pungi,hedge,heirs,reive,indue,wyted,torsk,fuffy,filmy,carks,neosa,lownd,quaky,lofts,hemin,vodun,pared,kawed,gurly,horas,gelly,foyle,shawm,faine,ulcer,podex,ariot,krays,towny,frags,humid,mints,sifts,merer,inerm,exude,selva,aspis,cinqs,yogic,auris,zests,bazoo,agios,rotal,binal,sella,astir,wacke,owler,saick,kylin,coxae,zokor,vying,ameer,taken,yests,haver,mugga,axiom,primi,pikau,scout,nears,sozin,pooly,roped,sezes,lavas,pilis,tabis,lazar,smote,feyly,japer,inros,griot,alurk,duvet,sepoy,rusts,curse,hahas,kerve,kyaks,longa,degum,dauts,ungum,walds,hollo,forty,howfs,nazis,enlit,gilpy,odeon,wheal,flobs,forby,lumen,opted,neebs,gaged,sulfa,toyon,clous,apart,merde,sukhs,abmho,rheid,bumfs,cogon,larks,parki,rotos,movie,nines,sambo,saint,fungo,snafu,alkos,staun,agros,bawks,takhi,snell,banks,inlet,rumen,shape,pesky,bacco,warst,dukes,typps,jauks,napoh,niton,khula,prese,tranq,gazes,majos,tuism,bacca,lasso,askew,anigh,heaps,sages,brise,kaims,tikes,bulge,gemma,tafia,annat,casks,taxol,nitty,miter,riqqs,cecum,avant,smurs,stray,yesty,boyly,skate,rangy,dosed,edify,gride,burds,agone,coxal,djinn,fadge,abele,skail,apeek,toons,spaes,payor,legit,gapes,takin,cuits,aisle,amici,ceroc,warms,soldi,spaws,naans,dolor,arnas,image,mouly,axels,codec,faffy,slopy,peppy,laari,oligo,prior,lupus,chare,rindy,proto,haick,leash,xysts,mufti,dandy,oxter,fries,hylic,musse,advts,musta,farad,ontal,above,punto,nicad,coirs,knosp,miros,samfu,doggo,oiler,mowie,mirza,fecal,rouet,yikes,jorts,denay,fulla,liart,sojus,ajupa,yumps,limos,gaped,miggs,haint,micos,weids,rorid,logos,mopey,axled,prove,lapje,ramps,oxlip,petal,aphis,fauch,goods,chiao,lazed,opahs,titch,lento,cahow,hutia,mochi,atlas,jolts,prats,omees,nipas,danio,dhoti,vents,allel,loure,erses,dumka,salop,zante,douma,agush,girsh,scyes,apism,ceaze,dicta,miffs,blady,feebs,tyiyn,arear,ruote,chewy,bonks,stogs,fuzil,suhur,oxeye,swamp,proyn,coped,grape,tichy,aarti,rudas,inbye,mopsy,tasca,amiga,gland,beeps,metis,bogey,glatt,nutty,dearn,ranse,aural,sleek,acton,talea,ovine,saist,rivet,leapt,wiper,alums,skimo,skimp,rouse,delay,gazal,appts,blert,cajun,anion,soxes,flips,tipsy,cuspy,trays,amate,yente,ictal,acoel,tumpy,scrim,scums,boing,roils,coffs,shily,tronk,tours,brood,dumas,cokes,dictu,think,teend,haugh,kenaf,lousy,wreak,maqui,radio,pasmo,grens,skint,mneme,liker,bukos,cisco,clang,cycad,micht,desex,naevi,salue,binge,abask,cager,orals,fryer,photo,grins,nurrs,meils,gilly,prorLine truncated
|
||||
"flair,goofs,laiks,karas,amity,gawsy,moggy,lordy,samba,alone,eatin,muset,poker,trins,grist,paver,istle,plaid,groat,blond,bortz,galop,aside,calpa,augur,limes,yrivd,quail,mongo,pagle,yechs,unwit,aback,pilot,acids,ewked,beray,prian,rypin,folic,plied,okies,gushy,banda,worms,divis,pakki,muxes,cudda,conga,dined,tiffs,moxas,terce,thine,douar,gemel,dreks,pedal,towai,thigs,taits,morat,nappe,novia,wembs,smaze,talik,oncet,pecks,pruny,blays,dwaal,luaus,urger,manna,xenon,spags,biter,bunty,sinky,otary,euked,apays,scala,ostia,nasty,oopsy,dauds,beget,harry,meath,voxel,jelly,hotch,sties,creme,rules,dobra,kloof,unmad,podus,yowsa,aiyah,folia,lowry,acedy,alecs,prowl,prune,elude,xylem,lumme,nisin,ruble,tulps,yopos,duits,lulus,necks,tapir,lezzo,leaze,evegs,bumps,odder,grips,prone,fugal,hullo,morne,owies,lobby,yarns,teade,smirk,yandy,cusks,jures,psyop,rhino,kanji,traik,areic,butle,spina,letup,khets,loess,mason,orpin,ureid,nurls,suona,bingo,namak,bassy,hasps,linky,molar,hoghs,walls,comma,titas,mache,trace,crine,coles,spard,hoked,eisel,pooay,rider,haldi,algas,tempi,absey,ancho,fetas,gojis,cleve,lohan,tasks,sedes,chang,pikul,roins,vatic,reast,catty,rafee,laika,soums,whiss,mayas,peple,kyack,pujah,percs,nexal,lochy,fordo,boyey,crumb,glums,demit,wanle,quate,amins,stuff,booty,spook,shool,sluit,manoa,antar,karat,nalla,chimp,frowy,buoys,kissy,furos,tenia,zloty,blear,kotow,sweed,oinks,midgy,unsub,golpe,lades,graph,minor,agbas,celom,wands,gonif,grime,mbuga,dozen,clunk,firth,kurus,meane,fenny,jaspe,golds,latex,diazo,brash,ahigh,pinot,pioye,swipe,flexi,buats,pamby,salut,humus,oleyl,kohen,deems,padou,colin,lepra,yorga,rokey,onces,weets,caput,pilae,yenta,ancon,belly,ripen,raake,palis,treed,boygs,slart,scuzz,tenno,aggie,kooks,petti,weeks,aided,hypos,imago,ameba,pager,award,scute,cippi,asdic,brins,meson,sunny,pyots,babby,ditts,spaul,patel,ching,loams,styed,nante,slipe,grese,wythe,heles,nummy,ketch,hoast,chump,dixie,idles,vodka,fermi,maize,aimed,azons,kamme,moits,exeem,goral,melch,shuck,taluk,pawls,gajos,ghaut,guile,gleys,jukus,gluon,manly,walty,yiked,corby,zaire,chary,arrah,droog,subby,ragde,ponds,oseys,grass,gurdy,aixes,marma,crimp,gassy,bronc,coopt,glibs,toman,dimps,vaids,liken,blowy,sware,print,yourt,unite,tadah,coria,whamo,teene,gormy,urvas,izzat,gyans,syens,fidos,strap,rebar,chile,rifer,urase,hings,major,rabid,tauon,ontic,garms,bombe,firni,germy,tunny,baboo,tulle,rendu,brule,oidia,dorse,apian,dabba,jumby,zobos,akoja,mated,waney,maaed,frank,flute,dryad,jupon,gyals,rurps,lovat,extol,sheol,wrate,afizz,septs,brede,chemo,vieux,sores,noxas,trock,nerts,misto,local,yourn,klong,missa,sukuk,peine,cabas,vardy,issei,boric,aleph,husky,hoven,aiery,apses,wharf,addas,assay,plast,succi,tales,datum,ulmin,eusol,grief,prong,whaps,vocal,vower,nides,rebus,brows,scath,scabs,rozes,blunt,adbot,myons,rally,eyers,fille,frist,zimbs,aymag,leeks,judas,flirs,prunt,inorb,fyked,dicto,chili,spial,lisks,earth,scone,bemud,tosas,tribe,daynt,cohab,preop,oware,atilt,shrow,glass,pavvy,raved,mindi,ochre,poley,deave,umpie,ranch,clair,fugus,bunny,tarps,doomy,rexes,vroom,lauan,aster,meets,blini,pavan,stull,flimp,mirks,shola,passe,skart,gazar,trefa,teind,widdy,dinos,cline,toked,cubic,touch,steys,lazos,samey,chino,rajah,lycee,popia,shaws,sates,other,duchy,moove,orcas,point,beaus,vigor,scaud,girls,gigas,folly,rumpy,gaols,motis,sauch,salud,pents,vozhd,obbos,hokas,kombi,bokeh,laics,namaz,slain,teets,force,phies,genic,inver,tachi,argus,prick,users,bluff,hecks,bewdy,silds,vivat,knowe,besaw,muons,noils,buiks,krait,green,dooky,yeeds,gings,kalis,chugs,soras,buyin,silks,serer,mercs,arses,limbo,moufs,carta,venin,crudo,stoit,cynic,rhabd,kaphs,arsed,noois,flier,ylkes,firie,thowl,virtu,tules,silva,trest,krill,houri,lezes,infra,honda,ansae,nicol,unkey,stims,trapt,abbot,pelch,greed,fault,jolie,axial,estoc,misls,norma,pleep,moire,kudzu,goose,flamy,gayal,gamba,urped,sybil,goxes,wives,muruk,often,welsh,poste,bisom,walie,tuber,doura,reerd,baons,rubin,relic,towzy,tinea,after,volva,egger,dungy,shone,haugo,zitty,yummo,caups,margo,lanas,tinds,fabbo,blanc,quilt,pekoe,napas,chaft,fique,milly,sigma,shmek,rumly,umbra,ovary,foley,nitid,nyong,ogled,tewed,bytes,litie,auger,cimar,seiza,tamps,bruit,tough,kopek,algae,rioja,deare,plips,rheas,proot,lolog,denet,hames,drubs,saute,swoon,fogle,tidal,deals,haler,aglet,segas,seric,neifs,dynel,oldie,feoff,ustad,hance,pixie,netop,raupo,keets,fusty,biked,buroo,mozes,paolo,phoca,dykey,ksars,rejon,dinks,egmas,nital,kilns,malas,hopak,pacai,kreef,lunch,kilps,losed,etwee,tenne,texta,wench,pects,mazey,spork,hatha,cotes,poncy,while,ozone,bantu,salol,remit,lippy,cocus,urbex,purls,saeta,linch,plock,levis,slant,murti,fades,found,hakam,buses,gogga,fouet,silky,bigha,begem,lease,spire,binds,sampi,pyres,newel,baghs,rudis,niefs,donne,howto,yucky,ummed,domal,essay,nujol,limba,blain,kbars,boets,lotta,diker,aloes,felch,aggry,kutus,amuck,smeik,herns,witch,yaird,pryse,munch,finos,molas,dolee,rubor,zonae,boyed,novas,kheth,fudge,wodge,aflap,guyle,arepa,hault,stobs,faxed,cento,avail,usnic,songLine truncated
|
||||
};
|
||||
private static final Set<String> WORDS = load();
|
||||
|
||||
private BlockleDictionary() {
|
||||
}
|
||||
|
||||
public static boolean contains(String word) {
|
||||
return word != null && WORDS.contains(word.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
public static int size() {
|
||||
return WORDS.size();
|
||||
}
|
||||
|
||||
private static Set<String> load() {
|
||||
Set<String> words = new HashSet<>();
|
||||
for (String chunk : CHUNKS) {
|
||||
Collections.addAll(words, chunk.split(","));
|
||||
}
|
||||
return Set.copyOf(words);
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.craisinlord.antarchy.content.computer.blockle;
|
||||
|
||||
public final class BlockleGame {
|
||||
private BlockleGame() {
|
||||
}
|
||||
|
||||
public static String evaluate(String answer, String guess) {
|
||||
if (answer == null || guess == null || answer.length() != 5 || guess.length() != 5) return "XXXXX";
|
||||
char[] result = {'X', 'X', 'X', 'X', 'X'};
|
||||
int[] remaining = new int[26];
|
||||
for (int index = 0; index < 5; index++) {
|
||||
if (answer.charAt(index) == guess.charAt(index)) {
|
||||
result[index] = 'G';
|
||||
} else {
|
||||
remaining[answer.charAt(index) - 'a']++;
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < 5; index++) {
|
||||
if (result[index] == 'G') {
|
||||
continue;
|
||||
}
|
||||
int letter = guess.charAt(index) - 'a';
|
||||
if (letter >= 0 && letter < remaining.length && remaining[letter] > 0) {
|
||||
result[index] = 'Y';
|
||||
remaining[letter]--;
|
||||
}
|
||||
}
|
||||
return new String(result);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -640,7 +640,7 @@ public final class CreativeTabContents {
|
||||
acceptPotionFamily(output, potionPath);
|
||||
}
|
||||
for (String diskId : List.of("antarchy:introduction", "antarchy:chronosphere", "antarchy:toreterror_arena", "antarchy:high_fructose_corn_syrup",
|
||||
"antarchy:elythia", "antarchy:cavaryn", "antarchy:thoraxis", "antarchy:temporal_fields", "antarchy:bloodglass", "antarchy:vortex")) {
|
||||
"antarchy:elythia", "antarchy:flying_squirrel", "antarchy:cavaryn", "antarchy:thoraxis", "antarchy:temporal_fields", "antarchy:bloodglass", "antarchy:vortex")) {
|
||||
ItemStack disk = new ItemStack(AntarchyObjects.FLOPPY_DISK.get());
|
||||
disk.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), ResourceLocation.parse(diskId));
|
||||
output.accept(disk);
|
||||
|
||||
+12
-1
@@ -32,6 +32,7 @@ import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
@@ -117,7 +118,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
|
||||
int lifetimeTicks, UUID queenId, int count) {
|
||||
DimensionalTearEntity tear = create(level, pos, yaw, lifetimeTicks);
|
||||
tear.queenOwnerId = queenId;
|
||||
tear.queenManticoreCount = Math.max(1, count);
|
||||
tear.queenManticoreCount = Math.max(0, count);
|
||||
QUEEN_TEAR_IDS.computeIfAbsent(queenId, ignored -> new HashSet<>()).add(tear.getUUID());
|
||||
return tear;
|
||||
}
|
||||
@@ -472,6 +473,16 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
if (this.queenOwnerId != null && source.getDirectEntity() instanceof Projectile
|
||||
&& !this.level().isClientSide) {
|
||||
this.removeLinkedPair();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fireImmune() {
|
||||
return true;
|
||||
|
||||
+52
-37
@@ -55,10 +55,8 @@ import com.craisinlord.antarchy.content.entity.royal.decree.ComeNoCloserDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.DoNotRunDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.FightMeCowardDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.HandsOffTheCrownDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.KeepYourDistanceDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.KneelDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.NoRespiteDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.NoRetreatDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.RoyalDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.ShowNoMercyDecree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.decree.SkyIsMineDecree;
|
||||
@@ -89,8 +87,6 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private static final int BEHAVIOR_SCORE_CAP = 12;
|
||||
/** Keep the tree-bound patrol safely inside the authored tree footprint. */
|
||||
private static final double TREE_ORBIT_RADIUS = 128.0D;
|
||||
private static final double TREE_RETURN_RADIUS = 192.0D;
|
||||
private static final double TREE_RETURN_RELEASE_RADIUS = 144.0D;
|
||||
private static final double TREE_ORBIT_ANGLE_STEP = 0.014D;
|
||||
private static final int TREE_ORBIT_PATH_SAMPLES = 16;
|
||||
private static final double KING_FOLLOW_RANGE = 192.0D;
|
||||
@@ -108,7 +104,6 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private double treePatrolMaximumY;
|
||||
private double treePatrolAngle;
|
||||
private boolean treePatrolBound;
|
||||
private boolean returningToTree;
|
||||
private int patrolCooldownTicks;
|
||||
private int decreeCooldownTicks;
|
||||
private int activeDecreeTicks;
|
||||
@@ -118,6 +113,7 @@ public class KingEntity extends RoyalBossEntity {
|
||||
private int iceballCooldownTicks = 120;
|
||||
private int iceSpikeCooldownTicks = 160;
|
||||
private boolean decreeRetreatPressure;
|
||||
private int royalBoundaryGraceTicks;
|
||||
@Nullable
|
||||
private ServerPlayer exileTarget;
|
||||
private int exileTicks;
|
||||
@@ -151,7 +147,7 @@ public class KingEntity extends RoyalBossEntity {
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return createBaseAttributes(AntarchySettings.kingHealth(), AntarchySettings.kingAttackDamage())
|
||||
return createBaseAttributes(AntarchySettings.kingHealth(), AntarchySettings.kingAttackDamage(), AntarchySettings.kingArmor())
|
||||
.add(net.minecraft.world.entity.ai.attributes.Attributes.FOLLOW_RANGE, KING_FOLLOW_RANGE);
|
||||
}
|
||||
|
||||
@@ -373,14 +369,11 @@ public class KingEntity extends RoyalBossEntity {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!this.level().isClientSide && this.shouldReturnToTree()) {
|
||||
this.returningToTree = true;
|
||||
this.setTarget(null);
|
||||
}
|
||||
super.tick();
|
||||
if (this.level().isClientSide) {
|
||||
return;
|
||||
}
|
||||
this.tickRoyalBoundary((ServerLevel) this.level());
|
||||
this.tickComeNoCloserIndicator();
|
||||
this.tickRoyalPunishment();
|
||||
this.tickPendingExileAnimation();
|
||||
@@ -390,21 +383,6 @@ public class KingEntity extends RoyalBossEntity {
|
||||
ServerLevel serverLevel = (ServerLevel) this.level();
|
||||
this.iceBuildup.entrySet().removeIf(entry -> serverLevel.getEntity(entry.getKey()) == null);
|
||||
if (this.iceBuildup.size() > 16) this.iceBuildup.clear();
|
||||
if (this.returningToTree) {
|
||||
this.setTarget(null);
|
||||
this.decreeRetreatPressure = false;
|
||||
if (this.treePatrolBound && this.treePatrolCenter != null) {
|
||||
this.setRoyalFlying(true);
|
||||
if (this.isWithinTreeReturnReleaseRadius()) {
|
||||
this.returningToTree = false;
|
||||
} else {
|
||||
this.tickTreePatrol();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.returningToTree = false;
|
||||
}
|
||||
}
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
|
||||
this.trackBehavior(target);
|
||||
@@ -474,14 +452,44 @@ public class KingEntity extends RoyalBossEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldReturnToTree() {
|
||||
return this.treePatrolBound && this.treePatrolCenter != null
|
||||
&& this.horizontalDistanceToTreeSqr() > TREE_RETURN_RADIUS * TREE_RETURN_RADIUS;
|
||||
}
|
||||
|
||||
private boolean isWithinTreeReturnReleaseRadius() {
|
||||
return this.treePatrolCenter != null
|
||||
&& this.horizontalDistanceToTreeSqr() <= TREE_RETURN_RELEASE_RADIUS * TREE_RETURN_RELEASE_RADIUS;
|
||||
private void tickRoyalBoundary(ServerLevel level) {
|
||||
if (!this.treePatrolBound || this.treePatrolCenter == null) {
|
||||
return;
|
||||
}
|
||||
double boundaryRadius = Math.max(32.0D, AntarchySettings.royalBoundaryRadius());
|
||||
double distance = Math.sqrt(this.horizontalDistanceToTreeSqr());
|
||||
if (distance > boundaryRadius) {
|
||||
Vec3 towardCenter = this.treePatrolCenter.subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||
if (towardCenter.lengthSqr() > 0.001D) {
|
||||
towardCenter = towardCenter.normalize();
|
||||
this.getMoveControl().setWantedPosition(this.getX() + towardCenter.x * 12.0D,
|
||||
this.groundYBelow(this.getX() + towardCenter.x * 12.0D,
|
||||
this.getZ() + towardCenter.z * 12.0D) + FLYING_PREFERRED_HOVER,
|
||||
this.getZ() + towardCenter.z * 12.0D, 1.4D);
|
||||
}
|
||||
}
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive()) {
|
||||
this.royalBoundaryGraceTicks = 0;
|
||||
return;
|
||||
}
|
||||
double warningRadius = Math.min(boundaryRadius, Math.max(16.0D, AntarchySettings.royalBoundaryWarningRadius()));
|
||||
double targetDistance = Math.sqrt(target.position().subtract(this.treePatrolCenter).multiply(1.0D, 0.0D, 1.0D).lengthSqr());
|
||||
if (targetDistance > warningRadius) {
|
||||
if (this.tickCount % 5 == 0) {
|
||||
level.sendParticles(ParticleTypes.END_ROD, target.getX(), target.getY() + 1.0D, target.getZ(),
|
||||
4, 0.3D, 0.5D, 0.3D, 0.02D);
|
||||
}
|
||||
if (targetDistance > boundaryRadius) {
|
||||
this.royalBoundaryGraceTicks++;
|
||||
if (this.royalBoundaryGraceTicks > Math.max(0, AntarchySettings.royalBoundaryGraceTicks())
|
||||
&& this.tickCount % 20 == 0) {
|
||||
this.invokeJudgment(target);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.royalBoundaryGraceTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double horizontalDistanceToTreeSqr() {
|
||||
@@ -824,10 +832,13 @@ public class KingEntity extends RoyalBossEntity {
|
||||
}
|
||||
|
||||
private void tickKingWholeBody(ServerLevel level, LivingEntity target) {
|
||||
if (this.isRoyalRecoveryActive()) {
|
||||
return;
|
||||
}
|
||||
boolean skyIsMine = this.activeDecree instanceof SkyIsMineDecree;
|
||||
boolean forceFar = this.decreeRetreatPressure
|
||||
|| this.activeDecree instanceof ComeNoCloserDecree
|
||||
|| this.activeDecree instanceof KeepYourDistanceDecree;
|
||||
;
|
||||
|
||||
{
|
||||
double distance = target.distanceTo(this);
|
||||
@@ -1013,7 +1024,7 @@ public class KingEntity extends RoyalBossEntity {
|
||||
for (int lane = -1; lane <= 1; lane++) {
|
||||
Vec3 point = start.add(line.scale(distance)).add(side.scale(lane * (i % 2 == 0 ? 2.0D : 0.0D)));
|
||||
double y = this.groundYBelow(point.x, point.z) + 0.1D;
|
||||
RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z));
|
||||
RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, new Vec3(point.x, y, point.z), this);
|
||||
level.addFreshEntity(spike);
|
||||
}
|
||||
}
|
||||
@@ -1053,12 +1064,15 @@ public class KingEntity extends RoyalBossEntity {
|
||||
Vec3 push = toTarget.normalize().scale(3.2D);
|
||||
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.5D, push.z));
|
||||
living.hasImpulse = true;
|
||||
living.hurt(this.damageSources().mobAttack(this), 6.0F);
|
||||
living.hurt(this.damageSources().mobAttack(this), this.scaleRoyalDamage(6.0D));
|
||||
}
|
||||
level.sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX() + forward.x * 6.0D, this.getY() + 4.0D, this.getZ() + forward.z * 6.0D, 12, 4.0D, 2.0D, 4.0D, 0.0D);
|
||||
}
|
||||
|
||||
private void tickDecree(ServerLevel level, LivingEntity target) {
|
||||
if (this.isRoyalRecoveryActive() && this.activeDecree == null) {
|
||||
return;
|
||||
}
|
||||
if (!(target instanceof ServerPlayer player) || !target.isAlive() || target.level() != level) {
|
||||
if (this.activeDecree != null) {
|
||||
this.endDecree(target);
|
||||
@@ -1137,6 +1151,7 @@ public class KingEntity extends RoyalBossEntity {
|
||||
this.activeDecreeTicks = 0;
|
||||
this.decreeRetreatPressure = false;
|
||||
this.decreeCooldownTicks = AntarchySettings.royalDecreeCooldownTicks();
|
||||
this.startRoyalRecovery(40);
|
||||
}
|
||||
|
||||
public void clearActiveDecree() {
|
||||
@@ -1175,7 +1190,7 @@ public class KingEntity extends RoyalBossEntity {
|
||||
if (this.judgmentCooldowns.getOrDefault(target.getUUID(), 0L) <= now) {
|
||||
this.judgmentCooldowns.put(target.getUUID(), now + 20L);
|
||||
this.projectRoyalSound(AntarchySoundEvents.KING_JUDGEMENT.get(), 4.0F, 1.0F, target);
|
||||
target.hurt(this.damageSources().magic(), 6.0F);
|
||||
target.hurt(this.damageSources().magic(), this.scaleRoyalDamage(6.0D));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -252,6 +252,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
}
|
||||
|
||||
private void tickDreamFireballs() {
|
||||
if (this.isRoyalRecoveryActive()) return;
|
||||
if (!(this.level() instanceof ServerLevel level)) return;
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive() || !this.canAttack(target)) return;
|
||||
@@ -360,6 +361,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
}
|
||||
|
||||
private void tickManticoreSummon() {
|
||||
if (this.isRoyalRecoveryActive()) return;
|
||||
if (this.manticoreSummonCooldownTicks > 0) {
|
||||
this.manticoreSummonCooldownTicks--;
|
||||
return;
|
||||
@@ -386,6 +388,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
? this.cooldown(Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks()))
|
||||
: FAILED_SUMMON_RETRY_TICKS;
|
||||
if (spawned > 0) {
|
||||
this.startRoyalRecovery(45);
|
||||
this.manticoreAnimationPending = true;
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
|
||||
serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(),
|
||||
@@ -413,6 +416,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) {
|
||||
int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
|
||||
int created = 0;
|
||||
int remaining = count;
|
||||
Vec3 towardTarget = target.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||
if (towardTarget.lengthSqr() < 1.0E-4D) {
|
||||
towardTarget = this.getViewVector(1.0F).multiply(1.0D, 0.0D, 1.0D);
|
||||
@@ -436,20 +440,24 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
double pairSeparation = 3.5D + this.random.nextDouble() * 3.0D;
|
||||
Vec3 firstPos = pairCenter.add(pairDirection.scale(pairSeparation));
|
||||
Vec3 secondPos = pairCenter.add(pairDirection.scale(-pairSeparation));
|
||||
int firstCount = Math.min(3, (remaining + 1) / 2);
|
||||
int secondCount = Math.min(3, remaining / 2);
|
||||
DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos,
|
||||
tearYaw, 240, this.getUUID(), Math.min(3, count));
|
||||
tearYaw, 240, this.getUUID(), firstCount);
|
||||
DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos,
|
||||
tearYaw, 240, this.getUUID(), Math.min(3, count));
|
||||
tearYaw, 240, this.getUUID(), secondCount);
|
||||
first.linkTo(second);
|
||||
second.linkTo(first);
|
||||
level.addFreshEntity(first);
|
||||
level.addFreshEntity(second);
|
||||
created++;
|
||||
remaining -= firstCount + secondCount;
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
private void tickQueenAbilities() {
|
||||
if (this.isRoyalRecoveryActive()) return;
|
||||
if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--;
|
||||
if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--;
|
||||
if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--;
|
||||
@@ -523,7 +531,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
case "crushing_gravity" -> {
|
||||
this.crushingGravityCooldownTicks = cooldown;
|
||||
this.royalEffects.start("crushing_gravity", CRUSHING_GRAVITY_DURATION,
|
||||
() -> {}, this::tickCrushingGravity, () -> {});
|
||||
() -> {}, this::tickCrushingGravity, () -> this.startRoyalRecovery(35));
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.6F);
|
||||
}
|
||||
case "acceleration" -> { this.accelerationCooldownTicks = cooldown; this.startRoyalAcceleration(); }
|
||||
@@ -565,7 +573,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
|
||||
this.getBoundingBox().inflate(18.0D), this::canDamageWithRoyalAttack)) {
|
||||
double distance = Math.max(1.0D, living.distanceTo(this));
|
||||
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
|
||||
living.hurt(source, this.scaleRoyalDamage(12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
|
||||
Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
|
||||
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
|
||||
living.hasImpulse = true;
|
||||
@@ -611,6 +619,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
}
|
||||
}
|
||||
this.frozenVelocities.clear();
|
||||
this.startRoyalRecovery(35);
|
||||
});
|
||||
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F);
|
||||
}
|
||||
@@ -669,6 +678,7 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
this.setRoyalAccelerated(false);
|
||||
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
|
||||
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
|
||||
this.startRoyalRecovery(30);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -355,7 +355,9 @@ public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
|
||||
}
|
||||
double distance = Math.max(1.0D, living.position().distanceTo(center));
|
||||
float falloff = (float) Math.max(0.25D, 1.0D - distance / radius);
|
||||
living.hurt(source, 26.0F * falloff);
|
||||
double damage = 26.0D * falloff;
|
||||
living.hurt(source, this.ownerEntity() instanceof RoyalBossEntity royalBoss
|
||||
? royalBoss.scaleRoyalDamage(damage) : (float) damage);
|
||||
Vec3 launch = living.position().subtract(center).normalize().scale(2.6D * falloff);
|
||||
living.setDeltaMovement(launch.x, 1.1D * falloff + 0.4D, launch.z);
|
||||
living.hasImpulse = true;
|
||||
|
||||
+96
-1
@@ -14,7 +14,11 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
|
||||
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
@@ -37,6 +41,8 @@ import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
|
||||
import net.minecraft.world.entity.ai.control.MoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
@@ -100,6 +106,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
private static final int CRUSH_MAX_BLOCKS = 24;
|
||||
private static final double CRUSH_MAX_RESISTANCE = 60.0D;
|
||||
private static final float CRUSH_DROP_CHANCE = 0.1F;
|
||||
private static final int RECOVERY_AFTER_BEAM_TICKS = 30;
|
||||
private static final ResourceLocation MULTIPLAYER_DAMAGE_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "royal_multiplayer_damage");
|
||||
private static final int OBSTRUCTION_CLEAR_INTERVAL_TICKS = 5;
|
||||
private static final double OBSTRUCTION_CLEAR_RADIUS = 3.5D;
|
||||
private static final int OBSTRUCTION_CLEAR_MAX_BLOCKS = 24;
|
||||
@@ -177,6 +185,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
@Nullable
|
||||
private Vec3 aerialCombatAnchor;
|
||||
private int aerialCombatAnchorTicks;
|
||||
private int recoveryWindowTicks;
|
||||
private boolean multiplayerScalingInitialized;
|
||||
private final Set<UUID> encounterParticipants = new HashSet<>();
|
||||
private double royalDamageMultiplier = 1.0D;
|
||||
|
||||
@Nullable
|
||||
private Entity[] multipartParts;
|
||||
@@ -249,10 +261,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createBaseAttributes(double health, double attackDamage) {
|
||||
return createBaseAttributes(health, attackDamage, AntarchySettings.royalBossArmor());
|
||||
}
|
||||
|
||||
protected static AttributeSupplier.Builder createBaseAttributes(double health, double attackDamage, double armor) {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, health)
|
||||
.add(Attributes.ATTACK_DAMAGE, attackDamage)
|
||||
.add(Attributes.ARMOR, AntarchySettings.royalBossArmor())
|
||||
.add(Attributes.ARMOR, armor)
|
||||
.add(Attributes.FOLLOW_RANGE, AntarchySettings.royalBossFollowRange())
|
||||
.add(Attributes.MOVEMENT_SPEED, AntarchySettings.royalBossMovementSpeed())
|
||||
.add(Attributes.FLYING_SPEED, AntarchySettings.royalBossMovementSpeed())
|
||||
@@ -447,6 +463,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
|
||||
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
|
||||
this.attackScheduler.tick();
|
||||
if (this.recoveryWindowTicks > 0) {
|
||||
this.recoveryWindowTicks--;
|
||||
}
|
||||
|
||||
if (this.isRoyalFlying() && this.tickCount % 40 == 0 && this.royalFlyLoopSound() != null) {
|
||||
this.playRoyalSound(this.royalFlyLoopSound(), 0.92F + this.random.nextFloat() * 0.12F);
|
||||
@@ -464,6 +483,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializeMultiplayerScaling(primaryTarget);
|
||||
|
||||
this.tickCombatLocomotionMode(primaryTarget);
|
||||
boolean aeriallyStabilized = this.steerTowardTarget(primaryTarget);
|
||||
if (!aeriallyStabilized) {
|
||||
@@ -475,6 +496,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
|
||||
Phase phase = this.phase();
|
||||
int activeHeadAttacks = 0;
|
||||
if (this.recoveryWindowTicks > 0) {
|
||||
return;
|
||||
}
|
||||
for (RoyalHead head : this.heads) {
|
||||
if (this.attackScheduler.laneBusy(this.headLane(head))) {
|
||||
activeHeadAttacks++;
|
||||
@@ -503,6 +527,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
}
|
||||
|
||||
private void tickRoyalBeam(LivingEntity primaryTarget) {
|
||||
if (this.recoveryWindowTicks > 0) {
|
||||
return;
|
||||
}
|
||||
Phase phase = this.phase();
|
||||
int activeBeams = 0;
|
||||
for (RoyalHead head : this.heads) {
|
||||
@@ -588,6 +615,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
@Override
|
||||
public void onComplete() {
|
||||
RoyalBossEntity.this.stopRoyalBeam(head);
|
||||
RoyalBossEntity.this.startRoyalRecovery(RECOVERY_AFTER_BEAM_TICKS);
|
||||
}
|
||||
})) {
|
||||
continue;
|
||||
@@ -601,6 +629,47 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
return phase.maxConcurrentHeadAttacks();
|
||||
}
|
||||
|
||||
protected boolean isRoyalRecoveryActive() {
|
||||
return this.recoveryWindowTicks > 0;
|
||||
}
|
||||
|
||||
protected void startRoyalRecovery(int ticks) {
|
||||
this.recoveryWindowTicks = Math.max(this.recoveryWindowTicks, ticks);
|
||||
}
|
||||
|
||||
private void initializeMultiplayerScaling(LivingEntity target) {
|
||||
if (this.multiplayerScalingInitialized || !(this.level() instanceof ServerLevel level)) {
|
||||
return;
|
||||
}
|
||||
this.multiplayerScalingInitialized = true;
|
||||
if (!AntarchySettings.royalBossMultiplayerScalingEnabled()) {
|
||||
return;
|
||||
}
|
||||
int maxPlayers = Math.max(1, AntarchySettings.royalBossScalingMaxPlayers());
|
||||
List<ServerPlayer> players = level.getPlayers(player -> player.isAlive()
|
||||
&& !player.isSpectator()
|
||||
&& player.distanceToSqr(this) <= AntarchySettings.royalBossFollowRange() * AntarchySettings.royalBossFollowRange());
|
||||
if (target instanceof ServerPlayer player && player.isAlive() && !players.contains(player)) {
|
||||
players.add(player);
|
||||
}
|
||||
int participantCount = Math.min(maxPlayers, Math.max(1, players.size()));
|
||||
for (ServerPlayer player : players) {
|
||||
if (this.encounterParticipants.size() >= participantCount) {
|
||||
break;
|
||||
}
|
||||
this.encounterParticipants.add(player.getUUID());
|
||||
}
|
||||
double healthMultiplier = 1.0D + Math.max(0, participantCount - 1)
|
||||
* Math.max(0.0D, AntarchySettings.royalBossHealthPerAdditionalPlayer());
|
||||
double baseHealth = this.getAttributeBaseValue(Attributes.MAX_HEALTH);
|
||||
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(baseHealth * healthMultiplier);
|
||||
this.setHealth(this.getMaxHealth());
|
||||
double damageMultiplier = Math.max(0.0D, participantCount - 1)
|
||||
* Math.max(0.0D, AntarchySettings.royalBossDamagePerAdditionalPlayer());
|
||||
this.royalDamageMultiplier = 1.0D + damageMultiplier;
|
||||
this.applyRoyalDamageModifier();
|
||||
}
|
||||
|
||||
private void stopRoyalBeam(RoyalHead head) {
|
||||
int index = head.slot().ordinal();
|
||||
head.setBeamActive(false);
|
||||
@@ -1211,12 +1280,38 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putBoolean("RoyalFlying", this.isRoyalFlying());
|
||||
tag.putBoolean("RoyalScalingInitialized", this.multiplayerScalingInitialized);
|
||||
tag.putInt("RoyalScalingParticipants", this.encounterParticipants.size());
|
||||
tag.putDouble("RoyalDamageMultiplier", this.royalDamageMultiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.setRoyalFlying(!tag.contains("RoyalFlying") || tag.getBoolean("RoyalFlying"));
|
||||
this.multiplayerScalingInitialized = tag.getBoolean("RoyalScalingInitialized");
|
||||
this.royalDamageMultiplier = Math.max(1.0D, tag.getDouble("RoyalDamageMultiplier"));
|
||||
if (tag.contains("RoyalScalingParticipants")) {
|
||||
int count = Math.max(1, tag.getInt("RoyalScalingParticipants"));
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.encounterParticipants.add(new UUID(0L, i + 1L));
|
||||
}
|
||||
}
|
||||
this.applyRoyalDamageModifier();
|
||||
}
|
||||
|
||||
public float scaleRoyalDamage(double damage) {
|
||||
return (float) (damage * this.royalDamageMultiplier);
|
||||
}
|
||||
|
||||
private void applyRoyalDamageModifier() {
|
||||
AttributeInstance attackDamage = this.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||
if (attackDamage == null || this.royalDamageMultiplier <= 1.0D
|
||||
|| attackDamage.getModifier(MULTIPLAYER_DAMAGE_ID) != null) {
|
||||
return;
|
||||
}
|
||||
attackDamage.addPermanentModifier(new AttributeModifier(MULTIPLAYER_DAMAGE_ID,
|
||||
this.royalDamageMultiplier - 1.0D, AttributeModifier.Operation.ADD_MULTIPLIED_BASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-2
@@ -136,7 +136,9 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
|
||||
this::canDamageWithImpact)) {
|
||||
double distance = Math.max(0.5D, living.position().distanceTo(center));
|
||||
float falloff = (float) Math.max(0.25D, 1.0D - distance / 5.0D);
|
||||
living.hurt(source, (float) (AntarchySettings.kingFireballDamage() * falloff));
|
||||
double damage = AntarchySettings.kingFireballDamage() * falloff;
|
||||
living.hurt(source, this.getOwner() instanceof RoyalBossEntity royalBoss
|
||||
? royalBoss.scaleRoyalDamage(damage) : (float) damage);
|
||||
living.setRemainingFireTicks(Math.max(living.getRemainingFireTicks(), 120));
|
||||
}
|
||||
igniteImpact(level, center, this.isDreamFireball());
|
||||
@@ -170,7 +172,9 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen
|
||||
DamageSource source = this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity living ? living : null);
|
||||
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(5.0D),
|
||||
this::canDamageWithImpact)) {
|
||||
living.hurt(source, (float) AntarchySettings.kingIceballDamage());
|
||||
double damage = AntarchySettings.kingIceballDamage();
|
||||
living.hurt(source, this.getOwner() instanceof RoyalBossEntity royalBoss
|
||||
? royalBoss.scaleRoyalDamage(damage) : (float) damage);
|
||||
living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 80, living.getTicksFrozen() + 180));
|
||||
living.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.MOVEMENT_SLOWDOWN, 120, 3));
|
||||
}
|
||||
|
||||
+20
-1
@@ -12,6 +12,8 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
@@ -26,6 +28,8 @@ public class RoyalIceSpikeEntity extends Entity implements GeoEntity {
|
||||
private static final String ANIMATION_CONTROLLER = "ice_spike";
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
private boolean impacted;
|
||||
@Nullable
|
||||
private UUID ownerId;
|
||||
|
||||
public RoyalIceSpikeEntity(EntityType<? extends RoyalIceSpikeEntity> type, Level level) {
|
||||
super(type, level);
|
||||
@@ -39,6 +43,12 @@ public class RoyalIceSpikeEntity extends Entity implements GeoEntity {
|
||||
return spike;
|
||||
}
|
||||
|
||||
public static RoyalIceSpikeEntity create(ServerLevel level, Vec3 position, LivingEntity owner) {
|
||||
RoyalIceSpikeEntity spike = create(level, position);
|
||||
spike.ownerId = owner.getUUID();
|
||||
return spike;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(net.minecraft.network.syncher.SynchedEntityData.Builder builder) {
|
||||
}
|
||||
@@ -65,7 +75,10 @@ public class RoyalIceSpikeEntity extends Entity implements GeoEntity {
|
||||
DamageSource source = this.damageSources().magic();
|
||||
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
|
||||
new AABB(this.blockPosition()).inflate(2.2D), entity -> entity.isAlive())) {
|
||||
living.hurt(source, (float) AntarchySettings.kingIceSpikeDamage());
|
||||
Entity owner = this.ownerId == null ? null : level.getEntity(this.ownerId);
|
||||
living.hurt(source, owner instanceof RoyalBossEntity royalBoss
|
||||
? royalBoss.scaleRoyalDamage(AntarchySettings.kingIceSpikeDamage())
|
||||
: (float) AntarchySettings.kingIceSpikeDamage());
|
||||
living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 60, living.getTicksFrozen() + 100));
|
||||
living.setDeltaMovement(living.getDeltaMovement().add(0.0D, 0.35D, 0.0D));
|
||||
living.hasImpulse = true;
|
||||
@@ -76,11 +89,17 @@ public class RoyalIceSpikeEntity extends Entity implements GeoEntity {
|
||||
@Override
|
||||
protected void readAdditionalSaveData(CompoundTag tag) {
|
||||
this.impacted = tag.getBoolean("Impacted");
|
||||
if (tag.hasUUID("Owner")) {
|
||||
this.ownerId = tag.getUUID("Owner");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalSaveData(CompoundTag tag) {
|
||||
tag.putBoolean("Impacted", this.impacted);
|
||||
if (this.ownerId != null) {
|
||||
tag.putUUID("Owner", this.ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-2
@@ -154,13 +154,13 @@ public final class RoyalBeamController {
|
||||
boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY;
|
||||
for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) {
|
||||
Vec3 sample = shootFrom.add(direction.scale(walked));
|
||||
hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
|
||||
hurtEntitiesAround(sample, settings.pathDamageRadius(), this.scaledDamage(settings.damage()), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
|
||||
if (pathMutates && shouldMutateTerrain(settings, walked)) {
|
||||
mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode);
|
||||
}
|
||||
}
|
||||
|
||||
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
|
||||
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), this.scaledDamage(settings.damage()), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), this.damagedThisTick);
|
||||
|
||||
if (!shouldMutateTerrain(settings, distance)) {
|
||||
return;
|
||||
@@ -174,6 +174,10 @@ public final class RoyalBeamController {
|
||||
}
|
||||
}
|
||||
|
||||
private float scaledDamage(float damage) {
|
||||
return this.owner instanceof RoyalBossEntity royalBoss ? royalBoss.scaleRoyalDamage(damage) : damage;
|
||||
}
|
||||
|
||||
private boolean shouldMutateTerrain(RoyalBeamSettings settings, double distanceFromOwner) {
|
||||
return settings.terrainEnabled()
|
||||
&& settings.maxTerrainMutationsPerTick() > 0
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.entity.royal.decree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
public final class AdvanceDecree implements RoyalDecree {
|
||||
private static final double REQUIRED_RANGE = 14.0D;
|
||||
public String translationKey() { return "decree.antarchy.advance"; }
|
||||
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { king.setDecreeRetreatPressure(true); }
|
||||
@Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
|
||||
return target.distanceToSqr(king) > REQUIRED_RANGE * REQUIRED_RANGE ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
|
||||
}
|
||||
@Override
|
||||
public int contextWeight(KingEntity king, LivingEntity target) {
|
||||
return 4 + king.behaviorScore(KingEntity.Behavior.KEEPING_FAR);
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.entity.royal.decree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
public final class KeepYourDistanceDecree implements RoyalDecree {
|
||||
private static final double INNER_RADIUS = 16.0D;
|
||||
public String translationKey() { return "decree.antarchy.keep_your_distance"; }
|
||||
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
|
||||
@Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) {
|
||||
return target.distanceToSqr(king) <= INNER_RADIUS * INNER_RADIUS ? Evaluation.VIOLATED : Evaluation.COMPLIANT;
|
||||
}
|
||||
@Override
|
||||
public int contextWeight(KingEntity king, LivingEntity target) {
|
||||
return 4 + king.behaviorScore(KingEntity.Behavior.HUGGING);
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.entity.royal.decree;
|
||||
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
public final class NoRetreatDecree implements RoyalDecree {
|
||||
private static final double ALLOWED_RANGE = 26.0D;
|
||||
public String translationKey() { return "decree.antarchy.no_retreat"; }
|
||||
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
|
||||
if (target.distanceToSqr(king) > ALLOWED_RANGE * ALLOWED_RANGE) {
|
||||
king.invokeJudgment(target);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int contextWeight(KingEntity king, LivingEntity target) {
|
||||
return 4 + king.behaviorScore(KingEntity.Behavior.RETREATING);
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package com.craisinlord.antarchy.content.entity.trades;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.trading.ItemCost;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Loads Computer Scientist offers from data/computer_scientist_trades/*.json. */
|
||||
public final class ComputerScientistTradeManager extends SimpleJsonResourceReloadListener {
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final String DIRECTORY = "computer_scientist_trades";
|
||||
private static final List<ResourceLocation> GENERAL_DISKS = List.of(
|
||||
id("introduction"), id("chronosphere"), id("high_fructose_corn_syrup"),
|
||||
id("elythia"), id("flying_squirrel"), id("cavaryn"), id("thoraxis"),
|
||||
id("temporal_fields"), id("bloodglass"), id("vortex"));
|
||||
private static volatile List<TradeEntry> loadedTrades = List.of();
|
||||
|
||||
public ComputerScientistTradeManager() {
|
||||
super(GSON, DIRECTORY);
|
||||
}
|
||||
|
||||
public static int[] slotsPerLevel() {
|
||||
return new int[] {4, 2, 2, 3, 4};
|
||||
}
|
||||
|
||||
public static VillagerTrades.ItemListing listing(int level, int index) {
|
||||
return (trader, random) -> {
|
||||
TradeEntry trade = loadedTrades.stream()
|
||||
.filter(entry -> entry.level == level && entry.index == index)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return trade == null ? null : trade.createOffer(random);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<ResourceLocation, JsonElement> object, ResourceManager resourceManager, ProfilerFiller profiler) {
|
||||
List<TradeEntry> parsed = new ArrayList<>();
|
||||
for (JsonElement file : object.values()) {
|
||||
if (!file.isJsonObject()) continue;
|
||||
JsonObject root = file.getAsJsonObject();
|
||||
if (root.has("trades") && root.get("trades").isJsonArray()) {
|
||||
for (JsonElement element : root.getAsJsonArray("trades")) {
|
||||
if (element.isJsonObject()) parsed.add(parse(element.getAsJsonObject()));
|
||||
}
|
||||
} else {
|
||||
parsed.add(parse(root));
|
||||
}
|
||||
}
|
||||
loadedTrades = List.copyOf(parsed);
|
||||
}
|
||||
|
||||
private static TradeEntry parse(JsonObject json) {
|
||||
return new TradeEntry(
|
||||
integer(json, "level", 1), integer(json, "index", 0),
|
||||
stack(json.getAsJsonObject("buy")),
|
||||
json.has("second_buy") ? stack(json.getAsJsonObject("second_buy")) : null,
|
||||
stack(json.getAsJsonObject("sell")),
|
||||
json.has("sell_disk") ? json.get("sell_disk").getAsString() : null,
|
||||
integer(json, "max_uses", 12), integer(json, "villager_xp", 5),
|
||||
json.has("price_multiplier") ? json.get("price_multiplier").getAsFloat() : 0.05F,
|
||||
json.has("chance") ? json.get("chance").getAsFloat() : 1.0F);
|
||||
}
|
||||
|
||||
private record TradeEntry(int level, int index, StackRange buy, StackRange secondBuy,
|
||||
StackRange sell, String sellDisk, int maxUses, int villagerXp,
|
||||
float priceMultiplier, float chance) {
|
||||
private MerchantOffer createOffer(net.minecraft.util.RandomSource random) {
|
||||
if (random.nextFloat() > chance) return null;
|
||||
ItemCost buyCost = buy.toCost(random);
|
||||
java.util.Optional<ItemCost> secondCost = secondBuy == null
|
||||
? java.util.Optional.empty() : java.util.Optional.of(secondBuy.toCost(random));
|
||||
ItemStack result = sellDisk == null ? sell.toStack(random) : diskStack(sellDisk, random, sell.toStack(random).getCount());
|
||||
return new MerchantOffer(buyCost, secondCost, result, maxUses, villagerXp, priceMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
private record StackRange(ResourceLocation item, int min, int max) {
|
||||
private Item itemValue() {
|
||||
return BuiltInRegistries.ITEM.get(item);
|
||||
}
|
||||
|
||||
private int count(net.minecraft.util.RandomSource random) {
|
||||
return min == max ? min : min + random.nextInt(max - min + 1);
|
||||
}
|
||||
|
||||
private ItemCost toCost(net.minecraft.util.RandomSource random) {
|
||||
return new ItemCost(itemValue(), count(random));
|
||||
}
|
||||
|
||||
private ItemStack toStack(net.minecraft.util.RandomSource random) {
|
||||
return new ItemStack(itemValue(), count(random));
|
||||
}
|
||||
}
|
||||
|
||||
private static StackRange stack(JsonObject json) {
|
||||
int min = integer(json, "min", integer(json, "count", 1));
|
||||
int max = integer(json, "max", min);
|
||||
return new StackRange(ResourceLocation.parse(json.get("item").getAsString()), min, max);
|
||||
}
|
||||
|
||||
private static ItemStack diskStack(String diskType, net.minecraft.util.RandomSource random, int count) {
|
||||
ResourceLocation diskId = diskType.equals("random_general")
|
||||
? GENERAL_DISKS.get(random.nextInt(GENERAL_DISKS.size()))
|
||||
: ResourceLocation.parse(diskType);
|
||||
ItemStack disk = new ItemStack(AntarchyObjects.FLOPPY_DISK.get(), count);
|
||||
disk.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), diskId);
|
||||
return disk;
|
||||
}
|
||||
|
||||
private static int integer(JsonObject json, String key, int fallback) {
|
||||
return json.has(key) ? json.get(key).getAsInt() : fallback;
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath("antarchy", path);
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.content.entity.vortex;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
@@ -200,7 +202,10 @@ public class VortexEntity extends Monster implements GeoEntity {
|
||||
|
||||
private void tickCombatMovement(LivingEntity target) {
|
||||
this.faceTowards(target.position().add(0.0D, target.getBbHeight() * 0.5D, 0.0D), 12.0F, 8.0F);
|
||||
this.getMoveControl().setWantedPosition(target.getX(), target.getY() + 2.5D, target.getZ(), 0.38D);
|
||||
Vec3 gravityUp = Vec3.atLowerCornerOf(
|
||||
AntarchyGravityRotationUtil.getGravityDownDirection(this).getOpposite().getNormal());
|
||||
Vec3 hoverPosition = target.position().add(gravityUp.scale(2.5D));
|
||||
this.getMoveControl().setWantedPosition(hoverPosition.x, hoverPosition.y, hoverPosition.z, 0.38D);
|
||||
|
||||
if (this.attackCooldown <= 0
|
||||
&& this.distanceToSqr(target) <= ATTACK_RANGE * ATTACK_RANGE
|
||||
@@ -251,6 +256,7 @@ public class VortexEntity extends Monster implements GeoEntity {
|
||||
double distance = direction.length();
|
||||
Vec3 drift = distance > 1.0E-6D ? direction.scale(0.24D / distance) : this.getViewVector(1.0F).scale(0.24D);
|
||||
WindVortexEntity vortex = WindVortexEntity.create(serverLevel, windVortexTypeSupplier.get(), origin, Vec3.ZERO, this, true);
|
||||
vortex.setAxis(AntarchyGravityRotationUtil.getGravityDownDirection(this).getOpposite());
|
||||
vortex.setTravel(drift);
|
||||
vortex.setTravelDuration(Mth.clamp((int) Math.ceil(distance / drift.length()), 8, 36));
|
||||
vortex.setHoming(false);
|
||||
|
||||
+29
-30
@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.content.entity.vortex;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -208,12 +210,10 @@ public class WindVortexEntity extends Entity {
|
||||
}
|
||||
} else if (!lensVortex) {
|
||||
Vec3 current = this.getDeltaMovement();
|
||||
Vec3 axis = this.getAxis();
|
||||
double downwardSpeed = Math.min(MAX_FALL_SPEED, Math.max(MINIMUM_DOWNWARD_DRIFT,
|
||||
-current.y + FALL_ACCELERATION));
|
||||
Vec3 drift = new Vec3(
|
||||
current.x * DRIFT_FRICTION,
|
||||
-downwardSpeed,
|
||||
current.z * DRIFT_FRICTION);
|
||||
-current.dot(axis) + FALL_ACCELERATION));
|
||||
Vec3 drift = current.scale(DRIFT_FRICTION).add(axis.scale(-downwardSpeed));
|
||||
Vec3 from = this.position();
|
||||
Vec3 to = from.add(drift);
|
||||
HitResult blockHit = this.level().clip(
|
||||
@@ -323,20 +323,17 @@ public class WindVortexEntity extends Entity {
|
||||
: this.getDeltaMovement();
|
||||
double speed = Mth.clamp(incoming.length(), 0.2D, 0.9D);
|
||||
Vec3 flow = incoming;
|
||||
if (normal.y > 0.5D) {
|
||||
flow = new Vec3(incoming.x, 0.0D, incoming.z);
|
||||
} else if (normal.y < -0.5D) {
|
||||
flow = new Vec3(incoming.x, 0.0D, incoming.z).add(0.0D, -0.08D, 0.0D);
|
||||
} else {
|
||||
Vec3 tangent = incoming.subtract(normal.scale(incoming.dot(normal)));
|
||||
Vec3 tangent = incoming.subtract(normal.scale(incoming.dot(normal)));
|
||||
if (tangent.lengthSqr() < 1.0E-6D) {
|
||||
tangent = this.getAxis().cross(normal);
|
||||
if (tangent.lengthSqr() < 1.0E-6D) {
|
||||
tangent = new Vec3(-normal.z, 0.0D, normal.x);
|
||||
if (tangent.lengthSqr() < 1.0E-6D) {
|
||||
tangent = new Vec3(0.0D, 0.0D, 1.0D);
|
||||
}
|
||||
tangent = new Vec3(0.0D, 1.0D, 0.0D).cross(normal);
|
||||
}
|
||||
if (tangent.lengthSqr() < 1.0E-6D) {
|
||||
tangent = new Vec3(1.0D, 0.0D, 0.0D);
|
||||
}
|
||||
flow = tangent.normalize().scale(speed);
|
||||
}
|
||||
flow = tangent.normalize().scale(speed);
|
||||
if (flow.lengthSqr() < 1.0E-6D) {
|
||||
this.travelVelocity = Vec3.ZERO;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
@@ -511,24 +508,20 @@ public class WindVortexEntity extends Entity {
|
||||
.add(tangent.scale(spin))
|
||||
.add(basis.axis.scale(axialSpeed))
|
||||
.scale(ENTITY_FORCE_SPEED_SCALE);
|
||||
Vec3 current = entity.getDeltaMovement();
|
||||
Vec3 updatedMovement = new Vec3(
|
||||
Mth.lerp(0.72D, current.x, wanted.x),
|
||||
Mth.lerp(0.58D, current.y, wanted.y),
|
||||
Mth.lerp(0.72D, current.z, wanted.z)
|
||||
);
|
||||
Vec3 current = AntarchyGravityApi.getWorldVelocity(entity);
|
||||
Vec3 updatedMovement = current.lerp(wanted, 0.72D);
|
||||
if (this.travelling) {
|
||||
updatedMovement = updatedMovement.add(
|
||||
this.travelVelocity.x * 0.8D * ENTITY_FORCE_SPEED_SCALE,
|
||||
0.0D,
|
||||
this.travelVelocity.z * 0.8D * ENTITY_FORCE_SPEED_SCALE);
|
||||
updatedMovement = updatedMovement.add(this.travelVelocity.scale(0.8D * ENTITY_FORCE_SPEED_SCALE));
|
||||
}
|
||||
if (mode == VortexMode.UPWARD && entity.onGround()) {
|
||||
double minRise = (entity instanceof Player ? 0.38D : 0.3D) * ENTITY_FORCE_SPEED_SCALE;
|
||||
updatedMovement = new Vec3(updatedMovement.x, Math.max(updatedMovement.y, minRise), updatedMovement.z);
|
||||
Vec3 gravityUp = Vec3.atLowerCornerOf(
|
||||
AntarchyGravityRotationUtil.getGravityDownDirection(entity).getOpposite().getNormal());
|
||||
double minimumAxialSpeed = Math.max(updatedMovement.dot(gravityUp), minRise);
|
||||
updatedMovement = updatedMovement.add(gravityUp.scale(minimumAxialSpeed - updatedMovement.dot(gravityUp)));
|
||||
entity.setOnGround(false);
|
||||
}
|
||||
entity.setDeltaMovement(updatedMovement);
|
||||
AntarchyGravityApi.setWorldVelocity(entity, updatedMovement);
|
||||
if (entity instanceof Player) {
|
||||
entity.setOnGround(false);
|
||||
}
|
||||
@@ -542,7 +535,7 @@ public class WindVortexEntity extends Entity {
|
||||
Vec3 radial = radialVector.lengthSqr() < 1.0E-6D ? basis.sideA : radialVector.scale(1.0D / horizontal);
|
||||
Vec3 tangent = basis.axis.cross(radial).normalize();
|
||||
double scale = this.launchStrength * Mth.clamp(this.getVortexHeight() / 5.0D, 0.5D, 2.5D);
|
||||
entity.setDeltaMovement(tangent.scale(0.72D * scale * ENTITY_FORCE_SPEED_SCALE)
|
||||
this.setWorldVelocity(entity, tangent.scale(0.72D * scale * ENTITY_FORCE_SPEED_SCALE)
|
||||
.add(radial.scale(0.34D * scale * ENTITY_FORCE_SPEED_SCALE))
|
||||
.add(basis.axis.scale((0.78D + progress * 0.28D) * scale * ENTITY_FORCE_SPEED_SCALE)));
|
||||
entity.hasImpulse = true;
|
||||
@@ -578,7 +571,9 @@ public class WindVortexEntity extends Entity {
|
||||
Vec3 direction = distance < 1.0E-4D ? new Vec3(0.0D, 1.0D, 0.0D) : toOwner.scale(1.0D / distance);
|
||||
double arc = Mth.clamp(distance * 0.06D, 0.25D, 1.1D) * ENTITY_FORCE_SPEED_SCALE;
|
||||
double power = this.launchStrength * Mth.clamp(distance / 6.0D, 0.6D, 2.0D) * ENTITY_FORCE_SPEED_SCALE;
|
||||
entity.setDeltaMovement(direction.scale(power).add(0.0D, arc, 0.0D));
|
||||
Vec3 gravityUp = Vec3.atLowerCornerOf(
|
||||
AntarchyGravityRotationUtil.getGravityDownDirection(entity).getOpposite().getNormal());
|
||||
this.setWorldVelocity(entity, direction.scale(power).add(gravityUp.scale(arc)));
|
||||
entity.fallDistance = 0.0F;
|
||||
entity.hasImpulse = true;
|
||||
entity.hurtMarked = true;
|
||||
@@ -596,6 +591,10 @@ public class WindVortexEntity extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
private void setWorldVelocity(Entity entity, Vec3 velocity) {
|
||||
AntarchyGravityApi.setWorldVelocity(entity, velocity);
|
||||
}
|
||||
|
||||
private DamageSource damageSource() {
|
||||
Entity owner = this.getOwnerEntity();
|
||||
if (owner instanceof LivingEntity living) {
|
||||
|
||||
+13
-4
@@ -41,14 +41,14 @@ public final class ComputerGuideData extends SimplePreparableReloadListener<Comp
|
||||
public static List<Entry> entriesFor(List<ResourceLocation> diskIds) {
|
||||
if (AntarchySettings.unlockAllArchives()) {
|
||||
Map<ResourceLocation, Entry> result = new LinkedHashMap<>();
|
||||
result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", ""));
|
||||
result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", "", ""));
|
||||
result.putAll(entries);
|
||||
return result.values().stream().sorted(Comparator.comparing(entry -> entry.titleKey().toString())).toList();
|
||||
}
|
||||
Map<ResourceLocation, Entry> result = new LinkedHashMap<>();
|
||||
for (ResourceLocation diskId : diskIds) {
|
||||
if (INTRODUCTION.equals(diskId)) {
|
||||
result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", ""));
|
||||
result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", "", ""));
|
||||
continue;
|
||||
}
|
||||
Disk disk = disks.get(diskId);
|
||||
@@ -115,9 +115,10 @@ public final class ComputerGuideData extends SimplePreparableReloadListener<Comp
|
||||
List<String> description = strings(object.get("description"));
|
||||
String item = string(object, "item", "");
|
||||
String entity = string(object, "entity", "");
|
||||
String recipe = string(object, "recipe", "");
|
||||
String structure = string(object, "structure", "");
|
||||
String dimension = string(object, "dimension", "");
|
||||
target.put(id, new Entry(id, type, category, title, subtitle, description, item, entity, structure, dimension));
|
||||
target.put(id, new Entry(id, type, category, title, subtitle, description, item, entity, recipe, structure, dimension));
|
||||
}
|
||||
|
||||
private static void parseDisk(ResourceLocation id, JsonElement element, Map<ResourceLocation, Disk> target) {
|
||||
@@ -163,13 +164,21 @@ public final class ComputerGuideData extends SimplePreparableReloadListener<Comp
|
||||
for (JsonElement value : array) {
|
||||
if (value.isJsonPrimitive()) {
|
||||
values.add(value.getAsString());
|
||||
} else if (value.isJsonObject()) {
|
||||
JsonObject render = value.getAsJsonObject();
|
||||
String item = string(render, "item", "");
|
||||
String entity = string(render, "entity", "");
|
||||
String recipe = string(render, "recipe", "");
|
||||
if (!item.isBlank()) values.add("@item:" + item);
|
||||
else if (!entity.isBlank()) values.add("@entity:" + entity);
|
||||
else if (!recipe.isBlank()) values.add("@recipe:" + recipe);
|
||||
}
|
||||
}
|
||||
return List.copyOf(values);
|
||||
}
|
||||
|
||||
public record Entry(ResourceLocation id, String type, String category, String titleKey, String subtitleKey,
|
||||
List<String> descriptionKeys, String itemId, String entityId, String structureId, String dimensionId) {
|
||||
List<String> descriptionKeys, String itemId, String entityId, String recipeId, String structureId, String dimensionId) {
|
||||
}
|
||||
|
||||
public record Disk(ResourceLocation id, String category, String titleKey, List<ResourceLocation> entries, String wallpaper) {
|
||||
|
||||
+1
-45
@@ -1,54 +1,10 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
|
||||
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
|
||||
import com.craisinlord.antarchy.content.block.ComputerBlock;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import software.bernie.geckolib.animatable.GeoItem;
|
||||
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ComputerItem extends BlockItem implements GeoItem {
|
||||
private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath("antarchy", "geo/computer.geo.json");
|
||||
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/item/computer.png");
|
||||
private static final ResourceLocation ANIMATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/computer.animation.json");
|
||||
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
public final class ComputerItem extends BlockItem {
|
||||
public ComputerItem(ComputerBlock block, Properties properties) {
|
||||
super(block, properties);
|
||||
GeoItem.registerSyncedAnimatable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "state", state -> state.setAndContinue(RawAnimation.begin().thenLoop("on_state"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
|
||||
consumer.accept(new GeoRenderProvider() {
|
||||
private AnimatedHeldItemRenderer<ComputerItem> renderer;
|
||||
|
||||
@Override
|
||||
public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
|
||||
if (renderer == null) {
|
||||
renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL, TEXTURE, ANIMATION));
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+7
-3
@@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
|
||||
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
|
||||
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.ChatFormatting;
|
||||
@@ -76,12 +77,15 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
|
||||
false
|
||||
);
|
||||
vortex.setMode(WindVortexEntity.VortexMode.UPWARD);
|
||||
vortex.setAxis(Direction.UP);
|
||||
vortex.setAxis(AntarchyGravityApi.isGravityInverted(player) ? Direction.DOWN : Direction.UP);
|
||||
vortex.setVortexSize((float) AntarchySettings.eyeOfTheStormUpdraftHeight(), (float) AntarchySettings.eyeOfTheStormUpdraftRadius());
|
||||
vortex.setVortexDurationTicks(AntarchySettings.eyeOfTheStormUpdraftDurationTicks());
|
||||
serverLevel.addFreshEntity(vortex);
|
||||
|
||||
player.setDeltaMovement(player.getDeltaMovement().add(0.0D, AntarchySettings.eyeOfTheStormUpdraftLaunchStrength(), 0.0D));
|
||||
Vec3 gravityUp = Vec3.atLowerCornerOf(
|
||||
(AntarchyGravityApi.isGravityInverted(player) ? Direction.DOWN : Direction.UP).getNormal());
|
||||
AntarchyGravityApi.setWorldVelocity(player,
|
||||
AntarchyGravityApi.getWorldVelocity(player).add(gravityUp.scale(AntarchySettings.eyeOfTheStormUpdraftLaunchStrength())));
|
||||
player.hasImpulse = true;
|
||||
player.resetFallDistance();
|
||||
player.getCooldowns().addCooldown(this, AntarchySettings.eyeOfTheStormUpdraftCooldownTicks());
|
||||
@@ -129,7 +133,7 @@ public class EyeOfTheStormItem extends Item implements GeoItem {
|
||||
true
|
||||
);
|
||||
vortex.setMode(WindVortexEntity.VortexMode.UPWARD);
|
||||
vortex.setAxis(Direction.UP);
|
||||
vortex.setAxis(owner != null && AntarchyGravityApi.isGravityInverted(owner) ? Direction.DOWN : Direction.UP);
|
||||
vortex.setVortexSize((float) AntarchySettings.eyeOfTheStormSurgeHeight(), (float) AntarchySettings.eyeOfTheStormSurgeRadius());
|
||||
vortex.setVortexDurationTicks(AntarchySettings.eyeOfTheStormSurgeDurationTicks());
|
||||
vortex.setVortexStrengths(AntarchySettings.eyeOfTheStormSurgePullStrength(), AntarchySettings.eyeOfTheStormSurgeReturnStrength());
|
||||
|
||||
@@ -14,6 +14,19 @@ public final class FloppyDiskItem extends Item {
|
||||
super(properties.stacksTo(1));
|
||||
}
|
||||
|
||||
public static float categoryModelProperty(ItemStack stack) {
|
||||
ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get());
|
||||
if (id == null) return 0.0F;
|
||||
var disk = com.craisinlord.antarchy.content.guide.ComputerGuideData.disk(id);
|
||||
if (disk == null) return 0.0F;
|
||||
return switch (disk.category()) {
|
||||
case "elythia" -> 1.0F;
|
||||
case "cavaryn" -> 2.0F;
|
||||
case "thoraxis" -> 3.0F;
|
||||
default -> 0.0F;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName(ItemStack stack) {
|
||||
ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get());
|
||||
@@ -28,6 +41,7 @@ public final class FloppyDiskItem extends Item {
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltip, TooltipFlag flag) {
|
||||
tooltip.add(Component.translatable("item.antarchy.floppy_disk.tooltip"));
|
||||
ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get());
|
||||
if (id != null) {
|
||||
var disk = com.craisinlord.antarchy.content.guide.ComputerGuideData.disk(id);
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailDeletePayload(BlockPos pos, String messageId, boolean sent) implements CustomPacketPayload {
|
||||
public static final Type<AntmailDeletePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_delete"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailDeletePayload> STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailDeletePayload::pos, ByteBufCodecs.stringUtf8(64), AntmailDeletePayload::messageId, ByteBufCodecs.BOOL, AntmailDeletePayload::sent, AntmailDeletePayload::new);
|
||||
@Override public Type<AntmailDeletePayload> type() { return TYPE; }
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailDraftPayload(BlockPos pos, int action, String data) implements CustomPacketPayload {
|
||||
public static final int SAVE = 0;
|
||||
public static final int DELETE = 1;
|
||||
public static final Type<AntmailDraftPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_draft"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailDraftPayload> STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailDraftPayload::pos, ByteBufCodecs.VAR_INT, AntmailDraftPayload::action, ByteBufCodecs.stringUtf8(65536), AntmailDraftPayload::data, AntmailDraftPayload::new);
|
||||
@Override public Type<AntmailDraftPayload> type() { return TYPE; }
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailMessageRequestPayload(BlockPos pos, String messageId) implements CustomPacketPayload {
|
||||
public static final Type<AntmailMessageRequestPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_message_request"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailMessageRequestPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
BlockPos.STREAM_CODEC, AntmailMessageRequestPayload::pos,
|
||||
ByteBufCodecs.stringUtf8(64), AntmailMessageRequestPayload::messageId,
|
||||
AntmailMessageRequestPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<AntmailMessageRequestPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+30
-1
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailAttachment;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailDraft;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailWire;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
@@ -23,7 +24,19 @@ public final class AntmailNetworking {
|
||||
}
|
||||
|
||||
public static void requestState(BlockPos pos) {
|
||||
sender.accept(new AntmailStateRequestPayload(pos));
|
||||
requestState(pos, AntmailStateRequestPayload.INBOX, 0, 0L);
|
||||
}
|
||||
|
||||
public static void requestState(BlockPos pos, int folder, int page) {
|
||||
requestState(pos, folder, page, 0L);
|
||||
}
|
||||
|
||||
public static void requestState(BlockPos pos, int folder, int page, long knownVersion) {
|
||||
sender.accept(new AntmailStateRequestPayload(pos, folder, page, knownVersion));
|
||||
}
|
||||
|
||||
public static void requestMessage(BlockPos pos, UUID messageId) {
|
||||
sender.accept(new AntmailMessageRequestPayload(pos, messageId.toString()));
|
||||
}
|
||||
|
||||
public static void send(BlockPos pos, String recipient, String subject, String body, List<AntmailAttachment> attachments) {
|
||||
@@ -37,4 +50,20 @@ public final class AntmailNetworking {
|
||||
public static void markUnread(BlockPos pos, UUID messageId) {
|
||||
sender.accept(new AntmailReadPayload(pos, messageId.toString(), false));
|
||||
}
|
||||
|
||||
public static void delete(BlockPos pos, UUID messageId, boolean sent) {
|
||||
sender.accept(new AntmailDeletePayload(pos, messageId.toString(), sent));
|
||||
}
|
||||
|
||||
public static void saveDraft(BlockPos pos, AntmailDraft draft) {
|
||||
sender.accept(new AntmailDraftPayload(pos, AntmailDraftPayload.SAVE, AntmailWire.encodeTag(draft.toTag())));
|
||||
}
|
||||
|
||||
public static void deleteDraft(BlockPos pos, UUID draftId) {
|
||||
sender.accept(new AntmailDraftPayload(pos, AntmailDraftPayload.DELETE, draftId.toString()));
|
||||
}
|
||||
|
||||
public static void retry(BlockPos pos, UUID messageId) {
|
||||
sender.accept(new AntmailRetryPayload(pos, messageId.toString()));
|
||||
}
|
||||
}
|
||||
+4
@@ -12,8 +12,12 @@ public final class AntmailPayloadTypes {
|
||||
return List.of(
|
||||
AntmailSetupPayload.TYPE,
|
||||
AntmailStateRequestPayload.TYPE,
|
||||
AntmailMessageRequestPayload.TYPE,
|
||||
AntmailSendPayload.TYPE,
|
||||
AntmailReadPayload.TYPE,
|
||||
AntmailDeletePayload.TYPE,
|
||||
AntmailDraftPayload.TYPE,
|
||||
AntmailRetryPayload.TYPE,
|
||||
AntmailResultPayload.TYPE
|
||||
);
|
||||
}
|
||||
|
||||
+20
-2
@@ -8,8 +8,26 @@ import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailResultPayload(BlockPos pos, int status, String address, String messageId, String detail, String data) implements CustomPacketPayload {
|
||||
public record AntmailResultPayload(BlockPos pos, int status, String address, String messageId, String detail, String data, long version) implements CustomPacketPayload {
|
||||
public static final Type<AntmailResultPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_result"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailResultPayload> STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailResultPayload::pos, ByteBufCodecs.VAR_INT, AntmailResultPayload::status, ByteBufCodecs.stringUtf8(64), AntmailResultPayload::address, ByteBufCodecs.stringUtf8(64), AntmailResultPayload::messageId, ByteBufCodecs.stringUtf8(128), AntmailResultPayload::detail, ByteBufCodecs.stringUtf8(65536), AntmailResultPayload::data, AntmailResultPayload::new);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailResultPayload> STREAM_CODEC = StreamCodec.of(
|
||||
(buf, payload) -> {
|
||||
BlockPos.STREAM_CODEC.encode(buf, payload.pos());
|
||||
ByteBufCodecs.VAR_INT.encode(buf, payload.status());
|
||||
ByteBufCodecs.stringUtf8(64).encode(buf, payload.address());
|
||||
ByteBufCodecs.stringUtf8(64).encode(buf, payload.messageId());
|
||||
ByteBufCodecs.stringUtf8(128).encode(buf, payload.detail());
|
||||
ByteBufCodecs.stringUtf8(65536).encode(buf, payload.data());
|
||||
ByteBufCodecs.VAR_LONG.encode(buf, payload.version());
|
||||
},
|
||||
buf -> new AntmailResultPayload(
|
||||
BlockPos.STREAM_CODEC.decode(buf),
|
||||
ByteBufCodecs.VAR_INT.decode(buf),
|
||||
ByteBufCodecs.stringUtf8(64).decode(buf),
|
||||
ByteBufCodecs.stringUtf8(64).decode(buf),
|
||||
ByteBufCodecs.stringUtf8(128).decode(buf),
|
||||
ByteBufCodecs.stringUtf8(65536).decode(buf),
|
||||
ByteBufCodecs.VAR_LONG.decode(buf)
|
||||
));
|
||||
@Override public Type<AntmailResultPayload> type() { return TYPE; }
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailRetryPayload(BlockPos pos, String messageId) implements CustomPacketPayload {
|
||||
public static final Type<AntmailRetryPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_retry"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailRetryPayload> STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailRetryPayload::pos, ByteBufCodecs.stringUtf8(64), AntmailRetryPayload::messageId, AntmailRetryPayload::new);
|
||||
@Override public Type<AntmailRetryPayload> type() { return TYPE; }
|
||||
}
|
||||
+122
-6
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.network;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailAddress;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailAttachment;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailDeliveryResult;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailDraft;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailMessage;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailMailbox;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailServerData;
|
||||
@@ -35,7 +36,8 @@ public final class AntmailServerHandler {
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailServerData.Registration registration = data.register(player.server, player.serverLevel(), payload.pos(), payload.username());
|
||||
String mailbox = registration.valid() ? AntmailWire.encodeTag(data.mailboxOrCreate(registration.address()).toTag()) : "";
|
||||
if (registration.valid()) data.setLastUsedAddress(player, registration.address());
|
||||
String mailbox = registration.valid() ? AntmailWire.encodeTag(data.mailboxOrCreate(registration.address()).toTag(false)) : "";
|
||||
String detail = registration.valid() ? "registration_success" : "registration_failed:" + registration.reason();
|
||||
result(player, payload.pos(), registration.valid() ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), registration.valid() ? registration.address().fullAddress() : "", "", detail, mailbox);
|
||||
}
|
||||
@@ -47,13 +49,48 @@ public final class AntmailServerHandler {
|
||||
return;
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
data.drainAddress(player.server, player.serverLevel(), payload.pos());
|
||||
AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos());
|
||||
if (address == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", "", "unconfigured", "");
|
||||
return;
|
||||
}
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(address);
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "", AntmailWire.encodeTag(mailbox.toTag()));
|
||||
if (payload.knownVersion() == data.mailboxVersion(address)) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "unchanged", "");
|
||||
return;
|
||||
}
|
||||
int folder = payload.folder() < AntmailStateRequestPayload.INBOX || payload.folder() > AntmailStateRequestPayload.DRAFTS
|
||||
? AntmailStateRequestPayload.INBOX : payload.folder();
|
||||
int page = Math.max(0, Math.min(payload.page(), AntmailValidation.MAX_MAILBOX_MESSAGES / AntmailMailbox.PAGE_SIZE));
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "", AntmailWire.encodeTag(mailbox.toPageTag(folder, page)));
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, AntmailMessageRequestPayload payload) {
|
||||
ComputerBlockEntity computer = authorizedComputer(player, payload.pos());
|
||||
if (computer == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", "");
|
||||
return;
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos());
|
||||
if (address == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", "");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
UUID id = UUID.fromString(payload.messageId());
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(address);
|
||||
AntmailMessage message = mailbox.findInbox(id);
|
||||
if (message == null) message = mailbox.findSent(id);
|
||||
if (message == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "not_found", "");
|
||||
return;
|
||||
}
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), payload.messageId(), "message_detail", AntmailWire.encodeTag(message.toTag()));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", "");
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, AntmailSendPayload payload) {
|
||||
@@ -73,7 +110,7 @@ public final class AntmailServerHandler {
|
||||
List<AntmailAttachment> attachments = AntmailWire.decodeAttachments(payload.attachments());
|
||||
AntmailMessage message = AntmailMessage.create(sender, recipient, payload.subject(), payload.body(), player.serverLevel().getGameTime(), attachments);
|
||||
AntmailDeliveryResult delivery = data.deliver(player.server, message);
|
||||
result(player, payload.pos(), delivery.status().ordinal(), sender.fullAddress(), message.id().toString(), delivery.detail(), AntmailWire.encodeTag(data.mailboxOrCreate(sender).toTag()));
|
||||
result(player, payload.pos(), delivery.status().ordinal(), sender.fullAddress(), message.id().toString(), delivery.detail(), AntmailWire.encodeTag(data.mailboxOrCreate(sender).toTag(false)));
|
||||
} catch (RuntimeException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), "", "", "invalid_message", "");
|
||||
}
|
||||
@@ -95,13 +132,87 @@ public final class AntmailServerHandler {
|
||||
UUID id = UUID.fromString(payload.messageId());
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(address);
|
||||
boolean changed = payload.read() ? mailbox.markRead(id) : markUnread(mailbox, id);
|
||||
if (changed) data.setDirty();
|
||||
result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag()));
|
||||
if (changed) data.touchMailbox(address);
|
||||
result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag(false)));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", "");
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, AntmailDeletePayload payload) {
|
||||
ComputerBlockEntity computer = authorizedComputer(player, payload.pos());
|
||||
if (computer == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", "");
|
||||
return;
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos());
|
||||
if (address == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", "");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
UUID id = UUID.fromString(payload.messageId());
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(address);
|
||||
boolean changed = payload.sent() ? mailbox.removeSent(id) : mailbox.removeInbox(id);
|
||||
if (changed) data.touchMailbox(address);
|
||||
result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag(false)));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", "");
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, AntmailDraftPayload payload) {
|
||||
ComputerBlockEntity computer = authorizedComputer(player, payload.pos());
|
||||
if (computer == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unauthorized", "");
|
||||
return;
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos());
|
||||
if (address == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", "", "unconfigured", "");
|
||||
return;
|
||||
}
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(address);
|
||||
try {
|
||||
if (payload.action() == AntmailDraftPayload.SAVE) {
|
||||
AntmailDraft draft = AntmailDraft.fromTag(AntmailWire.decodeTag(payload.data()));
|
||||
AntmailDraft stored = new AntmailDraft(draft.id(), address, draft.recipient(), draft.subject(), draft.body(), draft.attachments());
|
||||
if (!mailbox.saveDraft(stored)) throw new IllegalArgumentException("draft_limit");
|
||||
} else if (payload.action() == AntmailDraftPayload.DELETE) {
|
||||
if (!mailbox.removeDraft(UUID.fromString(payload.data()))) throw new IllegalArgumentException("not_found");
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid_action");
|
||||
}
|
||||
data.touchMailbox(address);
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "draft_saved", AntmailWire.encodeTag(mailbox.toTag(false)));
|
||||
} catch (RuntimeException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), address.fullAddress(), "", "draft_failed", AntmailWire.encodeTag(mailbox.toTag(false)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, AntmailRetryPayload payload) {
|
||||
ComputerBlockEntity computer = authorizedComputer(player, payload.pos());
|
||||
if (computer == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", "");
|
||||
return;
|
||||
}
|
||||
AntmailServerData data = AntmailServerData.access(player.server);
|
||||
AntmailAddress sender = data.addressAt(player.serverLevel().dimension().location(), payload.pos());
|
||||
if (sender == null) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", "");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
AntmailDeliveryResult delivery = data.retry(player.server, sender, UUID.fromString(payload.messageId()));
|
||||
AntmailMailbox mailbox = data.mailboxOrCreate(sender);
|
||||
result(player, payload.pos(), delivery == null ? AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal() : delivery.status().ordinal(), sender.fullAddress(), payload.messageId(), delivery == null ? "not_found" : delivery.detail(), AntmailWire.encodeTag(mailbox.toTag(false)));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), sender.fullAddress(), payload.messageId(), "invalid_id", "");
|
||||
}
|
||||
}
|
||||
|
||||
private static ComputerBlockEntity authorizedComputer(ServerPlayer player, BlockPos pos) {
|
||||
ComputerBlockEntity computer = computer(player, pos);
|
||||
return computer != null && computer.canUseFileSystem(player) ? computer : null;
|
||||
@@ -124,6 +235,11 @@ public final class AntmailServerHandler {
|
||||
}
|
||||
|
||||
private static void result(ServerPlayer player, BlockPos pos, int status, String address, String messageId, String detail, String data) {
|
||||
resultSender.accept(player, new AntmailResultPayload(pos, status, address, messageId, detail, data));
|
||||
long version = 0L;
|
||||
try {
|
||||
if (!address.isBlank()) version = AntmailServerData.access(player.server).mailboxVersion(AntmailAddress.parse(address));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
resultSender.accept(player, new AntmailResultPayload(pos, status, address, messageId, detail, data, version));
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -3,12 +3,21 @@ package com.craisinlord.antarchy.content.network;
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record AntmailStateRequestPayload(BlockPos pos) implements CustomPacketPayload {
|
||||
public record AntmailStateRequestPayload(BlockPos pos, int folder, int page, long knownVersion) implements CustomPacketPayload {
|
||||
public static final int INBOX = 0;
|
||||
public static final int SENT = 1;
|
||||
public static final int DRAFTS = 2;
|
||||
public static final Type<AntmailStateRequestPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_state_request"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailStateRequestPayload> STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailStateRequestPayload::pos, AntmailStateRequestPayload::new);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, AntmailStateRequestPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
BlockPos.STREAM_CODEC, AntmailStateRequestPayload::pos,
|
||||
ByteBufCodecs.VAR_INT, AntmailStateRequestPayload::folder,
|
||||
ByteBufCodecs.VAR_INT, AntmailStateRequestPayload::page,
|
||||
ByteBufCodecs.VAR_LONG, AntmailStateRequestPayload::knownVersion,
|
||||
AntmailStateRequestPayload::new);
|
||||
@Override public Type<AntmailStateRequestPayload> type() { return TYPE; }
|
||||
}
|
||||
+100
-2
@@ -6,7 +6,12 @@ import com.craisinlord.antarchy.content.computer.terminal.TerminalCommandService
|
||||
import com.craisinlord.antarchy.content.computer.terminal.TerminalFileSystem;
|
||||
import com.craisinlord.antarchy.content.computer.terminal.TerminalResult;
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailWire;
|
||||
import com.craisinlord.antarchy.content.computer.blockle.BlockleAnswers;
|
||||
import com.craisinlord.antarchy.content.computer.blockle.BlockleDictionary;
|
||||
import com.craisinlord.antarchy.content.computer.blockle.BlockleGame;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
@@ -59,6 +64,10 @@ public final class ComputerAccessHandler {
|
||||
case ComputerAccessPayload.TERMINAL_COMMAND -> terminalCommand(player, computer, payload);
|
||||
case ComputerAccessPayload.DESKTOP_STATE -> desktopState(player, computer, payload);
|
||||
case ComputerAccessPayload.DESKTOP_WALLPAPER -> desktopWallpaper(player, computer, payload);
|
||||
case ComputerAccessPayload.BASILISK_STATE -> basiliskState(player, computer, payload);
|
||||
case ComputerAccessPayload.ANTMAN_STATE -> antmanState(player, computer, payload);
|
||||
case ComputerAccessPayload.BLOCKLE_STATE -> blockleState(player, computer, payload, false);
|
||||
case ComputerAccessPayload.BLOCKLE_GUESS -> blockleState(player, computer, payload, true);
|
||||
default -> send(player, payload, ComputerAccessResultPayload.INVALID);
|
||||
}
|
||||
}
|
||||
@@ -91,6 +100,10 @@ public final class ComputerAccessHandler {
|
||||
if (computer.hasActiveUser() && !computer.isAuthenticatedBy(player)) {
|
||||
send(player, payload, ComputerAccessResultPayload.BUSY);
|
||||
} else if (computer.authenticate(player, payload.value())) {
|
||||
com.craisinlord.antarchy.content.antmail.AntmailServerData data = com.craisinlord.antarchy.content.antmail.AntmailServerData.access(player.server);
|
||||
net.minecraft.resources.ResourceLocation dimension = player.serverLevel().dimension().location();
|
||||
com.craisinlord.antarchy.content.antmail.AntmailAddress address = data.addressAt(dimension, computer.getBlockPos());
|
||||
if (address != null) data.setLastUsedAddress(player, address);
|
||||
send(player, payload, ComputerAccessResultPayload.SUCCESS);
|
||||
} else {
|
||||
send(player, payload, ComputerAccessResultPayload.INVALID_PASSWORD);
|
||||
@@ -128,8 +141,8 @@ public final class ComputerAccessHandler {
|
||||
ComputerFileSystem.ComputerFile file = computer.fileSystem().get(payload.value());
|
||||
if (file == null) {
|
||||
sendFile(player, payload, false, "", "not_found");
|
||||
} else if (file.type() != ComputerFileSystem.ComputerFile.Type.TEXT) {
|
||||
sendFile(player, payload, false, "", "not_text");
|
||||
} else if (file.type() != ComputerFileSystem.ComputerFile.Type.TEXT && file.type() != ComputerFileSystem.ComputerFile.Type.IMAGE) {
|
||||
sendFile(player, payload, false, "", "not_file");
|
||||
} else {
|
||||
sendFile(player, payload, true, file.path() + "\0" + file.contents(), "");
|
||||
}
|
||||
@@ -269,6 +282,91 @@ public final class ComputerAccessHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static void basiliskState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) {
|
||||
if (!computer.canUseFileSystem(player)) {
|
||||
sendFile(player, payload, false, "", "unauthorized");
|
||||
return;
|
||||
}
|
||||
if (!payload.value().isBlank()) {
|
||||
try {
|
||||
computer.setBasiliskScore(Integer.parseInt(payload.value()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
sendFile(player, payload, false, "", "invalid_score");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendFile(player, payload, true, computer.basiliskScore() + "\0" + computer.basiliskHighScore(), "");
|
||||
}
|
||||
|
||||
private static void antmanState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) {
|
||||
if (!computer.canUseFileSystem(player)) {
|
||||
sendFile(player, payload, false, "", "unauthorized");
|
||||
return;
|
||||
}
|
||||
if (!payload.value().isBlank()) {
|
||||
try {
|
||||
computer.setAntmanScore(Integer.parseInt(payload.value()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
sendFile(player, payload, false, "", "invalid_score");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendFile(player, payload, true, computer.antmanScore() + "\0" + computer.antmanHighScore(), "");
|
||||
}
|
||||
|
||||
private static void blockleState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload, boolean submit) {
|
||||
if (!computer.canUseFileSystem(player)) {
|
||||
sendFile(player, payload, false, "", "unauthorized");
|
||||
return;
|
||||
}
|
||||
ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", "blockle_game");
|
||||
if (!computer.diskIds().contains(disk)) {
|
||||
sendFile(player, payload, false, "", "not_installed");
|
||||
return;
|
||||
}
|
||||
ServerLevel overworld = player.server.getLevel(Level.OVERWORLD);
|
||||
if (overworld == null) {
|
||||
sendFile(player, payload, false, "", "overworld_unavailable");
|
||||
return;
|
||||
}
|
||||
long day = overworld.getDayTime() / 24000L;
|
||||
computer.resetBlockle(day);
|
||||
BlockleAnswers.Answer answer = BlockleAnswers.answerForDay(day);
|
||||
if (submit) {
|
||||
String guess = payload.value() == null ? "" : payload.value().toLowerCase(java.util.Locale.ROOT);
|
||||
if (guess.length() != 5 || !guess.chars().allMatch(value -> value >= 'a' && value <= 'z')) {
|
||||
sendFile(player, payload, false, "", "invalid_word");
|
||||
return;
|
||||
}
|
||||
if (!BlockleDictionary.contains(guess)) {
|
||||
sendFile(player, payload, false, "", "invalid_word");
|
||||
return;
|
||||
}
|
||||
if (computer.blockleGuesses().size() >= 6 || computer.blockleGuesses().stream().anyMatch(value -> value.equals(answer.word()))) {
|
||||
sendFile(player, payload, false, "", "game_over");
|
||||
return;
|
||||
}
|
||||
computer.addBlockleGuess(guess);
|
||||
}
|
||||
sendFile(player, payload, true, encodeBlockle(day, answer, computer.blockleGuesses()), "");
|
||||
}
|
||||
|
||||
private static String encodeBlockle(long day, BlockleAnswers.Answer answer, java.util.List<String> guesses) {
|
||||
StringBuilder result = new StringBuilder(Long.toString(day));
|
||||
boolean solved = false;
|
||||
for (String guess : guesses) {
|
||||
String colors = BlockleGame.evaluate(answer.word(), guess);
|
||||
if (guess.equals(answer.word())) solved = true;
|
||||
result.append('|').append(guess).append(',').append(colors);
|
||||
}
|
||||
if (solved || guesses.size() >= 6) {
|
||||
result.append('|').append(solved ? "SOLVED" : "FAILED").append('|').append(answer.word()).append('|').append(answer.itemId());
|
||||
} else {
|
||||
result.append('|').append("PLAYING");
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static void sendDesktop(ServerPlayer player, ComputerAccessPayload payload, boolean success, String data) {
|
||||
BlockEntity blockEntity = player.serverLevel().getBlockEntity(payload.pos());
|
||||
boolean hasPassword = blockEntity instanceof ComputerBlockEntity computer && computer.hasPassword();
|
||||
|
||||
+4
@@ -25,6 +25,10 @@ public record ComputerAccessPayload(BlockPos pos, int action, String value) impl
|
||||
public static final int FILE_DELETE = 13;
|
||||
public static final int FILE_MOVE = 14;
|
||||
public static final int TERMINAL_COMMAND = 15;
|
||||
public static final int BASILISK_STATE = 16;
|
||||
public static final int ANTMAN_STATE = 17;
|
||||
public static final int BLOCKLE_STATE = 18;
|
||||
public static final int BLOCKLE_GUESS = 19;
|
||||
public static final Type<ComputerAccessPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer_access"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ComputerAccessPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
BlockPos.STREAM_CODEC, ComputerAccessPayload::pos,
|
||||
|
||||
+24
@@ -79,4 +79,28 @@ public final class ComputerNetworking {
|
||||
public static void selectWallpaper(BlockPos pos, ResourceLocation id) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.DESKTOP_WALLPAPER, id.toString()));
|
||||
}
|
||||
|
||||
public static void requestBasiliskState(BlockPos pos) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BASILISK_STATE));
|
||||
}
|
||||
|
||||
public static void saveBasiliskScore(BlockPos pos, int score) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BASILISK_STATE, Integer.toString(Math.max(0, score))));
|
||||
}
|
||||
|
||||
public static void requestAntmanState(BlockPos pos) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.ANTMAN_STATE));
|
||||
}
|
||||
|
||||
public static void saveAntmanScore(BlockPos pos, int score) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.ANTMAN_STATE, Integer.toString(Math.max(0, score))));
|
||||
}
|
||||
|
||||
public static void requestBlockleState(BlockPos pos) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BLOCKLE_STATE));
|
||||
}
|
||||
|
||||
public static void submitBlockleGuess(BlockPos pos, String guess) {
|
||||
sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BLOCKLE_GUESS, guess));
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package com.craisinlord.antarchy.content.worldgen;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.mixins.PoiTypesAccessor;
|
||||
import com.craisinlord.integrated_api.mixins.structures.StructurePoolAccessor;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Applies Antarchy-owned additions to existing vanilla jigsaw pools without replacing them. */
|
||||
public final class VillagePoolAdditions {
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final String DIRECTORY = "structure_pool_additions";
|
||||
|
||||
private VillagePoolAdditions() {
|
||||
}
|
||||
|
||||
public static void apply(MinecraftServer server) {
|
||||
registerComputerPoiStates(server);
|
||||
Registry<StructureTemplatePool> pools = server.registryAccess().registryOrThrow(Registries.TEMPLATE_POOL);
|
||||
Registry<StructureProcessorList> processors = server.registryAccess().registryOrThrow(Registries.PROCESSOR_LIST);
|
||||
|
||||
for (Map.Entry<ResourceLocation, Resource> entry : server.getResourceManager()
|
||||
.listResources(DIRECTORY, path -> path.getPath().endsWith(".json")).entrySet()) {
|
||||
try (java.io.Reader reader = entry.getValue().openAsReader()) {
|
||||
JsonObject definition = GSON.fromJson(reader, JsonObject.class);
|
||||
addToPool(pools, processors, definition, entry.getKey());
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
Antarchy.LOGGER.error("Failed to apply structure pool additions from {}", entry.getKey(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerComputerPoiStates(MinecraftServer server) {
|
||||
ResourceLocation poiId = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer");
|
||||
Registry<net.minecraft.world.entity.ai.village.poi.PoiType> poiRegistry =
|
||||
server.registryAccess().registryOrThrow(Registries.POINT_OF_INTEREST_TYPE);
|
||||
Holder<net.minecraft.world.entity.ai.village.poi.PoiType> poi = poiRegistry.getHolderOrThrow(
|
||||
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, poiId));
|
||||
Map<BlockState, Holder<net.minecraft.world.entity.ai.village.poi.PoiType>> stateMap =
|
||||
PoiTypesAccessor.antarchy$getTypeByState();
|
||||
for (BlockState state : AntarchyObjects.COMPUTER.get().getStateDefinition().getPossibleStates()) {
|
||||
stateMap.put(state, poi);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addToPool(
|
||||
Registry<StructureTemplatePool> pools,
|
||||
Registry<StructureProcessorList> processors,
|
||||
JsonObject definition,
|
||||
ResourceLocation source
|
||||
) {
|
||||
ResourceLocation poolId = ResourceLocation.parse(definition.get("pool").getAsString());
|
||||
StructureTemplatePool pool = pools.get(poolId);
|
||||
if (pool == null) {
|
||||
Antarchy.LOGGER.warn("Skipping structure pool addition {} because pool {} does not exist", source, poolId);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject elementDefinition = definition.getAsJsonObject("element");
|
||||
ResourceLocation location = ResourceLocation.parse(elementDefinition.get("location").getAsString());
|
||||
ResourceLocation processorId = ResourceLocation.parse(stringValue(
|
||||
elementDefinition, "processors", "minecraft:empty"));
|
||||
Holder<StructureProcessorList> processor = processors.getHolderOrThrow(
|
||||
ResourceKey.create(Registries.PROCESSOR_LIST, processorId));
|
||||
StructureTemplatePool.Projection projection = projection(stringValue(
|
||||
elementDefinition, "projection", "rigid"));
|
||||
int weight = definition.has("weight") ? definition.get("weight").getAsInt() : 1;
|
||||
if (weight < 1) {
|
||||
throw new IllegalArgumentException("weight must be positive");
|
||||
}
|
||||
|
||||
StructurePoolElement poolElement = StructurePoolElement
|
||||
.legacy(location.toString(), processor)
|
||||
.apply(projection);
|
||||
StructurePoolAccessor accessor = (StructurePoolAccessor) pool;
|
||||
List<Pair<StructurePoolElement, Integer>> templates = new ArrayList<>(accessor.integratedapi_getRawTemplates());
|
||||
templates.add(Pair.of(poolElement, weight));
|
||||
accessor.integratedapi_setRawTemplates(templates);
|
||||
|
||||
// StructureTemplatePool caches its weighted list in addition to the raw entries.
|
||||
// Keep both views synchronized so the new house is immediately eligible.
|
||||
ObjectArrayList<StructurePoolElement> expandedTemplates = new ObjectArrayList<>(accessor.integratedapi_getTemplates());
|
||||
for (int i = 0; i < weight; i++) {
|
||||
expandedTemplates.add(poolElement);
|
||||
}
|
||||
accessor.integratedapi_setTemplates(expandedTemplates);
|
||||
}
|
||||
|
||||
private static StructureTemplatePool.Projection projection(String value) {
|
||||
return switch (value) {
|
||||
case "rigid" -> StructureTemplatePool.Projection.RIGID;
|
||||
case "terrain_matching" -> StructureTemplatePool.Projection.TERRAIN_MATCHING;
|
||||
default -> throw new IllegalArgumentException("Unknown structure pool projection: " + value);
|
||||
};
|
||||
}
|
||||
|
||||
private static JsonElement primitive(String value) {
|
||||
return new com.google.gson.JsonPrimitive(value);
|
||||
}
|
||||
|
||||
private static String stringValue(JsonObject object, String key, String fallback) {
|
||||
return object.has(key) ? object.get(key).getAsString() : fallback;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.antmail.AntmailEventData;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(PlayerAdvancements.class)
|
||||
public abstract class PlayerAdvancementsMixin {
|
||||
@Shadow @Final private ServerPlayer player;
|
||||
|
||||
@Inject(method = "award", at = @At("RETURN"))
|
||||
private void antarchy$onAward(AdvancementHolder advancement, String criterion, CallbackInfoReturnable<Boolean> callback) {
|
||||
if (callback.getReturnValueZ() && player.getAdvancements().getOrStartProgress(advancement).isDone()) AntmailEventData.onAdvancement(player, advancement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(net.minecraft.world.entity.ai.village.poi.PoiTypes.class)
|
||||
public interface PoiTypesAccessor {
|
||||
@Accessor("TYPE_BY_STATE")
|
||||
static Map<BlockState, Holder<PoiType>> antarchy$getTypeByState() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.craisinlord.antarchy.mixins;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiTypes;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(PoiTypes.class)
|
||||
public abstract class PoiTypesMixin {
|
||||
@Inject(method = "forState", at = @At("HEAD"), cancellable = true)
|
||||
private static void antarchy$computerPoi(BlockState state,
|
||||
CallbackInfoReturnable<Optional<Holder<PoiType>>> callback) {
|
||||
try {
|
||||
if (!state.is(AntarchyObjects.COMPUTER.get())) {
|
||||
return;
|
||||
}
|
||||
ResourceKey<PoiType> key = ResourceKey.create(
|
||||
Registries.POINT_OF_INTEREST_TYPE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer"));
|
||||
BuiltInRegistries.POINT_OF_INTEREST_TYPE.getHolder(key)
|
||||
.ifPresent(holder -> callback.setReturnValue(Optional.of(holder)));
|
||||
} catch (IllegalStateException ignored) {
|
||||
// Registration can be queried during bootstrap before loader bindings finish.
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -4,7 +4,7 @@ import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
|
||||
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
|
||||
// import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import java.util.List;
|
||||
@@ -349,12 +349,9 @@ public abstract class EntityMoveMixin {
|
||||
localMovement, cir.getReturnValue(), this.antarchy$collideWorldMovement, worldResult,
|
||||
entity.onGround(), entity.maxUpStep(), entity.getBoundingBox());
|
||||
}
|
||||
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(
|
||||
entity,
|
||||
entity.getBoundingBox(),
|
||||
this.antarchy$collideWorldMovement,
|
||||
worldResult
|
||||
));
|
||||
// Portal-gun collision restoration is temporarily disabled. It can
|
||||
// recurse through Sable's collision redirect and hang the server.
|
||||
cir.setReturnValue(worldResult);
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
||||
Reference in new issue
Block a user