Creating Automaps: Difference between revisions

From The DarkMod Wiki
Jump to navigationJump to search
m (→‎Additions: fixed link to Doom 3 iddevnet gui's page)
 
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
[[Category:Editing]]
== Introduction ==
== Introduction ==


Line 10: Line 10:
== Preparations ==
== Preparations ==


The first step is to actually draw your map. How you do this is your business. After you hve done so you should cut out all the seperate rooms you want to appear on the map later on. Make sure to make the background transparent (using gimp for example). If you do so you should have
The first step is to actually draw your map. How you do this is your business. After you have done so you should cut out all the seperate rooms you want to appear on the map later on. Make sure to make the background transparent (using gimp for example). If you do so you should have


- one picture containing the parts of the map the player already know at mission start (called ''startmap'' furthermore)
- one picture containing the parts of the map the player already know at mission start (called ''startmap'' furthermore)
Line 16: Line 16:


It is your choice if you want to put the ''startmap'' on the ''map_of_.tga'' file served with the '''startmap''' package or keep it seperate. However, for the tutorial let's just assume you have choosen the first possibility.
It is your choice if you want to put the ''startmap'' on the ''map_of_.tga'' file served with the '''startmap''' package or keep it seperate. However, for the tutorial let's just assume you have choosen the first possibility.
Furthermore you should get familiar with the zone system as it is needed here.


== Setting up the gui ==
== Setting up the gui ==


For every picture you now need a gui file set up. The content should look like that
The gui looks like this:


   windowDef Desktop
   windowDef Desktop
Line 29: Line 31:
         {
         {
                 rect      64,48, 512, 394
                 rect      64,48, 512, 394
                 background "''path to image''"
                 background "guis/automap/map_of"
                 visible 1
                 visible 1
         }
         }
        ...
         // Required include for inventory map
         // Required include for inventory map
         #include "guis/readables/inventory_map.guicode"
         #include "guis/readables/inventory_map.guicode"
   }
   }


The last two lines only goes to the file with the ''startmap''. Some notes on that. The only thing you need to adjust in the following are the arguments inside the ''background_map'' block going from one bracket to the next one. These are
In our case ''guis/automap/map_of'' is the file that holds the startmap. Where the dots are you have to add the following code for '''each''' area visible on the map.
    windowDef ''nameOfArea''
    {
        rect 100,100,380,300
        background "''path_to_imagefile''"
        visible "gui::gui_XYZ"
    }


- rect: the first two numbers are the position to place the image's upper left corner, the second two are the dimension
- ''nameOFArea'' can be choosen as you like, but each name must be unique and it makes sense to make it recogniseable
- background: contains the path to your image file
- visible: leave this at "1"


If you want to add text via the gui files add a new block that looks like that
- ''path_to_imagefile'' holds the path to the image to be drawn. Where nothing gets drawn the image should be transparent


  windowDef FMText
- the gui variable gui::gui_XYZ determines whether the image is visible or not. XYZ is the name of the corresponding location entity. You can easely get typos, so if something isn't working this is the first place to check
  {
    rect        200,120,100,100
    text        "Room A"
    textscale    0.5   
    font        "fonts/mac_humaine"
    textalign 1
    forecolor    0.0,0.0,0.0,1.0
  }


- textscale: adjusts the size of the text
Note that all pictures are using the same area of the gui. This easens the setup, but must be taken into account when creating the images for the map.
- font: the font the text should use
- textalign: 0=left, 1=center, 2=right
- forecolor: The color of the text (RGB) and the opacity (all values range from 0 to 1)


