Jump to content

Hypnomatic

Member
  • Content Count

    35
  • Joined

  • Last visited

  • Medals

Everything posted by Hypnomatic

  1. This is a fairly simple script I began writing to after being a bit curious about how ARMA's bullet physics worked, particularly the ricocheting. Recently there was a post on /r/ARMA about bullet penetration, so I cleaned it a bit up to get what you see below. This'll basically trace the movements of projectiles, so you can very clearly see their flight path, arc, and interaction with cover. It's not perfect, but it does what it needs to pretty well. Plus it's surprisingly addicting to just run around and shoot stuff just to see what happens to the bullets. UPDATE 10/26/2013: As some of you may have seen, Dslyecxi recently made a video that featured a modified version of this script, where the color of the line was reflective of the velocity of the bullet at that moment. He posted his modifications, which are now implemented in the main script! To activate it, pass true as the seventh parameter, like so: [player, nil, nil, nil, nil, nil, true] call hyp_fnc_traceFire; Note that as it stands, enabling the velocity-based coloring will override whatever color you specify in the second parameter. I have an idea or two of ways to fix it, but wanted to get this update out there first. Also, I completely forgot that the Steam Workshop was a thing, so I published a very simple mission with this script, and Tonic's Virtual Ammobox System and Virtual Vehicle Spawner enabled here: http://steamcommunity.com/sharedfiles/filedetails/?id=189421090 If there are any problems with the Steam Workshop mission, please mention them to me, as I very rarely actually publish missions, just scripts. The code: Download the raw code: https://raw.github.com/hypnomatic/ARMA_Examples/master/Tracers%20Script/Tracer_v0-2-1.sqf Link to Steam Workshop page for a simple mission featuring this script: http://steamcommunity.com/sharedfiles/filedetails/?id=189421090 How to use: For basic use on just yourself, assuming you don't want to deal use the Steam Workshop mission, all you need to do is create a new mission, and paste the linked code into your init.sqf. After all that code in your init.sqf, add the line: [] call hyp_fnc_traceFire; Save the file, restart your mission, and you should be good to go! With no extra parameters, this code is functionally equivalent to the original: it creates red lines behind projectiles fired by the player that are super smooth and persist forever. You can specify more parameters within the square brackets if you want a more specific outcome, as in the following examples. [] call hyp_fnc_traceFire; Save the file, restart your mission, and you should be good to go! With no extra parameters, this code is functionally equivalent to the original: it creates red lines behind projectiles fired by the player that are super smooth and persist forever. You can specify more parameters within the square brackets if you want a more specific outcome, as in the following examples. The syntax and expected parameters are as follows: (Note that all 7 parameters have default values, so you don't need to specify all of them if you don't want that specific feature.) [_unit, _color, _lifetime, _interval, _maxDistance, _maxDuration, trackVel] call hyp_fnc_traceFire; 0: _unit - Unit or vehicle to trace the projectiles of. Defaults to the player if no other parameter is specified. 1: _color - Color array for all lines fired by this unit. This is an array formatted as [r,g,b,a], with each value being between 0 and 1. (BIS wiki page) 2: _lifetime - Maximum number of seconds the projectile's line should linger after firing. Note that this timer starts at the same time as maxDuration, but is not checked until after the script finishes drawing a line. This can be thought of as a lower bound for how long a line will exist, as all lines will persist AT LEAST for lifetime's duration. 3: _interval - Number of frames to skip between each adding segment of the line. Defaults to skipping no frames, so the lines are updated every frame. 4: _maxDistance - Maximum distance from the initial position a projectile's line should be updated. Defaults to no maximum. 5: _maxDuration - Maximum number of seconds from firing a projectile's line should be updated. Defaults to no maximum. 6: _trackVel: If true, _color is overridden by the a color indicative of velocity at that moment. Thanks to Dslyecxi for this one! (Default false) All of these options may be a bit confusing, so let me offer an example or two: [player, [1,0,0,1], 3, 2, nil, 3] call hyp_fnc_traceFire; Can be thought of as saying: Track every projectile of: the player, with a red line, that will last for at least 3 seconds, that is updated every third frame (ie 2 frames skipped between each update), with no limit on distance, but will not be updated for more than 2 seconds. Now, if you want to completely omit a parameter so the default value is used, you have two options. You can either pass nil in place of that parameter, as in the example, which is ARMA's equivalent of "nothing". If you only want to pass specify a unit, and use the defaults for everything else, simply omitting all unwanted parameters like so: [player] call hyp_fnc_traceFire; However note that this only works if you want to leave out all parameters after the ones you specify. So if I wanted to specify a player and a max duration, I would need to pass: [player, nil, nil, nil, nil, 5] call hyp_fnc_traceFire; If you don't really mess with scripts much, this may be a bit tough to wrap your head around, so I'll be happy to answer any questions you may have. To get a bit more fancy, you can create a few units/vehicles in the editor, name them, and follow the example near the top of this post to specify your own parameters! Just put whatever you named your unit in the editor in the first parameter, and you're good to go! Just as an example: [player] call hyp_fnc_traceFire; [heli1] call hyp_fnc_traceFire; [car1] call hyp_fnc_traceFire; Would cause bullets fired by the player's unit, heli1, and car1 to be traced with default options. To give you yet another idea for what you can do, the following code will cause all units on the map to have their projectiles traced with team-specific colors that disappear after 3 seconds: { if (faction _x == "OPF_F") then { [_x, [1,0,0,1], 3, 2, nil, 3] call hyp_fnc_traceFire; }; if (faction _x == "BLU_F") then { [_x, [0,0,1,1], 3, 2, nil, 3] call hyp_fnc_traceFire; }; if (faction _x in ["IND_F", "IND_G_F", "CIV_F"]) then { [_x, [0,1,0,1], 3, 2, nil, 3] call hyp_fnc_traceFire; }; } forEach allUnits; In Use: Here's Dslyecxi's recent video featuring this script with his now-implemented modifications: And here's a snazzy video by Jester814 that makes fairly heavy use of the original version of this script: Notes/Known Bugs: - Many optics, and some vehicle interiors (Pawnee comes to mind) do not play nicely with the drawn lines. A chunk of space floating in front of the sight will block out the lines behind them, so I recommend testing without optics. The Pawnee, for whetever reason, does not show any drawn lines at all in first person, so you need to enter third person to see lines from it. - Water *mostly* works. Try it and you'll see what I mean. My guess is that it's getting the position relative to the sea level fine, but then has the height of the wave below it added, causing some mildly bendy lines. - You may notice that the "fired" eventHandler has a seemingly redundant parameter added on. This actually is an interesting consequence of having the code spawned, as it seemed the scheduler put just enough of a delay between the event firing and the code within the spawn executing that the first gotten position of the bullet would be a few meters away. So this gets the first position before the spawn, and passes it in, and all seems to work well. Note that I don't actually have much scientific evidence to backup my theory, just that the first gotten position within the spawn was certainly late, while outside the spawn it was just fine.
  2. Phew, sorry for the long streak of inactivity, been a bit preoccupied for the past month or two. @Fennek: As far as exporting data goes, it's not 100% intuitive at the moment, especially considering there's a bit of a divide between the workshop branch and the github branch. I need to look at the rewrite I was working on a month ago however, as I believe that one may lend itself to this better. Will report back after I check. Now regarding optimal spacing:It really doesn't matter how far the objects are: it matters how long it takes to travel between them. Positions are calculated every time the game renders a frame, so the more frames that are rendered while the bullet is flying the more accurate the line. You can use the command setAccTime (http://community.bistudio.com/wiki/setAccTime) to slow down time so more frames are rendered per in game second. That will give you a better idea than any spacing should. Once again, sorry for being so out of the loop with ARMA for the last two months, will be trying to get fully caught back up in the next week!
  3. @Grumpy Old Man: Yep! As a matter of fact, it is almost exactly that (Below is the code for BIS_fnc_conditionalSelect): /* ...Same header rakowozz posted, but I excluded it... */ private ["_cond","_ret","_x"]; _cond = _this select 1; _ret = []; //to be filled with return array { //just call the condition directly //note the current element is already assigned to _x, and will be accessible in the condition if(call _cond) then {[_ret, _x] call BIS_fnc_arrayPush}; } foreach (_this select 0); _ret
  4. Hypnomatic

    From display name to classname

    Glad to be of help! Also good point on that error message; I got it once on my first run, then forgot to restart the mission to see if it was still happening when I finished the rest of the code. There is one minor modification I would make to your changes however; Basically combine your new check into the old check and use ARMA's clunky Lazy evaluation. It doesn't effect the actual output of this function at all, but I think the exitWiths inside the loop won't properly break the loop if they're nested inside another if statement. Not 100% positive that it actually will be behaving like that, and can't test it now, but it may be worth considering it as: Hypno_fnc_getName = { //Modified by Iceman77 private["_this","_displayName","_default","_out","_cfgWeapons","_cfgMagazines"]; _displayName = _this select 0; _default = [_this, 1, "ERROR: Item not found"] call BIS_fnc_param; _out = _default; _cfgWeapons = configFile >> "CfgWeapons"; _cfgMagazines = configFile >> "CfgMagazines"; for "_i" from 0 to count (_cfgWeapons) - 1 do { if ((isClass (_cfgWeapons select _i)) && {_displayName == getText(_cfgWeapons select _i >> "displayName")}) exitWith { _out = configName (_cfgWeapons select _i); }; }; if (_out != _default) exitWith {_out}; for "_i" from 0 to count (_cfgMagazines) - 1 do { if ((isClass (_cfgMagazines select _i)) && {_displayName == getText(_cfgMagazines select _i >> "displayName")}) exitWith { _out = configName(_cfgMagazines select _i); }; }; _out }; Also as far as naming conventions go, I tend to use hyp_ instead of Hypno_ just because I'm lazy and like to type as little as possible, but whatever you use is entirely up to you. Heck I wouldn't be offended if you just decided to remove the Hypno_/hyp_ from the name entirely if it's more convenient for you. EDIT: Just noticed that _name wasn't updated to _displayName in the private[];. Likely wouldn't ever cause issues, but worth fixing to be safe.
  5. Hypnomatic

    From display name to classname

    I came up with one possible solution (Documented heavily-ish for anyone who stumbles upon this page): /* Syntax: [_name] call _fnc_getFromName; [_name, _defaultValue] call _fnc_getFromName; Return Type: STRING (Classname of corresponding DisplayName, if found) Additional Note: Simply traverses CfgWeapons and CfgMagazines, and checks every entree to see if its what we're looking for. Returns the classname or an optional default value (Default default value is "ERROR: Item not found"). Features Modifications by Iceman77 */ _fnc_getFromName = { private["_this","_displayName","_default","_out","_cfgWeapons","_cfgMagazines"]; _displayName = _this select 0; _default = [_this, 1, "ERROR: Item not found"] call BIS_fnc_param; _out = _default; _cfgWeapons = configFile >> "CfgWeapons"; _cfgMagazines = configFile >> "CfgMagazines"; for "_i" from 0 to count (_cfgWeapons) - 1 do { if ((isClass (_cfgWeapons select _i)) && {_displayName == getText(_cfgWeapons select _i >> "displayName")}) exitWith { _out = configName (_cfgWeapons select _i); }; }; if (_out != _default) exitWith {_out}; for "_i" from 0 to count (_cfgMagazines) - 1 do { if ((isClass (_cfgMagazines select _i)) && {_displayName == getText(_cfgMagazines select _i >> "displayName")}) exitWith { _out = configName(_cfgMagazines select _i); }; }; _out }; Basically, it'll just iterate through CfgMagazines and CfgWeapons until it finds the displayName it's looking for. It'll take an optional second parameter as a default value, or returns "ERROR: Item not found" as the default default value. It's not perfect, but I think it does its job well enough. It probably goes without saying, but anyone can feel free to use/modify/etc this as they need!
  6. Not possible as far as I'm aware. The way I'm interpreting that post is that you can't ask the user to go download a separate addon of their own accord, but addons you distribute with the mission or campaign are fair game. CBA comes to mind as an example; you can't ask the user to download and install CBA, but if you get permission to include it with your submission it'd be fine. Once again this is my best guess, so I'd recommend waiting for a more official response.
  7. @Larrow: Yep, I've been looking into what else I can replace with macros for that exact reason. I actually tried writing many of these functions as Macros as well, and they predictably performed noticeably faster. I'll do more testing later, and if it's actually worth the performance I'll rewrite and release these as macros too. The two biggest reasons I put that idea on hold are optional parameters and recursion: There's no real intuitive way I could find to allow parameters to be excluded, so a few functions would become a bit more awkward. Also a few of these functions are recursive, and you can't really do recursion with macros. In hindsight I see a way to do it, but it's not as pretty as I'd like. Will do more testing later on! Also, one thing I "think" I observed is that functions in the global namespace are a bit slower to be accessed than those that are local. I haven't done any proper testing on this however, so it could have been any number of other conditions responsible. Will also do some testing on this later on and report back with what I find!
  8. Hello all! I'm here to finally release the first batch of functions I've written for ARMA 3. I've been slowly accumulating a list of utilities and functions to solve miscellaneous tasks or problems since the release of the Alpha. A little while ago I decided I wanted to clean them up, document them well, and release them for all to use. One simple mini-wiki later and we're here! The full documentation, including downloads and installation instructions can be found HERE, however allow me to share a few of the highlights: Functions to add escape characters to strings, as well as strip them Functions to manipulate and handle arrays by reference more easily Functions to emulate a Key-Value (Associative) Array, complete with JSON encoding and decoding I am releasing all of this code under the very permissive BSD license, so you are more or less free to whatever you wish with this code. Use it, modify it, distribute it with your missions, it's just about all good! (Credit is always appreciated however.) If you have any issues, suggestions, comments, or criticism, please feel free to post it here! This is my first release of anything to quite this scale, so suggestions for improvement on the code, the site, etc. are very much so appreciated! Enjoy!
  9. Got a minor update for today! 2013-12-8 - Added kva_fnc_count and kva_fnc_forEach! (http://arma.dathypno.net/Function/kva_fnc_count/, http://arma.dathypno.net/Function/kva_fnc_forEach/) - Fixed minor bug with arr_fnc_resize not returning the reference if passed a positive length. - Added comment to arr_fnc_compare (Basically me rambling about how I found it funny string-comparison works so well on arrays). kva_fnc_count This guy's a super simple function, just returns the number of key-value pairs present in the KeyValueArray. Does not exclude anything tagged. kva_fnc_forEach Function to make traversing KeyValueArrays easier in certain circumstances, particularly if we have non-specific keys. The code you pass can include the following variables to reference the contents of the KVA: _x: Contains the current VALUE _forEachKey: Contains the current KEY _forEachIndex: Contains the current INDEX That's it for now! I'm probably next going to optimize the parsing of JSON, then switch gears to cleaning up and releasing two of my bigger functions: getting and setting an object's state via KeyValueArrays! Doing exactly this is probably the initial motivation for writing KeyValueArrays; I wanted a reliable way to completely serialize a player, vehicle, object, etc. as JSON, and pass it out of ARMA via an extension. The latest and greatest version I've written of this is likely coming soon!
  10. Okay, so now that it's a bit more reasonable of a time, I figure it's a good time for me to ramble off about some of the inner workings of this, if for no other reason for the sake of logging it for when I revisit this: Naming Conventions I used to follow the general naming convention of hyp_fnc_[functionName] for naming just about all of my functions, however my list of functions grew, and I wanted to make the name more directly reflective of its purpose and behavior. I'm also a sucker for descriptive prefixes/suffixes, so I adopted something to the effect of: hyp_fnc_[category]_[functionName]. This worked pretty well for me, however it was a bit too much typing for my taste. I finally decided that I really didn't care if I had a hyp_ prefix for every function, and decided to settle on [category]_fnc_[functionName]. I realize this technically leaves me more vulnerable to accidentally conflicting with other libraries, however we'll cross that bridge if we ever reach it. functionName and category are both in camelCase, however I may add an additional suffix from time to time, separated from category by _, but then camelCase itself. But that's a special case with reasoning that should be obvious if it ever occurs. File Structure / Includes / Etc. File structure is one of my biggest points of indecisiveness, as I very frequently change my mind on how exactly I want to organize my files. That said, I'm fairly comfortable sticking with the form I have now, which is effectively as follow: -- Mission Root -- hyp_Libraries #CfgFunction.h //Includes Config for all component folders Arrays #CfgFunctions.h //Includes Config for all "Arrays" functions #Headers.sqf //All macros or other forms of scripts to be included in every file in THIS particular folder fn_push.sqf //Begin files with each function's code ... ... Basically, we have a master #CfgFunctions.h file in the hyp_Libraries folder itself. This one #includes the #CfgFunctions.h of each category. Each of those category-specific #CfgFunctions.h hold the actual config entries for that folder/category. Each category-folder has a unique #Headers.sqf file that includes any Macros or code that should be repeated in every function. Macros #define handleNil(a) ( if true then {private "_v"; _v = a; if (!isNil("_v")) then {_v} else {nil} } ) This first macro came about when I was working on the array manipulation library. I wanted to make it work for ANY values stored in the array, and that includes nil. Nil is a tricky beast to work with, as assigning a variable to nil (ie _someVar = nil;) makes it indistinguishable from one that has never been declared, and causes an error to be thrown when you attempt to access that variable later on. Enter handleNil(). This is a simple macro that you pass a variable, or any value in general to. If the variable is valid, all is good and the macro simply returns its value. If passed a non-existent variable (a variable that equals nil), it will return nil (as created by the nil scripting command) instead of throwing errors. Simple, but necessary for any manipulations of data with unspecified datatypes. //param : "a" is an array, "b" is an index, "c" is a default value. If "a select b" is either out of bounds, or nil, c is returned. Otherwise "a select b" is returned #define param(a,b,c) ( if (count(a) > b && b >= 0) then { private "_v"; _v = a select b; if (!isNil("_v")) then {_v} else {c} } else {c} ) In working on these functions, I noticed that calling code in functions was a reasonable deal slower than directly executing the same code. As such, I wrote param(), a simple macro to get an index from an array. If the specified index is either out-of-bounds or nil, a default value is returned. It serves an awfully similar purpose to BIS_fnc_param, however trades the thoroughness of additional checks for a 90% increase in performance (ie macro is 10x faster than calling BIS_fnc_param); As a matter of fact, a few functions that accepted optional parameters were already smaller and faster than all of BIS_fnc_param itself, so using BIS_fnc_param decreased performance by over 100%. KeyValueArrays I swear I've written this same set of functions a dozen times now, but differently every time around. The intuitive design was always an array of 2 element arrays, each as a key and a value. It worked well enough, but searching for a key required a linear traversal of the array, which can reach O(n) in the worst case. I also tried a very simple linked list design, where each node looked something like this: [ ["class", "NODE"], ["key", "someKey"], ["value", 1234], ["next", <the next node>] ] The final design I've settled on is an array of keys, and an array of values, where each key corresponds to the value of the same index. Why this instead of the 2 element arrays? Simple, the engine command "find". I ran a few tests, and the extreme ends of my results were: When finding the 30th element in an array, linear traversal was 10x slower than the find command. When finding the 900th element in an array, linear traversal was 100x slower than the find command! So as you can see, find beat out linear traversal by quite a wide margin. Even a factor of 10x is massive when we're dealing with something that may be manipulated as frequently as an alternative array type. And finally, you may notice that the actual formatting of KeyValueArrays "objects" is as follows: [ ["class","KeyValueArray"], ["keys",[]], ["values",[]], ["tagged",[]] ] This is remnant of a combination of two main things; debug code to help me read the contents of the array more easily, and leftovers of my attempt at what I'm calling "Array Oriented Programming". It was basically using lots of arrays to sort of emulate OOP. It's on the backburner for now, but if it ever makes a resurgence I'd potentially maintain the same "Key-Value-Pairs" for each field, even though the keys are never actually accessed or read. Also, I mention the tagging system on the wiki page too, but it's my solution for deleting elements from the KeyValueArrays. Simply put, removing elements from an array by reference is not pretty, as you need to traverse the entire array every time you want to remove elements. That said, you can remove multiple elements in one pass with no additional impact on performance. That's where tagging comes in. Tag multiple elements if they should be removed, but don't *need* to be removed right away, and remove all of them in one well-performing swoop. So yeah, there's a quick delve into the inner workings of this library. I figure there's at least one or two guys out there who may find this interesting, and maybe a few who can offer suggestions or improvements!
  11. Hypnomatic

    Array Error

    @HeliJunkie: Looks like this switch statement itself is returning a value stored in a variable outside of the pasted chunk of code. @LawlessBaron: You're missing a few commas. The ones I've noticed are missing in both arrays: - After "RH_kimber" and before nil - After "RH_g19" and before nil - After "RH_vp70" and before nil
  12. Listen vs Dedicated There are two ways to host a server in ARMA: as a "listen" server, or as a "dedicated" server. A listen server is one you host from right within your client: your computer will be acting as the host for all players in game, while you would also be presumably playing in game. A dedicated server is one you launch, usually on a separate machine in a datacenter, with no player in the same client. This server doesn't need to share any processing power with the graphics, user interface, or other features the player would interact with, so they are what you'd use for bigger servers. isServer vs isDedicated isDedicated: Returns true when client it runs on is hosting the game, and is a dedicated server. In practice, this will mostly means that there is no player being controlled by this client, so you would use this if you have code that should only run on a dedicated server, not a listen server. isServer: Returns true when the client it runs on is hosting the game. This is true on both listen servers and dedicated servers, so anything that needs to be run only on one client (for example, a global command for setting up the mission, ie spawning in vehicles). You basically need to get an understanding on when and where your code needs to be run, be it on every client, just the server, or some combination of the two. To list a few examples: Does it run on every client that has is connected to the server, but not the server? Then contain it within a: if (!isDedicated) then {...}; //Note the ! Does it run on only the server?: if (isServer) then {...}; etc. Global vs. Local There's another use of these terms for describing multiplayer locality, however that's a headache of its own for another time. Instead, I think Killzone Kid does a pretty good job of explaining this topic, so I'm going to simply point you to his tutorials: http://killzonekid.com/arma-scripting-tutorials-variables-part-1/ I'd say read that and the following articles, and you hopefully will get a good idea of how the different types of variables work. exitWith Finally, an if (...) exitWith {...}; statement will execute the code within the {}, then break out of the current scope. So as an example: //Your init.sqf //Anything that will happen on all clients, including the server, would go here if (isServer) exitWith {}; //If this computer is the server, stop executing this code. //Now anything after this will only be executed on computers that are not the server. If anyone sees anything wrong, misleading, or omitted, please tell me and I'll correct it. Rather tired while writing this so some of my points may be a bit off.
  13. typeName does exactly what you're looking for: http://community.bistudio.com/wiki/typeName Just as an example: _someVariable = []; if (typeName _someVariable == "ARRAY") then { //It's an array! } else { //It's not an array. };
  14. Very cool, I've been itching to see something to this effect get fully implemented! Just to give you a few places to look for possible further inspiration, NouberNou had been working on a something up this alley a bit ago, but I believe he's not actively worked on it for a while: http://forums.bistudio.com/showthread.php?134053-Carma-A-C-to-SQF-quot-native-quot-Interface And then for a bit of self-plugging, I threw together a very quick and crude concept for some manner of OOP-alike a bit ago in response to a thread: http://forums.bistudio.com/showthread.php?166558-256-bit-integers-and-doubles-Also-Create-a-reference-to-a-variable&p=2530812&viewfull=1#post2530812 It's not pretty, and I may have made a slipup in terminology or general programming concepts along the way, but it takes a bit of a different approach to things from your solution, so maybe it'll be interesting or possibly helpful. You seem to have all of your bases covered in that regard however, so I'm not too worried. Either way, looking forward to seeing what comes of this if you take it all the way!
  15. Very cool script Naught, thanks for posting it! I've been pretty swamped with real-life things for the past week and a half, so a lot of the functionality I have planned or wanted to implement is only partially done. Heck the mission in the Steam Workshop is a very shallow representation of what I have in mind, but I wanted to get something out the door. Despite this, I've just finished rewriting everything important to be faster, much cleaner, and hopefully more modular, and hopefully I'll publish it some point later tomorrow. I definitely will take a look at implementing some manner of your HSV calculations, as it does look very handy! And of course anything of yours I use will be duly credited.
  16. I'm pretty sure that that cursorTarget is being evaluated only when the addAction command runs. By that I mean that cursorTarget is basically running once: Whatever is in front of the player's cursor when the action is added is always going to be passed to the script. So if the player is looking at nothing when you add the action, you'll get an objNull in _this select 3 every time. If you add the action when a player is looking at something, _this select 3 will always return that unit, even once you're no longer looking at it. So I recommend assigning cursorTarget to a variable at the start of test.sqf, and you should be golden, ie: _targetPlayer = cursorTarget;
  17. @aeroson: That's actually really neat, and something I was considering looking into, but didn't know how to implement well. I've had a few new ideas for how I want to handle this so I'm rewriting it again late-ish tomorrow or Friday (Bit busy), but to give you an idea of the changes I'm thinking right now: - The laptop for changing options is a very hacked together solution that I'm in general not happy with. It basically iterates through every unit and redeclares the optional parameters to the new values. It works, but as this script has gone on I've reevaluated what needs to be stored on a per unit basis, and how it's stored. Right now every unit stores its own set of params, but these are all changed at the same time to the same values, so they are all redundant. The change I'm thinking is that each unit would simply have a "tracers_var_params" value that points to an array with the interval, max distance, and such as elements. That way I can have multiple units all point to the same array, and any manipulations to that array are automatically reflected in all of the units. It would even allow me to have separate categories of units with different parameters stored in different arrays if so desired. It's also just storing a lot less duplicate data, which is nice. - Right now, the format of data being stored is basically [[[x,y,z],velocity], ...]. I'm thinking of changing this to [[[x,y,z],[r,g,b,a]], ...], or in other words, calculate the color of a line when it's recorded, and storing it with the position instead of the velocity. There are 2 major benefits to this: First the script only needs to divide the current velocity by the muzzle velocity once instead of every frame, which should help performance a tiny bit. Second, it allows aeroson's interpolation idea to theoretically be implemented without performance hits. Instead of executing his code every frame for each position, it would be executed once per position, which shouldn't hurt performance nearly as much. - Making respawning and the initializing the script cleaner. When I first wrote this, I was assuming I would just use a boolean stored in the object that would exist if the unit was initialized this life, and not exist at all if not. The quirk I completely forgot is that variables in a object's namespace persist through respawn, so I couldn't use the boolean. I tossed in a simple workaround, however, that I just need to integrate better. It's basically: _unit setVariable ["tracer_var_object", _unit]; //Store a reference to the current unit/object //And then the following would return true only if the unit has been initialized this life: _unit == _unit getVariable ["tracer_var_object", objNull] And a ton of other little things. Including maybe changing the naming convention of variables. I'm a big fan of descriptive prefixes and suffixes, and I've slowly been leaning against using hyp_ at all for the sake of being concise. I'll toss a few things around and see what sticks. But as all of these changes and ideas have popped up, I'm looking to continually maintain and improve the code.
  18. Dslyecxi's completely right, and to help illustrate his point, I temporarily added a line of code to draw icons above every recorded position: http://i.imgur.com/EW3SCZc.jpg In this image, the bullet on the left was fired at normal gamespeed, and the bullet on the right was fired at 0.1 gamespeed. To reiterate, the positions of active bullets are updated every time the game renders a frame. So at 60 FPS, there will be 60 of these icons, or 60 joints in the line for every second of travel. Note just how far apart these lines can get due to the high initial velocity of the bullet. and how a ricochet early on may cause misleading lines. Slowing the time improved this significantly however. @Raven: Here's a zip of the mission currently released on the Steam Workshop. Note that this is the editor folder of it, so you would place it with those missions and open it in the editor. https://github.com/hypnomatic/ARMA_Examples/raw/master/Tracers%20Script/TracerExample_v0-0-1.Altis.zip Also, worth noting that the code in this, and the mission released on the steam workshop, is a bit modified from the currently posted code. It's not nearly as clean behind the scenes as I would like, so I'm going to rewrite a few things before updating the code in the OP. Also, the Steam Workshop mission has been updated with a few new features!
  19. @Dslyecxi: Awesome video again. I've implemented a variant of your change into the main release, toggle-able with the 7th parameter. Also good note about the slowing of time, I never really thought about that, but it makes sense seeing as each bullet's positions are recorded on every rendered frame. @da12thMonkey, Nanucq: Thanks for the support! @thelog, ProGamer: Hmmph, in general I'm writing this script to be open for anyone to use however they see fit, so long as credit is given. That said, that mission seems to be effectively a showcase for my script, which seems unnecessary now that I have a mission I can personally keep up to date. I'll ask him about taking it down now that I have mine up. @twisted: Do you have any particular interval set? There are occasional visual bugs with the lines if they ricochet early on, as the bullet is moving so fast that the recorded positions are still fairly far apart. I want to say that that is caused by my script and not an actual ARMA bug. And now for the Update news that I added to the OP: UPDATE 10/26/2013: As some of you may have seen, Dslyecxi recently made a video that featured a modified version of this script, where the color of the line was reflective of the velocity of the bullet at that moment. He posted his modifications, which are now implemented in the main script! To activate it, pass true as the seventh parameter, like so: [player, nil, nil, nil, nil, nil, true] call hyp_fnc_traceFire; Note that as it stands, enabling the velocity-based coloring will override whatever color you specify in the second parameter. I have an idea or two of ways to fix it, but wanted to get this update out there first. Also, I completely forgot that the Steam Workshop was a thing, so I published a very simple mission with this script, and Tonic's Virtual Ammobox System and Virtual Vehicle Spawner enabled here: http://steamcommunity.com/sharedfiles/filedetails/?id=189421090 If there are any problems with the Steam Workshop mission, please mention them to me, as I very rarely actually publish missions, just scripts.
  20. The following should get the value you're looking for: _maxAmmo = getNumber(configFile >> "CfgMagazines" >> _magazineClassName >> "count") ; And as a usage example, the following will generate an array of 2 element arrays formatted as [magazineClassName, maxAmmoCount] for every magazine in the player's vehicle (or inventory if they're on foot). _output = []; { _output set [count _output, [_x, getNumber(configFile >> "CfgMagazines" >> _x >> "count") ]]; } forEach (magazines (vehicle player)); hint str _output; Now are far at parsing the string goes, I actually wrote a script to do exactly this before magazinesAmmo was imlpemented, as it was the only way I could find to get the ammo of magazines not currently loaded. It's not pretty, and it's more or less completely outdated thanks to magazinesAmmo/magazinesAmmoFull and the config check, but it may be interesting: private["_output","_lParen","_rParen","_fwdSlash","_magazinesDetail"]; _output = []; _lParen = toArray("(") select 0; _rParen = toArray(")") select 0; _fwdSlash = toArray("/") select 0; _magazinesDetail = (magazinesDetail (vehicle player)); { private["_currentAmmo","_maxAmmo","_inCurrentAmmo","_inMaxAmmo"]; //Arrays to store the characters of the values we're getting _currentAmmo = []; _maxAmmo = []; //Bools to keep track of which chunk of the string we're in _inCurrentAmmo = false; //True once Left Paren is hit, and false again if it hits an unexpected character _inMaxAmmo = false; //True once the forward slash is hit, and _inCurrentAmmo is true { /* Quick explanation: These if statements are expected to evaluate true from the bottom up. Ie: _x will be a useless character until it's a left paren. Then _x will be a digit in the current ammo until it's a forward slash. And until _x is a right paren, it is a digit in the max ammo count. Once _x hits a right paren after that, quit parsing because everything desired has been found. Note the extra check in if(_inCurrentAmmo) exitWith {...}; This check is needed as some magazine names have Parenthesis too. */ if (_x == _rParen && _inMaxAmmo && _inCurrentAmmo) exitWith {}; //Hit the closing paren of the ammo counts if true then { //Create a new scope so exitWith doesn't break the loop, effectively other language's "continue" if (_inMaxAmmo) exitWith { _maxAmmo set [count _maxAmmo, _x]; }; if (_x == _fwdSlash && _inCurrentAmmo) exitWith { _inMaxAmmo = true; }; if (_inCurrentAmmo) exitWith { if (_x in toArray("01234567890-.")) then { _currentAmmo set [count _currentAmmo, _x]; } else { _currentAmmo = []; _inCurrentAmmo = false; }; }; if (_x == _lParen) exitWith { _inCurrentAmmo = true; }; }; } forEach toArray(_x); //Convert the two arrays of characters to their corresponding values _currentAmmo = parseNumber(toString(_currentAmmo)); _maxAmmo = parseNumber(toString(_maxAmmo)); //Push them to outputas a 2 element array, where element 0 = current ammo, and element 1 = max ammo _output set [count _output, [_currentAmmo, _maxAmmo]]; } forEach _magazinesDetail; //At this point output contains all values for magazine ammo hint str _output;
  21. Hypnomatic

    Package @Addons with mission.

    Nope, addons aren't supported by Steam Workplace at all yet, and even once they are it won't allow you to include them in missions. Extensions can be extremely powerful, or even malicious, so allowing them to be downloaded and executed on someone's computer automatically isn't the safest practice. Now, you CAN launch the server with Addons, but only the server will have access to them. So if you were to make an addon folder on the server, and include KK's regex extension, you would be able to call it just fine on the server. Takes a bit more work to ensure it's only ever needed by the server, but it's doable.
  22. Round should do what you're looking for. _val= round 5.25; //result is 5
  23. Just to add on, Controls are their own datatype, also known as CONTROL. The "Control #___" string is what's generated when you try to represent a control as a string. As Larrow pointed out, ctrlIDC returns the control's id number as a scalar, which you can easily compare with. Fun other fact: Even though it's bad form, "Control #200" == str (_this select 0) Should return true.
  24. If I'm interpreting your question correctly, you want the action to be available when the player is standing on forest terrain and not otherwise. That can be achieved with: player addAction [format["test"], "actions\Wood.sqf",[_unit], 1, false, true, '(vehicle _this == _this) && (surfaceType getPosATL _this == "gdtStratisforestpine")', ""]; Can't test it ATM, but that should accomplish what you're looking for. For a bit more info on how/why I suggest this, this is using the 8th parameter of addAction (referred to as "condition" on wiki): http://community.bistudio.com/wiki/addAction
  25. Don't have a lot of first-hand testing/experience on the matter, but this ticket did raise a concern that you may need to take into account: http://feedback.arma3.com/view.php?id=15391 Basically, it claims that arrays have a fairly large overhead when broadcasted via publicVariable. Haven't had time to verify/test for myself, so I don't know how the overhead scales with bigger arrays and such, but it's worth keeping in mind. Bit pressed for time at the moment, so I can't offer a super-proper response now, but I'll look again later.
×