Jump to content

Zenophon

Member
  • Content Count

    530
  • Joined

  • Last visited

  • Medals

Everything posted by Zenophon

  1. In general, all the functions related to directly spawning people and vehicles can spawn addon units, and everything spawned using the framework can be 100% controlled by the mission maker. The framework does not use any preset lists of units that spawn. Whenever it spawns soldiers or vehicles randomly, it dynamically reads the config files looking for certain kinds of objects. Specifically to your question, I assume you mean spawning soldiers using one of these functions: Zen_SpawnGroup Zen_SpawnInfantry For Zen_SpawnGroup, you can simply list the classnames from the addon units as they appear in the editor or the readme of the mod. This is the fastest, simplest option to get addon units into your mission. For example: _group = ["mkSpawn", <Array of Addon Classnames Here>] call Zen_SpawnGroup; This will spawn the soldier objects in order at 'mkSpawn' and return the group. You still might want to set the AI's skill though, using Zen_SetAISkill. For Zen_SpawnInfantry, getting addon units requires a little more work and some basic knowledge of config files. You need to provide information about certain config file values. However, you then get to use the features of Zen_SpawnInfantry, such as a randomized number and type of units. You must open the config files in the editor, then find the addon units classnames under cfgVehicles (BIS really needs to add a search/filter option). The values for parameters 5 and 6 are called 'vehicleClass' and 'faction'. See Zen_SpawnInfantry documentation for parameters 5 and 6. For example: _group = ["mkSpawn", east, 0.3, 5, <vehicleClass value (String)>, <faction value (String)>] call Zen_SpawnInfantry; This will spawn 5 random soldiers whose side, vehicleClass, and faction match the arguments at 'mkSpawn' with skill 0.3 and return the group. For vehicle spawning functions, you can give classnames just like Zen_SpawnGroup. However, the crew of the vehicle (for those functions that create it) will be from the vanilla pool of units (they cannot default to anything else). You should first spawn the vehicle empty, using Zen_SpawnVehicle, then spawn the addon crew and put them inside like so: _vehicle = ["mkSpawn", <Addon Vehicle Classname (String)>] call Zen_SpawnVehicle; _crew = [_vehicle, <Array of Addon Classnames Here>] call Zen_SpawnGroup; 0 = [_crew, _vehicle, "All"] call Zen_MoveInVehicle; The vehicle at 'mkSpawn' now has all possible positions filled with addon units from the group. Just be sure to list the right number of classnames for that particular vehicle. You could also use an empty vehicle from the editor instead of spawning one in the code. This lets you put any crew you want into any vehicle. Finally, if you want to directly generate the classname lists the functions are using, the function to use is Zen_ConfigGetVehicleClasses. See the demonstration mission entitled RandomBattle.Altis for a very detailed explanation of that function and what I just posted about config values.
  2. Horner is correct about the local variables. The part he did not mention is that the code of an action is executed in a different scope from the function which executed the addAction statement. This code executes completely separately: [_vehCost] execVM "Repair.sqf" When the engine sees this statement alone, it cannot possible know the value of _vehCost. The addAction command has a special parameter for arguments, which preserves values attached to the action. The code should be this: _tank addAction [("<t color=""#66FFFF"">" + ("Repair/Flip") +"</t>"), {_this execVM "Repair.sqf"}, [_vehCost], 5, true, true, "", "player distance _target < 5 && !(player in _target)"]; Where _this refers to the arguments of the action being passed to the called code. On a related note, you might want to compile Repair.sqf into a function if it will be used a lot. This is more efficient and saves the engine time. For example: // somewhere at the start of the mission fnc_Repair = compileFinal preprocessFileLineNumbers "Repair.sqf"; // then later from any function _tank addAction [("<t color=""#66FFFF"">" + ("Repair/Flip") +"</t>"), fnc_Repair, [_vehCost], 5, true, true, "", "player distance _target < 5 && !(player in _target)"];
  3. Zenophon

    What's a good IDE for Arma?

    I use Notepad++ with a custom SQF highlighting/autocompletion language. Being able to customize pop-up hints to remind you of function parameters without stopping is very useful. It also has good code folding for custom languages, comment/line operation hotkeys, and decent, easy-to-use macros. You could try Sublime Text 3. It has an SQF plug-in with code snippets etc. if you dig around a little. They claim that you need to buy it, but you can actually use it for free forever. I personally didn't like it, as it is easier to control the SQF language with Notepad++.
  4. Zenophon

    Legal discussion regarding Steam Workshop

    Firstly, I only skimmed the whole thread, so I apologize if this has been brought up before. I am certainly no lawyer, so perhaps some one with relevant legal knowledge or experience can comment on this. However, if some one releases their work to SWS under a certain license, is Valve not legally bound by the terms of that license? In the work I have recently released (see my signature), I felt it was appropriate to make use of a very restrictive Creative Commons license. For reference: https://creativecommons.org/licenses/by-nc-nd/4.0/ Looking at various parts of the license, it seems that these terms below directly contradict the terms of SWS and do not allow the Steam Subscriber Agreement to simply erase any other agreements or force anything on the author without their clear, written consent. Quote CC-NC-ND-4.0 International: "6.c For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 6.d Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 7.a The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 8.c No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor." I agree with RKSL-Rock that we should at least question Valve's intent, and I take the terms of the Steam Subscriber Agreement very seriously. If Valve is willing to accept that alternate licenses apply to content on SWS, do they not still have the right to refuse hosting/distribution service? Is an implied agreement to the terms of SWS upon release there really legally binding if the author's own legal agreement requires clearer consent? Finally, on a more personal note, I believe that if Valve will not acknowledge and respect the IP rights of people who create content (the people they need to make SWS a success), then is not in the best interest of content creators individually or the community as a whole to support the SWS in any way. I, for one, will never release a mission there.
  5. You could just put an enemy soldier in the empty vehicle, then prevent that AI from moving/firing and tell your squad about him. https://community.bistudio.com/wiki/disableAI https://community.bistudio.com/wiki/reveal If your AT soldier still refuses to use his missile launcher, you could force him to do so by removing all ammo for his primary/handgun. This would also work for stopping an AI from firing. You might need to give the magazines back though. https://community.bistudio.com/wiki/removeMagazines Unfortunately, all of these are brute force solutions and workarounds that require triggering code. Unless they use the debug console, there is nothing that players can do in the mission through the commanding menu to get these results, unless the mission gives them actions/radio commands that trigger the code. In general, other things I have noticed that prevent the AI from firing at an obvious target are: An accuracy skill that is too low, making them think they cannot hit their target. Not enough damage, e.g. a rifle vs. a tank. Not knowing enough about the target, in which case you can try using the reveal target command.
  6. If the AI simply won't comply, you can try a brute force solution using trig and vectors: _newUnit allowDamage false; while {!(isTouchingGround _newUnit)} do { _unitPos = getPosATL _newUnit; _playerPos = getPosATL player; _dirToPlayer = (((_playerPos select 1) - (_unitPos select 1)) atan2 ((_playerPos select 0) - (_unitPos select 0))); _newUnit setDir (90 -_dirToPlayer); _newUnit setVelocity [50 * (sin _dirToPlayer), 50 * (cos _dirToPlayer), -10]; sleep 3; }; _newUnit allowDamage true; You can tweak the magnitude of the vector and the sleep duration to whatever looks best in-game. The allowDamage part is to prevent the AI from hitting the ground too hard and killing themselves. I haven't tested this, so it might be possible to remove the setVelocity command entirely and still get good results.
  7. So long as the markers follow a sequential pattern like 'start(1-10)', you can save yourself some typing and make things easier to change by generating the marker names as you delete them. for "_i" from 1 to 10 do { deleteMarker ("start" + str _i); }; If you change how many markers there are in the editor, just update '10' to the new number. I didn't test this, but it seems straightforward enough.
  8. I didn't like the _fnc_ part it put in the name; I prefer a more manual approach so I can name functions any way I want. There are also other global variables that need to be initialized before mission start, so I didn't want to split up the compile process. Also, I don't want to deal with functions working/not working at preInit or postInit. I have purposefully delayed compilation until the Init.sqf to make things more straightforward and prevent people from scattering code all across unit init fields in the editor. This clarifies client/server interaction, so mission makers are coding the first actions that happen from init.sqf and from the server's perspective, which most framework functions were designed for. The commands meant to start the init.sqf also do more than compile. They declare some more variables and functions, create side centers for spawning, detect JIP, etc.
  9. Zenophon

    Detect who killed who

    A 'point' is complete abstraction, you need to provide more detail about what a 'point' is and how you want a system of kill/death points to function. For a simple kill eventhandler to give a 'point' to the killer, I would start off with: _unit addEventHandler ["Killed", {_this call f_assignKillPoint}]; f_assignKillPoint = { _killer = _this select 1; if (_killer != (_this select 0)) then { _killer setVariable ["KillPoints", ((_killer getVariable ["KillPoints", 0]) + 1), true]; }; }; Eventhandlers are persistent for multiplayer respawn. The 'kill' eventhandler is executed locally, so the setVariable command uses 'true' to make the variable public. I also test is the unit has killed itself (could result from something like falling, depends on how the engine handles that).
  10. Zenophon

    Cases & Switches Help

    A switch statement compares the given value ('_type' in this case) to each case value ("sand" literal in your case). This comparison is equivalent to: (_type == "sand") The expression at each case need not be a literal, or even a single variable. Consider this: switch (true) do { case (_type in ["sand", "water"]): { // do something }; } The check for this case becomes: (true == (_type in ["sand", "water"])) However, I am not sure if this is what you really want your function to do. You need to be more specific about what this function is meant to do and where you are using it. If you want to return multiple strings as the first element of the array, you would just use a nested array: [["sand","water"],"glass",70,"Processing Sand"] The && operator is the boolean 'and' operation; you can only apply it to boolean types.
  11. Optimization is entirely different from programming style. You can write fast code that is impossible for anyone else (or even yourself later) to read. Ideally you would write something that can be maintained and debugged easily before worrying about how many milliseconds it takes to run. If you are just writing scripts or programs (this applies to any programming language or project) for yourself, then have a systematic style that you can understand later. The goal to is waste as little time as possible figuring out what you meant to do and more time improving and implementing new features. Encapsulate useful code into functions that can be used later. It is extremely beneficial to develop your own personal library of functions that can be applied very quickly to solve a problem. In general, there are a few things you can do to make your code more readable. Use blank lines to separate related blocks of code. Use informative variable names to indicate what they are meant to store. Only use comments for things that are not obvious. Regarding optimization, you might be interested in this: https://community.bistudio.com/wiki/Code_Optimisation Regarding the preprocessor, all sqf scripts must be preproccessed and compiled by the engine before they are executed. They are compiled into a special data type called 'code'. There are three ways to compile scripts to code: SomeFunction = { // code }; Placing this in a file will preprocess and compile the code, storing in the variable, when the engine executes the statement (statement meaning the = operator). You can now use this variable to refer to that code later. For example: 0 = [] call SomeFunction; has the same effect as: 0 = [] call <copy and paste code here>; However, if you place this call statement in a file that is preprocessed multiple times, all the copied and pasted code will be preprocessed and compiled again. Although defining the variable only preproccesses and compiles the code once, the variable can still be overwritten later. This makes the variable constant: SomeFunction = compileFinal preproccessFileLineNumber <path to file>; However, it requires that you place the code in a separate file rather than inside another file, which could get messy if you have many small functions. Thus, this compile method is only preferable for larger scripts. All of this is in contrast to the execVM command. This command preproccesses and compiles the code in the given file every time it is called. It would be inefficient to put this statement in a loop: 0 = [] execVM "function.sqf"; In summary, I recommend that you do not use execVM, but one of the other methods. Finally, all the optimization in the world doesn't help you if you are running 100 threads. Every use of 'spawn' or 'execVM' open a new thread for the game engine. Unfortunately, the game engine executes sqf code using a scheduler that only runs on one thread. Thus, true mutlithreading in sqf is not possible. The scheduler assigns CPU time to each sqf thread, cycling between them so that they appear to run in parallel. Any function that loops or waits should do so every few seconds (include a 'sleep 3;' or similar), so as not to burden the scheduler. Also, one thread that loops over many objects is preferable to many threads that act on one object. You should also use 'call' as much as possible, as it does not start a new thread. Instead, it pauses executing the caller thread and starts executing in a lower scope.
  12. Something like: this && (player in [s1, s2]) 'in' returns true if the player is given in the array.
  13. For the music code, you should just be able to copy it into the init. The only difference is that the init is executed slightly after all code in the editor. You can synch a trigger to a waypoint, and unit will not move to the next waypoint until the trigger activates. However, you need to create an initial waypoint right next to the the group, then synch a trigger to that. In the trigger's condition field, put: startMission And in your init, after the waitUntil loop, put: startMission = true; The squad will move to the first waypoint you placed, wait for the variable to be true after the intro plays, then they will move to what is now the second waypoint. You can increase the first waypoint's completion radius so that they do not move at all.
  14. Changing the time can be done before the mission start (during the briefing). All clients (JIP or at start) run the init.sqf. So as the mission starts, you want all machines (including the server) to execute the setDate command. Interestingly, because JIP players already sync time, this would set the time incorrectly for them. You want time < 10, not time > 10. In the init: if (time < 10) then { setDate [2035, 7, 6, (paramsArray select 3), 1]; };
  15. The 'setDate' command has local effect. It must be executed on all clients to affect them. Weather is sync'd to JIP players though. https://community.bistudio.com/wiki/setDate
  16. Zenophon

    Weird bug...

    What exactly does 'pmcsfz.sqf' do? Does it run on the server, clients? What does it return? Why return anything if you don't do anything else in the init.sqf? Triggers check locally on every client, so pmclevel must be defined on every machine. In the init, try something like this: _whiteList = []; // fill with object names if (player in _whiteList) then { pmclevel = 1; } else { pmclevel = 0; }; Each client will execute the init, define pmclevel, and the trigger will fire, moving that client to the marker.
  17. First, I would try removing the 'execVM "briefing.sqf";' line, probably not it though. Also ensure that no mods are running and the game is up to date. This is what I did to test this, and it is working: Create a new mission on Stratis, place a player unit anywhere, save the mission using any name. Go to the mission folder and create the init.sqf file. Copy these commands into the init: 0 = [player, "Galati, Altis",450,nil,120,1] spawn BIS_fnc_establishingShot; waitUntil { (!(isNil "BIS_fnc_establishingShot_playing") && {!(BIS_fnc_establishingShot_playing)}) }; sleep 3; player sidechat "start"; Save the init and run the mission. The entire sequence should play, and three seconds after it ends, 'start' should appear at the bottom of the screen. This will also work if you interrupt the sequence with [space]. If you can reproduce that result then the waitUntil loop is working fine. However, any commands in the editor or in 'briefing.sqf' will continue to run during the scene. Anything that needs to wait for the scene to finish should be coded beneath the waitUntil loop. The loop does not stop the mission or pause time, it stops the init.sqf from executing until the condition is true.
  18. Just substitute the waitUntil loop, like this: execVM "briefing.sqf"; 0 = [player, "Galati, Altis",450,nil,120,1] spawn BIS_fnc_establishingShot; waitUntil { (!(isNil "BIS_fnc_establishingShot_playing") && {!(BIS_fnc_establishingShot_playing)}) }; // rest of mission start
  19. I seems the problem is the server fps. A server's maximum fps is 50, so in this mission the server can perform less than a fifth as many actions than the maximum every second. I don't know which of those scripts uses the most resources, but I would guess that it is combining them that causes this issue. I would suggest starting with just one and adding more while observing the change in server fps. You could also see what impact the AI has by printing out how many there are. Something like: hints str ({!(isPlayer _x)} count allUnits); In general, systems that interact with players such as VAS will need fewer resources, as they only execute very quickly when players perform a specific action. While larger scripts or the AI will need more, as they are running and checking constantly.
  20. First, I did contradict myself about BIS_fnc_establishingShot_playing. It should be set to false when the scene stops playing (as the name would indicate). From lines 499-501 of the function: // Start mission BIS_missionStarted = true; BIS_fnc_establishingShot_playing = false; However, neither my nor Fight9's solution works because of a timing issue with BIS_fnc_establishingShot. BIS_fnc_establishingShot_playing is not immediately defined when the function runs. Because you use 'spawn' to execute BIS_fnc_establishingShot, the init.sqf continues and checks the value of BIS_fnc_establishingShot_playing when it was undefined. The waitUntil loop continues when the variable is undefined. You cannot use 'call' to execute BIS_fnc_establishingShot because the function uses sleep commands. You must wait until the value of BIS_fnc_establishingShot_playing is defined before checking it. The loop would look like this: waitUntil { (!(isNil "BIS_fnc_establishingShot_playing") && {!(BIS_fnc_establishingShot_playing)}) }; I have tested this quickly in the editor and it seems to work.
  21. I assume you are the admin of the server. In the chat type '#monitor 5' and the server will print its fps and up/down load every five seconds. If the server is heavily overloaded, players could see strange delays like this. Use '#monitor 0' to disable printing. Also, does this occur for all players at all times, or only in a specific instance? Is the vehicle local to the server, another player? After the animation plays, can a player move around or do anything. This could indicate that client and server are out of synch.
  22. The getVariable command should return the same value for every client. The database should be the same for every client also. I assume you are doing something like this every frame looping through all nearby units: _rankNumber = _unit getVariable "rankLevel"; _rank = // accessor to database _text = format ["%1 %2", _rank, (name _unit)]; // draw icon with _text For one client to see something different means that different data is being stored locally on that machine. If the issue is not fixed by broadcasting the setVariable value again (as you said you tried), then I would add defensive code to check the rank value. At worst, you could display no rank or the unit's game rank. http://community.bistudio.com/wiki/rank Aside from posting all the code (which I sense you are reluctant to do), all you can try is adding a lot of debug statements to log what is going on. If you log enough data, there is probably a pattern to this. I would start off by printing unit, name, getVariable value for each unit for every player on the server. Then run a function on the server that get the same data for every player (but doesn't draw any icons) and logs it. Ask the players to send you their log files and compare them to each other and to what the server logs for this info. You could go further by logging the players' position, if they have respawned, how many icons they are drawing (of which how many they see correctly). Also, check if the text ever goes back to the correct value, or if the issue slowly increases as the game goes on. You could also try not drawing the icon if the rank text is invalid. This would provide a visual reference for when it stopped working. Sorry if this doesn't help, maybe someone with direct experience with this kind of system could help more.
  23. This makes more sense now that you say drawIcon3d. I think the issue is not with the variable being stored. The cursorTarget command has some issues and drawbacks. See the notes here: http://community.bistudio.com/wiki/cursorTarget I would suggest checking the value returned by cursorTarget to make sure it is a player. Then, print out the value for rank to make sure it is defined. For example: if (!(isNull cursorTarget) && {isPlayer cursorTarget}) then { _otherPlayerRankLevel = cursorTarget getVariable ["rankLevel", -1]; diag_log _otherPlayerRankLevel; // debug if (_otherPlayerRankLevel != -1) then { // valid case to draw icon }; }; You could also print the value to in-game chat. I have set a default value for getVariable, so you can test for -1 value and handle this exception somehow. To get around one limitation with cursorTarget, you could use 'reveal' to make sure players can target other players around them: http://community.bistudio.com/wiki/reveal
  24. The variable '_intro' is not defined in the code you posted. I think scriptDone will return true if its argument has no value. I don't know if BIS_fnc_establishingShot always takes the same amount of time, but you could just 'sleep 19;' if that's how long it takes for you. Alternatively, after looking at the code for BIS_fnc_establishingShot, there is a variable named 'BIS_fnc_establishingShot_playing' that is set to true when the function ends. You could try this: waitUntil { !(BIS_fnc_establishingShot_playing) }; Regarding waypoints on the hud, this is a difficulty option that players can change. You could try this: http://community.bistudio.com/wiki/setWaypointVisible
×