diff --git a/.eslintrc.js b/.eslintrc.js
index b422f1e..f85257d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -15,6 +15,7 @@ module.exports = {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-useless-constructor': 'off',
- '@typescript-eslint/no-unused-vars': 'off'
+ '@typescript-eslint/no-unused-vars': 'off',
+ 'quotes': 'off'
}
}
diff --git a/public/index.html b/public/index.html
index 4123528..3736fa1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4,7 +4,8 @@
-
+
+
<%= htmlWebpackPlugin.options.title %>
diff --git a/public/reset.css b/public/reset.css
new file mode 100644
index 0000000..e29c0f5
--- /dev/null
+++ b/public/reset.css
@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
diff --git a/src/App.vue b/src/App.vue
index 15cdfc2..5f0a152 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,9 +1,6 @@
-
- This is the (extremely early alpha of the) new Feast. If you're looking for the old version,
go here!
-
-

+
@@ -11,12 +8,13 @@
@@ -35,6 +34,8 @@ export default class App extends Vue {
diff --git a/src/assets/logo.png b/src/assets/logo.png
deleted file mode 100644
index f3d2503..0000000
Binary files a/src/assets/logo.png and /dev/null differ
diff --git a/src/components/Combat.vue b/src/components/Combat.vue
index 203eef3..1bcf39d 100644
--- a/src/components/Combat.vue
+++ b/src/components/Combat.vue
@@ -8,14 +8,14 @@
Your moves
-
-
+
+
{{actionDescription}}
Enemy moves
-
-
+
+
@@ -71,4 +71,8 @@ a {
display: flex;
justify-content: center;
}
+.combat-button {
+ width: 100px;
+ height: 100px;
+}
diff --git a/src/components/Header.vue b/src/components/Header.vue
new file mode 100644
index 0000000..230c4fe
--- /dev/null
+++ b/src/components/Header.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/src/game/combat.ts b/src/game/combat.ts
index aa611e2..d8a05ee 100644
--- a/src/game/combat.ts
+++ b/src/game/combat.ts
@@ -39,46 +39,42 @@ export enum Stat {
export type Stats = {[key in Stat]: number}
-export enum State {
- Normal = 'Normal',
- Grappled = 'Grappled',
- Grappling = 'Grappling',
- Eaten = 'Eaten'
-}
-
export interface Combatant {
actions: Array;
}
export abstract class Action {
- allowed (user: Creature, target: Creature) {
- return this.userStates.has(user.state) && this.targetStates.has(target.state)
- }
+ abstract allowed (user: Creature, target: Creature): boolean
+ abstract execute(user: Creature, target: Creature): LogEntry
- abstract execute(user: Creature, target: Creature): LogEntry
+ constructor (public name: string, public desc: string) {
- constructor (public name: string, public desc: string, public userStates: Set, public targetStates: Set) {
+ }
- }
+ toString (): string {
+ return this.name
+ }
+}
- toString (): string {
- return this.name
- }
+export interface Actionable {
+ actions: Array;
}
abstract class SelfAction extends Action {
allowed (user: Creature, target: Creature) {
- if (user === target) {
- return super.allowed(user, target)
- } else {
- return false
- }
+ return user === target
}
}
abstract class PairAction extends Action {
allowed (user: Creature, target: Creature) {
- if (user !== target) {
+ return user !== target
+ }
+}
+
+abstract class TogetherAction extends PairAction {
+ allowed (user: Creature, target: Creature) {
+ if (user.containedIn === target.containedIn) {
return super.allowed(user, target)
} else {
return false
@@ -86,7 +82,7 @@ abstract class PairAction extends Action {
}
}
-export class AttackAction extends PairAction {
+export class AttackAction extends TogetherAction {
protected lines: POVActionPicker = {
[POV.First]: {
[POV.First]: (user, target) => new LogLines('You bite...yourself?'),
@@ -99,7 +95,7 @@ export class AttackAction extends PairAction {
}
constructor (protected damage: Damage) {
- super('Attack', 'Attack the enemy', new Set([State.Normal]), new Set([State.Normal]))
+ super('Attack', 'Attack the enemy')
}
execute (user: Creature, target: Creature): LogEntry {
@@ -108,7 +104,7 @@ export class AttackAction extends PairAction {
}
}
-export class DevourAction extends PairAction {
+export class DevourAction extends TogetherAction {
protected lines: POVActionPicker = {
[POV.First]: {
[POV.First]: (user, target) => new LogLines('You devour...yourself?'),
@@ -121,11 +117,11 @@ export class DevourAction extends PairAction {
}
constructor (protected container: Container) {
- super('Devour', 'Try to consume your foe', new Set([State.Normal]), new Set([State.Normal]))
+ super('Devour', 'Try to consume your foe')
+ this.name += ` (${container.name})`
}
execute (user: Creature, target: Creature): LogEntry {
- target.state = State.Eaten
return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), this.container.consume(target))
}
}
@@ -142,48 +138,116 @@ export class StruggleAction extends PairAction {
}
}
- constructor () {
- super('Struggle', 'Try to escape your predator', new Set([State.Eaten]), new Set([State.Normal]))
+ allowed (user: Creature, target: Creature) {
+ if (user.containedIn === this.container) {
+ return super.allowed(user, target)
+ } else {
+ return false
+ }
+ }
+
+ constructor (public container: Container) {
+ super('Struggle', 'Try to escape your predator')
}
execute (user: Creature, target: Creature): LogEntry {
if (user.containedIn) {
- user.state = State.Normal
return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), user.containedIn.release(user))
} else { return new LogLines("The prey wasn't actually eaten...") }
}
}
export class DigestAction extends SelfAction {
- protected lines: POVActionPicker = {
- [POV.First]: {
- [POV.First]: (user, target) => new LogLines('You rub your stomach'),
- [POV.Third]: (user, target) => new LogLines("You can't digest for other people...")
- },
- [POV.Third]: {
- [POV.First]: (user, target) => new LogLines("Other people can't digest for you..."),
- [POV.Third]: (user, target) => new LogLines(user.name.capital + ' rubs ' + user.pronouns.possessive + ' gut.')
- }
+ protected lines: POVActionPicker = {
+ [POV.First]: {
+ [POV.First]: (user, target) => new LogLines('You rub your stomach'),
+ [POV.Third]: (user, target) => new LogLines("You can't digest for other people...")
+ },
+ [POV.Third]: {
+ [POV.First]: (user, target) => new LogLines("Other people can't digest for you..."),
+ [POV.Third]: (user, target) => new LogLines(user.name.capital + ' rubs ' + user.pronouns.possessive + ' gut.')
}
+ }
- allowed (user: Creature, target: Creature) {
- if (user.containers.some(container => {
- return container.contents.length > 0
- })) {
- return super.allowed(user, target)
- } else {
- return false
- }
+ allowed (user: Creature, target: Creature) {
+ if (this.container.owner === user && this.container.contents.length > 0) {
+ return super.allowed(user, target)
+ } else {
+ return false
}
+ }
+
+ constructor (protected container: Container) {
+ super('Digest', 'Digest all of your current prey')
+ this.name += ` (${container.name})`
+ }
+
+ execute (user: Creature, target: Creature): LogEntry {
+ const results = new CompositeLog(...user.containers.map(container => container.tick(60)))
+ return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), results)
+ }
+}
- constructor () {
- super('Digest', 'Digest all of your current prey', new Set([State.Normal]), new Set([State.Normal]))
+export class ReleaseAction extends PairAction {
+ protected lines: POVActionPicker = {
+ [POV.First]: {
+ [POV.First]: (user, target) => new LogLines(`You can't release yourself`),
+ [POV.Third]: (user, target) => new LogLines(`You hork up ${target.name}}`)
+ },
+ [POV.Third]: {
+ [POV.First]: (user, target) => new LogLines(`${user.name.capital} horks you up`),
+ [POV.Third]: (user, target) => new LogLines(`${user.name.capital} horks up ${target.name.capital}`)
}
+ }
- execute (user: Creature, target: Creature): LogEntry {
- const results = new CompositeLog(...user.containers.map(container => container.tick(60)))
- return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), results)
+ allowed (user: Creature, target: Creature) {
+ if (target.containedIn === this.container) {
+ return super.allowed(user, target)
+ } else {
+ return false
}
+ }
+
+ constructor (protected container: Container) {
+ super('Release', 'Release one of your prey')
+ this.name += ` (${container.name})`
+ }
+
+ execute (user: Creature, target: Creature): LogEntry {
+ const results = this.container.release(target)
+ return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), results)
+ }
+}
+
+export class TransferAction extends PairAction {
+ protected lines: POVActionPicker = {
+ [POV.First]: {
+ [POV.First]: (user, target) => new LogLines(),
+ [POV.Third]: (user, target) => new LogLines(`You push your prey from your ${this.from.name} to your ${this.to.name}`)
+ },
+ [POV.Third]: {
+ [POV.First]: (user, target) => new LogLines(`You're shoved from ${user.name}'s ${this.from.name} to their ${this.to.name}`),
+ [POV.Third]: (user, target) => new LogLines(`${user.name.capital} pushes ${target.name} from their ${this.from.name} to their ${this.to.name}`)
+ }
+ }
+
+ allowed (user: Creature, target: Creature) {
+ if (target.containedIn === this.from) {
+ return super.allowed(user, target)
+ } else {
+ return false
+ }
+ }
+
+ constructor (protected from: Container, protected to: Container) {
+ super('Transfer', `Shove your prey from your ${from.name} to your ${to.name}`)
+ }
+
+ execute (user: Creature, target: Creature): LogEntry {
+ this.from.release(target)
+ this.to.consume(target)
+ return this.lines[user.perspective][target.perspective](user, target)
+ }
}
export interface CombatTest {
diff --git a/src/game/creatures/wolf.ts b/src/game/creatures/wolf.ts
index 0d67665..e94928d 100644
--- a/src/game/creatures/wolf.ts
+++ b/src/game/creatures/wolf.ts
@@ -1,7 +1,7 @@
import { Creature, POV } from '../entity'
-import { Stat, Damage, DamageType, AttackAction, DevourAction, StruggleAction, DigestAction } from '../combat'
+import { Stat, Damage, DamageType, AttackAction, StruggleAction, TransferAction } from '../combat'
import { MalePronouns, ImproperNoun } from '../language'
-import { VoreType, Stomach } from '../vore'
+import { VoreType, Stomach, Bowels } from '../vore'
import { LogLines } from '../interface'
class BiteAction extends AttackAction {
@@ -21,8 +21,10 @@ export class Wolf extends Creature {
const stomach = new Stomach(this, 50, new Damage({ amount: 50, type: DamageType.Acid }, { amount: 500, type: DamageType.Crush }))
this.containers.push(stomach)
- this.actions.push(new DevourAction(stomach))
- this.actions.push(new StruggleAction())
- this.actions.push(new DigestAction())
+ const bowels = new Bowels(this, 50, new Damage({ amount: 50, type: DamageType.Acid }, { amount: 500, type: DamageType.Crush }))
+
+ this.containers.push(bowels)
+
+ this.actions.push(new TransferAction(bowels, stomach))
}
}
diff --git a/src/game/entity.ts b/src/game/entity.ts
index 8b9e759..d78d1f9 100644
--- a/src/game/entity.ts
+++ b/src/game/entity.ts
@@ -1,4 +1,4 @@
-import { DamageType, Damage, Combatant, Stats, State, Action } from './combat'
+import { DamageType, Damage, Combatant, Stats, Action } from './combat'
import { Noun, Pronoun } from './language'
import { Pred, Prey, Container, VoreType } from './vore'
@@ -24,7 +24,6 @@ export class Creature implements Mortal, Pred, Prey, Combatant {
maxHealth = 100
resistances: Map = new Map()
perspective: POV = POV.Third
- state: State = State.Normal
containers: Array = []
actions: Array = [];
private baseBulk: number;
@@ -55,7 +54,12 @@ export class Creature implements Mortal, Pred, Prey, Combatant {
}
validActions (target: Creature): Array {
- return this.actions.filter(action => {
+ let choices = this.actions.concat(this.containers.flatMap(container => container.actions))
+
+ if (this.containedIn !== null) {
+ choices = choices.concat(this.containedIn.actions)
+ }
+ return choices.filter(action => {
return action.allowed(this, target)
})
}
diff --git a/src/game/vore.ts b/src/game/vore.ts
index 7cc4f58..3aa34c3 100644
--- a/src/game/vore.ts
+++ b/src/game/vore.ts
@@ -1,7 +1,6 @@
import { Entity, Mortal, POV } from './entity'
-import { Damage } from './combat'
+import { Damage, Actionable, Action, DevourAction, DigestAction, ReleaseAction, StruggleAction } from './combat'
import { LogLines, LogEntry, CompositeLog } from './interface'
-import { ProperNoun } from './language'
export enum VoreType {Oral}
export interface Prey extends Mortal {
@@ -15,21 +14,23 @@ export interface Pred extends Entity {
containers: Array;
}
-export interface Container {
- name: string;
- voreTypes: Set;
- contents: Array;
- capacity: number;
- fullness: number;
- canTake: (prey: Prey) => boolean;
- consume: (prey: Prey) => LogEntry;
- release: (prey: Prey) => LogEntry;
- struggle: (prey: Prey) => LogEntry;
- tick: (dt: number) => LogEntry;
- describe: () => LogEntry;
- digest: (prey: Prey) => LogEntry;
- absorb: (prey: Prey) => LogEntry;
- dispose: (preys: Prey[]) => LogEntry;
+export interface Container extends Actionable {
+ name: string;
+ owner: Pred;
+ voreTypes: Set;
+ contents: Array;
+ capacity: number;
+ fullness: number;
+ canTake: (prey: Prey) => boolean;
+ consume: (prey: Prey) => LogEntry;
+ release: (prey: Prey) => LogEntry;
+ struggle: (prey: Prey) => LogEntry;
+ tick: (dt: number) => LogEntry;
+ describe: () => LogEntry;
+ digest: (prey: Prey) => LogEntry;
+ absorb: (prey: Prey) => LogEntry;
+ dispose: (preys: Prey[]) => LogEntry;
+ actions: Array;
}
abstract class NormalContainer implements Container {
@@ -112,14 +113,23 @@ abstract class NormalContainer implements Container {
return new LogLines('GLORP')
}
- constructor (public name: string, protected owner: Pred, public voreTypes: Set, public capacity: number, private damage: Damage) {
+ actions: Array
+
+ constructor (public name: string, public owner: Pred, public voreTypes: Set, public capacity: number, private damage: Damage) {
this.contents = []
+
+ this.actions = []
}
}
export class Stomach extends NormalContainer {
constructor (owner: Pred, capacity: number, damage: Damage) {
super('Stomach', owner, new Set([VoreType.Oral]), capacity, damage)
+
+ this.actions.push(new DevourAction(this))
+ this.actions.push(new DigestAction(this))
+ this.actions.push(new ReleaseAction(this))
+ this.actions.push(new StruggleAction(this))
}
consume (prey: Prey): LogEntry {
@@ -172,3 +182,64 @@ export class Stomach extends NormalContainer {
}
}
}
+
+export class Bowels extends NormalContainer {
+ constructor (owner: Pred, capacity: number, damage: Damage) {
+ super('Bowels', owner, new Set([VoreType.Oral]), capacity, damage)
+
+ this.actions.push(new DevourAction(this))
+ this.actions.push(new DigestAction(this))
+ this.actions.push(new ReleaseAction(this))
+ this.actions.push(new StruggleAction(this))
+ }
+
+ consume (prey: Prey): LogEntry {
+ super.consume(prey)
+
+ const predPOV = this.owner.perspective
+ const preyPOV = prey.perspective
+ if (predPOV === POV.First && preyPOV === POV.Third) {
+ return new LogLines(prey.name.capital + ' slides down into your bowels')
+ } else if (predPOV === POV.Third && preyPOV === POV.First) {
+ return new LogLines(this.owner.name.capital + "'s guts swell as you slush down into " + this.owner.pronouns.possessive + ' bowels')
+ } else if (predPOV === POV.Third && preyPOV === POV.Third) {
+ return new LogLines(this.owner.name.capital + "'s belly fills with the struggling form of " + prey.name)
+ } else {
+ return new LogLines('FIX ME!')
+ }
+ }
+
+ digest (prey: Prey): LogEntry {
+ super.digest(prey)
+
+ const predPOV = this.owner.perspective
+ const preyPOV = prey.perspective
+
+ if (predPOV === POV.First && preyPOV === POV.Third) {
+ return new LogLines('Your bowels finishes off ' + prey.name)
+ } else if (predPOV === POV.Third && preyPOV === POV.First) {
+ return new LogLines(this.owner.name.capital + ' digests you')
+ } else if (predPOV === POV.Third && preyPOV === POV.Third) {
+ return new LogLines(this.owner.name.capital + ' finishes digesting ' + prey.name)
+ } else {
+ return new LogLines('FIX ME!')
+ }
+ }
+
+ absorb (prey: Prey): LogEntry {
+ super.absorb(prey)
+
+ const predPOV = this.owner.perspective
+ const preyPOV = prey.perspective
+
+ if (predPOV === POV.First && preyPOV === POV.Third) {
+ return new LogLines("Your bowels melts down what's left of " + prey.name)
+ } else if (predPOV === POV.Third && preyPOV === POV.First) {
+ return new LogLines(this.owner.name.capital + ' finishes absorbing you')
+ } else if (predPOV === POV.Third && preyPOV === POV.Third) {
+ return new LogLines(this.owner.name.capital + ' fully absorbs ' + prey.name)
+ } else {
+ return new LogLines('FIX ME!')
+ }
+ }
+}