Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Maple2.File.Ingest/Mapper/MapEntityMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ private IEnumerable<MapEntity> ParseMap(string xblock, IEnumerable<IMapEntity> e
};
case IMS2TriggerMesh mesh:
return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) {
Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible),
Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible, mesh.MinimapInVisible),
};
case IMS2TriggerPortal _:
throw new InvalidOperationException("IMS2TriggerPortal should be parsed as IPortal.");
Expand Down
32 changes: 32 additions & 0 deletions Maple2.File.Ingest/Mapper/TriggerMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Xml;
using M2dXmlGenerator;
using Maple2.File.Ingest.Utils;
using Maple2.File.IO;
using Maple2.File.IO.Crypto.Common;
Expand Down Expand Up @@ -60,6 +61,37 @@ protected override IEnumerable<TriggerMetadata> Map() {
};

public static string NormalizeTriggerXmlNames(XmlDocument xml) {
// check for state nodes with feature attributes and remove disabled ones
List<XmlNode> nodesToRemove = new List<XmlNode>();
foreach (XmlNode node in xml.SelectNodes("//state")!) {
XmlAttribute? featureAttr = node.Attributes?["feature"];
if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) {
nodesToRemove.Add(node);
}
}

// Check for action nodes with feature attributes and remove disabled ones
foreach (XmlNode node in xml.SelectNodes("//action")!) {
XmlAttribute? featureAttr = node.Attributes?["feature"];
if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) {
nodesToRemove.Add(node);
}
}

// Check for condition nodes with feature attributes and remove disabled ones
foreach (XmlNode node in xml.SelectNodes("//condition")!) {
XmlAttribute? featureAttr = node.Attributes?["feature"];
if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) {
nodesToRemove.Add(node);
}
}

// Remove disabled feature nodes
foreach (XmlNode node in nodesToRemove) {
node.ParentNode?.RemoveChild(node);
}

