RPG Library: Items - ThePix/QuestJS GitHub Wiki

RPGs have a lot of items, from magic weapons, to cursed rings.

Go here for the introduction to RPG.

For all items

Any item (or character) can have an "activeEffects" attribute that is a list of strings. Each entry should be the name of an effect. Thus, an item can have any number of effects at one time, and more can be added or removed as the game progresses.

An active effect will apply to the player as long as an item is held, however you can modify the effect to make it selective. For example, for an effect for a weapon, you might want to have the "modifyOutgoingAttack" function check that the item is equipped; for armour, you might want to have the "modifyIncomingAttack" function check that the item is worn.

WEAPON

Use this template for weapons, obviously; it needs the damage as a string. For example:

createItem("knife", WEAPON("d4+2"), {
  loc:"me",
  offensiveBonus:1,
});

The important attributes are:

loc: The name of the location as usual.

offensiveBonus: A bonus (or penalty) to the hit roll (defaults to zero).

element: A weapon can be associated with an element; gives increased or decreased damage against some foes (defaults to none).

modifyOutgoingAttack: A weapon can change any of the parameters of the attack though this function. Unlike the "activeEffects", this will only apply if the weapon is used in the attack.

damageType: You may want to specify the type of damage a weapon does, to allow different enemies to have vulnerabilites. For example, skeletons might only take damage from crushing weapons. The suggested types are Slash, Bash, Pierce and Axe.

weaponClass: You may want to specify the class of a weapon, for example to allow different spells to affect different weapons. The suggested types are Blade, Crushing, Firearm, Bow, Thrown, Axe and Polearm.

For weapons that take ammo, use the LIMITED_USE_WEAPON template instead, which also requires the initial ammo.

createItem("shotgun", LIMITED_USE_WEAPON("4d4", 1), {
  loc:"practice_room",
});

The rpg/weapons.js file can be used to add 30 weapon prototypes, and a good approach is to simply clone these. You should add descriptions.

const orc_chief_axe = cloneObject(w.great_axe_prototype, 'orc_chief')
orc_chief_axe.examine = 'This axe is huge. Crudely made, it still looks pretty viscious.'

SHIELD

A template for shields. Like weapons, shields have to be equipped. Has a built-in "modifyIncomingAttack" attribute. You could modify it for special effects, but it is probably better to add special effects using "activeEffects" (have the effect check if the shield is equipped).

createItem("target shield", SHIELD(2), {
  loc:"me",
});

The parameter is the defensive bonus; this number will be deducted from the "to hit" roll (so works very differently to armour, which reduces damage).

SPELLBOOK and SPELLFONT

Makes a spell book from a list of spell names.

createItem("spellbook", SPELLBOOK(["Fireball", "Stoneskin"]), {
  loc:"practice_room",
});

The player can then LEARN FIREBALL, etc. as long as she holding the book.

A SPELLFONT is the same, except it cannot be picked up, and therefore does not need to be held to learn the spells.

Both use their "spellsAvailableToLearn" attribute to store the spells available, as a string list, and this can be modified during play if desired.

Armour

There is no template for armour; armour is any WEARABLE with an "armour" attribute.

createItem("helmet", WEARABLE(2, ['head']), {
  loc:"practice_room",
  armour:10,
});

The armour of the player is the total armour of all she is wearing divided by 10 (modify settings.armourScaling to change). This means the helmet in the example gives +1 to the player's armour, which is quite significant under the default rules. I would suggest ensuring the player does not get much more than 50 armour total (i.e., +5 armour bonus) even at high level, as that will stop so much damage. Ration out appropriately! That said, chest and head armour should be proportionally more as these are more vital areas. Armour that covers more of the body should also have a higher armour value.

You really need to set armour to have a layer and slots to prevent the player wearing, say, fourteen helmets. See the WEARABLE template for details on that.

Magic Wearables

You can make a wearable magic item in two ways - either have it do something when the item is put on or removed, or give it a function to modify attacks. This example does the former.

createItem("agility_amulet", WEARABLE(4, ['neck']), {
  examine:"An example of a wearable magic item; it raises agility by 5.",
  afterWear:function(options) {
    options.char.agility += 5
  },
  afterRemove:function(options) {
    options.char.agility -= 5
  },
})

AMULET

The AMULET template is just a WEARABLE with the slots done for you; just as for the above example, it will fill slot "amulet" at layer 0. This amulet illustrates how a magic item can modify an incoming attack.

createItem("ice_amulet", AMULET(), {
  examine:"An example of a wearable magic item; it stops ice/frost damage.",
  modifyIncomingAttack:function(attack) {
    if (this.worn && attack.element === 'frost') {
      attack.damageMultiplier = 0
      attack.primarySuccess = attack.primarySuccess.replace(/[.!]/, ", but the ice amulet protects {sb:target}, and {pv:target:take} no damage.")
    }
  }
})

RING

Rings are slightly different to wearables because a character can wear more than one. The number is determined by their "maxNumberOfRings" attribute, which defaults to two, but you could argue a character can wear up to ten, or even twenty if you allow toes as well.

createItem("blue_ring", RING(), {
  loc:"me",
  worn:true,
  examine:"A fancy blue ring.",
})

Note that behind the scenes rings use the WEARABLE template (but without a slot or layer). If you want to restrict when a ring can be worn using "testWorn" you will need to include the existing code.

  testWear:function(options) {
    if (!this.testWearForRing(options)) return false
    // your tests here
    return true
  },

Potions and Scrolls

These are one-off items - after use, the item is gone. They are created by giving the template the name of a spell. In my view, potions should only cast spells on the person drinking it, while scrolls can be used to cast any spell; it is necessary therefore, to tell Quest if this scroll-spell needs a target.

createItem("pink_scroll", SCROLL("Fireball", false), {
  examine:'A scroll with a magical glyph on it.',
  identify:'Contains the spell "Fireball".',
})

createItem("blue_scroll", SCROLL("Ice shard", true), {
  examine:'A scroll with a magical glyph on it.',
  identify:'Contains the spell "Ice shard".',
})

createItem("healing_potion", POTION("Healing"), {
  examine:'A sweet smelling concoction!',
})

I would suggest you have some way for the player to determine what a potion or scroll does. In the examples above, the healing potion is obvious. The scrolls can be identified with the "Identify" spell, as they have an "identify" attribute. You could add hints, like the scroll feels warm or cold.