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

367 строки
12 KiB

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