/* * Copyright (c) 2021 Ross Cunniff * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "stdadv.oah" /* Include the standard package */ using namespace adv; using namespace stdadv; /* The following are Object properties */ public BROKEN, /* Is the robot damaged? */ TOLD, /* Have I told the robot something? */ BSTATE; /* State of the button */ const B_OFF = 0; /* Button is off */ const B_FLASH = 1; /* Button is flashing */ const B_LIT = 2; /* Button is lit */ /* Global variables */ var Score = 0; /* Current score */ /* Utility routines */ proc NoGo, Sayer, Myself, Lifter, DoorCk, TrapCk, RobMov, BlueCk, Header, MyDie, Skore, RobEntr, HatchSD; /* Locations in the dungeon */ LitRoom Redrm {...} LitRoom Bluerm {...} LitRoom Greenrm {...} LitRoom Cellar {...} LitRoom Endrm {...} /* Immovable objects */ class Button(Thing) {public var BSTATE = B_OFF;} Button button(Bluerm) {...} class Door(Thing) {public var pOpens, pOpened;} Door door(Cellar) {...} class Hatch(Thing) {public var pNoTake, pOpens;} Hatch hatch(Bluerm) {...} /* Objects which may become actors */ StdActor me(Redrm); class Robot(StdActor) {public var BROKEN, TOLD;} Robot robot(Greenrm) {...} /* Room descriptions */ ... Redrm { ldesc = proc() { "You are in a large room which is illuminated by a bright ", "red glow. Exits lie to the east and south.\n"; } sdesc = proc() {return Header("Red room", oadl::nargs());} ... } ... Greenrm { ldesc = proc() { "You are in a smallish room which is illuminated by a pleasant ", "green glow. The only exit is to the west.\n"; } sdesc = proc() {return Header("Green room", oadl::nargs());} ... } ... Bluerm { ldesc = proc() { "You are in a tiny room which is barely illuminated by a "; "dim blue glow. There is an exit to the north,"; if (button.BSTATE == B_LIT) { " and most of the floor has tilted up to reveal a hatch leading "; "down into blackness. A button on the wall is glowing brightly."; } else { " and you seem to make out something on the floor."; if (button.BSTATE) " A button on the wall is flashing urgently."; else " There is a button on the wall."; } " Above the button is a sign that reads:\n\n", " DANGER!\n\n", " HIGH VOLTAGE!\n\n"; } sdesc = proc() {return Header("Blue room", oadl::nargs());} ... } ... Cellar { ldesc = proc() { "You are in the cellar. Far above you can be seen a dim blue light."; if (door.pOpened) " An open door leads to the north.\n"; else " You can barely see the outline of a door to the north.\n"; } sdesc = proc() { return Header("Cellar", oadl::nargs()); } ... } ... Endrm { ldesc = proc() { "You exit from the dark cellar into a land filled with singing birds, "; "blooming flowers, flowing streams, and bright blue skies. In other "; "words, you have finished this game!\n"; Score = Score + 25; Skore(); Quit(); } } /* Verbs */ VerbClass score {...} VerbClass push {...} VerbClass shout {...} VerbClass adv::TELLER {...} Thing adv::STRING(); Synonym tell(adv::TELLER); Synonym V_say(adv::TELLER); Synonym press(push); Synonym feel(touch); Synonym yell(shout); /* Verb routines */ ... adv::TELLER { preact = proc() { if (Iobj != robot) { /* The only logical thing to talk to is the robot */ Sayer("Talking to yourself is said to be a sign of impending insanity"); } else if (!(Dobj ?= String)) { /* You must say strings */ Sayer("You must put what you want to say in quotes"); } else if (robot.loc != me.loc) { /* The robot must be in the same place as the player */ if (Myself()) "You don't see the robot here.\n"; } else { /* Everything is OK. Add 25 points to the score */ if (!robot.TOLD) { Score = Score + 25; robot.TOLD = true; } ExitPhase(PHX_CURRENT); } ExitPhase(PHX_ACTOR); } action = proc() { /* Tell the player that we heard him */ "\"Sure thing, Boss.\"\n"; /* Delete the old action */ robot.deactivate(); /* Add the new action - a non-interactive actor */ robot.activate(Dobj, false); } } ... shout { preact = proc() { if (Iobj && (Iobj != robot)) { /* Shouting at things other than the robot */ "AAARRRGGGHHH!\n"; } else if (!(Dobj ?= String)) { /* Shouting things other than strings */ "EEEYYYAAAHHH!\n"; } else if (robot.BROKEN) { "There is no response.\n"; } else { /* Shouting at the robot - same as telling the robot */ if (!robot.TOLD) { Score = Score + 25; robot.TOLD = true; } ExitPhase(PHX_CURRENT); } ExitPhase(PHX_ACTOR); } action = proc() { /* Tell the player we heard them */ if (robot.loc != me.loc) "In the distance you hear the words, "; "\"Sure thing, Boss\"\n"; /* Delete the old robot action */ robot.deactivate(); /* Add the new robot action */ robot.activate(Dobj, false); } } ... push { preact = proc() { /* Expect a plain direct object */ Expect(ONE_OBJ|PLAIN_OBJ, NO_OBJ); CheckAvail(); } action = proc() { Sayer("That doesn't seem to do anything"); ExitPhase(PHX_ACTOR); } } ... score { preact = proc() { /* Score can accept no objects */ Expect(NO_OBJ, NO_OBJ); Skore(); ExitPhase(PHX_ACTOR); } } /* Object properties */ ... button { sdesc = proc() { if (button.BSTATE == B_OFF) "a button"; else if (button.BSTATE == B_FLASH) "an urgently flashing button"; else "a brightly lit button"; } action = proc() { if (Myself() && ((Verb == push)||(Verb == take)||(Verb == touch))) { /* The player tried to do something with the button */ "As you reach for the button, a 10,000,000 volt bolt of lightning "; "arcs toward your finger, disintegrating you upon impact.\n"; MyDie(); } else if ((Verb == push) && (button.BSTATE == B_OFF)) { /* The robot pushed the button */ button.BSTATE = B_FLASH; Score = Score + 50; me.setfuse(Lifter, 4); ExitPhase(PHX_ACTOR); } else if (Verb == take) { /* Can't take the button */ Skip = true; } } } const SimpleRobot = "I am just a simple robot"; ... robot { ldesc = proc() {"There is a robot here.\n";} sdesc = proc() {"a robot";} action = proc() { if (Myself()) { /* I'm doing something with the robot */ if (Verb == adv::TELLER) { if (robot.BROKEN) { "There is no response.\n"; ExitPhase(PHX_ACTOR); } } else if (Verb == take) { "The robot weighs at least 500 pounds!\n"; ExitPhase(PHX_ACTOR); } } else if (Phase == PHASE_ACTOR) { /* This is being called as the Actor action */ ActAction(); switch( Verb ) { case push, go, wait, take, north, south, east, west, up, down : ""; default : /* The robot has a VERY simple vocabulary */ Sayer(SimpleRobot); robot.deactivate(); ExitPhase(PHX_ACTOR); } } else if (Verb == take) { /* The robot is trying to take itself */ Sayer("Mmmph! Akkk!! GGGGRR!! No can do. Sorry"); Skip = true; } else { /* The robot is doing something to itself */ Sayer(SimpleRobot); robot.deactivate(); ExitPhase(PHX_ACTOR); } } } /* We break me( action ) out into a named routine because * StdInit overwrites that property and we need to restore it */ proc MeAct() { if (Phase == PHASE_ACTOR) { /* This is the Actor action - call standard's actor action */ ActAction(); } else if (Verb == take) { Sayer("I thought you would never ask"); Skip = true; } } /* We break hatch( sdesc ) out into a named routine because * the hatch isn't visible until after Lifter has executed */ proc HatchSD() {"an open hatch";} const HatchMSG = "The hatch doesn't budge"; ... hatch { action = proc() { if (Verb == take) { /* Can't take the hatch */ Sayer(HatchMSG); Skip = true; } else if ((Verb == open) || (Verb == push)) { /* Can't open or push it, either */ Sayer(HatchMSG); ExitPhase(PHX_ACTOR); } } pOpens = true pNoTake = true } ... door { sdesc = proc() {"a door";} action = proc() { if (Verb == take) { "You can't take a door!\n"; Skip = true; } } pOpens = true } /* Transition routines. Note that RobMov is used in miss. * This produces the 'The robot exits to the <direction> * messages. The calls to RobEntr produce the messages like * 'The robot enters from the <direction>. */ ... Bluerm { action = proc() { Miss(RobMov, NoGo, NoGo, NoGo, NoGo, TrapCk); Hit($ME, Redrm, 0, 0, 0, 0, Cellar); RobEntr(); } } ... Redrm { action = proc() { Miss(NoGo, BlueCk, RobMov, NoGo, NoGo, NoGo); Hit($ME, 0, Bluerm, Greenrm); RobEntr(); } } ... Greenrm { action = proc() { Miss(NoGo, NoGo, NoGo, RobMov, NoGo, NoGo); Hit($ME, 0, 0, 0, Redrm); RobEntr(); } } ... Cellar { action = proc() { Miss(DoorCk, NoGo, NoGo, NoGo, BlueCk, NoGo); Hit($ME, Endrm, 0, 0, 0, Bluerm); RobEntr(); } } /* Routines */ /* Myself() - returns 1 if "me" is the current actor; 0 otherwise */ proc Myself() { return ($ME == me); } /* Sayer(str) - Says a string with appropriate quoting, depending * on whether the robot or the player is doing the saying. */ proc Sayer(str) { if (Myself()) { "", str, ".\n"; } else if (robot.loc == me.loc) { "\"", str, ", Boss.\"\n"; } else { "You hear a muffled voice in the distance.\n"; "(", str, ")\n"; } } /* NoGo() - "You can't go that way" */ proc NoGo() { Sayer("You can't go that way"); ExitPhase(PHX_ACTOR); } /* Header(str, arg0) - To accomplish the printing of header lines, * each location sdesc needs to return a string if a parameter is * passed to it. By doing return Header(<sdesc>, arg(0)), we can * centralize the saying/returning decision. */ proc Header(str, arg0) { if (!arg0) "", str, ".\n"; return str; } proc RobMov() { if (!Myself() && (robot.loc == me.loc)) { "The robot exits to the "; if (Verb == east) "east"; else if (Verb == west) "west"; else if (Verb == south) "south"; /* The robot can't be seen leaving to the north */ ".\n"; } } proc RobEntr() { if (!Myself() && (robot.loc == me.loc)) { if (Verb == north) "The robot enters from the south.\n"; else if (Verb == east) "The robot enters from the west.\n"; else if (Verb == west) "The robot enters from the east.\n"; /* The robot can't enter from the north in this scenario */ } } proc DoorCk() { if (!door.pOpened) { "The door seems to be closed.\n"; ExitPhase(PHX_ACTOR); } } proc TrapCk() { if (button.BSTATE != B_LIT) NoGo(); } /* BlueCk() - make sure that only one actor is in the blue room * at one time. */ proc BlueCk() { if ((me.loc == Bluerm) || (robot.loc == Bluerm)) { if (Myself()) "The room is too small for both you and the robot to fit.\n"; ExitPhase(PHX_ACTOR); } else if (!Myself() && (button.BSTATE == B_LIT)) { RobMov(); "You hear a loud CRASH! in the distance.\n"; Score = Score - 10; robot.BROKEN = true; robot.move(Bluerm); robot.deactivate(); ExitPhase(PHX_ACTOR); } RobMov(); } /* MyDie() - kill off the player */ proc MyDie() { Score = Score - 50; Skore(); Die(); } /* Lifter() - Lift the hatch, possibly killing the robot or the player */ proc Lifter() { if (me.loc == Bluerm) { "All of a sudden, the floor lifts up, and you are crushed between it ", "and the wall! "; MyDie(); } else { "In the distance, you hear a loud CRASH!\n"; if (robot.loc == Bluerm) { Score = Score - 10; robot.BROKEN = true; robot.deactivate(); } } hatch.sdesc = HatchSD; button.BSTATE = B_LIT; Bluerm.pSeen = false; } /* Prompt - print the status line and a prompt */ proc PROMPT() { Status($ME.loc.sdesc(1), Score, Turns); "> "; } /* Increment - increment the turn counter */ proc INCREMENT() { if (Myself()) { /* We only want to increment once per turn */ Incturn(); } else { /* We don't want Looker executing for the robot */ ExitPhase(PHX_CURRENT); } } /* Skore() - print out the current score. */ proc Skore() { "You have scored ", Score, " out of a possible 100 in ", Turns, " moves.\n"; } /* Dwimming routines */ proc adv::DWIMI(prs,obj) {return Dwimmer(prs,obj);} proc adv::DWIMD(prs,obj) {return Dwimmer(prs,obj);} proc adv::START() { //spec(MARGIN, 69); /* Set the screen to 69 wide */ Setdaemon(INCREMENT); /* Turn counter increment */ StdInit(me); /* Initialize standard */ me.action = MeAct; /* Restore me( action ) */ dirVec = {north, south, east, west, up, down}; /* Our own dirvec */ Prompt = PROMPT; /* and our own prompter */ Indent = true; /* Indent the object descriptions */ } /**** EOF actdemo.adl ****/