Skip to content
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

Introduce RuneLevelBonus #2519

Merged
merged 7 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
132 changes: 132 additions & 0 deletions .Lib9c.Tests/Action/RuneEnhancementTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Lib9c.Tests.Action
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Xunit;

public class RuneEnhancementTest
Expand Down Expand Up @@ -623,5 +624,136 @@ public void Execute_FailedLoadStateException()
BlockIndex = blockIndex,
}));
}

[Theory]
// Rune upgrade
[InlineData(new[] { 1 }, 9, false)]
[InlineData(new[] { 9 }, 1, false)]
[InlineData(new[] { 7 }, 3, false)]
[InlineData(new[] { 4, 4 }, 2, false)]
[InlineData(new[] { 4, 5 }, 1, false)]
// Crete new rune
[InlineData(new int[] { }, 1, true, 100)]
[InlineData(new int[] { }, 10, true)]
[InlineData(new[] { 1 }, 9, true)]
[InlineData(new[] { 9 }, 1, true)]
[InlineData(new[] { 7 }, 3, true)]
[InlineData(new[] { 4, 4 }, 2, true)]
[InlineData(new[] { 4, 5 }, 1, true)]
public void RuneBonus(int[] prevRuneLevels, int tryCount, bool createNewRune, int expectedRuneLevelBonus = 1000)
{
// Data
const int testRuneId = 30001;
var prevRuneIds = new[] { 10001, 10002, 10003 };
const int initialNcg = 10_000;
const int initialCrystal = 1_000_000;
const int initialRune = 1_000;

// Set states
var agentAddress = new PrivateKey().Address;
var avatarAddress = new PrivateKey().Address;
var sheets = TableSheetsImporter.ImportSheets();
var tableSheets = new TableSheets(sheets);
var blockIndex = tableSheets.WorldBossListSheet.Values
.OrderBy(x => x.StartedBlockIndex)
.First()
.StartedBlockIndex;

var goldCurrencyState = new GoldCurrencyState(_goldCurrency);
var rankingMapAddress = avatarAddress.Derive("ranking_map");
var agentState = new AgentState(agentAddress);
var avatarState = new AvatarState(
avatarAddress,
agentAddress,
0,
tableSheets.GetAvatarSheets(),
new GameConfigState(),
rankingMapAddress
);
agentState.avatarAddresses.Add(0, avatarAddress);
var context = new ActionContext();
var state = new World(MockUtil.MockModernWorldState)
.SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize())
.SetAgentState(agentAddress, agentState)
.SetAvatarState(avatarAddress, avatarState);

foreach (var (key, value) in sheets)
{
state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize());
}

// Set prev. runes
var allRuneState = new AllRuneState();
for (var i = 0; i < prevRuneLevels.Length; i++)
{
var runeId = prevRuneIds[i];
if (!createNewRune && i == 0)
{
runeId = testRuneId;
}

allRuneState.AddRuneState(runeId, prevRuneLevels[i]);
}

state = state.SetRuneState(avatarAddress, allRuneState);
var runeListSheet = tableSheets.RuneListSheet;
var runeLevelBonusSheet = tableSheets.RuneLevelBonusSheet;
var prevRuneLevelBonus = prevRuneLevels.Length == 0
? 0
: runeLevelBonusSheet.Values.First(row => row.RuneLevel == 1).Bonus;
Assert.Equal(
prevRuneLevelBonus,
RuneHelper.CalculateRuneLevelBonus(
allRuneState,
runeListSheet,
runeLevelBonusSheet
)
);

// RuneEnhancement
var ncgCurrency = state.GetGoldCurrency();
var crystalCurrency = CrystalCalculator.CRYSTAL;
var runeTicker = tableSheets.RuneSheet.Values.First(r => r.Id == testRuneId).Ticker;
var runeCurrency = Currency.Legacy(runeTicker, 0, minters: null);
state = state.MintAsset(context, agentAddress, ncgCurrency * initialNcg);
state = state.MintAsset(context, agentAddress, crystalCurrency * initialCrystal);
state = state.MintAsset(context, avatarAddress, runeCurrency * initialRune);

