October 22, 2019, 12:04:32 PM

Vita-Nex: Core



Author Topic: Customizing the Skill Codex  (Read 14668 times)

0 Members and 1 Guest are viewing this topic.

Offline Greed

  • User
  • *
  • Posts: 21
  • Likes: 2
    • View Profile
Customizing the Skill Codex
« on: February 10, 2015, 08:30:58 AM »
Hi Vorspire,

I'm interested in changing the skill codex, as great as it is, to only offer pre-AOS skills when used. If it isn't too much trouble would you mind pointing me in the direction of how to do that? Thanks so much for any reply. Oh and also I was curious how to make it not delete on use/not require to be in my backpack have an infinite number of uses

Edit - Figured out the strikethrough'd part on my own

Best,

Greed

P.S. Sorry for so many threads as of late. Not trying to spam it's just that you've done such a marvelous job creating and developing your VNC I can't help but enjoy learning more about it.
« Last Edit: February 10, 2015, 10:38:46 AM by Greed »

Offline Vorspire

  • Administrator
  • *****
  • Posts: 136
  • Likes: 34
  • Founder & Developer
    • View Profile
    • Vita-Nex: Core
Re: Customizing the Skill Codex
« Reply #1 on: February 10, 2015, 12:05:28 PM »
Your best option would be to create a child class that inherits the SkillCodex, then override the functions you want to change.

The SkillCodex has a way to filter skills from the list, but here is an example of what I use for some variations to it;

Code: [Select]
#region Header
//   Vorspire    _,-'/-'/  SkillCodexBase.cs
//   .      __,-; ,'( '/
//    \.    `-.__`-._`:_,-._       _ , . ``
//     `:-._,------' ` _,`--` -: `_ , ` ,' :
//        `---..__,,--'  (C) 2015  ` -'. -'
//        #  Vita-Nex [http://core.vita-nex.com]  #
//  {o)xxx|===============-   #   -===============|xxx(o}
//        #        The MIT License (MIT)          #
#endregion

#region References
using Server.Mobiles;

using VitaNex.Items;
using VitaNex.SuperGumps;
#endregion

namespace Server.Items
{
public abstract class SkillCodexBase : SkillCodex
{
private static readonly SkillName[] DefaultIgnoredSkills = {SkillName.Mysticism, SkillName.Imbuing};

public SkillCodexBase(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode, SkillCodexFlags flags)
: base(count, value, deleteWhenEmpty, mode, flags)
{
IgnoredSkills.AddRange(DefaultIgnoredSkills);
}

public SkillCodexBase(Serial serial)
: base(serial)
{ }

public override bool ValidateSkill(Mobile user, Skill skill, bool message)
{
if (user is PlayerMobile)
{
var pm = (PlayerMobile)user;

if (skill.SkillName == SkillName.Spellweaving)
{
if (!pm.Spellweaving)
{
if (message)
{
pm.SendMessage(
SuperGump.DefaultErrorHue,
"You must complete the Patience or Discipline quest chains to unlock {0}.",
skill.Name);
}

return false;
}
}
}

return base.ValidateSkill(user, skill, message);
}

public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);

var version = 0;

writer.Write(version);

switch (version)
{
case 0:
{ }
break;
}
}

public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);

var version = reader.ReadInt();

switch (version)
{
case 0:
{ }
break;
}
}
}

public abstract class SkillCodexFixedBase : SkillCodexBase
{
public SkillCodexFixedBase(int count, double value)
: base(count, value, true, SkillCodexMode.Fixed, SkillCodexFlags.Base)
{ }

public SkillCodexFixedBase(Serial serial)
: base(serial)
{ }

public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);

var version = 0;

writer.Write(version);

switch (version)
{
case 0:
{ }
break;
}
}

public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);

var version = reader.ReadInt();

switch (version)
{
case 0:
{ }
break;
}
}
}
}

I use these new child classes as parents to other child derivatives - the "FixedBase" version will set the selected skills to the given value outright, rather than using an offset (like plus or minus the value).

For example, if I wanted to give them 6 skill at 100.0, this would be appropriate:

Code: [Select]
	public sealed class SkillCodexFixedBase6At100 : SkillCodexFixedBase
{
[Constructable]
public SkillCodexFixedBase6At100()
: base(6, 100.0)
{ }

public SkillCodexFixedBase6At100(Serial serial)
: base(serial)
{ }

public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);

writer.SetVersion(0);
}

public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);

reader.GetVersion();
}
}

To answer your question about ignoring skills, you can do something like this:

Code: [Select]
IgnoredSkills.AddRange( SkillName.Alchemy, SkillName.Macing, SkillName.Imbuing );

IgnoredSkills is a dynamic list available to each SkillCodex instance, any skills listed will not be shown on the gump.
« Last Edit: February 10, 2015, 12:12:03 PM by Vorspire »

Offline Greed

  • User
  • *
  • Posts: 21
  • Likes: 2
    • View Profile
Re: Customizing the Skill Codex
« Reply #2 on: February 10, 2015, 12:11:09 PM »
Awesome thanks so much! I'll check it out later today :D

Offline Vorspire

  • Administrator
  • *****
  • Posts: 136
  • Likes: 34
  • Founder & Developer
    • View Profile
    • Vita-Nex: Core
Re: Customizing the Skill Codex
« Reply #3 on: February 10, 2015, 12:14:01 PM »
No problem :)
It's good that you're asking so many questions because it's a good substitute for others who can't find the docs (until they become available) :P

Offline Greed

  • User
  • *
  • Posts: 21
  • Likes: 2
    • View Profile
Re: Customizing the Skill Codex
« Reply #4 on: February 12, 2015, 12:30:27 AM »
Okay, and by the way thank you again, now I tried those excellent changes and of course they compiled perfectly however when I edited the part about addrange with regard to ignoring I get this message:

http://imgur.com/Pkbnw7D

Here is the code I'm running:

Code: [Select]

#region Header
//   Vorspire    _,-'/-'/  SkillCodex.cs
//   .      __,-; ,'( '/
//    \.    `-.__`-._`:_,-._       _ , . ``
//     `:-._,------' ` _,`--` -: `_ , ` ,' :
//        `---..__,,--'  (C) 2014  ` -'. -'
//        #  Vita-Nex [http://core.vita-nex.com]  #
//  {o)xxx|===============-   #   -===============|xxx(o}
//        #        The MIT License (MIT)          #
#endregion

#region References
using System;
using System.Collections.Generic;
using System.Drawing;

using Server;
using Server.Mobiles;

using VitaNex.Items;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion

namespace Server.Items
{
    public abstract class SkillCodexBase : SkillCodex
    {
        private static readonly SkillName[] DefaultIgnoredSkills = { SkillName.Mysticism, SkillName.Imbuing };

        public SkillCodexBase(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode, SkillCodexFlags flags)
            : base(count, value, deleteWhenEmpty, mode, flags)
        {
            IgnoredSkills.AddRange(SkillName.Alchemy, SkillName.Macing, SkillName.Imbuing);
        }

        public SkillCodexBase(Serial serial)
            : base(serial)
        { }

        public override bool ValidateSkill(Mobile user, Skill skill, bool message)
        {
            if (user is PlayerMobile)
            {
                var pm = (PlayerMobile)user;

                if (skill.SkillName == SkillName.Spellweaving)
                {
                    if (!pm.Spellweaving)
                    {
                        if (message)
                        {
                            pm.SendMessage(
                                SuperGump.DefaultErrorHue,
                                "You must complete the Patience or Discipline quest chains to unlock {0}.",
                                skill.Name);
                        }

                        return false;
                    }
                }
            }

            return base.ValidateSkill(user, skill, message);
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            var version = 0;

            writer.Write(version);

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            var version = reader.ReadInt();

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }
    }

    public abstract class SkillCodexFixedBase : SkillCodexBase
    {
        public SkillCodexFixedBase(int count, double value)
            : base(count, value, true, SkillCodexMode.Fixed, SkillCodexFlags.Base)
        { }

        public SkillCodexFixedBase(Serial serial)
            : base(serial)
        { }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            var version = 0;

