OFPEC Forum

Editors Depot - Mission Editing and Scripting => OFP - Editing/Scripting General => Topic started by: Fragorl on 22 Feb 2005, 06:49:07

Title: Properties for objects
Post by: Fragorl on 22 Feb 2005, 06:49:07
I've been pining for the 'dot' notation that C++ uses to access data for a particular object. In flashpoint script, you either have to use one of the commands in the comref (getpos for example), or store your data in a whole bunch of variables in script (_soldier1startloc, _soldier2startloc, _car23owner and so on). It can get cumbersome (for me) to keep track of/use/update all these variables in script sometimes especially in large .sqs files. Plus there is the additional problem of global variables being the only way that scripts can 'communicate' - otherwise they are completely cut off from each other. With global variables again there is the problem of keeping track of names, plus the fact that they all seem to be public and overwritten perfectly easily, and from anywhere. The fact that the script is parsed means there is no compiler error if you accidentally attempt to overwrite a 'private' variable (or at least one that somebody intended to be private) which is used some other script(s). Having masses of global variables seems... unsafe, and a bit uncomfortable at times.

So my idea, if original, is to store data pertaining to an object  (you define the data, and its tag) in an n-dimensional array. This would reduces the number of global variables to just one.

Example: say you had 2 scripts for a C&H game. The first monitored the capture points for east/west presence and updated who owned what. The second was an ai script that told a group of soldiers where to move next and what to do. When the first script detected that a capture point was taken, it would update the waypoint for the soldiers to the next objective. The second (ai) script would tell them to move to the objective/patrol and guard once they were there.

Problem: how do the 2 scripts communicate? The mission designer might understandably want the soldiers to patrol through certain waypoints at one objective, and another another set at the next. Or he might want them to mount a certain machine gun at the first objective, and a tank at the next. How does the mission designer store all these variables: objectives, positions, machine guns etc? It's possible but difficult to do this with conventional waypoints and switch commands, and it gets trickier the more objectives there are. Generally the mission designer will script, and use variable names.
In flashpoint, you might name them something like this: CurrentObjective, WayP1GuardPoint1, WayP1GuardPoint2, WayP2MG1, WayP2MG2 and so on. (The variables might be local or global, depending on how the scripts are structured). Here it gets messy, especially if the variables are global, because they'll need original names to stop them getting accidentally overwritten by competing scripts.

Solution: In C++, you might be able to store/retreive the data like this: currentobjective.patrolpoints [ 0 ] (for the first patrol point in a series for this objective), currentobjective.defences.machineguns (for a list of machinegun defences associated with this objective) etc. It's 'object orientated' in that the code revolves around various objects which contain data and functions that modify that data. These functions can also modify other object's data (if that object allows it), which makes it powerful. Apologies if you know all this already.

How to implement something this in OFP? As I mentioned before, with an n-dimensional 'array'. Since 'array's in ofp are very flexible, you can store this data like this:

[ [ AnObject,["dataname1",data1],["dataname2",data2] ] , [ AnotherObject,["dataname1",data1] ] , ... ]

With a simple check, you could find, say, AnObject 's second piece of data by searching firstly for AnObject in the array, and secondly for "dataname1" in it's various data arrays. Effectively you get 'AnObject.Dataname1', which equals 'data1'. In this way, you can store/retrive any number of pieces of info associated with a single object. Only 2 functions would be required for this which I can write quite easily- one to store new data, one to retreive it.

Advantages:

1. You can assign your own properties to an object without ever needing to name it, or know it's name. Simply because the object itself resides in the array. So you could assign a piece of data to an unnamed object, and run into it sometime later (e.g. with a nearestobject command) and find it's data again.

2. You can assign as many properties as you like to an object (within the various limitations of the game).

3. Drastically cuts down on the number of variable names needed.

Disadvantages:

1. The functions that find the data and the object iterate through all the elements. This means that the more pieces of data you add, and the more objects you add, the longer the check would take. The 'retreive data' functions are much slower than simply accessing a variable by name. Translation: More objects with more properties = slower to find one property.

2. Probably there are others.


Sorry for this long and rambling post. If you're still with me, please let me know what you think, or if this has been done before. If you have a question also please tell me.  ;)

 
Title: Re:Properties for objects
Post by: General Barron on 22 Feb 2005, 07:38:57
   I've thought about doing this exact same thing as well. AFAIK, this hasn't been done in the past, or atleast it hasn't been done much. When the CoC's Unified Artillery first came out, I thought they must have done something like this, since they seemed to have variables 'attatched' to units and nothing else. However, it turns out they used the call and format command to create dynamic global variables. While that is also a useful technique, I believe this one would be more usable on a larger scale, especially due to the savegame bug that occurs when you have too many variables.

   Perhaps we could turn this idea into a usable function/script suite? I actually am working on a script/addon that will require exactly this trick anyway.
Title: Re:Properties for objects
Post by: Fragorl on 22 Feb 2005, 07:59:09
Great! I'm pleased to hear it's in the works, and i'd be glad to help out. I'm not surprised that the idea's been thought up, though, it's fairly simple.

About CoC, I gueesed that they'd need some form of mass-handling of variables, but never got around to actually checking how. Using global variables like is an interesting idea, but as you say suffers from the savegamebug plus the ones I mentioned.

I hadn't thought about implementation, except vaguely on an individual-mission basis. function/script suite sounds better.  :)
Title: Re:Properties for objects
Post by: Fragorl on 24 Feb 2005, 11:56:21
OK anyhow here are a couple of (tested) functions, that add and modify the aforementioned 'properties'.

E.g. You want to add a property called "age" to the player.

[player,"age",38] call SetProperty

You want to retrieve the player's "age":

[player,"age"] call GetProperty

-- If you have defined "age", like in the above, you will get 38 returned.
-- if you havent, the function will return **EDIT (see below) -- objnull

Another e.g: You want to modify the property "Nearest Soldier" of the leader of the player's group.

[leader (group player),"Nearest Soldier",nearestobject [leader (group player),"SoldierWB"]] call Addproperty


*****Notes*****
Just one global variable needed. It's an array, called 'properties'. Careful not to modifiy it elsewhere (if you're using this system).

Just two functions you need: SetProperty.sqf, Getproperty.sqf. The global variable 'properties' is automatically set up the first time you run either of these.

