C Subroutines for DUNGEON
C
C COPYRIGHT 1980, 1990, INFOCOM COMPUTERS AND COMMUNICATIONS, CAMBRIDGE MA.
C ALL RIGHTS RESERVED, COMMERCIAL USAGE STRICTLY PROHIBITED
C WRITTEN BY R. M. SUPNIK
C
C RSPEAK-- Output random message routine
C
C Called by--
C
C	CALL RSPEAK(MSGNUM)
C
	SUBROUTINE RSPEAK(N)
	IMPLICIT INTEGER(A-Z)
C
	CALL RSPSB2(N,0,0)
	RETURN
C
	END
C
C RSPSUB-- Output random message with substitutable argument
C
C Called by--
C
C	CALL RSPSUB(MSGNUM,SUBNUM)
C
	SUBROUTINE RSPSUB(N,S1)
	IMPLICIT INTEGER(A-Z)
C
	CALL RSPSB2(N,S1,0)
	RETURN
C
	END

C RSPSB2-- Output random message with substitutable arguments
C
C Called by--
C
C	CALL RSPSB2(MSGNUM,S1,S2)
C
	SUBROUTINE RSPSB2(A,B,C)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	CHARACTER*(TEXLNT) B1,B2
C
C Convert all arguments from dictionary numbers (if positive)
c to absolute record numbers.
C
	X=A					! set up work variables.
	Y=B
	Z=C
	IF(X.GT.0) X=RTEXT(X)			! if >0, look up in rtext.
	IF(Y.GT.0) Y=RTEXT(Y)
	IF(Z.GT.0) Z=RTEXT(Z)
	X=IABS(X)				! take abs value.
	Y=IABS(Y)
	Z=IABS(Z)
	IF(X.EQ.0) RETURN			! anything to do?
	TELFLG=.TRUE.				! said something.
C
	READ(DBCH,REC=X) OLDREC,B1		! read first line.
100	CALL TXCRYP(X,B1)			! decrypt line.
C
200	IF(Y.EQ.0) GO TO 400			! any substitutable?
	I=INDEX(B1,'#')				! look for #.
	IF(I.GT.0) GO TO 1000			! found?
C
400	WRITE(OUTCH,650) B1(1:MAX0(1,NBLEN(B1)))! output line.
650	FORMAT(1X,A)
	X=X+1					! on to next record.
	READ(DBCH,REC=X) NEWREC,B1		! read next record.
	IF(OLDREC.EQ.NEWREC) GO TO 100		! continuation?
	RETURN					! no, exit.

C RSPSB2, PAGE 2
C
C Substitution with substitutable available.
C I is index of # in B1.
C Y is number of record to substitute.
C
C Procedure:
C   1) Copy rest of B1 to B2
C   2) Read substitutable over B1
C   3) Restore tail of original B1
C
C The implicit assumption here is that the substitutable string
c is very short.
C
1000	B2(1:(TEXLNT-I))=B1(I+1:TEXLNT)		! copy rest of B1.
C
	READ(DBCH,REC=Y) J,B1(I:TEXLNT)		! read sub record.
	CALL TXCRYP(Y,B1(I:TEXLNT))		! decrypt sub record.
	J=NBLEN(B1)				! backscan for blanks.
	B1(J+1:TEXLNT)=B2(1:TEXLNT-J)
C
	Y=Z					! set up for next
	Z=0					! substitution and
	GO TO 200				! recheck line.
C
	END

C OBJACT-- Apply objects from parse vector
C
C Declarations
C
	LOGICAL FUNCTION OBJACT(X)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL OAPPLI
C
	OBJACT=.TRUE.				! assume wins.
	IF(PRSI.EQ.0) GO TO 100			! ind object?
	IF(OAPPLI(OACTIO(PRSI),0)) RETURN	! yes, let it handle.
C
100	IF(PRSO.EQ.0) GO TO 200			! dir object?
	IF(OAPPLI(OACTIO(PRSO),0)) RETURN	! yes, let it handle.
C
200	OBJACT=.FALSE.				! loses.
	RETURN
C
	END

C BUG-- Report fatal system error
C
C Declarations
C
	SUBROUTINE BUG(A,B)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	TYPE 100,A,B				! gonzo
	IF(DBGFLG.NE.0) RETURN
	SUBBUF='CRASH.DAT'			! set up crash save name.
	SUBLNT=NBLEN(SUBBUF)
	CALL SAVEGM				! do final save.
	TYPE 200
	CALL EXIT
C
100	FORMAT(' Program error ',I2,', parameter =',I6)
200	FORMAT(' Game state saved in "CRASH.DAT".')
C
	END

C NEWSTA-- Set new status for object
C
C Called by--
C
C	CALL NEWSTA(OBJECT,STRING,NEWROOM,NEWCON,NEWADV)
C
	SUBROUTINE NEWSTA(O,R,RM,CN,AD)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	CALL RSPEAK(R)
	OROOM(O)=RM
	OCAN(O)=CN
	OADV(O)=AD
	RETURN
C
	END

C QHERE-- Test for object in room
C
C Declarations
C
	LOGICAL FUNCTION QHERE(OBJ,RM)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	QHERE=.TRUE.
	IF(OROOM(OBJ).EQ.RM) RETURN		! in room?
	DO 100 I=1,R2LNT			! no, sch room2.
	  IF((O2(I).EQ.OBJ).AND.(R2(I).EQ.RM)) RETURN
100	CONTINUE
	QHERE=.FALSE.				! not present.
	RETURN
C
	END

C QEMPTY-- Test for object empty
C
C Declarations
C
	LOGICAL FUNCTION QEMPTY(OBJ)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	QEMPTY=.FALSE.				! assume lose.
	DO 100 I=1,OLNT
	  IF(OCAN(I).EQ.OBJ) RETURN		! inside target?
