Hi,

I am trying to modify Detect Hidden to reveal trapped containers and display [Trapped] above the found trapped containers in the appropriate colour as per OSI. I want this to require 50 skill to work with 100 being flawless.

Initially I got it to display [trapped] but this appears at the target cursor and not over the container and since then I added the skill check which fails to compile.

I also added code to select the correct hue for the overhead message and this throws up errors too on TrapType. As you may remember I have struggled with enums before! :)

I am quite certain I am doing something elementally stupid but I can't figure it out!

Thanks so much for any help.

FB.

Here are the errors:
Code:
Errors:
 + Skills/DetectHidden.cs:
  CS1502: Line 94: The best overloaded method match for 'Server.Mobile.CheckSk
ill(Server.SkillName, double, double)' has some invalid arguments
  CS1503: Line 94: Argument 2: cannot convert from 'Server.Items.Container' to
 'double'
  CS1061: Line 96: 'Server.Items.Container' does not contain a definition for
'TrapType' and no extension method 'TrapType' accepting a first argument of type
 'Server.Items.Container' could be found (are you missing a using directive or a
n assembly reference?)
  CS1061: Line 99: 'Server.Items.Container' does not contain a definition for
'TrapType' and no extension method 'TrapType' accepting a first argument of type
 'Server.Items.Container' could be found (are you missing a using directive or a
n assembly reference?)
  CS1061: Line 102: 'Server.Items.Container' does not contain a definition for
 'TrapType' and no extension method 'TrapType' accepting a first argument of typ
e 'Server.Items.Container' could be found (are you missing a using directive or
an assembly reference?)
  CS1061: Line 105: 'Server.Items.Container' does not contain a definition for
 'TrapType' and no extension method 'TrapType' accepting a first argument of typ
e 'Server.Items.Container' could be found (are you missing a using directive or
an assembly reference?)

And here is my DetectHidden.cs. My code starts at line 85 in this segment "containerinRange"

Code:
using System;
using Server.Items;
using Server.Factions;
using Server.Mobiles;
using Server.Multis;
using Server.Targeting;
using Server.Regions;

namespace Server.SkillHandlers
{
 public class DetectHidden
 {
 public static void Initialize()
 {
 SkillInfo.Table[(int)SkillName.DetectHidden].Callback = new SkillUseCallback( OnUse );
 }

 public static TimeSpan OnUse( Mobile src )
 {
 src.SendLocalizedMessage( 500819 );//Where will you search?
 src.Target = new InternalTarget();

 return TimeSpan.FromSeconds( 6.0 );
 }

 private class InternalTarget : Target
 {
 public InternalTarget() : base( 12, true, TargetFlags.None )
 {
 }