If the object doesn't have any properties defined and you add a property to it, this will work. The object will be instantiated in the array, 'properties'

If the object has the property that you're trying to add already (ie the name is the same), it will be modified.

If the object has properties, but none under the name you're specifying, that property will be added.

EDIT: object no longer required. Works with a second string.
Title: Re:Properties for objects
Post by: Fragorl on 24 Feb 2005, 11:57:27
Attachment deleted. Functions now at editor's depot.
Title: Re:Properties for objects
Post by: General Barron on 24 Feb 2005, 21:16:40
Wow, very nice. I wouldn't have thought to use strings like you did (I was thinking of just setting/returning an array and letting the scripter decide what to do with it), but your method is very intuitive. I have one suggestion to the second function though: instead of returning false when nothing is found, it should return null. The reason is, if I want to have some sort of error checking in my script, it would have to look like this with your current function:

Code: [Select]
_result = .... call getProperty
? !_result : goto "error"
...blah blah....

This will return an error if _result is successfully returned (unless it is actually a boolean that is returned). If _result is an object, number, or whatever, it will cause an error. But if you return null on an error, then you don't have that problem:

Code: [Select]
_result = .... call getProperty
? isnull _result : goto "error"
...blah blah....

That shouldn't ever cause an error, no matter what _result is.

Other than that, they look pretty good, and should be submitted to the functions page, IMO. :)
Title: Re:Properties for objects
Post by: Fragorl on 24 Feb 2005, 22:09:00
Good point!  :o Yes, I didn't really think about that...
I think 'Isnull <bool/string/etc>' causes errors too though, coz isnull is just for groups and objects. I just tested that. I'll have to think about this...
Title: Re:Properties for objects
Post by: General Barron on 25 Feb 2005, 00:43:49
It does? Hmm... oh well, it can still be used. Instead of 'isnull', you would just need to check if the returned value is equal to itself:

? _return == _return : goto "returnisok"

If _return is null, then it will not be equal to itself. If it is anything else, then it will be equal to itself.
Title: Re:Properties for objects
Post by: Fragorl on 25 Feb 2005, 08:06:57
Tricksey! ;D I'll have to put a note in with the function for scripters that this is the check needed.
Now to advertise this...
Title: Re:Properties for objects
Post by: Fragorl on 04 May 2005, 10:45:57
Finally, I have got around to puting the two functions in the editors' depot.

