Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Bug Reports

Development::Bug Reports Post detailed bug reports and what you would like to see next in the emu here.

Reply
 
Thread Tools Display Modes
  #1  
Old 07-15-2008, 05:17 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default NPC Spell List Recast Delay Not Working

I have done some testing with the recast delay setting for NPC spell lists and it doesn't seem to be having any effect at all. If I am wrong, or just missing something to get it working properly, this post could be moved to the general or server support section.

I first tried setting the recast delay in seconds, thinking that it would be used similar to the recast delay setting for item clickies. That did nothing and did not effect the actual reuse time that NPCs were able to cast the same spell again.

So, I then tried changing it to Milliseconds and that too seemed to have no effect. Even if I set it as high as 1200000, which should be 20 minutes in milliseconds, the NPCs could still re-cast the same spell withing just a few seconds of having cast it. And, they can just cast it over and over again as if it is ignoring that restriction completely.

I am aware that any changes to spell lists requires a server reboot to take effect and I have been doing reboots after each new change I make.

What I am trying to do is use the spell version of Melee disciplines for Warrior, Rogue, and Monk to create a spell list that will let my melee NPCs disc for the new zone I am making. I am also adding discs to my highest level pet class pets (mage, necro, beastlord). I think it will add some nice diversity.

Unfortunately, with the way it is currently working, they are firing off disciplines every few swings and without recast delays working, I will be forced to remove this fun feature.

I am wondering if the code for recast delay on item clickies and player spells might be useful in getting the NPC spells list recast delays working properly.

I also wanted to note that I have the disciplines set as type 1 and type 2 for spell type in the lists. I used type 1 (nuke setting) for damage increasing discs, and type 2 (heal setting) for defensive disciplines.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #2  
Old 07-15-2008, 10:21 PM
moydock
Discordant
 
Join Date: Jun 2005
Posts: 286
Default

I believe I had this working a couple months back when I changed all my npc's. I'll test again and let you know.
__________________
-Croup (the rogue)
Creator of Pandemic (PvP-Racewars)
Reply With Quote
  #3  
Old 07-15-2008, 10:39 PM
moydock
Discordant
 
Join Date: Jun 2005
Posts: 286
Default

Yeah mines working fine. I'm not on the latest build, but fairly recent.
It's in seconds for me. For example, the heal I just tested:
ID: 367
npc_spells_id: 6
spellid: 200
type: 2
minlevel: 1
maxlevel: 8
manacost: -1
recast_delay: 30
priority: 1
__________________
-Croup (the rogue)
Creator of Pandemic (PvP-Racewars)
Reply With Quote
  #4  
Old 07-16-2008, 09:32 AM
So_1337
Dragon
 
Join Date: May 2006
Location: Cincinnati, OH
Posts: 689
Default

I noticed this recently, and it's especially obvious among death-touching mobs like the Golems in Fear. I was even considering a work-around.

Currently, they cast it shortly after they're first aggro'd, and then you never see it again. On Live, it had a refresh timer of one minute, and they'd nuke whoever was at the top of their hate list. I know perl isn't the best solution for this, but I considered writing a script that would fire it off once a minute as it should, using a timer.

But if this recast delay and priority thing is straightened out, it'd be much better, for sure.
Reply With Quote
  #5  
Old 07-16-2008, 04:08 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Maybe it has something to do with the Spell Type setting for the NPC Spell Lists. I am wondering if only certain types will recognize the Recast Delay setting and some will ignore it. IMO, they should all follow the Recast Delay restriction. The ones I have tested so far are set to type 1, which is the Nuke setting. So, maybe the Nuke setting doesn't follow that restriction. Maybe I can try setting them to another type (maybe the DoT setting) and see if that makes any difference. Though, I still think that they should all follow the Recast Delay restriction.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #6  
Old 07-16-2008, 04:21 PM
moydock
Discordant
 
Join Date: Jun 2005
Posts: 286
Default

I don't think it affects it. I'm only testing lvl 5 npc's but my druids never nuke twice in a row as well as not healing. I've got 15 second delay on their nukes too. They'll cast bust of flame, and then firefist, or whatever that dot is, but never 2 burst of flame in a row.
__________________
-Croup (the rogue)
Creator of Pandemic (PvP-Racewars)
Reply With Quote
  #7  
Old 07-16-2008, 07:50 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

