RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

BaseItem & BaseMobile

Do you support this suggestion?

  • Yes

    Votes: 12 60.0%
  • No

    Votes: 4 20.0%
  • Unsure

    Votes: 4 20.0%

  • Total voters
    20
  • Poll closed .
Status
Not open for further replies.

Vorspire

Knight
Would it be possible to have a BaseItem and BaseMobile class implemented in the next release?

I can't stress enough how useful this would be and wonder why it hasn't already been done.

In all honesty, I'm sick of editing 5 different "Base" classes when it comes to modding globally and especially when I'm helping a few people set up RunUO on a weekly basis.

Since BaseItem and BaseMobile don't exist on the accessible level (in the Scripts folder) for the average developer, making global edits is an extreme pain and as developers, you must well know that using the exact same code in multiple places is hard to maintain after so long...

Please consider it; I have already written a thread discussing the implications of using BaseItem and BaseMobile, which can be found here:
http://www.runuo.com/community/threads/new-shard-owners-future-development-tips.102869/#post-844970

Thank you for your time!
 

Jeff

Lord
I honestly don't see a use for this. Creating classes like this feels like a waste when you can simple edit the core. I think it would have been useful back in the days when A. Core source was not open source, or B. Core modification was unsupported... but neither of those are issues now. If you need to edit Item or Mobile, do so, and recompile, there are plenty of guides and help here to do it, and a true server admin should know how to do this (if not learn how) anyway.
 
I put unsure: I am not a developer, but I'd rather modify directly things in classes Item or Mobile in the core than have another inheriting class.
 

patate

Sorceror
I support the idea of BaseItem.cs
I need a way to tell my HarvestSystem that any item was used so I cancel the Harvest process
There's no way i'm going to edit all the items for that
 

Vorspire

Knight
Jeff knows fine well I'm capable of simply editing the core to make edits to Item.cs, but the reason I'm asking for the BaseItem and BaseMobile is for the end user's sake...
When they want to apply features to all items, they have to edit 6 different base classes to do so.. IIRC the whole point in OOP is to reduce the amount of code required to reach the desired result and that typically means derivation of classes through a prioritized chain of inheritance, starting from the bridge between CORE and DISTRO (Scripts)

This shouldn't even be a request, it should be a standard include; RunUO is already hard enough for developers to get anything done properly when it involves the core, a lot of people would rather give up on their ambition of adding a new feature if it means modding the core.
 

MarciXs

Sorceror
What features in particular are those that every item need?
The only thing I find weird is that for instance,things like, there's a Region.OnDamage thing. Where one would suspect to collect damages done on a certain mobile. Little do you know that damager or the mobile doing the damage is registered after it is done. Same with death. if you'd check Alive property inside OnDeath you'd get the mobile is still alive. I don't see why couldn't the preventing methods be something like Region.CanDieInRegion or Region.CanDamageInRegion that would be specific for the case. And those(the ones there are already) were called when the action was actually done.
I support the idea of BaseItem.cs
I need a way to tell my HarvestSystem that any item was used so I cancel the Harvest process
There's no way i'm going to edit all the items for that
Dictionary? List?
 

Vorspire

Knight
What features in particular are those that every item need?

I use BaseItem to globally implement:
ObjectPropertyList edits
Custom Race requirements.
DoubleClick Equipping
Item Sets
Item Sets activation/deactivation
Application of special abilities attached to Doubleclick and passive events
Override and make all items movable to staff members at all times
Custom Level requirements

Inherited by:
BaseArmor
BaseClothing
BaseJewelry
BaseWeapon

The only thing I find weird is that for instance,things like, there's a Region.OnDamage thing. Where one would suspect to collect damages done on a certain mobile. Little do you know that damager or the mobile doing the damage is registered after it is done. Same with death. if you'd check Alive property inside OnDeath you'd get the mobile is still alive. I don't see why couldn't the preventing methods be something like Region.CanDieInRegion or Region.CanDamageInRegion that would be specific for the case. And those(the ones there are already) were called when the action was actually done.
You'll find that if you call the base.SomeMethod before the code you're trying to execute for your checks, that it will fix pretty much all of the problems you've outlined here, it's all about stack call priority which is generally a more difficult concept to realise when programming.

There's also a few ways around getting the damager, you access Mobile.DamageStore and call DamageStore.FindMostRecentDamager
Also, instead of a standard Mobile.Alive check, use the Mobile.CheckAlive method, it's more accurate than a single boolean property who's value is ambiguous in the current context.
Another way to get around an Alive check when using OnDamage is to calculate the WillKill outcome, which is typically a check to see if the current damage will bring the Mobile below or equal to 0 Hit Points.

