public Summoned getNearestMinion(Vector3 point) { // make sure all summons in the list are valid summons.RemoveAll(x => x == null); // return if there are no summons if (summons.Count <= 0) { return(null); } // initialise the nearest as the first Summoned nearestSummon = summons[0]; float nearestSquareDist = Maths.squareDistance(point, summons[0].transform.position); // try to find the nearest one float squareDist = 0; for (int i = 1; i < summons.Count; i++) { squareDist = Maths.squareDistance(point, summons[i].transform.position); if (squareDist < nearestSquareDist) { nearestSquareDist = squareDist; nearestSummon = summons[i]; } } // return the nearest one found return(nearestSummon); }
public void AddSummon(Summoned summon) { summons.Add(summon); if (newSummonEvent != null) { newSummonEvent.Invoke(summon); } }
public void onNewSummon(Summoned summon) { // subscribe to the death events of skeletal minions if (summon && summon.references && summon.references.thisAbility) { if (summon.references.thisAbility == ability || summon.references.thisAbility == summonWarrior || summon.references.thisAbility == summonMage || summon.references.thisAbility == summonArcher || summon.references.thisAbility == summonWarlord || summon.references.thisAbility == summonBrawler) { Dying dying = summon.GetComponent <Dying>(); if (!dying) { Debug.LogError(summon.name + " does not have a Dying component yet, fix this script please"); return; } dying.deathEvent += OnSkeletonDeath; } } }
// Use this for initialization public override void onCreation() { bool success = false; CreationReferences references = GetComponent <CreationReferences>(); if (!references || !references.creator) { if (destroyIfFailedToMove) { SelfDestroyer dest = Comp <SelfDestroyer> .GetOrAdd(gameObject); dest.failedAbility = true; dest.die(); } return; } SummonTracker tracker = references.creator.GetComponent <SummonTracker>(); if (!tracker) { if (destroyIfFailedToMove) { SelfDestroyer dest = Comp <SelfDestroyer> .GetOrAdd(gameObject); dest.failedAbility = true; dest.die(); } return; } // find a lit of summon List <Summoned> summons = new List <Summoned>(); foreach (Summoned summoned in tracker.summons) { if (summoned) { summons.Add(summoned); } } // remove inactive objects from the list for (int i = summons.Count - 1; i >= 0; i--) { if (!summons[i].gameObject.activeSelf) { summons.Remove(summons[i]); } } // remove dying objects from the list if (requiredLifeState == lifeState.alive) { for (int i = summons.Count - 1; i >= 0; i--) { if (summons[i].GetComponent <Dying>() != null) { if (summons[i].GetComponent <StateController>().currentState == summons[i].GetComponent <StateController>().dying) { summons.Remove(summons[i]); } } } } // or remove living objects from the list else if (requiredLifeState == lifeState.dead) { for (int i = summons.Count - 1; i >= 0; i--) { if (summons[i].GetComponent <Dying>() != null) { if (summons[i].GetComponent <StateController>().currentState != summons[i].GetComponent <StateController>().dying) { summons.Remove(summons[i]); } } else { summons.Remove(summons[i]); } } } if (summons.Count == 0) { if (destroyIfFailedToMove) { SelfDestroyer dest = Comp <SelfDestroyer> .GetOrAdd(gameObject); dest.failedAbility = true; dest.die(); } return; } else { // find the nearest summon Summoned nearest = summons[0]; float distance = Vector3.Distance(transform.position, nearest.transform.position); foreach (Summoned summon in summons) { if (Vector3.Distance(transform.position, summon.transform.position) < distance) { nearest = summon; distance = Vector3.Distance(transform.position, summon.transform.position); } } // move to the summon's location transform.position = nearest.transform.position; // change to the summon's rotation transform.rotation = nearest.transform.rotation; success = true; if (kill) { Dying dying = nearest.GetComponent <Dying>(); if (dying) { if (eraseCorpse) { dying.setDelays(0.01f, 0.02f); } dying.myHealth.HealthDamage(dying.myHealth.maxHealth * 200); } } } if (destroyIfFailedToMove && success == false) { SelfDestroyer dest = Comp <SelfDestroyer> .GetOrAdd(gameObject); dest.failedAbility = true; dest.die(); } }
public void Summon() { for (int i = 0; i < numberToSummon; i++) { // get a reference to this objects creation references CreationReferences myReferences = GetComponent <CreationReferences>(); // remove previous summons if necessary if (limitNumber && myReferences && attachCreationReferences && entity.GetComponent <Prefabs>()) { abilitiesThatCountForLimit.Add(myReferences.thisAbility); List <GameObject> existingSummons = new List <GameObject>(); foreach (Summoned summoned in Summoned.all) { if (abilitiesThatCountForLimit.Contains(summoned.references.thisAbility)) { if (summoned.references.creator = myReferences.creator) { existingSummons.Add(summoned.gameObject); } } } // components for removing summons SelfDestroyer selfDestroyer = null; Dying dying = null; while (existingSummons.Count >= limit) { GameObject SummonToKill = getLowestPriority(existingSummons); existingSummons.Remove(SummonToKill); selfDestroyer = SummonToKill.GetComponent <SelfDestroyer>(); dying = SummonToKill.GetComponent <Dying>(); // death events if (dying && dying.getController()) { dying.unsummoned = true; dying.die(); } // self destroyer events if (selfDestroyer) { selfDestroyer.die(); } // if neither exist, resort to destroying the game object if (!dying && !selfDestroyer) { Destroy(SummonToKill); } } } // decide the position Vector3 pos = displacement; if (distance > 0) { float angle = Random.Range(0, Mathf.PI * 2); pos = new Vector3(distance * Mathf.Cos(angle), displacement.y, distance * Mathf.Sin(angle)); } pos = transform.position + pos; pos = new Vector3(pos.x, getY(pos), pos.z); // create the entity GameObject summon = Instantiate(entity, pos, Quaternion.Euler(0, 0, 0)); // adapt the entity foreach (EntityAdapter adapter in GetComponents <EntityAdapter>()) { summon = adapter.adapt(summon); } // attach creation references if necessary, if not then update them CreationReferences references = null; if (attachCreationReferences && myReferences != null) { if (!summon.GetComponent <CreationReferences>()) { references = summon.AddComponent <CreationReferences>(); } else { references = summon.GetComponent <CreationReferences>(); } references.creator = myReferences.creator; references.thisAbility = myReferences.thisAbility; } // add a summoned component Summoned summonedComponent = summon.AddComponent <Summoned>(); summonedComponent.singeCardForAllMinions = singeCardForAllMinions; // initialise the summoned component summonedComponent.references = references; summonedComponent.initialise(); //adds recent events tracker summon.AddComponent <CharacterStatusTracker>(); // get a reference to the minion's stats BaseStats stats = summon.GetComponent <BaseStats>(); // give it damage bonuses based on a tagged stats holder if (GetComponent <TaggedStatsHolder>() && stats) { TaggedStatsHolder holder = GetComponent <TaggedStatsHolder>(); foreach (TaggedStatsHolder.TaggableStat ts in holder.taggedStats) { List <Tags.AbilityTags> newTagList = new List <Tags.AbilityTags>(); newTagList.AddRange(ts.tagList); if (newTagList.Contains(Tags.AbilityTags.Minion)) { newTagList.Remove(Tags.AbilityTags.Minion); stats.ChangeStatModifier(ts.property, ts.addedValue, BaseStats.ModType.ADDED, newTagList); stats.ChangeStatModifier(ts.property, ts.increasedValue, BaseStats.ModType.INCREASED, newTagList); foreach (float value in ts.moreValues) { stats.ChangeStatModifier(ts.property, value, BaseStats.ModType.MORE, newTagList); } foreach (float value in ts.quotientValues) { stats.ChangeStatModifier(ts.property, value, BaseStats.ModType.QUOTIENT, newTagList); } } } } // make it follow the creator if necessary if (followsCreator) { if (GetComponent <CreationReferences>()) { // if no follow ranges have been defined then use the defaults if (followRangesAndPriorities.Count <= 0) { summon.AddComponent <Following>().leader = myReferences.creator; } // otherwise create follow states for each entry in the list Following following; foreach (FollowRangeAndPriority frar in followRangesAndPriorities) { following = summon.AddComponent <Following>(); following.leader = myReferences.creator; following.startFollowingRange = frar.range; following.priority = frar.priority; } } } // make it move away from the creator when close if necessary if (movesAwayFromSummonerWhenClose) { if (myReferences) { summon.AddComponent <MovingAwayFromSummoner>().summoner = myReferences.creator; } } // if necessary limit the duration by adding a destroy after duration component if (limitDuration) { // use a self destroyer if it has ones, otherwise use the dying state if (summon.GetComponent <SelfDestroyer>()) { DestroyAfterDuration destroyer = summon.AddComponent <DestroyAfterDuration>(); destroyer.duration = duration; } else { DieAfterDelay destroyer = null; if (!summon.GetComponent <DieAfterDelay>()) { destroyer = summon.AddComponent <DieAfterDelay>(); destroyer.timeUntilDeath = duration; } // if there is already a destroy after duration component, lower the duration if this component's duration is lower than the one already there else { destroyer = summon.GetComponent <DieAfterDelay>(); destroyer.timeUntilDeath = Mathf.Min(destroyer.timeUntilDeath, duration); } // make the destroyer unsummon rather than kill if (destroyer) { destroyer.countsAsUnsummoned = true; } } } // pass on alignment if necessary, may require an alignment manager to be added to the entity if (passOnAlignment && GetComponent <AlignmentManager>()) { AlignmentManager alignmentManager = null; if (!summon.GetComponent <AlignmentManager>()) { alignmentManager = summon.AddComponent <AlignmentManager>(); } else { alignmentManager = summon.GetComponent <AlignmentManager>(); } alignmentManager.alignment = GetComponent <AlignmentManager>().alignment; } // give the entity a custom name if necessary, may require a display information component to be added to the entity if (giveCustomName && customNames.Count > 0) { DisplayInformation displayInformation = null; if (!summon.GetComponent <DisplayInformation>()) { displayInformation = summon.AddComponent <DisplayInformation>(); } else { displayInformation = summon.GetComponent <DisplayInformation>(); } int nameIndex = Random.Range(0, customNames.Count); displayInformation.displayName = customNames[nameIndex]; } // add stats if (statList != null && statList.Count > 0) { if (stats) { foreach (TaggedStatsHolder.TaggableStat stat in statList) { stats.addStat(stat); } stats.UpdateStats(); stats.myHealth.currentHealth = stats.myHealth.maxHealth; } } // give the entity a custom tag if necessary if (giveCustomTag) { summon.tag = customTag; } if (stats) { stats.UpdateStats(); if (stats.myHealth) { stats.myHealth.currentHealth = stats.myHealth.maxHealth; } } // pass on this summon information to a summonChangeTracker SummonChangeTracker sct = summon.AddComponent <SummonChangeTracker>(); sct.statList.AddRange(statList); sct.limitDuration = limitDuration; sct.followsCreator = followsCreator; sct.followRangesAndPriorities = new List <FollowRangeAndPriority>(); sct.followRangesAndPriorities.AddRange(followRangesAndPriorities); sct.limitDuration = limitDuration; sct.duration = duration; } }
public override GameObject Mutate(GameObject abilityObject, Vector3 location, Vector3 targetLocation) { // DoT on cast buff if (statBuffs) { statBuffs.addBuff(4f, Tags.Properties.Damage, 0, increasedDotDamageOnCast, null, null, dotTag, "sacrifice dot on cast bug"); } if (boneNovaChance > 0 && (!mana || mana.currentMana > 0) && (boneNovaChance >= 1 || boneNovaChance > (Random.Range(0f, 1f)))) { // nova trigger CreateAbilityObjectOnDeath component = abilityObject.AddComponent <CreateAbilityObjectOnDeath>(); component.abilityToInstantiate = Ability.getAbility(AbilityID.boneNova); component.failsIfFailedAbility = true; component.offset = boneNovaOffset; // nova mutator BoneNovaMutator mutator = abilityObject.AddComponent <BoneNovaMutator>(); mutator.increasedSpeed = nova_increasedSpeed; mutator.pierces = nova_pierces; mutator.increasedDamage = nova_increasedDamage; mutator.increasedStunChance = nova_increasedStunChance; mutator.bleedChance = nova_bleedChance; mutator.addedCritChance = nova_addedCritChance; mutator.addedCritMultiplier = nova_addedCritMultiplier; mutator.cone = false; mutator.dontAttach = true; mutator.dontMoveToTarget = true; mutator.randomAngles = false; mutator.noVFX = true; mutator.moreDamageAgainstBleeding = nova_moreDamageAgainstBleeding; } if (bloodWraithChance > 0 && (bloodWraithChance >= 1 || bloodWraithChance > Random.Range(0f, 1f))) { // wraith trigger CreateAbilityObjectOnDeath component = abilityObject.AddComponent <CreateAbilityObjectOnDeath>(); component.abilityToInstantiate = Ability.getAbility(AbilityID.summonBloodWraith); component.failsIfFailedAbility = true; // wraith mutator BloodWraithMutator mutator = abilityObject.AddComponent <BloodWraithMutator>(); mutator.statList.AddRange(bloodWraithStats); mutator.increasedSize = increasedBloodWraithSize; } if (moreDamageAgainstBleeding != 0) { // create the conditional DamageConditionalEffect conditionalEffect = new DamageConditionalEffect(); HasStatusEffectConditional conditional = new HasStatusEffectConditional(); conditional.statusEffect = StatusEffectID.Bleed; conditionalEffect.conditional = conditional; conditionalEffect.effect = new DamageEffectMoreDamage(moreDamageAgainstBleeding); // add the conditional to all damage stats holders foreach (DamageStatsHolder holder in abilityObject.GetComponents <DamageStatsHolder>()) { holder.baseDamageStats.conditionalEffects.Add(conditionalEffect); } } // increase radius float totalIncreasedArea = increasedArea; if (increasedArea != 0 || increasedAreaWith3OrMoreMinions != 0) { // calculate total increased area if (increasedAreaWith3OrMoreMinions != 0 && tracker && tracker.summons != null && tracker.summons.Count >= 3) { totalIncreasedArea += increasedAreaWith3OrMoreMinions; } // calculate increased radius float increasedRadius = Mathf.Sqrt(totalIncreasedArea + 1) - 1;; // apply increased radius if (increasedRadius != 0) { foreach (CreateOnDeath cod in abilityObject.GetComponents <CreateOnDeath>()) { cod.increasedRadius = increasedRadius; cod.increasedHeight = increasedRadius; } foreach (SphereCollider col in abilityObject.GetComponents <SphereCollider>()) { col.radius *= (1 + increasedRadius); } foreach (CapsuleCollider col in abilityObject.GetComponents <CapsuleCollider>()) { col.radius *= (1 + increasedRadius); col.height *= (1 + increasedRadius); } } } if (chanceToIgnite > 0) { ChanceToApplyStatusOnEnemyHit newComponent = abilityObject.AddComponent <ChanceToApplyStatusOnEnemyHit>(); newComponent.statusEffect = StatusEffectList.getEffect(StatusEffectID.Ignite); newComponent.chance = chanceToIgnite; } if (addedFireDamage > 0) { foreach (DamageStatsHolder holder in abilityObject.GetComponents <DamageStatsHolder>()) { holder.addBaseDamage(DamageType.FIRE, addedFireDamage); } } if (increasedStunChance != 0) { foreach (DamageStatsHolder holder in abilityObject.GetComponents <DamageStatsHolder>()) { holder.baseDamageStats.increasedStunChance += increasedStunChance; } } // increase damage based on the number of minions float realIncreasedDamage = increasedDamage; if (increasedDamagePerMinion != 0) { if (tracker && tracker.summons != null) { realIncreasedDamage += increasedDamagePerMinion * tracker.summons.Count; } } // increased damage if the minion has more health if (increasedDamageIfDetonatedMinionHasMoreHealth != 0 && tracker) { // get the likely target Summoned targetMinion = tracker.getNearestMinion(targetLocation); if (targetMinion) { // check if it has more health BaseHealth minionHealth = targetMinion.getBaseHealth(); if (minionHealth && baseStats && baseStats.myHealth && minionHealth.currentHealth > baseStats.myHealth.currentHealth) { realIncreasedDamage += increasedDamageIfDetonatedMinionHasMoreHealth; } } } // more damage if you only have one minion if (increasedDamageWithOneMinion != 0) { if (tracker && tracker.numberOfMinions() == 1) { realIncreasedDamage += increasedDamageWithOneMinion; } } // increase damage if (realIncreasedDamage != 0) { foreach (DamageStatsHolder holder in abilityObject.GetComponents <DamageStatsHolder>()) { holder.increaseAllDamage(realIncreasedDamage); } } // chaining (mutator needs to be updated) if (chainsBetweenMinions) { CreateAbilityObjectOnNewAllyHit component = abilityObject.AddComponent <CreateAbilityObjectOnNewAllyHit>(); component.abilityToInstantiate = Ability.getAbility(AbilityID.sacrifice); component.onlyHitCreatorMinions = true; component.aimTowardsHit = true; SacrificeMutator mutator = abilityObject.AddComponent <SacrificeMutator>(); mutator.nova_increasedSpeed = nova_increasedSpeed; mutator.nova_pierces = nova_pierces; mutator.nova_increasedDamage = nova_increasedDamage; mutator.nova_increasedStunChance = nova_increasedStunChance; mutator.nova_bleedChance = nova_bleedChance; mutator.nova_addedCritChance = nova_addedCritChance; mutator.nova_addedCritMultiplier = nova_addedCritMultiplier; mutator.nova_moreDamageAgainstBleeding = nova_moreDamageAgainstBleeding; mutator.boneNovaChance = boneNovaChance; mutator.moreDamageAgainstBleeding = moreDamageAgainstBleeding; mutator.increasedStunChance = increasedStunChance; mutator.chanceToIgnite = chanceToIgnite; mutator.addedFireDamage = addedFireDamage; mutator.bloodWraithChance = bloodWraithChance; mutator.bloodWraithStats = new List <TaggedStatsHolder.TaggableStat>(); mutator.bloodWraithStats.AddRange(bloodWraithStats); mutator.increasedBloodWraithSize = increasedBloodWraithSize; mutator.chainsBetweenMinions = true; // snap shot the damage increase and area increase mutator.increasedDamage = realIncreasedDamage; mutator.increasedArea = totalIncreasedArea; } return(abilityObject); }
protected virtual void OnSummoned() { Summoned?.Invoke(this); }
public void Start() { CreationReferences references = GetComponent <CreationReferences>(); if (references && references.creator) { SummonTracker summonTracker = references.creator.GetComponent <SummonTracker>(); if (summonTracker) { List <Summoned> minionsToUse = new List <Summoned>(); List <Summoned> creatorMinions = summonTracker.summons; if (creatorMinions.Count > 0) { // select minions if (minionsToUseAbility == MinionUseType.Nearest) { Summoned selected = creatorMinions[0]; float selectedDistance = Vector3.Distance(transform.position, creatorMinions[0].transform.position); float distance = 0; foreach (Summoned minion in creatorMinions) { distance = Vector3.Distance(transform.position, minion.transform.position); if (distance < selectedDistance && (!requiresCanUseMovementAbilities || minion.GetComponent <CanUseMovementAbilities>())) { selectedDistance = distance; selected = minion; } } if (!requiresCanUseMovementAbilities || selected.GetComponent <CanUseMovementAbilities>()) { minionsToUse.Add(selected); } } else if (minionsToUseAbility == MinionUseType.Random) { int attempts = 0; Summoned selected = null; while (attempts < 20) { selected = creatorMinions[Random.Range(0, creatorMinions.Count)]; if (!requiresCanUseMovementAbilities || selected.GetComponent <CanUseMovementAbilities>()) { attempts = 100000; minionsToUse.Add(selected); } } } else if (minionsToUseAbility == MinionUseType.All) { if (!requiresCanUseMovementAbilities) { minionsToUse.AddRange(creatorMinions); } else { foreach (Summoned minion in creatorMinions) { if (minion.GetComponent <CanUseMovementAbilities>()) { minionsToUse.Add(minion); } } } } // make the minions use the ability Vector3 targetPostion = GetComponent <LocationDetector>().targetLocation; UsingAbility ua = null; foreach (Summoned minion in minionsToUse) { ua = minion.GetComponent <UsingAbility>(); if (ua) { ua.UseAbility(ability, targetPostion, false, false); ua.transform.LookAt(targetPostion); } } } } } }