Skip to content

Initial Card System Support #330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions sql/updates/update_2024-10-09_1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CREATE TABLE IF NOT EXISTS `cards` (
`characterId` bigint(20) NOT NULL,
`itemId` bigint(20) NOT NULL,
`sort` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `cards`
ADD PRIMARY KEY (`characterId`,`itemId`),
ADD KEY `itemId` (`itemId`);

ALTER TABLE `cards`
ADD CONSTRAINT `cards_ibfk_1` FOREIGN KEY (`characterId`) REFERENCES `characters` (`characterId`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `cards_ibfk_2` FOREIGN KEY (`itemId`) REFERENCES `items` (`itemUniqueId`) ON DELETE CASCADE ON UPDATE CASCADE;

CREATE TABLE IF NOT EXISTS `item_properties` (
`propertyId` bigint(20) NOT NULL,
`itemId` bigint(20) NOT NULL,
`name` varchar(64) NOT NULL,
`type` varchar(1) NOT NULL,
`value` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


ALTER TABLE `item_properties`
ADD PRIMARY KEY (`propertyId`),
ADD KEY `itemId` (`itemId`);


ALTER TABLE `item_properties`
MODIFY `propertyId` bigint(20) NOT NULL AUTO_INCREMENT;

ALTER TABLE `item_properties`
ADD CONSTRAINT `item_properties_ibfk_1` FOREIGN KEY (`itemId`) REFERENCES `items` (`itemUniqueId`) ON DELETE CASCADE ON UPDATE CASCADE;
140 changes: 140 additions & 0 deletions src/Shared/Data/Database/ItemExp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Melia.Shared.Game.Const;
using Newtonsoft.Json.Linq;
using Yggdrasil.Data.JSON;

namespace Melia.Shared.Data.Database
{
[Serializable]
public class ItemExpData
{
public EquipExpGroup Group { get; set; }
public int Level { get; set; }
public int Exp { get; set; }
public int GainExp { get; set; }
public float PriceMultiplier { get; set; } = 1.0f;
}

/// <summary>
/// Item Exp database.
/// </summary>
public class ItemExpDb : DatabaseJson<ItemExpData>
{
private readonly List<ItemExpData> _itemExp = new();

public int GetGainExp(EquipExpGroup group, int level = 1)
{
var exp = this.Entries.Find(a => a.Group == group && a.Level == 1)?.GainExp ?? 0;

return exp;
}

/// <summary>
/// Returns exp required to reach the next level after the
/// given one.
/// </summary>
/// <remarks>
/// Returns 0 if there's no data for the given level,
/// i.e. if it's the last one or goes beyond.
/// </remarks>
/// <param name="level"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">Thrown if level is invalid (< 1).</exception>
public int GetNextExp(EquipExpGroup group, int level)
{
if (level < 1)
throw new ArgumentException("Invalid level (too low).");

if (level > _itemExp.Count)
return 0;

var index = level - 1;
var exp = this.Entries.Where(a => a.Group == group).OrderBy(a => a.Level).ToList()[index];

return exp.Exp;
}

/// <summary>
/// Returns the EXP required to reach the given level.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
public int GetLevel(EquipExpGroup group, int exp)
{
var result = 1;
var data = this.Entries.Where(a => a.Group == group && a.Exp <= exp).OrderBy(a => a.Level).LastOrDefault();
if (data != null)
result = data.Level;

return result;
}

/// <summary>
/// Returns the price multiplier at a certain level.
/// </summary>
/// <param name="level"></param>
/// <returns></returns>
public float GetPriceMultiplier(EquipExpGroup group, int level)
{
var result = 1f;
var data = this.Entries.Find(a => a.Level == level);
if (data != null)
result = data.PriceMultiplier;

return result;
}

/// <summary>
/// Returns the EXP required to reach the given level.
/// </summary>
/// <param name="level"></param>
/// <returns></returns>
public int GetTotalExp(EquipExpGroup group, int level)
{
var result = 0;
for (var i = 1; i < level; ++i)
result += this.GetNextExp(group, i);

return result;
}

/// <summary>
/// Returns the max level.
/// </summary>
/// <returns></returns>
public int GetMaxLevel(EquipExpGroup group)
{
return _itemExp.Where(a => a.Group == group && a.Exp > 0).Max(a => a.Level);
}

/// <summary>
/// Reads given entry and adds it to the database.
/// </summary>
/// <remarks>
/// Uses the JSON database to be fed its entries, but doesn't
/// adhere to the Entries format and uses custom lists instead.
/// </remarks>
/// <param name="entry"></param>
protected override void ReadEntry(JObject entry)
{

entry.AssertNotMissing("level", "exp");

var data = new ItemExpData();

data.Group = entry.ReadEnum("group", EquipExpGroup.None);
data.Level = entry.ReadInt("level");
data.Exp = entry.ReadInt("exp");
data.GainExp = entry.ReadInt("gainExp");

this.Entries.Add(data);
}

protected override void AfterLoad()
{
_itemExp.AddRange(this.Entries);
}
}
}
11 changes: 10 additions & 1 deletion src/Shared/Data/Database/Items.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public class ItemData

public bool HasScript => this.Script != null;
public bool HasCooldown => this.CooldownTime > TimeSpan.Zero;


public EquipExpGroup EquipExpGroup { get; set; }
public CardGroup CardGroup { get; set; }
public int CardLevel { get; set; }
}

[Serializable]
Expand Down Expand Up @@ -158,7 +163,6 @@ protected override void ReadEntry(JObject entry)
data.Name = entry.ReadString("name");
data.Type = entry.ReadEnum<ItemType>("type");
data.Group = entry.ReadEnum<ItemGroup>("group");
data.Category = GetCategory(data);

data.Weight = entry.ReadFloat("weight", 0);
data.MaxStack = entry.ReadInt("maxStack", 1);
Expand All @@ -168,6 +172,11 @@ protected override void ReadEntry(JObject entry)
data.EquipType2 = entry.ReadEnum<EquipType>("equipType2", EquipType.None);
data.MinLevel = entry.ReadInt("minLevel", 1);

data.Category = GetCategory(data);
data.CardGroup = entry.ReadEnum("cardGroup", CardGroup.None);
data.CardLevel = entry.ReadInt("cardLevel", 0);
data.EquipExpGroup = entry.ReadEnum("equipExpGroup", EquipExpGroup.None);

data.MinAtk = entry.ReadFloat("minAtk", 0);
data.MaxAtk = entry.ReadFloat("maxAtk", 0);
data.PAtk = entry.ReadFloat("pAtk", 0);
Expand Down
1 change: 1 addition & 0 deletions src/Shared/Data/MeliaData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class MeliaData
public HelpDb HelpDb = new HelpDb();
public InvBaseIdDb InvBaseIdDb = new InvBaseIdDb();
public ItemDb ItemDb = new ItemDb();
public ItemExpDb ItemExpDb { get; } = new ItemExpDb();
public ItemMonsterDb ItemMonsterDb = new ItemMonsterDb();
public JobDb JobDb = new JobDb();
public MapDb MapDb = new MapDb();
Expand Down
32 changes: 32 additions & 0 deletions src/Shared/Database/MeliaDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,38 @@ protected void SaveProperties(string databaseName, string idName, long id, Prope
}
}

