Elvish_Hunter's Lua thread

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Yesterday I finished writing my second Lua tag. I took inspiration from the FutureWML page, and so we have an [harm_unit] tag.

Code: Select all

local helper = wesnoth.require "lua/helper.lua"

function wesnoth.wml_actions.harm_unit(cfg)
	local amount = tonumber(cfg.amount) or helper.wml_error("[harm_unit] missing required amount= attribute")
	local damage_type = (cfg.damage_type)
	local damage
	for index, unit_to_harm in ipairs(wesnoth.get_units(cfg)) do
		if damage_type then
			damage = (amount / 100) * wesnoth.unit_resistance(unit_to_harm, tostring(damage_type))
			else
				damage = amount
		end
		damage = math.floor(damage)
		unit_to_harm.hitpoints = unit_to_harm.hitpoints - damage
		wesnoth.float_label(unit_to_harm.x,unit_to_harm.y,string.format("<span foreground='red'>%d</span>",damage))
		if unit_to_harm.hitpoints <= 0 then
			wesnoth.fire("kill", {id=unit_to_harm.id, animate="yes", fire_event="yes"})	
		end
		wesnoth.set_variable("harm_amount", damage)
	end
	wesnoth.wml_actions.redraw {}
end
How it works:
This tag should harm selected units in a similar way to what happens in combat, and if their HP goes to 0 or less, kill them.
It requires the following:
- StandardUnitFilter, to select the units to harm
- amount, the amount of damage to apply on units; if missing, this gives an error
- damage_type, if present the final damage will be altered by unit resistance
It also sets the damage applied to variable harm_amount.

Now, I have three questions:
- FutureWML says that it should have also a "alignment" key. There is any way to implement this, that should be getting a particular location's ToD and check it against cfg.alignment? Please keep in mind that I'm still a Lua beginner.
- I placed ipairs(wesnoth.get_units) directly in the cycle for. Should I have used this instead?

Code: Select all

local units_to_harm=wesnoth.get_units(cfg)
for index, unit_to_harm in ipairs(units_to_harm) do
- If a leader is killed by wesnoth.fire{"kill"}, if no last breath/death event for this unit is defined, or if such event doesn't contain [endlevel] result=defeat, defeat isn't immediate, but happens on the next turn start. Is it an error of mine that I didn't noticed? If yes, how to fix this?

I already tested it, and seems to work. Can you kindly tell me if there is something else to correct, or is it ready for core?
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

Maybe you should post the stuff as patches then (if you want to get write access like me :P)
http://gna.org/patch/?group=wesnoth

2 problems:
-The fired events can invalidate unit references which are still to be iterated on (kill one of the remaining units) - look how it's done in move_unit (it's the same problem there).
-harm_amount is never cleared. Don't think there's a functionality for this yet... it should be auto-cleared when leaving the calling event.
Elvish_Hunter wrote:There is any way to implement this, that should be getting a particular location's ToD and check it against cfg.alignment?
If you've found no suitable wesnoth.something function then there's no way yet apparently...
Elvish_Hunter wrote: - I placed ipairs(wesnoth.get_units) directly in the cycle for. Should I have used this instead?

Code: Select all

local units_to_harm=wesnoth.get_units(cfg)
for index, unit_to_harm in ipairs(units_to_harm) do
If you need units_to_harm later on make a variable, else call it directly.
Elvish_Hunter wrote:- If a leader is killed by wesnoth.fire{"kill"}, if no last breath/death event for this unit is defined, or if such event doesn't contain [endlevel] result=defeat, defeat isn't immediate, but happens on the next turn start. Is it an error of mine that I didn't noticed? If yes, how to fix this?
I has always been this way afaik. (idependently from your lua code here)
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Anonymissimus wrote:Maybe you should post the stuff as patches then (if you want to get write access like me :P)
I'd be honoured to gain a Code and WML Contributor title, of course, but I don't know how to make patches on Windows...
EDIT: this mean that I'll have to install SVN, right?
Anonymissimus wrote:-The fired events can invalidate unit references which are still to be iterated on (kill one of the remaining units) - look how it's done in move_unit (it's the same problem there).
I'll look. Anyway, when I tested, this code really killed only those units that needed to be killed, that is those with 0 or less HP.
EDIT: Looked, apparently it's done in the same way:

Code: Select all

if fire_event then
				wesnoth.fire_event("moveto", x, y, x2, y2)
			end
Anonymissimus wrote:-harm_amount is never cleared.
harm_amount is not supposed to be cleared, but instead stored as WML variable. Not a bug, a feature.
http://wiki.wesnoth.org/FutureWML wrote:Ideas that are fairly sound
New Actions
* [harm_unit] - harms a unit(s) then saves the amount as $harm_amount
o [filter]
o amount
o damage_type
o alignment
Anonymissimus wrote:If you've found no suitable wesnoth.something function then there's no way yet apparently...
Then I can't add alignment key. I hoped that I simply overlooked it, but this time it wasn't my mistake (once in a lifetime :P )
Anonymissimus wrote:If you need units_to_harm later on make a variable
No, it's needed only in the cycle for.
Anonymissimus wrote:I has always been this way afaik.
Ah, OK. Thanks for all your clarifications!
If I'll have some other questions, or make some other stuff, I'll post here, of course. :)
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

