/*
* 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 ****/