Skip to content

Commit

Permalink
Merge pull request #28 from jonathan-robertson/add-quest-reset
Browse files Browse the repository at this point in the history
Add more Options and Quest Reset (buggy)
  • Loading branch information
jonathan-robertson authored Jul 21, 2022
2 parents 5802151 + ffb60ed commit 5cd9a50
Show file tree
Hide file tree
Showing 22 changed files with 1,359 additions and 243 deletions.
168 changes: 11 additions & 157 deletions Amnesia/API.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,29 @@
using Amnesia.Data;
using Amnesia.Handlers;
using Amnesia.Utilities;
using System;
using System.Collections.Generic;
using static EntityBuffs;

namespace Amnesia {
internal class API : IModApi {
private static readonly ModLog log = new ModLog(typeof(API));
private static readonly List<RespawnType> respawnTypesToMonitor = new List<RespawnType>{
RespawnType.JoinMultiplayer,
RespawnType.EnterMultiplayer
};
private static readonly List<EnumGameMessages> gameMessageTypesToMonitor = new List<EnumGameMessages>{
EnumGameMessages.EntityWasKilled
};

public static string QuestResetKickReason { get; private set; } = "This server is configured to erase some settings from your player file when you die for the final time. Please reconnect whenever you're ready.";

public static Dictionary<int, bool> Obituary { get; private set; } = new Dictionary<int, bool>();

public void InitMod(Mod _modInstance) {
if (Config.Load()) {
ModEvents.PlayerSpawnedInWorld.RegisterHandler(OnPlayerSpawnedInWorld);
ModEvents.GameMessage.RegisterHandler(OnGameMessage);
log.Error("Successfully loaded config; activating Amnesia mod.");
ModEvents.PlayerSpawnedInWorld.RegisterHandler(PlayerSpawnedInWorld.Handle);
ModEvents.GameMessage.RegisterHandler(GameMessage.Handle);
//ModEvents.PlayerDisconnected.RegisterHandler(PlayerDisconnected.Handle);
ModEvents.SavePlayerData.RegisterHandler(SavePlayerData.Handle);
//ModEvents.GameStartDone.RegisterHandler(GameStartDone.Handle);
} else {
log.Error("Unable to load or recover from configuration issue; this mod will not activate.");
}
}

/**
* <summary>Handle player spawning into world.</summary>
* <param name="clientInfo">The client currently spawning in.</param>
* <param name="respawnType">The type of respawn.</param>
* <param name="pos">The position this player is respawning to.</param>
* <remarks>This mod supports being dropped into an existing game, thanks to how we handle this process.</remarks>
*/
private void OnPlayerSpawnedInWorld(ClientInfo clientInfo, RespawnType respawnType, Vector3i pos) {
try {
if (!respawnTypesToMonitor.Contains(respawnType)) {
return; // exit early if we don't care about the presented respawnType
}

// Fetch player if possible
if (clientInfo == null || !GameManager.Instance.World.Players.dict.TryGetValue(clientInfo.entityId, out EntityPlayer player)) {
log.Warn("JoinMultiplayer/EnterMultiplayer event sent from a non-player client... may want to investigate");
return; // exit early if player cannot be found in active world
}

// Remove Positive Outlook if admin disabled it since player's last login
if (!Config.EnablePositiveOutlook) {
player.Buffs.RemoveBuff("buffAmnesiaPositiveOutlook");
}

// Update player's max/remaining lives to fit new MaxLives changes if necessary
AdjustToMaxOrRemainingLivesChange(player);
} catch (Exception e) {
log.Error("Failed to handle PlayerSpawnedInWorld event.", e);
}
}

public static void AdjustToMaxOrRemainingLivesChange(EntityPlayer player) {
// Initialize player and/or adjust max lives
var maxLivesSnapshot = player.GetCVar(Values.MaxLivesCVar);
Expand All @@ -74,121 +43,6 @@ public static void AdjustToMaxOrRemainingLivesChange(EntityPlayer player) {
if (remainingLivesSnapshot > Config.MaxLives) {
player.SetCVar(Values.RemainingLivesCVar, Config.MaxLives);
}

// Apply the appropriate buff to reflect the player's situation
if (UpdateAmnesiaBuff(player) != BuffStatus.Added) {
log.Error($"Failed to add buff to player {player.GetDebugName()}");
}
}

private bool OnGameMessage(ClientInfo clientInfo, EnumGameMessages messageType, string message, string mainName, bool localizeMain, string secondaryName, bool localizeSecondary) {
try {
if (!gameMessageTypesToMonitor.Contains(messageType)) {
return true; // exit early, do not interrupt other mods from processing event
}

if (clientInfo == null || !GameManager.Instance.World.Players.dict.TryGetValue(clientInfo.entityId, out var player)) {
log.Warn("EntityWasKilled event sent from a non-player client... may want to investigate");
return true; // exit early, do not interrupt other mods from processing event
}

var livesRemaining = player.GetCVar(Values.RemainingLivesCVar);

// cap lives to maximum(sanity check)
if (livesRemaining > Config.MaxLives) {
// "shouldn't" have to do this since we auto-push changes as they're made and on login... but just in case:
player.SetCVar(Values.MaxLivesCVar, Config.MaxLives);
livesRemaining = Config.MaxLives;
}

// Calculate and apply remaining lives
if (livesRemaining > 0) {
player.SetCVar(Values.RemainingLivesCVar, livesRemaining - 1);
} else if (livesRemaining == 0) {
ResetPlayer(player);
player.SetCVar(Values.RemainingLivesCVar, Config.MaxLives);
player.Buffs.AddBuff("buffAmnesiaMemoryLoss");
if (Config.EnablePositiveOutlook) {
player.Buffs.AddBuff("buffAmnesiaPositiveOutlook");
}
}

if (UpdateAmnesiaBuff(player) != BuffStatus.Added) {
log.Error($"Failed to add buff to player {player.GetDebugName()}");
}
} catch (Exception e) {
log.Error("Failed to handle GameMessage event.", e);
}
return true; // do not interrupt other mods from processing event
}

public static BuffStatus UpdateAmnesiaBuff(EntityPlayer player) {
var remainingLives = player.GetCVar(Values.RemainingLivesCVar);
if (remainingLives == 0) {
return player.Buffs.AddBuff("buffAmnesiaMentallyUnhinged");
} else if (remainingLives <= Config.WarnAtLife) {
return player.Buffs.AddBuff("buffAmnesiaMentallyUneasy");
} else {
return player.Buffs.AddBuff("buffAmnesiaMentallyStable");
}
}

/**
* <summary></summary>
* <remarks>Most of the following core logic was lifted from ActionResetPlayerData.PerformTargetAction</remarks>
*/
private void ResetPlayer(EntityPlayer player) {
var resetLevels = true; // TODO: could be config value

if (resetLevels) {
player.Progression.ResetProgression(true);
player.Progression.Level = 1;
player.Progression.ExpToNextLevel = player.Progression.GetExpForNextLevel();
player.Progression.SkillPoints = 0;
player.Progression.ExpDeficit = 0;
List<Recipe> recipes = CraftingManager.GetRecipes();
for (int i = 0; i < recipes.Count; i++) {
if (recipes[i].IsLearnable) {
player.Buffs.RemoveCustomVar(recipes[i].GetName());
}
}

// Inform client cycles of level adjustment for health/stamina/food/water max values
player.SetCVar("$LastPlayerLevel", player.Progression.Level);

// Flush xp tracking counters
player.SetCVar("_xpFromLoot", player.Progression.Level);
player.SetCVar("_xpFromHarvesting", player.Progression.Level);
player.SetCVar("_xpFromKill", player.Progression.Level);
player.SetCVar("$xpFromLootLast", player.Progression.Level);
player.SetCVar("$xpFromHarvestingLast", player.Progression.Level);
player.SetCVar("$xpFromKillLast", player.Progression.Level);

// Set flags to trigger incorporation of new stats/values into update cycle
player.Progression.bProgressionStatsChanged = true;
player.bPlayerStatsChanged = true;
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(NetPackageManager.GetPackage<NetPackagePlayerStats>().Setup(player), false, player.entityId);
}

/* TODO: Maybe this can be added later. Does not work as is written; probably needs to send some net packages to update the client
var removeLandclaims = true; // TODO: could be a param
var removeSleepingBag = true; // TODO: could be a param
if (removeLandclaims) {
PersistentPlayerData playerDataFromEntityID = GameManager.Instance.persistentPlayers.GetPlayerDataFromEntityID(player.entityId);
if (playerDataFromEntityID.LPBlocks != null) {
playerDataFromEntityID.LPBlocks.Clear();
}
NavObjectManager.Instance.UnRegisterNavObjectByOwnerEntity(player, "land_claim");
SdtdConsole.Instance.Output("removed land claims.");
}
if (removeSleepingBag) {
PersistentPlayerData playerDataFromEntityID = GameManager.Instance.persistentPlayers.GetPlayerDataFromEntityID(player.entityId);
player.SpawnPoints.Clear();
playerDataFromEntityID.ClearBedroll();
SdtdConsole.Instance.Output("removed sleeping bag and respawn target.");
}
*/
}
}
}
16 changes: 16 additions & 0 deletions Amnesia/Amnesia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,32 @@
<Compile Include="Commands\ConsoleCmdAmnesia.cs" />
<Compile Include="Data\Config.cs" />
<Compile Include="Data\Values.cs" />
<Compile Include="Handlers\GameMessage.cs" />
<Compile Include="Handlers\GameStartDone.cs" />
<Compile Include="Handlers\PlayerDisconnected.cs" />
<Compile Include="Handlers\PlayerSpawnedInWorld.cs" />
<Compile Include="Handlers\SavePlayerData.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utilities\ModLog.cs" />
<Compile Include="Utilities\PlayerHelper.cs" />
<Compile Include="Utilities\QuestHelper.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Config\buffs.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\items.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\Localization.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\traders.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\ui_display.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\XUi\windows.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down
Loading

0 comments on commit 5cd9a50

Please sign in to comment.