--
--	game-bots.occ -- bots
--	Copyright (C) 2004 Fred Barnes <frmb@kent.ac.uk>
--
--	This program is free software; you can redistribute it and/or modify
--	it under the terms of the GNU General Public License as published by
--	the Free Software Foundation; either version 2 of the License, or
--	(at your option) any later version.
--
--	This program is distributed in the hope that it will be useful,
--	but WITHOUT ANY WARRANTY; without even the implied warranty of
--	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--	GNU General Public License for more details.
--
--	You should have received a copy of the GNU General Public License
--	along with this program; if not, write to the Free Software
--	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--

#INCLUDE "types.inc"
#INCLUDE "course.module"

#USE "game-utils"
#USE "game-objects"
#USE "student-bots"


--{{{  PROC wander.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game, VAL BOOL bman)
PROC wander.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game, VAL BOOL bman)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            take.damage; mb1; mb2; ib1
              case := 2
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  MOBILE []BYTE msg:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    #IF FALSE
    --{{{  attach to game
    INITIAL BOOL attached IS FALSE:
    WHILE NOT attached
      SEQ
        CLAIM to.game
          SEQ
            to.game[in] ! attach.client; name; gamelink
            to.game[out] ? CASE
              ok
                attached := TRUE
              cli.error; msg; gamelink
                SKIP
        IF
          NOT attached
            INT t:
            SEQ
              tim ? t
              tim ? AFTER (t PLUS 1000000)
          TRUE
            SKIP
    --}}}
    #ENDIF
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            SKIP
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            IF
              bman
                link[s.out] ! status; 100; 100; "a wandering AI unit"
              TRUE
                link[s.out] ! status; 100; 100; "a wandering robot"
      --}}}
      --{{{  main bot-code
      INT t, d:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (2000000 + (d TIMES 500000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                IF
                  bman
                    link[out] ! say; "I*'m immortal :)"
                  TRUE
                    link[out] ! action; "clinks and clutters"
            tim ? AFTER t
              --{{{  local timeout, do something and reset timeout
              INT v:
              SEQ
                v, seed := random (5, seed)
                IF
                  v < 3
                    --{{{  try and move
                    INT locn:
                    [4]BOOL exit:
                    INT sexit:
                    SEQ
                      link[out] ! lookexits
                      link[in] ? CASE exits; locn; exit
                      sexit := -1
                      INITIAL INT retries IS 100:
                      WHILE (sexit < 0) AND (retries > 0)
                        SEQ
                          v, seed := random (4, seed)
                          IF
                            exit[v]
                              sexit := v
                            TRUE
                              retries := retries - 1
                      IF
                        sexit < 0
                          SKIP    -- not going anywhere..!
                        sexit = 0
                          link[out] ! north
                        sexit = 1
                          link[out] ! east
                        sexit = 2
                          link[out] ! south
                        sexit = 3
                          link[out] ! west
                    --}}}
                  TRUE
                    --{{{  other, say or do something
                    SEQ
                      v, seed := random (7, seed)
                      IF
                        (v >= 4) AND (NOT bman)
                          -- are there any edibles here ?
                          MOBILE []BYTE ename:
                          BOOL eat:
                          SEQ
                            eat := FALSE

                            link[out] ! objects
                            INITIAL BOOL done IS FALSE:
                            WHILE NOT done
                              link[in] ? CASE
                                MOBILE []BYTE oname:
                                BOOL leat:
                                string; oname
                                  SEQ
                                    IF
                                      oname = "bread"
                                        leat := TRUE
                                      oname = "cookies"
                                        leat := TRUE
                                      oname = "ribena"
                                        leat := TRUE
                                      TRUE
                                        leat := FALSE
                                    IF
                                      leat
                                        SEQ
                                          eat := TRUE
                                          ename := oname
                                      TRUE
                                        SKIP
                                end.of.list
                                  done := TRUE
                            link[out] ! get; ename
                            link[in] ? CASE
                              no
                                SKIP
                              CT.OBJECT! obj:
                              get.object; ename; obj
                                SEQ
                                  -- consume
                                  obj[in] ! die
                        v >= 4
                          SKIP
                        bman
                          IF
                            v = 0
                              link[out] ! say; "hello, world!"
                            v = 1
                              link[out] ! action; "looks around"
                            v = 2
                              link[out] ! say; "what a nice place this is"
                            v = 3
                              link[out] ! say; "sometimes I feel so trapped in here"
                        TRUE
                          IF
                            v = 0
                              link[out] ! action; "sweeps up crumbs"
                            v = 1
                              link[out] ! action; "dusts the decor"
                            v = 2
                              link[out] ! action; "looks around"
                            v = 3
                              link[out] ! action; "makes a whirring noise"
                    --}}}
                d, seed := random (6, seed)
                t := t PLUS (3000000 + (d TIMES 500000))
              --}}}

        SKIP
      --}}}
:
--}}}
--{{{  PROC shopkeeper.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
PROC shopkeeper.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            IF
              msg = name
                SKIP
              TRUE
                for.local ! drop.object; msg; oname
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! no
      --}}}
      --{{{  main bot-code
      INT t, d:
      CT.OBJECT! own.holding:
      MOBILE []BYTE own.name:
      BOOL holding:
      [128]BYTE t.buf:
      INT t.len:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        holding := FALSE

        INITIAL BOOL running IS TRUE:
        WHILE running
          SEQ
            tim ? t
            d, seed := random (6, seed)
            t := t PLUS (6000000 + (d TIMES 500000))
            PRI ALT
              local.in ? CASE
                MOBILE []BYTE pname:
                MOBILE []BYTE oname:
                drop.object; pname; oname
                  --{{{  someone dropping something
                  SEQ
                    tim ? t
                    tim ? AFTER (t PLUS 1000000)
                    link[out] ! get; CLONE oname
                    link[in] ? CASE
                      CT.OBJECT! tmpobj:
                      MOBILE []BYTE toname:
                      get.object; toname; tmpobj
                        SEQ
                          IF
                            holding
                              link[out] ! roomdrop; own.name; own.holding
                            TRUE
                              -- manifest some gold
                              FORK triv.object ("gold", ilocn, to.game)
                          holding := TRUE
                          own.holding := tmpobj
                          own.name := toname
                      no
                        SEQ
                          t.len := 0
                          buffer.set.string (t.buf, t.len, "you dropped ")
                          buffer.set.string (t.buf, t.len, oname)
                          buffer.set.string (t.buf, t.len, " ?")
                          link[out] ! say; [t.buf FOR t.len]
                  --}}}
                MOBILE []BYTE who, oname:
                INT damage:
                take.damage; who; oname; damage
                  --{{{  under attack
                  SEQ
                    link[out] ! do.attack; who; "scythe"; 15
                    link[out] ! say; "don*'t bother"
                  --}}}
              tim ? AFTER t
                --{{{  local timeout, do something
                INT v:
                SEQ
                  v, seed := random (6, seed)
                  --{{{  other, say or do something
                  SEQ
                    IF
                      v = 0
                        link[out] ! say; "bullshit"
                      v = 1
                        link[out] ! action; "farts"
                      v = 2
                        link[out] ! action; "moves stuff around"
                      v = 3
                        link[out] ! say; "did you want something ?"
                      v = 4
                        link[out] ! action; "points at the exit"
                      v = 5
                        link[out] ! say; "if you want some action, try the brothel"
                  --}}}
                --}}}

        SKIP
      --}}}