For the pictures of the rooms you want to be added later on you have to make sure that the ''rect'' values are set correctly. This may take some time. Note that the cordinate system used starts in the upper left corner. '''x''' (the first value) goes to the right and '''y''' (the second value''') goes to the left. Later on we will see how we can check the result in game.
== The entity Definition ==


  Note: The alignment of the map peaces is the part of the setup that takes the highest amount of time. The rest goes faster.
    entityDef atdm:automap
    {
        "inherit" "atdm:map_base"
        "editor_usage" "game map of terrain for player" // THIS ONLY SHOWS IN ENTITY INSPECTOR
        "inv_name" "Map" // SHOWS IN PLAYER'S INVENTORY.
        "inv_icon" "guis/assets/game_maps/map_of_icon.tga"
        "gui" "guis/automap/automap.gui"
        "scriptobject" "inventory_automap"
    }


== Things to do in DarkRadiant ==
The important parts here are the ''gui'' spawnarg, letting it use the before mentioned gui, and the ''scriptobject'' spawnarg.


This is the easiest part. In DR create an ''custom_static_item'' entity (darkmod/items/custom). Set the model to whatever you like, but if you plan it to be found later in the game you may choose a scroll or similar. Than set the following spawnargs:
== The script object ==


- inv_name: the name to be shown to the player
The scriptobject as well as an example setup can be found [http://forums.thedarkmod.com/topic/14394-apples-and-peaches-obsttortes-mapping-and-scripting-thread/?p=317949, here]
- inv_map_start: set this to "1" if the player should start with the map in his inventory
- inv_category: set to "#str_02390" (important), this allows the map to become visible via the map key (m by default)


I will refer to this entity as ''map'' entity furthermore. Now create a ''target_callscriptfunction'' entity (darkmod/targets) and give it a suitable name. Set the spawnarg "call" "map1ShowHide" on it and target it from the just created ''map'' entity.  
The main difference to a ''normal'' map is the updateLoop, which gets executed every few milliseconds.


If the player 'uses' the map (by pressing the map key for example) the function ''map1ShowHide'' will be executed.
    void inventory_automap::updateLoop()
 
    {
Redo this for every map and set "call" "mapXShowHide" where '''X''' is the map number.
        string loc;
 
        while(1)
The next thing needed to get the automap to work is to set up zones in your map. Every room that has it's own picture on the map must be represented by an own zone in you mission. Give every zone a suitable name, we will need those later on. Now set the spawnarg "call_on_entry" "mark" on them. This is the function that gets called whenever the player enters the specific zone.
         {
 
            loc=$player1.getLocation().getName();
  Note: Functions called via the target_callscriptfunction entity take the form '''void''' ''functionname''() while functions called
            loc="gui_"+loc;
         by zones take the form '''void''' ''functionname''('''entity''' ''zone'')
            setKey(loc,1);
 
            sys.wait(0.1);
== The script file ==
        }
 
Create a scriptfile called ''mymapname''.script. Once we are finished it should lool like this
 
  float roomName1,...;    // stores if the player has been there (needed for '''every''' room)
  float h_roomName1,...;  // stores the handle to the room guis (see below, needed for '''every''' room)
  float h_map1,...;       // stores the handles to the ''startmaps'' (needed for '''every''' map)
  float map1Shown,...;   // if the map is currently shown (needed for '''every''' map)
 
  void mapXShowHide() {  // shows/hides the map (needed for '''every''' map, '''X''' is the map number)
    if (mapXShown) {      // player hides the map
      mapXShown=0;
      $player.destroyOverlay(h_mapX);
      if (roomName1) $player1.destroyOverlay(h_roomName1); //destroys the gui overlay for the ''startmap''
      ...                // destroys the gui overlays for '''all''' shown rooms (repeat this line for '''every''' room om map X)
      return;            // that's it, the map is hidden
     }
     }
    sh=1;                // player shows the map
    h_mapX=$player1.createOverlay("''path to startmap.gui''",100);          // show ''startmap''
    if (roomName1) h_roomName1=$player.createOverlay("''path to gui''",101); // show room if player was there, do this for '''every''' room on map '''X''' while continuing the numbering (102,103,...)
    ...
  }
 
  void mark(entity zone) { //mark the area the player just entered as visited (you can use one function for all map or seperate them, your choice)
    string name;
    name=zone.getKey("name"); // the name of the zone
    if (name=="roomName1") {  // markes the room as visited if the player is there (needed for '''every''' room
      roomName1=1;
      return;
    }                       
    ...
  }
 
  void main() {
    roomName1=0; //do this for '''every''' room
  }


That's it. If you do this for large missions with many rooms the code will get quite long, but the single steps should be easy to understand. '''roomName1''' and so one are just placeholders. Choose the name of the corrosponding zones in you mission.  
As you can see it is pretty short. The only thing it does is retrieving the name of the location the player is currently in and pass this to the gui. Than it waits for 100 ms.


If you use several maps it may be a good idea to put the map number into the zone names, for example "''m1_tavern''". It may also be a good idea to split the guis and images into several folders, one for each map. This is your choice.
Example: The player is in a location which contains an info_location entity named ''kitchen''. This value gets read out and passed to the variable '''loc'''. Than the ''gui_'' string is added on front, so '''loc''' is now ''gui_kitchen''. (Remember the gui variable earlier on?) This value gets than set to 1. This will not have an effect if the value is already set to 1, but it doesn't hurt either.


== Conclusion ==
== Conclusion ==


It is quite simple to setup a fully functional automap. The main effort for the mapper is to actually draw the map and align the pieces later one. The script is easy but gets long. This is caused by the lack of suitable containers in the doom3 scripting language.
It is quite simple to setup a fully functional automap. The main effort for the mapper is to actually draw the map.


== Additions ==
== Additions ==


If you want to test the alignment in game, just change the lines in the ''main'' method in the script file. Set all the values to "1".
It is possible to extent the functionality of the automap. This will however require changes to the scriptobject.


If you want to have an automap that marks where the player currently is, the following changes need to be done:
You can setup Objectives where the player has to explore a certain percentage of the map (like in Mission #13 in Thief2: The Metal Age). This requires to set the percentage values you want each location to have on the respective location entities. You can than read them out in the update loop. To avoid the same values to be added up several times you would also need to keep track of which locations have already been counted. A global scripting variable keeps track of the percentage already discovered by the player and sets the respective mission objective to successful once it reaches the desired amount.


- The ''mark'' method is called via "call_on_exit" by the zones. "call_on_entry" calls a similar function that sets the value for the room to "2"
You can have maps that mark areas where you already have been and were you currently are. This requires altering the gui, too, as you have to specify the colors to be used for hilighting depending on whether the player is still in an area or not.
- The show method checks for the values to be "1" or "2" separately. For "2" a highlighted version is shown on the map
- Obviously you have to make these highlighted version first


If you want the map to be fond in the mission you have to do the following to avoid the rooms the player was before finding the map to be drawn on it
Similar to the images you can have text added to the map, like little notes that become visible once a condition is met (the player reads a note or similar). See [https://iddevnet.dhewm3.org/doom3/guis.html here] for more info on how to setup the gui.
 
- create a hidden, non-mandatory objective "if player possesses item". The ''item'' is the map.
- fill the '''success_script''' field with drawMap
- in the script file, add a ''float'' '''mapDraw''' at the beginning of the file
- set this to zero in the ''main'' method
- in the beginning of the ''mark'' method add the line ''if (!mapDraw) return;''
 
Obviously you will need to do this several times if the player can find several maps. If so, be sure to make different ''mark'' methods for every map to be found (thus meaning entities in the map file)
 
You can use this setup as a starting point for setting up objectives like "investigate 50% of the area".


Questions, replies and advices of any kind can go [http://forums.thedarkmod.com/topic/14394-apples-and-peaches-obsttortes-mapping-and-scripting-thread/ here]
Questions, replies and advices of any kind can go [http://forums.thedarkmod.com/topic/14394-apples-and-peaches-obsttortes-mapping-and-scripting-thread/ here]


--[[User:Obsttorte|Obsttorte]] ([[User talk:Obsttorte|talk]]) 06:33, 25 January 2013 (EST)
--[[User:Obsttorte|Obsttorte]] ([[User talk:Obsttorte|talk]]) 06:33, 25 January 2013 (EST)
{{GUI|sort=Automaps}}

Latest revision as of 10:23, 4 March 2024

Introduction

Normally the player is given a map at mission start, which is showing him the location he is attempting to sneak in. However, it may not seem very realistic that the player already possesses such detailed information without ever been in the specific place. An automap, thus meaning a map that gets drawn while the player proceeds in his mission may seem more realistic.

On the other hand such thing can also add a lot to immersion. The player don't know what is expecting him in the beginning, but benefits from the advantages a map brings to him while he proceeds. In addition an automap provides information about where the player already have been and therefore can avoid frustrating moments when he is searching for one little room he may have overseen.

The following article now describes how to set up such an item.

Preparations

The first step is to actually draw your map. How you do this is your business. After you have done so you should cut out all the seperate rooms you want to appear on the map later on. Make sure to make the background transparent (using gimp for example). If you do so you should have

- one picture containing the parts of the map the player already know at mission start (called startmap furthermore) - several pictures of the rooms he can discover later on

It is your choice if you want to put the startmap on the map_of_.tga file served with the startmap package or keep it seperate. However, for the tutorial let's just assume you have choosen the first possibility.

Furthermore you should get familiar with the zone system as it is needed here.

Setting up the gui

The gui looks like this:

 windowDef Desktop
 {
   	rect      0,0,640,480
   	nocursor 1
        
       windowDef background_map
       {
               rect      64,48, 512, 394
               background "guis/automap/map_of"
               visible 1
       }
       ...
       // Required include for inventory map
       #include "guis/readables/inventory_map.guicode"	
 }

In our case guis/automap/map_of is the file that holds the startmap. Where the dots are you have to add the following code for each area visible on the map.

   windowDef nameOfArea
   {
        rect 100,100,380,300
        background "path_to_imagefile"
        visible "gui::gui_XYZ"
   }

- nameOFArea can be choosen as you like, but each name must be unique and it makes sense to make it recogniseable

- path_to_imagefile holds the path to the image to be drawn. Where nothing gets drawn the image should be transparent

- the gui variable gui::gui_XYZ determines whether the image is visible or not. XYZ is the name of the corresponding location entity. You can easely get typos, so if something isn't working this is the first place to check

Note that all pictures are using the same area of the gui. This easens the setup, but must be taken into account when creating the images for the map.

The entity Definition

   entityDef atdm:automap
   {
       "inherit"			"atdm:map_base"
       "editor_usage"		"game map of terrain for player" // THIS ONLY SHOWS IN ENTITY INSPECTOR
       "inv_name"			"Map" // SHOWS IN PLAYER'S INVENTORY.
       "inv_icon"			"guis/assets/game_maps/map_of_icon.tga"
       "gui"				"guis/automap/automap.gui"
       "scriptobject"	"inventory_automap"
   }

The important parts here are the gui spawnarg, letting it use the before mentioned gui, and the scriptobject spawnarg.

The script object

The scriptobject as well as an example setup can be found here

The main difference to a normal map is the updateLoop, which gets executed every few milliseconds.

   void inventory_automap::updateLoop()
   {
       string loc;
       while(1)
       {
           loc=$player1.getLocation().getName();
           loc="gui_"+loc;
           setKey(loc,1);
           sys.wait(0.1);
       }
   }

As you can see it is pretty short. The only thing it does is retrieving the name of the location the player is currently in and pass this to the gui. Than it waits for 100 ms.

Example: The player is in a location which contains an info_location entity named kitchen. This value gets read out and passed to the variable loc. Than the gui_ string is added on front, so loc is now gui_kitchen. (Remember the gui variable earlier on?) This value gets than set to 1. This will not have an effect if the value is already set to 1, but it doesn't hurt either.

Conclusion

It is quite simple to setup a fully functional automap. The main effort for the mapper is to actually draw the map.

Additions

It is possible to extent the functionality of the automap. This will however require changes to the scriptobject.

You can setup Objectives where the player has to explore a certain percentage of the map (like in Mission #13 in Thief2: The Metal Age). This requires to set the percentage values you want each location to have on the respective location entities. You can than read them out in the update loop. To avoid the same values to be added up several times you would also need to keep track of which locations have already been counted. A global scripting variable keeps track of the percentage already discovered by the player and sets the respective mission objective to successful once it reaches the desired amount.

You can have maps that mark areas where you already have been and were you currently are. This requires altering the gui, too, as you have to specify the colors to be used for hilighting depending on whether the player is still in an area or not.

Similar to the images you can have text added to the map, like little notes that become visible once a condition is met (the player reads a note or similar). See here for more info on how to setup the gui.

Questions, replies and advices of any kind can go here

--Obsttorte (talk) 06:33, 25 January 2013 (EST)