Jump to content

Arrays in Questscripts?


Recommended Posts

I am trying arrays in a questscript the first time for my new mark and recall spells and I used arrays before at objects and that works great but I get these errors now when I start my quest

50760147081_aa81eafecc_o.png

 

The quest script looks like this:

scn PekRMNoviceQSTSCR

float fQuestDelayTime
Float Timer
Float xPos
Float yPos
Float zPos


Ref RemoveObjectRef
Ref GateBaseRef
Ref TempRef

string_var SMark01
string_var SMark02
string_var SMark03
string_var SMark04
string_var SMark05
string_var SMark06
string_var SMark07
string_var SMark08
string_var SMark09
string_var TempStr

array_var XmarkerArr
array_var TriggerArr
array_var GateArr
array_var GateBaseArr
array_var SIArr

short DoOnce
Short Turn
Short Button
Short RemoveGate

Begin GameMode

	Set fQuestDelayTime to 1

	if ( DoOnce == 0 )

		Set SMark01 to SV_Construct "Slot 1"
		Set SMark02 to SV_Construct "Slot 2"
		Set SMark03 to SV_Construct "Slot 3"
		Set SMark04 to SV_Construct "Slot 4"
		Set SMark05 to SV_Construct "Slot 5"
		Set SMark06 to SV_Construct "Slot 6"
		Set SMark07 to SV_Construct "Slot 7"
		Set SMark08 to SV_Construct "Slot 8"
		Set SMark09 to SV_Construct "Slot 9"

		let GateArr := ar_Construct Array	
		Set TempRef to PekRMGateNovice01Ref
		Let GateArr[1] := TempRef
		Message "Test 1"
		Printc "Test 1"
		Let GateArr[2] := PekRMGateNovice02Ref
		Let GateArr[3] := PekRMGateNovice03Ref
		Let GateArr[4] := PekRMGateNovice04Ref
		Let GateArr[5] := PekRMGateNovice05Ref
		Let GateArr[6] := PekRMGateNovice06Ref
		Let GateArr[7] := PekRMGateNovice07Ref
		Let GateArr[8] := PekRMGateNovice08Ref
		Let GateArr[9] := PekRMGateNovice09Ref

		Message "Test 2"
		Printc "Test 2"

		let TriggerArr := ar_Construct Array	
		Let TriggerArr[1] := PekRMTriggerNovice01Ref
		Let TriggerArr[2] := PekRMTriggerNovice02Ref
		Let TriggerArr[3] := PekRMTriggerNovice03Ref
		Let TriggerArr[4] := PekRMTriggerNovice04Ref
		Let TriggerArr[5] := PekRMTriggerNovice05Ref
		Let TriggerArr[6] := PekRMTriggerNovice06Ref
		Let TriggerArr[7] := PekRMTriggerNovice07Ref
		Let TriggerArr[8] := PekRMTriggerNovice08Ref
		Let TriggerArr[9] := PekRMTriggerNovice09Ref

		Message "Test 3"
		Printc "Test 3"
		let XmarkerArr := ar_Construct Array	
		Let XmarkerArr[1] := PekRMXmarkerNovice01Ref
		Let XmarkerArr[2] := PekRMXmarkerNovice02Ref
		Let XmarkerArr[3] := PekRMXmarkerNovice03Ref
		Let XmarkerArr[4] := PekRMXmarkerNovice04Ref
		Let XmarkerArr[5] := PekRMXmarkerNovice05Ref
		Let XmarkerArr[6] := PekRMXmarkerNovice06Ref
		Let XmarkerArr[7] := PekRMXmarkerNovice07Ref
		Let XmarkerArr[8] := PekRMXmarkerNovice08Ref
		Let XmarkerArr[9] := PekRMXmarkerNovice09Ref

		Message "Test 4"
		Printc "Test 4"
		let GateBaseArr := ar_Construct Array	
		Let GateBaseArr[1] := PekXmarkerBaseN01Ref
		Let GateBaseArr[2] := PekXmarkerBaseN02Ref
		Let GateBaseArr[3] := PekXmarkerBaseN03Ref
		Let GateBaseArr[4] := PekXmarkerBaseN04Ref
		Let GateBaseArr[5] := PekXmarkerBaseN05Ref
		Let GateBaseArr[6] := PekXmarkerBaseN06Ref
		Let GateBaseArr[7] := PekXmarkerBaseN07Ref
		Let GateBaseArr[8] := PekXmarkerBaseN08Ref
		Let GateBaseArr[9] := PekXmarkerBaseN09Ref


		Message "Test 5"
		Printc "Test 5"
		let SIArr := ar_Construct Array	
		Set Turn to 1
		While ( Turn < 10 )
			Set DoOnce to 666
			Let SIArr[Turn] := DoOnce
			Set Turn to Turn + 1
		Loop

		Message "Test 6"
		Printc "Test 6"

		Set DoOnce to -1

		StopQuest PekRMNoviceQST
	endif

	Set TempStr to SV_Construct "Gate to %z", SMark01
	PekRMGateNovice01Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark02
	PekRMGateNovice02Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark03
	PekRMGateNovice03Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark04
	PekRMGateNovice04Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark05
	PekRMGateNovice05Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark06
	PekRMGateNovice06Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark07
	PekRMGateNovice07Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark08
	PekRMGateNovice08Ref.SetName TempStr
	
	Set TempStr to SV_Construct "Gate to %z", SMark09
	PekRMGateNovice09Ref.SetName TempStr
	
	If ( RemoveGate == 1 )
		If ( Timer < 15 )
			Set Timer to Timer + GetSecondspassed
		Else
			Let RemoveObjectRef := GateArr[Button]
			Let GateBaseRef := GateBaseArr[Button]
			RemoveObjectRef.MoveTo GateBaseRef

			Let RemoveObjectRef := TriggerArr[Button]
			RemoveObjectRef.MoveTo GateBaseRef
			
			Set RemoveGate to 0
		EndIf
	EndIf