The proc chance might be why you aren't seeing them very close together. Try upping the proc chance up to 100% so that they should try to cast it every time it is available. Then, try setting your recast delay to something longer like 180 seconds and see if they cast it before that 3 minutes are up.

I will have to do more testing on it, but it is hard when I can only reset my server every now and then to test the changes, or it will effect my players too much if I reset alot.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #8  
Old 07-17-2008, 02:25 AM
moydock
Discordant
 
Join Date: Jun 2005
Posts: 286
Default

I tried setting the proc rate to 100 and oddly they don't seem to try to cast any quicker. Like, I attack them, and they wait awhile to cast the first nuke. Possibly because they need to wait the recast delay before casting the first spell.

Trust me though, I've sat in front of these things a lot. I would have seen one double cast by now.
I originally implemented the recast so that healers wouldn't chain heal themselves when they got low on health. And they don't anymore. I guess it's possible this has been broken in one of the somewhat recent updates.
__________________
-Croup (the rogue)
Creator of Pandemic (PvP-Racewars)
Reply With Quote
  #9  
Old 07-23-2008, 04:16 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Well, I am still trying to figure out why this is happening on my server. I was looking at my variables table and I have a setting for "ailevel", and it is set to 6 (which is how it came from the PEQ db), but I don't really know what that does. So far, it is the only thing I can think of that might be causing this issue. I am running the latest version of the emu (111 with some modified code additions, but nothing that I think would relate to this issue at all.

I was looking at the source and in npc.h, I see a public section that mentions ailevel, and has some recast delay info in there. So, I am wondering if maybe my ailevel is set wrong, and maybe setting it to 6 isn't enough for it to use recast delays...

Here is the code from the public section of npc.h that I am referring to (I highlighted a few things in RED cause I think they are involved in making the decision for recast delay settings):

Code:
class NPC : public Mob
{
public:
	static NPC* SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, float in_heading = 0, Client* client = 0);
	static sint8 GetAILevel(bool iForceReRead = false);
	
	NPC(const NPCType* data, Spawn2* respawn, float x, float y, float z, float heading, bool IsCorpse = false);
	
	virtual ~NPC();

	virtual bool IsNPC() const { return true; }

	virtual bool Process();
	virtual void	AI_Init();
	virtual void	AI_Start(int32 iMoveDelay = 0);
	virtual void	AI_Stop();
	void			AI_DoMovement();
	bool			AI_AddNPCSpells(int32 iDBSpellsID);
	virtual bool	AI_EngagedCastCheck();
	virtual bool	AI_PursueCastCheck();
	virtual bool	AI_IdleCastCheck();
	virtual void	AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot);
	
	virtual void SetTarget(Mob* mob);
	virtual uint16 GetSkill(SkillType skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; }
/*  virtual void SetSkill(int in_skill_num, int8 in_skill_value) { // socket 12-29-01
        if (in_skill_num <= HIGHEST_SKILL) { skills[in_skill_num + 1] = in_skill_value; } }*/

	void CalcItemBonuses(StatBonuses *newbon);
	virtual void CalcBonuses();

	
#ifdef GUILDWARS
	int32	GetGuildLocationID() { return guildlocationid; }
#endif

	// neotokyo: added frenzy
	bool	Attack(Mob* other, int Hand = 13, bool = false);
	void	Damage(Mob* other, sint32 damage, int16 spell_id, SkillType attack_skill, bool avoidable = true, sint8 buffslot = -1, bool iBuffTic = false);
	void	Death(Mob* other, sint32 damage, int16 spell_id, SkillType attack_skill);
	bool	DatabaseCastAccepted(int spell_id);
	bool	IsFactionListAlly(uint32 other_faction);
	FACTION_VALUE CheckNPCFactionAlly(sint32 other_faction);
	FACTION_VALUE GetReverseFactionCon(Mob* iOther);

	void	GoToBind()	{ GMMove(org_x, org_y, org_z, org_heading); }
	void	Gate();

	void	GetPetState(SpellBuff_Struct *buffs, int32 *items, char *name);
	void	SetPetState(SpellBuff_Struct *buffs, int32 *items);
	void	InteractiveChat(int8 chan_num, int8 language, const char * message, const char* targetname,Mob* sender);
	void	TakenAction(int8 action,Mob* actiontaker);
	virtual void SpellProcess();
	virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);

	void	AddItem(const Item_Struct* item, int8 charges, int8 slot = 0);
	void	AddItem(int32 itemid, int8 charges, int8 slot = 0);
	void	AddLootTable();
