Skip to content

Commit

Permalink
feat: random drops (#2486)
Browse files Browse the repository at this point in the history
* feat: random drops

* silent rename quantity

* remove extra spaces

* added notifiable drop logic to the npc editor

* change selected index on add

* added notifiable drop logic to the resource editor

---------

Co-authored-by: Robbie Lodico <pandacoder@pm.me>
  • Loading branch information
WeylonSantana and lodicolo authored Jan 21, 2025
1 parent f391d19 commit 241e419
Show file tree
Hide file tree
Showing 11 changed files with 2,980 additions and 2,883 deletions.
24 changes: 22 additions & 2 deletions Framework/Intersect.Framework.Core/GameObjects/Drop.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
using Newtonsoft.Json;

namespace Intersect.GameObjects;

public partial class Drop
{
private int _maxQuantity;

public double Chance { get; set; }

public Guid ItemId { get; set; }

public int Quantity { get; set; }
}
[JsonProperty]
[Obsolete(message: $"Use {nameof(MinQuantity)} instead, the Quantity property will be removed in 0.9-beta", error: true)]
private int Quantity
{
/* Setter only [JsonProperty] annotated private property to "silently" rename Quantity to MinQuantity */
set => MinQuantity = value;
}

/* By renaming Quantity to MinQuantity the "automatic" range given an original "Quantity" value of 3 will be 3 to 3, instead of 1 to 3 or 0 to 3 */
public int MinQuantity { get; set; } = 1;

public int MaxQuantity
{
/* Special getter ensures that MaxQuantity can never be less than MinQuantity, and it doesn't need a non-zero default value as a result */
get => Math.Max(_maxQuantity, MinQuantity);
set => _maxQuantity = value;
}
}
3,601 changes: 1,766 additions & 1,835 deletions Intersect.Editor/Forms/Editors/frmNpc.Designer.cs

Large diffs are not rendered by default.

145 changes: 78 additions & 67 deletions Intersect.Editor/Forms/Editors/frmNpc.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ComponentModel;
using System.Drawing.Imaging;
using DarkUI.Forms;
using Intersect.Editor.Content;
Expand All @@ -13,17 +14,18 @@

namespace Intersect.Editor.Forms.Editors;


public partial class FrmNpc : EditorForm
{

private List<NpcBase> mChanged = new List<NpcBase>();
private List<NpcBase> mChanged = [];

private string mCopiedItem;

private NpcBase mEditorItem;

private List<string> mKnownFolders = new List<string>();
private List<string> mKnownFolders = [];

private BindingList<NotifiableDrop> _dropList = [];

public FrmNpc()
{
Expand Down Expand Up @@ -112,6 +114,9 @@ private void frmNpc_Load(object sender, EventArgs e)
cmbScalingStat.Items.Add(Globals.GetStatName(x));
}

lstDrops.DataSource = _dropList;
lstDrops.DisplayMember = nameof(NotifiableDrop.DisplayName);

nudStr.Maximum = Options.Instance.Player.MaxStat;
nudMag.Maximum = Options.Instance.Player.MaxStat;
nudDef.Maximum = Options.Instance.Player.MaxStat;
Expand Down Expand Up @@ -211,7 +216,8 @@ private void InitLocalization()

grpDrops.Text = Strings.NpcEditor.drops;
lblDropItem.Text = Strings.NpcEditor.dropitem;
lblDropAmount.Text = Strings.NpcEditor.dropamount;
lblDropMaxAmount.Text = Strings.NpcEditor.DropMaxAmount;
lblDropMinAmount.Text = Strings.NpcEditor.DropMinAmount;
lblDropChance.Text = Strings.NpcEditor.dropchance;
btnDropAdd.Text = Strings.NpcEditor.dropadd;
btnDropRemove.Text = Strings.NpcEditor.dropremove;
Expand Down Expand Up @@ -426,40 +432,18 @@ private void DrawNpcSprite()
picNpc.BackgroundImage = picSpriteBmp;
}

private void UpdateDropValues(bool keepIndex = false)
private void UpdateDropValues()
{
var index = lstDrops.SelectedIndex;
lstDrops.Items.Clear();

var drops = mEditorItem.Drops.ToArray();
foreach (var drop in drops)
_dropList.Clear();
foreach (var drop in mEditorItem.Drops)
{
if (ItemBase.Get(drop.ItemId) == null)
_dropList.Add(new NotifiableDrop
{
mEditorItem.Drops.Remove(drop);
}
}

for (var i = 0; i < mEditorItem.Drops.Count; i++)
{
if (mEditorItem.Drops[i].ItemId != Guid.Empty)
{
lstDrops.Items.Add(
Strings.NpcEditor.dropdisplay.ToString(
ItemBase.GetName(mEditorItem.Drops[i].ItemId), mEditorItem.Drops[i].Quantity,
mEditorItem.Drops[i].Chance
)
);
}
else
{
lstDrops.Items.Add(TextUtils.None);
}
}

if (keepIndex && index < lstDrops.Items.Count)
{
lstDrops.SelectedIndex = index;
ItemId = drop.ItemId,
MinQuantity = drop.MinQuantity,
MaxQuantity = drop.MaxQuantity,
Chance = drop.Chance
});
}
}

Expand Down Expand Up @@ -724,71 +708,98 @@ private void nudExp_ValueChanged(object sender, EventArgs e)
mEditorItem.Experience = (int)nudExp.Value;
}

