-
Notifications
You must be signed in to change notification settings - Fork 14
RPG Library: The Attack Object
The attack object does most of the work during combat. The attack object is created when a character initiates an attack, and destroyed when applied to the target. In theory, an RPG is all about role-playing, but in practice, RPGs are most heavily concerned with combat, and so in a sense, this is the heart of the library.
Go here for the introduction to RPG.
There are two distinct stages to an attack. In the first stage, no target is assigned; changes here will affect them all. In the second stage the Attack object is cloned and assigned to one target; changes to the Attack object at this stage will apply to that target only.
When an attack is made an Attack object is created. For a weapon attack, the attributes of the weapon are combined with the skill (a default is used if no skill is specified). For a non-weapon attack - usually a Spell - just the skill/spell attributes are used. Lists of primary and secondary targets are made, as determined by the skill/spell.
This is passed to various interested parties giving them the opportunity to respond to and modify the outgoing attack:
- the skill or spell
- the attacker
- active effects on the weapon
- active effects on the attacker
- active effects on anything the attacker is carrying
- the room the target is in
- any active effects on the room
Any of these with a "modifyOutgoingAttack" function will have that function called, and sent the attack object, giving them the potential to change both themselves and the attack. The function may choose to do nothing in some situations; an amulet might only have an effect when worn, for example.
To terminate the attack altogether call "abort" on the attack. You might want to do this after establishing the player is out of ammunition, for example.
attack.abort("Out of ammo!")
Prior to stage two, the parent Attack object is cloned, and it is the clone that is then passed to each target (if there are multiple attacks against one target, a clone is created for each one). Stage two therefore applies to the clone of the original attack, and any changes made apply to only the clone.
In stage two, the attack is considered from the target point of view, so is incoming. Therefore spells, items, whatever need a "modifyIncomingAttack" function to modify the attack (this means an amulet could modify both an attack made by the wearer, and an attack targeting the wearing, or just one or the other).
- active effects on the target
- active effects on anything the target is carrying
- the target
The attack then needs to state what happened.
There are, then three stages to makes an attack. First it is created, then it is applied, then the player is told what happened. In this example, a goblin is attacking the player.
const attack = Attack.createAttack(w.goblin, player)
attack.apply()
attack.output()
As these functions can modify an attack, you need to know what the attributes of an Attack object are, and when it makes sense to change them. Those flagged as "yes" on the "first" column can be modified during the first stage, in a "modifyOutgoingAttack" function. Those flagged as "yes" on the "second" column can be modified during the second stage, in a "modifyIncomingAttack" function.
name | first | second | comment |
---|---|---|---|
attacker | no | no | The attacker. |
skill | no | no | The skill (or spell). Do not use it to modify the skill, changes will not be saved. |
weapon | no | no | The weapon used (may be undefined ). |
attackNumber | yes | no | The number of attacks per target |
armourModifier | yes | yes | This number is added to the armour of all targets. |
armourMultiplier | yes | yes | This number is multiplied with the armour of all targets (before armourModifier is added). Set to zero have a skill ignore armour. |
offensiveBonus | yes | yes | Bonus to the attack roll. |
abort | yes | no | If true, the second stage will not be run (use if, for example, the attacker is out of magic points) |
primaryTargets | yes | no | An array of targets. I suggest not changing. |
secondaryTargets | yes | special | An array of targets. I suggest not changing except that you might want to empty the list if the primary attack fails. Only try to change in second stage in "onPrimaryFailure", which is designed for that purpose. |
element | yes | yes | The magical element the attack uses (fire, frost, etc.). |
damageBonus | yes | yes | See below. |
damageNumber | yes | yes | See below. |
damageSides | yes | yes | See below. |
damageMultiplier | yes | yes | See below. |
secondaryDamageBonus | yes | yes | See below. |
secondaryDamageNumber | yes | yes | See below. |
secondaryDamageSides | yes | yes | See below. |
secondaryDamageMultiplier | yes | yes | See below. |
Damage and secondary damage is calculated:
{damageBonus + damageNumber * [random.int(damageSides) - armour] } * damageMultiplier
Note that "damageMultiplier" and "secondaryDamageMultiplier" get modified when determining elemental effects, but you can apply your own affects too.
It is generally better to adjust rather than set values. That is, rather than set damageMultiplier to 2, set it to be twice its current value:
damageMultiplier *= 2
This allows other things to have their own effect too. An amulet might double damage, a spell double it again and a weakness to that element double it again. Multipiers should always be modified by multiplying or dividing. Other attributes should be modified by adding or subtracting.
One of the big problems with combat is what gets reported to the player. How can we make the attacks read like prose? Or do we just give the hard facts?
Well, you can decide for yourself! Just add an "output" function to settings.
settings.attackOutputLevel = 2
settings.output = function(report) {
for (let el of report) {
if (el.level <= settings.attackOutputLevel) {
if (el.level === 1) {
msg(el.t)
}
else {
metamsg(el.t)
}
}
}
}
For this to work, all the output from your combat scripts need to go into the attack and collected, to be sent to this function on completion. The "reportTexts" attribute of the Attack object is an array of dictionaries, and each dictionary has a "t" attribute - the text - and a "level" attribute.
However, the easiest way to do output is to call the "msg" function of the attack itself. You can optionally include a number indicating the level of detail (1 is the most basic, while 4 reports die rolls, etc). The string will be passed through the text processor before adding, with the attack sent as a parameter, so you can refer to "weapon", "attacker" and "target".
Tutorial
- First steps
- Rooms and Exits
- Items
- Templates
- Items and rooms again
- More items
- Locks
- Commands
- Complex mechanisms
- Uploading
QuestJS Basics
- General
- Settings
- Attributes for items
- Attributes for rooms
- Attributes for exits
- Naming Items and Rooms
- Restrictions, Messages and Reactions
- Creating objects on the fly
- String Functions
- Random Functions
- Array/List Functions
- The
respond
function - Other Functions
The Text Processor
Commands
- Introduction
- Basic commands (from the tutorial)
- Complex commands
- Example of creating a command (implementing SHOOT GUN AT HENRY)
- More on commands
- Shortcut for commands
- Modifying existing commands
- Custom parser types
- Note on command results
- Meta-Commands
- Neutral language (including alternatives to "you")
- The parser
- Command matching
Templates for Items
- Introduction
- Takeable
- Openable
- Container and surface
- Locks and keys
- Wearable
- Furniture
- Button and Switch
- Readable
- Edible
- Vessel (handling liquids)
- Components
- Countable
- Consultable
- Rope
- Backscene (walls, etc.)
- Merchandise (including how to create a shop)
- Shiftable (can be pushed from one room to another)
See also:
- Custom templates (and alternatives)
Handing NPCs
- Introduction
- Attributes
- Allowing the player to give commands
- Conversations
- Simple TALK TO
- SAY
- ASK and TELL
- Dynamic conversations with TALK TO
- Following an agenda
- Reactions
- Giving
- Followers
- Changing the player point-of-view
The User Experience (UI)
The main screen
- Basics
- Printing Text Functions
- Special Text Effects
- Output effects (including pausing)
- Hyperlinks
- User Input
The Side Panes
Multi-media (sounds, images, maps, etc.)
- Images
- Sounds
- Youtube Video (Contribution by KV)
- Adding a map
- Node-based maps
- Image-based maps
- Hex maps
- Adding a playing board
- Roulette!... in a grid
Dialogue boxes
- Character Creation
- Other example dialogs [See also "User Input"]
Other Elements
- Toolbar (status bar across the top)
- Custom UI Elements
Role-playing Games
- Introduction
- Getting started
- Items
- Characters (and Monsters!)
- Attributes for characters
- Attacking and guarding
- Skills and Spells
- Limiting Magic
- Effects
- The Attack Object
- Quests for Quest
- User Interface
Web Basics
- HTML (the basic elements of a web page)
- CSS (how to style web pages)
- SVG (scalable vector graphics)
- Colours
- JavaScript
- Regular Expressions
How-to
Time
- Events (and Turnscripts)
- Date and Time (including custom calendars)
- Timed Events (i.e., real time, not game time)
Items
- Phone a Friend
- Using the USE verb
- Display Verbs
- Change Listeners
- Ensembles (grouping items)
Locations
- Large, open areas
- Region,s with sky, walls, etc.
- Dynamic Room Descriptions
- Transit system (lifts/elevators, buses, trains, simple vehicles)
- Rooms split into multiple locations
Exits
- Alternative Directions (eg, port and starboard)
- Destinations, Not Directions
Meta
- Customise Help
- Provide hints
- Include Achievements
- Add comments to your code
-
End The Game (
io.finish
)
Meta: About The Whole Game
- Translate from Quest 5
- Authoring Several Games at Once
- Chaining Several Games Together
- Competition Entry
- Walk-throughs
- Unit testing
- Debugging (trouble-shooting)
Releasing Your Game
Reference
- The Language File
- List of settings
- Scope
- The Output Queue
- Security
- Implementation notes (initialisation order, data structures)
- Files
- Code guidelines
- Save/load
- UNDO
- The editor
- The Cloak of Darkness
- Versions
- Quest 6 or QuestJS
- The other Folders
- Choose your own adventure