100	CONTINUE
	QEMPTY=.TRUE.
	RETURN
C
	END

C JIGSUP- You are dead
C
C Declarations
C
	SUBROUTINE JIGSUP(DESC)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL MOVETO,QHERE,F
	INTEGER RLIST(8)
C
C Functions and data
C
	DATA RLIST/6,36,35,34,4,34,6,5/
C
	CALL RSPEAK(DESC)			! describe sad state.
	PRSCON=0				! stop parser.
	IF(DBGFLG.NE.0) RETURN			! if dbg, exit.
	AVEHIC(WINNER)=0			! get rid of vehicle.
	IF(WINNER.EQ.PLAYER) GO TO 10		! himself?
	CALL RSPSUB(432,ODESC2(AOBJ(WINNER)))	! no, say who died.
	CALL NEWSTA(AOBJ(WINNER),0,0,0,0)	! send to hyper space.
	RETURN
C
10	CALL SCRUPD(-10)			! charge 10 points.
	IF(ENDGMF) GO TO 900			! no recovery in end game.
	IF(DEATHS.GE.2) GO TO 1000		! dead twice? kick him off.
	DEATHS=DEATHS+1				! record deaths.
	DEADF=.TRUE.				! flag dead player.
	CALL RSPEAK(8)				! tell him bad news
	AACTIO(PLAYER)=PLAYER			! turn on dead player func.
C
	DO 50 J=1,OLNT				! turn off fighting.
	  IF(QHERE(J,HERE)) OFLAG2(J)=OFLAG2(J).AND. .NOT.FITEBT
50	CONTINUE
C
	F=MOVETO(LLD1,WINNER)			! reposition him.
	EGYPTF=.TRUE.				! restore coffin.
	IF(OADV(COFFI).EQ.WINNER) CALL NEWSTA(COFFI,0,EGYPT,0,0)
	OFLAG2(DOOR)=OFLAG2(DOOR).AND. .NOT.TCHBT ! restore door.
	OFLAG1(ROBOT)=(OFLAG1(ROBOT).OR.VISIBT) .AND. .NOT.NDSCBT
	CALL NEWSTA(LAMP,0,LROOM,0,0)		! lamp to living room,
	OFLAG1(LAMP)=OFLAG1(LAMP).OR.VISIBT	! visible
	DO 100 I=1,CLNT				! disable cevnts if needed.
	  IF(CCNCEL(I)) CFLAG(I)=.FALSE.
100	CONTINUE

C JIGSUP, PAGE 2
C
C Now redistribute his valuables and other belongings.
C
C The lamp has been placed in the living room.
C The first 8 non-valuables are placed in locations around the house.
C His valuables are placed start at maze.
C Remaining non-valuables are after that.
C
	I=0
	DO 200 J=1,OLNT				! loop thru objects.
	  IF((OADV(J).NE.WINNER).OR.(OTVAL(J).NE.0))
	1	GO TO 200			! get his non-val objs.
	  I=I+1
	  IF(I.GT.8) GO TO 400			! move to random locations.
	  CALL NEWSTA(J,0,RLIST(I),0,0)
200	CONTINUE
C
400	I=MTROL					! now move valuables.
	NONOFL=RAIR+RWATER+REND			! dont move here.
	DO 300 J=1,OLNT
	  IF((OADV(J).NE.WINNER).OR.(OTVAL(J).EQ.0))
	1	GO TO 300			! on adv and valuable?
250	  I=I+1					! find next room.
	  IF((RFLAG(I).AND.NONOFL).NE.0) GO TO 250	! skip if nono.
	  CALL NEWSTA(J,0,I,0,0)		! yes, move.
300	CONTINUE
C
	DO 500 J=1,OLNT				! now get rid of remainder.
	  IF(OADV(J).NE.WINNER) GO TO 500
450	  I=I+1					! find next room.
	  IF((RFLAG(I).AND.NONOFL).NE.0) GO TO 450	! skip if nono.
	  CALL NEWSTA(J,0,I,0,0)
500	CONTINUE
	RETURN
C
C Cant or wont continue, clean up and exit.
C
900	CALL RSPEAK(625)			! in endgame, lose.
	GO TO 1100
C
1000	CALL RSPEAK(7)				! involuntary exit.
1100	CALL SCORE(.FALSE.)			! tell score.
	CLOSE (UNIT=DBCH)
	CALL EXIT
C
	END

C OACTOR-	Get actor associated with object
C
C Declarations
C
	INTEGER FUNCTION OACTOR(OBJ)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	DO 100 OACTOR=1,ALNT			! loop thru actors.
	  IF(AOBJ(OACTOR).EQ.OBJ) RETURN	! found it?
100	CONTINUE
	CALL BUG(40,OBJ)			! no, die.
	RETURN
C
	END

C PROB-		Compute probability
C
C Declarations
C
	LOGICAL FUNCTION PROB(G,B)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	I=G					! assume good luck.
	IF(BADLKF) I=B				! if bad, too bad.
	PROB=RND(100).LT.I			! compute.
	RETURN
C
	END

C RMDESC-- Print room description
C
C RMDESC prints a description of the current room.
C It is also the processor for verbs 'LOOK' and 'EXAMINE'
C when there is no direct object.
C
	LOGICAL FUNCTION RMDESC(FULL)
C
C FULL=	0/1/2/3=	full/obj/room/full but no applicable
C
C Declarations
C
	IMPLICIT INTEGER (A-Z)
	LOGICAL PROB,LIT,OBJACT
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	RMDESC=.TRUE.				! assume wins.
	RA=RACTIO(HERE)				! get room action.
	IF(PRSO.LT.XMIN) GO TO 50		! if direction,
	FROMDR=PRSO				! save and
	PRSO=0					! clear.
