Skip to content

Commit afca89d

Browse files
committed
Add ranged & thrown weapons
Also update melee weapon processing to add notes for applicable weapons (e.g., chainsaw)
1 parent 5a7d70d commit afca89d

21 files changed

+547
-61
lines changed

pom.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,12 @@
236236
<list>name.ipsi.project.fwbp.foundry.wod.ItemTypes</list>
237237
<list>name.ipsi.project.fwbp.foundry.wod.PowerTypes</list>
238238
<list>name.ipsi.project.fwbp.foundry.wod.WeaponConcealment</list>
239+
<list>name.ipsi.project.fwbp.foundry.wod.RangedWeaponData</list>
240+
<list>name.ipsi.project.fwbp.foundry.wod.RangedWeaponData$Clip</list>
241+
<list>name.ipsi.project.fwbp.foundry.wod.RangedWeaponData$Mode</list>
239242
<list>name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData</list>
240-
<list>name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData$Attack</list>
241-
<list>name.ipsi.project.fwbp.foundry.wod.MeleeWeaponData$Damage</list>
243+
<list>name.ipsi.project.fwbp.foundry.wod.Attack</list>
244+
<list>name.ipsi.project.fwbp.foundry.wod.Damage</list>
242245
<list>name.ipsi.project.fwbp.foundry.wod.werewolf.FetishData</list>
243246
<list>name.ipsi.project.fwbp.foundry.wod.werewolf.FetishType</list>
244247
<list>name.ipsi.project.fwbp.foundry.core.Module</list>