/// <summary>
/// Saves properties to the given database, with the id.
/// </summary>
/// <param name="databaseName"></param>
/// <param name="idName"></param>
/// <param name="id"></param>
/// <param name="properties"></param>
protected void SaveProperties(string databaseName, string idName, long id, Properties properties, MySqlConnection conn, MySqlTransaction trans)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the current SaveProperties method already gets its own connection and transaction, we could easily call this from it and reduce the redundant code, right?

{
using (var cmd = new MySqlCommand($"DELETE FROM `{databaseName}` WHERE `{idName}` = @id", conn, trans))
{
cmd.Parameters.AddWithValue("@id", id);
cmd.ExecuteNonQuery();
}

foreach (var property in properties.GetAll())
{
var typeStr = property is FloatProperty ? "f" : "s";
var valueStr = property.Serialize();

using (var cmd = new InsertCommand($"INSERT INTO `{databaseName}` {{0}}", conn, trans))
{
cmd.Set(idName, id);
cmd.Set("name", property.Ident);
cmd.Set("type", typeStr);
cmd.Set("value", valueStr);

cmd.Execute();
}
}
}

/// <summary>
/// Updates the login state of the given account.
/// </summary>
Expand Down
68 changes: 68 additions & 0 deletions src/Shared/Game/Const/Items.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,74 @@ public enum InventoryCategory
Armor_Shoulder,
}

