[engine] Extend weapon syntax for multiple weapon type series in same attack

Brainstorm ideas of possible additions to the game. Read this before posting!

Moderator: Forum Moderators

Forum rules
Before posting a new idea, you must read the following:
Post Reply
User avatar
WhiteWolf
Forum Moderator
Posts: 769
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

[engine] Extend weapon syntax for multiple weapon type series in same attack

Post by WhiteWolf »

Hello,

First let me start by saying that I am by no means a combat expert, and this is just my "Hollywood-infected" view of the technical details of armed fights :)
I think it would enrich Wesnoth's combat system, and open up room for some very interesting combos, to allow an attack to have multiple weapon types.

Motivation
The easiest example is a sword. Why is 99% of the type blade attack (1% someone tries pierce in UMC), when in reality it's a combination of the two, "parry, cut, parry, thrust", as you'd say? Hell, you could even use half-swording and use it as an impact attack. Of course this needs training and thus heavily depends on the unit, so I am not saying anything like this should be implemented in existing mainline units, I only wish for the option to create such units, that can use a combination of weapon types, thanks to their training/vigilance.

Implementations
The aim then is to define what type=blade,pierce,impact would mean for the game, and I multiple ideas with pros and cons.
  • The first is that the definition should be a comma separated list that describes the attack type of each consecutive strike. In the example above, the first strike would deal blade damage if hit, the second would deal pierce damage, the third impact. In a type=cold,cold,fire example the first hit would deal cold damage, so would the second, and the third would be fire. What if the number of attacks is higher than the length of this list? It should rewind from the start. type=blade,pierce with number=4 would mean blade,pierce,blade,pierce.
    Advantage: Deterministic, the outcome statistics, though a lot more difficult than with regular attacks, can always be calculated, and the player can evaluate his chances. It can "realistically" follow fixed advanced techniques (f.e. upper cut, lower cut, thrust == blade,blade,pierce).
    Disadvantages: The attack statistics would be hard to calculate because of the changing damage output. The question can also arise, that if an opponent is weak against blade but strong against pierce, then why does the unit have a strike with pierce instead of using blade only.(*)
  • Second implementation is that the list is a pool of available choices for the engine, and it picks the one that the current opponent is most sensitive to.
    Advantage: Deterministic, the player can always evaluate his chances. Allows for very versatile "swiss-army-knife" units.
    Disadvange: (huge): This is already possible by inserting the same attack with different types. Also, the balancing of such units could be tedious.
  • Third idea is the attack type should be decided randomly before the fight. This is more of a "wild-card" unit approach, that varies from trial to trial.
    Advantage: Nothing really, apart from the "wild-card", "fun-rng-unit" aspect.
    Disadvantage: Attack statistics cannot be calculated, backfiring attacks could be frustrating.
  • Last idea is that the attack type should be randomly recalculated at each strike. This is an even more crazy variant of the third option. However, it might be the better variant. With lots of attack strikes, there is always a good chance that some hits will have the type that the opponent is sensitive to, but still has some weaker blows to balance it out.
    Advantage: Unit balancing is probably not that hard. It's an even more "wild-card" unit approach with different type and thus different damage output with each strike :)
    Disadvantage: Attack statistics cannot be calculated, backfiring attacks could be frustrating. The question can also arise, that if an opponent is weak against a certain type, then why are there random weaker attacks of other types. (*)
(*): I think these points can be explained by the argument that a fight is not training, and it won't go perfectly. Even if your opponent is weak against for example blade, you can't always go for a cut. If you see an opening for a thrust, then why shouldn't you take it? Yes, it will deal less damage than a cut would have done, but there is no option to do a cut there. Small damage is still more than no damage - so you go for the thrust.

I personally prefer option #1, because it would open up possibilities for same crazy (in a good way) units in UMC with great variety. Combined with having multiple attacks like this, each of them corresponding to different technique, the RPG technique-unlock progression sounds great to me :)

What's currently possible and what would be needed for quasi-implementations
Number two is easily possible. However, I doubt that any other implementations are possible at the moment. You could create list-like weapon types with [language] so that it means something for the engine with an id and has a nice description for the player, but as far as I know, you can only modify a weapon's damage before the attack ( in attack event), and not during the attack (attacker hits and such).
What I can imagine as a great tool is kind of an "attacker/defender strikes" event that can modify all aspects of a weapon during combat, and fires before the hit connects and hit/miss is evaluated. I do not know if this is already possible with Lua, that would be cool.
Full engine support would be the best of course :)