End

Link to comment
Share on other sites

Hmm, the only thing immediately coming to mind is arrays always start at "0" index. Trying to set the "1" index of a newly created array without setting the "0" first can only fail.

 

It's not a StringMap after all, so you can't just start at any point in the index.

Link to comment
Share on other sites

Yes, you might be right about that for sure as I also asked in Reddit, and I got the same answer as someone did test to use arrays in a 2 questscripts and it failed when he started at 1 but worked if he started with 0. I will add garage to position 0 and see if it works. He wants me to use map if I want to use arrays in that way, starting at 1 but I want to use a simple array... ;)

 

Here is what he wrote:

 

What can I say. When I do

scn arraytestquestscript

array_var testarray
short i

begin gamemode

let testarray := ar_construct array
set i to 1
while ( i < 10 )
let testarray[i] := 1
set i to i + 1
loop

printc "yo"

end 

it fails, but when I do

scn arraytestquestscript

array_var testarray
short i

begin gamemode

let testarray := ar_construct array
set i to 0
while ( i < 10 )
let testarray[i] := 1
set i to i + 1
loop

printc "yo"

end 

it works. If you need the behavior of a map, you need to initialize it as such.

Link to comment
Share on other sites

  • 1 month later...

Hello,

I really love working with arrays, as they can hold many information.

Went even that far that I save an array into the savegame as it contains info that is required to be retrieved even after loading a game.

Even though it's recommended to normally use

Let <yourArray> := Ar_Null

to erase an array and avoid savegame bloating.

 

I would not recommend to use arrays without emptying them except if you want to save the data for later use similar as I did in my dynamic barter system.

Normally Oblivion has fixed barter gold.

While there are mods like "Living Economy" or "Enhanced Economy", I still don't get how they manage to dynamically change the merchants barter gold.

I mean it's easy with "SetBarterGold", but that doesn't dynamically change the Menu String, which holds the current barter gold value.

I've did it with "SetMenuStringValue".

Oblivion only raises the Mercantile skill only per transaction not per sold item for each transaction.

My old mentor AltDunmer made an initial fix for this, but PushTheWinButton perfected the solution by using a different OBSE function to raise the skill.

Which did not require any of the ugly workarounds my old mentor used.

Quest script to ensure that selling multiple items will be awarded with the proper skill value:

 

 

scn NOAmercantileLevelingFunction