:
--}}}
--{{{  PROC bartender.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
PROC bartender.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            SKIP
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! status; 100; 100; "the owner of this establishment"
      --}}}
      --{{{  main bot-code
      INT t, d:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (5000000 + (d TIMES 1600000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                --{{{  under attack
                SEQ
                  link[out] ! do.attack; who; "scythe"; 15
                  link[out] ! say; "don*'t bother"
                --}}}
            tim ? AFTER t
              --{{{  local timeout, do something and reset timeout
              INT v:
              SEQ
                v, seed := random (2, seed)
                --{{{  other, say or do something
                SEQ
                  IF
                    v = 0
                      link[out] ! action; "wanders around"
                    v = 1
                      link[out] ! action; "cleans glasses"
                --}}}
                d, seed := random (16, seed)
                t := t PLUS (8000000 + (d TIMES 500000))
              --}}}

        SKIP
      --}}}
:
--}}}
--{{{  PROC secretary.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
PROC secretary.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            SKIP
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! status; 100; 100; "a secretary"
      --}}}
      --{{{  main bot-code
      INT t, d:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (5000000 + (d TIMES 1600000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                --{{{  under attack
                SEQ
                  link[out] ! do.attack; who; "pointy stick"; 10
                  link[out] ! say; "don*'t bother"
                --}}}
            tim ? AFTER t
              --{{{  local timeout, do something and reset timeout
              INT v:
              SEQ
                v, seed := random (3, seed)
                --{{{  other, say or do something
                SEQ
                  IF
                    v = 0
                      link[out] ! say; "your money or your login"
                    v = 1
                      link[out] ! say; "printer credits can be obtained from the credit machine"
                    v = 2
                      link[out] ! say; " <your ad here!> "
                --}}}
                d, seed := random (16, seed)
                t := t PLUS (8000000 + (d TIMES 500000))
              --}}}

        SKIP
      --}}}
:
--}}}
--{{{  PROC architect.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
PROC architect.bot (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            take.damage; mb1; mb2; ib1
              case := 2
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}

    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb incoming messages
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            -- someone entering the room
            for.local ! enter.room; msg
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            SKIP
          MOBILE []BYTE pname:
          say.room; pname; msg
            for.local ! say.room; pname; msg
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! status; 100; 100; "the creator of this place"
      --}}}
      --{{{  main bot-code
      INT t, d:
      INT t.len:
      [128]BYTE t.buf:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        INITIAL BOOL running IS TRUE:
        WHILE running
          SEQ
            tim ? t
            d, seed := random (6, seed)
            t := t PLUS (6000000 + (d TIMES 500000))
          
            PRI ALT
              local.in ? CASE
                MOBILE []BYTE pname:
                enter.room; pname
                  --{{{  someone entering the room
                  SEQ
                    tim ? t
                    t := t PLUS 250000
                    tim ? AFTER t
                    t.len := 0
                    buffer.set.string (t.buf, t.len, "looks at ")
                    buffer.set.string (t.buf, t.len, pname)
                    link[out] ! action; [t.buf FOR t.len]
                  --}}}
                MOBILE []BYTE pname, msg:
                say.room; pname; msg
                  --{{{  someone saying something
                  SEQ
                    IF
                      msg[(SIZE msg) - 1] = '?'
                        -- probably a question
                        SEQ
                          tim ? t
                          t := t PLUS 250000
                          tim ? AFTER t
                          link[out] ! say; "i don*'t know!"
                      TRUE
                        SKIP
                  --}}}
                MOBILE []BYTE who, oname:
                INT damage:
                take.damage; who; oname; damage
                  --{{{  someone attacking! -- retaliate
                  TIMER tim:
                  INT t:
                  SEQ
                    tim ? t
                    tim ? AFTER (t PLUS 200000)
                    link[out] ! do.attack; who; "blunt chisel"; 15
                  --}}}
              tim ? AFTER t
                --{{{  local timeout, do something and reset timeout
                INT v:
                SEQ
                  v, seed := random (3, seed)
                  IF
                    v = 0
                      link[out] ! action; "looks at the creation, and is happy"
                    v = 1
                      link[out] ! action; "ponders"
                    v = 2
                      link[out] ! action; "looks around"
                --}}}

        SKIP
      --}}}