50	IF(FULL.EQ.1) GO TO 600			! objects only?
	IF(HERE.EQ.AROOM(PLAYER)) GO TO 100	! player just move?
	CALL RSPEAK(2)				! no, just say done.
	PRSA=WALKIW				! set up walk in action.
	RETURN
C
100	IF(LIT(HERE)) GO TO 300			! lit?
	CALL RSPEAK(430)			! warn of grue.
	RMDESC=.FALSE.
	RETURN
C
300	I=RDESC2-HERE				! assume short desc.
	IF((FULL.EQ.0)
	1	.AND. (SUPERF.OR.(((RFLAG(HERE).AND.RSEEN).NE.0)
	1	.AND. (BRIEFF.OR.PROB(80,80))))) GO TO 400
	I=RDESC1(HERE)				! use long.
	IF((I.NE.0).OR.(RA.EQ.0)) GO TO 400	! if got desc, skip.
	PRSA=LOOKW				! pretend look around.
	PRSO=0					! no object referenced.
	CALL RAPPLI(RA)				! let room handle.
	PRSA=FOOW				! nop parser.
	GO TO 500
C
400	CALL RSPEAK(I)				! output description.
500	IF(AVEHIC(WINNER).NE.0) CALL RSPSUB(431,ODESC2(AVEHIC(WINNER)))
	RFLAG(HERE)=RFLAG(HERE).OR.RSEEN	! indicate room seen.
C
600	IF(LIT(HERE)) GO TO 700			! if lit, do objects
	CALL RSPEAK(1036)			! can't see anything
	RETURN
C
700	IF(FULL.NE.2) CALL PRINCR(FULL,HERE)	! print room contents
	IF((FULL.NE.0).OR.(RA.EQ.0)) RETURN	! anything more?
	PRSA=WALKIW				! give him a surpise.
	CALL RAPPLI(RA)				! let room handle
	PRSA=FOOW
	RETURN
C
	END

C PRINCR- Print contents of room
C
C Declarations
C
	SUBROUTINE PRINCR(FULL,RM)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QEMPTY,QHERE
C
	J=329					! assume superbrief format.
	DO 500 I=1,OLNT				! loop on objects
	  IF(.NOT.QHERE(I,RM).OR.((OFLAG1(I).AND.VISIBT).EQ.0).OR.
	1	(((OFLAG1(I).AND.NDSCBT).NE.0).AND.(FULL.NE.1)).OR.
	2	(I.EQ.AVEHIC(WINNER))) GO TO 500
	  IF((FULL.EQ.0).AND.(SUPERF.OR.(BRIEFF.AND.
	1	((RFLAG(HERE).AND.RSEEN).NE.0)))) GO TO 200
C
C Do long description of object.
C
	  K=ODESCO(I)				! get untouched.
	  IF((K.EQ.0).OR.((OFLAG2(I).AND.TCHBT).NE.0)) K=ODESC1(I)
	  IF((K.EQ.0).AND.(FULL.EQ.1)) CALL RSPSUB(936,ODESC2(I))
	  CALL RSPEAK(K)			! describe.
	  GO TO 500
C
C Do short description of object.
C
200	  CALL RSPSUB(J,ODESC2(I))		! you can see it.
	  J=502
C
500	CONTINUE
C
C Now loop to print contents of objects in room.
C
	DO 1000 I=1,OLNT			! loop on objects.
	  IF(.NOT.QHERE(I,RM).OR.((OFLAG1(I).AND.VISIBT).EQ.0).OR.
	1	(((OFLAG1(I).AND.NDSCBT).NE.0).AND.(FULL.NE.1)))
	2	GO TO 1000
	  IF((OFLAG2(I).AND.ACTRBT).NE.0) CALL INVENT(OACTOR(I))
	  IF((((OFLAG1(I).AND.TRANBT).EQ.0).AND.((OFLAG2(I).AND.OPENBT)
	1	.EQ.0)).OR.QEMPTY(I)) GO TO 1000
C
C Object is not empty and is open or transparent.
C
	  IF(I.NE.TCASE) GO TO 600		! trophy case?
	  IF((.NOT.(BRIEFF.OR.SUPERF)).OR.(FULL.EQ.1))
	1	CALL PRINCO(I,1053,.FALSE.)	! print contents.
	  GO TO 1000
600	  CALL PRINCO(I,573,.TRUE.)		! print contents
1000	CONTINUE
	RETURN
C
	END

C INVENT- Print contents of adventurer
C
C Declarations
C
	SUBROUTINE INVENT(ADV)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QEMPTY
C
	I=575					! first line.
	IF(ADV.NE.PLAYER) I=576			! if not me.
	DO 10 J=1,OLNT				! loop
	  IF((OADV(J).NE.ADV).OR.((OFLAG1(J).AND.VISIBT).EQ.0))
	1	GO TO 10
	  CALL RSPSUB(I,ODESC2(AOBJ(ADV)))
	  I=0
	  CALL RSPSUB(502,ODESC2(J))
10	CONTINUE
C
	IF(I.EQ.0) GO TO 25			! any objects?
	IF(ADV.EQ.PLAYER) CALL RSPEAK(578)	! no, tell him.
	RETURN
C
25	DO 100 J=1,OLNT				! loop.
	  IF((OADV(J).NE.ADV).OR.((OFLAG1(J).AND.VISIBT).EQ.0).OR.
	1	(((OFLAG1(J).AND.TRANBT).EQ.0).AND.
	2	((OFLAG2(J).AND.OPENBT).EQ.0))) GO TO 100
	  IF(.NOT.QEMPTY(J)) CALL PRINCO(J,573,.TRUE.) ! if not empty, list.
