index.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. const { Document } = require('camo')
  2. const battleAIs = require('../battle/ai')
  3. const Move = require('../battle/move')
  4. const tinygradient = require('tinygradient')
  5. const tinycolor = require('tinycolor2')
  6. const healthGradient = tinygradient(['#BA0020', '#ffbf00', '#77dd77'])
  7. const colorDead = '#36454f'
  8. // A Character refers to both a player and an enemy. The difference is in
  9. // their `ai` that controls their actions (for players, this is DiscordAI).
  10. class Character extends Document {
  11. constructor() {
  12. super()
  13. this.schema({
  14. discordID: {type: String, required: false},
  15. battleAI: {type: String, required: true, choices: Object.keys(battleAIs)},
  16. partyID: {type: String, required: false}, // Automatically generated
  17. health: {type: Number, default: 5, min: 0},
  18. maxHealth: {type: Number, default: 5, min: 1},
  19. moveIDs: {type: [String]},
  20. })
  21. }
  22. // Rather than a string (this.battleAI), returns the class itself.
  23. get BattleAI() {
  24. return battleAIs[this.battleAI]
  25. }
  26. async modHealth(by, guild) {
  27. this.health += by
  28. if (this.health > this.maxHealth) this.health = this.maxHealth
  29. if (this.health < 0) this.health = 0
  30. if (guild) await this.healthUpdate(guild)
  31. return this.healthState
  32. }
  33. async healthUpdate(guild) {
  34. await this.save()
  35. if (!this.discordID) return
  36. const member = guild.members.get(this.discordID)
  37. // Remove existing health role, if any
  38. const roleExisting = member.roles.find(role => role.name.endsWith(' health'))
  39. if (roleExisting) await roleExisting.delete()
  40. // Reuse/create a health role and grant it
  41. const roleName = `${this.health}/${this.maxHealth} health`
  42. const role = member.roles.find(role => role.name === roleName)
  43. || await guild.createRole({
  44. name: roleName,
  45. color: this.health === 0
  46. ? colorDead
  47. : tinycolor(healthGradient.rgbAt(this.health / this.maxHealth)).toHexString()
  48. })
  49. await member.addRole(role)
  50. }
  51. get healthState() {
  52. if (this.health === this.maxHealth) return 'healthy'
  53. else if (this.health === 1) return 'peril'
  54. else if (this.health === 0) return 'dead'
  55. else return 'injured'
  56. }
  57. getName(guild) {
  58. // FIXME: names for characters without accociated discord users (eg. ai enemies)
  59. return guild.members.get(this.discordID).displayName
  60. }
  61. async getParty() {
  62. const Party = require('./party') // I don't like this any more than you do, but recursive requires DESTROY Node.js. Sorry.
  63. if (!this.partyID) {
  64. const party = Party.of([this])
  65. await party.save()
  66. this.partyID = party._id
  67. }
  68. return await Party.findOne({_id: this.partyID})
  69. }
  70. }
  71. module.exports = Character