static NetBattlefield ProduceFakeBFIfNeeded(int playerID, NetBattlefield bf) { //fake battlefield will contain all friendly cards currently not on battlefield //positioned on free positions so that AI can see if it can produce //any targets. there is no simulation involved so fake state should not harm "thinking" //we will not need fake BF IF all owned cards are already on BF. HashSet <NetCard> ownCastedCards = bf.GetCardsOnBattelfield(playerID); NetCardPlayerData ncd = bf.GetPlayerByPlayerID(playerID); if (ncd == null || NetType.IsNullOrEmpty(ncd.HandCards)) { return(bf); } List <int> ownCardsAtHand = ncd.HandCards.value; List <NetCard> cardsToCast = new List <NetCard>(); //this is different set than just cards which were used, because card which casts summons or //does support will not be present on battlefield and still cost more than base AP foreach (var v in ownCardsAtHand) { NetCard nc = bf.GetCardByID(v); if (!ownCastedCards.Contains(nc)) { cardsToCast.Add(nc); } } if (cardsToCast.Count == 0) { return(bf); } //there are cards which should be represented on battle positions //which requires new BF //we would do simple but fake casts NetBattlefield bf2 = bf.CloneAndRemember(); int halfSize = bf2.BattlefieldSize / 2; int start = playerID == 0 ? 0 : halfSize; int cardListIndex = 0; for (int i = 0; i < halfSize; i++) { if (cardListIndex == cardsToCast.Count) { break; } int index = i + start; if (bf2.BattlefieldPositions.value[index] == 0) { bf2.BattlefieldPositions.value[index] = cardsToCast[cardListIndex].CardID; } } return(bf2); }
// Helper that applies normal damage and shield leech (affected by leechFactor). static public object Act_ShieldLeech_Helper(NetBattlefield bf, NetQueueItem q, FInt leechFactor) { NetSkill ns = q.GetNetSkill(bf); NetCard owner = bf.GetCardByID(ns.OwnerCardID); NetCard target = null; if (NetType.IsNullOrEmpty(q.Targets)) { return(null); } FInt damage = owner.GetSkillCastingStrength(ns); int shieldLeeched = 0; // Primary targets. foreach (var v in q.Targets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { target.ReciveNormalDamage(damage, bf, q, v); shieldLeeched += (damage * leechFactor).ToInt(); } } // Secondary targets. if (!NetType.IsNullOrEmpty(q.SecondaryTargets)) { damage *= 0.5f; foreach (var v in q.SecondaryTargets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { target.ReciveNormalDamage(damage, bf, q, v); shieldLeeched += (damage * leechFactor).ToInt(); } } } if (shieldLeeched > 0) { FInt currentShields = owner.GetCA_SHIELD(); owner.SetCA_SHIELD(currentShields + shieldLeeched); } return(null); }
// Ancient version of the built-in script Act_DrainHealthEssence, which leeches 80% instead. // Note: Built-in script Act_DrainHealthAncient does not appear to do this. static public object Act_LifeLeech_Ancient(NetBattlefield bf, NetQueueItem q, List <NetQueueItem> stack, List <NetQueueItem> previousItems, MHRandom random) { NetSkill ns = q.GetNetSkill(bf); NetCard owner = bf.GetCardByID(ns.OwnerCardID); NetCard target = null; if (NetType.IsNullOrEmpty(q.Targets)) { return(null); } FInt damage = owner.GetSkillCastingStrength(ns); int lifeLeeched = 0; // Primary targets. foreach (var v in q.Targets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { target.ReciveNormalDamage(damage, bf, q, v); lifeLeeched += (damage * 0.8f).ToInt(); } } // Secondary targets. if (!NetType.IsNullOrEmpty(q.SecondaryTargets)) { damage *= 0.5f; foreach (var v in q.SecondaryTargets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { target.ReciveNormalDamage(damage, bf, q, v); lifeLeeched += (damage * 0.8f).ToInt(); } } } if (lifeLeeched > 0) { owner.ReciveHealthNormal(lifeLeeched, bf, q, t); } return(null); }
// // static public CardAIPlanData _ProducePlan(int playerID, CardAIPlanData data, int avaliableAP, MHRandom r, int calculationIntensity = 1) // { // if (calculationIntensity < 1 ) // { // Debug.LogError("[ERROR]0 level for AI will nto produce any plans!, Increase calculation intensity to minimum 1!"); // return new CardAIPlanData(); // } // // NetBattlefield bf = data.bf; // // List<NetCard> cards = CardsWithinAPRange(playerID, bf, avaliableAP); // if (cards == null) return new CardAIPlanData(); // // List<NetSkill> skills = SelectSkillsToCast(cards); // if (skills == null || skills.Count == 0) return new CardAIPlanData(); // // bool friendlyTurn = playerID > 0; // // //Do single cast of all skills to build some expectations // CardAIPlanData[] plans = new CardAIPlanData[skills.Count]; // // for (int i=0; i<skills.Count; i++) // { // NetSkill ns = skills[i]; // CardAIPlanData d = CastSkillOnece(friendlyTurn, ns, ns.GetOwner(bf), data, bf.CloneAndRemember(), r); // if (d.valid) // { // plans[i] = d; // } // } // // //sort result based on their value // List<CardAIPlanData> plansL = new List<CardAIPlanData>(plans); // plansL.Sort(delegate (CardAIPlanData a, CardAIPlanData b) // { // return a.value.CompareTo(b.value); // }); // // //get top plans and try to refine them before selecting the best // int min = Mathf.Min(plansL.Count, calculationIntensity); // plansL = plansL.GetRange(0, min); // // int refiningLevel = 8; // plans = new CardAIPlanData[plansL.Count * refiningLevel]; // for(int i=0; i < plansL.Count; i++) // { // for(int k=0; k< refiningLevel; k++) // { // //plansL[i].validTargets // } // } // // // Start secondary plans from the selected plans // return new CardAIPlanData(); // } static public List <NetCard> CardsWithinAPRange(CardAI ai, NetBattlefield bf, int testedAPRange) { NetListInt ni = bf.GetPlayerByPlayerID(ai.playerID).HandCards; if (NetType.IsNullOrEmpty(ni)) { return(null); } List <NetCard> ncs = new List <NetCard>(); foreach (var v in ni.value) { NetCard nc = bf.GetCardByID(v); if (nc.GetCastingCost() <= testedAPRange) { ncs.Add(nc); } } return(ncs); }
static public object Act_AddShieldingImproved( NetBattlefield bf, NetQueueItem q, List <NetQueueItem> stack, List <NetQueueItem> previousItems, MHRandom random) { NetSkill ns = q.GetNetSkill(bf); NetCard owner = bf.GetCardByID(ns.OwnerCardID); //skill needs target(-s) if (!NetTypeAtomic.IsValid(q.Targets)) { return(null); } FInt extra = FInt.ZERO; if (ns.MainAtt != null) { extra = owner.GetSkillCastingStrength(ns); } FInt value = ns.GetFloatAttribute("TAG-CA_SHIELD"); string sign = ns.GetStringAttribute("TAG-CA_SHIELD"); if (value == FInt.ZERO) { if (bf.ChallengeType == EChallengeType.TypePhysical) { value = ns.GetFloatAttribute("TAG-SHIELDING_PHYSICAL"); } else if (bf.ChallengeType == EChallengeType.TypeMental) { value = ns.GetFloatAttribute("TAG-SHIELDING_MENTAL"); } else if (bf.ChallengeType == EChallengeType.TypeSpirit) { value = ns.GetFloatAttribute("TAG-SHIELDING_SPIRIT"); } } if (string.IsNullOrEmpty(sign)) { if (bf.ChallengeType == EChallengeType.TypePhysical) { sign = ns.GetStringAttribute("TAG-SHIELDING_PHYSICAL"); } else if (bf.ChallengeType == EChallengeType.TypeMental) { sign = ns.GetStringAttribute("TAG-SHIELDING_MENTAL"); } else if (bf.ChallengeType == EChallengeType.TypeSpirit) { sign = ns.GetStringAttribute("TAG-SHIELDING_SPIRIT"); } } value += extra; value.CutToInt(); if (sign == "*") { value = value * 0.01f; } foreach (var v in q.Targets.value) { NetCard target = bf.ConvertPositionIDToCard(v); if (target == null) { continue; } FInt prev = target.GetCA_SHIELD(); if (sign == "+") { target.SetCA_SHIELD(value + prev); } else if (sign == "*") { target.SetCA_SHIELD(value * prev); } } if (NetType.IsNullOrEmpty(q.SecondaryTargets)) { return(null); } foreach (var v in q.SecondaryTargets.value) { NetCard target = bf.ConvertPositionIDToCard(v); if (target == null) { continue; } FInt prev = target.GetCA_SHIELD(); if (sign == "+") { target.SetCA_SHIELD(value + prev); } else if (sign == "*") { target.SetCA_SHIELD(value * prev); } } return(null); }
/// <summary> /// /// </summary> /// <param name="bf">general battlefield information</param> /// <param name="q">queue element which is already executed which trigers this skill</param> /// <param name="stack">Items which are still in stack including "q", which most likely is first element</param> /// <param name="previousItems">Queue items which were already executed in order they were executed</param> /// <param name="random">random generator specific to this thread</param> /// <returns></returns> /// <summary> /// Basic damage /// </summary> static public object Act_Damage_Procedural( NetBattlefield bf, NetQueueItem q, List <NetQueueItem> stack, List <NetQueueItem> previousItems, MHRandom random) { NetSkill ns = q.GetNetSkill(bf); NetCard owner = bf.GetCardByID(ns.OwnerCardID); NetCard target = null; if (NetType.IsNullOrEmpty(q.Targets)) { return(null); } int flags = ns.Flags; bool essence = (flags & (int)EActivatorBlocks.Essence) > 0; bool ancient = (flags & (int)EActivatorBlocks.Ancient) > 0; bool gray = !ancient && !essence; FInt damage = GameplayUtils.GetDamageFor(ns, owner); // if ((flags & (int)SkillGenerator.EActivatorBlocks.Additive) > 0) // { // // if (essence) // { // //essence is estimated to be equal between additive and multiplicative at // // value of 10 (A x 0.2 + 8) ~ (A = 10) // damage = owner.GetSkillCastingAdditiveStrength(ns, SkillGenerator.ESSENCE_ADDITIVE_BASE); // } // else if(ancient) // { // //essence is estimated to be equal between additive and multiplicative at // // value of 15 (A x 0.2 + 12) ~ (A = 15) // damage = owner.GetSkillCastingAdditiveStrength(ns, SkillGenerator.ANCIENT_ADDITIVE_BASE); // } // else // { // //essence is estimated to be equal between additive and multiplicative at // // value of 6 (A x 0.2 + 5) ~ (A = 6) // damage = owner.GetSkillCastingAdditiveStrength(ns, SkillGenerator.GRAY_ADDITIVE_BASE); // } // } // else // { // damage = owner.GetSkillCastingStrength(ns); // } bool trueDamage = (flags & (int)EActivatorBlocks.TrueDamage) > 0; bool poisonDamage = (flags & (int)EActivatorBlocks.PoisonDamage) > 0; bool lifeLeech = (flags & (int)EActivatorBlocks.LifeLeech) > 0; bool shieldLeech = (flags & (int)EActivatorBlocks.ShieldLeech) > 0; bool additive = (flags & (int)EActivatorBlocks.Additive) > 0; int lifeLeeched = 0; int shieldLeeched = 0; #region Primary targets foreach (var v in q.Targets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { FInt dmg = damage; if (poisonDamage && target.IsWounded()) { if (essence) { dmg *= 1.35f; } else { dmg *= 1.6f; } } if (trueDamage) { if (essence) { target.ReciveTrueDamageEssence(dmg, bf, q, v); } else //no test for ancient { target.ReciveTrueDamageAncient(dmg, bf, q, v); } } else { target.ReciveNormalDamage(dmg, bf, q, v); } if (lifeLeech) { if (essence) { lifeLeeched += (dmg * 0.4f).ToInt(); } else { lifeLeeched += (dmg * 0.8f).ToInt(); } } if (shieldLeech) { if (essence) { shieldLeeched += (dmg * 0.5f).ToInt(); } else { shieldLeeched += (dmg * 1.0f).ToInt(); } } } } #endregion #region Secondary targets ////do splash if needed if (!NetType.IsNullOrEmpty(q.SecondaryTargets)) { bool splashBonus = (flags & (int)EActivatorBlocks.AOE) > 0; if (gray || !splashBonus) { damage *= 0.5f; } else { //this is !gray && AOE if (essence) { damage *= 0.75f; } if (ancient) { damage *= 1f; } } //ancient damage is 100% splash foreach (var v in q.SecondaryTargets.value) { target = bf.ConvertPositionIDToCard(v); if (target != null) { FInt dmg = damage; if (poisonDamage && target.IsWounded()) { if (essence) { dmg *= 1.35f; } else { dmg *= 1.6f; } } if (trueDamage) { if (essence) { target.ReciveTrueDamageEssence(dmg, bf, q, v); } else //no test for ancient { target.ReciveTrueDamageAncient(dmg, bf, q, v); } } else { target.ReciveNormalDamage(dmg, bf, q, v); } if (lifeLeech) { if (essence) { lifeLeeched += (dmg * 0.4f).ToInt(); } else { lifeLeeched += (dmg * 0.8f).ToInt(); } } if (shieldLeech) { if (essence) { shieldLeeched += (dmg * 0.5f).ToInt(); } else { shieldLeeched += (dmg * 1.0f).ToInt(); } } } } } #endregion #region Leech if (lifeLeeched > 0) { FInt max = owner.GetCA_MAX_HEALTH(); FInt cur = owner.GetCA_HEALTH(); if (cur < max) { if (lifeLeeched > max - cur) { owner.SetCA_HEALTH(max); } else { owner.SetCA_HEALTH(cur + lifeLeeched); } } } if (shieldLeeched > 0) { FInt cur = owner.GetCA_SHIELD(); owner.SetCA_SHIELD(cur + shieldLeeched); } #endregion return(null); }