====================================================================== HACKMAP - Version 1.0 June 1/98 by Karel Blaskovic ====================================================================== Copyright © 1998 Karel Blaskovic All Rights Reserved TABLE OF CONTENTS: 1) Introduction a) Overview b) What Hackmap is good for c) What map versions Hackmap can edit d) How Hackmap works 2) Shareware and Ordering 3) Support and Feedback 4) Detailed Help a) Getting started i) Hackmap and Build ii) Hackmap and Build Map Format iii) Values - types and limits iv) Syntax used in Hackmap b) Running Hackmap for the first time i) Opening and Saving files ii) The main screen iii) Looking around iv) Online help c) Changing values i) Direct input in decimal ii) Direct input in binary d) Search and Replace fundamentals i) Methods of searching ii) Combinations of methods iii) Precautions iv) Simple Search and Replace Examples e) Search and Replace in depth i) Using functions for Searching ii) Using functions for Replacing iii) Examples using functions f) Tips and Tricks i) Copying Sectors g) Reference i) Hackmap Keyboard Commands ii) Mathematical Functions, operators, constants iii) Internal Hackmap variables iv) Internal Hackmap functions v) Element names in BUILD and HACKMAP vi) A few facts about some numbers 1) INTRODUCTION a) OVERVIEW HACKMAP is an MSDOS based MAP file editor for DUKE NUKEM 3D. It was designed and created to assist in the production of top-quality DUKE3D levels. HACKMAP is an extremely powerful tool that is intended to supplement the BUILD level-editor for the serious level builder as well the novice who wishes only to take advantage of a few important features or to save time while making levels. b) WHAT HACKMAP IS GOOD FOR Everything that isn't possible with Build, is possible with Hackmap. MAP files are completely at your mercy! Every aspect of every sector, wall, or sprite can be just viewed or altered. Finally - a quick and easy way to change the "z-levels" of your sectors! You can change one, some, or all values at a time making it a breeze to do things such as change shade or visibilities in the whole map, or change the wall tiles of a certain type to something else. Because HACKMAP was intentionally designed to have as few limitations as possible, it is limited only by the DUKE3D map file format itself, not by the abilities of DUKE3D.EXE or BUILD.EXE. One major advantage of this is that anything can be made to have any value as long as the MAP file can store the number. This unique ability allows you to assign values that are not possible with BUILD but are valid nevertheless, and coming up with some designs that up until now could not exist. You want a "curved" floor, or a window that is breakable only from one side? HACKMAP allows you to do these things and much more: it "kicks butt" and that after all, is the whole idea behind DUKE3D! c) WHAT MAP VERSIONS HACKMAP CAN EDIT HACKMAP is not limited to any specific version of DUKE3D, as long as it uses mapfile version 7, which works with the regular version and the atomic edition, and any other version that may come out that has the ability to use the same maps. d) HOW HACKMAP WORKS HACKMAP is not a graphic editor such as BUILD but is instead a numeric editor. Re-inventing BUILD.EXE would take longer to program, cost you more money, and would be for the most part, redundant and unnecessary. Instead, focus was kept on versatility, practicality, and raw muscle. It is fast and powerful yet very affordable. HACKMAP allows you to deal with DUKE3D level data directly. You can view the value of anything and everything, and can change the value of any piece of data that defines any aspect of any sector, wall, or sprite. HACKMAP is essentially a binary file editor that is geared specifically for DUKE3D map files, making the data understandable in terms already known to a level maker who is familiar with BUILD. Data can be changed individually, or "in bulk". A special search-and-replace feature makes the process of finding what data to change relatively easy. What is really amazing is that it allows you to apply virtually any function to any series of data elements. If you have a series of walls and you want the shade to change gradually from light to dark, where each wall is 1 darker than the one before it, you can apply a function similar to "n+1" where "n" is the shade value of the previous wall. In order to be able to do this, you are supplied with HACKMAP-specific variables which you can use in your functions. For example, "select#" is a variable which is the "n'th" object found in a particular search. (I refer to any individual sector, wall, or sprite as an "object" for lack of a better term. For more information, see the instructions where it describes syntax). There are also standard variables available such as PI, as well as a multitude of mathematical functions. You can use these to do things like make smooth hills on a floor (eg: in the shape of a sine function), or make a ramp that becomes gradually steeper (eg: floors sloped according to a square function). And, instead of taking hours, these things can be done in minutes. Your imagination is the limit when it comes to replace-functions because any function is acceptable. Using HACKMAP does not have to be as complicated as it may seem. For the most part, it is very simple. However, it does have the ability to do very complex things should some serious level-builders wish to make some awesome and unique maps. Some data in DUKE3D MAP files is subdivided by DUKE3D.EXE and BUILD.EXE, according to the value of a particular bit. For example, bit number 4 in the value of a wall's CSTAT determines whether or not the wall is masked or not. In order to easily edit these CSTAT values, numbers can be viewed and edited in binary. Best of all, there are also binary functions and binary operators. (eg: "&b", "and", "or", "xor"). This makes it easy to do things such as unmask all masked walls, or remove all one-way walls, or unblock all blocking walls. A little bit of knowledge on the part of the user is a definite asset. If you know how to use build, then you know most of what you need. However, it is possible to assign values that are beyond acceptable limits of BUILD and DUKE3D, essentially "hacking" your level beyond recognition. While some things, such as sector and wall shades, visibilities, and tile-numbers are fairly straight forward and easy to understand, others are not so straight forward. BUILDHLP.TXT or BUILDHLP.EXE explain much that there is to know about DUKE3D map files but they do not explain everything. I have in these instructions, made attempts to share what I have learned but it should be noted that even I do not know everything that there is to know about DUKE3D and its MAP files. Some things remain to be learned. For this reason I strongly suggest that map files are adequately backed up, especially if map files are being edited on an experimental basis. 2) SHAREWARE AND ORDERING HACKMAP is a shareware product. (To view the license agreement please read README.TXT and ORDER.TXT) You are free to use it to try it out. However, you will not be able to save any changes. This means that you will not be able to view the changes you have made in DUKE3D or BUILD except by viewing the number values in HACKMAP. Unfortunately inconvenient, it was the only way to ensure adequate incentive to order the full version. However, the sample MAPs are included with the shareware version to show the potential of HACKMAP. How the sample maps were created is described step by step in section (3.d.iv) and (3.e.iii): examples of using search and replace. At the present, HACKMAP is only available by cheque or money order. Fill out the form in ORDER.TXT and when it is received you will be promptly mailed the full version in the manner you specify. Since the order and the payment is to the author directly, and in light of many recent scams, for those who may have concerns I have provided personal information so that anyone may confirm my identity and sincerity should it be wished to do so. For more information, see ORDER.TXT. 3) SUPPORT AND FEEDBACK Everyone who tries HACKMAP is encouraged to give any useful feedback or comments. Improvements often only come with adequate feedback. All of those who have ordered the full version are entitled to unlimited support for the period of a minimum of one year. Furthermore, buying HACKMAP once entitles you to receive any future versions of HACKMAP at no cost! 4) DETAILED HELP a) GETTING STARTED i) HACKMAP AND BUILD HACKMAP is intended to work in conjunction with BUILD resulting in the need to switch from BUILD to HACKMAP and back to BUILD. Unfortunately, there is no way around this but running both programs from Windows makes this switching back and forth much more convenient. ii) HACKMAP AND BUILD MAP FORMAT HACKMAP, since it deals directly with MAP files, has everything to do with the BUILD MAP FORMAT. A copy of the BUILD MAP FORMAT is included in BUILDHLP.TXT or BUILDHLP.EXE. Although described with the assumption that the reader has an inherent understanding of either C or C++, the information illustrated is nevertheless useful for everyone because it describes the meaning of each element. For example, it states that "wallptr" is the "index to first wall of sector". This value is changed in BUILD by pointing the mouse cursor on a floor near a wall and pressing . BUILD describes this as the "first wall" and is important for sloping floors and ceilings, and panning textures. HACKMAP uses the same names as those described in the BUILD MAP FORMAT, except for some minor alterations. Not only are the same names used, but the order is the same as in the BUILD MAP FORMAT. A convenient conversion table is included in the reference section of this text. iii) VALUES - TYPES AND LIMITS A value can be any number provided that it does not exceed limits determined by the organization of the MAP file itself, except for any x, y, and z, value which has a HACKMAP induced limit. The limit for these is 99999999 which was introduced for display purposes but leaves a more than adequate possible value range. The actual limit for all elements can be seen by loading LIMITS.MAP into HACKMAP. LIMITS.MAP is a dummy MAP for this purpose only and should not be loaded into BUILD or DUKE3D. As mentioned previously, having the ablility to replace values to their absolute limits does not mean that any number is acceptable to BUILD and DUKE3D. This was done intentionally. You should generally not run into problems because you will have a specific value to change to already in mind. Experimenting with values however, may cause unpredictable behavior or errors while running BUILD and DUKE3D and so this should always be kept in mind. Values have different types, meaning that they can be signed or unsigned. The reason for this is that computers can cope with 0's and 1's but cannot directly cope with negative numbers and their "-" signs. Internally, a negative number is defined by the most significant bit of a binary number. If that bit is a "1", it is a negative number. Without getting into the method of binary computations, it may be helpful to note that since both signed and unsigned numbers can have a "1" as the most significant bit, any negative number can be also interpreted as a positive number depending on what you take the most significant bit to mean. For example, one-byte binary number &b11111111, if unsigned, has the value of 255, but if it is viewed as a signed number, the value is (2^8 - 255) = 256 - 255 = -1. The reason for stating this is that you may at some point in time while using HACKMAP, encounter numbers that don't seem right. For example, you may be changing the value for one kind of element in binary to &b1111111111111111, and may be having the number -1 appear in the box showing the decimal value. For a different kind of element, the same binary number &b1111111111111111, may show up as 65535. This is not an error. The difference is that the first element is a signed value and the second is an unsigned value, but both are stored as the same binary number. iv) SYNTAX USED IN HACKMAP There was a need to come up with a generic name for sectors, walls, and sprites. For this, the term "object" was given. "Object" is an individual sector, wall, or sprite. As a collective, they are given the name "type". For example, a level that has 100 sectors and 200 walls, has 100 objects of the type "sectors", and 200 objects of the type "walls". Also, each object has values associated with it that define its position in the level, the tile it uses, the shade, etc. HACKMAP uses the generic term "element" for these. For example, the tile number used as the mask on the masked wall numbered 20, in HACKMAP would be the "overpicnum" element of object numbered 20 of type "walls". It should be noted that objects are numbered starting from 0. This means that the first object is numbered 0, the eleventh is numbered 10, etc. Both the BUILD MAP FORMAT and HACKMAP state the number of objects and those are natural numbers (starting at 1). The highest sector number, for example in a map that has 220 sectors (numsectors=220), would be 219. b) RUNNING HACKMAP FOR THE FIRST TIME i) OPENING AND SAVING FILES The first thing that HACKMAP requires you to do is open a MAP file to view or edit. Especially during your initial trial, make sure you back up any MAP file you intend to use. No online help is available for the open-file routine but standard keys of , , and arrow keys are to be used. While in the open-file routine, will always cause the execution of the program to be terminated. At other times, causes you to go back to where you were before. Using is the only way to open a different file while HACKMAP is running. Saving files is accomplished by pressing when available. At some times, such as in the middle of a search, is not available. However, whenever there has been a change made to file and you quit using , or you want to open a different file, HACKMAP will prompt you and ask if you wish to save the current file. Once again, files are saved only with the full version of HACKMAP. ii) THE MAIN SCREEN After opening a MAP file, the main screen is displayed. To open a different file, opens the open-file window. On the top of the main screen, the basic map information is displayed and this information, like all values displayed in HACKMAP, are in decimal unless specified as binary. This data on the top of the main screen is the information that is not specific to any object. It included the MAP version, starting coordinates, the starting sector, and the number of objects of each type. These values cannot be altered with HACKMAP. Use BUILD to change the start position of Duke, and to add and delete objects. The lower section displays the current values of all elements for a given object. There are 23 elements for every sector and sprite, and 17 elements for every wall. When you first start HACKMAP, the main screen displays the elements for sector numbered 0. While in BUILD, much of this information is available to you in 2D mode: for sectors by positioning the mouse cursor on the desired sector and pressing , for walls by positioning the mouse cursor near the desired wall (on the correct side if it is a 2-sided wall) until it is flashing and pressing and then , and for sprites by positioning the mouse cursor near the desired sprite until it is flashing and pressing and then . However, this information does not use the same names as described in the BUILD MAP FORMAT or the names used in HACKMAP. Although quite easy to figure out, a conversion table is provided in the reference section. iii) LOOKING AROUND The type of object can be changed by pressing: for sectors, for walls, and for sprites. Once the desired type is selected, objects can be flipped through sequentially by using the <+> and <-> keys. You can jump to a particular object by pressing , typing the number of the object (objects remember are numbered from 0) and pressing . When you first start HACKMAP, the element cursor is not visible but is displayed the first time you press any arrow key. The cursor changes position only with the arrow keys and during a search or search and replace. It does not change position when loading a different MAP file. This allows for easy comparison of the value of an element between MAPs. iv) ONLINE HELP and are the help keys. They are available most of the time. shows the meaning of the function keys in HACKMAP as well as a quick reference to the recognized functions and symbols in HACKMAP. displays any keys that are available for use as input at the given time. The specific function of these keys is described in the reference section of this text file. c) CHANGING VALUES i) DIRECT INPUT IN DECIMAL The value of any element of any object can be changed by direct input in decimal. Only values that are highlighted by the element cursor can be changed. The arrow keys move the cursor around. One the correct element is selected, pressing will cause an additional display to appear below the list of elements and their values. To change a value, type in the number and press . Other keys such as and and arrow keys can also be used and a complete listing can be found in the reference section. Keep in mind that the limits for HACKMAP may be beyond acceptable limits of BUILD and DUKE3D. For more information read the section "Values - types and limits". ii) DIRECT INPUT IN BINARY Most values can also be changed by direct input in binary but it should only be desirable to do so for the CSTAT values where each bit of a binary number has a separate function (see the BUILD MAP FORMAT). Instead of typing the number in decimal, press . toggles between input in decimal and input in binary. Use <0>, <1>, and to change the bits. d) SEARCH AND REPLACE FUNDAMENTALS i) METHODS OF SEARCHING The key brings up the Search and Replace window. The up and down Arrow keys select which item is highlighted and the spacebar or Enter selects the item. A search works internally in the following order: type, object, element, value of the element. In other words, HACKMAP looks to the right type, then the right object, and then the right element, and then for a particular value or range of values (or bit pattern). Selecting "All" types, "All" objects, "All" elements, and "ALL" values, will simply move the element cursor to the next element in the MAP file. This is the basis for all searches but more specific searches limit this pattern when you alter the settings in the Search and Replace window. Search limits for values, objects, and the types can be narrowed down so that you can find anything you are looking for. The keys that can be used at any time during the setting of the parameters can be seen by pressing and are listed in the reference section of this text. Once the correct search and replace parameters are set, pressing initiates the search. Without "auto-replace" selected, the search will pause when it finds a matching value. At this time it is possible to enter manually any value. When is pressed the search continues. If you are search and replacing and come up to a value that you do not want to have altered, you may press the spacebar instead of . cancels the search. It is possible to turn the auto-replace on and off during a search and replace. The

