diff --git a/Defs/CustomDefs/TM_CustomClassDef.xml b/Defs/CustomDefs/TM_CustomClassDef.xml index df0ff495..c5eabf22 100644 --- a/Defs/CustomDefs/TM_CustomClassDef.xml +++ b/Defs/CustomDefs/TM_CustomClassDef.xml @@ -39,12 +39,17 @@ + + +
  • TM_C_Firebolt
  • + +
    true true - - + + false false @@ -54,10 +59,32 @@ - + + TM_C_Firebolt + 2 + 1 + +
  • TM_Firebolt
  • +
  • TM_Firestorm
  • +
    + +
  • TM_Custom_detailed_upgrade
  • +
  • TM_Custom_minimal_upgrade
  • +
    +
    - + + TM_Firebolt_pwr + Custom upgrade + description of this def + Description + 2 + 2 + + + TM_Custom_minimal_upgrade + - =============--> - \ No newline at end of file + diff --git a/Defs/CustomDefs/TM_FieldMedic.xml b/Defs/CustomDefs/TM_FieldMedic.xml new file mode 100644 index 00000000..961ff652 --- /dev/null +++ b/Defs/CustomDefs/TM_FieldMedic.xml @@ -0,0 +1,437 @@ + + + + + + + TM_Power_Emergency + 1 + +
  • TM_Emergency
  • +
    +
    + + + TM_Emergency + + UI/Emergency + The Field Medic rushes to aid their patients. + + The Field Medic gains increased base movement speed, tending speed and tending quality as well as reduced pain for a 30 seconds. + + TM_Stamina + 30 + + true + .08 + 1 + true + + TorannMagic.Verb_SB + false + false + false + false + true + TM_Emergency + false + Projectile_Default + 0 + 60.0 + 0 + TM_VibrationLow + 1 + false + TargetSelf + + true + false + false + false + + +
  • + TM_EmergencyHD + 1 + 0.030 +
  • +
    +
    +
    + + + TM_EmergencyHD + HediffWithComps + + I'll do my best. + (0.24,0.8,0.24) + true + 1.0 + false + +
  • + -1 +
  • +
    + +
  • + + .001 + -0.2 + + 0.4 + 0.1 + 0.5 + +
  • +
    +
    + + + + TM_Power_MedicalSupply + 1 + +
  • TM_MedicalSupply
  • +
    +
    + + TM_Upgrade_MedicalSupply_capacity + + TM_MedicalSupplyHD_capacity + Capacity + Increases the maximum amount of supply carried by the Field Medic + 1 + 7 + + + + TM_MedicalSupply + + UI/bloodgift + Resupply the Field Medic's medical supplies. + +The Field Medic will use one item from target stockpile and convert it into personal medical supplies he can use to fuel his abilities. + +The amount of supply gained is equal to 20 times the medical potency of the item squared: +medicine - 20 medical supplies +herbal medicine - 4.9 medical supplies +glitterworld medicine - 51.2 medical supplies + +The Field Medic can train to convert his equipment back into common medicine + 0 + true + 1 + TM_MedicalSupplyHD + false + + TorannMagic.Verb_MedicalSupply + false + false + false + false + true + TM_MedicalSupply + false + Projectile_Default + 8 + 1.0 + 2 + TM_VibrationLow + 10 + false + + false + false + false + true + + + + + TM_MedicalSupplyHD + HediffWithComps + + Accounts for the medical supplies equipped by a Field Medic. + (0.24,0.8,0.24) + false + false + false + false + 0.001 + false + 20 + +
  • + 60.0 + 10.0 + TM_MedicalSupplyHD_capacity + 0.0 + 0.0 + medical supplies are not meant to regen +
  • +
    + +
  • + + 0 + true +
  • +
    +
    + + + + TM_Power_Medigel + 1 + +
  • TM_Medigel
  • +
    +
    + + + TorannMagic.CustomAbility + TM_Medigel + + UI/Medigel + Applies a healing gel to the wounds of a target + +The Field Medic applies a gel on the wounds of a target, healing them over time. + + TM_MedicalSupplyHD + 20 + + true + 1 + false + + TorannMagic.Verb_SB + false + false + false + false + true + TM_Medigel + false + Projectile_Default + 3 + 1.0 + 1 + true + + 1 + false + TargetThing + + true + false + true + false + + +
  • + TM_Regeneration + 1 +
  • +
    +
    +
    + + + + TM_Power_CombatDrugs + 1 + +
  • TM_CombatDrugs
  • +
    + +
  • TM_Upgrade_CombatDrugs_amount
  • +
  • TM_Upgrade_CombatDrugs_duration
  • +
    +
    + + TM_Upgrade_CombatDrugs_duration + Potency + Increases the number of effects applied. + +level 0: 2 effects +level 1: 3 effects +level 2: all 4 effects + 2 + 2 + + + TM_Upgrade_CombatDrugs_amount + Stabilisation + Increases the duration of effects applied. + +Lasts 120 seconds plus 20 seconds per upgrade. + 1 + 6 + + + + TorannMagic.CustomAbility + TM_CombatDrugs + + UI/bloodgift + Gives combat drugs to an ally + +The Field Medic throws a syringe of improvised combat drugs to an ally. +The effects are always positive but may vary. + + TM_MedicalSupplyHD + 10 + + true + 1 + false + + TorannMagic.Verb_ApplyRandomHediffFromList + +
  • TM_CombatDrugsPain_HD
  • +
  • TM_CombatDrugsMoving_HD
  • +
  • TM_CombatDrugsAwareness_HD
  • +
  • TM_CombatDrugsAccuracy_HD
  • +
    + 2 + TM_Upgrade_CombatDrugs_amount + 0.12 + + TM_Upgrade_CombatDrugs_amount + 0.02 + true + false + false + false + false + true + TM_CombatDrugs + false + Projectile_Default + 3 + 1.0 + 10 + true + + 20 + false + TargetThing + + true + false + true + false + +
    +
    + + + TM_CombatDrugsPain_HD + HediffWithComps + + Combat drugs reducing pain + (0.24,0.8,0.24) + true + 1.0 + false + +
  • + -1 +
  • +
    + +
  • + + .001 + 0.2 +
  • +
    +
    + + TM_CombatDrugsMoving_HD + HediffWithComps + + Combat drugs enhancing movement + (0.24,0.8,0.24) + true + 1.0 + false + +
  • + -1 +
  • +
    + +
  • + + .001 + +
  • + Moving + .2 +
  • + + + 0.5 + + +
    +
    + + TM_CombatDrugsAwareness_HD + HediffWithComps + + Combat drugs enhancing awareness + (0.24,0.8,0.24) + true + 1.0 + false + +
  • + -1 +
  • +
    + +
  • + + .001 + +
  • + Sight + .2 +
  • + + + +5 + + +
    +
    + + TM_CombatDrugsAccuracy_HD + HediffWithComps + + Combat drugs enhancing accuracy + (0.24,0.8,0.24) + true + 1.0 + false + +
  • + -1 +
  • +
    + +
  • + + .001 + + +5 + +5 + +
  • +
    +
    + +
    diff --git a/Source/TMagic/TMagic/BasePower.cs b/Source/TMagic/TMagic/BasePower.cs new file mode 100644 index 00000000..b5a9b49b --- /dev/null +++ b/Source/TMagic/TMagic/BasePower.cs @@ -0,0 +1,84 @@ +using AbilityUser; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; + +namespace TorannMagic +{ + public abstract class BasePower : IExposable + { + private List abilityDefs; + private List upgrades; + + public int ticksUntilNextCast = -1; + + public int level; + + public int learnCost = 2; + public int upgradeCost = 1; + public int maxLevel = 3; + + public bool learned = true; + public bool autocast = false; + protected int interactionTick = 0; + + public bool AutoCast + { + get => autocast; + set + { + if (interactionTick < Find.TickManager.TicksGame) + { + autocast = value; + interactionTick = Find.TickManager.TicksGame + 30; + } + } + } + + public AbilityDef NextLevelAbilityDef + { + get => level + 1 < this.Abilities.Count ? Abilities[level + 1] : Abilities.Last(); + } + + public AbilityDef AbilityDef + { + get => level < this.Abilities.Count ? Abilities[level] : Abilities.Last(); + } + + public List Abilities + { + get => abilityDefs; + protected set => abilityDefs = value; + } + + public List Upgrades + { + get => upgrades; + protected set => upgrades = value; + } + + public Texture2D Icon + { + get => this.AbilityDef.uiIcon; + } + + public BasePower() + { + abilityDefs = new List(); + upgrades = new List(); + } + + public virtual void ExposeData() + { + Scribe_Values.Look(ref this.learned, "learned", true, false); + Scribe_Values.Look(ref this.autocast, "autocast", false, false); + Scribe_Values.Look(ref this.learnCost, "learnCost", 2, false); + Scribe_Values.Look(ref this.level, "level", 0, false); + Scribe_Values.Look(ref this.maxLevel, "maxLevel", 3, false); + Scribe_Values.Look(ref this.ticksUntilNextCast, "ticksUntilNextCast", -1, false); + Scribe_Collections.Look(ref this.abilityDefs, "abilityDefs", LookMode.Def, null); + Scribe_Collections.Look(ref this.upgrades, "upgrades", LookMode.Value, null); + } + } +} diff --git a/Source/TMagic/TMagic/CompAbilityUserCustom.cs b/Source/TMagic/TMagic/CompAbilityUserCustom.cs new file mode 100644 index 00000000..f354cb33 --- /dev/null +++ b/Source/TMagic/TMagic/CompAbilityUserCustom.cs @@ -0,0 +1,463 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using RimWorld; +using UnityEngine; +using AbilityUser; +using Verse; +using Verse.AI; +using Verse.Sound; +using AbilityUserAI; + +namespace TorannMagic +{ + [CompilerGenerated] + [Serializable] + [StaticConstructorOnStartup] + public class CompAbilityUserCustom : CompAbilityUser + { + public string LabelKey = "TM_Custom"; + + public int customIndex = -2; + public TMDefs.TM_CustomClass customClass = null; + + public bool temporaryPowersForNonColonists = true; + + private int autocastTick = 0; + private int nextAICastAttemptTick = 0; + public float weaponDamage = 1; + + + private CustomData data = null; + public CustomData Data + { + get + { + bool flag = this.data == null; + if (flag) + { + this.data = new CustomData(customClass?.customPowers?.ConvertAll((PowerDef def) => new CustomPower(def))); + } + return this.data; + } + } + + public override void PostDeSpawn(Map map) + { + base.PostDeSpawn(map); + } + + public override void PostDraw() + { + if (IsInitialized) + { + ModOptions.SettingsRef settingsRef = new ModOptions.SettingsRef(); + if (settingsRef.AIFriendlyMarking && base.AbilityUser.IsColonist) + { + //DrawMageMark(); + } + if (settingsRef.AIMarking && !base.AbilityUser.IsColonist) + { + //DrawMageMark(); + } + + //TODO: draw ability-specific things like TechnoBit, DemonScorn wings and MageLight ? + } + + base.PostDraw(); + } + + public override void CompTick() + { + base.CompTick(); + bool flag = base.AbilityUser != null; + if (flag && IsInitialized) + { + bool flag4 = Find.TickManager.TicksGame % 30 == 0; + if (flag4) + { + bool flag5 = this.Data.UserXP > this.Data.XPForLevel(Data.UserLevel + 1); + if (flag5) + { + this.LevelUp(); + } + if (Find.TickManager.TicksGame % 60 == 0) + { + if (this.Pawn.IsColonist && this.temporaryPowersForNonColonists) + { + ResolveFactionChange(); + } + } + } + bool spawned = base.AbilityUser.Spawned; + if (spawned) + { + if (Find.TickManager.TicksGame % 60 == 0) + { + if (this.Pawn.IsColonist) + { + // TODO: resolve sustained abilities such as power nodes, minions, time mark, etc... + } + //TODO: update resources like Mana or Blood + + } + ModOptions.SettingsRef settingsRef = new ModOptions.SettingsRef(); + if (this.autocastTick < Find.TickManager.TicksGame) //180 default + { + //TODO: check for autocast + this.autocastTick = Find.TickManager.TicksGame + (int)Rand.Range(.8f * settingsRef.autocastEvaluationFrequency, 1.2f * settingsRef.autocastEvaluationFrequency); + } + if (!this.Pawn.IsColonist && settingsRef.AICasting && settingsRef.AIAggressiveCasting && Find.TickManager.TicksGame > this.nextAICastAttemptTick) //Aggressive AI Casting + { + this.nextAICastAttemptTick = Find.TickManager.TicksGame + Rand.Range(300, 500); + if (this.Pawn.jobs != null && this.Pawn.CurJobDef != TorannMagicDefOf.TMCastAbilitySelf && this.Pawn.CurJobDef != TorannMagicDefOf.TMCastAbilityVerb) + { + ResolveEnnemyAutocast(); + } + } + //TODO: resolve passive abilities like TechnoOverdrive, Warlock Empathy and Succubus Lovin + if (Find.TickManager.TicksGame % 299 == 0) //cache weapon damage for tooltip and damage calculations + { + this.weaponDamage = TM_Calc.GetSkillDamage(this.Pawn); + } + } + else + { + // update cooldowns while not on a map + if (Find.TickManager.TicksGame % 600 == 0) + { + if (this.Pawn.Map == null) + { + if (AbilityData?.AllPowers != null) + { + foreach (PawnAbility power in AbilityData.AllPowers) + { + int cd = power.CooldownTicksLeft; + cd = cd < 600 ? cd : 600; + power.CooldownTicksLeft -= cd; + } + } + } + } + } + } + } + + public override void PostSpawnSetup(bool respawningAfterLoad) + { + base.PostSpawnSetup(respawningAfterLoad); + bool flag = base.AbilityUser != null; + //Log.Message("Post Spawn Setup for " + this.AbilityUser?.Name + " init status:" + IsInitialized); + if (flag && IsInitialized) + { + this.ResolveInspectorTab(); + bool spawned = base.AbilityUser.Spawned; + if (spawned) + { + bool flag2 = base.AbilityUser.story != null; + if (flag2) + { + this.ResolveOngoingAbilities(); + } + } + } + } + + public override bool TryTransformPawn() + { + return TM_ClassUtility.IsCustomClassIndex(this.AbilityUser.story.traits.allTraits) >= 0; + } + + public override void Initialize() + { + //Log.Message("Initializing " + this.AbilityUser.Name); + if (this.customClass == null) + { + this.customIndex = TM_ClassUtility.IsCustomClassIndex(this.AbilityUser.story.traits.allTraits); + //Log.Message("Initializing " + this.AbilityUser.Name + " with class " + this.customIndex); + if (this.customIndex >= 0) + { + this.customClass = TM_ClassUtility.CustomClasses()[this.customIndex]; + //Log.Message("This Custom class has " + customClass.customPowers.Count + " powers " + this.customIndex); + this.data = new CustomData(customClass.customPowers?.ConvertAll((PowerDef def) => new CustomPower(def))); + base.Initialize(); + } + //else + //{ + // this.parent.AllComps.Remove(this); + //} + } + } + + public override void PostInitialize() + { + base.PostInitialize(); + this.temporaryPowersForNonColonists = !this.Pawn.IsColonist; + AssignAbilities(temporaryPowersForNonColonists); // non-colonists have access to all starting abilities. + ResolveInspectorTab(); + } + + private void ResolveOngoingAbilities() + { + // TODO: resolve ongoing abilities that have fleeting data, such as druid's Fertile Lands + } + + public void DrawMageMark() + { + if (this.customClass != null) + { + Material mat = TM_RenderQueue.mageMarkMat; + if (this.customClass.classIconPath != "") + { + mat = MaterialPool.MatFrom("Other/" + this.customClass.classIconPath.ToString()); + } + if (this.customClass.classIconColor != null) + { + mat.color = this.customClass.classIconColor; + } + float num = Mathf.Lerp(1.2f, 1.55f, 1f); + Vector3 vector = this.AbilityUser.Drawer.DrawPos; + vector.x = vector.x + .45f; + vector.z = vector.z + .45f; + vector.y = Altitudes.AltitudeFor(AltitudeLayer.MoteOverhead); + float angle = 0f; + Vector3 s = new Vector3(.28f, 1f, .28f); + Matrix4x4 matrix = default(Matrix4x4); + matrix.SetTRS(vector, Quaternion.AngleAxis(angle, Vector3.up), s); + Graphics.DrawMesh(MeshPool.plane10, matrix, mat, 0); + } + } + + public void ResolveFactionChange() + { + if (temporaryPowersForNonColonists) + { + RemovePowers(); + AssignAbilities(); + this.temporaryPowersForNonColonists = false; + } + } + + public void LevelUp(bool hideNotification = false) + { + if (!(this.Pawn.story.traits.HasTrait(TorannMagicDefOf.Faceless) || this.Pawn.story.traits.HasTrait(TorannMagicDefOf.TM_Wayfarer))) + { + if (this.Data.UserLevel < 150)//customClass.maxLevel) + { + this.Data.UserLevel++; + bool flag = !hideNotification; + if (flag) + { + ModOptions.SettingsRef settingsRef = new ModOptions.SettingsRef(); + if (Pawn.IsColonist && settingsRef.showLevelUpMessage) + { + Messages.Message( this.Pawn.Name + " increased his skill level in " + this.customClass.classTrait.label, //TranslatorFormattedStringExtensions.Translate("TM_MagicLevelUp",this.parent.Label), + this.Pawn, MessageTypeDefOf.PositiveEvent); + } + } + } + else + { + this.Data.UserXP = this.Data.XPForLevel(this.Data.UserLevel); + } + } + } + + public void LevelUpPower(BasePower power) + { + int savedTicks = this.AbilityData.Powers.Find((PawnAbility a) => a.Def == power.AbilityDef)?.CooldownTicksLeft ?? -1; + base.RemovePawnAbility(power.AbilityDef); + power.level++; + //if ((power.AbilityDef as TMAbilityDef)?.shouldInitialize ?? false) + //{ //TODO: handle passive upgrades. shouldInitialize is not reliable because upgraded spells have it set to false, it is used as "isInitialAbility". + base.AddPawnAbility(power.AbilityDef, true, savedTicks); + Log.Message(power.AbilityDef.defName); + //} + } + + private void ResolveEnnemyAutocast() + { + IEnumerable enumerable = this.Pawn.EligibleAIProfiles(); + if (enumerable != null && enumerable.Count() > 0) + { + foreach (AbilityUserAIProfileDef item in enumerable) + { + if (item != null) + { + AbilityAIDef useThisAbility = null; + if (item.decisionTree != null) + { + useThisAbility = item.decisionTree.RecursivelyGetAbility(this.Pawn); + } + if (useThisAbility != null) + { + ThingComp val = this.Pawn.AllComps.First((ThingComp comp) => ((object)comp).GetType() == item.compAbilityUserClass); + CompAbilityUser compAbilityUser = val as CompAbilityUser; + if (compAbilityUser != null) + { + PawnAbility pawnAbility = compAbilityUser.AbilityData.AllPowers.First((PawnAbility ability) => ability.Def == useThisAbility.ability); + string reason = ""; + if (pawnAbility.CanCastPowerCheck(AbilityContext.AI, out reason)) + { + LocalTargetInfo target = useThisAbility.Worker.TargetAbilityFor(useThisAbility, this.Pawn); + if (target.IsValid) + { + pawnAbility.UseAbility(AbilityContext.Player, target); + } + } + } + } + } + } + } + } + + private void AssignAbilities(bool learnAll = false) + { + Pawn abilityUser = base.AbilityUser; + for (int z = 0; z < this.Data.Powers.Count; z++) + { + BasePower p = this.Data.Powers[z]; + if (p.AbilityDef is TMAbilityDef) + { + TMAbilityDef ability = (TMAbilityDef)p.AbilityDef; + // TODO: learning method : scrolls, chance, Inspiration? + if (!(learnAll || !abilityUser.health.hediffSet.HasHediff(TorannMagicDefOf.TM_Uncertainty, false) || Rand.Chance(ability.learnChance))) + { + p.learned = false; + } + + if (p.learned)// && ability.shouldInitialize) + { //TODO: handle passive abilities. shouldInitialize is not reliable because upgraded spells have it set to false, it is used as "isInitialAbility". + this.AddPawnAbility(ability); + } + } + } + if (this.customClass.classHediff != null) + { + HealthUtility.AdjustSeverity(abilityUser, this.customClass.classHediff, this.customClass.hediffSeverity); + } + } + + public void RemovePowers() + { + for (int i = 0; i < this.Data.Powers.Count; i++) + { + BasePower mp = this.Data.Powers[i]; + for (int j = 0; j < mp.Abilities.Count; j++) + { + this.RemovePawnAbility(mp.Abilities[j]); + } + mp.learned = false; + } + } + + public void ResetSkills() + { + int skillpoints = this.Data.Upgrades.Values.Sum((MagicPowerSkill x) => x.level * x.costToLevel); + + int magicPts = this.Data.AbilityPoints; + + this.data.ResetAllSkills(); + + this.Data.AbilityPoints = magicPts + skillpoints; + } + + public void RemoveTraits() + { + List traits = this.AbilityUser.story.traits.allTraits; + for (int i = 0; i < traits.Count; i++) + { + if (this.customClass != null) + { + traits.Remove(this.AbilityUser.story.traits.GetTrait(this.customClass.classTrait)); + this.customClass = null; + this.customIndex = -2; + } + } + } + + public void RemoveTMagicHediffs() + { + // TODO + List allHediffs = this.Pawn.health.hediffSet.GetHediffs().ToList(); + for (int i = 0; i < allHediffs.Count(); i++) + { + Hediff hediff = allHediffs[i]; + if (hediff.def.defName.StartsWith("TM_")) + { + this.Pawn.health.RemoveHediff(hediff); + } + + } + } + + public void RemoveAbilityUser() + { + this.RemovePowers(); + this.RemoveTMagicHediffs(); + this.RemoveTraits(); + this.data = new CustomData(); + base.IsInitialized = false; + } + + //public override List IgnoredHediffs() + //{ + // return new List + // { + // TorannMagicDefOf.TM_MightUserHD, + // TorannMagicDefOf.TM_MagicUserHD + // }; + //} + + public override void PostPreApplyDamage(DamageInfo dinfo, out bool absorbed) + { + //TODO: move the damage absorbtion code for each abilities in their own hediff + base.PostPreApplyDamage(dinfo, out absorbed); + } + + public void ResolveInspectorTab() + { + InspectTabBase inspectTabsx = base.AbilityUser.GetInspectTabs().FirstOrDefault((InspectTabBase x) => x.labelKey == "TM_TabCustom"); + IEnumerable inspectTabs = base.AbilityUser.GetInspectTabs(); + bool flag = inspectTabs != null && inspectTabs.Count() > 0; + if (flag) + { + if (inspectTabsx == null) + { + try + { + base.AbilityUser.def.inspectorTabsResolved.Add(InspectTabManager.GetSharedInstance(typeof(ITab_Pawn_Custom))); + } + catch (Exception ex) + { + Log.Error(string.Concat(new object[] + { + "Could not instantiate inspector tab of type ", + typeof(ITab_Pawn_Custom), + ": ", + ex + })); + } + } + } + } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref this.temporaryPowersForNonColonists, "temporaryPowersForNonColonists", true, false); + Scribe_Deep.Look(ref this.data, "customClassData", new object[] { }); + bool flag11 = Scribe.mode == LoadSaveMode.PostLoadInit; + if (flag11) + { + if (this.customIndex >= 0) + { + this.customClass = TM_ClassUtility.CustomClasses()[this.customIndex]; + } + } + } + + } +} diff --git a/Source/TMagic/TMagic/CustomAbility.cs b/Source/TMagic/TMagic/CustomAbility.cs new file mode 100644 index 00000000..e7e66ba7 --- /dev/null +++ b/Source/TMagic/TMagic/CustomAbility.cs @@ -0,0 +1,213 @@ +using AbilityUser; +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Verse; +using UnityEngine; + +namespace TorannMagic +{ + public class CustomAbility : PawnAbility + { + + public TMAbilityDef TMDef + { + get + { + return base.Def as TMAbilityDef; + } + } + TMAbilityCost cost; + TMAbilityCost Cost + { + get + { + if (cost == null) // TODO: handle when the Cost cannot be created + { + cost = TMDef.customCost?.ForPawn(this.Pawn) + ?? (TMDef.manaCost > 0 ? new TMAbilityNeedCost(this.Pawn.needs.TryGetNeed(TorannMagicDefOf.TM_Mana), TMDef.manaCost * 100f) + : (TMDef.staminaCost > 0 ? new TMAbilityNeedCost(this.Pawn.needs.TryGetNeed(TorannMagicDefOf.TM_Stamina), TMDef.staminaCost * 100f) + : new TMAbilityBadCost() as TMAbilityCost)); + } + return cost; + } + } + public CustomAbility() + { + } + + public CustomAbility(AbilityData data) : base(data) + { + } + + public CustomAbility(CompAbilityUser abilityUser) : base(abilityUser) + { + this.abilityUser = (abilityUser as CompAbilityUserMagic); + } + + public CustomAbility(Pawn user, AbilityUser.AbilityDef pdef) : base(user, pdef) + { + this.cost = (pdef as TMAbilityDef)?.customCost.ForPawn(user); + } + + public override void PostAbilityAttempt() //commented out in CompAbilityUserMagic + { + base.PostAbilityAttempt(); + + ModOptions.SettingsRef settingsRef = new ModOptions.SettingsRef(); + if (!this.Pawn.IsColonist && settingsRef.AIAggressiveCasting)// for AI + { + this.CooldownTicksLeft = Mathf.RoundToInt(this.MaxCastingTicks/2f); + } + else + { + this.CooldownTicksLeft = Mathf.RoundToInt(this.MaxCastingTicks); //TODO: integrate cooldown reduction effects, Arcalleum + } + float cost; + Cost.PayCost(out cost); + CompAbilityUserCustom custom = this.AbilityUser.Pawn.TryGetComp(); + if (custom != null && custom.Data.ReturnMatchingPower(this.Def, true)?.learned == true) + { + custom.Data.UserXP += Mathf.RoundToInt(cost * settingsRef.xpMultiplier); // TODO: XP modifiers (custom.Data.GainXP ? ) + } + } + + public override string PostAbilityVerbCompDesc(VerbProperties_Ability verbDef) + { + return "Costs " + Cost.Description; + //CompAbilityUserCustom custom = this.AbilityUser.Pawn.TryGetComp(); + //BasePower power = custom.Data.ReturnMatchingPower(Def); + //StringBuilder stringBuilder = new StringBuilder(); + //TMAbilityDef magicAbilityDef = (TMAbilityDef)verbDef.abilityDef; + //bool flag = magicAbilityDef != null; + //if (flag) + //{ + // string text = ""; + // string text2 = ""; + // string text3 = ""; + // float num = 0; + // float num2 = 0; + //if (magicAbilityDef == TorannMagicDefOf.TM_Teleport) + //{ + // num = this.MagicUser.ActualManaCost(magicDef)*100; + // MagicPowerSkill mps2 = this.MagicUser.MagicData.MagicPowerSkill_Teleport.FirstOrDefault((MagicPowerSkill x) => x.label == "TM_Teleport_ver"); + // MagicPowerSkill mps1 = this.MagicUser.MagicData.MagicPowerSkill_Teleport.FirstOrDefault((MagicPowerSkill x) => x.label == "TM_Teleport_pwr"); + // num2 = 80 + (mps1.level * 20) + (mps2.level * 20); + // text2 = "TM_AbilityDescPortalTime".Translate( + // num2.ToString() + // ); + //} + //else + //{ + // num = this.MagicUser.ActualManaCost(magicDef) * 100; + //} + + //if (this.Pawn.story.traits.HasTrait(TorannMagicDefOf.Faceless)) + //{ + // text = "TM_AbilityDescBaseStaminaCost".Translate( + // (magicAbilityDef.manaCost * 100).ToString("n1") + // ) + "\n" + "TM_AbilityDescAdjustedStaminaCost".Translate( + // (magicDef.manaCost * 100).ToString("n1") + // ); + //} + //else + //{ + // text = "TM_AbilityDescBaseManaCost".Translate( + // (magicAbilityDef.manaCost * 100).ToString("n1") + // ) + "\n" + "TM_AbilityDescAdjustedManaCost".Translate( + // num.ToString("n1") + // ); + //} + //if(this.MagicUser.coolDown != 1f) + //{ + // text3 = "TM_AdjustedCooldown".Translate( + // ((this.MaxCastingTicks * this.MagicUser.coolDown) / 60).ToString("0.00") + // ); + //} + //bool flag4 = text3 != ""; + //if(flag4) + //{ + // stringBuilder.AppendLine(text3); + //} + //} + //return stringBuilder.ToString(); + } + + public override bool CanCastPowerCheck(AbilityContext context, out string reason) + { + bool flag = base.CanCastPowerCheck(context, out reason); + bool result; + if (flag) + { + reason = ""; + TMAbilityDef tmAbilityDef; + bool flag1 = base.Def != null && (tmAbilityDef = (base.Def as TMAbilityDef)) != null; + if (flag1) + { + float actualCost; + if (Cost.CanPayCost(out actualCost)) + { + bool flagMute = this.Pawn.health.hediffSet.HasHediff(TorannMagicDefOf.TM_MuteHD); + if(flagMute) + { + reason = "TM_CasterMute".Translate( + base.Pawn.LabelShort + ); + result = false; + return result; + } + } + else if (this.Pawn.story.traits.HasTrait(TorannMagicDefOf.Faceless)) + { + CompAbilityUserMight mightComp = this.Pawn.GetComp(); + bool flag7 = mightComp != null && mightComp.Stamina != null && actualCost < mightComp.Stamina.CurLevel; + if (flag7) + { + reason = "TM_NotEnoughStamina".Translate( + base.Pawn.LabelShort + ); + result = false; + return result; + } + } + else + { + reason = "Cannot pay " + Cost.Description; + return false; + } + } + //List wornApparel = base.Pawn.apparel.WornApparel; + //for (int i = 0; i < wornApparel.Count; i++) + //{ + // if (!wornApparel[i].AllowVerbCast(base.Pawn.Position, base.Pawn.Map, base.abilityUser.Pawn.TargetCurrentlyAimingAt, this.Verb) && + // (this.magicDef.defName == "TM_LightningCloud" || this.magicDef.defName == "Laser_LightningBolt" || this.magicDef.defName == "TM_LightningStorm" || this.magicDef.defName == "TM_EyeOfTheStorm" || + // this.magicDef.defName.Contains("Laser_FrostRay") || this.magicDef.defName == "TM_Blizzard" || this.magicDef.defName == "TM_Snowball" || this.magicDef.defName == "TM_Icebolt" || + // this.magicDef.defName == "TM_Firestorm" || this.magicDef.defName == "TM_Fireball" || this.magicDef.defName == "TM_Fireclaw" || this.magicDef.defName == "TM_Firebolt" || + // this.magicDef.defName.Contains("TM_MagicMissile") || + // this.magicDef.defName.Contains("TM_DeathBolt") || + // this.magicDef.defName.Contains("TM_ShadowBolt") || + // this.magicDef.defName == "TM_BloodForBlood" || this.magicDef.defName == "TM_IgniteBlood" || + // this.magicDef.defName == "TM_Poison" || + // this.magicDef == TorannMagicDefOf.TM_ArcaneBolt) ) + // { + // reason = "TM_ShieldBlockingPowers".Translate( + // base.Pawn.Label, + // wornApparel[i].Label + // ); + // return false; + // } + //} + result = true; + + } + else + { + result = false; + } + return result; + + } + } +} diff --git a/Source/TMagic/TMagic/CustomData.cs b/Source/TMagic/TMagic/CustomData.cs new file mode 100644 index 00000000..4c4dc82b --- /dev/null +++ b/Source/TMagic/TMagic/CustomData.cs @@ -0,0 +1,153 @@ +using AbilityUser; +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using Verse; + +namespace TorannMagic +{ + public class CustomData : IExposable + { + private int userLevel = 0; + private int abilityPoints = 0; + private int userXP = 1; + private int ticksToLearnXP = -1; + private int ticksAffiliation = 0; + + private List powers; + private Dictionary upgrades; + + public List Powers + { + get => this.powers.ConvertAll((CustomPower p) => p as BasePower); + } + + public Dictionary Upgrades + { + get => upgrades; + } + + public int UserLevel + { + get => this.userLevel; + set + { + if (value > this.userLevel) + { + this.AbilityPoints += value - this.userLevel; + } + int minXp = XPForLevel(value); + if (this.userXP < minXp) + { + this.userXP = XPForLevel(value); + } + this.userLevel = value; + } + } + + public int UserXP + { + get => this.userXP; + set => this.userXP = value; + } + + public virtual int XPForLevel(int level) + { + return level * 500; + } + + public int AbilityPoints + { + get => this.abilityPoints; + set => this.abilityPoints = value; + } + + public int TicksToLearnXP + { + get => this.ticksToLearnXP; + set => this.ticksToLearnXP = value; + } + + public int TicksAffiliation + { + get => this.ticksAffiliation; + set => this.ticksAffiliation = value; + } + + public int GetUniquePowersWithSkillsCount() + { + return this.powers.Count; + } + + public int GetUpgradeLevel(string upgradeLabel) + { + return Upgrades.TryGetValue(upgradeLabel)?.level ?? 0; //FIXME: when not found should we return -1 ? + } + + public BasePower ReturnMatchingPower(AbilityUser.AbilityDef def, bool isCurrentAbility = false) + { + if (def == TorannMagicDefOf.TM_SoulBond || def == TorannMagicDefOf.TM_ShadowBolt || def == TorannMagicDefOf.TM_Dominate) + { + return null; + } + return powers.Find((CustomPower p) => p.Abilities.Contains(def) && (!isCurrentAbility || p.AbilityDef == def)); + } + + public List AllPowersForChaosMage + { + get + { + List tmpList = new List(); + List list = new List(); + list.Clear(); + list.AddRange(this.powers.FindAll((CustomPower p) => + { + TMAbilityDef def = p.AbilityDef as TMAbilityDef; + return def != null && def.canCopy + && def.staminaCost < float.Epsilon && def.chiCost < float.Epsilon && def.bloodCost < float.Epsilon; + })); + return list.ConvertAll((CustomPower p) => p as BasePower); ; + } + } + + public void ResetAllSkills() + { + this.Upgrades.Values.ToList().ForEach((MagicPowerSkill m) => m.level = 0); + } + + public CustomData() + { + } + + public CustomData(List powers) + { + this.userLevel = 0; + this.userXP = 0; + this.abilityPoints = 0; + this.powers = powers ?? new List(); + this.upgrades = this.powers.SelectMany((CustomPower p) => + p.localDef.upgrades.ConvertAll((UpgradeDef u) => u.asMagicPowerSkill()) + ).ToDictionary((MagicPowerSkill s) => s.label); + //this.upgrades_labels = upgrades.Keys.ToList(); + //this.upgrades_values = upgrades.Values.ToList(); + Log.Message("Creating new Data with " + this.powers.Count + " powers and " + this.upgrades.Count + " upgrades"); + } + + public void ExposeData() + { + //Scribe_References.Look(ref this.pawn, "magicPawn", false); + Scribe_Values.Look(ref this.userLevel, "c_userLevel", 0, false); + Scribe_Values.Look(ref this.userXP, "c_userXP", 0, false); + Scribe_Values.Look(ref this.abilityPoints, "c_magicAbilityPoints", 0, false); + Scribe_Values.Look(ref this.ticksToLearnXP, "c_ticksToLearnMagicXP", -1, false); + Scribe_Values.Look(ref this.ticksAffiliation, "c_ticksAffiliation", -1, false); + Scribe_Collections.Look(ref this.powers, "c_powers", LookMode.Deep, new object[0]); + + //this.upgrades_labels = upgrades.Keys.ToList(); + //this.upgrades_values = upgrades.Values.ToList(); + Scribe_Collections.Look(ref this.upgrades, "c_upgrades", LookMode.Value, LookMode.Deep); + //LookMode.Value, LookMode.Deep, ref this.upgrades_labels, ref this.upgrades_values); + } + } +} diff --git a/Source/TMagic/TMagic/CustomPower.cs b/Source/TMagic/TMagic/CustomPower.cs new file mode 100644 index 00000000..b163429d --- /dev/null +++ b/Source/TMagic/TMagic/CustomPower.cs @@ -0,0 +1,78 @@ +using AbilityUser; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; + +namespace TorannMagic +{ + public class CustomPower : BasePower + { + public PowerDef localDef; + public CustomPower() + { + } + public CustomPower(PowerDef def) : base() + { + this.localDef = def; + updateBasePower(); + } + public override void ExposeData() + { + base.ExposeData(); + Scribe_Defs.Look(ref localDef, "powerDef"); + if (Scribe.mode == LoadSaveMode.PostLoadInit) + { + updateBasePower(); + } + } + private void updateBasePower() + { + this.Abilities = localDef.abilityLine.ConvertAll((TMAbilityDef a) => a as AbilityDef); + this.Upgrades = localDef.upgrades.ConvertAll((UpgradeDef u) => u.UpgradeId); + this.upgradeCost = localDef.upgradeCost; + this.learnCost = localDef.learnCost; + this.maxLevel = localDef.abilityLine.Count - 1; + } + } + + public class PowerDef : Def + { + public int upgradeCost = 1; + public int learnCost = 1; + public List abilityLine; + public List upgrades = new List(); + } + + public class UpgradeDef : Def + { + public string title = null; + public string upgradeId = null; + public int upgradeCost = 1; + public string upgradeDescription = null; + public int levelMax = 3; + + public string Title + { + get => title ?? this.label ?? upgradeId?? this.defName; + } + + public string UpgradeId + { + get => upgradeId ?? this.defName; + } + + public string Description + { + get => upgradeDescription ?? this.description ?? title ?? this.label ?? this.defName; + } + + public MagicPowerSkill asMagicPowerSkill() + { + MagicPowerSkill mps = new MagicPowerSkill(UpgradeId, Description); + mps.costToLevel = upgradeCost; + mps.levelMax = levelMax; + return mps; + } + } +} diff --git a/Source/TMagic/TMagic/HediffComp_AbilityResource.cs b/Source/TMagic/TMagic/HediffComp_AbilityResource.cs new file mode 100644 index 00000000..f2f60ebb --- /dev/null +++ b/Source/TMagic/TMagic/HediffComp_AbilityResource.cs @@ -0,0 +1,87 @@ +using RimWorld; +using Verse; +using UnityEngine; +using System.Collections.Generic; +using System; +using HarmonyLib; + +namespace TorannMagic +{ + public class HediffCompProperties_AbilityResource : HediffCompProperties + { + public float maximumBase = 100f; + public float maximumPerUpgrade = 0f; + public string maximumUpgradeName; + public float regenPerTickBase = 0f; + public float regenPerTickPerUpgrade = 0f; + public string regenPerTickUpgradeName; + + public HediffCompProperties_AbilityResource() + { + this.compClass = typeof(HediffComp_AbilityResource); + } + + } + + [StaticConstructorOnStartup] + public class HediffComp_AbilityResource : HediffComp + { + + private bool initialized = false; + private bool removeNow = false; + + private int eventFrequency = 60; + + private HediffCompProperties_AbilityResource Props { get => this.props as HediffCompProperties_AbilityResource; } + + private string maximumCachedUpgradeName; + private float maximumCached; + private string regenPerTickCachedUpgradeName; + private float regenPerTickCached; + + public override string CompLabelInBracketsExtra => string.Concat(this.parent.Severity.ToString("0."), "/", maximumCached.ToString("0.")); + public override bool CompShouldRemove => this.removeNow || base.CompShouldRemove; + + private void Initialize() + { + maximumCachedUpgradeName = Props.maximumUpgradeName ?? (this.parent.def.defName + "_capacity"); + regenPerTickCachedUpgradeName = Props.regenPerTickUpgradeName ?? (this.parent.def.defName + "_regen"); + UpdateCachedValues(); + this.initialized = true; + } + + private void UpdateCachedValues() + { + CustomData customData = this.Pawn.GetComp()?.Data; + if (customData != null) + { + maximumCached = Props.maximumBase + Props.maximumPerUpgrade * customData.GetUpgradeLevel(maximumCachedUpgradeName); + regenPerTickCached = Props.regenPerTickBase + Props.regenPerTickPerUpgrade * customData.GetUpgradeLevel(regenPerTickCachedUpgradeName); + } + } + + public override void CompPostTick(ref float severityAdjustment) + { + base.CompPostTick(ref severityAdjustment); + bool flag = base.Pawn != null && base.Pawn.Map != null; + if (flag) + { + if (!this.initialized) + { + Initialize(); + } + + if (Find.TickManager.TicksGame % (this.eventFrequency * 5) == 3) + { + this.UpdateCachedValues(); + } + + if (Find.TickManager.TicksGame % this.eventFrequency == 3) + { + float newValue = this.parent.Severity + eventFrequency * regenPerTickCached; + this.parent.Severity = Mathf.Clamp(newValue, 0f, maximumCached); + } + } + } + } +} diff --git a/Source/TMagic/TMagic/ITab_Pawn_Custom.cs b/Source/TMagic/TMagic/ITab_Pawn_Custom.cs new file mode 100644 index 00000000..cca8eb2c --- /dev/null +++ b/Source/TMagic/TMagic/ITab_Pawn_Custom.cs @@ -0,0 +1,382 @@ +using HarmonyLib; +using RimWorld; +using System; +using UnityEngine; +using Verse; +using System.Collections.Generic; +using System.Linq; + +namespace TorannMagic +{ + [StaticConstructorOnStartup] + public class ITab_Pawn_Custom : ITab //code by Jecrell + { + private static readonly Texture2D manaTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.0f, 0.5f, 1f)); + private Pawn PawnToShowInfoAbout + { + get + { + Pawn pawn = base.SelPawn ?? (base.SelThing as Corpse)?.InnerPawn; + bool flag3 = pawn == null; + if (pawn == null) + { + Log.Error("Character tab found no selected pawn to display."); + } + return pawn; + } + } + + public override bool IsVisible + { + get + { + bool flag = base.SelPawn.story != null && base.SelPawn.IsColonist; + if (flag) + { + CompAbilityUserCustom compMagic = base.SelPawn.TryGetComp(); + return compMagic?.IsInitialized ?? false; + } + return false; + } + } + + public ITab_Pawn_Custom() + { + this.size = MagicCardUtility.MagicCardSize + new Vector2(17f, 17f) * 2f; + this.labelKey = "TM_TabCustom"; + } + + protected override void FillTab() + { + Rect rect = new Rect(17f, 17f, MagicCardUtility.MagicCardSize.x, MagicCardUtility.MagicCardSize.y); + DrawMagicCard(rect, this.PawnToShowInfoAbout); + } + + public static void DrawMagicCard(Rect rect, Pawn pawn) + { + + CompAbilityUserCustom comp = pawn.GetComp(); + int sizeY = 0; + if (comp.customClass != null) + { + sizeY = (comp.Data.GetUniquePowersWithSkillsCount() * 80); + if (sizeY > 500) + { + sizeY -= 500; + } + else + { + sizeY = 0; + } + } + + Rect sRect = new Rect(rect.x, rect.y, rect.width - 36f, rect.height + 56f + sizeY); + //scrollPosition = GUI.BeginScrollView(rect, scrollPosition, sRect, false, true); + GUI.BeginScrollView(rect, Vector2.zero, sRect, false, true); + + bool flag = comp != null; + if (flag) + { + bool flag2 = true; //comp.MagicUserLevel > 0; + if (flag2) + { + float x = Text.CalcSize("TM_Header".Translate()).x; + Rect rect2 = new Rect(rect.width / 2f - (x / 2), rect.y, rect.width, MagicCardUtility.HeaderSize); + Text.Font = GameFont.Small; + Widgets.Label(rect2, "TM_Header".Translate().CapitalizeFirst()); + Text.Font = GameFont.Small; + Widgets.DrawLineHorizontal(rect.x - 10f, rect2.yMax, rect.width - 15f); + Rect rect9 = new Rect(rect.x, rect2.yMax + MagicCardUtility.Padding, rect2.width, MagicCardUtility.SkillsColumnHeight); + Rect inRect = new Rect(rect9.x, rect9.y + MagicCardUtility.Padding, MagicCardUtility.SkillsColumnDivider, MagicCardUtility.SkillsColumnHeight); + Rect inRect2 = new Rect(rect9.x + MagicCardUtility.SkillsColumnDivider, rect9.y + MagicCardUtility.Padding, rect9.width - MagicCardUtility.SkillsColumnDivider, MagicCardUtility.SkillsColumnHeight); + InfoPane(inRect, comp, pawn); + float x5 = Text.CalcSize("TM_Spells".Translate()).x; + Rect rect10 = new Rect(rect.width / 2f - x5 / 2f, rect9.yMax - 60f, rect.width, MagicCardUtility.HeaderSize); + Text.Font = GameFont.Small; + Widgets.Label(rect10, "TM_Spells".Translate().CapitalizeFirst()); + Text.Font = GameFont.Small; + Widgets.DrawLineHorizontal(rect.x - 10f, rect10.yMax, rect.width - 15f); + Rect rect11 = new Rect(rect.x, rect10.yMax + MagicCardUtility.SectionOffset, rect10.width, MagicCardUtility.PowersColumnHeight); + if (comp.customClass != null) + { + Rect inRect3 = new Rect(rect.x, rect11.y, MagicCardUtility.PowersColumnWidth, MagicCardUtility.PowersColumnHeight); + CustomPowersHandler(inRect3, comp, TexButton.TMTex_SkillPointUsed); + } + } + } + GUI.EndScrollView(); + //Widgets.EndScrollView(); + //GUI.EndGroup(); + } + + public static void InfoPane(Rect inRect, CompAbilityUserCustom compMagic, Pawn pawn) + { + Rect rect = new Rect(inRect.x, inRect.y, inRect.width * 0.7f, MagicCardUtility.TextSize); + Text.Font = GameFont.Tiny; + Widgets.Label(rect, "TM_Level".Translate().CapitalizeFirst() + ": " + compMagic.Data.UserLevel.ToString()); + Text.Font = GameFont.Tiny; + bool godMode = DebugSettings.godMode; + if (godMode) + { + Rect rect2 = new Rect(rect.xMax, inRect.y, inRect.width * 0.3f, MagicCardUtility.TextSize); + bool flag = Widgets.ButtonText(rect2, "+", true, false, true); + if (flag) + { + //compMagic.LevelUp(true); + compMagic.Data.UserXP += 400; // testing xp bar and levelling + } + if (false) + { + Rect rect22 = new Rect(rect.xMax + 60f, inRect.y, 50f, MagicCardUtility.TextSize * 2); + bool flag22 = Widgets.ButtonText(rect22, "Reset Skills", true, false, true); + if (flag22) + { + compMagic.ResetSkills(); + } + Rect rect23 = new Rect(rect.xMax + 115f, inRect.y, 50f, MagicCardUtility.TextSize * 2); + bool flag23 = Widgets.ButtonText(rect23, "Remove Powers", true, false, true); + if (flag23) + { + compMagic.RemoveAbilityUser(); + } + } + } + Rect rect4 = new Rect(inRect.x, rect.yMax, inRect.width, MagicCardUtility.TextSize); + Text.Font = GameFont.Tiny; + Widgets.Label(rect4, "TM_PointsAvail".Translate() + ": " + compMagic.Data.AbilityPoints); + Text.Font = GameFont.Tiny; + if (!godMode) + { + //TODO: Display resources info + } + Rect rect5 = new Rect(rect4.x, rect4.yMax + 3f, inRect.width + 100f, MagicCardUtility.HeaderSize * 0.6f); + DrawLevelBar(rect5, compMagic, pawn, inRect); + } + + public static void DrawLevelBar(Rect rect, CompAbilityUserCustom compMagic, Pawn pawn, Rect rectG) + { + bool flag = rect.height > 70f; + if (flag) + { + float num = (rect.height - 70f) / 2f; + rect.height = 70f; + rect.y += num; + } + bool flag2 = Mouse.IsOver(rect); + if (flag2) + { + Widgets.DrawHighlight(rect); + } + TooltipHandler.TipRegion(rect, new TipSignal(() => MagicXPTipString(compMagic.Data), rect.GetHashCode())); + float num2 = 14f; + bool flag3 = rect.height < 50f; + if (flag3) + { + num2 *= Mathf.InverseLerp(0f, 50f, rect.height); + } + Text.Anchor = TextAnchor.UpperLeft; + Rect rect2 = new Rect(rect.x, rect.y + rect.height / 2f, rect.width, rect.height); + rect2 = new Rect(rect2.x, rect2.y, rect2.width, rect2.height - num2); + float xpPercent = (compMagic.Data.UserXP - compMagic.Data.XPForLevel(compMagic.Data.UserLevel)) + / (float)(compMagic.Data.XPForLevel(compMagic.Data.UserLevel + 1) - compMagic.Data.XPForLevel(compMagic.Data.UserLevel)); + Widgets.FillableBar(rect2, xpPercent, manaTex, BaseContent.GreyTex, false); + Rect rect3 = new Rect(rect2.x + 272f + MagicCardUtility.MagicButtonPointSize, rectG.y, 136f, MagicCardUtility.TextSize); + Rect rect31 = new Rect(rect2.x + 272f, rectG.y, MagicCardUtility.MagicButtonPointSize, MagicCardUtility.TextSize); + Rect rect4 = new Rect(rect3.x + rect3.width + (MagicCardUtility.MagicButtonPointSize * 2), rectG.y, 136f, MagicCardUtility.TextSize); + Rect rect41 = new Rect(rect3.x + rect3.width + MagicCardUtility.MagicButtonPointSize, rectG.y, MagicCardUtility.MagicButtonPointSize, MagicCardUtility.TextSize); + Rect rect5 = new Rect(rect2.x + 272f + MagicCardUtility.MagicButtonPointSize, rectG.yMin + 24f, 136f, MagicCardUtility.TextSize); + Rect rect51 = new Rect(rect2.x + 272f, rectG.yMin + 24f, MagicCardUtility.MagicButtonPointSize, MagicCardUtility.TextSize); + + //TODO: draw global skills? One could also add a fake power and put the global abilities there. + } + + public static string MagicXPTipString(CustomData compMagic) + { + return string.Concat( + compMagic.UserXP.ToString(), + " / ", + compMagic.XPForLevel(compMagic.UserLevel + 1).ToString(), + "\n", + "TM_MagicXPDesc".Translate() + ); + } + + public static void CustomPowersHandler(Rect inRect, CompAbilityUserCustom compMagic, Texture2D pointTexture) + { + float yOffset = inRect.y; + int itnum = 1; + using (List.Enumerator enumerator = compMagic.Data.Powers.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + BasePower power = enumerator.Current; + TMAbilityDef ability = (TMAbilityDef)power.AbilityDef; + if (ability == TorannMagicDefOf.TM_SoulBond || ability == TorannMagicDefOf.TM_ShadowBolt || ability == TorannMagicDefOf.TM_Dominate) + { + continue; + } + + Text.Font = GameFont.Small; + Rect abilityButton = new Rect(MagicCardUtility.MagicCardSize.x / 2f - MagicCardUtility.MagicButtonSize, yOffset, MagicCardUtility.MagicButtonSize, MagicCardUtility.MagicButtonSize); + if (itnum > 1) + { + Widgets.DrawLineHorizontal(0f + 20f, yOffset - 2f, MagicCardUtility.MagicCardSize.x - 40f); + } + if (power.level < power.maxLevel) + { + + TooltipHandler.TipRegion(abilityButton, () => string.Concat(new string[] + { + power.AbilityDef.label, + "\n\nCurrent Level:\n", + power.AbilityDef.description, + "\n\nNext Level:\n", + power.NextLevelAbilityDef.description, + "\n\n", + "TM_CheckPointsForMoreInfo".Translate() + }), 398462); + + } + else + { + TooltipHandler.TipRegion(abilityButton, () => string.Concat(new string[] + { + power.AbilityDef.label, + "\n\n", + power.AbilityDef.description, + "\n\n", + "TM_CheckPointsForMoreInfo".Translate() + }), 398462); + } + + //float xOffsetSkill = Text.CalcSize("TM_Effeciency".Translate()).x; + //float x3 = Text.CalcSize("TM_Versatility".Translate()).x; + Rect rect3 = new Rect(0f + MagicCardUtility.SpacingOffset, yOffset + 2f, MagicCardUtility.MagicCardSize.x, MagicCardUtility.ButtonSize * 1.15f); + + //Rect skillRect = new Rect(rect3.x + rect3.width / 2f - xOffsetSkill, rect3.y, (rect3.width - 20f) / 3f, rect3.height); + //Rect rect6 = new Rect(rect3.width - x3 * 2f, rect3.y, rect3.width / 3f, rect3.height); + + //bool flag9 = power.abilityDef.label == "Ray of Hope" || power.abilityDef.label == "Soothing Breeze" || power.abilityDef.label == "Frost Ray" || power.abilityDef.label == "AMP" || power.abilityDef.label == "Shadow" || power.abilityDef.label == "Magic Missile" || power.abilityDef.label == "Blink" || power.abilityDef.label == "Summon" || power.abilityDef.label == "Shield"; //add all other buffs or xml based upgrades + + Rect rectLabel = new Rect(0f + 20f, abilityButton.yMin, 350f - 44f, MagicCardUtility.MagicButtonPointSize); + //GUI.color = Color.yellow; + Widgets.Label(rectLabel, power.AbilityDef.LabelCap); + //GUI.color = Color.white; + if (power.learned != true) + { + Widgets.DrawTextureFitted(abilityButton, power.Icon, 1f); + Text.Font = GameFont.Tiny; + Rect rectLearn = new Rect(abilityButton.xMin - 44f, abilityButton.yMin, 40f, MagicCardUtility.MagicButtonPointSize); + if (compMagic.Data.AbilityPoints >= power.learnCost)//&& !power.requiresScroll) // FIXME: the ability can be leveled for 1 point, why? + { + bool flagLearn = Widgets.ButtonText(rectLearn, "TM_Learn".Translate(), true, false, true) && compMagic.AbilityUser.Faction == Faction.OfPlayer; + if (flagLearn) + { + // power.levelUp(comp); TODO: delegate levelup action to the individual power + power.learned = true; + if (!(power.AbilityDef?.defName == "TM_TechnoBit")) + { + compMagic.AddPawnAbility(enumerator.Current.AbilityDef); + } + if (power.AbilityDef?.defName == "TM_TechnoWeapon") + { + compMagic.AddPawnAbility(TorannMagicDefOf.TM_NanoStimulant); + } + compMagic.Data.AbilityPoints -= power.learnCost; + } + } + //else if (power.requiresScroll) + //{ + // Rect rectToLearn = new Rect(rect.xMin - 268f, rect.yMin + 22f, 250f, MagicCardUtility.MagicButtonPointSize); + // bool flagLearn = Widgets.ButtonText(rectToLearn, "TM_SpellLocked".Translate(power.abilityDef.LabelCap), false, false, false) && compMagic.AbilityUser.Faction == Faction.OfPlayer; + //} + else + { + string message = "" + enumerator.Current.learnCost + " points to " + "TM_Learn".Translate(); + float size = Text.CalcSize(message).x; + Rect rectToLearn = new Rect(abilityButton.xMin - size, abilityButton.yMin, size, MagicCardUtility.MagicButtonPointSize); + Widgets.Label(rectToLearn, message); + } + } + else + { + string level = " " + power.level + " / " + power.maxLevel; + Vector2 levelSpace = Text.CalcSize(level); + Rect rightOfImage = new Rect(abilityButton.xMax, abilityButton.yMin, levelSpace.x, levelSpace.y); // MagicCardUtility.TextSize); + Widgets.DrawTextureFitted(abilityButton, power.Icon, 1f); + if (power.maxLevel > 0) + { + Widgets.Label(rightOfImage, level); + } + if (enumerator.Current.level >= power.maxLevel + || compMagic.Data.AbilityPoints < power.upgradeCost + || compMagic.AbilityUser.Faction != Faction.OfPlayer) + { + } + else + { + Rect belowLevel = new Rect(abilityButton.xMax, abilityButton.yMax - levelSpace.y, levelSpace.x, levelSpace.y); // MagicCardUtility.TextSize); + bool flag1 = Widgets.ButtonText(belowLevel, "+", true, false, true); + if (flag1) + { + // power.levelUp(comp); TODO: delegate levelup action to the individual power + compMagic.LevelUpPower(power); + compMagic.Data.AbilityPoints -= power.upgradeCost; + } + } + } + + Text.Font = GameFont.Tiny; + float xOffset = rect3.x; + + float xSpacing = (MagicCardUtility.MagicCardSize.x / power.Upgrades.Count) - MagicCardUtility.SpacingOffset; + for (int i = 0; i < power.Upgrades.Count; ++i) + { + if (power.Upgrades[i] == null) { Log.Message("Null upgrade key in power " + ((power as CustomPower)?.localDef.defName ?? power.AbilityDef.defName)); continue; } + MagicPowerSkill skill = compMagic.Data.Upgrades.TryGetValue(power.Upgrades[i]); + CustomSkillHandler(xOffset, compMagic, power, skill, rect3); + itnum++; + xOffset += xSpacing; + } + yOffset += MagicCardUtility.MagicButtonSize + MagicCardUtility.TextSize + 4f;//MagicCardUtility.SpacingOffset; //was 4f + } + } + } + + public static void CustomSkillHandler(float xOffset, CompAbilityUserCustom compMagic, BasePower power, MagicPowerSkill skill, Rect rect3) + { + Rect rect4 = new Rect(xOffset + MagicCardUtility.MagicButtonPointSize, rect3.yMax, MagicCardUtility.MagicCardSize.x / 3f, rect3.height); + Rect rect41 = new Rect(xOffset, rect4.y, MagicCardUtility.MagicButtonPointSize, MagicCardUtility.MagicButtonPointSize); + Rect rect42 = new Rect(rect41.x, rect4.y, rect4.width - MagicCardUtility.MagicButtonPointSize, rect4.height / 2); + //string description = skill.shouldTranslate() ? skill.desc.Translate().ToString() : skill.desc; + //string description = skill.getLocalizedDescription(); + //TODO: find a way to add translation without modifying the language files directly? + string description = false ? skill.desc.Translate().ToString() : skill.desc; + string name = false ? skill.label.Translate().ToString() : skill.label; + TooltipHandler.TipRegion(rect42, new TipSignal(() => description, rect4.GetHashCode())); + Widgets.Label(rect4, name + ": " + skill.level + " / " + skill.levelMax); + if (skill.level < skill.levelMax + && compMagic.Data.AbilityPoints >= skill.costToLevel + && power.learned) + { + bool flag12 = Widgets.ButtonText(rect41, "+", true, false, true) && compMagic.AbilityUser.Faction == Faction.OfPlayer; + if (flag12) + { + bool flag17 = compMagic.AbilityUser.story != null && compMagic.AbilityUser.story.DisabledWorkTagsBackstoryAndTraits == WorkTags.Violent && power.AbilityDef.MainVerb.isViolent; + if (flag17) + { + Messages.Message("IsIncapableOfViolenceLower".Translate( + compMagic.Pawn.Name.ToString() + ), MessageTypeDefOf.RejectInput); + } + else + { + skill.level++; + compMagic.Data.AbilityPoints -= skill.costToLevel; + } + } + } + } + + } +} diff --git a/Source/TMagic/TMagic/MagicAbility.cs b/Source/TMagic/TMagic/MagicAbility.cs index 87496a3c..00c6cf2f 100644 --- a/Source/TMagic/TMagic/MagicAbility.cs +++ b/Source/TMagic/TMagic/MagicAbility.cs @@ -58,6 +58,10 @@ public MagicAbility() { } + public MagicAbility(AbilityData data) : base(data) + { + } + public MagicAbility(CompAbilityUser abilityUser) : base(abilityUser) { this.abilityUser = (abilityUser as CompAbilityUserMagic); @@ -96,13 +100,19 @@ public override void PostAbilityAttempt() //commented out in CompAbilityUserMag this.MagicUser.Mana.UseMagicPower(this.MagicUser.ActualManaCost(magicDef)/2f); } else - { + { this.MagicUser.Mana.UseMagicPower(this.MagicUser.ActualManaCost(magicDef)); } if(this.magicDef != TorannMagicDefOf.TM_TransferMana && magicDef.abilityHediff == null) - { - this.MagicUser.MagicUserXP += (int)((magicDef.manaCost * 300) * this.MagicUser.xpGain * settingsRef.xpMultiplier); + { + int xp = (int)((magicDef.manaCost * 300) * this.MagicUser.xpGain * settingsRef.xpMultiplier); + this.MagicUser.MagicUserXP += xp; + CompAbilityUserCustom custom = this.AbilityUser.Pawn.TryGetComp(); + if (custom != null && custom.Data.ReturnMatchingPower(this.Def, true)?.learned == true) + { + custom.Data.UserXP += xp; + } } } else if (this.MagicUser.Pawn.story.traits.HasTrait(TorannMagicDefOf.Faceless)) diff --git a/Source/TMagic/TMagic/MightAbility.cs b/Source/TMagic/TMagic/MightAbility.cs index 57c745bf..70ef444c 100644 --- a/Source/TMagic/TMagic/MightAbility.cs +++ b/Source/TMagic/TMagic/MightAbility.cs @@ -97,6 +97,7 @@ public override void PostAbilityAttempt() bool flag = this.mightDef != null; if (flag) { + int expGain = 0; if (mightDef.consumeEnergy) { bool flag3 = this.MightUser.Stamina != null; @@ -119,6 +120,19 @@ public override void PostAbilityAttempt() HealthUtility.AdjustSeverity(this.Pawn, TorannMagicDefOf.TM_ChiHD, -100 * this.ActualChiCost); this.MightUser.MightUserXP += (int)((mightDef.chiCost * 100) * this.MightUser.xpGain * settingsRef.xpMultiplier); } + expGain += (int)((mightDef.staminaCost * 180) * this.MightUser.xpGain * settingsRef.xpMultiplier); + + } + if (this.mightDef.chiCost != 0) + { + HealthUtility.AdjustSeverity(this.Pawn, TorannMagicDefOf.TM_ChiHD, -100 * this.ActualChiCost); + expGain += (int)((mightDef.chiCost * 100) * this.MightUser.xpGain * settingsRef.xpMultiplier); + } + this.MightUser.MightUserXP += expGain; + CompAbilityUserCustom custom = this.AbilityUser.Pawn.TryGetComp(); + if (custom != null && custom.Data.ReturnMatchingPower(this.Def, true)?.learned == true) + { + custom.Data.UserXP += expGain; } } } diff --git a/Source/TMagic/TMagic/TMAbilityCost.cs b/Source/TMagic/TMagic/TMAbilityCost.cs new file mode 100644 index 00000000..5e0c8bca --- /dev/null +++ b/Source/TMagic/TMagic/TMAbilityCost.cs @@ -0,0 +1,42 @@ +using System.Text; +using AbilityUser; +using RimWorld; +using Verse; +using System.Collections.Generic; + +namespace TorannMagic +{ + public abstract class TMAbilityCostProperties + { + public abstract TMAbilityCost ForPawn(Pawn pawn); + } + public abstract class TMAbilityCost + { + public abstract float BaseCost { get; } + public abstract string Description { get; } + public abstract bool CanPayCost(out float actualCost); + public abstract bool PayCost(out float actualPaid); + public abstract bool RefundCost(out float actualRefund); + } + public class TMAbilityBadCost : TMAbilityCost + { + public override float BaseCost => -1; + public override string Description + { + get => "impossible cost"; + } + public override bool CanPayCost(out float actualCost) + { + actualCost = BaseCost; + return false; + } + public override bool PayCost(out float actualPaid) + { + return CanPayCost(out actualPaid); + } + public override bool RefundCost(out float actualRefund) + { + return CanPayCost(out actualRefund); + } + } +} diff --git a/Source/TMagic/TMagic/TMAbilityCost_Hediff.cs b/Source/TMagic/TMagic/TMAbilityCost_Hediff.cs new file mode 100644 index 00000000..4091ae30 --- /dev/null +++ b/Source/TMagic/TMagic/TMAbilityCost_Hediff.cs @@ -0,0 +1,86 @@ +using System.Text; +using AbilityUser; +using RimWorld; +using Verse; +using System.Collections.Generic; + +namespace TorannMagic +{ + public class TMAbilityHediffCostProp : TMAbilityCostProperties + { + private HediffDef hediffDef; + private float baseCost = 0f; + private float baseCostReductionPerUpgrade = 0f; + public override TMAbilityCost ForPawn(Pawn pawn) + { + TMAbilityHediffCost.Backup backup = () => pawn.health.hediffSet.GetFirstHediffOfDef(hediffDef); + return new TMAbilityHediffCost(hediffDef, baseCost, backup); + } + } + + public class TMAbilityHediffCost : TMAbilityCost + { + public delegate Hediff Backup(); + + private string hediffLabel; + private Hediff found; + private Backup backup; + private float baseCost = 0f; + //private float baseCostReductionPerUpgrade = 0f; + private Hediff hediff + { + get { + if (found == null) // TODO: limit check frequency + { + found = backup(); + } + return found; + } + } + + public TMAbilityHediffCost(HediffDef def, float baseCost, Backup backup) + { + this.hediffLabel = def.LabelCap; + this.baseCost = baseCost; + this.backup = backup; + } + + public override float BaseCost + { + get => baseCost; + } + public override string Description + { + get => string.Concat(BaseCost, " ", hediffLabel); + } + public override bool CanPayCost(out float actualCost) + { + actualCost = baseCost; + return hediff?.Severity > actualCost; + } + public override bool PayCost(out float actualCost) + { + if (hediff?.Severity >= baseCost) + { + actualCost = baseCost; + hediff.Severity -= baseCost; + return true; + } + else + { + actualCost = baseCost; + return false; + } + } + public override bool RefundCost(out float actualRefund) + { + actualRefund = baseCost; + if (hediff != null) + { + hediff.Severity += baseCost; + return true; + } + return false; + } + } +} diff --git a/Source/TMagic/TMagic/TMAbilityCost_Need.cs b/Source/TMagic/TMagic/TMAbilityCost_Need.cs new file mode 100644 index 00000000..5cb3a9a4 --- /dev/null +++ b/Source/TMagic/TMagic/TMAbilityCost_Need.cs @@ -0,0 +1,71 @@ +using System.Text; +using AbilityUser; +using RimWorld; +using Verse; +using System.Collections.Generic; + +namespace TorannMagic +{ + public class TMAbilityNeedCostProp : TMAbilityCostProperties + { + private NeedDef needDef; + private float baseCost = 0f; + private float baseCostReductionPerUpgrade = 0f; + public override TMAbilityCost ForPawn(Pawn pawn) + { + Need need = pawn.needs.TryGetNeed(needDef); + return need != null ? new TMAbilityNeedCost(need, baseCost) : null; + } + } + + public class TMAbilityNeedCost : TMAbilityCost + { + private Need need; + private float baseCost = 0f; + //private float baseCostReductionPerUpgrade = 0f; + + public TMAbilityNeedCost(Need need, float baseCost) + { + this.need = need; + this.baseCost = baseCost / 100f; + Log.Message("Init cost " + baseCost + need.LabelCap); + } + + public override float BaseCost + { + get => baseCost; + } + public override string Description + { + get => string.Concat(BaseCost * 100f, " ", need.LabelCap); + } + public override bool CanPayCost(out float actualCost) + { + actualCost = baseCost; + return need.CurLevel > actualCost; + } + public override bool PayCost(out float actualCost) + { + if (need?.CurLevel < baseCost) + { + actualCost = baseCost; + return false; + } + else + { + actualCost = baseCost; + need.CurLevel -= baseCost; + return true; + } + } + public override bool RefundCost(out float actualRefund) + { + actualRefund = baseCost; + if (need != null) + { + need.CurLevel += baseCost; + } + return true; + } + } +} diff --git a/Source/TMagic/TMagic/TMAbilityDef.cs b/Source/TMagic/TMagic/TMAbilityDef.cs index 780bc1a2..8b2668b9 100644 --- a/Source/TMagic/TMagic/TMAbilityDef.cs +++ b/Source/TMagic/TMagic/TMAbilityDef.cs @@ -13,6 +13,7 @@ public class TMAbilityDef : AbilityUser.AbilityDef public float staminaCost = 0f; public float bloodCost = 0f; public float chiCost = 0f; + public TMAbilityCostProperties customCost = null; public bool consumeEnergy = true; public int abilityPoints = 1; public float learnChance = 1f; diff --git a/Source/TMagic/TMagic/TMDefs/TM_CustomClass.cs b/Source/TMagic/TMagic/TMDefs/TM_CustomClass.cs index 84225057..1b9b4a49 100644 --- a/Source/TMagic/TMagic/TMDefs/TM_CustomClass.cs +++ b/Source/TMagic/TMagic/TMDefs/TM_CustomClass.cs @@ -26,6 +26,7 @@ public class TM_CustomClass public List classFighterAbilities = new List(); public List learnableSpells = new List(); public List learnableSkills = new List(); + public List customPowers = new List(); //Class Designations public bool isMage = false; diff --git a/Source/TMagic/TMagic/TorannMagic.csproj b/Source/TMagic/TMagic/TorannMagic.csproj index 3857f25c..9688cdc1 100644 --- a/Source/TMagic/TMagic/TorannMagic.csproj +++ b/Source/TMagic/TMagic/TorannMagic.csproj @@ -641,6 +641,18 @@ + + + + + + + + + + + + diff --git a/Source/TMagic/TMagic/Verb_ApplyRandomHediffFromList.cs b/Source/TMagic/TMagic/Verb_ApplyRandomHediffFromList.cs new file mode 100644 index 00000000..8cdc2266 --- /dev/null +++ b/Source/TMagic/TMagic/Verb_ApplyRandomHediffFromList.cs @@ -0,0 +1,88 @@ +using Verse; +using AbilityUser; +using System.Collections.Generic; + +namespace TorannMagic +{ + public class Verb_ApplyRandomHediffFromList_Properties : VerbProperties_Ability + { + public List hediffDefs; + public int baseCount = 1; + public UpgradeDef countUpgrade; + public float baseSeverity = 0.5f; + public UpgradeDef severityUpgrade; + public float severityIncreasePerUpgrade = 0.1f; + public bool useEachOnceMax = false; + + public Verb_ApplyRandomHediffFromList_Properties() : base() + { + this.verbClass = verbClass ?? typeof(Verb_ApplyRandomHediffFromList); + } + } + + public class Verb_ApplyRandomHediffFromList : Verb_SB + { + Pawn target => currentTarget.Pawn; + Verb_ApplyRandomHediffFromList_Properties Properties => this.verbProps as Verb_ApplyRandomHediffFromList_Properties; + List Effects => Properties?.hediffDefs; + int CountUpgrade => Properties.countUpgrade == null ? 0 + : CasterPawn?.TryGetComp().Data.GetUpgradeLevel(Properties.countUpgrade.UpgradeId) ?? 0; + int SeverityUpgrade => Properties.severityUpgrade == null ? 0 + : CasterPawn?.TryGetComp().Data.GetUpgradeLevel(Properties.severityUpgrade?.UpgradeId) ?? 0; + + protected override bool TryCastShot() + { + if (Properties == null) + { + Log.Warning("The TorannMagic.Verb_ApplyRandomHediffFromList cannot be used outside a TorannMagic.Verb_ApplyRandomHediffFromList_Properties, check abilityDef " + this.Ability.Def.defName); + return false; + } + if (target != null ) + { + int amountToApply = Properties.baseCount + CountUpgrade; + float severity = Properties.baseSeverity + Properties.severityIncreasePerUpgrade * SeverityUpgrade; + + if (Properties.useEachOnceMax) + { + List toApply; + if (amountToApply >= Effects.Count) + { + toApply = Effects; + } + else + { + toApply = new List(amountToApply); + for (int i = 0; toApply.Count < amountToApply; ++i) + { + float rand = Rand.Value; + Log.Message("rand: " + rand); + Log.Message("target: " + (amountToApply - toApply.Count) / (float)(Effects.Count - i)); + if (rand < (amountToApply - toApply.Count) / (float)(Effects.Count - i)) + { + toApply.Add(Effects[i]); + } + } + } + foreach (HediffDef h in toApply) + { + HealthUtility.AdjustSeverity(target, h, severity); + } + } + else + { + Dictionary toApply = new Dictionary(Properties.hediffDefs.Count); + for (int i = 0; i < amountToApply; ++i) + { + HediffDef def = Effects.RandomElement(); + toApply.SetOrAdd(def, toApply.TryGetValue(def) + severity); + } + foreach (HediffDef d in Effects) + { + HealthUtility.AdjustSeverity(target, d, toApply.TryGetValue(d)); + } + } + } + return false; + } + } +} diff --git a/Source/TMagic/TMagic/Verb_MedicalSupply.cs b/Source/TMagic/TMagic/Verb_MedicalSupply.cs new file mode 100644 index 00000000..64391a1b --- /dev/null +++ b/Source/TMagic/TMagic/Verb_MedicalSupply.cs @@ -0,0 +1,92 @@ +using RimWorld; +using System; +using Verse; +using AbilityUser; +using UnityEngine; +using System.Collections.Generic; +using System.Linq; + +namespace TorannMagic +{ + public class Verb_MedicalSupply : Verb_UseAbility + { + bool validTarg; + public override bool CanHitTargetFrom(IntVec3 root, LocalTargetInfo targ) + { + if (targ.IsValid && targ.CenterVector3.InBounds(base.CasterPawn.Map) && !targ.Cell.Fogged(base.CasterPawn.Map)) + { + if ((root - targ.Cell).LengthHorizontal < this.verbProps.range) + { + validTarg = true; + } + else + { + //out of range + validTarg = false; + } + } + else + { + validTarg = false; + } + return validTarg; + } + + protected override bool TryCastShot() + { + bool result = false; + bool success; + + Pawn pawn = this.CasterPawn; + Map map = this.CasterPawn.Map; + ModOptions.SettingsRef settingsRef = new ModOptions.SettingsRef(); + //if (pawn != null && !pawn.Downed) + + List validParts = new List(); + Thing medicalThing = null; + if (GetMedicalSupplyFromCell(this.currentTarget.Cell, this.CasterPawn, out medicalThing, true)) + { + float potency = medicalThing.GetStatValue(StatDefOf.MedicalPotency); + float supplyGain = 20f * potency * potency; + if (!medicalThing.DestroyedOrNull() && medicalThing.stackCount <= 0) + { + medicalThing.Destroy(DestroyMode.Vanish); + } + else + { + medicalThing.SplitOff(1).Destroy(DestroyMode.Vanish); + } + + HealthUtility.AdjustSeverity(this.CasterPawn, HediffDef.Named("TM_MedicalSupplyHD"), supplyGain); + } + else + { + //todo: notify when invalid target + //Messages.Message("TM_NoMedicalSupply".Translate(this.CasterPawn.LabelShort), MessageTypeDefOf.RejectInput, false); + Log.Warning("failed to TryCastShot"); + } + this.burstShotsLeft = 0; + + return result; + } + + public static bool GetMedicalSupplyFromCell(IntVec3 cell, Pawn pawn, out Thing medicalSupply, bool manualCast = false) + { + List thingList = cell.GetThingList(pawn.Map); + medicalSupply = null; + for (int i = 0; i < thingList.Count; i++) + { + if (thingList[i] != null && !(thingList[i] is Pawn) && !(thingList[i] is Building) && (manualCast || !thingList[i].IsForbidden(pawn))) + { + if (thingList[i].def.statBases != null && thingList[i].GetStatValue(StatDefOf.MedicalPotency) > 0.25f) // random non-medical itens have a medical potency of 0.2 + { + medicalSupply = thingList[i]; + Log.Message("medicine item: " + medicalSupply.LabelShort + " with potency " + medicalSupply.GetStatValue(StatDefOf.MedicalPotency)); + break; + } + } + } + return medicalSupply != null; + } + } +} diff --git a/Textures/UI/Emergency.png b/Textures/UI/Emergency.png new file mode 100644 index 00000000..64c3c2a7 Binary files /dev/null and b/Textures/UI/Emergency.png differ diff --git a/Textures/UI/Medigel.png b/Textures/UI/Medigel.png new file mode 100644 index 00000000..79a605a3 Binary files /dev/null and b/Textures/UI/Medigel.png differ