Skip to content

Commit

Permalink
Merge pull request #17 from jpw1991/11-find-a-material-resource-cost-…
Browse files Browse the repository at this point in the history
…for-minions

11 find a material resource cost for minions
  • Loading branch information
jpw1991 committed May 19, 2024
2 parents f5d36cc + 3704a1b commit cfe145b
Show file tree
Hide file tree
Showing 15 changed files with 715 additions and 14 deletions.
5 changes: 3 additions & 2 deletions ChebsNecromancy.dfmod.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ModTitle": "Cheb's Necromancy",
"ModVersion": "0.0.15",
"ModVersion": "0.0.16",
"ModAuthor": "Cheb Gonaz",
"ContactInfo": "chebgonaz@gmail.com",
"DFUnity_Version": "1.1.0",
Expand Down Expand Up @@ -33,6 +33,7 @@
"Assets/Game/Mods/daggerfall-chebs-necromancy/Scripts/MinionSpawners/AncientVampireSpawner.cs",
"Assets/Game/Mods/daggerfall-chebs-necromancy/Scripts/RoleplayRealismItemsMod/StartingEquipmentStuff.cs",
"Assets/Game/Mods/daggerfall-chebs-necromancy/ChebsNecromancy.dfmod.json",
"Assets/Game/Mods/daggerfall-chebs-necromancy/modsettings.json"
"Assets/Game/Mods/daggerfall-chebs-necromancy/modsettings.json",
"Assets/Game/Mods/daggerfall-chebs-necromancy/Scripts/SpawnCorpseItemCommand.cs"
]
}
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ Spells can be created at the spell maker in the Mage's Guild.