/*
	void	NPCSpecialAttacks(const char* parse, int permtag);
	void	NPCDatabaseSpells(const char* parse);
	void	NPCUnharmSpell(int spell_id);
	void	CheckFriendlySpellStatus();

	void	CheckEnemySpellStatus();
	void	NPCHarmSpell(int target,int type);
	void    HateSummon();
*/	

	void	DescribeAggro(Client *towho, Mob *mob, bool verbose);
	void    RemoveItem(uint16 item_id, int16 quantity = 0, int16 slot = 0);
//	bool	AddNPCSpells(int32 iDBSpellsID, AISpells_Struct* AIspells);
//	void	RemoveItem(uint16 item_id);
	void	ClearItemList();
	ServerLootItem_Struct*	GetItem(int slot_id);
	void	AddCash(int16 in_copper, int16 in_silver, int16 in_gold, int16 in_platinum);
	void	AddCash();
	void	RemoveCash();
	void	QueryLoot(Client* to);
	int32	CountLoot();
	void	DumpLoot(int32 npcdump_index, ZSDump_NPC_Loot* npclootdump, int32* NPCLootindex);
	inline int32	GetLoottableID()	const { return loottable_id; }
//	void	SetPetType(int16 in_type)	{ typeofpet = in_type; } // put this here because only NPCs can be anything but charmed pets

	inline uint32	GetCopper()		const { return copper; }
	inline uint32	GetSilver()		const { return silver; }
	inline uint32	GetGold()		const { return gold; }
	inline uint32	GetPlatinum()	const { return platinum; }

	inline void	SetCopper(uint32 amt)		{ copper = amt; }
	inline void	SetSilver(uint32 amt)		{ silver = amt; }
	inline void	SetGold(uint32 amt)			{ gold = amt; }
	inline void	SetPlatinum(uint32 amt)		{ platinum = amt; }



	void SetGrid(int32 grid_){ grid=grid_; }
	void SetSp2(int32 sg2){ spawn_group=sg2; }
	void SetWaypointMax(int16 wp_){ wp_m=wp_; }

	int16 GetWaypointMax() const { return wp_m; }
	int32 GetGrid() const { return grid; }
	int32 GetSp2() const { return spawn_group; }

	uint32	MerchantType;
	void	Depop(bool StartSpawnTimer = true);
	void	Stun(int duration);
	void	UnStun();
	
	inline void SignalNPC(int _signal_id) { signaled = true; signal_id = _signal_id; }
	
	inline sint32	GetNPCFactionID()	const { return npc_faction_id; }
	inline sint32			GetPrimaryFaction()	const { return primary_faction; }
	sint32	GetNPCHate(Mob* in_ent)  {return hate_list.GetEntHate(in_ent);}
    bool    IsOnHatelist(Mob*p) { return hate_list.IsOnHateList(p);}

	void	SetNPCFactionID(sint32 in) { npc_faction_id = in; database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); }

	float   org_x, org_y, org_z, org_heading;
	
	int16	GetMaxDMG() const {return max_dmg;}
	bool	IsAnimal() const { return(bodytype == BT_Animal); }
	int16   GetPetSpellID() const {return pet_spell_id;}
	void    SetPetSpellID(int16 amt) {pet_spell_id = amt;}
	int32	GetMaxDamage(int8 tlevel);
	void    SetTaunting(bool tog) {taunting = tog;}
	void	PickPocket(Client* thief);
	void	StartSwarmTimer(int32 duration) { swarm_timer.Start(duration); }
	void	AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, sint8 charges, bool equipit, bool wearchange = false);
	void	DoClassAttacks(Mob *target);
	void	CheckSignal();
	
	//waypoint crap
	int		GetMaxWp() const { return max_wp; }
	void				DisplayWaypointInfo(Client *to);
	void				CalculateNewWaypoint();
//	int8				CalculateHeadingToNextWaypoint();
//	float				CalculateDistanceToNextWaypoint();
	void				AssignWaypoints(int32 grid);
	void				SetWaypointPause();
	void				UpdateWaypoint(int wp_index);
	// quest wandering commands
	void				StopWandering();
	void				ResumeWandering();
	void				PauseWandering(int pausetime);
	void				MoveTo(float mtx, float mty, float mtz);
	
	int32				GetEquipment(int8 material_slot) const;	// returns item id
	sint32				GetEquipmentMaterial(int8 material_slot) const;
	
	void				NextGuardPosition();
	void				SaveGuardSpot(bool iClearGuardSpot = false);
	inline bool			IsGuarding() const { return(guard_heading != 0); }
