Aller au contenu

Photo

Set Item Script - Suggestions?


  • Veuillez vous connecter pour répondre
16 réponses à ce sujet

#1
Artevere

Artevere
  • Members
  • 36 messages
 So, I'm plugging away on a Set Item Script for a PW I play on. Problem is, without arrays, I need to build a switch case inside a struct to pass values.

Each item set will have different bonuses. I don't want to lock the team into making sets that only add a specific number of bonuses per items equipped, so one set may give 3, 4, then 5 bonuses, and another may give 2, then 5 bonuses. 

A loop is a quick (though expensive) solution. I could also execute code directly inside the case switch.

/*************************************************************Evezial's Set Item ScriptThis script allows set items to apply a bonus based onthe number of set items currently equipped. It worksby running through each equpment slot whenever a setitem is equipped. For each additional item found, itapplies the bonus effect/s.
When items are unequipped, the effects will be removed.This requires each set to be entered onto the ez_setitem_geartable, along with the bonus it applies.
Each set will need its own script for unique bonusesthat will be applied. This script simply applies thebonuses related to the sets. Also, each item will needto have the proper local variables set.
Evezial Hall03132011**************************************************************/#include "ez_setitem_gear"


void main(){object oPC = GetPCItemLastEquippedBy(); 				//PC who equipped the item.object oItem = GetPCItemLastEquipped();					//Item being equipped.object oInvItem;										//Item currently being checked in player's inventory.
string sItemLocString = GetLocalString(oInvItem);		//Local string variable of item being checked.	string sKitLocString = GetLocalString(oItem, sKitID);		//Local string variable of the set.

int iSlot;												//Inventory slots (all 17 of them!).int iSetNum;										//Number of set items currently equipped.
//Compares the local string var of each item and adds up the matches.for(iSlot=0; iSlot < 18; iSlot++)	{		GetItemInSlot(iSlot, oPC);		if(sKitLocString == sItemLocString)			iSetNum++;	}	//Make certain that the 	if (iSetNum < 2)return;
		//Applies bonus to PCeffect iBonus = eItemSetBonus(sKitLocString, iSetNum, oPC);
}

And this is where the set items are defined:

/********************************************************************This script is the collection of all the set items on the server.As additional items are created, they must be added and programmedhere.*********************************************************************/
void eItemSetBonus(string sKitID, int iSetNum, object oPC){int iBonus = iSetNum - 2;		//Switch Case has to start at 0, so we subtract 2 from the iSetNum result, giving us a range from 0 to 4 for all items.
//These store up to 6 seperate effects that can be granted by the set items. More can be added if necessary.effect Effect1;effect Effect2;effect Effect3;effect Effect4;effect Effect5;effect Effect6;effect eLinked;
if(sKitID == "BigCleaver")	{	switch(iBonus)		{		case 0 : Effect1 = EffectAbilityIncrease(ABILITY_CHARISMA, 1); break;		case 1 : Effect1 = EffectAbilityIncrease(ABILITY_CHARISMA, 2); break;		case 2 : Effect1 = EffectAbilityIncrease(ABILITY_CHARISMA, 2);				 Effect2 = EffectAbilityIncrease(ABILITY_STRENGTH, 1);				 eLinked = EffectLinkEffects(Effect1, Effect2);				 break;		case 3 : Effect1 = EffectAbilityIncrease(ABILITY_CHARISMA, 2);				 Effect2 = EffectAbilityIncrease(ABILITY_STRENGTH, 2);				 eLinked = EffectLinkEffects(Effect1, Effect2); 				 break;		case 4 : Effect1 = EffectAbilityIncrease(ABILITY_CHARISMA, 2);				 Effect2 = EffectAbilityIncrease(ABILITY_STRENGTH, 2);				 Effect3 = EffectHaste();				 eLinked = EffectLinkEffects(Effect1, Effect2, Effect3);				 break;		}	}	//Next IF goes here.
//Link and apply effectseLinked = EffectLinkEffects(eLinked);eLinked = SupernaturalEffect(eLinked);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLinked, oPC);;
}

I think I like the idea of just linking and applying the effect directly in the switch case, so I don't have to worry about making a variable number of ApplyEffect functions in a loop.

My second, larger problem, is removing the effects if a set item is unequpped. My first though would be to remove all status effects, then reapply them as if there were one fewer of the set equipped. This could easily be exploited to get rid of curses and such.