![image](https://github.com/jpw1991/daggerfall-chebs-necromancy/assets/13718599/d6377ecd-f057-4e6d-8dca-a7f74160ba02)

## Ingredient Requirements

If enabled in the config (default: enabled), items will be required to create minions with. Items are only consumed on successful reanimation.

Minion | Requirements
--- | ---
Skeleton | Humanoid Corpse
Ghost | Humanoid Corpse, Ectoplasm
Zombie | Humanoid Corpse
Mummy | Humanoid Corpse, Oil/Bandage
Vampire | Humanoid Corpse, Red/Yellow Rose
Lich | Humanoid Corpse, Lich Dust
Ancient Vampire | Humanoid Corpse, Black/White Rose
Ancient Lich | Humanoid Corpse, Lich Dust

Item requirements help with balance by making minions more difficult to acquire, without imposing frustrating limits or durations.

## Installation

1. Copy mod to `/path/to/dfu/DaggerfallUnity_Data/StreamingAssets/Mods`
Expand All @@ -55,6 +72,8 @@ Check the log file and look for "Cheb" to find errors related to this mod.

The log file's [location differs per operating system](https://docs.unity3d.com/Manual/LogFiles.html). On Linux it is located in `$HOME/.config/unity3d/Daggerfall Workshop/Daggerfall Unity/Player.log`

The logging level can be increased/decreased in the mod settings. By default, only errors are logged.

## Cheating

<details>
Expand All @@ -67,13 +86,15 @@ The spell effects have a backend which can be triggered by console commands:
- `spawn skeleton` will spawn a skeletal warrior
- `spawn vampire` will spawn a vampire, etc.
- Type `recallminions` to bring the undead to your position if they get stuck or lost
- Type `sci` to spawn in a corpse item. This stands for `spawn corpse item` and follows the style of other daggerfall commands like `tgm`.

</details>

## Changelog

Version | Date | Info
--- |------------| ---
0.0.16 | 13/05/2024 | If enabled in config, enemy humanoids will drop corpses and these will be a requirement for successful reanimation.
0.0.15 | 28/04/2024 | Minions will follow you through area transitions.
0.0.14 | 28/04/2024 | Finish implementing limited magnitude scaling. A minion's health, as well as skeleton & zombie melee weapon quality, scales with the player's stats & skills and the spell's magnitude.
0.0.13 | 19/04/2024 | Expose many class options to the config so users can tweak stuff about it to their liking; improve quiz with necromancy-themed questions
Expand Down
155 changes: 151 additions & 4 deletions Scripts/ChebsNecromancy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using DaggerfallConnect.Arena2;
using DaggerfallWorkshop;
using DaggerfallWorkshop.Game;
using DaggerfallWorkshop.Game.Entity;
using DaggerfallWorkshop.Game.Items;
using DaggerfallWorkshop.Game.MagicAndEffects;
using DaggerfallWorkshop.Game.Serialization;
using DaggerfallWorkshop.Game.UserInterface;
Expand All @@ -16,6 +18,42 @@

namespace ChebsNecromancyMod
{
public class CustomCorpseItem : DaggerfallUnityItem
{
public const int TemplateIndex = 4733;
public const string DisplayName = "Humanoid Corpse";
public const ItemGroups TemplateItemGroup = ItemGroups.UselessItems1;

public CustomCorpseItem() : base(TemplateItemGroup, TemplateIndex)
{
value = 0;
weightInKg = 0f;
RenameItem(DisplayName);
}

// 380 is the value for MiscItems.Dead_Body in the texture archive. To view the archive, use the
// utility here: https://www.nexusmods.com/daggerfallunity/mods/460
// For some reason if I use 380 I get a blood splatter. But you know what? That's close enough for now.
public override int InventoryTextureArchive => 380;

public override bool IsStackable()
{
return true;
}

public override ItemData_v1 GetSaveData()
{
ItemData_v1 data = base.GetSaveData();
data.className = typeof(CustomCorpseItem).ToString();
return data;
}

public static DaggerfallUnityItem Create()
{
return ItemBuilder.CreateItem(TemplateItemGroup, TemplateIndex);
}
}

public enum Logging
{
Errors = 0,
Expand All @@ -25,6 +63,10 @@ public enum Logging
public class ChebsNecromancy : MonoBehaviour
{
public const string NecromancerCareerName = "Necromancer";

public static bool CorpseItemEnabled = true;
//public static DaggerfallUnityItem CorpseItem;

public static EffectBundleSettings AnimateDeadSpell, NoviceRecallSpell;
public static bool EnableCustomClassNecromancer = true;
public static DFCareer NecromancerCareer;
Expand Down Expand Up @@ -62,27 +104,126 @@ public static void Init(InitParams initParams)
RecallMinionsCommand.name, RecallMinionsCommand.description,
RecallMinionsCommand.usage, RecallMinionsCommand.Execute);

ConsoleCommandsDatabase.RegisterCommand(
SpawnCorpseItemCommand.name, SpawnCorpseItemCommand.description,
SpawnCorpseItemCommand.usage, SpawnCorpseItemCommand.Execute);

mod.LoadSettingsCallback = LoadSettings;

#region BeforeSettings
// Custom items
//CorpseItem = ItemBuilder.CreateItem(ItemGroups.MiscItems, (int)MiscItems.Dead_Body);
// CorpseItem.FromItemData(new ItemData_v1()
// {
//
// });
//var corpseItemTemplateIndex = ItemHelper.LastDFTemplate + 3000;
// var helper = new ItemHelper();
// helper.RegisterCustomItem(CustomCorpseItem.TemplateIndex, CustomCorpseItem.TemplateItemGroup, typeof(CustomCorpseItem));
DaggerfallUnity.Instance.ItemHelper.RegisterCustomItem(CustomCorpseItem.TemplateIndex, CustomCorpseItem.TemplateItemGroup, typeof(CustomCorpseItem));
ChebLog("CustomCorpseItem registered.");
// if (!helper.GetCustomItemClass(corpseItemTemplateIndex, out Type corpseItem))
// {
// ChebError("Failed to create custom corpse item.");
// }
// else
// {
// //CorpseItem = ItemBuilder.CreateItem(ItemGroups.MiscItems, corpseItemTemplateIndex);
// CorpseItem.value = 0;
// CorpseItem.weightInKg = 1.0f;
// CorpseItem.RenameItem("Humanoid Corpse");
// }
//CorpseItem = (CustomCorpseItem)Activator.CreateInstance(typeof(CustomCorpseItem)); // ItemBuilder.CreateItem(ItemGroups.MiscItems, corpseItemTemplateIndex);
//CorpseItem = ItemBuilder.CreateItem(ItemGroups.MiscItems, corpseItemTemplateIndex);
// CorpseItem.SetItem(ItemGroups.MiscItems, corpseItemTemplateIndex);
// var template = helper.GetItemTemplate(ItemGroups.MiscItems, corpseItemTemplateIndex);
// CorpseItem = ItemBuilder.CreateItem(ItemGroups.MiscItems, template.index); //CreateItem(template);
//ChebLog("Creating instance of CustomCorpseItem...");
//CorpseItem = ItemBuilder.CreateItem(ItemGroups.UselessItems1, CustomCorpseItem.TemplateIndex);
//CorpseItem = new CustomCorpseItem();
//CorpseItem.SetItem(ItemGroups.MiscItems, corpseItemTemplateIndex);

// Events
SaveLoadManager.OnLoad += RegisterExistingMinions;
//StateManager.OnStateChange += state => { ChebLog($"State changed: {state}"); };
// On pre-transition, make note of all active minions
PlayerEnterExit.OnPreTransition += args => { RecordActiveMinions(); };
// On post-transition, restore aforementioned active minions
PlayerEnterExit.OnTransitionExterior += args => { RestoreActiveMinions(); };
PlayerEnterExit.OnTransitionInterior += args => { RestoreActiveMinions(); };
PlayerEnterExit.OnTransitionDungeonExterior += args => { RestoreActiveMinions(); };
PlayerEnterExit.OnTransitionDungeonInterior += args => { RestoreActiveMinions(); };

// Custom item drops
EnemyDeath.OnEnemyDeath += OnEnemyDeath; // removed later, if disabled in settings.
#endregion

mod.LoadSettings();

#region AfterSettings
// Create spells after settings are loaded, so that values from the config get used.
AnimateDeadSpell = CreateAnimateDeadSpell();
NoviceRecallSpell = CreateNoviceRecallSpell();
#endregion

mod.IsReady = true;
}

public static void OnEnemyDeath(object sender, EventArgs eventArgs)
{
// drop a corpse if it's a humanoid or humanoid-like monster
var enemyDeath = sender as EnemyDeath;
if (enemyDeath == null) return;
if (!enemyDeath.TryGetComponent(out DaggerfallEntityBehaviour entityBehaviour)) return;
if (!(entityBehaviour.Entity is EnemyEntity enemyEntity)) return;
var dropCorpse = false;
if (enemyEntity.EntityType == EntityTypes.EnemyClass)
{
// humanoid
switch (enemyEntity.CareerIndex)
{
case (int)ClassCareers.Mage:
case (int)ClassCareers.Spellsword:
case (int)ClassCareers.Battlemage:
case (int)ClassCareers.Sorcerer:
case (int)ClassCareers.Healer:
case (int)ClassCareers.Nightblade:
case (int)ClassCareers.Bard:
case (int)ClassCareers.Acrobat:
case (int)ClassCareers.Assassin:
case (int)ClassCareers.Burglar:
case (int)ClassCareers.Rogue:
case (int)ClassCareers.Thief:
case (int)ClassCareers.Monk:
case (int)ClassCareers.Archer:
case (int)ClassCareers.Ranger:
case (int)ClassCareers.Barbarian:
case (int)ClassCareers.Warrior:
case (int)ClassCareers.Knight:
dropCorpse = true;
break;
}
}
else
{
// humanoid monster
switch (enemyEntity.CareerIndex)
{
case (int)MonsterCareers.Orc:
case (int)MonsterCareers.OrcSergeant:
case (int)MonsterCareers.OrcShaman:
case (int)MonsterCareers.OrcWarlord:
dropCorpse = true;
break;
}
}

if (dropCorpse)
{
ChebLog($"Dropping corpse for {enemyEntity.Name}");
//entityBehaviour.CorpseLootContainer.Items.AddItem(CorpseItem.Clone());
entityBehaviour.CorpseLootContainer.Items.AddItem(CustomCorpseItem.Create());
}
}

public static void RecordActiveMinions()
{
// Record all the active minions so that they can be restored post-transition.
Expand Down Expand Up @@ -336,8 +477,8 @@ static void LoadSettings(ModSettings modSettings, ModSettingsChange change)
var loggingMap = (Logging[])Enum.GetValues(typeof(Logging));
Log = loggingMap[modSettings.GetInt("General", "Logging")];

const string section = "Necromancer Class";
EnableCustomClassNecromancer = modSettings.GetBool(section, "Enabled");
const string classSection = "Necromancer Class";
EnableCustomClassNecromancer = modSettings.GetBool(classSection, "Enabled");

NecromancerCareer = GenerateNecromancerCareer(modSettings);

Expand Down Expand Up @@ -401,6 +542,12 @@ static void LoadSettings(ModSettings modSettings, ModSettingsChange change)
effectBroker.RegisterEffectTemplate(baseEntityEffect);
}
}

const string corpseSection = "Corpse Item";
CorpseItemEnabled = modSettings.GetBool(corpseSection, "Enabled");
if (!CorpseItemEnabled) EnemyDeath.OnEnemyDeath -= OnEnemyDeath;
// to do: get that to work
//CorpseItem.weightInKg = modSettings.GetInt(corpseSection, "Weight in KG");
}

