From 826fcc537d63bd7c4febb1a5667250bed7f1008b Mon Sep 17 00:00:00 2001 From: TimePath Date: Sun, 8 Feb 2015 03:08:22 +0000 Subject: [PATCH] Update QuakeC Introduction to GMQCC standards --- ...akeC.md => Introduction_to_QuakeC.markdown | 115 +++++++++--------- 1 file changed, 56 insertions(+), 59 deletions(-) rename Introduction_to_QuakeC.md => Introduction_to_QuakeC.markdown (91%) diff --git a/Introduction_to_QuakeC.md b/Introduction_to_QuakeC.markdown similarity index 91% rename from Introduction_to_QuakeC.md rename to Introduction_to_QuakeC.markdown index de3dd34..bf70711 100644 --- a/Introduction_to_QuakeC.md +++ b/Introduction_to_QuakeC.markdown @@ -9,7 +9,7 @@ Article TODO About QuakeC ------------ -QuakeC is a very simplified dialect of the well-known C programming language, and is used by the Quake I engine and its derivatives. Xonotic uses the FTEQCC dialect of QuakeC, so only this dialect will be described (as well as some common extensions among Quake engines). +QuakeC is a very simplified dialect of the well-known C programming language, and is used by the Quake I engine and its derivatives. Xonotic uses the GMQCC dialect of QuakeC, so only this dialect will be described (as well as some common extensions among Quake engines). Example code ------------ @@ -26,30 +26,31 @@ entity nextent(entity e) = #47; entity findchain(.string fld, string match) { entity first, prev; - entity e; first = prev = world; - for(e = world; (e = nextent(e)); ++e) - if(e.fld == match) - { + for(entity e = world; (e = nextent(e)); e++) { + if (e.fld == match) { e.chain = world; - if(prev) + if (prev) { prev.chain = e; - else + } else { first = e; + } prev = e; } - return first; + } + return first; } // ... entity findnearestspawn(vector v) { entity nearest; - entity e; - for(e = findchain(classname, "info_player_deathmatch"); e; e = e.chain) - if(!nearest) + for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain) { + if (!nearest) { nearest = e; - else if(vlen(e.origin - v) < vlen(nearest.origin - v)) + } else if(vlen(e.origin - v) < vlen(nearest.origin - v)) { nearest = e; + } + } return nearest; } ``` @@ -79,8 +80,6 @@ To declare a variable, the syntax is the same as in C: float i; ``` -However, variables cannot be initialized in their declaration for historical reasons, and trying to do so would define a constant. - Whenever a variable declaration could be interpreted as something else by the compiler, the *var* keyword helps disambiguating. For example, ```c @@ -111,7 +110,9 @@ Some variables are declared in [sys.qh](http://git.xonotic.org/?p=xonotic/xonoti Types ===== -Quake only knows four elementary data types: the basic types `float`, `vector`, `string`, and the object type `entity`. Also, there is a very special type of types, `fields`, and of course `functions`. FTEQCC also adds `arrays`, although these are slow and a bit buggy. Note that there are no pointers! +Quake only knows four elementary data types: the basic types `float`, `vector`, `string`, and the object type `entity`. Also, there is a very special type of types, `fields`, and of course `functions`. GMQCC also adds `arrays`, although these are slow. Note that there are no pointers! + +There are also `int` and `bool` typedefs, but no guarantees are made on the range of values as they are currently not supported by GMQCC. float ----- @@ -125,16 +126,16 @@ Common functions for `float` are especially **ceil**, **floor** (working just li vector ------ -This type is basically three floats together. By declaring a `vector v`, you also create three floats `v_x`, `v_y` and `v_z` (note the underscore) that contain the components of the vector. +This type is basically three floats together. By declaring a `vector v`, you also create three floats `v_x`, `v_y` and `v_z` (note the underscore) that contain the components of the vector. GMQCC also accepts dot notation to access these components: `v.x`, `v.y` and `v.z` + +**COMPILER BUG:** Always use `entity.vector_x = float` instead of `entity.vector.x = float`, as the latter creates incorrect code! Reading from vectors is fine, however. -Vectors can be used with the usual mathematical operators in the usual way used in mathematics. For example, `vector + vector` simply returns the sum of the vectors, and `vector * float` scales the vector by the given factor. Note however that dividing a vector by a float is NOT supported, one has to use `vector * (1 / float)` instead. Multiplying two vectors yields their dot product of type float. +Vectors can be used with the usual mathematical operators in the usual way used in mathematics. For example, `vector + vector` simply returns the sum of the vectors, and `vector * float` scales the vector by the given factor. Multiplying two vectors yields their dot product of type float. Common functions to be used on vectors are `vlen` (vector length), `normalize` (vector divided by its length, i.e. a unit vector). Vector literals are written like `'1 0 0'`. -**COMPILER BUG:** Always use `vector = vector * float` instead of `vector *= float`, as the latter creates incorrect code! - string ------ @@ -143,12 +144,12 @@ A *string* in QuakeC is an immutable reference to a null-terminated character st - **strcat** concatenates 2 to 8 strings together, as in: ```c - strcat("a", "b", "c")=="abc"; + strcat("a", "b", "c") == "abc"; ``` - **strstrofs(haystack, needle, offset)** searches for an occurrence of one string in another, as in: ```c - strstrofs("haystack", "ac", 0)==5; + strstrofs("haystack", "ac", 0) == 5; ``` The offset defines from which starting position to search, and the return value is `–1` if no match is found. The offset returned is *0*-based, and to search in the whole string, a start offset of *0* would be used. @@ -174,26 +175,27 @@ Note that there are different kinds of *strings*, regarding memory management: - **The null string:** A global uninitialized *string* variable has the special property that is is usually treated like the constant, empty, string *“”* (so using it does not constitute an error), but it is the only string that evaluates to FALSE in an if expression (but not in the ! operator~~ in boolean context, the string “” counts as FALSE too). As this is a useful property, Xonotic code declares such a string variable of the name *string\_null*. That means that the following patterns are commonly used for allocating strings: + Assigning to a global string variable: ```c - if(myglobal) + if (myglobal) { strunzone(myglobal); + } myglobal = strzone(...); ``` + Freeing the global string variable: ```c - if(myglobal) + if (myglobal) { strunzone(myglobal); + } myglobal = string_null; ``` + Checking if a global string value has been set: ```c - if(myglobal) { + if (myglobal) { value has been set; - } - else { + } else { string has not yet been set; } ``` @@ -276,7 +278,7 @@ Just like in C, the *void* type is a special placeholder type to declare that a arrays ------ -As the QuakeC virtual machine provides no pointers or similar ways to handle arrays, array support is added by FTEQCC and very limited. Arrays can only be global, must have a fixed size (not dynamically allocated), and are a bit buggy and slow. Almost as great as in FORTRAN, except they can’t be multidimensional either! +As the QuakeC virtual machine provides no pointers or similar ways to handle arrays, array support is added by GMQCC and very limited. Arrays can only be global, must have a fixed size (not dynamically allocated), and are a bit slow. Almost as great as in FORTRAN, except they can’t be multidimensional either! You declare arrays like in C: @@ -298,7 +300,7 @@ assassins[self.assassin_index] = self; The middle one is a global array of (allocated and constant) entity fields and **not** a field of array type (which does not exist), so its usage looks a bit strange: ```c -for(i = 0; i < BTREE_MAX_CHILDREN; ++i) +for (int i = 0; i < BTREE_MAX_CHILDREN; i++) self.(btree_child[i]) = world; ``` @@ -323,10 +325,10 @@ Do not use arrays when you do not need to - using both arrays and function calls Peculiar language constructs ============================ -This section deals with language constructs in FTEQCC that are not similar to anything in other languages. +This section deals with language constructs in QuakeC that are not similar to anything in other languages. -if not ------- +if not (deprecated) +------------------- There is a second way to do a negated *if*: @@ -335,10 +337,10 @@ if not(expression) ... ``` -It compiles to slightly more efficient code than +It compiles to the same code as ```c -if(!expression) +if (!expression) ... ``` @@ -352,7 +354,7 @@ if not("") will not execute (as *“”* counts as true in an *if* expression), but ```c -if(!"") +if (!"") ... ``` @@ -494,8 +496,7 @@ repeatedly. This function is defined as follows: It can be used to enumerate all entities of a given type, for example `"info_player_deathmatch"`: ```c -entity e; -for(e = world; (e = find(e, classname, "info_player_deathmatch")); ) +for (entity e = world; (e = find(e, classname, "info_player_deathmatch")); ) print("Spawn point found at ", vtos(e.origin), "\n"); ``` @@ -513,8 +514,7 @@ It is however noteworthy that some built-in functions create such linked lists u A loop like the following could be used with these: ```c -entity e; -for(e = findchain(classname, "info_player_deathmatch"); e; e = e.chain) +for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain) print("Spawn point found at ", vtos(e.origin), "\n"); ``` @@ -538,9 +538,8 @@ In the map editor, entities can be connected by assigning a name to them in the To QuakeC, these are just strings - to actually use the connection, one would use a find loop: ```c -entity oldself; -oldself = self; -for(self = world; (self = find(self, targetname, oldself.target)); ) +entity oldself = self; +for (self = world; (self = find(self, targetname, oldself.target)); ) self.use(); self = oldself; ``` @@ -559,7 +558,7 @@ void teleport_findtarget() { // ... self.enemy = find(world, targetname, self.target); - if(!self.enemy) + if (!self.enemy) // some error handling... // ... } @@ -576,36 +575,34 @@ void spawnfunc_trigger_teleport() *InitializeEntity* functions are guaranteed to be executed at the beginning of the next frame, before the *think* functions are run, and are run in an order according to their priorities (the *INITPRIO*\_ constants). -if-chains ---------- - -With default compile options (i.e. if the option *-flo* is not passed to the compiler), boolean expressions are evaluated fully. This means that in -```c -if(!flag && SomeComplexFunction(self)) - ... -``` - -*SomeComplexFunction* is always evaluated, even if *flag* is true. To avoid this, one can use: -```c -if(!flag) - if(SomeComplexFunction(self)) - ... -``` - Tracing ------- Pitfalls and compiler bugs ========================== +variable shadowing +------------------ + +```c +.float height; +void update_height(entity e, float height) { + e.height = height; +} +``` +`error: invalid types in assignment: cannot assign .float to float` + +The height *field* overrides the height *parameter*; change the parameter name somehow (`_height`). + complex operators ----------------- Do not count on the modifying and reading operators like *+=* or *++* to always work. Using them in simple cases like: ```c a += 42; -for(i = 0; i < n;i) +for (int i = 0; i < n; i++) { ... +} ``` is generally safe, but complex constructs like: ```c -- 2.39.2