:
--}}}
--{{{  PROC wizard.door.bot (VAL []BYTE name, VAL INT ilocn, nesw, tlocn, VAL []BYTE trigname, SHARED CT.GAMECTRL! to.game)
PROC wizard.door.bot (VAL []BYTE name, VAL INT ilocn, nesw, tlocn, VAL []BYTE trigname, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room:
  SHARED CT.ROOMLINK! target.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok1, ok2:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok1)
        rc ? to.room
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, tlocn, to.game, rc!, ok2)
        rc ? target.room
      IF
        (NOT ok1) OR (NOT ok2)
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            SKIP
          MOBILE []BYTE oname:
          drop.object; msg; oname
            IF
              msg = name
                SKIP
              TRUE
                for.local ! drop.object; msg; oname
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! status; 100; 100; "an immortal wizard"
      --}}}
      --{{{  main bot-code
      INT t, d:
      [128]BYTE t.buf:
      INT t.len:
      BOOL gate.open:
      INT gate.timeout:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        gate.open := FALSE

        INITIAL BOOL running IS TRUE:
        WHILE running
          SEQ
            tim ? t
            d, seed := random (6, seed)
            t := t PLUS (6000000 + (d TIMES 500000))
            PRI ALT
              local.in ? CASE
                MOBILE []BYTE pname:
                MOBILE []BYTE oname:
                drop.object; pname; oname
                  --{{{  someone dropping something
                  SEQ
                    IF
                      oname = trigname
                        --{{{  our trigger
                        SEQ
                          tim ? t
                          tim ? AFTER (t PLUS 1000000)
                          link[out] ! get; CLONE oname
                          link[in] ? CASE
                            CT.OBJECT! tmpobj:
                            MOBILE []BYTE toname:
                            get.object; toname; tmpobj
                              SEQ
                                -- we got the trigger :)
                                IF
                                  gate.open
                                    SEQ
                                      tim ? gate.timeout
                                      gate.timeout := gate.timeout PLUS 7500000
                                      link[out] ! say; "the gateway is already open"
                                  TRUE
                                    SEQ
                                      tim ? gate.timeout
                                      gate.timeout := gate.timeout PLUS 7500000
                                      gate.open := TRUE
                                      link[out] ! say; "opening the gateway"
                                      CLAIM to.room
                                        SEQ
                                          to.room[in] ! set.link; nesw
                                          to.room[link.in] ! target.room
                            no
                              SEQ
                                SKIP    -- trigger went away
                        --}}}
                      TRUE
                        --{{{  something else dropped
                        SEQ
                          tim ? t
                          tim ? AFTER (t PLUS 250000)
                          link[out] ! say; "hmm, interesting"
                        --}}}
                  --}}}
                MOBILE []BYTE who, oname:
                INT damage:
                take.damage; who; oname; damage
                  --{{{  under attack
                  TIMER tim:
                  INT t:
                  SEQ
                    link[out] ! action; "gets angry"
                    tim ? t
                    t := t PLUS 500000
                    tim ? AFTER t
                    link[out] ! do.attack; who; "lightening"; 30
                  --}}}
              gate.open & tim ? AFTER gate.timeout
                --{{{  timeout, shut the door
                SEQ
                  CLAIM to.room
                    SEQ
                      to.room[in] ! del.link; nesw
                  gate.open := FALSE
                --}}}
              #IF FALSE
              tim ? AFTER t
                --{{{  local timeout, do something
                INT v:
                SEQ
                  v, seed := random (6, seed)
                  --{{{  other, say or do something
                  SEQ
                    IF
                      v = 0
                        link[out] ! say; "bullshit"
                      v = 1
                        link[out] ! action; "farts"
                      v = 2
                        link[out] ! action; "moves stuff around"
                      v = 3
                        link[out] ! say; "did you want something ?"
                      v = 4
                        link[out] ! action; "points at the exit"
                      v = 5
                        link[out] ! say; "if you want some action, try the brothel"
                  --}}}
                --}}}
              #ENDIF

        SKIP
      --}}}
