private bool TryFindAmmoInInventory(out Thing ammoThing) { ammoThing = null; if (CompInventory == null) { return(false); } // Try finding suitable ammoThing for currently set ammo first ammoThing = CompInventory.ammoList.Find(thing => thing.def == selectedAmmo); if (ammoThing != null) { return(true); } // Try finding ammo from different type foreach (AmmoLink link in Props.ammoSet.ammoTypes) { ammoThing = CompInventory.ammoList.Find(thing => thing.def == link.ammo); if (ammoThing != null) { selectedAmmo = (AmmoDef)link.ammo; return(true); } } return(false); }
public override void Initialize(CompProperties vprops) { base.Initialize(vprops); //spawnUnloaded checks have all been moved to methods calling ResetAmmoCount. //curMagCountInt = Props.spawnUnloaded && UseAmmo ? 0 : Props.magazineSize; // Initialize ammo with default if none is set if (UseAmmo) { if (Props.ammoSet.ammoTypes.NullOrEmpty()) { Log.Error(parent.Label + " has no available ammo types"); } else { if (currentAmmoInt == null) { currentAmmoInt = (AmmoDef)Props.ammoSet.ammoTypes[0].ammo; } if (selectedAmmo == null) { selectedAmmo = currentAmmoInt; } } } }
public override void Initialize(CompProperties vprops) { base.Initialize(vprops); curMagCountInt = Props.spawnUnloaded ? 0 : Props.magazineSize; // Initialize ammo with default if none is set if (useAmmo) { if (Props.ammoSet.ammoTypes.NullOrEmpty()) { Log.Error(parent.Label + " has no available ammo types"); } else { if (currentAmmoInt == null) { currentAmmoInt = (AmmoDef)Props.ammoSet.ammoTypes[0]; } if (selectedAmmo == null) { selectedAmmo = currentAmmoInt; } } } }
public bool TryFindAmmoInInventory(out Thing ammoThing) { ammoThing = null; if (CompInventory == null) { return(false); } // Try finding suitable ammoThing for currently set ammo first ammoThing = CompInventory.ammoList.Find(thing => thing.def == selectedAmmo); if (ammoThing != null) { return(true); } if (Props.reloadOneAtATime && CurMagCount > 0) { //Current mag already has a few rounds in, and the inventory doesn't have any more of that type. //If we let this method pick a new selectedAmmo below, it would convert the already loaded rounds to a different type, //so for OneAtATime weapons, we stop the process here here. return(false); } // Try finding ammo from different type foreach (AmmoLink link in Props.ammoSet.ammoTypes) { ammoThing = CompInventory.ammoList.Find(thing => thing.def == link.ammo); if (ammoThing != null) { selectedAmmo = (AmmoDef)link.ammo; return(true); } } return(false); }
public static bool IsAmmoSystemActive(AmmoDef def) { if (Controller.settings.EnableAmmoSystem) { return(true); } return(def != null && def.isMortarAmmo); }
/// <summary> /// Resets current ammo count to a full magazine. Intended use is pawn/turret generation where we want raiders/enemy turrets to spawn with loaded magazines. DO NOT /// use for regular reloads, those should be handled through LoadAmmo() instead. /// </summary> /// <param name="newAmmo">Currently loaded ammo type will be set to this, null will load currently selected type.</param> public void ResetAmmoCount(AmmoDef newAmmo = null) { if (newAmmo != null) { currentAmmoInt = newAmmo; } curMagCountInt = Props.magazineSize; }
/// <summary> /// <para>Reduces ammo count and updates inventory if necessary, call this whenever ammo is consumed by the gun (e.g. firing a shot, clearing a jam). </para> /// <para>Has an optional argument for the amount of ammo to consume per shot, which defaults to 1; this caters for special cases such as different sci-fi weapons using up different amounts of the same energy cell ammo type per shot, or a double-barrelled shotgun that fires both cartridges at the same time (projectile treated as a single, more powerful bullet)</para> /// </summary> public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1) { ammoConsumedPerShot = (ammoConsumedPerShot > 0) ? ammoConsumedPerShot : 1; if (Wielder == null && turret == null) { Log.Error(parent.ToString() + " tried reducing its ammo count without a wielder"); } // Mag-less weapons feed directly from inventory if (!HasMagazine) { if (UseAmmo) { if (!TryFindAmmoInInventory(out ammoToBeDeleted)) { return(false); } if (ammoToBeDeleted.def != CurrentAmmo) { currentAmmoInt = ammoToBeDeleted.def as AmmoDef; } if (ammoToBeDeleted.stackCount > 1) { ammoToBeDeleted = ammoToBeDeleted.SplitOff(1); } } return(true); } // If magazine is empty, return false if (curMagCountInt <= 0) { CurMagCount = 0; return(false); } // Reduce ammo count and update inventory CurMagCount = (curMagCountInt - ammoConsumedPerShot < 0) ? 0 : curMagCountInt - ammoConsumedPerShot; /*if (curMagCountInt - ammoConsumedPerShot < 0) * { * curMagCountInt = 0; * } else * { * curMagCountInt = curMagCountInt - ammoConsumedPerShot; * }*/ // Original: curMagCountInt--; if (curMagCountInt < 0) { TryStartReload(); } return(true); }
protected override float GetWeightForDef(ThingDef def) { float weight = 1; AmmoDef ammo = def as AmmoDef; if (ammo != null && ammo.ammoClass.advanced) { weight *= 0.2f; } return(weight); }
/// <summary> /// Reduces ammo count and updates inventory if necessary, call this whenever ammo is consumed by the gun (e.g. firing a shot, clearing a jam) /// </summary> public bool TryReduceAmmoCount() { if (Wielder == null && turret == null) { Log.Error(parent.ToString() + " tried reducing its ammo count without a wielder"); } // Mag-less weapons feed directly from inventory if (!HasMagazine) { if (UseAmmo) { if (!TryFindAmmoInInventory(out ammoToBeDeleted)) { return(false); } if (ammoToBeDeleted.def != CurrentAmmo) { currentAmmoInt = ammoToBeDeleted.def as AmmoDef; } if (ammoToBeDeleted.stackCount > 1) { ammoToBeDeleted = ammoToBeDeleted.SplitOff(1); } } return(true); } // If magazine is empty, return false if (curMagCountInt <= 0) { curMagCountInt = 0; return(false); } // Reduce ammo count and update inventory curMagCountInt--; if (CompInventory != null) { CompInventory.UpdateInventory(); } if (curMagCountInt < 0) { TryStartReload(); } return(true); }
private void LoadWeaponWithRandAmmo(ThingWithComps gun) { CompAmmoUser compAmmo = gun.TryGetComp <CompAmmoUser>(); if (compAmmo == null) { return; } if (!compAmmo.UseAmmo) { compAmmo.ResetAmmoCount(); return; } // Determine ammo IEnumerable <AmmoDef> availableAmmo = compAmmo.Props.ammoSet.ammoTypes.Where(a => a.ammo.alwaysHaulable).Select(a => a.ammo); //Running out of options. alwaysHaulable does exist in xml. AmmoDef ammoToLoad = availableAmmo.RandomElementByWeight(a => a.generateCommonality); compAmmo.ResetAmmoCount(ammoToLoad); }
private FloatMenu MakeAmmoMenu() { List <ThingDef> ammoList = new List <ThingDef>(); // List of all ammo types the gun can use and the pawn has in his inventory if (compAmmo.turret != null) { // If we have no inventory available (e.g. manned turret), add all possible ammo types to the selection ammoList.AddRange(compAmmo.Props.ammoSet.ammoTypes); } else { // Iterate through all suitable ammo types and check if they're in our inventory foreach (ThingDef curAmmoDef in compAmmo.Props.ammoSet.ammoTypes) { if (compAmmo.compInventory.ammoList.Any(x => x.def == curAmmoDef)) { ammoList.Add(curAmmoDef); } } } // Append float menu options for every available ammo type List <FloatMenuOption> floatOptionList = new List <FloatMenuOption>(); if (ammoList.NullOrEmpty()) { floatOptionList.Add(new FloatMenuOption("CE_OutOfAmmo".Translate(), null)); } else { foreach (ThingDef curDef in ammoList) { AmmoDef ammoDef = (AmmoDef)curDef; floatOptionList.Add(new FloatMenuOption(ammoDef.ammoClass.LabelCap, new Action(delegate { compAmmo.selectedAmmo = ammoDef; }))); } } return(new FloatMenu(floatOptionList)); }
public void LoadAmmo(Thing ammo = null) { if (Holder == null && turret == null) { Log.Error(parent.ToString() + " tried loading ammo with no owner"); return; } int newMagCount; if (UseAmmo) { Thing ammoThing; bool ammoFromInventory = false; if (ammo == null) { if (!TryFindAmmoInInventory(out ammoThing)) { DoOutOfAmmoAction(); return; } ammoFromInventory = true; } else { ammoThing = ammo; } currentAmmoInt = (AmmoDef)ammoThing.def; if (Props.magazineSize < ammoThing.stackCount) { newMagCount = Props.magazineSize; ammoThing.stackCount -= Props.magazineSize; if (CompInventory != null) { CompInventory.UpdateInventory(); } } else { newMagCount = ammoThing.stackCount; if (ammoFromInventory) { CompInventory.container.Remove(ammoThing); } else if (!ammoThing.Destroyed) { ammoThing.Destroy(); } } } else { newMagCount = Props.magazineSize; } curMagCountInt = newMagCount; if (turret != null) { turret.isReloading = false; } if (parent.def.soundInteract != null) { parent.def.soundInteract.PlayOneShot(new TargetInfo(Position, Find.VisibleMap, false)); } }
public void LoadAmmo(Thing ammo = null) { if (Holder == null && turret == null) { Log.Error(parent.ToString() + " tried loading ammo with no owner"); return; } int newMagCount; if (UseAmmo) { Thing ammoThing; bool ammoFromInventory = false; if (ammo == null) { if (!TryFindAmmoInInventory(out ammoThing)) { DoOutOfAmmoAction(); return; } ammoFromInventory = true; } else { ammoThing = ammo; } currentAmmoInt = (AmmoDef)ammoThing.def; // If there's more ammo in inventory than the weapon can hold, or if there's greater than 1 bullet in inventory if reloading one at a time if ((Props.reloadOneAtATime ? 1 : Props.magazineSize) < ammoThing.stackCount) { if (Props.reloadOneAtATime) { newMagCount = curMagCountInt + 1; ammoThing.stackCount--; } else { newMagCount = Props.magazineSize; ammoThing.stackCount -= Props.magazineSize; } } // If there's less ammo in inventory than the weapon can hold, or if there's only one bullet left if reloading one at a time else { newMagCount = (Props.reloadOneAtATime) ? curMagCountInt + 1 : ammoThing.stackCount; if (ammoFromInventory) { CompInventory.container.Remove(ammoThing); } else if (!ammoThing.Destroyed) { ammoThing.Destroy(); } } } else { newMagCount = (Props.reloadOneAtATime) ? (curMagCountInt + 1) : Props.magazineSize; } CurMagCount = newMagCount; if (turret != null) { turret.isReloading = false; } if (parent.def.soundInteract != null) { parent.def.soundInteract.PlayOneShot(new TargetInfo(Position, Find.CurrentMap, false)); } }
/// <summary> /// Similar to ThingContainer.TotalStackCountOfDef(), returns the count of all matching AmmoDefs in AmmoList cache. /// </summary> /// <param name="def">ThingDef to count.</param> /// <returns>int amount of AmmoDef found in AmmoList.</returns> public int AmmoCountOfDef(AmmoDef def) { return(ammoListCached.Where(t => t.def == def).Sum(t => t.stackCount)); }
/* Rough Algorithm * Need Things so the collection of ammo users that use magazines. Also need a collection of ammo (ThingDef is OK here). * For each weapon (that fits above), * -If we have no ammo in inventory that the gun is loaded with, check loadouts/holdtracker for a clip's worth of ammo that the gun contains. * --Find ammo the gun uses that we have a clip's worth in inventory (should check it against loadout/holdtracker as well) * -If weapon is low on ammo and we have enough in inventory to fill it up. * * If either of the above are true, trigger a reload. */ /// <summary> /// Check's the pawn's equipment and inventory for weapons that could use a reload. /// </summary> /// <param name="pawn">Pawn to check the equipment and inventory of.</param> /// <param name="reloadWeapon">Thing weapon which needs to be reloaded.</param> /// <param name="reloadAmmo">AmmoDef ammo to reload the gun with.</param> /// <returns>Bool value indicating if the job needs to be done.</returns> private bool DoReloadCheck(Pawn pawn, out ThingWithComps reloadWeapon, out AmmoDef reloadAmmo) { reloadWeapon = null; reloadAmmo = null; // First need to create the collections that will be searched. List <ThingWithComps> guns = new List <ThingWithComps>(); CompInventory inventory = pawn.TryGetComp <CompInventory>(); CompAmmoUser tmpComp; Loadout loadout = pawn.GetLoadout(); bool pawnHasLoadout = loadout != null && !loadout.Slots.NullOrEmpty(); if (inventory == null) { return(false); // There isn't any work to do since the pawn doesn't have a CE Inventory. } if ((tmpComp = pawn.equipment?.Primary?.TryGetComp <CompAmmoUser>()) != null && tmpComp.HasMagazine) { guns.Add(pawn.equipment.Primary); } // CompInventory doesn't track equipment and it's desired to check the pawn's equipped weapon before inventory items so need to copy stuff from Inventory Cache. guns.AddRange(inventory.rangedWeaponList.Where(t => t.TryGetComp <CompAmmoUser>() != null && t.GetComp <CompAmmoUser>().HasMagazine)); if (guns.NullOrEmpty()) { return(false); // There isn't any work to do since the pawn doesn't have any ammo using guns. } // look at each gun... foreach (ThingWithComps gun in guns) { // Get key stats of the weapon. tmpComp = gun.TryGetComp <CompAmmoUser>(); AmmoDef ammoType = tmpComp.CurrentAmmo; int ammoAmount = tmpComp.CurMagCount; int magazineSize = tmpComp.Props.magazineSize; // Is the gun loaded with ammo not in a Loadout/HoldTracker? if (tmpComp.UseAmmo && pawnHasLoadout && !TrackingSatisfied(pawn, ammoType, magazineSize)) { // Do we have ammo in the inventory that the gun uses which satisfies requirements? (expensive) AmmoDef matchAmmo = tmpComp.Props.ammoSet.ammoTypes .Where(al => al.ammo != ammoType) .Select(al => al.ammo) .FirstOrDefault(ad => TrackingSatisfied(pawn, ad, magazineSize) && inventory.AmmoCountOfDef(ad) >= magazineSize); if (matchAmmo != null) { reloadWeapon = gun; reloadAmmo = matchAmmo; return(true); } } // Is the gun low on ammo? if (tmpComp.CurMagCount < magazineSize) { // Do we have enough ammo in the inventory to top it off? if (!tmpComp.UseAmmo || inventory.AmmoCountOfDef(ammoType) >= (magazineSize - tmpComp.CurMagCount)) { reloadWeapon = gun; reloadAmmo = ammoType; return(true); //} else { // (ProfoundDarkness) I think the idea of this branch was that the JobGiver might initiate fetching ammo but it actually runs AFTER the one that fetches ammo for loadout. // There wasn't enough in the inventory to top it off. At this point we know the loadout is satisfied for this ammo... // We could do a more strict check to see if the pawn's loadout is satisfied to pick up ammo and if not swap to another ammo...? } } } return(false); }
public static bool InjectAmmos() { // Initialize list of all weapons so we don't have to iterate through all the defs, all the time CE_Utility.allWeaponDefs.Clear(); foreach (ThingDef def in DefDatabase <ThingDef> .AllDefsListForReading) { if (def.IsWeapon && (def.canBeSpawningInventory || def.tradeability == Tradeability.Stockable || def.weaponTags.Contains("TurretGun"))) { CE_Utility.allWeaponDefs.Add(def); } } if (CE_Utility.allWeaponDefs.NullOrEmpty()) { Log.Warning("CE Ammo Injector found no weapon defs"); return(true); } // Find all ammo using guns foreach (ThingDef weaponDef in CE_Utility.allWeaponDefs) { CompProperties_AmmoUser props = weaponDef.GetCompProperties <CompProperties_AmmoUser>(); if (props != null && props.ammoSet != null && !props.ammoSet.ammoTypes.NullOrEmpty()) { foreach (ThingDef curDef in props.ammoSet.ammoTypes) { AmmoDef ammoDef = curDef as AmmoDef; if (ammoDef != null) { // Enable trading if (ammoDef.tradeTags.Contains(enableTradeTag)) { ammoDef.tradeability = Tradeability.Stockable; } // Enable recipe if (ammoDef.tradeTags.Contains(enableCraftingTag)) { RecipeDef recipe = DefDatabase <RecipeDef> .GetNamed(("Make" + ammoDef.defName), false); if (recipe == null) { Log.Error("CE ammo injector found no recipe named Make" + ammoDef.defName); } else { if (ammoCraftingStation == null) { Log.ErrorOnce("CE ammo injector crafting station is null", 84653201); } else { if (!recipe.recipeUsers.Contains(ammoCraftingStation)) { recipe.recipeUsers.Add(ammoCraftingStation); } } } } } } } } return(true); }
protected override IEnumerable <Toil> MakeNewToils() { // Error checking/input validation. if (turret == null) { Log.Error(string.Concat(errorBase, "TargetThingA isn't a Building_TurretGunCE")); yield return(null); } if (compReloader == null) { Log.Error(string.Concat(errorBase, "TargetThingA (Building_TurretGunCE) is missing its CompAmmoUser.")); yield return(null); } if (compReloader.UseAmmo && ammo == null) { Log.Error(string.Concat(errorBase, "TargetThingB is either null or not an AmmoThing.")); yield return(null); } AddEndCondition(delegate { return((pawn.Downed || pawn.Dead || pawn.InMentalState || pawn.IsBurning()) ? JobCondition.Incompletable : JobCondition.Ongoing); }); this.FailOnIncapable(PawnCapacityDefOf.Manipulation); // Set fail condition on turret. if (pawn.Faction != Faction.OfPlayer) { this.FailOnDestroyedOrNull(TargetIndex.A); } else { this.FailOnDestroyedNullOrForbidden(TargetIndex.A); } // Perform ammo system specific activities, failure condition and hauling if (compReloader.UseAmmo) { var toilGoToCell = Toils_Goto.GotoCell(ammo.Position, PathEndMode.Touch).FailOnBurningImmobile(TargetIndex.B); var toilCarryThing = Toils_Haul.StartCarryThing(TargetIndex.B).FailOnBurningImmobile(TargetIndex.B); if (TargetThingB is AmmoThing) { toilGoToCell.AddEndCondition(delegate { return((TargetThingB as AmmoThing).IsCookingOff ? JobCondition.Incompletable : JobCondition.Ongoing); }); toilCarryThing.AddEndCondition(delegate { return((TargetThingB as AmmoThing).IsCookingOff ? JobCondition.Incompletable : JobCondition.Ongoing); }); } if (pawn.Faction != Faction.OfPlayer) { ammo.SetForbidden(true, false); toilGoToCell.FailOnDestroyedOrNull(TargetIndex.B); toilCarryThing.FailOnDestroyedOrNull(TargetIndex.B); } else { toilGoToCell.FailOnDestroyedNullOrForbidden(TargetIndex.B); toilCarryThing.FailOnDestroyedNullOrForbidden(TargetIndex.B); } //yield return Toils_Reserve.Reserve(TargetIndex.B, Mathf.Max(1, TargetThingB.stackCount - job.count), job.count); yield return(toilGoToCell); yield return(toilCarryThing); //yield return Toils_Haul.PlaceHauledThingInCell(TargetIndex.A, null, false); } // If ammo system is turned off we just need to go to the turret. yield return(Toils_Goto.GotoCell(turret.Position, PathEndMode.Touch)); //If pawn fails reloading from this point, reset isReloading this.AddFinishAction(delegate { turret.isReloading = false; }); // Wait in place Toil waitToil = new Toil() { actor = pawn }; waitToil.initAction = delegate { // Initial relaod process activities. turret.isReloading = true; waitToil.actor.pather.StopDead(); if (compReloader.ShouldThrowMote) { MoteMaker.ThrowText(turret.Position.ToVector3Shifted(), turret.Map, string.Format("CE_ReloadingTurretMote".Translate(), TargetThingA.LabelCapNoCount)); } //Thing newAmmo; //compReloader.TryUnload(out newAmmo); //if (newAmmo?.CanStackWith(ammo) ?? false) //{ // pawn.carryTracker.TryStartCarry(newAmmo, Mathf.Min(newAmmo.stackCount, compReloader.Props.magazineSize - ammo.stackCount)); //} AmmoDef currentAmmo = compReloader.CurrentAmmo; if (currentAmmo != ammo?.def) //Turrets are reloaded without unloading the mag first (if using same ammo type), to support very high capacity magazines { compReloader.TryUnload(out Thing newAmmo); } }; waitToil.defaultCompleteMode = ToilCompleteMode.Delay; waitToil.defaultDuration = Mathf.CeilToInt(compReloader.Props.reloadTime.SecondsToTicks() / pawn.GetStatValue(CE_StatDefOf.ReloadSpeed)); yield return(waitToil.WithProgressBarToilDelay(TargetIndex.A)); //Actual reloader Toil reloadToil = new Toil(); reloadToil.defaultCompleteMode = ToilCompleteMode.Instant; reloadToil.initAction = delegate { compReloader.LoadAmmo(ammo); turret.isReloading = false; }; //if (compReloader.useAmmo) reloadToil.EndOnDespawnedOrNull(TargetIndex.B); yield return(reloadToil); }
private FloatMenu MakeAmmoMenu() { List <ThingDef> ammoList = new List <ThingDef>(); // List of all ammo types the gun can use and the pawn has in his inventory if (compAmmo.turret != null) { // If we have no inventory available (e.g. manned turret), add all possible ammo types to the selection foreach (AmmoLink link in compAmmo.Props.ammoSet.ammoTypes) { ammoList.Add(link.ammo); } } else { // Iterate through all suitable ammo types and check if they're in our inventory foreach (AmmoLink curLink in compAmmo.Props.ammoSet.ammoTypes) { if (compAmmo.CompInventory.ammoList.Any(x => x.def == curLink.ammo)) { ammoList.Add(curLink.ammo); } } } // Append float menu options for every available ammo type List <FloatMenuOption> floatOptionList = new List <FloatMenuOption>(); if (ammoList.NullOrEmpty()) { floatOptionList.Add(new FloatMenuOption("CE_OutOfAmmo".Translate(), null)); } else { // Append all available ammo types foreach (ThingDef curDef in ammoList) { AmmoDef ammoDef = (AmmoDef)curDef; floatOptionList.Add(new FloatMenuOption(ammoDef.ammoClass.LabelCap, new Action(delegate { bool shouldReload = Controller.settings.AutoReloadOnChangeAmmo && (compAmmo.SelectedAmmo != ammoDef || compAmmo.CurMagCount < compAmmo.Props.magazineSize) && compAmmo.turret?.MannableComp == null; compAmmo.SelectedAmmo = ammoDef; if (shouldReload) { if (compAmmo.turret != null) { compAmmo.turret.TryOrderReload(); } else { compAmmo.TryStartReload(); } } }))); } } // Append unload command var hasOperator = compAmmo.Wielder != null || (compAmmo.turret?.MannableComp?.MannedNow ?? false); if (compAmmo.UseAmmo && hasOperator && compAmmo.HasMagazine && compAmmo.CurMagCount > 0) { floatOptionList.Add(new FloatMenuOption("CE_UnloadLabel".Translate(), new Action(delegate { compAmmo.TryUnload(); }))); } // Append reload command if (compAmmo.HasMagazine && hasOperator) { floatOptionList.Add(new FloatMenuOption("CE_ReloadLabel".Translate(), new Action(action))); } return(new FloatMenu(floatOptionList)); }
public AmmoLink(AmmoDef ammo, ThingDef projectile) { this.ammo = ammo; this.projectile = projectile; }