4 using FarseerPhysics.Dynamics;
5 using Microsoft.Xna.Framework;
7 using System.Collections.Generic;
17 private Vector2 barrelPos;
19 [
Serialize(
"0.0,0.0",
IsPropertySaveable.No, description:
"The position of the barrel as an offset from the item's center (in pixels). Determines where the projectiles spawn.")]
22 get {
return XMLExtensions.Vector2ToString(ConvertUnits.ToDisplayUnits(barrelPos)); }
23 set { barrelPos = ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(value)); }
26 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"How long the user has to wait before they can fire the weapon again (in seconds).")]
29 get {
return reload; }
30 set { reload = Math.Max(value, 0.0f); }
40 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"Reload time at 0 skill level. Reload time scales with skill level up to the Weapons skill requirement.")]
61 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"Random spread applied to the firing angle of the projectiles when used by a character with sufficient skills to use the weapon (in degrees).")]
68 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"Random spread applied to the firing angle of the projectiles when used by a character with insufficient skills to use the weapon (in degrees).")]
75 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"The impulse applied to the physics body of the projectile (the higher the impulse, the faster the projectiles are launched). Sum of weapon + projectile.")]
82 [
Serialize(0.0f,
IsPropertySaveable.Yes, description:
"Percentage of damage mitigation ignored when hitting armored body parts (deflecting limbs). Sum of weapon + projectile."),
Editable(MinValueFloat = 0.0f, MaxValueFloat = 1f)]
92 [
Serialize(0f,
IsPropertySaveable.Yes, description:
"The time required for a charge-type turret to charge up before able to fire.")]
106 [
Serialize(defaultValue: 0f,
IsPropertySaveable.Yes, description:
"Additive penalty to accuracy (spread angle) when dual-wielding.")]
113 private readonly IReadOnlySet<Identifier> suitableProjectiles;
116 private enum ChargingState
122 private ChargingState currentChargingState;
129 Vector2 flippedPos = barrelPos;
131 return Vector2.Transform(flippedPos, bodyTransform) *
item.
Scale;
138 private float currentChargeTime;
139 private bool tryingToCharge;
142 : base(
item, element)
145 if (element.
Parent is { } parent)
154 DebugConsole.AddWarning($
"Invalid XML at {item.Name}: ReloadNoSkill is lower or equal than it's reload skill, despite having ReloadSkillRequirement.",
157 InitProjSpecific(element);
183 float previousChargeTime = currentChargeTime;
185 float chargeDeltaTime = tryingToCharge &&
ReloadTimer <= 0f ? deltaTime : -deltaTime;
186 currentChargeTime = Math.Clamp(currentChargeTime + chargeDeltaTime, 0f,
MaxChargeTime);
188 tryingToCharge =
false;
190 if (currentChargeTime == 0f)
192 currentChargingState = ChargingState.Inactive;
194 else if (currentChargeTime < previousChargeTime)
196 currentChargingState = ChargingState.WindingDown;
201 currentChargingState = ChargingState.WindingUp;
204 UpdateProjSpecific(deltaTime);
207 partial
void UpdateProjSpecific(
float deltaTime);
211 float degreeOfFailure = MathHelper.Clamp(1.0f -
DegreeOfSuccess(user), 0.0f, 1.0f);
212 degreeOfFailure *= degreeOfFailure;
218 return MathHelper.ToRadians(spread);
228 private static float ApplyDualWieldPenaltyReduction(
Character character,
float originalPenalty,
float neutralValue)
231 statAdjustmentPrc = MathHelper.Clamp(statAdjustmentPrc, 0f, 1f);
232 float reducedPenaltyMultiplier = MathHelper.Lerp(originalPenalty, neutralValue, statAdjustmentPrc);
233 return reducedPenaltyMultiplier;
236 private readonly List<Body> ignoredBodies =
new List<Body>();
239 tryingToCharge =
true;
240 if (character ==
null || character.
Removed) {
return false; }
245 float baseReloadTime = reload;
246 float weaponSkill = character.
GetSkillLevel(Tags.WeaponsSkill);
249 if (applyReloadFailure)
253 baseReloadTime = MathHelper.Lerp(reload,
ReloadNoSkill, reloadFailure);
264 currentChargeTime = 0f;
276 degreeOfFailure *= degreeOfFailure;
277 if (degreeOfFailure > Rand.Range(0.0f, 1.0f))
285 if (projectile !=
null)
292 if (lastProjectile != projectile)
297 lastProjectile?.
Item.GetComponent<
Rope>()?.Snap();
302 ignoredBodies.Clear();
313 var holdable = heldItem.GetComponent<
Holdable>();
314 if (holdable?.Pusher !=
null)
316 ignoredBodies.Add(holdable.Pusher.FarseerBody);
335 LaunchProjSpecific();
351 if (containedItem ==
null) {
continue; }
353 if (IsSuitableProjectile(projectile)) {
return projectile; }
357 if (containedSubItems ==
null) {
continue; }
358 foreach (
Item subItem
in containedSubItems)
360 if (subItem ==
null) {
continue; }
364 if (triggerOnUseOnContainers && subItem.
Condition > 0.0f)
368 if (IsSuitableProjectile(subProjectile)) {
return subProjectile; }
375 private bool IsSuitableProjectile(
Projectile projectile)
377 if (projectile?.
Item ==
null) {
return false; }
378 if (!suitableProjectiles.Any()) {
return true; }
382 partial
void LaunchProjSpecific();
virtual Vector2 AimSourceSimPos
void CheckTalents(AbilityEffectType abilityEffectType, AbilityObject abilityObject)
float GetStatValue(StatTypes statType, bool includeSaved=true)
float GetSkillLevel(Identifier skillIdentifier)
Get the character's current skill level, taking into account any temporary boosts from wearables and ...
bool IsDualWieldingRangedWeapons()
bool IsKeyDown(InputType inputType)
readonly AnimController AnimController
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
IEnumerable< Item > AllItemsMod
All items contained in the inventory. Allows modifying the contents of the inventory while being enum...
ItemInventory OwnInventory
bool IsShootable
Should the item's Use method be called with the "Use" or with the "Shoot" key?
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb limb=null, Entity useTarget=null, bool isNetworkEvent=false, Vector2? worldPosition=null)
Executes all StatusEffects of the specified type. Note that condition checks are ignored here: that s...
void RemoveContained(Item contained)
bool HasTag(Identifier tag)
bool RequireAimToUse
If true, the user has to hold the "aim" key before use is registered. False by default.
float GetQualityModifier(Quality.StatType statType)
AbilityRangedWeapon(Item item)
The base class for components holding the different functionalities of the item
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
float DegreeOfSuccess(Character character)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
readonly ItemInventory Inventory
void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List< Body > ignoredBodies, bool createNetworkEvent, float damageMultiplier=1f, float launchImpulseModifier=0f)
float GetSpreadFromPool()
Item Launcher
The item that launched this projectile (if any)
Projectile FindProjectile(bool triggerOnUseOnContainers=false)
float DualWieldReloadTimePenaltyMultiplier
RangedWeapon(Item item, ContentXElement element)
float DualWieldAccuracyPenalty
override void Update(float deltaTime, Camera cam)
Projectile LastProjectile
override bool Use(float deltaTime, Character character=null)
override void Equip(Character character)
override bool SecondaryUse(float deltaTime, Character character=null)
float ReloadSkillRequirement
float WeaponDamageModifier
Vector2? TransformedBarrelPos
void ApplyTorque(float torque)
void ApplyLinearImpulse(Vector2 impulse)
ContentPackage? ContentPackage
readonly Identifier Identifier
ActionType
ActionTypes define when a StatusEffect is executed.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.