/// <summary>
/// Card's group.
/// </summary>
public enum CardGroup
{
None,
ATK,
DEF,
GODDESS,
LEG,
REINFORCE_CARD,
REINFORCE_GODDESS_CARD,
STAT,
UTIL,
}

/// <summary>
/// An item's equip exp group.
/// </summary>
/// <remarks>
/// Field "equipExpGroup" in our data, "EquipXpGroup" in the client.
/// </remarks>
public enum EquipExpGroup
{
None,
Blessed,
Card,
Card_dummy01,
Card_dummy01_Event,
Card_Fish_300,
Card_Gacha_dummy01,
Card_xp_100,
Equip,
Gem,
GemExp_5000,
GemExp_randomQuest1,
GemExp_randomQuest2,
GemExp_randomQuest3,
GemExp_randomQuest4,
GemExp_randomQuest5,
GemExp_Talt01,
GemExpStone01,
GemExpStone02,
GemExpStone03,
GemExpStone04,
GemExpStone05,
GemExpStone07,
GemExpStone09,
GemExpStone10,
GemExpStone11,
GemExpStone12,
Gem_Skill,
Goddess_Card,
Goddess_ReinForce_Card,
hethran_material,
KQ_token_hethran_1,
KQ_token_hethran_2,
KQ_token_hethran_3,
Legend_Card,
Legend_Reinforce_Card_MaxLv4,
Legend_Reinforce_Card_MaxLv6,
Legend_Reinforce_Card_MaxLv8,
Legend_Reinforce_Card_MaxLv10,
Lv10_Card_TP,
Misc,
Old_Card_TP,
}

/// <summary>
/// An item's type.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/Shared/Game/Const/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ public static class AddonMessage
/// Sent when reading a new collection
/// </summary>
public const string UPDATE_READ_COLLECTION_COUNT = "UPDATE_READ_COLLECTION_COUNT";

/// <summary>
/// Item gaining exp
/// </summary>
public const string ITEM_EXP_START = "ITEM_EXP_START";

/// <summary>
/// Stop Item gaining exp
/// </summary>
public const string ITEM_EXP_STOP = "ITEM_EXP_STOP";

/// <summary>
/// Item gaining exp has ended.
/// </summary>
public const string ITEM_EXPUP_END = "ITEM_EXPUP_END";
}

public static class SystemMessage
Expand Down
1 change: 1 addition & 0 deletions src/Shared/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ public void LoadData(ServerType serverType)
this.LoadDb(this.Data.HelpDb, "db/help.txt");
this.LoadDb(this.Data.InvBaseIdDb, "db/invbaseids.txt");
this.LoadDb(this.Data.ItemDb, "db/items.txt");
this.LoadDb(this.Data.ItemExpDb, "db/item_exp.txt");
this.LoadDb(this.Data.ItemMonsterDb, "db/itemmonsters.txt");
this.LoadDb(this.Data.JobDb, "db/jobs.txt");
this.LoadDb(this.Data.MapDb, "db/maps.txt");
Expand Down
Loading