I have removed the functions from this page as they are slightly out of date, and of course now redundant. Get them here (http://www.ofpec.com/editors/funcref.php?filter_func=63).

Any questions/comments welcome.
Title: Re:Properties for objects
Post by: Chad on 04 May 2005, 13:59:37
Or you could make a compiler that takes higher language syntax like java and convert that syntax into the ofp game script engine.

// Author Chad Lion
// Version: 1.00
// Date: May 3, 2005
//
// This document is for demonstration purposes.
// Its to reflect a ongoing development of two current projects
// One the object orientated compiler and the next is the
// A.I. Heuristic
public class Demo{
    public static void main( object parmunit, object target ){
      object unit[] = units group parmunit;  
      boolean found = true;
     
      while( found ){
         for( int i = 0; i < (count unit); i=i+1 ){
            object tmp = unit;
         
            if(  alive tmp ){
               if( ((tmp knowsabout target) > 0 ) && ( found != false ) ){
                  telleveryone( unit, target );
                  found = false;
                  print( "Finish telling units" );
               }
            }
         }    
      }    
    }
   
    public static void telleveryone( object unit[], object target ){
         for( int i = 0; i < (count unit); i=i+1 ){
            object tmp = unit;
            if( alive tmp ){
               Soilder s = new Soilder( tmp, target );
               s.startthread();  
            }
         }
    }
}
public class Soilder{
   private object me;
   private object target;
   
   public Soilder( object me, object target ){
      this.me = me;
      this.target = target;
   }
   
   public void startthread(){
      me setBehaviour "COMBAT";
      me setdir DirToObj( me, target );
      me allowfleeing 0;
     
           
   
      while( ( alive target ) && ( alive me ) ){
         if( me hasweapon "M60" ){
            if( (me hasweapon "M60") && ((me distance target) < 400 ) ){
               dostop me;
               me setUnitPos "down";
               while( ( alive target ) && ((me distance target) < 400 ) ){
                     me dofire target;
                     ~1;
               }
            }else{
               me domove getpos target;            
            }
         }else{
            if( (me distance target) < 200 ){              
               dostop me;
               me setUnitPos "down";
               me dofire target;
            }else{
               me domove getpos target;            
            }        
         }
         ~10;
      }
      freeheap( this );    
   }
   
   
   // calculate the direction from the ai to the target engaged
   // thanks gen
   public int DirToObj( object me, object target ){
      int ai[] = getpos me;
      int thetarget[] = getpos target;
      int result = 0;
     
result = (thetarget[0] - ai[0]) atan2 (thetarget[1] - ai[1]);
      if( result < 0 ){
         result = result + 360;
      }
     
      freeheap(ai);
      freeheap(thetarget);
      return result;
   }  
}  
The above is an example, and it compiels to a script file that ofp executes and runs. Full ability out of game error checking, increase performance well degreasing development time. Increase flexability with incapsulation and abstraction. All the problems within ofp script engine are handeld by the compiler. It means you spend more time in production than worrieing about the underlying language and issues.

I am the lead developer.

But i have allready said to much.
Title: Re:Properties for objects
Post by: Chad on 04 May 2005, 14:05:14
btw in regards to you first questions, within the compiler you can make your own static methods that resemble the commands you wish to excute, giving you the functionality that you wish with dot notation.
Title: Re:Properties for objects
Post by: Fragorl on 05 May 2005, 01:02:43
H'm. My original aim was to create a more effective way of managing global variables- the variables themselves.

Can you explain what you are doing with this code? I can follow it, but it is missing sections and I want to know more about it.

The above is an example, and it compiels to a script file that ofp executes and runs. Full ability out of game error checking, increase performance well degreasing development time. Increase flexability with incapsulation and abstraction. All the problems within ofp script engine are handeld by the compiler.
Now this really interests me. You say that the above compiles to a script file, .sqs. I can understand that you might interpret that content into a script file, but it would be very complex. It interprets all your 'while's and 'for's into a script file? Well, for each loop you have, you will need to have 2 new labels, if you're working with script. So perhaps your compiler adds labels for each loop statement it encounters?

     while( found ){
        for( int i = 0; i < (count unit); i=i+1 ){ .....
        }
      }

---> interpreted to

     #label1
     ?!_found: goto "endlabel1"
         #label2
         ?_i>=count _unit:goto"endlabel2"
         ...
         ...
         _i=_i+1
         goto"label2"
         #endlabel2
      ...
      ...
      goto "label1"
      #endlabel1    

where each loop you add involves two new arbitrarily named labels which your compiler adds. I can imagine that you might be able to somehow simulate 'else' statements, which script does not support. As for simple returning methods, I imagine you can simulate them too, using either .sqf files, or even in your .sqs file using yet more labels.

But i have allready said to much.
You may need to explain some more. Am I generally correct as to what you are doing? Or barking up the wrong tree altogether? Do you output one .sqs file for each class?

Remeber that as long as you're outputting script files, you're still subject to the limitations of the OFP game engine. Any limitations that apply to script will apply to the products of your compiler. But maybe you have a more effective way of organising your script files?

Anyhow, let me restate the purpose of my two .sqf functions:

They are designed to manage global variables in a more effective way, cutting down on the total number and providing an easier way to exchange them between script files (which is 100% possible as it is). They help secure variables against overwriting, and also provide a way to 'attach' properties to game objects.

Maybe my spiel at the beginning about C++misled you. That is all I set out to do here.
Title: Re:Properties for objects
Post by: Planck on 05 May 2005, 01:09:35
Quote
I can imagine that you might be able to somehow simulate 'else' statements, which script does not support.

Are you sure about this?

I've never used it but the comref does list else.

ifCode else elseCode


Perhaps you have tried it and it didn't work?


Planck
Title: Re:Properties for objects
Post by: Chad on 05 May 2005, 02:37:38
You are very much right on the mark considering the while and #goto statements.

I will exsplain how the compiler works.

The compiler takes higher language syntax like java, the first pass it checks the syntax of the document well building up a data tree structure of you objects, methods, member fields, and local variables within your code. It will come to a in-game statement for example,    dostop. It will then do an xml parser check with the 3 different xml files provided with the comref documentation. If a match is found then its flaged as being a ofp command to execute.

The next stage of the compiler is doing type checking on the 2nd pass of the code. Once it has built up the data structure more like code structure to make it simple. It then checks the data types against each other. This is to provent people from mistakenly compairing an object against a string. This is where the functionality of out of game error checking and syntax anyalysing comes into play. During the same second pass it emits assembly code.

Assemlby code? what?

yes, but a more advanced method of actualy doing it than what people may think. The compiler contains an interface, that sits between different languages. This interface is just a assembly code matcher, what sits behind the interface is whats amazing. The interface provides the ability  for the compiler to emit the same instruction then that instruction is matched to different languages.

Think of it like this way, i can swap the instruction set from assembly to pascal to ofp, and so on. This functionality enables the compiler to have a level of abstraction.

The compiler emits two main files, these being one file called
com.sqs
and
comt.sqs

The com.sqs file is the main file you execute within the game with
[] exec "com.sqs"

To pass parameters from the game into the compiler you would do this
[ this, "string" ] exec com.sqs
Then to access those variables within your higher language you would do this.

public static void main( object me, string mystring )

Or a more perffered method of doing this is the following

object me = (object) chad
string mystring = (string) mystring

the difference being, that you can now access global variables within the game then data cast them into the correct types so the compiler knows they exsists.

So in conclusion, optimisation can be done within the compiler without effecting the syntax at all. So technicaly, loops can be create at compile time into sqf function and such be called, without any modification to the code.

At this current stage, the complexity of the compiler and the technology used is pretty advanced. I have worked on the project for the last 1 year, and now its at completion i am in development of the ai hurristic with this tool.

I have run tests corresponding to object code, and performance compaired to original script files writen by hand. Although writen by hand script files run about 2% faster than the emited game code the compiler produces. But, its not about the speed its about the functionality the compiler gives to an organisation to produce training similation tools thats the major difference.

Because the compiler uses xml files and parsers them on the fly. All current and ongoing commands can be easly copied and pasted into the xml files. without effecting the main compiler.

The last topic i have left to last, is memory optimisation. The maximum number of variables the compiler uses for a script is only 8. So each time you run the script it will only use 8 private variables no more.

For example.
_reg1
_reg2
_reg3
_reg4 ect..

Thats the rundown of the system, I havent decided what i am going to do with the compiler in regards if BIS would be intrested in it. But at this current time it was produced to develop the ogn ai hurristic system.
Title: Re:Properties for objects
Post by: Chad on 05 May 2005, 02:42:06
btw it also enables array
int i[][] = new int[100][100];

now try to figure out how it does that :)

or
Solider t[][] = new Solider[100][100];

Yes it will create an array of size 100 * 100 = 10,000 elements
Title: Re:Properties for objects
Post by: Fragorl on 05 May 2005, 08:58:59
Right.

This is an amazing invention, and I think there are quite a few people here at ofpec (me included!) who would be more than interested to hear about it, ask questions, etc.

I suggest that you start a new topic for the compiler, so that people know what you're discussing, perhaps an explanation/introduction so that people understand exactly what it does, how it benefits and improves the editor's job, and so on.

I don't think you're doing it justice by posting here, simply because I'm expecting very little traffic in this thread (maybe one or two people who see the functions and are interested), and consequently very few people will come looking in here for something original (;D - well, you know what I mean), or something other than what's already been mentioned above.

It's my fault I think for starting this thread in the 'ideas' board; it really belongs under beta testing now that the functions have been submitted.

I will gladly re-post my comments if you start a new thread.
Title: Re:Properties for objects
Post by: Chad on 05 May 2005, 12:18:12
Np. I will be doing an offical release in the next coming weeks. The offical release will contain alot of demostration material and documentation with the product. It will also have the second version of the AI hurristic map to show off the compilers full ability.

Until then i normal release small examples like this to public forum to get some intrest going in the product before release :)
Title: Re:Properties for objects
Post by: Fragorl on 07 May 2005, 10:00:00
This is a bit late coming, sorry Planck.

