4 コミット 0efcec5da6 ... aab543c0b9

作者 SHA1 メッセージ 日付
  Reddo aab543c0b9 Update dist 5 年 前
  Reddo 81b67e8568 Start passing AI stuff to where it belongs 5 年 前
  Reddo 4690d54899 Turn duration no longer necessary for debug - turn duration was solved forever. 5 年 前
  Reddo c200472d21 Autmoatically throttle AI in the event of the game becoming too slow. Should never trigger outside of stress testing, though. 5 年 前

+ 17 - 0
app/Elements/Classes/Say.ts

@@ -36,12 +36,29 @@ class Say {
 
     private centered : boolean = false;
 
+    public static Mention (target : Thing, uppercase = true) : Array<any> {
+        if (target == WorldState.player) {
+            return [new SayYou(uppercase)];
+        } else {
+            return [new SayThe(uppercase), target];
+        }
+    }
+
+    public static YourTheir (target : Thing, uppercase = true) {
+        if (target == WorldState.player) {
+            return [new SayYour(uppercase)];
+        } else {
+            return [new SayHisHersIts(target, uppercase)];
+        }
+    }
+
     public constructor (...objs) {
         this.add(...objs);
     }
 
     public add (...objs : Array<Say | OneOf | Object | Printable | string | number | String | ((say : Say) => string)>) {
         this.sequence.push(...objs);
+        return this;
     }
 
     public remove (...objs) {

+ 18 - 0
app/Elements/Classes/Say/SayYou.ts

@@ -0,0 +1,18 @@
+class SayYou extends Say {
+    private node : Text = document.createTextNode("a ");
+    private uppercase = true;
+
+    public constructor (autoUppercase = true) {
+        super();
+        this.uppercase = autoUppercase;
+    }
+
+    public async getPureElements (say : Say) : Promise<Array<Element | Text>> {
+        this.node.nodeValue =  "you";
+        if (this.uppercase && say.currentParagraph.length == 0) {
+            this.node.nodeValue = this.node.nodeValue.charAt(0).toUpperCase()
+                + this.node.nodeValue.substr(1, this.node.nodeValue.length - 1);
+        }
+        return [this.node];
+    }
+}

+ 18 - 0
app/Elements/Classes/Say/SayYour.ts

@@ -0,0 +1,18 @@
+class SayYour extends Say {
+    private node : Text = document.createTextNode("a ");
+    private uppercase = true;
+
+    public constructor (autoUppercase = true) {
+        super();
+        this.uppercase = autoUppercase;
+    }
+
+    public async getPureElements (say : Say) : Promise<Array<Element | Text>> {
+        this.node.nodeValue =  "your";
+        if (this.uppercase && say.currentParagraph.length == 0) {
+            this.node.nodeValue = this.node.nodeValue.charAt(0).toUpperCase()
+                + this.node.nodeValue.substr(1, this.node.nodeValue.length - 1);
+        }
+        return [this.node];
+    }
+}

+ 22 - 6
app/World/Classes/AI.ts

@@ -41,6 +41,8 @@ class AI {
     public static talktoRules = new Rulebook<Thing>("Default Talk To Rules");
     public extraTalktoRules : Array<Rulebook<Thing>> = [];
 
+    public storedReaction : Action;
+
     public constructor (options : AIOptions) {
         for (let key in options) {
             this[key] = options[key];
@@ -55,6 +57,12 @@ class AI {
         let promise : Promise<Action>;
         let inCombat = false;
 
+        if (this.storedReaction != undefined) {
+            let result = this.storedReaction;
+            this.storedReaction = undefined;
+            return result;
+        }
+
         if (this.hostileTargets.length > 0) {
             for (let i = this.hostileTargets.length - 1; i >= 0; i--) {
                 if (this.actor.getRoom() == this.hostileTargets[i].getRoom()) {
@@ -135,6 +143,16 @@ class AI {
         this.hostilityLevels.splice(index, 1);
     }
 
+    public getHostilityTo (target : Thing) {
+        let index = this.hostilityTargets.indexOf(target);
+        if (index != -1) {
+            return this.hostilityLevels[index];
+        } else {
+            return 0;
+        }
+    }
+
+    // TODO: Make this a rulebook.
     public coolOff () {
         for (let i = this.hostilityTargets.length - 1; i >= 0; i--) {
             this.hostilityLevels[i] -= this.coolOffRate;
@@ -150,12 +168,10 @@ class AI {
         }
     }
 
-    public getHostilityTo (target : Thing) {
-        let index = this.hostilityTargets.indexOf(target);
-        if (index != -1) {
-            return this.hostilityLevels[index];
-        } else {
-            return 0;
+    // TODO: Make this a rulebook. This happens every time an aggressive action is done.
+    public getPoked (action : Action) {
+        if (this.actor instanceof Person) {
+            AIRules.getPoked(this.actor, action);
         }
     }
 }

+ 45 - 49
app/World/Classes/AI/AIGrudge.ts

@@ -11,60 +11,56 @@ module AIRules {
     export var resultRetaliate = new ContentMarker("I'll hit you once so you can see how you like it", true);
     export var resultHostile = new ContentMarker("I'll hit you until you drop dead.", true);
 
-    export function printGrudgeResult (aggressor : Thing, victim : Person, ...markers : Array<ContentMarker>) {
-        if (victim.isVisibleTo(WorldState.player)) {
-            let group = new ContentGroup();
-            let unit = new CombatPokeUnit();
-            group.addUnit(unit);
-            unit.setTarget(victim);
-            unit.setAggressor(aggressor);
-            unit.addMarker(...markers);
+    export function printGrudgeResult (aggressor : Thing, victim : Person, ...markers : Array<ContentMarker>) : Say {
+        let group = new ContentGroup();
+        let unit = new CombatPokeUnit();
+        group.addUnit(unit);
+        unit.setTarget(victim);
+        unit.setAggressor(aggressor);
+        unit.addMarker(...markers);
 
-            if (aggressor == WorldState.player) {
-                victim.AI.warnedTimes++;
-            }
-            Elements.CurrentTurnHandler.printAsContent(new Say(...CombatPokeDescription.getDescription(group)));
+        if (aggressor == WorldState.player) {
+            victim.AI.warnedTimes++;
         }
+        return new Say(...CombatPokeDescription.getDescription(group));
     }
 
-    export var Grudge = AI.rules.createAndAddRule({
-        name : "Grudge",
-        firstPriority : AIRules.PRIORITY_ACTING_ON_SITUATION,
-        priority : AIRules.PRIORITY_ACTING_ON_SITUATION - 3,
-        code : (runner : RulebookRunner<Person>) => {
-            let person = runner.noun;
-            for (let i = 0; i < TurnSequence.currentTurnActionsTargets.length; i++) {
-                if (TurnSequence.currentTurnActionsTargets[i] == person) {
-                    let action = TurnSequence.currentTurnActions[i];
-                    if (action.actingAgressively) {
-                        person.AI.addHostility(action.actor, action.aggressivenessRating);
-                        if (action.actor == WorldState.player) {
-                            person.reputation -= action.aggressivenessRating;
-                        }
+    // TODO: Make a rulebook called "ReactionTo", this is a rule that should be there with a condition of Action = ActingAggressively.
+    export function getPoked (person : Person, action : Action) {
+        if (person.AI.hostileTargets.includes(action.actor)) {
+            return; // Already hostile
+        }
+        person.AI.addHostility(action.actor, action.aggressivenessRating);
+        if (action.actor == WorldState.player) {
+            person.reputation -= action.aggressivenessRating;
+        }
 
-                        let ai = person.AI;
-                        let gain = ai.grudgeRate * action.aggressivenessRating;
-                        let actionLevel = actionMin;
-                        let result = resultNotHostile;
-                        if (ai.getHostilityTo(action.actor) > 100) {
-                            result = resultHostile;
-                        } else if (ai.retaliates && ai.getHostilityTo(action.actor) >= (ai.hostileThreshold / 2)) {
-                            result = resultRetaliate;
-                        }
-                        if (gain >= (ai.hostileThreshold / 2)) {
-                            actionLevel = actionMax;
-                        } else if (gain >= (ai.hostileThreshold / 4)) {
-                            actionLevel = actionMed;
-                        }
+        let ai = person.AI;
+        let response : Say;
+        let gain = ai.grudgeRate * action.aggressivenessRating;
+        let actionLevel = actionMin;
+        let result = resultNotHostile;
+        if (ai.getHostilityTo(action.actor) > 100) {
+            result = resultHostile;
+        } else if (ai.retaliates && ai.getHostilityTo(action.actor) >= (ai.hostileThreshold / 2)) {
+            result = resultRetaliate;
+        }
+        if (gain >= (ai.hostileThreshold / 2)) {
+            actionLevel = actionMax;
+        } else if (gain >= (ai.hostileThreshold / 4)) {
+            actionLevel = actionMed;
+        }
+        response = printGrudgeResult(action.actor, person, actionLevel, result);
 
-                        printGrudgeResult(action.actor, person, actionLevel, result);
-                        if (result == resultRetaliate) {
-                            return new ActionAttack(person, action.actor);
-                        }
-                        return new ActionWait(person); // we talked so we shouldn't move further
-                    }
-                }
-            }
+        let nAct : Action;
+        if (result == resultRetaliate) {
+            nAct = new ActionAttack(person, action.actor);
+            nAct.finalSay = response.add(Say.PARAGRAPH_BREAK);
+            nAct.finalSayOnEnd = false;
+        } else {
+            nAct = new ActionWait(person);
+            nAct.finalSay = response.add(Say.PARAGRAPH_BREAK);
         }
-    });
+        person.AI.storedReaction = nAct;
+    }
 }

+ 28 - 29
app/World/Classes/Action.ts

@@ -21,6 +21,9 @@ class Action {
     public requiresVisibility = true; // First noun must be visible and in the same room
     public allowedStances = [PersonStance.ALLFOURS, PersonStance.STANDING];
 
+    public finalSay : Say;
+    public finalSayOnEnd = true;
+
 
     public constructor (actor : Thing, ...nouns : Array<any>) {
         this.actor = actor;
@@ -88,6 +91,14 @@ class Action {
          */
         TurnSequence.addAction(this);
 
+        if (this.finalSay != undefined) {
+            if (this.finalSayOnEnd) {
+                this.say.add(...this.finalSay.sequence);
+            } else {
+                this.say = new Say(...this.finalSay.sequence, ...this.say.sequence);
+            }
+        }
+
         return this.say;
     }
 
@@ -197,32 +208,20 @@ Action.check.addRule(
 );
 
 // TODO: Pass everything on here directly to the AI so that IT can handle this.
-// Action.carry.addRule(
-//     new Rule({
-//         name : "Check any Action - Angery",
-//         firstPriority : Rule.PRIORITY_LOWEST,
-//         priority: Rule.PRIORITY_LOWEST,
-//         code : (rulebook : RulebookRunner<Action>) => {
-//             let action = <Action> rulebook.noun;
-//             let target = action.getNoun(0);
-//             let tai = (<Person> target).AI;
-//             tai.anger += action.aggressivenessRating * tai.grudgeRate;
-//             let cu = new CombatPokeUnit().setTarget(target);
-//             if (tai.anger < 10) {
-//                 cu.addMarker(CombatPokeResult.NOHEAT);
-//             } else if (tai.anger < 100) {
-//                 cu.addMarker(CombatPokeResult.ANNOYED);
-//             } else {
-//                 cu.addMarker(CombatPokeResult.AGGROED);
-//                 (<Person>target).AI.hostileTo.push(action.actor);
-//             }
-//
-//
-//             action.say.add(Say.PARAGRAPH_BREAK, ...CombatPokeDescription.getDescription(new ContentGroup(cu)));
-//         },
-//         conditions : (rulebook : RulebookRunner<Action>) => {
-//             let action = <Action> rulebook.noun;
-//             return action.actor == WorldState.player && action.actingAgressively && action.getNoun(0) instanceof Person && !action.getNoun(0).isHostileTo(action.actor) && action.getNoun(0).getHealthOnScale() > 0;
-//         }
-//     })
-// );
+Action.carry.addRule(
+    new Rule({
+        name : "Check any Action - Angery",
+        firstPriority : Rule.PRIORITY_LOWEST,
+        priority: Rule.PRIORITY_LOWEST,
+        code : (rulebook : RulebookRunner<Action>) => {
+            let action = <Action> rulebook.noun;
+            let target = action.getNoun(0);
+            let tai = (<Person> target).AI;
+            tai.getPoked(action);
+        },
+        conditions : (rulebook : RulebookRunner<Action>) => {
+            let action = <Action> rulebook.noun;
+            return action.actingAgressively && action.getNoun(0) instanceof Person;
+        }
+    })
+);

+ 31 - 5
app/World/EveryTurn.ts

@@ -1,26 +1,50 @@
 /// <reference path="./Classes/Rulebook.ts" />
 /// <reference path="./Classes/Rule.ts" />
 /// <reference path="Classes/Things/Person.ts" />
+/// <reference path="TurnSequence.ts" />
 module EveryTurn {
     export var EveryTurn = new Rulebook("Every Turn");
+    export var lastTurnTime = 10;
+    export var lastPercentage = 1;
+    export var minPercentage = 0.05;
+    export var maxTurnDuration = 100;
+    export var maxTurnsDead = 4;
 
     export var RunAIRule = EveryTurn.createAndAddRule({
         name : "Run NPC AI Rule",
         code : async function () {
+            let allPeople = <Array<Person>> Thing.InsideRoomRelation.getAnyRightType(Person);
+            let change;
+            if (lastTurnTime > maxTurnDuration) {
+                change = -0.1 * (lastTurnTime / maxTurnDuration);
+            } else {
+                change = +0.1 * (maxTurnDuration / lastTurnTime);
+            }
+            lastPercentage += change;
+            lastPercentage = lastPercentage < minPercentage ? minPercentage : lastPercentage > 1 ? 1 : lastPercentage;
             function isAIAvailable (person : Person) {
                 return (person != WorldState.player
-                    && ((person.getRoom() instanceof RoomRandom
-                        && (<RoomRandom> person.getRoom()).placed))
-                            && (person.lastActionTurn < WorldState.getCurrentTurn()));
+                    && (
+                        (person.getRoom() instanceof RoomRandom
+                        && (<RoomRandom>person.getRoom()).placed)
+                    )
+                    && (person.lastActionTurn < WorldState.getCurrentTurn())
+                    && (
+                        person.lastActionTurn < (WorldState.getCurrentTurn() - maxTurnsDead) || Math.random() <= lastPercentage ||
+                        person.getRoom() == WorldState.player.getRoom() || WorldState.player.getRoom().getConnectedRooms().includes(person.getRoom())
+                    )
+                );
             }
 
 
-            let people = <Array<Person>> Thing.InsideRoomRelation.getAnyRightType(Person).filter(isAIAvailable);
+            let people = allPeople.filter(isAIAvailable);
+
+            let t0 = performance.now();
             for (let i = 0; i < people.length; i++) {
                 let person = people[i];
                 if (person.getHealthOnScale() > 0) {
                     let action = await people[i].AI.execute();
-                    let visible = people[i].isVisibleTo(WorldState.player);
+                    let visible = people[i].isVisibleTo(WorldState.player); // Was the actor visible BEFORE doing the action?
                     if (action == undefined) action = new ActionWait(person);
                     let printValue: Say = await action.execute();
 
@@ -33,6 +57,8 @@ module EveryTurn {
                     }
                 }
             }
+            let t1 = performance.now();
+            lastTurnTime = t1 - t0;
         }
     });
 

+ 1 - 1
app/World/Settings.ts

@@ -3,7 +3,7 @@ module Settings {
     var debugEmpty = () => {};
 
     export var hardDebug = false;
-    export var sayTurnTime = true;
+    export var sayTurnTime = false;
 
     export function setDebug (isDebug : boolean) {
         if (isDebug) {

+ 7 - 12
content/main.ts

@@ -241,7 +241,7 @@ for (let i = 0; i < 0; i++) {
 }
 let randomOrc;
 let randomOrc2;
-for (let i = 0; i < 800; i++) {
+for (let i = 0; i < 8; i++) {
     let orc = new OrcDebugger();
     randomOrc = orc;
     if (randomOrc2 == undefined) {
@@ -324,17 +324,17 @@ spitroast.addUnit()
 
 (new CombatDescription("Allranging Fists"))
     .setDescriptionFunction((actor, target, weapons, markers) => {
-        let say = new Say("You attack ", new SayThe(), target, " with your fists");
+        let say = new Say(...Say.Mention(actor), " attack", target != WorldState.player ? "s " : " ", ...Say.Mention(target), " with ", ...Say.YourTheir(target) ," fists");
         if (markers.indexOf(CombatHit.MISS) != -1) {
-            say.add(", but you miss");
+            say.add(", but ", ...Say.Mention(actor), " miss");
         } else if (markers.indexOf(CombatHit.CRITICAL) != -1) {
             say.add(", it is a strong hit");
         }
 
         if (markers.indexOf(CombatResult.KNOCKED) != -1) {
-            say.add(", the strength of your attack knocks ", new SayHimHerIt(target), " on the floor.");
+            say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target) , " on the floor.");
         } else if (markers.indexOf(CombatResult.KNOCKED_OFF) != -1) {
-            say.add(", the strength of your attack knocks ", new SayHimHerIt(target), " unconscious.");
+            say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target), " unconscious.");
         } else if (markers.indexOf(CombatResult.KILLED) != -1) {
             say.add(", ", new SayHeSheIt(target), " dies.");
         } else {
@@ -355,7 +355,7 @@ spitroast.addUnit()
         let say = new Say(new SayBold(target), ": ");
 
         let action = new SayAction();
-        action.add(new SayThe(), target, " looks at ");
+        action.add("looks at ");
         if (aggressor != WorldState.player) {
             action.add(new SayThe(), aggressor)
         } else {
@@ -403,9 +403,4 @@ spitroast.addUnit()
     .setAggressor(Person)
     .setTarget(OrcDebugger)
     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.resultRetaliate, AIRules.resultHostile, AIRules.resultNotHostile))
-    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.actionMin, AIRules.actionMed, AIRules.actionMax));
-
-
-let outsideRoom = new Room("Outside Room");
-window['outsideRoom'] = outsideRoom;
-window['player'] = WorldState.player;
+    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.actionMin, AIRules.actionMed, AIRules.actionMax));

ファイルの差分が大きいため隠しています
+ 0 - 0
dist/The Obelisk.html


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません