Feast 2.0!
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 

294 wiersze
10 KiB

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