mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
implement interception rewards, random item boxes
This commit is contained in:
@@ -178,6 +178,10 @@ namespace EpinelPS.Data
|
||||
|
||||
[LoadRecord("ConditionRewardTable.json", "id", typeof(ConditionRewardTable))]
|
||||
public readonly Dictionary<int, ConditionRewardRecord> ConditionRewards = [];
|
||||
[LoadRecord("ItemConsumeTable.json", "id", typeof(ItemConsumeTable))]
|
||||
public readonly Dictionary<int, ItemConsumeRecord> ConsumableItems = [];
|
||||
[LoadRecord("ItemRandomTable.json", "id", typeof(RandomItemTable))]
|
||||
public readonly Dictionary<int, RandomItemRecord> RandomItem = [];
|
||||
static async Task<GameData> BuildAsync()
|
||||
{
|
||||
await Load();
|
||||
|
||||
@@ -794,5 +794,41 @@
|
||||
{
|
||||
public List<ConditionRewardRecord> records = [];
|
||||
}
|
||||
|
||||
public enum ItemSubType
|
||||
{
|
||||
BundleBox,
|
||||
ItemRandomBoxList,
|
||||
ItemRandomBoxNormal,
|
||||
TimeReward,
|
||||
Box,
|
||||
ProfileRandomBox,
|
||||
ArcadeItem,
|
||||
EquipCombination
|
||||
}
|
||||
public class ItemConsumeRecord
|
||||
{
|
||||
public int id;
|
||||
public string use_type = "";
|
||||
public string item_type = "";
|
||||
public ItemSubType item_sub_type;
|
||||
public int use_id;
|
||||
}
|
||||
public class ItemConsumeTable
|
||||
{
|
||||
public List<ItemConsumeRecord> records = [];
|
||||
}
|
||||
public class RandomItemRecord
|
||||
{
|
||||
public int id;
|
||||
public int group_id;
|
||||
public string reward_type = "";
|
||||
public int reward_id;
|
||||
public int reward_value_min;
|
||||
public int reward_value_max;
|
||||
public int ratio;
|
||||
}
|
||||
public class RandomItemTable
|
||||
{
|
||||
public List<RandomItemRecord> records = [];
|
||||
}
|
||||
}
|
||||
|
||||
32
EpinelPS/LobbyServer/Inventory/UseRandomBox.cs
Normal file
32
EpinelPS/LobbyServer/Inventory/UseRandomBox.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using EpinelPS.Database;
|
||||
using EpinelPS.Utils;
|
||||
|
||||
namespace EpinelPS.LobbyServer.Inventory
|
||||
{
|
||||
[PacketPath("/inventory/userandombox")]
|
||||
public class UseRandomBox : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqUseRandomBox>();
|
||||
var user = GetUser();
|
||||
|
||||
var response = new ResUseRandomBox();
|
||||
|
||||
var box = user.Items.Where(x => x.Isn == req.Isn).FirstOrDefault() ?? throw new InvalidDataException("cannot find box with isn " + req.Isn);
|
||||
if (req.Count > box.Count) throw new Exception("count mismatch");
|
||||
|
||||
box.Count -= req.Count;
|
||||
if (box.Count == 0) user.Items.Remove(box);
|
||||
|
||||
response.Reward = NetUtils.UseLootBox(user, box.ItemType, req.Count);
|
||||
|
||||
// update client side box count
|
||||
response.Reward.UserItems.Add(NetUtils.UserItemDataToNet(box));
|
||||
|
||||
JsonDb.Save();
|
||||
|
||||
await WriteDataAsync(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using EpinelPS.Database;
|
||||
using EpinelPS.Data;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using EpinelPS.Data;
|
||||
using EpinelPS.Database;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using static Google.Rpc.Context.AttributeContext.Types;
|
||||
|
||||
namespace EpinelPS.Utils
|
||||
@@ -43,6 +44,22 @@ namespace EpinelPS.Utils
|
||||
};
|
||||
}
|
||||
|
||||
internal static NetUserItemData UserItemDataToNet(ItemData item)
|
||||
{
|
||||
return new NetUserItemData()
|
||||
{
|
||||
Count = item.Count,
|
||||
Tid = item.ItemType,
|
||||
Csn = item.Csn,
|
||||
Lv = item.Level,
|
||||
Exp = item.Exp,
|
||||
Corporation = item.Corp,
|
||||
Isn = item.Isn,
|
||||
Position = item.Position
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static List<NetUserItemData> GetUserItems(User user)
|
||||
{
|
||||
List<NetUserItemData> ret = new();
|
||||
@@ -58,33 +75,12 @@ namespace EpinelPS.Utils
|
||||
}
|
||||
else
|
||||
{
|
||||
itemDictionary[item.ItemType] = new NetUserItemData()
|
||||
{
|
||||
Count = item.Count,
|
||||
Tid = item.ItemType,
|
||||
Csn = item.Csn,
|
||||
Lv = item.Level,
|
||||
Exp = item.Exp,
|
||||
Corporation = item.Corp,
|
||||
Isn = item.Isn,
|
||||
Position = item.Position
|
||||
};
|
||||
itemDictionary[item.ItemType] = UserItemDataToNet(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newItem = new NetUserItemData()
|
||||
{
|
||||
Count = item.Count,
|
||||
Tid = item.ItemType,
|
||||
Csn = item.Csn,
|
||||
Lv = item.Level,
|
||||
Exp = item.Exp,
|
||||
Corporation = item.Corp,
|
||||
Isn = item.Isn,
|
||||
Position = item.Position
|
||||
};
|
||||
itemDictionary[item.ItemType] = newItem;
|
||||
itemDictionary[item.ItemType] = UserItemDataToNet(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,5 +371,33 @@ namespace EpinelPS.Utils
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static NetRewardData UseLootBox(User user, int boxId, int count)
|
||||
{
|
||||
ItemConsumeRecord? cItem = GameData.Instance.ConsumableItems.Where(x => x.Value.id == boxId).FirstOrDefault().Value ?? throw new Exception("cannot find box id " + boxId);
|
||||
|
||||
if (cItem.use_type != "ItemRandomBox") throw new Exception("expected random box");
|
||||
|
||||
// find matching probability entries
|
||||
var probabilityEntries = GameData.Instance.RandomItem.Values.Where(x => x.group_id == cItem.use_id).ToArray();
|
||||
if (!probabilityEntries.Any()) throw new Exception($"cannot find any probability entries with ID {cItem.use_id}, box ID: {cItem.id}");
|
||||
|
||||
// run probability as many times as needed
|
||||
NetRewardData ret = new() { PassPoint = new() };
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var winningRecord = Rng.PickWeightedItem(probabilityEntries);
|
||||
|
||||
if (winningRecord.reward_value_min != winningRecord.reward_value_max)
|
||||
{
|
||||
Logging.WriteLine("TODO: reward_value_max", LogType.Warning);
|
||||
}
|
||||
|
||||
RewardUtils.AddSingleObject(user, ref ret, winningRecord.reward_id, winningRecord.reward_type, winningRecord.reward_value_min);
|
||||
}
|
||||
JsonDb.Save();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using EpinelPS.Database;
|
||||
using EpinelPS.Data;
|
||||
using EpinelPS.Database;
|
||||
using Org.BouncyCastle.Ocsp;
|
||||
|
||||
namespace EpinelPS.Utils
|
||||
{
|
||||
@@ -56,12 +57,12 @@ namespace EpinelPS.Utils
|
||||
BeforeExp = user.userPointData.ExperiencePoint,
|
||||
BeforeLv = user.userPointData.UserLevel,
|
||||
|
||||
// IncreaseExp = rewardData.user_exp,
|
||||
// IncreaseExp = rewardData.user_exp,
|
||||
CurrentExp = newXp,
|
||||
CurrentLv = newLevel,
|
||||
|
||||
GainExp = rewardData.user_exp,
|
||||
|
||||
|
||||
};
|
||||
user.userPointData.ExperiencePoint = newXp;
|
||||
|
||||
@@ -70,126 +71,167 @@ namespace EpinelPS.Utils
|
||||
|
||||
foreach (var item in rewardData.rewards)
|
||||
{
|
||||
if (item.reward_id != 0 || !string.IsNullOrEmpty(item.reward_type))
|
||||
if (item.reward_percent != 1000000)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.reward_type) || string.IsNullOrWhiteSpace(item.reward_type)) { }
|
||||
else if (item.reward_type == "Currency")
|
||||
{
|
||||
bool found = false;
|
||||
foreach (var currentReward in user.Currency)
|
||||
{
|
||||
if (currentReward.Key == (CurrencyType)item.reward_id)
|
||||
{
|
||||
user.Currency[currentReward.Key] += item.reward_value;
|
||||
|
||||
ret.Currency.Add(new NetCurrencyData()
|
||||
{
|
||||
FinalValue = user.Currency[currentReward.Key],
|
||||
Value = item.reward_value,
|
||||
Type = item.reward_id
|
||||
});
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
user.Currency.Add((CurrencyType)item.reward_id, item.reward_value);
|
||||
ret.Currency.Add(new NetCurrencyData()
|
||||
{
|
||||
FinalValue = item.reward_value,
|
||||
Value = item.reward_value,
|
||||
Type = item.reward_id
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (item.reward_type == "Item")
|
||||
{
|
||||
// Check if user already has said item. If it is level 1, increase item count.
|
||||
// If user does not have item, generate a new item ID
|
||||
if (user.Items.Where(x => x.ItemType == item.reward_id && x.Level == 1).Any())
|
||||
{
|
||||
ItemData? newItem = user.Items.Where(x => x.ItemType == item.reward_id && x.Level == 1).FirstOrDefault();
|
||||
if (newItem != null)
|
||||
{
|
||||
newItem.Count += item.reward_value;
|
||||
|
||||
// Tell the client the reward and its amount
|
||||
ret.Item.Add(new NetItemData()
|
||||
{
|
||||
Count = item.reward_value,
|
||||
Tid = item.reward_id,
|
||||
//Isn = newItem.Isn
|
||||
});
|
||||
|
||||
// Tell the client the new amount of this item
|
||||
ret.UserItems.Add(new NetUserItemData()
|
||||
{
|
||||
Isn = newItem.Isn,
|
||||
Tid = newItem.ItemType,
|
||||
Count = newItem.Count
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("should not occur");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var id = user.GenerateUniqueItemId();
|
||||
user.Items.Add(new ItemData() { ItemType = item.reward_id, Isn = id, Level = 1, Exp = 0, Count = item.reward_value });
|
||||
ret.Item.Add(new NetItemData()
|
||||
{
|
||||
Count = item.reward_value,
|
||||
Tid = item.reward_id,
|
||||
//Isn = id
|
||||
});
|
||||
|
||||
// Tell the client the new amount of this item (which is the same as user did not have item previously)
|
||||
ret.UserItems.Add(new NetUserItemData()
|
||||
{
|
||||
Isn = id,
|
||||
Tid = item.reward_id,
|
||||
Count = item.reward_value
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (item.reward_type == "Memorial")
|
||||
{
|
||||
if (!user.Memorial.Contains(item.reward_id))
|
||||
{
|
||||
ret.Memorial.Add(item.reward_id);
|
||||
user.Memorial.Add(item.reward_id);
|
||||
}
|
||||
}
|
||||
else if (item.reward_type == "Bgm")
|
||||
{
|
||||
if (!user.JukeboxBgm.Contains(item.reward_id))
|
||||
{
|
||||
ret.JukeboxBgm.Add(item.reward_id);
|
||||
user.JukeboxBgm.Add(item.reward_id);
|
||||
}
|
||||
}
|
||||
else if (item.reward_type == "InfraCoreExp")
|
||||
{
|
||||
ret.InfraCoreExp = new NetIncreaseExpData()
|
||||
{
|
||||
BeforeLv = user.InfraCoreLvl,
|
||||
BeforeExp = user.InfraCoreExp,
|
||||
// TODO
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("TODO: Reward type " + item.reward_type);
|
||||
}
|
||||
Logging.WriteLine("WARNING: ignoring percent: " + item.reward_percent / 10000 + ", item will be added anyways", LogType.Warning);
|
||||
}
|
||||
|
||||
AddSingleObject(user, ref ret, item.reward_id, item.reward_type, item.reward_value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a single item to users inventory, and also adds it to ret parameter.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="ret"></param>
|
||||
/// <param name="rewardId"></param>
|
||||
/// <param name="rewardType"></param>
|
||||
/// <param name="rewardCount"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static void AddSingleObject(User user, ref NetRewardData ret, int rewardId, string rewardType, int rewardCount)
|
||||
{
|
||||
if (rewardId != 0 || !string.IsNullOrEmpty(rewardType))
|
||||
{
|
||||
if (string.IsNullOrEmpty(rewardType) || string.IsNullOrWhiteSpace(rewardType)) { }
|
||||
else if (rewardType == "Currency")
|
||||
{
|
||||
bool found = false;
|
||||
foreach (var currentReward in user.Currency)
|
||||
{
|
||||
if (currentReward.Key == (CurrencyType)rewardId)
|
||||
{
|
||||
user.Currency[currentReward.Key] += rewardCount;
|
||||
|
||||
ret.Currency.Add(new NetCurrencyData()
|
||||
{
|
||||
FinalValue = user.Currency[currentReward.Key],
|
||||
Value = rewardCount,
|
||||
Type = rewardId
|
||||
});
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
user.Currency.Add((CurrencyType)rewardId, rewardCount);
|
||||
ret.Currency.Add(new NetCurrencyData()
|
||||
{
|
||||
FinalValue = rewardCount,
|
||||
Value = rewardCount,
|
||||
Type = rewardId
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (rewardType == "Item")
|
||||
{
|
||||
// Check if user already has said item. If it is level 1, increase item count.
|
||||
// If user does not have item, generate a new item ID
|
||||
if (user.Items.Where(x => x.ItemType == rewardId && x.Level == 1).Any())
|
||||
{
|
||||
ItemData? newItem = user.Items.Where(x => x.ItemType == rewardId && x.Level == 1).FirstOrDefault();
|
||||
if (newItem != null)
|
||||
{
|
||||
newItem.Count += rewardCount;
|
||||
|
||||
// Tell the client the reward and its amount
|
||||
ret.Item.Add(new NetItemData()
|
||||
{
|
||||
Count = rewardCount,
|
||||
Tid = rewardId,
|
||||
//Isn = newItem.Isn
|
||||
});
|
||||
|
||||
// Tell the client the new amount of this item
|
||||
ret.UserItems.Add(new NetUserItemData()
|
||||
{
|
||||
Isn = newItem.Isn,
|
||||
Tid = newItem.ItemType,
|
||||
Count = newItem.Count
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("should not occur");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var id = user.GenerateUniqueItemId();
|
||||
user.Items.Add(new ItemData() { ItemType = rewardId, Isn = id, Level = 1, Exp = 0, Count = rewardCount });
|
||||
ret.Item.Add(new NetItemData()
|
||||
{
|
||||
Count = rewardCount,
|
||||
Tid = rewardId,
|
||||
//Isn = id
|
||||
});
|
||||
|
||||
// Tell the client the new amount of this item (which is the same as user did not have item previously)
|
||||
ret.UserItems.Add(new NetUserItemData()
|
||||
{
|
||||
Isn = id,
|
||||
Tid = rewardId,
|
||||
Count = rewardCount
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (rewardType == "Memorial")
|
||||
{
|
||||
if (!user.Memorial.Contains(rewardId))
|
||||
{
|
||||
ret.Memorial.Add(rewardId);
|
||||
user.Memorial.Add(rewardId);
|
||||
}
|
||||
}
|
||||
else if (rewardType == "Bgm")
|
||||
{
|
||||
if (!user.JukeboxBgm.Contains(rewardId))
|
||||
{
|
||||
ret.JukeboxBgm.Add(rewardId);
|
||||
user.JukeboxBgm.Add(rewardId);
|
||||
}
|
||||
}
|
||||
else if (rewardType == "InfraCoreExp")
|
||||
{
|
||||
ret.InfraCoreExp = new NetIncreaseExpData()
|
||||
{
|
||||
BeforeLv = user.InfraCoreLvl,
|
||||
BeforeExp = user.InfraCoreExp,
|
||||
// TODO
|
||||
};
|
||||
}
|
||||
else if (rewardType == "ItemRandomBox")
|
||||
{
|
||||
ItemConsumeRecord? cItem = GameData.Instance.ConsumableItems.Where(x => x.Value.id == rewardId).FirstOrDefault().Value;
|
||||
|
||||
if (cItem.item_sub_type == ItemSubType.ItemRandomBoxList)
|
||||
{
|
||||
NetRewardData reward = NetUtils.UseLootBox(user, rewardId, rewardCount);
|
||||
|
||||
NetUtils.RegisterRewardsForUser(user, reward);
|
||||
ret = NetUtils.MergeRewards([ret, reward], user);
|
||||
}
|
||||
else
|
||||
{
|
||||
var itm = new NetItemData()
|
||||
{
|
||||
Count = rewardCount,
|
||||
Tid = cItem.id,
|
||||
Isn = user.GenerateUniqueItemId()
|
||||
};
|
||||
ret.Item.Add(itm);
|
||||
user.Items.Add(new ItemData() { Count = rewardCount, Isn = itm.Isn, ItemType = itm.Tid });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.WriteLine("TODO: Reward type " + rewardType, LogType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace EpinelPS.Utils
|
||||
using EpinelPS.Data;
|
||||
|
||||
namespace EpinelPS.Utils
|
||||
{
|
||||
public class Rng
|
||||
{
|
||||
@@ -14,6 +16,30 @@
|
||||
public static int RandomId()
|
||||
{
|
||||
return random.Next();
|
||||
} /// <summary>
|
||||
/// Picks a random item. weights is a list of numbers which represents probability, table ids represent ID for weight
|
||||
/// </summary>
|
||||
/// <param name="weights"></param>
|
||||
/// <param name="tableIds"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static RandomItemRecord PickWeightedItem(RandomItemRecord[] records)
|
||||
{
|
||||
int totalWeight = 0;
|
||||
foreach (var item in records)
|
||||
totalWeight += item.ratio;
|
||||
|
||||
int randomNumber = random.Next(0, totalWeight);
|
||||
|
||||
int runningSum = 0;
|
||||
for (int i = 0; i < records.Length; i++)
|
||||
{
|
||||
runningSum += records[i].ratio;
|
||||
if (randomNumber < runningSum)
|
||||
return records[i];
|
||||
}
|
||||
|
||||
throw new Exception("Weight distribution error.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user