key does this. It is recommended that until you become familiar with HACKMAP, you do not select "auto-replace" to start with. Instead, start without it and once you are sure that your search and replace is doing what you wanted it to do, then hit

to initiate the auto-replace. At the end of every search, some statistics are displayed. If the search is canceled, the statistics that are displayed may not add up perfectly; typically, details of what was unchanged may show one less than the total number unchanged. ii) COMBINATIONS OF METHODS It is possible to set some interesting and useful combinations. You can search for a range of values and a particular bit pattern. This is somewhat redundant since you can determine the values by a more specific bit pattern, but it is nevertheless allowed. Another valid combination is searching for objects with a function and a range. This will cause a search to find objects according to the specified function as long as it is within the specified range. iii) PRECAUTIONS There is a small snag to watch for when searching for objects by function. It is possible to choose a function that will result in an endless search loop. If this happens, check the function and the range of objects. Use of functions is described better in (4.e.iii) examples. iv) SIMPLE SEARCH AND REPLACE EXAMPLES Example #1: Suppose you wanted to find all wall picnums (tile numbers) that range from 650 to 655 in the given MAP file, you'd set the type to "walls only", objects to "All", elements to "Specific" and then choose element number 7 which is "picnum", and finally would set the values parameter to "Range" and type in the range from value (650) and the range to value (655). If you also wanted to replace these, select "and replace" and then choose a value. Example #2: If you wanted to search for a particular bit, for example a "1" in the bit #0 position, because you wanted to find all blocking walls, the parameters would be set as: type "Walls only", Objects "All", Elements "Specific" - "cstat", Values "Select bits" - "...............1". In the "select bits" parameter, you may have a "0","1" or a period. Having either a "0" or "1" means that in order for a value to be found, the particular bit must be what is specified. A period means that either a "0" or "1" in that bit position is acceptable for the element value to be found. e) SEARCH AND REPLACE IN DEPTH i) USING FUNCTIONS FOR SEARCHING Only the object number searched can be searched for using a function. If you had a series of 50 walls which were numbered 0,1,2...49 and wanted to search for a value only in the even-numbered walls excluding wall #0, you could select the "objects" - "specific" parameters and manually type the numbers of the walls you wanted: 2,4,6,...48. To save time, you could use a function. There is often more than one way to specify a function because there are different internal variable that can be used. The internal variable "select#" is an index to the "n'th" object found. The function "select#*2" (which actually means "next object to be found = (select#*2)" is what you could use. "select#" always equals 1 when a search starts. To find the first matching object, the value of "select#" is substituted into the equation and is then calculated. So we have: (1)*2 which equals 2. The first object found will be object #2. When the search continues, "select#" is incremented by one and is now equal to 2. The second element found would be: (2)*2 = 4. "select#" would then be again incremented and we would have: (3)*2 = 6, and this process would continue until there are no objects left to find. There is another way to find all even numbered objects excluding #0, by using a function using "object#". "object#" is substituted by the current object number displayed when a search continues. In order to start at object #2, you would have to ensure that object #0 is displayed in the main screen before pressing and bringing up the search and replace window. Assuming this is the case, and you type "object#+2" as your function, the following is what would take place: The current object is object #0, (0)+2 = 2. "object#" now equals 2. For the second find, (2)+2 = 4. "object#" now equals 4 and so we'd arrive at the third by calculating (4)+2 = 6. ii) USING FUNCTIONS FOR REPLACING Using functions for replacing is the same idea as using them for searching. The important difference is that replace functions are applied to determine the replacing value, rather than the object found. Suppose we start with our 50 walls (#0 to #49) and want to make each wall darker than the one before it by 1. Again, we could do this more than one way. The easiest is to use the function "object#+1". During the search and replace, "object#" is substituted, the function is calculated, and the calculated value is the replacing value for the element or elements selected, in this case we would have selected the "shade" element. Replacing functions have access to a greater number of internal variables, a list of which is provided in the reference section of this text. Replacing functions can be as complex as you like, producing some very interesting results. The examples included in the following section describes the process that was involved in making the three sample maps: SAMPLE1A.MAP, SAMPLE1B.MAP, and SAMPLE1C.MAP. iii) EXAMPLES OF USING FUNCTIONS The Making of SAMPLE1A.MAP, SAMPLE1B.MAP, SAMPLE1C.MAP: This example is explained in detail and may seem complicated at first. Once you become familiar with HACKMAP, using functions, and know the necessary pieces of information about DUKE3D MAPs, things go much faster and easier. SAMPLE1C.MAP took me about five minutes to make from SAMPLE1B.MAP. Three of those five were spent coming up with the function to use! Step #1) The first step involved in making SAMPLE1A.MAP involved the use of BUILD. A simple narrow sector with 4 walls was made. To make things easier, the floor was raised so that the z-level = 0. The ceiling was also raised. Also to make things easier, wall #0 was made to be the "firstwall". Then, using the mouse and the right key, this sector was copied and the copy placed beside the original turning wall #2 (opposite the "firstwall") into a red 2-way wall. These two sectors were copied and placed in a similar adjacent fashion creating a total of 4 sector. The process was repeated a few more times until a there was a total of 128 sectors (#0 to #127). To have a look at the sector and wall numbering that BUILD had assigned our sectors and walls, we used and . Sectors run in a line and are numbered #0 to #127. The walls of sector #0 are numbered #0 to #3, the walls of sector #1 are numbered #4 to #7. We see then that the walls along the top are numbered #1,#5,#9 and so on, and the walls on the bottom are numbered #2,#6,#10 and so on. This map was then saved as SAMPLE1A.MAP Step #2) When this was complete we switched to HACKMAP and we decided to change the tiles of the floor and ceilings. After pressing we selected the following parameters: "and replace", values: "all", elements: "specific" - "ceilpicnum", objects "all", type "sectors only",replace: value: 241. Making sure the setting are correct, we initiated the search and replace with . With a few presses of and checking to make sure what was happening is what we desired, we switched to auto by pressing

