Removing Objects

Post » Fri May 27, 2011 9:10 am

Probably a silly question. For some reason I can't seem to find any answer.


Is there any way for a scripted object to remove itself from an actor's inventory, without calling a second object/script? I've tried it a few different ways and they all seem to CTD -- the alternative methods that I could think of, such as calling a targeted global script to remove the object, are a "last resort" that I'd rather not use unless I have to. Anyone know of a good trick?
User avatar
anna ley
 
Posts: 3382
Joined: Fri Jul 07, 2006 2:04 am

Post » Fri May 27, 2011 8:47 am

I think you're stuck with another object or a global... Globals aren't so bad though if you run startscript from your object to remove then have stopscript in the global script (if resources are your concern).
User avatar
Natalie Harvey
 
Posts: 3433
Joined: Fri Aug 18, 2006 12:15 pm

Post » Fri May 27, 2011 8:22 am

A tiny run-once global like that isn't going to hurt anything. It would run for only one frame, remove the stuff, and stop itself. Done and gone. I've run constantly-running MWSE global scripts that would make your head bleed if you looked at the scripting in them, and have very little FPS hit on a computer almost as old as Morrowind itself. :P

I guess what I'm getting at is, don't fear the globals. Globals are your friend.
User avatar
katsomaya Sanchez
 
Posts: 3368
Joined: Tue Jun 13, 2006 5:03 am

Post » Fri May 27, 2011 8:26 pm

Is there any way for a scripted object to remove itself from an actor's inventory, without calling a second object/script? I've tried it a few different ways and they all seem to CTD -- the alternative methods that I could think of, such as calling a targeted global script to remove the object, are a "last resort" that I'd rather not use unless I have to. Anyone know of a good trick?

What is wrong with using a global script?

If you say it is a messy solution that makes the mod more complex then I will agree with you. However it is such a common method that everyone (that includes yourself in a months time) should understand what it does and it is the only general method available.

If you say it is because it is because global scripts should be avoided, then I direct you at this quote and suggest that you only optimize when you have a problem (Because most of the time what you think is the problem isn't)

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" -- http://en.wikipedia.org/wiki/Donald_Knuth
User avatar
Adam Kriner
 
Posts: 3448
Joined: Mon Aug 06, 2007 2:30 am

Post » Fri May 27, 2011 8:15 pm

Thanks for the advice, guys. I suppose I'll go ahead and use the global targeted script (the least-bad option =P ).


The main concern I had wasn't even elegance (my scripts are all pretty inelegant, but get the job done...), but rather, that a targeted script can only be running on one object at a time. It seems like in what I'm testing, multiple objects could be requesting the targeted script all at once -- or nearly at the same time. I suppose I'll see if it becomes an issue or not.
User avatar
Genevieve
 
Posts: 3424
Joined: Sun Aug 13, 2006 4:22 pm

Post » Fri May 27, 2011 8:08 pm

Instead of relying on the script being targeted, use "item_id"->RemoveItem # and test for conditions to remove each item if you need to run the script more than once, or on several objects at the same time.
User avatar
[ becca ]
 
Posts: 3514
Joined: Wed Jun 21, 2006 12:59 pm

Post » Fri May 27, 2011 9:59 am

The main concern I had wasn't even elegance (my scripts are all pretty inelegant, but get the job done...), but rather, that a targeted script can only be running on one object at a time. It seems like in what I'm testing, multiple objects could be requesting the targeted script all at once -- or nearly at the same time. I suppose I'll see if it becomes an issue or not.


On method to get around this would be the following.

begin foo    short doRemoveItem    if ( doRemoveItem )        if ( scriptRunning myRemoveItemScript == 0 )            startscript myRemoveItemScript        endif        return     endif        ;rest of scriptend


That way when you want to remove the item you just set the flag (doRemoveItem) to 1. It is slightly slower but maybe more robust.
User avatar
Carlos Rojas
 
Posts: 3391
Joined: Thu Aug 16, 2007 11:19 am

Post » Fri May 27, 2011 8:11 pm

I settled upon a variation on that method -- and it seems like this will work fairly well. Thanks all =)



