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!

[RunUO 2.0 RC1] XmlMobFactions - custom creature faction system

ArteGordon

Wanderer
[RunUO 2.0 RC1] XmlMobFactions - custom creature faction system

XmlMobFactions
v1.06a
RunUO 2.0 version
updated 6/30/06
ArteGordon

Latest News:
Version 1.06 has been updated to run under RunUO 2.0.

Summary:

A system that keeps track of a players faction with user-definable mob groups. Faction can be gained and lost by killing mobs that are allied or opposed to each faction group. Additional scripts that are included provide additional features such as giving out faction for completing quests, making equipment that requires faction to equip, or weapons that have damage bonuses based upon faction levels with certain groups. By following the recommended installation steps, additional faction-dependent features can be added such as making mob taming, control, and how long it takes for mobs to acquire a player as a target dependent on faction standing with a given mobs group.
Also supports mob faction rewards that can be purchased using mob kill Credits.


Recent Updates

New to version 1.06a
updated 6/30/05

- updated MobFactionRegionStone.cs with a small fix for a deserialization problem with MobFactionRegions.


New to version 1.06
update 2/1/05

- added a new MobFactionRegion feature that allows you to define a region which restricts entry based upon a minimum mob faction level. This could be placed in front of dungeons, or buildings, for example, to limit access to those with sufficient mob faction.
Players that try to recall, gate, or teleport into the region will be automatically ejected to a location that can be specified via props. By default, this location is the location of the MobFactionRegionStone that controls the region, but it can be set to anywhere.
Desired faction type and faction level can be set via [props along with some other properties.
Note, if this is placed within existing regions, you may have to raise the MobFactionRegion priority via the MobFactionRegionStone in order for it to function.

- added a MobFactionRegionStone that allows staff to define MobFactionRegions. It can either be setup as a simple single rectangular region by double clicking the flag and defining a bounding box, or you can give it the name of an existing region in the CopyRegion property and it will copy that regions bounding areas. That way you can use other tools to make more complicated regions and then just put the MobFactionRegion on top of them. Note, this doesnt have any effect on the region that you copy.
For example, if you wanted to make the Britain Graveyard accessible only to players with sufficient Undead faction, you would just set the CopyRegion to "Britain Graveyard", the FactionType to "Undead", and the FactionLevel to whatever you wanted.

New to Version 1.05a
update 1/29/05

- added optional installation step 8 that allows you to make barding dependent on mob faction.

New to Version 1.05
updated 1/22/05

- updated the installation instructions for 1.0.0



Description:

This system makes use of the XmlSpawner2 attachment system and the XmlSpawner2 package must be installed to support it. You dont need to use the xmlspawners themselves, and it doesnt matter whether you use the standard distribution spawners or any other spawning system but you do need the XmlSpawner2 package installed.


Because the attachment system neither requires nor makes any changes to serialization/deserializations on any item or mobile, it can be safely added and removed at any time, now or in the future, without interfering with any other systems that you might have installed.

Note, this has nothing to do with the upcoming release of the official player faction system and will not interfere with that system in any way.

Commands supported:

[addallmobfactions - this command will add the mob factions attachment to all current players and can only be run by an administrator.

[removeallmobfactions - this command will remove the mob factions attachment from all current players and can only be run by an administrator.

[checkmobfactions - this command can be run by players to report their current standing with all existing mob factions (this is the same as using the item identification skill on themselves if the optional installation step 8 of the XmlSpawner2 package has been followed).

[verbosemobfactions true/false - this command can be run by players to toggle the verbose reporting of faction gained and lost for the mobs they kill.


The system comes configured with a set of default factions based around the slayer opponent groups with some modifications. These include

Player,
Humanoid,
Undead,
Reptilian,
Arachnid,
Elemental,
Abyss,
DragonLords,
NecroMasters,
Fairie,
Plant,
Underworld


Killing a mob that belongs to one of these factions will cause a loss in faction with that group and its allies (the amount can be scaled for each ally), and a gain in faction with the groups opponents (the amount can be scaled for each opponent).
Additionally, factions such as the DragonLords or NecroMasters which dont have any default members can be set up as quest factions, or can have members dynamically assigned by using the XmlDynamicFaction attachment to assign/spawn a mob into that faction.

For example, here are some of the ally/opponent relationships for a few of the default groups

Player: Allies=Player, Fairies: Opponents=Arachnid, Humanoid, Undead, Reptilian, Elemental, Abyss
Humanoid: Allies=Humanoid: Opponents=Undead, Player, Plant
Undead: Allies=Undead, Abyss: Opponents=Humanoid, Player, Fairie

The groups, their members, allies, and opponents can be configured in the XmlMobFactions.cs file and new factions, such as the Fairie, and Plant factions can be easily added into the faction group definitions in that script. The groups also do not have to remain static. They can be changed and new factions can be added transparently at any time after the system is installed and they will be automatically reflected in each players faction list.


Installation:
For RunUO 2.0

STEP 1:
Install the latest XmlSpawner2 package. You can find it here XmlSpawner2. Make sure that you perform installation steps 2 and 6, and optionally steps 8 and 9, from that thread. If you decide not to perform XmlSpawner2 installation step 9, everything in the system will work except for the XmlFactionEquip attachment. If you decide not to perform XmlSpawner2 installation step 8, everything in the system will work except for the ability to use the Item Identification skill on yourself to open your faction window or to view any faction-dependent bonuses on items.

STEP 2:
Place the scripts in this package into your custom scripts directory, or into the XmlAttachments directory of your XmlSpawner2 installation. Place the optional .xml examples in the top level of your RunUO installation directory.

STEP 3:
Add mob faction support to all existing players while ingame using the command "[addallmobfactions". If you change your mind and decide to remove it, you can use the "[removeallmobfactions" command. If you would just like to try it out on one player you can use the "[addatt xmlmobfactions" command and target a player.

STEP 4:
To automatically add mob faction support to newly created characters, add the following line to the end of the EventSink_CharacterCreated method in CharacterCreation.cs

around line 709 of Scripts/Misc/CharacterCreation.cs change
Code:
	new WelcomeTimer( newChar ).Start();
to
Code:
	new WelcomeTimer( newChar ).Start();

	// mod to attach the MobFactions attachment automatically to new chars
	XmlAttach.AttachTo(newChar, new XmlMobFactions());
also at the top of the file add the line
Code:
using Server.Engines.XmlSpawner2;

STEP 5: (optional)
o make the speed with which mobs acquire players as combat targets depend on the players faction with that mobs group, make the following changes

around line 2471 of Scripts/Engines/AI/AI/BaseAI.cs in the AcquireFocusMobmethod, change

Code:
		// Can't acquire a target we can't see.
		if ( !m_Mobile.CanSee( m ) )
						continue;
to
Code:
		// Can't acquire a target we can't see.
		if ( !m_Mobile.CanSee( m ) )
						continue;

		// Mob faction dependent target acquisition
		if (!Server.Engines.XmlSpawner2.XmlMobFactions.CheckAcquire(m_Mobile, m))
			continue;

around line 4236 of Scripts/Engines/AI/Creature/BaseCreature.cs change

public virtual TimeSpan ReacquireDelay{ get{ return TimeSpan.FromSeconds( 10.0 ); } }

to
Code:
	public virtual TimeSpan ReacquireDelay{ get{ return TimeSpan.FromSeconds( 2.0 ); } }
You set it to whatever you like, but 2 seconds is a good value to start with. See the CheckAcquire method XmlMobFactions.cs for more details.

and at the top of the baseai.cs and basecreature.cs files add
Code:
using Server.Engines.XmlSpawner2;

STEP 6: (optional)
To make the chance of controlling a creature depend on the players faction with that creatures group, make the following change to BaseCreature.cs

around line 832 of Scripts/Engines/AI/Creature/BaseCreature.cs at the end of the GetControlChance method, change

Code:
	return ( (double)chance / 1000 );
to
Code:
	// faction mod for mob control.  This will add +- 25% chance for control over a range of +-25K faction.
	// using a positive scaling factor means that positive faction will increase control chance
	chance += (int)XmlMobFactions.GetScaledFaction(m, this, -250, 250, 0.001);

	return ( (double)chance / 1000 );

and at the top of the file add
Code:
using Server.Engines.XmlSpawner2;

STEP 7: (optional)
To make the chance of taming a creature depend on the players faction with that creatures group, make the following change to AnimalTaming.cs

around line 323 of Scripts/Skills/AnimalTaming.cs in the OnTick method, change
Code:
	minSkill += 24.9;
to
Code:
	minSkill += 24.9;
						
	// faction mod for taming change.  This will modify min taming skill required by +-25 over a range of +-25K faction.
	// using a negative scaling factor means that positive faction will reduce skill requirement
	minSkill += XmlMobFactions.GetScaledFaction(m_Tamer, m_Creature, -25, 25, -0.001);

and at the top of the file add
Code:
using Server.Engines.XmlSpawner2;

STEP 8: (optional)
To make the chance of barding a creature depend on the players faction with that creatures group, make the following changes to Peacemaking.cs, Discordance.cs, and Provocation.cs. You dont have to do them all if you dont want to. For instance, if you just wanted to mod peacemaking, that would be fine.

around line 148 of Scripts/Skills/Peacemaking.cs in the OnTarget method, change
Code:
	double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0;
	double music = from.Skills[SkillName.Musicianship].Value;
to
Code:
	double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0;
	double music = from.Skills[SkillName.Musicianship].Value;
							
	// Adjust min peacemaking skill required by +-25 over a range of +-25K faction.
	// using a negative scaling factor means that positive faction will reduce skill requirement (make it easier)
	diff += XmlMobFactions.GetScaledFaction(from, targ, -25, 25, -0.001);

around line 111 of Scripts/Skills/Provocation.cs in the OnTarget method, change
Code:
	double diff = ((m_Instrument.GetDifficultyFor( m_Creature ) + m_Instrument.GetDifficultyFor( creature )) * 0.5) - 5.0;
	double music = from.Skills[SkillName.Musicianship].Value;
to
Code:
	double diff = ((m_Instrument.GetDifficultyFor( m_Creature ) + m_Instrument.GetDifficultyFor( creature )) * 0.5) - 5.0;
	double music = from.Skills[SkillName.Musicianship].Value;

	// Adjust min provocation skill required by +-25 over a range of +-25K faction.
	// using a negative scaling factor means that positive faction will reduce skill requirement (make it easier)
	diff += (XmlMobFactions.GetScaledFaction(from, m_Creature, -25, 25, -0.001) + XmlMobFactions.GetScaledFaction(from, creature, -25, 25, -0.001))*0.5;

around line 145 of Scripts/Skills/Discordance.cs in the OnTarget method, change
Code:
	double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0;
	double music = from.Skills[SkillName.Musicianship].Value;
to
Code:
	double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0;
	double music = from.Skills[SkillName.Musicianship].Value;
						
	// Adjust min discordance skill required by +-25 over a range of +-25K faction.
	// using a negative scaling factor means that positive faction will reduce skill requirement (make it easier)
	diff += XmlMobFactions.GetScaledFaction(from, targ, -25, 25, -0.001);

and at the top of each of the files add
Code:
using Server.Engines.XmlSpawner2;


Changelog:

Version 1.04
updated 1/14/05
- added page scrolling buttons to the faction rewards gump.

- a few minor modifications to the default factions lists.

Version 1.03
updated 12/04/04
- minor display change on mouseover for items with XmlFactionMastery attachment. The properties will no longer attempt to display the %damage increase (which it could not do because it did not know who the player was performing the mouseover). The correct damage information will still be displayed when using the Item Identification skill.

- added a checkbox to the mob factions gump to toggle the verbosemobfactions setting that can be used instead of the "[verbosemobfactions true/false"command.

New to Version 1.02
updated 11/13/04

- modified [addallmobfactions command to ignore players that already have XmlMobFactions attachments instead of resetting them.

- added several significant performance optimizations.

- modified some of the default factions.

Version 1.01
updated 11/09/04

- added kill Credits that are added for each kill and can be use to purchase items from the MobFactionsRewardStone. The m_CreditScale factor determines how many credits are gained per kill based on the fame of the mob (default 0.1%).

- added the MobFactionsRewardStone that can be used to purchase rewards using kill Credits. Just "[add MobFactionsRewardStone to place it. Rewards can have specific faction requirements as well as Credit requirements. (see the examples of rewards in XmlMobFactionsRewards.cs)

- added the XmlMobFactionsRewards class that lets you specify the rewards their costs and optionally, minimum faction requirements. Items, Mobiles, and Attachments can be specified as rewards.

- added the static methods int XmlMobFactions.GetCredits(Mobile m), bool XmlMobFactions.TakeCredits(Mobile m, int credits), and bool XmlMobFactions.HasCredits(Mobile m, int credits) which external scripts can use to interface with the Credits system. Note, if a negative credits argument is passed to TakeCredits, it will add to the players Credit total. HasCredits and TakeCredits return a bool depending on whether player has sufficient credits.

- modified the mob factions gump to report available kill Credits.

Version 1.0
updated 11/06/04

- added the XmlMobFactions attachment. This is the main attachment that supports mob factions.

- added the XmlFactionMastery attachment that will increase damage of a weapon or player with this attachment by the some percentage when members of the specified faction are hit. The percentage increase depends on the owners total faction with opponents of the target faction. For example, attaching Undead XmlFactionMastery to a weapon (e.g. [addatt XmlFactionMastery Undead) will cause it to do bonus damage against any mob in the undead faction depending on the owners faction with its opponents (e.g. Humanoid). As faction level changes, the bonus will also change and so it can become weaker or more powerful depending on use. Using the ItemIdentification skill on the weapon will reveal the current bonus level. This can be attached to mobiles or weapons. If attached directly to a player, then the bonus will apply to any weapon the player uses.
This can also be added directly to a weapon in its constructor (just as any other attachment can) by including a line like

XmlAttach.AttachTo(this, new XmlFactionMastery("Undead"));

in the weapons script. (note, you will also have to add a "using Server.Engines.XmlSpawner2;" to the top of the script). Other overloads are available as well that allow you to specify the damage bonus, duration of effect, etc. in addition to the faction type.

Faction Opponents and Allies can be modified by the user in the XmlMobFactions.cs script.
An example of spawning a weapon with this attachment is given in factionmastery.xml (place the .xml file in the top level of your RunUO installation and use the "[xmlloadhere factionmastery.xml" command to load the .xml spawner file)


- added the XmlDynamicFaction attachment that allows you to place any mob (or player) into the specified faction. Added an example of spawning a mob with a specified faction in factionspawn.xml (place the .xml file in the top level of your RunUO installation and use the "[xmlloadhere factionspawn.xml" command to load the .xml spawner file).

- added the XmlAddFaction attachment that can be used to give a specified amount of faction to a player. If attached directly to a player it gives the faction immediately. It can be also used to give faction as a quest reward by specifying it as the AttachmentString in a questholder or questnote. If attached to a mob, then that faction will be given to the player when the mob is killed (in addition to any faction that is normally given for killing the mob). Added an example of spawning a quest with a specified faction in questfaction.xml (place the .xml file in the top level of your RunUO installation and use the "[xmlloadhere questfaction.xml" command to load the .xml spawner file).

- added the XmlFactionEquip attachment that can be applied to weapons, jewelry, or armor to add a minimum mob faction requirement to equip. This allows you to make faction-dependent weapons/armor (must go through XmlSpawner2 installation step 9 in order to take advantage of this). Added an example of spawning a weapon with a specified faction requirement in equipfaction.xml (place the .xml file in the top level of your RunUO installation and use the "[xmlloadhere equipfaction.xml" command to load the .xml spawner file). Note, using the Item Identification skill on an item with this attachment will display the faction requirement.
 

Attachments

  • XmlMobFactions-20-v106a.zip
    32.6 KB · Views: 264

FAXEX

Wanderer
When i try enter in Faction region with smaller amount level:


Code:
Server Crash Report
===================

RunUO Version 2.0, Build 2357.32527
Operating System: Microsoft Windows NT 5.1.2600 Service Pack 2
.NET Framework: 2.0.50727.42
Time: 02.08.2006 12:03:20
Mobiles: 4146
Items: 59810
Clients:
- Count: 2
+ 127.0.0.1: (account = Admin) (mobile = 0x2 'Phoenix')
+ 127.0.0.1: (account = 1) (mobile = 0x7 'Oracle')

Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
   at Server.Sector.OnLeave(Mobile m)
   at Server.Map.OnMove(Point3D oldLocation, Mobile m)
   at Server.Mobile.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobiles.BaseCreature.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobile.MoveToWorld(Point3D newLocation, Map map)
   at Server.Engines.XmlSpawner2.MobFactionRegion.KickOut(Mobile m)
   at Server.Engines.XmlSpawner2.MobFactionRegion.OnEnter(Mobile m)
   at Server.Region.OnRegionChange(Mobile m, Region oldRegion, Region newRegion)
   at Server.Mobile.UpdateRegion()
   at Server.Mobile.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobiles.BaseCreature.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobile.set_Location(Point3D value)
   at Server.Mobiles.XmlSpawner.Defrag(Boolean killtest)
   at Server.Mobiles.XmlSpawner.get_TotalSpawnedObjects()
   at Server.Mobiles.XmlSpawner.InternalTimer2..ctor(XmlSpawner spawner, TimeSpan delay)
   at Server.Mobiles.XmlSpawner.DoTimer(TimeSpan delay)
   at Server.Mobiles.XmlSpawner.DoTimer()
   at Server.Mobiles.XmlSpawner.OnTick()
   at Server.Mobiles.XmlSpawner.InternalTimer2.OnTick()
   at Server.Timer.Slice()
   at Server.Core.Main(String[] args)
 

ArteGordon

Wanderer
FAXEX said:
When i try enter in Faction region with smaller amount level:


Code:
Server Crash Report
===================

RunUO Version 2.0, Build 2357.32527
Operating System: Microsoft Windows NT 5.1.2600 Service Pack 2
.NET Framework: 2.0.50727.42
Time: 02.08.2006 12:03:20
Mobiles: 4146
Items: 59810
Clients:
- Count: 2
+ 127.0.0.1: (account = Admin) (mobile = 0x2 'Phoenix')
+ 127.0.0.1: (account = 1) (mobile = 0x7 'Oracle')

Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
   at Server.Sector.OnLeave(Mobile m)
   at Server.Map.OnMove(Point3D oldLocation, Mobile m)
   at Server.Mobile.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobiles.BaseCreature.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobile.MoveToWorld(Point3D newLocation, Map map)
   at Server.Engines.XmlSpawner2.MobFactionRegion.KickOut(Mobile m)
   at Server.Engines.XmlSpawner2.MobFactionRegion.OnEnter(Mobile m)
   at Server.Region.OnRegionChange(Mobile m, Region oldRegion, Region newRegion)
   at Server.Mobile.UpdateRegion()
   at Server.Mobile.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobiles.BaseCreature.SetLocation(Point3D newLocation, Boolean isTeleport)
   at Server.Mobile.set_Location(Point3D value)
   at Server.Mobiles.XmlSpawner.Defrag(Boolean killtest)
   at Server.Mobiles.XmlSpawner.get_TotalSpawnedObjects()
   at Server.Mobiles.XmlSpawner.InternalTimer2..ctor(XmlSpawner spawner, TimeSpan delay)
   at Server.Mobiles.XmlSpawner.DoTimer(TimeSpan delay)
   at Server.Mobiles.XmlSpawner.DoTimer()
   at Server.Mobiles.XmlSpawner.OnTick()
   at Server.Mobiles.XmlSpawner.InternalTimer2.OnTick()
   at Server.Timer.Slice()
   at Server.Core.Main(String[] args)

I will have to check that out.
 

Johabius

Knight
Okay Arte, here I am again with another error for you.

Code:
RunUO - [www.runuo.com] Version 2.0, Build 2357.32527
Core: Running on .NET Framework Version 2.0.50727
Core: Running with arguments: -debug

Scripts: Compiling C# scripts...failed (1 errors, 1 warnings)
Errors:
 + Customs/XMLMobFactions/XmlAttachments/XmlMobFactions.cs:
    CS0246: Line 92: The type or namespace name 'Blackthorn' could not be found
(are you missing a using directive or an assembly reference?)
    CS0246: Line 102: The type or namespace name 'Sphynx' could not be found (ar
e you missing a using directive or an assembly reference?)
    CS0246: Line 133: The type or namespace name 'AcidElemental' could not be fo
und (are you missing a using directive or an assembly reference?)
Scripts: One or more scripts failed to compile or no script files were found.
 - Press return to exit, or R to try again.
It seems that I am missing some mobiles here. Would it be safe to remove the Blackthorn, Sphynx, and Acid Elemental from the XmlMobFactions file, or can you provide the mobiles?
Here is the file:
Code:
using System;
using System.Data;
using Server;
using Server.Items;
using Server.Network;
using Server.Mobiles;
using Server.Spells;
using System.Collections;
using System.Collections.Generic;
using Server.Commands.Generic;
using Server.Commands;
using Server.Gumps;
using System.Text;
/*
** XmlMobFactions
** 10/27/04
** ArteGordon
**
** This attachment will allow you to create a system for creating mob factions that can be maintained on a player or a piece of equipment
** Mob factions can be set up to add/subtract faction based upon mobs that are killed within the specified group or in opponent groups
** Mob factions can also be created to be used in quests or other systems that can modify faction through means other than killing mobs
*/
namespace Server.Engines.XmlSpawner2
{
public class XmlMobFactions : XmlAttachment
{
private static Group [] m_KillGroups; // this is the list of groups that gain/lose faction through killing mobs
public static Group[] KillGroups { get{ return m_KillGroups; } }

private bool verboseMobFactions = true; // determines whether faction gain/lost messages are displayed on each kill. True by default
private int m_Credits = 0;

[CommandProperty( AccessLevel.GameMaster )]
public int Credits { get{ return m_Credits; } set { m_Credits = value; } }

// this scaling to determines credits gained per mob kill.
// default is set to 0.1% of fame. Note, regardless of the scaling at least 1 credit will be gained per kill
private static double m_CreditScale = 0.001;
// these are the mapping between faction levels and titles
public static string GetTitle(int level)
{
if(level < -20000)
return "AttackOnSight";
if(level >= -20000 && level < -15000)
return "Hated";
if(level >= -15000 && level < -10000)
return "Despised";
if(level >= -10000 && level < -5000)
return "Disliked";
if(level >= -5000 && level < -500)
return "Distrusted";
if(level >= -500 && level <= 500)
return "Neutral";
if(level <= 5000 && level > 500)
return "Trusted";
if(level <= 10000 && level > 5000)
return "Liked";
if(level <= 15000 && level > 10000)
return "Admired";
if(level <= 20000 && level > 15000)
return "Revered";
if(level <= 25000 && level > 20000)
return "Worshipped";
if(level > 25000)
return "Untouchable";

// should never get here
return null;
}
// ------------------------------------------------------------------------------
// BEGINNING of user-defined faction information
// ------------------------------------------------------------------------------
// these are the Members that define group members
private static Type[] PlayerMembers =
new Type[]
{
typeof( PlayerMobile )
};
private static Type[] HumanoidMembers =
new Type[]
{
typeof( Ogre ), typeof( OgreLord ), typeof( ArcticOgreLord ),typeof( Orc ), typeof( OrcishMage ),
typeof( OrcishLord ), typeof( OrcBrute ), typeof( OrcBomber ), typeof( OrcCaptain ), typeof( Troll ),
typeof( Cyclops ), typeof( Titan ), typeof( Juggernaut ), typeof( JukaMage ), typeof( JukaLord ),
typeof( JukaWarrior ), typeof( Blackthorn ), typeof( Betrayer ), typeof( Ettin ), typeof( Ratman ),
typeof( RatmanMage ), typeof( RatmanArcher )
};
private static Type[] UndeadMembers =
new Type[]
{
typeof( AncientLich ), typeof( Bogle ), typeof( BoneMagi ), typeof( Lich ), typeof( LichLord ),
typeof( Shade ), typeof( Spectre ), typeof( Wraith ), typeof( BoneKnight ), typeof( Ghoul ),
typeof( Mummy ), typeof( SkeletalKnight ), typeof( Skeleton ), typeof( Zombie ), typeof(Succubus),
typeof( SkeletalMage), typeof( RottingCorpse ), /* CUSTOM MOB */ typeof( Sphynx )
};
private static Type[] ReptilianMembers =
new Type[]
{
typeof( AncientWyrm ), typeof( Dragon ), typeof( Drake ), typeof( SerpentineDragon ), typeof( ShadowWyrm ),
typeof( SkeletalDragon ), typeof( SwampDragon ), typeof( WhiteWyrm ), typeof( Wyvern ) , typeof( Lizardman ),
typeof( OphidianArchmage ), typeof( OphidianKnight ), typeof( OphidianMage ), typeof( OphidianMatriarch ), typeof( OphidianWarrior ),
typeof( IceSerpent ), typeof( GiantIceWorm ), typeof( GiantSerpent ), typeof( IceSnake ), typeof( LavaSerpent ), typeof( LavaSnake ),
typeof( SilverSerpent ), typeof( Snake ), typeof( SeaSerpent ), typeof( Kraken), typeof( DeepSeaSerpent)
};
private static Type[] ArachnidMembers =
new Type[]
{
typeof( DreadSpider ), typeof( FrostSpider ), typeof( GiantBlackWidow ), typeof( GiantSpider ),
typeof( TerathanAvenger ), typeof( TerathanDrone ), typeof( TerathanMatriarch ), typeof( TerathanWarrior ),
typeof( Scorpion ), typeof( Mephitis ), typeof(SolenHelper), typeof( RedSolenWorker), typeof( RedSolenWarrior),
typeof( RedSolenQueen), typeof( RedSolenInfiltratorWarrior), typeof( RedSolenInfiltratorQueen), typeof( AntLion),
typeof( Beetle)
};
private static Type[] ElementalMembers =
new Type[]
{
typeof( BloodElemental ), typeof( EarthElemental ), typeof( SummonedEarthElemental ), typeof( AgapiteElemental ),
typeof( BronzeElemental ), typeof( CopperElemental ), typeof( DullCopperElemental ), typeof( GoldenElemental ),
typeof( ShadowIronElemental ), typeof( ValoriteElemental ), typeof( VeriteElemental ), typeof( PoisonElemental ),
typeof( FireElemental ), typeof( SummonedFireElemental ), typeof( SnowElemental ), typeof( AirElemental ),
typeof( SummonedAirElemental ), typeof( WaterElemental ), typeof( SummonedWaterElemental ), typeof(CrystalElemental),
typeof(AcidElemental), typeof( Efreet )
};
private static Type[] AbyssMembers =
new Type[]
{
typeof( AbysmalHorror ), typeof( Balron ), typeof( BoneDemon ), typeof( ChaosDaemon ), typeof( Daemon ),
typeof( SummonedDaemon ), typeof( DemonKnight ), typeof( Gargoyle ), typeof( FireGargoyle ),
typeof( HordeMinion ), typeof( IceFiend ), typeof( Imp ),
typeof( StoneGargoyle ), typeof(HellHound), typeof(EnslavedGargoyle), typeof(Nightmare)
};
private static Type[] FairieMembers =
new Type[]
{
typeof( Pixie ), typeof( Wisp ), typeof( ShadowWisp ), typeof( EtherealWarrior )
};
private static Type[] PlantMembers =
new Type[]
{
typeof( SwampTentacle ), typeof( Quagmire ), typeof( Corpser ), typeof( BogThing ), typeof( Bogling ), typeof( WhippingVine ),
typeof( Reaper )
};
private static Type[] UnderworldMembers =
new Type[]
{
typeof( Gazer), typeof(ElderGazer), typeof(WandererOfTheVoid), typeof(WailingBanshee), typeof(VampireBat), typeof(Revenant),
typeof(Ravager), typeof(Impaler), typeof(GoreFiend), typeof(Gibberling), typeof(FleshRenderer), typeof(PatchworkSkeleton),
typeof(FleshGolem), typeof(FleshRenderer), typeof(Devourer), typeof(DarknightCreeper), typeof(AbysmalHorror),
typeof(SkitteringHopper), typeof(MoundOfMaggots)
};
 
// this is the list that contains ALL of the group types that can have faction.
// Note that just because they are listed here does not mean they will actually have a faction associated with them. Faction groups have to be
// set up in the Initialize method.
// You can add as many as you like but End_Unused MUST be the last grouptype
public enum GroupTypes
{
Player,
Humanoid,
Undead,
Reptilian,
Arachnid,
Elemental,
Abyss,
DragonLords,
NecroMasters,
Fairie,
Plant,
Underworld,
End_Unused // End_Unused MUST remain at the end of the list
};
 
 
// all mob factions are set up here
public static new void Initialize()
{
// stress testing
//Timer.DelayCall( TimeSpan.FromSeconds(15),TimeSpan.FromSeconds(15), new TimerCallback( XmlSpawner.XmlTrace_OnCommand ) );
CommandSystem.Register("VerboseMobFactions", AccessLevel.Player, new CommandEventHandler(VerboseMobFactions_OnCommand));
CommandSystem.Register("AddAllMobFactions", AccessLevel.Administrator, new CommandEventHandler(AddAllMobFactions_OnCommand));
CommandSystem.Register("RemoveAllMobFactions", AccessLevel.Administrator, new CommandEventHandler(RemoveAllMobFactions_OnCommand));
CommandSystem.Register("CheckMobFactions", AccessLevel.Player, new CommandEventHandler(CheckMobFactions_OnCommand));
// set up all of the mob factions
Group PlayerGroup = new Group(GroupTypes.Player);
Group UndeadGroup = new Group(GroupTypes.Undead);
Group HumanoidGroup = new Group(GroupTypes.Humanoid);
Group ArachnidGroup = new Group(GroupTypes.Arachnid);
Group ReptilianGroup = new Group(GroupTypes.Reptilian);
Group ElementalGroup = new Group(GroupTypes.Elemental);
Group AbyssGroup = new Group(GroupTypes.Abyss);
Group DragonLordsGroup = new Group();
Group NecroMastersGroup = new Group(GroupTypes.NecroMasters);
Group FairieGroup = new Group(GroupTypes.Fairie);
Group PlantGroup = new Group(GroupTypes.Plant);
Group UnderworldGroup = new Group(GroupTypes.Underworld);
// these groups have Members and opponents lists that determine which mobs give faction as well as the multipliers for the
// amount of faction gained and lost by killing
PlayerGroup.Opponents = new Group[] {ArachnidGroup, HumanoidGroup, UndeadGroup, ReptilianGroup, ElementalGroup, AbyssGroup};
PlayerGroup.OpponentGain = new double [] { 2, 1, 2, 2, 1, 4 }; // scale factor for faction gained by opponent groups
PlayerGroup.Allies = new Group[] { PlayerGroup, FairieGroup };
PlayerGroup.AllyLoss = new double [] { 30.0, 10 }; // scale factor for faction lost for killing within group
PlayerGroup.Members = new ArrayList(PlayerMembers);
UndeadGroup.Opponents = new Group[] {HumanoidGroup, PlayerGroup, FairieGroup};
UndeadGroup.OpponentGain = new double [] { 1, 0.1, 0.5 };
UndeadGroup.Allies = new Group[] { UndeadGroup, AbyssGroup };
UndeadGroup.AllyLoss = new double [] {1.2, 0.4};
UndeadGroup.Members = new ArrayList(UndeadMembers);
UndeadGroup.Members.Add(new XmlDynamicFaction("Undead"));
HumanoidGroup.Opponents = new Group[] {UndeadGroup, PlayerGroup, PlantGroup};
HumanoidGroup.OpponentGain = new double [] { 1, 0.05, 0.5 };
HumanoidGroup.Allies = new Group[] { HumanoidGroup };
HumanoidGroup.AllyLoss = new double [] {1.2};
HumanoidGroup.Members = new ArrayList(HumanoidMembers);
HumanoidGroup.Members.Add(new XmlDynamicFaction("Humanoid"));
ArachnidGroup.Opponents = new Group[] {ReptilianGroup, PlayerGroup, FairieGroup};
ArachnidGroup.OpponentGain = new double [] { 1, 0.15, 0.3 };
ArachnidGroup.Allies = new Group[] { ArachnidGroup, PlantGroup };
ArachnidGroup.AllyLoss = new double [] {1.2, 0.5 };
ArachnidGroup.Members = new ArrayList(ArachnidMembers);
ArachnidGroup.Members.Add(new XmlDynamicFaction("Arachnid"));
ReptilianGroup.Opponents = new Group[] {ArachnidGroup, PlayerGroup};
ReptilianGroup.OpponentGain = new double [] { 1, 0.1 };
ReptilianGroup.Allies = new Group[] { ReptilianGroup };
ReptilianGroup.AllyLoss = new double [] {1.2};
ReptilianGroup.Members = new ArrayList(ReptilianMembers);
ReptilianGroup.Members.Add(new XmlDynamicFaction("Reptilian"));
ElementalGroup.Opponents = new Group[] {AbyssGroup, PlayerGroup, PlantGroup};
ElementalGroup.OpponentGain = new double [] { 1, 0.05, 0.5 };
ElementalGroup.Allies = new Group[] { ElementalGroup };
ElementalGroup.AllyLoss = new double [] {1.2};
ElementalGroup.Members = new ArrayList(ElementalMembers);
ElementalGroup.Members.Add(new XmlDynamicFaction("Elemental"));
AbyssGroup.Opponents = new Group[] {ElementalGroup, PlayerGroup, FairieGroup};
AbyssGroup.OpponentGain = new double [] { 1, 0.2, 1 };
AbyssGroup.Allies = new Group[] { AbyssGroup, UndeadGroup };
AbyssGroup.AllyLoss = new double [] {1.2, 0.4};
AbyssGroup.Members = new ArrayList(AbyssMembers);
AbyssGroup.Members.Add(new XmlDynamicFaction("Abyss"));
FairieGroup.Opponents = new Group[] {ArachnidGroup, UndeadGroup, AbyssGroup};
FairieGroup.OpponentGain = new double [] { 0.5, 0.7, 1 };
FairieGroup.Allies = new Group[] { FairieGroup, PlayerGroup };
FairieGroup.AllyLoss = new double [] { 1.2, 0.5};
FairieGroup.Members = new ArrayList(FairieMembers);
FairieGroup.Members.Add(new XmlDynamicFaction("Fairie"));
PlantGroup.Opponents = new Group[] {ElementalGroup, HumanoidGroup};
PlantGroup.OpponentGain = new double [] { 0.5, 0.7 };
PlantGroup.Allies = new Group[] { PlantGroup };
PlantGroup.AllyLoss = new double [] { 1.2 };
PlantGroup.Members = new ArrayList(PlantMembers);
PlantGroup.Members.Add(new XmlDynamicFaction("Plant"));

UnderworldGroup.Opponents = new Group[] { AbyssGroup, HumanoidGroup};
UnderworldGroup.OpponentGain = new double [] { 0.5, 0.7 };
UnderworldGroup.Allies = new Group[] { UnderworldGroup, UndeadGroup, ElementalGroup };
UnderworldGroup.AllyLoss = new double [] { 1.2, 1.0, 1.0};
UnderworldGroup.Members = new ArrayList(UnderworldMembers);
UnderworldGroup.Members.Add(new XmlDynamicFaction("Underworld"));
// this group does not have a predefined set of members but instead uses only the dynamic faction system to determine whether a mob
// is a member of the group
NecroMastersGroup.Members = new ArrayList();
NecroMastersGroup.Members.Add(new XmlDynamicFaction("NecroMasters"));
NecroMastersGroup.Allies = new Group[] { NecroMastersGroup };
NecroMastersGroup.AllyLoss = new double [] { 1 };
// Note that the groups do not have to have opponents or members and therefore do not have to gain/lose faction by killing mobs
// these could be used to maintain factions that are set by quests or other events
DragonLordsGroup.GroupType = GroupTypes.DragonLords;
// only have to list groups here that gain/lose faction through killing mobs and/or should be actively checked for AI related functions
// such as target acquisition, taming, mob control etc. 
// You can leave out groups that are intended to be passive placeholders for faction, such as the DragonLordsGroup example.
// stress testing
//m_KillGroups = new Group[1000];
//for(int i=0;i<1000;i++) m_KillGroups[i] = AbyssGroup;
m_KillGroups = new Group[]
{
PlayerGroup,
UndeadGroup,
HumanoidGroup,
ArachnidGroup,
ReptilianGroup,
ElementalGroup,
AbyssGroup,
FairieGroup,
PlantGroup,
UnderworldGroup,
NecroMastersGroup
};
}
// define these properties to allow public access to faction levels for the various groups
// if you add new faction groups, you should add a faction property for them
[CommandProperty( AccessLevel.GameMaster )]
public int Player { get { return GetFactionLevel(GroupTypes.Player); } set { SetFactionLevel(GroupTypes.Player,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Humanoid { get { return GetFactionLevel(GroupTypes.Humanoid); } set { SetFactionLevel(GroupTypes.Humanoid,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Undead { get { return GetFactionLevel(GroupTypes.Undead); } set { SetFactionLevel(GroupTypes.Undead,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Reptilian{ get { return GetFactionLevel(GroupTypes.Reptilian); } set { SetFactionLevel(GroupTypes.Reptilian,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Arachnid { get { return GetFactionLevel(GroupTypes.Arachnid); } set { SetFactionLevel(GroupTypes.Arachnid,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Elemental { get { return GetFactionLevel(GroupTypes.Elemental); } set { SetFactionLevel(GroupTypes.Elemental,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Abyss { get { return GetFactionLevel(GroupTypes.Abyss); } set { SetFactionLevel(GroupTypes.Abyss,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int DragonLords { get { return GetFactionLevel(GroupTypes.DragonLords); } set { SetFactionLevel(GroupTypes.DragonLords,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int NecroMasters { get { return GetFactionLevel(GroupTypes.NecroMasters); } set { SetFactionLevel(GroupTypes.NecroMasters,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Fairie { get { return GetFactionLevel(GroupTypes.Fairie); } set { SetFactionLevel(GroupTypes.Fairie,value); } }
[CommandProperty( AccessLevel.GameMaster )]
public int Plant { get { return GetFactionLevel(GroupTypes.Plant); } set { SetFactionLevel(GroupTypes.Plant,value); } }

[CommandProperty( AccessLevel.GameMaster )]
public int Underworld { get { return GetFactionLevel(GroupTypes.Underworld); } set { SetFactionLevel(GroupTypes.Underworld,value); } }
// ------------------------------------------------------------------------------
// END of user-defined faction information
// ------------------------------------------------------------------------------
 
// these variables control the way in which faction level affects mob target acquisition if the BaseAI AquireFocusMob modification has been made.
// These settings work best when combined with a reduction in the BaseCreature ReaquireDelay setting from the default of 10 secs to 1-2 secs.
static int baseValue = 180000; // increasing this increases the faction effect on delayed acquisition. Higher=longer average delay
static int maxFaction = 25000; // this sets the faction level above which mobs will no longer acquire the target
// This method will calculate an target acquisition probability based upon the targets faction level with the mob
// This was intended to be called from BaseAI in the AquireFocusMob method
// Higher faction will lead to longer acquisition time
// Lower or negative faction will lead to faster acquisition time
// mob will be the creature and target will be the player
public static bool CheckAcquire(Mobile mob, Mobile target)
{
// only acquisition of players is affected by faction
if(!target.Player)
{
return true;
}
 
// by default, mobs that arent in a faction group will have the target acquisition probability calculated as though
// they had a faction level of 0
int facvalue = (int)GetScaledFaction(target, mob, -baseValue, maxFaction, 1);
// return true if the target can be acquired by the mob.
// higher mob faction on the target means lower probability of acquisition
bool cantarget = (baseValue + facvalue) < Utility.Random(baseValue + maxFaction);

return cantarget;
}
public static double GetScaledFaction(Mobile from, Mobile mob, double min, double max, double scale)
{
if(from == null || mob == null || !from.Player) return 0;
double facvalue = 0;
//XmlMobFactions x = (XmlMobFactions)XmlAttach.FindAttachment(XmlAttach.MobileAttachments, from, typeof(XmlMobFactions), "Standard");
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, from, typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
//if(x != null)
//{
int count = 0;
// find any static groups that this mob belongs to. Note, if it belongs to more than one group, calculate the average.
ArrayList glist = FindGroups(mob);
if(glist != null && glist.Count > 0)
{
foreach( GroupTypes g in glist)
{
if(g != GroupTypes.End_Unused)
{
facvalue += x.GetFactionLevel(g)*scale;
count++;
}
}
}
// does this mob have dynamic faction assignments?
ArrayList dlist = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, mob, typeof(XmlDynamicFaction));
if(dlist != null && dlist.Count > 0)
{
//if(XmlAttach.FindAttachment(XmlAttach.MobileAttachments, mob, typeof(XmlDynamicFaction)) != null)
//{
// do this for dynamic factions as well
ArrayList dglist = DynamicFindGroups(mob);
if(dglist != null && dglist.Count > 0)
{
foreach( GroupTypes g in dglist)
{

if(g != GroupTypes.End_Unused)
{
facvalue += x.GetFactionLevel(g)*scale;
count++;
}
}
}
}

// compute the average faction value
if(count > 0)
{
facvalue /= count;
}
}
if(facvalue > max) facvalue = max;
if(facvalue < min) facvalue = min;
return facvalue;
}
public static int GetCredits(Mobile m)
{
int val = 0;
ArrayList list = XmlAttach.FindAttachments(m,typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
val = ((XmlMobFactions)list[0]).Credits;
}
return val;
}

public static bool HasCredits(Mobile m, int credits)
{
if(m == null || m.Deleted) return false;
ArrayList list = XmlAttach.FindAttachments(m,typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
if(x.Credits >= credits)
{
return true;
}
}
return false;
}
public static bool TakeCredits(Mobile m, int credits)
{
if(m == null || m.Deleted) return false;
ArrayList list = XmlAttach.FindAttachments(m,typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
if(x.Credits >= credits)
{
x.Credits -= credits;
return true;
}
}
return false;
}

public static int GetFactionLevel(Mobile m, GroupTypes grouptype)
{
if(m == null || grouptype == GroupTypes.End_Unused) return 0;
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, m, typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
if(x.FactionList == null ) return 0;
foreach(GroupStatus g in x.FactionList)
{
if(g.GroupType == grouptype) return g.FactionLevel;
}
}
return 0;
}
private static Hashtable GroupTypeHash = new Hashtable();
// this method returns a list of the group types that the mobile belongs to using the KillGroups master list
public static ArrayList FindGroups(Mobile m)
{
if(m == null) return null;
// see whether this mobile type is already in the hash table
if(GroupTypeHash.Contains(m.GetType()))
{
// then get the list from there
return (ArrayList)GroupTypeHash[m.GetType()];
}
ArrayList list = new ArrayList();
foreach(Group g in KillGroups)
{
if(MatchType(g.Members, m))
{
list.Add(g.GroupType);
}
}
GroupTypeHash.Add(m.GetType(),list);
return list;
}

// this method returns a list of the group types that the mobile belongs based upon dynamic factions
public static ArrayList DynamicFindGroups(Mobile m)
{
if(m == null) return null;
ArrayList list = new ArrayList();
foreach(Group g in KillGroups)
{
if(DynamicMatchType(g.Members, m))
{
list.Add(g.GroupType);
}
}
return list;
}
public static Group FindGroup(GroupTypes gtype)
{
foreach(Group g in KillGroups)
{
if(g.GroupType == gtype)
{
return g;
}
}
return null;
}
private static bool MatchType( ArrayList array, Mobile m)
{
if(array == null || m == null) return false;
foreach( object o in array)
{
if(o is Type && ((Type)o == m.GetType() || ((Type)o).IsSubclassOf(m.GetType()))) return true;
}
return false;
}

private static bool DynamicMatchType( ArrayList array, Mobile m)
{
if(array == null || m == null) return false;
foreach( object o in array)
{
if(o is XmlDynamicFaction && XmlDynamicFaction.MatchFaction(m, ((XmlDynamicFaction)o).Name)) return true;
}
return false;
}
public class Group
{
public GroupTypes GroupType;
public ArrayList Members;
public Group [] Opponents;
public Group [] Allies;
public double [] AllyLoss; // scale factor for faction lost by killing this group
public double [] OpponentGain; // scale factor for faction gained by opponent groups for killing this group

public Group(GroupTypes type)
{
GroupType = type;
}
public Group()
{
GroupType = GroupTypes.End_Unused;
}
}
private class GroupStatus
{
public GroupTypes GroupType;
public int FactionLevel;

public GroupStatus(GroupTypes type)
{
GroupType = type;
}
public GroupStatus()
{
GroupType = GroupTypes.End_Unused;
}
}
private GroupStatus [] FactionList; // this keeps track of all of the faction information in the attachment
private TimeSpan m_Refractory = TimeSpan.Zero; // 0 seconds default time between activations
private DateTime m_EndTime;
 
[CommandProperty( AccessLevel.GameMaster )]
public TimeSpan Refractory { get { return m_Refractory; } set { m_Refractory = value; } }
 
public int GetFactionLevel(GroupTypes grouptype)
{
if(FactionList == null ) return 0;
foreach(GroupStatus g in FactionList)
{
if(g.GroupType == grouptype) return g.FactionLevel;
}
return 0;
}
public void SetFactionLevel(GroupTypes grouptype, int value)
{
if(FactionList == null ) return;
foreach(GroupStatus g in FactionList)
{
if(g.GroupType == grouptype)
{
g.FactionLevel = value;
return;
}
}
}
[Usage( "VerboseMobFactions [true/false]" )]
[Description( "Turns on/off display of faction gain/loss on mob kills" )]
public static void VerboseMobFactions_OnCommand( CommandEventArgs e )
{
// get the mob factions attachment
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, e.Mobile, typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
if(e.Arguments.Length > 0)
{
try
{
x.verboseMobFactions = bool.Parse(e.Arguments[0]);
} 
catch{}
}
e.Mobile.SendMessage("VerboseMobFactions is set to {0}",x.verboseMobFactions);
} 
else
{
e.Mobile.SendMessage("Standard XmlMobFactions attachment not found");
}
}
[Usage( "CheckMobFactions" )]
[Description( "Reports faction levels" )]
public static void CheckMobFactions_OnCommand( CommandEventArgs e )
{
// get the mob factions attachment
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, e.Mobile, typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
XmlMobFactions x = list[0] as XmlMobFactions;
e.Mobile.SendMessage("{0}",x.OnIdentify(e.Mobile));
}
}
[Usage( "AddAllMobFactions" )]
[Description( "Adds the standard XmlMobFaction attachment to all players" )]
public static void AddAllMobFactions_OnCommand( CommandEventArgs e )
{
int count = 0;
foreach(Mobile m in World.Mobiles.Values)
{
if(m.Player)
{
// does this player already have a points attachment?
ArrayList list = XmlAttach.FindAttachments(m, typeof(XmlMobFactions), "Standard");
if(list == null || list.Count == 0)
{
XmlAttachment x = new XmlMobFactions();
XmlAttach.AttachTo(e.Mobile, m,x);
count++;
}
}
}
e.Mobile.SendMessage("Added XmlMobFaction attachments to {0} players",count);
}
[Usage( "RemoveAllMobFactions" )]
[Description( "Removes the standard XmlMobFaction attachment from all players" )]
public static void RemoveAllMobFactions_OnCommand( CommandEventArgs e )
{
int count = 0;
foreach(Mobile m in World.Mobiles.Values)
{
if(m.Player)
{
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, m, typeof(XmlMobFactions), "Standard");
if(list != null && list.Count > 0)
{
foreach(XmlAttachment x in list)
{
x.Delete();
}
}
count++;
}
}
e.Mobile.SendMessage("Removed XmlMobFaction attachments from {0} players",count);
}
// These are the various ways in which the message attachment can be constructed.
// These can be called via the [addatt interface, via scripts, via the spawner ATTACH keyword.
// Other overloads could be defined to handle other types of arguments
// a serial constructor is REQUIRED
public XmlMobFactions(ASerial serial) : base(serial)
{
}
[Attachable]
public XmlMobFactions(string name, double refractory)
{
Name = name;
Refractory = TimeSpan.FromSeconds(refractory);
FactionList = new GroupStatus[(int)GroupTypes.End_Unused];
for(int i = 0;i<FactionList.Length;i++)
{
FactionList[i] = new GroupStatus();
FactionList[i].GroupType = (GroupTypes)i;
}
}

[Attachable]
public XmlMobFactions() : this("Standard", 0)
{
}
[Attachable]
public XmlMobFactions(double refractory) : this("Standard", refractory)
{
}
private bool SameGuild(Mobile killed, Mobile killer)
{
return ( killer.Guild == killed.Guild && killer.Guild != null && killed.Guild != null);
}
public override bool HandlesOnKill { get { return true; } }
private static Hashtable GroupHash = new Hashtable();
private bool m_ChallengeStatus = false;
public override void OnBeforeKill(Mobile killed, Mobile killer )
{
// if you have XmlPoints installed and wish to prevent challenge games and duels from affecting
// faction points then uncomment the following line
//m_ChallengeStatus = XmlPoints.AreChallengers(killer, killed);
}
public override void OnKill(Mobile killed, Mobile killer )
{
base.OnKill(killed, killer);
// supports ignoring XmlPoints challenges
if(m_ChallengeStatus)
{
m_ChallengeStatus = false;
return;
}
if(killed == null || killer == null || killer == killed) return;

// check for within guild kills and ignore them
if(SameGuild(killed,killer)) return;
// this calculates the base faction level that will be gained/lost based upon the fame of the killed mob
double value = (double)(killed.Fame/1000.0);
if(value <= 0) value = 1;
// calculates credits gained in a similar way
int cval = (int)(killed.Fame*m_CreditScale);
if(cval <= 0) cval = 1;
Credits += cval;
// prepare the group lists that will be checked for faction
ArrayList glist = null;
ArrayList dglist = null;
// check to see whether this mob type has already been hashed into a group list
if(GroupHash.Contains(killed.GetType()))
{
glist = (ArrayList)GroupHash[killed.GetType()];
} 
else
{
// otherwise look it up the slow way and prepare a hash entry for it at the same time
// unless it is using dynamic faction
glist = new ArrayList();
foreach(Group g in KillGroups)
{
if(MatchType(g.Members, killed))
{
glist.Add(g);
}
}
GroupHash.Add(killed.GetType(),glist);
}
// have to look up dynamic factions the exhaustive way
// does this mob have dynamic faction assignments?
ArrayList list = XmlAttach.FindAttachments(XmlAttach.MobileAttachments, killed, typeof(XmlDynamicFaction));
if(list != null && list.Count > 0)
{
//if(XmlAttach.FindAttachment(XmlAttach.MobileAttachments, killed, typeof(XmlDynamicFaction)) != null)
//{
dglist = new ArrayList();
foreach(Group g in KillGroups)
{
if(DynamicMatchType(g.Members, killed))
{
dglist.Add(g);
}
}
}
ArrayList alist = new ArrayList();
if(glist != null && glist.Count > 0)
alist.Add(glist);
if(dglist != null && dglist.Count > 0)
alist.Add(dglist);
// go through this with static and dynamic factions
foreach(ArrayList al in alist)
{
foreach(Group g in al)
{
// tabulate the faction loss from target group allies
if(g.Allies != null && g.Allies.Length > 0)
{
for(int i = 0; i< g.Allies.Length;i++)
{
Group ally = g.Allies[i];
int facloss = 0;
try
{
facloss = (int)(value*g.AllyLoss[i]);
} 
catch{}
if(facloss <= 0) facloss = 1;

int p = GetFactionLevel(ally.GroupType) - facloss;
SetFactionLevel(ally.GroupType, p);
if(verboseMobFactions)
killer.SendMessage("lost {0} faction {1}", ally.GroupType,facloss);
}
}
// tabulate the faction gain from target group opponents
if(g.Opponents != null && g.Opponents.Length > 0)
{
for(int i = 0; i< g.Opponents.Length;i++)
{
Group opp = g.Opponents[i];
int facgain = 0;
try
{
facgain = (int)(value*g.OpponentGain[i]);
} 
catch {}
if(facgain <= 0) facgain = 1;
int p = GetFactionLevel(opp.GroupType) + facgain;
SetFactionLevel(opp.GroupType, p);
if(verboseMobFactions)
killer.SendMessage("gained {0} faction {1}",opp.GroupType, facgain);
}
}
}
}
m_EndTime = DateTime.Now + Refractory;
}
public override void Serialize( GenericWriter writer )
{
base.Serialize(writer);
writer.Write( (int) 1 );
// version 1
writer.Write( m_Credits );
// version 0
writer.Write( verboseMobFactions );
if(FactionList != null)
{
writer.Write(FactionList.Length);
for(int i = 0;i<FactionList.Length;i++)
{
GroupStatus g = FactionList[i];
// by saving the group type as a string, it allows proper deserialization even if the list of faction group types change
writer.Write(g.GroupType.ToString());
writer.Write(g.FactionLevel);
}
} 
else
{
writer.Write((int)0);
}
writer.Write(m_Refractory);
writer.Write(m_EndTime - DateTime.Now);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch(version)
{
case 1:
m_Credits = reader.ReadInt();
goto case 0;
case 0:
// version 0
verboseMobFactions = reader.ReadBool();
int count = reader.ReadInt();
int newcount = (int)GroupTypes.End_Unused;;
// prepare the new faction status list
FactionList = new GroupStatus[newcount];
// initialize the faction status list with the default grouptypes
for(int j = 0;j<newcount;j++)
{
FactionList[j] = new GroupStatus((GroupTypes)j);
}
// now read in the serialized FactionList entries and cross reference to the current GroupTypes
for(int i = 0;i<count;i++)
{
string gname = reader.ReadString();
int gfac = reader.ReadInt();
// look up the enum by name
GroupTypes gtype = GroupTypes.End_Unused;
try
{
gtype = (GroupTypes)Enum.Parse(typeof(GroupTypes), gname);
} 
catch{}

// try to find the matching entry in the recently constructed faction status list
if(gtype != GroupTypes.End_Unused)
{
for(int j = 0;j<newcount;j++)
{
GroupStatus g = FactionList[j];
if(g.GroupType == gtype)
{
g.FactionLevel = gfac;
break;
}
}
}
}
Refractory = reader.ReadTimeSpan();
TimeSpan remaining = reader.ReadTimeSpan();
m_EndTime = DateTime.Now + remaining;
break;
}
}

private class MobFactionGump : Gump
{
private XmlMobFactions m_attachment;
private string m_text;
public MobFactionGump( Mobile from, XmlMobFactions a, string text) : base( 0,0)
{
if(a == null) return;
m_attachment = a;
m_text = text;
// prepare the page
AddPage( 0 );

AddBackground( 0, 0, 400, 330, 5054 );
AddAlphaRegion( 0, 0, 400, 330 );
AddLabel( 20, 2, 55, "Mob Faction Standings" );
if(a != null)
{
AddLabel( 200, 2, 30, String.Format("Available Credits: {0}", a.Credits) );
}
AddHtml( 20,20, 360, 260, text, true , true );

// add the verbose factions checkbox
AddLabel( 50, 290, 55, "Verbose Factions" );
AddButton( 20, 290, (a.verboseMobFactions ? 0xD3 :0xD2), (a.verboseMobFactions ? 0xD2 :0xD3), 100, GumpButtonType.Reply, 0);
}

public override void OnResponse( NetState state, RelayInfo info )
{
if(m_attachment == null || state == null || state.Mobile == null || info == null) return;

switch(info.ButtonID)
{
case 100:
m_attachment.verboseMobFactions = !m_attachment.verboseMobFactions;

state.Mobile.SendGump( new MobFactionGump(state.Mobile, m_attachment, m_text));
break;
}
}
}
public override string OnIdentify(Mobile from)
{
// dont let other people identify your faction standings
if(AttachedTo is Mobile && (from != (Mobile)AttachedTo && from != null && from.AccessLevel == AccessLevel.Player)) return null;
// display the faction status in a gump
StringBuilder gumpmsg = new StringBuilder();
gumpmsg.AppendFormat("\n{0,-15}{1,-15}{2,-10}\n",
"Mob Faction","Standing","Level");
if(FactionList != null)
foreach(GroupStatus g in FactionList)
{
gumpmsg.AppendFormat("{0,-15}{1,-15}{2,-10}\n",
g.GroupType, GetTitle(g.FactionLevel), g.FactionLevel);
}
 
if(from != null)
{
from.CloseGump(typeof(MobFactionGump));
from.SendGump(new MobFactionGump(from, this, gumpmsg.ToString()));
}
string msg = null;
if(Expiration > TimeSpan.Zero)
{
msg = String.Format("{0}expires in {0} mins ", msg, Expiration.TotalMinutes);
}
if(Refractory > TimeSpan.Zero)
{
return String.Format("{0}with a minimum of {1} secs between gains",msg, Refractory.TotalSeconds);
} 
else
return msg;
}
}
}
Forgive me for asking such simple questions, but I am still learning.:eek:
 

ArteGordon

Wanderer
ah, sorry about that. I forgot to clean those out.
You can add or remove any mobs you want from those lists. They are entirely user-defined and so there are no creatures that "have" to be there. Change them, rearrange them, remove them, whatever you want.
 

Johabius

Knight
Thank you again Arte! Just wanted to make sure that I wasn't removing anything that needed to be there before I ended up with more errors:D
 
Top