

Drakoz
Members-
Posts
272 -
Joined
-
Last visited
Content Type
Profiles
Forums
Events
Everything posted by Drakoz
-
[EDIT] Never mind - it worked for me - see my next post. My original comment... I think the issue has to do with the Dim()? You declare myJson globally, but perhaps there is some issue with REXEC(...fnsReadStatusJson()...) creating the buffer (and failing). But by running it before the REXEC once, perhaps it creates the buffer and then it is OK? I don't know why, but doing that kind of thing is playing with memory and could cause all kinds of issues. I'm still looking at it, but wanted to post this comment because it looks like you are looking at it right now too.
-
Ya, even I was confused about REXEC() the other day because I forgot about the need to use Actkey. The Speak() function should work in Win XP through Win 10, but may fail depending on your Internet Security options (like if you locked things down) or if you don't have Internet Explorer installed at all. Not really sure about this as I've only talked to 2 people who had trouble. It is using a Windows HTML Application function call (using "spawn mshta.exe...") to the Windows Speech API. No need to install any special software. I posted about this function here on the DCS forums as well as on SimHQ.com, but it was in answer to some other questions. There isn't a topic dedicated to it, though. It works great in general. I like it mainly because the external helper app I'm using is default Windows. But what you are doing is probably exactly the same, just using a different helper app.
-
How are you running REXEC()? It must either be called from within a MapKey(), KeyAxis(), etc., or you have to run it using ActKey(KEYON+REXEC(....)); if you want to just run it stand alone. For example, if you just want to run the REXEC() from main() and just have it run continuously from the moment you first run the script, you need to use ActKey(). REXEC() is exactly like EXEC() regarding how and where you can use it. So any example you see using EXEC(), it applies the same to REXEC(). I was just discussing several examples of using REXEC() to create background timers. My examples include using REXEC() to run both functions like you are trying, as well as multi-line code written into the REXEC() itself, which is another common gotcha with EXEC() and REXEC(). My examples show off most of the variations of using REXEC(). If you are curious, check it out here: https://simhq.com/forum/ubbthreads.php/topics/4508325/start-a-timer-on-a-key-press
-
Good question. I tried several variations of using strstr() and was also not able to get it to work. Strings in TARGET are not totally correct compared to C programming. There are a few hoops you have to jump through to use strings in TARGET. Here's two specific examples I tried among others... In this example, I take the simplest method. A quoted text string tends to always work in TARGET since they are constants (no aliases, or having to deal with worrying about string length). printf("strstr test 1: %d\xa", strstr("This is my string", "is") ); The above returns: strstr test 1: 0 C programming expects to see NULL characters (an 8 bit 0) at the end of a string in order to terminate it. TARGET does not add these NULL's automatically in most cases. So below, I added NULLs to my strings using sprintf and \x0 (because x0 is a NULL). Also, using Dim() to create 265 byte buffers means now I am using variables, not constants. It shouldn't matter, but this way I can make sure there is a NULL at the end of the strings. But same result, strstr() didn't work. char str1; Dim(&str1, 256); char str2; Dim(&str2, 256); sprintf(&str1, "This is my string\x0"); sprintf(&str2, "is\x0"); printf("_%s_ _%s_\xa", &str1, &str2); printf("strstr: %d\xa", strstr(&str1, &str2)); The above returns: _This is my string_ _is_ strstr: 0 I tried a few other ideas, but ya, it appears strstr() does not work. In C Programming, strstr() returns a pointer to the location within the string (str1 above) that is the first match. But since TARGET does not directly support pointers (it only supports the concept of an alias which is the address to a variable and is referenced as &str1), they couldn't do it that way. But on the off chance they did and just defined it incorrectly, then strstr() would return a memory address as its integer. It isn't doing that. So ya, I wonder what am I missing as well, but it looks like strstr() doesn't work.
-
Printing Text in TARGET - How to use printf() and sprintf()
Drakoz replied to Drakoz's topic in Thrustmaster
Using a printf, if the terminal you print to supports ASCII codes to change the color, you would use the \x codes to send the series of ASCII codes to do what you want. But I have seen no sign that the TARGET console has that ability. -
You have to put the Configure() statements before the if(Init(&EventHandle)) return 1; line. BTW, Configure() is actually intended to allow you to have a Thrustmaster device attached to your computer and Configure() will prevent that device from being a part of the Thrustmaster Combined setup created by TARGET. Hence it will remain as a normal DirectX controller. But many of us use Configure to get rid of the device not found messages in the TARGET console. Now that you have some time with basic scripts, you might want to review the TARGET Script manual again. There is a lot of info there, but often you have to read it several times to understand it. Even now, I often look something up in the manual and I am reminded of something I forgot, or didn't quite understand previously.
-
Sounds good. Glad you got it figured out. Like I said, you'll be an expert by the end of the year. Ya, I always type all the keyboard commands in for new DCS aircraft. But that is also because I tend to add stuff as I decide how I want to map things, so the time consuming part isn't typing all the commands. It is deciding how I want to map stuff to the controller. But I have a core set of commands that I use for all aircraft of course, which saves a lot of typing. I end up only having to add 20 to 40 new defines for new aircraft.
-
To exclude a device, use the Configure() command. Do one for each TM device you don't have. Here is the entire list. Just comment out the devices you want to use. Configure(&Joystick, MODE_EXCLUDED); // Exclude Warthog Joystick //Configure(&Throttle, MODE_EXCLUDED); // Exclude Warthog Throttle Configure(&HCougar, MODE_EXCLUDED); // Exclude Cougar Stick and Throttle Configure(&T16000, MODE_EXCLUDED); // Exclude T16000M Joystick (Right Hand) Configure(&T16000L, MODE_EXCLUDED); // Exclude T16000M Joystick (Left Hand) Configure(&LMFD, MODE_EXCLUDED); // Exclude Left MFD Configure(&RMFD, MODE_EXCLUDED); // Exclude Right MFD //Configure(&TWCSThrottle, MODE_EXCLUDED); // Exclude TWCS Throttle Configure(&TFRPRudder, MODE_EXCLUDED); // Exclude TFRP Rudder Configure(&TFRPHARudder, MODE_EXCLUDED); // Exclude TFRPHA Rudder Configure(&JoystickF18, MODE_EXCLUDED); // Exclude F18 Stick Handle For a text editor, ya don't use the TARGET Script editor. Notepad++ is a much better choice. Set Notepad++ to think of .tmc, .ttm, and .tmh files as C files and it will color the text correctly. I recently started using Visual Studio Code (Microsoft's code editor, not their developer environment which is just Visual Studio). VS Code gives better control over multiple files, side by side views, etc. But it is a programmer's editor, so I wouldn't call it user friendly. But the key point is the TARGET Script editor doesn't compile the file in the editor window. It compiles the file off the hard disk. So if you edit the file in an external editor like Notepad++ and save it, TARGET will compile your newly saved file. Just ignore the files that are opened in TARGET. A warning, though. I think, if you make an edit to the file in the TARGET Script editor, it will compile the file in TARGET, or at least give you a moment of confusion as it asks if you want to save the file or something like that. So make sure you don't accidentally make an edit there. You can also compile and run the .tmc file using the TARGETGUI.exe if you use the -r command line option. It is the same compiler used by the TARGET GUI program, but the -r option bypasses the GUI and just brings the compiler and the console window which prints out the messages from TARGET, or your script.
-
AXMAP3 (or rather AXMAP1 and AXMAP2 at the same time)
Drakoz replied to Drakoz's topic in Thrustmaster
Yes. Thankfully DCS supports almost everything we might want to map an axis to, and what isn't axis mappable is easily handled by an AXMAP1 or AXMAP2. In TSW, though, you sometimes have to press and hold a key for half a second to get over a notch on the lever and then press the same key with short taps (like 100ms each) to adjust the lever forward or back precisely. AXMAP1 or AXMAP2 won't do that. This is where KeyAxisDirectional() comes in. I can't think of an example where we need to do this in DCS. But I've seen a need for it in a few other flight sims over the years. Sadly no. pro_SD40GP38_ThrZoneList is just an array of integers, not an actual function like LIST(). Here is the definition for pro_SD40GP38_ThrZoneList: int pro_SD40GP38_ThrZoneList[] = {0,5,111,222,333,444,556,667,778,889,995,1000}; I'm using shifted integers (e.g. 5 = 0.5%, 111 = 11.1%). &pro_SD40GP38_ThrZoneList (note the &) just allows me to pass the array to KeyAxisDirectional(). Making a version of LIST() to define points down to 0.1% would also require modifying a significant amount of target.tmh. I'm sure Thrustmaster avoided floating point for performance reasons, but on a modern computer, this isn't likely an issue. TARGET runs very fast, and could probably handle floating point without difficulty today. Regardless, the reason I used 111 = 11.1% is to avoid floating point. I thought about making a version of LIST() for my own function, but using floating point or shifted integers instead. But how LIST (which is actually AXMAP2), CHAIN, and SEQ work still eludes me. These 3 functions use Map(..., MakeProcInstance(), MAP_IPTR_VPN) to create a reference to the primary function (e.g. axmap2proc, chainproc, and seqproc) and store that in the keyalloc[] array including the parameters (e.g. the numbers for LIST). But since MakeProcInstance() and Map() are built in functions, it is harder to understand exactly what they are doing. I mean I could duplicate what they did and get a working result, but I don't like doing stuff like that until I understand what is really going on. It is too easy to clobber a patch of memory and crash TARGET stepping out of the box like that. So I made KeyAxisDirectional() to just accept an alias to an array of int for now. -
So that I don't hijack this thread, I created a new thread to explain my "AXMAP3" function. See it here:
-
If you know what I mean by wanting the capabilities of AXMAP1 and AXMAP2 in a single function - an "AXMAP3", then read on. This goes back to something I wanted to do in FOXY a long time ago, but couldn't. I wanted to do this in TARGET as well, but it took me a while to figure a few things out because I wanted it to work in KeyAxis() like AXMAP1 and AXMAP2. I was posting this in response to another topic, but it was quickly hijacking that thread, so I decided to just post it in a new topic. I've been developing a script for Train Sim World (TSW) that is a glorified axis to keyboard mapper that actually works. TSW doesn't support game controllers and running a train with a keyboard is ridiculous. So regardless of TSW, this would be useful for mapping an axis to control anything that uses keyboard keys only, but where AXMAP1 or AXMAP2 doesn't cut it. I wanted to use my Warthog to run TSW. But trying to use KeyAxis() with AXMAP1 or AXMAP2 doesn't work very well either. You need an "AXMAP3", which effectively merges the capabilities of AXMAP1 and AXMAP2 in a single function. You can see the full script on the Dovetail Train Sim World forums linked below including links to videos showing it working. But, I'll explain some of the details here for the sake of discussion if anyone cares. https://forums.dovetailgames.com/threads/thurstmaster-target-script-for-warthog-throttle-saitek-tq-profile.3634/ In summary.... Yes this really is the summary - I am posting a little bit to see if anyone cares about stuff like this. If yes, I can post more explanation, or post it as a working library for others to use. If it doesn't make sense, don't worry, I don't expect anyone that isn't a programmer or a TARGET expert to understand this. But I am happy to discuss and teach it for anyone that wants to learn about this stuff. This is what I did - my "AXMAP3" is really AXMAPEXEC(KeyAxisDirectional()), where AXMAPEXEC is a special version of EXEC that works in KeyAxis() without having to be enclosed in AXMAP1 or AXMAP2. Yes, if you haven't tried, you can't do the following: KeyAxis(&Throttle, THR_RIGHT, 'o', EXEC("Myfunction();") ); So I created AXMAPEXEC() to solve that. You can use it as follows: KeyAxis(&Throttle, THR_RIGHT, 'o', AXMAPEXEC("Myfunction();") ); AXMAPEXEC() calls KeyAxisDirectional(), which is performing the equivalent function of an AXMAP1 and AXMAP2 at the same time - presses one key in the up direction, another in the down direction, but I can define for every zone in the LIST() (as defined by the &...ZoneList variables in my example below) a different key press as well as a different key press time regardless of the axis moving up or down. Because of how TARGET works, it works amazingly well. No missed key presses which is typical with all the other axis to keyboard mapping functions I've seen for other joystick programming software. Here is an example of the code from my TSW script. It uses the Warthog Right axis to control the locomotive throttle, and the left axis has a UMD state to control one of three different brake levers on the locomotive, and all this works within the framework of a standard KeyAxis() command. // Warthog THR_RIGHT axis config - engine throttle KeyAxis(&Throttle, THR_RIGHT, 'o', AXMAPEXEC("KeyAxisDirectional(&Throttle, THR_RIGHT, &pro_SD40GP38_ThrZoneList, &pro_SD40GP38_ThrActionUP, &pro_SD40GP38_ThrActionDN);")); // Warthog THR_LEFT axis config using UMD Switch - U=dynamic brake, M=automatic brake, D=indepedent brake KeyAxis(&Throttle, THR_LEFT, 'u', AXMAPEXEC("KeyAxisDirectional(&Throttle, THR_LEFT, &pro_SD40GP38_DynZoneList, &pro_SD40GP38_DynActionUP, &pro_SD40GP38_DynActionDN);")); KeyAxis(&Throttle, THR_LEFT, 'm', AXMAPEXEC("KeyAxisDirectional(&Throttle, THR_LEFT, &pro_SD40GP38_AutoZoneList, &pro_SD40GP38_AutoActionUP, &pro_SD40GP38_AutoActionDN);")); KeyAxis(&Throttle, THR_LEFT, 'd', AXMAPEXEC("KeyAxisDirectional(&Throttle, THR_LEFT, &pro_SD40GP38_IndZoneList, &pro_SD40GP38_IndActionUP, &pro_SD40GP38_IndActionDN);")); An example of the parameters passed to KeyAxisDirectional(): &pro_SD40GP38_ThrZoneList references a list of zones by percentage of axis travel kind of like what we do with LIST() normally. Unlike LIST(), the resolution by percentage goes down to 0.1%, which is critical for an axis to keyboard mapper like this. &pro_SD40GP38_ThrActionUP references a table of keypress actions and press times when the axis moves up (increasing value). &pro_SD40GP38_ThrActionDN references a table of keypress actions and press times when the axis moves down (decreasing value). I'll stop there because it is better if you want to understand this, go look at my TSW script, and then ask questions here. Making the script forced me to understand TARGET to a very deep level. The above example may seem simple, but it took a lot of understanding about target.tmh to get there. Here is the function for AXMAPEXEC(). It is very similar to EXEC(), but has a critical difference that allows it to work outside of AXMAP1() or AXMAP2() when inside a KeyAxis(). AXMAPEXEC(): //****************************************************************** // AXMAPEXEC(), axmapexecproc() // Version of EXEC() for use in KeyAxis() by itself. This is because EXEC() only works inside an AXMAP1 or AXMAP2, // but not as the only command in KeyAxis(). So had to create a special version of EXEC() to solve this. // AXMAPEXEC() works the same as EXEC() as far as syntax, but should only be used in KeyAxis() commands. // Examples: // KeyAxis(&Throttle, THR_LEFT, 'm', AXMAPEXEC("ActKey(...);")); Execute ActKey() for M layer // KeyAxis(&Throttle, THR_LEFT, 0, AXMAPEXEC("YourFunc(...);")); Execute YourFunc() for all layers IO and UMD int AXMAPEXEC(alias cmd) { tmp[0]=&cmd; return ASMAlloc(1, &&tmp, &axmapexecproc); } int axmapexecproc(alias v, int p, int x=0) { if (x < -AMAX) return 0; p = v[(p+2 & 0xffff)]; if(p) execute(p); } If you just want to use AXMAPEXEC() in your script, paste the code above into your .tmc or .ttm file and it is good to go. As in my KeyAxisDirectional() example above, you will want to pass in the device and axis names to your function. So you might do something like this: KeyAxis(&Throttle, THR_LEFT, 'u', AXMAPEXEC("MyFunction(&Throttle, THR_LEFT);")); int MyFunction(alias dev, int axis) { if (dev[axis] > 20000) { // do something here } else if (dev[axis] <= 20000) { // do something else } } This is a ridiculous example because you can do the above using AXMAP1(), but my point is, with the above, you are able to do anything you can imagine, not just what AXMAP1 or AXMAP2 allows you to do. And in my case, my imagination led me to KeyAxisDirectional(). To see how KeyAxisDirectional() works, it is best to go check out my posted script at the Train Sim World forums. There are a bunch of massive tables in the .ttm file which makes it work. If I tried to post it here, so much would be taken out of context, it would be confusing. But if there is interest, I can explain what I did better, and/or post full working examples here in the form of a library. BTW, to try it out, you don't need Train Sim World. You can see everything my script does using the TARGET EventTester since all it is doing is press keys. Also, the AXMAPEXEC() function is new to my script in v3.0. The TSW forum topic only have v2.x of my script which does not use AXMAPEXEC(). I will post the v3.0 script later tonight. Before using AXMAPEXEC() I had a huge if else statement which was getting ridiculous. It is much easier to remap an axis using KeyAxis(), so I created AXMAPEXEC() as this follows the nomenclature Thrustmaster intended with TARGET.
-
Did you solve this one? Sometimes an error occurs several lines below the actual cause of the error. When you say you commented out line 10 and the error just moved to line 11, then line 28 - that is a perfect example where the error is several lines before the spot TARGET calls as the error line. I would need to see DCS_A-10C_Macros.ttm to maybe confirm this. If you aren't aware, the include statement takes the included file and literally places the contents of that file in your main file at the location of the include statement, and then passes the single "file" to the compiler. So it is possible that an error at the beginning of one .ttm file could be the result of a parsing issue from the end of the previous file. But on this one, I agree with lxsapper. It is probably because you have duplicate defined names because of how you used DCS TARGET Macro File Generator. But the error is a little cryptic on that. I haven't seen that error before.
-
Agreed. And how cool it is that TM gave us the .tmh files so we can play with this stuff. Not sure if they intended it that way, but I am profusely glad they did. I am even creating my own versions of functions like EXEC() and AXMAP1() to do special things (e.g. call it AXMAP3). I would never have figured any of that out without the .tmh files.
-
Lange_666, I've seen a few posts you made where you are modifying the stock .tmh files (target.tmh, hid.tmh, defines.tmh, and/or sys.tmh) to solve a problem with your script (not add a feature, but solve a problem). I fear you may be making things difficult on yourself. There is absolutely no reason to touch these files unless you are trying to add a feature that TARGET does not support (e.g. the 128 DX button mod). Even then, because you are new to TARGET, I would suggest sticking with unmodified .tmh files until you really need to add such features. Modifying the .tmh files to solve a problem will only make things worse. TARGET is very powerful, which means the more complicated your script, the more likely you will create errors or issues. You have run into quite a few "advanced" issues in the last couple weeks, most of which are directly related to making things more complicated than needed. I certainly don't want to discourage you from trying complex scripts. At the rate you are going, by the end of the year, you will be a TARGET guru. But for now, simplicity might be your friend.
-
There are actually 4 files you need: target.tmh, hid.tmh, defines.tmh, and sys.tmh. target.tmh is referenced by the include in your program, but the other 3 are referenced using include statements in target.tmh and hid.tmh. Anyway, you have to make sure all 4 files are available. As already stated, by default, they are here: C:\Program Files (x86)\Thrustmaster\TARGET\scripts\ But, you can also put these files in your personal TARGET scripts folder. By default your personal scripts are stored here (where %USERNAME% represents your Windows user name): C:\Users\%USERNAME%\AppData\Roaming\Thrustmaster\TARGET\Scripts If you put target.tmh (and the other .tmh files) there, it will use those files instead of the version found in the Program Files folder. So if you do modify or use modified .tmh files, you should put them in your personal scripts folder and not touch the originals. This makes it easier to edit the files as well because they aren't behind a protected folder. You can set these file locations in the TARGET Script editor under Options -> File Paths. By default, when you install TARGET, it includes the directories mentioned above. The order of precedence (using the .tmh files in your personal folder instead of those in Program Files) is determined by the order of paths in the File Paths options. So since you are using a custom installation, I thought might care. But.... Since you are just starting out with TARGET, I would suggest don't use the modified target.tmh people are referencing above. Well, not unless you actually need more than 32 DX buttons. Learn TARGET in its unmodified state first, and only go to modifying target.tmh when you understand why you care. It is a great mod. Just saying, don't go there until you need it.
-
How to print text to the TARGET console (printf), or into a string buffer (sprintf). This is in response to a question someone asked me, so I thought I would post here as it is generally useful to know. For more details than explained here, internet search on printf() and sprintf() for C Programming. Below I explain these functions specifically related to TARGET. The basic concept is to print a quoted string and include % format specifiers in the string to represent variables you want to print. For example below, we print an integer, vout, using %d. Note how the main text is written out between " ", the %d is included inside the quoted text, and the variable(s) to print are listed after the quoted text as a comma separated list. int vout = 25; printf(“Text to be printed: %d\xa”, vout); Prints the following to the TARGET Console window: Text to be printed: 25 In normal C, you would use the escape code \n for a newline or \t for a tab over, but in TARGET, we have to insert the ASCII code for a newline, which is 0x0A (hexadecimal, or 10 decimal). Hence we use \xa on the end of the quoted text to do a new line. To tab over we have to use \x9 for the ASCII 9 or TAB character. Go here (https://www.asciitable.com/) to see the full ASCII table. The only ASCII codes I have used in TARGET are \xa and \x9. Here are all the % format specifiers I have tested in TARGET. Others may be work, but not all the C printf() % format specifiers are supported in TARGET. %d – decimal integer %u – unsigned decimal integer %x – hexadecimal %f – floating point (e.g. %0.1f prints xx.x) %c – char %s – string Does not work %b - binary sprintf() works the same except that instead of printing to the TARGET console, sprintf() "prints" into a string referenced by an alias. So here is an example of sprintf(): int vout = 25; char mystring; Dim(&mystring, 256); sprintf(&mystring, "Text to be printed: %d\xa”, vout); Sets &mystring to reference the following text: "Text to be printed: 25" Here are several examples: include "target.tmh" int main() { if(Init(&EventHandle)) return 1; int var1 = 1; int var2 = 2; int var3 = 3; float float1 = 1.234; float float2 = 0.1234; alias string1="This is my string."; int hexordec = 0x1F; // 1F hex = 16 decimal printf("Print multiple varaibles: %d, %d, %d\xa", var1, var2, var3); // prints: // Print multiple varaibles: 1, 2, 3 printf("This is my float, %0.2f, and this is my integer, %d.\xa", float1, var1); // prints: // This is my float, 1.23, and ths is my integer, 1. printf("This is a percentage: %0.1f%%\xa", float2 * 100); // prints: // This is a percentage: 12.3% // Note in order to print a %, you must excape the % by using %%. printf("Did you know 1 + 1 = %d? ", 1 + 1); printf("But 1 / 1 = 1. "); printf("More importantly, did you notice these 3 printf statements printed on a single line?\xa"); // prints: // Did you know 1 + 1 = 2? But 1 / 1 = 1. More importantly, did you notice these 3 printf statements printed on a single line? printf("How do I print a string? Do this: %s\xa", &string1); // prints: // How do I print a string? Do this: This is my string. printf("Hex or Decimal? %x hex = %d deicmal\xa", hexordec, hexordec); // prints: // Hex or Decimal? 1f hex = 16 decimal // And an sprintf example char buffer; Dim(&buffer, 256); // create a 256 byte buffer for sprintf. sprintf(&buffer, "A decimal is %d. A float is %0.2f. And I can add a string like this: %s", var1, float1, &string1); printf("%s\xa", &buffer); // This is a way you can build a string variable (&buffer) with text and results from variables. // Then I print it out using a simple printf() and we get this: // A decimal is 1. A float is 1.23. And I can add a string like this: This is my string. } int EventHandle(int type, alias o, int x) { DefaultMapping(&o, x); }
-
Yes, that is what I thought. Hence why I posted two examples, each of which deal with the paths differently. Figuring out how to manipulate strings and alias's is kind of confusing in TARGET because TARGET doesn't handle strings quite the same as normal C.
-
And if you want a way to do Text to Speech in TARGET, the following uses Windows mshta.exe to execute some vbscript to speak the given text. Paste in TARGET and run it to try it out. Don't forget when using this in an EXEC(), you must use \" (a backslash before the quote) in order to escape the quotes and properly pass them to Speak(). See usage examples below. include "target.tmh" // Disable Speak() output - disabled =0, enabled =1 define SpeakEnabled 1 int main() { if(Init(&EventHandle)) return 1; // declare the event handler, return on error Speak("Script Loaded"); Speak("Press LDGH to say more"); MapKey(&Throttle, LDGH, EXEC("Speak(\"Hello World\");")); } //****************************************************************** // Speak() // Text to Speech - this function uses Windows mshta.exe and a simple Visual Basic script to perform text to speech // text - the text or phrase to speak // The concept of using mshta vbscript was found here: https://stackoverflow.com/questions/1040655/ms-speech-from-command-line // No extra programs are required for this to work. int Speak(alias text) { if (!SpeakEnabled) return 0; // disable TextToSpeech sounds if (&text == "") return 0; // exit if no parameter given //printf("Speaking: %s\xa", &text); char buffer; Dim(&buffer, 256); sprintf(&buffer, "spawn mshta vbscript:Execute(\"CreateObject(\"\"SAPI.SpVoice\"\").Speak(\"\"%s\"\")(window.close)\")", &text); system(&buffer); } //event handler int EventHandle(int type, alias o, int x) { DefaultMapping(&o, x); }
-
I was messing around more with playing .WAV files and thought the following would be useful. A function call to make it easier to play .WAV files. The following examples are complete .tmc files which you should be able to copy and paste into the TARGET editor. There should be no copy and paste issues related to the forums cutting out characters as mentioned in previous posts. There are two versions. Both are intended that you store all your .WAV files in a single folder (e.g. a Sounds folder in your Scripts folder). The first example expects that sWavPlayer.exe is in the same folder as the .WAV files. The second example allows for sWavPlayer.exe to be in a different folder than the .WAV files (e.g. c:\bin\sWavPlayer.exe). It was mentioned before this 2nd example didn't work because sWavPlayer.exe expects the .WAV file to be in the same folder. But sWavPlayer.exe works with files in a different folder if you give it a full path to the .WAV file. Below shows how to do it in TARGET. The main difference is, the first one uses "spawn -w" to set the working folder for sWavPlayer.exe, but then sWavPlayer expects the .WAV files to be in the same folder. The second example just uses "spawn" with no -w option and uses full path names to both sWavPlayer.exe and the folder where you store your .WAV files. You could do the file paths many ways. I provided two examples to help everyone understand how to build a system("spawn"); command using variables and defines to configure full paths. The trick is to use sprintf() to concatenate the strings together in to buffer. Don't forget to set SOUNDS_Folder and sWavPlayer_Folder to match the path to your .WAV files or where you store sWavPlayer, as well as change the name of the .WAV file used (I am playing a file called failure_beep.wav. First Example: sWavPlayer.exe and .WAV files in same folder include "target.tmh" int main() { if(Init(&EventHandle)) return 1; // declare the event handler, return on error MapKey(&Throttle, LDGH, EXEC("PlayWav(\"failure_beep.wav\");")); } //****************************************************************** // PlayWav() // Function to play a .WAV file using sWavPlayer.exe // sWavPlayer.exe can be downloaded from here: http://www.dcmembers.com/skwire/download/swavplayer/ // // Set SOUNDS_Folder = the folder where you store sounds. Make sure sWavPlayer.exe is also stored in this folder. // Make sure to use \\ for all occurrences of \ in the path. define SOUNDS_Folder "C:\\Users\\mike\\AppData\\Roaming\\Thrustmaster\\TARGET\\Scripts\\Sounds\\" int PlayWav(alias wavfile) { if (&wavfile == "") return 0; // exit if no parameter given printf("PlayWav playing file: %s\xa", &wavfile); alias f = SOUNDS_Folder; char buffer; Dim(&buffer, 256); // max command length is 256 characters sprintf(&buffer, "spawn -w %s %ssWavPlayer.exe %s", &f, &f, &wavfile); printf("buffer = %s\xa", &buffer); system(&buffer); } //event handler int EventHandle(int type, alias o, int x) { DefaultMapping(&o, x); } Second Example: Separate paths for sWavPlayer.exe and the .WAV files. include "target.tmh" int main() { if(Init(&EventHandle)) return 1; // declare the event handler, return on error MapKey(&Throttle, LDGH, EXEC("PlayWav(\"failure_beep.wav\");")); } //****************************************************************** // PlayWav() // Function to play a .WAV file using sWavPlayer.exe // sWavPlayer.exe can be downloaded from here: http://www.dcmembers.com/skwire/download/swavplayer/ // // Set SOUNDS_Folder = the folder where you store sounds. Make sure sWavPlayer.exe is also stored in this folder. // Make sure to use \\ for all occurrences of \ in the path. define SOUNDS_Folder "C:\\Users\\mike\\AppData\\Roaming\\Thrustmaster\\TARGET\\Scripts\\Sounds\\" // Set sWavPlayer_Folder = the folder that contains sWavPlayer.exe define sWavPlayer_Folder "c:\\bin\\" int PlayWav(alias wavfile) { if (&wavfile == "") return 0; // exit if no parameter given printf("PlayWav playing file: %s\xa", &wavfile); alias f1 = SOUNDS_Folder; alias f2 = sWavPlayer_Folder; char buffer; Dim(&buffer, 256); // max command length is 256 characters sprintf(&buffer, "spawn %ssWavPlayer.exe %s%s", &f2, &f1, &wavfile); printf("buffer = %s\xa", &buffer); system(&buffer); } //event handler int EventHandle(int type, alias o, int x) { DefaultMapping(&o, x); }
-
Where is that thread? I haven't revisited the issue, but sounds (ha) like you have a solution.
-
Remember, a MapKey() statement runs only once and sets its values when it runs. So in your example, you had the following (I simplified it down to a single MapKey, not MapKeyIOUMD): MapKey(&Throttle, CHF, CHAIN(EXEC("count = count + 50;"), D(5), LED(&Throttle, LED_INTENSITY, count))); The problem is, the LED() command sees count only when it runs the MapKey() command, so it permanently sets the intensity to the value of count when you run MapKey(). It is as if count is a constant. The only way to pass a variable (not just its value) into a MapKey() is to use a type alias (like &Throttle, or &count), but LED() expects the intensity value to be an int, not an alias. So you can't use &count. So you have to use some form of EXEC() so that it will execute the code every time and hence reference the current value of count, not a constant value stored previously. The obvious way to do that is just use ActKey() in an EXEC(). Here is a version of your code but fixed by using EXEC("ActKey();"). Also, I added some extra code around count to make sure it is never < 0 or > 255. So it enforces using 0, 50, 100, 150, 200, and 250, which gives you the 6 possible LED intensity values for the Warthog. Finally, I added a printf() in EventHandle() just for feedback. include "target.tmh" int count=150; //program startup int main() { if(Init(&EventHandle)) return 1; // declare the event handler, return on error ActKey(PULSE+KEYON+LED(&Throttle, LED_INTENSITY, count)); //set Backlight Intensity MapKey(&Throttle, CHF, CHAIN(EXEC("count = count + 50; if(count>250) count=250;"), D(5), EXEC("ActKey(PULSE+KEYON+LED(&Throttle, LED_INTENSITY, count));") ) ); MapKey(&Throttle, CHB, CHAIN(EXEC("count = count - 50; if(count<0) count=0;"), D(5), EXEC("ActKey(PULSE+KEYON+LED(&Throttle, LED_INTENSITY, count));") ) ); printf("----------------------------------\xa"); } // end main() //event handler int EventHandle(int type, alias o, int x) { DefaultMapping(&o, x); printf("count=%d\xa", count); } P.S. This is the answer most people probably would have tried eventually, but without knowing why it is needed. It is important to understand that MapKey() statements in main() execute only once and that MapKey() stores its configurations in arrays (keyalloc[] and devicedata[] as defined in target.tmh). Those configs are acted upon, or executed later by DefaultMapping() which is called by EventHandle() every time a button is pressed or an axis moves on a controller.
-
Here is an example I use to call an external program called wbeep.exe to give me audible feedback in my TARGET Scripts. //****************************************************************** // Beep() // Beep function - calls wbeep.exe which must be located at the path below. int Beep(int freq, int duration) // Beep(frequency, duration in ms) { if (!freq | !duration) return 0; // Ignore beep - 0 given as freq or duration char buffer;Dim(&buffer, 64); sprintf (&buffer, "spawn c:\\bin\\wbeep.exe %d %d", freq, duration); system(&buffer); } Nothing amazing except to point out, this shows how to send parameters to the external program. I'm pretty sure this example has been given in this thread a long time ago. One thing to remember about using system() is the external program is run by the TARGET Windows service, not by the TARGET Script editor. So permissions are determined by the TARGET Service's permissions. Also, the TARGET Service does not have a console or any method to display information or ask for a response from the user. Some external programs will not run properly because of this. Like I have tried to use a program to play a .WAV file (better than a beep, right?), but it didn't work. I forget why, but it had something to do with the idea that my wbeep.exe program is using very simple low level function calls, but the program that tried to play the .WAV file needed a user console or access to higher level functions that the TARGET Service did not have access to. It's been years since I tried, so I forget the exact issues. Others (probably on this fourm topic) have discussed this. Also, I have seen when I try to make a beep longer than 1 second, it gets truncated to 1 second, as if the TARGET Service kills off the external process at 1 second. Perhaps to prevent a run away program (??).
-
I just answered this question over at SimHQ with a much more extensive example. Here's the topic: https://simhq.com/forum/ubbthreads.php/topics/4021268/several-dcs-profiles-in-one#Post4021268 I have a profile for Train Sim World where I use MSP (shift button) plus LDGH on the Warthog Throttle to cycle through some 20 different locomotive profiles in a single .tmc file. I use the LEDs on the Throttle to display a binary code count of what profile I'm on, as well as make beep sounds of increasing tone so I can quickly cycle through to the correct profile and know which profile I am on without having to alt-tab out of the game. The profiles start at 1, go to 20, and then cycle back to 1. I also use printf() statements to the TARGET script editor console to say what profile I have loaded. It is a lengthy example, so I won't repost any of it here. It is pretty simple and very similar to what SGT_Coyle was just talking about. Sadly, there is no way to tell TARGET to load and run a different script. But as others mentioned, you can certainly break things down into as many different files to keep things organized as you like, just "including" those files in the main .tmc. It isn't too difficult to maintain either. Just be structured in how you lay out your functions, or you can even use separate files for each aircraft. Many ways to do it. Once you have the organization figured out, it is easy from then on.
-
When you change to MapAxis(), you need to "unmap" the KeyAxis(). When you change to KeyAxis(), you need to unmap the MapAxis(). I did an experiment on this and I think I remember that I was able to unmap the MapAxis() or KeyAxis() by just zeroing out the actions as follows: KeyAxis(&Throttle, THR_FC, 0, 0); or MapAxis(&Throttle, THR_FC, 0); So just add these unmap commands to your functions, and you should have it working as you want.
-
Ya, sorry I haven't dove into LUA and DCSBios yet - you are way ahead of me. I only suggested the "cheat" comment because I have seen several people ask questions like yours and the only consistent answer I've seen is single and multi player use different hooks to the LUA code. I'm in the dark beyond that. BTW, the Youtube video link doesn't work for me. Was it taken down, or posted as a private video?