Syntax10.Scn.Fnt Syntax10b.Scn.Fnt Syntax14b.Scn.Fnt FoldElems ParcElems Alloc MODULE ObTris; (** V1.0 (C) 1 Nov 1995 by Ralf Degner, E-Mail: degner@pallas.amp.uni-hannover.de *) (* If you use MacOberon or the Ceres replace Input.TimeUnit by 300 *) IMPORT Oberon, Viewers, Display, MenuViewers, TextFrames, Texts, Files, Input; CONST red=1; blue=3; green=2; yellow=4; col1=5; col2=6; col3=7; Menu = "System.Close System.Copy System.Grow ObTris.Start ObTris.ShowNext ObTris.Score"; XAnzahl=10; YAnzahl=24; MinKasten=4; UntenOffset=10; ObenOffset=4; SeitenOffset=10; NextXPos=-5; NextYPos=YAnzahl DIV 2; LinesProLevel=10; SpeedUpProLevel=20; ScoreFakt=10; ScoreFileMark=06C6F6976H; String = ARRAY 32 OF CHAR; Game = POINTER TO GameDesc; GameDesc = RECORD Field: ARRAY XAnzahl+2 OF ARRAY YAnzahl+2 OF INTEGER; Runs, ShowNext: BOOLEAN; Delay, Score, Level, Lines: LONGINT; x, y, p, fig, next: INTEGER; END; Frame = POINTER TO FrameDesc; FrameDesc = RECORD(Display.FrameDesc) XOffset, YOffset: INTEGER; Kasten: INTEGER; Aktiv: BOOLEAN; G: Game; END; ObTrisMsg = RECORD(Display.FrameMsg) END; DrawMsg = RECORD(ObTrisMsg) G: Game; END; W: Texts.Writer; Name: String; Seed, Delay: LONGINT; Fig: ARRAY 8 OF ARRAY 4 OF ARRAY 4 OF ARRAY 4 OF INTEGER; FigSize: ARRAY 8 OF INTEGER; HiScore, HiLevel, HiLines: ARRAY 10 OF LONGINT; HiName: ARRAY 10 OF String; ScoreFile: Files.File; ScoreRider: Files.Rider; ch: ARRAY 7 OF CHAR; (* Generate Random Numbers *) PROCEDURE Random(Ein: INTEGER):INTEGER; CONST a=16807; m=2147483647; q=m DIV a; r=m MOD a; VAR g: LONGINT; BEGIN g:=a*(Seed MOD q)-r*(Seed DIV q); IF g>0 THEN Seed:=g ELSE Seed:=g+m END; RETURN SHORT(Seed) MOD Ein END Random; (* Print current Keys *) PROCEDURE PrintKeys(); VAR d: INTEGER; BEGIN Texts.WriteString(W, "Current Keys: "); FOR d:=0 TO 5 DO IF ch[d]=CHR(193) THEN Texts.WriteString(W, "UP") ELSIF ch[d]=CHR(194) THEN Texts.WriteString(W, "DOWN") ELSIF ch[d]=CHR(196) THEN Texts.WriteString(W, "LEFT") ELSIF ch[d]=CHR(195) THEN Texts.WriteString(W, "RIGHT") ELSIF ch[d]=CHR(13) THEN Texts.WriteString(W, "RETURN") ELSIF ch[d]=CHR(27) THEN Texts.WriteString(W, "ESC") ELSIF ch[d]=CHR(9) THEN Texts.WriteString(W, "TAB") ELSIF ch[d]=" " THEN Texts.WriteString(W, "SPACE") ELSE Texts.Write(W, ch[d]); END; Texts.Write(W, " "); END; Texts.WriteLn(W); END PrintKeys; (* Store HiScore *) PROCEDURE SaveHi(Register: BOOLEAN); VAR d: INTEGER; BEGIN Files.Set(ScoreRider, ScoreFile, 0); Files.WriteLInt(ScoreRider, ScoreFileMark); FOR d:=0 TO 5 DO Files.Write(ScoreRider, ch[d]) END; FOR d:=0 TO 9 DO Files.WriteBytes(ScoreRider, HiName[d], 32); Files.WriteLInt(ScoreRider, HiScore[d]); Files.WriteLInt(ScoreRider, HiLevel[d]); Files.WriteLInt(ScoreRider, HiLines[d]); END; IF Register THEN Files.Register(ScoreFile) ELSE Files.Close(ScoreFile) END END SaveHi; (* Load HiScore *) PROCEDURE LoadHi(); d: INTEGER; m: LONGINT; PROCEDURE ClearHi(); VAR n: INTEGER; BEGIN ch[0]:="j"; ch[1]:="k"; ch[2]:="i"; ch[3]:="m"; ch[4]:="h"; ch[5]:="p"; FOR n:=0 TO 9 DO HiScore[n]:=0; HiLevel[n]:=0; HiLines[n]:=0; COPY("Amiga", HiName[n]) END END ClearHi; BEGIN ScoreFile:=Files.Old("ObTris.Score"); IF ScoreFile=NIL THEN ScoreFile:=Files.New("ObTris.Score"); ClearHi(); SaveHi(TRUE) ELSE Files.Set(ScoreRider, ScoreFile, 0); Files.ReadLInt(ScoreRider, m); IF m=ScoreFileMark THEN FOR d:=0 TO 5 DO Files.Read(ScoreRider, ch[d]) END; FOR d:=0 TO 9 DO Files.ReadBytes(ScoreRider, HiName[d], 32); Files.ReadLInt(ScoreRider, HiScore[d]); Files.ReadLInt(ScoreRider, HiLevel[d]); Files.ReadLInt(ScoreRider, HiLines[d]) END ELSE ClearHi(); SaveHi(FALSE) END END LoadHi; (* New Score for Hall of Fame ? If Yes, Register *) PROCEDURE RegisterScore(s, le, li: LONGINT); VAR d, n: LONGINT; BEGIN d:=9; WHILE (d#-1) & (HiScore[d]f.G.Level THEN INC(f.G.Level); INC(f.G.Score, LinesProLevel DIV 2); f.G.Delay:=(f.G.Delay * (100-SpeedUpProLevel)) DIV 100 END KillLines; (* Clear Field *) PROCEDURE ClearField(G: Game); VAR XDum, YDum: INTEGER; BEGIN FOR XDum:= 1 TO XAnzahl DO FOR YDum:=1 TO YAnzahl DO G.Field[XDum, YDum]:=0 END END; FOR YDum:=0 TO YAnzahl DO G.Field[0, YDum]:=1; G.Field[XAnzahl+1, YDum]:=1; END; FOR XDum:=0 TO XAnzahl+1 DO G.Field[XDum, 0]:=1; G.Field[XDum, YAnzahl+1]:=1 END ClearField; (* Clear Frame and Draw everything necessary *) PROCEDURE ClearFrame(f: Frame; x, y, w, h: INTEGER); VAR XDum, YDum: INTEGER; BEGIN Oberon.RemoveMarks(x, y, w, h); Display.ReplConst(Display.black, x, y, w, h, Display.paint); IF f.Aktiv THEN XDum:=f.Kasten*XAnzahl+1; YDum:=f.Kasten*YAnzahl; Display.ReplConst(Display.white, f.XOffset-3, f.YOffset-3, XDum+4, YDum+3, Display.paint); Display.ReplConst(Display.black, f.XOffset-1, f.YOffset-1, XDum, YDum+1, Display.paint); IF f.G.ShowNext THEN XDum:=f.XOffset+f.Kasten*NextXPos-3; YDum:=f.YOffset+f.Kasten*NextYPos-3; Display.ReplConst(Display.white, XDum, YDum, 4*f.Kasten+5, 2*f.Kasten+5, Display.paint); Display.ReplConst(Display.black, XDum+2, YDum+2, 4*f.Kasten+1, 2*f.Kasten+1, Display.paint); END; RedrawField(f); IF f.G.Runs THEN DrawFig(f, f.G.x, f.G.y, f.G.fig, f.G.p); IF f.G.ShowNext THEN DrawFig(f, NextXPos+1, NextYPos+1, f.G.next, 0) END END END ClearFrame; (* copy frame with same data *) PROCEDURE CopyMe(f: Frame): Frame; VAR nf: Frame; BEGIN NEW(nf);IF nf=NIL THEN RETURN NIL;END; nf.handle:=f.handle; nf.G:=f.G; RETURN nf; END CopyMe; (* Open MenuFrame with ObTris.Menu.Text *) PROCEDURE MenuFrame(): TextFrames.Frame; mf: TextFrames.Frame; buf: Texts.Buffer; t: Texts.Text; r: Texts.Reader; end: LONGINT; ch: CHAR; BEGIN IF Files.Old("ObTris.Menu.Text")=NIL THEN mf:=TextFrames.NewMenu("ObTris", Menu) ELSE mf:=TextFrames.NewMenu("ObTris", ""); NEW(t);Texts.Open(t, "ObTris.Menu.Text"); Texts.OpenReader(r, t, 0); REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch=0DX); IF r.eot THEN end:=t.len ELSE end:=Texts.Pos(r)-1; END; NEW(buf); Texts.OpenBuf(buf); Texts.Save(t, 0, end, buf);Texts.Append(mf.text, buf) END; RETURN mf; END MenuFrame; (* Open new Text-Frame *) PROCEDURE OpenViewer(text: Texts.Text); VAR x, y: INTEGER; v: Viewers.Viewer; cf: TextFrames.Frame; BEGIN Oberon.AllocateSystemViewer(Oberon.Par.vwr.X, x, y); cf := TextFrames.NewText(text, 0); v := MenuViewers.New(TextFrames.NewMenu("ObTris Hall of Fame", "System.Close System.Copy System.Grow"), cf, TextFrames.menuH, x, y) END OpenViewer; (* Handler of an ObTris Frame *) PROCEDURE Handler(f: Display.Frame; VAR m: Display.FrameMsg); VAR self: Frame; BEGIN self:=f(Frame); WITH m: Oberon.InputMsg DO IF m.id=Oberon.track THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, m.X, m.Y) END | m: Oberon.CopyMsg DO m.F:=CopyMe(self) | m: MenuViewers.ModifyMsg DO WITH m: MenuViewers.ModifyMsg DO IF m.H#0 THEN CalcKasten(self, f.X, m.Y, f.W, m.H); ClearFrame(self, f.X, m.Y, f.W, m.H) END END | m: ObTrisMsg DO WITH m: DrawMsg DO IF m.G=self.G THEN CalcKasten(self, f.X, f.Y, f.W, f.H); ClearFrame(self, f.X, f.Y, f.W, f.H) END ELSE END ELSE END Handler; (* get current/marked Frame *) PROCEDURE GetFrame(VAR f: Display.Frame): BOOLEAN; VAR v: Viewers.Viewer; BEGIN IF Oberon.Par.frame=Oberon.Par.vwr.dsc THEN IF (Oberon.Par.frame # NIL) THEN f:=Oberon.Par.frame.next; RETURN TRUE END ELSE v:=Oberon.MarkedViewer(); IF (v.dsc # NIL) & (v.dsc.next # NIL) THEN f:=v.dsc.next; RETURN TRUE END END; RETURN FALSE; END GetFrame; (* Calc System Speed *) PROCEDURE CalcSysSpeed(): LONGINT; EndTime, Time, StartTime, q, Anz: LONGINT; c: CHAR; f: Frame; x, y, fig, p, d: INTEGER; BEGIN NEW(f);x:=0; y:=0; fig:=0; p:=0; Anz:=0; StartTime:=Input.Time(); ch[6]:=CHR(0); EndTime:=StartTime+Input.TimeUnit; (* replace Input.TimeUnit by 300 at MacOberon and Ceres *) REPEAT FOR q:=0 TO 31 DO IF Input.Available()>0 THEN Input.Read(c); IF (c=ch[6]) OR (CAP(c)="P") THEN ELSIF c=ch[6] THEN IF TestFig(f.G, x-1, y, fig, p) THEN ClearFig(f, x, y, fig, p); DEC(x); DrawFig(f, x, y, fig, p) END ELSIF c=ch[6] THEN IF TestFig(f.G, x+1, y, fig, p) THEN ClearFig(f, x, y, fig, p); INC(x); DrawFig(f, x, y, fig, p) END ELSIF c=ch[6] THEN d:=p+1; IF d=4 THEN d:=0; END; IF TestFig(f.G, x, y, fig, d) THEN ClearFig(f, x, y, fig, p); DrawFig(f, x, y, fig, d); p:=d END ELSIF c=ch[6] THEN d:=p-1; IF d=-1 THEN d:=3; END; IF TestFig(f.G, x, y, fig, d) THEN ClearFig(f, x, y, fig, p); DrawFig(f, x, y, fig, d); p:=d END ELSIF c=ch[6] THEN d:=y; WHILE TestFig(f.G, x, y-1, fig, p) DO DEC(y); END; ClearFig(f, x, d, fig, p); DrawFig(f, x, y, fig, p) END END END; INC(Anz, 31); Time:=Input.Time() UNTIL Time>=EndTime; RETURN (Anz*(EndTime-StartTime)) DIV Input.TimeUnit; (* replace Input.TimeUnit by 300 at MacOberon and Ceres *) END CalcSysSpeed; (* Main-Loop of the Game *) PROCEDURE GameLoop(f: Frame); c: CHAR; DelCount: LONGINT; x, y, p, fig, next, d: INTEGER; msg: DrawMsg; BEGIN x:=f.G.x; y:=f.G.y; p:=f.G.p; fig:=f.G.fig; next:=f.G.next; f.G.Runs:=TRUE; Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); IF f.G.ShowNext THEN DrawFig(f, NextXPos+1, NextYPos+1, next, 0); END; LOOP IF TestFig(f.G, x, y-1, fig, p) THEN ClearFig(f, x, y, fig, p); DEC(y); DrawFig(f, x, y, fig, p) ELSE RegisterFig(f.G, x, y, fig, p); KillLines(f); y:=YAnzahl-2; p:=0; fig:=next; next:=Random(7); x:=((XAnzahl-FigSize[fig]) DIV 2)+1; IF fig=1 THEN DEC(y); p:=2; END; IF ~TestFig(f.G, x, y, fig, p) THEN f.G.Runs:=FALSE; EXIT END; IF f.G.ShowNext THEN ClearFig(f, NextXPos+1, NextYPos+1, fig, 0); DrawFig(f, NextXPos+1, NextYPos+1, next, 0); END; DrawFig(f, x, y, fig, p); END; FOR DelCount:=0 TO f.G.Delay DO IF Input.Available()>0 THEN Input.Read(c); IF (c=ch[5]) OR (CAP(c)="P") THEN EXIT ELSIF c=ch[0] THEN IF TestFig(f.G, x-1, y, fig, p) THEN ClearFig(f, x, y, fig, p); DEC(x); DrawFig(f, x, y, fig, p) END ELSIF c=ch[1] THEN IF TestFig(f.G, x+1, y, fig, p) THEN ClearFig(f, x, y, fig, p); INC(x); DrawFig(f, x, y, fig, p) END ELSIF (c=ch[2]) & (x#-1) THEN d:=p+1; IF d=4 THEN d:=0; END; IF TestFig(f.G, x, y, fig, d) THEN ClearFig(f, x, y, fig, p); DrawFig(f, x, y, fig, d); p:=d END ELSIF (c=ch[3]) & (x#-1) THEN d:=p-1; IF d=-1 THEN d:=3; END; IF TestFig(f.G, x, y, fig, d) THEN ClearFig(f, x, y, fig, p); DrawFig(f, x, y, fig, d); p:=d END ELSIF c=ch[4] THEN d:=y; WHILE TestFig(f.G, x, y-1, fig, p) DO DEC(y); END; ClearFig(f, x, d, fig, p); DrawFig(f, x, y, fig, p) END END END END; IF f.G.Runs THEN f.G.x:=x; f.G.y:=y; f.G.p:=p; f.G.fig:=fig; f.G.next:=next; Texts.WriteString(W, "Current ObTris Status -") ELSE Texts.WriteString(W, "--- GAME OVER --- "); RegisterScore(f.G.Score, f.G.Level, f.G.Lines); END; Texts.WriteString(W, " Score: "); Texts.WriteInt(W, f.G.Score*ScoreFakt, 1); Texts.WriteString(W, " Lines: "); Texts.WriteInt(W, f.G.Lines, 1); Texts.WriteString(W, " Level: "); Texts.WriteInt(W, f.G.Level, 1); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); msg.G:=f.G; Viewers.Broadcast(msg); END GameLoop; (* Start New Game *) PROCEDURE StartNewGame(g: Frame); VAR msg: DrawMsg; BEGIN IF g.Aktiv THEN g.G.Delay:=Delay; ClearField(g.G); g.G.y:=YAnzahl-2; g.G.p:=0; g.G.fig:=Random(7); g.G.next:=Random(7); g.G.Lines:=0; g.G.Score:=0; g.G.Level:=0; g.G.x:=((XAnzahl-FigSize[g.G.fig]) DIV 2)+1; IF g.G.fig=1 THEN DEC(g.G.y); g.G.p:=2; END; msg.G:=g.G; Viewers.Broadcast(msg); GameLoop(g) END StartNewGame; (* Open new ObTris Frame *) PROCEDURE Open*(); f: Frame; v: MenuViewers.Viewer; x, y: INTEGER; BEGIN NEW(f); NEW(f.G); f.G.ShowNext:=TRUE; f.Aktiv:=FALSE; f.handle:=Handler; Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); v:=MenuViewers.New(MenuFrame(), f, TextFrames.menuH, x, y); ClearField(f.G); END Open; (* Start New Game Command *) PROCEDURE StartNew*(); VAR f, g: Display.Frame; BEGIN IF GetFrame(f) THEN g:=f; WITH g: Frame DO StartNewGame(g) ELSE END END StartNew; (* Restart Game or Start New *) PROCEDURE Start*(); VAR f, g: Display.Frame; BEGIN IF GetFrame(f) THEN g:=f; WITH g: Frame DO IF g.Aktiv THEN IF g.G.Runs THEN GameLoop(g) ELSE StartNewGame(g) END END ELSE END END Start; (* Restart Game or Start New *) PROCEDURE ShowNext*(); f, g: Display.Frame; msg: DrawMsg; BEGIN IF GetFrame(f) THEN g:=f; WITH g: Frame DO g.G.ShowNext:=~g.G.ShowNext; msg.G:=g.G; Viewers.Broadcast(msg) ELSE END END ShowNext; (* set new username *) PROCEDURE SetUser*; S: Texts.Scanner; text: Texts.Text; beg, end, time: LONGINT; BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); IF S.class=Texts.Char THEN IF S.c="^" THEN Oberon.GetSelection(text, beg, end, time); IF time=-1 THEN RETURN; END; Texts.OpenScanner(S, text, beg); Texts.Scan(S) ELSE RETURN END END; IF S.class=Texts.Name THEN COPY(S.s, Name); END; Texts.WriteString(W, "Current Username : "); Texts.WriteString(W, Name); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); END SetUser; (* show Hi-Score *) PROCEDURE Score*; i: INTEGER; te: Texts.Text; BEGIN NEW(te); te:=TextFrames.Text(""); IF Files.Old("ObTris.Score.Text")=NIL THEN NEW(te); te:=TextFrames.Text(""); Texts.WriteString(W, " Oberon-Tetris Hall Of Fame ! ");Texts.WriteLn(W); Texts.WriteString(W, "______________________________________________________");Texts.WriteLn(W) ELSE Texts.Open(te, "ObTris.Score.Text"); END; FOR i:=0 TO 9 DO Texts.WriteInt(W, i+1, 1); Texts.Write(W, CHR(9)); Texts.WriteString(W, HiName[i]); Texts.Write(W, CHR(9)); Texts.WriteInt(W, HiScore[i]*ScoreFakt, 1); Texts.Write(W, CHR(9)); Texts.WriteInt(W, HiLevel[i], 1); Texts.Write(W, CHR(9)); Texts.WriteInt(W, HiLines[i], 1); Texts.WriteLn(W); END; Texts.WriteLn(W); Texts.WriteString(W, "Current Username : "); Texts.WriteString(W, Name); Texts.WriteLn(W); Texts.WriteLn(W); PrintKeys(); Texts.Append(te, W.buf); OpenViewer(te); END Score; (* set keys *) PROCEDURE SetKeys*; S: Texts.Scanner; text: Texts.Text; d, beg, end, time: LONGINT; c: ARRAY 6 OF CHAR; BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); IF (S.class=Texts.Char) & (S.c="^") THEN Oberon.GetSelection(text, beg, end, time); IF time=-1 THEN RETURN; END; Texts.OpenScanner(S, text, beg); Texts.Scan(S) END; FOR d:=0 TO 5 DO IF (S.class=Texts.Char) & (S.c#CHR(0)) THEN c[d]:=S.c ELSIF (S.class=Texts.Int) & (S.i>=0) & (S.i<=9) THEN c[d]:=CHR(48+S.i) ELSIF S.class=Texts.Name THEN IF S.s="UP" THEN c[d]:=CHR(193) ELSIF S.s="DOWN" THEN c[d]:=CHR(194) ELSIF S.s="LEFT" THEN c[d]:=CHR(196) ELSIF S.s="RIGHT" THEN c[d]:=CHR(195) ELSIF S.s="RETURN" THEN c[d]:=CHR(13) ELSIF S.s="TAB" THEN c[d]:=CHR(9) ELSIF S.s="SPACE" THEN c[d]:=" " ELSIF S.s="ESC" THEN c[d]:=CHR(27) ELSE c[d]:=S.s[0] END ELSE Texts.WriteString(W, "Wrong Key Or Not Enough Keys!"); Texts.Append(Oberon.Log, W.buf); RETURN END; Texts.Scan(S) END; FOR d:=0 TO 5 DO ch[d]:=c[d] END; PrintKeys(); Texts.Append(Oberon.Log, W.buf); SaveHi(FALSE); END SetKeys; (* Create all Figures in Fig *) PROCEDURE CreateFigures(); VAR a, p, x, y, s, d: INTEGER; PROCEDURE ClearFig(fi, neu: INTEGER); BEGIN FOR x:=0 TO 3 DO FOR y:=0 TO 3 DO Fig[fi, neu,x, y]:=0 END END END ClearFig; BEGIN (* clear all Figures at Pos 0*) FOR a:=0 TO 6 DO ClearFig(a, 0) END; (* set Figures at Pos 1*) FigSize[0]:=2; Fig[0,0,0,0]:=blue; Fig[0,0,1,0]:=blue; Fig[0,0,0,1]:=blue; Fig[0,0,1,1]:=blue; FigSize[1]:=4; Fig[1,0,0,1]:=red; Fig[1,0,1,1]:=red; Fig[1,0,2,1]:=red; Fig[1,0,3,1]:=red; FigSize[2]:=3; Fig[2,0,1,1]:=green; Fig[2,0,0,0]:=green; Fig[2,0,1,0]:=green; Fig[2,0,2,1]:=green; FigSize[3]:=3; Fig[3,0,1,1]:=col1; Fig[3,0,0,1]:=col1; Fig[3,0,1,0]:=col1; Fig[3,0,2,0]:=col1; FigSize[4]:=3; Fig[4,0,1,1]:=yellow; Fig[4,0,0,0]:=yellow; Fig[4,0,0,1]:=yellow; Fig[4,0,2,1]:=yellow; FigSize[5]:=3; Fig[5,0,1,1]:=col2; Fig[5,0,2,0]:=col2; Fig[5,0,0,1]:=col2; Fig[5,0,2,1]:=col2; FigSize[6]:=3; Fig[6,0,1,1]:=col3; Fig[6,0,1,0]:=col3; Fig[6,0,0,1]:=col3; Fig[6,0,2,1]:=col3; (* generate rotated Figures *) FOR a:=0 TO 6 DO FOR p:=1 TO 3 DO s:=FigSize[a]-1; IF (s=1) OR (s=2) THEN ClearFig(a, p); END; FOR x:=0 TO s DO FOR y:=0 TO s DO d:=Fig[a, p-1, x, y]; Fig[a, p, s-y, x]:=d END END END END CreateFigures; BEGIN Texts.OpenWriter(W); Texts.WriteString(W, "ObTris (Oberon-Tetris) V1.0"); Texts.WriteLn(W); Texts.WriteString(W, "(C) 1 Nov 1995 by Ralf Degner"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); IF Oberon.User="" THEN Name:="AMIGA" ELSE COPY(Oberon.User, Name); END; Delay:=CalcSysSpeed(); CreateFigures(); LoadHi(); Seed:=Input.Time(); END ObTris.Open System.Free ObTris ~