-------------------------------------------------------------------< Features >-
  - Settable materia slots on equipment, complete with joined slots.
  - Skills obtained temporarily with Materia.
  - Materia is settable and removable on Weapons and Armor.
  - Materia gains levels.
  - Should work on most ROM MUDs, was written on QuickMUD.
  - Includes functions for Compound style materia (checks for linked skills).
--------------------------------------------------------------------< Changes >-
  - Materia now have 5 possible joints instead of 3.
  - Several small bugs fixed, mostly with linked materia.
  - Instructions for a "Doublecast" Materia included.
  - Instructions for a "Damage Plus" Materia included.
  - Syntax for the materia command changed.
  - Materia may now be set to worn gear and to specific slots.
  - Materia may now be removed from worn gear.
  - Materia of worn gear may now be checked.
  - All materia may now be listed.
  - Materia stats of equipped materia may now be viewed.
---------------------------------------------------------------< More Changes >-
  - Corrected an oversight in materia list.
  - Added materia to stat and identify, which I'd completely forgotten before.
---------------------------------------------------------------< Installation >-
First, add the file "materia.c" to your src directory.  If neccessary, add it to
your makefile, as well.  Now I'm going to get into the other changes; if this is
too hard for you to install, you probably have no business running a MUD.

Also be sure to add ITEM_MATERIA to both const.c and tables.c with the other
item types.

merc.h
----------
in the struct obj_index_data
	int					max_slot;
	bool				joints[5];

in the struct obj_data
	int					materia_slot;
	int					max_slot;
	bool				joints[5];
	int					ap;

with the other function prototypes:
/* materia.c */
bool	has_materia_slot	args ( ( OBJ_DATA * obj, int slot ) );
bool	is_linked_materia	args ( ( OBJ_DATA * aMat, OBJ_DATA * bMat ) );
bool	is_linked_skill		args ( ( CHAR_DATA * ch, int aSn, int bSn ) );
int     ap_per_level		args ( ( int level ) );
void	gain_ap				args ( ( CHAR_DATA * ch, int ap ) );


fight.c
----------
in group gain, after the gain_exp call:
		gain_ap (ch, number_range (1, 5));
  if you want more specific ap gains, calculate them yourself

interp.h
----------
DECLARE_DO_FUN( do_materia		);

interp.c
----------
with the manipulation commands
    {"materia", do_materia, POS_RESTING, 0, LOG_NORMAL, 1},

olc_act.c
----------
in oedit_show, I put this right after the weight
	if (pObj->item_type == ITEM_WEAPON || pObj->item_type == ITEM_ARMOR)
	{//This displays materia setup as it appeared in FFVII
		sprintf (buf, "Materia Setup: %s%s%s%s%s%s%s%s%s%s%s\n\r",
				 pObj->max_slot >= 1 ? "( )" : "",
				 pObj->joints[0] ? "-" : " ",
				 pObj->max_slot >= 2 ? "( )" : "",
				 pObj->joints[1] ? "-" : " ",
				 pObj->max_slot >= 3 ? "( )" : "",
				 pObj->joints[2] ? "-" : " ",
				 pObj->max_slot >= 4 ? "( )" : "",
				 pObj->joints[3] ? "-" : " ",
				 pObj->max_slot >= 5 ? "( )" : "",
				 pObj->joints[4] ? "-" : " ",
				 pObj->max_slot >= 6 ? "( )" : "");
		send_to_char (buf, ch);
	}

in show_obj_values, within the switch
        case ITEM_MATERIA:
            sprintf (buf,
                     "[v0] Ability 1:  %s\n\r"
                     "[v1] Ability 2:  %s\n\r"
                     "[v2] Ability 3:  %s\n\r"
                     "[v3] Ability 4:  %s\n\r"
                     "[v4] Ability 5:  %s\n\r",
					 obj->value[0] != -1 ? skill_table[obj->value[0]].name : "none",
					 obj->value[1] != -1 ? skill_table[obj->value[1]].name : "none",
					 obj->value[2] != -1 ? skill_table[obj->value[2]].name : "none",
					 obj->value[3] != -1 ? skill_table[obj->value[3]].name : "none",
					 obj->value[4] != -1 ? skill_table[obj->value[4]].name : "none");
            send_to_char (buf, ch);
            break;