:
--}}}
--{{{  PROC trader.bot (VAL []BYTE name, VAL INT ilocn, VAL []BYTE gobject, SHARED CT.GAMECTRL! to.game)
PROC trader.bot (VAL []BYTE name, VAL INT ilocn, VAL []BYTE gobject, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INT v:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! self.enter.room; v
                4
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            self.enter.room; v
              case := 3
            take.damage; mb1; mb2; ib1
              case := 4
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        INT v:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            self.enter.room; v
              out ! self.enter.room; v
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  SHARED CT.ROOMLINK! to.room, target.room:
  SEQ

    link, gamelink := MOBILE CT.CLIENT

    --{{{  register
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}
    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          get.name
            link[s.out] ! name; name
          genmsg; msg
            SKIP
          have.object; msg
            link[s.out] ! no
          enter.room; msg
            SKIP
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          INT roomno:
          self.enter.room; roomno
            for.local ! self.enter.room; roomno
          MOBILE []BYTE oname:
          drop.object; msg; oname
            IF
              msg = name
                SKIP
              TRUE
                for.local ! drop.object; msg; oname
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          status
            link[s.out] ! status; 100; 100; "one of the traders"
      --}}}
      --{{{  main bot-code
      INT t, d:
      [128]BYTE t.buf:
      INT t.len:
      INT cur.locn:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1

        cur.locn := ilocn
        INITIAL BOOL running IS TRUE:
        WHILE running
          SEQ
            tim ? t
            d, seed := random (6, seed)
            t := t PLUS (10000000 + (d TIMES 500000))
            PRI ALT
              local.in ? CASE
                INT room:
                self.enter.room; room
                  cur.locn := room
                MOBILE []BYTE pname:
                MOBILE []BYTE oname:
                drop.object; pname; oname
                  --{{{  someone dropping something
                  SEQ
                    --{{{  pick it up
                    SEQ
                      tim ? t
                      tim ? AFTER (t PLUS 1000000)
                      link[out] ! get; CLONE oname
                      link[in] ? CASE
                        CT.OBJECT! tmpobj:
                        MOBILE []BYTE toname:
                        get.object; toname; tmpobj
                          SEQ
                            -- drop eat the object and drop some silver
                            tmpobj[in] ! die
                            FORK triv.object ("silver", cur.locn, to.game)
                        no
                          SKIP    -- went away
                    --}}}
                  --}}}
                MOBILE []BYTE who, oname:
                INT damage:
                take.damage; who; oname; damage
                  --{{{  under attack
                  SEQ
                    SKIP
                  --}}}
              tim ? AFTER t
                --{{{  local timeout, wander somewhere else
                INT v:
                SEQ
                  --{{{  try and move
                  INT locn:
                  [4]BOOL exit:
                  INT sexit:
                  SEQ
                    link[out] ! lookexits
                    link[in] ? CASE exits; locn; exit
                    sexit := -1
                    INITIAL INT retries IS 100:
                    WHILE (sexit < 0) AND (retries > 0)
                      SEQ
                        v, seed := random (4, seed)
                        IF
                          exit[v]
                            sexit := v
                          TRUE
                            retries := retries - 1
                    IF
                      sexit < 0
                        SKIP    -- not going anywhere..!
                      sexit = 0
                        link[out] ! north
                      sexit = 1
                        link[out] ! east
                      sexit = 2
                        link[out] ! south
                      sexit = 3
                        link[out] ! west
                  --}}}
                --}}}
      --}}}
:
--}}}

