123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- const Move = require('./move')
- const discord = require('discord.js')
- const _ = require('lodash')
- const prompt = require('../util/prompt')
- // The base class! Each method is called to request a decision from the AI.
- // Instanced on a battle-by-battle, per-character basis.
- class AI {
- constructor(char, battle, team) {
- this.char = char
- this.battle = battle
- this.team = team
- }
- // Returns { move: Move, targets: [Character] } to perform next.
- async moveChoice(self, battle) {}
- }
- // For player-controlled characters. Interfaces with Discord.
- class DiscordAI extends AI {
- async moveChoice() {
- // Move
- const moves = await Promise.all(this.char.moveIDs.map(id => Move.findOne({_id: id})))
- const { move } = await prompt({
- channel: this.team.channel,
- user: this.char,
- title: this.char.getName(this.battle.game.guild),
- description: 'Next move:',
- choices: moves.map(move => ({emote: move.emote, name: `**${move.name}** - ${move.description}`, move}))
- })
- // Target(s)
- let targetPromptDesc = `Use _${move.name}_ on:`
- if (move.target.numberMin === 1 && move.target.numberMax > 1) {
- targetPromptDesc += ` (pick up to ${move.target.numberMax})`
- } else if (move.target.numberMax > move.target.numberMin) {
- targetPromptDesc += ` (pick ${move.target.numberMin} - ${move.target.numberMax})`
- }
- let targetable
- switch (move.target.type) {
- // Only yourself.
- case 'self':
- targetable = [this.char]
- break
- // Everyone in your party but yourself.
- case 'party':
- targetable = this.team.party.members
- .filter(m => m.discordID !== this.char.discordID)
- break
- // Anyone in a different party (ie. hostile),
- case 'enemy':
- targetable = _.flatten(this.battle.teams
- .map(({ party }) => party)
- .filter(party => party._id !== this.team.party._id)
- .map(party => party.members))
- break
- // Anyone else in the battle.
- case 'any':
- targetable = this.battle.everyone
- .filter(m => m._id !== this.char._id)
- break
- }
- if (targetable.length === 1) {
- // Only one character can be targeted, so just choose them.
- return {move, targets: targetable}
- } else if (targetable.length === 0) {
- // No-one can be targeted, so ask for a different move choice.
- // TODO: handle this case better?
- const msg = await this.team.channel.send('No targets for that move!')
- const ret = await this.moveChoice()
- await msg.delete()
- return ret
- }
- // TODO: think of a better way to represent users
- const targetEmotes = ['😀', '😂', '😉', '😎', '😏', '😐', '😜']
- const targets = await prompt.multi({
- channel: this.team.channel,
- user: this.char,
- title: this.char.getName(this.battle.game.guild),
- description: targetPromptDesc,
- choices: targetable
- .map((char, i) => (
- {emote: targetEmotes[i], name: char.getName(this.battle.game.guild), char}
- )),
- chooseMin: move.target.numberMin,
- chooseMax: move.target.numberMax,
- })
- return {move, targets: targets.map(choice => choice.char)}
- }
- }
- module.exports = {DiscordAI}
|