RnD Scripter for Windows RnD Scripter for Linux Pre-written scripts are avalible here: http://www.zomis.net/rnd/rndce/showscript.php Index |
About RnD ScripterRnD Scripter is made by Simon "Zomis" Forsberg.It is a program to help developing RnD Levels. If you have an editing process which might be repeated many times it could be faster and handier to use a script to edit the level. Originally, RnD Scripter was made to make it easier to devlop The world making challenge-style levels such as this one If you are using this program, please let me know! Feedback will give me motivation to develop RnD Scripter more, or other RnD-related utilities. Contact info: E-mail: zomis2k@hotmail.com Rocks'n'Diamonds Web Forum: http://www.artsoft.org/forum Back to Index Program InterfaceScript MenuNew - Creates a new, empty scriptLoad - Is meant to load a new script, but until later you have to use the button to the right of the "Script File" input Save - Saves the script to the filename shown in "Script File" Save as - Opens a save dialog to save the script Compile - Checks if the current script code is valid to be runned Run - Tries to run the current script, before running a compile is made. If the script is runned successfully, it will output "Script Executed" to the log window. (Execute is the same as Run in programming) Exit - Exit from the program Back to Index Level MenuLoad - Is meant to load a level, but until later you have to use the button to the right of the "Level File" inputNOTE: If you have loaded a level, then you change the level using the Rocks'n'Diamonds level editor, you must remember to reload the level. Otherwise, the changes you made using RnD are lost. Save - Saves the level to the filename shown in "Level File". NOTE: If you don't save the level it's mostly no use of running a script. Save as - Shows a save dialog to let you choose where to save the level Back to Index Options MenuScript Font - Opens a font dialog to set the font for the script editorLog Font - Opens a font dialog to set the font for the log output Include rndscripter.zce - Automatically inserts a line at the beginning of the script to include a default script with many constants and functions to use. Backup edited levels - Creates a backup (if possible) of the current level before saving the level - HIGHLY RECOMMENDED! Autosave level - Automatically saves the level after running the script - NOT RECOMMENDED These options are saved to "rndscripter.ini" in your personal Rocks'n'Diamonds folder Back to Index Help MenuHelp - Tells you to go to this pageAbout - Shows a message about me and how I created the program Back to Index Components"Level File" - This shows the current level file loaded, and gives you possibility to load a new level"Script File" - This shows if you have loaded any script file, and gives you option to load one. You do not need to load a script to run one. You can edit and run scripts by yourself without loading from a file. Script - This is the data of the current script. Log output - Shows output from the RnD Scripter program and messages sent by scripts. The latest message is always displayed on top. Back to Index Command line optionsrndce.exe levelfile - starts rndce and loads Script Tutorital1. Our own "Hello World" scriptThis is a script which displayes a "Hello World" message together with the name of the levelDisplaying messages can be done using the function named "Mess" A script should start with "begin" and end with "end.". Between those, [b]statements[/b] can be written. A statement should end with a semicolon ';'. Below, we write two statemeents, both using the same function "Mess": begin Mess('Hello World!'); Mess(level.name); end.Because level.name is a variable, it should not be enclosed with '-characters. NOTE: Only '-characters can be used to enclose a string. " can not be used for this purpose. If the name of the level is "nameless level" this will output to the Log output: namesless level Hello World! (Note that the "namesless level" is shown on top because messages to the log is always inserted on top) Now if we want to display these two messages on the same line (like "Hello World! nameless level"), we need to use a "+" character to append text begin Mess('Hello World! ' + level.name); end.Hello World! namesless level Back to Index 2. AssignmentsNow let's try changing the name of the level. We will change the name of the level to "level by rnd scripter"Assignments are done like this: Variable := Value; begin Mess('Bye bye "' + level.name + '"'); level.name:='level by rnd scripter'; end.If the name of the level previously was "namesless level" this will output: Bye bye "namesless level" Now if you run the script again, you will get Bye bye "level by rnd scripter" Because after running the script the first time, the name of the level has been changed to "level by rnd scripter" So running the script the second time shows the previous name of the level, then changes the name to "level by rnd scripter" (so in reality, no change is made because the previous name is equal to the new name) Back to Index 3. Playing with integersNow let's see if we can modify something else than the level's name. For example, let's change the amount of emeralds needed and the amount of time the player has avalible to solve the level.Let's make a script which triples the needed gems and halves the time needed to solve the level Arithmetic operators avalible are +, -, *, /, div, mod. Since the time cannot contain decimals, we should use div and not /. Using / always results in a value with decimals. Notice the difference here: 4 / 2 = 2.0 while 4 div 2 = 2 begin Messi('Previous gems and time', [level.gems_needed, level.time]); level.gems_needed:=level.gems_needed * 3; level.time:=level.time div 2; Messi('New gems and time', [level.gems_needed, level.time]); end.The "Messi" function is to display a message followed by a couple of integer values. The first parameter is the message to be displayed, the second parameter (which should be enclosed with bracets "[]") is the values to be displayed. If gems needed was 4 and time was 11 this will output: Previous gems and time: 4, 11 New gems and time: 12, 5 So what happens really? How can level.gems_needed assign to itself * 3? Wouldn't that be a neverending loop? When doing this assignment, the script first checks what the new value to be assigned really is. So it evaluates level.gems_needed * 3 (resulting in 4 * 3 = 12), and then it assignes that value to level.gems_needed. So level.gems_needed:=12; Back to Index 4. CommentsYou can comment your code in three ways,The first way is to use // which will make the rest of the line a comment The second way is to use paranthesis together with an asterix. (* this is your comment. *) The comment will start at (* and end at *) The third way is to use a special form of bracets, { these bracets are a short form of using the above solution } Here is our assignment script together with a couple of comments begin// this is just something we need to have, to know where the script should start... Mess('Bye bye "' + (* get the level name and show it *)level.name + '"'); level.name:={assign a new level name here}'level by rnd scripter'; end.// this is also an important thing, so that the script know where to end.Note that comments inside a string are not comments, but instead a string. So writing this: begin Mess('{ i want to comment this }Hello World!'); end.actually outputs: { i want to comment this }Hello World! So remember to always write the comment outside strings, like below begin Mess({ i want to comment this }'Hello World!'); end.Back to Index 5. Constants and variablesEver wondered what a variable is? A variable is a value you have, which can be changed. A light switch is a form of variable. A light switch can have one of two values: On or Off.But then, what's a constant? Constants are fixed variables. A constant can not be assigned a new value, so constants are unchangeable fixed values. Of course, constants needs to get their value some way. Constants gets their value during design-time (when you write your script). Variables get their value during run-time (when the script is being runned). So, enough of that theoretic crap, how can these be used? In fact, you have already used them in the earlier scripts. level.name is a variable, just like level.gems_needed and level.time. While 'Hello World!' is a constant, just like the values 2 and 3. But of course, you can define your own variables and constants. Let's show an example of rewriting the "Playing with integers" script. var s: string; sum: integer; const mess_value = 'gems and time'; mult_value = 3; div_value = 2; begin s:='Previous ' + mess_value; sum:=level.gems_needed + level.time; Messi(s, [level.gems_needed, level.time, sum]); level.gems_needed:=level.gems_needed * mult_value; level.time:=level.time div div_value; s:='New ' + mess_value; sum:=level.gems_needed + level.time; Messi(s, [level.gems_needed, level.time, sum]); end.So what the **** have we done now, really? Don't worry, I'll explain step by step what we have done. First: var s: string; sum: integer;"var" tells the script that we're going to define some variables. "s: string;" defines a variable named "s" of type "string" "sum: integer;" defines a variable named "sum" of type "integer" Note that these variables haven't been assigned a value yet, this is done later, when the script starts. Now for the second part, const mess_value = 'gems and time'; mult_value = 3; div_value = 2;Defines three constants named "mess_value", "mult_value" and "div_value". These constants are assigned the values 'gems and time', 3, and finally, 2. Guess which constant gets which value? No, I won't reveal the answer. I think you can find out for yourself that mess_value is assigned 'gems and time' and that mult_value are assigned 3 and that leaves div_value to be assigned 2. Ah, crap.. I revealed it now, didn't I? Oh well. Now for the real action, the script statements. Does it look advanced? Well, it looks probably harder than it really is. begin s:='Previous ' + mess_value; sum:=level.gems_needed + level.time; Messi(s, [level.gems_needed, level.time, sum]); level.gems_needed:=level.gems_needed * mult_value; level.time:=level.time div div_value; s:='New ' + mess_value; sum:=level.gems_needed + level.time; Messi(s, [level.gems_needed, level.time, sum]); end. At first, the variable "s" is assigned the value 'Previous ' + mess_value, and since mess_value was assigned 'gems and time', s is assigned 'Previous gems and time' Then sum is assigned the sum of level.gems_needed and level.time. So if gems_needed equals 4 and time equals 11, sum is assigned 4 + 11 = 15 Then a message is displayed at the log: "Previous gems and time: 4, 11, 15" it says. Then, level.gems_needed is assigned a new value: 4 * mult_value. So then, what is mult_value? Ah, it says "mult_value = 3" in the "const" section. So then level.gems_needed:=4 * 3 = 12; level.time := 11 div div_value. Since div_value is a constant with the value 2, level.time := 11 div 2 = 5; Then s is assigned a new value. Since s is a variable, the value can change endless times. So s is now set to 'New gems and time' And then, sum is also assigned a new value. Since level.gems_needed and level.time has changed, sum is now assigned to 12 + 5 = 17 And finally, a message is displayed: "New gems and time: 12, 5, 17" That's it! Of course, there wasn't much need of using variables and constants in this script. But imagine if you would have wanted to multiply several things with the same value, then constants would be really handy! Back to Index 6. If-then conditionsI think it's about time we put some logic into our script, it should be able to decide for itself what it should do.For example, it should check if time avalible is 10 or below, if it is then it should double it. Otherwise, it should increase the amount of gems needed by 1. You understand? So we have 2 possible scenarios. If time avalible is 10 or below, or if it isn't (that is, it's above 10). begin if level.time <= 10 then level.time:=level.time * 2 else level.gems_needed:=level.gems_needed + 1; end.Note that there should NOT be a semicolon after the first statement here. That is because in Pascal programming semicolons are not allowed before else. This differs from a couple of other languages. If you write a semicolon before else, you will get an "Identifer expected" compile error. So, this wasn't so hard, right? There are a number of different comparation operators you can use. below < equal = above > equal or below <= equal or above >= conditional_not not conditional_or or conditional_and and conditional_xor xor The last 4 operators are just like the normal boolean operators not true = false, not false = true true or true = true, true or false = true, false or false = false true and true = true, true and false = false, false and false = false true xor true = false, true xor false = true, false xor false = false Back to Index 7. Case-ofNow we've learned the most important things, but be amazed because some goodies are coming up. These goodies are mainly to make all your scripts faster and easier to write.Have you ever experienced annoyance when you need to write a bunch of "if-then-else" statements over and over? You can use case-of-end to make this process faster. Take a look at this script begin case level.gems_needed of 0: Mess('there are no gems needed'); 11: level.gems_needed:=level.gems_needed - 1; 12, 14, 16: level.gems_needed:=level.gems_needed + 2; 13, 15, 17..20: level.gems_needed:=level.gems_needed - 2; else Mess('There are some gems needed'); end; end.The above script does the same as the following if level.gems_needed = 0 then Mess('there are no gems needed') else if (level.gems_needed = 11) then level.gems_needed:=level.gems_needed - 1 else if (level.gems_needed = 12) or (level.gems_needed = 14) or (level.gems_needed = 16) then level.gems_needed:=level.gems_needed + 2 else if (level.gems_needed = 13) or (level.gems_needed = 15) then level.gems_needed:=level.gems_needed - 2 else Mess('There are some gems needed'); Choose yourself which way you choose :) For case-definitions, you should write like this: value: do something; You can combine these in several ways like it was done above. However, each value can only be usedonce. Note: Case statements does not need the "else" part of the case. But it's very useful. Back to Index 8. Functions, procedures and parametersImagine that you have something similar you need to do over and over again. Believe it or not, you have already used all this.For example, if you want to compare two values and set the lower value to the higher value, you can use a procedure. And if you'd like to know how big the difference is between two values, you can use a function. procedure SetToHigh(var a, b: integer); begin if a > b then b:=a else a:=b; end; function Difference(a, b: integer): integer; begin if a > b then Result:=a - b else Result:=b - a; end; begin Messi('Diff between gems and time is', [Difference(level.gems_needed, level.time)]); SetToHigh(level.gems_needed, level.time); end.Back to Index 9. ArraysThe level field, group element data, change pages, and element contents are stored in arrays to make the handling easier. An array is simply a group of variables of the same type arranged in numerical index.The level field can contain 128*128 elements, instead of defining 128*128 = 16384 variables, a multi-dimentional array is used. A multi-dimentional array is an array of another array. The first column of elements on the field is one array, but that's not enough to store all the 16384 elements that can be used on a level. So then multi-dimensional arrays are used. Take a look at this code: begin level.field[0][0]:=0; end.This will simply set the top-left element of the map to empty space. Back to Index 10. for-loopsTo modify arrays, or to accomplish a specific thing a limited number of times, it's useful to use for-loops.for-loops use a variable of type integer, this variable is given an initial value and is increased/drecreased until it reaches a specific value. For every time this variable is increased or decreased, any kind of actions can be done. var i: integer; begin for i:=1 to 10 do begin level.time:=level.time + i; end; end.The assignment above will be executed 10 times. The i variable will be given the values 1 to 10 during those times. So if level.time has an initial value of 4. It will be given the value of 59 at the end. I'll explain why The first time, i is given the value 1. So level.time:=level.time + 1; (level.time := 4 + 1) So level.time := 5 here. Then when that's done, i is given the value 2 and the assignment is done again level.time := 5 + 2; And so on... level.time := 7 + 3; level.time := 10 + 4; level.time := 14 + 5; level.time := 19 + 6; level.time := 25 + 7; level.time := 32 + 8; level.time := 40 + 9; level.time := 49 + 10; Now the i variable has reached the value 10, so the for-loop ends. So level.time = 59. But of course, this isn't the end of the usuability of for-loops for-loops can be nested and used to access arrays. var x,y: integer; begin for x:=0 to 50-1 do for y:=0 to 20-1 do begin level.field[x+10][y+20]:=0; end; end.The above code will create a rectangle of 50*20 size containing empty space, with the start at x position 10 and y position 20. So it will end at x = 59 and y = 39. NOTE: Since x and y are given the initial value 0, we need to decrease the end constants (50 and 20 in this case) by 1 to make sure that the rectangle is given the correct size. Any value can be used as start and end for a for-loop. Back to Index 11. Repeat-untilRepeat-until will do something until a specific condition is true.begin repeat level.time := level.time + 2; level.gems_needed := level.gems_needed - 2; until (level.time > 30) or (level.gems_needed < 50); end.So if level.time equals 10 and levle.gems_needed equals 60 in this example, the repeat-until statement is executed 6 times. level.time := 10 + 2; level.gems_needed := 60 - 2; level.time := 12 + 2; level.gems_needed := 58 - 2; level.time := 14 + 2; level.gems_needed := 56 - 2; level.time := 16 + 2; level.gems_needed := 54 - 2; level.time := 18 + 2; level.gems_needed := 52 - 2; level.time := 20 + 2; level.gems_needed := 50 - 2; So finally, the condition level.gems_needed < 50 will come true, so the repeat will end because until-condition is met. So in the end, level.time = 22 and level.gems_needed = 48. WARNING: Using repeat-until can result in neverending loops. If a neverending loop occours, you have to terminate RND Scripter and start it again which might result in loss of work. Even if the until-condition is already met when the repeat begins, it will be executed once anyway. And if the until-condition isn't met when the first repeat ends, then it will continue until the until-condition is met again. Back to Index 12. While-condition-doA while-condition-do is the same as if condition then repeat ... until not conditionbegin while level.time < 10 do level.time:=level.time + 2; end.If level.time is 12 before the above script is executed, nothing will happen. But if level.time is 7, level.time will be increased by 2 twice, making the end value 11. The above script is the same as writing: begin if level.time < 10 then repeat level.time:=level.time + 2; until not (level.time < 10); end.WARNING: Using while-condition-do can result in neverending loops. If a neverending loop occours, you have to terminate RND Scripter and start it again which might result in loss of work. Back to Index 13. Flow ControlTo control for-loops, while-statements, repeat-until, there's a couple of commands you can use. Those are Continue, Break and Exit.begin Messi('Level.time was', [level.time]); while level.time < 10 do begin if level.time = 6 then Break; if level.time = 1 then Exit; if level.time = 4 then begin level.time:=3; Continue; end; level.gems_needed:=level.gems_needed + 1; Messi('Level.time is now', [level.time]); level.time:=level.time + 2; end; Messi('Level.time is finally', [level.time]); end.In the above script, all three commands has been used. Now for the explanations: Continue - Will start a new while/for/repeat. In the above example, if level.time = 4 then it will set the time to 3 and then start over from "while level.time < 10 do", so that level.gems_needed and below will not be executed this time. Break - Will stop the current while/for/repeat loop. So if level.time = 6 in the above example, it will break there and will output "Level.time is finally: 6" Exit - Will halt the execution of the script. So if level.time = 1 above, it will not show the "Level.time is finally"-message. Back to Index Appendix, pre-defined functionsHere are some pre-defined functions you can use with RnD Scripter There are more pre-defined functions which I don't know what they should be used for or don't think they are useful so they are not included in this list As RnD Scripter is developed, more and more functions will come. So you can't find one of the functions here, then update to the latest release.procedure Mess(s: string); Outputs a string to the log output procedure Messi(const S: String; const i: array of integer); Outputs a string together with a couple of integer values function CE(const i: integer): integer; Converts a Custom Element number to it's real element number. Should be used when accessing the element_info array function GE(const i: integer): integer; Converts a Group Element number to it's real element number. Should be used when accessing the element_info array function InRange(const AValue, AMin, AMax: Integer): Boolean; Checks if AValue is above AMin and below AMax function EnsureRange(const AValue, AMin, AMax: Integer): Integer; Sets AValue to AMin if below AMin and to AMax if above AMax. function Min(a, b: Integer): Integer; Checks which of the values is the smaller one function Max(a, b: Integer): Integer; Checks which of the values is the bigger one function Random(min, max: Integer): Integer; Returns a random generated number from min to max. Will be avalible in BETA 3 function MinIntValue(const Data: array of Integer): Integer; Returns the smallest number function MaxIntValue(const Data: array of Integer): Integer; Returns the largest number function FindCE(const CEname: string): Integer; Finds first CE named CEname Will be avalible in BETA 3 function FindGE(const GEname: string): Integer; Finds first GE named CEname Will be avalible in BETA 3 function LoadLevel(const LevelNumber: Integer; var level: LevelInfo; var elements: eleminfoarr): Boolean; Lets you load another level. You need to define one LevelInfo and one ElemInfoArr variable to store the results. Will be avalible in BETA 3 procedure ClearElement(element: integer); Sets the properties of the element to default. Will be avalible in BETA 3 procedure SetElement(element: integer; hexdata: string); Sets properties of a custom element using microchunk hexadecimal values, for example: SetElement(CE(1), '410001'); sets the gfx_element to sand for Custom Element 1 Will be avalible in BETA 3 procedure CountElements(var Result: ElementCountArray; level: LevelInfo; countsort, descendingorder: boolean); Scans through the map and outputs an array of all elements in level, their element numbers and their corresponding count function InputInt(const ACaption, APrompt: string; var AValue: Integer; const Min, Max: Integer): Boolean; Shows a popup to let user choose the value of AValue function InputQuery(const ACaption, APrompt : String; MaskInput : Boolean; var Value : String) : Boolean; Shows a popup to let user choose what Value should contain function InputBool(const ACaption, APrompt: string; var AValue: Boolean): Boolean; Shows a popup to let user choose whether AValue should be true or not procedure Alert(const aMsg: string); Shows a messagebox with the message aMsg function IfThen(val:boolean; const iftrue, iffalse:integer): integer; If val then return iftrue, else return iffalse function IntPower(base: extended; const exponent : Integer): extended; Returns base raised to exponent. IntPower(2, 4) = 2*2*2*2 = 16 function inttostr(i: Longint): string; Converts an integer to a string function strtoint(s: string): Longint; Converts a string to an integer function copy(s: string; ifrom, icount: Longint): string; Copies icount characters from s with start at the position ifrom. (First character of a string is at position 1) function pos(substr, s: string): Longint; Returns the position of substr in string s. Returns 0 if substr not found. procedure delete(var s: string; ifrom, icount: Longint); Deletes icount characters from s with start at position ifrom. procedure insert(s: string; var s2: string; ipos: Longint); Inserts s into s2 at position ipos. function getarraylength(arr: array): integer; Gets the length of a dynamically allocated array procedure setarraylength(arr: array; newsize: integer); Sets the length of a dynamically allocated array function AnsiUppercase(s : string) : string; Converts string to UPPERCASE function AnsiLowercase(s : string) : string; Converts string to lowercase function Trim(s : string) : string; Trims empty space from end of string function Length(s : String) : Longint; Returns length of string function Sin(e : Extended) : Extended; function Cos(e : Extended) : Extended; Returns sine or cosine of e function Sqrt(e : Extended) : Extended; Returns square root of e function Round(e : Extended) : Longint; Rounds e to nearest integer function Trunc(e : Extended) : Longint; Truncates e down to nearest integer function Pi : Extended; Returns 3.14159265358979... function Abs(e : Extended) : Extended; Get the absolute of e function StrToFloat(s: string): Extended; function FloatToStr(e : Extended) : String; Convert a string to float integer or vice versaBack to Index Appendix, type structuresBelow is the complete list of structures and their contents. When accessing these, it must be done like this:variable.structuremember So the bottom left corner element of the extended change target of custom element 4 page 2 can be accessed like this: element_info[CE(4)].change_page[1].target_content.e[0][2]; type TContent = packed record e: array [0..Pred(3),0..Pred(3)] of integer; end; TContentArr = array [0..Pred(MAX_ELEMENT_CONTENTS)] of TContent; PContentArr = ^TContentArr; EnvelopeInfo = packed record xsize: integer; ysize: integer; text: array [0..Pred(MAX_ENVELOPE_TEXT_LEN+1)] of char; end; pLevelFileInfo = ^LevelFileInfo; LevelFileInfo = packed record nr: integer; typea: integer; packeda: boolean; basename: pchar; filename: pchar; end; DateInfo = packed record year: integer; month: integer; day: integer; end; pLevelInfo = ^LevelInfo; LevelInfo = packed record file_info: LevelFileInfo; game_engine_type: integer; (* level stored in native format for the alternative native game engines *) //native_em_level: pLevelInfo_EM; ***ZZZ*** skipped file_version: integer; (* file format version the level is stored with *) game_version: integer; (* game release version the level was created with *) creation_date: DateInfo; encoding_16bit_field: boolean; (* level contains 16-bit elements *) encoding_16bit_yamyam: boolean; (* yamyam contains 16-bit elements *) encoding_16bit_amoeba: boolean; (* amoeba contains 16-bit elements *) fieldx: integer; fieldy: integer; time: integer; (* available time (seconds) *) gems_needed: integer; name: array [0..Pred(MAX_LEVEL_NAME_LEN+1)] of char; author: array [0..Pred(MAX_LEVEL_AUTHOR_LEN+1)] of char; envelope: array [0..Pred(NUM_ENVELOPES)] of EnvelopeInfo; score: array [0..Pred(LEVEL_SCORE_ELEMENTS)] of integer; yamyam_content: TContentArr; num_yamyam_contents: integer; amoeba_speed: integer; amoeba_content: integer; game_of_life: array [0..Pred(4)] of integer; biomaze: array [0..Pred(4)] of integer; time_magic_wall: integer; time_wheel: integer; time_light: integer; time_timegate: integer; shield_normal_time: integer; shield_deadly_time: integer; extra_time: integer; time_orb_time: integer; extra_time_score: integer; start_element: array [0..Pred(MAX_PLAYERS)] of integer; use_start_element: array [0..Pred(MAX_PLAYERS)] of boolean; artwork_element: array [0..Pred(MAX_PLAYERS)] of integer; use_artwork_element: array [0..Pred(MAX_PLAYERS)] of boolean; explosion_element: array [0..Pred(MAX_PLAYERS)] of integer; use_explosion_element: array [0..Pred(MAX_PLAYERS)] of boolean; (* values for the new EMC elements *) android_move_time: integer; android_clone_time: integer; ball_random: boolean; ball_state_initial: boolean; ball_time: integer; lenses_score: integer; magnify_score: integer; slurp_score: integer; lenses_time: integer; magnify_time: integer; wind_direction_initial: integer; ball_content: TContentArr; num_ball_contents: integer; num_android_clone_elements: integer; android_clone_element: array [0..Pred(MAX_ANDROID_ELEMENTS)] of integer; can_move_into_acid_bits: integer; (* bitfield to store property for elements *) dont_collide_with_bits: integer; (* bitfield to store property for elements *) initial_player_stepsize: array [0..Pred(MAX_PLAYERS)] of integer; (* initial player speed *) initial_player_gravity: array [0..Pred(MAX_PLAYERS)] of boolean; em_slippery_gems: boolean; (* EM style "gems slip from wall" behaviour *) use_spring_bug: boolean; (* for compatibility with old levels *) use_time_orb_bug: boolean; (* for compatibility with old levels *) instant_relocation: boolean; (* no visual delay when relocating player *) can_pass_to_walkable: boolean; (* player can pass to empty or walkable tile *) grow_into_diggable: boolean; (* amoeba can grow into anything diggable *) continuous_snapping: boolean; (* repeated snapping without releasing key *) block_snap_field: boolean; (* snapping blocks field to show animation *) block_last_field: boolean; (* player blocks previous field while moving *) sp_block_last_field: boolean; (* player blocks previous field while moving *) (* ('int' instead of 'boolean' because used as selectbox value in editor) *) use_step_counter: integer; (* count steps instead of seconds for level *) field: array [0..Pred(MAX_LEV_FIELDX)] of array [0..Pred(MAX_LEV_FIELDY)] of word; use_custom_template: boolean; (* use custom properties from template file *) no_valid_file: boolean; (* set when level file missing or invalid *) changed: boolean; (* set when level was changed in the editor *) end; ElementChangeInfo = packed record can_change: boolean; (* use or ignore this change info *) has_event: array [0..Pred(NUM_CHANGE_EVENTS)] of boolean; (* change events *) trigger_player: integer; (* player triggering change *) trigger_side: integer; (* side triggering change *) trigger_page: longword; (* page triggering change *) target_element: integer; (* target element after change *) delay_fixed: integer; (* added frame delay before changed (fixed) *) delay_random: integer; (* added frame delay before changed (random) *) delay_frames: integer; (* either 1 (frames) or 50 (seconds; 50 fps) *) trigger_element: integer; (* element triggering change *) target_content: TContent; (* elements for extended change target *) use_target_content: boolean; (* use extended change target *) only_if_complete: boolean; (* only use complete target content *) use_random_replace: boolean; (* use random value for replacing elements *) random_percentage: integer; (* random value for replacing elements *) replace_when: integer; (* type of elements that can be replaced *) explode: boolean; (* explode instead of change *) has_action: boolean; (* execute action on specified condition *) action_type: integer; (* type of action *) action_mode: integer; (* mode of action *) action_arg: integer; (* parameter of action *) end; pElementChangeInfo = ^ElementChangeInfo; ElementGroupInfo = packed record num_elements: integer; (* number of elements in this group *) element: array [0..Pred(MAX_ELEMENTS_IN_GROUP)] of integer; (* list of elements in this group *) choice_mode: integer; (* how to choose element from group *) (* ---------- internal values used at runtime when playing ---------- *) (* the following is the same as above, but with recursively resolved group elements (group elements may also contain further group elements!) *) num_elements_resolved: integer; element_resolved: array [0..Pred(NUM_FILE_ELEMENTS)] of smallint; choice_pos: integer; (* current element choice position *) end; pElementGroupInfo = ^ElementGroupInfo; ElementNameInfo = packed record token_name: pchar; (* ---------- token and description strings ---------- *) (* element token used in config files *) class_name: pchar; (* element class used in config files *) editor_description: pchar; (* pre-defined description for level editor *) end; pElementInfo = ^ElementInfo; ElementInfo = packed record token_name: pchar; (* ---------- token and description strings ---------- *) (* element token used in config files *) class_name: pchar; (* element class used in config files *) editor_description: string; (* pre-defined description for level editor *) custom_description: string; (* alternative description from config file *) description: array [0..Pred(MAX_ELEMENT_NAME_LEN+1)] of char; (* for custom/group elements *) (* ---------- special element property values ---------- *) properties: array [0..Pred(NUM_EP_BITFIELDS)] of Longword; (* element base properties *) use_gfx_element: boolean; (* use custom graphic element *) gfx_element: integer; (* optional custom graphic element *) access_direction: integer; (* accessible from which direction *) collect_score_initial: integer; (* initial score value for collecting *) collect_count_initial: integer; (* initial count value for collecting *) ce_value_fixed_initial: integer; (* initial value for custom variable (fix) *) ce_value_random_initial: integer; (* initial value for custom variable (rnd) *) use_last_ce_value: boolean; (* use value from element before change *) push_delay_fixed: integer; (* constant delay before pushing *) push_delay_random: integer; (* additional random delay before pushing *) drop_delay_fixed: integer; (* constant delay after dropping *) drop_delay_random: integer; (* additional random delay after dropping *) move_delay_fixed: integer; (* constant delay after moving *) move_delay_random: integer; (* additional random delay after moving *) move_pattern: integer; (* direction movable element moves to *) move_direction_initial: integer; (* initial direction element moves to *) move_stepsize: integer; (* step size element moves with *) move_enter_element: integer; (* element that can be entered (and removed) *) move_leave_element: integer; (* element that can be left behind *) move_leave_type: integer; (* change (limited) or leave (unlimited) *) slippery_type: integer; (* how/where other elements slip away *) content: TContent; (* new elements after explosion *) explosion_type: integer; (* type of explosion, like 3x3, 3+3 or 1x1 *) explosion_delay: integer; (* duration of explosion of this element *) ignition_delay: integer; (* delay for explosion by other explosion *) change_page: array [0..MAX_CHANGE_PAGES] of ElementChangeInfo; (* actual list of change pages *) change: pElementChangeInfo; (* pointer to current change page *) num_change_pages: shortint; (* actual number of change pages *) current_change_page: integer; (* currently edited change page *) group: pElementGroupInfo; (* pointer to element group info *) (* ---------- internal values used at runtime when playing ---------- *) has_change_event: array [0..Pred(NUM_CHANGE_EVENTS)] of boolean; event_page_nr: array [0..Pred(NUM_CHANGE_EVENTS)] of integer; (* page number for each event *) (* ---------- internal values used in level editor ---------- *) access_type: integer; (* walkable or passable *) access_layer: integer; (* accessible over/inside/under *) access_protected: integer; (* protection against deadly elements *) walk_to_action: integer; (* diggable/collectible/pushable *) smash_targets: integer; (* can smash player/enemies/everything *) deadliness: integer; (* deadly when running/colliding/touching *) can_explode_by_fire: boolean; (* element explodes by fire *) can_explode_smashed: boolean; (* element explodes when smashed *) can_explode_impact: boolean; (* element explodes on impact *) modified_settings: boolean; (* set for all modified custom elements *) end;Back to Index Appendix elementsA more detailed documentation about this will be written later.See Alan's map reference document or take a look at the Level Sketch code guide Back to Index Troubleshooting ScriptsWe all make errors. Sometimes it's easy to know what the error is, sometimes it's harder.When there compiler encounters an error, it will display a message like this: [Error] (9:25): Undeclared identifer 'levsel' The error in this case was the line Mess('Level Name: ' + levsel.name);The variable "level" has been misspelled, and there is no variable named "levsel" On every compile error, there are two numbers telling you on which line and column the problem was encountered. In this case it was line 9, column 25. Typical errors include: ; not allowed before else Undeclared identifer x expected but y found functions has result types, procedures doesn't If you get an error message and you don't know exactly what you have written incorrectly, check the structure of your script. Make sure that all the reserved words are written correctly and in correct order, those reserved words are words like "procedure, var, const, else, begin, end, while, for, do, repeat, until" However, some errors can not be detected by the compiler, so they will show up in runtime. Such errors often includes range errors. For example, trying to access level.field[258][6021] won't work since it reaches from 0 to 127. Or accessing element_info[CE(20)].content.e[2][3] when the array only ranges from 0 to 2 If you encounter an inormality when running a script, it's very useful to use the Mess and Messi functions to log the progress of the execution. |