How do I make Spells with no Books?

For the T-Engine version 3.0.0 alpha14, by SkizzaltixZaxali

Introduction

This guide is aimed at people who have a little experience with programming, but you don't need any real practical skills--I have very little, if any, ability to code Lua.

However, if this seems confusing and you're not sure why the computer makes you do some things and not others, you may want to look at the Background section in BucketMan's How to work with hooks guide, he gives a very good description of the computer's mind.

Enough chatter, what am I trying to teach you?

Would you like your module to have a class, or classes, who have magic, but no books? If so, the answer is mkeys. An mkey is one of the little lines of text that pops up when you press m in the game, like Cast a Spell or Manipulate Gemstones.

Okay, first let's get a little spell here. Let's call it fireball, and have it act like Manathrust, only explode on the target and burn for a turn. If you can't understand the spell jargon, never fear--You can copy and paste everything you need from ToME.

FIREBALL = add_spell
{
        name =          "Fireball",
        school =        {SCHOOL_FIRE},
        level =         10,
        mana =          15,
        mana_max =      15,
        stat =          A_INT,
        fail =          10,
        stick =
        {
                        charge =    { 5, 7 },
                        [TV_ORB] =
                        {
                                rarity =                15,
                                base_level =    { 1, 15 },
                                max_level =             { 25, 50 },
                        },
        },
        spell =         function()
                        local ret, dir, type

                        ret, dir = get_aim_dir()
                        if not ret then return SPELL_NOTHING end
                        if get_cast_level() >= 30 then type = dam.FIRE
                        else type = dam.FIRE end
                        fire_cloud(type, dir, 1, 2, 15, player.speed())
                        return true
        end,
        info =  function()
                        return "dam "..(5 + get_cast_level(150)).." rad 2 dur "..(1)
        end,
        desc =  {
                        "Fires a ball of flames.",
        }
}

Simple enough. Remember to define it at the top of the file, too:

declare_global_constants {

        "FIREBALL",
}

Now, let's add the mkey at the bottom of the file. No defining needed for this, so you can simply copy and paste what's here! You could also stick another file anywhere you wanted to with all your mkeys in it, just remember to access it with tome_dofile_anywhere from somewhere, say spells.lua or init.lua.

add_mkey
{
        mkey    = "Fireball",
        type    = "skill",
        fct     = function()
                local book = make_book
                (
                        FIREBALL
                )
                local spl = nil
                local ret, rep = repeat_keys.pull()
                if ret then
                        if is_ok_spell(rep, book) then
                                spl = rep
                        end
                end

                spl = spl or get_school_spell("cast", "is_ok_spell", book)
                if spl ~= -1 then
                        cast_school_spell(spl, spell(spl))
                end

                delete_object(book)
        end,
}

IMPORTANT: If you have multiple spells in the mkey, you need to put a comma after each one other than the last. So:

add_mkey
{
        mkey    = "Magic",
        type    = "skill",
        fct     = function()
                local book = make_book
                (
                        SPELL_ONE,
                        SPELL_TWO
                )
                local spl = nil
                local ret, rep = repeat_keys.pull()
                if ret then
                        if is_ok_spell(rep, book) then
                                spl = rep
                        end
                end

                spl = spl or get_school_spell("cast", "is_ok_spell", book)
                if spl ~= -1 then
                        cast_school_spell(spl, spell(spl))
                end

                delete_object(book)
        end,
}

There! Now you're ready to access the mkey from a skill. First, make the skill. you can either use the skill that the school the spell is from, or if you want multiple spell trees for each school, make a new skill for each one! There's even a way to make it so the player never knows they have all these dummy skills for their magic, but that comes later.

new_skill
{
        define_as = "SKILL_FIREBALL"
        name = "Fireball"
        desc =
        {
                "Ability to cast Fireball.",
        }
        action = { nil, "Use Fireball" }
        friends = { ["Fire"]=100, }
        exclude = { "Antimagic", }
}

