123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- const { Document, EmbeddedDocument } = require('camo')
- class Move extends Document {
- constructor() {
- super()
- this.schema({
- // For prompts
- name: {type: String, required: true},
- description: {type: String, default: ''},
- emote: {type: String, required: true},
- // For battle timers
- basePrepareTicks: {type: Number, min: 0, default: 0}, // After choice
- baseCooldownTicks: {type: Number, min: 0, default: 0}, // After action
- // See respective classes
- target: TargetDesc,
- actions: [ActionDesc],
- })
- }
- // Performs (read: acts out) this.actions on a single target.
- async performOn(target, user, battle) {
- for (const action of this.actions) {
- const actionTarget = action.to === 'target' ? target : user
- if (action.type === 'damage') {
- // TODO defense stat, etc
- const { amount } = action.data
- const healthState = await actionTarget.modHealth(-amount, battle.game.guild)
- if (healthState === 'dead') {
- await battle.sendMessageToAll(`**${actionTarget.getName(battle.game.guild)}** died.`)
- } else {
- await battle.sendMessageToAll(`**${actionTarget.getName(battle.game.guild)}** took ${amount} damage.`)
- }
- } else if (action.type === 'heal') {
- const { amount } = action.data
- if (actionTarget.healthState === 'dead') {
- await battle.sendMessageToAll(`**${actionTarget.getName(battle.game.guild)}** is dead and cannot be healed normally.`)
- } else if (actionTarget.healthState === 'healthy') {
- await battle.sendMessageToAll(`**${actionTarget.getName(battle.game.guild)}** is already at max health.`)
- } else {
- await actionTarget.modHealth(+amount, battle.game.guild)
- await battle.sendMessageToAll(`**${actionTarget.getName(battle.game.guild)}** recovered ${amount} health.`)
- }
- } else {
- throw 'Unknown action descriptor type: ' + action.type
- }
- }
- }
- // Creates a new move, or re-uses if one with the same name is already found.
- static async upsert(emote, name, description, {target, actions=[], basePrepareTicks, baseCooldownTicks}={}) {
- const existing = await Move.findOne({name})
- if (existing) {
- return existing
- } else {
- const move = Move.create({
- name, description, emote,
- basePrepareTicks, baseCooldownTicks,
- target: target instanceof TargetDesc ? target : TargetDesc.of(target),
- actions: actions.map(a => a instanceof ActionDesc ? a : ActionDesc.create(a)),
- })
- return move.save()
- }
- }
- }
- // Target descriptor - describes the number and types of character a move can
- // be used on (TODO: under certain conditions).
- class TargetDesc extends EmbeddedDocument {
- constructor() {
- super()
- this.schema({
- type: {type: String, choices: ['self', 'party', 'enemy', 'any']},
- numberMin: {type: Number, min: 1, default: 1},
- numberMax: {type: Number, min: 1, default: 1},
- //condition: ConditionDesc,
- })
- }
- static of(type, numberMin=1, numberMax) {
- if (numberMax === undefined) {
- // 'Up to'
- numberMax = numberMin
- numberMin = 1
- }
- return TargetDesc.create({type, numberMin, numberMax})
- }
- }
- // Action descriptor - describes the effect a move has on the target or the
- // user. Includes status-effect inflictions and damage-dealing.
- class ActionDesc extends EmbeddedDocument {
- constructor() {
- super()
- this.schema({
- type: {type: String, required: true, choice: ['damage', 'heal']},
- data: Object, // Depending on type
- to: {type: String, required: 'target', choice: ['user', 'target']}
- //condition: ConditionDesc,
- })
- }
- }
- // TODO: Condition descriptor - eg. carrying Potato AND hp < 10
- // We should make a DSL that lets you easily define these using simple syntax.
- module.exports = Object.assign(Move, {TargetDesc, ActionDesc})
|