/*	void				SaveSpawnSpot();
	inline const float	GetSpawnX() const { return spawn_x; }
	inline const float	GetSpawnY() const { return spawn_y; }
	inline const float	GetSpawnZ() const { return spawn_z; }
	inline const float	GetSpawnHeading() const { return spawn_heading; }
	*/
	void				AI_SetRoambox(float iDist, float iRoamDist, int32 iDelay = 2500);
	void				AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, int32 iDelay = 2500);
	
	
	inline bool WillAggroNPCs() const { return(npc_aggro); }
	
	inline void GiveNPCTypeData(NPCType *ours) { NPCTypedata_ours = ours; }
	inline const int32 GetNPCSpellsID()	const { return npc_spells_id; }
	
	ItemList	itemlist; //kathgar - why is this public?  Doing other things or I would check the code
	
	NPCProximity* proximity;

	Spawn2*	respawn2;

	AA_SwarmPetInfo *GetSwarmInfo() { return (swarmInfoPtr); }
	void SetSwarmInfo(AA_SwarmPetInfo *mSwarmInfo) { swarmInfoPtr = mSwarmInfo; }

	sint32	GetAccuracyRating() { return (accuracy_rating); }
	void	SetAccuracyRating(sint32 d) { accuracy_rating = d;}

protected:
	
	const NPCType*	NPCTypedata;
	NPCType*	NPCTypedata_ours;	//special case for npcs with uniquely created data.

	friend class EntityList;
	list<struct NPCFaction*> faction_list;
	uint32	copper;
	uint32	silver;
	uint32	gold;
	uint32	platinum;
	int32   grid;
	int32   spawn_group;
	int16	wp_m;

	sint32	npc_faction_id;
	sint32	primary_faction;
	
	Timer	attacked_timer;		//running while we are being attacked (damaged)
	Timer	combat_event_timer;	//running while we are engaged in offensive or defensive combat activities
    Timer	swarm_timer;
    Timer	classattack_timer;
	Timer	knightattack_timer;
    Timer	assist_timer;		//ask for help from nearby mobs
	Timer	global_position_update_timer;

	bool	attack_event;	//true if we have fired an EVENT_ATTACK and our attacked timer has not gone off
	bool	combat_event;	//true if we have fired an EVENT_COMBAT and our combat activity timer has not gone off

//	int8	position;	// 0 - Standing, 1 - Sitting, 2 - Crouching, 4 - Looting
    Timer	sendhpupdate_timer;
	Timer	enraged_timer;

	int32	npc_spells_id;
	struct AISpells_Struct {
		int16	type;			// 0 = never, must be one (and only one) of the defined values
		uint16	spellid;		// <= 0 = no spell
		sint16	manacost;		// -1 = use spdat, -2 = no cast time
		int32	time_cancast;	// when we can cast this spell next
		sint32	recast_delay;
		sint16	priority;
	};
	int8	casting_spell_AIindex;
	Timer*	AIautocastspell_timer;
	int32*	pDontCastBefore_casting_spell;
	AISpells_Struct	AIspells[MAX_AISPELLS]; // expected to be pre-sorted, best at low index
	void AddSpellToNPCList(AISpells_Struct* AIspells, sint16 iPriority, sint16 iSpellID, uint16 iType, sint16 iManaCost, sint32 iRecastDelay);
	bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
	void AIDoSpellCast(int8 i, Mob* tar, sint32 mana_cost, int32* oDontDoAgainBefore = 0);
	
	
	int16	max_dmg;
	int16	min_dmg;
	sint32	accuracy_rating;
	
	//pet crap:
	int16	pet_spell_id;
	bool	taunting;
    Timer	taunt_timer;		//for pet taunting
	
	bool npc_aggro;
	
	int		signal_id;
	bool	signaled;	// used by quest signal() command
	
	//waypoint crap:
	//MyList <wplist> Waypoints;
	vector<wplist> Waypoints;
	void _ClearWaypints();
	int		max_wp;
	int		save_wp;
    float guard_x, guard_y, guard_z, guard_heading;