Dictionary? List?
How? Why? - There is no point in giving an answer without elaborating on your reasons for suggesting the usage of those collections.
 

MarciXs

Sorceror
I use BaseItem to globally implement:
ObjectPropertyList edits
Custom Race requirements.
DoubleClick Equipping
Item Sets
Item Sets activation/deactivation
Application of special abilities attached to Doubleclick and passive events
Override and make all items movable to staff members at all times
Custom Level requirements

Inherited by:
BaseArmor
BaseClothing
BaseJewelry
BaseWeapon

You'll find that if you call the base.SomeMethod before the code you're trying to execute for your checks, that it will fix pretty much all of the problems you've outlined here, it's all about stack call priority which is generally a more difficult concept to realise when programming.

There's also a few ways around getting the damager, you access Mobile.DamageStore and call DamageStore.FindMostRecentDamager
Also, instead of a standard Mobile.Alive check, use the Mobile.CheckAlive method, it's more accurate than a single boolean property who's value is ambiguous in the current context.
Another way to get around an Alive check when using OnDamage is to calculate the WillKill outcome, which is typically a check to see if the current damage will bring the Mobile below or equal to 0 Hit Points.

How? Why? - There is no point in giving an answer without elaborating on your reasons for suggesting the usage of those collections.

No it won't fix anything. Cause when the code is at Region OnDamage it's gonna finish it before it will go back to OnDamage in Mobile. It can call base method as many times as it wants.

Yes I could(DamageEntries,FindRecent...), I did think of that. But then I started thinking. That would mean the damage was registered only on death. Or even worse I had to have a timer of some sorts? To do these checks. Sure there are different ways to do it. But I want it as simple as simple you want your features.
So, yeah that's actually I ended up using, the OnDamage in Mobile that has the boolean of willKill. Good thing I use my own Mobiles. So no mods(distro/core). I'm actually quite curious as to how you are registering damage on your PVP system(at least I read you were).
Anyway, actually it would just do fine if the Region would accept also the damager Mobile as an argument. Would make it very easy to have specific mobiles that have better damage chance at certain regions. Sure, there is a way around that but I mean, honestly, why couldn't the method pass to Region who is getting damage and who is doing it...
Anyway.
Nope, CheckAlive just returns property of Alive which is what I checked. The only difference is it sends the message overhead. You are dead blah blah.
The problem is that Mobile when Killed gets all the items taken etc. Then it goes into Region and is apparently dead but, it is not because we consider mobile dead when it is a ghost. And it becomes ghost only in the next method after the Region.OnDeath which is Mobile.OnDeath. Why couldn't there just be a method, CanDie .. and the Region OnDeath called at the very end. I mean the Mobile is dead, it's over.

As for your features, I think some of them could have been implemented without being implemented on every Item level. That's my opinion anyway.

As for List/Dictionary. I thought he wanted to let his harvest system know which items were used, then i re-read and realized that he wanted to basically prevent that from being done.
Do it in the BaseRegion.
Region.OnDoubleClick( mobile, item )

your best bet I believe.
 

patate

Sorceror
Why is a Region he controller of items? I don't find this intuitive at all.
I went for the BaseItem.cs strategy and I feel I saved time instead of searching around all scripts to find who's the boss :p
 

Vorspire

Knight
All fair point MarciXs - but I've still not run into the problems you're facing although you'v got me thinking now about tracking damage done, if it's actually working properly (it seems like it is though)

You talk about it being annoying that region does not supply the person doing the damage OnDamage, I totally agree, but there's an even more pressing issue, why the hell is there absolutely NO WAY what-so-ever to track beneficial actions on targets, like healing - How much healing was done, who healed and who was the target, it's impossible to get all of that info without modifying the structure of RunUO itself. DamageStore - cool, we need a HealStore too!

My PvP system runs on Regions, the battle region propagates events upwards into the battle class to be filtered and handled uniquely.
This is my AutoPVP_Region script, maybe you'll fin it useful, but it outlines for the most part how to achieve what we're discussing.

Rich (BB code):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;

using Server;
using Server.Mobiles;
using Server.Regions;
using Server.Targeting;
using Server.Items;

namespace Server.AutoPVP
{
	public class AutoPVP_RegionPreviewTile : Static
	{
		public override bool DisplayLootType { get { return false; } }
		public override bool DisplayWeight { get { return false; } }
		public override double DefaultWeight { get { return 0; } }
		public override bool Decays { get { return true; } }
		public override TimeSpan DecayTime { get { return TimeSpan.FromMinutes(1.0); } }

		public AutoPVP_RegionPreviewTile(string name, int hue)
			: base(9272, 1)
		{
			Movable = false;
			Name = name;
			Hue = hue;
		}

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