Make sure you have the Use on the beginning there in the skill, but NOT IN THE MKEY. The Use there just tells the engine to find any mkey with the name that comes after the Use.

Now you want to put the spell in the player's descriptor:

new_player_descriptor
{
        type = "subclass"
        name = "Mage"
        desc = {
                "The basic spellcaster with lots of different skills",
        }
        stats = {}
        skills =
        {
                ["Fire"]             = { mods.add(0)   , mods.add(1000) }
                ["Fireball"]         = { mods.add(1000), mods.add(0)    }
        }
}

There! Now, do you want to make sure the player never sees the Fireball skill? What you do is go to skills.lua, and at the bottom there's a bunch of stuff that looks like this:

        ["Magic"] =
        {
                ["Magic-device"] =
                {
                        ["Alchemy"] = {}
                }
                ["Spell-power"] = {}
                ["Sorcery"] = {}
                ["Geomancy"] = {}
                ["Fire"] = {}
        }

Don't put another little line in there for Fireball. This way, the player never sees the skill! Make sure that the player can see all the skills for the magic schools, otherwise they can never learn new magic.

Chatter

LukeHaub: Are you sure about:

action = {nil, "Use Fireball"}

because i just created a dummy mkey to test it out and it wouldn't show up when i pressed 'm'

I'm fairly certain it's supposed to be

action = { MKEY_IDENTIFIER, "String that shows up when you press m"}

LukeHaub: I've done some code diving and it's clear that the X in action = { X, Y } is the index of the mkey in the mkey array which should be returned when you create the mkey and Y is the description that shows up in the menu. But looking through skills.lua and abilities.lua in the data/player folder i've noticed a lot of entries of the form action = { nil, "some string" } but i've been unable to find the code that uses the string to get the right mkey index. Their also doesn't seem to be any pattern between the string in mkey and Y of action = { X, Y }

e.g.

action = { nil, "Cast a thaumaturgy spell" }
["mkey"] = "Thaumaturgy",

action = { nil, "Use Runespells" } 
["mkey"] = "Runecraft",

action = { nil, "Bear-form" }
["mkey"] = "Bear-form",

Although i haven't actually found code supporting this yet you will however see a correlation between the names of the skills and abilities and the mkey names

e.g.

["mkey"] = "Bear-form",
name = "Bear-form" <- from abilities.lua

["mkey"] = "Runecraft",
name = "Runecraft" <- from skills.lua

["mkey"] = "Thaumaturgy",
name = "Thaumaturgy" <- from skills.lua

this would indicate that the reason your mkey showed up in the menu and mine didn't has nothing to do with adding "Use" at the start of the string in action = {... but is actually because you had a skill of the same name as your mkey but I didn't

LukeHaub: I've just tested it with

new_skill
{
        define_as = "SKILL_ELEPHANT"
        name = "Elephant"
        desc = 
        {
                "Weeeeeeee, it's an elephant",
        }
        action = { nil, "I once owned an elephant that could ride a donkey"}
}

add_mkey
{
        ["mkey"]        = "Elephant",
        ["type"]        = "skill",
        ["fct"]         = function()
                -- Lots of fun stuff in here about elephants
        end,
}

And it worked fine. So I think it's fairly clear that the string in action {...} does nothing but describe it in the menu. The name of the skill your mkey is attached to however has to match the mkey name if you're planning on using nil.

two safer ways of doing it are

add_mkey
{
        ["mkey"] = X, -- where X is a number
        ...
        ...
}
action = { X, "string" }

or you could go

A_GLOBAL_CONSTANT = add_mkey
{
        ["mkey"] = "this doesn't really matter and you could leave it out all together"
        ...
        ...
}

action = { A_GLOBAL_CONSTANT, "string" }

DarkGod: Nice thanks!

Module Developers Discussion/Module Writing How-To Guides/Bookless Spell Guide (last edited 2007-03-15 09:40:17 by DarkGod)