--
--	game-control.occ -- main game control
--	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"

#INCLUDE "config.inc"
#USE "game-utils"
#USE "game-client"
#USE "game-tty"

#INCLUDE "file.module"


--{{{  PROC game.makelink (SHARED CT.ROOMLINK! room, target, VAL INT nesw)
PROC game.makelink (SHARED CT.ROOMLINK! room, target, VAL INT nesw)
  CLAIM room
    SEQ
      room[in] ! set.link; nesw
      room[link.in] ! target
:
--}}}
--{{{  PROC game.control (CT.GAMECTRL? in, SHARED CT.GAMECTRL! selflink, CHAN BYTE err!)
PROC game.control (CT.GAMECTRL? in, SHARED CT.GAMECTRL! selflink, CHAN BYTE err!)
  INT nrooms, hiroom:
  MOBILE []SHARED CT.ROOMLINK! rlinks:
  MOBILE []BOOL rlinks.ok:

  INT nnames:
  MOBILE []MOBILE []BYTE names:
  MOBILE []BOOL names.ok:
  SEQ
    nrooms := ##MAX.ROOMS
    hiroom := -1
    rlinks := MOBILE [nrooms]SHARED CT.ROOMLINK!
    rlinks.ok := MOBILE [nrooms]BOOL
    SEQ i = 0 FOR nrooms
      rlinks.ok[i] := FALSE

    nnames := ##MAX.USERS
    names := MOBILE [nnames]MOBILE []BYTE
    names.ok := MOBILE [nnames]BOOL
    SEQ i = 0 FOR nnames
      names.ok[i] := FALSE

    out.string ("the new occam adventure, version ", 0, err!)
    out.string (##VERSION, 0, err!)
    err ! '*n'
    out.string ("game.control: starting...*n", 0, err!)
    INITIAL BOOL running IS TRUE:
    WHILE running
      in[in] ? CASE
        spawn.room
          SKIP
        spawn.object
          SKIP
        SHARED CT.ROOMLINK! trl:
        INT roomno:
        attach.room; roomno; trl
          --{{{  attach.room
          SEQ
            IF
              roomno < 0
                roomno := 0
              roomno >= nrooms
                roomno := nrooms - 1
              TRUE
                SKIP
            rlinks.ok[roomno] := TRUE
            rlinks[roomno] := trl
            IF
              roomno > hiroom
                hiroom := roomno
              TRUE
                SKIP
            -- out.string ("game.control: room ", 0, err!)
            -- out.int (roomno, 0, err!)
            -- out.string (" attached.*n", 0, err!)
          --}}}
        INT roomno, nesw, target:
        link.room; roomno; nesw; target
          --{{{  link.room
          SEQ
            IF
              (((roomno < 0) OR (roomno >= nrooms)) OR ((target < 0) OR (target >= nrooms))) OR ((nesw < 0) OR (nesw >= 4))
                in[out] ! error; "bad value"
              (NOT rlinks.ok[roomno]) OR (NOT rlinks.ok[target])
                in[out] ! error; "missing room"
              TRUE
                SHARED CT.ROOMLINK! ctr:
                SEQ
                  ctr := CLONE rlinks[target]
                  CLAIM rlinks[roomno]
                    SEQ
                      rlinks[roomno][in] ! set.link; nesw
                      rlinks[roomno][link.in] ! ctr
                  in[out] ! ok
                  -- out.string ("game.control: room-room linked ok*n", 0, err!)
          --}}}
        INT roomno:
        get.room; roomno
          --{{{  get.room
          IF
            (roomno < 0) OR (roomno >= nrooms)
              in[out] ! error; "bad value"
            NOT rlinks.ok[roomno]
              in[out] ! error; "missing room"
            TRUE
              SEQ
                in[out] ! room; rlinks[roomno]
          --}}}
        CT.CLIENT! cli:
        MOBILE []BYTE cname:
        attach.client; cname; cli
          --{{{  attach.client
          IF
            rlinks.ok[0]
              SEQ
                CLAIM rlinks[0]
                  rlinks[0][in] ! new.client; cname; cli
                in[out] ! ok
            TRUE
              in[out] ! cli.error; "no start"; cli
          --}}}
        CT.CLIENT! cli:
        MOBILE []BYTE cname:
        dead.client; cname; cli
          --{{{  dead client
          SEQ
            cli[s.in] ! resurrect
            IF
              rlinks.ok[8]
                SEQ
                  CLAIM rlinks[8]
                    rlinks[8][in] ! new.client; cname; cli
              rlinks.ok[0]
                SEQ
                  CLAIM rlinks[0]
                    rlinks[0][in] ! new.client; cname; cli
              TRUE
                SEQ
                  out.string ("dead.client has nowhere to go!*n", 0, err!)
                  STOP
          --}}}
        MOBILE []BYTE rname:
        read.room.desc; rname
          --{{{  read.room.desc
          [128]BYTE f.buf:
          INT f.len:
          BOOL ok:
          MOBILE []MOBILE []BYTE lines:
          SEQ
            f.len := 0
            buffer.set.string (f.buf, f.len, ##BASEPATH)
            buffer.set.string (f.buf, f.len, rname)
            buffer.set.string (f.buf, f.len, ".desc")

            file.readlines ([f.buf FOR f.len], ok, lines)
            IF
              NOT ok
                SKIP
              TRUE
                SEQ i = 0 FOR SIZE lines
                  in[out] ! string; lines[i]
            in[out] ! end.of.list
          --}}}
        MOBILE []BYTE msg:
        debug; msg
          --{{{  debugging message
          SEQ
            out.string ("DEBUG: ", 0, err!)
            out.string (msg, 0, err!)
            err ! '*n'
          --}}}
        MOBILE []BYTE name:
        register.name; name
          --{{{  attempt to register a name
          IF
            IF i = 0 FOR nnames
              (names.ok[i]) AND (names[i] = name)
                in[out] ! name.in.use
            IF i = 0 FOR nnames
              NOT names.ok[i]
                SEQ
                  names[i] := name
                  names.ok[i] := TRUE
                  in[out] ! ok
            TRUE
              in[out] ! name.in.use
          --}}}
        MOBILE []BYTE name:
        unregister.name; name
          --{{{  attempt to unregister a name (always succeeds..)
          IF
            IF i = 0 FOR nnames
              (names.ok[i]) AND (names[i] = name)
                SEQ
                  names.ok[i] := FALSE
                  in[out] ! ok
            TRUE
              in[out] ! ok
          --}}}
        get.n.rooms
          in[out] ! n.rooms; hiroom + 1
          
:
--}}}
--{{{  PROC process.script (VAL []BYTE fname, SHARED CT.GAMECTRL! game.cli, SHARED CT.LINCLINK! linc.cli, CHAN BYTE out!)
PROC process.script (VAL []BYTE fname, SHARED CT.GAMECTRL! game.cli, SHARED CT.LINCLINK! linc.cli, CHAN BYTE out!)
  CHAN MOBILE []BYTE script, icopy:
  CHAN P.CLIENT.OUT cmd.sink, cmd.dead:
  CHAN P.CLIENT.IN ret.dead, cmd.noret:
  CHAN S.CLIENT.IN msg:
  PAR
    --{{{  try and read a SCRIPT file
    INT fd, f.len:
    SEQ
      f.len := 0
      file.open (fname, O.RDONLY, fd)
      IF
        fd < 0
          SKIP
        TRUE
          [1024]BYTE f.buf:
          INITIAL BOOL done IS FALSE:
          INT offset:
          SEQ
            offset := 0
            WHILE NOT done
              SEQ
                IF
                  offset > 0
                    SEQ
                      -- copy end of the buffer back to the start
                      
                      SEQ i = 0 FOR (f.len - offset)
                        f.buf[i] := f.buf[i + offset]
                      offset := f.len - offset
                  TRUE
                    SKIP
                file.read (fd, [f.buf FROM offset], f.len)
                IF
                  f.len <= 0
                    done := TRUE
                  TRUE
                    INT start, stop:
                    SEQ
                      start := 0
                      -- add in the extra bit
                      f.len := f.len + offset

                      WHILE start < f.len
                        SEQ
                          stop := start
                          WHILE (stop < f.len) AND (f.buf[stop] <> #0A)
                            stop := stop + 1
                          IF
                            start = stop
                              SKIP
                            stop = f.len
                              offset := start       -- remember for next time
                            TRUE
                              SEQ
                                script ! [f.buf FROM start FOR (stop - start)]
                                offset := 0
                          start := stop + 1
            INT r:
            file.close (fd, r)
            script ! ##DIEMESSAGE
    --}}}
    --{{{  process script commands
    CHAN P.CLIENT.OUT scmds:
    PAR
      tty.if.decode (script?, icopy!, scmds!, TRUE)
      SEQ
        handle.cmd (scmds?, msg!, cmd.dead!, ret.dead?, cmd.sink!, cmd.noret?, game.cli, linc.cli, TRUE)
        cmd.sink ! do.logout
    --}}}
    --{{{  icopy sink
    INITIAL BOOL running IS TRUE:
    WHILE running
      MOBILE []BYTE str:
      SEQ
        icopy ? str
        IF
          str = ##DIEMESSAGE
            running := FALSE
          TRUE
            SKIP
    --}}}
    --{{{  cmd.sink
    INITIAL BOOL running IS TRUE:
    WHILE running
      cmd.sink ? CASE
        MOBILE []BYTE msg:
        say; msg
          SKIP
        do.logout
          running := FALSE
    --}}}
    --{{{  msg
    INITIAL BOOL running IS TRUE:
    WHILE running
      MOBILE []BYTE m:
      msg ? CASE
        genmsg; m
          SEQ
            out.string (m, 0, out!)
            out ! '*n'
        do.logout
          running := FALSE
    --}}}
:
--}}}