		public override void Serialize(GenericWriter writer)
		{ }

		public override void Deserialize(GenericReader reader)
		{ Delete(); }
	}

	public class AutoPVP_RegionParams : IEnumerable
	{
		private object[] _Args;
		public object[] Args { get { return _Args; } set { _Args = (value != null) ? value : new object[0]; } }

		public AutoPVP_RegionParams(params object[] args)
		{
			if (args == null)
			{ args = new object[0]; }

			_Args = args;
		}

		public object this[int index]
		{
			get
			{
				if (index < 0 || index >= _Args.Length)
				{ return null; }

				return _Args[index];
			}
			set
			{
				if (index < 0 || index >= _Args.Length)
				{ return; }

				_Args[index] = value;
			}
		}

		public Dictionary<int, T> Select<T>()
		{
			Dictionary<int, T> list = new Dictionary<int, T>();

			for (int index = 0; index < _Args.Length; index++)
			{
				if (_Args[index] != null && _Args[index] is T)
				{ list.Add(index, (T)_Args[index]); }
			}

			return list;
		}

		public T Get<T>(int index)
		{
			if (index < 0 || index >= _Args.Length)
			{ return default(T); }

			if (_Args[index] != null && _Args[index] is T)
			{ return (T)_Args[index]; }

			return default(T);
		}

		public void Set(int index, object value)
		{
			if (index < 0 || index >= _Args.Length)
			{ return; }

			_Args[index] = value;
		}

		public int Add(object value)
		{
			List<object> args = new List<object>(_Args);
			args.Add(value);

			int index = args.IndexOf(value);

			_Args = args.ToArray();
			args.Clear();

			return index;
		}

		public void RemoveAt(int index)
		{
			if (index < 0 || index >= _Args.Length)
			{ return; }

			List<object> args = new List<object>(_Args);

			args.RemoveAt(index);
			_Args = args.ToArray();
			args.Clear();
		}

		public IEnumerator GetEnumerator()
		{ return _Args.GetEnumerator(); }
	}

	[PropertyObject]
	public class AutoPVP_Region : BaseRegion
	{
		private static Dictionary<AutoPVP_Battle, Dictionary<Point3D, AutoPVP_RegionPreviewTile>>
			_PreviewTiles = new Dictionary<AutoPVP_Battle, Dictionary<Point3D, AutoPVP_RegionPreviewTile>>();

		public static void DisplayPreview(AutoPVP_Battle battle)
		{
			if (!_PreviewTiles.ContainsKey(battle))
			{ _PreviewTiles.Add(battle, new Dictionary<Point3D, AutoPVP_RegionPreviewTile>()); }

			int hueBase = 11, hueJump = 11;

			try
			{
				for (int index = 0; index < battle.Options.Locations.Bounds.Count; index++)
				{
					Rectangle3D rect = battle.Options.Locations.Bounds[index];
					Point3D
						start = rect.Start,
						end = rect.End;

					int area = (rect.Width * rect.Height);

					if (area > 1024)
					{
						for (int w = 0; w < rect.Width; w++)
						{
							AddPreviewTile(battle, hueBase, hueJump, index, start.X + w, start.Y);
							AddPreviewTile(battle, hueBase, hueJump, index, end.X - w, end.Y);
						}

						for (int h = 0; h < rect.Height; h++)
						{
							AddPreviewTile(battle, hueBase, hueJump, index, start.X, start.Y + h);
							AddPreviewTile(battle, hueBase, hueJump, index, end.X, end.Y - h);
						}
					}
					else
					{
						for (int x = start.X; x < end.X; x++)
						{
							for (int y = start.Y; y < end.Y; y++)
							{ AddPreviewTile(battle, hueBase, hueJump, index, x, y); }
						}
					}

					battle.Ticker.AddHandler(new AutoPVP_BattleTickerHandler(delegate(AutoPVP_BattleTickerHandlerState state)
					{
						if (index < 0 || index >= battle.Options.Locations.Bounds.Count)
						{ return true; }

						Rectangle3D subRect = battle.Options.Locations.Bounds[index];
						int subArea = (rect.Width * rect.Height);

						if (subArea == area && subRect.Start == rect.Start && subRect.End == rect.End)
						{ ClearPreview(battle, index); }

						return true;
					}), battle.Ticker.InternalCounter + (area / 60));
				}
			}
			catch { }
		}