var action = new RuneEnhancement
{
AvatarAddress = avatarAddress,
RuneId = testRuneId,
TryCount = tryCount,
};
var ctx = new ActionContext
{
BlockIndex = blockIndex,
PreviousState = state,
RandomSeed = 0,
Signer = agentAddress,
};

// Check bonus
var nextState = action.Execute(ctx);
var nextAllRuneState = nextState.GetRuneState(avatarAddress);
var expectedBonusLevel = 0;
foreach (var rune in nextAllRuneState.Runes.Values)
{
var runeRow = runeListSheet.Values.FirstOrDefault(row => row.Id == rune.RuneId);
if (runeRow is not null)
{
expectedBonusLevel += runeRow.BonusCoef * rune.Level;
}
}

Assert.Equal(
expectedRuneLevelBonus,
RuneHelper.CalculateRuneLevelBonus(
nextAllRuneState,
runeListSheet,
runeLevelBonusSheet
)
);
}
}
}
38 changes: 38 additions & 0 deletions .Lib9c.Tests/TableData/Rune/RuneLevelBonusSheetTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Lib9c.Tests.TableData.Rune
{
using System;
using Nekoyume.TableData.Rune;
using Xunit;

public class RuneLevelBonusSheetTest
{
public RuneLevelBonusSheetTest()
{
if (!TableSheetsImporter.TryGetCsv(nameof(RuneLevelBonusSheet), out var csv))
{
throw new Exception($"Sheet not found: {nameof(RuneLevelBonusSheet)}");
}

var runeLevelBonusSheet = new RuneLevelBonusSheet();
runeLevelBonusSheet.Set(csv);
}

[Fact]
public void SheetTest()
{
const string csvContent =
@"id,rune_level,bonus
1,1,100
";
var sheet = new RuneLevelBonusSheet();
sheet.Set(csvContent);

Assert.Single(sheet);
Assert.NotNull(sheet.First);
var row = sheet.First;
Assert.Equal(1, row.Id);
Assert.Equal(1, row.RuneLevel);
Assert.Equal(100, row.Bonus);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
namespace Lib9c.Tests.TableData
namespace Lib9c.Tests.TableData.Rune
{
using System;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Xunit;

public class RuneListSheetTest
{
private RuneListSheet _runeListSheet;

public RuneListSheetTest()
{
if (!TableSheetsImporter.TryGetCsv(nameof(RuneListSheet), out var csv))
{
throw new Exception($"Not found sheet: {nameof(RuneListSheet)}");
}

_runeListSheet = new RuneListSheet();
_runeListSheet.Set(csv);
var runeListSheet = new RuneListSheet();
runeListSheet.Set(csv);
}

[Fact]
public void SetToSheet()
{
const string content =
@"id,grade,rune_type,required_level,use_place
250010001,1,1,1,7
@"id,grade,rune_type,required_level,use_place,bonus_coef
250010001,1,1,1,7,100
";

var sheet = new RuneListSheet();
Expand All @@ -37,6 +35,7 @@ public void SetToSheet()
Assert.Equal(1, sheet.First.RuneType);
Assert.Equal(1, sheet.First.RequiredLevel);
Assert.Equal(7, sheet.First.UsePlace);
Assert.Equal(100, sheet.First.BonusCoef);
}
}
}
3 changes: 3 additions & 0 deletions .Lib9c.Tests/TableSheets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Lib9c.Tests
using Nekoyume.TableData.Garages;
using Nekoyume.TableData.GrandFinale;
using Nekoyume.TableData.Pet;
using Nekoyume.TableData.Rune;
using Nekoyume.TableData.Stake;
using Nekoyume.TableData.Summon;

Expand Down Expand Up @@ -228,6 +229,8 @@ public StakeActionPointCoefficientSheet StakeActionPointCoefficientSheet

public RuneOptionSheet RuneOptionSheet { get; private set; }

public RuneLevelBonusSheet RuneLevelBonusSheet { get; private set; }

public GrandFinaleScheduleSheet GrandFinaleScheduleSheet { get; private set; }

public GrandFinaleParticipantsSheet GrandFinaleParticipantsSheet { get; private set; }
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/BattleArena.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Serilog;
using static Lib9c.SerializeKeys;

Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/EventDungeonBattle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Event;
using Nekoyume.TableData.Rune;
using Serilog;
using static Lib9c.SerializeKeys;

Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/HackAndSlash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Crystal;
using Nekoyume.TableData.Rune;
using Serilog;
using static Lib9c.SerializeKeys;
using Skill = Nekoyume.Model.Skill.Skill;
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/HackAndSlashSweep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Serilog;
using static Lib9c.SerializeKeys;

Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/JoinArena.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Serilog;

namespace Nekoyume.Action
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/JoinArena3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Serilog;

namespace Nekoyume.Action
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Action/Raid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;
using Serilog;
using static Lib9c.SerializeKeys;

Expand Down
4 changes: 2 additions & 2 deletions Lib9c/Action/RuneEnhancement.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Bencodex.Types;
using Lib9c.Abstractions;
using Libplanet.Action;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume.Exceptions;
using Nekoyume.Extensions;
using Nekoyume.Helper;
using Nekoyume.Model.Rune;
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;

namespace Nekoyume.Action
{
Expand Down Expand Up @@ -75,6 +74,7 @@ public override IWorld Execute(IActionContext context)
typeof(RuneSheet),
typeof(RuneListSheet),
typeof(RuneCostSheet),
typeof(RuneLevelBonusSheet),
});

// Validation
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Addresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static class Addresses
public static readonly Address DailyReward = new Address("0000000000000000000000000000000000000020");
public static readonly Address ActionPoint = new Address("0000000000000000000000000000000000000021");
public static readonly Address RuneState = new Address("0000000000000000000000000000000000000022");
public static readonly Address RuneLevelBonus = new Address("0000000000000000000000000000000000000023");

public static Address GetSheetAddress<T>() where T : ISheet => GetSheetAddress(typeof(T).Name);

Expand Down
18 changes: 17 additions & 1 deletion Lib9c/Helper/RuneHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using Libplanet.Types.Assets;
using Nekoyume.Action;
using Nekoyume.Battle;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;

namespace Nekoyume.Helper
{
Expand Down Expand Up @@ -119,10 +121,24 @@ public static bool TryEnhancement(
return true;
}

public static FungibleAssetValue CalculateStakeReward(FungibleAssetValue stakeAmount, int rate)
public static FungibleAssetValue CalculateStakeReward(FungibleAssetValue stakeAmount,
int rate)
{
var (quantity, _) = stakeAmount.DivRem(stakeAmount.Currency * rate);
return StakeRune * quantity;
}

public static int CalculateRuneLevelBonus(AllRuneState allRuneState,
RuneListSheet runeListSheet, RuneLevelBonusSheet runeLevelBonusSheet)
{
var bonusLevel = (from rune in allRuneState.Runes.Values
let runeRow = runeListSheet.Values.FirstOrDefault(row => row.Id == rune.RuneId)
where runeRow is not null
select runeRow.BonusCoef * rune.Level).Sum();

var bonusRow = runeLevelBonusSheet.Values.OrderByDescending(row => row.RuneLevel)
.FirstOrDefault(row => row.RuneLevel <= bonusLevel);
return bonusRow?.Bonus ?? 0;
}
}
}
2 changes: 1 addition & 1 deletion Lib9c/Model/State/AllRuneState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Nekoyume.Model.State
/// </summary>
public class AllRuneState : IState
{
private Dictionary<int, RuneState> Runes { get; }
public Dictionary<int, RuneState> Runes { get; }

public AllRuneState()
{
Expand Down
1 change: 1 addition & 0 deletions Lib9c/Model/State/RuneSlotState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Nekoyume.Model.EnumType;
using Nekoyume.Model.Rune;
using Nekoyume.TableData;
using Nekoyume.TableData.Rune;

namespace Nekoyume.Model.State
{
Expand Down
Loading
Loading