From dd7a48981aa11708debca0096ecea2f173625938 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 15 Sep 2024 15:05:10 +0200 Subject: [PATCH] Fix time based happiness working as expected (#10196) Expiration based happiness got rid of it's inverted logic Time based happiness can now be used as an inverted day counter for this purpose Sleep and food modifiers converted into time based to mitigate issues these modifiers were having Migration should happen automatically, because currently stored NBT data used to be for expiration, but holds the same day values that time based has. So the old NBT data will be loaded on top of the new time based instance. --- .../ICitizenHappinessHandler.java | 6 +- .../happiness/AbstractHappinessModifier.java | 4 +- .../ExpirationBasedHappinessModifier.java | 48 ++---------- .../citizen/happiness/HappinessRegistry.java | 5 +- .../citizen/happiness/IHappinessModifier.java | 6 +- .../happiness/StaticHappinessModifier.java | 4 +- .../happiness/TimeBasedHappinessModifier.java | 78 +++++++++++++------ .../api/util/constant/NbtTagConstants.java | 1 - .../ModHappinessFactorTypeInitializer.java | 4 +- .../minecolonies/core/colony/CitizenData.java | 6 +- .../core/colony/CitizenDataView.java | 2 +- .../CitizenHappinessHandler.java | 14 ++-- 12 files changed, 92 insertions(+), 86 deletions(-) diff --git a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenHappinessHandler.java b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenHappinessHandler.java index 34d9cdf774c..3d206175834 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenHappinessHandler.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenHappinessHandler.java @@ -53,15 +53,17 @@ public interface ICitizenHappinessHandler * Read the handler from NBT. * * @param compound the compound to read it from. + * @param persist whether we're reading from persisted data or from networking. */ - void read(CompoundTag compound); + void read(CompoundTag compound, final boolean persist); /** * Write the handler to NBT. * * @param compound the compound to write it to. + * @param persist whether we're reading from persisted data or from networking. */ - void write(CompoundTag compound); + void write(CompoundTag compound, final boolean persist); /** * Get a list of all modifiers. diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/AbstractHappinessModifier.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/AbstractHappinessModifier.java index 878322e7cfb..c45c2774ead 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/AbstractHappinessModifier.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/AbstractHappinessModifier.java @@ -60,7 +60,7 @@ public String getId() } @Override - public void read(final CompoundTag compoundNBT) + public void read(final CompoundTag compoundNBT, final boolean persist) { this.id = compoundNBT.getString(TAG_ID); this.weight = compoundNBT.getDouble(TAG_WEIGHT); @@ -77,7 +77,7 @@ public void read(final CompoundTag compoundNBT) } @Override - public void write(final CompoundTag compoundNBT) + public void write(final CompoundTag compoundNBT, final boolean persist) { compoundNBT.putString(TAG_ID, this.id); compoundNBT.putDouble(TAG_WEIGHT, this.weight); diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/ExpirationBasedHappinessModifier.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/ExpirationBasedHappinessModifier.java index d9022eb5e56..57f8d95a640 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/ExpirationBasedHappinessModifier.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/ExpirationBasedHappinessModifier.java @@ -15,18 +15,13 @@ public final class ExpirationBasedHappinessModifier extends AbstractHappinessMod /** * The number of passed days. */ - private int days = 0; + private int days; /** * Period of time this modifier applies. */ private int period; - /** - * If this should give a penalty if not active. - */ - private boolean inverted; - /** * Create an instance of the happiness modifier. * @@ -38,24 +33,10 @@ public final class ExpirationBasedHappinessModifier extends AbstractHappinessMod public ExpirationBasedHappinessModifier(final String id, final double weight, final IHappinessSupplierWrapper supplier, final int period) { super(id, weight, supplier); + this.days = period; this.period = period; } - /** - * Create an instance of the happiness modifier. - * - * @param id its string id. - * @param weight its weight. - * @param period the period. - * @param supplier the supplier to get the factor. - * @param inverted if inverted. - */ - public ExpirationBasedHappinessModifier(final String id, final double weight, final IHappinessSupplierWrapper supplier, final int period, final boolean inverted) - { - this(id, weight, supplier, period); - this.inverted = inverted; - } - /** * Create an instance of the happiness modifier. */ @@ -67,22 +48,11 @@ public ExpirationBasedHappinessModifier() @Override public double getFactor(final ICitizenData data) { - if (inverted) + if (days > 0 && days <= period) { - if (days <= period) - { - return 1.0; - } return super.getFactor(data); } - else - { - if (days < period) - { - return super.getFactor(data); - } - return 1.0; - } + return 1.0; } @Override @@ -107,21 +77,19 @@ public int getDays() } @Override - public void read(final CompoundTag compoundNBT) + public void read(final CompoundTag compoundNBT, final boolean persist) { - super.read(compoundNBT); + super.read(compoundNBT, persist); this.days = compoundNBT.getInt(TAG_DAY); - this.inverted = compoundNBT.getBoolean(TAG_INVERTED); this.period = compoundNBT.getInt(TAG_PERIOD); } @Override - public void write(final CompoundTag compoundNBT) + public void write(final CompoundTag compoundNBT, final boolean persist) { - super.write(compoundNBT); + super.write(compoundNBT, persist); compoundNBT.putString(NbtTagConstants.TAG_MODIFIER_TYPE, HappinessRegistry.EXPIRATION_MODIFIER.toString()); compoundNBT.putInt(TAG_DAY, days); - compoundNBT.putBoolean(TAG_INVERTED, inverted); compoundNBT.putInt(TAG_PERIOD, period); } } diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/HappinessRegistry.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/HappinessRegistry.java index 0c35c894dd1..55c63827b7f 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/HappinessRegistry.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/HappinessRegistry.java @@ -66,9 +66,10 @@ public IHappinessModifier create() * Static getter to load a happiness modifier from a compound. * * @param compound the compound to load it from. + * @param persist whether we're reading from persisted data or from networking. * @return the modifier instance. */ - public static IHappinessModifier loadFrom(@NotNull final CompoundTag compound) + public static IHappinessModifier loadFrom(@NotNull final CompoundTag compound, final boolean persist) { final ResourceLocation modifierType = compound.contains(NbtTagConstants.TAG_MODIFIER_TYPE) ? new ResourceLocation(compound.getString(NbtTagConstants.TAG_MODIFIER_TYPE)) @@ -79,7 +80,7 @@ public static IHappinessModifier loadFrom(@NotNull final CompoundTag compound) { try { - modifier.read(compound); + modifier.read(compound, persist); } catch (final RuntimeException ex) { diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/IHappinessModifier.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/IHappinessModifier.java index f95de4efad6..394d870aad2 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/IHappinessModifier.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/IHappinessModifier.java @@ -34,13 +34,15 @@ public interface IHappinessModifier * Read the modifier from nbt. * * @param compoundNBT the compound to read it from. + * @param persist whether we're reading from persisted data or from networking. */ - void read(final CompoundTag compoundNBT); + void read(final CompoundTag compoundNBT, final boolean persist); /** * Write it to NBT. * * @param compoundNBT the compound to write it to. + * @param persist whether we're reading from persisted data or from networking. */ - void write(final CompoundTag compoundNBT); + void write(final CompoundTag compoundNBT, final boolean persist); } diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/StaticHappinessModifier.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/StaticHappinessModifier.java index 4552505f7c3..9ebda2ebeeb 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/StaticHappinessModifier.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/StaticHappinessModifier.java @@ -29,9 +29,9 @@ public StaticHappinessModifier() } @Override - public void write(final CompoundTag compoundNBT) + public void write(final CompoundTag compoundNBT, final boolean persist) { - super.write(compoundNBT); + super.write(compoundNBT, persist); compoundNBT.putString(NbtTagConstants.TAG_MODIFIER_TYPE, HappinessRegistry.STATIC_MODIFIER.toString()); } } diff --git a/src/main/java/com/minecolonies/api/entity/citizen/happiness/TimeBasedHappinessModifier.java b/src/main/java/com/minecolonies/api/entity/citizen/happiness/TimeBasedHappinessModifier.java index 04c9863f3fa..2ba71632cff 100755 --- a/src/main/java/com/minecolonies/api/entity/citizen/happiness/TimeBasedHappinessModifier.java +++ b/src/main/java/com/minecolonies/api/entity/citizen/happiness/TimeBasedHappinessModifier.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.BiPredicate; import static com.minecolonies.api.util.constant.NbtTagConstants.*; @@ -17,10 +18,15 @@ */ public final class TimeBasedHappinessModifier extends AbstractHappinessModifier implements ITimeBasedHappinessModifier { + /** + * A predicate to check whether the current day should roll over or reset. + */ + private BiPredicate dayRollOverPredicate; + /** * The time based factors. */ - private Tuple[] timeBasedFactor; + private List> timeBasedFactor = new ArrayList<>(); /** * The number of passed days. @@ -35,10 +41,32 @@ public final class TimeBasedHappinessModifier extends AbstractHappinessModifier * @param supplier the supplier to get the factor. * @param timeBasedFactor tuples about the boost/buff factor over time. */ - public TimeBasedHappinessModifier(final String id, final double weight, final IHappinessSupplierWrapper supplier, final Tuple...timeBasedFactor) + @SafeVarargs + public TimeBasedHappinessModifier(final String id, final double weight, final IHappinessSupplierWrapper supplier, final Tuple... timeBasedFactor) + { + this(id, weight, supplier, (modifier, data) -> modifier.getFactor(data) < 1, timeBasedFactor); + } + + /** + * Create an instance of the happiness modifier. + * + * @param id its string id. + * @param weight its weight. + * @param supplier the supplier to get the factor. + * @param timeBasedFactor tuples about the boost/buff factor over time. + * @param dayRollOverPredicate a predicate to check whether the current day should roll over or reset. + */ + @SafeVarargs + public TimeBasedHappinessModifier( + final String id, + final double weight, + final IHappinessSupplierWrapper supplier, + final BiPredicate dayRollOverPredicate, + final Tuple... timeBasedFactor) { super(id, weight, supplier); - this.timeBasedFactor = timeBasedFactor; + this.dayRollOverPredicate = dayRollOverPredicate; + this.timeBasedFactor = List.of(timeBasedFactor); } /** @@ -59,7 +87,7 @@ public double getFactor(final ICitizenData citizenData) { for (final Tuple tuple : timeBasedFactor) { - if (this.days > tuple.getA()) + if (this.days >= tuple.getA()) { factor = baseFactor * tuple.getB(); } @@ -83,7 +111,7 @@ public int getDays() @Override public void dayEnd(final ICitizenData data) { - if (getFactor(data) < 1) + if (dayRollOverPredicate.test(this, data)) { days++; } @@ -94,34 +122,40 @@ public void dayEnd(final ICitizenData data) } @Override - public void read(final CompoundTag compoundNBT) + public void read(final CompoundTag compoundNBT, final boolean persist) { - super.read(compoundNBT); + super.read(compoundNBT, persist); this.days = compoundNBT.getInt(TAG_DAY); - final ListTag listTag = compoundNBT.getList(TAG_LIST, Constants.TAG_COMPOUND); - final List> list = new ArrayList<>(); - for (int i = 0; i < listTag.size(); i++) + if (!persist) { - final CompoundTag entryTag = listTag.getCompound(i); - list.add(new Tuple<>(entryTag.getInt(TAG_DAY), entryTag.getDouble(TAG_VALUE))); + final ListTag listTag = compoundNBT.getList(TAG_LIST, Constants.TAG_COMPOUND); + final List> list = new ArrayList<>(); + for (int i = 0; i < listTag.size(); i++) + { + final CompoundTag entryTag = listTag.getCompound(i); + list.add(new Tuple<>(entryTag.getInt(TAG_DAY), entryTag.getDouble(TAG_VALUE))); + } + this.timeBasedFactor = list; } - this.timeBasedFactor = list.toArray(new Tuple[0]); } @Override - public void write(final CompoundTag compoundNBT) + public void write(final CompoundTag compoundNBT, final boolean persist) { - super.write(compoundNBT); + super.write(compoundNBT, persist); compoundNBT.putString(NbtTagConstants.TAG_MODIFIER_TYPE, HappinessRegistry.TIME_PERIOD_MODIFIER.toString()); - compoundNBT.putInt(TAG_DAY, days); - final ListTag listTag = new ListTag(); - for (final Tuple entry : timeBasedFactor) + if (!persist) { - final CompoundTag listEntry = new CompoundTag(); - listEntry.putInt(TAG_DAY, entry.getA()); - listEntry.putDouble(TAG_VALUE, entry.getB()); + final ListTag listTag = new ListTag(); + for (final Tuple entry : timeBasedFactor) + { + final CompoundTag listEntry = new CompoundTag(); + listEntry.putInt(TAG_DAY, entry.getA()); + listEntry.putDouble(TAG_VALUE, entry.getB()); + listTag.add(listEntry); + } + compoundNBT.put(TAG_LIST, listTag); } - compoundNBT.put(TAG_LIST, listTag); } } diff --git a/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java b/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java index 68ff7c5fac4..2f2ca0d4bdf 100755 --- a/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java @@ -60,7 +60,6 @@ public final class NbtTagConstants public static final String TAG_OFFHAND_HELD_ITEM_SLOT = "OffhandHeldItemSlot"; public static final String TAG_STATUS = "status"; public static final String TAG_DAY = "day"; - public static final String TAG_INVERTED = "inverted"; public static final String TAG_PERIOD = "period"; public static final String TAG_IS_BUILT = "isBuilt"; public static final String TAG_CUSTOM_NAME = "customName"; diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModHappinessFactorTypeInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModHappinessFactorTypeInitializer.java index 8c8c315ac1b..066b7e621ac 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModHappinessFactorTypeInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModHappinessFactorTypeInitializer.java @@ -45,7 +45,7 @@ private ModHappinessFactorTypeInitializer() HappinessRegistry.healthFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(HEALTH_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> data.getEntity().isPresent() ? (data.getEntity().get().getCitizenDiseaseHandler().isSick() ? 0.5 : 1.0) : 1.0)); HappinessRegistry.idleatjobFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(IDLEATJOB_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> data.isIdleAtJob() ? 0.5 : 1.0)); - HappinessRegistry.sleptTonightFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(SLEPTTONIGHT_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> data.getJob() instanceof AbstractJobGuard ? 1 : 0.0)); - HappinessRegistry.foodFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(FOOD_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> (data.getHomeBuilding() == null || data.getHomeBuilding().getBuildingLevel() <= 2) ? 0.0 : data.getHomeBuilding().getBuildingLevel() - 2.0)); + HappinessRegistry.sleptTonightFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(SLEPTTONIGHT_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> data.getJob() instanceof AbstractJobGuard ? 1 : 0.5)); + HappinessRegistry.foodFunction = DEFERRED_REGISTER_HAPPINESS_FUNCTION.register(FOOD_FUNCTION.getPath(), () -> new HappinessFunctionEntry(data -> (data.getHomeBuilding() == null || data.getHomeBuilding().getBuildingLevel() <= 2) ? 1 : 0.5)); } } diff --git a/src/main/java/com/minecolonies/core/colony/CitizenData.java b/src/main/java/com/minecolonies/core/colony/CitizenData.java index 0fd4a1300a0..605452da0d1 100755 --- a/src/main/java/com/minecolonies/core/colony/CitizenData.java +++ b/src/main/java/com/minecolonies/core/colony/CitizenData.java @@ -997,7 +997,7 @@ public void serializeViewNetworkData(@NotNull final FriendlyByteBuf buf) } final CompoundTag happinessCompound = new CompoundTag(); - citizenHappinessHandler.write(happinessCompound); + citizenHappinessHandler.write(happinessCompound, false); buf.writeNbt(happinessCompound); buf.writeInt(status != null ? status.getId() : -1); @@ -1234,7 +1234,7 @@ public CompoundTag serializeNBT() nbtTagCompound.put("job", jobCompound); } - citizenHappinessHandler.write(nbtTagCompound); + citizenHappinessHandler.write(nbtTagCompound, true); citizenMournHandler.write(nbtTagCompound); inventory.write(nbtTagCompound); @@ -1399,7 +1399,7 @@ public void deserializeNBT(final CompoundTag nbtTagCompound) } } - this.citizenHappinessHandler.read(nbtTagCompound); + this.citizenHappinessHandler.read(nbtTagCompound, true); this.citizenMournHandler.read(nbtTagCompound); if (nbtTagCompound.contains(TAG_LEVEL_MAP) && !nbtTagCompound.contains(TAG_NEW_SKILLS)) diff --git a/src/main/java/com/minecolonies/core/colony/CitizenDataView.java b/src/main/java/com/minecolonies/core/colony/CitizenDataView.java index 03f15716e4b..c064ac18d32 100644 --- a/src/main/java/com/minecolonies/core/colony/CitizenDataView.java +++ b/src/main/java/com/minecolonies/core/colony/CitizenDataView.java @@ -362,7 +362,7 @@ public void deserialize(@NotNull final FriendlyByteBuf buf) sortedInteractions = new ArrayList<>(citizenChatOptions.values()); sortedInteractions.sort(Comparator.comparingInt(e -> -e.getPriority().getPriority())); - citizenHappinessHandler.read(buf.readNbt()); + citizenHappinessHandler.read(buf.readNbt(), false); int statusindex = buf.readInt(); statusIcon = statusindex >= 0 ? VisibleCitizenStatus.getForId(statusindex) : null; diff --git a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenHappinessHandler.java b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenHappinessHandler.java index 6556b267f1e..8d07d6efbdf 100755 --- a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenHappinessHandler.java +++ b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenHappinessHandler.java @@ -75,8 +75,8 @@ public CitizenHappinessHandler(final ICitizenData data) new DynamicHappinessSupplier(IDLEATJOB_FUNCTION), new Tuple<>(IDLE_AT_JOB_COMPLAINS_DAYS, 0.5), new Tuple<>(IDLE_AT_JOB_DEMANDS_DAYS, 0.1))); - addModifier(new ExpirationBasedHappinessModifier(SLEPTTONIGHT, 1.5, new DynamicHappinessSupplier(SLEPTTONIGHT_FUNCTION), 3, true)); - addModifier(new ExpirationBasedHappinessModifier(HADDECENTFOOD, 3.0, new DynamicHappinessSupplier(FOOD_FUNCTION), 7, true)); + addModifier(new TimeBasedHappinessModifier(SLEPTTONIGHT, 1.5, new DynamicHappinessSupplier(SLEPTTONIGHT_FUNCTION), (modifier, d) -> true, new Tuple<>(0, 2d), new Tuple<>(2, 1.6d), new Tuple<>(3, 1d))); + addModifier(new TimeBasedHappinessModifier(HADDECENTFOOD, 3.0, new DynamicHappinessSupplier(FOOD_FUNCTION), (modifier, d) -> true, new Tuple<>(0, 2d), new Tuple<>(4, 1.6d), new Tuple<>(7, 1d))); } /** @@ -156,7 +156,7 @@ public double getHappiness(final IColony colony, final ICitizenData citizenData) } @Override - public void read(final CompoundTag compound) + public void read(final CompoundTag compound, final boolean persist) { // Only deserialize for new version. Old can keep the above defaults just fine. if (compound.contains(TAG_NEW_HAPPINESS)) @@ -168,11 +168,11 @@ public void read(final CompoundTag compound) final String id = compoundTag.getString(TAG_ID); if (happinessFactors.containsKey(id)) { - happinessFactors.get(id).read(compoundTag); + happinessFactors.get(id).read(compoundTag, persist); } else if (VALID_HAPPINESS_MODIFIERS.contains(id)) { - final IHappinessModifier modifier = HappinessRegistry.loadFrom(compoundTag); + final IHappinessModifier modifier = HappinessRegistry.loadFrom(compoundTag, persist); if (modifier != null) { happinessFactors.put(modifier.getId(), modifier); @@ -183,13 +183,13 @@ else if (VALID_HAPPINESS_MODIFIERS.contains(id)) } @Override - public void write(final CompoundTag compound) + public void write(final CompoundTag compound, final boolean persist) { final ListTag listTag = new ListTag(); for (final IHappinessModifier happinessModifier : happinessFactors.values()) { final CompoundTag compoundNbt = new CompoundTag(); - happinessModifier.write(compoundNbt); + happinessModifier.write(compoundNbt, persist); listTag.add(compoundNbt); }