//    float spawn_x, spawn_y, spawn_z, spawn_heading;
	float roambox_max_x;
	float roambox_max_y;
	float roambox_min_x;
	float roambox_min_y;
	float roambox_distance;
	float roambox_movingto_x;
	float roambox_movingto_y;
	int32 roambox_delay;
	
	int16   skills[HIGHEST_SKILL+1];
	int32   equipment[MAX_MATERIALS];	//this is an array of item IDs
	int16	d_meele_texture1;		//this is an item Material value
	int16	d_meele_texture2;		//this is an item Material value (offhand)

	AA_SwarmPetInfo *swarmInfoPtr;
This issue is hanging me up from adding disciplines to NPCs to make some interesting encounters. I know I could probably do most of it via quests if I wanted, but these are mainly for my trash mobs. I am trying to make trash mob fights a little more dynamic.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #10  
Old 07-25-2008, 06:04 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

After more testing, it seems like recast delay is working properly, but is picky about what kind of spells you set to which type:

From MobAI.cpp:
Code:
const int SpellType_Nuke=1;
const int SpellType_Heal=2;
const int SpellType_Root=4;
const int SpellType_Buff=8;
const int SpellType_Escape=16;
const int SpellType_Pet=32;
const int SpellType_Lifetap=64;
const int SpellType_Snare=128;
const int SpellType_DOT=256;
This list includes most of the spell types people might like to use for NPCs, but it seems like some just don't fit in anywhere. I would like to add a new one for in combat buffs to be used for spells like Yaulp and for Melee Disciplines. Yaulp works ok as a normal buff, but it is a little bit pointless to have NPCs constantly recasting that spell on themselves. They really should only use it during combat, but there currently isn't any way for that to work properly. The main reason I want to add this new category is for Disciplines.

It seems that when you set a discipline to type 1 (nuke), it will cast properly, but since it is self only, it apparently doesn't pass all of the checks for being a "nuke" spell. So, I think the problem is that it is never reaching the point where it is supposed to check the recast delay setting. Maybe the nuke one could just be edited to allow this, or a whole new type could be added:

Code:
const int SpellType_Combat_Buff=512;
Then, I think that would need to be added here in the MobAI.cpp:
Code:
const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Combat_Buff;
const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet;
Looking at some of the examples, we have:

Buff
Code:
					case SpellType_Buff: {
						if (
							(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
							&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
							&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
							&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
							&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar)	//no buffing PC's pets, but they can buff themself
							) {
							AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
							return true;
						}
						break;
					}
Nuke
Code:
					case SpellType_Nuke: {
						if (
							manaR >= 40 && (rand()%100) < 50
							&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
							) {
							if(!checked_los) {
								if(!CheckLosFN(tar))
									return(false);	//cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
								checked_los = true;
							}
							AIDoSpellCast(i, tar, mana_cost);
							return true;
						}
						break;
					}
And, I am just guessing that putting Disciplines in Nuke is failing and going to the following:

Code:
					default: {
						cout<<"Error: Unknown spell type in AICastSpell. caster:"<<this->GetName()<<" type:"<<AIspells[i].type<<" slot:"<<i<<endl;
						break;
					}
But, the actual checks after a successful cast seem to be here:
Code:
	//this gets called from InterruptSpell() for failure or SpellFinished() for success
	void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
		if (slot == 1) {
			int32 recovery_time = 0;
			if (iCastSucceeded) {
				if (casting_spell_AIindex < MAX_AISPELLS) {
						recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
						if (AIspells[casting_spell_AIindex].recast_delay >= 0){
							if (AIspells[casting_spell_AIindex].recast_delay <1000)
								AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
	}
						else
							AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
				}
				if (!IsEngaged())
					recovery_time += RandomTimer(2000, 3000);
				if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
					recovery_time = AIautocastspell_timer->GetSetAtTrigger();
				AIautocastspell_timer->Start(recovery_time, false);
			}
			else
				AIautocastspell_timer->Start(800, false);
			casting_spell_AIindex = MAX_AISPELLS;
		}
}
So, I think that the only other thing that might need to be added is a new section for Combat_Buffs. Though, I am not sure if I need to add anything somewhere else to get it to work. I think that by copying the settings for buff and calling it Combat_Buff, and then adding Combat_Buff to detrimental spells, it will allow for NPCs to only cast these types of buffs during combat, which is the opposite from normal buffs.

Code:
					case SpellType_Combat_Buff: {
						if (
							(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
							&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
							&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
							&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
							&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar)	//no buffing PC's pets, but they can buff themself
							) {
							AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
							return true;
						}
						break;
					}
