Skip to content

Commit

Permalink
Soaking tick events (#62)
Browse files Browse the repository at this point in the history
* change tick context to allow soakables

* implement soaking tick events

* add javadoc

* add to testmod

* return -1 as fallback for get soaking change

* remove test controller and test config
  • Loading branch information
TheDeathlyCow authored Feb 18, 2025
1 parent 7b2a537 commit 53e3586
Show file tree
Hide file tree
Showing 13 changed files with 234 additions and 201 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package com.github.thedeathlycow.thermoo.api.environment.event;

import com.github.thedeathlycow.thermoo.api.environment.component.EnvironmentComponentTypes;
import com.github.thedeathlycow.thermoo.api.temperature.Soakable;
import com.github.thedeathlycow.thermoo.api.temperature.TemperatureAware;
import com.github.thedeathlycow.thermoo.api.temperature.event.TemperatureTickContext;
import com.github.thedeathlycow.thermoo.api.temperature.event.TickContext;
import net.minecraft.component.ComponentMap;
import net.minecraft.component.ComponentType;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

/**
* Context objects for environmental temperature ticking events on temperature awares
*
* @param <T> The temperature aware type
*/
@ApiStatus.NonExtendable
public interface EnvironmentTickContext<T extends TemperatureAware> extends TemperatureTickContext<T> {
public interface EnvironmentTickContext<T extends TemperatureAware & Soakable> extends TickContext<T> {
/**
* The current environment components at the world and position.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ default int getMaxWetTicks(Soakable soakable) {
*
* @param soakable The soakable to compute increase for
* @return Returns the soaking change for the player.
* @deprecated Replaced with {@link com.github.thedeathlycow.thermoo.api.temperature.event.LivingEntitySoakingTickEvents}
*/
@Deprecated(since = "4.4")
int getSoakChange(Soakable soakable);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.github.thedeathlycow.thermoo.api.temperature.event;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.entity.LivingEntity;

/**
* Events for ticking soaking changes on entities on the logical server. These events will apply to spectator entities,
* but will not apply to dead or removed entities.
* <p>
* These events are invoked in the following order:
* <ul><li>ALLOW_SOAKING_UPDATE</li>
* <li>GET_SOAKING_CHANGE</li>
* <li>ALLOW_SOAKING_CHANGE</li></ul>
*/
public final class LivingEntitySoakingTickEvents {
/**
* Checks if the soaking change update tick for a living entity should be allowed to proceed. Returning any non-default
* value will force the update to proceed right away. By default, the update will be allowed to proceed.
*/
public static final Event<AllowSoakingUpdate> ALLOW_SOAKING_UPDATE = EventFactory.createArrayBacked(
AllowSoakingUpdate.class,
listeners -> context -> {
for (AllowSoakingUpdate listener : listeners) {
TriState result = listener.allowUpdate(context);
if (result != TriState.DEFAULT) {
return result;
}
}
return TriState.DEFAULT;
}
);

/**
* Gets the soaking change increase that should be applied to a living entity this update by summing all values
* supplied by listeners.
* <p>
* If the listeners return a sum total of 0, then this event will return -1 (to dry out entities).
*/
public static final Event<GetSoakingChange> GET_SOAKING_CHANGE = EventFactory.createArrayBacked(
GetSoakingChange.class,
listeners -> context -> {
int total = 0;
for (GetSoakingChange listener : listeners) {
int change = listener.addChange(context);
total += change;
}
return total == 0 ? -1 : total;
}
);

/**
* Checks if the final soaking change update calculated by {@link #GET_SOAKING_CHANGE}
* should be allowed to be applied to a living entity this tick. Returning any non-default value will force the
* update to be applied right away. By default, the update will be allowed to be applied.
*/
public static final Event<AllowSoakingChange> ALLOW_SOAKING_CHANGE = EventFactory.createArrayBacked(
AllowSoakingChange.class,
listeners -> (context, soakingChange) -> {
for (AllowSoakingChange listener : listeners) {
TriState result = listener.allowChange(context, soakingChange);
if (result != TriState.DEFAULT) {
return result;
}
}
return TriState.DEFAULT;
}
);

@FunctionalInterface
public interface AllowSoakingUpdate {
/**
* Whether this listener should allow a soaking change update to begin.
*
* @param context Context of the living entity for the tick.
* @return Return true or false to make the update happen right away, or default to fall back to other listeners.
* The default behaviour will be to allow the update.
*/
TriState allowUpdate(TickContext<LivingEntity> context);
}

@FunctionalInterface
public interface GetSoakingChange {
/**
* Calculates the soaking tick change that this listener wants to add to a living entity this tick.
*
* @param context Context of the living entity for the tick.
* @return Return the soaking tick change that this listener wants to apply to the entity in
* the context. This value is added to the values supplied by the other listeners.
*/
int addChange(TickContext<LivingEntity> context);
}

@FunctionalInterface
public interface AllowSoakingChange {
/**
* Whether this listener should allow a soaking change update to apply.
*
* @param context Context of the living entity for the tick.
* @param soakingChange The actual change in soaking ticks calculated from the {@link GetSoakingChange} events. This value is non-zero.
* @return Return true or false to make the update apply right away, or default to fall back to other listeners.
* The default behaviour will be to allow the update.
*/
TriState allowChange(TickContext<LivingEntity> context, int soakingChange);
}

private LivingEntitySoakingTickEvents() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public final class LivingEntityTemperatureTickEvents {
);

/**
* Gets the active change update that should be applied to a living entity this tick.
* Gets the active change update that should be applied to a living entity this tick by summing all values supplied
* by listeners. May be positive or negative.
* <p>
* Active changes should be used for temperature changes from entity effects such as heat from being on fire, or
* cold from being submerged in powder snow.
Expand Down Expand Up @@ -153,18 +154,19 @@ public interface AllowTemperatureUpdate {
* @return Return true or false to make the update happen right away, or default to fall back to other listeners.
* The default behaviour will be to allow the update.
*/
TriState allowUpdate(TemperatureTickContext<LivingEntity> context);
TriState allowUpdate(TickContext<LivingEntity> context);
}

@FunctionalInterface
public interface GetTemperatureChange {
/**
* Calculates the temperature change that this listener wants to apply to a living entity this tick.
* Calculates the temperature change that this listener wants to add to a living entity this tick.
*
* @param context Context of the living entity for the tick.
* @return Return the temperature point change that this listener wants to apply to the entity in the context.
* This value is added to the values supplied by the other listeners.
*/
int addTemperature(TemperatureTickContext<LivingEntity> context);
int addTemperature(TickContext<LivingEntity> context);
}

@FunctionalInterface
Expand All @@ -177,7 +179,7 @@ public interface AllowTemperatureChange {
* @return Return true or false to make the update apply right away, or default to fall back to other listeners.
* The default behaviour will be to allow the update.
*/
TriState allowChange(TemperatureTickContext<LivingEntity> context, int temperatureChange);
TriState allowChange(TickContext<LivingEntity> context, int temperatureChange);
}

private LivingEntityTemperatureTickEvents() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.thedeathlycow.thermoo.api.temperature.event;

import com.github.thedeathlycow.thermoo.api.temperature.Soakable;
import com.github.thedeathlycow.thermoo.api.temperature.TemperatureAware;
import net.minecraft.entity.LivingEntity;
import net.minecraft.server.world.ServerWorld;
Expand All @@ -8,26 +9,26 @@
import org.jetbrains.annotations.NotNull;

/**
* Context objects for general temperature ticking events on temperature awares
* Context objects for general temperature ticking events on temperature awares and soakables
*
* @param <T> The temperature aware type
*/
@ApiStatus.NonExtendable
public interface TemperatureTickContext<T extends TemperatureAware> {
public interface TickContext<T extends TemperatureAware & Soakable> {
/**
* The temperature aware being ticked
* The temperature aware/soakable being ticked
*/
@NotNull
T affected();

/**
* The server world of the affected temperature aware
* The server world of the affected temperature aware/soakable
*/
@NotNull
ServerWorld world();

/**
* The block position of the affected temperature aware. This should be preferred over using methods such as
* The block position of the affected temperature aware/soakable. This should be preferred over using methods such as
* {@link LivingEntity#getBlockPos()} since it can correct for being slightly sunk into blocks like mud or soul sand
* by taking the block position that is slightly above their actual {@linkplain LivingEntity#getPos() position}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import com.github.thedeathlycow.thermoo.api.temperature.HeatingMode;
import com.github.thedeathlycow.thermoo.api.temperature.HeatingModes;
import com.github.thedeathlycow.thermoo.api.temperature.event.LivingEntitySoakingTickEvents;
import com.github.thedeathlycow.thermoo.api.temperature.event.LivingEntityTemperatureTickEvents;
import com.github.thedeathlycow.thermoo.api.temperature.event.TemperatureTickContext;
import com.github.thedeathlycow.thermoo.api.temperature.event.TickContext;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState;
Expand All @@ -22,16 +23,22 @@ public static void tick(LivingEntity entity) {
}

if (entity.getWorld() instanceof ServerWorld serverWorld) {
var context = new TemperatureTickContextImpl(entity, serverWorld, getTemperatureTickPos(entity));
tickChange(
var context = new TickContextImpl(entity, serverWorld, getTemperatureTickPos(entity));
tickSoakingChange(
context,
LivingEntitySoakingTickEvents.ALLOW_SOAKING_UPDATE,
LivingEntitySoakingTickEvents.GET_SOAKING_CHANGE,
LivingEntitySoakingTickEvents.ALLOW_SOAKING_CHANGE
);
tickTemperatureChange(
context,
HeatingModes.PASSIVE,
LivingEntityTemperatureTickEvents.ALLOW_PASSIVE_TEMPERATURE_UPDATE,
LivingEntityTemperatureTickEvents.GET_PASSIVE_TEMPERATURE_CHANGE,
LivingEntityTemperatureTickEvents.ALLOW_PASSIVE_TEMPERATURE_CHANGE
);

tickChange(
tickTemperatureChange(
context,
HeatingModes.ACTIVE,
LivingEntityTemperatureTickEvents.ALLOW_ACTIVE_TEMPERATURE_UPDATE,
Expand All @@ -48,23 +55,24 @@ public static void tick(LivingEntity entity) {
*/
public static BlockPos getTemperatureTickPos(LivingEntity entity) {
Vec3d pos = entity.getPos();
final float offset = 0.21f;
if (entity.supportingBlockPos.isPresent()) {
BlockPos blockPos = entity.supportingBlockPos.get();
BlockState blockState = entity.getWorld().getBlockState(blockPos);
return !blockState.isIn(BlockTags.FENCES) && !blockState.isIn(BlockTags.WALLS) && !(blockState.getBlock() instanceof FenceGateBlock)
? blockPos.withY(MathHelper.floor(pos.y + 0.21f))
? blockPos.withY(MathHelper.floor(pos.y + offset))
: blockPos;
} else {
return new BlockPos(
MathHelper.floor(pos.x),
MathHelper.floor(pos.y + 0.21f),
MathHelper.floor(pos.y + offset),
MathHelper.floor(pos.z)
);
}
}

private static void tickChange(
TemperatureTickContext<LivingEntity> context,
private static void tickTemperatureChange(
TickContext<LivingEntity> context,
HeatingMode heatingMode,
Event<LivingEntityTemperatureTickEvents.AllowTemperatureUpdate> allowUpdate,
Event<LivingEntityTemperatureTickEvents.GetTemperatureChange> getTempChange,
Expand All @@ -80,11 +88,28 @@ private static void tickChange(
}
}

private record TemperatureTickContextImpl(
private static void tickSoakingChange(
TickContext<LivingEntity> context,
Event<LivingEntitySoakingTickEvents.AllowSoakingUpdate> allowUpdate,
Event<LivingEntitySoakingTickEvents.GetSoakingChange> addSoakChange,
Event<LivingEntitySoakingTickEvents.AllowSoakingChange> allowChange
) {
if (allowUpdate.invoker().allowUpdate(context) == TriState.FALSE) {
return;
}

int soakingChange = addSoakChange.invoker().addChange(context);

if (soakingChange != 0 && allowChange.invoker().allowChange(context, soakingChange) != TriState.FALSE) {
context.affected().thermoo$addWetTicks(soakingChange);
}
}

private record TickContextImpl(
LivingEntity affected,
ServerWorld world,
BlockPos pos
) implements TemperatureTickContext<LivingEntity> {
) implements TickContext<LivingEntity> {

}

Expand Down
Loading

0 comments on commit 53e3586

Please # to comment.