Jump to content

2 OBSE array questions (and 2 "General")


hawke1133

Recommended Posts

Here is the idea. I need to replicate multidimensional arrays where the outer index can be dynamicaly added to (increase the population) and the inner array has a set amount of index slots.

This is what I tried and it almost works except for some syntax errors. (No clue as to why it will not allow me to set the inner array status.)

 

scn myarray

array_var actor
array_var actorStates 
ref tempref
begin gamemode
if actorStates == 0  
      let actorStates := ar_Construct actorStates; 
  	let actorStates := ar_resize actorStates 4;
endif
     ;=== runs once you enter a new cell ===
    if actors == 0
        let actors := ar_Construct Array 
        let actors[0] := ar_Construct actorStates ; Appears to work. No errors.
    endif 
; <other functions>
;here is my problem, accessing the inner array.

let actors [tempref][1] := 1 ; Errors are "invalid operrand for '[' " / "Too many operands"/ "invalid operrand for ':=' " / and "unknown variable for [tempref] [1] for parameter variable."
end

 

Looks like I might have to use either the Map array type or use your suggestion QQuix.

The thing that I need is simple in idea. For each refid, I need a sub array of X size to hold arbitrary state variables.

In C I would just do this:

 

int arrayActors [ 40][ 4 ]; I probably would use dynamic memory allocation (malloc) and a pointer array if I needed on the fly arbitrary outer arrays.
void main(void)
{
  arrayActors [0] [0] == 1
  printf "Array 0 0 = %d" ,arrayActors [0][0]
}

Hope this helps. (It seems complex, but in theory would be simple if we had multidimensional arrays. lol)

Link to comment
Share on other sites

You are on the right track . . . but . . .

 

OBSE arrays are not like fixed length, multidimensional matrices (3x4. 5x7x9, etc).

Arrays are more like a tree structure where each branch may be completely different from the others, therefore, each branch must be explicitly initialized.

 

Arguments for ar_Construct are supposed to be one of the literals: "Array", "StringMap" or "Map".

So, I would say that your "let actorStates := ar_Construct actorStates" would not compile. I've just tested and it seems ar_Construct defaults to "Array" if the argument is not valid, so, no problem as, luckily, this is what you want. (but the correct code would be "let actorStates := ar_Construct Array")

 

Also, "let actorStates := ar_resize actorStates 4" is not what you want. The line should be just " ar_resize actorStates 4". The way it is, you are setting actorStates to 1 (the return of the ar_resize function).

 

If you want to store actorStates as the first element of actors, use "let actors[0] := actorStates" (not "let actors[0] := ar_Construct actorStates")

 

 

The error happens because you are using a reference var (tempref) as the index to the array. Array type arrays must have integers as indexes. (it has been requested but, to date, no one of the array types can have references as index)

Link to comment
Share on other sites

Okay, where can I go from here? I need a dynamically expanding array that can store refid (for a key) that allows me to store up to X size of variables that are accessible and can be modified. (Basically state variables.)

My goal is

refid 1

a) has met player? (short/boolean)

b) time met player (1 hour game timer delay between each "meeting" per script function)

c) has ownership of "something" (short/boolean)

d) times met player

e) misc state function (to be added if needed)

 

Then I need to detect each actor's refid (that comes up to the player), compare it to the array and check these states. This occurs each time an actor comes up to the player. (Also append if actor is not in the array list.)

 

What is the best way to do that and still be able to walk /search/append the array? (This was for a specific script, but now has become a "quest" script for accessibility through out the mod.) Should I build this array using the "iloop" method as a template and then ar_copy it into an actual array? (instead of append)

 

fg109 answered a similar question in the topic "References withing Arrays" and that is what I based my initial assumption on. That I could create an array like actorArray [0][0] and expand the inner array value to 4 without having to do a loop to prebuild the inner array.

Similar to this:

short iloop
let iloop :=-1
if iloop <= 4
    let actorArray[0][iloop] := 0 ;storing a null value to build the inner array
    let iloop += 1
endif
; on finding a new actor
ar_append actorArray ; would this also add the 5 stage inner index?

(I am trying not to use generic npcs as containers to store this data.) Or am I still barking up the wrong "tree"? (Pun intended. lol)

Edited by hawke1133
Link to comment
Share on other sites

As I said, you are on the right track. You have all you need, just need to organize your thoughts.

 

Divide and conquer:

 

1) The inner array:

 

let actorStates := ar_Construct Array
ar_resize actorStates 4