            writer.Write(version);

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            var version = reader.ReadInt();

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }
    }
}

namespace VitaNex.Items
{
public enum SkillCodexFlags : byte
{
Base = 0x00,
Cap = 0x01,
Both = 0x02
}

public enum SkillCodexMode : byte
{
Fixed = 0x00,
Increase = 0x01,
Decrease = 0x02
}

public class SkillCodex : Item
{
private SuperGump SelectionGump { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public SkillCodexMode Mode { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public SkillCodexFlags Flags { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public int Count { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public double Value { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public int ValueFixed { get { return (int)(Value * 10); } set { Value = value / 10; } }

[CommandProperty(AccessLevel.GameMaster)]
public bool DeleteWhenEmpty { get; set; }

public List<SkillName> IgnoredSkills { get; protected set; }
public List<SkillName> SelectedSkills { get; protected set; }

[Constructable]
public SkillCodex()
: this(1)
{ }

[Constructable]
public SkillCodex(int count)
: this(count, 100.0)
{ }

[Constructable]
public SkillCodex(int count, double value)
: this(count, value, true)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty)
: this(count, value, deleteWhenEmpty, SkillCodexMode.Fixed)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode)
: this(count, value, deleteWhenEmpty, mode, SkillCodexFlags.Base)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode, SkillCodexFlags flags)
: base(8793)
{
SelectedSkills = new List<SkillName>();
IgnoredSkills = new List<SkillName>();

Mode = mode;
Flags = flags;
Count = count;
Value = value;
DeleteWhenEmpty = deleteWhenEmpty;

Name = "Codex of Wisdom";
LootType = LootType.Blessed;
Stackable = false;
}

public SkillCodex(Serial serial)
: base(serial)
{ }

public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);

string html = String.Empty, flags = String.Empty;

html += String.Format("<basefont color=#{0:X6}>Use: ", Color.Cyan.ToArgb());

switch (Flags)
{
case SkillCodexFlags.Base:
flags = "value";
break;
case SkillCodexFlags.Cap:
flags = "cap";
break;
case SkillCodexFlags.Both:
flags = "value and cap";
break;
}

switch (Mode)
{
case SkillCodexMode.Increase:
{
html += String.Format(
"Increase {0} skill{1} {2} by {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
case SkillCodexMode.Decrease:
{
html += String.Format(
"Decrease {0} skill{1} {2} by {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
case SkillCodexMode.Fixed:
{
html += String.Format("Set {0} skill{1} {2} to {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
}

list.Add(html);
}

public override void OnDoubleClick(Mobile from)
{
if (from == null || from.Deleted || !Validate(from, true))
{
return;
}

SelectSkills(from);
InvalidateProperties();
}

public virtual bool ValidateSkill(Mobile user, Skill skill, bool message)
{
if (user == null || user.Deleted || skill == null)
{
return false;
}

switch (Mode)
{
case SkillCodexMode.Increase:
{
if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (!CanReduceSkills(user))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already know everything this codex can offer, reduce some skills to make room for more knowledge.");
}

return false;
}
}

if (skill.IsCapped() || skill.WillCap(Value, false))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}

if (!skill.IsLocked(SkillLock.Up))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
case SkillCodexMode.Decrease:
{
if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (user.SkillsTotal - ValueFixed < 0)
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "You already forgot everything this codex can offer.");
}

return false;
}

if (skill.IsZero() || skill.WillZero(Value, false))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already forgot everything this codex can offer about {0}, any further and you'll forget how to breath!",
skill.Name);
}

return false;
}

if (!skill.IsLocked(SkillLock.Down))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
case SkillCodexMode.Fixed:
{
if (Flags == SkillCodexFlags.Cap)
{
if (skill.CapFixedPoint == ValueFixed)
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}
}

if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (ValueFixed < skill.BaseFixedPoint)
{
if (user.SkillsTotal - (skill.BaseFixedPoint - ValueFixed) < 0)
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already forgot everything this codex can offer, any further and you'll forget how to breath!");
}

return false;
}
}
else if (ValueFixed > skill.BaseFixedPoint)
{
if (user.SkillsTotal + (ValueFixed - skill.BaseFixedPoint) > user.SkillsCap)
{
if (!CanReduceSkills(user))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already know everything this codex can offer, reduce some skills to make room for more knowledge.");
}

return false;
}
}
}
else
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}