private static void RegisterExistingMinions(SaveData_v1 saveDataV1)
Expand Down
1 change: 0 additions & 1 deletion Scripts/Effects/RecallMinionsEffect.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using DaggerfallConnect;
using DaggerfallWorkshop;
using DaggerfallWorkshop.Game.MagicAndEffects;
using UnityEngine;

Expand Down
60 changes: 60 additions & 0 deletions Scripts/Effects/SummonAncientLichEffect.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using ChebsNecromancyMod.MinionSpawners;
using DaggerfallConnect;
using DaggerfallWorkshop.Game;
using DaggerfallWorkshop.Game.Items;
using DaggerfallWorkshop.Game.MagicAndEffects;
using UnityEngine;

Expand Down Expand Up @@ -37,12 +39,70 @@ public override void SetProperties()
properties.MagnitudeCosts = MakeEffectCosts(MagnitudeCostA, MagnitudeCostB, MagnitudeCostOffset);
}

public override bool ChanceSuccess => base.ChanceSuccess && (!ChebsNecromancy.CorpseItemEnabled || HasReagents());

protected bool HasReagents()
{
if (caster == null)
{
ChebsNecromancy.ChebError("HasReagents: caster is null");
return false;
}

var corpseItem = caster.Entity.Items
.GetItem(CustomCorpseItem.TemplateItemGroup, CustomCorpseItem.TemplateIndex);
if (corpseItem == null)
{
DaggerfallUI.AddHUDText("No corpse item available.");
return false;
}

var lichDust = caster.Entity.Items
.GetItem(ItemGroups.CreatureIngredients1, (int)CreatureIngredients1.Lich_dust);
if (lichDust == null)
{
DaggerfallUI.AddHUDText("No lich dust available.");
return false;
}

return true;
}

