protected override bool TryCastShot() { if (caster.def.category == ThingCategory.Pawn) { Pawn launcherPawn = caster as Pawn; AddHediffChance = AddHediffChance * CasterPawn.GetStatValue(StatDefOf.ToxicSensitivity, true); var rand = Rand.Value; // This is a random percentage between 0% and 100% if (rand <= AddHediffChance) // If the percentage falls under the chance, success! { var randomSeverity = Rand.Range(0.05f, 0.15f); var effectOnPawn = launcherPawn?.health?.hediffSet?.GetFirstHediffOfDef(HediffToAdd); if (effectOnPawn != null) { effectOnPawn.Severity += randomSeverity; } else { Hediff hediff = HediffMaker.MakeHediff(HediffToAdd, launcherPawn, null); hediff.Severity = randomSeverity; launcherPawn.health.AddHediff(hediff, null, null); } } } return(base.TryCastShot()); }
private float GetNonMissChance(LocalTargetInfo target) { if (surpriseAttack) { return(1f); } if (IsTargetImmobile(target)) { return(1f); } return(CasterPawn.GetStatValue(StatDefOf.MeleeHitChance)); }
public bool MesaTryStartCastOn(LocalTargetInfo castTarg, bool attack = false, bool canHitNonTargetPawns = true) { if (newWarmupTime > verbProps.warmupTime) { newWarmupTime = verbProps.warmupTime; } if (caster == null) { Log.Error("Verb " + GetUniqueLoadID() + " needs caster to work (possibly lost during saving/loading)."); return(false); } if (!caster.Spawned) { return(false); } if (state == VerbState.Bursting || !CanHitTarget(castTarg)) { return(false); } if (CausesTimeSlowdown(castTarg)) { Find.TickManager.slower.SignalForceNormalSpeed(); } surpriseAttack = attack; canHitNonTargetPawnsNow = canHitNonTargetPawns; currentTarget = castTarg; if (CasterIsPawn && verbProps.warmupTime > 0f) { if (!TryFindShootLineFromTo(caster.Position, castTarg, out var newShootLine)) { return(false); } CasterPawn.Drawer.Notify_WarmingCastAlongLine(newShootLine, caster.Position); var statValue = CasterPawn.GetStatValue(StatDefOf.AimingDelayFactor); var ticks = (newWarmupTime * statValue).SecondsToTicks(); CasterPawn.stances.SetStance(new Stance_Warmup(ticks, castTarg, this)); } else { WarmupComplete(); } return(true); }
// unmodified private float GetHitChance(LocalTargetInfo target) { if (surpriseAttack) { return(1f); } if (IsTargetImmobile(target)) { return(1f); } if (CasterPawn.skills != null) { return(CasterPawn.GetStatValue(StatDefOf.MeleeHitChance, true)); } return(DefaultHitChance); }
public bool PreCastShotCheck(LocalTargetInfo castTarg, LocalTargetInfo destTarg, bool surpriseAttack, bool canHitNonTargetPawns) { if (caster == null) { Log.Error("Verb " + GetUniqueLoadID() + " needs caster to work (possibly lost during saving/loading)."); return(false); } if (!caster.Spawned) { return(false); } if (state == VerbState.Bursting || (!CanHitTarget(castTarg) && verbProps.requireLineOfSight)) { return(false); } if (CausesTimeSlowdown(castTarg)) { Find.TickManager.slower.SignalForceNormalSpeed(); } this.surpriseAttack = surpriseAttack; this.canHitNonTargetPawnsNow = canHitNonTargetPawns; this.currentTarget = castTarg; this.currentDestination = destTarg; if (CasterIsPawn && verbProps.warmupTime > 0f) { if (verbProps.requireLineOfSight) { ShootLine resultingLine; if (!TryFindShootLineFromTo(caster.Position, castTarg, out resultingLine)) { Messages.Message("AU_NoLineOfSight".Translate(), MessageTypeDefOf.RejectInput); return(false); } CasterPawn.Drawer.Notify_WarmingCastAlongLine(resultingLine, caster.Position); } float statValue = CasterPawn.GetStatValue(StatDefOf.AimingDelayFactor); int ticks = (verbProps.warmupTime * statValue).SecondsToTicks(); CasterPawn.stances.SetStance(new Stance_Warmup(ticks, castTarg, this)); } else { WarmupComplete(); } return(true); }
public override bool ValidateTarget(LocalTargetInfo target) { if (!base.ValidateTarget(target)) { return(false); } if (CasterPawn.GetStatValue(StatDefOf.PsychicSensitivity) < float.Epsilon) { Messages.Message("CommandPsycastZeroPsychicSensitivity".Translate(), caster, MessageTypeDefOf.RejectInput); return(false); } if (Psycast.def.EntropyGain > float.Epsilon && CasterPawn.psychicEntropy.WouldOverflowEntropy(Psycast.def.EntropyGain + PsycastUtility.TotalEntropyFromQueuedPsycasts(CasterPawn))) { Messages.Message("CommandPsycastWouldExceedEntropy".Translate(), caster, MessageTypeDefOf.RejectInput); return(false); } return(true); }
public virtual bool TryStartCastOn(GlobalTargetInfo castTarg, float heading, bool surpriseAttack = false, bool canHitNonTargetPawns = true) { if (caster is null) { Log.Error("Verb " + GetUniqueLoadID() + " needs caster to work (possibly lost during saving/loading).", false); return(false); } if (!caster.Spawned) { return(false); } if (state == VerbState.Bursting || !CanHitTarget(castTarg)) { return(false); } this.heading = heading; IntVec3 exitTarget = caster.Position.CellFromDistAngle(Building_Artillery.MaxMapDistance, this.heading); if (CausesTimeSlowdown(castTarg)) { Find.TickManager.slower.SignalForceNormalSpeed(); } this.surpriseAttack = surpriseAttack; canHitNonTargetPawnsNow = canHitNonTargetPawns; target = castTarg; if (CasterIsPawn && verbProps.warmupTime > 0f) { CasterPawn.Drawer.Notify_WarmingCastAlongLine(new ShootLine(caster.Position, exitTarget), caster.Position); float statValue = CasterPawn.GetStatValue(StatDefOf.AimingDelayFactor, true); int ticks = (verbProps.warmupTime * statValue).SecondsToTicks(); CasterPawn.stances.SetStance(new Stance_Warmup(ticks, exitTarget, this)); } else { WarmupComplete(); } return(true); }
/// <summary> /// Calculates primary DamageInfo from verb, as well as secondary DamageInfos to apply (i.e. surprise attack stun damage). /// Also calculates the maximum body height an attack can target, so we don't get rabbits biting out a colonist's eye or something. /// </summary> /// <param name="target">The target damage is to be applied to</param> /// <returns>Collection with primary DamageInfo, followed by secondary types</returns> private IEnumerable <DamageInfo> DamageInfosToApply(LocalTargetInfo target, bool isCrit = false) { //START 1:1 COPY Verb_MeleeAttack.DamageInfosToApply float damAmount = verbProps.AdjustedMeleeDamageAmount(this, CasterPawn); var critModifier = isCrit && verbProps.meleeDamageDef.armorCategory == DamageArmorCategoryDefOf.Sharp && !CasterPawn.def.race.Animal ? 2 : 1; var armorPenetration = (verbProps.meleeDamageDef.armorCategory == DamageArmorCategoryDefOf.Sharp ? ArmorPenetrationSharp : ArmorPenetrationBlunt) * critModifier; DamageDef damDef = verbProps.meleeDamageDef; BodyPartGroupDef bodyPartGroupDef = null; HediffDef hediffDef = null; if (EquipmentSource != null) { //melee weapon damage variation damAmount *= Rand.Range(StatWorker_MeleeDamage.GetDamageVariationMin(CasterPawn), StatWorker_MeleeDamage.GetDamageVariationMax(CasterPawn)); } else if (!CE_StatDefOf.UnarmedDamage.Worker.IsDisabledFor(CasterPawn)) //ancient soldiers can punch even if non-violent, this prevents the disabled stat from being used { //unarmed damage bonus offset damAmount += CasterPawn.GetStatValue(CE_StatDefOf.UnarmedDamage); } if (CasterIsPawn) { bodyPartGroupDef = verbProps.AdjustedLinkedBodyPartsGroup(tool); if (damAmount >= 1f) { if (HediffCompSource != null) { hediffDef = HediffCompSource.Def; } } else { damAmount = 1f; damDef = DamageDefOf.Blunt; } } var source = EquipmentSource != null ? EquipmentSource.def : CasterPawn.def; Vector3 direction = (target.Thing.Position - CasterPawn.Position).ToVector3(); DamageDef def = damDef; //END 1:1 COPY BodyPartHeight bodyRegion = GetBodyPartHeightFor(target); //Custom // Add check for body height //START 1:1 COPY DamageInfo mainDinfo = new DamageInfo(def, damAmount, armorPenetration, -1f, caster, null, source, DamageInfo.SourceCategory.ThingOrUnknown, null); //Alteration // Predators get a neck bite on immobile targets if (caster.def.race.predator && IsTargetImmobile(target) && target.Thing is Pawn pawn) { var neck = pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Top, BodyPartDepth.Outside) .FirstOrDefault(r => r.def == BodyPartDefOf.Neck); mainDinfo.SetHitPart(neck); } mainDinfo.SetBodyRegion(bodyRegion); //Alteration mainDinfo.SetWeaponBodyPartGroup(bodyPartGroupDef); mainDinfo.SetWeaponHediff(hediffDef); mainDinfo.SetAngle(direction); yield return(mainDinfo); DamageInfo damageInfo = new DamageInfo(def, damAmount, armorPenetration, -1f, this.caster, null, source, DamageInfo.SourceCategory.ThingOrUnknown, null); damageInfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside); damageInfo.SetWeaponBodyPartGroup(bodyPartGroupDef); damageInfo.SetWeaponHediff(hediffDef); damageInfo.SetAngle(direction); yield return(damageInfo); if (this.tool != null && this.tool.extraMeleeDamages != null) { foreach (ExtraDamage extraDamage in this.tool.extraMeleeDamages) { if (Rand.Chance(extraDamage.chance)) { damAmount = extraDamage.amount; damAmount = Rand.Range(damAmount * 0.8f, damAmount * 1.2f); damageInfo = new DamageInfo(extraDamage.def, damAmount, extraDamage.AdjustedArmorPenetration(this, this.CasterPawn), -1f, this.caster, null, source, DamageInfo.SourceCategory.ThingOrUnknown, null); damageInfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside); damageInfo.SetWeaponBodyPartGroup(bodyPartGroupDef); damageInfo.SetWeaponHediff(hediffDef); damageInfo.SetAngle(direction); yield return(damageInfo); } } List <ExtraDamage> .Enumerator enumerator = default(List <ExtraDamage> .Enumerator); } // Apply critical damage if (isCrit && !CasterPawn.def.race.Animal && verbProps.meleeDamageDef.armorCategory != DamageArmorCategoryDefOf.Sharp && target.Thing.def.race.IsFlesh) { var critAmount = GenMath.RoundRandom(mainDinfo.Amount * 0.25f); var critDinfo = new DamageInfo(DamageDefOf.Stun, critAmount, armorPenetration, -1, caster, null, source); critDinfo.SetBodyRegion(bodyRegion, BodyPartDepth.Outside); critDinfo.SetWeaponBodyPartGroup(bodyPartGroupDef); critDinfo.SetWeaponHediff(hediffDef); critDinfo.SetAngle(direction); yield return(critDinfo); } }
protected override int TicksToNextHit() { return((int)Math.Round(BaseTicksBetweenPickHits / (double)CasterPawn.GetStatValue(StatDefOf.MiningSpeed))); }