The above will do what you need. It creates an array with 4 entries: actorStates[0], actorStates[1], actorStates[2] and actorStates[3]. (Although, for your objective, you need a 5th entry to hold the actor's reference).

 

With this array, you will place, for example, 'times met player' in entry actorStates[2], write down on a piece of paper that index 2 refers to 'times met player' and every time your script needs to know the number of times that particular actor has met the player you check actorStates[2].

 

Problem is that you take a break for a week and the next time you look at your script, you waste a lot of time (I do!) trying to figure out what the hell actorStates[2] is supposed to hold.

 

That is why, for the sake of script readability, I strongly prefer to use stringmaps instead of arrays. StringMaps use strings as indexes and strings can have meaning. I mean, when you look at an old script and see 'ActorStates["TimesMetPlayer"]', you immediately know what the entry holds.

 

So, if I were you, my inner array would be something like:

 

ActorStates["Ref"]
ActorStates["HasMetPlayer"]
ActorStates["TimeMetPlayer"]
ActorStates["HasSomething"]
ActorStates["TimesMetPlayer"]

 

2) The outer array:

 

I suppose her is no doubts about it. 'let actors := ar_Construct Array' does the job. You only have to define when you need to reset it (once per game? once per cell?)

 

3) Adding an inner array to the outer array:

 

Once you have the inner array constructed and populated with your data, adding it to the outer array is quite simple: just use the ar_append command ('ar_append OuterArray InnerArray' or, in this case, 'ar_append actors ActorStates')

 

4) Searching the outer array for an actor:

 

Here, ar_find wont help, because it will look for an specific entry in the outer array and we need to find an entry in one of the inner arrays.

So there would be two approaches for this:

a) looping thru the outer array looking for the actor reference in one of the inner arrays. E.g.:

 

ix= 0
while ix < ar_size actors
if actors[ix]["Ref"] == <ref of the actor you are looking for>
	break
endif
ix += 1
loop

 

b) An auxiliary, parallel array with just the refs, so you can use ar_find, as in my earlier example a couple of posts back.

Link to comment
Share on other sites

I won't pretend I know as much about scripting as QQuix, but I like doing it. This is what I came up with after reading all the posts here:

 

 

scn example

array_var CurrentActors
array_var ActorRef
array_var ActorStates
array_var temparr

short index
short tempshort

float fquestdelaytime
float tempfloat
float tempfloat2

ref tempref
ref container
ref item

Begin GameMode

if (fquestdelaytime != 1)
	let fquestdelaytime := 1
endif

if (CurrentActors == 0)
	let CurrentActors := ar_Construct Array
	let ActorRef := ar_Construct Array
	let ActorStates := ar_Construct Array
	let temparr := ar_Construct StringMap
	let temparr["HasMetPlayer"] := 0
	let temparr["TimeLastMet"] := 0
	let temparr["OwnsItem"] := 0
	let temparr["TimesMetPlayer"] := 0
	let temparr["Misc"] := 0
endif

;scan for actors every time the player moves to a new cell, then adds them to some arrays
if (GetCellChanged)
	ar_Erase CurrentActors
	let index := 0
	let tempref := GetFirstRef 69
	while (tempref)
;the CurrentActors array is for use later on in the script
		let CurrentActors[index] := tempref
		if eval (ar_find tempref ActorRef) < 0
;this should make it so that ActorRef[x] and ActorStates[x] refer to the same actor... the nested arrays just seemed too complicated
			ar_append ActorRef tempref
			ar_append ActorStates temparr
		endif
		let index := index + 1
		let tempref := GetNextRef
	loop
endif

;here is where the CurrentActors array is used... Didn't want to go through all the actors in Actor array if they're not in the current cell	
while (tempshort < ar_size CurrentActors)
	let tempref := CurrentActors[tempshort]
	let index := ar_find tempref ActorRef
;I used GetDistance instead of trying to check if dialogue occurred because by using "meeting", I thought it wasn't an actual conversation
	if (tempref.GetDistance Player < 512)
		let ActorStates[index]["HasMetPlayer"] := 1
		let tempfloat := (GameDaysPassed * 24) + GetCurrentTime
		let tempfloat2 := tempfloat - ActorStates[tempshort]["TimeLastMet"]
		if (tempfloat2 >= 1)
			let ActorStates[index]["TimeLastMet"] := tempfloat
			let ActorStates[index]["TimesMetPlayer"] := ActorStates[index]["TimesMetPlayer"] + 1
		endif
;some inventory walking to check whether the actor has the "item"... I don't like it since I don't like using nested loops but can't think of another way
		foreach item <- tempref
			if (item.GetBaseObject == ***object ID of the item***)
				let ActorStates[index]["OwnsItem"] := 1
				Break
			endif
			let ActorStates[index]["OwnsItem"] := 0
		loop
	endif
	let tempshort := tempshort + 1
loop

End

 

 

I compiled it successfully, but I have no idea if it works since I have not tested it. Instead of using nested arrays ("inner" and "outer"), I just did what QQuix suggested in an earlier post since that didn't seem as complicated.

Edited by fg109
Link to comment
Share on other sites

I did not tested it either, but your code seems OK.

