(1.6.0) Release Commit

This commit is contained in:
mikx
2026-02-15 03:06:48 -05:00
parent d706e173b8
commit aaf9bf22dd
11 changed files with 440 additions and 608 deletions

View File

@@ -1,129 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Annotations;
using System.Windows.Controls;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;
namespace MxValheim.EventSystem
{
internal class EventHud
{
private static GameObject _eventRoot;
private static Text _eventTitleText;
private static Text _eventProgressText;
private static Image _progressBar;
private static CanvasGroup _canvasGroup;
public static void CreateEventHud()
{
// 1. Setup Root (Stacked under your KillFeed which is at -30)
_eventRoot = new GameObject("MxEventHUD_Root");
UnityEngine.Object.DontDestroyOnLoad(_eventRoot);
UnityEngine.Canvas c = _eventRoot.AddComponent<UnityEngine.Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
c.sortingOrder = 9999; // Just behind killfeed
CanvasScaler scaler = _eventRoot.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
_canvasGroup = _eventRoot.AddComponent<CanvasGroup>();
_canvasGroup.alpha = 0f;
// 2. Main Panel (Positioned at -80 to sit below the KillFeed)
GameObject panel = new GameObject("Background", typeof(RectTransform), typeof(Image));
panel.transform.SetParent(_eventRoot.transform, false);
Image panelImage = panel.GetComponent<Image>();
panelImage.color = new UnityEngine.Color(0, 0, 0, 0.7f);
// Find Valheim Sprite
foreach (Sprite s in Resources.FindObjectsOfTypeAll<Sprite>())
{
if (s.name == "item_background")
{
panelImage.sprite = s;
panelImage.type = Image.Type.Sliced;
break;
}
}
RectTransform pRect = panel.GetComponent<RectTransform>();
pRect.anchorMin = pRect.anchorMax = pRect.pivot = new Vector2(0.5f, 1f);
pRect.anchoredPosition = new Vector2(0, -80); // Lowered to avoid overlap
pRect.sizeDelta = new Vector2(450, 60); // Slightly taller for progress bar
// 3. Vertical Layout for Title + Progress Bar
VerticalLayoutGroup layout = panel.AddComponent<VerticalLayoutGroup>();
layout.childAlignment = UnityEngine.TextAnchor.MiddleCenter;
layout.spacing = 5f;
layout.padding = new RectOffset(10, 10, 5, 5);
// 4. Title Text
_eventTitleText = CreateText(panel.transform, "EVENT ACTIVE", 14, UnityEngine.Color.yellow);
// 5. Progress Bar Background
GameObject barBg = new GameObject("BarBG", typeof(RectTransform), typeof(Image));
barBg.transform.SetParent(panel.transform, false);
barBg.GetComponent<Image>().color = new UnityEngine.Color(0.2f, 0.2f, 0.2f, 0.8f);
barBg.GetComponent<RectTransform>().sizeDelta = new Vector2(400, 10);
// 6. Actual Progress Fill
GameObject fillObj = new GameObject("Fill", typeof(RectTransform), typeof(Image));
fillObj.transform.SetParent(barBg.transform, false);
_progressBar = fillObj.GetComponent<Image>();
_progressBar.color = UnityEngine.Color.red;
_progressBar.type = Image.Type.Filled;
_progressBar.fillMethod = Image.FillMethod.Horizontal;
RectTransform fRect = _progressBar.GetComponent<RectTransform>();
fRect.anchorMin = Vector2.zero;
fRect.anchorMax = Vector2.one;
fRect.sizeDelta = Vector2.zero;
// 7. Counter Text (e.g., 12/20)
_eventProgressText = CreateText(panel.transform, "0 / 0", 10, UnityEngine.Color.white);
_eventRoot.SetActive(false);
}
public static Text CreateText(Transform parent, string content, int size, UnityEngine.Color col)
{
GameObject txtObj = new GameObject("Text", typeof(RectTransform), typeof(Text));
txtObj.transform.SetParent(parent, false);
Text t = txtObj.GetComponent<Text>();
t.text = content;
t.fontSize = size;
t.color = col;
t.alignment = UnityEngine.TextAnchor.MiddleCenter;
t.font = GetValheimFont();
return t;
}
public static UnityEngine.Font GetValheimFont()
{
foreach (UnityEngine.Font f in Resources.FindObjectsOfTypeAll<UnityEngine.Font>())
if (f.name == "AveriaSerifLibre-Bold") return f;
return Resources.GetBuiltinResource<UnityEngine.Font>("Arial.ttf");
}
public static void UpdateDisplay(string title, int current, int total, Color barColor)
{
if (_eventRoot == null) return;
_eventRoot.SetActive(true);
_canvasGroup.alpha = 1f;
_eventTitleText.text = title.ToUpper();
_eventProgressText.text = $"{current} / {total}";
_progressBar.fillAmount = (float)current / total;
_progressBar.color = barColor;
}
public static void Hide() => _canvasGroup.alpha = 0f;
}
}

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MxValheim.EventSystem
{
internal class EventList
{
public static void SetupCustomRaid(RandEventSystem system)
{
// 1. Create the Event Data
RandomEvent myCustomRaid = new RandomEvent
{
m_name = "MxEvent_God1",
m_enabled = true,
m_duration = 600f, // 10 minutes
m_nearBaseOnly = true, // Must be near player structures
m_pauseIfNoPlayerInArea = true,
m_forceMusic = "Music_Boss_Moder", // Use specific music
m_startMessage = "The gods themselves challenge you...",
m_endMessage = "The gods are satisfied.",
m_forceEnvironment = "SnowStorm" // Force weather
};
// 2. Define what spawns during this event
SpawnSystem.SpawnData frostWraith = new SpawnSystem.SpawnData
{
m_name = "Frost Wraith",
m_prefab = ZNetScene.instance.GetPrefab("Wraith"), // Get the Wraith prefab
m_maxSpawned = 5, // Max alive at once
m_spawnInterval = 10f, // Try to spawn every 10s
m_spawnDistance = 30f, // Distance from player
m_spawnRadiusMax = 10f,
m_spawnRadiusMin = 5f,
m_groupSizeMin = 1,
m_groupSizeMax = 2,
m_enabled = true
};
myCustomRaid.m_spawn = new List<SpawnSystem.SpawnData> { frostWraith };
// 3. Add to the game's master list
system.m_events.Add(myCustomRaid);
}
}
}

