private bool CheckArmorShapeAllowed(Templates.TplArmor armor, AllodsMap.AlmShop.AlmShopShelf rules) { for (int i = 0; i < 7; i++) { uint classFlag = 1u << (i + 15); for (int j = 0; j < 15; j++) { uint materialFlag = 1u << j; // armor itself is not allowed if ((armor.ClassesAllowed[i] & materialFlag) == 0) { continue; } // not allowed in shop if (((uint)rules.ItemMaterials & materialFlag) == materialFlag && ((uint)rules.ItemClasses & classFlag) == classFlag) { return(true); } } } return(false); }
protected override void OnUpdateItems() { if (IsHero) { Item cuirass = GetItemFromBody(BodySlot.CuirassCloak); Item weapon = GetItemFromBody(BodySlot.Weapon); Item shield = GetItemFromBody(BodySlot.Shield); int isLightArmored = 0; if (cuirass != null) { isLightArmored = UnitClassLoader.HeroMaterials[cuirass.Class.MaterialID]; } // now if we are a mage, then we either have armed or unarmed sprite. // if we're a fighter, we should pick appropriate version of the sprite instead. UnitClass newClass = null; if ((Gender & GenderFlags.Mage) != 0) { newClass = (weapon != null) ? UnitClassLoader.HeroMageSt[1] : UnitClassLoader.HeroMage[1]; // always heroes. heroes_l mages don't exist! } else if (weapon == null) { newClass = UnitClassLoader.HeroUnarmed[isLightArmored]; } else { Templates.TplArmor weaponKind = weapon.Class.Option; switch (weaponKind.AttackType) { case 1: // sword if (weaponKind.TwoHanded == 2) { newClass = UnitClassLoader.HeroSwordsman2h[isLightArmored]; } else if (shield != null) { newClass = UnitClassLoader.HeroSwordsman_[isLightArmored]; } else { newClass = UnitClassLoader.HeroSwordsman[isLightArmored]; } break; case 2: // axe if (weaponKind.TwoHanded == 2) { newClass = UnitClassLoader.HeroAxeman2h[isLightArmored]; } else if (shield != null) { newClass = UnitClassLoader.HeroAxeman_[isLightArmored]; } else { newClass = UnitClassLoader.HeroAxeman[isLightArmored]; } break; case 3: // club newClass = (shield != null) ? UnitClassLoader.HeroClubman_[isLightArmored] : UnitClassLoader.HeroClubman[isLightArmored]; break; case 4: // pike newClass = (shield != null) ? UnitClassLoader.HeroPikeman_[isLightArmored] : UnitClassLoader.HeroPikeman[isLightArmored]; break; case 5: // shooting (bow or crossbow) if (weaponKind.Name.ToLower().Contains("crossbow")) { newClass = UnitClassLoader.HeroCrossbowman[isLightArmored]; } else { newClass = UnitClassLoader.HeroArcher[isLightArmored]; } break; } } if (newClass != Class) { Class = newClass; DoUpdateView = true; } } // if not client, recalc stats if (NetworkManager.IsClient) { return; } // max brms short maxBody = 100, maxReaction = 100, maxMind = 100, maxSpirit = 100; if ((Gender & GenderFlags.MaleFighter) == GenderFlags.MaleFighter) { maxBody = 52; maxReaction = 50; maxMind = 48; maxSpirit = 46; } else if ((Gender & GenderFlags.FemaleFighter) == GenderFlags.FemaleFighter) { maxBody = 50; maxReaction = 52; maxMind = 46; maxSpirit = 48; } else if ((Gender & GenderFlags.MaleMage) == GenderFlags.MaleMage) { maxBody = 48; maxReaction = 46; maxMind = 52; maxSpirit = 50; } else if ((Gender & GenderFlags.FemaleMage) == GenderFlags.FemaleMage) { maxBody = 46; maxReaction = 48; maxMind = 50; maxSpirit = 52; } CoreStats.Body = Math.Min(CoreStats.Body, maxBody); CoreStats.Reaction = Math.Min(CoreStats.Reaction, maxReaction); CoreStats.Mind = Math.Min(CoreStats.Mind, maxMind); CoreStats.Spirit = Math.Min(CoreStats.Spirit, maxSpirit); float origHealth = (float)Stats.Health / Stats.HealthMax; float origMana = (float)Stats.Mana / Stats.ManaMax; // speed and scanrange CoreStats.RotationSpeed = (byte)Template.RotationSpeed; if (CoreStats.RotationSpeed < 1) { CoreStats.RotationSpeed = 1; } CoreStats.Speed = (byte)Template.Speed; if (CoreStats.Speed < 1) { CoreStats.Speed = 1; } CoreStats.ScanRange = Template.ScanRange; // CoreStats.HealthRegeneration = 100; CoreStats.ManaRegeneration = 100; // skills if (Gender.HasFlag(GenderFlags.Fighter)) { CoreStats.SkillBlade = (byte)GetSkill(ExperienceSkill.Blade); CoreStats.SkillAxe = (byte)GetSkill(ExperienceSkill.Axe); CoreStats.SkillBludgeon = (byte)GetSkill(ExperienceSkill.Bludgeon); CoreStats.SkillPike = (byte)GetSkill(ExperienceSkill.Pike); CoreStats.SkillShooting = (byte)GetSkill(ExperienceSkill.Shooting); } else if (Gender.HasFlag(GenderFlags.Mage)) { CoreStats.SkillFire = (byte)GetSkill(ExperienceSkill.Fire); CoreStats.SkillWater = (byte)GetSkill(ExperienceSkill.Water); CoreStats.SkillAir = (byte)GetSkill(ExperienceSkill.Air); CoreStats.SkillEarth = (byte)GetSkill(ExperienceSkill.Earth); CoreStats.SkillAstral = (byte)GetSkill(ExperienceSkill.Astral); } // // add stats from items ItemStats = new UnitStats(); foreach (Item bodyitem in ItemsBody) { ItemStats.MergeEffects(bodyitem.Effects); } Stats.Body = (short)(CoreStats.Body + ItemStats.Body); Stats.Reaction = (short)(CoreStats.Reaction + ItemStats.Reaction); Stats.Mind = (short)(CoreStats.Mind + ItemStats.Mind); Stats.Spirit = (short)(CoreStats.Spirit + ItemStats.Spirit); if (Stats.Reaction < 10) { Stats.Speed = (byte)Stats.Reaction; } else { Stats.Speed = (byte)Math.Min((float)Stats.Reaction / 5 + 12, 255f); } if (Class.ID == 19 || Class.ID == 21) { Stats.Speed += 10; } Stats.RotationSpeed = Stats.Speed; Stats.Speed += ItemStats.Speed; Stats.RotationSpeed += ItemStats.RotationSpeed; // float experience_total = GetExperience(); float fighter_mult = (Gender & GenderFlags.Fighter) != 0 ? 2 : 1; float mage_mult = (Gender & GenderFlags.Mage) != 0 ? 2 : 1; if (CoreStats.HealthMax == -1 || IsHero) { CoreStats.HealthMax = (int)(Stats.Body * fighter_mult); CoreStats.HealthMax += (int)(Log11(experience_total / 5000f + fighter_mult)); CoreStats.HealthMax = (int)((Pow11(Stats.Body) / 100f + 1f) * CoreStats.HealthMax); } if ((Gender & GenderFlags.Mage) != 0) { CoreStats.ManaMax = (int)(Stats.Spirit * mage_mult); CoreStats.ManaMax += (int)(Log11(experience_total / 5000f + mage_mult)); CoreStats.ManaMax = (int)((Pow11(Stats.Spirit) / 100f + 1f) * CoreStats.ManaMax); } else { CoreStats.ManaMax = -1; } Stats.HealthMax = CoreStats.HealthMax + ItemStats.HealthMax; Stats.ManaMax = CoreStats.ManaMax + ItemStats.ManaMax; float fighterSkill = 0; if ((Gender & GenderFlags.Fighter) != 0) { Item weapon = GetItemFromBody(BodySlot.Weapon); if (weapon != null && weapon.Class != null && weapon.Class.Option != null) { ExperienceSkill fsk; switch (weapon.Class.Option.AttackType) { default: case 1: fsk = ExperienceSkill.Blade; break; case 2: fsk = ExperienceSkill.Axe; break; case 3: fsk = ExperienceSkill.Bludgeon; break; case 4: fsk = ExperienceSkill.Pike; break; case 5: fsk = ExperienceSkill.Shooting; break; } fighterSkill = GetSkill(fsk); } } Stats.DamageMax = (short)(Stats.Body / 10); Stats.DamageMin = (short)(Stats.DamageMax / 2); Stats.ToHit = (short)Math.Pow(1f + (Stats.Reaction / 100f) + (fighterSkill / 100f), 9); Stats.DamageMin += ItemStats.DamageMin; Stats.DamageMax += (short)(ItemStats.DamageMax + Stats.DamageMin); Stats.ToHit += ItemStats.ToHit; Stats.DamageMin = (short)(Stats.DamageMin * (1f + fighterSkill / 20 * Stats.Body / 100f)); Stats.DamageMax = (short)(Stats.DamageMax * (1f + fighterSkill / 20 * Stats.Body / 100f)); Stats.DamageMin += ItemStats.DamageBonus; Stats.DamageMax += ItemStats.DamageBonus; Stats.Defence = (short)((float)ItemStats.Defence * (Stats.Reaction / 15)); Stats.Absorbtion = ItemStats.Absorbtion; int minProt = Stats.Spirit / 2; int maxProt = Math.Min(100, (70 + Stats.Spirit / 2)); Stats.ProtectionFire = ItemStats.ProtectionFire; Stats.ProtectionWater = ItemStats.ProtectionWater; Stats.ProtectionAir = ItemStats.ProtectionAir; Stats.ProtectionEarth = ItemStats.ProtectionEarth; Stats.ProtectionAstral = ItemStats.ProtectionAstral; Stats.ScanRange = 4f + (float)Math.Pow(1f + Stats.Reaction / 100f, Stats.Mind / 10f); Stats.ScanRange += ItemStats.ScanRange; for (int i = 0; i < SpellEffects.Count; i++) { SpellEffects[i].ProcessStats(Stats); } Stats.ProtectionFire = (byte)Math.Min(maxProt, Math.Max(minProt, Stats.ProtectionFire)); Stats.ProtectionWater = (byte)Math.Min(maxProt, Math.Max(minProt, Stats.ProtectionWater)); Stats.ProtectionAir = (byte)Math.Min(maxProt, Math.Max(minProt, Stats.ProtectionAir)); Stats.ProtectionEarth = (byte)Math.Min(maxProt, Math.Max(minProt, Stats.ProtectionEarth)); Stats.ProtectionAstral = (byte)Math.Min(maxProt, Math.Max(minProt, Stats.ProtectionAstral)); if (Gender.HasFlag(GenderFlags.Fighter)) { Stats.SkillBlade = (byte)(CoreStats.SkillBlade + ItemStats.SkillBlade); Stats.SkillAxe = (byte)(CoreStats.SkillAxe + ItemStats.SkillAxe); Stats.SkillBludgeon = (byte)(CoreStats.SkillBludgeon + ItemStats.SkillBludgeon); Stats.SkillPike = (byte)(CoreStats.SkillPike + ItemStats.SkillPike); Stats.SkillShooting = (byte)(CoreStats.SkillShooting + ItemStats.SkillShooting); } else if (Gender.HasFlag(GenderFlags.Mage)) { CoreStats.SkillFire = (byte)(CoreStats.SkillFire + ItemStats.SkillFire); CoreStats.SkillWater = (byte)(CoreStats.SkillWater + ItemStats.SkillWater); CoreStats.SkillAir = (byte)(CoreStats.SkillAir + ItemStats.SkillAir); CoreStats.SkillEarth = (byte)(CoreStats.SkillEarth + ItemStats.SkillEarth); CoreStats.SkillAstral = (byte)(CoreStats.SkillAstral + ItemStats.SkillAstral); } //Debug.LogFormat("ItemStats = {0}", ItemStats.ToString()); CalculateVision(); DoUpdateInfo = true; DoUpdateView = true; }
public static ItemClass GetItemClassBySpecifier(string specifier) { specifier = specifier.Trim().ToLower(); string commonspecifier = "common " + specifier; foreach (ItemClass cls in Classes) { string sname = cls.ServerName.ToLower(); if (sname == specifier || sname == commonspecifier) { return(cls); } } // try to guess based on specifier. this is used in world.res string[] specSplit = specifier.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string searchFor; // this is reused // first off, if last word is "shield", then we're looking for something in Templates.Shields int gSlot = -1; Templates.TplArmor gOption = null; int gOptionId = -1; if (specSplit[specSplit.Length - 1] == "shield") { searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Shields.Count; i++) { Templates.TplArmor shieldKind = TemplateLoader.Templates.Shields[i]; if (shieldKind.Name.ToLower() == searchFor) { gOption = shieldKind; gOptionId = i; gSlot = shieldKind.Slot; break; } } if (gOption != null) { // remove the option specifier (leaving only class and material) specSplit = specSplit.Take(specSplit.Length - (j + 2)).ToArray(); break; } } } // otherwise search in weapons and armor. else { searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Weapons.Count; i++) { Templates.TplArmor weaponKind = TemplateLoader.Templates.Weapons[i]; if (weaponKind.Name.ToLower() == searchFor) { gOption = weaponKind; gOptionId = i; gSlot = weaponKind.Slot; break; } } if (gOption == null) { for (int i = 0; i < TemplateLoader.Templates.Armor.Count; i++) { Templates.TplArmor armorKind = TemplateLoader.Templates.Armor[i]; if (armorKind.Name.ToLower() == searchFor) { gOption = armorKind; gOptionId = i; gSlot = armorKind.Slot; break; } } } if (gOption != null) { specSplit = specSplit.Take(specSplit.Length - (j + 1)).ToArray(); break; } } } // here we either have gOption and gSlot or the item is not found. if (gOption == null || gSlot < 0) { return(null); } // now find material int gMaterial = -1; searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Materials.Count; i++) { Templates.TplMaterial fmat = TemplateLoader.Templates.Materials[i]; if (fmat.Name.ToLower() == searchFor) { gMaterial = i; break; } } if (gMaterial >= 0) { specSplit = specSplit.Take(specSplit.Length - (j + 1)).ToArray(); break; } } if (gMaterial < 0) { // check if option has "none" material allowed (aka 15) if (gOption.IsAllowed(-1, 15)) { gMaterial = 15; } else { // there are very special items like "Sonic Beam" that have ONLY option in them ItemClass newCls = new ItemClass(); newCls.ServerName = gOption.Name; newCls.VisualName = newCls.ServerName; newCls.Option = gOption; newCls.IsMagic = false; newCls.IsSpecial = true; newCls.ItemID = (ushort)gOptionId; newCls.UsableMage = false; newCls.UsableFighter = false; newCls.Price = 0; return(newCls); } } int gClass = -1; // common by default searchFor = string.Join(" ", specSplit); for (int i = 0; i < TemplateLoader.Templates.Classes.Count; i++) { Templates.TplClass fcls = TemplateLoader.Templates.Classes[i]; if (fcls.Name.ToLower() == searchFor) { gClass = i; break; } } if (gClass < 0) { gClass = 0; // default is common. the only default here. } // how try to find required item. Templates.TplClass reqClass = TemplateLoader.GetClassById(gClass); Templates.TplMaterial reqMaterial = TemplateLoader.GetMaterialById(gMaterial); Templates.TplArmor reqOption; if (reqClass == null || reqMaterial == null) // this shouldnt happen tbh { return(null); } // do option remap for shields. nival did NOT write "soft helm" or "soft large shield". instead they just wrote "helm" and "large shield". reqOption = gOption; int oldOptionId = gOptionId; if (reqMaterial.Name.ToLower().Contains("wood") && !reqOption.IsAllowed(gClass, gMaterial)) { gOptionId += 2; // small/large -> wooden small/wooden large } else if (reqMaterial.Name.ToLower().Contains("leather") && !reqOption.IsAllowed(gClass, gMaterial)) { gOptionId += 1; // small/large -> soft small/soft large } if (oldOptionId != gOptionId) { reqOption = TemplateLoader.GetOptionByIdAndSlot(gOptionId, gSlot); if (reqOption == null) { return(null); } } for (int i = 0; i < Classes.Count; i++) { if (Classes[i].Class == reqClass && Classes[i].Material == reqMaterial && Classes[i].Option == reqOption) { return(Classes[i]); } } return(null); }
public override void UpdateItems() { if (IsHero) { Item cuirass = GetItemFromBody(BodySlot.CuirassCloak); Item weapon = GetItemFromBody(BodySlot.Weapon); Item shield = GetItemFromBody(BodySlot.Shield); int isLightArmored = 0; if (cuirass != null) { isLightArmored = UnitClassLoader.HeroMaterials[cuirass.Class.MaterialID]; } // now if we are a mage, then we either have armed or unarmed sprite. // if we're a fighter, we should pick appropriate version of the sprite instead. UnitClass newClass = null; if ((Gender & GenderFlags.Mage) != 0) { newClass = (weapon != null) ? UnitClassLoader.HeroMageSt[1] : UnitClassLoader.HeroMage[1]; // always heroes. heroes_l mages don't exist! } else if (weapon == null) { newClass = UnitClassLoader.HeroUnarmed[isLightArmored]; } else { Templates.TplArmor weaponKind = weapon.Class.Option; switch (weaponKind.AttackType) { case 1: // sword if (weaponKind.TwoHanded == 2) { newClass = UnitClassLoader.HeroSwordsman2h[isLightArmored]; } else if (shield != null) { newClass = UnitClassLoader.HeroSwordsman_[isLightArmored]; } else { newClass = UnitClassLoader.HeroSwordsman[isLightArmored]; } break; case 2: // axe if (weaponKind.TwoHanded == 2) { newClass = UnitClassLoader.HeroAxeman2h[isLightArmored]; } else if (shield != null) { newClass = UnitClassLoader.HeroAxeman_[isLightArmored]; } else { newClass = UnitClassLoader.HeroAxeman[isLightArmored]; } break; case 3: // club newClass = (shield != null) ? UnitClassLoader.HeroClubman_[isLightArmored] : UnitClassLoader.HeroClubman[isLightArmored]; break; case 4: // pike newClass = (shield != null) ? UnitClassLoader.HeroPikeman_[isLightArmored] : UnitClassLoader.HeroPikeman[isLightArmored]; break; case 5: // shooting (bow or crossbow) if (weaponKind.Name.ToLower().Contains("crossbow")) { newClass = UnitClassLoader.HeroCrossbowman[isLightArmored]; } else { newClass = UnitClassLoader.HeroArcher[isLightArmored]; } break; } } if (newClass != Class) { Class = newClass; DoUpdateView = true; } } // if not client, recalc stats // actually, let client do this too, we send it the required info anyway // max brms short maxBody = 100, maxReaction = 100, maxMind = 100, maxSpirit = 100; if ((Gender & GenderFlags.MaleFighter) == GenderFlags.MaleFighter) { maxBody = 52; maxReaction = 50; maxMind = 48; maxSpirit = 46; } else if ((Gender & GenderFlags.FemaleFighter) == GenderFlags.FemaleFighter) { maxBody = 50; maxReaction = 52; maxMind = 46; maxSpirit = 48; } else if ((Gender & GenderFlags.MaleMage) == GenderFlags.MaleMage) { maxBody = 48; maxReaction = 46; maxMind = 52; maxSpirit = 50; } else if ((Gender & GenderFlags.FemaleMage) == GenderFlags.FemaleMage) { maxBody = 46; maxReaction = 48; maxMind = 50; maxSpirit = 52; } CoreStats.Body = Math.Min(CoreStats.Body, maxBody); CoreStats.Reaction = Math.Min(CoreStats.Reaction, maxReaction); CoreStats.Mind = Math.Min(CoreStats.Mind, maxMind); CoreStats.Spirit = Math.Min(CoreStats.Spirit, maxSpirit); // // add stats from items ItemStats = new UnitStats(); foreach (Item bodyitem in ItemsBody) { ItemStats.MergeEffects(bodyitem.Effects); } //Debug.LogFormat("ItemStats = {0}", ItemStats.ToString()); Stats = new UnitStats(CoreStats); Stats.MergeStats(ItemStats); Stats.DamageMax += Stats.DamageMin; // allods use damagemax this way // CoreStats = only BRMS used //Debug.LogFormat("Body = {0}, {1}, {2}", Stats.Body, CoreStats.Body, ItemStats.Body); /* *(_WORD *)(this + 150) = (signed __int64)(sub_53066C((double)*(signed int *)(this + 304) / 5000.0 + 1.0) * (double)((v34 != 0) + 1) + (double)*(_WORD *)(this + 150)); *(_WORD *)(v28 + 150) = (signed __int64)((sub_53064C(*(_WORD *)(v28 + 132)) / 100.0 + 1.0) * (double)*(_WORD *)(v28 + 150)); * * sub_53066C(a1): * log(a1) / log(1.1) * * sub_53064C(a1): * pow(1.1, a1) * * health = body * (is_fighter ? 2 : 1) * health += sub_53066C(experience_total) / 5000.0 + 1 * (is_fighter ? 2 : 1) * health *= sub_53064C(body) / 100.0 + 1.0 */ float experience_total = 7320; float fighter_mult = (Gender & GenderFlags.Fighter) != 0 ? 2 : 1; float mage_mult = (Gender & GenderFlags.Mage) != 0 ? 2 : 1; Stats.HealthMax = (int)(Stats.Body * fighter_mult); Stats.HealthMax += (int)(Log11(experience_total / 5000f + fighter_mult)); Stats.HealthMax = (int)((Pow11(Stats.Body) / 100f + 1f) * Stats.HealthMax); if ((Gender & GenderFlags.Mage) != 0) { Stats.ManaMax = (int)(Stats.Spirit * mage_mult); Stats.ManaMax += (int)(Log11(experience_total / 5000f + mage_mult)); Stats.ManaMax = (int)((Pow11(Stats.Spirit) / 100f + 1f) * Stats.ManaMax); } else { Stats.ManaMax = -1; } // /* * if ( *(v28 + 134) >= 12 ) *(v28 + 140) = *(v28 + 134) / 5 + 12; // speed * else *(v28 + 140) = *(v28 + 134); * if ( *(v28 + 14) == 19 || *(v28 + 14) == 21 ) // ManHorse_LanceShield, ManHorse_SwordShield *(v28 + 140) += 10; */ if (Stats.Reaction < 12) { Stats.Speed = (byte)Stats.Reaction; } else { Stats.Speed = (byte)Math.Min(Stats.Reaction / 5 + 12, 255); } if (Class.ID == 19 || Class.ID == 21) // horseman { Stats.Speed += 10; } Stats.RotationSpeed = Stats.Speed; DoUpdateInfo = true; DoUpdateView = true; }