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

228 строки
7.1 KiB

  1. import { StatusEffect, Damage, DamageType, Action, Condition, Vigor, Stat } from '../combat'
  2. import { DynText, LiveText, ToBe, Verb } from '../language'
  3. import { Creature } from "../creature"
  4. import { LogLine, LogEntry, LogLines, FAElem, nilLog } from '../interface'
  5. import { VoreContainer } from '../vore'
  6. import * as Words from '../words'
  7. export class InstantKillEffect extends StatusEffect {
  8. constructor () {
  9. super('Instant Kill', 'Instant kill!', 'fas fa-skull')
  10. }
  11. onApply (creature: Creature) {
  12. creature.vigors.Health = 0
  13. creature.removeEffect(this)
  14. return new LogLines(
  15. new LogLine(
  16. `${creature.name.capital} ${creature.name.conjugate(new ToBe())} killed instantly! `,
  17. new FAElem('fas fa-skull')
  18. ),
  19. creature.takeDamage(new Damage())
  20. )
  21. }
  22. }
  23. export class StunEffect extends StatusEffect {
  24. constructor (private duration: number) {
  25. super('Stun', 'Cannot act!', 'fas fa-sun')
  26. this.desc = new DynText('Stunned for your next ', new LiveText(this, x => x.duration), ' actions!')
  27. }
  28. get topLeft () {
  29. return this.duration.toString()
  30. }
  31. onApply (creature: Creature) {
  32. return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} is stunned!`)
  33. }
  34. onRemove (creature: Creature) {
  35. return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} no longer stunned.`)
  36. }
  37. preTurn (creature: Creature): { prevented: boolean; log: LogEntry } {
  38. if (--this.duration <= 0) {
  39. return {
  40. prevented: true,
  41. log: new LogLines(
  42. `${creature.name.capital} ${creature.name.conjugate(new ToBe())} stunned! ${creature.pronouns.capital.subjective} can't move.`,
  43. creature.removeEffect(this)
  44. )
  45. }
  46. } else {
  47. return {
  48. prevented: true,
  49. log: new LogLines(
  50. `${creature.name.capital} ${creature.name.conjugate(new ToBe())} stunned! ${creature.pronouns.capital.subjective} can't move!`
  51. )
  52. }
  53. }
  54. }
  55. }
  56. export class DamageTypeResistanceEffect extends StatusEffect {
  57. constructor (private damageTypes: DamageType[], private amount: number) {
  58. super('Resistance', 'Block ' + ((1 - amount) * 100).toFixed() + '% of these damage types: ' + damageTypes.join(", "), 'fas fa-shield-alt')
  59. }
  60. onApply (creature: Creature) {
  61. return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new Verb('gain'))} a shield!`)
  62. }
  63. onRemove (creature: Creature) {
  64. return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new Verb('lose'))} ${creature.pronouns.possessive} shield!`)
  65. }
  66. modResistance (type: DamageType, factor: number) {
  67. if (this.damageTypes.includes(type)) {
  68. return factor * this.amount
  69. } else {
  70. return factor
  71. }
  72. }
  73. }
  74. export class PredatorCounterEffect extends StatusEffect {
  75. constructor (private devour: Action, private chance: number) {
  76. super('Predatory Counter', 'Eat them back', 'fas fa-redo')
  77. this.desc = new DynText(new LiveText(this, x => (x.chance * 100).toFixed(0)), '% chance to devour your attackers')
  78. }
  79. preAttack (creature: Creature, attacker: Creature) {
  80. if (this.devour.allowed(creature, attacker) && Math.random() < this.chance) {
  81. return {
  82. prevented: true,
  83. log: new LogLines(
  84. `${creature.name.capital} ${creature.name.conjugate(new Verb('surprise'))} ${attacker.name.objective} and ${creature.name.conjugate(new Verb('try', 'tries'))} to devour ${attacker.pronouns.objective}!`,
  85. this.devour.execute(creature, attacker)
  86. )
  87. }
  88. } else {
  89. return { prevented: false, log: nilLog }
  90. }
  91. }
  92. }
  93. export class UntouchableEffect extends StatusEffect {
  94. constructor () {
  95. super('Untouchable', 'Cannot be attacked', 'fas fa-times')
  96. }
  97. preAttack (creature: Creature, attacker: Creature) {
  98. return {
  99. prevented: true,
  100. log: new LogLine(`${creature.name.capital} cannot be attacked.`)
  101. }
  102. }
  103. }
  104. export class DazzlingEffect extends StatusEffect {
  105. constructor (private conditions: Condition[]) {
  106. super('Dazzling', 'Stuns enemies who try to affect this creature', 'fas fa-spinner')
  107. }
  108. preReceiveAction (creature: Creature, attacker: Creature) {
  109. if (this.conditions.every(cond => cond.allowed(creature, attacker))) {
  110. attacker.applyEffect(new StunEffect(1))
  111. return {
  112. prevented: true,
  113. log: new LogLine(`${attacker.name.capital} can't act against ${creature.name.objective}!`)
  114. }
  115. } else {
  116. return {
  117. prevented: false,
  118. log: nilLog
  119. }
  120. }
  121. }
  122. }
  123. export class SurrenderEffect extends StatusEffect {
  124. constructor () {
  125. super('Surrendered', 'This creature has given up, and will fail most tests', 'fas fa-flag')
  126. }
  127. onApply (creature: Creature): LogEntry {
  128. creature.takeDamage(
  129. new Damage(
  130. { amount: creature.vigors.Resolve, target: Vigor.Resolve, type: DamageType.Pure }
  131. )
  132. )
  133. return new LogLine(
  134. `${creature.name.capital} ${creature.name.conjugate(new Verb('surrender'))}!`
  135. )
  136. }
  137. failTest (creature: Creature, opponent: Creature): { failed: boolean; log: LogEntry } {
  138. return {
  139. failed: true,
  140. log: nilLog
  141. }
  142. }
  143. }
  144. export class SizeEffect extends StatusEffect {
  145. constructor (private change: number) {
  146. super('Size-Shifted', 'This creature has changed in size', 'fas fa-ruler')
  147. }
  148. onApply (creature: Creature): LogLine {
  149. return new LogLine(`Smol`)
  150. }
  151. scale (scale: number): number {
  152. return scale * this.change
  153. }
  154. }
  155. export class DigestionPowerEffect extends StatusEffect {
  156. constructor (private factor: number) {
  157. super('Acid-fueled', 'This creature is digesting faster than nomral', 'fas fa-flask')
  158. }
  159. onApply (creature: Creature): LogLine {
  160. const voreContainer: VoreContainer|undefined = creature.containers.find(c => c.digest !== null)
  161. if (voreContainer !== undefined) {
  162. return new LogLine(`${creature.name.capital.possessive}'s ${voreContainer.name} ${Words.Churns} and ${voreContainer.sound}`)
  163. } else {
  164. return new LogLine(`${creature.name.capital} can't digest people...`)
  165. }
  166. }
  167. modDigestionDamage (predator: Creature, prey: Creature, container: VoreContainer, damage: Damage): Damage {
  168. return damage.scale(this.factor)
  169. }
  170. }
  171. export class StatEffect extends StatusEffect {
  172. constructor (private stat: Stat, private amount: number, private factor: number) {
  173. super('Stat boosted', 'This creature has modified stats', 'fas fa-user-plus')
  174. }
  175. modStat (creature: Creature, stat: Stat, current: number): number {
  176. if (stat === this.stat) {
  177. return current * this.factor + this.amount
  178. } else {
  179. return current
  180. }
  181. }
  182. }
  183. export class InstantDigestionEffect extends StatusEffect {
  184. constructor () {
  185. super("Instant digestion", "This creature will melt people instantly", "fas fa-skull")
  186. }
  187. postConsume (predator: Creature, prey: Creature, container: VoreContainer) {
  188. prey.applyEffect(new InstantKillEffect())
  189. predator.voreStats.Mass += prey.voreStats.Mass
  190. prey.voreStats.Mass = 0
  191. return new LogLines(
  192. `${prey.name.capital} ${prey.name.conjugate(new ToBe())} instantly digested! `,
  193. new FAElem('fas fa-skull'),
  194. container.tick(0, [prey])
  195. )
  196. }
  197. }