Skip to content

Commit

Permalink
Cleanup SkillManager
Browse files Browse the repository at this point in the history
Fix IsLearnableSkill
  • Loading branch information
WoozChucky committed May 31, 2024
1 parent 3df255f commit 9986f76
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 100 deletions.
27 changes: 27 additions & 0 deletions src/Core/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace QuantumCore.Extensions;

public static class EnumExtensions
{
public static bool TryParseEnum<TEnum>(string value, out TEnum result) where TEnum : struct, Enum
{
// Check if the value is a single word
if (!value.Contains('_'))
{
// Capitalize the first letter and lowercase the rest to match enum naming convention
var formattedSingleWord = char.ToUpper(value[0]) + value[1..].ToLower();
return Enum.TryParse(formattedSingleWord, true, out result);
}

// If the value contains underscores, split and format it
var parts = value.ToLower().Split('_');
for (var i = 0; i < parts.Length; i++)
{
parts[i] = char.ToUpper(parts[i][0]) + parts[i][1..];
}

var formattedValue = string.Join("", parts);

// Try to parse the formatted string as the enum type
return Enum.TryParse(formattedValue, true, out result);
}
}
9 changes: 9 additions & 0 deletions src/CorePluginAPI/EPlayerClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace QuantumCore.API;

public enum EPlayerClass
{
Warrior = 0,
Ninja = 1,
Sura = 2,
Shaman = 3,
}
67 changes: 66 additions & 1 deletion src/Executables/Game/ParserUtils.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using EnumsNET;
using QuantumCore.API;
using QuantumCore.API.Game.World;
using QuantumCore.API.Core.Models;
using QuantumCore.API.Game.Skills;
using QuantumCore.Core.Utils;
using QuantumCore.Extensions;
using QuantumCore.Game.Drops;
using QuantumCore.Game.Services;
using QuantumCore.Game.World;
Expand Down Expand Up @@ -71,6 +74,68 @@ public static async Task<ImmutableArray<CommonDropEntry>> GetCommonDropsAsync(Te

return list.ToImmutableArray();
}

public static async Task<ImmutableArray<SkillData>> GetSkillsAsync(string path, CancellationToken token = default)
{
var list = new List<SkillData>();

await foreach(var line in File.ReadLinesAsync(path, Encoding.GetEncoding("EUC-KR"), token))
{
// parse line
var split = line.Split('\t');

var data = new SkillData
{
Id = uint.Parse(split[0]),
Name = split[1],
Type = short.Parse(split[2]),
LevelStep = short.Parse(split[3]),
MaxLevel = short.Parse(split[4]),
LevelLimit = short.Parse(split[5]),
PointOn = split[6],
PointPoly = split[7],
SPCostPoly = split[8],
DurationPoly = split[9],
DurationSPCostPoly = split[10],
CooldownPoly = split[11],
MasterBonusPoly = split[12],
AttackGradePoly = split[13],
Flags = ExtractSkillFlags(split[14]),
AffectFlags = ExtractAffectFlags(split[15]),
PointOn2 = split[16],
PointPoly2 = split[17],
DurationPoly2 = split[18],
AffectFlags2 = ExtractAffectFlags(split[19]),
PrerequisiteSkillVnum = int.Parse(split[20]),
PrerequisiteSkillLevel = int.Parse(split[21]),
SkillType = Enum.TryParse<ESkillType>(split[22], true, out var result) ? result : ESkillType.Normal,
MaxHit = short.Parse(split[23]),
SplashAroundDamageAdjustPoly = split[24],
TargetRange = int.Parse(split[25]),
SplashRange = uint.Parse(split[26])
};

list.Add(data);
}

return [..list];
}

private static List<ESkillFlag> ExtractSkillFlags(string value)
{
var values = string.IsNullOrWhiteSpace(value)
? []
: value.Split(',').Select(flag => EnumExtensions.TryParseEnum<ESkillFlag>(flag, out var result) ? result : ESkillFlag.None).ToList();
values.RemoveAll(v => v == ESkillFlag.None);
return values;
}

private static List<ESkillAffectFlag> ExtractAffectFlags(string value)
{
return string.IsNullOrWhiteSpace(value)
? [ESkillAffectFlag.Ymir]
: value.Split(',').Select(flag => EnumExtensions.TryParseEnum<ESkillAffectFlag>(flag, out var result) ? result : ESkillAffectFlag.Ymir).ToList();
}

private static void ParseCommonDropAndAdd(ReadOnlySpan<char> line, ICollection<CommonDropEntry> list)
{
Expand Down
94 changes: 1 addition & 93 deletions src/Executables/Game/SkillManager.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System.Collections.Immutable;
using System.Text;
using Microsoft.Extensions.Logging;
using QuantumCore.API;
using QuantumCore.API.Core.Models;
using QuantumCore.API.Game.Skills;

namespace QuantumCore.Game;

Expand Down Expand Up @@ -36,105 +34,15 @@ public SkillManager(ILogger<SkillManager> logger)
}
return null;
}

