feat: Added new config: "recentSpellExpiryTime": The time in ticks after which recent spell casts expire and no longer count toward progression penalties. Default is 1200 ticks (1 minute). Lower values make progression penalties shorter-lived, higher values make them last longer.

This commit is contained in:
WinDanesz
2025-08-31 13:58:52 +02:00
parent 1c1e6d1e00
commit 7f9edcfaef
2 changed files with 41 additions and 6 deletions
@@ -215,6 +215,8 @@ public final class Settings {
public boolean wandsMustBeHeldToDecrementCooldown = false; public boolean wandsMustBeHeldToDecrementCooldown = false;
/** <b>[Server-only]</b> Whether to enable Wizardry mob loot injection. Allows an easier switch instead of blacklisting all entities. */ /** <b>[Server-only]</b> Whether to enable Wizardry mob loot injection. Allows an easier switch instead of blacklisting all entities. */
public boolean injectMobDrops = true; public boolean injectMobDrops = true;
/** <b>[Server-only]</b> The time in ticks after which recent spell casts expire and no longer count toward progression penalties. */
public int recentSpellExpiryTime = 1200;
/** /**
* <b>[Server-only]</b> List of registry names of entities which summoned creatures are allowed to attack, in addition * <b>[Server-only]</b> List of registry names of entities which summoned creatures are allowed to attack, in addition
* to the defaults. * to the defaults.
@@ -866,6 +868,14 @@ public final class Settings {
injectMobDrops = property.getBoolean(); injectMobDrops = property.getBoolean();
propOrder.add(property.getName()); propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "recentSpellExpiryTime", 1200,
"The time in ticks after which recent spell casts expire and no longer count toward progression penalties. Default is 1200 ticks (1 minute). Lower values make progression penalties shorter-lived, higher values make them last longer.",
60, 72000); // Between 3 seconds and 1 hour
property.setLanguageKey("config." + Wizardry.MODID + ".recent_spell_expiry_time");
Wizardry.proxy.setToNumberSliderEntry(property);
recentSpellExpiryTime = property.getInt();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them."); property = config.get(TWEAKS_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist"); property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist");
property.setRequiresMcRestart(true); property.setRequiresMcRestart(true);
@@ -47,6 +47,7 @@ import javax.annotation.Nullable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.AbstractMap.SimpleEntry;
/** /**
* Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave minimum * Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave minimum
@@ -126,7 +127,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
* be modified via {@link WizardData#setVariable(IVariable, Object)}, which (as a method) is able to enforce it. */ * be modified via {@link WizardData#setVariable(IVariable, Object)}, which (as a method) is able to enforce it. */
private final Map<IVariable, Object> spellData; private final Map<IVariable, Object> spellData;
private Queue<Spell> recentSpells; private Queue<SimpleEntry<Spell, Long>> recentSpells;
// This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry. // This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry.
// Leaving this for now because merging it into the spell data system will be more tricky // Leaving this for now because merging it into the spell data system will be more tricky
@@ -294,15 +295,19 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
* @param spell The spell to be tracked. * @param spell The spell to be tracked.
*/ */
public void trackRecentSpell(Spell spell){ public void trackRecentSpell(Spell spell){
this.recentSpells.add(spell); this.recentSpells.add(new SimpleEntry<>(spell, player.world.getTotalWorldTime()));
} }
/** /**
* Returns the number of times the given spell is tracked in this player's recently-cast spells. * Returns the number of times the given spell is tracked in this player's recently-cast spells.
* Only counts spells cast within the configured expiry time.
* @param spell The spell to count casts for. * @param spell The spell to count casts for.
*/ */
public int countRecentCasts(Spell spell){ public int countRecentCasts(Spell spell){
return (int)this.recentSpells.stream().filter(s -> s == spell).count(); // We know this can't be more than 5 long currentTime = player.world.getTotalWorldTime();
return (int)this.recentSpells.stream()
.filter(entry -> entry.getKey() == spell && (currentTime - entry.getValue()) < Wizardry.settings.recentSpellExpiryTime)
.count(); // We know this can't be more than 5
} }
// Imbuements // Imbuements
@@ -513,6 +518,12 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
if(player.ticksExisted % IMBUEMENT_UPDATE_INTERVAL == 0) updateImbuedItems(); if(player.ticksExisted % IMBUEMENT_UPDATE_INTERVAL == 0) updateImbuedItems();
updateContinuousSpellCasting(); updateContinuousSpellCasting();
// Clean up expired recent spells every 60 ticks (1 second)
if(player.ticksExisted % 60 == 0) {
long currentTime = player.world.getTotalWorldTime();
this.recentSpells.removeIf(entry -> (currentTime - entry.getValue()) >= Wizardry.settings.recentSpellExpiryTime);
}
this.spellData.forEach((k, v) -> this.spellData.put(k, k.update(player, v))); this.spellData.forEach((k, v) -> this.spellData.put(k, k.update(player, v)));
this.spellData.keySet().removeIf(k -> k.canPurge(player, this.spellData.get(k))); this.spellData.keySet().removeIf(k -> k.canPurge(player, this.spellData.get(k)));
} }
@@ -579,7 +590,15 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
properties.setInteger("maxTierReached", maxTierReached.ordinal()); properties.setInteger("maxTierReached", maxTierReached.ordinal());
NBTExtras.storeTagSafely(properties, "recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata()))); // Serialize recent spells with timestamps as compound tags
NBTTagList recentSpellsList = new NBTTagList();
for(SimpleEntry<Spell, Long> entry : recentSpells) {
NBTTagCompound spellTag = new NBTTagCompound();
spellTag.setInteger("spellId", entry.getKey().metadata());
spellTag.setLong("timestamp", entry.getValue());
recentSpellsList.appendTag(spellTag);
}
NBTExtras.storeTagSafely(properties, "recentSpells", recentSpellsList);
storedVariables.forEach(k -> k.write(properties, this.spellData.get(k))); storedVariables.forEach(k -> k.write(properties, this.spellData.get(k)));
@@ -606,8 +625,14 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
// Probably won't be null but we may as well just reinitialise it instead of clearing it // Probably won't be null but we may as well just reinitialise it instead of clearing it
this.recentSpells = EvictingQueue.create(MAX_RECENT_SPELLS); this.recentSpells = EvictingQueue.create(MAX_RECENT_SPELLS);
this.recentSpells.addAll(NBTExtras.NBTToList(nbt.getTagList("recentSpells", NBT.TAG_INT), // Deserialize recent spells with timestamps
(NBTTagInt tag) -> Spell.byMetadata(tag.getInt()))); NBTTagList recentSpellsList = nbt.getTagList("recentSpells", NBT.TAG_COMPOUND);
for(int i = 0; i < recentSpellsList.tagCount(); i++) {
NBTTagCompound spellTag = recentSpellsList.getCompoundTagAt(i);
Spell spell = Spell.byMetadata(spellTag.getInteger("spellId"));
long timestamp = spellTag.getLong("timestamp");
this.recentSpells.add(new SimpleEntry<>(spell, timestamp));
}
try{ try{
storedVariables.forEach(k -> this.spellData.put(k, k.read(nbt))); storedVariables.forEach(k -> this.spellData.put(k, k.read(nbt)));