private void lstDrops_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstDrops.SelectedIndex > -1)
{
cmbDropItem.SelectedIndex = ItemBase.ListIndex(mEditorItem.Drops[lstDrops.SelectedIndex].ItemId) + 1;
nudDropMaxAmount.Value = mEditorItem.Drops[lstDrops.SelectedIndex].MaxQuantity;
nudDropMinAmount.Value = mEditorItem.Drops[lstDrops.SelectedIndex].MinQuantity;
nudDropChance.Value = (decimal)mEditorItem.Drops[lstDrops.SelectedIndex].Chance;
}
}

private void cmbDropItem_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstDrops.SelectedIndex > -1 && lstDrops.SelectedIndex < mEditorItem.Drops.Count)
int index = lstDrops.SelectedIndex;
if (index < 0 || index > lstDrops.Items.Count)
{
mEditorItem.Drops[lstDrops.SelectedIndex].ItemId = ItemBase.IdFromList(cmbDropItem.SelectedIndex - 1);
return;
}

UpdateDropValues(true);
mEditorItem.Drops[index].ItemId = ItemBase.IdFromList(cmbDropItem.SelectedIndex - 1);
_dropList[index].ItemId = mEditorItem.Drops[index].ItemId;
}

private void nudDropAmount_ValueChanged(object sender, EventArgs e)
private void nudDropMaxAmount_ValueChanged(object sender, EventArgs e)
{
// This should never be below 1. We shouldn't accept giving 0 items!
nudDropAmount.Value = Math.Max(1, nudDropAmount.Value);

if (lstDrops.SelectedIndex < lstDrops.Items.Count)
int index = lstDrops.SelectedIndex;
if (index < 0 || index > lstDrops.Items.Count)
{
return;
}

mEditorItem.Drops[(int)lstDrops.SelectedIndex].Quantity = (int)nudDropAmount.Value;
UpdateDropValues(true);
mEditorItem.Drops[index].MaxQuantity = (int)nudDropMaxAmount.Value;
_dropList[index].MaxQuantity = mEditorItem.Drops[index].MaxQuantity;
}

private void lstDrops_SelectedIndexChanged(object sender, EventArgs e)
private void nudDropMinAmount_ValueChanged(object sender, EventArgs e)
{
if (lstDrops.SelectedIndex > -1)
int index = lstDrops.SelectedIndex;
if (index < 0 || index > lstDrops.Items.Count)
{
cmbDropItem.SelectedIndex = ItemBase.ListIndex(mEditorItem.Drops[lstDrops.SelectedIndex].ItemId) + 1;
nudDropAmount.Value = mEditorItem.Drops[lstDrops.SelectedIndex].Quantity;
nudDropChance.Value = (decimal)mEditorItem.Drops[lstDrops.SelectedIndex].Chance;
return;
}

mEditorItem.Drops[index].MinQuantity = (int)nudDropMinAmount.Value;
_dropList[index].MinQuantity = mEditorItem.Drops[index].MinQuantity;
}

private void btnDropAdd_Click(object sender, EventArgs e)
private void nudDropChance_ValueChanged(object sender, EventArgs e)
{
mEditorItem.Drops.Add(new Drop());
mEditorItem.Drops[mEditorItem.Drops.Count - 1].ItemId = ItemBase.IdFromList(cmbDropItem.SelectedIndex - 1);
mEditorItem.Drops[mEditorItem.Drops.Count - 1].Quantity = (int)nudDropAmount.Value;
mEditorItem.Drops[mEditorItem.Drops.Count - 1].Chance = (double)nudDropChance.Value;
int index = lstDrops.SelectedIndex;
if (index < 0 || index > lstDrops.Items.Count)
{
return;
}

UpdateDropValues();
mEditorItem.Drops[index].Chance = (double)nudDropChance.Value;
_dropList[index].Chance = mEditorItem.Drops[index].Chance;
}

private void btnDropRemove_Click(object sender, EventArgs e)
private void btnDropAdd_Click(object sender, EventArgs e)
{
if (lstDrops.SelectedIndex > -1)
var drop = new Drop()
{
var i = lstDrops.SelectedIndex;
lstDrops.Items.RemoveAt(i);
mEditorItem.Drops.RemoveAt(i);
}
ItemId = ItemBase.IdFromList(cmbDropItem.SelectedIndex - 1),
MaxQuantity = (int)nudDropMaxAmount.Value,
MinQuantity = (int)nudDropMinAmount.Value,
Chance = (double)nudDropChance.Value
};

UpdateDropValues(true);
mEditorItem.Drops.Add(drop);

_dropList.Add(new NotifiableDrop
{
ItemId = drop.ItemId,
MinQuantity = drop.MinQuantity,
MaxQuantity = drop.MaxQuantity,
Chance = drop.Chance
});

lstDrops.SelectedIndex = _dropList.Count - 1;
}

private void nudDropChance_ValueChanged(object sender, EventArgs e)
private void btnDropRemove_Click(object sender, EventArgs e)
{
if (lstDrops.SelectedIndex < lstDrops.Items.Count)
if (lstDrops.SelectedIndex < 0)
{
return;
}

mEditorItem.Drops[(int)lstDrops.SelectedIndex].Chance = (double)nudDropChance.Value;
UpdateDropValues(true);
var index = lstDrops.SelectedIndex;
mEditorItem.Drops.RemoveAt(index);
_dropList.RemoveAt(index);
}

private void chkIndividualLoot_CheckedChanged(object sender, EventArgs e)
Expand Down
52 changes: 26 additions & 26 deletions Intersect.Editor/Forms/Editors/frmNpc.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -179,6 +179,6 @@
</value>
</data>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>65</value>
<value>54</value>
</metadata>
</root>
Loading

0 comments on commit 241e419

Please sign in to comment.