Merge pull request #488 from CelticMinstrel/backstab

This commit is contained in:
Celtic Minstrel 2015-09-19 15:42:35 -04:00
commit e74c119b9b
55 changed files with 308 additions and 273 deletions

View file

@ -91,6 +91,19 @@ Version 1.13.1+dev:
* Ability to patch movetypes to account for custom terrains or damage types
* Removed y offset by -1 from [message]'s scroll-to-unit logic.
* Add [found_item] ConditionalWML to check if an [object]id= ActionWML has been taken
* New auto-stored WML variable $other_unit usable in the following contexts:
* [filter_adjacent] - $other_unit refers to the $this_unit of the enclosing filter
(In weapon specials and unit abilities, the unit owning the ability.)
* [filter_self/opponent/attacker/defender] (weapon specials)
$other_unit refers to the other unit in the attack
(eg in [filter_self], it's the opponent)
* [affect_adjacent][filter] (unit abilities)
$other_unit refers to the unit owning the ability
* New rotate operators for directions: dir:cw and dir:ccw
(This is useful mostly in conjunction with variable substitution.)
These operators are applied after the existing '-' operator that takes the
opposite direction.
* Adjacency filters in abilities and weapon specials now support count= and is_enemy=
* Editor:
* Added Category field and color sliders to the Edit Label panel.
* Miscellaneous and bug fixes:

View file

@ -22,7 +22,7 @@
cost=36
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "A king who is skilled at combat commands great respect — especially among soldiers. His strength allows him to wield powerful weapons and move quickly, even with heavy armor. His leadership abilities inspire his troops."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
die_sound=mermen-die.ogg

View file

@ -22,7 +22,7 @@
cost=60
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "Warrior kings often become legendary in their own time. Their skill and strength allows them to be truly formidable in battle. They are also fast despite their heavy armor. Even skilled soldiers look upon them with awe."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
die_sound=mermen-die.ogg

View file

@ -22,7 +22,7 @@
cost=15
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "A young king with some battle experience will be treated with some respect. Common citizens will be inspired to do their utmost when the king is nearby, but battle-hardened veterens will be less impressed."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
die_sound=mermen-die.ogg

View file

@ -7,7 +7,7 @@
name=_"King of Wesnoth"
hitpoints=68
[abilities]
{ABILITY_LEADERSHIP_LEVEL_5}
{ABILITY_LEADERSHIP}
[/abilities]
experience=250
level=5

View file

@ -8,7 +8,7 @@
hitpoints=57 # A little tougher
[abilities]
{ABILITY_HEALS}
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
movement=6 # A little faster
advances_to=Mage Magister

View file

@ -6,7 +6,7 @@
id=Mage Leader
name=_"Mage Leader"
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
movement=6 # A little faster
experience=100 # Same as base unit

View file

@ -8,7 +8,7 @@
hitpoints=57 # A little tougher
[abilities]
{ABILITY_HEALS}
{ABILITY_LEADERSHIP_LEVEL_4}
{ABILITY_LEADERSHIP}
[/abilities]
[resistance]
fire=60 # Add fire resistance

View file

@ -22,7 +22,7 @@
{AMLA_DEFAULT}
cost=60
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
{ABILITY_INITIATIVE}
[/abilities]
usage=fighter
@ -78,7 +78,7 @@
{AMLA_DEFAULT}
cost=110
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
{ABILITY_INITIATIVE}
[/abilities]
usage=fighter

View file

@ -7,7 +7,7 @@
image="units/konrad-commander{AFFIX}.png"
hitpoints=45
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -7,7 +7,7 @@
image="units/konrad-lord{AFFIX}.png"
hitpoints=68
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -26,7 +26,7 @@
advances_to=Battle Princess
cost=39
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
{ABILITY_INITIATIVE}
[/abilities]
usage=fighter
@ -87,7 +87,7 @@
advances_to=Battle Princess
cost=110
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
{ABILITY_INITIATIVE}
[/abilities]
usage=fighter

View file

@ -20,7 +20,7 @@
advances_to=Death Knight
cost=36
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
{ABILITY_SUBMERGE}
[/abilities]
usage=fighter

View file

@ -9,7 +9,7 @@
{MAGENTA_IS_THE_TEAM_COLOR}
hitpoints=53
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -9,7 +9,7 @@
{MAGENTA_IS_THE_TEAM_COLOR}
hitpoints=42
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -15,7 +15,7 @@
cost=19
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
hide_help=true
description= _ "King of Wesnoth."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}

View file

@ -7,7 +7,7 @@
image="units/noble-commander.png"
hitpoints=48
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -7,7 +7,7 @@
image="units/noble-lord.png"
hitpoints=64
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=smallfoot
movement=6

View file

