diff --git a/assets/tessdata/engd2r_inv_th.traineddata b/assets/tessdata/engd2r_inv_th.traineddata new file mode 100644 index 0000000..55986ab Binary files /dev/null and b/assets/tessdata/engd2r_inv_th.traineddata differ diff --git a/assets/tessdata/engd2r_inv_th_fast.traineddata b/assets/tessdata/engd2r_inv_th_fast.traineddata new file mode 100644 index 0000000..de53c81 Binary files /dev/null and b/assets/tessdata/engd2r_inv_th_fast.traineddata differ diff --git a/assets/tessdata/engd2r_ui.traineddata b/assets/tessdata/engd2r_ui.traineddata new file mode 100644 index 0000000..d41eac1 Binary files /dev/null and b/assets/tessdata/engd2r_ui.traineddata differ diff --git a/assets/tessdata/engd2r_ui_fast.traineddata b/assets/tessdata/engd2r_ui_fast.traineddata new file mode 100644 index 0000000..52bbc3c Binary files /dev/null and b/assets/tessdata/engd2r_ui_fast.traineddata differ diff --git a/assets/tessdata/ocr_config.txt b/assets/tessdata/ocr_config.txt new file mode 100644 index 0000000..1e7dfbd --- /dev/null +++ b/assets/tessdata/ocr_config.txt @@ -0,0 +1,2 @@ +language_model_penalty_non_freq_dict_word 1 +language_model_penalty_non_dict_word 1 \ No newline at end of file diff --git a/assets/tessdata/ocr_errors.csv b/assets/tessdata/ocr_errors.csv new file mode 100644 index 0000000..d322639 --- /dev/null +++ b/assets/tessdata/ocr_errors.csv @@ -0,0 +1,19 @@ +SHIFLD, SHIELD +SPFAR, SPEAR +GLOVFS, GLOVES +TELEFORT, TELEPORT +TROPHV, TROPHY +CLAVMORE, CLAYMORE +MAKIMUM, MAXIMUM +DEKTERITY, DEXTERITY +DERTERITY, DEXTERITY +QUAHTITY, QUANTITY +DEFERSE, DEFENSE +ARMGR, ARMOR +ARMER, ARMOR +COMDAT, COMBAT +WEAPORS, WEAPONS +AXECLASS, AXE CLASS +IOX%, 10% +IO%, 10% +TWYO, TWO \ No newline at end of file diff --git a/assets/tessdata/word_lists/all_strings.txt b/assets/tessdata/word_lists/all_strings.txt new file mode 100644 index 0000000..59475df --- /dev/null +++ b/assets/tessdata/word_lists/all_strings.txt @@ -0,0 +1,3159 @@ +A +ABADDON +ABHAYA +ABOMINABLE +ABOUT +ABRASION +ABSOLUTION +ABSORB +ABSORBED +ABSORBS +ABSORPTION +ABYSS +ACCELERATION +ACCURACY +ACHERON +ACHMEL +ACHRON +ACROBATIC +ACT +ACTIVATING +ACTIVE +ADAMANT +ADD +ADDER +ADDS +ADHERENT +ADJUDICATION +ADMIRATION +ADROIT +ADVANCE +AEGIS +AERIN +AFFLICT +AFFLICTED +AFTER +AGAINST +AGES +AGONY +AHSAB +AIM +AJHEED +AKARA +AKARAN +AKARAT'S +ALACRITY +ALADDIN'S +ALARIC +ALARMING +ALBAN +ALCUIN +ALDUR'S +ALHIZEER +ALI +ALIZA +ALKOR +ALL +ALMA +ALMA'S +ALMIGHTY +ALPHA +ALTAR +AMAZON +AMBER +AMBERGRIS +AMBIENT +AMBUSH +AMELIORATION +AMETHYST +AMIANTHUS +AMICAE +AMN +AMODEUS' +AMPLIFY +AMPLISA +AMULET +AN +ANCIENT +ANCIENT'S +ANCIENTS +ANCIENTS' +AND +ANDARIEL +ANDARIEL'S +ANGEL +ANGEL'S +ANGELIC +ANGUISH +ANNIHILATOR +ANNIHILUS +ANNOR +ANTHRAX +ANTIDOTE +ANTIMAGIC +ANTLERS +ANYA +ANYA'S +APE +APOCRYPHA +APOTHECARY'S +APPARITION +APPENDAGES +APPLY +APPRENTICE +ARACH +ARACHNID +ARAGON'S +ARBALEST +ARCADIAN +ARCANE +ARCANNA'S +ARCH +ARCH-ANGEL'S +ARCHER +ARCHER'S +ARCHON +ARCING +ARCTIC +ARGENT +ARIOC'S +ARKAINE'S +ARM +ARMAGEDDON +ARMET +ARMOR +ARMORER +ARMORY +ARMS +AROUND +ARREAT +ARREAT'S +ARROW +ARROWS +ARSENAL +ART +ARTFUL +ARTIFACTS +ARTISAN'S +ARTS +AS +ASH +ASHEARA +ASHEARA'S +ASHWOOD +ASK +ASSAILANT +ASSASSIN +ASSAULT +ASTRAL +ASTREON'S +AT +ATAGHAN +ATHAME +ATHENA'S +ATHLETIC +ATLANTEAN +ATLAS +ATMA +ATMA'S +ATTACK +ATTACKER +ATTACKS +ATTEMPTS +ATTRACT +ATTRIBUTES +ATTRITION +AURA +AURAS +AUREOLIC +AURORA'S +AUTHORITY +AVARICE +AVATAR +AVENGER +AVERAGE +AVOID +AWL +AXE +AZRAEL +AZURE +AZUREWRATH +BAAL +BAAL'S +BABA +BADGE +BADNESS +BAEZIL'S +BAG +BAHAMUT'S +BAKER +BALANCE +BALANCED +BALL +BALLISTA +BALROG +BAND +BANE +BANG +BANISHED +BANNER +BANSHEE'S +BAR +BARANAR'S +BARANI +BARB +BARBARIAN +BARBED +BARBS +BARDICHE +BARLOC +BARRACKS +BARREL +BARRICADE +BARRICADED +BARRIER +BARTUC +BARTUC'S +BASANTI +BASH +BASHER +BASHING +BASINET +BASIS +BASKET +BASTARD +BAT +BATTLE +BATTLEBRANCH +BATTLEGEAR +BATTLEMAID +BAYOU +BAZAAR +BE +BEADS +BEAR +BEARDED +BEARER +BEARS +BEAST +BEAUTY +BEC-DE-CORBIN +BED +BEETLE +BEETLEBURST +BEFORE +BELLE +BELT +BENDER +BENEATH +BER +BERSERK +BERSERKER +BERSERKER'S +BERYL +BETRAYAL +BETTER +BEWARE +BIGGIN'S +BILE +BILL +BINDING +BING +BIRD +BISHIBOSH +BITE +BITES +BITTER +BLACK +BLACKBOG'S +BLACKENED +BLACKHAND +BLACKHORN +BLACKHORN'S +BLACKLEACH +BLACKOAK +BLACKTONGUE +BLADE +BLADEBONE +BLADEBUCKLE +BLADES +BLAISE +BLANCHED +BLAST +BLASTBARK +BLAZE +BLAZER +BLAZING +BLESSED +BLESSING +BLIGHT +BLIGHTING +BLINDS +BLING +BLINKBAT'S +BLISS +BLISTER +BLITZEN +BLIZZARD +BLOCK +BLOCKED +BLOCKING +BLOOD +BLOODFIST +BLOODLETTER +BLOODLORD +BLOODMOON +BLOODRISE +BLOODTHIEF +BLOODTREE +BLOODWITCH +BLOODY +BLOW +BLUDGEON +BLUNDERBORE +BODY +BOG +BOHDAN +BOLT +BOLTS +BONE +BONEBREAKER +BONEFLAME +BONEFLESH +BONEHANDLE +BONEHEW +BONESAW +BONESCALPEL +BONESHADE +BONESLAYER +BONESNAP +BONEWEAVE +BONG +BONNET +BONUS +BONUSES +BOOK +BOOKSHELF +BOOTS +BOREAL +BORS +BOULDER +BOVINE +BOW +BOWL +BOWYER'S +BOX +BRACE +BRACERS +BRAIN +BRAINHEW +BRAMBLE +BRANCH +BRAND +BRANDED +BRANDISTOCK +BRAWLER +BRAZIER +BREAK +BREAKER +BREAST +BREATH +BREMM +BRETHREN +BRIDGE +BRIGADIER +BRIGHT +BRILLIANCE +BRILLIANT +BRIMSTONE +BRINGER +BRISER +BRISK +BROAD +BROC +BROGUES +BROKEN +BROM +BRONZE +BROW +BROWN +BRUTAL +BRUTE +BUCKLE +BUCKLER +BUGS +BUL-KATHOS +BUL-KATHOS' +BULWYE +BURGUNDY +BURIAL +BURIZA-DO +BURLY +BURN +BURNING +BURNT +BURROWER +BURST +BUSH +BUTCHER +BUTCHER'S +BUTCHERY +BUY +BUZZARD +BUZZING +BVERRIT +BY +CABINET +CADAVER +CADEN +CADUCEUS +CAGE +CAIN +CAIN'S +CAIRN +CALL +CALLING +CALM +CAMEL +CAMPHOR +CANDLES +CANDLESTICK +CANNIBAL +CANNOT +CANOPIC +CANTOR +CANYON +CAP +CAPTAIN'S +CAPTIVE +CARAPACE +CARBUNCLE +CARCASS +CARETAKER +CARETAKER'S +CARIN +CARMINE +CARNAGE +CARRION +CARVER +CARVERS +CASE +CASKET +CASQUE +CAST +CASTER +CAT +CAT'S +CATACOMBS +CATAPULT +CATGUT +CATHAN'S +CATHEDRAL +CAUSES +CAUSEWAY +CAVE +CAVERN +CAW +CEDAR +CELESTIAL +CELLAR +CENTAUR +CENTURION +CEREBUS' +CEREMONIAL +CERULEAN +CESTUS +CHAIN +CHAINS +CHALAN +CHALICE +CHAM +CHAMBER +CHAMPION +CHANCE +CHANDELIER +CHANG +CHANT +CHAOS +CHAOTIC +CHARACTER +CHARGE +CHARGE-UP +CHARGED +CHARGES +CHARM +CHARSI +CHARSI'S +CHEST +CHESTNUT +CHIEFTAIN +CHILDREN +CHILLED +CHILLING +CHIPPED +CHOICE +CHOIR +CHOKING +CHOP +CHROMATIC +CHU-KO-NU +CINNABAR +CINQUEDEAS +CIRCLE +CIRCLET +CITY +CIVERB'S +CLAN +CLASP +CLASPED +CLASS +CLASS-SPECIFIC +CLAW +CLAWS +CLAY +CLAYMORE +CLEANSING +CLEAVER +CLEGLAW'S +CLICK +CLIFF +CLIFFKILLER +CLOAK +CLOISTER +CLOSED +CLOTH +CLOUD +CLOUDCRACK +CLOUDY +CLUB +CLUTCH +CLUTCHES +COAT +COBALT +COBRA +COCOON +COFFIN +COIF +COIL +COLD +COLDCROW +COLDKILL +COLDSTEAL +COLDSTEEL +COLDWORM +COLENZO +COLLAR +COLLECTOR +COLORFUL +COLOSSUS +COMBAT +COMBATANT +COMET +COMMAND +COMMANDER'S +COMMUNAL +COMPACT +COMPELLING +COMPLETE +COMPOSITE +CONCENTRATE +CONCENTRATION +CONCH +CONDENSING +CONFIGURE +CONFUSE +CONFUSION +CONQUEROR +CONQUEST +CONSECRATED +CONSTRICTING +CONSULT +CONTINUE +CONTROL +CONTROLS +CONVERSION +CONVICTION +COOLNESS +CORAL +CORD +CORDON +CORONA +CORONET +CORPORAL +CORPSE +CORPSEFIRE +CORPSEMOURN +CORPSES +CORPULENT +CORROSIVE +CORRUPTION +CORSPES +COST +COUANT +COUNCIL +COUNTESS +COVER +COW +COWL +CRACK +CRACKED +CRACKLING +CRAFTMANSHIP +CRAINTE +CRANEBEAK +CRANIUM +CRATE +CRAWLER +CREATURE +CREDENDUM +CREDIT +CREEPER +CREEPING +CREMATION +CRESCENT +CREST +CRIMSON +CRITICAL +CROOK +CROSSBOW +CROW +CROWBILL +CROWN +CRUDE +CRUEL +CRUELTY +CRUSADER +CRUSHER +CRUSHFLANGE +CRUSHING +CRUTCH +CRUX +CRY +CRYPT +CRYPTIC +CRYSTAL +CRYSTALLINE +CTHON +CUBE +CUDGEL +CUFF +CUIRASS +CULWEN'S +CUNNING +CURIO +CURLY +CURSED +CURSES +CURSING +CUSTODIAN +CUT-THROAT +CUTLASS +CYCLE +CYCLONE +DAC +DACIAN +DAGGER +DAMAGE +DAMAGED +DAMIEN +DAMNED +DANCER +DANGOON'S +DARING +DARK +DARKFEAR +DARKFORCE +DARKGLOW +DARKNESS +DARKONE +DARKSIGHT +DARKSOUL +DARKWING +DART +DAWN +DAYLIGHT +DAYTIME +DAZZLING +DE +DEAD +DEADLY +DEATH +DEATH'S +DEATHBIT +DEATHSPADE +DEATHWAND +DEBI +DEBRIS +DECAPITATOR +DECAYED +DECAYING +DECEPTION +DECKARD +DECOY +DECREPIFY +DEFEAT +DEFENDER +DEFENSE +DEFENSIVE +DEFIANCE +DEFILER +DEFLECTING +DEL +DELIRIUM +DELIVERANCE +DEMON +DEMON'S +DEMONHEAD +DEMONHIDE +DEMONHORN'S +DEMONS +DEMONWEB +DEN +DENSE +DESERT +DESIRE +DESPAIR +DESTROY +DESTROYER +DESTRUCTION +DETAIL +DEVAK +DEVIL +DEVILKIN +DEVIOUS +DEVOURER +DEXTERITY +DIABLO +DIABLO'S +DIADEM +DIAMOND +DIANE +DIGGLER +DIM +DIMENSIONAL +DIMOAK'S +DIRE +DIRK +DISCIPLE +DISCIPLINE +DISCIPLINES +DISEASE +DISFIGURED +DISGUISE +DISK +DISPATCH +DISUSED +DIVER +DIVINE +DIVO +DJINN +DO +DOCKS +DOCTOR +DODGE +DOG +DOL +DOLL +DOLLS +DOOM +DOOMBRINGER +DOOMSEER +DOOMSLINGER +DOOR +DOPPLEGANGER'S +DOUBLE +DOUBT +DRACUL'S +DRAGON +DRAGON'S +DRAGONHAND +DRAGONSCALE +DRAGONTOOTH +DRAIN +DRAINER +DRAINS +DRAKE'S +DRAKEFLAME +DREAD +DREAM +DRIED +DRIFTER +DRINKER +DROGNAN +DROOL +DROP +DROWNED +DRUID +DRULAN'S +DRUS +DRY +DUN +DUNE +DUNES +DUNG +DUNGEON +DURABILITY +DURANCE +DURATION +DURESS +DURGA +DURIEL +DURIEL'S +DURING +DUSK +DUSKDEEP +DUST +DUSTY +DWARF +DWELLER +DYING +DYNAMO +EACH +EADGILS +EAGLE +EAGLEEYE +EAGLEHORN +EAGLEWIND +EAR +EARS +EARTH +EARTHSHAKER +EASE +EATER +EATS +EBURINE +ECHOING +EDGE +EFREETI +EGG +EGGS +EGTHEOW +EL +ELATION +ELD +ELDER +ELDRITCH +ELEGANT +ELEMENTAL +ELEMENTS +ELEPHANT +ELEXA +ELGIFU +ELIXIR +ELLY +ELUDING +ELYSIAN +ELZIX +EMBALMED +EMBLEM +EMBOSSED +EMBRACE +EMERALD +EMILIO +EMPOWERING +EMPYRIAN +EMUND +ENCAMPMENT +ENCHANT +ENCHANTED +ENCHANTMENT +END +ENDLESSHAIL +ENDUGU +ENDURING +ENEMIES +ENEMY +ENERGIZING +ENERGY +ENFORCER +ENHANCED +ENIGMA +ENIRHS +ENLIGHTENED +ENLIGHTENMENT +ENNUI +ENSANGUINATOR +ENTRAPPING +ENTROPY +ENVY +EQUILIBRIUM +EQUIP +ERION'S +EROSION +ESCHUTA'S +ESEN'S +ESPANDON +ESPIRITU +ESSENCE +ETERNITY +ETH +ETHELRED +ETHEREAL +ETLICH +ETTIN +EUNUCH +EVADE +EVE +EVERLASTING +EVERY +EVIL +EVISCERATION +EVISCERATOR +EVO +EXCALIBUR +EXCELLENCE +EXCHANGE +EXECUTIONER +EXECUTIONER'S +EXILE +EXPERIENCE +EXPERT'S +EXPLODING +EXPLORE +EXPLOSION +EXPLOSIVE +EXTRA +EYE +EYEBACK +EYELESS +FACE +FACET +FADE +FAITH +FAITHFUL +FAL +FALCATA +FALCHION +FALCON +FALCONEYE +FALLEN +FALX +FAMILIAR +FAMINE +FANATIC +FANATICISM +FANCY +FANE +FANG +FANGED +FANGSKIN +FAR +FARA +FARREN +FASCIA +FASCINATING +FAST +FASTER +FASTEST +FATHOM +FAVOR +FAZEL +FEAST +FEATHERS +FEATURE +FECHMAR +FEEDER +FEEL +FELICITOUS +FELLOAK +FEND +FENDING +FER +FERAL +FEROCIOUS +FERVOR +FESTER +FESTERING +FETID +FETISH +FETISHES +FETLOCK +FIELD +FIEND +FIERCE +FIERY +FIGHTER +FIGURINE +FINAL +FIND +FINE +FINE-SPUN +FINERY +FINGER +FINISHING +FIONA +FIRE +FIREBOLTS +FIRELIZARD'S +FIRES +FIRESTORM +FIRESTORMS +FISHER +FISSURE +FIST +FISTS +FLAG +FLAIL +FLAMBERGE +FLAME +FLAMEBELLOW +FLAMEFINGER +FLAMESPIKE +FLAMING +FLAMME +FLANGE +FLANGED +FLANK +FLAVIE +FLAWED +FLAWLESS +FLAYED +FLAYER +FLAYERS +FLEE +FLEECE +FLEET +FLESH +FLESHBONE +FLESHRENDER +FLESHRIPPER +FLETCH +FLETCHER'S +FLIGHT +FLORIA +FLOWKRAD'S +FLUX +FLY +FLYING +FOCI +FOGGY +FOLCWALD +FOLIAGE +FOLLY +FOOL'S +FOOTHILLS +FOR +FORCEFUL +FORE-FATHERS +FOREST +FORGE +FORGOTTEN +FORK +FORKED +FORM +FORTIFICATION +FORTIFIED +FORTITUDE +FORTRESS +FORTUITOUS +FORTUNE +FOUL +FOULDOG +FOX +FRAME +FRANCISCA +FRANTIC +FREEDOM +FREEZE +FREEZES +FREEZING +FRENZY +FRIENDSHIP +FRIGID +FRIGIDITY +FRINGE +FRISIAN +FROM +FROST +FROSTBURN +FROSTWIND +FROTH +FROZEN +FROZENSTEIN +FUEGO +FULL +FULMINATING +FUNGAL +FUR +FURIOUS +FURNACE +FURS +FURY +FUSCINA +GA +GAEAN +GAGE +GAIA'S +GAILE +GAIN +GAINED +GALE +GAMMA +GARB +GARGANTUAN +GARGOYLE +GARGOYLE'S +GARMUND +GARNET +GAS +GATE +GAUDY +GAUNTLET +GAUNTLETS +GAVE +GAVEL +GAYA +GAZE +GEAR +GEGLASH +GELEB +GEM +GEMMED +GENERAL'S +GENERATOR +GERKE'S +GERONIMO'S +GESHEF +GET +GETTING +GHEED +GHEED'S +GHOLEIN +GHOST +GHOSTFLAME +GHOSTLY +GHOUL +GHOULHIDE +GIANT +GIBBET +GIDBINN +GILDED +GILLIAN'S +GIMMERSHRED +GINTHER'S +GIRDLE +GIRTH +GIVE +GLACIAL +GLACIER +GLADIATOR'S +GLADIUS +GLAIVE +GLEAMSCYTHE +GLIMMERING +GLIMMERSHRED +GLOAM +GLOBE +GLOOM +GLOOM'S +GLOOMBAT +GLORIOUS +GLORY +GLOVES +GLOWING +GLYPH +GNARL +GNARLED +GNASH +GNASHER +GNAT +GNOLL +GO +GOAD +GOBLIN +GODLY +GOES +GOLD +GOLDEN +GOLDSKIN +GOLDSTRIKE +GOLDWRAP +GOLEM +GOLEMLORD'S +GOO +GOOD +GORE +GOREBELLY +GOREFOOT +GOREROD +GORESHOVEL +GORGET +GORGETHROAT +GORGON +GORM +GOTHIC +GOUT +GRACE +GRAND +GRANDE +GRANDFATHER +GRANDMASTER'S +GRASP +GRASSY +GRAVE +GRAVENSPINE +GRAVEPALM +GRAVEROBBER'S +GRAY +GREAT +GREATER +GREATLY +GREAVES +GREED +GREEN +GREIZ +GREYFORM +GRIEF +GRIFFON +GRIFFON'S +GRIM +GRIM'S +GRIN +GRINDER +GRINDING +GRIP +GRISWOLD +GRISWOLD'S +GRIZZLY +GROTESQUE +GROUNDING +GROUNDS +GROWLER +GRUMBLE +GUARD +GUARDIAN +GUARDIAN'S +GUARDIANSHIP +GUARDING +GUARDS +GUIDED +GUILLAUME'S +GUISE +GUL +GULL +GULLETWOUND +GULZAR +GUT +GUTHLAF +GUTTER +GWINNI +GYMNASTIC +GYRE +HACK +HADES +HADESHORN +HADRIEL +HAEMOSU'S +HAG +HAILSTONE +HALABERD'S +HALBERD +HALBU +HALF +HALGAUNT +HALLOWED +HALLS +HALO +HAMMER +HAND +HANDS +HANNAH +HAPHET +HARBINGER +HARD +HARDY +HAREM +HARLEQUIN +HARMFUL +HARMONY +HARNESS +HARP +HARPOON +HARPOONIST'S +HARROGATH +HARVEST +HARVESTER +HAS +HASEEN +HASTE +HAT +HATCHET +HATE +HATRED +HAUBERK +HAVOC +HAWK +HAWKEYE +HAWKMAIL +HAZADE +HAZE +HAZY +HEAD +HEADDRESS +HEADGEAR +HEADPIECE +HEADS +HEADSTRIKER +HEAL +HEALED +HEALFDANE +HEALING +HEALS +HEALTH +HEART +HEARTY +HEATED +HEATER +HEATHER +HEATHOLAF +HEAVEN'S +HEAVENLY +HEAVENS +HEAVY +HEEL +HEIROPHANT +HEL +HELL +HELL'S +HELLACIOUS +HELLCAST +HELLCLAP +HELLFIRE +HELLFORGE +HELLMOUTH +HELLPLAGUE +HELLRACK +HELLSLAYER +HELLSPAWN +HELM +HELMET +HELMS +HELP +HENGEST +HEOROGAR +HEPHASTO +HERALD +HERALDIC +HERB +HEREMOD +HERMETIC +HEROES +HEW +HEXFIRE +HEXING +HIBERNAL +HIDDEN +HIDE +HIDING +HIEROPHANT +HIEROPHANT'S +HIGH +HIGHLAND +HIGHLANDS +HIGHLORD'S +HIGHTOWER'S +HILD +HILLS +HIRE +HIS +HIT +HNAEF +HOBNAILS +HOKU +HOLD +HOLDS +HOLE +HOLLIS +HOLLOW +HOLY +HOLYSHRINE +HOMING +HOMUNCULUS +HONE +HONOR +HONORABLE +HOOD +HOOK +HOOVES +HOPE +HORADRIC +HORADRIM +HORAZON'S +HORDE +HORIZON'S +HORN +HORNED +HORNS +HORROR +HORSE +HOTSPUR +HOWL +HOWLER +HOWLING +HOWLTUSK +HRATLI +HROTHGAR +HSARU'S +HSARUS' +HULK +HUMILITY +HUMONGOUS +HUNG +HUNGER +HUNGRY +HUNTER +HUNTER'S +HUNTRESS +HURLBAT +HURRICANE +HUSK +HUSOLDAL +HUT +HWANIN'S +HYDRA +HYDRASKULL +HYGELAC +HYMN +HYPERION +I +IANSANG'S +IANTHA +ICE +ICEBLINK +ICEBOLT +ICEFIST +ICEHAWK +ICHORSTING +ICICLE +ICON +ICY +IDENTIFY +IDIOT +IGNORE +ILLUSION +ILZAN +IMBECILE +IMMOLATING +IMMOLATION +IMMORTAL +IMMUNE +IMMUNITY +IMP +IMPALE +IMPALER +IMPALING +IMPECCABLE +IMPERIAL +IMPROVED +IN +INCINERATION +INCREASE +INCREASED +INDESTRUCTIBLE +INDIEGO'S +INDIGO +INERTIA +INFANTRY +INFECTIOUS +INFECTOR +INFERNAL +INFERNO +INFERNOSTRIDE +INFIDEL +INFIERNO +INFINITY +INFLAMMABILITY +INFORMATION +INIFUSS +INNER +INNOCENCE +INSERT +INSIDIOUS +INSIGHT +INSULATION +INTO +INVADER +INVENTORY +INVISIBILITY +INVISO +IO +IP +IRATHA'S +IRE +IRO +IRON +IRONSTONE +IRRESISTIBLE +ISENHART'S +ISLESTRIKE +ISMAIL +ISOLDE +IST +IT +ITCHIES +ITEM +ITEMS +ITH +ITHERA +ITONYA +IVORY +IZUAL +IZUAL'S +JAB +JABARI +JACINTH +JACK +JACK'S +JACKAL +JADE +JAGGED +JAH +JAIL +JALAL'S +JAMELLA +JANG +JAR +JARED'S +JARULF +JAVELIN +JAWBONE +JEALOUSY +JELANI +JEMALI +JERHYN +JESTER'S +JEWEL +JEWELER'S +JO +JOKER'S +JORDAN +JOURNAL +JOY +JOYFULNESS +JUDGEMENT +JUG +JUJUBE +JUNGLE +JUSTICE +KAA +KAELAN +KAELIM +KALEIDOSCOPE +KANG'S +KARMA +KASHYA +KASIM +KATAR +KEEN +KEEP +KEEPER +KEEPER'S +KELPIE +KENSHI'S +KEY +KHALEEL +KHALIM'S +KHAN +KICK +KICKING +KICKS +KILL +KILLER +KILLERS +KILLING +KINEMIL'S +KING +KING'S +KINGDOM +KINGS +KINGSLAYER +KIRA'S +KITE +KLAR +KLATU +KLAUDIA +KLISK +KNAVE'S +KNELL +KNIFE +KNIGHT +KNIGHT'S +KNOCKBACK +KNOT +KNOUT +KNOWLEDGE +KNUCKLE +KNUCKLES +KO +KORD +KORLIC +KRAKEN +KRINTIZ +KRIS +KUKO +KUNDRI +KURAST +KYANON +KYLE +KYOKO +LACERATOR +LACQUERED +LADEN +LADY +LAIR +LAKE +LAM +LAMENT +LAMPREY +LANCE +LANCER +LANCER'S +LANGER +LANTH +LAPIS +LARGE +LARRY +LARZUK +LARZUK'S +LASH +LAST +LASTING +LAVA +LAW +LAWBRINGER +LAWFUL +LAYING +LAYS +LAZARUS +LAZULI +LEACH +LEADCROW +LEAF +LEAP +LEAPER +LEARN +LEATHER +LEATHERS +LEAVER +LEAVING +LEECH +LEFT +LEG +LEGACY +LEGEND +LEGENDARY +LEHARAS +LEM +LENGTH +LENYMO +LEORIC +LESSON +LESTRON'S +LEVEL +LEVELS +LEVER +LEVIATHAN +LI +LIAZA +LIBERATOR +LICH +LIDLESS +LIEF +LIENE +LIFE +LIFECHOKE +LIGHT +LIGHTBRAND +LIGHTNING +LIGHTSABRE +LILAC +LILITH +LILLY +LIMB +LINKED +LION +LIONHEART +LISTER +LIZARD'S +LO +LOATH +LOCHABER +LOCK +LOCKED +LOCUST +LOCUSTS +LOG +LONG +LONGEST +LOOK +LOOM +LOOP +LOOSE +LORD +LORD'S +LORE +LORICATED +LOST +LOVE +LOW +LOWER +LOWERS +LOYALTY +LUCK +LUCKY +LUM +LUNA +LUNAR +LUNATA +LURKER +LUST +LUT +LYCANDER'S +LYCANTHROPY +LYSANDER +M'AVINA'S +MACE +MACHINE +MAD +MADAWC +MADNESS +MAEKO +MAELSTROM +MAFFER +MAGE +MAGEFIST +MAGEKILLER'S +MAGES +MAGEWRATH +MAGGOT +MAGGOTS +MAGI +MAGIC +MAGMA +MAGNUS +MAGNUS' +MAGUS +MAHALA +MAHIM-OAK +MAIDEN +MAIDEN'S +MAIL +MAIM +MAIMER +MAIMING +MAIN +MAJESTIC +MAJESTY +MAKER +MAL +MALAH +MALAH'S +MALICE +MALICIOUS +MALIGNANT +MALLET +MALUS +MAMMOTH +MAN +MANA +MANALD +MANCATCHER +MANE +MANG +MANGLER +MANIPULATOR +MANTLE +MAR +MARA'S +MARAUDER +MARK +MAROON +MARRED +MARROW +MARROWWALK +MARSH +MARSHAL'S +MARTEL +MARTIAL +MASK +MASSIVE +MASTER +MASTER'S +MASTERIES +MASTERPIECE +MASTERY +MATRIARCHAL +MATRON +MATRON'S +MAUL +MAULER +MAUSOLEUM +MAW +MAXIMUM +MCAULEY'S +MEASURE +MEAT +MEATSCRAPE +MECHANIC'S +MEDITATION +MEDUSA'S +MEGHAN +MELEE +MELODY +MEMBER +MEMORY +MENACE +MENTAL +MENTALIST'S +MENU +MEPHISTO +MEPHISTO'S +MERCHANTS +MERCILESS +MERCY +MERMAN'S +MESH +MESHIF +MESSERSCHMIDT'S +MET +METALGRID +METALITE'S +METEOR +METEORIC +MIGHT +MIGHTY +MILABREGA'S +MILITARY +MIND +MINIMUM +MINION +MINOR +MINOTAUR +MIRRORED +MISSHAPEN +MISSILE +MISSILES +MIST +MITHRIL +MITTS +MIZAN +MNEMONIC +MOE +MOJO +MOLD +MOLDY +MOLECH +MOLTEN +MONARCH +MONASTERY +MONK'S +MONSTER +MONSTERS +MONUMENTAL +MOON +MOONFALL +MOONREND +MOOR +MORDOC'S +MORE +MORN +MORNING +MORON +MORTAL +MOSER'S +MOSQUITO +MOUNT +MOVE +MUCH +MULE +MULTIPLE +MUMMIFIED +MUMMY +MUSCULAR +MUSIC +MUSTY +MYRMIDON +MYSTERY +MYTH +MYTHICAL +NADIR +NAGA +NAGAS +NAGELRING +NAILS +NAJ'S +NARPHET +NATALYA +NATALYA'S +NATURAL +NATURE'S +NEAR +NEBUCHADNEZZAR'S +NECKLACE +NECROMAGE +NECROMANCER +NECROMANCER'S +NECROSKELETON +NEEDLE +NEERAJ +NEF +NEGATION +NEGRA +NEST +NETHERCROW +NEUTRAL +NEWS +NEXT +NICKEL +NIGHT +NIGHTMARE +NIGHTSMOKE +NIGHTSUMMON +NIGHTTIME +NIGHTWING'S +NIHLATHAK +NIHLATHAK'S +NIRVANA +NO +NOBILITY +NOCK +NOKOZAN +NOOSE +NORD'S +NORMAL +NOSFERATU'S +NOT +NOVA +NOW +NOXIOUS +NULL +OAK +OASIS +OATH +OBEDIENCE +OBELISK +OBJECT +OBLIVION +OBSCENE +OBSESSION +OBSIDIAN +OCEAN +OCHER +OCULUS +ODIFEROUS +ODIUM +OF +OFFENSIVE +OGRE +OGUN'S +OHM +OIL +OLD +OLENA +OMNISCIENT +ON +ONDAL'S +ONE +ONE-HAND +ONELA +ONES +ONLY +OOZE +OPEN +OR +ORANGE +ORB +ORDER +ORDERS +ORIANA +ORIFICE +ORIGINAL +ORMUS +ORMUS' +ORNATE +ORPHAN'S +ORT +OSLAF +OTHERS +OUTER +OVER +OVERSEER +OX +PACING +PAD +PAGAN'S +PAIGE +PAIN +PALACE +PALADIN +PALE +PALO +PANDEMONIUM +PARADOX +PARAGON +PARKERSOR'S +PARRY +PARTIZAN +PARTY +PASSAGE +PASSION +PASSIVE +PAST +PATIENCE +PATRIARCH +PATTER +PAVISE +PAWS +PEACE +PEARL +PEASANT +PELLET +PELT +PELTA +PENETRATE +PENITENCE +PER +PERCENT +PERFECT +PERFECTION +PERFORMANCE +PERIL +PERMANENTLY +PERNICIOUS +PERPETUAL +PESTILENCE +PESTILENT +PET +PETRIFIED +PETS +PHAET +PHASE +PHOENIX +PHYSICAL +PICK +PICKET +PIERCE +PIERCING +PIERRE +PIETY +PIKE +PILE +PILFERING +PILLAR +PILUM +PINCERS +PINDLESKIN +PIT +PITBLOOD +PITSPAWN +PLAGUE +PLAINS +PLATE +PLATEAU +PLATED +PLATINUM +PLAYER +PLEDGE +PLUCKEYE +PLUS +POIGNARD +POINT +POINTS +POISON +POLE +POLEARM +POLEAXE +POLISHED +POMPEII'S +POPPY +PORTAL +PORTRAIT +POSSESSED +POTION +POWER +POWERED +POX +PRAISE +PRATHAM +PRAYER +PRECISION +PRECOCIOUS +PRESERVED +PRESERVER'S +PREVENT +PREVIOUS +PRICES +PRIDE +PRIEST'S +PRINCE +PRINCIPLE +PRISMATIC +PRISON +PRIVATE +PROCEED +PROD +PROJECTOR +PROLOGUE +PROMISE +PROPOGATION +PROSPERITY +PROTECTION +PROTECTOR +PROWESS +PROWLER +PRUDENCE +PSYCHIC +PUKE +PUL +PULSE +PUNCTURING +PUNISHING +PUNISHMENT +PUPIL +PURE +PURITY +PUS +PUTRID +PUZZLER +PYRE +QUAL-KEHK +QUAL-KHEK'S +QUALITY +QUANTITY +QUARREL +QUARTERSTAFF +QUE-HEGAN'S +QUEEN +QUENCHING +QUESTION +QUHAB +QUICK +QUICKENING +QUICKNESS +QUILL +QUILTED +QUIXOTIC +QUOTA +RABBIT +RABIES +RACK +RADAMENT +RADAMENT'S +RADIANCE +RADIUS +RAGE +RAGING +RAGNAR +RAIDEN'S +RAIDER +RAIMENT +RAIN +RAINBOW +RAISE +RAISES +RAISSA +RAKANISHU +RAKESCAR +RAL +RALDIN +RANCID +RANGER +RANK +RAPTOR +RASHA +RASHA'A +RASHA'S +RAT +RAT'S +RATCHEST +RATE +RATING +RATMAN +RATMEN +RATTLE +RATTLECAGE +RAVEN +RAVEN'S +RAVENLORE +RAVENS +RAZAN +RAZOR +RAZOR'S +RAZORS +RAZORSWITCH +RAZORTAIL +RAZORTINE +READ +READINESS +REALGAR +REANIMATE +REAPER +REAPER'S +REASON +REAVER +REBUKE +RECHARGE +RECLUSIVE +RECOVERED +RECOVERY +RECTIFIER +RED +REDEEM +REDEEMER +REDEMPTION +REDUCED +REDUCES +REFILLING +REFLECTION +REFLEX +REFUGE +REGALIA +REGENERATE +REGENERATION +REGROWTH +REIGN +REINFORCED +REJUVENATION +RELIC +RELICS +RELIGION +RELIQUARY +REMAINING +REMEDY +REMORSE +REND +RENDER +REPAIR +REPEATING +REPLENISH +REPLENISHES +REPLENISHING +REPULSIVE +REQUIRED +REQUIREMENTS +RESCUE +RESILIENT +RESIST +RESISTANCE +RESISTANCES +RESISTANT +RESONANT +REST +RESTORATION +RESTORE +RESURRECTION +RETURN +RETURNED +REVENGE +REVIVE +REVIVED +REVIVIFICATION +REWARD +RHADGE +RHYME +RHYTHM +RIBCRACKER +RIDER +RIFT +RIFTLASH +RIFTSLASH +RIFTWING +RIFTWRAITH +RIG +RIGHT +RIGHTEOUS +RING +RINGS +RIPHOOK +RIPPER +RIPRAP +RIPSAW +RISING +RITE +RIVER +RIXOT'S +ROBE +ROBES +ROBIN'S +ROBINEYE +ROBUST +ROCK +ROCKFLEECE +ROCKHEW +ROCKS +ROCKSTOPPER +ROCKY +ROD +ROGUE +ROGUE'S +RONDACHE +RONDEL +ROSE +ROT +ROTTING +ROUND +ROYAL +ROZENE +RUBY +RUDE +RUGGED +RUINED +RULE +RUN/WALK +RUNE +RUNES +RUNIC +RUSSET +RUST +RUSTHANDLE +RUSTY +RYANN +SABER +SABOT +SABRE +SACRED +SACRIFICE +SAFETY +SAGE +SAINTLY +SALAMANDER +SALLET +SALVATION +SAMUEL'S +SAMURAI +SANCTUARY +SAND +SANDER'S +SANDS +SANDSTORM +SANGUINARY +SANGUINE +SANKEKUR'S +SAPPHIRE +SARACEN'S +SARCOPHAGUS +SARINA +SASH +SAVAGE +SAW +SAZABI'S +SCALE +SCALES +SCALP +SCALPEL +SCALPER +SCARAB +SCARABSHELL +SCARLET +SCAVENGER +SCEPTER +SCHAEFER'S +SCIMITAR +SCINTILLATING +SCIROCCO +SCISSORS +SCORCH +SCORCHED +SCOURGE +SCOUT +SCRAPER +SCRATCH +SCREAMING +SCROLL +SCROLLS +SCUTUM +SCYLD +SCYTHE +SEAL +SEARCH +SEARING +SEC +SECOND +SECONDS +SECRET +SEE +SEETHE +SEIS +SELF-REPAIR +SELL +SENSEI'S +SENTINEL +SENTRY +SEPTIC +SERAPH +SERAPH'S +SERENDIPITY +SERPENT +SERPENT'S +SERPENTSKIN +SERRATED +SEVEN +SEVENTH +SEVER +SEWER +SEWERS +SEXTON +SHADE +SHADOW +SHADOWFANG +SHADOWS +SHAEL +SHAFT +SHAFTSTOP +SHAKABRA'S +SHAKAKU +SHAKO +SHAMAN +SHAMAN'S +SHAMSHIR +SHANK +SHAPE +SHAPE-SHIFTING +SHARD +SHARKSKIN +SHARKTOOTH +SHARP +SHARPTOOTH +SHARYN +SHATTER +SHEATH +SHELL +SHELTER +SHENK +SHIELD +SHIELDS +SHIFTER +SHIFTING +SHIKHA +SHILLELAGH +SHIMMERING +SHIVER +SHIVERING +SHOCK +SHOCKING +SHOGUKUSHA'S +SHOOTS +SHORT +SHOT +SHOTS +SHOUT +SHOW +SHREWD +SHRINE +SHROUD +SHRUB +SICKLE +SIEGE +SIGEMUND +SIGGARD'S +SIGHT +SIGHTLESS +SIGIL +SIGN +SIGON'S +SIGURD +SILENCE +SILKS +SILKWEAVE +SILVER +SILVER-EDGED +SIMBILAN +SIMPLICITY +SIN +SINBLADE +SINEW +SINGING +SIPHON +SIREN'S +SIX +SIZE +SKELETAL +SKELETON +SKEWER +SKILL +SKILLFUL +SKILLS +SKIN +SKULL +SKULLDER'S +SKULLPILE +SKULLS +SKULLTRED +SKY +SKYSTRIKE +SLAIN +SLASHER +SLASHERS +SLAUGHTER +SLAYER +SLAYER'S +SLAYING +SLIGHTLY +SLIME +SLINGER +SLIPPERS +SLOW +SLOWER +SLOWEST +SLOWS +SLUDGE +SMALL +SMASH +SMASHER +SMITE +SMITH +SMITING +SMOKE +SMOKED +SMOKING +SMOLDERING +SNAKE +SNAKE'S +SNAKECORD +SNAKES +SNAPCHIP +SNARE +SNARL +SNIPER +SNOT +SNOW +SNOWCLASH +SNOWY +SOCKET +SOCKETED +SOCKETS +SOL +SOLAR +SOLD +SOLDIER +SOLDIER'S +SOLDIERS +SONG +SONG'S +SORCERESS +SORCERY +SORROW +SOUL +SOULFEAST +SOULFLAY +SOULLESS +SOULS +SOULSTONE +SOUND +SOUNDING +SOURCE +SPARKFIST +SPARKING +SPARKLING +SPARROWEYE +SPAWN +SPAWNER +SPEAK +SPEAR +SPEARMAIDEN'S +SPEARWOMAN +SPECTER +SPECTRAL +SPEED +SPELL +SPELLSTEEL +SPETUM +SPHERE +SPICULUM +SPIDER +SPIDERWEB +SPIKE +SPIKED +SPIKEFIST +SPIKES +SPINE +SPINERIPPER +SPINNER +SPIRAL +SPIRE +SPIRED +SPIRIT +SPIRITUAL +SPITEFUL +SPITS +SPITTER +SPLAY +SPLEEN +SPLENDOR +SPLINT +SPLITTER +SPOT +SPRINKLER +SPROCKET +SPUR +SQUID +SQUIRE'S +SSZARK +STABILITY +STACK +STAFF +STAG +STAIR +STAIRS +STAKE +STALAGMITE +STALKER +STALWART +STAMINA +STAND +STANDARD +STAR +STARE +STARLIGHT +STARS +STASH +STAT +STATIC +STATURE +STAY +STEAL +STEALING +STEALS +STEALSKULL +STEALTH +STEEL +STEELCLASH +STEELDRIVER +STEELGOAD +STEELREND +STELLAR +STEPPES +STICK +STILETTO +STILL +STING +STINGER +STINGING +STOICISM +STOLEN +STONE +STONERAGE +STONERAVEN +STONES +STONY +STOP +STORM +STORMCHASER +STORMEYE +STORMGUILD +STORMLASH +STORMRIDER +STORMS +STORMSHIELD +STORMSPIKE +STORMSPIRE +STORMSTRIKE +STORMTREE +STORMWILLOW +STOUT +STOUTNAIL +STRAFE +STRANGE +STRANGLER +STRANGLING +STRAP +STREAM +STRENGTH +STRENGTHENING +STRIKE +STRIKER +STRING +STRONG +STRONGER +STUDDED +STUMP +STUN +STURDY +STYGIAN +SUCCUBUS +SUCKER +SUFFERING +SUICIDE +SUIT +SUMMIT +SUMMON +SUMMONER +SUMMONER'S +SUMMONING +SUN +SUNDAN +SUNDER +SUNFIRE +SUNLIGHT +SUPER +SUPERIOR +SUPERSTITION +SUR +SURESHRILL +SUSTENANCE +SUWAYYAH +SWAMP +SWAMPY +SWARM +SWASHBUCKLER +SWEETNESS +SWIFT +SWIFTNESS +SWING +SWINGING +SWIRLING +SWORD +SWORDBACK +SWORDGUARD +SWORDS +SYMBOL +SZ +TABAR +TABOO +TAEBAEK'S +TAIL +TAINTBREEDER +TAINTED +TAKE +TAKEN +TAKES +TAL +TALBERD'S +TALIC +TALISMAN +TALK +TALON +TALONS +TAMOE +TAN +TANCRED'S +TANG'S +TANGERINE +TANNR +TAP +TARGE +TARGET +TARGET'S +TARNHELM +TAUNT +TEACHING +TEAR +TEARHAUNCH +TEARS +TEETH +TELASH +TELEKINESIS +TELEPORT +TELEPORTATION +TELLING +TEMPER +TEMPEST +TEMPLAR +TEMPLAR'S +TEMPLE +TEMPLES +TEMPTATION +TEMPTRESS +TENDERIZER +TENET +TERRA'S +TERRENE +TERROR +TERROR'S +THADAR +THARR +THAT +THAW +THAWING +THE +THEODORIC +THIEF +THIEVES +THIN +THIRST +THIS +THORN +THORNED +THORNS +THOUGHT +THRASHER +THREE +THRESH +THRESHER +THRONE +THROUGH +THROW +THROWABLE +THROWING +THUL +THUNDER +THUNDERGOD'S +THUNDERSTROKE +TIAMAT'S +TIARA +TIGER +TIGULATED +TIME +TIMELESS +TIMES +TIN +TINE +TIR +TIRELESS +TITAN +TITAN'S +TO +TODESFAELLE +TOE +TOKEN +TOLL +TOMAHAWK +TOMB +TOMBALE +TOMBS +TOME +TONGUE +TOOLS +TOORC +TOOTH +TOOTHROW +TOP +TOPAZ +TORC +TORCH +TORKEL +TORMENT +TORMENTOR +TORNADO +TORQUER +TOSTIG +TOTE +TOTEM +TOTEMIC +TOUCH +TOUGH +TOWER +TOWN +TOWNPORTAL +TOWNSFOLK +TOXIC +TRACK +TRADITION +TRAIL +TRAINER'S +TRAMPLE +TRANG-OUL'S +TRANSCENDENCE +TRANSFORM +TRANSMUTE +TRANSPORTATION +TRAP +TRAPPED +TRAPS +TRAVEL +TRAVELER +TRAVINCAL +TREACHERY +TREAD +TREADS +TREE +TREEHEAD +TREENTSTER +TREK +TRELLISED +TRIAD'S +TRIBAL +TRIBUTE +TRICKS +TRICKSTER'S +TRIDENT +TRISTRAM +TRIUMPHANT +TROLL +TROPHY +TRUE +TRUNCHEON +TRUST +TRUTH +TRYNEUS +TULWAR +TUNDRA +TUNNELS +TURN +TURQUOISE +TUSK +TWILIGHT +TWIN +TWISTED +TWISTER +TWITCHTHROE +TWO-HAND +TWO-HANDED +TYLENA +TYRAEL +TYRAEL'S +TYRANT +TYREAL +TYREAL'S +ULF +ULRIC +UM +UMBRAL +UME'S +UNBENDING +UNCLEAN +UNDEAD +UNDEFILED +UNDER +UNDERGROUND +UNEARTHED +UNEARTHLY +UNFERTH +UNHOLY +UNICORN +UNIDENTIFIED +UNLEASHED +UNRAVELER +UNRAVELLER +UNSUMMON +UP +UPPER +URDAR +URGENT +URN +USE +UTHER +VALHALLA +VALKYRIE +VALKYRIE'S +VALLEY +VALOR +VAMBRACES +VAMPIRE +VAMPIREBONE +VAMPIREFANG +VANJI +VARAYA +VASE +VAUGHT +VEIL +VENDOR +VENGEANCE +VENGENCE +VENOM +VENOMOUS +VENTAR +VERDUNGO'S +VERMILION +VERSUS +VERY +VESTIGE +VESTMENTS +VETERAN'S +VEX +VICAR +VICIOUS +VICTOR +VICTORIOUS +VICTORY +VIDALA'S +VIGIL +VIGOR +VIGOROUS +VIKHYAT +VILE +VILEHAND +VILENESS +VILLAGE +VILLAGER +VINE +VINES +VINVEAR +VIPER +VIPERFORK +VIPERMAGI +VIRGIN +VIRIDIAN +VIRILITY +VIRTUE +VISAGE +VISALA +VISCERATUANT +VISION +VISIONARY +VISOR +VITA +VITAL +VITALITY +VIVID +VIZIER +VIZJEREI +VOICE +VOID +VOIDBRINGER +VOLCANIC +VOLCANO +VOMIR +VORTEX +VOULGE +VS +VULPINE +VULTURE +WACKER +WAHEED +WAIL +WAILING +WAKE +WALK/RUN +WALKING +WALKS +WALL +WALLACE'S +WALRUS +WAND +WANDERER +WANDERER'S +WANG +WAR +WARCRIES +WARCRY +WARD +WARDEN +WARDEN'S +WARDER +WARDING +WARHOUND +WARLORD'S +WARMING +WARMTH +WARP +WARPED +WARPSPEAR +WARRIOR +WARRIOR'S +WARRIV +WARRIV'S +WARSHRIKE +WASTE +WATCH +WATCHER +WATCHTOWER +WATER +WATERWALK +WAVE +WAY +WAYPOINT +WEAKEN +WEALTH +WEAPON +WEAPONS +WEAVER +WEB +WEDDING +WEDER +WEIRD +WELL +WENDY +WEREBEAR +WEREWOLF +WHALE +WHEN +WHIRLWIND +WHISPER +WHITE +WHITSTAN'S +WHORL +WICKED +WIDOW +WIDOWMAKER +WIGHT +WIGLAF +WILD +WILHELM'S +WILL +WILLHELM'S +WIND +WINDFORCE +WINDHAMMER +WINDING +WINDSTRIKE +WING +WINGED +WINGS +WINTER +WIRE +WIRED +WIRT'S +WISDOM +WISH +WISP +WITCH +WITCH-HUNTER'S +WITCHWILD +WITH +WITHERSTRING +WITHIN +WITHOUT +WIZARDRY +WIZARDSPIKE +WIZENDRAW +WOE +WOESTAVE +WOLF +WOLFHOWL +WOLFRIDER +WOLVERINE +WOLVES +WONDER +WOOD +WOODEN +WOODFIST +WORLDSTONE +WORM +WORMSKULL +WORTH +WOUND +WOUNDS +WRACK +WRAITH +WRAITHFANG +WRAP +WRAPPINGS +WRAPS +WRATH +WRETCHED +WRIST +WULF +WULFGAR +WULFSTAN +WYAND +WYRM +WYRM'S +WYRMHIDE +WYVERN'S +XENOS +YAGGAI +YARDS +YARI +YASHIED +YATIRAJ +YELLING +YETI +YEW +YOLK +YOU +YOUNG +YOUR +YOUTH +YRMENLAF +ZAKARUM +ZAKARUM'S +ZAKARUMITE +ZANARHI +ZEAL +ZEALOT +ZEPHYR +ZIRCON +ZOD +ZOMBIE +ZWEIHANDER \ No newline at end of file diff --git a/environment.yml b/environment.yml index 19cbf2c..ea00011 100644 --- a/environment.yml +++ b/environment.yml @@ -1,8 +1,13 @@ name: botty +channels: + - conda-forge dependencies: - python=3.9 - pywin32 - pip + - leptonica + - tesseract + - pkg-config - pip: - pyinstaller - opencv-python @@ -23,3 +28,5 @@ dependencies: - cryptography - pillow - discord.py + - parse + - tesserocr diff --git a/src/item/item_cropper.py b/src/item/item_cropper.py index 8d05c47..babc107 100644 --- a/src/item/item_cropper.py +++ b/src/item/item_cropper.py @@ -1,10 +1,10 @@ import cv2 import numpy as np -from config import Config -from utils.misc import color_filter, erode_to_black from dataclasses import dataclass import time +from utils.misc import color_filter, erode_to_black +from config import Config # TODO: With OCR we can then add a "text" field to this class @dataclass @@ -18,22 +18,22 @@ class ItemCropper: self._config = Config() self._gaus_filter = (19, 1) - self._expected_height_range = [int(round(num, 0)) for num in [x / 1.5 for x in [14, 40]]] - self._expected_width_range = [int(round(num, 0)) for num in [x / 1.5 for x in [60, 1280]]] + self._expected_height_range = [round(num) for num in [x / 1.5 for x in [14, 40]]] + self._expected_width_range = [round(num) for num in [x / 1.5 for x in [60, 1280]]] self._hud_mask = cv2.imread(f"assets/hud_mask.png", cv2.IMREAD_GRAYSCALE) self._hud_mask = cv2.threshold(self._hud_mask, 1, 255, cv2.THRESH_BINARY)[1] self._item_colors = ['white', 'gray', 'blue', 'green', 'yellow', 'gold', 'orange'] - def clean_img(self, inp_img: np.ndarray) -> np.ndarray: + def clean_img(self, inp_img: np.ndarray, black_thresh: int = 14) -> np.ndarray: img = inp_img[:, :, :] if img.shape[0] == self._hud_mask.shape[0] and img.shape[1] == self._hud_mask.shape[1]: img = cv2.bitwise_and(img, img, mask=self._hud_mask) # In order to not filter out highlighted items, change their color to black highlight_mask = color_filter(img, self._config.colors["item_highlight"])[0] img[highlight_mask > 0] = (0, 0, 0) - img = erode_to_black(img) + img = erode_to_black(img, black_thresh) return img def crop(self, inp_img: np.ndarray, padding_y: int = 5) -> list[ItemText]: @@ -50,7 +50,7 @@ class ItemCropper: blured_img = np.clip(cv2.GaussianBlur(filtered_img_gray, self._gaus_filter, cv2.BORDER_DEFAULT), 0, 255) contours = cv2.findContours(blured_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len(contours) == 2 else contours[1] - for count, cntr in enumerate(contours): + for cntr in contours: x, y, w, h = cv2.boundingRect(cntr) expected_height = 1 if (self._expected_height_range[0] < h < self._expected_height_range[1]) else 0 # increase height a bit to make sure we have the full item name in the cluster @@ -72,9 +72,9 @@ class ItemCropper: max_idx = color_averages.index(max(color_averages)) if key == self._item_colors[max_idx]: item_clusters.append(ItemText( - color_key=self._item_colors[max_idx], - roi=[x, y, w, h], - data=cropped_item + color_key = key, + roi = [x, y, w, h], + data = cropped_item )) debug_str += f" | cluster: {time.time() - start}" # print(debug_str) @@ -87,8 +87,8 @@ if __name__ == "__main__": from screen import Screen keyboard.add_hotkey('f12', lambda: os._exit(1)) - cropper = ItemCropper() screen = Screen() + cropper = ItemCropper() while 1: img = screen.grab().copy() diff --git a/src/item/item_finder.py b/src/item/item_finder.py index 3d59899..7f604b7 100644 --- a/src/item/item_finder.py +++ b/src/item/item_finder.py @@ -5,6 +5,7 @@ import time import os from dataclasses import dataclass import math + from config import Config from utils.misc import color_filter, cut_roi from item import ItemCropper diff --git a/src/ocr.py b/src/ocr.py new file mode 100644 index 0000000..329f40c --- /dev/null +++ b/src/ocr.py @@ -0,0 +1,290 @@ +from fileinput import close +from tesserocr import PyTessBaseAPI, PSM, OEM +import numpy as np +import cv2 +import re +import csv +import difflib +from utils.misc import erode_to_black + +from logger import Logger +from typing import List, Union +from dataclasses import dataclass + +@dataclass +class OcrResult: + text: str = None + original_text: str = None + word_confidences: list = None + mean_confidence: float = None + # these are kept to help train OCR + original_img: np.ndarray = None + processed_img: np.ndarray = None + def __getitem__(self, key): + return super().__getattribute__(key) + +class Ocr: + def __init__(self): + self._I_1 = re.compile(r"(?<=[%I0-9\-+])I|I(?=[%I0-9\-+])") + self._II_U = re.compile(r"(?<=[A-Z])II|II(?=[A-Z])|1?=[a-z]") + self._One_I = re.compile(r"(?<=[A-Z])1|1(?=[A-Z])|1?=[a-z]") + self._OneOne_U = re.compile(r"(?<=[A-Z])11|11(?=[A-Z])|1?=[a-z]") + with open('assets/tessdata/ocr_errors.csv') as file: + self._ocr_errors = dict(csv.reader(file, skipinitialspace = False, delimiter = ',', quoting = csv.QUOTE_NONE)) + + """ + OCR input processing functions: + """ + def _crop_pad(self, image: np.ndarray = None): + # crop + image = image[4: image.shape[0]-4, 5: image.shape[1]-5] + # re-pad + image = np.pad(image, pad_width=[(4, 4),(4, 4),(0, 0)], mode='constant') + return image + + """ + OCR functions: + """ + + def _img_to_bytes(self, image: np.ndarray, colorspace: str = 'BGR'): + # Sets an OpenCV-style image for recognition: https://github.com/sirfz/tesserocr/issues/198 + bytes_per_pixel = image.shape[2] if len(image.shape) == 3 else 1 + height, width = image.shape[:2] + bytes_per_line = bytes_per_pixel * width + + if bytes_per_pixel != 1 and colorspace != 'RGB': + # non-RGB color image -> convert to RGB + image = cv2.cvtColor(image, getattr(cv2, f'COLOR_{colorspace}2RGB')) + elif bytes_per_pixel == 1 and image.dtype == bool: + # binary image -> convert to bitstream + image = np.packbits(image, axis=1) + bytes_per_line = image.shape[1] + width = bytes_per_line * 8 + bytes_per_pixel = 0 + # else image already RGB or grayscale + + return image.tobytes(), width, height, bytes_per_pixel, bytes_per_line + + def image_to_text(self, + images: Union[np.ndarray, List[np.ndarray]], + model: str = "engd2r_inv_th", + psm: int = 3, + word_list: str = "all_strings.txt", + scale: float = 1.0, + crop_pad: bool = True, + erode: bool = True, + invert: bool = True, + threshold: int = 25, + digits_only: bool = False, + fix_regexps: bool = True, + check_known_errors: bool = True, + check_wordlist: bool = True, + word_match_threshold: float = 0.9 + ) -> list[str]: + """ + Uses Tesseract to read image(s) + :param images (required): image or list of images to read in OpenCV format. + Use a list of images rather than looping over single images where possible for best performance. + :param model: OCR language model basename to use (in assets/tessdata folder) + :param psm: Tesseract PSM to use. 7=single uniform text line, 6=single block of text, 3=auto without orientation. + See https://www.pyimagesearch.com/2021/11/15/tesseract-page-segmentation-modes-psms-explained-how-to-improve-your-ocr-accuracy/ + :param word_list: predefined wordlist to use. Tesseract will use these to help with recognition + :param scale: scales input image, sometimes necessary for smaller text (but doesn't always improve accuracy). Engd2r_inv_th trained on ~1.6x scaled assets. + :param crop_pad: crop the outer part and then re-pad image. Intended for item drops. + :param erode: use erosion function to erode image to black borders (i.e. for item drops) + :param invert: invert and threshold the input image(s) + :param threshold: apply threshold to image (ex. 25 would threshold around V=25). Set to 0 to not threshold image. + :param digits_only: only look for digits + :param fix_regexps: use regex for various cases of common errors (I <-> 1, etc.) + :param check_known_errors: check for predefined common errors and replace + :param check_wordlist: check dictionary of words and match closest match if proximity is greater than match_threshold + :param word_match_threshold: (see check_wordlist) + :return: Returns an OcrResult object + """ + + if type(images) == np.ndarray: + images = [images] + results = [] + + with PyTessBaseAPI(psm=psm, oem=OEM.LSTM_ONLY, path=f"assets/tessdata", lang=model ) as api: + api.ReadConfigFile("assets/tessdata/ocr_config.txt") + if word_list: + api.SetVariable("user_words_file", word_list) + #api.SetSourceResolution(72 * scale) + for image in images: + processed_img = image + if scale: + processed_img = cv2.resize(processed_img, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST) + if crop_pad: + processed_img = self._crop_pad(processed_img) + if erode: + processed_img = erode_to_black(processed_img) + image_is_binary = (image.shape[2] if len(image.shape) == 3 else 1) == 1 and image.dtype == bool + if image_is_binary: + if threshold: + processed_img = cv2.cvtColor(processed_img, cv2.COLOR_BGR2GRAY) + processed_img = cv2.threshold(processed_img, threshold, 255, cv2.THRESH_BINARY)[1] + if invert: + if threshold or image_is_binary: + processed_img = cv2.bitwise_not(processed_img) + else: + processed_img = ~processed_img + api.SetImageBytes(*self._img_to_bytes(processed_img)) + if digits_only: + api.SetVariable("tessedit_char_blacklist", ".,!?@#$%&*()<>_-+=/:;'\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") + api.SetVariable("tessedit_char_whitelist", "0123456789") + original_text = api.GetUTF8Text() + text = original_text + # replace newlines if image is a single line + if psm in (7, 8, 13): + text = text.replace('\n', '') + word_confidences = api.AllWordConfidences() + if fix_regexps: + text = self._fix_regexps(text) + if check_known_errors: + text = self._check_known_errors(text) + if check_wordlist and any([x <= 88 for x in word_confidences]): + text = self._check_wordlist(text, word_list, word_confidences, word_match_threshold) + results.append(OcrResult( + original_text = original_text, + text = text, + word_confidences = word_confidences, + mean_confidence = api.MeanTextConf(), + original_img = image, + processed_img = processed_img + )) + return results + + """ + OCR output processing functions: + """ + def _check_known_errors(self, text): + for key, value in self._ocr_errors.items(): + if key in text: + text = text.replace(key, value) + return text + + def _check_wordlist(self, text: str = None, word_list: str = None, confidences: list = [], match_threshold: float = 0.9) -> str: + with open(f'assets/tessdata/word_lists/{word_list}') as file: + word_list = [line.rstrip() for line in file] + + word_count=0 + new_string="" + text = text.replace('\n',' NEWLINEHERE ') + for word in text.split(' '): + word = word.strip() + if word and word != "NEWLINEHERE": + try: + if confidences[word_count] <= 88: + if (word not in word_list) and (re.sub(r"[^a-zA-Z0-9]", "", word) not in word_list): + closest_match = difflib.get_close_matches(word, word_list, cutoff=match_threshold) + if closest_match and closest_match != word: + new_string += f"{closest_match[0]} " + Logger.debug(f"check_wordlist: Replacing {word} ({confidences[word_count]}%) with {closest_match[0]}, score=") + else: + new_string += f"{word} " + else: + new_string += f"{word} " + else: + new_string += f"{word} " + word_count += 1 + except IndexError: + # bizarre word_count index exceeded sometimes... can't reproduce and words otherwise seem to match up + Logger.error(f"check_wordlist: IndexError for word: {word}, index: {word_count}, text: {text}") + return text + except: + Logger.error(f"check_wordlist: Unknown error for word: {word}, index: {word_count}, text: {text}") + return text + elif word == "NEWLINEHERE": + new_string += "\n" + return new_string.strip() + + def _fix_regexps(self, ocr_output: str, repeat_count: int = 0) -> str: + # case: two 1's within a string; e.g., "SIIPER MANA POTION" + try: + text = self._II_U.sub('U', ocr_output) + except: + Logger.error(f"Error _II_ -> _U_ on {ocr_output}") + text = ocr_output + # case: two 1's within a string; e.g., "S11PER MANA POTION" + try: + text = self._OneOne_U.sub('U', text) + except: + Logger.error(f"Error _11_ -> _U_ on {ocr_output}") + # case: an I within a number or by a sign; e.g., "+32I to mana attack rating" + try: + text = self._I_1.sub('1', text) + except: + Logger.error(f"Error I -> 1 on {ocr_output}") + # case: a 1 within a string; e.g., "W1RT'S LEG" + try: + text = self._One_I.sub('I', text) + except: + Logger.error(f"Error 1 -> I on {ocr_output}") + + # case: a solitary I; e.g., " I TO 5 DEFENSE" + cnt=0 + while True: + cnt += 1 + if cnt >30: + Logger.error(f"Error ' I ' -> ' 1 ' on {ocr_output}") + break + if " I " in text: + text = text.replace(" I ", " 1 ") + continue + elif ' I\n' in text: + text = text.replace(' I\n', ' 1\n') + continue + elif '\nI ' in text: + text = text.replace('\nI ', '\n1 ') + continue + break + + # case: consecutive I's; e.g., "DEFENSE: II" + repeat=False + cnt=0 + while "II" in text: + cnt += 1 + if cnt >30: + Logger.error(f"Error 4 on {ocr_output}") + break + text = text.replace("II", "11") + repeat=True + repeat_count += 1 + if repeat and repeat_count < 10: + self._fix_regexps(text) + + return text + +if __name__ == "__main__": + import os + import keyboard + keyboard.add_hotkey('f12', lambda: os._exit(1)) + keyboard.wait("f11") + from utils.misc import cut_roi + from config import Config + + from screen import Screen + screen = Screen() + ocr = Ocr() + img = screen.grab() + # img = cut_roi(img, Config.ui_roi["char_selection_top"]) + + Logger.debug("OCR result:") + ocr_result = ocr.image_to_text( + images = img, + model = "engd2r_ui", + psm = 3, + word_list = "all_strings.txt", + scale = 1.0, + crop_pad = False, + erode = False, + invert = False, + threshold = 0, + digits_only = False, + fix_regexps = False, + check_known_errors = False, + check_wordlist = False, + word_match_threshold = 0.9 + )[0] + Logger.debug(ocr_result.text) \ No newline at end of file diff --git a/src/utils/item_extractor.py b/src/utils/item_extractor.py index b0cdb12..ed983f2 100644 --- a/src/utils/item_extractor.py +++ b/src/utils/item_extractor.py @@ -5,11 +5,10 @@ import argparse import os import cv2 import numpy as np -from utils.misc import color_filter + from item.item_cropper import ItemCropper import time - if __name__ == "__main__": parser = argparse.ArgumentParser(description="Script to autocrop items.") parser.add_argument("--file_path", type=str, help="Path to screenshots e.g. C:/data") @@ -18,7 +17,6 @@ if __name__ == "__main__": args.file_path = "C:\\Users\\aliig\\Desktop\\bot\\botty-gleed-ocr\\input_images" gen_truth = 1 - item_cropper = ItemCropper() for filename in os.listdir(args.file_path):