		public static Point3D GetSurfaceTop(Point3D p, Map map)
		{
			Point3D point = new Point3D(p.X, p.Y, map.GetAverageZ(p.X, p.Y));

			object o = map.GetTopSurface(p);

			if (o != null)
			{
				if (o is LandTile)
				{ point.Z = ((LandTile)o).Z + ((LandTile)o).Height; }
				else if (o is StaticTile)
				{ point.Z = ((StaticTile)o).Z + ((StaticTile)o).Height; }
				else if (o is Item)
				{ point = ((Item)o).GetSurfaceTop(); }
			}

			return point;
		}

		private static void AddPreviewTile(AutoPVP_Battle battle, int hueBase, int hueJump, int index, int x, int y)
		{
			Point3D p = new Point3D(x, y, 0);
			AutoPVP_RegionPreviewTile tile;

			if (!_PreviewTiles[battle].ContainsKey(p))
			{
				tile = new AutoPVP_RegionPreviewTile(AutoPVP_System.GetMessage(41, battle.GameRegionName), hueBase + (hueJump * index));
				_PreviewTiles[battle].Add(p, tile);
			}
			else
			{
				tile = _PreviewTiles[battle][p];
				tile.Hue = hueBase + (hueJump * index);
			}

			tile.MoveToWorld(GetSurfaceTop(p, battle.Options.Locations.BattleMap), battle.Options.Locations.BattleMap);
		}

		public static void ClearPreview(AutoPVP_Battle battle)
		{
			if (!_PreviewTiles.ContainsKey(battle))
			{ return; }

			try
			{
				for (int index = 0; index < battle.Options.Locations.Bounds.Count; index++)
				{ ClearPreview(battle, index); }
			}
			catch { }
		}

		public static void ClearPreview(AutoPVP_Battle battle, int index)
		{
			if (!_PreviewTiles.ContainsKey(battle))
			{ return; }

			try
			{
				Rectangle3D rect = battle.Options.Locations.Bounds[index];
				Point3D
					start = rect.Start,
					end = rect.End;

				for (int x = start.X; x < end.X; x++)
				{
					for (int y = start.Y; y < end.Y; y++)
					{
						Point3D p = new Point3D(x, y, 0);

						if (_PreviewTiles[battle].ContainsKey(p))
						{
							_PreviewTiles[battle][p].Delete();
							_PreviewTiles[battle].Remove(p);
						}
					}
				}
			}
			catch { }
		}

		public static Rectangle3D[] AreaHotFix(params Rectangle3D[] rects)
		{
			if (rects == null)
			{ return new Rectangle3D[0]; }

			List<Rectangle3D> fixedRects = new List<Rectangle3D>(rects.Length);

			foreach (Rectangle3D orig in rects)
			{
				Point3D start = orig.Start, end = orig.End;
				start.Z = MinZ;
				end.Z = MaxZ;
				fixedRects.Add(new Rectangle3D(start, end));
			}

			return fixedRects.ToArray();
		}

		public static bool Contains(Sector s, Point3D p)
		{
			foreach (RegionRect rRect in s.RegionRects)
			{
				if (Contains(rRect.Rect, p))
				{ return true; }
			}

			return false;
		}

		public static bool Contains(Region r, Point3D p)
		{ return Contains(r.Area, p); }

		public static bool Contains(List<Rectangle3D> rects, Point3D p)
		{ return Contains(rects.ToArray(), p); }

		public static bool Contains(Rectangle3D[] rects, Point3D p)
		{
			foreach (Rectangle3D rect in rects)
			{
				if (Contains(rect, p))
				{ return true; }
			}

			return false;
		}

		public static bool Contains(Rectangle3D rect, Point3D p)
		{ return (p.X >= rect.Start.X && p.Y >= rect.Start.Y && p.X <= rect.End.X && p.Y <= rect.End.Y); }

		private AutoPVP_Battle _Battle;
		public AutoPVP_Battle Battle { get { return _Battle; } }

		private AutoPVP_RegionParams _Args = new AutoPVP_RegionParams();
		public AutoPVP_RegionParams Args { get { return _Args; } }

		public AutoPVP_Region(AutoPVP_Battle battle, AutoPVP_RegionParams args)
			: base(battle.GameRegionName, battle.Options.Locations.BattleMap, battle.Options.Locations.BattlePriority, AreaHotFix(battle.Options.Locations.Bounds.ToArray()))
		{
			_Battle = battle;

			if (args != null)
			{ _Args = args; }
		}

		public AutoPVP_Region(AutoPVP_Battle battle, params object[] args)
			: this(battle, new AutoPVP_RegionParams(args))
		{ }

		public void MicroSync()
		{
			if (_Battle == null)
			{ return; }

			OnMicroSync();
		}

		public virtual void OnMicroSync()
		{ }