 protected override void OnTarget( Mobile src, object targ )
 {
 bool foundAnyone = false;

 Point3D p;
 if ( targ is Mobile )
 p = ((Mobile)targ).Location;
 else if ( targ is Item )
 p = ((Item)targ).Location;
 else if ( targ is IPoint3D )
 p = new Point3D( (IPoint3D)targ );
 else 
 p = src.Location;

 double srcSkill = src.Skills[SkillName.DetectHidden].Value;
 int range = (int)(srcSkill / 10.0);

 if ( !src.CheckSkill( SkillName.DetectHidden, 0.0, 100.0 ) )
 range /= 2;

 BaseHouse house = BaseHouse.FindHouseAt( p, src.Map, 16 );

 bool inHouse = ( house != null && house.IsFriend( src ) );

 if ( inHouse )
 range = 22;

 if ( range > 0 )
 {
 IPooledEnumerable inRange = src.Map.GetMobilesInRange( p, range );

 foreach ( Mobile trg in inRange )
 {
 if ( trg.Hidden && src != trg )
 {
 double ss = srcSkill + Utility.Random( 21 ) - 10;
 double ts = trg.Skills[SkillName.Hiding].Value + Utility.Random( 21 ) - 10;

 if ( src.AccessLevel >= trg.AccessLevel && ( ss >= ts || ( inHouse && house.IsInside( trg ) ) ) )
 {
 if ( trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y) )
 continue;

 trg.RevealingAction();
 trg.SendLocalizedMessage( 500814 ); // You have been revealed!
 foundAnyone = true;
 }
 }
 }

 inRange.Free();

 IPooledEnumerable containerinRange = src.Map.GetItemsInRange( p, range );

 int trapHue = 0x3B2;

 foreach ( Item item in containerinRange )
 {
 if (item is Container)
 {
 Container cont = (Container)item;
 
 if (src.CheckSkill(SkillName.DetectHidden, cont, 0.10))
 {
 if ( cont.TrapType == TrapType.ExplosionTrap )
 trapHue = 0x21;

 else if ( cont.TrapType == TrapType.PoisonTrap )
 trapHue = 0x44;

 else if ( cont.TrapType == TrapType.DartTrap )
 trapHue = 0x3;

 else if ( cont.TrapType == TrapType.MagicTrap )
 trapHue = 0x30;

 cont.PublicOverheadMessage( Server.Network.MessageType.Label, trapHue, 500813 ); //trapped
 }
 }
 
 }

 inRange.Free();

 if ( Faction.Find( src ) != null )
 {
 IPooledEnumerable itemsInRange = src.Map.GetItemsInRange( p, range );

 foreach ( Item item in itemsInRange )
 {
 if ( item is BaseFactionTrap )
 {
 BaseFactionTrap trap = (BaseFactionTrap) item;

 if ( src.CheckTargetSkill( SkillName.DetectHidden, trap, 80.0, 100.0 ) )
 {
 src.SendLocalizedMessage( 1042712, true, " " + (trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName) ); // You reveal a trap placed by a faction:

 trap.Visible = true;
 trap.BeginConceal();

 foundAnyone = true;
 }
 }
 }

 itemsInRange.Free();
 }
 }

 if ( !foundAnyone )
 {
 src.SendLocalizedMessage( 500817 ); // You can see nothing hidden there.
 }
 }
 }
 }
}
 
Most of those errors come from the fact that you are looking at the properties of a Container which doesn't have any properties like "TrapType". A TrapableContainer does.

The safest way to cast to a TrapableContainer is like so.

Let's say you have a reference to a Container:

Code:
private static TrapType CheckTrapType( Container cont )
{
   // code will go here.
}

You can safely cast "cont" to be a TrapableContainer like so:

Code:
private static TrapType CheckTrapType( Container cont )
{
   var trappedCont = cont as TrapableContainer;
   if(trappedCont == null)
   {
      // this is not a trapablecontainer, it has no trap
      return TrapType.None;
   }

   // at this point we know for sure that "trappedCont" is a TrapableContainer, so return its trap type
   return trappedCont.TrapType;
}

In your situation, it would make sense to simply change your "Container" references to by of type "TrapableContainer" - this is just an example showing where you are going wrong.

I would recommend downloading and using Visual Studio if you do not already as it will make things a lot more obvious as you code.
 
Last edited:
Looks like your error is in the skill check procedure. Change this:

Code:
 if (src.CheckSkill(SkillName.DetectHidden, cont, 0.10))

to this:

Code:
 if (src.Mobile_SkillCheckDirectTarget(src, SkillName.DetectHidden, cont, 0.10))

Let me know if that works for you.
 
CheckSkill is not the correct method to match with the signature of the arguments being used that cause the errors.
Replace it with CheckTargetSkill instead;

C#:
if(src.CheckTargetSkill(SkillName.DetectHidden, cont, 0.10))

@Lokai, I don't recommend calling the static handler directly because doing that avoids the point of having a place to hook-in override handlers - Mobile.CheckSkill checks for and calls a delegate, which by default is the static Mobile_SkillCheckDirect[Target] - However, you are right in suggesting the use of CheckTargetSkill (implied)
 
Hi Jack,

Thanks for that. It compiles now. I will re-write it as you suggest. I still need to figure out how to get the [trapped] message over the chests, though.

I have VS2012 but it only does store apps. I will try to grab the desktop version. Is 2012 Express OK or do I need an earlier one? Or should I grab the latest Visual Studio 2015 Community edition?
 
Ahh thanks Lokai and Voxpire. Much appreciated.

That fixed that. I am now just left with the problem of the [trapped] messages not appearing over the chests. I'm getting different coloured messages appear in the journal so I know that is working. It's just that they all appear at the cursor location, so the target I guess rather than the trapped containers.