@ -6,7 +6,7 @@
[/base_unit]
name= _ "Warrior King"
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "As the leaders of the human Kingdoms, Kings are responsible for ruling and protecting their subjects. Kings can coordinate the attacks of level 1 and 2 units, thereby increasing their fighting efficiency."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
# This is a somewhat elderly royal warrior

View file

@ -8,7 +8,7 @@
{MAGENTA_IS_THE_TEAM_COLOR}
hitpoints=46
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
{ABILITY_DISTRACT}
[/abilities]
movement_type=elusivefoot

View file

@ -8,7 +8,7 @@
{MAGENTA_IS_THE_TEAM_COLOR}
hitpoints=62
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
{ABILITY_DISTRACT}
[/abilities]
movement_type=elusivefoot

View file

@ -9,7 +9,7 @@
hitpoints=33
[abilities]
{ABILITY_DISTRACT}
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=elusivefoot
movement=7

View file

@ -21,7 +21,7 @@
experience=80
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "Leaders of the border guards, mounted commanders are trained not only to ride and fight, but to lead. They command the garrisons that keep peace in the provinces of Wesnoth."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}+{SPECIAL_NOTES_CHARGE}
die_sound=horse-die.ogg

View file

@ -21,7 +21,7 @@
{AMLA_DEFAULT}
experience=150
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
usage=fighter
description= _ "Veteran commanders have faced battle many times and led their men with steady hands and calm determination."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}+{SPECIAL_NOTES_SLOW}

View file

@ -20,7 +20,7 @@
experience=75
advances_to=Infantry Commander
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
usage=fighter
description= _ "Commanders of the infantry of Wesnoth are responsible for the garrison and defense of the border towns and villages. They are drawn from the ranks of veteran soldiers, not nobility, and all have shown leadership and courage in battle."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}

View file

@ -21,7 +21,7 @@
advances_to=Horseman Commander
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "Scarcely 17 or 18 years old, the sons of knights and lords were given mounts and swords and told to become leaders of men. Those whose mettle was strong enough for the task became the commanders of the armies of Wesnoth."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
die_sound=horse-die.ogg

View file

@ -23,7 +23,7 @@
experience=150
usage=fighter
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
description= _ "Being the most able leaders of the border guards, Mounted Generals are trained not only to ride and fight, but to lead. They command the garrisons that keep peace in the provinces of Wesnoth."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}+{SPECIAL_NOTES_CHARGE}
die_sound=horse-die.ogg

View file

@ -13,7 +13,7 @@
[/leading_anim]
hitpoints=47
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
movement_type=desert_elf
movement=5

View file

@ -7,7 +7,7 @@
{MAGENTA_IS_THE_TEAM_COLOR}
hitpoints=62
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[leading_anim]
start_time=-150

View file

@ -38,20 +38,8 @@
image=units/elves-desert/kaleh-leading.png
[effect]
apply_to=new_ability
[filter]
level=2
[/filter]
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
[/abilities]
[/effect]
[effect]
apply_to=new_ability
[filter]
level=3
[/filter]
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[/effect]
) {VARIANT_NAME}}

View file

@ -30,7 +30,7 @@
advances_to=null
cost=60
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
usage=fighter
description= _ "In this new harsh world, might often makes right and these commanders are strong enough to rise to positions of leadership. Leading small groups of warriors, commanders rally their troops around them and show no mercy to their enemies, striking fiercely with both sword and bow."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}

View file

@ -96,159 +96,10 @@ A poisoned unit cannot be cured of its poison by a healer, and must seek the car
[/resistance]
#enddef
#define ABILITY_LEADERSHIP_LEVEL_1
# Canned definition of the Leadership (level 1) ability to be included in an
# [abilities] clause.
[leadership]
id=leadership
value=25
cumulative=no
name= _ "leadership"
female_name= _ "female^leadership"
description= _ "This unit can lead our own units that are next to it, making them fight better.
Adjacent own units of lower level will do more damage in battle. When a unit adjacent to, of a lower level than, and on the same side as a unit with Leadership engages in combat, its attacks do 25% more damage times the difference in their levels."
affect_self=no
[affect_adjacent]
[filter]
level=0
[/filter]
[/affect_adjacent]
[/leadership]
#enddef
#define ABILITY_LEADERSHIP_LEVEL_2
# Canned definition of the Leadership (level 2) ability to be included in an
# [abilities] clause.
[leadership]
id=leadership
value=50
cumulative=no
name= _ "leadership"
female_name= _ "female^leadership"
description= _ "This unit can lead our own units that are next to it, making them fight better.
Adjacent own units of lower level will do more damage in battle. When a unit adjacent to, of a lower level than, and on the same side as a unit with Leadership engages in combat, its attacks do 25% more damage times the difference in their levels."
affect_self=no
[affect_adjacent]
[filter]
level=0
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=25
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=1
[/filter]
[/affect_adjacent]
[/leadership]
#enddef
#define ABILITY_LEADERSHIP_LEVEL_3
# Canned definition of the Leadership (level 3) ability to be included in an
# [abilities] clause.
[leadership]
id=leadership
value=75
cumulative=no
name= _ "leadership"
female_name= _ "female^leadership"
description= _ "This unit can lead our own units that are next to it, making them fight better.
Adjacent own units of lower level will do more damage in battle. When a unit adjacent to, of a lower level than, and on the same side as a unit with Leadership engages in combat, its attacks do 25% more damage times the difference in their levels."
affect_self=no
[affect_adjacent]
[filter]
level=0
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=50
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=1
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=25
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=2
[/filter]
[/affect_adjacent]
[/leadership]
#enddef
#define ABILITY_LEADERSHIP_LEVEL_4
# Canned definition of the Leadership (level 4) ability to be included in an
# [abilities] clause.
[leadership]
id=leadership
value=100
cumulative=no
name= _ "leadership"
female_name= _ "female^leadership"
description= _ "This unit can lead our own units that are next to it, making them fight better.
Adjacent own units of lower level will do more damage in battle. When a unit adjacent to, of a lower level than, and on the same side as a unit with Leadership engages in combat, its attacks do 25% more damage times the difference in their levels."
affect_self=no
[affect_adjacent]
[filter]
level=0
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=75
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=1
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=50
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=2
[/filter]
[/affect_adjacent]
[/leadership]
[leadership]
id=leadership
value=25
cumulative=no
affect_self=no
[affect_adjacent]
[filter]
level=3
[/filter]
[/affect_adjacent]
[/leadership]
#enddef
#define ABILITY_LEADERSHIP_LEVEL_5
# Canned definition of the Leadership (level 5) ability to be included in an
#define ABILITY_LEADERSHIP
# Canned definition of the Leadership ability to be included in an
# [abilities] clause.
# Note: Works only on units of level 1-5
[leadership]
id=leadership
value=125
@ -261,7 +112,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj
affect_self=no
[affect_adjacent]
[filter]
level=0
level=$($other_unit.level - 5)
[/filter]
[/affect_adjacent]
[/leadership]
@ -272,7 +123,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj
affect_self=no
[affect_adjacent]
[filter]
level=1
level=$($other_unit.level - 4)
[/filter]
[/affect_adjacent]
[/leadership]
@ -283,7 +134,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj
affect_self=no
[affect_adjacent]
[filter]
level=2
level=$($other_unit.level - 3)
[/filter]
[/affect_adjacent]
[/leadership]
@ -294,7 +145,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj
affect_self=no
[affect_adjacent]
[filter]
level=3
level=$($other_unit.level - 2)
[/filter]
[/affect_adjacent]
[/leadership]
@ -305,7 +156,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj
affect_self=no
[affect_adjacent]
[filter]
level=4
level=$($other_unit.level - 1)
[/filter]
[/affect_adjacent]
[/leadership]
@ -551,8 +402,16 @@ Enemy units cannot see this unit while it is in deep water, except if they have
name= _ "backstab"
description= _ "When used offensively, this attack deals double damage if there is an enemy of the target on the opposite side of the target, and that unit is not incapacitated (turned to stone or otherwise paralyzed)."
multiply=2
backstab=yes
active_on=offense
[filter_opponent]
[filter_adjacent]
adjacent=$other_unit.facing
is_enemy=yes
[not]
status=petrified
[/not]
[/filter_adjacent]
[/filter_opponent]
[/damage]
#enddef

View file

@ -12,6 +12,26 @@
#enddef
#wmllint: markcheck on
#define ABILITY_LEADERSHIP_LEVEL_1
{ABILITY_LEADERSHIP}
#enddef
#define ABILITY_LEADERSHIP_LEVEL_2
{ABILITY_LEADERSHIP}
#enddef
#define ABILITY_LEADERSHIP_LEVEL_3
{ABILITY_LEADERSHIP}
#enddef
#define ABILITY_LEADERSHIP_LEVEL_4
{ABILITY_LEADERSHIP}
#enddef
#define ABILITY_LEADERSHIP_LEVEL_5
{ABILITY_LEADERSHIP}
#enddef
#define EARLY_FINISH_BONUS_CAPTION
# Deprecated; use the below EARLY_FINISH_BONUS_FOOTNOTE instead
_"<small>(early finish bonus)</small>"#enddef

View file

@ -21,7 +21,7 @@
{LEADING_ANIM "units/drakes/flameheart-lead-2.png" "units/drakes/flameheart-lead-1.png" 39,-29}
{DEFENSE_ANIM "units/drakes/flameheart-defend-2.png" "units/drakes/flameheart-defend-1.png" {SOUND_LIST:DRAKE_HIT} }
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=war blade

