mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
Upstream has released updates that appear to apply and compile correctly Gale Changes: Dreeam-qwq/Gale@f2c8aaf Sync update from ver/1.21.1 branch
24911 lines
1.3 MiB
24911 lines
1.3 MiB
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Github Actions <no-reply@github.com>
|
|
Date: Thu, 12 Dec 2024 11:10:14 +0000
|
|
Subject: [PATCH] Purpur Server Changes
|
|
|
|
Original license: MIT
|
|
Original project: https://github.com/PurpurMC/Purpur
|
|
|
|
Commit: 16ce24aa7eb08232030e4570e027f7baefa5f3f9
|
|
|
|
Patches below are removed in this patch:
|
|
Pufferfish-Server-Changes.patch
|
|
Fix-pufferfish-issues.patch
|
|
Brand changes in Rebrand.patch
|
|
Metrics changes in Purpur-config-files.patch
|
|
Fix-decompile-errors.patch
|
|
Configurable-server-mod-name.patch
|
|
Alternative-Keepalive-Handling.patch
|
|
Logger-settings-suppressing-pointless-logs.patch
|
|
Add-log-suppression-for-LibraryLoader.patch
|
|
Fix-outdated-server-showing-in-ping-before-server-fu.patch
|
|
Fix-cow-rotation-when-shearing-mooshroom.patch
|
|
Skip-events-if-there-s-no-listeners.patch
|
|
Add-5-second-tps-average-in-tps.patch
|
|
Arrows-should-not-reset-despawn-counter.patch
|
|
MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch
|
|
Option-to-disable-kick-for-out-of-order-chat.patch
|
|
Make-pufferfish-config-relocatable.patch
|
|
MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index 2fddb2f371b5b901be668fb60dbf871830ec571f..3b0cd45cb07d9563c84901729f1f7edc498653bd 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -67,6 +67,12 @@ dependencies {
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
|
|
|
|
+ // Purpur start
|
|
+ implementation("org.mozilla:rhino-runtime:1.7.15")
|
|
+ implementation("org.mozilla:rhino-engine:1.7.15")
|
|
+ implementation("dev.omega24:upnp4j:1.0")
|
|
+ // Purpur end
|
|
+
|
|
testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
|
|
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
|
testImplementation("org.junit.platform:junit-platform-suite-engine:1.10.0")
|
|
@@ -189,7 +195,7 @@ fun TaskContainer.registerRunTask(
|
|
name: String,
|
|
block: JavaExec.() -> Unit
|
|
): TaskProvider<JavaExec> = register<JavaExec>(name) {
|
|
- group = "paper"
|
|
+ group = "paperweight" // Purpur
|
|
mainClass.set("org.bukkit.craftbukkit.Main")
|
|
standardInput = System.`in`
|
|
workingDir = rootProject.layout.projectDirectory
|
|
diff --git a/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8ace07630c
|
|
--- /dev/null
|
|
+++ b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java
|
|
@@ -0,0 +1,85 @@
|
|
+package org.purpurmc.purpur.gui.util;
|
|
+
|
|
+import org.apache.logging.log4j.Level;
|
|
+import org.apache.logging.log4j.core.LogEvent;
|
|
+import org.apache.logging.log4j.core.config.Configuration;
|
|
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
|
+import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
|
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternConverter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternParser;
|
|
+import org.apache.logging.log4j.util.PerformanceSensitive;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY)
|
|
+@ConverterKeys({"highlightGUIError"})
|
|
+@PerformanceSensitive("allocation")
|
|
+public final class HighlightErrorConverter extends LogEventPatternConverter {
|
|
+ private static final String ERROR = "\u00A74\u00A7l"; // Bold Red
|
|
+ private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow
|
|
+
|
|
+ private final List<PatternFormatter> formatters;
|
|
+
|
|
+ private HighlightErrorConverter(List<PatternFormatter> formatters) {
|
|
+ super("highlightGUIError", null);
|
|
+ this.formatters = formatters;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void format(LogEvent event, StringBuilder toAppendTo) {
|
|
+ Level level = event.getLevel();
|
|
+ if (level.isMoreSpecificThan(Level.ERROR)) {
|
|
+ format(ERROR, event, toAppendTo);
|
|
+ return;
|
|
+ } else if (level.isMoreSpecificThan(Level.WARN)) {
|
|
+ format(WARN, event, toAppendTo);
|
|
+ return;
|
|
+ }
|
|
+ for (PatternFormatter formatter : formatters) {
|
|
+ formatter.format(event, toAppendTo);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void format(String style, LogEvent event, StringBuilder toAppendTo) {
|
|
+ int start = toAppendTo.length();
|
|
+ toAppendTo.append(style);
|
|
+ int end = toAppendTo.length();
|
|
+
|
|
+ for (PatternFormatter formatter : formatters) {
|
|
+ formatter.format(event, toAppendTo);
|
|
+ }
|
|
+
|
|
+ if (toAppendTo.length() == end) {
|
|
+ toAppendTo.setLength(start);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean handlesThrowable() {
|
|
+ for (final PatternFormatter formatter : formatters) {
|
|
+ if (formatter.handlesThrowable()) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public static HighlightErrorConverter newInstance(Configuration config, String[] options) {
|
|
+ if (options.length != 1) {
|
|
+ LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length);
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if (options[0] == null) {
|
|
+ LOGGER.error("No pattern supplied on highlightGUIError");
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ PatternParser parser = PatternLayout.createPatternParser(config);
|
|
+ List<PatternFormatter> formatters = parser.parse(options[0]);
|
|
+ return new HighlightErrorConverter(formatters);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
index 3470720466fc81f977c18e3a97bb918926025a22..c8651af322927c46d075f88890fcd0476bd85440 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
@@ -136,6 +136,10 @@ public class MobGoalHelper {
|
|
static {
|
|
// TODO these kinda should be checked on each release, in case obfuscation changes
|
|
deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee");
|
|
+ // Purpur start
|
|
+ deobfuscationMap.put("zombie_1", "zombie_attack_villager");
|
|
+ deobfuscationMap.put("drowned_1", "drowned_attack_villager");
|
|
+ // Purpur end
|
|
|
|
ignored.add("goal_selector_1");
|
|
ignored.add("goal_selector_2");
|
|
diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
|
|
index 00470a690b4b0fc8996a03ecd21af8163094184d..23609a71a993fc91271578ee0a541a9c6ec7354f 100644
|
|
--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
|
|
+++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
|
|
@@ -32,6 +32,7 @@ public record ServerBuildInfoImpl(
|
|
|
|
private static final String BRAND_PAPER_NAME = "Paper";
|
|
private static final String BRAND_GALE_NAME = "Gale"; // Gale - branding changes
|
|
+ private static final String BRAND_PURPUR_NAME = "Purpur"; // Purpur
|
|
private static final String BRAND_LEAF_NAME = "Leaf"; // Leaf
|
|
|
|
private static final String BUILD_DEV = "DEV";
|
|
diff --git a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
index f0fce4113fb07c64adbec029d177c236cbdcbae8..865dc183276720d54d31d2a54d1bb5c845e80598 100644
|
|
--- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
+++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
@@ -78,10 +78,10 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
this.setAliases(Arrays.asList("pl"));
|
|
}
|
|
|
|
- private static <T> List<Component> formatProviders(TreeMap<String, PluginProvider<T>> plugins) {
|
|
+ private static <T> List<Component> formatProviders(TreeMap<String, PluginProvider<T>> plugins, @NotNull CommandSender sender) { // Purpur
|
|
List<Component> components = new ArrayList<>(plugins.size());
|
|
for (PluginProvider<T> entry : plugins.values()) {
|
|
- components.add(formatProvider(entry));
|
|
+ components.add(formatProvider(entry, sender)); // Purpur
|
|
}
|
|
|
|
boolean isFirst = true;
|
|
@@ -109,7 +109,7 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
return formattedSublists;
|
|
}
|
|
|
|
- private static Component formatProvider(PluginProvider<?> provider) {
|
|
+ private static Component formatProvider(PluginProvider<?> provider, @NotNull CommandSender sender) { // Purpur
|
|
TextComponent.Builder builder = Component.text();
|
|
if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) {
|
|
builder.append(LEGACY_PLUGIN_STAR);
|
|
@@ -117,13 +117,65 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
|
|
String name = provider.getMeta().getName();
|
|
Component pluginName = Component.text(name, fromStatus(provider))
|
|
- .clickEvent(ClickEvent.runCommand("/version " + name));
|
|
+ // Purpur start
|
|
+ .clickEvent(ClickEvent.suggestCommand("/version " + name));
|
|
+
|
|
+ if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) {
|
|
+ // Event components
|
|
+ String description = provider.getMeta().getDescription();
|
|
+ TextComponent.Builder hover = Component.text();
|
|
+ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(provider.getMeta().getVersion(), NamedTextColor.GREEN));
|
|
+
|
|
+ if (description != null) {
|
|
+ hover.append(Component.newline())
|
|
+ .append(Component.text("Description: ", NamedTextColor.WHITE))
|
|
+ .append(Component.text(description, NamedTextColor.GREEN));
|
|
+ }
|
|
+
|
|
+ if (provider.getMeta().getWebsite() != null) {
|
|
+ hover.append(Component.newline())
|
|
+ .append(Component.text("Website: ", NamedTextColor.WHITE))
|
|
+ .append(Component.text(provider.getMeta().getWebsite(), NamedTextColor.GREEN));
|
|
+ }
|
|
+
|
|
+ if (!provider.getMeta().getAuthors().isEmpty()) {
|
|
+ hover.append(Component.newline());
|
|
+ if (provider.getMeta().getAuthors().size() == 1) {
|
|
+ hover.append(Component.text("Author: "));
|
|
+ } else {
|
|
+ hover.append(Component.text("Authors: "));
|
|
+ }
|
|
+
|
|
+ hover.append(getAuthors(provider.getMeta()));
|
|
+ }
|
|
+
|
|
+ pluginName.hoverEvent(hover.build());
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
builder.append(pluginName);
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @NotNull
|
|
+ private static TextComponent getAuthors(@NotNull final PluginMeta pluginMeta) {
|
|
+ TextComponent.Builder builder = Component.text();
|
|
+ List<String> authors = pluginMeta.getAuthors();
|
|
+
|
|
+ for (int i = 0; i < authors.size(); i++) {
|
|
+ if (i > 0) {
|
|
+ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE));
|
|
+ }
|
|
+
|
|
+ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN));
|
|
+ }
|
|
+
|
|
+ return builder.build();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
private static Component asPlainComponents(String strings) {
|
|
net.kyori.adventure.text.TextComponent.Builder builder = Component.text();
|
|
for (String string : strings.split("\n")) {
|
|
@@ -182,24 +234,24 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
}
|
|
}
|
|
|
|
- Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
|
|
+ //Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
|
|
//.append(INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO)); TODO: Add docs
|
|
|
|
- sender.sendMessage(infoMessage);
|
|
+ //sender.sendMessage(infoMessage); // Purpur
|
|
|
|
- if (!paperPlugins.isEmpty()) {
|
|
- sender.sendMessage(PAPER_HEADER);
|
|
- }
|
|
+ //if (!paperPlugins.isEmpty()) { // Purpur
|
|
+ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur
|
|
+ //} // Purpur
|
|
|
|
- for (Component component : formatProviders(paperPlugins)) {
|
|
+ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur
|
|
sender.sendMessage(component);
|
|
}
|
|
|
|
- if (!spigotPlugins.isEmpty()) {
|
|
- sender.sendMessage(BUKKIT_HEADER);
|
|
- }
|
|
+ //if (!spigotPlugins.isEmpty()) { // Purpur
|
|
+ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur
|
|
+ //} // Purpur
|
|
|
|
- for (Component component : formatProviders(spigotPlugins)) {
|
|
+ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur
|
|
sender.sendMessage(component);
|
|
}
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java
|
|
index b993d3e2e9d987115129067e3a51060880453ee2..a9d5bad94d499ea8c63beff268713261cbdea216 100644
|
|
--- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java
|
|
+++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java
|
|
@@ -263,6 +263,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
|
for (final NodePath path : RemovedConfigurations.REMOVED_WORLD_PATHS) {
|
|
builder.addAction(path, TransformAction.remove());
|
|
}
|
|
+ org.purpurmc.purpur.configuration.transformation.VoidDamageHeightMigration.apply(builder, contextMap); // Purpur
|
|
builder.build().apply(node);
|
|
|
|
final ConfigurationTransformation.VersionedBuilder versionedBuilder = Transformations.versionedBuilder();
|
|
diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
|
index b1c917d65076a3805e5b78cb946753f0c101e214..82210667376fd466d5d4cdcb56b62f6165bd5cde 100644
|
|
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
|
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
|
@@ -409,6 +409,7 @@ public class WorldConfiguration extends ConfigurationPart {
|
|
public boolean useVanillaWorldScoreboardNameColoring = false;
|
|
}
|
|
|
|
+ @Setting(org.purpurmc.purpur.configuration.transformation.VoidDamageHeightMigration.ENVIRONMENT_KEY) // Purpur
|
|
public Environment environment;
|
|
|
|
public class Environment extends ConfigurationPart {
|
|
@@ -418,7 +419,9 @@ public class WorldConfiguration extends ConfigurationPart {
|
|
public boolean disableExplosionKnockback = false;
|
|
public boolean generateFlatBedrock = false;
|
|
public FrostedIce frostedIce;
|
|
+ @Setting(org.purpurmc.purpur.configuration.transformation.VoidDamageHeightMigration.VOID_DAMAGE_KEY) // Purpur
|
|
public DoubleOr.Disabled voidDamageAmount = new DoubleOr.Disabled(OptionalDouble.of(4));
|
|
+ @Setting(org.purpurmc.purpur.configuration.transformation.VoidDamageHeightMigration.VOID_DAMAGE_MIN_HEIGHT_OFFSET_KEY) // Purpur
|
|
public double voidDamageMinBuildHeightOffset = -64.0;
|
|
|
|
public class FrostedIce extends ConfigurationPart {
|
|
diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
index a8e813ca89b033f061e695288b3383bdcf128531..1ab65af9359d19530bba7f985a604d2a430ee234 100644
|
|
--- a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
+++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
@@ -54,9 +54,9 @@ public final class SysoutCatcher {
|
|
final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(clazz);
|
|
|
|
// Instead of just printing the message, send it to the plugin's logger
|
|
- plugin.getLogger().log(this.level, this.prefix + line);
|
|
+ plugin.getLogger().log(this.level, /*this.prefix +*/ line); // Purpur - prefix not needed
|
|
|
|
- if (SysoutCatcher.SUPPRESS_NAGS) {
|
|
+ if (true || SysoutCatcher.SUPPRESS_NAGS) { // Purpur - nagging is annoying
|
|
return;
|
|
}
|
|
if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) {
|
|
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
index 13bd145b1e8006a53c22f5dc0c78f29b540c7663..0d133cd7993eb40b19e2aabe8e2bfcdcf5352398 100644
|
|
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
@@ -211,6 +211,19 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ public boolean testPermission(int i, String bukkitPermission) {
|
|
+ if (hasPermission(i, bukkitPermission)) {
|
|
+ return true;
|
|
+ }
|
|
+ net.kyori.adventure.text.Component permissionMessage = getLevel().getServer().server.permissionMessage();
|
|
+ if (!permissionMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
|
+ sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(permissionMessage.replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<permission>").replacement(bukkitPermission).build())));
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public Vec3 getPosition() {
|
|
return this.worldPosition;
|
|
}
|
|
@@ -312,6 +325,30 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void sendSuccess(@Nullable String message) {
|
|
+ sendSuccess(message, false);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable String message, boolean broadcastToOps) {
|
|
+ if (message == null) {
|
|
+ return;
|
|
+ }
|
|
+ sendSuccess(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), broadcastToOps);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ sendSuccess(message, false);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message, boolean broadcastToOps) {
|
|
+ if (message == null) {
|
|
+ return;
|
|
+ }
|
|
+ sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void sendSuccess(Supplier<Component> feedbackSupplier, boolean broadcastToOps) {
|
|
boolean flag1 = this.source.acceptsSuccess() && !this.silent;
|
|
boolean flag2 = broadcastToOps && this.source.shouldInformAdmins() && !this.silent;
|
|
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
|
|
index 8f0162ffa1461e7444b51917001d8a27ca1f3dae..6191930c133758936ffd3cc2588c3f8713145508 100644
|
|
--- a/src/main/java/net/minecraft/commands/Commands.java
|
|
+++ b/src/main/java/net/minecraft/commands/Commands.java
|
|
@@ -223,8 +223,8 @@ public class Commands {
|
|
JfrCommand.register(this.dispatcher);
|
|
}
|
|
|
|
- if (SharedConstants.IS_RUNNING_IN_IDE) {
|
|
- TestCommand.register(this.dispatcher);
|
|
+ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands) TestCommand.register(this.dispatcher); // Purpur
|
|
RaidCommand.register(this.dispatcher, commandRegistryAccess);
|
|
DebugPathCommand.register(this.dispatcher);
|
|
DebugMobSpawningCommand.register(this.dispatcher);
|
|
@@ -252,6 +252,14 @@ public class Commands {
|
|
StopCommand.register(this.dispatcher);
|
|
TransferCommand.register(this.dispatcher);
|
|
WhitelistCommand.register(this.dispatcher);
|
|
+ org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.RamBarCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur
|
|
}
|
|
|
|
if (environment.includeIntegrated) {
|
|
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
index c8d39e6e1c570c9219f6066da273dc0130920519..b455c7e9d18bac3654daa8510f85cc21202e254b 100644
|
|
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
@@ -198,10 +198,10 @@ public class EntitySelector {
|
|
|
|
if (this.playerName != null) {
|
|
entityplayer = source.getServer().getPlayerList().getPlayerByName(this.playerName);
|
|
- return entityplayer == null ? List.of() : List.of(entityplayer);
|
|
+ return entityplayer == null || !canSee(source, entityplayer) ? List.of() : List.of(entityplayer); // Purpur
|
|
} else if (this.entityUUID != null) {
|
|
entityplayer = source.getServer().getPlayerList().getPlayer(this.entityUUID);
|
|
- return entityplayer == null ? List.of() : List.of(entityplayer);
|
|
+ return entityplayer == null || !canSee(source, entityplayer) ? List.of() : List.of(entityplayer); // Purpur
|
|
} else {
|
|
Vec3 vec3d = (Vec3) this.position.apply(source.getPosition());
|
|
AABB axisalignedbb = this.getAbsoluteAabb(vec3d);
|
|
@@ -214,7 +214,7 @@ public class EntitySelector {
|
|
ServerPlayer entityplayer1 = (ServerPlayer) entity;
|
|
|
|
if (predicate.test(entityplayer1)) {
|
|
- return List.of(entityplayer1);
|
|
+ return !canSee(source, entityplayer1) ? List.of() : List.of(entityplayer1); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -225,6 +225,7 @@ public class EntitySelector {
|
|
|
|
if (this.isWorldLimited()) {
|
|
object = source.getLevel().getPlayers(predicate, i);
|
|
+ ((List) object).removeIf(entityplayer3 -> !canSee(source, (ServerPlayer) entityplayer3)); // Purpur
|
|
} else {
|
|
object = new ObjectArrayList();
|
|
Iterator iterator = source.getServer().getPlayerList().getPlayers().iterator();
|
|
@@ -232,7 +233,7 @@ public class EntitySelector {
|
|
while (iterator.hasNext()) {
|
|
ServerPlayer entityplayer2 = (ServerPlayer) iterator.next();
|
|
|
|
- if (predicate.test(entityplayer2)) {
|
|
+ if (predicate.test(entityplayer2) && canSee(source, entityplayer2)) { // Purpur
|
|
((List) object).add(entityplayer2);
|
|
if (((List) object).size() >= i) {
|
|
return (List) object;
|
|
@@ -299,4 +300,10 @@ public class EntitySelector {
|
|
public static Component joinNames(List<? extends Entity> entities) {
|
|
return ComponentUtils.formatList(entities, Entity::getDisplayName);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ private boolean canSee(CommandSourceStack sender, ServerPlayer target) {
|
|
+ return !org.purpurmc.purpur.PurpurConfig.hideHiddenPlayersFromEntitySelector || !(sender.getEntity() instanceof ServerPlayer player) || player.getBukkitEntity().canSee(target.getBukkitEntity());
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
|
|
index f58a94efafbc01d402cd03a108bb90f60930a316..21ea63da99c5b3e2e1ab9cc1049c903bba6cf288 100644
|
|
--- a/src/main/java/net/minecraft/core/BlockPos.java
|
|
+++ b/src/main/java/net/minecraft/core/BlockPos.java
|
|
@@ -62,6 +62,12 @@ public class BlockPos extends Vec3i {
|
|
public static final int MAX_HORIZONTAL_COORDINATE = 33554431;
|
|
// Paper end - Optimize Bit Operations by inlining
|
|
|
|
+ // Purpur start
|
|
+ public BlockPos(net.minecraft.world.entity.Entity entity) {
|
|
+ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public BlockPos(int x, int y, int z) {
|
|
super(x, y, z);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
index 0d12605dc84dad49faa18bf1fd058c3c168623ee..c6490554a3025f4de3f3218178fad76cd1848a19 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
@@ -930,5 +930,22 @@ public interface DispenseItemBehavior {
|
|
DispenserBlock.registerBehavior(Items.TNT_MINECART, new MinecartDispenseItemBehavior(EntityType.TNT_MINECART));
|
|
DispenserBlock.registerBehavior(Items.HOPPER_MINECART, new MinecartDispenseItemBehavior(EntityType.HOPPER_MINECART));
|
|
DispenserBlock.registerBehavior(Items.COMMAND_BLOCK_MINECART, new MinecartDispenseItemBehavior(EntityType.COMMAND_BLOCK_MINECART));
|
|
+ // Purpur start
|
|
+ DispenserBlock.registerBehavior(Items.ANVIL, (new OptionalDispenseItemBehavior() {
|
|
+ @Override
|
|
+ public ItemStack execute(BlockSource dispenser, ItemStack stack) {
|
|
+ net.minecraft.world.level.Level level = dispenser.level();
|
|
+ if (!level.purpurConfig.dispenserPlaceAnvils) return super.execute(dispenser, stack);
|
|
+ Direction facing = dispenser.blockEntity().getBlockState().getValue(DispenserBlock.FACING);
|
|
+ BlockPos pos = dispenser.pos().relative(facing);
|
|
+ BlockState state = level.getBlockState(pos);
|
|
+ if (state.isAir()) {
|
|
+ level.setBlockAndUpdate(pos, Blocks.ANVIL.defaultBlockState().setValue(net.minecraft.world.level.block.AnvilBlock.FACING, facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : facing.getClockWise()));
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return stack;
|
|
+ }
|
|
+ }));
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
|
|
index bf8c511739265c6a9cd277752e844481598f8966..ffe2399ab6b1f311536475d8216238b5b01c5dab 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
|
|
@@ -41,7 +41,7 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
|
|
return false;
|
|
} else {
|
|
LivingEntity entityliving = (LivingEntity) list.getFirst();
|
|
- EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(stack);
|
|
+ EquipmentSlot enumitemslot = pointer.level().purpurConfig.dispenserApplyCursedArmor ? entityliving.getEquipmentSlotForItem(stack) : entityliving.getEquipmentSlotForDispenserItem(stack); if (enumitemslot == null) return false; // Purpur - Dispenser curse of binding protection
|
|
ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
|
|
|
// CraftBukkit start
|
|
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
|
index 1900da381257a00fc3b96d397e298caf2f8c861f..ebef8e06835e3ef29fad60006c570f791e4a3937 100644
|
|
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
@@ -617,11 +617,20 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
|
private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
|
private static int currTick; // Paper - Buffer joins to world
|
|
+ private static int tickSecond; // Purpur
|
|
public void tick() {
|
|
this.flushQueue();
|
|
// Paper start - Buffer joins to world
|
|
if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) {
|
|
Connection.currTick = net.minecraft.server.MinecraftServer.currentTick;
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) {
|
|
+ if (++Connection.tickSecond > 20) {
|
|
+ Connection.tickSecond = 0;
|
|
+ Connection.joinAttemptsThisTick = 0;
|
|
+ }
|
|
+ } else
|
|
+ // Purpur end
|
|
Connection.joinAttemptsThisTick = 0;
|
|
}
|
|
// Paper end - Buffer joins to world
|
|
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
|
|
index c7b5429910df7e3c4cfe1ac2f095845fd493c9b1..e0c2e52801421845c74da938b20692a68d838cdf 100644
|
|
--- a/src/main/java/net/minecraft/server/Main.java
|
|
+++ b/src/main/java/net/minecraft/server/Main.java
|
|
@@ -120,6 +120,12 @@ public class Main {
|
|
JvmProfiler.INSTANCE.start(Environment.SERVER);
|
|
}
|
|
|
|
+ // Purpur start - load config files early
|
|
+ org.bukkit.configuration.file.YamlConfiguration purpurConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("purpur-settings"));
|
|
+ org.purpurmc.purpur.PurpurConfig.clampEnchantLevels = purpurConfiguration.getBoolean("settings.enchantment.clamp-levels", true);
|
|
+ org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands = purpurConfiguration.getBoolean("settings.register-minecraft-debug-commands");
|
|
+ // Purpur end - load config files early
|
|
+
|
|
io.papermc.paper.plugin.PluginInitializerManager.load(optionset); // Paper
|
|
Bootstrap.bootStrap();
|
|
Bootstrap.validate();
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index e084dd8f6e299e2ec71d7d5741c92b0e171f34f6..a624cd8a0fa9ad05f8f69c50f3fdf961713b1791 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -298,6 +298,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public OptionSet options;
|
|
public org.bukkit.command.ConsoleCommandSender console;
|
|
public static int currentTick; // Paper - improve tick loop
|
|
+ public static final long startTimeMillis = System.currentTimeMillis();
|
|
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
|
public int autosavePeriod;
|
|
// Paper - don't store the vanilla dispatcher
|
|
@@ -313,6 +314,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations; // Paper - add paper configuration files
|
|
public final org.galemc.gale.configuration.GaleConfigurations galeConfigurations; // Gale - Gale configuration
|
|
public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
|
+ public boolean lagging = false; // Purpur
|
|
+ protected boolean upnp = false; // Purpur
|
|
|
|
public volatile Thread shutdownThread; // Paper
|
|
public volatile boolean abnormalExit = false; // Paper
|
|
@@ -1038,6 +1041,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
MinecraftServer.LOGGER.info("Stopping server");
|
|
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
|
|
+ // Purpur start
|
|
+ if (upnp) {
|
|
+ if (dev.omega24.upnp4j.UPnP4J.close(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ LOGGER.info("[UPnP] Port {} closed", this.getPort());
|
|
+ } else {
|
|
+ LOGGER.error("[UPnP] Failed to close port {}", this.getPort());
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start
|
|
if (this.server != null) {
|
|
this.server.spark.disable(); // Paper - spark
|
|
@@ -1140,6 +1152,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.safeShutdown(waitForShutdown, false);
|
|
}
|
|
public void safeShutdown(boolean waitForShutdown, boolean isRestarting) {
|
|
+ org.purpurmc.purpur.task.BossBarTask.stopAll(); // Purpur
|
|
+ org.purpurmc.purpur.task.BeehiveTask.instance().unregister(); // Purpur
|
|
this.isRestarting = isRestarting;
|
|
this.hasLoggedStop = true; // Paper - Debugging
|
|
if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
|
|
@@ -1261,6 +1275,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
LOGGER.info("*************************************************************************************");
|
|
}
|
|
// Paper end - Add onboarding message for initial server start
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.configuration.transformation.VoidDamageHeightMigration.HAS_BEEN_REGISTERED) {
|
|
+ try {
|
|
+ org.purpurmc.purpur.PurpurConfig.config.save((File) this.options.valueOf("purpur-settings"));
|
|
+ } catch (IOException ex) {
|
|
+ Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Could not save " + this.options.valueOf("purpur-settings"), ex);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ // Purpur start
|
|
+ if (!Boolean.getBoolean("Purpur.IReallyDontWantStartupCommands") && !org.purpurmc.purpur.PurpurConfig.startupCommands.isEmpty()) {
|
|
+ LOGGER.info("Purpur: Running startup commands specified in purpur.yml.");
|
|
+ for (final String startupCommand : org.purpurmc.purpur.PurpurConfig.startupCommands) {
|
|
+ LOGGER.info("Purpur: Running the following command: \"{}\"", startupCommand);
|
|
+ ((DedicatedServer) this).handleConsoleInput(startupCommand, this.createCommandSourceStack());
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
while (this.running) {
|
|
long i;
|
|
@@ -1297,6 +1330,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.recentTps[0] = tps1.getAverage();
|
|
this.recentTps[1] = tps5.getAverage();
|
|
this.recentTps[2] = tps15.getAverage();
|
|
+ lagging = recentTps[0] < org.purpurmc.purpur.PurpurConfig.laggingThreshold; // Purpur
|
|
tickSection = currentTime;
|
|
}
|
|
// Paper end - further improve server tick loop
|
|
@@ -1787,7 +1821,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
long worldTime = level.getGameTime();
|
|
final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight);
|
|
for (Player entityhuman : level.players()) {
|
|
- if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) {
|
|
+ if (!(entityhuman instanceof ServerPlayer) || (!level.isForceTime() && (tickCount + entityhuman.getId()) % 20 != 0)) { // Purpur
|
|
continue;
|
|
}
|
|
ServerPlayer entityplayer = (ServerPlayer) entityhuman;
|
|
@@ -1807,6 +1841,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
|
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
|
|
worldserver.updateLagCompensationTick(); // Paper - lag compensation
|
|
+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur
|
|
|
|
/* Drop global time updates
|
|
if (this.tickCount % 20 == 0) {
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
index 66e7fa8a01d5364f7a82ed36f41edc6735b0b5ef..67f7c397328c8fbdbd30e1f3b94821a785ff1036 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -249,6 +249,7 @@ public class PlayerAdvancements {
|
|
advancement.value().display().ifPresent((advancementdisplay) -> {
|
|
// Paper start - Add Adventure message to PlayerAdvancementDoneEvent
|
|
if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur
|
|
this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false);
|
|
// Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/commands/EnchantCommand.java b/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
index cf0a5943f457c532958f40b4989fa18f967abae6..2ab8ff8ca51eb841932ccca4a348acc0141264a8 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
@@ -70,7 +70,7 @@ public class EnchantCommand {
|
|
|
|
private static int enchant(CommandSourceStack source, Collection<? extends Entity> targets, Holder<Enchantment> enchantment, int level) throws CommandSyntaxException {
|
|
Enchantment enchantment2 = enchantment.value();
|
|
- if (level > enchantment2.getMaxLevel()) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && level > enchantment2.getMaxLevel()) { // Purpur - Config to allow unsafe enchants
|
|
throw ERROR_LEVEL_TOO_HIGH.create(level, enchantment2.getMaxLevel());
|
|
} else {
|
|
int i = 0;
|
|
@@ -81,7 +81,7 @@ public class EnchantCommand {
|
|
ItemStack itemStack = livingEntity.getMainHandItem();
|
|
if (!itemStack.isEmpty()) {
|
|
if (enchantment2.canEnchant(itemStack)
|
|
- && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(itemStack).keySet(), enchantment)) {
|
|
+ && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(itemStack).keySet(), enchantment) || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && !itemStack.hasEnchantment(enchantment))) { // Purpur - Config to allow unsafe enchants
|
|
itemStack.enchant(enchantment, level);
|
|
i++;
|
|
} else if (targets.size() == 1) {
|
|
diff --git a/src/main/java/net/minecraft/server/commands/GameModeCommand.java b/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
index d1da3600dc07107309b20ebe6e7c0c4da0e8de76..244b4719c689f153fa36381a60acc280bb0bd9b3 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
@@ -57,6 +57,18 @@ public class GameModeCommand {
|
|
}
|
|
|
|
private static int setMode(CommandContext<CommandSourceStack> context, Collection<ServerPlayer> targets, GameType gameMode) {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.commandGamemodeRequiresPermission) {
|
|
+ String gamemode = gameMode.getName();
|
|
+ CommandSourceStack sender = context.getSource();
|
|
+ if (!sender.testPermission(2, "minecraft.command.gamemode." + gamemode)) {
|
|
+ return 0;
|
|
+ }
|
|
+ if (sender.getEntity() instanceof ServerPlayer player && (targets.size() > 1 || !targets.contains(player)) && !sender.testPermission(2, "minecraft.command.gamemode." + gamemode + ".other")) {
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
int i = 0;
|
|
|
|
for (ServerPlayer serverPlayer : targets) {
|
|
diff --git a/src/main/java/net/minecraft/server/commands/GiveCommand.java b/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
index 0d9de4c61c7b26a6ff37c12fde629161fd0c3d5a..2f7897744f4aea718170698881773e9031a58a51 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
@@ -60,6 +60,7 @@ public class GiveCommand {
|
|
boolean flag = entityplayer.getInventory().add(itemstack1);
|
|
ItemEntity entityitem;
|
|
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableGiveCommandDrops) continue; // Purpur - add config option for toggling give command dropping
|
|
if (flag && itemstack1.isEmpty()) {
|
|
entityitem = entityplayer.drop(itemstack, false, false, false); // CraftBukkit - SPIGOT-2942: Add boolean to call event
|
|
if (entityitem != null) {
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index 535dc6414c6b14ebd652695615e5a62d82f34171..940fdcfe0e1bd68891903f33d61d63c2d72fc3df 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
@@ -111,6 +111,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
return;
|
|
}
|
|
// Paper start - Use TerminalConsoleAppender
|
|
+ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - has no GUI or has console (did not double-click)
|
|
new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
|
|
/*
|
|
jline.console.ConsoleReader bufferedreader = DedicatedServer.this.reader;
|
|
@@ -219,6 +220,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
org.spigotmc.SpigotConfig.registerCommands();
|
|
// Spigot end
|
|
io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
|
|
+ // Purpur start
|
|
+ try {
|
|
+ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings"));
|
|
+ } catch (Exception e) {
|
|
+ DedicatedServer.LOGGER.error("Unable to load server configuration", e);
|
|
+ return false;
|
|
+ }
|
|
+ org.purpurmc.purpur.PurpurConfig.registerCommands();
|
|
+ // Purpur end
|
|
// Paper start - initialize global and world-defaults configuration
|
|
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
|
|
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
|
@@ -295,6 +305,30 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
if (true) throw new IllegalStateException("Failed to bind to port", ioexception); // Paper - Propagate failed to bind to port error
|
|
return false;
|
|
}
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.useUPnP) {
|
|
+ LOGGER.info("[UPnP] Attempting to start UPnP port forwarding service...");
|
|
+ if (dev.omega24.upnp4j.UPnP4J.isUPnPAvailable()) {
|
|
+ if (dev.omega24.upnp4j.UPnP4J.isOpen(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ this.upnp = false;
|
|
+ LOGGER.info("[UPnP] Port {} is already open", this.getPort());
|
|
+ } else if (dev.omega24.upnp4j.UPnP4J.open(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ this.upnp = true;
|
|
+ LOGGER.info("[UPnP] Successfully opened port {}", this.getPort());
|
|
+ } else {
|
|
+ this.upnp = false;
|
|
+ LOGGER.info("[UPnP] Failed to open port {}", this.getPort());
|
|
+ }
|
|
+
|
|
+ if (upnp) {
|
|
+ LOGGER.info("[UPnP] {}:{}", dev.omega24.upnp4j.UPnP4J.getExternalIP(), this.getPort());
|
|
+ }
|
|
+ } else {
|
|
+ this.upnp = false;
|
|
+ LOGGER.error("[UPnP] Service is unavailable");
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
// CraftBukkit start
|
|
// this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up
|
|
@@ -369,6 +403,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
}
|
|
|
|
if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) mobSpawnExecutor.start(); // Pufferfish
|
|
+ org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur
|
|
+ if (org.purpurmc.purpur.PurpurConfig.beeCountPayload) org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur
|
|
return true;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
index c3ec370b83b895be0f03662e3884fa4a2442a2a6..05e16103af3fd276f0196ddf1a2e5b729b025c34 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
@@ -56,6 +56,7 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
|
|
public final boolean onlineMode = this.get("online-mode", true);
|
|
public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false);
|
|
public final String serverIp = this.get("server-ip", "");
|
|
+ public final String serverName = this.get("server-name", "Unknown Server"); // Purpur
|
|
public final boolean pvp = this.get("pvp", true);
|
|
public final boolean allowFlight = this.get("allow-flight", false);
|
|
public final String motd = this.get("motd", "A Minecraft Server");
|
|
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
index 759062d219ff490a3cb19e710c4d18e3e08288e0..8f74c2ec5252b6265549589310d742337c91cb2c 100644
|
|
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
@@ -43,6 +43,11 @@ public class MinecraftServerGui extends JComponent {
|
|
private Thread logAppenderThread;
|
|
private final Collection<Runnable> finalizers = Lists.newArrayList();
|
|
final AtomicBoolean isClosing = new AtomicBoolean();
|
|
+ // Purpur start
|
|
+ private final CommandHistory history = new CommandHistory();
|
|
+ private String currentCommand = "";
|
|
+ private int historyIndex = 0;
|
|
+ // Purpur end
|
|
|
|
public static MinecraftServerGui showFrameFor(final DedicatedServer server) {
|
|
try {
|
|
@@ -51,7 +56,7 @@ public class MinecraftServerGui extends JComponent {
|
|
;
|
|
}
|
|
|
|
- final JFrame jframe = new JFrame("Minecraft server");
|
|
+ final JFrame jframe = new JFrame("Purpur Minecraft server"); // Purpur
|
|
final MinecraftServerGui servergui = new MinecraftServerGui(server);
|
|
|
|
jframe.setDefaultCloseOperation(2);
|
|
@@ -59,7 +64,7 @@ public class MinecraftServerGui extends JComponent {
|
|
jframe.pack();
|
|
jframe.setLocationRelativeTo((Component) null);
|
|
jframe.setVisible(true);
|
|
- jframe.setName("Minecraft server"); // Paper - Improve ServerGUI
|
|
+ jframe.setName("Purpur Minecraft server"); // Paper - Improve ServerGUI // Purpur
|
|
|
|
// Paper start - Improve ServerGUI
|
|
try {
|
|
@@ -71,7 +76,7 @@ public class MinecraftServerGui extends JComponent {
|
|
jframe.addWindowListener(new WindowAdapter() {
|
|
public void windowClosing(WindowEvent windowevent) {
|
|
if (!servergui.isClosing.getAndSet(true)) {
|
|
- jframe.setTitle("Minecraft server - shutting down!");
|
|
+ jframe.setTitle("Purpur Minecraft server - shutting down!"); // Purpur
|
|
server.halt(true);
|
|
servergui.runFinalizers();
|
|
}
|
|
@@ -159,7 +164,7 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
private JComponent buildChatPanel() {
|
|
JPanel jpanel = new JPanel(new BorderLayout());
|
|
- JTextArea jtextarea = new JTextArea();
|
|
+ org.purpurmc.purpur.gui.JColorTextPane jtextarea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur
|
|
JScrollPane jscrollpane = new JScrollPane(jtextarea, 22, 30);
|
|
|
|
jtextarea.setEditable(false);
|
|
@@ -171,10 +176,43 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
if (!s.isEmpty()) {
|
|
this.server.handleConsoleInput(s, this.server.createCommandSourceStack());
|
|
+ // Purpur start
|
|
+ history.add(s);
|
|
+ historyIndex = -1;
|
|
+ // Purpur end
|
|
}
|
|
|
|
jtextfield.setText("");
|
|
});
|
|
+ // Purpur start
|
|
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up");
|
|
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down");
|
|
+ jtextfield.getActionMap().put("up", new javax.swing.AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
|
+ if (historyIndex < 0) {
|
|
+ currentCommand = jtextfield.getText();
|
|
+ }
|
|
+ if (historyIndex < history.size() - 1) {
|
|
+ jtextfield.setText(history.get(++historyIndex));
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ jtextfield.getActionMap().put("down", new javax.swing.AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
|
+ if (historyIndex >= 0) {
|
|
+ if (historyIndex == 0) {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(currentCommand);
|
|
+ } else {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(history.get(historyIndex));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
jtextarea.addFocusListener(new FocusAdapter() { // CraftBukkit - decompile error
|
|
public void focusGained(FocusEvent focusevent) {}
|
|
});
|
|
@@ -210,7 +248,7 @@ public class MinecraftServerGui extends JComponent {
|
|
}
|
|
|
|
private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper
|
|
- public void print(JTextArea textArea, JScrollPane scrollPane, String message) {
|
|
+ public void print(org.purpurmc.purpur.gui.JColorTextPane textArea, JScrollPane scrollPane, String message) { // Purpur
|
|
if (!SwingUtilities.isEventDispatchThread()) {
|
|
SwingUtilities.invokeLater(() -> {
|
|
this.print(textArea, scrollPane, message);
|
|
@@ -224,11 +262,14 @@ public class MinecraftServerGui extends JComponent {
|
|
flag = (double) jscrollbar.getValue() + jscrollbar.getSize().getHeight() + (double) (MinecraftServerGui.MONOSPACED.getSize() * 4) > (double) jscrollbar.getMaximum();
|
|
}
|
|
|
|
+ /* // Purpur
|
|
try {
|
|
document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(message).replaceAll(""), (AttributeSet) null); // CraftBukkit
|
|
} catch (BadLocationException badlocationexception) {
|
|
;
|
|
}
|
|
+ */ // Purpur
|
|
+ textArea.append(message); // Purpur
|
|
|
|
if (flag) {
|
|
jscrollbar.setValue(Integer.MAX_VALUE);
|
|
@@ -236,4 +277,16 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static class CommandHistory extends java.util.LinkedList<String> {
|
|
+ @Override
|
|
+ public boolean add(String command) {
|
|
+ if (size() > 1000) {
|
|
+ remove();
|
|
+ }
|
|
+ return super.offerFirst(command);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
index bf813380d5767ce05cdeca7084e6f19aa106803a..796322fc35da0b47654e60388ec93cae7b999766 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
@@ -81,7 +81,7 @@ public class ServerEntity {
|
|
@Nullable
|
|
private List<SynchedEntityData.DataValue<?>> trackedDataValues;
|
|
// CraftBukkit start
|
|
- private final Set<ServerPlayerConnection> trackedPlayers;
|
|
+ public final Set<ServerPlayerConnection> trackedPlayers; // Purpur - private -> public
|
|
|
|
public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, Set<ServerPlayerConnection> trackedPlayers) {
|
|
this.trackedPlayers = trackedPlayers;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index ea586b6c382ac49ffce0675442cc5dfb5c638628..7d3d42e4fb09ac466cd41df3830fbcffc92d2737 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -221,6 +221,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
private final StructureManager structureManager;
|
|
private final StructureCheck structureCheck;
|
|
private final boolean tickTime;
|
|
+ private double preciseTime; // Purpur
|
|
+ private boolean forceTime; // Purpur
|
|
private final RandomSequences randomSequences;
|
|
|
|
// CraftBukkit start
|
|
@@ -229,6 +231,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
|
|
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
|
|
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
|
|
+ public boolean hasRidableMoveEvent = false; // Purpur
|
|
|
|
public LevelChunk getChunkIfLoaded(int x, int z) {
|
|
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
|
|
@@ -602,7 +605,24 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
// CraftBukkit end
|
|
this.tickTime = flag1;
|
|
this.server = minecraftserver;
|
|
- this.customSpawners = list;
|
|
+ // Purpur start - enable/disable MobSpawners per world
|
|
+ this.customSpawners = Lists.newArrayList();
|
|
+ if (purpurConfig.phantomSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.level.levelgen.PhantomSpawner());
|
|
+ }
|
|
+ if (purpurConfig.patrolSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.level.levelgen.PatrolSpawner());
|
|
+ }
|
|
+ if (purpurConfig.catSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.npc.CatSpawner());
|
|
+ }
|
|
+ if (purpurConfig.villageSiegeSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.ai.village.VillageSiege());
|
|
+ }
|
|
+ if (purpurConfig.villagerTraderSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.npc.WanderingTraderSpawner(iworlddataserver));
|
|
+ }
|
|
+ // Purpur end
|
|
this.serverLevelData = iworlddataserver;
|
|
ChunkGenerator chunkgenerator = worlddimension.generator();
|
|
// CraftBukkit start
|
|
@@ -674,6 +694,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler);
|
|
// Paper end - rewrite chunk system
|
|
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
|
+ this.preciseTime = this.serverLevelData.getDayTime(); // Purpur
|
|
}
|
|
|
|
// Paper start
|
|
@@ -720,7 +741,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
long j;
|
|
|
|
- if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
+ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
// CraftBukkit start
|
|
j = this.levelData.getDayTime() + 24000L;
|
|
TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime());
|
|
@@ -828,6 +849,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.serverLevelData.setGameTime(i);
|
|
this.serverLevelData.getScheduledEvents().tick(this.server, i);
|
|
if (this.serverLevelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
+ // Purpur start
|
|
+ int incrementTicks = isDay() ? this.purpurConfig.daytimeTicks : this.purpurConfig.nighttimeTicks;
|
|
+ if (incrementTicks != 12000) {
|
|
+ this.preciseTime += 12000 / (double) incrementTicks;
|
|
+ this.setDayTime(this.preciseTime);
|
|
+ } else
|
|
+ // Purpur end
|
|
this.setDayTime(this.levelData.getDayTime() + 1L);
|
|
}
|
|
|
|
@@ -836,8 +864,22 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
|
|
public void setDayTime(long timeOfDay) {
|
|
this.serverLevelData.setDayTime(timeOfDay);
|
|
+ // Purpur start
|
|
+ this.preciseTime = timeOfDay;
|
|
+ this.forceTime = false;
|
|
+ }
|
|
+ public void setDayTime(double i) {
|
|
+ this.serverLevelData.setDayTime((long) i);
|
|
+ this.forceTime = true;
|
|
+ // Purpur end
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public boolean isForceTime() {
|
|
+ return this.forceTime;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void tickCustomSpawners(boolean spawnMonsters, boolean spawnAnimals) {
|
|
Iterator iterator = this.customSpawners.iterator();
|
|
|
|
@@ -923,10 +965,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper - Configurable spawn chances for skeleton horses
|
|
|
|
if (flag1) {
|
|
- SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
|
+ // Purpur start
|
|
+ net.minecraft.world.entity.animal.horse.AbstractHorse entityhorseskeleton;
|
|
+ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) {
|
|
+ entityhorseskeleton = EntityType.ZOMBIE_HORSE.create(this, EntitySpawnReason.EVENT);
|
|
+ } else {
|
|
+ entityhorseskeleton = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
|
+ if (entityhorseskeleton != null) ((SkeletonHorse) entityhorseskeleton).setTrap(true);
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
if (entityhorseskeleton != null) {
|
|
- entityhorseskeleton.setTrap(true);
|
|
+ //entityhorseskeleton.setTrap(true); // Purpur - moved up
|
|
entityhorseskeleton.setAge(0);
|
|
entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
|
|
this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
|
|
@@ -1002,7 +1052,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
return holder.is(PoiTypes.LIGHTNING_ROD);
|
|
}, (blockposition1) -> {
|
|
return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1;
|
|
- }, pos, 128, PoiManager.Occupancy.ANY);
|
|
+ }, pos, org.purpurmc.purpur.PurpurConfig.lightningRodRange, PoiManager.Occupancy.ANY);
|
|
|
|
return optional.map((blockposition1) -> {
|
|
return blockposition1.above(1);
|
|
@@ -1051,11 +1101,27 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
if (this.canSleepThroughNights()) {
|
|
if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
- MutableComponent ichatmutablecomponent;
|
|
+ Component ichatmutablecomponent;
|
|
|
|
if (this.sleepStatus.areEnoughSleeping(i)) {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.isBlank()) {
|
|
+ return;
|
|
+ }
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.equalsIgnoreCase("default")) {
|
|
+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepSkippingNight));
|
|
+ } else
|
|
ichatmutablecomponent = Component.translatable("sleep.skipping_night");
|
|
} else {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.isBlank()) {
|
|
+ return;
|
|
+ }
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.equalsIgnoreCase("default")) {
|
|
+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent,
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("count", Integer.toString(this.sleepStatus.amountSleeping())),
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("total", Integer.toString(this.sleepStatus.sleepersNeeded(i)))));
|
|
+ } else
|
|
+ // Purpur end
|
|
ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i));
|
|
}
|
|
|
|
@@ -1195,6 +1261,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
@VisibleForTesting
|
|
public void resetWeatherCycle() {
|
|
// CraftBukkit start
|
|
+ if (this.purpurConfig.rainStopsAfterSleep) // Purpur
|
|
this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents
|
|
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
// Not that everyone ever manages to get the whole server to sleep at the same time....
|
|
@@ -1202,6 +1269,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.serverLevelData.setRainTime(0);
|
|
}
|
|
// CraftBukkit end
|
|
+ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur
|
|
this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents
|
|
// CraftBukkit start
|
|
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
@@ -2741,7 +2809,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
// Spigot Start
|
|
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
|
|
// Paper start - Fix merchant inventory not closing on entity removal
|
|
- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
|
|
+ if (!entity.level().purpurConfig.playerVoidTrading && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Purpur
|
|
merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
|
|
}
|
|
// Paper end - Fix merchant inventory not closing on entity removal
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
index 7e06a80e6deb80df865f7798588a92b88084411b..61a990d17adaa9f4144d8a1010e764ae5b4ee2fa 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -328,6 +328,10 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent
|
|
public @Nullable String clientBrandName = null; // Paper - Brand support
|
|
public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event
|
|
+ public boolean purpurClient = false; // Purpur
|
|
+ private boolean tpsBar = false; // Purpur
|
|
+ private boolean compassBar = false; // Purpur
|
|
+ private boolean ramBar = false; // Purpur
|
|
|
|
// Paper start - rewrite chunk system
|
|
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
|
|
@@ -690,6 +694,9 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
});
|
|
}
|
|
|
|
+ if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur
|
|
+ if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur
|
|
+ if (nbt.contains("Purpur.RamBar")) { this.ramBar = nbt.getBoolean("Purpur.RamBar"); } // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -742,6 +749,9 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
}
|
|
|
|
this.saveEnderPearls(nbt);
|
|
+ nbt.putBoolean("Purpur.RamBar", this.ramBar); // Purpur
|
|
+ nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur
|
|
+ nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur
|
|
}
|
|
|
|
private void saveParentVehicle(CompoundTag nbt) {
|
|
@@ -1031,6 +1041,15 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
this.trackEnteredOrExitedLavaOnVehicle();
|
|
this.updatePlayerAttributes();
|
|
this.advancements.flushDirty(this);
|
|
+
|
|
+ // Purpur start
|
|
+ if (this.level().purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level().getGameTime() % 100 == 0) { // 5 seconds
|
|
+ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION);
|
|
+ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds
|
|
+ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
private void updatePlayerAttributes() {
|
|
@@ -1328,6 +1347,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
}));
|
|
PlayerTeam scoreboardteam = this.getTeam();
|
|
|
|
+ if (org.purpurmc.purpur.PurpurConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(ichatbasecomponent); else // Purpur
|
|
if (scoreboardteam != null && scoreboardteam.getDeathMessageVisibility() != Team.Visibility.ALWAYS) {
|
|
if (scoreboardteam.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) {
|
|
this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent);
|
|
@@ -1431,6 +1451,16 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
if (this.isInvulnerableTo(world, source)) {
|
|
return false;
|
|
} else {
|
|
+ // Purpur start
|
|
+ if (source.is(DamageTypeTags.IS_FALL)) { // Purpur
|
|
+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) {
|
|
+ return false;
|
|
+ }
|
|
+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.Boat && !level().purpurConfig.boatsDoFallDamage) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL);
|
|
|
|
if (!flag && this.spawnInvulnerableTime > 0 && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
|
|
@@ -1658,6 +1688,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
|
|
this.unsetRemoved();
|
|
// CraftBukkit end
|
|
+ this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur - Fix stuck in portals
|
|
this.setServerLevel(worldserver);
|
|
this.connection.internalTeleport(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives()); // CraftBukkit - use internal teleport without event
|
|
this.connection.resetPosition();
|
|
@@ -1767,7 +1798,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
return entitymonster.isPreventingPlayerRest(this.serverLevel(), this);
|
|
});
|
|
|
|
- if (!list.isEmpty()) {
|
|
+ if (!this.level().purpurConfig.playerSleepNearMonsters && !list.isEmpty()) { // Purpur
|
|
return Either.left(net.minecraft.world.entity.player.Player.BedSleepingProblem.NOT_SAFE);
|
|
}
|
|
}
|
|
@@ -1807,7 +1838,19 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
});
|
|
|
|
if (!this.serverLevel().canSleepThroughNights()) {
|
|
- this.displayClientMessage(Component.translatable("sleep.not_possible"), true);
|
|
+ // Purpur start
|
|
+ Component clientMessage;
|
|
+ if (org.purpurmc.purpur.PurpurConfig.sleepNotPossible.isBlank()) {
|
|
+ clientMessage = null;
|
|
+ } else if (!org.purpurmc.purpur.PurpurConfig.sleepNotPossible.equalsIgnoreCase("default")) {
|
|
+ clientMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepNotPossible));
|
|
+ } else {
|
|
+ clientMessage = Component.translatable("sleep.not_possible");
|
|
+ }
|
|
+ if (clientMessage != null) {
|
|
+ this.displayClientMessage(clientMessage, true);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
((ServerLevel) this.level()).updateSleepingPlayerList();
|
|
@@ -1929,6 +1972,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
|
|
@Override
|
|
public void openTextEdit(SignBlockEntity sign, boolean front) {
|
|
+ if (level().purpurConfig.signAllowColors) this.connection.send(sign.getTranslatedUpdatePacket(textFilteringEnabled, front)); // Purpur
|
|
this.connection.send(new ClientboundBlockUpdatePacket(this.level(), sign.getBlockPos()));
|
|
this.connection.send(new ClientboundOpenSignEditorPacket(sign.getBlockPos(), front));
|
|
}
|
|
@@ -2245,6 +2289,26 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
this.lastSentExp = -1; // CraftBukkit - Added to reset
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void sendActionBarMessage(@Nullable String message) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendActionBarMessage(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ if (message != null) {
|
|
+ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendActionBarMessage(@Nullable Component message) {
|
|
+ if (message != null) {
|
|
+ displayClientMessage(message, true);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void displayClientMessage(Component message, boolean overlay) {
|
|
this.sendSystemMessage(message, overlay);
|
|
@@ -2475,6 +2539,20 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
return new CommandSourceStack(this.commandSource(), this.position(), this.getRotationVector(), this.serverLevel(), this.getPermissionLevel(), this.getName().getString(), this.getDisplayName(), this.server, this);
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ public void sendMiniMessage(@Nullable String message) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendMessage(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ if (message != null) {
|
|
+ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void sendSystemMessage(Component message) {
|
|
this.sendSystemMessage(message, false);
|
|
}
|
|
@@ -2596,8 +2674,68 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
|
|
public void resetLastActionTime() {
|
|
this.lastActionTime = Util.getMillis();
|
|
+ this.setAfk(false); // Purpur
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ private boolean isAfk = false;
|
|
+
|
|
+ @Override
|
|
+ public void setAfk(boolean afk) {
|
|
+ if (this.isAfk == afk) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ String msg = afk ? org.purpurmc.purpur.PurpurConfig.afkBroadcastAway : org.purpurmc.purpur.PurpurConfig.afkBroadcastBack;
|
|
+
|
|
+ org.purpurmc.purpur.event.PlayerAFKEvent event = new org.purpurmc.purpur.event.PlayerAFKEvent(this.getBukkitEntity(), afk, this.level().purpurConfig.idleTimeoutKick, msg, !Bukkit.isPrimaryThread());
|
|
+ if (!event.callEvent() || event.shouldKick()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ this.isAfk = afk;
|
|
+
|
|
+ if (!afk) {
|
|
+ resetLastActionTime();
|
|
+ }
|
|
+
|
|
+ msg = event.getBroadcastMsg();
|
|
+ if (msg != null && !msg.isEmpty()) {
|
|
+ String playerName = this.getGameProfile().getName();
|
|
+ if (org.purpurmc.purpur.PurpurConfig.afkBroadcastUseDisplayName) {
|
|
+ net.kyori.adventure.text.Component playerDisplayNameComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getBukkitEntity().getDisplayName());
|
|
+ playerName = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(playerDisplayNameComponent);
|
|
+ }
|
|
+ server.getPlayerList().broadcastMiniMessage(String.format(msg, playerName), false);
|
|
+ }
|
|
+
|
|
+ if (this.level().purpurConfig.idleTimeoutUpdateTabList) {
|
|
+ String scoreboardName = getScoreboardName();
|
|
+ String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName());
|
|
+ String[] split = playerListName.split(scoreboardName);
|
|
+ String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, "");
|
|
+ String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, "");
|
|
+ if (afk) {
|
|
+ getBukkitEntity().setPlayerListName(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix + prefix + scoreboardName + suffix + org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, true);
|
|
+ } else {
|
|
+ getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix, true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ((ServerLevel) this.level()).updateSleepingPlayerList();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isAfk() {
|
|
+ return this.isAfk;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canBeCollidedWith() {
|
|
+ return !this.isAfk() && super.canBeCollidedWith();
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
public ServerStatsCounter getStats() {
|
|
return this.stats;
|
|
}
|
|
@@ -3304,4 +3442,50 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
return (CraftPlayer) super.getBukkitEntity();
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start
|
|
+ public void teleport(Location to) {
|
|
+ this.ejectPassengers();
|
|
+ this.stopRiding(true);
|
|
+
|
|
+ if (this.isSleeping()) {
|
|
+ this.stopSleepInBed(true, false);
|
|
+ }
|
|
+
|
|
+ if (this.containerMenu != this.inventoryMenu) {
|
|
+ this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT);
|
|
+ }
|
|
+
|
|
+ ServerLevel toLevel = ((CraftWorld) to.getWorld()).getHandle();
|
|
+ if (this.level() == toLevel) {
|
|
+ this.connection.teleport(to);
|
|
+ } else {
|
|
+ this.server.getPlayerList().respawn(this, true, RemovalReason.KILLED, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH, to);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean tpsBar() {
|
|
+ return this.tpsBar;
|
|
+ }
|
|
+
|
|
+ public void tpsBar(boolean tpsBar) {
|
|
+ this.tpsBar = tpsBar;
|
|
+ }
|
|
+
|
|
+ public boolean compassBar() {
|
|
+ return this.compassBar;
|
|
+ }
|
|
+
|
|
+ public void compassBar(boolean compassBar) {
|
|
+ this.compassBar = compassBar;
|
|
+ }
|
|
+
|
|
+ public boolean ramBar() {
|
|
+ return this.ramBar;
|
|
+ }
|
|
+
|
|
+ public void ramBar(boolean ramBar) {
|
|
+ this.ramBar = ramBar;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
index a96f859a5d0c6ec692d4627a69f3c9ee49199dbc..88eb3774f688bcff383efa7f113bd0b1b97d8a11 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
@@ -403,6 +403,7 @@ public class ServerPlayerGameMode {
|
|
} else {capturedBlockEntity = true;} // Paper - Send block entities after destroy prediction
|
|
return false;
|
|
}
|
|
+ if (this.player.level().purpurConfig.slabHalfBreak && this.player.isShiftKeyDown() && iblockdata.getBlock() instanceof net.minecraft.world.level.block.SlabBlock && ((net.minecraft.world.level.block.SlabBlock) iblockdata.getBlock()).halfBreak(iblockdata, pos, this.player)) return true; // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@@ -523,6 +524,7 @@ public class ServerPlayerGameMode {
|
|
public InteractionHand interactHand;
|
|
public ItemStack interactItemStack;
|
|
public InteractionResult useItemOn(ServerPlayer player, Level world, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) {
|
|
+ if (shiftClickMended(stack)) return InteractionResult.SUCCESS; // Purpur
|
|
BlockPos blockposition = hitResult.getBlockPos();
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
boolean cancelledBlock = false;
|
|
@@ -583,7 +585,7 @@ public class ServerPlayerGameMode {
|
|
ItemStack itemstack1 = stack.copy();
|
|
InteractionResult enuminteractionresult;
|
|
|
|
- if (!flag1) {
|
|
+ if (!flag1 || (player.level().purpurConfig.composterBulkProcess && iblockdata.is(Blocks.COMPOSTER))) { // Purpur
|
|
InteractionResult enuminteractionresult1 = iblockdata.useItemOn(player.getItemInHand(hand), world, player, hand, hitResult);
|
|
|
|
if (enuminteractionresult1.consumesAction()) {
|
|
@@ -631,4 +633,18 @@ public class ServerPlayerGameMode {
|
|
public void setLevel(ServerLevel world) {
|
|
this.level = world;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean shiftClickMended(ItemStack itemstack) {
|
|
+ if (this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints > 0 && this.player.isShiftKeyDown() && this.player.getBukkitEntity().hasPermission("purpur.mending_shift_click")) {
|
|
+ int points = Math.min(this.player.totalExperience, this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints);
|
|
+ if (points > 0 && itemstack.isDamaged() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MENDING, itemstack) > 0) {
|
|
+ this.player.giveExperiencePoints(-points);
|
|
+ this.player.level().addFreshEntity(new net.minecraft.world.entity.ExperienceOrb(this.player.level(), this.player.getX(), this.player.getY(), this.player.getZ(), points, org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN, this.player, this.player));
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
|
index 92815589da3c2a1cb768ac8081660c9c2ccb2b14..32ab2e0f7bae7d0a54cebdd46c95e574c41ad1e3 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
|
@@ -88,6 +88,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
|
private static final long KEEPALIVE_LIMIT = KEEPALIVE_LIMIT_IN_SECONDS * 1000;
|
|
// Gale end - Purpur - send multiple keep-alive packets
|
|
protected static final ResourceLocation MINECRAFT_BRAND = ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support
|
|
+ protected static final ResourceLocation PURPUR_CLIENT = ResourceLocation.fromNamespaceAndPath("purpur", "client"); // Purpur
|
|
|
|
public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) { // CraftBukkit
|
|
this.server = minecraftserver;
|
|
@@ -193,6 +194,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
|
ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex);
|
|
this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
|
|
}
|
|
+ // Purpur start
|
|
+ } else if (identifier.equals(PURPUR_CLIENT)) {
|
|
+ try {
|
|
+ player.purpurClient = true;
|
|
+ } catch (Exception ignore) {
|
|
+ }
|
|
+ // Purpur end
|
|
} else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) {
|
|
try {
|
|
String channels = payload.toString(com.google.common.base.Charsets.UTF_8);
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index 3455b7f21fee42d21182b8b5f8af7a9a238f646c..eb8ea4df3996a22d46a6fdc1df2fa1f26aa95aec 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -342,6 +342,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
private boolean justTeleported = false;
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ private final com.google.common.cache.LoadingCache<CraftPlayer, Boolean> kickPermissionCache = com.google.common.cache.CacheBuilder.newBuilder()
|
|
+ .maximumSize(1000)
|
|
+ .expireAfterWrite(1, java.util.concurrent.TimeUnit.MINUTES)
|
|
+ .build(
|
|
+ new com.google.common.cache.CacheLoader<>() {
|
|
+ @Override
|
|
+ public Boolean load(CraftPlayer player) {
|
|
+ return player.hasPermission("purpur.bypassIdleKick");
|
|
+ }
|
|
+ }
|
|
+ );
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void tick() {
|
|
if (this.ackBlockChangesUpTo > -1) {
|
|
@@ -398,6 +412,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
this.recipeSpamPackets.tick(); // Paper - auto recipe limit
|
|
this.dropSpamThrottler.tick();
|
|
if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits
|
|
+ // Purpur start
|
|
+ this.player.setAfk(true);
|
|
+ if (!this.player.level().purpurConfig.idleTimeoutKick || (!Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")) && kickPermissionCache.getUnchecked(this.player.getBukkitEntity()))) {
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
|
|
this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
|
|
}
|
|
@@ -663,6 +683,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
this.lastYaw = to.getYaw();
|
|
this.lastPitch = to.getPitch();
|
|
|
|
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
|
|
+
|
|
Location oldTo = to.clone();
|
|
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
|
|
this.cserver.getPluginManager().callEvent(event);
|
|
@@ -739,6 +761,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
|
if (packet.getId() == this.awaitingTeleport) {
|
|
if (this.awaitingPositionFromClient == null) {
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn("Disconnected on accept teleport packet. Was not expecting position data from client at this time"); // Purpur
|
|
this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
|
|
return;
|
|
}
|
|
@@ -1185,6 +1208,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
final int maxBookPageSize = pageMax.intValue();
|
|
final double multiplier = Math.clamp(io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier, 0.3D, 1D);
|
|
long byteAllowed = maxBookPageSize;
|
|
+ // Purpur start
|
|
+ int slot = packet.slot();
|
|
+ ItemStack itemstack = Inventory.isHotbarSlot(slot) || slot == Inventory.SLOT_OFFHAND ? this.player.getInventory().getItem(slot) : ItemStack.EMPTY;
|
|
+ // Purpur end
|
|
for (final String page : pageList) {
|
|
final int byteLength = page.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
|
|
byteTotal += byteLength;
|
|
@@ -1209,7 +1236,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
}
|
|
|
|
if (byteTotal > byteAllowed) {
|
|
- ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size());
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send too large of a book. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size());
|
|
+ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur
|
|
this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
|
|
return;
|
|
}
|
|
@@ -1231,10 +1259,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
Objects.requireNonNull(list);
|
|
optional.ifPresent(list::add);
|
|
list.addAll(packet.pages());
|
|
+ // Purpur start
|
|
+ boolean hasEditPerm = getCraftPlayer().hasPermission("purpur.book.color.edit");
|
|
+ boolean hasSignPerm = hasEditPerm || getCraftPlayer().hasPermission("purpur.book.color.sign");
|
|
+ // Purpur end
|
|
Consumer<List<FilteredText>> consumer = optional.isPresent() ? (list1) -> {
|
|
- this.signBook((FilteredText) list1.get(0), list1.subList(1, list1.size()), i);
|
|
+ this.signBook((FilteredText) list1.get(0), list1.subList(1, list1.size()), i, hasSignPerm); // Purpur
|
|
} : (list1) -> {
|
|
- this.updateBookContents(list1, i);
|
|
+ this.updateBookContents(list1, i, hasEditPerm); // Purpur
|
|
};
|
|
|
|
this.filterTextPacket((List) list).thenAcceptAsync(consumer, this.server);
|
|
@@ -1242,13 +1274,18 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
}
|
|
|
|
private void updateBookContents(List<FilteredText> pages, int slotId) {
|
|
+ // Purpur start
|
|
+ updateBookContents(pages, slotId, false);
|
|
+ }
|
|
+ private void updateBookContents(List<FilteredText> pages, int slotId, boolean hasPerm) {
|
|
+ // Purpur end
|
|
// CraftBukkit start
|
|
ItemStack handItem = this.player.getInventory().getItem(slotId);
|
|
ItemStack itemstack = handItem.copy();
|
|
// CraftBukkit end
|
|
|
|
if (itemstack.has(DataComponents.WRITABLE_BOOK_CONTENT)) {
|
|
- List<Filterable<String>> list1 = pages.stream().map(this::filterableFromOutgoing).toList();
|
|
+ List<Filterable<String>> list1 = pages.stream().map(filteredText -> filterableFromOutgoing(filteredText).map(s -> color(s, hasPerm))).toList(); // Purpur
|
|
|
|
itemstack.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list1));
|
|
this.player.getInventory().setItem(slotId, CraftEventFactory.handleEditBookEvent(this.player, slotId, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent)
|
|
@@ -1256,6 +1293,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
}
|
|
|
|
private void signBook(FilteredText title, List<FilteredText> pages, int slotId) {
|
|
+ // Purpur start
|
|
+ signBook(title, pages, slotId, false);
|
|
+ }
|
|
+ private void signBook(FilteredText title, List<FilteredText> pages, int slotId, boolean hasPerm) {
|
|
+ // Purpur end
|
|
ItemStack itemstack = this.player.getInventory().getItem(slotId);
|
|
|
|
if (itemstack.has(DataComponents.WRITABLE_BOOK_CONTENT)) {
|
|
@@ -1263,10 +1305,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
|
|
itemstack1.remove(DataComponents.WRITABLE_BOOK_CONTENT);
|
|
List<Filterable<Component>> list1 = (List<Filterable<Component>>) (List) pages.stream().map((filteredtext1) -> { // CraftBukkit - decompile error
|
|
- return this.filterableFromOutgoing(filteredtext1).map(Component::literal);
|
|
+ return this.filterableFromOutgoing(filteredtext1).map(s -> hexColor(s, hasPerm)); // Purpur
|
|
}).toList();
|
|
|
|
- itemstack1.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getName().getString(), 0, list1, true));
|
|
+ itemstack1.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title).map(s -> color(s, hasPerm)), this.player.getName().getString(), 0, list1, true)); // Purpur
|
|
CraftEventFactory.handleEditBookEvent(this.player, slotId, itemstack, itemstack1); // CraftBukkit
|
|
this.player.getInventory().setItem(slotId, itemstack); // CraftBukkit - event factory updates the hand book
|
|
}
|
|
@@ -1276,6 +1318,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
return this.player.isTextFilteringEnabled() ? Filterable.passThrough(message.filteredOrEmpty()) : Filterable.from(message);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private Component hexColor(String str, boolean hasPerm) {
|
|
+ return hasPerm ? PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().deserialize(str)) : Component.literal(str);
|
|
+ }
|
|
+
|
|
+ private String color(String str, boolean hasPerm) {
|
|
+ return hasPerm ? org.bukkit.ChatColor.color(str, false) : str;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void handleEntityTagQuery(ServerboundEntityTagQueryPacket packet) {
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
|
@@ -1325,7 +1377,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
@Override
|
|
public void handleMovePlayer(ServerboundMovePlayerPacket packet) {
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
|
- if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) {
|
|
+ // Purpur start
|
|
+ boolean invalidX = Double.isNaN(packet.getX(0.0D));
|
|
+ boolean invalidY = Double.isNaN(packet.getY(0.0D));
|
|
+ boolean invalidZ = Double.isNaN(packet.getZ(0.0D));
|
|
+ boolean invalidYaw = !Floats.isFinite(packet.getYRot(0.0F));
|
|
+ boolean invalidPitch = !Floats.isFinite(packet.getXRot(0.0F));
|
|
+ if (invalidX || invalidY || invalidZ || invalidYaw || invalidPitch) {
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn(String.format("Disconnected on move player packet. Invalid data: x=%b, y=%b, z=%b, yaw=%b, pitch=%b", invalidX, invalidY, invalidZ, invalidYaw, invalidPitch)); // Purpur
|
|
+ // Purpur end
|
|
this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
|
|
} else {
|
|
ServerLevel worldserver = this.player.serverLevel();
|
|
@@ -1505,7 +1565,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
movedWrongly = true;
|
|
if (event.getLogWarning())
|
|
// Paper end
|
|
- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur
|
|
} // Paper
|
|
}
|
|
|
|
@@ -1573,6 +1633,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
this.lastYaw = to.getYaw();
|
|
this.lastPitch = to.getPitch();
|
|
|
|
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
|
|
+
|
|
Location oldTo = to.clone();
|
|
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
|
|
this.cserver.getPluginManager().callEvent(event);
|
|
@@ -1618,6 +1680,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
this.player.tryResetCurrentImpulseContext();
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ if (this.player.level().purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.level().purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.level().purpurConfig.ignoreScissorsInLava && this.player.isInLava()) && (isScissor(this.player.getItemInHand(InteractionHand.MAIN_HAND)) || isScissor(this.player.getItemInHand(InteractionHand.OFF_HAND))) && (int) (Math.random() * 10) == 0) {
|
|
+ this.player.hurt(this.player.damageSources().scissors(), (float) this.player.level().purpurConfig.scissorsRunningDamage);
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.dontRunWithScissors.isBlank()) this.player.sendActionBarMessage(org.purpurmc.purpur.PurpurConfig.dontRunWithScissors);
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
this.player.checkMovementStatistics(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5);
|
|
this.lastGoodX = this.player.getX();
|
|
this.lastGoodY = this.player.getY();
|
|
@@ -1657,6 +1726,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public boolean isScissor(ItemStack stack) {
|
|
+ if (!stack.is(Items.SHEARS)) return false;
|
|
+ net.minecraft.world.item.component.CustomModelData customModelData = stack.get(net.minecraft.core.component.DataComponents.CUSTOM_MODEL_DATA);
|
|
+ return customModelData == null || customModelData.value() == 0;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - optimise out extra getCubes
|
|
private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) {
|
|
final List<AABB> collisionsBB = new java.util.ArrayList<>();
|
|
@@ -2030,6 +2107,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
|
|
boolean cancelled;
|
|
if (movingobjectposition == null || movingobjectposition.getType() != HitResult.Type.BLOCK) {
|
|
+ if (this.player.gameMode.shiftClickMended(itemstack)) return; // Purpur
|
|
org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack, enumhand);
|
|
cancelled = event.useItemInHand() == Event.Result.DENY;
|
|
} else {
|
|
@@ -2813,6 +2891,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
AABB axisalignedbb = entity.getBoundingBox();
|
|
|
|
if (this.player.canInteractWithEntity(axisalignedbb, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0D))) { // Paper - configurable lenience value for interact range
|
|
+ if (entity instanceof Mob mob) mob.ticksSinceLastInteraction = 0; // Purpur
|
|
packet.dispatch(new ServerboundInteractPacket.Handler() {
|
|
private void performInteraction(InteractionHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
|
|
ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand);
|
|
@@ -2826,6 +2905,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
|
|
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
|
|
|
|
+ player.processClick(enumhand); // Purpur
|
|
+
|
|
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
|
|
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
|
|
entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
index 70534d9d58a209c0c8fd6f4d3ba65773f420f559..d5acf191e3f34c84b3711c0ba77e7cb001aee507 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
@@ -335,7 +335,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!");
|
|
ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(s1)); // Spigot
|
|
} else {
|
|
- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username"));
|
|
+ ServerLoginPacketListenerImpl.this.disconnect(org.purpurmc.purpur.PurpurConfig.unverifiedUsername.equals("default") ? Component.translatable("multiplayer.disconnect.unverified_username") : io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.unverifiedUsername))); // Purpur
|
|
ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", s1);
|
|
}
|
|
} catch (AuthenticationUnavailableException authenticationunavailableexception) {
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 99521abc925e0c7754d9e5aaad52e4fa70383946..3d6a728c6f9542109e7466d590bb8f015b8c4ac1 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -430,6 +430,7 @@ public abstract class PlayerList {
|
|
scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam);
|
|
}
|
|
// Paper end - Configurable player collision
|
|
+ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur
|
|
if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.playerLoginLocations) { // Gale - JettPack - make logging login location configurable
|
|
PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ());
|
|
// Gale start - JettPack - make logging login location configurable
|
|
@@ -557,6 +558,7 @@ public abstract class PlayerList {
|
|
}
|
|
public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) {
|
|
// Paper end - Fix kick event leave message not being sent
|
|
+ org.purpurmc.purpur.task.BossBarTask.removeFromAll(entityplayer.getBukkitEntity()); // Purpur
|
|
ServerLevel worldserver = entityplayer.serverLevel();
|
|
|
|
entityplayer.awardStat(Stats.LEAVE_GAME);
|
|
@@ -727,7 +729,7 @@ public abstract class PlayerList {
|
|
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure
|
|
} else {
|
|
// return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile) ? IChatBaseComponent.translatable("multiplayer.disconnect.server_full") : null;
|
|
- if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile)) {
|
|
+ if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameprofile))) { // Purpur
|
|
event.disallow(PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure
|
|
}
|
|
}
|
|
@@ -1050,6 +1052,20 @@ public abstract class PlayerList {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur Start
|
|
+ public void broadcastMiniMessage(@Nullable String message, boolean overlay) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) {
|
|
+ if (message != null) {
|
|
+ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void broadcastAll(Packet<?> packet, ResourceKey<Level> dimension) {
|
|
Iterator iterator = this.players.iterator();
|
|
|
|
@@ -1153,6 +1169,7 @@ public abstract class PlayerList {
|
|
} else {
|
|
b0 = (byte) (24 + permissionLevel);
|
|
}
|
|
+ if (b0 < 28 && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b0 = 28; // Purpur
|
|
|
|
player.connection.send(new ClientboundEntityEventPacket(player, b0));
|
|
}
|
|
@@ -1161,6 +1178,27 @@ public abstract class PlayerList {
|
|
player.getBukkitEntity().recalculatePermissions(); // CraftBukkit
|
|
this.server.getCommands().sendCommands(player);
|
|
} // Paper - Add sendOpLevel API
|
|
+
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkit = player.getBukkitEntity();
|
|
+ if (bukkit.hasPermission("purpur.enderchest.rows.six")) {
|
|
+ player.sixRowEnderchestSlotCount = 54;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.five")) {
|
|
+ player.sixRowEnderchestSlotCount = 45;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.four")) {
|
|
+ player.sixRowEnderchestSlotCount = 36;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.three")) {
|
|
+ player.sixRowEnderchestSlotCount = 27;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.two")) {
|
|
+ player.sixRowEnderchestSlotCount = 18;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.one")) {
|
|
+ player.sixRowEnderchestSlotCount = 9;
|
|
+ }
|
|
+ } else {
|
|
+ player.sixRowEnderchestSlotCount = -1;
|
|
+ }
|
|
+ //Purpur end
|
|
}
|
|
|
|
public boolean isWhiteListed(GameProfile profile) {
|
|
diff --git a/src/main/java/net/minecraft/server/players/SleepStatus.java b/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
index 823efad652d8ff9e96b99375b102fef6f017716e..caa8a69bde0c212c36dd990a67836ac2f95548c0 100644
|
|
--- a/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
+++ b/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
@@ -19,7 +19,7 @@ public class SleepStatus {
|
|
|
|
public boolean areEnoughDeepSleeping(int percentage, List<ServerPlayer> players) {
|
|
// CraftBukkit start
|
|
- int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping; }).count();
|
|
+ int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping || (eh.level().purpurConfig.idleTimeoutCountAsSleeping && eh.isAfk()); }).count(); // Purpur
|
|
boolean anyDeepSleep = players.stream().anyMatch(Player::isSleepingLongEnough);
|
|
|
|
return anyDeepSleep && j >= this.sleepersNeeded(percentage);
|
|
@@ -52,7 +52,7 @@ public class SleepStatus {
|
|
|
|
if (!entityplayer.isSpectator()) {
|
|
++this.activePlayers;
|
|
- if (entityplayer.isSleeping() || entityplayer.fauxSleeping) { // CraftBukkit
|
|
+ if ((entityplayer.isSleeping() || entityplayer.fauxSleeping) || (entityplayer.level().purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk())) { // CraftBukkit // Purpur
|
|
++this.sleepingPlayers;
|
|
}
|
|
// CraftBukkit start
|
|
diff --git a/src/main/java/net/minecraft/util/StringUtil.java b/src/main/java/net/minecraft/util/StringUtil.java
|
|
index 6c33002dc8bbb3759c3156302ab7d1f26ce5e8ee..c89fc375aff548a2b03eaf4da3b6a075012df012 100644
|
|
--- a/src/main/java/net/minecraft/util/StringUtil.java
|
|
+++ b/src/main/java/net/minecraft/util/StringUtil.java
|
|
@@ -69,6 +69,7 @@ public class StringUtil {
|
|
|
|
// Paper start - Username validation
|
|
public static boolean isReasonablePlayerName(final String name) {
|
|
+ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches(); // Purpur
|
|
if (name.isEmpty() || name.length() > 16) {
|
|
return false;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/CombatRules.java b/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
index 064c1e33f3feee77837bb57887877ae1ca39548d..ffd009bca3fdbfd0b14df78072ef8d472a57cd65 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
@@ -15,7 +15,7 @@ public class CombatRules {
|
|
|
|
public static float getDamageAfterAbsorb(LivingEntity armorWearer, float damageAmount, DamageSource damageSource, float armor, float armorToughness) {
|
|
float f = 2.0F + armorToughness / 4.0F;
|
|
- float g = Mth.clamp(armor - damageAmount / f, armor * 0.2F, 20.0F);
|
|
+ float g = Mth.clamp(armor - damageAmount / f, armor * 0.2F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur
|
|
float h = g / 25.0F;
|
|
ItemStack itemStack = damageSource.getWeaponItem();
|
|
float i;
|
|
@@ -30,7 +30,7 @@ public class CombatRules {
|
|
}
|
|
|
|
public static float getDamageAfterMagicAbsorb(float damageDealt, float protection) {
|
|
- float f = Mth.clamp(protection, 0.0F, 20.0F);
|
|
+ float f = Mth.clamp(protection, 0.0F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur
|
|
return damageDealt * (1.0F - f / 25.0F);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java
|
|
index 99a7e9eb75231c15bd8bb24fbb4e296bc9fdedff..4fb025a63628eb60509d90b680922a0220104bcb 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java
|
|
@@ -54,7 +54,7 @@ public class CombatTracker {
|
|
|
|
private Component getMessageForAssistedFall(Entity attacker, Component attackerDisplayName, String itemDeathTranslationKey, String deathTranslationKey) {
|
|
ItemStack itemStack = attacker instanceof LivingEntity livingEntity ? livingEntity.getMainHandItem() : ItemStack.EMPTY;
|
|
- return !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME)
|
|
+ return !itemStack.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemStack.has(DataComponents.CUSTOM_NAME)) // Purpur
|
|
? Component.translatable(itemDeathTranslationKey, this.mob.getDisplayName(), attackerDisplayName, itemStack.getDisplayName())
|
|
: Component.translatable(deathTranslationKey, this.mob.getDisplayName(), attackerDisplayName);
|
|
}
|
|
@@ -98,6 +98,13 @@ public class CombatTracker {
|
|
Component component = ComponentUtils.wrapInSquareBrackets(Component.translatable(string + ".link")).withStyle(INTENTIONAL_GAME_DESIGN_STYLE);
|
|
return Component.translatable(string + ".message", this.mob.getDisplayName(), component);
|
|
} else {
|
|
+ // Purpur start
|
|
+ if (damageSource.isScissors()) {
|
|
+ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgRunWithScissors, this.mob);
|
|
+ } else if (damageSource.isStonecutter()) {
|
|
+ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgStonecutter, this.mob);
|
|
+ }
|
|
+ // Purpur end
|
|
return damageSource.getLocalizedDeathMessage(this.mob);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
index 379b36944ddb149e2f48221aa39ad090bbd2faeb..91a5be2eaedb0fa94580de60a9625f94ae724235 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
@@ -29,6 +29,8 @@ public class DamageSource {
|
|
private boolean sweep = false;
|
|
private boolean melting = false;
|
|
private boolean poison = false;
|
|
+ private boolean scissors = false; // Purpur
|
|
+ private boolean stonecutter = false; // Purpur
|
|
@Nullable
|
|
private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API
|
|
|
|
@@ -59,6 +61,26 @@ public class DamageSource {
|
|
return this.poison;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public DamageSource scissors() {
|
|
+ this.scissors = true;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public boolean isScissors() {
|
|
+ return this.scissors;
|
|
+ }
|
|
+
|
|
+ public DamageSource stonecutter() {
|
|
+ this.stonecutter = true;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public boolean isStonecutter() {
|
|
+ return this.stonecutter;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - fix DamageSource API
|
|
@Nullable
|
|
public Entity getCustomEventDamager() {
|
|
@@ -117,6 +139,8 @@ public class DamageSource {
|
|
damageSource.sweep = this.isSweep();
|
|
damageSource.poison = this.isPoison();
|
|
damageSource.melting = this.isMelting();
|
|
+ damageSource.scissors = this.isScissors(); // Purpur
|
|
+ damageSource.stonecutter = this.isStonecutter(); // Purpur
|
|
return damageSource;
|
|
}
|
|
// CraftBukkit end
|
|
@@ -194,10 +218,19 @@ public class DamageSource {
|
|
|
|
ItemStack itemstack1 = itemstack;
|
|
|
|
- return !itemstack1.isEmpty() && itemstack1.has(DataComponents.CUSTOM_NAME) ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent);
|
|
+ return !itemstack1.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemstack1.has(DataComponents.CUSTOM_NAME)) ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent);
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public Component getLocalizedDeathMessage(String str, LivingEntity entity) {
|
|
+ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName());
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name);
|
|
+ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template);
|
|
+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public String getMsgId() {
|
|
return this.type().msgId();
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java
|
|
index be87cb3cfa15a7d889118cdc4b87232e30749023..d343fd5c9f31073f1b3a0f91b8ea61a67077cc12 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java
|
|
@@ -46,11 +46,15 @@ public class DamageSources {
|
|
// CraftBukkit start
|
|
private final DamageSource melting;
|
|
private final DamageSource poison;
|
|
+ private final DamageSource scissors; // Purpur
|
|
+ private final DamageSource stonecutter; // Purpur
|
|
|
|
public DamageSources(RegistryAccess registryManager) {
|
|
this.damageTypes = registryManager.lookupOrThrow(Registries.DAMAGE_TYPE);
|
|
this.melting = this.source(DamageTypes.ON_FIRE).melting();
|
|
this.poison = this.source(DamageTypes.MAGIC).poison();
|
|
+ this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur
|
|
+ this.stonecutter = this.source(DamageTypes.MAGIC).stonecutter(); // Purpur
|
|
// CraftBukkit end
|
|
this.inFire = this.source(DamageTypes.IN_FIRE);
|
|
this.campfire = this.source(DamageTypes.CAMPFIRE);
|
|
@@ -101,6 +105,15 @@ public class DamageSources {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ public DamageSource scissors() {
|
|
+ return this.scissors;
|
|
+ }
|
|
+ public DamageSource stonecutter() {
|
|
+ return this.stonecutter;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public DamageSource inFire() {
|
|
return this.inFire;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/effect/HungerMobEffect.java b/src/main/java/net/minecraft/world/effect/HungerMobEffect.java
|
|
index 11d1ee8fae7670f02cb3f5d57f4774dbde77f48c..88cf1353892a7ead4e0f16822216b151726ac0e4 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/HungerMobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/HungerMobEffect.java
|
|
@@ -13,7 +13,7 @@ class HungerMobEffect extends MobEffect {
|
|
@Override
|
|
public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) {
|
|
if (entity instanceof Player entityhuman) {
|
|
- entityhuman.causeFoodExhaustion(0.005F * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent
|
|
+ entityhuman.causeFoodExhaustion(entity.level().purpurConfig.humanHungerExhaustionAmount * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent // Purpur
|
|
}
|
|
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java b/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java
|
|
index 83c6d17f75c3f0b531bdfd5b5f9bc2a5b3eb7a2c..df43c007e7b9fee58b2cd0892b966ce88cb1f890 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java
|
|
@@ -11,8 +11,8 @@ class PoisonMobEffect extends MobEffect {
|
|
|
|
@Override
|
|
public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) {
|
|
- if (entity.getHealth() > 1.0F) {
|
|
- entity.hurtServer(world, entity.damageSources().poison(), 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON
|
|
+ if (entity.getHealth() > entity.level().purpurConfig.entityMinimalHealthPoison) { // Purpur
|
|
+ entity.hurtServer(world, entity.damageSources().poison(), entity.level().purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON // Purpur
|
|
}
|
|
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java b/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java
|
|
index b43e573e91d1f1b407774bb2d60ee5f099f171e7..25aa932fc03eeebc5aabca6c5eae0cbfc8ad8396 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java
|
|
@@ -12,7 +12,7 @@ class RegenerationMobEffect extends MobEffect {
|
|
@Override
|
|
public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) {
|
|
if (entity.getHealth() < entity.getMaxHealth()) {
|
|
- entity.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit
|
|
+ entity.heal(entity.level().purpurConfig.entityHealthRegenAmount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit // Purpur
|
|
}
|
|
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java
|
|
index 98b74649a667fb9b10afef0ba5383a73022d8c71..0c7c0524e487ff32e16dd9939d92bc6441602747 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java
|
|
@@ -21,7 +21,8 @@ class SaturationMobEffect extends InstantenousMobEffect {
|
|
int oldFoodLevel = entityhuman.getFoodData().foodLevel;
|
|
org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, amplifier + 1 + oldFoodLevel);
|
|
if (!event.isCancelled()) {
|
|
- entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F);
|
|
+ if (entityhuman.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) entityhuman.burpDelay = entityhuman.level().purpurConfig.playerBurpDelay; // Purpur
|
|
+ entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level().purpurConfig.humanSaturationRegenAmount); // Purpur
|
|
}
|
|
|
|
((CraftPlayer) entityhuman.getBukkitEntity()).sendHealthUpdate();
|
|
diff --git a/src/main/java/net/minecraft/world/effect/WitherMobEffect.java b/src/main/java/net/minecraft/world/effect/WitherMobEffect.java
|
|
index 303cefba51e19ac43b1f6188ad64ef480715ebaf..98ec88751b3e71c2e7aad633096b7f41608c0b33 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/WitherMobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/WitherMobEffect.java
|
|
@@ -10,7 +10,7 @@ class WitherMobEffect extends MobEffect {
|
|
|
|
@Override
|
|
public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) {
|
|
- entity.hurtServer(world, entity.damageSources().wither(), 1.0F);
|
|
+ entity.hurtServer(world, entity.damageSources().wither(), entity.level().purpurConfig.entityWitherDegenerationAmount); // Purpur
|
|
return true;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index e9438f3b2acabcb9d3ecd6484704db1a94b21a0e..68f94543395c143234957cd5e33600a0c4b1c87d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -175,7 +175,7 @@ import org.bukkit.plugin.PluginManager;
|
|
// CraftBukkit end
|
|
|
|
public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity { // Paper - rewrite chunk system // Paper - optimise entity tracker
|
|
-
|
|
+ public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur
|
|
// CraftBukkit start
|
|
private static final int CURRENT_LEVEL = 2;
|
|
public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first setPositionRotation
|
|
@@ -299,6 +299,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
public double xOld;
|
|
public double yOld;
|
|
public double zOld;
|
|
+ public float maxUpStep; // Purpur
|
|
public boolean noPhysics;
|
|
private boolean wasOnFire;
|
|
public final RandomSource random;
|
|
@@ -339,7 +340,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
private final Set<String> tags;
|
|
private final double[] pistonDeltas;
|
|
private long pistonDeltasGameTime;
|
|
- private EntityDimensions dimensions;
|
|
+ protected EntityDimensions dimensions; // Purpur - private -> protected
|
|
private float eyeHeight;
|
|
public boolean isInPowderSnow;
|
|
public boolean wasInPowderSnow;
|
|
@@ -390,6 +391,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
private final int despawnTime; // Paper - entity despawn time limit
|
|
public boolean activatedPriorityReset = false; // Pufferfish - DAB
|
|
public int activatedPriority = org.dreeam.leaf.config.modules.opt.DynamicActivationofBrain.maximumActivationPrio; // Pufferfish - DAB (golf score)
|
|
+ public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API
|
|
|
|
public void setOrigin(@javax.annotation.Nonnull Location location) {
|
|
this.origin = location.toVector();
|
|
@@ -570,6 +572,27 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
}
|
|
// Paper end - optimise entity tracker
|
|
+ // Purpur start
|
|
+ public boolean canSaveToDisk() {
|
|
+ return true;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ // Purpur start - copied from Mob - API for any mob to burn daylight
|
|
+ public boolean isSunBurnTick() {
|
|
+ if (this.level().isDay() && !this.level().isClientSide) {
|
|
+ float f = this.getLightLevelDependentMagicValue();
|
|
+ BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ());
|
|
+ boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow;
|
|
+
|
|
+ if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end - copied from Mob - API for any mob to burn daylight
|
|
|
|
public Entity(EntityType<?> type, Level world) {
|
|
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
|
@@ -579,7 +602,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
this.bb = Entity.INITIAL_AABB;
|
|
this.stuckSpeedMultiplier = Vec3.ZERO;
|
|
this.nextStep = 1.0F;
|
|
- this.random = SHARED_RANDOM; // Paper - Share random for entities to make them more random
|
|
+ this.random = world == null || world.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create(); // Paper - Share random for entities to make them more random // Purpur
|
|
this.remainingFireTicks = -this.getFireImmuneTicks();
|
|
this.fluidHeight = new Object2DoubleArrayMap(2);
|
|
this.fluidOnEyes = new HashSet();
|
|
@@ -977,6 +1000,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
&& this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
|
|
&& (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
|
|
// Paper end - Configurable nether ceiling damage
|
|
+ if (this.level().purpurConfig.teleportOnNetherCeilingDamage && this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this instanceof ServerPlayer player) player.teleport(io.papermc.paper.util.MCUtil.toLocation(this.level, this.level.getSharedSpawnPos())); else // Purpur
|
|
this.onBelowWorld();
|
|
}
|
|
|
|
@@ -1943,7 +1967,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
|
|
public boolean fireImmune() {
|
|
- return this.getType().fireImmune();
|
|
+ return this.immuneToFire != null ? immuneToFire : this.getType().fireImmune(); // Purpur - add fire immune API
|
|
}
|
|
|
|
public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
|
|
@@ -2016,7 +2040,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return this.isInWater() || flag;
|
|
}
|
|
|
|
- void updateInWaterStateAndDoWaterCurrentPushing() {
|
|
+ public void updateInWaterStateAndDoWaterCurrentPushing() { // Purpur - package-private -> public
|
|
Entity entity = this.getVehicle();
|
|
|
|
if (entity instanceof AbstractBoat abstractboat) {
|
|
@@ -2708,6 +2732,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
nbttagcompound.putBoolean("Paper.FreezeLock", true);
|
|
}
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ if (immuneToFire != null) {
|
|
+ nbttagcompound.putBoolean("Purpur.FireImmune", immuneToFire);
|
|
+ }
|
|
+ // Purpur end
|
|
return nbttagcompound;
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT");
|
|
@@ -2856,6 +2885,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
freezeLocked = nbt.getBoolean("Paper.FreezeLock");
|
|
}
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.FireImmune")) {
|
|
+ immuneToFire = nbt.getBoolean("Purpur.FireImmune");
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT");
|
|
@@ -3107,6 +3141,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
if (this.isAlive() && this instanceof Leashable leashable) {
|
|
if (leashable.getLeashHolder() == player) {
|
|
if (!this.level().isClientSide()) {
|
|
+ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.AbstractVillager) return InteractionResult.CONSUME; // Purpur
|
|
// CraftBukkit start - fire PlayerUnleashEntityEvent
|
|
// Paper start - Expand EntityUnleashEvent
|
|
org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials());
|
|
@@ -3312,6 +3347,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
this.passengers = ImmutableList.copyOf(list);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) {
|
|
+ onMount(player);
|
|
+ this.rider = player;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.gameEvent(GameEvent.ENTITY_MOUNT, passenger);
|
|
}
|
|
}
|
|
@@ -3351,6 +3393,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return false;
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start
|
|
+ if (this.rider != null && this.passengers.get(0) == this.rider) {
|
|
+ onDismount(this.rider);
|
|
+ this.rider = null;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
|
|
this.passengers = ImmutableList.of();
|
|
} else {
|
|
@@ -3431,14 +3481,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return Vec3.directionFromRotation(this.getRotationVector());
|
|
}
|
|
|
|
+ public BlockPos portalPos = BlockPos.ZERO; // Purpur
|
|
public void setAsInsidePortal(Portal portal, BlockPos pos) {
|
|
if (this.isOnPortalCooldown()) {
|
|
+ if (!(level().purpurConfig.playerFixStuckPortal && this instanceof Player && !pos.equals(this.portalPos))) // Purpur - Fix stuck in portals
|
|
this.setPortalCooldown();
|
|
- } else {
|
|
+ } else if (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - Entities can use portals
|
|
if (this.portalProcess != null && this.portalProcess.isSamePortal(portal)) {
|
|
if (!this.portalProcess.isInsidePortalThisTick()) {
|
|
this.portalProcess.updateEntryPosition(pos.immutable());
|
|
this.portalProcess.setAsInsidePortalThisTick(true);
|
|
+ this.portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals
|
|
}
|
|
} else {
|
|
this.portalProcess = new PortalProcessor(portal, pos.immutable());
|
|
@@ -3650,7 +3703,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
|
|
public int getMaxAirSupply() {
|
|
- return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ return this.level == null? this.maxAirTicks : this.level().purpurConfig.drowningAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() // Purpur
|
|
}
|
|
|
|
public int getAirSupply() {
|
|
@@ -4139,7 +4192,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
// CraftBukkit end
|
|
|
|
public boolean canUsePortal(boolean allowVehicles) {
|
|
- return (allowVehicles || !this.isPassenger()) && this.isAlive();
|
|
+ return (allowVehicles || !this.isPassenger()) && this.isAlive() && (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Purpur - Entities can use portals
|
|
}
|
|
|
|
public boolean canTeleport(Level from, Level to) {
|
|
@@ -4732,6 +4785,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return Mth.lerp(delta, this.yRotO, this.yRot);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public AABB getAxisForFluidCheck() {
|
|
+ return this.getBoundingBox().deflate(0.001D);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - optimise collisions
|
|
public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
|
|
if (this.touchingUnloadedChunk()) {
|
|
@@ -5143,7 +5202,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
|
|
public float maxUpStep() {
|
|
- return 0.0F;
|
|
+ return maxUpStep;
|
|
}
|
|
|
|
public void onExplosionHit(@Nullable Entity entity) {}
|
|
@@ -5344,4 +5403,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return ((net.minecraft.server.level.ServerLevel) this.level).isPositionEntityTicking(this.blockPosition());
|
|
}
|
|
// Paper end - Expose entity id counter
|
|
+ // Purpur start
|
|
+ @Nullable
|
|
+ private Player rider = null;
|
|
+
|
|
+ @Nullable
|
|
+ public Player getRider() {
|
|
+ return rider;
|
|
+ }
|
|
+
|
|
+ public boolean isRidable() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean isControllable() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public void onMount(Player rider) {
|
|
+ if (this instanceof Mob) {
|
|
+ ((Mob) this).setTarget(null, null, false);
|
|
+ ((Mob) this).getNavigation().stop();
|
|
+ }
|
|
+ rider.setJumping(false); // fixes jump on mount
|
|
+ }
|
|
+
|
|
+ public void onDismount(Player player) {
|
|
+ }
|
|
+
|
|
+ public boolean onSpacebar() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean onClick(InteractionHand hand) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean processClick(InteractionHand hand) {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
index 6bf691fcc6486bde73bae30eff09142802c29eda..59c4d3753c7084e92402608b7fb3c4adbc6c2f65 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
@@ -39,6 +39,7 @@ public final class EntitySelector {
|
|
return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks;
|
|
};
|
|
// Paper end - Ability to control player's insomnia and phantoms
|
|
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur
|
|
|
|
private EntitySelector() {}
|
|
// Paper start - Affects Spawning API
|
|
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
index 24eaa9a2e6f0cf198a307058e655d5eb16a2c8c5..002795df9c9c8d27f07f855dff148dfe353bef68 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
@@ -389,7 +389,8 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
@Nullable
|
|
private Component description;
|
|
private final Optional<ResourceKey<LootTable>> lootTable;
|
|
- private final EntityDimensions dimensions;
|
|
+ private EntityDimensions dimensions; // Purpur - remove final
|
|
+ public void setDimensions(EntityDimensions dimensions) { this.dimensions = dimensions; } // Purpur
|
|
private final float spawnDimensionsScale;
|
|
private final FeatureFlagSet requiredFeatures;
|
|
|
|
@@ -405,6 +406,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
return EntityType.register(EntityType.vanillaEntityId(id), type);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
|
|
+ return getFromKey(ResourceLocation.parse(bukkitType.getKey().toString()));
|
|
+ }
|
|
+
|
|
+ public static EntityType<?> getFromKey(ResourceLocation location) {
|
|
+ return BuiltInRegistries.ENTITY_TYPE.getValue(location);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ResourceLocation getKey(EntityType<?> type) {
|
|
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
|
|
}
|
|
@@ -605,6 +616,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
return this.category;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public String getName() {
|
|
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
|
|
+ }
|
|
+
|
|
+ public String getTranslatedName() {
|
|
+ return getDescription().getString();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public String getDescriptionId() {
|
|
return this.descriptionId;
|
|
}
|
|
@@ -662,6 +683,12 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
entity.load(nbt);
|
|
}, () -> {
|
|
EntityType.LOGGER.warn("Skipping Entity with id {}", nbt.getString("id"));
|
|
+ // Purpur start - log skipped entity's position
|
|
+ try {
|
|
+ ListTag pos = nbt.getList("Pos", 6);
|
|
+ EntityType.LOGGER.warn("Location: {} {},{},{}", world.getWorld().getName(), pos.getDouble(0), pos.getDouble(1), pos.getDouble(2));
|
|
+ } catch (Throwable ignore) {}
|
|
+ // Purpur end
|
|
});
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
index bf0838f574fa3fb9654e087d602b8d380bd7fb28..32a0db7e8f974712bd8a05f16f3bd48ac66142d5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
@@ -333,7 +333,7 @@ public class ExperienceOrb extends Entity {
|
|
public void playerTouch(Player player) {
|
|
if (player instanceof ServerPlayer entityplayer) {
|
|
if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(entityplayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent
|
|
- player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, 2, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2;
|
|
+ player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, this.level().purpurConfig.playerExpPickupDelay, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; // Purpur
|
|
player.take(this, 1);
|
|
int i = this.repairPlayerItems(entityplayer, this.value);
|
|
|
|
@@ -351,7 +351,7 @@ public class ExperienceOrb extends Entity {
|
|
}
|
|
|
|
private int repairPlayerItems(ServerPlayer player, int amount) {
|
|
- Optional<EnchantedItemInUse> optional = EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged);
|
|
+ Optional<EnchantedItemInUse> optional = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged); // Purpur - Add option to mend the most damaged equipment first
|
|
|
|
if (optional.isPresent()) {
|
|
ItemStack itemstack = ((EnchantedItemInUse) optional.get()).itemStack();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
index b851c3ee1426bc0a259bf6c4a662af0c9883dd71..3283228d7ebf98ce98780725a0a412bea4200da5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
@@ -25,6 +25,39 @@ public class GlowSquid extends Squid {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.glowSquidRidable;
|
|
+ }
|
|
+
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.glowSquidControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canFly() {
|
|
+ return this.level().purpurConfig.glowSquidsCanFly;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.glowSquidTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.glowSquidAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected ParticleOptions getInkParticle() {
|
|
return ParticleTypes.GLOW_SQUID_INK;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
index 167d164d07285bfff6eb8076d7abe17ca9543df9..260e9b0398ddaacacfe5de352ac686ef273a6167 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -246,9 +246,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
protected int deathScore;
|
|
public float lastHurt;
|
|
public boolean jumping;
|
|
- public float xxa;
|
|
- public float yya;
|
|
- public float zza;
|
|
+ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER
|
|
+ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER
|
|
+ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER
|
|
protected int lerpSteps;
|
|
protected double lerpX;
|
|
protected double lerpY;
|
|
@@ -295,6 +295,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper
|
|
public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
|
|
+ protected boolean shouldBurnInDay = false; public boolean shouldBurnInDay() { return this.shouldBurnInDay; } public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } // Purpur - API for any mob to burn daylight
|
|
|
|
@Override
|
|
public float getBukkitYaw() {
|
|
@@ -323,7 +324,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.lastClimbablePos = Optional.empty();
|
|
this.activeLocationDependentEnchantments = new EnumMap(EquipmentSlot.class);
|
|
this.appliedScale = 1.0F;
|
|
- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
|
|
+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur
|
|
+ this.initAttributes(); // Purpur
|
|
this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit
|
|
// CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
|
|
this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
|
|
@@ -338,6 +340,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (Tag) dynamicopsnbt.emptyMap()))));
|
|
}
|
|
|
|
+ protected void initAttributes() {}// Purpur
|
|
+
|
|
public Brain<?> getBrain() {
|
|
return this.brain;
|
|
}
|
|
@@ -373,6 +377,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public static AttributeSupplier.Builder createLivingAttributes() {
|
|
return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS).add(Attributes.MAX_ABSORPTION).add(Attributes.STEP_HEIGHT).add(Attributes.SCALE).add(Attributes.GRAVITY).add(Attributes.SAFE_FALL_DISTANCE).add(Attributes.FALL_DAMAGE_MULTIPLIER).add(Attributes.JUMP_STRENGTH).add(Attributes.OXYGEN_BONUS).add(Attributes.BURNING_TIME).add(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE).add(Attributes.WATER_MOVEMENT_EFFICIENCY).add(Attributes.MOVEMENT_EFFICIENCY).add(Attributes.ATTACK_KNOCKBACK);
|
|
}
|
|
+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur
|
|
|
|
@Override
|
|
protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) {
|
|
@@ -474,6 +479,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (d1 < 0.0D) {
|
|
d0 = this.level().getWorldBorder().getDamagePerBlock();
|
|
if (d0 > 0.0D) {
|
|
+ if (level().purpurConfig.teleportIfOutsideBorder && this instanceof ServerPlayer serverPlayer) { serverPlayer.teleport(io.papermc.paper.util.MCUtil.toLocation(level(), ((ServerLevel) level()).getSharedSpawnPos())); return; } // Purpur
|
|
this.hurtServer(worldserver1, this.damageSources().outOfBorder(), (float) Math.max(1, Mth.floor(-d1 * d0)));
|
|
}
|
|
}
|
|
@@ -485,7 +491,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
if (flag1) {
|
|
this.setAirSupply(this.decreaseAirSupply(this.getAirSupply()));
|
|
- if (this.getAirSupply() == -20) {
|
|
+ if (this.getAirSupply() == -this.level().purpurConfig.drowningDamageInterval) { // Purpur
|
|
this.setAirSupply(0);
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
@@ -497,7 +503,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d0, this.getY() + d2, this.getZ() + d3, vec3d.x, vec3d.y, vec3d.z);
|
|
}
|
|
|
|
- this.hurt(this.damageSources().drown(), 2.0F);
|
|
+ this.hurt(this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -830,6 +836,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
|
|
nbt.put("Brain", nbtbase);
|
|
});
|
|
+ nbt.putBoolean("Purpur.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - API for any mob to burn daylight
|
|
}
|
|
|
|
@Override
|
|
@@ -918,6 +925,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain")));
|
|
}
|
|
|
|
+ // Purpur start - API for any mob to burn daylight
|
|
+ if (nbt.contains("Purpur.ShouldBurnInDay")) {
|
|
+ this.shouldBurnInDay = nbt.getBoolean("Purpur.ShouldBurnInDay");
|
|
+ }
|
|
+ // Purpur end - API for any mob to burn daylight
|
|
}
|
|
|
|
// CraftBukkit start
|
|
@@ -1052,9 +1064,29 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (entity != null) {
|
|
EntityType<?> entitytypes = entity.getType();
|
|
|
|
- if (entitytypes == EntityType.SKELETON && this.getItemBySlot(EquipmentSlot.HEAD).is(Items.SKELETON_SKULL) || entitytypes == EntityType.ZOMBIE && this.getItemBySlot(EquipmentSlot.HEAD).is(Items.ZOMBIE_HEAD) || entitytypes == EntityType.PIGLIN && this.getItemBySlot(EquipmentSlot.HEAD).is(Items.PIGLIN_HEAD) || entitytypes == EntityType.PIGLIN_BRUTE && this.getItemBySlot(EquipmentSlot.HEAD).is(Items.PIGLIN_HEAD) || entitytypes == EntityType.CREEPER && this.getItemBySlot(EquipmentSlot.HEAD).is(Items.CREEPER_HEAD)) { // Gale - Petal - reduce skull ItemStack lookups for reduced visibility
|
|
- d0 *= 0.5D;
|
|
+ // Purpur start
|
|
+ if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL)) {
|
|
+ d0 *= entity.level().purpurConfig.skeletonHeadVisibilityPercent;
|
|
+ } else if (entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD)) {
|
|
+ d0 *= entity.level().purpurConfig.zombieHeadVisibilityPercent;
|
|
+ }
|
|
+ else if (entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) {
|
|
+ d0 *= entity.level().purpurConfig.creeperHeadVisibilityPercent;
|
|
+ } else if ((entitytypes == EntityType.PIGLIN || entitytypes == EntityType.PIGLIN_BRUTE) && itemstack.is(Items.PIGLIN_HEAD)) {
|
|
+ d0 *= entity.level().purpurConfig.piglinHeadVisibilityPercent;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ // Purpur start
|
|
+ if (entity instanceof LivingEntity entityliving) {
|
|
+ if (entityliving.hasEffect(MobEffects.BLINDNESS)) {
|
|
+ int amplifier = entityliving.getEffect(MobEffects.BLINDNESS).getAmplifier();
|
|
+ for (int i = 0; i < amplifier; i++) {
|
|
+ d0 *= this.level().purpurConfig.mobsBlindnessMultiplier;
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Purpur end
|
|
}
|
|
|
|
return d0;
|
|
@@ -1110,6 +1142,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
while (iterator.hasNext()) {
|
|
MobEffectInstance effect = iterator.next();
|
|
+ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level().purpurConfig.milkClearsBeneficialEffects && effect.getEffect().value().isBeneficial()) continue; // Purpur
|
|
EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED);
|
|
if (event.isCancelled()) {
|
|
continue;
|
|
@@ -1454,6 +1487,24 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.stopSleeping();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (source.getEntity() instanceof net.minecraft.world.entity.player.Player player && source.getEntity().level().purpurConfig.creativeOnePunch && !source.is(DamageTypeTags.IS_PROJECTILE)) {
|
|
+ if (player.isCreative()) {
|
|
+ org.apache.commons.lang3.mutable.MutableDouble attackDamage = new org.apache.commons.lang3.mutable.MutableDouble();
|
|
+ player.getMainHandItem().forEachModifier(EquipmentSlot.MAINHAND, (attributeHolder, attributeModifier) -> {
|
|
+ if (attributeModifier.operation() == AttributeModifier.Operation.ADD_VALUE) {
|
|
+ attackDamage.addAndGet(attributeModifier.amount());
|
|
+ }
|
|
+ });
|
|
+
|
|
+ if (attackDamage.doubleValue() == 0.0D) {
|
|
+ // One punch!
|
|
+ amount = 9999F;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.noActionTime = 0;
|
|
if (amount < 0.0F) {
|
|
amount = 0.0F;
|
|
@@ -1553,13 +1604,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (entity1 instanceof net.minecraft.world.entity.player.Player) {
|
|
net.minecraft.world.entity.player.Player entityhuman = (net.minecraft.world.entity.player.Player) entity1;
|
|
|
|
- this.lastHurtByPlayerTime = 100;
|
|
+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur
|
|
this.lastHurtByPlayer = entityhuman;
|
|
} else if (entity1 instanceof Wolf) {
|
|
Wolf entitywolf = (Wolf) entity1;
|
|
|
|
if (entitywolf.isTame()) {
|
|
- this.lastHurtByPlayerTime = 100;
|
|
+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur
|
|
LivingEntity entityliving2 = entitywolf.getOwner();
|
|
|
|
if (entityliving2 instanceof net.minecraft.world.entity.player.Player) {
|
|
@@ -1706,6 +1757,18 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemstack == null || itemstack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) {
|
|
+ for (ItemStack item : player.getInventory().items) {
|
|
+ if (item.getItem() == Items.TOTEM_OF_UNDYING) {
|
|
+ itemstack1 = item;
|
|
+ itemstack = item.copy();
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null;
|
|
EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot);
|
|
event.setCancelled(itemstack == null);
|
|
@@ -1877,7 +1940,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
boolean flag = false;
|
|
|
|
if (this.dead && adversary instanceof WitherBoss) { // Paper
|
|
- if (worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (worldserver.purpurConfig.witherBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
BlockPos blockposition = this.blockPosition();
|
|
BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState();
|
|
|
|
@@ -1914,6 +1977,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
this.dropEquipment(world); // CraftBukkit - from below
|
|
if (this.shouldDropLoot() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
|
|
+ if (!(damageSource.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level().purpurConfig.disableDropsOnCrammingDeath)) { // Purpur
|
|
this.dropFromLootTable(world, damageSource, flag);
|
|
// Paper start
|
|
final boolean prev = this.clearEquipmentSlots;
|
|
@@ -1922,6 +1986,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
// Paper end
|
|
this.dropCustomDeathLoot(world, damageSource, flag);
|
|
this.clearEquipmentSlots = prev; // Paper
|
|
+ } // Purpur
|
|
}
|
|
// CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
|
|
org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, damageSource, this.drops, () -> {
|
|
@@ -3188,6 +3253,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
if (f > 0.0F) {
|
|
this.playSound(this.getFallDamageSound((int) f), 1.0F, 1.0F);
|
|
+ if (level().purpurConfig.elytraKineticDamage) // Purpur
|
|
this.hurt(this.damageSources().flyIntoWall(), f);
|
|
}
|
|
}
|
|
@@ -3717,8 +3783,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.pushEntities();
|
|
|
|
// Paper start - Add EntityMoveEvent
|
|
- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
|
|
- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
|
|
+ // Purpur start
|
|
+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
|
|
+ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
|
|
+ // Purpur end
|
|
Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
|
|
Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
|
|
@@ -3728,6 +3796,21 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
|
|
}
|
|
}
|
|
+ // Purpur start
|
|
+ if (getRider() != null) {
|
|
+ getRider().resetLastActionTime();
|
|
+ if (((ServerLevel) level()).hasRidableMoveEvent && this instanceof Mob) {
|
|
+ Location from = new Location(level().getWorld(), xo, yo, zo, this.yRotO, this.xRotO);
|
|
+ Location to = new Location(level().getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot());
|
|
+ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (Player) getRider().getBukkitEntity(), from, to.clone());
|
|
+ if (!event.callEvent()) {
|
|
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
|
|
+ } else if (!to.equals(event.getTo())) {
|
|
+ absMoveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
// Paper end - Add EntityMoveEvent
|
|
world = this.level();
|
|
@@ -3737,6 +3820,34 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start - copied from Zombie - API for any mob to burn daylight
|
|
+ if (this.isAlive()) {
|
|
+ boolean flag = this.shouldBurnInDay() && this.isSunBurnTick(); // Paper - shouldBurnInDay API // Purpur - use shouldBurnInDay() method to handle Phantoms properly - API for any mob to burn daylight
|
|
+
|
|
+ if (flag) {
|
|
+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
+
|
|
+ if (!itemstack.isEmpty()) {
|
|
+ if (itemstack.isDamageableItem()) {
|
|
+ Item item = itemstack.getItem();
|
|
+
|
|
+ itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
+ if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
+ this.onEquippedItemBroken(item, EquipmentSlot.HEAD);
|
|
+ this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ flag = false;
|
|
+ }
|
|
+
|
|
+ if (flag) {
|
|
+ if (getRider() == null || !this.isControllable()) // Purpur - ignore mobs which are uncontrollable or without rider - API for any mob to burn daylight
|
|
+ this.igniteForSeconds(8.0F);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end - copied from Zombie - API for any mob to burn daylight
|
|
}
|
|
|
|
public boolean isSensitiveToWater() {
|
|
@@ -3763,7 +3874,17 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}).toList();
|
|
EquipmentSlot enumitemslot = (EquipmentSlot) Util.getRandom(list, this.random);
|
|
|
|
- this.getItemBySlot(enumitemslot).hurtAndBreak(1, this, enumitemslot);
|
|
+ // Purpur start
|
|
+ int damage = level().purpurConfig.elytraDamagePerSecond;
|
|
+ if (level().purpurConfig.elytraDamageMultiplyBySpeed > 0) {
|
|
+ double speed = getDeltaMovement().lengthSqr();
|
|
+ if (speed > level().purpurConfig.elytraDamageMultiplyBySpeed) {
|
|
+ damage *= (int) speed;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.getItemBySlot(enumitemslot).hurtAndBreak(damage, this, enumitemslot);
|
|
+ // Purpur end
|
|
}
|
|
|
|
this.gameEvent(GameEvent.ELYTRA_GLIDE);
|
|
@@ -3772,7 +3893,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
}
|
|
|
|
- protected boolean canGlide() {
|
|
+ public boolean canGlide() { // Purpur
|
|
if (!this.onGround() && !this.isPassenger() && !this.hasEffect(MobEffects.LEVITATION)) {
|
|
Iterator iterator = EquipmentSlot.VALUES.iterator();
|
|
|
|
@@ -4694,7 +4815,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (equippable != null && equippable.dispensable()) {
|
|
EquipmentSlot enumitemslot = equippable.slot();
|
|
|
|
- return this.canUseSlot(enumitemslot) && equippable.canBeEquippedBy(this.getType()) ? this.getItemBySlot(enumitemslot).isEmpty() && this.canDispenserEquipIntoSlot(enumitemslot) : false;
|
|
+ return this.canUseSlot(enumitemslot) && equippable.canBeEquippedBy(this.getType()) && this.getItemBySlot(enumitemslot).isEmpty() && this.canDispenserEquipIntoSlot(enumitemslot);
|
|
} else {
|
|
return false;
|
|
}
|
|
@@ -4719,6 +4840,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
return equippable == null ? slot == EquipmentSlot.MAINHAND && this.canUseSlot(EquipmentSlot.MAINHAND) : slot == equippable.slot() && this.canUseSlot(equippable.slot()) && equippable.canBeEquippedBy(this.getType());
|
|
}
|
|
|
|
+ // Purpur start - Dispenser curse of binding protection
|
|
+ public @Nullable EquipmentSlot getEquipmentSlotForDispenserItem(ItemStack itemstack) {
|
|
+ return EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.BINDING_CURSE, itemstack) > 0 ? null : this.getEquipmentSlotForItem(itemstack);
|
|
+ }
|
|
+ // Purpur end - Dispenser curse of binding protection
|
|
+
|
|
private static SlotAccess createEquipmentSlotAccess(LivingEntity entity, EquipmentSlot slot) {
|
|
return slot != EquipmentSlot.HEAD && slot != EquipmentSlot.MAINHAND && slot != EquipmentSlot.OFFHAND ? SlotAccess.forEquipmentSlot(entity, slot, (itemstack) -> {
|
|
return itemstack.isEmpty() || entity.getEquipmentSlotForItem(itemstack) == slot;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
index 92e38600c64fa8311a191f76aa9c7654ce10c5b4..a2ab53e70328b4ac0d019ebbd3d3cffeeb29d76b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
@@ -146,6 +146,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
private BlockPos restrictCenter;
|
|
private float restrictRadius;
|
|
|
|
+ public int ticksSinceLastInteraction; // Purpur
|
|
public boolean aware = true; // CraftBukkit
|
|
|
|
protected Mob(EntityType<? extends Mob> type, Level world) {
|
|
@@ -161,8 +162,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
this.restrictRadius = -1.0F;
|
|
this.goalSelector = new GoalSelector();
|
|
this.targetSelector = new GoalSelector();
|
|
- this.lookControl = new LookControl(this);
|
|
- this.moveControl = new MoveControl(this);
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur
|
|
this.jumpControl = new JumpControl(this);
|
|
this.bodyRotationControl = this.createBodyControl();
|
|
this.navigation = this.createNavigation(world);
|
|
@@ -334,6 +335,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
entityliving = null;
|
|
}
|
|
}
|
|
+ if (entityliving instanceof net.minecraft.server.level.ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur
|
|
this.target = entityliving;
|
|
return true;
|
|
// CraftBukkit end
|
|
@@ -374,8 +376,29 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
this.resetAmbientSoundTime();
|
|
this.playAmbientSound();
|
|
}
|
|
+
|
|
+ incrementTicksSinceLastInteraction(); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private void incrementTicksSinceLastInteraction() {
|
|
+ ++this.ticksSinceLastInteraction;
|
|
+ if (getRider() != null) {
|
|
+ this.ticksSinceLastInteraction = 0;
|
|
+ return;
|
|
+ }
|
|
+ if (this.level().purpurConfig.entityLifeSpan <= 0) {
|
|
+ return; // feature disabled
|
|
+ }
|
|
+ if (!this.removeWhenFarAway(0) || isPersistenceRequired() || requiresCustomPersistence() || hasCustomName()) {
|
|
+ return; // mob persistent
|
|
+ }
|
|
+ if (this.ticksSinceLastInteraction > this.level().purpurConfig.entityLifeSpan) {
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void playHurtSound(DamageSource damageSource) {
|
|
this.resetAmbientSoundTime();
|
|
@@ -543,6 +566,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
}
|
|
|
|
nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit
|
|
+ nbt.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -620,6 +644,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
this.aware = nbt.getBoolean("Bukkit.Aware");
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.ticksSinceLastInteraction")) {
|
|
+ this.ticksSinceLastInteraction = nbt.getInt("Purpur.ticksSinceLastInteraction");
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -670,7 +699,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
Level world = this.level();
|
|
|
|
if (world instanceof ServerLevel worldserver) {
|
|
- if (this.canPickUpLoot() && this.isAlive() && !this.dead && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.canPickUpLoot() && this.isAlive() && !this.dead && (worldserver.purpurConfig.entitiesPickUpLootBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur
|
|
Vec3i baseblockposition = this.getPickupReach();
|
|
List<ItemEntity> list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ()));
|
|
Iterator iterator = list.iterator();
|
|
@@ -1370,7 +1399,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
attributemodifiable.addPermanentModifier(new AttributeModifier(Mob.RANDOM_SPAWN_BONUS_ID, randomsource.triangle(0.0D, 0.11485000000000001D), AttributeModifier.Operation.ADD_MULTIPLIED_BASE));
|
|
}
|
|
|
|
- this.setLeftHanded(randomsource.nextFloat() < 0.05F);
|
|
+ this.setLeftHanded(randomsource.nextFloat() < world.getLevel().purpurConfig.entityLeftHandedChance); // Purpur
|
|
return entityData;
|
|
}
|
|
|
|
@@ -1472,7 +1501,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
protected void onOffspringSpawnedFromEgg(Player player, Mob child) {}
|
|
|
|
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
public boolean isWithinRestriction() {
|
|
@@ -1711,6 +1740,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
this.playAttackSound();
|
|
}
|
|
|
|
+ if (target instanceof net.minecraft.server.level.ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur
|
|
return flag;
|
|
}
|
|
|
|
@@ -1722,27 +1752,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
// Gale end - JettPack - optimize sun burn tick - cache eye blockpos
|
|
|
|
public boolean isSunBurnTick() {
|
|
- if (this.level().isDay() && !this.level().isClientSide) {
|
|
- // Gale start - JettPack - optimize sun burn tick - optimizations and cache eye blockpos
|
|
- if (this.cached_position != this.position) {
|
|
- this.cached_eye_blockpos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ());
|
|
- this.cached_position = this.position;
|
|
- }
|
|
-
|
|
- float f = this.getLightLevelDependentMagicValue(cached_eye_blockpos); // Pass BlockPos to getBrightness
|
|
-
|
|
- // Check brightness first
|
|
- if (f <= 0.5F) return false;
|
|
- if (this.random.nextFloat() * 30.0F >= (f - 0.4F) * 2.0F) return false;
|
|
- // Gale end - JettPack - optimize sun burn tick - optimizations and cache eye blockpos
|
|
- boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow;
|
|
-
|
|
- if (!flag && this.level().canSeeSky(this.cached_eye_blockpos)) { // Gale - JettPack - optimize sun burn tick - optimizations and cache eye blockpos
|
|
- return true;
|
|
- }
|
|
- }
|
|
-
|
|
- return false;
|
|
+ // Purpur - implemented in Entity - API for any mob to burn daylight
|
|
+ return super.isSunBurnTick();
|
|
}
|
|
|
|
@Override
|
|
@@ -1804,4 +1815,58 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
|
public float[] getArmorDropChances() {
|
|
return this.armorDropChances;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public double getMaxY() {
|
|
+ return level().getHeight();
|
|
+ }
|
|
+
|
|
+ public InteractionResult tryRide(Player player, InteractionHand hand) {
|
|
+ return tryRide(player, hand, InteractionResult.PASS);
|
|
+ }
|
|
+
|
|
+ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) {
|
|
+ if (!isRidable()) {
|
|
+ return result;
|
|
+ }
|
|
+ if (hand != InteractionHand.MAIN_HAND) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (player.isShiftKeyDown()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!player.getItemInHand(hand).isEmpty()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!passengers.isEmpty() || player.isPassenger()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (this instanceof TamableAnimal tamable) {
|
|
+ if (tamable.isTame() && !tamable.isOwnedBy(player)) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!tamable.isTame() && !level().purpurConfig.untamedTamablesAreRidable) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ if (this instanceof AgeableMob ageable) {
|
|
+ if (ageable.isBaby() && !level().purpurConfig.babiesAreRidable) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) {
|
|
+ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
|
|
+ serverPlayer.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob);
|
|
+ }
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ player.setYRot(this.getYRot());
|
|
+ player.setXRot(this.getXRot());
|
|
+ if (player.startRiding(this)) {
|
|
+ return InteractionResult.SUCCESS;
|
|
+ } else {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
index e45ed0ffa031f7ab5fa39b71a2678c5a282e0561..7bc3a6f4dabc6411b6ff17e6dbbd190d57076cd1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
@@ -26,15 +26,22 @@ public class AttributeMap {
|
|
// Gale end - Lithium - replace AI attributes with optimized collections
|
|
private final AttributeSupplier supplier;
|
|
private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
|
|
+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur
|
|
|
|
public AttributeMap(AttributeSupplier defaultAttributes) {
|
|
+ // Purpur start
|
|
+ this(defaultAttributes, null);
|
|
+ }
|
|
+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) {
|
|
+ this.entity = entity;
|
|
+ // Purpur end
|
|
this.supplier = defaultAttributes;
|
|
this.createInstance = attributex -> this.supplier.createInstance(this::onAttributeModified, attributex); // Gale - Airplane - reduce entity allocations
|
|
}
|
|
|
|
private void onAttributeModified(AttributeInstance instance) {
|
|
this.attributesToUpdate.add(instance);
|
|
- if (instance.getAttribute().value().isClientSyncable()) {
|
|
+ if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur
|
|
this.attributesToSync.add(instance);
|
|
}
|
|
}
|
|
@@ -48,7 +55,7 @@ public class AttributeMap {
|
|
}
|
|
|
|
public Collection<AttributeInstance> getSyncableAttributes() {
|
|
- return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable()).collect(Collectors.toList());
|
|
+ return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute().value()))).collect(Collectors.toList()); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
index 386f9bca728055f7b75fb690b307ff4510068105..0bb08af954d224a2b4404615bee720ac4bdbac55 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
@@ -130,7 +130,7 @@ public class DefaultAttributes {
|
|
.put(EntityType.OCELOT, Ocelot.createAttributes().build())
|
|
.put(EntityType.PANDA, Panda.createAttributes().build())
|
|
.put(EntityType.PARROT, Parrot.createAttributes().build())
|
|
- .put(EntityType.PHANTOM, Monster.createMonsterAttributes().build())
|
|
+ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur
|
|
.put(EntityType.PIG, Pig.createAttributes().build())
|
|
.put(EntityType.PIGLIN, Piglin.createAttributes().build())
|
|
.put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build())
|
|
@@ -161,7 +161,7 @@ public class DefaultAttributes {
|
|
.put(EntityType.VILLAGER, Villager.createAttributes().build())
|
|
.put(EntityType.VINDICATOR, Vindicator.createAttributes().build())
|
|
.put(EntityType.WARDEN, Warden.createAttributes().build())
|
|
- .put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build())
|
|
+ .put(EntityType.WANDERING_TRADER, net.minecraft.world.entity.npc.WanderingTrader.createAttributes().build()) // Purpur
|
|
.put(EntityType.WITCH, Witch.createAttributes().build())
|
|
.put(EntityType.WITHER, WitherBoss.createAttributes().build())
|
|
.put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build())
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
index f0703302e7dbbda88de8c648d20d87c55ed9b1e0..a913ebabaa5f443afa987b972355a8f8d1723c78 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
@@ -29,6 +29,7 @@ public class RangedAttribute extends Attribute {
|
|
|
|
@Override
|
|
public double sanitizeValue(double value) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.clampAttributes) return Double.isNaN(value) ? this.minValue : value; // Purpur
|
|
return Double.isNaN(value) ? this.minValue : Mth.clamp(value, this.minValue, this.maxValue);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
index 59732e3b17c20143ecbdc6afa480fdbdc1afc55f..93d17d93922841354fb0bfb15ce43776fafb19d2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
@@ -81,7 +81,7 @@ public class AcquirePoi {
|
|
};
|
|
// Paper start - optimise POI access
|
|
java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
|
|
- io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes);
|
|
+ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), world.purpurConfig.villagerAcquirePoiSearchRadius, world.purpurConfig.villagerAcquirePoiSearchRadius*world.purpurConfig.villagerAcquirePoiSearchRadius, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes);
|
|
Set<Pair<Holder<PoiType>, BlockPos>> set = new java.util.HashSet<>(poiposes);
|
|
// Paper end - optimise POI access
|
|
Path path = findPathToPois(entity, set);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
index 2ade08d1466660ee1787fa97908002ef56389712..018cc6ff39641157668fca09e64bcddf7d4d3a5c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
@@ -41,17 +41,19 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
private long nextOkStartTime;
|
|
private int timeWorkedSoFar;
|
|
private final List<BlockPos> validFarmlandAroundVillager = Lists.newArrayList();
|
|
+ private boolean clericWartFarmer = false; // Purpur
|
|
|
|
public HarvestFarmland() {
|
|
super(ImmutableMap.of(MemoryModuleType.LOOK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.SECONDARY_JOB_SITE, MemoryStatus.VALUE_PRESENT));
|
|
}
|
|
|
|
protected boolean checkExtraStartConditions(ServerLevel world, Villager entity) {
|
|
- if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!world.purpurConfig.villagerBypassMobGriefing == !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
- } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER) {
|
|
+ } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER && !(world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC)) { // Purpur
|
|
return false;
|
|
} else {
|
|
+ if (!this.clericWartFarmer && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC) this.clericWartFarmer = true; // Purpur
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = entity.blockPosition().mutable();
|
|
|
|
this.validFarmlandAroundVillager.clear();
|
|
@@ -82,6 +84,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
Block block = iblockdata.getBlock();
|
|
Block block1 = world.getBlockState(pos.below()).getBlock();
|
|
|
|
+ if (this.clericWartFarmer) return block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3 || iblockdata.isAir() && block1 == Blocks.SOUL_SAND; // Purpur
|
|
return block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) || iblockdata.isAir() && block1 instanceof FarmBlock;
|
|
}
|
|
|
|
@@ -107,20 +110,20 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
Block block = iblockdata.getBlock();
|
|
Block block1 = world.getBlockState(this.aboveFarmlandPos.below()).getBlock();
|
|
|
|
- if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata)) {
|
|
+ if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) && !this.clericWartFarmer || this.clericWartFarmer && block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3) { // Purpur
|
|
if (CraftEventFactory.callEntityChangeBlockEvent(entity, this.aboveFarmlandPos, iblockdata.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state
|
|
world.destroyBlock(this.aboveFarmlandPos, true, entity);
|
|
} // CraftBukkit
|
|
}
|
|
|
|
- if (iblockdata.isAir() && block1 instanceof FarmBlock && entity.hasFarmSeeds()) {
|
|
+ if (iblockdata.isAir() && (block1 instanceof FarmBlock && !this.clericWartFarmer || this.clericWartFarmer && block1 == Blocks.SOUL_SAND) && entity.hasFarmSeeds()) { // Purpur
|
|
SimpleContainer inventorysubcontainer = entity.getInventory();
|
|
|
|
for (int j = 0; j < inventorysubcontainer.getContainerSize(); ++j) {
|
|
ItemStack itemstack = inventorysubcontainer.getItem(j);
|
|
boolean flag = false;
|
|
|
|
- if (!itemstack.isEmpty() && itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)) {
|
|
+ if (!itemstack.isEmpty() && (itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) || this.clericWartFarmer && itemstack.getItem() == net.minecraft.world.item.Items.NETHER_WART)) {
|
|
Item item = itemstack.getItem();
|
|
|
|
if (item instanceof BlockItem) {
|
|
@@ -136,7 +139,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
}
|
|
|
|
if (flag) {
|
|
- world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), this.clericWartFarmer ? SoundEvents.NETHER_WART_PLANTED : SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur
|
|
itemstack.shrink(1);
|
|
if (itemstack.isEmpty()) {
|
|
inventorysubcontainer.setItem(j, ItemStack.EMPTY);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
index 3513b15f6622bfc134ecfcd9129f81a8acc2c601..6e70579a58a1bf906b176b81713e55318199cef6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
@@ -57,7 +57,7 @@ public class InteractWithDoor {
|
|
|
|
if (iblockdata.is(BlockTags.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition)) { // Purpur
|
|
DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock();
|
|
|
|
if (!blockdoor.isOpen(iblockdata)) {
|
|
@@ -79,7 +79,7 @@ public class InteractWithDoor {
|
|
|
|
if (iblockdata1.is(BlockTags.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition1)) { // Purpur
|
|
DoorBlock blockdoor1 = (DoorBlock) iblockdata1.getBlock();
|
|
|
|
if (!blockdoor1.isOpen(iblockdata1)) {
|
|
@@ -122,7 +122,7 @@ public class InteractWithDoor {
|
|
|
|
if (!iblockdata.is(BlockTags.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) || DoorBlock.requiresRedstone(entity.level(), iblockdata, blockposition)) { // Purpur
|
|
iterator.remove();
|
|
} else {
|
|
DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
index 18dad0825616c4167a0a7555689ee64910a87e09..6945992491027d43eca4f1ca697ad45ce06ded55 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
@@ -46,6 +46,7 @@ public class ShowTradesToPlayer extends Behavior<Villager> {
|
|
|
|
@Override
|
|
public boolean canStillUse(ServerLevel world, Villager entity, long time) {
|
|
+ if (!entity.level().purpurConfig.villagerDisplayTradeItem) return false; // Purpur
|
|
return this.checkExtraStartConditions(world, entity)
|
|
&& this.lookTime > 0
|
|
&& entity.getBrain().getMemory(MemoryModuleType.INTERACTION_TARGET).isPresent();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
index 8508ac7de8cda3127b73e11ff4aee62502e65ead..b1544e028d5a9b84b944e1fb5a12bb163067fb54 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
@@ -59,6 +59,12 @@ public class TradeWithVillager extends Behavior<Villager> {
|
|
throwHalfStack(entity, ImmutableSet.of(Items.WHEAT), villager);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.villagerClericsFarmWarts && world.purpurConfig.villagerClericFarmersThrowWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC && entity.getInventory().countItem(Items.NETHER_WART) > Items.NETHER_WART.getDefaultMaxStackSize() / 2) {
|
|
+ throwHalfStack(entity, ImmutableSet.of(Items.NETHER_WART), villager);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (!this.trades.isEmpty() && entity.getInventory().hasAnyOf(this.trades)) {
|
|
throwHalfStack(entity, this.trades, villager);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
index bb65d46967cb04f611b3c9c97d5732cfb21ede9b..7f4156e4690bbd57f9e9141f008851062cae733d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
@@ -52,8 +52,13 @@ public class VillagerGoalPackages {
|
|
}
|
|
|
|
public static ImmutableList<Pair<Integer, ? extends BehaviorControl<? super Villager>>> getWorkPackage(VillagerProfession profession, float speed) {
|
|
+ // Purpur start
|
|
+ return getWorkPackage(profession, speed, false);
|
|
+ }
|
|
+ public static ImmutableList<Pair<Integer, ? extends BehaviorControl<? super Villager>>> getWorkPackage(VillagerProfession profession, float speed, boolean clericsFarmWarts) {
|
|
+ // Purpur end
|
|
WorkAtPoi workAtPoi;
|
|
- if (profession == VillagerProfession.FARMER) {
|
|
+ if (profession == VillagerProfession.FARMER || (clericsFarmWarts && profession == VillagerProfession.CLERIC)) { // Purpur
|
|
workAtPoi = new WorkAtComposter();
|
|
} else {
|
|
workAtPoi = new WorkAtPoi();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
index 0a608418f87b71d5d71706712e1f82da0d7e4d34..03e7ca83e4c28dfaa5b52bcb100bd542db105970 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
@@ -125,8 +125,10 @@ public class VillagerMakeLove extends Behavior<Villager> {
|
|
return Optional.empty();
|
|
}
|
|
// Move age setting down
|
|
- parent.setAge(6000);
|
|
- partner.setAge(6000);
|
|
+ // Purpur start
|
|
+ parent.setAge(world.purpurConfig.villagerBreedingTicks);
|
|
+ partner.setAge(world.purpurConfig.villagerBreedingTicks);
|
|
+ // Purpur end
|
|
world.addFreshEntityWithPassengers(entityvillager2, CreatureSpawnEvent.SpawnReason.BREEDING);
|
|
// CraftBukkit end
|
|
world.broadcastEntityEvent(entityvillager2, (byte) 12);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
index c8fd5696de7c3623cdb4f498190a5c2708cf843e..e403d9dfeeaa3dcf53be790d761e7e922419efb0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
@@ -29,6 +29,20 @@ public class MoveControl implements Control {
|
|
this.mob = entity;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void setSpeedModifier(double speed) {
|
|
+ this.speedModifier = speed;
|
|
+ }
|
|
+
|
|
+ public void setForward(float forward) {
|
|
+ this.strafeForwards = forward;
|
|
+ }
|
|
+
|
|
+ public void setStrafe(float strafe) {
|
|
+ this.strafeRight = strafe;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean hasWanted() {
|
|
return this.operation == MoveControl.Operation.MOVE_TO;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
index fbfc2f2515ad709b2c1212aef9521e795547d66b..e77bd11af62682d5eca41f6c9e1aed30eb6879ce 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Mob;
|
|
|
|
-public class SmoothSwimmingLookControl extends LookControl {
|
|
+public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
private final int maxYRotFromCenter;
|
|
private static final int HEAD_TILT_X = 10;
|
|
private static final int HEAD_TILT_Y = 20;
|
|
@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.lookAtCooldown > 0) {
|
|
this.lookAtCooldown--;
|
|
this.getYRotD().ifPresent(yaw -> this.mob.yHeadRot = this.rotateTowards(this.mob.yHeadRot, yaw + 20.0F, this.yMaxRotSpeed));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
index 7324da6b7dd2623ce394e3827ff77ef684a3b98b..d0ba8f74cd0d676640776c46df1913852f4a4a2e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
@@ -33,7 +33,7 @@ public class BreakDoorGoal extends DoorInteractGoal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return !super.canUse() ? false : (!getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen());
|
|
+ return !super.canUse() ? false : (!this.mob.level().purpurConfig.zombieBypassMobGriefing == !getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
index 32bb591371fe78ba10a2bc52389ef33978cbc0eb..13f5e5c199688954c263b9e3397e02c9f77bbb92 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
@@ -74,7 +74,7 @@ public class EatBlockGoal extends Goal {
|
|
|
|
final BlockState blockState = this.level.getBlockState(blockposition); // Paper - fix wrong block state
|
|
if (EatBlockGoal.IS_TALL_GRASS.test(blockState)) { // Paper - fix wrong block state
|
|
- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state
|
|
+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state // Purpur
|
|
this.level.destroyBlock(blockposition, false);
|
|
}
|
|
|
|
@@ -83,7 +83,7 @@ public class EatBlockGoal extends Goal {
|
|
BlockPos blockposition1 = blockposition.below();
|
|
|
|
if (this.level.getBlockState(blockposition1).is(Blocks.GRASS_BLOCK)) {
|
|
- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state
|
|
+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state // Purpur
|
|
this.level.levelEvent(2001, blockposition1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState()));
|
|
this.level.setBlock(blockposition1, Blocks.DIRT.defaultBlockState(), 2);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
index df695b444fa2a993d381e2f197182c3e91a68502..0f4f546cd0eda4bd82b47446ae23ac32da8a9556 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
@@ -22,6 +22,7 @@ public class LlamaFollowCaravanGoal extends Goal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
+ if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur
|
|
if (!this.llama.isLeashed() && !this.llama.inCaravan()) {
|
|
List<Entity> list = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0, 4.0, 9.0), entity -> {
|
|
EntityType<?> entityType = entity.getType();
|
|
@@ -71,6 +72,7 @@ public class LlamaFollowCaravanGoal extends Goal {
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
+ if (!this.llama.shouldJoinCaravan) return false; // Purpur
|
|
if (this.llama.inCaravan() && this.llama.getCaravanHead().isAlive() && this.firstIsLeashed(this.llama, 0)) {
|
|
double d = this.llama.distanceToSqr(this.llama.getCaravanHead());
|
|
if (d > 676.0) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
index 9d245d08be61d7edee9138196ae3bf52023e3993..771bb96032149a8573d1de14fa2ab19012c82000 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
@@ -41,7 +41,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- if (!getServerLevel((Entity) this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!getServerLevel(this.removerMob).purpurConfig.zombieBypassMobGriefing == !getServerLevel((Entity) this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
} else if (this.nextStartTick > 0) {
|
|
--this.nextStartTick;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
index b0944fa1f3849dd24cd010fa0a6638f5fd7179d1..d409ae987088df3d47192128401d7491aaabc87c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
@@ -67,7 +67,7 @@ public class RunAroundLikeCrazyGoal extends Goal {
|
|
int i = this.horse.getTemper();
|
|
int j = this.horse.getMaxTemper();
|
|
|
|
- if (j > 0 && this.horse.getRandom().nextInt(j) < i && !CraftEventFactory.callEntityTameEvent(this.horse, ((CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent
|
|
+ if ((this.horse.level().purpurConfig.alwaysTameInCreative && entityhuman.hasInfiniteMaterials()) || (j > 0 && this.horse.getRandom().nextInt(j) < i && !CraftEventFactory.callEntityTameEvent(this.horse, ((CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled())) { // CraftBukkit - fire EntityTameEvent // Purpur
|
|
this.horse.tameWithName(entityhuman);
|
|
return;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
index 137ec75ee803789deb7b1ca93dd9369c9af362b9..ca95d25af3e9a0536868b0c7fd8e7d2ff1154ee3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
@@ -54,6 +54,14 @@ public class SwellGoal extends Goal {
|
|
this.creeper.setSwellDir(-1);
|
|
} else {
|
|
this.creeper.setSwellDir(1);
|
|
+ // Purpur start
|
|
+ if (this.creeper.level().purpurConfig.creeperEncircleTarget) {
|
|
+ net.minecraft.world.phys.Vec3 relative = this.creeper.position().subtract(this.target.position());
|
|
+ relative = relative.yRot((float) Math.PI / 3).normalize().multiply(2, 2, 2);
|
|
+ net.minecraft.world.phys.Vec3 destination = this.target.position().add(relative);
|
|
+ this.creeper.getNavigation().moveTo(destination.x, destination.y, destination.z, 1);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
index 84ab90dd1fe693da71732533ccff940c1007e1b6..179fdf7b7f68db610925a9d7b88879289b1b98b8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
@@ -67,7 +67,7 @@ public class TemptGoal extends Goal {
|
|
}
|
|
|
|
private boolean shouldFollow(LivingEntity entity) {
|
|
- return this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem());
|
|
+ return (this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem())) && (!(this.mob instanceof net.minecraft.world.entity.npc.Villager villager) || !villager.isSleeping()); // Purpur Fix #512
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
|
index 92731b6b593289e9f583c9b705b219e81fcd8e73..9104d7010bda6f9f73b478c11490ef9c53f76da2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
|
@@ -56,7 +56,7 @@ public class NearestBedSensor extends Sensor<Mob> {
|
|
// Paper start - optimise POI access
|
|
java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
|
|
// don't ask me why it's unbounded. ask mojang.
|
|
- io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes);
|
|
+ io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), world.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur
|
|
Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
|
|
// Paper end - optimise POI access
|
|
if (path != null && path.canReach()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
index 1595568f3140a62b0f2236644ac2da11db12af05..d548d1b2686667d809f363cd0ae4444bc3918bf2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
@@ -29,6 +29,13 @@ public class SecondaryPoiSensor extends Sensor<Villager> {
|
|
return;
|
|
}
|
|
// Gale end - Lithium - skip secondary POI sensor if absent
|
|
+ // Purpur start - make sure clerics don't wander to soul sand when the option is off
|
|
+ Brain<?> brain = entity.getBrain();
|
|
+ if (!world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == net.minecraft.world.entity.npc.VillagerProfession.CLERIC) {
|
|
+ brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
ResourceKey<Level> resourceKey = world.dimension();
|
|
BlockPos blockPos = entity.blockPosition();
|
|
List<GlobalPos> list = Lists.newArrayList();
|
|
@@ -45,7 +52,7 @@ public class SecondaryPoiSensor extends Sensor<Villager> {
|
|
}
|
|
}
|
|
|
|
- Brain<?> brain = entity.getBrain();
|
|
+ //Brain<?> brain = entity.getBrain(); // Purpur - moved up
|
|
if (!list.isEmpty()) {
|
|
brain.setMemory(MemoryModuleType.SECONDARY_JOB_SITE, list);
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
index b51a04d3e006bc770006cff790791bc0f6bee77d..886ca1c8a22714bc299ad08659e5279281669bb3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
@@ -64,6 +64,10 @@ public class TargetingConditions {
|
|
return false;
|
|
} else if (this.selector != null && !this.selector.test(target, world)) {
|
|
return false;
|
|
+ // Purpur start
|
|
+ } else if (!world.purpurConfig.idleTimeoutTargetPlayer && target instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) {
|
|
+ return false;
|
|
+ // Purpur end
|
|
} else {
|
|
if (tester == null) {
|
|
if (this.isCombat && (!target.canBeSeenAsEnemy() || world.getDifficulty() == Difficulty.PEACEFUL)) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
index 05ab901becfc7ffe4e4483ac2c7acb2e2a72490f..75445d2fa84620f3d27235a941107db199f563d9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
@@ -47,12 +47,59 @@ public class Bat extends AmbientCreature {
|
|
|
|
public Bat(EntityType<? extends Bat> type, Level world) {
|
|
super(type, world);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur
|
|
if (!world.isClientSide) {
|
|
this.setResting(true);
|
|
}
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED.value(); } // Fixes log spam on clients
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.batRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.batRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.batControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.batMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ if (isResting()) {
|
|
+ setResting(false);
|
|
+ level().levelEvent(null, 1025, new BlockPos(this).above(), 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean isFlapping() {
|
|
return !this.isResting() && (float) this.tickCount % 10.0F == 0.0F;
|
|
@@ -102,7 +149,7 @@ public class Bat extends AmbientCreature {
|
|
protected void pushEntities() {}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
public boolean isResting() {
|
|
@@ -135,6 +182,14 @@ public class Bat extends AmbientCreature {
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
super.customServerAiStep(world);
|
|
BlockPos blockposition = this.blockPosition();
|
|
BlockPos blockposition1 = blockposition.above();
|
|
@@ -213,6 +268,29 @@ public class Bat extends AmbientCreature {
|
|
}
|
|
}
|
|
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.batMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.batScale);
|
|
+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.batFollowRange);
|
|
+ this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level().purpurConfig.batKnockbackResistance);
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.batMovementSpeed);
|
|
+ this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level().purpurConfig.batFlyingSpeed);
|
|
+ this.getAttribute(Attributes.ARMOR).setBaseValue(this.level().purpurConfig.batArmor);
|
|
+ this.getAttribute(Attributes.ARMOR_TOUGHNESS).setBaseValue(this.level().purpurConfig.batArmorToughness);
|
|
+ this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level().purpurConfig.batAttackKnockback);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.batTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.batAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
@@ -232,7 +310,7 @@ public class Bat extends AmbientCreature {
|
|
int i = world.getMaxLocalRawBrightness(pos);
|
|
byte b0 = 4;
|
|
|
|
- if (Bat.isHalloween()) {
|
|
+ if (Bat.isHalloweenSeason(world.getMinecraftWorld())) { // Purpur
|
|
b0 = 7;
|
|
} else if (random.nextBoolean()) {
|
|
return false;
|
|
@@ -242,6 +320,8 @@ public class Bat extends AmbientCreature {
|
|
}
|
|
}
|
|
|
|
+ public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur
|
|
+
|
|
// Gale start - predict Halloween
|
|
/**
|
|
* The 1-indexed month of the year that Halloween starts (inclusive).
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
index 9aedc62b1766f6a7db4da7eba55167d21d698791..9708ed3e00059fdf5d1d60e0c607d0ab153d1d3f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
@@ -87,6 +87,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new PanicGoal(this, 1.25));
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6, 1.4, EntitySelector.NO_SPECTATORS::test));
|
|
this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this));
|
|
@@ -100,7 +101,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
@Override
|
|
public void travel(Vec3 movementInput) {
|
|
if (this.isControlledByLocalInstance() && this.isInWater()) {
|
|
- this.moveRelative(0.01F, movementInput);
|
|
+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
|
|
if (this.getTarget() == null) {
|
|
@@ -161,7 +162,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
protected void playStepSound(BlockPos pos, BlockState state) {
|
|
}
|
|
|
|
- static class FishMoveControl extends MoveControl {
|
|
+ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
|
|
private final AbstractFish fish;
|
|
|
|
FishMoveControl(AbstractFish owner) {
|
|
@@ -169,14 +170,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
this.fish = owner;
|
|
}
|
|
|
|
+ // Purpur start
|
|
@Override
|
|
- public void tick() {
|
|
+ public void purpurTick(Player rider) {
|
|
+ super.purpurTick(rider);
|
|
+ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.fish.isEyeInFluid(FluidTags.WATER)) {
|
|
this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0));
|
|
}
|
|
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) {
|
|
- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f));
|
|
double d = this.wantedX - this.fish.getX();
|
|
double e = this.wantedY - this.fish.getY();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
index 5677dc97ed83652f261100cf391883cfac7d16fe..9987d28ea145f6d0126cb4ea22001e0922fb51bd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
@@ -49,6 +49,7 @@ public abstract class Animal extends AgeableMob {
|
|
@Nullable
|
|
public UUID loveCause;
|
|
public ItemStack breedItem; // CraftBukkit - Add breedItem variable
|
|
+ public abstract int getPurpurBreedTime(); // Purpur
|
|
|
|
protected Animal(EntityType<? extends Animal> type, Level world) {
|
|
super(type, world);
|
|
@@ -157,7 +158,7 @@ public abstract class Animal extends AgeableMob {
|
|
if (this.isFood(itemstack)) {
|
|
int i = this.getAge();
|
|
|
|
- if (!this.level().isClientSide && i == 0 && this.canFallInLove()) {
|
|
+ if (!this.level().isClientSide && i == 0 && this.canFallInLove() && (this.level().purpurConfig.animalBreedingCooldownSeconds <= 0 || !this.level().hasBreedingCooldown(player.getUUID(), this.getClass()))) { // Purpur
|
|
final ItemStack breedCopy = itemstack.copy(); // Paper - Fix EntityBreedEvent copying
|
|
this.usePlayerItem(player, hand, itemstack);
|
|
this.setInLove(player, breedCopy); // Paper - Fix EntityBreedEvent copying
|
|
@@ -261,12 +262,20 @@ public abstract class Animal extends AgeableMob {
|
|
AgeableMob entityageable = this.getBreedOffspring(world, other);
|
|
|
|
if (entityageable != null) {
|
|
- entityageable.setBaby(true);
|
|
- entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F);
|
|
- // CraftBukkit start - call EntityBreedEvent
|
|
+ // Purpur start
|
|
ServerPlayer breeder = Optional.ofNullable(this.getLoveCause()).or(() -> {
|
|
return Optional.ofNullable(other.getLoveCause());
|
|
}).orElse(null);
|
|
+ if (breeder != null && world.purpurConfig.animalBreedingCooldownSeconds > 0) {
|
|
+ if (world.hasBreedingCooldown(breeder.getUUID(), this.getClass())) {
|
|
+ return;
|
|
+ }
|
|
+ world.addBreedingCooldown(breeder.getUUID(), this.getClass());
|
|
+ }
|
|
+ entityageable.setBaby(true);
|
|
+ entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F);
|
|
+ // CraftBukkit start - call EntityBreedEvent
|
|
+ // Purpur end
|
|
int experience = this.getRandom().nextInt(7) + 1;
|
|
EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(entityageable, this, other, breeder, this.breedItem, experience);
|
|
if (entityBreedEvent.isCancelled()) {
|
|
@@ -294,8 +303,10 @@ public abstract class Animal extends AgeableMob {
|
|
entityplayer.awardStat(Stats.ANIMALS_BRED);
|
|
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer, this, entityanimal, entityageable);
|
|
} // Paper
|
|
- this.setAge(6000);
|
|
- entityanimal.setAge(6000);
|
|
+ // Purpur start
|
|
+ this.setAge(this.getPurpurBreedTime());
|
|
+ entityanimal.setAge(entityanimal.getPurpurBreedTime());
|
|
+ // Purpur end
|
|
this.resetLove();
|
|
entityanimal.resetLove();
|
|
worldserver.broadcastEntityEvent(this, (byte) 18);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
index 42276acfeadec6e7aa9a91d3f446f4fedb04829d..dc8df0912c1d18176e18a8f4dc43c4f60f81b659 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
@@ -150,6 +150,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
public Bee(EntityType<? extends Bee> type, Level world) {
|
|
super(type, world);
|
|
this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
|
|
+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur
|
|
// Paper start - Fix MC-167279
|
|
class BeeFlyingMoveControl extends FlyingMoveControl {
|
|
public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) {
|
|
@@ -158,22 +159,69 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ flyingController.purpurTick(mob.getRider());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.mob.getY() <= Bee.this.level().getMinY()) {
|
|
this.mob.setNoGravity(false);
|
|
}
|
|
super.tick();
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
this.moveControl = new BeeFlyingMoveControl(this, 20, true);
|
|
// Paper end - Fix MC-167279
|
|
this.lookControl = new Bee.BeeLookControl(this);
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F);
|
|
- this.setPathfindingMalus(PathType.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(PathType.WATER_BORDER, 16.0F);
|
|
this.setPathfindingMalus(PathType.COCOA, -1.0F);
|
|
this.setPathfindingMalus(PathType.FENCE, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.beeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.beeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.beeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.beeMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
@@ -188,6 +236,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.399999976158142D, true));
|
|
this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
@@ -207,6 +256,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal());
|
|
this.goalSelector.addGoal(8, new Bee.BeeWanderGoal());
|
|
this.goalSelector.addGoal(9, new FloatGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new Bee.BeeHurtByOtherGoal(this)).setAlertOthers(new Class[0]));
|
|
this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this));
|
|
this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
|
|
@@ -376,7 +426,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
}
|
|
|
|
public static boolean isNightOrRaining(Level world) {
|
|
- return world.dimensionType().hasSkyLight() && (world.isNight() || world.isRaining());
|
|
+ return world.dimensionType().hasSkyLight() && ((world.isNight() && !world.purpurConfig.beeCanWorkAtNight) || (world.isRaining() && !world.purpurConfig.beeCanWorkInRain)); // Purpur
|
|
}
|
|
|
|
public void setStayOutOfHiveCountdown(int cannotEnterHiveTicks) {
|
|
@@ -411,6 +461,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.hurtServer(world, this.damageSources().drown(), 1.0F);
|
|
}
|
|
|
|
+ if (flag && !this.level().purpurConfig.beeDiesAfterSting) setHasStung(false); else // Purpur
|
|
if (flag) {
|
|
++this.timeSinceSting;
|
|
if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) {
|
|
@@ -435,6 +486,27 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
return tileentitybeehive != null && tileentitybeehive.isFireNearby();
|
|
}
|
|
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.beeMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.beeScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.beeBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.beeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.beeAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public int getRemainingPersistentAngerTime() {
|
|
return (Integer) this.entityData.get(Bee.DATA_REMAINING_ANGER_TIME);
|
|
@@ -701,16 +773,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.hivePos = pos;
|
|
}
|
|
|
|
- private class BeeLookControl extends LookControl {
|
|
+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
BeeLookControl(final Mob entity) {
|
|
super(entity);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Bee.this.isAngry()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -882,6 +954,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
if (optional.isPresent()) {
|
|
Bee.this.savedFlowerPos = (BlockPos) optional.get();
|
|
Bee.this.navigation.moveTo((double) Bee.this.savedFlowerPos.getX() + 0.5D, (double) Bee.this.savedFlowerPos.getY() + 0.5D, (double) Bee.this.savedFlowerPos.getZ() + 0.5D, 1.2000000476837158D);
|
|
+ new org.purpurmc.purpur.event.entity.BeeFoundFlowerEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos)).callEvent(); // Purpur
|
|
return true;
|
|
} else {
|
|
Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60);
|
|
@@ -925,6 +998,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.pollinating = false;
|
|
Bee.this.navigation.stop();
|
|
Bee.this.remainingCooldownBeforeLocatingNewFlower = 200;
|
|
+ new org.purpurmc.purpur.event.entity.BeeStopPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), Bee.this.savedFlowerPos == null ? null : io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos), Bee.this.hasNectar()).callEvent(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -974,6 +1048,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.setWantedPos();
|
|
}
|
|
|
|
+ if (this.successfulPollinatingTicks == 0) new org.purpurmc.purpur.event.entity.BeeStartedPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos)).callEvent(); // Purpur
|
|
++this.successfulPollinatingTicks;
|
|
if (Bee.this.random.nextFloat() < 0.05F && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) {
|
|
this.lastSoundPlayedTick = this.successfulPollinatingTicks;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
index 471d5727b964922d8e898be9e1d5c30f9d3bac97..4aad4fdc80070f4000e929fff126714fc67050b0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
@@ -100,12 +100,59 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian
|
|
this.reassessTameGoals();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.catRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.catRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.catControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setInSittingPose(false);
|
|
+ setLying(false);
|
|
+ setRelaxStateOne(false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.catMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.catScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.catBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.catTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.catAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.temptGoal = new Cat.CatTemptGoal(this, 0.6D, (itemstack) -> {
|
|
return itemstack.is(ItemTags.CAT_FOOD);
|
|
}, true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5D));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this));
|
|
@@ -118,6 +165,7 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian
|
|
this.goalSelector.addGoal(10, new BreedGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
|
+ this.targetSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, (TargetingConditions.Selector) null));
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
@@ -317,6 +365,14 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian
|
|
return Mth.lerp(tickDelta, this.relaxStateOneAmountO, this.relaxStateOneAmount);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void tame(Player player) {
|
|
+ setCollarColor(level().purpurConfig.catDefaultCollarColor);
|
|
+ super.tame(player);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public Cat getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
@@ -376,6 +432,7 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian
|
|
|
|
@Override
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
Item item = itemstack.getItem();
|
|
InteractionResult enuminteractionresult;
|
|
@@ -470,7 +527,7 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian
|
|
}
|
|
|
|
private void tryToTame(Player player) {
|
|
- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit
|
|
+ if ((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit
|
|
this.tame(player);
|
|
this.setOrderedToSit(true);
|
|
this.level().broadcastEntityEvent(this, (byte) 7);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
index 919406f18bccec676adec133a60a938dc77f81c4..2bf42a1b1aadcce3f94f3377025cf291aee321b3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
@@ -53,10 +53,52 @@ public class Chicken extends Animal {
|
|
this.setPathfindingMalus(PathType.WATER, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.chickenRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.chickenRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.chickenControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.chickenMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.chickenScale);
|
|
+ if (level().purpurConfig.chickenRetaliate) {
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.chickenBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.chickenTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.chickenAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
- this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ // this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); // Purpur - moved down
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, (itemstack) -> {
|
|
return itemstack.is(ItemTags.CHICKEN_FOOD);
|
|
@@ -65,6 +107,14 @@ public class Chicken extends Animal {
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.chickenRetaliate) {
|
|
+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false));
|
|
+ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this));
|
|
+ } else {
|
|
+ this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -73,7 +123,7 @@ public class Chicken extends Animal {
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D);
|
|
+ return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
index 824e5e4fe7619ae46061c3c978c9a044db8c84ab..f0b6118a9995bb41836685bbf94d2e7fb15761eb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
@@ -13,6 +13,33 @@ public class Cod extends AbstractSchoolingFish {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.codRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.codControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.codMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.codTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.codAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public ItemStack getBucketItemStack() {
|
|
return new ItemStack(Items.COD_BUCKET);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
index 3e00bbff266fc71b07014e7e047d77b7f809239f..6b517deec01445de4205eedb1557995a92d3ae67 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
@@ -37,6 +37,7 @@ import org.bukkit.event.player.PlayerBucketFillEvent;
|
|
// CraftBukkit end
|
|
|
|
public class Cow extends Animal {
|
|
+ private boolean isNaturallyAggressiveToPlayers; // Purpur
|
|
|
|
private static final EntityDimensions BABY_DIMENSIONS = EntityType.COW.getDimensions().scale(0.5F).withEyeHeight(0.665F);
|
|
|
|
@@ -44,18 +45,66 @@ public class Cow extends Animal {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.cowRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.cowRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.cowControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.cowBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.cowTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, net.minecraft.world.entity.SpawnGroupData entityData) {
|
|
+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance;
|
|
+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.cowAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D));
|
|
+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, (itemstack) -> {
|
|
- return itemstack.is(ItemTags.COW_FOOD);
|
|
+ return level().purpurConfig.cowFeedMushrooms > 0 && (itemstack.is(net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) || itemstack.is(net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem())) || itemstack.is(ItemTags.COW_FOOD); // Purpur
|
|
}, false));
|
|
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -64,7 +113,7 @@ public class Cow extends Animal {
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D);
|
|
+ return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -94,6 +143,7 @@ public class Cow extends Animal {
|
|
|
|
@Override
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (itemstack.is(Items.BUCKET) && !this.isBaby()) {
|
|
@@ -102,7 +152,7 @@ public class Cow extends Animal {
|
|
|
|
if (event.isCancelled()) {
|
|
player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@@ -111,6 +161,10 @@ public class Cow extends Animal {
|
|
|
|
player.setItemInHand(hand, itemstack1);
|
|
return InteractionResult.SUCCESS;
|
|
+ // Purpur start - feed mushroom to change to mooshroom
|
|
+ } else if (level().purpurConfig.cowFeedMushrooms > 0 && this.getType() != EntityType.MOOSHROOM && isMushroom(itemstack)) {
|
|
+ return this.feedMushroom(player, itemstack);
|
|
+ // Purpur end
|
|
} else {
|
|
return super.mobInteract(player, hand);
|
|
}
|
|
@@ -126,4 +180,66 @@ public class Cow extends Animal {
|
|
public EntityDimensions getDefaultDimensions(Pose pose) {
|
|
return this.isBaby() ? Cow.BABY_DIMENSIONS : super.getDefaultDimensions(pose);
|
|
}
|
|
+
|
|
+ // Purpur start - feed mushroom to change to mooshroom
|
|
+ private int redMushroomsFed = 0;
|
|
+ private int brownMushroomsFed = 0;
|
|
+
|
|
+ private boolean isMushroom(ItemStack stack) {
|
|
+ return stack.getItem() == net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem() || stack.getItem() == net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem();
|
|
+ }
|
|
+
|
|
+ private int incrementFeedCount(ItemStack stack) {
|
|
+ if (stack.getItem() == net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) {
|
|
+ return ++redMushroomsFed;
|
|
+ } else {
|
|
+ return ++brownMushroomsFed;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private InteractionResult feedMushroom(Player player, ItemStack stack) {
|
|
+ level().broadcastEntityEvent(this, (byte) 18); // hearts
|
|
+ playSound(SoundEvents.COW_MILK, 1.0F, 1.0F);
|
|
+ if (incrementFeedCount(stack) < level().purpurConfig.cowFeedMushrooms) {
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.CONSUME; // require 5 mushrooms to transform (prevents mushroom duping)
|
|
+ }
|
|
+ MushroomCow mooshroom = EntityType.MOOSHROOM.create(level(), EntitySpawnReason.CONVERSION);
|
|
+ if (mooshroom == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (stack.getItem() == net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem()) {
|
|
+ mooshroom.setVariant(MushroomCow.Variant.BROWN);
|
|
+ } else {
|
|
+ mooshroom.setVariant(MushroomCow.Variant.RED);
|
|
+ }
|
|
+ mooshroom.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
+ mooshroom.setHealth(this.getHealth());
|
|
+ mooshroom.setAge(getAge());
|
|
+ mooshroom.copyPosition(this);
|
|
+ mooshroom.setYBodyRot(this.yBodyRot);
|
|
+ mooshroom.setYHeadRot(this.getYHeadRot());
|
|
+ mooshroom.yRotO = this.yRotO;
|
|
+ mooshroom.xRotO = this.xRotO;
|
|
+ if (this.hasCustomName()) {
|
|
+ mooshroom.setCustomName(this.getCustomName());
|
|
+ }
|
|
+ if (CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ this.level().addFreshEntity(mooshroom);
|
|
+ this.remove(RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ for (int i = 0; i < 15; ++i) {
|
|
+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, net.minecraft.core.particles.ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
index 5af4d590a9b0f17ba53c6959d9c18bd1269878a4..c1842894f96a567707992d8ff938dbf689dd0df6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
@@ -85,14 +85,99 @@ public class Dolphin extends AgeableWaterCreature {
|
|
return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater();
|
|
};
|
|
public static final float BABY_SCALE = 0.65F;
|
|
+ private boolean isNaturallyAggressiveToPlayers; // Purpur
|
|
+ private int spitCooldown; // Purpur
|
|
|
|
public Dolphin(EntityType<? extends Dolphin> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ class DolphinMoveControl extends SmoothSwimmingMoveControl {
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD;
|
|
+ private final Dolphin dolphin;
|
|
+
|
|
+ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) {
|
|
+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant);
|
|
+ this.dolphin = dolphin;
|
|
+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (dolphin.getRider() != null && dolphin.isControllable()) {
|
|
+ purpurTick(dolphin.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void purpurTick(Player rider) {
|
|
+ if (dolphin.getAirSupply() < 150) {
|
|
+ // if drowning override player WASD controls to find air
|
|
+ super.tick();
|
|
+ } else {
|
|
+ waterMoveControllerWASD.purpurTick(rider);
|
|
+ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur end
|
|
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
|
this.setCanPickUpLoot(true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.dolphinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.dolphinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (spitCooldown == 0 && getRider() != null) {
|
|
+ spitCooldown = level().purpurConfig.dolphinSpitCooldown;
|
|
+
|
|
+ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity();
|
|
+ if (!player.hasPermission("allow.special.dolphin")) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ org.bukkit.Location loc = player.getEyeLocation();
|
|
+ loc.setPitch(loc.getPitch() - 10);
|
|
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector());
|
|
+
|
|
+ org.purpurmc.purpur.entity.DolphinSpit spit = new org.purpurmc.purpur.entity.DolphinSpit(level(), this);
|
|
+ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level().purpurConfig.dolphinSpitSpeed, 5.0F);
|
|
+
|
|
+ level().addFreshEntity(spit);
|
|
+ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.dolphinMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.dolphinScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.dolphinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.dolphinAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) {
|
|
@@ -101,6 +186,7 @@ public class Dolphin extends AgeableWaterCreature {
|
|
SpawnGroupData groupdataentity1 = (SpawnGroupData) Objects.requireNonNullElseGet(entityData, () -> {
|
|
return new AgeableMob.AgeableMobGroupData(0.1F);
|
|
});
|
|
+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance; // Purpur
|
|
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, groupdataentity1);
|
|
}
|
|
@@ -177,17 +263,21 @@ public class Dolphin extends AgeableWaterCreature {
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new BreathAirGoal(this));
|
|
this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
|
|
+ this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this));
|
|
this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D));
|
|
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
|
|
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
|
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(5, new DolphinJumpGoal(this, 10));
|
|
- this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true));
|
|
+ //this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - moved up
|
|
this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal());
|
|
this.goalSelector.addGoal(8, new FollowBoatGoal(this));
|
|
this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers());
|
|
+ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
@@ -231,7 +321,7 @@ public class Dolphin extends AgeableWaterCreature {
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
- return true;
|
|
+ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs;
|
|
}
|
|
|
|
@Override
|
|
@@ -264,6 +354,11 @@ public class Dolphin extends AgeableWaterCreature {
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
+ // Purpur start
|
|
+ if (spitCooldown > 0) {
|
|
+ spitCooldown--;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.isNoAi()) {
|
|
this.setAirSupply(this.getMaxAirSupply());
|
|
} else {
|
|
@@ -412,6 +507,7 @@ public class Dolphin extends AgeableWaterCreature {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
+ if (this.dolphin.level().purpurConfig.dolphinDisableTreasureSearching) return false; // Purpur
|
|
return this.dolphin.gotFish() && this.dolphin.getAirSupply() >= 100;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
index a9a8ebb2cebe668628d5bdb33fa1399e0ab1e08b..ac044be03494c3d6bad6bbc22321445e04d430cc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
@@ -144,6 +144,65 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
this.getNavigation().setRequiredPathLength(32.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.foxRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.foxRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.foxControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setCanPickUpLoot(false);
|
|
+ clearStates();
|
|
+ setIsPouncing(false);
|
|
+ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND));
|
|
+ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onDismount(Player rider) {
|
|
+ super.onDismount(rider);
|
|
+ setCanPickUpLoot(true);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.foxMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.foxScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.foxBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.foxTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.foxAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
@@ -163,6 +222,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
return entityliving instanceof AbstractSchoolingFish;
|
|
});
|
|
this.goalSelector.addGoal(0, new Fox.FoxFloatGoal());
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
|
|
this.goalSelector.addGoal(1, new Fox.FaceplantGoal());
|
|
this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2D));
|
|
@@ -189,6 +249,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal());
|
|
this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F));
|
|
this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal());
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving, worldserver) -> {
|
|
return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID());
|
|
}));
|
|
@@ -338,6 +399,11 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
}
|
|
|
|
private void setTargetGoals() {
|
|
+ // Purpur start - do not add duplicate goals
|
|
+ this.targetSelector.removeGoal(this.landTargetGoal);
|
|
+ this.targetSelector.removeGoal(this.turtleEggTargetGoal);
|
|
+ this.targetSelector.removeGoal(this.fishTargetGoal);
|
|
+ // Purpur end
|
|
if (this.getVariant() == Fox.Variant.RED) {
|
|
this.targetSelector.addGoal(4, this.landTargetGoal);
|
|
this.targetSelector.addGoal(4, this.turtleEggTargetGoal);
|
|
@@ -367,6 +433,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
|
|
public void setVariant(Fox.Variant variant) {
|
|
this.entityData.set(Fox.DATA_TYPE_ID, variant.getId());
|
|
+ this.setTargetGoals(); // Purpur - fix API bug not updating pathfinders on type change
|
|
}
|
|
|
|
List<UUID> getTrustedUUIDs() {
|
|
@@ -702,6 +769,29 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
}
|
|
// Paper end
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) {
|
|
+ if (level().purpurConfig.foxTypeChangesWithTulips) {
|
|
+ ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (getVariant() == Variant.RED && itemstack.getItem() == Items.WHITE_TULIP) {
|
|
+ setVariant(Variant.SNOW);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
|
+ } else if (getVariant() == Variant.SNOW && itemstack.getItem() == Items.ORANGE_TULIP) {
|
|
+ setVariant(Variant.RED);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
// Paper start - Cancellable death event
|
|
protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel world, DamageSource damageSource) {
|
|
@@ -754,16 +844,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
return new Vec3(0.0D, (double) (0.55F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F));
|
|
}
|
|
|
|
- public class FoxLookControl extends LookControl {
|
|
+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public FoxLookControl() {
|
|
super(Fox.this);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Fox.this.isSleeping()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -774,16 +864,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
}
|
|
}
|
|
|
|
- private class FoxMoveControl extends MoveControl {
|
|
+ private class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
public FoxMoveControl() {
|
|
super(Fox.this);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Fox.this.canMove()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -901,8 +991,10 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer2, this.animal, this.partner, entityfox);
|
|
}
|
|
|
|
- this.animal.setAge(6000);
|
|
- this.partner.setAge(6000);
|
|
+ // Purpur start
|
|
+ this.animal.setAge(this.animal.getPurpurBreedTime());
|
|
+ this.partner.setAge(this.partner.getPurpurBreedTime());
|
|
+ // Purpur end
|
|
this.animal.resetLove();
|
|
this.partner.resetLove();
|
|
worldserver.addFreshEntityWithPassengers(entityfox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
|
|
@@ -1288,7 +1380,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Variant> {
|
|
}
|
|
|
|
protected void onReachedTarget() {
|
|
- if (getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (getServerLevel(Fox.this.level()).purpurConfig.foxBypassMobGriefing ^ getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
BlockState iblockdata = Fox.this.level().getBlockState(this.blockPos);
|
|
|
|
if (iblockdata.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
index e07b79ef172095c1800c88342b3ac8dc7703aea2..9a6471d2f1eb1c8af006b70b6bba0b668220fb00 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
@@ -57,13 +57,59 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
private int remainingPersistentAngerTime;
|
|
@Nullable
|
|
private UUID persistentAngerTarget;
|
|
+ @Nullable private UUID summoner; // Purpur
|
|
|
|
public IronGolem(EntityType<? extends IronGolem> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.ironGolemRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ironGolemRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.ironGolemControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ironGolemMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ironGolemScale);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.ironGolemTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.ironGolemAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
|
|
+ if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
|
|
this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F));
|
|
this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false));
|
|
@@ -71,6 +117,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
|
|
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
@@ -135,6 +182,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putBoolean("PlayerCreated", this.isPlayerCreated());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
this.addPersistentAngerSaveData(nbt);
|
|
}
|
|
|
|
@@ -142,6 +190,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.setPlayerCreated(nbt.getBoolean("PlayerCreated"));
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
this.readPersistentAngerSaveData(this.level(), nbt);
|
|
}
|
|
|
|
@@ -267,18 +316,19 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (!itemstack.is(Items.IRON_INGOT)) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else {
|
|
float f = this.getHealth();
|
|
|
|
this.heal(25.0F);
|
|
if (this.getHealth() == f) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else {
|
|
float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
|
|
|
|
this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0F, f1);
|
|
itemstack.consume(1, player);
|
|
+ if (this.level().purpurConfig.ironGolemHealCalm && isAngry() && getHealth() == getMaxHealth()) stopBeingAngry(); // Purpur
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
index 0047c9f3c6dc901944187784e42cd09a3c6b460c..24205e62b95833779a7dff598930c4672a33fa36 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
@@ -65,6 +65,43 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.mooshroomRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.mooshroomRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.mooshroomControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.mooshroomMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.mooshroomBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.mooshroomTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.mooshroomAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
|
|
return world.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : world.getPathfindingCostFromLightLevels(pos);
|
|
@@ -134,7 +171,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops);
|
|
if (event != null) {
|
|
if (event.isCancelled()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
|
|
// Paper end - custom shear drops
|
|
@@ -155,7 +192,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
Optional<SuspiciousStewEffects> optional = this.getEffectsFromItemStack(itemstack);
|
|
|
|
if (optional.isEmpty()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
itemstack.consume(1, player);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
index 0554ee499c452db6c1e6852f5022b1f197adb024..dee59cb4b87845c940ee089aa932aa69dd2539d6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
@@ -65,6 +65,44 @@ public class Ocelot extends Animal {
|
|
this.reassessTrustingGoals();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.ocelotRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ocelotRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.ocelotControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ocelotMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ocelotScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.ocelotBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.ocelotTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.ocelotAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public boolean isTrusting() {
|
|
return (Boolean) this.entityData.get(Ocelot.DATA_TRUSTING);
|
|
}
|
|
@@ -98,12 +136,14 @@ public class Ocelot extends Animal {
|
|
return itemstack.is(ItemTags.OCELOT_FOOD);
|
|
}, true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, this.temptGoal);
|
|
this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F));
|
|
this.goalSelector.addGoal(8, new OcelotAttackGoal(this));
|
|
this.goalSelector.addGoal(9, new BreedGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false));
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
@@ -244,7 +284,7 @@ public class Ocelot extends Animal {
|
|
if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) {
|
|
BlockPos blockposition = this.blockPosition();
|
|
|
|
- if (blockposition.getY() < world.getSeaLevel()) {
|
|
+ if (!level().purpurConfig.ocelotSpawnUnderSeaLevel && blockposition.getY() < world.getSeaLevel()) {
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
index be753557d7ebd6f1e82b1bdb6d60ecc450f72eec..83372c86bd54eedd9b136ddfcbfc67d303058c0a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
@@ -112,6 +112,54 @@ public class Panda extends Animal {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.pandaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pandaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.pandaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setForwardMot(0.0F);
|
|
+ sit(false);
|
|
+ eat(false);
|
|
+ setOnBack(false);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pandaMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pandaScale);
|
|
+ setAttributes();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.pandaBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.pandaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.pandaAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) {
|
|
return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot();
|
|
@@ -271,6 +319,7 @@ public class Panda extends Animal {
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0D));
|
|
this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true));
|
|
@@ -288,6 +337,7 @@ public class Panda extends Animal {
|
|
this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this));
|
|
this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0]));
|
|
}
|
|
|
|
@@ -617,7 +667,10 @@ public class Panda extends Animal {
|
|
|
|
public void setAttributes() {
|
|
if (this.isWeak()) {
|
|
- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0D);
|
|
+ // Purpur start
|
|
+ net.minecraft.world.entity.ai.attributes.AttributeInstance maxHealth = this.getAttribute(Attributes.MAX_HEALTH);
|
|
+ maxHealth.setBaseValue(maxHealth.getValue() / 2);
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (this.isLazy()) {
|
|
@@ -640,7 +693,7 @@ public class Panda extends Animal {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (this.isScared()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else if (this.isOnBack()) {
|
|
this.setOnBack(false);
|
|
return InteractionResult.SUCCESS;
|
|
@@ -679,12 +732,12 @@ public class Panda extends Animal {
|
|
}
|
|
}
|
|
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
return InteractionResult.SUCCESS_SERVER;
|
|
} else {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -729,7 +782,7 @@ public class Panda extends Animal {
|
|
return itemEntity.getItem().is(ItemTags.PANDA_EATS_FROM_GROUND) && itemEntity.isAlive() && !itemEntity.hasPickUpDelay();
|
|
}
|
|
|
|
- private static class PandaMoveControl extends MoveControl {
|
|
+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Panda panda;
|
|
|
|
@@ -739,9 +792,9 @@ public class Panda extends Animal {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.panda.canPerformAction()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
index 8883894da73c7d5975a8826d23ee7f542db98b0b..28a9d267099f6c24f71dc5a11179d59c27265a88 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
@@ -126,12 +126,89 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
public Parrot(EntityType<? extends Parrot> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new FlyingMoveControl(this, 10, false);
|
|
+ // Purpur start
|
|
+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F);
|
|
+ class ParrotMoveControl extends FlyingMoveControl {
|
|
+ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) {
|
|
+ super(entity, maxPitchChange, noGravity);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ flyingController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted();
|
|
+ }
|
|
+ }
|
|
+ this.moveControl = new ParrotMoveControl(this, 10, false);
|
|
+ // Purpur end
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F);
|
|
this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0F);
|
|
this.setPathfindingMalus(PathType.COCOA, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.parrotRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.parrotRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.parrotControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.parrotMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.parrotMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.parrotScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.parrotTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.parrotAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) {
|
|
@@ -150,8 +227,11 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25D));
|
|
+ //this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25D)); // Purpur - move down
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ if (this.level().purpurConfig.parrotBreedable) this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); // Purpur
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.25D)); // Purpur
|
|
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(2, new FollowOwnerGoal(this, 1.0D, 5.0F, 1.0F));
|
|
@@ -250,7 +330,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
}
|
|
|
|
if (!this.level().isClientSide) {
|
|
- if (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit
|
|
+ if ((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled())) { // CraftBukkit // Purpur
|
|
this.tame(player);
|
|
this.level().broadcastEntityEvent(this, (byte) 7);
|
|
} else {
|
|
@@ -258,6 +338,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
}
|
|
}
|
|
|
|
+ if (this.level().purpurConfig.parrotBreedable) return super.mobInteract(player, hand); // Purpur
|
|
return InteractionResult.SUCCESS;
|
|
} else if (!itemstack.is(ItemTags.PARROT_POISONOUS_FOOD)) {
|
|
if (!this.isFlying() && this.isTame() && this.isOwnedBy(player)) {
|
|
@@ -282,7 +363,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
public boolean isFood(ItemStack stack) {
|
|
- return false;
|
|
+ return this.level().purpurConfig.parrotBreedable && stack.is(ItemTags.PARROT_FOOD); // Purpur
|
|
}
|
|
|
|
public static boolean checkParrotSpawnRules(EntityType<Parrot> type, LevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) {
|
|
@@ -294,13 +375,13 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
public boolean canMate(Animal other) {
|
|
- return false;
|
|
+ return super.canMate(other); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
- return null;
|
|
+ return world.purpurConfig.parrotBreedable ? EntityType.PARROT.create(world, EntitySpawnReason.BREEDING) : null; // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pig.java b/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
index c39b2580a67c9b0bf8631f108e0628fa9732ada1..7895fca01c20da24a10ac6a642ebba87f73f2799 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
@@ -65,9 +65,48 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
|
|
this.steering = new ItemBasedSteering(this.entityData, Pig.DATA_BOOST_TIME, Pig.DATA_SADDLE_ID);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.pigRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pigRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.pigControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pigMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pigScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.pigBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.pigTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.pigAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(3, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(4, new TemptGoal(this, 1.2D, (itemstack) -> {
|
|
@@ -156,6 +195,17 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
boolean flag = this.isFood(player.getItemInHand(hand));
|
|
|
|
+ if (level().purpurConfig.pigGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) {
|
|
+ this.steering.setSaddle(false);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ ItemStack saddle = new ItemStack(Items.SADDLE);
|
|
+ if (!player.getInventory().add(saddle)) {
|
|
+ player.drop(saddle, false);
|
|
+ }
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+
|
|
if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
|
|
if (!this.level().isClientSide) {
|
|
player.startRiding(this);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
index cd72d8f766069796ce1fe4a83b8646692005ff8c..86c44912a363401bdd716c22d24dfd7e92cfd0be 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
@@ -59,11 +59,82 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
private int remainingPersistentAngerTime;
|
|
@Nullable
|
|
private UUID persistentAngerTarget;
|
|
+ private int standTimer = 0; // Purpur
|
|
|
|
public PolarBear(EntityType<? extends PolarBear> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.polarBearRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.polarBearRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.polarBearControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (!isStanding()) {
|
|
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) {
|
|
+ setStanding(true);
|
|
+ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F);
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.polarBearMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.polarBearScale);
|
|
+ }
|
|
+
|
|
+ public boolean canMate(Animal other) {
|
|
+ if (other == this) {
|
|
+ return false;
|
|
+ } else if (this.isStanding()) {
|
|
+ return false;
|
|
+ } else if (this.getTarget() != null) {
|
|
+ return false;
|
|
+ } else if (!(other instanceof PolarBear)) {
|
|
+ return false;
|
|
+ } else {
|
|
+ PolarBear bear = (PolarBear) other;
|
|
+ if (bear.isStanding()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (bear.getTarget() != null) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.isInLove() && bear.isInLove();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.polarBearBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.polarBearTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.polarBearAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
@@ -72,20 +143,28 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean isFood(ItemStack stack) {
|
|
- return false;
|
|
+ return level().purpurConfig.polarBearBreedableItem != null && stack.getItem() == level().purpurConfig.polarBearBreedableItem; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal());
|
|
this.goalSelector
|
|
.addGoal(1, new PanicGoal(this, 2.0, polarBear -> polarBear.isBaby() ? DamageTypeTags.PANIC_CAUSES : DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES));
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.polarBearBreedableItem != null) {
|
|
+ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D));
|
|
+ this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, net.minecraft.world.item.crafting.Ingredient.of(level().purpurConfig.polarBearBreedableItem), false));
|
|
+ }
|
|
+ // Purpur end
|
|
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25));
|
|
this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal());
|
|
this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal());
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
@@ -204,6 +283,12 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
if (!this.level().isClientSide) {
|
|
this.updatePersistentAnger((ServerLevel)this.level(), true);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ if (isStanding() && --standTimer <= 0) {
|
|
+ setStanding(false);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -223,6 +308,7 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
|
|
public void setStanding(boolean warning) {
|
|
this.entityData.set(DATA_STANDING_ID, warning);
|
|
+ standTimer = warning ? 20 : -1; // Purpur
|
|
}
|
|
|
|
public float getStandingAnimationScale(float tickDelta) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
index cdb74f86ee92ee143af29962a85d45ca585cee44..c5a39ea2ad0e5e5ac434d79c1a57e0068b8bc809 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
@@ -52,6 +52,33 @@ public class Pufferfish extends AbstractFish {
|
|
this.refreshDimensions();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.pufferfishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.pufferfishControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pufferfishMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.pufferfishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.pufferfishAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
index 8cc6022507c97af62fb2b4455198bc35744137c9..b3a0146ccfcda9fa33b91d33458086b510bb4d7b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
@@ -88,6 +88,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
private boolean wasOnGround;
|
|
private int jumpDelayTicks;
|
|
public int moreCarrotTicks;
|
|
+ private boolean actualJump; // Purpur
|
|
|
|
public Rabbit(EntityType<? extends Rabbit> type, Level world) {
|
|
super(type, world);
|
|
@@ -95,9 +96,76 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
this.moveControl = new Rabbit.RabbitMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.rabbitRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.rabbitRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.rabbitControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (onGround) {
|
|
+ actualJump = true;
|
|
+ jumpFromGround();
|
|
+ actualJump = false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private void handleJumping() {
|
|
+ if (onGround) {
|
|
+ RabbitJumpControl jumpController = (RabbitJumpControl) jumpControl;
|
|
+ if (!wasOnGround) {
|
|
+ setJumping(false);
|
|
+ jumpController.setCanJump(false);
|
|
+ }
|
|
+ if (!jumpController.wantJump()) {
|
|
+ if (moveControl.hasWanted()) {
|
|
+ startJumping();
|
|
+ }
|
|
+ } else if (!jumpController.canJump()) {
|
|
+ jumpController.setCanJump(true);
|
|
+ }
|
|
+ }
|
|
+ wasOnGround = onGround;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.rabbitMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.rabbitScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.rabbitBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.rabbitTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.rabbitAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
|
|
this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 0.8D));
|
|
@@ -114,6 +182,14 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
|
|
@Override
|
|
protected float getJumpPower() {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ if (getForwardMot() < 0) {
|
|
+ setSpeed(getForwardMot() * 2F);
|
|
+ }
|
|
+ return actualJump ? 0.5F : 0.3F;
|
|
+ }
|
|
+ // Purpur end
|
|
float f = 0.3F;
|
|
|
|
if (this.horizontalCollision || this.moveControl.hasWanted() && this.moveControl.getWantedY() > this.getY() + 0.5D) {
|
|
@@ -188,6 +264,12 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
|
|
@Override
|
|
public void customServerAiStep(ServerLevel world) {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ handleJumping();
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.jumpDelayTicks > 0) {
|
|
--this.jumpDelayTicks;
|
|
}
|
|
@@ -402,10 +484,23 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
|
|
this.setVariant(entityrabbit_variant);
|
|
+
|
|
+ // Purpur start
|
|
+ if (entityrabbit_variant != Variant.EVIL && world.getLevel().purpurConfig.rabbitNaturalToast > 0D && random.nextDouble() <= world.getLevel().purpurConfig.rabbitNaturalToast) {
|
|
+ setCustomName(Component.translatable("Toast"));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData);
|
|
}
|
|
|
|
private static Rabbit.Variant getRandomRabbitVariant(LevelAccessor world, BlockPos pos) {
|
|
+ // Purpur start
|
|
+ Level level = world.getMinecraftWorld();
|
|
+ if (level.purpurConfig.rabbitNaturalKiller > 0D && world.getRandom().nextDouble() <= level.purpurConfig.rabbitNaturalKiller) {
|
|
+ return Rabbit.Variant.EVIL;
|
|
+ }
|
|
+ // Purpur end
|
|
Holder<Biome> holder = world.getBiome(pos);
|
|
int i = world.getRandom().nextInt(100);
|
|
|
|
@@ -469,7 +564,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
}
|
|
|
|
- private static class RabbitMoveControl extends MoveControl {
|
|
+ private static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Rabbit rabbit;
|
|
private double nextJumpSpeed;
|
|
@@ -480,14 +575,14 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.rabbit.onGround() && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl) this.rabbit.jumpControl).wantJump()) {
|
|
this.rabbit.setSpeedModifier(0.0D);
|
|
} else if (this.hasWanted()) {
|
|
this.rabbit.setSpeedModifier(this.nextJumpSpeed);
|
|
}
|
|
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -549,7 +644,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
@Override
|
|
public boolean canUse() {
|
|
if (this.nextStartTick <= 0) {
|
|
- if (!getServerLevel((Entity) this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!getServerLevel((Entity) this.rabbit).purpurConfig.rabbitBypassMobGriefing == !getServerLevel((Entity) this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
index 661997c39df777b6e332f0a5710e7f63a116a499..1a0c71ed6f3bd3d961f9b30ab29a62d683195355 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
@@ -32,6 +32,33 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder<Salmo
|
|
this.refreshDimensions();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.salmonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.salmonControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.salmonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.salmonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.salmonAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public int getMaxSchoolSize() {
|
|
return 5;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
index a021252871fd93d4fb3f23598d6008f25a350150..6838b6f9229d120bca847e6c418d1e40d794fa4c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
@@ -91,10 +91,49 @@ public class Sheep extends Animal implements Shearable {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.sheepRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.sheepRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.sheepControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.sheepMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.sheepScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.sheepBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.sheepTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.sheepAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.eatBlockGoal = new EatBlockGoal(this);
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.1D, (itemstack) -> {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
index fd9f6c17448a4d87f940eb8f544ecb9669068582..13eecc676e33623e776d32495969f111bf6c0e44 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
@@ -50,17 +50,57 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
|
|
private static final EntityDataAccessor<Byte> DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE);
|
|
private static final byte PUMPKIN_FLAG = 16;
|
|
+ @Nullable private java.util.UUID summoner; // Purpur
|
|
|
|
public SnowGolem(EntityType<? extends SnowGolem> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.snowGolemRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snowGolemRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.snowGolemControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snowGolemMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.snowGolemScale);
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable java.util.UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.snowGolemAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25D, 20, 10.0F));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur
|
|
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entityliving, worldserver) -> {
|
|
return entityliving instanceof Enemy;
|
|
}));
|
|
@@ -80,6 +120,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putBoolean("Pumpkin", this.hasPumpkin());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -88,12 +129,13 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
if (nbt.contains("Pumpkin")) {
|
|
this.setPumpkin(nbt.getBoolean("Pumpkin"));
|
|
}
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level().purpurConfig.snowGolemTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -106,10 +148,11 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
this.hurtServer(worldserver, this.damageSources().melting(), 1.0F); // CraftBukkit - DamageSources.ON_FIRE -> CraftEventFactory.MELTING
|
|
}
|
|
|
|
- if (!worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!worldserver.purpurConfig.snowGolemBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return;
|
|
}
|
|
|
|
+ if (getRider() != null && this.isControllable() && !level().purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden
|
|
BlockState iblockdata = Blocks.SNOW.defaultBlockState();
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
@@ -166,7 +209,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops);
|
|
if (event != null) {
|
|
if (event.isCancelled()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
|
|
// Paper end - custom shear drops
|
|
@@ -178,8 +221,16 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
}
|
|
|
|
return InteractionResult.SUCCESS;
|
|
+ // Purpur start
|
|
+ } else if (level().purpurConfig.snowGolemPutPumpkinBack && !hasPumpkin() && itemstack.getItem() == Blocks.CARVED_PUMPKIN.asItem()) {
|
|
+ setPumpkin(true);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ // Purpur end
|
|
} else {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
index f9fdc600dc680c55219fcbf9bc8f151a733a093c..36a56553702fa6e4a2ac92b3639c210c94faee73 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
@@ -46,13 +46,67 @@ public class Squid extends AgeableWaterCreature {
|
|
|
|
public Squid(EntityType<? extends Squid> type, Level world) {
|
|
super(type, world);
|
|
- //this.random.setSeed((long)this.getId()); // Paper - Share random for entities to make them more random
|
|
+ if (!world.purpurConfig.entitySharedRandom) this.random.setSeed((long)this.getId()); // Paper - Share random for entities to make them more random // Purpur
|
|
this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.squidRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.squidControllable;
|
|
+ }
|
|
+
|
|
+ protected void rotateVectorAroundY(org.bukkit.util.Vector vector, double degrees) {
|
|
+ double rad = Math.toRadians(degrees);
|
|
+ double cos = Math.cos(rad);
|
|
+ double sine = Math.sin(rad);
|
|
+ double x = vector.getX();
|
|
+ double z = vector.getZ();
|
|
+ vector.setX(cos * x - sine * z);
|
|
+ vector.setZ(sine * x + cos * z);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.squidMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.squidScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.phys.AABB getAxisForFluidCheck() {
|
|
+ // Stops squids from floating just over the water
|
|
+ return super.getAxisForFluidCheck().offsetY(level().purpurConfig.squidOffsetWaterCheck);
|
|
+ }
|
|
+
|
|
+ public boolean canFly() {
|
|
+ return this.level().purpurConfig.squidsCanFly;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isInWater() {
|
|
+ return this.wasTouchingWater || canFly();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.squidTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.squidAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Squid.SquidFleeGoal());
|
|
}
|
|
|
|
@@ -127,6 +181,7 @@ public class Squid extends AgeableWaterCreature {
|
|
}
|
|
|
|
if (this.isInWaterOrBubble()) {
|
|
+ if (canFly()) setNoGravity(!wasTouchingWater); // Purpur
|
|
if (this.tentacleMovement < (float) Math.PI) {
|
|
float f = this.tentacleMovement / (float) Math.PI;
|
|
this.tentacleAngle = Mth.sin(f * f * (float) Math.PI) * (float) Math.PI * 0.25F;
|
|
@@ -305,10 +360,41 @@ public class Squid extends AgeableWaterCreature {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ net.minecraft.world.entity.player.Player rider = squid.getRider();
|
|
+ if (rider != null && squid.isControllable()) {
|
|
+ if (rider.jumping) {
|
|
+ squid.onSpacebar();
|
|
+ }
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot();
|
|
+ float speed = (float) squid.getAttributeValue(Attributes.MOVEMENT_SPEED) * 10F;
|
|
+ if (forward < 0.0F) {
|
|
+ speed *= -0.5;
|
|
+ }
|
|
+ org.bukkit.util.Vector dir = rider.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(speed / 20.0F);
|
|
+ if (strafe != 0.0F) {
|
|
+ if (forward == 0.0F) {
|
|
+ dir.setY(0);
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? -90 : 90);
|
|
+ } else if (forward < 0.0F) {
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? 45 : -45);
|
|
+ } else {
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? -45 : 45);
|
|
+ }
|
|
+ }
|
|
+ if (forward != 0.0F || strafe != 0.0F) {
|
|
+ squid.movementVector = new Vec3((float) dir.getX(), (float) dir.getY(), (float) dir.getZ());
|
|
+ } else {
|
|
+ squid.movementVector = Vec3.ZERO;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
int i = this.squid.getNoActionTime();
|
|
if (i > 100) {
|
|
this.squid.movementVector = Vec3.ZERO;
|
|
- } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) {
|
|
+ } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.isInWater() || !this.squid.hasMovementVector()) { // Purpur
|
|
float f = this.squid.getRandom().nextFloat() * (float) (Math.PI * 2);
|
|
this.squid.movementVector = new Vec3(
|
|
(double)(Mth.cos(f) * 0.2F), (double)(-0.1F + this.squid.getRandom().nextFloat() * 0.2F), (double)(Mth.sin(f) * 0.2F)
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
index 8d59d606bdaaea7c64389572b2810b65414a1533..7f9d3177285f6496c4313da63f0fd0cc78266586 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
@@ -67,6 +67,33 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.tropicalFishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.tropicalFishControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.tropicalFishMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.tropicalFishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.tropicalFishAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static String getPredefinedName(int variant) {
|
|
return "entity.minecraft.tropical_fish.predefined." + variant;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
index d6605c15111dbdb6ee61a24822bc0a9aed7198d6..c9e307452a097329c26893673055cfb72a43e4c7 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
@@ -86,6 +86,44 @@ public class Turtle extends Animal {
|
|
this.moveControl = new Turtle.TurtleMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.turtleRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.turtleRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.turtleControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.turtleMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.turtleScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.turtleBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.turtleTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.turtleAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public void setHomePos(BlockPos pos) {
|
|
this.entityData.set(Turtle.HOME_POS, pos);
|
|
}
|
|
@@ -188,6 +226,7 @@ public class Turtle extends Animal {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0D));
|
|
@@ -349,13 +388,15 @@ public class Turtle extends Animal {
|
|
return this.isBaby() ? Turtle.BABY_DIMENSIONS : super.getDefaultDimensions(pose);
|
|
}
|
|
|
|
- private static class TurtleMoveControl extends MoveControl {
|
|
+ private static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Turtle turtle;
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
|
|
|
|
TurtleMoveControl(Turtle turtle) {
|
|
super(turtle);
|
|
this.turtle = turtle;
|
|
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur
|
|
}
|
|
|
|
private void updateSpeed() {
|
|
@@ -375,7 +416,7 @@ public class Turtle extends Animal {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
this.updateSpeed();
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.turtle.getNavigation().isDone()) {
|
|
double d0 = this.wantedX - this.turtle.getX();
|
|
@@ -391,7 +432,7 @@ public class Turtle extends Animal {
|
|
|
|
this.turtle.setYRot(this.rotlerp(this.turtle.getYRot(), f, 90.0F));
|
|
this.turtle.yBodyRot = this.turtle.getYRot();
|
|
- float f1 = (float) (this.speedModifier * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f1 = (float) (this.getSpeedModifier() * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
|
|
this.turtle.setSpeed(Mth.lerp(0.125F, this.turtle.getSpeed(), f1));
|
|
this.turtle.setDeltaMovement(this.turtle.getDeltaMovement().add(0.0D, (double) this.turtle.getSpeed() * d1 * 0.1D, 0.0D));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
index fb84ee1225cd762ef306d1fc3e1baed42c034a3c..420345f130a40c4f59a021a4bdce3e218dc87cde 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
@@ -103,6 +103,37 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
|
|
return entitytypes == EntityType.SHEEP || entitytypes == EntityType.RABBIT || entitytypes == EntityType.FOX;
|
|
};
|
|
+ // Purpur start - rabid wolf spawn chance
|
|
+ private boolean isRabid = false;
|
|
+ private static final TargetingConditions.Selector RABID_PREDICATE = (entity, ignored) -> entity instanceof net.minecraft.server.level.ServerPlayer || entity instanceof net.minecraft.world.entity.Mob;
|
|
+ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_VANILLA = new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR);
|
|
+ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_RABID = new NonTameRandomTargetGoal<>(this, LivingEntity.class, false, RABID_PREDICATE);
|
|
+ private static final class AvoidRabidWolfGoal extends AvoidEntityGoal<Wolf> {
|
|
+ private final Wolf wolf;
|
|
+
|
|
+ public AvoidRabidWolfGoal(Wolf wolf, float distance, double minSpeed, double maxSpeed) {
|
|
+ super(wolf, Wolf.class, distance, minSpeed, maxSpeed);
|
|
+ this.wolf = wolf;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && !this.wolf.isRabid() && this.toAvoid != null && this.toAvoid.isRabid(); // wolves which are not rabid run away from rabid wolves
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.wolf.setTarget(null);
|
|
+ super.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ this.wolf.setTarget(null);
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
private static final float START_HEALTH = 8.0F;
|
|
private static final float TAME_HEALTH = 40.0F;
|
|
private static final float ARMOR_REPAIR_UNIT = 0.125F;
|
|
@@ -124,12 +155,87 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
this.setPathfindingMalus(PathType.DANGER_POWDER_SNOW, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.wolfRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wolfRidableInWater;
|
|
+ }
|
|
+
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setInSittingPose(false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.wolfControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wolfMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.wolfScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.wolfBreedingTicks;
|
|
+ }
|
|
+
|
|
+ public boolean isRabid() {
|
|
+ return this.isRabid;
|
|
+ }
|
|
+
|
|
+ public void setRabid(boolean isRabid) {
|
|
+ this.isRabid = isRabid;
|
|
+ updatePathfinders(true);
|
|
+ }
|
|
+
|
|
+ public void updatePathfinders(boolean modifyEffects) {
|
|
+ this.targetSelector.removeGoal(PATHFINDER_VANILLA);
|
|
+ this.targetSelector.removeGoal(PATHFINDER_RABID);
|
|
+ if (this.isRabid) {
|
|
+ setOwnerUUID(null);
|
|
+ setTame(false, true);
|
|
+ this.targetSelector.addGoal(5, PATHFINDER_RABID);
|
|
+ if (modifyEffects) this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.CONFUSION, 1200));
|
|
+ } else {
|
|
+ this.targetSelector.addGoal(5, PATHFINDER_VANILLA);
|
|
+ this.stopBeingAngry();
|
|
+ if (modifyEffects) this.removeEffect(net.minecraft.world.effect.MobEffects.CONFUSION);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tame(Player player) {
|
|
+ setCollarColor(level().purpurConfig.wolfDefaultCollarColor);
|
|
+ super.tame(player);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.wolfTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.wolfAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5D, DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D));
|
|
+ this.goalSelector.addGoal(3, new AvoidRabidWolfGoal(this, 24.0F, 1.5D, 1.5D)); // Purpur
|
|
this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F));
|
|
this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0D, true));
|
|
this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F));
|
|
@@ -138,11 +244,12 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
this.goalSelector.addGoal(9, new BegGoal(this, 8.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(10, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
|
|
this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
|
|
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
- this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR));
|
|
+ // this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR)); // Purpur - moved to updatePathfinders()
|
|
this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false));
|
|
this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true));
|
|
@@ -191,6 +298,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putByte("CollarColor", (byte) this.getCollarColor().getId());
|
|
+ nbt.putBoolean("Purpur.IsRabid", this.isRabid); // Purpur
|
|
this.getVariant().unwrapKey().ifPresent((resourcekey) -> {
|
|
nbt.putString("variant", resourcekey.location().toString());
|
|
});
|
|
@@ -208,6 +316,10 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
if (nbt.contains("CollarColor", 99)) {
|
|
this.setCollarColor(DyeColor.byId(nbt.getInt("CollarColor")));
|
|
}
|
|
+ // Purpur start
|
|
+ this.isRabid = nbt.getBoolean("Purpur.IsRabid");
|
|
+ this.updatePathfinders(false);
|
|
+ // Purpur end
|
|
|
|
this.readPersistentAngerSaveData(this.level(), nbt);
|
|
}
|
|
@@ -226,6 +338,12 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
}
|
|
|
|
this.setVariant(holder1);
|
|
+
|
|
+ // Purpur start
|
|
+ this.isRabid = world.getLevel().purpurConfig.wolfNaturalRabid > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.wolfNaturalRabid;
|
|
+ this.updatePathfinders(false);
|
|
+ // Purpur end
|
|
+
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData);
|
|
}
|
|
|
|
@@ -269,6 +387,11 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
public void tick() {
|
|
super.tick();
|
|
if (this.isAlive()) {
|
|
+ // Purpur start
|
|
+ if (this.age % 300 == 0 && this.isRabid()) {
|
|
+ this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.CONFUSION, 400));
|
|
+ }
|
|
+ // Purpur end
|
|
this.interestedAngleO = this.interestedAngle;
|
|
if (this.isInterested()) {
|
|
this.interestedAngle += (1.0F - this.interestedAngle) * 0.4F;
|
|
@@ -499,6 +622,19 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
itemstack.consume(1, player);
|
|
this.tryToTame(player);
|
|
return InteractionResult.SUCCESS_SERVER;
|
|
+ // Purpur start
|
|
+ } else if (this.level().purpurConfig.wolfMilkCuresRabies && itemstack.getItem() == Items.MILK_BUCKET && this.isRabid()) {
|
|
+ if (!player.isCreative()) {
|
|
+ player.setItemInHand(hand, new ItemStack(Items.BUCKET));
|
|
+ }
|
|
+ this.setRabid(false);
|
|
+ for (int i = 0; i < 10; ++i) {
|
|
+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 1.5), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS_SERVER;
|
|
+ // Purpur end
|
|
} else {
|
|
return super.mobInteract(player, hand);
|
|
}
|
|
@@ -506,7 +642,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol
|
|
|
|
private void tryToTame(Player player) {
|
|
// CraftBukkit - added event call and isCancelled check.
|
|
- if (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) {
|
|
+ if ((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) {
|
|
this.tame(player);
|
|
this.navigation.stop();
|
|
this.setTarget((LivingEntity) null);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
index 5470b000760728b6703fccab5448a3f62d4335c8..b9f39d873e243da34aafa9f285978d2d9f6b0100 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
@@ -102,10 +102,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
|
|
private float spinningAnimationTicks;
|
|
private float spinningAnimationTicks0;
|
|
public boolean forceDancing = false; // CraftBukkit
|
|
+ private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur
|
|
|
|
public Allay(EntityType<? extends Allay> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new FlyingMoveControl(this, 20, true);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.1F, 0.5F);
|
|
+ this.moveControl = new FlyingMoveControl(this, 20, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.setCanPickUpLoot(this.canPickUpLoot());
|
|
this.vibrationUser = new Allay.VibrationUser();
|
|
this.vibrationData = new VibrationSystem.Data();
|
|
@@ -119,6 +132,34 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.allayRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.allayRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.allayControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.allayMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.allayScale);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Allay> brainProvider() {
|
|
return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES);
|
|
@@ -220,7 +261,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
AllayAi.updateActivity(this);
|
|
super.customServerAiStep(world);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
|
index 844a5a61428234adcc12e7e8af8f2781a4ad4be8..35e89ecad020a2a5320a757b154fbf409bb2b19b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
|
@@ -80,6 +80,34 @@ public class Armadillo extends Animal {
|
|
return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 12.0D).add(Attributes.MOVEMENT_SPEED, 0.14D);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.armadilloRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.armadilloRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.armadilloControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.armadilloMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.armadilloScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.armadilloBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
index dab55d73069018b17769b82a79acb5b8fe62772d..7e0b7485e06289478976c7a289a8fa5fe4974d99 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
@@ -98,6 +98,44 @@ public class Axolotl extends Animal implements VariantHolder<Axolotl.Variant>, B
|
|
this.lookControl = new Axolotl.AxolotlLookControl(this, 20);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.axolotlRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.axolotlControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.axolotlMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.axolotlScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.axolotlBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.axolotlTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.axolotlAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
|
|
return 0.0F;
|
|
@@ -293,7 +331,7 @@ public class Axolotl extends Animal implements VariantHolder<Axolotl.Variant>, B
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
AxolotlAi.updateActivity(this);
|
|
if (!this.isNoAi()) {
|
|
@@ -514,14 +552,22 @@ public class Axolotl extends Animal implements VariantHolder<Axolotl.Variant>, B
|
|
private static class AxolotlMoveControl extends SmoothSwimmingMoveControl {
|
|
|
|
private final Axolotl axolotl;
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
|
|
|
|
public AxolotlMoveControl(Axolotl axolotl) {
|
|
super(axolotl, 85, 10, 0.1F, 0.5F, false);
|
|
this.axolotl = axolotl;
|
|
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(axolotl, 0.5D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (axolotl.getRider() != null && axolotl.isControllable()) {
|
|
+ waterController.purpurTick(axolotl.getRider());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (!this.axolotl.isPlayingDead()) {
|
|
super.tick();
|
|
}
|
|
@@ -536,9 +582,9 @@ public class Axolotl extends Animal implements VariantHolder<Axolotl.Variant>, B
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Axolotl.this.isPlayingDead()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
index 002e8b5a7037e48a7d9bd84c321b152f40fe1fce..542b972b266eae642b220025c6173c738fcff80f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
@@ -85,6 +85,17 @@ public class Camel extends AbstractHorse {
|
|
navigation.setCanWalkOverFences(true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater;
|
|
+ }
|
|
+
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.camelBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
@@ -306,6 +317,23 @@ public class Camel extends AbstractHorse {
|
|
return this.dashCooldown;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.camelMaxHealthMin, this.level().purpurConfig.camelMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.camelJumpStrengthMin, this.level().purpurConfig.camelJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.camelMovementSpeedMin, this.level().purpurConfig.camelMovementSpeedMax);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.CAMEL_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
index e6e12142c24e7fff99423678bc47c2e1d38e8afd..0f077f9be003a17b77e9b29fa2f398a4de1fa3c5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
@@ -104,6 +104,8 @@ public class Frog extends Animal implements VariantHolder<Holder<FrogVariant>> {
|
|
public final AnimationState croakAnimationState = new AnimationState();
|
|
public final AnimationState tongueAnimationState = new AnimationState();
|
|
public final AnimationState swimIdleAnimationState = new AnimationState();
|
|
+ private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur
|
|
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur
|
|
|
|
public Frog(EntityType<? extends Animal> type, Level world) {
|
|
super(type, world);
|
|
@@ -111,6 +113,58 @@ public class Frog extends Animal implements VariantHolder<Holder<FrogVariant>> {
|
|
this.setPathfindingMalus(PathType.WATER, 4.0F);
|
|
this.setPathfindingMalus(PathType.TRAPDOOR, -1.0F);
|
|
this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ this.purpurLandController = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.2F);
|
|
+ this.purpurWaterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
|
|
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ net.minecraft.world.entity.player.Player rider = mob.getRider();
|
|
+ if (rider != null && mob.isControllable()) {
|
|
+ if (mob.isInWater()) {
|
|
+ purpurWaterController.purpurTick(rider);
|
|
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, -0.005D, 0.0D));
|
|
+ } else {
|
|
+ purpurLandController.purpurTick(rider);
|
|
+ }
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.frogRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.frogRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.frogControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return (getRider() != null && isControllable()) ? level().purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.frogBreedingTicks;
|
|
}
|
|
|
|
@Override
|
|
@@ -185,7 +239,7 @@ public class Frog extends Animal implements VariantHolder<Holder<FrogVariant>> {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
FrogAi.updateActivity(this);
|
|
super.customServerAiStep(world);
|
|
@@ -379,7 +433,7 @@ public class Frog extends Animal implements VariantHolder<Holder<FrogVariant>> {
|
|
return world.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos);
|
|
}
|
|
|
|
- class FrogLookControl extends LookControl {
|
|
+ class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
FrogLookControl(final Mob entity) {
|
|
super(entity);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
index db617182d75f3418fef4b34cd8eddbfc90fc5a9e..2b3e722a5c802fbbebb339df4398905e144fd174 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
@@ -49,13 +49,50 @@ public class Tadpole extends AbstractFish {
|
|
protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS);
|
|
protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING);
|
|
public boolean ageLocked; // Paper
|
|
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur
|
|
|
|
public Tadpole(EntityType<? extends AbstractFish> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
|
|
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ Player rider = mob.getRider();
|
|
+ if (rider != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(rider);
|
|
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.002D, 0.0D));
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.tadpoleRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.tadpoleRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.tadpoleControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected PathNavigation createNavigation(Level world) {
|
|
return new WaterBoundPathNavigation(this, world);
|
|
@@ -84,7 +121,7 @@ public class Tadpole extends AbstractFish {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
TadpoleAi.updateActivity(this);
|
|
super.customServerAiStep(world);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
index 509d1d1497c3f015686dbc485b82649a98356a02..e9a42f8a73e17c6a7fe5603686c67424e1f8ed9b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
@@ -91,6 +91,38 @@ public class Goat extends Animal {
|
|
});
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.goatRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.goatRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.goatControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.goatBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.goatTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.goatAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Goat> brainProvider() {
|
|
return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES);
|
|
@@ -193,7 +225,7 @@ public class Goat extends Animal {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
GoatAi.updateActivity(this);
|
|
super.customServerAiStep(world);
|
|
@@ -393,6 +425,7 @@ public class Goat extends Animal {
|
|
|
|
// Paper start - Goat ram API
|
|
public void ram(net.minecraft.world.entity.LivingEntity entity) {
|
|
+ if(!new org.purpurmc.purpur.event.entity.GoatRamEntityEvent((org.bukkit.entity.Goat) getBukkitEntity(), (org.bukkit.entity.LivingEntity) entity.getBukkitLivingEntity()).callEvent()) return; // Purpur
|
|
Brain<Goat> brain = this.getBrain();
|
|
brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position());
|
|
brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
index 8aed30cdbbfdd42c20dcd4c8773c8a0ee21a980d..b258be16f32ffd58ac8406deac9423cb01ca9a5c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
@@ -228,11 +228,59 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
|
|
protected AbstractHorse(EntityType<? extends AbstractHorse> type, Level world) {
|
|
super(type, world);
|
|
+ this.moveControl = new net.minecraft.world.entity.ai.control.MoveControl(this); // Purpur - use vanilla controller
|
|
+ this.lookControl = new net.minecraft.world.entity.ai.control.LookControl(this); // Purpur - use vanilla controller
|
|
this.createInventory();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return false; // vanilla handles
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth(random));
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.generateSpeed(random));
|
|
+ this.getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(this.generateJumpStrength(random));
|
|
+ }
|
|
+
|
|
+ protected double generateMaxHealth(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ int diff = Mth.floor(max - min);
|
|
+ double base = max - diff;
|
|
+ int first = Mth.floor((double) diff / 2);
|
|
+ int rest = diff - first;
|
|
+ return base + random.nextInt(first + 1) + random.nextInt(rest + 1);
|
|
+ }
|
|
+
|
|
+ protected double generateJumpStrength(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ return min + (max - min) * this.random.nextDouble();
|
|
+ }
|
|
+
|
|
+ protected double generateSpeed(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ return min + (max - min) * this.random.nextDouble();
|
|
+ }
|
|
+
|
|
+ protected float generateMaxHealth(RandomSource random) {
|
|
+ return 15.0F + (float) random.nextInt(8) + (float) random.nextInt(9);
|
|
+ }
|
|
+
|
|
+ protected double generateJumpStrength(RandomSource random) {
|
|
+ return 0.4000000059604645D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D;
|
|
+ }
|
|
+
|
|
+ protected double generateSpeed(RandomSource random) {
|
|
+ return (0.44999998807907104D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D) * 0.25D;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D, AbstractHorse.class));
|
|
@@ -243,6 +291,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
if (this.canPerformRearing()) {
|
|
this.goalSelector.addGoal(9, new RandomStandGoal(this));
|
|
}
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
|
|
|
|
this.addBehaviourGoals();
|
|
}
|
|
@@ -1269,7 +1318,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
entityData = new AgeableMob.AgeableMobGroupData(0.2F);
|
|
}
|
|
|
|
- this.randomizeAttributes(world.getRandom());
|
|
+ // this.randomizeAttributes(world.getRandom()); // Purpur - replaced by initAttributes()
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
index 5cafdde956d7a5b00cd5aec5c44849639307363d..dbf9fa551023cc9bf634fd5f5d504c4d689264cd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
@@ -16,6 +16,43 @@ public class Donkey extends AbstractChestedHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.donkeyMaxHealthMin, this.level().purpurConfig.donkeyMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.donkeyJumpStrengthMin, this.level().purpurConfig.donkeyJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.donkeyMovementSpeedMin, this.level().purpurConfig.donkeyMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.donkeyBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.donkeyTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.donkeyAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.DONKEY_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
index b5ec7c8ad0e482930d1a54b590b26093f4e477ea..780cad91fff78bda6264cfd78ff7a408a79e8a2f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
@@ -43,6 +43,43 @@ public class Horse extends AbstractHorse implements VariantHolder<Variant> {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.horseRidableInWater;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.horseMaxHealthMin, this.level().purpurConfig.horseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.horseJumpStrengthMin, this.level().purpurConfig.horseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.horseMovementSpeedMin, this.level().purpurConfig.horseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.horseBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.horseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.horseAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void randomizeAttributes(RandomSource random) {
|
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
index 18bd483fe46de3d9dc129bffbccfba9d4cab9550..8401c7ae749f6300281cbd6b2bfc77f03d5eb9ea 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
@@ -72,11 +72,86 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
private Llama caravanHead;
|
|
@Nullable
|
|
public Llama caravanTail; // Paper
|
|
+ public boolean shouldJoinCaravan = true; // Purpur
|
|
|
|
public Llama(EntityType<? extends Llama> type, Level world) {
|
|
super(type, world);
|
|
this.getNavigation().setRequiredPathLength(40.0F);
|
|
this.maxDomestication = 30; // Paper - Missing entity API; configure max temper instead of a hardcoded value
|
|
+ // Purpur start
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.llamaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.llamaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.llamaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSaddled() {
|
|
+ return super.isSaddled() || (isTamed());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.llamaMaxHealthMin, this.level().purpurConfig.llamaMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.llamaJumpStrengthMin, this.level().purpurConfig.llamaJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.llamaMovementSpeedMin, this.level().purpurConfig.llamaMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.llamaBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.llamaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.llamaAlwaysDropExp;
|
|
}
|
|
|
|
public boolean isTraderLlama() {
|
|
@@ -107,6 +182,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("Variant", this.getVariant().id);
|
|
nbt.putInt("Strength", this.getStrength());
|
|
+ nbt.putBoolean("Purpur.ShouldJoinCaravan", shouldJoinCaravan); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -114,11 +190,13 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
this.setStrength(nbt.getInt("Strength"));
|
|
super.readAdditionalSaveData(nbt);
|
|
this.setVariant(Llama.Variant.byId(nbt.getInt("Variant")));
|
|
+ if (nbt.contains("Purpur.ShouldJoinCaravan")) this.shouldJoinCaravan = nbt.getBoolean("Purpur.ShouldJoinCaravan"); // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(2, new LlamaFollowCaravanGoal(this, 2.0999999046325684D));
|
|
this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25D, 40, 20.0F));
|
|
@@ -131,6 +209,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7D));
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new Llama.LlamaHurtByTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new Llama.LlamaAttackWolfGoal(this));
|
|
}
|
|
@@ -379,6 +458,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
|
|
public void leaveCaravan() {
|
|
if (this.caravanHead != null) {
|
|
+ new org.purpurmc.purpur.event.entity.LlamaLeaveCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity()).callEvent(); // Purpur
|
|
this.caravanHead.caravanTail = null;
|
|
}
|
|
|
|
@@ -386,6 +466,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
}
|
|
|
|
public void joinCaravan(Llama llama) {
|
|
+ if (!this.level().purpurConfig.llamaJoinCaravans || !shouldJoinCaravan || !new org.purpurmc.purpur.event.entity.LlamaJoinCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity(), (org.bukkit.entity.Llama) llama.getBukkitEntity()).callEvent()) return; // Purpur
|
|
this.caravanHead = llama;
|
|
this.caravanHead.caravanTail = this;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
index b108f34393230c090c3e5797f52aa076135915e4..626a7d6391882bed7778be8c74b39e4de5c7c3c2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
@@ -15,6 +15,43 @@ public class Mule extends AbstractChestedHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.muleRidableInWater;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.muleMaxHealthMin, this.level().purpurConfig.muleMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.muleJumpStrengthMin, this.level().purpurConfig.muleJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.muleMovementSpeedMin, this.level().purpurConfig.muleMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.muleBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.muleTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.muleAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.MULE_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
index 7d0197447990bb79b85a4f1529c3ac3eefb03c84..9307c07b8f75caacbde6839893f54700bf85275a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
@@ -42,6 +42,43 @@ public class SkeletonHorse extends AbstractHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isTamed() {
|
|
+ return super.isTamed() || this.level().purpurConfig.skeletonHorseRidable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.skeletonHorseMaxHealthMin, this.level().purpurConfig.skeletonHorseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.skeletonHorseJumpStrengthMin, this.level().purpurConfig.skeletonHorseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.skeletonHorseMovementSpeedMin, this.level().purpurConfig.skeletonHorseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.skeletonHorseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.skeletonHorseAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D);
|
|
}
|
|
@@ -59,7 +96,9 @@ public class SkeletonHorse extends AbstractHorse {
|
|
}
|
|
|
|
@Override
|
|
- protected void addBehaviourGoals() {}
|
|
+ protected void addBehaviourGoals() {
|
|
+ if (level().purpurConfig.skeletonHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this));
|
|
+ }
|
|
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
index 7b43fcf86984cc200f34c189e7a547996416f379..1d765ad53a9d8dd388106bc0cb02f4919a1f8173 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
@@ -33,6 +33,58 @@ public class TraderLlama extends Llama {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.traderLlamaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.traderLlamaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.traderLlamaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSaddled() {
|
|
+ return super.isSaddled() || isTamed();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.traderLlamaMaxHealthMin, this.level().purpurConfig.traderLlamaMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.traderLlamaJumpStrengthMin, this.level().purpurConfig.traderLlamaJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.traderLlamaMovementSpeedMin, this.level().purpurConfig.traderLlamaMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.traderLlamaBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.traderLlamaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.traderLlamaAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public boolean isTraderLlama() {
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
index e89638fc9c87b555651813f615b0ccba10d2a9d0..2b7b0d766ceba4b2261be2d8308170ae795f9cb3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
@@ -33,6 +33,48 @@ public class ZombieHorse extends AbstractHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieHorseRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isTamed() {
|
|
+ return super.isTamed() || this.level().purpurConfig.zombieHorseRidable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level().purpurConfig.zombieHorseMaxHealthMin, this.level().purpurConfig.zombieHorseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level().purpurConfig.zombieHorseJumpStrengthMin, this.level().purpurConfig.zombieHorseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level().purpurConfig.zombieHorseMovementSpeedMin, this.level().purpurConfig.zombieHorseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.zombieHorseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.zombieHorseAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F);
|
|
}
|
|
@@ -78,6 +120,7 @@ public class ZombieHorse extends AbstractHorse {
|
|
|
|
@Override
|
|
protected void addBehaviourGoals() {
|
|
+ if (level().purpurConfig.zombieHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
index b61b41f542bd52965a9fbd79644038c68e7dea98..bd752597c925a68f834939d502a7224e022ae79e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
@@ -88,6 +88,34 @@ public class Sniffer extends Animal {
|
|
this.setPathfindingMalus(PathType.DAMAGE_CAUTIOUS, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.snifferRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snifferRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.snifferControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snifferMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.snifferScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.snifferBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
index 58f303ce22cba0840a577b09ec5c32b13717fa15..a8830f8434d7c9d1a15ddd403d1ea1dbe530ce54 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
@@ -27,6 +27,13 @@ public class EnderDragonPart extends Entity {
|
|
this.name = name;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) {
|
|
+ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
index 7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d..b1db1e92de3a88a0f0e0fdb42b0bf9732095c8a9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
@@ -31,6 +31,12 @@ public class EndCrystal extends Entity {
|
|
private static final EntityDataAccessor<Boolean> DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN);
|
|
public int time;
|
|
public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals
|
|
+ // Purpur start
|
|
+ private net.minecraft.world.entity.monster.Phantom targetPhantom;
|
|
+ private int phantomBeamTicks = 0;
|
|
+ private int phantomDamageCooldown = 0;
|
|
+ private int idleCooldown = 0;
|
|
+ // Purpur end
|
|
|
|
public EndCrystal(EntityType<? extends EndCrystal> type, Level world) {
|
|
super(type, world);
|
|
@@ -43,6 +49,22 @@ public class EndCrystal extends Entity {
|
|
this.setPos(x, y, z);
|
|
}
|
|
|
|
+ public boolean shouldExplode() {
|
|
+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplode : level().purpurConfig.baselessEndCrystalExplode;
|
|
+ }
|
|
+
|
|
+ public float getExplosionPower() {
|
|
+ return (float) (showsBottom() ? level().purpurConfig.basedEndCrystalExplosionPower : level().purpurConfig.baselessEndCrystalExplosionPower);
|
|
+ }
|
|
+
|
|
+ public boolean hasExplosionFire() {
|
|
+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionFire : level().purpurConfig.baselessEndCrystalExplosionFire;
|
|
+ }
|
|
+
|
|
+ public Level.ExplosionInteraction getExplosionEffect() {
|
|
+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionEffect : level().purpurConfig.baselessEndCrystalExplosionEffect;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected Entity.MovementEmission getMovementEmission() {
|
|
return Entity.MovementEmission.NONE;
|
|
@@ -80,8 +102,52 @@ public class EndCrystal extends Entity {
|
|
}
|
|
}
|
|
// Paper end - Fix invulnerable end crystals
|
|
+ if (this.level().purpurConfig.endCrystalCramming > 0 && this.level().getEntitiesOfClass(EndCrystal.class, getBoundingBox()).size() > this.level().purpurConfig.endCrystalCramming) this.hurt(this.damageSources().cramming(), 6.0F); // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) {
|
|
+ return; // on cooldown
|
|
+ }
|
|
+
|
|
+ if (targetPhantom == null) {
|
|
+ for (net.minecraft.world.entity.monster.Phantom phantom : level().getEntitiesOfClass(net.minecraft.world.entity.monster.Phantom.class, getBoundingBox().inflate(level().purpurConfig.phantomAttackedByCrystalRadius))) {
|
|
+ if (phantom.hasLineOfSight(this)) {
|
|
+ attackPhantom(phantom);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ setBeamTarget(new BlockPos(targetPhantom).offset(0, -2, 0));
|
|
+ if (--phantomBeamTicks > 0 && targetPhantom.isAlive()) {
|
|
+ phantomDamageCooldown--;
|
|
+ if (targetPhantom.hasLineOfSight(this)) {
|
|
+ if (phantomDamageCooldown <= 0) {
|
|
+ phantomDamageCooldown = 20;
|
|
+ targetPhantom.hurt(targetPhantom.damageSources().indirectMagic(this, this), level().purpurConfig.phantomAttackedByCrystalDamage);
|
|
+ }
|
|
+ } else {
|
|
+ forgetPhantom(); // no longer in sight
|
|
+ }
|
|
+ } else {
|
|
+ forgetPhantom(); // attacked long enough
|
|
+ }
|
|
}
|
|
+ }
|
|
+
|
|
+ private void attackPhantom(net.minecraft.world.entity.monster.Phantom phantom) {
|
|
+ phantomDamageCooldown = 0;
|
|
+ phantomBeamTicks = 60;
|
|
+ targetPhantom = phantom;
|
|
+ }
|
|
|
|
+ private void forgetPhantom() {
|
|
+ targetPhantom = null;
|
|
+ setBeamTarget(null);
|
|
+ phantomBeamTicks = 0;
|
|
+ phantomDamageCooldown = 0;
|
|
+ idleCooldown = 60;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -128,16 +194,18 @@ public class EndCrystal extends Entity {
|
|
}
|
|
// CraftBukkit end
|
|
if (!source.is(DamageTypeTags.IS_EXPLOSION)) {
|
|
+ if (shouldExplode()) {// Purpur
|
|
DamageSource damagesource1 = source.getEntity() != null ? this.damageSources().explosion(this, source.getEntity()) : null;
|
|
|
|
// CraftBukkit start
|
|
- ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, 6.0F, false);
|
|
+ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, getExplosionPower(), hasExplosionFire()); // Purpur
|
|
if (event.isCancelled()) {
|
|
return false;
|
|
}
|
|
|
|
this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
|
|
- world.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK);
|
|
+ world.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), getExplosionEffect()); // Purpur
|
|
+ } else this.unsetRemoved(); // Purpur
|
|
} else {
|
|
this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
index 2df8bf818345246cc1f88b93e4a3b62e61772efb..c9e3bb91ff506a35551a58f469ad2849e6058668 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
@@ -108,6 +108,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
@Nullable
|
|
private BlockPos podium;
|
|
// Paper end - Allow changing the EnderDragon podium
|
|
+ private boolean hadRider; // Purpur
|
|
|
|
public EnderDragon(EntityType<? extends EnderDragon> entitytypes, Level world) {
|
|
super(EntityType.ENDER_DRAGON, world);
|
|
@@ -129,6 +130,37 @@ public class EnderDragon extends Mob implements Enemy {
|
|
this.noPhysics = true;
|
|
this.phaseManager = new EnderDragonPhaseManager(this);
|
|
this.explosionSource = new ServerExplosion(world.getMinecraftWorld(), this, null, null, new Vec3(Double.NaN, Double.NaN, Double.NaN), Float.NaN, true, Explosion.BlockInteraction.DESTROY); // CraftBukkit
|
|
+
|
|
+ // Purpur start
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this) {
|
|
+ @Override
|
|
+ public void vanillaTick() {
|
|
+ // dragon doesn't use the controller. do nothing
|
|
+ }
|
|
+ };
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void vanillaTick() {
|
|
+ // dragon doesn't use the controller. do nothing
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot() - 180F, rider.xRotO * 0.5F);
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.enderDragonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.enderDragonRidableInWater;
|
|
}
|
|
|
|
public void setDragonFight(EndDragonFight fight) {
|
|
@@ -143,6 +175,27 @@ public class EnderDragon extends Mob implements Enemy {
|
|
return this.fightOrigin;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.enderDragonControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.enderDragonMaxY;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.enderDragonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.enderDragonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0D);
|
|
}
|
|
@@ -184,6 +237,37 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
+ // Purpur start
|
|
+ boolean hasRider = getRider() != null && this.isControllable();
|
|
+ if (hasRider) {
|
|
+ if (!hadRider) {
|
|
+ hadRider = true;
|
|
+ noPhysics = false;
|
|
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(4.0F, 2.0F);
|
|
+ }
|
|
+
|
|
+ // dragon doesn't use controllers, so must tick manually
|
|
+ moveControl.tick();
|
|
+ lookControl.tick();
|
|
+
|
|
+ moveRelative((float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F, new Vec3(-getStrafeMot(), getVerticalMot(), -getForwardMot()));
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot);
|
|
+ move(MoverType.PLAYER, mot);
|
|
+
|
|
+ mot = mot.multiply(0.9F, 0.9F, 0.9F);
|
|
+ setDeltaMovement(mot);
|
|
+
|
|
+ // control wing flap speed on client
|
|
+ phaseManager.setPhase(mot.x() * mot.x() + mot.z() * mot.z() < 0.005F ? EnderDragonPhase.HOVERING : EnderDragonPhase.HOLDING_PATTERN);
|
|
+ } else if (hadRider) {
|
|
+ hadRider = false;
|
|
+ noPhysics = true;
|
|
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(16.0F, 8.0F);
|
|
+ phaseManager.setPhase(EnderDragonPhase.HOLDING_PATTERN); // HoldingPattern
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.processFlappingMovement();
|
|
if (this.level().isClientSide) {
|
|
this.setHealth(this.getHealth());
|
|
@@ -210,6 +294,8 @@ public class EnderDragon extends Mob implements Enemy {
|
|
float f;
|
|
|
|
if (this.isDeadOrDying()) {
|
|
+ if (hasRider) ejectPassengers(); // Purpur
|
|
+
|
|
float f1 = (this.random.nextFloat() - 0.5F) * 8.0F;
|
|
|
|
f = (this.random.nextFloat() - 0.5F) * 4.0F;
|
|
@@ -222,9 +308,9 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
f = 0.2F / ((float) vec3d.horizontalDistance() * 10.0F + 1.0F);
|
|
f *= (float) Math.pow(2.0D, vec3d.y);
|
|
- if (this.phaseManager.getCurrentPhase().isSitting()) {
|
|
+ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur
|
|
this.flapTime += 0.1F;
|
|
- } else if (this.inWall) {
|
|
+ } else if (!hasRider && this.inWall) { // Purpur
|
|
this.flapTime += f * 0.5F;
|
|
} else {
|
|
this.flapTime += f;
|
|
@@ -240,7 +326,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
float f4;
|
|
float f5;
|
|
|
|
- if (world1 instanceof ServerLevel) {
|
|
+ if (world1 instanceof ServerLevel && !hasRider) { // Purpur
|
|
ServerLevel worldserver1 = (ServerLevel) world1;
|
|
DragonPhaseInstance idragoncontroller = this.phaseManager.getCurrentPhase();
|
|
|
|
@@ -326,7 +412,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
if (world2 instanceof ServerLevel) {
|
|
ServerLevel worldserver2 = (ServerLevel) world2;
|
|
|
|
- if (this.hurtTime == 0) {
|
|
+ if (!hasRider && this.hurtTime == 0) { // Purpur
|
|
this.knockBack(worldserver2, worldserver2.getEntities((Entity) this, this.wing1.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
this.knockBack(worldserver2, worldserver2.getEntities((Entity) this, this.wing2.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
this.hurt(worldserver2, worldserver2.getEntities((Entity) this, this.head.getBoundingBox().inflate(1.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
@@ -374,7 +460,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
if (world3 instanceof ServerLevel) {
|
|
ServerLevel worldserver3 = (ServerLevel) world3;
|
|
|
|
- this.inWall = this.checkWalls(worldserver3, this.head.getBoundingBox()) | this.checkWalls(worldserver3, this.neck.getBoundingBox()) | this.checkWalls(worldserver3, this.body.getBoundingBox());
|
|
+ this.inWall = !hasRider && this.checkWalls(worldserver3, this.head.getBoundingBox()) | this.checkWalls(worldserver3, this.neck.getBoundingBox()) | this.checkWalls(worldserver3, this.body.getBoundingBox()); // Purpur
|
|
if (this.dragonFight != null) {
|
|
this.dragonFight.updateDragon(this);
|
|
}
|
|
@@ -510,7 +596,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
|
|
if (!iblockdata.isAir() && !iblockdata.is(BlockTags.DRAGON_TRANSPARENT)) {
|
|
- if (world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) {
|
|
+ if ((world.purpurConfig.enderDragonBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { // Purpur
|
|
// CraftBukkit start - Add blocks to list rather than destroying them
|
|
// flag1 = worldserver.removeBlock(blockposition, false) || flag1;
|
|
flag1 = true;
|
|
@@ -651,7 +737,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
boolean flag = worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT);
|
|
short short0 = 500;
|
|
|
|
- if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) {
|
|
+ if (this.dragonFight != null && (level().purpurConfig.enderDragonAlwaysDropsFullExp || !this.dragonFight.hasPreviouslyKilledDragon())) {
|
|
short0 = 12000;
|
|
}
|
|
|
|
@@ -1069,6 +1155,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
+ if (this.level().purpurConfig.enderDragonCanRideVehicles) return this.boardingCooldown <= 0; // Purpur
|
|
return false;
|
|
}
|
|
|
|
@@ -1095,6 +1182,6 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
@Override
|
|
protected float sanitizeScale(float scale) {
|
|
- return 1.0F;
|
|
+ return 1.0F; // Purpur - diff on change
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
index 244e38db508efa3eebebb6392c4ebb0805367baf..ac8f05e20e810e61e4c77aa7a6e41008779563dc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
@@ -86,20 +86,60 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
return !entityliving.getType().is(EntityTypeTags.WITHER_FRIENDS) && entityliving.attackable();
|
|
};
|
|
private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR);
|
|
+ @Nullable private java.util.UUID summoner; // Purpur
|
|
+ private int shootCooldown = 0; // Purpur
|
|
// Paper start
|
|
private boolean canPortal = false;
|
|
|
|
public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; }
|
|
// Paper end
|
|
+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur
|
|
|
|
public WitherBoss(EntityType<? extends WitherBoss> type, Level world) {
|
|
super(type, world);
|
|
this.bossEvent = (ServerBossEvent) (new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS)).setDarkenScreen(true);
|
|
- this.moveControl = new FlyingMoveControl(this, 10, false);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.1F);
|
|
+ this.moveControl = new FlyingMoveControl(this, 10, false) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.setHealth(this.getMaxHealth());
|
|
this.xpReward = 50;
|
|
}
|
|
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witherScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.witherTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable java.util.UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.witherAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected PathNavigation createNavigation(Level world) {
|
|
FlyingPathNavigation navigationflying = new FlyingPathNavigation(this, world);
|
|
@@ -110,13 +150,114 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
return navigationflying;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.witherRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.witherControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.witherMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 5F;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.5, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ this.entityData.set(DATA_TARGETS.get(0), 0);
|
|
+ this.entityData.set(DATA_TARGETS.get(1), 0);
|
|
+ this.entityData.set(DATA_TARGETS.get(2), 0);
|
|
+ getNavigation().stop();
|
|
+ shootCooldown = 20;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onClick(net.minecraft.world.InteractionHand hand) {
|
|
+ return shoot(getRider(), hand == net.minecraft.world.InteractionHand.MAIN_HAND ? new int[]{1} : new int[]{2});
|
|
+ }
|
|
+
|
|
+ public boolean shoot(@Nullable Player rider, int[] heads) {
|
|
+ if (shootCooldown > 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ shootCooldown = 20;
|
|
+ if (rider == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = rider.getBukkitEntity();
|
|
+ if (!player.hasPermission("allow.special.wither")) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.phys.HitResult rayTrace = getRayTrace(120, net.minecraft.world.level.ClipContext.Fluid.NONE);
|
|
+ if (rayTrace == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ Vec3 loc;
|
|
+ if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) {
|
|
+ BlockPos pos = ((net.minecraft.world.phys.BlockHitResult) rayTrace).getBlockPos();
|
|
+ loc = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D);
|
|
+ } else if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.ENTITY) {
|
|
+ Entity target = ((net.minecraft.world.phys.EntityHitResult) rayTrace).getEntity();
|
|
+ loc = new Vec3(target.getX(), target.getY() + (target.getEyeHeight() / 2), target.getZ());
|
|
+ } else {
|
|
+ org.bukkit.block.Block block = player.getTargetBlock(null, 120);
|
|
+ loc = new Vec3(block.getX() + 0.5D, block.getY() + 0.5D, block.getZ() + 0.5D);
|
|
+ }
|
|
+
|
|
+ for (int head : heads) {
|
|
+ shoot(head, loc.x(), loc.y(), loc.z(), rider);
|
|
+ }
|
|
+
|
|
+ return true; // handled
|
|
+ }
|
|
+
|
|
+ public void shoot(int head, double x, double y, double z, Player rider) {
|
|
+ level().levelEvent(null, 1024, blockPosition(), 0);
|
|
+ double headX = getHeadX(head);
|
|
+ double headY = getHeadY(head);
|
|
+ double headZ = getHeadZ(head);
|
|
+ Vec3 vec3d = new Vec3(x - headX, y - headY, z - headZ);
|
|
+ WitherSkull skull = new WitherSkull(level(), this, vec3d.normalize());
|
|
+ skull.setPosRaw(headX, headY, headZ);
|
|
+ level().addFreshEntity(skull);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal());
|
|
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 40, 20.0F));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, WitherBoss.LIVING_ENTITY_SELECTOR));
|
|
}
|
|
@@ -134,6 +275,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("Invul", this.getInvulnerableTicks());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -143,6 +285,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
if (this.hasCustomName()) {
|
|
this.bossEvent.setName(this.getDisplayName());
|
|
}
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
|
|
}
|
|
|
|
@@ -261,6 +404,15 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
|
|
+ }
|
|
+ if (shootCooldown > 0) {
|
|
+ shootCooldown--;
|
|
+ }
|
|
+ // Purpur end
|
|
int i;
|
|
|
|
if (this.getInvulnerableTicks() > 0) {
|
|
@@ -277,7 +429,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
- if (!this.isSilent()) {
|
|
+ if (!this.isSilent() && level().purpurConfig.witherPlaySpawnSound) {
|
|
// CraftBukkit start - Use relative location for far away sounds
|
|
// worldserver.globalLevelEvent(1023, new BlockPosition(this), 0);
|
|
int viewDistance = world.getCraftServer().getViewDistance() * 16;
|
|
@@ -302,7 +454,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
|
|
this.setInvulnerableTicks(i);
|
|
if (this.tickCount % 10 == 0) {
|
|
- this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit
|
|
+ this.heal(this.getMaxHealth() / 33, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit // Purpur
|
|
}
|
|
|
|
} else {
|
|
@@ -362,7 +514,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
|
|
if (this.destroyBlocksTick > 0) {
|
|
--this.destroyBlocksTick;
|
|
- if (this.destroyBlocksTick == 0 && world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.destroyBlocksTick == 0 && (world.purpurConfig.witherBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur
|
|
boolean flag = false;
|
|
|
|
j = Mth.floor(this.getBbWidth() / 2.0F + 1.0F);
|
|
@@ -389,8 +541,10 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
}
|
|
}
|
|
|
|
- if (this.tickCount % 20 == 0) {
|
|
- this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit
|
|
+ // Purpur start - customizable heal rate and amount
|
|
+ if (this.tickCount % level().purpurConfig.witherHealthRegenDelay == 0) {
|
|
+ this.heal(level().purpurConfig.witherHealthRegenAmount, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit
|
|
+ // Purpur end
|
|
}
|
|
|
|
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
|
|
@@ -579,11 +733,11 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
}
|
|
|
|
public int getAlternativeTarget(int headIndex) {
|
|
- return (Integer) this.entityData.get((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex));
|
|
+ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(WitherBoss.DATA_TARGETS.get(headIndex)); // Purpur
|
|
}
|
|
|
|
public void setAlternativeTarget(int headIndex, int id) {
|
|
- this.entityData.set((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex), id);
|
|
+ if (getRider() == null || !this.isControllable()) this.entityData.set(WitherBoss.DATA_TARGETS.get(headIndex), id); // Purpur
|
|
}
|
|
|
|
public boolean isPowered() {
|
|
@@ -592,6 +746,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
+ if (this.level().purpurConfig.witherCanRideVehicles) return this.boardingCooldown <= 0; // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
index 70b8023c3badc745f342d5b0ab54699e3923826a..037586c0fdb42a02660aba89dd741a647c67e52b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
@@ -114,10 +114,12 @@ public class ArmorStand extends LivingEntity {
|
|
private boolean noTickPoseDirty = false;
|
|
private boolean noTickEquipmentDirty = false;
|
|
// Paper end - Allow ArmorStands not to tick
|
|
+ public boolean canMovementTick = true; // Purpur
|
|
|
|
public ArmorStand(EntityType<? extends ArmorStand> type, Level world) {
|
|
super(type, world);
|
|
if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick
|
|
+ if (world != null) this.canMovementTick = world.purpurConfig.armorstandMovement; // Purpur
|
|
this.handItems = NonNullList.withSize(2, ItemStack.EMPTY);
|
|
this.armorItems = NonNullList.withSize(4, ItemStack.EMPTY);
|
|
this.headPose = ArmorStand.DEFAULT_HEAD_POSE;
|
|
@@ -126,6 +128,7 @@ public class ArmorStand extends LivingEntity {
|
|
this.rightArmPose = ArmorStand.DEFAULT_RIGHT_ARM_POSE;
|
|
this.leftLegPose = ArmorStand.DEFAULT_LEFT_LEG_POSE;
|
|
this.rightLegPose = ArmorStand.DEFAULT_RIGHT_LEG_POSE;
|
|
+ this.setShowArms(world != null && world.purpurConfig.armorstandPlaceWithArms); // Purpur
|
|
}
|
|
|
|
public ArmorStand(Level world, double x, double y, double z) {
|
|
@@ -618,6 +621,7 @@ public class ArmorStand extends LivingEntity {
|
|
private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(ServerLevel world, DamageSource damageSource) { // Paper
|
|
ItemStack itemstack = new ItemStack(Items.ARMOR_STAND);
|
|
|
|
+ if (this.level().purpurConfig.persistentDroppableEntityDisplayNames)
|
|
itemstack.set(DataComponents.CUSTOM_NAME, this.getCustomName());
|
|
this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior
|
|
return this.brokenByAnything(world, damageSource); // Paper
|
|
@@ -681,6 +685,7 @@ public class ArmorStand extends LivingEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ maxUpStep = level().purpurConfig.armorstandStepHeight;
|
|
// Paper start - Allow ArmorStands not to tick
|
|
if (!this.canTick) {
|
|
if (this.noTickPoseDirty) {
|
|
@@ -1013,4 +1018,18 @@ public class ArmorStand extends LivingEntity {
|
|
}
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void updateInWaterStateAndDoWaterCurrentPushing() {
|
|
+ if (this.level().purpurConfig.armorstandWaterMovement &&
|
|
+ (this.level().purpurConfig.armorstandWaterFence || !(level().getBlockState(blockPosition().below()).getBlock() instanceof net.minecraft.world.level.block.FenceBlock)))
|
|
+ super.updateInWaterStateAndDoWaterCurrentPushing();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void aiStep() {
|
|
+ if (this.canMovementTick && this.canMove) super.aiStep();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
index bbdaaa1cc0b4aed28bc39385508d221055b99d4d..bd5e034ce58ebe53d2121209d76ae60134ce72fe 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
@@ -240,7 +240,13 @@ public class ItemFrame extends HangingEntity {
|
|
}
|
|
|
|
if (dropSelf) {
|
|
- this.spawnAtLocation(world, this.getFrameItemStack());
|
|
+ // Purpur start
|
|
+ final ItemStack itemFrame = this.getFrameItemStack();
|
|
+ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ itemFrame.set(DataComponents.CUSTOM_NAME, null);
|
|
+ }
|
|
+ this.spawnAtLocation(world, itemFrame);
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (!itemstack.isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/Painting.java b/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
index fd0e78a2318e3950d011c17358245e107b38154a..0fcab828e81176323cbdf16c0ec714d9a2846ae5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
@@ -179,7 +179,13 @@ public class Painting extends HangingEntity implements VariantHolder<Holder<Pain
|
|
}
|
|
}
|
|
|
|
- this.spawnAtLocation(world, (ItemLike) Items.PAINTING);
|
|
+ // Purpur start
|
|
+ final ItemStack painting = new ItemStack((ItemLike) Items.PAINTING);
|
|
+ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ painting.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, null);
|
|
+ }
|
|
+ this.spawnAtLocation(world, painting);
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
index ca36be3c3189adc99ef04e4ce4307948adda1deb..8162d95c111dc0e05c6993a43e40c3406060e600 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
@@ -64,6 +64,12 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
public boolean canMobPickup = true; // Paper - Item#canEntityPickup
|
|
private int despawnRate = -1; // Paper - Alternative item-despawn-rate
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
|
|
+ // Purpur start
|
|
+ public boolean immuneToCactus = false;
|
|
+ public boolean immuneToExplosion = false;
|
|
+ public boolean immuneToFire = false;
|
|
+ public boolean immuneToLightning = false;
|
|
+ // Purpur end
|
|
|
|
public ItemEntity(EntityType<? extends ItemEntity> type, Level world) {
|
|
super(type, world);
|
|
@@ -410,7 +416,16 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
|
|
@Override
|
|
public final boolean hurtServer(ServerLevel world, DamageSource source, float amount) {
|
|
- if (this.isInvulnerableToBase(source)) {
|
|
+ // Purpur start
|
|
+ if (
|
|
+ (immuneToCactus && source.is(net.minecraft.world.damagesource.DamageTypes.CACTUS)) ||
|
|
+ (immuneToFire && (source.is(net.minecraft.tags.DamageTypeTags.IS_FIRE) || source.is(net.minecraft.world.damagesource.DamageTypes.ON_FIRE) || source.is(net.minecraft.world.damagesource.DamageTypes.IN_FIRE))) ||
|
|
+ (immuneToLightning && source.is(net.minecraft.world.damagesource.DamageTypes.LIGHTNING_BOLT)) ||
|
|
+ (immuneToExplosion && source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION))
|
|
+ ) {
|
|
+ return false;
|
|
+ } else if (this.isInvulnerableToBase(source)) {
|
|
+ // Purpur end
|
|
return false;
|
|
} else if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && source.getEntity() instanceof Mob) {
|
|
return false;
|
|
@@ -621,6 +636,12 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
public void setItem(ItemStack stack) {
|
|
this.getEntityData().set(ItemEntity.DATA_ITEM, stack);
|
|
this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.itemImmuneToCactus.contains(stack.getItem())) immuneToCactus = true;
|
|
+ if (level().purpurConfig.itemImmuneToExplosion.contains(stack.getItem())) immuneToExplosion = true;
|
|
+ if (level().purpurConfig.itemImmuneToFire.contains(stack.getItem())) immuneToFire = true;
|
|
+ if (level().purpurConfig.itemImmuneToLightning.contains(stack.getItem())) immuneToLightning = true;
|
|
+ // level end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
index de87483600e55d88176fe25db621bbd3e464729f..287ba483614e79e78022e703ef891f59f41ac455 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
@@ -249,4 +249,31 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
|
return !level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid();
|
|
}
|
|
// Paper end - Option to prevent TNT from moving in water
|
|
+ // Purpur start - Shears can defuse TNT
|
|
+ @Override
|
|
+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) {
|
|
+ Level world = this.level();
|
|
+
|
|
+ if (world instanceof ServerLevel serverWorld && level().purpurConfig.shearsCanDefuseTnt) {
|
|
+ final net.minecraft.world.item.ItemStack inHand = player.getItemInHand(hand);
|
|
+
|
|
+ if (!inHand.is(net.minecraft.world.item.Items.SHEARS) || !player.getBukkitEntity().hasPermission("purpur.tnt.defuse") ||
|
|
+ serverWorld.random.nextFloat() > serverWorld.purpurConfig.shearsCanDefuseTntChance) return net.minecraft.world.InteractionResult.PASS;
|
|
+
|
|
+ net.minecraft.world.entity.item.ItemEntity tntItem = new net.minecraft.world.entity.item.ItemEntity(serverWorld, getX(), getY(), getZ(),
|
|
+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.TNT));
|
|
+ tntItem.setPickUpDelay(10);
|
|
+
|
|
+ inHand.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand));
|
|
+ serverWorld.addFreshEntity(tntItem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CUSTOM);
|
|
+
|
|
+ this.playSound(net.minecraft.sounds.SoundEvents.SHEEP_SHEAR);
|
|
+
|
|
+ this.kill(serverWorld);
|
|
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
|
+ }
|
|
+
|
|
+ return super.interact(player, hand);
|
|
+ }
|
|
+ // Purpur end - Shears can defuse TNT
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
index 32670a3cb4b54b66d655197e3fde834d2b2b6d34..39d02cf0e31832e30c4f034b0b5385e3e0057e60 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
@@ -68,16 +68,19 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
protected AbstractSkeleton(EntityType<? extends AbstractSkeleton> type, Level world) {
|
|
super(type, world);
|
|
this.reassessWeaponGoal();
|
|
+ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new RestrictSunGoal(this));
|
|
this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
@@ -96,37 +99,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
abstract SoundEvent getStepSound();
|
|
|
|
// Paper start - shouldBurnInDay API
|
|
- private boolean shouldBurnInDay = true;
|
|
+ //private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight
|
|
public boolean shouldBurnInDay() { return shouldBurnInDay; }
|
|
public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; }
|
|
// Paper end - shouldBurnInDay API
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- boolean flag = shouldBurnInDay && this.isSunBurnTick(); // Paper - shouldBurnInDay API
|
|
-
|
|
- if (flag) {
|
|
- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
-
|
|
- if (!itemstack.isEmpty()) {
|
|
- if (itemstack.isDamageableItem()) {
|
|
- Item item = itemstack.getItem();
|
|
-
|
|
- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
- this.onEquippedItemBroken(item, EquipmentSlot.HEAD);
|
|
- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
- }
|
|
- }
|
|
-
|
|
- flag = false;
|
|
- }
|
|
-
|
|
- if (flag) {
|
|
- this.igniteForSeconds(8.0F);
|
|
- }
|
|
- }
|
|
-
|
|
+ // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
super.aiStep();
|
|
}
|
|
|
|
@@ -158,11 +138,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
this.reassessWeaponGoal();
|
|
this.setCanPickUpLoot(this.level().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper - Add world settings for mobs picking up loot
|
|
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
|
|
- LocalDate localdate = LocalDate.now();
|
|
- int i = localdate.get(ChronoField.DAY_OF_MONTH);
|
|
- int j = localdate.get(ChronoField.MONTH_OF_YEAR);
|
|
-
|
|
- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) {
|
|
+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur
|
|
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
|
|
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
|
|
}
|
|
@@ -221,7 +197,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
}
|
|
|
|
if (event.getProjectile() == entityarrow.getBukkitEntity()) {
|
|
- Projectile.spawnProjectileUsingShoot(entityarrow, worldserver, itemstack1, d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - worldserver.getDifficulty().getId() * 4));
|
|
+ Projectile.spawnProjectileUsingShoot(entityarrow, worldserver, itemstack1, d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, this.level().purpurConfig.skeletonBowAccuracyMap.getOrDefault(this.level().getDifficulty().getId(), (float) (14 - this.level().getDifficulty().getId() * 4))); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
}
|
|
@@ -243,7 +219,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
super.readAdditionalSaveData(nbt);
|
|
this.reassessWeaponGoal();
|
|
// Paper start - shouldBurnInDay API
|
|
- if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
}
|
|
// Paper end - shouldBurnInDay API
|
|
@@ -253,7 +229,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay);
|
|
+ //nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
}
|
|
// Paper end - shouldBurnInDay API
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Blaze.java b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
index e33fa82ca1332b95bb067fd621212d3026eee1b7..07db4557ab0d7a4a0f5432257bd18195d2de7255 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
@@ -33,26 +33,74 @@ public class Blaze extends Monster {
|
|
|
|
public Blaze(EntityType<? extends Blaze> type, Level world) {
|
|
super(type, world);
|
|
- this.setPathfindingMalus(PathType.WATER, -1.0F);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(PathType.LAVA, 8.0F);
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F);
|
|
this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F);
|
|
this.xpReward = 10;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.blazeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.blazeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.blazeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.blazeMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.blazeMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.blazeScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.blazeAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this));
|
|
this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0));
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 0.0F));
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0);
|
|
+ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -112,11 +160,18 @@ public class Blaze extends Monster {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level().purpurConfig.blazeTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
this.nextHeightOffsetChangeTick--;
|
|
if (this.nextHeightOffsetChangeTick <= 0) {
|
|
this.nextHeightOffsetChangeTick = 100;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Bogged.java b/src/main/java/net/minecraft/world/entity/monster/Bogged.java
|
|
index 975477663b6d76a69c006a89e440e21471b39b89..ca63ab37bc6b5b4cb5abf2848dae476b5d937f2a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java
|
|
@@ -44,6 +44,29 @@ public class Bogged extends AbstractSkeleton implements Shearable {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.boggedRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.boggedRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.boggedControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.boggedMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.boggedScale);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
index 4e621a7f36b3d718695434a2a4e3060283667bb2..9a274b83a3a7726cac421856dbc7be01de45d29b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
@@ -27,6 +27,39 @@ public class CaveSpider extends Spider {
|
|
return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.caveSpiderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.caveSpiderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.caveSpiderControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.caveSpiderMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.caveSpiderScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.caveSpiderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.caveSpiderAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public boolean doHurtTarget(ServerLevel world, Entity target) {
|
|
if (super.doHurtTarget(world, target)) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
index 1906dfc22af208d6e801ad4a8f2f9e9702432691..38cbe2fce9c36195aa9bea2af26d14364b216825 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
@@ -60,21 +60,99 @@ public class Creeper extends Monster {
|
|
public int explosionRadius = 3;
|
|
private int droppedSkulls;
|
|
public Entity entityIgniter; // CraftBukkit
|
|
+ // Purpur start
|
|
+ private int spacebarCharge = 0;
|
|
+ private int prevSpacebarCharge = 0;
|
|
+ private int powerToggleDelay = 0;
|
|
+ // Purpur end
|
|
+ private boolean exploding = false; // Purpur - Config to make Creepers explode on death
|
|
|
|
public Creeper(EntityType<? extends Creeper> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.creeperRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creeperRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.creeperControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void customServerAiStep(ServerLevel world) {
|
|
+ if (powerToggleDelay > 0) {
|
|
+ powerToggleDelay--;
|
|
+ }
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ if (getRider().getForwardMot() != 0 || getRider().getStrafeMot() != 0) {
|
|
+ spacebarCharge = 0;
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ }
|
|
+ if (spacebarCharge == prevSpacebarCharge) {
|
|
+ spacebarCharge = 0;
|
|
+ }
|
|
+ prevSpacebarCharge = spacebarCharge;
|
|
+ }
|
|
+ super.customServerAiStep(world);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (powerToggleDelay > 0) {
|
|
+ return true; // just toggled power, do not jump or ignite
|
|
+ }
|
|
+ spacebarCharge++;
|
|
+ if (spacebarCharge > maxSwell - 2) {
|
|
+ spacebarCharge = 0;
|
|
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.powered.creeper")) {
|
|
+ powerToggleDelay = 20;
|
|
+ setPowered(!isPowered());
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ if (!isIgnited()) {
|
|
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0 &&
|
|
+ getRider().getBukkitEntity().hasPermission("allow.special.creeper")) {
|
|
+ setIgnited(true);
|
|
+ setSwellDir(1);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
this.goalSelector.addGoal(2, new SwellGoal(this));
|
|
+ this.goalSelector.addGoal(3, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
}
|
|
@@ -174,6 +252,40 @@ public class Creeper extends Monster {
|
|
}
|
|
}
|
|
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.creeperMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.creeperScale);
|
|
+ }
|
|
+
|
|
+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, @Nullable net.minecraft.world.entity.SpawnGroupData entityData) {
|
|
+ double chance = world.getLevel().purpurConfig.creeperChargedChance;
|
|
+ if (chance > 0D && random.nextDouble() <= chance) {
|
|
+ setPowered(true);
|
|
+ }
|
|
+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.creeperTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ // Purpur start - Config to make Creepers explode on death
|
|
+ @Override
|
|
+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel world, DamageSource damageSource) {
|
|
+ if (!this.exploding && this.level().purpurConfig.creeperExplodeWhenKilled && damageSource.getEntity() instanceof net.minecraft.server.level.ServerPlayer) {
|
|
+ this.explodeCreeper();
|
|
+ }
|
|
+ return super.dropAllDeathLoot(world, damageSource);
|
|
+ }
|
|
+ // Purpur end - Config to make Creepers explode on death
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.creeperAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected SoundEvent getHurtSound(DamageSource source) {
|
|
return SoundEvents.CREEPER_HURT;
|
|
@@ -262,16 +374,18 @@ public class Creeper extends Monster {
|
|
|
|
public void explodeCreeper() {
|
|
Level world = this.level();
|
|
+ this.exploding = true; // Purpur - Config to make Creepers explode on death
|
|
|
|
if (world instanceof ServerLevel worldserver) {
|
|
float f = this.isPowered() ? 2.0F : 1.0F;
|
|
+ float multiplier = worldserver.purpurConfig.creeperHealthRadius ? this.getHealth() / this.getMaxHealth() : 1; // Purpur
|
|
|
|
// CraftBukkit start
|
|
- ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * f, false);
|
|
+ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, (this.explosionRadius * f) * multiplier, false); // Purpur
|
|
if (!event.isCancelled()) {
|
|
// CraftBukkit end
|
|
this.dead = true;
|
|
- worldserver.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this)
|
|
+ worldserver.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), worldserver.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) && level().purpurConfig.creeperAllowGriefing ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this) // Purpur
|
|
this.spawnLingeringCloud();
|
|
this.triggerOnDeathMobEffects(worldserver, Entity.RemovalReason.KILLED);
|
|
this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
|
|
@@ -283,6 +397,7 @@ public class Creeper extends Monster {
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ this.exploding = false; // Purpur - Config to make Creepers explode on death
|
|
}
|
|
|
|
private void spawnLingeringCloud() {
|
|
@@ -324,6 +439,7 @@ public class Creeper extends Monster {
|
|
com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited);
|
|
if (event.callEvent()) {
|
|
this.entityData.set(Creeper.DATA_IS_IGNITED, event.isIgnited());
|
|
+ if (!event.isIgnited()) setSwellDir(-1); // Purpur
|
|
}
|
|
}
|
|
// Paper end - CreeperIgniteEvent
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
index 2e73917ce9270de7483bb1d4e9bf312a31ec9b1e..949207eda199c874f2f14074b5a4fff5462b86b9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
@@ -72,6 +72,59 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
return Zombie.createAttributes().add(Attributes.STEP_HEIGHT, 1.0);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.drownedRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.drownedRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.drownedControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.drownedMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.drownedScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.drownedSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.drownedTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level().purpurConfig.drownedJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level().purpurConfig.drownedJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level().purpurConfig.drownedJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.drownedAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void addBehaviourGoals() {
|
|
this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0));
|
|
@@ -79,10 +132,23 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0, false));
|
|
this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0));
|
|
this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0, this.level().getSeaLevel()));
|
|
+ if (level().purpurConfig.drownedBreakDoors) this.goalSelector.addGoal(6, new net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors));
|
|
this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0));
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Drowned.class).setAlertOthers(ZombifiedPiglin.class));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (target, world) -> this.okTarget(target)));
|
|
- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper - Check drowned for villager aggression config
|
|
+ // Purpur start
|
|
+ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Paper - Check drowned for villager aggression config
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse();
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false));
|
|
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
@@ -396,7 +462,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
}
|
|
}
|
|
|
|
- static class DrownedMoveControl extends MoveControl {
|
|
+ static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
private final Drowned drowned;
|
|
|
|
public DrownedMoveControl(Drowned drowned) {
|
|
@@ -405,7 +471,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
LivingEntity livingEntity = this.drowned.getTarget();
|
|
if (this.drowned.wantsToSwim() && this.drowned.isInWater()) {
|
|
if (livingEntity != null && livingEntity.getY() > this.drowned.getY() || this.drowned.searchingForLand) {
|
|
@@ -425,7 +491,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
float h = (float)(Mth.atan2(f, d) * 180.0F / (float)Math.PI) - 90.0F;
|
|
this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), h, 90.0F));
|
|
this.drowned.yBodyRot = this.drowned.getYRot();
|
|
- float i = (float)(this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float i = (float)(this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
float j = Mth.lerp(0.125F, this.drowned.getSpeed(), i);
|
|
this.drowned.setSpeed(j);
|
|
this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add((double)j * d * 0.005, (double)j * e * 0.1, (double)j * f * 0.005));
|
|
@@ -434,7 +500,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0, -0.008, 0.0));
|
|
}
|
|
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
index 378694a38115c012978e1fea59d049d1ebd04110..a000304e3ac4c34b020f7457aa2589c87f140d8c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
@@ -33,6 +33,34 @@ public class ElderGuardian extends Guardian {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.elderGuardianRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.elderGuardianControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.elderGuardianMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.elderGuardianScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.elderGuardianTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.elderGuardianAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
index 35ffcd82df6af65a9c1b9a1a6acdd8b7567b8645..ebc2a461a9b41a28388362c777f53a0ee7f839f3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
@@ -92,12 +92,41 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
public EnderMan(EntityType<? extends EnderMan> type, Level world) {
|
|
super(type, world);
|
|
- this.setPathfindingMalus(PathType.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.endermanRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermanRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.endermanControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.endermanMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.endermanScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.endermanAlwaysDropExp;
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this));
|
|
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
|
|
@@ -105,9 +134,10 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this));
|
|
this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false));
|
|
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving, ignored) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur
|
|
this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false));
|
|
}
|
|
|
|
@@ -235,7 +265,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
boolean isBeingStaredBy(Player player) {
|
|
// Paper start - EndermanAttackPlayerEvent
|
|
- boolean shouldAttack = isBeingStaredBy0(player);
|
|
+ boolean shouldAttack = !this.level().purpurConfig.endermanDisableStareAggro && isBeingStaredBy0(player); // Purpur
|
|
com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) player.getBukkitEntity());
|
|
event.setCancelled(!shouldAttack);
|
|
return event.callEvent();
|
|
@@ -263,12 +293,12 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level().purpurConfig.endermanTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (world.isDay() && this.tickCount >= this.targetChangeTime + 600) {
|
|
+ if ((getRider() == null || !this.isControllable()) && world.isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - no random teleporting
|
|
float f = this.getLightLevelDependentMagicValue();
|
|
|
|
if (f > 0.5F && world.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent
|
|
@@ -390,6 +420,8 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
public boolean hurtServer(ServerLevel world, DamageSource source, float amount) {
|
|
if (this.isInvulnerableTo(world, source)) {
|
|
return false;
|
|
+ } else if (getRider() != null && this.isControllable()) { return super.hurtServer(world, source, amount); // Purpur - no teleporting on damage
|
|
+ } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && source.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height
|
|
} else {
|
|
boolean flag = source.getDirectEntity() instanceof ThrownPotion;
|
|
boolean flag1;
|
|
@@ -404,6 +436,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
} else {
|
|
flag1 = flag && this.hurtWithCleanWater(world, source, (ThrownPotion) source.getDirectEntity(), amount);
|
|
|
|
+ if (!flag1 && world.purpurConfig.endermanIgnoreProjectiles) return super.hurtServer(world, source, amount); // Purpur
|
|
if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent
|
|
for (int i = 0; i < 64; ++i) {
|
|
if (this.teleport()) {
|
|
@@ -448,7 +481,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean requiresCustomPersistence() {
|
|
- return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
|
|
+ return super.requiresCustomPersistence() || (!this.level().purpurConfig.endermanDespawnEvenWithBlock && this.getCarriedBlock() != null); // Purpur
|
|
}
|
|
|
|
private static class EndermanFreezeWhenLookedAt extends Goal {
|
|
@@ -495,7 +528,16 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return this.enderman.getCarriedBlock() == null ? false : (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0);
|
|
+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur
|
|
+ // Purpur start
|
|
+ if (this.enderman.getCarriedBlock() == null) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -540,7 +582,16 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return this.enderman.getCarriedBlock() != null ? false : (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0);
|
|
+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur
|
|
+ // Purpur start
|
|
+ if (this.enderman.getCarriedBlock() != null) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
index 534e98dd7291e09dee1d0f77cbf221b15708590f..f8373fc9839fccb31e3dd090de70e2cd7c9e6cfc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
@@ -32,20 +32,64 @@ public class Endermite extends Monster {
|
|
|
|
private static final int MAX_LIFE = 2400;
|
|
public int life;
|
|
+ private boolean isPlayerSpawned; // Purpur
|
|
|
|
public Endermite(EntityType<? extends Endermite> type, Level world) {
|
|
super(type, world);
|
|
this.xpReward = 3;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.endermiteRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermiteRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.endermiteControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.endermiteMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.endermiteScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.endermiteTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ public boolean isPlayerSpawned() {
|
|
+ return this.isPlayerSpawned;
|
|
+ }
|
|
+
|
|
+ public void setPlayerSpawned(boolean playerSpawned) {
|
|
+ this.isPlayerSpawned = playerSpawned;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.endermiteAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
|
|
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
@@ -83,12 +127,14 @@ public class Endermite extends Monster {
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.life = nbt.getInt("Lifetime");
|
|
+ this.isPlayerSpawned = nbt.getBoolean("PlayerSpawned"); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("Lifetime", this.life);
|
|
+ nbt.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
index 6592baa53ecb4e364d1c1b6f64178fc86c59a982..e231bb33b7e6a00d7c1a6c3540b4b48cfd4c15e3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
@@ -53,10 +53,44 @@ public class Evoker extends SpellcasterIllager {
|
|
this.xpReward = 10;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.evokerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.evokerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.evokerControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.evokerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.evokerScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.evokerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.evokerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal());
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6D, 1.0D));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0D, 1.2D));
|
|
@@ -66,6 +100,7 @@ public class Evoker extends SpellcasterIllager {
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
|
|
@@ -342,7 +377,7 @@ public class Evoker extends SpellcasterIllager {
|
|
} else {
|
|
ServerLevel worldserver = getServerLevel(Evoker.this.level());
|
|
|
|
- if (!worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!worldserver.purpurConfig.evokerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
} else {
|
|
List<Sheep> list = worldserver.getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0D, 4.0D, 16.0D));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
index a8c8c03e972aa6352843cf4c3e4aebfb8f493125..173b10fa553db30c321bfd9eabe13915b63cf920 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
@@ -44,11 +44,47 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
this.moveControl = new Ghast.GhastMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.ghastRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ghastRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.ghastControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.ghastMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this));
|
|
this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this));
|
|
this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving, worldserver) -> {
|
|
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
|
|
}));
|
|
@@ -96,6 +132,22 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
}
|
|
}
|
|
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ghastMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ghastScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.ghastTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.ghastAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
@@ -103,7 +155,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -155,7 +207,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
|
|
}
|
|
|
|
- private static class GhastMoveControl extends MoveControl {
|
|
+ private static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
private final Ghast ghast;
|
|
private int floatDuration;
|
|
@@ -166,7 +218,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO) {
|
|
if (this.floatDuration-- <= 0) {
|
|
this.floatDuration += this.ghast.getRandom().nextInt(5) + 2;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
index 118521ae54254b0a73bb7cba7b2871c9c26f89fc..859d316825658c11f58dd92912edbee75eaeabb9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
@@ -12,12 +12,95 @@ public class Giant extends Monster {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.giantRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.giantRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.giantControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ if (level().purpurConfig.giantHaveAI) {
|
|
+ this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ this.goalSelector.addGoal(7, new net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
+ this.goalSelector.addGoal(8, new net.minecraft.world.entity.ai.goal.LookAtPlayerGoal(this, net.minecraft.world.entity.player.Player.class, 16.0F));
|
|
+ this.goalSelector.addGoal(8, new net.minecraft.world.entity.ai.goal.RandomLookAroundGoal(this));
|
|
+ this.goalSelector.addGoal(5, new net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal(this, 1.0D));
|
|
+ if (level().purpurConfig.giantHaveHostileAI) {
|
|
+ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class));
|
|
+ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.player.Player.class, true));
|
|
+ this.targetSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.npc.Villager.class, false));
|
|
+ this.targetSelector.addGoal(4, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.animal.IronGolem.class, true));
|
|
+ this.targetSelector.addGoal(5, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.animal.Turtle.class, true));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.giantTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.giantAlwaysDropExp;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.giantMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.giantScale);
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.giantMovementSpeed);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.giantAttackDamage);
|
|
+ }
|
|
+
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 100.0).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.0);
|
|
}
|
|
|
|
+ @Override
|
|
+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, @org.jetbrains.annotations.Nullable net.minecraft.world.entity.SpawnGroupData entityData) {
|
|
+ net.minecraft.world.entity.SpawnGroupData groupData = super.finalizeSpawn(world, difficulty, spawnReason, entityData);
|
|
+ if (groupData == null) {
|
|
+ populateDefaultEquipmentSlots(this.random, difficulty);
|
|
+ populateDefaultEquipmentEnchantments(world, this.random, difficulty);
|
|
+ }
|
|
+ return groupData;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void populateDefaultEquipmentSlots(net.minecraft.util.RandomSource random, net.minecraft.world.DifficultyInstance difficulty) {
|
|
+ super.populateDefaultEquipmentSlots(this.random, difficulty);
|
|
+ // TODO make configurable
|
|
+ if (random.nextFloat() < (level().getDifficulty() == net.minecraft.world.Difficulty.HARD ? 0.1F : 0.05F)) {
|
|
+ this.setItemSlot(net.minecraft.world.entity.EquipmentSlot.MAINHAND, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.IRON_SWORD));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ // make giants jump as high as everything else relative to their size
|
|
+ // 1.0 makes bottom of feet about as high as their waist when they jump
|
|
+ return level().purpurConfig.giantJumpHeight;
|
|
+ }
|
|
+
|
|
@Override
|
|
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
|
|
- return world.getPathfindingCostFromLightLevels(pos);
|
|
+ return super.getWalkTargetValue(pos, world); // Purpur - fix light requirements for natural spawns
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
index 951f46684623582980901c1ebc1870aa5bcf25a1..da833bf35342f771ecccd5dcac4fe87f72d047b0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
@@ -67,15 +67,52 @@ public class Guardian extends Monster {
|
|
this.xpReward = 10;
|
|
this.setPathfindingMalus(PathType.WATER, 0.0F);
|
|
this.moveControl = new Guardian.GuardianMoveControl(this);
|
|
+ // Purpur start
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void setYawPitch(float yaw, float pitch) {
|
|
+ super.setYawPitch(yaw, pitch * 0.35F);
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.clientSideTailAnimation = this.random.nextFloat();
|
|
this.clientSideTailAnimationO = this.clientSideTailAnimation;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.guardianRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.guardianControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.guardianMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.guardianScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.guardianTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.guardianAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D);
|
|
|
|
this.randomStrollGoal = new RandomStrollGoal(this, 1.0D, 80);
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field
|
|
this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction);
|
|
this.goalSelector.addGoal(7, this.randomStrollGoal);
|
|
@@ -84,6 +121,7 @@ public class Guardian extends Monster {
|
|
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
|
this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
|
pathfindergoalmovetowardsrestriction.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this)));
|
|
}
|
|
|
|
@@ -330,7 +368,7 @@ public class Guardian extends Monster {
|
|
@Override
|
|
public void travel(Vec3 movementInput) {
|
|
if (this.isControlledByLocalInstance() && this.isInWater()) {
|
|
- this.moveRelative(0.1F, movementInput);
|
|
+ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, movementInput); // Purpur
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
|
|
if (!this.isMoving() && this.getTarget() == null) {
|
|
@@ -342,7 +380,7 @@ public class Guardian extends Monster {
|
|
|
|
}
|
|
|
|
- private static class GuardianMoveControl extends MoveControl {
|
|
+ private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
|
|
|
|
private final Guardian guardian;
|
|
|
|
@@ -351,8 +389,17 @@ public class Guardian extends Monster {
|
|
this.guardian = guardian;
|
|
}
|
|
|
|
+ // Purpur start
|
|
@Override
|
|
- public void tick() {
|
|
+ public void purpurTick(Player rider) {
|
|
+ super.purpurTick(rider);
|
|
+ guardian.setDeltaMovement(guardian.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ guardian.setMoving(guardian.getForwardMot() > 0.0F); // control tail speed
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) {
|
|
Vec3 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ());
|
|
double d0 = vec3d.length();
|
|
@@ -363,7 +410,7 @@ public class Guardian extends Monster {
|
|
|
|
this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F));
|
|
this.guardian.yBodyRot = this.guardian.getYRot();
|
|
- float f1 = (float) (this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f1 = (float) (this.getSpeedModifier() * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1);
|
|
|
|
this.guardian.setSpeed(f2);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
index 184fa759db065fb345f3623752229430816d8ad3..7c8ec5cd88fb2083f458a945e716b6f118555db8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
@@ -21,6 +21,59 @@ public class Husk extends Zombie {
|
|
|
|
public Husk(EntityType<? extends Husk> type, Level world) {
|
|
super(type, world);
|
|
+ this.setShouldBurnInDay(false); // Purpur - API for any mob to burn daylight
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.huskRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.huskRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.huskControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.huskMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.huskSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level().purpurConfig.huskJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level().purpurConfig.huskJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level().purpurConfig.huskJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.huskTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.huskAlwaysDropExp;
|
|
}
|
|
|
|
public static boolean checkHuskSpawnRules(EntityType<Husk> type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) {
|
|
@@ -29,7 +82,7 @@ public class Husk extends Zombie {
|
|
|
|
@Override
|
|
public boolean isSunSensitive() {
|
|
- return false;
|
|
+ return this.shouldBurnInDay; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
index db3aac9ba711dcd18ffc35c4a745ecaec89d0166..2ca241344efc6320d2018bdc772f74470080eeed 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
@@ -59,10 +59,46 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.illusionerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.illusionerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.illusionerControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ protected void initAttributes() {
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.illusionerMovementSpeed);
|
|
+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.illusionerFollowRange);
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.illusionerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.illusionerScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.illusionerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.illusionerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal());
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal());
|
|
@@ -71,6 +107,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
index ae710c3fffc7840a9ff2cbc5cdacef8a2e248253..63caf20256a3deae98b9cd9f54650def172f0e57 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
@@ -24,6 +24,58 @@ public class MagmaCube extends Slime {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.magmaCubeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.magmaCubeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.magmaCubeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ protected String getMaxHealthEquation() {
|
|
+ return level().purpurConfig.magmaCubeMaxHealth;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected String getAttackDamageEquation() {
|
|
+ return level().purpurConfig.magmaCubeAttackDamage;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected java.util.Map<Integer, Double> getMaxHealthCache() {
|
|
+ return level().purpurConfig.magmaCubeMaxHealthCache;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected java.util.Map<Integer, Double> getAttackDamageCache() {
|
|
+ return level().purpurConfig.magmaCubeAttackDamageCache;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.magmaCubeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.magmaCubeAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F);
|
|
}
|
|
@@ -71,6 +123,7 @@ public class MagmaCube extends Slime {
|
|
float f = (float)this.getSize() * 0.1F;
|
|
this.setDeltaMovement(vec3.x, (double)(this.getJumpPower() + f), vec3.z);
|
|
this.hasImpulse = true;
|
|
+ this.actualJump = false; // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Monster.java b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
index e2de074bbe7bab0e5a7aecc1fae4c5914a203dd4..c2061f575c731ecc6071384b007517c08e0cf983 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
@@ -88,6 +88,14 @@ public abstract class Monster extends PathfinderMob implements Enemy {
|
|
}
|
|
|
|
public static boolean isDarkEnoughToSpawn(ServerLevelAccessor world, BlockPos pos, RandomSource random) {
|
|
+ // Purpur start
|
|
+ if (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce || !world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce) {
|
|
+ net.minecraft.world.level.block.state.BlockState spawnBlock = world.getBlockState(pos.below());
|
|
+ if ((!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.PACKED_ICE)) || (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.BLUE_ICE))) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (world.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) {
|
|
return false;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
index 150fd890ac65097b5434fd88e8d2b24a89dca79a..cda6cb5b10b895bab48d2212f259ba4ca40e1ed6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
@@ -49,6 +49,8 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
Vec3 moveTargetPoint;
|
|
public BlockPos anchorPoint;
|
|
Phantom.AttackPhase attackPhase;
|
|
+ private static final net.minecraft.world.item.crafting.Ingredient TORCH = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.item.Items.TORCH, net.minecraft.world.item.Items.SOUL_TORCH); // Purpur
|
|
+ Vec3 crystalPosition; // Purpur
|
|
|
|
public Phantom(EntityType<? extends Phantom> type, Level world) {
|
|
super(type, world);
|
|
@@ -58,6 +60,92 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
this.xpReward = 5;
|
|
this.moveControl = new Phantom.PhantomMoveControl(this);
|
|
this.lookControl = new Phantom.PhantomLookControl(this, this);
|
|
+ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.phantomRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.phantomRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.phantomControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.phantomMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() {
|
|
+ return Monster.createMonsterAttributes().add(Attributes.FLYING_SPEED, 3.0D);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) {
|
|
+ shoot();
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean shoot() {
|
|
+ org.bukkit.Location loc = ((org.bukkit.entity.LivingEntity) getBukkitEntity()).getEyeLocation();
|
|
+ loc.setPitch(-loc.getPitch());
|
|
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(100).add(loc.toVector());
|
|
+
|
|
+ org.purpurmc.purpur.entity.PhantomFlames flames = new org.purpurmc.purpur.entity.PhantomFlames(level(), this);
|
|
+ flames.canGrief = level().purpurConfig.phantomAllowGriefing;
|
|
+ flames.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), 1.0F, 5.0F);
|
|
+ level().addFreshEntity(flames);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void dropFromLootTable(ServerLevel world, DamageSource damageSource, boolean causedByPlayer) {
|
|
+ boolean dropped = false;
|
|
+ if (lastHurtByPlayer == null && damageSource.getEntity() instanceof net.minecraft.world.entity.boss.enderdragon.EndCrystal) {
|
|
+ if (random.nextInt(5) < 1) {
|
|
+ dropped = spawnAtLocation(world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.PHANTOM_MEMBRANE)) != null;
|
|
+ }
|
|
+ }
|
|
+ if (!dropped) {
|
|
+ super.dropFromLootTable(world, damageSource, causedByPlayer);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean isCirclingCrystal() {
|
|
+ return crystalPosition != null;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.phantomTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.phantomAlwaysDropExp;
|
|
}
|
|
|
|
@Override
|
|
@@ -72,9 +160,17 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal());
|
|
- this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal());
|
|
- this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal());
|
|
+ // Purpur start
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ if (level().purpurConfig.phantomOrbitCrystalRadius > 0) {
|
|
+ this.goalSelector.addGoal(1, new FindCrystalGoal(this));
|
|
+ this.goalSelector.addGoal(2, new OrbitCrystalGoal(this));
|
|
+ }
|
|
+ this.goalSelector.addGoal(3, new Phantom.PhantomAttackStrategyGoal());
|
|
+ this.goalSelector.addGoal(4, new Phantom.PhantomSweepAttackGoal());
|
|
+ this.goalSelector.addGoal(5, new Phantom.PhantomCircleAroundAnchorGoal());
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal());
|
|
}
|
|
|
|
@@ -90,7 +186,10 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
|
|
private void updatePhantomSizeInfo() {
|
|
this.refreshDimensions();
|
|
- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) (6 + this.getPhantomSize()));
|
|
+ // Purpur start
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomMaxHealth, () -> this.level().purpurConfig.phantomMaxHealthCache, () -> 20.0D));
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomAttackDamage, () -> this.level().purpurConfig.phantomAttackDamageCache, () -> (double) 6 + this.getPhantomSize()));
|
|
+ // Purpur end
|
|
}
|
|
|
|
public int getPhantomSize() {
|
|
@@ -115,6 +214,21 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
return true;
|
|
}
|
|
|
|
+ private double getFromCache(java.util.function.Supplier<String> equation, java.util.function.Supplier<java.util.Map<Integer, Double>> cache, java.util.function.Supplier<Double> defaultValue) {
|
|
+ int size = getPhantomSize();
|
|
+ Double value = cache.get().get(size);
|
|
+ if (value == null) {
|
|
+ try {
|
|
+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ value = defaultValue.get();
|
|
+ }
|
|
+ cache.get().put(size, value);
|
|
+ }
|
|
+ return value;
|
|
+ }
|
|
+
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
@@ -135,21 +249,23 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() - (double) f3, this.getY() + (double) f5, this.getZ() - (double) f4, 0.0D, 0.0D, 0.0D);
|
|
}
|
|
|
|
+ if (level().purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API
|
|
- this.igniteForSeconds(8.0F);
|
|
- }
|
|
-
|
|
+ // Purpur - implemented in LivingEntity; moved down to shouldBurnInDay() - API for any mob to burn daylight
|
|
super.aiStep();
|
|
}
|
|
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) {
|
|
this.anchorPoint = this.blockPosition().above(5);
|
|
- this.setPhantomSize(0);
|
|
+ // Purpur start
|
|
+ int min = world.getLevel().purpurConfig.phantomMinSize;
|
|
+ int max = world.getLevel().purpurConfig.phantomMaxSize;
|
|
+ this.setPhantomSize(min == max ? min : world.getRandom().nextInt(max + 1 - min) + min);
|
|
+ // Purpur end
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, entityData);
|
|
}
|
|
|
|
@@ -165,7 +281,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
if (nbt.hasUUID("Paper.SpawningEntity")) {
|
|
this.spawningEntity = nbt.getUUID("Paper.SpawningEntity");
|
|
}
|
|
- if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
}
|
|
// Paper end
|
|
@@ -182,7 +298,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
if (this.spawningEntity != null) {
|
|
nbt.putUUID("Paper.SpawningEntity", this.spawningEntity);
|
|
}
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay);
|
|
+ //nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
// Paper end
|
|
}
|
|
|
|
@@ -242,8 +358,14 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
return this.spawningEntity;
|
|
}
|
|
public void setSpawningEntity(java.util.UUID entity) { this.spawningEntity = entity; }
|
|
- private boolean shouldBurnInDay = true;
|
|
- public boolean shouldBurnInDay() { return shouldBurnInDay; }
|
|
+ //private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight
|
|
+ // Purpur start - API for any mob to burn daylight
|
|
+ public boolean shouldBurnInDay() {
|
|
+ boolean burnFromDaylight = this.shouldBurnInDay && this.level().purpurConfig.phantomBurnInDaylight;
|
|
+ boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight;
|
|
+ return burnFromDaylight || burnFromLightSource;
|
|
+ }
|
|
+ // Purpur end - API for any mob to burn daylight
|
|
public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; }
|
|
// Paper end
|
|
|
|
@@ -254,7 +376,125 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
private AttackPhase() {}
|
|
}
|
|
|
|
- private class PhantomMoveControl extends MoveControl {
|
|
+ // Purpur start
|
|
+ class FindCrystalGoal extends Goal {
|
|
+ private final Phantom phantom;
|
|
+ private net.minecraft.world.entity.boss.enderdragon.EndCrystal crystal;
|
|
+ private Comparator<net.minecraft.world.entity.boss.enderdragon.EndCrystal> comparator;
|
|
+
|
|
+ FindCrystalGoal(Phantom phantom) {
|
|
+ this.phantom = phantom;
|
|
+ this.comparator = Comparator.comparingDouble(phantom::distanceToSqr);
|
|
+ this.setFlags(EnumSet.of(Flag.LOOK));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ double range = maxTargetRange();
|
|
+ List<net.minecraft.world.entity.boss.enderdragon.EndCrystal> crystals = level().getEntitiesOfClass(net.minecraft.world.entity.boss.enderdragon.EndCrystal.class, phantom.getBoundingBox().inflate(range));
|
|
+ if (crystals.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ crystals.sort(comparator);
|
|
+ crystal = crystals.get(0);
|
|
+ if (phantom.distanceToSqr(crystal) > range * range) {
|
|
+ crystal = null;
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ if (crystal == null || !crystal.isAlive()) {
|
|
+ return false;
|
|
+ }
|
|
+ double range = maxTargetRange();
|
|
+ return phantom.distanceToSqr(crystal) <= (range * range) * 2;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ phantom.crystalPosition = new Vec3(crystal.getX(), crystal.getY() + (phantom.random.nextInt(10) + 10), crystal.getZ());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ crystal = null;
|
|
+ phantom.crystalPosition = null;
|
|
+ super.stop();
|
|
+ }
|
|
+
|
|
+ private double maxTargetRange() {
|
|
+ return phantom.level().purpurConfig.phantomOrbitCrystalRadius;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ class OrbitCrystalGoal extends Goal {
|
|
+ private final Phantom phantom;
|
|
+ private float offset;
|
|
+ private float radius;
|
|
+ private float verticalChange;
|
|
+ private float direction;
|
|
+
|
|
+ OrbitCrystalGoal(Phantom phantom) {
|
|
+ this.phantom = phantom;
|
|
+ this.setFlags(EnumSet.of(Flag.MOVE));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return phantom.isCirclingCrystal();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.radius = 5.0F + phantom.random.nextFloat() * 10.0F;
|
|
+ this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F;
|
|
+ this.direction = phantom.random.nextBoolean() ? 1.0F : -1.0F;
|
|
+ updateOffset();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (phantom.random.nextInt(350) == 0) {
|
|
+ this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F;
|
|
+ }
|
|
+ if (phantom.random.nextInt(250) == 0) {
|
|
+ ++this.radius;
|
|
+ if (this.radius > 15.0F) {
|
|
+ this.radius = 5.0F;
|
|
+ this.direction = -this.direction;
|
|
+ }
|
|
+ }
|
|
+ if (phantom.random.nextInt(450) == 0) {
|
|
+ this.offset = phantom.random.nextFloat() * 2.0F * 3.1415927F;
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.distanceToSqr(phantom.getX(), phantom.getY(), phantom.getZ()) < 4.0D) {
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.y < phantom.getY() && !phantom.level().isEmptyBlock(new BlockPos(phantom).below(1))) {
|
|
+ this.verticalChange = Math.max(1.0F, this.verticalChange);
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.y > phantom.getY() && !phantom.level().isEmptyBlock(new BlockPos(phantom).above(1))) {
|
|
+ this.verticalChange = Math.min(-1.0F, this.verticalChange);
|
|
+ updateOffset();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void updateOffset() {
|
|
+ this.offset += this.direction * 15.0F * 0.017453292F;
|
|
+ phantom.moveTargetPoint = phantom.crystalPosition.add(
|
|
+ this.radius * Mth.cos(this.offset),
|
|
+ -4.0F + this.verticalChange,
|
|
+ this.radius * Mth.sin(this.offset));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
private float speed = 0.1F;
|
|
|
|
@@ -262,8 +502,19 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
super(entity);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void purpurTick(Player rider) {
|
|
+ if (!Phantom.this.onGround) {
|
|
+ // phantom is always in motion when flying
|
|
+ // TODO - FIX THIS
|
|
+ // rider.setForward(1.0F);
|
|
+ }
|
|
+ super.purpurTick(rider);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Phantom.this.horizontalCollision) {
|
|
Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
|
|
this.speed = 0.1F;
|
|
@@ -309,14 +560,20 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
}
|
|
}
|
|
|
|
- private class PhantomLookControl extends LookControl {
|
|
+ private class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public PhantomLookControl(final Phantom entity, final Mob phantom) {
|
|
super(phantom);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
- public void tick() {}
|
|
+ public void vanillaTick() {} // Purpur
|
|
}
|
|
|
|
private class PhantomBodyRotationControl extends BodyRotationControl {
|
|
@@ -403,6 +660,12 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
return false;
|
|
} else if (!entityliving.isAlive()) {
|
|
return false;
|
|
+ // Purpur start
|
|
+ } else if (level().purpurConfig.phantomBurnInLight > 0 && level().getLightEmission(new BlockPos(Phantom.this)) >= level().purpurConfig.phantomBurnInLight) {
|
|
+ return false;
|
|
+ } else if (level().purpurConfig.phantomIgnorePlayersWithTorch && (TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)))) {
|
|
+ return false;
|
|
+ // Purpur end
|
|
} else {
|
|
if (entityliving instanceof Player) {
|
|
Player entityhuman = (Player) entityliving;
|
|
@@ -549,6 +812,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
ServerLevel worldserver = getServerLevel(Phantom.this.level());
|
|
List<Player> list = worldserver.getNearbyPlayers(this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0D, 64.0D, 16.0D));
|
|
|
|
+ if (level().purpurConfig.phantomIgnorePlayersWithTorch) list.removeIf(human -> TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)));// Purpur
|
|
if (!list.isEmpty()) {
|
|
list.sort(Comparator.comparing((Entity e) -> { return e.getY(); }).reversed()); // CraftBukkit - decompile error
|
|
Iterator iterator = list.iterator();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
index 3e8631c7bd1e7591051ca21c6ae7acd87d3c7529..85d5c84a8861905e4546901aa6707078571eb402 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
@@ -64,16 +64,51 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.pillagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pillagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.pillagerControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pillagerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pillagerScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.pillagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.pillagerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F)); // Paper - decomp fix
|
|
this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0D, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 15.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 15.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
index c96fbfe448b3e7b722a8db0e1688276776abd94e..98c1934b4895a86cd8748edf906aaa721a87a123 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
@@ -77,15 +77,57 @@ public class Ravager extends Raider {
|
|
this.setPathfindingMalus(PathType.LEAVES, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.ravagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ravagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.ravagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ getNavigation().stop();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ravagerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ravagerScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.ravagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.ravagerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0D, 1.2D));
|
|
+ if (level().purpurConfig.ravagerAvoidRabbits) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.AvoidEntityGoal<>(this, net.minecraft.world.entity.animal.Rabbit.class, 6.0F, 1.0D, 1.2D)); // Purpur
|
|
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, true));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(2, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entityliving, worldserver) -> {
|
|
@@ -138,7 +180,7 @@ public class Ravager extends Raider {
|
|
@Override
|
|
public void aiStep() {
|
|
super.aiStep();
|
|
- if (this.isAlive()) {
|
|
+ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur
|
|
if (this.isImmobile()) {
|
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0D);
|
|
} else {
|
|
@@ -153,7 +195,7 @@ public class Ravager extends Raider {
|
|
if (world instanceof ServerLevel) {
|
|
ServerLevel worldserver = (ServerLevel) world;
|
|
|
|
- if (this.horizontalCollision && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.horizontalCollision && (worldserver.purpurConfig.ravagerBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur
|
|
boolean flag = false;
|
|
AABB axisalignedbb = this.getBoundingBox().inflate(0.2D);
|
|
Iterator iterator = BlockPos.betweenClosed(Mth.floor(axisalignedbb.minX), Mth.floor(axisalignedbb.minY), Mth.floor(axisalignedbb.minZ), Mth.floor(axisalignedbb.maxX), Mth.floor(axisalignedbb.maxY), Mth.floor(axisalignedbb.maxZ)).iterator();
|
|
@@ -163,7 +205,7 @@ public class Ravager extends Raider {
|
|
BlockState iblockdata = worldserver.getBlockState(blockposition);
|
|
Block block = iblockdata.getBlock();
|
|
|
|
- if (block instanceof LeavesBlock) {
|
|
+ if (this.level().purpurConfig.ravagerGriefableBlocks.contains(block)) { // Purpur
|
|
// CraftBukkit start
|
|
if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state
|
|
continue;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
index 6e0f2f6573ed6be9b91de960d55c269417ad8907..e3fefd52c83079fe3eab1a96dd81a183f718192b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
@@ -84,7 +84,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
|
|
return new Vector3f((float) baseblockposition.getX(), (float) baseblockposition.getY(), (float) baseblockposition.getZ());
|
|
});
|
|
- private static final float MAX_SCALE = 3.0F;
|
|
+ public static final float MAX_SCALE = 3.0F; // Purpur
|
|
private float currentPeekAmountO;
|
|
private float currentPeekAmount;
|
|
@Nullable
|
|
@@ -98,12 +98,60 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
this.lookControl = new Shulker.ShulkerLookControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.shulkerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.shulkerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.shulkerControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.shulkerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.shulkerScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.shulkerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) {
|
|
+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (player.level().purpurConfig.shulkerChangeColorWithDye && itemstack.getItem() instanceof net.minecraft.world.item.DyeItem dye && dye.getDyeColor() != this.getColor()) {
|
|
+ this.setVariant(Optional.of(dye.getDyeColor()));
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
|
+ }
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.shulkerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true));
|
|
this.goalSelector.addGoal(4, new Shulker.ShulkerAttackGoal());
|
|
this.goalSelector.addGoal(7, new Shulker.ShulkerPeekGoal());
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{this.getClass()})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this));
|
|
this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this));
|
|
@@ -482,12 +530,21 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
Vec3 vec3d = this.position();
|
|
AABB axisalignedbb = this.getBoundingBox();
|
|
|
|
- if (!this.isClosed() && this.teleportSomewhere()) {
|
|
- int i = this.level().getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(8.0D), Entity::isAlive).size();
|
|
- float f = (float) (i - 1) / 5.0F;
|
|
-
|
|
- if (this.level().random.nextFloat() >= f) {
|
|
+ if ((!this.level().purpurConfig.shulkerSpawnFromBulletRequireOpenLid || !this.isClosed()) && this.teleportSomewhere()) {
|
|
+ // Purpur start
|
|
+ float chance = this.level().purpurConfig.shulkerSpawnFromBulletBaseChance;
|
|
+ if (!this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation.isBlank()) {
|
|
+ int nearby = this.level().getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(this.level().purpurConfig.shulkerSpawnFromBulletNearbyRange), Entity::isAlive).size();
|
|
+ try {
|
|
+ chance -= ((Number) scriptEngine.eval("let nearby = " + nearby + "; " + this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation)).floatValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ chance -= (nearby - 1) / 5.0F;
|
|
+ }
|
|
+ }
|
|
+ if (this.level().random.nextFloat() <= chance) {
|
|
Shulker entityshulker = (Shulker) EntityType.SHULKER.create(this.level(), EntitySpawnReason.BREEDING);
|
|
+ // Purpur end
|
|
|
|
if (entityshulker != null) {
|
|
entityshulker.setVariant(this.getVariant());
|
|
@@ -590,7 +647,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
|
|
@Override
|
|
protected float sanitizeScale(float scale) {
|
|
- return Math.min(scale, 3.0F);
|
|
+ return Math.min(scale, 3.0F); // Purpur - diff on change
|
|
}
|
|
|
|
public void setVariant(Optional<DyeColor> variant) {
|
|
@@ -601,7 +658,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
|
|
@Override
|
|
public Optional<DyeColor> getVariant() {
|
|
- return Optional.ofNullable(this.getColor());
|
|
+ return Optional.ofNullable(this.level().purpurConfig.shulkerSpawnFromBulletRandomColor ? DyeColor.random(this.level().random) : this.getColor()); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@@ -611,7 +668,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
return b0 != 16 && b0 <= 15 ? DyeColor.byId(b0) : null;
|
|
}
|
|
|
|
- private class ShulkerLookControl extends LookControl {
|
|
+ private class ShulkerLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public ShulkerLookControl(final Mob entity) {
|
|
super(entity);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
index ff65cb8ea5233f2dd159f42ad53bc9d300cd604f..7a6ad611faf39eb8dd87bd498169571633c33769 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
@@ -44,14 +44,51 @@ public class Silverfish extends Monster {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.silverfishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.silverfishRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.silverfishControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.silverfishMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.silverfishScale);
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.silverfishMovementSpeed);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.silverfishAttackDamage);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.silverfishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.silverfishAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
|
|
this.goalSelector.addGoal(3, this.friendsGoal);
|
|
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
@@ -165,12 +202,12 @@ public class Silverfish extends Monster {
|
|
|
|
if (block instanceof InfestedBlock) {
|
|
// CraftBukkit start
|
|
- BlockState afterState = getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? iblockdata.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)); // Paper - fix wrong block state
|
|
+ BlockState afterState = (getServerLevel(world).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) ? iblockdata.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)); // Paper - fix wrong block state
|
|
if (!CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, afterState)) { // Paper - fix wrong block state
|
|
continue;
|
|
}
|
|
// CraftBukkit end
|
|
- if (getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (getServerLevel(world).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
world.destroyBlock(blockposition1, true, this.silverfish);
|
|
} else {
|
|
world.setBlock(blockposition1, ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)), 3);
|
|
@@ -208,7 +245,7 @@ public class Silverfish extends Monster {
|
|
} else {
|
|
RandomSource randomsource = this.mob.getRandom();
|
|
|
|
- if (getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) {
|
|
+ if (getServerLevel((Entity) this.mob).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) { // Purpur
|
|
this.selectedDirection = Direction.getRandom(randomsource);
|
|
BlockPos blockposition = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ()).relative(this.selectedDirection);
|
|
BlockState iblockdata = this.mob.level().getBlockState(blockposition);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
index 3972e2ed0554e2550519e994888e068df0a151e5..3cbe4c1ed514936a00e0181cb1e647fbb58032bb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
@@ -17,6 +17,16 @@ import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.Level;
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+// Purpur end
|
|
+
|
|
public class Skeleton extends AbstractSkeleton {
|
|
|
|
private static final int TOTAL_CONVERSION_TIME = 300;
|
|
@@ -29,6 +39,40 @@ public class Skeleton extends AbstractSkeleton {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.skeletonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.skeletonRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.skeletonControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.skeletonMaxHealth);
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.skeletonTakeDamageFromWater;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.skeletonAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
@@ -147,4 +191,63 @@ public class Skeleton extends AbstractSkeleton {
|
|
}
|
|
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ private int witherRosesFed = 0;
|
|
+
|
|
+ @Override
|
|
+ public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ ItemStack stack = player.getItemInHand(hand);
|
|
+
|
|
+ if (level().purpurConfig.skeletonFeedWitherRoses > 0 && this.getType() != EntityType.WITHER_SKELETON && stack.getItem() == Blocks.WITHER_ROSE.asItem()) {
|
|
+ return this.feedWitherRose(player, stack);
|
|
+ }
|
|
+
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+
|
|
+ private InteractionResult feedWitherRose(Player player, ItemStack stack) {
|
|
+ if (++witherRosesFed < level().purpurConfig.skeletonFeedWitherRoses) {
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+
|
|
+ WitherSkeleton skeleton = EntityType.WITHER_SKELETON.create(level(), net.minecraft.world.entity.EntitySpawnReason.CONVERSION);
|
|
+ if (skeleton == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ skeleton.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
+ skeleton.setHealth(this.getHealth());
|
|
+ skeleton.setAggressive(this.isAggressive());
|
|
+ skeleton.copyPosition(this);
|
|
+ skeleton.setYBodyRot(this.yBodyRot);
|
|
+ skeleton.setYHeadRot(this.getYHeadRot());
|
|
+ skeleton.yRotO = this.yRotO;
|
|
+ skeleton.xRotO = this.xRotO;
|
|
+
|
|
+ if (this.hasCustomName()) {
|
|
+ skeleton.setCustomName(this.getCustomName());
|
|
+ }
|
|
+
|
|
+ if (CraftEventFactory.callEntityTransformEvent(this, skeleton, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ this.level().addFreshEntity(skeleton);
|
|
+ this.remove(RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < 15; ++i) {
|
|
+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
index 72346a7e5269c91e3143933ac37e65ad9639b791..dad4ef9c672eb4247142de5d045678795951164c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
@@ -65,6 +65,7 @@ public class Slime extends Mob implements Enemy {
|
|
public float squish;
|
|
public float oSquish;
|
|
private boolean wasOnGround;
|
|
+ protected boolean actualJump; // Purpur
|
|
|
|
public Slime(EntityType<? extends Slime> type, Level world) {
|
|
super(type, world);
|
|
@@ -72,12 +73,89 @@ public class Slime extends Mob implements Enemy {
|
|
this.moveControl = new Slime.SlimeMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.slimeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.slimeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.slimeAlwaysDropExp;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.slimeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.slimeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ float height = super.getJumpPower();
|
|
+ return getRider() != null && this.isControllable() && actualJump ? height * 1.5F : height;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (onGround && getRider() != null && this.isControllable()) {
|
|
+ actualJump = true;
|
|
+ if (getRider().getForwardMot() == 0 || getRider().getStrafeMot() == 0) {
|
|
+ jumpFromGround(); // jump() here if not moving
|
|
+ }
|
|
+ }
|
|
+ return true; // do not jump() in wasd controller, let vanilla controller handle
|
|
+ }
|
|
+
|
|
+ protected String getMaxHealthEquation() {
|
|
+ return level().purpurConfig.slimeMaxHealth;
|
|
+ }
|
|
+
|
|
+ protected String getAttackDamageEquation() {
|
|
+ return level().purpurConfig.slimeAttackDamage;
|
|
+ }
|
|
+
|
|
+ protected java.util.Map<Integer, Double> getMaxHealthCache() {
|
|
+ return level().purpurConfig.slimeMaxHealthCache;
|
|
+ }
|
|
+
|
|
+ protected java.util.Map<Integer, Double> getAttackDamageCache() {
|
|
+ return level().purpurConfig.slimeAttackDamageCache;
|
|
+ }
|
|
+
|
|
+ protected double getFromCache(java.util.function.Supplier<String> equation, java.util.function.Supplier<java.util.Map<Integer, Double>> cache, java.util.function.Supplier<Double> defaultValue) {
|
|
+ int size = getSize();
|
|
+ Double value = cache.get().get(size);
|
|
+ if (value == null) {
|
|
+ try {
|
|
+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ value = defaultValue.get();
|
|
+ }
|
|
+ cache.get().put(size, value);
|
|
+ }
|
|
+ return value;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Slime.SlimeFloatGoal(this));
|
|
this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this));
|
|
this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this));
|
|
this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving, worldserver) -> {
|
|
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
|
|
}));
|
|
@@ -102,9 +180,9 @@ public class Slime extends Mob implements Enemy {
|
|
this.entityData.set(Slime.ID_SIZE, j);
|
|
this.reapplyPosition();
|
|
this.refreshDimensions();
|
|
- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double) (j * j));
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(this::getMaxHealthEquation, this::getMaxHealthCache, () -> (double) size * size)); // Purpur
|
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double) (0.2F + 0.1F * (float) j));
|
|
- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) j);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(this::getAttackDamageEquation, this::getAttackDamageCache, () -> (double) j)); // Purpur
|
|
if (heal) {
|
|
this.setHealth(this.getMaxHealth());
|
|
}
|
|
@@ -386,6 +464,7 @@ public class Slime extends Mob implements Enemy {
|
|
|
|
this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z);
|
|
this.hasImpulse = true;
|
|
+ this.actualJump = false; // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@@ -419,7 +498,7 @@ public class Slime extends Mob implements Enemy {
|
|
return super.getDefaultDimensions(pose).scale((float) this.getSize());
|
|
}
|
|
|
|
- private static class SlimeMoveControl extends MoveControl {
|
|
+ private static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private float yRot;
|
|
private int jumpDelay;
|
|
@@ -438,21 +517,33 @@ public class Slime extends Mob implements Enemy {
|
|
}
|
|
|
|
public void setWantedMovement(double speed) {
|
|
- this.speedModifier = speed;
|
|
+ this.setSpeedModifier(speed); // Purpur
|
|
this.operation = MoveControl.Operation.MOVE_TO;
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (slime.getRider() != null && slime.isControllable()) {
|
|
+ purpurTick(slime.getRider());
|
|
+ if (slime.getForwardMot() != 0 || slime.getStrafeMot() != 0) {
|
|
+ if (jumpDelay > 10) {
|
|
+ jumpDelay = 6;
|
|
+ }
|
|
+ } else {
|
|
+ jumpDelay = 20;
|
|
+ }
|
|
+ } else {
|
|
+ // Purpur end
|
|
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F));
|
|
this.mob.yHeadRot = this.mob.getYRot();
|
|
this.mob.yBodyRot = this.mob.getYRot();
|
|
- if (this.operation != MoveControl.Operation.MOVE_TO) {
|
|
+ } if ((slime.getRider() == null || !slime.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur
|
|
this.mob.setZza(0.0F);
|
|
} else {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
if (this.mob.onGround()) {
|
|
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
|
|
if (this.jumpDelay-- <= 0) {
|
|
this.jumpDelay = this.slime.getJumpDelay();
|
|
if (this.isAggressive) {
|
|
@@ -469,7 +560,7 @@ public class Slime extends Mob implements Enemy {
|
|
this.mob.setSpeed(0.0F);
|
|
}
|
|
} else {
|
|
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
index 91e521414c3ea5722aac7506b7589fbb399e9636..1669acbcf97bee0fa6b0ee91cf53217c53cf55d8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
@@ -51,9 +51,43 @@ public class Spider extends Monster {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.spiderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.spiderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.spiderControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.spiderMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.spiderScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.spiderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.spiderAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Armadillo.class, 6.0F, 1.0D, 1.2D, (entityliving) -> {
|
|
return !((Armadillo) entityliving).isScared();
|
|
}));
|
|
@@ -62,6 +96,7 @@ public class Spider extends Monster {
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class));
|
|
this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
index baaf17107584b253d7e268749849bf5b0d0c88ab..bb6984d82e6c0a83f456e725b20e0f21e0cac602 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
@@ -22,6 +22,38 @@ public class Stray extends AbstractSkeleton {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.strayRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.strayRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.strayControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.strayMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.strayTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.strayAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static boolean checkStraySpawnRules(
|
|
EntityType<Stray> type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random
|
|
) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
index 711b7eb8e9fdedbc87965828e573fe8d5c357d53..c3b5b34a54de945071692293645b8a8865aed961 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
@@ -91,12 +91,45 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
super(type, world);
|
|
this.steering = new ItemBasedSteering(this.entityData, Strider.DATA_BOOST_TIME, Strider.DATA_SADDLE_ID);
|
|
this.blocksBuilding = true;
|
|
- this.setPathfindingMalus(PathType.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(PathType.LAVA, 0.0F);
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F);
|
|
this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.striderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.striderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.striderControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.striderMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.striderScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.striderBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.striderAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static boolean checkStriderSpawnRules(EntityType<Strider> type, LevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) {
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable();
|
|
|
|
@@ -158,6 +191,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.65D));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.temptGoal = new TemptGoal(this, 1.4D, (itemstack) -> {
|
|
return itemstack.is(ItemTags.STRIDER_TEMPT_ITEMS);
|
|
@@ -412,7 +446,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level().purpurConfig.striderTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -454,6 +488,19 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
boolean flag = this.isFood(player.getItemInHand(hand));
|
|
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.striderGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) {
|
|
+ this.steering.setSaddle(false);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ ItemStack saddle = new ItemStack(Items.SADDLE);
|
|
+ if (!player.getInventory().add(saddle)) {
|
|
+ player.drop(saddle, false);
|
|
+ }
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
|
|
if (!this.level().isClientSide) {
|
|
player.startRiding(this);
|
|
@@ -466,7 +513,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
if (!enuminteractionresult.consumesAction()) {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
- return (InteractionResult) (itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS);
|
|
+ return (InteractionResult) (itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : tryRide(player, hand)); // Purpur
|
|
} else {
|
|
if (flag && !this.isSilent()) {
|
|
this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
index 183a33b7d666d652b455baa7e8339e9c4a870a58..fe19c0cf6a2c81b547158179518bf26be388cc7a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
@@ -59,6 +59,66 @@ public class Vex extends Monster implements TraceableEntity {
|
|
this.xpReward = 3;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.vexRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vexRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.vexControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level().purpurConfig.vexMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ float speed;
|
|
+ if (onGround) {
|
|
+ speed = (float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F;
|
|
+ } else {
|
|
+ speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ }
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
|
|
+ return false; // no fall damage please
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vexMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vexScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.vexTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.vexAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean isFlapping() {
|
|
return this.tickCount % Vex.TICKS_PER_FLAP == 0;
|
|
@@ -71,7 +131,7 @@ public class Vex extends Monster implements TraceableEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
- this.noPhysics = true;
|
|
+ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur
|
|
super.tick();
|
|
this.noPhysics = false;
|
|
this.setNoGravity(true);
|
|
@@ -86,17 +146,19 @@ public class Vex extends Monster implements TraceableEntity {
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, new Vex.VexChargeAttackGoal());
|
|
this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal());
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D);
|
|
+ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur;
|
|
}
|
|
|
|
@Override
|
|
@@ -228,14 +290,14 @@ public class Vex extends Monster implements TraceableEntity {
|
|
this.setDropChance(EquipmentSlot.MAINHAND, 0.0F);
|
|
}
|
|
|
|
- private class VexMoveControl extends MoveControl {
|
|
+ private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
public VexMoveControl(final Vex entityvex) {
|
|
super(entityvex);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO) {
|
|
Vec3 vec3d = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
|
|
double d0 = vec3d.length();
|
|
@@ -244,7 +306,7 @@ public class Vex extends Monster implements TraceableEntity {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5D));
|
|
} else {
|
|
- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.speedModifier * 0.05D / d0)));
|
|
+ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.getSpeedModifier() * 0.05D / d0))); // Purpur
|
|
if (Vex.this.getTarget() == null) {
|
|
Vec3 vec3d1 = Vex.this.getDeltaMovement();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
index 96b105697c91314148fd1b783501389214b1a3f0..a2c81d2a1077b2977f1595fd592044baf3e81bab 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
@@ -55,15 +55,50 @@ public class Vindicator extends AbstractIllager {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.vindicatorRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vindicatorRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.vindicatorControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vindicatorMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vindicatorScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.vindicatorTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.vindicatorAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2));
|
|
this.goalSelector.addGoal(2, new Vindicator.VindicatorBreakDoorGoal(this));
|
|
this.goalSelector.addGoal(3, new AbstractIllager.RaiderOpenDoorGoal(this));
|
|
this.goalSelector.addGoal(4, new Raider.HoldGroundAttackGoal(this, 10.0F));
|
|
this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, false));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true));
|
|
@@ -132,6 +167,11 @@ public class Vindicator extends AbstractIllager {
|
|
RandomSource randomSource = world.getRandom();
|
|
this.populateDefaultEquipmentSlots(randomSource, difficulty);
|
|
this.populateDefaultEquipmentEnchantments(world, randomSource, difficulty);
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.vindicatorJohnnySpawnChance > 0D && random.nextDouble() <= level().purpurConfig.vindicatorJohnnySpawnChance) {
|
|
+ setCustomName(Component.translatable("Johnny"));
|
|
+ }
|
|
+ // Purpur end
|
|
return spawnGroupData;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
index a03fa8a3e648532a7ffaaf523ca87c13e8af4c0a..313228811d1eff478887511f99b49706efc49774 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
@@ -57,6 +57,39 @@ public class Witch extends Raider implements RangedAttackMob {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.witchRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witchRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.witchControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witchMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witchScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.witchTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.witchAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
@@ -65,10 +98,12 @@ public class Witch extends Raider implements RangedAttackMob {
|
|
});
|
|
this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, (TargetingConditions.Selector) null);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 60, 10.0F));
|
|
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[]{Raider.class}));
|
|
this.targetSelector.addGoal(2, this.healRaidersGoal);
|
|
this.targetSelector.addGoal(3, this.attackPlayersGoal);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
index 37d3acda84a984bf4f1c44b3d27e2102839d3e8e..23fc36780a3e15260f8cb1001c8d676464a9df3a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
@@ -33,6 +33,39 @@ public class WitherSkeleton extends AbstractSkeleton {
|
|
this.setPathfindingMalus(PathType.LAVA, 8.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.witherSkeletonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherSkeletonRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.witherSkeletonControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherSkeletonMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witherSkeletonScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.witherSkeletonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.witherSkeletonAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
index 50b559a92b54c0be7624b1aebc70537573c58666..3f388ea39d2760df946cc027a4ae63704dcaa03a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
@@ -83,6 +83,39 @@ public class Zoglin extends Monster implements HoglinBase {
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.zoglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zoglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.zoglinControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zoglinMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zoglinScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.zoglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.zoglinAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Zoglin> brainProvider() {
|
|
return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
|
|
@@ -246,6 +279,7 @@ public class Zoglin extends Monster implements HoglinBase {
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
this.updateActivity();
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
index a12461907278cfbfa3b1c0aa74b9f07a31768b8a..85b03e0bf7436cb846df13c575ad78ac6a17a151 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
@@ -99,22 +99,70 @@ public class Zombie extends Monster {
|
|
private int inWaterTime;
|
|
public int conversionTime;
|
|
// private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field // Paper - remove anti tick skipping measures / wall time
|
|
- private boolean shouldBurnInDay = true; // Paper - Add more Zombie API
|
|
+ //private boolean shouldBurnInDay = true; // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
|
|
public Zombie(EntityType<? extends Zombie> type, Level world) {
|
|
super(type, world);
|
|
this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(world.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, world.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper - Configurable door breaking difficulty
|
|
+ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight
|
|
}
|
|
|
|
public Zombie(Level world) {
|
|
this(EntityType.ZOMBIE, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.zombieRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.zombieControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombieScale);
|
|
+ }
|
|
+
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level().purpurConfig.zombieJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ public double jockeyChance() {
|
|
+ return level().purpurConfig.zombieJockeyChance;
|
|
+ }
|
|
+
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level().purpurConfig.zombieJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.zombieTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.zombieAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
if (this.level().paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0D, 3)); // Paper - Add zombie targets turtle egg config
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.addBehaviourGoals();
|
|
}
|
|
|
|
@@ -124,7 +172,19 @@ public class Zombie extends Monster {
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers(ZombifiedPiglin.class));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
- if ( this.level().spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Spigot
|
|
+ // Purpur start
|
|
+ if ( this.level().spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Spigot
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse();
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
@@ -239,32 +299,7 @@ public class Zombie extends Monster {
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- if (this.isAlive()) {
|
|
- boolean flag = this.isSunSensitive() && this.isSunBurnTick();
|
|
-
|
|
- if (flag) {
|
|
- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
-
|
|
- if (!itemstack.isEmpty()) {
|
|
- if (itemstack.isDamageableItem()) {
|
|
- Item item = itemstack.getItem();
|
|
-
|
|
- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
- this.onEquippedItemBroken(item, EquipmentSlot.HEAD);
|
|
- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
- }
|
|
- }
|
|
-
|
|
- flag = false;
|
|
- }
|
|
-
|
|
- if (flag) {
|
|
- this.igniteForSeconds(8.0F);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
+ // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
super.aiStep();
|
|
}
|
|
|
|
@@ -324,6 +359,7 @@ public class Zombie extends Monster {
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ public boolean shouldBurnInDay() { return this.isSunSensitive(); } // Purpur - for ABI compatibility - API for any mob to burn daylight
|
|
public boolean isSunSensitive() {
|
|
return this.shouldBurnInDay; // Paper - Add more Zombie API
|
|
}
|
|
@@ -462,7 +498,7 @@ public class Zombie extends Monster {
|
|
nbt.putBoolean("CanBreakDoors", this.canBreakDoors());
|
|
nbt.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1);
|
|
nbt.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1);
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API
|
|
+ //nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
}
|
|
|
|
@Override
|
|
@@ -475,7 +511,7 @@ public class Zombie extends Monster {
|
|
this.startUnderWaterConversion(nbt.getInt("DrownedConversionTime"));
|
|
}
|
|
// Paper start - Add more Zombie API
|
|
- if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight
|
|
this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
}
|
|
// Paper end - Add more Zombie API
|
|
@@ -528,19 +564,20 @@ public class Zombie extends Monster {
|
|
}
|
|
|
|
if (object instanceof Zombie.ZombieGroupData entityzombie_groupdatazombie) {
|
|
- if (entityzombie_groupdatazombie.isBaby) {
|
|
- this.setBaby(true);
|
|
+ // Purpur start
|
|
+ if (!jockeyOnlyBaby() || entityzombie_groupdatazombie.isBaby) {
|
|
+ this.setBaby(entityzombie_groupdatazombie.isBaby);
|
|
if (entityzombie_groupdatazombie.canSpawnJockey) {
|
|
- if ((double) randomsource.nextFloat() < 0.05D) {
|
|
- List<Chicken> list = world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN);
|
|
+ if ((double) randomsource.nextFloat() < jockeyChance()) {
|
|
+ List<Chicken> list = jockeyTryExistingChickens() ? world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN) : java.util.Collections.emptyList();
|
|
+ // Purpur end
|
|
|
|
if (!list.isEmpty()) {
|
|
Chicken entitychicken = (Chicken) list.get(0);
|
|
|
|
entitychicken.setChickenJockey(true);
|
|
this.startRiding(entitychicken);
|
|
- }
|
|
- } else if ((double) randomsource.nextFloat() < 0.05D) {
|
|
+ } else { // Purpur
|
|
Chicken entitychicken1 = (Chicken) EntityType.CHICKEN.create(this.level(), EntitySpawnReason.JOCKEY);
|
|
|
|
if (entitychicken1 != null) {
|
|
@@ -550,6 +587,7 @@ public class Zombie extends Monster {
|
|
this.startRiding(entitychicken1);
|
|
world.addFreshEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit
|
|
}
|
|
+ } // Purpur
|
|
}
|
|
}
|
|
}
|
|
@@ -562,11 +600,7 @@ public class Zombie extends Monster {
|
|
}
|
|
|
|
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
|
|
- LocalDate localdate = LocalDate.now();
|
|
- int i = localdate.get(ChronoField.DAY_OF_MONTH);
|
|
- int j = localdate.get(ChronoField.MONTH_OF_YEAR);
|
|
-
|
|
- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) {
|
|
+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur
|
|
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
|
|
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
|
|
}
|
|
@@ -608,7 +642,7 @@ public class Zombie extends Monster {
|
|
}
|
|
|
|
protected void randomizeReinforcementsChance() {
|
|
- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.10000000149011612D);
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieSpawnReinforcements); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
index 18c19e4b675000aacb74344909fc104964231008..6f6b32bf7f68d05e4173c31f2e631a409b858a05 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
@@ -85,6 +85,58 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
|
|
});
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.zombieVillagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieVillagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.zombieVillagerControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieVillagerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieVillagerSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.zombieVillagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level().purpurConfig.zombieVillagerJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level().purpurConfig.zombieVillagerJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level().purpurConfig.zombieVillagerJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.zombieVillagerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
@@ -177,10 +229,10 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (itemstack.is(Items.GOLDEN_APPLE)) {
|
|
- if (this.hasEffect(MobEffects.WEAKNESS)) {
|
|
+ if (this.hasEffect(MobEffects.WEAKNESS) && level().purpurConfig.zombieVillagerCureEnabled) { // Purpur
|
|
itemstack.consume(1, player);
|
|
if (!this.level().isClientSide) {
|
|
- this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600);
|
|
+ this.startConverting(player.getUUID(), this.random.nextInt(level().purpurConfig.zombieVillagerCuringTimeMax - level().purpurConfig.zombieVillagerCuringTimeMin + 1) + level().purpurConfig.zombieVillagerCuringTimeMin); // Purpur
|
|
}
|
|
|
|
return InteractionResult.SUCCESS_SERVER;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
index 03e3cbe73119ca76417d4dd192e1560bdfc373ec..8c3271dcc8c9aa58e2e007eba282c11e42b4e0c9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
@@ -63,6 +63,54 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.setPathfindingMalus(PathType.LAVA, 8.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.zombifiedPiglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombifiedPiglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.zombifiedPiglinControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombifiedPiglinScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.zombifiedPiglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level().purpurConfig.zombifiedPiglinJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level().purpurConfig.zombifiedPiglinJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level().purpurConfig.zombifiedPiglinJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.zombifiedPiglinAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public void setPersistentAngerTarget(@Nullable UUID angryAt) {
|
|
this.persistentAngerTarget = angryAt;
|
|
@@ -110,7 +158,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.maybeAlertOthers();
|
|
}
|
|
|
|
- if (this.isAngry()) {
|
|
+ if (this.isAngry() && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur
|
|
this.lastHurtByPlayerTime = this.tickCount;
|
|
}
|
|
|
|
@@ -165,7 +213,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.ticksUntilNextAlert = ZombifiedPiglin.ALERT_INTERVAL.sample(this.random);
|
|
}
|
|
|
|
- if (entityliving instanceof Player) {
|
|
+ if (entityliving instanceof Player && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur
|
|
this.setLastHurtByPlayer((Player) entityliving);
|
|
}
|
|
|
|
@@ -245,7 +293,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
|
|
@Override
|
|
protected void randomizeReinforcementsChance() {
|
|
- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D);
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombifiedPiglinSpawnReinforcements); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java
|
|
index 444e67eb9fa1fabff2304896bdd71772747dc437..b48b0124014f1cc9d206d343daf7170691d04b3f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java
|
|
@@ -61,6 +61,29 @@ public class Creaking extends Monster {
|
|
this.xpReward = 0;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.creakingRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creakingRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.creakingControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected BodyRotationControl createBodyControl() {
|
|
return new Creaking.CreakingBodyRotationControl(this);
|
|
@@ -180,6 +203,14 @@ public class Creaking extends Monster {
|
|
return this.isActive() ? null : SoundEvents.CREAKING_AMBIENT;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.creakingMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.creakingScale);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected SoundEvent getHurtSound(DamageSource source) {
|
|
return SoundEvents.CREAKING_SWAY;
|
|
@@ -291,28 +322,28 @@ public class Creaking extends Monster {
|
|
}
|
|
}
|
|
|
|
- class CreakingLookControl extends LookControl {
|
|
+ class CreakingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
public CreakingLookControl(final Creaking creaking) {
|
|
super(creaking);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Creaking.this.canMove()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
|
|
- class CreakingMoveControl extends MoveControl {
|
|
+ class CreakingMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
public CreakingMoveControl(final Creaking creaking) {
|
|
super(creaking);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Creaking.this.canMove()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
index 9802aa9a1e4822684ba4406d2cafa4af69fe2ec5..4930459f7fe91eddb5049be3503bf0f2e3821ec4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
@@ -69,11 +69,49 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.hoglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.hoglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.hoglinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.hoglinMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.hoglinScale);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@VisibleForTesting
|
|
public void setTimeInOverworld(int timeInOverworld) {
|
|
this.timeInOverworld = timeInOverworld;
|
|
}
|
|
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level().purpurConfig.hoglinBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.hoglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.hoglinAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public boolean canBeLeashed() {
|
|
return true;
|
|
@@ -139,7 +177,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
|
|
private int behaviorTick; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
HoglinAi.updateActivity(this);
|
|
if (this.isConverting()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
index aeedddd4b2ade905455c04ce475d35042c54e741..26d2ce3504efd8077a2d8c7d29a179f9ba47d63b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
@@ -94,6 +94,39 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.piglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.piglinControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.piglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.piglinAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
@@ -305,7 +338,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
private int behaviorTick; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
PiglinAi.updateActivity(this);
|
|
super.customServerAiStep(world);
|
|
@@ -401,7 +434,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
|
|
@Override
|
|
public boolean wantsToPickUp(ServerLevel world, ItemStack stack) {
|
|
- return world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack);
|
|
+ return (world.purpurConfig.piglinBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); // Purpur
|
|
}
|
|
|
|
protected boolean canReplaceCurrentItem(ItemStack stack) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java
|
|
index e283b1296c1e831376bfe9491cbf02ed4b3fffe4..27a6de70530c2a1cbe2f77a7fb493038121710ea 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java
|
|
@@ -605,11 +605,18 @@ public class PiglinAi {
|
|
}
|
|
|
|
itemstack = (ItemStack) iterator.next();
|
|
- } while (!itemstack.is(ItemTags.PIGLIN_SAFE_ARMOR));
|
|
+ } while (!itemstack.is(ItemTags.PIGLIN_SAFE_ARMOR) && (!entity.level().purpurConfig.piglinIgnoresArmorWithGoldTrim || !isWearingGoldTrim(itemstack.getItem()))); // Purpur
|
|
|
|
return true;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private static boolean isWearingGoldTrim(Item itemstack) {
|
|
+ net.minecraft.world.item.equipment.trim.ArmorTrim armorTrim = itemstack.components().get(net.minecraft.core.component.DataComponents.TRIM);
|
|
+ return armorTrim != null && armorTrim.material().is(net.minecraft.world.item.equipment.trim.TrimMaterials.GOLD);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
private static void stopWalking(Piglin piglin) {
|
|
piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET);
|
|
piglin.getNavigation().stop();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
index 0cab5d5aa80f9ca8c34f982f0b81044328ba2d8f..e5e88d4b1d2a0ff043e75cf081eef60e774d3c0f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
@@ -63,6 +63,39 @@ public class PiglinBrute extends AbstractPiglin {
|
|
this.xpReward = 20;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.piglinBruteRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinBruteRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.piglinBruteControllable;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinBruteMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinBruteScale);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.piglinBruteTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.piglinBruteAlwaysDropExp;
|
|
+ }
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes()
|
|
.add(Attributes.MAX_HEALTH, 50.0)
|
|
@@ -113,6 +146,7 @@ public class PiglinBrute extends AbstractPiglin {
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel world) {
|
|
+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider
|
|
this.getBrain().tick(world, this);
|
|
PiglinBruteAi.updateActivity(this);
|
|
PiglinBruteAi.maybePlayActivitySound(this);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
index 64f3204b4b6ac0c57d0eb833a959f666f5259c6b..d0f744597de323f6169e15cabe9b3a80dbdbf5bb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
@@ -125,8 +125,32 @@ public class Warden extends Monster implements VibrationSystem {
|
|
this.setPathfindingMalus(PathType.LAVA, 8.0F);
|
|
this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F);
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.wardenRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wardenRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.wardenControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getAddEntityPacket(ServerEntity entityTrackerEntry) {
|
|
return new ClientboundAddEntityPacket(this, entityTrackerEntry, this.hasPose(Pose.EMERGING) ? 1 : 0);
|
|
@@ -392,17 +416,14 @@ public class Warden extends Monster implements VibrationSystem {
|
|
|
|
@Contract("null->false")
|
|
public boolean canTargetEntity(@Nullable Entity entity) {
|
|
- boolean flag;
|
|
-
|
|
+ if (getRider() != null && isControllable()) return false; // Purpur
|
|
if (entity instanceof LivingEntity entityliving) {
|
|
if (this.level() == entity.level() && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) && !this.isAlliedTo(entity) && entityliving.getType() != EntityType.ARMOR_STAND && entityliving.getType() != EntityType.WARDEN && !entityliving.isInvulnerable() && !entityliving.isDeadOrDying() && this.level().getWorldBorder().isWithinBounds(entityliving.getBoundingBox())) {
|
|
- flag = true;
|
|
- return flag;
|
|
+ return true; // Purpur - wtf
|
|
}
|
|
}
|
|
|
|
- flag = false;
|
|
- return flag;
|
|
+ return false; // Purpur - wtf
|
|
}
|
|
|
|
public static void applyDarknessAround(ServerLevel world, Vec3 pos, @Nullable Entity entity, int range) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
index 5f656fc726a1dc5f42657095a2f2b7cf85b92d7c..6c74cf1dea99b3b967b8c3d76f405f823c881fb9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
@@ -48,6 +48,7 @@ import org.bukkit.event.entity.VillagerAcquireTradeEvent;
|
|
// CraftBukkit end
|
|
|
|
public abstract class AbstractVillager extends AgeableMob implements InventoryCarrier, Npc, Merchant {
|
|
+ static final net.minecraft.world.item.crafting.Ingredient TEMPT_ITEMS = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.level.block.Blocks.EMERALD_BLOCK.asItem()); // Purpur
|
|
|
|
// CraftBukkit start
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
index b0236c7bf9441aa84d3795ffed05dd6099f29636..796dcc0dcf9022b455b8847e045266b8802da0cf 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
@@ -27,7 +27,7 @@ public class CatSpawner implements CustomSpawner {
|
|
if (this.nextTick > 0) {
|
|
return 0;
|
|
} else {
|
|
- this.nextTick = 1200;
|
|
+ this.nextTick = world.purpurConfig.catSpawnDelay; // Purpur
|
|
Player player = world.getRandomPlayer();
|
|
if (player == null) {
|
|
return 0;
|
|
@@ -61,8 +61,12 @@ public class CatSpawner implements CustomSpawner {
|
|
|
|
private int spawnInVillage(ServerLevel world, BlockPos pos) {
|
|
int i = 48;
|
|
- if (world.getPoiManager().getCountInRange(entry -> entry.is(PoiTypes.HOME), pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) {
|
|
- List<Cat> list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(48.0, 8.0, 48.0));
|
|
+ // Purpur start
|
|
+ int range = world.purpurConfig.catSpawnVillageScanRange;
|
|
+ if (range <= 0) return 0;
|
|
+ if (world.getPoiManager().getCountInRange(entry -> entry.is(PoiTypes.HOME), pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) {
|
|
+ List<Cat> list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range));
|
|
+ // Purpur end
|
|
if (list.size() < 5) {
|
|
return this.spawnCat(pos, world);
|
|
}
|
|
@@ -73,7 +77,11 @@ public class CatSpawner implements CustomSpawner {
|
|
|
|
private int spawnInHut(ServerLevel world, BlockPos pos) {
|
|
int i = 16;
|
|
- List<Cat> list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(16.0, 8.0, 16.0));
|
|
+ // Purpur start
|
|
+ int range = world.purpurConfig.catSpawnSwampHutScanRange;
|
|
+ if (range <= 0) return 0;
|
|
+ List<Cat> list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range));
|
|
+ // Purpur end
|
|
return list.size() < 1 ? this.spawnCat(pos, world) : 0;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
index bb5a924c203be427e3faf84917b86622fdec5f25..fd373d98f836c057c30c4fbd5d7618cc4e757b78 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
@@ -139,6 +139,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> {
|
|
return holder.is(PoiTypes.MEETING);
|
|
});
|
|
+ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur
|
|
+ private int notLobotomizedCount = 0; // Purpur
|
|
|
|
public long nextGolemPanic = -1; // Pufferfish
|
|
|
|
@@ -156,6 +158,93 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.villagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.villagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.villagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ if (level().purpurConfig.villagerFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canBeLeashed() {
|
|
+ return level().purpurConfig.villagerCanBeLeashed;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.villagerMaxHealth);
|
|
+ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.villagerScale);
|
|
+ this.getAttribute(Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.villagerTemptRange); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.villagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.villagerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
+ private boolean checkLobotomized() {
|
|
+ int interval = this.level().purpurConfig.villagerLobotomizeCheckInterval;
|
|
+ boolean shouldCheckForTradeLocked = this.level().purpurConfig.villagerLobotomizeWaitUntilTradeLocked;
|
|
+ if (this.notLobotomizedCount > 3) {
|
|
+ // check half as often if not lobotomized for the last 3+ consecutive checks
|
|
+ interval *= 2;
|
|
+ }
|
|
+ if (this.level().getGameTime() % interval == 0) {
|
|
+ // offset Y for short blocks like dirt_path/farmland
|
|
+ this.isLobotomized = !(shouldCheckForTradeLocked && this.getVillagerXp() == 0) && !canTravelFrom(BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z));
|
|
+
|
|
+ if (this.isLobotomized) {
|
|
+ this.notLobotomizedCount = 0;
|
|
+ } else {
|
|
+ this.notLobotomizedCount++;
|
|
+ }
|
|
+ }
|
|
+ return this.isLobotomized;
|
|
+ }
|
|
+
|
|
+ private boolean canTravelFrom(BlockPos pos) {
|
|
+ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south());
|
|
+ }
|
|
+
|
|
+ private boolean canTravelTo(BlockPos pos) {
|
|
+ net.minecraft.world.level.block.state.BlockState state = this.level().getBlockStateIfLoaded(pos);
|
|
+ if (state == null) {
|
|
+ // chunk not loaded
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.level.block.Block bottom = state.getBlock();
|
|
+ if (bottom instanceof net.minecraft.world.level.block.FenceBlock ||
|
|
+ bottom instanceof net.minecraft.world.level.block.FenceGateBlock ||
|
|
+ bottom instanceof net.minecraft.world.level.block.WallBlock) {
|
|
+ // bottom block is too tall to get over
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.level.block.Block top = level().getBlockState(pos.above()).getBlock();
|
|
+ // only if both blocks have no collision
|
|
+ return !bottom.hasCollision && !top.hasCollision;
|
|
+ }
|
|
+
|
|
@Override
|
|
public Brain<Villager> getBrain() {
|
|
return (Brain<Villager>) super.getBrain(); // CraftBukkit - decompile error
|
|
@@ -190,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
brain.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(0.5F));
|
|
} else {
|
|
brain.setSchedule(Schedule.VILLAGER_DEFAULT);
|
|
- brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT)));
|
|
+ brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F, this.level().purpurConfig.villagerClericsFarmWarts), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))); // Purpur
|
|
}
|
|
|
|
brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerprofession, 0.5F));
|
|
@@ -217,7 +306,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.TEMPT_RANGE, 10.0D); // Purpur - add TEMPT_RANGE
|
|
}
|
|
|
|
public boolean assignProfessionWhenSpawned() {
|
|
@@ -251,10 +340,18 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
// Paper start - EAR 2
|
|
this.customServerAiStep(world, false);
|
|
}
|
|
- protected void customServerAiStep(ServerLevel world, final boolean inactive) {
|
|
+ protected void customServerAiStep(ServerLevel world, boolean inactive) { // Purpur - not final
|
|
// Paper end - EAR 2
|
|
+ // Purpur start
|
|
+ if (this.level().purpurConfig.villagerLobotomizeEnabled) {
|
|
+ // treat as inactive if lobotomized
|
|
+ inactive = inactive || checkLobotomized();
|
|
+ } else {
|
|
+ this.isLobotomized = false;
|
|
+ }
|
|
+ // Purpur end
|
|
// Pufferfish start
|
|
- if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) {
|
|
+ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur
|
|
this.getBrain().tick(world, this); // Paper
|
|
}
|
|
// Pufferfish end
|
|
@@ -313,7 +410,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) {
|
|
if (this.isBaby()) {
|
|
this.setUnhappy();
|
|
- return InteractionResult.SUCCESS;
|
|
+ return tryRide(player, hand, InteractionResult.SUCCESS); // Purpur
|
|
} else {
|
|
if (!this.level().isClientSide) {
|
|
boolean flag = this.getOffers().isEmpty();
|
|
@@ -327,9 +424,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
if (flag) {
|
|
- return InteractionResult.CONSUME;
|
|
+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur
|
|
}
|
|
|
|
+ if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
|
|
+ if (this.level().purpurConfig.villagerAllowTrading) // Purpur
|
|
this.startTrading(player);
|
|
}
|
|
|
|
@@ -494,7 +593,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
while (iterator.hasNext()) {
|
|
MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
|
|
|
|
- merchantrecipe.updateDemand();
|
|
+ merchantrecipe.updateDemand(this.level().purpurConfig.villagerMinimumDemand); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -727,7 +826,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
|
|
@Override
|
|
public boolean canBreed() {
|
|
- return this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0;
|
|
+ return this.level().purpurConfig.villagerCanBreed && this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; // Purpur
|
|
}
|
|
|
|
private boolean hungry() {
|
|
@@ -906,6 +1005,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
|
|
public boolean hasFarmSeeds() {
|
|
return this.getInventory().hasAnyMatching((itemstack) -> {
|
|
+ // Purpur start
|
|
+ if (this.level().purpurConfig.villagerClericsFarmWarts && this.getVillagerData().getProfession() == VillagerProfession.CLERIC) {
|
|
+ return itemstack.is(Items.NETHER_WART);
|
|
+ }
|
|
+ // Purpur end
|
|
return itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS);
|
|
});
|
|
}
|
|
@@ -963,6 +1067,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
public void spawnGolemIfNeeded(ServerLevel world, long time, int requiredCount) {
|
|
+ if (world.purpurConfig.villagerSpawnIronGolemRadius > 0 && world.getEntitiesOfClass(net.minecraft.world.entity.animal.IronGolem.class, getBoundingBox().inflate(world.purpurConfig.villagerSpawnIronGolemRadius)).size() > world.purpurConfig.villagerSpawnIronGolemLimit) return; // Purpur
|
|
if (this.wantsToSpawnGolem(time)) {
|
|
AABB axisalignedbb = this.getBoundingBox().inflate(10.0D, 10.0D, 10.0D);
|
|
List<Villager> list = world.getEntitiesOfClass(Villager.class, axisalignedbb);
|
|
@@ -1027,6 +1132,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
|
|
@Override
|
|
public void startSleeping(BlockPos pos) {
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.bedExplodeOnVillagerSleep && this.level().getBlockState(pos).getBlock() instanceof net.minecraft.world.level.block.BedBlock) {
|
|
+ this.level().explode(null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (float) this.level().purpurConfig.bedExplosionPower, this.level().purpurConfig.bedExplosionFire, this.level().purpurConfig.bedExplosionEffect);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
super.startSleeping(pos);
|
|
this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime()); // CraftBukkit - decompile error
|
|
this.brain.eraseMemory(MemoryModuleType.WALK_TARGET);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
index 8734ab1bd8299bbf43906d81a349c2a13e0981a7..3ca83269311cbc18c9ef3ce62cff6a2d4dc0a683 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
@@ -31,7 +31,7 @@ public record VillagerProfession(
|
|
public static final VillagerProfession ARMORER = register("armorer", PoiTypes.ARMORER, SoundEvents.VILLAGER_WORK_ARMORER);
|
|
public static final VillagerProfession BUTCHER = register("butcher", PoiTypes.BUTCHER, SoundEvents.VILLAGER_WORK_BUTCHER);
|
|
public static final VillagerProfession CARTOGRAPHER = register("cartographer", PoiTypes.CARTOGRAPHER, SoundEvents.VILLAGER_WORK_CARTOGRAPHER);
|
|
- public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, SoundEvents.VILLAGER_WORK_CLERIC);
|
|
+ public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, ImmutableSet.of(Items.NETHER_WART), ImmutableSet.of(Blocks.SOUL_SAND), SoundEvents.VILLAGER_WORK_CLERIC); // Purpur
|
|
public static final VillagerProfession FARMER = register(
|
|
"farmer",
|
|
PoiTypes.FARMER,
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
index 1e77cce428d9e53142aaa2cf780b7f862d536eca..42c91e52060fad4a7a598f9e9ef88fd0e0ff8475 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
@@ -72,6 +72,50 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
//this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader // Paper - move back to MobSpawnerTrader - Vanilla behavior is that only traders spawned by it have this value set.
|
|
}
|
|
|
|
+ // Purpur - start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level().purpurConfig.wanderingTraderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wanderingTraderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level().purpurConfig.wanderingTraderControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canBeLeashed() {
|
|
+ return level().purpurConfig.wanderingTraderCanBeLeashed;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wanderingTraderMaxHealth);
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.wanderingTraderTemptRange); // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() {
|
|
+ return Mob.createMobAttributes().add(net.minecraft.world.entity.ai.attributes.Attributes.TEMPT_RANGE, 10.0D);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level().purpurConfig.wanderingTraderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level().purpurConfig.wanderingTraderAlwaysDropExp;
|
|
+ }
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
@@ -79,7 +123,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
return this.canDrinkPotion && this.level().isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
|
|
}));
|
|
this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> {
|
|
- return this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
|
|
+ return level().purpurConfig.milkClearsBeneficialEffects && this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API // Purpur
|
|
}));
|
|
this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D));
|
|
@@ -92,6 +136,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 0.5D));
|
|
this.goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this));
|
|
this.goalSelector.addGoal(2, new WanderingTrader.WanderToPositionGoal(this, 2.0D, 0.35D));
|
|
+ if (level().purpurConfig.wanderingTraderFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); // Purpur
|
|
this.goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.35D));
|
|
this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 0.35D));
|
|
this.goalSelector.addGoal(9, new InteractGoal(this, Player.class, 3.0F, 1.0F));
|
|
@@ -120,11 +165,13 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
|
|
if (!this.level().isClientSide) {
|
|
if (this.getOffers().isEmpty()) {
|
|
- return InteractionResult.CONSUME;
|
|
+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur
|
|
}
|
|
-
|
|
- this.setTradingPlayer(player);
|
|
- this.openTradingScreen(player, this.getDisplayName(), 1);
|
|
+ if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
|
|
+ if (this.level().purpurConfig.wanderingTraderAllowTrading) { // Purpur
|
|
+ this.setTradingPlayer(player);
|
|
+ this.openTradingScreen(player, this.getDisplayName(), 1);
|
|
+ } // Purpur
|
|
}
|
|
|
|
return InteractionResult.SUCCESS;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
index a728dcbf956f108f01c966c7531449a506a14a87..4c1378132201c1e5d1bc01f8c0cbba91629bcffa 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
@@ -160,7 +160,17 @@ public class WanderingTraderSpawner implements CustomSpawner {
|
|
int k = pos.getX() + this.random.nextInt(range * 2) - range;
|
|
int l = pos.getZ() + this.random.nextInt(range * 2) - range;
|
|
int i1 = world.getHeight(Heightmap.Types.WORLD_SURFACE, k, l);
|
|
- BlockPos blockposition2 = new BlockPos(k, i1, l);
|
|
+ // Purpur start - allow traders to spawn below nether roof
|
|
+ BlockPos.MutableBlockPos blockposition2 = new BlockPos.MutableBlockPos(k, i1, l);
|
|
+ if (world.dimensionType().hasCeiling()) {
|
|
+ do {
|
|
+ blockposition2.relative(net.minecraft.core.Direction.DOWN);
|
|
+ } while (!world.getBlockState(blockposition2).isAir());
|
|
+ do {
|
|
+ blockposition2.relative(net.minecraft.core.Direction.DOWN);
|
|
+ } while (world.getBlockState(blockposition2).isAir() && blockposition2.getY() > 0);
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
if (spawnplacementtype.isSpawnPositionOk(world, blockposition2, EntityType.WANDERING_TRADER)) {
|
|
blockposition1 = blockposition2;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
index 7187652d038a5ea7b3555f5ad6f3671263947375..3dc166fd669cecded3d40ef8722bed400f611d03 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
@@ -198,11 +198,21 @@ public abstract class Player extends LivingEntity {
|
|
private int currentImpulseContextResetGraceTime;
|
|
public boolean affectsSpawning = true; // Paper - Affects Spawning API
|
|
public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage
|
|
+ public int sixRowEnderchestSlotCount = -1; // Purpur
|
|
+ public int burpDelay = 0; // Purpur
|
|
+ public boolean canPortalInstant = false; // Purpur
|
|
|
|
// CraftBukkit start
|
|
public boolean fauxSleeping;
|
|
public int oldLevel = -1;
|
|
|
|
+ public void setAfk(boolean afk) {
|
|
+ }
|
|
+
|
|
+ public boolean isAfk() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
@Override
|
|
public CraftHumanEntity getBukkitEntity() {
|
|
return (CraftHumanEntity) super.getBukkitEntity();
|
|
@@ -211,6 +221,19 @@ public abstract class Player extends LivingEntity {
|
|
|
|
public final int sendAllPlayerInfoBucketIndex; // Gale - Purpur - spread out sending all player info
|
|
|
|
+ // Purpur start
|
|
+ public abstract void resetLastActionTime();
|
|
+
|
|
+ @Override
|
|
+ public boolean processClick(InteractionHand hand) {
|
|
+ Entity vehicle = getRootVehicle();
|
|
+ if (vehicle != null && vehicle.getRider() == this) {
|
|
+ return vehicle.onClick(hand);
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public Player(Level world, BlockPos pos, float yaw, GameProfile gameProfile) {
|
|
super(EntityType.PLAYER, world);
|
|
this.lastItemInMainHand = ItemStack.EMPTY;
|
|
@@ -256,6 +279,12 @@ public abstract class Player extends LivingEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (this.burpDelay > 0 && --this.burpDelay == 0) {
|
|
+ this.level().playSound(null, getX(), getY(), getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1.0F, this.level().random.nextFloat() * 0.1F + 0.9F);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.noPhysics = this.isSpectator();
|
|
if (this.isSpectator()) {
|
|
this.setOnGround(false);
|
|
@@ -340,6 +369,17 @@ public abstract class Player extends LivingEntity {
|
|
this.turtleHelmetTick();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (this.level().purpurConfig.playerNetheriteFireResistanceDuration > 0 && this.level().getGameTime() % 20 == 0) {
|
|
+ if (this.getItemBySlot(EquipmentSlot.HEAD).is(Items.NETHERITE_HELMET)
|
|
+ && this.getItemBySlot(EquipmentSlot.CHEST).is(Items.NETHERITE_CHESTPLATE)
|
|
+ && this.getItemBySlot(EquipmentSlot.LEGS).is(Items.NETHERITE_LEGGINGS)
|
|
+ && this.getItemBySlot(EquipmentSlot.FEET).is(Items.NETHERITE_BOOTS)) {
|
|
+ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, this.level().purpurConfig.playerNetheriteFireResistanceDuration, this.level().purpurConfig.playerNetheriteFireResistanceAmplifier, this.level().purpurConfig.playerNetheriteFireResistanceAmbient, this.level().purpurConfig.playerNetheriteFireResistanceShowParticles, this.level().purpurConfig.playerNetheriteFireResistanceShowIcon), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NETHERITE_ARMOR);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.cooldowns.tick();
|
|
this.updatePlayerPose();
|
|
if (this.currentImpulseContextResetGraceTime > 0) {
|
|
@@ -630,7 +670,7 @@ public abstract class Player extends LivingEntity {
|
|
while (iterator.hasNext()) {
|
|
Entity entity = (Entity) iterator.next();
|
|
|
|
- if (entity.getType() == EntityType.EXPERIENCE_ORB) {
|
|
+ if (entity.getType() == EntityType.EXPERIENCE_ORB && entity.level().purpurConfig.playerExpPickupDelay >= 0) { // Purpur
|
|
list1.add(entity);
|
|
} else if (!entity.isRemoved()) {
|
|
this.touch(entity);
|
|
@@ -1282,7 +1322,7 @@ public abstract class Player extends LivingEntity {
|
|
flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits
|
|
if (flag2) {
|
|
damagesource = damagesource.critical(true); // Paper start - critical damage API
|
|
- f *= 1.5F;
|
|
+ f *= this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur
|
|
}
|
|
|
|
float f3 = f + f1;
|
|
@@ -1648,7 +1688,7 @@ public abstract class Player extends LivingEntity {
|
|
}
|
|
|
|
@Override
|
|
- protected boolean canGlide() {
|
|
+ public boolean canGlide() { // Purpur
|
|
return !this.abilities.flying && super.canGlide();
|
|
}
|
|
|
|
@@ -1908,7 +1948,23 @@ public abstract class Player extends LivingEntity {
|
|
|
|
@Override
|
|
protected int getBaseExperienceReward(ServerLevel world) {
|
|
- return !world.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0;
|
|
+ // Purpur start
|
|
+ if (!world.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator()) {
|
|
+ int toDrop;
|
|
+ try {
|
|
+ toDrop = Math.round(((Number) scriptEngine.eval("let expLevel = " + experienceLevel + "; " +
|
|
+ "let expTotal = " + totalExperience + "; " +
|
|
+ "let exp = " + experienceProgress + "; " +
|
|
+ level().purpurConfig.playerDeathExpDropEquation)).floatValue());
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ toDrop = experienceLevel * 7;
|
|
+ }
|
|
+ return Math.min(toDrop, level().purpurConfig.playerDeathExpDropMax);
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -1986,6 +2042,13 @@ public abstract class Player extends LivingEntity {
|
|
return slot != EquipmentSlot.BODY;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return !level().purpurConfig.playerRidableInWater;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean setEntityOnShoulder(CompoundTag entityNbt) {
|
|
if (!this.isPassenger() && this.onGround() && !this.isInWater() && !this.isInPowderSnow) {
|
|
if (this.getShoulderEntityLeft().isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
index 6e313a45356ed2b043ef626c7ca191b294acaf70..5a6dc4366f8fb9f5d5df44c29597a1e174d95569 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
@@ -80,6 +80,7 @@ public abstract class AbstractArrow extends Projectile {
|
|
public ItemStack pickupItemStack;
|
|
@Nullable
|
|
public ItemStack firedFromWeapon;
|
|
+ public net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments = net.minecraft.world.item.enchantment.ItemEnchantments.EMPTY; // Purpur - Add an option to fix MC-3304 projectile looting
|
|
|
|
// Spigot Start
|
|
@Override
|
|
@@ -623,6 +624,12 @@ public abstract class AbstractArrow extends Projectile {
|
|
return this.firedFromWeapon;
|
|
}
|
|
|
|
+ // Purpur start - Add an option to fix MC-3304 projectile looting
|
|
+ public void setActualEnchantments(net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments) {
|
|
+ this.actualEnchantments = actualEnchantments;
|
|
+ }
|
|
+ // Purpur end - Add an option to fix MC-3304 projectile looting
|
|
+
|
|
protected SoundEvent getDefaultHitGroundSoundEvent() {
|
|
return SoundEvents.ARROW_HIT;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
index 2f00676f62478897ae4931ea06e047567c407535..55ea7f82fac9a3de6d7e0725a9b6ea08088bc85c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
@@ -23,13 +23,13 @@ public class LargeFireball extends Fireball {
|
|
|
|
public LargeFireball(EntityType<? extends LargeFireball> type, Level world) {
|
|
super(type, world);
|
|
- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
|
|
+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // CraftBukkit // Purpur
|
|
}
|
|
|
|
public LargeFireball(Level world, LivingEntity owner, Vec3 velocity, int explosionPower) {
|
|
super(EntityType.FIREBALL, owner, velocity, world);
|
|
this.explosionPower = explosionPower;
|
|
- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
|
|
+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // CraftBukkit // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -38,7 +38,7 @@ public class LargeFireball extends Fireball {
|
|
Level world = this.level();
|
|
|
|
if (world instanceof ServerLevel worldserver) {
|
|
- boolean flag = worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ boolean flag = worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur
|
|
|
|
// CraftBukkit start - fire ExplosionPrimeEvent
|
|
ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity());
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
index 958ea103cc80da7366cc33dc385b76d4f5c809f2..0b7f27a6cc6be58fa5b60002059c9fbb3b1b7b67 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
@@ -33,6 +33,12 @@ public class LlamaSpit extends Projectile {
|
|
this.setPos(owner.getX() - (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(owner.yBodyRot * 0.017453292F), owner.getEyeY() - 0.10000000149011612D, owner.getZ() + (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(owner.yBodyRot * 0.017453292F));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void super_tick() {
|
|
+ super.tick();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected double getDefaultGravity() {
|
|
return 0.06D;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
index 507a7f2116b020a5af4b8fff15b73dba9904874f..a7fe724fd2aec7a72781e7b3ab74ff317cec8fbf 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
@@ -519,7 +519,7 @@ public abstract class Projectile extends Entity implements TraceableEntity {
|
|
public boolean mayInteract(ServerLevel world, BlockPos pos) {
|
|
Entity entity = this.getOwner();
|
|
|
|
- return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.purpurConfig.projectilesBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
}
|
|
|
|
public boolean mayBreak(ServerLevel world) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
index bb159ea4baf208aab6d6fcfbbddacd5b089b55c8..588b07ec4501924a49264183b414a7fd64bb6550 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
@@ -30,7 +30,7 @@ public class SmallFireball extends Fireball {
|
|
super(EntityType.SMALL_FIREBALL, owner, velocity, world);
|
|
// CraftBukkit start
|
|
if (this.getOwner() != null && this.getOwner() instanceof Mob) {
|
|
- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
index 70961e151666a0ecf5b791853f4581eaebbdcc8b..0db58e7d63a5c1b43a2224c247979f23a1d3f899 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
@@ -58,11 +58,41 @@ public class Snowball extends ThrowableItemProjectile {
|
|
protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
super.onHitEntity(entityHitResult);
|
|
Entity entity = entityHitResult.getEntity();
|
|
- int i = entity instanceof Blaze ? 3 : 0;
|
|
+ int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur
|
|
|
|
entity.hurt(this.damageSources().thrown(this, this.getOwner()), (float) i);
|
|
}
|
|
|
|
+ // Purpur start - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire
|
|
+ @Override
|
|
+ protected void onHitBlock(net.minecraft.world.phys.BlockHitResult blockHitResult) {
|
|
+ super.onHitBlock(blockHitResult);
|
|
+
|
|
+ if (!this.level().isClientSide) {
|
|
+ net.minecraft.core.BlockPos blockposition = blockHitResult.getBlockPos();
|
|
+ net.minecraft.core.BlockPos blockposition1 = blockposition.relative(blockHitResult.getDirection());
|
|
+
|
|
+ net.minecraft.world.level.block.state.BlockState iblockdata = this.level().getBlockState(blockposition);
|
|
+
|
|
+ if (this.level().purpurConfig.snowballExtinguishesFire && this.level().getBlockState(blockposition1).is(net.minecraft.world.level.block.Blocks.FIRE)) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition1, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) {
|
|
+ this.level().removeBlock(blockposition1, false);
|
|
+ }
|
|
+ } else if (this.level().purpurConfig.snowballExtinguishesCandles && net.minecraft.world.level.block.AbstractCandleBlock.isLit(iblockdata)) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.AbstractCandleBlock.LIT, false))) {
|
|
+ net.minecraft.world.level.block.AbstractCandleBlock.extinguish(null, iblockdata, this.level(), blockposition);
|
|
+ }
|
|
+ } else if (this.level().purpurConfig.snowballExtinguishesCampfires && net.minecraft.world.level.block.CampfireBlock.isLitCampfire(iblockdata)) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false))) {
|
|
+ this.level().levelEvent(null, 1009, blockposition, 0);
|
|
+ net.minecraft.world.level.block.CampfireBlock.dowse(this.getOwner(), this.level(), blockposition, iblockdata);
|
|
+ this.level().setBlockAndUpdate(blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void onHit(HitResult hitResult) {
|
|
super.onHit(hitResult);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
index bd2684528157f928460f2143dd71a48e11983123..0720df603b4f89dd6aa346091b13033ad5d62907 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
@@ -152,10 +152,11 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
- if (this.random.nextFloat() < 0.05F && worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
|
|
+ if (this.random.nextFloat() < worldserver.purpurConfig.enderPearlEndermiteChance && worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur
|
|
Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(worldserver, EntitySpawnReason.TRIGGERED);
|
|
|
|
if (entityendermite != null) {
|
|
+ entityendermite.setPlayerSpawned(true); // Purpur
|
|
entityendermite.moveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
|
|
worldserver.addFreshEntity(entityendermite, CreatureSpawnEvent.SpawnReason.ENDER_PEARL);
|
|
}
|
|
@@ -170,7 +171,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
if (entityplayer1 != null) {
|
|
entityplayer1.resetFallDistance();
|
|
entityplayer1.resetCurrentImpulseContext();
|
|
- entityplayer1.hurtServer(entityplayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API
|
|
+ entityplayer1.hurtServer(entityplayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur
|
|
}
|
|
|
|
this.playSound(worldserver, vec3d);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
index 322733266fdca8ce43434a8ffea304c51794bcbb..489c26423a7f5bc9da45d247de57ec989cc74119 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
@@ -69,7 +69,7 @@ public class ThrownTrident extends AbstractArrow {
|
|
Entity entity = this.getOwner();
|
|
byte b0 = (Byte) this.entityData.get(ThrownTrident.ID_LOYALTY);
|
|
|
|
- if (b0 > 0 && (this.dealtDamage || this.isNoPhysics()) && entity != null) {
|
|
+ if (b0 > 0 && (this.dealtDamage || this.isNoPhysics() || (level().purpurConfig.tridentLoyaltyVoidReturnHeight < 0.0D && getY() < level().purpurConfig.tridentLoyaltyVoidReturnHeight)) && entity != null) { // Purpur
|
|
if (!this.isAcceptibleReturnOwner()) {
|
|
Level world = this.level();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
index 4c47b30867e30d84908abf93dbefc252bc8c3453..e63b408594b5d2673148e39c1deafc8510537bee 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
@@ -103,7 +103,7 @@ public class WitherSkull extends AbstractHurtingProjectile {
|
|
if (!this.level().isClientSide) {
|
|
// CraftBukkit start
|
|
// this.level().explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, World.a.MOB);
|
|
- ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false);
|
|
+ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.level().purpurConfig.witherExplosionRadius, false); // Purpur
|
|
this.level().getCraftServer().getPluginManager().callEvent(event);
|
|
|
|
if (!event.isCancelled()) {
|
|
@@ -115,6 +115,19 @@ public class WitherSkull extends AbstractHurtingProjectile {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canHitEntity(Entity target) {
|
|
+ // do not hit rider
|
|
+ return target != this.getRider() && super.canHitEntity(target);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
|
builder.define(WitherSkull.DATA_DANGEROUS, false);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
index ab132041982df2a701e4baea8195873f31b4a5fb..722c1660cf6b93d0f9c05cafe587b1834c5c3a22 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
@@ -345,7 +345,7 @@ public abstract class Raider extends PatrollingMonster {
|
|
}
|
|
|
|
private boolean cannotPickUpBanner() {
|
|
- if (!getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items
|
|
+ if ((!this.mob.level().purpurConfig.pillagerBypassMobGriefing == !getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING)) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur
|
|
if (!this.mob.hasActiveRaid()) {
|
|
return true;
|
|
} else if (this.mob.getCurrentRaid().isOver()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raids.java b/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
index 439d61d8689fabe940006b9b317a6810175dccfb..6b30941a84054efb5fcccb5d9e6c80d713a23889 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
@@ -26,6 +26,7 @@ import net.minecraft.world.phys.Vec3;
|
|
public class Raids extends SavedData {
|
|
|
|
private static final String RAID_FILE_ID = "raids";
|
|
+ public final Map<java.util.UUID, Integer> playerCooldowns = Maps.newHashMap();
|
|
public final Map<Integer, Raid> raidMap = Maps.newHashMap();
|
|
private final ServerLevel level;
|
|
private int nextAvailableID;
|
|
@@ -51,6 +52,17 @@ public class Raids extends SavedData {
|
|
|
|
public void tick() {
|
|
++this.tick;
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0 && this.tick % 20 == 0) {
|
|
+ com.google.common.collect.ImmutableMap.copyOf(playerCooldowns).forEach((uuid, i) -> {
|
|
+ if (i < 1) {
|
|
+ playerCooldowns.remove(uuid);
|
|
+ } else {
|
|
+ playerCooldowns.put(uuid, i - 1);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ // Purpur end
|
|
Iterator<Raid> iterator = this.raidMap.values().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -122,11 +134,13 @@ public class Raids extends SavedData {
|
|
*/
|
|
|
|
if (!raid.isStarted() || (raid.isInProgress() && raid.getRaidOmenLevel() < raid.getMaxRaidOmenLevel())) { // CraftBukkit - fixed a bug with raid: players could add up Bad Omen level even when the raid had finished
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0 && playerCooldowns.containsKey(player.getUUID())) return null; // Purpur
|
|
// CraftBukkit start
|
|
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(raid, player)) {
|
|
player.removeEffect(net.minecraft.world.effect.MobEffects.RAID_OMEN);
|
|
return null;
|
|
}
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), level.purpurConfig.raidCooldownSeconds); // Purpur
|
|
|
|
if (!raid.isStarted() && !this.raidMap.containsKey(raid.getId())) {
|
|
this.raidMap.put(raid.getId(), raid);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
|
index 1fdbef16cd29c8fc74578ac3328f985eca61088d..56c265940208bc94f531a5af94f564b59f35ebf3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
|
@@ -499,6 +499,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
|
|
|
|
if (f > 0.0F) {
|
|
this.landFriction = f;
|
|
+ if (level().purpurConfig.boatEjectPlayersOnLand) ejectPassengers(); // Purpur
|
|
return AbstractBoat.Status.ON_LAND;
|
|
} else {
|
|
return AbstractBoat.Status.IN_AIR;
|
|
@@ -929,7 +930,13 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
|
|
|
|
@Override
|
|
public final ItemStack getPickResult() {
|
|
- return new ItemStack((ItemLike) this.dropItem.get());
|
|
+ // Purpur start
|
|
+ final ItemStack boat = new ItemStack((ItemLike) this.dropItem.get());
|
|
+ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ boat.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, null);
|
|
+ }
|
|
+ return boat;
|
|
+ // Purpur end
|
|
}
|
|
|
|
public static enum Status {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
index cdc8606ffe5c75ee19d92e9f86f26b2a502d765e..b31940441596079aae1cd2a38b9d22be18358448 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
@@ -92,6 +92,10 @@ public abstract class AbstractMinecart extends VehicleEntity {
|
|
private double flyingY = 0.95;
|
|
private double flyingZ = 0.95;
|
|
public Double maxSpeed;
|
|
+ // Purpur start
|
|
+ public double storedMaxSpeed;
|
|
+ public boolean isNewBehavior;
|
|
+ // Purpur end
|
|
// CraftBukkit end
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
|
|
|
|
@@ -100,8 +104,13 @@ public abstract class AbstractMinecart extends VehicleEntity {
|
|
this.blocksBuilding = true;
|
|
if (AbstractMinecart.useExperimentalMovement(world)) {
|
|
this.behavior = new NewMinecartBehavior(this);
|
|
+ this.isNewBehavior = true; // Purpur
|
|
} else {
|
|
this.behavior = new OldMinecartBehavior(this);
|
|
+ // Purpur start
|
|
+ this.isNewBehavior = false;
|
|
+ maxSpeed = storedMaxSpeed = world.purpurConfig.minecartMaxSpeed;
|
|
+ // Purpur end
|
|
}
|
|
|
|
}
|
|
@@ -289,6 +298,14 @@ public abstract class AbstractMinecart extends VehicleEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (!this.isNewBehavior) {
|
|
+ if (storedMaxSpeed != level().purpurConfig.minecartMaxSpeed) {
|
|
+ maxSpeed = storedMaxSpeed = level().purpurConfig.minecartMaxSpeed;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// CraftBukkit start
|
|
double prevX = this.getX();
|
|
double prevY = this.getY();
|
|
@@ -426,16 +443,62 @@ public abstract class AbstractMinecart extends VehicleEntity {
|
|
this.behavior.moveAlongTrack(world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private Double lastSpeed;
|
|
+
|
|
+ public double getControllableSpeed() {
|
|
+ BlockState blockState = level().getBlockState(this.blockPosition());
|
|
+ if (!blockState.isSolid()) {
|
|
+ blockState = level().getBlockState(this.blockPosition().relative(Direction.DOWN));
|
|
+ }
|
|
+ Double speed = level().purpurConfig.minecartControllableBlockSpeeds.get(blockState.getBlock());
|
|
+ if (!blockState.isSolid()) {
|
|
+ speed = lastSpeed;
|
|
+ }
|
|
+ if (speed == null) {
|
|
+ speed = level().purpurConfig.minecartControllableBaseSpeed;
|
|
+ }
|
|
+ return lastSpeed = speed;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected void comeOffTrack(ServerLevel world) {
|
|
double d0 = this.getMaxSpeed(world);
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0));
|
|
+
|
|
+ // Purpur start
|
|
+ if (level().purpurConfig.minecartControllable && !isInWater() && !isInLava() && !passengers.isEmpty()) {
|
|
+ Entity passenger = passengers.get(0);
|
|
+ if (passenger instanceof Player) {
|
|
+ Player player = (Player) passenger;
|
|
+ if (player.jumping && this.onGround) {
|
|
+ setDeltaMovement(new Vec3(getDeltaMovement().x, level().purpurConfig.minecartControllableHopBoost, getDeltaMovement().z));
|
|
+ }
|
|
+ if (player.zza != 0.0F) {
|
|
+ Vector velocity = player.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed());
|
|
+ if (player.zza < 0.0) {
|
|
+ velocity.multiply(-0.5);
|
|
+ }
|
|
+ setDeltaMovement(new Vec3(velocity.getX(), getDeltaMovement().y, velocity.getZ()));
|
|
+ }
|
|
+ this.setYRot(passenger.getYRot() - 90);
|
|
+ maxUpStep = level().purpurConfig.minecartControllableStepHeight;
|
|
+ } else {
|
|
+ maxUpStep = 0.0F;
|
|
+ }
|
|
+ } else {
|
|
+ maxUpStep = 0.0F;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.onGround()) {
|
|
// CraftBukkit start - replace magic numbers with our variables
|
|
this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ));
|
|
// CraftBukkit end
|
|
}
|
|
+ else if (level().purpurConfig.minecartControllable) setDeltaMovement(new Vec3(getDeltaMovement().x * derailedX, getDeltaMovement().y, getDeltaMovement().z * derailedZ)); // Purpur
|
|
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
if (!this.onGround()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
index f43439b31a14b9db4744512465d81134ebe5b3e1..0b9741dfebd0bb95e8c0e1f55ce18dfb353f693a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
@@ -426,7 +426,7 @@ public class NewMinecartBehavior extends MinecartBehavior {
|
|
private Vec3 calculateBoostTrackSpeed(Vec3 velocity, BlockPos railPos, BlockState railState) {
|
|
if (railState.is(Blocks.POWERED_RAIL) && (Boolean) railState.getValue(PoweredRailBlock.POWERED)) {
|
|
if (velocity.length() > 0.01D) {
|
|
- return velocity.normalize().scale(velocity.length() + 0.06D);
|
|
+ return velocity.normalize().scale(velocity.length() + this.level().purpurConfig.poweredRailBoostModifier); // Purpur
|
|
} else {
|
|
Vec3 vec3d1 = this.minecart.getRedstoneDirection(railPos);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java b/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java
|
|
index 04a622f52353ebcc21f41c233f5a0fd67690cf4a..f10ce069ef427df16fd0ce0e60b85c805ca703f0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java
|
|
@@ -310,9 +310,9 @@ public class OldMinecartBehavior extends MinecartBehavior {
|
|
vec3d5 = this.getDeltaMovement();
|
|
d17 = vec3d5.horizontalDistance();
|
|
if (d17 > 0.01D) {
|
|
- double d19 = 0.06D;
|
|
+ double d19 = world.purpurConfig.poweredRailBoostModifier; // Purpur
|
|
|
|
- this.setDeltaMovement(vec3d5.add(vec3d5.x / d17 * 0.06D, 0.0D, vec3d5.z / d17 * 0.06D));
|
|
+ this.setDeltaMovement(vec3d5.add(vec3d5.x / d17 * world.purpurConfig.poweredRailBoostModifier, 0.0D, vec3d5.z / d17 * world.purpurConfig.poweredRailBoostModifier)); // Purpur
|
|
} else {
|
|
Vec3 vec3d6 = this.getDeltaMovement();
|
|
double d20 = vec3d6.x;
|
|
diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java
|
|
index 6a686be6a69ae890d519a54ca099d4ba14e5b9e1..b8b0b89b7f0a21ecff4ab6286f8a114e2d6b6b39 100644
|
|
--- a/src/main/java/net/minecraft/world/food/FoodData.java
|
|
+++ b/src/main/java/net/minecraft/world/food/FoodData.java
|
|
@@ -44,6 +44,7 @@ public class FoodData {
|
|
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityplayer, foodinfo.nutrition() + oldFoodLevel, itemstack);
|
|
|
|
if (!event.isCancelled()) {
|
|
+ if (entityplayer.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) entityplayer.burpDelay = entityplayer.level().purpurConfig.playerBurpDelay; // Purpur
|
|
this.add(event.getFoodLevel() - oldFoodLevel, foodinfo.saturation());
|
|
}
|
|
|
|
@@ -96,7 +97,7 @@ public class FoodData {
|
|
++this.tickTimer;
|
|
if (this.tickTimer >= this.starvationRate) { // CraftBukkit - add regen rate manipulation
|
|
if (player.getHealth() > 10.0F || enumdifficulty == Difficulty.HARD || player.getHealth() > 1.0F && enumdifficulty == Difficulty.NORMAL) {
|
|
- player.hurtServer(worldserver, player.damageSources().starve(), 1.0F);
|
|
+ player.hurtServer(worldserver, player.damageSources().starve(), player.level().purpurConfig.hungerStarvationDamage); // Purpur
|
|
}
|
|
|
|
this.tickTimer = 0;
|
|
diff --git a/src/main/java/net/minecraft/world/food/FoodProperties.java b/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
index 882b72799ae532f4e181214d5756ec024af223e2..9a3f2a95debcf8b94f7deb375922ea09b30aabab 100644
|
|
--- a/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
+++ b/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
@@ -33,7 +33,7 @@ public record FoodProperties(int nutrition, float saturation, boolean canAlwaysE
|
|
world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), (SoundEvent) consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, randomsource.triangle(1.0F, 0.4F));
|
|
if (user instanceof Player entityhuman) {
|
|
entityhuman.getFoodData().eat(this, stack, (ServerPlayer) entityhuman); // CraftBukkit
|
|
- world.playSound((Player) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(randomsource, 0.9F, 1.0F));
|
|
+ //world.playSound((Player) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(randomsource, 0.9F, 1.0F)); // Purpur - moved to Player#tick()
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
index 4680f77a275d8d2b226018db89a571ac25998dd8..bfc90524bd739ed1d91fe9912e38093b3c28928f 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
@@ -80,6 +80,7 @@ public abstract class AbstractContainerMenu {
|
|
@Nullable
|
|
private ContainerSynchronizer synchronizer;
|
|
private boolean suppressRemoteUpdates;
|
|
+ @Nullable protected ItemStack activeQuickItem = null; // Purpur - Anvil API
|
|
|
|
// CraftBukkit start
|
|
public boolean checkReachable = true;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
index 1240df9368855f836412b06cf564926a18bfe90d..e559eabed82d2f402908e5b80d1505076ccc53a2 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
@@ -114,7 +114,13 @@ public abstract class AbstractFurnaceMenu extends RecipeBookMenu {
|
|
} else if (slot != 1 && slot != 0) {
|
|
if (this.canSmelt(itemstack1)) {
|
|
if (!this.moveItemStackTo(itemstack1, 0, 1, false)) {
|
|
- return ItemStack.EMPTY;
|
|
+ // Purpur start - fix #625
|
|
+ if (this.isFuel(itemstack1)) {
|
|
+ if (!this.moveItemStackTo(itemstack1, 1, 2, false)) {
|
|
+ return ItemStack.EMPTY;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
} else if (this.isFuel(itemstack1)) {
|
|
if (!this.moveItemStackTo(itemstack1, 1, 2, false)) {
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
index 286ae002e1711ad9e800b7f2091988d66cd572a7..5959e1df9374bcb889be1726abb83b8c58962d05 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
@@ -25,6 +25,12 @@ import org.slf4j.Logger;
|
|
import org.bukkit.craftbukkit.inventory.view.CraftAnvilView;
|
|
// CraftBukkit end
|
|
|
|
+// Purpur start - Anvil API
|
|
+import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+// Purpur end - Anvil API
|
|
+
|
|
public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
public static final int INPUT_SLOT = 0;
|
|
@@ -55,6 +61,10 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
private CraftAnvilView bukkitEntity;
|
|
// CraftBukkit end
|
|
public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions
|
|
+ // Purpur start - Anvil API
|
|
+ public boolean bypassCost = false;
|
|
+ public boolean canDoUnsafeEnchants = false;
|
|
+ // Purpur end - Anvil API
|
|
|
|
public AnvilMenu(int syncId, Inventory inventory) {
|
|
this(syncId, inventory, ContainerLevelAccess.NULL);
|
|
@@ -82,12 +92,17 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
@Override
|
|
protected boolean mayPickup(Player player, boolean present) {
|
|
- return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && present; // CraftBukkit - allow cost 0 like a free item
|
|
+ return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && (this.bypassCost || this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST) && present; // CraftBukkit - allow cost 0 like a free item // Purpur - Anvil API
|
|
}
|
|
|
|
@Override
|
|
protected void onTake(Player player, ItemStack stack) {
|
|
+ // Purpur start - Anvil API
|
|
+ ItemStack itemstack = this.activeQuickItem != null ? this.activeQuickItem : stack;
|
|
+ if (org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)).callEvent();
|
|
+ // Purpur end - Anvil API
|
|
if (!player.getAbilities().instabuild) {
|
|
+ if (this.bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur - Anvil API
|
|
player.giveExperienceLevels(-this.cost.get());
|
|
}
|
|
|
|
@@ -138,6 +153,12 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
@Override
|
|
public void createResult() {
|
|
+ // Purpur start - Anvil API
|
|
+ this.bypassCost = false;
|
|
+ this.canDoUnsafeEnchants = false;
|
|
+ if (org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent(getBukkitView()).callEvent();
|
|
+ // Purpur end - Anvil API
|
|
+
|
|
ItemStack itemstack = this.inputSlots.getItem(0);
|
|
|
|
this.onlyRenaming = false;
|
|
@@ -146,7 +167,7 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
long j = 0L;
|
|
byte b0 = 0;
|
|
|
|
- if (!itemstack.isEmpty() && EnchantmentHelper.canStoreEnchantments(itemstack)) {
|
|
+ if (!itemstack.isEmpty() && this.canDoUnsafeEnchants || EnchantmentHelper.canStoreEnchantments(itemstack)) { // Purpur - Anvil API
|
|
ItemStack itemstack1 = itemstack.copy();
|
|
ItemStack itemstack2 = this.inputSlots.getItem(1);
|
|
ItemEnchantments.Mutable itemenchantments_a = new ItemEnchantments.Mutable(EnchantmentHelper.getEnchantmentsForCrafting(itemstack1));
|
|
@@ -213,7 +234,10 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1);
|
|
Enchantment enchantment = (Enchantment) holder.value();
|
|
- boolean flag3 = enchantment.canEnchant(itemstack);
|
|
+ // Purpur start - Config to allow unsafe enchants
|
|
+ boolean flag3 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || enchantment.canEnchant(itemstack); // whether the enchantment can be applied on specific item type
|
|
+ boolean flag4 = true; // whether two incompatible enchantments can be applied on a single item
|
|
+ // Purpur end - Config to allow unsafe enchants
|
|
|
|
if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) {
|
|
flag3 = true;
|
|
@@ -225,16 +249,22 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
Holder<Enchantment> holder1 = (Holder) iterator1.next();
|
|
|
|
if (!holder1.equals(holder) && !Enchantment.areCompatible(holder, holder1)) {
|
|
- flag3 = false;
|
|
+ flag4 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants; // Purpur - Anvil API // Purpur - flag3 -> flag4 - Config to allow unsafe enchants
|
|
+ // Purpur start - Config to allow unsafe enchants
|
|
+ if (!flag4 && org.purpurmc.purpur.PurpurConfig.replaceIncompatibleEnchants) {
|
|
+ iterator1.remove(); // replace current enchant with the incompatible one trying to be applied
|
|
+ flag4 = true;
|
|
+ }
|
|
+ // Purpur end - Config to allow unsafe enchants
|
|
++i;
|
|
}
|
|
}
|
|
|
|
- if (!flag3) {
|
|
+ if (!flag3 || !flag4) { // Purpur - Config to allow unsafe enchants
|
|
flag2 = true;
|
|
} else {
|
|
flag1 = true;
|
|
- if (i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.allowHigherEnchantsLevels && i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions // Purpur - Config to allow unsafe enchants
|
|
i2 = enchantment.getMaxLevel();
|
|
}
|
|
|
|
@@ -264,6 +294,54 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
if (!this.itemName.equals(itemstack.getHoverName().getString())) {
|
|
b0 = 1;
|
|
i += b0;
|
|
+ // Purpur start
|
|
+ if (this.player != null) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = this.player.getBukkitEntity();
|
|
+ String name = this.itemName;
|
|
+ boolean removeItalics = false;
|
|
+ if (player.hasPermission("purpur.anvil.remove_italics")) {
|
|
+ if (name.startsWith("&r")) {
|
|
+ name = name.substring(2);
|
|
+ removeItalics = true;
|
|
+ } else if (name.startsWith("<r>")) {
|
|
+ name = name.substring(3);
|
|
+ removeItalics = true;
|
|
+ } else if (name.startsWith("<reset>")) {
|
|
+ name = name.substring(7);
|
|
+ removeItalics = true;
|
|
+ }
|
|
+ }
|
|
+ if (this.player.level().purpurConfig.anvilAllowColors) {
|
|
+ if (player.hasPermission("purpur.anvil.color")) {
|
|
+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([0-9a-fr])").matcher(name);
|
|
+ while (matcher.find()) {
|
|
+ String match = matcher.group(1);
|
|
+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ //name = name.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1");
|
|
+ }
|
|
+ if (player.hasPermission("purpur.anvil.format")) {
|
|
+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([k-or])").matcher(name);
|
|
+ while (matcher.find()) {
|
|
+ String match = matcher.group(1);
|
|
+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ //name = name.replaceAll("(?i)&([l-or])", "\u00a7$1");
|
|
+ }
|
|
+ }
|
|
+ net.kyori.adventure.text.Component component;
|
|
+ if (this.player.level().purpurConfig.anvilColorsUseMiniMessage && player.hasPermission("purpur.anvil.minimessage")) {
|
|
+ component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.bukkit.ChatColor.stripColor(name));
|
|
+ } else {
|
|
+ component = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(name);
|
|
+ }
|
|
+ if (removeItalics) {
|
|
+ component = component.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ itemstack1.set(DataComponents.CUSTOM_NAME, io.papermc.paper.adventure.PaperAdventure.asVanilla(component));
|
|
+ }
|
|
+ else
|
|
+ // Purpur end
|
|
itemstack1.set(DataComponents.CUSTOM_NAME, Component.literal(this.itemName));
|
|
}
|
|
} else if (itemstack.has(DataComponents.CUSTOM_NAME)) {
|
|
@@ -287,6 +365,12 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
this.onlyRenaming = true;
|
|
}
|
|
|
|
+ // Purpur start - Anvil API
|
|
+ if (this.bypassCost && this.cost.get() >= this.maximumRepairCost) {
|
|
+ this.cost.set(this.maximumRepairCost - 1);
|
|
+ }
|
|
+ // Purpur end - Anvil API
|
|
+
|
|
if (this.cost.get() >= this.maximumRepairCost && !this.player.getAbilities().instabuild) { // CraftBukkit
|
|
itemstack1 = ItemStack.EMPTY;
|
|
}
|
|
@@ -307,6 +391,13 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemstack1); // CraftBukkit
|
|
this.broadcastChanges();
|
|
+
|
|
+ // Purpur start - Anvil API
|
|
+ if ((this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants) && itemstack1 != ItemStack.EMPTY) { // Purpur - Config to allow unsafe enchants
|
|
+ ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), 2, itemstack1));
|
|
+ ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetDataPacket(this.containerId, 0, this.cost.get()));
|
|
+ }
|
|
+ // Purpur end - Anvil API
|
|
} else {
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), ItemStack.EMPTY); // CraftBukkit
|
|
this.cost.set(AnvilMenu.DEFAULT_DENIED_COST); // CraftBukkit - use a variable for set a cost for denied item
|
|
@@ -315,7 +406,7 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
}
|
|
|
|
public static int calculateIncreasedRepairCost(int cost) {
|
|
- return (int) Math.min((long) cost * 2L + 1L, 2147483647L);
|
|
+ return org.purpurmc.purpur.PurpurConfig.anvilCumulativeCost ? (int) Math.min((long) cost * 2L + 1L, 2147483647L) : 0; // Purpur - Make anvil cumulative cost configurable
|
|
}
|
|
|
|
public boolean setItemName(String newItemName) {
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ArmorSlot.java b/src/main/java/net/minecraft/world/inventory/ArmorSlot.java
|
|
index 6c0b6abb1698fac9bb902f695b725d4ab783ee90..091e3c3514fcb378b68098114106d09f04d8fb0d 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/ArmorSlot.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ArmorSlot.java
|
|
@@ -45,7 +45,7 @@ class ArmorSlot extends Slot {
|
|
@Override
|
|
public boolean mayPickup(Player playerEntity) {
|
|
ItemStack itemStack = this.getItem();
|
|
- return (itemStack.isEmpty() || playerEntity.isCreative() || !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE))
|
|
+ return (itemStack.isEmpty() || playerEntity.isCreative() || (!EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE) || playerEntity.level().purpurConfig.playerRemoveBindingWithWeakness && playerEntity.hasEffect(net.minecraft.world.effect.MobEffects.WEAKNESS)))
|
|
&& super.mayPickup(playerEntity);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ChestMenu.java b/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
index 48a6b6136ac3414ca735f93a14b1a8d76210603c..27321b07cd04814bc1ff720c65770d7755625bb6 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
@@ -66,10 +66,30 @@ public class ChestMenu extends AbstractContainerMenu {
|
|
return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, 6);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static ChestMenu oneRow(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x1, syncId, playerInventory, inventory, 1);
|
|
+ }
|
|
+
|
|
+ public static ChestMenu twoRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x2, syncId, playerInventory, inventory, 2);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ChestMenu threeRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
return new ChestMenu(MenuType.GENERIC_9x3, syncId, playerInventory, inventory, 3);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static ChestMenu fourRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x4, syncId, playerInventory, inventory, 4);
|
|
+ }
|
|
+
|
|
+ public static ChestMenu fiveRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x5, syncId, playerInventory, inventory, 5);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ChestMenu sixRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, inventory, 6);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
index 50a735dd97daab4fb9579f922a4c63de60204f29..5b8ad051347f73553acb65c5ddc690d2b7eaa754 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
@@ -42,6 +42,12 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
|
|
import org.bukkit.entity.Player;
|
|
// CraftBukkit end
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.EnchantingTableBlockEntity;
|
|
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
|
|
+// Purpur end
|
|
+
|
|
public class EnchantmentMenu extends AbstractContainerMenu {
|
|
|
|
static final ResourceLocation EMPTY_SLOT_LAPIS_LAZULI = ResourceLocation.withDefaultNamespace("item/empty_slot_lapis_lazuli");
|
|
@@ -76,6 +82,22 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
return context.getLocation();
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void onClose(CraftHumanEntity who) {
|
|
+ super.onClose(who);
|
|
+
|
|
+ if (who.getHandle().level().purpurConfig.enchantmentTableLapisPersists) {
|
|
+ access.execute((level, pos) -> {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+ if (blockEntity instanceof EnchantingTableBlockEntity enchantmentTable) {
|
|
+ enchantmentTable.setLapis(this.getItem(1).getCount());
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
};
|
|
this.random = RandomSource.create();
|
|
this.enchantmentSeed = DataSlot.standalone();
|
|
@@ -100,6 +122,16 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
return Pair.of(InventoryMenu.BLOCK_ATLAS, EnchantmentMenu.EMPTY_SLOT_LAPIS_LAZULI);
|
|
}
|
|
});
|
|
+ // Purpur start
|
|
+ access.execute((level, pos) -> {
|
|
+ if (level.purpurConfig.enchantmentTableLapisPersists) {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+ if (blockEntity instanceof EnchantingTableBlockEntity enchantmentTable) {
|
|
+ this.getSlot(1).set(new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis()));
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
this.addStandardInventorySlots(playerInventory, 8, 84);
|
|
this.addDataSlot(DataSlot.shared(this.costs, 0));
|
|
this.addDataSlot(DataSlot.shared(this.costs, 1));
|
|
@@ -329,6 +361,7 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
public void removed(net.minecraft.world.entity.player.Player player) {
|
|
super.removed(player);
|
|
this.access.execute((world, blockposition) -> {
|
|
+ if (world.purpurConfig.enchantmentTableLapisPersists) this.getSlot(1).set(ItemStack.EMPTY); // Purpur
|
|
this.clearContainer(player, this.enchantSlots);
|
|
});
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
index 5687f492fc76f699e2a388790ca5380d9b8c8d0a..111da7435f0abb5a57bd2c5fecead2380ac4347a 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
@@ -96,12 +96,14 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
|
|
@Override
|
|
public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {
|
|
+ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur
|
|
context.execute((world, blockposition) -> {
|
|
if (world instanceof ServerLevel) {
|
|
// Paper start - Fire BlockExpEvent on grindstone use
|
|
org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition), this.getExperienceAmount(world));
|
|
event.callEvent();
|
|
- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player);
|
|
+ org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent grindstoneTakeResultEvent = new org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), event.getExpToDrop()); grindstoneTakeResultEvent.callEvent(); // Purpur
|
|
+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Purpur
|
|
// Paper end - Fire BlockExpEvent on grindstone use
|
|
}
|
|
|
|
@@ -135,7 +137,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
Holder<Enchantment> holder = (Holder) entry.getKey();
|
|
int k = entry.getIntValue();
|
|
|
|
- if (!holder.is(EnchantmentTags.CURSE)) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value())) { // Purpur
|
|
j += ((Enchantment) holder.value()).getMinCost(k);
|
|
}
|
|
}
|
|
@@ -222,7 +224,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
Entry<Holder<Enchantment>> entry = (Entry) iterator.next();
|
|
Holder<Enchantment> holder = (Holder) entry.getKey();
|
|
|
|
- if (!holder.is(EnchantmentTags.CURSE) || itemenchantments_a.getLevel(holder) == 0) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()) || itemenchantments_a.getLevel(holder) == 0) { // Purpur
|
|
itemenchantments_a.upgrade(holder, entry.getIntValue());
|
|
}
|
|
}
|
|
@@ -230,10 +232,70 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
});
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private java.util.List<net.minecraft.core.component.DataComponentType<?>> GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST = java.util.List.of(
|
|
+ // DataComponents.MAX_STACK_SIZE,
|
|
+ // DataComponents.DAMAGE,
|
|
+ // DataComponents.BLOCK_STATE,
|
|
+ DataComponents.CUSTOM_DATA,
|
|
+ // DataComponents.MAX_DAMAGE,
|
|
+ // DataComponents.UNBREAKABLE,
|
|
+ // DataComponents.CUSTOM_NAME,
|
|
+ // DataComponents.ITEM_NAME,
|
|
+ // DataComponents.LORE,
|
|
+ // DataComponents.RARITY,
|
|
+ // DataComponents.ENCHANTMENTS,
|
|
+ // DataComponents.CAN_PLACE_ON,
|
|
+ // DataComponents.CAN_BREAK,
|
|
+ DataComponents.ATTRIBUTE_MODIFIERS,
|
|
+ DataComponents.CUSTOM_MODEL_DATA,
|
|
+ // DataComponents.HIDE_ADDITIONAL_TOOLTIP,
|
|
+ // DataComponents.HIDE_TOOLTIP,
|
|
+ // DataComponents.REPAIR_COST,
|
|
+ // DataComponents.CREATIVE_SLOT_LOCK,
|
|
+ // DataComponents.ENCHANTMENT_GLINT_OVERRIDE,
|
|
+ // DataComponents.INTANGIBLE_PROJECTILE,
|
|
+ // DataComponents.FOOD,
|
|
+ // DataComponents.FIRE_RESISTANT,
|
|
+ // DataComponents.TOOL,
|
|
+ // DataComponents.STORED_ENCHANTMENTS,
|
|
+ DataComponents.DYED_COLOR,
|
|
+ // DataComponents.MAP_COLOR,
|
|
+ // DataComponents.MAP_ID,
|
|
+ // DataComponents.MAP_DECORATIONS,
|
|
+ // DataComponents.MAP_POST_PROCESSING,
|
|
+ // DataComponents.CHARGED_PROJECTILES,
|
|
+ // DataComponents.BUNDLE_CONTENTS,
|
|
+ // DataComponents.POTION_CONTENTS,
|
|
+ DataComponents.SUSPICIOUS_STEW_EFFECTS
|
|
+ // DataComponents.WRITABLE_BOOK_CONTENT,
|
|
+ // DataComponents.WRITTEN_BOOK_CONTENT,
|
|
+ // DataComponents.TRIM,
|
|
+ // DataComponents.DEBUG_STICK_STATE,
|
|
+ // DataComponents.ENTITY_DATA,
|
|
+ // DataComponents.BUCKET_ENTITY_DATA,
|
|
+ // DataComponents.BLOCK_ENTITY_DATA,
|
|
+ // DataComponents.INSTRUMENT,
|
|
+ // DataComponents.OMINOUS_BOTTLE_AMPLIFIER,
|
|
+ // DataComponents.RECIPES,
|
|
+ // DataComponents.LODESTONE_TRACKER,
|
|
+ // DataComponents.FIREWORK_EXPLOSION,
|
|
+ // DataComponents.FIREWORKS,
|
|
+ // DataComponents.PROFILE,
|
|
+ // DataComponents.NOTE_BLOCK_SOUND,
|
|
+ // DataComponents.BANNER_PATTERNS,
|
|
+ // DataComponents.BASE_COLOR,
|
|
+ // DataComponents.POT_DECORATIONS,
|
|
+ // DataComponents.CONTAINER,
|
|
+ // DataComponents.BEES,
|
|
+ // DataComponents.LOCK,
|
|
+ // DataComponents.CONTAINER_LOOT,
|
|
+ );
|
|
+ // Purpur end
|
|
private ItemStack removeNonCursesFrom(ItemStack item) {
|
|
ItemEnchantments itemenchantments = EnchantmentHelper.updateEnchantments(item, (itemenchantments_a) -> {
|
|
itemenchantments_a.removeIf((holder) -> {
|
|
- return !holder.is(EnchantmentTags.CURSE);
|
|
+ return !org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()); // Purpur
|
|
});
|
|
});
|
|
|
|
@@ -248,6 +310,23 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
}
|
|
|
|
item.set(DataComponents.REPAIR_COST, i);
|
|
+
|
|
+ // Purpur start
|
|
+ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder();
|
|
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveAttributes) {
|
|
+ item.getComponents().forEach(typedDataComponent -> {
|
|
+ if (GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST.contains(typedDataComponent.type())) {
|
|
+ builder.remove(typedDataComponent.type());
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveDisplay) {
|
|
+ builder.remove(DataComponents.CUSTOM_NAME);
|
|
+ builder.remove(DataComponents.LORE);
|
|
+ }
|
|
+ item.applyComponents(builder.build());
|
|
+ // Purpur end
|
|
+
|
|
return item;
|
|
}
|
|
|
|
@@ -309,7 +388,9 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
+ this.activeQuickItem = itemstack; // Purpur
|
|
slot1.onTake(player, itemstack1);
|
|
+ this.activeQuickItem = null; // Purpur
|
|
}
|
|
|
|
return itemstack;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
index a5d53a656513ae81cc3f9fc506caf6adaba62a8e..ac9df238ef0f3d009f25976b95e0b750e963e952 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
@@ -164,7 +164,9 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
+ this.activeQuickItem = itemstack; // Purpur - Anvil API
|
|
slot1.onTake(player, itemstack1);
|
|
+ this.activeQuickItem = null; // Purpur - Anvil API
|
|
}
|
|
|
|
return itemstack;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
index a15d5ff872dbd77f3c3145e0328f3d02e431ff8c..1dcf36d502990d32fc4cd3ea69c3ea334baed69a 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
@@ -31,11 +31,18 @@ public class PlayerEnderChestContainer extends SimpleContainer {
|
|
}
|
|
|
|
public PlayerEnderChestContainer(Player owner) {
|
|
- super(27);
|
|
+ super(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? 54 : 27); // Purpur
|
|
this.owner = owner;
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getContainerSize() {
|
|
+ return owner.sixRowEnderchestSlotCount < 0 ? super.getContainerSize() : owner.sixRowEnderchestSlotCount;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void setActiveChest(EnderChestBlockEntity blockEntity) {
|
|
this.activeChest = blockEntity;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/ArmorStandItem.java b/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
index cb4baebe22eeab17aed67a5ecc506b932fe2230b..c1a6734cc08de1c9fc413b1fa81a199ac9547ec2 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
@@ -59,6 +59,14 @@ public class ArmorStandItem extends Item {
|
|
return InteractionResult.FAIL;
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start
|
|
+ if (!world.purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ entityarmorstand.setCustomName(null);
|
|
+ }
|
|
+ if (world.purpurConfig.armorstandSetNameVisible && entityarmorstand.getCustomName() != null) {
|
|
+ entityarmorstand.setCustomNameVisible(true);
|
|
+ }
|
|
+ // Purpur end
|
|
worldserver.addFreshEntityWithPassengers(entityarmorstand);
|
|
world.playSound((Player) null, entityarmorstand.getX(), entityarmorstand.getY(), entityarmorstand.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F);
|
|
entityarmorstand.gameEvent(GameEvent.ENTITY_PLACE, context.getPlayer());
|
|
diff --git a/src/main/java/net/minecraft/world/item/AxeItem.java b/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
index abff08f2d61014944235ffe2f5494a718a28cc10..dc2c415ab227e1357533079ada4903e9f69d4f55 100644
|
|
--- a/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
@@ -62,13 +62,15 @@ public class AxeItem extends DiggerItem {
|
|
if (playerHasShieldUseIntent(context)) {
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
- Optional<BlockState> optional = this.evaluateNewBlockState(level, blockPos, player, level.getBlockState(blockPos));
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional = this.evaluateActionable(level, blockPos, player, level.getBlockState(blockPos)); // Purpur
|
|
if (optional.isEmpty()) {
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
+ org.purpurmc.purpur.tool.Actionable actionable = optional.get(); // Purpur
|
|
+ BlockState state = actionable.into().withPropertiesOf(level.getBlockState(blockPos)); // Purpur
|
|
ItemStack itemStack = context.getItemInHand();
|
|
// Paper start - EntityChangeBlockEvent
|
|
- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, optional.get())) {
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, state)) { // Purpur
|
|
return InteractionResult.PASS;
|
|
}
|
|
// Paper end
|
|
@@ -76,8 +78,15 @@ public class AxeItem extends DiggerItem {
|
|
CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, blockPos, itemStack);
|
|
}
|
|
|
|
- level.setBlock(blockPos, optional.get(), 11);
|
|
- level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, optional.get()));
|
|
+ // Purpur start
|
|
+ level.setBlock(blockPos, state, 11);
|
|
+ actionable.drops().forEach((drop, chance) -> {
|
|
+ if (level.random.nextDouble() < chance) {
|
|
+ Block.popResourceFromFace(level, blockPos, context.getClickedFace(), new ItemStack(drop));
|
|
+ }
|
|
+ });
|
|
+ level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, state));
|
|
+ // Purpur end
|
|
if (player != null) {
|
|
itemStack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(context.getHand()));
|
|
}
|
|
@@ -92,22 +101,24 @@ public class AxeItem extends DiggerItem {
|
|
return context.getHand().equals(InteractionHand.MAIN_HAND) && player.getOffhandItem().is(Items.SHIELD) && !player.isSecondaryUseActive();
|
|
}
|
|
|
|
- private Optional<BlockState> evaluateNewBlockState(Level world, BlockPos pos, @Nullable Player player, BlockState state) {
|
|
- Optional<BlockState> optional = this.getStripped(state);
|
|
+ private Optional<org.purpurmc.purpur.tool.Actionable> evaluateActionable(Level world, BlockPos pos, @Nullable Player player, BlockState state) { // Purpur
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional = Optional.ofNullable(world.purpurConfig.axeStrippables.get(state.getBlock())); // Purpur
|
|
if (optional.isPresent()) {
|
|
- world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ world.playSound(STRIPPABLES.containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
return optional;
|
|
} else {
|
|
- Optional<BlockState> optional2 = WeatheringCopper.getPrevious(state);
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional2 = Optional.ofNullable(world.purpurConfig.axeWeatherables.get(state.getBlock())); // Purpur
|
|
if (optional2.isPresent()) {
|
|
- world.playSound(player, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ world.playSound(WeatheringCopper.getPrevious(state).isPresent() ? player : null, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
world.levelEvent(player, 3005, pos, 0);
|
|
return optional2;
|
|
} else {
|
|
- Optional<BlockState> optional3 = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(state.getBlock()))
|
|
- .map(block -> block.withPropertiesOf(state));
|
|
+ // Purpur start
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional3 = Optional.ofNullable(world.purpurConfig.axeWaxables.get(state.getBlock()));
|
|
+ // .map(block -> block.withPropertiesOf(state));
|
|
+ // Purpur end
|
|
if (optional3.isPresent()) {
|
|
- world.playSound(player, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ world.playSound(HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
world.levelEvent(player, 3004, pos, 0);
|
|
return optional3;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
index c816c935ecc74a811ffdffbe6ded73c06e92324a..d58619d1d63a03598b8740dd789d4b6f2c93f8d0 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
@@ -151,7 +151,16 @@ public class BlockItem extends Item {
|
|
}
|
|
|
|
protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) {
|
|
- return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack);
|
|
+ // Purpur start
|
|
+ boolean handled = updateCustomBlockEntityTag(world, player, pos, stack);
|
|
+ if (world.purpurConfig.persistentTileEntityLore) {
|
|
+ BlockEntity blockEntity1 = world.getBlockEntity(pos);
|
|
+ if (blockEntity1 != null) {
|
|
+ blockEntity1.setPersistentLore(stack.getOrDefault(DataComponents.LORE, net.minecraft.world.item.component.ItemLore.EMPTY));
|
|
+ }
|
|
+ }
|
|
+ return handled;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Nullable
|
|
@@ -213,6 +222,7 @@ public class BlockItem extends Item {
|
|
|
|
if (tileentity != null) {
|
|
if (!world.isClientSide && tileentity.onlyOpCanSetNbt() && (player == null || !(player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place"))))) { // Spigot - add permission
|
|
+ if (!(!world.isClientSide && world.purpurConfig.silkTouchEnabled && tileentity instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity && player.getBukkitEntity().hasPermission("purpur.drop.spawners")))
|
|
return false;
|
|
}
|
|
|
|
@@ -248,6 +258,7 @@ public class BlockItem extends Item {
|
|
ItemContainerContents itemcontainercontents = (ItemContainerContents) entity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
|
|
|
|
if (itemcontainercontents != null) {
|
|
+ if (entity.level().purpurConfig.shulkerBoxItemDropContentsWhenDestroyed && this.getBlock() instanceof ShulkerBoxBlock) // Purpur
|
|
ItemUtils.onContainerDestroyed(entity, itemcontainercontents.nonEmptyItemsCopy());
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/BoatItem.java b/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
index e51ffd6c5047ee907a58f3029f0ea7fc66aedfa7..78d41d57df9cb61b295f1f54db1e1d62c13db701 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
@@ -71,6 +71,11 @@ public class BoatItem extends Item {
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
abstractboat.setYRot(user.getYRot());
|
|
+ // Purpur start
|
|
+ if (!world.purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ abstractboat.setCustomName(null);
|
|
+ }
|
|
+ // Purpur end
|
|
if (!world.noCollision(abstractboat, abstractboat.getBoundingBox())) {
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/item/BowItem.java b/src/main/java/net/minecraft/world/item/BowItem.java
|
|
index bb593209c95c9cf1f9c5d52d52fab4a33ddbabcf..58fa528e4b2589d362eb976afd6221cd94f2623c 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BowItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BowItem.java
|
|
@@ -28,6 +28,11 @@ public class BowItem extends ProjectileWeaponItem {
|
|
return false;
|
|
} else {
|
|
ItemStack itemStack = player.getProjectile(stack);
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.infinityWorksWithoutArrows && itemStack.isEmpty() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, stack) > 0) {
|
|
+ itemStack = new ItemStack(Items.ARROW);
|
|
+ }
|
|
+ // Purpur end
|
|
if (itemStack.isEmpty()) {
|
|
return false;
|
|
} else {
|
|
@@ -38,7 +43,7 @@ public class BowItem extends ProjectileWeaponItem {
|
|
} else {
|
|
List<ItemStack> list = draw(stack, itemStack, player);
|
|
if (world instanceof ServerLevel serverLevel && !list.isEmpty()) {
|
|
- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, f * 3.0F, 1.0F, f == 1.0F, null);
|
|
+ this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, f * 3.0F, (float) world.purpurConfig.bowProjectileOffset, f == 1.0F, null); // Purpur
|
|
}
|
|
|
|
world.playSound(
|
|
@@ -89,7 +94,7 @@ public class BowItem extends ProjectileWeaponItem {
|
|
public InteractionResult use(Level world, Player user, InteractionHand hand) {
|
|
ItemStack itemStack = user.getItemInHand(hand);
|
|
boolean bl = !user.getProjectile(itemStack).isEmpty();
|
|
- if (!user.hasInfiniteMaterials() && !bl) {
|
|
+ if (!user.hasInfiniteMaterials() && !bl && !(world.purpurConfig.infinityWorksWithoutArrows && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemStack) > 0)) { // Purpur
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
user.startUsingItem(hand);
|
|
diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
index 3bddfb6f7412ab86e0c090d0cbc6cf254b3f891c..6aa8ee091d3a7d2826d08ab9a03f970ef71a81ea 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
@@ -196,7 +196,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
|
|
// CraftBukkit end
|
|
if (!flag2) {
|
|
return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit
|
|
- } else if (world.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) {
|
|
+ } else if ((world.dimensionType().ultraWarm() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur
|
|
int i = blockposition.getX();
|
|
int j = blockposition.getY();
|
|
int k = blockposition.getZ();
|
|
@@ -204,7 +204,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
|
|
world.playSound(entityhuman, blockposition, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
|
|
|
|
for (int l = 0; l < 8; ++l) {
|
|
- world.addParticle(ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D);
|
|
+ ((ServerLevel) world).sendParticles(null, ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D, true); // Purpur
|
|
}
|
|
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
index 52c40eafc77e50a6fd21b9a7a250cea501f11690..86204c2ab5bbd5d45ddb1d626f844d91ccae6b4f 100644
|
|
--- a/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
@@ -69,7 +69,7 @@ public class CrossbowItem extends ProjectileWeaponItem {
|
|
ItemStack itemStack = user.getItemInHand(hand);
|
|
ChargedProjectiles chargedProjectiles = itemStack.get(DataComponents.CHARGED_PROJECTILES);
|
|
if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) {
|
|
- this.performShooting(world, user, hand, itemStack, getShootingPower(chargedProjectiles), 1.0F, null);
|
|
+ this.performShooting(world, user, hand, itemStack, getShootingPower(chargedProjectiles), (float) world.purpurConfig.crossbowProjectileOffset, null); // Purpur
|
|
return InteractionResult.CONSUME;
|
|
} else if (!user.getProjectile(itemStack).isEmpty()) {
|
|
this.startSoundPlayed = false;
|
|
diff --git a/src/main/java/net/minecraft/world/item/DyeColor.java b/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
index 79dc7cf5bfe92b4df21d164f39726dfe618331e4..6721432f9cdd11c9658c34f0ac407be217f9d276 100644
|
|
--- a/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
+++ b/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
@@ -103,4 +103,10 @@ public enum DyeColor implements StringRepresentable {
|
|
public String getSerializedName() {
|
|
return this.name;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static DyeColor random(net.minecraft.util.RandomSource random) {
|
|
+ return values()[random.nextInt(values().length)];
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/EggItem.java b/src/main/java/net/minecraft/world/item/EggItem.java
|
|
index 3ddd34e5d05fa1355a2affd329d72dea216cd0e4..770bdb3fb2426083ff6785f1c38ffe9d11f898e5 100644
|
|
--- a/src/main/java/net/minecraft/world/item/EggItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/EggItem.java
|
|
@@ -27,7 +27,7 @@ public class EggItem extends Item implements ProjectileItem {
|
|
if (world instanceof ServerLevel worldserver) {
|
|
// CraftBukkit start
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
- final Projectile.Delayed<ThrownEgg> thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
|
|
+ final Projectile.Delayed<ThrownEgg> thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, worldserver, itemstack, user, 0.0F, 1.5F, (float) worldserver.purpurConfig.eggProjectileOffset); // Purpur
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity());
|
|
if (event.callEvent() && thrownEgg.attemptSpawn()) {
|
|
if (event.shouldConsume()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java
|
|
index 9f92b9addaedb4bae06b32226a74c8e5ddc6c2a2..0a05fedc9108fa5cf5a5d9e3395bcbbd735fc87d 100644
|
|
--- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java
|
|
@@ -27,7 +27,7 @@ public class EndCrystalItem extends Item {
|
|
BlockPos blockposition = context.getClickedPos();
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
|
|
- if (!iblockdata.is(Blocks.OBSIDIAN) && !iblockdata.is(Blocks.BEDROCK)) {
|
|
+ if (!world.purpurConfig.endCrystalPlaceAnywhere && !iblockdata.is(Blocks.OBSIDIAN) && !iblockdata.is(Blocks.BEDROCK)) {
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
BlockPos blockposition1 = blockposition.above(); final BlockPos aboveBlockPosition = blockposition1; // Paper - OBFHELPER
|
|
diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
index b232390d8ee8e449e61c0ea7f3af60df507abb97..4039d300debadf29e6c544e8b4c950b7121a02d1 100644
|
|
--- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
@@ -24,7 +24,7 @@ public class EnderpearlItem extends Item {
|
|
if (world instanceof ServerLevel worldserver) {
|
|
// CraftBukkit start
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
- final Projectile.Delayed<ThrownEnderpearl> thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
|
|
+ final Projectile.Delayed<ThrownEnderpearl> thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, 1.5F, (float) worldserver.purpurConfig.enderPearlProjectileOffset); // Purpur
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity());
|
|
if (event.callEvent() && thrownEnderpearl.attemptSpawn()) {
|
|
if (event.shouldConsume()) {
|
|
@@ -35,6 +35,7 @@ public class EnderpearlItem extends Item {
|
|
|
|
world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
|
|
user.awardStat(Stats.ITEM_USED.get(this));
|
|
+ user.getCooldowns().addCooldown(itemstack, user.getAbilities().instabuild ? world.purpurConfig.enderPearlCooldownCreative : world.purpurConfig.enderPearlCooldown); // Purpur
|
|
} else {
|
|
// Paper end - PlayerLaunchProjectileEvent
|
|
if (user instanceof net.minecraft.server.level.ServerPlayer) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
index 29a048a9b09166838616ac7ba1d31625d56b0bca..184e6d9bf393188fc1f1c7acd545b4ac6d31f6a4 100644
|
|
--- a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
@@ -66,6 +66,18 @@ public class FireworkRocketItem extends Item implements ProjectileItem {
|
|
com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) delayed.projectile().getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand));
|
|
if (event.callEvent() && delayed.attemptSpawn()) {
|
|
user.awardStat(Stats.ITEM_USED.get(this)); // Moved up from below
|
|
+
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.elytraDamagePerFireworkBoost > 0) {
|
|
+ List<net.minecraft.world.entity.EquipmentSlot> list = net.minecraft.world.entity.EquipmentSlot.VALUES.stream().filter((enumitemslot) -> net.minecraft.world.entity.LivingEntity.canGlideUsing(user.getItemBySlot(enumitemslot), enumitemslot)).toList();
|
|
+ net.minecraft.world.entity.EquipmentSlot enumitemslot = net.minecraft.Util.getRandom(list, user.random);
|
|
+
|
|
+ ItemStack glideItem = user.getItemBySlot(enumitemslot);
|
|
+ if (user.canGlide()) {
|
|
+ glideItem.hurtAndBreak(world.purpurConfig.elytraDamagePerFireworkBoost, user, enumitemslot);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (event.shouldConsume() && !user.hasInfiniteMaterials()) {
|
|
itemStack.shrink(1); // Moved up from below
|
|
} else ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
|
|
diff --git a/src/main/java/net/minecraft/world/item/HangingEntityItem.java b/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
index cdc17ad948d8ac5de62f14b1a561433d33211f32..44a7cee7df2927a923455e8cedaab59307b42506 100644
|
|
--- a/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
@@ -75,6 +75,11 @@ public class HangingEntityItem extends Item {
|
|
|
|
if (!customdata.isEmpty()) {
|
|
EntityType.updateCustomEntityTag(world, entityhuman, (Entity) object, customdata);
|
|
+ // Purpur start
|
|
+ if (!world.purpurConfig.persistentDroppableEntityDisplayNames) {
|
|
+ ((Entity) object).setCustomName(null);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (((HangingEntity) object).survives()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/HoeItem.java b/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
index d2871bb4fd670ae4133d13f290b3256c9177d8e6..0936bdc945f73c7750c20a34276aead2921eeb61 100644
|
|
--- a/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
@@ -46,15 +46,23 @@ public class HoeItem extends DiggerItem {
|
|
public InteractionResult useOn(UseOnContext context) {
|
|
Level level = context.getLevel();
|
|
BlockPos blockPos = context.getClickedPos();
|
|
- Pair<Predicate<UseOnContext>, Consumer<UseOnContext>> pair = TILLABLES.get(level.getBlockState(blockPos).getBlock());
|
|
- if (pair == null) {
|
|
- return InteractionResult.PASS;
|
|
- } else {
|
|
- Predicate<UseOnContext> predicate = pair.getFirst();
|
|
- Consumer<UseOnContext> consumer = pair.getSecond();
|
|
+ // Purpur start
|
|
+ Block clickedBlock = level.getBlockState(blockPos).getBlock();
|
|
+ var tillable = level.purpurConfig.hoeTillables.get(clickedBlock);
|
|
+ if (tillable == null) { return InteractionResult.PASS; } else {
|
|
+ Predicate<UseOnContext> predicate = tillable.condition().predicate();
|
|
+ Consumer<UseOnContext> consumer = (ctx) -> {
|
|
+ level.setBlock(blockPos, tillable.into().defaultBlockState(), 11);
|
|
+ tillable.drops().forEach((drop, chance) -> {
|
|
+ if (level.random.nextDouble() < chance) {
|
|
+ Block.popResourceFromFace(level, blockPos, ctx.getClickedFace(), new ItemStack(drop));
|
|
+ }
|
|
+ });
|
|
+ };
|
|
+ // Purpur end
|
|
if (predicate.test(context)) {
|
|
Player player = context.getPlayer();
|
|
- level.playSound(player, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ if (!TILLABLES.containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
if (!level.isClientSide) {
|
|
consumer.accept(context);
|
|
if (player != null) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
index 9de79c7087d3ce55631f08191a9b7994567f1788..1029499ce8fb236a23beb9dae168b82039734e59 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -503,6 +503,7 @@ public final class ItemStack implements DataComponentHolder {
|
|
world.isBlockPlaceCancelled = true; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
|
|
for (BlockState blockstate : blocks) {
|
|
blockstate.update(true, false);
|
|
+ ((CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur
|
|
}
|
|
world.isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
|
|
world.preventPoiUpdated = false;
|
|
@@ -535,6 +536,7 @@ public final class ItemStack implements DataComponentHolder {
|
|
if (!(block.getBlock() instanceof BaseEntityBlock)) { // Containers get placed automatically
|
|
block.onPlace(world, newblockposition, oldBlock, true, context);
|
|
}
|
|
+ block.getBlock().forgetPlacer(); // Purpur
|
|
|
|
world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getBlockState(newblockposition), updateFlag, 512); // send null chunk as chunk.k() returns false by this point
|
|
}
|
|
@@ -681,6 +683,26 @@ public final class ItemStack implements DataComponentHolder {
|
|
return this.isDamageableItem() && this.getDamageValue() > 0;
|
|
}
|
|
|
|
+ // Purpur start - Add option to mend the most damaged equipment first
|
|
+ public float getDamagePercent() {
|
|
+ if (this.has(DataComponents.UNBREAKABLE)) {
|
|
+ return 0.0F;
|
|
+ }
|
|
+
|
|
+ final int maxDamage = this.getOrDefault(DataComponents.MAX_DAMAGE, 0);
|
|
+ if (maxDamage == 0) {
|
|
+ return 0.0F;
|
|
+ }
|
|
+
|
|
+ final int damage = this.getOrDefault(DataComponents.DAMAGE, 0);
|
|
+ if (damage == 0) {
|
|
+ return 0.0F;
|
|
+ }
|
|
+
|
|
+ return (float) damage / maxDamage;
|
|
+ }
|
|
+ // Purpur end - Add option to mend the most damaged equipment first
|
|
+
|
|
public int getDamageValue() {
|
|
return Mth.clamp((Integer) this.getOrDefault(DataComponents.DAMAGE, 0), 0, this.getMaxDamage());
|
|
}
|
|
@@ -761,6 +783,12 @@ public final class ItemStack implements DataComponentHolder {
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent(serverPlayer, this); // Paper - Add EntityDamageItemEvent
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start
|
|
+ if (this.has(DataComponents.GLIDER)) {
|
|
+ setDamageValue(this.getMaxDamage() - 1);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
this.shrink(1);
|
|
breakCallback.accept(item);
|
|
@@ -1325,6 +1353,12 @@ public final class ItemStack implements DataComponentHolder {
|
|
return !((ItemEnchantments) this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).isEmpty();
|
|
}
|
|
|
|
+ // Purpur start - Config to allow unsafe enchants
|
|
+ public boolean hasEnchantment(Holder<Enchantment> enchantment) {
|
|
+ return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).getLevel(enchantment) > 0;
|
|
+ }
|
|
+ // Purpur end - Config to allow unsafe enchants
|
|
+
|
|
public ItemEnchantments getEnchantments() {
|
|
return (ItemEnchantments) this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java
|
|
index 5a70111cd39af50981cfd440c021340da1de5eab..580bd63fdbf9555f867362d3c1f39f41fd750089 100644
|
|
--- a/src/main/java/net/minecraft/world/item/Items.java
|
|
+++ b/src/main/java/net/minecraft/world/item/Items.java
|
|
@@ -363,7 +363,7 @@ public class Items {
|
|
public static final Item PURPUR_BLOCK = registerBlock(Blocks.PURPUR_BLOCK);
|
|
public static final Item PURPUR_PILLAR = registerBlock(Blocks.PURPUR_PILLAR);
|
|
public static final Item PURPUR_STAIRS = registerBlock(Blocks.PURPUR_STAIRS);
|
|
- public static final Item SPAWNER = registerBlock(Blocks.SPAWNER);
|
|
+ public static final Item SPAWNER = registerBlock(Blocks.SPAWNER, org.purpurmc.purpur.item.SpawnerItem::new, new Item.Properties().rarity(Rarity.EPIC)); // Purpur
|
|
public static final Item CREAKING_HEART = registerBlock(Blocks.CREAKING_HEART);
|
|
public static final Item CHEST = registerBlock(Blocks.CHEST, settings -> settings.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
|
public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE);
|
|
@@ -2104,7 +2104,7 @@ public class Items {
|
|
"sweet_berries", createBlockItemWithCustomItemName(Blocks.SWEET_BERRY_BUSH), new Item.Properties().food(Foods.SWEET_BERRIES)
|
|
);
|
|
public static final Item GLOW_BERRIES = registerItem(
|
|
- "glow_berries", createBlockItemWithCustomItemName(Blocks.CAVE_VINES), new Item.Properties().food(Foods.GLOW_BERRIES)
|
|
+ "glow_berries", settings -> new org.purpurmc.purpur.item.GlowBerryItem(Blocks.CAVE_VINES, settings.useItemDescriptionPrefix()), new Item.Properties().food(Foods.GLOW_BERRIES) // Purpur
|
|
);
|
|
public static final Item CAMPFIRE = registerBlock(Blocks.CAMPFIRE, settings -> settings.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
|
public static final Item SOUL_CAMPFIRE = registerBlock(
|
|
diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java
|
|
index 571f2540a1e9422025efe651167e26b44b437daa..c2f3c8b3d8eeb609b6d6067c4fb404aefbf94ec5 100644
|
|
--- a/src/main/java/net/minecraft/world/item/MapItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/MapItem.java
|
|
@@ -194,6 +194,7 @@ public class MapItem extends Item {
|
|
public static void renderBiomePreviewMap(ServerLevel world, ItemStack map) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(map, world);
|
|
if (mapItemSavedData != null) {
|
|
+ mapItemSavedData.isExplorerMap = true; // Purpur
|
|
if (world.dimension() == mapItemSavedData.dimension) {
|
|
int i = 1 << mapItemSavedData.scale;
|
|
int j = mapItemSavedData.centerX;
|
|
diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
index 7153d9ed12276a0f2d8b8a17c79734aa25ed1fa5..dc49ea6454e04ae8ec68af12c4bf2ff022540671 100644
|
|
--- a/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
@@ -35,8 +35,9 @@ public class MinecartItem extends Item {
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
|
|
if (!iblockdata.is(BlockTags.RAILS)) {
|
|
- return InteractionResult.FAIL;
|
|
- } else {
|
|
+ if (!world.purpurConfig.minecartPlaceAnywhere) return InteractionResult.FAIL; // Purpur
|
|
+ if (iblockdata.isSolid()) blockposition = blockposition.relative(context.getClickedFace());
|
|
+ } // else { // Purpur - place minecarts anywhere
|
|
ItemStack itemstack = context.getItemInHand();
|
|
RailShape blockpropertytrackposition = iblockdata.getBlock() instanceof BaseRailBlock ? (RailShape) iblockdata.getValue(((BaseRailBlock) iblockdata.getBlock()).getShapeProperty()) : RailShape.NORTH_SOUTH;
|
|
double d0 = 0.0D;
|
|
@@ -80,6 +81,6 @@ public class MinecartItem extends Item {
|
|
itemstack.shrink(1);
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
- }
|
|
+ // } // Purpur - place minecarts anywhere
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/NameTagItem.java b/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
index 000d1863bfba98b5132dfc6743362d687b2f54f3..20fece9908382f40b4082f7b1fb7d41914ae31be 100644
|
|
--- a/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
@@ -23,6 +23,7 @@ public class NameTagItem extends Item {
|
|
if (!event.callEvent()) return InteractionResult.PASS;
|
|
LivingEntity newEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle();
|
|
newEntity.setCustomName(event.getName() != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(event.getName()) : null);
|
|
+ if (user.level().purpurConfig.armorstandFixNametags && entity instanceof net.minecraft.world.entity.decoration.ArmorStand) entity.setCustomNameVisible(true); // Purpur
|
|
if (event.isPersistent() && newEntity instanceof Mob mob) {
|
|
// Paper end - Add PlayerNameEntityEvent
|
|
mob.setPersistenceRequired();
|
|
diff --git a/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
index 78ba170a83f8c026bd110eae494c52577182ed61..c2ae50872cead7202246b9cce4db6e0a81e1cf5f 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
@@ -105,6 +105,8 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
entityarrow.setCritArrow(true);
|
|
}
|
|
|
|
+ entityarrow.setActualEnchantments(weaponStack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting
|
|
+
|
|
return entityarrow;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/ShovelItem.java b/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
index 55c18f182166f4905d623d6f5e909eefd5ed2483..d10c4705cc9e7faabd4a5619e1da107231bdb37e 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
@@ -47,9 +47,12 @@ public class ShovelItem extends DiggerItem {
|
|
BlockState blockState2 = FLATTENABLES.get(blockState.getBlock());
|
|
BlockState blockState3 = null;
|
|
Runnable afterAction = null; // Paper
|
|
- if (blockState2 != null && level.getBlockState(blockPos.above()).isAir()) {
|
|
- afterAction = () -> level.playSound(player, blockPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper
|
|
- blockState3 = blockState2;
|
|
+ // Purpur start
|
|
+ var flattenable = level.purpurConfig.shovelFlattenables.get(blockState.getBlock());
|
|
+ if (flattenable != null && level.getBlockState(blockPos.above()).isAir()) {
|
|
+ afterAction = () -> {if (!FLATTENABLES.containsKey(blockState.getBlock())) level.playSound(null, blockPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F);}; // Paper
|
|
+ blockState3 = flattenable.into().defaultBlockState();
|
|
+ // Purpur end
|
|
} else if (blockState.getBlock() instanceof CampfireBlock && blockState.getValue(CampfireBlock.LIT)) {
|
|
afterAction = () -> { // Paper
|
|
if (!level.isClientSide()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/SnowballItem.java b/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
index 57872ebef6beb8cdc03c9f8f19de94652ee19062..60a064c26de0566aaf9f8be886402e291c03ca3b 100644
|
|
--- a/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
@@ -27,7 +27,7 @@ public class SnowballItem extends Item implements ProjectileItem {
|
|
// world.playSound((EntityHuman) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEffects.SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
|
|
if (world instanceof ServerLevel worldserver) {
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
- final Projectile.Delayed<Snowball> snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
|
|
+ final Projectile.Delayed<Snowball> snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, worldserver, itemstack, user, 0.0F, 1.5F, (float) worldserver.purpurConfig.snowballProjectileOffset); // Purpur
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity());
|
|
if (event.callEvent() && snowball.attemptSpawn()) {
|
|
user.awardStat(Stats.ITEM_USED.get(this));
|
|
diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
index 9956ed42df55daa6d97fd6e3ab5368dad91cfaf0..e0e746d6c78421b40777125ba49f0a04809f5415 100644
|
|
--- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
@@ -73,6 +73,24 @@ public class SpawnEggItem extends Item {
|
|
Spawner spawner = (Spawner) tileentity;
|
|
|
|
entitytypes = this.getType(itemstack);
|
|
+
|
|
+ // Purpur start
|
|
+ if (spawner instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity) {
|
|
+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.CreatureSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(entitytypes.getName()));
|
|
+ if (!event.callEvent()) {
|
|
+ return InteractionResult.FAIL;
|
|
+ }
|
|
+ entitytypes = EntityType.getFromBukkitType(event.getEntityType());
|
|
+ } else if (spawner instanceof net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity) {
|
|
+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.TrialSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(entitytypes.getName()));
|
|
+ if (!event.callEvent()) {
|
|
+ return InteractionResult.FAIL;
|
|
+ }
|
|
+ entitytypes = EntityType.getFromBukkitType(event.getEntityType());
|
|
+ }
|
|
+ // Purpur end
|
|
spawner.setEntityId(entitytypes, world.getRandom());
|
|
world.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3);
|
|
world.gameEvent((Entity) context.getPlayer(), (Holder) GameEvent.BLOCK_CHANGE, blockposition);
|
|
diff --git a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
index fa9d2ae44fcdd06f8f33cd14ffca422b20a01451..ffbc71ca2a27800d7758e3db339bf06a39ef1f11 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
@@ -21,7 +21,7 @@ public class ThrowablePotionItem extends PotionItem implements ProjectileItem {
|
|
ItemStack itemStack = user.getItemInHand(hand);
|
|
if (world instanceof ServerLevel serverLevel) {
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
- final Projectile.Delayed<ThrownPotion> thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, 0.5F, 1.0F);
|
|
+ final Projectile.Delayed<ThrownPotion> thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, 0.5F, (float) serverLevel.purpurConfig.throwablePotionProjectileOffset); // Purpur
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity());
|
|
if (event.callEvent() && thrownPotion.attemptSpawn()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
index 8b9a93ef71164cce8a616735b71d96d37e83b1a8..23e04d0d68ffa0e07ab295e7121f8a4963f4914a 100644
|
|
--- a/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
@@ -88,7 +88,7 @@ public class TridentItem extends Item implements ProjectileItem {
|
|
// itemstack.hurtWithoutBreaking(1, entityhuman); // CraftBukkit - moved down
|
|
if (f == 0.0F) {
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
- Projectile.Delayed<ThrownTrident> tridentDelayed = Projectile.spawnProjectileFromRotationDelayed(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, 1.0F);
|
|
+ Projectile.Delayed<ThrownTrident> tridentDelayed = Projectile.spawnProjectileFromRotationDelayed(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, (float) worldserver.purpurConfig.tridentProjectileOffset); // Purpur
|
|
// Paper start - PlayerLaunchProjectileEvent
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity());
|
|
if (!event.callEvent() || !tridentDelayed.attemptSpawn()) {
|
|
@@ -100,6 +100,9 @@ public class TridentItem extends Item implements ProjectileItem {
|
|
return false;
|
|
}
|
|
ThrownTrident entitythrowntrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent
|
|
+
|
|
+ entitythrowntrident.setActualEnchantments(stack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting
|
|
+
|
|
if (event.shouldConsume()) stack.hurtWithoutBreaking(1, entityhuman); // Paper - PlayerLaunchProjectileEvent
|
|
entitythrowntrident.pickupItemStack = stack.copy(); // SPIGOT-4511 update since damage call moved
|
|
// CraftBukkit end
|
|
@@ -132,6 +135,18 @@ public class TridentItem extends Item implements ProjectileItem {
|
|
f4 *= f / f6;
|
|
f5 *= f / f6;
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(entityhuman, stack, f3, f4, f5); // CraftBukkit
|
|
+
|
|
+ // Purpur start
|
|
+ List<EquipmentSlot> list = EquipmentSlot.VALUES.stream().filter((enumitemslot) -> LivingEntity.canGlideUsing(entityhuman.getItemBySlot(enumitemslot), enumitemslot)).toList();
|
|
+ if (!list.isEmpty()) {
|
|
+ EquipmentSlot enumitemslot = net.minecraft.Util.getRandom(list, entityhuman.random);
|
|
+ ItemStack glideItem = entityhuman.getItemBySlot(enumitemslot);
|
|
+ if (glideItem.has(net.minecraft.core.component.DataComponents.GLIDER) && world.purpurConfig.elytraDamagePerTridentBoost > 0) {
|
|
+ glideItem.hurtAndBreak(world.purpurConfig.elytraDamagePerTridentBoost, entityhuman, enumitemslot);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
entityhuman.push((double) f3, (double) f4, (double) f5);
|
|
entityhuman.startAutoSpinAttack(20, 8.0F, stack);
|
|
if (entityhuman.onGround()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java b/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java
|
|
index 0651c2af040e3f248860cfb3c5effce91589380e..d884df481b4bbb978113a4ac7a1feac31cf2f951 100644
|
|
--- a/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java
|
|
@@ -24,6 +24,12 @@ public record ClearAllStatusEffectsConsumeEffect() implements ConsumeEffect {
|
|
@Override
|
|
// CraftBukkit start
|
|
public boolean apply(Level world, ItemStack itemstack, LivingEntity entityliving, EntityPotionEffectEvent.Cause cause) {
|
|
+ // Purpur start
|
|
+ net.minecraft.world.effect.MobEffectInstance badOmen = entityliving.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
|
+ if (!world.purpurConfig.milkCuresBadOmen && itemstack.is(net.minecraft.world.item.Items.MILK_BUCKET) && badOmen != null) {
|
|
+ return entityliving.removeAllEffects(cause) && entityliving.addEffect(badOmen);
|
|
+ }
|
|
+ // Purpur end
|
|
return entityliving.removeAllEffects(cause);
|
|
// CraftBukkit end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
index 812f919a7a7e309c8513f44104f092496037608f..10730b307971915f52b3e41068a864b8ee1352b4 100644
|
|
--- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
@@ -45,6 +45,7 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
// CraftBukkit start
|
|
@Nullable
|
|
private List<ItemStack> itemStacks;
|
|
+ public Predicate<org.bukkit.inventory.ItemStack> predicate; // Purpur
|
|
|
|
public boolean isExact() {
|
|
return this.itemStacks != null;
|
|
@@ -100,6 +101,11 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
|
|
return false;
|
|
}
|
|
+ // Purpur start
|
|
+ if (predicate != null) {
|
|
+ return predicate.test(itemstack.asBukkitCopy());
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit end
|
|
List<Holder<Item>> list = this.items();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
index f99b87cf70df7eaac13d46f4e0234b1e6483d342..73241113e50dc8be89ef8850d49d95ec31fb194f 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
@@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
+import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.function.BiConsumer;
|
|
import java.util.function.Consumer;
|
|
@@ -578,4 +579,58 @@ public class EnchantmentHelper {
|
|
interface EnchantmentVisitor {
|
|
void accept(Holder<Enchantment> enchantment, int level);
|
|
}
|
|
+
|
|
+ // Purpur start - Enchantment convenience methods
|
|
+ public static Holder.Reference<Enchantment> getEnchantmentHolder(ResourceKey<Enchantment> enchantment) {
|
|
+ return net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(enchantment);
|
|
+ }
|
|
+
|
|
+ public static int getItemEnchantmentLevel(ResourceKey<Enchantment> enchantment, ItemStack stack) {
|
|
+ return getItemEnchantmentLevel(getEnchantmentHolder(enchantment), stack);
|
|
+ }
|
|
+ // Purpur end - Enchantment convenience methods
|
|
+
|
|
+ // Purpur start - Add option to mend the most damaged equipment first
|
|
+ public static Optional<EnchantedItemInUse> getMostDamagedItemWith(DataComponentType<?> componentType, LivingEntity entity) {
|
|
+ ItemStack maxStack = null;
|
|
+ EquipmentSlot maxSlot = null;
|
|
+ float maxPercent = 0.0F;
|
|
+
|
|
+ equipmentSlotLoop:
|
|
+ for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
|
|
+ ItemStack stack = entity.getItemBySlot(equipmentSlot);
|
|
+
|
|
+ // do not even check enchantments for item with lower or equal damage percent
|
|
+ float percent = stack.getDamagePercent();
|
|
+ if (percent <= maxPercent) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ItemEnchantments itemEnchantments = stack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
|
+
|
|
+ for (Entry<Holder<Enchantment>> entry : itemEnchantments.entrySet()) {
|
|
+ Enchantment enchantment = entry.getKey().value();
|
|
+
|
|
+ net.minecraft.core.component.DataComponentMap effects = enchantment.effects();
|
|
+ if (!effects.has(componentType)) {
|
|
+ // try with another enchantment
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (enchantment.matchingSlot(equipmentSlot)) {
|
|
+ maxStack = stack;
|
|
+ maxSlot = equipmentSlot;
|
|
+ maxPercent = percent;
|
|
+
|
|
+ // check another slot now
|
|
+ continue equipmentSlotLoop;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return maxStack != null
|
|
+ ? Optional.of(new EnchantedItemInUse(maxStack, maxSlot, entity))
|
|
+ : Optional.empty();
|
|
+ }
|
|
+ // Purpur end - Add option to mend the most damaged equipment first
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java b/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java
|
|
index cfc6a657cae92c68868a76c1b7b1febe2a16e9f4..a12c08da793139e39dc11c213c94796b83bd8240 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java
|
|
@@ -35,7 +35,7 @@ public class ItemEnchantments implements TooltipProvider {
|
|
private static final java.util.Comparator<Holder<Enchantment>> ENCHANTMENT_ORDER = java.util.Comparator.comparing(Holder::getRegisteredName);
|
|
public static final ItemEnchantments EMPTY = new ItemEnchantments(new Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), true);
|
|
// Paper end
|
|
- private static final Codec<Integer> LEVEL_CODEC = Codec.intRange(1, 255);
|
|
+ private static final Codec<Integer> LEVEL_CODEC = Codec.intRange(1, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)); // Purpur
|
|
private static final Codec<Object2IntAVLTreeMap<Holder<Enchantment>>> LEVELS_CODEC = Codec.unboundedMap(
|
|
Enchantment.CODEC, LEVEL_CODEC
|
|
)// Paper start - sort enchantments
|
|
@@ -69,7 +69,7 @@ public class ItemEnchantments implements TooltipProvider {
|
|
|
|
for (Entry<Holder<Enchantment>> entry : enchantments.object2IntEntrySet()) {
|
|
int i = entry.getIntValue();
|
|
- if (i < 0 || i > 255) {
|
|
+ if (i < 0 || i > (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)) { // Purpur
|
|
throw new IllegalArgumentException("Enchantment " + entry.getKey() + " has invalid level " + i);
|
|
}
|
|
}
|
|
@@ -164,13 +164,13 @@ public class ItemEnchantments implements TooltipProvider {
|
|
if (level <= 0) {
|
|
this.enchantments.removeInt(enchantment);
|
|
} else {
|
|
- this.enchantments.put(enchantment, Math.min(level, 255));
|
|
+ this.enchantments.put(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767))); // Purpur
|
|
}
|
|
}
|
|
|
|
public void upgrade(Holder<Enchantment> enchantment, int level) {
|
|
if (level > 0) {
|
|
- this.enchantments.merge(enchantment, Math.min(level, 255), Integer::max);
|
|
+ this.enchantments.merge(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)), Integer::max); // Purpur
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
index 0efc8d997b34302c3e0a5d7ec73a11a940dbeefe..af157881d440b34cfe79fbc9b03cc9ef28515eb8 100644
|
|
--- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
@@ -131,7 +131,12 @@ public class MerchantOffer {
|
|
}
|
|
|
|
public void updateDemand() {
|
|
- this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962
|
|
+ // Purpur start
|
|
+ this.updateDemand(0);
|
|
+ }
|
|
+ public void updateDemand(int minimumDemand) {
|
|
+ this.demand = Math.max(minimumDemand, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962
|
|
+ // Purpur end
|
|
}
|
|
|
|
public ItemStack assemble() {
|
|
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
index 7de66aa435dd36899b80f4ecc64480680e474d94..bb4411cfdf1bc7adc12c2f918d2eec830299f38b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
@@ -59,6 +59,7 @@ public abstract class BaseSpawner {
|
|
}
|
|
|
|
public boolean isNearPlayer(Level world, BlockPos pos) {
|
|
+ if (world.purpurConfig.spawnerDeactivateByRedstone && world.hasNeighborSignal(pos)) return false; // Purpur
|
|
return world.hasNearbyAlivePlayerThatAffectsSpawning((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange); // Paper - Affects Spawning API
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
index 5d7a6e4b73f032db356e7ec369b150013e940ee6..6b2cda6d578a0983b2401ea20629275431018433 100644
|
|
--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
@@ -184,7 +184,7 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst
|
|
|
|
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
|
|
for (Player player : this.players()) {
|
|
- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
|
|
+ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) { // Purpur
|
|
double d = player.distanceToSqr(x, y, z);
|
|
if (range < 0.0 || d < range * range) {
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index dd4d40a14cef268fe992f2c5ba523748fb619bd9..c3b9a9904f7b34df3dbd0a25e52946ab45f0f4d6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -178,6 +178,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
// Gale end - Gale configuration
|
|
|
|
public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
|
+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur
|
|
public static BlockPos lastPhysicsProblem; // Spigot
|
|
private org.spigotmc.TickLimiter entityLimiter;
|
|
private org.spigotmc.TickLimiter tileLimiter;
|
|
@@ -187,6 +188,49 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
|
|
public final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = new ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()); // Gale - Pufferfish - move random tick random
|
|
|
|
+ // Purpur start
|
|
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> playerBreedingCooldowns;
|
|
+
|
|
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> getNewBreedingCooldownCache() {
|
|
+ return com.google.common.cache.CacheBuilder.newBuilder().expireAfterWrite(this.purpurConfig.animalBreedingCooldownSeconds, java.util.concurrent.TimeUnit.SECONDS).build();
|
|
+ }
|
|
+
|
|
+ public void resetBreedingCooldowns() {
|
|
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache();
|
|
+ }
|
|
+
|
|
+ public boolean hasBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) { // Purpur
|
|
+ return this.playerBreedingCooldowns.getIfPresent(new BreedingCooldownPair(player, animalType)) != null;
|
|
+ }
|
|
+
|
|
+ public void addBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
|
+ this.playerBreedingCooldowns.put(new BreedingCooldownPair(player, animalType), new Object());
|
|
+ }
|
|
+
|
|
+ private static final class BreedingCooldownPair {
|
|
+ private final java.util.UUID playerUUID;
|
|
+ private final Class<? extends net.minecraft.world.entity.animal.Animal> animalType;
|
|
+
|
|
+ public BreedingCooldownPair(java.util.UUID playerUUID, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
|
+ this.playerUUID = playerUUID;
|
|
+ this.animalType = animalType;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(Object o) {
|
|
+ if (this == o) return true;
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
+ BreedingCooldownPair that = (BreedingCooldownPair) o;
|
|
+ return playerUUID.equals(that.playerUUID) && animalType.equals(that.animalType);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ return java.util.Objects.hash(playerUUID, animalType);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public CraftWorld getWorld() {
|
|
return this.world;
|
|
}
|
|
@@ -847,6 +891,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
|
this.galeConfig = galeWorldConfigCreator.apply(this.spigotConfig); // Gale - Gale configuration
|
|
+ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur
|
|
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur
|
|
this.generator = gen;
|
|
this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
|
|
|
|
@@ -2040,4 +2086,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
return this.id;
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean isNether() {
|
|
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER;
|
|
+ }
|
|
+
|
|
+ public boolean isTheEnd() {
|
|
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
index 2febef53fc1c2bbacab94feb5de4e6934e47038e..88b3715df673c6d12aea69fde075ad3caa8c51e8 100644
|
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -273,7 +273,7 @@ public final class NaturalSpawner {
|
|
blockposition_mutableblockposition.set(l, i, i1);
|
|
double d0 = (double) l + 0.5D;
|
|
double d1 = (double) i1 + 0.5D;
|
|
- Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, false);
|
|
+ Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur
|
|
|
|
if (entityhuman != null) {
|
|
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
|
|
diff --git a/src/main/java/net/minecraft/world/level/ServerExplosion.java b/src/main/java/net/minecraft/world/level/ServerExplosion.java
|
|
index 4de71492339c3d31a34f1fa2aa75e8b216485ef0..7b0a7bc89ae5a459a7db8d9ff728ddf5eb7e1024 100644
|
|
--- a/src/main/java/net/minecraft/world/level/ServerExplosion.java
|
|
+++ b/src/main/java/net/minecraft/world/level/ServerExplosion.java
|
|
@@ -309,7 +309,7 @@ public class ServerExplosion implements Explosion {
|
|
public ServerExplosion(ServerLevel world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 pos, float power, boolean createFire, Explosion.BlockInteraction destructionType) {
|
|
this.level = world;
|
|
this.source = entity;
|
|
- this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values
|
|
+ this.radius = (float) (world == null || world.purpurConfig.explosionClampRadius ? Math.max(power, 0.0) : power); // CraftBukkit - clamp bad values // Purpur
|
|
this.center = pos;
|
|
this.fire = createFire;
|
|
this.blockInteraction = destructionType;
|
|
@@ -664,10 +664,27 @@ public class ServerExplosion implements Explosion {
|
|
|
|
public void explode() {
|
|
// CraftBukkit start
|
|
- if (this.radius < 0.1F) {
|
|
+ if ((this.level == null || this.level.purpurConfig.explosionClampRadius) && this.radius < 0.1F) { // Purpur
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start - add PreExplodeEvents
|
|
+ if (this.source != null) {
|
|
+ Location location = new Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z);
|
|
+ if(!new org.purpurmc.purpur.event.entity.PreEntityExplodeEvent(this.source.getBukkitEntity(), location, this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, org.bukkit.craftbukkit.CraftExplosionResult.toBukkit(getBlockInteraction())).callEvent()) {
|
|
+ this.wasCanceled = true;
|
|
+ return;
|
|
+ }
|
|
+ } else {
|
|
+ Location location = new Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z);
|
|
+ org.bukkit.block.Block block = location.getBlock();
|
|
+ org.bukkit.block.BlockState blockState = (this.damageSource.getDirectBlockState() != null) ? this.damageSource.getDirectBlockState() : block.getState();
|
|
+ if(!new org.purpurmc.purpur.event.PreBlockExplodeEvent(location.getBlock(), this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, blockState, org.bukkit.craftbukkit.CraftExplosionResult.toBukkit(getBlockInteraction())).callEvent()) {
|
|
+ this.wasCanceled = true;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
// Paper start - collision optimisations
|
|
this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
index 50c907c962f936d2035bb7550750cdbd220b29c2..f9a2d2d4f798efa0d691996ec5ff7fe00260b36c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
@@ -59,6 +59,53 @@ public class AnvilBlock extends FallingBlock {
|
|
return this.defaultBlockState().setValue(FACING, ctx.getHorizontalDirection().getClockWise());
|
|
}
|
|
|
|
+ // Purpur start - repairable/damageable anvils
|
|
+ @Override
|
|
+ protected net.minecraft.world.InteractionResult useItemOn(final net.minecraft.world.item.ItemStack stack, final BlockState state, final Level world, final BlockPos pos, final Player player, final net.minecraft.world.InteractionHand hand, final BlockHitResult hit) {
|
|
+ if (world.purpurConfig.anvilRepairIngotsAmount > 0 && stack.is(net.minecraft.world.item.Items.IRON_INGOT)) {
|
|
+ if (stack.getCount() < world.purpurConfig.anvilRepairIngotsAmount) {
|
|
+ // not enough iron ingots, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return net.minecraft.world.InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.ANVIL)) {
|
|
+ // anvil is already fully repaired, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return net.minecraft.world.InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(world.purpurConfig.anvilRepairIngotsAmount);
|
|
+ }
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_PLACE, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return net.minecraft.world.InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (world.purpurConfig.anvilDamageObsidianAmount > 0 && stack.is(net.minecraft.world.item.Items.OBSIDIAN)) {
|
|
+ if (stack.getCount() < world.purpurConfig.anvilDamageObsidianAmount) {
|
|
+ // not enough obsidian, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return net.minecraft.world.InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
|
+ world.destroyBlock(pos, false);
|
|
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.DAMAGED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ }
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(world.purpurConfig.anvilDamageObsidianAmount);
|
|
+ }
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_LAND, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return net.minecraft.world.InteractionResult.CONSUME;
|
|
+ }
|
|
+ return net.minecraft.world.InteractionResult.TRY_WITH_EMPTY_HAND;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
|
|
if (!world.isClientSide) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
index affbbf6abc6bc09ecb652c1dee92aa297458bc39..febc05dc39741807127cba4a2a55aaad62b0800c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
@@ -50,6 +50,20 @@ public class AzaleaBlock extends BushBlock implements BonemealableBlock {
|
|
|
|
@Override
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ // Purpur start
|
|
+ growTree(world, random, pos, state);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void randomTick(net.minecraft.world.level.block.state.BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
|
+ double chance = state.getBlock() == Blocks.FLOWERING_AZALEA ? world.purpurConfig.floweringAzaleaGrowthChance : world.purpurConfig.azaleaGrowthChance;
|
|
+ if (chance > 0.0D && world.getMaxLocalRawBrightness(pos.above()) > 9 && random.nextDouble() < chance) {
|
|
+ growTree(world, random, pos, state);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void growTree(ServerLevel world, RandomSource random, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) {
|
|
+ // Purpur end
|
|
TreeGrower.AZALEA.growTree(world, world.getChunkSource().getGenerator(), pos, state, random);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
index d7ca7a43d2d5f8cad416156fd40588cdd6634f52..231338adda19f57bf1a95379cc2e14341d4068d0 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
@@ -39,6 +39,7 @@ public abstract class BaseCoralPlantTypeBlock extends Block implements SimpleWat
|
|
}
|
|
|
|
protected static boolean scanForWater(BlockState state, BlockGetter world, BlockPos pos) {
|
|
+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur
|
|
if (state.getValue(WATERLOGGED)) {
|
|
return true;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
index c02c4834ace843633b77fb43eeadd3ddc7b1f743..c130d316e87f1f896d33ab43831063a89e3bef2b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
@@ -106,7 +106,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
Vec3 vec3d = pos.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK);
|
|
+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur
|
|
return InteractionResult.SUCCESS_SERVER;
|
|
} else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) {
|
|
if (!BedBlock.canSetSpawn(world)) return this.explodeBed(state, world, pos); // Paper - check explode first
|
|
@@ -159,7 +159,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
Vec3 vec3d = blockposition.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
|
|
+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // CraftBukkit - add state // Purpur
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
}
|
|
@@ -183,7 +183,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
@Override
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- super.fallOn(world, state, pos, entity, fallDistance * 0.5F);
|
|
+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
index 9e3f1441d62128535112621bf259c24f1a90595b..2535e6d71b690f8dfde41a7d9cb76b6f010f5aa7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
@@ -246,7 +246,7 @@ public class BigDripleafBlock extends HorizontalDirectionalBlock implements Bone
|
|
BigDripleafBlock.playTiltSound(world, blockposition, soundeffect);
|
|
}
|
|
|
|
- int i = BigDripleafBlock.DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt);
|
|
+ int i = world.purpurConfig.bigDripleafTiltDelay.getOrDefault(tilt, -1); // Purpur
|
|
|
|
if (i != -1) {
|
|
world.scheduleTick(blockposition, (Block) this, i);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
|
|
index ef8d8990a8a98c0cbc3e7aa45d22c6008f0ff2bb..070a3dbd9b92053bfed11e1f8be6f9153dc47c29 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/Block.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
|
|
@@ -88,6 +88,10 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
public static final int UPDATE_LIMIT = 512;
|
|
protected final StateDefinition<Block, BlockState> stateDefinition;
|
|
private BlockState defaultBlockState;
|
|
+ // Purpur start
|
|
+ public float fallDamageMultiplier = 1.0F;
|
|
+ public float fallDistanceMultiplier = 1.0F;
|
|
+ // Purpur end
|
|
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
|
|
public final boolean isDestroyable() {
|
|
return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
|
|
@@ -303,7 +307,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
public static void dropResources(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockEntity blockEntity) {
|
|
if (world instanceof ServerLevel) {
|
|
Block.getDrops(state, (ServerLevel) world, pos, blockEntity).forEach((itemstack) -> {
|
|
- Block.popResource((ServerLevel) world, pos, itemstack);
|
|
+ Block.popResource((ServerLevel) world, pos, applyLoreFromTile(itemstack, blockEntity)); // Purpur
|
|
});
|
|
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
|
|
}
|
|
@@ -322,7 +326,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
event.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping
|
|
event.callEvent();
|
|
for (org.bukkit.inventory.ItemStack drop : event.getDrops()) {
|
|
- popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop));
|
|
+ popResource(serverLevel, pos, applyLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur
|
|
}
|
|
state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
|
|
block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping
|
|
@@ -339,13 +343,32 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
// Paper end - Properly handle xp dropping
|
|
if (world instanceof ServerLevel) {
|
|
Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> {
|
|
- Block.popResource(world, pos, itemstack1);
|
|
+ Block.popResource(world, pos, applyLoreFromTile(itemstack1, blockEntity)); // Purpur
|
|
});
|
|
state.spawnAfterBreak((ServerLevel) world, pos, tool, dropExperience); // Paper - Properly handle xp dropping
|
|
}
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private static ItemStack applyLoreFromTile(ItemStack stack, @Nullable BlockEntity blockEntity) {
|
|
+ if (stack.getItem() instanceof BlockItem) {
|
|
+ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel) {
|
|
+ net.minecraft.world.item.component.ItemLore lore = blockEntity.getPersistentLore();
|
|
+ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder();
|
|
+ if (blockEntity.getLevel().purpurConfig.persistentTileEntityLore && lore != null) {
|
|
+ builder.set(net.minecraft.core.component.DataComponents.LORE, lore);
|
|
+ }
|
|
+ if (!blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayName) {
|
|
+ builder.remove(net.minecraft.core.component.DataComponents.CUSTOM_NAME);
|
|
+ }
|
|
+ stack.applyComponents(builder.build());
|
|
+ }
|
|
+ }
|
|
+ return stack;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static void popResource(Level world, BlockPos pos, ItemStack stack) {
|
|
double d0 = (double) EntityType.ITEM.getHeight() / 2.0D;
|
|
double d1 = (double) pos.getX() + 0.5D + Mth.nextDouble(world.random, -0.25D, 0.25D);
|
|
@@ -433,7 +456,17 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
} // Paper - fix drops not preventing stats/food exhaustion
|
|
}
|
|
|
|
- public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {}
|
|
+ // Purpur start
|
|
+ @Nullable protected LivingEntity placer = null;
|
|
+
|
|
+ public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {
|
|
+ this.placer = placer;
|
|
+ }
|
|
+
|
|
+ public void forgetPlacer() {
|
|
+ this.placer = null;
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
public boolean isPossibleToRespawnInThis(BlockState state) {
|
|
return !state.isSolid() && !state.liquid();
|
|
@@ -444,7 +477,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
}
|
|
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- entity.causeFallDamage(fallDistance, 1.0F, entity.damageSources().fall());
|
|
+ entity.causeFallDamage(fallDistance * fallDistanceMultiplier, fallDamageMultiplier, entity.damageSources().fall()); // Purpur
|
|
}
|
|
|
|
public void updateEntityMovementAfterFallOn(BlockGetter world, Entity entity) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/Blocks.java b/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
index 66a07f7cbf1c1d6ecbe055cbf4f63eb07d93e90c..63d67d46d30ed8ed57cdc0e59b6cb6b75ab22c1f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
@@ -6465,6 +6465,7 @@ public class Blocks {
|
|
BlockBehaviour.Properties.of()
|
|
.mapColor(MapColor.PLANT)
|
|
.forceSolidOff()
|
|
+ .randomTicks() // Purpur
|
|
.instabreak()
|
|
.sound(SoundType.AZALEA)
|
|
.noOcclusion()
|
|
@@ -6476,6 +6477,7 @@ public class Blocks {
|
|
BlockBehaviour.Properties.of()
|
|
.mapColor(MapColor.PLANT)
|
|
.forceSolidOff()
|
|
+ .randomTicks() // Purpur
|
|
.instabreak()
|
|
.sound(SoundType.FLOWERING_AZALEA)
|
|
.noOcclusion()
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java
|
|
index 385da0585f409ee453f10d45f5837cdc09adc21b..c65016cba376a41c267fb4b6499ec0a263851558 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java
|
|
@@ -123,10 +123,10 @@ public class BubbleColumnBlock extends Block implements BucketPickup {
|
|
if (state.is(Blocks.BUBBLE_COLUMN)) {
|
|
return state;
|
|
} else if (state.is(Blocks.SOUL_SAND)) {
|
|
- return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(false));
|
|
+ return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(org.purpurmc.purpur.PurpurConfig.soulSandBlockReverseBubbleColumnFlow)); // Purpur
|
|
} else {
|
|
return state.is(Blocks.MAGMA_BLOCK)
|
|
- ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(true))
|
|
+ ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(!org.purpurmc.purpur.PurpurConfig.magmaBlockReverseBubbleColumnFlow)) // Purpur
|
|
: Blocks.WATER.defaultBlockState();
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BushBlock.java b/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
index eb324fda54ada3ed7941713a784ed2d686ec8c4b..09cc76f3fee4a767c9ec3fa592f2c3c6146344ec 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
@@ -55,4 +55,24 @@ public abstract class BushBlock extends Block {
|
|
protected boolean isPathfindable(BlockState state, PathComputationType type) {
|
|
return type == PathComputationType.AIR && !this.hasCollision ? true : super.isPathfindable(state, type);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public void playerDestroyAndReplant(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, net.minecraft.world.item.ItemStack itemInHand, net.minecraft.world.level.ItemLike itemToReplant) {
|
|
+ player.awardStat(net.minecraft.stats.Stats.BLOCK_MINED.get(this));
|
|
+ player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED);
|
|
+ java.util.List<net.minecraft.world.item.ItemStack> dropList = Block.getDrops(state, (net.minecraft.server.level.ServerLevel) world, pos, blockEntity, player, itemInHand);
|
|
+
|
|
+ boolean planted = false;
|
|
+ for (net.minecraft.world.item.ItemStack itemToDrop : dropList) {
|
|
+ if (!planted && itemToDrop.getItem() == itemToReplant) {
|
|
+ world.setBlock(pos, defaultBlockState(), 3);
|
|
+ itemToDrop.setCount(itemToDrop.getCount() - 1);
|
|
+ planted = true;
|
|
+ }
|
|
+ Block.popResource(world, pos, itemToDrop);
|
|
+ }
|
|
+
|
|
+ state.spawnAfterBreak((net.minecraft.server.level.ServerLevel) world, pos, itemInHand, true);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CactusBlock.java b/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
index c045b1cccf0047dbef8c04d5a28d31d53389054f..9f163ed07f8e6a5370c4c355b4e910f7a49b6bcd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
@@ -24,7 +24,7 @@ import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
|
|
|
-public class CactusBlock extends Block {
|
|
+public class CactusBlock extends Block implements BonemealableBlock { // Purpur
|
|
|
|
public static final MapCodec<CactusBlock> CODEC = simpleCodec(CactusBlock::new);
|
|
public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
|
|
@@ -115,7 +115,7 @@ public class CactusBlock extends Block {
|
|
|
|
enumdirection = (Direction) iterator.next();
|
|
iblockdata1 = world.getBlockState(pos.relative(enumdirection));
|
|
- } while (!iblockdata1.isSolid() && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA));
|
|
+ } while ((!world.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors || !iblockdata1.isSolid()) && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA)); // Purpur
|
|
|
|
return false;
|
|
}
|
|
@@ -135,4 +135,34 @@ public class CactusBlock extends Block {
|
|
protected boolean isPathfindable(BlockState state, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(final LevelReader world, final BlockPos pos, final BlockState state) {
|
|
+ if (!((Level) world).purpurConfig.cactusAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false;
|
|
+
|
|
+ int cactusHeight = 0;
|
|
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
|
+ cactusHeight++;
|
|
+ }
|
|
+
|
|
+ return cactusHeight < ((Level) world).paperConfig().maxGrowthHeight.cactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int cactusHeight = 0;
|
|
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
|
+ cactusHeight++;
|
|
+ }
|
|
+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.cactus - cactusHeight; i++) {
|
|
+ world.setBlockAndUpdate(pos.above(i), state.setValue(CactusBlock.AGE, 0));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CakeBlock.java b/src/main/java/net/minecraft/world/level/block/CakeBlock.java
|
|
index 648c2510beb162e73aed236a3169d0bbb8fc5050..3563a241c0b697dc0167cf7b1aa73fef7d1e7934 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CakeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CakeBlock.java
|
|
@@ -119,6 +119,7 @@ public class CakeBlock extends Block {
|
|
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel);
|
|
|
|
if (!event.isCancelled()) {
|
|
+ if (player.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) player.burpDelay = player.level().purpurConfig.playerBurpDelay; // Purpur
|
|
player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
index 18d4020017d76303d3179fad8974574777ea6305..2ee2b1485f848ac5270bc3f7e1e5b1bc3029b0bb 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
@@ -140,7 +140,7 @@ public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedB
|
|
BlockPos blockposition = ctx.getClickedPos();
|
|
boolean flag = world.getFluidState(blockposition).getType() == Fluids.WATER;
|
|
|
|
- return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, !flag)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection());
|
|
+ return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, world.purpurConfig.campFireLitWhenPlaced ? !flag : world.purpurConfig.campFireLitWhenPlaced)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
index 22242d11e009acab4c9738a1c6ada8b9ba678a0c..828fa78a89f13ef81c53b4d6ea6ef86c7b53024e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
@@ -72,7 +72,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock {
|
|
SnowGolem entitysnowman = (SnowGolem) EntityType.SNOW_GOLEM.create(world, EntitySpawnReason.TRIGGERED);
|
|
|
|
if (entitysnowman != null) {
|
|
- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos());
|
|
+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos(), this.placer); // Purpur
|
|
}
|
|
} else {
|
|
BlockPattern.BlockPatternMatch shapedetector_shapedetectorcollection1 = this.getOrCreateIronGolemFull().find(world, pos);
|
|
@@ -82,7 +82,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock {
|
|
|
|
if (entityirongolem != null) {
|
|
entityirongolem.setPlayerCreated(true);
|
|
- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos());
|
|
+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos(), this.placer); // Purpur
|
|
}
|
|
}
|
|
}
|
|
@@ -90,6 +90,16 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock {
|
|
}
|
|
|
|
private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos) {
|
|
+ // Purpur start
|
|
+ spawnGolemInWorld(world, patternResult, entity, pos, null);
|
|
+ }
|
|
+ private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos, net.minecraft.world.entity.LivingEntity placer) {
|
|
+ if (entity instanceof SnowGolem snowGolem) {
|
|
+ snowGolem.setSummoner(placer == null ? null : placer.getUUID());
|
|
+ } else if (entity instanceof IronGolem ironGolem) {
|
|
+ ironGolem.setSummoner(placer == null ? null : placer.getUUID());
|
|
+ }
|
|
+ // Purpur end
|
|
// clearPatternBlocks(world, shapedetector_shapedetectorcollection); // CraftBukkit - moved down
|
|
entity.moveTo((double) pos.getX() + 0.5D, (double) pos.getY() + 0.05D, (double) pos.getZ() + 0.5D, 0.0F, 0.0F);
|
|
// CraftBukkit start
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
index c9968934f4ecaa8d81e545f279b3001c7b1ce545..03e4fce6f8226451365fc2831b5bf1e5e6091730 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
@@ -37,7 +37,7 @@ public class CauldronBlock extends AbstractCauldronBlock {
|
|
}
|
|
|
|
protected static boolean shouldHandlePrecipitation(Level world, Biome.Precipitation precipitation) {
|
|
- return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < 0.05F : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < 0.1F : false);
|
|
+ return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < world.purpurConfig.cauldronRainChance : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < world.purpurConfig.cauldronPowderSnowChance : false); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
index c4473c2a778116d48bc3e4796afd901f455070e6..e62217c0bfa6cc426c7d41154f3ccc34d8f242ca 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
@@ -94,4 +94,11 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements CaveVines {
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
world.setBlock(pos, state.setValue(BERRIES, Boolean.valueOf(true)), 2);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.caveVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java b/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java
|
|
index daae7fd6e0148cfba8e359d990748a0c83a3376e..0e06b1bcd906e92c083dc74d56d6d0a2a36f62a7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java
|
|
@@ -67,7 +67,7 @@ public interface ChangeOverTimeBlock<T extends Enum<T>> {
|
|
}
|
|
|
|
float f = (float) (k + 1) / (float) (k + j + 1);
|
|
- float f1 = f * f * this.getChanceModifier();
|
|
+ float f1 = world.purpurConfig.disableOxidationProximityPenalty ? this.getChanceModifier() : f * f * this.getChanceModifier(); // Purpur
|
|
|
|
return random.nextFloat() < f1 ? this.getNext(state) : Optional.empty();
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ChestBlock.java b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
index edef8fc62f8dba1b57214d8d7d805ff0d83f4114..663eb96b8227f000448957b5d8ea13ca78cee329 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
@@ -341,6 +341,7 @@ public class ChestBlock extends AbstractChestBlock<ChestBlockEntity> implements
|
|
}
|
|
|
|
public static boolean isBlockedChestByBlock(BlockGetter world, BlockPos pos) {
|
|
+ if (world instanceof Level && ((Level) world).purpurConfig.chestOpenWithBlockOnTop) return false; // Purpur
|
|
BlockPos blockposition1 = pos.above();
|
|
|
|
return world.getBlockState(blockposition1).isRedstoneConductor(world, blockposition1);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
index ceab17cdf519278393e41311488e6d7f99133ebe..c9c575c48965a31830cf97578911a69e889ed349 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
@@ -241,18 +241,27 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
|
|
int i = (Integer) state.getValue(ComposterBlock.LEVEL);
|
|
|
|
if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) {
|
|
- if (i < 7 && !world.isClientSide) {
|
|
- BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack);
|
|
- // Paper start - handle cancelled events
|
|
- if (iblockdata1 == null) {
|
|
- return InteractionResult.PASS;
|
|
- }
|
|
- // Paper end
|
|
-
|
|
- world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
|
|
- player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
|
|
- stack.consume(1, player);
|
|
+ // Purpur start - sneak to bulk process composter
|
|
+ BlockState newState = process(i, player, state, world, pos, stack);
|
|
+ if (newState == null) {
|
|
+ return InteractionResult.PASS;
|
|
}
|
|
+ if (world.purpurConfig.composterBulkProcess && player.isShiftKeyDown() && newState != state) {
|
|
+ BlockState oldState;
|
|
+ int oldCount, newCount, oldLevel, newLevel;
|
|
+ do {
|
|
+ oldState = newState;
|
|
+ oldCount = stack.getCount();
|
|
+ oldLevel = oldState.getValue(ComposterBlock.LEVEL);
|
|
+ newState = process(oldLevel, player, oldState, world, pos, stack);
|
|
+ if (newState == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ newCount = stack.getCount();
|
|
+ newLevel = newState.getValue(ComposterBlock.LEVEL);
|
|
+ } while (newCount > 0 && (newCount != oldCount || newLevel != oldLevel || newState != oldState));
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
return InteractionResult.SUCCESS;
|
|
} else {
|
|
@@ -260,6 +269,25 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start - sneak to bulk process composter
|
|
+ private static @Nullable BlockState process(int level, Player player, BlockState state, Level world, BlockPos pos, ItemStack stack) {
|
|
+ if (level < 7 && !world.isClientSide) {
|
|
+ BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack);
|
|
+ // Paper start - handle cancelled events
|
|
+ if (iblockdata1 == null) {
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
|
|
+ player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
|
|
+ stack.consume(1, player);
|
|
+ return iblockdata1;
|
|
+ }
|
|
+ return state;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
|
|
int i = (Integer) state.getValue(ComposterBlock.LEVEL);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CoralBlock.java b/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
index a59b23f4062fa896836dec72cbd5097411774ad1..c526ea13a726624adaa654f09ff84c899c13ab98 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
@@ -60,6 +60,7 @@ public class CoralBlock extends Block {
|
|
}
|
|
|
|
protected boolean scanForWater(BlockGetter world, BlockPos pos) {
|
|
+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur
|
|
Direction[] aenumdirection = Direction.values();
|
|
int i = aenumdirection.length;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
index 1ada5ed825501666addacf527a513ab7bd4a3a58..33c27909290ff3ab483226cf65b1a1bc2e983cbc 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
@@ -180,7 +180,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
|
|
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
|
|
if (world instanceof ServerLevel worldserver) {
|
|
- if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit
|
|
+ if (entity instanceof Ravager && world.purpurConfig.ravagerGriefableBlocks.contains(world.getBlockState(pos).getBlock()) && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.purpurConfig.ravagerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur
|
|
worldserver.destroyBlock(pos, true, entity);
|
|
}
|
|
}
|
|
@@ -216,4 +216,15 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(CropBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops, boolean dropExp) {
|
|
+ if (world.purpurConfig.hoeReplantsCrops && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) {
|
|
+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, getBaseSeedId());
|
|
+ } else {
|
|
+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/DoorBlock.java b/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
index 077b99caf0ec0ee098786d23194d88e1dc4481ce..daf865c20cc193a12db0d98e3c0472eefdf635c2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
@@ -200,6 +200,7 @@ public class DoorBlock extends Block {
|
|
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
|
|
if (!this.type.canOpenByHand()) {
|
|
return InteractionResult.PASS;
|
|
+ } else if (requiresRedstone(world, state, pos)) { return InteractionResult.CONSUME; // Purpur
|
|
} else {
|
|
state = (BlockState) state.cycle(DoorBlock.OPEN);
|
|
world.setBlock(pos, state, 10);
|
|
@@ -301,4 +302,18 @@ public class DoorBlock extends Block {
|
|
flag = false;
|
|
return flag;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static boolean requiresRedstone(Level level, BlockState state, BlockPos pos) {
|
|
+ if (level.purpurConfig.doorRequiresRedstone.contains(state.getBlock())) {
|
|
+ // force update client
|
|
+ BlockPos otherPos = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN);
|
|
+ BlockState otherState = level.getBlockState(otherPos);
|
|
+ level.sendBlockUpdated(pos, state, state, 3);
|
|
+ level.sendBlockUpdated(otherPos, otherState, otherState, 3);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
index 30d15686b1a81de7ac28feb0c6188eb007c6f2fd..b6799db00e157892dd4339a01d2ca36092c8e491 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
@@ -48,8 +48,8 @@ public class DragonEggBlock extends FallingBlock {
|
|
}
|
|
|
|
private void teleport(BlockState state, Level world, BlockPos pos) {
|
|
+ if (!world.purpurConfig.dragonEggTeleport) return; // Purpur
|
|
WorldBorder worldborder = world.getWorldBorder();
|
|
-
|
|
for (int i = 0; i < 1000; ++i) {
|
|
BlockPos blockposition1 = pos.offset(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16));
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java b/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java
|
|
index a7fb500d950687743d1fc0b3ad3e6d10dcc6e31a..ce6a9e15ae0114623e79b5d8c244c2c490a3f74e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java
|
|
@@ -123,4 +123,18 @@ public class EnchantingTableBlock extends BaseEntityBlock {
|
|
protected boolean isPathfindable(BlockState state, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean moved) {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+
|
|
+ if (level.purpurConfig.enchantmentTableLapisPersists && blockEntity instanceof EnchantingTableBlockEntity enchantmentTable) {
|
|
+ net.minecraft.world.Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.LAPIS_LAZULI, enchantmentTable.getLapis()));
|
|
+ level.updateNeighbourForOutputSignal(pos, this);
|
|
+ }
|
|
+
|
|
+ super.onRemove(state, level, pos, newState, moved);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java
|
|
index a7a21f071161fb7e73a046717d2462f871ab653c..abb75f9389167a1f51a2c50831664d50181749de 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java
|
|
@@ -104,6 +104,13 @@ public class EndGatewayBlock extends BaseEntityBlock implements Portal {
|
|
TheEndGatewayBlockEntity tileentityendgateway = (TheEndGatewayBlockEntity) tileentity;
|
|
|
|
if (!tileentityendgateway.isCoolingDown()) {
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.imposeTeleportRestrictionsOnGateways && (entity.isVehicle() || entity.isPassenger())) {
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
entity.setAsInsidePortal(this, pos);
|
|
TheEndGatewayBlockEntity.triggerCooldown(world, pos, state, tileentityendgateway);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
index 4aa14f975e1ceedf3d4a427e0daefb58b12fcafe..2dffc3990d9ae3d595d923239885e3a7d8ec04f3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
@@ -70,6 +70,13 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal {
|
|
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
|
|
if (entity.canUsePortal(false)) {
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.imposeTeleportRestrictionsOnEndPortals && (entity.isVehicle() || entity.isPassenger())) {
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - Entity in portal
|
|
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.ENDER); // Paper - add portal type
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
index 2a207fb2e1c26b562de42240e11c856bd2a23458..6ad4aa371607ab92616626285a7e71757c76a3db 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
@@ -89,7 +89,7 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
|
|
// Paper start - Fix InventoryOpenEvent cancellation - moved up;
|
|
playerEnderChestContainer.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations
|
|
if (world instanceof ServerLevel serverLevel && player.openMenu(
|
|
- new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE)
|
|
+ new SimpleMenuProvider((i, inventory, playerx) -> org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? getEnderChestSixRows(i, inventory, player, playerEnderChestContainer) : ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE) // Purpur
|
|
).isPresent()) {
|
|
// Paper end - Fix InventoryOpenEvent cancellation - moved up;
|
|
// Paper - Fix InventoryOpenEvent cancellation - moved up;
|
|
@@ -104,6 +104,35 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity();
|
|
+ if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) {
|
|
+ player.sixRowEnderchestSlotCount = 54;
|
|
+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.five")) {
|
|
+ player.sixRowEnderchestSlotCount = 45;
|
|
+ return ChestMenu.fiveRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.four")) {
|
|
+ player.sixRowEnderchestSlotCount = 36;
|
|
+ return ChestMenu.fourRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.three")) {
|
|
+ player.sixRowEnderchestSlotCount = 27;
|
|
+ return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.two")) {
|
|
+ player.sixRowEnderchestSlotCount = 18;
|
|
+ return ChestMenu.twoRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.one")) {
|
|
+ player.sixRowEnderchestSlotCount = 9;
|
|
+ return ChestMenu.oneRow(syncId, inventory, playerEnderChestContainer);
|
|
+ }
|
|
+ }
|
|
+ player.sixRowEnderchestSlotCount = -1;
|
|
+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
|
return new EnderChestBlockEntity(pos, state);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
index c3dba0c2c94f3804338f86621dc42405e380a6b3..eaac00e2534aca4eab92c7b9f9248e04b35b47df 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
@@ -112,7 +112,7 @@ public class FarmBlock extends Block {
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
super.fallOn(world, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage.
|
|
if (world instanceof ServerLevel worldserver) {
|
|
- if (world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) {
|
|
+ if ((worldserver.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= worldserver.purpurConfig.farmlandTrampleHeight : world.random.nextFloat() < fallDistance - 0.5F) && entity instanceof LivingEntity && (entity instanceof Player || worldserver.purpurConfig.farmlandBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur
|
|
// CraftBukkit start - Interact soil
|
|
org.bukkit.event.Cancellable cancellable;
|
|
if (entity instanceof Player) {
|
|
@@ -126,6 +126,22 @@ public class FarmBlock extends Block {
|
|
return;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.farmlandTramplingDisabled) return;
|
|
+ if (world.purpurConfig.farmlandTramplingOnlyPlayers && !(entity instanceof Player)) return;
|
|
+ if (world.purpurConfig.farmlandAlpha) {
|
|
+ Block block = world.getBlockState(pos.below()).getBlock();
|
|
+ if (block instanceof FenceBlock || block instanceof WallBlock) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if (world.purpurConfig.farmlandTramplingFeatherFalling) {
|
|
+ Iterator<net.minecraft.world.item.ItemStack> armor = ((LivingEntity) entity).getArmorSlots().iterator();
|
|
+ if (armor.hasNext() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, armor.next()) >= (int) entity.fallDistance) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) {
|
|
return;
|
|
}
|
|
@@ -174,7 +190,7 @@ public class FarmBlock extends Block {
|
|
}
|
|
}
|
|
|
|
- return false;
|
|
+ return ((ServerLevel) world).purpurConfig.farmlandGetsMoistFromBelow && world.getFluidState(pos.relative(Direction.DOWN)).is(FluidTags.WATER); // Purpur;
|
|
// Paper end - Perf: remove abstract block iteration
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
index 9b424d7661fedf8ee1eb9f3167c62e563f04d4d1..2af311847a085a8073e9bcb26c762d1bbe1eae2c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
@@ -34,12 +34,12 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(RandomSource random) {
|
|
- return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, random.nextInt(25));
|
|
+ return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, random.nextInt(getMaxGrowthAge())); // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected boolean isRandomlyTicking(BlockState state) {
|
|
- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25;
|
|
+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -55,7 +55,7 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
} else {
|
|
modifier = world.spigotConfig.caveVinesModifier;
|
|
}
|
|
- if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution
|
|
+ if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge() && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution // Purpur
|
|
// Spigot end
|
|
BlockPos blockposition1 = pos.relative(this.growthDirection);
|
|
|
|
@@ -77,11 +77,11 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
}
|
|
|
|
public BlockState getMaxAgeState(BlockState state) {
|
|
- return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, 25);
|
|
+ return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, getMaxGrowthAge()); // Purpur
|
|
}
|
|
|
|
public boolean isMaxAge(BlockState state) {
|
|
- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) == 25;
|
|
+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) >= getMaxGrowthAge(); // Purpur
|
|
}
|
|
|
|
protected BlockState updateBodyAfterConvertedFromHead(BlockState from, BlockState to) {
|
|
@@ -123,13 +123,13 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
@Override
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
BlockPos blockposition1 = pos.relative(this.growthDirection);
|
|
- int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, 25);
|
|
+ int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, getMaxGrowthAge()); // Purpur
|
|
int j = this.getBlocksToGrowWhenBonemealed(random);
|
|
|
|
for (int k = 0; k < j && this.canGrowInto(world.getBlockState(blockposition1)); ++k) {
|
|
world.setBlockAndUpdate(blockposition1, (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, i));
|
|
blockposition1 = blockposition1.relative(this.growthDirection);
|
|
- i = Math.min(i + 1, 25);
|
|
+ i = Math.min(i + 1, getMaxGrowthAge()); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -142,4 +142,6 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
protected GrowingPlantHeadBlock getHeadBlock() {
|
|
return this;
|
|
}
|
|
+
|
|
+ public abstract int getMaxGrowthAge(); // Purpur
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/HayBlock.java b/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
index ef364aa171a48482a45bc18cfe730ec20c3f7be6..74971d90506aa253d5ee821b5390fb2551a3a393 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
@@ -23,6 +23,6 @@ public class HayBlock extends RotatedPillarBlock {
|
|
|
|
@Override
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- entity.causeFallDamage(fallDistance, 0.2F, world.damageSources().fall());
|
|
+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
index a94762e65853ccad38cf90b0049ca256106c0c9f..38633e168a9b36e37feea00964d53e657926639e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
@@ -42,7 +42,7 @@ public class IceBlock extends HalfTransparentBlock {
|
|
public void afterDestroy(Level world, BlockPos pos, ItemStack tool) {
|
|
// Paper end - Improve Block#breakNaturally API
|
|
if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_ICE_MELTING)) {
|
|
- if (world.dimensionType().ultraWarm()) {
|
|
+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur
|
|
world.removeBlock(pos, false);
|
|
return;
|
|
}
|
|
@@ -70,7 +70,7 @@ public class IceBlock extends HalfTransparentBlock {
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
- if (world.dimensionType().ultraWarm()) {
|
|
+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur
|
|
world.removeBlock(pos, false);
|
|
} else {
|
|
world.setBlockAndUpdate(pos, IceBlock.meltsInto());
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/KelpBlock.java b/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
index 784b19bc78c8ad9476b6dac37b6778a409a7c675..d49dd8b20d3785cc9482ed2a34fbd7aed4c9e537 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
@@ -72,4 +72,11 @@ public class KelpBlock extends GrowingPlantHeadBlock implements LiquidBlockConta
|
|
protected FluidState getFluidState(BlockState state) {
|
|
return Fluids.WATER.getSource(false);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.kelpMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
index a2d023ff011f71f80032f02430a53d6a08a23623..399441dd8388dcdec08768665d3cfa189aca0a95 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
@@ -140,7 +140,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
|
|
- if (this.shouldSpreadLiquid(world, pos, state)) {
|
|
+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur
|
|
world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava
|
|
}
|
|
|
|
@@ -168,7 +168,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
|
|
- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) {
|
|
+ if (world.getWorldBorder().world.purpurConfig.tickFluids && state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { // Purpur
|
|
tickView.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world));
|
|
}
|
|
|
|
@@ -177,7 +177,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, @Nullable Orientation wireOrientation, boolean notify) {
|
|
- if (this.shouldSpreadLiquid(world, pos, state)) {
|
|
+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur
|
|
world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
index 7ffdcf18bf4bd8b5325c76945b2d80ca3fe52958..dfa931316fde0b2e80068a0edd1427ffd096b15b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
@@ -29,7 +29,7 @@ public class MagmaBlock extends Block {
|
|
|
|
@Override
|
|
public void stepOn(Level world, BlockPos pos, BlockState state, Entity entity) {
|
|
- if (!entity.isSteppingCarefully() && entity instanceof LivingEntity) {
|
|
+ if ((!entity.isSteppingCarefully() || world.purpurConfig.magmaBlockDamageWhenSneaking) && entity instanceof LivingEntity) { // Purpur
|
|
entity.hurt(world.damageSources().hotFloor().directBlock(world, pos), 1.0F); // CraftBukkit
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
index 2b31bf586c1c0bd393d2aa8d0b6635dd9f22f21c..02bcba52e28f757b59e2f384d5744834962870f0 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
@@ -78,7 +78,7 @@ public class NetherPortalBlock extends Block implements Portal {
|
|
|
|
@Override
|
|
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
|
- if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < world.getDifficulty().getId()) { // Spigot
|
|
+ if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(world.purpurConfig.piglinPortalSpawnModifier) < world.getDifficulty().getId()) { // Spigot // Purpur
|
|
while (world.getBlockState(pos).is((Block) this)) {
|
|
pos = pos.below();
|
|
}
|
|
@@ -117,6 +117,13 @@ public class NetherPortalBlock extends Block implements Portal {
|
|
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
|
|
if (entity.canUsePortal(false)) {
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.imposeTeleportRestrictionsOnNetherPortals && (entity.isVehicle() || entity.isPassenger())) {
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL).callEvent()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - Entity in portal
|
|
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.NETHER); // Paper - add portal type
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
@@ -130,7 +137,7 @@ public class NetherPortalBlock extends Block implements Portal {
|
|
@Override
|
|
public int getPortalTransitionTime(ServerLevel world, Entity entity) {
|
|
if (entity instanceof Player entityhuman) {
|
|
- return Math.max(0, world.getGameRules().getInt(entityhuman.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY));
|
|
+ return Math.max(0, entityhuman.canPortalInstant ? 1 : world.getGameRules().getInt(entityhuman.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY)); // Purpur
|
|
} else {
|
|
return 0;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
index acbd60a2f162fe0e254e36d0e8e7face3fc8a7b3..b8355ea1de26c4b6905f477fb4e110f1762447b4 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
@@ -16,7 +16,7 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
-public class NetherWartBlock extends BushBlock {
|
|
+public class NetherWartBlock extends BushBlock implements BonemealableBlock { // Purpur
|
|
|
|
public static final MapCodec<NetherWartBlock> CODEC = simpleCodec(NetherWartBlock::new);
|
|
public static final int MAX_AGE = 3;
|
|
@@ -68,4 +68,32 @@ public class NetherWartBlock extends BushBlock {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(NetherWartBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops, boolean dropExp) {
|
|
+ if (world.purpurConfig.hoeReplantsNetherWarts && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) {
|
|
+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, Items.NETHER_WART);
|
|
+ } else {
|
|
+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(final net.minecraft.world.level.LevelReader world, final BlockPos pos, final BlockState state) {
|
|
+ return ((net.minecraft.world.level.Level) world).purpurConfig.netherWartAffectedByBonemeal && state.getValue(NetherWartBlock.AGE) < 3;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int i = Math.min(3, state.getValue(NetherWartBlock.AGE) + 1);
|
|
+ state = state.setValue(NetherWartBlock.AGE, i);
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, pos, state, 2); // CraftBukkit
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
index 6582db84c5307257f16c321453491cf24e40c9c7..f9015d4e478efeec8a796b7a897638f76064db20 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
@@ -97,7 +97,7 @@ public class NoteBlock extends Block {
|
|
}
|
|
|
|
private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) {
|
|
- if (((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) {
|
|
+ if (world.purpurConfig.noteBlockIgnoreAbove || ((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) { // Purpur
|
|
// CraftBukkit start
|
|
// org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
|
|
// if (event.isCancelled()) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
index 93ed9406c34804831b86d006dbd6087db9948f08..26cb9990b91991e0a2eadc2dcbbf229e2e88fb2d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
@@ -75,6 +75,7 @@ public class ObserverBlock extends DirectionalBlock {
|
|
@Override
|
|
protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
|
|
if (state.getValue(ObserverBlock.FACING) == direction && !(Boolean) state.getValue(ObserverBlock.POWERED)) {
|
|
+ if (!world.getWorldBorder().world.purpurConfig.disableObserverClocks || !(neighborState.getBlock() instanceof ObserverBlock) || neighborState.getValue(ObserverBlock.FACING).getOpposite() != direction) // Purpur
|
|
this.startSignal(world, tickView, pos);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
index 53cea36ec931de89e0060613acf87beb51dc16ec..fd5489993dca0f940da69e9163f78e5c2e6ee063 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
@@ -194,7 +194,7 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate
|
|
|
|
@VisibleForTesting
|
|
public static void maybeTransferFluid(BlockState state, ServerLevel world, BlockPos pos, float dripChance) {
|
|
- if (dripChance <= 0.17578125F || dripChance <= 0.05859375F) {
|
|
+ if (dripChance <= world.purpurConfig.cauldronDripstoneWaterFillChance || dripChance <= world.purpurConfig.cauldronDripstoneLavaFillChance) { // Purpur
|
|
if (PointedDripstoneBlock.isStalactiteStartPos(state, world, pos)) {
|
|
Optional<PointedDripstoneBlock.FluidInfo> optional = PointedDripstoneBlock.getFluidAboveStalactite(world, pos, state);
|
|
|
|
@@ -203,13 +203,13 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate
|
|
float f1;
|
|
|
|
if (fluidtype == Fluids.WATER) {
|
|
- f1 = 0.17578125F;
|
|
+ f1 = world.purpurConfig.cauldronDripstoneWaterFillChance; // Purpur
|
|
} else {
|
|
if (fluidtype != Fluids.LAVA) {
|
|
return;
|
|
}
|
|
|
|
- f1 = 0.05859375F;
|
|
+ f1 = world.purpurConfig.cauldronDripstoneLavaFillChance; // Purpur
|
|
}
|
|
|
|
if (dripChance < f1) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
index 53f1a7ed6b4bd6e2d8460531226aabf249994c02..3760c3c9ab45d7152661edd5a48893e1b583fb95 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
@@ -76,7 +76,7 @@ public class PowderSnowBlock extends Block implements BucketPickup {
|
|
if (world instanceof ServerLevel worldserver) {
|
|
// CraftBukkit start
|
|
if (entity.isOnFire() && entity.mayInteract(worldserver, pos)) {
|
|
- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player))) {
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !((worldserver.purpurConfig.powderSnowBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || entity instanceof Player))) { // Purpur
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
index b763361a8f0f1b46093d5dd9afe8dba0cadf9c78..bd14c08defe8afc5ceca59d16a5b1dbad178f594 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
@@ -30,7 +30,7 @@ public class PoweredRailBlock extends BaseRailBlock {
|
|
}
|
|
|
|
protected boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) {
|
|
- if (distance >= 8) {
|
|
+ if (distance >= world.purpurConfig.railActivationRange) { // Purpur
|
|
return false;
|
|
} else {
|
|
int j = pos.getX();
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
index 9117c035d5a6ff114b028fad3380ceb1fc2b9691..2c5e394156dbf76107adb4913a094dfd4a598dd7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
@@ -149,7 +149,7 @@ public class RespawnAnchorBlock extends Block {
|
|
};
|
|
Vec3 vec3d = explodedPos.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), explosiondamagecalculator, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
|
|
+ if (world.purpurConfig.respawnAnchorExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), explosiondamagecalculator, vec3d, (float) world.purpurConfig.respawnAnchorExplosionPower, world.purpurConfig.respawnAnchorExplosionFire, world.purpurConfig.respawnAnchorExplosionEffect); // CraftBukkit - add state // Purpur
|
|
}
|
|
|
|
public static boolean canSetSpawn(Level world) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
index 0e05bfef55dcacb50766bba8328ffeb8a5394c31..e00fcea07e74de647c26ff9eb32bc682738c15b7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
@@ -135,7 +135,7 @@ public class SculkShriekerBlock extends BaseEntityBlock implements SimpleWaterlo
|
|
@Nullable
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
|
- return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER);
|
|
+ return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER).setValue(SculkShriekerBlock.CAN_SUMMON, ctx.getLevel().purpurConfig.sculkShriekerCanSummonDefault); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SlabBlock.java b/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
index 9274fd639c22e305dda567b303f9b01068adb52c..4433e432ea0ee8d11045b87e68dac3ed43e8cf82 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
@@ -150,4 +150,25 @@ public class SlabBlock extends Block implements SimpleWaterloggedBlock {
|
|
return false;
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean halfBreak(BlockState state, BlockPos pos, net.minecraft.server.level.ServerPlayer player) {
|
|
+ if (state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE) {
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.phys.HitResult result = player.getRayTrace(16, net.minecraft.world.level.ClipContext.Fluid.NONE);
|
|
+ if (result.getType() != net.minecraft.world.phys.HitResult.Type.BLOCK) {
|
|
+ return false;
|
|
+ }
|
|
+ double hitY = result.getLocation().y();
|
|
+ int blockY = org.bukkit.util.NumberConversions.floor(hitY);
|
|
+ player.level().setBlock(pos, state.setValue(SlabBlock.TYPE, (hitY - blockY > 0.5 || blockY - pos.getY() == 1) ? SlabType.BOTTOM : SlabType.TOP), 3);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ net.minecraft.world.entity.item.ItemEntity item = new net.minecraft.world.entity.item.ItemEntity(player.level(), pos.getX(), pos.getY(), pos.getZ(), new ItemStack(asItem()));
|
|
+ item.setDefaultPickUpDelay();
|
|
+ player.level().addFreshEntity(item);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
index 9908a0b5b1fec5f9de518a733f7abbbff7e1a9f9..0ad444cf7f798f63e9140a42c5d5d8cab0fcf14f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
@@ -88,6 +88,12 @@ public class SnowLayerBlock extends Block {
|
|
protected boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) {
|
|
BlockState iblockdata1 = world.getBlockState(pos.below());
|
|
|
|
+ // Purpur start
|
|
+ if (iblockdata1.is(Blocks.BLUE_ICE) && !world.getWorldBorder().world.purpurConfig.snowOnBlueIce) {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return iblockdata1.is(BlockTags.SNOW_LAYER_CANNOT_SURVIVE_ON) ? false : (iblockdata1.is(BlockTags.SNOW_LAYER_CAN_SURVIVE_ON) ? true : Block.isFaceFull(iblockdata1.getCollisionShape(world, pos.below()), Direction.UP) || iblockdata1.is((Block) this) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 8);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
index 4f190a40b8474aa06a92c8afcc06d0044120ff7b..80ee7a6f010cc838625674007a3ea908f2f9dadd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
@@ -42,6 +42,57 @@ public class SpawnerBlock extends BaseEntityBlock {
|
|
return createTickerHelper(type, BlockEntityType.MOB_SPAWNER, world.isClientSide ? SpawnerBlockEntity::clientTick : SpawnerBlockEntity::serverTick);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(Level level, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) {
|
|
+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.drop.spawners") && isSilkTouch(level, stack)) {
|
|
+ ItemStack item = new ItemStack(Blocks.SPAWNER.asItem());
|
|
+
|
|
+ net.minecraft.world.level.SpawnData nextSpawnData = blockEntity instanceof SpawnerBlockEntity spawnerBlock ? spawnerBlock.getSpawner().nextSpawnData : null;
|
|
+ java.util.Optional<net.minecraft.world.entity.EntityType<?>> type = java.util.Optional.empty();
|
|
+ if (nextSpawnData != null) {
|
|
+ type = net.minecraft.world.entity.EntityType.by(nextSpawnData.getEntityToSpawn());
|
|
+ net.minecraft.world.level.SpawnData.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, nextSpawnData).result().ifPresent(tag -> item.set(net.minecraft.core.component.DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY.update(compoundTag -> compoundTag.put("Purpur.SpawnData", tag))));
|
|
+ }
|
|
+
|
|
+ if (type.isPresent()) {
|
|
+ final net.kyori.adventure.text.Component mobName = io.papermc.paper.adventure.PaperAdventure.asAdventure(type.get().getDescription());
|
|
+
|
|
+ String name = level.purpurConfig.silkTouchSpawnerName;
|
|
+ if (name != null && !name.isEmpty() && !name.equals("Monster Spawner")) {
|
|
+ net.kyori.adventure.text.Component displayName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName));
|
|
+ if (name.startsWith("<reset>")) {
|
|
+ displayName = displayName.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ item.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, io.papermc.paper.adventure.PaperAdventure.asVanilla(displayName));
|
|
+ }
|
|
+
|
|
+ List<String> lore = level.purpurConfig.silkTouchSpawnerLore;
|
|
+ if (lore != null && !lore.isEmpty()) {
|
|
+
|
|
+ List<Component> loreComponentList = new java.util.ArrayList<>();
|
|
+ for (String line : lore) {
|
|
+ net.kyori.adventure.text.Component lineComponent = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(line, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName));
|
|
+ if (line.startsWith("<reset>")) {
|
|
+ lineComponent = lineComponent.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ loreComponentList.add(io.papermc.paper.adventure.PaperAdventure.asVanilla(lineComponent));
|
|
+ }
|
|
+
|
|
+ item.set(net.minecraft.core.component.DataComponents.LORE, new net.minecraft.world.item.component.ItemLore(loreComponentList, loreComponentList));
|
|
+ }
|
|
+ item.set(net.minecraft.core.component.DataComponents.HIDE_ADDITIONAL_TOOLTIP, net.minecraft.util.Unit.INSTANCE);
|
|
+ }
|
|
+ popResource(level, pos, item);
|
|
+ }
|
|
+ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops, dropExp);
|
|
+ }
|
|
+
|
|
+ private boolean isSilkTouch(Level level, ItemStack stack) {
|
|
+ return stack != null && level.purpurConfig.silkTouchTools.contains(stack.getItem()) && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH, stack) >= level.purpurConfig.minimumSilkTouchSpawnerRequire;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void spawnAfterBreak(BlockState state, ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) {
|
|
super.spawnAfterBreak(state, world, pos, tool, dropExperience);
|
|
@@ -50,6 +101,7 @@ public class SpawnerBlock extends BaseEntityBlock {
|
|
|
|
@Override
|
|
public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) {
|
|
+ if (worldserver.purpurConfig.silkTouchEnabled && isSilkTouch(worldserver, itemstack)) return 0; // Purpur
|
|
if (flag) {
|
|
int i = 15 + worldserver.random.nextInt(15) + worldserver.random.nextInt(15);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
index 59cf905b1b5686f6f4f2bad94730ffa69d3a2834..4c5bc71fef307d13b640e534ace0b4411f6e97e2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
@@ -61,7 +61,7 @@ public class SpongeBlock extends Block {
|
|
|
|
private boolean removeWaterBreadthFirstSearch(Level world, BlockPos pos) {
|
|
BlockStateListPopulator blockList = new BlockStateListPopulator(world); // CraftBukkit - Use BlockStateListPopulator
|
|
- BlockPos.breadthFirstTraversal(pos, 6, 65, (blockposition1, consumer) -> {
|
|
+ BlockPos.breadthFirstTraversal(pos, world.purpurConfig.spongeAbsorptionRadius, world.purpurConfig.spongeAbsorptionArea, (blockposition1, consumer) -> { // Purpur
|
|
Direction[] aenumdirection = SpongeBlock.ALL_DIRECTIONS;
|
|
int i = aenumdirection.length;
|
|
|
|
@@ -80,7 +80,7 @@ public class SpongeBlock extends Block {
|
|
FluidState fluid = blockList.getFluidState(blockposition1);
|
|
// CraftBukkit end
|
|
|
|
- if (!fluid.is(FluidTags.WATER)) {
|
|
+ if (!fluid.is(FluidTags.WATER) && (!world.purpurConfig.spongeAbsorbsLava || !fluid.is(FluidTags.LAVA)) && (!world.purpurConfig.spongeAbsorbsWaterFromMud || !iblockdata.is(Blocks.MUD))) { // Purpur
|
|
return false;
|
|
} else {
|
|
Block block = iblockdata.getBlock();
|
|
@@ -95,6 +95,10 @@ public class SpongeBlock extends Block {
|
|
|
|
if (iblockdata.getBlock() instanceof LiquidBlock) {
|
|
blockList.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 3); // CraftBukkit
|
|
+ // Purpur start
|
|
+ } else if (iblockdata.is(Blocks.MUD)) {
|
|
+ blockList.setBlock(blockposition1, Blocks.CLAY.defaultBlockState(), 3);
|
|
+ // Purpur end
|
|
} else {
|
|
if (!iblockdata.is(Blocks.KELP) && !iblockdata.is(Blocks.KELP_PLANT) && !iblockdata.is(Blocks.SEAGRASS) && !iblockdata.is(Blocks.TALL_SEAGRASS)) {
|
|
return false;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
index e61644241f24b42bb4f702d3eef5b590b4d107c8..0bf6503819b02e5ba2c346d9d563a93f2946d89b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
@@ -98,4 +98,14 @@ public class StonecutterBlock extends Block {
|
|
protected boolean isPathfindable(BlockState state, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void stepOn(Level level, BlockPos pos, BlockState state, net.minecraft.world.entity.Entity entity) {
|
|
+ if (level.purpurConfig.stonecutterDamage > 0.0F && entity instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ entity.hurt(entity.damageSources().stonecutter().directBlock(level, pos), level.purpurConfig.stonecutterDamage);
|
|
+ }
|
|
+ super.stepOn(level, pos, state, entity);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
index 547ea09ed84595286c97c128b3b96f6d387ae25f..d0f8a13f27132257ece6dadf736c2dc6b1e5720e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
@@ -20,7 +20,7 @@ import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
-public class SugarCaneBlock extends Block {
|
|
+public class SugarCaneBlock extends Block implements BonemealableBlock { // Purpur
|
|
|
|
public static final MapCodec<SugarCaneBlock> CODEC = simpleCodec(SugarCaneBlock::new);
|
|
public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
|
|
@@ -113,4 +113,34 @@ public class SugarCaneBlock extends Block {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(SugarCaneBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(final LevelReader world, final BlockPos pos, final BlockState state) {
|
|
+ if (!((net.minecraft.world.level.Level) world).purpurConfig.sugarCanAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false;
|
|
+
|
|
+ int reedHeight = 0;
|
|
+ while (world.getBlockState(pos.below(reedHeight)).is(this)) {
|
|
+ reedHeight++;
|
|
+ }
|
|
+
|
|
+ return reedHeight < ((net.minecraft.world.level.Level) world).paperConfig().maxGrowthHeight.reeds;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int reedHeight = 0;
|
|
+ while (world.getBlockState(pos.below(reedHeight)).is(this)) {
|
|
+ reedHeight++;
|
|
+ }
|
|
+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.reeds - reedHeight; i++) {
|
|
+ world.setBlockAndUpdate(pos.above(i), state.setValue(SugarCaneBlock.AGE, 0));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
index 953ddb2ea6fd48e57712e30a6addf23e188e5312..f1dfb23160ff70e0da4dd2af2d83e879527c6651 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
@@ -171,7 +171,7 @@ public class TurtleEggBlock extends Block {
|
|
private boolean shouldUpdateHatchLevel(Level world) {
|
|
float f = world.getTimeOfDay(1.0F);
|
|
|
|
- return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(500) == 0;
|
|
+ return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(world.purpurConfig.turtleEggsRandomTickCrackChance) == 0;
|
|
}
|
|
|
|
@Override
|
|
@@ -204,6 +204,31 @@ public class TurtleEggBlock extends Block {
|
|
}
|
|
|
|
private boolean canDestroyEgg(ServerLevel world, Entity entity) {
|
|
- return !(entity instanceof Turtle) && !(entity instanceof Bat) ? (!(entity instanceof LivingEntity) ? false : entity instanceof Player || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) : false;
|
|
+ // Purpur start
|
|
+ if (entity instanceof Turtle || entity instanceof Bat) {
|
|
+ return false;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromExpOrbs && entity instanceof net.minecraft.world.entity.ExperienceOrb) {
|
|
+ return true;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromItems && entity instanceof net.minecraft.world.entity.item.ItemEntity) {
|
|
+ return true;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromMinecarts && entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) {
|
|
+ return true;
|
|
+ }
|
|
+ if (!(entity instanceof LivingEntity)) {
|
|
+ return false;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsTramplingFeatherFalling) {
|
|
+ java.util.Iterator<ItemStack> armor = ((LivingEntity) entity).getArmorSlots().iterator();
|
|
+ return !armor.hasNext() || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, armor.next()) < (int) entity.fallDistance;
|
|
+ }
|
|
+ if (entity instanceof Player) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return world.purpurConfig.turtleEggsBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
index 6342bb11a162b9e6d9475c5989b1670d77e8f0fb..f8be92512446d3f0e5f0f21222bbefd04ab2838a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
@@ -34,4 +34,11 @@ public class TwistingVinesBlock extends GrowingPlantHeadBlock {
|
|
protected boolean canGrowInto(BlockState state) {
|
|
return NetherVines.isValidGrowthState(state);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.twistingVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
index 3dec5a082606ee35a8c8d7f746480262d6a189c5..b2f6ccae9576c176263e51a232e17a08d54543b3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
@@ -34,4 +34,11 @@ public class WeepingVinesBlock extends GrowingPlantHeadBlock {
|
|
protected boolean canGrowInto(BlockState state) {
|
|
return NetherVines.isValidGrowthState(state);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.weepingVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
index 0fbe66cc02bd3d95c0a5dcd55380a1b4a2f17ca6..3a7126883f11ac5a647947eaf060df15536a6cb2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
@@ -80,6 +80,7 @@ public class WitherSkullBlock extends SkullBlock {
|
|
entitywither.moveTo((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.55D, (double) blockposition1.getZ() + 0.5D, shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F, 0.0F);
|
|
entitywither.yBodyRot = shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F;
|
|
entitywither.makeInvulnerable();
|
|
+ entitywither.setSummoner(iblockdata.getBlock().placer == null ? null : iblockdata.getBlock().placer.getUUID()); // Purpur
|
|
// CraftBukkit start
|
|
if (!world.addFreshEntity(entitywither, SpawnReason.BUILD_WITHER)) {
|
|
return;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
index 43899f544cc22666f9ca496128650ce7751ec913..9cba9ebcaa3935778865e3af5211e0b0796cbe16 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
@@ -219,6 +219,21 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
|
}
|
|
|
|
ItemStack itemstack = (ItemStack) blockEntity.items.get(1);
|
|
+ // Purpur start
|
|
+ boolean usedLavaFromUnderneath = false;
|
|
+ if (world.purpurConfig.furnaceUseLavaFromUnderneath && !blockEntity.isLit() && itemstack.isEmpty() && !blockEntity.items.get(0).isEmpty() && world.getGameTime() % 20 == 0) {
|
|
+ BlockPos below = blockEntity.getBlockPos().below();
|
|
+ BlockState belowState = world.getBlockStateIfLoaded(below);
|
|
+ if (belowState != null && belowState.is(Blocks.LAVA)) {
|
|
+ net.minecraft.world.level.material.FluidState fluidState = belowState.getFluidState();
|
|
+ if (fluidState != null && fluidState.isSource()) {
|
|
+ world.setBlock(below, Blocks.AIR.defaultBlockState(), 3);
|
|
+ itemstack = Items.LAVA_BUCKET.getDefaultInstance();
|
|
+ usedLavaFromUnderneath = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
ItemStack itemstack1 = (ItemStack) blockEntity.items.get(0);
|
|
boolean flag2 = !itemstack1.isEmpty();
|
|
boolean flag3 = !itemstack.isEmpty();
|
|
@@ -308,6 +323,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
|
setChanged(world, pos, state);
|
|
}
|
|
|
|
+ if (usedLavaFromUnderneath) blockEntity.items.set(1, ItemStack.EMPTY); // Purpur
|
|
}
|
|
|
|
private static boolean canBurn(RegistryAccess dynamicRegistryManager, @Nullable RecipeHolder<? extends AbstractCookingRecipe> recipe, SingleRecipeInput input, NonNullList<ItemStack> inventory, int maxCount) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
index 618552afbdacc919c33b30a6bf4834fb71ab3d5b..7a059d20abdcc0073a314311d78f63ae4bd0365e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
@@ -68,7 +68,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
public BarrelBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.BARREL, pos, state);
|
|
- this.items = NonNullList.withSize(27, ItemStack.EMPTY);
|
|
+ // Purpur start
|
|
+ this.items = NonNullList.withSize(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ }, ItemStack.EMPTY);
|
|
+ // Purpur end
|
|
this.openersCounter = new ContainerOpenersCounter() {
|
|
@Override
|
|
protected void onOpen(Level world, BlockPos pos, BlockState state) {
|
|
@@ -119,7 +128,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
@Override
|
|
public int getContainerSize() {
|
|
- return 27;
|
|
+ // Purpur start
|
|
+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ };
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -139,7 +157,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
@Override
|
|
protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) {
|
|
- return ChestMenu.threeRows(syncId, playerInventory, this);
|
|
+ // Purpur start
|
|
+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> ChestMenu.sixRows(syncId, playerInventory, this);
|
|
+ case 5 -> ChestMenu.fiveRows(syncId, playerInventory, this);
|
|
+ case 4 -> ChestMenu.fourRows(syncId, playerInventory, this);
|
|
+ case 2 -> ChestMenu.twoRows(syncId, playerInventory, this);
|
|
+ case 1 -> ChestMenu.oneRow(syncId, playerInventory, this);
|
|
+ default -> ChestMenu.threeRows(syncId, playerInventory, this);
|
|
+ };
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
index 0e0d178f2793ab014358f534c8dc53218b89f083..2d190b3a6378b8cbadfa65510df1ccfbd5882ef8 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
@@ -92,6 +92,16 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
|
|
public double getEffectRange() {
|
|
if (this.effectRange < 0) {
|
|
+ // Purpur Start
|
|
+ if (this.level != null) {
|
|
+ switch (this.levels) {
|
|
+ case 1: return this.level.purpurConfig.beaconLevelOne;
|
|
+ case 2: return this.level.purpurConfig.beaconLevelTwo;
|
|
+ case 3: return this.level.purpurConfig.beaconLevelThree;
|
|
+ case 4: return this.level.purpurConfig.beaconLevelFour;
|
|
+ }
|
|
+ }
|
|
+ // Purpur End
|
|
return this.levels * 10 + 10;
|
|
} else {
|
|
return effectRange;
|
|
@@ -168,6 +178,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
int j = pos.getY();
|
|
int k = pos.getZ();
|
|
BlockPos blockposition1;
|
|
+ boolean isTintedGlass = false;
|
|
|
|
if (blockEntity.lastCheckY < j) {
|
|
blockposition1 = pos;
|
|
@@ -201,6 +212,9 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
}
|
|
}
|
|
} else {
|
|
+ if (world.purpurConfig.beaconAllowEffectsWithTintedGlass && block.equals(Blocks.TINTED_GLASS)) {
|
|
+ isTintedGlass = true;
|
|
+ }
|
|
if (tileentitybeacon_beaconcolortracker == null || iblockdata1.getLightBlock() >= 15 && !iblockdata1.is(Blocks.BEDROCK)) {
|
|
blockEntity.checkingBeamSections.clear();
|
|
blockEntity.lastCheckY = l;
|
|
@@ -220,7 +234,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
blockEntity.levels = BeaconBlockEntity.updateBase(world, i, j, k);
|
|
}
|
|
|
|
- if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) {
|
|
+ if (blockEntity.levels > 0 && (!blockEntity.beamSections.isEmpty() || (world.purpurConfig.beaconAllowEffectsWithTintedGlass && isTintedGlass))) {
|
|
BeaconBlockEntity.applyEffects(world, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - Custom beacon ranges
|
|
BeaconBlockEntity.playSound(world, pos, SoundEvents.BEACON_AMBIENT);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
index 83ad45aed0894e90825d22e078632352c3a06816..def408384cbd571b7bee23f5cecf430a5d690c4b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
@@ -60,7 +60,7 @@ public class BeehiveBlockEntity extends BlockEntity {
|
|
private List<BeehiveBlockEntity.BeeData> stored = Lists.newArrayList();
|
|
@Nullable
|
|
public BlockPos savedFlowerPos;
|
|
- public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold
|
|
+ public int maxBees = org.purpurmc.purpur.PurpurConfig.beeInsideBeeHive; // CraftBukkit - allow setting max amount of bees a hive can hold // Purpur
|
|
|
|
public BeehiveBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.BEEHIVE, pos, state);
|
|
@@ -147,11 +147,33 @@ public class BeehiveBlockEntity extends BlockEntity {
|
|
return list;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public List<Entity> releaseBee(BlockState iblockdata, BeehiveBlockEntity.BeeData data, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) {
|
|
+ List<Entity> list = Lists.newArrayList();
|
|
+
|
|
+ BeehiveBlockEntity.releaseOccupant(this.level, this.worldPosition, iblockdata, data.occupant, list, tileentitybeehive_releasestatus, this.savedFlowerPos, force);
|
|
+
|
|
+ if (!list.isEmpty()) {
|
|
+ stored.remove(data);
|
|
+
|
|
+ super.setChanged();
|
|
+ }
|
|
+
|
|
+ return list;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@VisibleForDebug
|
|
public int getOccupantCount() {
|
|
return this.stored.size();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public List<BeeData> getStored() {
|
|
+ return stored;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - Add EntityBlockStorage clearEntities
|
|
public void clearBees() {
|
|
this.stored.clear();
|
|
@@ -472,9 +494,9 @@ public class BeehiveBlockEntity extends BlockEntity {
|
|
}
|
|
}
|
|
|
|
- private static class BeeData {
|
|
+ public static class BeeData { // Purpur - change from private to public
|
|
|
|
- private final BeehiveBlockEntity.Occupant occupant;
|
|
+ public final BeehiveBlockEntity.Occupant occupant; // Purpur - make public
|
|
private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts
|
|
private int ticksInHive;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
index 1f929b467a0ece3143af58a657cf5983c07a8d51..eaa6ece956f90632831f0558924eaf18680a252b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
@@ -95,6 +95,12 @@ public abstract class BlockEntity {
|
|
if (persistentDataTag instanceof CompoundTag) {
|
|
this.persistentDataContainer.putAll((CompoundTag) persistentDataTag);
|
|
}
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.persistentLore")) {
|
|
+ net.minecraft.world.item.component.ItemLore.CODEC.decode(net.minecraft.nbt.NbtOps.INSTANCE, nbt.getCompound("Purpur.persistentLore")).result()
|
|
+ .ifPresent(tag -> this.persistentLore = tag.getFirst());
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@@ -111,6 +117,15 @@ public abstract class BlockEntity {
|
|
this.loadAdditional(nbt, registries);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ protected void saveAdditional(CompoundTag nbt) {
|
|
+ if (this.persistentLore != null) {
|
|
+ net.minecraft.world.item.component.ItemLore.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, this.persistentLore).result()
|
|
+ .ifPresent(tag -> nbt.put("Purpur.persistentLore", tag));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {}
|
|
|
|
public final CompoundTag saveWithFullMetadata(HolderLookup.Provider registries) {
|
|
@@ -419,4 +434,16 @@ public abstract class BlockEntity {
|
|
|
|
<T> T getOrDefault(DataComponentType<? extends T> type, T fallback);
|
|
}
|
|
+ // Purpur start
|
|
+ @Nullable
|
|
+ private net.minecraft.world.item.component.ItemLore persistentLore = null;
|
|
+
|
|
+ public void setPersistentLore(net.minecraft.world.item.component.ItemLore lore) {
|
|
+ this.persistentLore = lore;
|
|
+ }
|
|
+
|
|
+ public @org.jetbrains.annotations.Nullable net.minecraft.world.item.component.ItemLore getPersistentLore() {
|
|
+ return this.persistentLore;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
index d354c2ad41533ec8d52f7c5f63f8f74a0b1ce235..a43c41a26c1e34a2f4eda1c498a562664a783c77 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
@@ -169,7 +169,7 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
if ((l > 1 || i1 > 1 || j1 > 1) && (i == 0 && (i1 == 2 || j1 == 2) || j == 0 && (l == 2 || j1 == 2) || k == 0 && (l == 2 || i1 == 2))) {
|
|
BlockPos blockposition2 = pos.offset(i, j, k);
|
|
BlockState iblockdata = world.getBlockState(blockposition2);
|
|
- Block[] ablock = ConduitBlockEntity.VALID_BLOCKS;
|
|
+ Block[] ablock = world.purpurConfig.conduitBlocks; // Purpur
|
|
int k1 = ablock.length;
|
|
|
|
for (int l1 = 0; l1 < k1; ++l1) {
|
|
@@ -189,13 +189,13 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
|
|
private static void applyEffects(Level world, BlockPos pos, List<BlockPos> activatingBlocks) {
|
|
// CraftBukkit start
|
|
- ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks));
|
|
+ ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks, world)); // Purpur
|
|
}
|
|
|
|
- public static int getRange(List<BlockPos> list) {
|
|
+ public static int getRange(List<BlockPos> list, Level world) { // Purpur
|
|
// CraftBukkit end
|
|
int i = list.size();
|
|
- int j = i / 7 * 16;
|
|
+ int j = i / 7 * world.purpurConfig.conduitDistance; // Purpur
|
|
// CraftBukkit start
|
|
return j;
|
|
}
|
|
@@ -238,20 +238,20 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
tileentityconduit.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, blockposition, tileentityconduit.destroyTargetUUID);
|
|
tileentityconduit.destroyTargetUUID = null;
|
|
} else if (tileentityconduit.destroyTarget == null) {
|
|
- List<LivingEntity> list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(blockposition), (entityliving1) -> {
|
|
+ List<LivingEntity> list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(blockposition, world), (entityliving1) -> { // Purpur
|
|
return entityliving1 instanceof Enemy && entityliving1.isInWaterOrRain();
|
|
});
|
|
|
|
if (!list1.isEmpty()) {
|
|
tileentityconduit.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size()));
|
|
}
|
|
- } else if (!tileentityconduit.destroyTarget.isAlive() || !blockposition.closerThan(tileentityconduit.destroyTarget.blockPosition(), 8.0D)) {
|
|
+ } else if (!tileentityconduit.destroyTarget.isAlive() || !blockposition.closerThan(tileentityconduit.destroyTarget.blockPosition(), world.purpurConfig.conduitDamageDistance)) { // Purpur
|
|
tileentityconduit.destroyTarget = null;
|
|
}
|
|
|
|
// CraftBukkit start
|
|
if (damageTarget && tileentityconduit.destroyTarget != null) {
|
|
- if (tileentityconduit.destroyTarget.hurtServer((ServerLevel) world, world.damageSources().magic().directBlock(world, blockposition), 4.0F)) {
|
|
+ if (tileentityconduit.destroyTarget.hurtServer((ServerLevel) world, world.damageSources().magic().directBlock(world, blockposition), world.purpurConfig.conduitDamageAmount)) { // Purpur
|
|
world.playSound(null, tileentityconduit.destroyTarget.getX(), tileentityconduit.destroyTarget.getY(), tileentityconduit.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
}
|
|
// CraftBukkit end
|
|
@@ -276,16 +276,22 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
}
|
|
|
|
public static AABB getDestroyRangeAABB(BlockPos pos) {
|
|
+ // Purpur start
|
|
+ return getDestroyRangeAABB(pos, null);
|
|
+ }
|
|
+
|
|
+ private static AABB getDestroyRangeAABB(BlockPos pos, Level level) {
|
|
+ // Purpur end
|
|
int i = pos.getX();
|
|
int j = pos.getY();
|
|
int k = pos.getZ();
|
|
|
|
- return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(8.0D);
|
|
+ return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(level == null ? 8.0D : level.purpurConfig.conduitDamageDistance); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
private static LivingEntity findDestroyTarget(Level world, BlockPos pos, UUID uuid) {
|
|
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving) -> {
|
|
+ List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos, world), (entityliving) -> { // Purpur
|
|
return entityliving.getUUID().equals(uuid);
|
|
});
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
|
|
index 39aac959775afeaeea211f21d498cb0ddf0a3fcb..6349a342c023f378af431a73a62fb017882e257d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
|
|
@@ -28,6 +28,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable
|
|
private static final RandomSource RANDOM = RandomSource.create();
|
|
@Nullable
|
|
private Component name;
|
|
+ private int lapis = 0; // Purpur
|
|
|
|
public EnchantingTableBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.ENCHANTING_TABLE, pos, state);
|
|
@@ -39,6 +40,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable
|
|
if (this.hasCustomName()) {
|
|
nbt.putString("CustomName", Component.Serializer.toJson(this.name, registries));
|
|
}
|
|
+ nbt.putInt("Purpur.Lapis", this.lapis); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -47,6 +49,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable
|
|
if (nbt.contains("CustomName", 8)) {
|
|
this.name = parseCustomNameSafe(nbt.getString("CustomName"), registries);
|
|
}
|
|
+ this.lapis = nbt.getInt("Purpur.Lapis"); // Purpur
|
|
}
|
|
|
|
public static void bookAnimationTick(Level world, BlockPos pos, BlockState state, EnchantingTableBlockEntity blockEntity) {
|
|
@@ -138,4 +141,14 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable
|
|
public void removeComponentsFromTag(CompoundTag nbt) {
|
|
nbt.remove("CustomName");
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public int getLapis() {
|
|
+ return this.lapis;
|
|
+ }
|
|
+
|
|
+ public void setLapis(int lapis) {
|
|
+ this.lapis = lapis;
|
|
+ }
|
|
+ // Purpur
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java b/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java
|
|
index 61ef08ac941b1e8988d001241780d3a1582f7a2d..2eab5b43ab654966d26424597c1f3baa919e8434 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java
|
|
@@ -17,7 +17,7 @@ import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
|
|
public class FuelValues {
|
|
- private final Object2IntSortedMap<Item> values;
|
|
+ public final Object2IntSortedMap<Item> values; // Purpur - private -> public
|
|
|
|
FuelValues(Object2IntSortedMap<Item> fuelValues) {
|
|
this.values = fuelValues;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
index 8ac19e1e052e73ff3fd09089bb8e3fd687390ee4..6da1eec98c08e4909ecbd48fe90b3fd62011e284 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
@@ -201,16 +201,31 @@ public class SignBlockEntity extends BlockEntity {
|
|
return this.setText((SignText) textChanger.apply(signtext), front);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private Component translateColors(org.bukkit.entity.Player player, String line, Style style) {
|
|
+ if (level.purpurConfig.signAllowColors) {
|
|
+ if (player.hasPermission("purpur.sign.color")) line = line.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1");
|
|
+ if (player.hasPermission("purpur.sign.style")) line = line.replaceAll("(?i)&([l-or])", "\u00a7$1");
|
|
+ if (player.hasPermission("purpur.sign.magic")) line = line.replaceAll("(?i)&([kr])", "\u00a7$1");
|
|
+
|
|
+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(line));
|
|
+ } else {
|
|
+ return Component.literal(line).setStyle(style);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
private SignText setMessages(net.minecraft.world.entity.player.Player entityhuman, List<FilteredText> list, SignText signtext, boolean front) { // CraftBukkit
|
|
SignText originalText = signtext; // CraftBukkit
|
|
for (int i = 0; i < list.size(); ++i) {
|
|
FilteredText filteredtext = (FilteredText) list.get(i);
|
|
Style chatmodifier = signtext.getMessage(i, entityhuman.isTextFilteringEnabled()).getStyle();
|
|
|
|
+ org.bukkit.entity.Player player = (org.bukkit.craftbukkit.entity.CraftPlayer) entityhuman.getBukkitEntity(); // Purpur
|
|
if (entityhuman.isTextFilteringEnabled()) {
|
|
- signtext = signtext.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only
|
|
+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur
|
|
} else {
|
|
- signtext = signtext.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.raw())).setStyle(chatmodifier), Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only
|
|
+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.raw()), chatmodifier), translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -348,6 +363,28 @@ public class SignBlockEntity extends BlockEntity {
|
|
return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); // Paper - Fix commands from signs not firing command events
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public ClientboundBlockEntityDataPacket getTranslatedUpdatePacket(boolean filtered, boolean front) {
|
|
+ final CompoundTag nbt = new CompoundTag();
|
|
+ this.saveAdditional(nbt, this.getLevel().registryAccess());
|
|
+ final Component[] lines = front ? frontText.getMessages(filtered) : backText.getMessages(filtered);
|
|
+ final String side = front ? "front_text" : "back_text";
|
|
+ for (int i = 0; i < 4; i++) {
|
|
+ final var component = io.papermc.paper.adventure.PaperAdventure.asAdventure(lines[i]);
|
|
+ final String line = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().serialize(component);
|
|
+ final var text = net.kyori.adventure.text.Component.text(line);
|
|
+ final String json = net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serialize(text);
|
|
+ if (!nbt.contains(side)) nbt.put(side, new CompoundTag());
|
|
+ final CompoundTag sideNbt = nbt.getCompound(side);
|
|
+ if (!sideNbt.contains("messages")) sideNbt.put("messages", new net.minecraft.nbt.ListTag());
|
|
+ final net.minecraft.nbt.ListTag messagesNbt = sideNbt.getList("messages", Tag.TAG_STRING);
|
|
+ messagesNbt.set(i, net.minecraft.nbt.StringTag.valueOf(json));
|
|
+ }
|
|
+ nbt.putString("PurpurEditor", "true");
|
|
+ return ClientboundBlockEntityDataPacket.create(this, (blockEntity, registryAccess) -> nbt);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
index 205e223c356634bd6bc6bd58c6f0b7fda61a6f5f..bea05cb928d540a2f19b51bb7352d032b2dd69cd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
@@ -81,7 +81,7 @@ public class PistonStructureResolver {
|
|
return true;
|
|
} else {
|
|
int i = 1;
|
|
- if (i + this.toPush.size() > 12) {
|
|
+ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
} else {
|
|
while (isSticky(blockState)) {
|
|
@@ -95,7 +95,7 @@ public class PistonStructureResolver {
|
|
break;
|
|
}
|
|
|
|
- if (++i + this.toPush.size() > 12) {
|
|
+ if (++i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
}
|
|
}
|
|
@@ -140,7 +140,7 @@ public class PistonStructureResolver {
|
|
return true;
|
|
}
|
|
|
|
- if (this.toPush.size() >= 12) {
|
|
+ if (this.toPush.size() >= this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
index 5b6fbfd1a7a2d87fb2b87d5d1e674206cdf9b280..dcf2dcece3e995ce4646b931329246be19a4e1c2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
@@ -91,7 +91,7 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
|
|
protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP};
|
|
public final boolean hasCollision;
|
|
- protected final float explosionResistance;
|
|
+ public float explosionResistance; // Purpur - protected final -> public
|
|
protected final boolean isRandomlyTicking;
|
|
protected final SoundType soundType;
|
|
protected final float friction;
|
|
@@ -99,7 +99,7 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
protected final float jumpFactor;
|
|
protected final boolean dynamicShape;
|
|
protected final FeatureFlagSet requiredFeatures;
|
|
- protected final BlockBehaviour.Properties properties;
|
|
+ public final BlockBehaviour.Properties properties; // Purpur - protected -> public
|
|
protected final Optional<ResourceKey<LootTable>> drops;
|
|
protected final String descriptionId;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
index 356d010506fd21f3c752e4aa86c46c1106fdde3b..86e16dd6b905af31795fda8002f2e1f857ddcb9f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
@@ -106,6 +106,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
|
|
}
|
|
// Paper end - Entity load/save limit per chunk
|
|
CompoundTag compoundTagx = new CompoundTag();
|
|
+ if (!entity.canSaveToDisk()) return; // Purpur
|
|
if (entity.save(compoundTagx)) {
|
|
listTag.add(compoundTagx);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
index 997f80d332b95bb011cbbc27c065b2811a2dddc7..d9a1ad945504d26a2c0717e40782f2859a4b2e9b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
@@ -48,7 +48,7 @@ public class PhantomSpawner implements CustomSpawner {
|
|
int spawnAttemptMaxSeconds = world.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds;
|
|
this.nextTick += (spawnAttemptMinSeconds + randomsource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20;
|
|
// Paper end - Ability to control player's insomnia and phantoms
|
|
- if (world.getSkyDarken() < 5 && world.dimensionType().hasSkyLight()) {
|
|
+ if (world.getSkyDarken() < world.purpurConfig.phantomSpawnMinSkyDarkness && world.dimensionType().hasSkyLight()) { // Purpur
|
|
return 0;
|
|
} else {
|
|
int i = 0;
|
|
@@ -60,10 +60,10 @@ public class PhantomSpawner implements CustomSpawner {
|
|
if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper - Add phantom creative and insomniac controls
|
|
BlockPos blockposition = entityplayer.blockPosition();
|
|
|
|
- if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) {
|
|
+ if (!world.dimensionType().hasSkyLight() || (!world.purpurConfig.phantomSpawnOnlyAboveSeaLevel || blockposition.getY() >= world.getSeaLevel()) && (!world.purpurConfig.phantomSpawnOnlyWithVisibleSky || world.canSeeSky(blockposition))) { // Purpur
|
|
DifficultyInstance difficultydamagescaler = world.getCurrentDifficultyAt(blockposition);
|
|
|
|
- if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * 3.0F)) {
|
|
+ if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * (float) world.purpurConfig.phantomSpawnLocalDifficultyChance)) { // Purpur
|
|
ServerStatsCounter serverstatisticmanager = entityplayer.getStats();
|
|
int j = Mth.clamp(serverstatisticmanager.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
|
|
boolean flag2 = true;
|
|
@@ -83,7 +83,7 @@ public class PhantomSpawner implements CustomSpawner {
|
|
|
|
if (NaturalSpawner.isValidEmptySpawnBlock(world, blockposition1, iblockdata, fluid, EntityType.PHANTOM)) {
|
|
SpawnGroupData groupdataentity = null;
|
|
- int k = 1 + randomsource.nextInt(difficultydamagescaler.getDifficulty().getId() + 1);
|
|
+ int k = world.purpurConfig.phantomSpawnMinPerAttempt + world.random.nextInt((world.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? difficultydamagescaler.getDifficulty().getId() : world.purpurConfig.phantomSpawnMaxPerAttempt - world.purpurConfig.phantomSpawnMinPerAttempt) + 1); // Purpur
|
|
|
|
for (int l = 0; l < k; ++l) {
|
|
// Paper start - PhantomPreSpawnEvent
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
index 221978c64cf6171db078c6cbfc850f6aeae73884..408e7c61d87a0e6d8502bf1f5ca76fd728c5d10c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -266,7 +266,7 @@ public abstract class FlowingFluid extends Fluid {
|
|
}
|
|
}
|
|
|
|
- if (j >= 2 && this.canConvertToSource(world)) {
|
|
+ if (j >= getRequiredSources(world) && this.canConvertToSource(world)) { // Purpur
|
|
BlockState iblockdata2 = world.getBlockState(blockposition_mutableblockposition.setWithOffset(pos, Direction.DOWN));
|
|
FluidState fluid1 = iblockdata2.getFluidState();
|
|
|
|
@@ -356,6 +356,12 @@ public abstract class FlowingFluid extends Fluid {
|
|
|
|
protected abstract boolean canConvertToSource(ServerLevel world);
|
|
|
|
+ // Purpur start
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return 2;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected void spreadTo(LevelAccessor world, BlockPos pos, BlockState state, Direction direction, FluidState fluidState) {
|
|
Block block = state.getBlock();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
index 884db3e64cb22ed765beec8f11ea309fcf810207..6e643c1a7f7e71cfd20603facaf224985ee81716 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
@@ -181,7 +181,7 @@ public abstract class LavaFluid extends FlowingFluid {
|
|
|
|
@Override
|
|
public int getTickDelay(LevelReader world) {
|
|
- return world.dimensionType().ultraWarm() ? 10 : 30;
|
|
+ return world.dimensionType().ultraWarm() ? world.getWorldBorder().world.purpurConfig.lavaSpeedNether : world.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -199,6 +199,13 @@ public abstract class LavaFluid extends FlowingFluid {
|
|
world.levelEvent(1501, pos, 0);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return level.purpurConfig.lavaInfiniteRequiredSources;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected boolean canConvertToSource(ServerLevel world) {
|
|
return world.getGameRules().getBoolean(GameRules.RULE_LAVA_SOURCE_CONVERSION);
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
index 552925ba47c7475e2e1ec2ded0966f28ed3e50a5..1e741f36b79585f33abe413beafe00cf5205d54f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
@@ -81,6 +81,13 @@ public abstract class WaterFluid extends FlowingFluid {
|
|
return world.getGameRules().getBoolean(GameRules.RULE_WATER_SOURCE_CONVERSION);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return level.purpurConfig.waterInfiniteRequiredSources;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - Add BlockBreakBlockEvent
|
|
@Override
|
|
protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
index c84fd369d92932903c76bb2012602617d3e2d213..224896124706764412033c8726c822e116f2c0f1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
@@ -240,7 +240,7 @@ public class WalkNodeEvaluator extends NodeEvaluator {
|
|
if ((node == null || node.costMalus < 0.0F)
|
|
&& maxYStep > 0
|
|
&& (pathType != PathType.FENCE || this.canWalkOverFences())
|
|
- && pathType != PathType.UNPASSABLE_RAIL
|
|
+ && (this.mob.level().purpurConfig.mobsIgnoreRails || pathType != PathType.UNPASSABLE_RAIL) // Purpur
|
|
&& pathType != PathType.TRAPDOOR
|
|
&& pathType != PathType.POWDER_SNOW) {
|
|
node = this.tryJumpOn(x, y, z, maxYStep, prevFeetY, direction, nodeType, mutableBlockPos);
|
|
@@ -491,7 +491,7 @@ public class WalkNodeEvaluator extends NodeEvaluator {
|
|
return PathType.TRAPDOOR;
|
|
} else if (blockState.is(Blocks.POWDER_SNOW)) {
|
|
return PathType.POWDER_SNOW;
|
|
- } else if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
+ } else if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH) || blockState.is(Blocks.STONECUTTER)) { // Purpur
|
|
return PathType.DAMAGE_OTHER;
|
|
} else if (blockState.is(Blocks.HONEY_BLOCK)) {
|
|
return PathType.STICKY_HONEY;
|
|
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalShape.java b/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
index 90056822cd17f3d33d14b3f94b34750ee522a0a9..acdff7b4a00d563739fd301c3633a266875296fa 100644
|
|
--- a/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
+++ b/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
@@ -35,7 +35,7 @@ public class PortalShape {
|
|
private static final int MIN_HEIGHT = 3;
|
|
public static final int MAX_HEIGHT = 21;
|
|
private static final BlockBehaviour.StatePredicate FRAME = (iblockdata, iblockaccess, blockposition) -> {
|
|
- return iblockdata.is(Blocks.OBSIDIAN);
|
|
+ return iblockdata.is(Blocks.OBSIDIAN) || (org.purpurmc.purpur.PurpurConfig.cryingObsidianValidForPortalFrame && iblockdata.is(Blocks.CRYING_OBSIDIAN)); // Purpur
|
|
};
|
|
private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F;
|
|
private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0D;
|
|
diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
index b15b0c8057e61c6aef05c0865e2c3e06adcf938b..aabc0b5a3e50aad8c4f902fa41e6bed319599ff3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
@@ -81,6 +81,7 @@ public class MapItemSavedData extends SavedData {
|
|
private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
|
|
private int trackedDecorationCount;
|
|
private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
|
|
+ public boolean isExplorerMap; // Purpur
|
|
|
|
// CraftBukkit start
|
|
public final CraftMapView mapView;
|
|
diff --git a/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java b/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java
|
|
index 5f27e1ce23f2ed68e4c8af1986fafce940dbf826..d8cf49cbd82ed12d23fa10a81a88cc4bcf1c0f10 100644
|
|
--- a/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java
|
|
+++ b/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java
|
|
@@ -66,6 +66,11 @@ public class EnchantedCountIncreaseFunction extends LootItemConditionalFunction
|
|
Entity entity = context.getOptionalParameter(LootContextParams.ATTACKING_ENTITY);
|
|
if (entity instanceof LivingEntity livingEntity) {
|
|
int i = EnchantmentHelper.getEnchantmentLevel(this.enchantment, livingEntity);
|
|
+ // Purpur start - Add an option to fix MC-3304 projectile looting
|
|
+ if (org.purpurmc.purpur.PurpurConfig.fixProjectileLootingTransfer && context.getOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY) instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) {
|
|
+ i = arrow.actualEnchantments.getLevel(this.enchantment);
|
|
+ }
|
|
+ // Purpur end - Add an option to fix MC-3304 projectile looting
|
|
if (i == 0) {
|
|
return stack;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java
|
|
index 6cf6d4ec7b9e43c7b2b4c0e2fb080964ff588130..e74866e5195a5eeae7666ad7be750edac5947094 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/AABB.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/AABB.java
|
|
@@ -551,4 +551,10 @@ public class AABB {
|
|
public static AABB ofSize(Vec3 center, double dx, double dy, double dz) {
|
|
return new AABB(center.x - dx / 2.0, center.y - dy / 2.0, center.z - dz / 2.0, center.x + dx / 2.0, center.y + dy / 2.0, center.z + dz / 2.0);
|
|
}
|
|
+
|
|
+ // Purpur - tuinity added method
|
|
+ public final AABB offsetY(double dy) {
|
|
+ return new AABB(this.minX, this.minY + dy, this.minZ, this.maxX, this.maxY + dy, this.maxZ);
|
|
+ }
|
|
+ // Purpur
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
index 94ca0407303c4493ab4928b12ec6ecc75aaca549..a138e1b6b66d99f2035de054137a607aa6b7f0b9 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
@@ -363,14 +363,26 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
|
|
@Override
|
|
public Location getLocation() {
|
|
+ // Purpur start
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getLocation();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
CompoundTag data = this.getData();
|
|
if (data == null) {
|
|
return null;
|
|
}
|
|
|
|
- if (data.contains("Pos") && data.contains("Rotation")) {
|
|
- ListTag position = (ListTag) data.get("Pos");
|
|
- ListTag rotation = (ListTag) data.get("Rotation");
|
|
+ // Purpur start - OfflinePlayer API
|
|
+ //if (data.contains("Pos") && data.contains("Rotation")) {
|
|
+ ListTag position = data.getList("Pos", net.minecraft.nbt.Tag.TAG_DOUBLE);
|
|
+ ListTag rotation = data.getList("Rotation", net.minecraft.nbt.Tag.TAG_FLOAT);
|
|
+
|
|
+ if (position.isEmpty() && rotation.isEmpty()) {
|
|
+ return null;
|
|
+ }
|
|
+ // Purpur end - OfflinePlayer API
|
|
|
|
UUID uuid = new UUID(data.getLong("WorldUUIDMost"), data.getLong("WorldUUIDLeast"));
|
|
|
|
@@ -381,9 +393,9 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
rotation.getFloat(0),
|
|
rotation.getFloat(1)
|
|
);
|
|
- }
|
|
+ //} // Purpur - OfflinePlayer API
|
|
|
|
- return null;
|
|
+ //return null; // Purpur - OfflinePlayer API
|
|
}
|
|
|
|
@Override
|
|
@@ -626,4 +638,191 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
manager.save();
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start - OfflinePlayer API
|
|
+ @Override
|
|
+ public boolean getAllowFlight() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getAllowFlight();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ if (!data.contains("abilities")) return false;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getByte("mayfly") == (byte) 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setAllowFlight(boolean flight) {
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setAllowFlight(flight);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putByte("mayfly", (byte) (flight ? 1 : 0));
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isFlying() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.isFlying();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ if (!data.contains("abilities")) return false;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getByte("flying") == (byte) 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setFlying(boolean value) {
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setFlying(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putByte("mayfly", (byte) (value ? 1 : 0));
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setFlySpeed(float value) throws IllegalArgumentException {
|
|
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("FlySpeed needs to be between -1 and 1");
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setFlySpeed(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putFloat("flySpeed", value);
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getFlySpeed() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getFlySpeed();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return 0;
|
|
+ if (!data.contains("abilities")) return 0;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getFloat("flySpeed");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setWalkSpeed(float value) throws IllegalArgumentException {
|
|
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("WalkSpeed needs to be between -1 and 1");
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setWalkSpeed(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putFloat("walkSpeed", value);
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getWalkSpeed() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getWalkSpeed();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return 0;
|
|
+ if (!data.contains("abilities")) return 0;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getFloat("walkSpeed");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleport(destination);
|
|
+ } else {
|
|
+ return setLocation(destination);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause){
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleport(destination, cause);
|
|
+ } else {
|
|
+ return setLocation(destination);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleportAsync(destination);
|
|
+ } else {
|
|
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleportAsync(destination, cause);
|
|
+ } else {
|
|
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean setLocation(Location location) {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ data.putLong("WorldUUIDMost", location.getWorld().getUID().getMostSignificantBits());
|
|
+ data.putLong("WorldUUIDLeast", location.getWorld().getUID().getLeastSignificantBits());
|
|
+ net.minecraft.nbt.ListTag position = new net.minecraft.nbt.ListTag();
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getX()));
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getY()));
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getZ()));
|
|
+ data.put("Pos", position);
|
|
+ net.minecraft.nbt.ListTag rotation = new net.minecraft.nbt.ListTag();
|
|
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getYaw()));
|
|
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getPitch()));
|
|
+ data.put("Rotation", rotation);
|
|
+ save(data);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Safely replaces player's .dat file with provided CompoundTag
|
|
+ * @param compoundTag
|
|
+ */
|
|
+ private void save(CompoundTag compoundTag) {
|
|
+ File playerDir = server.console.playerDataStorage.getPlayerDir();
|
|
+ try {
|
|
+ File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
|
|
+ net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile.toPath());
|
|
+ File playerDataFile = new File(playerDir, this.getUniqueId()+".dat");
|
|
+ File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old");
|
|
+ net.minecraft.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath());
|
|
+ } catch (java.io.IOException e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ // Purpur end - OfflinePlayer API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index c11f638e120ae4989d110f55c4569dd67b6f3260..ef8041877456316fc32a382e28ee81161d962353 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -427,6 +427,20 @@ public final class CraftServer implements Server {
|
|
this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager);
|
|
this.pluginManager.paperPluginManager = this.paperPluginManager;
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ org.purpurmc.purpur.language.Language.setLanguage(new org.purpurmc.purpur.language.Language() {
|
|
+ private net.minecraft.locale.Language language = net.minecraft.locale.Language.getInstance();
|
|
+ @Override
|
|
+ public boolean has(@org.jetbrains.annotations.NotNull String key) {
|
|
+ return language.has(key);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @org.jetbrains.annotations.NotNull String getOrDefault(@org.jetbrains.annotations.NotNull String key) {
|
|
+ return language.getOrDefault(key);
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
|
|
CraftRegistry.setMinecraftRegistry(console.registryAccess());
|
|
|
|
@@ -1088,6 +1102,7 @@ public final class CraftServer implements Server {
|
|
org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
|
|
this.console.paperConfigurations.reloadConfigs(this.console);
|
|
this.console.galeConfigurations.reloadConfigs(this.console); // Gale - Gale configuration
|
|
+ org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur
|
|
for (ServerLevel world : this.console.getAllLevels()) {
|
|
// world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
|
|
world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
|
|
@@ -1103,6 +1118,7 @@ public final class CraftServer implements Server {
|
|
}
|
|
}
|
|
world.spigotConfig.init(); // Spigot
|
|
+ world.purpurConfig.init(); // Purpur
|
|
}
|
|
|
|
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
|
|
@@ -1120,6 +1136,7 @@ public final class CraftServer implements Server {
|
|
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
|
|
io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
|
|
this.spark.registerCommandBeforePlugins(this); // Paper - spark
|
|
+ org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur
|
|
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
|
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
|
|
|
@@ -1632,6 +1649,58 @@ public final class CraftServer implements Server {
|
|
return true;
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ @Override
|
|
+ public void addFuel(org.bukkit.Material material, int burnTime) {
|
|
+ Preconditions.checkArgument(burnTime > 0, "BurnTime must be greater than 0");
|
|
+
|
|
+ net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material));
|
|
+ MinecraftServer.getServer().fuelValues().values.put(itemStack.getItem(), burnTime);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void removeFuel(org.bukkit.Material material) {
|
|
+ net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material));
|
|
+ MinecraftServer.getServer().fuelValues().values.keySet().removeIf(itemStack::is);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ this.worlds.forEach((name, world) -> world.sendBlockHighlight(location, duration, text, argb));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ this.worlds.forEach((name, world) -> clearBlockHighlights());
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
@Override
|
|
public List<Recipe> getRecipesFor(ItemStack result) {
|
|
Preconditions.checkArgument(result != null, "ItemStack cannot be null");
|
|
@@ -3045,6 +3114,18 @@ public final class CraftServer implements Server {
|
|
}
|
|
// Gale end - Gale configuration - API
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public YamlConfiguration getPurpurConfig() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.config;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Properties getServerProperties() {
|
|
+ return getProperties().properties;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void restart() {
|
|
org.spigotmc.RestartCommand.restart();
|
|
@@ -3338,4 +3419,16 @@ public final class CraftServer implements Server {
|
|
return MinecraftServer.lastTickOversleepTime;
|
|
}
|
|
// Gale end - YAPFA - last tick time - API
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public String getServerName() {
|
|
+ return this.getProperties().serverName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isLagging() {
|
|
+ return getServer().lagging;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index d39a0785f42c72d7b32af5c1c8067b2857f22a48..768ad742ff654a1e80c0d3746bc0ba503f350cd8 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -2384,6 +2384,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
return (this.getHandle().getDragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().getDragonFight());
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public float getLocalDifficultyAt(Location location) {
|
|
+ return getHandle().getCurrentDifficultyAt(io.papermc.paper.util.MCUtil.toBlockPosition(location)).getEffectiveDifficulty();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestAddMarker(getHandle(), io.papermc.paper.util.MCUtil.toBlockPosition(location), text, argb, duration);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestClearPacket(getHandle());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public Collection<GeneratedStructure> getStructures(int x, int z) {
|
|
return this.getStructures(x, z, struct -> true);
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
index 50a2b6e73ba4d8ad65582b2544fa409fd44f36d5..a7cc9af6979860d54c677a7d8d6c1589e97d4841 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
@@ -176,6 +176,14 @@ public class Main {
|
|
.describedAs("Jar file");
|
|
// Paper end
|
|
|
|
+ // Purpur Start
|
|
+ acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings")
|
|
+ .withRequiredArg()
|
|
+ .ofType(File.class)
|
|
+ .defaultsTo(new File("purpur.yml"))
|
|
+ .describedAs("Yml file");
|
|
+ // Purpur end
|
|
+
|
|
// Paper start
|
|
acceptsAll(asList("server-name"), "Name of the server")
|
|
.withRequiredArg()
|
|
@@ -259,7 +267,7 @@ public class Main {
|
|
System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper
|
|
}
|
|
|
|
- if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) {
|
|
+ if (false && Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { // Purpur
|
|
Date buildDate = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(Main.class.getPackage().getImplementationVendor()); // Paper
|
|
|
|
Calendar deadline = Calendar.getInstance();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java
|
|
index 1a2a05160ba51d9c75f1ae6ae61d944d81428722..3beb26ad2ef0fded49a8da8c5dec64f9508c1995 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java
|
|
@@ -16,8 +16,15 @@ import org.bukkit.entity.Bee;
|
|
|
|
public class CraftBeehive extends CraftBlockEntityState<BeehiveBlockEntity> implements Beehive {
|
|
|
|
+ private final List<org.purpurmc.purpur.entity.StoredEntity<Bee>> storage = new ArrayList<>(); // Purpur
|
|
+
|
|
public CraftBeehive(World world, BeehiveBlockEntity tileEntity) {
|
|
super(world, tileEntity);
|
|
+ // Purpur start - load bees to be able to modify them individually
|
|
+ for(BeehiveBlockEntity.BeeData data : tileEntity.getStored()) {
|
|
+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
protected CraftBeehive(CraftBeehive state, Location location) {
|
|
@@ -75,15 +82,54 @@ public class CraftBeehive extends CraftBlockEntityState<BeehiveBlockEntity> impl
|
|
bees.add((Bee) bee.getBukkitEntity());
|
|
}
|
|
}
|
|
-
|
|
+ storage.clear(); // Purpur
|
|
return bees;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public Bee releaseEntity(org.purpurmc.purpur.entity.StoredEntity<Bee> entity) {
|
|
+ ensureNoWorldGeneration();
|
|
+
|
|
+ if(!getEntities().contains(entity)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if(isPlaced()) {
|
|
+ BeehiveBlockEntity beehive = ((BeehiveBlockEntity) this.getTileEntityFromWorld());
|
|
+ BeehiveBlockEntity.BeeData data = ((org.purpurmc.purpur.entity.PurpurStoredBee) entity).getHandle();
|
|
+
|
|
+ List<Entity> list = beehive.releaseBee(getHandle(), data, BeeReleaseStatus.BEE_RELEASED, true);
|
|
+
|
|
+ if (list.size() == 1) {
|
|
+ storage.remove(entity);
|
|
+
|
|
+ return (Bee) list.get(0).getBukkitEntity();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<org.purpurmc.purpur.entity.StoredEntity<Bee>> getEntities() {
|
|
+ return new ArrayList<>(storage);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void addEntity(Bee entity) {
|
|
Preconditions.checkArgument(entity != null, "Entity must not be null");
|
|
|
|
- this.getSnapshot().addOccupant(((CraftBee) entity).getHandle());
|
|
+ int length = this.getSnapshot().getStored().size(); // Purpur
|
|
+ getSnapshot().addOccupant(((CraftBee) entity).getHandle());
|
|
+
|
|
+ // Purpur start - check if new bee was added, and if yes, add to stored bees
|
|
+ List<BeehiveBlockEntity.BeeData> storedBeeData = this.getSnapshot().getStored();
|
|
+ if(length < storedBeeData.size()) {
|
|
+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(storedBeeData.getLast(), this));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -100,6 +146,7 @@ public class CraftBeehive extends CraftBlockEntityState<BeehiveBlockEntity> impl
|
|
@Override
|
|
public void clearEntities() {
|
|
getSnapshot().clearBees();
|
|
+ storage.clear(); // Purpur
|
|
}
|
|
// Paper end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java
|
|
index c1759aeb3e6ad0e4eb66cba3da1b120dd1dce812..1a91bc2e422db0eba65694ac046f1b362c6b0cd6 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java
|
|
@@ -73,7 +73,7 @@ public class CraftConduit extends CraftBlockEntityState<ConduitBlockEntity> impl
|
|
public int getRange() {
|
|
this.ensureNoWorldGeneration();
|
|
ConduitBlockEntity conduit = (ConduitBlockEntity) this.getTileEntityFromWorld();
|
|
- return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks) : 0;
|
|
+ return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks, this.world.getHandle()) : 0; // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
index 4e56018b64d11f76c8da43fd8f85c6de72204e36..aa8212432825db65cf485cd93f734ccd9eefcb5a 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
@@ -21,7 +21,12 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
|
|
|
|
@Override
|
|
public void sendMessage(String message) {
|
|
- this.sendRawMessage(message);
|
|
+ // Purpur start
|
|
+ String[] parts = message.split("\n");
|
|
+ for (String part : parts) {
|
|
+ this.sendRawMessage(part);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -91,7 +96,7 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
|
|
// Paper start
|
|
@Override
|
|
public void sendMessage(final net.kyori.adventure.identity.Identity identity, final net.kyori.adventure.text.Component message, final net.kyori.adventure.audience.MessageType type) {
|
|
- this.sendRawMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message));
|
|
+ this.sendMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
index d657fd2c507a5b215aeab0a5f3e9c2ee892a27c8..985e9ec21c60a1f47973bd5fc53b96a6f9b7d04a 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
@@ -21,12 +21,12 @@ public class CraftEndermite extends CraftMonster implements Endermite {
|
|
|
|
@Override
|
|
public boolean isPlayerSpawned() {
|
|
- return false;
|
|
+ return getHandle().isPlayerSpawned(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void setPlayerSpawned(boolean playerSpawned) {
|
|
- // Nop
|
|
+ getHandle().setPlayerSpawned(playerSpawned); // Purpur
|
|
}
|
|
// Paper start
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
index ddabaed899c755925ad8618b78c33dacaf2126ac..51aee9a468f4ebfa9672fd9ce84883cf080859e3 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
@@ -87,6 +87,23 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
this.entityType = CraftEntityType.minecraftToBukkit(entity.getType());
|
|
}
|
|
|
|
+ // Purpur start - API for any mob to burn daylight
|
|
+ @Override
|
|
+ public boolean isImmuneToFire() {
|
|
+ return getHandle().fireImmune();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(Boolean fireImmune) {
|
|
+ getHandle().immuneToFire = fireImmune;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isInDaylight() {
|
|
+ return getHandle().isSunBurnTick();
|
|
+ }
|
|
+ // Purpur end - API for any mob to burn daylight
|
|
+
|
|
public static <T extends Entity> CraftEntity getEntity(CraftServer server, T entity) {
|
|
Preconditions.checkArgument(entity != null, "Unknown entity");
|
|
|
|
@@ -246,6 +263,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
boolean ignorePassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS);
|
|
// Don't allow teleporting between worlds while keeping passengers
|
|
if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) {
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur
|
|
return false;
|
|
}
|
|
|
|
@@ -1306,4 +1324,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
}
|
|
}
|
|
// Paper end - broadcast hurt animation
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public org.bukkit.entity.Player getRider() {
|
|
+ net.minecraft.world.entity.player.Player rider = getHandle().getRider();
|
|
+ return rider != null ? (org.bukkit.entity.Player) rider.getBukkitEntity() : null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasRider() {
|
|
+ return getHandle().getRider() != null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return getHandle().isRidable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidableInWater() {
|
|
+ return !getHandle().dismountsUnderwater();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
index e345cdbfab44a0f5da80d738798dbb4424b7ab5c..856f12eb276c214f2f57a58a89a4da9eea34db2d 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
@@ -273,6 +273,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
|
@Override
|
|
public void recalculatePermissions() {
|
|
this.perm.recalculatePermissions();
|
|
+ getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
index 63cae1a2e95d8da17c45c4404a8dd0ca6a413c39..966587c2788b5c93be83259ddc962a89cde7cbaa 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
@@ -27,4 +27,17 @@ public class CraftIronGolem extends CraftGolem implements IronGolem {
|
|
public void setPlayerCreated(boolean playerCreated) {
|
|
this.getHandle().setPlayerCreated(playerCreated);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
index 30d62ee4d5cd2ddacb8783b5bbbf475d592b3e02..5c1cda88080850314dac196dbe71ff12e48a8aca 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
@@ -151,4 +151,51 @@ public class CraftItem extends CraftEntity implements Item {
|
|
public String toString() {
|
|
return "CraftItem";
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void setImmuneToCactus(boolean immuneToCactus) {
|
|
+ this.getHandle().immuneToCactus = immuneToCactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToCactus() {
|
|
+ return this.getHandle().immuneToCactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToExplosion(boolean immuneToExplosion) {
|
|
+ this.getHandle().immuneToExplosion = immuneToExplosion;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToExplosion() {
|
|
+ return this.getHandle().immuneToExplosion;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) {
|
|
+ this.getHandle().immuneToFire = (immuneToFire != null && immuneToFire);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(boolean immuneToFire) {
|
|
+ this.setImmuneToFire((Boolean) immuneToFire);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToFire() {
|
|
+ return this.getHandle().immuneToFire;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToLightning(boolean immuneToLightning) {
|
|
+ this.getHandle().immuneToLightning = immuneToLightning;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToLightning() {
|
|
+ return this.getHandle().immuneToLightning;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
index d0c409f4efad289e3e325f44b500fc72589d89d4..051ffc663317fe5a4fafe0750c89fafdece4d316 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
@@ -523,7 +523,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
net.minecraft.server.level.ServerPlayer entityPlayer = killer == null ? null : ((CraftPlayer) killer).getHandle();
|
|
getHandle().lastHurtByPlayer = entityPlayer;
|
|
getHandle().lastHurtByMob = entityPlayer;
|
|
- getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : 100; // 100 value taken from EntityLiving#damageEntity
|
|
+ getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : getHandle().level().purpurConfig.mobLastHurtByPlayerTime; // 100 value taken from EntityLiving#damageEntity // Purpur
|
|
}
|
|
// Paper end
|
|
|
|
@@ -1211,4 +1211,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
return this.getHandle().canUseSlot(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot));
|
|
}
|
|
// Paper end - Expose canUseSlot
|
|
+
|
|
+ // Purpur start - API for any mob to burn daylight
|
|
+ @Override
|
|
+ public boolean shouldBurnInDay() {
|
|
+ return this.getHandle().shouldBurnInDay();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setShouldBurnInDay(final boolean shouldBurnInDay) {
|
|
+ this.getHandle().setShouldBurnInDay(shouldBurnInDay);
|
|
+ }
|
|
+ // Purpur end - API for any mob to burn daylight
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
index 351f42842b780d053cd2e5bad9ae299449141b10..4860574e7fad7a9527dda599703c573c5b4b234b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
@@ -90,4 +90,16 @@ public class CraftLlama extends CraftChestedHorse implements Llama, com.destroys
|
|
return this.getHandle().caravanTail == null ? null : (Llama) this.getHandle().caravanTail.getBukkitEntity();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean shouldJoinCaravan() {
|
|
+ return getHandle().shouldJoinCaravan;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setShouldJoinCaravan(boolean shouldJoinCaravan) {
|
|
+ getHandle().shouldJoinCaravan = shouldJoinCaravan;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
index d4e497961578bb693275cdf95915b60b2cc76eb7..63065a22ff359c142bab23fccacfd5ebd86f81a5 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
@@ -583,10 +583,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
|
|
@Override
|
|
public void setPlayerListName(String name) {
|
|
+ // Purpur start
|
|
+ setPlayerListName(name, false);
|
|
+ }
|
|
+ public void setPlayerListName(String name, boolean useMM) {
|
|
+ // Purpur end
|
|
if (name == null) {
|
|
name = this.getName();
|
|
}
|
|
- this.getHandle().listName = name.equals(this.getName()) ? null : CraftChatMessage.fromStringOrNull(name);
|
|
+ this.getHandle().listName = name.equals(this.getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur
|
|
if (this.getHandle().connection == null) return; // Paper - Updates are possible before the player has fully joined
|
|
for (ServerPlayer player : (List<ServerPlayer>) this.server.getHandle().players) {
|
|
if (player.getBukkitEntity().canSee(this)) {
|
|
@@ -1435,6 +1440,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
// Paper start - Teleport passenger API
|
|
// Don't allow teleporting between worlds while keeping passengers
|
|
if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) {
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur start
|
|
return false;
|
|
}
|
|
|
|
@@ -1456,6 +1462,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
}
|
|
|
|
if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API
|
|
+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur start
|
|
return false;
|
|
}
|
|
|
|
@@ -2769,6 +2776,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
return this.getHandle().getAbilities().walkingSpeed * 2f;
|
|
}
|
|
|
|
+ // Purpur start - OfflinePlayer API
|
|
+ @Override
|
|
+ public boolean teleportOffline(@NotNull Location destination) {
|
|
+ return this.teleport(destination);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination, PlayerTeleportEvent.TeleportCause cause) {
|
|
+ return this.teleport(destination, cause);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination) {
|
|
+ return this.teleportAsync(destination);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination, PlayerTeleportEvent.TeleportCause cause) {
|
|
+ return this.teleportAsync(destination, cause);
|
|
+ }
|
|
+ // Purpur end - OfflinePlayer API
|
|
+
|
|
private void validateSpeed(float value) {
|
|
Preconditions.checkArgument(value <= 1f && value >= -1f, "Speed value (%s) need to be between -1f and 1f", value);
|
|
}
|
|
@@ -3580,4 +3609,70 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
|
|
}
|
|
// Paper end - entity effect API
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean usesPurpurClient() {
|
|
+ return getHandle().purpurClient;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isAfk() {
|
|
+ return getHandle().isAfk();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setAfk(boolean setAfk) {
|
|
+ getHandle().setAfk(setAfk);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void resetIdleTimer() {
|
|
+ getHandle().resetLastActionTime();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestAddMarkerDebugPayload(io.papermc.paper.util.MCUtil.toBlockPosition(location), argb, text, duration)));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestClearMarkersDebugPayload()));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendDeathScreen(net.kyori.adventure.text.Component message) {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message)));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
index 4ce2373ff71c3c1b8951646e057587a3ab09e145..4f7f6cf6ca24406570d2d29dc63dc89401119961 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
@@ -28,4 +28,17 @@ public class CraftSnowman extends CraftGolem implements Snowman, com.destroystok
|
|
public String toString() {
|
|
return "CraftSnowman";
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
index 8e895d6f84f7d84b219f2424909dd42e5f08dec4..53dcce0701d713c5dd097340a91b8be4806de4b8 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
@@ -375,4 +375,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
|
|
getHandle().getGossips().gossips.clear();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isLobotomized() {
|
|
+ return getHandle().isLobotomized();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
index 7881c6253c1d652c0c0d54a9a8accdf0a1ff0f3e..da6ccb2a38df76770821a1a2203e54e722f541ca 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
@@ -99,4 +99,17 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok
|
|
this.getHandle().makeInvulnerable();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
index ecd33b4add46acbe4e4f8879c0601220423d66ca..1506a8c0fa490726eb4a4ae14f3aa194dc81d9ab 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
@@ -146,4 +146,16 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf {
|
|
return this.getKey().hashCode();
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRabid() {
|
|
+ return getHandle().isRabid();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setRabid(boolean isRabid) {
|
|
+ getHandle().setRabid(isRabid);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
index e37aaf77f94b97b736cc20ef070cefdff0400188..763d06265c7d0000e4c641c3aaba785bb0efb23e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -602,6 +602,15 @@ public class CraftEventFactory {
|
|
// Paper end
|
|
craftServer.getPluginManager().callEvent(event);
|
|
|
|
+ // Purpur start
|
|
+ if (who != null) {
|
|
+ switch (action) {
|
|
+ case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> who.processClick(InteractionHand.MAIN_HAND);
|
|
+ case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> who.processClick(InteractionHand.OFF_HAND);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return event;
|
|
}
|
|
|
|
@@ -1131,7 +1140,7 @@ public class CraftEventFactory {
|
|
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
|
|
} else if (source.getDirectBlock() != null) {
|
|
DamageCause cause;
|
|
- if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) {
|
|
+ if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL) || source.isStonecutter()) { // Purpur
|
|
cause = DamageCause.CONTACT;
|
|
} else if (source.is(DamageTypes.HOT_FLOOR)) {
|
|
cause = DamageCause.HOT_FLOOR;
|
|
@@ -1191,6 +1200,7 @@ public class CraftEventFactory {
|
|
EntityDamageEvent event;
|
|
if (damager != null) {
|
|
event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions, critical);
|
|
+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur
|
|
} else {
|
|
event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions);
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
index 6d3f9d5dab6c9a2860ae31cae24310aa2d62da7c..4f29c579f94efe59a8c78520d75676fc4875e2f0 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
@@ -145,8 +145,19 @@ public class CraftContainer extends AbstractContainerMenu {
|
|
case PLAYER:
|
|
case CHEST:
|
|
case ENDER_CHEST:
|
|
+ // Purpur start
|
|
+ this.delegate = new ChestMenu(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? net.minecraft.world.inventory.MenuType.GENERIC_9x6 : net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ break;
|
|
case BARREL:
|
|
- this.delegate = new ChestMenu(net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ this.delegate = new ChestMenu(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> net.minecraft.world.inventory.MenuType.GENERIC_9x6;
|
|
+ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5;
|
|
+ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4;
|
|
+ case 2 -> net.minecraft.world.inventory.MenuType.GENERIC_9x2;
|
|
+ case 1 -> net.minecraft.world.inventory.MenuType.GENERIC_9x1;
|
|
+ default -> net.minecraft.world.inventory.MenuType.GENERIC_9x3;
|
|
+ }, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ // Purpur end
|
|
break;
|
|
case DISPENSER:
|
|
case DROPPER:
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
index c6159c70f7a37b9bffe268b91905ce848d1d2927..d02adaaa6fbdc1c0eff44cb4a1f1642f9575a821 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
@@ -84,7 +84,7 @@ public class CraftInventory implements Inventory {
|
|
|
|
@Override
|
|
public void setContents(ItemStack[] items) {
|
|
- Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize());
|
|
+ // Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); // Purpur
|
|
|
|
for (int i = 0; i < this.getSize(); i++) {
|
|
if (i >= items.length) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
index 792cb6adf0c7a6335cc5985fce8bed2e0f1149af..5734c5caffda79383ae30df20c3defb51b87f39e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
@@ -19,6 +19,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn
|
|
private int repairCost;
|
|
private int repairCostAmount;
|
|
private int maximumRepairCost;
|
|
+ // Purpur start - Anvil API
|
|
+ private boolean bypassCost;
|
|
+ private boolean canDoUnsafeEnchants;
|
|
+ // Purpur end - Anvil API
|
|
|
|
public CraftInventoryAnvil(Location location, Container inventory, Container resultInventory) {
|
|
super(inventory, resultInventory);
|
|
@@ -27,6 +31,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn
|
|
this.repairCost = CraftInventoryAnvil.DEFAULT_REPAIR_COST;
|
|
this.repairCostAmount = CraftInventoryAnvil.DEFAULT_REPAIR_COST_AMOUNT;
|
|
this.maximumRepairCost = CraftInventoryAnvil.DEFAULT_MAXIMUM_REPAIR_COST;
|
|
+ // Purpur start - Anvil API
|
|
+ this.bypassCost = false;
|
|
+ this.canDoUnsafeEnchants = false;
|
|
+ // Purpur end - Anvil API
|
|
}
|
|
|
|
@Override
|
|
@@ -113,4 +121,30 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn
|
|
consumer.accept(cav);
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start - Anvil API
|
|
+ @Override
|
|
+ public boolean canBypassCost() {
|
|
+ this.syncWithArbitraryViewValue((cav) -> this.bypassCost = cav.canBypassCost());
|
|
+ return this.bypassCost;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setBypassCost(boolean bypassCost) {
|
|
+ this.bypassCost = bypassCost;
|
|
+ this.syncViews((cav) -> cav.setBypassCost(bypassCost));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canDoUnsafeEnchants() {
|
|
+ this.syncWithArbitraryViewValue((cav) -> this.canDoUnsafeEnchants = cav.canDoUnsafeEnchants());
|
|
+ return this.canDoUnsafeEnchants;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) {
|
|
+ this.canDoUnsafeEnchants = canDoUnsafeEnchants;
|
|
+ this.syncViews((cav) -> cav.setDoUnsafeEnchants(canDoUnsafeEnchants));
|
|
+ }
|
|
+ // Purpur end - Anvil API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
index 78975412da0f0c2b802bfce6d30d56b26d8023e2..4ec6a07796023aab2f8f84f131f48108c235c852 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
@@ -658,4 +658,285 @@ public final class CraftItemStack extends ItemStack {
|
|
}
|
|
|
|
// Paper end - data component API
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public String getDisplayName() {
|
|
+ return getItemMeta().getDisplayName();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setDisplayName(String name) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ itemMeta.setDisplayName(name);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasDisplayName() {
|
|
+ return hasItemMeta() && getItemMeta().hasDisplayName();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getLocalizedName() {
|
|
+ return getItemMeta().getLocalizedName();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setLocalizedName(String name) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ itemMeta.setLocalizedName(name);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasLocalizedName() {
|
|
+ return hasItemMeta() && getItemMeta().hasLocalizedName();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasLore() {
|
|
+ return hasItemMeta() && getItemMeta().hasLore();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasEnchant(Enchantment ench) {
|
|
+ return hasItemMeta() && getItemMeta().hasEnchant(ench);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getEnchantLevel(Enchantment ench) {
|
|
+ return getItemMeta().getEnchantLevel(ench);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Map<Enchantment, Integer> getEnchants() {
|
|
+ return getItemMeta().getEnchants();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.addEnchant(ench, level, ignoreLevelRestriction);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean removeEnchant(Enchantment ench) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.removeEnchant(ench);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasEnchants() {
|
|
+ return hasItemMeta() && getItemMeta().hasEnchants();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasConflictingEnchant(Enchantment ench) {
|
|
+ return hasItemMeta() && getItemMeta().hasConflictingEnchant(ench);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setCustomModelData(Integer data) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ itemMeta.setCustomModelData(data);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getCustomModelData() {
|
|
+ return getItemMeta().getCustomModelData();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasCustomModelData() {
|
|
+ return hasItemMeta() && getItemMeta().hasCustomModelData();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasBlockData() {
|
|
+ return hasItemMeta() && ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).hasBlockData();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public org.bukkit.block.data.BlockData getBlockData(Material material) {
|
|
+ return ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).getBlockData(material);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setBlockData(org.bukkit.block.data.BlockData blockData) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ ((org.bukkit.inventory.meta.BlockDataMeta) itemMeta).setBlockData(blockData);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getRepairCost() {
|
|
+ return ((org.bukkit.inventory.meta.Repairable) getItemMeta()).getRepairCost();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setRepairCost(int cost) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ ((org.bukkit.inventory.meta.Repairable) itemMeta).setRepairCost(cost);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasRepairCost() {
|
|
+ return hasItemMeta() && ((org.bukkit.inventory.meta.Repairable) getItemMeta()).hasRepairCost();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isUnbreakable() {
|
|
+ return hasItemMeta() && getItemMeta().isUnbreakable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setUnbreakable(boolean unbreakable) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ itemMeta.setUnbreakable(unbreakable);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasAttributeModifiers() {
|
|
+ return hasItemMeta() && getItemMeta().hasAttributeModifiers();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public com.google.common.collect.Multimap<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getAttributeModifiers() {
|
|
+ return getItemMeta().getAttributeModifiers();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public com.google.common.collect.Multimap<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getAttributeModifiers(org.bukkit.inventory.EquipmentSlot slot) {
|
|
+ return getItemMeta().getAttributeModifiers(slot);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Collection<org.bukkit.attribute.AttributeModifier> getAttributeModifiers(org.bukkit.attribute.Attribute attribute) {
|
|
+ return getItemMeta().getAttributeModifiers(attribute);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean addAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.addAttributeModifier(attribute, modifier);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setAttributeModifiers(com.google.common.collect.Multimap<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> attributeModifiers) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ itemMeta.setAttributeModifiers(attributeModifiers);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.removeAttributeModifier(attribute);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean removeAttributeModifier(org.bukkit.inventory.EquipmentSlot slot) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.removeAttributeModifier(slot);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ boolean result = itemMeta.removeAttributeModifier(attribute, modifier);
|
|
+ setItemMeta(itemMeta);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasDamage() {
|
|
+ return hasItemMeta() && ((org.bukkit.inventory.meta.Damageable) getItemMeta()).hasDamage();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getDamage() {
|
|
+ return ((org.bukkit.inventory.meta.Damageable) getItemMeta()).getDamage();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setDamage(int damage) {
|
|
+ ItemMeta itemMeta = getItemMeta();
|
|
+ ((org.bukkit.inventory.meta.Damageable) itemMeta).setDamage(damage);
|
|
+ setItemMeta(itemMeta);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void repair() {
|
|
+ repair(1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean damage() {
|
|
+ return damage(1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void repair(int amount) {
|
|
+ damage(-amount);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean damage(int amount) {
|
|
+ return damage(amount, false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean damage(int amount, boolean ignoreUnbreaking) {
|
|
+ org.bukkit.inventory.meta.Damageable damageable = (org.bukkit.inventory.meta.Damageable) getItemMeta();
|
|
+ if (amount > 0) {
|
|
+ int unbreaking = getEnchantLevel(Enchantment.UNBREAKING);
|
|
+ int reduce = 0;
|
|
+ for (int i = 0; unbreaking > 0 && i < amount; ++i) {
|
|
+ if (reduceDamage(java.util.concurrent.ThreadLocalRandom.current(), unbreaking)) {
|
|
+ ++reduce;
|
|
+ }
|
|
+ }
|
|
+ amount -= reduce;
|
|
+ if (amount <= 0) {
|
|
+ return isBroke(damageable.getDamage());
|
|
+ }
|
|
+ }
|
|
+ int damage = damageable.getDamage() + amount;
|
|
+ damageable.setDamage(damage);
|
|
+ setItemMeta((ItemMeta) damageable);
|
|
+ return isBroke(damage);
|
|
+ }
|
|
+
|
|
+ private boolean isBroke(int damage) {
|
|
+ if (damage > getType().getMaxDurability()) {
|
|
+ if (getAmount() > 0) {
|
|
+ // ensure it "breaks"
|
|
+ setAmount(0);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ private boolean reduceDamage(java.util.Random random, int unbreaking) {
|
|
+ if (getType().isArmor()) {
|
|
+ return random.nextFloat() < 0.6F;
|
|
+ }
|
|
+ return random.nextInt(unbreaking + 1) > 0;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
index 3592091c6d1371224e82e1f95b003951ad2f8779..4fdc78a9c74b42a8894030221e0452493d68020e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
@@ -38,6 +38,7 @@ public interface CraftRecipe extends Recipe {
|
|
stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat)));
|
|
} else if (bukkit instanceof RecipeChoice.ExactChoice) {
|
|
stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> CraftItemStack.asNMSCopy(mat)).toList());
|
|
+ stack.predicate = ((RecipeChoice.ExactChoice) bukkit).getPredicate(); // Purpur
|
|
// Paper start - support "empty" choices - legacy method that spigot might incorrectly call
|
|
// Their impl of Ingredient.of() will error, ingredients need at least one entry.
|
|
// Callers running into this exception may have passed an incorrect empty() recipe choice to a non-empty slot or
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
|
|
index f86c95a13dff012de5db3e41ac261e9e8d44d9f3..1db0b790d824e419bb5fb6ab1f3003e120f9763b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
|
|
@@ -75,4 +75,26 @@ public class CraftAnvilView extends CraftInventoryView<AnvilMenu, AnvilInventory
|
|
this.setMaximumRepairCost(legacy.getMaximumRepairCost());
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start - Anvil API
|
|
+ @Override
|
|
+ public boolean canBypassCost() {
|
|
+ return this.container.bypassCost;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setBypassCost(boolean bypassCost) {
|
|
+ this.container.bypassCost = bypassCost;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canDoUnsafeEnchants() {
|
|
+ return this.container.canDoUnsafeEnchants;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) {
|
|
+ this.container.canDoUnsafeEnchants = canDoUnsafeEnchants;
|
|
+ }
|
|
+ // Purpur end - Anvil API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
|
|
index db8d8e2a07296d62c3097f02b03319e2e1ba9394..e5c30847297e056782084d81fb9300f98d4a8f75 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
|
|
@@ -708,4 +708,32 @@ public class MaterialRerouting {
|
|
meta.setCanPlaceOn(materials);
|
|
}
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
|
|
+ public static void addFuel(Server server, Material material, int burnTime) {
|
|
+ server.addFuel(material, burnTime);
|
|
+ }
|
|
+
|
|
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
|
|
+ public static void removeFuel(Server server, Material material) {
|
|
+ server.removeFuel(material);
|
|
+ }
|
|
+
|
|
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
|
|
+ @RerouteStatic("org/bukkit/Bukkit")
|
|
+ public static void addFuel(Material material, int burnTime) {
|
|
+ Bukkit.addFuel(material, burnTime);
|
|
+ }
|
|
+
|
|
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
|
|
+ @RerouteStatic("org/bukkit/Bukkit")
|
|
+ public static void removeFuel(Material material) {
|
|
+ Bukkit.removeFuel(material);
|
|
+ }
|
|
+
|
|
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/commit/607d909efba516893072b782c0393c53d048210e)
|
|
+ public static BlockData getBlockData(ItemStack itemStack, Material material) {
|
|
+ return itemStack.getBlockData(MaterialRerouting.transformToBlockType(material));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
|
|
index cf0920e5f84b35647882fb963e9972af4e8427e0..e30c851acf49a425cd4fd409a6d5bbb2ff836e0e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
|
|
@@ -49,4 +49,10 @@ public class CraftMapRenderer extends MapRenderer {
|
|
}
|
|
}
|
|
|
|
+ // Purpur - start
|
|
+ @Override
|
|
+ public boolean isExplorerMap() {
|
|
+ return this.worldMap.isExplorerMap;
|
|
+ }
|
|
+ // Purpur - end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
index b3169c551b8410f5861f9db0543c785439ecba7c..916ca3f1f39e10158fc7c10141785fff49ed1501 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
@@ -23,7 +23,15 @@ public final class CommandPermissions {
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands);
|
|
- DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands);
|
|
+ // Purpur start
|
|
+ Permission gamemodeVanilla = DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode", PermissionDefault.OP, commands);
|
|
+ for (net.minecraft.world.level.GameType gametype : net.minecraft.world.level.GameType.values()) {
|
|
+ Permission gamemodeSelf = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName(), "Allows the user to set " + gametype.getName() + " gamemode for self", PermissionDefault.OP);
|
|
+ Permission gamemodeOther = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName() + ".other", "Allows the user to set " + gametype.getName() + " gamemode for other players", PermissionDefault.OP);
|
|
+ gamemodeSelf.addParent(gamemodeOther, true);
|
|
+ gamemodeVanilla.addParent(gamemodeSelf, true);
|
|
+ }
|
|
+ // Purpur end
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "experience", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); // Paper - wrong permission; redirects are de-redirected and the root literal name is used, so xp -> experience
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands);
|
|
diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4401cd919951e92f613e0899f243b0105dd852b7
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
@@ -0,0 +1,580 @@
|
|
+package org.purpurmc.purpur;
|
|
+
|
|
+import com.google.common.base.Throwables;
|
|
+import com.google.common.collect.ImmutableMap;
|
|
+import com.mojang.datafixers.util.Pair;
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.minecraft.core.Registry;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.effect.MobEffect;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.entity.EntityDimensions;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.food.FoodProperties;
|
|
+import net.minecraft.world.food.Foods;
|
|
+import net.minecraft.world.item.enchantment.Enchantment;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import org.bukkit.configuration.InvalidConfigurationException;
|
|
+import org.bukkit.configuration.file.YamlConfiguration;
|
|
+import org.purpurmc.purpur.command.PurpurCommand;
|
|
+import org.purpurmc.purpur.task.TPSBarTask;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.IOException;
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
+import java.lang.reflect.Method;
|
|
+import java.lang.reflect.Modifier;
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collections;
|
|
+import java.util.HashMap;
|
|
+import java.util.HashSet;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.Set;
|
|
+import java.util.logging.Level;
|
|
+
|
|
+@SuppressWarnings("unused")
|
|
+public class PurpurConfig {
|
|
+ private static final String HEADER = "This is the main configuration file for Purpur.\n"
|
|
+ + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
|
|
+ + "with caution, and make sure you know what each option does before configuring.\n"
|
|
+ + "\n"
|
|
+ + "If you need help with the configuration or have any questions related to Purpur,\n"
|
|
+ + "join us in our Discord guild.\n"
|
|
+ + "\n"
|
|
+ + "Website: https://purpurmc.org \n"
|
|
+ + "Docs: https://purpurmc.org/docs \n";
|
|
+ private static File CONFIG_FILE;
|
|
+ public static YamlConfiguration config;
|
|
+
|
|
+ private static Map<String, Command> commands;
|
|
+
|
|
+ public static int version;
|
|
+ static boolean verbose;
|
|
+
|
|
+ public static void init(File configFile) {
|
|
+ CONFIG_FILE = configFile;
|
|
+ config = new YamlConfiguration();
|
|
+ try {
|
|
+ config.load(CONFIG_FILE);
|
|
+ } catch (IOException ignore) {
|
|
+ } catch (InvalidConfigurationException ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex);
|
|
+ throw Throwables.propagate(ex);
|
|
+ }
|
|
+ config.options().header(HEADER);
|
|
+ config.options().copyDefaults(true);
|
|
+ verbose = getBoolean("verbose", false);
|
|
+
|
|
+ commands = new HashMap<>();
|
|
+ commands.put("purpur", new PurpurCommand("purpur"));
|
|
+
|
|
+ version = getInt("config-version", 37);
|
|
+ set("config-version", 37);
|
|
+
|
|
+ readConfig(PurpurConfig.class, null);
|
|
+
|
|
+ Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache);
|
|
+ }
|
|
+
|
|
+ protected static void log(String s) {
|
|
+ if (verbose) {
|
|
+ log(Level.INFO, s);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected static void log(Level level, String s) {
|
|
+ Bukkit.getLogger().log(level, s);
|
|
+ }
|
|
+
|
|
+ public static void registerCommands() {
|
|
+ for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
|
+ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ static void readConfig(Class<?> clazz, Object instance) {
|
|
+ for (Method method : clazz.getDeclaredMethods()) {
|
|
+ if (Modifier.isPrivate(method.getModifiers())) {
|
|
+ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
|
|
+ try {
|
|
+ method.setAccessible(true);
|
|
+ method.invoke(instance);
|
|
+ } catch (InvocationTargetException ex) {
|
|
+ throw Throwables.propagate(ex.getCause());
|
|
+ } catch (Exception ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ config.save(CONFIG_FILE);
|
|
+ } catch (IOException ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void set(String path, Object val) {
|
|
+ config.addDefault(path, val);
|
|
+ config.set(path, val);
|
|
+ }
|
|
+
|
|
+ private static String getString(String path, String def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getString(path, config.getString(path));
|
|
+ }
|
|
+
|
|
+ private static boolean getBoolean(String path, boolean def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getBoolean(path, config.getBoolean(path));
|
|
+ }
|
|
+
|
|
+ private static double getDouble(String path, double def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getDouble(path, config.getDouble(path));
|
|
+ }
|
|
+
|
|
+ private static int getInt(String path, int def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getInt(path, config.getInt(path));
|
|
+ }
|
|
+
|
|
+ private static <T> List<?> getList(String path, T def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getList(path, config.getList(path));
|
|
+ }
|
|
+
|
|
+ static Map<String, Object> getMap(String path, Map<String, Object> def) {
|
|
+ if (def != null && config.getConfigurationSection(path) == null) {
|
|
+ config.addDefault(path, def);
|
|
+ return def;
|
|
+ }
|
|
+ return toMap(config.getConfigurationSection(path));
|
|
+ }
|
|
+
|
|
+ private static Map<String, Object> toMap(ConfigurationSection section) {
|
|
+ ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ Object obj = section.get(key);
|
|
+ if (obj != null) {
|
|
+ builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return builder.build();
|
|
+ }
|
|
+
|
|
+ public static String cannotRideMob = "<red>You cannot mount that mob";
|
|
+ public static String afkBroadcastAway = "<yellow><italic>%s is now AFK";
|
|
+ public static String afkBroadcastBack = "<yellow><italic>%s is no longer AFK";
|
|
+ public static boolean afkBroadcastUseDisplayName = false;
|
|
+ public static String afkTabListPrefix = "[AFK] ";
|
|
+ public static String afkTabListSuffix = "";
|
|
+ public static String creditsCommandOutput = "<green>%s has been shown the end credits";
|
|
+ public static String demoCommandOutput = "<green>%s has been shown the demo screen";
|
|
+ public static String pingCommandOutput = "<green>%s's ping is %sms";
|
|
+ public static String ramCommandOutput = "<green>Ram Usage: <used>/<xmx> (<percent>)";
|
|
+ public static String rambarCommandOutput = "<green>Rambar toggled <onoff> for <target>";
|
|
+ public static String tpsbarCommandOutput = "<green>Tpsbar toggled <onoff> for <target>";
|
|
+ public static String dontRunWithScissors = "<red><italic>Don't run with scissors!";
|
|
+ public static String uptimeCommandOutput = "<green>Server uptime is <uptime>";
|
|
+ public static String unverifiedUsername = "default";
|
|
+ public static String sleepSkippingNight = "default";
|
|
+ public static String sleepingPlayersPercent = "default";
|
|
+ public static String sleepNotPossible = "default";
|
|
+ private static void messages() {
|
|
+ cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
|
|
+ afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway);
|
|
+ afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack);
|
|
+ afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName);
|
|
+ afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix)));
|
|
+ afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix)));
|
|
+ creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput);
|
|
+ demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput);
|
|
+ pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput);
|
|
+ ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput);
|
|
+ rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput);
|
|
+ tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput);
|
|
+ dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors);
|
|
+ uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput);
|
|
+ unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername);
|
|
+ sleepSkippingNight = getString("settings.messages.sleep-skipping-night", sleepSkippingNight);
|
|
+ sleepingPlayersPercent = getString("settings.messages.sleeping-players-percent", sleepingPlayersPercent);
|
|
+ sleepNotPossible = getString("settings.messages.sleep-not-possible", sleepNotPossible);
|
|
+ }
|
|
+
|
|
+ public static String deathMsgRunWithScissors = "<player> slipped and fell on their shears";
|
|
+ public static String deathMsgStonecutter = "<player> has sawed themself in half";
|
|
+ private static void deathMessages() {
|
|
+ deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors);
|
|
+ deathMsgStonecutter = getString("settings.messages.death-message.stonecutter", deathMsgStonecutter);
|
|
+ }
|
|
+
|
|
+ public static boolean advancementOnlyBroadcastToAffectedPlayer = false;
|
|
+ public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false;
|
|
+ private static void broadcastSettings() {
|
|
+ if (version < 13) {
|
|
+ boolean oldValue = getBoolean("settings.advancement.only-broadcast-to-affected-player", false);
|
|
+ set("settings.broadcasts.advancement.only-broadcast-to-affected-player", oldValue);
|
|
+ set("settings.advancement.only-broadcast-to-affected-player", null);
|
|
+ }
|
|
+ advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer);
|
|
+ deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer);
|
|
+ }
|
|
+
|
|
+ public static double laggingThreshold = 19.0D;
|
|
+ private static void tickLoopSettings() {
|
|
+ laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold);
|
|
+ }
|
|
+
|
|
+ public static boolean disableGiveCommandDrops = false;
|
|
+ private static void disableGiveCommandDrops() {
|
|
+ disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops);
|
|
+ }
|
|
+
|
|
+ public static String commandRamBarTitle = "<gray>Ram<yellow>:</yellow> <used>/<xmx> (<percent>)";
|
|
+ public static BossBar.Overlay commandRamBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
|
|
+ public static BossBar.Color commandRamBarProgressColorGood = BossBar.Color.GREEN;
|
|
+ public static BossBar.Color commandRamBarProgressColorMedium = BossBar.Color.YELLOW;
|
|
+ public static BossBar.Color commandRamBarProgressColorLow = BossBar.Color.RED;
|
|
+ public static String commandRamBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
|
|
+ public static String commandRamBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
|
|
+ public static String commandRamBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
|
|
+ public static int commandRamBarTickInterval = 20;
|
|
+ public static String commandTPSBarTitle = "<gray>TPS<yellow>:</yellow> <tps> MSPT<yellow>:</yellow> <mspt> Ping<yellow>:</yellow> <ping>ms";
|
|
+ public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
|
|
+ public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT;
|
|
+ public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN;
|
|
+ public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW;
|
|
+ public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED;
|
|
+ public static String commandTPSBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
|
|
+ public static String commandTPSBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
|
|
+ public static String commandTPSBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
|
|
+ public static int commandTPSBarTickInterval = 20;
|
|
+ public static String commandCompassBarTitle = "S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 ";
|
|
+ public static BossBar.Overlay commandCompassBarProgressOverlay = BossBar.Overlay.PROGRESS;
|
|
+ public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE;
|
|
+ public static float commandCompassBarProgressPercent = 1.0F;
|
|
+ public static int commandCompassBarTickInterval = 5;
|
|
+ public static boolean commandGamemodeRequiresPermission = false;
|
|
+ public static boolean hideHiddenPlayersFromEntitySelector = false;
|
|
+ public static String uptimeFormat = "<days><hours><minutes><seconds>";
|
|
+ public static String uptimeDay = "%02d day, ";
|
|
+ public static String uptimeDays = "%02d days, ";
|
|
+ public static String uptimeHour = "%02d hour, ";
|
|
+ public static String uptimeHours = "%02d hours, ";
|
|
+ public static String uptimeMinute = "%02d minute, and ";
|
|
+ public static String uptimeMinutes = "%02d minutes, and ";
|
|
+ public static String uptimeSecond = "%02d second";
|
|
+ public static String uptimeSeconds = "%02d seconds";
|
|
+ private static void commandSettings() {
|
|
+ commandRamBarTitle = getString("settings.command.rambar.title", commandRamBarTitle);
|
|
+ commandRamBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.rambar.overlay", commandRamBarProgressOverlay.name()));
|
|
+ commandRamBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.good", commandRamBarProgressColorGood.name()));
|
|
+ commandRamBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.medium", commandRamBarProgressColorMedium.name()));
|
|
+ commandRamBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.low", commandRamBarProgressColorLow.name()));
|
|
+ commandRamBarTextColorGood = getString("settings.command.rambar.text-color.good", commandRamBarTextColorGood);
|
|
+ commandRamBarTextColorMedium = getString("settings.command.rambar.text-color.medium", commandRamBarTextColorMedium);
|
|
+ commandRamBarTextColorLow = getString("settings.command.rambar.text-color.low", commandRamBarTextColorLow);
|
|
+ commandRamBarTickInterval = getInt("settings.command.rambar.tick-interval", commandRamBarTickInterval);
|
|
+
|
|
+ commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle);
|
|
+ commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name()));
|
|
+ commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name()));
|
|
+ commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name()));
|
|
+ commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name()));
|
|
+ commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name()));
|
|
+ commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood);
|
|
+ commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium);
|
|
+ commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow);
|
|
+ commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval);
|
|
+
|
|
+ commandCompassBarTitle = getString("settings.command.compass.title", commandCompassBarTitle);
|
|
+ commandCompassBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.compass.overlay", commandCompassBarProgressOverlay.name()));
|
|
+ commandCompassBarProgressColor = BossBar.Color.valueOf(getString("settings.command.compass.progress-color", commandCompassBarProgressColor.name()));
|
|
+ commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent);
|
|
+ commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval);
|
|
+
|
|
+ commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission);
|
|
+ hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector);
|
|
+ uptimeFormat = getString("settings.command.uptime.format", uptimeFormat);
|
|
+ uptimeDay = getString("settings.command.uptime.day", uptimeDay);
|
|
+ uptimeDays = getString("settings.command.uptime.days", uptimeDays);
|
|
+ uptimeHour = getString("settings.command.uptime.hour", uptimeHour);
|
|
+ uptimeHours = getString("settings.command.uptime.hours", uptimeHours);
|
|
+ uptimeMinute = getString("settings.command.uptime.minute", uptimeMinute);
|
|
+ uptimeMinutes = getString("settings.command.uptime.minutes", uptimeMinutes);
|
|
+ uptimeSecond = getString("settings.command.uptime.second", uptimeSecond);
|
|
+ uptimeSeconds = getString("settings.command.uptime.seconds", uptimeSeconds);
|
|
+ }
|
|
+
|
|
+ public static int barrelRows = 3;
|
|
+ public static boolean enderChestSixRows = false;
|
|
+ public static boolean enderChestPermissionRows = false;
|
|
+ public static boolean cryingObsidianValidForPortalFrame = false;
|
|
+ public static int beeInsideBeeHive = 3;
|
|
+ public static boolean anvilCumulativeCost = true;
|
|
+ public static int lightningRodRange = 128;
|
|
+ public static Set<Enchantment> grindstoneIgnoredEnchants = new HashSet<>();
|
|
+ public static boolean grindstoneRemoveAttributes = false;
|
|
+ public static boolean grindstoneRemoveDisplay = false;
|
|
+ public static int caveVinesMaxGrowthAge = 25;
|
|
+ public static int kelpMaxGrowthAge = 25;
|
|
+ public static int twistingVinesMaxGrowthAge = 25;
|
|
+ public static int weepingVinesMaxGrowthAge = 25;
|
|
+ public static boolean magmaBlockReverseBubbleColumnFlow = false;
|
|
+ public static boolean soulSandBlockReverseBubbleColumnFlow = false;
|
|
+ private static void blockSettings() {
|
|
+ if (version < 3) {
|
|
+ boolean oldValue = getBoolean("settings.barrel.packed-barrels", true);
|
|
+ set("settings.blocks.barrel.six-rows", oldValue);
|
|
+ set("settings.packed-barrels", null);
|
|
+ oldValue = getBoolean("settings.large-ender-chests", true);
|
|
+ set("settings.blocks.ender_chest.six-rows", oldValue);
|
|
+ set("settings.large-ender-chests", null);
|
|
+ }
|
|
+ if (version < 20) {
|
|
+ boolean oldValue = getBoolean("settings.blocks.barrel.six-rows", false);
|
|
+ set("settings.blocks.barrel.rows", oldValue ? 6 : 3);
|
|
+ set("settings.blocks.barrel.six-rows", null);
|
|
+ }
|
|
+ barrelRows = getInt("settings.blocks.barrel.rows", barrelRows);
|
|
+ if (barrelRows < 1 || barrelRows > 6) {
|
|
+ Bukkit.getLogger().severe("settings.blocks.barrel.rows must be 1-6, resetting to default");
|
|
+ barrelRows = 3;
|
|
+ }
|
|
+ org.bukkit.event.inventory.InventoryType.BARREL.setDefaultSize(switch (barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ });
|
|
+ enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows);
|
|
+ org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27);
|
|
+ enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows);
|
|
+ cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame);
|
|
+ beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive);
|
|
+ anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost);
|
|
+ lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange);
|
|
+ ArrayList<String> defaultCurses = new ArrayList<>(){{
|
|
+ add("minecraft:binding_curse");
|
|
+ add("minecraft:vanishing_curse");
|
|
+ }};
|
|
+ if (version < 24 && !getBoolean("settings.blocks.grindstone.ignore-curses", true)) {
|
|
+ defaultCurses.clear();
|
|
+ }
|
|
+ getList("settings.blocks.grindstone.ignored-enchants", defaultCurses).forEach(key -> {
|
|
+ Registry<Enchantment> registry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT);
|
|
+ Enchantment enchantment = registry.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (enchantment == null) return;
|
|
+ grindstoneIgnoredEnchants.add(enchantment);
|
|
+ });
|
|
+ grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes);
|
|
+ grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay);
|
|
+ caveVinesMaxGrowthAge = getInt("settings.blocks.cave_vines.max-growth-age", caveVinesMaxGrowthAge);
|
|
+ if (caveVinesMaxGrowthAge > 25) {
|
|
+ caveVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.cave_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ kelpMaxGrowthAge = getInt("settings.blocks.kelp.max-growth-age", kelpMaxGrowthAge);
|
|
+ if (kelpMaxGrowthAge > 25) {
|
|
+ kelpMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.kelp.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ twistingVinesMaxGrowthAge = getInt("settings.blocks.twisting_vines.max-growth-age", twistingVinesMaxGrowthAge);
|
|
+ if (twistingVinesMaxGrowthAge > 25) {
|
|
+ twistingVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.twisting_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ weepingVinesMaxGrowthAge = getInt("settings.blocks.weeping_vines.max-growth-age", weepingVinesMaxGrowthAge);
|
|
+ if (weepingVinesMaxGrowthAge > 25) {
|
|
+ weepingVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.weeping_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ magmaBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.magma-block.reverse-bubble-column-flow", magmaBlockReverseBubbleColumnFlow);
|
|
+ soulSandBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.soul-sand.reverse-bubble-column-flow", soulSandBlockReverseBubbleColumnFlow);
|
|
+ }
|
|
+
|
|
+ public static boolean allowInapplicableEnchants = false;
|
|
+ public static boolean allowIncompatibleEnchants = false;
|
|
+ public static boolean allowHigherEnchantsLevels = false;
|
|
+ public static boolean allowUnsafeEnchantCommand = false;
|
|
+ public static boolean replaceIncompatibleEnchants = false;
|
|
+ public static boolean clampEnchantLevels = true;
|
|
+ private static void enchantmentSettings() {
|
|
+ if (version < 30) {
|
|
+ boolean oldValue = getBoolean("settings.enchantment.allow-unsafe-enchants", false);
|
|
+ set("settings.enchantment.anvil.allow-unsafe-enchants", oldValue);
|
|
+ set("settings.enchantment.anvil.allow-inapplicable-enchants", true);
|
|
+ set("settings.enchantment.anvil.allow-incompatible-enchants", true);
|
|
+ set("settings.enchantment.anvil.allow-higher-enchants-levels", true);
|
|
+ set("settings.enchantment.allow-unsafe-enchants", null);
|
|
+ }
|
|
+ if (version < 37) {
|
|
+ boolean allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", false);
|
|
+ if (!allowUnsafeEnchants) {
|
|
+ set("settings.enchantment.anvil.allow-inapplicable-enchants", false);
|
|
+ set("settings.enchantment.anvil.allow-incompatible-enchants", false);
|
|
+ set("settings.enchantment.anvil.allow-higher-enchants-levels", false);
|
|
+ }
|
|
+ set("settings.enchantment.anvil.allow-unsafe-enchants", null);
|
|
+ }
|
|
+ allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants);
|
|
+ allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants);
|
|
+ allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels);
|
|
+ allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchantCommand);
|
|
+ replaceIncompatibleEnchants = getBoolean("settings.enchantment.anvil.replace-incompatible-enchants", replaceIncompatibleEnchants);
|
|
+ clampEnchantLevels = getBoolean("settings.enchantment.clamp-levels", clampEnchantLevels);
|
|
+ }
|
|
+
|
|
+ public static boolean endermanShortHeight = false;
|
|
+ private static void entitySettings() {
|
|
+ endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight);
|
|
+ if (endermanShortHeight) EntityType.ENDERMAN.setDimensions(EntityDimensions.scalable(0.6F, 1.9F));
|
|
+ }
|
|
+
|
|
+ public static boolean allowWaterPlacementInTheEnd = true;
|
|
+ private static void allowWaterPlacementInEnd() {
|
|
+ allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd);
|
|
+ }
|
|
+
|
|
+ public static boolean beeCountPayload = false;
|
|
+ private static void beeCountPayload() {
|
|
+ beeCountPayload = getBoolean("settings.bee-count-payload", beeCountPayload);
|
|
+ }
|
|
+
|
|
+ public static boolean tpsCatchup = true;
|
|
+ private static void tpsCatchup() {
|
|
+ tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup);
|
|
+ }
|
|
+
|
|
+ public static boolean useUPnP = false;
|
|
+ public static boolean maxJoinsPerSecond = false;
|
|
+ private static void networkSettings() {
|
|
+ useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP);
|
|
+ maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond);
|
|
+ }
|
|
+
|
|
+ public static java.util.regex.Pattern usernameValidCharactersPattern;
|
|
+ private static void usernameValidationSettings() {
|
|
+ String defaultPattern = "^[a-zA-Z0-9_.]*$";
|
|
+ String setPattern = getString("settings.username-valid-characters", defaultPattern);
|
|
+ usernameValidCharactersPattern = java.util.regex.Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern);
|
|
+ }
|
|
+
|
|
+ public static boolean fixProjectileLootingTransfer = false;
|
|
+ private static void fixProjectileLootingTransfer() {
|
|
+ fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer);
|
|
+ }
|
|
+
|
|
+ public static boolean clampAttributes = true;
|
|
+ private static void clampAttributes() {
|
|
+ clampAttributes = getBoolean("settings.clamp-attributes", clampAttributes);
|
|
+ }
|
|
+
|
|
+ public static boolean limitArmor = true;
|
|
+ private static void limitArmor() {
|
|
+ limitArmor = getBoolean("settings.limit-armor", limitArmor);
|
|
+ }
|
|
+
|
|
+ private static void blastResistanceSettings() {
|
|
+ getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) {
|
|
+ log(Level.SEVERE, "Invalid block for `settings.blast-resistance-overrides`: " + blockId);
|
|
+ return;
|
|
+ }
|
|
+ if (!(value instanceof Number blastResistance)) {
|
|
+ log(Level.SEVERE, "Invalid blast resistance for `settings.blast-resistance-overrides." + blockId + "`: " + value);
|
|
+ return;
|
|
+ }
|
|
+ block.explosionResistance = blastResistance.floatValue();
|
|
+ });
|
|
+ }
|
|
+ private static void blockFallMultiplierSettings() {
|
|
+ getMap("settings.block-fall-multipliers", Map.ofEntries(
|
|
+ Map.entry("minecraft:hay_block", Map.of("damage", 0.2F)),
|
|
+ Map.entry("minecraft:white_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:light_gray_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:gray_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:black_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:brown_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:pink_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:red_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:orange_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:yellow_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:green_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:lime_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:cyan_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:light_blue_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:blue_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:purple_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:magenta_bed", Map.of("distance", 0.5F))
|
|
+ )).forEach((blockId, value) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) {
|
|
+ log(Level.SEVERE, "Invalid block for `settings.block-fall-multipliers`: " + blockId);
|
|
+ return;
|
|
+ }
|
|
+ if (!(value instanceof Map<?, ?> map)) {
|
|
+ log(Level.SEVERE, "Invalid fall multiplier for `settings.block-fall-multipliers." + blockId + "`: " + value
|
|
+ + ", expected a map with keys `damage` and `distance` to floats.");
|
|
+ return;
|
|
+ }
|
|
+ Object rawFallDamageMultiplier = map.get("damage");
|
|
+ if (rawFallDamageMultiplier == null) rawFallDamageMultiplier = 1F;
|
|
+ if (!(rawFallDamageMultiplier instanceof Number fallDamageMultiplier)) {
|
|
+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".damage`: " + map.get("damage"));
|
|
+ return;
|
|
+ }
|
|
+ Object rawFallDistanceMultiplier = map.get("distance");
|
|
+ if (rawFallDistanceMultiplier == null) rawFallDistanceMultiplier = 1F;
|
|
+ if (!(rawFallDistanceMultiplier instanceof Number fallDistanceMultiplier)) {
|
|
+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".distance`: " + map.get("distance"));
|
|
+ return;
|
|
+ }
|
|
+ block.fallDamageMultiplier = fallDamageMultiplier.floatValue();
|
|
+ block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue();
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public static boolean playerDeathsAlwaysShowItem = false;
|
|
+ private static void playerDeathsAlwaysShowItem() {
|
|
+ playerDeathsAlwaysShowItem = getBoolean("settings.player-deaths-always-show-item", playerDeathsAlwaysShowItem);
|
|
+ }
|
|
+
|
|
+ public static boolean registerMinecraftDebugCommands = false;
|
|
+ private static void registerMinecraftDebugCommands() {
|
|
+ registerMinecraftDebugCommands = getBoolean("settings.register-minecraft-debug-commands", registerMinecraftDebugCommands);
|
|
+ }
|
|
+
|
|
+ public static List<String> startupCommands = new ArrayList<>();
|
|
+ private static void startupCommands() {
|
|
+ startupCommands.clear();
|
|
+ getList("settings.startup-commands", new ArrayList<String>()).forEach(line -> {
|
|
+ String command = line.toString();
|
|
+ if (command.startsWith("/")) {
|
|
+ command = command.substring(1);
|
|
+ }
|
|
+ startupCommands.add(command);
|
|
+ });
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..36b875a1cb10f1704e8530b6eb7b7e9dc51ef995
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
@@ -0,0 +1,3431 @@
|
|
+package org.purpurmc.purpur;
|
|
+
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.monster.Shulker;
|
|
+import net.minecraft.world.item.DyeColor;
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.state.properties.Tilt;
|
|
+import org.purpurmc.purpur.tool.Flattenable;
|
|
+import org.purpurmc.purpur.tool.Strippable;
|
|
+import org.purpurmc.purpur.tool.Tillable;
|
|
+import org.purpurmc.purpur.tool.Waxable;
|
|
+import org.purpurmc.purpur.tool.Weatherable;
|
|
+import org.apache.commons.lang.BooleanUtils;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.World;
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import java.util.ArrayList;
|
|
+import java.util.HashMap;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.function.Predicate;
|
|
+import java.util.logging.Level;
|
|
+import static org.purpurmc.purpur.PurpurConfig.log;
|
|
+
|
|
+@SuppressWarnings("unused")
|
|
+public class PurpurWorldConfig {
|
|
+
|
|
+ private final String worldName;
|
|
+ private final World.Environment environment;
|
|
+
|
|
+ public PurpurWorldConfig(String worldName, World.Environment environment) {
|
|
+ this.worldName = worldName;
|
|
+ this.environment = environment;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ public void init() {
|
|
+ log("-------- World Settings For [" + worldName + "] --------");
|
|
+ PurpurConfig.readConfig(PurpurWorldConfig.class, this);
|
|
+ }
|
|
+
|
|
+ private void set(String path, Object val) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, val);
|
|
+ PurpurConfig.config.set("world-settings.default." + path, val);
|
|
+ if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) {
|
|
+ PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val);
|
|
+ PurpurConfig.config.set("world-settings." + worldName + "." + path, val);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private ConfigurationSection getConfigurationSection(String path) {
|
|
+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
|
|
+ return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path);
|
|
+ }
|
|
+
|
|
+ private String getString(String path, String def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private boolean getBoolean(String path, boolean def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private boolean getBoolean(String path, Predicate<Boolean> predicate) {
|
|
+ String val = getString(path, "default").toLowerCase();
|
|
+ Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
|
|
+ return predicate.test(bool);
|
|
+ }
|
|
+
|
|
+ private double getDouble(String path, double def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private int getInt(String path, int def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private <T> List<?> getList(String path, T def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private Map<String, Object> getMap(String path, Map<String, Object> def) {
|
|
+ final Map<String, Object> fallback = PurpurConfig.getMap("world-settings.default." + path, def);
|
|
+ final Map<String, Object> value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null);
|
|
+ return value.isEmpty() ? fallback : value;
|
|
+ }
|
|
+
|
|
+ public float armorstandStepHeight = 0.0F;
|
|
+ public boolean armorstandSetNameVisible = false;
|
|
+ public boolean armorstandFixNametags = false;
|
|
+ public boolean armorstandMovement = true;
|
|
+ public boolean armorstandWaterMovement = true;
|
|
+ public boolean armorstandWaterFence = true;
|
|
+ public boolean armorstandPlaceWithArms = false;
|
|
+ private void armorstandSettings() {
|
|
+ armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight);
|
|
+ armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible);
|
|
+ armorstandFixNametags = getBoolean("gameplay-mechanics.armorstand.fix-nametags", armorstandFixNametags);
|
|
+ armorstandMovement = getBoolean("gameplay-mechanics.armorstand.can-movement-tick", armorstandMovement);
|
|
+ armorstandWaterMovement = getBoolean("gameplay-mechanics.armorstand.can-move-in-water", armorstandWaterMovement);
|
|
+ armorstandWaterFence = getBoolean("gameplay-mechanics.armorstand.can-move-in-water-over-fence", armorstandWaterFence);
|
|
+ armorstandPlaceWithArms = getBoolean("gameplay-mechanics.armorstand.place-with-arms-visible", armorstandPlaceWithArms);
|
|
+ }
|
|
+
|
|
+ public boolean useBetterMending = false;
|
|
+ public boolean alwaysTameInCreative = false;
|
|
+ public boolean boatEjectPlayersOnLand = false;
|
|
+ public boolean boatsDoFallDamage = false;
|
|
+ public boolean disableDropsOnCrammingDeath = false;
|
|
+ public boolean milkCuresBadOmen = true;
|
|
+ public double tridentLoyaltyVoidReturnHeight = 0.0D;
|
|
+ public boolean entitiesCanUsePortals = true;
|
|
+ public int raidCooldownSeconds = 0;
|
|
+ public int animalBreedingCooldownSeconds = 0;
|
|
+ public boolean persistentDroppableEntityDisplayNames = true;
|
|
+ public boolean entitiesPickUpLootBypassMobGriefing = false;
|
|
+ public boolean fireballsBypassMobGriefing = false;
|
|
+ public boolean projectilesBypassMobGriefing = false;
|
|
+ public boolean noteBlockIgnoreAbove = false;
|
|
+ public boolean imposeTeleportRestrictionsOnGateways = false;
|
|
+ public boolean imposeTeleportRestrictionsOnNetherPortals = false;
|
|
+ public boolean imposeTeleportRestrictionsOnEndPortals = false;
|
|
+ public boolean tickFluids = true;
|
|
+ public double mobsBlindnessMultiplier = 1;
|
|
+ public boolean mobsIgnoreRails = false;
|
|
+ public boolean rainStopsAfterSleep = true;
|
|
+ public boolean thunderStopsAfterSleep = true;
|
|
+ public boolean persistentTileEntityLore = false;
|
|
+ public boolean persistentTileEntityDisplayName = true;
|
|
+ public int mobLastHurtByPlayerTime = 100;
|
|
+ public boolean milkClearsBeneficialEffects = true;
|
|
+ public boolean disableOxidationProximityPenalty = false;
|
|
+ private void miscGameplayMechanicsSettings() {
|
|
+ useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending);
|
|
+ alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative);
|
|
+ boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand);
|
|
+ boatsDoFallDamage = getBoolean("gameplay-mechanics.boat.do-fall-damage", boatsDoFallDamage);
|
|
+ disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath);
|
|
+ milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen);
|
|
+ tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight);
|
|
+ entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals);
|
|
+ raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds);
|
|
+ animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds);
|
|
+ persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames);
|
|
+ entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing);
|
|
+ fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing);
|
|
+ projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing);
|
|
+ noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove);
|
|
+ imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways);
|
|
+ imposeTeleportRestrictionsOnNetherPortals = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-nether-portals", imposeTeleportRestrictionsOnNetherPortals);
|
|
+ imposeTeleportRestrictionsOnEndPortals = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-end-portals", imposeTeleportRestrictionsOnEndPortals);
|
|
+ tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids);
|
|
+ mobsBlindnessMultiplier = getDouble("gameplay-mechanics.entity-blindness-multiplier", mobsBlindnessMultiplier);
|
|
+ mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails);
|
|
+ rainStopsAfterSleep = getBoolean("gameplay-mechanics.rain-stops-after-sleep", rainStopsAfterSleep);
|
|
+ thunderStopsAfterSleep = getBoolean("gameplay-mechanics.thunder-stops-after-sleep", thunderStopsAfterSleep);
|
|
+ if (PurpurConfig.version < 35) {
|
|
+ boolean oldVal = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityLore);
|
|
+ set("gameplay-mechanics.persistent-tileentity-display-names-and-lore", null);
|
|
+ set("gameplay-mechanics.persistent-tileentity-lore", oldVal);
|
|
+ set("gameplay-mechanics.persistent-tileentity-display-name", !oldVal);
|
|
+ }
|
|
+ persistentTileEntityLore = getBoolean("gameplay-mechanics.persistent-tileentity-lore", persistentTileEntityLore);
|
|
+ persistentTileEntityDisplayName = getBoolean("gameplay-mechanics.persistent-tileentity-display-name", persistentTileEntityDisplayName);
|
|
+ mobLastHurtByPlayerTime = getInt("gameplay-mechanics.mob-last-hurt-by-player-time", mobLastHurtByPlayerTime);
|
|
+ milkClearsBeneficialEffects = getBoolean("gameplay-mechanics.milk-clears-beneficial-effects", milkClearsBeneficialEffects);
|
|
+ disableOxidationProximityPenalty = getBoolean("gameplay-mechanics.disable-oxidation-proximity-penalty", disableOxidationProximityPenalty);
|
|
+ }
|
|
+
|
|
+ public int daytimeTicks = 12000;
|
|
+ public int nighttimeTicks = 12000;
|
|
+ private void daytimeCycleSettings() {
|
|
+ daytimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.daytime", daytimeTicks);
|
|
+ nighttimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.nighttime", nighttimeTicks);
|
|
+ }
|
|
+
|
|
+ public int drowningAirTicks = 300;
|
|
+ public int drowningDamageInterval = 20;
|
|
+ public double damageFromDrowning = 2.0F;
|
|
+ private void drowningSettings() {
|
|
+ drowningAirTicks = getInt("gameplay-mechanics.drowning.air-ticks", drowningAirTicks);
|
|
+ drowningDamageInterval = getInt("gameplay-mechanics.drowning.ticks-per-damage", drowningDamageInterval);
|
|
+ damageFromDrowning = getDouble("gameplay-mechanics.drowning.damage-from-drowning", damageFromDrowning);
|
|
+ }
|
|
+
|
|
+ public int elytraDamagePerSecond = 1;
|
|
+ public double elytraDamageMultiplyBySpeed = 0;
|
|
+ public int elytraDamagePerFireworkBoost = 0;
|
|
+ public int elytraDamagePerTridentBoost = 0;
|
|
+ public boolean elytraKineticDamage = true;
|
|
+ private void elytraSettings() {
|
|
+ elytraDamagePerSecond = getInt("gameplay-mechanics.elytra.damage-per-second", elytraDamagePerSecond);
|
|
+ elytraDamageMultiplyBySpeed = getDouble("gameplay-mechanics.elytra.damage-multiplied-by-speed", elytraDamageMultiplyBySpeed);
|
|
+ elytraDamagePerFireworkBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.firework", elytraDamagePerFireworkBoost);
|
|
+ elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost);
|
|
+ elytraKineticDamage = getBoolean("gameplay-mechanics.elytra.kinetic-damage", elytraKineticDamage);
|
|
+ }
|
|
+
|
|
+ public int entityLifeSpan = 0;
|
|
+ public float entityLeftHandedChance = 0.05f;
|
|
+ public boolean entitySharedRandom = true;
|
|
+ private void entitySettings() {
|
|
+ entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan);
|
|
+ entityLeftHandedChance = (float) getDouble("gameplay-mechanics.entity-left-handed-chance", entityLeftHandedChance);
|
|
+ entitySharedRandom = getBoolean("settings.entity.shared-random", entitySharedRandom);
|
|
+ }
|
|
+
|
|
+ public boolean infinityWorksWithoutArrows = false;
|
|
+ private void infinityArrowsSettings() {
|
|
+ infinityWorksWithoutArrows = getBoolean("gameplay-mechanics.infinity-bow.works-without-arrows", infinityWorksWithoutArrows);
|
|
+ }
|
|
+
|
|
+ public boolean explosionClampRadius = true;
|
|
+ private void explosionSettings() {
|
|
+ explosionClampRadius = getBoolean("gameplay-mechanics.clamp-explosion-radius", explosionClampRadius);
|
|
+ }
|
|
+
|
|
+ public List<Item> itemImmuneToCactus = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToExplosion = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToFire = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToLightning = new ArrayList<>();
|
|
+ public boolean dontRunWithScissors = false;
|
|
+ public boolean ignoreScissorsInWater = false;
|
|
+ public boolean ignoreScissorsInLava = false;
|
|
+ public double scissorsRunningDamage = 1D;
|
|
+ public float enderPearlDamage = 5.0F;
|
|
+ public int enderPearlCooldown = 20;
|
|
+ public int enderPearlCooldownCreative = 20;
|
|
+ public float enderPearlEndermiteChance = 0.05F;
|
|
+ public int glowBerriesEatGlowDuration = 0;
|
|
+ public boolean shulkerBoxItemDropContentsWhenDestroyed = true;
|
|
+ public boolean compassItemShowsBossBar = false;
|
|
+ public boolean snowballExtinguishesFire = false;
|
|
+ public boolean snowballExtinguishesCandles = false;
|
|
+ public boolean snowballExtinguishesCampfires = false;
|
|
+ private void itemSettings() {
|
|
+ itemImmuneToCactus.clear();
|
|
+ getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToCactus.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToCactus.add(item);
|
|
+ });
|
|
+ itemImmuneToExplosion.clear();
|
|
+ getList("gameplay-mechanics.item.immune.explosion", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToExplosion.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToExplosion.add(item);
|
|
+ });
|
|
+ itemImmuneToFire.clear();
|
|
+ getList("gameplay-mechanics.item.immune.fire", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToFire.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToFire.add(item);
|
|
+ });
|
|
+ itemImmuneToLightning.clear();
|
|
+ getList("gameplay-mechanics.item.immune.lightning", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToLightning.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToLightning.add(item);
|
|
+ });
|
|
+ dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors);
|
|
+ ignoreScissorsInWater = getBoolean("gameplay-mechanics.item.shears.ignore-in-water", ignoreScissorsInWater);
|
|
+ ignoreScissorsInLava = getBoolean("gameplay-mechanics.item.shears.ignore-in-lava", ignoreScissorsInLava);
|
|
+ scissorsRunningDamage = getDouble("gameplay-mechanics.item.shears.sprinting-damage", scissorsRunningDamage);
|
|
+ enderPearlDamage = (float) getDouble("gameplay-mechanics.item.ender-pearl.damage", enderPearlDamage);
|
|
+ enderPearlCooldown = getInt("gameplay-mechanics.item.ender-pearl.cooldown", enderPearlCooldown);
|
|
+ enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative);
|
|
+ enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance);
|
|
+ glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration);
|
|
+ shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed);
|
|
+ compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar);
|
|
+ snowballExtinguishesFire = getBoolean("gameplay-mechanics.item.snowball.extinguish.fire", snowballExtinguishesFire);
|
|
+ snowballExtinguishesCandles = getBoolean("gameplay-mechanics.item.snowball.extinguish.candles", snowballExtinguishesCandles);
|
|
+ snowballExtinguishesCampfires = getBoolean("gameplay-mechanics.item.snowball.extinguish.campfires", snowballExtinguishesCampfires);
|
|
+ }
|
|
+
|
|
+ public double minecartMaxSpeed = 0.4D;
|
|
+ public boolean minecartPlaceAnywhere = false;
|
|
+ public boolean minecartControllable = false;
|
|
+ public float minecartControllableStepHeight = 1.0F;
|
|
+ public double minecartControllableHopBoost = 0.5D;
|
|
+ public boolean minecartControllableFallDamage = true;
|
|
+ public double minecartControllableBaseSpeed = 0.1D;
|
|
+ public Map<Block, Double> minecartControllableBlockSpeeds = new HashMap<>();
|
|
+ public double poweredRailBoostModifier = 0.06;
|
|
+ private void minecartSettings() {
|
|
+ if (PurpurConfig.version < 12) {
|
|
+ boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere);
|
|
+ set("gameplay-mechanics.controllable-minecarts.place-anywhere", null);
|
|
+ set("gameplay-mechanics.minecart.place-anywhere", oldBool);
|
|
+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", minecartControllable);
|
|
+ set("gameplay-mechanics.controllable-minecarts.enabled", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.enabled", oldBool);
|
|
+ double oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.step-height", minecartControllableStepHeight);
|
|
+ set("gameplay-mechanics.controllable-minecarts.step-height", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.step-height", oldDouble);
|
|
+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", minecartControllableHopBoost);
|
|
+ set("gameplay-mechanics.controllable-minecarts.hop-boost", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.hop-boost", oldDouble);
|
|
+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", minecartControllableFallDamage);
|
|
+ set("gameplay-mechanics.controllable-minecarts.fall-damage", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.fall-damage", oldBool);
|
|
+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", minecartControllableBaseSpeed);
|
|
+ set("gameplay-mechanics.controllable-minecarts.base-speed", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.base-speed", oldDouble);
|
|
+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed");
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ if ("grass-block".equals(key)) key = "grass_block"; // oopsie
|
|
+ oldDouble = section.getDouble(key, minecartControllableBaseSpeed);
|
|
+ set("gameplay-mechanics.controllable-minecarts.block-speed." + key, null);
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed." + key, oldDouble);
|
|
+ }
|
|
+ set("gameplay-mechanics.controllable-minecarts.block-speed", null);
|
|
+ }
|
|
+ set("gameplay-mechanics.controllable-minecarts", null);
|
|
+ }
|
|
+
|
|
+ minecartMaxSpeed = getDouble("gameplay-mechanics.minecart.max-speed", minecartMaxSpeed);
|
|
+ minecartPlaceAnywhere = getBoolean("gameplay-mechanics.minecart.place-anywhere", minecartPlaceAnywhere);
|
|
+ minecartControllable = getBoolean("gameplay-mechanics.minecart.controllable.enabled", minecartControllable);
|
|
+ minecartControllableStepHeight = (float) getDouble("gameplay-mechanics.minecart.controllable.step-height", minecartControllableStepHeight);
|
|
+ minecartControllableHopBoost = getDouble("gameplay-mechanics.minecart.controllable.hop-boost", minecartControllableHopBoost);
|
|
+ minecartControllableFallDamage = getBoolean("gameplay-mechanics.minecart.controllable.fall-damage", minecartControllableFallDamage);
|
|
+ minecartControllableBaseSpeed = getDouble("gameplay-mechanics.minecart.controllable.base-speed", minecartControllableBaseSpeed);
|
|
+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.minecart.controllable.block-speed");
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(key));
|
|
+ if (block != Blocks.AIR) {
|
|
+ minecartControllableBlockSpeeds.put(block, section.getDouble(key, minecartControllableBaseSpeed));
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D);
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D);
|
|
+ }
|
|
+ poweredRailBoostModifier = getDouble("gameplay-mechanics.minecart.powered-rail.boost-modifier", poweredRailBoostModifier);
|
|
+ }
|
|
+
|
|
+ public float entityHealthRegenAmount = 1.0F;
|
|
+ public float entityMinimalHealthPoison = 1.0F;
|
|
+ public float entityPoisonDegenerationAmount = 1.0F;
|
|
+ public float entityWitherDegenerationAmount = 1.0F;
|
|
+ public float humanHungerExhaustionAmount = 0.005F;
|
|
+ public float humanSaturationRegenAmount = 1.0F;
|
|
+ private void mobEffectSettings() {
|
|
+ entityHealthRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.health-regen-amount", entityHealthRegenAmount);
|
|
+ entityMinimalHealthPoison = (float) getDouble("gameplay-mechanics.mob-effects.minimal-health-poison-amount", entityMinimalHealthPoison);
|
|
+ entityPoisonDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.poison-degeneration-amount", entityPoisonDegenerationAmount);
|
|
+ entityWitherDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.wither-degeneration-amount", entityWitherDegenerationAmount);
|
|
+ humanHungerExhaustionAmount = (float) getDouble("gameplay-mechanics.mob-effects.hunger-exhaustion-amount", humanHungerExhaustionAmount);
|
|
+ humanSaturationRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.saturation-regen-amount", humanSaturationRegenAmount);
|
|
+ }
|
|
+
|
|
+ public boolean catSpawning;
|
|
+ public boolean patrolSpawning;
|
|
+ public boolean phantomSpawning;
|
|
+ public boolean villagerTraderSpawning;
|
|
+ public boolean villageSiegeSpawning;
|
|
+ public boolean mobSpawningIgnoreCreativePlayers = false;
|
|
+ private void mobSpawnerSettings() {
|
|
+ // values of "default" or null will default to true only if the world environment is normal (aka overworld)
|
|
+ Predicate<Boolean> predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL);
|
|
+ catSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-cats", predicate);
|
|
+ patrolSpawning = getBoolean("gameplay-mechanics.mob-spawning.raid-patrols", predicate);
|
|
+ phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate);
|
|
+ villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate);
|
|
+ villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate);
|
|
+ mobSpawningIgnoreCreativePlayers = getBoolean("gameplay-mechanics.mob-spawning.ignore-creative-players", mobSpawningIgnoreCreativePlayers);
|
|
+ }
|
|
+
|
|
+ public boolean disableObserverClocks = false;
|
|
+ private void observerSettings() {
|
|
+ disableObserverClocks = getBoolean("blocks.observer.disable-clock", disableObserverClocks);
|
|
+ }
|
|
+
|
|
+ public int playerNetheriteFireResistanceDuration = 0;
|
|
+ public int playerNetheriteFireResistanceAmplifier = 0;
|
|
+ public boolean playerNetheriteFireResistanceAmbient = false;
|
|
+ public boolean playerNetheriteFireResistanceShowParticles = false;
|
|
+ public boolean playerNetheriteFireResistanceShowIcon = true;
|
|
+ private void playerNetheriteFireResistance() {
|
|
+ playerNetheriteFireResistanceDuration = getInt("gameplay-mechanics.player.netherite-fire-resistance.duration", playerNetheriteFireResistanceDuration);
|
|
+ playerNetheriteFireResistanceAmplifier = getInt("gameplay-mechanics.player.netherite-fire-resistance.amplifier", playerNetheriteFireResistanceAmplifier);
|
|
+ playerNetheriteFireResistanceAmbient = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.ambient", playerNetheriteFireResistanceAmbient);
|
|
+ playerNetheriteFireResistanceShowParticles = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-particles", playerNetheriteFireResistanceShowParticles);
|
|
+ playerNetheriteFireResistanceShowIcon = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-icon", playerNetheriteFireResistanceShowIcon);
|
|
+ }
|
|
+
|
|
+ public boolean idleTimeoutKick = true;
|
|
+ public boolean idleTimeoutTickNearbyEntities = true;
|
|
+ public boolean idleTimeoutCountAsSleeping = false;
|
|
+ public boolean idleTimeoutUpdateTabList = false;
|
|
+ public boolean idleTimeoutTargetPlayer = true;
|
|
+ public String playerDeathExpDropEquation = "expLevel * 7";
|
|
+ public int playerDeathExpDropMax = 100;
|
|
+ public boolean teleportIfOutsideBorder = false;
|
|
+ public boolean teleportOnNetherCeilingDamage = false;
|
|
+ public boolean totemOfUndyingWorksInInventory = false;
|
|
+ public boolean playerFixStuckPortal = false;
|
|
+ public boolean creativeOnePunch = false;
|
|
+ public boolean playerSleepNearMonsters = false;
|
|
+ public boolean playersSkipNight = true;
|
|
+ public double playerCriticalDamageMultiplier = 1.5D;
|
|
+ public int playerBurpDelay = 10;
|
|
+ public boolean playerBurpWhenFull = false;
|
|
+ public boolean playerRidableInWater = false;
|
|
+ public boolean playerRemoveBindingWithWeakness = false;
|
|
+ public int shiftRightClickRepairsMendingPoints = 0;
|
|
+ public int playerExpPickupDelay = 2;
|
|
+ public boolean playerVoidTrading = false;
|
|
+ private void playerSettings() {
|
|
+ if (PurpurConfig.version < 19) {
|
|
+ boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer);
|
|
+ set("gameplay-mechanics.player.idle-timeout.mods-target", null);
|
|
+ set("gameplay-mechanics.player.idle-timeout.mobs-target", oldVal);
|
|
+ }
|
|
+ idleTimeoutKick = System.getenv("PURPUR_FORCE_IDLE_KICK") == null ? getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick) : Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK"));
|
|
+ idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities);
|
|
+ idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping);
|
|
+ idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList);
|
|
+ idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer);
|
|
+ playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation);
|
|
+ playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax);
|
|
+ teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder);
|
|
+ teleportOnNetherCeilingDamage = getBoolean("gameplay-mechanics.player.teleport-on-nether-ceiling-damage", teleportOnNetherCeilingDamage);
|
|
+ totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory);
|
|
+ playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal);
|
|
+ creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch);
|
|
+ playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters);
|
|
+ playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight);
|
|
+ playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier);
|
|
+ playerBurpDelay = getInt("gameplay-mechanics.player.burp-delay", playerBurpDelay);
|
|
+ playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull);
|
|
+ playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater);
|
|
+ playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness);
|
|
+ shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints);
|
|
+ playerExpPickupDelay = getInt("gameplay-mechanics.player.exp-pickup-delay-ticks", playerExpPickupDelay);
|
|
+ playerVoidTrading = getBoolean("gameplay-mechanics.player.allow-void-trading", playerVoidTrading);
|
|
+ }
|
|
+
|
|
+ public boolean silkTouchEnabled = false;
|
|
+ public String silkTouchSpawnerName = "<reset><white>Monster Spawner";
|
|
+ public List<String> silkTouchSpawnerLore = new ArrayList<>();
|
|
+ public List<Item> silkTouchTools = new ArrayList<>();
|
|
+ public int minimumSilkTouchSpawnerRequire = 1;
|
|
+ private void silkTouchSettings() {
|
|
+ if (PurpurConfig.version < 21) {
|
|
+ String oldName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName);
|
|
+ set("gameplay-mechanics.silk-touch.spawner-name", "<reset>" + ChatColor.toMM(oldName.replace("{mob}", "<mob>")));
|
|
+ List<String> list = new ArrayList<>();
|
|
+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a <mob>"))
|
|
+ .forEach(line -> list.add("<reset>" + ChatColor.toMM(line.toString().replace("{mob}", "<mob>"))));
|
|
+ set("gameplay-mechanics.silk-touch.spawner-lore", list);
|
|
+ }
|
|
+ silkTouchEnabled = getBoolean("gameplay-mechanics.silk-touch.enabled", silkTouchEnabled);
|
|
+ silkTouchSpawnerName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName);
|
|
+ minimumSilkTouchSpawnerRequire = getInt("gameplay-mechanics.silk-touch.minimal-level", minimumSilkTouchSpawnerRequire);
|
|
+ silkTouchSpawnerLore.clear();
|
|
+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a <mob>"))
|
|
+ .forEach(line -> silkTouchSpawnerLore.add(line.toString()));
|
|
+ silkTouchTools.clear();
|
|
+ getList("gameplay-mechanics.silk-touch.tools", List.of(
|
|
+ "minecraft:iron_pickaxe",
|
|
+ "minecraft:golden_pickaxe",
|
|
+ "minecraft:diamond_pickaxe",
|
|
+ "minecraft:netherite_pickaxe"
|
|
+ )).forEach(key -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (item != Items.AIR) silkTouchTools.add(item);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public double bowProjectileOffset = 1.0D;
|
|
+ public double crossbowProjectileOffset = 1.0D;
|
|
+ public double eggProjectileOffset = 1.0D;
|
|
+ public double enderPearlProjectileOffset = 1.0D;
|
|
+ public double throwablePotionProjectileOffset = 1.0D;
|
|
+ public double tridentProjectileOffset = 1.0D;
|
|
+ public double snowballProjectileOffset = 1.0D;
|
|
+ private void projectileOffsetSettings() {
|
|
+ bowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.bow", bowProjectileOffset);
|
|
+ crossbowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.crossbow", crossbowProjectileOffset);
|
|
+ eggProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.egg", eggProjectileOffset);
|
|
+ enderPearlProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.ender-pearl", enderPearlProjectileOffset);
|
|
+ throwablePotionProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.throwable-potion", throwablePotionProjectileOffset);
|
|
+ tridentProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.trident", tridentProjectileOffset);
|
|
+ snowballProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.snowball", snowballProjectileOffset);
|
|
+ }
|
|
+
|
|
+ public int snowballDamage = -1;
|
|
+ private void snowballSettings() {
|
|
+ snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage);
|
|
+ }
|
|
+
|
|
+ public Map<Block, Strippable> axeStrippables = new HashMap<>();
|
|
+ public Map<Block, Waxable> axeWaxables = new HashMap<>();
|
|
+ public Map<Block, Weatherable> axeWeatherables = new HashMap<>();
|
|
+ public Map<Block, Tillable> hoeTillables = new HashMap<>();
|
|
+ public Map<Block, Flattenable> shovelFlattenables = new HashMap<>();
|
|
+ public boolean hoeReplantsCrops = false;
|
|
+ public boolean hoeReplantsNetherWarts = false;
|
|
+ private void toolSettings() {
|
|
+ axeStrippables.clear();
|
|
+ axeWaxables.clear();
|
|
+ axeWeatherables.clear();
|
|
+ hoeTillables.clear();
|
|
+ shovelFlattenables.clear();
|
|
+ if (PurpurConfig.version < 18) {
|
|
+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + ".tools.hoe.tilling");
|
|
+ if (section != null) {
|
|
+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tillables", section);
|
|
+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tilling", null);
|
|
+ }
|
|
+ section = PurpurConfig.config.getConfigurationSection("world-settings.default.tools.hoe.tilling");
|
|
+ if (section != null) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.hoe.tillables", section);
|
|
+ PurpurConfig.config.set("world-settings.default.tools.hoe.tilling", null);
|
|
+ }
|
|
+ }
|
|
+ if (PurpurConfig.version < 29) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap<String, Double>()));
|
|
+ }
|
|
+ if (PurpurConfig.version < 32) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap<String, Double>()));
|
|
+ }
|
|
+ if (PurpurConfig.version < 33) {
|
|
+ getList("gameplay-mechanics.shovel-turns-block-to-grass-path", new ArrayList<String>(){{
|
|
+ add("minecraft:coarse_dirt");
|
|
+ add("minecraft:dirt");
|
|
+ add("minecraft:grass_block");
|
|
+ add("minecraft:mycelium");
|
|
+ add("minecraft:podzol");
|
|
+ add("minecraft:rooted_dirt");
|
|
+ }}).forEach(key -> {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.shovel.flattenables." + key.toString(), Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>()));
|
|
+ });
|
|
+ set("gameplay-mechanics.shovel-turns-block-to-grass-path", null);
|
|
+ }
|
|
+ if (PurpurConfig.version < 34) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_chiseled_copper", Map.of("into", "minecraft:oxidized_chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_door", Map.of("into", "minecraft:oxidized_copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_trapdoor", Map.of("into", "minecraft:oxidized_copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_grate", Map.of("into", "minecraft:oxidized_copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_bulb", Map.of("into", "minecraft:oxidized_copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap<String, Double>()));
|
|
+ }
|
|
+ getMap("tools.axe.strippables", Map.ofEntries(
|
|
+ Map.entry("minecraft:oak_wood", Map.of("into", "minecraft:stripped_oak_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oak_log", Map.of("into", "minecraft:stripped_oak_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dark_oak_wood", Map.of("into", "minecraft:stripped_dark_oak_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dark_oak_log", Map.of("into", "minecraft:stripped_dark_oak_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:acacia_wood", Map.of("into", "minecraft:stripped_acacia_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:acacia_log", Map.of("into", "minecraft:stripped_acacia_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:birch_wood", Map.of("into", "minecraft:stripped_birch_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:birch_log", Map.of("into", "minecraft:stripped_birch_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:jungle_wood", Map.of("into", "minecraft:stripped_jungle_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:jungle_log", Map.of("into", "minecraft:stripped_jungle_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:spruce_wood", Map.of("into", "minecraft:stripped_spruce_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:spruce_log", Map.of("into", "minecraft:stripped_spruce_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:warped_stem", Map.of("into", "minecraft:stripped_warped_stem", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:warped_hyphae", Map.of("into", "minecraft:stripped_warped_hyphae", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:crimson_stem", Map.of("into", "minecraft:stripped_crimson_stem", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:crimson_hyphae", Map.of("into", "minecraft:stripped_crimson_hyphae", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.strippables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeStrippables.put(block, new Strippable(into, drops));
|
|
+ });
|
|
+ getMap("tools.axe.waxables", Map.ofEntries(
|
|
+ Map.entry("minecraft:waxed_copper_block", Map.of("into", "minecraft:copper_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper", Map.of("into", "minecraft:oxidized_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper", Map.of("into", "minecraft:oxidized_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper_slab", Map.of("into", "minecraft:oxidized_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper_stairs", Map.of("into", "minecraft:oxidized_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_chiseled_copper", Map.of("into", "minecraft:oxidized_chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper_door", Map.of("into", "minecraft:oxidized_copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper_trapdoor", Map.of("into", "minecraft:oxidized_copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper_grate", Map.of("into", "minecraft:oxidized_copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper_bulb", Map.of("into", "minecraft:oxidized_copper_bulb", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.waxables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeWaxables.put(block, new Waxable(into, drops));
|
|
+ });
|
|
+ getMap("tools.axe.weatherables", Map.ofEntries(
|
|
+ Map.entry("minecraft:exposed_copper", Map.of("into", "minecraft:copper_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.weatherables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeWeatherables.put(block, new Weatherable(into, drops));
|
|
+ });
|
|
+ getMap("tools.hoe.tillables", Map.ofEntries(
|
|
+ Map.entry("minecraft:grass_block", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dirt_path", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dirt", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:coarse_dirt", Map.of("condition", "air_above", "into", "minecraft:dirt", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:rooted_dirt", Map.of("condition", "always", "into", "minecraft:dirt", "drops", Map.of("minecraft:hanging_roots", 1.0D))))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + "`"); return; }
|
|
+ String conditionId = (String) map.get("condition");
|
|
+ Tillable.Condition condition = Tillable.Condition.get(conditionId);
|
|
+ if (condition == null) { PurpurConfig.log(Level.SEVERE, "Invalid condition for `tools.hoe.tillables." + blockId + ".condition`: " + conditionId); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.hoe.tillables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ hoeTillables.put(block, new Tillable(condition, into, drops));
|
|
+ });
|
|
+ getMap("tools.shovel.flattenables", Map.ofEntries(
|
|
+ Map.entry("minecraft:grass_block", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:podzol", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:coarse_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:mycelium", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:rooted_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.shovel.flattenables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ shovelFlattenables.put(block, new Flattenable(into, drops));
|
|
+ });
|
|
+ hoeReplantsCrops = getBoolean("tools.hoe.replant-crops", hoeReplantsCrops);
|
|
+ hoeReplantsNetherWarts = getBoolean("tools.hoe.replant-nether-warts", hoeReplantsNetherWarts);
|
|
+ }
|
|
+
|
|
+ public boolean anvilAllowColors = false;
|
|
+ public boolean anvilColorsUseMiniMessage;
|
|
+ public int anvilRepairIngotsAmount = 0;
|
|
+ public int anvilDamageObsidianAmount = 0;
|
|
+ private void anvilSettings() {
|
|
+ anvilAllowColors = getBoolean("blocks.anvil.allow-colors", anvilAllowColors);
|
|
+ anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage);
|
|
+ anvilRepairIngotsAmount = getInt("blocks.anvil.iron-ingots-used-for-repair", anvilRepairIngotsAmount);
|
|
+ anvilDamageObsidianAmount = getInt("blocks.anvil.obsidian-used-for-damage", anvilDamageObsidianAmount);
|
|
+ }
|
|
+
|
|
+ public double azaleaGrowthChance = 0.0D;
|
|
+ private void azaleaSettings() {
|
|
+ azaleaGrowthChance = getDouble("blocks.azalea.growth-chance", azaleaGrowthChance);
|
|
+ }
|
|
+
|
|
+ public int beaconLevelOne = 20;
|
|
+ public int beaconLevelTwo = 30;
|
|
+ public int beaconLevelThree = 40;
|
|
+ public int beaconLevelFour = 50;
|
|
+ public boolean beaconAllowEffectsWithTintedGlass = false;
|
|
+ private void beaconSettings() {
|
|
+ beaconLevelOne = getInt("blocks.beacon.effect-range.level-1", beaconLevelOne);
|
|
+ beaconLevelTwo = getInt("blocks.beacon.effect-range.level-2", beaconLevelTwo);
|
|
+ beaconLevelThree = getInt("blocks.beacon.effect-range.level-3", beaconLevelThree);
|
|
+ beaconLevelFour = getInt("blocks.beacon.effect-range.level-4", beaconLevelFour);
|
|
+ beaconAllowEffectsWithTintedGlass = getBoolean("blocks.beacon.allow-effects-with-tinted-glass", beaconAllowEffectsWithTintedGlass);
|
|
+ }
|
|
+
|
|
+ public boolean bedExplode = true;
|
|
+ public boolean bedExplodeOnVillagerSleep = false;
|
|
+ public double bedExplosionPower = 5.0D;
|
|
+ public boolean bedExplosionFire = true;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ private void bedSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()))) {
|
|
+ set("blocks.bed.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ bedExplode = getBoolean("blocks.bed.explode", bedExplode);
|
|
+ bedExplodeOnVillagerSleep = getBoolean("blocks.bed.explode-on-villager-sleep", bedExplodeOnVillagerSleep);
|
|
+ bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower);
|
|
+ bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire);
|
|
+ try {
|
|
+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.bed.explosion-effect`! Using default of `BLOCK`");
|
|
+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Map<Tilt, Integer> bigDripleafTiltDelay = new HashMap<>();
|
|
+ private void bigDripleafSettings() {
|
|
+ bigDripleafTiltDelay.clear();
|
|
+ getMap("blocks.big_dripleaf.tilt-delay", Map.ofEntries(
|
|
+ Map.entry("UNSTABLE", 10),
|
|
+ Map.entry("PARTIAL", 10),
|
|
+ Map.entry("FULL", 100))
|
|
+ ).forEach((tilt, delay) -> {
|
|
+ try {
|
|
+ bigDripleafTiltDelay.put(Tilt.valueOf(tilt), (int) delay);
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ PurpurConfig.log(Level.SEVERE, "Invalid big_dripleaf tilt key: " + tilt);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean cactusBreaksFromSolidNeighbors = true;
|
|
+ public boolean cactusAffectedByBonemeal = false;
|
|
+ private void cactusSettings() {
|
|
+ cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors);
|
|
+ cactusAffectedByBonemeal = getBoolean("blocks.cactus.affected-by-bonemeal", cactusAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean sugarCanAffectedByBonemeal = false;
|
|
+ private void sugarCaneSettings() {
|
|
+ sugarCanAffectedByBonemeal = getBoolean("blocks.sugar_cane.affected-by-bonemeal", sugarCanAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean netherWartAffectedByBonemeal = false;
|
|
+ private void netherWartSettings() {
|
|
+ netherWartAffectedByBonemeal = getBoolean("blocks.nether_wart.affected-by-bonemeal", netherWartAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean campFireLitWhenPlaced = true;
|
|
+ private void campFireSettings() {
|
|
+ campFireLitWhenPlaced = getBoolean("blocks.campfire.lit-when-placed", campFireLitWhenPlaced);
|
|
+ }
|
|
+
|
|
+ public boolean chestOpenWithBlockOnTop = false;
|
|
+ private void chestSettings() {
|
|
+ chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop);
|
|
+ }
|
|
+
|
|
+ public boolean composterBulkProcess = false;
|
|
+ private void composterSettings() {
|
|
+ composterBulkProcess = getBoolean("blocks.composter.sneak-to-bulk-process", composterBulkProcess);
|
|
+ }
|
|
+
|
|
+ public boolean coralDieOutsideWater = true;
|
|
+ private void coralSettings() {
|
|
+ coralDieOutsideWater = getBoolean("blocks.coral.die-outside-water", coralDieOutsideWater);
|
|
+ }
|
|
+
|
|
+ public boolean dispenserApplyCursedArmor = true;
|
|
+ public boolean dispenserPlaceAnvils = false;
|
|
+ private void dispenserSettings() {
|
|
+ dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor);
|
|
+ dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils);
|
|
+ }
|
|
+
|
|
+ public List<Block> doorRequiresRedstone = new ArrayList<>();
|
|
+ private void doorSettings() {
|
|
+ getList("blocks.door.requires-redstone", new ArrayList<String>()).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ doorRequiresRedstone.add(block);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean dragonEggTeleport = true;
|
|
+ private void dragonEggSettings() {
|
|
+ dragonEggTeleport = getBoolean("blocks.dragon_egg.teleport", dragonEggTeleport);
|
|
+ }
|
|
+
|
|
+ public boolean baselessEndCrystalExplode = true;
|
|
+ public double baselessEndCrystalExplosionPower = 6.0D;
|
|
+ public boolean baselessEndCrystalExplosionFire = false;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ public boolean basedEndCrystalExplode = true;
|
|
+ public double basedEndCrystalExplosionPower = 6.0D;
|
|
+ public boolean basedEndCrystalExplosionFire = false;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ public int endCrystalCramming = 0;
|
|
+ public boolean endCrystalPlaceAnywhere = false;
|
|
+ private void endCrystalSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) {
|
|
+ set("blocks.end-crystal.baseless.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ if ("DESTROY".equals(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()))) {
|
|
+ set("blocks.end-crystal.base.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ baselessEndCrystalExplode = getBoolean("blocks.end-crystal.baseless.explode", baselessEndCrystalExplode);
|
|
+ baselessEndCrystalExplosionPower = getDouble("blocks.end-crystal.baseless.explosion-power", baselessEndCrystalExplosionPower);
|
|
+ baselessEndCrystalExplosionFire = getBoolean("blocks.end-crystal.baseless.explosion-fire", baselessEndCrystalExplosionFire);
|
|
+ try {
|
|
+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.baseless.explosion-effect`! Using default of `BLOCK`");
|
|
+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ basedEndCrystalExplode = getBoolean("blocks.end-crystal.base.explode", basedEndCrystalExplode);
|
|
+ basedEndCrystalExplosionPower = getDouble("blocks.end-crystal.base.explosion-power", basedEndCrystalExplosionPower);
|
|
+ basedEndCrystalExplosionFire = getBoolean("blocks.end-crystal.base.explosion-fire", basedEndCrystalExplosionFire);
|
|
+ try {
|
|
+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.base.explosion-effect`! Using default of `BLOCK`");
|
|
+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ endCrystalCramming = getInt("blocks.end-crystal.cramming-amount", endCrystalCramming);
|
|
+ endCrystalPlaceAnywhere = getBoolean("gameplay-mechanics.item.end-crystal.place-anywhere", endCrystalPlaceAnywhere);
|
|
+ }
|
|
+
|
|
+ public boolean farmlandBypassMobGriefing = false;
|
|
+ public boolean farmlandGetsMoistFromBelow = false;
|
|
+ public boolean farmlandAlpha = false;
|
|
+ public boolean farmlandTramplingDisabled = false;
|
|
+ public boolean farmlandTramplingOnlyPlayers = false;
|
|
+ public boolean farmlandTramplingFeatherFalling = false;
|
|
+ public double farmlandTrampleHeight = -1D;
|
|
+ private void farmlandSettings() {
|
|
+ farmlandBypassMobGriefing = getBoolean("blocks.farmland.bypass-mob-griefing", farmlandBypassMobGriefing);
|
|
+ farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow);
|
|
+ farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha);
|
|
+ farmlandTramplingDisabled = getBoolean("blocks.farmland.disable-trampling", farmlandTramplingDisabled);
|
|
+ farmlandTramplingOnlyPlayers = getBoolean("blocks.farmland.only-players-trample", farmlandTramplingOnlyPlayers);
|
|
+ farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling);
|
|
+ farmlandTrampleHeight = getDouble("blocks.farmland.trample-height", farmlandTrampleHeight);
|
|
+ }
|
|
+
|
|
+ public double floweringAzaleaGrowthChance = 0.0D;
|
|
+ private void floweringAzaleaSettings() {
|
|
+ floweringAzaleaGrowthChance = getDouble("blocks.flowering_azalea.growth-chance", floweringAzaleaGrowthChance);
|
|
+ }
|
|
+
|
|
+ public boolean furnaceUseLavaFromUnderneath = false;
|
|
+ private void furnaceSettings() {
|
|
+ if (PurpurConfig.version < 17) {
|
|
+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath);
|
|
+ boolean oldValue = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath);
|
|
+ set("blocks.furnace.infinite-fuel", null);
|
|
+ set("blocks.furnace.use-lava-from-underneath", oldValue);
|
|
+ }
|
|
+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath);
|
|
+ }
|
|
+
|
|
+ public boolean mobsSpawnOnPackedIce = true;
|
|
+ public boolean mobsSpawnOnBlueIce = true;
|
|
+ public boolean snowOnBlueIce = true;
|
|
+ private void iceSettings() {
|
|
+ mobsSpawnOnPackedIce = getBoolean("blocks.packed_ice.allow-mob-spawns", mobsSpawnOnPackedIce);
|
|
+ mobsSpawnOnBlueIce = getBoolean("blocks.blue_ice.allow-mob-spawns", mobsSpawnOnBlueIce);
|
|
+ snowOnBlueIce = getBoolean("blocks.blue_ice.allow-snow-formation", snowOnBlueIce);
|
|
+ }
|
|
+
|
|
+ public int lavaInfiniteRequiredSources = 2;
|
|
+ public int lavaSpeedNether = 10;
|
|
+ public int lavaSpeedNotNether = 30;
|
|
+ private void lavaSettings() {
|
|
+ lavaInfiniteRequiredSources = getInt("blocks.lava.infinite-required-sources", lavaInfiniteRequiredSources);
|
|
+ lavaSpeedNether = getInt("blocks.lava.speed.nether", lavaSpeedNether);
|
|
+ lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether);
|
|
+ }
|
|
+
|
|
+ public int pistonBlockPushLimit = 12;
|
|
+ private void pistonSettings() {
|
|
+ pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit);
|
|
+ }
|
|
+
|
|
+ public boolean magmaBlockDamageWhenSneaking = false;
|
|
+ private void magmaBlockSettings() {
|
|
+ magmaBlockDamageWhenSneaking = getBoolean("blocks.magma-block.damage-when-sneaking", magmaBlockDamageWhenSneaking);
|
|
+ }
|
|
+
|
|
+ public boolean powderSnowBypassMobGriefing = false;
|
|
+ private void powderSnowSettings() {
|
|
+ powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing);
|
|
+ }
|
|
+
|
|
+ public int railActivationRange = 8;
|
|
+ private void railSettings() {
|
|
+ railActivationRange = getInt("blocks.powered-rail.activation-range", railActivationRange);
|
|
+ }
|
|
+
|
|
+ public boolean respawnAnchorExplode = true;
|
|
+ public double respawnAnchorExplosionPower = 5.0D;
|
|
+ public boolean respawnAnchorExplosionFire = true;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ private void respawnAnchorSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()))) {
|
|
+ set("blocks.respawn_anchor.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ respawnAnchorExplode = getBoolean("blocks.respawn_anchor.explode", respawnAnchorExplode);
|
|
+ respawnAnchorExplosionPower = getDouble("blocks.respawn_anchor.explosion-power", respawnAnchorExplosionPower);
|
|
+ respawnAnchorExplosionFire = getBoolean("blocks.respawn_anchor.explosion-fire", respawnAnchorExplosionFire);
|
|
+ try {
|
|
+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.respawn_anchor.explosion-effect`! Using default of `BLOCK`");
|
|
+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean sculkShriekerCanSummonDefault = false;
|
|
+ private void sculkShriekerSettings() {
|
|
+ sculkShriekerCanSummonDefault = getBoolean("blocks.sculk_shrieker.can-summon-default", sculkShriekerCanSummonDefault);
|
|
+ }
|
|
+
|
|
+ public boolean signAllowColors = false;
|
|
+ private void signSettings() {
|
|
+ signAllowColors = getBoolean("blocks.sign.allow-colors", signAllowColors);
|
|
+ }
|
|
+
|
|
+ public boolean slabHalfBreak = false;
|
|
+ private void slabSettings() {
|
|
+ slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak);
|
|
+ }
|
|
+
|
|
+ public boolean spawnerDeactivateByRedstone = false;
|
|
+ private void spawnerSettings() {
|
|
+ spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone);
|
|
+ }
|
|
+
|
|
+ public int spongeAbsorptionArea = 65;
|
|
+ public int spongeAbsorptionRadius = 6;
|
|
+ public boolean spongeAbsorbsLava = false;
|
|
+ public boolean spongeAbsorbsWaterFromMud = false;
|
|
+ private void spongeSettings() {
|
|
+ spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea);
|
|
+ spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius);
|
|
+ spongeAbsorbsLava = getBoolean("blocks.sponge.absorbs-lava", spongeAbsorbsLava);
|
|
+ spongeAbsorbsWaterFromMud = getBoolean("blocks.sponge.absorbs-water-from-mud", spongeAbsorbsWaterFromMud);
|
|
+ }
|
|
+
|
|
+ public float stonecutterDamage = 0.0F;
|
|
+ private void stonecutterSettings() {
|
|
+ stonecutterDamage = (float) getDouble("blocks.stonecutter.damage", stonecutterDamage);
|
|
+ }
|
|
+
|
|
+ public boolean turtleEggsBreakFromExpOrbs = false;
|
|
+ public boolean turtleEggsBreakFromItems = false;
|
|
+ public boolean turtleEggsBreakFromMinecarts = false;
|
|
+ public boolean turtleEggsBypassMobGriefing = false;
|
|
+ public int turtleEggsRandomTickCrackChance = 500;
|
|
+ public boolean turtleEggsTramplingFeatherFalling = false;
|
|
+ private void turtleEggSettings() {
|
|
+ turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs);
|
|
+ turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems);
|
|
+ turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts);
|
|
+ turtleEggsBypassMobGriefing = getBoolean("blocks.turtle_egg.bypass-mob-griefing", turtleEggsBypassMobGriefing);
|
|
+ turtleEggsRandomTickCrackChance = getInt("blocks.turtle_egg.random-tick-crack-chance", turtleEggsRandomTickCrackChance);
|
|
+ turtleEggsTramplingFeatherFalling = getBoolean("blocks.turtle_egg.feather-fall-distance-affects-trampling", turtleEggsTramplingFeatherFalling);
|
|
+ }
|
|
+
|
|
+ public int waterInfiniteRequiredSources = 2;
|
|
+ private void waterSources() {
|
|
+ waterInfiniteRequiredSources = getInt("blocks.water.infinite-required-sources", waterInfiniteRequiredSources);
|
|
+ }
|
|
+
|
|
+ public boolean babiesAreRidable = true;
|
|
+ public boolean untamedTamablesAreRidable = true;
|
|
+ public boolean useNightVisionWhenRiding = false;
|
|
+ public boolean useDismountsUnderwaterTag = true;
|
|
+ private void ridableSettings() {
|
|
+ babiesAreRidable = getBoolean("ridable-settings.babies-are-ridable", babiesAreRidable);
|
|
+ untamedTamablesAreRidable = getBoolean("ridable-settings.untamed-tamables-are-ridable", untamedTamablesAreRidable);
|
|
+ useNightVisionWhenRiding = getBoolean("ridable-settings.use-night-vision", useNightVisionWhenRiding);
|
|
+ useDismountsUnderwaterTag = getBoolean("ridable-settings.use-dismounts-underwater-tag", useDismountsUnderwaterTag);
|
|
+ }
|
|
+
|
|
+ public boolean allayRidable = false;
|
|
+ public boolean allayRidableInWater = true;
|
|
+ public boolean allayControllable = true;
|
|
+ public double allayMaxHealth = 20.0D;
|
|
+ public double allayScale = 1.0D;
|
|
+ private void allaySettings() {
|
|
+ allayRidable = getBoolean("mobs.allay.ridable", allayRidable);
|
|
+ allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater);
|
|
+ allayControllable = getBoolean("mobs.allay.controllable", allayControllable);
|
|
+ allayMaxHealth = getDouble("mobs.allay.attributes.max_health", allayMaxHealth);
|
|
+ allayScale = Mth.clamp(getDouble("mobs.allay.attributes.scale", allayScale), 0.0625D, 16.0D);
|
|
+ }
|
|
+
|
|
+ public boolean armadilloRidable = false;
|
|
+ public boolean armadilloRidableInWater = true;
|
|
+ public boolean armadilloControllable = true;
|
|
+ public double armadilloMaxHealth = 12.0D;
|
|
+ public double armadilloScale = 1.0D;
|
|
+ public int armadilloBreedingTicks = 6000;
|
|
+ private void armadilloSettings() {
|
|
+ armadilloRidable = getBoolean("mobs.armadillo.ridable", armadilloRidable);
|
|
+ armadilloRidableInWater = getBoolean("mobs.armadillo.ridable-in-water", armadilloRidableInWater);
|
|
+ armadilloControllable = getBoolean("mobs.armadillo.controllable", armadilloControllable);
|
|
+ armadilloMaxHealth = getDouble("mobs.armadillo.attributes.max_health", armadilloMaxHealth);
|
|
+ armadilloScale = Mth.clamp(getDouble("mobs.armadillo.attributes.scale", armadilloScale), 0.0625D, 16.0D);
|
|
+ armadilloBreedingTicks = getInt("mobs.armadillo.breeding-delay-ticks", armadilloBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean axolotlRidable = false;
|
|
+ public boolean axolotlControllable = true;
|
|
+ public double axolotlMaxHealth = 14.0D;
|
|
+ public double axolotlScale = 1.0D;
|
|
+ public int axolotlBreedingTicks = 6000;
|
|
+ public boolean axolotlTakeDamageFromWater = false;
|
|
+ public boolean axolotlAlwaysDropExp = false;
|
|
+ private void axolotlSettings() {
|
|
+ axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable);
|
|
+ axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable);
|
|
+ axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth);
|
|
+ axolotlScale = Mth.clamp(getDouble("mobs.axolotl.attributes.scale", axolotlScale), 0.0625D, 16.0D);
|
|
+ axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks);
|
|
+ axolotlTakeDamageFromWater = getBoolean("mobs.axolotl.takes-damage-from-water", axolotlTakeDamageFromWater);
|
|
+ axolotlAlwaysDropExp = getBoolean("mobs.axolotl.always-drop-exp", axolotlAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean batRidable = false;
|
|
+ public boolean batRidableInWater = true;
|
|
+ public boolean batControllable = true;
|
|
+ public double batMaxY = 320D;
|
|
+ public double batMaxHealth = 6.0D;
|
|
+ public double batScale = 1.0D;
|
|
+ public double batFollowRange = 16.0D;
|
|
+ public double batKnockbackResistance = 0.0D;
|
|
+ public double batMovementSpeed = 0.6D;
|
|
+ public double batFlyingSpeed = 0.6D;
|
|
+ public double batArmor = 0.0D;
|
|
+ public double batArmorToughness = 0.0D;
|
|
+ public double batAttackKnockback = 0.0D;
|
|
+ public boolean batTakeDamageFromWater = false;
|
|
+ public boolean batAlwaysDropExp = false;
|
|
+ private void batSettings() {
|
|
+ batRidable = getBoolean("mobs.bat.ridable", batRidable);
|
|
+ batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater);
|
|
+ batControllable = getBoolean("mobs.bat.controllable", batControllable);
|
|
+ batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.bat.attributes.max-health", batMaxHealth);
|
|
+ set("mobs.bat.attributes.max-health", null);
|
|
+ set("mobs.bat.attributes.max_health", oldValue);
|
|
+ }
|
|
+ batMaxHealth = getDouble("mobs.bat.attributes.max_health", batMaxHealth);
|
|
+ batScale = Mth.clamp(getDouble("mobs.bat.attributes.scale", batScale), 0.0625D, 16.0D);
|
|
+ batFollowRange = getDouble("mobs.bat.attributes.follow_range", batFollowRange);
|
|
+ batKnockbackResistance = getDouble("mobs.bat.attributes.knockback_resistance", batKnockbackResistance);
|
|
+ batMovementSpeed = getDouble("mobs.bat.attributes.movement_speed", batMovementSpeed);
|
|
+ batFlyingSpeed = getDouble("mobs.bat.attributes.flying_speed", batFlyingSpeed);
|
|
+ batArmor = getDouble("mobs.bat.attributes.armor", batArmor);
|
|
+ batArmorToughness = getDouble("mobs.bat.attributes.armor_toughness", batArmorToughness);
|
|
+ batAttackKnockback = getDouble("mobs.bat.attributes.attack_knockback", batAttackKnockback);
|
|
+ batTakeDamageFromWater = getBoolean("mobs.bat.takes-damage-from-water", batTakeDamageFromWater);
|
|
+ batAlwaysDropExp = getBoolean("mobs.bat.always-drop-exp", batAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean beeRidable = false;
|
|
+ public boolean beeRidableInWater = true;
|
|
+ public boolean beeControllable = true;
|
|
+ public double beeMaxY = 320D;
|
|
+ public double beeMaxHealth = 10.0D;
|
|
+ public double beeScale = 1.0D;
|
|
+ public int beeBreedingTicks = 6000;
|
|
+ public boolean beeTakeDamageFromWater = true;
|
|
+ public boolean beeCanWorkAtNight = false;
|
|
+ public boolean beeCanWorkInRain = false;
|
|
+ public boolean beeAlwaysDropExp = false;
|
|
+ public boolean beeDiesAfterSting = true;
|
|
+ private void beeSettings() {
|
|
+ beeRidable = getBoolean("mobs.bee.ridable", beeRidable);
|
|
+ beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater);
|
|
+ beeControllable = getBoolean("mobs.bee.controllable", beeControllable);
|
|
+ beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.bee.attributes.max-health", beeMaxHealth);
|
|
+ set("mobs.bee.attributes.max-health", null);
|
|
+ set("mobs.bee.attributes.max_health", oldValue);
|
|
+ }
|
|
+ beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth);
|
|
+ beeScale = Mth.clamp(getDouble("mobs.bee.attributes.scale", beeScale), 0.0625D, 16.0D);
|
|
+ beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks);
|
|
+ beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater);
|
|
+ beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight);
|
|
+ beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain);
|
|
+ beeAlwaysDropExp = getBoolean("mobs.bee.always-drop-exp", beeAlwaysDropExp);
|
|
+ beeDiesAfterSting = getBoolean("mobs.bee.dies-after-sting", beeDiesAfterSting);
|
|
+ }
|
|
+
|
|
+ public boolean blazeRidable = false;
|
|
+ public boolean blazeRidableInWater = true;
|
|
+ public boolean blazeControllable = true;
|
|
+ public double blazeMaxY = 320D;
|
|
+ public double blazeMaxHealth = 20.0D;
|
|
+ public double blazeScale = 1.0D;
|
|
+ public boolean blazeTakeDamageFromWater = true;
|
|
+ public boolean blazeAlwaysDropExp = false;
|
|
+ private void blazeSettings() {
|
|
+ blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable);
|
|
+ blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater);
|
|
+ blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable);
|
|
+ blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.blaze.attributes.max-health", blazeMaxHealth);
|
|
+ set("mobs.blaze.attributes.max-health", null);
|
|
+ set("mobs.blaze.attributes.max_health", oldValue);
|
|
+ }
|
|
+ blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth);
|
|
+ blazeScale = Mth.clamp(getDouble("mobs.blaze.attributes.scale", blazeScale), 0.0625D, 16.0D);
|
|
+ blazeTakeDamageFromWater = getBoolean("mobs.blaze.takes-damage-from-water", blazeTakeDamageFromWater);
|
|
+ blazeAlwaysDropExp = getBoolean("mobs.blaze.always-drop-exp", blazeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean boggedRidable = false;
|
|
+ public boolean boggedRidableInWater = true;
|
|
+ public boolean boggedControllable = true;
|
|
+ public double boggedMaxHealth = 16.0D;
|
|
+ public double boggedScale = 1.0D;
|
|
+ private void boggedSettings() {
|
|
+ boggedRidable = getBoolean("mobs.bogged.ridable", boggedRidable);
|
|
+ boggedRidableInWater = getBoolean("mobs.bogged.ridable-in-water", boggedRidableInWater);
|
|
+ boggedControllable = getBoolean("mobs.bogged.controllable", boggedControllable);
|
|
+ boggedMaxHealth = getDouble("mobs.bogged.attributes.max_health", boggedMaxHealth);
|
|
+ boggedScale = Mth.clamp(getDouble("mobs.bogged.attributes.scale", boggedScale), 0.0625D, 16.0D);
|
|
+ }
|
|
+
|
|
+ public boolean camelRidableInWater = false;
|
|
+ public double camelMaxHealthMin = 32.0D;
|
|
+ public double camelMaxHealthMax = 32.0D;
|
|
+ public double camelJumpStrengthMin = 0.42D;
|
|
+ public double camelJumpStrengthMax = 0.42D;
|
|
+ public double camelMovementSpeedMin = 0.09D;
|
|
+ public double camelMovementSpeedMax = 0.09D;
|
|
+ public int camelBreedingTicks = 6000;
|
|
+ private void camelSettings() {
|
|
+ camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater);
|
|
+ camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin);
|
|
+ camelMaxHealthMax = getDouble("mobs.camel.attributes.max_health.max", camelMaxHealthMax);
|
|
+ camelJumpStrengthMin = getDouble("mobs.camel.attributes.jump_strength.min", camelJumpStrengthMin);
|
|
+ camelJumpStrengthMax = getDouble("mobs.camel.attributes.jump_strength.max", camelJumpStrengthMax);
|
|
+ camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin);
|
|
+ camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax);
|
|
+ camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean catRidable = false;
|
|
+ public boolean catRidableInWater = true;
|
|
+ public boolean catControllable = true;
|
|
+ public double catMaxHealth = 10.0D;
|
|
+ public double catScale = 1.0D;
|
|
+ public int catSpawnDelay = 1200;
|
|
+ public int catSpawnSwampHutScanRange = 16;
|
|
+ public int catSpawnVillageScanRange = 48;
|
|
+ public int catBreedingTicks = 6000;
|
|
+ public DyeColor catDefaultCollarColor = DyeColor.RED;
|
|
+ public boolean catTakeDamageFromWater = false;
|
|
+ public boolean catAlwaysDropExp = false;
|
|
+ private void catSettings() {
|
|
+ catRidable = getBoolean("mobs.cat.ridable", catRidable);
|
|
+ catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater);
|
|
+ catControllable = getBoolean("mobs.cat.controllable", catControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cat.attributes.max-health", catMaxHealth);
|
|
+ set("mobs.cat.attributes.max-health", null);
|
|
+ set("mobs.cat.attributes.max_health", oldValue);
|
|
+ }
|
|
+ catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth);
|
|
+ catScale = Mth.clamp(getDouble("mobs.cat.attributes.scale", catScale), 0.0625D, 16.0D);
|
|
+ catSpawnDelay = getInt("mobs.cat.spawn-delay", catSpawnDelay);
|
|
+ catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange);
|
|
+ catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange);
|
|
+ catBreedingTicks = getInt("mobs.cat.breeding-delay-ticks", catBreedingTicks);
|
|
+ try {
|
|
+ catDefaultCollarColor = DyeColor.valueOf(getString("mobs.cat.default-collar-color", catDefaultCollarColor.name()));
|
|
+ } catch (IllegalArgumentException ignore) {
|
|
+ catDefaultCollarColor = DyeColor.RED;
|
|
+ }
|
|
+ catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater);
|
|
+ catAlwaysDropExp = getBoolean("mobs.cat.always-drop-exp", catAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean caveSpiderRidable = false;
|
|
+ public boolean caveSpiderRidableInWater = true;
|
|
+ public boolean caveSpiderControllable = true;
|
|
+ public double caveSpiderMaxHealth = 12.0D;
|
|
+ public double caveSpiderScale = 1.0D;
|
|
+ public boolean caveSpiderTakeDamageFromWater = false;
|
|
+ public boolean caveSpiderAlwaysDropExp = false;
|
|
+ private void caveSpiderSettings() {
|
|
+ caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable);
|
|
+ caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater);
|
|
+ caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cave_spider.attributes.max-health", caveSpiderMaxHealth);
|
|
+ set("mobs.cave_spider.attributes.max-health", null);
|
|
+ set("mobs.cave_spider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth);
|
|
+ caveSpiderScale = Mth.clamp(getDouble("mobs.cave_spider.attributes.scale", caveSpiderScale), 0.0625D, 16.0D);
|
|
+ caveSpiderTakeDamageFromWater = getBoolean("mobs.cave_spider.takes-damage-from-water", caveSpiderTakeDamageFromWater);
|
|
+ caveSpiderAlwaysDropExp = getBoolean("mobs.cave_spider.always-drop-exp", caveSpiderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean chickenRidable = false;
|
|
+ public boolean chickenRidableInWater = false;
|
|
+ public boolean chickenControllable = true;
|
|
+ public double chickenMaxHealth = 4.0D;
|
|
+ public double chickenScale = 1.0D;
|
|
+ public boolean chickenRetaliate = false;
|
|
+ public int chickenBreedingTicks = 6000;
|
|
+ public boolean chickenTakeDamageFromWater = false;
|
|
+ public boolean chickenAlwaysDropExp = false;
|
|
+ private void chickenSettings() {
|
|
+ chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable);
|
|
+ chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater);
|
|
+ chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.chicken.attributes.max-health", chickenMaxHealth);
|
|
+ set("mobs.chicken.attributes.max-health", null);
|
|
+ set("mobs.chicken.attributes.max_health", oldValue);
|
|
+ }
|
|
+ chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth);
|
|
+ chickenScale = Mth.clamp(getDouble("mobs.chicken.attributes.scale", chickenScale), 0.0625D, 16.0D);
|
|
+ chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate);
|
|
+ chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks);
|
|
+ chickenTakeDamageFromWater = getBoolean("mobs.chicken.takes-damage-from-water", chickenTakeDamageFromWater);
|
|
+ chickenAlwaysDropExp = getBoolean("mobs.chicken.always-drop-exp", chickenAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean codRidable = false;
|
|
+ public boolean codControllable = true;
|
|
+ public double codMaxHealth = 3.0D;
|
|
+ public double codScale = 1.0D;
|
|
+ public boolean codTakeDamageFromWater = false;
|
|
+ public boolean codAlwaysDropExp = false;
|
|
+ private void codSettings() {
|
|
+ codRidable = getBoolean("mobs.cod.ridable", codRidable);
|
|
+ codControllable = getBoolean("mobs.cod.controllable", codControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cod.attributes.max-health", codMaxHealth);
|
|
+ set("mobs.cod.attributes.max-health", null);
|
|
+ set("mobs.cod.attributes.max_health", oldValue);
|
|
+ }
|
|
+ codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth);
|
|
+ codScale = Mth.clamp(getDouble("mobs.cod.attributes.scale", codScale), 0.0625D, 16.0D);
|
|
+ codTakeDamageFromWater = getBoolean("mobs.cod.takes-damage-from-water", codTakeDamageFromWater);
|
|
+ codAlwaysDropExp = getBoolean("mobs.cod.always-drop-exp", codAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean cowRidable = false;
|
|
+ public boolean cowRidableInWater = true;
|
|
+ public boolean cowControllable = true;
|
|
+ public double cowMaxHealth = 10.0D;
|
|
+ public double cowScale = 1.0D;
|
|
+ public int cowFeedMushrooms = 0;
|
|
+ public int cowBreedingTicks = 6000;
|
|
+ public boolean cowTakeDamageFromWater = false;
|
|
+ public double cowNaturallyAggressiveToPlayersChance = 0.0D;
|
|
+ public double cowNaturallyAggressiveToPlayersDamage = 2.0D;
|
|
+ public boolean cowAlwaysDropExp = false;
|
|
+ private void cowSettings() {
|
|
+ if (PurpurConfig.version < 22) {
|
|
+ double oldValue = getDouble("mobs.cow.naturally-aggressive-to-players-chance", cowNaturallyAggressiveToPlayersChance);
|
|
+ set("mobs.cow.naturally-aggressive-to-players-chance", null);
|
|
+ set("mobs.cow.naturally-aggressive-to-players.chance", oldValue);
|
|
+ }
|
|
+ cowRidable = getBoolean("mobs.cow.ridable", cowRidable);
|
|
+ cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater);
|
|
+ cowControllable = getBoolean("mobs.cow.controllable", cowControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cow.attributes.max-health", cowMaxHealth);
|
|
+ set("mobs.cow.attributes.max-health", null);
|
|
+ set("mobs.cow.attributes.max_health", oldValue);
|
|
+ }
|
|
+ cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth);
|
|
+ cowScale = Mth.clamp(getDouble("mobs.cow.attributes.scale", cowScale), 0.0625D, 16.0D);
|
|
+ cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms);
|
|
+ cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks);
|
|
+ cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater);
|
|
+ cowNaturallyAggressiveToPlayersChance = getDouble("mobs.cow.naturally-aggressive-to-players.chance", cowNaturallyAggressiveToPlayersChance);
|
|
+ cowNaturallyAggressiveToPlayersDamage = getDouble("mobs.cow.naturally-aggressive-to-players.damage", cowNaturallyAggressiveToPlayersDamage);
|
|
+ cowAlwaysDropExp = getBoolean("mobs.cow.always-drop-exp", cowAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean creakingRidable = false;
|
|
+ public boolean creakingRidableInWater = true;
|
|
+ public boolean creakingControllable = true;
|
|
+ public double creakingMaxHealth = 1.0D;
|
|
+ public double creakingScale = 1.0D;
|
|
+ private void creakingSettings() {
|
|
+ creakingRidable = getBoolean("mobs.creaking.ridable", creakingRidable);
|
|
+ creakingRidableInWater = getBoolean("mobs.creaking.ridable-in-water", creakingRidableInWater);
|
|
+ creakingControllable = getBoolean("mobs.creaking.controllable", creakingControllable);
|
|
+ creakingMaxHealth = getDouble("mobs.creaking.attributes.max_health", creakingMaxHealth);
|
|
+ creakingScale = Mth.clamp(getDouble("mobs.creaking.attributes.scale", creakingScale), 0.0625D, 16.0D);
|
|
+ }
|
|
+
|
|
+ public boolean creeperRidable = false;
|
|
+ public boolean creeperRidableInWater = true;
|
|
+ public boolean creeperControllable = true;
|
|
+ public double creeperMaxHealth = 20.0D;
|
|
+ public double creeperScale = 1.0D;
|
|
+ public double creeperChargedChance = 0.0D;
|
|
+ public boolean creeperAllowGriefing = true;
|
|
+ public boolean creeperBypassMobGriefing = false;
|
|
+ public boolean creeperTakeDamageFromWater = false;
|
|
+ public boolean creeperExplodeWhenKilled = false;
|
|
+ public boolean creeperHealthRadius = false;
|
|
+ public boolean creeperAlwaysDropExp = false;
|
|
+ public double creeperHeadVisibilityPercent = 0.5D;
|
|
+ public boolean creeperEncircleTarget = false;
|
|
+ private void creeperSettings() {
|
|
+ creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable);
|
|
+ creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater);
|
|
+ creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.creeper.attributes.max-health", creeperMaxHealth);
|
|
+ set("mobs.creeper.attributes.max-health", null);
|
|
+ set("mobs.creeper.attributes.max_health", oldValue);
|
|
+ }
|
|
+ creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth);
|
|
+ creeperScale = Mth.clamp(getDouble("mobs.creeper.attributes.scale", creeperScale), 0.0625D, 16.0D);
|
|
+ creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance);
|
|
+ creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing);
|
|
+ creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing);
|
|
+ creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater);
|
|
+ creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled);
|
|
+ creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius);
|
|
+ creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp);
|
|
+ creeperHeadVisibilityPercent = getDouble("mobs.creeper.head-visibility-percent", creeperHeadVisibilityPercent);
|
|
+ creeperEncircleTarget = getBoolean("mobs.creeper.encircle-target", creeperEncircleTarget);
|
|
+ }
|
|
+
|
|
+ public boolean dolphinRidable = false;
|
|
+ public boolean dolphinControllable = true;
|
|
+ public int dolphinSpitCooldown = 20;
|
|
+ public float dolphinSpitSpeed = 1.0F;
|
|
+ public float dolphinSpitDamage = 2.0F;
|
|
+ public double dolphinMaxHealth = 10.0D;
|
|
+ public double dolphinScale = 1.0D;
|
|
+ public boolean dolphinDisableTreasureSearching = false;
|
|
+ public boolean dolphinTakeDamageFromWater = false;
|
|
+ public double dolphinNaturallyAggressiveToPlayersChance = 0.0D;
|
|
+ public boolean dolphinAlwaysDropExp = false;
|
|
+ private void dolphinSettings() {
|
|
+ dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable);
|
|
+ dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable);
|
|
+ dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown);
|
|
+ dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed);
|
|
+ dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.dolphin.attributes.max-health", dolphinMaxHealth);
|
|
+ set("mobs.dolphin.attributes.max-health", null);
|
|
+ set("mobs.dolphin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth);
|
|
+ dolphinScale = Mth.clamp(getDouble("mobs.dolphin.attributes.scale", dolphinScale), 0.0625D, 16.0D);
|
|
+ dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching);
|
|
+ dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater);
|
|
+ dolphinNaturallyAggressiveToPlayersChance = getDouble("mobs.dolphin.naturally-aggressive-to-players-chance", dolphinNaturallyAggressiveToPlayersChance);
|
|
+ dolphinAlwaysDropExp = getBoolean("mobs.dolphin.always-drop-exp", dolphinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean donkeyRidableInWater = false;
|
|
+ public double donkeyMaxHealthMin = 15.0D;
|
|
+ public double donkeyMaxHealthMax = 30.0D;
|
|
+ public double donkeyJumpStrengthMin = 0.5D;
|
|
+ public double donkeyJumpStrengthMax = 0.5D;
|
|
+ public double donkeyMovementSpeedMin = 0.175D;
|
|
+ public double donkeyMovementSpeedMax = 0.175D;
|
|
+ public int donkeyBreedingTicks = 6000;
|
|
+ public boolean donkeyTakeDamageFromWater = false;
|
|
+ public boolean donkeyAlwaysDropExp = false;
|
|
+ private void donkeySettings() {
|
|
+ donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.donkey.attributes.max-health.min", donkeyMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.donkey.attributes.max-health.max", donkeyMaxHealthMax);
|
|
+ set("mobs.donkey.attributes.max-health", null);
|
|
+ set("mobs.donkey.attributes.max_health.min", oldMin);
|
|
+ set("mobs.donkey.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ donkeyMaxHealthMin = getDouble("mobs.donkey.attributes.max_health.min", donkeyMaxHealthMin);
|
|
+ donkeyMaxHealthMax = getDouble("mobs.donkey.attributes.max_health.max", donkeyMaxHealthMax);
|
|
+ donkeyJumpStrengthMin = getDouble("mobs.donkey.attributes.jump_strength.min", donkeyJumpStrengthMin);
|
|
+ donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax);
|
|
+ donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin);
|
|
+ donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax);
|
|
+ donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks);
|
|
+ donkeyTakeDamageFromWater = getBoolean("mobs.donkey.takes-damage-from-water", donkeyTakeDamageFromWater);
|
|
+ donkeyAlwaysDropExp = getBoolean("mobs.donkey.always-drop-exp", donkeyAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean drownedRidable = false;
|
|
+ public boolean drownedRidableInWater = true;
|
|
+ public boolean drownedControllable = true;
|
|
+ public double drownedMaxHealth = 20.0D;
|
|
+ public double drownedScale = 1.0D;
|
|
+ public double drownedSpawnReinforcements = 0.1D;
|
|
+ public boolean drownedJockeyOnlyBaby = true;
|
|
+ public double drownedJockeyChance = 0.05D;
|
|
+ public boolean drownedJockeyTryExistingChickens = true;
|
|
+ public boolean drownedTakeDamageFromWater = false;
|
|
+ public boolean drownedBreakDoors = false;
|
|
+ public boolean drownedAlwaysDropExp = false;
|
|
+ private void drownedSettings() {
|
|
+ drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable);
|
|
+ drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater);
|
|
+ drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.drowned.attributes.max-health", drownedMaxHealth);
|
|
+ set("mobs.drowned.attributes.max-health", null);
|
|
+ set("mobs.drowned.attributes.max_health", oldValue);
|
|
+ }
|
|
+ drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth);
|
|
+ drownedScale = Mth.clamp(getDouble("mobs.drowned.attributes.scale", drownedScale), 0.0625D, 16.0D);
|
|
+ drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements);
|
|
+ drownedJockeyOnlyBaby = getBoolean("mobs.drowned.jockey.only-babies", drownedJockeyOnlyBaby);
|
|
+ drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance);
|
|
+ drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens);
|
|
+ drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater);
|
|
+ drownedBreakDoors = getBoolean("mobs.drowned.can-break-doors", drownedBreakDoors);
|
|
+ drownedAlwaysDropExp = getBoolean("mobs.drowned.always-drop-exp", drownedAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean elderGuardianRidable = false;
|
|
+ public boolean elderGuardianControllable = true;
|
|
+ public double elderGuardianMaxHealth = 80.0D;
|
|
+ public double elderGuardianScale = 1.0D;
|
|
+ public boolean elderGuardianTakeDamageFromWater = false;
|
|
+ public boolean elderGuardianAlwaysDropExp = false;
|
|
+ private void elderGuardianSettings() {
|
|
+ elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable);
|
|
+ elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.elder_guardian.attributes.max-health", elderGuardianMaxHealth);
|
|
+ set("mobs.elder_guardian.attributes.max-health", null);
|
|
+ set("mobs.elder_guardian.attributes.max_health", oldValue);
|
|
+ }
|
|
+ elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth);
|
|
+ elderGuardianScale = Mth.clamp(getDouble("mobs.elder_guardian.attributes.scale", elderGuardianScale), 0.0625D, 16.0D);
|
|
+ elderGuardianTakeDamageFromWater = getBoolean("mobs.elder_guardian.takes-damage-from-water", elderGuardianTakeDamageFromWater);
|
|
+ elderGuardianAlwaysDropExp = getBoolean("mobs.elder_guardian.always-drop-exp", elderGuardianAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean enchantmentTableLapisPersists = false;
|
|
+ private void enchantmentTableSettings() {
|
|
+ enchantmentTableLapisPersists = getBoolean("blocks.enchantment-table.lapis-persists", enchantmentTableLapisPersists);
|
|
+ }
|
|
+
|
|
+ public boolean enderDragonRidable = false;
|
|
+ public boolean enderDragonRidableInWater = true;
|
|
+ public boolean enderDragonControllable = true;
|
|
+ public double enderDragonMaxY = 320D;
|
|
+ public double enderDragonMaxHealth = 200.0D;
|
|
+ public boolean enderDragonAlwaysDropsFullExp = false;
|
|
+ public boolean enderDragonBypassMobGriefing = false;
|
|
+ public boolean enderDragonTakeDamageFromWater = false;
|
|
+ public boolean enderDragonCanRideVehicles = false;
|
|
+ private void enderDragonSettings() {
|
|
+ enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable);
|
|
+ enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater);
|
|
+ enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable);
|
|
+ enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.ender_dragon.max-health", enderDragonMaxHealth);
|
|
+ set("mobs.ender_dragon.max-health", null);
|
|
+ set("mobs.ender_dragon.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ender_dragon.attributes.max-health", enderDragonMaxHealth);
|
|
+ set("mobs.ender_dragon.attributes.max-health", null);
|
|
+ set("mobs.ender_dragon.attributes.max_health", oldValue);
|
|
+ }
|
|
+ enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth);
|
|
+ enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp);
|
|
+ enderDragonBypassMobGriefing = getBoolean("mobs.ender_dragon.bypass-mob-griefing", enderDragonBypassMobGriefing);
|
|
+ enderDragonTakeDamageFromWater = getBoolean("mobs.ender_dragon.takes-damage-from-water", enderDragonTakeDamageFromWater);
|
|
+ enderDragonCanRideVehicles = getBoolean("mobs.ender_dragon.can-ride-vehicles", enderDragonCanRideVehicles);
|
|
+ }
|
|
+
|
|
+ public boolean endermanRidable = false;
|
|
+ public boolean endermanRidableInWater = true;
|
|
+ public boolean endermanControllable = true;
|
|
+ public double endermanMaxHealth = 40.0D;
|
|
+ public double endermanScale = 1.0D;
|
|
+ public boolean endermanAllowGriefing = true;
|
|
+ public boolean endermanDespawnEvenWithBlock = false;
|
|
+ public boolean endermanBypassMobGriefing = false;
|
|
+ public boolean endermanTakeDamageFromWater = true;
|
|
+ public boolean endermanAggroEndermites = true;
|
|
+ public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false;
|
|
+ public boolean endermanDisableStareAggro = false;
|
|
+ public boolean endermanIgnoreProjectiles = false;
|
|
+ public boolean endermanAlwaysDropExp = false;
|
|
+ private void endermanSettings() {
|
|
+ endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable);
|
|
+ endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater);
|
|
+ endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.enderman.attributes.max-health", endermanMaxHealth);
|
|
+ set("mobs.enderman.attributes.max-health", null);
|
|
+ set("mobs.enderman.attributes.max_health", oldValue);
|
|
+ }
|
|
+ if (PurpurConfig.version < 15) {
|
|
+ // remove old option
|
|
+ set("mobs.enderman.aggressive-towards-spawned-endermites", null);
|
|
+ }
|
|
+ endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth);
|
|
+ endermanScale = Mth.clamp(getDouble("mobs.enderman.attributes.scale", endermanScale), 0.0625D, 16.0D);
|
|
+ endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing);
|
|
+ endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock);
|
|
+ endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing);
|
|
+ endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater);
|
|
+ endermanAggroEndermites = getBoolean("mobs.enderman.aggressive-towards-endermites", endermanAggroEndermites);
|
|
+ endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned);
|
|
+ endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro);
|
|
+ endermanIgnoreProjectiles = getBoolean("mobs.enderman.ignore-projectiles", endermanIgnoreProjectiles);
|
|
+ endermanAlwaysDropExp = getBoolean("mobs.enderman.always-drop-exp", endermanAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean endermiteRidable = false;
|
|
+ public boolean endermiteRidableInWater = true;
|
|
+ public boolean endermiteControllable = true;
|
|
+ public double endermiteMaxHealth = 8.0D;
|
|
+ public double endermiteScale = 1.0D;
|
|
+ public boolean endermiteTakeDamageFromWater = false;
|
|
+ public boolean endermiteAlwaysDropExp = false;
|
|
+ private void endermiteSettings() {
|
|
+ endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable);
|
|
+ endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater);
|
|
+ endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.endermite.attributes.max-health", endermiteMaxHealth);
|
|
+ set("mobs.endermite.attributes.max-health", null);
|
|
+ set("mobs.endermite.attributes.max_health", oldValue);
|
|
+ }
|
|
+ endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth);
|
|
+ endermiteScale = Mth.clamp(getDouble("mobs.endermite.attributes.scale", endermiteScale), 0.0625D, 16.0D);
|
|
+ endermiteTakeDamageFromWater = getBoolean("mobs.endermite.takes-damage-from-water", endermiteTakeDamageFromWater);
|
|
+ endermiteAlwaysDropExp = getBoolean("mobs.endermite.always-drop-exp", endermiteAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean evokerRidable = false;
|
|
+ public boolean evokerRidableInWater = true;
|
|
+ public boolean evokerControllable = true;
|
|
+ public double evokerMaxHealth = 24.0D;
|
|
+ public double evokerScale = 1.0D;
|
|
+ public boolean evokerBypassMobGriefing = false;
|
|
+ public boolean evokerTakeDamageFromWater = false;
|
|
+ public boolean evokerAlwaysDropExp = false;
|
|
+ private void evokerSettings() {
|
|
+ evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable);
|
|
+ evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater);
|
|
+ evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.evoker.attributes.max-health", evokerMaxHealth);
|
|
+ set("mobs.evoker.attributes.max-health", null);
|
|
+ set("mobs.evoker.attributes.max_health", oldValue);
|
|
+ }
|
|
+ evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth);
|
|
+ evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D);
|
|
+ evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing);
|
|
+ evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater);
|
|
+ evokerAlwaysDropExp = getBoolean("mobs.evoker.always-drop-exp", evokerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean foxRidable = false;
|
|
+ public boolean foxRidableInWater = true;
|
|
+ public boolean foxControllable = true;
|
|
+ public double foxMaxHealth = 10.0D;
|
|
+ public double foxScale = 1.0D;
|
|
+ public boolean foxTypeChangesWithTulips = false;
|
|
+ public int foxBreedingTicks = 6000;
|
|
+ public boolean foxBypassMobGriefing = false;
|
|
+ public boolean foxTakeDamageFromWater = false;
|
|
+ public boolean foxAlwaysDropExp = false;
|
|
+ private void foxSettings() {
|
|
+ foxRidable = getBoolean("mobs.fox.ridable", foxRidable);
|
|
+ foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater);
|
|
+ foxControllable = getBoolean("mobs.fox.controllable", foxControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.fox.attributes.max-health", foxMaxHealth);
|
|
+ set("mobs.fox.attributes.max-health", null);
|
|
+ set("mobs.fox.attributes.max_health", oldValue);
|
|
+ }
|
|
+ foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth);
|
|
+ foxScale = Mth.clamp(getDouble("mobs.fox.attributes.scale", foxScale), 0.0625D, 16.0D);
|
|
+ foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips);
|
|
+ foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks);
|
|
+ foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing);
|
|
+ foxTakeDamageFromWater = getBoolean("mobs.fox.takes-damage-from-water", foxTakeDamageFromWater);
|
|
+ foxAlwaysDropExp = getBoolean("mobs.fox.always-drop-exp", foxAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean frogRidable = false;
|
|
+ public boolean frogRidableInWater = true;
|
|
+ public boolean frogControllable = true;
|
|
+ public float frogRidableJumpHeight = 0.65F;
|
|
+ public int frogBreedingTicks = 6000;
|
|
+ private void frogSettings() {
|
|
+ frogRidable = getBoolean("mobs.frog.ridable", frogRidable);
|
|
+ frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater);
|
|
+ frogControllable = getBoolean("mobs.frog.controllable", frogControllable);
|
|
+ frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight);
|
|
+ frogBreedingTicks = getInt("mobs.frog.breeding-delay-ticks", frogBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean ghastRidable = false;
|
|
+ public boolean ghastRidableInWater = true;
|
|
+ public boolean ghastControllable = true;
|
|
+ public double ghastMaxY = 320D;
|
|
+ public double ghastMaxHealth = 10.0D;
|
|
+ public double ghastScale = 1.0D;
|
|
+ public boolean ghastTakeDamageFromWater = false;
|
|
+ public boolean ghastAlwaysDropExp = false;
|
|
+ private void ghastSettings() {
|
|
+ ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable);
|
|
+ ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater);
|
|
+ ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable);
|
|
+ ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ghast.attributes.max-health", ghastMaxHealth);
|
|
+ set("mobs.ghast.attributes.max-health", null);
|
|
+ set("mobs.ghast.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth);
|
|
+ ghastScale = Mth.clamp(getDouble("mobs.ghast.attributes.scale", ghastScale), 0.0625D, 16.0D);
|
|
+ ghastTakeDamageFromWater = getBoolean("mobs.ghast.takes-damage-from-water", ghastTakeDamageFromWater);
|
|
+ ghastAlwaysDropExp = getBoolean("mobs.ghast.always-drop-exp", ghastAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean giantRidable = false;
|
|
+ public boolean giantRidableInWater = true;
|
|
+ public boolean giantControllable = true;
|
|
+ public double giantMovementSpeed = 0.5D;
|
|
+ public double giantAttackDamage = 50.0D;
|
|
+ public double giantMaxHealth = 100.0D;
|
|
+ public double giantScale = 1.0D;
|
|
+ public float giantStepHeight = 2.0F;
|
|
+ public float giantJumpHeight = 1.0F;
|
|
+ public boolean giantHaveAI = false;
|
|
+ public boolean giantHaveHostileAI = false;
|
|
+ public boolean giantTakeDamageFromWater = false;
|
|
+ public boolean giantAlwaysDropExp = false;
|
|
+ private void giantSettings() {
|
|
+ giantRidable = getBoolean("mobs.giant.ridable", giantRidable);
|
|
+ giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater);
|
|
+ giantControllable = getBoolean("mobs.giant.controllable", giantControllable);
|
|
+ giantMovementSpeed = getDouble("mobs.giant.movement-speed", giantMovementSpeed);
|
|
+ giantAttackDamage = getDouble("mobs.giant.attack-damage", giantAttackDamage);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.giant.max-health", giantMaxHealth);
|
|
+ set("mobs.giant.max-health", null);
|
|
+ set("mobs.giant.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.giant.attributes.max-health", giantMaxHealth);
|
|
+ set("mobs.giant.attributes.max-health", null);
|
|
+ set("mobs.giant.attributes.max_health", oldValue);
|
|
+ }
|
|
+ giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth);
|
|
+ giantScale = Mth.clamp(getDouble("mobs.giant.attributes.scale", giantScale), 0.0625D, 16.0D);
|
|
+ giantStepHeight = (float) getDouble("mobs.giant.step-height", giantStepHeight);
|
|
+ giantJumpHeight = (float) getDouble("mobs.giant.jump-height", giantJumpHeight);
|
|
+ giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI);
|
|
+ giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI);
|
|
+ giantTakeDamageFromWater = getBoolean("mobs.giant.takes-damage-from-water", giantTakeDamageFromWater);
|
|
+ giantAlwaysDropExp = getBoolean("mobs.giant.always-drop-exp", giantAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean glowSquidRidable = false;
|
|
+ public boolean glowSquidControllable = true;
|
|
+ public double glowSquidMaxHealth = 10.0D;
|
|
+ public double glowSquidScale = 1.0D;
|
|
+ public boolean glowSquidsCanFly = false;
|
|
+ public boolean glowSquidTakeDamageFromWater = false;
|
|
+ public boolean glowSquidAlwaysDropExp = false;
|
|
+ private void glowSquidSettings() {
|
|
+ glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable);
|
|
+ glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable);
|
|
+ glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth);
|
|
+ glowSquidScale = Mth.clamp(getDouble("mobs.glow_squid.attributes.scale", glowSquidScale), 0.0625D, 16.0D);
|
|
+ glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly);
|
|
+ glowSquidTakeDamageFromWater = getBoolean("mobs.glow_squid.takes-damage-from-water", glowSquidTakeDamageFromWater);
|
|
+ glowSquidAlwaysDropExp = getBoolean("mobs.glow_squid.always-drop-exp", glowSquidAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean goatRidable = false;
|
|
+ public boolean goatRidableInWater = true;
|
|
+ public boolean goatControllable = true;
|
|
+ public double goatMaxHealth = 10.0D;
|
|
+ public double goatScale = 1.0D;
|
|
+ public int goatBreedingTicks = 6000;
|
|
+ public boolean goatTakeDamageFromWater = false;
|
|
+ public boolean goatAlwaysDropExp = false;
|
|
+ private void goatSettings() {
|
|
+ goatRidable = getBoolean("mobs.goat.ridable", goatRidable);
|
|
+ goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater);
|
|
+ goatControllable = getBoolean("mobs.goat.controllable", goatControllable);
|
|
+ goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth);
|
|
+ goatScale = Mth.clamp(getDouble("mobs.goat.attributes.scale", goatScale), 0.0625D, 16.0D);
|
|
+ goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks);
|
|
+ goatTakeDamageFromWater = getBoolean("mobs.goat.takes-damage-from-water", goatTakeDamageFromWater);
|
|
+ goatAlwaysDropExp = getBoolean("mobs.goat.always-drop-exp", goatAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean guardianRidable = false;
|
|
+ public boolean guardianControllable = true;
|
|
+ public double guardianMaxHealth = 30.0D;
|
|
+ public double guardianScale = 1.0D;
|
|
+ public boolean guardianTakeDamageFromWater = false;
|
|
+ public boolean guardianAlwaysDropExp = false;
|
|
+ private void guardianSettings() {
|
|
+ guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable);
|
|
+ guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.guardian.attributes.max-health", guardianMaxHealth);
|
|
+ set("mobs.guardian.attributes.max-health", null);
|
|
+ set("mobs.guardian.attributes.max_health", oldValue);
|
|
+ }
|
|
+ guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth);
|
|
+ guardianScale = Mth.clamp(getDouble("mobs.guardian.attributes.scale", guardianScale), 0.0625D, 16.0D);
|
|
+ guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater);
|
|
+ guardianAlwaysDropExp = getBoolean("mobs.guardian.always-drop-exp", guardianAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean forceHalloweenSeason = false;
|
|
+ public float chanceHeadHalloweenOnEntity = 0.25F;
|
|
+ private void halloweenSetting() {
|
|
+ forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason);
|
|
+ chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity);
|
|
+ }
|
|
+
|
|
+ public boolean hoglinRidable = false;
|
|
+ public boolean hoglinRidableInWater = true;
|
|
+ public boolean hoglinControllable = true;
|
|
+ public double hoglinMaxHealth = 40.0D;
|
|
+ public double hoglinScale = 1.0D;
|
|
+ public int hoglinBreedingTicks = 6000;
|
|
+ public boolean hoglinTakeDamageFromWater = false;
|
|
+ public boolean hoglinAlwaysDropExp = false;
|
|
+ private void hoglinSettings() {
|
|
+ hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable);
|
|
+ hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater);
|
|
+ hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.hoglin.attributes.max-health", hoglinMaxHealth);
|
|
+ set("mobs.hoglin.attributes.max-health", null);
|
|
+ set("mobs.hoglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth);
|
|
+ hoglinScale = Mth.clamp(getDouble("mobs.hoglin.attributes.scale", hoglinScale), 0.0625D, 16.0D);
|
|
+ hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks);
|
|
+ hoglinTakeDamageFromWater = getBoolean("mobs.hoglin.takes-damage-from-water", hoglinTakeDamageFromWater);
|
|
+ hoglinAlwaysDropExp = getBoolean("mobs.hoglin.always-drop-exp", hoglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean horseRidableInWater = false;
|
|
+ public double horseMaxHealthMin = 15.0D;
|
|
+ public double horseMaxHealthMax = 30.0D;
|
|
+ public double horseJumpStrengthMin = 0.4D;
|
|
+ public double horseJumpStrengthMax = 1.0D;
|
|
+ public double horseMovementSpeedMin = 0.1125D;
|
|
+ public double horseMovementSpeedMax = 0.3375D;
|
|
+ public int horseBreedingTicks = 6000;
|
|
+ public boolean horseTakeDamageFromWater = false;
|
|
+ public boolean horseAlwaysDropExp = false;
|
|
+ private void horseSettings() {
|
|
+ horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.horse.attributes.max-health.min", horseMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.horse.attributes.max-health.max", horseMaxHealthMax);
|
|
+ set("mobs.horse.attributes.max-health", null);
|
|
+ set("mobs.horse.attributes.max_health.min", oldMin);
|
|
+ set("mobs.horse.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ horseMaxHealthMin = getDouble("mobs.horse.attributes.max_health.min", horseMaxHealthMin);
|
|
+ horseMaxHealthMax = getDouble("mobs.horse.attributes.max_health.max", horseMaxHealthMax);
|
|
+ horseJumpStrengthMin = getDouble("mobs.horse.attributes.jump_strength.min", horseJumpStrengthMin);
|
|
+ horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax);
|
|
+ horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin);
|
|
+ horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax);
|
|
+ horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks);
|
|
+ horseTakeDamageFromWater = getBoolean("mobs.horse.takes-damage-from-water", horseTakeDamageFromWater);
|
|
+ horseAlwaysDropExp = getBoolean("mobs.horse.always-drop-exp", horseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean huskRidable = false;
|
|
+ public boolean huskRidableInWater = true;
|
|
+ public boolean huskControllable = true;
|
|
+ public double huskMaxHealth = 20.0D;
|
|
+ public double huskScale = 1.0D;
|
|
+ public double huskSpawnReinforcements = 0.1D;
|
|
+ public boolean huskJockeyOnlyBaby = true;
|
|
+ public double huskJockeyChance = 0.05D;
|
|
+ public boolean huskJockeyTryExistingChickens = true;
|
|
+ public boolean huskTakeDamageFromWater = false;
|
|
+ public boolean huskAlwaysDropExp = false;
|
|
+ private void huskSettings() {
|
|
+ huskRidable = getBoolean("mobs.husk.ridable", huskRidable);
|
|
+ huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater);
|
|
+ huskControllable = getBoolean("mobs.husk.controllable", huskControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.husk.attributes.max-health", huskMaxHealth);
|
|
+ set("mobs.husk.attributes.max-health", null);
|
|
+ set("mobs.husk.attributes.max_health", oldValue);
|
|
+ }
|
|
+ huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth);
|
|
+ huskScale = Mth.clamp(getDouble("mobs.husk.attributes.scale", huskScale), 0.0625D, 16.0D);
|
|
+ huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements);
|
|
+ huskJockeyOnlyBaby = getBoolean("mobs.husk.jockey.only-babies", huskJockeyOnlyBaby);
|
|
+ huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance);
|
|
+ huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens);
|
|
+ huskTakeDamageFromWater = getBoolean("mobs.husk.takes-damage-from-water", huskTakeDamageFromWater);
|
|
+ huskAlwaysDropExp = getBoolean("mobs.husk.always-drop-exp", huskAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean illusionerRidable = false;
|
|
+ public boolean illusionerRidableInWater = true;
|
|
+ public boolean illusionerControllable = true;
|
|
+ public double illusionerMovementSpeed = 0.5D;
|
|
+ public double illusionerFollowRange = 18.0D;
|
|
+ public double illusionerMaxHealth = 32.0D;
|
|
+ public double illusionerScale = 1.0D;
|
|
+ public boolean illusionerTakeDamageFromWater = false;
|
|
+ public boolean illusionerAlwaysDropExp = false;
|
|
+ private void illusionerSettings() {
|
|
+ illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable);
|
|
+ illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater);
|
|
+ illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable);
|
|
+ illusionerMovementSpeed = getDouble("mobs.illusioner.movement-speed", illusionerMovementSpeed);
|
|
+ illusionerFollowRange = getDouble("mobs.illusioner.follow-range", illusionerFollowRange);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.illusioner.max-health", illusionerMaxHealth);
|
|
+ set("mobs.illusioner.max-health", null);
|
|
+ set("mobs.illusioner.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.illusioner.attributes.max-health", illusionerMaxHealth);
|
|
+ set("mobs.illusioner.attributes.max-health", null);
|
|
+ set("mobs.illusioner.attributes.max_health", oldValue);
|
|
+ }
|
|
+ illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth);
|
|
+ illusionerScale = Mth.clamp(getDouble("mobs.illusioner.attributes.scale", illusionerScale), 0.0625D, 16.0D);
|
|
+ illusionerTakeDamageFromWater = getBoolean("mobs.illusioner.takes-damage-from-water", illusionerTakeDamageFromWater);
|
|
+ illusionerAlwaysDropExp = getBoolean("mobs.illusioner.always-drop-exp", illusionerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ironGolemRidable = false;
|
|
+ public boolean ironGolemRidableInWater = true;
|
|
+ public boolean ironGolemControllable = true;
|
|
+ public boolean ironGolemCanSwim = false;
|
|
+ public double ironGolemMaxHealth = 100.0D;
|
|
+ public double ironGolemScale = 1.0D;
|
|
+ public boolean ironGolemTakeDamageFromWater = false;
|
|
+ public boolean ironGolemPoppyCalm = false;
|
|
+ public boolean ironGolemHealCalm = false;
|
|
+ public boolean ironGolemAlwaysDropExp = false;
|
|
+ private void ironGolemSettings() {
|
|
+ ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable);
|
|
+ ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater);
|
|
+ ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable);
|
|
+ ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.iron_golem.attributes.max-health", ironGolemMaxHealth);
|
|
+ set("mobs.iron_golem.attributes.max-health", null);
|
|
+ set("mobs.iron_golem.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth);
|
|
+ ironGolemScale = Mth.clamp(getDouble("mobs.iron_golem.attributes.scale", ironGolemScale), 0.0625D, 16.0D);
|
|
+ ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater);
|
|
+ ironGolemPoppyCalm = getBoolean("mobs.iron_golem.poppy-calms-anger", ironGolemPoppyCalm);
|
|
+ ironGolemHealCalm = getBoolean("mobs.iron_golem.healing-calms-anger", ironGolemHealCalm);
|
|
+ ironGolemAlwaysDropExp = getBoolean("mobs.iron_golem.always-drop-exp", ironGolemAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean llamaRidable = false;
|
|
+ public boolean llamaRidableInWater = false;
|
|
+ public boolean llamaControllable = true;
|
|
+ public double llamaMaxHealthMin = 15.0D;
|
|
+ public double llamaMaxHealthMax = 30.0D;
|
|
+ public double llamaJumpStrengthMin = 0.5D;
|
|
+ public double llamaJumpStrengthMax = 0.5D;
|
|
+ public double llamaMovementSpeedMin = 0.175D;
|
|
+ public double llamaMovementSpeedMax = 0.175D;
|
|
+ public int llamaBreedingTicks = 6000;
|
|
+ public boolean llamaTakeDamageFromWater = false;
|
|
+ public boolean llamaJoinCaravans = true;
|
|
+ public boolean llamaAlwaysDropExp = false;
|
|
+ private void llamaSettings() {
|
|
+ llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable);
|
|
+ llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater);
|
|
+ llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.llama.attributes.max-health.min", llamaMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.llama.attributes.max-health.max", llamaMaxHealthMax);
|
|
+ set("mobs.llama.attributes.max-health", null);
|
|
+ set("mobs.llama.attributes.max_health.min", oldMin);
|
|
+ set("mobs.llama.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ llamaMaxHealthMin = getDouble("mobs.llama.attributes.max_health.min", llamaMaxHealthMin);
|
|
+ llamaMaxHealthMax = getDouble("mobs.llama.attributes.max_health.max", llamaMaxHealthMax);
|
|
+ llamaJumpStrengthMin = getDouble("mobs.llama.attributes.jump_strength.min", llamaJumpStrengthMin);
|
|
+ llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax);
|
|
+ llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin);
|
|
+ llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax);
|
|
+ llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks);
|
|
+ llamaTakeDamageFromWater = getBoolean("mobs.llama.takes-damage-from-water", llamaTakeDamageFromWater);
|
|
+ llamaJoinCaravans = getBoolean("mobs.llama.join-caravans", llamaJoinCaravans);
|
|
+ llamaAlwaysDropExp = getBoolean("mobs.llama.always-drop-exp", llamaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean magmaCubeRidable = false;
|
|
+ public boolean magmaCubeRidableInWater = true;
|
|
+ public boolean magmaCubeControllable = true;
|
|
+ public String magmaCubeMaxHealth = "size * size";
|
|
+ public String magmaCubeAttackDamage = "size";
|
|
+ public Map<Integer, Double> magmaCubeMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> magmaCubeAttackDamageCache = new HashMap<>();
|
|
+ public boolean magmaCubeTakeDamageFromWater = false;
|
|
+ public boolean magmaCubeAlwaysDropExp = false;
|
|
+ private void magmaCubeSettings() {
|
|
+ magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable);
|
|
+ magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater);
|
|
+ magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ String oldValue = getString("mobs.magma_cube.attributes.max-health", magmaCubeMaxHealth);
|
|
+ set("mobs.magma_cube.attributes.max-health", null);
|
|
+ set("mobs.magma_cube.attributes.max_health", oldValue);
|
|
+ }
|
|
+ magmaCubeMaxHealth = getString("mobs.magma_cube.attributes.max_health", magmaCubeMaxHealth);
|
|
+ magmaCubeAttackDamage = getString("mobs.magma_cube.attributes.attack_damage", magmaCubeAttackDamage);
|
|
+ magmaCubeMaxHealthCache.clear();
|
|
+ magmaCubeAttackDamageCache.clear();
|
|
+ magmaCubeTakeDamageFromWater = getBoolean("mobs.magma_cube.takes-damage-from-water", magmaCubeTakeDamageFromWater);
|
|
+ magmaCubeAlwaysDropExp = getBoolean("mobs.magma_cube.always-drop-exp", magmaCubeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean mooshroomRidable = false;
|
|
+ public boolean mooshroomRidableInWater = true;
|
|
+ public boolean mooshroomControllable = true;
|
|
+ public double mooshroomMaxHealth = 10.0D;
|
|
+ public double mooshroomScale = 1.0D;
|
|
+ public int mooshroomBreedingTicks = 6000;
|
|
+ public boolean mooshroomTakeDamageFromWater = false;
|
|
+ public boolean mooshroomAlwaysDropExp = false;
|
|
+ private void mooshroomSettings() {
|
|
+ mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable);
|
|
+ mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater);
|
|
+ mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.mooshroom.attributes.max-health", mooshroomMaxHealth);
|
|
+ set("mobs.mooshroom.attributes.max-health", null);
|
|
+ set("mobs.mooshroom.attributes.max_health", oldValue);
|
|
+ }
|
|
+ mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth);
|
|
+ mooshroomScale = Mth.clamp(getDouble("mobs.mooshroom.attributes.scale", mooshroomScale), 0.0625D, 16.0D);
|
|
+ mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks);
|
|
+ mooshroomTakeDamageFromWater = getBoolean("mobs.mooshroom.takes-damage-from-water", mooshroomTakeDamageFromWater);
|
|
+ mooshroomAlwaysDropExp = getBoolean("mobs.mooshroom.always-drop-exp", mooshroomAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean muleRidableInWater = false;
|
|
+ public double muleMaxHealthMin = 15.0D;
|
|
+ public double muleMaxHealthMax = 30.0D;
|
|
+ public double muleJumpStrengthMin = 0.5D;
|
|
+ public double muleJumpStrengthMax = 0.5D;
|
|
+ public double muleMovementSpeedMin = 0.175D;
|
|
+ public double muleMovementSpeedMax = 0.175D;
|
|
+ public int muleBreedingTicks = 6000;
|
|
+ public boolean muleTakeDamageFromWater = false;
|
|
+ public boolean muleAlwaysDropExp = false;
|
|
+ private void muleSettings() {
|
|
+ muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.mule.attributes.max-health.min", muleMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.mule.attributes.max-health.max", muleMaxHealthMax);
|
|
+ set("mobs.mule.attributes.max-health", null);
|
|
+ set("mobs.mule.attributes.max_health.min", oldMin);
|
|
+ set("mobs.mule.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ muleMaxHealthMin = getDouble("mobs.mule.attributes.max_health.min", muleMaxHealthMin);
|
|
+ muleMaxHealthMax = getDouble("mobs.mule.attributes.max_health.max", muleMaxHealthMax);
|
|
+ muleJumpStrengthMin = getDouble("mobs.mule.attributes.jump_strength.min", muleJumpStrengthMin);
|
|
+ muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax);
|
|
+ muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin);
|
|
+ muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax);
|
|
+ muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks);
|
|
+ muleTakeDamageFromWater = getBoolean("mobs.mule.takes-damage-from-water", muleTakeDamageFromWater);
|
|
+ muleAlwaysDropExp = getBoolean("mobs.mule.always-drop-exp", muleAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ocelotRidable = false;
|
|
+ public boolean ocelotRidableInWater = true;
|
|
+ public boolean ocelotControllable = true;
|
|
+ public double ocelotMaxHealth = 10.0D;
|
|
+ public double ocelotScale = 1.0D;
|
|
+ public int ocelotBreedingTicks = 6000;
|
|
+ public boolean ocelotTakeDamageFromWater = false;
|
|
+ public boolean ocelotAlwaysDropExp = false;
|
|
+ public boolean ocelotSpawnUnderSeaLevel = false;
|
|
+ private void ocelotSettings() {
|
|
+ ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable);
|
|
+ ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater);
|
|
+ ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ocelot.attributes.max-health", ocelotMaxHealth);
|
|
+ set("mobs.ocelot.attributes.max-health", null);
|
|
+ set("mobs.ocelot.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth);
|
|
+ ocelotScale = Mth.clamp(getDouble("mobs.ocelot.attributes.scale", ocelotScale), 0.0625D, 16.0D);
|
|
+ ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks);
|
|
+ ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater);
|
|
+ ocelotAlwaysDropExp = getBoolean("mobs.ocelot.always-drop-exp", ocelotAlwaysDropExp);
|
|
+ ocelotSpawnUnderSeaLevel = getBoolean("mobs.ocelot.spawn-below-sea-level", ocelotSpawnUnderSeaLevel);
|
|
+ }
|
|
+
|
|
+ public boolean pandaRidable = false;
|
|
+ public boolean pandaRidableInWater = true;
|
|
+ public boolean pandaControllable = true;
|
|
+ public double pandaMaxHealth = 20.0D;
|
|
+ public double pandaScale = 1.0D;
|
|
+ public int pandaBreedingTicks = 6000;
|
|
+ public boolean pandaTakeDamageFromWater = false;
|
|
+ public boolean pandaAlwaysDropExp = false;
|
|
+ private void pandaSettings() {
|
|
+ pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable);
|
|
+ pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater);
|
|
+ pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.panda.attributes.max-health", pandaMaxHealth);
|
|
+ set("mobs.panda.attributes.max-health", null);
|
|
+ set("mobs.panda.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth);
|
|
+ pandaScale = Mth.clamp(getDouble("mobs.panda.attributes.scale", pandaScale), 0.0625D, 16.0D);
|
|
+ pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks);
|
|
+ pandaTakeDamageFromWater = getBoolean("mobs.panda.takes-damage-from-water", pandaTakeDamageFromWater);
|
|
+ pandaAlwaysDropExp = getBoolean("mobs.panda.always-drop-exp", pandaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean parrotRidable = false;
|
|
+ public boolean parrotRidableInWater = true;
|
|
+ public boolean parrotControllable = true;
|
|
+ public double parrotMaxY = 320D;
|
|
+ public double parrotMaxHealth = 6.0D;
|
|
+ public double parrotScale = 1.0D;
|
|
+ public boolean parrotTakeDamageFromWater = false;
|
|
+ public boolean parrotBreedable = false;
|
|
+ public boolean parrotAlwaysDropExp = false;
|
|
+ private void parrotSettings() {
|
|
+ parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable);
|
|
+ parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater);
|
|
+ parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable);
|
|
+ parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.parrot.attributes.max-health", parrotMaxHealth);
|
|
+ set("mobs.parrot.attributes.max-health", null);
|
|
+ set("mobs.parrot.attributes.max_health", oldValue);
|
|
+ }
|
|
+ parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth);
|
|
+ parrotScale = Mth.clamp(getDouble("mobs.parrot.attributes.scale", parrotScale), 0.0625D, 16.0D);
|
|
+ parrotTakeDamageFromWater = getBoolean("mobs.parrot.takes-damage-from-water", parrotTakeDamageFromWater);
|
|
+ parrotBreedable = getBoolean("mobs.parrot.can-breed", parrotBreedable);
|
|
+ parrotAlwaysDropExp = getBoolean("mobs.parrot.always-drop-exp", parrotAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean phantomRidable = false;
|
|
+ public boolean phantomRidableInWater = true;
|
|
+ public boolean phantomControllable = true;
|
|
+ public double phantomMaxY = 320D;
|
|
+ public float phantomFlameDamage = 1.0F;
|
|
+ public int phantomFlameFireTime = 8;
|
|
+ public boolean phantomAllowGriefing = false;
|
|
+ public String phantomMaxHealth = "20.0";
|
|
+ public String phantomAttackDamage = "6 + size";
|
|
+ public Map<Integer, Double> phantomMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> phantomAttackDamageCache = new HashMap<>();
|
|
+ public double phantomAttackedByCrystalRadius = 0.0D;
|
|
+ public float phantomAttackedByCrystalDamage = 1.0F;
|
|
+ public double phantomOrbitCrystalRadius = 0.0D;
|
|
+ public int phantomSpawnMinSkyDarkness = 5;
|
|
+ public boolean phantomSpawnOnlyAboveSeaLevel = true;
|
|
+ public boolean phantomSpawnOnlyWithVisibleSky = true;
|
|
+ public double phantomSpawnLocalDifficultyChance = 3.0D;
|
|
+ public int phantomSpawnMinPerAttempt = 1;
|
|
+ public int phantomSpawnMaxPerAttempt = -1;
|
|
+ public int phantomBurnInLight = 0;
|
|
+ public boolean phantomIgnorePlayersWithTorch = false;
|
|
+ public boolean phantomBurnInDaylight = true;
|
|
+ public boolean phantomFlamesOnSwoop = false;
|
|
+ public boolean phantomTakeDamageFromWater = false;
|
|
+ public boolean phantomAlwaysDropExp = false;
|
|
+ public int phantomMinSize = 0;
|
|
+ public int phantomMaxSize = 0;
|
|
+ private void phantomSettings() {
|
|
+ phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable);
|
|
+ phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater);
|
|
+ phantomControllable = getBoolean("mobs.phantom.controllable", phantomControllable);
|
|
+ phantomMaxY = getDouble("mobs.phantom.ridable-max-y", phantomMaxY);
|
|
+ phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage);
|
|
+ phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime);
|
|
+ phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.phantom.attributes.max-health", Double.parseDouble(phantomMaxHealth));
|
|
+ set("mobs.phantom.attributes.max-health", null);
|
|
+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue));
|
|
+ }
|
|
+ if (PurpurConfig.version < 25) {
|
|
+ double oldValue = getDouble("mobs.phantom.attributes.max_health", Double.parseDouble(phantomMaxHealth));
|
|
+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue));
|
|
+ }
|
|
+ phantomMaxHealth = getString("mobs.phantom.attributes.max_health", phantomMaxHealth);
|
|
+ phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage);
|
|
+ phantomMaxHealthCache.clear();
|
|
+ phantomAttackDamageCache.clear();
|
|
+ phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius);
|
|
+ phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage);
|
|
+ phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius);
|
|
+ phantomSpawnMinSkyDarkness = getInt("mobs.phantom.spawn.min-sky-darkness", phantomSpawnMinSkyDarkness);
|
|
+ phantomSpawnOnlyAboveSeaLevel = getBoolean("mobs.phantom.spawn.only-above-sea-level", phantomSpawnOnlyAboveSeaLevel);
|
|
+ phantomSpawnOnlyWithVisibleSky = getBoolean("mobs.phantom.spawn.only-with-visible-sky", phantomSpawnOnlyWithVisibleSky);
|
|
+ phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance);
|
|
+ phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt);
|
|
+ phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt);
|
|
+ phantomBurnInLight = getInt("mobs.phantom.burn-in-light", phantomBurnInLight);
|
|
+ phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight);
|
|
+ phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch);
|
|
+ phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop);
|
|
+ phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater);
|
|
+ phantomAlwaysDropExp = getBoolean("mobs.phantom.always-drop-exp", phantomAlwaysDropExp);
|
|
+ phantomMinSize = Mth.clamp(getInt("mobs.phantom.size.min", phantomMinSize), 0, 64);
|
|
+ phantomMaxSize = Mth.clamp(getInt("mobs.phantom.size.max", phantomMaxSize), 0, 64);
|
|
+ if (phantomMinSize > phantomMaxSize) {
|
|
+ phantomMinSize = phantomMinSize ^ phantomMaxSize;
|
|
+ phantomMaxSize = phantomMinSize ^ phantomMaxSize;
|
|
+ phantomMinSize = phantomMinSize ^ phantomMaxSize;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean pigRidable = false;
|
|
+ public boolean pigRidableInWater = false;
|
|
+ public boolean pigControllable = true;
|
|
+ public double pigMaxHealth = 10.0D;
|
|
+ public double pigScale = 1.0D;
|
|
+ public boolean pigGiveSaddleBack = false;
|
|
+ public int pigBreedingTicks = 6000;
|
|
+ public boolean pigTakeDamageFromWater = false;
|
|
+ public boolean pigAlwaysDropExp = false;
|
|
+ private void pigSettings() {
|
|
+ pigRidable = getBoolean("mobs.pig.ridable", pigRidable);
|
|
+ pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater);
|
|
+ pigControllable = getBoolean("mobs.pig.controllable", pigControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pig.attributes.max-health", pigMaxHealth);
|
|
+ set("mobs.pig.attributes.max-health", null);
|
|
+ set("mobs.pig.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth);
|
|
+ pigScale = Mth.clamp(getDouble("mobs.pig.attributes.scale", pigScale), 0.0625D, 16.0D);
|
|
+ pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack);
|
|
+ pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks);
|
|
+ pigTakeDamageFromWater = getBoolean("mobs.pig.takes-damage-from-water", pigTakeDamageFromWater);
|
|
+ pigAlwaysDropExp = getBoolean("mobs.pig.always-drop-exp", pigAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean piglinRidable = false;
|
|
+ public boolean piglinRidableInWater = true;
|
|
+ public boolean piglinControllable = true;
|
|
+ public double piglinMaxHealth = 16.0D;
|
|
+ public double piglinScale = 1.0D;
|
|
+ public boolean piglinBypassMobGriefing = false;
|
|
+ public boolean piglinTakeDamageFromWater = false;
|
|
+ public int piglinPortalSpawnModifier = 2000;
|
|
+ public boolean piglinAlwaysDropExp = false;
|
|
+ public double piglinHeadVisibilityPercent = 0.5D;
|
|
+ public boolean piglinIgnoresArmorWithGoldTrim = false;
|
|
+ private void piglinSettings() {
|
|
+ piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable);
|
|
+ piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater);
|
|
+ piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.piglin.attributes.max-health", piglinMaxHealth);
|
|
+ set("mobs.piglin.attributes.max-health", null);
|
|
+ set("mobs.piglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth);
|
|
+ piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D);
|
|
+ piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing);
|
|
+ piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater);
|
|
+ piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier);
|
|
+ piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp);
|
|
+ piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent);
|
|
+ piglinIgnoresArmorWithGoldTrim = getBoolean("mobs.piglin.ignores-armor-with-gold-trim", piglinIgnoresArmorWithGoldTrim);
|
|
+ }
|
|
+
|
|
+ public boolean piglinBruteRidable = false;
|
|
+ public boolean piglinBruteRidableInWater = true;
|
|
+ public boolean piglinBruteControllable = true;
|
|
+ public double piglinBruteMaxHealth = 50.0D;
|
|
+ public double piglinBruteScale = 1.0D;
|
|
+ public boolean piglinBruteTakeDamageFromWater = false;
|
|
+ public boolean piglinBruteAlwaysDropExp = false;
|
|
+ private void piglinBruteSettings() {
|
|
+ piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable);
|
|
+ piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater);
|
|
+ piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.piglin_brute.attributes.max-health", piglinBruteMaxHealth);
|
|
+ set("mobs.piglin_brute.attributes.max-health", null);
|
|
+ set("mobs.piglin_brute.attributes.max_health", oldValue);
|
|
+ }
|
|
+ piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth);
|
|
+ piglinBruteScale = Mth.clamp(getDouble("mobs.piglin_brute.attributes.scale", piglinBruteScale), 0.0625D, 16.0D);
|
|
+ piglinBruteTakeDamageFromWater = getBoolean("mobs.piglin_brute.takes-damage-from-water", piglinBruteTakeDamageFromWater);
|
|
+ piglinBruteAlwaysDropExp = getBoolean("mobs.piglin_brute.always-drop-exp", piglinBruteAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean pillagerRidable = false;
|
|
+ public boolean pillagerRidableInWater = true;
|
|
+ public boolean pillagerControllable = true;
|
|
+ public double pillagerMaxHealth = 24.0D;
|
|
+ public double pillagerScale = 1.0D;
|
|
+ public boolean pillagerBypassMobGriefing = false;
|
|
+ public boolean pillagerTakeDamageFromWater = false;
|
|
+ public boolean pillagerAlwaysDropExp = false;
|
|
+ private void pillagerSettings() {
|
|
+ pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable);
|
|
+ pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater);
|
|
+ pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pillager.attributes.max-health", pillagerMaxHealth);
|
|
+ set("mobs.pillager.attributes.max-health", null);
|
|
+ set("mobs.pillager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth);
|
|
+ pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D);
|
|
+ pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing);
|
|
+ pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater);
|
|
+ pillagerAlwaysDropExp = getBoolean("mobs.pillager.always-drop-exp", pillagerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean polarBearRidable = false;
|
|
+ public boolean polarBearRidableInWater = true;
|
|
+ public boolean polarBearControllable = true;
|
|
+ public double polarBearMaxHealth = 30.0D;
|
|
+ public double polarBearScale = 1.0D;
|
|
+ public String polarBearBreedableItemString = "";
|
|
+ public Item polarBearBreedableItem = null;
|
|
+ public int polarBearBreedingTicks = 6000;
|
|
+ public boolean polarBearTakeDamageFromWater = false;
|
|
+ public boolean polarBearAlwaysDropExp = false;
|
|
+ private void polarBearSettings() {
|
|
+ polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable);
|
|
+ polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater);
|
|
+ polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.polar_bear.attributes.max-health", polarBearMaxHealth);
|
|
+ set("mobs.polar_bear.attributes.max-health", null);
|
|
+ set("mobs.polar_bear.attributes.max_health", oldValue);
|
|
+ }
|
|
+ polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth);
|
|
+ polarBearScale = Mth.clamp(getDouble("mobs.polar_bear.attributes.scale", polarBearScale), 0.0625D, 16.0D);
|
|
+ polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString);
|
|
+ Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(polarBearBreedableItemString));
|
|
+ if (item != Items.AIR) polarBearBreedableItem = item;
|
|
+ polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks);
|
|
+ polarBearTakeDamageFromWater = getBoolean("mobs.polar_bear.takes-damage-from-water", polarBearTakeDamageFromWater);
|
|
+ polarBearAlwaysDropExp = getBoolean("mobs.polar_bear.always-drop-exp", polarBearAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean pufferfishRidable = false;
|
|
+ public boolean pufferfishControllable = true;
|
|
+ public double pufferfishMaxHealth = 3.0D;
|
|
+ public double pufferfishScale = 1.0D;
|
|
+ public boolean pufferfishTakeDamageFromWater = false;
|
|
+ public boolean pufferfishAlwaysDropExp = false;
|
|
+ private void pufferfishSettings() {
|
|
+ pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable);
|
|
+ pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pufferfish.attributes.max-health", pufferfishMaxHealth);
|
|
+ set("mobs.pufferfish.attributes.max-health", null);
|
|
+ set("mobs.pufferfish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth);
|
|
+ pufferfishScale = Mth.clamp(getDouble("mobs.pufferfish.attributes.scale", pufferfishScale), 0.0625D, 16.0D);
|
|
+ pufferfishTakeDamageFromWater = getBoolean("mobs.pufferfish.takes-damage-from-water", pufferfishTakeDamageFromWater);
|
|
+ pufferfishAlwaysDropExp = getBoolean("mobs.pufferfish.always-drop-exp", pufferfishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean rabbitRidable = false;
|
|
+ public boolean rabbitRidableInWater = true;
|
|
+ public boolean rabbitControllable = true;
|
|
+ public double rabbitMaxHealth = 3.0D;
|
|
+ public double rabbitScale = 1.0D;
|
|
+ public double rabbitNaturalToast = 0.0D;
|
|
+ public double rabbitNaturalKiller = 0.0D;
|
|
+ public int rabbitBreedingTicks = 6000;
|
|
+ public boolean rabbitBypassMobGriefing = false;
|
|
+ public boolean rabbitTakeDamageFromWater = false;
|
|
+ public boolean rabbitAlwaysDropExp = false;
|
|
+ private void rabbitSettings() {
|
|
+ rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable);
|
|
+ rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater);
|
|
+ rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.rabbit.attributes.max-health", rabbitMaxHealth);
|
|
+ set("mobs.rabbit.attributes.max-health", null);
|
|
+ set("mobs.rabbit.attributes.max_health", oldValue);
|
|
+ }
|
|
+ rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth);
|
|
+ rabbitScale = Mth.clamp(getDouble("mobs.rabbit.attributes.scale", rabbitScale), 0.0625D, 16.0D);
|
|
+ rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast);
|
|
+ rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller);
|
|
+ rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks);
|
|
+ rabbitBypassMobGriefing = getBoolean("mobs.rabbit.bypass-mob-griefing", rabbitBypassMobGriefing);
|
|
+ rabbitTakeDamageFromWater = getBoolean("mobs.rabbit.takes-damage-from-water", rabbitTakeDamageFromWater);
|
|
+ rabbitAlwaysDropExp = getBoolean("mobs.rabbit.always-drop-exp", rabbitAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ravagerRidable = false;
|
|
+ public boolean ravagerRidableInWater = false;
|
|
+ public boolean ravagerControllable = true;
|
|
+ public double ravagerMaxHealth = 100.0D;
|
|
+ public double ravagerScale = 1.0D;
|
|
+ public boolean ravagerBypassMobGriefing = false;
|
|
+ public boolean ravagerTakeDamageFromWater = false;
|
|
+ public List<Block> ravagerGriefableBlocks = new ArrayList<>();
|
|
+ public boolean ravagerAlwaysDropExp = false;
|
|
+ public boolean ravagerAvoidRabbits = false;
|
|
+ private void ravagerSettings() {
|
|
+ ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable);
|
|
+ ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater);
|
|
+ ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ravager.attributes.max-health", ravagerMaxHealth);
|
|
+ set("mobs.ravager.attributes.max-health", null);
|
|
+ set("mobs.ravager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth);
|
|
+ ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D);
|
|
+ ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing);
|
|
+ ravagerTakeDamageFromWater = getBoolean("mobs.ravager.takes-damage-from-water", ravagerTakeDamageFromWater);
|
|
+ getList("mobs.ravager.griefable-blocks", new ArrayList<String>(){{
|
|
+ add("minecraft:oak_leaves");
|
|
+ add("minecraft:spruce_leaves");
|
|
+ add("minecraft:birch_leaves");
|
|
+ add("minecraft:jungle_leaves");
|
|
+ add("minecraft:acacia_leaves");
|
|
+ add("minecraft:dark_oak_leaves");
|
|
+ add("minecraft:beetroots");
|
|
+ add("minecraft:carrots");
|
|
+ add("minecraft:potatoes");
|
|
+ add("minecraft:wheat");
|
|
+ }}).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ ravagerGriefableBlocks.add(block);
|
|
+ }
|
|
+ });
|
|
+ ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp);
|
|
+ ravagerAvoidRabbits = getBoolean("mobs.ravager.avoid-rabbits", ravagerAvoidRabbits);
|
|
+ }
|
|
+
|
|
+ public boolean salmonRidable = false;
|
|
+ public boolean salmonControllable = true;
|
|
+ public double salmonMaxHealth = 3.0D;
|
|
+ public double salmonScale = 1.0D;
|
|
+ public boolean salmonTakeDamageFromWater = false;
|
|
+ public boolean salmonAlwaysDropExp = false;
|
|
+ private void salmonSettings() {
|
|
+ salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable);
|
|
+ salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.salmon.attributes.max-health", salmonMaxHealth);
|
|
+ set("mobs.salmon.attributes.max-health", null);
|
|
+ set("mobs.salmon.attributes.max_health", oldValue);
|
|
+ }
|
|
+ salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth);
|
|
+ salmonScale = Mth.clamp(getDouble("mobs.salmon.attributes.scale", salmonScale), 0.0625D, 16.0D);
|
|
+ salmonTakeDamageFromWater = getBoolean("mobs.salmon.takes-damage-from-water", salmonTakeDamageFromWater);
|
|
+ salmonAlwaysDropExp = getBoolean("mobs.salmon.always-drop-exp", salmonAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean sheepRidable = false;
|
|
+ public boolean sheepRidableInWater = true;
|
|
+ public boolean sheepControllable = true;
|
|
+ public double sheepMaxHealth = 8.0D;
|
|
+ public double sheepScale = 1.0D;
|
|
+ public int sheepBreedingTicks = 6000;
|
|
+ public boolean sheepBypassMobGriefing = false;
|
|
+ public boolean sheepTakeDamageFromWater = false;
|
|
+ public boolean sheepAlwaysDropExp = false;
|
|
+ private void sheepSettings() {
|
|
+ sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable);
|
|
+ sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater);
|
|
+ sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.sheep.attributes.max-health", sheepMaxHealth);
|
|
+ set("mobs.sheep.attributes.max-health", null);
|
|
+ set("mobs.sheep.attributes.max_health", oldValue);
|
|
+ }
|
|
+ sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth);
|
|
+ sheepScale = Mth.clamp(getDouble("mobs.sheep.attributes.scale", sheepScale), 0.0625D, 16.0D);
|
|
+ sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks);
|
|
+ sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing);
|
|
+ sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater);
|
|
+ sheepAlwaysDropExp = getBoolean("mobs.sheep.always-drop-exp", sheepAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean shulkerRidable = false;
|
|
+ public boolean shulkerRidableInWater = true;
|
|
+ public boolean shulkerControllable = true;
|
|
+ public double shulkerMaxHealth = 30.0D;
|
|
+ public double shulkerScale = 1.0D;
|
|
+ public boolean shulkerTakeDamageFromWater = false;
|
|
+ public float shulkerSpawnFromBulletBaseChance = 1.0F;
|
|
+ public boolean shulkerSpawnFromBulletRequireOpenLid = true;
|
|
+ public double shulkerSpawnFromBulletNearbyRange = 8.0D;
|
|
+ public String shulkerSpawnFromBulletNearbyEquation = "(nearby - 1) / 5.0";
|
|
+ public boolean shulkerSpawnFromBulletRandomColor = false;
|
|
+ public boolean shulkerChangeColorWithDye = false;
|
|
+ public boolean shulkerAlwaysDropExp = false;
|
|
+ private void shulkerSettings() {
|
|
+ shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable);
|
|
+ shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater);
|
|
+ shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.shulker.attributes.max-health", shulkerMaxHealth);
|
|
+ set("mobs.shulker.attributes.max-health", null);
|
|
+ set("mobs.shulker.attributes.max_health", oldValue);
|
|
+ }
|
|
+ shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth);
|
|
+ shulkerScale = Mth.clamp(getDouble("mobs.shulker.attributes.scale", shulkerScale), 0.0625D, Shulker.MAX_SCALE);
|
|
+ shulkerTakeDamageFromWater = getBoolean("mobs.shulker.takes-damage-from-water", shulkerTakeDamageFromWater);
|
|
+ shulkerSpawnFromBulletBaseChance = (float) getDouble("mobs.shulker.spawn-from-bullet.base-chance", shulkerSpawnFromBulletBaseChance);
|
|
+ shulkerSpawnFromBulletRequireOpenLid = getBoolean("mobs.shulker.spawn-from-bullet.require-open-lid", shulkerSpawnFromBulletRequireOpenLid);
|
|
+ shulkerSpawnFromBulletNearbyRange = getDouble("mobs.shulker.spawn-from-bullet.nearby-range", shulkerSpawnFromBulletNearbyRange);
|
|
+ shulkerSpawnFromBulletNearbyEquation = getString("mobs.shulker.spawn-from-bullet.nearby-equation", shulkerSpawnFromBulletNearbyEquation);
|
|
+ shulkerSpawnFromBulletRandomColor = getBoolean("mobs.shulker.spawn-from-bullet.random-color", shulkerSpawnFromBulletRandomColor);
|
|
+ shulkerChangeColorWithDye = getBoolean("mobs.shulker.change-color-with-dye", shulkerChangeColorWithDye);
|
|
+ shulkerAlwaysDropExp = getBoolean("mobs.shulker.always-drop-exp", shulkerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean silverfishRidable = false;
|
|
+ public boolean silverfishRidableInWater = true;
|
|
+ public boolean silverfishControllable = true;
|
|
+ public double silverfishMaxHealth = 8.0D;
|
|
+ public double silverfishScale = 1.0D;
|
|
+ public double silverfishMovementSpeed = 0.25D;
|
|
+ public double silverfishAttackDamage = 1.0D;
|
|
+ public boolean silverfishBypassMobGriefing = false;
|
|
+ public boolean silverfishTakeDamageFromWater = false;
|
|
+ public boolean silverfishAlwaysDropExp = false;
|
|
+ private void silverfishSettings() {
|
|
+ silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable);
|
|
+ silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater);
|
|
+ silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.silverfish.attributes.max-health", silverfishMaxHealth);
|
|
+ set("mobs.silverfish.attributes.max-health", null);
|
|
+ set("mobs.silverfish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth);
|
|
+ silverfishScale = Mth.clamp(getDouble("mobs.silverfish.attributes.scale", silverfishScale), 0.0625D, 16.0D);
|
|
+ silverfishMovementSpeed = getDouble("mobs.silverfish.attributes.movement_speed", silverfishMovementSpeed);
|
|
+ silverfishAttackDamage = getDouble("mobs.silverfish.attributes.attack_damage", silverfishAttackDamage);
|
|
+ silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing);
|
|
+ silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater);
|
|
+ silverfishAlwaysDropExp = getBoolean("mobs.silverfish.always-drop-exp", silverfishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean skeletonRidable = false;
|
|
+ public boolean skeletonRidableInWater = true;
|
|
+ public boolean skeletonControllable = true;
|
|
+ public double skeletonMaxHealth = 20.0D;
|
|
+ public double skeletonScale = 1.0D;
|
|
+ public boolean skeletonTakeDamageFromWater = false;
|
|
+ public boolean skeletonAlwaysDropExp = false;
|
|
+ public double skeletonHeadVisibilityPercent = 0.5D;
|
|
+ public int skeletonFeedWitherRoses = 0;
|
|
+ public String skeletonBowAccuracy = "14 - difficulty * 4";
|
|
+ public Map<Integer, Float> skeletonBowAccuracyMap = new HashMap<>();
|
|
+ private void skeletonSettings() {
|
|
+ skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable);
|
|
+ skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater);
|
|
+ skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.skeleton.attributes.max-health", skeletonMaxHealth);
|
|
+ set("mobs.skeleton.attributes.max-health", null);
|
|
+ set("mobs.skeleton.attributes.max_health", oldValue);
|
|
+ }
|
|
+ skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth);
|
|
+ skeletonScale = Mth.clamp(getDouble("mobs.skeleton.attributes.scale", skeletonScale), 0.0625D, 16.0D);
|
|
+ skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater);
|
|
+ skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp);
|
|
+ skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent);
|
|
+ skeletonFeedWitherRoses = getInt("mobs.skeleton.feed-wither-roses", skeletonFeedWitherRoses);
|
|
+ final String defaultSkeletonBowAccuracy = skeletonBowAccuracy;
|
|
+ skeletonBowAccuracy = getString("mobs.skeleton.bow-accuracy", skeletonBowAccuracy);
|
|
+ for (int i = 1; i < 4; i++) {
|
|
+ final float divergence;
|
|
+ try {
|
|
+ divergence = ((Number) Entity.scriptEngine.eval("let difficulty = " + i + "; " + skeletonBowAccuracy)).floatValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ break;
|
|
+ }
|
|
+ skeletonBowAccuracyMap.put(i, divergence);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean skeletonHorseRidable = false;
|
|
+ public boolean skeletonHorseRidableInWater = true;
|
|
+ public boolean skeletonHorseCanSwim = false;
|
|
+ public double skeletonHorseMaxHealthMin = 15.0D;
|
|
+ public double skeletonHorseMaxHealthMax = 15.0D;
|
|
+ public double skeletonHorseJumpStrengthMin = 0.4D;
|
|
+ public double skeletonHorseJumpStrengthMax = 1.0D;
|
|
+ public double skeletonHorseMovementSpeedMin = 0.2D;
|
|
+ public double skeletonHorseMovementSpeedMax = 0.2D;
|
|
+ public boolean skeletonHorseTakeDamageFromWater = false;
|
|
+ public boolean skeletonHorseAlwaysDropExp = false;
|
|
+ private void skeletonHorseSettings() {
|
|
+ skeletonHorseRidable = getBoolean("mobs.skeleton_horse.ridable", skeletonHorseRidable);
|
|
+ skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater);
|
|
+ skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.skeleton_horse.attributes.max-health", skeletonHorseMaxHealthMin);
|
|
+ set("mobs.skeleton_horse.attributes.max-health", null);
|
|
+ set("mobs.skeleton_horse.attributes.max_health.min", oldValue);
|
|
+ set("mobs.skeleton_horse.attributes.max_health.max", oldValue);
|
|
+ }
|
|
+ skeletonHorseMaxHealthMin = getDouble("mobs.skeleton_horse.attributes.max_health.min", skeletonHorseMaxHealthMin);
|
|
+ skeletonHorseMaxHealthMax = getDouble("mobs.skeleton_horse.attributes.max_health.max", skeletonHorseMaxHealthMax);
|
|
+ skeletonHorseJumpStrengthMin = getDouble("mobs.skeleton_horse.attributes.jump_strength.min", skeletonHorseJumpStrengthMin);
|
|
+ skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax);
|
|
+ skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin);
|
|
+ skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax);
|
|
+ skeletonHorseTakeDamageFromWater = getBoolean("mobs.skeleton_horse.takes-damage-from-water", skeletonHorseTakeDamageFromWater);
|
|
+ skeletonHorseAlwaysDropExp = getBoolean("mobs.skeleton_horse.always-drop-exp", skeletonHorseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean slimeRidable = false;
|
|
+ public boolean slimeRidableInWater = true;
|
|
+ public boolean slimeControllable = true;
|
|
+ public String slimeMaxHealth = "size * size";
|
|
+ public String slimeAttackDamage = "size";
|
|
+ public Map<Integer, Double> slimeMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> slimeAttackDamageCache = new HashMap<>();
|
|
+ public boolean slimeTakeDamageFromWater = false;
|
|
+ public boolean slimeAlwaysDropExp = false;
|
|
+ private void slimeSettings() {
|
|
+ slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable);
|
|
+ slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater);
|
|
+ slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ String oldValue = getString("mobs.slime.attributes.max-health", slimeMaxHealth);
|
|
+ set("mobs.slime.attributes.max-health", null);
|
|
+ set("mobs.slime.attributes.max_health", oldValue);
|
|
+ }
|
|
+ slimeMaxHealth = getString("mobs.slime.attributes.max_health", slimeMaxHealth);
|
|
+ slimeAttackDamage = getString("mobs.slime.attributes.attack_damage", slimeAttackDamage);
|
|
+ slimeMaxHealthCache.clear();
|
|
+ slimeAttackDamageCache.clear();
|
|
+ slimeTakeDamageFromWater = getBoolean("mobs.slime.takes-damage-from-water", slimeTakeDamageFromWater);
|
|
+ slimeAlwaysDropExp = getBoolean("mobs.slime.always-drop-exp", slimeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean snowGolemRidable = false;
|
|
+ public boolean snowGolemRidableInWater = true;
|
|
+ public boolean snowGolemControllable = true;
|
|
+ public boolean snowGolemLeaveTrailWhenRidden = false;
|
|
+ public double snowGolemMaxHealth = 4.0D;
|
|
+ public double snowGolemScale = 1.0D;
|
|
+ public boolean snowGolemPutPumpkinBack = false;
|
|
+ public int snowGolemSnowBallMin = 20;
|
|
+ public int snowGolemSnowBallMax = 20;
|
|
+ public float snowGolemSnowBallModifier = 10.0F;
|
|
+ public double snowGolemAttackDistance = 1.25D;
|
|
+ public boolean snowGolemBypassMobGriefing = false;
|
|
+ public boolean snowGolemTakeDamageFromWater = true;
|
|
+ public boolean snowGolemAlwaysDropExp = false;
|
|
+ private void snowGolemSettings() {
|
|
+ snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable);
|
|
+ snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater);
|
|
+ snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable);
|
|
+ snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.snow_golem.attributes.max-health", snowGolemMaxHealth);
|
|
+ set("mobs.snow_golem.attributes.max-health", null);
|
|
+ set("mobs.snow_golem.attributes.max_health", oldValue);
|
|
+ }
|
|
+ snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth);
|
|
+ snowGolemScale = Mth.clamp(getDouble("mobs.snow_golem.attributes.scale", snowGolemScale), 0.0625D, 16.0D);
|
|
+ snowGolemPutPumpkinBack = getBoolean("mobs.snow_golem.pumpkin-can-be-added-back", snowGolemPutPumpkinBack);
|
|
+ snowGolemSnowBallMin = getInt("mobs.snow_golem.min-shoot-interval-ticks", snowGolemSnowBallMin);
|
|
+ snowGolemSnowBallMax = getInt("mobs.snow_golem.max-shoot-interval-ticks", snowGolemSnowBallMax);
|
|
+ snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier);
|
|
+ snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance);
|
|
+ snowGolemBypassMobGriefing = getBoolean("mobs.snow_golem.bypass-mob-griefing", snowGolemBypassMobGriefing);
|
|
+ snowGolemTakeDamageFromWater = getBoolean("mobs.snow_golem.takes-damage-from-water", snowGolemTakeDamageFromWater);
|
|
+ snowGolemAlwaysDropExp = getBoolean("mobs.snow_golem.always-drop-exp", snowGolemAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean snifferRidable = false;
|
|
+ public boolean snifferRidableInWater = true;
|
|
+ public boolean snifferControllable = true;
|
|
+ public double snifferMaxHealth = 14.0D;
|
|
+ public double snifferScale = 1.0D;
|
|
+ public int snifferBreedingTicks = 6000;
|
|
+ private void snifferSettings() {
|
|
+ snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable);
|
|
+ snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater);
|
|
+ snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable);
|
|
+ snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth);
|
|
+ snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D);
|
|
+ snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", snifferBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean squidRidable = false;
|
|
+ public boolean squidControllable = true;
|
|
+ public double squidMaxHealth = 10.0D;
|
|
+ public double squidScale = 1.0D;
|
|
+ public boolean squidImmuneToEAR = true;
|
|
+ public double squidOffsetWaterCheck = 0.0D;
|
|
+ public boolean squidsCanFly = false;
|
|
+ public boolean squidTakeDamageFromWater = false;
|
|
+ public boolean squidAlwaysDropExp = false;
|
|
+ private void squidSettings() {
|
|
+ squidRidable = getBoolean("mobs.squid.ridable", squidRidable);
|
|
+ squidControllable = getBoolean("mobs.squid.controllable", squidControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.squid.attributes.max-health", squidMaxHealth);
|
|
+ set("mobs.squid.attributes.max-health", null);
|
|
+ set("mobs.squid.attributes.max_health", oldValue);
|
|
+ }
|
|
+ squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth);
|
|
+ squidScale = Mth.clamp(getDouble("mobs.squid.attributes.scale", squidScale), 0.0625D, 16.0D);
|
|
+ squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR);
|
|
+ squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck);
|
|
+ squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly);
|
|
+ squidTakeDamageFromWater = getBoolean("mobs.squid.takes-damage-from-water", squidTakeDamageFromWater);
|
|
+ squidAlwaysDropExp = getBoolean("mobs.squid.always-drop-exp", squidAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean spiderRidable = false;
|
|
+ public boolean spiderRidableInWater = false;
|
|
+ public boolean spiderControllable = true;
|
|
+ public double spiderMaxHealth = 16.0D;
|
|
+ public double spiderScale = 1.0D;
|
|
+ public boolean spiderTakeDamageFromWater = false;
|
|
+ public boolean spiderAlwaysDropExp = false;
|
|
+ private void spiderSettings() {
|
|
+ spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable);
|
|
+ spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater);
|
|
+ spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.spider.attributes.max-health", spiderMaxHealth);
|
|
+ set("mobs.spider.attributes.max-health", null);
|
|
+ set("mobs.spider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth);
|
|
+ spiderScale = Mth.clamp(getDouble("mobs.spider.attributes.scale", spiderScale), 0.0625D, 16.0D);
|
|
+ spiderTakeDamageFromWater = getBoolean("mobs.spider.takes-damage-from-water", spiderTakeDamageFromWater);
|
|
+ spiderAlwaysDropExp = getBoolean("mobs.spider.always-drop-exp", spiderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean strayRidable = false;
|
|
+ public boolean strayRidableInWater = true;
|
|
+ public boolean strayControllable = true;
|
|
+ public double strayMaxHealth = 20.0D;
|
|
+ public double strayScale = 1.0D;
|
|
+ public boolean strayTakeDamageFromWater = false;
|
|
+ public boolean strayAlwaysDropExp = false;
|
|
+ private void straySettings() {
|
|
+ strayRidable = getBoolean("mobs.stray.ridable", strayRidable);
|
|
+ strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater);
|
|
+ strayControllable = getBoolean("mobs.stray.controllable", strayControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.stray.attributes.max-health", strayMaxHealth);
|
|
+ set("mobs.stray.attributes.max-health", null);
|
|
+ set("mobs.stray.attributes.max_health", oldValue);
|
|
+ }
|
|
+ strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth);
|
|
+ strayScale = Mth.clamp(getDouble("mobs.stray.attributes.scale", strayScale), 0.0625D, 16.0D);
|
|
+ strayTakeDamageFromWater = getBoolean("mobs.stray.takes-damage-from-water", strayTakeDamageFromWater);
|
|
+ strayAlwaysDropExp = getBoolean("mobs.stray.always-drop-exp", strayAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean striderRidable = false;
|
|
+ public boolean striderRidableInWater = false;
|
|
+ public boolean striderControllable = true;
|
|
+ public double striderMaxHealth = 20.0D;
|
|
+ public double striderScale = 1.0D;
|
|
+ public int striderBreedingTicks = 6000;
|
|
+ public boolean striderGiveSaddleBack = false;
|
|
+ public boolean striderTakeDamageFromWater = true;
|
|
+ public boolean striderAlwaysDropExp = false;
|
|
+ private void striderSettings() {
|
|
+ striderRidable = getBoolean("mobs.strider.ridable", striderRidable);
|
|
+ striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater);
|
|
+ striderControllable = getBoolean("mobs.strider.controllable", striderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.strider.attributes.max-health", striderMaxHealth);
|
|
+ set("mobs.strider.attributes.max-health", null);
|
|
+ set("mobs.strider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth);
|
|
+ striderScale = Mth.clamp(getDouble("mobs.strider.attributes.scale", striderScale), 0.0625D, 16.0D);
|
|
+ striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks);
|
|
+ striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack);
|
|
+ striderTakeDamageFromWater = getBoolean("mobs.strider.takes-damage-from-water", striderTakeDamageFromWater);
|
|
+ striderAlwaysDropExp = getBoolean("mobs.strider.always-drop-exp", striderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean tadpoleRidable = false;
|
|
+ public boolean tadpoleRidableInWater = true;
|
|
+ public boolean tadpoleControllable = true;
|
|
+ private void tadpoleSettings() {
|
|
+ tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable);
|
|
+ tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater);
|
|
+ tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable);
|
|
+ }
|
|
+
|
|
+ public boolean traderLlamaRidable = false;
|
|
+ public boolean traderLlamaRidableInWater = false;
|
|
+ public boolean traderLlamaControllable = true;
|
|
+ public double traderLlamaMaxHealthMin = 15.0D;
|
|
+ public double traderLlamaMaxHealthMax = 30.0D;
|
|
+ public double traderLlamaJumpStrengthMin = 0.5D;
|
|
+ public double traderLlamaJumpStrengthMax = 0.5D;
|
|
+ public double traderLlamaMovementSpeedMin = 0.175D;
|
|
+ public double traderLlamaMovementSpeedMax = 0.175D;
|
|
+ public int traderLlamaBreedingTicks = 6000;
|
|
+ public boolean traderLlamaTakeDamageFromWater = false;
|
|
+ public boolean traderLlamaAlwaysDropExp = false;
|
|
+ private void traderLlamaSettings() {
|
|
+ traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable);
|
|
+ traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater);
|
|
+ traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.trader_llama.attributes.max-health.min", traderLlamaMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.trader_llama.attributes.max-health.max", traderLlamaMaxHealthMax);
|
|
+ set("mobs.trader_llama.attributes.max-health", null);
|
|
+ set("mobs.trader_llama.attributes.max_health.min", oldMin);
|
|
+ set("mobs.trader_llama.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ traderLlamaMaxHealthMin = getDouble("mobs.trader_llama.attributes.max_health.min", traderLlamaMaxHealthMin);
|
|
+ traderLlamaMaxHealthMax = getDouble("mobs.trader_llama.attributes.max_health.max", traderLlamaMaxHealthMax);
|
|
+ traderLlamaJumpStrengthMin = getDouble("mobs.trader_llama.attributes.jump_strength.min", traderLlamaJumpStrengthMin);
|
|
+ traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax);
|
|
+ traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin);
|
|
+ traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax);
|
|
+ traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks);
|
|
+ traderLlamaTakeDamageFromWater = getBoolean("mobs.trader_llama.takes-damage-from-water", traderLlamaTakeDamageFromWater);
|
|
+ traderLlamaAlwaysDropExp = getBoolean("mobs.trader_llama.always-drop-exp", traderLlamaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean tropicalFishRidable = false;
|
|
+ public boolean tropicalFishControllable = true;
|
|
+ public double tropicalFishMaxHealth = 3.0D;
|
|
+ public double tropicalFishScale = 1.0D;
|
|
+ public boolean tropicalFishTakeDamageFromWater = false;
|
|
+ public boolean tropicalFishAlwaysDropExp = false;
|
|
+ private void tropicalFishSettings() {
|
|
+ tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable);
|
|
+ tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.tropical_fish.attributes.max-health", tropicalFishMaxHealth);
|
|
+ set("mobs.tropical_fish.attributes.max-health", null);
|
|
+ set("mobs.tropical_fish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth);
|
|
+ tropicalFishScale = Mth.clamp(getDouble("mobs.tropical_fish.attributes.scale", tropicalFishScale), 0.0625D, 16.0D);
|
|
+ tropicalFishTakeDamageFromWater = getBoolean("mobs.tropical_fish.takes-damage-from-water", tropicalFishTakeDamageFromWater);
|
|
+ tropicalFishAlwaysDropExp = getBoolean("mobs.tropical_fish.always-drop-exp", tropicalFishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean turtleRidable = false;
|
|
+ public boolean turtleRidableInWater = true;
|
|
+ public boolean turtleControllable = true;
|
|
+ public double turtleMaxHealth = 30.0D;
|
|
+ public double turtleScale = 1.0D;
|
|
+ public int turtleBreedingTicks = 6000;
|
|
+ public boolean turtleTakeDamageFromWater = false;
|
|
+ public boolean turtleAlwaysDropExp = false;
|
|
+ private void turtleSettings() {
|
|
+ turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable);
|
|
+ turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater);
|
|
+ turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.turtle.attributes.max-health", turtleMaxHealth);
|
|
+ set("mobs.turtle.attributes.max-health", null);
|
|
+ set("mobs.turtle.attributes.max_health", oldValue);
|
|
+ }
|
|
+ turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth);
|
|
+ turtleScale = Mth.clamp(getDouble("mobs.turtle.attributes.scale", turtleScale), 0.0625D, 16.0D);
|
|
+ turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks);
|
|
+ turtleTakeDamageFromWater = getBoolean("mobs.turtle.takes-damage-from-water", turtleTakeDamageFromWater);
|
|
+ turtleAlwaysDropExp = getBoolean("mobs.turtle.always-drop-exp", turtleAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean vexRidable = false;
|
|
+ public boolean vexRidableInWater = true;
|
|
+ public boolean vexControllable = true;
|
|
+ public double vexMaxY = 320D;
|
|
+ public double vexMaxHealth = 14.0D;
|
|
+ public double vexScale = 1.0D;
|
|
+ public boolean vexTakeDamageFromWater = false;
|
|
+ public boolean vexAlwaysDropExp = false;
|
|
+ private void vexSettings() {
|
|
+ vexRidable = getBoolean("mobs.vex.ridable", vexRidable);
|
|
+ vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater);
|
|
+ vexControllable = getBoolean("mobs.vex.controllable", vexControllable);
|
|
+ vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.vex.attributes.max-health", vexMaxHealth);
|
|
+ set("mobs.vex.attributes.max-health", null);
|
|
+ set("mobs.vex.attributes.max_health", oldValue);
|
|
+ }
|
|
+ vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth);
|
|
+ vexScale = Mth.clamp(getDouble("mobs.vex.attributes.scale", vexScale), 0.0625D, 16.0D);
|
|
+ vexTakeDamageFromWater = getBoolean("mobs.vex.takes-damage-from-water", vexTakeDamageFromWater);
|
|
+ vexAlwaysDropExp = getBoolean("mobs.vex.always-drop-exp", vexAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean villagerRidable = false;
|
|
+ public boolean villagerRidableInWater = true;
|
|
+ public boolean villagerControllable = true;
|
|
+ public double villagerMaxHealth = 20.0D;
|
|
+ public double villagerScale = 1.0D;
|
|
+ public boolean villagerFollowEmeraldBlock = false;
|
|
+ public double villagerTemptRange = 10.0D;
|
|
+ public boolean villagerCanBeLeashed = false;
|
|
+ public boolean villagerCanBreed = true;
|
|
+ public int villagerBreedingTicks = 6000;
|
|
+ public boolean villagerClericsFarmWarts = false;
|
|
+ public boolean villagerClericFarmersThrowWarts = true;
|
|
+ public boolean villagerBypassMobGriefing = false;
|
|
+ public boolean villagerTakeDamageFromWater = false;
|
|
+ public boolean villagerAllowTrading = true;
|
|
+ public boolean villagerAlwaysDropExp = false;
|
|
+ public int villagerMinimumDemand = 0;
|
|
+ public boolean villagerLobotomizeEnabled = false;
|
|
+ public int villagerLobotomizeCheckInterval = 100;
|
|
+ public boolean villagerLobotomizeWaitUntilTradeLocked = false;
|
|
+ public boolean villagerDisplayTradeItem = true;
|
|
+ public int villagerSpawnIronGolemRadius = 0;
|
|
+ public int villagerSpawnIronGolemLimit = 0;
|
|
+ public int villagerAcquirePoiSearchRadius = 48;
|
|
+ public int villagerNearestBedSensorSearchRadius = 48;
|
|
+ private void villagerSettings() {
|
|
+ villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable);
|
|
+ villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater);
|
|
+ villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.villager.attributes.max-health", villagerMaxHealth);
|
|
+ set("mobs.villager.attributes.max-health", null);
|
|
+ set("mobs.villager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth);
|
|
+ villagerScale = Mth.clamp(getDouble("mobs.villager.attributes.scale", villagerScale), 0.0625D, 16.0D);
|
|
+ villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock);
|
|
+ villagerTemptRange = getDouble("mobs.villager.attributes.tempt_range", villagerTemptRange);
|
|
+ villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed);
|
|
+ villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed);
|
|
+ villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks);
|
|
+ villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts);
|
|
+ villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts);
|
|
+ villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing);
|
|
+ villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater);
|
|
+ villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading);
|
|
+ villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp);
|
|
+ villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand);
|
|
+ if (PurpurConfig.version < 9) {
|
|
+ boolean oldValue = getBoolean("mobs.villager.lobotomize-1x1", villagerLobotomizeEnabled);
|
|
+ set("mobs.villager.lobotomize.enabled", oldValue);
|
|
+ set("mobs.villager.lobotomize-1x1", null);
|
|
+ }
|
|
+ if (PurpurConfig.version < 27) {
|
|
+ int oldValue = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval);
|
|
+ set("mobs.villager.lobotomize.check-interval", oldValue == 60 ? 100 : oldValue);
|
|
+ }
|
|
+ villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled);
|
|
+ villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval);
|
|
+ villagerLobotomizeWaitUntilTradeLocked = getBoolean("mobs.villager.lobotomize.wait-until-trade-locked", villagerLobotomizeWaitUntilTradeLocked);
|
|
+ villagerDisplayTradeItem = getBoolean("mobs.villager.display-trade-item", villagerDisplayTradeItem);
|
|
+ villagerSpawnIronGolemRadius = getInt("mobs.villager.spawn-iron-golem.radius", villagerSpawnIronGolemRadius);
|
|
+ villagerSpawnIronGolemLimit = getInt("mobs.villager.spawn-iron-golem.limit", villagerSpawnIronGolemLimit);
|
|
+ villagerAcquirePoiSearchRadius = getInt("mobs.villager.search-radius.acquire-poi", villagerAcquirePoiSearchRadius);
|
|
+ villagerNearestBedSensorSearchRadius = getInt("mobs.villager.search-radius.nearest-bed-sensor", villagerNearestBedSensorSearchRadius);
|
|
+ }
|
|
+
|
|
+ public boolean vindicatorRidable = false;
|
|
+ public boolean vindicatorRidableInWater = true;
|
|
+ public boolean vindicatorControllable = true;
|
|
+ public double vindicatorMaxHealth = 24.0D;
|
|
+ public double vindicatorScale = 1.0D;
|
|
+ public double vindicatorJohnnySpawnChance = 0D;
|
|
+ public boolean vindicatorTakeDamageFromWater = false;
|
|
+ public boolean vindicatorAlwaysDropExp = false;
|
|
+ private void vindicatorSettings() {
|
|
+ vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable);
|
|
+ vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater);
|
|
+ vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.vindicator.attributes.max-health", vindicatorMaxHealth);
|
|
+ set("mobs.vindicator.attributes.max-health", null);
|
|
+ set("mobs.vindicator.attributes.max_health", oldValue);
|
|
+ }
|
|
+ vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth);
|
|
+ vindicatorScale = Mth.clamp(getDouble("mobs.vindicator.attributes.scale", vindicatorScale), 0.0625D, 16.0D);
|
|
+ vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance);
|
|
+ vindicatorTakeDamageFromWater = getBoolean("mobs.vindicator.takes-damage-from-water", vindicatorTakeDamageFromWater);
|
|
+ vindicatorAlwaysDropExp = getBoolean("mobs.vindicator.always-drop-exp", vindicatorAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wanderingTraderRidable = false;
|
|
+ public boolean wanderingTraderRidableInWater = true;
|
|
+ public boolean wanderingTraderControllable = true;
|
|
+ public double wanderingTraderMaxHealth = 20.0D;
|
|
+ public double wanderingTraderScale = 1.0D;
|
|
+ public boolean wanderingTraderFollowEmeraldBlock = false;
|
|
+ public double wanderingTraderTemptRange = 10.0D;
|
|
+ public boolean wanderingTraderCanBeLeashed = false;
|
|
+ public boolean wanderingTraderTakeDamageFromWater = false;
|
|
+ public boolean wanderingTraderAllowTrading = true;
|
|
+ public boolean wanderingTraderAlwaysDropExp = false;
|
|
+ private void wanderingTraderSettings() {
|
|
+ wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable);
|
|
+ wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater);
|
|
+ wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wandering_trader.attributes.max-health", wanderingTraderMaxHealth);
|
|
+ set("mobs.wandering_trader.attributes.max-health", null);
|
|
+ set("mobs.wandering_trader.attributes.max_health", oldValue);
|
|
+ }
|
|
+ wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth);
|
|
+ wanderingTraderScale = Mth.clamp(getDouble("mobs.wandering_trader.attributes.scale", wanderingTraderScale), 0.0625D, 16.0D);
|
|
+ wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock);
|
|
+ wanderingTraderTemptRange = getDouble("mobs.wandering_trader.attributes.tempt_range", wanderingTraderTemptRange);
|
|
+ wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed);
|
|
+ wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater);
|
|
+ wanderingTraderAllowTrading = getBoolean("mobs.wandering_trader.allow-trading", wanderingTraderAllowTrading);
|
|
+ wanderingTraderAlwaysDropExp = getBoolean("mobs.wandering_trader.always-drop-exp", wanderingTraderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wardenRidable = false;
|
|
+ public boolean wardenRidableInWater = true;
|
|
+ public boolean wardenControllable = true;
|
|
+ private void wardenSettings() {
|
|
+ wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable);
|
|
+ wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater);
|
|
+ wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable);
|
|
+ }
|
|
+
|
|
+ public boolean witchRidable = false;
|
|
+ public boolean witchRidableInWater = true;
|
|
+ public boolean witchControllable = true;
|
|
+ public double witchMaxHealth = 26.0D;
|
|
+ public double witchScale = 1.0D;
|
|
+ public boolean witchTakeDamageFromWater = false;
|
|
+ public boolean witchAlwaysDropExp = false;
|
|
+ private void witchSettings() {
|
|
+ witchRidable = getBoolean("mobs.witch.ridable", witchRidable);
|
|
+ witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater);
|
|
+ witchControllable = getBoolean("mobs.witch.controllable", witchControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.witch.attributes.max-health", witchMaxHealth);
|
|
+ set("mobs.witch.attributes.max-health", null);
|
|
+ set("mobs.witch.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth);
|
|
+ witchScale = Mth.clamp(getDouble("mobs.witch.attributes.scale", witchScale), 0.0625D, 16.0D);
|
|
+ witchTakeDamageFromWater = getBoolean("mobs.witch.takes-damage-from-water", witchTakeDamageFromWater);
|
|
+ witchAlwaysDropExp = getBoolean("mobs.witch.always-drop-exp", witchAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean witherRidable = false;
|
|
+ public boolean witherRidableInWater = true;
|
|
+ public boolean witherControllable = true;
|
|
+ public double witherMaxY = 320D;
|
|
+ public double witherMaxHealth = 300.0D;
|
|
+ public double witherScale = 1.0D;
|
|
+ public float witherHealthRegenAmount = 1.0f;
|
|
+ public int witherHealthRegenDelay = 20;
|
|
+ public boolean witherBypassMobGriefing = false;
|
|
+ public boolean witherTakeDamageFromWater = false;
|
|
+ public boolean witherCanRideVehicles = false;
|
|
+ public float witherExplosionRadius = 1.0F;
|
|
+ public boolean witherPlaySpawnSound = true;
|
|
+ public boolean witherAlwaysDropExp = false;
|
|
+ private void witherSettings() {
|
|
+ witherRidable = getBoolean("mobs.wither.ridable", witherRidable);
|
|
+ witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater);
|
|
+ witherControllable = getBoolean("mobs.wither.controllable", witherControllable);
|
|
+ witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.wither.max-health", witherMaxHealth);
|
|
+ set("mobs.wither.max_health", null);
|
|
+ set("mobs.wither.attributes.max-health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wither.attributes.max-health", witherMaxHealth);
|
|
+ set("mobs.wither.attributes.max-health", null);
|
|
+ set("mobs.wither.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth);
|
|
+ witherScale = Mth.clamp(getDouble("mobs.wither.attributes.scale", witherScale), 0.0625D, 16.0D);
|
|
+ witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount);
|
|
+ witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay);
|
|
+ witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing);
|
|
+ witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater);
|
|
+ witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles);
|
|
+ witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius);
|
|
+ witherPlaySpawnSound = getBoolean("mobs.wither.play-spawn-sound", witherPlaySpawnSound);
|
|
+ witherAlwaysDropExp = getBoolean("mobs.wither.always-drop-exp", witherAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean witherSkeletonRidable = false;
|
|
+ public boolean witherSkeletonRidableInWater = true;
|
|
+ public boolean witherSkeletonControllable = true;
|
|
+ public double witherSkeletonMaxHealth = 20.0D;
|
|
+ public double witherSkeletonScale = 1.0D;
|
|
+ public boolean witherSkeletonTakeDamageFromWater = false;
|
|
+ public boolean witherSkeletonAlwaysDropExp = false;
|
|
+ private void witherSkeletonSettings() {
|
|
+ witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable);
|
|
+ witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater);
|
|
+ witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wither_skeleton.attributes.max-health", witherSkeletonMaxHealth);
|
|
+ set("mobs.wither_skeleton.attributes.max-health", null);
|
|
+ set("mobs.wither_skeleton.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth);
|
|
+ witherSkeletonScale = Mth.clamp(getDouble("mobs.wither_skeleton.attributes.scale", witherSkeletonScale), 0.0625D, 16.0D);
|
|
+ witherSkeletonTakeDamageFromWater = getBoolean("mobs.wither_skeleton.takes-damage-from-water", witherSkeletonTakeDamageFromWater);
|
|
+ witherSkeletonAlwaysDropExp = getBoolean("mobs.wither_skeleton.always-drop-exp", witherSkeletonAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wolfRidable = false;
|
|
+ public boolean wolfRidableInWater = true;
|
|
+ public boolean wolfControllable = true;
|
|
+ public double wolfMaxHealth = 8.0D;
|
|
+ public double wolfScale = 1.0D;
|
|
+ public DyeColor wolfDefaultCollarColor = DyeColor.RED;
|
|
+ public boolean wolfMilkCuresRabies = true;
|
|
+ public double wolfNaturalRabid = 0.0D;
|
|
+ public int wolfBreedingTicks = 6000;
|
|
+ public boolean wolfTakeDamageFromWater = false;
|
|
+ public boolean wolfAlwaysDropExp = false;
|
|
+ private void wolfSettings() {
|
|
+ wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable);
|
|
+ wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater);
|
|
+ wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wolf.attributes.max-health", wolfMaxHealth);
|
|
+ set("mobs.wolf.attributes.max-health", null);
|
|
+ set("mobs.wolf.attributes.max_health", oldValue);
|
|
+ }
|
|
+ wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth);
|
|
+ wolfScale = Mth.clamp(getDouble("mobs.wolf.attributes.scale", wolfScale), 0.0625D, 16.0D);
|
|
+ try {
|
|
+ wolfDefaultCollarColor = DyeColor.valueOf(getString("mobs.wolf.default-collar-color", wolfDefaultCollarColor.name()));
|
|
+ } catch (IllegalArgumentException ignore) {
|
|
+ wolfDefaultCollarColor = DyeColor.RED;
|
|
+ }
|
|
+ wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies);
|
|
+ wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid);
|
|
+ wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks);
|
|
+ wolfTakeDamageFromWater = getBoolean("mobs.wolf.takes-damage-from-water", wolfTakeDamageFromWater);
|
|
+ wolfAlwaysDropExp = getBoolean("mobs.wolf.always-drop-exp", wolfAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zoglinRidable = false;
|
|
+ public boolean zoglinRidableInWater = true;
|
|
+ public boolean zoglinControllable = true;
|
|
+ public double zoglinMaxHealth = 40.0D;
|
|
+ public double zoglinScale = 1.0D;
|
|
+ public boolean zoglinTakeDamageFromWater = false;
|
|
+ public boolean zoglinAlwaysDropExp = false;
|
|
+ private void zoglinSettings() {
|
|
+ zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable);
|
|
+ zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater);
|
|
+ zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zoglin.attributes.max-health", zoglinMaxHealth);
|
|
+ set("mobs.zoglin.attributes.max-health", null);
|
|
+ set("mobs.zoglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth);
|
|
+ zoglinScale = Mth.clamp(getDouble("mobs.zoglin.attributes.scale", zoglinScale), 0.0625D, 16.0D);
|
|
+ zoglinTakeDamageFromWater = getBoolean("mobs.zoglin.takes-damage-from-water", zoglinTakeDamageFromWater);
|
|
+ zoglinAlwaysDropExp = getBoolean("mobs.zoglin.always-drop-exp", zoglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombieRidable = false;
|
|
+ public boolean zombieRidableInWater = true;
|
|
+ public boolean zombieControllable = true;
|
|
+ public double zombieMaxHealth = 20.0D;
|
|
+ public double zombieScale = 1.0D;
|
|
+ public double zombieSpawnReinforcements = 0.1D;
|
|
+ public boolean zombieJockeyOnlyBaby = true;
|
|
+ public double zombieJockeyChance = 0.05D;
|
|
+ public boolean zombieJockeyTryExistingChickens = true;
|
|
+ public boolean zombieAggressiveTowardsVillagerWhenLagging = true;
|
|
+ public boolean zombieBypassMobGriefing = false;
|
|
+ public boolean zombieTakeDamageFromWater = false;
|
|
+ public boolean zombieAlwaysDropExp = false;
|
|
+ public double zombieHeadVisibilityPercent = 0.5D;
|
|
+ private void zombieSettings() {
|
|
+ zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable);
|
|
+ zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater);
|
|
+ zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie.attributes.max-health", zombieMaxHealth);
|
|
+ set("mobs.zombie.attributes.max-health", null);
|
|
+ set("mobs.zombie.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth);
|
|
+ zombieScale = Mth.clamp(getDouble("mobs.zombie.attributes.scale", zombieScale), 0.0625D, 16.0D);
|
|
+ zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements);
|
|
+ zombieJockeyOnlyBaby = getBoolean("mobs.zombie.jockey.only-babies", zombieJockeyOnlyBaby);
|
|
+ zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance);
|
|
+ zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens);
|
|
+ zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging);
|
|
+ zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing);
|
|
+ zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater);
|
|
+ zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp);
|
|
+ zombieHeadVisibilityPercent = getDouble("mobs.zombie.head-visibility-percent", zombieHeadVisibilityPercent);
|
|
+ }
|
|
+
|
|
+ public boolean zombieHorseRidable = false;
|
|
+ public boolean zombieHorseRidableInWater = false;
|
|
+ public boolean zombieHorseCanSwim = false;
|
|
+ public double zombieHorseMaxHealthMin = 15.0D;
|
|
+ public double zombieHorseMaxHealthMax = 15.0D;
|
|
+ public double zombieHorseJumpStrengthMin = 0.4D;
|
|
+ public double zombieHorseJumpStrengthMax = 1.0D;
|
|
+ public double zombieHorseMovementSpeedMin = 0.2D;
|
|
+ public double zombieHorseMovementSpeedMax = 0.2D;
|
|
+ public double zombieHorseSpawnChance = 0.0D;
|
|
+ public boolean zombieHorseTakeDamageFromWater = false;
|
|
+ public boolean zombieHorseAlwaysDropExp = false;
|
|
+ private void zombieHorseSettings() {
|
|
+ zombieHorseRidable = getBoolean("mobs.zombie_horse.ridable", zombieHorseRidable);
|
|
+ zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater);
|
|
+ zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie_horse.attributes.max-health", zombieHorseMaxHealthMin);
|
|
+ set("mobs.zombie_horse.attributes.max-health", null);
|
|
+ set("mobs.zombie_horse.attributes.max_health.min", oldValue);
|
|
+ set("mobs.zombie_horse.attributes.max_health.max", oldValue);
|
|
+ }
|
|
+ zombieHorseMaxHealthMin = getDouble("mobs.zombie_horse.attributes.max_health.min", zombieHorseMaxHealthMin);
|
|
+ zombieHorseMaxHealthMax = getDouble("mobs.zombie_horse.attributes.max_health.max", zombieHorseMaxHealthMax);
|
|
+ zombieHorseJumpStrengthMin = getDouble("mobs.zombie_horse.attributes.jump_strength.min", zombieHorseJumpStrengthMin);
|
|
+ zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax);
|
|
+ zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin);
|
|
+ zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax);
|
|
+ zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance);
|
|
+ zombieHorseTakeDamageFromWater = getBoolean("mobs.zombie_horse.takes-damage-from-water", zombieHorseTakeDamageFromWater);
|
|
+ zombieHorseAlwaysDropExp = getBoolean("mobs.zombie_horse.always-drop-exp", zombieHorseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombieVillagerRidable = false;
|
|
+ public boolean zombieVillagerRidableInWater = true;
|
|
+ public boolean zombieVillagerControllable = true;
|
|
+ public double zombieVillagerMaxHealth = 20.0D;
|
|
+ public double zombieVillagerScale = 1.0D;
|
|
+ public double zombieVillagerSpawnReinforcements = 0.1D;
|
|
+ public boolean zombieVillagerJockeyOnlyBaby = true;
|
|
+ public double zombieVillagerJockeyChance = 0.05D;
|
|
+ public boolean zombieVillagerJockeyTryExistingChickens = true;
|
|
+ public boolean zombieVillagerTakeDamageFromWater = false;
|
|
+ public int zombieVillagerCuringTimeMin = 3600;
|
|
+ public int zombieVillagerCuringTimeMax = 6000;
|
|
+ public boolean zombieVillagerCureEnabled = true;
|
|
+ public boolean zombieVillagerAlwaysDropExp = false;
|
|
+ private void zombieVillagerSettings() {
|
|
+ zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable);
|
|
+ zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater);
|
|
+ zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie_villager.attributes.max-health", zombieVillagerMaxHealth);
|
|
+ set("mobs.zombie_villager.attributes.max-health", null);
|
|
+ set("mobs.zombie_villager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth);
|
|
+ zombieVillagerScale = Mth.clamp(getDouble("mobs.zombie_villager.attributes.scale", zombieVillagerScale), 0.0625D, 16.0D);
|
|
+ zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements);
|
|
+ zombieVillagerJockeyOnlyBaby = getBoolean("mobs.zombie_villager.jockey.only-babies", zombieVillagerJockeyOnlyBaby);
|
|
+ zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance);
|
|
+ zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens);
|
|
+ zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater);
|
|
+ zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin);
|
|
+ zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax);
|
|
+ zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled);
|
|
+ zombieVillagerAlwaysDropExp = getBoolean("mobs.zombie_villager.always-drop-exp", zombieVillagerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombifiedPiglinRidable = false;
|
|
+ public boolean zombifiedPiglinRidableInWater = true;
|
|
+ public boolean zombifiedPiglinControllable = true;
|
|
+ public double zombifiedPiglinMaxHealth = 20.0D;
|
|
+ public double zombifiedPiglinScale = 1.0D;
|
|
+ public double zombifiedPiglinSpawnReinforcements = 0.0D;
|
|
+ public boolean zombifiedPiglinJockeyOnlyBaby = true;
|
|
+ public double zombifiedPiglinJockeyChance = 0.05D;
|
|
+ public boolean zombifiedPiglinJockeyTryExistingChickens = true;
|
|
+ public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = true;
|
|
+ public boolean zombifiedPiglinTakeDamageFromWater = false;
|
|
+ public boolean zombifiedPiglinAlwaysDropExp = false;
|
|
+ private void zombifiedPiglinSettings() {
|
|
+ zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable);
|
|
+ zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater);
|
|
+ zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombified_piglin.attributes.max-health", zombifiedPiglinMaxHealth);
|
|
+ set("mobs.zombified_piglin.attributes.max-health", null);
|
|
+ set("mobs.zombified_piglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth);
|
|
+ zombifiedPiglinScale = Mth.clamp(getDouble("mobs.zombified_piglin.attributes.scale", zombifiedPiglinScale), 0.0625D, 16.0D);
|
|
+ zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements);
|
|
+ zombifiedPiglinJockeyOnlyBaby = getBoolean("mobs.zombified_piglin.jockey.only-babies", zombifiedPiglinJockeyOnlyBaby);
|
|
+ zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance);
|
|
+ zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens);
|
|
+ zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry);
|
|
+ zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater);
|
|
+ zombifiedPiglinAlwaysDropExp = getBoolean("mobs.zombified_piglin.always-drop-exp", zombifiedPiglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public float hungerStarvationDamage = 1.0F;
|
|
+ private void hungerSettings() {
|
|
+ hungerStarvationDamage = (float) getDouble("hunger.starvation-damage", hungerStarvationDamage);
|
|
+ }
|
|
+
|
|
+ public int conduitDistance = 16;
|
|
+ public double conduitDamageDistance = 8;
|
|
+ public float conduitDamageAmount = 4;
|
|
+ public Block[] conduitBlocks;
|
|
+ private void conduitSettings() {
|
|
+ conduitDistance = getInt("blocks.conduit.effect-distance", conduitDistance);
|
|
+ conduitDamageDistance = getDouble("blocks.conduit.mob-damage.distance", conduitDamageDistance);
|
|
+ conduitDamageAmount = (float) getDouble("blocks.conduit.mob-damage.damage-amount", conduitDamageAmount);
|
|
+ List<Block> conduitBlockList = new ArrayList<>();
|
|
+ getList("blocks.conduit.valid-ring-blocks", new ArrayList<String>(){{
|
|
+ add("minecraft:prismarine");
|
|
+ add("minecraft:prismarine_bricks");
|
|
+ add("minecraft:sea_lantern");
|
|
+ add("minecraft:dark_prismarine");
|
|
+ }}).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ conduitBlockList.add(block);
|
|
+ }
|
|
+ });
|
|
+ conduitBlocks = conduitBlockList.toArray(Block[]::new);
|
|
+ }
|
|
+
|
|
+ public float cauldronRainChance = 0.05F;
|
|
+ public float cauldronPowderSnowChance = 0.1F;
|
|
+ public float cauldronDripstoneWaterFillChance = 0.17578125F;
|
|
+ public float cauldronDripstoneLavaFillChance = 0.05859375F;
|
|
+ private void cauldronSettings() {
|
|
+ cauldronRainChance = (float) getDouble("blocks.cauldron.fill-chances.rain", cauldronRainChance);
|
|
+ cauldronPowderSnowChance = (float) getDouble("blocks.cauldron.fill-chances.powder-snow", cauldronPowderSnowChance);
|
|
+ cauldronDripstoneWaterFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-water", cauldronDripstoneWaterFillChance);
|
|
+ cauldronDripstoneLavaFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-lava", cauldronDripstoneLavaFillChance);
|
|
+ }
|
|
+
|
|
+ public float shearsCanDefuseTntChance = 0.00F;
|
|
+ public boolean shearsCanDefuseTnt = false;
|
|
+ private void shearsCanDefuseTntSettings() {
|
|
+ shearsCanDefuseTntChance = (float) getDouble("gameplay-mechanics.item.shears.defuse-tnt-chance", 0.00D);
|
|
+ shearsCanDefuseTnt = shearsCanDefuseTntChance > 0.00F;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/CompassCommand.java b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..79b8490832d2a0cc7846ddcb091cb6bcac74ea45
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java
|
|
@@ -0,0 +1,27 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.task.CompassTask;
|
|
+
|
|
+public class CompassCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("compass")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.compass"))
|
|
+ .executes(context -> {
|
|
+ ServerPlayer player = context.getSource().getPlayerOrException();
|
|
+ CompassTask task = CompassTask.instance();
|
|
+ if (player.compassBar()) {
|
|
+ task.removePlayer(player.getBukkitEntity());
|
|
+ player.compassBar(false);
|
|
+ } else {
|
|
+ task.addPlayer(player.getBukkitEntity());
|
|
+ player.compassBar(true);
|
|
+ }
|
|
+ return 1;
|
|
+ })
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..40d2fab4a9728ac90c36e30c130f3116b7025d11
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java
|
|
@@ -0,0 +1,35 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class CreditsCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("credits")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.credits"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.credits.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, 1F);
|
|
+ player.connection.send(packet);
|
|
+ String output = String.format(PurpurConfig.creditsCommandOutput, player.getGameProfile().getName());
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/DemoCommand.java b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..235f3cd89f675b70a6152a00534608c0902f19fd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java
|
|
@@ -0,0 +1,35 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class DemoCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("demo")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.demo"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.demo.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.DEMO_EVENT, 0);
|
|
+ player.connection.send(packet);
|
|
+ String output = String.format(PurpurConfig.demoCommandOutput, player.getGameProfile().getName());
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/PingCommand.java b/src/main/java/org/purpurmc/purpur/command/PingCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f202b98a194604e39798fdb8e417c6d2835f71c8
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/PingCommand.java
|
|
@@ -0,0 +1,33 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.craftbukkit.util.CraftChatMessage;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class PingCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("ping")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.ping"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ping.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ String output = String.format(PurpurConfig.pingCommandOutput, player.getGameProfile().getName(), player.connection.latency());
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2621e54879e9ab0029a875f1d09eee67878b90d5
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
|
|
@@ -0,0 +1,66 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+import java.io.File;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+import java.util.stream.Collectors;
|
|
+import java.util.stream.Stream;
|
|
+
|
|
+public class PurpurCommand extends Command {
|
|
+ public PurpurCommand(String name) {
|
|
+ super(name);
|
|
+ this.description = "Purpur related commands";
|
|
+ this.usageMessage = "/purpur [reload | version]";
|
|
+ this.setPermission("bukkit.command.purpur");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
|
+ if (args.length == 1) {
|
|
+ return Stream.of("reload", "version")
|
|
+ .filter(arg -> arg.startsWith(args[0].toLowerCase()))
|
|
+ .collect(Collectors.toList());
|
|
+ }
|
|
+ return Collections.emptyList();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
|
+ if (!testPermission(sender)) return true;
|
|
+
|
|
+ if (args.length != 1) {
|
|
+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (args[0].equalsIgnoreCase("reload")) {
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
|
|
+
|
|
+ MinecraftServer console = MinecraftServer.getServer();
|
|
+ PurpurConfig.init((File) console.options.valueOf("purpur-settings"));
|
|
+ for (ServerLevel level : console.getAllLevels()) {
|
|
+ level.purpurConfig.init();
|
|
+ level.resetBreedingCooldowns();
|
|
+ }
|
|
+ console.server.reloadCount++;
|
|
+
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete.");
|
|
+ } else if (args[0].equalsIgnoreCase("version")) {
|
|
+ Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
|
|
+ if (verCmd != null) {
|
|
+ return verCmd.execute(sender, commandLabel, new String[0]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2852c07adb080c34905f5d1b19efed8ea47eecc6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java
|
|
@@ -0,0 +1,44 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.RamBarTask;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class RamBarCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("rambar")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar"))
|
|
+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ boolean result = RamBarTask.instance().togglePlayer(player.getBukkitEntity());
|
|
+ player.ramBar(result);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.rambarCommandOutput,
|
|
+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
|
|
+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
|
|
+ Placeholder.parsed("target", player.getGameProfile().getName()));
|
|
+
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/RamCommand.java b/src/main/java/org/purpurmc/purpur/command/RamCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..992f8dfc628c7485e335191e1308cdfd4eedfbe8
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/RamCommand.java
|
|
@@ -0,0 +1,30 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.RamBarTask;
|
|
+
|
|
+public class RamCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("ram")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ram"))
|
|
+ .executes(context -> {
|
|
+ CommandSourceStack sender = context.getSource();
|
|
+ RamBarTask ramBar = RamBarTask.instance();
|
|
+ sender.sendSuccess(() -> PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(PurpurConfig.ramCommandOutput,
|
|
+ Placeholder.component("allocated", ramBar.format(ramBar.getAllocated())),
|
|
+ Placeholder.component("used", ramBar.format(ramBar.getUsed())),
|
|
+ Placeholder.component("xmx", ramBar.format(ramBar.getXmx())),
|
|
+ Placeholder.component("xms", ramBar.format(ramBar.getXms())),
|
|
+ Placeholder.unparsed("percent", ((int) (ramBar.getPercent() * 100)) + "%")
|
|
+ )), false);
|
|
+ return 1;
|
|
+ })
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d8f9b044107ff7c29a83eb5378aa9f5465ba1995
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java
|
|
@@ -0,0 +1,44 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.TPSBarTask;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class TPSBarCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("tpsbar")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar"))
|
|
+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ boolean result = TPSBarTask.instance().togglePlayer(player.getBukkitEntity());
|
|
+ player.tpsBar(result);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.tpsbarCommandOutput,
|
|
+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
|
|
+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
|
|
+ Placeholder.parsed("target", player.getGameProfile().getName()));
|
|
+
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4bb475099bcf8f05d5f1474e7fbf29c57c2c40cd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java
|
|
@@ -0,0 +1,55 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.concurrent.TimeUnit;
|
|
+import java.util.function.Function;
|
|
+
|
|
+public class UptimeCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("uptime")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.uptime"))
|
|
+ .executes((context) -> execute(context.getSource()))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender) {
|
|
+ Data data = new Data();
|
|
+
|
|
+ data.format = PurpurConfig.uptimeFormat;
|
|
+ data.hide = true;
|
|
+ data.millis = System.currentTimeMillis() - MinecraftServer.startTimeMillis;
|
|
+
|
|
+ process(data, "<days>", PurpurConfig.uptimeDay, PurpurConfig.uptimeDays, TimeUnit.DAYS, TimeUnit.MILLISECONDS::toDays);
|
|
+ process(data, "<hours>", PurpurConfig.uptimeHour, PurpurConfig.uptimeHours, TimeUnit.HOURS, TimeUnit.MILLISECONDS::toHours);
|
|
+ process(data, "<minutes>", PurpurConfig.uptimeMinute, PurpurConfig.uptimeMinutes, TimeUnit.MINUTES, TimeUnit.MILLISECONDS::toMinutes);
|
|
+ data.hide = false; // never hide seconds
|
|
+ process(data, "<seconds>", PurpurConfig.uptimeSecond, PurpurConfig.uptimeSeconds, TimeUnit.SECONDS, TimeUnit.MILLISECONDS::toSeconds);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.uptimeCommandOutput, Placeholder.unparsed("uptime", data.format));
|
|
+ sender.sendSuccess(output, false);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ private static void process(Data data, String replace, String singular, String plural, TimeUnit unit, Function<Long, Long> func) {
|
|
+ if (data.format.contains(replace)) {
|
|
+ long val = func.apply(data.millis);
|
|
+ if (data.hide) data.hide = val == 0;
|
|
+ if (!data.hide) data.millis -= unit.toMillis(val);
|
|
+ data.format = data.format.replace(replace, data.hide ? "" : String.format(val == 1 ? singular : plural, val));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static class Data {
|
|
+ String format;
|
|
+ boolean hide;
|
|
+ long millis;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/configuration/transformation/VoidDamageHeightMigration.java b/src/main/java/org/purpurmc/purpur/configuration/transformation/VoidDamageHeightMigration.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a04d23bd98075cd65a24d4de8d18281d1668480f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/configuration/transformation/VoidDamageHeightMigration.java
|
|
@@ -0,0 +1,67 @@
|
|
+package org.purpurmc.purpur.configuration.transformation;
|
|
+
|
|
+import io.papermc.paper.configuration.Configurations;
|
|
+import io.papermc.paper.configuration.PaperConfigurations;
|
|
+import io.papermc.paper.configuration.type.number.DoubleOr;
|
|
+import java.util.OptionalDouble;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.spongepowered.configurate.ConfigurateException;
|
|
+import org.spongepowered.configurate.ConfigurationNode;
|
|
+import org.spongepowered.configurate.NodePath;
|
|
+import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
|
+import org.spongepowered.configurate.transformation.TransformAction;
|
|
+
|
|
+import static org.spongepowered.configurate.NodePath.path;
|
|
+
|
|
+public class VoidDamageHeightMigration implements TransformAction {
|
|
+
|
|
+ public static boolean HAS_BEEN_REGISTERED = false;
|
|
+
|
|
+ public static final String ENVIRONMENT_KEY = "environment";
|
|
+ public static final String VOID_DAMAGE_KEY = "void-damage-amount";
|
|
+ public static final String VOID_DAMAGE_MIN_HEIGHT_OFFSET_KEY = "void-damage-min-build-height-offset";
|
|
+ public static final double DEFAULT_VOID_DAMAGE_HEIGHT = -64.0D;
|
|
+ public static final double DEFAULT_VOID_DAMAGE = 4.0D;
|
|
+
|
|
+ private final String worldName;
|
|
+
|
|
+ private VoidDamageHeightMigration(String worldName) {
|
|
+ this.worldName = PaperConfigurations.WORLD_DEFAULTS.equals(worldName) ? "default" : worldName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Object @Nullable [] visitPath(final NodePath path, final ConfigurationNode value) throws ConfigurateException {
|
|
+ String purpurVoidDamageHeightPath = "world-settings." + this.worldName + ".gameplay-mechanics.void-damage-height";
|
|
+ ConfigurationNode voidDamageMinHeightOffsetNode = value.node(ENVIRONMENT_KEY, VOID_DAMAGE_MIN_HEIGHT_OFFSET_KEY);
|
|
+ if (PurpurConfig.config.contains(purpurVoidDamageHeightPath)) {
|
|
+ double purpurVoidDamageHeight = PurpurConfig.config.getDouble(purpurVoidDamageHeightPath);
|
|
+ if (purpurVoidDamageHeight != DEFAULT_VOID_DAMAGE_HEIGHT && (voidDamageMinHeightOffsetNode.empty() || voidDamageMinHeightOffsetNode.getDouble() == DEFAULT_VOID_DAMAGE_HEIGHT)) {
|
|
+ voidDamageMinHeightOffsetNode.raw(null);
|
|
+ voidDamageMinHeightOffsetNode.set(purpurVoidDamageHeight);
|
|
+ }
|
|
+ PurpurConfig.config.set(purpurVoidDamageHeightPath, null);
|
|
+ }
|
|
+
|
|
+ String purpurVoidDamagePath = "world-settings." + this.worldName + ".gameplay-mechanics.void-damage-dealt";
|
|
+ ConfigurationNode voidDamageNode = value.node(ENVIRONMENT_KEY, VOID_DAMAGE_KEY);
|
|
+ if (PurpurConfig.config.contains(purpurVoidDamagePath)) {
|
|
+ double purpurVoidDamage = PurpurConfig.config.getDouble(purpurVoidDamagePath);
|
|
+ if (purpurVoidDamage != DEFAULT_VOID_DAMAGE && (voidDamageNode.empty() || voidDamageNode.getDouble() == DEFAULT_VOID_DAMAGE)) {
|
|
+ voidDamageNode.raw(null);
|
|
+ voidDamageNode.set(new DoubleOr.Disabled(OptionalDouble.of(purpurVoidDamage)));
|
|
+ }
|
|
+ PurpurConfig.config.set(purpurVoidDamagePath, null);
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public static void apply(final ConfigurationTransformation.Builder builder, final Configurations.ContextMap contextMap) {
|
|
+ if (PurpurConfig.version < 36) {
|
|
+ HAS_BEEN_REGISTERED = true;
|
|
+ builder.addAction(path(), new VoidDamageHeightMigration(contextMap.require(Configurations.WORLD_NAME)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ed494e0ad278813a0eb261101447b84cca3ad7aa
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
|
|
@@ -0,0 +1,71 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class FlyingMoveControllerWASD extends MoveControllerWASD {
|
|
+ protected final float groundSpeedModifier;
|
|
+ protected final float flyingSpeedModifier;
|
|
+ protected int tooHighCooldown = 0;
|
|
+ protected boolean setNoGravityFlag;
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0F);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) {
|
|
+ this(entity, groundSpeedModifier, 1.0F, true);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) {
|
|
+ this(entity, groundSpeedModifier, flyingSpeedModifier, true);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) {
|
|
+ super(entity);
|
|
+ this.groundSpeedModifier = groundSpeedModifier;
|
|
+ this.flyingSpeedModifier = flyingSpeedModifier;
|
|
+ this.setNoGravityFlag = setNoGravityFlag;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = Math.max(0.0F, rider.getForwardMot());
|
|
+ float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F);
|
|
+ float strafe = rider.getStrafeMot();
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity)) {
|
|
+ entity.onSpacebar();
|
|
+ }
|
|
+
|
|
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
|
|
+ if (tooHighCooldown <= 0) {
|
|
+ tooHighCooldown = 20;
|
|
+ }
|
|
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D));
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float speed = (float) getSpeedModifier();
|
|
+
|
|
+ if (entity.onGround) {
|
|
+ speed *= groundSpeedModifier; // TODO = fix this!
|
|
+ } else {
|
|
+ speed *= flyingSpeedModifier;
|
|
+ }
|
|
+
|
|
+ if (setNoGravityFlag) {
|
|
+ entity.setNoGravity(forward > 0);
|
|
+ }
|
|
+
|
|
+ entity.setSpeed(speed);
|
|
+ entity.setVerticalMot(vertical);
|
|
+ entity.setStrafeMot(strafe);
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9383c07fa53141127106a1f289366a040960d52e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
|
|
@@ -0,0 +1,63 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD {
|
|
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity) {
|
|
+ super(entity);
|
|
+ }
|
|
+
|
|
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) {
|
|
+ super(entity, groundSpeedModifier);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot() * 0.5F;
|
|
+ float vertical = 0;
|
|
+
|
|
+ if (forward < 0.0F) {
|
|
+ forward *= 0.5F;
|
|
+ strafe *= 0.5F;
|
|
+ }
|
|
+
|
|
+ float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED);
|
|
+
|
|
+ if (entity.onGround) {
|
|
+ speed *= groundSpeedModifier;
|
|
+ }
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar()) {
|
|
+ entity.setNoGravity(true);
|
|
+ vertical = 1.0F;
|
|
+ } else {
|
|
+ entity.setNoGravity(false);
|
|
+ }
|
|
+
|
|
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
|
|
+ if (tooHighCooldown <= 0) {
|
|
+ tooHighCooldown = 20;
|
|
+ }
|
|
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D));
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(speed);
|
|
+ entity.setSpeed((float) getSpeedModifier());
|
|
+ entity.setVerticalMot(vertical);
|
|
+ entity.setStrafeMot(strafe);
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+
|
|
+ Vec3 mot = entity.getDeltaMovement();
|
|
+ if (mot.y > 0.2D) {
|
|
+ entity.setDeltaMovement(mot.x, 0.2D, mot.z);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..dd219518150ca90f89ad238904fd4095efe032d8
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
|
|
@@ -0,0 +1,79 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+
|
|
+import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.control.LookControl;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class LookControllerWASD extends LookControl {
|
|
+ protected final Mob entity;
|
|
+ private float yOffset = 0;
|
|
+ private float xOffset = 0;
|
|
+
|
|
+ public LookControllerWASD(Mob entity) {
|
|
+ super(entity);
|
|
+ this.entity = entity;
|
|
+ }
|
|
+
|
|
+ // tick
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot(), rider.getXRot());
|
|
+ }
|
|
+
|
|
+ public void vanillaTick() {
|
|
+ super.tick();
|
|
+ }
|
|
+
|
|
+ public void setYawPitch(float yRot, float xRot) {
|
|
+ entity.setXRot(normalizePitch(xRot + xOffset));
|
|
+ entity.setYRot(normalizeYaw(yRot + yOffset));
|
|
+ entity.setYHeadRot(entity.getYRot());
|
|
+ entity.xRotO = entity.getXRot();
|
|
+ entity.yRotO = entity.getYRot();
|
|
+
|
|
+ ClientboundMoveEntityPacket.PosRot entityPacket = new ClientboundMoveEntityPacket.PosRot(
|
|
+ entity.getId(),
|
|
+ (short) 0, (short) 0, (short) 0,
|
|
+ (byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F),
|
|
+ (byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F),
|
|
+ entity.onGround
|
|
+ );
|
|
+ ((ServerLevel) entity.level()).getChunkSource().broadcast(entity, entityPacket);
|
|
+ }
|
|
+
|
|
+ public void setOffsets(float yaw, float pitch) {
|
|
+ yOffset = yaw;
|
|
+ xOffset = pitch;
|
|
+ }
|
|
+
|
|
+ public float normalizeYaw(float yaw) {
|
|
+ yaw %= 360.0f;
|
|
+ if (yaw >= 180.0f) {
|
|
+ yaw -= 360.0f;
|
|
+ } else if (yaw < -180.0f) {
|
|
+ yaw += 360.0f;
|
|
+ }
|
|
+ return yaw;
|
|
+ }
|
|
+
|
|
+ public float normalizePitch(float pitch) {
|
|
+ if (pitch > 90.0f) {
|
|
+ pitch = 90.0f;
|
|
+ } else if (pitch < -90.0f) {
|
|
+ pitch = -90.0f;
|
|
+ }
|
|
+ return pitch;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ad85c1ff6cd5d5ce2262bdb367ce9c8a5b707170
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
|
|
@@ -0,0 +1,92 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.ai.control.MoveControl;
|
|
+import net.minecraft.world.entity.player.Input;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import org.purpurmc.purpur.event.entity.RidableSpacebarEvent;
|
|
+
|
|
+public class MoveControllerWASD extends MoveControl {
|
|
+ protected final Mob entity;
|
|
+ private final double speedModifier;
|
|
+
|
|
+ public MoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0D);
|
|
+ }
|
|
+
|
|
+ public MoveControllerWASD(Mob entity, double speedModifier) {
|
|
+ super(entity);
|
|
+ this.entity = entity;
|
|
+ this.speedModifier = speedModifier;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void vanillaTick() {
|
|
+ super.tick();
|
|
+ }
|
|
+
|
|
+ public void purpurTick(Player rider) {
|
|
+ Input lastClientInput = ((ServerPlayer) rider).getLastClientInput();
|
|
+ float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F) * 0.5F;
|
|
+ float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.25F;
|
|
+
|
|
+ if (forward <= 0.0F) {
|
|
+ forward *= 0.5F;
|
|
+ }
|
|
+
|
|
+ float yawOffset = 0;
|
|
+ if (strafe != 0) {
|
|
+ if (forward == 0) {
|
|
+ yawOffset += strafe > 0 ? -90 : 90;
|
|
+ forward = Math.abs(strafe * 2);
|
|
+ } else {
|
|
+ yawOffset += strafe > 0 ? -30 : 30;
|
|
+ strafe /= 2;
|
|
+ if (forward < 0) {
|
|
+ yawOffset += strafe > 0 ? -110 : 110;
|
|
+ forward *= -1;
|
|
+ }
|
|
+ }
|
|
+ } else if (forward < 0) {
|
|
+ yawOffset -= 180;
|
|
+ forward *= -1;
|
|
+ }
|
|
+
|
|
+ ((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0);
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) {
|
|
+ entity.jumpFromGround();
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
|
|
+
|
|
+ entity.setSpeed((float) getSpeedModifier());
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+
|
|
+ public static boolean spacebarEvent(Mob entity) {
|
|
+ if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
+ return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent();
|
|
+ } else {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ba2a37dad43e238e54632975abea8ee6fafaa9e0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
|
|
@@ -0,0 +1,50 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class WaterMoveControllerWASD extends MoveControllerWASD {
|
|
+ private final double speedModifier;
|
|
+
|
|
+ public WaterMoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0D);
|
|
+ }
|
|
+
|
|
+ public WaterMoveControllerWASD(Mob entity, double speedModifier) {
|
|
+ super(entity);
|
|
+ this.speedModifier = speedModifier;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot() * 0.5F; // strafe slower by default
|
|
+ float vertical = -(rider.xRotO / 90);
|
|
+
|
|
+ if (forward == 0.0F) {
|
|
+ // strafe slower if not moving forward
|
|
+ strafe *= 0.5F;
|
|
+ // do not move vertically if not moving forward
|
|
+ vertical = 0.0F;
|
|
+ } else if (forward < 0.0F) {
|
|
+ // water animals can't swim backwards
|
|
+ forward = 0.0F;
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity)) {
|
|
+ entity.onSpacebar();
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
|
|
+ entity.setSpeed((float) getSpeedModifier() * 0.1F);
|
|
+
|
|
+ entity.setForwardMot(forward * (float) speedModifier);
|
|
+ entity.setStrafeMot(strafe * (float) speedModifier);
|
|
+ entity.setVerticalMot(vertical * (float) speedModifier);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f25abee6dbf99c8d08f8e09db02b41df86115faa
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
|
|
@@ -0,0 +1,107 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.animal.Dolphin;
|
|
+import net.minecraft.world.entity.projectile.LlamaSpit;
|
|
+import net.minecraft.world.entity.projectile.ProjectileUtil;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.EntityHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+import org.bukkit.event.entity.EntityRemoveEvent;
|
|
+
|
|
+public class DolphinSpit extends LlamaSpit {
|
|
+ public LivingEntity dolphin;
|
|
+ public int ticksLived;
|
|
+
|
|
+ public DolphinSpit(EntityType<? extends LlamaSpit> type, Level world) {
|
|
+ super(type, world);
|
|
+ }
|
|
+
|
|
+ public DolphinSpit(Level world, Dolphin dolphin) {
|
|
+ this(EntityType.LLAMA_SPIT, world);
|
|
+ setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin);
|
|
+ this.dolphin = dolphin;
|
|
+ this.setPos(
|
|
+ dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(dolphin.yBodyRot * 0.017453292F),
|
|
+ dolphin.getEyeY() - 0.10000000149011612D,
|
|
+ dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F));
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ public void tick() {
|
|
+ super_tick();
|
|
+
|
|
+ Vec3 mot = this.getDeltaMovement();
|
|
+ HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
|
|
+
|
|
+ this.preHitTargetOrDeflectSelf(hitResult);
|
|
+
|
|
+ double x = this.getX() + mot.x;
|
|
+ double y = this.getY() + mot.y;
|
|
+ double z = this.getZ() + mot.z;
|
|
+
|
|
+ this.updateRotation();
|
|
+
|
|
+ Vec3 motDouble = mot.scale(2.0);
|
|
+ for (int i = 0; i < 5; i++) {
|
|
+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.BUBBLE,
|
|
+ getX() + random.nextFloat() / 2 - 0.25F,
|
|
+ getY() + random.nextFloat() / 2 - 0.25F,
|
|
+ getZ() + random.nextFloat() / 2 - 0.25F,
|
|
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
|
|
+ }
|
|
+
|
|
+ if (++ticksLived > 20) {
|
|
+ this.discard(EntityRemoveEvent.Cause.DISCARD);
|
|
+ } else {
|
|
+ this.setDeltaMovement(mot.scale(0.99D));
|
|
+ if (!this.isNoGravity()) {
|
|
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
|
|
+ }
|
|
+
|
|
+ this.setPos(x, y, z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
|
|
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
|
|
+ .scale(speed));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
+ Entity shooter = this.getOwner();
|
|
+ if (shooter instanceof LivingEntity) {
|
|
+ entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.dolphinSpitDamage);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitBlock(BlockHitResult blockHitResult) {
|
|
+ if (this.hitCancelled) {
|
|
+ return;
|
|
+ }
|
|
+ BlockState state = this.level().getBlockState(blockHitResult.getBlockPos());
|
|
+ state.onProjectileHit(this.level(), state, blockHitResult, this);
|
|
+ this.discard(EntityRemoveEvent.Cause.DISCARD);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..58957b0bd3cd2c37fd4a6766a02e2506d9f51010
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
|
|
@@ -0,0 +1,129 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.decoration.ArmorStand;
|
|
+import net.minecraft.world.entity.monster.Phantom;
|
|
+import net.minecraft.world.entity.projectile.LlamaSpit;
|
|
+import net.minecraft.world.entity.projectile.ProjectileUtil;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.EntityHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+public class PhantomFlames extends LlamaSpit {
|
|
+ public Phantom phantom;
|
|
+ public int ticksLived;
|
|
+ public boolean canGrief = false;
|
|
+
|
|
+ public PhantomFlames(EntityType<? extends LlamaSpit> type, Level world) {
|
|
+ super(type, world);
|
|
+ }
|
|
+
|
|
+ public PhantomFlames(Level world, Phantom phantom) {
|
|
+ this(EntityType.LLAMA_SPIT, world);
|
|
+ setOwner(phantom.getRider() != null ? phantom.getRider() : phantom);
|
|
+ this.phantom = phantom;
|
|
+ this.setPos(
|
|
+ phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * 0.017453292F),
|
|
+ phantom.getEyeY() - 0.10000000149011612D,
|
|
+ phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * 0.017453292F));
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ public void tick() {
|
|
+ super_tick();
|
|
+
|
|
+ Vec3 mot = this.getDeltaMovement();
|
|
+ HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
|
|
+
|
|
+ this.preHitTargetOrDeflectSelf(hitResult);
|
|
+
|
|
+ double x = this.getX() + mot.x;
|
|
+ double y = this.getY() + mot.y;
|
|
+ double z = this.getZ() + mot.z;
|
|
+
|
|
+ this.updateRotation();
|
|
+
|
|
+ Vec3 motDouble = mot.scale(2.0);
|
|
+ for (int i = 0; i < 5; i++) {
|
|
+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.FLAME,
|
|
+ getX() + random.nextFloat() / 2 - 0.25F,
|
|
+ getY() + random.nextFloat() / 2 - 0.25F,
|
|
+ getZ() + random.nextFloat() / 2 - 0.25F,
|
|
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
|
|
+ }
|
|
+
|
|
+ if (++ticksLived > 20) {
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ } else if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ } else if (this.isInWaterOrBubble()) {
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ } else {
|
|
+ this.setDeltaMovement(mot.scale(0.99D));
|
|
+ if (!this.isNoGravity()) {
|
|
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
|
|
+ }
|
|
+
|
|
+ this.setPos(x, y, z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
|
|
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
|
|
+ .scale(speed));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
+ Level world = this.level();
|
|
+
|
|
+ if (world instanceof ServerLevel worldserver) {
|
|
+ Entity shooter = this.getOwner();
|
|
+ if (shooter instanceof LivingEntity) {
|
|
+ Entity target = entityHitResult.getEntity();
|
|
+ if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) {
|
|
+ boolean hurt = target.hurtServer(worldserver, target.damageSources().mobProjectile(this, (LivingEntity) shooter), worldserver.purpurConfig.phantomFlameDamage);
|
|
+ if (hurt && worldserver.purpurConfig.phantomFlameFireTime > 0) {
|
|
+ target.igniteForSeconds(worldserver.purpurConfig.phantomFlameFireTime);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitBlock(BlockHitResult blockHitResult) {
|
|
+ Level world = this.level();
|
|
+
|
|
+ if (world instanceof ServerLevel worldserver) {
|
|
+ if (this.hitCancelled) {
|
|
+ return;
|
|
+ }
|
|
+ if (this.canGrief) {
|
|
+ BlockState state = worldserver.getBlockState(blockHitResult.getBlockPos());
|
|
+ state.onProjectileHit(worldserver, state, blockHitResult, this);
|
|
+ }
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java b/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7608bf0981fa0d37031e51e57e4086cb5ec4c88b
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java
|
|
@@ -0,0 +1,106 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.nbt.Tag;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.item.component.CustomData;
|
|
+import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
|
+import org.bukkit.block.EntityBlockStorage;
|
|
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
|
|
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
|
|
+import org.bukkit.entity.Bee;
|
|
+import org.bukkit.entity.EntityType;
|
|
+import org.bukkit.persistence.PersistentDataContainer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.util.Locale;
|
|
+
|
|
+public class PurpurStoredBee implements StoredEntity<Bee> {
|
|
+ private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
|
|
+
|
|
+ private final EntityBlockStorage<Bee> blockStorage;
|
|
+ private final BeehiveBlockEntity.BeeData handle;
|
|
+ private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(PurpurStoredBee.DATA_TYPE_REGISTRY);
|
|
+
|
|
+ private Component customName;
|
|
+
|
|
+ public PurpurStoredBee(BeehiveBlockEntity.BeeData data, EntityBlockStorage<Bee> blockStorage) {
|
|
+ this.handle = data;
|
|
+ this.blockStorage = blockStorage;
|
|
+
|
|
+ CompoundTag customData = handle.occupant.entityData().copyTag();
|
|
+ this.customName = customData.contains("CustomName")
|
|
+ ? PaperAdventure.asAdventure(net.minecraft.network.chat.Component.Serializer.fromJson(customData.getString("CustomName"), MinecraftServer.getDefaultRegistryAccess()))
|
|
+ : null;
|
|
+
|
|
+ if(customData.contains("BukkitValues", Tag.TAG_COMPOUND)) {
|
|
+ this.persistentDataContainer.putAll(customData.getCompound("BukkitValues"));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public BeehiveBlockEntity.BeeData getHandle() {
|
|
+ return handle;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Component customName() {
|
|
+ return customName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void customName(@Nullable Component customName) {
|
|
+ this.customName = customName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable String getCustomName() {
|
|
+ return PaperAdventure.asPlain(customName, Locale.US);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setCustomName(@Nullable String name) {
|
|
+ customName(name != null ? Component.text(name) : null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull PersistentDataContainer getPersistentDataContainer() {
|
|
+ return persistentDataContainer;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasBeenReleased() {
|
|
+ return !blockStorage.getEntities().contains(this);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Bee release() {
|
|
+ return blockStorage.releaseEntity(this);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable EntityBlockStorage<Bee> getBlockStorage() {
|
|
+ if(hasBeenReleased()) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ return blockStorage;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull EntityType getType() {
|
|
+ return EntityType.BEE;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void update() {
|
|
+ handle.occupant.entityData().copyTag().put("BukkitValues", this.persistentDataContainer.toTagCompound());
|
|
+ if(customName == null) {
|
|
+ handle.occupant.entityData().copyTag().remove("CustomName");
|
|
+ } else {
|
|
+ handle.occupant.entityData().copyTag().putString("CustomName", net.minecraft.network.chat.Component.Serializer.toJson(PaperAdventure.asVanilla(customName), MinecraftServer.getDefaultRegistryAccess()));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8babdaddd8b33278aea0369dbbeeb445abe45016
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
|
|
@@ -0,0 +1,20 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+
|
|
+import java.util.EnumSet;
|
|
+
|
|
+public class HasRider extends Goal {
|
|
+ public final Mob entity;
|
|
+
|
|
+ public HasRider(Mob entity) {
|
|
+ this.entity = entity;
|
|
+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return entity.getRider() != null && entity.isControllable();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..432f4f3d82af2f19820890b68d33189a9f2c69f9
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
|
|
@@ -0,0 +1,17 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
|
+
|
|
+public class HorseHasRider extends HasRider {
|
|
+ public final AbstractHorse horse;
|
|
+
|
|
+ public HorseHasRider(AbstractHorse entity) {
|
|
+ super(entity);
|
|
+ this.horse = entity;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && horse.isSaddled();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..18a95e043cbffa65eeaaf65ff7695e5dc939820c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
|
|
@@ -0,0 +1,17 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.animal.horse.Llama;
|
|
+
|
|
+public class LlamaHasRider extends HasRider {
|
|
+ public final Llama llama;
|
|
+
|
|
+ public LlamaHasRider(Llama entity) {
|
|
+ super(entity);
|
|
+ this.llama = entity;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && llama.isSaddled() && llama.isControllable();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9660716f4162a4441c6e1b0baddef8f5086566c5
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java
|
|
@@ -0,0 +1,91 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+import net.minecraft.world.entity.animal.IronGolem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+
|
|
+import java.util.EnumSet;
|
|
+import java.util.UUID;
|
|
+
|
|
+public class ReceiveFlower extends Goal {
|
|
+ private final IronGolem irongolem;
|
|
+ private ServerPlayer target;
|
|
+ private int cooldown;
|
|
+
|
|
+ public ReceiveFlower(IronGolem entity) {
|
|
+ this.irongolem = entity;
|
|
+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ if (this.irongolem.getOfferFlowerTick() > 0) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!this.irongolem.isAngry()) {
|
|
+ return false;
|
|
+ }
|
|
+ UUID uuid = this.irongolem.getPersistentAngerTarget();
|
|
+ if (uuid == null) {
|
|
+ return false;
|
|
+ }
|
|
+ Entity target = ((ServerLevel) this.irongolem.level()).getEntity(uuid);
|
|
+ if (!(target instanceof ServerPlayer player)) {
|
|
+ return false;
|
|
+ }
|
|
+ InteractionHand hand = getPoppyHand(player);
|
|
+ if (hand == null) {
|
|
+ return false;
|
|
+ }
|
|
+ removeFlower(player, hand);
|
|
+ this.target = player;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return this.cooldown > 0;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.cooldown = 100;
|
|
+ this.irongolem.stopBeingAngry();
|
|
+ this.irongolem.offerFlower(true);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ this.irongolem.offerFlower(false);
|
|
+ this.target = null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ this.irongolem.getLookControl().setLookAt(this.target, 30.0F, 30.0F);
|
|
+ --this.cooldown;
|
|
+ }
|
|
+
|
|
+ private InteractionHand getPoppyHand(ServerPlayer player) {
|
|
+ if (isPoppy(player.getMainHandItem())) {
|
|
+ return InteractionHand.MAIN_HAND;
|
|
+ }
|
|
+ if (isPoppy(player.getOffhandItem())) {
|
|
+ return InteractionHand.OFF_HAND;
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ private void removeFlower(ServerPlayer player, InteractionHand hand) {
|
|
+ player.setItemInHand(hand, ItemStack.EMPTY);
|
|
+ }
|
|
+
|
|
+ private boolean isPoppy(ItemStack item) {
|
|
+ return item.getItem() == Blocks.POPPY.asItem();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/gui/GUIColor.java b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..550222758bf0e7deff26a6e813a860b7be365e87
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
|
|
@@ -0,0 +1,58 @@
|
|
+package org.purpurmc.purpur.gui;
|
|
+
|
|
+import net.md_5.bungee.api.ChatColor;
|
|
+
|
|
+import java.awt.Color;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+
|
|
+public enum GUIColor {
|
|
+ BLACK(ChatColor.BLACK, new Color(0x000000)),
|
|
+ DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)),
|
|
+ DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)),
|
|
+ DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)),
|
|
+ DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)),
|
|
+ DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)),
|
|
+ GOLD(ChatColor.GOLD, new Color(0xBB8800)),
|
|
+ GRAY(ChatColor.GRAY, new Color(0x888888)),
|
|
+ DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)),
|
|
+ BLUE(ChatColor.BLUE, new Color(0x5555FF)),
|
|
+ GREEN(ChatColor.GREEN, new Color(0x55FF55)),
|
|
+ AQUA(ChatColor.AQUA, new Color(0x55DDDD)),
|
|
+ RED(ChatColor.RED, new Color(0xFF5555)),
|
|
+ LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)),
|
|
+ YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)),
|
|
+ WHITE(ChatColor.WHITE, new Color(0xBBBBBB));
|
|
+
|
|
+ private final ChatColor chat;
|
|
+ private final Color color;
|
|
+
|
|
+ private static final Map<ChatColor, GUIColor> BY_CHAT = new HashMap<>();
|
|
+
|
|
+ GUIColor(ChatColor chat, Color color) {
|
|
+ this.chat = chat;
|
|
+ this.color = color;
|
|
+ }
|
|
+
|
|
+ public Color getColor() {
|
|
+ return color;
|
|
+ }
|
|
+
|
|
+ public ChatColor getChatColor() {
|
|
+ return chat;
|
|
+ }
|
|
+
|
|
+ public String getCode() {
|
|
+ return chat.toString();
|
|
+ }
|
|
+
|
|
+ public static GUIColor getColor(ChatColor chat) {
|
|
+ return BY_CHAT.get(chat);
|
|
+ }
|
|
+
|
|
+ static {
|
|
+ for (GUIColor color : values()) {
|
|
+ BY_CHAT.put(color.chat, color);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d75fb5e77eff27d86135ed7d605dbc250b660f7d
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
|
|
@@ -0,0 +1,83 @@
|
|
+package org.purpurmc.purpur.gui;
|
|
+
|
|
+import com.google.common.collect.Sets;
|
|
+import javax.swing.UIManager;
|
|
+import net.md_5.bungee.api.chat.BaseComponent;
|
|
+import net.md_5.bungee.api.chat.TextComponent;
|
|
+
|
|
+import javax.swing.JTextPane;
|
|
+import javax.swing.Timer;
|
|
+import javax.swing.text.AttributeSet;
|
|
+import javax.swing.text.BadLocationException;
|
|
+import javax.swing.text.SimpleAttributeSet;
|
|
+import javax.swing.text.StyleConstants;
|
|
+import javax.swing.text.StyleContext;
|
|
+import java.util.Set;
|
|
+
|
|
+public class JColorTextPane extends JTextPane {
|
|
+ private static final GUIColor DEFAULT_COLOR;
|
|
+ static {
|
|
+ DEFAULT_COLOR = UIManager.getSystemLookAndFeelClassName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")
|
|
+ ? GUIColor.WHITE : GUIColor.BLACK;
|
|
+ }
|
|
+
|
|
+
|
|
+ public void append(String msg) {
|
|
+ // TODO: update to use adventure instead
|
|
+ BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, DEFAULT_COLOR.getChatColor());
|
|
+ for (BaseComponent component : components) {
|
|
+ String text = component.toPlainText();
|
|
+ if (text == null || text.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ GUIColor guiColor = GUIColor.getColor(component.getColor());
|
|
+
|
|
+ StyleContext context = StyleContext.getDefaultStyleContext();
|
|
+ AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR);
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough());
|
|
+ //attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly
|
|
+
|
|
+ try {
|
|
+ int pos = getDocument().getLength();
|
|
+ getDocument().insertString(pos, text, attr);
|
|
+
|
|
+ if (component.isObfuscated()) {
|
|
+ // dirty hack to blink some text
|
|
+ Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground()));
|
|
+ BLINKS.add(blink);
|
|
+ }
|
|
+ } catch (BadLocationException ignore) {
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final Set<Blink> BLINKS = Sets.newHashSet();
|
|
+ private static boolean SYNC_BLINK;
|
|
+
|
|
+ static {
|
|
+ new Timer(500, e -> {
|
|
+ SYNC_BLINK = !SYNC_BLINK;
|
|
+ BLINKS.forEach(Blink::blink);
|
|
+ }).start();
|
|
+ }
|
|
+
|
|
+ public class Blink {
|
|
+ private final int start, length;
|
|
+ private final AttributeSet attr1, attr2;
|
|
+
|
|
+ private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) {
|
|
+ this.start = start;
|
|
+ this.length = length;
|
|
+ this.attr1 = attr1;
|
|
+ this.attr2 = attr2;
|
|
+ }
|
|
+
|
|
+ private void blink() {
|
|
+ getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b257f35caa13b660854cf17f41fd8fba1d56c458
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.purpurmc.purpur.item;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.effect.MobEffects;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.item.BlockItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import org.bukkit.event.entity.EntityPotionEffectEvent;
|
|
+
|
|
+public class GlowBerryItem extends BlockItem {
|
|
+ public GlowBerryItem(Block block, Properties settings) {
|
|
+ super(block, settings);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemStack finishUsingItem(ItemStack stack, Level world, LivingEntity user) {
|
|
+ ItemStack result = super.finishUsingItem(stack, world, user);
|
|
+ if (world.purpurConfig.glowBerriesEatGlowDuration > 0 && user instanceof ServerPlayer player) {
|
|
+ player.addEffect(new MobEffectInstance(MobEffects.GLOWING, world.purpurConfig.glowBerriesEatGlowDuration), EntityPotionEffectEvent.Cause.FOOD);
|
|
+ }
|
|
+ return result;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ed50cb2115401c9039df4136caf5a087a5f5991c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java
|
|
@@ -0,0 +1,40 @@
|
|
+package org.purpurmc.purpur.item;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.component.DataComponents;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.item.BlockItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.item.component.CustomData;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+
|
|
+public class SpawnerItem extends BlockItem {
|
|
+
|
|
+ public SpawnerItem(Block block, Properties settings) {
|
|
+ super(block, settings);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player, ItemStack stack, BlockState state) {
|
|
+ boolean handled = super.updateCustomBlockEntityTag(pos, level, player, stack, state);
|
|
+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.place.spawners")) {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+ if (blockEntity instanceof SpawnerBlockEntity spawner) {
|
|
+ CompoundTag customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
|
+ if (customData.contains("Purpur.mob_type")) {
|
|
+ EntityType.byString(customData.getString("Purpur.mob_type")).ifPresent(type -> spawner.getSpawner().setEntityId(type, level, level.random, pos));
|
|
+ } else if (customData.contains("Purpur.SpawnData")) {
|
|
+ net.minecraft.world.level.SpawnData.CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, customData.getCompound("Purpur.SpawnData")).result()
|
|
+ .ifPresent(spawnData -> spawner.getSpawner().nextSpawnData = spawnData);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return handled;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java b/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..793a3ea45fe04e84725926f17615c26e008b0ce4
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java
|
|
@@ -0,0 +1,27 @@
|
|
+package org.purpurmc.purpur.network;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.network.FriendlyByteBuf;
|
|
+import net.minecraft.network.codec.StreamCodec;
|
|
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+public record ClientboundBeehivePayload(BlockPos pos, int numOfBees) implements CustomPacketPayload {
|
|
+ public static final StreamCodec<FriendlyByteBuf, ClientboundBeehivePayload> STREAM_CODEC = CustomPacketPayload.codec(ClientboundBeehivePayload::write, ClientboundBeehivePayload::new);
|
|
+ public static final Type<ClientboundBeehivePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("purpur", "beehive_s2c"));
|
|
+
|
|
+ public ClientboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) {
|
|
+ this(friendlyByteBuf.readBlockPos(), friendlyByteBuf.readInt());
|
|
+ }
|
|
+
|
|
+ private void write(FriendlyByteBuf friendlyByteBuf) {
|
|
+ friendlyByteBuf.writeBlockPos(this.pos);
|
|
+ friendlyByteBuf.writeInt(this.numOfBees);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Type<? extends CustomPacketPayload> type() {
|
|
+ return TYPE;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java b/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..fa72769e06061609e1e658a0250e99c8cb026c0e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.purpurmc.purpur.network;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.network.FriendlyByteBuf;
|
|
+import net.minecraft.network.codec.StreamCodec;
|
|
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+public record ServerboundBeehivePayload(BlockPos pos) implements CustomPacketPayload {
|
|
+ public static final StreamCodec<FriendlyByteBuf, ServerboundBeehivePayload> STREAM_CODEC = CustomPacketPayload.codec(ServerboundBeehivePayload::write, ServerboundBeehivePayload::new);
|
|
+ public static final Type<ServerboundBeehivePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("purpur", "beehive_c2s"));
|
|
+
|
|
+ public ServerboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) {
|
|
+ this(friendlyByteBuf.readBlockPos());
|
|
+ }
|
|
+
|
|
+ private void write(FriendlyByteBuf friendlyByteBuf) {
|
|
+ friendlyByteBuf.writeBlockPos(this.pos);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Type<? extends CustomPacketPayload> type() {
|
|
+ return TYPE;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java b/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..664f9d5e1ce5e2787bf699bd11758b9e3aa8ed3a
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java
|
|
@@ -0,0 +1,67 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import io.netty.buffer.Unpooled;
|
|
+import net.minecraft.network.FriendlyByteBuf;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.plugin.PluginBase;
|
|
+import org.bukkit.plugin.messaging.PluginMessageListener;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.purpurmc.purpur.network.ClientboundBeehivePayload;
|
|
+import org.purpurmc.purpur.network.ServerboundBeehivePayload;
|
|
+import org.purpurmc.purpur.util.MinecraftInternalPlugin;
|
|
+
|
|
+public class BeehiveTask implements PluginMessageListener {
|
|
+
|
|
+ private static BeehiveTask instance;
|
|
+
|
|
+ public static BeehiveTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new BeehiveTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ private final PluginBase plugin = new MinecraftInternalPlugin();
|
|
+
|
|
+ private BeehiveTask() {
|
|
+ }
|
|
+
|
|
+ public void register() {
|
|
+ Bukkit.getMessenger().registerOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString());
|
|
+ Bukkit.getMessenger().registerIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString(), this);
|
|
+ }
|
|
+
|
|
+ public void unregister() {
|
|
+ Bukkit.getMessenger().unregisterOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString());
|
|
+ Bukkit.getMessenger().unregisterIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] bytes) {
|
|
+ FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.copiedBuffer(bytes));
|
|
+ ServerboundBeehivePayload payload = ServerboundBeehivePayload.STREAM_CODEC.decode(byteBuf);
|
|
+
|
|
+ ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
|
+
|
|
+ // targeted block info max range specified in client at net.minecraft.client.gui.hud.DebugHud#render
|
|
+ if (!payload.pos().getCenter().closerThan(serverPlayer.position(), 20)) return; // Targeted Block info max range is 20
|
|
+ if (serverPlayer.level().getChunkIfLoaded(payload.pos()) == null) return;
|
|
+
|
|
+ BlockEntity blockEntity = serverPlayer.level().getBlockEntity(payload.pos());
|
|
+ if (!(blockEntity instanceof BeehiveBlockEntity beehive)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ClientboundBeehivePayload customPacketPayload = new ClientboundBeehivePayload(payload.pos(), beehive.getOccupantCount());
|
|
+ FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
|
+ ClientboundBeehivePayload.STREAM_CODEC.encode(friendlyByteBuf, customPacketPayload);
|
|
+ byte[] byteArray = new byte[friendlyByteBuf.readableBytes()];
|
|
+ friendlyByteBuf.readBytes(byteArray);
|
|
+ player.sendPluginMessage(this.plugin, customPacketPayload.type().id().toString(), byteArray);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3c3d4cd52db93b97a40321030a70ebc282c9636b
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java
|
|
@@ -0,0 +1,121 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.scheduler.BukkitRunnable;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.HashSet;
|
|
+import java.util.Iterator;
|
|
+import java.util.Map;
|
|
+import java.util.UUID;
|
|
+import org.purpurmc.purpur.util.MinecraftInternalPlugin;
|
|
+
|
|
+public abstract class BossBarTask extends BukkitRunnable {
|
|
+ private final Map<UUID, BossBar> bossbars = new HashMap<>();
|
|
+ private boolean started;
|
|
+
|
|
+ abstract BossBar createBossBar();
|
|
+
|
|
+ abstract void updateBossBar(BossBar bossbar, Player player);
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ Iterator<Map.Entry<UUID, BossBar>> iter = bossbars.entrySet().iterator();
|
|
+ while (iter.hasNext()) {
|
|
+ Map.Entry<UUID, BossBar> entry = iter.next();
|
|
+ Player player = Bukkit.getPlayer(entry.getKey());
|
|
+ if (player == null) {
|
|
+ iter.remove();
|
|
+ continue;
|
|
+ }
|
|
+ updateBossBar(entry.getValue(), player);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void cancel() {
|
|
+ super.cancel();
|
|
+ new HashSet<>(this.bossbars.keySet()).forEach(uuid -> {
|
|
+ Player player = Bukkit.getPlayer(uuid);
|
|
+ if (player != null) {
|
|
+ removePlayer(player);
|
|
+ }
|
|
+ });
|
|
+ this.bossbars.clear();
|
|
+ }
|
|
+
|
|
+ public boolean removePlayer(Player player) {
|
|
+ BossBar bossbar = this.bossbars.remove(player.getUniqueId());
|
|
+ if (bossbar != null) {
|
|
+ player.hideBossBar(bossbar);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public void addPlayer(Player player) {
|
|
+ removePlayer(player);
|
|
+ BossBar bossbar = createBossBar();
|
|
+ this.bossbars.put(player.getUniqueId(), bossbar);
|
|
+ this.updateBossBar(bossbar, player);
|
|
+ player.showBossBar(bossbar);
|
|
+ }
|
|
+
|
|
+ public boolean hasPlayer(UUID uuid) {
|
|
+ return this.bossbars.containsKey(uuid);
|
|
+ }
|
|
+
|
|
+ public boolean togglePlayer(Player player) {
|
|
+ if (removePlayer(player)) {
|
|
+ return false;
|
|
+ }
|
|
+ addPlayer(player);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public void start() {
|
|
+ stop();
|
|
+ this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1);
|
|
+ started = true;
|
|
+ }
|
|
+
|
|
+ public void stop() {
|
|
+ if (started) {
|
|
+ cancel();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void startAll() {
|
|
+ RamBarTask.instance().start();
|
|
+ TPSBarTask.instance().start();
|
|
+ CompassTask.instance().start();
|
|
+ }
|
|
+
|
|
+ public static void stopAll() {
|
|
+ RamBarTask.instance().stop();
|
|
+ TPSBarTask.instance().stop();
|
|
+ CompassTask.instance().stop();
|
|
+ }
|
|
+
|
|
+ public static void addToAll(ServerPlayer player) {
|
|
+ Player bukkit = player.getBukkitEntity();
|
|
+ if (player.ramBar()) {
|
|
+ RamBarTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ if (player.tpsBar()) {
|
|
+ TPSBarTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ if (player.compassBar()) {
|
|
+ CompassTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void removeFromAll(Player player) {
|
|
+ RamBarTask.instance().removePlayer(player);
|
|
+ TPSBarTask.instance().removePlayer(player);
|
|
+ CompassTask.instance().removePlayer(player);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/CompassTask.java b/src/main/java/org/purpurmc/purpur/task/CompassTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bece7eefc8ba8822b433835526251d2fb916c025
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/CompassTask.java
|
|
@@ -0,0 +1,68 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.item.Items;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+public class CompassTask extends BossBarTask {
|
|
+ private static CompassTask instance;
|
|
+
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static CompassTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new CompassTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++tick < PurpurConfig.commandCompassBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ tick = 0;
|
|
+
|
|
+ MinecraftServer.getServer().getAllLevels().forEach((level) -> {
|
|
+ if (level.purpurConfig.compassItemShowsBossBar) {
|
|
+ level.players().forEach(player -> {
|
|
+ if (!player.compassBar()) {
|
|
+ if (player.getMainHandItem().getItem() != Items.COMPASS && player.getOffhandItem().getItem() != Items.COMPASS) {
|
|
+ removePlayer(player.getBukkitEntity());
|
|
+ } else if (!hasPlayer(player.getUUID())) {
|
|
+ addPlayer(player.getBukkitEntity());
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ });
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), PurpurConfig.commandCompassBarProgressPercent, PurpurConfig.commandCompassBarProgressColor, PurpurConfig.commandCompassBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ float yaw = player.getLocation().getYaw();
|
|
+ int length = PurpurConfig.commandCompassBarTitle.length();
|
|
+ int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F));
|
|
+ bossbar.name(Component.text(PurpurConfig.commandCompassBarTitle.substring(pos - 25, pos + 25)));
|
|
+ }
|
|
+
|
|
+ private float normalize(float yaw) {
|
|
+ while (yaw < -180.0F) {
|
|
+ yaw += 360.0F;
|
|
+ }
|
|
+ while (yaw > 180.0F) {
|
|
+ yaw -= 360.0F;
|
|
+ }
|
|
+ return yaw;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/RamBarTask.java b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8e98c0ae73e2c40002a72b5d0d246ffa0c3ab38f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java
|
|
@@ -0,0 +1,117 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.lang.management.ManagementFactory;
|
|
+import java.lang.management.MemoryUsage;
|
|
+
|
|
+public class RamBarTask extends BossBarTask {
|
|
+ private static RamBarTask instance;
|
|
+ private long allocated = 0L;
|
|
+ private long used = 0L;
|
|
+ private long xmx = 0L;
|
|
+ private long xms = 0L;
|
|
+ private float percent = 0F;
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static RamBarTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new RamBarTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandRamBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ bossbar.progress(getBossBarProgress());
|
|
+ bossbar.color(getBossBarColor());
|
|
+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandRamBarTitle,
|
|
+ Placeholder.component("allocated", format(this.allocated)),
|
|
+ Placeholder.component("used", format(this.used)),
|
|
+ Placeholder.component("xmx", format(this.xmx)),
|
|
+ Placeholder.component("xms", format(this.xms)),
|
|
+ Placeholder.unparsed("percent", ((int) (this.percent * 100)) + "%")
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++this.tick < PurpurConfig.commandRamBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ this.tick = 0;
|
|
+
|
|
+ MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
|
|
+
|
|
+ this.allocated = heap.getCommitted();
|
|
+ this.used = heap.getUsed();
|
|
+ this.xmx = heap.getMax();
|
|
+ this.xms = heap.getInit();
|
|
+ this.percent = Math.max(Math.min((float) this.used / this.xmx, 1.0F), 0.0F);
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ private float getBossBarProgress() {
|
|
+ return this.percent;
|
|
+ }
|
|
+
|
|
+ private BossBar.Color getBossBarColor() {
|
|
+ if (this.percent < 0.5F) {
|
|
+ return PurpurConfig.commandRamBarProgressColorGood;
|
|
+ } else if (this.percent < 0.75F) {
|
|
+ return PurpurConfig.commandRamBarProgressColorMedium;
|
|
+ } else {
|
|
+ return PurpurConfig.commandRamBarProgressColorLow;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Component format(long v) {
|
|
+ String color;
|
|
+ if (this.percent < 0.60F) {
|
|
+ color = PurpurConfig.commandRamBarTextColorGood;
|
|
+ } else if (this.percent < 0.85F) {
|
|
+ color = PurpurConfig.commandRamBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandRamBarTextColorLow;
|
|
+ }
|
|
+ String value;
|
|
+ if (v < 1024) {
|
|
+ value = v + "B";
|
|
+ } else {
|
|
+ int z = (63 - Long.numberOfLeadingZeros(v)) / 10;
|
|
+ value = String.format("%.1f%s", (double) v / (1L << (z * 10)), "BKMGTPE".charAt(z));
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.unparsed("text", value));
|
|
+ }
|
|
+
|
|
+ public long getAllocated() {
|
|
+ return this.allocated;
|
|
+ }
|
|
+
|
|
+ public long getUsed() {
|
|
+ return this.used;
|
|
+ }
|
|
+
|
|
+ public long getXmx() {
|
|
+ return this.xmx;
|
|
+ }
|
|
+
|
|
+ public long getXms() {
|
|
+ return this.xms;
|
|
+ }
|
|
+
|
|
+ public float getPercent() {
|
|
+ return this.percent;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8769993e7ca59da309087051a3cd38fc562c15d1
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java
|
|
@@ -0,0 +1,142 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.entity.Player;
|
|
+
|
|
+public class TPSBarTask extends BossBarTask {
|
|
+ private static TPSBarTask instance;
|
|
+ private double tps = 20.0D;
|
|
+ private double mspt = 0.0D;
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static TPSBarTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new TPSBarTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandTPSBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ bossbar.progress(getBossBarProgress());
|
|
+ bossbar.color(getBossBarColor());
|
|
+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle,
|
|
+ Placeholder.component("tps", getTPSColor()),
|
|
+ Placeholder.component("mspt", getMSPTColor()),
|
|
+ Placeholder.component("ping", getPingColor(player.getPing()))
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++tick < PurpurConfig.commandTPSBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ tick = 0;
|
|
+
|
|
+ this.tps = Math.max(Math.min(Bukkit.getTPS()[0], 20.0D), 0.0D);
|
|
+ this.mspt = Bukkit.getAverageTickTime();
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ private float getBossBarProgress() {
|
|
+ if (PurpurConfig.commandTPSBarProgressFillMode == FillMode.MSPT) {
|
|
+ return Math.max(Math.min((float) mspt / 50.0F, 1.0F), 0.0F);
|
|
+ } else {
|
|
+ return Math.max(Math.min((float) tps / 20.0F, 1.0F), 0.0F);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private BossBar.Color getBossBarColor() {
|
|
+ if (isGood(PurpurConfig.commandTPSBarProgressFillMode)) {
|
|
+ return PurpurConfig.commandTPSBarProgressColorGood;
|
|
+ } else if (isMedium(PurpurConfig.commandTPSBarProgressFillMode)) {
|
|
+ return PurpurConfig.commandTPSBarProgressColorMedium;
|
|
+ } else {
|
|
+ return PurpurConfig.commandTPSBarProgressColorLow;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isGood(FillMode mode) {
|
|
+ return isGood(mode, 0);
|
|
+ }
|
|
+
|
|
+ private boolean isGood(FillMode mode, int ping) {
|
|
+ if (mode == FillMode.MSPT) {
|
|
+ return mspt < 40;
|
|
+ } else if (mode == FillMode.TPS) {
|
|
+ return tps >= 19;
|
|
+ } else if (mode == FillMode.PING) {
|
|
+ return ping < 100;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isMedium(FillMode mode) {
|
|
+ return isMedium(mode, 0);
|
|
+ }
|
|
+
|
|
+ private boolean isMedium(FillMode mode, int ping) {
|
|
+ if (mode == FillMode.MSPT) {
|
|
+ return mspt < 50;
|
|
+ } else if (mode == FillMode.TPS) {
|
|
+ return tps >= 15;
|
|
+ } else if (mode == FillMode.PING) {
|
|
+ return ping < 200;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private Component getTPSColor() {
|
|
+ String color;
|
|
+ if (isGood(FillMode.TPS)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.TPS)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps)));
|
|
+ }
|
|
+
|
|
+ private Component getMSPTColor() {
|
|
+ String color;
|
|
+ if (isGood(FillMode.MSPT)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.MSPT)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", mspt)));
|
|
+ }
|
|
+
|
|
+ private Component getPingColor(int ping) {
|
|
+ String color;
|
|
+ if (isGood(FillMode.PING, ping)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.PING, ping)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping)));
|
|
+ }
|
|
+
|
|
+ public enum FillMode {
|
|
+ TPS, MSPT, PING
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Actionable.java b/src/main/java/org/purpurmc/purpur/tool/Actionable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e18c37f06730da9d3055d5215e813b1477c1e70e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Actionable.java
|
|
@@ -0,0 +1,24 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public abstract class Actionable {
|
|
+ private final Block into;
|
|
+ private final Map<Item, Double> drops;
|
|
+
|
|
+ public Actionable(Block into, Map<Item, Double> drops) {
|
|
+ this.into = into;
|
|
+ this.drops = drops;
|
|
+ }
|
|
+
|
|
+ public Block into() {
|
|
+ return into;
|
|
+ }
|
|
+
|
|
+ public Map<Item, Double> drops() {
|
|
+ return drops;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Flattenable.java b/src/main/java/org/purpurmc/purpur/tool/Flattenable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..345d4ee4ff0b78bd1050959711a4f5d16a5e8aee
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Flattenable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Flattenable extends Actionable {
|
|
+ public Flattenable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Strippable.java b/src/main/java/org/purpurmc/purpur/tool/Strippable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bf5402214f41af9c09bd6c5c4f45d330516d742e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Strippable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Strippable extends Actionable {
|
|
+ public Strippable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Tillable.java b/src/main/java/org/purpurmc/purpur/tool/Tillable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..715f6dd44480347eebced43c11bc364e05727498
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Tillable.java
|
|
@@ -0,0 +1,50 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.HoeItem;
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.item.context.UseOnContext;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import java.util.function.Predicate;
|
|
+
|
|
+public class Tillable extends Actionable {
|
|
+ private final Condition condition;
|
|
+
|
|
+ public Tillable(Condition condition, Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ this.condition = condition;
|
|
+ }
|
|
+
|
|
+ public Condition condition() {
|
|
+ return condition;
|
|
+ }
|
|
+
|
|
+ public enum Condition {
|
|
+ AIR_ABOVE(HoeItem::onlyIfAirAbove),
|
|
+ ALWAYS((useOnContext) -> true);
|
|
+
|
|
+ private final Predicate<UseOnContext> predicate;
|
|
+
|
|
+ Condition(Predicate<UseOnContext> predicate) {
|
|
+ this.predicate = predicate;
|
|
+ }
|
|
+
|
|
+ public Predicate<UseOnContext> predicate() {
|
|
+ return predicate;
|
|
+ }
|
|
+
|
|
+ private static final Map<String, Condition> BY_NAME = new HashMap<>();
|
|
+
|
|
+ static {
|
|
+ for (Condition condition : values()) {
|
|
+ BY_NAME.put(condition.name(), condition);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static Condition get(String name) {
|
|
+ return BY_NAME.get(name.toUpperCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Waxable.java b/src/main/java/org/purpurmc/purpur/tool/Waxable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..64adb13b29b6757dcf227a55588da70ecabe083f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Waxable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Waxable extends Actionable {
|
|
+ public Waxable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Weatherable.java b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b7586f494528f30eb0da82420d3bcf5b83a1a902
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Weatherable extends Actionable {
|
|
+ public Weatherable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java b/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..129acb8ad139decc6b1c023cb10bc32dc91d64d1
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java
|
|
@@ -0,0 +1,152 @@
|
|
+package org.purpurmc.purpur.util;
|
|
+
|
|
+import org.bukkit.Server;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.configuration.file.FileConfiguration;
|
|
+import org.bukkit.generator.BiomeProvider;
|
|
+import org.bukkit.generator.ChunkGenerator;
|
|
+import org.bukkit.plugin.PluginBase;
|
|
+import org.bukkit.plugin.PluginDescriptionFile;
|
|
+import org.bukkit.plugin.PluginLoader;
|
|
+import org.bukkit.plugin.PluginLogger;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.InputStream;
|
|
+import java.util.List;
|
|
+
|
|
+public class MinecraftInternalPlugin extends PluginBase {
|
|
+ private boolean enabled = true;
|
|
+
|
|
+ private final String pluginName;
|
|
+ private PluginDescriptionFile pdf;
|
|
+
|
|
+ public MinecraftInternalPlugin() {
|
|
+ this.pluginName = "Minecraft";
|
|
+ pdf = new PluginDescriptionFile(pluginName, "1.0", "nms");
|
|
+ }
|
|
+
|
|
+ public void setEnabled(boolean enabled) {
|
|
+ this.enabled = enabled;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public File getDataFolder() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginDescriptionFile getDescription() {
|
|
+ return pdf;
|
|
+ }
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
|
|
+ return pdf;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ @Override
|
|
+ public FileConfiguration getConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public InputStream getResource(String filename) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveDefaultConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveResource(String resourcePath, boolean replace) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void reloadConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginLogger getLogger() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginLoader getPluginLoader() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Server getServer() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isEnabled() {
|
|
+ return enabled;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onDisable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onLoad() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onEnable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isNaggable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setNaggable(boolean canNag) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ // Paper start - lifecycle events
|
|
+ @Override
|
|
+ public @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+ // Paper end - lifecycle events
|
|
+}
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
index 62bb1fa77045ea83afe8a181c3b3a4d7284103b7..a526121ff6af27bb0a87fec672aa23493d018df0 100644
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
@@ -211,6 +211,8 @@ public class ActivationRange
|
|
continue;
|
|
}
|
|
|
|
+ if (!player.level().purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur
|
|
+
|
|
// Paper start
|
|
int worldHeight = world.getHeight();
|
|
ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
|
|
@@ -405,6 +407,7 @@ public class ActivationRange
|
|
*/
|
|
public static boolean checkIfActive(Entity entity)
|
|
{
|
|
+ if (entity.level().purpurConfig.squidImmuneToEAR && entity instanceof net.minecraft.world.entity.animal.Squid) return true; // Purpur
|
|
// Never safe to skip fireworks or item gravity
|
|
if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Paper - Needed for item gravity, see ItemEntity tick
|
|
return true;
|
|
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
|
index d2a75850af9c6ad2aca66a5f994f1b587d73eac4..a056aa167887abef9e6d531a9edd2cda433567d2 100644
|
|
--- a/src/main/resources/log4j2.xml
|
|
+++ b/src/main/resources/log4j2.xml
|
|
@@ -2,7 +2,16 @@
|
|
<Configuration status="WARN" shutdownHook="disable">
|
|
<Appenders>
|
|
<Queue name="ServerGuiConsole">
|
|
- <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
|
|
+ <!-- Purpur start - copied from TerminalConsole -->
|
|
+ <PatternLayout>
|
|
+ <LoggerNamePatternSelector defaultPattern="%highlightGUIError{[%d{HH:mm:ss} %level]: [%logger] %stripAnsi{%msg}%n%xEx{full}}">
|
|
+ <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
|
+ <!-- Disable prefix for various plugins that bypass the plugin logger -->
|
|
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
|
|
+ pattern="%highlightGUIError{[%d{HH:mm:ss} %level]: %stripAnsi{%msg}%n%xEx{full}}" />
|
|
+ </LoggerNamePatternSelector>
|
|
+ </PatternLayout>
|
|
+ <!-- Purpur end -->
|
|
</Queue>
|
|
<TerminalConsole name="TerminalConsole">
|
|
<PatternLayout>
|
|
diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
index 75ed5050f72c001d6eab117a2c0b352a413548bd..180c0a532bbac10a8280b63eb7aa783a1bfbb237 100644
|
|
--- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
+++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
@@ -46,6 +46,7 @@ public class MinecraftCommandPermissionsTest {
|
|
Set<String> foundPerms = new HashSet<>();
|
|
for (CommandNode<CommandSourceStack> child : root.getChildren()) {
|
|
final String vanillaPerm = VanillaCommandWrapper.getPermission(child);
|
|
+ if (TO_SKIP.contains(vanillaPerm)) continue; // Purpur
|
|
if (!perms.contains(vanillaPerm)) {
|
|
missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command");
|
|
} else {
|
|
@@ -58,6 +59,25 @@ public class MinecraftCommandPermissionsTest {
|
|
}
|
|
|
|
private static final List<String> TO_SKIP = List.of(
|
|
+ // Purpur start
|
|
+ "minecraft.command.compass",
|
|
+ "minecraft.command.credits",
|
|
+ "minecraft.command.demo",
|
|
+ "minecraft.command.ping",
|
|
+ "minecraft.command.ram",
|
|
+ "minecraft.command.rambar",
|
|
+ "minecraft.command.tpsbar",
|
|
+ "minecraft.command.uptime",
|
|
+ "minecraft.command.debug",
|
|
+ "minecraft.command.gamemode.adventure",
|
|
+ "minecraft.command.gamemode.adventure.other",
|
|
+ "minecraft.command.gamemode.creative",
|
|
+ "minecraft.command.gamemode.creative.other",
|
|
+ "minecraft.command.gamemode.spectator",
|
|
+ "minecraft.command.gamemode.spectator.other",
|
|
+ "minecraft.command.gamemode.survival",
|
|
+ "minecraft.command.gamemode.survival.other",
|
|
+ // Purpur end
|
|
"minecraft.command.selector"
|
|
);
|
|
|