100	CONTINUE
	RETURN
C
	END

C PRINCO-	Print contents of object
C
C Declarations
C
	SUBROUTINE PRINCO(OBJ,DESC,LDESCF)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QEMPTY,LDESCF,MOREF,QSEEIN,QUAL
C
C Functions and data
C
	QSEEIN(X)=((OFLAG1(X).AND.TRANBT).NE.0).OR.
	1	  ((OFLAG2(X).AND.OPENBT).NE.0)
	QUAL(X,Y)=((OFLAG1(X).AND.VISIBT).NE.0).AND.
	1	   (OCAN(X).EQ.Y).AND.(X.NE.AOBJ(WINNER))
C
	MOREF=.FALSE.				! no additional printouts.
	ALSO=0					! no untouched descriptions.
	IF(SUPERF.OR..NOT.LDESCF) GO TO 2000	! skip long descriptions?
	DO 1000 I=1,OLNT			! loop thru objects.
	  IF(.NOT.QUAL(I,OBJ)) GO TO 1000	! inside target?
	  IF((ODESCO(I).EQ.0).OR.
	1   ((OFLAG2(I).AND.TCHBT).NE.0)) GO TO 700
	  CALL RSPEAK(ODESCO(I))		! print untouched descr.
	  ALSO=1				! flag.
	  IF(.NOT.QSEEIN(I).OR.QEMPTY(I)) GO TO 1000
	  CALL RSPSUB(573,ODESC2(I))		! object, which contains:
	  DO 500 J=1,OLNT			! loop thru objects.
	    IF(QUAL(J,I)) CALL RSPSUB(502,ODESC2(J))
500	  CONTINUE
	  GO TO 1000
700	  MOREF=.TRUE.
1000	CONTINUE
	IF(.NOT.MOREF) RETURN			! more to do?
C
2000	CALL RSPSUB(DESC+ALSO,ODESC2(OBJ))	! print header.
	DO 3000 I=1,OLNT			! loop thru objects.
	  IF(.NOT.QUAL(I,OBJ)) GO TO 3000	! inside target?
	  IF((ALSO.NE.0).AND.(ODESCO(I).NE.0).AND.
	1    ((OFLAG2(I).AND.TCHBT).EQ.0)) GO TO 3000
	  IF(.NOT.QSEEIN(I).OR.QEMPTY(I)) GO TO 2700
	  CALL RSPSUB(1050,ODESC2(I))		! object, which contains:
	  DO 2500 J=1,OLNT			! loop thru objects.
	    IF(QUAL(J,I)) CALL RSPSUB(1051,ODESC2(J))
2500	  CONTINUE
	  GO TO 3000
2700	  CALL RSPSUB(502,ODESC2(I))		! object, nothing inside.
3000	CONTINUE
	RETURN
C
	END

C MOVETO- Move player to new room
C
C Declarations
C
	LOGICAL FUNCTION MOVETO(NR,WHO)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL NLV,LHR,LNR
C
	MOVETO=.FALSE.				! assume fails.
	LHR=(RFLAG(HERE).AND.RLAND).NE.0	! land  here flag.
	LNR=(RFLAG(NR).AND.RLAND).NE.0		! land there flag.
	J=AVEHIC(WHO)				! his vehicle
C
	IF(J.NE.0) GO TO 100			! in vehicle?
	IF(LNR) GO TO 500			! no, going to land?
	CALL RSPEAK(427)			! can't go without vehicle.
	RETURN
C
100	BITS=0					! assume nowhere.
	IF(J.EQ.RBOAT) BITS=RWATER		! in boat?
	IF(J.EQ.BALLO) BITS=RAIR		! in balloon?
	IF(J.EQ.BUCKE) BITS=RBUCK		! in bucket?
	NLV=(RFLAG(NR).AND.BITS).EQ.0		! got wrong vehicle flag.
	IF((.NOT.LNR .AND.NLV) .OR.
	1	(LNR.AND.LHR.AND.NLV.AND.(BITS.NE.RLAND)))
	2	GO TO 800			! got wrong vehicle?
C
500	MOVETO=.TRUE.				! move should succeed.
	IF((RFLAG(NR).AND.RMUNG).EQ.0) GO TO 600 ! room munged?
	CALL RSPEAK(RDESC1(NR))			! yes, tell how.
	RETURN
C
600	IF(WHO.NE.PLAYER) CALL NEWSTA(AOBJ(WHO),0,NR,0,0)
	IF(J.NE.0) CALL NEWSTA(J,0,NR,0,0)
	HERE=NR
	AROOM(WHO)=HERE
	CALL SCRUPD(RVAL(NR))			! score room
	RVAL(NR)=0
	RETURN
C
800	CALL RSPSUB(428,ODESC2(J))		! wrong vehicle.
	RETURN
C
	END

C SCORE-- Print out current score
C
C Declarations
C
	SUBROUTINE SCORE(FLG)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL FLG
	INTEGER RANK(10),ERANK(5)
C
C Functions and data
C
	DATA RANK/20,19,18,16,12,8,4,2,1,0/
	DATA ERANK/20,15,10,5,0/
C
	AS=ASCORE(WINNER)
	IF(ENDGMF) GO TO 60			! endgame?
	IF(FLG) WRITE(OUTCH,100)
	IF(.NOT.FLG) WRITE(OUTCH,110)
	IF(MOVES.NE.1) WRITE(OUTCH,120) AS,MXSCOR,MOVES
	IF(MOVES.EQ.1) WRITE(OUTCH,130) AS,MXSCOR,MOVES
	DO 10 I=1,10
	  IF((AS*20/MXSCOR).GE.RANK(I)) GO TO 50
