Skip to content
View in the app

A better way to browse. Learn more.

主视角中国

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

tracor

初级会员
  • 注册时间

  • 上次访问

由 tracor 发表的全部内容

  1. 不是我自己需要用的,我在IT部门工作,有多个可以自己任意使用的公网IP和主机,并且也建有专供自己查询使用的MOHAA技术网站http://222.40.19.226,之所以要发在这里,一是为了捧场,感谢S-22曾经提供的帮助,二是为了有更多的人能对开发MOHAA MOD感兴趣。
  2. 不是不愿意发。 有时候发点东西,后来发现有误或者需要更改,却又改不成,还得联系版主帮忙修改,又不知等到啥时候,所以就没兴趣了。 我建议过开放编辑功能,没人理,没人理只好算了。 还有,栏目过少,就这一个,所有种类的帖子都在一块儿混,我发的技术帖也沦为灌水帖了。
  3. MOHAA script language tutorial The language used to script in MOHAA is very similar to programming in C++ or Java. This tutorial will try to demystify this scripting language for beginners and advanced scripters alike. This tutorial can be downloaded in zipped html format here: Attach:mohaa_script_tutorial.zip It's also available online in french, thanx to the exelent work of Tropheus On this page... 1. The language 2. Basic mathematical operators 3. Variables 4. Control statements 1.  4.1 The if statement 2.  4.2 The while statement 3.  4.3 The for statement 4.  4.4 The switch statement 5. The targetname operator 6. Arrays 1.  6.1 Creating an array 2.  6.2 Multidimensional arrays 3.  6.3 Array Examples 7. Vectors 8. Methods 1.  8.1 Receiving information into a method 9. Threads 1.  9.1 Ways to start a thread 2.  9.2 Passing information to a threads method 3.  9.3 Waiting for a thread to complete its execution 4.  9.4 Returning information to the thread's caller 5.  9.5 The exec command 10. Automatically started scripts 11. Predefined objects 12. The self object 13. Operator predecence 14. Casting 1.  14.1 Automatic casting 2.  14.2 Manual casting 3.  14.3 Examples of manual casting 15. Strings 16. References 17. Appendixes This tutorial is based on 3 things: · The file Attach:Script_Files.txt, supplied in the docs folder of the MOHRadiant installation. · My own experiences, in mapping, scripting an programming. · The infinite scripting wisdom of jv_map over at .MAP ( Creator of such wonderful things as the jv_bot multiplayer BOT's ). The language The MOHAA scripting language is defined in the file Attach:Script_Files.txt that is shipped in the docs folder of the MOHRadiant installation/the MOH Miscellaneous Script Documentation.html file that is shipped in the docs folder of the Spearhead SDK. The problem ( advantage? ) is that this definition is very exact ( read mathematical ) and hard to understand for someone without a degree in linguistics or programming language theory. Another problem is that it is not very descriptive, and lacks examples. And finally, it only describes part of the language. So I'm going to try to fill in the blanks and explain this to you as clearly as I can. Basic mathematical operators Programming and scripting languages are very mathematical in nature, so lets start with the mathematical operators: expr + expr Adds 2 expressions. Example 6 + 2 results in the value 8 expr - expr Subtracts 2 expressions. Example 6 - 2 results in the value 4 expr * expr Multiplies 2 expressions. Example 6 * 2 results in the value 12 expr / expr Divides 2 expressions. Example 6 / 2 results in the value 3 expr % expr Modulus 2 expressions ( Remainder after division by integer). Example 4 % 2 results in the value 0, as you get "2 complete 2:s" out of 4, with nothing remaining. 3 % 6 is 3... 5 % 2 is 1... 5 % 4 is 1. expr ++ Adds the value 1 to an expression. Example 6 ++ results in the value 7 expr -- Subtracts the value 1 from an expression. Example 6 -- results in the value 5 expr | expr bitwise or (outputs integer) expr ~np~^ ~/np~ expr bitwise exclusive or (outputs integer) expr & expr bitwise and (outputs integer) These operators are used a lot, and can be nested into more complex statements like: 4 * 2 + 5 % 2. So... we wont get very far with just calculating 5 * 12 will we? No, we need a place to store the value we calculated: Variables. Variables A variable is a name that can be used to store a value. Any object in the game can have variables in its variable list. There are some variables that is created by the game, or you can create your own variables. To use a predefined variable, you must know its name: there is a predefined variable that is used by the exploder system ( exploder.scr ) called level.targets_to_destroy... lets use this as an example: what is the value of level.targets_to_destroy + 1 ? Well... that depends on what the value of level.targets_to_destroy is... if level.targets_to_destroy has the value 1, then the expression evaluates to 2. Now that you are aware of variables, I can tell you of the very important assignment operator =. This operator lets you set the value of a variable by doing like this: var = expr Assigns the value of the expression to the variable. So you want to set the number of targets to destroy? No problem: level.targets_to_destroy = 2 Done! The really cool stuff will come when you start using more variables and less actual numbers. Like "I want the roundlimit for my map to be 2 minutes for every objective, and have a random number of objectives between 2 and 5". No problem! If you can dream it up you can probably script it up to! Example: level.targets_to_destroy = randomint(3) + 2 level.roundlimit = level.targets_to_destroy * 2 You start to see how sweet this is now, right? But it gets even more sweet. Creating variables This is easy: just name it, and it will exist. But you must name it in an object, like: local.index = 0 Check the section on Predefined objects to find out what special objects are available. Usually you'll use the local object, and sometimes the level object. If you are making re-usable code which can be used for multiple entities in the game simultaneously, you will probably set variables for each entity that you are handling, possibly referencing the entities via self. Creating variables in the Radiant editor To create variables in objects in the MOHRadiant editor, simply select the object and enter the name and value of the variable you want in the "Entity property box". There are some trix to tell the scripting language how to use these variables: $variable defines the key as a variable with data type string. #variable defines the key as a variable with data type integer (I think). variable makes the game handle the key as a command for the entity. Maybe I can make it more understandable with a scripting example: $variable and #variable would be: self.variable = "myvariable" or self.variable = 4 variable is used like this: self variable "myvariable" As 'variable' is not a valid command the latter results in an error ('failed execution of event 'variable''). ( Thanx to jv_map for explaining these variable naming standards to me ) Control statements Sometimes you want some parts of the script to be skipped under some circumstances. Sometimes you want a part of the script to be repeated a number of times. This can be achieved with the four control statements: if IF a condition is met: do this stuff while WHILE a condition is met: repeat this stuff for FOR as long as a condition is met: repeat this stuff switch SWITCH between this stuff depending on the condition But we cant really understand these control statements if we don't first understand how to define a condition: All control statements ( except switch ) act on true / false ( binary ) conditions. This is really 1 & 0:s, but there is no such thing as a maybe here: Its yes or no, true or false, good or evil, axis or allies... nothing in between. So lets take a look at the binary operators: expr == expr Equality, if the 2 expressions has equal values: output 1, else 0 expr != expr Inequality, if the 2 expressions has different values: output 1, else 0 expr < expr Less than, if the first expression has a smaller value than the last expression: output 1, else 0 expr > expr Greater than, if the first expression has a bigger value than the last expression: output 1, else 0 expr <= expr Less than or equal, if the first expression has a smaller value, or an equal value to the last expression: output 1, else 0 expr >= expr Greater than or equal, if the first expression has a bigger value, or an equal value to the last expression: output 1, else 0 expr && expr Logical and, if both expressions are true ( 1 ): output 1, else 0 expr || expr Logical or, if at least one of the expressions is true ( 1 ): output 1, else 0 ...OK! Now we are equipped to handle conditional statements! The if statement There are two types of if statements: The if, and the if else. IF the expression within the parentheses is true ( evaluates to 1 ), execute the statements within the "curly brackets ( the { and } ). if (expr) { statement ... statement } IF the expression within the parentheses is true ( evaluates to 1 ), execute the statements within the "curly brackets" following the if clause, ELSE execute the statements within the "curly brackets" following the else clause. if (expr) { statement ... statement } else { statement ... statement } Example: if (level.targets_destroyed < 1) { teamwin allies } else { iprintlnbold_noloc "There are remaining objectives!" } The while statement The while statement tests a condition, if it is true ( 1 ) the wile statements code is executed and then the condition is tested again... if it is still true, the code is executed again... until the condition is not true anymore: while (expr) { statement ... statement } Example: local.time = 5 while ( local.time > 0 ) { iprintlnbold_noloc local.time + " seconds remaining!" wait 1 local.time = local.time - 1 } The for statement The for statement is very similar to the while statement, but its declaration is more complex; making it easier to adapt to special cases: for ( statement1 ; expr ; statement2 ) { statement ... statement } At the start of execution of this entire statement, statement1 is executed. At the start of a cycle of the loop the expression expr is evaluated, and while the expression evaluates to true ( 1 ) the statement(s) within the "curly brackets" following the for clause are executed. At the end of each cycle of the loop, statement2 is executed. This sounds rather complicated, but if I show you an example of how it is usually used, it will become clear. Notice how this example is identical in function to the while example above: for (local.time = 5; time > 0; local.time = local.time - 1) { iprintlnbold_noloc local.time + " seconds remaining!" wait 1 } The switch statement This conditional statement differs a bit from the others, as it does not take a binary value as the argument ( it can, but it does not have to ), instead it converts anything it gets into a text string. This is how it looks: switch (expr) { label1: statement ... statement break label2: statement ... statement break case 0: statement ... statement break case 1: statement ... statement break default: statement ... statement break } Its not as scary as it looks, but there are some tricky bits in the switch statement. Lets see an example of its use before I explain it: switch ( local.gameType ) { NIL: local.gameTypeText = "ERROR: NOT INITIALIZED!" break case 1: local.gameTypeText = "Free For All" break case 2: local.gameTypeText = "Team Death Match" break case 3: local.gameTypeText = "Round Based Death Match" break case 4: local.gameTypeText = "Objective" break default: local.gameTypeText = "Unknown or incorrect" break } So this is how it works: First the value of the switch argument is evaluated ( local.gameType ). Then the cases are searched one by one. Say local.gameType has the value 2... case 1.. no, case 2 yep: execute from there. So why the breaks everywhere? Well, the switch statement will execute from where it finds a matching case, so if you remove all breaks from the switch statement above, and the local.gameType variable is 3, the local.gameTypeText variable will be set to "Round Based Death Match (RDM)", then immediately overwritten with "Objective (OBJ)", then overwritten again with "Unknown or incorrect". This is sometime the behavior you want but if not, DON'T FORGET TO END THE CASES WITH BREAK. The targetname operator You have probably seen words starting with $ lots of time in scripts. The definition is: '''The targetname operator $ converts a string to the object with targetname equal to that string. '''Not very enlightening? Maybe not, but here is how you use it: Lets say you have a model in your map that you want to remove or change in some other way in the game. How do you access it in the script? You give it a key / value pair of targetname / someGoodName. After you've done this, you can access the object with $someGoodName in the script. Then you can do stuff like: $someGoodName hide or any other in-game scripting you like. If you have a variable containing the targetname of an entity, you need to use a slightly different expression to get the entity with that targetname. If you have, say: local.my_targetname = "foo" then instead of using $foo to reference that entity, you can use $(local.my_targetname). Note the use of parentheses around the variable name vs. no use of parentheses when you are providing the targetname directly. Arrays Arrays are collections of variables. If you imagine a variable and an array variable like labeled boxes with their contents inside the box like this: Then you can access the variable aVariable by simply writing: aVariable. But what about the array? Well, if you want to refer to the complete array, you just write: anArray... OK... but you want to see what is inside that array, the values, so how do you do that? You do it like this: anArray[anIndex] Where anIndex is the index number of the "box" you want to "look inside". The first box has index 1, the next 2, then 3, and so on... So: anArray[2] equals 3.7 and anArray[3] = aVariable results in: } Creating an array Constant arrays: From the SDK documentation: Created by expression of the form entry_1::entry_2::entry_3:: ... :: entry_n Constant arrays start their indexing at 1. Once created a constant array can not be changed but it can be read for its values. Created like this: local.n = hello::world::this::is::a::test::123 And can be used like this: println local.n[1] // prints hello println local.n[7] // prints 123 println local.n[8] // results in Script Error: const array index '8' out of range The elements of the constant array can be of various types, e.g.: local.spawninfo = "models/vehicles/jeep.tik": 100 0 0)::"my_jeep" spawn local.spawninfo[1] origin local.spawninfo[2] targetname local.spawninfo[3] Hash table arrays: From the SDK documentation: Uninitialized entries evaluate to NIL. Any new entry can be set. A hash table is like a (constant) array, but instead of using integers in a particular range (from [1] to the size of the array), you can use any integer or string. Compared to a constant array, where you define all of the contents of the array in one statement, with a hash table array you create and modify the array as you please. For example: local.spawninfo[1] = "models/vehicles/jeep.tik" local.spawninfo[3] = "my_jeep" // wait for the trigger $sometrigger to be triggered, // possibly by a player walking over it or hitting USE while facing it $sometrigger waittill trigger // put the origin of the player that triggered the trigger into my array local.spawninfo[2] = parm.other.origin Also, as noted, the indices do not need to be numeric, e.g.: local.weapon_types["models/weapons/kar98sniper.tik"] = "sniper" local.weapon_types["models/weapons/bar.tik"] = "mg" A hash table array could be used for storing information gathered over time, vs. a constant array which stores information you generated when you were writing the script. Targetname arrays: From the SDK documentation: Created by the $ targetname operator if more than one entity exists for that targetname. For example, $player is an array if more than one player is in the game. Targetname arrays start their indexing at 1. Note that $player can be treated like an array even if there is only one player in the game, or even if there are none. In the case of a single-player game, you would refer to the player as $player, but in a multi-player game, you would generally treat $player as an array all of the time, even when there's only one player on the server; in that case it just happens to be an array with a length of 1. Even if more than one entity exists with a given targetname, you can still use the "$ targetname" operator; in this case it will refer to all entities with that targetname; for example, in a multi-player game, $player gravity 0 would set gravity to 0 for every player on the server. Getting the length of an array For constant and targetname arrays, you can determine the number of elements in the array using .size. The number of players in a multi-player game is $player.size and you can loop over the player using: for (local.i = 1; local.i <= $player.size; local.i++) { ... since, as noted in the SDK documentation quoted above, targetname arrays start their indices at 1, and there will always be $player.size entries in the $player array. Multidimensional arrays So can you have an array of arrays? No problem! They are called multidimensional arrays, and they are basically arrays that contains more arrays. If a standard ( 1-dimensional ) array could de imagined like the line of boxes, then a 2 dimensional array is like your computer screen... to define a point on your screen you use 2 coordinates, right? The same with a 2-dimensional array, you just add another index like this: anotherArray[1][4] would refer to the forth element of the first array. Add another dimension and you get 3D coordinates in space like this: anotherArray[1][4][2] would refer to the second element of the forth array of the first array. This is confusing enough... you will seldom use these kinds of arrays. But you can if you want to impress your fellow scripters with incomprehensible gibberish. Array Examples // prints the element at position 10 of the local.n array println local.n[10] // sets the element at position (1, 3) of the local.n array // equal to 10 (Hash table array) local.n[1][3] = 10 // constant array local.n = hello::world::this::is::a::test::123 println local.n[1] // prints hello println local.n[7] // prints 123 // results in Script Error: const array index '8' out of range println local.n[8] local.n[variableOne][variableTwo][5] = 23 local.a = local.n[variableOne] local.b = local.a[variableTwo] println local.b[5] // prints 23 for (local.n = 1; local.n <= 10; local.n++) { // print out element in game.stats array at position game.stats_name[local.n] println game.stats[game.stats_name[local.n]] } local.a = (a::::c println local.a[1][1] // prints a println local.a[1][2] // prints b println local.a[2] // prints c Vectors Vectors ( or coordinates ) are definitions of a point in space, like the players position in the game. They are arrays of 3 numbers ( this is, after all, a 3D game ). Vector Examples Vectors are accessed like arrays in the indices 0, 1, 2. A vector could be set like: local.my_vector = (10 -2 60.1) Then this vector could be accessed like: println local.my_vector[2] ...which would print 60.1 to the console. Example $player.origin += (5 6 7) // offset the player's origin by (5 6 7). Note Due to a parsing deficiency, vectors like (-1 2 3) should be written ( -1 2 3). That is, a space must be between the "(" and the "-". Methods You can ( and should ) separate out reusable areas of code into separate units. For example: if you made a working elevator, the code will be a lot easier to make ( and for others to understand ) if the elevator code for going up is separated from the code to initialize the elevator or the code to go down. A method looks like this: my_method: for ( local.index =5; local.index < 20 ; local.index ++ ) { iprintlnbold_noloc "Bla nr. " + local.index wait 2 } end The my_method: line sets the method label of the method. This is later used to execute the method from another location. When called, the method will execute line by line until the end is reached. Use methods for code that will be used many times at multiple places, or just as a way to divide your code into smaller parts that is easier to understand by itself... Take a look at the Threads section to see how the methods are called. Threads an methods are closely connected as you always start a new thread to execute a method. Receiving information into a method This section really belongs in both the Methods section and the Threads section, so if something is unclear: read the Threads section and return here afterwards. Say you want to control two ( or more! ) elevators in your map... you don't want to duplicate the "going up" code once for every elevator ( maybe for two elevators, but imagine a system of elevators ). Code reuse lets you minimize the code mass and thereby minimizing the risk for errors, and the work of finding errors that has entered the code. So we want to tell the method: "Do the same code as usual, but do it to this elevator this time ( next time it may be another elevator, but still the same code ). So here is how we send parameters to a method: instead of: going_up: // Only one elevator $an_elevator moveto $an_elevator.top $an_elevator playsound elevator_run $an_elevator waitmove end We write it like this: going_up local.an_elevator: // With any elevator local.an_elevator moveto local.an_elevator.top local.an_elevator playsound elevator_run local.an_elevator waitmove end This way the same code can be used for any number of elevators in your map. You are not limited to only one parameter, just add more variables to hold more parameters... not sure how many parameters will be sent? Send an array of parameters! Whoopee! :-) Threads Multiple parts of scripts may execute at the same time. This is made possible by the use of threads. Say you want a print on screen every minute to remind the players that time is running out... this could be done with the following code: for ( local.index = 5; local.index > 0 ; local.index-- ) { iprintlnbold_noloc local.index + " minutes left" wait 60 } ...it WILL work if you put it in the main method, but if you wanted anything else to be timed like this ( like an air raid ), the code would become ever more complex... so instead we put the code in a different method and let a separate thread execute the code. The new method looks like this: my_time_counter: for ( local.index = 5; local.index > 0 ; local.index -- ) { iprintlnbold_noloc local.index + " minutes left" wait 60 } } And the code to start a thread that executes the method looks like this: thread my_time_counter or waitthread my_time_counter ... and it is placed in the main method ( it can be placed in any method, but is usually started from main ). Ways to start a thread There are two ways to start a thread: 1. Automatically 2. Manually Automatic thread start The two scripts for your map ( MY_MAP_NAME.scr and MY_MAP_NAME_precache.scr ) are started automatically, if they exist. More exactly: the main method of your map script and all of your precache script is executed by automatically started threads. You have no control over this ( other than not making these files ). Also: Scripts in the anim directory are executed to carry out animation behavior of AI characters. Which script is executed is determined by internal AI state or scripts such as global/shoot.scr. Manual thread start A complete manual thread starting command looks like this: <AN_OBJECT> thread <A_METHOD_LABEL> sets the self object to <AN_OBJECT>. This command is optional, and if it is left out the new thread will receive the same self object as the thread that created it. This is the method that is to be executed. If the method is located in the same script file, it will just be the name of the method, like this: thread my_method_name Or if the method ( bomb_thinker ) is located in another file ( global/obj_dm.scr ), it will look like this: thread global/obj_dm.scr::bomb_thinker There is a variant on this last call. You can call the main method ( only the main method! ) of a script file by just naming the file as method label, like this: thread global/exploder.scr ...this is equal to writing: thread global/exploder.scr::main Note: Please see the "Methods" section of ((MOHAA Script language appendix C)) for corrected information about the "main" method. Passing information to a threads method Using the elevator up method example above, you can pass a parameter ( or more, but in the example: only one, as defined in the method definition ) into the method of a thread at thread creation time like this: thread going_up $some_boring_elevator ...now the going_up method that is executed in the new thread, can access the elevator you sent it. Waiting for a thread to complete its execution If for some reason you don't want to continue executing the current thread until the one you just started ends: replace the thread command with waittread, like this: waitthread my_method_name This way, execution of the current thread will not continue until the new thread has completed. Be sure to know what you are doing before using this... because as long as my_method_name does not complete, the calling thread will go nowhere... Returning information to the thread's caller You may wish to return information to the thread that started your thread. For example, you could write a thread which performs some calculations or gathers some information and then returns it to the caller so you don't have to duplicate the code everywhere you want that information/calculation. To return information to the caller, give the value you wish to return after the end command, e.g.: get_player_weapon local.player: local.player weaponcommand dual targetname the_weapon local.weapon = $the_weapon local.weapon targetname "" end local.weapon // return the weapon (Note that this thread doesn't correctly handle the case of the player not having a weapon) The benefit of using a thread to get the weapon, as shown in the example, is that those three lines of code don't need to be duplicated everywhere you want to get the weapon. Note that you can use multiple end statements in your thread; you may wish to return some kind of error indication in an if statement if you find that some conditions you expected to be met were not met. Also note that in order to return a value to the calling thread, the calling thread must be waiting for that value - if the calling thread used thread to start your thread, there's no way to know when the result would be available and should be assigned/used. waitthread must be used, e.g.: local.player_weapon = waitthread get_player_weapon local.player The exec command The exec command is very similar to the thread command. The first difference is that it must be called with the fully qualified script file name, like this: exec maps/obj/my_script_name.scr::my_method_name ...and it is most often used to just execute scripts, like this well used example: exec global/DMprecache.scr There is also a waitexec command, that differs a bit from the waitthread command in that it waits until all the threads in the threadgroup have terminated, until ending its wait. Remember thread groups are a bit weird. In most cases, a new thread is part of a separate thread group. A thread only belongs to the parent thread group if it's in the same file and self is not defined (equal to self of parent thread). Example: $myobject waitexec <myscript>::mythread mythread: thread myotherthreada self thread myotherthreadb end myotherthreada: wait 5 end myotherthreadb: wait 10 end In this example, myotherthreada belongs to the same thread group as mythread, but myotherthreadb has its own thread group (since it has 'self' in front of the call). Therefore the waitexec line will only wait till myotherthreada is finished. ( Source: jv_map ) Automatically started scripts These scripters are started automatically: maps/MY_MAP_NAME.scr ( or in the maps/dm or maps/obj folder ) A level script is associated with each map, and is loaded and started at the start of that map (and not for subsequent starts from a saved game). This script is used for triggering all map related dynamic objects such as doors, elevators, AI, etc. maps/mapname.scr corresponds to maps/mapname.bsp. A level script is optional. maps/MY_MAP_NAME_precache.scr ( or in the maps/dm or maps/obj folder ) A level precache script is associated with each map, and is loaded and started whenever the map is loaded (even from a saved game). This script is used for precaching map specific resources. Maps/mapname_precache.scr corresponds to maps/mapname.bsp. A level precache script is optional. Scripts in the anim directory ...are executed to carry out animation behavior of AI characters. Which script is executed is determined by internal AI state or scripts such as global/shoot.scr. Predefined objects These object exist at all times and can be very helpful in your scripting. Look in the file Attach:g_allclasses.html in the docs folder of your MOHRadiant installation for a complete definition of these objects: game From the SDK documentation: Refers to the unique game object which maintains its state across levels. Only primitive values (integers/floats/strings/vectors) will persist across levels This is an object of class Game. level From the SDK documentation: Refers to the unique level object which maintains its state for the duration of a level. This is an object of class Level. It is commonly used to store information that is accessed by multiple threads; an alternate location to store such information would be as variables in any entities used by the threads. local From the SDK documentation: Refers to the thread executing the current command. This is an object of class ScriptThread?. It is commonly used to store variables for use by the thread. Any call to a command that does not explicitly specify an entity that it applies to will apply to local, i.e. $foo remove will apply the remove command to $foo; end will apply the end command to local. parm From the SDK documentation: Refers to the unique parm object which can be used to pass parameters to new threads. Note that any use of this variable could be coded "better" by using parameters in the creation of new threads. If you have a thread "foo" which wishes to call a thread "bar" and pass it the value "1" which "bar" will display on the screen, you could use a structure like this: foo: parm.print_this = 1 waitthread bar end bar: iprintln_noloc parm.print_this end However, problems can occur with a code structure like this. There is only one parm object, just like there is only one level object, so if the "bar" thread had a wait command in it before it did the iprintln_noloc, it's possible another thread could set parm.print_this to another value and then the "bar" thread would print the wrong value. Hence, it is better to use parameters in the creation of new threads, as described under "Receiving information into a method". The parm object is also used by the game engine to pass certain information into threads. When a trigger is triggered parm.other will be set to the entity that triggered the trigger. Hence, it is common to see code which uses the waittill trigger command on a trigger and then sets local.player = parm.other, because in the case of a trigger_multiple with no spawnflags set, or a trigger_use, parm.other will be set to the Player entity that triggered the trigger. It is also common to see code like local.player = parm.other at the top of a thread when that thread is named in the setthread attribute of a trigger. When a trigger's spawnflags are set to be triggered by projectiles, parm.other will be set to the Projectile entity (e.g. grenade, rocket, tank shell) that entered the trigger. When a trigger's spawnflags are set to be triggered by "damage", parm.other will be set to the Player who fired the bullet or threw the grenade which caused damage to the trigger area; in the case of the damage being caused by an exploding barrel, for example, the barrel entity would be passed in parm.other. self From the SDK documentation: Refers to the object that the thread is processing for. This object is the same for all threads in a group of threads. The self object can be of any class. group From the SDK documentation: Refers to the object representing the group of threads the thread executing the current command belongs to. This is an object of class ScriptThread?. The self object There is a special object called "self". The "self" object has its value set at the creation of a group of threads. The following are some such situations: Automatically started scripts self is NULL for level scripts. Self is the character for animation scripts. Command: thread label Since the new thread has the same group as the original thread, self in the new thread is equal to self in the original thread. Command: object thread label self in the new thread is set equal to object. Event threads If a thread is initiated in response to an event of an object, then self is set equal to this object. Operator predecence All operators are executed in a special order, as some have predecence over others. The operators are listed in order of evaluation priority, highest first: * / % + - < > <= >= == != & ^ | && || These predecence rules will govern how complex statement are evaluated. Example: 5 + 3 * 2 = 11 First 3 * 2 is evaluated, because * has a higher priority. Then the result is added to 5. What if you really meant the result to be 16? Well, you can use parentheses to change operator evaluation like this: ( 5 + 3 ) * 2 = 16 Casting You can convert between the types of the values you use in scripting. This by your direct order or automatically ( and sometimes when you least expect it ). Automatic casting If a parameter in a statement is required to be of some type, then an automatic cast will be attempted. Manual casting By writing one of the following keywords in front of a value (which are functions provided by the ScriptThread? class), a cast will be attempted: int float string You can cast in any way between these values, but take caution. Examples of manual casting println ( int 3.5 ) will print 3 ( the remainder is cut of in the cast ). println ( float "2" ) will print 2.000 ( a float has 3 decimals precision ). println ( string 3.5 ) will print 3.500 ( remember, 3.5 is a float and as such has 3 decimals precision... so the result will not be 3.5 ). println ( float "thirtysomething" ) will print 0.000 ( the conversion to a number will fail, yielding a 0 value... that is then converted to a float value of 0.000 ). Strings Strings differ from the other types in a lot of ways. Characters of a string are accessed by the [] array operator. Indexing starts at 0. For example, "abc"[2] evaluates to the string "c". There is no character type, so characters are just strings of length 1.
  4. MOHAA scripting language, Script architecture Script architecture All commands used in scripting is written in a script file. This document will explain how a script file is constructed. Table of contents On this page... 1. The *.scr file 2. Comments 1.  2.1 Line comment 2.  2.2 Block comment 3. Methods 1.  3.1 The special main method 4. Contents of your main method 5. A complete example *.scr file The *.scr file All scripting commands are put into *.scr files. These are standard text files that can be created in any text editor ( such as Notepad on the Windows platform or VI & Emacs on the UNIX platforms ). These files end with .scr instead of .txt as text files often end with. This .scr suffix is sometimes a problem on Windows platforms, because most Windows platforms are configured to interpret files ending with .scr as screensaver applications, so if you just double click on an *.scr file in Windows - you get an error message saying: "C:\some_catalog\filename.scr is not a valid WIN32? application.". So you will generally open your script files from the text editor instead of clicking on the file directly. Comments An important part of an *.scr file is its comments. Line comment Basically: anything placed after 2 '/' characters will be ignored by MOH and can be used by the scripter to place helpful comments and explanations of what is actually happening in the script file, without making the game interpret it as wrongful scripting. Block comment Everything after '/*' will be ignored by MOH. It will ignore everything until it finds '*/'. This means that you can comment out large areas ( spanning multiple lines ) of a script. Example: // Pops the first player's helmet off if he's got one... $player[0] pophelmet // I'm so funny /* ...yes, its useless... but I'm a scripter, I can do whatever I want! */ ...is the same ( to MOH ) as writing: $player[0] pophelmet In C, comments of the following form are often used: /* * This is a block comment. */ However, in MOH, this form of comment will cause an error, because MOH appears to require the /* to not be at the end of the line. The workaround is to put a space after the /*. Methods Inside the *.scr file the commands should be placed inside methods. Methods are collections of commands placed within an area of the file starting with a method label ( or method name ), and ending with the keyword end. It looks like this: a_good_method_label: // No commands in this method, but there COULD be... // ...because this is just an example. end Note that commands which cause a thread to execute from a specific label are effectively doing a 'goto' to that label; unlike most modern programming languages, MOH's scripts do not truly support the notion of a function or method, so the convenient method behaviour is constructed from combining the label and end commands. Because of this, the following code is valid: foo: iprintln_noloc "foo" bar: iprintln_noloc "bar" end Calling "waitthread bar" will cause "bar" to be displayed on the screen, and "waitthread foo" will cause both "foo" and "bar" to be displayed on the screen. "end" is simply a command which instructs the thread to terminate, and in the case where the thread was started due to "waitthread", causes the calling thread to resume execution after the "waitthread" call. The same applies to exec calls, "end" causes a jump back to the statement after the "exec" call -- dcoshea ( edited by Bjarne ), The special main method A main method is a method with the method label named 'main'. It is special because it is the "default" method... Default in this case means that if a command is given to execute the script named my_cool_script.scr like this: exec my_cool_folder/my_cool_script.scr ...what will actually happen is this: exec my_cool_folder/my_cool_script.scr::main So? Well... this is what will happen to the main script connected to your map. If your maps name is my_first_map, and it is a DM map, then MOH will automatically attempt to do this when loading your map: exec maps/dm/my_first_map.scr ...and so it is necessary for you to have the 'main' method in your maps script file if you actually want it to be executed. Note: the name "main" does not technically have to be used. Map scripts are executed starting from the first line of the file, regardless of any labels, and in fact no label has to be present, as can be seen in some single-player map scripts that come with the game. The initial thread will simply execute the script until an "end" statement is reached. "exec" also does not care about labels - it also starts executing the script from the first line of the file. An example of a script that uses this functionality is shown in this post on TMT. Contents of your main method So now it is time to build the main method. Very simplified, it looks like this: main: // Stuff to do before prespawn level waittill prespawn // Stuff to do before spawn level waittill spawn // Stuff to do before roundstart level waittill roundstart // Stuff to do after roundstart end For this to make any sense, you need to understand at what times the prespawn, spawn and roundstart events occur. prespawn Prespawn occurs when the map is loaded, and ready to receive players. spawn Spawn occurs when the first player enters the map. roundstart Roundstart only occurs in multiplayer objective games, after at least one player has spawned in the map on each side ( at least one Axis and at least one Allied soldier ). A complete example *.scr file Here is a complete example *.scr file with lots of comments to explain what happens. // This is just info telling other who made this stuff // and where to get more info, if needed. // // obj_vemork_12 // ARCHITECTURE: Bjarne Grönnevik // SCRIPTING: Bjarne Grönnevik // LINK: http://www.gronnevik.se/rjukanproject/ // VERSION: 1.2 main: // Sets up the text that the players will see // when selecting team and, when pressing the // scoreboard key when playing. setcvar "g_obj_alliedtext1" "Smash the distillers" setcvar "g_obj_alliedtext2" "or clear the factory" setcvar "g_obj_alliedtext3" "from Axis forces" setcvar "g_obj_axistext1" "Defend the heavy water" setcvar "g_obj_axistext2" "production plant from" setcvar "g_obj_axistext3" "the allied assault." // Sets up the picture that the players will see // when pressing the scoreboard key when playing setcvar "g_scoreboardpic" "obj_vemork_12" // Sets up a fog effect $world farplane_color "0.3 0.3 0.3" $world farplane 5500 level waittill prespawn // Loads a lot of data used in MP games, so that there // will be no "Please wait, loading..." messages when // playing :-) exec global/DMprecache.scr // Some scripts use this, you wont... I just put it // in to be sure. level.script = maps/obj/vemork_factory_small.scr // Tells MOH what background music to play exec global/ambient.scr m6l1a // Initialize the exploder subsystem ( used for bombs // and stuff ) thread global/exploder.scr::main level waittill spawn // Axis like the bombs unplanted level.defusing_team = "axis" // Allies will try to plant the bombs level.planting_team = "allies" // How many objectives to blow before winning level.targets_to_destroy = 3 // Default damage of the bomb level.bomb_damage = 200 // Default radius of bomb blast level.bomb_explosion_radius = 2048 // Will a player respawn when dying? 1 or 0 (0=no respawn) level.dmrespawning = 0 // round time limit in minutes level.dmroundlimit = 5 // If time runs out: who will win the match? // set to axis, allies, kills, or draw level.clockside = axis level waittill roundstart // By not setting up the bomb and start the win checks // until after roundstart we prevent a single allied // soldier to enter the map and win the map without // resistance. $distiller_bomb thread global/obj_dm.scr::bomb_thinker // Start the win check thread for allies thread allies_win_check // Start the win check thread for axis $distiller_bomb thread axis_win_timer end /******************************************************** * Allies victory test ( Win by acieving all the * 3 objectives or eradication of opposing force ) */ allies_win_check: while(level.targets_destroyed < level.targets_to_destroy) { waitframe } teamwin allies end /******************************************************** * Axis victory test ( Win by timeout or eradication of * opposing force ) */ axis_win_timer: level waittill axiswin // At the end Axis win end
  5. 经典问题,建议置顶。
  6. 商量好了吗,如果不行的话我们就改和=FF=或=FAFL=打友谊赛了。 如果人手不够,随便上什么队员都行。 重在交流,锻炼队伍,给他们创造比赛的环境。
  7. 宗旨:加强交流,促进友谊,锻炼队伍,发现人才 比赛服务器:暂定FPSCHINA(DMW) 规则:采用联赛规则 参赛人员: 1、未参加过联赛的队员 2、参加过联赛但比赛经验比较少的队员 3、如符合条件1、2的人数不够,其他队员也可 比赛成绩: 只作为内部参考资料,不在第三方网站发布。 可否?请贵队考虑。 谢谢。 友谊赛赛程表:http://www.chinavteam.com/chinav/read.php?tid=7615
  8. 宗旨:加强交流,促进友谊,锻炼队伍,发现人才 时间:2005.1.20(腊月二十一) 22:00 地图:Brest 比赛服务器:根据大家ping的情况以下4个服务器任选一 FPSCHINA、 =CHINA.V=北方(铁通)、 =CHINA.V=南方(电信)、 WWW.MOHWorld.Com(网通)服务器 规则:采用联赛规则 选对权:=FREE= 参赛人员: 1、未参加过联赛的队员 2、参加过联赛但比赛经验比较少的队员 比赛成绩: 只作为内部参考资料,不在第三方网站发布。 可否?请贵队考虑。 未尽事宜,可在本帖跟帖讨论。 谢谢。
  9. 第6场比赛 =CHINA.V= VS =FAFL=的时间应该是2006.01.06,抱歉我写错了,特此更改。
  10. =CHINA.V= TS服务器地址更改为 ts.chinavteam.com 请贵队有关联络人员牢记。谢谢。
  11. 为了避免版权纠纷,不再提供MOHAA下载,特此声明。
  12. 为了避免版权纠纷,不再提供MOHAA下载了,需要的朋友请自己想办法解决。 同时,链接http://www.fpschina.com/index.php?showtopic=3016宣布失效。
  13. 申请地址: http://www.chinavteam.com/chinav/read.php?...page=1&toread=1
  14. 晕,最近网络状况也比刚开时差多了,fpschina2.15刚开的时候我20:00 ping 50,现在21:44 ping 261,到福州电信才198
  15. 哈哈哈哈,您老的太极拳功夫连张三丰也自愧不如呀。
  16. 楼上的啥眼神呀,把CHINA变成channel可不怎么好玩吧。 同时建议S-22开放永久编辑功能,给别人一个改过自新的机会。
  17. CHINAVTEAM 公共TS服务器1#地址: www.chinavteam.com:8888 备用TS服务器地址:chinav.eicp.net:8888 开放时间:24*7(停电、检修除外) 权限:普通用户可以建立临时频道和子频道,并自动获得频道管理员权限。 欢迎使用。 ----------------------------------------------------------------------------------------------------------- CHINAVTEAM 公共TS服务器2#(战队专用)地址: www.chinavteam.com:9999 备用TS服务器地址:chinav.eicp.net:9999 开放时间:24*7(停电、检修除外) 权限:除普通用户的权限外,还提供注册用户,以及部分服务器管理员权限。 欢迎各战队使用。 有需要的战队请到CHINAVTEAM外联中心发帖申请,以便赋予相应权限。 CHINAVTEAM外联中心地址:http://www.chinavteam.com/chinav/thread.php?fid=211
  18. 非常不错,基本上各大网络通吃的服务器。哈哈,赞一个。
  19. 没有人担心会有“渡海”和“前线”两支队伍参赛,只是不允许同一个人代表两支队伍参赛。 “渡海”参赛大家同样热烈欢迎,公布了这么久,不要再说你们不知道。
  20. 所谓专业就是在 “Medal of Honor 综合讨论区“ 探讨一些跟Medal of Honor有关的话题。 这样S-22才会觉得回答得有价值。
  21. 问问题问点专业的,像这些问题自己google就行了,用不着麻烦S-22他老人家。
  22. 优质唐僧,赞一下。
  23. 还有,看了贵队的入队申请书,怎么好几个都是渡海作战部队的无情329批复的?难道你们是渡海作战部队下属的一个支队吗? 由于大家对贵队缺乏了解,烦请搂主详细解释来龙去脉。
  24. 不晓得和所谓的“|CMA|中国荣誉勋章联盟”这个政治团体有无关系。 再看此帖,“|CMA|中国荣誉勋章联盟”的管理员渡海作战部队的无情329说: “是啊,渡海和前线是有关系的啊,是我们扶持起来啊” http://chinacma.vicp.net/dispbbs.asp?boardID=20&ID=15&page=1 晕晕晕

Account

Navigation

搜索

搜索

设置浏览器推送通知

Chrome(安卓)
  1. 点击地址栏旁边的锁形图标。
  2. 点击 权限 → 通知
  3. 根据需要调整通知设置。
Chrome(桌面)
  1. 点击地址栏中的锁形图标。
  2. 选择 网站设置
  3. 找到 通知,然后根据需要调整设置。