protected void ConsumeReagents()
{
if (caster == null)
{
ChebsNecromancy.ChebError("ConsumeReagents: caster is null");
return;
}

var corpseItem = caster.Entity.Items
.GetItem(CustomCorpseItem.TemplateItemGroup, CustomCorpseItem.TemplateIndex);
if (corpseItem == null)
{
ChebsNecromancy.ChebError("Failed to consume reagents: corpseItem is null");
return;
}
caster.Entity.Items.RemoveOne(corpseItem);

var lichDust = caster.Entity.Items
.GetItem(ItemGroups.CreatureIngredients1, (int)CreatureIngredients1.Lich_dust);
if (lichDust == null)
{
ChebsNecromancy.ChebError("Failed to consume reagents: lichDust is null");
return;
}
caster.Entity.Items.RemoveOne(lichDust);
}

protected override void DoEffect()
{
base.DoEffect();

Spawn(GetMagnitude(), caster.Entity.Skills.GetLiveSkillValue(DFCareer.Skills.Mysticism),
caster.Entity.Stats.LiveIntelligence, caster.Entity.Stats.LiveWillpower, true);

ConsumeReagents();
}

public static void Spawn(int magnitude, int mysticismLevel, int intelligence, int willpower, bool showHUDMessage)
Expand Down
Loading

0 comments on commit cfe145b

Please sign in to comment.