in set_obj_values, add this case:
        case ITEM_MATERIA:
            switch (value_num)
            {
                default:
                    do_help (ch, "ITEM_MATERIA");
                    return FALSE;
                case 0:
                    send_to_char ("ABILITY SLOT 1 SET.\n\r", ch);
                    pObj->value[0] = skill_lookup (argument);
                    break;
                case 1:
                    send_to_char ("ABILITY SLOT 2 SET.\n\r", ch);
                    pObj->value[1] = skill_lookup (argument);
                    break;
                case 2:
                    send_to_char ("ABILITY SLOT 3 SET.\n\r", ch);
                    pObj->value[2] = skill_lookup (argument);
                    break;
                case 3:
                    send_to_char ("ABILITY SLOT 4 SET.\n\r", ch);
                    pObj->value[3] = skill_lookup (argument);
                    break;
                case 4:
                    send_to_char ("ABILITY SLOT 5 SET.\n\r", ch);
                    pObj->value[4] = skill_lookup (argument);
                    break;

            }
            break;

OEDIT (oedit_slots)
{
    OBJ_INDEX_DATA *pObj;
	int slots;

    EDIT_OBJ (ch, pObj);

    if (argument[0] == '\0' || !is_number (argument))
    {
        send_to_char ("Syntax:  slots [number]\n\r", ch);
        return FALSE;
    }

	slots = atoi (argument);

	if (slots < 0 || slots > 6)
	{
        send_to_char ("Maximum slots must be between 0 and 6.\n\r", ch);
        return FALSE;
	}

    pObj->max_slot = slots;
	
	if (slots < 6)
		pObj->joints[4] = FALSE;
	if (slots < 5)
		pObj->joints[3] = FALSE;
	if (slots < 4)
		pObj->joints[2] = FALSE;
	if (slots < 3)
		pObj->joints[1] = FALSE;
	if (slots < 2)
		pObj->joints[0] = FALSE;

    send_to_char ("Max slots set.\n\r", ch);

    return TRUE;
}

OEDIT (oedit_joints)
{
    OBJ_INDEX_DATA *pObj;
	int joint;

    EDIT_OBJ (ch, pObj);

    if (argument[0] == '\0' || !is_number (argument))
    {
        send_to_char ("Syntax:  joints [1, 2, 3]\n\r", ch);
        return FALSE;
    }

    joint = atoi (argument);
	
	if (joint > 5 || joint < 1)
	{
        send_to_char ("What joint are you trying to set?\n\r", ch);
        return FALSE;
	}

	if (joint == 1)
	{
		if (pObj->max_slot < 2)
		{
			send_to_char ("The item needs at least 2 slots to set a joint.\n\r", ch);
			return FALSE;
		}

		pObj->joints[0] = !pObj->joints[0];
	}
	if (joint == 2)
	{
		if (pObj->max_slot < 3)
		{
			send_to_char ("The item needs at least 3 slots to set joint 2.\n\r", ch);
			return FALSE;
		}

		pObj->joints[1] = !pObj->joints[1];
	}
	if (joint == 3)
	{
		if (pObj->max_slot < 4)
		{
			send_to_char ("The item needs at least 4 slots to set joint 3.\n\r", ch);
			return FALSE;
		}

		pObj->joints[2] = !pObj->joints[2];
	}
	if (joint == 4)
	{
		if (pObj->max_slot < 5)
		{
			send_to_char ("The item needs at least 5 slots to set joint 4.\n\r", ch);
			return FALSE;
		}

		pObj->joints[3] = !pObj->joints[3];
	}
	if (joint == 5)
	{
		if (pObj->max_slot < 6)
		{
			send_to_char ("The item needs at least 6 slots to set joint 5.\n\r", ch);
			return FALSE;
		}

		pObj->joints[4] = !pObj->joints[4];
	}

    send_to_char ("Joint toggled.\n\r", ch);
    return TRUE;
}

remember to add the prototypes and table entries to olc.h and olc.c,
respectively


olc_save.c
----------
save_object, right after it writes the wear_flags...

	fprintf (fp, "%d %d %d %d %d %d\n", pObjIndex->max_slot,
				!!pObjIndex->joints[0], !!pObjIndex->joints[1],
				!!pObjIndex->joints[2], !!pObjIndex->joints[3],
				!!pObjIndex->joints[4]);

save_object, where it writes values, add this case:
        case ITEM_MATERIA:
            fprintf (fp, "'%s' '%s' '%s' '%s' '%s'\n",
                     pObjIndex->value[0] != -1 ?
                     skill_table[pObjIndex->value[0]].name
                     : "",
                     pObjIndex->value[1] != -1 ?
                     skill_table[pObjIndex->value[1]].name
                     : "",
                     pObjIndex->value[2] != -1 ?
                     skill_table[pObjIndex->value[2]].name
                     : "",
                     pObjIndex->value[3] != -1 ?
                     skill_table[pObjIndex->value[3]].name
                     : "",
                     pObjIndex->value[4] != -1 ?
                     skill_table[pObjIndex->value[4]].name : "");
            break;