View file

@ -20,7 +20,7 @@
{LEADING_ANIM "units/drakes/flare-lead-2.png" "units/drakes/flare-lead-1.png" 37,-19}
{DEFENSE_ANIM "units/drakes/flare-defend-2.png" "units/drakes/flare-defend-1.png" {SOUND_LIST:DRAKE_HIT} }
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=war blade

View file

@ -27,7 +27,7 @@
{DEFENSE_ANIM_RANGE "units/elves-wood/captain-defend.png" "units/elves-wood/captain.png" {SOUND_LIST:ELF_HIT} melee}
{DEFENSE_ANIM_RANGE "units/elves-wood/captain-bow-defend.png" "units/elves-wood/captain-bow.png" {SOUND_LIST:ELF_HIT} ranged }
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -26,7 +26,7 @@
description= _ "The longevity and natural intelligence of elves make them apt for military matters, enough even to counter their general distaste for war. Elves remember things much more clearly than humankind, and can often intuit what others can only be trained to do. Certainly, on those rare occasions when an elf sets his mind to war, the strategy that results is often the work of a master."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}
die_sound={SOUND_LIST:ELF_HIT}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -24,7 +24,7 @@
{STANDING_ANIM_DIRECTIONAL "units/goblins/rouser.png" "units/goblins/rouser-ne.png"}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=spear

View file

@ -37,7 +37,7 @@
impact=90
[/resistance]
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -32,7 +32,7 @@
impact=80
[/resistance]
[abilities]
{ABILITY_LEADERSHIP_LEVEL_4}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -21,7 +21,7 @@
{DEFENSE_ANIM_RANGE "units/human-loyalists/lieutenant-defend.png" "units/human-loyalists/lieutenant.png" {SOUND_LIST:HUMAN_HIT} melee}
{DEFENSE_ANIM_RANGE "units/human-loyalists/lieutenant-crossbow-defend.png" "units/human-loyalists/lieutenant-crossbow.png" {SOUND_LIST:HUMAN_HIT} ranged}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -21,7 +21,7 @@
{DEFENSE_ANIM_RANGE "units/human-loyalists/sergeant-defend.png" "units/human-loyalists/sergeant.png" {SOUND_LIST:HUMAN_HIT} melee}
{DEFENSE_ANIM_RANGE "units/human-loyalists/sergeant-crossbow-defend.png" "units/human-loyalists/sergeant-crossbow.png" {SOUND_LIST:HUMAN_HIT} ranged}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -6,7 +6,7 @@
image="units/monsters/fire-dragon.png"
{DEFENSE_ANIM_RANGE "units/monsters/fire-dragon.png" "units/monsters/fire-dragon.png" {SOUND_LIST:DRAKE_HIT} melee}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_5}
{ABILITY_LEADERSHIP}
[/abilities]
hitpoints=101
movement_type=drakefly

View file

@ -21,7 +21,7 @@
{DEFENSE_ANIM_RANGE "units/orcs/leader-defend-2.png" "units/orcs/leader-defend-1.png" {SOUND_LIST:ORC_HIT} melee}
{DEFENSE_ANIM_RANGE "units/orcs/leader-ranged-defend.png" "units/orcs/leader-ranged.png" {SOUND_LIST:ORC_HIT} ranged}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_1}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=sword

View file

@ -21,7 +21,7 @@
{DEFENSE_ANIM_RANGE "units/orcs/ruler-defend-2.png" "units/orcs/ruler-defend-1.png" {SOUND_LIST:ORC_HIT} melee}
{DEFENSE_ANIM_RANGE "units/orcs/ruler-ranged-defend.png" "units/orcs/ruler-ranged.png" {SOUND_LIST:ORC_HIT} ranged}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_2}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=greatsword

View file

@ -22,7 +22,7 @@
{DEFENSE_ANIM_RANGE "units/orcs/sovereign-defend-2.png" "units/orcs/sovereign-defend-1.png" {SOUND_LIST:ORC_HIT} melee}
{DEFENSE_ANIM_RANGE "units/orcs/sovereign-ranged-defend.png" "units/orcs/sovereign-ranged.png" {SOUND_LIST:ORC_HIT} ranged}
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
[/abilities]
[attack]
name=greatsword

View file

@ -31,7 +31,7 @@
description= _ "Tales are told of the mightiest warriors and generals, who, cursed with hate and stung by betrayal, have come back to this world as Death Knights. Wielding the same weapons as in life, they command the Undead in their quest for revenge."+{SPECIAL_NOTES}+{SPECIAL_NOTES_LEADERSHIP}+{SPECIAL_NOTES_SUBMERGE}
die_sound=lich-die.ogg
[abilities]
{ABILITY_LEADERSHIP_LEVEL_3}
{ABILITY_LEADERSHIP}
{ABILITY_SUBMERGE}
[/abilities]
[attack]