10	CONTINUE
	CALL RSPEAK(886)			! negative score.
	RETURN
C
50	CALL RSPEAK(484+I)
	RETURN
C
60	IF(FLG) WRITE(OUTCH,140)
	IF(.NOT.FLG) WRITE(OUTCH,150)
	WRITE(OUTCH,120) EGSCOR,EGMXSC,MOVES
	DO 70 I=1,5
	  IF((EGSCOR*20/EGMXSC).GE.ERANK(I)) GO TO 80
70	CONTINUE
	CALL RSPEAK(886)			! negative score.
	RETURN
C
80	CALL RSPEAK(786+I)
	RETURN
C
100	FORMAT(' Your score would be',$)
110	FORMAT(' Your score is',$)
120	FORMAT('+',I4,' [total of',I4,' points], in',I5,' moves.')
130	FORMAT('+',I4,' [total of',I4,' points], in',I5,' move.')
140	FORMAT(' Your score in the endgame would be',$)
150	FORMAT(' Your score in the endgame is',$)
C
	END

C SCRUPD- Update winner's score
C
C Declarations
C
	SUBROUTINE SCRUPD(N)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	IF(ENDGMF) GO TO 100			! endgame?
	ASCORE(WINNER)=ASCORE(WINNER)+N		! update score
	RWSCOR=RWSCOR+N				! update raw score
	IF(ASCORE(WINNER).LT.(MXSCOR-(10*MIN0(1,DEATHS)))) RETURN
	CFLAG(CEVEGH)=.TRUE.			! turn on end game
	CTICK(CEVEGH)=15
	RETURN
C
100	EGSCOR=EGSCOR+N				! update eg score.
	RETURN
C
	END

C FINDXT- Find exit from room
C
C Declarations
C
	LOGICAL FUNCTION FINDXT(DIR,RM)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	FINDXT=.TRUE.				! assume wins.
	XI=REXIT(RM)				! find first entry.
	IF(XI.EQ.0) GO TO 1000			! no exits?
C
100	I=TRAVEL(XI)				! get entry.
	XROOM1=I.AND.XRMASK			! isolate room.
	XTYPE=(((I.AND..NOT.XLFLAG)/XFSHFT).AND.XFMASK)+1
	GO TO (110,120,130,130),XTYPE		! branch on entry.
	CALL BUG(10,XTYPE)
C
130	XOBJ=TRAVEL(XI+2).AND.XRMASK		! door/cexit- get obj/flag.
	XACTIO=TRAVEL(XI+2)/XASHFT
120	XSTRNG=TRAVEL(XI+1)			! door/cexit/nexit - string.
110	XI=XI+XELNT(XTYPE)			! advance to next entry.
	IF((I.AND.XDMASK).EQ.DIR) RETURN	! match?
	IF((I.AND.XLFLAG).EQ.0) GO TO 100	! last entry?
1000	FINDXT=.FALSE.				! yes, lose.
	RETURN
C
	END

C FWIM- Find what I mean
C
C Declarations
C
	INTEGER FUNCTION FWIM(F1,F2,RM,CON,ADV,NOCARE)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL NOCARE
C
	FWIM=0					! assume nothing.
	DO 1000 I=1,OLNT			! loop
	  IF(((RM.EQ.0).OR.(OROOM(I).NE.RM)) .AND.
	1	((ADV.EQ.0).OR.(OADV(I).NE.ADV)) .AND.
	2	((CON.EQ.0).OR.(OCAN(I).NE.CON)))
	3	GO TO 1000
C
C Object is on list... is it a match?
C
	  IF((OFLAG1(I).AND.VISIBT).EQ.0) GO TO 1000
	  IF((.NOT.NOCARE .AND.((OFLAG1(I).AND.TAKEBT).EQ.0)) .OR.
	1	(((OFLAG1(I).AND.F1).EQ.0).AND.
	2	 ((OFLAG2(I).AND.F2).EQ.0))) GO TO 500
	  IF(FWIM.EQ.0) GO TO 400		! already got something?
	  FWIM=-FWIM				! yes, ambiguous.
	  RETURN
C
400	  FWIM=I				! note match.
C
C Does object contain a match?
C
500	  IF((OFLAG2(I).AND.OPENBT).EQ.0) GO TO 1000 ! closed?
	  DO 700 J=1,OLNT			! no, search contents.
	    IF((OCAN(J).NE.I).OR.((OFLAG1(J).AND.VISIBT).EQ.0) .OR.
	1	(((OFLAG1(J).AND.F1).EQ.0).AND.
	2	 ((OFLAG2(J).AND.F2).EQ.0))) GO TO 700
	    IF(FWIM.EQ.0) GO TO 600
	    FWIM=-FWIM
	    RETURN
C
600	    FWIM=J
700	  CONTINUE
1000	CONTINUE
	RETURN
C
	END

C ORPHAN- Set up orphans for parser
C
C Declarations
C
	SUBROUTINE ORPHAN(OR1,OR2,OR3,OR4,OR5,OR6,OR7,OR8)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	CHARACTER*(*) OR6
C
	OFLAG=OR1
	OACT=OR2
	OPREP1=OR3
	OOBJ1=OR4
	OPREP=OR5
	ONAME=OR6
	OPREP2=OR7
	OOBJ2=OR8
	RETURN
C
	END

C YESNO- Obtain yes/no answer
C
C Called by-
C
C	YES-IS-TRUE=YESNO(QUESTION,YES-STRING,NO-STRING)
C
	LOGICAL FUNCTION YESNO(Q,Y,N)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	CHARACTER*1 ANS