On a tangentially related note... rather than flooding the CS forum with threads, could anyone better explain to me the operation of the MWSE function xGetOwner? In this case, suppose actor A has item B contained within their inventory. If a local script on item B calls xGetOwner, will a reference to actor A be returned?

If not, is there any other way to determine the reference of the actor whose inventory contains object B?



MWSE has some nice functions but not everything is as intuitive as regular Morrowind scripting for me. :(
User avatar
NeverStopThe
 
Posts: 3405
Joined: Tue Mar 27, 2007 11:25 pm

Post » Fri May 27, 2011 9:24 pm

I'm going to take a wild guess and say that xGetOwner probably doesn't work for objects in inventory just like xGetCondition doesn't. I believe all those functions only work for items placed in the game world.

What exactly are you trying to do? We might have some more helpful suggestions if we knew what you wanted to accomplish. :)
User avatar
Lisa Robb
 
Posts: 3542
Joined: Mon Nov 27, 2006 9:13 pm

Post » Fri May 27, 2011 5:47 pm

A shame that it doesn't work that way; I wonder if any future development for MWSE is planned...



To break down what I've been trying to do (which won't work given that xGetOwner won't work the way I initially thought), I am trying to find a MWSE substitute for ExplodeSpell in my Elemental Maigcka mod. ExplodeSpell has gotten me some fairly good results, but has a few problems with propagating spell effects: it doesn't cause enemies to attack me, and it has to display the visual effects appropriate to the effect that I'm propagating. Furthermore, anything more advanced than a spell effect cannot really be unleashed this way.


The plan for augmenting/upgrading my current work was to see if I could develop a "token" system. Objects that I would traditionally have used ExplodeSpell on to generate a spell effect would instead check for actors within a specific radius, and if they didn't have the token for that spell already in inventory, add one to them. The token would identify the reference of the actor whose inventory it was residing within, and apply either some sort of scripted behavior or add an Ability spell to do the job of the spell hitting them. After a certain duration, the token would retract its effects and be removed from inventory. I thought that this could work reasonably well... but it won't work given my misunderstanding of xGetOwner. A bit indirect but it seemed like it would be a versatile system that could scale to any number of targets easily.

Is there any hope for saluaging the idea? I've been scratching my head over this one for a few days but I can't seem to think of good alternatives. The main appeal of the token approach, to me, was that each token could have a copy of the same local script that would affect the proper actors -- whereas doing anything through global/targeted scripts would have greater difficulty in dealing with multiple targets at the same time (for area-effect spells).
User avatar
Lizzie
 
Posts: 3476
Joined: Sun Nov 19, 2006 5:51 am

Post » Fri May 27, 2011 2:27 pm

You could use MWSE to do something like that. What you would need to do is cycle through the cell for npcs, and make a GetDistance check from the player (is the area effect going to be centered on the player?)

Here's a version of a script I did using reference checks to see if npcs had been changed into rocks by a scripted spell. I use token items to track if the npc is a rock or not:

