diff --git a/Server.Configurations/ZolianDB/ZolianMaps.sql b/Server.Configurations/ZolianDB/ZolianMaps.sql index 179c5ce6..03864e0d 100644 Binary files a/Server.Configurations/ZolianDB/ZolianMaps.sql and b/Server.Configurations/ZolianDB/ZolianMaps.sql differ diff --git a/Server.Configurations/ZolianDB/ZolianMundanes.sql b/Server.Configurations/ZolianDB/ZolianMundanes.sql index 1202a2b3..1fe72388 100644 Binary files a/Server.Configurations/ZolianDB/ZolianMundanes.sql and b/Server.Configurations/ZolianDB/ZolianMundanes.sql differ diff --git a/Zolian.Server.Base/GameScripts/Affects/DebuffAffects.cs b/Zolian.Server.Base/GameScripts/Affects/DebuffAffects.cs index 176ec45c..d4a7b19f 100644 --- a/Zolian.Server.Base/GameScripts/Affects/DebuffAffects.cs +++ b/Zolian.Server.Base/GameScripts/Affects/DebuffAffects.cs @@ -1399,7 +1399,14 @@ public class DebuffReaping : Debuff public override void OnApplied(Sprite affected, Debuff debuff) { if (affected is Aisling { GameMaster: true }) return; - + if (affected is Aisling mapCheck && mapCheck.Map.ID is >= 800 and <= 810) + { + mapCheck.Client.TransitionToMap(188, new Position(12, 22)); + mapCheck.CurrentHp = 1; + mapCheck.Client.SendAttributes(StatUpdateType.Full); + return; + } + if (affected.Debuffs.TryAdd(debuff.Name, debuff)) { DebuffSpell = debuff; @@ -1495,7 +1502,7 @@ public override void OnEnded(Sprite affected, Debuff debuff) aisling.Client.SendServerMessage(ServerMessageType.OrangeBar1, "Your soul has been ripped from your mortal coil."); aisling.SendTargetedClientMethod(PlayerScope.AislingsOnSameMap, client => client.SendSound(5, false)); - + aisling.PrepareForHell(); aisling.CastDeath(); aisling.Resting = Enums.RestPosition.Standing; diff --git a/Zolian.Server.Base/GameScripts/Areas/Abel/Atlantis.cs b/Zolian.Server.Base/GameScripts/Areas/Abel/Atlantis.cs index 56ebd4d7..7109db7b 100644 --- a/Zolian.Server.Base/GameScripts/Areas/Abel/Atlantis.cs +++ b/Zolian.Server.Base/GameScripts/Areas/Abel/Atlantis.cs @@ -4,6 +4,8 @@ using System.Numerics; using Darkages.Enums; using Darkages.Sprites.Entity; +using Darkages.GameScripts.Affects; +using Darkages.Network.Server; namespace Darkages.GameScripts.Areas.Abel; @@ -20,21 +22,54 @@ public override void OnPlayerWalk(WorldClient client, Position oldLocation, Posi var vectorMap = new Vector2(newLocation.X, newLocation.Y); if (client.Aisling.Pos != vectorMap) return; - switch (newLocation.X) + if (client.Aisling.Pos == new Vector2(31, 29) || + client.Aisling.Pos == new Vector2(30, 29) || + client.Aisling.Pos == new Vector2(30, 28) || + client.Aisling.Pos == new Vector2(31, 28)) { - case 15 when newLocation.Y == 14: - if (client.Aisling.QuestManager.ScubaGearCrafted && client.Aisling.EquipmentManager.Equipment[16]?.Item?.Template.Name == "Scuba Gear" - || client.Aisling.Race.RaceFlagIsSet(Race.Merfolk)) - { - client.TransitionToMap(188, new Position(2, 19)); - return; - } - - client.SendServerMessage(ServerMessageType.ActiveMessage, "I'm afraid I'll drown if I slip under this rock! I need to find some Scuba Gear."); + foreach (var npc in ServerSetup.Instance.GlobalMundaneCache) + { + if (npc.Value.Scripts is null) continue; + if (!npc.Value.Scripts.TryGetValue("Rifting Warden", out var scriptObj)) continue; + scriptObj.OnClick(client, npc.Value.Serial); break; + } } + + if (client.Aisling.EquipmentManager.Equipment[16]?.Item?.Template.Name == "Scuba Gear") return; + if (client.Aisling.Race.RaceFlagIsSet(Race.Merfolk)) return; + var drownTick = client.Aisling.MaximumHp * 0.05; + client.Aisling.CurrentHp -= (long)drownTick; + client.SendAttributes(StatUpdateType.Vitality); + + if (!(client.Aisling.CurrentHp <= client.Aisling.MaximumHp * 0.10)) return; + var drown = new DebuffReaping(); + drown.OnApplied(client.Aisling, drown); } - public override void OnItemDropped(WorldClient client, Item itemDropped, Position locationDropped) { } - public override void OnGossip(WorldClient client, string message) { } + public override void OnItemDropped(WorldClient client, Item itemDropped, Position locationDropped) + { + if (client.Aisling.EquipmentManager.Equipment[16]?.Item?.Template.Name == "Scuba Gear") return; + if (client.Aisling.Race.RaceFlagIsSet(Race.Merfolk)) return; + var drownTick = client.Aisling.MaximumHp * 0.05; + client.Aisling.CurrentHp -= (long)drownTick; + client.SendAttributes(StatUpdateType.Vitality); + + if (!(client.Aisling.CurrentHp <= client.Aisling.MaximumHp * 0.10)) return; + var drown = new DebuffReaping(); + drown.OnApplied(client.Aisling, drown); + } + + public override void OnGossip(WorldClient client, string message) + { + if (client.Aisling.EquipmentManager.Equipment[16]?.Item?.Template.Name == "Scuba Gear") return; + if (client.Aisling.Race.RaceFlagIsSet(Race.Merfolk)) return; + var drownTick = client.Aisling.MaximumHp * 0.05; + client.Aisling.CurrentHp -= (long)drownTick; + client.SendAttributes(StatUpdateType.Vitality); + + if (!(client.Aisling.CurrentHp <= client.Aisling.MaximumHp * 0.10)) return; + var drown = new DebuffReaping(); + drown.OnApplied(client.Aisling, drown); + } } \ No newline at end of file diff --git a/Zolian.Server.Base/GameScripts/Areas/Abel/AtlantisEntrance.cs b/Zolian.Server.Base/GameScripts/Areas/Abel/AtlantisEntrance.cs new file mode 100644 index 00000000..b8e07a93 --- /dev/null +++ b/Zolian.Server.Base/GameScripts/Areas/Abel/AtlantisEntrance.cs @@ -0,0 +1,40 @@ +using Darkages.Network.Client; +using Darkages.ScriptingBase; +using Darkages.Types; +using System.Numerics; +using Darkages.Enums; +using Darkages.Sprites.Entity; + +namespace Darkages.GameScripts.Areas.Abel; + +[Script("AtlantisEntrance")] +public class AtlantisEntrance : AreaScript +{ + public AtlantisEntrance(Area area) : base(area) => Area = area; + public override void Update(TimeSpan elapsedTime) { } + public override void OnMapEnter(WorldClient client) { } + public override void OnMapExit(WorldClient client) { } + + public override void OnPlayerWalk(WorldClient client, Position oldLocation, Position newLocation) + { + var vectorMap = new Vector2(newLocation.X, newLocation.Y); + if (client.Aisling.Pos != vectorMap) return; + + switch (newLocation.X) + { + case 15 when newLocation.Y == 14: + if (client.Aisling.QuestManager.ScubaGearCrafted && client.Aisling.EquipmentManager.Equipment[16]?.Item?.Template.Name == "Scuba Gear" + || client.Aisling.Race.RaceFlagIsSet(Race.Merfolk)) + { + client.TransitionToMap(188, new Position(2, 19)); + return; + } + + client.SendServerMessage(ServerMessageType.ActiveMessage, "I'm afraid I'll drown if I slip under this rock! I need to find some Scuba Gear."); + break; + } + } + + public override void OnItemDropped(WorldClient client, Item itemDropped, Position locationDropped) { } + public override void OnGossip(WorldClient client, string message) { } +} \ No newline at end of file diff --git a/Zolian.Server.Base/GameScripts/Areas/Generic/Rift.cs b/Zolian.Server.Base/GameScripts/Areas/Generic/Rift.cs new file mode 100644 index 00000000..ebfbbac2 --- /dev/null +++ b/Zolian.Server.Base/GameScripts/Areas/Generic/Rift.cs @@ -0,0 +1,66 @@ +using Darkages.Common; +using Darkages.Network.Client; +using Darkages.ScriptingBase; +using Darkages.Sprites; +using Darkages.Types; + +using System.Collections.Concurrent; +using System.Security.Cryptography; + +namespace Darkages.GameScripts.Areas.Generic; + +[Script("Rift")] +public class Rift : AreaScript +{ + private readonly ConcurrentDictionary _playersOnMap = []; + private WorldServerTimer AnimTimer { get; } + private bool _animate; + + public Rift(Area area) : base(area) + { + Area = area; + AnimTimer = new WorldServerTimer(TimeSpan.FromMilliseconds(1 + 2000)); + } + + public override void Update(TimeSpan elapsedTime) + { + if (_playersOnMap.IsEmpty) + _animate = false; + + if (_animate) + HandleMapAnimations(elapsedTime); + } + + public override void OnMapEnter(WorldClient client) + { + _playersOnMap.TryAdd(client.Aisling.Serial, client.Aisling); + client.SendServerMessage(ServerMessageType.ActiveMessage, "Strong one, temper your will!"); + client.SendSound((byte)Random.Shared.Next(119), true); + if (!_playersOnMap.IsEmpty) + _animate = true; + } + + public override void OnMapExit(WorldClient client) + { + _playersOnMap.TryRemove(client.Aisling.Serial, out _); + + if (_playersOnMap.IsEmpty) + _animate = false; + } + + public override void OnPlayerWalk(WorldClient client, Position oldLocation, Position newLocation) => _playersOnMap.TryAdd(client.Aisling.Serial, client.Aisling); + + private void HandleMapAnimations(TimeSpan elapsedTime) + { + var a = AnimTimer.Update(elapsedTime); + if (!a) return; + if (_playersOnMap.IsEmpty) return; + + for (var i = 0; i < 6; i++) + { + var randA = Random.Shared.Next(0, 40); + var randB = Random.Shared.Next(80, 119); + _playersOnMap.Values.FirstOrDefault()?.SendAnimationNearby(384, new Position(randA, randB)); + } + } +} \ No newline at end of file diff --git a/Zolian.Server.Base/GameScripts/Formulas/ItemQualityVariance.cs b/Zolian.Server.Base/GameScripts/Formulas/ItemQualityVariance.cs index 2ca72636..7c38bd32 100644 --- a/Zolian.Server.Base/GameScripts/Formulas/ItemQualityVariance.cs +++ b/Zolian.Server.Base/GameScripts/Formulas/ItemQualityVariance.cs @@ -108,7 +108,8 @@ public static Item.Quality DetermineHighQuality() > .77 and <= .89 => Item.Quality.Epic, > .89 and <= .99 => Item.Quality.Legendary, > .99 and <= .997 => Item.Quality.Forsaken, - > .997 => Item.Quality.Mythic, + > .997 and <= .999 => Item.Quality.Mythic, + > 1 => Item.Quality.Primordial, _ => Item.Quality.Rare }; } diff --git a/Zolian.Server.Base/GameScripts/Formulas/RiftRewards.cs b/Zolian.Server.Base/GameScripts/Formulas/RiftRewards.cs new file mode 100644 index 00000000..8821fd5c --- /dev/null +++ b/Zolian.Server.Base/GameScripts/Formulas/RiftRewards.cs @@ -0,0 +1,419 @@ +using Darkages.Common; +using Darkages.Enums; +using Darkages.GameScripts.Creations; +using Darkages.Network.Server; +using Darkages.ScriptingBase; +using Darkages.Sprites; +using Darkages.Sprites.Entity; +using Darkages.Types; + +using static Darkages.Sprites.Entity.Item; + +namespace Darkages.GameScripts.Formulas; + +[Script("Rift Rewards")] +public class RiftRewards : RewardScript +{ + private readonly Monster _monster; + + public RiftRewards(Monster monster, Aisling player) + { + _monster = monster; + _ = player; + } + + public override void GenerateRewards(Monster monster, Aisling player) + { + GenerateExperience(player, true); + if (monster.Level >= 250 && player.ExpLevel >= 250 && player.Stage >= ClassStage.Master) + GenerateAbility(player, true); + GenerateGold(); + GenerateDrops(monster, player); + } + + public override void GenerateInanimateRewards(Monster monster, Aisling player) + { + GenerateGold(); + DetermineDefinedMonsterDrop(monster, player); + } + + private void DetermineRandomSpecialDrop(Monster monster, Aisling player) + { + var dropList = JoinList(monster); + if (dropList.Count <= 0) return; + var items = new List(); + + // Build item list based off of rewards a player can receive from the Monster's level + foreach (var drop in dropList.Where(drop => ServerSetup.Instance.GlobalItemTemplateCache.ContainsKey(drop))) + { + // Equipment & Enchantable + if (ServerSetup.Instance.GlobalItemTemplateCache[drop].Flags.FlagIsSet(ItemFlags.Equipable) || ServerSetup.Instance.GlobalItemTemplateCache[drop].Enchantable) + { + var chance = Generator.RandomPercentPrecise(); + if (chance > ServerSetup.Instance.GlobalItemTemplateCache[drop].DropRate) continue; + + var equipItem = new Item(); + + if (ServerSetup.Instance.GlobalItemTemplateCache[drop].Enchantable) + { + var quality = ItemQualityVariance.DetermineHighQuality(); + var variance = ItemQualityVariance.DetermineVariance(); + var wVariance = ItemQualityVariance.DetermineWeaponVariance(); + equipItem = equipItem.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop], quality, variance, wVariance); + ItemQualityVariance.ItemDurability(equipItem, quality); + items.Add(equipItem); + continue; + } + + equipItem = equipItem.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop]); + ItemQualityVariance.ItemDurability(equipItem, Quality.Common); + items.Add(equipItem); + continue; + } + + var item2 = new Item(); + item2 = item2.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop]); + items.Add(item2); + } + + // Build a list of items based on chance + var buildItemsList = BuildItemList(items); + + var numberOfItems = Generator.RandNumGen3(); + if (numberOfItems == 0) return; + + // Populate a maximum of items based on chance + var maxTwoOrThreeItemsList = RandomPullMaxItems(buildItemsList, numberOfItems); + + // Display rewards + foreach (var item in maxTwoOrThreeItemsList) + { + item.Release(_monster, _monster.Position); + + if (item.Enchantable && item.ItemQuality is Quality.Epic or Quality.Legendary or Quality.Forsaken) + { + Task.Delay(100).ContinueWith(ct => + { + player.SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendSound(88, false)); + }); + } + + if (item.Enchantable && item.ItemQuality is Quality.Mythic) + { + Task.Delay(100).ContinueWith(ct => + { + player.SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendSound(157, false)); + }); + } + } + } + + private void DetermineDefinedMonsterDrop(Monster monster, Aisling player) + { + var templateDrops = monster.Template.Drops; + if (templateDrops.Count <= 0) return; + var items = new List(); + + // Build item list based off of rewards a player can receive from the Monster's level + foreach (var drop in templateDrops.Where(drop => ServerSetup.Instance.GlobalItemTemplateCache.ContainsKey(drop))) + { + // Equipment & Enchantable + if (ServerSetup.Instance.GlobalItemTemplateCache[drop].Flags.FlagIsSet(ItemFlags.Equipable) || ServerSetup.Instance.GlobalItemTemplateCache[drop].Enchantable) + { + var chance = Generator.RandomPercentPrecise(); + if (chance > ServerSetup.Instance.GlobalItemTemplateCache[drop].DropRate) continue; + + var equipItem = new Item(); + + if (ServerSetup.Instance.GlobalItemTemplateCache[drop].Enchantable) + { + var quality = ItemQualityVariance.DetermineHighQuality(); + var variance = ItemQualityVariance.DetermineVariance(); + var wVariance = ItemQualityVariance.DetermineWeaponVariance(); + equipItem = equipItem.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop], quality, variance, wVariance); + ItemQualityVariance.ItemDurability(equipItem, quality); + items.Add(equipItem); + continue; + } + + equipItem = equipItem.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop]); + ItemQualityVariance.ItemDurability(equipItem, Quality.Common); + items.Add(equipItem); + continue; + } + + var chance2 = Generator.RandomPercentPrecise(); + var item2 = new Item(); + item2 = item2.Create(_monster, ServerSetup.Instance.GlobalItemTemplateCache[drop]); + if (chance2 <= item2.Template.DropRate) + items.Add(item2); + } + + // Build a list of items based on chance + var buildItemsList = BuildLowChanceItemList(items); + var monsterDefinedDrop = RandomPullOneItem(buildItemsList); + + // Display reward + foreach (var item in monsterDefinedDrop) + { + item.Release(_monster, _monster.Position); + + if (item.Enchantable && item.ItemQuality is Quality.Epic or Quality.Legendary or Quality.Forsaken) + { + Task.Delay(100).ContinueWith(ct => + { + player.SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendSound(88, false)); + }); + } + + if (item.Enchantable && item.ItemQuality is Quality.Mythic) + { + Task.Delay(100).ContinueWith(ct => + { + player.SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendSound(157, false)); + }); + } + } + } + + private static List BuildLowChanceItemList(List itemsList) + { + var buildItemsList = new List(); + + foreach (var item in itemsList) + { + var chance = Generator.RandomPercentPrecise(); + switch (chance) + { + // If greater than equal 40%, don't add the item + case >= .40: + continue; + default: + buildItemsList.Add(item); + continue; + } + } + + return buildItemsList; + } + + private static List BuildItemList(List itemsList) + { + var buildItemsList = new List(); + + foreach (var item in itemsList) + { + var chance = Generator.RandomPercentPrecise(); + switch (chance) + { + // If greater than equal 85%, don't add the item + case >= .85: + continue; + default: + buildItemsList.Add(item); + continue; + } + } + + return buildItemsList; + } + + private static List RandomPullOneItem(List itemsList) + { + var randomItem = new List(); + + if (itemsList.Count > 1) + { + var item = itemsList.RandomIEnum(); + randomItem.Add(item); + } + else + { + return itemsList; + } + + return randomItem; + } + + private static List RandomPullMaxItems(List itemsList, int count) + { + var maxTwoOrThreeItemsList = new List(); + + if (itemsList.Count > 1) + { + for (var i = 0; i < count; i++) + { + var item = itemsList.RandomIEnum(); + maxTwoOrThreeItemsList.Add(item); + } + } + else + { + return itemsList; + } + + return maxTwoOrThreeItemsList; + } + + private void GenerateDrops(Monster monster, Aisling player) + { + DetermineRandomSpecialDrop(monster, player); + DetermineDefinedMonsterDrop(monster, player); + } + + private void GenerateExperience(Aisling player, bool canCrit = false) + { + var exp = (int)_monster.Experience * 2; + + if (canCrit) + { + var critical = Generator.RandomPercentPrecise(); + + if (critical >= .85) + { + exp *= 2; + player.SendAnimationNearby(341, null, player.Serial); + } + } + + var difference = player.ExpLevel + player.AbpLevel - _monster.Template.Level; + var soloExp = LevelRestrictionsOnExpAp(exp, difference); + + // Enqueue experience event + if (player.WithinRangeOf(_monster, 16)) + player.Client.EnqueueExperienceEvent(player, soloExp, true); + + if (player.GroupParty?.PartyMembers == null) return; + + // Enqueue experience event for party members + foreach (var party in player.GroupParty.PartyMembers.Values.Where(party => party.Serial != player.Serial)) + { + if (party.Map != _monster.Map) continue; + if (!party.WithinRangeOf(_monster, 16)) continue; + + var partyDiff = party.ExpLevel + player.AbpLevel - _monster.Template.Level; + var partyExp = LevelRestrictionsOnExpAp(exp, partyDiff); + + party.Client.EnqueueExperienceEvent(party, partyExp, true); + } + } + + private void GenerateAbility(Aisling player, bool canCrit = false) + { + var ap = (int)_monster.Ability * 2; + + if (canCrit) + { + var critical = Generator.RandomPercentPrecise(); + + if (critical >= .85) + { + ap *= 2; + player.SendAnimationNearby(386, null, player.Serial); + } + } + + var difference = player.ExpLevel + player.AbpLevel - _monster.Template.Level; + var soloAp = LevelRestrictionsOnExpAp(ap, difference); + + // Enqueue experience event + if (player.WithinRangeOf(_monster, 16)) + player.Client.EnqueueAbilityEvent(player, (int)soloAp, true); + + if (player.GroupParty?.PartyMembers == null) return; + + // Enqueue experience event for party members + foreach (var party in player.GroupParty.PartyMembers.Values.Where(party => party.Serial != player.Serial)) + { + if (party.ExpLevel < 250 || party.Stage < ClassStage.Master) + { + party.Client.SendServerMessage(ServerMessageType.ActiveMessage, $"{{=sNot able to earn ability points yet"); + continue; + } + + if (party.Map != player.Map) continue; + if (!party.WithinRangeOf(_monster, 16)) continue; + + var partyDiff = party.ExpLevel + player.AbpLevel - _monster.Template.Level; + var partyExp = LevelRestrictionsOnExpAp(ap, partyDiff); + + party.Client.EnqueueAbilityEvent(party, (int)partyExp, true); + } + } + + private static long LevelRestrictionsOnExpAp(long exp, int difference) + { + var restrictedExp = difference switch + { + // Monster is higher level than player + <= -80 => 1, + <= -50 => (int)(exp * 0.25), + <= -30 => (int)(exp * 0.5), + <= -15 => (int)(exp * 0.75), + // Monster is lower level than player + >= 80 => 1, + >= 50 => (int)(exp * 0.15), + >= 30 => (int)(exp * 0.33), + >= 15 => (int)(exp * 0.66), + _ => exp + }; + + return restrictedExp; + } + + private void GenerateGold() + { + if (!_monster.Template.LootType.LootFlagIsSet(LootQualifer.Gold)) return; + + var sum = (uint)Random.Shared.Next(_monster.Template.Level * 500, _monster.Template.Level * 1500); + + if (_monster.Template.LootType.LootFlagIsSet(LootQualifer.LootGoblinG) || + _monster.Template.LootType.LootFlagIsSet(LootQualifer.LootGoblinY) || + _monster.Template.LootType.LootFlagIsSet(LootQualifer.LootGoblinP) || + _monster.Template.LootType.LootFlagIsSet(LootQualifer.LootGoblinO) || + _monster.Template.LootType.LootFlagIsSet(LootQualifer.LootGoblinR)) + { + sum *= (uint)_monster.Template.LootType; + } + + if (sum > 0) + Money.Create(_monster, sum, new Position(_monster.Pos.X, _monster.Pos.Y)); + } + + private static List JoinList(Monster monster) + { + var dropList = new List(); + var ring = GenerateDropsBasedOnLevel(monster, RingDrops); + var belt = GenerateDropsBasedOnLevel(monster, BeltDrops); + var boot = GenerateDropsBasedOnLevel(monster, BootDrops); + var earring = GenerateDropsBasedOnLevel(monster, EarringDrops); + var greaves = GenerateDropsBasedOnLevel(monster, GreaveDrops); + var hand = GenerateDropsBasedOnLevel(monster, HandDrops); + var necklace = GenerateDropsBasedOnLevel(monster, NecklaceDrops); + var offHand = GenerateDropsBasedOnLevel(monster, OffHandDrops); + var shield = GenerateDropsBasedOnLevel(monster, ShieldDrops); + var wrist = GenerateDropsBasedOnLevel(monster, WristDrops); + if (ring != null) + dropList.AddRange(ring); + if (belt != null) + dropList.AddRange(belt); + if (boot != null) + dropList.AddRange(boot); + if (earring != null) + dropList.AddRange(earring); + if (greaves != null) + dropList.AddRange(greaves); + if (hand != null) + dropList.AddRange(hand); + if (necklace != null) + dropList.AddRange(necklace); + if (offHand != null) + dropList.AddRange(offHand); + if (shield != null) + dropList.AddRange(shield); + if (wrist != null) + dropList.AddRange(wrist); + + return dropList; + } +} \ No newline at end of file diff --git a/Zolian.Server.Base/GameScripts/Mundanes/Abel/Chromitus.cs b/Zolian.Server.Base/GameScripts/Mundanes/Abel/Chromitus.cs new file mode 100644 index 00000000..92f9ae9f --- /dev/null +++ b/Zolian.Server.Base/GameScripts/Mundanes/Abel/Chromitus.cs @@ -0,0 +1,110 @@ +using Darkages.Common; +using Darkages.Enums; +using Darkages.GameScripts.Formulas; +using Darkages.Network.Client; +using Darkages.Network.Server; +using Darkages.ScriptingBase; +using Darkages.Sprites.Entity; +using Darkages.Templates; +using Darkages.Types; + +namespace Darkages.GameScripts.Mundanes.Abel; + +[Script("Rifting Warden")] +public class Chromitus : MundaneScript +{ + public Chromitus(WorldServer server, Mundane mundane) : base(server, mundane) { } + + public override void OnClick(WorldClient client, uint serial) + { + base.OnClick(client, serial); + TopMenu(client); + } + + protected override void TopMenu(WorldClient client) + { + base.TopMenu(client); + + var options = new List + { + new(0x01, "500 - 550"), + new(0x02, "550 - 600"), + new(0x03, "600 - 650"), + new(0x04, "650 - 700"), + new(0x05, "700 - 750"), + new(0x06, "750 - 800"), + new(0x07, "800 - 850"), + new(0x08, "850 - 900"), + new(0x09, "900 - 950"), + new(0x0A, "950 - 1000"), + new(0x00, "1000+") + }; + + client.SendOptionsDialog(Mundane, "You found your way here.. If you're strong enough, I'll allow you entry to complete the trials.\nNow, which rift would you like to enter?", options.ToArray()); + } + + public override void OnResponse(WorldClient client, ushort responseID, string args) + { + var rand = Random.Shared.Next(0, 1); + + switch (responseID) + { + case 0x01: + { + client.TransitionToMap(801, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x02: + { + client.TransitionToMap(802, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x03: + { + client.TransitionToMap(803, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x04: + { + client.TransitionToMap(804, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x05: + { + client.TransitionToMap(805, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x06: + { + client.TransitionToMap(806, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x07: + { + client.TransitionToMap(807, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x08: + { + client.TransitionToMap(808, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x09: + { + client.TransitionToMap(809, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + case 0x0A: + { + client.TransitionToMap(810, rand == 0 ? new Position(4, 4) : new Position(122, 117)); + } + break; + } + } + + public override void OnGossip(WorldClient client, string message) + { + if (!message.Contains("rift", StringComparison.InvariantCultureIgnoreCase)) return; + TopMenu(client); + } +} \ No newline at end of file diff --git a/Zolian.Server.Base/GameScripts/Mundanes/Generic/UserHelper.cs b/Zolian.Server.Base/GameScripts/Mundanes/Generic/UserHelper.cs index d50c90b6..4e3184f5 100644 --- a/Zolian.Server.Base/GameScripts/Mundanes/Generic/UserHelper.cs +++ b/Zolian.Server.Base/GameScripts/Mundanes/Generic/UserHelper.cs @@ -6,6 +6,7 @@ using Darkages.Sprites.Entity; using System.Globalization; using System.Text.RegularExpressions; +using Darkages.GameScripts.Monsters; namespace Darkages.GameScripts.Mundanes.Generic; @@ -82,6 +83,7 @@ protected override void TopMenu(WorldClient client) var latencyCode = ColorCodeLatency(latency); var mapNum = client.Aisling.Map.ID; var playerBoxed = client.Aisling.ExpTotal; + if (mapNum is >= 800 and <= 810) mapNum = 0; client.SendServerMessage(ServerMessageType.ScrollWindow, $"{{=gMap#: {{=a{mapNum} {{=gInsight: {{=b{level} {{=gRank: {{=b{ability} {{=gLatency: {{={latencyCode}{latencyMs}\n" + $"{{=gBase Stats| {{=cS:{{=a{baseStr}{{=c, I:{{=a{baseInt}{{=c, W:{{=a{baseWis}{{=c, C:{{=a{baseCon}{{=c, D:{{=a{baseDex}\n" + diff --git a/Zolian.Server.Base/Network/Client/WorldClient.cs b/Zolian.Server.Base/Network/Client/WorldClient.cs index a908c3d9..4c19be54 100644 --- a/Zolian.Server.Base/Network/Client/WorldClient.cs +++ b/Zolian.Server.Base/Network/Client/WorldClient.cs @@ -524,6 +524,8 @@ public void DeathStatusCheck() Aisling.SendTargetedClientMethod(PlayerScope.AislingsOnSameMap, c => c.SendServerMessage(ServerMessageType.ActiveMessage, $"{Aisling.Username} has died.")); } + ReviveOnPlayerKillMap(Aisling); + return; } @@ -534,6 +536,57 @@ public void DeathStatusCheck() EnqueueDebuffAppliedEvent(Aisling, debuff); } + private static void ReviveOnPlayerKillMap(Aisling aisling) + { + Task.Delay(1000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 10"); + }); + Task.Delay(2000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 9"); + }); + Task.Delay(3000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 8"); + }); + Task.Delay(4000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 7"); + }); + Task.Delay(5000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 6"); + }); + Task.Delay(6000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 5"); + }); + Task.Delay(7000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 4"); + }); + Task.Delay(8000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 3"); + }); + Task.Delay(9000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 2"); + }); + Task.Delay(10000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revive in 1"); + }); + Task.Delay(11000).ContinueWith(c => + { + aisling.Client.SendServerMessage(ServerMessageType.ActiveMessage, "Revived"); + aisling.Client.Recover(); + aisling.Client.UpdateDisplay(); + aisling.Client.SendDisplayAisling(aisling); + }); + } + #region Player Load public async Task Load() diff --git a/Zolian.Server.Base/ScriptingBase/ItemScript.cs b/Zolian.Server.Base/ScriptingBase/ItemScript.cs index 1c432b4e..2e97aebc 100644 --- a/Zolian.Server.Base/ScriptingBase/ItemScript.cs +++ b/Zolian.Server.Base/ScriptingBase/ItemScript.cs @@ -48,9 +48,11 @@ protected void CalculateGearPoints(IWorldClient client) totalPoints += 1000; break; case Item.Quality.Primordial: - case Item.Quality.Transcendent: totalPoints += 2000; break; + case Item.Quality.Transcendent: + totalPoints += 3000; + break; } switch (slot.Item.ItemMaterial) diff --git a/Zolian.Server.Base/Sprites/Entity/Item.cs b/Zolian.Server.Base/Sprites/Entity/Item.cs index 9147adf0..33e29fdc 100644 --- a/Zolian.Server.Base/Sprites/Entity/Item.cs +++ b/Zolian.Server.Base/Sprites/Entity/Item.cs @@ -1028,7 +1028,9 @@ private void QualityVarianceCalc(WorldClient client, Item equipment) {Quality.Epic, new QualityBonus(2, 2, 2, 2, 2, 2, 10, 1, 10, 750, 250, 5)}, {Quality.Legendary, new QualityBonus(3, 3, 3, 3, 3, 15, 20, 2, 20, 1000, 500, 10)}, {Quality.Forsaken, new QualityBonus(4, 4, 4, 4, 4, 20, 25, 3, 25, 1500, 1000, 20)}, - {Quality.Mythic, new QualityBonus(5, 5, 5, 5, 5, 25, 30, 5, 30, 2500, 2000, 30)} + {Quality.Mythic, new QualityBonus(5, 5, 5, 5, 5, 25, 30, 5, 30, 2500, 2000, 30)}, + {Quality.Primordial, new QualityBonus(8, 8, 8, 8, 8, 30, 30, 8, 40, 10000, 10000, 40)}, + {Quality.Transcendent, new QualityBonus(10, 10, 10, 10, 10, 45, 45, 10, 45, 15000, 15000, 50)} }; if (!qualityBonuses.TryGetValue(equipment.ItemQuality, out var bonus)) return; diff --git a/Zolian.Server.Base/Sprites/Entity/Monster.cs b/Zolian.Server.Base/Sprites/Entity/Monster.cs index f227ed2b..9b7ba812 100644 --- a/Zolian.Server.Base/Sprites/Entity/Monster.cs +++ b/Zolian.Server.Base/Sprites/Entity/Monster.cs @@ -178,6 +178,19 @@ public void GenerateRewards(Aisling player) player.UpdateStats(); } + public void GenerateRiftRewards(Aisling player) + { + if (Rewarded) return; + if (player.Equals(null)) return; + if (player.Client.Aisling == null) return; + + var script = ScriptManager.Load("Rift Rewards", this, player).FirstOrDefault(); + script.Value?.GenerateRewards(this, player); + + Rewarded = true; + player.UpdateStats(); + } + public void GenerateInanimateRewards(Aisling player) { if (Rewarded) return;