My second thought was to only remove effects that are both permanent and supernatural (required so the abilities don't disappear on rest or disenchant). I'm not familiar enough with DnD to know if there are any castable spells that match this. On top of that, it's REALLY expensive to run that loop. My very powerful machine running a local game paused for almost a second while the loop ran.

Any ideas how to make it less intrusive and more reliable?

void main(){object oPC = GetLastOpenedBy();
effect eEffect = GetFirstEffect(oPC);
while (GetIsEffectValid(eEffect))	{     if (GetEffectDurationType(eEffect) == DURATION_TYPE_PERMANENT && GetEffectSubType(eEffect) == SUBTYPE_SUPERNATURAL)	 	 {	          RemoveEffect(oPC, eEffect);    	 }	}     eEffect = GetNextEffect(oPC);	}


#2
Lugaid of the Red Stripes

Lugaid of the Red Stripes
  • Members
  • 955 messages
As for removing effects, with my terrain triggers I cycle through the creature's effects and examine each effect's creator. When it's OBJECT_SELF, i.e. the trigger applying and removing the effects, I remove it. You could do something similar, finding a particular object to run your script on that doesn't apply any other effects, maybe one of the items in question.

#3
Artevere

Artevere
  • Members
  • 36 messages
Script has to run on UnEquip. If it only compares a single parameter, it seems to be fine. When I have two (Permanent AND supernatural) it sucks up a lot of cycles.

The problem with using effect creator is that any buffs they put on themselves is in the same category. So as soon as an item is unequipped, they lose all their buffs...

Hmm, I wonder if there is a way to set the creator to a local variable and use that instead.

#4
Artevere

Artevere
  • Members
  • 36 messages
Actually, just reread the comments about GetEffectCreator and how it determines who the creator is. It badly needs to be edited, but I *think* it's saying any object that created the script is set as the creator, which would allow me to simply match the local string variable on the item set. Hrm.

#5
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
That is correct Artevere. You could assign the creation of the effects to one specific object that is used only for the sets if you wanted. Then just check that object was the creator..if so remove.

#6
dethia

dethia
  • Members
  • 146 messages
Since you are working in a PW you can use nwnx to simulate arrays. Of course the question here is which is more resource efficient. I will give a very brief and minor example that perhaps give you the jist of the process.

Let's say I have hypothetically 2 values,

int DogRank, and float Size. I want to sort the dog ranks by their size. That is right now dog 1 has a size of 2.0, while dog 2 has a size of 3.0 and thus should clearly be ranked above dog 1 and dog 2 needsto move down. So I create a temporary table with 3 columsn, the first column is the array index, the second column is the DogRank (dog 1, dog 2 dog 3 etc..) and the 3rd column is the corresponding dog size. The array index is populated by a variable and will go from either 0 to n or from 1 to n (that's up to you).

Heh I hope this isn't too confusing. First we populate the temporary table as is and it would look something like this

1 | Dog1 | 2.0
2 | Dog2 | 4.0
3 |Dog3| | 3.5

We run the following command to then get the respective rank for a particular dog

string	sDogRank	= IntToString(DOG_RANK_VALUE);
select row from (select @row:=@row+1 row, p.*
								from TEMP_TABLE p, (SELECT @row:=0) r
								order by Size desc) as TEMP_TABLE
								where dog_rank = sDogRank;

Iti s a bit hard to see it in the context but, long story short is that in a PW with nwnx support you can emulate arrays using either temporary or permanent tables.

#7
Artevere

Artevere
  • Members
  • 36 messages
So, basically use the database engine attached to NWNX to create virtual arrays? Hmm. I hadn't thought of that. I think I have a quicker solution (in terms of resource use), but I'll probably be using this idea in the near future. Thanks :)

#8
Artevere

Artevere
  • Members
  • 36 messages
Hmm, something is definitely wrong with my loop here. I know the first portion works from the OnEquip version. It runs through and adds everything properly. However, this script dies the moment it hits the "while(GetIsEffectValid(eEffect))" Not certain why. It looks like like any other nested loop out there:




#include "ez_setitem_items"

void main()

{
object oPC = GetPCItemLastUnequippedBy(); 					//PC who equipped the item.
object oItem = GetPCItemLastUnequipped();					//Item being equipped.

//if this is not a set item, stop script.
int iItemSetCheck = GetLocalInt(oItem, "IsSet");
if(iItemSetCheck != 1)return;

object oInvItem;
object oEffectCreator;

string sItemLocString;										//Local string variable of item being checked.	
string sKitLocString = GetLocalString(oItem, "sKitID");		//Local string variable of the item set.
string sInvItem;											//Item in player inventory that is currently being checked.
string sSetNumEcho;

int iSlot;													//Inventory slots (all 17 of them!).
int iSetNum;												//Number of set items currently equipped.

effect eEffect;

SendMessageToPC(oPC, "UnEquip Variables Initialized");

//starts loop to scan inventory for item information
while(iSlot < NUM_INVENTORY_SLOTS)
	{
		oInvItem = GetItemInSlot(iSlot, oPC);					//Locks each inventory item in oInvItem
		sItemLocString = GetLocalString(oInvItem, "sKitID");	//Checks the local string variable of item
			if(sKitLocString == sItemLocString)					//compares the item variable to the set variable
			{
			iSetNum++;											//if it is an item in the set, add 1.
			}

//starts loop to peel bonus effects for removing an item
		SendMessageToPC(oPC, "Starting cleaning loop...");	
		
		eEffect = GetFirstEffect(oPC);			
		
		while(GetIsEffectValid(eEffect))				
		{
     		if (GetEffectCreator(eEffect) == oInvItem)
	 	 		{	
          		RemoveEffect(oPC, eEffect);
				SendMessageToPC(oPC, "Effect Removed");	
    			}
		}
		SendMessageToPC(oPC, "looking for another effect");	
       eEffect = GetNextEffect(oPC);
	}

sSetNumEcho = IntToString(iSetNum);
SendMessageToPC(oPC, sSetNumEcho);	

SendMessageToPC(oPC, "Making certain at least 2 items remain");	
if(iSetNum < 2)return;

SendMessageToPC(oPC, "Reapplying existing bonuses");
eItemSetBonus(sKitLocString, iSetNum, oPC);		

}




#9
Artevere

Artevere
  • Members
  • 36 messages
Nevermind, got the loop working. Accidentally placed code outside the while loop. Whoops.

But, (GetEffectCreator(eEffect) == oInvItem) never equals true. The script loops 17 times and the effect remains.

Did some debug messages, and it looks like it is always outputting 0 for effect creator, and anything but for the item object.

#10
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
I suspect this has more to do with what is creating the effect. When  you equip an item and the effect is created, are you assigning the creation of the effect to the object that was equipped?

This is an example I just pulled real quick from a helm, tag based, script I made that shows the assinging the effect creation to the helmet upon equipping it and then removing it when the helm is unequipped:


#include "x2_inc_switches"
void main()
{
    int nEvent = GetUserDefinedItemEventNumber();

    if (nEvent == X2_ITEM_EVENT_EQUIP)
    {
        object oHelm = GetPCItemLastEquipped();
        object oPC = GetPCItemLastEquippedBy();
        AssignCommand(oHelm, ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectBlindness()), oPC));
    }

    if (nEvent == X2_ITEM_EVENT_UNEQUIP)
    {
        object oHelm = GetPCItemLastUnequipped();
        object oPC = GetPCItemLastUnequippedBy();
        effect eEffect = GetFirstEffect(oPC);
        while (GetIsEffectValid(eEffect))
        {
        if (GetEffectCreator(eEffect) == oHelm && GetEffectType(eEffect) == EFFECT_TYPE_BLINDNESS)
            {
                RemoveEffect(oPC, eEffect);
                return;
            }
        eEffect = GetNextEffect(oPC);
        }
    }
}