float fquestdelaytime
short sNOAoldGold
short sNOAconvCheck
short sNOALastTransactionCount
short sNOASSetOnceSell
short sNOASSetOnceBuy
array_var arNOATransactionInfo
ref rNOAsendMerchant
ref rNOAcompMerchant
short sNOAsendMerchGold
short sNOAsendTransGold

array_var arNOAMerchantRef
array_var arNOAMerchGold
array_var arNOAResetTime

int i
int iMerchantSaved

Begin GameMode
    if sNOAconvCheck == 2
        Let sNOAconvCheck := 1    ;=> Run block once again at next conversation
    elseif sNOAconvCheck == 0
        Let fquestdelaytime := 0.1    ;=> Quick processing is necessary for accuracy.
        Let sNOAconvCheck := 2
    endif
End

Begin MenuMode
    If (Menumode 1009 && sNOAconvCheck == 1)
        DisableControl 4    ;=> To ensure the loop is done before the player leaves the dialogue menu.
            Let i := 0
            if eval(Ar_Size arNOAMerchantRef) > 0    ;=> Buying or selling something to a merchant will
                Let rNOAsendMerchant := GetFirstRef 35    ;=> start this block.
                While (rNOAsendMerchant)
                    if rNOAsendMerchant.GetCurrentAIPackage == 6
                        Break ;=> Only one NPC can talk with the player in dialogue
                    endif
                    Let rNOAsendMerchant := GetNextRef                        
                Loop
                if rNOAsendMerchant.GetOffersServicesNow    ;=> Is it a merchant?
                    While eval(Ar_Size arNOAMerchantRef) > i
                        Let rNOAcompMerchant := arNOAMerchantRef[i]
                        if rNOAcompMerchant == rNOAsendMerchant
                            Let iMerchantSaved := 1 ;=> We already have that merchant, so not saving it once more.
                            if eval(arNOAResetTime[i]) <= GameDaysPassed    ;=> Evaluate merchant's 'money reset
                                Let sNOAoldGold := arNOAMerchGold[i]
                                Let arNOAResetTime[i] := GameDaysPassed + 3
                                if rNOAsendMerchant.GetBarterGold < sNOAoldGold
                                    rNOAsendMerchant.SetBarterGold sNOAoldGold
                                endif
                            endif
                        endif
                        Let i += 1
                    Loop
                    if iMerchantSaved
                        Let iMerchantSaved := 0
                    else
                        Ar_Append arNOAMerchantRef rNOAsendMerchant
                        Let sNOAoldGold := rNOAsendMerchant.GetBarterGold
                        Ar_Append arNOAMerchGold sNOAoldGold
                        Let sNOAoldGold := GameDaysPassed + 3
                        Ar_Append arNOAResetTime sNOAoldGold
                    endif
                endif                    
            endif
        Let sNOAconvCheck := 2    ;=> This will only be run once.
        Let sNOAoldGold := player.getgold
        EnableControl 4        ;=> Reference evaluation is over.
    elseif (MenuMode 1008 && sNOAconvCheck == 2)    ;=> Only start the block in container menu
        if IsBarterMenuActive    ;=> Only proceed if the container is a barter menu
            If (player.GetGold != sNOAoldGold) && Player.GetBaseAV Mercantile < 100
                Let sNOALastTransactionCount := GetLastTransactionQuantity
                if sNOALastTransactionCount == 0
                    Return    ;=> Failsafe
                endif
                if GetContainerMenuView
                        if sNOASSetOnceSell
                        else
                            if NOAinitQuest.sNOAmercantileBuySkill    ;=> If buying inceases the skill too,
                                SetSkillUseIncrement 0.2 Mercantile 0    ;=> the incrementation will be reduced.
                            else
                                SetSkillUseIncrement 0.4 Mercantile 0    ;=> Default incrementation.
                            endif
                            Let sNOASSetOnceSell := 1
                            Let sNOASSetOnceBuy := 0                                
                        endif                                 ;=> We got already awarded with an increase
                    if sNOALastTransactionCount > 1    ;=> Only if we sold more than one item.
                        Let sNOALastTransactionCount -= 1    ;=> as of the actual transaction. So one less.
                        While sNOALastTransactionCount > 0    ;=> Now we increment the skill one per one.
                            IncrementPlayerSkillUse Mercantile 0 1
                            if Player.GetBaseAV Mercantile == 100
                                Break    ;=> Oh, so you reached 100? Fine, then exit the loop NOW!
                            endif
                            Let sNOALastTransactionCount -= 1    ;=> Each incrementation reduces the counter until it reaches
                        Loop                            ;=> 0 and exits the loop.
                    endif
                        Let arNOATransactionInfo := GetTransactionInfo "sell"
                        Let rNOAsendMerchant := arNOATransactionInfo["buyer"]
                        Let sNOAsendMerchGold := rNOAsendMerchant.GetBarterGold
                        Let sNOAsendTransGold := arNOATransactionInfo["price"]
                        Call NOAdynBarterGoldFunction 1 rNOAsendMerchant sNOAsendMerchGold sNOAsendTransGold
                elseif GetContainerMenuView == 0
                            if sNOASSetOnceBuy
                            else
                                SetSkillUseIncrement 0.4 Mercantile 0
                                Let sNOASSetOnceSell := 0
                                Let sNOASSetOnceBuy := 1
                            endif
                        if sNOALastTransactionCount > 0
                            While sNOALastTransactionCount > 0
                                IncrementPlayerSkillUse Mercantile 0 1
                                if Player.GetBaseAV Mercantile == 100
                                    Break
                                endif
                                Let sNOALastTransactionCount -= 1
                            Loop
                        endif
                        Let arNOATransactionInfo := GetTransactionInfo "buy"
                        Let rNOAsendMerchant := arNOATransactionInfo["seller"]
                        Let sNOAsendMerchGold := rNOAsendMerchant.GetBarterGold
                        Let sNOAsendTransGold := arNOATransactionInfo["price"]
                        Call NOAdynBarterGoldFunction 2 rNOAsendMerchant sNOAsendMerchGold sNOAsendTransGold
                endif
                Let arNOATransactionInfo := Ar_Null
            endif
            Let sNOAoldGold := player.getgold
        endif            
    endif
