Difference between revisions of "IVAN basic script course"
(Created page with "= holybanana's Iter Vehemens ad Necem basic script course = == About script and this course == This series of documents is aimed at teaching the IVAN community how the IVAN...") |
(No difference)
|
Revision as of 10:44, 7 May 2016
holybanana's Iter Vehemens ad Necem basic script course
About script and this course
This series of documents is aimed at teaching the IVAN community how the IVAN script works. The basic principle is that the IVAN source code determines how the world of IVAN and it's various contents, dungeons, levels, characters, items, materials, etc. are created. IVAN script, on the other hand, tells what are created. So when you try to go down from GC7, the script tells the game that there is a level GC8, what materials its walls are made, what kinds of rooms it has and if special rooms, for instance a secret vault or an orc room, are randomly placed there. It also tells that Ivan and Vladimir must be placed there and that they belong to a unique team. It knows the equipment, attributes and weapon skills of Ivan. It commands that the a pile of roubles and vodka bottles must be placed in his inventory. It contains the basic replies Ivan spits out when talked to. The source code follows the script's commands and maked them virtual reality. It also handles the things that are too difficult to formalize in the simple language of IVAN scripts. For instance, that Ivan refuses to unequip his Gorovits family equipment and that he joins the player when the latter is tough enough are facts that are due to the source code, not the script.
I do not intend to systematically document every feature of the script system. Rather will I present as much as is needed for fluent use of script for basic purposes. The order I present these things is chosen to aid learning, so I will not proceed from the general to the specific as a systematic documentation would most naturally do. Instead I often give an example first and the general rule or the dry table of data later. I think you'll grasp the script easiest by writing it instead of reading charts and such.
Lecture 1: Database types and configs
First, get IVAN. In this lecture we don't need the source code yet, so a binary release is enough. You'll also need a simple text editor and an image editor which can show pcx files.
Install IVAN and look at your IVAN installation folder. You should find a folder named Script. I'm not absolutely sure where this folder is located under different OSs, ask Hex (our porter) or the community for help if you can't find it.
Open glterra.dat. IVAN's squares have two terrains, ground terrain and over terrain. This is the file where the data of the ground terrains is stored.
You'll see several structures like this:
solidterrain /* glterrain-> */ {
Walkability = WALK|FLY|ETHEREAL; IsAbstract = true; ... Config SNOW_TERRAIN; { OKVisualEffects = MIRROR|FLIP|ROTATE; MainMaterialConfig == SNOW; NameSingular = "ground"; BitmapPos = 16, 16; } ...
}
Here solidterrain is called a type. SNOW_TERRAIN is called a config. Types can have very special unique attributes, for instance a octopus character type may have dozens of bodyparts. Configs only differ from an object of their base type by more subtle details that can be defined in the script.
For instance, SNOW_TERRAIN here acts just like any other solidterrain except for its outlook.
The values that are inside the type definition but outside any config determine the main config, config number 0. The values of the main config are inherited to all other configs.
Types must be added both in the code and in the script. Configs may be added to script only, although if you want to use the config in the code, you need to add a short definition to Main/Include/confdef.h, too.
Now you may try adding a new config, for instance CUSTOM_TERRAIN. But wait! Other files need to know this config, too. Open define.dat. Search for SNOW_TERRAIN. You'll find the lines:
- define PARQUET 1
- define FLOOR 2
- define GROUND 3
- define GRASS_TERRAIN 4
- define LANDING_SITE 5
- define SNOW_TERRAIN 6
- define DARK_GRASS_TERRAIN 7
- define SAND_TERRAIN 8
These are the configs of solidterrain. You must add CUSTOM_TERRAIN here.
- define CUSTOM_TERRAIN 9
Now every script file knows what you mean when you talk about CUSTOM_TERRAIN. Coders should do exactly the same steps in confdef.h, ie. add
- define CUSTOM_TERRAIN 9
after the definition of SAND_TERRAIN. This way, the code would also know CUSTOM_TERRAIN.
Note that the number 9 here is an arbitrary value which the game uses to distinguish this config. It is a good custom to number the configs 1,2,3,... Note that number 0 cannot be used, since it is the number of the main config.
Now you can add something like this in glterra.dat:
Config CUSTOM_TERRAIN; { MainMaterialConfig == ???; NameSingular = ???; BitmapPos = ???, ???; }
These three database values are obligatory here. How do we know what we can put in place of the ???-strings?
For materials, this is easy. Open material.dat. Check out all the materials. Let's say you like the config EBONY_WOOD. Then
MainMaterialConfig == EBONY_WOOD;
does the trick. You don't have to care about the type of the material, only the config name is needed. Note that this is an exception. Normally both the type and the config are needed to specify a database unambiguously. For instance, to instantiate a kobold lord you use kobold(LORD), not just LORD. This is because a config LORD may be a config of some other character too. For materials, we have guaranteed that different types have different configs, so they can be referred to more simply.
It will be explained in later lectures why we use a double equality symbol == here instead of =.
Fill anything you like for NameSingular. The name should be surrounded by quotation marks, for instance "dance floor".
Now open the Graphics folder, which is in the same place as the Script folder. Open GLTerra.pcx with a picture editor. Choose a 16x16 tile from the pcx and note the position of its upper left corner. Say it is 32,0. Then add
BitmapPos = 32,0;
to the config.
In the next lecture I'll tell you how to instantiate your new terrain. After instantiation you are able to see your terrain in the game.
Easy exercise: Add a new ground terrain and start the game. If you get an error message, check the contents of this lecture again. Otherwise you pass.
Lecture 2: Instantiation of entities
From lecture I, you have a new ground terrain of type solidterrain, config CUSTOM_TERRAIN. We now want to use this in the game.
The data of IVAN's levels is stored in the file dungeon.dat. Let's choose level 1 of gloomy cave to edit. The name of gloomy cave is ELPURI_CAVE in the script. There is a line
Dungeon ELPURI_CAVE;
in dungeon.dat. Below it you will find the following entry:
Level 0; { FillSquare = solidterrain(GROUND), MORAINE earth;
RoomDefault { Pos = 2:XSize-5,2:YSize-5; WallSquare = solidterrain(GROUND), FIR_WOOD wall(BRICK_OLD); FloorSquare = solidterrain(PARQUET), 0; DoorSquare = solidterrain(PARQUET), FIR_WOOD door; } }
This is level 1, because in the script levels are numbered 0,1,2,... Now experiment by changing solidterrain(PARQUET) to solidterrain(CUSTOM_TERRAIN) in both of the lines where it occurs above.
Now you've made your first (quite artificial) modification to IVAN! How to test it quickly? Enter the game, activate Wizard Mode, press 3 (see whole map cheat), search the gloomy cave in the look mode, press 4 (walk through walls and over water cheat), run to the cave and enter it. Note that the floors of all the rooms have changed to your custom terrain.
Of course, there are many more values you can change in dungeon.dat. You see many statements of the form X = Y; or X = Y(Z); here. All of these mean that when IVAN needs the thing X, it instantiates an entity of type Y. If Z is not defined, it uses the main config, otherwise config Z is chosen. If the main config is not available, a random config is chosen. For instance in the case of the type kamikazedwarf, there is no "generic kamikaze dwarf", only a dwarf for each god. Another example is solidterrain. Remember that it had a line IsAbstract=true; in it when we presented its definition in the last lecture. This is just the command that makes the main config unavailable.
Let's present another example of instantiation. In char.dat you note that there is a type orc and it has a config SLAUGHTERER. The script
Square, Random; { Character = orc(SLAUGHTERER); }
adds an orc slaughterer to the GC1 if you put it inside the definition of Level 0 above.
Often, you want to give further orders how to instantiate an entity. One way to do this is to change its material. This is easy, just add the material's config's name before the instantiation. For example GOLD solidterrain(CUSTOM_TERRAIN) instantiates a golden custom terrain. For some entities there may be more materials. Then you may specify them all, for instance STEEL RUBY meleeweapon(LONG_SWORD) creates a steel long sword with a ruby handle. If the materials are not specified, the game uses the default materials in the respective database.
Note that this syntax for changing materials doesn't currently work for characters. How do you make a valpurium golem then? There is a config for a golem of each material. So you don't write VALPURIUM golem but golem(VALPURIUM). The former is not a syntax error, but it doesn't work (it should, in fact, I'll have to correct this some day).
Another way to customize the instantiation is to add parameters to it. This is done in a block surrounded by the symbols { and } after the instantiation. For instance, for items you can specify a chance to appear. This is a percentage, so chance 50 means the item appears 5 times in 10 games on average. Example of use: STEEL RUBY meleeweapon(LONG_SWORD) { Chance = 50; }
There are many other parameters for entities, but we will not list them in this lecture.
Instantiation is used in files other than dungeon.dat, too. For example, for any humanoid character in char.dat equipment is instantiated as above. For instance RightWielded = STEEL RUBY meleeweapon(LONG_SWORD) { Chance = 50; } means that the character has a 50% chance of having a steel/ruby long sword in his or her right hand when he or she is generated. Note that the script has little error checking here, the game may crash if you try to equip a weapon to a monster which does not have hands. So always check whether the instantiation commands you give are sane.
That's enough for now. As an exercise, try giving the player (char.dat, type playerkind, main config) some starting equipment, for instance by copypasting someone else's equipment. Then add Sherarax to a random place in New Attnam and set her Team to MONSTER_TEAM. Tweak your equipment, but not your attributes, until you can beat her in melee combat and take a screenshot of the situation, with equipment screen open and her death message clearly shown in the message panel. Appendix: Comparison to source code
It is interesting to compare the contents of this lecture to how the same things are done in the source code. If you want to create a character, item or terrain of type X and config Y, you use the following command:
X::Spawn(Y);
This returns a pointer to an object of the type X. For instance,
bear* Teddy = bear::Spawn(POLAR_BEAR);
creates a bear to which Teddy points at. If you want to use the main config, you may leave out the Y and just call Spawn(). Note that this does not choose a random config if the main config is unavailable, like in the script. It may crash in this case, so always check that the IsAbstract is not set to true if you want to use the main config.
You can use the same syntax for materials, but since their configs are all distinct even if the types differ, you can use a shorter macro to create them:
material* Material = MAKE_MATERIAL(X,Y);
Here X is the config, for instance IRON, and Y is the volume, in cubic centimetres (1/1000th of a litre). You don't have to define the volume if you just change some object's material. Just leave it out then, and the game summons as much material as was present before the change.
Note that you must put whatever you created somewhere before it appears in the game. This is done differently for materials, terrains, items and characters. For characters, you must also set its team. Search for Spawn and MAKE_MATERIAL in the code to find example how this is done. Here's one though.
darkmage* Izzy = darkmage::Spawn(ARCH_MAGE); Izzy->SetTeam(game::GetTeam(MONSTER_TEAM)); Izzy->PutToOrNear(PLAYER->GetPos());
Copypaste this at the beginning of the function commandsystem::NOP (short for No OPeration) in Main/Source/command.cpp. You must also add the following includes to the file:
- include "human.h" //the class darkmage is declared here
- include "confdef.h" //the definition of ARCH_MAGE is here
Start a game and summon a few archmages from the wait command '.'. Good luck.
When you've fed up with mutilation, clones and arcanite golems, let's move to a serious exercise. How do you modify commandsystem::NOP so that every time '.' is pressed the holy banana of Oily Orpiv is summoned in you inventory? Present the whole function and the includes.
Lecture 3: Overview of the structure of IVAN and its script and graphics files
The world map of IVAN is hard-coded. In the world map there are dungeons, currently Attnam, New Attnam, UT and GC. In dungeons there is a number of levels. Levels consist of squares. Some, but not all squares belong to a room. The entities which can occur in a square of a level of IVAN include objects and characters. Objects are things that can be made of materials. Terrains and items are objects. There is always a ground terrain in a square. There may also be an over terrain over it, for instance a wall or a throne. Characters are made up from special items called bodyparts. Every character belongs to a team. The content of dungeons and levels and the properties of the aforementioned entities are mostly defined by the script. There are special entities, for instance webs, which are hard-coded, and many entities have special properties which are too difficult to be defined by the simple language of the script, so they are hard-coded, too.
We will now fully list the things that can be defined in the script and the files where their data is located.
dungeons: dungeon.dat levels: dungeon.dat rooms: dungeon.dat squares: dungeon.dat teams: dungeon.dat materials: material.dat ground terrains: glterra.dat over terrains: olterra.dat items: item.dat characters: char.dat
The names glterra.dat and olterra.dat come from the class names ground level terrain and over level terrain. In the game there are also ground world map terrains and over world map terrains, but their properties cannot be defined in the script.
Note that characters are divided further to humanoids and nonhumanoids. The former have seven bodyparts, the latter only one, the torso. You currently have to look at the source code to know which characters are humanoids and which nonhumanoids, if you don't remember it from the game. We apologize for the inconvenience and will correct this.
There is also define.dat which contains definitions of the form #define DEFINITION expression. If DEFINITION is encountered when parsing the script, the value of expression is substituted for it. So since the file contains a line #define WAR_LADY 3 every string WAR_LADY in the script is really just number 3, the config number of mistress warladies. The syntax is chosen so that the same definition can be used in the source code, too. In the file Main/Include/confdef.h there is the same line #define WAR_LADY 3, and this is a valid C++ macro definition. So WAR_LADY may be used both in the script and the code and has the same meaning.
In lecture I we noted that the graphics of ground terrains must be chosen from GLTerra.pcx. We now list the graphics files associated with each type of entities.
ground terrains: GLTerra.pcx over terrains: OLTerra.pcx items: Item.pcx humanoids: Humanoid.pcx nonhumanoids: Char.pcx
That is, the BitmapPos database value of ground terrains, over terrains and items means the location of the upper left corner of the 16x16 or 32x32 picture of the entity in the respective file above. For humanoids and nonhumanoids the same holds for the database values TorsoBitmapPos, HeadBitmapPos, etc. More information about the graphics files may be found in the graphics documentation.
Exercise: randomly generated orc slaughterers may carry potions of troll blood. Find out in which file this detail is defined. Present the line. Then find out where it is defined that the orc general and the orc officers in the orc room of GC have some more expensive potions in their inventory. Present the lines.
Lecture 4: Basic syntax structure of the script
Browsing through he script, you may note that most of the commands there are of form X { A=B; C=D; E=F; ... }. Let's call this form a structure definition and the commands A=B; C=D; and so on member value assignments. In other words, the script consists of structures which have members to which you can assign values. The order which you present structure definitions and member value assignments has usually no relevance. (There are some rare exceptions.) The script also works whether you divide your script to separate lines or not. You can also insert extra spaces and tabs between words without changing the meaning of the script. However, you cannot cut a line or insert a space or tab inside a word, for instance write "darkmage" as "dark mage". Even though it is not syntactically necessary, you should follow the intendation used previously in the script. So write like this:
X {
A=B; C=D; E=F; ...
}
And please avoid intending like this, for instance:
X { A=B; C=D; E=F; ... }
Each member of a structure has a type. (Remember "type" also has another meaning introduced in lecture I. We use the phrases "entity type" and "script member type" to distinguish between these two meanings, if there's some possibility of confusion.) The most common are other structures, integers, vectors, strings, contentscripts and arrays of elements of a certain type.
Integers are presented like this: 5, 1000000, -532489, 6*527, 5*(-6+62/7)+9 and so on. Division is rounded down. One special class of integers are colors, which are presented like this: rgb16(100,0,100) or rgb24(255,0,0). These are functions which transform triples (red,green,blue) into integers. The elements of these triples are in the range from 0 to 255. So rgb16(0,255,0) and rgb24(0,255,0) both mean bright green. rgb16 is used for colors of material coloring and rgb24 is used for lights.
Vectors represent positions in two-dimensional spaces, for instance in bitmaps or levels. They are presented as X, Y where X and Y are integers.
Any word is a string. So is a phrase surrounded by quotation marks. So Alfred is a string and so is "Alex the Great", but the words Alex the Great are three separate strings. It is customary to always use quotation marks to help readers recognize strings, so you should use "Alfred" instead of Alfred. You may use the symbols \" to insert a quotation mark inside a string. Example: "The guard says: \"Buzz off!\"".
Contentscripts are the commands which represent instantiations of entities, explained in lecture II, for instance OMMEL_HAIR cloak(CLOAK_OF_FIRE_RESISTANCE). There are character, item, ground terrain and over terrain contentscripts.
An array of elements of a certain type is presented as { n, A, B, C, ..., X; }, where A, B, C, ..., X is a list of n elements which are all of that type. For instance, let's consider an array of item contentscripts, which are quite common. Let's choose a structure member of such an array type: the Inventory member of character databases of char.dat. As an example, here's the basic inventory of Golgor Dhan:
Inventory = { 2, scrollofenchantarmor { Times = 2; }, scrollofenchantweapon { Times = 2; } }
Btw, here we learn a new parameter for instantiations, Times. It is a member of integer type and determines how many times the instantiation is repeated. So the above script means exactly the same as
Inventory = { 4, scrollofenchantarmor, scrollofenchantarmor, scrollofenchantweapon, scrollofenchantweapon; }
Often, you want the array to have only one element. Here's an example:
Square, Random; { Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } }
This creates a big mine at a random location which is visible to monsters but not other teams, is active and has 50% chance of appearing. Note that you use the syntax == X; instead of = { 1, X; }. The latter works also, but is uglier.
A structure may have a member of its own type as a member or member of a member, etc. This allows recursion. For instance, you can determine the contents of chest as an array of item contentscripts even though the chest is presented as an item contentscript itself. Example:
Inventory == itemcontainer(LARGE_CHEST) { ItemsInside == itemcontainer(CHEST) { ItemsInside == itemcontainer(SMALL_CHEST); } }
If you add this to the player, he will get a chest inside a chest inside a chest.
Exercise: Suppose the player's bananagrower friends give him a farewell gift at the start of each journey. That is, a random amount of bananas is generated at the start of the game in the player's inventory. One banana is always generated. Five to six bananas are most common. The maximum of ten bananas is generated only rarely. Bananagrowers are generous to their friends but ten is already a bit too much, as they have to feed their children. How do you implement this feature using just the script?
Exercise II: Is there any way to have an item appear one time in ten thousand games in a specific place, using just the script? Answer yes or no. If you answered yes, present an example.
Lecture 5: Adding materials
This lecture is a bit more like a reference guide than a lesson. You don't need to know everything in it. In practice, you should first choose a material which is very close to your material, copypaste its data and modify it. If you want to know what some value does, or you need to know whether there is a database value which does the thing you want, then you consult this guide.
When adding materials, you first choose the type, as always. The hard-coded types are
solid: No special properties. organic: Spoils eventually. gas: Can appear over a square as a cloud of smoke. liquid: Can be spilled on items or squares. flesh: Spoils eventually, can have colored skin, can have leprosy. powder: Can become wet. ironalloy: Can rust.
What does "can have colored skin" mean? If a limb is made of some material other than flesh, its color is that material's color, but for flesh it may be overridden by the SkinColor database member of character databases, even though the material is blood red from the inside.
After choosing the appropriate type, you choose a new config, for instance CUSTOM_MATERIAL. Note that you write it with BIG_LETTERS, using '_' in place of space. Suppose you chose to add an organic material. You look into define.dat and find the following lines:
- define ORGANIC_ID (4096 * 2)
- define BANANA_FLESH (ORGANIC_ID + 1)
- define SCHOOL_FOOD (ORGANIC_ID + 2)
...
- define OMMEL_BONE (ORGANIC_ID + 13)
- define OMMEL_TOOTH (ORGANIC_ID + 14)
You just add
- define CUSTOM_MATERIAL (ORGANIC_ID + 15)
here and start making the config in material.dat, after the old organic configs, like explained in lecture I.
Materials have five database values which consist of a number of flags which are either on or off. These are CommonFlags, NameFlags, CategoryFlags, BodyFlags and InteractionFlags. By default, the flags of a material are the flags of the database which is its base, for instance for a flesh material the flags of flesh. If you want to set a flag on, you use the command Flags = Base|Flag; and if you want to set it off, you use the command Flags = Base&~Flag; Here Flags is one of the flag members above. This syntax may seem odd for those who are not C/C++ programmers, but those with knowledge of bitwise operations | (or), & (and) and ~ (not) immediately notice that this is similar to the basic C/C++ syntax.
We will now list all the flags for materials. CommonFlags are
CAN_BE_WISHED: If the material can be summoned with a scroll of change material. IS_VALUABLE: If this is set, pets avoid consuming this and leave it for you. CAN_BE_MIRRORED: If you can mirror an item made of this material.
NameFlags are USE_AN: Whether you use "an" instead of "a" as the article of the material's name.
CategoryFlags are
IS_METAL: Can be fixed by a smith. IS_BLOOD: Affects descriptions of items and squares covered with this liquid. CAN_BE_TAILORED: Can be fixed by a tailor. IS_SPARKLING: Sparkle animation is generated for objects made of this material. IS_SCARY: Monsters avoid this kind of smoke. IS_GOLEM_MATERIAL: Golems made of this material are generated.
BodyFlags are
IS_ALIVE: Affects bodyparts made of this material in various ways, for instance whether it bleeds. Only characters with an alive torso can be poisoned or drained from life, etc. IS_WARM: A creature can be seen by infravision if it has a bodypart whose material is contains warm blood. CAN_HAVE_PARASITE: Can contain a parasite if spoiling. USE_MATERIAL_ATTRIBUTES: If this is not on, a bodypart's physical attributes (Strength, HP, etc.) are initialized based on the character's database and the bodypart can gain exp. Otherwise they depend on the material's StrengthValue and Flexibility. CAN_REGENERATE: Whether the bodypart can regenerate lost HPs.
InteractionFlags are
CAN_BURN: Only books and scrolls made of burnable materials are destroyed by explosions. CAN_EXPLODE: Containers containing this material can explode. CAN_DISSOLVE: Is affected by acid. AFFECT_INSIDE: When consumed, the liquid doesn't affect immediately, but is left in the body, affecting your throat and stomach until it vanishes. Used for acids currently. EFFECT_IS_GOOD: Pet's are not enraged if you throw containers, for instance potions, full of this material at them.
Here's a full list of material database values. int means that the type of the value is an integer. I have marked the obligatory ones with an asterisk (*). These you must define every time you make a new material. int StrengthValue (*): One of the most important attributes. Affects how much damage items made of the material do and how much they resist damage before breaking. Determines the strength and HP of a bodypart made of this material if the USE_MATERIAL_ATTRIBUTES flag is set. int ConsumeType: You can find the consume types in define.dat. They are named starting with CT_, for instance CT_METAL. This affects whether monsters can consume items made of this material. int Density (*): The density in grams per litre. int Color (*): This should be a 16-bit color, eg. rgb(150,100,100). Affects outlook. int RainColor: This is only used when the material is raining from the sky. int PriceModifier: The value of a lump of this material is determined by this. int Emitation: This should be a 24-bit color, eg. rgb24(135, 155, 135). This determines the color and strength of light emitated by the material. int NutritionValue: How quickly a character consuming this material becomes satiated. string NameStem (*): The basic name of the material. string AdjectiveStem: The adjective form of the material's name, eg. "golden" for "gold". Defaults to NameStem. int Effect: The effect which is run when the material enters the body of a creature. You can find all the effects in define.dat. They start with EFFECT_, for instance EFFECT_HEAL. int ConsumeEndMessage: Index of msg which is printed when consuming this stuff has ended. They start with CEM_ in define.dat. int HitMessage: Index of msg which is printed when consuming this stuff has ended. They start with HM_ in define.dat. int ExplosivePower: Only affects if the CAN_EXPLODE flag is set. int Alpha: Determines the transparency of the material. 255 means fully opaque, 0 means invisible. int Flexibility (*): A very important attribute, too. Tells how much agility or dexterity penalty an armour made of this material causes. Also determines the dexterity or agility of limbs made of this material. int SpoilModifier: How slowly the material spoils. Only affects organic and flesh materials. int EffectStrength: How strong the effect of the Effect value is. int DigProductMaterial: Which material is created when a wall made of this material is destroyed. int ConsumeWisdomLimit: Monsters which have this or higher intelligence don't consume this material. Default is NO_LIMIT. int AttachedGod (*): Affects offering and whether a god can make a bodypart of this material. You can check the gods from define.dat. For instance VALPURUS. string BreatheMessage: A string which is printed if one enters a square which has a cloud of smoke made of this material over it. int StepInWisdomLimit: Monsters which have this or higher intelligence don't step in a cloud of this material, unless they have to. Default is NO_LIMIT. int RustModifier: For ironalloys, how quickly the material itself rusts. For liquids, how quickly the material causes rust on ironalloys. int Acidicity: How powerful acid this material is, or 0 if it's neutral. item contentscript NaturalForm: The item which is created when a terrain made of this material is destroyed. Also used in several other occasions, for instance when a golem is destroyed. int HardenedMaterial: Which material this material hardens to. Please choose a material with the same AttachedGod. int IntelligenceRequirement: The intelligence requirement of hardening another material to this material. Summoning a material requires this +5 int. int Stickiness: How sticky the material is if you are stuck to it.
Exercise: Create an ommel nut flesh material, which spoils slowly and radiates bright yellow light. Make it so that golems can be generated of this material but it cannot be wished nor mirrored. It belongs to Nefas. Let it have the same effect and messages as ommel urine, but the effect is stronger. Let its natural form be a nut. Present the config definition.