It's as if I am using targ instead of cont

Also I suppose I should change it to PrivateOverheadMessage as it shouldn't probably be a public affair. I can't remember on OSI if others could see the messages other than the "caster". Anyone know?

Thank you all so much for your help.

David
 
Hi Jack,

Thanks for that. It compiles now. I will re-write it as you suggest. I still need to figure out how to get the [trapped] message over the chests, though.

I have VS2012 but it only does store apps. I will try to grab the desktop version. Is 2012 Express OK or do I need an earlier one? Or should I grab the latest Visual Studio 2015 Community edition?
I personally use VS2015 CE.

I don't believe there is a method in Item.cs for a private overhead message that is not localized (I don't think there is a Cliloc for "[Trapped]"?), LabelTo would be the closest thing but it doesn't support hue.

To send a hued, non-localized overhead message onto the item you could do this:
Code:
    var hue = 0x22; // example hue
    from.Send(new UnicodeMessage(cont.Serial, cont.ItemID, MessageType.Label, hue, 3, "ENU", cont.Name, "[Trapped]"));
 
Thank you Jack

That doesn't make any difference here. The [Trapped] message still only appears at the cursor. And curiously there are grey coloured [trapped] showing in my journal as well as the expected red one. So it would appear not to be picking up the other trap types. I am certain I got some green ones in there yesterday!

[Edit:] Yes I am getting greens too. Just tried it again.

I also tried PrivateOverheadMessage as per my code here(which is commented out) but that gives an error for Server.Items.TrappableContainer not being found.

There is a clilloc for [trapped] in my clilloc list and it is 500813 - I think there is another a little further down the numbers too.

So this is where I am at now, with Jack's Unicode change. I know I still have to change the skill check to start at 50 and be 100% accurate at skill 100 but just trying to get the overhead thing working for now.

My whole DetectHidden.cs file (starts line 101 in my editor):
Code:
using System;
using Server.Items;
using Server.Factions;
using Server.Mobiles;
using Server.Multis;
using Server.Targeting;
using Server.Regions;
using Server.Network;

