Nick:  
Pass:     
Help Register


Post Reply 
Modding Sneakpeak level modding
Author Message
Rasmus Offline
Main developer of Dwelvers
Dwelvers Developers

Posts: 3.619
Joined: Sep 2013
Reputation: 502
Post: #1
Sneakpeak level modding
Sneakpeak level modding

One day left before release, and the tutorial is now completed. The last days I have been working on the level modding that came with this, and this is something I need to get of my chest, because I think this will be awesome once it is fully operational Big Grin

It is very operational at this point, but somewhat limited because right now the modding has been focused towards making the tutorial map, once we get more maps in there and requests from others it will expand more and more.

So this is how it works at this point:
All XML files located in the "media/maps" folder can be loaded from the game menu when clicking "start game".

I will not go into to much details on what variable changes what in the game, but rather explain the structure of how the level modding works.

For those of you that aren't used to XML here is short description of the language: http://en.wikipedia.org/wiki/XML (it is pretty straight forward and a lot like html, just that own defined elements can be set)

So, a very simple xml level file can look like this:
Code:
<?xml version="1.0" encoding="utf-8"?>
<map>
  <description>Sandbox, easy mode.</description>
  <allowcustommapseed/>
  <mapdata>
    <size>100, 100</size>
    <mapseed>0</mapseed>
    <autogencreatures enabled = "1"/>
    <autogenfishes enabled = "1"/>
    <autogenfullterrain enabled = "1"/>
    <player pos2D = "50, 50"/>
    <building type = "TrainingRoom" areapos3D = "22, 10, 21, 24, 10, 24"/>
  </mapdata>
</map>

Map configuration
  • <description> Is the map description that will show up in the game menu when choosing the level (not implemented at this moment.)
  • <allowcustommapseed> Means that the player is allowed to set his own specific level seed to be able to generate a specific map.
  • <mapdata> Here the user can set some initiation values for the map.

Getting started
Here is the code to set the victory conditions for the player:
Code:
<map>
  <init>
    <seteventid enable = "1">onwin</seteventid>
  </init>
   <event id = "onwin">
    <if>
      <state var = "%gamedata/level/dungeon/creatures.size%" conditions = "type == 'Knight' AND health > '0' AND playerid == '-1'" operator = "==">0</state>
    </if>
    <then>
      <setvar var = "gamedata/player0/win">1</setvar>
    </then>
  </event>
   <event id = "foo">
  </event>
</map>

This one looks a little more advanced, but let me go through it step by step.
  • <init> Anything defined here will be executed when the level loads.
  • <seteventid> Here the user sets if the an event should be enabled or not, this is defined by the "inner text", and the "enable" attribute.
  • <event> If an event is enabled then the code inside it will be executed one time each frame. All events should also have a id just like the one in the example has.

In the code above the eventid "onwin" gets enabled in the <init> element. This means that the code inside <event id = "onwin"> will be executed every frame. As you can see the <event id = "foo"> never gets enabled, therefore it will never be executed in this code.

If statements
So for each frame the following code will be executed in the XML file:
Code:
<if>
  <state var = "%gamedata/level/dungeon/creatures.size%" conditions = "type == 'Knight' AND health > '0' AND playerid == '-1'" operator = "==">0</state>
</if>
<then>
  <setvar var = "gamedata/player0/win">1</setvar>
</then>

Here the program will start by entering the <if> element. The <state> element inside it will decide weather or not this if statement is true or false.

A <state> element is structured like this:
<state var = "*leftoperand*" operator = "*operator*">*rightoperand*</state>

The operator can have five different values:
  • "==" Equals to (if the operator is not defined this is default)
  • ">" Higher than
  • "<" Lower than
  • ">=" Higher or equals to
  • "<=" Lower or equals to

So if the operator is equal to "==" then we will the if statement:
If *leftoperand* Equals to *rightoperand* then the if statement is true.

In the code above the right operand equal to "0", and the left operand is equal to the (gamedata/level/dungeon/creatures.size) number of creatures in the dungeon.

So if the number of creatures in the dungeon is "0" then we will get:
(0 == 0)
Which is true. But if we would have three creatures in the dungeon we would get.
(3 == 0)
Which is not true..

In the code above I have also included:
conditions = "type == 'Knight' AND health > '0' AND playerid == '-1'"

