Page 1 of 5

Tome Library

Posted: Sun Sep 23, 2012 9:33 pm
by Ixnatifual
INTRODUCTION
So I was contemplating placing one of those stat-boosting tomes from Grimrock in one of my secret areas. To my dismay I discovered there were only 3 of them! So I figured it might be a good idea to add a few more, and found out how to do so. I was a bit undecided on what tome I actually wanted, so I figured "what if I just make a ton of tomes and pick one at random?" So I ended up making 25 extra tomes. And then I figured other modders might be interested in a bigger tome selection without all the hassle of making one. The result is the Tome Library!

TOME LIBRARY OVERVIEW
Below is an overview of the various tomes provided by this library. (The Tome of Health, Tome of Fire and Tome of Infinite Wisdom are also included in the list)

Fighter Tomes
Tome of Might (Gain Athletics +3 and +1 Strength)
Tome of Deflection (Gain Armor +3 and and +1 Protection)
Tome of Hearty Hewing (Gain Axes +4 and +5 Health)
Codex of Relentless Pummeling (Gain Maces +5)
Tome of Swordsmanship (Gain Swords +3 and +10 Energy)
Tome of Brawling (Gain Unarmed Combat +3 and +5 Evasion)

Mage Tomes
Tome of Air (Gain Air Magic +4 and Resist Shock +5)
Tome of Earth (Gain Earth Magic +2 and Resist Poison +15)
Tome of Fire (Gain Fire Magic +3 and Resist Fire +10)
Codex of Endless Winter (Gain Ice Magic +5)
Manual of Battle Mages (Spellcraft +4 and Energy +5)
Tome of Sticks (Gain Staff Defense +3 and Evasion +5)

Rogue Tomes
Tome of Merciless Murder (Gain Assassination +5)
Booklet of Shanking (Gain Daggers +3 and Energy +10)
Tome of Avoidance (Gain Dodging +3 and Dexterity +1)
Manual of Archery (Gain Missile Weapons +4 and Energy +5)
Tome of Hurling (Gain Throwing Weapons +3 and Strength +1)
Tome of Brawling (Gain Unarmed Combat +3 and +5 Evasion)