. When the search and replace finished, we now had tile #241 on all of our ceilings. Step #3) In a similar fashion, we then changed all the floortiles to tile#372. Then we changed the ceiling shade to 12 and the floor shade to 13. These were done with the exact same parameters in the search and replace with the exception that the specific element and replacing value were different. Step #4) For the walls, in the main screen we pressed and then . We want the far left and far right wall to have a picnum (tile number) of 241. Out of all the top-to-bottom running walls, only these two are visible because the others are two-way walls so we can feel free to change the picnums of all walls running in a top-to-bottom direction to tile number 241. The top-to-bottom walls are numbered #0,#2,#4,#6...etc. Our search and replace had the following parameters: "and replace", Values: "all", elements: "specific"+"picnum", objects: "use Function" - "(select#-1)*2", type: "walls only", replace: value: 241. Step #5) Now we repeat the process for left-to-right walls: the walls along the top and bottom. These walls are numbered #1,#3,#5,...etc. For these we selected tile #435. Using the same parameters except for the right selection for the element, and using the object search function "select#*2-1" and a replace value of 435, is how this was achieved. Step #6) To make things interesting, we decided to change the tile of every left-to-right wall in sector #0,#16,#32...etc. Those walls are numbered #1,#65,#129,#193...etc. We know this because the top wall of sector #0 is wall#1, the top wall of sector #1 is wall #5, the top wall of sector #3 is wall #9 and so on. The top wall is always equal to the (sector number)*4+1. We do a search and replace and we have the following parameters: "and replace", values: "all", elements: "specific"+"picnum", objects: "use Function"+"select#*64+1", type: "walls only", and a replace value of 705 which is the number of the tile corresponding to a wall light. We then do the exact same thing for the walls along the bottom (#3,#67,#131,#195,...etc) with the object function "select#*64+3". This map we now saved as SAMPLE1B.MAP. Step #7) Now comes the hard part - making the floor in the shape of wave. For this we decide to use a function of sine because we know that the graph of the sine function looks like a wave. What else do we know? The sine of 0 is 0, sine of 90 degrees is 1, sine of 180 degrees is 0, and the sine of 270 degrees is -1. The sine of anything always results in a value between 0 and 1. We also know that using in BUILD on a floor causes the z-value of the floor to change by 1024. Also, the higher the floor, the lower the z-value (which is somewhat backwards but that's the way it is). If we want the height of our wave on the floor to range between 8192 and -8192 (remember our floor was set to have a z-value of 0 to start), which is the equivalent of 8 's and 8 's at the highest and lowest part of the wave respectively, we need to multiply a simple sine function which produces values between -1 and 1 by 8192. We also want four full waves on our floor. One wave in the standard sine function is from 0 to 360 degrees. Four waves therefore, are from 0 to 360*4 which is 0 to 1440. We have 128 sectors. 1440 degrees divided by 128 sectors is 11.25 degrees per sector. We now have enough information to come up with a replace function. The new z-value of our floor will be: int(sin(rad(object#*11.25))*8192). We would analyze this equation like this: "object#" is substituted by the current object number. (we must make sure we start at 0 before opening the search window with - or we could use "(select#-1)" which would bring the same result and we we not have to be sure of this) We want the sine of the (object number)*11.25 since we wanted to jump 11.25 degrees for every sector. before taking the sine though, we convert degrees to radians with the rad() function. After taking the sine (which produces values from -1 to 1) we multiply our factor of 8192. To eliminate any decimal values produced, we put the whole thing into an int() function which rounds any value down to the first integer value lower than the original value. Our search and replace parameters would then be "and replace", values: "all", elements: "specific"+"floorz", objects "all", replace: "use Function" + "int(sin(rad(object#*11.25))*8192)". If we had a look at our map now, we we would see that the levels of our floors are overall in the shape of a wave but are not sloped - we have a series of steps going up and down in the shape of a sine wave. Step #8) Finally we must take care of the slope of each step. It is not stated specifically anywhere in the manual for BUILD, but slope values can be reached by taking the height difference between one sector and the next sector (rise) and dividing it by the length of the floor (run) and multiplying by 256. In BUILD, using the <[> or the <]> keys can make slopes but these keys change the slope value of a floor or ceiling in increments of 512. However, DUKE3D.EXE will accept in-between values. HACKMAP can change values to basically anything. To change the slopes on the floors, we must come up with a replace function. We need to come up with specific values for our rise and run so we can calculate the slope. Our run is the width of our sectors. That width is 256. We know this because of the size of grid used in BUILD, and we know it for sure if we calculate it by the position of the walls. The x and y coordinates of walls specify the position of the left end of the wall. The left side of Wall #1 in our map connects with the left side of sector #0. The right side of sector #0 is also the left side of sector #1 which has the same x coordinate as wall #5 because wall #5 is the top wall of sector #1. So we take the x coordinate of wall #1 and subtract from the x coordinate of wall #1 and we get: (26880)-(26624) = 256. As for the required "rise", we must use the same equation we used to change the height of the floor. The height of the first sector (sector #0) was: int(sin(rad(object#*11.25))*8192) and the height of the second (sector #1) was: int(sin(rad((object#+1)*11.25))*8192) --(assuming object# remained constant). The difference in height between those two sectors then is: int(sin(rad((object#+1)*11.25))*8192 - int(sin(rad(object#*11.25))*8192 and that difference is used in the calculation of the slope of object#. Putting all these bit of information together we can come up with our complete replace function which is: int([int(sin(rad((object#+1)*11.25))*8192) - int(sin(rad(object#*11.25))*8192)]/[256]*256) The rise and run can be seen in the square brackets. Since we are dividing by 256 and then multiplying by 256, the equation can be simplified down to just: int[int(sin(rad((object#+1)*11.25))*8192) - int(sin(rad(object#*11.25))*8192)] Our parameters are then: "and replace", values "all", elements "specific" + "floorhieghtnum", objects "all", type "sectors only", replace: "use function" + "int[int(sin(rad((object#+1)*11.25))*8192) - int(sin(rad(object#*11.25))*8192)]". After having performed this search and replace, one small detail is left. In order for DUKE3D to slope any floors, bit#1 of the floor's "stat" element must be a "1". One last search and replace with the following parameters is necessary: "and replace", values: "all", elements: "specific"+"floorstat", objects "all", type: "sectors only", replace: "use Function" - "elemx or &b000000000000010. This replace function takes whatever the current value of the current element is (elemx), and performs a bitwise "or" function with the binary number shown. This places a "1" in bit position 1 and does not change the value of any other bit. In the case of this sample map, all floors had a floorstat value of 0 to begin with and we could have replaced by value, with the value 2 which is "10" in binary (which ="000000000000010"). The MAP file was then saved as SAMPLE1C.MAP, and we now have a wave on a floor unlike anything anyone has ever seen in DUKE3D! f) TIPS AND TRICKS i) COPYING SECTORS As you use HACKMAP more and more, it will become apparent just how much time it can save. One of the best uses for HACKMAP deals with its ability to easily change the z-levels of sector floors and ceilings as well as the z-coord of sprites. Here's how to take advantage of this: Any sector or group of sectors that you use often (like doors, doors with frames, etc) does not need to be re-made or, copied and fitted painstakingly. With HACKMAP you can make a library MAP file that store things until you wish to copy them. If you think you will be using a particular door often, copy it to the library MAP. Once there, use HACKMAP to change the z-values of all sector floors, ceilings and sprites. Change them so that the floor of the lowest sector is at a particular level, perhaps -500000 or some other uncommon number that is not likely to be found in anywhere in anyone's map. As you copy more things to this library MAP, you can select the right sectors to change z-levels by search and replacing only those that have a value greater than -500000. When you want to reuse something such as a door with its frame, copy it to the MAP of your choice with BUILD. Then use HACKMAP to alter the z-level. You can select what z-coordinates to change by search and replacing only those values that are less than or equal to -500000, and you can replace it to any z-level you want. By doing this you are essentially using the levels -500000 and above as a "buffer area" used to contain things in the process of copying (remember - the higher the floor, the lower its z-coordinate). For example, a simple drop-down door is found in a level and you want to be able to re-use it. The floor is at 30000 and the ceiling is at 20000. Copy it to your template MAP. Do a search using the value range -499999 to 999999. Use the function "elemx+(-500000-(+30000))" or simply "elemx-530000". That will make the elements you specify ("floorz" and "ceilingz" for sectors, "z-coord" for sprites) to decrease in value by 530000. Your sector floor will then be at -500000, and your ceiling will be at -510000 and any sprites will be somewhere in between. When you want to copy it to a new map, copy it using BUILD, and then change the z-levels accordingly with HACKMAP. If you wanted the floor of your copied door to end up at -32000, then you would search for values less than or equal to -500000 and would use the replace function "elemx-(-500000-(-32000))" or simply "elemx+468000" for all elements that define a z position of any kind. Even better is if you move the sectors and sprites to your library so that the significant parts of the sector or group of sectors is at a certain level, rather that simply the lowest sector floor. By significant parts, I mean those that will end up joining other sectors when you finish copying them. If this method is chosen, and your buffer-zone starts at -500000, you'd want to place your significant part at a level such as -600000 so that even floors extending below the significant part's z-level have a value below -500000. By doing this you will save hours and hours of unnecessary and monotonous work and can spend the extra time using your imagination inventing new things! f) REFERENCE i) HACKMAP KEYBOARD COMMANDS Open File: - selects filename, files, directories, OK, and CANCEL arrow keys - moves cursor in filename, or selects particular file or directory - opens the currently displayed file - opens nothing and exits HACKMAP other keys - , Main Screen: - view sector data - view wall data - view sprite data <+> & <-> - view next or previous object & - same as <+> and <-> - jump to object # ? arrow keys - move element pointer - change element value - help : general - help : display valid keystrokes - save file as - search (and replace) - go back and open another MAP file - exit HACKMAP Change Element Value: <0> to <9> - types digit <-> - make value negative - toggles input in decimal and binary - enters the new value of the element - changes value display to zero - same as left arrow - moves cursor left right arrow - moves cursor right in binary input mode - skip element without changes during a search and replace or selects a "." in the current bit position during binary input