		public virtual void Serialize(GenericWriter writer)
		{
			int version = 0;
			writer.Write(version); //Version

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

		public virtual void Deserialize(GenericReader reader)
		{
			int version = reader.ReadInt();

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

		public override bool AllowBeneficial(Mobile from, Mobile target)
		{
			if (_Battle != null)
			{ return _Battle.AllowBeneficial(from, target); }

			return base.AllowBeneficial(from, target);
		}

		public override bool AllowHarmful(Mobile from, Mobile target)
		{
			if (_Battle != null)
			{ return _Battle.AllowHarmful(from, target); }

			return base.AllowHarmful(from, target);
		}

		public override bool AllowHousing(Mobile from, Point3D p)
		{
			if (_Battle != null)
			{ return _Battle.AllowHousing(from, p); }

			return base.AllowHousing(from, p);
		}

		public override bool AllowSpawn()
		{
			if (_Battle != null)
			{ return _Battle.AllowSpawn(); }

			return base.AllowSpawn();
		}

		public override bool CanUseStuckMenu(Mobile m)
		{
			if (_Battle != null)
			{ return _Battle.CanUseStuckMenu(m); }

			return base.CanUseStuckMenu(m);
		}

		public override void OnAggressed(Mobile aggressor, Mobile aggressed, bool criminal)
		{
			if (_Battle != null)
			{
				_Battle.OnAggressed(aggressor, aggressed, criminal);
				return;
			}

			base.OnAggressed(aggressor, aggressed, criminal);
		}

		public override bool AcceptsSpawnsFrom(Region region)
		{
			if (_Battle != null)
			{ return _Battle.AcceptsSpawnsFrom(region); }

			return base.AcceptsSpawnsFrom(region);
		}

		public override void AlterLightLevel(Mobile m, ref int global, ref int personal)
		{
			if (_Battle != null)
			{
				_Battle.AlterLightLevel(m, ref global, ref personal);
				return;
			}

			base.AlterLightLevel(m, ref global, ref personal);
		}

		public override bool CheckAccessibility(Item item, Mobile from)
		{
			if (_Battle != null)
			{ return _Battle.CheckAccessibility(item, from); }

			return base.CheckAccessibility(item, from);
		}

		public override TimeSpan GetLogoutDelay(Mobile m)
		{
			if (_Battle != null)
			{ return _Battle.GetLogoutDelay(m); }

			return base.GetLogoutDelay(m);
		}

		public override bool OnDecay(Item item)
		{
			if (_Battle != null)
			{ return _Battle.OnDecay(item); }

			return base.OnDecay(item);
		}

		public override bool OnSingleClick(Mobile m, object o)
		{
			if (_Battle != null)
			{ return _Battle.OnSingleClick(m, o); }

			return base.OnSingleClick(m, o);
		}

		public override bool OnBeforeDeath(Mobile m)
		{
			if (_Battle != null)
			{ return _Battle.OnBeforeDeath(m); }

			return base.OnBeforeDeath(m);
		}

		public override bool OnBeginSpellCast(Mobile m, ISpell s)
		{
			if (_Battle != null)
			{ return _Battle.OnBeginSpellCast(m, s); }

			return base.OnBeginSpellCast(m, s);
		}

		public override void OnBeneficialAction(Mobile helper, Mobile target)
		{
			if (_Battle != null)
			{
				_Battle.OnBeneficialAction(helper, target);
				return;
			}

			base.OnBeneficialAction(helper, target);
		}

		public override bool OnCombatantChange(Mobile m, Mobile oldMob, Mobile newMob)
		{
			if (_Battle != null)
			{ return _Battle.OnCombatantChange(m, oldMob, newMob); }

			return base.OnCombatantChange(m, oldMob, newMob);
		}

		public override void OnCriminalAction(Mobile m, bool message)
		{
			if (_Battle != null)
			{
				_Battle.OnCriminalAction(m, message);
				return;
			}

			base.OnCriminalAction(m, message);
		}

		public override bool OnDamage(Mobile m, ref int damage)
		{
			if (_Battle != null)
			{ return _Battle.OnDamage(m.FindMostRecentDamager(true), m, ref damage); }

			return base.OnDamage(m, ref damage);
		}

		public override void OnDeath(Mobile m)
		{
			if (_Battle != null)
			{
				_Battle.OnDeath(m);
				return;
			}

			base.OnDeath(m);
		}

		public override void OnDidHarmful(Mobile harmer, Mobile harmed)
		{
			if (_Battle != null)
			{
				_Battle.OnDidHarmful(harmer, harmed);
				return;
			}

			base.OnDidHarmful(harmer, harmed);
		}

		public override bool OnDoubleClick(Mobile m, object o)
		{
			if (_Battle != null)
			{ return _Battle.OnDoubleClick(m, o); }

			return base.OnDoubleClick(m, o);
		}

		public override void OnEnter(Mobile m)
		{
			if (_Battle != null)
			{
				_Battle.OnEnter(m);
				return;
			}

			base.OnEnter(m);
		}

		public override void OnExit(Mobile m)
		{
			if (_Battle != null)
			{
				_Battle.OnExit(m);
				return;
			}

			base.OnExit(m);
		}

		public override void OnGotBeneficialAction(Mobile helper, Mobile target)
		{
			if (_Battle != null)
			{
				_Battle.OnGotBeneficialAction(helper, target);
				return;
			}

			base.OnGotBeneficialAction(helper, target);
		}

		public override void OnGotHarmful(Mobile harmer, Mobile harmed)
		{
			if (_Battle != null)
			{
				_Battle.OnGotHarmful(harmer, harmed);
				return;
			}

			base.OnGotHarmful(harmer, harmed);
		}

		public override bool OnHeal(Mobile m, ref int heal)
		{
			if (_Battle != null)
			{
				Mobile from = null;
				return _Battle.OnHeal(from, m, ref heal);
			}

			return base.OnHeal(m, ref heal);
		}

		public override void OnLocationChanged(Mobile m, Point3D oldLocation)
		{
			if (_Battle != null)
			{
				_Battle.OnLocationChanged(m, oldLocation);
				return;
			}

			base.OnLocationChanged(m, oldLocation);
		}

		public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
		{
			if (_Battle != null)
			{ return _Battle.OnMoveInto(m, d, newLocation, oldLocation); }

			return base.OnMoveInto(m, d, newLocation, oldLocation);
		}

		public override bool OnResurrect(Mobile m)
		{
			if (_Battle != null)
			{ return _Battle.OnResurrect(m); }

			return base.OnResurrect(m);
		}

		public override bool OnSkillUse(Mobile m, int skill)
		{
			if (_Battle != null)
			{ return _Battle.OnSkillUse(m, skill); }

			return base.OnSkillUse(m, skill);
		}

		public override void OnSpeech(SpeechEventArgs args)
		{
			if (_Battle != null)
			{
				_Battle.OnSpeech(args);
				return;
			}

			base.OnSpeech(args);
		}

		public override void OnSpellCast(Mobile m, ISpell s)
		{
			if (_Battle != null)
			{
				_Battle.OnSpellCast(m, s);
				return;
			}

			base.OnSpellCast(m, s);
		}

		public override bool OnTarget(Mobile m, Target t, object o)
		{
			if (_Battle != null)
			{ return _Battle.OnTarget(m, t, o); }

			return base.OnTarget(m, t, o);
		}

		public override void SpellDamageScalar(Mobile caster, Mobile target, ref double damage)
		{
			if (_Battle != null)
			{
				_Battle.SpellDamageScalar(caster, target, ref damage);
				return;
			}

			base.SpellDamageScalar(caster, target, ref damage);
		}
	}
}

OnDamage seems to work fine, but I'm going to double-test.
Notice the OnHeal method... The intentional setting of the reference to null, until RunUO provides the API for me to fix that, it will always be like that, making it impossible to track healing properly as a statistic.

The specific calls to the battle class methods of interest:
Rich (BB code):
		public bool OnBeforeDeath(Mobile m)
		{
			if (m == null || m.Deleted)
			{ return false; }

			if (CheckDeath(m))
			{
				OnDeathAccept(m);
				return true;
			}

			OnDeathDeny(m);
			return false;
		}

