[Tutorial] Adding Custom Views / Windows

Having digged a bit into the details of how the journal was ment wo work, I have gathered some information on how to add a custom view into Stonehearth (and link it to a command and an entity).

As I have just started to understand how the things are working together and for the sake of having this designed as an entry tutorial, the view we create will not be too complicated and only include one additional action, i.e. to close the window.

This time we will have to change more files than in the other tutorials so far… but there is nothing too complicated.

  • Add a command in “…\Stonehearth\mods\stonehearth\data\commands” :one:
  • Add a User Interface in “…\Stonehearth\mods\stonehearth\ui\game” :two:
  • Add entries to the Manifest “…\Stonehearth\mods\stonehearth\manifest.json” :three:
  • Add command to entity(-ies) “…\Stonehearth\mods\stonehearth\entities” :four:

So, let’s get our hands dirty and into it…

:one: ** Create a new command**

You can see a command as the small icons which are visible once you have selected an entity in the world (e.g. a worker or a crafted item). In the folder “…\Stonehearth\mods\stonehearth\data\commands” you will find the available commands and for this example we want to create a new one. Just copy & paste the “show_journal”-folder and rename it to something like “show_info”. In this folder you will find 2 files, one picture and a .json-file. Rename also both of this files and rename 3 entries in the .json-file from “show_journal” to “show_info”.

The .json-file simply defines the name of the command and the action which is called once the user clicks on the icon. Nothing too complicated so far.

:two: ** Define the view (User Interface)**

The folder “…\Stonehearth\mods\stonehearth\ui\game” includes different elements which you will see as a User Interface in the game. We want to add here details about the look and functions of our custom view.

Go ahead and copy & paste the folder “show_journal” (which should also exist already in the game-folder) and rename it to “show_info”. Inside the folder you will find another folder, which includes images. We want to add another image here which will be used to close the view. Just copy & paste the following into the image-folder:

“…\Stonehearth\mods\stonehearth\ui\game\show_workshop\images\close_button.png”

Now there are three files in the “show_info”-folder:

  • “show_info.less” (after you have renamed it)… is a css-file and contains formating information.
  • “show_info.html” (after you have renamed it)… where you can also embed handlebars expressions (actions).
  • “show_info:js” (after you have renamed it)… which contains the “logic”.

If you have copy & pasted the “show_journal”-folder, you can adjust the css-file a bit to look like this:

#infoWindow {
   position: absolute;
   top: 100px;
   left: 100px;
   width: 424px;
   height: 589px;
   background: url('images/recipe_scroll.png');
}

#infoTitle {
   margin-top: 30px;
   margin-left: 80px;
   margin-right: 80px;
   text-align: center;

   .controlFont;
   font-size: 18px;
}

#infoEntries {
   position: absolute;
   width: 350px;
   height: 460px;
   overflow: auto;
   margin: 30px;

   font-size: 14px;
}

#closeButton {
   position: absolute;
   top: 30px;
   right: 50px;
   width: 25px;
   height: 27px;
   display: block;
   background: url('./images/close_button.png');
}

As this is css, we are defining some design elements (window, title, etc.) and their size, location, etc.

Next you want to adjust the .html-file to look more like this:

<script type="text/x-handlebars" data-template-name="stonehearthInfo">
   <div id="infoWindow">
      <a id="closeButton" {{action hide target="view"}}></a>
      <div id="infoTitle">Custom Text</div>
      <div id="infoEntries">
		 Dog goes woof<br>
  		 Cat goes meow<br>
		 Bird goes tweet<br>
		 and mouse goes squeek<p>

		 Cow goes moo<br>
		 Frog goes croak<br>
		 and the elephant goes toot<p>

		 Ducks say quack<br>
		 and fish go blub<br>
	 	 and the seal goes ow ow ow ow ow<p>

		 But theres one sound<br>
		 That no one knows<br>
		 What does the fox say?
      </div>
   </div>
</script>

You see that we are using the names for our design elements as defined in the .css-file. Other than that you will only see one “strange” entry which is:

  <a id="closeButton" {{action hide target="view"}}></a>

That is actually where we are embedding an expression from handlebars. We are defining a button which is linked to an action (to hide the view). Apart from that, plain html.

Finally we have the .js-file which we want to look like this:

$(document).ready(function(){
    $(top).on('show_info.stonehearth', function (_, e) {
      var view = App.gameView.addView(App.StonehearthInfoView, { uri: e.entity });
    });
});

App.StonehearthInfoView = App.View.extend({
    templateName: 'stonehearthInfo',
    modal: true,

    actions: {
        hide: function() {
            var self = this;
            self.destroy()
        }
    }
});

This is a quite short-version of the .js-file if you compare it to others which you can find in the game-folder. It only adds a view to the game and links it to an action (closing the window).

:three: ** Introducing UI to manifest.json**

At the bottom of the file “”…\Stonehearth\mods\stonehearth\manifest.json"", you will find some entries linked to the UI. Actually you will find one section for the “html”, one for the “js” and one for the “less”-files. Just add the link to your files:

  • “file(ui/game/show_info/show_info.html)”,
  • “file(ui/game/show_info/show_info.js)”,
  • “file(ui/game/show_info/show_info.less)”,

If you are not sure where to add them, look for “show_journal” and add your links underneath the different entries you will find.

