Feast 2.0!
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 

339 行
15 KiB

  1. import { Entity, Mortal, POV, Creature } from './entity'
  2. import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats } from './combat'
  3. import { LogLines, LogEntry, CompositeLog, LogLine } from './interface'
  4. import { Noun, Pronoun, POVPair, POVPairArgs, ImproperNoun, POVSolo } from './language'
  5. import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions'
  6. export enum VoreType {
  7. Oral = "Oral Vore",
  8. Anal = "Anal Vore",
  9. Cock = "Cock Vore",
  10. Unbirth = "Unbirthing"
  11. }
  12. export abstract class Vore implements Mortal {
  13. abstract name: Noun;
  14. abstract pronouns: Pronoun;
  15. abstract perspective: POV;
  16. abstract vigors: {[key in Vigor]: number};
  17. abstract maxVigors: {[key in Vigor]: number};
  18. abstract disabled: boolean;
  19. abstract resistances: Map<DamageType, number>;
  20. abstract takeDamage (damage: Damage): LogEntry;
  21. abstract stats: Stats;
  22. abstract baseStats: Stats;
  23. abstract status: string;
  24. abstract preyPrefs: Set<VoreType>;
  25. abstract voreStats: VoreStats;
  26. abstract containedIn: Container | null;
  27. abstract predPrefs: Set<VoreType>;
  28. abstract containers: Array<Container>;
  29. destroy (): LogEntry {
  30. const lines = new POVSolo<Vore>([
  31. [[POV.First], (target: Vore) => new LogLine('You die!')],
  32. [[POV.Third], (target: Vore) => new LogLine(`${target.name.capital} dies!`)]
  33. ])
  34. this.containers.map(container => {
  35. container.contents.map(prey => {
  36. prey.containedIn = this.containedIn
  37. if (this.containedIn !== null) {
  38. this.containedIn.contents.push(prey)
  39. }
  40. })
  41. })
  42. return lines.run(this)
  43. }
  44. }
  45. export interface Container extends Actionable {
  46. name: Noun;
  47. owner: Vore;
  48. voreTypes: Set<VoreType>;
  49. contents: Array<Vore>;
  50. digested: Array<Vore>;
  51. capacity: number;
  52. fullness: number;
  53. canTake: (prey: Vore) => boolean;
  54. consume: (prey: Vore) => LogEntry;
  55. release: (prey: Vore) => LogEntry;
  56. struggle: (prey: Vore) => LogEntry;
  57. tick: (dt: number) => LogEntry;
  58. describe: () => LogEntry;
  59. digest: (prey: Vore) => LogEntry;
  60. absorb: (prey: Vore) => LogEntry;
  61. dispose: (preys: Vore[]) => LogEntry;
  62. actions: Array<Action>;
  63. }
  64. abstract class NormalContainer implements Container {
  65. contents: Array<Vore>
  66. digested: Array<Vore>
  67. abstract consumeLines: POVPair<Vore, Vore>
  68. abstract releaseLines: POVPair<Vore, Vore>
  69. abstract struggleLines: POVPair<Vore, Vore>
  70. abstract tickLines: POVPairArgs<Vore, Vore, { damage: Damage }>
  71. abstract digestLines: POVPair<Vore, Vore>
  72. abstract absorbLines: POVPair<Vore, Vore>
  73. abstract disposeLines: POVPair<Vore, Vore>
  74. get fullness (): number {
  75. return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0)
  76. }
  77. canTake (prey: Vore): boolean {
  78. const fits = this.capacity - this.fullness >= prey.voreStats.Bulk
  79. const permitted = Array.from(this.voreTypes).every(voreType => {
  80. return prey.preyPrefs.has(voreType)
  81. })
  82. return fits && permitted
  83. }
  84. consume (prey: Vore): LogEntry {
  85. this.contents.push(prey)
  86. prey.containedIn = this
  87. return this.consumeLines.run(this.owner, prey)
  88. }
  89. release (prey: Vore): LogEntry {
  90. prey.containedIn = this.owner.containedIn
  91. this.contents = this.contents.filter(victim => victim !== prey)
  92. if (this.owner.containedIn !== null) {
  93. this.owner.containedIn.contents.push(prey)
  94. }
  95. return this.releaseLines.run(this.owner, prey)
  96. }
  97. struggle (prey: Vore): LogEntry {
  98. return this.struggleLines.run(prey, this.owner)
  99. }
  100. tick (dt: number): LogEntry {
  101. const justDigested: Array<Vore> = []
  102. const scaled = this.damage.scale(dt / 60)
  103. const damageResults: Array<LogEntry> = []
  104. this.contents.forEach(prey => {
  105. damageResults.push(prey.takeDamage(scaled))
  106. if (prey.vigors[Vigor.Health] <= 0) {
  107. this.digested.push(prey)
  108. justDigested.push(prey)
  109. }
  110. })
  111. const tickedEntries = new LogLines(...this.contents.map(prey => this.tickLines.run(this.owner, prey, { damage: scaled })))
  112. const digestedEntries = new LogLines(...justDigested.map(prey => this.digest(prey)))
  113. this.contents = this.contents.filter(prey => {
  114. return prey.vigors[Vigor.Health] > 0
  115. })
  116. return new LogLines(tickedEntries, new LogLines(...damageResults), digestedEntries)
  117. }
  118. describe (): LogEntry {
  119. const lines: Array<string> = []
  120. this.contents.forEach(prey => {
  121. lines.push(prey.toString())
  122. })
  123. return new LogLine(...lines)
  124. }
  125. digest (prey: Vore): LogEntry {
  126. return this.digestLines.run(this.owner, prey)
  127. }
  128. absorb (prey: Vore): LogEntry {
  129. return this.absorbLines.run(this.owner, prey)
  130. }
  131. dispose (preys: Vore[]): LogEntry {
  132. return new CompositeLog(...preys.map(prey => this.disposeLines.run(this.owner, prey)))
  133. }
  134. actions: Array<Action>
  135. constructor (public name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number, private damage: Damage) {
  136. this.contents = []
  137. this.digested = []
  138. this.actions = []
  139. this.name = name
  140. this.actions.push(new DevourAction(this))
  141. this.actions.push(new DigestAction(this))
  142. this.actions.push(new ReleaseAction(this))
  143. this.actions.push(new StruggleAction(this))
  144. }
  145. }
  146. abstract class InnerContainer extends NormalContainer {
  147. release (prey: Vore): LogEntry {
  148. prey.containedIn = this.escape
  149. this.contents = this.contents.filter(victim => victim !== prey)
  150. return this.releaseLines.run(this.owner, prey)
  151. }
  152. constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, damage: Damage, private escape: Container) {
  153. super(name, owner, voreTypes, capacity, damage)
  154. this.actions = []
  155. this.actions.push(new DigestAction(this))
  156. this.actions.push(new StruggleAction(this))
  157. }
  158. }
  159. export class Stomach extends NormalContainer {
  160. constructor (owner: Vore, capacity: number, damage: Damage) {
  161. super(new ImproperNoun('stomach', 'stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage)
  162. }
  163. consumeLines = new POVPair([
  164. [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)],
  165. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)],
  166. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name}`)]
  167. ])
  168. releaseLines = new POVPair([
  169. [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)],
  170. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)],
  171. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name}`)]
  172. ])
  173. struggleLines = new POVPair([
  174. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  175. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  176. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)]
  177. ])
  178. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  179. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach gurgles ${target.name} for `, args.damage.renderShort())],
  180. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach churns you for `, args.damage.renderShort())],
  181. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} churns ${target.name} for `, args.damage.renderShort())]
  182. ])
  183. digestLines = new POVPair([
  184. [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)],
  185. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)],
  186. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)]
  187. ])
  188. absorbLines = new POVPair([
  189. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  190. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  191. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  192. ])
  193. disposeLines = new POVPair([
  194. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  195. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  196. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  197. ])
  198. }
  199. export class InnerStomach extends InnerContainer {
  200. constructor (owner: Vore, capacity: number, damage: Damage, escape: Container) {
  201. super(new ImproperNoun('inner stomach', 'inner stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage, escape)
  202. }
  203. consumeLines = new POVPair([
  204. [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)],
  205. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)],
  206. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name.capital}`)]
  207. ])
  208. releaseLines = new POVPair([
  209. [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)],
  210. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)],
  211. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name.capital}`)]
  212. ])
  213. struggleLines = new POVPair([
  214. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  215. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  216. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)]
  217. ])
  218. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  219. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach gurgles ${target.name} for `, args.damage.renderShort())],
  220. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach churns you for `, args.damage.renderShort())],
  221. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} churns ${target.name} for `, args.damage.renderShort())]
  222. ])
  223. digestLines = new POVPair([
  224. [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)],
  225. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)],
  226. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)]
  227. ])
  228. absorbLines = new POVPair([
  229. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  230. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  231. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  232. ])
  233. disposeLines = new POVPair([
  234. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  235. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  236. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  237. ])
  238. }
  239. export class Bowels extends NormalContainer {
  240. constructor (owner: Vore, capacity: number, damage: Damage) {
  241. super(new ImproperNoun('bowel', 'bowels').plural, owner, new Set([VoreType.Anal]), capacity, damage)
  242. }
  243. consumeLines = new POVPair([
  244. [[POV.First, POV.Third], (user, target) => new LogLine(`You force ${target.name} into your bowels`)],
  245. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} works you into ${user.pronouns.possessive} ass`)],
  246. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} anal-vores ${target.name.capital}`)]
  247. ])
  248. releaseLines = new POVPair([
  249. [[POV.First, POV.Third], (user, target) => new LogLine(`You let out ${target.name}`)],
  250. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} lets you out `)],
  251. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} lets out ${target.name.capital}`)]
  252. ])
  253. struggleLines = new POVPair([
  254. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  255. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way out your rump!`)],
  256. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the bowels of ${target.name}`)]
  257. ])
  258. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  259. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your bowels gurgle ${target.name} for `, args.damage.renderShort())],
  260. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s bowels churn you for `, args.damage.renderShort())],
  261. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${target.name.capital} churns ${user.name} for `, args.damage.renderShort())]
  262. ])
  263. digestLines = new POVPair([
  264. [[POV.First, POV.Third], (user, target) => new LogLine(`Your bowels overwhelm ${target.name}`)],
  265. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s bowels finish you off`)],
  266. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the bowels of ${user.name}`)]
  267. ])
  268. absorbLines = new POVPair([
  269. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  270. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  271. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  272. ])
  273. disposeLines = new POVPair([
  274. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  275. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  276. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  277. ])
  278. }