C
100	CALL RSPEAK(Q)				! ask
	READ(INPCH,110,END=120) ANS		! get answer
110	FORMAT(A)
	IF((ANS.EQ.'Y').OR.(ANS.EQ.'y')) GO TO 200
	IF((ANS.EQ.'N').OR.(ANS.EQ.'n')) GO TO 300
120	CALL RSPEAK(6)				! scold.
	GO TO 100
C
200	YESNO=.TRUE.				! yes,
	CALL RSPEAK(Y)				! out with it.
	RETURN
C
300	YESNO=.FALSE.				! no,
	CALL RSPEAK(N)				! likewise.
	RETURN
C
	END

C ROBADV-- Steal winner's valuables
C
C Declarations
C
	INTEGER FUNCTION ROBADV(ADV,NR,NC,NA)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	ROBADV=0				! count objects
	DO 100 I=1,OLNT
	  IF((OADV(I).NE.ADV).OR.(OTVAL(I).LE.0).OR.
	1	((OFLAG2(I).AND.SCRDBT).NE.0)) GO TO 100
	  CALL NEWSTA(I,0,NR,NC,NA)		! steal object
	  ROBADV=ROBADV+1
100	CONTINUE
	RETURN
C
	END

C ROBRM-- Steal room valuables
C
C Declarations
C
	INTEGER FUNCTION ROBRM(RM,PR,NR,NC,NA)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL PROB,QHERE
C
	ROBRM=0					! count objects
	DO 100 I=1,OLNT				! loop on objects.
	  IF(.NOT. QHERE(I,RM)) GO TO 100
	  IF((OTVAL(I).LE.0).OR.((OFLAG2(I).AND.SCRDBT).NE.0).OR.
	1	((OFLAG1(I).AND.VISIBT).EQ.0).OR.(.NOT.PROB(PR,PR)))
	2	GO TO 50
	  CALL NEWSTA(I,0,NR,NC,NA)
	  ROBRM=ROBRM+1
	  OFLAG2(I)=OFLAG2(I).OR.TCHBT
	  GO TO 100
50	  IF((OFLAG2(I).AND.ACTRBT).NE.0)
	1	ROBRM=ROBRM+ROBADV(OACTOR(I),NR,NC,NA)
100	CONTINUE
	RETURN
C
	END

C WINNIN-- See if villain is winning
C
C Declarations
C
	LOGICAL FUNCTION WINNIN(VL,HR)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL PROB
C
	VS=OCAPAC(VL)				! villain strength
	PS=VS-FIGHTS(HR,.TRUE.)			! his margin over hero
	WINNIN=PROB(90,100)
	IF(PS.GT.3) RETURN			! +3... 90% winning
	WINNIN=PROB(75,85)
	IF(PS.GT.0) RETURN			! >0... 75% winning
	WINNIN=PROB(50,30)
	IF(PS.EQ.0) RETURN			! =0... 50% winning
	WINNIN=PROB(25,25)
	IF(VS.GT.1) RETURN			! any villain strength.
	WINNIN=PROB(10,0)
	RETURN
C
	END

C FIGHTS-- Compute fight strength
C
C Declarations
C
	INTEGER FUNCTION FIGHTS(H,FLG)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	PARAMETER (STRMAX=7)
	PARAMETER (STRMIN=2)
	LOGICAL FLG
C
	FIGHTS=STRMIN+((((STRMAX-STRMIN)*ASCORE(H))+(MXSCOR/2))/MXSCOR)
	IF(FLG) FIGHTS=FIGHTS+ASTREN(H)
	RETURN
C
	END

C VILSTR-	Compute villain strength
C
C Declarations
C
	INTEGER FUNCTION VILSTR(V)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	VILSTR=OCAPAC(V)
	IF(VILSTR.LE.0) RETURN
	IF((V.NE.THIEF).OR..NOT.THFENF) GO TO 100
	THFENF=.FALSE.				! thief unengrossed.
	VILSTR=MIN0(VILSTR,2)			! no better than 2.
C
100	DO 200 I=1,VLNT				! see if best weapon.
	  IF((VILLNS(I).EQ.V).AND.(PRSI.EQ.VBEST(I)))
	1	VILSTR=MAX0(1,VILSTR-1)
200	CONTINUE
	RETURN
C
	END

C GTTIME-- Get total time played
C
C Declarations
C
	SUBROUTINE GTTIME(T)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	CALL ITIME(H,M,S)
	T=((H*60)+M)-((SHOUR*60)+SMIN)
	IF(T.LT.0) T=T+1440
	T=T+PLTIME
	RETURN
C
	END

C OPNCLS-- Process open/close for doors
C
C Declarations
C
	LOGICAL FUNCTION OPNCLS(OBJ,SO,SC)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QOPEN
C
C Functions and data
C
	QOPEN(O)=(OFLAG2(O).AND.OPENBT).NE.0
C
	OPNCLS=.TRUE.				! assume wins.
	IF(PRSA.EQ.CLOSEW) GO TO 100		! close?
	IF(PRSA.EQ.OPENW) GO TO 50		! open?
	OPNCLS=.FALSE.				! lose
	RETURN
C
50	IF(QOPEN(OBJ)) GO TO 200		! open... is it?
	CALL RSPEAK(SO)
	OFLAG2(OBJ)=OFLAG2(OBJ).OR.OPENBT
	RETURN
C
100	IF(.NOT.QOPEN(OBJ)) GO TO 200		! close... is it?
	CALL RSPEAK(SC)
	OFLAG2(OBJ)=OFLAG2(OBJ).AND..NOT.OPENBT
	RETURN
C
200	CALL RSPEAK(125+RND(3))			! dummy.
	RETURN
C
	END

C LIT-- Is room lit?
C
C Declarations
C
	LOGICAL FUNCTION LIT(RM)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QHERE
