diff --git a/pom.xml b/pom.xml index b121ed9..b764238 100644 --- a/pom.xml +++ b/pom.xml @@ -236,9 +236,12 @@ name.ipsi.project.fwbp.foundry.wod.ItemTypes name.ipsi.project.fwbp.foundry.wod.PowerTypes name.ipsi.project.fwbp.foundry.wod.WeaponConcealment + name.ipsi.project.fwbp.foundry.wod.RangedWeaponData + name.ipsi.project.fwbp.foundry.wod.RangedWeaponData$Clip + name.ipsi.project.fwbp.foundry.wod.RangedWeaponData$Mode name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData - name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData$Attack - name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData$Damage + name.ipsi.project.fwbp.foundry.wod.Attack + name.ipsi.project.fwbp.foundry.wod.Damage name.ipsi.project.fwbp.foundry.wod.werewolf.FetishData name.ipsi.project.fwbp.foundry.wod.werewolf.FetishType name.ipsi.project.fwbp.foundry.core.Module diff --git a/src/main/java/name/ipsi/project/fwbp/Preprocessor.java b/src/main/java/name/ipsi/project/fwbp/Preprocessor.java index d03b252..73c9d24 100644 --- a/src/main/java/name/ipsi/project/fwbp/Preprocessor.java +++ b/src/main/java/name/ipsi/project/fwbp/Preprocessor.java @@ -429,7 +429,10 @@ public enum GroupType { rites("Rite"), fetishes("Fetish"), talens("Talen"), - mandf("MeritAndFlaw") + mandf("MeritAndFlaw"), + melee_weapons("MeleeWeapon"), + thrown_weapons("ThrownWeapon"), + ranged_weapons("RangedWeapon") ; private final String classPrefix; diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/MeleeWeapon.java b/src/main/java/name/ipsi/project/fwbp/books/shared/MeleeWeapon.java index b554064..22bd023 100644 --- a/src/main/java/name/ipsi/project/fwbp/books/shared/MeleeWeapon.java +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/MeleeWeapon.java @@ -1,14 +1,15 @@ package name.ipsi.project.fwbp.books.shared; -import name.ipsi.project.fwbp.books.shared.BookEntry; -import name.ipsi.project.fwbp.foundry.wod.DamageTypes; import name.ipsi.project.fwbp.foundry.core.FoundryUtils; +import name.ipsi.project.fwbp.foundry.wod.DamageTypes; import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment; +import java.util.List; + public record MeleeWeapon( String id, String name, - String description, + List description, int difficulty, int damageBonus, DamageTypes damageType, @@ -20,7 +21,7 @@ public record MeleeWeapon( boolean selfDamage, boolean natural ) implements BookEntry { - public MeleeWeapon(String name, String description, int difficulty, int damageBonus, DamageTypes damageType, WeaponConcealment concealment, boolean canEntangle, boolean breaksAfterUse, boolean twoHanded, boolean silver, boolean selfDamage, boolean natural) { + public MeleeWeapon(String name, List description, int difficulty, int damageBonus, DamageTypes damageType, WeaponConcealment concealment, boolean canEntangle, boolean breaksAfterUse, boolean twoHanded, boolean silver, boolean selfDamage, boolean natural) { this(FoundryUtils.generateId("melee-weapon", name), name, description, difficulty, damageBonus, damageType, concealment, canEntangle, breaksAfterUse, twoHanded, silver, selfDamage, natural); } } diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/RangedWeapon.java b/src/main/java/name/ipsi/project/fwbp/books/shared/RangedWeapon.java index b257efd..7c1b6ad 100644 --- a/src/main/java/name/ipsi/project/fwbp/books/shared/RangedWeapon.java +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/RangedWeapon.java @@ -1,4 +1,54 @@ package name.ipsi.project.fwbp.books.shared; -public record RangedWeapon(String id) implements BookEntry { +import name.ipsi.project.fwbp.foundry.core.FoundryUtils; +import name.ipsi.project.fwbp.foundry.wod.DamageTypes; +import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment; + +import java.util.List; + +public record RangedWeapon( + String id, + String name, + List description, + int damage, + DamageTypes damageType, + WeaponConcealment concealment, + Integer range, + Integer rate, + Integer clip, + boolean canReload, + boolean hasBurst, + boolean hasFullAuto, + boolean hasSpray +) implements BookEntry { + public RangedWeapon( + String name, + List description, + int damage, + DamageTypes damageType, + WeaponConcealment concealment, + Integer range, + Integer rate, + Integer clip, + boolean canReload, + boolean hasBurst, + boolean hasFullAuto, + boolean hasSpray + ) { + this( + FoundryUtils.generateId("ranged-weapon", name), + name, + description, + damage, + damageType, + concealment, + range, + rate, + clip, + canReload, + hasBurst, + hasFullAuto, + hasSpray + ); + } } diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/ThrownWeapon.java b/src/main/java/name/ipsi/project/fwbp/books/shared/ThrownWeapon.java index 3242f08..0fbf22a 100644 --- a/src/main/java/name/ipsi/project/fwbp/books/shared/ThrownWeapon.java +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/ThrownWeapon.java @@ -1,4 +1,22 @@ package name.ipsi.project.fwbp.books.shared; -public record ThrownWeapon(String id) implements BookEntry { +import name.ipsi.project.fwbp.foundry.core.FoundryUtils; +import name.ipsi.project.fwbp.foundry.wod.DamageTypes; +import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment; + +import java.util.List; + +public record ThrownWeapon( + String id, + String name, + List description, + int difficulty, + boolean includeStrength, + int damageBonus, + DamageTypes damageType, + WeaponConcealment concealment +) implements BookEntry { + public ThrownWeapon(String name, List description, int difficulty, boolean includeStrength, int damageBonus, DamageTypes damageType, WeaponConcealment concealment) { + this(FoundryUtils.generateId("thrown-weapon", name), name, description, difficulty, includeStrength, damageBonus, damageType, concealment); + } } diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/locations/MeleeWeaponLocation.java b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/MeleeWeaponLocation.java new file mode 100644 index 0000000..aa3cb0e --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/MeleeWeaponLocation.java @@ -0,0 +1,4 @@ +package name.ipsi.project.fwbp.books.shared.locations; + +public record MeleeWeaponLocation(TextLocation textLocation) { +} diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/locations/RangedWeaponLocation.java b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/RangedWeaponLocation.java new file mode 100644 index 0000000..72d8dd8 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/RangedWeaponLocation.java @@ -0,0 +1,4 @@ +package name.ipsi.project.fwbp.books.shared.locations; + +public record RangedWeaponLocation(TextLocation textLocation) { +} diff --git a/src/main/java/name/ipsi/project/fwbp/books/shared/locations/ThrownWeaponLocation.java b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/ThrownWeaponLocation.java new file mode 100644 index 0000000..7f9f332 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/shared/locations/ThrownWeaponLocation.java @@ -0,0 +1,4 @@ +package name.ipsi.project.fwbp.books.shared.locations; + +public record ThrownWeaponLocation(TextLocation textLocation) { +} diff --git a/src/main/java/name/ipsi/project/fwbp/books/werewolf/Werewolf20Extractor.java b/src/main/java/name/ipsi/project/fwbp/books/werewolf/Werewolf20Extractor.java index ec9184c..91352ba 100644 --- a/src/main/java/name/ipsi/project/fwbp/books/werewolf/Werewolf20Extractor.java +++ b/src/main/java/name/ipsi/project/fwbp/books/werewolf/Werewolf20Extractor.java @@ -37,11 +37,9 @@ public class Werewolf20Extractor { RiteLocations.DATA, FetishLocations.DATA, TalenLocations.DATA, - new WeaponLocations( - new Paragraph(303, makeRect(121, 129, 263, 280)), - new Paragraph(303, makeRect(0, 0, 0, 0)), - new Paragraph(304, makeRect(0, 0, 0, 0)) - ), + MeleeWeaponLocations.DATA.get(0), + ThrownWeaponLocations.DATA.get(0), + RangedWeaponLocations.DATA.get(0), Collections.emptyList(), MeritAndFlawLocations.DATA ); @@ -56,7 +54,9 @@ private record Book( List rites, List fetishes, List talens, - WeaponLocations weapons, + MeleeWeaponLocation meleeWeapons, + ThrownWeaponLocation thrownWeapons, + RangedWeaponLocation rangedWeapons, List spirits, List meritsAndFlaws ) {} @@ -280,7 +280,7 @@ public List process() { processTribes(); - processMeleeWeapons(); + processWeapons(); processBackgrounds(); @@ -435,10 +435,10 @@ private void processTribes() { } } - private void processMeleeWeapons() { + private void processWeapons() { entries.add(new MeleeWeapon( "Bite - Crinos, Hispo, Lupus", - getText(parser, new Paragraph(297, makeRect(304, 213, 245, 77))).get(0).replaceAll("^\\s*•?\\s*Bite:\\s*", ""), + Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 213, 245, 77))).get(0).replaceAll("^\\s*•?\\s*Bite:\\s*", ""))), 5, 1, DamageTypes.AGGRAVATED, @@ -453,7 +453,7 @@ private void processMeleeWeapons() { entries.add(new MeleeWeapon( "Claw - Crinos, Hispo", - getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""), + Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""))), 6, 2, DamageTypes.AGGRAVATED, @@ -468,7 +468,7 @@ private void processMeleeWeapons() { entries.add(new MeleeWeapon( "Claw - Glabro, Lupus", - getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""), + Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""))), 6, 2, DamageTypes.BASHING, @@ -482,29 +482,152 @@ private void processMeleeWeapons() { )); log.info("Extracting Melee weapons"); + Map meleeNotes = new HashMap<>(); + + var notePattern = Pattern.compile("^\\s*(\\*+)\\s*(.*)$"); + for (var line : getTextAsLines(parser, BOOK_DETAILS.meleeWeapons().textLocation())) { + var matcher = notePattern.matcher(line); + if (matcher.matches()) { + meleeNotes.put(matcher.group(1), matcher.group(2)); + } + } + var meleeWeaponPattern = Pattern.compile("([\\w\\s]+)(\\**)?\\s*(\\d+)(\\**)?\\s*Strength(\\s*\\+\\s*(\\d+))?/([BLA])(\\**)?\\s*([PJTN])\\s*"); - for (var line : getTextAsLines(parser, BOOK_DETAILS.weapons().meleeWeapons())) { + for (var line : getTextAsLines(parser, BOOK_DETAILS.meleeWeapons().textLocation())) { log.trace("Processing weapon line"); var matcher = meleeWeaponPattern.matcher(line); if (matcher.matches()) { log.trace("Weapon matches"); + var description = new ArrayList(); + boolean canEntangle = matcher.group(4) != null && matcher.group(4).trim().equals("*"); + if (canEntangle) { + description.add(new StringEntry(fixText(meleeNotes.get("*")))); + } + boolean breaksAfterUse = matcher.group(8) != null && matcher.group(8).trim().equals("**"); + if (breaksAfterUse) { + description.add(new StringEntry(fixText(meleeNotes.get("**")))); + } + boolean twoHanded = matcher.group(2) != null && matcher.group(2).trim().equals("***"); + if (twoHanded) { + description.add(new StringEntry(fixText(meleeNotes.get("***")))); + } + boolean silver = matcher.group(2) != null && matcher.group(2).trim().equals("****"); + if (silver) { + description.add(new StringEntry(fixText(meleeNotes.get("****")))); + } + boolean selfDamage = matcher.group(8) != null && matcher.group(8).trim().equals("*****"); + if (selfDamage) { + description.add(new StringEntry(fixText(meleeNotes.get("*****")))); + } entries.add(new MeleeWeapon( matcher.group(1).trim(), - "", + description, Integer.parseInt(matcher.group(3).trim()), matcher.group(6) == null ? 0 : Integer.parseInt(matcher.group(6).trim()), DamageTypes.parse(matcher.group(7).trim()), WeaponConcealment.parse(matcher.group(9).trim()), - matcher.group(4) != null && matcher.group(4).trim().equals("*"), - matcher.group(8) != null && matcher.group(8).trim().equals("**"), - matcher.group(2) != null && matcher.group(2).trim().equals("***"), - matcher.group(2) != null && matcher.group(2).trim().equals("****"), - matcher.group(8) != null && matcher.group(8).trim().equals("*****"), + canEntangle, + breaksAfterUse, + twoHanded, + silver, + selfDamage, false )); log.trace("Weapon processed"); } else { - log.warn("Line {} doesn't match against weapon regex", line); + log.debug("Line {} doesn't match against weapon regex", line); + } + } + + log.info("Extracting Throw weapons"); + var thrownWeaponPattern = Pattern.compile("([A-Za-z\\s,-]+)\\s*(\\d+)\\s*(Strength)?(\\s*\\+\\s*)?(\\d+)?/([BLA])\\s*([PJTN]|varies)\\s*"); + for (var line : getTextAsLines(parser, BOOK_DETAILS.thrownWeapons().textLocation())) { + log.trace("Processing throw line"); + var matcher = thrownWeaponPattern.matcher(line); + if (matcher.matches()) { + log.trace("Weapon matches"); + entries.add(new ThrownWeapon( + matcher.group(1).trim(), + Collections.emptyList(), + Integer.parseInt(matcher.group(2).trim()), + matcher.group(3) != null, + matcher.group(5) == null ? 0 : Integer.parseInt(matcher.group(5).trim()), + DamageTypes.parse(matcher.group(6).trim()), + WeaponConcealment.parse(matcher.group(7).trim()) + )); + log.trace("Weapon processed"); + } else { + log.debug("Line {} doesn't match against thrown weapon regex", line); + } + } + + log.info("Extracting ranged weapons"); + Map rangedNotes = new HashMap<>(); + + var prevLineMatched = false; + var prevKey = ""; + for (var line : getTextAsLines(parser, BOOK_DETAILS.rangedWeapons().textLocation())) { + var matcher = notePattern.matcher(line); + if (matcher.matches()) { + prevKey = matcher.group(1).trim(); + rangedNotes.put(prevKey, matcher.group(2).trim()); + prevLineMatched = true; + } else if (prevLineMatched) { + rangedNotes.put(prevKey, rangedNotes.get(prevKey).trim() + " " + line.trim()); + prevLineMatched = false; + prevKey = null; + } + } + + var rangedWeaponPattern = Pattern.compile("(?[A-Za-z\\s.,-]+)(?\\**)?\\s*(?\\d+)\\s*(?\\d+)\\s*(?\\d+|\\*)\\s*(?\\d+)(\\+\\s*(?\\d))?\\s*(?[PJTN])(?\\**)\\s*"); + for (var line : getTextAsLines(parser, BOOK_DETAILS.rangedWeapons().textLocation())) { + log.trace("Processing weapon line"); + var matcher = rangedWeaponPattern.matcher(line); + if (matcher.matches()) { + log.trace("Weapon matches"); + var description = new ArrayList(); + var nameStars = matcher.group("namestars"); + var bursty = false; + var damageType = DamageTypes.LETHAL; + if ("*".equals(nameStars)) { + bursty = true; + description.add(new StringEntry(fixText(rangedNotes.get(nameStars).trim()))); + } + if ("**".equals(nameStars) || line.contains("Bow") || line.contains("bow")) { + description.add(new StringEntry(fixText(rangedNotes.get("**").trim()))); + } + if ("****".equals(nameStars)) { + damageType = DamageTypes.BASHING; + description.add(new StringEntry(fixText(rangedNotes.get(nameStars).trim()))); + } + + var concealStars = matcher.group("concealstars"); + if ("***".equals(concealStars)) { + description.add(new StringEntry(fixText(rangedNotes.get(concealStars).trim()))); + } + + Integer rate = matcher.group("rate").equals("*") ? null : Integer.parseInt(matcher.group("rate")); + int clip = Integer.parseInt(matcher.group("clip")); + if (matcher.group("clipbonus") != null) { + clip += Integer.parseInt(matcher.group("clipbonus")); + } + entries.add(new RangedWeapon( + matcher.group("name").trim(), + description, + Integer.parseInt(matcher.group("damage").trim()), + damageType, + WeaponConcealment.parse(matcher.group("conceal").trim()), + Integer.parseInt(matcher.group("range")), + rate, + clip, + false, + bursty, + bursty, + bursty + )); + log.trace("Weapon processed"); + } else { + log.debug("Line {} doesn't match against weapon regex", line); } } } diff --git a/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/MeleeWeaponLocations.java b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/MeleeWeaponLocations.java new file mode 100644 index 0000000..8d7e7f3 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/MeleeWeaponLocations.java @@ -0,0 +1,52 @@ +package name.ipsi.project.fwbp.books.werewolf.locations; + +import com.itextpdf.kernel.geom.Rectangle; +import name.ipsi.project.fwbp.books.shared.locations.MeleeWeaponLocation; +import name.ipsi.project.fwbp.books.shared.locations.TextArea; +import name.ipsi.project.fwbp.books.shared.locations.TextLocation; +import name.ipsi.project.fwbp.books.shared.locations.TextLocationType; + +import java.util.Arrays; +import java.util.List; + +public class MeleeWeaponLocations { + public static final List DATA = Arrays.asList( + new MeleeWeaponLocation( + new TextLocation( + TextLocationType.TABLE, + new TextArea(303, new Rectangle(245.4442f, 678.96f, 103.10393f, 13.0f)), + new TextArea(303, new Rectangle(154.62f, 212.02211f, 285.1996f, 11.929993f)), + new TextArea(303, new Rectangle(123.75f, 652.332f, 257.44598f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 638.882f, 255.26059f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 625.432f, 255.26059f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 611.9821f, 257.44598f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 598.532f, 259.62158f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 585.082f, 259.62158f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 571.632f, 255.26059f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 558.182f, 259.62158f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 544.7321f, 259.62158f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 531.282f, 258.53378f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 517.832f, 258.53378f, 11.799988f)), + new TextArea(303, new Rectangle(123.75f, 504.382f, 272.44983f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 490.932f, 257.44598f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 477.4821f, 258.53378f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 464.03198f, 255.26059f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 450.582f, 258.53378f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 437.13208f, 259.62158f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 423.682f, 258.53378f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 410.232f, 259.62158f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 396.78198f, 259.62158f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 383.332f, 262.07162f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 369.72208f, 258.04f, 11.960022f)), + new TextArea(303, new Rectangle(123.75f, 356.27206f, 303.30002f, 11.960022f)), + new TextArea(303, new Rectangle(123.75f, 342.82205f, 188.98001f, 11.960022f)), + new TextArea(303, new Rectangle(123.75f, 329.37204f, 226.98001f, 11.960022f)), + new TextArea(303, new Rectangle(123.75f, 316.08203f, 235.0f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 302.63202f, 102.17001f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 289.182f, 334.88004f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 275.732f, 347.98004f, 11.800018f)), + new TextArea(303, new Rectangle(123.75f, 262.28198f, 196.92001f, 11.800018f)) + ) + ) + ); +} diff --git a/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/RangedWeaponLocations.java b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/RangedWeaponLocations.java new file mode 100644 index 0000000..bc0764b --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/RangedWeaponLocations.java @@ -0,0 +1,67 @@ +package name.ipsi.project.fwbp.books.werewolf.locations; + +import com.itextpdf.kernel.geom.Rectangle; +import name.ipsi.project.fwbp.books.shared.locations.RangedWeaponLocation; +import name.ipsi.project.fwbp.books.shared.locations.TextArea; +import name.ipsi.project.fwbp.books.shared.locations.TextLocation; +import name.ipsi.project.fwbp.books.shared.locations.TextLocationType; + +import java.util.Arrays; +import java.util.List; + +public class RangedWeaponLocations { + public static final List DATA = Arrays.asList( + new RangedWeaponLocation( + new TextLocation( + TextLocationType.TABLE, + new TextArea(304, new Rectangle(257.4787f, 702.72003f, 112.50009f, 13.0f)), + new TextArea(304, new Rectangle(79.55f, 689.38214f, 348.2038f, 11.929993f)), + new TextArea(304, new Rectangle(79.55f, 675.932f, 320.4403f, 11.959961f)), + new TextArea(304, new Rectangle(97.55f, 662.63196f, 91.179214f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 649.032f, 318.2578f, 11.960022f)), + new TextArea(304, new Rectangle(97.55f, 635.73206f, 116.208405f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 622.1321f, 320.4418f, 11.960022f)), + new TextArea(304, new Rectangle(97.55f, 608.832f, 63.6902f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 595.232f, 318.2567f, 11.959961f)), + new TextArea(304, new Rectangle(97.55f, 581.932f, 108.60359f, 11.650024f)), + new TextArea(304, new Rectangle(79.55f, 568.3321f, 322.6148f, 11.959961f)), + new TextArea(304, new Rectangle(97.55f, 555.0321f, 101.8024f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 541.4321f, 318.2573f, 11.959839f)), + new TextArea(304, new Rectangle(97.55f, 528.13196f, 88.180405f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 514.532f, 321.53f, 11.960022f)), + new TextArea(304, new Rectangle(97.55f, 501.2321f, 68.06099f, 11.649994f)), + new TextArea(304, new Rectangle(79.5474f, 487.63208f, 322.6178f, 11.960022f)), + new TextArea(304, new Rectangle(97.55f, 474.33197f, 85.2012f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 460.73196f, 318.2566f, 11.960144f)), + new TextArea(304, new Rectangle(97.55f, 447.4321f, 176.98802f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 433.8321f, 321.5305f, 11.95993f)), + new TextArea(304, new Rectangle(97.55f, 420.53207f, 93.903595f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 406.93207f, 321.5297f, 11.95993f)), + new TextArea(304, new Rectangle(97.55f, 393.63208f, 87.9256f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 380.03207f, 322.6179f, 11.9600525f)), + new TextArea(304, new Rectangle(97.55f, 366.732f, 95.55f, 11.650024f)), + new TextArea(304, new Rectangle(79.5474f, 353.132f, 32.124405f, 11.930023f)), + new TextArea(304, new Rectangle(97.55f, 339.83197f, 304.61267f, 11.810028f)), + new TextArea(304, new Rectangle(97.55f, 326.38208f, 304.6128f, 11.810028f)), + new TextArea(304, new Rectangle(97.55f, 312.9321f, 304.6128f, 11.810028f)), + new TextArea(304, new Rectangle(97.55f, 299.482f, 317.4016f, 11.810028f)), + new TextArea(304, new Rectangle(97.55f, 286.03207f, 303.5246f, 11.810028f)), + new TextArea(304, new Rectangle(97.55f, 272.58197f, 304.61456f, 11.810028f)), + new TextArea(304, new Rectangle(79.55f, 258.9821f, 320.44128f, 11.960022f)), + new TextArea(304, new Rectangle(79.55f, 245.5321f, 322.8911f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 232.082f, 362.4323f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 218.63211f, 216.64864f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 205.18211f, 468.40076f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 191.73212f, 357.09247f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 178.28212f, 470.73328f, 11.959991f)), + new TextArea(304, new Rectangle(97.552605f, 166.44211f, 64.78779f, 11.800003f)), + new TextArea(304, new Rectangle(79.55f, 152.83212f, 162.47421f, 11.959991f)), + new TextArea(304, new Rectangle(79.55f, 139.54211f, 234.32787f, 11.800003f)), + new TextArea(304, new Rectangle(79.55f, 126.092125f, 468.1167f, 11.799995f)), + new TextArea(304, new Rectangle(79.55f, 112.64213f, 468.3811f, 11.799995f)), + new TextArea(304, new Rectangle(79.55f, 99.19213f, 470.76276f, 11.799995f)), + new TextArea(304, new Rectangle(97.552605f, 87.19213f, 191.86441f, 11.799995f)) + ) + ) + ); +} diff --git a/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/ThrownWeaponLocations.java b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/ThrownWeaponLocations.java new file mode 100644 index 0000000..e0f54ea --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/books/werewolf/locations/ThrownWeaponLocations.java @@ -0,0 +1,28 @@ +package name.ipsi.project.fwbp.books.werewolf.locations; + +import com.itextpdf.kernel.geom.Rectangle; +import name.ipsi.project.fwbp.books.shared.locations.TextArea; +import name.ipsi.project.fwbp.books.shared.locations.TextLocation; +import name.ipsi.project.fwbp.books.shared.locations.TextLocationType; +import name.ipsi.project.fwbp.books.shared.locations.ThrownWeaponLocation; + +import java.util.Arrays; +import java.util.List; + +public class ThrownWeaponLocations { + public static final List DATA = Arrays.asList( + new ThrownWeaponLocation( + new TextLocation( + TextLocationType.TABLE, + new TextArea(303, new Rectangle(239.221f, 225.36009f, 115.57796f, 13.0f)), + new TextArea(303, new Rectangle(154.62f, 212.02211f, 285.1996f, 11.929993f)), + new TextArea(303, new Rectangle(154.62f, 198.7321f, 257.44598f, 11.800003f)), + new TextArea(303, new Rectangle(154.62f, 185.28209f, 257.44598f, 11.800003f)), + new TextArea(303, new Rectangle(154.62f, 171.83209f, 259.62158f, 11.800003f)), + new TextArea(303, new Rectangle(154.62f, 158.3821f, 274.8508f, 11.800003f)), + new TextArea(303, new Rectangle(154.62f, 144.9321f, 259.62158f, 11.800003f)), + new TextArea(303, new Rectangle(154.62f, 131.4821f, 255.26059f, 11.800003f)) + ) + ) + ); +} diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/Attack.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/Attack.java new file mode 100644 index 0000000..2002cd0 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/Attack.java @@ -0,0 +1,11 @@ +package name.ipsi.project.fwbp.foundry.wod; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record Attack( + String attribute, + int accuracy, + String ability, + @JsonProperty("isrollable") boolean rollable +) { +} diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/Damage.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/Damage.java new file mode 100644 index 0000000..186f2e3 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/Damage.java @@ -0,0 +1,11 @@ +package name.ipsi.project.fwbp.foundry.wod; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record Damage( + String attribute, + int bonus, + DamageTypes type, + @JsonProperty("isrollable") boolean rollable +) { +} diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/MeleeWeaponData.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/MeleeWeaponData.java index 3011f00..c31f787 100644 --- a/src/main/java/name/ipsi/project/fwbp/foundry/wod/MeleeWeaponData.java +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/MeleeWeaponData.java @@ -30,19 +30,4 @@ public MeleeWeaponData(Attack attack, WeaponConcealment conceal, Damage damage, this(attack, conceal, damage, description, difficulty, true, false, false, natural, twohanded, "", "2.0.3", ""); } - public record Attack( - String attribute, - int accuracy, - String ability, - @JsonProperty("isrollable") boolean rollable - ) { - } - - public record Damage( - String attribute, - int bonus, - DamageTypes type, - @JsonProperty("isrollable") boolean rollable - ) { - } } diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/RangedWeaponData.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/RangedWeaponData.java new file mode 100644 index 0000000..b86eaa7 --- /dev/null +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/RangedWeaponData.java @@ -0,0 +1,41 @@ +package name.ipsi.project.fwbp.foundry.wod; + +import com.fasterxml.jackson.annotation.JsonProperty; +import name.ipsi.project.fwbp.foundry.core.ItemData; + +public record RangedWeaponData( + Attack attack, + Clip clip, + WeaponConcealment conceal, + Damage damage, + String description, + int difficulty, + @JsonProperty("iscreated") boolean created, + @JsonProperty("isequipped") boolean equipped, + @JsonProperty("ismagical") boolean magical, + @JsonProperty("istwohanded") boolean twohanded, + Mode mode, + String parentId, + Integer range, + Integer rate, + String version, + String worldanvil +) implements ItemData { + + public RangedWeaponData(Attack attack, Clip clip, WeaponConcealment concealment, Damage damage, String description, Mode mode, Integer range, Integer rate) { + this(attack, clip, concealment, damage, description, 6, true, false, false, false, mode, null, range, rate, "2.0.3", null); + } + + public RangedWeaponData(Attack attack, WeaponConcealment concealment, Damage damage, String description, int difficulty) { + this(attack, null, concealment, damage, description, difficulty, true, false, false, false, null, null,null, null, "2.0.3", null); + } + + public record Clip(Integer value, Integer max) {} + + public record Mode( + @JsonProperty("hasreload") boolean reload, + @JsonProperty("hasburst") boolean burst, + @JsonProperty("hasfullauto") boolean fullAuto, + @JsonProperty("hasspray") boolean spray + ) {} +} diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/WeaponConcealment.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/WeaponConcealment.java index acce041..40a1639 100644 --- a/src/main/java/name/ipsi/project/fwbp/foundry/wod/WeaponConcealment.java +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/WeaponConcealment.java @@ -14,6 +14,7 @@ public enum WeaponConcealment { public static WeaponConcealment parse(String type) { return switch (type) { + case "varies" -> null; case "P" -> POCKET; case "J" -> JACKET; case "T" -> TRENCHCOAT; diff --git a/src/main/java/name/ipsi/project/fwbp/foundry/wod/werewolf/Werewolf20FoundryConverter.java b/src/main/java/name/ipsi/project/fwbp/foundry/wod/werewolf/Werewolf20FoundryConverter.java index 5d94924..9d7c21a 100644 --- a/src/main/java/name/ipsi/project/fwbp/foundry/wod/werewolf/Werewolf20FoundryConverter.java +++ b/src/main/java/name/ipsi/project/fwbp/foundry/wod/werewolf/Werewolf20FoundryConverter.java @@ -44,6 +44,8 @@ public Adventure processAsAdventure(List entries) throws IOException folders.add(createFolder("flaws-garou", "Flaws - Garou", DocumentTypes.ITEM, FolderSortingModes.MANUAL, itemFolderSort++, null)); folders.add(createFolder("weapons-melee", "Weapons - Melee", DocumentTypes.ITEM, FolderSortingModes.ALPHABETICAL, itemFolderSort++, null)); + folders.add(createFolder("weapons-thrown", "Weapons - Thrown", DocumentTypes.ITEM, FolderSortingModes.ALPHABETICAL, itemFolderSort++, null)); + folders.add(createFolder("weapons-ranged", "Weapons - Ranged", DocumentTypes.ITEM, FolderSortingModes.ALPHABETICAL, itemFolderSort++, null)); double sortIdx = 0; for(var type : RiteType.values()) { folders.add(createFolder("garou-rites-" + type.displayName(), type.displayName(), DocumentTypes.ITEM, FolderSortingModes.ALPHABETICAL, sortIdx++, folderIdsByName.get("Rites - Garou"))); @@ -86,7 +88,11 @@ public Adventure processAsAdventure(List entries) throws IOException } else if (entry instanceof Gift g) { items.add(processGift(g, folderIdsByName.get("Gifts - Garou"))); } else if (entry instanceof MeleeWeapon mw) { - items.add(processMeleeWeapon(mw, folderIdsByName.get("Weapons - Melee"))); + items.add(processWeapon(mw, folderIdsByName.get("Weapons - Melee"))); + } else if (entry instanceof ThrownWeapon tw) { + items.add(processWeapon(tw)); + } else if (entry instanceof RangedWeapon rw) { + items.add(processWeapon(rw)); } else if (entry instanceof Background b) { items.add(processBackground(b)); } else if (entry instanceof Rite r) { @@ -131,32 +137,33 @@ private Folder createFolder(String idGroup, String name, DocumentTypes documentT return new Folder(id, name, documentType, sortingMode, sortOrder, null, Collections.emptyMap(), parentFolderId, null); } - private Item processMeleeWeapon(MeleeWeapon meleeWeapon, String folder) { + private Item processWeapon(MeleeWeapon weapon, String folder) throws IOException { log.trace("Converting melee weapon"); + var description = Templater.instance().compile("weapon").apply(weapon); return new Item( - meleeWeapon.id(), - meleeWeapon.name(), + weapon.id(), + weapon.name(), ItemTypes.MELEE_WEAPON, - meleeWeapon.natural() ? "systems/worldofdarkness/assets/img/items/naturalweapons.svg" + weapon.natural() ? "systems/worldofdarkness/assets/img/items/naturalweapons.svg" : "systems/worldofdarkness/assets/img/items/meleeweapons.svg", new MeleeWeaponData( - new MeleeWeaponData.Attack( + new Attack( "dexterity", 0, - "brawl", + weapon.natural() ? "brawl" : "melee", true ), - meleeWeapon.concealment(), - new MeleeWeaponData.Damage( + weapon.concealment(), + new Damage( "strength", - meleeWeapon.damageBonus(), - meleeWeapon.damageType(), + weapon.damageBonus(), + weapon.damageType(), true ), - meleeWeapon.description(), - String.valueOf(meleeWeapon.difficulty()), - meleeWeapon.natural(), - meleeWeapon.twoHanded() + description, + String.valueOf(weapon.difficulty()), + weapon.natural(), + weapon.twoHanded() ), "", folder, @@ -167,6 +174,77 @@ private Item processMeleeWeapon(MeleeWeapon meleeWeapon, String folder) { ); } + private Item processWeapon(ThrownWeapon weapon) throws IOException { + var description = Templater.instance().compile("weapon").apply(weapon); + log.trace("Converting thrown weapon"); + return new Item( + weapon.id(), + weapon.name(), + ItemTypes.RANGED_WEAPON, + "systems/worldofdarkness/assets/img/items/rangedweapons.svg", + new RangedWeaponData( + new Attack( + "dexterity", + 0, + "athletics", + true + ), + weapon.concealment(), + new Damage( + weapon.includeStrength() ? "strength" : "", + weapon.damageBonus(), + weapon.damageType(), + true + ), + description, + weapon.difficulty() + ), + "", + folderIdsByName.get("Weapons - Thrown"), + 0, + DocumentOwnershipLevel.defaultObserver(), + Collections.emptyMap(), + Packs.Weapons + ); + } + + private Item processWeapon(RangedWeapon weapon) throws IOException { + log.trace("Converting ranged weapon"); + var description = Templater.instance().compile("weapon").apply(weapon); + return new Item( + weapon.id(), + weapon.name(), + ItemTypes.RANGED_WEAPON, + "systems/worldofdarkness/assets/img/items/rangedweapons.svg", + new RangedWeaponData( + new Attack( + "dexterity", + 0, + weapon.name().toLowerCase().contains("bow") ? "archery" : "firearms", + true + ), + new RangedWeaponData.Clip(weapon.clip(), weapon.clip()), + weapon.concealment(), + new Damage( + "", + weapon.damage(), + weapon.damageType(), + true + ), + description, + new RangedWeaponData.Mode(weapon.canReload(), weapon.hasBurst(), weapon.hasFullAuto(), weapon.hasSpray()), + weapon.range(), + weapon.rate() + ), + "", + folderIdsByName.get("Weapons - Ranged"), + 0, + DocumentOwnershipLevel.defaultObserver(), + Collections.emptyMap(), + Packs.Weapons + ); + } + private Journal processBreed(Breed b) throws IOException { log.debug("Running template for breed {}", b.name()); var html = Templater.instance().compile("breed").apply(b); diff --git a/src/main/resources/handlebars/weapon.hbs b/src/main/resources/handlebars/weapon.hbs new file mode 100644 index 0000000..7e32913 --- /dev/null +++ b/src/main/resources/handlebars/weapon.hbs @@ -0,0 +1 @@ +{{>description description}} \ No newline at end of file diff --git a/src/main/resources/id-list.txt b/src/main/resources/id-list.txt index 1e28dcb..246d450 100644 Binary files a/src/main/resources/id-list.txt and b/src/main/resources/id-list.txt differ diff --git a/src/test/java/name/ipsi/project/fwbp/MainTest.java b/src/test/java/name/ipsi/project/fwbp/MainTest.java index 41b07c7..153b7fa 100644 --- a/src/test/java/name/ipsi/project/fwbp/MainTest.java +++ b/src/test/java/name/ipsi/project/fwbp/MainTest.java @@ -39,7 +39,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "module.json"), "96947f364f029a8b6eb0629bdb8d167829254ae3215775ccb34b2f2613ff56ca8dc3786193a970c41c01787d2746415ba59a572803285a5d3d53556b4fefb39a"); expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "w20.css"), "2debd674fb547d898a6cfc8addefa1e22cacde66938df545de2937c23715b39ecc4185818845ad2318c98dba0514e476d23377d80ba9fd450be4e0addf550cad"); expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "w20.js"), "01d4f461c7e84a173e206815cd9e48ceef1606c954c189f85e0d293657ac9393beb42faab59711f374733ab3b6c5bca6cebf63aed5d767c574e8e6e0d325d363"); - expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "packs", "w20.db"), "bac277ffd66f179cf422320c84ce686a25b5960ff5771f0499f58854979ba77fee22cc73bde97a11a43df53de641f6cdf5a7485989eb5c9b03aa12fdccc05414"); + expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "packs", "w20.db"), "6239e01c6a18d8969c15e593d71ed408860eadcaec375a78431c4b971115b3104af39af9bd3eaf0dff6ec7833e7e37525fe0d322064160551497162888ed3552"); expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "images", "adventure-cover.jpeg"), "17d71cdd62f4c930b803fbcf1fab02a3d0244eb7fa1d4359ab088381b1288f1bb191117ec60d3075528732ff9ce4888545e6b66b0d681b00b68b79a214daee1c"); expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "images", "black-furies-splash.jpeg"), "0c2f71fba6238336509d84672af71bee9344c477e0696ddd2f4dcf7d53193a1df4726f59f78135d653411f9e44dddbb08f54c65cb552a3ae3ce358dc1e2a4dea"); expectedFiles.put(Path.of("modules", "wod-werewolf-20-core", "images", "black-spiral-dancers-splash.jpeg"), "10c193572ee8f878ce800f3e4bd6db12e3971714f9f69bda1ca15ee8edb64b9540f6638aacefdafdeef07d7027dc9c6f44d0423ca9c75caa492bcc752feaa372"); @@ -78,8 +78,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { } }); - assertIterableEquals(expectedFiles.entrySet(), actualFiles.entrySet()); - + // Have to save the IDs first, otherwise test is not reproducible Main.writeIds(); + + assertIterableEquals(expectedFiles.entrySet(), actualFiles.entrySet()); } } \ No newline at end of file