Feast 2.0!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

296 lines
10 KiB

  1. import { StatTest, StatVigorTest } from './tests'
  2. import { DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '../language'
  3. import { Entity } from '../entity'
  4. import { Creature } from "../creature"
  5. import { Damage, DamageFormula, Stat, Vigor, Action, Condition } from '../combat'
  6. import { LogLine, LogLines, LogEntry, nilLog } from '../interface'
  7. import { VoreContainer, Container } from '../vore'
  8. import { CapableCondition, UserDrainedVigorCondition, TogetherCondition, EnemyCondition, SoloCondition, PairCondition, ContainsCondition, ContainedByCondition } from './conditions'
  9. export class PassAction extends Action {
  10. constructor () {
  11. super("Pass", "Do nothing", [new SoloCondition()])
  12. }
  13. execute (user: Creature, target: Creature): LogEntry {
  14. return new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('pass', 'passes'))}`)
  15. }
  16. describe (user: Creature, target: Creature): LogEntry {
  17. return new LogLine("Do nothing.")
  18. }
  19. }
  20. export abstract class DamageAction extends Action {
  21. protected test: StatTest
  22. abstract successLine: PairLineArgs<Creature, { damage: Damage }>
  23. abstract failLine: PairLine<Creature>
  24. constructor (name: TextLike, desc: TextLike, protected damage: DamageFormula, conditions: Condition[] = []) {
  25. super(
  26. name,
  27. desc,
  28. [new CapableCondition(), new EnemyCondition()].concat(conditions)
  29. )
  30. this.test = new StatTest(Stat.Power, 10)
  31. }
  32. execute (user: Creature, target: Creature): LogEntry {
  33. const effectResults = target.effects.map(effect => effect.preAttack(target, user))
  34. if (effectResults.some(result => result.prevented)) {
  35. return new LogLines(...effectResults.map(result => result.log))
  36. }
  37. if (this.test.test(user, target)) {
  38. const damage = this.damage.calc(user, target)
  39. const targetResult = target.takeDamage(damage)
  40. const ownResult = this.successLine(user, target, { damage: damage })
  41. return new LogLines(ownResult, targetResult)
  42. } else {
  43. return this.failLine(user, target)
  44. }
  45. }
  46. }
  47. export class AttackAction extends DamageAction {
  48. constructor (damage: DamageFormula, protected verb: Verb = new Verb('smack')) {
  49. super(verb.root.capital, 'Attack the enemy', damage, [new TogetherCondition()])
  50. }
  51. describe (user: Creature, target: Creature): LogEntry {
  52. return new LogLine(`Attack ${target.name}. `, this.damage.describe(user, target), '. ', this.test.explain(user, target))
  53. }
  54. successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine(
  55. `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `,
  56. target.effectiveDamage(args.damage).renderShort()
  57. )
  58. failLine: PairLine<Creature> = (user, target) => new LogLine(
  59. `${user.name.capital} ${user.name.conjugate(new Verb('miss', 'misses'))} ${target.name.objective}`
  60. )
  61. }
  62. export class DevourAction extends Action {
  63. private test: StatVigorTest
  64. constructor (protected container: Container) {
  65. super(
  66. new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'),
  67. new LiveText(container, x => `Try to ${x.consumeVerb} your foe`),
  68. [new CapableCondition(), new TogetherCondition()]
  69. )
  70. this.test = new StatVigorTest(Stat.Power)
  71. }
  72. allowed (user: Creature, target: Creature): boolean {
  73. const owner = this.container.owner === user
  74. const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref))
  75. const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref))
  76. if (owner && predOk && preyOk) {
  77. return super.allowed(user, target)
  78. } else {
  79. return false
  80. }
  81. }
  82. describe (user: Creature, target: Creature): LogEntry {
  83. return new LogLine(`Try to ${this.container.consumeVerb} your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target))
  84. }
  85. execute (user: Creature, target: Creature): LogEntry {
  86. if (this.test.test(user, target)) {
  87. return this.container.consume(target)
  88. } else {
  89. return this.failLine(user, target, { container: this.container })
  90. }
  91. }
  92. protected failLine: PairLineArgs<Entity, { container: Container }> = (user, target, args) => new LogLine(
  93. `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${args.container.consumeVerb} ${target.name.objective}.`
  94. )
  95. }
  96. export class FeedAction extends Action {
  97. constructor (protected container: Container) {
  98. super(
  99. 'Feed',
  100. 'Feed yourself to your opponent',
  101. [new UserDrainedVigorCondition(Vigor.Resolve), new TogetherCondition()]
  102. )
  103. this.name += ` (${container.name})`
  104. }
  105. allowed (user: Creature, target: Creature): boolean {
  106. const owner = this.container.owner === target
  107. const predOk = Array.from(this.container.voreTypes).every(pref => user.preyPrefs.has(pref))
  108. const preyOk = Array.from(this.container.voreTypes).every(pref => target.predPrefs.has(pref))
  109. if (owner && predOk && preyOk) {
  110. return super.allowed(user, target)
  111. } else {
  112. return false
  113. }
  114. }
  115. execute (user: Creature, target: Creature): LogEntry {
  116. return new LogLines(this.successLine(user, target), this.container.consume(user))
  117. }
  118. describe (user: Creature, target: Creature): LogEntry {
  119. return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name}'s ${this.container.name}...`)
  120. }
  121. protected successLine: PairLine<Entity> = (user, target) => new LogLine(
  122. `${user.name.capital} ${user.name.conjugate(new Verb('feed'))} ${user.pronouns.reflexive} to ${target.name}. `
  123. )
  124. protected failLine: PairLine<Entity> = (user, target) => new LogLine(
  125. `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to feed ${user.pronouns.reflexive} to ${target.name}. `
  126. )
  127. }
  128. export class StruggleAction extends Action {
  129. private test: StatVigorTest
  130. constructor (public container: Container) {
  131. super(
  132. new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'),
  133. 'Try to escape from your foe',
  134. [new CapableCondition(), new PairCondition(), new ContainedByCondition(container)]
  135. )
  136. this.test = new StatVigorTest(Stat.Power)
  137. }
  138. execute (user: Creature, target: Creature): LogEntry {
  139. if (user.containedIn !== null) {
  140. if (this.test.test(user, target)) {
  141. return new LogLines(this.successLine(user, target, { container: this.container }), user.containedIn.release(user))
  142. } else {
  143. return this.failLine(user, target, { container: this.container })
  144. }
  145. } else {
  146. return new LogLine("Vore's bugged!")
  147. }
  148. }
  149. describe (user: Creature, target: Creature): LogEntry {
  150. return new LogLine(`Try to escape from ${target.name}'s ${this.container.name}. `, this.test.explain(user, target))
  151. }
  152. protected successLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine(
  153. `${prey.name.capital} ${prey.name.conjugate(new Verb('escape'))} from ${pred.name.possessive} ${args.container.name}.`
  154. )
  155. protected failLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine(
  156. `${prey.name.capital} ${prey.name.conjugate(new Verb('fail'))} to escape from ${pred.name.possessive} ${args.container.name}.`
  157. )
  158. }
  159. export abstract class EatenAction extends Action {
  160. constructor (public container: Container, name: TextLike, desc: string) {
  161. super(
  162. new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'),
  163. desc,
  164. [new CapableCondition(), new PairCondition()]
  165. )
  166. }
  167. allowed (user: Creature, target: Creature) {
  168. if (target.containedIn === this.container) {
  169. return super.allowed(user, target)
  170. } else {
  171. return false
  172. }
  173. }
  174. }
  175. export class DigestAction extends Action {
  176. constructor (protected container: VoreContainer) {
  177. super(
  178. new DynText('Digest (', new LiveText(container, container => container.name.all), ')'),
  179. 'Digest your prey',
  180. [new CapableCondition(), new SoloCondition()]
  181. )
  182. }
  183. allowed (user: Creature, target: Creature) {
  184. if (this.container.owner === user && this.container.contents.length > 0) {
  185. return super.allowed(user, target)
  186. } else {
  187. return false
  188. }
  189. }
  190. execute (user: Creature, target: Creature): LogEntry {
  191. const results = this.container.tick(60)
  192. return new LogLines(results)
  193. }
  194. describe (user: Creature, target: Creature): LogEntry {
  195. return new LogLine(`Digest everyone inside of your ${this.container.name}.`)
  196. }
  197. }
  198. export class ReleaseAction extends Action {
  199. constructor (protected container: Container) {
  200. super(
  201. new DynText('Release (', new LiveText(container, x => x.name.all), ')'),
  202. 'Release one of your prey',
  203. [new CapableCondition(), new PairCondition(), new ContainsCondition(container)]
  204. )
  205. }
  206. allowed (user: Creature, target: Creature) {
  207. if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) {
  208. return super.allowed(user, target)
  209. } else {
  210. return false
  211. }
  212. }
  213. execute (user: Creature, target: Creature): LogEntry {
  214. return this.container.release(target)
  215. }
  216. describe (user: Creature, target: Creature): LogEntry {
  217. return new LogLine(`Release ${target.name} from your ${this.container.name}.`)
  218. }
  219. }
  220. export class TransferAction extends Action {
  221. verb: Verb = new Verb('send')
  222. constructor (protected from: Container, protected to: Container, name = 'Transfer') {
  223. super(
  224. name,
  225. `${from.name.all.capital} to ${to.name.all}`,
  226. [new CapableCondition(), new PairCondition()]
  227. )
  228. }
  229. line: PairLineArgs<Creature, { from: Container; to: Container }> = (user, target, args) => new LogLine(
  230. `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`
  231. )
  232. allowed (user: Creature, target: Creature) {
  233. if (target.containedIn === this.from && this.from.contents.includes(target)) {
  234. return super.allowed(user, target)
  235. } else {
  236. return false
  237. }
  238. }
  239. execute (user: Creature, target: Creature): LogEntry {
  240. this.from.release(target)
  241. this.to.consume(target)
  242. return this.line(user, target, { from: this.from, to: this.to })
  243. }
  244. describe (user: Creature, target: Creature): LogEntry {
  245. return new LogLine(`Push ${target.name} from your ${this.from.name} to your ${this.to.name}`)
  246. }
  247. }