Merge pull request #4725 from Elvish-Hunter/wmllint_fix_4379

Fix for bug #4379 (wmllint issues with defense cap)
This commit is contained in:
Charles Dang 2020-01-27 18:07:21 -05:00 committed by GitHub
commit 6dcb8fed17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 12 deletions

View file

@ -10,7 +10,6 @@
hills=60
mountains=50
fungus=60
# wmllint: match forest=-70 with {NOTE_DEFENSE_CAP}
forest=-70
village=60
[/defense]

View file

@ -17,7 +17,6 @@
cost=52
#extra resistance for these units
usage=scout
# wmllint: notecheck off
description= _ "Cavaliers are masters at the use of both sword and crossbow from horseback. Their combination of striking power and mobility is fearsome, and they have a reputation for dash and aggressiveness to match it. The daring deeds of Cavaliers are the subject of many a tale and song."
{NOTE_DEFENSE_CAP}
die_sound=horse-die.ogg

View file

@ -16,7 +16,6 @@
cost=17
usage=scout
#extra resistance for these units
# wmllint: notecheck off
description= _ "Cavalrymen are distinguished from horsemen by their tactics and equipment. A cavalryman wears heavier armor, and carries a sword and shield, rather than a lance. Their tactics do not include charging; instead they maneuver to slash with a sword, using both horse and rider as an effective tool of melee.
Cavalrymen are very useful for taking and holding positions on open ground, for screening friendly soldiers, and also for scouting work."

View file

@ -16,7 +16,6 @@
cost=34
#extra resistance for these units
usage=scout
# wmllint: notecheck off
description= _ "The more talented cavalrymen in the armies of Wesnoth are trained in the use of the crossbow, and matched with much more powerful steeds. Well-armored, and skilled in the use of their swords, these soldiers can drive forward and hold the ground they take. Their mobility and resilience make them of great value on the battlefield."
{NOTE_DEFENSE_CAP}
die_sound=horse-die.ogg

View file

@ -10,7 +10,6 @@
hills=60
mountains=50
fungus=60
# wmllint: match forest=-70 with {NOTE_DEFENSE_CAP}
forest=-70
village=60
[/defense]

View file

@ -189,6 +189,7 @@
import sys, os, re, argparse, string, copy, difflib, time, gzip, codecs
from wesnoth.wmltools3 import *
from wesnoth.wmliterator3 import *
from collections import defaultdict
# Changes meant to be done on maps and .cfg lines.
mapchanges = (
@ -1056,10 +1057,10 @@ def standard_unit_filter():
# Sanity checking
# Associations for the ability sanity checks.
# Please note that a special note can be associated with multiple abilities
# but any given ability can be associated with only one special note
# Some notes are handled directly in the global_sanity_check() function
notepairs = [
("movement_type=mounted", "{NOTE_DEFENSE_CAP}"),
("movement_type=undeadspirit", "{NOTE_SPIRIT}"),
("type=arcane", "{NOTE_ARCANE}"),
("{ABILITY_HEALS}", "{NOTE_HEALS}"),
("{ABILITY_EXTRA_HEAL}", "{NOTE_EXTRA_HEAL}"),
("{ABILITY_UNPOISON}", "{NOTE_UNPOISON}"),
@ -1458,7 +1459,11 @@ def global_sanity_check(filename, lines):
in_unit_type = None
notecheck = True
trait_note = dict(notepairs)
note_trait = {p[1]:p[0] for p in notepairs}
# it's possible that a note might be associated with two abilities
# use a multimap-like data structure for this reason
note_trait = defaultdict(list) # {p[1]:p[0] for p in notepairs}
for pair in notepairs:
note_trait[pair[1]].append(pair[0])
unit_id = ""
base_unit = ""
for nav in WmllintIterator(lines, filename):
@ -1481,6 +1486,9 @@ def global_sanity_check(filename, lines):
temp_movetypes = []
temp_races = []
temp_advances = []
arcane_note_needed = False
spirit_note_needed = False
defense_cap_note_needed = False
continue
elif nav.element == "[/unit_type]":
#print('"%s", %d: unit has traits %s and notes %s' \
@ -1501,15 +1509,31 @@ def global_sanity_check(filename, lines):
derived_units.append((filename, nav.lineno + 1, unit_id, base_unit))
if unit_id and not base_unit:
missing_notes = []
if arcane_note_needed and "{NOTE_ARCANE}" not in notes:
missing_notes.append("{NOTE_ARCANE}")
if spirit_note_needed and "{NOTE_SPIRIT}" not in notes:
missing_notes.append("{NOTE_SPIRIT}")
if defense_cap_note_needed and "{NOTE_DEFENSE_CAP}" not in notes:
missing_notes.append("{NOTE_DEFENSE_CAP}")
for trait in traits:
tn = trait_note[trait]
if tn not in notes and tn not in missing_notes:
missing_notes.append(tn)
missing_traits = []
if (not arcane_note_needed) and "{NOTE_ARCANE}" in notes:
missing_traits.append("type=arcane")
if (not spirit_note_needed) and "{NOTE_SPIRIT}" in notes:
missing_traits.append("movement_type=undeadspirit")
if (not defense_cap_note_needed) and "{NOTE_DEFENSE_CAP}" in notes:
missing_traits.append("movement_type=mounted or [defense] tag")
for note in notes:
nt = note_trait[note]
if nt not in traits and nt not in missing_traits:
missing_traits.append(nt)
for nt in note_trait[note]: # defaultdict makes nt a list, not a string!
if nt in traits:
break
else: # this is done only if there isn't at least one trait matching the note
for nt in note_trait[note]:
if nt not in missing_traits:
missing_traits.append(nt)
# If the unit didn't specify hitpoints, there is some wacky
# stuff going on (possibly pseudo-[base_unit] behavior via
# macro generation) so disable some of the consistency checks.
@ -1538,6 +1562,9 @@ def global_sanity_check(filename, lines):
temp_movetypes = []
temp_races = []
temp_advances = []
arcane_note_needed = False
spirit_note_needed = False
defense_cap_note_needed = False
# the glob pattern matches any WML tag starting with filter, including [filter] itself
if '[unit_type]' in nav.ancestors() and not nav.glob_ancestors("[[]filter*[]]"):
try:
@ -1559,6 +1586,10 @@ def global_sanity_check(filename, lines):
if '{' not in value:
assert(unit_id)
unit_movetypes.append((unit_id, filename, nav.lineno + 1, value))
if value == "undeadspirit":
spirit_note_needed = True
elif value == "mounted":
defense_cap_note_needed = True
elif key == "race":
if '{' not in value:
assert(unit_id or base_unit)
@ -1569,6 +1600,10 @@ def global_sanity_check(filename, lines):
advancements = value
if advancements.strip() != "null":
advances.append((unit_id, filename, nav.lineno + 1, advancements))
elif key == "type" and value == "arcane" and "[attack]" in nav.ancestors():
arcane_note_needed = True
elif "[defense]" in nav.ancestors() and re.match(r"\-\d+",value):
defense_cap_note_needed = True
except TypeError:
pass
precomment = nav.text
@ -1576,6 +1611,10 @@ def global_sanity_check(filename, lines):
precomment = nav.text[:nav.text.find("#")]
if "{NOTE" in precomment:
has_special_notes = True
# these special cases are handled better outside of notepairs
for note in ("{NOTE_DEFENSE_CAP}","{NOTE_SPIRIT}","{NOTE_ARCANE}"):
if note in precomment:
notes.append(note)
for (p, q) in notepairs:
if p in precomment:
traits.append(p)