Yes, I have tried using 'else' in functions and it works fine. However in script, i'm not sure what the equivalent is for 'else'; i know '?' == 'if'.

But considering you can do everything by call commands anyway, instead of
? _bool : outcome1 = true
?!_bool : outcome2 = true
you could do
call " if (_bool) then {outcome1=true;} else {outcome2 = true;};"
so what i said wasn't technically right ;)
Title: Re:Properties for objects
Post by: Planck on 08 May 2005, 14:44:07
I had a quick play with using 'else' in a simple script.

Code: [Select]
x = 2

#loop
if (x==2) then {hint format ["%1", "then"]} else {hint format ["%1", "else"]}
~3
x = x + 1
? x == 4 : exit

goto "loop"

It seems to work ok.......however.......

It doesn't like '?' or ':', it seems you must use 'if' and 'then' .....also note the curly brackets seem to be necessary also.

EDIT:  Also the normal brackets round the condition at the start seem to be necessary also.


Planck
Title: Re:Properties for objects
Post by: Fragorl on 09 May 2005, 06:31:01
H'm, so you don't need that 'call .....' business then. Great. And it looks like those semicolons you're forced to permeate functions with aren't required in script either. Usually, the parser complains if you leave out the ';' after the second set of curly braces in a .sqf (with an 'else'), likewise it complains if you miss out the semicolon after the first set of curly braces in a .sqf (just an 'if... then... {}; '). So not really like C++ in that respect.
Title: Re:Properties for objects
Post by: h- on 09 May 2005, 16:22:21
The .sqs parser considers the lines end to be the 'end of line', dunno how it checks it though...
For some reason the sqf parser needs the semicolon to know the end of line, but that's probably because it enables the use of 'formatted' code (whatever it's called in english ::) )...


Anyway, I just dropped here to give hails because I was just about to start doing pretty much the same thing as these functions of yours do, gladly I checked the Ed Depot first :P

So, I will be using these then...
Saves me hrs and hrs of hair pulling... ::)
I probably need to edit them a bit to fit my needs, if that's ok (don't worry; credit will be given...)...

Oh btw, some 'critics/sugs':
-It's not wise to use un-OFP tagged global variable... (variable conflicts are always possible)
-And/or why not give the function user a possibility to define the 'property array' (name it)...?
If this is used in many instances to set/get a lot of stuff it would be nice if you could cathegorise different things in different arrays (easier to keep track of them) and so the Properties array would't grow to have 1 000 000 arrays in it...
The search may become slowish when there's a lot of stuff in there...
Title: Re:Properties for objects
Post by: General Barron on 09 May 2005, 20:27:19
Quote
-It's not wise to use un-OFP tagged global variable... (variable conflicts are always possible)
-And/or why not give the function user a possibility to define the 'property array' (name it)...?
I was going to suggest the exact same thing. One other example of why you would want to do this would be if you are using these functions in an addon. Obviously, you wouldn't want the addon's variables to over-write any mission variables, so the addon's data array would need to have it's own name. Obviously, you could just edit the functions and change the var names yourself, but that is beside the point... :)
Title: Re:Properties for objects
Post by: h- on 09 May 2005, 21:59:17
Quote
Obviously, you could just edit the functions and change the var names yourself, but that is beside the point... :)
Quite... :P
In my case would need quite a few copies of these functions...
Title: Re:Properties for objects
Post by: Fragorl on 10 May 2005, 01:09:36
Thanks for the feedback, General and HateR :D

Anyway, I just dropped here to give hails because I was just about to start doing pretty much the same thing as these functions of yours do, gladly I checked the Ed Depot first :P

That's what they're for!
I was going to suggest the exact same thing. One other example of why you would want to do this would be if you are using these functions in an addon. Obviously, you wouldn't want the addon's variables to over-write any mission variables

No, in you can have two identical sets of the functions, one in the addon, one in the mission, and they will complement each other perfectly. You can simply have this:

["Mission Variables","MissionVar1",player] call SetProperty
["Addon Variables","MissionVar1", leader player] call SetProperty

which will work fine. Of course, nothing is stopping you from having different names for your global array.

Obviously, you could just edit the functions and change the var names yourself, but that is beside the point... :)

Well, no, that is precisely the point! Only a few quick edits to rename 'Properties' for each func, and you'd be sorted. That is quite alot easier than the alternative, supplying your Global Array name as an argument. I'd like to keep the number of arguments as small as possible; it makes for a tidier, more compact 'call' plus there's less to remember! And, you'd be essentially doing the same thing each time:
[object,"Property",value,"MY GLOBAL ARRAY"] call SetProperty

However, if it looks like having separate global arrays is a better idea becuase of what HateR said about array sizes getting too large, I will happily incorporate this argument in as well.

RE: tags. So I should make it 'FRG_properties'? I think I registered FRG a while back... have to check that...
Title: Re:Properties for objects
Post by: General Barron on 10 May 2005, 07:15:12
Well, you could make the "variable name" an optional parameter. If the editor doesn't specify, then it would use the standard FRG_properties var name. And yes, you definately should register and use a OFPEC tag for the global vars in your public scripts.  :-*
Title: Re:Properties for objects
Post by: Fragorl on 11 May 2005, 00:13:52
Oops, I overlooked one very important thing (I think) -

If I allow the user to specify their own name. the function no longer sets itself up. Simply because, in order to modify the array you pass, you have to use 'call format' - like this

call format ["%1 = [blah blah...]", GENB_Properties]

and in order to do that you must FIRST say 'GENB_Properties = []' somewhere.

Ugh. I'm wondering if it's possible to do something like this:

[object, "name", value, GENB_Properties = [], "GENB_Properties"]

or something to tell the parser to create a new 'array' object first, and then use it from there.
Title: Re:Properties for objects
Post by: General Barron on 11 May 2005, 01:15:28
Quote
Ugh. I'm wondering if it's possible to do something like this:

[object, "name", value, GENB_Properties = [], "GENB_Properties"]

or something to tell the parser to create a new 'array' object first, and then use it from there.

Actually, that is possible. You can place ANY command within an array, and it will execute that command when it reads that line of script. I made a thread with that discovery somewhere a while back, but I don't think anyone came up with any real use for the fact.



