Feast 2.0!
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 

403 líneas
15 KiB

  1. import { Creature, POV } from '../entity'
  2. import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction, CombatTest, Stat, DamageFormula, UniformRandomDamageFormula, Action, DamageInstance, StatDamageFormula, VoreStat } from '../combat'
  3. import { ImproperNoun, POVPair, POVPairArgs, ProperNoun, FemalePronouns, RandomWord, Adjective, Verb } from '../language'
  4. import { LogLine, LogLines, LogEntry, Newline } from '../interface'
  5. import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container } from '../vore'
  6. import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions'
  7. import { TogetherCondition, ContainsCondition, EnemyCondition, AllyCondition, PairCondition, CapableCondition } from '../combat/conditions'
  8. import { InstantKill } from '../combat/effects'
  9. import * as Words from '../words'
  10. import { StatVigorTest } from '../combat/tests'
  11. class LevelDrain extends Action {
  12. execute (user: Creature, target: Creature): LogEntry {
  13. const damage: Damage = new Damage(...Object.keys(Stat).map(stat => {
  14. return {
  15. type: DamageType.Acid,
  16. target: stat as Stat,
  17. amount: target.baseStats[stat as Stat] / 5
  18. }
  19. }))
  20. const heal: Damage = new Damage(...Object.keys(Stat).map(stat => {
  21. return {
  22. type: DamageType.Heal,
  23. target: stat as Stat,
  24. amount: target.baseStats[stat as Stat] / 5
  25. }
  26. }))
  27. user.takeDamage(heal)
  28. const targetResult = target.takeDamage(damage)
  29. return new LogLines(
  30. new LogLine(`${user.name.capital.possessive} ${this.container.name} drains power from ${target.name.objective}, siphoning `, damage.renderShort(), ` from ${target.pronouns.possessive} body!`),
  31. targetResult
  32. )
  33. }
  34. describe (user: Creature, target: Creature): LogEntry {
  35. return new LogLine(`Drain energy from ${target.name}`)
  36. }
  37. constructor (private container: Container) {
  38. super(
  39. 'Level Drain',
  40. 'Drain energy from your prey',
  41. [
  42. new ContainsCondition(container),
  43. new CapableCondition()
  44. ]
  45. )
  46. }
  47. }
  48. class HypnotizeAction extends Action {
  49. lines = new POVPair<Creature, Creature>([
  50. [[POV.Second, POV.Third], (user, target) => new LogLine(`Your hypnotic gaze enthralls ${target.name}, putting ${target.pronouns.objective} under your control!`)],
  51. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital}'s hypnotic gaze enthralls you, putting you under ${user.pronouns.possessive} control!`)],
  52. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital}'s hypnotic gaze enthralls ${target.name}, putting ${target.pronouns.objective} under ${user.pronouns.possessive} control!`)]
  53. ])
  54. execute (user: Creature, target: Creature): LogEntry {
  55. target.side = user.side
  56. return this.lines.run(user, target)
  57. }
  58. describe (user: Creature, target: Creature): LogEntry {
  59. return new LogLine(`Force your target to fight by your side`)
  60. }
  61. constructor () {
  62. super(
  63. `Hypnotize`,
  64. `Change their mind!`,
  65. [
  66. new TogetherCondition(),
  67. new EnemyCondition(),
  68. new CapableCondition()
  69. ]
  70. )
  71. }
  72. }
  73. class MawContainer extends NormalContainer {
  74. consumeVerb = new Verb('grab', 'grabs', 'grabbing', 'grabbed')
  75. releaseVerb = new Verb('release')
  76. struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
  77. consumeLines = new POVPair<Vore, Vore>([
  78. [[POV.Second, POV.Third], (user, target) => new LogLine(`You snatch ${target.name} up in your jaws`)],
  79. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} snatches you up in ${user.pronouns.possessive} maw`)],
  80. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} snatches ${target.name} up in ${user.pronouns.possessive} maw`)]
  81. ])
  82. releaseLines = new POVPair([
  83. [[POV.Second, POV.Third], (user, target) => new LogLine(`You let out ${target.name}`)],
  84. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} lets you out `)],
  85. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} lets out ${target.name}`)]
  86. ])
  87. struggleLines = new POVPair([
  88. [[POV.Second, POV.Third], (user, target) => new LogLine(`You claw your way free of ${target.name}`)],
  89. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way free!`)],
  90. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from ${target.name}`)]
  91. ])
  92. constructor (owner: Vore, stomach: VoreContainer) {
  93. super(new ImproperNoun('maw'), owner, new Set([VoreType.Oral]), 50)
  94. const transfer = new TransferAction(this, stomach)
  95. transfer.verb = new Verb('gulp')
  96. this.actions.push(transfer)
  97. }
  98. }
  99. class FlexToesAction extends GroupAction {
  100. line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital.possessive} toes crush ${target.name.objective} for `, args.damage.renderShort(), ` damage!`)
  101. describeGroup (user: Creature, targets: Creature[]): LogEntry {
  102. return new LogLine(`Flex your toes. `, this.damage.explain(user))
  103. }
  104. execute (user: Creature, target: Creature): LogEntry {
  105. const damage = this.damage.calc(user, target)
  106. return new LogLines(target.takeDamage(damage), this.line(user, target, { damage: damage }))
  107. }
  108. describe (user: Creature, target: Creature): LogEntry {
  109. return new LogLine(`Flex your toes! `, this.damage.describe(user, target))
  110. }
  111. constructor (private damage: DamageFormula, container: Container) {
  112. super('Flex Toes', 'Flex your toes!', [
  113. new ContainsCondition(container),
  114. new PairCondition()
  115. ])
  116. }
  117. }
  118. class BootContainer extends NormalContainer {
  119. consumeLines = new POVPair<Vore, Vore>([
  120. [[POV.Second, POV.Third], (user, target) => new LogLine(`You stuff ${target.name} into your boot.`)],
  121. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} stuffs you in ${user.pronouns.possessive} boot, pinning you between toes and insole.`)],
  122. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} stuffs ${target.name} in ${user.pronouns.possessive} boot.`)]
  123. ])
  124. releaseLines = new POVPair([
  125. [[POV.Second, POV.Third], (user, target) => new LogLine(`You dump ${target.name} out from your boot.`)],
  126. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} dumps you out from ${user.pronouns.possessive} boot.`)],
  127. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} dumps ${target.name} out from ${user.pronouns.possessive} boot.`)]
  128. ])
  129. struggleLines = new POVPair([
  130. [[POV.Second, POV.Third], (user, target) => new LogLine(`You slip out from ${target.name}'s boot.`)],
  131. [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} squeezes ${user.pronouns.possessive} way free of your footwear!`)],
  132. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from ${target.name}'s boot.`)]
  133. ])
  134. consumeVerb = new Verb('trap', 'traps', 'trapped', 'trapping')
  135. releaseVerb = new Verb('dump')
  136. struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
  137. constructor (owner: Vore) {
  138. super(new ImproperNoun('boot'), owner, new Set(), 50)
  139. const flex = new FlexToesAction(
  140. new UniformRandomDamageFormula(new Damage(
  141. { target: Stat.Toughness, type: DamageType.Crush, amount: 10 },
  142. { target: Stat.Power, type: DamageType.Crush, amount: 10 },
  143. { target: Stat.Speed, type: DamageType.Crush, amount: 10 },
  144. { target: Stat.Willpower, type: DamageType.Crush, amount: 30 },
  145. { target: Stat.Charm, type: DamageType.Crush, amount: 10 }
  146. ), 0.5),
  147. this
  148. )
  149. this.actions.push(flex)
  150. }
  151. }
  152. const huge = new RandomWord([
  153. new Adjective('massive'),
  154. new Adjective('colossal'),
  155. new Adjective('big ol\''),
  156. new Adjective('heavy'),
  157. new Adjective('crushing'),
  158. new Adjective('huge')
  159. ])
  160. class BiteAction extends AttackAction {
  161. constructor () {
  162. super(
  163. new ConstantDamageFormula(new Damage({ amount: 50, type: DamageType.Slash, target: Vigor.Health })),
  164. new Verb('bite', 'bites', 'biting', 'bit')
  165. )
  166. this.name = "Bite"
  167. }
  168. }
  169. class ChewAction extends GroupAction {
  170. line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital} chews on ${target.name.objective} for `, args.damage.renderShort(), `!`)
  171. describeGroup (user: Creature, targets: Creature[]): LogEntry {
  172. return new LogLine('Crunch \'em all. ', this.damage.explain(user))
  173. }
  174. execute (user: Creature, target: Creature): LogEntry {
  175. const damage = this.damage.calc(user, target)
  176. const results: Array<LogEntry> = []
  177. results.push(this.line(user, target, { damage: damage }))
  178. results.push(new LogLine(' '))
  179. results.push(target.takeDamage(damage))
  180. if (target.vigors.Health <= 0) {
  181. if (this.killAction.allowed(user, target)) {
  182. results.push(new Newline())
  183. results.push(this.killAction.execute(user, target))
  184. }
  185. }
  186. return new LogLine(...results)
  187. }
  188. describe (user: Creature, target: Creature): LogEntry {
  189. return new LogLine('Do the crunch')
  190. }
  191. constructor (private damage: DamageFormula, container: Container, private killAction: Action) {
  192. super('Chew', 'Give them the big chew', [
  193. new ContainsCondition(container)
  194. ])
  195. }
  196. }
  197. class StompAction extends GroupAction {
  198. lines: POVPair<Creature, Creature> = new POVPair([
  199. [[POV.Second, POV.Third], (user: Creature, target: Creature) => new LogLine(`You flatten ${target.name} under your foot!`)],
  200. [[POV.Third, POV.Second], (user: Creature) => new LogLine(`${user.name.capital} flattens you under ${user.pronouns.possessive} ${huge} foot!`)],
  201. [[POV.Third, POV.Third], (user: Creature, target: Creature) => new LogLine(`${user.name.capital} flattens ${target.name} under ${user.pronouns.possessive} ${huge} foot!`)]
  202. ])
  203. execute (user: Creature, target: Creature): LogEntry {
  204. return new LogLines(this.lines.run(user, target), new InstantKill().apply(target))
  205. }
  206. describe (user: Creature, target: Creature): LogEntry {
  207. return new LogLine('Stomp one sucker')
  208. }
  209. describeGroup (user: Creature, targets: Array<Creature>): LogEntry {
  210. return new LogLine('Stomp all ', targets.length.toString(), ' of \'em!')
  211. }
  212. constructor () {
  213. super('Stomp', 'STOMP!', [
  214. new TogetherCondition(),
  215. new EnemyCondition(),
  216. new CapableCondition()
  217. ])
  218. }
  219. }
  220. class StompAllyAction extends Action {
  221. lines: POVPair<Creature, Creature> = new POVPair([
  222. [[POV.Second, POV.Third], (user: Creature, target: Creature) => new LogLine(`You flatten ${target.name} under your boot!`)],
  223. [[POV.Third, POV.Second], (user: Creature) => new LogLine(`${user.name.capital} flattens you under ${user.pronouns.possessive} ${huge} boot!`)],
  224. [[POV.Third, POV.Third], (user: Creature, target: Creature) => new LogLine(`${user.name.capital} flattens ${target.name} under ${user.pronouns.possessive} ${huge} boot!`)]
  225. ])
  226. execute (user: Creature, target: Creature): LogEntry {
  227. const damages: Array<DamageInstance> = Object.keys(Stat).map(stat => ({
  228. target: stat as Stat,
  229. amount: target.stats[stat as Stat] / 3,
  230. type: DamageType.Heal
  231. }))
  232. const heal = new Damage(
  233. ...damages
  234. )
  235. user.takeDamage(heal)
  236. target.destroyed = true
  237. return new LogLines(
  238. this.lines.run(user, target),
  239. new InstantKill().apply(target),
  240. new LogLine(`${user.name.capital} absorbs ${target.pronouns.possessive} power, gaining `, heal.renderShort())
  241. )
  242. }
  243. describe (user: Creature, target: Creature): LogEntry {
  244. return new LogLine('Crush an ally to absorb their power')
  245. }
  246. constructor () {
  247. super('Stomp Ally', '-1 ally, +1 buff', [
  248. new TogetherCondition(),
  249. new AllyCondition(),
  250. new CapableCondition()
  251. ])
  252. }
  253. }
  254. class DevourAllAction extends GroupAction {
  255. line = (user: Creature, target: Creature) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('scoop'))} ${target.name} up!`)
  256. groupLine = (user: Creature, args: { count: number }) => new LogLine(`${Words.SwallowSound.allCaps}! All ${args.count} of ${user.pronouns.possessive} prey pour down ${user.name.possessive} ${Words.Slick} gullet as ${user.pronouns.subjective} ${user.name.conjugate(Words.Swallows)}; they're just ${user.kind.all} chow now`)
  257. execute (user: Creature, target: Creature): LogEntry {
  258. this.container.consume(target)
  259. return new LogLines(this.line(user, target))
  260. }
  261. describe (user: Creature, target: Creature): LogEntry {
  262. return new LogLine('Stomp one sucker')
  263. }
  264. executeGroup (user: Creature, targets: Array<Creature>): LogEntry {
  265. return new LogLines(...targets.filter(target => this.test.test(user, target)).map(target => this.execute(user, target)).concat(
  266. [
  267. new Newline(),
  268. this.groupLine(user, { count: targets.length })
  269. ]
  270. ))
  271. }
  272. describeGroup (user: Creature, targets: Array<Creature>): LogEntry {
  273. return new LogLine('Eat all ', targets.length.toString(), ' of \'em!')
  274. }
  275. private test: CombatTest
  276. constructor (private container: VoreContainer) {
  277. super('Devour All', 'GULP!', [
  278. new TogetherCondition(),
  279. new EnemyCondition(),
  280. new CapableCondition()
  281. ])
  282. this.test = new StatVigorTest(Stat.Power)
  283. }
  284. }
  285. export class Withers extends Creature {
  286. title = "Huge Hellhound"
  287. desc = "Will eat your party"
  288. constructor () {
  289. super(
  290. new ProperNoun('Withers'),
  291. new ImproperNoun('hellhound', 'hellhounds'),
  292. FemalePronouns,
  293. { Toughness: 40, Power: 50, Speed: 30, Willpower: 40, Charm: 70 },
  294. new Set(),
  295. new Set([VoreType.Oral]),
  296. 5000
  297. )
  298. this.actions.push(new BiteAction())
  299. this.groupActions.push(new StompAction())
  300. this.side = Side.Monsters
  301. const stomach = new Stomach(this, 50, new Damage(
  302. { amount: 300, type: DamageType.Acid, target: Vigor.Health },
  303. { amount: 200, type: DamageType.Crush, target: Vigor.Stamina },
  304. { amount: 200, type: DamageType.Dominance, target: Vigor.Resolve }
  305. ))
  306. this.containers.push(stomach)
  307. this.otherActions.push(new FeedAction(stomach))
  308. this.groupActions.push(new DevourAllAction(stomach))
  309. const maw = new MawContainer(this, stomach)
  310. this.otherContainers.push(maw)
  311. const transfer = new TransferAction(maw, stomach)
  312. transfer.verb = new Verb('gulp')
  313. this.actions.push(new ChewAction(
  314. new UniformRandomDamageFormula(new Damage(
  315. { target: Vigor.Health, type: DamageType.Crush, amount: 10000 }
  316. ), 0.5),
  317. maw,
  318. transfer
  319. ))
  320. const boot = new BootContainer(this)
  321. this.otherContainers.push(boot)
  322. this.actions.push(new StompAllyAction())
  323. this.actions.push(
  324. new AttackAction(
  325. new StatDamageFormula([
  326. { fraction: 0.5, stat: Stat.Toughness, target: Vigor.Health, type: DamageType.Crush },
  327. { fraction: 0.05, stat: VoreStat.Bulk, target: Vigor.Health, type: DamageType.Crush }
  328. ]),
  329. new Verb('stomp')
  330. )
  331. )
  332. this.actions.push(new HypnotizeAction())
  333. this.actions.push(new LevelDrain(stomach))
  334. }
  335. }