3 * Micropolis, Unix Version. This game was released for the Unix platform
4 * in or about 1990 and has been modified for inclusion in the One Laptop
5 * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
6 * you need assistance with this program, you may contact:
7 * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or (at
12 * your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details. You should have received a
18 * copy of the GNU General Public License along with this program. If
19 * not, see <http://www.gnu.org/licenses/>.
21 * ADDITIONAL TERMS per GNU GPL Section 7
23 * No trademark or publicity rights are granted. This license does NOT
24 * give you any right, title or interest in the trademark SimCity or any
25 * other Electronic Arts trademark. You may not distribute any
26 * modification of this program using the trademark SimCity or claim any
27 * affliation or association with Electronic Arts Inc. or its employees.
29 * Any propagation or conveyance of this program must include this
30 * copyright notice and these terms.
32 * If you convey this program (or any modifications of it) and assume
33 * contractual liability for the program to recipients of it, you agree
34 * to indemnify Electronic Arts for any liability that those contractual
35 * assumptions impose on Electronic Arts.
37 * You may not misrepresent the origins of this program; modified
38 * versions of the program must be marked as such and not identified as
39 * the original program.
41 * This disclaimer supplements the one included in the General Public
42 * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
43 * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
44 * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
45 * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
46 * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
47 * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
48 * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
49 * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
50 * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
51 * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
52 * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
53 * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
54 * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
55 * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
56 * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
57 * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
58 * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
59 * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
65 #define filename2UNIX(name) \
66 { char *p; for (p = name; *p; p++) if (*p == '\\') *p = '/'; }
68 #define filename2UNIX(name) /**/
72 Tcl_Interp
*tk_mainInterp
= NULL
;
73 Tcl_CmdBuf buffer
= NULL
;
74 Tk_TimerToken sim_timer_token
= 0;
75 int sim_timer_idle
= 0;
76 int sim_timer_set
= 0;
78 int UpdateDelayed
= 0;
79 int AutoScrollEdge
= 16;
80 int AutoScrollStep
= 16;
81 int AutoScrollDelay
= 10;
82 Tk_TimerToken earthquake_timer_token
;
83 int earthquake_timer_set
= 0;
84 int earthquake_delay
= 3000;
85 int PerformanceTiming
;
90 #define DEF_VIEW_FONT "-Adobe-Helvetica-Bold-R-Normal-*-140-*"
92 Tk_ConfigSpec TileViewConfigSpecs
[] = {
93 {TK_CONFIG_FONT
, "-font", (char *) NULL
, (char *) NULL
,
94 DEF_VIEW_FONT
, Tk_Offset(SimView
, fontPtr
), 0},
95 {TK_CONFIG_STRING
, "-messagevar", (char *) NULL
, (char *) NULL
,
96 NULL
, Tk_Offset(SimView
, message_var
), 0},
97 {TK_CONFIG_PIXELS
, "-width", "width", "Width",
98 0, Tk_Offset(SimView
, width
), 0},
99 {TK_CONFIG_PIXELS
, "-height", "height", "Height",
100 0, Tk_Offset(SimView
, height
), 0},
101 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
106 int TileViewCmd(CLIENT_ARGS
);
107 static void TileViewEventProc(ClientData clientData
, XEvent
*eventPtr
);
108 static void DestroyTileView(ClientData clientData
);
109 static void MicropolisTimerProc(ClientData clientData
);
111 int SimCmd(CLIENT_ARGS
);
112 int DoEditorCmd(CLIENT_ARGS
);
113 int DoMapCmd(CLIENT_ARGS
);
114 int GraphViewCmd(CLIENT_ARGS
);
115 int DoGraphCmd(CLIENT_ARGS
);
116 int SpriteCmd(CLIENT_ARGS
);
117 extern int Tk_PieMenuCmd();
118 extern int Tk_IntervalCmd();
122 TileViewCmd(CLIENT_ARGS
)
124 Tk_Window tkwin
= (Tk_Window
) clientData
;
129 Tcl_AppendResult(interp
, "wrong # args: should be \"",
130 argv
[0], " pathName ?options?\"", (char *) NULL
);
134 if (strcmp(argv
[0], "editorview") == 0)
135 viewclass
= Editor_Class
;
136 else if (strcmp(argv
[0], "mapview") == 0)
137 viewclass
= Map_Class
;
142 tkwin
= Tk_CreateWindowFromPath(interp
, tkwin
,
143 argv
[1], (char *) NULL
);
148 view
= (SimView
*)ckalloc(sizeof (SimView
));
151 view
->interp
= interp
;
154 if (viewclass
== Editor_Class
) {
155 Tk_SetClass(view
->tkwin
, "EditorView");
157 Tk_CreateEventHandler(view
->tkwin
,
158 VisibilityChangeMask
|
160 StructureNotifyMask
|
164 TileViewEventProc
, (ClientData
) view
);
165 Tcl_CreateCommand(interp
, Tk_PathName(view
->tkwin
),
166 DoEditorCmd
, (ClientData
) view
, (void (*)()) NULL
);
168 Tk_SetClass(view
->tkwin
, "MapView");
170 Tk_CreateEventHandler(view
->tkwin
,
171 VisibilityChangeMask
|
173 StructureNotifyMask
/* |
176 PointerMotionMask */ ,
177 TileViewEventProc
, (ClientData
) view
);
178 Tcl_CreateCommand(interp
, Tk_PathName(view
->tkwin
),
179 DoMapCmd
, (ClientData
) view
, (void (*)()) NULL
);
182 Tk_MakeWindowExist(view
->tkwin
);
184 if (getenv("XSYNCHRONIZE") != NULL
) {
185 XSynchronize(Tk_Display(tkwin
), 1);
188 if (viewclass
== Editor_Class
) {
189 InitNewView(view
, "MicropolisEditor", Editor_Class
, EDITOR_W
, EDITOR_H
);
192 InitNewView(view
, "MicropolisMap", Map_Class
, MAP_W
, MAP_H
);
196 if (ConfigureTileView(interp
, view
, argc
-2, argv
+2, 0) != TCL_OK
) {
197 /* XXX: destroy view */
198 Tk_DestroyWindow(view
->tkwin
);
202 switch (view
->class) {
212 interp
->result
= Tk_PathName(view
->tkwin
);
218 ConfigureTileView(Tcl_Interp
*interp
, SimView
*view
,
219 int argc
, char **argv
, int flags
)
221 if (Tk_ConfigureWidget(interp
, view
->tkwin
, TileViewConfigSpecs
,
222 argc
, argv
, (char *) view
, flags
) != TCL_OK
) {
226 if (view
->class == Map_Class
) {
227 Tk_GeometryRequest(view
->tkwin
, MAP_W
, MAP_H
);
229 if (view
->width
|| view
->height
) {
230 Tk_GeometryRequest(view
->tkwin
, view
->width
, view
->height
);
233 EventuallyRedrawView(view
);
243 //fprintf(stderr, "InvalidateMaps\n");
244 for (view
= sim
->map
; view
!= NULL
; view
= view
->next
) {
247 EventuallyRedrawView(view
);
254 InvalidateEditors(void)
258 //fprintf(stderr, "InvalidateEditors\n");
259 for (view
= sim
->editor
; view
!= NULL
; view
= view
->next
) {
262 EventuallyRedrawView(view
);
273 //fprintf(stderr, "RedrawMaps\n");
275 for (view
= sim
->map
; view
!= NULL
; view
= view
->next
) {
277 EventuallyRedrawView(view
);
288 //fprintf(stderr, "RedrawEditors\n");
290 for (view
= sim
->editor
; view
!= NULL
; view
= view
->next
) {
292 EventuallyRedrawView(view
);
299 DisplayTileView(ClientData clientData
)
301 SimView
*view
= (SimView
*) clientData
;
302 Tk_Window tkwin
= view
->tkwin
;
304 view
->flags
&= ~VIEW_REDRAW_PENDING
;
305 if (view
->visible
&& (tkwin
!= NULL
) && Tk_IsMapped(tkwin
)) {
306 switch (view
->class) {
310 DoUpdateEditor(view
);
313 //fprintf(stderr, "DisplayTileView\n");
325 TileViewEventProc expose configure motion
334 EventuallyRedrawView(SimView
*view
)
336 if (!(view
->flags
& VIEW_REDRAW_PENDING
)) {
337 Tk_DoWhenIdle(DisplayTileView
, (ClientData
) view
);
338 view
->flags
|= VIEW_REDRAW_PENDING
;
345 CancelRedrawView(SimView
*view
)
347 if (view
->flags
& VIEW_REDRAW_PENDING
) {
348 Tk_CancelIdleCall(DisplayTileView
, (ClientData
) view
);
350 view
->flags
&= ~VIEW_REDRAW_PENDING
;
355 TileAutoScrollProc(ClientData clientData
)
357 SimView
*view
= (SimView
*)clientData
;
360 if (view
->tool_mode
!= 0) {
362 int root_x
, root_y
, x
, y
;
363 unsigned int key_buttons
;
366 XQueryPointer(Tk_Display(view
->tkwin
), Tk_WindowId(view
->tkwin
),
367 &root
, &child
, &root_x
, &root_y
, &x
, &y
, &key_buttons
);
369 if (x
< AutoScrollEdge
)
370 dx
= -AutoScrollStep
;
371 else if (x
> (view
->w_width
- AutoScrollEdge
))
373 if (y
< AutoScrollEdge
)
374 dy
= -AutoScrollStep
;
375 else if (y
> (view
->w_height
- AutoScrollEdge
))
379 int px
= view
->pan_x
, py
= view
->pan_y
;
381 if (view
->tool_mode
== -1) {
385 DoPanBy(view
, dx
, dy
);
386 view
->tool_x
+= view
->pan_x
- px
;
387 view
->tool_y
+= view
->pan_y
- py
;
388 view
->auto_scroll_token
=
389 Tk_CreateTimerHandler(AutoScrollDelay
, TileAutoScrollProc
,
392 sprintf(buf
, "UIDidPan %s %d %d", Tk_PathName(view
->tkwin
), x
, y
);
400 TileViewEventProc(ClientData clientData
, XEvent
*eventPtr
)
402 SimView
*view
= (SimView
*) clientData
;
404 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
406 EventuallyRedrawView(view
);
407 } else if (eventPtr
->type
== MapNotify
) {
409 } else if (eventPtr
->type
== UnmapNotify
) {
411 } else if (eventPtr
->type
== VisibilityNotify
) {
412 if (eventPtr
->xvisibility
.state
== VisibilityFullyObscured
)
416 } else if (eventPtr
->type
== ConfigureNotify
) {
417 if (view
->class == Editor_Class
)
419 eventPtr
->xconfigure
.width
,
420 eventPtr
->xconfigure
.height
);
421 EventuallyRedrawView(view
);
422 } else if (eventPtr
->type
== DestroyNotify
) {
423 Tcl_DeleteCommand(view
->interp
, Tk_PathName(view
->tkwin
));
425 CancelRedrawView(view
);
426 Tk_EventuallyFree((ClientData
) view
, DestroyTileView
);
427 } else if ((view
->class == Editor_Class
) &&
428 (view
->show_me
!= 0) &&
429 ((eventPtr
->type
== EnterNotify
) ||
430 (eventPtr
->type
== LeaveNotify
) ||
431 (eventPtr
->type
== MotionNotify
))) {
432 int last_x
= view
->tool_x
, last_y
= view
->tool_y
,
433 last_showing
= view
->tool_showing
;
436 if (eventPtr
->type
== EnterNotify
) {
438 x
= eventPtr
->xcrossing
.x
; y
= eventPtr
->xcrossing
.y
;
439 } else if (eventPtr
->type
== LeaveNotify
) {
441 x
= eventPtr
->xcrossing
.x
; y
= eventPtr
->xcrossing
.y
;
444 x
= eventPtr
->xmotion
.x
; y
= eventPtr
->xmotion
.y
;
447 if (view
->tool_mode
!= 0) {
449 if ((x
< AutoScrollEdge
) ||
450 (x
> (view
->w_width
- AutoScrollEdge
)) ||
451 (y
< AutoScrollEdge
) ||
452 (y
> (view
->w_height
- AutoScrollEdge
))) {
453 if (!view
->auto_scroll_token
) {
454 view
->auto_scroll_token
=
455 Tk_CreateTimerHandler(AutoScrollDelay
, TileAutoScrollProc
,
459 if (view
->auto_scroll_token
) {
460 Tk_DeleteTimerHandler(view
->auto_scroll_token
);
461 view
->auto_scroll_token
= 0;
466 ViewToPixelCoords(view
, x
, y
, &x
, &y
);
467 view
->tool_showing
= showing
;
469 if (view
->tool_mode
!= -1) {
470 view
->tool_x
= x
; view
->tool_y
= y
;
473 /* XXX: redraw all views showing cursor */
474 /* XXX: also, make sure switching tools works w/out moving */
475 if (((view
->tool_showing
!= last_showing
) ||
476 ((view
->tool_x
>> 4) != (last_x
>> 4)) ||
477 ((view
->tool_y
>> 4) != (last_y
>> 4)))) {
479 EventuallyRedrawView(view
);
489 DestroyTileView(ClientData clientData
)
491 SimView
*view
= (SimView
*) clientData
;
498 StdinProc(ClientData clientData
, int mask
)
501 static int gotPartial
= 0;
505 if (mask
& TK_READABLE
) {
506 if (fgets(line
, 200, stdin
) == NULL
) {
509 sim_exit(0); // Just sets tkMustExit and ExitReturn
512 Tk_DeleteFileHandler(0);
519 cmd
= Tcl_AssembleCmd(buffer
, line
);
525 result
= Tcl_RecordAndEval(tk_mainInterp
, cmd
, 0);
526 if (*tk_mainInterp
->result
!= 0) {
527 if ((result
!= TCL_OK
) || sim_tty
) {
528 printf("%s\n", tk_mainInterp
->result
);
540 StructureProc(ClientData clientData
, XEvent
*eventPtr
)
542 if (eventPtr
->type
== DestroyNotify
) {
550 DelayedMap(ClientData clientData
)
552 while (Tk_DoOneEvent(TK_IDLE_EVENTS
) != 0) {
553 /* Empty loop body. */
555 if (MainWindow
== NULL
) {
558 Tk_MapWindow(MainWindow
);
564 DidStopPan(SimView
*view
)
567 sprintf(buf
, "UIDidStopPan %s", Tk_PathName(view
->tkwin
));
574 MicropolisTimerProc(ClientData clientData
)
576 sim_timer_token
= NULL
;
585 StartMicropolisTimer();
587 StopMicropolisTimer();
593 ReallyStartMicropolisTimer(ClientData clientData
)
595 int delay
= sim_delay
;
596 XDisplay
*xd
= XDisplays
;
598 StopMicropolisTimer();
601 if ((NeedRest
> 0) ||
603 (xd
->tkDisplay
->buttonWinPtr
!= NULL
) ||
604 (xd
->tkDisplay
->grabWinPtr
!= NULL
)) {
605 if (ShakeNow
|| NeedRest
) {
606 if (delay
< 50000) delay
= 50000;
615 Tk_CreateMicroTimerHandler(
626 StartMicropolisTimer(void)
628 if (sim_timer_idle
== 0) {
631 ReallyStartMicropolisTimer
,
638 StopMicropolisTimer(void)
640 if (sim_timer_idle
!= 0) {
643 ReallyStartMicropolisTimer
,
648 if (sim_timer_token
!= 0) {
649 Tk_DeleteTimerHandler(sim_timer_token
);
661 StartMicropolisTimer();
667 DelayedUpdate(ClientData clientData
)
669 //fprintf(stderr, "DelayedUpdate\n");
679 if (!UpdateDelayed
) {
681 Tk_DoWhenIdle(DelayedUpdate
, (ClientData
) NULL
);
690 if (earthquake_timer_set
) {
691 Tk_DeleteTimerHandler(earthquake_timer_token
);
693 earthquake_timer_set
= 0;
700 MakeSound("city", "Explosion-Low");
701 Eval("UIEarthQuake");
703 if (earthquake_timer_set
) {
704 Tk_DeleteTimerHandler(earthquake_timer_token
);
706 Tk_CreateTimerHandler(earthquake_delay
, (void (*)())StopEarthquake
, (ClientData
) 0);
707 earthquake_timer_set
= 1;
714 if (tk_mainInterp
!= NULL
) {
715 Eval("catch {DoStopMicropolis}");
723 int result
= Tcl_Eval(tk_mainInterp
, buf
, 0, (char **) NULL
);
724 if (result
!= TCL_OK
) {
725 char *errorinfo
= Tcl_GetVar(tk_mainInterp
, "errorInfo",
727 if (errorinfo
== NULL
) errorinfo
= "<no backtrace>";
728 fprintf(stderr
, "Micropolis: error in TCL code: %s\n%s\n",
729 tk_mainInterp
->result
, errorinfo
);
740 tk_mainInterp
= Tcl_CreateExtendedInterp();
743 /* XXX: Figure out Extended TCL */
745 tclAppLongname
= "Wish - Tk Shell";
746 tclAppVersion
= TK_VERSION
;
747 Tcl_ShellEnvInit (interp
, TCLSH_ABORT_STARTUP_ERR
,
749 0, NULL
, /* argv var already set */
750 fileName
== NULL
, /* interactive? */
751 NULL
); /* Standard default file */
754 MainWindow
= Tk_CreateMainWindow(tk_mainInterp
, FirstDisplay
, "Micropolis");
755 if (MainWindow
== NULL
) {
756 fprintf(stderr
, "%s\n", tk_mainInterp
->result
);
757 sim_really_exit(1); // Just sets tkMustExit and ExitReturn
759 Tk_SetClass(MainWindow
, "Tk");
760 Tk_CreateEventHandler(MainWindow
, StructureNotifyMask
,
761 StructureProc
, (ClientData
) NULL
);
762 /* Tk_DoWhenIdle(DelayedMap, (ClientData) NULL); */
764 Tk_GeometryRequest(MainWindow
, 256, 256);
765 border
= Tk_Get3DBorder(tk_mainInterp
, MainWindow
, None
, "gray75");
766 if (border
== NULL
) {
767 Tcl_SetResult(tk_mainInterp
, (char *) NULL
, TCL_STATIC
);
768 Tk_SetWindowBackground(MainWindow
,
769 WhitePixelOfScreen(Tk_Screen(MainWindow
)));
771 Tk_SetBackgroundFromBorder(MainWindow
, border
);
773 XSetForeground(Tk_Display(MainWindow
),
774 DefaultGCOfScreen(Tk_Screen(MainWindow
)),
775 BlackPixelOfScreen(Tk_Screen(MainWindow
)));
779 editor_command_init();
780 graph_command_init();
782 sprite_command_init();
783 sound_command_init();
789 Tcl_CreateCommand(tk_mainInterp
, "piemenu", Tk_PieMenuCmd
,
790 (ClientData
)MainWindow
, (void (*)()) NULL
);
791 Tcl_CreateCommand(tk_mainInterp
, "interval", Tk_IntervalCmd
,
792 (ClientData
)MainWindow
, (void (*)()) NULL
);
796 sprintf(initCmd
, "source %s/micropolis.tcl", ResourceDir
);
797 filename2UNIX(initCmd
);
799 sim_exit(1); // Just sets tkMustExit and ExitReturn
805 buffer
= Tcl_CreateCmdBuf();
808 Tk_CreateFileHandler(0, TK_READABLE
, StdinProc
, (ClientData
) 0);
813 sprintf(buf
, "UIStartMicropolis {%s} {%s} {%s}",
814 HomeDir
, ResourceDir
, HostName
);
816 if (Eval(buf
) != TCL_OK
) {
817 sim_exit(1); // Just sets tkMustExit and ExitReturn
833 if (buffer
!= NULL
) {
834 Tcl_DeleteCmdBuf(buffer
);
837 Tcl_DeleteInterp(tk_mainInterp
);