namespace Server.SkillHandlers
{
public class DetectHidden
{
public static void Initialize()
{
SkillInfo.Table[(int)SkillName.DetectHidden].Callback = new SkillUseCallback( OnUse );
}

public static TimeSpan OnUse( Mobile src )
{
src.SendLocalizedMessage( 500819 );//Where will you search?
src.Target = new InternalTarget();

return TimeSpan.FromSeconds( 6.0 );
}


private static TrapType CheckTrapType( Container cont )
{

var trappedCont = cont as TrapableContainer;

if(trappedCont == null)
{
// this is not a trapablecontainer, it has no trap
return TrapType.None;
}

// at this point we know for sure that "trappedCont" is a TrapableContainer, so return its trap type
return trappedCont.TrapType;
}

private class InternalTarget : Target
{
public InternalTarget() : base( 12, true, TargetFlags.None )
{
}

protected override void OnTarget( Mobile src, object targ )
{
bool foundAnyone = false;

Point3D p;
if ( targ is Mobile )
p = ((Mobile)targ).Location;
else if ( targ is Item )
p = ((Item)targ).Location;
else if ( targ is IPoint3D )
p = new Point3D( (IPoint3D)targ );
else
p = src.Location;

double srcSkill = src.Skills[SkillName.DetectHidden].Value;
int range = (int)(srcSkill / 10.0);

if ( !src.CheckSkill( SkillName.DetectHidden, 0.0, 100.0 ) )
range /= 2;

BaseHouse house = BaseHouse.FindHouseAt( p, src.Map, 16 );

bool inHouse = ( house != null && house.IsFriend( src ) );

if ( inHouse )
range = 22;

if ( range > 0 )
{
IPooledEnumerable inRange = src.Map.GetMobilesInRange( p, range );

foreach ( Mobile trg in inRange )
{
if ( trg.Hidden && src != trg )
{
double ss = srcSkill + Utility.Random( 21 ) - 10;
double ts = trg.Skills[SkillName.Hiding].Value + Utility.Random( 21 ) - 10;

if ( src.AccessLevel >= trg.AccessLevel && ( ss >= ts || ( inHouse && house.IsInside( trg ) ) ) )
{
if ( trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y) )
continue;

trg.RevealingAction();
trg.SendLocalizedMessage( 500814 ); // You have been revealed!
foundAnyone = true;
}
}
}

inRange.Free();

IPooledEnumerable containerinRange = src.Map.GetItemsInRange( p, range );

var trapHue = 0x3B2;

foreach ( Item item in containerinRange )
{
if (item is TrapableContainer)
{
TrapableContainer cont = (TrapableContainer)item;

if(src.CheckTargetSkill(SkillName.DetectHidden, cont, 0.10))
{
CheckTrapType( cont);

if ( cont.TrapType == TrapType.ExplosionTrap )
trapHue = 0x21;

else if ( cont.TrapType == TrapType.PoisonTrap )
trapHue = 0x44;

else if ( cont.TrapType == TrapType.DartTrap )
trapHue = 0x3;

else if ( cont.TrapType == TrapType.MagicTrap )
trapHue = 0x30;

// cont.PublicOverheadMessage( Server.Network.MessageType.Label, trapHue, 500813 ); //trapped
// cont.PrivateOverheadMessage( MessageType.Regular, trapHue, 500813, src.NetState ); //trapped
src.Send(new UnicodeMessage(cont.Serial, cont.ItemID, MessageType.Label, trapHue, 3, "ENU", cont.Name, "[Trapped]"));

}
}

}

inRange.Free();

if ( Faction.Find( src ) != null )
{
IPooledEnumerable itemsInRange = src.Map.GetItemsInRange( p, range );

foreach ( Item item in itemsInRange )
{
if ( item is BaseFactionTrap )
{
BaseFactionTrap trap = (BaseFactionTrap) item;

if ( src.CheckTargetSkill( SkillName.DetectHidden, trap, 80.0, 100.0 ) )
{
src.SendLocalizedMessage( 1042712, true, " " + (trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName) ); // You reveal a trap placed by a faction:

trap.Visible = true;
trap.BeginConceal();

foundAnyone = true;
}
}
}

itemsInRange.Free();
}
}

if ( !foundAnyone )
{
src.SendLocalizedMessage( 500817 ); // You can see nothing hidden there.
}
}
}
}
}
 
Am I right in thinking that I need to modify the core to add a method with Point3D location of am I still doing something stupid?

I ask because it seems to me that everything else that you target with the cursor, for example locking down chests and so on, actually display the [locked down] label at the cursor position and not actually on the chest.

Thanks

David
 
Thank you for your help guys.

This is the final code I think!

I have trapped containers displaying [trapped] over them as per OSI in the appropriate colours, plus the hue shade darkens as the trap level increases. I have trap levels 0-5 on my shard, where 0 is magic trap only. This required changing Nerun's dungeon chest code to add a trap level for dungeon chests and change them to explosion/poison/dart traps with appropriate power rather than just all being magic trap. That means re-spawning them all of course! But this means that you can't just step back a tile and open it without getting hurt, but need to either RemoveTrap or take 4 steps away and cast telekinesis - tricky in a dungeon! I'm a Siege Perilous style shard in Renaissance (ish) era, so this is pretty OSI accurate. I don't think telekinesis stopped you getting dead there though at 4 tiles but it will do for me!

Trapped containers are not detected with skill less than 50.0 and I also skill check on each inspected container so that skill gain increases with the number of detected traps. So normal slow skill gain until 50 and then it rockets given a lot of traps, reducing in speed as your skill increases.

The increased trap power ( > 100 on higher level chests) also meant having to modify RemoveTrap skill because the skill check meant that trap powers over 100 couldn't ever be removed. I need some advice on that still but it works currently. Just means that if you have 100 RemoveTrap skill you always remove a trap on the first attempt which is not how it should be, so I guess I need to put some randomisation in there, but I expected the skill check to deal with that. It doesn't. Neither did the min/max skill check in DetectHidden. I'm not sure if I am just not comprehending it or if there is something wrong with the way the min/max works in the skill check.