This means that the left operand will only count in creatures that are Knights, have a health above 0 and have the playerid -1 (don't belong to a player).

So if we have 10 creatures in the dungeon, but if there are only 2 of them that fits the conditions, then the left operand will be equal to 2.

The bottom line is that this if statement will not be true until there are zero enemy knights in the dungeon that are healthy.

If... or... then... else...
If this is true, then the program is allowed to execute what's inside the <then> statement.
Code:
<then>
  <setvar var = "gamedata/player0/win">1</setvar>
</then>

Here the program executes the <setvar>. I will get into what this means further down, but the important thing to understand here is that the program will not execute the code inside <then> until the if statement is true.

There are other elements that are also affected by the if statement:
Code:
<if>
  <state var = "%a%" operator = "==">1</state>
  <state var = "%b%" operator = "==">1</state>
</if>
<or>
  <state var = "%c%" operator = ">">5</state>
</or>
<then>
</then>
<else>
</else>

In this case if "a" is equal to 1 and "b" is equal to 1 then the if statement will be true and can progress down to <then>. If "a" is equal to 1 and "b" is not equal to 1, then the if statement will be false and don't progress down to <then>.

But if the if statement is false, then it gets a second chance in the <or> statement. If "c" is higher than 5, then the if statement will be treated as true and be able to progress down to <then>.

If both the <if> and the <or> statement turns out to be false, then the program will not execute the code inside <then> but instead execute the code inside <else> (if available).

It is possible to ignore the <then> element all together and only go for <else> if the user only wants to execute a certain code only if the <if> statement is false.

Is it a variable or a value?
You may have noticed that I have put these percent (%) signs next to some of the variables. This is to let the program know if it is a variable or a value.

For example, lets say that we have two different variables:
"a" and "b" which is both equal to 1.
If I write:
<state var = "a">b</state>
then it would be the same as (a is equal to b), but this is not true, because these are different letters that are not equal to each other.
If I instead write:
<state var = "%a%">%b%</state>
Then it would be the same as writing (1 is equal to 1), which is true.
Writing:
<state var = "%a%">b</state>
Would give (1 is equal to a), which is not true.

The reason we need to define them as variables or not is because of situations like this:
a = Knight
<state var = "a">Knight</state>
Which gives (a is equal to Knight), which is not true. But:
<state var = "%a%">Knight</state>
(Knight is equal to Knight) is true.

Setting variables
There are a couple of ways to modify the variables:
  1. <setvar var ="a">5</setvar>
  2. <setvar var ="b">3</setvar>
  3. <setvar var ="c">%a%</setvar>
  4. <setvar var ="d">%c%</setvar>
  5. <incvar var ="d">%b%</incvar>
  6. <decvar var ="d">8</decvar>
  7. <randvar var ="e">8</randvar>


I have labelled the lines with numbers to make this example easier to follow.
Just as the <state> the "var" value is the left operand and the inner text is the right operand.
  • <setvar> will make the left operand become equal to the right operand.
  • <incvar> will increase the left operand with the value of the right operand.
  • <decvar> will decrease the left operand with the value of the right operand.
  • <randvar> will set the left operand to a random value between 0 and the right operand - 1. So if the right operand is equal to 8 then the random value will be between 0 - 7.

So, now I will go through the numeric lines step by step:
  1. a = 5 (<setvar var ="a">5</setvar>)
  2. b = 3 (<setvar var ="b">3</setvar>)
  3. c = 5 (<setvar var ="c">%a%</setvar>)
  4. d = 5 <setvar var ="d">%c%</setvar>
  5. d = 5 + 3 = 8 <incvar var ="d">%b%</incvar>
  6. d = 8 - 8 = 0 <decvar var ="d">8</decvar>
  7. e = 3 (by random) <randvar var ="e">8</randvar>


This may not seem as useful at first glance, why would I want to modify values that has no direct impact on the game?

The reason is that <setvar> and all the other elements can affect in-game-variables as well, such as creatures level or health, building types, and much more on command.

A line like this can increase the health on all enemy creatures by 20:
<incvar var = "%gamedata/level/dungeon/creatures.health%" conditions = "playerid == '-1'">20</incvar>

This line will make all "Rock" buildings into "ClaimedWall" buildings inside the area (10, 10 - 20, 20).
<setvar var = "%gamedata/level/dungeon/buildings.type%" areaPos2D = "10, 10, 20, 20" conditions = "type == 'Rock'">ClaimedWall</setvar>

Then we can assign them all to the player like this:
<setvar var = "%gamedata/level/dungeon/buildings.playerid%" areaPos2D = "10, 10, 20, 20" conditions = "type == 'ClaimedWall'">0</setvar>

This is still being worked on, but as I already can fetch values from individual buildings and creatures it is just a small step towards being able to modify them.

Enable, disable and goto event id's
As I pointed out in the beginning <event> can be enabled and disabled with this code:
Enable: <seteventid enable = "1">onwin</seteventid>
Disable: <seteventid enable = "0">onwin</seteventid>

This can also be done inside other event id's like this:

Code:
<event id = "tutorial1">
  <if>
    <state var = "%gamedata/player0/tutorial/camerazoom%">1</state>
  </if>
  <then>
    <seteventid enable = "0">tutorial1</seteventid>
    <seteventid enable = "1">tutorial2</seteventid>
  </then>
</event>

<event id = "tutorial2">
  <if>
    <state var = "%gamedata/player0/tutorial/cameramove%">1</state>
  </if>
  <then>
    <seteventid enable = "0">tutorial2</seteventid>
    <seteventid enable = "1">tutorial3</seteventid>
  </then>
</event>

In this code when the camera has zoomed in and out, then <event id = "tutorial1"> will get disabled and <event id = "tutorial2"> will get enabled.
Then when the camera has moved around <event id = "tutorial2"> will get disabled and <event id = "tutorial3"> will get enabled.
This is perfect for when there are objectives the player must complete in a row.

In my case I also add a <gotoevent> when a objective is completed like this:

Code:
<event id = "tutorial1">
  <if>
    <state var = "%gamedata/player0/tutorial/camerazoom%">1</state>
  </if>
  <then>
    <seteventid enable = "0">tutorial1</seteventid>
    <seteventid enable = "1">tutorial2</seteventid>
    <gotoevent>objectivecompletedspeech</gotoevent>
  </then>
</event>

<event id = "tutorial2">
  <if>
    <state var = "%gamedata/player0/tutorial/cameramove%">1</state>
  </if>
  <then>
    <seteventid enable = "0">tutorial2</seteventid>
    <seteventid enable = "1">tutorial3</seteventid>
    <gotoevent>objectivecompletedspeech</gotoevent>
  </then>
</event>

This means that the code inside the <event id = "objectivecompletedspeech"> will get executed as well. I made it like this because sometimes there is code that needs to be written several times and look the same. By putting this code inside the <event id = "objectivecompletedspeech"> the user will only need to write the line:
<gotoevent>objectivecompletedspeech</gotoevent> to get the same code executed over and over again.

An example for this is when I want the dark mother to say something encouraging every time the player completes an objective, but that she shouldn't say the same phrase over and over again.

Code:
<event id = "objectivecompletedspeech">
  <randvar var = "dmrandvoice">5</randvar>
  <if>
    <state var = "%dmrandvoice%">0</state>
  </if>
  <then>
    <setvar var = "gamedata/player0/darkmother.talk">darkmother/dm_objcomplete1</setvar>
  </then>
  <else>
    <if>
      <state var = "%dmrandvoice%">1</state>
    </if>
    <then>
      <setvar var = "gamedata/player0/darkmother.talk">darkmother/dm_objcomplete2</setvar>
    </then>
    <else>
      <if>
        <state var = "%dmrandvoice%">2</state>
      </if>
      <then>
        <setvar var = "gamedata/player0/darkmother.talk">darkmother/dm_objcomplete3</setvar>
      </then>
      <else>
        <if>
          <state var = "%dmrandvoice%">3</state>
        </if>
        <then>
          <setvar var = "gamedata/player0/darkmother.talk">darkmother/dm_objcomplete4</setvar>
        </then>
        <else>
          <if>
            <state var = "%dmrandvoice%">4</state>
          </if>
          <then>
            <setvar var = "gamedata/player0/darkmother.talk">darkmother/dm_objcomplete5</setvar>
          </then>
        </else>
      </else>
    </else>
  </else>
</event>

This one almost made me want to enable arrays in the XML code Tongue
What it does is that it sets the variable "dmrandvoice" to a number between 0 and 4. And depending on the number this variable got she will say the phrase defined with this number.


There are still more to be said, but I will leave it like this for now, I hope I explained it well enough to be understood Smile
29-05-2014 07:37 PM
Find all posts by this user Quote this message in a reply
Xiemas Offline
Dark Side Member

Posts: 359
Joined: Dec 2013
Reputation: 13
Post: #2
RE:
Wow that look very nice! That's a +1 for you!
I have a question though. Is it possible to mod a world you played in? And if so can you reset the black smoke and is it possible to prevent you from braking blocks?

Nice man WOW Tongue
Pyramid of Live
[Image: kEbemdH.png]
29-05-2014 08:49 PM
Find all posts by this user Quote this message in a reply
Mello Tonin Offline
Guru O' Mello

Posts: 2.216
Joined: Aug 2013
Reputation: 176
Post: #3
RE:
It's gonna take a long time to get acclimated to all the changes, and I think this may be the best one yet! I'm torn now between this and focusing on Hydraulics...You know what this means?

I'm going to buy some really good coffee tomorrow, and a ton of brew!

Gonna toast one to you Rasman, and the rest of the crew! Maybe even hold ya to that promise we made a while back. Oh it's gonna be a rollercoaster ride this weekend!
30-05-2014 04:38 AM
Find all posts by this user Quote this message in a reply
Rasmus Offline
Main developer of Dwelvers
Dwelvers Developers

Posts: 3.619
Joined: Sep 2013
Reputation: 502
Post: #4
RE:
(29-05-2014 08:49 PM)Xiemas Wrote:  Wow that look very nice! That's a +1 for you!
I have a question though. Is it possible to mod a world you played in? And if so can you reset the black smoke and is it possible to prevent you from braking blocks?

Nice man WOW Tongue

The code in the XML file gets executed once every frame while playing, so if the user set a certain condition for when he wants the fog of war to be reset it is possible. The same with making blocks unbreakable.
If you are asking if it is possible to make this mod right now, then the answer is no. But I will create a topic with requested moddabilities and add them with every release Smile
(This post was last modified: 30-05-2014 04:55 AM by Rasmus.)
30-05-2014 04:52 AM
Find all posts by this user Quote this message in a reply
Rasmus Offline
Main developer of Dwelvers
Dwelvers Developers

Posts: 3.619
Joined: Sep 2013
Reputation: 502
Post: #5
RE: Sneakpeak level modding
(30-05-2014 04:38 AM)Mello Tonin Wrote:  It's gonna take a long time to get acclimated to all the changes, and I think this may be the best one yet! I'm torn now between this and focusing on Hydraulics...You know what this means?

I'm going to buy some really good coffee tomorrow, and a ton of brew!

Gonna toast one to you Rasman, and the rest of the crew! Maybe even hold ya to that promise we made a while back. Oh it's gonna be a rollercoaster ride this weekend!

Haha, coffeeeee....
That is a good friend of mine too. This is my life atm:
Code:
while (dwelvers.finihed == false)
{
Eat();
Nap();
Coffee();
if (weekday == 'Monday')
{
Watch('Game Of Thrones');
}
Code();
}

Thanks Smile This was something I really enjoyed coding, every time I go into coding something new that I have never done before my motivation goes through the roof. It has made me more confident that all buildings in the game can be coded in the same way, making the game super duper moddable Tongue

Btw, I did make it so that the hydraulics can get built inside rooms now, I have also added a new hydraulic named "inverter". Hopefully this will make it even more fun Tongue In the future I will expand onto the moveable wall so that it can move what is in front of it, like other walls, hydraulics and tables for example.
30-05-2014 05:13 AM
Find all posts by this user Quote this message in a reply
Mello Tonin Offline
Guru O' Mello

Posts: 2.216
Joined: Aug 2013
Reputation: 176
Post: #6
RE:
Nice, I dig your routine code, especially the Watch function (I have the same function when weekday == 'Monday'). Which reminds me, I have an image I came across this week I think you'll get a kick out of. I'll post it in Last Post Wins later.
30-05-2014 10:21 PM
Find all posts by this user Quote this message in a reply
Rasmus Offline
Main developer of Dwelvers
Dwelvers Developers

Posts: 3.619
Joined: Sep 2013
Reputation: 502
Post: #7
RE:
This I gotta see Big Grin
30-05-2014 10:35 PM
Find all posts by this user Quote this message in a reply
Post Reply 


Forum Jump:


User(s) browsing this thread: 1 Guest(s)


© 2013-2017 Dwelvers | Contact