Any opinions?
Main UMC campaigns: The Ravagers - now for 1.16, with new bugs!
Old UMC works: The Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
User avatar
Ravana
Forum Moderator
Posts: 3000
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by Ravana »

You can modify damage between strikes, see concentrated and growing fury specials.
User avatar
WhiteWolf
Forum Moderator
Posts: 769
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by WhiteWolf »

Quite misremembered that :doh: Then, it's already quite possible to experiment with something like this, great :D
Main UMC campaigns: The Ravagers - now for 1.16, with new bugs!
Old UMC works: The Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
User avatar
Celtic_Minstrel
Developer
Posts: 2207
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by Celtic_Minstrel »

WhiteWolf wrote: October 10th, 2019, 7:04 am Disadvantages: The attack statistics would be hard to calculate because of the changing damage output.
WhiteWolf wrote: October 10th, 2019, 7:04 am Disadvantage: Attack statistics cannot be calculated, backfiring attacks could be frustrating.
I think you're vastly overestimating the difficulty of determining the attack statistics. For the first case, you just need to modify the formula or something – I'm not sure exactly how easy it is, but I wouldn't think it difficult enough to be a significant advantage.

As for the third case, uhh, what? Attack statistics cannot be calculated because it's random? Statistics are all about random things! Of course you could calculate the attack statistics if the game chose a random damage type from the list. It would be just the same as the first case – you need to change the logic of how the calculations go.

Aside from the weird conceptions of what the disadvantages are, I kinda like the idea (I'd probably prefer the first method). Not enough to go in and implement it, though.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
User avatar
WhiteWolf
Forum Moderator
Posts: 769
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by WhiteWolf »

Celtic_Minstrel wrote: November 14th, 2019, 4:49 am
WhiteWolf wrote: October 10th, 2019, 7:04 am Disadvantages: The attack statistics would be hard to calculate because of the changing damage output.
WhiteWolf wrote: October 10th, 2019, 7:04 am Disadvantage: Attack statistics cannot be calculated, backfiring attacks could be frustrating.
I think you're vastly overestimating the difficulty of determining the attack statistics. For the first case, you just need to modify the formula or something – I'm not sure exactly how easy it is, but I wouldn't think it difficult enough to be a significant advantage.

As for the third case, uhh, what? Attack statistics cannot be calculated because it's random? Statistics are all about random things! Of course you could calculate the attack statistics if the game chose a random damage type from the list. It would be just the same as the first case – you need to change the logic of how the calculations go.
Yeah, that again shows how I did not think this through well enough before talking :)
Once I find the time to give it a more thorough consideration and implement something like this (not in the very-near future sadly), I'll post how it performs in action.
Main UMC campaigns: The Ravagers - now for 1.16, with new bugs!
Old UMC works: The Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
User avatar
Celtic_Minstrel
Developer
Posts: 2207
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by Celtic_Minstrel »

For the record, I believe it would be possible to implement any of these four approaches using WML and Lua. Also… I think all four approaches could be supported with a single implementation based on WML formulas.

These sample formulas are not robust and probably contain errors, but should give a general idea of how a formula can make any of the three approaches possible.
WhiteWolf wrote: October 10th, 2019, 7:04 am The first is that the definition should be a comma separated list that describes the attack type of each consecutive strike. In the example above, the first strike would deal blade damage if hit, the second would deal pierce damage, the third impact. In a type=cold,cold,fire example the first hit would deal cold damage, so would the second, and the third would be fire. What if the number of attacks is higher than the length of this list? It should rewind from the start. type=blade,pierce with number=4 would mean blade,pierce,blade,pierce.

Code: Select all

type="(
	# Assume n is the current hit number, so 1 for the first hit, 2 for the second, etc. #
	options[n % size(options)]
where
	options = ['blade', 'pierce', 'impact']
)"
WhiteWolf wrote: October 10th, 2019, 7:04 am Second implementation is that the list is a pool of available choices for the engine, and it picks the one that the current opponent is most sensitive to.

Code: Select all

type="(
	# Step 3: return the damage type with that lowest resistance. #
	options[index_of(worst, resistances)]
where
	# Step 2: determine which of those resistances is the lowest. #
	worst = reduce(resistances, infinity, min(a, b))
where
	# Step 1: figure out the enemy's resistance to each of the types. #
	resistances = map(options, enemy.resistance[self])