// Continue with the existing normalization logic
foreach (XmlNode node in xml.SelectNodes("//state")!) {
XmlAttribute? attr = node.Attributes?["name"];
Debug.Assert(attr?.Value != null, "Unable to find name param");
Expand Down
10 changes: 8 additions & 2 deletions Maple2.Model/Game/TriggerObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ public virtual void WriteTo(IByteWriter writer) {

public class TriggerObjectSound(Ms2TriggerSound metadata) : TriggerObject<Ms2TriggerSound>(metadata);

public class TriggerObjectMesh(Ms2TriggerMesh metadata) : TriggerObject<Ms2TriggerMesh>(metadata) {
public class TriggerObjectMesh : TriggerObject<Ms2TriggerMesh> {
public bool MinimapVisible { get; init; }
public int Fade { get; set; }
public float Scale { get; set; } = 1f;
public float Scale { get; set; }

public TriggerObjectMesh(Ms2TriggerMesh metadata) : base(metadata) {
MinimapVisible = metadata.MinimapInvisible;
this.Scale = metadata.Scale;
this.Visible = metadata.Visible;
}
Comment thread
Zintixx marked this conversation as resolved.

public override void WriteTo(IByteWriter writer) {
base.WriteTo(writer);
Expand Down
3 changes: 2 additions & 1 deletion Maple2.Model/Metadata/MapEntity/Trigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public record Ms2TriggerLadder(
public record Ms2TriggerMesh(
float Scale,
int TriggerId,
bool Visible)
bool Visible,
bool MinimapInvisible)
: Trigger(TriggerId, Visible);

public record Ms2TriggerPortal(
Expand Down
7 changes: 6 additions & 1 deletion Maple2.Server.Game/Manager/MasteryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public int this[MasteryType type] {
};
set {
short startLevel = GetLevel(type);
int startValue = this[type];
switch (type) {
case MasteryType.Fishing:
Mastery.Fishing = Math.Clamp(value, Mastery.Fishing, Constant.FishingMasteryMax);
Expand Down Expand Up @@ -79,14 +80,18 @@ public int this[MasteryType type] {
}

session.Send(MasteryPacket.UpdateMastery(type, session.Mastery[type]));
if (startLevel < GetLevel(type)) {
int currentLevel = GetLevel(type);
if (startLevel < currentLevel || startValue == 0) {
session.ConditionUpdate(ConditionType.mastery_grade, codeLong: (int) type);
}
if (startLevel > currentLevel) {
session.ConditionUpdate(ConditionType.set_mastery_grade, codeLong: (int) type);
if (type == MasteryType.Music) {
session.ConditionUpdate(ConditionType.music_play_grade);
}
}
}

}

public short GetLevel(MasteryType type) {
Expand Down
1 change: 0 additions & 1 deletion Maple2.Server.Game/Model/Field/Entity/FieldInstrument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ public class FieldInstrument(FieldManager field, int objectId, InstrumentMetadat
public long StartTick { get; set; }
public bool Ensemble { get; set; }
public Item? Score { get; set; }

}
23 changes: 23 additions & 0 deletions Maple2.Server.Game/Model/Field/Widget/GuideWidget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Concurrent;
using System.Reflection;
using Maple2.Server.Game.Manager.Field;

namespace Maple2.Server.Game.Model.Widget;

public class GuideWidget : Widget {

public GuideWidget(FieldManager field) : base(field) {
Conditions = new ConcurrentDictionary<string, int>();
}

public override bool Check(string name, string arg) {
return Conditions.GetValueOrDefault(name) == 1;
}

public override void Action(string function, int numericArg, string stringArg) {
MethodInfo? method = GetType().GetMethod(function, BindingFlags.NonPublic | BindingFlags.Instance);
if (method != null) {
method.Invoke(this, [stringArg, numericArg]);
}
}
}
1 change: 1 addition & 0 deletions Maple2.Server.Game/Model/Field/Widget/IWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public interface IWidget {
/// <param name="numericArg">A numeric argument for the action.</param>
/// <param name="stringArg">A string argument for the action.</param>
void Action(string function, int numericArg, string stringArg);
bool Check(string name, string arg);
}
33 changes: 30 additions & 3 deletions Maple2.Server.Game/Model/Field/Widget/OxQuizWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Maple2.Model.Metadata;
using Maple2.Server.Game.Manager.Field;
using Maple2.Server.Game.Packets;
using Serilog;

namespace Maple2.Server.Game.Model.Widget;

Expand All @@ -17,10 +18,36 @@ public OxQuizWidget(FieldManager field) : base(field) {
questions = new ConcurrentDictionary<int, OxQuizTable.Entry>();
}

public override bool Check(string name, string arg) {
return Conditions.GetValueOrDefault(name) == 1;
}

public override void Action(string function, int numericArg, string stringArg) {
MethodInfo? method = GetType().GetMethod(function, BindingFlags.NonPublic | BindingFlags.Instance);
if (method != null) {
method.Invoke(this, [stringArg, numericArg]);
switch (function) {
case "DevMode":
DevMode(stringArg, numericArg);
break;
case "PickQuiz":
PickQuiz(stringArg, numericArg);
break;
case "ShowQuiz":
ShowQuiz(stringArg, numericArg);
break;
case "ShowAnswer":
ShowAnswer(stringArg, numericArg);
break;
case "PreJudge":
PreJudge(stringArg, numericArg);
break;
case "Judge":
Judge(stringArg, numericArg);
break;
case "Winner":
Winner(stringArg, numericArg);
break;
default:
Log.Logger.Warning("Unknown function called on OxQuizWidget: {Function}", function);
break;
}
}

Expand Down
3 changes: 3 additions & 0 deletions Maple2.Server.Game/Model/Field/Widget/Widget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public class Widget : IWidget {
public ConcurrentDictionary<string, int> Conditions { get; set; }
public virtual void Action(string function, int numericArg, string stringArg) {
}
public virtual bool Check(string name, string arg) {
return false;
}

public Widget(FieldManager field) {
Field = field;
Expand Down
24 changes: 19 additions & 5 deletions Maple2.Server.Game/Model/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,15 @@ public Stats(NpcMetadataStat npcStats) {
/// Clears and sets static stats.
/// </summary>
public void SetStaticStats(IReadOnlyDictionary<BasicAttribute, long> statsDictionary) {
basicValues.Clear();
specialValues.Clear();
ClearStats();
foreach (BasicAttribute attribute in statsDictionary.Keys) {
this[attribute].AddBase(statsDictionary[attribute]);
}
// Does not add PhysicalAtk or MagicalAtk due to it not using jobCode
}

public void Reset(IReadOnlyDictionary<BasicAttribute, long> metadata, JobCode jobCode) {
basicValues.Clear();
specialValues.Clear();
ClearStats();

foreach (BasicAttribute attribute in metadata.Keys) {
if (attribute is BasicAttribute.PhysicalAtk or BasicAttribute.MagicalAtk) {
Expand All @@ -73,7 +71,7 @@ public void Reset(IReadOnlyDictionary<BasicAttribute, long> metadata, JobCode jo

[Obsolete("Use Reset(UserStatMetadata, JobCode) instead.")]
public void Reset(JobCode jobCode, short level) {
basicValues.Clear();
ClearStats();

this[BasicAttribute.Strength].AddBase(BaseStat.Strength(jobCode, level));
this[BasicAttribute.Dexterity].AddBase(BaseStat.Dexterity(jobCode, level));
Expand Down Expand Up @@ -149,6 +147,22 @@ public Stat this[SpecialAttribute attribute] {
}
set => specialValues[attribute] = value;
}

/// <summary>
/// Safely clear stats.
/// </summary>
private void ClearStats() {
foreach (Stat stat in basicValues.Values) {
stat.AddBase(-stat.Base);
stat.AddTotal(-stat.Total);
stat.AddRate(-stat.Rate);
}
foreach (Stat stat in specialValues.Values) {
stat.AddBase(-stat.Base);
stat.AddTotal(-stat.Total);
stat.AddRate(-stat.Rate);
}
}
}

public sealed class Stat {
Expand Down
7 changes: 4 additions & 3 deletions Maple2.Server.Game/PacketHandlers/InstrumentHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,12 @@ private void HandleStopScore(GameSession session) {
return;
}

int masteryValue = session.Instrument.Score?.Metadata.Music?.MasteryValue ?? 1;
int masteryValueMax = session.Instrument.Score?.Metadata.Music?.MasteryValueMax ?? 1;

// TODO: Prestige exp
long totalTickTime = Environment.TickCount64 - session.Instrument.StartTick;
if (session.ItemMetadata.TryGet(session.Instrument.Value.EquipId, out ItemMetadata? metadata) && metadata.Music != null) {
session.Mastery[MasteryType.Music] += (int) Math.Min(totalTickTime * metadata.Music.MasteryValue / 1000, metadata.Music.MasteryValueMax);
}
session.Mastery[MasteryType.Music] += (int) Math.Min(totalTickTime * masteryValue / 1000, masteryValueMax);

short masteryLevel = session.Mastery.GetLevel(MasteryType.Music);
ExpType expType = masteryLevel switch {
Expand Down
4 changes: 2 additions & 2 deletions Maple2.Server.Game/Trigger/Helpers/ITriggerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,13 @@ public interface ITriggerContext {
public bool ScoreBoardScore(int score, OperatorType operatorType);
public bool ShadowExpeditionPoints(int score);
public bool TimeExpired(string timerId);
public bool UserDetected(int[] boxIds, int jobCode);
public bool UserDetected(int[] boxIds, int jobCode, bool negate);
public bool UserValue(string key, int value, bool negate);
public bool WaitAndResetTick(int waitTick);
public bool WaitSecondsUserValue(string key, string desc);
public bool WaitTick(int waitTick);
public bool WeddingEntryInField(string entryType, bool isInField);
public bool WeddingHallState(string state, bool success);
public bool WeddingMutualAgreeResult(string agreeType);
public bool WidgetValue(string type, string widgetName, int condition, bool negate, string desc);
public bool WidgetValue(string type, string widgetName, string widgeArg, bool negate, string desc);
}
20 changes: 15 additions & 5 deletions Maple2.Server.Game/Trigger/Helpers/TriggerFunctionMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,25 +254,35 @@ public static class TriggerFunctionMapping {
{ "score_board_score", (ctx, attrs) => ctx.ScoreBoardScore(ParseInt(attrs?["score"]?.Value), ParseOperatorType(attrs?["compare_op"]?.Value)) },
{ "shadow_expedition_points", (ctx, attrs) => ctx.ShadowExpeditionPoints(ParseInt(attrs?["score"]?.Value)) },
{ "time_expired", (ctx, attrs) => ctx.TimeExpired(attrs?["timer_id"]?.Value ?? string.Empty) },
{ "user_detected", (ctx, attrs) => ctx.UserDetected(ParseIntArray(attrs?["box_ids"]?.Value), ParseInt(attrs?["job_code"]?.Value)) },
{ "user_detected", (ctx, attrs) => ctx.UserDetected(ParseIntArray(attrs?["box_ids"]?.Value), ParseInt(attrs?["job_code"]?.Value), ParseBool(attrs?["negate"]?.Value)) },
{ "user_value", (ctx, attrs) => ctx.UserValue(attrs?["key"]?.Value ?? string.Empty, ParseInt(attrs?["value"]?.Value), ParseBool(attrs?["negate"]?.Value)) },
{ "wait_and_reset_tick", (ctx, attrs) => ctx.WaitAndResetTick(ParseInt(attrs?["wait_tick"]?.Value)) },
{ "wait_seconds_user_value", (ctx, attrs) => ctx.WaitSecondsUserValue(attrs?["key"]?.Value ?? string.Empty, attrs?["desc"]?.Value ?? string.Empty) },
{ "wait_tick", (ctx, attrs) => ctx.WaitTick(ParseInt(attrs?["wait_tick"]?.Value)) },
{ "wedding_entry_in_field", (ctx, attrs) => ctx.WeddingEntryInField(attrs?["entry_type"]?.Value ?? string.Empty, ParseBool(attrs?["is_in_field"]?.Value)) },
{ "wedding_hall_state", (ctx, attrs) => ctx.WeddingHallState(attrs?["hallState"]?.Value ?? string.Empty, ParseBool(attrs?["success"]?.Value)) },
{ "wedding_mutual_agree_result", (ctx, attrs) => ctx.WeddingMutualAgreeResult(attrs?["agree_type"]?.Value ?? string.Empty) },
{ "widget_value", (ctx, attrs) => ctx.WidgetValue(attrs?["type"]?.Value ?? string.Empty, attrs?["widget_name"]?.Value ?? string.Empty, ParseInt(attrs?["condition"]?.Value), ParseBool(attrs?["negate"]?.Value), attrs?["desc"]?.Value ?? string.Empty) },
{ "widget_value", (ctx, attrs) => ctx.WidgetValue(attrs?["type"]?.Value ?? string.Empty, attrs?["widget_name"]?.Value ?? string.Empty, attrs?["condition"]?.Value, ParseBool(attrs?["negate"]?.Value), attrs?["desc"]?.Value ?? string.Empty) },
};

private static Weather ParseWeather(string? value) {
if (string.IsNullOrEmpty(value)) return Weather.Clear;
return (Weather) Enum.Parse(typeof(Weather), value, true);
return Enum.TryParse(value, true, out Weather weather) ? weather : Weather.Clear;
}

private static BannerType ParseBannerType(string? value) {
if (string.IsNullOrEmpty(value)) return BannerType.Lose;
return (BannerType) Enum.Parse(typeof(BannerType), value, true);
if (string.IsNullOrEmpty(value)) return BannerType.Text;
return value switch {
// "0"
"1" => BannerType.Text,
// "2"
"3" => BannerType.Winner,
"4" => BannerType.Lose,
"5" => BannerType.GameOver,
"6" => BannerType.Bonus,
"7" => BannerType.Success,
_ => BannerType.Text,
};
}

private static Locale ParseLocale(string? value) {
Expand Down
5 changes: 2 additions & 3 deletions Maple2.Server.Game/Trigger/TriggerContext.Interface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,13 @@ public void WidgetAction(string type, string func, string widgetArg, string desc
}

#region Conditions
public bool WidgetValue(string type, string widgetName, int condition, bool negate, string desc = "") {
public bool WidgetValue(string type, string widgetName, string widgetArg, bool negate, string desc = "") {
DebugLog("[WidgetValue] type:{Type}, widgetName:{Name}, desc:{Desc}", type, widgetName, desc);
if (!Field.Widgets.TryGetValue(type, out Widget? widget)) {
return negate;
}


bool result = widget.Conditions.GetValueOrDefault(widgetName) == condition;
bool result = widget.Check(widgetName, widgetArg);
if (negate) {
return !result;
}
Expand Down
10 changes: 7 additions & 3 deletions Maple2.Server.Game/Trigger/TriggerContext.Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public bool QuestUserDetected(int[] boxIds, int[] questIds, int[] questStates, i
DebugLog("[QuestUserDetected] boxIds:{BoxIds}, questIds:{QuestIds}, questStates:{QuestStates}, jobCode:{JobCode}",
string.Join(", ", boxIds), string.Join(", ", questIds), string.Join(", ", questStates), (JobCode) jobCode);


foreach (FieldPlayer player in PlayersInBox(boxIds)) {
foreach (int questId in questIds) {
if (!player.Session.Quest.TryGetQuest(questId, out Quest? quest)) {
Expand Down Expand Up @@ -263,18 +264,21 @@ public bool QuestUserDetected(int[] boxIds, int[] questIds, int[] questStates, i
return negate;
}

public bool UserDetected(int[] boxIds, int jobCode) {
public bool UserDetected(int[] boxIds, int jobCode, bool negate) {
DebugLog("[UserDetected] boxIds:{BoxIds}, jobCode:{JobCode}", string.Join(", ", boxIds), (JobCode) jobCode);
IEnumerable<TriggerBox> boxes = boxIds
.Select(boxId => Objects.Boxes.GetValueOrDefault(boxId))
.Where(box => box != null)!;

bool result;
if (jobCode != 0) {
return Field.Players.Values
result = Field.Players.Values
.Any(player => player.Value.Character.Job.Code() == (JobCode) jobCode && boxes.Any(box => box.Contains(player.Position)));
} else {
result = Field.Players.Values.Any(player => boxes.Any(box => box.Contains(player.Position)));
}

return Field.Players.Values.Any(player => boxes.Any(box => box.Contains(player.Position)));
return negate ? !result : result;
}

public bool WaitSecondsUserValue(string key, string desc) {
Expand Down
Loading