Guide to installing Mythik Achievement System to a 'shard core running DotNet < 4.8, using ServUO Pub 57 repo as a foundation:
1. The Server folder contains the EventSink.cs file. Open both EventSink.cs files "side-by-side", using a program such as WinMerge. Sometimes the merges are nice and clean, but if the EventSink is much older, the merge might be a lot more complex, so in the event of a messy merge, use Notepad++ to open both files and then copy/paste entire sections of code from ServUO's repo into the older core. Note the version code changes mentioned above!
2. If you do not have the Playermobile edits, you will have to use the define STOREITEM <--- This is what creates the AchievementSystemMemoryStone in the next step.
Drag/Drop Mythik Achievement System into Scripts->Customs and then open AchievementSystem.cs and remove the // at the very top from define STOREITEM and make the following changes to "force" the system to use the stone.
Scroll down a few lines and add // to the lines that say:
```
//#if STOREONITEM
//#else
//using Scripts.Mythik.Mobiles;
//#endif
#if STOREONITEM
if (!AchievementSystemMemoryStone.GetInstance().Achievements.ContainsKey(player.Serial))
AchievementSystemMemoryStone.GetInstance().Achievements.Add(player.Serial, new Dictionary<int, AchieveData>());
var achieves = AchievementSystemMemoryStone.GetInstance().Achievements[player.Serial];
var total = AchievementSystemMemoryStone.GetInstance().GetPlayerPointsTotal(player);
//#else
//var achieves = (player as MythikPlayerMobile).Achievements;
//var total = (player as MythikPlayerMobile).AchievementPointsTotal;
#endif
#if STOREONITEM
if (!AchievementSystemMemoryStone.GetInstance().Achievements.ContainsKey(player.Serial))
AchievementSystemMemoryStone.GetInstance().Achievements.Add(player.Serial, new Dictionary<int, AchieveData>());
var achieves = AchievementSystemMemoryStone.GetInstance().Achievements[player.Serial];
//#else
//var achieves = (player as MythikPlayerMobile).Achievements;
#endif
```
3. Next open AchievementSystemMemoryStone.cs and at the top where it says:
```
public static AchievementSystemMemoryStone GetInstance()
{
if (m_instance == null)
m_instance = new AchievementSystemMemoryStone();
m_instance.MoveToWorld(new Point3D(0, 0, 0), Map.Felucca); //<----- CHANGE THESE LOCATION COORDS
return m_instance;
}
```
If you do not change these coords, every time a player uses the command Feats, the stone will move to those coordinates. If you move the stone to another location, it will move back. You do not have to change the coords, but the stone will be at 0,0,0 in Felucca, which is the top corner of the ocean on the map.
The system is dependent upon EventSinks, so those need to be added in the proper locations.
4a. For the HarvestSystem.cs file and InvokeResourceHarvestSuccess, those changes are in BonusHarvestResource (bonusItem does not exist outside this method):
```
BonusHarvestResource bonus = def.GetBonusResource();
if ( bonus != null && bonus.Type != null && skillBase >= bonus.ReqSkill )
{
Item bonusItem = Construct( bonus.Type, from );
if ( Give( from, bonusItem, true ) ) //Bonuses always allow placing at feet, even if pack is full irregrdless of def
{
bonus.SendSuccessTo( from );
}
else
{
item.Delete();
}
#region ServUO Pub 57 for Mythik Achievement System
EventSink.InvokeResourceHarvestSuccess(new ResourceHarvestSuccessEventArgs(from, tool, item, bonusItem, this)); //<--- This means every time players harvest a *bonus* resource while mining, it will increase the ore count for the Achievement for that attempt only.
#endregion
}
```
4b. For BaseCreature.cs and InvokeOnKilledBy, those changes are in OnKilledBy:
```
public virtual void OnKilledBy(Mobile mob)
{
#region Mondain's Legacy
if (GivesMinorArtifact && Paragon.CheckArtifactChance(mob, this))
GiveMinorArtifact(mob);
#endregion
#region SA
if (GivesSAArtifact && Paragon.CheckArtifactChance(mob, this))
GiveSAArtifact(mob);
#endregion
#region ServUO Pub 57 Mythik Achievement System
EventSink.InvokeOnKilledBy(new OnKilledByEventArgs(this, mob));
#endregion
}
```
4c. For Server->Region.cs and Invoke OnEnterRegion, those changes are in OnRegionChange:
```
//Region oldR = oldRegion;
//Region newR = newRegion;
#region ServUO Pub 57 Mythik Achievement System
var oldR = oldRegion;
var newR = newRegion;
while (oldR != newR)
{
var oldRChild = oldR != null ? oldR.ChildLevel : -1;
var newRChild = newR != null ? newR.ChildLevel : -1;
if (oldRChild >= newRChild && oldR != null)
{
oldR.OnExit(m);
oldR = oldR.Parent;
}
if (newRChild >= oldRChild && newR != null)
{
newR.OnEnter(m);
EventSink.InvokeOnEnterRegion(new OnEnterRegionEventArgs(m, oldRegion, newR));
newR = newR.Parent;
}
}
#endregion
/*
while ( oldR != newR )
{
int oldRChild = ( oldR != null ? oldR.ChildLevel : -1 );
int newRChild = ( newR != null ? newR.ChildLevel : -1 );
if ( oldRChild >= newRChild )
{
oldR.OnExit( m );
oldR = oldR.Parent;
}
if ( newRChild >= oldRChild )
{
newR.OnEnter( m );
newR = newR.Parent;
}
}
*/
```
4d. For Crafting and Scripts->Services->Craft->CraftItem.cs, those changes are inside CheckSkills->item !=null, which is part of CompleteCraft:
```
if (item != null)...
from.AddToBackpack(item);
#region ServUO Pub 57 Mythik Achievement System
EventSink.InvokeCraftSuccess(new CraftSuccessEventArgs(from, item, tool is Item ? (Item)tool : null));
#endregion
from.PlaySound( 0x57 );
```
5. Editing the AchievementGump:
A good example of the menu structure is more easily noticeable if the code is staggered.
```
Categories.Add(new AchievementCategory(1, 0, "Exploration"));
Categories.Add(new AchievementCategory(2, 1, "Towns"));
Categories.Add(new AchievementCategory(3, 1, "Dungeons"));
Achievements.Add(new DiscoveryAchievement(0, 2, 0x14EB, false, null, "Minoc", "Discover Minoc Township", 5, "Minoc"));
```
The hierarchy is Exploration -> Towns | Dungeons ->Minoc. Notice the integers in the code: 1, 0, Exploration, 2, 1, Towns, 3, 1, Dungeons, 0, 2, Minoc. This is how the menus are connected together. Each Child adopts the Parent's number, so Exploration is the Parent (1), Towns | Dungeons are each a Child and a Parent and inherit from Parent (1) and provide for Child (2) and (3), and Minoc is a Child of Towns (2). If a dungeon is added, it would be a Child of Dungeons (3).
There is limited space vertically on the whole interface, so use that space as wisely as possible by nesting Achievements, but also note that you can only nest so far as well.
Each Achievement is structured in a similar fashion, for example DiscoveryAchievement:
var achieve = new HarvestAchievement(100, 7, 0, false, null, 100, "100 Iron Ore", "Mine up a bonus resource and increase the Iron Ore total to reach the goal.", 5, typeof(IronOre), typeof(Lettuce));
AchievementType(AchieveMentNumber, MenuParent, MenuPosition, itemIcon, HiddenUntilComplete (true/false), Pre-requisite or null, "MenuEntry", "Achievement Title", 5 <- Points earned when completed, typeof(IronOre)<-required object to complete, typeof(Lettuce)<-item awarded when complete.
If linking achievements together, create an entry for each var, increasing the value by +1,var1, var2, var3...then call that var in the next linked achievement, for example:
var achieve = new HarvestAchievement(100, 7, 0, false, null, 100, "100 Iron Ore", "Mine up a bonus resource and increase the Iron Ore total to reach the goal.", 5, typeof(IronOre), typeof(Lettuce));
Achievements.Add(achieve); //<--Entry added for var achieve above
Achievements.Add(new HarvestAchievement(101, 7, 1, false, achieve, 500, "500 Iron Ore", "Mine 500 Iron Ore for each bonus received.", 5, typeof(IronOre), typeof(Peach))); //VAR ^
Hopefully a lot more folks can get this system installed, because DL (darklotus) knocked this out of the park! It is an incredible benefit to any 'shard!