if (skill.IsLocked(SkillLock.Locked))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
}

return true;
}

public virtual bool Validate(Mobile user, bool message)
{
if (user == null || user.Deleted || !user.CanSee(this))
{
return false;
}

if (!IsChildOf(user.Backpack))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "This codex must be in your backpack to read it.");
}

return false;
}

if (Count <= 0)
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "This codex does not contain any skills to learn.");
}

return false;
}

return true;
}

public virtual void SelectSkills(Mobile user)
{
if (user == null || user.Deleted || !Validate(user, true) || !(user is PlayerMobile))
{
return;
}

if (SelectionGump != null)
{
SelectionGump.Close(true);
}

SelectionGump = new SkillSelectionGump(
user as PlayerMobile,
null,
Count,
null,
null,
skills =>
{
if (!ApplySkills(user, skills))
{
SelectSkills(user);
}
},
IgnoredSkills.ToArray()).Send();
}

public virtual bool ApplySkills(Mobile user, SkillName[] skills)
{
if (user == null || user.Deleted || skills == null || skills.Length == 0 || !Validate(user, true))
{
return false;
}

foreach (SkillName sn in skills)
{
if (user.Deleted || !Validate(user, true))
{
return false;
}

Skill skill = user.Skills[sn];

if (!ValidateSkill(user, skill, true))
{
continue;
}

bool charge = false;

if (Flags == SkillCodexFlags.Cap || Flags == SkillCodexFlags.Both)
{
switch (Mode)
{
case SkillCodexMode.Increase:
{
skill.IncreaseCap(Value);
}
break;
case SkillCodexMode.Decrease:
{
skill.DecreaseCap(Value);
}
break;
case SkillCodexMode.Fixed:
{
skill.SetCap(Value);
}
break;
}

if (Flags != SkillCodexFlags.Both)
{
EndApply(skill);
return true;
}

charge = true;
}

if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
charge = false;

switch (Mode)
{
case SkillCodexMode.Increase:
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (TryReduceSkills(user) && skill.IncreaseBase(Value))
{
charge = true;
}
}
else if (skill.IncreaseBase(Value))
{
charge = true;
}
}
break;
case SkillCodexMode.Decrease:
{
if (skill.DecreaseBase(Value))
{
charge = true;
}
}
break;
case SkillCodexMode.Fixed:
{
if (ValueFixed > skill.BaseFixedPoint)
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (TryReduceSkills(user) && skill.SetBase(Value))
{
charge = true;
}
}
else if (skill.SetBase(Value))
{
charge = true;
}
}
else if (ValueFixed < skill.BaseFixedPoint && skill.SetBase(Value))
{
charge = true;
}
}
break;
}
}

if (charge)
{
EndApply(skill);
}
}

return true;
}

protected void EndApply(Skill skill)
{
skill.Normalize();
UseCharges();

if (Count <= 0)
{
if (DeleteWhenEmpty)
{
Delete();
}
}
}

protected bool CanReduceSkills(Mobile user)
{
int target = (user.SkillsTotal + ValueFixed) - user.SkillsCap;
int canReduceBy = 0;

foreach (Skill s in user.Skills)
{
if (s.IsLocked(SkillLock.Down))
{
if (canReduceBy + s.BaseFixedPoint > target)
{
canReduceBy += (target - canReduceBy);
}
else
{
canReduceBy += s.BaseFixedPoint;
}
}

if (canReduceBy >= target)
{
return true;
}
}

return false;
}

protected bool TryReduceSkills(Mobile user)
{
if (!CanReduceSkills(user))
{
return false;
}

int target = (user.SkillsTotal + ValueFixed) - user.SkillsCap;
int reducedBy = 0;

foreach (Skill s in user.Skills)
{
if (s.IsLocked(SkillLock.Down))
{
if (reducedBy + s.BaseFixedPoint > target)
{
int diff = (target - reducedBy);

if (s.DecreaseBase(diff / 10))
{
reducedBy += diff;
}
}
else
{
if (s.SetBase(0))
{
reducedBy += s.BaseFixedPoint;
}
}
}

if (reducedBy >= target)
{
return true;
}
}

return false;
}