I guess I will test this out on my server and just cross my fingers that I don't break anything lol. I am such a gimpy coder, but this makes at least a little sense to me... If it works, I will post it in server code submissions with better details on how to add this code in, even though everything should already be here.

If anyone else has any input on getting this to work the way I want, I would love to hear it
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #11  
Old 07-25-2008, 06:27 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Just found 3 more lines that I think needed to be adjusted for it to work:

Code:
	   ||	AIspells[i].type == SpellType_Combat_Buff
Code:
				if(!AICastSpell(target, 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Combat_Buff)) {
Code:
		if(!AICastSpell(target, 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Combat_Buff)) {
Compiling it now and will post if it works or not. Crossing my fingers.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #12  
Old 07-25-2008, 07:27 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Well, it compiled fine, but now when I set my NPCs spells to type 512, it doesn't seem to use them at all in combat or out of combat... Guess this needs further looking into, but I think it is closer now heh.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #13  
Old 07-26-2008, 10:50 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Well, I didn't have to add a new category after-all lol. I found the reason why my recast delays weren't working. It was because I was using recast timers similar to actual disc reuse timers. Some of them were 20 minutes or more. And in seconds, that is 1200.

I looked at recast delay settings in the source and I found this:

MobAI.cpp
Code:
void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
	if (slot == 1) {
		int32 recovery_time = 0;
		if (iCastSucceeded) {
			if (casting_spell_AIindex < MAX_AISPELLS) {
					recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
					if (AIspells[casting_spell_AIindex].recast_delay >= 0){
						if (AIspells[casting_spell_AIindex].recast_delay <1000)
							AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
}
					else
						AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
			}
			if (!IsEngaged())
				recovery_time += RandomTimer(2000, 3000);
			if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
				recovery_time = AIautocastspell_timer->GetSetAtTrigger();
			AIautocastspell_timer->Start(recovery_time, false);
		}
		else
			AIautocastspell_timer->Start(800, false);
		casting_spell_AIindex = MAX_AISPELLS;
	}
}
And I simply changed the check for < 1000 to < 10000 and now my recast delays are working perfectly. Here is the change I made, highlighted in RED:

Code:
void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
	if (slot == 1) {
		int32 recovery_time = 0;
		if (iCastSucceeded) {
			if (casting_spell_AIindex < MAX_AISPELLS) {
					recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
					if (AIspells[casting_spell_AIindex].recast_delay >= 0){
						if (AIspells[casting_spell_AIindex].recast_delay <10000)
							AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
}
					else
						AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
			}
			if (!IsEngaged())
				recovery_time += RandomTimer(2000, 3000);
			if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
				recovery_time = AIautocastspell_timer->GetSetAtTrigger();
			AIautocastspell_timer->Start(recovery_time, false);
		}
		else
			AIautocastspell_timer->Start(800, false);
		casting_spell_AIindex = MAX_AISPELLS;
	}
}
I don't see any reason why this couldn't be added to the source updates. I am not sure why the current limit was set to 1000 or less. I guess it wouldn't even apply in most cases, but disciplines for pets and NPC encounters that might last 20+ minutes could be effected by this.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #14  
Old 09-02-2008, 09:31 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Well, I am starting again to get the new spell type category working for NPCs. I haven't fully decided if I want to just make 1 new category or 2. I think Yaulp would be fine to set as a buff that is used in combat, but disciplines need a little extra rules to them. I currently have my NPC disciplines set to the nuke category and that almost works the way that I want with the only problem being that they will stack multiple discs at once which makes them way too overpowered. The idea is to make a new category just for discs that will do combat buffs, but only do 1 at a time and won't use another until the previous one has worn off.

I think the code I posted above for combat_buffs is probably very close to working for this. I just need to figure out why the new category wasn't being used when I set it for an NPC in their spell list. I imagine I am only missing something minor to get it to start using them. Then, the only other thing I would need is a way to make sure only 1 combat buff (disc) is being used at a time.

Of course since KLS added a new field for dispells and it is 512, then the new discipline field would have to use 1024. And if I added a combat_buff field as well for Yaulp and maybe others, it would have to be 2048.

If anyone has time to look over my code, I think these would make for some nice new features to help expand what NPCs can do without having to make quest scripts for every mob.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #15  
Old 09-02-2008, 10:03 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Sir, get out of my head. I was seriously just thinking about expanding the npc cast system further with in combat buffs classification only like 2 days ago.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 10:09 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3