Module Developer's Guides

If you have a module that you'd like comments on or would like to know how to create your very own module, post here

Moderator: Moderator

Post Reply
Message
Author
FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Module Developer's Guides

#1 Post by FACM »

Part 1: Races, Classes, etc. [Player Descriptors]

In the default ToME module, you get to choose from a race, a sub-race, gender, a background, a class, and then a subclass. To the T-Engine, these are all the same thing, called 'player descriptors' (Charcter descriptors would be more accurate, but that's nitpicking), or PDs for short. PDs are what let you alter the player character in all sorts of different ways. Player descriptors typically stack, so any modifications done to one will usually add with any other modifications, rather than replacing them.

There's no definition or distinction between different types of PDs by the engine. They can all have the same functionality, the only place any consistency is found is by the module author. So, if you only want stats to come from races, and skills from classes, you will have to make sure that only races gain stat bonuses and only classes have skill bonuses. The engine will not discriminate against this for you.

All the example below will be from the default ToME module, so you should be able to see pretty easily where everything came from and can easily modify it and see what happens with your copy of ToME.

Possibly the most important PD is the one that the player will never see, the base descriptor, found in /data/player/descriptors.lua. This is the one that is loaded for every character, and provides the universal traits that each character will share. The function for it, in 3.0.0alpha19, looks like this:

Code: Select all

new_player_descriptor
{
    type = "base"
    name = "Base Descriptor"
    body = { INVEN=23 MAINHAND=1 OFFHAND=1 BODY=1 OUTER=1 LITE=1 HANDS=1 TOOL=1 RING=2 HEAD=1 NECK=1 FEET=1 MOUNT=1 BACKPACK=1 }
    skills =
    {
        ["Monster-lore"]     = { mods.add(0),     mods.add(500) }
        ["Prayer"]         = { mods.add(0),     mods.add(500) }
        ["Udun"]         = { mods.add(0),     mods.add(400) }
        ["Magic-device"]     = { mods.add(1000),     mods.add(1000) }
        ["Riding"]              = { mods.add(0),        mods.add(500)  }
    }
    starting_objects =
    {
        { obj=function(obj) object_prep(obj, TV_PARCHMENT, SV_BEGINNER_PARCHMENT) end },
        { obj=function(obj) object_prep(obj, TV_FOOD, SV_FOOD_RATION) end min=3 max=7 },
        { obj=function(obj) object_prep(obj, TV_LITE, SV_LITE_TORCH) end min=3 max=7 },
        { obj=function(obj) object_prep(obj, TV_TOOL, SV_SHOVEL) end },
    }
}

This defines out the body and inventory slots for each character, the default levels for gaining some skills, and the minimum equpment each character will need to have. Don't worry if you don't know what each part does, I'll explain those out in more detail later.

The second PD applied by the ToME module is your race, followed by subrace, found appropriate in /data/player/races.lua and /data/player/subraces.lua. Races are where ToME typically grants the charcter their most unique abilities and major stat adjustments. Below is the PD for elves, and then a subrace of elf.

Code: Select all

new_player_descriptor
{
    type = "race"
    define_as = "RACE_ELF"
    name = "Elf"
    desc = {
        "Elves are the first born of Arda.",
    }
    descriptor_choices =
    {
    subrace =
        {
            Noldor = "allow"
            Avari = "allow"
            __ALL__ = "never"
        }
    }
    stats = { [A_STR]=1, [A_INT]=3, [A_WIS]=2, [A_DEX]=3, [A_CON]=1, [A_CHR]=5, }
    life_rating = 15
    experience = 100
    age = { 100, 30 }
    weight =
    {
        female = { 180, 15 }
        male = { 190, 20 }
    }
    height =
    {
        female = { 82, 10 }
        male = { 90, 10 }
    }
    infravision = 4
    flags = { ELF=1 }
}

new_player_descriptor
{
    type = "subrace"
    define_as = "SUBRACE_NOLDOR"
    name = "Noldor"
    desc = {
        "The Noldor are the second clan of Elves who came to Valinor, and ",
        "are accounted as the greatest of all peoples in Middle-earth. ",
        "They are masters of all skills, and are strong and intelligent. ",
        "They can play all classes except rogues, and very well at that. ",
        "High-elves begin their lives able to see the unseen, and resist ",
        "light effects just like regular elves.  However, there are few ",
        "things that they have not seen already, and experience is very ",
        "hard for them to gain."
    }
    stats = { [A_STR]=1, [A_INT]=3, [A_WIS]=2, [A_DEX]=3, [A_CON]=1, [A_CHR]=5, }
    experience = 200
    levels =
    {
        [1] = { SEE_INVIS=true RESIST=getter.resists{LITE=60} }
    }

    skills =    {
        ["Weaponmastery"]   = { mods.add( 2000), mods.add(0) },
        ["Archery"]         = { mods.add( 5000), mods.add(0) },
        ["Sneakiness"]      = { mods.add(  600), mods.add(0) },
        ["Stealth"]         = { mods.add( 8000), mods.add(0) },
        ["Disarming"]       = { mods.add(  800), mods.add(0) },
        ["Magic-device"]    = { mods.add( 4000), mods.add(0) },
        ["Spirituality"]    = { mods.add(20000), mods.add(0) }
    }
}
For gender, in sexes.lua, the descriptor is much simpler:

Code: Select all

new_player_descriptor
{
    type = "sex"
    name = "Male"
    desc = "Males. Need a good description"
    flags =
    {
        MALE=true
    }
}

The character backgrounds, in races_bg.lua, are typically minor modifications to the character. They can have a wide variety of effects on a character, but usually only have a minor overall change to it.

Code: Select all

new_player_descriptor
{
    type = "race_background"
    name = "Barbarian"
    desc = {
        "Hardy members of their race they are strong fighters but poor spellcasters.",
    }
    stats = { [A_STR]=2, [A_INT]=-3, [A_WIS]=-2, [A_DEX]=1, [A_CON]=1, [A_CHR]=-3, }
    luck = 1
    mana = 50
    life_rating = 2
    experience = 25
    flags = { PLACE=birth.place.AFTER }
    levels =
    {
        [10] = {
            RESIST = getter.resists{FEAR=100}
        }
    }
}
A typical class, like this one from archer_classes.lua, is going to be the most complex PD that gets applied to the character.

Code: Select all

new_player_descriptor
{
    type = "subclass"
    name = "Archer"
    desc = {
        "'Kill them before they see you' could be the motto of the archer class.",
        "As deadly with a bow as a warrior is with a sword.",
    }
    stats = { [A_STR]=2, [A_INT]=1, [A_DEX]=2, [A_CON]=1, [A_CHR]=1, }
    skills =
    {
        ["Archery"]         = { mods.add(1000), mods.add(850)  }
        ["Bow-mastery"]     = { mods.add(0)   , mods.add(500)  }
        ["Combat"]          = { mods.add(1000), mods.add(800)  }
        ["Disarming"]       = { mods.add(1000), mods.add(900)  }
        ["Magic"]           = { mods.add(0)   ,mods.add(200)   }
        ["Magic-device"]    = { mods.add(0)   , mods.add(100)  }
        ["Sling-mastery"]   = { mods.add(0)   , mods.add(500)  }
        ["Sneakiness"]      = { mods.add(1000), mods.add(900)  }
        ["Weaponmastery"]   = { mods.add(1000), mods.add(500)  }
    }
    starting_objects =
    {
        { obj = lookup_kind(TV_LIGHT_ARMOUR, SV_SOFT_LEATHER_JACKET) },
        { obj = lookup_kind(TV_BOW, SV_SHORT_BOW) },
        { obj = lookup_kind(TV_ARROW, SV_BODKIN_ARROW) min=25 max=25 },
    }
}
After making all of these selections, ToME lets you assign stats, skills, and then starts you off into Middle-Earth. The order is determined by the birth.sequence function inside /data/player/birth.lua, and you can change around the order of your own descriptors if you want.

Code: Select all

birth.sequence
{
    -- Load the base template, only one possibility, no choice will
    -- be displayed
    { descriptor_type = "base" },
    -- Ask for race
    {
        title = "Select your race"
        desc = {
            "Race determines your basic physical and mental statistisc and some races may"
            "have special powers."
            "If you do not know what to choose, Humans are the best for beginners."
        }
        descriptor_type = "race"
    },
    -- Ask for subrace
    {
        title = "Select your subrace"
        desc = {
            "The race you have selected comes in different 'flavors',"
            "each of them has different traits and may have special powers."
            "If you do not know what to choose, the first choice is the best for beginners."
        }
        descriptor_type = "subrace"
    },
    -- Ask for racial background
----AND SO ON, AND SO ON, ETC.----

To insert your own PD in this sequence, you'd just need to add in your own block of code into this list like this:

Code: Select all

    -- Ask for new PD
    {
        title = "Select your NEW_PD_HERE"
        desc = {
            "This is the description for your new PD.",
            "Make it clear, but don't use actual numbers."
        }
        descriptor_type = "NEW_PD_HERE"
    },
Now, we've seen a few example PDs, and how to change their order and insert them. With that out of the way, we can get to the fun part: Figuring our how to make our PDs affect the character. Again, the T-Engine doesn't limit what a PD can do, the module author does. Now, let's take a look at everything we can do within a PD. While all of the example here are from ToME, they can all apply just as easily to your own module. There are a couple of required fields, however, to explain before gett ing to the parts that define the character. Note that code statements are being put inside of ' ' quotes, and that you won't want to use those around each statement in your module's code.

type: This is used by the T-Engine to sort out all of the PDs present in the module. Mostly, this is used when displaying the options during character creation. To define it, use a statement like 'type = "race"' inside the PD.

name: The player-readable name of this PD. This is what will be displayed on the character sheet screen, and at character creation when choosing this type of PD. To define it, use a statement like 'name = "Human"' inside the PD.

desc: The player-readable name of this PD. This is what will be displayed at character creation when choosing this type of PD. To define it, use a statement like the example below inside the PD:

Code: Select all

    'desc = {
            "Humans are are everywhere.",
            "They are the basic race to which all others are compared.",
            "Average in ability, they can be any class.",
        }'
These 3 are required by the T-Engine to be filled in, or else the LUA parser tends to throw a fit and your module won't load. Everything else is optional for any given PD, and you can use any of these statements in any PD you see fit. You wouldn't want an empty PD, though, so make sure to inclue something.

descriptor_choices: This block is used to determine which other PDs are allowed to mix with this PD. In ToME, this is mostly used to define out subraces, but is also used for gender. You can apply this to as many other types of PD as you want, as seen in the example below, but you should make sure not to exclude PDs that have already been chosen by the player. A pretty complex example is below:

Code: Select all

    'descriptor_choices =
        {
            subrace =
            {
                Noldor = "allow"
                Avari = "allow"
                __ALL__ = "never"
            } --sets the default elven subraces as valid options for this entry
            sex =
            {
                __ALL__ = "never"
                Male = "allow"
            } --disallows females to exist for this descriptor.
        }'

stats: The stats block of code displays the stat adjustments for this PD. The baseline for each stat in ToME is 10, and ToME uses the standard Dungeons and Dragons 3rd edition core stats. If your module has other stats or removes any of these, then you will need to make sure your stats block matches up to your stats. You can adjust stats up or down, and you can choose to not alter a stat simply by leaving it out of your block. A typical stats block looks like this:
stats = { [A_STR]=-1, [A_INT]=3, [A_WIS]=2, [A_DEX]=3, [A_CON]=-1, [A_CHR]=5, }
This block reduces strength and constitution by 1, boosts intelligence and dexterity by 3, wisdom by 2, and charisma by 5. Without any other stat adjustments on other PDs, this character has a strength of 9, intelligence of 13, etc.

luck: Luck is a pretty simple stat, that partially determines the quality and likelihood of random drops, good dungeon floors, etc. It is limited by the T-Engine to a range between -30 and 30, defaulting to 0 without any modifier. To set luck for a PD, simply use a statement like 'luck = 5'

mana: The mana statement determines how much mana your character will gain in compairison to the baseline. This factor is a percentage multiplier, and defaults to 100. Barbarians, for example, have the statement 'mana = 50' in their PD, so they will gain 50% of the mana that a normal character would. Hermits, on the other hand, have 'mana = 120', and gain 20% more than normal characters.

blows: The blows statement lets you define attacks for a PD. They can set the number of attacks, the weight behind them (if you want one particular race to hit harder without giving them a strength modifier, for example), and a multiplier value, the purpose of which I'm uncertain. An example blows statement is below:

Code: Select all

    'blows =
        {
            mul = 4 --multiplier value
            num = 4 --4 starting blows per round.
            weight = 35 --weight factor of each hit.
        }'

life_rating: life_rating is a modifier to the hit points a character has. This can be a bonus or a negative penalty. Not defining one simply means that the character will get the standard hit points. To adjust it, just use a statement like 'life_rating = 21'.

experience: This is a factor that determines how fast a character will level. Each instance of this statement adds onto previous ones, though you can subtract by using negative values. Adjusting this is the most typical way to balance playable races in roguelikes, though in ToME some classes or backgrounds can have slight adjustments to it as well. This is 0 by default, which would prevent your character from ever gaining levels, and I have no idea how a total negative value would be handled, so it's best to make sure that it has a positive value after charcter creation. To set it, use a statement like 'experience = 150'.

age: The age block determines what the spread is for the age of a character. The first number is the minimum age, and the second is how many more years could be added onto that, determined randomly. unlike most stats, a second age block will replace the first, instead of adding to it. To set it, use a block like the example:
'age = { 14, 6 } --Character is at least 14, may be up to 20 years old.'

weight: This block determines the random weight for the player. Behaves very similarly to age, in that the first number is the base weight and the second is the maximum to add onto the base. Differs from age in that you need to set weights for all the genders available (The T-Engine defines a third gender, NEUTER, but never uses it anywhere, nor does the ToME module). An example use is:

Code: Select all

        'weight =
        {
            female = { 100, 20 } --At least 100, add 0-20 on top of that
            male = { 100, 25 } --At least 100, add 0-25 on top of that
        }'

height: Exactly like weight above, but for how tall the character is, rather than how heavy.

Code: Select all

        'height=
        {
            female = { 66, 4 } -- 66 inches minium, up to 70 inches.
            male = { 72, 6 } -- 72 inches minimum, up to 78 inches.
        }'

infravision: Sets the infravision radius, in map squares/tiles, of the PD. If this is 0, the PD does not grant infravision (in which case, you should just leave this statement out instead). Infravision can detect certain monsters that you couldn't otherwise see once they're in range.

levels: If you want this PD to provide specific benefits at certain levels, you'll want to define those in here. This can be used to set any flag you could set otherwise in the game, and adjust any particular number by an yamount you want. The example below details out a small sample of how to set a few different levels with a few different benefits:

Code: Select all

        'levels =
        {
            [ 1] = { RESIST = getter.resists{BLIND=100} SPEEDS=getter.speeds{WALK=-7} }
            [ 5] = { SEE_INVIS=true }
            [20] = { ESP=getter.flags{ORC=1 TROLL=1 EVIL=1 } }
            --At level 1, we are immune to blinding effects and have a reduced walk speed.
            --At level 5, the SEE_INVIS flag is set to true, letting us see invisible monsters.
            --At level 20, we get to detect monsters with the ORC, TROLL, or EVIL flags.
        }'    

body: body is used to set how many inventory and equipment slots a character has. In most cases this will be set in the base descriptor, but if you wanted to make a race that couldn't wear armor, you could use a negative value in the proper slot. If you don't want to modify a specific value, simply leave it out from the statement in the current PD. The base example for body is:
'body = { INVEN=23 MAINHAND=1 OFFHAND=1 BODY=1 OUTER=1 LITE=1 HANDS=1 TOOL=1 RING=2 HEAD=1 NECK=1 FEET=1 MOUNT=1 BACKPACK=1 }'
If you wanted to have a 'one-armed bandit' PD, you could use 'body = { OFFHAND = -1 }' to remove the offhand slot.

flags: The flags block allows you to set any flag in the game to the desired value. Unlike levels, these flags are always on from character creation, and don't require you to gain any levels to use them. These tend to be fairly simple, obvious things in ToME, but there's no reason it couldn't be. A simple, gender example:
'flags = { MALE=true } --set the MALE flag value'

skills: The skills block is used to adjust both the current skill value and the amount-gained-per-skill-point value of skills. To use this, first call out a skill by it's visible name, then set the amount to add to the skill, then the amount to gain per skill point. These values all add with other instances, and can be negative to provide a penalty. The example below shows both uses:

Code: Select all

        'skills =
            {
                ["Barehand-combat"] =  { mods.add(0)   , mods.add(200)  } 
                ["Boulder-throwing"] = { mods.add(0)   , mods.add(600)  }
                ["Stealth"]         =  { mods.add(-2000), mods.add(0)    }
                --0 point in barehand combat, .2 added to its level per skill point spent.
                --0 point in boulder throwing, .6 added to its level per skill point spent.
                --2 points removed from stealth, doesn't adjust the amount gained per skill point.
            }'

starting_objects: starting_objects gives the character the included items from this PD at character creation. In most cases, you'll end up calling out the items by T_VAL and S_VAL, but sometimes you'll also want to add in an amount of them for things like food or ammo, in which case you can set the 'min=' and 'max=' statements. If 'min' and 'max' are not the same, the character will receive a random value somewhere between them. This statement cannot remove items already given to a player.

Code: Select all

        'starting_objects =
            {
                { obj = lookup_kind(TV_LIGHT_ARMOUR, SV_SOFT_LEATHER_JACKET) },
                { obj = lookup_kind(TV_BOW, SV_SHORT_BOW) },
                { obj = lookup_kind(TV_ARROW, SV_BODKIN_ARROW) min=25 max=25 },
                --Gives the character starting armor, weapon, and ammo.
            }'

abilities: The abilities block lets you set when a PD grants an ability (specifically those defined with the new_ability() function). The abilities appear on the same page as skills in the character creation process. You have to call out the ability by name exactly as it appears on that screen, and then use the = character to set which level you will gain that. The example abilities block from the Ent race:

Code: Select all

        'abilities =
            {
                ["Tree walking"] = 1
                --Enabled the ability named "Tree Walking" at level 1.
            }'

The last 2 statements are ToME-specific statements, and are not part of the T-Engine. These would have to be handled by your module elsewhere within it's code, which is beyond the scope of this document.

titles: titles presents the list of titles to present to a character as they increase in level, usually only set for the character's class. This statement is used by the ToME module, and doesn't appear in the T-Engine of its own accord. The basic mage list is our example:
titles = { "Apprentice", "Trickster", "Illusionist", "Spellbinder", "Evoker", "Conjurer", "Warlock", "Sorcerer", "Ipsissimus", "Archimage", }'

allowed_gods: This statement appears in the caster classes in ToME, but doesn't appear to be implemented yet. It's usage is 'allowed_gods = "All Gods"' in all cases where it does appear.

Now that we'e gone over all of the things you can do with player descriptors, you should be able to go back to the examples at the very top and see what each one is doing when the player selects it. You should also have a pretty good idea of how to make your own player descriptors now, limited less by the engine and more free to use your creativity to bring your ideas to fruition with the proper application of these statements.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#2 Post by FACM »

T-Engine Module Development
Part 2: Towns, Dungeons, etc.: Maps

Now that we've covered player descriptors in part 1, it seems logical that we move on to the next part of the game a player will see: the map. These are a little more complicated than player descriptors, but that's mostly because the layout for setting up maps, towns, and dungeons is scattered around the ToME module, and it can be a little tricky to track down all the pieces.

The best starting point for this, in the ToME module, is /data/dungeon/features.lua. This file details out all the possible options for what each tile can be, and how things react to it. Here's a quick sample:

Code: Select all

new_feature_type
	{
		define_as = "FEAT_DIRT"
		name = "dirt"
		display = '.' color = color.LIGHT_UMBER
		priority = 20
		flags =
		{
			FLOOR=true CAN_RUN=true DONT_NOTICE_RUNNING=true SUPPORT_GROWTH=true
			SUPPORT_LIGHT=getter.lit(nil, color.UMBER)
		}
	}
This is probably the most common tile you'll see in a game. Appropriately, it's also probably the simplest. You can make them plenty more complicated, if you want to, as you can see in the example for fountains:

Code: Select all

	new_feature_type
	{
		define_as = "FEAT_FOUNTAIN"
		name = "fountain"
		on_walk = "The liquid here seems magical."
		display = '_' color = color.WHITE
		priority = 22
		flags =
		{
			FLOOR=true NOTICE=true REMEMBER=true CAN_RUN=true
			ON_MAKE =
				function(y, x, feat)
				local k_idx =
					rand_obj.rand_k_idx(object_level, 1,
										{features.fountain_potion_tester})

				if not k_idx then
					cave_set_feat(y, x, FEAT_EMPTY_FOUNTAIN)
					wiz_print("No suitable potions for fountain")
					return
				end

				local obj = new_object()
				object_prep(obj, k_idx)

				obj.number = rng.roll(3, 4)

					cave(y, x).flags[FLAG_FOUNTAIN] = obj
				end

			FEAT_DESC =
				function(y, x, feat)
					local c_ptr = cave(y, x)
					if not known_flag(c_ptr, FLAG_FOUNTAIN) then
						return
					end

					local obj = c_ptr.flags[FLAG_FOUNTAIN]

					if tag(obj) ~= TAG_OBJECT or obj.tval ~= TV_POTION then
						return "Terrain: fountain of something strange"
					end

					if is_aware(obj) then
						return "Terrain: fountain of " ..
							k_info(obj.k_idx).name
					else
						return "Terrain: fountain of " ..
							flavour.get_flavour(obj.k_idx) .. " liquid"
					end
				end
		}
	}
If you're curious as to what this code does, it's defining the fountain object so that when it's created, it grabs a random potion and fills the fountain with it. It then tells the player when it walks over if what potion it is, if they know it, or just the description of the potion if they don't. I'll come back to detailing out everything you can do with a feature later. Let's continue on with how everything fits together first.

Once the T-Engine's loaded up all the features, we have the pieces we'll need to make an actual map. A .map file is still a LUA script file, just named .map so that we know it will mostly be map data, and not other code. Inside /data/towns/bree.map, we see the necessary steps to create an area for players to move around in. The first line, 'map.define("bree.map")', just tells the engine we're defining out an area in this file. The file then brings up this line of code: 'map.import("towns/symbols.map")'. This tells the game that, beyond the global features we defined earlier, that we're also going to use the shared set of standard tiles in /data/towns/symbols.map. Whether you look in there, or keep reading bree.map, you'll see several similar lines of code, such as 'map.symbol{ symbol='a' feat=FEAT_SHOP info=3 building=store.INN_PRANCING_PONY }'. After all of the small code snippets, we finally get to the actual map, something like this:

Code: Select all

map.map
	{
	[[                          ]],
	[[--Gigantic map removed -- ]],
	[[                          ]]
	}

The convenient part of making maps is that you can just simply draw them out in the file. You have to keep each row of the map between then dual-brackets, but beyond that it's pretty easy to just type in what you want to see. Note that all of small little code snippets in the map file are how the game actually take the code in /data/dungeon/features.lua and turns it into visible tiles. So if we look back and see that we have 'map.symbol{ symbol='T' feat=FEAT_TREES info=3 }' in our map file, it means that anywhere we typed a T in our map.map{} block, it puts in the block of code that we called FEAT_TREES, which is below for reference:

Code: Select all

new_feature_type
	{
		define_as = "FEAT_TREES"
		name = "tree"
		on_block = "a tree blocking your way"
		display = '#' color = color.LIGHT_GREEN
		priority = 20
		flags =
		{
			SUPPORT_LIGHT=getter.lit(nil, color.GREEN)
			CAN_FLY=3 WALL=true NO_WALK=true
			NO_VISION=true DONT_NOTICE_RUNNING=true
			DESTROY_INTO=FEAT_GRASS CAN_PASS=getter.flags(features.trees_pass)
			DEAD_TREE_FEAT=FEAT_DEAD_TREE SUBST_TREE=true
		}
	}

You may have noticed that we were typing T in on our map to draw trees, but in the actual feature code the display property is '#'. Just so it's clear, this is to give the map designer better control over how the map appears. Several features can share the same display icon, but if we want to include both of them in a map, we'll have to give them different symbols on the map. The T-Engine will show the player the display icon for each feature when they're playing.

Now, let's say that you have a new town you want to put into the module. These are all done in /data/towns/towns.lua, in this block of code:

Code: Select all

local towns =
	{
		[1] = { "towns/bree.map", 	"towns/d_bree.map" }
		[4] = { "towns/lothlorien.map", "towns/d_lothlorien.map" }
		[5] = { "towns/rivendell.map", 	"towns/d_rivendell.map" }
	}
Here, we're giving each town entry 2 maps. The first is the main, original map. The second one, which is optional, is a destroyed version of the town.

I should also point out that there are quite a few shops defined already for ToME, and the relevant files for those are found in /data/shops/stores.lua. Most stores will be pretty similar, varying only in what they sell and buy, like this one:

Code: Select all

new_store
	{
		define_as = "store.STORE_ARMOURY"
		name = "Armoury"
		display = '2' color = color.SLATE
		max_obj = 24
		actions = { store.ACTION_NONE,	store.ACTION_NONE,
				store.ACTION_SELL,	store.ACTION_BUY,
				store.ACTION_NONE,	store.ACTION_EXAMINE }
		owners = store.OWNER_DEFAULT
		item_kinds =
		{
			{ chance=100 item="& robe~" },
			{ chance=100 item="& soft leather jacket~" },
			{ chance=100 item="& splint mail~" },
			{ chance=100 item="& metal scale mail~" },
			{ chance=100 item="& chain mail~" },
			{ chance=100 item="& leather cap~" },
			{ chance=100 item="& partial helm~" },
			{ chance=100 item="& set~ of leather gloves" },
			{ chance=100 item="& wooden shield~" },
			{ chance=100 item="& buckler~" },
		}
		buy =
		{
			TV_BOOTS,
			TV_GLOVES,
			TV_LIGHT_HELM,
			TV_HEAVY_HELM,
			TV_SHIELD,
			TV_CLOAK,
			TV_LIGHT_ARMOUR,
			TV_HEAVY_ARMOUR,
		},
	}
The only unusual thing to look out for with stores is that for stocked items, you have to use the full name of the items to sell, rather than calling out TVALs and SVAL like you usually do when dealing with items.

This covers creating preset towns. Creating an overworld, referred to as the wilderness map by the ToME module and the T-Engine, is done in /data/wild.terrain.lua and /data/wild/world.map, but follows most of the same ideas presented here. The way the properties of each tile is defined is a little different than on a town/dungeon tile, but that's because each wilderness tile can become it's own randomly generated map.

Now let's get to dungeons proper. In most cases, you won't be drawing out each floor of a dungeon, instead letting the T-Engine randomly generate it up for you. Here's a sample dungeon from /data/dungeon/dungeons.lua and it's related code:

Code: Select all

new_dungeon_type
	{
		define_as = "DUNGEON_ORC_MINES"
		name = "Orc Mines" short_name = "OMi"
		desc = "an entrance to the Orc Mines."
		mindepth = 1 maxdepth = 10
		min_player_level = 1
		size_y = 3 size_x = 3
		min_monsters = 14
		alloc_chance = 160
		fill_method = 0
		floors =
		{
			[FEAT_DIRT] = { 90, 60 }
			[FEAT_ASH] = { 10, 40 }
			[FEAT_DIRT] = { 0, 0 }
		}
		walls =
		{
			inner = FEAT_MOUNTAIN,
			outer = FEAT_WALL_INNER,
			[FEAT_MOUNTAIN] = { 100, 100 }
			[FEAT_WALL_EXTRA] = { 0, 0 }
			[FEAT_WALL_EXTRA] = { 0, 0 }
		}
		flags =
		{
			CAVE=true
			OBJ_THEME = getter.flags {
				THEME_TREASURE=5
				THEME_COMBAT=60
				THEME_MAGIC=10
				THEME_TOOLS=25
			}
		}
		rules =
		{
			[{100, "or"}] =
			{
				flags = {
					ALLOW_IN_ORC_MINES=true
					ALLOW_IN_EVERYWHERE=true
				}
			}
		}
	}

This is a pretty basic dungeon definition. In the first part, we draw up some basic information about the dungeon. Next we tell the engine which features to use for floors and walls, each with the percentage they should appear at. Then we set a few flags for our dungeon, and finally set the rules that determine which enemies can appear inside this dungeon. The last piece that needs done to make this dungeon playable is to define an entrance for it, which is done within a .map file. Currently (3.0.0alpha19), this is done for this dungeon in /data/wild/world.map: '--map.wild.entrance{ dungeon=DUNGEON_ORC_MINES y=14 x=78 }'. You may have noticed earlier, where there was similar code in bree.map:
'map.symbol{ symbol='{' feat=FEAT_MORE info=3 dungeon=DUNGEON_BARROW_DOWNS }'. If you want your dungeon to appear within a sub-map, and not the wilderness map, you'd do it this way and type the symbol in wherever you wanted the entrance to be. Note that FEAT_MORE is not actually defined anywhere within the modules files, but is a special name used by the T-Engine. The engine knows that dungeon entrances always use the '>' character, and that they have some extra handling compared to typical floors and walls.

Now that we've gone over the flow of how everything fits together for maps of all different types, I'll detail up a nice breakdown of all the parameters for each object:


new_feature_type:
Required:
define_as: The name you'll refer to this feature as in other parts of the code. This must be unique among feature names. The example: 'define_as = "FEAT_FOUNTAIN"'

name: the name for the feature the player will see when looking at it. This does not have to be unique, nor will you refer to the feature by this property in code. Example use: 'name = "fountain"'

display: The character to show in-game for this feature. Note that the character uses ' ' instead of " " like most LUA strings. Example: display = '_'

color: What color to draw the feature in. Colors are defined in /scripts/color.lua. Example: "color = color.WHITE"

flags: flags may not be truly mandatory, but there's a ton of options that can go into it. Look under 'Optional' to see all of them.

Optional:
on_walk: A message to display to the player when they walk onto this feature. 'on_walk = "The liquid here seems magical."'

priority: This sets a priority value that determines....... something. Example: 'priority = 22'

on_block: The message that pops up when you walk into the feature, if you don't want the standard one for walls. Example: 'on_block = "the side of a rocky burial mound blocking your way"'

shimmers: shimmers means that the tile will cycle through the list of given colors at random. The more often a color appears, the more likely it is to occur. Example:

Code: Select all

shimmers = { color.BLUE, color.BLUE, color.BLUE, color.BLUE, color.BLUE, color.BLUE, color.LIGHT_BLUE, }

flags: Sets the flags for this feature. There's a lot of flags that can apply, and your custom module might have others. Here's a list of what the ToME module uses, and what they mean, and given in a quick example form:
[Basics]
FLOOR=true (Treat this feature as a floor the player can walk over)
WALL=true (Treat this feature as a wall, and preven the player from moving through it)
NOTICE=true (Player can see this feature without having to walk into it first.)
DONT_NOTICE_RUNNING=true (Doesn't get noticed if you're running past/into it)
REMEMBER=true (Stays visible on the map after it's left the light radius)
CAN_RUN=true (This tile won't stop the player from running through it.)
PERMANENT=true (This feature can't be destroyed or moved)
TUNNELABLE={0,160} (How much time/effort it takes to tunnel through this wall.)
NO_WALK=true (Players can't walk over/through this feature)
NO_VISION=true (Player's can't see over/through this feature)
ATTR_MULTI=true (Not sure. Seems to appear around 'shimmers' most of the time.)
[Advanced]
NO_TUNNEL_MSG="You cannot tunnel a quest entrance." (What to display if the player tries to tunnel through this untunnelable feature.)
QUEST_CHANGER=true (Can be set to 'true' or '-1')
CAN_PASS=getter.flags{PASS_WEB=true, PASS_INCORP=1} (If the player has these flags, or higher values of these flags, they can walk through this wall.
all used values in ToME:
PASS_WEB, PASS_INCORP, PASS_ICE, PASS_STONE, PASS_MOUNTAIN, PASS_CLIMB, PASS_GASEOUS, PASS_LIQUID, PASS_TREES)
CAN_FLY=1 (If the player has the CAN_FLY flag at the same or higher value, they can fly over this feature)
CAN_CLIMB=1 (If the player has the CAN_CLIMB flag at the same or higher value, they can climb over this feature)
CAN_LEVITATE=true (If the player has the CAN_LEVITATE flag at the same or higher value, they can float over this feature)
SUBST_WEB=true (This feature is a spider web, used somewhere else in the ToME module)
SUBST_ROCK=true (This feature is sone of some kind, used somewhere else in the ToME module)
SUBST_MAGMA=true (This feature is a magma vein, used somewhere else in the ToME module)
SUBST_QUARTZ=true (This feature is a quartz vein, used somewhere else in the ToME module)
SUBST_TREE=true(This feature is a tree, used somewhere else in the ToME module)
SUBST_SAND=true(This feature is a sandwall, used somewhere else in the ToME module)
SUBST_RUBBLE=true(This feature is rubble, used somewhere else in the ToME module)
WATER=true (This feature is water, used by ToME elsewhere, probably monster generation/placement)
LAVA=true (This feature is lava, used by ToME elsewhere, probably monster generation/placement)
SUPPORT_LIGHT=getter.lit(color.YELLOW, color.LIGHT_DARK) (Illuminates itself, in the given color)
SUPPORT_GROWTH=true (This feature can spread around to other features.)
DESTROY_INTO=FEAT_ICE (After this feature has been destroyed, replace it with the specified feature)
DEAD_TREE_FEAT=FEAT_DEAD_TREE (Used specifically by trees, otherwise works like DESTROY_INTO)
EASY_DIG=true (Tunnelling is significantly simpler on this feature)
NO_RESTING = "Cannot rest here" (The message to display if the player tries to rest on this feature. Cancels resting.)
ALTAR=god.ERU (Used by the code for handling gods in ToME.)
DIGGER_MIMIC=FEAT_WALL_EXTRA (If the player tries to tunnel, treat this feature like the specified one. Used by illusory walls.)
TUNNEL_WORKING_MSG="You tunnel into the glacial wall... #BOh chilly#w." (Text to display when the user tries to tunnel through this feature, if you don't want the default to apply)
MONST_NO_PLACE=true (Monsters can't be spawned on this feature)
[Functions]
ON_MAKE = (Function, to determine specific properties when this tile is created. Used by fountains.)
FEAT_DESC = (If you use ON_MAKE and want to display the random property, use this instead of 'desc='. Used by fountains.)
MOVE_PRE_DIR = (Run this code before the player moves when they're standing on this feature. Used by icy floors.)
STAIR_DO = (If the player tries to move down/up on this feature, do this code. Used by the rift portals.)
can_enter = (Code to use to check if things can move into this tile)
MOVE_POST_DEST = (Run this code after moving off of this feature.)
MONST_CAN_ENTER = (Code to run if a monster enters or tries to enter this feature.)
MONST_CAN_ATTACK = (Code to run if a monster attacks this feature.)

attacks: If this feature is meant to do damage when something walks over it, what type and how much will be defined in the attacks block. The block for deep lava looks like this:

Code: Select all

attacks =
	{
		[{dam.FIRE, 1}] = { 2,-1 }
	}
new_dungeon_type:
[Required]
define_as: The name you'll use to refer to this dungeon elsewhere in the module. Example:'define_as = "DUNGEON_WILDERNESS"'

name: What to call the dungeon, when you've got the space to do so. Example: 'name = "Wilderness" '

short_name: What to call the dungeon when you have 3 letters to do so with. Example: 'short_name = "Wdn"'

desc: A short description of the dungeon. Example: 'desc = "a way to the Barrow-Downs."'

mindepth: Used to determine the weakest enemies that can appear, based on their level property. Example: 'mindepth = 1'

maxdepth: Used to determine the strongest enemies that can appear, based on their level property. Example: 'maxdepth = 10'

min_player_level: If the player's not at this level or higher, make them walk back out with a 'You don't feel experienced enough to enter' message. Example:'min_player_level = 1'

size_y: An approximate size for the vertical axis of each floor. Example: 'size_y = 3'

size_x: An approximate size for the horizontal axis of each floor. Example: 'size_x = 3'

min_monsters: The fewest monsters to place within a level of the dungeon. Example: 'min_monsters = 14'

alloc_chance: Not sure. Possibly a chance to generate random loot across the dungeon. Example: 'alloc_chance = 500'

floors: What features to use, and the percentage to use them, within the dungeon as floor tiles. Not certain what the 2 numbers mean. Example:

Code: Select all

floors =
	{
		[FEAT_DIRT] = { 78, 0 }
		[FEAT_GRASS] = { 18, 95 }
		[FEAT_FLOWER] = { 4, 5 }
	}

walls: What features to use, and the percentage to use them, within the dungeon as wall tiles. Not certain what the two numbers mean.

Code: Select all

	walls =
	{
		inner = FEAT_MOUNTAIN,
		outer = FEAT_TREES,
		[FEAT_TREES] = { 34, 100 }
		[FEAT_MOUNTAIN] = { 66, 0 }
	}

rules: Sets the rules that have to be followed when choosing monsters for this dungeon. In ToME, this simply means picking monsters within mindepth and maxdepth level and then pulling out monsters with the flags in the rules block. You can use multiple rules if you want, as long as the numbers used equal up to 100. The number is a percentage, and the following string is a boolean command ("and", "or", "not or", "all") to use to determine how to treat the flags. You can also call out races to allow by symbol, as in 'races = {"w"}' Example:

Code: Select all

	rules =
	{
		[{100, "or"}] =
		{
			flags = {
				ALLOW_IN_BARROW_DOWNS=true
				ALLOW_IN_FOREST=true
				ALLOW_IN_EVERYWHERE=true
			}
		}
	}

flags: Again, there's lots of choices to use for flags, so they're all detailed out in the 'Optional' section.



[Optional]
'__index__ = 0': This is only used by the wilderness map, so you probably shouldn't copy it for other maps.
entries: This sets a position for you on the wilderness map, should you leave the top or bottom part of the dungeon or recall out All sub-parts of the entries block are optional, and you don't have to use both. Example:

Code: Select all

	entries = {
		top = {y = 21, x = 35}
		bottom = {y = 50, x = 62}
	}

fill_method: No idea. 0, 1, 2, and 3 are used in the ToME module. Not used in every dungeon. Example: 'fill_method = 0'.

levels: Use the levels block if you have a pre-made map you'd like to have set as a certain level in the dungeon. Levels can have their own sub-flags and properties, but I wouldn't stray far from the example given for each level. This example is from Mount Doom and only covers 1 floor, but you can set as many floors as you want in it:

Code: Select all

levels =
	{
		[99] =
		{
			name = "Mt Doom"
			level_map = "maps/mount_doom.map"
			desc = "You finally reach the top of Mount Doom, here must lie the Great Fire."
			flags = {
				SAVE_LEVEL=true
				NO_GENO=true NO_NEW_MONSTER=true NO_TELEPORT=true
				-- Fates here might prevent player from destroying
				-- the One Ring.  Probably won't happen, but let's
				-- be paranoid
				NO_EXEC_FATES=true
			}
		}
	}

flags: Sets the flags for this feature. There's a lot of flags that can apply, and your custom module might have others. Here's a list of what the ToME module uses, and what they mean, and given in a quick example form:
[Basic]
NO_SURFACE_ENTRY=true (This dungeon cannot be entered from the wilderness map, only a town map.)
AMBIENT_MUSIC = "barrow-downs" (Play the selected music file while the player in in this dungeon.)
SURFACE_LITE=true (This dungeon has the same light as the wilderness map, so it's much easier to see in this map during the day.)
FLAT=true (Not sure.)
NO_SHAFT=true (Avoids creating long hallways)
OBJ_THEME (Sets what type of gear is going to be found, and the percentage for each type. Percent must equal 100. See the example:)

Code: Select all

OBJ_THEME = getter.flags {
			THEME_TREASURE=25
			THEME_COMBAT=25
			THEME_MAGIC=25
			THEME_TOOLS=25
		}
DUNGEON_GUARDIAN = MONSTER_BARROWS_KING (The monster with this flag is the boss monster for this dungeon.)
FATES_OK=true (Fates are processed while in this dungeon. Used by the ToME module somewhere.)
CAVE=true (This dungeon is a cave. Used in ToME code somewhere.)
LAVA_RIVER=true (A lava river will show up somewhere in the dungeon.)
WATER_RIVER=true (A standard river of water will appear somewhere in the dungeon.)
CAVERN=true (This is a cavern, like a cave but bigger.)
DOUBLE=true (no idea)

[Advanced]
NO_STREAMERS=true (No idea.)
CIRCULAR_ROOMS=true (Makes rooms round instead of square.)
NO_DOORS=true (doesn't put doors in this dungeon.)
generator: Calls out a specific level generator to use, instead of the stoc one. Example: 'generator generator = "gen_land_of_rhun"'
ENTRANCE_MIMIC: Make the entrance to this dungeon look and act like the designated feature. Example: 'ENTRANCE_MIMIC = FEAT_SWAMP_POOL'
AMBIENT_SOUNDS_FREQUENCY = sets a period of time between random ambient sounds. Requires the next flag to do anything. Example: 'AMBIENT_SOUNDS_FREQUENCY = 40'
AMBIENT_SOUNDS: Sets the given sound files to be played every AMBIENT_SOUNDS_FREQUENCY seconds. Example: 'AMBIENT_SOUNDS = getter.array{ "creaking-door" "creaking-door" "thunder" }'
TERRAIN_IGNORE: Not entirely certain how this works. It's used in a dungeon that has the terrain feature as part of the walls. The code is:

Code: Select all

TERRAIN_IGNORE = getter.array {
			[FEAT_WEB] = true
		}
NO_EASY_MOVE=true (Prevents any sort of fast movement or moving through walls in this dungeon)
NO_RECALL=true (Prevents magical teleportation out of this dungeon.)
NO_RECALL_OUT="As an astral being you can't recall." (Presents a custom message to players that try to recall out. Not used much in ToME.)
WATER_BREATH=true (Requires the player to be able to breathe water to enter)
NO_DESTROY=true (no idea.)
EMPTY=true (Not sure. Empty dungeons seem wrong.)
RANDOM_TOWNS=true (Creates randomized towns on some floors of the dungeon.)
HOT=true (No idea what effect this has on gameplay)
SAND_VEIN=true (Creates sand veins?)
FORCE_DOWN=true (Prevents the player from going up to previous levels?)
FORGET=true (Keeps you from remembering where you've walked before)
LEVEL_BONUS: Grants the following values to the player while in the dungeon. Seems to be used mostly for debugging tests, but could be useful in a module as well. Example:

Code: Select all

			LEVEL_BONUS = getter.flags {
				SEE_INVIS = 1
				PASS_WALL = getter.flags {	PASS_INCORP = 10000	}
			}
NO_NEW_FATES=true (Player cannot gain new fates in this dungeon)
NO_EXEC_FATES=true (No player fates will come to pass in this dungeon.)
NO_NEW_QUESTS=true (Player can't gain new quests in this dungeon.)
NO_GENO=true (Prevent genocide effects in this dungeon)
NO_NEW_MONSTER=true (don't spawn new monsters in this dungeon.)
SAVE_LEVEL=true (This level is restored if you come back later, as it was when you left.)
OBJ_FOUND = "during the Orcish ambush" (Used if you want to make items acquired in this dungeon have a different string explaining where you found it.)

[Functions]
STAIR_POST = (Run this after moving through the stairs in this dungeon.)
ROOM_DEFINITION = (Changes around rooms after level generation by running this code.)
LEVEL_GENERATE_POST = (Run this code after creating a level. Typically used to make some visual alterations to the dungeon floor.)
LEVEL_END_GEN = (Seems to be the same time as LEVEL_GENERATE_POST, but doesn't quite work the same way.)
LEFT_DUNGEON = (Code to run once you've escaped the dungeon. STAIR_POST also does this, assuming you're making sure the player actually went out of the dungeon.)

new_store:
define_as: The name you'll refer to the store with throughout the code. Example: 'define_as = "store.STORE_GENERAL"'

name: The player-visible name. Example: 'name = "General Store"'

display: The symbol to use on the map. Example: "display = '1' "

color: The color to draw the map symbol in. Example: 'color = color.LIGHT_UMBER'

max_obj: The maximum amount of items to sell in the shop. Example: 'max_obj = 24'
Actions: The allowed actions the player can do in the store. Example:

Code: Select all

	actions = { store.ACTION_NONE,	store.ACTION_NONE,
		    store.ACTION_SELL,	store.ACTION_BUY,
		    store.ACTION_NONE,	store.ACTION_EXAMINE }

owners: If you want to customize the owner, and have them defined, you set them here. Example: 'owners = store.OWNER_DEFAULT'

item_kinds: This is where you set what items the store will sell. The chance value is the percentage of the time that item will be available, and the item must refer to the the name property of an item, not a TVAL or SVAL like most item references. Example:

Code: Select all

item_kinds =
	{
        { chance=100 item="& Ration~ of Food" },
        { chance=100 item="& Hard Biscuit~" },
        { chance=90 item="& Strip~ of Venison" },
        { chance=70 item="& Pint~ of Fine Wine" },
        { chance=80 item="& Pint~ of Fine Ale" },
        { chance=60 item="& Shovel~" },
        { chance=50 item="& Pick~" },
        { chance=100 item="& Iron Spike~" },
		{ chance=100 item="& torch~ #" },
		{ chance=100 item="& torch~ #" },
		{ chance=100 item="& torch~ #" },
		{ chance=100 item="& torch~ #" },
		{ chance=100 item="& torch~ #" },
		{ chance=100 item="& filthy rag~" },
		{ chance=100 item="& filthy rag~" },
		{ chance=100 item="& filthy rag~" },
		{ chance=25 item="& lantern~ #" },
		{ chance=100 item="& flask~ of oil" },
		{ chance=100 item="& flask~ of oil" },
		{ chance=100 item="& flask~ of oil" },
		{ chance=100 item="& flask~ of oil" },
		{ chance=100 item="& flask~ of oil" },
		{ chance=100 item="& bodkin arrow~" },
		{ chance=100 item="& bodkin arrow~" },
		{ chance=100 item="& sling stone~" },
		{ chance=100 item="& sling stone~" },
		{ chance=100 item="& cotton cloak~" },
	}
	
buy: Sets what item types the shop will pay for, using their TVAL. Example:

Code: Select all

buy =
	{
		TV_CORPSE,
		TV_FOOD,
		TV_LITE,
		TV_FUEL,
		TV_SPIKE,
		TV_SHOT,
		TV_ARROW,
		TV_CLOAK,
		TV_BOTTLE,
	}

flags: Yes, stores too can have flags. They use far fewer than most things, though.
MUSEUM=1 (Stores objects, need to look into this code deeper.)
FREE=1 (?)
STORE_MAINTAIN_TURNS=0 (Store refreshed their inventory every time this number of turns passes. 0 sets this store to never refresh their inventory, such as the players home.)
RANDOM=1
LEVEL (Function)

map.symbol:
Only a few choices for this case:
[Required]
symbol='{' (The symbol used to place this on the map.)
feat= (The feature this symbol represents. If it doesn't call out a defined feature type, it has to be one of the below options:)
FEAT_MORE (This is a dungeon entrance, requires the dungeon property.)
FEAT_SHOP (This is the entrance to a defined store. Required the building property.)
info=3 (not sure. 3 seems to be the most common and/or only value.)

[Optional]
monster=RACE_FARLINA_SNOWFOOT (This tile is a creature, of the specified tag. the feat property will be the tile under the creature.)
dungeon=DUNGEON_BARROW_DOWNS (This leads to the specificed dungeon.)
building=store.INN_PRANCING_PONY (Which store this shop is.)
flags={ TOWN_HOT_SPOT=true } (NPCs move towards the TOWN_HOT_SPOT)
Last edited by FACM on Thu Jan 28, 2010 2:58 am, edited 2 times in total.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#3 Post by FACM »

T-Engine Module Development
Part 3: Items

In the T-Enigne, there's 2 key values for items: Type Values (T_VALs) and Subtype Values (S_VAL). Likewise, in the ToME mdoule there are 2 central points for making items: /data/items/items.lua and /scripts/object.lua. object.lua is where all the item T_VALs and S_VALs are declared, and items.lua is where all the properties of the items are filled in. So, look at how an axe would be defined. First, you set up the values in objects.lua:

Code: Select all

define_objects_categorization
	{
	--snipped--
		{ "AXE",
		{
			"FLINT_AXE",
			"BISCAYAN_AXE",
			"WOODSMAN_AXE",
			"EXECUTION_AXE",
			"RHUNIC_AXE",
			"LABRYS",
			"DANE_AXE",
			"HATCHET",
			"BARUK_KHAZAD"
		},
	},
	--snipped--

That defines our T_VAL as "AXE" [the TV_ part will be added automatically] and sets 9 S_VALs for it. Then, in items.lua, we have:

Code: Select all

new_item_kinds
	{
	--snipped--
	[TV_AXE] =
	{
		color = color.WHITE
		name = "axes"
		desc =
		{
			"A hafted weapon with a wide blade on one end.",
		}
		symbol = '/'
		defaults = {
			display = '/'
			color = color.WHITE
			flags = {
				WIELD_SLOT = INVEN_MAINHAND
				WEAPON = obvious(true)
				WEAPON_ENCHANT = object_enchant_drop.WEAPON_ENCHANT_RELATIVE_TH | object_enchant_drop.WEAPON_ENCHANT_RELATIVE_TD
				SKILL = getter.skill("Axe-mastery")
				DAM_COMPUTE = combat.COMPUTE_ROLL
				HARMED_BY = getter.resists{ACID=true FIRE=true}
				ID_SKILL  = getter.skill("Weaponmastery")
			}
		}

		[SV_FLINT_AXE] = {
			name = "& flint axe~"
			desc =
			{
				"A flinty axe"
			}
			level = 5 weight = 220 cost = 250
			allocation = { {5,1} }
			flags = {
				MUST2H = obvious(true)
				DAM = getter.damages{SLASH = {1,9}}
			}
		}
		--snipped--

Here, we see that anything assigned to the T_VAL is a default, and applies to all the S_VALs unless the S_VAL has its own declared. Pretty much everything item-wise works with this setup, where there's the T_VAL that works as a group and S_VALs are memebers of that group. Note that if you're creating your own new T_VAL group, that you'll also have to add some code into /data/items/descs.lua, which tells the game engine how to describe an item when the player examines it and does some sanity checking to make sure the item is reasonable for that group (making sure boots don't shoot arrows, etc).

The big exception to this is artifacts, in /data/items/artifacts.lua, which get treated as their own special case and don't have a T_VAL or S_VAL. The first, most common artifact will be our example:

Code: Select all

new_artifact
	{
	define_as = "ART_PHIAL"
	name = "& glowing phial~ of galadriel"
	unknown_name = "& glowing phial~"

	desc = {
		"A small crystal phial, with the light of Earendil's Star ",
		"contained inside.  Its light is imperishable, and near it ",
		"darkness cannot endure.",
	}
	base_object = { tval=TV_LITE, sval=SV_LITE_FEANORIAN }
	color = color.YELLOW
	level = 20 rarity = 10 weight = 10 cost = 10000
	flags =	{
		INSTA_ART = true LITE = obvious(4)
		LITE_SUN = obvious(true) LUCK = 4
		SKILL_BONUS = getter.skills{["Sneakiness"] = 4000}

		WIELD_POST=function(obj) message(color.GREEN, "You feel the courrage to go in the dark places of the world where nobody else will go.") end
		TAKEOFF_POST=function(obj) message(color.RED, "Your confidence in yourself seems to wither away just by thinking of dark places.") end

		-- Activation
		DEVICE_USE = obvious(getter.single_device_spell
		{
			DEVICE_TYPE = "activate"
			-- ACT_SPELL   = "activations.artifacts.galadriel"
			ACT_SPELL   =
				function(obj, spell_flags, skill,
						 mana_cost, who)
					if not obj then
						return "lite", "to lite up the room"
					end
					message(color.YELLOW, "It wells with clear light...")
					lite_room(player.py, player.px)
					return true
				end

			-- Every 5 - 15 turns (4 + 1d11)
			ACT_COST_BASE = 4 ACT_COST_DICE=1 ACT_COST_SIDES=11
		}) -- DEVICE_USE

		-- Mana and autocharging needed for activation
		MANA_CURR = obvious(15) MANA_MAX = obvious(15)
		AUTO_CHARGE_TYPE=obvious(obj_mana.auto_charge_types.TIME)
		AUTO_CHARGE_RATE=obvious(1) AUTO_CHARGE_WIELD=obvious(true)
	}
	}

In all fairness, artifacts really are just complicated items. They'll typically do something significantly more special than any standard item, which means a bit of dedicated code, as you can see above. They don't HAVE to be, but typically you'll want a reason for a set artifact to be pretty special, otherwise random items will eventually overtake it.

You can also have artifacts that belong to a set, which would grant extra powers if you equipped both items. As of alpha19, there's only one partially complete set in ToME:

Code: Select all

new_artifact_set
	{
	name = "Light Bringers"
	desc = "It is from a group of Elven items that strive to bring light to the world."
	artifacts =
	{
		-- Phial of Galadriel
		[ART_PHIAL] =
		{
			[2] = { STATS = getter.stats{[A_WIS]=3 [A_CHR]=4} RESIST = getter.resists{FIRE = 90 DARK=70} }
		}
		-- Sting
		[ART_NARYA] =
		{
			[2] = { STEALTH=2 REGEN=1 }
		}
	}
	}

That's the entirety of /data/items/sets.lua right now. We can see that you call out the set, the items that belong to it, and then what additional properties each items gets when you have multiple items equipped. We can't see it here, but if there were multiple items in this set, there'd likely be entries for [3] with more powers, and then you'd see that the abilities on [2] are tied to each item. To try and clarify this, pretend there's a third item in this set. If we had ART_PHIAL and ART_NARYA on, each item would get it's bonuses above. If I took off ART_NARYA and wore our third item, ART_PHIAL would regain all of its bonuses under [2], but we wouldn't get the STEALTH=2 or REGEN=1 from ART_NARYA. The only thing we can't determine from this code alone is if further entries in the list add to or replace earlier values.

The last note on artifacts: In T-Engine 3.0.0alpha19, there aren't random artifacts. The ToME Module has a placeholder empty file for supporting them, but they're not in yet. If you're doing a module where you want random artifacts to be generated, then you'll have to work up your own code for that for now.

I skipped straight from normal items to artifacts, so I need to step back now and explain out how magical items work. In /data/items/egos.lua, there are all of the magical properties that can randomly be applied to regular items. The first example:

Code: Select all

new_item_ego
	{
	name = { "before", "fiery" }
	item_kinds =
	{
		[TV_POLEARM] = true
		[TV_AXE] = true
		[TV_HAFTED] = true
		[TV_KNIFE] = true
		[TV_SWORD] = true
	}
	level = 1
	allocation = 100
	value_add = 2000
	rating = 20
	flags =
	{
		[100] =
		{
			RESIST = obvious(getter.array{[dam.FIRE] = 66})
			BRAND = obvious(getter.array{[dam.FIRE] = 3})
			LITE = obvious(1)
		}
	}
	}

This is the 'Fiery' brand enhancement. We see which T_VALS it is allowed to be applied to, a couple of properties to determine how often it appears, and then the flags section, which details out the special abilities of this enchantment. The [100] is a percentage (I think), so if you wanted some enchantment of yours to always have X powers and only have Y powers half the time, you could add in a [50] block beneath the [100] block and give it a list of powers that won't always appear.

The last overarching item-related subsystem handles sentient items. Sentient items gain levels as they gain exp, and can gain random enchantments that you'd defined for them to gain. Enabling them is not immediately clear, but you need to set at least 3 flags for an item to properly fit the sentient system:

SENTIENT = obvious("melee")
LEVELS = true
OBJ_EXP_MON_KILL_ACTIVE = true

The first flag sets the item to be clearly sentient, and to use the "melee" subsystem for handling the item. There can be multiple subsystems for sentient items, since not each magical property makes sense for each item. Armor doesn't usually add to damage, and you might want to set up multiple systems if you don't want each sentient weapon to work the same way. The second flag indicates to the game that this item will level up. This flag alone will enable a leveling system buried somewhere within the module, but the sentient item system over-rides that. The last flag indicates that the item gaines exp when it's used to kill a monster. If you kill a monster with a spell, or a different weapon, it doesn't contribute towards its leveling. Also, this won't let armor level up, unless you somehow are wielding armor as your weapon.

Now, the detailed explanations of everything:

new_item_kinds:
[TV_GROUP]: Starts off the block for the type value/category of items. Only differs from the SV_SUBITEM block because it requires the 'defaults' block and the SV_ entry cannot have it. Both groups otherwise use the same tags.

[SV_SUBITEM]: Starts the block for the specific item. If a value isn't defined here for an item, it takes the property from the 'defaults' block of the TV_GROUP it belongs to. Both groups otherwise use the same tags.

name: The player-visible name. Example: name = "polearms"
desc: The player-visible description. TVAL descriptions are displayed above SVAL descriptions when examining an item. Example:

Code: Select all

desc =
		{
			"Polearms are long, heavy weapons designed for use in a formation.",
		}

color: The color the item appears when on the dungeon floor. Example: color = color.SLATE

[These 2 are separate entries, but I'm not sure what the difference is.]
symbol: The icon to use for the item. Uses ' ' instead of " " tags around the character. The \ character must be used as '\\' to be used by the engine. Example: symbol = '/'
display: The icon to use for the itme. Uses ' ' instead of " " tags around the character. The \ character must be used as '\\' to be used by the engine. Example: display = '/'

level: The level of the item, determines what dungeon-level the item can appear on. example: level = 5
weight: How heavy the item is, in some unit. Low strength prevents a player from carrying lots of heavy things. Example: weight = 160
cost: How much the item costs from a shop. Shopkeepers buy it for a fraction of this value. Example: cost = 250
allocation: Not sure. First number seems to always match the level property, second varies. Use: allocation = { {5,1} }

ac: Armor class granted by this item. Negative numbers may provide a penalty. Example: ac = 4
to_h: This item's adjustment to a character's to hit rating. Can provide a penalty by being negative. Example: to_h = -2
to_d: This item's adjustment to a character's to damage rating. Can provide a penalty by being negative. Example: to_d = 4

dice: No idea. Currently seems to be used on spellbooks. Usage: dice = {1, 1}

flags: Again, lots of options here. A list of all the flags the ToME module uses, though more are always possible in your module:
WIELD_SLOT = INVEN_MAINHAND (Which inventory slot this item takes up, if any.)
WEAPON = obvious(true) (This item is a weapon)
WEAPON_ENCHANT = object_enchant_drop.WEAPON_ENCHANT_RELATIVE_TH | object_enchant_drop.WEAPON_ENCHANT_RELATIVE_TD (Sets random weapon enchant levels, appropriate to the dungeon level. WEAPON_ENCHANT_ values can be ABSOLUTE_TH, ABSOLUTE_TD, RELATIVE_TH, RELATIVE_TD)
SKILL = getter.skill("Polearm-mastery") (This item uses a specific skill to determine effects like damage/hit/etc)
HARMED_BY = getter.resists{ACID=true FIRE=true} (Taking damage from these damage types can break this item)
ID_SKILL = getter.skill("Weaponmastery") (Use this skill to psuedo-ID this item.)
MUST2H = obvious(true) (This item can't be used with an offhand weapon.)
DAM = getter.damages{CRUSH = {1,3} PIERCE = {1,7} } (Set the damage type[s] and amount[s]. First value is number of dice, second is the number of sides.)
COULD2H = obvious(true) (This weapon is big enough to use two handed, but doesn't have to be.)
DAM_COMPUTE = combat.COMPUTE_ROLL (How to work out damage. Typically appears in the 'defaults' block. Doesn't appear on all weapons.)
DUAL_WIELD = obvious(true) (This weapon can be wielded in both main and offhand slots. WIELD_SLOT must be INVEN_MAINHAND for this to work, as INVEN_OFFHAND makes it an off-hand only item.)
AMMO = TV_ARROW (This item fires ammuntion, and it uses the given T_VAL item type as ammuntion)
SHOW_COMBAT_MODS = true (Displays the multiplier this weapon adds to ammo. Only used on ranged weapons.)
MULTIPLIER = 2 (Multiplies ammo damage by this amount. Only used on ranged weapons)
BASE_RANGE = 5 (Baseline distance in tiles on how far the weapon can be used effectively)
BREAKAGE_CHANCE = 25 (Used on ammo, has a chance on hitting something to break.)
MAX_QUANTITY = 30 (How many of this item can fit in one stack.)
SPELL_IDX = getter.spells{"Demon Whip","Demon Madness","Demon Field"} (This item grants these spells to the owner. Uses the spell's visible name, not the LUA variable name for the spell.)
SPELLBOOK = true (This item can be used to learn new spells)
WIELD_CAST = true (This item must be wielded to cast spells from it.)
ARMOR_ENCHANT = object_enchant_drop.ARMOR_ENCHANT_RELATIVE_AC | object_enchant_drop.ARMOR_ENCHANT_RELATIVE_DR (As WEAPON_ENCHANT, but for AC and DR instead of TH and TD)
RESIST = obvious(getter.resists{ CRUSH=5 SLASH=5 PIERCE=5 }) (Sets the damage resistances of this item. Usually found on armor.)
ABSORB = obvious(getter.absorbs{ CRUSH=9 SLASH=9 PIERCE=9 }) (Works like RESIST, but used on shields instead of armor.)
SPELL_FAILURE = obvious(1) (This item provides a spell failure chance when equipped.)
ALWAYS_MATERIAL = true (This item always needs to be material OR this item always needs to be made of a specific material.)
SKILL_BONUS = obvious(getter.skills{["Sneakiness"] = -2000 }) (This item provides a bonus [or penalty if negative] to a skill. Values is measured in thousandths of a whole skill point.)
SPEEDS = obvious(getter.speeds{WALK=-3}) (This item adjusts one or more of the player's speeds. Can be positive or negative.)
NORM_ART = getter.object( 70 , 51 ) (No idea. May involve artifacts. Used on one scrolls in ToMe.)
FULL_NAME = true (Displays out the item's full name always. Used on one scroll in ToME.)
FOUNTAIN = true (Used on potions in ToME, indicates the items effects can be done by fountains. Can be set to false to prevent this item from appearing in fountains, which is nice for some of the more harmful potions.)
FOOD_VALUE = 0 (Increases your fullness by this amount when eaten.)
IGNORE = getter.resists{ COLD = true FIRE = true ACID = true ELEC = true } (This item ignores these damage types entirely.)
EASY_KNOW = true (This items stats are automatically identified.)
LITE = obvious(1) (This item is a light source, with the given radius.)
FUEL_MAX = 4000 (The most fuel this item can hold)
FUEL = obvious(2000) (How much fuel this item has currently.)
FUEL_LITE = obvious(1) (This item can be fuel for items with a FUEL_SOURCE value of the same type.)
FUEL_SOURCE = obvious(1) (This item is a source of fuel for lanterns/torches. Only items with the same FUEL_LITE value can refuel this item.)
ATTR_MULTI = true (Not certain.)
DECAY = true (Used on corpses to indicate they decay to other items/nothing over time.)
ON_EAT = food.eat_bland (Which description to use when eating this item, or other effects)
GOLD_VALUE = 24 (This item is currency, worth the given amount. ToME appears to use several items with set values for currency instead of a couple with ranges of random values.)
METAB_PCT = obvious(50) (Changes your metabolism speed by the given percent)
FLAVOUR = "pitch black" (Forces this item, if the TVAL has random flavors, to always use the given flavor descriptor.)
INSTA_ART = true (No idea. May involve artifacts. Only used in areas not rewritten in the ToME module.)
SPECIAL_GENE = true (This item will not be randomly generated, and must appear as either a set loot item, wishes, quests, etc.)
NUTRI_MOD = obvious(5) (Not sure. May overlap in functionality with METAB_PCT to some extent. Only used in areas not rewritten in the ToME module.)
FLY = obvious(1) (Grants player the ability to fly with the given value when worn.)
SHOW_AC_MODS = true (Always show the to_a value for the item, even if it's +0)
SHOW_COMBAT_MODS = true (Always shows to_h and to_d for the item, even if one/both are +0)
LOCKED = obvious(true) (This item is locked, usually used on chests and other openable items.)
CLOSED = obvious(true) (used on chests, forces them to always be closed)
OPEN = true (Used only on a testing item, indicates it can be closed)
AMOUNT = 2 (Not sure. Used on chests. May indicate number of items to generate in the chest.)
PARSE_WISH_STR = sticks.wand.parse_wish_str (Used on wands/orbs, handles item if it's a wishing wand.)
GRANT_WISH = sticks.wand.grant_wish (Used if the item is a wishing wand/orb)
GEM_SOCKET = obvious(true) (This item can hold a gem to provide charges for use.)
MANA_CURR = getter.mbonus(30, 200) (The current mana the item has. Typically used on gemstones for wands.)
GEMSTONE = true (This item is a gemstone that goes into a wand.)
RUNE_DAM = obvious(dam.FIRE) (Indicates this item is a rune, and that it makes effect use this damage type.)
RUNE_HARDNESS = 1 (Hardness value of a rune.)
RUNE_DAM_NAME = "elemental" (Use this name for the damage type instead of the default for the type in RUNE_DAM)
RUNE_DAM_INFO = function() end (Tells you what the rune will do, if the rune is of an appropriate type.)
RUNE_PROJ = PROJECT_GRID (No clue. Involves rune spells.)
RUNE_PROJ_NOT = (PROJECT_THRU | PROJECT_STOP | PROJECT_KILL) (No clue. Involves rune spells.)
RUNE_EXCL_SHAPES = getter.flags{RUNE_SHAPE_VIEW=true} (This rune can't be used on spells with the given shapes.)
RUNE_SHAPE = obvious(getter.runeshape("ARROW")) (What type of attack the rune spell will be. Used on secondary runes.)
RUNE_WEIGHT = 2 (How much the rune weights)
DIG_POWER = obvious(getter.flags{ SUBST_ROCK=20 SUBST_SAND=20 SUBST_TREE=20 SUBST_ICE=20 }) (This item can be used to dig through these surfaces at the given power levels. Higher values dig through faster.)
EQUIPMENT_SIZE = obvious(getter.body_parts{ INVEN=3 }) (Adds the given type and number of inventory slots while worn. Typically used for backpacks and quivers.)

[Typical Functions]
ON_READ (Used by scrolls and other items that use the Read hotkey.)
ON_QUAFF (Used by potions and other drinkable items using the Quaff hotkey.)
ON_MAKE (Sets properties of this item at the time its made. Usually used for random properties that can't get set with just getter.random())
OBJECT_VALUE (If your item has random properties set via ON_MAKE, and you want their effects to adjust the value, use this function.)
PLAYER_DESTROY_POST (Run this code after this item is destroyed, either explicitly or after being consumed.)
OPEN_POST (What happens after an item, like a chest, is opened.)
TAKEOFF_PRE (Effects done before taking off a piece of gear)
TAKEOFF_POST (Done after an item has been unequipped.)
WIELD_PRE (Code done before equipping an item.)
WIELD_POST (Executed after putting on an item.)
DEVICE_USE (This item is usable, and does these effects when used.)


new_artifact:
The bad news is that I don't have a full write-up for artifacts to explain them out. The good news is, I don't really need to do one. Artifacts are more or less just extra-powerful items that aren't stored in the same array as normal items. The only new property that ToME uses is 'unknown_name', which just shows you the name to refer to the item as until it's been identified properly. Note that artifacts do need a base item type as well, so you do need to fill out a TVAL and SVAL for them, and then use base_object to call it out, like 'base_object = { tval=TV_LITE, sval=SV_LITE_FEANORIAN }'. Everything else about artifacts is the same as for standard items, artifacts just tend to have at least a couple special abilties.

new_item_ego:

name: Both the name of the ego effect, and where in the item's name it goes. EX: 'name = { "before", "fiery" }'

item_kinds: What TVALs this ego can be applied to. You can call out SVALs as well if you want to limit your ego to certain items.Ex:
'item_kinds =
{
[TV_POLEARM] = true
[TV_AXE] = true
[TV_HAFTED] = true
[TV_KNIFE] = true
[TV_SWORD] = true
}'

To limit it to certain SVALS of the given TVALs, use code lie '[TV_HEAVY_ARMOUR] = {SV_DRAGON_PLATE_MAIL, SV_PLATE_MAIL}' for each TVAL you want.

ego_group: ego_group puts this ego into the given category. An item should only have 1 ego of any given group at once. EX: ego_group = "material"

level: What level this ego can start being applied to items. EX: 'level = 1'

allocation: Not completely sure, but it helps determine how often the ego appears. EX: 'allocation = 100'

value_add: How much this ego increases the item's value by. EX: 'value_add = 2000'

rating: No clue. Seems to be mandatory. EX: 'rating = 20'

flags: Again, you can control pretty much any flag here. The main difference is that egos have a percentage chance to all of their properties, so if you wanted an ego to vary a bit from appearance to appearance, you could set up multiple effects under different numerical headers. See the example and it should be a little more clear, as this ego has 3 effects 100% of the time:
'flags =
{
[100] =
{
RESIST = obvious(getter.array{[dam.FIRE] = 66})
BRAND = obvious(getter.array{[dam.FIRE] = 3})
LITE = obvious(1)
}'

sentient.add_subsytem:

name: The name you'll refer to this subsytem as throughout the code. Example: name = "melee"

gains: The possible upgrades an item belonging to this subsystem can get. Each level, one of these will be chosen. Example code:

Code: Select all

	gains = {
		{chance = 1, to_h = 1, to_d = 1},
		{chance = 1, to_h = 2, to_d = 1},
		{chance = 2, to_h = 1, points = 1, realm_chance = 40},
		{chance = 2, pval_chance = 100, power_chance = 100}
	}
the totals for 'chance' values are added up, and 1 die with that total sides is rolled. Each entry has 'chance' sides that will result in that effect. Any standard item modifier can be applied in a sectoin, but a couple new ones appear. 'points' is used to buy realms, each one has a different costs before it can be learned. 'realm_chance' is a percentage value that indicates how likely the item is to try and buy a new realm with its available points. 'pval_chance' and 'power_chance' are percentages that indicate how likely that an item will gain a new power from a known realm.
So here, the game rolls 1d6 to see which effect happens. A 1 gives +1/+1, a 2 gives +2/+1, 3 or 4 gives +1/+0 plus a point to spend towards new realms, and 5 and 6 grant the item a new power from one of it's available realms.
tvals: What TVALs this subsystem can be applied to. Example: tvals = {TV_POLEARM, TV_AXE, TV_HAFTED, TV_KNIFE, TV_SWORD}

sentient.add_realm:

subsys: What subsytem this realm will belong to. Example: subsys = "melee"

name: The name of this realm. Example: name = "fire"

cost: How many points it costs an item to buy this realm. Example: cost = 1

color: The text descriptor color that the name will appear in when this realm is mentioned in a message to the player. Example: color = "#r"

powers: This block determines each power that can be gained by a sentient item. Note that powers do not oost anything to buy as long as the item has the associated realm. A 'powers' subblock looks like this:

Code: Select all

{
			name  = "Add to light",
			msg   = "Your weapon glows brightly!",
			grant = {LITE = 1},
			grant_when = sentient.grant_when.ALWAYS
			grant_type = sentient.grant_type.ADD
		},
name: what this power is called.
msg: What to display to the player when the item gains this power
grant: What flags to grant to the item. Will look a lot like ego special powers if not adding special flags.

grant_when: condition to provide this bonus to the item. Can be OBJ_NOT_HAVE(Item doesn't have something similar), PLAYER_NOT_HAVE(The player doesn't have the ability already), OBJ_HAS_LESS(The item has a lesser value), PLAYER_HAS_LESS(the player has a lesser value for this), IS_ART(This item is an artifact), NOT_ART(This item is not an artifact), or ALWAYS(Ignore any other condition). Boolean operators (|, &, etc.) can be used to check multiple conditions.
grant_type: How this power is granted. Can be SET(Makes the grant this value), ADD(adds this value to existing values), or FUNC(More complicated adjustments)
Last edited by FACM on Wed Feb 10, 2010 6:47 pm, edited 3 times in total.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#4 Post by FACM »

T-Engine Module Development
Part 4: Magic, ToME style

*Pending completion of part 3*

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#5 Post by FACM »

T-Engine Module Development
Part 5: World Flavor and Misc. Other Development

*pending completion of part 4*

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#6 Post by FACM »

T-Engine Module Development
Part 6: Custom Features, Stage 1

*Pending completion of Part 5*

LordBucket
Uruivellas
Posts: 929
Joined: Wed Apr 28, 2004 2:08 am
Location: Orange County, Ca

Re: Module Developer's Guides

#7 Post by LordBucket »

Note that now that the wiki is functional again, the How to Guides are once again available.

Though it appears that with the new format, headers and menus and things have vanished.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#8 Post by FACM »

I see the same old wiki there was before, that's currently locked down. I'll check again when I get home from work.

EDIT: Home, still see the old wiki.

LordBucket
Uruivellas
Posts: 929
Joined: Wed Apr 28, 2004 2:08 am
Location: Orange County, Ca

Re: Module Developer's Guides

#9 Post by LordBucket »

FACM wrote: currently locked down
Odd. Links that have been dead for years now work for me. For example...How to move the character is accessible on my browser.

Do you get an edit message? If so...Neil might want to know about it.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#10 Post by FACM »

The web page is visible to me. I've seen it for the couple of months I've looked into making a T-Engine module. But every page says 'Immutable page', so I can't post/edit anything to it myself. I assumed that this was done after the announcement that the wiki was moving, where he said that anything posted might be lost. I haven't seen the followup announcement saying that the wiki's moved successfully, though it seems silly to post that if the new one's going to get dropped right onto the old address.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#11 Post by FACM »

These have been moved to the new wiki! Come and help me with all of the question marks in my guide.

http://doku.t-o-m-e.net/module_guide_1
http://doku.t-o-m-e.net/module_guide_2
http://doku.t-o-m-e.net/module_guide_3

madmonk
Reaper
Posts: 2257
Joined: Wed Dec 12, 2007 12:21 am
Location: New Zealand

Re: Module Developer's Guides

#12 Post by madmonk »

I can't find the link on the Module discussion page... You might want to add it!
Regards

Jon.

FACM
Higher
Posts: 65
Joined: Sun Jan 24, 2010 5:54 pm

Re: Module Developer's Guides

#13 Post by FACM »

The link is at http://doku.t-o-m-e.net/t3modules:frontpage

The blue part that says 'Check out the documents here for help' should link to a master page for my guides. If that's not clear, format it so that it is. It's a wiki for a reason. I'm going to be mostly concerned with getting content up until I've gotten my first 5 or 6 guides finished. I won't likely go back and clean up formatting until then. If you want to jump on that to make it more obvious whats going on where please do so.

Post Reply