public virtual void UseCharges(int count = 1)
{
Count = Math.Max(0, Count - count);
InvalidateProperties();
}

public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);

int version = writer.SetVersion(1);

switch (version)
{
case 1:
{
writer.WriteFlag(Mode);
writer.WriteFlag(Flags);
writer.Write(Count);
writer.Write(Value);
writer.Write(DeleteWhenEmpty);
writer.WriteList(IgnoredSkills, skill => writer.WriteFlag(skill));
}
break;
case 0:
{
writer.Write((byte)Mode);
writer.Write((byte)Flags);
writer.Write(Count);
writer.Write(Value);
writer.Write(DeleteWhenEmpty);
writer.WriteList(IgnoredSkills, skill => writer.Write((short)skill));
}
break;
}
}

public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);

int version = reader.GetVersion();

switch (version)
{
case 1:
{
Mode = reader.ReadFlag<SkillCodexMode>();
Flags = reader.ReadFlag<SkillCodexFlags>();
Count = reader.ReadInt();
Value = reader.ReadDouble();
DeleteWhenEmpty = reader.ReadBool();
IgnoredSkills = reader.ReadList(r => r.ReadFlag<SkillName>());
}
break;
case 0:
{
Mode = (SkillCodexMode)reader.ReadByte();
Flags = (SkillCodexFlags)reader.ReadByte();
Count = reader.ReadInt();
Value = reader.ReadDouble();
DeleteWhenEmpty = reader.ReadBool();
IgnoredSkills = reader.ReadList(() => (SkillName)reader.ReadShort());
}
break;
}
}
}
}

« Last Edit: February 12, 2015, 12:35:44 AM by Greed »

Offline Vorspire

  • Administrator
  • *****
  • Posts: 136
  • Likes: 34
  • Founder & Developer
    • View Profile
    • Vita-Nex: Core
Re: Customizing the Skill Codex
« Reply #5 on: February 12, 2015, 03:03:41 PM »
Ah OK, sorry, that AddRange method is from the next VNc update hehe, just change it slightly:

Code: [Select]
IgnoredSkills.AddRange(new[]{SkillName.Alchemy, SkillName.Macing, SkillName.Imbuing});

Offline Greed

  • User
  • *
  • Posts: 21
  • Likes: 2
    • View Profile
Re: Customizing the Skill Codex
« Reply #6 on: February 12, 2015, 04:07:40 PM »
Oh Ok  ;D

Now that does compile but it doesn't seem to get rid of the ignored skills. Hmm.

Code: [Select]
#region Header
//   Vorspire    _,-'/-'/  SkillCodex.cs
//   .      __,-; ,'( '/
//    \.    `-.__`-._`:_,-._       _ , . ``
//     `:-._,------' ` _,`--` -: `_ , ` ,' :
//        `---..__,,--'  (C) 2014  ` -'. -'
//        #  Vita-Nex [http://core.vita-nex.com]  #
//  {o)xxx|===============-   #   -===============|xxx(o}
//        #        The MIT License (MIT)          #
#endregion

#region References
using System;
using System.Collections.Generic;
using System.Drawing;

using Server;
using Server.Mobiles;

using VitaNex.Items;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion

namespace Server.Items
{
    public abstract class SkillCodexBase : SkillCodex
    {
        private static readonly SkillName[] DefaultIgnoredSkills = { SkillName.Mysticism, SkillName.Imbuing };

        public SkillCodexBase(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode, SkillCodexFlags flags)
            : base(count, value, deleteWhenEmpty, mode, flags)
        {
            IgnoredSkills.AddRange(new[] { SkillName.Alchemy, SkillName.Macing, SkillName.Imbuing });
        }

        public SkillCodexBase(Serial serial)
            : base(serial)
        { }

