浏览代码

Rewrite the drunken mess

Reddo 5 年之前
父节点
当前提交
b3bf36816a

+ 126 - 0
app/World/Classes/ContentPicker/ContentAtom.ts

@@ -0,0 +1,126 @@
+/**
+ * Represents the smallest part of a content. Is a wrapper for what is being compared.
+ * Holds an array of things. Only returns positive comparisons when all of them match, unless both sides only left optional nouns.
+ */
+class ContentAtom {
+    protected nouns : Array<any> = [];
+
+    public constructor (...nouns : Array<any>) {
+        this.addNoun(...nouns);
+    }
+
+    public addNoun (...nouns : Array<any>) {
+        this.nouns.push(...nouns);
+        return this;
+    }
+
+    public setNouns (...nouns : Array<any>) {
+        this.nouns = [];
+        this.addNoun(...nouns);
+        return this;
+    }
+
+    public getNouns () {
+        return [...this.nouns];
+    }
+
+    public getAtomPriority () {
+        let weight = 0;
+        this.nouns.forEach(value => {
+            weight += ContentAtom.weightNoun(value);
+        });
+        return weight;
+    }
+
+    /**
+     * Comparisons are always made assuming we're comparing the SPECIFIC side to the VAGUE side.
+     * @param other
+     */
+    public compareAgainst (other : ContentAtom) {
+        let specificNouns = [...this.nouns];
+        let vagueNouns = [...other.nouns];
+
+        for (let i = specificNouns.length - 1; i >= 0; i--) {
+            let specificNoun = specificNouns[i];
+            for (let k = vagueNouns.length - 1; k >= 0; k--) {
+                let vagueNoun = vagueNouns[k];
+                if (ContentAtom.compareNoun(specificNoun, vagueNoun)) {
+                    specificNouns.splice(i, 1);
+                    vagueNouns.splice(k, 1);
+                    break;
+                }
+            }
+        }
+
+        if (specificNouns.length == 0 && vagueNouns.length == 0) {
+            return true;
+        } else {
+            // If any mandatory nouns were left unfilled, return false
+            for (let i = 0; i < specificNouns.length; i++) {
+                if (!(specificNouns[i] instanceof ContentNoun) || (<ContentNoun> specificNouns[i]).getType() == ContentNounType.MANDATORY) {
+                    return false;
+                }
+            }
+
+            // If any non-adaptive nouns were left on the vague side, return false
+            // Adaptive nouns means the content will handle it not existing for us
+            for (let i = 0; i < vagueNouns.length; i++) {
+                if (!(vagueNouns[i] instanceof ContentNoun) || (<ContentNoun> vagueNouns[i]).getType() != ContentNounType.FULLY_ADAPTIVE) {
+                    return false;
+                }
+            }
+
+            // All tests passed, must be okay
+            return true;
+        }
+    }
+
+    public static compareNoun (specificNoun : any, vagueNoun : any) {
+        if (specificNoun instanceof ContentNoun) {
+            return specificNoun.compareAgainst(vagueNoun);
+        } else if (typeof vagueNoun == "function") {
+            // specific must inherit vague or be vague
+            return specificNoun == vagueNoun || specificNoun instanceof vagueNoun ||
+                   (typeof specificNoun == "function" && (<any> specificNoun).prototype instanceof vagueNoun)
+        } else if (vagueNoun instanceof Thing) {
+            // specific must be vague
+            return specificNoun == vagueNoun;
+        }
+
+        // Last strict check, probably returns false every time, unless we're dealing with numbers?
+        return vagueNoun === specificNoun;
+    }
+
+    public static weightNoun (noun : any) : number {
+        if (noun instanceof ContentNoun) {
+            // This is a collection of multiple nouns! Add the weights?
+            let weight = 0;
+            noun.getNouns().forEach(value => {
+                weight += ContentAtom.weightNoun(value);
+            });
+            return weight;
+        } else if (noun instanceof ContentNounSimple) {
+            // It's just a marker
+            return 1;
+        } else {
+            // Normal noun.
+            if (typeof noun == "function") {
+                if (<any>noun.prototype instanceof Thing) {
+                    let specificity = 2; // Vague Thing
+                    let parentClass = Object.getPrototypeOf(noun);
+                    while (parentClass != Thing) {
+                        specificity += 0.1;
+                        parentClass = Object.getPrototypeOf(parentClass);
+                    }
+                    return specificity;
+                } else {
+                    return 2.5; // It's not a "Thing", so it's probably a weird class, which is kind of specific
+                }
+            } else if (noun instanceof Thing) {
+                return 4; // Specific thing
+            } else {
+                return 0.5; // The fuck is this shit, probably a number or something.
+            }
+        }
+    }
+}

