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