public static int CalculateReassemblyChance(NWPlayer player, int penalty) { const int BaseChance = 70; int harvesting = SkillService.GetPCSkillRank(player, SkillType.Harvesting); var itemBonuses = PlayerStatService.GetPlayerItemEffectiveStats(player); int perkLevel = PerkService.GetPCPerkLevel(player, PerkType.MolecularReassemblyProficiency); // Calculate the base chance after factoring in skills, perks, and items. int categoryChance = (int)(BaseChance + (harvesting / 2.5f) + perkLevel * 10 + itemBonuses.Harvesting / 3f); // Reduce the chance by the penalty. This penalty is generally determined by how many properties were already // applied during this batch. categoryChance -= penalty; // Keep bounds between 0 and 100 if (categoryChance < 0) { return(0); } else if (categoryChance > 100) { return(100); } else { return(categoryChance); } }
private static void HandleFPRegenerationTick(NWPlayer oPC, Data.Entity.Player entity) { entity.CurrentFPTick = entity.CurrentFPTick - 1; int rate = 20; int amount = 1; if (entity.CurrentFPTick <= 0) { if (entity.CurrentFP < entity.MaxFP) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(oPC); // CHA bonus int cha = oPC.CharismaModifier; if (cha > 0) { amount += cha; } amount += effectiveStats.FPRegen; if (oPC.Chest.CustomItemType == CustomItemType.ForceArmor) { int clarityLevel = PerkService.GetPCPerkLevel(oPC, PerkType.Clarity); if (clarityLevel > 0) { amount += clarityLevel + 1; } } entity = AbilityService.RestoreFP(oPC, amount, entity); } entity.CurrentFPTick = rate; } }
private static void HandleRegenerationTick(NWPlayer oPC, Data.Entity.Player entity) { entity.RegenerationTick = entity.RegenerationTick - 1; int rate = 20; int amount = entity.HPRegenerationAmount; if (entity.RegenerationTick <= 0) { if (oPC.CurrentHP < oPC.MaxHP) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(oPC); // CON bonus int con = oPC.ConstitutionModifier; if (con > 0) { amount += con; } amount += effectiveStats.HPRegen; if (oPC.Chest.CustomItemType == CustomItemType.HeavyArmor) { int sturdinessLevel = PerkService.GetPCPerkLevel(oPC, PerkType.Sturdiness); if (sturdinessLevel > 0) { amount += sturdinessLevel + 1; } } _.ApplyEffectToObject(_.DURATION_TYPE_INSTANT, _.EffectHeal(amount), oPC.Object); } entity.RegenerationTick = rate; } }
private static int EffectiveMaxFP(NWPlayer player, EffectiveItemStats stats) { int fp = 20; fp += (player.IntelligenceModifier + player.WisdomModifier + player.CharismaModifier) * 5; fp += PerkService.GetPCPerkLevel(player, PerkType.FP) * 5; fp += stats.FP; if (fp < 0) { fp = 0; } return(fp); }
private static int EffectiveMaxHitPoints(NWPlayer player, EffectiveItemStats stats) { int hp = 25 + player.ConstitutionModifier * 5; float effectPercentBonus = CustomEffectService.CalculateEffectHPBonusPercent(player); hp += PerkService.GetPCPerkLevel(player, PerkType.Health) * 5; hp += stats.HP; hp = hp + (int)(hp * effectPercentBonus); if (hp > 1275) { hp = 1275; } if (hp < 20) { hp = 20; } return(hp); }
private static void HandleGrenadeProficiency(NWPlayer oPC, NWObject target) { NWItem weapon = _.GetSpellCastItem(); if (weapon.BaseItemType != BASE_ITEM_GRENADE) { return; } int perkLevel = PerkService.GetPCPerkLevel(oPC, PerkType.GrenadeProficiency); int chance = 10 * perkLevel; float duration; switch (perkLevel) { case 1: case 2: case 3: case 4: duration = 6; break; case 5: case 6: case 7: case 8: case 9: duration = 9; break; default: return; } if (RandomService.D100(1) <= chance) { _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, _.EffectKnockdown(), target, duration); } }
public static float CalculateCraftingDelay(NWPlayer oPC, int skillID) { int atmosphere = CalculateAreaAtmosphereBonus(oPC.Area); PerkType perkType; float adjustedSpeed = 1.0f; SkillType skillType = (SkillType)skillID; // Identify which perk to use for this skill. if (skillType == SkillType.Weaponsmith) { perkType = PerkType.SpeedyWeaponsmith; } else if (skillType == SkillType.Armorsmith) { perkType = PerkType.SpeedyArmorsmith; } else if (skillType == SkillType.Cooking) { perkType = PerkType.SpeedyCooking; } else if (skillType == SkillType.Engineering) { perkType = PerkType.SpeedyEngineering; } else if (skillType == SkillType.Fabrication) { perkType = PerkType.SpeedyFabrication; } else if (skillType == SkillType.Medicine) { perkType = PerkType.SpeedyMedicine; } else if (skillType == SkillType.Harvesting) { perkType = PerkType.SpeedyReassembly; } else { return(BaseCraftDelay); } int perkLevel = PerkService.GetPCPerkLevel(oPC, perkType); // Each perk level reduces crafting speed by 10%. switch (perkLevel) { case 1: adjustedSpeed = 0.9f; break; case 2: adjustedSpeed = 0.8f; break; case 3: adjustedSpeed = 0.7f; break; case 4: adjustedSpeed = 0.6f; break; case 5: adjustedSpeed = 0.5f; break; case 6: adjustedSpeed = 0.4f; break; case 7: adjustedSpeed = 0.3f; break; case 8: adjustedSpeed = 0.2f; break; case 9: adjustedSpeed = 0.1f; break; case 10: adjustedSpeed = 0.01f; break; } // Workshops with an atmosphere bonus decrease crafting time. if (atmosphere >= 45) { adjustedSpeed -= 0.2f; } else if (atmosphere >= 5) { adjustedSpeed -= 0.1f; } // Never fall below 1% of overall crafting time. if (adjustedSpeed <= 0.01f) { adjustedSpeed = 0.01f; } return(BaseCraftDelay * adjustedSpeed); }
private static int CalculateBAB(NWPlayer oPC, NWItem ignoreItem, EffectiveItemStats stats) { NWItem weapon = oPC.RightHand; // The unequip event fires before the item is actually unequipped, so we need // to have additional checks to make sure we're not getting the weapon that's about to be // unequipped. if (weapon.Equals(ignoreItem)) { weapon = null; NWItem offHand = oPC.LeftHand; if (offHand.CustomItemType == CustomItemType.Vibroblade || offHand.CustomItemType == CustomItemType.FinesseVibroblade || offHand.CustomItemType == CustomItemType.Baton || offHand.CustomItemType == CustomItemType.HeavyVibroblade || offHand.CustomItemType == CustomItemType.Saberstaff || offHand.CustomItemType == CustomItemType.Polearm || offHand.CustomItemType == CustomItemType.TwinBlade || offHand.CustomItemType == CustomItemType.MartialArtWeapon || offHand.CustomItemType == CustomItemType.BlasterPistol || offHand.CustomItemType == CustomItemType.BlasterRifle || offHand.CustomItemType == CustomItemType.Throwing) { weapon = offHand; } } if (weapon == null || !weapon.IsValid) { weapon = oPC.Arms; } if (!weapon.IsValid) { return(0); } SkillType itemSkill = ItemService.GetSkillTypeForItem(weapon); if (itemSkill == SkillType.Unknown || itemSkill == SkillType.LightArmor || itemSkill == SkillType.HeavyArmor || itemSkill == SkillType.ForceArmor || itemSkill == SkillType.Shields) { return(0); } int weaponSkillID = (int)itemSkill; PCSkill skill = DataService.Single <PCSkill>(x => x.PlayerID == oPC.GlobalID && x.SkillID == weaponSkillID); if (skill == null) { return(0); } int skillBAB = skill.Rank / 10; int perkBAB = 0; int backgroundBAB = 0; BackgroundType background = (BackgroundType)oPC.Class1; bool receivesBackgroundBonus = false; // Apply increased BAB if player is using a weapon for which they have a proficiency. PerkType proficiencyPerk = PerkType.Unknown; SkillType proficiencySkill = SkillType.Unknown; switch (weapon.CustomItemType) { case CustomItemType.Vibroblade: proficiencyPerk = PerkType.VibrobladeProficiency; proficiencySkill = SkillType.OneHanded; break; case CustomItemType.FinesseVibroblade: proficiencyPerk = PerkType.FinesseVibrobladeProficiency; proficiencySkill = SkillType.OneHanded; receivesBackgroundBonus = background == BackgroundType.Duelist; break; case CustomItemType.Baton: proficiencyPerk = PerkType.BatonProficiency; proficiencySkill = SkillType.OneHanded; receivesBackgroundBonus = background == BackgroundType.SecurityOfficer; break; case CustomItemType.HeavyVibroblade: proficiencyPerk = PerkType.HeavyVibrobladeProficiency; proficiencySkill = SkillType.TwoHanded; receivesBackgroundBonus = background == BackgroundType.Soldier; break; case CustomItemType.Saberstaff: proficiencyPerk = PerkType.SaberstaffProficiency; proficiencySkill = SkillType.Lightsaber; break; case CustomItemType.Polearm: proficiencyPerk = PerkType.PolearmProficiency; proficiencySkill = SkillType.TwoHanded; break; case CustomItemType.TwinBlade: proficiencyPerk = PerkType.TwinVibrobladeProficiency; proficiencySkill = SkillType.TwinBlades; receivesBackgroundBonus = background == BackgroundType.Berserker; break; case CustomItemType.MartialArtWeapon: proficiencyPerk = PerkType.MartialArtsProficiency; proficiencySkill = SkillType.MartialArts; receivesBackgroundBonus = background == BackgroundType.TerasKasi; break; case CustomItemType.BlasterPistol: proficiencyPerk = PerkType.BlasterPistolProficiency; proficiencySkill = SkillType.Firearms; receivesBackgroundBonus = background == BackgroundType.Smuggler; break; case CustomItemType.BlasterRifle: proficiencyPerk = PerkType.BlasterRifleProficiency; proficiencySkill = SkillType.Firearms; receivesBackgroundBonus = background == BackgroundType.Sharpshooter || background == BackgroundType.Mandalorian; break; case CustomItemType.Throwing: proficiencyPerk = PerkType.ThrowingProficiency; proficiencySkill = SkillType.Throwing; break; case CustomItemType.Lightsaber: proficiencyPerk = PerkType.LightsaberProficiency; proficiencySkill = SkillType.Lightsaber; break; } if (weapon.GetLocalInt("LIGHTSABER") == TRUE) { proficiencyPerk = PerkType.LightsaberProficiency; proficiencySkill = SkillType.Lightsaber; } if (proficiencyPerk != PerkType.Unknown && proficiencySkill != SkillType.Unknown) { perkBAB += PerkService.GetPCPerkLevel(oPC, proficiencyPerk); } if (receivesBackgroundBonus) { backgroundBAB = background == BackgroundType.Mandalorian ? 1 : 2; } return(1 + skillBAB + perkBAB + stats.BAB + backgroundBAB); // Note: Always add 1 to BAB. 0 will cause a crash in NWNX. }
public static void HandlePlasmaCellPerk(NWPlayer player, NWObject target) { if (!player.IsPlayer) { return; } if (_.GetHasFeat((int)CustomFeatType.PlasmaCell, player) == FALSE) { return; // Check if player has the perk } if (player.RightHand.CustomItemType != CustomItemType.BlasterPistol && player.RightHand.CustomItemType != CustomItemType.BlasterRifle) { return; // Check if player has the right weapons } if (target.GetLocalInt("TRANQUILIZER_EFFECT_FIRST_RUN") == _.TRUE) { return; // Check if Tranquilizer is on to avoid conflict } if (player.GetLocalInt("PLASMA_CELL_TOGGLE_OFF") == _.TRUE) { return; // Check if Plasma Cell toggle is on or off } if (target.GetLocalInt("TRANQUILIZER_EFFECT_FIRST_RUN") == _.TRUE) { return; } int perkLevel = PerkService.GetPCPerkLevel(player, PerkType.PlasmaCell); int chance; CustomEffectType[] damageTypes; switch (perkLevel) { case 1: chance = 10; damageTypes = new[] { CustomEffectType.FireCell }; break; case 2: chance = 10; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell }; break; case 3: chance = 20; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell }; break; case 4: chance = 20; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell }; break; case 5: chance = 30; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell }; break; case 6: chance = 30; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell }; break; case 7: chance = 40; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell }; break; case 8: chance = 40; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell }; break; case 9: chance = 50; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell }; break; case 10: chance = 50; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell, CustomEffectType.DivineCell }; break; default: return; } foreach (var effect in damageTypes) { if (RandomService.D100(1) <= chance) { CustomEffectService.ApplyCustomEffect(player, target.Object, effect, RandomService.D6(1), perkLevel, null); } } }
private static void OnModuleUseFeat() { NWPlayer pc = Object.OBJECT_SELF; NWCreature target = NWNXEvents.OnFeatUsed_GetTarget().Object; int featID = NWNXEvents.OnFeatUsed_GetFeatID(); var perkFeat = DataService.SingleOrDefault <PerkFeat>(x => x.FeatID == featID); if (perkFeat == null) { return; } Data.Entity.Perk perk = DataService.GetAll <Data.Entity.Perk>().SingleOrDefault(x => x.ID == perkFeat.PerkID); if (perk == null) { return; } // Check to see if we are a spaceship. Spaceships can't use abilities... if (pc.GetLocalInt("IS_SHIP") > 0 || pc.GetLocalInt("IS_GUNNER") > 0) { pc.SendMessage("You cannot use that ability while piloting a ship."); return; } var perkAction = PerkService.GetPerkHandler(perkFeat.PerkID); Player playerEntity = DataService.Get <Player>(pc.GlobalID); int pcPerkLevel = PerkService.GetPCPerkLevel(pc, perk.ID); // If player is disabling an existing stance, remove that effect. if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { PCCustomEffect stanceEffect = DataService.SingleOrDefault <PCCustomEffect>(x => x.StancePerkID == perk.ID && x.PlayerID == pc.GlobalID); if (stanceEffect != null) { if (CustomEffectService.RemoveStance(pc)) { return; } } } if (pcPerkLevel <= 0) { pc.SendMessage("You do not meet the prerequisites to use this ability."); return; } if (perkAction.IsHostile() && target.IsPlayer) { if (!PVPSanctuaryService.IsPVPAttackAllowed(pc, target.Object)) { return; } } if (pc.Area.Resref != target.Area.Resref || _.LineOfSightObject(pc.Object, target.Object) == 0) { pc.SendMessage("You cannot see your target."); return; } if (!perkAction.CanCastSpell(pc, target)) { pc.SendMessage(perkAction.CannotCastSpellMessage(pc, target) ?? "That ability cannot be used at this time."); return; } int fpCost = perkAction.FPCost(pc, perkAction.FPCost(pc, perk.BaseFPCost, featID), featID); if (playerEntity.CurrentFP < fpCost) { pc.SendMessage("You do not have enough FP. (Required: " + fpCost + ". You have: " + playerEntity.CurrentFP + ")"); return; } if (pc.IsBusy || pc.CurrentHP <= 0) { pc.SendMessage("You are too busy to activate that ability."); return; } // Check cooldown int? cooldownCategoryID = perkAction.CooldownCategoryID(pc, perk.CooldownCategoryID, featID); PCCooldown pcCooldown = DataService.GetAll <PCCooldown>().SingleOrDefault(x => x.PlayerID == pc.GlobalID && x.CooldownCategoryID == cooldownCategoryID); if (pcCooldown == null) { pcCooldown = new PCCooldown { CooldownCategoryID = Convert.ToInt32(cooldownCategoryID), DateUnlocked = DateTime.UtcNow.AddSeconds(-1), PlayerID = pc.GlobalID }; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Insert); } DateTime unlockDateTime = pcCooldown.DateUnlocked; DateTime now = DateTime.UtcNow; if (unlockDateTime > now) { string timeToWait = TimeService.GetTimeToWaitLongIntervals(now, unlockDateTime, false); pc.SendMessage("That ability can be used in " + timeToWait + "."); return; } // Force Abilities (aka Spells) if (perk.ExecutionTypeID == (int)PerkExecutionType.ForceAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_FORCE); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.ForceAbility, featID); } // Combat Abilities else if (perk.ExecutionTypeID == (int)PerkExecutionType.CombatAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.CombatAbility, featID); } // Queued Weapon Skills else if (perk.ExecutionTypeID == (int)PerkExecutionType.QueuedWeaponSkill) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); HandleQueueWeaponSkill(pc, perk, perkAction, featID); } // Stances else if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_COMBATABILITY); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.Stance, featID); } }
private static void HandleEvadeOrDeflectBlasterFire() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWCreature damager = data.Damager.Object; NWCreature target = Object.OBJECT_SELF; NWItem damagerWeapon = _.GetLastWeaponUsed(damager); NWItem targetWeapon = target.RightHand; int perkLevel; // Attacker isn't using a pistol or rifle. Return. if (damagerWeapon.CustomItemType != CustomItemType.BlasterPistol && damagerWeapon.CustomItemType != CustomItemType.BlasterRifle) { return; } int modifier; string action; // Check target's equipped weapon, armor and perk. if (target.Chest.CustomItemType == CustomItemType.LightArmor && (targetWeapon.CustomItemType == CustomItemType.MartialArtWeapon || !target.RightHand.IsValid && !target.LeftHand.IsValid)) { // Martial Arts (weapon or unarmed) uses the Evade Blaster Fire perk which is primarily DEX based. perkLevel = PerkService.GetPCPerkLevel(target.Object, PerkType.EvadeBlasterFire); modifier = target.DexterityModifier; action = "evade"; } else if (target.Chest.CustomItemType == CustomItemType.ForceArmor && (targetWeapon.CustomItemType == CustomItemType.Lightsaber || targetWeapon.CustomItemType == CustomItemType.Saberstaff || targetWeapon.GetLocalInt("LIGHTSABER") == TRUE)) { // Lightsabers (lightsaber or saberstaff) uses the Deflect Blaster Fire perk which is primarily CHA based. perkLevel = PerkService.GetPCPerkLevel(target.Object, PerkType.DeflectBlasterFire); modifier = target.CharismaModifier; action = "deflect"; } else { return; } // Don't have the perk. Return. if (perkLevel <= 0) { return; } // Check attacker's DEX against the primary stat of the perk. int delta = modifier - damager.DexterityModifier; if (delta <= 0) { return; } // Has the delay between block/evade attempts past? DateTime cooldown = DateTime.UtcNow; string lastAttemptVar = target.GetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN"); if (!string.IsNullOrWhiteSpace(lastAttemptVar)) { cooldown = DateTime.Parse(lastAttemptVar); } // Cooldown hasn't expired yet. Not ready to attempt a deflect. if (cooldown >= DateTime.UtcNow) { return; } // Ready to attempt a deflect. Adjust chance based on the delta of attacker DEX versus primary stat of defender. int chanceToDeflect = 5 * delta; if (chanceToDeflect > 80) { chanceToDeflect = 80; } int delay; // Seconds delay between deflect attempts. switch (perkLevel) { case 1: delay = 18; break; case 2: delay = 12; break; case 3: delay = 6; break; default: throw new Exception("HandleEvadeOrDeflectBlasterFire -> Perk Level " + perkLevel + " unsupported."); } cooldown = DateTime.UtcNow.AddSeconds(delay); target.SetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN", cooldown.ToString(CultureInfo.InvariantCulture)); int roll = RandomService.D100(1); if (roll <= chanceToDeflect) { target.SendMessage(ColorTokenService.Gray("You " + action + " a blaster shot.")); data.AdjustAllByPercent(-1); NWNXDamage.SetDamageEventData(data); } else { target.SendMessage(ColorTokenService.Gray("You fail to " + action + " a blaster shot. (" + roll + " vs " + chanceToDeflect + ")")); } }
private static void HandleBattlemagePerk() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject target = Object.OBJECT_SELF; if (!data.Damager.IsPlayer || !target.IsNPC) { return; } if (_.GetHasFeat((int)CustomFeatType.Battlemage, data.Damager.Object) == FALSE) { return; } NWPlayer player = data.Damager.Object; NWItem weapon = _.GetLastWeaponUsed(player.Object); if (weapon.CustomItemType != CustomItemType.Baton) { return; } if (player.Chest.CustomItemType != CustomItemType.ForceArmor) { return; } int perkRank = PerkService.GetPCPerkLevel(player, PerkType.Battlemage); int restoreAmount = 0; bool metRoll = RandomService.Random(100) + 1 <= 50; switch (perkRank) { case 1 when metRoll: restoreAmount = 1; break; case 2: restoreAmount = 1; break; case 3: restoreAmount = 1; if (metRoll) { restoreAmount++; } break; case 4: restoreAmount = 2; break; case 5: restoreAmount = 2; if (metRoll) { restoreAmount++; } break; case 6: restoreAmount = 3; break; } if (restoreAmount > 0) { AbilityService.RestoreFP(player, restoreAmount); } }