+ 57 - 0
app/World/Classes/ContentPicker/ContentAtom/ContentAtomCombat.ts

@@ -0,0 +1,57 @@
+///<reference path="../ContentAtom.ts"/>
+///<reference path="../ContentNounSimple.ts"/>
+/**
+ * This atom should be used in place of the default atom when handling Combat.
+ */
+class ContentAtomCombat extends ContentAtom {
+    /**
+     * Only one of the following markers appears at once.
+     */
+    public static HIT = new ContentNounSimple("HIT");
+    public static MISS = new ContentNounSimple("MISS");
+    public static CRITICAL = new ContentNounSimple("CRITICAL");
+
+    /**
+     * Only one of the following markers appears at once.
+     */
+    public static KNOCKED = new ContentNounSimple("KNOCKED");
+    public static KNOCKED_OFF = new ContentNounSimple("KNOCKED_OFF");
+    public static KILLED = new ContentNounSimple("KILLED");
+
+    public attacker : any;
+    public target : any;
+    public weapons : ContentAtom = new ContentAtom();
+    public markers : ContentAtom = new ContentAtom();
+
+    constructor (attacker : any, target : any, weapons : Array<any> = [], markers : Array<ContentNounSimple> = []) {
+        super();
+        this.attacker = attacker;
+        this.target = target;
+        this.weapons.addNoun(...weapons);
+        this.markers.addNoun(...markers);
+    }
+
+    public compareAgainst (other : ContentAtom) {
+        if (other instanceof ContentAtomCombat) {
+            return (
+                ContentAtom.compareNoun(this.attacker, other.attacker) &&
+                ContentAtom.compareNoun(this.target, other.target) &&
+                this.weapons.compareAgainst(other.weapons) &&
+                this.markers.compareAgainst(other.markers)
+            );
+        }
+        return false;
+    }
+
+    public getAtomPriority () {
+        return (
+            ContentAtom.weightNoun(this.attacker) +
+            ContentAtom.weightNoun(this.target) +
+            this.weapons.getAtomPriority() +
+            this.markers.getAtomPriority()
+        );
+    }
+}
+
+//new ContentAtomCombat(WorldState.player, OrcDebugger, [HumanoidHands], [ContentAtomCombat.HIT]);
+//Looks good.

+ 54 - 0
app/World/Classes/ContentPicker/ContentMolecule.ts

@@ -0,0 +1,54 @@
+/**
+ * Essentially a group of Atoms.
+ */
+class ContentMolecule {
+    protected atoms : Array<ContentAtom> = [];
+
+    public constructor (...atoms : Array<ContentAtom>) {
+        this.addAtom(...atoms);
+    }
+
+    public addAtom (...atoms : Array<ContentAtom>) {
+        this.atoms.push(...atoms);
+        return this;
+    }
+
+    public getAtoms () {
+        return this.atoms;
+    }
+
+    public findMatches (many : Array<ContentMolecule>) {
+        let missingAtoms = [...this.atoms];
+        let chosenOnes = [];
+
+        for (let k = 0; k < many.length; k++) {
+            let vagueMolecule = many[k]
+            let vagueAtoms = [...vagueMolecule.atoms];
+
+            // Check if the ENTIRETY of vague side fits in here.
+            let cachedAtoms = missingAtoms.slice();
+            for (let i = vagueAtoms.length - 1; i >= 0; i--) {
+                let vagueAtom = vagueAtoms[i];
+                for (let k = cachedAtoms.length - 1; k >= 0; k--) {
+                    let specificAtom = cachedAtoms[k];
+                    if (specificAtom.compareAgainst(vagueAtom)) {
+                        cachedAtoms.splice(k, 1);
+                        vagueAtoms.splice(i, 1);
+                        break;
+                    }
+                }
+            }
+
+            if (vagueAtoms.length == 0) {
+                missingAtoms = cachedAtoms;
+                chosenOnes.push(k);
+            }
+        }
+
+        if (missingAtoms.length == 0) {
+            return chosenOnes; // Return best match
+        } else {
+            return []; // No match
+        }
+    }
+}