End

 

 

Function script to change the barter gold dynamically:

 

 

scn NOAdynBarterGoldFunction

short sNOABuyOrSell
ref rNOAMerchantRef
short iNOAMerchantGold
short sNOATransactionGold
short sNOAworkWith
string_var strNOAworkWith

Begin Function{sNOABuyOrSell, rNOAMerchantRef, iNOAMerchantGold, sNOATransactionGold}
    if eval(Ar_Size NOAmercantileLevelingQuest.arNOAMerchantRef) < 0
        Let NOAmercantileLevelingQuest.arNOAMerchantRef := ar_Construct Array
        Let NOAmercantileLevelingQuest.arNOAMerchGold := ar_Construct Array
        Let NOAmercantileLevelingQuest.arNOAResetTime := ar_Construct Array
        Ar_Append NOAmercantileLevelingQuest.arNOAMerchantRef rNOAMerchantRef
        Ar_Append NOAmercantileLevelingQuest.arNOAMerchGold iNOAMerchantGold
        Let sNOAworkWith := GameDaysPassed + 3
        Ar_Append NOAmercantileLevelingQuest.arNOAResetTime sNOAworkWith
    endif
    if sNOABuyOrSell == 1
        If (rNOAMerchantRef.GetBarterGold - sNOATransactionGold) < 0
            rNOAMerchantRef.SetBarterGold 0
        else
            Let sNOAworkWith := rNOAMerchantRef.GetBarterGold - sNOATransactionGold
            rNOAMerchantRef.SetBarterGold sNOAworkWith
        endif
        Let strNOAworkWith := $sNOAworkWith
        SetMenuStringValue "cont_background\page_layout\cont_contents\cont_npc_money_icon\cont_npc_money_text\string|%z", strNOAworkWith 1008
    elseif sNOABuyOrSell == 2
        Let sNOAworkWith := rNOAMerchantRef.GetBarterGold + sNOATransactionGold
        rNOAMerchantRef.SetBarterGold sNOAworkWith
        Let strNOAworkWith := $sNOAworkWith
        SetMenuStringValue "cont_background\page_layout\cont_contents\cont_npc_money_icon\cont_npc_money_text\string|%z", strNOAworkWith 1008
    endif
    sv_destruct strNOAworkWith
End

 

 

 

Furthermore, you can also work with arrays that have an unkown amount of entries.