Elvish_Hunter wrote: EDIT: this mean that I'll have to install SVN, right?
yes
harm_amount is not supposed to be cleared, but instead stored as WML variable. Not a bug, a feature.
It's not a variable specifically requested by the wml coder as opposed to the other variable-storing tags. It should behave like e.g. second_unit (in moveto events) and such, which are only valid until the end of the event. harm_amount however remains until teh end of the campaign unless cleared by the wml coder.
Then I can't add alignment key. I hoped that I simply overlooked it, but this time it wasn't my mistake (once in a lifetime :P )
You could do wesnoth.get_locations for all locs matching the alignment= key and then look in the obtained array for the unit's hex but it'd be very inefficient. I guess I could make a function available in the lua interface but you need to wait until next 1.9 release unless you compile yourself.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Anonymissimus wrote:It's not a variable specifically requested by the wml coder as opposed to the other variable-storing tags. It should behave like e.g. second_unit (in moveto events) and such, which are only valid until the end of the event. harm_amount however remains until teh end of the campaign unless cleared by the wml coder.
It should. But by looking to mainline wml-tags and helper (not so easy, for a beginner like me) I wasn't able to find any reference to second_unit. I tried also adding the value to wesnoth.current.event_context, either by using table.insert or directly, obtaining an error "table expected, got userdata". So, I suppose that such auto-storing (unit, second_unit, x1, x2...) are still managed by C++, that means: I have no idea, for now at least, on how to make harm_amount behave like second_unit.
Anonymissimus wrote:You could do wesnoth.get_locations for all locs matching the alignment= key and then look in the obtained array for the unit's hex but it'd be very inefficient.
I imagined this alignment to, in fact, assign alignment to the damage itself, that is if [harm_unit] has alignment=chaotic, and happens during daytime, damage will be reduced by 25%, like if done by a chaotic unit. I wonder, however, like this key will work with liminal alignment, or with 24-hour ToD... :?
I guess that I'll wait for your function, or for me to have a good idea on this.
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)
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: Elvish_Hunter's Lua thread

Post by silene »

Elvish_Hunter wrote:I imagined this alignment to, in fact, assign alignment to the damage itself, that is if [harm_unit] has alignment=chaotic, and happens during daytime, damage will be reduced by 25%, like if done by a chaotic unit. I wonder, however, like this key will work with liminal alignment, or with 24-hour ToD... :?
I guess that I'll wait for your function, or for me to have a good idea on this.
Concerning alignment (and it would also subsume resistance computation), you could perhaps create a dummy unit with a given alignment and a given damage type, and then throw it at the to-be-damaged unit (LuaWML:Units#wesnoth.simulate_combat.) I guess the easiest way to interpret the result of the simulation would be to give the dummy unit 100% cth, a dummy range, and a single attack.
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Thanks silene. For what I understood, at this point, is better to remove the alignment= key idea, to make this code simpler to maintain.
There is still one question, however: there is any way to make harm_amount automatically cleared at the end of the event in which [harm_unit] is used? If not, I think that I'll add a key "output=yes/no", with default no (that is, if no key given it will be nil), so it will be up to the campaign/UMC/whatever author to decide if have the damage stored in variable harm_amount or not.
Thanks again, to you and Anonymissimus, for your support! :)
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

I'd just do
if cfg.variable then wesnoth.set_variable(cfg.variable, ...) else ...

No, there's not (yet) any way.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Anonymissimus wrote:if cfg.variable then wesnoth.set_variable(cfg.variable, ...) else ...
Yes, I thought about this possibility. I'll do this way.
Anonymissimus wrote:No, there's not (yet) any way.
Good to know.
Now, I'll correct this tag to add the variable= key, test it and try to make a patch.

EDIT: tag corrected, here there is the new code.
Spoiler:
I added the variable key, and also a scroll= key, that, if enabled, scrolls to each unit before harming it. Final question: I rounded damage with math.floor. Considering that, in Wesnoth, even with 100% resistance, every successful blow deals at least 1 damage, should I have used math.ceil, or

Code: Select all

