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

349 строки
12 KiB

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