Feast 2.0!
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

160 строки
5.2 KiB

  1. import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat } from './combat'
  2. import { Noun, Pronoun } from './language'
  3. import { LogEntry, LogLines } from './interface'
  4. import { Vore, VoreContainer, VoreType } from './vore'
  5. import { Item, EquipmentSlot, Equipment, ItemKind } from './items'
  6. import { PassAction } from './combat/actions'
  7. import { AI, NoAI } from './ai'
  8. export class Creature extends Vore implements Combatant {
  9. actions: Array<Action> = [];
  10. containedIn: VoreContainer | null = null;
  11. desc = "Some creature";
  12. get effects (): Array<Effective> {
  13. return (this.statusEffects as Effective[]).concat(
  14. Object.values(this.equipment).filter(item => item !== undefined).flatMap(
  15. item => (item as Equipment).effects
  16. )
  17. )
  18. }
  19. statusEffects: Array<StatusEffect> = [];
  20. groupActions: Array<GroupAction> = [];
  21. items: Array<Item> = [];
  22. otherActions: Array<Action> = [];
  23. side: Side;
  24. title = "Lv. 1 Creature";
  25. equipment: {[key in EquipmentSlot]?: Equipment } = {}
  26. ai: AI = new NoAI()
  27. constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, preyPrefs: Set<VoreType>, predPrefs: Set<VoreType>, mass: number) {
  28. super(name, kind, pronouns, stats, preyPrefs, predPrefs, mass)
  29. this.actions.push(new PassAction())
  30. this.side = Side.Heroes
  31. const baseVoreStats = this.voreStats
  32. /* eslint-disable-next-line */
  33. const self = this
  34. this.voreStats = {
  35. get [VoreStat.Bulk] () {
  36. return baseVoreStats.Bulk
  37. },
  38. get [VoreStat.Mass] () {
  39. const base = baseVoreStats.Mass
  40. const adjusted = self.effects.reduce((scale: number, effect: Effective) => effect.scale(scale), base)
  41. return adjusted
  42. },
  43. get [VoreStat.PreyCount] () {
  44. return baseVoreStats["Prey Count"]
  45. }
  46. }
  47. }
  48. applyEffect (effect: StatusEffect): LogEntry {
  49. this.statusEffects.push(effect)
  50. return effect.onApply(this)
  51. }
  52. /**
  53. * Determines how much damage an attack would do
  54. */
  55. effectiveDamage (damage: Damage): Damage {
  56. const preDamage = this.effects.reduce((modifiedDamage: Damage, effect: Effective) => {
  57. return effect.preDamage(this, modifiedDamage)
  58. }, damage)
  59. return super.effectiveDamage(preDamage)
  60. }
  61. resistanceTo (damageType: DamageType) {
  62. const base = super.resistanceTo(damageType)
  63. const modified = this.effects.reduce((resist, effect) => effect.modResistance(damageType, resist), base)
  64. return modified
  65. }
  66. executeAction (action: Action, target: Creature): LogEntry {
  67. const preActionResults = this.effects.map(effect => effect.preAction(this))
  68. const preReceiveActionResults = target.effects.map(effect => effect.preReceiveAction(target, this))
  69. const blocking = preActionResults.concat(preReceiveActionResults).filter(result => result.prevented)
  70. if (blocking.length > 0) {
  71. return new LogLines(...blocking.map(result => result.log))
  72. } else {
  73. return action.execute(this, target)
  74. }
  75. }
  76. removeEffect (effect: StatusEffect): LogEntry {
  77. this.statusEffects = this.statusEffects.filter(eff => eff !== effect)
  78. return effect.onRemove(this)
  79. }
  80. equip (item: Equipment, slot: EquipmentSlot) {
  81. const equipped = this.equipment[slot]
  82. if (equipped !== undefined) {
  83. this.unequip(slot)
  84. }
  85. this.equipment[slot] = item
  86. }
  87. unequip (slot: EquipmentSlot) {
  88. const item = this.equipment[slot]
  89. if (item !== undefined) {
  90. this.items.push(item)
  91. this.equipment[slot] = undefined
  92. }
  93. }
  94. get status (): Array<VisibleStatus> {
  95. const results: Array<VisibleStatus> = []
  96. if (this.vigors[Vigor.Health] <= 0) {
  97. results.push(new ImplicitStatus('Dead', 'Out of health', 'fas fa-heart'))
  98. }
  99. if (this.vigors[Vigor.Stamina] <= 0) {
  100. results.push(new ImplicitStatus('Unconscious', 'Out of stamina', 'fas fa-bolt'))
  101. }
  102. if (this.vigors[Vigor.Resolve] <= 0) {
  103. results.push(new ImplicitStatus('Broken', 'Out of resolve', 'fas fa-brain'))
  104. }
  105. if (this.containedIn !== null) {
  106. results.push(new ImplicitStatus('Eaten', 'Devoured by ' + this.containedIn.owner.name, 'fas fa-drumstick-bite'))
  107. }
  108. this.statusEffects.forEach(effect => {
  109. results.push(effect)
  110. })
  111. return results
  112. }
  113. validActions (target: Creature): Array<Action> {
  114. let choices = ([] as Action[]).concat(
  115. this.actions,
  116. this.containers.flatMap(container => container.actions),
  117. target.otherActions,
  118. this.otherContainers.flatMap(container => container.actions),
  119. Object.values(this.equipment).filter(item => item !== undefined).flatMap(item => (item as Equipment).actions),
  120. this.items.filter(item => item.kind === ItemKind.Consumable && !item.consumed).flatMap(item => item.actions)
  121. )
  122. if (this.containedIn !== null) {
  123. choices = choices.concat(this.containedIn.actions)
  124. }
  125. return choices.filter(action => {
  126. return action.allowed(this, target)
  127. })
  128. }
  129. validGroupActions (targets: Array<Creature>): Array<GroupAction> {
  130. const choices = this.groupActions
  131. return choices.filter(action => {
  132. return targets.some(target => action.allowed(this, target))
  133. })
  134. }
  135. }