FancyAddonIcons
An add-on's icon is the first thing a player is going to look at when picking add-ons. A good add-on icon can attract many players, so it should not be neglected. However, custom images cannot be used, they will be seen only by people who have it installed, that usually means that the author will think it's correct, although it is not. Despite this limitation, there is a way to create quite cool add-on icons. It is called ImagePathFunctionWML.
Contents
Introduction
Of course, you can create a code like:
items/bonestack.png~BLIT(items/bonestack.png~CROP(20,0,52,52),0,20)~BLIT(items/bonestack.png~CROP(0,0,52,52),20,20)~BLIT(items/burial.png~CROP(20,0,52,72),0,0)~BLIT(items/burial.png~CROP(0,0,52,62),20,10)~BLIT(units/undead-skeletal/archer-die2-6.png~CROP(10,0,62,62)~RC(magenta>black),0,10)~BLIT(units/orcs/sovereign-lead-2.png~RC(magenta>black))~BLIT(items/burial.png~CROP(0,0,62,52),10,20)
But testing this will be very tedious and annoying, reloading the entire add-on to view it somewhere, or even uploading it to test it every time. To make this easier, download an add-on named Image loading test, it will help you a lot with this. The add-on is absolutely minimalistic, and has no obvious effect anywhere. To use it, either execute this WML chunk:
[lua] code=<<wesnoth.image_test()>> [/lua]
Or activate the debug mode (:debug in CommandMode), and use this command:
lua wesnoth.image_test()
The second option is probably more convenient.
A window will show up, there will be a text box and two buttons. Type your image code into the text box and click on the Show button (caution, don't hit enter, it will make it exit). This way, you will be able to verify it very quickly. It is recommended to do this while running wesnoth from command prompt (on Windows) or Terminal (on Linux), it will notice you about some errors in your code (on Windows, you can also read stderr.txt afterwards).
Because the code on a single line possibly with many nested brackets is almost unreadable for humans, it is recommended to write it in a text file with indentation and new lines, then to use Find&Replace to remove all tabs, spaces and new lines and paste it into the text box. I will use indented codes later, for better readability, and it will be necessary to remove all spaces and new lines from it or copying the version when it is already removed bellow it if you want to try it out.
To understand better the numeric values that look like just made up to fit, you might want to learn the coordinates needed in an image editor like GIMP.
Now, you can verify your codes easily, and you can progress to the next step.
Step by step tutorial
Let us think about creating an add-on image for a campaign where elves attack undead. It would consist of an Elvish Hero chopping undead to pieces, supported by spells of an Elvish Sorceress behind him. We will create an image for it in several steps. Try out each one of them to see what is changed.
1. Image showing an Elvish Hero in an attacking position is units/elves-wood/hero-melee-3.png. So we start with it:
units/elves-wood/hero-melee-3.png
2. Nothing interesting so far. Let us add a skeleton he is slashing. The image units/undead-skeletal/skeleton/skeleton-se-melee3.png is in a good fighting position, also exposing his belly to our hero. We will use the ~BLIT function of ImagePathFunctionWML to add him to the picture. Now we have:
units/elves-wood/hero-melee-3.png ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png )
Note that the new lines and indentation are added only for readability, it will not work in game, you'll have to remove all new lines and breaks. Here it is without indents:
units/elves-wood/hero-melee-3.png~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png)
3. However, they are quite in an awkward position. We will need to add some offset to the skeleton. ~BLIT accepts two additional arguments that mean the offset of the image. However, it would not fit on the image then (this prints an error message into the command line, Termina or stderr.txt), so we need to crop it. The ~CROP function accepts four arguments, the first two are the coordinates where the selected part of the image should start (pixels to the right first, pixels down second), the second two is the desired size of the result (width and height respectively). In this case, we start cropping on the top left corner, therefore the first two coordinates will be 0 and 0. The offset will be 20,20 (pixels to left first, pixels down second), so we need the resolution of the image to be 52x52 (original is 72x72, we subtract 20 from both numbers). The four arguments of crop will be therefore 0,0,52,52, and the two additional arguments of ~BLIT will be 20,20. Together, it should look like this:
units/elves-wood/hero-melee-3.png ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(0,0,52,52) ,20,20)
Or without indentation:
units/elves-wood/hero-melee-3.png~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(0,0,52,52),20,20)
4. Now, the skeleton needs to be headed differently, because he isn't attacking the hero. The ~FLIP function is designed for this. Use ~FLIP(horizontal) for this. We will also need to adjust the offset in CROP and BLIT, because it would look bad:
units/elves-wood/hero-melee-3.png ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,47)~FL(horizontal) ,40,20)
Or without indentation:
units/elves-wood/hero-melee-3.png~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,47)~FL(horizontal),40,20)
5. However, we want the skeleton to be chopped in half by the Hero's sword. We can do this by placing two conveniently cropped images of skeleton one above another.
units/elves-wood/hero-melee-3.png ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal) ,40,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,22)~FL(horizontal) ,40,47)
Or without indentation:
units/elves-wood/hero-melee-3.png~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal),40,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,22)~FL(horizontal),40,47)
6. We want our heroes to be also teamcoloured, so we can change the default colour (magenta) to some other colour. The colours you can change it to can be found in the data/core/team_colors.cfg file. The colours are changed using the ~RC function. Because our heroes are good and the undead are bad, let's team-colour the undead black and the heroes white.
units/elves-wood/hero-melee-3.png~RC(magenta>white) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black) ,40,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,22)~FL(horizontal)~RC(magenta>black) ,40,47)
Or without indentation:
units/elves-wood/hero-melee-3.png~RC(magenta>white)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black),40,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,22)~FL(horizontal)~RC(magenta>black),40,47)
7. Now, we want to add the Sorceress casting a spell to the background. She should be behind the Elvish Hero, and she should have an offset, but that can be done by cropping. To make sure that she won't be hid behind the Hero too badly, the hero and the skeleton will have to be offset too. Because we need an image of dimensions 72x72 as background, and a cropped Sorceress does not have such dimensions, we will have to use misc/blank-hex.png as background (it is a blank image) and place the sorceress on it. The offsets were adjusted to make it look better.
misc/blank-hex.png ~BLIT( units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62) ,0,0) ~BLIT( units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62) ,0,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black) ,40,20) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black) ,40,57)
Or without indentation:
misc/blank-hex.png~BLIT(units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(15,10,57,62),0,0)~BLIT(units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62),0,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black),40,20)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black),40,57)
8. Now, we need to add some halo to her, she is casting a spell, no? A suitable halo can be halo/elven/faerie-fire-halo4.png, let's use it. Its resolution is 96x96, that is quite awkward because it is larger than our image, but we can crop it easily.
misc/blank-hex.png ~BLIT( units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62) ,0,0) ~BLIT( halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52) ,0,0) ~BLIT( units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62) ,0,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black) ,40,20) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black) ,40,57)
Or without indentation:
misc/blank-hex.png~BLIT(units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62),0,0)~BLIT(halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52),0,0)~BLIT(units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62),0,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black),40,20)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black),40,57)
9. Now, to add the homing missile of the spell. halo/elven/ice-halo3.png looks convenient. Placing it on the image together with a skeleton that would be the victim of this spell is analogical to things done previously.
misc/blank-hex.png ~BLIT( units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62) ,0,0) ~BLIT( halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52) ,0,0) ~BLIT( units/undead-skeletal/deathblade-idle-2.png~FL(horizontal)~CROP(0,15,42,67)~RC(magenta>black) ,30,0) ~BLIT( halo/elven/ice-halo3.png~CROP(0,20,62,52) ,10,0) ~BLIT( units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62) ,0,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black) ,40,20) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black) ,40,57)
Or without indentation:
misc/blank-hex.png~BLIT(units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62),0,0)~BLIT(halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52),0,0)~BLIT(halo/elven/ice-halo3.png~CROP(0,20,62,52),10,0)~BLIT(units/undead-skeletal/deathblade-idle-2.png~FL(horizontal)~CROP(0,15,42,67)~RC(magenta>black),30,0)~BLIT(units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62),0,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black),40,20)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black),40,57)
10. We might colourise the skeleton blue, to make him appear frozen. There are two functions for this, ~CS and ~GS. ~GS removes all colour, transforming all colours into shades of gray. ~CS is used to change the colour, it accepts three arguments, the change to the red colour, the change to the green colour and the change to the blue colour. Negative numbers (from -1 to -255) mean removing the colour from all pixels (respecting transparency), so ~CS(0,-255,-255) will remove all colours except red, so that only the red part of the light will remain. Positive numbers (from 1 to 255) will add the colour to all pixels (unless invisible), so ~CS(255,0,0) will set the red fraction of the colour to maximum. ~CS(255,-255,-255) will remove all colours but red, and add a maximum of red everywhere, creating a single-colour image. To create the effect of frozen, we first use ~GS and then we use ~CS to make it bluer.
misc/blank-hex.png ~BLIT( units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62) ,0,0) ~BLIT( halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52) ,0,0) ~BLIT( units/undead-skeletal/deathblade-idle-2.png~FL(horizontal)~CROP(0,15,42,67)~RC(magenta>black)~GS()~CS(50,50,150) ,30,0) ~BLIT( halo/elven/ice-halo3.png~CROP(0,20,62,52) ,10,0) ~BLIT( units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62) ,0,10) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black) ,40,20) ~BLIT( units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black) ,40,57)
Or without indentation:
misc/blank-hex.png~BLIT(units/elves-wood/sorceress-magic-3.png~RC(magenta>white)~CROP(10,15,57,62),0,0)~BLIT(halo/elven/faerie-fire-halo4.png~CROP(14,44,72,52),0,0)~BLIT(units/undead-skeletal/deathblade-idle-2.png~FL(horizontal)~CROP(0,15,42,67)~RC(magenta>black)~GS()~CS(50,50,150),30,0)~BLIT(halo/elven/ice-halo3.png~CROP(0,20,62,52),10,0)~BLIT(units/elves-wood/hero-melee-3.png~RC(magenta>white)~CROP(0,0,72,62),0,10)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,5,32,29)~FL(horizontal)~RC(magenta>black),40,20)~BLIT(units/undead-skeletal/skeleton/skeleton-se-melee3.png~CROP(20,35,32,12)~FL(horizontal)~RC(magenta>black),40,57)
Now, we are done with this image.
More examples
Here are more examples of stuff you can do using these methods. They don't contain precise information how it is done, but it should be clear if you have read the tutorial.
The Spellsword
The purpose of this example is to show the usage of the opacity ~O function. When called on magical halo, it can be used to make the halo partially before and partially behind the unit (place the image there twice with 50% opacity, once under him, once above him). It can be also used to create motion blur.
halo/elven/elven-shield-halo-100pct.png~CROP(44,44,72,72)~O(50%) ~BLIT( units/elves-wood/high-lord-attack-sword-2.png~CROP(6,6,66,66)~O(25%)~RC(magenta>blue) ) ~BLIT( units/elves-wood/high-lord-attack-sword-2.png~CROP(3,3,69,69)~O(50%)~RC(magenta>blue) ) ~BLIT( units/elves-wood/high-lord-attack-sword-2.png~RC(magenta>blue) ) ~BLIT( halo/elven/elven-shield-halo-100pct.png~CROP(44,44,72,72)~O(50%) ) ~BLIT( halo/elven/faerie-fire-halo3.png~CROP(0,5,57,72) ,15,0)
Without indentation:
halo/elven/elven-shield-halo-100pct.png~CROP(44,44,72,72)~O(50%)~BLIT(units/elves-wood/high-lord-attack-sword-2.png~CROP(6,6,66,66)~O(25%)~RC(magenta>blue))~BLIT(units/elves-wood/high-lord-attack-sword-2.png~CROP(3,3,69,69)~O(50%)~RC(magenta>blue))~BLIT(units/elves-wood/high-lord-attack-sword-2.png~RC(magenta>blue))~BLIT(halo/elven/elven-shield-halo-100pct.png~CROP(44,44,72,72)~O(50%))~BLIT(halo/elven/faerie-fire-halo3.png~CROP(0,5,57,72),15,0)
Zorro
The purpose of this example is to show that some body parts can be taken from the original body (provided that they can be selected easily), modified and placed back. Or placed on to different place. Or to a completely different sprite.
units/human-loyalists/master-at-arms-crossbow-2.png~RC(magenta>black) ~BLIT( units/human-loyalists/master-at-arms-crossbow-2.png~CROP(2,27,18,14)~FL(horiz) ,48,27) ~BLIT( units/human-outlaws/assassin-throwknife1.png~CROP(29,17,11,13)~RC(magenta>black) ,26,21) ~BLIT( halo/misc/leadership-flare-7.png~CROP(0,5,46,67) ,26,0) ~BLIT( halo/misc/leadership-flare-6.png~CROP(33,7,39,65) ,0,0) ~BLIT( units/human-loyalists/master-at-arms-crossbow-2.png~RC(magenta>black)~CROP(20,11,20,17)~CS(-100,-100,-100) ,20,11)
Without indentation:
units/human-loyalists/master-at-arms-crossbow-2.png~RC(magenta>black)~BLIT(units/human-loyalists/master-at-arms-crossbow-2.png~CROP(2,27,18,14)~FL(horiz),48,27)~BLIT(units/human-outlaws/assassin-throwknife1.png~CROP(29,17,11,13)~RC(magenta>black),26,21)~BLIT(halo/misc/leadership-flare-7.png~CROP(0,5,46,67),26,0)~BLIT(halo/misc/leadership-flare-6.png~CROP(33,7,39,65),0,0)~BLIT(units/human-loyalists/master-at-arms-crossbow-2.png~RC(magenta>black)~CROP(20,11,20,17)~CS(-100,-100,-100),20,11)
Lich Wars
The purpose of this is to show how can text be used in the graphics. It mostly revolves around the misc/font8x8.png file (it is not in core, but in another images folder belonging to the game, next to the data folder that contains the core folder; using it causes no trouble).
misc/blank-hex.png ~BLIT( units/undead-skeletal/deathblade-dying-2.png~FL(horiz)~RC(magenta>red)~CROP(0,0,62,62)~O(50%) ,10,10) ~BLIT( units/undead-skeletal/deathblade-dying-1.png~FL(horiz)~RC(magenta>red)~CROP(0,0,62,62) ,10,10) ~BLIT( units/undead-necromancers/ancient-lich-melee.png~RC(magenta>red)~CROP(20,10,52,62) ) ~BLIT( halo/undead/black-magic-3.png~FL(vert)~CROP(15,40,72,60)~O(70%) ) ~BLIT( misc/blank-hex.png ~BLIT( misc/font8x8.png~CROP(39,33,8,8) ,16,56) ~BLIT( misc/font8x8.png~CROP(16,32,8,8) ,24,56) ~BLIT( misc/font8x8.png~CROP(47,24,8,8) ,32,56) ~BLIT( misc/font8x8.png~CROP(8,33,8,8) ,40,56) ~BLIT( misc/font8x8.png~CROP(48,41,8,8) ,16,64) ~BLIT( misc/font8x8.png~CROP(32,25,8,8) ,24,64) ~BLIT( misc/font8x8.png~CROP(8,40,8,8) ,32,64) ~BLIT( misc/font8x8.png~CROP(16,41,8,8) ,40,64) ~CS(-255,0,-255) )
Without indentation:
misc/blank-hex.png~BLIT(units/undead-skeletal/deathblade-dying-2.png~FL(horiz)~RC(magenta>red)~CROP(0,0,62,62)~O(50%),10,10)~BLIT(units/undead-skeletal/deathblade-dying-1.png~FL(horiz)~RC(magenta>red)~CROP(0,0,62,62),10,10)~BLIT(units/undead-necromancers/ancient-lich-melee.png~RC(magenta>red)~CROP(20,10,52,62))~BLIT(halo/undead/black-magic-3.png~FL(vert)~CROP(15,40,72,60)~O(70%))~BLIT(misc/blank-hex.png~BLIT(misc/font8x8.png~CROP(39,33,8,8),16,56)~BLIT(misc/font8x8.png~CROP(16,32,8,8),24,56)~BLIT(misc/font8x8.png~CROP(47,24,8,8),32,56)~BLIT(misc/font8x8.png~CROP(8,33,8,8),40,56)~BLIT(misc/font8x8.png~CROP(48,41,8,8),16,64)~BLIT(misc/font8x8.png~CROP(32,25,8,8),24,64)~BLIT(misc/font8x8.png~CROP(8,40,8,8),32,64)~BLIT(misc/font8x8.png~CROP(16,41,8,8),40,64)~CS(-255,0,-255))