Making ActorRef and ActorStates symmetric, you've got a better solution than the one I suggested earlier. (by symmetric, I mean elements with the same index in both arrays are related)

 

But there is a correction to make:

 

As opposed to other types of variables, when you assign an array to an array_var or to an element of another array, the script engine does not create a new array. The same array is 'pointed to' by both vars.

Example:

 

Array_var ArrayA
Array_var ArrayB
Array_var ArrayC
Let ArrayA := ar_construct array ;<< ArrayA var now points to a newly created array, say, array #49 within the OBSE engine
Let ArrayA[0] := "ABC"
Let ArrayA[1] := "DEF"
Let ArrayB := ArrayA  ;<< ArrayB var now points to array #49, the same as ArrayA
Let ArrayB[1] := "XZY" ; << changes the second element of array #49
Print ArrayA[0]  ; << Prints "ABC"
Print ArrayA[1]  ; << Prints "XYZ"  ;<< New content, although looking via ArrayA
;===we could go even further ===
Let ArrayC := ar_construct array
Let ArrayC[0] := ArrayB  ;<< Now the first element of ArrayC also points to array #49
Let ArrayC[0][1] := "MNO" ; << again, changes the second element of array #49
Print ArrayA[1]  ; << Prints "MNO"  
Print ArrayB[1]  ; << Prints "MNO"  
Print ArrayC[0][1]  ; << Prints "MNO"  - the three are the same 

 

This may be confusing at first, but it is very handy.

 

So, in your code, when you append temparr to ActorStates, you are actually appending the same array over and over so all the elements of ActorStates are actually one and the same. The first time you change one, you will change all.

 

Therefore, since you intend temparr to be a template, you should add A COPY of temparr each time, not temparr itself. Like this: 'ar_append ActorStates ar_copy temparr' (yes, it works).

 

Other that that, you could use GetItemCount to check if the NPC has a particular item in inventory.

Link to comment
Share on other sites

I knew I was forgetting something... Can't believe I didn't remember about GetItemCount. And thanks for pointing out the mistakes in what I posted. I learn something new every time you post. :thumbsup:
Link to comment
Share on other sites

As I said, you are on the right track. You have all you need, just need to organize your thoughts.

 

Divide and conquer:

 

1) The inner array:

 

let actorStates := ar_Construct Array
ar_resize actorStates 4

The above will do what you need. It creates an array with 4 entries: actorStates[0], actorStates[1], actorStates[2] and actorStates[3]. (Although, for your objective, you need a 5th entry to hold the actor's reference).

 

Problem is that you take a break for a week and the next time you look at your script, you waste a lot of time (I do!) trying to figure out what the hell actorStates[2] is supposed to hold.

 

That is why, for the sake of script readability, I strongly prefer to use stringmaps instead of arrays. StringMaps use strings as indexes and strings can have meaning. I mean, when you look at an old script and see 'ActorStates["TimesMetPlayer"]', you immediately know what the entry holds.

 

So, if I were you, my inner array would be something like:

 

ActorStates["Ref"]
ActorStates["HasMetPlayer"]
ActorStates["TimeMetPlayer"]
ActorStates["HasSomething"]
ActorStates["TimesMetPlayer"]

 

2) The outer array:

 

I suppose her is no doubts about it. 'let actors := ar_Construct Array' does the job. You only have to define when you need to reset it (once per game? once per cell?)

 

3) Adding an inner array to the outer array:

 

Once you have the inner array constructed and populated with your data, adding it to the outer array is quite simple: just use the ar_append command ('ar_append OuterArray InnerArray' or, in this case, 'ar_append actors ActorStates')

 

4) Searching the outer array for an actor:

 

Here, ar_find wont help, because it will look for an specific entry in the outer array and we need to find an entry in one of the inner arrays.

So there would be two approaches for this:

a) looping thru the outer array looking for the actor reference in one of the inner arrays. E.g.:

 

ix= 0
while ix < ar_size actors
if actors[ix]["Ref"] == <ref of the actor you are looking for>
	break
endif
ix += 1
loop

 

b) An auxiliary, parallel array with just the refs, so you can use ar_find, as in my earlier example a couple of posts back.

 

Let me put it to you this way, I am old school programming trained and "';remark every line of code" lol

I will add a header remark to the array definitions. Because they are defined specifically for a set purpose, I will have no problems (or my fellow coder.)

Now, I should be able to dynamically add these arrays when needed by

 while ix < ar_size actors
if actors[ix]["Ref"] == <ref of the actor you are looking for>
	break
           ar_append actors ; add an actor array
           ar_append  actors ar_copy ActorStates ; add the inner array.
        endif
ix += 1

Thanks for all the help! (Aside from using this for just one mod, I think I will create a small mod to populate Risa (cool looking party mod) with people and dance the night away! lol

Edited by hawke1133
Link to comment
Share on other sites

  • Recently Browsing   0 members

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