:four: ** Link command to entity**

We need to link our command to an entity which will allow us to trigger the related action (showing the view). Entities you will find in the folder “…\Stonehearth\mods\stonehearth\entities” and they are actually everything you can see in the world (more or less). So you will find here the craftable items, animals, monsters, workers, etc.

Not all entities are eligible to include all commands. E.g. the journal requires some data about the entity which is calling it (e.g. personality). Furniture might not have “personality” (let’s not discuss this now :wink:), so this command will not work for furniture. Just play around a bit and try adding commands to different things (pay attention with the “destroy_entity”-command, it DOES work with workers :stuck_out_tongue_closed_eyes:.

For this example I have adjusted the “Camp Standard” and opened the “camp_standard.json” which you can find here: “…\Stonehearth\mods\stonehearth\entities\gizmos\camp_standard”. This entity will not have any commands yet included. Just go ahead and add the following code somewhere inside the “components”-brackets:

  "stonehearth:commands" : {
     "commands" : [
        "file(/data/commands/show_info)"
     ]
  },

If you restart the game now, you shoudl see an additional icon once you click on the Camp Standard. Once activated, a new view should become visible which you can either close by clicking again on the item, or using the cross on the top right.

The video (19 Min.) might clarify some open questions:

10 Likes

seriously, you need to pace yourself… you and @RepeatPan are going to sprain something cranking out all this material… :smile:

overall, this is brilliant, and really enables others to jump into the mix… :+1:

i have one question about the quoted text above… are you using existing code as a reference? im curious about the tags… specifically why we arent seeing <br/> and a closing </p> to compliment the <p>… or, rather… seeing and opening <p>, in front of “Dog”, with the closing </p> after “squeek”…

trivial, i suppose… but still curious…

Not re-using any code for that, just added some plain text with line breaks and paragraphs to show something. I think it is quite common to not close those two tags as it is optional in HTML to close them.

true, HTML is very forgiving… :smile: but my OCD really wants to write this as:

1 Like

To avoid that the :ambulance: needs to take care of you, just go ahead and write it like that… I hereby approve it :wink:.

… and now I go and punish myself. Just a little.

1 Like

Well done! :slight_smile: Very clear and simple and accurate. :slight_smile: :slight_smile: :slight_smile:

Just checking–are you sure this isn’t .html, not xml?

Some more context…

Stonehearth app views are descended from Emberjs views. We’re using their data binding infrastructure (and not much else). Data is bound from an ember view to the html template using handlebars basically by adding data to the view’s context variable (as opposed to the controller), and then accessing it from the html template.

For example, let’s say you wanted to add a simple variable text string to the screen you put up, like:
Name: VoxelPirate.

Except you’d like to have VoxelPirate be changeable from the javascript layer, so you can put in whatever name you like (eventually drawn from the game state in Lua). What you can do is add this to your .js file:

   didInsertElement: function() {  //part of Ember view, called on template change
      this._super();                      //call the fn I inherit from
     if (this.get('context')) {           //if context exists yet
        this.set('context.myname', 'VoxelPirate'); //set name to VoxelPirate
     }
   }

Then later, in your html:

<span>Name: {{myname}}</span>

Whenever you change the variable myname in javascript, the UI should change too.

There is one more trick, and you’ll know almost all there is to know about creating views in the UI. When you create an AppView with the uri field sent to the uri of an entity (like, an object in the world) its context is pre-seeded with that object. You can get real time, live-updating data about that object by listing what components on the object you care about in the components field. For example, if you look inside show_workshop, you’ll see something like this:

On getting the show workshop event, create a show_workshop view with the uri of the entity of the workshop:

$(top).on("radiant_show_workshop", function (_, e) {
      var view = App.gameView.addView(App.StonehearthCrafterView, { uri: e.entity });
});

Then, in the view itself, there’s this code:

   components: {   //To the view's context var, get real-time
                          //updates on the following nested(!) components
      "unit_info": {},  
      "stonehearth:workshop": {
         "crafter": {
            "unit_info" : {},
            "stonehearth:crafter": {
               "craftable_recipes" : {
                  "recipes" : []
               }
            }
         },
         "order_list" : {
            "recipe" : {}
         }
      }
   },

You then access this data from html by just listening on those variables. If you look at show_workshop.html you’ll see:

<div id="title">{{unit_info.name}}</div>

This just prints the name field of the unit_info object that has been added to the view’s context.
You could also get it from javascript with:

this.get('context.unit_info')

If the unit info changes in lua, the JS and html will be automatically updated. To update the lua in turn, you’ll need to call a server side function. You can call any function declared in the manifest (as I wrote yesterday), or a function specifically on any component of the entity in question.

For example, to send the workshop order to the server, inside show_workshop, call:

   local workshop = this.get('context.stonehearth:workshop').__self;
   radiant.call_obj(workshop, 'add_order', recipe, condition);

This takes the workshop component and calls the ‘add_order’ function inside of it.

4 Likes

How could I contradict to a woman? Just fixed the “typo” :wink:.

And once more thanks a lot for the additional insights. Appreciate this quite a lot, really :smile:.

Wow, this is awesome information and still relevant! it just helped me when I was struggling with handling URI’s and UI components! :heart:

Anyone new to modding the UI should be pointed toward this first (even though the unit_info stuff is outdated). :smiley:

1 Like