damage=( (amount / 100) * wesnoth.unit_resistance(unit_to_harm, tostring(damage_type))) or 1 --untested
or another, more complex, structure? (Anyway, I think that it'll be quite rare that someone will try to cast fire damage upon an Armageddon Drake, that with both math.ceil and floor will be 0...)
After that, I think that it will be ready for the patch.
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

Here's a patch, making
local time = wesnoth.get_time_of_day(10, 10)
available. time is the time's id string then. When I find out more about the secrets of returning a table there may be mroe info.
For compiling I suggest Crab's cmake+MSVC 9 strategy, that's the easiest one, especially since you know how .bat files work.
time_of_day.patch
(1.28 KiB) Downloaded 648 times
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Thanks for your patch, but I decided to not add the alignment key, at least for now, mainly because we have at least three different ToD tables around (default, two suns, 24 hour, and users can define new ToD in WML). So, to add it, the best solution will be having a function that gets not only the ToD ID, but also its liminal_bonus and lawful_bonus keys.
Anonymissimus wrote:For compiling I suggest Crab's cmake+MSVC 9 strategy, that's the easiest one, especially since you know how .bat files work.
I decided to go for the even easier way: Ubuntu 10.04 LTS, secondary Linux machine, svn diff>harm_unit.patch to the Terminal. :) For now, my main machine will remain Windows, also because I have still a lot of things to learn in Linux, but at least I have a secondary system just in case.
Anyway, here there is my patch and my wiki description (feel free to correct it), I'll upload the patch also on Gna.
EDIT: patch uploaded on Gna as #2230.
harm_unit.patch
Feel free to correct it if needed.
(1.85 KiB) Downloaded 751 times
Spoiler:
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

-you don't need () around cfg.something
-tonumber(args) returns nil in case args is there but can't be converted so change the error message to something more general
-the filter tag should be left away as in your previous versions I think, that extra key's don't harm
-the line
if not units_to_harm then return end
does nothing, you already check whether it is nil
-d in string.format stands for digit iirc, that is, damage >= 10 is not correctly handled
-use wml_actions.tag(...), not wesnoth.fire("tag", ...); the ladder is deprecated
-there's a wesnoth.delay function; I'd prefer wesnoth.tag over wml_actions.tag if there is a suitable function
-if you do the cfg.scroll option I'd make the delay time an argument too (magic numbers are bad)
-use unit_to_harm.valid, see this:

Code: Select all

		wesnoth.put_unit({ type = "Troll", x = 20, y = 10 })
		local troll = wesnoth.get_units({ x = 20, y = 10 })[1]
		wml_actions.kill({ x = 20, y = 10 }) -- this may be done by a fired event for a unit which is still to be iterated on
 		local x = troll.x -- error since the reference "troll" is no longer valid
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Anonymissimus wrote:-the filter tag should be left away as in your previous versions I think, that extra key's don't harm
I made it this way to match the behaviour of [heal_unit], that requires a [filter] tag.
Anonymissimus wrote:-d in string.format stands for digit iirc, that is, damage >= 10 is not correctly handled
Copying from my Lua reference:
Formatting field types
%d decimal integer
Anonymissimus wrote:-use wml_actions.tag(...), not wesnoth.fire("tag", ...); the ladder is deprecated
-there's a wesnoth.delay function; I'd prefer wesnoth.tag over wml_actions.tag if there is a suitable function
Am I wrong, or wml_actions.kill and wesnoth.delay are completely undocumented in LuaWML? And here, there isn't written that wesnoth.fire is deprecated.Thanks for warning me. :)
Anonymissimus wrote:-if you do the cfg.scroll option I'd make the delay time an argument too (magic numbers are bad)
OK, but 500 will remain as default value.
Anonymissimus wrote:-use unit_to_harm.valid, see this:
I don't (yet) understand how this may happen, considering that I check for unit ID to kill, but fixed anyway.
Here there is a corrected version, it seems to work, if it's fine I'll upload it again on Gna.
Spoiler:
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)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Elvish_Hunter's Lua thread

Post by Anonymissimus »

Elvish_Hunter wrote:Am I wrong, or wml_actions.kill and wesnoth.delay are completely undocumented in LuaWML? And here, there isn't written that wesnoth.fire is deprecated.Thanks for warning me. :)
All action tags (C++ ones and lua ones) are available as wml_actions.tag().
It's not quite deprecated yet - look in compatibility-1.8.lua.

This breaks your current code:

Code: Select all

	[event]
		name=start
		{GENERIC_UNIT 1 Troll 1 1}
		{GENERIC_UNIT 1 Troll 2 2}
		[harm_unit]
			[filter]
			[/filter]
			amount=200
		[/harm_unit]
	[/event]
	[event]
		name=die
		[kill]
		[/kill]
	[/event]
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
User avatar
Elvish_Hunter
Posts: 1575
Joined: September 4th, 2009, 2:39 pm
Location: Lintanir Forest...

Re: Elvish_Hunter's Lua thread

Post by Elvish_Hunter »

Anonymissimus wrote:All action tags (C++ ones and lua ones) are available as wml_actions.tag().
Thanks for the information.
Anonymissimus wrote:This breaks your current code:
So, I did some experiments, and this happens because after killing the unit with [kill], [harm_unit] cannot longer find it.
I have no good ideas on how to fix it, at the moment :( (and this can be understandable, considering that until two weeks ago I never wrote custom tags); if you have an idea, can you kindly suggest me how to fix it?
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)
Post Reply