Coding Advent Calendar II
Moderator: Forum Moderators
- Please use [code] BBCode tags in your posts for embedding WML snippets.
- To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Coding Advent Calendar II
- codingadventcalendar.png (14.69 KiB) Viewed 1472 times
viewtopic.php?p=660691#p660691
I'd like to continue this 'tradition' and will present a piece of code every day till the 24th of December. The main idea is to give you pieces of WML code you can use right away in your own projects. I'll try to make it beginner friendly and of course you may use, share and change the code as you like.
Why do I do this? Imho because the examples in the wiki aren't good. The wiki offers a good technical description of tags, but lacks good and working examples.
Note: As this is mostly for WML beginners, it might not be interesting for you. For those who are looking for challenges and coding puzzles you should check the project https://adventofcode.com/. Enjoy.
The topics of this thread:
01. change terrain and move to events
02. sounds, set variables, gold manipulation, print and messages
03. how to make a simple macro
04. random generated variables and event 'new turn'
05. how to make a scenario, examples for scenarios, main.cfg, server.pbl
06. the campaign DDD is now available to review the source code and testing
07. placing npc units and talk events, earn gold by selling your ore to the king
08. creating shops, dialogue options and spend gold
09. objects - different examples how to alter movement costs, defense, resistance and many more
10. refine ore - upgrade equipment with ore
11. Jonna teaches spellcasting, adding icons to menu
12. type in a password and check it: 'cheater option' and playable version 0.2
13. Creating own terrain, add a 'vein' terrain overlay
14. Mine a specific terrain without placing single events. (Reactive Terrain Part I)
15. Random dungeon: The 'Cheesehole Concept'.
How to use while loops.
16. Place random monsternests and random monsters into the dungeon.
17. Place 'biomes': add map parts using terrain_mask and place monsters on them.
18. Biome example: Placing random movement events, delayed_variable_substitution=no and ai_special=guardian.
19. Placing random leaders and factions. Side concepts.
20. Upgrades with scaling costs using variables.
22. Reactive terrain II: Shrines, Artifacts, Monsternest, Minecarts etc. Place 'events' with the map editor.
23. Making different dungeon types and dungeon levels.
24. The final version of DDD: how to make quests and advancement trees for units.
25. Advice on how to port an add-on from 1.14 to 1.16
Door 1:
This coding advent calendar will be different from the last one. We will create a game (add-on) together and piece by piece build it. I hope this will inspire more players to create own scenarios and content. Maybe some of these parts can be used in the wiki as example.
The idea is to code a mini-game / add-on called 'Miner' (I know this idea isn't super creative and there are already some add-osn like this on the add-on server already, please excuse the example just in case, such as Christophe33). In our mini-game you are err Dwarf Dwarfson a famous miner who tries to mine riches in the underground. In the scenarios he will be able to mine ore in different ways. Your dwarf will than be able to mine a potential location. For this we will need a simple event.
It's a moveto event that destroys the wall and turns the terrain, when a character moves to a specific spot:
Code: Select all
[event]
name=moveto
first_time_only=yes
[filter]
x=1
y=2
[/filter]
[terrain]
x=1
y=1
terrain=Rr
[/terrain]
[/event]
When a character, and I mean any character, moves to the position x=1 and y=2 he will destroy the wall.
Let's look into the details:
[event] >> starts the event
name=moveto >> is the type of event that is triggered.
first_time_only=yes >> will prevent that the event is triggered over an over again.
[filter] >> this starts a filter, which defines WHEN the event is triggered.
x=1 >> this is definition of the filter, it just says x=1 and y=2
y=2
id=Dwarvenminerid >> I added this one, this is how you filter for a specific unit with the super cool (sarcasm) id Dwarvenminderid.
[/filter] >> this ends the filter, this is necessary to clearly say, when the filter definition is over
[terrain] >> this is a terrain tag, it changes the terrain on a specific numbers of location
x=1 >> the definition, you could also add several pieces of variables, for example 1,2,3,4 or 1-4
y=1
terrain=Rr >> this code says, what the terrain of 1,1 will turn into. But what is Rr? See here: https://wiki.wesnoth.org/TerrainCodeTableWML
[/terrain] >> ends terrain definition
[/event]>> ends the event
Adapt and change the event in the way you like, tomorrow we will improve it slightly with more features.
Door 2:
Now, now. It might be that you are somewhat disappointed by this first event. It works, but it is a simple mechanic and requires lot of time to find out all coordinates, which has to be given. But there is one thing, which isn't good. Beside that it 'destroys' walls or terrain, there is no additional effect such as giving the player gold or ore that he has mined. A sound effect would be cool to. Maybe it would be cool to add all this to our event.
Code: Select all
[event]
name=moveto
first_time_only=yes
[filter]
x=1
y=2
[/filter]
[terrain]
x=1
y=1
terrain=Rr
[/terrain]
[sound]
name=mace.ogg
[/sound]
[gold]
side=1
amount=1
[/gold]
[set_variable]
name=ore
add=1
[/set_variable]
[/event]
Let's explain the new parts:
This one is cool, it will play a sound.
[sound]
name=mace.ogg
[/sound]
But what where can you find the list of sounds that are possible?
These sound can be found under data\code\sounds in the installation folder.
For those of you who use steam this would be:
C:\Program Files (x86)\Steam\SteamApps\common\wesnoth\data\core\sounds
Alternatively you can even add an own 'sounds' folder to your campaign and can address the files it in the same way as above.
I will explain this later when we build our first campaign.
This is a nice feature which grants the player of a specific side an amount of gold.
Our dwarf seems to have found some gold in the ore he was mining. Huzzah Hurray.
Usually the protagonist of a campaign is side 1, while side 2 might be the enemy or one of the heroes allies.
In multiplayer campaigns you have many protagonists, so it wouldn't be so wise to use it there.
[gold]
side=1
amount=1
[/gold]
Here we set an variable. This means, even so this variable hasn't been there before it is now there. If the variable ore wasn't there it, it is now and it will be 1. Next time we trigger another event, it will be 2 and so on. So this is basically a counter of how many ore the dwarf has mined. We could use this information for example for winning the scenario / level, when he has at least 10, we could grant him an achievement once he reached 100 or more logical he could sell the ore or craft things out of it. With this variable our event now actually does something.
[set_variable]
name=ore
add=1
[/set_variable]
We should however add something more. The player might see that his gold has increased, but he is unaware that he found ore. Maybe that vein granted two 'ores' or it was just a waste of time and he received 0 ore and not even a single piece of gold. So we should inform the player what he has found. There are two possible ways to do that. We could display a message or we show a label.
A message is pretty simple:
Code: Select all
[message]
speaker=narrator
message= _ "You found 1 ore in this vein! You even found 1 piece of gold!"
[/message]
There is a disadvantage in this kind of event as it interrupts the game flow and forces the player to actively click (or enter) it away.
One of the advantages is the ability to show your characters picture. While speaker=narrator will just show a normal message without a character picture adding the "id" of a specific unit, a unit type or a side will display a message for a unit that fits the description.
Code: Select all
[message]
speaker=Dwarvenminerid
message= _ "Hurray! I found 1 ore and even gold in this vein!"
[/message]
The alternative is print. Print will display a message on the screen for a short period of time and will not force the player to enter it away.
The disadvantage is, that the player might oversee the message.
Code: Select all
[print]
text="You found one ore!"
size=30
duration=100
red,green,blue=155,0,0
[/print]
size >> the size of the displayed message
duration >> number of seconds the message appears
red,green,blue >> defines the color as an rgb (155,0,0 is dark red, 255,255,255 is white, 0,0,0 is black, 255,0,0, red, 0,255,0 green, 0,0,255 blue and so on)
Now we are missing a final part for our event. There is no way the player can actually know where to mine. It would be could to give him a hint, by placing a label, which will display a small piece of text. This is optional, but here is how this works.
Code: Select all
[label]
x=1
y=2
text="Mine here"
[/label]
Code: Select all
[label]
x=1
y=2
text="Mine here"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x=1
y=2
[/filter]
[terrain]
x=1
y=1
terrain=Rr
[/terrain]
[sound]
name=mace.ogg
[/sound]
[gold]
side=1
amount=1
[/gold]
[set_variable]
name=ore
add=1
[/set_variable]
[print]
text="You found 1 ore and 1 gold!"
size=30
duration=100
red,green,blue=155,0,0
[/print]
[/event]
Door 3:
We now made a fine movement event, which allows us to make a real game, as we count the number of ore that our dwarf has mined.
We could even adjust the number of ore and gold he gets. However, there are some drawbacks of this event.
First of all I need to place a lot of events all over the map and I need to plan the map accordingly. I need many coordinates and definitions of how much gold and ore our little dwarven miner receives. And the event is very long and will take many lines in our scenario. Plus - if we want to improve the event somehow, we would need to change all events, which is a huge amount of work.
Now you say: "I'm lazy, but smart! Can't a machine do that for me?"
Well done, young padawan! That's what we look into today. This is actually one of the coolest features of WML.
You can design macros and I'm not talking about noodles.
We will define a macro called MINE_HERE:
Code: Select all
#define MINE_HERE movex movey minex miney
[label]
x={movex}
y={movey}
text="Mine here"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x={movex}
y={movey}
[/filter]
[terrain]
x={minex}
y={miney}
terrain=Rr
[/terrain]
[sound]
name=mace.ogg
[/sound]
[gold]
side=1
amount=1
[/gold]
[set_variable]
name=ore
add=1
[/set_variable]
[print]
text="You found 1 ore and 1 gold!"
size=30
duration=100
red,green,blue=155,0,0
[/print]
[/event]
#enddef
This is usually the "\utils\" folder inside of a scenario. Can you give some examples?
Under C:\Program Files (x86)\Steam\SteamApps\common\wesnoth\data\campaigns\ you should find all scenarios of the core game.
Many of them uses the 'util' folder where these utils are defined.
For example this one: C:\Program Files (x86)\Steam\SteamApps\common\wesnoth\data\campaigns\Two_Brothers\utils
Once you saved such a definition, you can use it like that in your scenario:
{MINE_HERE 1 2 1 1}
This will add our movement event, which will place a label on coordinates 1,2 Mine here and once you moved there it will turn the coordinates 1,1 into the terrain Rr and all the other things we defined before.
{MINE_HERE 3 4 3 3}
This will add our movement event, which will place a label on coordinates 3,4 Mine here and once you moved there it will turn the coordinates 3,3 into the terrain Rr and all the other things we defined before.
{MINE_HERE 5 5 10 10}
This will add our movement event, which will place a label on coordinates 5,5 Mine here and once you moved there it will turn the coordinates 10,10* into the terrain Rr and all the other things we defined before. *WAIT WAIT WAIT ... it will destroy a terrain on the other side of the map? Maybe a mystic power which allows the dwarf to magically mine that thing? As you say, even here it can happen from time to time that you make mistakes. But with this little device you can save yourself a lot of time and you can change the definition any time you like and the macro will perform correctly. Please note that each macro also needs all variables you defined above - so a {MINE_HERE 5 5 5} will not work and worst of all your project will not start. Fortunately the game will put out an error message and even name the line of the macro inside your file.
There are a lot of macros in the scenarios and maybe you have seen some of these spooky and complicated looking macros before:
{NAMED_LOYAL_UNIT 1 Horseman 33 21 Tarek (_ "Tarek")}
or
{PLACE_IMAGE scenery/village-human-burned2.png 7 12}
or
{TURNS_RUN_OUT}
These are actually not hard to understand, once you tried and understood our MINE_HERE example above.
These macros are already there and exist in the core. So you can use them as well, if you want.
These macros can be found in the installation folder of Battle for Wesnoth under \data\core\macros.
If you use Steam this is for example: C:\Program Files (x86)\Steam\SteamApps\common\wesnoth\data\core\macros
So much about macros.
Door 4:
Now we are almost there. We have a macro or movement event that can be used to count the number of ores we mines. We should improve the macro a final time. Fortunately we can do that now, even so we might have placed the macro like '1000' times in the scenarios of our campaign. Also it would be cool, that if we reached a number of ores, that we could win the scenario and get to the next one, maybe a city where he can sell the ore and buys new equipment and can enter even harder level - maybe with monsters? So we could make an actual game, that evolves over the time and theoretically we could add an game infinite game play to our little game / add-on.
But first things first. What should the improvements of the MINE_HERE macro be? It would be nice, if we could place an image or better terrain that is to be mined and turned into rubles after we mined it. So we could place the image of an ore vein. That could be harder than you think it could be, as you can not use a terrain and image tag everywhere. While a label can be placed inside of a scenario, their are some restrictions for using some kind of tags. Also we don't have an image and everybody who knows my campaigns knows how sloppy and fast paced my artwork is. Honestly, I want to check this first.
But there is another thing we should think about. Wouldn't it be cool if we could have different kind of veins, so our miner could find more or less ore? So one vein could give him 0 ores another one 3 or even 5 ores and the number of gold could also be different. Now you might think of one approach:
Why don't we add this into our macro? We could for example change the macro like this:
Code: Select all
#define MINE_HERE movex movey minex miney ore gold
[label]
x={movex}
y={movey}
text="Mine here"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x={movex}
y={movey}
[/filter]
[terrain]
x={minex}
y={miney}
terrain=Rr
[/terrain]
[sound]
name=mace.ogg
[/sound]
[gold]
side=1
amount={gold}
[/gold]
[set_variable]
name=ore
add={ore}
[/set_variable]
[print]
text="You found {ore} ore and {gold} gold!"
size=30
duration=100
red,green,blue=155,0,0
[/print]
[/event]
#enddef
We can avoid this dilemma and add a little action for the player. We make the number of ores and gold random. And this is easily possible with this piece of code:
Code: Select all
[set_variable]
name=amountore
rand=0..5
[/set_variable]
[set_variable]
name=amountgold
rand=0..2
[/set_variable]
We could also have used: 0,1,2,3,4,5 or 1,3,5
In the last case, we could only have the results 1 or 3 or 5.
Code: Select all
#define MINE_HERE movex movey minex miney
[label]
x={movex}
y={movey}
text="Mine here"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x={movex}
y={movey}
[/filter]
[terrain]
x={minex}
y={miney}
terrain=Rr
[/terrain]
[sound]
name=mace.ogg
[/sound]
[set_variable]
name=amountore
rand=0..5
[/set_variable]
[set_variable]
name=amountgold
rand=0..2
[/set_variable]
[gold]
side=1
amount=$amountgold
[/gold]
[set_variable]
name=ore
add=$amountore
[/set_variable]
[print]
text="You found $amountore ore and $amountgold gold!"
size=30
duration=100
red,green,blue=155,0,0
[/print]
[/event]
#enddef
Maybe you've noticed the $ signs before the new variable we made. This is necessary to add the value of the variable at this point.
It's a common mistake to forget the $ signs. If we would have used:
[set_variable]
name=ore
add=amountore
[/set_variable]
The ore variable would have turned into a 'string' or text. So ore would now have saved the value=amountore instead of 0,1,2,3,4 or 5.
Not good if we want to end the game with a specific number of ores selected. Let's see.
We now know that our dwarves can find 1-5 ore and 0-2 gold in each vein. So we can calculate the power-gain for each level and the minimum he can get. Now we want to make an event, that ends the level, once he has completed his mining task.
For this we will us the event new turn, which will happen, once the player starts a new turn.
Code: Select all
[event]
name=new turn
first_time_only=no
[if]
[variable]
name=ore
greater_than=10
[/variable]
[then]
[message]
speaker=Dwarvenminerid
message= _ "Hurray! I've finished my mining task!"
[/message]
[endlevel]
result=victory
next_scenario=City
save=no
carryover_report=no
carryover_percentage=100
linger_mode=no
bonus=no
[/endlevel]
[/then]
[/if]
[/event]
I've got to explain this code.
Code: Select all
[event]
name=new turn
first_time_only=no #we choose first time only, as we want the event to check every new turn, not just the first one.
[if] # this is the beginning of an if clause. It checks if variables or other conditions are fulfilled and will than do the thing in the [then] tags
[variable] # this is the condition of the if clause, you can have different conditions, now we check for a variable or
name=ore # the name of the variable we check
greater_than=10 # the condition, here if the number mined ore is greater than 10 - this means 11 or greater
[/variable]
[then]
[message]
speaker=Dwarvenminerid
message= _ "Hurray! I've finished my mining task!"
[/message]
[endlevel] # this one will end the level and jump to the next scenario
result=victory # we can use victory or defeat here, as we want the game to continue we chose victory ;)
next_scenario=City # this is the new scenario we will open, it's the Id of the scenario (we will look into this later)
save=no
carryover_report=no # you will receive no report about the gold you got
carryover_percentage=100 # you will take all the precious gold you mined with you
linger_mode=no # you will not stay in the scenario, but directly jump into the next one
bonus=no # you don't receive a bonus, even so you finished the game earlier
[/endlevel]
[/then]
[/if]
[/event]
We will need at least 11 veins in the scenario we create, so that our miner can finish the scenario. We don't have to make a macro for this event, but maybe we could when entered several more levels. But we could also just copy it and edit the amount of ore needed to win the campaign.
That's it. We have everything now to make a basic game, so we can build our first level (scenario) and the campaign itself tomorrow.
Door 5:
(I know that, because I do that a lot! So don't do it! )
We now know what to do: For now we need to make a campaign and at least two scenarios.
One scenario for the mining level and one for the 'city' where our miner receives supplies.
For making a campaign we can download the add-on Campaign-How-To (A Simple Campaign) by Anonymissimus and opensorucejunkie.
The great thing here, is that we already have many folders and definitions done for us.
Remember how I said that we can make an own folder for our sounds within the campaign?
It's already part of this campaign and even offers folders and files for translations, macros, terrain, images and units.
It offers a lot of explanations and tips how to create your own campaign. I could probably fill this advent calendar with all the details.
The explanations are usually behind a # sign, which doesn't mean it's part of a tweet, but a comment.
Comments will be ignored. I used that comments in my descriptions before.
Essential is that we copy the folder and name our campaign accordingly and change the server.pbl.
I named it "Dwarf_Dwarfson_Dwarvenminer" (Shortcut DDD).
After copying the file and change the folder into Dwarf_Dwarfson_Dwarvenminer (use no spaces, but _ instead), my first step was to replace A_Simple_Campaign. You can use the features of your editor to find and replace text in a specific folder. This is done like that:
Than I replaced most of the comments, we don't need them for now.
I also changed the pictures an description inside the _main.cfg file.
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
[textdomain]
name="wesnoth-Dwarf_Dwarfson_Dwarvenminer"
path="data/add-ons/Dwarf_Dwarfson_Dwarvenminer/translations"
[/textdomain]
[campaign]
id=Dwarf_Dwarfson_Dwarvenminer
name=_ "Dwarf Dwarfson Dwarvenminer"
abbrev=_"DDD"
define=Dwarf_Dwarfson_Dwarvenminer
{CAMPAIGN_DIFFICULTY NORMAL "units/dwarves/explorer-melee-4.png" (_"Medium") (_"Normal")} {DEFAULT_DIFFICULTY}
icon=data/add-ons/Dwarf_Dwarfson_Dwarvenminer/images/dwarvenminer.png
image=portraits/dwarves/explorer.png
description=_ "Dwarf Dwarfson Dwarvenminer is a powerful dwarvenminer that wants to become the richest of all dwarfs. Did I mention dwarfs yet? Dwarfs Dwarfs Dwarfs.
This is part of the project Coding Advent Calendar II. The source code is explained piece by piece.
If you like to make own content and need some ideas, here you will hopefully find them."
first_scenario=Mine
[/campaign]
#ifdef Dwarf_Dwarfson_Dwarvenminer
[binary_path]
path=data/add-ons/Dwarf_Dwarfson_Dwarvenminer/
[/binary_path]
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/macros}
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/utils}
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/scenarios}
[+units]
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/units}
[/units]
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/terrain}
#endif
#ifdef EDITOR
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/terrain}
[binary_path]
path=data/add-ons/Dwarf_Dwarfson_Dwarvenminer/
[/binary_path]
[editor_group]
id=ddd
name= _ "Dwarf Dwarfson Dwarvenminer"
icon="group_custom"
[/editor_group]
#endif
We need just two files in the top folder. The server.pbl and the main.cfg. The rest is not necessary.
I've adjusted the _server.pbl like this. (note delete the _ behind pbl first)
Code: Select all
author="Heindal"
email="heindal@scherbia.de"
icon="data/core/images/units/dwarves/explorer-melee-4.png"
passphrase="putinthepasswordhere"
title="Dwarf Dwarfson Dwarvenminer"
translate="false"
type="campaign"
version="0.1"
description="Play as a dwarfenminer and mine ore and gold. Upgrade your equipment in a city and mine even more ore in procedual generated levels.
This add-on is part of the WML Code Advent Calendar II and every part of the development is documented so other user can use parts and piece of code this campaign."
(note that without coordinates your hero will be spawned outside the map in the void and your campaign will not work than)
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
# Note - that's what I'm talking about! This is a macro which is defined at the beginning of a scenario!
# You can undef it after the scenario, as it is no longer needed.
#define STORY
[story]
[part]
story= _ "You are dwarf, a cursed good one. But you made a mistake. No actually not. You became a miner and want to become the best miner in dwarf history.
Your name is legend, you are legion. You are Dwarf Dwarfson Dwarvenminer. Chief of the Dwarf Dwarfson Dwarvenminer Inc. Good luck, Dwarfson!"
background=portraits/dwarves/explorer.png
[/part]
[/story]
#enddef
[scenario]
id=Mine
name=_"Mine"
map_data="{~add-ons/Dwarf_Dwarfson_Dwarvenminer/maps/Mine.map}"
victory_when_enemies_defeated=no #there are so far no enemies, so we set this to no
next_scenario=null # there is no next scenario, we will define that in our event
turns=-1 # this allows infinite turns
{SCENARIO_MUSIC breaking_the_chains.ogg}
# this is where our macro from above is added. Unnecessary if you ask me. we could have added the story part right here, too.
# but if you have a single scenario that need a macro, you can use this one.
{STORY}
{UNDERGROUND}
[side]
side=1
controller=human
team_name=1
user_team_name= _ "Dwarf Dwarfson"
type=Dwarvish Explorer
id=Dwarvenminerid
name=_"Dwarf Dwarfson"
gender=male
canrecruit=yes
unrenamable=yes
recruit=
village_gold=0
income=-2
gold=0
[/side]
[side]
side=2
controller=ai
team_name=2
user_team_name= _ "Hisses in the Dark"
type=Giant Spider
id=Monster
name=_"Monster"
gender=male
canrecruit=yes
unrenamable=yes
recruit=
[/side]
[event]
name=prestart
[objectives]
side=1
summary=_"final objectives:"
[objective]
description= _ "Mine more than 10 ore"
condition=win
[/objective]
[objective]
description= _ "Death"
condition=lose
[/objective]
[/objectives]
[/event]
[event]
name=prestart
[message]
speaker=Dwarvenminerid
message=_"Let's get going. Ore doesn't mine itself!"
[/message]
[/event]
{MINE_HERE 1 2 1 1}
{MINE_HERE 3 2 3 1}
{MINE_HERE 5 2 5 1}
{MINE_HERE 7 2 7 1}
{MINE_HERE 9 2 9 1}
{MINE_HERE 11 2 11 1}
{MINE_HERE 13 2 13 1}
{MINE_HERE 15 2 15 1}
{MINE_HERE 17 2 17 1}
{MINE_HERE 19 2 19 1}
{MINE_HERE 21 2 21 1}
{MINE_HERE 23 2 23 1}
[event]
name=new turn
first_time_only=no
[if]
[variable]
name=ore
greater_than=10
[/variable]
[then]
[message]
speaker=Dwarvenminerid
message= _ "Hurray! I've finished my mining task!"
[/message]
[endlevel]
result=victory
next_scenario=City
save=no
carryover_report=no
carryover_percentage=100
linger_mode=no
bonus=no
[/endlevel]
[/then]
[/if]
[/event]
[/scenario]
The city code will contain a way to return to the Mine Level. For this we will need to reset the ore variable to 0. Maybe later we can make an if clause and check if our miner has still unprocessed ore, for now this is a loop, which is and allows us to play a little.
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
[scenario]
id=City
name=_"City"
map_data="{~add-ons/Dwarf_Dwarfson_Dwarvenminer/maps/City.map}"
victory_when_enemies_defeated=no #there are so far no enemies, so we set this to no
next_scenario=null # there is no next scenario, we will define that in our event
turns=-1 # this allows infinite turns
{SCENARIO_MUSIC breaking_the_chains.ogg}
# this is where our macro from above is added. Unnecessary if you ask me. we could have added the story part right here, too.
# but if you have a single scenario that need a macro, you can use this one.
[story]
[part]
story= _ "The City. You returned from the mine with $ore ore!"
background=portraits/dwarves/explorer.png
[/part]
[/story]
{UNDERGROUND}
[side]
side=1
controller=human
team_name=1
user_team_name= _ "Dwarf Dwarfson"
type=Dwarvish Explorer
id=Dwarfenminerid
name=_"Dwarf Dwarfson"
gender=male
canrecruit=yes
unrenamable=yes
village_gold=0
income=-2
[/side]
[side]
side=2
controller=null
team_name=1
user_team_name= _ "NPC"
type=Dwarvish Runemaster
id=King
name=_"King"
gender=male
canrecruit=yes
unrenamable=yes
recruit=
[/side]
[event]
name=prestart
[objectives]
side=1
summary=_"final objectives:"
[objective]
description= _ "Process ore and gather supplies"
condition=win
[/objective]
[objective]
description= _ "Death"
condition=lose
[/objective]
[/objectives]
[/event]
[event]
name=start
[message]
speaker=Dwarvenminerid
message=_"Let's get going. Ore doesn't mine itself!"
[/message]
[/event]
[label]
x=1
y=1
text="The Mine"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x=1
y=1
[/filter]
[set_variable]
name=ore
value=0
[/set_variable]
[endlevel]
result=victory
next_scenario=Mine
save=no
carryover_report=no
carryover_percentage=100
linger_mode=no
bonus=no
[/endlevel]
[/event]
[/scenario]
Door 6:
- campaign.png (29.02 KiB) Viewed 1418 times
The campaign is now available for download (1.14, 1.16.). Enjoy.
Please note that many explanation were missing in Door 5. But they are provided by the Simple Campaign I've mentioned earlier.
They really described everything there, so this is a perfect reference for you in case you have questions.
Door 7:
We will now upgrade our city placing units and events. Here just some examples.
You should make a plan before you do something like that, maybe on a sheet of paper or with your real map using a screenshot, like I did.
Let's first place an npc. We start with the king.
Code: Select all
[unit]
side=2 #side of the unit, it is allied, we define that in the scenario itself
type=Dwarvish Lord # the type of the unit, types can be found here https://units.wesnoth.org/1.16/mainline/en_US/mainline.html
id=King # The Id, important when we want to make this unit talk to us.
name= _ "The King" # The name the player will see
x=2 # our coordinates
y=8
canrecruit=yes # this unit could recruit units, it will get a golden crown
[/unit]
Note, as side 2 is defined with controller=null units of that side will not move nor engage.
Perfect for our npcs.
Code: Select all
[label]
x=3
y=8
text="Talk"
[/label]
[event]
name=moveto
first_time_only=yes
[filter]
x=3
y=8
[/filter]
[message]
speaker=King
message= _ "I will pay you 1 gold per ore you've mined. Here you go $ore gold for you."
[/message]
[gold]
side=1
amount=$ore
[/gold]
[set_variable]
name=ore
value=0
[/set_variable]
[/event]
Door 8:
Unit can't be placed in the scenario itself, but must be nested either inside a side tag, or within an event. E.g. you could spawn units with an event or at the beginning of the scenario inside the side tag.
Example:
Code: Select all
[side]
side=2
controller=null
team_name=1
user_team_name= _ "NPC"
type=Dwarvish Runemaster
id=Leader
name=_"Leader"
gender=male
canrecruit=yes
unrenamable=yes
recruit=
[unit]
side=2 #side of the unit, it is allied, we define that in the scenario itself
type=Dwarvish Lord # the type of the unit, types can be found here https://units.wesnoth.org/1.16/mainline/en_US/mainline.html
id=King # The Id, important when we want to make this unit talk to us.
name= _ "The King" # The name the player will see
x=2 # our coordinates
y=8
canrecruit=yes # this unit could recruit units, it will get a golden crown
[/unit]
# we could have used this macro to add-on {NAMED_LOYAL_UNIT 2 (Royal Guard) 26 21 (Royal Guard1) ( _ "City Guard")}
# this can cause some harm, as you sometimes mix the ID of a unit with the type
[unit]
side=2
type=Dwarvish Guardsman
id=Guard
name= _ "Mine Guard"
x=5
y=3
[/unit]
[/side]
Now we will make a shop. There different option to make a shop and different approaches. One idea is to enter a completely new scenario and make terrains for buttons, equipments, buying and selling etc., which react when you click on them. Sounds complicated? It is, but it is also a nice approach on how to do things. You can also make an inventory like that.
An easier option is to make an obstacle, you can just "destroy" when you have enough gold. This is 'arcade style'. You move your character to a specific spot and destroy the obstacle / wall, which allows you to get the object behind it. Once you move their the object will be taken and grant a bonus. Objects are important, as they can change your character such as granting new abilities, attacks, getting higher a defense and so on. Usually these are real 'objects' such as weapons and equipment you can find in some scenarios in different campaigns. Making a shop will require us to look into objects any way. We already know how to change terrains and movement. This could be done easily if we find out how 'objects' work and we could check if we had enough gold.
The third options is to use dialogue options. I do that a lot. In some campaigns you will have to make a decisions. Maybe you remember a fate of two brothers where a knight offers his assistance. Or in some situations where you can decide to take a different way or receive a variety of bonusses.
Options are done like this (example taken from Fate of Two Brothers):
Code: Select all
[message]
speaker=Arvith
message= _ "It is my place to decide this."
[option]
label= _ "Oh, all right then. Come along with us."
[command]
[message]
speaker=Brena
message= _ "Thank you. My comrades and I will help you on your noble quest."
[/message]
[/command]
[/option]
[option]
label= _ "I am sorry. We have not the time to spare."
[command]
[message]
speaker=Brena
message= _ "Take this, then, for I see that you are on a quest. My comrades will help you whenever you call for them."
[/message]
[sound]
name=gold.ogg
[/sound]
[gold]
side=1
amount=70
[/gold]
[message]
speaker=narrator
image="wesnoth-icon.png"
message= _ "You receive 70 pieces of gold!"
[/message]
[kill]
id=Brena
[/kill]
[/command]
[/option]
[/message]
Code: Select all
[label]
x=10
y=7
text="Talk"
[/label]
[event]
name=moveto
first_time_only=no
[filter]
x=10
y=7
[/filter]
[message]
speaker=Blacksmith
message= _ "Hello Mr. Dwarfson. What can I do for you today?"
[/message]
[message]
speaker=Dwarvenminerid
message= _ "Well, the usual. Axes, pickaxes, bombs, spareparts and repairing. Show me your wares!"
[/message]
[set_variable]
name=finished
value=no
[/set_variable]
[while]
[variable]
name=finished
equals=no
[/variable]
[do]
[store_gold]
side=1
variable=gold
[/store_gold]
[message]
speaker=Blacksmith
message= _ "Here you go:"
[option]
image=attacks/battleaxe.png
message= _ "Sharpen Battleaxe
<small>increase your melee damage by 1 for <b>10 gold</b></small>"
[command]
[if]
[variable]
name=gold
greater_than_equal_to=10
[/variable]
[then]
[message]
speaker=Blacksmith
message= _ "It's a pleasure making business with you!"
[/message]
[gold]
side=1
amount=-10
[/gold]
[object]
silent=yes
[effect]
apply_to=attack
name=battle axe
increase_damage=1
[/effect]
[/object]
[/then]
[else]
[message]
speaker=Blacksmith
message= _ "Sorry, you do not have enough gold!"
[/message]
[/else]
[/if]
[/command]
[/option]
[option]
image=attacks/axe-crude.png
message= _ "Improve Throwing Axes
<small>increase your ranged damage by 1 for <b>10 gold</b></small>"
[command]
[if]
[variable]
name=gold
greater_than_equal_to=10
[/variable]
[then]
[message]
speaker=Blacksmith
message= _ "It's a pleasure making business with you!"
[/message]
[gold]
side=1
amount=-10
[/gold]
[object]
silent=yes
[effect]
apply_to=attack
name=axe
increase_damage=1
[/effect]
[/object]
[/then]
[else]
[message]
speaker=Blacksmith
message= _ "Sorry, you do not have enough gold!"
[/message]
[/else]
[/if]
[/command]
[/option]
[option]
[show_if]
[variable]
name=pick-axe
not_equals=yes
[/variable]
[/show_if]
image=attacks/pick-axe.png
message= _ "Buy Pickaxe
<small>will give you a new attack and allows you to break walls for <b>10 gold</b></small>"
[command]
[if]
[variable]
name=gold
greater_than_equal_to=10
[/variable]
[then]
[message]
speaker=Blacksmith
message= _ "It's a pleasure making business with you!"
[/message]
[gold]
side=1
amount=-10
[/gold]
[object]
silent=yes
[effect]
apply_to=new_attack
name=pick-axe
type=impact
range=melee
damage=30
number=1
icon=attacks/pick-axe.png
[/effect]
[/object]
[set_variable]
name=pick-axe
value=yes
[/set_variable]
[/then]
[else]
[message]
speaker=Blacksmith
message= _ "Sorry, you do not have enough gold!"
[/message]
[/else]
[/if]
[/command]
[/option]
[option]
image=icons/sandals.png
message= _ "Leave"
[command]
[set_variable]
name=finished
value=yes
[/set_variable]
[/command]
[/option]
[/message]
[/do]
[/while]
[/event]
Code: Select all
[store_gold]
side=1
variable=gold
[/store_gold]
As it is nested in the while loop, it will constantly save the variable.
This allows us to check inside the options if our gold his high enough to buy a specific item. It will also be updated once we decide to buy an item and the store will behave correctly. Otherwise we could buy what we want, the gold variable isn't changed.
We will look into objects tomorrow. The campaign has been updated (1.16), so you can review the code.
Door 9:
Yesterday we combined different tags. We used while to make an ongoing process, which we can end by chosing an option.
Furthermore we used 'options', 'if then' and 'objects'. Let's look into objects today. Objects can be found in many campaigns.
Hidden inside a dungeon the players can find mysterious artifacts which grant a unit special abilities, increase their damage, movement and much much more. Yesterday we used this object.
Code: Select all
[object]
silent=yes
[effect]
apply_to=attack
name=pickaxe
increase_damage=2
[/effect]
[/object]
One could say, we misused it for our purpose to change the stats of a unit selecting this option.
You can find examples in the macro section of the wesnoth folder in the file 'items':
C:\Program Files (x86)\Steam\SteamApps\common\wesnoth\data\core\macros
Based on this, we will now make a potion that grants several effects at once.
This page on the wiki might help to find out, what is possible:
https://wiki.wesnoth.org/EffectWML
Code: Select all
[item]
x=1
y=1
image=items/potion-red.png
[/item]
[event]
name=moveto
first_time_only=no
[filter]
x=1
y=1
[/filter]
[object]
id=Uberpotion
name= _ "Uberpotion"
image=items/potion-red.png
duration=scenario
description= _ "This tastes strange."
cannot_use_message= _ "No, you can't drink this potion."
[filter]
x=1
y=1
[/filter]
[then]
[remove_item]
x,y=1,1
[/remove_item]
[/then]
[effect]
apply_to=hitpoints
increase=10%
[/effect]
[effect]
apply_to=hitpoints
increase_total=10%
[/effect]
[effect]
apply_to=new_ability
[abilities]
{ABILITY_REGENERATES}
[/abilities]
[/effect]
[effect]
apply_to=movement
increase=2
[/effect]
[effect]
apply_to=attack
range=ranged
set_type=fire
[/effect]
[effect]
apply_to=new_animation
id="axe"
[attack_anim]
[filter_attack]
name=axe
[/filter_attack]
{MISSILE_FRAME_FIREBALL_XY 0 0}
start_time=-200
[frame]
sound=fire.wav
[/frame]
[/attack_anim]
[/effect]
[effect]
apply_to=status
add=poisoned
[/effect]
[/object]
[/event]
So moving to 1,1 will remove the item (picture) from 1,1 and will grant the unit 10% more hitpoints, will heal 10 % of its hitpoints, will receive the ability Regenerate, will get two additional movement points, will change his ranged attack to fire damage and turn the axe animation he has into a fireball.
Because this potion is so powerful it is unfortunately toxic and will poison the unit that will drink it. You can also filter, which unit may drink the potion. Right now anyone moving to 1,1 will be able to drink it. But with the filter inside of object you could for example say: id=Dwarvenminerid.
Than only you hero would be worthy to drink this potion. Alternatively you could select side=1, than any recalled or recruited unit of the same side as our hero could drink this potion.
You can do a lot with effect even turn a unit into different types, or change the advancement of a unit. But you can even change some stats such the defense of unit (chance of enemy to hit you in specific terrain), the resistance (the damage a unit takes from an attack) or even the movement information, how fast a unit can move on specific terrain. This is slightly different from the effects we did above. This is how it works:
This object is called 'Book of Fate' which is part of one of my campaigns. Here you can see how you can change the defense and the resistance.
Please note:
Code: Select all
[object]
silent=yes
[effect]
apply_to=defense
replace=false
[defense]
castle=-1
cave=-1
reef=-1
deep_water=-1
flat=-1
forest=-1
frozen=-1
hills=-1
mountains=-1
fungus=-1
sand=-1
shallow_water=-1
swamp_water=-1
village=-1
[/defense]
[/effect]
[effect]
apply_to=resistance
replace=false
[resistance]
blade=-1
pierce=-1
impact=-1
fire=-1
cold=-1
arcane=-1
[/resistance]
[/effect]
[/object]
Thats not true. The resistance works this way:
A resistance of 100 means, that the unit receives 100% of the damage. Increasing the resistance by 1 means that he now takes 101%. So we need to reduce it by one, to get to 99%. It's the same with defense, but in that case it is the chance to hit the unit, on a specific terrain.
The last part is the modifcation of movement costs i mentioned before:
Code: Select all
[object]
silent=yes
[effect]
apply_to=movement_costs
replace=yes
[movement_costs]
shallow_water=1
swamp_water=2
deep_water=2
[/movement_costs]
[/effect]
[/object]
Door 10:
Now can combine everything we know so far. We want to add the headquarter of our mining company. We can use the same concept that we used for the blacksmith, but instead of checking for gold, we will work with variables such as 'ore'.
This is the concept for the store. I might change it, lateron.
Code: Select all
[label]
x=1
y=5
text="Headquarter"
[/label]
[event]
name=moveto
first_time_only=no
[filter]
x=1
y=5
[/filter]
[set_variable]
name=finished
value=no
[/set_variable]
[while]
[variable]
name=finished
equals=no
[/variable]
[do]
[message]
speaker=Dwarvenminerid
message= _ "Here I can process the ore and turn it into iron and upgrade my equipment.
<small>You have: $ore ore, $iron iron, $mithril mithril</small>"
[option]
image=icons/bag.png
message= _ "Improve capacity for 10 ore
<small>improved lores and equipment increases the amount of ore you can carry by 5, so far $capacity</small>"
[command]
[if]
[variable]
name=ore
greater_than_equal_to=10
[/variable]
[then]
[set_variable]
name=capacity
add=5
[/set_variable]
[set_variable]
name=ore
sub=10
[/set_variable]
[sound]
name=mace.ogg
[/sound]
[message]
speaker=Dwarvenminerid
message= _ "Done! I can now carry up to $capacity."
[/message]
[/then]
[else]
[message]
speaker=Dwarvenminerid
message= _ "Not enough ore!"
[/message]
[/else]
[/if]
[/command]
[/option]
[option]
image=attacks/fire-blast.png
message= _ "Process ore
<small>this will consume your ore, has a high chance to create iron and a small chance to create mithril</small>"
[command]
[sound]
name=flame-big.ogg
[/sound]
[while]
[variable]
name=ore
greater_than=0
[/variable]
[do]
[set_variable]
name=ore
sub=1
[/set_variable]
[set_variable]
name=ironchance
rand=0..100
[/set_variable]
[set_variable]
name=ironchance
add=$ironchancebonus
[/set_variable]
[if]
[variable]
name=ironchance
greater_than_equal_to=30
[/variable]
[then]
[set_variable]
name=iron
add=1
[/set_variable]
[/then]
[/if]
[set_variable]
name=mithrilchance
rand=0..100
add=$mithrilchancebonus
[/set_variable]
[if]
[variable]
name=mithrilchance
greater_than_equal_to=95
[/variable]
[then]
[set_variable]
name=mithril
add=1
[/set_variable]
[/then]
[/if]
[/do]
[/while]
[/command]
[/option]
[option]
[show_if]
[variable]
name=ironchancebonus
less_than_equal_to=30
[/variable]
[/show_if]
image=attacks/hammer.png
message= _ "Upgrade processing for 10 iron
<small>increases the chance to receive iron, slightly increases chance for mithril</small>"
[command]
[if]
[variable]
name=iron
greater_than_equal_to=10
[/variable]
[then]
[sound]
name=fire.wav
[/sound]
[set_variable]
name=ironchancebonus
add=3
[/set_variable]
[set_variable]
name=mithrilchancebonus
add=1
[/set_variable]
[/then]
[else]
[message]
speaker=Dwarvenminerid
message= _ "Not enough iron!"
[/message]
[/else]
[/if]
[/command]
[/option]
[option]
[show_if]
[variable]
name=ore
greater_than_equal_to=10
[/variable]
[/show_if]
image=icons/breastplate2.png
message= _ "Improve armor with ore parts
<small>receive 5 hitpoints, costs 10 ore</small>"
[command]
[sound]
name=flame-big.ogg
[/sound]
[set_variable]
name=ore
sub=10
[/set_variable]
[object]
silent=yes
[effect]
apply_to=hitpoints
increase_total=5
[/effect]
[/object]
[/command]
[/option]
[option]
[show_if]
[variable]
name=iron
greater_than_equal_to=10
[/variable]
[/show_if]
image=icons/breastplate2.png
message= _ "Improve armor with iron parts
<small>receive 5 hitpoints, +1 resistance, costs 10 iron</small>"
[command]
[sound]
name=flame-big.ogg
[/sound]
[set_variable]
name=iron
sub=10
[/set_variable]
[object]
silent=yes
[effect]
apply_to=hitpoints
increase_total=5
[/effect]
[effect]
apply_to=resistance
replace=false
[resistance]
blade=-1
pierce=-1
impact=-1
fire=-1
cold=-1
arcane=-1
[/resistance]
[/effect]
[/object]
[/command]
[/option]
[option]
[show_if]
[variable]
name=mithril
greater_than_equal_to=10
[/variable]
[/show_if]
image=icons/breastplate2.png
message= _ "Improve armor with mithril parts
<small>receive 1 movement point, costs 10 mithril</small>"
[command]
[sound]
name=flame-big.ogg
[/sound]
[set_variable]
name=mithril
sub=10
[/set_variable]
[object]
silent=yes
[effect]
apply_to=movement
increase=1
[/effect]
[/object]
[/command]
[/option]
[option]
image=icons/sandals.png
message= _ "Leave"
[command]
[set_variable]
name=finished
value=yes
[/set_variable]
[/command]
[/option]
[/message]
[/do]
[/while]
[/event]
Door 11 - 24 in the next post (due to text restrictions)
Developer of: Trapped, Five Fates, Strange Legacy, Epical, UR Epic Era
Dungeonmasters of Wesnoth, Wild Peasants vs Devouring Corpses, Dwarf Dwarfson Dwarvenminer
Re: Coding Advent Calendar II
Door: 11
Today we want to add an option to allow our dwarf to cast a spell. Something that will harm a unit and scorch the earth under it's feet.
And we could even use it to destroy walls in our way. First well need to change the event for Jonna, our Firemage.
And yes, Jonna is a reference to the fire mage in the game 'Stoneshard'.
So this is how you could design an npc, who maybe can give a quest or gives information to your hero.
Code: Select all
[label]
x=10
y=3
text="Talk"
[/label]
[event]
name=moveto
first_time_only=no
[filter]
x=10
y=3
[/filter]
[set_variable]
name=finished
value=no
[/set_variable]
[while]
[variable]
name=finished
equals=no
[/variable]
[do]
[message]
speaker=Jonna
message= _ "A mysterious fire mage."
[option]
image=dwarvenminer.png
message= _ "Talk"
[command]
[message]
speaker=Jonna
message= _ "Hi Dwarf. How are you?"
[/message]
[message]
speaker=Dwarvenminerid
message= _ "Hi Jonna. I'm fine. How about you? Did you already find friends here?"
[/message]
[message]
speaker=Jonna
message= _ "You know, Dwarves aren't known for their hospitality, at least not for strangers. Well there are some exceptions."
[/message]
[message]
speaker=Dwarvenminerid
message= _ "Thanks. Got to get back to mining, young lady. And maybe one day you will tell me about your past."
[/message]
[message]
speaker=Jonna
message= _ "I will. Good luck!"
[/message]
[/command]
[/option]
[option]
image=icons/sandals.png
message= _ "Leave"
[command]
[set_variable]
name=finished
value=yes
[/set_variable]
[/command]
[/option]
[/message]
[/do]
[/while]
[/event]
Code: Select all
[option]
[show_if]
[variable]
name=fireball
not_equals=yes
[/variable]
[/show_if]
image=attacks/fireball.png
message= _ "Can you teach me that fireball spell?
<small>Jonna wants to have 10 mithril for that.</small>"
[command]
[if]
[variable]
name=mithril
greater_than_equal_to=10
[/variable]
[then]
[message]
speaker=Jonna
message= _ "Vuur! <i>Jonna whispers a strange word into your ear. Your entire body is vibrating for a short moment. A word of power. The word for fire.</i>"
[/message]
[set_variable]
name=fireball
value=yes
[/set_variable]
[set_menu_item]
id=1
description="Fireball"
image=small/sharpshooter.png
[show_if]
[variable]
name=fireball
equals=yes
[/variable]
[/show_if]
[command]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-1.png"
[/item]
[delay]
time=30
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-2.png"
[/item]
[delay]
time=30
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-3.png"
[/item]
[delay]
time=30
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-4.png"
[/item]
[delay]
time=30
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-5.png"
[/item]
[delay]
time=120
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-6.png"
[/item]
[delay]
time=70
[/delay]
[item]
x,y=$x1,$y1
halo="halo/flame-burst-7.png"
[/item]
[delay]
time=70
[/delay]
[sound]
name=flame-big.ogg
[/sound]
[remove_item]
x,y=$x1,$y1
[/remove_item]
[harm_unit]
[filter]
x,y=$x1,$y1
[/filter]
amount=10
damage_type=fire
fire_event=yes
animate=yes
experience=yes
[/harm_unit]
[harm_unit]
[filter]
id=Dwarvenminerid
[/filter]
amount=5
damage_type=fire
fire_event=yes
animate=yes
experience=yes
[/harm_unit]
[terrain]
x,y=$x1,$y1
terrain=Rb
[/terrain]
[/command]
[/set_menu_item]
[set_variable]
name=mithril
sub=10
[/set_variable]
[/then]
[else]
[message]
speaker=Jonna
message= _ "If you could bring me this mithril for my research I will teach this spell to you!"
[/message]
[/else]
[/if]
[/command]
[/option]
Now we add on option to cast a fireball. This will cost mithril, which is a rare ingredient we can get when processing ore.
Here the explanation:
Code: Select all
[set_menu_item]
id=1 # this i the id of the menu, there can be only one 1. So make sure you have
description="Fireball" # this is the name that is shown in the menu
image=small/sharpshooter.png #
[show_if] #this is condition - only if the fireball variable is yes, this option is shown, we could have filtered for enemies as well
[variable] # so a heal spell would only show when i click on allied units and a damage spell only when I click on enemies
name=fireball
equals=yes
[/variable]
[/show_if]
[command] # after command we DO SOMETHING
[item] # I admit this is an odd way to make an animation but it works, I place an item there and then delete it after some time
x,y=$x1,$y1
halo="halo/flame-burst-1.png"
[/item]
[delay] # this is the delay until the next item will show up
time=30
[/delay]
[remove_item] # then we remove the items at the very end
x,y=$x1,$y1 # Wait Wait Wait! What is this. $x1 and $y1 are essential, these effect the current position
[/remove_item] # in the moment we click on a tile the variables x1 and y1 are set with the current position
# so you shouldn't use x1 and y1 in your own definition, except you want them to work on the current position
# current position - sorry couldn't resist
[harm_unit] # this one is new, it harms a unit,it can be combined with other events and can effect several units
[filter] # what units? this done with the filter - so here the unit on the current position
x,y=$x1,$y1
[/filter]
amount=10 # this is the amount of damage
damage_type=fire # this the type of damage, so a fire elemental with a high resistance to fire will take less damage,
#this is optional, if you don't define it you will do PURE damage
fire_event=yes # this will fire events such as last breath and dieing events
animate=yes # the unit will react as if it was attacked, cool if you ask me, but your decision
experience=yes # this is unnecessary here, but if the fireball would be part of a real "attack", the experience would be granted
[/harm_unit]
- Fireball.png (131.97 KiB) Viewed 1346 times
Door: 12
The version 0.3 has been released. Enjoy.
It comes with the following changes:
You can now process ore and produce iron with a low chance for mithril. You can upgrade this process by using iron, increasing your chances to produce both iron and mithril. As you can also upgrade the capacity of ore you can carry, I had to improve the 'Mine' level. The idea is that you return their and mine the same blocks of ore over and over again (well just for now, we will change that later).
There are two problems about this. First: we decided that our base capacity is 10 and that this is the mining goal. Even so we manage to increase it, we will be stuck in the level. That's why I created an exit which will take you back to the city.
So that you can return to the mine, I made following changes, inside the so called prestart event. Changes done by the 'event start' can be seen by the player. For example if you place units inside the start event, the player might see them appear, while in the prestart event, these units would be already there. One could say: with the prestart event we set the stage, with start event we start the show. So in the start event you might show the intro and sheer endless dialogues or monologues of your heroes.
Code: Select all
[event]
name=prestart
[if]
[variable] # this checks if the city was visited yet, if so it gives you the gold from the city, we saved in the event moveto
name=cityvisited # obviously, we have to give the player this variable once he enters the city
equals=yes
[/variable]
[then]
[modify_side]
side=1
gold=$stored_gold
[/modify_side]
[/then]
[/if]
[set_variable] # this sets our mining task (one could say the capacity of our miner)
name=miningtask
value=10
[/set_variable]
[if]
[variable] # this checkts if the capacity has been upgraded
name=capacity
greater_than=1
[/variable]
[then]
[set_variable] # if so, the capacity is added to the 'miningtask' which now seems like a bad name for this variable
name=miningtask
add=$capacity
[/set_variable]
[/then]
[/if]
[objectives]
side=1
summary=_"final objectives:"
[objective]
description= _ "Mine more than $miningtask ore" # I've changed this one here. if no upgrade is done, it will still be 10
condition=win
[/objective]
[objective]
description= _ "Death"
condition=lose
[/objective]
[/objectives]
[/event]
Code: Select all
I added this little code to our movement event for mining. So our dwarf will now perform an attack animation when he mines.
Looks pretty cool to me and fortunately he is sharpening that axe to cut through the stone.
[animate_unit]
flag=attack
with_bars=no
[filter]
id=Dwarvenminerid
[/filter]
[primary_attack]
name=battle axe
range=melee
[/primary_attack]
hits=yes
[facing]
[filter]
x={minex}
y={miney}
[/filter]
[/facing]
[/animate_unit]
Code: Select all
[option]
image=attacks/fangs-animal.png
message= _ "Cheat?
<small>You receive 10 mithril.</small>"
[command]
[message]
speaker=Dwarvenminerid
[text_input] # this allows the player to input text
variable=password # this is the variable the number or text is saved in
label="Password (Hint - Spaceballs):" # this is the label which is shown to the player
text= # this could be a predefined text, for example password in our case
[/text_input]
[/message]
[if]
[variable]
name=password # this is where we check the password
equals=12345 # this is what we want the password to be
[/variable]
[then]
[set_variable]
name=mithril
add=10
[/set_variable]
[/then]
[else]
[message]
speaker=Dwarvenminerid
message= _ "Not the correct password!"
[/message]
[/else]
[/if]
[/command]
[/option]
But maybe a terrain first and make scaling costs for the upgrades. Have fun.
Door: 13
Today we will make a small task, but nevertheless time consuming. We create a new terrain, a vein.
The code changes for that are easy.
We change the file terrain-definitions.cfg inside of the 'terrain' folder.
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
[terrain_type]
# symbol_image is the image shown in the editor's tile palette.
# This image is looked for in the terrain/ subdirectory of the images/ subdirectories specified by all active [binary_paths]. These are data/core/ (always active) and data/add-ons/Dwarf_Dwarfson_Dwarvenminer/ for this campaign.
# ".." however tells to step one directory up (from terrain/ into images/) (and then into images/scenery).
symbol_image="../scenery/gate-rusty-sw"
id=asc-custom-terrain
name=_ "ASC's custom terrain"
string=^Xzg
aliasof=Xt
editor_group=ddd
[/terrain_type]
[terrain_type]
#for this example we don't have to move to scenery, we can stay inside of terrain
symbol_image="vein"
id=vein
name=_ "Vein"
string=Xue^Vei
aliasof=Xue
editor_group=ddd
[/terrain_type]
[terrain_type]
#this is only an overlay, while the type above, will place an terrain Xue as well
symbol_image="vein"
id=vein2
name=_ "Vein Overlay"
string=^Vei
aliasof=Xue
editor_group=ddd
[/terrain_type]
We also have to change the terrain-graphics.cfg inside the terrain folder.
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
# This macro is for a terrain that fits on a single tile (and its graphics must do too, or the edges will be clipped).
# For more complex tiles, see the terrain tutorial thread: http://forums.wesnoth.org/viewtopic.php?f=21&t=34201
#define ASC_SINGLE_HEX TERRAIN_CODE FLAG TILE_IMAGE
[terrain_graphics]
[tile]
x=0
y=0
type={TERRAIN_CODE}
set_no_flag={FLAG}
[image]
name={TILE_IMAGE}
[/image]
[/tile]
[/terrain_graphics]
#enddef
# This custom terrain is an overlay, because the terrain-type starts with ^
# The text "overlay" is just to stop other overlays drawing on the same hex
{ASC_SINGLE_HEX (*^Xzg) "overlay" "door-sw.png"}
# Here's a simpler way to do the same thing, using a core macro:
# {OVERLAY *^Xzg (door-sw)}
#undef ASC_SINGLE_HEX
{OVERLAY Xue^Vei (vein)}
{OVERLAY *^Vei (vein)}
Code: Select all
{OVERLAY Xue^Vei (vein)}
{OVERLAY *^Vei (vein)}
But now you say: "Heindal! You good looking villian! How does the map editor knows about these terrain files?"
Now Now! Watch your tone! We already defined that in the main.cfg.
Code: Select all
#ifdef EDITOR
{~add-ons/Dwarf_Dwarfson_Dwarvenminer/terrain}
[binary_path]
path=data/add-ons/Dwarf_Dwarfson_Dwarvenminer/
[/binary_path]
[editor_group]
id=ddd
name= _ "Dwarf Dwarfson Dwarvenminer"
icon="group_custom"
[/editor_group]
#endif
editor_group=ddd
So this is the basic idea. However, the first example will not work as we have intended it. It will place the terrain and than place an overlay over it.
See here:
- terrain.png (57.27 KiB) Viewed 1321 times
Door: 14
The add-on works, but the mining is a little bit slow. Moving to an 'exact' location, which is shown with a label is working, but it feels odd. Furthermore we will have a problem, once we make 'random' dungeons. We would need to place veins and than we would place an movement event next to it. Even so we could do so, it would be complicated and requires a lot of work with either arrays or a lot of calculation, which would make it likely that we do mistakes on the way. We should rework our concept (I admit that this was the idea right from the start).
So - here is the idea. We make it possible to walk on the vein and then start an event, if the vein contains the terrain *^Vei.
Just in case: * stands is a wildcard and stands for any other terrain. (by the way you can use that not only in wesnoth, it is wildly used in any content or editorial management systems and databases). So we could use the two terrain types we defined yesterday.
I've changed Xue^Vei into Ur^Vei and aliasof=Xue to aliasof=Ur. This will allow us to walk on the vein, as the new terrain will behave like Ur.
We could also have used Gg for green gras to create a walk-able terrain.
Code: Select all
[terrain_type]
#for this example we don't have to move to scenery, we can stay inside of terrain
symbol_image="vein"
id=vein
name=_ "Vein"
string=Ur^Vei
aliasof=Ur
editor_group=ddd
[/terrain_type]
Code: Select all
#define MINE_EVERYTHING
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[filter_condition]
[have_location]
terrain=*^Vei
[and]
x=$x1
y=$y1
[/and]
[/have_location]
[/filter_condition]
[animate_unit]
flag=attack
with_bars=no
[filter]
id=Dwarvenminerid
[/filter]
[primary_attack]
name=battle axe
range=melee
[/primary_attack]
hits=yes
[facing]
[filter]
x=$x2
y=$y2
[/filter]
[/facing]
[/animate_unit]
[sound]
name=mace.ogg
[/sound]
[terrain]
x=$x1
y=$y1
terrain=Ur
[/terrain]
[set_variable]
name=amountore
rand=0..5
[/set_variable]
[set_variable]
name=amountgold
rand=0..2
[/set_variable]
[gold]
side=1
amount=$amountgold
[/gold]
[set_variable]
name=ore
add=$amountore
[/set_variable]
[print]
text="You found $amountore ore and $amountgold gold!"
size=30
duration=500
red,green,blue=155,0,0
[/print]
[/event]
#enddef
You can read more about this here: https://wiki.wesnoth.org/EventWML#.5Bfilter.5D
Maybe we could make it different or maybe even easier. But it works and I'm happy for now. However, this event will give us some great opportunities to make other macros, such as 'digging' when a wall is in our way, without placing any events things manually! We could theoretically make a farming game, where you dig and water the soil and plant plants inside the prepared soil. Or a street paving game or something.
One last thing you might wonder about: $x2 and $y2 is the location, where my unit started his movement. I had to use this for our animation, as the animation will just allow us to 'attack' a different location. Alternatively we could have changed the terrain we left, leaving a lava field behind (imagine a fire elemental which does that). Or even could change all terrains from $x1 to $x2 and $y1 to $y2. (we could think about an alien that leaves acid behind, or a super cool assassin who had a 'tectonic shock armor' and would scatter all the terrains behind her - yeah that's an idea from an older project)
This has so much potential. Hope you will like this one.
Door: 15
Let's make a random dungeon. This concept is called 'Cheese Hole Concept' and I developed it for this calendar and this specific situation.
A mine would require digging. How can we do so? We will just change our macro from yesterday, so that our miner will react on Xu terrain.
If he has Xu terrain nearby, he will destroy it. We will adapt our code from yesterday slightly.
Code: Select all
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[filter_condition]
[have_location]
terrain=Xu
[and]
x=$x1
y=$y1
radius=1
[/and]
[/have_location]
[/filter_condition]
[animate_unit]
flag=attack
with_bars=no
[filter]
id=Dwarvenminerid
[/filter]
[primary_attack]
name=battle axe
range=melee
[/primary_attack]
hits=yes
[facing]
[filter]
x=$x2
y=$y2
[/filter]
[/facing]
[/animate_unit]
[sound]
name=mace.ogg
[/sound]
[terrain]
x=$x1
y=$y1
terrain=Ur
radius=1
[/terrain]
[/event]
Code: Select all
[set_variable]
name=orelocations
rand=30..50
[/set_variable]
[while]
[variable]
name=orelocations
greater_than=0
[/variable]
[do]
[set_variable]
name=orelocationx
rand=1..44
[/set_variable]
[set_variable]
name=orelocationy
rand=1..33
[/set_variable]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ur
radius=1
[/terrain]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ur^Vei
[/terrain]
[set_variable]
name=orelocations
sub=1
[/set_variable]
[/do]
[/while]
What if there are monsters as well? We will look into this tomorrow.
Door: 16
In the same way we placed our little vein pockets inside our mine, we could place monster nests as well. We could use a while loop, dice a random number of monster nests and check if all the monster nests are in place. It's ok when a monster nest will replace the ore we placed before, or if it will be spawned next to it. It even increases the different combinations that are possible. Note: because we placed the monster nests after the ore, the ore will be gone, if we dice the same coordinates for our monsternest.
Using:
[set_variable]
name=monstertype
rand=Giant Ant,Giant Rat,Mudcrawler,Skeleton,Skeleton Archer,Walking Corpse,Fire Ant,Rock Scorpion,Giant Spider
[/set_variable]
We can now place random monster, by adding the monstertype variable to our units as monstertype.
[unit]
side=2
type=$monstertype
id=Monster$monsternestx$monsternesty|1 #as you see this a little bit more complicated, i use this to create a unique id
# As there can be only on monster with the same id. There is small chance that this will not work, as a variable can be diced two times in a row.
# In that case it should replace the monster that was placed before, but I am not sure about that. Might have bug potential later.
name= _ "$monstertype"
x=$monsternestx
y=$monsternesty
[/unit]
We use radius=2 inside of terrain to make even a greater 'cheese hole' so that our several minions of evil have some space. Radius can not only be used together with terrain, but also for filters for actions (such as the fireball) or it can be used for animation, for example to play the same animation at the same spot. (Lightning Storm ^^). The center of our monster nest will be a nest filled with eggs (so that is why the monsters attack you). We will use the tag [item] to place an image. In some cases such as undeads a nest doesn't make sense, so we will place a mausoleum there, in case of a giant rat we will go with a trash pile and swamp for the mudcrawlers. So here is the code for that:
Code: Select all
[set_variable]
name=monsternests
rand=2..5
[/set_variable]
[while]
[variable]
name=monsternests
greater_than=0
[/variable]
[do]
[set_variable]
name=monsternestx
rand=1..44
[/set_variable]
[set_variable]
name=monsternesty
rand=1..33
[/set_variable]
[terrain]
x=$monsternestx
y=$monsternesty
terrain=Ur^Edb
radius=2
[/terrain]
[set_variable]
name=monstertype
rand=Giant Ant,Giant Rat,Mudcrawler,Skeleton,Skeleton Archer,Walking Corpse,Fire Ant,Rock Scorpion,Giant Spider
[/set_variable]
[set_variable]
name=imagenest
value=scenery/nest-full.png
[/set_variable]
[if]
[variable]
name=monstertype
equals=Skeleton
[/variable]
[or]
[variable]
name=monstertype
equals=Skeleton Archer
[/variable]
[/or]
[or]
[variable]
name=monstertype
equals=Walking Corpse
[/variable]
[/or]
[then]
[set_variable]
name=imagenest
value=scenery/mausoleum.png
[/set_variable]
[/then]
[/if]
[if]
[variable]
name=monstertype
equals=Mudcrawler
[/variable]
[then]
[set_variable]
name=imagenest
value=scenery/rubble.png
[/set_variable]
[/then]
[/if]
[if]
[variable]
name=monstertype
equals=Giant Rat
[/variable]
[then]
[set_variable]
name=imagenest
value=scenery/trash.png
[/set_variable]
[/then]
[/if]
[item]
x=$monsternestx
y=$monsternesty
image=$imagenest
[/item]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx$monsternesty|1
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx$monsternesty|2
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx$monsternesty|3
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[set_variable]
name=monsternests
sub=1
[/set_variable]
[/do]
[/while]
Door: 17
We have a mine, that now spawns monster nests and little pockets with ore. It would be cool if there was more. How about that our dwarf could find ancient ruins, a treasure chamber or an underground see of water or lava? Maybe there could be some unique guardians, which guard this spots?
You can do so by using a while loop as before and dicing a variable for a random map part that will be placed. We could do so using a great amount of "if then" condition, but that is where a "switch" comes in handy, my hard coding amigo! A switch will deliver different results and safe us some pieces of code. But how do we place terrains over an existing map. This is done with [terrain_mask].
Now lets review the code:
Code: Select all
[set_variable]
name=biome #please note, we will make real 'biomes' later
rand=2..4
[/set_variable]
[while]
[variable]
name=biome
greater_than=0
[/variable]
[do]
[set_variable]
name=biometype #
rand=river,prison
[/set_variable]
[set_variable]
name=biomex
rand=1..30
[/set_variable] # we do this to avoid that the biomes are placed at the border of the map
[set_variable]
name=biomey
rand=1..20
[/set_variable] # we do this, to avoid that the biomes spawn to close to the starting point
[switch]
variable=biometype
[case]
value="river"
[terrain_mask]
x,y=$biomex,$biomey
mask="border_size=1
usage=map
Wwrg, Wog, Wwrg, Wog, Wwrg, Wog, Wwg, Wog, Wwg, Wog, Wwg, Wog, Wwg, Wog, Wwg, Qxe, Qxe
Wwrg, Wwrg, Wwrg, Wwrg, Wwg, Wwrg, Wwr, Wwg, Wwg, Wwg, Wwg, Wwg, Wwg, Wwg, Wwg, Wwg, Qxe
Wog, Wwrg^Ii, Wog, Wwg, Wog, Wwt, Wwr, Wwt, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Qxe
Wog, Wog^Ii, Wog, Wog, Wog, Wog, Wwr, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Qxe
Wog, Wog^Ii, Wog, Wog, Wog, Wog, Wwr, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Qxe
Ww, Wog^Ii, Wog, Wog, Wog, Wog, Wwt, Wwr, Wwt, Wog, Wog, Wog, Wog, Wog, Wog, Wog, Qxe
Ww, Wog^Ii, Wwg, Wog, Wwg, Wwt, Wwg, Wwr, Wwg, Wwt, Ww, Wog, Ww, Wog, Wwrg, Wwrg, Qxe
Wog, Wwg, Wwg, Wwg, Wwg, Wwg, Wwg, Wwr, Wwg, Wwg, Wwg, Wwg, Wwg, Wwrg, Wwg, Qxe, Qxe"
[/terrain_mask]
[/case]
[case]
value="prison"
[terrain_mask]
x,y=$biomex,$biomey
mask="border_size=1
usage=map
Xue, Xue, Xue, Xue, Xue, Xue, Xue, Xue, Xue, Xue, Xue
Xue, Xos, Xos, Xos, Xos, Xos, Xos, Xos, Xos, Xue, Xue
Xue, Xos, Uue^Edb, Uue^Edb, Ur^Edb, Xos, Ur^Edb, Uu^Ii, Uu^Ii, Xos, Xue
Xue, Xos, Uue^Edb, Ur^Edb, Ur^Edb, Xos, Xos, Ur^Edb, Uu^Edb, Xos, Xue
Xue, Xos, Uue^Edb, Ur^Edb, Ur^Edb, Ur, Rr^Pr/, Xoc, Uu^Edb, Xos, Xue
Xue, Xos, Wog, Ur^Edb, Xos, Xos, Xoc, Ur^Edb, Uu^Edb, Xos, Xue
Xue, Xos, Xos, Xos, Xos, Xos, Xos, Uu^Edb, Xu, Xos, Xue
Xue, Xos, Xos, Xos, Xos, Xos, Xos, Xos, Xu, Xos, Xue
Xue, Xue, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Xue, Xue"
[/terrain_mask]
[/case]
[/switch]
So we define another variable called enemyspawnx and enemyspawny and use them, by feeding them the variables biomex and biomey.
This way we can place the monsters where we want them.
Code: Select all
[set_variable]
name=enemyspawnx
value=$biomex
add=8
[/set_variable]
[set_variable]
name=enemyspawny
value=$biomey
add=2
[/set_variable]
[unit]
side=2
type=Tentacle of the Deep
id=Tentacle$biomex|$biomey
name=_ "Tentacle"
x,y=$enemyspawnx,$enemyspawny
[/unit]
[unit]
side=2
type=Tentacle of the Deep
id=Tentacle$biomex|$biomey|2
name=_ "Tentacle"
x,y=$enemyspawnx,$enemyspawny
[/unit]
[unit]
side=2
type=Tentacle of the Deep
id=Tentacle$biomex|$biomey|3
name=_ "Tentacle"
x,y=$enemyspawnx,$enemyspawny
[/unit]
Door: 18
Let's look into one of these biomes, where I placed an event and a monster.
Yeah, its a paw. Or a dragon claw. It's also a dragon nest. As you see, to place the dragon and his loot able hoard, we will need to add 3 to y and 4 to x.
Code: Select all
[case]
value="dragonhoard"
[terrain_mask]
x,y=$biomex,$biomey
mask="border_size=1
usage=map
Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf
Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf, Qlf
Qlf, Qlf, Ql, Ql, Ql, Ql, Ql, Ql, Ql, Qlf, Qlf
Qlf, Ql, Ur^Edb, Qlf, Ur^Edb, Qlf, Ur^Edb, Qlf, Ur^Edb, Ql, Qlf
Qlf, Ql, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edt, Ur^Edb, Ur^Edb, Ur^Edb, Ql, Qlf
Qlf, Ql, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ql, Qlf
Qlf, Ql, Ql, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ur^Edb, Ql, Ql, Qlf
Qlf, Qlf, Qlf, Ql, Ur, Ur^Edb, Ur, Ql, Qlf, Qlf, Qlf
Qlf, Qlf, Qlf, Qlf, Ur, Ur, Ur, Qlf, Qlf, Qlf, Qlf"
[/terrain_mask]
[set_variable]
name=enemyspawnx
value=$biomex
add=4
[/set_variable]
[set_variable]
name=enemyspawny
value=$biomey
add=3
[/set_variable]
[unit]
side=2
type=Fire Dragon
id=FireDragon$biomex|$biomey
name=_ "Fire Dragon"
x,y=$enemyspawnx,$enemyspawny
ai_special=guardian
[/unit]
[item]
x=$enemyspawnx
y=$enemyspawny
image=items/gold-coins-large.png
[/item]
[event]
name=moveto
first_time_only=yes
delayed_variable_substitution=no
[filter]
id=Dwarvenminerid
x=$enemyspawnx
y=$enemyspawny
[/filter]
[print]
text="GOLD!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[gold]
side=1
amount=50
[/gold]
[remove_item]
x=$enemyspawnx
y=$enemyspawny
image=items/gold-coins-large.png
[/remove_item]
[/event]
[/case]
While our movement event that checks the veins, is working fine, these movement events have some kind of a problem. If you place them without any further details, all movements event will be triggered, once you fire one of them. This means: having a trap that harms the unit and a treasure chest placed like this, would cause the trap and the treasure chest movement event at the same time. To prevent this we have used: delayed_variable_substitution=no. For a long period of time I did not use or kind of ignored it, but it is very useful to separate events.
Hey, what is this "ai_special=guardian" you have used in the unit? It will make the dragon wait until he sees and enemy. Maybe you have played campaigns where you have to sneak around, and guards did not notice you. This units this special guardian, which tells them to hold their ground. Otherwise the dragon will wander around. Like a lot. I hated it in my first tests, as soon as you've entered a greater cave, the dragon came. That is nasty, as you are not supposed to defeat it early in the game. Tomorrow, our maybe even today I will publish the new version, so you can test this for yourself.
Door: 19
Wouldn't it be cool to have leaders in the dungeon that could recruit units and act a little bit more flexible? Maybe a random faction that starts to recruit and fight, maybe even on your side. One campaign that have the code for this, was Descent Into Darkness, Final Chapter: Endless Night. Here you have sides, that have no leader. Than there is be a random generator and rename the empty faction and fill it with details such as a unit for a leader and possible recruits. We could this here too, but only a part of it. As we have biomes we can place, we could place a goblin village inside our dungeon.
The goblins could even be friendly and help us to clear the dungeon? Ok, what do we do? We need at least one additional side, which is a side allied with us. We could also add another side, of an enemy that will fight the monsters in the dungeon as well.
Let's start with the sides:
Code: Select all
[side]
side=1
controller=human
team_name=1
user_team_name= _ "Dwarf Dwarfson"
type=Dwarvish Explorer
id=Dwarvenminerid
name=_"Dwarf Dwarfson"
gender=male
shroud=yes
fog=yes
canrecruit=yes
unrenamable=yes
recruit=
village_gold=0
income=-2
[/side]
[side]
side=2
controller=ai
team_name=2
user_team_name= _ "Hisses in the Dark"
id=Monster
name=_"Monster"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
recruit=
[ai]
agression=1
caution=0
village_value=0.0
grouping=no
simple_targeting=yes
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_movements yes}
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_combat yes}
{AI_SIMPLE_ALWAYS_ASPECT aggression 1.0}
{AI_SIMPLE_ALWAYS_ASPECT caution 0}
[/ai]
[/side]
[side]
side=3
controller=ai
team_name=3
user_team_name= _ "An underground facility"
id=Sentient
name=_"Sentient"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
recruit=
[ai]
agression=1
caution=0
village_value=0.0
grouping=no
simple_targeting=yes
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_movements yes}
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_combat yes}
{AI_SIMPLE_ALWAYS_ASPECT aggression 1.0}
{AI_SIMPLE_ALWAYS_ASPECT caution 0}
[/ai]
[/side]
[side]
side=4
controller=ai
team_name=1
user_team_name= _ "Allied"
id=Allied
name=_"Allied"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
[/side]
Please note these changes for side one:
shroud=yes (the map is hidden, this makes sense in a mine to and allows us to dig into a dragon hoard, accidently. Which is part of the fun.)
fog=yes (says that this unit has a fog of war - this is optional, but makes sense in a mine, that your vision is limited)
village_gold=0 (conquered gold is defined here, we will receive 0 income for villages)
income=-2 (starting income is usually 2 gold, but we want our dwarf to earn his bread by hard work, not by ending turns)
The other sides, have "no_leader=yes", which means no leader is spawned. Monsters in the dungeons usually don't have a leader.
Side 2 is taken by the monsters, Side 3 is a potential enemy, which will fight the you and the monsters as well.
Side 4 is our allied faction. It is allied because it has the team_name=1 attribute like our side 1.
Maybe you have noticed the [ai] part inside of Side 2 and Side 3. This makes this ai super aggressive and it will sacrifice units, even so it makes no tactical sense, such as attacking you, even so your are about to get a level up and fully healed.
Now let's place a map part for our goblin village:
Code: Select all
[case]
value="goblinvillage"
[terrain_mask]
x,y=$biomex,$biomey
mask="border_size=1
usage=map
Qxe, Qxe, Qxe, Qxe, Qxe, Qxe, Ur, Ur, Ur, Qxe, Qxe, Qxe, Qxe, Qxe, Qxe, Qxe, Qxe
Qxe, Qxe, Ur^Edt, Ur^Edt, Uu^Vu, Uue, Cer, Cer, Cer, Uue, Wwg, Qxe, Tb^Tf, Qxe, Qxe, Qxe, Qxe
Qxe, Qxe, Uu^Vu, Uue, Uue, Uue, Uue, Uue, Uue, Uue, Wwg, Tb^Tf, Tb^Tf, Tb^Tf, Qxe, Qxe, Qxe
Qxe, Qxe, Uue, Uue, Uue, Uue, Ur, Uue, Ur, Uue^Em, Ur^Emf, Wwg, Wwg, Tb^Tf, Tb^Tf, Qxe, Qxe
Ur, Cer, Uue, Uue, Uue, Uue, Ur, Ur, Ur^Edb, Ur^Em, Ur^Em, Ur^Emf, Ur^Emf, Wwg^Ii, Tb^Tf, Qxe, Qxe
Ur, Cer, Uue, Uue, Uue, Ur^Edt, Ur^Edb, Ur^Edb, Ur^Ecf, Ur^Edb, Ur, Ur^Emf, Ur^Emf, Ur^Emf, Uu^Vu, Qxe, Qxe
Qxe, Cer, Uue, Uue, Ur^Edt, Ur^Edt, Ur^Edt, Ur^Edb, Ur^Edb, Ur^Edb, Uu, Ur, Ur, Ur^Em, Ur^Em, Qxe, Qxe
Qxe, Qxe, Uu^Vu, Uue, Ur, Ur^Esd, Ur^Edt, Ur^Edt, Uu, Uu, Cer, Uu, Uu, Ur^Em, Uu^Vu, Qxe, Qxe
Qxe, Qxe, Qxe, Uue, Uue, Ur^Esd, Ur^Edt, Ur^Edt, Uu, Cer, Ker, Cer, Uu, Ur^Edt, Ur^Edt, Qxe, Qxe
Qxe, Qxe, Qxe, Uu^Vu, Uue, Ur^Edt, Ur^Edt, Ur^Edt, Uu, Cer, Cer, Cer, Uu, Ur^Edt, Qxe, Qxe, Qxe
Qxe, Qxe, Qxe, Qxe, Qxe, Uu^Vu, Uu, Uu, Uu, Uu, Uu, Uu, Ur, Uu^Vu, Qxe, Qxe, Qxe
Ur, Qxe, Ur, Qxe, Qxe, Qxe, Qxe, Uu, Ur, Uu, Ur, Uu, Qxe, Qxe, Qxe, Qxe, Ur"
[/terrain_mask]
[set_variable]
name=enemyspawnx
value=$biomex
add=9
[/set_variable]
[set_variable]
name=enemyspawny
value=$biomey
add=7
[/set_variable]
[set_variable]
name=alliedornot
rand=3,4
[/set_variable]
[unit]
side=$alliedornot
type=Goblin Rouser
id=Goblinelder$biomex|$biomey
name=_ "Goblin Elder"
x,y=$enemyspawnx,$enemyspawny
canrecruit=yes
[/unit]
[unit]
side=$alliedornot
type=Goblin Spearman
id=GoblinSpearman$biomex|$biomey
name=_ "Goblin Spearman"
x,y=$enemyspawnx,$enemyspawny
[/unit]
[unit]
side=$alliedornot
type=Goblin Spearman
id=GoblinSpearman$biomex|$biomey2
name=_ "Goblin Spearman"
x,y=$enemyspawnx,$enemyspawny
[/unit]
[modify_side]
side=$alliedornot
user_team_name= _ "Goblin Village"
flag=
flag_icon=
color=brown
recruit=Goblin Spearman
[/modify_side]
[set_variable]
name=biome
sub=3
[/set_variable]
[/case]
So this is the code, that is working - at least in 1.16 and 1.14.
Usually you would spawn a new faction like this:
Code: Select all
[unit]
side=4
type=Goblin Rouser
id=Goblinelder$biomex|$biomey
name=_ "Goblin Elder"
x,y=$enemyspawnx,$enemyspawny
canrecruit=yes
[/unit]
[modify_side]
side=4
user_team_name= _ "Goblin Village"
flag=
flag_icon=
color=brown
recruit=Goblin Spearman
[/modify_side]
That's the explanation for this:
[set_variable]
name=biome
sub=3
[/set_variable]
The new version of Dwarf Dwarfson Dwarvenminer is now available and ready for a test run. I've added many ways to test, such as improved the cheating in the headquarter, infinite movement to quickly, you can clear the shroud and the fog and re-dice a dungeon to test the random dungeon levels and encounters. Note that some features aren't part of this add-on yet, such as upgrading fireballs, bounty hunter licence and the ability to unlock new dungeons.
Door: 20
Making the add-on perfect, means to add things to make it comfortable for the players. I added infinite movement, so let us have a look into this.
We will need a move to event. And yes, it's move to again. I think it is one of the most important events in this game. Even so you want to have a standard campaign, you you will need it for everything that is not the standard defeat all enemy units or leaders. But let's get back to the topic of infinite movement.
Code: Select all
#infinite movement
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[store_unit] # store unit has been there before, when I started with wml it stores a unit matching the filter
kill=no # fortunately the unit isn't killed
variable=temp # this is the variable our unit is saved, we could have named it hero or abc
[filter]
id=Dwarvenminerid # this is the filter, we've used that often already, i our case a specific unit with it's id
[/filter]
[/store_unit]
[set_variable] # this sets the variable as we know
name=temp.moves # but what we do here, is the change the variable in an object temp (our unit)
to_variable=temp.max_moves # temp moves is the number of movement left, max moves is the maximum number of movements of this unit
[/set_variable] # just setting this won't change a thing
[unstore_unit] # we need to unstore the unit, so that the changes have an effect.
find_vacant=no
variable=temp
[/unstore_unit]
[clear_variable] # after that we clear or delete the variable, just to be sure
name=temp
[/clear_variable]
[/event]
This little device allows us, to make infinite movements without ending the turn. We however have the problem that BoW is a turn based system. So when our hero encounters a unit, who would need to end this infinite movement and turn it back on when he is out of danger. We could add a condition with if then and set to true once our hero sees an enemy unit. Than we engage combat, which means no more infinite moves .
Code: Select all
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[if] # this it the condition
[variable]
name=encounter
not_equals=yes
[/variable]
[then]
[store_unit]
kill=no
variable=temp
[filter]
id=Dwarvenminerid
[/filter]
[/store_unit]
[set_variable]
name=temp.moves
to_variable=temp.max_moves
[/set_variable]
[unstore_unit]
find_vacant=no
variable=temp
[/unstore_unit]
[clear_variable]
name=temp
[/clear_variable]
[/then]
[/if]
[/event]
[event]
name=sighted # this event is new, it will be active, once a side sees another side.
first_time_only=yes
[filter]
side=2,3 # for some reason we have to do it like this, maybe because only side 1 has shroud and fog activated
[/filter]
[filter_second]
side=1
[/filter_second]
[set_variable]
name=encounter
value=yes
[/set_variable]
[print]
text="COMBAT!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[/event]
[event]
name=sighted
first_time_only=yes
[filter]
side=4
[/filter]
[filter_second]
side=1
[/filter_second]
[print]
text="Hej! Nice to see a friendly face in these parts!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[/event]
Door: 21
Small games, like DDD, usually have an upgrade mechanic. For each upgrade the cost will increase. We want to allow the player to upgrade as much as he wants. With the time those upgrades have a greater value for the player and give him an objective.
We already have a lot of upgrades, but to realize scaling cost we must replace the costs (no matter if mithril or gold) with an variable.
Let's have look:
Code: Select all
[option]
image=attacks/axe-crude.png
message= _ "Improve Throwing Axes
<small>increase your ranged damage by 1 for <b>$taxedmgup gold</b></small>"
[command]
[if]
[variable]
name=gold
greater_than_equal_to=$taxedmgup
[/variable]
[then]
[message]
speaker=Blacksmith
message= _ "Done! It's a pleasure making business with you!"
[/message]
[gold]
side=1
amount=-$taxedmgup
[/gold]
[object]
silent=yes
[effect]
apply_to=attack
name=axe
increase_damage=1
[/effect]
[/object]
[set_variable]
name=taxedmgup
multiply=2
[/set_variable]
[/then]
[else]
[message]
speaker=Blacksmith
message= _ "Sorry, you do not have enough gold!"
[/message]
[/else]
[/if]
[/command]
[/option]
[set_variable]
name=taxedmgup
multiply=2
[/set_variable]
Another important thing is, that we have to define a starting value for our costs. This is could be done, when we enter the city for the first time, or in another scenario. Variables always keep over all scenarios. Fortunately, so our fireball was saved, or that we had bought a pickaxe. Players of the version 0.3 already know, that I've turned our first level into a tutorial level. So I defined the starting costs in this scenario.
[set_variable]
name=taxedmgup
value=10
[/set_variable]
What happens if you don't define a variable: than this variable isn't shown, until it receives a value or their is an addition (+1) or subtraction (-1).
Than it will get the value (+1 or -1). Another idea is to define the variable in an event. For example, when buying the pick-axe I could have defined the cost for the upgrades. This concept is elegant and will just define variables that are needed. I did so in 0.3 concerning the earthslam attack.
However I now added the scaling costs to every upgrade in the game and even created a way to improve the fireball attack. The principles are the same. Usually the cost are multiplied with 2, in some games the rises are more forgiving such as 1.5 and in some games even 3 to prevent the player from getting to strong in one field early on. If you want to use scaling costs in your game project, you have to decide what the game should be like.
Door: 22
We need a little action here. What would be if you could plunder mine carts, or monster nest?
How about shrines that reveal the map or heal us. There should definetly be more artifacts to loot.
Hey, and we need more different veins! Mithril, Diamond, Goldveins!
Now could do that the complicated way: We could do place events in the cfg scenario.
But hey! The veins can be placed with a terrain and we can make our miner react on it.
Couldn't we make temples, artifacts, minecart terrains and tell our miner to react on those terrains, too?
So we could use the map editor to place events!
We just need to adjust this "MINE_EVERYTHING"
Code: Select all
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[filter_condition]
[have_location]
terrain=*^MRF
[and]
x=$x1
y=$y1
[/and]
[/have_location]
[/filter_condition]
[sound]
name=club-miss.ogg
[/sound]
[terrain]
x=$x1
y=$y1
terrain=Ur^MCR
[/terrain]
[set_variable]
name=ore
add=5
[/set_variable]
[print]
text="You found 5 ore!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[/event]
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[filter_condition]
[have_location]
terrain=*^SHR
[and]
x=$x1
y=$y1
[/and]
[/have_location]
[/filter_condition]
[sound]
name=magic-holy-1.ogg
[/sound]
[object]
silent=yes
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/object]
[print]
text="You've been healed!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[/event]
[event]
name=moveto
first_time_only=no
[filter]
id=Dwarvenminerid
[/filter]
[filter_condition]
[have_location]
terrain=*^GSH
[and]
x=$x1
y=$y1
[/and]
[/have_location]
[/filter_condition]
[sound]
name=magic-holy-1.ogg
[/sound]
[modify_side]
side=1
shroud=no
[/modify_side]
[print]
text="The map is revealed!"
size=30
duration=2000
red,green,blue=155,0,0
[/print]
[/event]
Code: Select all
changes to terrain-definitions.cfg
[terrain_type]
symbol_image="minecart_full"
id=minecart4
name=_ "Minecart full"
string=^MFU
aliasof=Ur
editor_group=ddd
[/terrain_type]
[terrain_type]
symbol_image="minecart"
id=minecart2
name=_ "Minecart"
string=^MCG
aliasof=Ur
editor_group=ddd
[/terrain_type]
[terrain_type]
symbol_image="shrine"
id=shrine
name=_ "Dwarven Shrine"
string=^SHR
aliasof=Ur
editor_group=ddd
[/terrain_type]
[terrain_type]
symbol_image="gshrine"
id=gshrine
name=_ "Geomanty Shrine"
string=^GSH
aliasof=Ur
editor_group=ddd
[/terrain_type]
changes to: terrain-graphics.cfg
{OVERLAY *^MCG (minecart)}
{OVERLAY *^MFU (minecart_full)}
{OVERLAY *^SHR (shrine)}
{OVERLAY *^GSH (gshrine)}
You could design objects such as staffs, weapons etc., which than react on the players.
It has downside however, you have to change the terrain, which isn't always wanted.
Even so you place just an overlay, it will delete other terrain overlays (such as flowers, fences etc.) on that spot.
These are the reactive terrains I've made for ddd:
Developer of: Trapped, Five Fates, Strange Legacy, Epical, UR Epic Era
Dungeonmasters of Wesnoth, Wild Peasants vs Devouring Corpses, Dwarf Dwarfson Dwarvenminer
Re: Coding Advent Calendar II
Door: 23
Lets talk about biomes. So far we have one random dungeon, but just one. We have different random mapparts which are placed on this random dungeon with different terrain types, such as water. To make real biomes and different dungeon types, we would need to define them first. I made the following:
Ore Mine: our basic mine, with easier monster nests and a reduced number of encounters.
City: contains a lot of ruins, parts of a canalisation and undead creatures. There is a high chance to discover artifacts. There might be low chance for other veins.
Mountain: contains mithril veins, which can be mined for mithril. Its the home of trolls, orcs, goblins and many creatures such as Wyvern, Rocs and Gryphon. So the chance to encounter dangerous creatures is slightly higher.
The Frost: contains ice creatures such as Yetis and the new cool frost monsters. You can find goldveins here and mithril veins with a low chance.
The Lava Mine: fiery terrain and home to many dangerous fire creatures. Dragon Hoards for example can only be found here.
Ultimate Dungeon: A giant dungeon (100*66) with all encounters (mapparts and monsters) many moster nest and all resources and veins. Can only be unlocked when all other dungeons are unlocked first.
In the future we could make something like a Desert dungeon, which might hold key to artifacts and maybe get the chance for new words of powers. But for now, I will stick with the dungeons above.
We make our random mine scenarioe perfect and than we copy it and change the names. However, a giant part of these scenarios will be the encounters, the mapparts we place here. Some map parts such as those containing lava make no sense in the Frost Dungeon. We could manually delete them for the level, but that is not good an takes us a lot of time (for 6 levels).
And we have the problem, that once we want to improve a map part, which isn't yet cool enough, we would need to change this in 6 dungeons. So the first thing I did was to make a macro called:
{SWITCH_BIOMETYPE}
It is the containig the switch. So I now can control which kind of terrain is used in all of the dungeons. As this is a lot of text I even made an own file for this in the utils folder. This is by the way no problem. You can have many files here, but all macros inside it can still be used in your campaign files.
So the new code for the biomes would look like this for our random dungeons.
Code: Select all
[set_variable]
name=biome
rand=2..4
[/set_variable]
[while]
[variable]
name=biome
greater_than=0
[/variable]
[do]
[set_variable]
name=biometype rand=dwarventemple,geomantyshrine,dwarvencity,goblinvillage,trap,abandonedcity,bandits,artifact,river,waterhole,wall,sea,gapbridge,gap,bearcave,dumpster,waterfall,mushroomwood,veinpocket,mystery,oldmining
[/set_variable]
[set_variable]
name=biomex
rand=1..30
[/set_variable]
[set_variable]
name=biomey
rand=1..20
[/set_variable]
{SWITCH_BIOMETYPE}
[set_variable]
name=biome
sub=1
[/set_variable]
[/do]
[/while]
The first things that I change in the dungeons is that line:
Code: Select all
[set_variable]
name=biometype rand=dwarventemple,geomantyshrine,dwarvencity,goblinvillage,trap,abandonedcity,bandits,artifact,river,waterhole,wall,sea,gapbridge,gap,bearcave,du mpster,waterfall,mushroomwood,veinpocket,mystery,oldmining
[/set_variable]
Rock Scorpion,Yeti,Gryphon,Ogre,Wild Wyvern,Icemonax,Frost Stoat,Great Icemonax,Roc,Direwolf,Great Wolf,Death Knight,Troll,Troll Rocklobber
They will be surrounded with snow, instead of ground.
In the mountain I've even placed mountain terrain. So you digged out of the mountain so to say. Than I place extra resources the same way I placed our ore veins before, but instead of ore it will be gold, mithril or diamond veins, which all have their own terrain.
One advantage of making own scenario files for each dungeon is that I can access the levels easily and add map parts in an easy way. I can also easily add new feature that are just relevant for this kind of dungeons. So I placed huge lavaseas and volcanos in the lava level. Many parts will be replaced by our ore veins, monster nests and biomes. But a part of it might prevail on the map, giving this level a unique touch.
The disadvantage is that I now have to maintain 6 files.
I could have made this with the same dungeon, but it seemed to complicated and time consuming. But in the end I ended up to change the same thing in 6 different scenario files or levels.
For example I noticed a bug in the random ore mine and had to change it in all other dungeons as well: Monster$monsternestx$monsternesty|1 wasn't working as intended. It is not a unique id. So killing a monster with that Id will also kill another monster which might have the same id as well.
This one is correct: Monster$monsternestx|$monsternesty|1 is working. The | was missing. If you use variables in a string like that, you have to use $ to call the variable and | to end it.
So, finally: here the complete code for the frost dungeon:
Code: Select all
#textdomain wesnoth-Dwarf_Dwarfson_Dwarvenminer
[scenario]
id=Randomminesnow
name=_"The Frost"
map_data="{~add-ons/Dwarf_Dwarfson_Dwarvenminer/maps/Randomminesnow.map}"
victory_when_enemies_defeated=no #there are so far no enemies, so we set this to no
next_scenario=null # there is no next scenario, we will define that in our event
turns=-1 # this allows infinite turns
{SCENARIO_MUSIC frantic.ogg}
# this is where our macro from above is added. Unnecessary if you ask me. we could have added the story part right here, too.
# but if you have a single scenario that need a macro, you can use this one.
[story]
[part]
story= _ "The Frost. Gold veins have been recently discovered."
background=story/landscape-mountains-01.jpg
[/part]
[/story]
{UNDERGROUND}
[side]
side=1
controller=human
team_name=1
user_team_name= _ "Dwarf Dwarfson"
id=Dwarvenminerid
name=_"Dwarf Dwarfson"
gender=male
shroud=yes
fog=yes
canrecruit=yes
unrenamable=yes
recruit=
village_gold=0
income=-2
[/side]
[side]
side=2
controller=ai
team_name=2
user_team_name= _ "Hisses in the Dark"
type=Mudcrawler
id=Monster
name=_"Monster"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
recruit=
gold=0
[ai]
agression=1
caution=0
village_value=0.0
grouping=no
simple_targeting=yes
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_movements yes}
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_combat yes}
{AI_SIMPLE_ALWAYS_ASPECT aggression 1.0}
{AI_SIMPLE_ALWAYS_ASPECT caution 0}
[/ai]
[/side]
[side]
side=3
controller=ai
team_name=3
user_team_name= _ "An underground facility"
type=Mudcrawler
id=Sentient
name=_"Sentient"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
recruit=
gold=0
[ai]
agression=1
caution=0
village_value=0.0
grouping=no
simple_targeting=yes
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_movements yes}
{AI_SIMPLE_ALWAYS_ASPECT combat_ignore_bad_combat yes}
{AI_SIMPLE_ALWAYS_ASPECT aggression 1.0}
{AI_SIMPLE_ALWAYS_ASPECT caution 0}
[/ai]
[/side]
[side]
side=4
controller=ai
team_name=1
user_team_name= _ "Allied"
type=
id=Allied
name=_"Allied"
gender=male
canrecruit=yes
unrenamable=yes
no_leader=yes
gold=0
[/side]
[event]
name=prestart
[if]
[variable]
name=cityvisited
equals=yes
[/variable]
[then]
[modify_side]
side=1
gold=$stored_gold
[/modify_side]
[/then]
[/if]
[set_variable]
name=encounter
value=no
[/set_variable]
[objectives]
side=1
summary=_"final objectives:"
[objective]
description= _ "Mine up to $miningtask ore"
condition=win
[/objective]
[objective]
description= _ "Death"
condition=lose
[/objective]
[/objectives]
[/event]
[event]
name=start
[set_variable]
name=orelocations
rand=10..20
[/set_variable]
[while]
[variable]
name=orelocations
greater_than=0
[/variable]
[do]
[set_variable]
name=orelocationx
rand=1..44
[/set_variable]
[set_variable]
name=orelocationy
rand=1..33
[/set_variable]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ai^Es
radius=1
[/terrain]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ur^Vei
[/terrain]
[set_variable]
name=orelocations
sub=1
[/set_variable]
[/do]
[/while]
[set_variable]
name=orelocations
rand=1..5
[/set_variable]
[while]
[variable]
name=orelocations
greater_than=0
[/variable]
[do]
[set_variable]
name=orelocationx
rand=1..44
[/set_variable]
[set_variable]
name=orelocationy
rand=1..33
[/set_variable]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ai^Es
radius=1
[/terrain]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ur^MVe
[/terrain]
[set_variable]
name=orelocations
sub=1
[/set_variable]
[/do]
[/while]
[set_variable]
name=orelocations
rand=10..15
[/set_variable]
[while]
[variable]
name=orelocations
greater_than=0
[/variable]
[do]
[set_variable]
name=orelocationx
rand=1..44
[/set_variable]
[set_variable]
name=orelocationy
rand=1..33
[/set_variable]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ai^Es
radius=1
[/terrain]
[terrain]
x=$orelocationx
y=$orelocationy
terrain=Ur^GVe
[/terrain]
[set_variable]
name=orelocations
sub=1
[/set_variable]
[/do]
[/while]
[set_variable]
name=biome
rand=2..4
[/set_variable]
[while]
[variable]
name=biome
greater_than=0
[/variable]
[do]
[set_variable]
name=biometype
rand=trolls,dwarventemple,yeticave,giantnest,geomantyshrine,goblinvillage,bandits,river,waterhole,wall,sea,gapbridge,gap,ruins,bearcave,waterfall,veinpocket,mystery,oldmining
[/set_variable]
[set_variable]
name=biomex
rand=1..30
[/set_variable]
[set_variable]
name=biomey
rand=1..20
[/set_variable]
{SWITCH_BIOMETYPE}
[set_variable]
name=biome
sub=1
[/set_variable]
[/do]
[/while]
[set_variable]
name=monsternests
rand=2..5
[/set_variable]
[while]
[variable]
name=monsternests
greater_than=0
[/variable]
[do]
[set_variable]
name=monsternestx
rand=1..44
[/set_variable]
[set_variable]
name=monsternesty
rand=1..33
[/set_variable]
[terrain]
x=$monsternestx
y=$monsternesty
terrain=Aa^Edb
radius=2
[/terrain]
[set_variable]
name=monstertype
rand=Rock Scorpion,Yeti,Gryphon,Ogre,Wild Wyvern,Icemonax,Frost Stoat,Great Icemonax,Roc,Direwolf,Great Wolf,Death Knight,Troll,Troll Rocklobber
[/set_variable]
[item]
x=$monsternestx
y=$monsternesty
image=$items/bones.png
[/item]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx|$monsternesty|1
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx|$monsternesty|2
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[unit]
side=2
type=$monstertype
id=Monster$monsternestx|$monsternesty|3
name= _ "Monster"
x=$monsternestx
y=$monsternesty
[/unit]
[set_variable]
name=monsternests
sub=1
[/set_variable]
[/do]
[/while]
[/event]
[label]
x=19
y=33
text="City"
[/label]
[event]
name=moveto
first_time_only=no
[filter]
x=19
y=33
[/filter]
[message]
speaker=Dwarvenminerid
message= _ "Back to the city!"
[/message]
[store_gold]
side=1
variable=stored_gold
[/store_gold]
[endlevel]
result=victory
next_scenario=City
save=no
carryover_report=no
carryover_percentage=100
linger_mode=no
bonus=no
[/endlevel]
[/event]
{MINE_EVERYTHING}
[/scenario]
Door: 24
changes:
- there are now different dungeons types as described yesterday.
- your hero is now an own unit with an own amla path
- the amla features 4 infinite advancement and 4 paths that unlock different abilities and can be combined with upgrades for unique playstyles
- you can follow a short story line with missions
- the city now has 2 additional npcs, the farmer and your father (featuring the story line)
- there are new biomes, such as dwarven temples, berserker and troll fortresses, giant nests and yeticaves
But this is a coding calendar. Hm. So let's look into how to make a dwarvenminer unit. I simply copied an existing unit - 'the Explorer' and changed his name and id. I changed the experience to 30, added a new attack for the pickaxe. I also added an new animation for the pickaxe, by copying the image files for the axe attack and replaced the axe by a pickaxe. I saved these image files in my campaign folder. After that I added some advancements to the unit. The final result is here:
Code: Select all
#textdomain wesnoth-units
[unit_type]
id=Dwarvish Miner
name= _ "Dwarvish Miner"
race=dwarf
image=units/dwarves/explorer.png
profile="portraits/dwarves/explorer.png"
hitpoints=60
movement_type=dwarvishfoot
movement=5
experience=30
level=3
alignment=neutral
advances_to=null
usage=scout
description= _ "Dwarf Dwarvson is an Explorer. Dwarvish Explorers are peerless survivalists. Using only the equipment they carry, they can range for months around the forests and mountains looking for new seams of ore and deposits of minerals. Whilst their skill in a melee is less than some other dwarves, they are unmatched with throwing axes, having practiced this skill hunting in the mountains. Their maneuverability makes them dangerous and tricky foes. As a veteran in previous wars, Dwarf has developed some techniques of his own to surive in the harsh environment of the ore mines."
die_sound={SOUND_LIST:DWARF_DIE}
{DEFENSE_ANIM "units/dwarves/explorer-defend-2.png" units/dwarves/explorer-defend-1.png {SOUND_LIST:DWARF_HIT} }
[attack]
name=battle axe
description= _"battle axe"
type=blade
range=melee
damage=10
number=3
icon=attacks/battleaxe.png
[/attack]
[attack]
name=axe
description= _"axe"
type=blade
range=ranged
damage=11
number=3
icon=attacks/axe-crude.png
[/attack]
[attack]
name=pickaxe
description= _"pick-axe"
type=impact
range=melee
damage=15
number=1
icon=attacks/pick-axe.png
[/attack]
[attack_anim]
[filter_attack]
range=ranged
name=axe
[/filter_attack]
{MISSILE_FRAME_HATCHET}
start_time=-300
[frame]
image="units/dwarves/explorer-ranged-[1~3].png:[200*2,100]"
[/frame]
{SOUND:HIT_AND_MISS hatchet.wav hatchet-miss.wav -100}
[/attack_anim]
[attack_anim]
[filter_attack]
range=melee
name=battle axe
[/filter_attack]
start_time=-260
offset=0.0~0.1:210,0.1~0.6:150,0.6~0.0:150
[frame]
image="units/dwarves/explorer-melee-[1~6].png:[80*2,50*2,100*2]"
[/frame]
{SOUND:HIT_AND_MISS axe.ogg {SOUND_LIST:MISS} -50}
[/attack_anim]
[attack_anim]
[filter_attack]
range=melee
name=pickaxe
[/filter_attack]
start_time=-260
offset=0.0~0.1:210,0.1~0.6:150,0.6~0.0:150
[frame]
image="units/explorer-melee2-[1~6].png:[80*2,50*2,100*2]"
[/frame]
{SOUND:HIT_AND_MISS mace.wav {SOUND_LIST:MISS} -50}
[/attack_anim]
[advancement]
major_amla=yes
max_times=-1
description= _ "Constitution: Hitpoints increase by 15%"
image=attacks/crush-wose.png
[effect]
apply_to=hitpoints
increase_total=15%
heal_full=yes
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
max_times=-1
description= _ "Dexterity: Improve Ranged Damage"
image=attacks/axe-crude.png
[effect]
apply_to=attack
range=ranged
increase_damage=2
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
max_times=-1
description= _ "Speed: Movement Increase"
image=icons/boots_elven.png
[effect]
apply_to=movement
increase=1
[/effect]
[effect]
apply_to=max_experience
increase=50%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
max_times=-1
description= _ "Strength: Improve Melee Damage"
image=attacks/hoof.png
[effect]
apply_to=attack
range=melee
increase_damage=2
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=strength
max_times=1
description= _ "Fighter: Increase Hitpoints by 25%"
image=attacks/fist-troll.png
[effect]
apply_to=hitpoints
increase_total=25%
heal_full=yes
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=strength2
max_times=1
require_amla=strength
description= _ "Fighter: Increase all damages by 2"
image=attacks/fist-troll.png
[effect]
apply_to=attack
range=melee
increase_damage=2
[/effect]
[effect]
apply_to=attack
range=ranged
increase_damage=2
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=strength3
max_times=1
require_amla=strength2
description= _ "Fighter: Poison Immunity"
image=attacks/fist-troll.png
[effect]
apply_to=new_ability
[abilities]
[heals]
id=Poison Immunity
name= _ "Poison Immunity"
affect_self=yes
poison=cured
[/heals]
[/abilities]
[/effect]
[effect]
apply_to=max_experience
increase=50%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=dexterity
max_times=1
description= _ "Ranger: 'Firststrike' with ranged weapons"
image=icons/cloak_leather_brown.png
[effect]
apply_to=attack
range=ranged
[set_specials]
mode=append
{WEAPON_SPECIAL_FIRSTSTRIKE}
[/set_specials]
[/effect]
[effect]
to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=dexterity2
max_times=1
require_amla=dexterity
description= _ "Ranger: Receive Nightstalk"
image=icons/cloak_leather_brown.png
[effect]
apply_to=new_ability
[abilities]
{ABILITY_NIGHTSTALK}
[/abilities]
[/effect]
[effect]
to=max_experience
increase=50%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=dexterity3
max_times=1
require_amla=dexterity2
description= _ "Ranger: Increase ranged damage by 4"
image=attacks/fist-troll.png
[effect]
apply_to=attack
range=ranged
increase_damage=4
[/effect]
[effect]
apply_to=max_experience
increase=50%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=const
max_times=1
description= _ "Tank: Increase Resistance"
image=icons/steel_armor.png
[effect]
apply_to=resistance
replace=false
[resistance]
blade=-5
pierce=-5
impact=-5
fire=-5
cold=-5
arcane=-5
[/resistance]
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=const2
max_times=1
require_amla=const
description= _ "Tank: Receive Steadfast"
image=icons/steel_armor.png
[effect]
apply_to=new_ability
[abilities]
{ABILITY_STEADFAST}
[/abilities]
[/effect]
[effect]
apply_to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=const2
max_times=1
require_amla=const
description= _ "Tank: Receive +35% Hitpoints"
image=icons/steel_armor.png
[effect]
apply_to=hitpoints
increase_total=35%
heal_full=yes
[/effect]
[effect]
apply_to=max_experience
increase=50%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=speed
max_times=1
description= _ "Trickster: Receive Skirmisher"
image=icons/sandals.png
[effect]
apply_to=new_ability
[abilities]
{ABILITY_SKIRMISHER}
[/abilities]
[/effect]
[effect]
to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=speed2
max_times=1
require_amla=speed
description= _ "Trickster: Increase Defense"
image=icons/sandals.png
[effect]
apply_to=defense
replace=false
[defense]
castle=-5
cave=-5
reef=-5
deep_water=-5
flat=-5
forest=-5
frozen=-5
hills=-5
mountains=-5
fungus=-5
sand=-5
shallow_water=-5
swamp=-5
unwalkable=-5
village=-5
[/defense]
[/effect]
[effect]
to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[advancement]
id=speed3
max_times=1
require_amla=speed2
description= _ "Trickster: Firststrike Melee"
image=icons/sandals.png
[effect]
apply_to=attack
range=melee
[set_specials]
mode=append
{WEAPON_SPECIAL_FIRSTSTRIKE}
[/set_specials]
[/effect]
[effect]
to=max_experience
increase=25%
[/effect]
[effect]
apply_to=hitpoints
heal_full=yes
[/effect]
[/advancement]
[/unit_type]
If you use advancements, make sure to delete the AMLA Default first and make sure that the unit has advances_to=null. Funny thing, the advancement did not work from the start, because I put them outside of the unit_type. Without an amla a unit can not receive a level up.
I also made a simple animation for your father, a skilled smith for armors of any kind. I added a small description of how I made it to the campaign, together with the gimp file for you to use.
Finally, let's talk about quests. If you use quest and or story lines, you should go with a numeric value. So I've made a variable called 'quest' . This variable starts with quest=0. Once our hero fulfills a task, the variable is increased by one. This is very effective and saves you a lot of time!
Imagine you have a hero, who has to defeat a dragon to save a village. If the dragon isn't dead yet, our villagers could give the hero advice what to do or wish him well on his endeavor. After he has slain every villager is happy and the celebrate giving out other dialogues than before. By raising the variable by 1 you could check for this variable and make some dialogues and dialogue options appear or disappear.
Code: Select all
[option]
[show_if]
[variable]
name=quest
equals=2
[/variable]
[/show_if]
image=dwarvenminer.png
message= _ "Talk: Reclaim Lavamine"
[command]
[message]
speaker=Dwarvenminerid
message= _ "My lord! We need to reclaim the Lava Mine!"
[/message]
[message]
speaker=King
message= _ "Is that so? Do you have any idea about the costs for such an endeavor?"
[/message]
[/command]
[/option]
This example above shows an exact point in the story line when a message should appear. You could also say from 1 - 5 of the story line a specific dialogue can appear, in case the quest goes a little longer. So that's it.
I hope you enjoyed the advent calendar, that you learn something or maybe found something you could use in your own projects.
Feel free to use any of the shared concepts or pieces of code. Wherever and whoever you are. Merry Christmas and a Happy New Year.
Door: 25
Advice on how to port an add-on from 1.14 to 1.16:
This just a basic guide. There might be missing some parts, but these steps helped me to port all my campaigns to BoW 1.16.
Some of the utils are deprecated. You can effectively change that, by adding the deprecated utils to your utils folder.
- deprecated-utils.cfg
- (14.36 KiB) Downloaded 48 times
{SOUND:SLOW}
{SOUND:POISON}
They should be replaced in your campaign, or you make new one and add them to the deprecated utils file.
Deprecated macros: the most important are MENU_IMG_TXT and MENU_IMG_TXT2.
As the code for the options has changed, you will need to change the MENU_IMG_TXT - I already did that for you in the deprecated file:
Code: Select all
#define MENU_IMG_TXT IMAGE TEXT
image={IMAGE}
message= _ {TEXT}#enddef
MENU_IMG_TXT2 is often used in the main cfg, this no longer works:
Deprecated:
Code: Select all
difficulties=EASY,HARD,NIGHTMARE
difficulty_descriptions={MENU_IMG_TXT2 "units/human-outlaws/thief.png~RC(black>blue)" _"Thief" _"(Beginner)"} +
";" + {MENU_IMG_TXT2 "units/human-outlaws/footpad.png~RC(black>blue)" _"Footpad" _"(Challenging)"} + ";" + {MENU_IMG_TXT2 "units/human-outlaws/thug.png~RC(black>blue)" _"Thug" _"(Nightmare)"}
description= _ "
Code: Select all
difficulties=EASY,HARD,NIGHTMARE
{CAMPAIGN_DIFFICULTY EASY "units/human-outlaws/thief.png~RC(black>blue)" ( _ "Thief") ( _ "Beginner")} {DEFAULT_DIFFICULTY}
{CAMPAIGN_DIFFICULTY HARD "units/human-outlaws/footpad.png~RC(black>blue)" ( _ "Footpad") ( _ "Hard")}
{CAMPAIGN_DIFFICULTY NIGHTMARE "units/human-outlaws/thug.png~RC(black>blue)" ( _ "Thug") ( _ "Nightmare")}
description= _ "
There are some image issues, as some units now have new paths, some icons have changed.
Fortunately there are no terrain issues, at least as far as I've seen.
The game is little bit less forgiving, when it comes to the storage of gold. Be sure just to define it once, at the beginning of while loops in shops.
There is an example for that in this calendar.
So that's it. Have fun.
Developer of: Trapped, Five Fates, Strange Legacy, Epical, UR Epic Era
Dungeonmasters of Wesnoth, Wild Peasants vs Devouring Corpses, Dwarf Dwarfson Dwarvenminer
- Announcements
- ↳ News
- General
- ↳ Users’ Forum
- ↳ Tournaments
- ↳ Release Announcements, Compiling & Installation
- ↳ Technical Support
- ↳ iOS Support
- ↳ Strategies & Tips
- ↳ Gameplay Feedback
- ↳ Mainline Campaign Feedback
- ↳ Tutorial
- ↳ A Tale of Two Brothers
- ↳ An Orcish Incursion
- ↳ The South Guard
- ↳ Heir to the Throne
- ↳ Liberty
- ↳ Legend of Wesmere (single player)
- ↳ Legend of Wesmere (multiplayer)
- ↳ Eastern Invasion
- ↳ The Hammer of Thursagan
- ↳ Descent into Darkness
- ↳ Delfador’s Memoirs
- ↳ Dead Water
- ↳ Secrets of the Ancients
- ↳ Sceptre of Fire
- ↳ Son of the Black Eye
- ↳ The Rise of Wesnoth
- ↳ Northern Rebirth
- ↳ Under the Burning Suns
- ↳ Winds of Fate
- ↳ Add-on Feedback
- ↳ Website
- Development
- ↳ Art Contributions
- ↳ Art Workshop
- ↳ Writers’ Forum
- ↳ WML Workshop
- ↳ Lua Labs
- ↳ Faction & Era Development
- ↳ Multiplayer Development
- ↳ Scenario & Campaign Development
- ↳ Mainline Campaign Development
- ↳ UMC Replays
- ↳ Music & Sound Development
- ↳ Translations & Internationalization
- ↳ Translation Stats
- ↳ Ideas
- ↳ Coder’s Corner
- ↳ Developers’ Discussions
- ↳ Wesnoth Organizational Updates
- ↳ Art Development
- Miscellaneous
- ↳ Game Development
- ↳ Off-Topic
- ↳ Forum Games