View File

@@ -0,0 +1,51 @@
using MxValheim.Patch.HUD;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace MxValheim.EventSystem
{
internal class Experience
{
public static int GetRequiredExpByLevel(int level)
{
return (level * 100);
}
public static void AddExperience(string playerid, int amount)
{
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_RequestProfile", playerid);
int level = Profiles.playerLevel;
int exp = Profiles.playerExperience;
exp += amount;
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_ServerSetUserExperience", playerid, exp);
exp = Profiles.playerExperience;
int maxexp = GetRequiredExpByLevel(level);
UpdateProgressBar(playerid);
if (exp >= maxexp)
{
exp -= maxexp;
level++;
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_ServerSetUserLevel", playerid, level);
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_ServerSetUserExperience", playerid, exp);
UpdateProgressBar(playerid);
}
}
public static void UpdateProgressBar(string playerid)
{
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_RequestProfile", playerid);
float expPercentage = ((float)Profiles.playerExperience / GetRequiredExpByLevel(Profiles.playerLevel));
if (Clock.m_barFill != null)
{
Clock.m_barFill.anchorMax = new Vector2(expPercentage, 1);
Clock.m_barFill.offsetMin = Vector2.zero;
Clock.m_barFill.offsetMax = Vector2.zero;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using HarmonyLib;
using Splatform;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -16,207 +17,24 @@ namespace MxValheim.EventSystem
{
internal class EventSystem_Patch
{
public static GameObject _eventRoot;
public static Text _eventTitleText;
public static Text _eventProgressText;
public static Image _progressBar;
public static CanvasGroup _canvasGroup;
// --- STATE DATA ---
public enum RaidPhase
{
None,
Scouts,
EliteGuard,
BossInbound,
Completed
}
public static RaidPhase CurrentPhase = RaidPhase.None;
public static int KillsInCurrentPhase = 0;
// --- 1. HUD CREATION (Your Style) ---
public static void CreateEventHud()
{
if (_eventRoot != null) return;
_eventRoot = new GameObject("MxEventHUD_Root");
UnityEngine.Object.DontDestroyOnLoad(_eventRoot);
Canvas c = _eventRoot.AddComponent<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
c.sortingOrder = 9999;
CanvasScaler scaler = _eventRoot.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
_canvasGroup = _eventRoot.AddComponent<CanvasGroup>();
_canvasGroup.alpha = 0f;
// Background Panel (Positioned -90 to sit below KillFeed at -30)
GameObject panel = new GameObject("Background", typeof(RectTransform), typeof(Image));
panel.transform.SetParent(_eventRoot.transform, false);
Image panelImage = panel.GetComponent<Image>();
panelImage.color = new Color(0, 0, 0, 0.7f);
// Styling with Valheim Assets
foreach (Sprite s in Resources.FindObjectsOfTypeAll<Sprite>())
{
if (s.name == "item_background")
{
panelImage.sprite = s;
panelImage.type = Image.Type.Sliced;
break;
}
}
RectTransform pRect = panel.GetComponent<RectTransform>();
pRect.anchorMin = pRect.anchorMax = pRect.pivot = new Vector2(0.5f, 1f);
pRect.anchoredPosition = new Vector2(0, -90); // Stacks below your KillFeed
pRect.sizeDelta = new Vector2(400, 55);
VerticalLayoutGroup layout = panel.AddComponent<VerticalLayoutGroup>();
layout.childAlignment = TextAnchor.MiddleCenter;
layout.spacing = 2f;
layout.padding = new RectOffset(10, 10, 5, 5);
// Title
_eventTitleText = CreateText(panel.transform, "RAID STATUS", 14, Color.yellow);
// Progress Bar
GameObject barBg = new GameObject("BarBG", typeof(RectTransform), typeof(Image));
barBg.transform.SetParent(panel.transform, false);
barBg.GetComponent<Image>().color = new Color(0.1f, 0.1f, 0.1f, 0.9f);
barBg.GetComponent<RectTransform>().sizeDelta = new Vector2(350, 8);
GameObject fillObj = new GameObject("Fill", typeof(RectTransform), typeof(Image));
fillObj.transform.SetParent(barBg.transform, false);
_progressBar = fillObj.GetComponent<Image>();
_progressBar.color = Color.red;
_progressBar.type = Image.Type.Filled;
_progressBar.fillMethod = Image.FillMethod.Horizontal;
_progressBar.GetComponent<RectTransform>().anchorMin = Vector2.zero;
_progressBar.GetComponent<RectTransform>().anchorMax = Vector2.one;
_progressBar.GetComponent<RectTransform>().sizeDelta = Vector2.zero;
// Progress Text
_eventProgressText = CreateText(panel.transform, "0 / 0", 11, Color.white);
}
private static Text CreateText(Transform parent, string content, int size, Color col)
{
GameObject txtObj = new GameObject("Text", typeof(Text));
txtObj.transform.SetParent(parent, false);
Text t = txtObj.GetComponent<Text>();
t.text = content;
t.fontSize = size;
t.color = col;
t.alignment = TextAnchor.MiddleCenter;
Font valFont = null;
foreach (Font f in Resources.FindObjectsOfTypeAll<Font>())
if (f.name == "AveriaSerifLibre-Bold") valFont = f;
t.font = valFont ?? Resources.GetBuiltinResource<Font>("Arial.ttf");
return t;
}
// --- 2. WAVE LOGIC ---
public static void UpdateWave(bool reset = false)
{
Debug.Log($"MxEvent:UpdateWave Seen a wave start.");
if (reset)
{
CurrentPhase = RaidPhase.Scouts;
KillsInCurrentPhase = 0;
}
int target = GetTargetForPhase(CurrentPhase);
Color barColor = (CurrentPhase == RaidPhase.Scouts) ? Color.magenta : Color.red;
UpdateDisplay($"PHASE: {CurrentPhase}", KillsInCurrentPhase, target, barColor);
}
public static int GetTargetForPhase(RaidPhase phase)
{
switch (phase)
{
case RaidPhase.Scouts: return 5; // 5 kills to clear scouts
case RaidPhase.EliteGuard: return 10; // 10 kills for elite guard
case RaidPhase.BossInbound: return 1; // Just the boss
default: return 0;
}
}
public static void AdvancePhase()
{
KillsInCurrentPhase = 0; // Reset counter for the new phase
if (CurrentPhase == RaidPhase.Scouts) CurrentPhase = RaidPhase.EliteGuard;
else if (CurrentPhase == RaidPhase.EliteGuard) CurrentPhase = RaidPhase.BossInbound;
else CurrentPhase = RaidPhase.Completed;
if (CurrentPhase == RaidPhase.Scouts)
{
// Spawn Eikthyr at the player's location
GameObject boarPrefab = ZNetScene.instance.GetPrefab("Boar");
if (boarPrefab != null)
{
UnityEngine.Object.Instantiate(boarPrefab, Player.m_localPlayer.transform.position + Vector3.forward * 20f, Quaternion.identity);
}
}
if (CurrentPhase == RaidPhase.BossInbound)
{
// Spawn Eikthyr at the player's location
GameObject bossPrefab = ZNetScene.instance.GetPrefab("Eikthyr");
if (bossPrefab != null)
{
UnityEngine.Object.Instantiate(bossPrefab, Player.m_localPlayer.transform.position + Vector3.forward * 10f, Quaternion.identity);
}
}
Debug.Log($"MxEvent: Advancing to {CurrentPhase}");
// Play a sound to notify the player (The 'Quest Update' sound)
Player.m_localPlayer?.Message(MessageHud.MessageType.Center, $"$inventory_newphase: {CurrentPhase}");
if (CurrentPhase == RaidPhase.Completed)
{
Debug.Log("MxEvent: All phases complete. Ending vanilla raid.");
EndRaidManually();
}
}
public static void UpdateDisplay(string title, int curr, int total, Color col)
{
Debug.Log($"MxEvent:UpdateDisplay Something reached my display update logic.");
if (_eventRoot == null) CreateEventHud();
_eventRoot.SetActive(true);
_canvasGroup.alpha = 1f;
_eventTitleText.text = title;
_eventProgressText.text = $"{curr} / {total}";
_progressBar.fillAmount = (float)curr / total;
_progressBar.color = col;
}
public static void EndRaidManually()
{
if (RandEventSystem.instance != null)
{
// This tells Valheim the time is up
RandomEvent activeEvent = RandEventSystem.instance.GetActiveEvent();
if (activeEvent != null)
{
activeEvent.m_time = 0;
}
}
FadeOutHud(); // Start your fade out
}
// --- 3. HARMONY PATCHES ---
[HarmonyPatch(typeof(Player), nameof(Player.Awake))]
public static class PlayerAwakePatch
{
static void Postfix(Player __instance)
{
// Only trigger for the actual human player, not NPCs/Dummies
if (__instance == Player.m_localPlayer)
{
string pid = GetUserPlayerID(Player.m_localPlayer).ToString();
ZLog.Log($"[ProfileSystem] Spawning finished. Requesting profile for {pid}");
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_RequestProfile", pid);
}
}
}
[HarmonyPatch(typeof(RandEventSystem), nameof(RandEventSystem.FixedUpdate))]
public static class DebugRaidFrequency
{
@@ -234,32 +52,7 @@ namespace MxValheim.EventSystem
{
static void Prefix(RandEventSystem __instance)
{
RandomEvent activeEvent = __instance.GetActiveEvent();
// If we are in our custom phases and haven't reached "Completed" yet
if (activeEvent != null && CurrentPhase != RaidPhase.Completed)
{
// Valheim subtracts time in FixedUpdate.
// We add it back using the engine's global fixedDeltaTime.
activeEvent.m_time += Time.fixedDeltaTime;
}
}
}
// Ensure HUD is ready when the game UI loads
[HarmonyPatch(typeof(Hud), nameof(Hud.Awake))]
[HarmonyPostfix]
static void InitHud() => CreateEventHud();
[HarmonyPatch(typeof(Hud), nameof(Hud.Update))]
[HarmonyPostfix]
static void CheckForExistingRaid()
{
// If the HUD isn't visible but a raid IS active, force it to show
if (CurrentPhase == RaidPhase.None && RandEventSystem.instance.GetActiveEvent() != null)
{
Debug.Log("MxEvent: Detected existing raid on HUD update. Synchronizing...");
UpdateWave(true);
}
}
@@ -282,13 +75,29 @@ namespace MxValheim.EventSystem
_lastKnownEvent = activeEvent;
// Force HUD Creation and Display
UpdateWave(true);
//UpdateWave(true);
}
else if (activeEvent == null && _lastKnownEvent != null)
{
Debug.Log("MxEvent: HUD Heartbeat detected Raid End.");
string pid = GetUserPlayerID(Player.m_localPlayer).ToString();
Heightmap.Biome currentBiome = EnvMan.instance.GetCurrentBiome();
Reward.GiveItemStatic("Coins",Reward.CalculateRewardCoin(Profiles.playerLevel, currentBiome));
System.Random rand = new System.Random();
Reward.GiveItemStatic("Feathers", rand.Next(1,10));
if (rand.Next(100) < 50)
{
Reward.GiveItemRandom(Reward.GetRewardPrefab(Profiles.playerLevel, "food"), 1);
}
if (rand.Next(100) < 25)
{
Reward.GiveItemRandom(Reward.GetRewardPrefab(Profiles.playerLevel, "heal"), 1);
}
if (rand.Next(100) < 10)
{
Reward.GiveItemRandom(Reward.GetRewardPrefab(Profiles.playerLevel, "metal"), 1);
}
_lastKnownEvent = null;
FadeOutHud();
}
}
}
@@ -312,85 +121,41 @@ namespace MxValheim.EventSystem
Debug.Log("MxEvent: Confirmed Active Event!");
Vector3 playerPos = Player.m_localPlayer.transform.position;
string pid = GetUserPlayerID(Player.m_localPlayer).ToString();
Debug.Log($"EventSystem:ApplyDamage User {pid}");
float distance = Vector3.Distance(playerPos, activeEvent.m_pos);
Debug.Log($"MxEvent: Distance: {distance}");
if (distance <= 96f)
{
KillsInCurrentPhase++;
int target = GetTargetForPhase(CurrentPhase);
int level = __instance.GetLevel();
int exp =
(level == 1) ? 2:
(level == 2) ? 3:
(level == 3) ? 4:
5;
if (KillsInCurrentPhase >= target && CurrentPhase != RaidPhase.Completed)
{
AdvancePhase();
}
Experience.AddExperience(pid, exp);
// Send the update
/* Send the update
ZRoutedRpc.instance.InvokeRoutedRPC(
ZRoutedRpc.Everybody,
"RPC_UpdateRaidHUD",
$"PHASE: {CurrentPhase}",
KillsInCurrentPhase,
GetTargetForPhase(CurrentPhase)
);
);*/
}
}
}
}
public static bool IsEventCreature(Character character)
{
ZNetView nview = character.GetComponent<ZNetView>();
if (nview == null || nview.GetZDO() == null) return false;
// Layer 1: Check the networked boolean (The standard way)
if (nview.GetZDO().GetBool("eventcreature")) return true;
// Layer 2: Check the BaseAI component (The local way)
BaseAI ai = character.GetComponent<BaseAI>();
if (ai != null)
{
bool isRaidMob = (bool)AccessTools.Field(typeof(BaseAI), "m_eventCreature").GetValue(ai);
if (isRaidMob) return true;
}
return false;
}
// Hide HUD when raid ends
[HarmonyPatch(typeof(RandEventSystem), nameof(RandEventSystem.ResetRandomEvent))]
[HarmonyPostfix]
static void OnRaidEnd()
{
FadeOutHud();
CurrentPhase = RaidPhase.None;
}
private static IEnumerator FadeOutHud()
{
Debug.Log("MxEvent: Starting Fade Out...");
// Safety check for the component
if (_canvasGroup == null)
{
_eventRoot.SetActive(false);
yield break;
}
float startAlpha = _canvasGroup.alpha;
float rate = 1.0f / 1.5f; // Fade over 1.5 seconds
float progress = 0.0f;
while (progress < 1.0f)
{
progress += Time.deltaTime * rate;
_canvasGroup.alpha = Mathf.Lerp(startAlpha, 0, progress);
yield return null;
}
_canvasGroup.alpha = 0;
_eventRoot.SetActive(false); // Fully disable to ensure it's gone
Debug.Log("MxEvent: HUD Hidden and Deactivated.");
}
}
}