View file

@ -634,6 +634,11 @@ linechanges = (
# Changed in 1.13.2.
("[advance]","[advancement]"),
("[/advance]","[/advancement]"),
("{ABILITY_LEADERSHIP_LEVEL_1}", "{ABILITY_LEADERSHIP}"),
("{ABILITY_LEADERSHIP_LEVEL_2}", "{ABILITY_LEADERSHIP}"),
("{ABILITY_LEADERSHIP_LEVEL_3}", "{ABILITY_LEADERSHIP}"),
("{ABILITY_LEADERSHIP_LEVEL_4}", "{ABILITY_LEADERSHIP}"),
("{ABILITY_LEADERSHIP_LEVEL_5}", "{ABILITY_LEADERSHIP}"),
("misc/icon-amla-tough.png","icons/amla-default.png")
)
@ -748,7 +753,7 @@ notepairs = [
("{ABILITY_CURES}", "{SPECIAL_NOTES_CURES}"),
("{ABILITY_REGENERATES}", "{SPECIAL_NOTES_REGENERATES}"),
("{ABILITY_STEADFAST}", "{SPECIAL_NOTES_STEADFAST}"),
("{ABILITY_LEADERSHIP_LEVEL_", "{SPECIAL_NOTES_LEADERSHIP}"), # No } deliberately
("{ABILITY_LEADERSHIP}", "{SPECIAL_NOTES_LEADERSHIP}"),
("{ABILITY_SKIRMISHER}", "{SPECIAL_NOTES_SKIRMISHER}"),
("{ABILITY_ILLUMINATES}", "{SPECIAL_NOTES_ILLUMINATES}"),
("{ABILITY_TELEPORT}", "{SPECIAL_NOTES_TELEPORT}"),

View file

@ -1225,6 +1225,9 @@ namespace {
a_.get_unit().set_movement(-1, true);
return;
}
a_.get_unit().set_facing(a_.loc_.get_relative_dir(d_.loc_));
d_.get_unit().set_facing(d_.loc_.get_relative_dir(a_.loc_));
a_.get_unit().set_attacks(a_.get_unit().attacks_left()-1);
VALIDATE(a_.weapon_ < static_cast<int>(a_.get_unit().attacks().size()),

View file

@ -69,25 +69,61 @@ std::size_t hash_value(map_location const & a){
map_location::DIRECTION map_location::parse_direction(const std::string& str)
{
if(!str.empty()) {
if(str == "n") {
return NORTH;
} else if(str == "ne") {
return NORTH_EAST;
} else if(str == "se") {
return SOUTH_EAST;
} else if(str == "s") {
return SOUTH;
} else if(str == "sw") {
return SOUTH_WEST;
} else if(str == "nw") {
return NORTH_WEST;
} else if(str[0] == '-' && str.length() <= 10) {
// A minus sign reverses the direction
return get_opposite_dir(parse_direction(str.substr(1)));
if(str.empty()) {
return NDIRECTIONS;
}
// Syntax: [-] (n|ne|se|s|sw|nw) [:cw|:ccw]
// - means "take opposite direction" and has higher precedence
// :cw and :ccw mean "one step (counter-)clockwise"
// Parentheses can be used for grouping or to apply an operator more than once
const size_t open = str.find_first_of('('), close = str.find_last_of(')');
if (open != std::string::npos && close != std::string::npos) {
std::string sub = str.substr(open + 1, close - open - 1);
map_location::DIRECTION dir = parse_direction(sub);
sub = str;
sub.replace(open, close - open + 1, write_direction(dir));
return parse_direction(sub);
}
const size_t start = str[0] == '-' ? 1 : 0;
const size_t end = str.find_first_of(':');
const std::string& main_dir = str.substr(start, end - start);
map_location::DIRECTION dir;
if (main_dir == "n") {
dir = NORTH;
} else if (main_dir == "ne") {
dir = NORTH_EAST;
} else if (main_dir == "se") {
dir = SOUTH_EAST;
} else if (main_dir == "s") {
dir = SOUTH;
} else if (main_dir == "sw") {
dir = SOUTH_WEST;
} else if (main_dir == "nw") {
dir = NORTH_WEST;
} else {
return NDIRECTIONS;
}
if (start == 1) {
dir = get_opposite_dir(dir);
}
if (end != std::string::npos) {
const std::string rel_dir = str.substr(end + 1);
if (rel_dir == "cw") {
dir = rotate_right(dir, 1);
} else if (rel_dir == "ccw") {
dir = rotate_right(dir, -1);
} else {
return NDIRECTIONS;
}
}
return NDIRECTIONS;
return dir;
}
std::vector<map_location::DIRECTION> map_location::parse_directions(const std::string& str)

View file

@ -806,14 +806,27 @@ int game_lua_kernel::intf_match_unit(lua_State *L)
filter_context & fc = game_state_;
if (int side = lu->on_recall_list()) {
if (!lua_isnoneornil(L, 3)) {
WRN_LUA << "wesnoth.match_unit called with 3rd argument, but unit to match was on recall list. ";
WRN_LUA << "Thus the 3rd argument is ignored.\n";
}
team &t = (teams())[side - 1];
scoped_recall_unit auto_store("this_unit",
t.save_id(), t.recall_list().find_index(u->id()));
lua_pushboolean(L, unit_filter(filter, &fc).matches(*u, map_location()));
return 1;
}
lua_pushboolean(L, unit_filter(filter, &fc).matches(*u));
if (!lua_isnoneornil(L, 3)) {
lua_unit *lu_adj = static_cast<lua_unit *>(lua_touserdata(L, 1));
unit_ptr u_adj = lu_adj->get();
if (!u_adj) {
return luaL_argerror(L, 3, "unit not found");
}
lua_pushboolean(L, unit_filter(filter, &fc).matches(*u, *u_adj));
} else {
lua_pushboolean(L, unit_filter(filter, &fc).matches(*u));
}
return 1;
}

View file

@ -394,7 +394,7 @@ private:
* cfg: an ability WML structure
*/
bool ability_active(const std::string& ability,const config& cfg,const map_location& loc) const;
bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const map_location& loc) const;
bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const map_location& loc,const unit& from) const;
bool ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const;
bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const;

View file

@ -26,13 +26,35 @@
#include "unit_abilities.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include "filter_context.hpp"
#include <boost/foreach.hpp>
static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
namespace {
class temporary_facing
{
map_location::DIRECTION save_dir_;
unit_map::const_iterator u_;
public:
temporary_facing(unit_map::const_iterator u, map_location::DIRECTION new_dir)
: save_dir_(u.valid() ? u->facing() : map_location::NDIRECTIONS)
, u_(u)
{
if (u_.valid()) {
u_->set_facing(new_dir);
}
}
~temporary_facing()
{
if (u_.valid()) {
u_->set_facing(save_dir_);
}
}
};
}
/*
*
@ -139,7 +161,7 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc
BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) {
if (affects_side(j, *resources::teams, side(), it->side()) &&
it->ability_active(tag_name, j, adjacent[i]) &&
ability_affects_adjacent(tag_name, j, i, loc))
ability_affects_adjacent(tag_name, j, i, loc, *it))
return true;
}
}
@ -182,7 +204,7 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc
BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) {
if (affects_side(j, *resources::teams, side(), it->side()) &&
it->ability_active(tag_name, j, adjacent[i]) &&
ability_affects_adjacent(tag_name, j, i, loc))
ability_affects_adjacent(tag_name, j, i, loc, *it))
res.push_back(unit_ability(&j, adjacent[i]));
}
}
@ -310,35 +332,56 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
BOOST_FOREACH(const config &i, cfg.child_range("filter_adjacent"))
{
size_t count = 0;
const unit_filter ufilt(vconfig(i), resources::filter_con, illuminates);
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
BOOST_FOREACH(const map_location::DIRECTION index, dirs)
{
map_location::DIRECTION index =
map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS)
continue;
unit_map::const_iterator unit = units.find(adjacent[index]);
if (unit == units.end())
return false;
if (!ufilt( *unit ))
if (!ufilt(*unit, *this))
return false;
if (i.has_attribute("is_enemy")) {
const display_context& dc = resources::filter_con->get_disp_context();
if (i["is_enemy"].to_bool() != dc.teams()[unit->side() - 1].is_enemy(side_)) {
continue;
}
}
count++;
}
if (i["count"].empty() && count != dirs.size()) {
return false;
}
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
return false;
}
}
BOOST_FOREACH(const config &i, cfg.child_range("filter_adjacent_location"))
{
size_t count = 0;
terrain_filter adj_filter(vconfig(i), resources::filter_con);
adj_filter.flatten(illuminates);
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
BOOST_FOREACH(const map_location::DIRECTION index, dirs)
{
map_location::DIRECTION index = map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS) {
continue;
}
if(!adj_filter.match(adjacent[index])) {
return false;
}
count++;
}
if (i["count"].empty() && count != dirs.size()) {
return false;
}
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
return false;
}
}
return true;
@ -348,23 +391,24 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
* cfg: an ability WML structure
*
*/
bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc) const
bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
{
bool illuminates = ability == "illuminates";
assert(dir >=0 && dir <= 5);
static const std::string adjacent_names[6] = {"n","ne","se","s","sw","nw"};
map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
BOOST_FOREACH(const config &i, cfg.child_range("affect_adjacent"))
{
if (i.has_attribute("adjacent")) { //key adjacent defined
std::vector<std::string> dirs = utils::split(i["adjacent"]);
if (std::find(dirs.begin(),dirs.end(),adjacent_names[dir]) == dirs.end())
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
continue;
}
}
const config &filter = i.child("filter");
if (!filter || //filter tag given
unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc) ) {
unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc, from) ) {
return true;
}
}
@ -767,12 +811,13 @@ namespace { // Helpers for attack_type::special_active()
* @param[in] child_tag The tag of the child filter to use.
*/
static bool special_unit_matches(const unit_map::const_iterator & un_it,
const unit_map::const_iterator & u2,
const map_location & loc,
const attack_type * weapon,
const config & filter,
const std::string & child_tag)
{
if ( !loc.valid() )
if (!loc.valid() || !u2.valid())
// The special's context was set to ignore this unit, so assume we pass.
// (This is used by reports.cpp to show active specials when the
// opponent is not known. From a player's perspective, the special
@ -786,7 +831,7 @@ namespace { // Helpers for attack_type::special_active()
return true;
// Check for a unit match.
if ( !un_it.valid() || !unit_filter(vconfig(filter_child), resources::filter_con).matches(*un_it, loc) )
if ( !un_it.valid() || !unit_filter(vconfig(filter_child), resources::filter_con).matches(*un_it, loc, *u2))
return false;
// Check for a weapon match.
@ -843,6 +888,10 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
const unit_map & units = *resources::units;
unit_map::const_iterator self = units.find(self_loc_);
unit_map::const_iterator other = units.find(other_loc_);
// Make sure they're facing each other.
temporary_facing self_facing(self, self_loc_.get_relative_dir(other_loc_));
temporary_facing other_facing(other, other_loc_.get_relative_dir(self_loc_));
// Translate our context into terms of "attacker" and "defender".
unit_map::const_iterator & att = is_attacker_ ? self : other;
@ -853,13 +902,13 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
const attack_type * def_weapon = is_attacker_ ? other_attack_ : this;
// Filter the units involved.
if ( !special_unit_matches(self, self_loc_, this, special, "filter_self") )
if (!special_unit_matches(self, other, self_loc_, this, special, "filter_self"))
return false;
if ( !special_unit_matches(other, other_loc_, other_attack_, special, "filter_opponent") )
if (!special_unit_matches(other, self, other_loc_, other_attack_, special, "filter_opponent"))
return false;
if ( !special_unit_matches(att, att_loc, att_weapon, special, "filter_attacker") )
if (!special_unit_matches(att, def, att_loc, att_weapon, special, "filter_attacker"))
return false;
if ( !special_unit_matches(def, def_loc, def_weapon, special, "filter_defender") )
if (!special_unit_matches(def, att, def_loc, def_weapon, special, "filter_defender"))
return false;
map_location adjacent[6];
@ -868,32 +917,52 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
// Filter the adjacent units.
BOOST_FOREACH(const config &i, special.child_range("filter_adjacent"))
{
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
size_t count = 0;
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
BOOST_FOREACH(const map_location::DIRECTION index, dirs)
{
map_location::DIRECTION index =
map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS)
continue;
unit_map::const_iterator unit = units.find(adjacent[index]);
if ( unit == units.end() ||
!unit_filter(vconfig(i), resources::filter_con).matches(*unit, adjacent[index]) ) //TODO: Should this filter get precomputed?
!unit_filter(vconfig(i), resources::filter_con).matches(*unit, adjacent[index], *self)) //TODO: Should this filter get precomputed?
return false;
if (i.has_attribute("is_enemy")) {
const display_context& dc = resources::filter_con->get_disp_context();
if (i["is_enemy"].to_bool() != dc.teams()[unit->side() - 1].is_enemy(self->side())) {
continue;
}
}
count++;
}
if (i["count"].empty() && count != dirs.size()) {
return false;
}
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
return false;
}
}
// Filter the adjacent locations.
BOOST_FOREACH(const config &i, special.child_range("filter_adjacent_location"))
{
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
size_t count = 0;
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
BOOST_FOREACH(const map_location::DIRECTION index, dirs)
{
map_location::DIRECTION index =
map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS)
continue;
terrain_filter adj_filter(vconfig(i), resources::filter_con);
if(!adj_filter.match(adjacent[index])) {
return false;
}
count++;
}
if (i["count"].empty() && count != dirs.size()) {
return false;
}
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
return false;
}
}
@ -948,6 +1017,10 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) :
BOOST_FOREACH (const unit_ability & ability, list) {
const config& cfg = *ability.first;
std::string const &effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
if (!cfg["backstab"].blank()) {
lg::wml_error << "The backstab= key in weapon specials is deprecated; use [filter_adjacent] instead\n";
}
if (!backstab && cfg["backstab"].to_bool())
continue;

View file

@ -53,6 +53,14 @@ bool unit_filter::matches(const unit & u) const {
return matches (u, u.get_location());
}
bool unit_filter::matches(const unit & u, const map_location & loc, const unit & u2) const {
return impl_->matches(u,loc,&u2);
}
bool unit_filter::matches(const unit & u, const unit & u2) const {
return matches(u, u.get_location(), u2);
}
//bool unit_filter::matches(const unit & /*u*/, const map_location & /*loc*/) const {
// assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided");
// return false;
@ -66,7 +74,7 @@ static boost::shared_ptr<unit_filter_abstract_impl> construct(const vconfig & vc
class null_unit_filter_impl : public unit_filter_abstract_impl {
public:
null_unit_filter_impl(const filter_context & fc) : fc_(fc) {}
virtual bool matches(const unit & /*u*/, const map_location & /*loc*/) const {
virtual bool matches(const unit & /*u*/, const map_location & /*loc*/, const unit *) const {
return true;
}
virtual std::vector<const unit *> all_matches_on_map() const {
@ -145,7 +153,7 @@ public:
this->vcfg.make_safe();
}
virtual bool matches(const unit & u, const map_location & loc) const;
virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const;
virtual std::vector<const unit *> all_matches_on_map() const;
virtual unit_const_ptr first_match_on_map() const;
@ -188,13 +196,19 @@ unit_filter::unit_filter(const vconfig & vcfg, const filter_context * fc, bool f
/** Begin implementations of filter impl's
*/
bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc) const
bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc, const unit * u2) const
{
bool matches = true;
if(loc.valid()) {
scoped_xy_unit auto_store("this_unit", loc.x, loc.y, fc_.get_disp_context().units());
matches = internal_matches_filter(u, loc);
if (u2) {
const map_location& loc2 = u2->get_location();
scoped_xy_unit auto_store("other_unit", loc2.x, loc2.y, fc_.get_disp_context().units());
matches = internal_matches_filter(u, loc);
} else {
matches = internal_matches_filter(u, loc);
}
} else {
// If loc is invalid, then this is a recall list unit (already been scoped)
matches = internal_matches_filter(u, loc);
@ -466,7 +480,7 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
for (j = dirs.begin(); j != j_end; ++j) {
unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
if (unit_itor == units.end() || !filt(*unit_itor)) {
if (unit_itor == units.end() || !filt(*unit_itor, u)) {
continue;
}
boost::optional<bool> is_enemy;
@ -529,7 +543,7 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map() const {
std::vector<const unit *> ret;
BOOST_FOREACH(const unit & u, fc_.get_disp_context().units()) {
if (matches(u, u.get_location())) {
if (matches(u, u.get_location(), NULL)) {
ret.push_back(&u);
}
}
@ -539,7 +553,7 @@ std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map() const {
unit_const_ptr basic_unit_filter_impl::first_match_on_map() const {
const unit_map & units = fc_.get_disp_context().units();
for(unit_map::const_iterator u = units.begin(); u != units.end(); u++) {
if (matches(*u,u->get_location())) {
if (matches(*u,u->get_location(),NULL)) {
return u.get_shared_ptr();
}
}

View file

@ -38,7 +38,7 @@ struct map_location;
class unit_filter_abstract_impl {
public:
virtual bool matches(const unit & u, const map_location & loc) const = 0;
virtual bool matches(const unit & u, const map_location & loc, const unit * u2 = NULL) const = 0;
virtual std::vector<const unit*> all_matches_on_map() const = 0;
virtual unit_const_ptr first_match_on_map() const = 0;
virtual ~unit_filter_abstract_impl() {}
@ -68,6 +68,9 @@ public:
/// (Only use for units currently on the map; otherwise use the overload
/// that takes a location, possibly with a null location.)
bool matches(const unit & u) const;
bool matches(const unit & u, const map_location & loc, const unit & u2) const;
bool matches(const unit & u, const unit & u2) const;
bool operator()(const unit & u, const map_location & loc) const {
return matches(u,loc);
@ -77,6 +80,14 @@ public:
return matches(u);
}
bool operator()(const unit & u, const map_location & loc, const unit & u2) const {
return matches(u,loc,u2);
}
bool operator()(const unit & u, const unit & u2) const {
return matches(u,u2);
}
std::vector<const unit *> all_matches_on_map() const {
return impl_->all_matches_on_map();
}