- turns auto-replace on and off during a search and replace - back to main screen or cancel search and replace other keys - Search and Replace: up & down arrow - move position of highlighted item - same as down arrow - mark the highlighted item - same as spacebar right arrow - for some items, if they were selected previously, will do the same thing as the with the difference that previously input values are kept - initiates the search, or search and replace other keys - -after pressing or some items require more input. Common keys are: , numbers and letters, right arrow, , , -during "object: specific" selection, if no objects were previously selected, the only valid keys are and right arrow. If right arrow is pressed, selection of specific objects can be typed in. enters the object number and continues with another. To finish the sequence use the key. At this time you may use the up and down arrow keys to check your inputs, use to remove any undesired inputs, right arrow to amend an input, and and the very end to finish or to cancel. ii) MATHEMATICAL FUNCTIONS AND OPERATORS * The characters "~", "a", "b" signify numeric expressions * The character "#" signifies a string of 0's and 1's (binary) Function: Syntax: Result: ----------------------------------------------------------------------- INT | int~ or int(~) | -rounds ~ down to next lowest | | integer value ABS | abs~ abs(~) | -returns the absolute value of ~ SGN | sgn~ sgn(~) | -returns -1,0, or 1 depending if | | ~ is negative,0, or positive SIN | sin~ sin(~) | -returns the sine of ~ COS | cos~ cos(~) | -returns the cosine of ~ TAN | tan~ tan(~) | -returns the tangent of ~ ASN | asn~ asn(~) | -returns the inverse sine of ~ ACS | acs~ acs(~) | -returns the inverse cosine of ~ ATN | atn~ atn(~) | -returns the inverse tangent of ~ LOG10 | log10~ log10(~) | -returns the log, base 10, of ~ LOG2 | log2~ log2(~) | -returns the log, base 2, of ~ LOG | log~ log(~) | -returns the natural log (ln) of~ FACT | fact~ fact(~) | -returns the factorial of ~ MOD | amodb (a)mod(b) | -returns the modulus: the | | remainder after a is divided by b DEG | deg~ deg(~) | -converts from degrees to radians RAD | rad~ rad(~) | -converts from radians to degrees &b | &b# | -converts # to decimal ¬ | ¬# | -converts # to decimal after | | switching all 0's to 1's and | | switching all 1's to 0's ---------------------------------------------------------------------- Other: ---------------------------------------------------------------------- ()[]{}| | -any of these brackets are valid ^ | | -exponentiation symbol * / | | -multipy and divide + - | | -add and substract and | aandb (a)and(b) | -performs bitwise "and" between two | | decimal numbers. decimal numbers | | are automatically converted to | | binary and then back to decimal or | aorb (a)or(b) | -performs bitwise "or" xor | axorb (a)xor(b) | -performs bitwise "xor" pi | | -returns 3.14159265358979 rnd | | -returns random number between 0 and 1 ---------------------------------------------------------------------- * When using functions, some lead signs in some combinations are not valid: (ie ...*-2/.... must be ....*(-2)/...) iii) INTERNAL HACKMAP VARIABLES Variable: | Availability: | Meaning: ----------------------------------------------------------------------- |Object | Replace | |Search | | ----------------------------------------------------------------------- elemx | - | yes | -the value that is highlighted at any | | | time by the element value pointer elem(~) | - | yes | -the value of an element in the current | | | object. Elements are indexed by number. bitmask | - | yes | -is the binary string of digits that was | | | last defined by value:"select bits". | | | Any "." are replaced with "0" !bitmask | - | yes | -is the same as using ¬bitmask. It is | | | the bitmask with the 0's replaced with | | | 1's and the 1's replaced with 0's object# | yes | yes | -the value that is equal to the current | | | object number. select# | yes | yes | -the value that is equal to the "n'th" | | | object being searched for. It always | | | starts at 1 and increments by one | | | after the next matching object is found | | | and optionally replaced. tfound# | yes | yes | -the total number of elements found found# | yes | yes | -the number of elements found in the | | | current objects. It is never < 1. ----------------------------------------------------------------------- * examples: 1) if wall #2 is the current object, elem(9) would be equal to the the wall's "shade". Index numbers start at 1 and go to 23 for sectors and sprites and range from 1 to 17 for walls. 2) if a value: "select bits" was chosen, and "....0..1.1..001" was entered, "000000010100001" would be the "bitmask" and "111111101011110" would be the "!bitmask" iv) INTERNAL HACKMAP FUNCTIONS Internal HACKMAP functions are input in the same place as object search functions. This is mostly only for convenience. Internal HACKMAP functions do not behave like other functions. They are essentially "pre-search" aids. The two HACKMAP internal functions are: WALLS(~) and SPRITES(~). These functions automatically pre-select the wall numbers or sprite numbers of the walls and sprites of the given sector "~". For example, suppose that in a MAP, sector #4 has 4 walls that are numbered #67,#68,#74,#76 . Typing WALLS(4) after selecting objects: "use Function" will cause two things to happen. Objects: "specific" will be marked instead of objects:"use Function", and the numbers 67,68,74,76 will be entered as specific selections as if you had typed them in yourself. The display for objects:"specific" will show the value of the first wall, in this case it will show "1) Wall # 67". SPRITES(~) works in the exact same way as WALLS(~). The presearch with SPRITES(~) takes longer than WALLS because there is no index to the sprites in the sector data and must actually be searched for. WALLS(~) systematically finds the right walls by checking the "wallptr" value of the sector and subsequently the "point2" values of the walls. v) ELEMENT NAMES IN BUILD AND HACKMAP ---------------------------------------------------------------------- BUILD MAP FORMAT | HACKMAP | BUILD ---------------------------------------------------------------------- SECTORS: ---------------------------------------------------------------------- wallptr | wallptr | Firstwall wallnum | wallnum | Numberofwalls ceilingz | ceilingz | Z-coordinate floorz | floorz | Z-coordinate ceilingstat | ceilstat | Flags (hex) floorstat | floorstat | Flags (hex) ceilingpicnum | ceilpicnum | Tile number ceilingheinum | ceilheinum | Ceiling heinum ceilingshade | ceilshade | Shade byte ceilingpal | ceilpal | Palookup number ceilingxpanning | ceilxpan | ceilingypanning | ceilypan | floorpicnum | floorpicnum | Tile number floorheinum | floorheinum | Floor heinum floorshade | floorshade | Shade byte floorpal | floorpal | Palookup number floorxpanning | floorxpan | floorypanning | floorypan | visibility | visibility | Visibility filler | filler | lotag | lotag | lotag (and Tags) hitag | hitag | hitag (and Tags) extra | extra | Extra ---------------------------------------------------------------------- WALLS ---------------------------------------------------------------------- x | x-coord | X-coordinate y | y-coord | X-coordinate point2 | point2 | Point2 nextwall | nextwall | nextwall nextsector | nextsector | nextsector cstat | cstat | Flags (hex) picnum | picnum | Tile number overpicnum | overpicnum | OverTile number shade | shade | Shade pal | pal | Pal xrepeat | xrepeat | (X,Y) repeat yrepeat | yrepeat | (X,Y) repeat xpanning | xpanning | (X,Y) pan ypanning | ypanning | (X,Y) pan lotag | lotag | lotag (and Tags) hitag | hitag | hitag (and Tags) extra | extra | Extra ---------------------------------------------------------------------- SPRITES ---------------------------------------------------------------------- x | x-coord | X-coordinate y | y-coord | Y-coordinate z | z-coord | Z-coordinate cstat | cstat | Flags (hex) picnum | picnum | Tile number shade | shade | Shade pal | pal | Pal clipdist | clipdist | Clipdist filler | filler | xrepeat | xrepeat | (X,Y) repeat yrepeat | yrepeat | (X,Y) repeat xoffset | xoffset | (X,Y) offset yoffset | yoffset | (X,Y) offset sectnum | sectnum | Sectnum statnum | statnum | Statnum ang | angle | Angle owner | owner | Owner xvel | xvel | X-Velocity yvel | yvel | Y-Velocity zvel | xvel | Z-Velocity lotag | lotag | lotag (and Tags) hitag | hitag | hitag (and Tags) extra | extra | Extra ---------------------------------------------------------------------- vi) A FEW FACTS ABOUT SOME NUMBERS -Build automatically adjusts the xrepeat and yrepeat, as well as the xpanning and ypanning when walls are created. The exact method is unknown to me. -It is not possible to change only the "wallptr" of a sector without re-numbering the walls in the sector. Swapping objects easily is not possible in HACKMAP version 1.0. Anything that may require the "firstwall" of a sector to be in a particular place should be done in BUILD with before switching to HACKMAP. This will always be required when dealing with sloped floors and ceilings since they always slope from the "firstwall". -Some elements such as "shade" can have negative values but have the same effect in Duke3D and BUILD as a value of zero. This can be made use of if you want to temporarily brighten you MAP so that it is easier to work with. You can, with a search and replace, with a replace function, change all the shades by subtracting a constant from the original value (eg "elemx - 50"). This has the same effect as changing all shades to zero (assuming that there were no shades darker than 50). To change back to the original, use the inverse function of what you used to in the first place (eg "elemx + 50"). -I have tried to change the values of some elements for sprites in the hopes of being able to make sprites which are moving when DUKE3D first starts the level. I don't know if it is possible. If it is, I sure would like to know about it! HAPPY HACKING !!! =======================================================================