C
	LIT=.TRUE.				! assume wins
	IF(DEADF.OR.((RFLAG(RM).AND.RLIGHT).NE.0)) RETURN	! room lit?
C
	DO 1000 I=1,OLNT			! look for lit obj
	  IF(QHERE(I,RM)) GO TO 100		! in room?
	  OA=OADV(I)				! no
	  IF(OA.LE.0) GO TO 1000		! on adv?
	  IF(AROOM(OA).NE.RM) GO TO 1000	! adv in room?
C
C Obj in room or on adv in room
C
100	  IF((OFLAG1(I).AND.ONBT).NE.0) RETURN	! lit?
	  IF(((OFLAG1(I).AND.VISIBT).EQ.0).OR.
	1	(((OFLAG1(I).AND.TRANBT).EQ.0).AND.
	2	((OFLAG2(I).AND.OPENBT).EQ.0))) GO TO 1000
C
C Obj is visible and open or transparent
C
	  DO 500 J=1,OLNT
	    IF((OCAN(J).EQ.I).AND.((OFLAG1(J).AND.ONBT).NE.0))
	1	RETURN
500	  CONTINUE
1000	CONTINUE
	LIT=.FALSE.
	RETURN
C
	END

C WEIGHT- Returns sum of weight of qualifying objects
C
C Declarations
C
	INTEGER FUNCTION WEIGHT(RM,CN,AD)
	IMPLICIT INTEGER (A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	LOGICAL QHERE
C
	WEIGHT=0
	DO 100 I=1,OLNT				! omit big fixed items.
	  IF(OSIZE(I).GE.10000) GO TO 100	! if fixed, forget it.
	  IF((QHERE(I,RM).AND.(RM.NE.0)).OR.
	1	((OADV(I).EQ.AD).AND.(AD.NE.0))) GO TO 50
	  J=I					! see if contained.
25	  J=OCAN(J)				! get next level up.
	  IF(J.EQ.0) GO TO 100			! end of list?
	  IF(J.NE.CN) GO TO 25
50	  WEIGHT=WEIGHT+OSIZE(I)
100	CONTINUE
	RETURN
C
	END

C GHERE--	Is global actually in this room?
C
C Declarations
C
	LOGICAL FUNCTION GHERE(OBJ,RM)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	GHERE=.TRUE.				! assume wins.
	IF(OBJ.LE.GLOBAL) RETURN		! if untested, return.
	GO TO ( 1000,2000,3000,4000,5000,5000,5000,6000,
	3	7000,8000,9000,9100,8000,10000,11000,
	4	12000,13000,14000,15000),OBJ-GLOBAL
	CALL BUG(60,OBJ)
C
C 1000--	House
C
1000	GHERE=((RM.GE.WHOUS).AND.(RM.LE.EHOUS)).OR.
	1	((RM.GE.FORE1).AND.(RM.LE.CLEAR)).OR.(RM.EQ.MTREE)
	RETURN
C
C 2000--	Bird
C
2000	GHERE=((RM.GE.FORE1).AND.(RM.LT.CLEAR)).OR.(RM.EQ.MTREE)
	RETURN
C
C 3000--	Tree
C
3000	GHERE=((RM.GE.FORE1).AND.(RM.LT.CLEAR)).AND.(RM.NE.FORE3)
	RETURN
C
C 4000--	North wall
C
4000	GHERE=((RM.GE.BKVW).AND.(RM.LE.BKBOX)).OR.(RM.EQ.CPUZZ)
	RETURN
C
C 5000--	East, south, west walls
C
5000	GHERE=((RM.GE.BKVW).AND.(RM.LT.BKBOX)).OR.(RM.EQ.CPUZZ)
	RETURN
C
C 6000--	Global water
C
6000	GHERE=(RFLAG(RM).AND.(RWATER+RFILL)).NE.0
	RETURN
C
C 7000--	Global guardians
C
7000	GHERE=((RM.GE.MRC).AND.(RM.LE.MRD)).OR.
	1	((RM.GE.MRCE).AND.(RM.LE.MRDW)).OR.(RM.EQ.INMIR)
	RETURN
C
C 8000--	Rose/channel
C
8000	GHERE=((RM.GE.MRA).AND.(RM.LE.MRD)).OR.(RM.EQ.INMIR)
	RETURN
C
C 9000--	Mirror
C 9100		Panel
C
9100	IF(RM.EQ.FDOOR) RETURN			! panel at fdoor.
9000	GHERE=((RM.GE.MRA).AND.(RM.LE.MRC)).OR.
	1	((RM.GE.MRAE).AND.(RM.LE.MRCW))
	RETURN
C
C 10000--	Master
C
10000	GHERE=(RM.EQ.FDOOR).OR.(RM.EQ.NCORR).OR.(RM.EQ.PARAP).OR.
	1	(RM.EQ.CELL).OR.(RM.EQ.PCELL).OR.(RM.EQ.NCELL)
	RETURN
C
C 11000--	Ladder
C
11000	GHERE=(RM.EQ.CPUZZ)
	RETURN
C
C 12000--	Well
C
12000	GHERE=(RM.EQ.TWELL).OR.(RM.EQ.BWELL)
	RETURN
C
C 13000--	Rope in slide
C
13000	GHERE=(RM.GE.SLID1).AND.(RM.LE.SLEDG)
	RETURN
C
C 14000--	Slide
C
14000	GHERE=(RM.GE.SLIDE).OR.((RM.GE.SLID1).AND.(RM.LE.SLEDG))
	RETURN
C
C 15000--	Bunch pseudo object
C
15000	GHERE=.FALSE.			! never present
	RETURN
C
	END

C MRHERE--	Is mirror here?
C
C Declarations
C
	INTEGER FUNCTION MRHERE(RM)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	IF((RM.LT.MRAE).OR.(RM.GT.MRDW)) GO TO 100
C
C Room is an e-w room, mirror must be n-s (mdir= 0 or 180)
C
	MRHERE=1				! assume mirror 1 here.
	IF(MOD(RM-MRAE,2).EQ.(MDIR/180)) MRHERE=2
	RETURN
C
C Room is north or south of mirror.  If mirror is n-s or not
c within one room of room, lose.
C
100	MRHERE=0
	IF((IABS(MLOC-RM).NE.1).OR.(MOD(MDIR,180).EQ.0)) RETURN
C
C Room is within one of mloc, and mdir is e-w
C
	MRHERE=1
	IF(((RM.LT.MLOC).AND.(MDIR.LT.180)).OR.
	1  ((RM.GT.MLOC).AND.(MDIR.GT.180))) MRHERE=2
	RETURN
C
	END

C ENCRYP--	Encrypt password
C
C Declarations
C
	SUBROUTINE ENCRYP(INW,OUTW)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	CHARACTER*(WRDLNT) INW,OUTW,KEYW
	INTEGER UINW(8),UKEYW(8)
	DATA KEYW/'ECOVXRMS'/
C
	ICHARA=ICHAR('A')-1			! character base.
	UINWS=0					! unbiased inw sum.
	UKEYWS=0				! unbiased keyw sum.
	J=1					! pointer in keyword.
	DO 100 I=1,WRDLNT			! unbias, compute sums.
	  UKEYW(I)=ICHAR(KEYW(I:I))-ICHARA	! strip ascii.
	  IF(ICHAR(INW(J:J)).LE.ICHARA) J=1	! recycle on bad.
	  UINW(I)=ICHAR(INW(J:J))-ICHARA
	  UKEYWS=UKEYWS+UKEYW(I)
	  UINWS=UINWS+UINW(I)
	  J=J+1
100	CONTINUE
C
	USUM=MOD(UINWS,8)+(8*MOD(UKEYWS,8))	! compute mask.
	DO 200 I=1,8
	  J=(UINW(I).XOR.UKEYW(I).XOR.USUM).AND.31
	  USUM=MOD(USUM+1,32)
	  IF(J.GT.26) J=MOD(J,26)
	  OUTW(I:I)=CHAR(MAX0(1,J)+ICHARA)
200	CONTINUE
	RETURN
C
	END

C CPGOTO--	Move to next state in puzzle room
C
C Declarations
C
	SUBROUTINE CPGOTO(ST)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
C
	RFLAG(CPUZZ)=RFLAG(CPUZZ).AND..NOT.RSEEN
	DO 100 I=1,OLNT				! relocate objects.
	  IF((OROOM(I).EQ.CPUZZ).AND.
	1	((OFLAG2(I).AND.(ACTRBT+VILLBT)).EQ.0))
	2	CALL NEWSTA(I,0,CPHERE*HFACTR,0,0)
	  IF(OROOM(I).EQ.(ST*HFACTR))
	1	CALL NEWSTA(I,0,CPUZZ,0,0)
100	CONTINUE
	CPHERE=ST
	RETURN
C
	END

C CPINFO--	Describe puzzle room
C
C Declarations
C
	SUBROUTINE CPINFO(RMK,ST)
	IMPLICIT INTEGER(A-Z)
	INCLUDE 'DPARAM.FOR/NOLIST'
	INTEGER DGMOFT(8)
	CHARACTER*2 DGM(8),PICT(5),QMK
C
C Functions and local data
C
	DATA DGMOFT/-9,-8,-7,-1,1,7,8,9/
	DATA PICT/'SS','SS','SS','  ','MM'/
	DATA QMK/'??'/
C
	CALL RSPEAK(RMK)
	DO 100 I=1,8
	  J=DGMOFT(I)
	  DGM(I)=PICT(CPVEC(ST+J)+4)		! get picture element.
	  IF((IABS(J).EQ.1).OR.(IABS(J).EQ.8)) GO TO 100
	  K=8
	  IF(J.LT.0) K=-8			! get ortho dir.
	  L=J-K
	  IF((CPVEC(ST+K).NE.0).AND.(CPVEC(ST+L).NE.0))
	1	DGM(I)=QMK
100	CONTINUE
	WRITE(OUTCH,10) DGM
C
	IF(ST.EQ.10) CALL RSPEAK(870)		! at hole?
	IF(ST.EQ.37) CALL RSPEAK(871)		! at niche?
	I=872					! door open?
	IF(CPOUTF) I=873
	IF(ST.EQ.52) CALL RSPEAK(I)		! at door?
	IF(CPVEC(ST+1).EQ.-2) CALL RSPEAK(874)	! east ladder?
	IF(CPVEC(ST-1).EQ.-3) CALL RSPEAK(875)	! west ladder?
	RETURN
C
10	FORMAT('       |',A,1X,A,1X,A,'|'/,
	1' West  |',A,' .. ',A,'|  East',/
	2'       |',A,1X,A,1X,A,'|')
C
	END

C NBLEN-	Compute string length without trailing blanks
C
C Declarations
C
	INTEGER FUNCTION NBLEN(STRING)
	IMPLICIT INTEGER(A-Z)
	CHARACTER*(*) STRING
C
	NBLEN=LEN(STRING)			! get nominal length
100	IF(NBLEN.LE.0) RETURN			! any string left?
	IF(STRING(NBLEN:NBLEN).NE.' ') RETURN	! found a non-blank?
	NBLEN=NBLEN-1				! no, trim len by 1
	GO TO 100				! and continue.
C
	END
