/// <summary> /// Loads user definitons for ammo and weapons /// </summary> private static void LoadUserDefinitions() { Type ammoType = typeof(AmmoDefinition); foreach (AmmoDefinition def in KeenAmmoDefinitions) { string filename = $"Ammo_{def.SubtypeId}"; if (FileExistsInWorldStorage(filename, ammoType)) { try { TextReader r = MyAPIGateway.Utilities.ReadFileInWorldStorage(filename, ammoType); AmmoDefinition a = MyAPIGateway.Utilities.SerializeFromXML <AmmoDefinition>(r.ReadToEnd()); r.Close(); Tools.Info($"{((a.Enabled) ? "(ENABLED)" : "(DISABLED)")} Loading user ammo definition {a.SubtypeId}"); if (a.Enabled) { Static.AmmoDefinitions.Add(a); } } catch { Tools.Error($"Failed to load file {def.SubtypeId}"); } } } Type weaponType = typeof(WeaponDefinition); foreach (WeaponDefinition def in KeenWeaponDefinitions) { string filename = $"Weapon_{def.SubtypeId}"; if (FileExistsInWorldStorage(filename, weaponType)) { try { TextReader r = MyAPIGateway.Utilities.ReadFileInWorldStorage(filename, weaponType); WeaponDefinition w = MyAPIGateway.Utilities.SerializeFromXML <WeaponDefinition>(r.ReadToEnd()); r.Close(); Tools.Info($"{((w.Enabled) ? "(ENABLED)" : "(DISABLED)")} Loading user weapon definition {w.SubtypeId}"); if (w.Enabled) { Static.WeaponDefinitions.Add(w); } } catch { Tools.Error($"Failed to load file {def.SubtypeId}"); } } } }
public Projectile(Vector3D origin, Vector3 direction, Vector3D startVelocity, AmmoDefinition ammo, long shooterId) { Ammo = ammo; ShooterId = shooterId; Origin = origin; Position = origin; Direction = direction; float speed = ammo.DesiredSpeed + ((ammo.SpeedVariance > 0) ? MyRandom.Instance.GetRandomFloat(-ammo.SpeedVariance, ammo.SpeedVariance) : 0); Velocity = startVelocity + (Direction * speed); }
private void UpdateAI() { if (!IsValidTarget(Target)) { IsAIShooting = false; Target = TargetAcquisition(); } if (Target != null) { MatrixD muzzleMatrix = gun.GunBase.GetMuzzleWorldMatrix(); string ammoId = gun.GunBase.CurrentAmmoDefinition.Id.SubtypeId.String; AmmoDefinition ammo = Settings.AmmoDefinitionLookup[ammoId]; Vector3D linearVelocity; if (Target is IMyCubeBlock) { linearVelocity = (Target as MyCubeBlock).CubeGrid.Physics.LinearVelocity; } else { linearVelocity = Target.Physics.LinearVelocity; } Vector3D targetPosition; if (Target is IMyCharacter) { targetPosition = Target.PositionComp.WorldVolume.Center + (Target.WorldMatrix.Up * 0.5f); } else { targetPosition = Target.PositionComp.WorldAABB.Center; } Vector3D leadPosition = CalculateProjectileInterceptPosition(ammo.DesiredSpeed, Block.CubeGrid.Physics.LinearVelocity, muzzleMatrix.Translation, linearVelocity, targetPosition); if (IsTargetVisible(Target, leadPosition)) { Tools.AddGPS(CubeBlock.EntityId + 2, leadPosition); float remainingAngle = LookAt(leadPosition); // does not shoot if the angle (radians) is not close to on target IsAIShooting = Math.Abs(remainingAngle) < 0.02; } else { Target = null; } //MyAPIGateway.Utilities.ShowNotification($"{Target.GetType().Name} {(CubeBlock.WorldMatrix.Translation - Target.WorldMatrix.Translation).Length().ToString("n0")} - {remainingAngle.ToString("n5")} - {linearVelocity.ToString("n2")}", 1); } }
public void Copy(AmmoDefinition a) { SubtypeId = a.SubtypeId; DesiredSpeed = a.DesiredSpeed; SpeedVariance = a.SpeedVariance; MaxTrajectory = a.MaxTrajectory; BackkickForce = a.BackkickForce; //PhysicalMaterial = a.PhysicalMaterial; ProjectileHitImpulse = a.ProjectileHitImpulse; ProjectileTrailScale = a.ProjectileTrailScale; ProjectileTrailColor = a.ProjectileTrailColor; ProjectileTrailMaterial = a.ProjectileTrailMaterial; ProjectileTrailProbability = a.ProjectileTrailProbability; ProjectileOnHitEffectName = a.ProjectileOnHitEffectName; ProjectileMassDamage = a.ProjectileMassDamage; ProjectileHealthDamage = a.ProjectileHealthDamage; ProjectileHeadShotDamage = a.ProjectileHeadShotDamage; ProjectileCount = a.ProjectileCount; Ricochet = a.Ricochet; }
/// <summary> /// This converts keens definitions into mostly identical copies that are used within the system. /// </summary> private static void LoadKeenDefinitions() { KeenAmmoDefinitions.Clear(); KeenWeaponDefinitions.Clear(); Type ammoType = typeof(AmmoDefinition); Type weaponType = typeof(WeaponDefinition); foreach (MyDefinitionBase def in MyDefinitionManager.Static.GetAllDefinitions()) { try { if (def is MyAmmoMagazineDefinition) { MyAmmoDefinition ammo = MyDefinitionManager.Static.GetAmmoDefinition((def as MyAmmoMagazineDefinition).AmmoDefinitionId); if (ammo.IsExplosive) { Tools.Info($"Skipping keen ammo definition: {ammo.Id}"); continue; } else { Tools.Info($"Loading keen ammo definition: {ammo.Id}"); } AmmoDefinition a = AmmoDefinition.CreateFromKeenDefinition(ammo as MyProjectileAmmoDefinition); KeenAmmoDefinitions.Add(a); string filename = $"Ammo_{a.SubtypeId}"; if (Static.WriteDefaultDefinitionsToFile && !FileExistsInWorldStorage(filename, ammoType)) { try { AmmoDefinition ca = a.Clone(); ca.Enabled = false; TextWriter writer = MyAPIGateway.Utilities.WriteFileInWorldStorage(filename, ammoType); writer.Write(MyAPIGateway.Utilities.SerializeToXML(ca)); writer.Close(); } catch { Tools.Error($"Unable to write to file {a.SubtypeId}"); } } } else if (def is MyWeaponBlockDefinition) { MyWeaponBlockDefinition block = def as MyWeaponBlockDefinition; MyWeaponDefinition weaponDef = MyDefinitionManager.Static.GetWeaponDefinition(block.WeaponDefinitionId); if (weaponDef.HasMissileAmmoDefined) { Tools.Info($"Skipping keen weapon definition: {block.WeaponDefinitionId}"); continue; } else { Tools.Info($"Loading keen weapon definition: {block.WeaponDefinitionId}"); } // stop vanilla projectile from firing // Thanks for the help Digi for (int i = 0; i < weaponDef.WeaponAmmoDatas.Length; i++) { var ammoData = weaponDef.WeaponAmmoDatas[i]; if (ammoData == null) { continue; } ammoData.ShootIntervalInMiliseconds = int.MaxValue; } WeaponDefinition w = WeaponDefinition.CreateFromKeenDefinition(block, weaponDef); KeenWeaponDefinitions.Add(w); string filename = $"Weapon_{w.SubtypeId}"; if (Static.WriteDefaultDefinitionsToFile && !FileExistsInWorldStorage(filename, weaponType)) { try { WeaponDefinition cw = w.Clone(); cw.Enabled = false; TextWriter writer = MyAPIGateway.Utilities.WriteFileInWorldStorage(filename, weaponType); writer.Write(MyAPIGateway.Utilities.SerializeToXML(cw)); writer.Close(); } catch { Tools.Error($"Unable to write to file {w.SubtypeId}"); } } } } catch (Exception e) { Tools.Error($"Failed to load definition: {def.Id}\n{e}"); } } }
public void Spawn(Vector3D origin, Vector3 direction, Vector3D startVelocity, long shooterId, AmmoDefinition ammo) { lock (projectileCreationLock) { projectileCount++; if (Projectiles.Length < projectileCount) { Projectile[] newArray = new Projectile[Projectiles.Length * 2]; Array.Copy(Projectiles, newArray, Projectiles.Length); Projectiles = newArray; } Projectiles[projectileCount - 1] = new Projectile(origin, direction, startVelocity, ammo, shooterId); } }
/// <summary> /// First call in the update loop /// Used to update the firing state of this weapon /// </summary> public virtual void Update() { // stop looping if not shooting if (!IsShooting) { //if (!IsAnimated) //{ // ControlLayer.NeedsUpdate = VRage.ModAPI.MyEntityUpdateEnum.NONE; //} return; } byte notify = 0x0; DateTime currentTime = DateTime.UtcNow; double timeSinceLastShot = (currentTime - LastShootTime).TotalMilliseconds; double timeSinceLastAmmoFeed = (currentTime - LastAmmoFeed).TotalMilliseconds; if (timeSinceLastAmmoFeed > AmmoFeedInterval) { if ((Inventory.CurrentVolume.RawValue / Inventory.MaxVolume.RawValue) < InventoryFillFactorMin) { InventoryComponent.Fill(CubeBlock, gun.GunBase.CurrentAmmoMagazineId); } } //if (!MyAPIGateway.Utilities.IsDedicated && MyAPIGateway.Session != null) //{ // MyAPIGateway.Utilities.ShowNotification($"ShootTime: {timeSinceLastShot.ToString("n0")}ms - {State.Value} - {IsShooting} - {IsReloading} - {(timeSinceLastShot * (AmmoData.RateOfFire * Tools.MinutesToMilliseconds)).ToString("n2")} {CurrentShotInBurst}/{AmmoData.ShotsInBurst}", 1); //} // Stops if weapons are reloading if (IsReloading) { if (timeSinceLastShot < ReloadTime) { return; } Reloading.Value = false; //State.Value &= ~WeaponState.Reloading; } // Stops if weapons are not ready to fire this frame if (timeSinceLastShot * (AmmoData.RateOfFire * Tools.MinutesToMilliseconds) < 1f) { return; } // Stops if weapons are not working/functional if (!Block.IsWorking) { if (!Block.IsFunctional) { notify |= 0x1; } else { notify |= 0x2; } } else { bool enoughAmmo = gun.GunBase.HasEnoughAmmunition(); if (!enoughAmmo) { StartNoAmmoSound(); notify |= 0x4; } else if (MySessionComponentSafeZones.IsActionAllowed(Block.GetPosition(), Tools.CastProhibit(MySessionComponentSafeZones.AllowedActions, 2))) { // Fixed guns do not update unless the mouse is pressed. // This updates the position when terminal fire is active. if (IsFixedGun) { MyEntitySubpart subpart; if (CubeBlock.Subparts.TryGetValue("Barrel", out subpart)) { gun.GunBase.WorldMatrix = subpart.PositionComp.WorldMatrixRef; } } // NOTE: RateOfFire is limited to 3600 rounds per seconds using this method MatrixD muzzleMatrix = gun.GunBase.GetMuzzleWorldMatrix(); Vector3 direction = muzzleMatrix.Forward; Vector3D origin = muzzleMatrix.Translation; string ammoId = gun.GunBase.CurrentAmmoDefinition.Id.SubtypeId.String; AmmoDefinition ammo = Settings.AmmoDefinitionLookup[ammoId]; // calculate deviation sbyte index = DeviationIndex.Value; MatrixD positionMatrix = Matrix.CreateWorld(origin, Tools.ApplyDeviation(direction, DeviateShotAngle, ref index), muzzleMatrix.Up); DeviationIndex.SetValue(index, SyncType.None); // spawn projectile Core.Static.Spawn(positionMatrix.Translation, positionMatrix.Forward, Block.CubeGrid.Physics.LinearVelocity, Block.EntityId, ammo); //Projectile bullet = new Projectile(CubeBlock.EntityId, positionMatrix.Translation, positionMatrix.Forward, Block.CubeGrid.Physics.LinearVelocity, ammoId); //Core.SpawnProjectile(bullet); gun.GunBase.ConsumeAmmo(); //apply recoil if (ammo.BackkickForce > 0) { //CubeBlock.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_IMPULSE_AND_WORLD_ANGULAR_IMPULSE, -direction * ammo.BackkickForce, CubeBlock.WorldMatrix.Translation, Vector3.Zero); //Core.PhysicsRequests.Enqueue(new PhysicsDefinition { // Target = CubeBlock, // Force = -direction * ammo.BackkickForce, // Position = CubeBlock.WorldMatrix.Translation //}); } // create sound StartShootSound(); //MakeSecondaryShotSound(); // create muzzle flash if (!MyAPIGateway.Utilities.IsDedicated && Settings.Static.DrawMuzzleFlash) { MatrixD matrix = MatrixD.CreateFromDir(direction); matrix.Translation = origin; bool foundParticle = MyParticlesManager.TryCreateParticleEffect(MuzzleFlashSpriteName, ref matrix, ref origin, uint.MaxValue, out muzzleFlash); if (foundParticle) { MuzzleFlashActive = true; MuzzleFlashCurrentTime = 0; muzzleFlash.Play(); } } CurrentShotInBurst++; if (AmmoData.ShotsInBurst == 0) { CurrentShotInBurst = 0; } else if (CurrentShotInBurst == AmmoData.ShotsInBurst) { notify |= 0x8; CurrentShotInBurst = 0; Reloading.Value = true; //State.Value |= WeaponState.Reloading; DeviationIndex.Push(); } if (IsTerminalShootOnce) { State.SetValue(State.Value & ~WeaponState.TerminalShootOnce); } LastShootTime = currentTime; } } if (Notify != notify) { Core.NotifyNextFrame(Block.CubeGrid.EntityId); Notify = notify; } }