Miscellaneous Tomes
Tome of Bodybuilding (Gain Strength +2 and Health +5)
Tome of Agility (Gain Dexterity +2 and Evasion +2)
Tome of Hardiness (Gain Vitality +2 and Resist Poison +5
Almanac of Defiance (Gain Willpower +2 and Energy +5)
Tome of Learning (Gain 2500 experience)
Tome of Health (Gain Health +25)
Tome of Defense (Gain Protection +2 and Health +5)
Tome of Energy (Gain Energy +25)
Tome of Adaptation (Gain Resist Fire +5, Resist Ice +5, Resist Poison +5 and Resist Shock +5)
Tome of Reflexes (Gain Evasion +10)
Tome of Infinite Wisdom (Gain Skill Points +5)


TOME DEFINITIONS FOR ITEMS.LUA
Here's the definitions you'll need to add to your custom dungeon's items.lua file in order to place the tomes.
SpoilerShow

Code: Select all

-- WARRIOR SKILL TOMES
cloneObject {
	name = "tome_athletics",
	baseObject = "tome_health",
	uiName = "Tome of Might",
	gameEffect = "Gain Athletics +3 and Strength +1",
	description = "Written by forgotten instructors from an ancient era, this book describes how to advance one's might and muscle through progressively more difficult excercises.",
	skill = "athletics",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("athletics", 3, true)
				champion:modifyStatCapacity("strength", 1)
				champion:modifyStat("strength", 1)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_armors",
	baseObject = "tome_health",
	uiName = "Manual of Deflection",
	gameEffect = "Gain Armors +3 and Protection +1",
	description = "This valuable training manual gives the reader insights on how subtle changes in movement can help armor more effectively turn aside incoming blows.",
	skill = "armors",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("armors", 3, true)
				champion:modifyStatCapacity("protection", 1)
				champion:modifyStat("protection", 1)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_axes",
	baseObject = "tome_health",
	uiName = "Tome of Hearty Hewing",
	gameEffect = "Gain Axes +4 and Health +5",
	description = "These appears to be the memoirs of a crazed axe-wielding warrior whose bloody recounts reveal surprisingly deadly methods of chopping at things well into one's golden years.",
	skill = "axes",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("axes", 4, true)
				champion:modifyStatCapacity("health", 5)
				champion:modifyStat("health", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_maces",
	baseObject = "tome_health",
	uiName = "Codex of Relentless Pummeling",
	gameEffect = "Gain Maces +5",
	description = "The dented metal covers of this ancient codex on hammering indicate its previous owner was a hard hitter.",
	skill = "maces",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("maces", 5, true)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_swords",
	baseObject = "tome_health",
	uiName = "Tome of Swordsmanship",
	gameEffect = "Gain Swords +3 and Energy +10",
	description = "Written by a hedge knight claiming to have won every melee in every tournament he's ever entered, through mastery of his favored weapon and the stamina to keep swinging it.",
	skill = "swords",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("swords", 3, true)
				champion:modifyStatCapacity("energy", 10)
				champion:modifyStat("energy", 10)
  				playSound("level_up")
 		 		return true 
			end
}


-- MAGE SKILL TOMES

cloneObject {
	name = "tome_air",
	baseObject = "tome_health",
	uiName = "Tome of Air",
	gameEffect = "Gain Air Magic +4 and Resist Shock +5",
	description = "At first glance a simple-looking book on the subject of weather. But when opened, its pages rustle like leaves in the wind.",
	skill = "spellcraft",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("air_magic", 4, true)
				champion:modifyStatCapacity("resist_shock", 5)
				champion:modifyStat("resist_shock", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_earth",
	baseObject = "tome_health",
	uiName = "Tome of Earth",
	gameEffect = "Gain Resist Poison +15 and Earth Magic +2",
	description = "This dusty book contains the pooled knowledge of a secretive sect of arachnophobic druids.",
	skill = "spellcraft",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("earth_magic", 2, true)
				champion:modifyStatCapacity("resist_poison", 15)
				champion:modifyStat("resist_poison", 15)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_ice",
	baseObject = "tome_health",
	uiName = "Grimoire of Endless Winter",
	gameEffect = "Gain Ice Magic +5",
	description = "Engraved with ice-blue runes that glow with power, this ancient codex's chilling texts are rife with the arcane theories of the cold-hearted Frost Lords.",
	skill = "spellcraft",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("ice_magic", 5, true)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_spellcraft",
	baseObject = "tome_health",
	uiName = "Manual of Battle Mages",
	gameEffect = "Gain SpellCraft +4 and Energy +5",
	description = "A guide to the practical practitioner on how to have a lasting magical presence on the battlefield.",
	skill = "spellcraft",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("spellcraft", 4, true)
				champion:modifyStatCapacity("energy", 5)
				champion:modifyStat("energy", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_staff_defense",
	baseObject = "tome_health",
	uiName = "Tome of Sticks",
	gameEffect = "Gain Staff Defense +3 and Evasion +2",
	description = "A book on revolutionary methods of staff fighting. It is written in short but effective sets of easy to understand instructions.",
	skill = "staves",
	requiredLevel = 2,
	onUseItem = function(self,champion)
  				champion:trainSkill("staves", 3, true)
				champion:modifyStatCapacity("evasion", 2)
				champion:modifyStat("evasion", 2)
  				playSound("level_up")
 		 		return true 
			end
}

-- ROGUE SKILL TOMES
cloneObject {
	name = "tome_assassination",
	baseObject = "tome_health",
	uiName = "Tome of Merciless Murder",
	gameEffect = "Gain Assassination +5",
	description = "The blood-red cover of the book is adorned with a grinning silver skull. Its pages are filled with murderous instructions, ideal for the cold-hearted killer.",
	skill = "assassination",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("assassination", 5, true)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_daggers",
	baseObject = "tome_health",
	uiName = "Booklet of Shanking",
	gameEffect = "Gain Daggers +3 and Energy +10",
	description = "A short but practical instruction booklet, written by a convicted criminal, on how to maximize damage by sticking short, sharp objects into the opposing guys.",
	skill = "daggers",
	requiredLevel = 3,
	onUseItem = function(self,champion)
  				champion:trainSkill("daggers", 3, true)
				champion:modifyStatCapacity("energy", 10)
				champion:modifyStat("energy", 10)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_dodge",
	baseObject = "tome_health",
	uiName = "Tome of Avoidance",
	gameEffect = "Gain Dodge +3 and Dexterity +1",
	description = "A riveting tale of skullduggery about a scoundrel with a knack for getting into trouble and a gift for emerging unscathed.",
	skill = "dodge",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("dodge", 3, true)
				champion:modifyStatCapacity("dexterity", 1)
				champion:modifyStat("dexterity", 1)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_missile_weapons",
	baseObject = "tome_health",
	uiName = "Manual of Archery",
	gameEffect = "Gain Missile Weapons +4 and Energy +5",
	description = "The chief instructor of the Royal Marksmen demands perfection of his pupils even at the very end of a grueling training session. This training manuel describes how he accomplishes just that.",
	skill = "missile_weapons",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("missile_weapons", 3, true)
				champion:modifyStatCapacity("energy", 5)
				champion:modifyStat("energy", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_throwing_weapons",
	baseObject = "tome_health",
	uiName = "Tome of Hurling",
	gameEffect = "Gain Throwing Weapons +3 and Strength +1",
	description = "And old, plain-looking book whose moldy pages give expert opinion on how to build the strength and momentum necessary for tossing objects at distant targets.",
	skill = "throwing_weapons",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("throwing_weapons", 3, true)
				champion:modifyStatCapacity("strength", 1)
				champion:modifyStat("strength", 1)
  				playSound("level_up")
 		 		return true 
			end
}

-- COMMON SKILL TOMES
cloneObject {
	name = "tome_unarmed_combat",
	baseObject = "tome_health",
	uiName = "Tome of Brawling",
	gameEffect = "Gain Unarmed Combat +3 and Evasion +2",
	description = "This tome describes training methods capable of turning a man's very limbs into weapons.",
	skill = "unarmed_combat",
	requiredLevel = 5,
	onUseItem = function(self,champion)
  				champion:trainSkill("unarmed_combat", 3, true)
				champion:modifyStatCapacity("evasion", 2)
				champion:modifyStat("evasion", 2)
  				playSound("level_up")
 		 		return true 
			end
}

-- MISCELLANEOUS TOMES
cloneObject {
	name = "tome_strength",
	baseObject = "tome_health",
	uiName = "Tome of Bodybuilding",
	gameEffect = "Gain Strength +2 and Health +5",
	description = "You groan at the unnatural weight of this book, which describes how a group of men trained to build the strength and size needed to catch the eye of the famed sculptor from the Isle of Volcanoes.",
	weight = 10,
	onUseItem = function(self,champion)
				champion:modifyStatCapacity("strength", 2)
				champion:modifyStat("strength", 2)
				champion:modifyStatCapacity("health", 5)
				champion:modifyStat("health", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_dexterity",
	baseObject = "tome_health",
	uiName = "Tome of Agility",
	gameEffect = "Gain Dexterity +2 and Evasion +1",
	description = "The secret texts of this tome relays to the user how a ninja understands that true invisibility is a question of patience and agility.",
	onUseItem = function(self,champion)
				champion:modifyStatCapacity("dexterity", 2)
				champion:modifyStat("dexterity", 2)
				champion:modifyStatCapacity("evasion", 1)
				champion:modifyStat("evasion", 1)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_vitality",
	baseObject = "tome_health",
	uiName = "Chronicle of Hardiness",
	gameEffect = "Gain Vitality +2 and Resist Poison +5",
	description = "Written by a man whose name bears an odd resemblance to a toxic beverage, this book's pages detail how men of the enigmatic tribes of the south have learned to run for days without tiring.",
	onUseItem = function(self,champion)
				champion:modifyStatCapacity("vitality", 2)
				champion:modifyStat("vitality", 2)
				champion:modifyStatCapacity("resist_poison", 5)
				champion:modifyStat("resist_poison", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_willpower",
	baseObject = "tome_health",
	uiName = "Almanac of Defiance",
	gameEffect = "Gain Willpower +2 and Energy +5",
	description = "This book reveals the closely-guarded secrets of an order of monks infamous for their aggravating stubbornness.",
	onUseItem = function(self,champion)
  				champion:modifyStatCapacity("willpower", 2)
				champion:modifyStat("willpower", 2)
				champion:modifyStatCapacity("energy", 5)
				champion:modifyStat("energy", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_experience",
	baseObject = "tome_health",
	uiName = "Tome of Learning",
	gameEffect = "Gain Experience +2500",
	description = "Shuffling through the pages of this thick book takes you through exciting places and situations. The phenomenal writing makes you actually feel like you were actually there...",
	onUseItem = function(self,champion)
  				champion:gainExp(2500)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_protection",
	baseObject = "tome_health",
	uiName = "Tome of Defense",
	gameEffect = "Gain Protection +2 and Health +5",
	description = "A wonderfully illustrated book, revealing the secrets of Grandmaster Xonas, whose impressive feats include taking hammer blows to his gut without flinching.",
	onUseItem = function(self,champion)
  				champion:modifyStatCapacity("protection", 2)
				champion:modifyStat("protection", 2)
				champion:modifyStatCapacity("health", 5)
				champion:modifyStat("health", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_energy",
	baseObject = "tome_health",
	uiName = "Manual of Energy",
	gameEffect = "Gain Energy +25",
	description = "Clearly written by a sadist, this manual provides various rigorous excercise routines capable of tremendously boosting your stamina.",
	onUseItem = function(self,champion)
  				champion:modifyStatCapacity("energy", 25)
				champion:modifyStat("energy", 25)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_resist",
	baseObject = "tome_health",
	uiName = "Tome of Adaptability",
	gameEffect = "Gain Resist Cold +5, Resist Fire +5, Resist Poison +5 and Resist Shock +5",
	description = "This journal, kept by the renowned explorer Ygvirr, explains how the human body is capable of adapting to the harshest environments.",
	onUseItem = function(self,champion)
  				champion:modifyStatCapacity("resist_cold", 5)
				champion:modifyStat("resist_cold", 5)
  				champion:modifyStatCapacity("resist_fire", 5)
				champion:modifyStat("resist_fire", 5)
  				champion:modifyStatCapacity("resist_poison", 5)
				champion:modifyStat("resist_poison", 5)
  				champion:modifyStatCapacity("resist_shock", 5)
				champion:modifyStat("resist_shock", 5)
  				playSound("level_up")
 		 		return true 
			end
}

cloneObject {
	name = "tome_evasion",
	baseObject = "tome_health",
	uiName = "Tome of Reflexes",
	gameEffect = "Gain Evasion +5",
	description = "This volume is written by a frustrated scholar who wanted to write the biography of the elusive princess Arinna. Despite years of effort, he never managed to even speak with the princess. The myriad methods by which Arinna avoided the scholar are highly inspirational, however.",
	onUseItem = function(self,champion)
  				champion:modifyStatCapacity("evasion", 5)
				champion:modifyStat("evasion", 5)
  				playSound("level_up")
 		 		return true 
			end
}
The tomes are all cloned from the Tome of Health. Since all three standard tomes seem to use the same model anyway, this should be fine.

DESIGNING YOUR OWN TOMES
I decided to set up some logical rules to guide me when making tomes. What I wanted was tomes that were not too powerful or too weak, and also to avoid strange situations where classes gain boosts to skills they don't normally have access to. I ended creating a point system, setting some simple rules on requirements for using tomes as well as put down a few thoughts on the more creative aspects of the process.

You don't need to know this stuff to use the library nor are you required to when making your own times. But if you have similar goals as I did when adding to the existing pool of tomes, you'll probably want to read on. And even if you don't, it might be a good idea to read on anyway. Who knows? Maybe it'll provide valuable insight.


KEEPING TOMES BALANCED
To ensure the tomes are within a reasonable power level comparable to Grimrock's three predefined tomes (Tome of Health, Tome of Fire and Tome of Infinite Wisdom), I've created a point system as a guide to roughly how much power a character can gain from reading a given tome.

The values assigned aren't done through strict mathematical analysis, but from using Grimrock's existing Tomes and skill systems as reference points. And a good amount of "winging it."

Point values of stats
1 Strength = 4
1 Dexterity = 4
1 Vitality = 4
1 Willpower = 4
1 Protection = 4
1 Evasion = 2
1 skill point = 2
5 resistance = 2*
5 Health = 2*
5 Energy = 2*
250 XP = 1

* Alternatively you can award half the bonus, rounded down, for half the point cost.
Note that the predefined tomes all score exactly 10 in the system.

All tomes in the library are worth 10 points or slightly less, making them roughly equal to Grimrock's predefined tomes. If you want to make tomes that are even more powerful (or perhaps weaker), the point system can also help you estimate exactly how far you've gone with it. You could also go for a tiered tome system. Perhaps on the first levels of a dungeon, players only find tomes worth 5 points. Mid-dungeon, they start finding tomes worth 10 points. Defeating the powerful challenges of the deepest levels may uncover tomes worth as much as 15 points or more!


WHICH CHARACTERS CAN USE A TOME?
To ensure you don't end up with strange situations such as a Fighter getting points in Fire magic, any tome which grants bonus to a specific skill must require a character have a skill in the same class category before using the tome. For the majority of tomes in the library, I've set 5 skill points as the requirement. This is based on Grimrock's Tome of Fire. But in practice, 1 point should be enough to prevent any weirdness. For mage skills I've used Spellcraft for all the new tomes, also like the Tome of Fire. For other skill tomes I've used the same skill, since there really isn't a general purpose skill you can expect them to have. And it wouldn't do much good to read an Axe skill book as a Sword fighter anyway.

But requirements can also be used as a way to require additional investment into a skill, which may be fitting depending on a tome's background story.


DESCRIPTIONS AND STYLE
For the technical description of exactly what a tome does (the "gameEffect" entry in items.lua), I decided to use the following method:

List in order of highest point value stat boosts. For equal value boosts, order alphabetically.

As you may have noticed I've tried to vary exactly how the tomes work as well. A skill book doesn't always exactly +3 skill points. I've done this to make the various tomes feel a bit more unique than they otherwise would have. In general I've also used synonums for naming them. And in cases of extremely specialised books I sometimes accenuate that in the naming as well. Each tome in the library also comes with its own background story in a vein similar to Grimrock's tomes. Although probably a lot less imaginative.


DEPLOYING RANDOM TOMES
At first I went to random.org to generate a random number to decide which tome I wanted to place first... then I thought "why not let the game pick a random tome on its own?" So I made a script entity, named it global_functions and put this in it:
SpoilerShow

Code: Select all

-- Returns a random tome asset name
function getRandomTome()	
	local tomes = {"tome_air", "tome_armors", "tome_assassination",
		"tome_athletics", "tome_axes", "tome_daggers", "tome_dexterity",
		"tome_dodge", "tome_earth", "tome_energy", "tome_evasion",
		"tome_experience", "tome_fire", "tome_health", "tome_ice",
		"tome_maces", "tome_missile_weapons", "tome_protection",
		"tome_resist", "tome_spellcraft", "tome_staff_defense",
		"tome_strength", "tome_swords", "tome_throwing_weapons",
		"tome_unarmed_combat", "tome_vitality", "tome_willpower",
		"tome_wisdom"}	
	return tomes[math.random(1, table.getn(tomes))]
end
To make use of it, for example to place a random tome in an alcove, you can place another script entity near the alcove and put this in it:
SpoilerShow

Code: Select all

temple_alcove_2:addItem(spawn(global_functions.getRandomTome()))
And then you're set! Each time a new game is made, a tome, picked at random from the 28 available, should get placed within that alcove.


NOW GO PLAY WITH THE TOMES
If you find any issues with the resources provided here, let me know!


UPDATES
October 6th: Changed point value of Evasion from 5 Evasion = 4 to 1 Evasion = 2. Agility, Sticks, Brawling and Reflexes tomes have had their Evasion bonuses reduced to reflect this rebalancing.

Re: Tome Library

Posted: Sun Sep 23, 2012 9:39 pm
by petri
Very nice!

Re: Tome Library

Posted: Sun Sep 23, 2012 9:52 pm
by Komag
This is impressive, a very nice new resource for further customization, and I really appreciate that you took time to carefully consider the balance with respect to the original tomes. Nice work!

Re: Tome Library

Posted: Sun Sep 23, 2012 10:14 pm
by Neikun
Oh good. This will be a great resource for game builders, as they'll have little to worry about in regards to balancing.

You know, with resources in bulk like this, we're really going to need a more specific asset browser.
Browse by item, but also browse by item type. (tome)
Browse by prop, but also by prop type (door)
That sorta thing.

Re: Tome Library

Posted: Sun Sep 23, 2012 10:15 pm
by Montis
Yes, really good stuff here :)

Re: Tome Library

Posted: Tue Sep 25, 2012 11:53 am
by Whalefish
Awesome! Thanks for all the work you did. I will put this to use.

Re: Tome Library

Posted: Tue Sep 25, 2012 12:14 pm
by Montis
Btw: I added this (awesome) thread to the Useful Scripts Repository.

Re: Tome Library

Posted: Tue Sep 25, 2012 2:26 pm
by HaunterV
amazing.

also opened my eyes to something i think. is that random tome script similar to one that generates a random item in the alcoves?

Re: Tome Library

Posted: Tue Sep 25, 2012 4:58 pm
by Ixnatifual
I'm glad you guys are finding it useful.
HaunterV wrote:amazing.

also opened my eyes to something i think. is that random tome script similar to one that generates a random item in the alcoves?
I don't know that script, but it probably calls math.random too :)

Re: Tome Library

Posted: Tue Sep 25, 2012 5:25 pm
by SpacialKatana
I'm going to use that code for my random barrel drops. Nice work mate, saves me using math.random lines for each item grrrr ;)