Begin Uvi_Rock_CellCheck; global script, started from first equip of the new rock wand; cleans up rock items from dead npcs, and adds memitems removed in target scriptShort stateShort doOnceShort framecountShort RandLong tmpLong tmp2Long npcRefLong npctargetFloat notransif ( MenuMode == 1 )   returnendifif ( doOnce == 0 )   set uvi_rocknpc to -99 ;disables old rock dialogue still present in main mod   set uvi_rockused to 0 ;disables old pre-greetings still present in main mod   set doOnce to 1endifif ( framecount < 20 ) ;using a framecounter instead of timer because timer was too sluggish   set framecount to ( framecount + 1 )   returnendififx ( npcRef )   setx npcRef to xNextRef npcRef ;finds the next NPC or creature in the cellelse   setx npcRef to xFirstNPC ;finds the first NPC or creature in the cellendifif ( npcRef == 0 )   ;end of list or empty cell   set framecount to 0   returnendifsetx tmp to npcRef->xRefTypeif ( tmp != 1598246990 ) ;if not type "NPC_" - presumably a creature   set framecount to 0   returnendifsetx npctarget to xGetPCTargetif ( npctarget == npcRef ) ;if npc is targeted, transformation is handled by other script so return	set framecount to 0	returnendifxSetRef npcRefset tmp to ( GetItemCount "uvi_memitem_rock" )if ( tmp )   set state to 1 ;this NPC is already a rockelse   set state to 0 ;this NPC is not a rockendif;remove spell that temporarily replaces item, and replace item after distance checkxSetRef npcRefset tmp to ( GetSpell "uvi_rock_memory" )xSetRef npcRefif ( GetDistance Player >= 100 )   set tmp2 to 1endififx ( tmp )   ifx ( tmp2 )      npcRef->RemoveSpell "uvi_rock_memory"      npcRef->xAddItem "uvi_memitem_wasrock" 1      set tmp2 to 0   endifendif      ifx ( state ) ;npc is a rock	xSetRef npcRef	set tmp to GetHealth	if ( tmp > 0 ) ;npc is alive, so don't clean up		set framecount to 0		return	endif	xSetRef npcRef	set tmp to ( GetItemCount "uvi_rock_lgauntlet" )	ifx ( tmp )		npcRef->xRemoveItem "uvi_rock_lgauntlet" tmp		set tmp to ( tmp - tmp )	endif	xSetRef npcRef	set tmp to ( GetItemCount "uvi_invisible_rgauntlet" )	ifx ( tmp )		npcRef->xRemoveItem "uvi_invisible_rgauntlet" tmp		set tmp to ( tmp - tmp )	endif	xSetRef npcRef	set tmp to ( GetItemCount "uvi_invisible_robe" )	ifx ( tmp )		npcRef->xRemoveItem "uvi_invisible_robe" tmp		set tmp to ( tmp - tmp )	endif; now equip dummy armor and clothing to refresh their inventory; Armor; Apart from a conflict between gauntlets and bracers, all armors can be worn together	npcRef->xAddItem "gp_dummy_boots" 1	npcRef->xRemoveItem "gp_dummy_boots" 1	npcRef->xAddItem "gp_dummy_cuirass" 1	npcRef->xRemoveItem "gp_dummy_cuirass" 1	npcRef->xAddItem "gp_dummy_greaves" 1	npcRef->xRemoveItem "gp_dummy_greaves" 1	npcRef->xAddItem "gp_dummy_helm" 1	npcRef->xRemoveItem "gp_dummy_helm" 1	npcRef->xAddItem "gp_dummy_gauntlet_left" 1            ; gauntlets more dominant than bracers	npcRef->xRemoveItem "gp_dummy_gauntlet_left" 1	npcRef->xAddItem "gp_dummy_leftbracer" 1	npcRef->xRemoveItem "gp_dummy_leftbracer" 1	npcRef->xAddItem "gp_dummy_pauldron_left" 1	npcRef->xRemoveItem "gp_dummy_pauldron_left" 1      ; pauldron can be worn with gauntlets/bracers so dont effect them	npcRef->xAddItem "gp_dummy_gauntlet_right" 1         ; gauntlets more dominant than bracers	npcRef->xRemoveItem "gp_dummy_gauntlet_right" 1	npcRef->xAddItem "gp_dummy_rightbracer" 1	npcRef->xRemoveItem "gp_dummy_rightbracer" 1	npcRef->xAddItem "gp_dummy_pauldron_right" 1	npcRef->xRemoveItem "gp_dummy_pauldron_right" 1	npcRef->xAddItem "gp_dummy_towershield" 1	npcRef->xRemoveItem "gp_dummy_towershield" 1; Weapon	npcRef->xAddItem "silver dagger" 1	npcRef->xRemoveItem "silver dagger" 1; Clothing; All these 10 clothing items can be worn with each other	npcRef->xAddItem "uv_reequipc_amulet" 1            ; even clothing types without bodyparts must still be pushed off as these items are still equipped so arnt seen in inventory without being pushed off	npcRef->xRemoveItem "uv_reequipc_amulet" 1	npcRef->xAddItem "uv_reequipc_belt" 1	npcRef->xRemoveItem "uv_reequipc_belt" 1	npcRef->xAddItem "uv_reequipc_leftglove" 1	npcRef->xRemoveItem "uv_reequipc_leftglove" 1	npcRef->xAddItem "uv_reequipc_pants" 1	npcRef->xRemoveItem "uv_reequipc_pants" 1	npcRef->xAddItem "uv_reequipc_rightglove" 1	npcRef->xRemoveItem "uv_reequipc_rightglove" 1	npcRef->xAddItem "uv_reequipc_ring" 1	npcRef->xRemoveItem "uv_reequipc_ring" 1	npcRef->xAddItem "uv_reequipc_robe" 1	npcRef->xRemoveItem "uv_reequipc_robe" 1	npcRef->xAddItem "uv_reequipc_shirt" 1	npcRef->xRemoveItem "uv_reequipc_shirt" 1	npcRef->xAddItem "uv_reequipc_shoes" 1	npcRef->xRemoveItem "uv_reequipc_shoes" 1	npcRef->xAddItem "uv_reequipc_skirt" 1	npcRef->xRemoveItem "uv_reequipc_skirt" 1	xSetRef npcRef	ModDisposition 40	PlaySound "mysticism hit"	xSetRef npcRef	set tmp to ( GetItemCount "uvi_memitem_wasrock" ) ;if npc is dead, remove memitems	ifx ( tmp )		npcRef->xRemoveItem "uvi_memitem_wasrock" tmp		set tmp to ( tmp - tmp )	endif	xSetRef npcRef	set tmp to ( GetItemCount "uvi_memitem_rock" ) ;if npc is dead, remove memitems	ifx ( tmp )		npcRef->xRemoveItem "uvi_memitem_rock" tmp		set tmp to ( tmp - tmp )	endif	set state to 0	set framecount to 0endifEnd