There is a better solution, however, which you already use. At the beginning of your function, you already check to see if "properties" (the current global array) is instantiated, and if it isn't, you initialize it. All you need to do is a little restructure of your code, and then you can make that check with any variable:

Code: [Select]
_myobject = _this select 0;
_mypropertystring = _this select 1;
_myvalue = _this select 2;
_varname = "FRG_properties";
if ((count _this) > 3) then {_varname = _this select 3;};

if (format["%1",call _varname] == "scalar bool array string 0xfcffffef") then
   {
   call format[{%1 = [];}, _varname];
   }; comment {Create the array once};
...
Note that I changed the script from using a hard-coded variable name, to instead use a string-representation of a variable name. Then I just made it so that if the user doesn't pass a name to the function, it uses the default variable.

From there onto the rest of the function, you would need to change all occurances of "properties" into some type of call or format combination. In general:

To read the value of a variable (given a string with the var name), you just "call" it:

  _value = call _varname

To set the value of a variable, you need to format a codestring and then call it:

  call format[{%1 = _value}, _varname]
  call format[{%1 set[0,_value]}, _varname]

and so on.


Title: Re:Properties for objects
Post by: Fragorl on 11 May 2005, 08:23:11
Of course, I forgot the one fundamental thing. Arrays are returned by reference not by value, in ofp as elsewhere. I was worrying that (_myArray = _this select 3) would make a copy of _myArray, but of course it wouldn't. It just makes a new pointer to the original, and any modifications you make will affect the overall array ::)

So that whole worry of initialising the variable before changing it with a call format was unfounded. So, I will update the function as you suggest, probably in the next couple of days :)
Title: Re:Properties for objects
Post by: General Barron on 11 May 2005, 22:52:19
Hmm... I forgot about that one as well. So I suppose there would be no need for call/formatting then?

The user would still be required to initialize the variable on his own though...
Title: Re:Properties for objects
Post by: Fragorl on 12 May 2005, 10:11:27
Right, so that means you don't need the "GENB_Properties", just the 'GENB_Properies = []' argument. That could work fine
Title: Re:Properties for objects
Post by: Fragorl on 05 Jul 2005, 02:42:04
OK, I have at last gotten around to incorporating HateR's and Gen Barron's suggestions

SetProperty v1.1 (http://www.ofpec.com/editors/funcref.php?filter_func=63)
GetProperty v1.1 (http://www.ofpec.com/editors/funcref.php?filter_func=64)

As of v1.1 the two functions can take an optional extra argument. This is a string, and is the name of the custom array you wish to store the properties in.

The custom array is created the first time either of the functions is run, just like the default array which is still used if you don't specify the extra parameter.

The default array is now named 'FRG_Properties' in accordance with Ofpec rules.

If you substitute the two old functions with these two new ones, they are completely backwards compatible. No additional setting up is required.

Thanks Gen and HateR
Title: Re:Properties for objects
Post by: General Barron on 06 Jul 2005, 10:07:43
Nice. This will be seeing some use. :)
Title: Re:Properties for objects
Post by: DBR_ONIX on 07 Jul 2005, 16:20:13
I had a try with these functions (V1, not the new version I think)
But, I couldn't get them to work ???
Basicly, I wanted to use them for a money system on an RPG mission.
So at the start (Tried init.sqs, and then renaming it to start and running it via radio trigger to see if running it before screwed it up, for some strange reason..), I run the following code :
Code: [Select]
;General scripty stuff
GetProperty = preprocessFile "getproperty.sqf"
SetProperty = preprocessFile "setproperty.sqf"

; Set up starting stuff
[aP1,"BankMoney",200] call SetProperty
[aP2,"BankMoney","200"] call SetProperty

[ap1,"PocketMoney",10] call SetProperty
[ap2,"PocketMoney",10] call SetProperty

; Debug
player sidechat FORMAT ["Debug - ap1 Bank : %1 :: ap2 Bank : %2 ::: ap1 Pocket : %3 :: ap2 Pocket : %4",[ap1,"BankMoney"] call GetProperty,[ap2,"BankMoney"] call GetProperty,[ap1,"PocketMoney"] call GetProperty,[ap2,"Pocket"] call GetProperty]

player sidechat "blah"

But, it never got to that sidechats..

I'll try the new version when I get back from holdiay (leaving tomorrow). But if I could get this working, it'd make the misison so much easier to code.
Instead of having a buy script for each player, and addng them in grouped-to-player triggers (Horrible). I could have dialouges (Need someone who can use dialouges, or learn them by self.. noo :P), which run a buy script,
And check _this select 0 (not sure if this works with dialouges, but just the general idea) has enough money
If so, remove the cost from the varible (from gerproperty), and shove it back via setProperty.
This means there could be a single buy script, with a ?(_action=="gun"):goto "buyGun" for each thing (Cars, guns, food etc), which takes the money, and does appropriate action (create gun/ammo in weaponHolder thing, for example)
But, this all hinges on getting these functions to work :P

Is there something I'm doing wrong in that code?

Thanks :)
- Ben
Title: Re:Properties for objects
Post by: Fragorl on 09 Jul 2005, 00:58:03
Hm, don't know why the sidechats aren't working.

However, I replaced the sidechat format... with a hint format etc and it all came out fine. Except for "pocket" which was undefined, but I guess you changed that for testing purposes.

Problem solved ;)
Title: Re:Properties for objects
Post by: h- on 09 Jul 2005, 14:11:00
Sweetness :)