src/main/java/name/ipsi/project/fwbp/Preprocessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,10 @@ public enum GroupType {
429429
rites("Rite"),
430430
fetishes("Fetish"),
431431
talens("Talen"),
432-
mandf("MeritAndFlaw")
432+
mandf("MeritAndFlaw"),
433+
melee_weapons("MeleeWeapon"),
434+
thrown_weapons("ThrownWeapon"),
435+
ranged_weapons("RangedWeapon")
433436
;
434437

435438
private final String classPrefix;
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package name.ipsi.project.fwbp.books.shared;
22

3-
import name.ipsi.project.fwbp.books.shared.BookEntry;
4-
import name.ipsi.project.fwbp.foundry.wod.DamageTypes;
53
import name.ipsi.project.fwbp.foundry.core.FoundryUtils;
4+
import name.ipsi.project.fwbp.foundry.wod.DamageTypes;
65
import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment;
76

7+
import java.util.List;
8+
89
public record MeleeWeapon(
910
String id,
1011
String name,
11-
String description,
12+
List<TextEntry> description,
1213
int difficulty,
1314
int damageBonus,
1415
DamageTypes damageType,
@@ -20,7 +21,7 @@ public record MeleeWeapon(
2021
boolean selfDamage,
2122
boolean natural
2223
) implements BookEntry {
23-
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) {
24+
public MeleeWeapon(String name, List<TextEntry> description, int difficulty, int damageBonus, DamageTypes damageType, WeaponConcealment concealment, boolean canEntangle, boolean breaksAfterUse, boolean twoHanded, boolean silver, boolean selfDamage, boolean natural) {
2425
this(FoundryUtils.generateId("melee-weapon", name), name, description, difficulty, damageBonus, damageType, concealment, canEntangle, breaksAfterUse, twoHanded, silver, selfDamage, natural);
2526
}
2627
}
Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,54 @@
11
package name.ipsi.project.fwbp.books.shared;
22

3-
public record RangedWeapon(String id) implements BookEntry {
3+
import name.ipsi.project.fwbp.foundry.core.FoundryUtils;
4+
import name.ipsi.project.fwbp.foundry.wod.DamageTypes;
5+
import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment;
6+
7+
import java.util.List;
8+
9+
public record RangedWeapon(
10+
String id,
11+
String name,
12+
List<TextEntry> description,
13+
int damage,
14+
DamageTypes damageType,
15+
WeaponConcealment concealment,
16+
Integer range,
17+
Integer rate,
18+
Integer clip,
19+
boolean canReload,
20+
boolean hasBurst,
21+
boolean hasFullAuto,
22+
boolean hasSpray
23+
) implements BookEntry {
24+
public RangedWeapon(
25+
String name,
26+
List<TextEntry> description,
27+
int damage,
28+
DamageTypes damageType,
29+
WeaponConcealment concealment,
30+
Integer range,
31+
Integer rate,
32+
Integer clip,
33+
boolean canReload,
34+
boolean hasBurst,
35+
boolean hasFullAuto,
36+
boolean hasSpray
37+
) {
38+
this(
39+
FoundryUtils.generateId("ranged-weapon", name),
40+
name,
41+
description,
42+
damage,
43+
damageType,
44+
concealment,
45+
range,
46+
rate,
47+
clip,
48+
canReload,
49+
hasBurst,
50+
hasFullAuto,
51+
hasSpray
52+
);
53+
}
454
}
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
package name.ipsi.project.fwbp.books.shared;
22

3-
public record ThrownWeapon(String id) implements BookEntry {
3+
import name.ipsi.project.fwbp.foundry.core.FoundryUtils;
4+
import name.ipsi.project.fwbp.foundry.wod.DamageTypes;
5+
import name.ipsi.project.fwbp.foundry.wod.WeaponConcealment;
6+
7+
import java.util.List;
8+
9+
public record ThrownWeapon(
10+
String id,
11+
String name,
12+
List<TextEntry> description,
13+
int difficulty,
14+
boolean includeStrength,
15+
int damageBonus,
16+
DamageTypes damageType,
17+
WeaponConcealment concealment
18+
) implements BookEntry {
19+
public ThrownWeapon(String name, List<TextEntry> description, int difficulty, boolean includeStrength, int damageBonus, DamageTypes damageType, WeaponConcealment concealment) {
20+
this(FoundryUtils.generateId("thrown-weapon", name), name, description, difficulty, includeStrength, damageBonus, damageType, concealment);
21+
}
422
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package name.ipsi.project.fwbp.books.shared.locations;
2+
3+
public record MeleeWeaponLocation(TextLocation textLocation) {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package name.ipsi.project.fwbp.books.shared.locations;
2+
3+
public record RangedWeaponLocation(TextLocation textLocation) {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package name.ipsi.project.fwbp.books.shared.locations;
2+
3+
public record ThrownWeaponLocation(TextLocation textLocation) {
4+
}

src/main/java/name/ipsi/project/fwbp/books/werewolf/Werewolf20Extractor.java

Lines changed: 142 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,9 @@ public class Werewolf20Extractor {
3737
RiteLocations.DATA,
3838
FetishLocations.DATA,
3939
TalenLocations.DATA,
40-
new WeaponLocations(
41-
new Paragraph(303, makeRect(121, 129, 263, 280)),
42-
new Paragraph(303, makeRect(0, 0, 0, 0)),
43-
new Paragraph(304, makeRect(0, 0, 0, 0))
44-
),
40+
MeleeWeaponLocations.DATA.get(0),
41+
ThrownWeaponLocations.DATA.get(0),
42+
RangedWeaponLocations.DATA.get(0),
4543
Collections.emptyList(),
4644
MeritAndFlawLocations.DATA
4745
);
@@ -56,7 +54,9 @@ private record Book(
5654
List<RiteLocation> rites,
5755
List<FetishLocation> fetishes,
5856
List<TalenLocation> talens,
59-
WeaponLocations weapons,
57+
MeleeWeaponLocation meleeWeapons,
58+
ThrownWeaponLocation thrownWeapons,
59+
RangedWeaponLocation rangedWeapons,
6060
List<SpiritLocation> spirits,
6161
List<MeritAndFlawLocation> meritsAndFlaws
6262
) {}
@@ -280,7 +280,7 @@ public List<BookEntry> process() {
280280

281281
processTribes();
282282

283-
processMeleeWeapons();
283+
processWeapons();
284284

285285
processBackgrounds();
286286

@@ -435,10 +435,10 @@ private void processTribes() {
435435
}
436436
}
437437

438-
private void processMeleeWeapons() {
438+
private void processWeapons() {
439439
entries.add(new MeleeWeapon(
440440
"Bite - Crinos, Hispo, Lupus",
441-
getText(parser, new Paragraph(297, makeRect(304, 213, 245, 77))).get(0).replaceAll("^\\s*•?\\s*Bite:\\s*", ""),
441+
Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 213, 245, 77))).get(0).replaceAll("^\\s*•?\\s*Bite:\\s*", ""))),
442442
5,
443443
1,
444444
DamageTypes.AGGRAVATED,
@@ -453,7 +453,7 @@ private void processMeleeWeapons() {
453453

454454
entries.add(new MeleeWeapon(
455455
"Claw - Crinos, Hispo",
456-
getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""),
456+
Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""))),
457457
6,
458458
2,
459459
DamageTypes.AGGRAVATED,
@@ -468,7 +468,7 @@ private void processMeleeWeapons() {
468468

469469
entries.add(new MeleeWeapon(
470470
"Claw - Glabro, Lupus",
471-
getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""),
471+
Collections.singletonList(new StringEntry(getText(parser, new Paragraph(297, makeRect(304, 335, 246, 52))).get(0).replaceAll("^\\s*•?\\s*Claw:\\s*", ""))),
472472
6,
473473
2,
474474
DamageTypes.BASHING,
@@ -482,29 +482,152 @@ private void processMeleeWeapons() {
482482
));
483483

484484
log.info("Extracting Melee weapons");
485+
Map<String, String> meleeNotes = new HashMap<>();
486+
487+
var notePattern = Pattern.compile("^\\s*(\\*+)\\s*(.*)$");
488+
for (var line : getTextAsLines(parser, BOOK_DETAILS.meleeWeapons().textLocation())) {
489+
var matcher = notePattern.matcher(line);
490+
if (matcher.matches()) {
491+
meleeNotes.put(matcher.group(1), matcher.group(2));
492+
}
493+
}
494+
485495
var meleeWeaponPattern = Pattern.compile("([\\w\\s]+)(\\**)?\\s*(\\d+)(\\**)?\\s*Strength(\\s*\\+\\s*(\\d+))?/([BLA])(\\**)?\\s*([PJTN])\\s*");
486-
for (var line : getTextAsLines(parser, BOOK_DETAILS.weapons().meleeWeapons())) {
496+
for (var line : getTextAsLines(parser, BOOK_DETAILS.meleeWeapons().textLocation())) {
487497
log.trace("Processing weapon line");
488498
var matcher = meleeWeaponPattern.matcher(line);
489499
if (matcher.matches()) {
490500
log.trace("Weapon matches");
501+
var description = new ArrayList<TextEntry>();
502+
boolean canEntangle = matcher.group(4) != null && matcher.group(4).trim().equals("*");
503+
if (canEntangle) {
504+
description.add(new StringEntry(fixText(meleeNotes.get("*"))));
505+
}
506+
boolean breaksAfterUse = matcher.group(8) != null && matcher.group(8).trim().equals("**");
507+
if (breaksAfterUse) {
508+
description.add(new StringEntry(fixText(meleeNotes.get("**"))));
509+
}
510+
boolean twoHanded = matcher.group(2) != null && matcher.group(2).trim().equals("***");
511+
if (twoHanded) {
512+
description.add(new StringEntry(fixText(meleeNotes.get("***"))));
513+
}
514+
boolean silver = matcher.group(2) != null && matcher.group(2).trim().equals("****");
515+
if (silver) {
516+
description.add(new StringEntry(fixText(meleeNotes.get("****"))));
517+
}
518+
boolean selfDamage = matcher.group(8) != null && matcher.group(8).trim().equals("*****");
519+
if (selfDamage) {
520+
description.add(new StringEntry(fixText(meleeNotes.get("*****"))));
521+
}
491522
entries.add(new MeleeWeapon(
492523
matcher.group(1).trim(),
493-
"",
524+
description,
494525
Integer.parseInt(matcher.group(3).trim()),
495526
matcher.group(6) == null ? 0 : Integer.parseInt(matcher.group(6).trim()),
496527
DamageTypes.parse(matcher.group(7).trim()),
497528
WeaponConcealment.parse(matcher.group(9).trim()),
498-
matcher.group(4) != null && matcher.group(4).trim().equals("*"),
499-
matcher.group(8) != null && matcher.group(8).trim().equals("**"),
500-
matcher.group(2) != null && matcher.group(2).trim().equals("***"),
501-
matcher.group(2) != null && matcher.group(2).trim().equals("****"),
502-
matcher.group(8) != null && matcher.group(8).trim().equals("*****"),
529+
canEntangle,
530+
breaksAfterUse,
531+
twoHanded,
532+
silver,
533+
selfDamage,
503534
false
504535
));
505536
log.trace("Weapon processed");
506537
} else {
507-
log.warn("Line {} doesn't match against weapon regex", line);
538+
log.debug("Line {} doesn't match against weapon regex", line);
539+
}
540+
}
541+
542+
log.info("Extracting Throw weapons");
543+
var thrownWeaponPattern = Pattern.compile("([A-Za-z\\s,-]+)\\s*(\\d+)\\s*(Strength)?(\\s*\\+\\s*)?(\\d+)?/([BLA])\\s*([PJTN]|varies)\\s*");
544+
for (var line : getTextAsLines(parser, BOOK_DETAILS.thrownWeapons().textLocation())) {
545+
log.trace("Processing throw line");
546+
var matcher = thrownWeaponPattern.matcher(line);
547+
if (matcher.matches()) {
548+
log.trace("Weapon matches");
549+
entries.add(new ThrownWeapon(
550+
matcher.group(1).trim(),
551+
Collections.emptyList(),
552+
Integer.parseInt(matcher.group(2).trim()),
553+
matcher.group(3) != null,
554+
matcher.group(5) == null ? 0 : Integer.parseInt(matcher.group(5).trim()),
555+
DamageTypes.parse(matcher.group(6).trim()),
556+
WeaponConcealment.parse(matcher.group(7).trim())
557+
));
558+
log.trace("Weapon processed");
559+
} else {
560+
log.debug("Line {} doesn't match against thrown weapon regex", line);
561+
}
562+
}
563+
564+
log.info("Extracting ranged weapons");
565+
Map<String, String> rangedNotes = new HashMap<>();
566+
567+
var prevLineMatched = false;
568+
var prevKey = "";
569+
for (var line : getTextAsLines(parser, BOOK_DETAILS.rangedWeapons().textLocation())) {
570+
var matcher = notePattern.matcher(line);
571+
if (matcher.matches()) {
572+
prevKey = matcher.group(1).trim();
573+
rangedNotes.put(prevKey, matcher.group(2).trim());
574+
prevLineMatched = true;
575+
} else if (prevLineMatched) {
576+
rangedNotes.put(prevKey, rangedNotes.get(prevKey).trim() + " " + line.trim());
577+
prevLineMatched = false;
578+
prevKey = null;
579+
}
580+
}
581+
582+
var rangedWeaponPattern = Pattern.compile("(?<name>[A-Za-z\\s.,-]+)(?<namestars>\\**)?\\s*(?<damage>\\d+)\\s*(?<range>\\d+)\\s*(?<rate>\\d+|\\*)\\s*(?<clip>\\d+)(\\+\\s*(?<clipbonus>\\d))?\\s*(?<conceal>[PJTN])(?<concealstars>\\**)\\s*");
583+
for (var line : getTextAsLines(parser, BOOK_DETAILS.rangedWeapons().textLocation())) {
584+
log.trace("Processing weapon line");
585+
var matcher = rangedWeaponPattern.matcher(line);
586+
if (matcher.matches()) {
587+
log.trace("Weapon matches");
588+
var description = new ArrayList<TextEntry>();
589+
var nameStars = matcher.group("namestars");
590+
var bursty = false;
591+
var damageType = DamageTypes.LETHAL;
592+
if ("*".equals(nameStars)) {
593+
bursty = true;
594+
description.add(new StringEntry(fixText(rangedNotes.get(nameStars).trim())));
595+
}
596+
if ("**".equals(nameStars) || line.contains("Bow") || line.contains("bow")) {
597+
description.add(new StringEntry(fixText(rangedNotes.get("**").trim())));
598+
}
599+
if ("****".equals(nameStars)) {
600+
damageType = DamageTypes.BASHING;
601+
description.add(new StringEntry(fixText(rangedNotes.get(nameStars).trim())));
602+
}
603+
604+
var concealStars = matcher.group("concealstars");
605+
if ("***".equals(concealStars)) {
606+
description.add(new StringEntry(fixText(rangedNotes.get(concealStars).trim())));
607+
}
608+
609+
Integer rate = matcher.group("rate").equals("*") ? null : Integer.parseInt(matcher.group("rate"));
610+
int clip = Integer.parseInt(matcher.group("clip"));
611+
if (matcher.group("clipbonus") != null) {
612+
clip += Integer.parseInt(matcher.group("clipbonus"));
613+
}
614+
entries.add(new RangedWeapon(
615+
matcher.group("name").trim(),
616+
description,
617+
Integer.parseInt(matcher.group("damage").trim()),
618+
damageType,
619+
WeaponConcealment.parse(matcher.group("conceal").trim()),
620+
Integer.parseInt(matcher.group("range")),
621+
rate,
622+
clip,
623+
false,
624+
bursty,
625+
bursty,
626+
bursty
627+
));
628+
log.trace("Weapon processed");
629+
} else {
630+
log.debug("Line {} doesn't match against weapon regex", line);
508631
}
509632
}
510633
}

0 commit comments

Comments
 (0)