This is only one half of the scripting though, the other half performs the transformation by using XGetpCTarget and checks that the rock spell has been cast on the npc before transforming them. The original spell used this script to make that check, but just a warning, it was a bit too slow on the uptake in exterior cells because it had too many objects to check for (and the framecount made it even slower). That is why I use the targeting script in conjunction with this one. However, I use the framecount because the script is constantly running in the background checking for npc rocks. If you are triggering it and stopping the script after it's completed, then you don't need a framecounter to mitigate fps loss.
User avatar
emma sweeney
 
Posts: 3396
Joined: Fri Sep 22, 2006 7:02 pm

Post » Fri May 27, 2011 3:51 pm

Thanks for the example to chew over; I'll have to give this a try.


A related question, but in a more general sense of MWSE mod development: is it safe to go back and forth from the CS (editing objects, interiors, scripts, etc) and MWEdit (creating/changing scripts, etc) when some of the scripts have MWSE commands in them? To be more specific, if I create a script and compile it with MWSE commands in MWEdit, is it safe to then open that mod with the regular CS and do whatever I need to? If not, will opening MWEdit and recompiling the MWSE script make it okay again?

Just curious about how much flexibility I have before I stop making little test ESPs and start using MWSE in my main file.
User avatar
Heather Stewart
 
Posts: 3525
Joined: Thu Aug 10, 2006 11:04 pm

Post » Fri May 27, 2011 7:24 pm

You only need to compile MWSE scripts and save them in MWEdit. The rest can safely be done in the CS, and you don't need to recompile the MWSE scripts when you save a mod in the CS. Just don't touch the MWSE scripts in the CS and you will be fine. Some regular scripting MWEdit has some problems with, so if you're not using MWSE functions in a specific script, and it won't compile in MWEdit, then compile it in the CS instead.
User avatar
Cody Banks
 
Posts: 3393
Joined: Thu Nov 22, 2007 9:30 am


Return to III - Morrowind