commit:0cbf22bc7a16549a69e184fd6e7e5f47bcbbcccb
author:Chip Black
committer:Chip Black
date:Thu Aug 6 01:47:02 2020 -0500
parents:7cdca8743f42ecb37ef3b125c6ea80545f355c7a
Add buying, object conditionals, move command

... and sleep is now fractional seconds.
diff --git a/src/direction.ts b/src/direction.ts
line changes: +49/-0
index 0000000..9598f33
--- /dev/null
+++ b/src/direction.ts
@@ -0,0 +1,49 @@
+import { Token } from 'nearley';
+
+export enum Direction {
+    North,
+    East,
+    South,
+    West,
+}
+
+export function directionFromToken(t: Token): Direction {
+    const s = typeof t == 'string' ? t : t.value;
+    switch(s.slice(1)) {
+        case 'n':
+        case 'north':
+            return Direction.North;
+            break;
+        case 'e':
+        case 'east':
+            return Direction.East;
+            break;
+        case 's':
+        case 'south':
+            return Direction.South;
+            break;
+        case 'w':
+        case 'west':
+            return Direction.West;
+            break;
+        default:
+            throw new Error('Invalid direction: ' + s);
+    }
+}
+
+export function directionToDelta(d: Direction): [number, number] {
+    switch(d) {
+        case Direction.North:
+            return [0, -1];
+            break;
+        case Direction.East:
+            return [1, 0];
+            break;
+        case Direction.South:
+            return [0, 1];
+            break;
+        case Direction.West:
+            return [-1, 0];
+            break;
+    }
+}

diff --git a/src/script/menu.ts b/src/script/menu.ts
line changes: +35/-1
index f58fa38..289c1a3
--- a/src/script/menu.ts
+++ b/src/script/menu.ts
@@ -58,9 +58,21 @@ export class MenuInstruction implements ScriptInstruction {
                         condition,
                     };
                 case 'buy':