public static bool TryParseEnum<TEnum>(string value, out TEnum result) where TEnum : struct, Enum
{
// Check if the value is a single word
if (!value.Contains("_"))
{
// Capitalize the first letter and lowercase the rest to match enum naming convention
var formattedSingleWord = char.ToUpper(value[0]) + value.Substring(1).ToLower();
return Enum.TryParse(formattedSingleWord, true, out result);
}

// If the value contains underscores, split and format it
var parts = value.ToLower().Split('_');
for (int i = 0; i < parts.Length; i++)
{
parts[i] = char.ToUpper(parts[i][0]) + parts[i].Substring(1);
}

var formattedValue = string.Join("", parts);

// Try to parse the formatted string as the enum type
return Enum.TryParse(formattedValue, true, out result);
}

public async Task LoadAsync(CancellationToken token = default)
{
if (_skills.Length > 0)
{
return;
}

var fileEncoding = Encoding.GetEncoding("EUC-KR");

await foreach(var line in File.ReadLinesAsync("data/936skilltable.txt", fileEncoding, token))
{
// parse line
var split = line.Split('\t');

List<ESkillFlag> ExtractSkillFlags(string value)
{
var values = string.IsNullOrWhiteSpace(value)
? []
: value.Split(',').Select(flag => TryParseEnum<ESkillFlag>(flag, out var result) ? result : ESkillFlag.None).ToList();
values.RemoveAll(v => v == ESkillFlag.None);
return values;
}

List<ESkillAffectFlag> ExtractAffectFlags(string value)
{
return string.IsNullOrWhiteSpace(value)
? [ESkillAffectFlag.Ymir]
: value.Split(',').Select(flag => TryParseEnum<ESkillAffectFlag>(flag, out var result) ? result : ESkillAffectFlag.Ymir).ToList();
}

try
{
var flags = ExtractSkillFlags(split[14]);
var affectFlags = ExtractAffectFlags(split[15]);
var affectFlags2 = ExtractAffectFlags(split[19]);

var data = new SkillData
{
Id = uint.Parse(split[0]),
Name = split[1],
Type = short.Parse(split[2]),
LevelStep = short.Parse(split[3]),
MaxLevel = short.Parse(split[4]),
LevelLimit = short.Parse(split[5]),
PointOn = split[6],
PointPoly = split[7],
SPCostPoly = split[8],
DurationPoly = split[9],
DurationSPCostPoly = split[10],
CooldownPoly = split[11],
MasterBonusPoly = split[12],
AttackGradePoly = split[13],
Flags = flags,
AffectFlags = affectFlags,
PointOn2 = split[16],
PointPoly2 = split[17],
DurationPoly2 = split[18],
AffectFlags2 = affectFlags2,
PrerequisiteSkillVnum = int.Parse(split[20]),
PrerequisiteSkillLevel = int.Parse(split[21]),
SkillType = Enum.TryParse<ESkillType>(split[22], true, out var result) ? result : ESkillType.Normal,
MaxHit = short.Parse(split[23]),
SplashAroundDamageAdjustPoly = split[24],
TargetRange = int.Parse(split[25]),
SplashRange = uint.Parse(split[26])
};

_skills = _skills.Add(data);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to parse skill line: {Line}", line);
throw;
}
}
_skills = await ParserUtils.GetSkillsAsync("data/936skilltable.txt", token);

_logger.LogInformation("Loaded {Count} skills", _skills.Length);
}
Expand Down
20 changes: 14 additions & 6 deletions src/Executables/Game/Skills/PlayerSkills.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,19 +335,27 @@ public void SkillUp(uint skillId, ESkillLevelMethod method = ESkillLevelMethod.P

private bool IsLearnableSkill(uint skillId)
{
//todo: read skill proto information nad get specified skill information

if (GetSkillLevel(skillId) >= SkillMaxLevel)
var proto = _skillManager.GetSkill(skillId);
if (proto == null)
{
return false;
}

if (_player.Player.SkillGroup == 0)
if (GetSkillLevel(skillId) >= SkillMaxLevel) return false;

if (proto.Type == 0)
{
return false;
return GetSkillLevel(skillId) < proto.MaxLevel;
}

return true; // todo: temporary
if (proto.Type == 5)
{
return skillId != (int) ESkillIndexes.HorseWildAttackRange || _player.Player.PlayerClass == (int) EPlayerClass.Ninja;
}

if (_player.Player.SkillGroup == 0) return false;

return proto.Type - 1 == _player.Player.PlayerClass;
}

private int GetSkillLevel(uint skillId)
Expand Down

0 comments on commit 9986f76

Please sign in to comment.