+ 80 - 0
app/World/Classes/ContentPicker/ContentMolecule/ContentMoleculeCombat.ts

@@ -0,0 +1,80 @@
+///<reference path="../ContentMolecule.ts"/>
+class ContentMoleculeCombat extends ContentMolecule {
+    protected say : Say | ((attacker : any, target : any, weapons : Array<any>, markers : Array<ContentNounSimple>) => Say);
+    protected saidTimes = 0;
+
+    public constructor (description : Say | ((attacker : any, target : any, weapons : Array<any>, markers : Array<ContentNounSimple>) => Say)) {
+        super();
+        this.say = description;
+        ContentMoleculeCombat.MOLECULES.push(this);
+    }
+
+    public getSay(specificMolecule : ContentMolecule) {
+        this.saidTimes++;
+        if (this.say instanceof Say) {
+            return this.say;
+        } else {
+            let combatAtom = <ContentAtomCombat>(specificMolecule.getAtoms()[0]);
+            return this.say(combatAtom.attacker, combatAtom.target, combatAtom.weapons.getNouns(), combatAtom.markers.getNouns());
+        }
+    }
+
+    public getWeight () {
+        // TODO: Check if this implementation is good enough.
+        let weight = -this.saidTimes;
+        this.atoms.forEach(value => {
+            weight += value.getAtomPriority()
+        });
+        return weight;
+    }
+
+    protected static MOLECULES = [];
+
+    /**
+     * Returns an array of ContentMoleculeCombat that fulfill ContentMolecule given.
+     * @param specificMolecule
+     */
+    public static getSay (specificMolecule : ContentMolecule) {
+        // Randomize the array to prevent the same message from showing up every time
+        let many : Array<ContentMoleculeCombat> = new Shuffler(ContentMoleculeCombat.MOLECULES).getShuffled();
+
+        // Order the array placing higher priority messages on top, but messages with same priority are still randomized
+        many.sort((a, b) => {
+            return b.getWeight() - a.getWeight()
+        });
+
+        let matches = specificMolecule.findMatches(many);
+        if (matches.length == 0) {
+            let atom = <ContentAtomCombat> specificMolecule.getAtoms()[0];
+            Elements.CurrentTurnHandler.printAsError(
+                new Say(
+                    "Unable to find match for this combat passage. Attacker: ", atom.attacker,
+                    ". Target: ", atom.target,
+                    ". Weapons used:", ...(atom.weapons.getNouns()),
+                    ". Markers: ", ...(atom.markers.getNouns()),
+                    ". Please report for fixing."
+                )
+            );
+        } else {
+            let finalSay = new Say();
+            matches.forEach((value : ContentMoleculeCombat) => {
+                finalSay.add(value.getSay(specificMolecule));
+            });
+            return finalSay;
+        }
+    }
+}
+
+// new ContentMoleculeCombat(
+//     (attacker, target, weapons, markers) => {
+//         return new Say("Woohoo");
+//     }
+// ).addAtom(
+//     new ContentAtomCombat(
+//         Person,
+//         Person,
+//         [Thing],
+//         [new ContentNoun(ContentAtomCombat.HIT, ContentAtomCombat.CRITICAL).setType(ContentNounType.FULLY_ADAPTIVE), ContentAtomCombat.KNOCKED_OFF]
+//     )
+// );
+// Definitely tons saner than the current implementation

+ 62 - 0
app/World/Classes/ContentPicker/ContentNoun.ts