save.c
----------
in fwrite_obj, any old place
    if (obj->joints != obj->pIndexData->joints)
        fprintf (fp, "Joints  %d %d %d %d %d\n",
		!!obj->joints[0], !!obj->joints[1], !!obj->joints[2],
		!!obj->joints[3], !!obj->joints[4]);

	if (obj->materia_slot != -1)
           fprintf (fp, "Slot  %d\n", obj->materia_slot);
        if (obj->ap != 0)
           fprintf (fp, "AP    %d\n", obj->ap);

	if (obj->max_slot != obj->pIndexData->max_slot)
        fprintf (fp, "SltMx %d\n", obj->max_slot);

in fwrite_obj, in the item_type switch, add this case:
        case ITEM_MATERIA:
            if (obj->value[0] > 0)
            {
                fprintf (fp, "Ability 1 '%s'\n",
                         skill_table[obj->value[0]].name);
            }

            if (obj->value[1] > 0)
            {
                fprintf (fp, "Ability 2 '%s'\n",
                         skill_table[obj->value[1]].name);
            }

            if (obj->value[2] > 0)
            {
                fprintf (fp, "Ability 3 '%s'\n",
                         skill_table[obj->value[2]].name);
            }
            if (obj->value[3] > 0)
            {
                fprintf (fp, "Ability 4 '%s'\n",
                         skill_table[obj->value[3]].name);
            }
            if (obj->value[4] > 0)
            {
                fprintf (fp, "Ability 5 '%s'\n",
                         skill_table[obj->value[4]].name);
            }

            break;

in the 'A' case of fread_obj:
                KEY ("AP", obj->ap, fread_number (fp));

                if (!str_cmp (word, "Ability"))
                {
                    int iValue;
                    int sn;

                    iValue = fread_number (fp) - 1;
                    sn = skill_lookup (fread_word (fp));
                    if (iValue < 0 || iValue > 4)
                    {
                        bug ("Fread_obj: bad iValue %d.", iValue);
                    }
                    else if (sn < 0)
                    {
                        bug ("Fread_obj: unknown skill.", 0);
                    }
                    else
                    {
                        obj->value[iValue] = sn;
                    }
                    fMatch = TRUE;
                    break;
                }

you'll need to add case 'J':
            case 'J':
                if (!str_cmp (word, "Joints"))
                {
					int i;

					for (i = 0; i < 5; i++)
						obj->joints[i] = !!fread_number (fp);
                    fMatch = TRUE;
				}
                break;

in case 'S':
                KEY ("Slot", obj->materia_slot, fread_number (fp));
                KEY ("SltMx", obj->max_slot, fread_number (fp));

handler.c
----------
at the top of get_skill, add:
	OBJ_DATA * eq, * materia;
    int i;

now, in the if (!IS_NPC (ch)) statement of get_skill, at the end of the
  statement, add:

		for (eq = ch->carrying; eq != NULL; eq = eq->next_content)
		{
			if ((eq->item_type == ITEM_WEAPON || eq->item_type == ITEM_ARMOR)
				&& eq->wear_loc != WEAR_NONE)
			{
				for (materia = eq->contains; materia != NULL;
						materia = materia->next_content)
				{
					for (i = 0; i < 5; i++)
					{
						if (materia->value[i] == sn
							&& materia->level > i)
							skill = 100;

					}
				}
			}
			else continue;
		}

act_wiz.c
----------
somewhere in ostat, I put it right above values:
	if (obj->item_type == ITEM_WEAPON || obj->item_type == ITEM_ARMOR)
	{
		sprintf (buf, "Materia Setup: %s%s%s%s%s%s%s%s%s.\n\r",
				 obj->max_slot >= 1 ? "( )" : "",
				 obj->joints[0] ? "-" : " ",
				 obj->max_slot >= 2 ? "( )" : "",
				 obj->joints[1] ? "-" : " ",
				 obj->max_slot >= 3 ? "( )" : "",
				 obj->joints[2] ? "-" : " ",
				 obj->max_slot >= 4 ? "( )" : "",
				 obj->joints[3] ? "-" : " ",
				 obj->max_slot >= 5 ? "( )" : "",
				 obj->joints[4] ? "-" : " ",
				 obj->max_slot >= 6 ? "( )" : "");
		send_to_char (buf, ch);
	}

