Creating Automaps: Difference between revisions

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


Line 55: Line 55:
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.
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.


== Things to do in DarkRadiant ==
== The entity Definition ==


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:
    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"
    }


- inv_name: the name to be shown to the player
The important parts here are the ''gui'' spawnarg, letting it use the before mentioned gui, and the ''scriptobject'' spawnarg.


- inv_map_start: set this to "1" if the player should start with the map in his inventory
== The script object ==


- inv_category: set to "#str_02390" (important), this allows the map to become visible via the map key (m by default)
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]


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:
 
- 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"
 
- 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
 
- 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
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.


- in the beginning of the ''mark'' method add the line ''if (!mapDraw) return;''
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.


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)
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.
 
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)