        public override bool ValidateSkill(Mobile user, Skill skill, bool message)
        {
            if (user is PlayerMobile)
            {
                var pm = (PlayerMobile)user;

                if (skill.SkillName == SkillName.Spellweaving)
                {
                    if (!pm.Spellweaving)
                    {
                        if (message)
                        {
                            pm.SendMessage(
                                SuperGump.DefaultErrorHue,
                                "You must complete the Patience or Discipline quest chains to unlock {0}.",
                                skill.Name);
                        }

                        return false;
                    }
                }
            }

            return base.ValidateSkill(user, skill, message);
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            var version = 0;

            writer.Write(version);

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            var version = reader.ReadInt();

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }
    }

    public abstract class SkillCodexFixedBase : SkillCodexBase
    {
        public SkillCodexFixedBase(int count, double value)
            : base(count, value, true, SkillCodexMode.Fixed, SkillCodexFlags.Base)
        { }

        public SkillCodexFixedBase(Serial serial)
            : base(serial)
        { }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            var version = 0;

            writer.Write(version);

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            var version = reader.ReadInt();

            switch (version)
            {
                case 0:
                    { }
                    break;
            }
        }
    }
}

namespace VitaNex.Items
{
public enum SkillCodexFlags : byte
{
Base = 0x00,
Cap = 0x01,
Both = 0x02
}

public enum SkillCodexMode : byte
{
Fixed = 0x00,
Increase = 0x01,
Decrease = 0x02
}

public class SkillCodex : Item
{
private SuperGump SelectionGump { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public SkillCodexMode Mode { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public SkillCodexFlags Flags { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public int Count { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public double Value { get; set; }

[CommandProperty(AccessLevel.GameMaster)]
public int ValueFixed { get { return (int)(Value * 10); } set { Value = value / 10; } }

[CommandProperty(AccessLevel.GameMaster)]
public bool DeleteWhenEmpty { get; set; }

public List<SkillName> IgnoredSkills { get; protected set; }
public List<SkillName> SelectedSkills { get; protected set; }

[Constructable]
public SkillCodex()
: this(1)
{ }

[Constructable]
public SkillCodex(int count)
: this(count, 100.0)
{ }

[Constructable]
public SkillCodex(int count, double value)
: this(count, value, true)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty)
: this(count, value, deleteWhenEmpty, SkillCodexMode.Fixed)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode)
: this(count, value, deleteWhenEmpty, mode, SkillCodexFlags.Base)
{ }

[Constructable]
public SkillCodex(int count, double value, bool deleteWhenEmpty, SkillCodexMode mode, SkillCodexFlags flags)
: base(8793)
{
SelectedSkills = new List<SkillName>();
IgnoredSkills = new List<SkillName>();

Mode = mode;
Flags = flags;
Count = count;
Value = value;
DeleteWhenEmpty = deleteWhenEmpty;

Name = "Codex of Wisdom";
LootType = LootType.Blessed;
Stackable = false;
}

public SkillCodex(Serial serial)
: base(serial)
{ }

public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);

string html = String.Empty, flags = String.Empty;

html += String.Format("<basefont color=#{0:X6}>Use: ", Color.Cyan.ToArgb());

switch (Flags)
{
case SkillCodexFlags.Base:
flags = "value";
break;
case SkillCodexFlags.Cap:
flags = "cap";
break;
case SkillCodexFlags.Both:
flags = "value and cap";
break;
}

switch (Mode)
{
case SkillCodexMode.Increase:
{
html += String.Format(
"Increase {0} skill{1} {2} by {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
case SkillCodexMode.Decrease:
{
html += String.Format(
"Decrease {0} skill{1} {2} by {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
case SkillCodexMode.Fixed:
{
html += String.Format("Set {0} skill{1} {2} to {3:F2}%", Count, Count == 1 ? String.Empty : "s", flags, Value);
}
break;
}

list.Add(html);
}

public override void OnDoubleClick(Mobile from)
{
if (from == null || from.Deleted || !Validate(from, true))
{
return;
}

SelectSkills(from);
InvalidateProperties();
}

public virtual bool ValidateSkill(Mobile user, Skill skill, bool message)
{
if (user == null || user.Deleted || skill == null)
{
return false;
}

switch (Mode)
{
case SkillCodexMode.Increase:
{
if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (!CanReduceSkills(user))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already know everything this codex can offer, reduce some skills to make room for more knowledge.");
}

return false;
}
}

if (skill.IsCapped() || skill.WillCap(Value, false))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}

if (!skill.IsLocked(SkillLock.Up))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
case SkillCodexMode.Decrease:
{
if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (user.SkillsTotal - ValueFixed < 0)
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "You already forgot everything this codex can offer.");
}

return false;
}

if (skill.IsZero() || skill.WillZero(Value, false))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already forgot everything this codex can offer about {0}, any further and you'll forget how to breath!",
skill.Name);
}

return false;
}

