Okay, let's cut through it.
A short history:
OFP:CWC was released with a powerful scripting language that allowed the execution of
small programs stored in the form of text files. When the exec command is run, OFP loads into memory one of these textfiles (if it is not already in memory), with a limited amount of processing
OFP:R's release built upon a feature available in OFP:CWC, namely the ability of the scripting engine to execute strings, and turned this into a secondary scripting system. Now, in addition to exec'ing scripts, we can call functions, and we can load strings from files (hence call loadfile "myfunction.sqf").
--
Alright, what does this mean?
First off, as alluded to by others, there are several kinds of things you can do in OFP scripting languages. Let's classify them by their addressee, and apologies for nonstandard terminology. What you put in a script can be addressed to:
A) A Preprocessor
B) The Script Scheduler
C) The Line Processor
D) The Game Engine
Preprocessors1) The Function Preprocessor:
__ when you call
Preprocessfile, you are starting a routine that takes a string, removes all the // /* */ comments, and replaces the #defines in the text.
Instructions that target the Function Preprocessor:
//
/* */
#DEFINE
2) The Script Preprocessor:
__ exec actually runs the script file through a preprocessor before it gets into memory. The script preprocessor strips out all the ;comments and blank spaces, removes the #labels and indices them separately. It only does this once per script in memory. A script remains in memory until all instances of that script terminate. (This trivial detail is one thing easily discovered when alt-tabbing in the mission editor and editing files on the fly).
Instructions that target the Script Preprocessor:
; (comment)
#LABEL
The Script SchedulerWell, maybe that's not what it is, but it's what it does.
OFP's engine does some fancy balancing acts between the various demands on system resources. Among other things, it dedicates time to processing all active scripts. While BIS may make claims about "atomic blocks" of up to 100 lines, claims which I wouldn't want to contradict, the scheduler allocates "time" to process scripts based on the number of lines to process. The fact that there appears to be a correlation between number of lines processed and time taken to process them does not mean that each line is processed in its own time slice. Anyway, you can address the script scheduler with the following instructions:
~
n: Suspend script for
n seconds.
@
condition: check script every slice for condition -- if condition obtains, continue script.
GOTO
label: change the line pointer on the script to point to the line corresponding to
label. (I'm not sure if a goto breaks atomicity, but ~ and @ definitely do)
EXIT: terminate the function
The Line ProcessorAt the heart of the OFP scripting engine is the Line Processor. This goes through the text and determines what to do. Everything on the same line is done in the same game-time slice. The following commands address the line processor:
1. Calling Functions
Functions are simply one or more OFP game engine commands that are run on the same line, that is in the same slice.
{function} forEach [array]: execute the function once for each element in the array. Particular elements are represented by _x.
{condition} Count [array]: check the condition once for each element in the array. Particular elements are represented by _x.
These two commands are OFP's oldest in terms of executing functions. The function is run in the same scope as the surrounding code; in scripts, this means that you can define local variables in a foreach/count function (I think, but I'd advise against it)
[template] call
function: executes the function (a string, whether hard coded, variable (keep to less than 4096 bytes or it will be corrupted by a savegame!) or accessed via loadfile).
while {condition} do {function}: repeat function until condition obtains. Please note that this is a line processor function, so time does not pass. while {_time < 1} do {twiddle thumbs} will never terminate.
if (condition) do {function} else {function}: the only conditional that works in a function. Opens a new scope.
Note that all these functions are run in-line. Using []exec opens a process after the line/atomic block.
2. Other stuff
Private []; in scopes below scripts (=functions), only global variables and local variables already defined in higher scopes can be called. Private defines local variables for use in that scope.
comment {}; is parsed but ignored
semicolons (
separate commands.
The Game EngineEverything else I'll just attribute to the "game engine" more generally.
--- Particularities:
scripts and functions have slightly different syntax
In particular, scripts allow the use of ?condition:action, which acts in the same scope as the script, in addition to if (condition) then {action} else {action}, which acts in a subordinate scope.
Anyway, to answer your question: I've seen folks argue for atomicity, and I suppose it's how things work. However, I don't trust it. Anything in a function doesn't need to worry about collisions. Anything in a script needs to consider that from line to line, the game state, and everything in it changes. Time passes. If you need stuff to happen at the same time in a script, put it on the same line and separate with semicolons.