Papyrus: How to place questmap marker?

Post » Wed Jun 20, 2012 1:42 pm

Hi there,

how do I place a map marker (the user defined blue one) or, alternatively, a quest marker at given coordinates using papyrus?

The goal is to create a spell that will fetch the nearest unmined ore vein. FindClosestReferenceOfAnyTypeInList will return the nearest and I think using FindRandomReferenceOfAnyTypeInList withing a loop expanding the search radius will work to find an unmined nearby (if I find out how to determine if the vein is already depleted, but that's the second step).

Once I have the reference to the vein I'd like to place a kind of marker there. Player.Moveto might be the easiest but could be exploited, so I'd like something like Clairvoyance or simply a map marker.
  • Can I generate the manually placed blue map marker from within a papyrus script?
  • A workaround might be to start a quest that has a marker associated? Any advice on that?
  • Any way to place some visible effect at that coordinate, temporarily, an explosion or thunderclap or...?
Thanks in advance!
User avatar
FirDaus LOVe farhana
 
Posts: 3369
Joined: Thu Sep 13, 2007 3:42 am

Post » Wed Jun 20, 2012 9:34 pm

Make a mini-quest that is started/stopped by the spell, and make a reference alias for the ore veins (Make it Optional, you're going to fill it with the spell) - Make the quest repeatable and "allow repeated stages" - In stage 0, RegisterForSingleUpdate(2.0) and set up the OnUpdate() event in the script (after the second "Don't edit anything between these comments" comment)

In the Objectives tab, set up an objective 10, with a target of the OreVeinAlias

Event OnUpdate()	if Alias_OreVeinAlias.GetRef()		SetObjectiveDisplayed(10,True,True) ; Force objective to be displayed, even if it already was	else		SetObjectiveDisplayed(10,False)	endif	RegisterForSingleUpdate(2.0) ; checks the alias every 2 secondsEndEvent

The spell would simply Start the quest and set the reference alias to the ore vein it found, and clear the alias and stop the quest when the effect ends.
User avatar
Allison Sizemore
 
Posts: 3492
Joined: Wed Jul 19, 2006 6:09 am

Post » Wed Jun 20, 2012 4:40 pm

I think map markers are statics so they can't be moved (or at least trying to use MoveTo in the console for static objects don't work). A quest marker should be pretty easy.

Just create a reference alias with the optional flag checked. When you find an ore vein, use ForceRefTo in order to set the reference alias as the ore vein. Then create a quest objective, and set the target ref to your reference alias. Use SetObjectiveDisplayed() to turn on and off the quest marker.

To check if the ore vein is depleted, use something like:

if (MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent == 0)	;depletedelseif (MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent < 0)	;fullelse	Debug.Notification((MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent + "ores left.")endif

ninja'd :ninja:
User avatar
Ricky Meehan
 
Posts: 3364
Joined: Wed Jun 27, 2007 5:42 pm

Post » Wed Jun 20, 2012 10:58 pm

I think map markers are statics so they can't be moved (or at least trying to use MoveTo in the console for static objects don't work). A quest marker should be pretty easy.

Just create a reference alias with the optional flag checked. When you find an ore vein, use ForceRefTo in order to set the reference alias as the ore vein. Then create a quest objective, and set the target ref to your reference alias. Use SetObjectiveDisplayed() to turn on and off the quest marker.

To check if the ore vein is depleted, use something like:

if (MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent == 0)	;depletedelseif (MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent < 0)	;fullelse	Debug.Notification((MyRefAlias.GetReference() as MineOreScript).ResourceCountCurrent + "ores left.")endif

ninja'd :ninja:

If the resource count is LESS THAN zero? Does it use a negative number to represent a "full" ore node? weird.
User avatar
FLYBOYLEAK
 
Posts: 3440
Joined: Tue Oct 30, 2007 6:41 am

Post » Wed Jun 20, 2012 11:16 pm

Yeah, the default value for ResourceCountCurrent is -1.
int property ResourceCountCurrent = -1 auto Hidden{Used to track the current remaining resources}

It changes to the total after the first strike.
    if ResourceCountCurrent == -1        ResourceCountCurrent = ResourceCountTotal    EndIfp
User avatar
Anthony Rand
 
Posts: 3439
Joined: Wed May 09, 2007 5:02 am

Post » Wed Jun 20, 2012 1:55 pm

RedWood Elf and RandomNoob, thank you very much for your help!

Below you can see the scripts for an initial version that's working more or less. I'd be glad if you could have a look & comment. And I have still some quest related questions.

The first script is attached to a magic effect; it looks for non depleted ore veins in the player's vicinity. (The search radius expands with (concentration) spell time, starting fairly low, so in effect one emulates looping through ever more distant candidates.)
Quest relevant are the last 3 lines (Quest: Start/SetActive, Alias:ForceRefTo)
Spoiler
Scriptname EK_FindOreVein extends ActiveMagicEffect  FormList	   Property EK_VanillaOreVeinList	AutoQuest		  Property EK_MiningQuest		   AutoReferenceAlias Property EK_OreVeinAlias		  AutoGlobalVariable Property EK_GlobMiningDistanceMin AutoGlobalVariable Property EK_GlobMiningDistanceRPS Autofloat TUpdate = 0.2float StepSizefloat ActDistEvent OnEffectStart(actor akTarget, actor akCaster)  float MiningDistanceMin = EK_GlobMiningDistanceMin.GetValue() as float  float MiningDistanceRPS = EK_GlobMiningDistanceRPS.GetValue() as float  StepSize = MiningDistanceRPS * TUpdate  ActDist  = MiningDistanceMin  RegisterForSingleUpdate(TUpdate)EndEventEvent OnUpdate()	Actor Player = Game.GetPlayer()  ; step forward distance  ActDist = ActDist + StepSize  ; find random list member (may be empty, already!) within distance	ObjectReference OreVeinRef = \	Game.FindRandomReferenceOfAnyTypeInListFromRef(EK_VanillaOreVeinList, \												   Player, ActDist)	EK_VanillaOreVeinList.Revert()  ; no vein at all -> try again	if ( OreVeinRef == none )	RegisterForSingleUpdate(TUpdate)		return	endif  if ( (OreVeinRef as MineOreScript).ResourceCountCurrent >= 0 )	; on non-full vein try again	RegisterForSingleUpdate(TUpdate)		return  endif;  ; vein is full (ResourceCountCurrent < 0)  EK_MiningQuest.Start()  EK_MiningQuest.SetActive(True)  EK_OreVeinAlias.ForceRefTo(OreVeinRef)endEvent

The second script is attached to the objective. I tried to fit all functionality into one script, not using stages (other than startup and shutdown) or papyrus fragments attached to them (reason: I like to have all relevant coding in one place, not in several GUI-windows like the fragments).
Spoiler
Scriptname EK_MarkOreVein extends QuestReferenceAlias Property EK_OreVeinAlias	  AutoGlobalVariable Property EK_GlobMiningTimeOut AutoEvent OnUpdate()  Debug.Notification("Root - OnUpdate")  GoToState("Idle")EndEventState Idle  Event OnUpdate()	RegisterForSingleUpdate(2.0)	Debug.Notification("Idle - OnUpdate")	if IsRunning()	  GoToState("Waiting")	endIf  EndEventEndStateState Waiting  ; wait for something to fill EK_OreVeinAlias  Event OnUpdate()	Debug.Notification("Waiting - OnUpdate")	if EK_OreVeinAlias.GetRef()	  ; display objective	  SetObjectiveDisplayed(10, True, True)	  Debug.Notification("ObjectiveDisplayed")	  ; got to state timeout	  GoToState("Timeout")	  float TimeOut = EK_GlobMiningTimeOut.GetValue() as float	  RegisterForSingleUpdate(TimeOut)	else	  ; landing here after the completion of the first objective	  Debug.Notification("no ObjectiveDisplayed")	  SetObjectiveDisplayed(10, False)	  RegisterForSingleUpdate(2.0)	endif  EndEventEndStateState Timeout  Event OnUpdate()	Debug.Notification("TimeOut - OnUpdate")	; clear alias	EK_OreVeinAlias.Clear()	; deactivate quest, clear current entry from log	EK_OreVeinAlias.Clear()	SetActive(False)	CompleteAllObjectives()	SetObjectiveDisplayed(10, False)	; prepare for next call	GoToState("Idle")	RegisterForSingleUpdate(2.0)  EndEventEndState

Quest flow and some harmless questions first:
  • The quest seems to be called after game load, I get notifications 'Root - OnUpdate' (empty state, I guess), then in a loop 'Idle - OnUpdate', even though I did not register for update, explicitely. Is this done automatically?
  • When the magic effect starts the quest for real I get again 'Root - OnUpdate', then once 'Idle - OnUpdate', propagated to 'Waiting - OnUpdate'. This is what I intended.
  • The objective is set, journal entry and map marker appear, and after a duration EK_GlobMiningTimeOut the script goes to 'TimeOut - OnUpdate', thus removing marker and journal entry, just as intended.
I can stop the quest in the TimeOut state but I did not succeed in re-starting it (tried reset in the quest script, SetStage anywhere or StartQuest in the magic effect script does nothing, either), even though I set 'allow repeated stages'.
@RedWoodElf: What did you mean with 'make the quest repeatable'?

Because of this the scripted solution does not stop the quest but puts it into state 'Waiting'. This way the magic effect script can assign a new value to the OreVeinAlias. The StartQuest on a quest still running doesn't seem to hurt....

So this seems to work after a fashion but I'd like a cleaner solution that, if possible,
  • prevents the quest from looping in state "Idle" before even started explicitely
  • and does a clean stop and re-start instead of having it looping in state 'Waiting'.
Still I'd like to have all the functionality in one script only (I did not find OnStage blocks in the Wiki but read somewhere that the papyrus fragments associated with a stage use this keyword?).
@RedWoodElf: I did not understand your words (after the second "Don't edit anything between these comments" comment). Are those comments from an auto-generated papyrus fragment?

Thanks again!


Edit: Hmmm, something else is fishy. I wanted to test this with a second character B using a savegame made before I added this quest (let's call it SAVE_B_1 for later reference).
Here the quest script gives no debug notification at all. Maybe the savegame of the character A I used for development now contains some remains from discarded attempts, like the OnInit blocks I had once but then decided they did no good?

Another Edit: This is tricky. I made a new save of the character B (SAVE_B_2) and added an OnInit Block, so the script quoted above now starts with
Spoiler
; run once when the script is initialized and on reset or quest startEvent OnInit()  Debug.Notification("Empty - OnInit")    RegisterForUpdate(2)    GotoState("Idle")EndEventEvent OnUpdate()  Debug.Notification("Empty - OnUpdate")    RegisterForUpdate(2)    GotoState("Idle")EndEvent
This had no effect at all when I restarted Skyrim with the savegame SAVE_B_2 just made, the quest script still wouldn't give any messages!
But when I loaded the earlier save SAVE_B_1, the quest and spell worked as with character A.

So it seems that the answer to my question why the quest had been looping in state 'Idle' without having been explicitely initialized is: the initialization had been carried over from the savegame and still works even if the quest in the current form would not have been able to start it at all.


Maybe all this inconsistencies could be addressed once I learn to stop and restart the quest cleanly?
User avatar
Calum Campbell
 
Posts: 3574
Joined: Tue Jul 10, 2007 7:55 am

Post » Thu Jun 21, 2012 1:06 am

to make a quest repeatable, simply don't check the "Run once" box on the first tab in the quest.
User avatar
Nicola
 
Posts: 3365
Joined: Wed Jul 19, 2006 7:57 am

Post » Wed Jun 20, 2012 10:35 pm

to make a quest repeatable, simply don't check the "Run once" box on the first tab in the quest.
That's weird then, I did never check this box, so I must have made an error, elsewhere.
User avatar
Trevi
 
Posts: 3404
Joined: Fri Apr 06, 2007 8:26 pm

Post » Wed Jun 20, 2012 3:04 pm

That's weird then, I did never check this box, so I must have made an error, elsewhere.

How about "Allow repeated stages" - is that checked?
User avatar
Spencey!
 
Posts: 3221
Joined: Thu Aug 17, 2006 12:18 am

Post » Wed Jun 20, 2012 10:11 pm

How about "Allow repeated stages" - is that checked?
Yes, it is. Maybe I need to work with stages, explicitely (I don't hope so) or my testing has been tainted with savegame residues...
User avatar
Sophie Louise Edge
 
Posts: 3461
Joined: Sat Oct 21, 2006 7:09 pm

Post » Thu Jun 21, 2012 12:06 am

All is well now: testing from a clean save shows:
  • (Naturally) I need an OnInit block
  • With this present, the quest can be stopped and restarted.
  • Not using RegisterForUpdate in the OnInit but having a Quest. RegisterForSingleUpdate in the ActiveMagicEffect Script that starts the quest prevents the quest from idly looping.
@RandomNoob: I have now tested your method of checking for empty veins: it works as you said it would.

One, hopefully last question:

Is there a way to interrupt the concentration spell that fires the ActiveMagicEffect? As it is now, when a vein is found an the mapping quest started, the concentration spell continues till the player stops pressing the mouse. In the same way it can be recast while the quest is still running, thus burning magic without effect.
I'd like to do something like triggering the OnEffectFinish condition from withing the script. Dispel() doen't do the trick.
User avatar
ashleigh bryden
 
Posts: 3446
Joined: Thu Jun 29, 2006 5:43 am

Post » Thu Jun 21, 2012 2:16 am

I haven't used it before, but you could try http://www.creationkit.com/InterruptCast_-_ObjectReference. Or if that doesn't work, you could try using http://www.creationkit.com/SendAnimationEvent_-_Debug to send an InterruptCast event or maybe a stagger event.
User avatar
Smokey
 
Posts: 3378
Joined: Mon May 07, 2007 11:35 pm

Post » Wed Jun 20, 2012 9:28 pm

http://www.creationkit.com/InterruptCast_-_ObjectReference
works perfectly, thank you, again!

Initial release http://skyrim.nexusmods.com/downloads/file.php?id=14925
User avatar
Justin Bywater
 
Posts: 3264
Joined: Tue Sep 11, 2007 10:44 pm


Return to V - Skyrim