mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-13 15:34:36 +01:00
Implement usetimereward, usepiece (#39)
* implement timereward * implement usepiece * will not grouping selectedCharacters * remove unused field * i forgot remove debug code * maybe recycleroom worked * fix Tid has 0 value * add class research and corporation research * delete unnecessary using * rename args and local var * fix receiving currency reward * remove line from AddSingleCurrencyObject
This commit is contained in:
@@ -185,6 +185,17 @@ namespace EpinelPS.Data
|
|||||||
public readonly Dictionary<int, LostSectorRecord> LostSector = [];
|
public readonly Dictionary<int, LostSectorRecord> LostSector = [];
|
||||||
[LoadRecord("LostSectorStageTable.json", "id", typeof(LostSectorStageTable))]
|
[LoadRecord("LostSectorStageTable.json", "id", typeof(LostSectorStageTable))]
|
||||||
public readonly Dictionary<int, LostSectorStageRecord> LostSectorStages = [];
|
public readonly Dictionary<int, LostSectorStageRecord> LostSectorStages = [];
|
||||||
|
[LoadRecord("ItemPieceTable.json", "id", typeof(ItemPieceTable))]
|
||||||
|
public readonly Dictionary<int, ItemPieceRecord> PieceItems = [];
|
||||||
|
[LoadRecord("GachaGradeProbTable.json", "id", typeof(GachaGradeProbTable))]
|
||||||
|
public readonly Dictionary<int, GachaGradeProbRecord> GachaGradeProb = [];
|
||||||
|
[LoadRecord("GachaListProbTable.json", "id", typeof(GachaListProbTable))]
|
||||||
|
public readonly Dictionary<int, GachaListProbRecord> GachaListProb = [];
|
||||||
|
[LoadRecord("RecycleResearchStatTable.json", "id", typeof(RecycleResearchStatTable))]
|
||||||
|
public readonly Dictionary<int, RecycleResearchStatRecord> RecycleResearchStats = [];
|
||||||
|
[LoadRecord("RecycleResearchLevelTable.json", "id", typeof(RecycleResearchLevelTable))]
|
||||||
|
public readonly Dictionary<int, RecycleResearchLevelRecord> RecycleResearchLevels = [];
|
||||||
|
|
||||||
static async Task<GameData> BuildAsync()
|
static async Task<GameData> BuildAsync()
|
||||||
{
|
{
|
||||||
await Load();
|
await Load();
|
||||||
|
|||||||
@@ -303,6 +303,30 @@
|
|||||||
public List<GachaType> records = [];
|
public List<GachaType> records = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class GachaGradeProbRecord
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public int group_id;
|
||||||
|
public string rare = "";
|
||||||
|
public int prob;
|
||||||
|
public int gacha_list_id;
|
||||||
|
}
|
||||||
|
public class GachaGradeProbTable
|
||||||
|
{
|
||||||
|
public List<GachaGradeProbRecord> records = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GachaListProbRecord
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public int group_id;
|
||||||
|
public int gacha_id;
|
||||||
|
}
|
||||||
|
public class GachaListProbTable
|
||||||
|
{
|
||||||
|
public List<GachaListProbRecord> records = [];
|
||||||
|
}
|
||||||
|
|
||||||
public class EventManager
|
public class EventManager
|
||||||
{
|
{
|
||||||
public int id;
|
public int id;
|
||||||
@@ -812,11 +836,23 @@
|
|||||||
public string item_type = "";
|
public string item_type = "";
|
||||||
public ItemSubType item_sub_type;
|
public ItemSubType item_sub_type;
|
||||||
public int use_id;
|
public int use_id;
|
||||||
|
public int use_value;
|
||||||
}
|
}
|
||||||
public class ItemConsumeTable
|
public class ItemConsumeTable
|
||||||
{
|
{
|
||||||
public List<ItemConsumeRecord> records = [];
|
public List<ItemConsumeRecord> records = [];
|
||||||
}
|
}
|
||||||
|
public class ItemPieceRecord
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public string use_type = "";
|
||||||
|
public int use_id;
|
||||||
|
public int use_value;
|
||||||
|
}
|
||||||
|
public class ItemPieceTable
|
||||||
|
{
|
||||||
|
public List<ItemPieceRecord> records = [];
|
||||||
|
}
|
||||||
public class RandomItemRecord
|
public class RandomItemRecord
|
||||||
{
|
{
|
||||||
public int id;
|
public int id;
|
||||||
@@ -831,6 +867,33 @@
|
|||||||
{
|
{
|
||||||
public List<RandomItemRecord> records = [];
|
public List<RandomItemRecord> records = [];
|
||||||
}
|
}
|
||||||
|
public class RecycleResearchStatRecord
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public string recycle_type = "";
|
||||||
|
public int unlock_condition_id;
|
||||||
|
public int unlock_level;
|
||||||
|
public int attack;
|
||||||
|
public int defense;
|
||||||
|
public int hp;
|
||||||
|
}
|
||||||
|
public class RecycleResearchStatTable
|
||||||
|
{
|
||||||
|
public List<RecycleResearchStatRecord> records = [];
|
||||||
|
}
|
||||||
|
public class RecycleResearchLevelRecord
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public string recycle_type = "";
|
||||||
|
public int recycle_level;
|
||||||
|
public int exp;
|
||||||
|
public int item_id;
|
||||||
|
public int item_value;
|
||||||
|
}
|
||||||
|
public class RecycleResearchLevelTable
|
||||||
|
{
|
||||||
|
public List<RecycleResearchLevelRecord> records = [];
|
||||||
|
}
|
||||||
public enum ContentOpenType
|
public enum ContentOpenType
|
||||||
{
|
{
|
||||||
Stage,
|
Stage,
|
||||||
|
|||||||
@@ -83,6 +83,14 @@ namespace EpinelPS.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long AvailableAt;
|
public long AvailableAt;
|
||||||
}
|
}
|
||||||
|
public class RecycleRoomResearchProgress
|
||||||
|
{
|
||||||
|
public int Level = 1;
|
||||||
|
public int Exp;
|
||||||
|
public int Attack;
|
||||||
|
public int Defense;
|
||||||
|
public int Hp;
|
||||||
|
}
|
||||||
public class SimroomData
|
public class SimroomData
|
||||||
{
|
{
|
||||||
public int CurrentDifficulty;
|
public int CurrentDifficulty;
|
||||||
@@ -266,6 +274,7 @@ namespace EpinelPS.Database
|
|||||||
public List<SynchroSlot> SynchroSlots = new List<SynchroSlot>();
|
public List<SynchroSlot> SynchroSlots = new List<SynchroSlot>();
|
||||||
public bool SynchroDeviceUpgraded = false;
|
public bool SynchroDeviceUpgraded = false;
|
||||||
public int SynchroDeviceLevel = 200;
|
public int SynchroDeviceLevel = 200;
|
||||||
|
public Dictionary<int, RecycleRoomResearchProgress> ResearchProgress = [];
|
||||||
|
|
||||||
public ResetableData ResetableData = new();
|
public ResetableData ResetableData = new();
|
||||||
public WeeklyResetableData WeeklyResetableData = new();
|
public WeeklyResetableData WeeklyResetableData = new();
|
||||||
@@ -482,7 +491,7 @@ namespace EpinelPS.Database
|
|||||||
var matchingCharacterIds = GameData.Instance.CharacterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
|
var matchingCharacterIds = GameData.Instance.CharacterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
|
||||||
|
|
||||||
// Step 3: Check if any of your owned characters have a 'Tid' in the set of matching IDs
|
// Step 3: Check if any of your owned characters have a 'Tid' in the set of matching IDs
|
||||||
return Characters.Where(ownedCharacter => matchingCharacterIds.Contains(ownedCharacter.Tid)).First();
|
return Characters.Where(ownedCharacter => matchingCharacterIds.Contains(ownedCharacter.Tid)).FirstOrDefault();
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
namespace EpinelPS.LobbyServer.Inventory
|
namespace EpinelPS.LobbyServer.Inventory
|
||||||
@@ -5,16 +7,204 @@ namespace EpinelPS.LobbyServer.Inventory
|
|||||||
[PacketPath("/inventory/usepiece")]
|
[PacketPath("/inventory/usepiece")]
|
||||||
public class UsePiece : LobbyMsgHandler
|
public class UsePiece : LobbyMsgHandler
|
||||||
{
|
{
|
||||||
|
private static readonly Random random = new Random();
|
||||||
|
|
||||||
protected override async Task HandleAsync()
|
protected override async Task HandleAsync()
|
||||||
{
|
{
|
||||||
|
// TODO: If this process takes too long, consider to avoid using function chain.
|
||||||
|
/*
|
||||||
|
* Req Contains:
|
||||||
|
* Isn: long value, the item serial number of the piece
|
||||||
|
* Count: int value, how many time
|
||||||
|
*/
|
||||||
var req = await ReadData<ReqUsePiece>();
|
var req = await ReadData<ReqUsePiece>();
|
||||||
var user = GetUser();
|
var user = GetUser();
|
||||||
|
|
||||||
var response = new ResUsePiece();
|
var response = new ResUsePiece();
|
||||||
|
|
||||||
// TODO
|
var piece = user.Items.FirstOrDefault(x => x.Isn == req.Isn) ?? throw new InvalidDataException("cannot find piece with isn " + req.Isn);
|
||||||
|
if (req.Count * 50 > piece.Count) throw new Exception("count mismatch");
|
||||||
|
|
||||||
|
piece.Count -= req.Count * 50;
|
||||||
|
if (piece.Count == 0) user.Items.Remove(piece);
|
||||||
|
|
||||||
|
ItemPieceRecord? pItem = GameData.Instance.PieceItems
|
||||||
|
.FirstOrDefault(x => x.Value.id == piece.ItemType).Value
|
||||||
|
?? throw new Exception("cannot find piece id " + piece.ItemType);
|
||||||
|
|
||||||
|
var probList = GameData.Instance.GachaGradeProb
|
||||||
|
.Where(x => x.Key == pItem.use_id)
|
||||||
|
.SelectMany(grade => GameData.Instance.GachaListProb.Where(list => list.Value.group_id == grade.Value.gacha_list_id))
|
||||||
|
.Select(i => i.Value);
|
||||||
|
var allCharacters = probList.SelectMany(e => GameData.Instance.CharacterTable.Values.Where(c => c.id == e.gacha_id));
|
||||||
|
|
||||||
|
NetRewardData reward = new();
|
||||||
|
var selectedCharacters = Enumerable.Range(1, req.Count)
|
||||||
|
.Select(_ => SelectRandomCharacter(allCharacters, pItem.id));
|
||||||
|
|
||||||
|
int totalBodyLabels = 0;
|
||||||
|
foreach (var character in selectedCharacters)
|
||||||
|
{
|
||||||
|
ItemData? spareItem = user.Items.FirstOrDefault(i => i.ItemType == character.piece_id);
|
||||||
|
|
||||||
|
if (user.GetCharacter(character.id) is Database.Character ownedCharacter)
|
||||||
|
{
|
||||||
|
// If the character already exists, we can increase its piece count
|
||||||
|
int maxLimitBroken = GetValueByRarity(character.original_rare, 0, 2, 11);
|
||||||
|
bool canIncreaseItem = character.original_rare != "R" && ownedCharacter.Grade + (spareItem?.Count ?? 0) < maxLimitBroken;
|
||||||
|
(int newSpareItemCount, int dissoluteCharacterCount) = canIncreaseItem ? (1, 0) : (0, 1);
|
||||||
|
if (canIncreaseItem)
|
||||||
|
{
|
||||||
|
if (spareItem != null)
|
||||||
|
{
|
||||||
|
spareItem.Count = newSpareItemCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spareItem = new()
|
||||||
|
{
|
||||||
|
ItemType = character.piece_id,
|
||||||
|
Csn = 0,
|
||||||
|
Count = newSpareItemCount,
|
||||||
|
Level = 0,
|
||||||
|
Exp = 0,
|
||||||
|
Position = 0,
|
||||||
|
Corp = 0,
|
||||||
|
Isn = user.GenerateUniqueItemId()
|
||||||
|
};
|
||||||
|
user.Items.Add(spareItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
reward.UserItems.Add(NetUtils.UserItemDataToNet(spareItem));
|
||||||
|
reward.Character.Add(GetNetCharacterData(ownedCharacter));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we cannot increase the item, we give body label instead
|
||||||
|
int bodyLabel = GetValueByRarity(character.original_rare, 150, 200, 6000);
|
||||||
|
totalBodyLabels += bodyLabel * dissoluteCharacterCount;
|
||||||
|
reward.Character.Add(GetNetCharacterData(ownedCharacter, bodyLabel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var csn = user.GenerateUniqueCharacterId();
|
||||||
|
reward.UserCharacters.Add(new NetUserCharacterDefaultData
|
||||||
|
{
|
||||||
|
CostumeId = 0,
|
||||||
|
Csn = csn,
|
||||||
|
Grade = 0,
|
||||||
|
Lv = 1,
|
||||||
|
Skill1Lv = 1,
|
||||||
|
Skill2Lv = 1,
|
||||||
|
Tid = character.id,
|
||||||
|
UltiSkillLv = 1
|
||||||
|
});
|
||||||
|
reward.Character.Add(new NetCharacterData
|
||||||
|
{
|
||||||
|
Csn = user.GenerateUniqueCharacterId(),
|
||||||
|
Tid = character.id,
|
||||||
|
});
|
||||||
|
user.Characters.Add(new Database.Character
|
||||||
|
{
|
||||||
|
CostumeId = 0,
|
||||||
|
Csn = csn,
|
||||||
|
Grade = 0,
|
||||||
|
Level = 1,
|
||||||
|
Skill1Lvl = 1,
|
||||||
|
Skill2Lvl = 1,
|
||||||
|
Tid = character.id,
|
||||||
|
UltimateLevel = 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add "New Character" Badge
|
||||||
|
user.AddBadge(BadgeContents.NikkeNew, character.name_code.ToString());
|
||||||
|
user.AddTrigger(TriggerType.ObtainCharacter, 1, character.name_code);
|
||||||
|
if (character.original_rare == "SSR")
|
||||||
|
{
|
||||||
|
user.AddTrigger(TriggerType.ObtainCharacterSSR, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user.AddTrigger(TriggerType.ObtainCharacterNew, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character.original_rare == "SSR" || character.original_rare == "SR")
|
||||||
|
{
|
||||||
|
user.BondInfo.Add(new() { NameCode = character.name_code, Lv = 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.AddTrigger(TriggerType.GachaCharacter, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
reward.Currency.Add(new NetCurrencyData() { Type = (int)CurrencyType.DissolutionPoint, Value = totalBodyLabels });
|
||||||
|
user.AddCurrency(CurrencyType.DissolutionPoint, totalBodyLabels);
|
||||||
|
reward.UserItems.Add(NetUtils.UserItemDataToNet(piece));
|
||||||
|
|
||||||
|
response.Reward = reward;
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
await WriteDataAsync(response);
|
await WriteDataAsync(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CharacterRecord SelectRandomCharacter(IEnumerable<CharacterRecord> characters, int pieceId)
|
||||||
|
{
|
||||||
|
var gradeProb = GetPieceGradeProb(pieceId);
|
||||||
|
var rCharacters = characters.Where(c => c.original_rare == "R");
|
||||||
|
var srCharacters = characters.Where(c => c.original_rare == "SR");
|
||||||
|
var ssrCharacters = characters.Where(c => c.original_rare == "SSR");
|
||||||
|
|
||||||
|
double roll = random.NextDouble() * 100;
|
||||||
|
|
||||||
|
if (0.0 < gradeProb.RProb && roll < gradeProb.RProb && rCharacters.Any())
|
||||||
|
{
|
||||||
|
return rCharacters.ElementAt(random.Next(rCharacters.Count()));
|
||||||
}
|
}
|
||||||
|
else if (0.0 < gradeProb.SRProb && roll < gradeProb.RProb + gradeProb.SRProb && srCharacters.Any())
|
||||||
|
{
|
||||||
|
return srCharacters.ElementAt(random.Next(srCharacters.Count()));
|
||||||
|
}
|
||||||
|
else if (0.0 < gradeProb.SSRProb && roll < gradeProb.RProb + gradeProb.SRProb + gradeProb.SSRProb && ssrCharacters.Any())
|
||||||
|
{
|
||||||
|
return ssrCharacters.ElementAt(random.Next(ssrCharacters.Count()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("No characters available for the given value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: find the file where the grade probability is stored.
|
||||||
|
// TODO: Add Helm Mold and Laplace Mold
|
||||||
|
private PieceGradeProb GetPieceGradeProb(int pieceId) => pieceId switch
|
||||||
|
{
|
||||||
|
5310301 => new PieceGradeProb(0.0, 38.9997, 61.0003), // High quality Mold
|
||||||
|
5310302 or 5310303 or 5310304 or 5310305 => new PieceGradeProb(19.9998, 29.9997, 50.0005), // Manufacturer Mold
|
||||||
|
5310306 or 5310307 or 5310308 or 5310309 => new PieceGradeProb(0.0, 0.0, 100.0), // New Commander Mold or Perfect Mold
|
||||||
|
5330201 or 5359001 => new PieceGradeProb(0.0, 78.9993, 21.0007), // Mid quality Mold
|
||||||
|
_ => throw new Exception("unknown piece id")
|
||||||
|
};
|
||||||
|
|
||||||
|
private int GetValueByRarity(string rarity, int rValue, int srValue, int ssrValue) => rarity switch
|
||||||
|
{
|
||||||
|
"R" => rValue,
|
||||||
|
"SR" => srValue,
|
||||||
|
"SSR" => ssrValue,
|
||||||
|
_ => throw new Exception($"Unknown character rarity: {rarity}")
|
||||||
|
};
|
||||||
|
|
||||||
|
private NetCharacterData GetNetCharacterData(Database.Character character, int bodyLabel = 0)
|
||||||
|
{
|
||||||
|
return new NetCharacterData
|
||||||
|
{
|
||||||
|
Csn = character.Csn,
|
||||||
|
Tid = character.Tid,
|
||||||
|
PieceCount = bodyLabel == 0 ? 1 : 0,
|
||||||
|
CurrencyValue = bodyLabel
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal record PieceGradeProb(double RProb, double SRProb, double SSRProb);
|
||||||
}
|
}
|
||||||
|
|||||||
52
EpinelPS/LobbyServer/Inventory/UseTimeReward.cs
Normal file
52
EpinelPS/LobbyServer/Inventory/UseTimeReward.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Inventory
|
||||||
|
{
|
||||||
|
[PacketPath("/inventory/usetimereward")]
|
||||||
|
public class UseTimeReward : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Req Contains:
|
||||||
|
* Isn: long value
|
||||||
|
* Count: int value, how many items to use
|
||||||
|
*/
|
||||||
|
var req = await ReadData<ReqUseTimeReward>();
|
||||||
|
var user = GetUser();
|
||||||
|
var response = new ResUseTimeReward();
|
||||||
|
|
||||||
|
var timeReward = user.Items.Where(x => x.Isn == req.Isn).FirstOrDefault() ?? throw new InvalidDataException("cannot find time reward with isn " + req.Isn);
|
||||||
|
if (req.Count > timeReward.Count) throw new Exception("count mismatch");
|
||||||
|
|
||||||
|
timeReward.Count -= req.Count;
|
||||||
|
if (timeReward.Count == 0) user.Items.Remove(timeReward);
|
||||||
|
|
||||||
|
ItemConsumeRecord? cItem = GameData.Instance.ConsumableItems
|
||||||
|
.FirstOrDefault(x => x.Value.id == timeReward.ItemType).Value
|
||||||
|
?? throw new Exception("cannot find box id " + timeReward.ItemType);
|
||||||
|
|
||||||
|
// TODO: find out where these numbers come from
|
||||||
|
(CurrencyType itemType, long amount) = cItem.use_id switch
|
||||||
|
{
|
||||||
|
1 => (CurrencyType.Gold, NetUtils.GetOutpostRewardAmount(user, CurrencyType.Gold, TimeSpan.FromSeconds(cItem.use_value).TotalMinutes, false)),
|
||||||
|
2 => (CurrencyType.CharacterExp, NetUtils.GetOutpostRewardAmount(user, CurrencyType.CharacterExp, TimeSpan.FromSeconds(cItem.use_value).TotalMinutes, false)),
|
||||||
|
4 => (CurrencyType.CharacterExp2, NetUtils.GetOutpostRewardAmount(user, CurrencyType.CharacterExp2, TimeSpan.FromSeconds(cItem.use_value).TotalMinutes, false)),
|
||||||
|
_ => throw new Exception("unknown use_id " + cItem.use_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
NetRewardData reward = new();
|
||||||
|
RewardUtils.AddSingleCurrencyObject(user, ref reward, itemType, amount);
|
||||||
|
|
||||||
|
response.Reward = reward;
|
||||||
|
// update client side item count
|
||||||
|
response.Reward.UserItems.Add(NetUtils.UserItemDataToNet(timeReward));
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using EpinelPS.Utils;
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
namespace EpinelPS.LobbyServer.Outpost
|
namespace EpinelPS.LobbyServer.Outpost
|
||||||
{
|
{
|
||||||
@@ -8,10 +9,19 @@ namespace EpinelPS.LobbyServer.Outpost
|
|||||||
protected override async Task HandleAsync()
|
protected override async Task HandleAsync()
|
||||||
{
|
{
|
||||||
var req = await ReadData<ReqGetRecycleRoomData>();
|
var req = await ReadData<ReqGetRecycleRoomData>();
|
||||||
|
var user = GetUser();
|
||||||
// TODO: save these things
|
|
||||||
var response = new ResGetRecycleRoomData();
|
var response = new ResGetRecycleRoomData();
|
||||||
|
|
||||||
|
response.Recycle.AddRange(user.ResearchProgress.Select(progress =>
|
||||||
|
{
|
||||||
|
return new NetUserRecycleRoomData()
|
||||||
|
{
|
||||||
|
Tid = progress.Key,
|
||||||
|
Lv = progress.Value.Level,
|
||||||
|
Exp = progress.Value.Exp
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
await WriteDataAsync(response);
|
await WriteDataAsync(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
108
EpinelPS/LobbyServer/Outpost/Recycle/LevelUpResearch.cs
Normal file
108
EpinelPS/LobbyServer/Outpost/Recycle/LevelUpResearch.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Outpost.Recycle
|
||||||
|
{
|
||||||
|
[PacketPath("/outpost/RecycleRoom/LevelUpResearch")]
|
||||||
|
public class LevelUpResearch : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Req Contains:
|
||||||
|
* Tid: int value, research tid
|
||||||
|
* Items: int value, used items.
|
||||||
|
*/
|
||||||
|
var req = await ReadData<ReqRecycleLevelUpResearch>();
|
||||||
|
var user = GetUser();
|
||||||
|
var response = new ResRecycleLevelUpResearch();
|
||||||
|
|
||||||
|
user.ResearchProgress.TryGetValue(req.Tid, out var progress);
|
||||||
|
|
||||||
|
// Check progress is null, null means research is not unlocked.
|
||||||
|
if (progress != null)
|
||||||
|
{
|
||||||
|
AddProgressToResearch(response, user, progress, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProgressToResearch(ResRecycleLevelUpResearch response, User user, RecycleRoomResearchProgress progress, ReqRecycleLevelUpResearch req)
|
||||||
|
{
|
||||||
|
GameData.Instance.RecycleResearchStats.TryGetValue(req.Tid, out var statRecord);
|
||||||
|
if (statRecord is null)
|
||||||
|
return;
|
||||||
|
var levelRecord = GameData.Instance.RecycleResearchLevels.Values.Where(e => e.recycle_type == statRecord.recycle_type)
|
||||||
|
.FirstOrDefault(e => e.recycle_level == progress.Level);
|
||||||
|
if (levelRecord is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (statRecord.recycle_type == "Personal") // main research
|
||||||
|
{
|
||||||
|
var usedItem = user.Items.FirstOrDefault(e => e.ItemType == levelRecord.item_id); // item_id equals level-up item's tid.
|
||||||
|
if (usedItem is null || usedItem.Count < levelRecord.item_value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
usedItem.Count -= levelRecord.item_value;
|
||||||
|
response.Items.Add(NetUtils.UserItemDataToNet(usedItem));
|
||||||
|
|
||||||
|
progress.Level += 1;
|
||||||
|
progress.Hp = statRecord.hp * progress.Level;
|
||||||
|
response.Recycle = new()
|
||||||
|
{
|
||||||
|
Tid = req.Tid,
|
||||||
|
Lv = progress.Level,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (statRecord.recycle_type == "Class" || statRecord.recycle_type == "Corporation") // class research or corporation research
|
||||||
|
{
|
||||||
|
var netItem = req.Items.Single();
|
||||||
|
var usedItem = user.Items.FirstOrDefault(e => e.ItemType == netItem.Tid);
|
||||||
|
if (usedItem is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
usedItem.Count -= netItem.Count;
|
||||||
|
response.Items.Add(NetUtils.UserItemDataToNet(usedItem));
|
||||||
|
(int newLevel, int newExp) = CalcCorpAndClassLevelUp(statRecord.recycle_type, netItem.Count, progress.Level, progress.Exp);
|
||||||
|
progress.Level = newLevel;
|
||||||
|
progress.Exp = newExp;
|
||||||
|
response.Recycle = new()
|
||||||
|
{
|
||||||
|
Tid = req.Tid,
|
||||||
|
Lv = newLevel,
|
||||||
|
Exp = newExp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"unknown recycle type {statRecord.recycle_type}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First: level, Second: exp
|
||||||
|
private (int, int) CalcCorpAndClassLevelUp(string researchType, int itemCount, int startLevel = 1, int startExp = 0)
|
||||||
|
{
|
||||||
|
// levelRecord.exp is required exp to level up.
|
||||||
|
var levelRecords = GameData.Instance.RecycleResearchLevels.Values.Where(e => e.recycle_type == researchType && e.recycle_level > startLevel);
|
||||||
|
|
||||||
|
foreach (var record in levelRecords)
|
||||||
|
{
|
||||||
|
if (itemCount < record.exp)
|
||||||
|
{
|
||||||
|
startExp += itemCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemCount -= record.exp - startExp;
|
||||||
|
startLevel += 1;
|
||||||
|
startExp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (startLevel, startExp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace EpinelPS.LobbyServer.Outpost.Recycle
|
namespace EpinelPS.LobbyServer.Outpost.Recycle
|
||||||
{
|
{
|
||||||
@@ -13,9 +10,32 @@ namespace EpinelPS.LobbyServer.Outpost.Recycle
|
|||||||
protected override async Task HandleAsync()
|
protected override async Task HandleAsync()
|
||||||
{
|
{
|
||||||
var req = await ReadData<ReqRecycleRunResearch>();
|
var req = await ReadData<ReqRecycleRunResearch>();
|
||||||
|
var user = GetUser();
|
||||||
var response = new ResRecycleRunResearch();
|
var response = new ResRecycleRunResearch();
|
||||||
// TODO
|
|
||||||
|
user.ResearchProgress.TryGetValue(req.Tid, out var progress);
|
||||||
|
|
||||||
|
// Check progress is null, non-null means research is already unlocked.
|
||||||
|
if (progress is null)
|
||||||
|
{
|
||||||
|
var researchRecord = GameData.Instance.RecycleResearchStats.Values.FirstOrDefault(e => e.id == req.Tid)
|
||||||
|
?? throw new Exception("not found research record with tid " + req.Tid);
|
||||||
|
progress = new()
|
||||||
|
{
|
||||||
|
Attack = researchRecord.attack,
|
||||||
|
Defense = researchRecord.defense,
|
||||||
|
Hp = researchRecord.hp,
|
||||||
|
};
|
||||||
|
user.ResearchProgress.Add(req.Tid, progress);
|
||||||
|
}
|
||||||
|
response.Recycle = new()
|
||||||
|
{
|
||||||
|
Tid = req.Tid,
|
||||||
|
Lv = progress.Level,
|
||||||
|
Exp = progress.Exp,
|
||||||
|
};
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
await WriteDataAsync(response);
|
await WriteDataAsync(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -393,6 +393,9 @@ namespace EpinelPS.Utils
|
|||||||
Logging.WriteLine("TODO: reward_value_max", LogType.Warning);
|
Logging.WriteLine("TODO: reward_value_max", LogType.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (winningRecord.reward_type == "Currency")
|
||||||
|
RewardUtils.AddSingleCurrencyObject(user, ref ret, (CurrencyType)winningRecord.reward_id, winningRecord.reward_value_min);
|
||||||
|
else
|
||||||
RewardUtils.AddSingleObject(user, ref ret, winningRecord.reward_id, winningRecord.reward_type, winningRecord.reward_value_min);
|
RewardUtils.AddSingleObject(user, ref ret, winningRecord.reward_id, winningRecord.reward_type, winningRecord.reward_value_min);
|
||||||
}
|
}
|
||||||
JsonDb.Save();
|
JsonDb.Save();
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ namespace EpinelPS.Utils
|
|||||||
foreach (var item in rewardData.rewards)
|
foreach (var item in rewardData.rewards)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(item.reward_type))
|
if (!string.IsNullOrEmpty(item.reward_type))
|
||||||
|
{
|
||||||
|
if (item.reward_type == "Currency")
|
||||||
|
{
|
||||||
|
AddSingleCurrencyObject(user, ref ret, (CurrencyType)item.reward_id, item.reward_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (item.reward_percent != 1000000)
|
if (item.reward_percent != 1000000)
|
||||||
{
|
{
|
||||||
@@ -81,9 +87,29 @@ namespace EpinelPS.Utils
|
|||||||
AddSingleObject(user, ref ret, item.reward_id, item.reward_type, item.reward_value);
|
AddSingleObject(user, ref ret, item.reward_id, item.reward_type, item.reward_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
public static void AddSingleCurrencyObject(User user, ref NetRewardData ret, CurrencyType currencyType, long rewardCount)
|
||||||
|
{
|
||||||
|
bool found = user.Currency.Any(pair => pair.Key == currencyType);
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
user.Currency[currencyType] += rewardCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user.Currency.Add(currencyType, rewardCount);
|
||||||
|
}
|
||||||
|
ret.Currency.Add(new NetCurrencyData()
|
||||||
|
{
|
||||||
|
FinalValue = found ? user.GetCurrencyVal(currencyType) : rewardCount,
|
||||||
|
Value = rewardCount,
|
||||||
|
Type = (int)currencyType
|
||||||
|
});
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a single item to users inventory, and also adds it to ret parameter.
|
/// Adds a single item to users inventory, and also adds it to ret parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -98,37 +124,6 @@ namespace EpinelPS.Utils
|
|||||||
if (rewardId != 0 || !string.IsNullOrEmpty(rewardType))
|
if (rewardId != 0 || !string.IsNullOrEmpty(rewardType))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(rewardType) || string.IsNullOrWhiteSpace(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" || rewardType.StartsWith("Equipment_"))
|
else if (rewardType == "Item" || rewardType.StartsWith("Equipment_"))
|
||||||
{
|
{
|
||||||
// Check if user already has said item. If it is level 1, increase item count.
|
// Check if user already has said item. If it is level 1, increase item count.
|
||||||
|
|||||||
Reference in New Issue
Block a user