if (!skill.IsLocked(SkillLock.Down))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
case SkillCodexMode.Fixed:
{
if (Flags == SkillCodexFlags.Cap)
{
if (skill.CapFixedPoint == ValueFixed)
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}
}

if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
if (ValueFixed < skill.BaseFixedPoint)
{
if (user.SkillsTotal - (skill.BaseFixedPoint - ValueFixed) < 0)
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already forgot everything this codex can offer, any further and you'll forget how to breath!");
}

return false;
}
}
else if (ValueFixed > skill.BaseFixedPoint)
{
if (user.SkillsTotal + (ValueFixed - skill.BaseFixedPoint) > user.SkillsCap)
{
if (!CanReduceSkills(user))
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue,
"You already know everything this codex can offer, reduce some skills to make room for more knowledge.");
}

return false;
}
}
}
else
{
if (message)
{
user.SendMessage(
SuperGump.DefaultErrorHue, "You already know everything this codex can offer about {0}.", skill.Name);
}

return false;
}

if (skill.IsLocked(SkillLock.Locked))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "The skill {0} is locked.", skill.Name);
}

return false;
}
}
}
break;
}

return true;
}

public virtual bool Validate(Mobile user, bool message)
{
if (user == null || user.Deleted || !user.CanSee(this))
{
return false;
}

if (!IsChildOf(user.Backpack))
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "This codex must be in your backpack to read it.");
}

return false;
}

if (Count <= 0)
{
if (message)
{
user.SendMessage(SuperGump.DefaultErrorHue, "This codex does not contain any skills to learn.");
}

return false;
}

return true;
}

public virtual void SelectSkills(Mobile user)
{
if (user == null || user.Deleted || !Validate(user, true) || !(user is PlayerMobile))
{
return;
}

if (SelectionGump != null)
{
SelectionGump.Close(true);
}

SelectionGump = new SkillSelectionGump(
user as PlayerMobile,
null,
Count,
null,
null,
skills =>
{
if (!ApplySkills(user, skills))
{
SelectSkills(user);
}
},
IgnoredSkills.ToArray()).Send();
}

public virtual bool ApplySkills(Mobile user, SkillName[] skills)
{
if (user == null || user.Deleted || skills == null || skills.Length == 0 || !Validate(user, true))
{
return false;
}

foreach (SkillName sn in skills)
{
if (user.Deleted || !Validate(user, true))
{
return false;
}

Skill skill = user.Skills[sn];

if (!ValidateSkill(user, skill, true))
{
continue;
}

bool charge = false;

if (Flags == SkillCodexFlags.Cap || Flags == SkillCodexFlags.Both)
{
switch (Mode)
{
case SkillCodexMode.Increase:
{
skill.IncreaseCap(Value);
}
break;
case SkillCodexMode.Decrease:
{
skill.DecreaseCap(Value);
}
break;
case SkillCodexMode.Fixed:
{
skill.SetCap(Value);
}
break;
}

if (Flags != SkillCodexFlags.Both)
{
EndApply(skill);
return true;
}

charge = true;
}

if (Flags == SkillCodexFlags.Base || Flags == SkillCodexFlags.Both)
{
charge = false;

switch (Mode)
{
case SkillCodexMode.Increase:
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (TryReduceSkills(user) && skill.IncreaseBase(Value))
{
charge = true;
}
}
else if (skill.IncreaseBase(Value))
{
charge = true;
}
}
break;
case SkillCodexMode.Decrease:
{
if (skill.DecreaseBase(Value))
{
charge = true;
}
}
break;
case SkillCodexMode.Fixed:
{
if (ValueFixed > skill.BaseFixedPoint)
{
if (user.SkillsTotal + ValueFixed > user.SkillsCap)
{
if (TryReduceSkills(user) && skill.SetBase(Value))
{
charge = true;
}
}
else if (skill.SetBase(Value))
{
charge = true;
}
}
else if (ValueFixed < skill.BaseFixedPoint && skill.SetBase(Value))
{
charge = true;
}
}
break;
}
}

