hawke1133 Posted May 7, 2011 Author Share Posted May 7, 2011 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 More sharing options...
QQuix Posted May 7, 2011 Share Posted May 7, 2011 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 More sharing options...
hawke1133 Posted May 7, 2011 Author Share Posted May 7, 2011 (edited) 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 1a) 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 May 7, 2011 by hawke1133 Link to comment Share on other sites More sharing options...
QQuix Posted May 7, 2011 Share Posted May 7, 2011 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 4The 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 More sharing options...
fg109 Posted May 7, 2011 Share Posted May 7, 2011 (edited) 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 May 7, 2011 by fg109 Link to comment Share on other sites More sharing options...
QQuix Posted May 7, 2011 Share Posted May 7, 2011 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 More sharing options...
fg109 Posted May 7, 2011 Share Posted May 7, 2011 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 More sharing options...
hawke1133 Posted May 7, 2011 Author Share Posted May 7, 2011 (edited) 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 4The 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" lolI 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 May 9, 2011 by hawke1133 Link to comment Share on other sites More sharing options...
Recommended Posts