This stuff is now quite essential part of some scripts/systems in FWW2 Mod... :P
Title: Re:Properties for objects
Post by: Fragorl on 10 Jul 2005, 01:17:42
It makes me glad to hear that ;D
Title: Re:Properties for objects
Post by: Bluelikeu on 13 Jul 2005, 01:45:20
Just as a side question: Where is Chad and his infamous compiler? 'Been waiting for quite some time.
Title: Re:Properties for objects
Post by: Fragorl on 17 Jul 2005, 10:09:31
Bad news. I've been putting the system through it's paces, so to speak, by taking a large number of functions (in string form, loadfile'd) and adding them into a single array via setproperty. Everything goes according to plan until the fourth function or so is loaded, after which operation flashpoint crashes to desktop whenever you try and add another.

So there is obviously an upper limit on the total amount of data you can store in an array, which is confusing since up until now I'd thought that arrays in operation flashpoint functioned a lot like lists- meaning that they annex memory wherever there is a chunk of the correct size, regardless of it's position on the stack. The system works fine if I create a new array and start filling that one up.

Any ideas on how to get around this are welcome. I remember a thread a while back where someone mentioned troubles with array sizes.... i didnt bother to read it at the time, unfortunately. If anyone's run into this problem, let me know. Thanks
Title: Re:Properties for objects
Post by: UNN on 17 Jul 2005, 18:54:42
Hi,

It's not array's that have the limit your hitting but the Format command:

Code: [Select]
if ( format["%1", FRG_Properties] == "scalar bool array string 0xfcffffef") then {
      _userPropertyArray = [];

If you have to many characters in your array and you try and format it to a string, it will crash.

You can use something like this to get round the problem:

Code: [Select]
_ARRAY_INITIALISED={_T=True ; If (Count UKF_WMIKARRAY>=0) Then {_T=False} ; !_T};

If !(Call _ARRAY_INITIALISED) Then
   {
   UKF_WMIKARRAY=[];
   };
Title: Re:Properties for objects
Post by: Fragorl on 18 Jul 2005, 01:23:41
Oh, i see!

In fact, i kept running into the problem where I had the line 'hint format' etc etc, in some other code .... but i thought it was the hinting, not the formatting, that was the trouble. It would have never even occurred to me to check for 'format...'. Thanks a whole bunch, UNN, saved my skin ;D

That workaround is pretty fantastic as well.... i assume the 'call' command supresses the error message that comes from 'count UKF_WMIKARRAY>=0' when UKF_WMIKARRAY is uninitialised... very tricky. i take it this is a problem that UK forces ran into as well. then?

Cheers


Title: Re:Properties for objects
Post by: UNN on 18 Jul 2005, 02:43:28
Quote
It would have never even occurred to me to check for 'format...'.

It's probably the same with Hint to. Or at least, just the way ofp handles strings in both cases?

Quote
That workaround is pretty fantastic as well

Thank BN880 from the COC for that :) It is quite handy, you can even use it to surpress script not found errors, and detect some other variable types. Does not seem to work with Object related commands though :(

Quote
i take it this is a problem that UK forces ran into as well. then?

It's an old problem, I just happend to have those scripts at hand. Before BN880's function, you had to have a boolean along with the array, and check that with Format, rather than the array.

It should be faster than comparing a string with the format command, but not as fast as just checking a single boolean. Depends how many elements you have in your array I guess.

You could change it to this, and derive your booleans name from the user defined array name you get as a parameter:

Code: [Select]
_ARRAY_INITIALISED={_T=True ; If UKF_WMIKARRAY_B Then {UKF_WMIKARRAY_B=True ; _T=False} ; !_T};
Title: Re:Properties for objects
Post by: Fragorl on 18 Jul 2005, 08:30:13
Version 1.15
(same place)

Thanks UNN. The conditional statement

Code: [Select]
_ARRAY_INITIALISED={_T=True ; If (Count UKF_WMIKARRAY>=0) Then {_T=False} ; !_T};

If !(Call _ARRAY_INITIALISED) Then
   {
   UKF_WMIKARRAY=[];
   };
does not work directly, since the function above returns 'scalar bool array string nothing 0xfcffffef' when the array is not initialised, which doesnt activate the 'if' clause. However, it does return 'true' when the array was init'd, and this means I could still use the general idea as a check. the new code (from the new versions) is as follows:

Code: [Select]
_checkVariable = {_bool = true; count _this >= 0; _bool};

if      (count _this == 3) then {
   _init = false;
   if ( FRG_Properties call _checkVariable ) then {
      _userPropertyArray = FRG_Properties;
      _init = true;
   };
   if (!_init) then {
      _userPropertyArray = [];   
      FRG_Properties = _userPropertyArray;
   };
} else {
   _init = false;
   if (count _this == 4) then {
      call format ["_userPropertyArray = %1", _this select 3];
      if ( _userPropertyArray call _checkVariable ) then {
         call format["_userPropertyArray = %1",(_this select 3)];
         _init = true;   
      };
      if (!_init) then {
         _userPropertyArray = [];
         call format["%1 = _userPropertyArray",(_this select 3)];
      };
   };
};

where _checkvariable returns either 'true' or scalar etc etc etc. Then the boolean '_init' is used to check whether the var was initialised (nb an else statement could not be used here. ) 'true' will activate the 'if', scalar etc will not, however if the 'if' is not activated then _init is left as false, telling the game to run the next 'if' statement after that.
Title: Re:Properties for objects
Post by: UNN on 18 Jul 2005, 09:24:38
Quote
does not work directly, since the function above returns 'scalar bool array string nothing 0xfcffffef' when the array is not initialised?

Are you sure you implemeted it correctly? You can run the following as a script from a trigger, and it will only execute the sidechat once. No matter how many times you keep calling it.

Code: [Select]
_ARRAY_INITIALISED={_T=True ; If (Count TESTARRAY>=0) Then {_T=False} ; !_T}

If !(Call _ARRAY_INITIALISED) Then {TESTARRAY=[] ; Player SideChat Format ["Run Once %1",TESTARRAY]}

_ARRAY_INITIALISED always returns false the first time you run it.

I wonder if somethings going wrong elsewhere, as the function you posted does not work when tried on it's own:

Code: [Select]
_checkVariable = {_bool = true; count TESTARRAY>= 0; _bool};

If (Call _checkVariable) Then {TESTARRAY=[] ; Player SideChat Format ["Run Once %1",TESTARRAY]};

The _CeckVariable function will always return true, even if TESTARRAY does not exist? I never got a single scalar error results as final output , from any of the functions?
Title: Re:Properties for objects
Post by: Fragorl on 20 Jul 2005, 06:32:36
Apologies for my late reply, I was floored by a nasty bug within hours of posting last time, and I'm still recovering. :-\

I ran the script as you said, and the results were as you predicted.

I think the problem here is that i modified the inline function so that it reads:
_checkVariable = {_bool = true; count _this >= 0; _bool};
and takes an argument, instead of just checking a single array name such as 'TESTARRAY'. For some reason, this is when the scalar etc etc etc shows up.

 If you try this version, you will find you get the results i described:

[] call _checkVariable; result is 'true'

_array call _checkVariable; result is 'scalar bool array string nothing 0xfcffffef'

_array = [];
_array call _checkVariable; result is 'true'

The thing is, the user may specify their array to have any name they like, so it's easier to run a general check like this. The code from the new functions is also easy enough to follow, so nothing's been lost in that department.

Quote
I wonder if somethings going wrong elsewhere, as the function you posted does not work when tried on it's own:

I did a fresh test; created a new editor mission, d/l'ed the functions from the ed's depot, and did some basic tests. The functions worked fine. I've no idea what could be causing your problem
Title: Re:Properties for objects
Post by: UNN on 20 Jul 2005, 09:10:19
I tried passing the array, and used _This in the function, got the same results as you. It appears to treat errors with _This differently to other variables.

I modified the original function, with a local variable instead of a global, and it works for both now:

Code: [Select]
_ARRAY_INITIALISED={private ["_array"] ; _Array=_This Select 0 ; _T=True ; If (Count _Array>=0) Then {_T=False} ; !_T};

If !([TESTARRAY] Call _ARRAY_INITIALISED) Then {TESTARRAY=[] ; Player SideChat Format ["Run Once %1",TESTARRAY]};

Although I'm probably going to drop counts on arrays, and add a boolean check. Sometimes I have 200+ elements, so I don't want to be counting them all the time. Assuming thats what OFP does when you call Count ???
Title: Re:Properties for objects
Post by: Fragorl on 26 Jul 2005, 00:44:53
I *guess* that OFP keeps track of the size of its arrays by treating them as objects, and then whenever they are operated on, eg. by the 'set' command, a variable within the array object that keeps track of it's size is also modified. This approach would mean negligible overhead associated with checking the array size. However, we've got no way of knowing.... However, the thing about the function approach is, once any operator that is designed to work on arrays has been run on an undefined variable (for example 'count'), the whole function aborts and spits out the scalar array nonsense. So you'll have noticed that in the function:

_checkVariable = {_bool = true; count _this >= 0; _bool};

the 'count _this' bit isn't actually doing anything except killing the function if the parameter is not an array. So perhaps you could have

_checkVariable = {_bool = true; _this + []; _bool};

which would do excalty the same thing, except without doing any checks on the array.
Title: Re:Properties for objects
Post by: UNN on 02 Aug 2005, 04:15:56
Quote
I *guess* that OFP keeps track of the size of its arrays by treating them as objects, and then whenever they are operated on, eg. by the 'set' command, a variable within the array object that keeps track of it's size is also modified.

This has to be one possibility, although it would have to cater for + and - as well as set. But easy enough if your BIS, I guess.

Quote
_checkVariable = {_bool = true; count _this >= 0; _bool};

I don't think it kills the function, it just skips this bit:

Code: [Select]
; count _this >= 0 ;


Title: Re:Properties for objects
Post by: Fragorl on 02 Aug 2005, 05:14:25
I think it skips the rest of the function after it encounters the dodgy bit, ie the 'count' operation on a non-array type variable. '_bool' is not returned so _thingie call _checkvariable has some sort of Not-A-Value type variable assigned to it.

+/- ... BIS can do whatever they like, as you point out.
Title: Re:Properties for objects
Post by: UNN on 02 Aug 2005, 14:02:25
Quote
I think it skips the rest of the function after it encounters the dodgy bit, ie the 'count' operation on a non-array type variable. '_bool' is not returned so _thingie call _checkvariable has some sort of Not-A-Value type variable assigned to it.

Gah..Yeah it's that damn _this again. You are correct, sorry. It's just not standard behaviour, but it does offer a safe workaround for the count command.

Cheers
Title: Re:Properties for objects
Post by: Fragorl on 02 Aug 2005, 22:56:57
Interesting how many different 'nothings' there are: <Null>, <Null-Object>, scalar bool array string, scalar bool array string nothing, then of course there's scalar, bool, and so on. I think I understand where most of them come from, although I am a little hazy one where '<null>' originates, despite having seen it around (mostly when checking arrays, it seems to turn up occasionally). I guess we've just learned that the 'nothing' bit means a null value assigned when a function is called using _this.... strange are the ways of OFP.
Title: Re:Properties for objects
Post by: Killswitch on 05 Aug 2005, 10:53:01
I've been using the Set/GetProperty functions for a while. Neat. However, they needed some fixin'  ;D

The variable existence checks you've been having troubles with...well, this works:
Code: [Select]
...
_checkVariable={_r=true;if(count(_this select 0)>=0)then{_r=false};!_r};
...
...
if ( [FRG_Properties] call _checkVariable ) then {
...
...
Note how I call _checkVariable by passing the parameter into it as an array. OFP has it's ways...
Title: Re:Properties for objects
Post by: UNN on 06 Aug 2005, 00:19:32
Lol, I seem to be going round in circles with this, I'm constatly changing my mind. I just did a double check, using:

Code: [Select]
_checkVariable = {_bool = true; _this=_this+[]; _bool};

Player SideChat Format ["Run Once %1",TESTARRAY Call _checkVariable]

And you get a 'scalar bool array string nothing 0xfcffffef' error in sidechat. But that does not appear to equate to false? So if you change it to:

Code: [Select]
_checkVariable = {_bool = true; _this=_this+[]; _bool};

If !(TESTARRAY Call _checkVariable) Then {Player SideChat Format ["Run Once %1",TESTARRAY]}

The sidechat command never gets called.

But you could use it this way:

Code: [Select]
_checkVariable = {_bool = true; _this=_this+[]; _bool};

If (Format ["%1",TESTARRAY Call _checkVariable]=="scalar bool array string nothing 0xfcffffef") Then {TESTARRAY=[]}

Thats fine, because your never trying to format the entire array, just a boolean. So it always returns a true or false result.

@KillSwitch

We were trying to come up with a way that avoids using the count command, as we don't know for sure how ofp handles array counting.
Title: Re:Properties for objects
Post by: Fragorl on 09 Aug 2005, 13:10:02
Sorry about the delay for my reply, I've been busy with boring RL things... annoying distractions mostly ::)

Killswitch:
Thanks, glad to hear Set/Get are working for you.
I'd just like to point out that at the moment the funcs are tested and working in good order. We're fine tuning, just to make sure we have the quickest and most un-intensive initialisation check possible, to cut down on overhead (a good general purpose word that ;D). UNN has brought up that using count, once the array and all its subarrays start to get large, might create a longer and longer delay, something i'm obviously anxious to avoid.

UNN & Killswitch
The method is entirely unorthodox, as you can see, but relatively fast. It relies on several factors, none of which would qualify as particularly good programming practise :P. They involve:

- Deliberately causing an error in a returning method, by passing it the wrong type of data if uninitialised. An operation inside the method (either count or something else array specific) causes the error when it is passed the wrong type of data. However, the error message gets suppressed

- Passing a nothing/null type variable to an 'if' statement (that's if the array was uninitialised). This is not true, not false, in fact not a boolean at all. Oddly, this doesnt produce an error message, but ofp simply skips the if statement.

- If the array was initialised (ie already existed), the if statment is run. A boolean in this if statement is set to true, which tells the function not to initialise the array. It's all quite straight forward

Code: [Select]
   _init = false;
   if ( FRG_Properties call _checkVariable ) then {
      _userPropertyArray = FRG_Properties;
      _init = true;
   };
   if (!_init) then {
      _userPropertyArray = [];   
      FRG_Properties = _userPropertyArray;
   };
The reason i dont use an else here, instead of the two 'if's, is because if the 'if' is skipped over, then the else is too.
Title: Re:Properties for objects
Post by: Fragorl on 05 Sep 2005, 06:07:17
OK - UNN's rating check method for unit pointers has provoked me into action :). I've streamlined the Set and Get funcs here to run more quickly (with any luck).

What I've done:

Got rid of all the extra variables that I didn't really need in the functions, to save OFP  time reading and writing them.

Improved the coding for the initialisation bit at the beginning of the functions- it wasn't written very well, and now takes up about half the space. I'd effectively duplicated the code, which i didnt need to do.

Changed how the Get function works - it should be much faster now in returning values.

Changed the structure of the while loops in the Set method, and taking out one of the break variables (didn't actually need it).

My use of arrays was incorrect in some places. Basically I was writing the same information into the property array twice, because I didnt fully understand how OFP handled arrays when I first wrote it. Needless to say that has been changed, hopefully with a correspondig speed boost.

Removed all comment{} comments and replaced them with // and /**/ comments. This is because when the file is read into a string, the comment{} comments aren't removed, whereas the other type are. This surely had a detrimental effect on speed. Note that the functions will no longer work with loadfile after this.

For the few people who are interested, I will post the new functions as soon as I have tested them.

EDIT:

I have tested them, and there is quite a marked performance increase for the new versions. Lag becomes noticeable (for me) at 6 x (10 'get' calls + 10 'set' calls) x 0.1 second loops. The variables being stored and retreived were ~80 character strings. The old versions were unusable at these kind of frequencies.

Funcs uploaded at the usual place.

Title: Re:Properties for objects
Post by: hardrock on 08 Dec 2005, 20:01:02
I'd like to dig this older thread up again with some improvement suggestions.

Right now, your array format is [ [object, ["prop",value], ["prop",value], ["prop",value]] , [object,...] ]
I think you could improve this by creating an a bit different array structure. You would have one array containing all the objects, and another one containing arrays of properties and values.

[Objects, [ [Properties,Values], [Properties,Values], [Properties,Values] ]

or, written more codewise

[ [object1, object2, object3],[  [  ["prop1","prop2","prop3",...], [value1, value2, value3,...]  ] , [  ["prop1_1","prop1_2","prop1_3",...], [value1_1, value1_2, value1_3,...]  ]  ] ]

This way you could speed up the setting process, as you would simply have to check if the object is in the objects array (using 'in') instead of running through the whole big array. The same counts for the single properties: Instead of loading an array, checking if the first element is the searched property, loading the next one, checking again a.s.o. only to find in the worst case, that the property doesn't exist yet (after having checked 20 others), you simply type if (_prop in _properties). If not, you push the new property and value on the according arrays and get the hell out of there. Else you can still do some looping.

If you have problems with the size of the arrays, you could even split into three arrays, one for the objects, one for the properties and one for the values.

I hope this helped a bit.
Title: Re:Properties for objects
Post by: Fragorl on 11 Dec 2005, 08:43:43
Well, that's definitely a possibility - it appeals partly because of the cutdown on code for checking if a property exists,  partly because it groups the searching terms, ie the strings, together. Had I started writing the functions today, I would have most likely created the array the way you've suggested here.

I don't know if we can make any comment on how the 'in' operator works, or whether or not the game engine does something with it's arrays to make searching go faster. But It'd be nice to think that the Bohemians did something of the sort.

This is probably worth a redesign, except that I don't think it will benefit very many people. If anyone really wants, though, then I'll do it, and also improve the search technique (ie with a binary search or something).

Title: Re:Properties for objects
Post by: General Barron on 11 Dec 2005, 11:04:50
This is probably worth a redesign, except that I don't think it will benefit very many people. If anyone really wants, though, then I'll do it, and also improve the search technique (ie with a binary search or something).

I'm always in favor of people updating their resources. There's always room for improvement :).

As far as searching goes: I really don't think it is worth it, if even possible, to do anything other than just iterating one at a time thru the list. Searching generally requires sorting (by what? side?), and I seriously doubt anyone will even fill the array with enough items to make searching worthwhile.
Title: Re:Properties for objects
Post by: Fragorl on 12 Dec 2005, 00:31:54
I'm always in favor of people updating their resources. There's always room for improvement :).
This is true.

wrt sorting, I was going to write a function do it by string (recall that a new propery can be assigned to an object or a string, and all property names are strings ... in the case of objects i could just format[] them) until i remembered that there is no way to examine strings character by character ... which rules out comparing them ... which rules out sorting them ... which rules out a fancy search. But if there were such facilities, then this would most certainly be possible.

> Filling the array with enough items...
You never know. If someone wanted to implement this for, say, every object on the map, or use this in an ecp-sized project (hundreds of small and mid-sized pieces of data stored...), then it might be conceivably possible. I'm sure that the slowness of retrieval is due to the priority that functions don't get, however.  :-\