		public virtual bool CheckDeath(Mobile m)
		{ return Options.Rules.CanDie; }

		public virtual void OnDeathAccept(Mobile m)
		{
			if (m == null || m.Deleted)
			{ return; }

			if (m is PlayerMobile)
			{
				PlayerMobile pm = (PlayerMobile)m;

				if (IsParticipant(pm))
				{ FindTeam(pm).OnMemberDeath(pm); }
			}
		}

		public virtual void OnDeathDeny(Mobile m)
		{
			if (m == null || m.Deleted)
			{ return; }

			if (m is PlayerMobile)
			{
				PlayerMobile pm = (PlayerMobile)m;

				EnsureStatistics(pm).Deaths++;

				if (_PointsOnKill)
				{ RevokePoints(pm); }

				Broadcast(256, pm.RawName);

				Mobile killer = pm.FindMostRecentDamager(false);

				if (killer is PlayerMobile)
				{
					PlayerMobile killerPM = (PlayerMobile)killer;

					if (killerPM != null && !killerPM.Deleted && IsParticipant(killerPM))
					{
						pm.LastKiller = killerPM;
						EnsureStatistics(killerPM).Kills++;

						if (_PointsOnKill)
						{ AwardPoints(killerPM); }
					}
				}

				Refresh(pm, true);

				AutoPVP_Team team = FindTeam(pm);

				if (team != null && !team.Deleted)
				{
					OnLose(pm);

					if (team.RemoveMember(pm, false))
					{ AddSpectator(pm, true); }
				}
			}

			m.SendMessage(AutoPVP_System.GetMessage(m, 219));
		}

Rich (BB code):
		public bool OnDamage(Mobile from, Mobile damaged, ref int damage)
		{
			if (CheckDamage(from, damaged, ref damage))
			{
				OnDamageAccept(from, damaged, ref damage);
				return true;
			}

			OnDamageDeny(from, damaged, ref damage);
			return false;
		}

