it is complex, but not overly complex.
if you skimp on the checks, you could have undesirable behavior.
using System;
using Server;
using Server.Items;
using System.Collections;
using System.Collections.Generic;
using Server.Gumps;
using Server.Mobiles;
using Server.ContextMenus;
using Server.Prompts;
using Server.Targeting;
using Server.Network;
namespace Server.Items
{
public class CorgulAltar : Item
{
public bool tmap_done;
[Constructable]
public CorgulAltar()
: base(0x35EF)
{
tmap_done = false;
this.Name = "Sacrificial Altar";
this.Hue = 2075;
this.Movable = false;
}
public override void OnDoubleClick(Mobile from)
{
from.SendMessage("Your offering will be consumed by the altar if the sacrifice is accepted. You will then have 30 seconds to re-use the shrine to collect your new map and pay the blood cost.");
from.Target = new InternalTarget(this, tmap_done);
}
private class InternalTarget : Target
{
private CorgulAltar m_Item;
public bool tmap_done;
public InternalTarget(CorgulAltar item, bool t)
: base(2, false, TargetFlags.None)
{
tmap_done = t;
m_Item = item;
}
protected override void OnTarget(Mobile from, object targeted)
{
PlayerMobile pm = from as PlayerMobile;
if (m_Item.Deleted)
return;
WorldMap wmap;
TreasureMap tmap;
if (targeted is TreasureMap && tmap_done == false)
{
tmap = (TreasureMap)targeted;
from.SendMessage("Your offering has been accepted. The price of blood will be taken when you sacrifice your “world map”.");
tmap.Delete();
}
else if(!(targeted is TreasureMap) && tmap_done == true)
{
from.SendMessage("You must target a treasure map first.");
return;
}
if (targeted is WorldMap && tmap_done == true)
{
from.SendMessage("Your final offering has been accepted and the blood cost has been collected.");
from.Hits = 1;
from.AddToBackpack(new CorgulMap());
wmap.Delete();
}
else
{
from.SendMessage("You must target a world map.");
return;
}
}
}
public CorgulAltar(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0); // version
writer.Write((bool)tmap_done);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
tmap_done = reader.ReadBool();
}
}
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Item.Deleted)
return;
bool mapDeleted = true;
if ( tmap_done )
{
TreasureMap tmap = targeted as TreasureMap;
WorldMap wmap = targeted as WorldMap;
if ( tmap != null && !tmap.Deleted )
{
from.SendMessage("Your offering has been accepted. The price of blood will be taken when you sacrifice your “world map”.");
tmap.Delete();
}
else if ( wmap != null !wmap.Deleted )
{
from.SendMessage("Your final offering has been accepted and the blood cost has been collected.");
from.Hits = 1;
from.AddToBackpack(new CorgulMap());
wmap.Delete();
}
else
{
mapDeleted = false;
}
}
if ( !tmap_done || !mapDeleted)
{
from.SendMessage("You must target either a Treasure Map or a World Map first.");
}
}
public class CorgulAltar : Item
{
private Dictionary<Mobile, CorgulContext> m_MapTable;
public Dictionary<Mobile, CorgulContext> MapTable
{
get{ return m_MapTable; }
private set
{
m_MapTable = value;
}
}
[Constructable]
public CorgulAltar() : base(0x35EF)
{
Name = "Sacrificial Altar";
Hue = 2075;
Movable = false;
MapTable = new Dictionary<Mobile, CorgulContext>();
}
public CorgulAltar(Serial s) : base(s) {}
public override void OnDoubleClick(Mobile from)
{
if(!MapTable.ContainsKey(from) || !MapTable[from].SacrificedTMap)
{
MapTable[from] = new CorgulContext(from);
from.SendMessage("Select a treasure map");
from.BeginTarget(2, false, TargetFlags.None, new TargetCallback( OnTargetTMap ));
}
else if(!MapTable[from].RewardReceived())
{
from.SendMessage("You've already sacrificed a treasure map. Now you must sacrifice a world map");
from.BeginTarget(2, false, TargetFlags.None, new TargetCallback( OnTargetWMap ));
}
else
{
from.SendMessage("You have already received your reward from this altar.");
}
}
private void OnTargetTMap(Mobile from, object targeted)
{
if(targeted is TreasureMap)
{
MapTable[from].SacrificedTMap = true;
((TreasureMap)targeted).Delete();
from.SendMessage("Your offering has been accepted. The price of blood will be taken when you sacrifice your \"world map.\"");
from.BeginTarget(2, false, TargetFlags.None, new TargetCallback( OnTargetWMap ));
}
else
{
from.SendMessage("No, pick a treasure map");
from.BeginTarget(2, false, TargetFlags.None, new TargetCallback( OnTargetTMap ));
}
}
private void OnTargetWMap(Mobile from, object targeted)
{
if(targeted is WorldMap)
{
MapTable[from].SacrificedWMap = true;
((WorldMap)targeted).Delete();
from.SendMessage("Your final offering has been accepted and the blood cost has been collected.");
from.Hits = 1;
from.AddToBackpack(new CorgulMap());
}
else
{
from.SendMessage("No, pick a world map");
from.BeginTarget(2, false, TargetFlags.None, new TargetCallback( OnTargetWMap ));
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
writer.Write((int)MapTable.Count);
foreach(KeyValuePair<Mobile, CorgulContext> kvp in MapTable)
{
writer.WriteMobile<Mobile>(kvp.Key);
kvp.Value.Serialize(writer);
}
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch(version)
{
case 0:
int tableCount = reader.ReadInt();
MapTable = new Dictionary<Mobile, CorgulContext>(tableCount);
for(int i = 0; i < tableCount; i++)
{
MapTable[reader.ReadMobile<Mobile>()] = new CorgulContext(reader);
}
break;
}
}
private class CorgulContext
{
private Mobile m_Mobile;
private bool m_TMap;
private bool m_WMap;
public Mobile Mobile
{
get{ return m_Mobile; }
set{ m_Mobile = value; }
}
public bool SacrificedTMap
{
get{ return m_TMap; }
set{ m_TMap = value; }
}
public bool SacrificedWMap
{
get{ return m_WMap; }
set{ m_WMap = value; }
}
public CorgulContext(Mobile from)
{
m_Mobile = from;
}
public bool RewardReceived()
{
return (SacrificedTMap && SacrificedWMap);
}
public void Serialize(GenericWriter writer)
{
writer.Write((int)0);
writer.WriteMobile<Mobile>(Mobile);
writer.Write(SacrificedTMap);
writer.Write(SacrificedWMap);
}
public CorgulContext(GenericReader reader)
{
int version = reader.ReadInt();
switch(version)
{
case 0:
Mobile = reader.ReadMobile<Mobile>();
SacrificedTMap = reader.ReadBool();
SacrificedWMap = reader.ReadBool();
break;
}
}
}
}
Yes, but if the WMap target is cancelled or lost, the player would need to start over from the beginning. Not a big deal, though, since that solution would be much simpler.Khaz, you can use TargetStateCallback and pass previously targeted map to the next target. Then validate that map wasn't deleted between targets and still in player's backpack and delete it.
ReSharper said:1 Using directive is not required by the code and can be safely removed
3 Using directive is not required by the code and can be safely removed
4 Using directive is not required by the code and can be safely removed
5 Using directive is not required by the code and can be safely removed
22 Inconsistent accessibility: type argument 'Server.CorgulAltar.CorgulContext' is less accessible than property 'Server.CorgulAltar.MapTable'
51 Redundant explicit delegate creation
56 Redundant explicit delegate creation
73 Redundant explicit delegate creation
78 Redundant explicit delegate creation
98 Redundant explicit delegate creation
106 Type cast is redundant
108 Type cast is redundant
112 Type argument specification is redundant
175 Type cast is redundant
177 Type argument specification is redundant
YupDisclaimer: written in textpad so I dont know for sure that it compiles
Yup! I actually wrote it first with auto-generated properties and then some, but realized it wouldn't be 2.x compliant and changed it before postingI just wish RunUO used .NET 4 as standard, I'm so used to writing .NET 4 compliant code that giving .NET 2 help out on these forums is becoming quite a challenge, lol.
This altar item could be done in half the lines of code, starting with auto-properties, and maybe tuples instead of nested custom classes.
using CorgulContext = System.Tuple<Server.Mobile, System.Boolean, System.Boolean>;
Good deal... there are certainly pieces in the example you can simplify!lol.. you guys are having too much fun debating all of this! Let me clarify, I'm using full .NET 4.0 support (from the ForkUO fork). So no need to fear having to offer assistance based on old 2.0 code.