--{{{  PROC spawn.bot (VAL []BYTE bottype, VAL []BYTE name, VAL INT ilocn, tlocn, tsrc, VAL []BYTE tname, SHARED CT.GAMECTRL! to.game)
PROC spawn.bot (VAL []BYTE bottype, VAL []BYTE name, VAL INT ilocn, tlocn, tsrc, VAL []BYTE tname, SHARED CT.GAMECTRL! to.game)
  IF
    bottype = "wander"
      FORK wander.bot (name, ilocn, to.game, TRUE)
    bottype = "cleaner"
      FORK wander.bot (name, ilocn, to.game, FALSE)
    bottype = "shopkeeper"
      FORK shopkeeper.bot (name, ilocn, to.game)
    bottype = "bartender"
      FORK bartender.bot (name, ilocn, to.game)
    bottype = "secretary"
      FORK secretary.bot (name, ilocn, to.game)
    bottype = "architect"
      FORK architect.bot (name, ilocn, to.game)
    bottype = "wizard"
      FORK wizard.door.bot (name, ilocn, tsrc, tlocn, tname, to.game)
    bottype = "trader"
      FORK trader.bot (name, ilocn, tname, to.game)
    TRUE
      spawn.student.bot (bottype, name, ilocn, to.game)
:
--}}}

