Skip to content

Commit 7b3081e

Browse files
authored
Merge pull request #114 from fmasa/enemy-in-shadows
[EiS] Enemy in Shadows Spell import
2 parents 38daf67 + 899eb9d commit 7b3081e

File tree

12 files changed

+120
-15
lines changed

12 files changed

+120
-15
lines changed

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/RulebookCompendiumImportScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow
2929
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.PdfCompendiumImporter
3030
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.Book
3131
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.CoreRulebook
32+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.EnemyInShadowsCompanion
3233
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.UpInArms
3334
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.WindsOfMagic
3435
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.loadDocument
@@ -101,7 +102,7 @@ class RulebookCompendiumImportScreen(
101102
modifier = Modifier.padding(bottom = Spacing.medium)
102103
)
103104

104-
val books = remember { listOf(CoreRulebook, UpInArms, WindsOfMagic) }
105+
val books = remember { listOf(CoreRulebook, UpInArms, WindsOfMagic, EnemyInShadowsCompanion) }
105106

106107
books.forEach { book ->
107108
key(book) {

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/Spell.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ data class Spell(
2626
const val RANGE_MAX_LENGTH = 50
2727
const val TARGET_MAX_LENGTH = 50
2828
const val DURATION_MAX_LENGTH = 50
29-
const val EFFECT_MAX_LENGTH = 1000
29+
const val EFFECT_MAX_LENGTH = 1500
3030
const val LORE_MAX_LENGTH = 50
3131
}
3232

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/importer/books/CoreRulebook.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ object CoreRulebook :
7979
}
8080

8181
override fun importSpells(document: Document): List<Spell> {
82-
return SpellParser().import(document, this, sequenceOf(240..257))
82+
return SpellParser(
83+
specialLores = mapOf(
84+
"Petty Spells" to "Petty Spells",
85+
"Arcane Spells" to "Arcane Spells",
86+
),
87+
).import(document, this, sequenceOf(240..257))
8388
}
8489

8590
override fun importBlessings(document: Document): List<Blessing> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books
2+
3+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.Spell
4+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.Document
5+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.SpellParser
6+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.TextPosition
7+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.TextToken
8+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.Token
9+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.sources.SpellSource
10+
11+
object EnemyInShadowsCompanion : Book, SpellSource {
12+
13+
override val name = "Enemy in Shadows - Companion"
14+
15+
private const val pageOffset = 2
16+
17+
override fun importSpells(document: Document): List<Spell> {
18+
return SpellParser(
19+
specialLores = mapOf(
20+
"Chaos Arcane Spells" to "Chaos Arcane Spells",
21+
),
22+
ignoredSpellLikeHeadings = setOf("Lore Attribute"),
23+
).import(
24+
document,
25+
this,
26+
pageRanges = sequenceOf(79 + pageOffset..83 + pageOffset)
27+
).map { it.copy(isVisibleToPlayers = false) }
28+
}
29+
30+
override fun areSameStyle(a: TextPosition, b: TextPosition): Boolean {
31+
return super.areSameStyle(a, b) || arePartsOfHeading2(a, b)
32+
}
33+
34+
private fun arePartsOfHeading2(a: TextPosition, b: TextPosition): Boolean {
35+
// Some special symbols have different height
36+
return a.getFont().getName().endsWith("CaslonAntique-Bold-SC700") &&
37+
a.getFont().getName().endsWith("CaslonAntique-Bold-SC700") && (
38+
a.getFontSizeInPt() == b.getFontSizeInPt() || (
39+
minOf(a.getFontSizeInPt(), b.getFontSizeInPt()) == 12f &&
40+
maxOf(a.getFontSizeInPt(), b.getFontSizeInPt()) == 18f
41+
)
42+
)
43+
}
44+
45+
override fun resolveToken(textToken: TextToken): Token? {
46+
if (
47+
(textToken.fontSizePt == 12f || textToken.fontSizePt == 18f) &&
48+
textToken.fontName.endsWith("CaslonAntique-Bold-SC700")
49+
) {
50+
return Token.Heading2(textToken.text)
51+
}
52+
53+
if (
54+
(textToken.fontSizePt == 19f || textToken.fontSizePt == 22f) &&
55+
textToken.fontName.endsWith("CaslonAntique-Bold")
56+
) {
57+
return Token.Heading2(textToken.text)
58+
}
59+
60+
if (textToken.fontSizePt == 12f && textToken.fontName.endsWith("ACaslonPro-Bold")) {
61+
return Token.Heading3(textToken.text)
62+
}
63+
64+
if (textToken.fontSizePt == 10f || textToken.fontSizePt == 9f) {
65+
if (textToken.fontName.endsWith("ACaslonPro-Bold")) {
66+
return Token.BoldPart(textToken.text)
67+
}
68+
69+
if (textToken.fontName.endsWith("ACaslonPro-Italic")) {
70+
return Token.ItalicsPart(textToken.text)
71+
}
72+
if (textToken.fontName.endsWith("ACaslonPro-Regular")) {
73+
return Token.NormalPart(textToken.text)
74+
}
75+
}
76+
77+
return null
78+
}
79+
}

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/importer/books/WindsOfMagic.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ object WindsOfMagic : Book, CareerSource, SpellSource {
3131

3232
override fun importSpells(document: Document): List<Spell> {
3333
return SpellParser(
34+
specialLores = mapOf(
35+
"New Arcane Spells" to "Arcane Spells",
36+
),
3437
isEnd = {
3538
(
3639
it is Token.Heading1 && it.text.trim()

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/importer/parsers/CareerParser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class CareerParser {
3939
val stream = TokenStream(firstColumn.toList())
4040
val english = Localization.English
4141

42-
val name = stream.consumeOneOfType<Token.Heading1>().text
42+
val name = stream.consumeOneOfType<Token.Heading>().text
4343
.trim()
4444
.splitToSequence(' ', '\n')
4545
.map { it.lowercase() }

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/importer/parsers/SpellParser.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import com.benasher44.uuid.uuid4
44
import cz.frantisekmasa.wfrp_master.common.compendium.domain.Spell
55

66
class SpellParser(
7+
private val specialLores: Map<String, String>,
8+
private val ignoredSpellLikeHeadings: Set<String> = emptySet(),
79
private val isEnd: (Token?) -> Boolean = { it == null },
810
) {
911

@@ -39,7 +41,10 @@ class SpellParser(
3941
}
4042

4143
private fun consumeSpells(lore: String, stream: TokenStream): Sequence<Spell> = sequence {
42-
stream.dropUntil { it is Token.Heading3 }
44+
stream.dropUntil { token ->
45+
token is Token.Heading3 &&
46+
ignoredSpellLikeHeadings.none { token.text.trim().equals(it, ignoreCase = true) }
47+
}
4348

4449
while (stream.peek() is Token.Heading3) {
4550
val name = stream.consumeOneOfType<Token.Heading3>().text.trim()
@@ -136,10 +141,5 @@ class SpellParser(
136141

137142
companion object {
138143
private val loreHeadingRegex = Regex("(The )?Lore of ([a-z]+)", RegexOption.IGNORE_CASE)
139-
private val specialLores = mapOf(
140-
"Petty Spells" to "Petty Spells",
141-
"Arcane Spells" to "Arcane Spells",
142-
"New Arcane Spells" to "Arcane Spells",
143-
)
144144
}
145145
}

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/compendium/domain/importer/parsers/Token.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ sealed interface Token {
44

55
sealed class TextToken(val text: String) : Token
66

7-
interface Heading
7+
interface Heading : Token {
8+
val text: String
9+
}
810
class Heading1(text: String) : TextToken(text), Heading
911
class Heading2(text: String) : TextToken(text), Heading
1012
class Heading3(text: String) : ParagraphToken(text, "heading3"), Heading

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/core/domain/spells/Spell.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ data class Spell(
5757
const val RANGE_MAX_LENGTH = 50
5858
const val TARGET_MAX_LENGTH = 50
5959
const val DURATION_MAX_LENGTH = 50
60-
const val EFFECT_MAX_LENGTH = 1000
60+
const val EFFECT_MAX_LENGTH = 1500
6161

6262
fun fromCompendium(spell: CompendiumSpell): Spell {
6363
return Spell(

common/src/jvmTest/kotlin/ImporterTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.CoreRulebook
2+
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.EnemyInShadowsCompanion
23
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.UpInArms
34
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.books.WindsOfMagic
45
import cz.frantisekmasa.wfrp_master.common.compendium.domain.importer.parsers.Document
@@ -84,6 +85,14 @@ class ImporterTest {
8485
}
8586
}
8687

88+
@Test
89+
fun `spell import (Enemy in Shadows - Companion)`() {
90+
withEnemyInShadowsCompanion { document ->
91+
val spells = EnemyInShadowsCompanion.importSpells(document)
92+
assertEquals(9 + 14, spells.size)
93+
}
94+
}
95+
8796
@Test
8897
fun `blessings import (Core Rulebook)`() {
8998
withCoreRuleBook { document ->
@@ -112,6 +121,12 @@ class ImporterTest {
112121
.use(block)
113122
}
114123

124+
private fun withEnemyInShadowsCompanion(block: (Document) -> Unit) {
125+
loadDocument(
126+
javaClass.getResourceAsStream("enemy_in_shadows_companion.pdf") as InputStream
127+
).use(block)
128+
}
129+
115130
private fun withUpInArms(block: (Document) -> Unit) {
116131
loadDocument(javaClass.getResourceAsStream("up_in_arms.pdf") as InputStream)
117132
.use(block)

0 commit comments

Comments
 (0)