Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/src/main/java/vice/sol_valheim/FoodHUD.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private static void renderFoodSlot(#if PRE_CURRENT_MC_1_19_2 PoseStack #elif POS
int startWidth = width - (size * offset) - offset + 1;
float ticksLeftPercent = Float.min(1.0F, (float) food.ticksLeft / foodConfig.getTime());
int barHeight = Integer.max(1, (int)((size + 2f) * ticksLeftPercent));
int barColor = ticksLeftPercent < SOLValheim.Config.common.eatAgainPercentage ?
int barColor = ticksLeftPercent < 0.2 ?
FastColor.ARGB32.color(180, 255, 10, 10) :
FastColor.ARGB32.color(96, 0, 0, 0);

Expand Down
34 changes: 19 additions & 15 deletions common/src/main/java/vice/sol_valheim/ModConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import me.shedaniel.autoconfig.annotation.ConfigEntry;
import me.shedaniel.autoconfig.serializer.PartitioningSerializer;
import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffect;
Expand All @@ -15,10 +16,7 @@
import net.minecraft.world.item.Items;
import net.minecraft.world.item.UseAnim;

import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.*;


@Config(name = SOLValheim.MOD_ID)
Expand All @@ -30,7 +28,7 @@ public static Common.FoodConfig getFoodConfig(Item item) {
if(item != Items.CAKE && !item.isEdible() && !isDrink)
return null;

var existing = SOLValheim.Config.common.foodConfigs.get(item.arch$registryName());
var existing = SOLValheim.Config.common.foodConfigs.get(item.arch$registryName().toString());
if (existing == null)
{
var registry = item.arch$registryName().toString();
Expand Down Expand Up @@ -68,13 +66,13 @@ else if (registry.contains("milk")) {
existing.healthRegenModifier = 1.5f;
}

// if (registry.equals("minecraft:beetroot_soup")) {
// var effectConfig = new Common.MobEffectConfig();
// effectConfig.ID = BuiltInRegistries.MOB_EFFECT.getKey(MobEffects.MOVEMENT_SPEED).toString();
// existing.extraEffects.add(effectConfig);
// }
if (registry.equals("minecraft:beetroot_soup")) {
var effectConfig = new Common.MobEffectConfig();
effectConfig.ID = BuiltInRegistries.MOB_EFFECT.getKey(MobEffects.MOVEMENT_SPEED).toString();
existing.extraEffects.add(effectConfig);
}

SOLValheim.Config.common.foodConfigs.put(item.arch$registryName(), existing);
SOLValheim.Config.common.foodConfigs.put(item.arch$registryName().toString(), existing);
}

return existing;
Expand Down Expand Up @@ -108,6 +106,9 @@ public static final class Common implements ConfigData {
@ConfigEntry.Gui.Tooltip() @Comment("Extra speed given when your hearts are full (0 to disable)")
public float speedBoost = 0.20f;

@ConfigEntry.Gui.Tooltip() @Comment("Maximum allowed hearts from food")
public int maxFoodHealth = 100;

@ConfigEntry.Gui.Tooltip() @Comment("Number of hearts to start with")
public int startingHealth = 3;

Expand All @@ -123,14 +124,17 @@ public static final class Common implements ConfigData {
@ConfigEntry.Gui.Tooltip() @Comment("Simulate food ticking down during night")
public boolean passTicksDuringNight = true;

@ConfigEntry.Gui.Tooltip() @Comment("Whether or not food tooltips should show extra effects")
public boolean displayEffects = true;

@ConfigEntry.Gui.Tooltip(count = 5) @Comment("""
Food nutrition and effect overrides (Auto Generated if Empty)
- nutrition: Affects Heart Gain & Health Regen
- saturationModifier: Affects Food Duration & Player Speed
- healthRegenModifier: Multiplies health regen speed
- extraEffects: Extra effects provided by eating the food. Format: { String ID, float duration, int amplifier }
""")
public Dictionary<ResourceLocation, FoodConfig> foodConfigs = new Hashtable<>();
public Map<String, FoodConfig> foodConfigs = new HashMap<>();

public static final class FoodConfig implements ConfigData {
public int nutrition;
Expand All @@ -139,16 +143,16 @@ public static final class FoodConfig implements ConfigData {
public List<MobEffectConfig> extraEffects = new ArrayList<>();

public int getTime() {
var time = (int) (SOLValheim.Config.common.defaultTimer * 20 * saturationModifier * nutrition);
int time = (int) (SOLValheim.Config.common.defaultTimer * 20 * saturationModifier * nutrition);
return Math.max(time, 6000);
}

public int getHearts() {
return Math.max(nutrition, 2);
}

public float getHealthRegen()
{
public float getHealthRegen() {
// 0.25f min, 2f max
return Mth.clamp(nutrition * 0.10f * healthRegenModifier, 0.25f, 2f);
}
}
Expand Down
64 changes: 33 additions & 31 deletions common/src/main/java/vice/sol_valheim/SOLValheim.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,39 +54,30 @@ public static void init() {
AutoConfig.register(ModConfig.class, PartitioningSerializer.wrap(JanksonConfigSerializer::new));
Config = AutoConfig.getConfigHolder(ModConfig.class).getConfig();

if (Config.common.foodConfigs.isEmpty())
{
System.out.println("Generating default food configs, this might take a second.");
long startTime = System.nanoTime();

#if PRE_CURRENT_MC_1_19_2
Registry.ITEM.forEach(ModConfig::getFoodConfig);
#elif POST_CURRENT_MC_1_20_1
BuiltInRegistries.ITEM.forEach(ModConfig::getFoodConfig);
#endif

boolean addedAny = false;

#if PRE_CURRENT_MC_1_19_2
for (Item item : Registry.ITEM) {
#elif POST_CURRENT_MC_1_20_1
for (Item item : BuiltInRegistries.ITEM) {
#endif
var key = item.arch$registryName();
var existing = Config.common.foodConfigs.get(key);
if (existing == null) {
ModConfig.getFoodConfig(item);
addedAny = true;
}
}

if (addedAny) {
//System.out.println("Generated config for missing items...");
AutoConfig.getConfigHolder(ModConfig.class).save();

long endTime = System.nanoTime();
long executionTime = (endTime - startTime) / 1000000;
System.out.println("Generating default food configs took " + executionTime + "ms.");
}

//
// try {
// var field = FoodProperties.class.getDeclaredField("canAlwaysEat");
// field.setBoolean(Items.ROTTEN_FLESH.getFoodProperties(), true);
// }
// catch (Exception e) {
// System.out.println(e);
// }
}



public static void addTooltip(ItemStack item, TooltipFlag flag, List<Component> list)
{
public static void addTooltip(ItemStack item, TooltipFlag flag, List<Component> list){
var food = item.getItem();
if (food == Items.ROTTEN_FLESH) {
list.add(Component.literal("☠ Empties Your Stomach!").withStyle(ChatFormatting.GREEN));
Expand All @@ -105,12 +96,23 @@ public static void addTooltip(ItemStack item, TooltipFlag flag, List<Component>

list.add(Component.literal("⌚ " + String.format("%.0f", minutes) + " Minute" + (minutes > 1 ? "s" : "")).withStyle(ChatFormatting.GOLD));

for (var effect : config.extraEffects) {
var eff = effect.getEffect();
if (eff == null)
continue;
if(!config.extraEffects.isEmpty() && Config.common.displayEffects) {
list.add(Component.literal(""));
for (var effect : config.extraEffects) {
var eff = effect.getEffect();
if (eff == null)
continue;

float effectDurationSeconds = config.getTime() * effect.duration / 20f;
int minutesPart = (int) (effectDurationSeconds / 60);
int secondsPart = (int) (effectDurationSeconds % 60);


list.add(Component.literal("★ " + eff.getDisplayName().getString() + (effect.amplifier > 1 ? " " + effect.amplifier : "")).withStyle(ChatFormatting.GREEN));
if (eff.isBeneficial())
list.add(Component.literal("★ " + eff.getDisplayName().getString() + (effect.amplifier > 1 ? " " + effect.amplifier : "") + String.format(" (%02d:%02d)", minutesPart, secondsPart)).withStyle(ChatFormatting.GREEN));
else
list.add(Component.literal("❌ " + eff.getDisplayName().getString() + (effect.amplifier > 1 ? " " + effect.amplifier : "") + String.format(" (%02d:%02d)", minutesPart, secondsPart)).withStyle(ChatFormatting.DARK_RED));
}
}

if (item.getUseAnimation() == UseAnim.DRINK) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public boolean canStartSprinting(boolean bool)
var solPlayer = (PlayerEntityMixinDataAccessor) this;
var mayFly = ((LocalPlayer) (Object) this).getAbilities().mayfly;

if (mayFly || ((LocalPlayer) (Object) this).tickCount < SOLValheim.Config.common.respawnGracePeriod * 20)
if (mayFly || SOLValheim.Config.common.respawnGracePeriod < 0 || ((LocalPlayer) (Object) this).tickCount < SOLValheim.Config.common.respawnGracePeriod * 20)
{
return true;
}
Expand All @@ -42,7 +42,7 @@ public void canStartSprinting(CallbackInfoReturnable<Boolean> cir)
var solPlayer = (PlayerEntityMixinDataAccessor) this;
var mayFly = ((LocalPlayer) (Object) this).getAbilities().mayfly;

if (mayFly || ((LocalPlayer) (Object) this).tickCount < SOLValheim.Config.common.respawnGracePeriod * 20)
if (mayFly || SOLValheim.Config.common.respawnGracePeriod < 0 || ((LocalPlayer) (Object) this).tickCount < SOLValheim.Config.common.respawnGracePeriod * 20)
{
cir.setReturnValue(true);
cir.cancel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import vice.sol_valheim.ModConfig;
import vice.sol_valheim.SOLValheim;
import vice.sol_valheim.accessors.FoodDataPlayerAccessor;
import vice.sol_valheim.accessors.PlayerEntityMixinDataAccessor;
Expand Down Expand Up @@ -96,9 +97,29 @@ private void onTick(CallbackInfo info) {
if (!sol_valheim$food_data.ItemEntries.isEmpty()) {
sol_valheim$food_data.tick();
sol_valheim$trackData();

var player = (Player) (Object) this;
if (player.tickCount % 20 == 0) {
// Handle main eaten items
for (var eaten : sol_valheim$food_data.ItemEntries) {
var config = ModConfig.getFoodConfig(eaten.item);
if (config != null) {
sol_valheim$applyFoodEffectsToPlayer(player, eaten, config);
}
}
// Handle drink slot
if (sol_valheim$food_data.DrinkSlot != null) {
var config = ModConfig.getFoodConfig(sol_valheim$food_data.DrinkSlot.item);
if (config != null) {
sol_valheim$applyFoodEffectsToPlayer(player, sol_valheim$food_data.DrinkSlot, config);
}
}
}
}

float maxhp = Math.min(40, (SOLValheim.Config.common.startingHealth * 2) + sol_valheim$food_data.getTotalFoodNutrition());
float maxhp = Math.min(SOLValheim.Config.common.maxFoodHealth * 2, (SOLValheim.Config.common.startingHealth * 2) + sol_valheim$food_data.getTotalFoodNutrition());
// hack: round to full hearts
maxhp = Math.round(maxhp / 2) * 2;

Player player = (Player) (LivingEntity) this;
player.getFoodData().setSaturation(0);
Expand All @@ -123,6 +144,40 @@ else if (maxhp < 20 && speedBuff != null)
}
}

@Unique
private void sol_valheim$applyFoodEffectsToPlayer(Player player,
ValheimFoodData.EatenFoodItem eatenData,
ModConfig.Common.FoodConfig config)
{
int ticksLeft = eatenData.ticksLeft;

for (var effectCfg : config.extraEffects) {
var mobEffect = effectCfg.getEffect();
if (mobEffect == null)
continue;

int amplifier = Math.max(effectCfg.amplifier - 1, 0);
float fractionActive = effectCfg.duration;
int totalTime = config.getTime();
int threshold = (int) (totalTime * fractionActive);

if (ticksLeft >= (totalTime - threshold)) {
player.addEffect(
new net.minecraft.world.effect.MobEffectInstance(
mobEffect,
120, // just so it doesn't flash
amplifier,
false,
false
)
);
}
else {
player.removeEffect(mobEffect);
}
}
}

@Inject(at = {@At("HEAD")}, method = {"canEat(Z)Z"}, cancellable = true)
private void onCanConsume(boolean ignorehunger, CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(true);
Expand Down