@@ -0,0 +1,62 @@
+/**
+ * Mandatory = One of the nouns MUST be present.
+ * Optional = If on specific side, can be ignored. Behaves as mandatory on Vague side.
+ * Fully_Adaptive = Behaves as optional on either side.
+ */
+enum ContentNounType {MANDATORY, OPTIONAL, FULLY_ADAPTIVE};
+
+/**
+ * Holds an array of valid nouns. This is meant to allow smarter responses that work off multiple Nouns without requiring too much code.
+ * NOTE: This means only ONE of the nouns needs to fit!
+ */
+class ContentNoun {
+    private nouns : Array<any> = [];
+    private type = ContentNounType.MANDATORY;
+
+    public constructor (...nouns : Array<any>) {
+        this.addNoun(...nouns);
+    }
+
+    public addNoun (...nouns : Array<any>) {
+        this.nouns.push(...nouns);
+        return this;
+    }
+
+    public getNouns () {
+        return [...this.nouns];
+    }
+
+    public setType (type : ContentNounType) {
+        this.type = type;
+        return this;
+    }
+
+    public getType () {
+        return this.type;
+    }
+
+    public compareAgainst (anything : any) {
+        if (anything instanceof ContentNoun) {
+            // test each of my nouns against each of other nouns
+            for (let i = 0; i < this.nouns.length; i++) {
+                let specificNoun = this.nouns[i];
+                for (let k = 0; k < anything.nouns.length; k++) {
+                    let vagueNoun = anything.nouns[k];
+                    if (ContentAtom.compareNoun(specificNoun, vagueNoun)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        } else {
+            // test each noun against anything
+            for (let i = 0; i < this.nouns.length; i++) {
+                if (ContentAtom.compareNoun(this.nouns[i], anything)) {
+                    // A match was found.
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}

+ 12 - 0
app/World/Classes/ContentPicker/ContentNounSimple.ts

@@ -0,0 +1,12 @@
+///<reference path="../../../Elements/Classes/Say.ts"/>
+class ContentNounSimple implements Printable {
+    private name : string;
+
+    public constructor (name : string) {
+        this.name = name;
+    }
+
+    public getPrintedName(): string {
+        return " " + this.name + " ";
+    }
+}

+ 0 - 0
app/World/Classes/ContentPicker/AdaptiveDifferential.ts → app/World/Classes/ContentPickerOld/AdaptiveDifferential.ts


+ 0 - 0
app/World/Classes/ContentPicker/Combat/CombatDescription.ts → app/World/Classes/ContentPickerOld/Combat/CombatDescription.ts


+ 0 - 0
app/World/Classes/ContentPicker/Combat/CombatMarker.ts → app/World/Classes/ContentPickerOld/Combat/CombatMarker.ts


+ 0 - 0
app/World/Classes/ContentPicker/Combat/CombatPokeDescription.ts → app/World/Classes/ContentPickerOld/Combat/CombatPokeDescription.ts


+ 0 - 0
app/World/Classes/ContentPicker/Combat/CombatPokeUnit.ts → app/World/Classes/ContentPickerOld/Combat/CombatPokeUnit.ts


+ 0 - 0
app/World/Classes/ContentPicker/Combat/CombatUnit.ts → app/World/Classes/ContentPickerOld/Combat/CombatUnit.ts


+ 0 - 0
app/World/Classes/ContentPicker/ContentDescription.ts → app/World/Classes/ContentPickerOld/ContentDescription.ts


+ 0 - 0
app/World/Classes/ContentPicker/ContentDifferential.ts → app/World/Classes/ContentPickerOld/ContentDifferential.ts


+ 0 - 0
app/World/Classes/ContentPicker/ContentGroup.ts → app/World/Classes/ContentPickerOld/ContentGroup.ts


+ 0 - 0
app/World/Classes/ContentPicker/ContentMarker.ts → app/World/Classes/ContentPickerOld/ContentMarker.ts


+ 0 - 0
app/World/Classes/ContentPicker/ContentUnit.ts → app/World/Classes/ContentPickerOld/ContentUnit.ts


+ 0 - 0
app/World/Classes/ContentPicker/Fucking/FuckingDescription.ts → app/World/Classes/ContentPickerOld/Fucking/FuckingDescription.ts


+ 0 - 0
app/World/Classes/ContentPicker/Fucking/FuckingMarker.ts → app/World/Classes/ContentPickerOld/Fucking/FuckingMarker.ts


+ 0 - 0
app/World/Classes/ContentPicker/Fucking/FuckingUnit.ts → app/World/Classes/ContentPickerOld/Fucking/FuckingUnit.ts