where
	options = ['blade', 'pierce', 'impact'],
	infinity = 999
)"
(Note that the "resistance" variable doesn't actually exist currently, so this formula would require an engine change to work.)
WhiteWolf wrote: October 10th, 2019, 7:04 amThird idea is the attack type should be decided randomly before the fight. This is more of a "wild-card" unit approach, that varies from trial to trial.
Well, okay, this one's tricky to implement as a formula that's evaluated on each hit… I think it'd still be possible, maybe feed the current turn number into a complicated formula that has random-like output. The following is a really bad example of this, but could probably work.

Code: Select all

type="(
	options[i]
where
	# [4] And last of all, round to an integer #
	i = floor(
		# [3] Prevent negative values #
		abs(
			# [1] Pretend this is a random function... #
			sin(current_turn % 360)
			# [2] Sine is in range -1..1, scale it to range -3..3 #
			* size(options)
		)
	)
where
	options = ['blade', 'pierce', 'impact']
)"
Ultimately, I think anyone wanting this effect would be better off using an event-based approach.
WhiteWolf wrote: October 10th, 2019, 7:04 am Last idea is that the attack type should be randomly recalculated at each strike. This is an even more crazy variant of the third option. However, it might be the better variant. With lots of attack strikes, there is always a good chance that some hits will have the type that the opponent is sensitive to, but still has some weaker blows to balance it out.

Code: Select all

type="(
	# Pick a random number between 1 and the number of options. #
	options[1 d size(options)]
where
	options = ['blade', 'pierce', 'impact']
)"
(I don't remember if the dice syntax works like that.)
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
User avatar
WhiteWolf
Forum Moderator
Posts: 769
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by WhiteWolf »

Celtic_Minstrel wrote: November 19th, 2019, 5:35 amAlso… I think all four approaches could be supported with a single implementation based on WML formulas.

These sample formulas are not robust and probably contain errors, but should give a general idea of how a formula can make any of the three approaches possible.
Do you mean that the unit_type cfg could explicitly accept these formulas? Whoa, that'd make things easy and cool. :D I tried the
Celtic_Minstrel wrote: November 19th, 2019, 5:35 am

Code: Select all

type="(
	# Assume n is the current hit number, so 1 for the first hit, 2 for the second, etc. #
	options[n % size(options)]
where
	options = ['blade', 'pierce', 'impact']
)"
,and the
Celtic_Minstrel wrote: November 19th, 2019, 5:35 am

Code: Select all

type="(
	# Pick a random number between 1 and the number of options. #
	options[1 d size(options)]
where
	options = ['blade', 'pierce', 'impact']
)"
(I don't remember if the dice syntax works like that.)
quickly, sadly none of it works, I get a lua error:
<Lua error> [string "..."]:12: <name> expected near 'local', which I can't really understand.
Also, how would you determine the n hit number in the first case? You'd still need an event that sets it as a variable for attacks with this weapon type?
Main UMC campaigns: The Ravagers - now for 1.16, with new bugs!
Old UMC works: The Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
User avatar
Celtic_Minstrel
Developer
Posts: 2207
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: [engine] Extend weapon syntax for multiple weapon type series in same attack

Post by Celtic_Minstrel »

WhiteWolf wrote: November 19th, 2019, 2:27 pm
Celtic_Minstrel wrote: November 19th, 2019, 5:35 amAlso… I think all four approaches could be supported with a single implementation based on WML formulas.

These sample formulas are not robust and probably contain errors, but should give a general idea of how a formula can make any of the three approaches possible.
Do you mean that the unit_type cfg could explicitly accept these formulas? Whoa, that'd make things easy and cool. :D
Yes, if someone implements it in the engine. (If you're doing it in an add-on you'd probably have to find some other way of storing that information.)
WhiteWolf wrote: November 19th, 2019, 2:27 pm sadly none of it works, I get a lua error:
<Lua error> [string "..."]:12: <name> expected near 'local', which I can't really understand.
Also, how would you determine the n hit number in the first case? You'd still need an event that sets it as a variable for attacks with this weapon type?
If you want to just test out the formulas with Lua, the following code should give you an idea of how to do so (using one of the simplest formulas from the previous post); you can also press F in-game to open a formula evaluation console, but for testing purposes you'd then need to define n and any other variables in a where clause.

Code: Select all

local formula = wesnoth.compile_formula "options[n % size(options)] where options = ['blade', 'pierce', 'impact']:
-- compile_formula returns the formula as a function, so you can call it like this:
local result = formula{
	n = 1,
	enemy = wesnoth.get_unit "some_unit_id",
	current_turn = 7,
	-- any other variables you need
}
Remember that the super-crazy formula for choosing the best type actually won't work, because formula units don't have a resistance variable (yet).
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
Post Reply