		public virtual bool CheckDamage(Mobile from, Mobile damaged, ref int damage)
		{ return _Options.Rules.CanBeDamaged; }

		public virtual void OnDamageAccept(Mobile from, Mobile damaged, ref int damage)
		{
			if (damage <= 0)
			{ return; }

			if (from != null && !from.Deleted)
			{
				if (from is PlayerMobile)
				{
					PlayerMobile pm = (PlayerMobile)from;

					if (IsParticipant(pm))
					{ EnsureStatistics(pm).DamageDone += damage; }
				}
			}

			if (damaged != null && !damaged.Deleted)
			{
				if (damaged is PlayerMobile)
				{
					PlayerMobile pm = (PlayerMobile)damaged;

					if (IsParticipant(pm))
					{ EnsureStatistics(pm).DamageTaken += damage; }
				}
			}
		}

		public virtual void OnDamageDeny(Mobile from, Mobile damaged, ref int damage)
		{
			if (damaged == null || damaged.Deleted || damage <= 0)
			{ return; }

			damaged.SendMessage(AutoPVP_System.GetMessage(damaged, 222));
		}

Rich (BB code):
		public bool OnHeal(Mobile from, Mobile healed, ref int heal)
		{
			if (CheckHeal(from, healed, ref heal))
			{
				OnHealAccept(from, healed, ref heal);
				return true;
			}

			OnHealDeny(from, healed, ref heal);
			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="from">CONSTANT NULL</param>
		public virtual bool CheckHeal(Mobile from, Mobile healed, ref int heal)
		{ return _Options.Rules.CanHeal; }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="from">CONSTANT NULL</param>
		public virtual void OnHealAccept(Mobile from, Mobile healed, ref int heal)
		{
			if (heal <= 0)
			{ return; }

			if (from != null && !from.Deleted)
			{
				if (from is PlayerMobile)
				{
					PlayerMobile pm = (PlayerMobile)from;

					if (IsParticipant(pm))
					{ EnsureStatistics(pm).HealingDone += heal; }
				}
			}

			if (healed != null && !healed.Deleted)
			{
				if (healed is PlayerMobile)
				{
					PlayerMobile pm = (PlayerMobile)healed;

					if (IsParticipant(pm))
					{ EnsureStatistics(pm).HealingTaken += heal; }
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="from">CONSTANT NULL</param>
		public virtual void OnHealDeny(Mobile from, Mobile healed, ref int heal)
		{
			if (healed == null || healed.Deleted || heal <= 0)
			{ return; }

			healed.SendMessage(AutoPVP_System.GetMessage(healed, 223));
		}

Logically the mobile should be considered dead when within the scope of the OnDeath method, it's not necessary to perform Alive checks inside that method IMO.
The actual death logic is handled with OnBeforeDeath.
 

MarciXs

Sorceror
Why is a Region he controller of items? I don't find this intuitive at all.
I went for the BaseItem.cs strategy and I feel I saved time instead of searching around all scripts to find who's the boss :p
It's so that you can have Regions where you can't use items. I suggested to do it in Region it as it wouldn't need any core mods.

AS for OnDamage if you look at the code
Code:
public virtual void Damage( int amount, Mobile from, bool informMount )
{
if( !CanBeDamaged() || m_Deleted )
return;

if( !this.Region.OnDamage( this, ref amount ) )  // This is where one would register the damage done
return;

if( amount > 0 )
{
int oldHits = Hits;
int newHits = oldHits - amount;

if( m_Spell != null )
m_Spell.OnCasterHurt();

//if ( m_Spell != null && m_Spell.State == SpellState.Casting )
//m_Spell.Disturb( DisturbType.Hurt, false, true );

if( from != null )
RegisterDamage( amount, from ); // But this is only when the damager/attacker is added to the DamageEntries

You are doing the way I wanted to do it. But when you look at the code you can see that damage registering happens only after the Region OnDamage which means m.FindMostRecentDamager(true) Wouldn't have the current attacker/damager registered in it.
 

Vorspire

Knight
Hmm, then that means that the first damage done by one player to another is never recorded until the next damage done, which technically means that the last damage done never gets registered.
I don't mind the small inaccuracy, I only use it for collecting statistics to display in the PvP Profile gump.
It's entirely possible to defer the code until the damage is registered though, or if you know your Region.OnDamage override is going to return true, you could call RegisterDamage independantly (if it's access permits it)
 

MarciXs

Sorceror
Hmm, then that means that the first damage done by one player to another is never recorded until the next damage done, which technically means that the last damage done never gets registered.
I don't mind the small inaccuracy, I only use it for collecting statistics to display in the PvP Profile gump.
It's entirely possible to defer the code until the damage is registered though, or if you know your Region.OnDamage override is going to return true, you could call RegisterDamage independantly (if it's access permits it)
Yeah but, say the player damages himself, then the damage points that should have gone to the player attacking him would go to the player himself as he would be the last one to damage himself at that time.
It might be not too bad on 1vs1 but as soon as there are more than one player the damage points will get quite mixed up.
You could call RegisterDamage, but how will you know who attacked?
 

Vorspire

Knight
Yeah but, say the player damages himself, then the damage points that should have gone to the player attacking him would go to the player himself as he would be the last one to damage himself at that time.
It might be not too bad on 1vs1 but as soon as there are more than one player the damage points will get quite mixed up.
You could call RegisterDamage, but how will you know who attacked?

That's the cool thing, the FindMostrecentDamager accepts a boolean includeSelf argument, I call it with false, so it doesn't count for damage done to themselves.
The thing is, PandoraUO is running my PvP system right now and one of the battles is a free for all of up to 40 players, the damage statistics seem to be working fine, but it could be the illusion pertaining to my previous statement about deferred damage detection.

(I'm still pissy about there not being a HealStore)
 

Kukkolka

Sorceror
I used BaseMobile and BaseItem for my server. I found it much easier then to edit then the core with what by now would have been 2 million lines of code.
I use BaseMobile to globally implement:
  • Feat tests and special abilities to players and creatures a like
  • experience and levels requirements and some gains for players and creatures
  • core skill functionaly tests
  • LOS, Hide, Search, Spot & Listen checks
  • enforce 3 more stats like Constitution, Wisdom, Charisma
  • Magical effects
  • Sleeping
  • Spell lists
  • Fingerprinting and tracks leaving
  • Flying
  • much more
I use BaseItem to globally implement:

  • Item linking between each other
  • doubleclick functionalities
  • Arcane Markings
  • Quest linkings
  • XmlSpawner linking
  • Custom magical properties
  • Custom magical spell attachments
  • Market prices
  • fingerprinting
  • hiding and stashing functions
  • detection and collision tests
  • much more
 

MarciXs

Sorceror
I used BaseMobile and BaseItem for my server. I found it much easier then to edit then the core with what by now would have been 2 million lines of code.
I use BaseMobile to globally implement:
  • Feat tests and special abilities to players and creatures a like
  • experience and levels requirements and some gains for players and creatures
  • core skill functionaly tests
  • LOS, Hide, Search, Spot & Listen checks
  • enforce 3 more stats like Constitution, Wisdom, Charisma
  • Magical effects
  • Sleeping
  • Spell lists
  • Fingerprinting and tracks leaving
  • Flying
  • much more
I use BaseItem to globally implement:

  • Item linking between each other
  • doubleclick functionalities
  • Arcane Markings
  • Quest linkings
  • XmlSpawner linking
  • Custom magical properties
  • Custom magical spell attachments
  • Market prices
  • fingerprinting
  • hiding and stashing functions
  • detection and collision tests
  • much more

Why would you implement flying on base level? Just curious...
 

MarciXs

Sorceror
I'd do it to make every BaseMobile child capable of flight, there's no other good reason really :)
Yeah but I can't see a point in that. Is it so it's "easier" to make changes later on? I would rather create BaseFlyingCreature. Out of which I could have something like SlowBaseFlyingCreature and FastBaseFlyingCreature etc. Then accordingly Bird would go out of SlowBaseFlyingCreature, while something like Hawk could be FastBaseFlyingCreature.

I mean for me it is much easier that way. Maybe it's a preference I dunno. I just can't see the point in loading stuff into base classes. I mean even the Stream class doesn't have all the stuff the NetworkStream needs or MemoryStream.

But it's his way of coding I suppose.
 
Status
Not open for further replies.
Top