Ar_Append <yourArray> <the entry you want to add>

I like to use Ar_Append especially, when the entry should only be added to the array when a specific condition is met.

 

If you want to go through the array with unknown entries you surely have to check your index variable against the entries available:

While eval(Ar_Size <yourArray>) > i
    Let <SomeVariable> := <yourArray>[i]
    Let i += 1
Loop

Also be aware that "eval" is required to get the actual array size.

BUT, it does NOT work in this way:

While i < eval(Ar_Size <yourArray>)

Don't ask me why, but Eval always had to be aside the If or While to get the condition working properly. XD

I learned it the hard way. ^^

 

Also be sure that "Eval(Ar_Size <yourArray>)" always returns the total amount of entries in your array.

Which doesn't go well with the index.

As all indexes start at 0, be sure that you never use ">=" in the condition.

 

Lets say you have an array with 4 entries.

Index is 0 to 3.

Yet "Ar_Size" returns 4.

 

So using

While eval(Ar_Size <yourArray>) >= i

will result in "4" being also included for the index evaluation, but as it is an improper index for the array the script runs into an error.

Link to comment
Share on other sites

Yes. That is my current problem that I do forget that the indian number 0 actually is something. If we get the size of an array, we must use < or > and never <=

I did learn something new when I read how you optimize stuff. In basic and also in C++, we do use

i++

to increase a number and I did not know about

Let i += 1

which is so damn neat and I will use it from now on and I love it as I do hate

Set i to i + 1

My arrays will most likely not be so big that they will cause bloats but if that is the case, Screw Oblivion... :wink: *goes and flush the rubbish into the water closet*

I am back - What did we debate?

Why did they fail to add it here -> Let? But they do write it here where I found it while searching for +=

 

-----------------------------------------------------------

 

I do fail to imagine what use we have of maps? I mean, ordinary arrays are so self explained and useful for databases but when would I ever need an array using maps??? In my mind, an ordinary variable could do the job like a short, float, ref or string?? What have I missed? Maybe I have missed a lot really, most likely... :wink:

Edited by Pellape
Link to comment
Share on other sites

Yeah... Rather use the GECK Wiki, it has similar functions, but far better explanation.

https://geckwiki.com/index.php?title=Let

 

Even though thanks to the JIP LN NVSE plugin there are hundred more functions for New Vegas. ^^

https://geckwiki.com/index.php?title=Complete_List_of_Function_in_Fallout_New_Vegas

 

Compared to Oblivion:

https://cs.elderscrolls.com/index.php?title=List_of_Functions

 

 

I honestly fear maps.

I only used simple arrays so far and neither nested ones. XD

 

 

It even went that far, that I used 3 simple arrays for values that could be housed otherwise.

One array for the merchant reference

One array for the default barter gold

One array for the reset time of the barter gold

 

They all get populated at the same time, so all of them will ever have the correct info at the very same index for the very same merchant.

 

It might be possible to make one array for the merchant ref and nest another array to that which could hold both values.

So the first reference (index 0) needs the first 2 values (index 0, 1).

The second (index 1), needs the next 2 values (index 2, 3).

 

But I always fear about index mismatches, so I rather went the simple way. XD

Link to comment
Share on other sites

In Oblivion, I have used arrays extensively: hundreds of arrays with thousands of entries each, several levels of nesting, and never, ever have experienced any bugs.

 

If memory serves, most of them were stringmaps, some were maps and a few were simple arrays.

Link to comment
Share on other sites

But your scripting comprehension is legendary QQuix. :tongue:

The CSE also reminds me about you everytime I use it.

There is an uncompiled QQuix script, where you mentioned your idea about Coda for CSE. :wink:

 

 

I would die for an example from you with nested arrays. :smile:

 

 

Oh and speaking about "index mismatches". I mean not that the arrays are unstable, I mean that the scripter causes index mismatches as of not thinking about all unhandled exceptions. ^^

Guess using nested arrays require a lot accuracy in the script, which is why I would love to see an example from you.

Link to comment
Share on other sites

We must also take in count the current OBSE bugs that Idle is working with at this very moment. They do not relate with arrays but it was when I tried to add nothing to an array I stumbled on the bugs... Maybe they always where there and maybe they are not an OBSE bug at all... Well idle will find out.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...