Variable substitution question
Moderator: Forum Moderators
Forum rules
- 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.
- Atreides
- Posts: 1075
- Joined: March 30th, 2019, 10:38 pm
- Location: On the 2nd story of the centre village of Merwuerdigliebe turning the lights on and off
Variable substitution question
I have here this inherited code I'm trying to get to work. It stops at the pre-processor when loading MP because it can't find the macro
because the
So my question is: Was the author attempting something that is not even possible?
ABILITY_EREGATH_SANDWALL_"$sandwalldirection"
because the
"$sandwalldirection"
part is not being replaced by one of N,NE,SE,S,SW,NW
. Those 6 macros do exist, I didn't post them.So my question is: Was the author attempting something that is not even possible?
Code: Select all
#define ABILITY_EREGATH_SANDWALL
[dummy]
id=eregathsandwallnormal
name= _ "sand wall"
description= _ "Each turn, this unit raises a sand wall. The sand wall protects this unit from all attacks from one direction, chosen randomly each new turn."
[/dummy]
# wmllint: unbalanced-on
[/abilities]
[event]
name=side turn
first_time_only=no
{VARIABLE_OP sandwalldirection rand "N,NE,SE,S,SW,NW"}
[modify_unit]
[filter]
side=$side_number
type="Eregath Sandcaller,Eregath Sandwarper,Em_Chagal"
[/filter]
[modifications]
[abilities]
{ABILITY_EREGATH_SANDWALL_"$sandwalldirection"}
[/abilities]
[/modifications]
[/modify_unit]
{CLEAR_VARIABLE sandwalldirection}
[/event]
- Pentarctagon
- Project Manager
- Posts: 5564
- Joined: March 22nd, 2009, 10:50 pm
- Location: Earth (occasionally)
Re: Variable substitution question
Yes. Variable substitution happens long after the preprocessor tries to figure out how to handle macros.So my question is: Was the author attempting something that is not even possible?
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
take one down, patch it around
-2,147,483,648 little bugs in the code
- Celtic_Minstrel
- Developer
- Posts: 2222
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Variable substitution question
You could probably make this work by putting
Something like this (untested):
{ABILITY_EREGATH_SANDWALL_N}
into a variable (and the same with the other 5) and using [insert_tag]
there.Something like this (untested):
Code: Select all
[event]
name=side turn
first_time_only=no
{VARIABLE_OP sandwalldirection rand "N,NE,SE,S,SW,NW"}
[modify_unit]
[filter]
side=$side_number
type="Eregath Sandcaller,Eregath Sandwarper,Em_Chagal"
[/filter]
[modifications]
[abilities]
[insert_tag]
name=dummy
variable=ABILITY_EREGATH_SANDWALL_$sandwalldirection
[/insert_tag]
[/abilities]
[/modifications]
[/modify_unit]
{CLEAR_VARIABLE sandwalldirection}
[/event]
[event]
name=prestart
[set_variables]
name=ABILITY_EREGATH_SANDWALL_N
{ABILITY_EREGATH_SANDWALL_N}
[/set_variables]
# ditto for the other 5 directions
# the macros themselves probably need to be changed a little for this to work, too
[/event]
- Elvish_Hunter
- Posts: 1576
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: Variable substitution question
The more I'm staring at this thread, the more questions I get: why did the author decide that six macros, probably different only for the content of a variable, would be necessary? Wouldn't it be possible to replace everything with just one macro and put the random variable selection (and maybe a
I think it'd be interesting to see the rest of the code and evaluate if it's time not for a simple fix, but for a refactoring. Can you post that?
[switch] [case]
block) there?I think it'd be interesting to see the rest of the code and evaluate if it's time not for a simple fix, but for a refactoring. Can you post that?
Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
- Atreides
- Posts: 1075
- Joined: March 30th, 2019, 10:38 pm
- Location: On the 2nd story of the centre village of Merwuerdigliebe turning the lights on and off
Re: Variable substitution question
Certainly! I'll post the entire thing below.Elvish_Hunter wrote: ↑January 26th, 2022, 5:41 pm The more I'm staring at this thread, the more questions I get: why did the author decide that six macros, probably different only for the content of a variable, would be necessary? Wouldn't it be possible to replace everything with just one macro and put the random variable selection (and maybe a[switch] [case]
block) there?
I think it'd be interesting to see the rest of the code and evaluate if it's time not for a simple fix, but for a refactoring. Can you post that?
Code: Select all
#define ABILITY_EREGATH_SANDWALL
[dummy]
id=eregathsandwallnormal
name= _ "sand wall"
description= _ "Each turn, this unit raises a sand wall. The sand wall protects this unit from all attacks from one direction, chosen randomly each new turn."
[/dummy]
# wmllint: unbalanced-on
[/abilities]
[event]
name=side turn
first_time_only=no
{VARIABLE_OP sandwalldirection rand "N,NE,SE,S,SW,NW"}
[modify_unit]
[filter]
side=$side_number
type="Eregath Sandcaller,Eregath Sandwarper,Em_Chagal"
[/filter]
[modifications]
[abilities]
{ABILITY_EREGATH_SANDWALL_"$sandwalldirection"}
[/abilities]
[/modifications]
[/modify_unit]
{CLEAR_VARIABLE sandwalldirection}
[/event]
[event]
name=recruit
first_time_only=no
{VARIABLE_OP sandwalldirection rand "N,NE,SE,S,SW,NW"}
[modify_unit]
[filter]
side=$side_number
type="Eregath Sandcaller,Eregath Sandwarper,Em_Chagal"
[/filter]
[modifications]
[abilities]
{ABILITY_EREGATH_SANDWALL_"$sandwalldirection"}
[/abilities]
[/modifications]
[/modify_unit]
{CLEAR_VARIABLE sandwalldirection}
[/event]
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwallne
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.ne
[/filter_adjacent]
[/have_unit]
)}
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwallse
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.se
[/filter_adjacent]
[/have_unit]
)}
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwallnw
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.nw
[/filter_adjacent]
[/have_unit]
)}
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwallsw
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.sw
[/filter_adjacent]
[/have_unit]
)}
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwalls
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.s
[/filter_adjacent]
[/have_unit]
)}
{FORCE_CHANCE_TO_HIT side=$side_number type=Eregath Sandcaller,Eregath Sandwarper,Em_Chagal 0 (
[have_unit]
x,y=$x1,$y1
ability=eregathsandwalln
[filter_adjacent]
x,y=$x2,$y2
adjacent=$second_unit.variables.n
[/filter_adjacent]
[/have_unit]
)}
[+abilities]
# wmllint: unbalanced-off
#enddef
#define SANDWALL_UTILITY NAME DESC
[dummy]
id=eregathsandwall{NAME}
name= _ "sand wall "+{NAME}
description= _ "This unit is currently immune to all attacks from the "+{DESC}+"."
[/dummy]
#enddef
#define ABILITY_EREGATH_SANDWALL_NE
{SANDWALL_UTILITY ne northeast}
#enddef
#define ABILITY_EREGATH_SANDWALL_N
{SANDWALL_UTILITY n north}
#enddef
#define ABILITY_EREGATH_SANDWALL_NW
{SANDWALL_UTILITY nw northwest}
#enddef
#define ABILITY_EREGATH_SANDWALL_SE
{SANDWALL_UTILITY se southeast}
#enddef
#define ABILITY_EREGATH_SANDWALL_S
{SANDWALL_UTILITY s south}
#enddef
#define ABILITY_EREGATH_SANDWALL_SW
{SANDWALL_UTILITY sw southwest}
#enddef
Heh, there were 4 non trivial abilities in the faction, all broken, I was able to repair 2 of them myself. There is another that's even more difficult (it includes lua which I have not delved into (yet?)) called Intoxicate which meant to give an incurable poison to the victim causing 6 damage and death if not cured. I discovered to my horror that it was somehow spreading randomly...
Re: Variable substitution question
FORCE_CHANCE_TO_HIT should not work. Your unit types contain space so would be treated as separate arguments.
I think this could be implemented as single ability macro (possibly with multiple ability or special tags) which relies on unit variables to keep track which direction is active for specific unit. Not sure if it needs to be done with events or can manage with [disable].
I think this could be implemented as single ability macro (possibly with multiple ability or special tags) which relies on unit variables to keep track which direction is active for specific unit. Not sure if it needs to be done with events or can manage with [disable].
- beetlenaut
- Developer
- Posts: 2825
- Joined: December 8th, 2007, 3:21 am
- Location: Washington State
- Contact:
Re: Variable substitution question
Yikes! If code has this much copying and pasting in it, it's usually a sign that something went wrong. The point of a computer is to avoid exactly that.
I got bored today, so I did the refactoring that Elvish_Hunter suggested. I also included an image indicating where the wall is. (You will have to find better-quality images yourself.) Even with that addition, it's not any longer.
It won't work without some supporting files, so I put it in a campaign--a version of WML_Guide that I use for testing. I created a Sand Burner out of the Dunefolk unit. It can make sand walls that I think work the way you want them to. You might find a bug or need to make changes, but this code should be a lot easier to work with.
I got bored today, so I did the refactoring that Elvish_Hunter suggested. I also included an image indicating where the wall is. (You will have to find better-quality images yourself.) Even with that addition, it's not any longer.
Code: Select all
#define REMOVE_SAND_WALL UNIT X Y
[remove_item]
x={X}
y={Y}
image="walls/wall-${UNIT}.variables.wall_dir|.png"
[/remove_item]
#enddef
#define PLACE_SAND_WALL UNIT X Y
[item]
x={X}
y={Y}
image="walls/wall-${UNIT}.variables.wall_dir|.png"
[/item]
#enddef
#define CHOOSE_SAND_WALL_DIR UNIT
[set_variable]
name={UNIT}.variables.wall_dir
rand=n,s,nw,ne,sw,se
[/set_variable]
[unstore_unit]
variable={UNIT}
[/unstore_unit]
{PLACE_SAND_WALL {UNIT} ${UNIT}.x ${UNIT}.y}
#enddef
#define ABILITY_SAND_WALL
[dummy]
id=eregathsandwall
name= _ "sand wall"
description= _ "Each turn, this unit raises a sand wall. The sand wall protects this unit from all attacks from one direction, chosen randomly each new turn."
[/dummy]
# wmllint: unbalanced-on
[/abilities]
[event]
name=attack
first_time_only=no
[filter]
[filter_adjacent]
id=$second_unit.id
ability=eregathsandwall
# The minus sign in the next line chooses the opposite direction.
adjacent="-$second_unit.variables.wall_dir"
[/filter_adjacent]
[/filter]
[object]
{DEBUG_MSG "on"}
id=forced_cth
[filter]
id=$unit.id
[/filter]
silent=yes
duration=turn end
take_only_once=no
[effect]
apply_to=new_ability
[abilities]
[chance_to_hit]
id=forced_cth
value=0
affect_self=yes
overwrite_specials=both_sides
[/chance_to_hit]
[/abilities]
[/effect]
[/object]
[event]
name=attack end
[remove_object]
object_id=forced_cth
[/remove_object]
[/event]
[/event]
[event]
name=side_turn
first_time_only=no
[store_unit]
[filter]
side=$side_number
ability=eregathsandwall
[/filter]
variable=sand_units
[/store_unit]
[foreach]
array=sand_units
variable=sand_unit
[do]
{REMOVE_SAND_WALL sand_unit $sand_unit.x $sand_unit.y}
{CHOOSE_SAND_WALL_DIR sand_unit}
[/do]
[/foreach]
[/event]
[event]
name=die
first_time_only=no
[filter]
ability=eregathsandwall
[/filter]
{REMOVE_SAND_WALL unit $x1 $y1}
[/event]
[event]
name=unit_placed
first_time_only=no
[filter]
ability=eregathsandwall
[/filter]
{CHOOSE_SAND_WALL_DIR unit}
[/event]
[event]
name=moveto
first_time_only=no
[filter]
ability=eregathsandwall
[/filter]
{REMOVE_SAND_WALL unit $x2 $y2}
{PLACE_SAND_WALL unit $x1 $y1}
# Flip the images back if the user undoes the move.
[on_undo]
{REMOVE_SAND_WALL unit $x1 $y1}
{PLACE_SAND_WALL unit $x2 $y2}
[/on_undo]
[allow_undo]
[/allow_undo]
[/event]
[+abilities]
# wmllint: unbalanced-off
#enddef
- Attachments
-
- Tester.zip
- (491.56 KiB) Downloaded 32 times
Campaigns: Dead Water,
The Founding of Borstep,
Secrets of the Ancients,
and WML Guide
The Founding of Borstep,
Secrets of the Ancients,
and WML Guide
- Atreides
- Posts: 1075
- Joined: March 30th, 2019, 10:38 pm
- Location: On the 2nd story of the centre village of Merwuerdigliebe turning the lights on and off
Re: Variable substitution question
Ooooh nice! Yes that works and is very elegant. Thanks very much.
I almost got the old version to work with Celtic and Ravana's suggestions, had to take a crash course on insert tag and set variables first though. :- ) Learned lots of new stuff.
I almost got the old version to work with Celtic and Ravana's suggestions, had to take a crash course on insert tag and set variables first though. :- ) Learned lots of new stuff.
- Elvish_Hunter
- Posts: 1576
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: Variable substitution question
I suddenly regret asking for it...
Emphasis mine. How is it supposed to be cured if it's incurable? Anyway, if you want to show us that Lua code we can take a look; perhaps it's possible to get rid of it and just use
[harm_unit]
.
I see that your code usesbeetlenaut wrote: ↑January 28th, 2022, 4:44 am I got bored today, so I did the refactoring that Elvish_Hunter suggested.
overwrite_specials=both_sides
, just like the mainline FORCE_CHANCE_TO_HIT
macro, but it's not documented in the wiki. I reported the missing documentation here: https://github.com/wesnoth/wesnoth/issues/5175.Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
- beetlenaut
- Developer
- Posts: 2825
- Joined: December 8th, 2007, 3:21 am
- Location: Washington State
- Contact:
Re: Variable substitution question
You're welcome. I don't do it very often. I didn't think it needed [insert_tag] or variable macro names, but I wasn't sure I was right and decided to find out.
I only used it because the macro does. I don't know what it does either, but I assumed it might be necessary in some cases. I didn't spend too much time trying to figure it out. That's the only place it's used, so there isn't much to go on unless you feel like tracing through the game's source code.Elvish_Hunter wrote: ↑January 28th, 2022, 10:33 pm I see that your code uses overwrite_specials=both_sides, just like the mainline FORCE_CHANCE_TO_HIT macro
Campaigns: Dead Water,
The Founding of Borstep,
Secrets of the Ancients,
and WML Guide
The Founding of Borstep,
Secrets of the Ancients,
and WML Guide
- Atreides
- Posts: 1075
- Joined: March 30th, 2019, 10:38 pm
- Location: On the 2nd story of the centre village of Merwuerdigliebe turning the lights on and off
Re: Variable substitution question
You noticed that too? :- ) Yeah, I'm only quoting what the author said, although to be fair he did write "virtually incurable". Funny thing is that it's not. It's basically 6 poison damage a turn that can be removed at a village like any other poison. The _only_ different thing is that it can cause death, I guess.Elvish_Hunter wrote: ↑January 28th, 2022, 10:33 pmEmphasis mine. How is it supposed to be cured if it's incurable? :? Anyway, if you want to show us that Lua code we can take a look; perhaps it's possible to get rid of it and just use[harm_unit]
.
Not sure it's worth posting, I'll bet there's some simple way to get that same effect. Maybe an event that checks for a unit that has less than 9 HP and has poison status and kill it at turn start.
Re: Variable substitution question
It is possible to create custom poison, but it is more effort, and in game more confusing than its worth. I expect core poison does not cause death to not lose xp.
- Elvish_Hunter
- Posts: 1576
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: Variable substitution question
So it was just false advertising.
It's quite possible by setting the "intoxicated" status on a unit (statuses are just boolean flags), then at every side turn harm every unit with that status. There are two problems with this. First: would it be supposed to stack with regular poison (if you're feeling evil, you can give 14 HPs of damage every turn... )? Second: the AI won't have any knowledge of this special poison, so you'll probably need to code an AI stage in Lua for that.
Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
- Atreides
- Posts: 1075
- Joined: March 30th, 2019, 10:38 pm
- Location: On the 2nd story of the centre village of Merwuerdigliebe turning the lights on and off
Re: Variable substitution question
Uh oh, now you've gone and done it. :- ) I checked and it does indeed use a status very close in name...Elvish_Hunter wrote: ↑January 30th, 2022, 10:00 am:lol: So it was just false advertising.It's quite possible by setting the "intoxicated" status on a unit (statuses are just boolean flags), then at every side turn harm every unit with that status. There are two problems with this. First: would it be supposed to stack with regular poison (if you're feeling evil, you can give 14 HPs of damage every turn... :twisted: )? Second: the AI won't have any knowledge of this special poison, so you'll probably need to code an AI stage in Lua for that.
Here it is (remember you asked for it hehe)
Code: Select all
#define WEAPON_SPECIAL_EREGATH_INTOXICATE
[dummy]
id=eregathintoxicate
name= _ "intoxicate"
description= _ "This unit uses a certain poison that is virtually incurable.
A unit affected by intoxicating poison loses 6hp each turn until they die. Intoxicating poison is only curable at a village."
[/dummy]
# wmllint: unbalanced-on
[/specials]
[/attack]
[event]
name=preload
first_time_only=no
[lua]
code=<<
local _ = wesnoth.textdomain "mydomain"
local old_unit_status = wesnoth.theme_items.unit_status
function wesnoth.theme_items.unit_status()
local u = wesnoth.get_displayed_unit()
if not u then return {} end
local s = old_unit_status()
if u.status.toxicated then
table.insert(s, { "element", {
image = "items/potion-poison.png",
tooltip = _"toxicated: This unit is toxicated. It will lose 6hp each turn until it dies or is cured by standing on a village."
} })
end
return s
end
>>
[/lua]
[/event]
[event]
name=attacker hits
first_time_only=no
[filter_weapon]
special=eregathintoxicate
[/filter_weapon]
[modify_unit]
[filter]
x,y=$x2,$y2
[/filter]
[status]
toxicated=yes
[/status]
[/modify_unit]
[/event]
[event]
name=side turn
first_time_only=no
[store_unit]
[filter]
side=$side_number
[filter_wml]
[status]
toxicated=yes
[/status]
[/filter_wml]
[/filter]
variable=toxicated
[/store_unit]
{FOREACH toxicated i}
{VARIABLE_OP toxicated[$i] sub 6} # toxicated[$i].hitpoints??? seemed to work as is???
[unstore_unit]
variable=toxicated[$i]
text=_ "6"
green=128
[/unstore_unit]
[if]
[variable]
name=toxicated[$i].hitpoints
less_than=1
[/variable]
[then]
[kill]
x,y=toxicated[$i].$x,toxicated[$i].$y
[/kill]
[/then]
[/if]
{NEXT i}
[/event]
[event]
name=moveto
first_time_only=no
[filter]
[filter_location]
terrain=*^V*
[/filter_location]
[/filter]
[modify_unit]
[filter]
x,y=$x1,$y1
[/filter]
[status]
toxicated=no
[/status]
[/modify_unit]
[/event]
[+attack]
[+specials]
# wmllint: unbalanced-off
#enddef
- Elvish_Hunter
- Posts: 1576
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: Variable substitution question
At least this one isn't a mess like that other one...
Anyway, what the Lua code does (or should do, I didn't check if it actually works or what's needed to update it to the new 1.15/1.16 API) is displaying a status icon (just like it happens for slowed, poisoned and petrified units). However, the icon used here has a 72x72 resolution, and status icons have a 24x24 resolution.
Regarding the ability itself, I see that units are filtered using
[filter_wml]
:
Code: Select all
[store_unit]
[filter]
side=$side_number
[filter_wml]
[status]
toxicated=yes
[/status]
[/filter_wml]
[/filter]
variable=toxicated
[/store_unit]
Code: Select all
[store_unit]
[filter]
side=$side_number
status=toxicated
[/filter]
variable=toxicated
[/store_unit]
{FOREACH} {NEXT}
cycle should be replaced with a [foreach]
tag. The whole variable setting operation to subtract HPs can actually be replaced with [harm_unit]
; it's also interesting to note that, as it is, intoxication can stack with regular poison (no filters to prevent this) and can only be cured in villages - no healers allowed! But at least, moving into a village is enough to cure it (yes, they used a moveto instead of a side_turn event).Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)