also in do_ostat, within the item_type switch:
        case ITEM_MATERIA:
            sprintf (buf, "Contains the follow abilities:");
            send_to_char (buf, ch);

            if (obj->value[0] >= 0 && obj->value[0] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[0]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[1] >= 0 && obj->value[1] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[1]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[2] >= 0 && obj->value[2] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[2]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[3] >= 0 && obj->value[3] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[3]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[4] >= 0 && obj->value[4] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[4]].name, ch);
                send_to_char ("'", ch);
            }

            send_to_char (".\n\r", ch);
            break;


db2.c
----------
 ---- IMPORTANT! ----
   Before making these changes, boot up the MUD, and asave world, with the
   changes to olc_save.c already in place.  Then add these changes, compile,
   and copyover.
 ---------------------

in load_objects, right after it reads wear_flags
		pObjIndex->max_slot = fread_number (fp);
		pObjIndex->joints[0] = !!fread_number (fp);
		pObjIndex->joints[1] = !!fread_number (fp);
		pObjIndex->joints[2] = !!fread_number (fp);
		pObjIndex->joints[3] = !!fread_number (fp);
		pObjIndex->joints[4] = !!fread_number (fp);
also in load_objects, within the item_type switch:
            case ITEM_MATERIA:
                pObjIndex->value[0] = skill_lookup (fread_word (fp));
                pObjIndex->value[1] = skill_lookup (fread_word (fp));
                pObjIndex->value[2] = skill_lookup (fread_word (fp));
                pObjIndex->value[3] = skill_lookup (fread_word (fp));
                pObjIndex->value[4] = skill_lookup (fread_word (fp));
                break;


db.c
----------
in create_object, somewhere, add:
	obj->max_slot  = pObjIndex->max_slot;
	obj->joints[0] = pObjIndex->joints[0];
	obj->joints[1] = pObjIndex->joints[1];
	obj->joints[2] = pObjIndex->joints[2];
	obj->joints[3] = pObjIndex->joints[3];
	obj->joints[4] = pObjIndex->joints[4];
 	obj->materia_slot = -1;

also in create_object, to prevent a bug message, add this to the same case as
just about every other item type:
        case ITEM_MATERIA:

in clone_object, pretty much anywhere you'd like, add:
	clone->max_slot  = parent->max_slot;
	clone->joints[0] = parent->joints[0];
	clone->joints[1] = parent->joints[1];
	clone->joints[2] = parent->joints[2];
	clone->joints[3] = parent->joints[3];
	clone->joints[4] = parent->joints[4];

magic.c
----------
in find_spell, after
            if (ch->level >= skill_table[sn].skill_level[ch->class]
                && ch->pcdata->learned[sn] > 0)
                return sn;
    add
	    if (get_skill (ch, sn) == 100)
		return sn;

in do_cast, where it sends "You don't know any spells of that name.", change 
                                                           || ch->
                                                           pcdata->learned[sn]
                                                           == 0)))
                                to
                                                           || get_skill(ch, sn)
							   < 1)))


in spell_identify, add
	if (obj->item_type == ITEM_WEAPON || obj->item_type == ITEM_ARMOR)
	{
		sprintf (buf, "Materia setup is %s%s%s%s%s%s%s%s%s%s%s.\n\r",
				 obj->max_slot >= 1 ? "( )" : "",
				 obj->joints[0] ? "-" : " ",
				 obj->max_slot >= 2 ? "( )" : "",
				 obj->joints[1] ? "-" : " ",
				 obj->max_slot >= 3 ? "( )" : "",
				 obj->joints[2] ? "-" : " ",
				 obj->max_slot >= 4 ? "( )" : "",
				 obj->joints[3] ? "-" : " ",
				 obj->max_slot >= 5 ? "( )" : "",
				 obj->joints[4] ? "-" : " ",
				 obj->max_slot >= 6 ? "( )" : "");
		send_to_char (buf, ch);
	}

also in spell_identify, within the item_type switch:
        case ITEM_MATERIA:
            sprintf (buf, "Contains the follow abilities:");
            send_to_char (buf, ch);

            if (obj->value[0] >= 0 && obj->value[0] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[0]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[1] >= 0 && obj->value[1] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[1]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[2] >= 0 && obj->value[2] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[2]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[3] >= 0 && obj->value[3] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[3]].name, ch);
                send_to_char ("'", ch);
            }

            if (obj->value[4] >= 0 && obj->value[4] < MAX_SKILL)
            {
                send_to_char (" '", ch);
                send_to_char (skill_table[obj->value[4]].name, ch);
                send_to_char ("'", ch);
            }

            send_to_char (".\n\r", ch);
            break;

mem.c
----------
in new_obj_index:
	pObj->max_slot = 0;
	pObj->joints[0] = FALSE;
	pObj->joints[1] = FALSE;
	pObj->joints[2] = FALSE;
	pObj->joints[3] = FALSE;
	pObj->joints[4] = FALSE;


rom.are/help.are
----------
-1 MATERIA~
Syntax: materia set    <materia> <object>
Syntax: materia remove <materia>
Syntax: materia stats  <materia>
Syntax: materia check  <object>
Syntax: materia list

MATERIA SET will set a piece of materia into a weapon or piece of armor.
MATERIA REMOVE will remove a piece of materia from whatever it is set into.
MATERIA CHECK will check the materia slots and current materia of an item.
MATERIA STATS will give detailed stats on a piece of materia.
MATERIA LIST will list all your materia.

By setting materia into your weapons and armor, you can gain access to new
abilities, not normally available to your class.

                    Materia was originally coded by Cab Himself, of FFT:O.
~

olc.hlp
0 ITEM_MATERIA~
.   value 0    level 1 ability
   value 1    level 2 ability
   value 2    level 3 ability
   value 3    level 4 ability
   value 4    level 5 ability

Generally, you should set all materia to level 1, that way, the player has to
level it up the whole way.
~
--------------------------------------------------------------------------------
  Linked Materia - Doublecast

const.c
----------
    {
     "doublecast", {53, 53, 53, 53}, {2, 2, 2, 2},
     spell_null, TAR_IGNORE, POS_STANDING,
     NULL, SLOT (0), 0, 12,
     "", "!Doublecast!", ""},
  Be sure to increase MAX_SKILLS.

In do_cast, where it determines mana cost:
	if (get_skill (ch, skill_lookup ("doublecast")) > 0 &&
		is_linked_skill (ch, sn, skill_lookup ("doublecast")))
		mana = mana * 3 / 2;

Also in do_cast, where it calls the spell_fun, replace the entire chunk with:
        if (IS_NPC (ch) || class_table[ch->class].fMana)
		{
            /* class has spells */
			if (get_skill (ch, skill_lookup ("doublecast")) > 0 &&
				is_linked_skill (ch, sn, skill_lookup ("doublecast")))
			{
				(*skill_table[sn].spell_fun) (sn, ch->level, ch, vo, target);
				(*skill_table[sn].spell_fun) (sn, ch->level, ch, vo, target);
			}
			else
				(*skill_table[sn].spell_fun) (sn, ch->level, ch, vo, target);
        }
		else
		{
			if (get_skill (ch, skill_lookup ("doublecast")) > 0 &&
				is_linked_skill (ch, sn, skill_lookup ("doublecast")))
			{
				(*skill_table[sn].spell_fun) (sn, 3 * ch->level / 4, ch, vo, target);
				(*skill_table[sn].spell_fun) (sn, 3 * ch->level / 4, ch, vo, target);
			}
			else
				(*skill_table[sn].spell_fun) (sn, 3 * ch->level / 4, ch, vo, target);
        }

Clean compile, and create a materia with a spell on it, a materia with
Doublecast, and a weapon with the first two slots linked.  Set the materia in
the weapon, and cast a spell from the materia (make sure you know it from
Materia and not class, by setting the materia's level to 5).  The spell will
fire twice.
--------------------------------------------------------------------------------
  Linked Materia - Damage Plus

const.c
----------
    {
     "damage plus", {53, 53, 53, 53}, {2, 2, 2, 2},
     spell_null, TAR_IGNORE, POS_STANDING,
     NULL, SLOT (0), 0, 12,
     "", "!damage plus!", ""},

fight.c
----------
in bool_damage, right after the section with the damage reduction comment,
add:
	if (get_skill (ch, skill_lookup ("damage plus")) > 0 &&
		is_linked_skill (ch, dt, skill_lookup ("damage plus")))
		dam = dam * 3 / 2;

Do the same as with doublecast.  Any spells on a materia joined to damage plus
will hit for 150% damage.  Make a tri-slot [( )-( )-( )] and you can both
doublecast AND damage plus a spell.  Make these kinds of things sparsely
available, as they could greatly throw off the balance of the MUD.
--------------------------------------------------------------------------------
If you find any bugs, notice something that's missing, or just have questions,
feel free to email me at eclipsing.souls -AT- gmail -DOT- com.
          Updated (1.21) at 10/15/2007, 2:22PM.   -- Midboss