View File

@@ -0,0 +1,167 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MxValheim.EventSystem
{
internal class Profiles
{
public class ProfileRoot
{
public string playerid { get; set; }
public string player_name { get; set; }
public int level { get; set; }
public int experience { get; set; }
}
public static bool userExists = false;
public static int playerLevel = 1;
public static int playerExperience = 0;
public static void ServerUserExists(string playerid)
{
// Check to ensure this is actually the server running this
ZNet zn = new ZNet();
if (!zn.IsDedicated()) return;
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
if (File.Exists(profilePath)) userExists = true;
}
public static void ServerReceiveCreateUser(long sender, string playerid, string playerName)
{
// Check to ensure this is actually the server running this
ZNet zn = new ZNet();
if (!zn.IsDedicated()) return;
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
ProfileRoot pr = new ProfileRoot
{
playerid = playerid,
player_name = playerName,
level = 1,
experience = 0
};
var raw = JsonConvert.SerializeObject(pr, Formatting.Indented);
File.WriteAllText(profilePath, raw);
}
public static void ServerHandleProfileRequest(long sender, string playerid)
{
// Check to ensure this is actually the server running this
ZNet zn = new ZNet();
if (!zn.IsDedicated()) return;
ZNetPeer peer = ZNet.instance.GetPeer(sender);
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
if (File.Exists(profilePath))
{
string rawJson = File.ReadAllText(profilePath);
// Send ONLY to the 'sender' who requested it
ZRoutedRpc.instance.InvokeRoutedRPC(sender, "RPC_ClientReceiveProfile", rawJson);
}
else
{
ProfileRoot pr = new ProfileRoot
{
playerid = playerid,
player_name = peer.m_playerName,
level = 1,
experience = 0
};
var raw = JsonConvert.SerializeObject(pr, Formatting.Indented);
File.WriteAllText(profilePath, raw);
}
}
public static void RequestCreateUser(string playerid, Player player)
{
string playerName = player.GetHoverName();
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_ServerCreateUser", playerid, playerName);
}
public static void ClientLoadProfile(long sender, string json)
{
ProfileRoot loadedProfile = JsonConvert.DeserializeObject<ProfileRoot>(json);
playerLevel = loadedProfile.level;
playerExperience = loadedProfile.experience;
//ZLog.Log($"[Client] Profile loaded! Level: {loadedProfile.level}");
}
public static void CreateUser(string playerid, Player player)
{
// We only send the raw data; we let the server handle the file I/O
string playerName = player?.GetHoverName() ?? "Unknown";
// Invoke the RPC on the Server (0 is the server ID)
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_ServerCreateUser", playerid, playerName);
}
public static void ServerSetUserExperience(long sender, string playerid, int exp)
{
// Check to ensure this is actually the server running this
ZNet zn = new ZNet();
if (!zn.IsDedicated()) return;
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
ProfileRoot pro = JsonConvert.DeserializeObject<ProfileRoot>(File.ReadAllText(profilePath));
ProfileRoot pr = new ProfileRoot
{
playerid = playerid,
player_name = pro.player_name,
level = pro.level,
experience = exp
};
var raw = JsonConvert.SerializeObject(pr, Formatting.Indented);
File.WriteAllText(profilePath, raw);
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_RequestProfile", playerid);
}
public static void SetUserExperience(string playerid, int exp)
{
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
ProfileRoot pro = JsonConvert.DeserializeObject<ProfileRoot>(File.ReadAllText(profilePath));
ProfileRoot pr = new ProfileRoot
{
playerid = playerid,
player_name = pro.player_name,
level = pro.level,
experience = exp
};
var raw = JsonConvert.SerializeObject(pr, Formatting.Indented);
File.WriteAllText(profilePath, raw);
}
public static void ServerSetUserLevel(long sender, string playerid, int level)
{
// Check to ensure this is actually the server running this
ZNet zn = new ZNet();
if (!zn.IsDedicated()) return;
string profilePath = Path.Combine(MxValheimMod.internalDataEventSystemPath, $"{playerid}.json");
ProfileRoot pro = JsonConvert.DeserializeObject<ProfileRoot>(File.ReadAllText(profilePath));
ProfileRoot pr = new ProfileRoot
{
playerid = playerid,
player_name = pro.player_name,
level = level,
experience = pro.experience
};
var raw = JsonConvert.SerializeObject(pr, Formatting.Indented);
File.WriteAllText(profilePath, raw);
ZRoutedRpc.instance.InvokeRoutedRPC(0, "RPC_RequestProfile", playerid);
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace MxValheim.EventSystem
{
internal class Reward
{
public static int CalculateRewardCoin(int level, Heightmap.Biome biome)
{
Dictionary<int, int> biomeBase = new Dictionary<int, int>()
{
{ 1, 10 },
{ 2, 20 },
{ 4, 30 },
{ 8, 40 },
{ 16, 50 },
{ 512, 60 },
{ 64, 70 },
};
int rewardCoin = biomeBase[((int)biome)] * level;
return rewardCoin;
}
public static string GetRewardPrefab(int level, string type)
{
string[] foodList = { "OnionSoup", "CarrotSoup", "DeerStew", "Salad", "SerpentStew", "WolfMeatSkewer" };
string[] healList = { "MeadHealthMinor", "MeadHealthMedium", "MeadHealthMajor", "MeadHealthLingering" };
string[] metalList = { "CopperOre", "TinOre", "IronScrap", "SilverOre", "BlackMetalScrap", "FlametalOreNew" };
System.Random rng = new System.Random();
int foodTier = rng.Next(1, foodList.Length);
int healTier = rng.Next(1, healList.Length);
int metalTier = rng.Next(1, metalList.Length);
if (type == "food") return foodList[foodTier];
if (type == "heal") return healList[healTier];
if (type == "metal") return metalList[metalTier];
return "Wood";
}
public static void GiveItemRandom(string itemName, int qty)
{
System.Random rng = new System.Random();
Player player = Player.m_localPlayer;
if (player == null) return;
GameObject prefab = ObjectDB.instance.GetItemPrefab(itemName);
if (prefab != null)
{
player.GetInventory().AddItem(itemName, qty, 1, 0, 0, "");
player.Message(MessageHud.MessageType.TopLeft, $"Received {qty}x {itemName}");
}
else
{
Debug.LogWarning($"Item {itemName} not found in ObjectDB!");
}
}
public static void GiveItemStatic(string itemName, int qty)
{
Player player = Player.m_localPlayer;
if (player == null) return;
GameObject prefab = ObjectDB.instance.GetItemPrefab(itemName);
if (prefab != null)
{
player.GetInventory().AddItem(itemName, qty, 1, 0, 0, "");
player.Message(MessageHud.MessageType.TopLeft, $"Received {qty}x {itemName}");
}
else
{
Debug.LogWarning($"Item {itemName} not found in ObjectDB!");
}
}
}
}

View File

@@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace MxValheim.EventSystem
{
public enum RaidPhase { None, Scouts, Siege, Boss, Victory }
public static class WaveManager
{
public static RaidPhase CurrentPhase = RaidPhase.None;
public static int KillsInCurrentPhase = 0;
public static int TotalInPhase = 20;
public static void AdvanceWave()
{
KillsInCurrentPhase = 0;
CurrentPhase++;
switch (CurrentPhase)
{
case RaidPhase.Scouts:
//
break;
case RaidPhase.Siege:
//
break;
case RaidPhase.Boss:
//
//SpawnBoss();
break;
case RaidPhase.Victory:
//
break;
}
}
public static void UpdateWave(bool reset = false)
{
if (reset)
{
CurrentPhase = RaidPhase.Scouts;
KillsInCurrentPhase = 0;
}
int target = GetTargetForWave(CurrentPhase);
Color barColor = (CurrentPhase == RaidPhase.Boss) ? Color.magenta : Color.red;
EventHud.UpdateDisplay($"PHASE: {CurrentPhase}", KillsInCurrentPhase, target, barColor);
}
private static int GetTargetForWave(RaidPhase phase)
{
switch (phase)
{
case RaidPhase.Scouts: return 5;
case RaidPhase.Siege: return 15;
case RaidPhase.Boss: return 1;
default: return 0;
}
}
}
}