Hope this helps. Good luck.

#11
Artevere

Artevere
  • Members
  • 36 messages
Hmm. Most interesting. Would it be possible, then, to assign a static object in the work as the creator, then simply remove all effects created by the object? For example, assign a fencepost that will always exist within the module called "EffectRemover" then just use GetEffectCreator to call the fence post?

#12
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Yes. Though I'm not sure whether or not it can be a "static" object. Might need to be a non-static object but it shouldn't need to be usable.

Modifié par GhostOfGod, 20 mars 2011 - 08:58 .


#13
Artevere

Artevere
  • Members
  • 36 messages
That should actually help remove a great deal of overhead from the script. Thanks for the suggestion :D

#14
Artevere

Artevere
  • Members
  • 36 messages
Hmm, from my tests, an object (tried both static and usable types), shows as 7f000000 and the effect creator is 0.

Quite the pain, really. I'll try an inventory item that all players will be carrying.


EDIT: Well, the item and effect both share the same value, but the IF statement remains false... murf.

Modifié par Artevere, 20 mars 2011 - 09:32 .


#15
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
How/when are you creating the effect?

This would be incorrect:

object oPC = blah blah blah;
object oFence = GetObjectByTag("Fence");
effect eBlind = EffectBlindness();
AssignCommand(oFence, ApplyEffectToObject(DURATION_TYPE_PERMANENT,  eBlind, oPC));

In the case above, as soon as you define the effect it is created. And since it is not done inside the asign command, the module is actually the one that creates the blind effect.

But if you wait to define your effect inside the assign command:

object oPC = blah blah blah;
object oFence = GetObjectByTag("Fence");
AssignCommand(oFence, ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBlindness(), oPC));

This will assign the fence as the creator of the effect.

Hope it helps.

#16
Artevere

Artevere
  • Members
  • 36 messages
Ah, I see. Yeah, I was assigning all the variables outside of the command. The lexicon recommended using bind, but it isn't realy necessary. I can just apply each effect seperately within the case. Only problem is assigning the supernatural effect to it within the function.

Would: 

AssignCommand(oNullRod, ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityIncrease(ABILITY_CHARISMA, 1)), oPC)); 

work?

EDIT: It did work, but the script still doesn't. What I may do is just use the module. I think that will work for my needs anyway. I just need a global value other than the player that never changes.

Modifié par Artevere, 20 mars 2011 - 09:22 .


#17
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages
Hi GhostOfGod,

That explanation of the effect creator code was most enlightening. I had no idea that placing the EffectBlindness() part inside the Assign section made the difference of who created the effect.

One would have thought that the creator of the effect was defined at the moment of assigning rather than when the effect was defined.

Thanks!

Lance.