Compare commits

...
Author SHA1 Message Date
NotAlexNoyle 85a2070f4a Exclude CODEOWNERS from language statistics 2026-10-29 16:19:03 -04:00
NotAlexNoyle ab1365b738 Add CODEOWNERS (one staff approval) 2026-10-29 16:19:03 -04:00
NotAlexNoyle d5a72a1900 Stop duplicating OCM's sheild module 2026-08-10 07:49:54 -04:00
NotAlexNoyle 0a02f9b5d3 Add totems in hotbar 2026-06-15 09:50:09 -04:00
NotAlexNoyle aa9dd8a4e3 Update README.md 2026-03-09 08:50:16 -04:00
NotAlexNoyle 209d53e975 Merge pull request #2 from true-og/codex/remove-open-iron-doors-with-hand-module
Remove OpenIronDoorsWithHand module and config entries
2026-03-09 08:49:13 -04:00
7 changed files with 152 additions and 8 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+3 -1
View File
@@ -10,6 +10,8 @@ PurpurExtras is a plugin designed to add all [Purpur](https://github.com/PurpurM
- Lobotomized CreateSusBlockModule.
- Removed OpenIronDoorsWithHandModule (also applies to trapdoors).
- Added more error handling.
## Contact
@@ -20,4 +22,4 @@ Join us on [Discord](https://discord.gg/ma9pMYpBU6)
[![GPL-2.0 License](https://img.shields.io/github/license/PurpurMC/PurpurExtras?&logo=github)](LICENSE)
All code is licensed under the GPL-2.0 license, unless otherwise noted.
All code is licensed under the GPL-2.0 license, unless otherwise noted.
@@ -0,0 +1,125 @@
package org.purpurmc.purpurextras.modules;
import java.util.ArrayList;
import java.util.Collection;
import org.bukkit.EntityEffect;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.purpurmc.purpurextras.PurpurExtrasOG;
/**
* If enabled, a totem of undying will save a player from death while it sits
* anywhere in their hotbar, not only when held in the main hand or off hand.
* Vanilla resurrection (totem in either hand) is left untouched; this module
* only covers the remaining hotbar slots. Void deaths are intentionally ignored
* because surviving them requires the teleport handled by
* {@link VoidTotemModule}.
*/
public class HotbarTotemModule implements PurpurExtrasModule, Listener {
private final Collection<PotionEffect> totemEffects = new ArrayList<>();
protected HotbarTotemModule() {
totemEffects.add(new PotionEffect(PotionEffectType.REGENERATION, 20 * 45, 1));
totemEffects.add(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 40, 0));
totemEffects.add(new PotionEffect(PotionEffectType.ABSORPTION, 20 * 5, 1));
}
@Override
public void enable() {
PurpurExtrasOG plugin = PurpurExtrasOG.getInstance();
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public boolean shouldEnable() {
return PurpurExtrasOG.getPurpurConfig().getBoolean("settings.totem.work-from-hotbar", false);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onLethalDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
// Void deaths are handled by VoidTotemModule, which teleports the player to
// safety.
if (event.getCause().equals(EntityDamageEvent.DamageCause.VOID))
return;
// Damage is survivable, let it through unchanged.
if (player.getHealth() - event.getFinalDamage() > 0)
return;
PlayerInventory inventory = player.getInventory();
// Vanilla already resurrects when a totem is held in either hand.
if (inventory.getItemInMainHand().getType().equals(Material.TOTEM_OF_UNDYING)
|| inventory.getItemInOffHand().getType().equals(Material.TOTEM_OF_UNDYING))
return;
int slot = findHotbarTotemSlot(inventory);
if (slot < 0)
return;
event.setCancelled(true);
consumeTotem(inventory, slot);
applyTotemEffects(player);
}
private int findHotbarTotemSlot(PlayerInventory inventory) {
for (int slot = 0; slot <= 8; slot++) {
ItemStack item = inventory.getItem(slot);
if (item != null && item.getType().equals(Material.TOTEM_OF_UNDYING))
return slot;
}
return -1;
}
private void consumeTotem(PlayerInventory inventory, int slot) {
ItemStack totem = inventory.getItem(slot);
if (totem == null)
return;
int remaining = totem.getAmount() - 1;
if (remaining <= 0) {
inventory.setItem(slot, null);
} else {
totem.setAmount(remaining);
inventory.setItem(slot, totem);
}
}
private void applyTotemEffects(Player player) {
player.setFallDistance(0);
player.setHealth(1);
player.playEffect(EntityEffect.TOTEM_RESURRECT);
player.getActivePotionEffects().forEach(potionEffect -> player.removePotionEffect(potionEffect.getType()));
player.addPotionEffects(this.totemEffects);
}
}
@@ -30,11 +30,20 @@ public class ShieldSettingsModule implements PurpurExtrasModule, Listener {
public void enable() {
PurpurExtrasOG plugin = PurpurExtrasOG.getInstance();
if (shieldDamageReduction != DEFAULT_SHIELD_DAMAGE_REDUCTION) {
// OldCombatMechanics owns shield reduction when present,
// else both scale blocking and compound. Presence, not
// enabled state, since OCM may enable after this module.
boolean ocmOwnsShields = plugin.getServer().getPluginManager().getPlugin("OldCombatMechanics") != null;
if (shieldDamageReduction != DEFAULT_SHIELD_DAMAGE_REDUCTION && !ocmOwnsShields) {
plugin.getServer().getPluginManager()
.registerEvents(new ShieldDamageReductionListener(shieldDamageReduction), plugin);
} else if (shieldDamageReduction != DEFAULT_SHIELD_DAMAGE_REDUCTION) {
plugin.getLogger().info("Skipping shield damage reduction: OldCombatMechanics owns it.");
}
if (shieldCooldown != DEFAULT_SHIELD_COOLDOWN) {
@@ -5,6 +5,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
public class ShieldDamageReductionListener implements Listener {
@@ -24,14 +25,16 @@ public class ShieldDamageReductionListener implements Listener {
if (!humanEntity.isBlocking())
return;
double originalDamage = event.getDamage();
double finalDamage = event.getFinalDamage();
double reduction = originalDamage - finalDamage;
// Scale only the blocking modifier. Writing a post-armour value
// into BASE made armour and resistance apply a second time.
if (!event.isApplicable(EntityDamageEvent.DamageModifier.BLOCKING))
return;
double newReduction = shieldDamageReduction * reduction;
double newDamage = finalDamage + newReduction;
double blocking = event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING);
if (blocking == 0.0)
return;
event.setDamage(newDamage);
event.setDamage(EntityDamageEvent.DamageModifier.BLOCKING, blocking * shieldDamageReduction);
}
+1
View File
@@ -54,6 +54,7 @@ settings:
enabled: true
totem:
work-on-void-death: true
work-from-hotbar: true
blocks:
shift-right-click-for-invisible-item-frames: false
chorus-flowers-always-drop: true