Anyway, this works well as far as I can tell and I am currently running it on my test server to give it a workout before putting it live. I would appreciate it if you could point out any "gotchas" or if I have done anything dangerous.

Thanks
David

Code:
// David: Make detect hidden detect trapped containers and display [trapped] above them
// David: Display [trapped] in Red, green or blue hues according to TrapType and level
 IPooledEnumerable containerinRange = src.Map.GetItemsInRange( p, range );

 var trapHue = 0x3B2;  // Hue Grey until TrapType known

 foreach ( Item item in containerinRange )
 {
 if (item is TrapableContainer)
 {
 TrapableContainer cont = (TrapableContainer)item;

 if( srcSkill >= 50 )
 {
 if( src.CheckTargetSkill(SkillName.DetectHidden, cont, srcSkill/100 ))
 {
 var contTrapType = CheckTrapType( cont);
 
 if ( contTrapType == TrapType.ExplosionTrap )
 trapHue = ( cont.TrapLevel < 1 ? 241 : (42 - ( cont.TrapLevel ) ) );
 
 else if ( contTrapType == TrapType.PoisonTrap )
 trapHue = ( cont.TrapLevel < 1 ? 271 : (72 - ( cont.TrapLevel ) ) );
 
 else if ( contTrapType == TrapType.DartTrap )
 trapHue = ( cont.TrapLevel < 1 ? 301 : (102 - ( cont.TrapLevel ) ) );
 
 else if ( contTrapType == TrapType.MagicTrap )
 trapHue = ( cont.TrapLevel < 1 ? 251 : (52 - ( cont.TrapLevel ) ) );
 
 if ( !( contTrapType ==  TrapType.None ) )
 { 
 MessageHelper.SendLocalizedMessageTo( cont, src, 500813, trapHue ); 
 foundAnyone = true;
 }
 }
 }
 }
 
 }
 
if( src.CheckTargetSkill(SkillName.DetectHidden, cont, srcSkill/100 ))

If you want to allow a chance of failure at all skills up to 120, I think you can change this to srcSkill/121. That should scale with greater chance the higher your skill, but still be possible to fail up to 120.
 
My individual skill cap is 100, so perhaps I should try 101.

I did try that initially with the method that uses both min and max skill so min was 50 (the minimum required to detect a trap) and max was 101 (the skill required to be 100% accurate or so I thought) and doing it like that made it fail to detect the traps 100% of the time. The only place I was getting skill gain was in the skill check further up where only one skill parameter is used.

So that was why I put in the separate check for skill being > 50 before it would even look for a trap.

It's not a problem in Detect Hidden because I actually want someone with 100 skill to detect all traps at maximum range without fail. But with Remove Trap I don't want someone with 100 skill to be able to disarm a level 5 trap that can potentially do 250 damage 100% of the time and that seems to be the only way I can get it to work which made me think I had understood it wrong. I would like it to fail around half the time on the level 4 traps and perhaps 75% on the level 5 traps. I will likely experiment with it a little more over the weekend and if necessary do a UtilityRand to get the percentages in manually.

Cheers

David
 
Something simple like;
C#:
double chanceToFail = trapLevel / (double)trapLevelMax;

Assuming 'trapLevelMax' is 10, chances to fail would scale from 0.00 to 1.00 and go up in increments of 0.10
The chance to fail would be 10% for Level 1, 50% for Level 5 and 100% for Level 10.

Should you decide to introduce higher levels, or remove levels, the formula will still scale correctly between 0.00 and 1.00

If you wanted the scale to be 0.00 to 0.75, you'd rework the formula a little like so;
C#:
double chanceToFail = ( trapLevel / (double)trapLevelMax ) * 0.75;

If you wanted to incorporate a skill into the formula, you can have it make up a portion of the chance, using the above example range of 0.00 to 0.75, we can make it regain the extra 0.25 using the skill value, inverted so that the higher the skill, the lower the added value;
C#:
double chanceToFail = ( ( trapLevel / (double)trapLevelMax ) * 0.75 );

chanceToFail += 1.00 - ( ( ( skillValue / skillValueMax ) * ( 1.00 - chanceToFail ) ) );

Checking the chance to fail;
C#:
if( Utility.RandomDouble() < chanceToFail )
{
    // failed
}
 
Back