Contents Back Forward |
10. Map Thread | |||||||||||||||||||||||||||||||
10.1 Map Thread overview The Map Thread is the thread which manages map directly in Tot memory. Development has been very difficult for a couple of reasons, so, i must say i'm not totally satisfied about how it works, so you should use this thread (at least until problems with this thread are solved) only if it's strictly necessary. Anyway, if properly configured, Map Thread doesn't scan any byte in memory, it just call continuously MapCheck, it's up to the designer to check tiles in which he's interested. A VERY IMPORTANT thing to note is that i was not able to find other map blocks apart from map0 blocks, this means that, actually, in multimap games you'll have access only to map1 tiles informations. The exact source-code of Map Thread is the following, as you can see, incredibly silly:
while(true)//Start a continuous cycle
| 10.2 Configuring MapThread The big problem with MapThread is that, differently for what happens with other structures, the map structure changes its position in memory each time the scenario is loaded. Probably this is caused by some unknown memory structure which precede the map thread anyway, the result is that CSPL don't know, when a SCN is loaded, where it's map structure will be placed. My solution to the problem was to let CSPL search dynamically for Map structure; This means two things: 1) CSPL needs to know how the SCN map should resemble 2) CSPL needs to know where to look (approximately) This means that we should tell CSPL when a candidate map is really our map (comparing the whole map is unfeasible in human times) For the second point i've found that the map block starts in memory somewhere in [0x1a5000,0x1a50000+0x6000] (memory intervals). I don't know if this is valid for each scenario a player could design but i choose it very large so it's probable that it will works with all scenarios. Anyway if you find that your CSPL program cannot find the map section in memory try to enlarge scanning area changing START_ADDRESS and DIM_ADDRESS variables defined in CSPLclient.h, obviously START_ADDRESS is the address where CSPL starts to scan (0x1a50000 by default) while DIM_ADDRESS (name quite strange but i don't want to change it...) represents the width of the area to scan (0x60000 by default). For the first point i've find that usually the first 12 bytes of Map block are enough to identify the Map block in Tot memory. Again, if you find that you need more bytes to match the map block in memory with the map block in SCN, you can change value of DimPattern variable which is defined in CSPLClient.h with a default value of 12. This leads us to another problem, CSPL needs to know where to find our SCN file, so it can read 12 bytes from the map block in SCN. The designer who wants to use MapThread functions should change SCNFileName variable in CSPLClient.h so that it contains path to scn file (since scenarios will be shipped on a lot of different computers i suggest you to use relative paths, so ".\\example.scn" means just that example.scn is in the same directory of CSPL executable). NOTICE: This also means that if you change something about the first 12 bytes of map block CSPL will not found the map section in memory because the first 12 bytes in memory won't match with infos extracted by SCN file, anyway, since the first 12 bytes of mapblock regards a couple of useless north pole terrain tiles, you should never face this problems This seems extremely hard and how i said above i'm not happy of this solution, but it's more easy to use than one can think: usually the designer leaves START_ADDRESS, DIM_ADDRESS and DimPattern to their default values and just changes SCNFileName to relative path of his SCN file and that's all, the designer can start to use MapThread functions since CSPL will open automatically scn, will read map block, will scan Tot memory searching for a match and will start MapThread without annoying more the designer with techy details. |
10.3 Global functions In CSPL i've defined several functions to manage map tiles:
Plus a couple of constants used to define no resource or river presence (Look at A.H. Doc about HexEditing):
DWORD OffsetMap(int x, int y) This function is implemented mainly for internal library use: Since Map is implemented in Civ as a 1-D data set, while map is 2D, this function gets the coordinates of a particular tile and returns the Civ2 memory offset to tile passed as parameter. byte GetTerrain(int x,int y) This function returns the terrain type of tile located at x,y (passed as parameter). The value returned is the same of the previous Terrain types table. void GetTile(Tile* Casella,int x,int y) This function is similar to the previous one, but, instead of returning only the terrain type of tile, this function returns whole informations about tile located at x,y. void SetTile(Tile* Casella,int x,int y) This function is the opposite of the previous one. It takes a pre-filled Tile structure and writes it in memory, overwriting old tile located at x,y. Usually this function is used with the previous one in the following way:
void Get9Tiles(TilesBox* Box,int x, int y) This is a particular function, it is used to take infos about all tiles adjacent to a particular tile. It should be used to extract info about tiles adjacent to a particular unit or a particular city:
|
10.3 Example 9 : GeoBuilder Find an example about this particular thread is a bit difficult there aren't many situations in which controlling the tile info is useful. This is, by the way, a very lucky thing since this thread is the most complex one and suffer of heavy limitations (explained above in 10.1 and 10.2) such as Map0 only, needs SCN file to be decided at compile-time and other things. Anyway, i can think about two interesting uses for this thread: 1) Miner Unit: Imagine to have a cspl program which, sometimes, changes a tile (obviously if it is not occupied by a city) in a special terrain type, Mine, which was not used on map start (or were used scarcely); imagine also a special unit, Miner, and a CSPL coded event of this type: "IF Miner is on Mine THEN Send nr Money/Shields/Food to Miner HomeCity", Obviously in this way, controlling Mines with Miners is vital for each player (Ok, it seems great but i think it will hard to "learn" this to Tot standard AI). You'll need to know which turn is to avoid loops: Miners should send resources to HomeCity only once for turn. 2)Atlantis Isle: This requires a pre-drawn map. It consist in taking a piece of map where an isle lies (or where ocean lies) and, when a specific turn comes, isle will be replaced by water (or viceversa); using also CreateCity and SetCivsInPlay functions CSPL designer should be able to add even an entire new civ living on the isle when it will surface. In this example we will try to implement something similar to event 2, only we will try to simply "write" on with mountain/ocean the text "CSPL" on turn 10 (pretty useless but without a premade map i can't raise an isle, i don't know even where the ocean is) PHASE 0: CREATING A NEW PROJECTAs we've learned in the previous chapters the first step towards CSPL compilation is the project creation (usually done with CSPLCompanion);again create a new project called GeoBuilder. PHASE 1: UNDERSTANDING WHAT WE NEEDThe first thing a CSPL designer should think is : "which thread i need?"In this situation, since we just want to play with map, so we need Map Thread. PHASE 2: DESIGNING THE EVENTThe next thing we have to do is to design the "skeleton" of our event:From the first chapter we know that event is made of HEAD (Trigger Statement) and BODY (Action Statement): In this case HEAD is "Turn is 10" while BODY is "Write CSPL changing ocean tiles in mountains and terrain tiles in ocean." PHASE 3: FIXING UP SECONDARY PROBLEMSHow to write CSPL on screen?Well, let's say that a letter will be 5x5 tiles while characters will be separated by 2 tiles: C S P L will be done by :
Notice: I used coords 0,2,4, etc... because of the strange way of code coordinates used by Tot (probably due to isometric reasons). So, if, for example, we are in tile 0,0 , the tile on the right will be 0,2 while 0,1 is a non-existant tile. It's very important to notice here that coordinates you've use with GetTile, SetTile and other Map functions are Tot coordinates, and so it's an error to call SetTile(...,0,1) because, as i said, Tile at coordinate 0,1 is not existant!Ok, now we know how to design characters we need, let's move to coding phase PHASE 4: CODING THE EVENTOf course it's better to create a separate function to do our write-stuffWe will have void WriteIt(); it should start write form let's say, x = 4, y = 4 our 'CSPL' Let's start writing the 'C' char, we will use function GetTile to grab x,y Tile and if it contains land we will substitute it with an ocean square and writes it back with SetTile, while if it contains ocean we will substitute it with mountains.
Ok, i think it's easy to understand what i've done: i've used two nested for cycle to scan all 5x5 tiles square and only when i was on a square to be changed i called ChangeTile function. This means also we need to define another function ChangeTile which takes as parameter a Tile, change it to desert, and rewrite it back to memory. It's very easy to implement:
and we've finished for ChangeTile.Now it's time to write 'S' char:
Now it's time to write 'P' char:
{//Write the 'P' char starting from coordinates Offsetx,Offsety Now it's time to write 'L' char:
And we've finished writing chars. Now let's see the main skeleton of WriteIt function:
void WriteIt()
Let's see the MapCheck(): (we need a global variable called Flag initialized to true to avoid loops on turn 10)
void MapCheck()
And we've finished with MapCheck. PHASE 5: MERGING THE RESULTING SOURCE CODENow it's time to merge all source code we've written:Editing CSPLClient.h: In CSPLClient.h we need to activate the Map thread and to define the Global variable Flag: BYTE ACTIVITY_FLAG=ACTIVATE_MAP; Then we've to define auxiliary functions we want to use: void WriteIt(); Now there is a very important thing still to do: configuring MapThread. As we saw in 10.2 we need to check variable values in the first two sections of CSPLClient.h; We know that default values are ok for the majority of scenarios and so let standard values for START_ADDRESS,DIM_ADDRESS,DimPattern; Let's look at SCNFilename, standard value is ".\\example.scn", this means that CSPL will try to extract map info from example.scn file in the same directory of CSPLClient program. In a real scenario, we probably will need to change this name to scenario SCN file name but, since we're just testing features of CSPL we will let example.scn as name and we will create a "example.scn" file later, during testing. So we don't have to change anyone of these values from their default, we've finished with CSPLClient.h. Editing CSPLClient.csp: Here we've to modify MapCheck function:
Then we've to define two aux. functions:
void ChangeTile(Tile* tile,int x, int y) Ok, we've finished, let's compile it! PHASE 6: COMPILING AND LINKING THE SOURCE CODEAt this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link GeoBuilder; you should obtain a CSPLClient executable in GeoBuilder directory.To test this example you should create a game, SAVE IT AS example.scn scenario, start CSPLClient.exe and wait 'till the tenth turn, at that point map should change to show our little sign. |