+                    const obj = gs.objects.get(o[3][1].value);
+                    if (!obj) {
+                        throw new Error('Object with id ' + o[3][1].value + ' doesn\'t exist in this world.');
+                    }
+
+                    let label;
+                    if (o[5].value == 0) {
+                        label = 'Take ' + obj.name;
+                    } else {
+                        label = 'Buy ' + obj.name + ' ($' + o[5].value + ')';
+                    }
+
                     return <MenuBuy>{
                         kind: MenuOptionType.Buy,
-                        label: o[3][1].value, // TODO: fetch object name from object database
+                        label,
                         object: o[3][1].value,
                         value: o[5].value,
                         condition,
@@ -81,6 +93,21 @@ export class MenuInstruction implements ScriptInstruction {
         return new Promise( (resolve, reject) => {
             gs.actions.clear();
             for (let o of this.options) {
+                if (o.condition) {
+                    switch(o.condition.type) {
+                    case MenuConditionType.Flag:
+                        // TODO flags
+                        break;
+                    case MenuConditionType.Object:
+                        let status = gs.playerInventory.has(o.condition.name) && gs.playerInventory.get(o.condition.name) > 0;
+                        if (o.condition.negate) {
+                            status = !status;
+                        }
+                        console.log(o.condition, status);
+                        if (!status) continue;
+                        break;
+                    }
+                }
                 gs.actions.addAction(o.label, () => {
                     gs.actions.clear();
                     switch(o.kind) {
@@ -89,6 +116,13 @@ export class MenuInstruction implements ScriptInstruction {
                             break;
                         case MenuOptionType.Buy:
                             console.log('Buy', o.object, 'for', o.value);
+                            gs.addInventory(o.object);
+                            const objectName = gs.objects.get(o.object).name;
+                            if (o.value == 0) {
+                                gs.print("You have taken the", objectName);
+                            } else {
+                                gs.print("You have bought a", objectName, "for", '$' + o.value);
+                            }
                             resolve({ status: InstructionStatus.OK });
                             break;
                         case MenuOptionType.Exit:

diff --git a/src/script/move.ts b/src/script/move.ts
line changes: +28/-0
index 0000000..13eab82
--- /dev/null
+++ b/src/script/move.ts
@@ -0,0 +1,28 @@
+import { ScriptInstruction, InstructionStatus, InstructionPromise } from './';
+
+import gs from '../gamestate';
+import { Direction, directionToDelta } from '../direction';
+
+export class MoveInstruction implements ScriptInstruction {
+    direction?: Direction;
+    newPosition?: [number, number];
+
+    constructor(d: Direction | number, e?: number) {
+        if (typeof e == 'undefined') {
+            this.direction = d;
+        } else {
+            this.newPosition = [d, e];
+        }
+    }
+
+    execute(): InstructionPromise {
+        if (typeof this.direction != 'undefined') {
+            const [dx, dy] = directionToDelta(this.direction);
+            gs.movePlayerRel(dx, dy);
+        } else {
+            gs.movePlayer(this.newPosition[0], this.newPosition[1]);
+        }
+
+        return Promise.resolve({ status: InstructionStatus.OK });
+    }
+}

diff --git a/src/script/parser.ne b/src/script/parser.ne
line changes: +15/-7
index b133d13..2dd5ba5
--- a/src/script/parser.ne
+++ b/src/script/parser.ne
@@ -9,12 +9,15 @@ import { FinishInstruction } from './finish';
 import { ClearScreenInstruction } from './clear';
 import { SleepInstruction } from './sleep';
 import { PauseInstruction } from './pause';
+import { MoveInstruction } from './move';
+
+import { Direction, directionFromToken } from '../direction';
 
 const lexer = moo.compile({
-  ws:     { match: /[ \t\r\n]+/, lineBreaks: true },
-  number: /[0-9]+(?:\.[0-9]+)?/,
-  keyword: ['say', 'menu', 'item', 'end', 'exit', 'goto', 'if',
-  'finish', 'clear', 'buy', 'transact', 'setflag'],
+  ws:      { match: /[ \t\r\n]+/, lineBreaks: true },
+  number:  /[0-9]+(?:\.[0-9]+)?/,
+  keyword: ['say', 'menu', 'item', 'end', 'exit', 'goto', 'if', 'finish', 'clear', 'buy', 'transact', 'setflag', 'move'],
+  direction: /\+(?:n|e|s|w|north|east|south|west)/,
   label:  /[a-zA-Z_][a-zA-Z0-9_]*:/,
   word:   /[a-zA-Z_][a-zA-Z0-9_]*/,
   dqstring: { match: /"(?:\\["\\]|[^\n"\\])*"/, value: s => s.slice(1, -1) },
@@ -23,6 +26,7 @@ const lexer = moo.compile({
   bang:      '!',
   flag_mark: '$',
   obj_mark:  '@',
+  comma:     ',',
 });
 %}
 
@@ -36,11 +40,13 @@ statement -> %label __                      {% data => new Label(data[0].value.s
 statement -> "goto" __ %word __             {% data => new GotoInstruction(data[2].value) %}
 statement -> "finish" __                    {% data => new FinishInstruction() %}
 statement -> "clear" __                     {% data => new ClearScreenInstruction() %}
-statement -> "sleep" __ %number __          {% data => new SleepInstruction(data[2]) %}
+statement -> "sleep" __ number __           {% data => new SleepInstruction(data[2]) %}
 statement -> "pause" __ string __           {% data => new PauseInstruction(data[2]) %}
+statement -> "move" __ direction __         {% data => new MoveInstruction(data[2]) %}
+statement -> "move" __ number _ %comma _ number __ {% data => new MoveInstruction(data[2], data[6]) %}
 
 menuitem -> if:? "item" __ string __ program "end" __
-menuitem -> if:? "buy" __ obj_identifier __ %number __
+menuitem -> if:? "buy" __ obj_identifier __ number __
 menuitem -> if:? "exit" __ string __
 
 if -> "if" __ %bang:? flag_identifier __
@@ -49,11 +55,13 @@ if -> "if" __ %bang:? obj_identifier __
 flag_identifier -> %flag_mark %word
 obj_identifier -> %obj_mark %word
 
-number -> %number {% data => parseInt(data[0]) %}
+number -> %number {% data => parseFloat(data[0]) %}
 
 string -> %dqstring {% data => data[0].value %}
 string -> %sqstring {% data => data[0].value %}
 string -> %mlstring {% data => data[0].value %}
 
+direction -> %direction {% data => directionFromToken(data[0]) %}
+
 __ -> %ws {% _ => null %}
 _ -> null | %ws {% _ => null %}

diff --git a/src/script/sleep.ts b/src/script/sleep.ts
line changes: +4/-4
index 650b7aa..46a456b
--- a/src/script/sleep.ts
+++ b/src/script/sleep.ts
@@ -1,15 +1,15 @@
 import { ScriptInstruction, InstructionPromise, InstructionStatus } from './';
 
 export class SleepInstruction implements ScriptInstruction {
-    duration: number
+    milliseconds: number
 
-    constructor(duration: number) {
-        this.duration = duration;
+    constructor(seconds: number) {
+        this.milliseconds = seconds * 1000;
     }
 
     execute(): InstructionPromise {
         return new Promise( (resolve, reject) => {
-            setTimeout(resolve, this.duration, { status: InstructionStatus.OK });
+            setTimeout(resolve, this.milliseconds, { status: InstructionStatus.OK });
         });
     }
 }