Feast 2.0!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

534 lines
15 KiB

  1. import { Place, Choice, Direction, World } from '@/game/world'
  2. import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns, TheyPronouns } from '@/game/language'
  3. import { Encounter, Stat, Damage, DamageType, Vigor, Side } from '@/game/combat'
  4. import * as Items from '@/game/items'
  5. import { LogLine, nilLog, LogLines } from '@/game/interface'
  6. import { Creature } from '@/game/creature'
  7. import { DevourAction } from '@/game/combat/actions'
  8. import { InstantDigestionEffect, SurrenderEffect } from '@/game/combat/effects'
  9. import moment from 'moment'
  10. import { VoreAI } from '@/game/ai'
  11. import { DeliciousPerk } from '@/game/combat/perks'
  12. import Inazuma from '../creatures/characters/inazuma'
  13. import Samuel from '../creatures/characters/Samuel'
  14. import Human from '../creatures/human'
  15. import Slime from '../creatures/monsters/slime'
  16. function makeParty (): Creature[] {
  17. const fighter = new Human(new ProperNoun("Redgar"), MalePronouns, {
  18. stats: {
  19. Toughness: 20,
  20. Power: 20,
  21. Reflexes: 15,
  22. Agility: 15,
  23. Willpower: 15,
  24. Charm: 10
  25. }
  26. })
  27. fighter.title = "Lv. 6 Fighter"
  28. fighter.equip(new Items.Sword(), Items.EquipmentSlot.MainHand)
  29. const rogue = new Human(new ProperNoun('Lidda'), FemalePronouns, {
  30. stats: {
  31. Toughness: 10,
  32. Power: 15,
  33. Reflexes: 20,
  34. Agility: 20,
  35. Willpower: 15,
  36. Charm: 20
  37. }
  38. })
  39. rogue.title = "Lv. 5 Rogue"
  40. rogue.equip(new Items.Dagger(), Items.EquipmentSlot.MainHand)
  41. const wizard = new Human(new ProperNoun('Mialee'), FemalePronouns, {
  42. stats: {
  43. Toughness: 10,
  44. Power: 10,
  45. Reflexes: 15,
  46. Agility: 15,
  47. Willpower: 20,
  48. Charm: 25
  49. }
  50. })
  51. wizard.title = "Lv. 6 Wizard"
  52. wizard.equip(new Items.Wand(), Items.EquipmentSlot.MainHand)
  53. const cleric = new Human(new ProperNoun('Jozan'), MalePronouns, {
  54. stats: {
  55. Toughness: 15,
  56. Power: 15,
  57. Reflexes: 10,
  58. Agility: 10,
  59. Willpower: 20,
  60. Charm: 15
  61. }
  62. })
  63. cleric.title = "Lv. 5 Cleric"
  64. cleric.equip(new Items.Mace(), Items.EquipmentSlot.MainHand)
  65. return [fighter, cleric, rogue, wizard]
  66. }
  67. export const Newtown = (): Place => {
  68. const home = new Place(
  69. new ProperNoun("Home"),
  70. "A place you can rest after long adventures"
  71. )
  72. const debug = new Place(
  73. new ProperNoun("Debug Room"),
  74. "Where weird stuff happens"
  75. )
  76. const southTownStreet = new Place(
  77. new ProperNoun("South Town Street"),
  78. "Town street south of the Town square"
  79. )
  80. const northTownStreet = new Place(
  81. new ProperNoun("North Town Street"),
  82. "Town street north of the Town square"
  83. )
  84. const northTownShop = new Place(
  85. new ProperNoun("North Town Shop"),
  86. "A shop for your impulsive buying needs"
  87. )
  88. const eastTownStreet = new Place(
  89. new ProperNoun("East Town Street"),
  90. "Town street east of the Town square"
  91. )
  92. const westTownStreet = new Place(
  93. new ProperNoun("West Town Street"),
  94. "Town street west of the Town square"
  95. )
  96. const townSquare = new Place(
  97. new ProperNoun("Town Square"),
  98. "The central-most part of town, and a hub of bustling activity"
  99. )
  100. const eastGate = new Place(
  101. new ProperNoun("East Gate"),
  102. "The towns gate, leading out into the wilderness"
  103. )
  104. const woods = new Place(
  105. new ProperNoun("The Woods"),
  106. "A scary part of the forest where monsters hide"
  107. )
  108. const deepwoods = new Place(
  109. new ProperNoun("Deep Woods"),
  110. "Extra scary"
  111. )
  112. deepwoods.choices.push(
  113. new Choice(
  114. "Fight Inazuma",
  115. "Go fight Inazuma!",
  116. (world, executor) => {
  117. const enemy = new Inazuma()
  118. const encounter = new Encounter(
  119. {
  120. name: "Fight some tough nerd",
  121. intro: () => new LogLine(`Inazuma Approaches!`)
  122. },
  123. [world.player, enemy].concat(world.party)
  124. )
  125. world.encounter = encounter
  126. return nilLog
  127. }
  128. )
  129. )
  130. const bossEncounters = [
  131. new Encounter(
  132. { name: "Inazuma", intro: () => nilLog },
  133. makeParty().concat([new Inazuma()])
  134. )
  135. ]
  136. home.choices.push(
  137. new Choice(
  138. "Nap",
  139. "Zzzzzz",
  140. (world) => {
  141. return new LogLines(
  142. `You lie down for a nice nap...`,
  143. world.advance(moment.duration(1, "hour"))
  144. )
  145. }
  146. )
  147. )
  148. home.choices.push(
  149. new Choice(
  150. "Heal",
  151. "Become not dead and/or eaten",
  152. (world, executor) => {
  153. Object.keys(Vigor).forEach(vigor => {
  154. executor.vigors[vigor as Vigor] = executor.maxVigors[vigor as Vigor]
  155. })
  156. if (executor.containedIn !== null) {
  157. executor.containedIn.release(executor)
  158. }
  159. executor.statusEffects.forEach(effect => {
  160. executor.removeEffect(effect)
  161. })
  162. executor.destroyed = false
  163. return new LogLine(`You're healthy again`)
  164. }
  165. )
  166. )
  167. home.choices.push(
  168. new Choice(
  169. "Heal your party",
  170. "Revive your party, and ensure they are not dead and/or eaten",
  171. (world, executor) => {
  172. world.party.forEach((partyMember) => {
  173. Object.keys(Vigor).forEach(vigor => {
  174. partyMember.vigors[vigor as Vigor] = partyMember.maxVigors[vigor as Vigor]
  175. })
  176. if (partyMember.containedIn !== null) {
  177. partyMember.containedIn.release(partyMember)
  178. }
  179. partyMember.statusEffects.forEach(effect => {
  180. partyMember.removeEffect(effect)
  181. })
  182. partyMember.destroyed = false
  183. })
  184. Object.keys(Vigor).forEach(vigor => {
  185. executor.vigors[vigor as Vigor] = executor.maxVigors[vigor as Vigor]
  186. })
  187. if (executor.containedIn !== null) {
  188. executor.containedIn.release(executor)
  189. }
  190. executor.statusEffects.forEach(effect => {
  191. executor.removeEffect(effect)
  192. })
  193. executor.destroyed = false
  194. return new LogLine(`Your party healthy again`)
  195. }
  196. )
  197. )
  198. home.choices.push(
  199. new Choice(
  200. "Grab potions",
  201. "Grab some potions",
  202. (world, executor) => {
  203. executor.items.push(new Items.HealthPotion())
  204. executor.items.push(new Items.AcidPotion())
  205. executor.items.push(new Items.ShrinkPotion())
  206. executor.items.push(new Items.StrengthPotion())
  207. return new LogLine("You grab some potions")
  208. }
  209. )
  210. )
  211. townSquare.choices.push(
  212. new Choice(
  213. "Eat someone",
  214. "Slurp",
  215. (world, executor) => {
  216. const snack = new Human(new ProperNoun(["Snack", "Treat", "Tasty", "Dinner", "Appetizer"][Math.floor(Math.random() * 5)]), [MalePronouns, FemalePronouns, TheyPronouns][Math.floor(Math.random() * 3)])
  217. snack.applyEffect(new SurrenderEffect())
  218. const options = executor.validActions(snack).filter(action => action instanceof DevourAction)
  219. return options[Math.floor(options.length * Math.random())].execute(executor, snack)
  220. }
  221. )
  222. )
  223. townSquare.choices.push(
  224. new Choice(
  225. "Fight someone",
  226. "Ow",
  227. (world) => {
  228. const enemy = new Human(new ProperNoun("Nerd"), TheyPronouns)
  229. enemy.side = Side.Monsters
  230. enemy.ai = new VoreAI(enemy)
  231. enemy.equip(new Items.Sword(), Items.EquipmentSlot.MainHand)
  232. enemy.addPerk(new DeliciousPerk())
  233. const encounter = new Encounter(
  234. {
  235. name: "Fight some tasty nerd",
  236. intro: () => new LogLine(`You find some nerd to fight.`)
  237. },
  238. [world.player, enemy].concat(world.party)
  239. )
  240. world.encounter = encounter
  241. return nilLog
  242. }
  243. )
  244. )
  245. townSquare.choices.push(
  246. new Choice(
  247. "Recruit someone",
  248. "Not ow",
  249. (world) => {
  250. const ally = new Human(new ProperNoun("Ally"), TheyPronouns)
  251. ally.side = Side.Heroes
  252. ally.ai = new VoreAI(ally)
  253. ally.equip(new Items.Sword(), Items.EquipmentSlot.MainHand)
  254. world.party.push(ally)
  255. return new LogLine(`You recruit a nerd`)
  256. }
  257. )
  258. )
  259. northTownShop.choices.push(
  260. new Choice(
  261. "Buy a shiny rock",
  262. "This rock has no use.",
  263. (world, executor) => {
  264. if (executor.wallet.Gold >= 500) {
  265. executor.wallet.Gold -= 500
  266. executor.items.push(
  267. new Items.KeyItem(new ProperNoun("Shiny Rock"), "Very shiny")
  268. )
  269. return new LogLine(`You buy a shiny rock`)
  270. } else {
  271. return new LogLine(`Shiny rocks are 500 gold coins, loser!`)
  272. }
  273. }
  274. )
  275. )
  276. northTownShop.choices.push(
  277. new Choice(
  278. "Buy a health potion",
  279. "50 Gold",
  280. (world, executor) => {
  281. if (executor.wallet.Gold >= 50) {
  282. executor.wallet.Gold -= 50
  283. executor.items.push(
  284. new Items.HealthPotion()
  285. )
  286. return new LogLine(`You buy a health potion.`)
  287. } else {
  288. return new LogLine(`Health potions are 50 gold coins.`)
  289. }
  290. }
  291. )
  292. )
  293. northTownShop.choices.push(
  294. new Choice(
  295. "Buy a strength potion",
  296. "40 Gold",
  297. (world, executor) => {
  298. if (executor.wallet.Gold >= 40) {
  299. executor.wallet.Gold -= 40
  300. executor.items.push(
  301. new Items.StrengthPotion()
  302. )
  303. return new LogLine(`You buy a strength potion.`)
  304. } else {
  305. return new LogLine(`Strength potions are 40 gold coins.`)
  306. }
  307. }
  308. )
  309. )
  310. northTownShop.choices.push(
  311. new Choice(
  312. "Buy an acid potion",
  313. "25 Gold",
  314. (world, executor) => {
  315. if (executor.wallet.Gold >= 25) {
  316. executor.wallet.Gold -= 25
  317. executor.items.push(
  318. new Items.AcidPotion()
  319. )
  320. return new LogLine(`You buy an acid potion.`)
  321. } else {
  322. return new LogLine(`Acid potions are 25 gold coins.`)
  323. }
  324. }
  325. )
  326. )
  327. northTownShop.choices.push(
  328. new Choice(
  329. "Buy a shrink potion",
  330. "10 Gold",
  331. (world, executor) => {
  332. if (executor.wallet.Gold >= 10) {
  333. executor.wallet.Gold -= 10
  334. executor.items.push(
  335. new Items.ShrinkPotion()
  336. )
  337. return new LogLine(`You buy a shrink potion.`)
  338. } else {
  339. return new LogLine(`Shrink potions are 10 gold coins.`)
  340. }
  341. }
  342. )
  343. )
  344. debug.choices.push(
  345. new Choice(
  346. "Cut stats",
  347. "Make your stats less good-er",
  348. (world, executor) => {
  349. Object.keys(Stat).forEach(stat => {
  350. executor.baseStats[stat as Stat] -= 5
  351. executor.takeDamage(new Damage(
  352. { amount: 5, target: (stat as Stat), type: DamageType.Pure }
  353. ))
  354. })
  355. return new LogLine(`You're weaker now`)
  356. }
  357. )
  358. )
  359. debug.choices.push(
  360. new Choice(
  361. "Boost stats",
  362. "Make your stats more good-er",
  363. (world, executor) => {
  364. Object.keys(Stat).forEach(stat => {
  365. executor.baseStats[stat as Stat] += 5
  366. executor.takeDamage(new Damage(
  367. { amount: 5, target: (stat as Stat), type: DamageType.Heal }
  368. ))
  369. })
  370. return new LogLine(`You're stronger now`)
  371. }
  372. )
  373. )
  374. debug.choices.push(
  375. new Choice(
  376. "Grow",
  377. "Make yourself larger",
  378. (world, executor) => {
  379. executor.voreStats.Mass *= 1.5
  380. return new LogLine(`You're larger now`)
  381. }
  382. )
  383. )
  384. debug.choices.push(
  385. new Choice(
  386. "Shrink",
  387. "Make yourself smaller",
  388. (world, executor) => {
  389. executor.voreStats.Mass /= 1.5
  390. return new LogLine(`You're smaller now`)
  391. }
  392. )
  393. )
  394. debug.choices.push(
  395. new Choice(
  396. "Instant Digestion",
  397. "Make your stomach REALLY powerful",
  398. (world, executor) => {
  399. executor.applyEffect(new InstantDigestionEffect())
  400. return new LogLine(`You're really gonna melt people now.`)
  401. }
  402. )
  403. )
  404. debug.choices.push(
  405. new Choice(
  406. "Set Name",
  407. "Set your name",
  408. (world, executor) => {
  409. const input = prompt("Enter a name")
  410. if (input !== null) {
  411. executor.baseName = new ProperNoun(input)
  412. return new LogLine(`Your new name is ${executor.baseName}.`)
  413. } else {
  414. return new LogLine(`nvm`)
  415. }
  416. }
  417. )
  418. )
  419. debug.choices.push(
  420. new Choice(
  421. "Add money",
  422. "Get some money",
  423. (world, executor) => {
  424. executor.wallet.Gold += 1000
  425. return new LogLine(`$$$$$$$$$$$$$$$$$`)
  426. }
  427. )
  428. )
  429. woods.choices.push(
  430. new Choice(
  431. "Fight a slime",
  432. "Go fight a slime",
  433. (world, executor) => {
  434. const enemy = new Slime()
  435. const encounter = new Encounter(
  436. {
  437. name: "Fight some tasty nerd",
  438. intro: () => new LogLine(`A slime draws near!`)
  439. },
  440. [world.player, enemy].concat(world.party)
  441. )
  442. world.encounter = encounter
  443. return nilLog
  444. }
  445. )
  446. )
  447. woods.choices.push(
  448. new Choice(
  449. "Fight Samuel",
  450. "Go fight a poor little wolf!",
  451. (world, executor) => {
  452. const enemy = new Samuel()
  453. const encounter = new Encounter(
  454. {
  455. name: "Fight some tasty nerd",
  456. intro: () => new LogLine(`Samuel pokes his head out from the bushes!`)
  457. },
  458. [world.player, enemy].concat(world.party)
  459. )
  460. world.encounter = encounter
  461. return nilLog
  462. }
  463. )
  464. )
  465. const bosses = new Place(
  466. new ProperNoun("BOSS ZONE"),
  467. "Extra scary"
  468. )
  469. bossEncounters.forEach(encounter => {
  470. bosses.choices.push(
  471. new Choice(
  472. encounter.desc.name,
  473. "Boss fight!",
  474. (world) => {
  475. world.encounter = encounter
  476. return nilLog
  477. }
  478. )
  479. )
  480. })
  481. home.biconnect(Direction.South, debug, "Walk", "Enter the debug room", "Leave", "Exit the debug room")
  482. home.biconnect(Direction.Northeast, townSquare, "Leave", "Go outside", "Enter", "Enter your home")
  483. townSquare.biconnect(Direction.North, northTownStreet, "Walk", "Go that way", "Walk", "Go that way")
  484. townSquare.biconnect(Direction.East, eastTownStreet, "Walk", "Go that way", "Walk", "Go that way")
  485. townSquare.biconnect(Direction.South, southTownStreet, "Walk", "Go that way", "Walk", "Go that way")
  486. townSquare.biconnect(Direction.West, westTownStreet, "Walk", "Go that way", "Walk", "Go that way")
  487. northTownShop.biconnect(Direction.West, northTownStreet, "Exit", "Leave the shop", "Enter", "Enter the shop")
  488. debug.biconnect(Direction.South, bosses, "Enter", "Boss Fight!", "Leave", "Go back to the debug room")
  489. eastTownStreet.biconnect(Direction.East, eastGate, "Walk", "Go that way", "Walk", "Go that way")
  490. eastGate.biconnect(Direction.East, woods, "Walk", "Enter the woods", "Approach", "Enter the city gates")
  491. woods.biconnect(Direction.North, deepwoods, "Walk", "Go deeper", "Walk", "Leave the deep woods")
  492. return home
  493. }