if (charge)
{
EndApply(skill);
}
}

return true;
}

protected void EndApply(Skill skill)
{
skill.Normalize();
UseCharges();

if (Count <= 0)
{
if (DeleteWhenEmpty)
{
Delete();
}
}
}

protected bool CanReduceSkills(Mobile user)
{
int target = (user.SkillsTotal + ValueFixed) - user.SkillsCap;
int canReduceBy = 0;

foreach (Skill s in user.Skills)
{
if (s.IsLocked(SkillLock.Down))
{
if (canReduceBy + s.BaseFixedPoint > target)
{
canReduceBy += (target - canReduceBy);
}
else
{
canReduceBy += s.BaseFixedPoint;
}
}

if (canReduceBy >= target)
{
return true;
}
}

return false;
}

protected bool TryReduceSkills(Mobile user)
{
if (!CanReduceSkills(user))
{
return false;
}

int target = (user.SkillsTotal + ValueFixed) - user.SkillsCap;
int reducedBy = 0;

foreach (Skill s in user.Skills)
{
if (s.IsLocked(SkillLock.Down))
{
if (reducedBy + s.BaseFixedPoint > target)
{
int diff = (target - reducedBy);

if (s.DecreaseBase(diff / 10))
{
reducedBy += diff;
}
}
else
{
if (s.SetBase(0))
{
reducedBy += s.BaseFixedPoint;
}
}
}

if (reducedBy >= target)
{
return true;
}
}

return false;
}

public virtual void UseCharges(int count = 1)
{
Count = Math.Max(0, Count - count);
InvalidateProperties();
}

public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);

int version = writer.SetVersion(1);

switch (version)
{
case 1:
{
writer.WriteFlag(Mode);
writer.WriteFlag(Flags);
writer.Write(Count);
writer.Write(Value);
writer.Write(DeleteWhenEmpty);
writer.WriteList(IgnoredSkills, skill => writer.WriteFlag(skill));
}
break;
case 0:
{
writer.Write((byte)Mode);
writer.Write((byte)Flags);
writer.Write(Count);
writer.Write(Value);
writer.Write(DeleteWhenEmpty);
writer.WriteList(IgnoredSkills, skill => writer.Write((short)skill));
}
break;
}
}

public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);

int version = reader.GetVersion();

switch (version)
{
case 1:
{
Mode = reader.ReadFlag<SkillCodexMode>();
Flags = reader.ReadFlag<SkillCodexFlags>();
Count = reader.ReadInt();
Value = reader.ReadDouble();
DeleteWhenEmpty = reader.ReadBool();
IgnoredSkills = reader.ReadList(r => r.ReadFlag<SkillName>());
}
break;
case 0:
{
Mode = (SkillCodexMode)reader.ReadByte();
Flags = (SkillCodexFlags)reader.ReadByte();
Count = reader.ReadInt();
Value = reader.ReadDouble();
DeleteWhenEmpty = reader.ReadBool();
IgnoredSkills = reader.ReadList(() => (SkillName)reader.ReadShort());
}
break;
}
}
}
}


Edit - I ended up making my own skill thingies. They aren't as nice as yours but they are still pretty cool and get the job done.  ;)
« Last Edit: February 14, 2015, 09:21:34 AM by Greed »

 

Carbonate design by Bloc
variant: carbon
SMF 2.0.15 | SMF © 2017, Simple Machines