]> git.zerfleddert.de Git - micropolis/blob - src/sim/w_map.c
correctly resize editor window to be in sync with the mini map
[micropolis] / src / sim / w_map.c
1 /* w_map.c
2 *
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.
8 *
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.
13 *
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/>.
20 *
21 * ADDITIONAL TERMS per GNU GPL Section 7
22 *
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.
28 *
29 * Any propagation or conveyance of this program must include this
30 * copyright notice and these terms.
31 *
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.
36 *
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.
40 *
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
60 * NOT APPLY TO YOU.
61 */
62 #include "sim.h"
63
64
65 Tcl_HashTable MapCmds;
66
67
68 extern Tk_ConfigSpec TileViewConfigSpecs[];
69
70
71 Ink *NewInk();
72
73
74 MapCmdconfigure(VIEW_ARGS)
75 {
76 int result = TCL_OK;
77
78 if (argc == 2) {
79 result = Tk_ConfigureInfo(interp, view->tkwin, TileViewConfigSpecs,
80 (char *) view, (char *) NULL, 0);
81 } else if (argc == 3) {
82 result = Tk_ConfigureInfo(interp, view->tkwin, TileViewConfigSpecs,
83 (char *) view, argv[2], 0);
84 } else {
85 result = ConfigureTileView(interp, view, argc-2, argv+2,
86 TK_CONFIG_ARGV_ONLY);
87 }
88 return result;
89 }
90
91 MapCmdposition(VIEW_ARGS)
92 {
93 if ((argc != 2) && (argc != 4)) {
94 return TCL_ERROR;
95 }
96 if (argc == 4) {
97 if ((Tcl_GetInt(interp, argv[2], &view->w_x) != TCL_OK)
98 || (Tcl_GetInt(interp, argv[3], &view->w_y) != TCL_OK)) {
99 return TCL_ERROR;
100 }
101 }
102 sprintf(interp->result, "%d %d", view->w_x, view->w_y);
103 return TCL_OK;
104 }
105
106 MapCmdsize(VIEW_ARGS)
107 {
108 if ((argc != 2) && (argc != 4)) {
109 return TCL_ERROR;
110 }
111 if (argc == 4) {
112 int w, h;
113
114 if (Tcl_GetInt(interp, argv[2], &w) != TCL_OK) {
115 return TCL_ERROR;
116 }
117 if (Tcl_GetInt(interp, argv[3], &h) != TCL_OK) {
118 return TCL_ERROR;
119 }
120 view->w_width = w;
121 view->w_height = h;
122 }
123 sprintf(interp->result, "%d %d", view->w_width, view->w_height);
124 return TCL_OK;
125 }
126
127 MapCmdMapState(VIEW_ARGS)
128 {
129 int state;
130
131 if ((argc != 2) && (argc != 3)) {
132 return TCL_ERROR;
133 }
134
135 if (argc == 3) {
136 if ((Tcl_GetInt(interp, argv[2], &state) != TCL_OK) ||
137 (state < 0) || (state >= NMAPS)) {
138 return TCL_ERROR;
139 }
140
141 DoSetMapState(view, state); Kick();
142 }
143
144 sprintf(interp->result, "%d", view->map_state);
145 return TCL_OK;
146 }
147
148 MapCmdShowEditors(VIEW_ARGS)
149 {
150 int val;
151
152 if ((argc != 2) && (argc != 3)) {
153 return TCL_ERROR;
154 }
155
156 if (argc == 3) {
157 if (Tcl_GetInt(interp, argv[2], &val) != TCL_OK) {
158 return TCL_ERROR;
159 }
160
161 view->show_editors = val;
162 }
163
164 sprintf(interp->result, "%d", view->show_editors);
165 return TCL_OK;
166 }
167
168 MapCmdPanStart(VIEW_ARGS)
169 {
170 int x, y, left, right, top, bottom, width, height;
171 SimView *ed;
172
173 if (argc != 4) {
174 return TCL_ERROR;
175 }
176
177 if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
178 (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
179 return TCL_ERROR;
180 }
181
182 for (ed = sim->editor; ed != NULL; ed = ed->next) {
183 if ((ed->x != view->x) || (ed->show_me == 0))
184 continue;
185
186 width = ed->w_width;
187 height = ed->w_height;
188 left = ed->pan_x - (width / 2);
189 top = ed->pan_y - (height / 2);
190 right = left + width;
191 bottom = top + height;
192
193 left = left * 3 / 16 - 4;
194 top = top * 3 / 16 - 4;
195 right = right * 3 / 16 + 4;
196 bottom = bottom * 3 / 16 + 4;
197
198 if ((x >= left) && (x <= right) &&
199 (y >= top) && (y <= bottom)) {
200 goto gotit;
201 }
202 }
203
204 gotit:
205 view->last_x = x;
206 view->last_y = y;
207 view->track_info = (char *)ed;
208 return TCL_OK;
209 }
210
211 MapCmdPanTo(VIEW_ARGS)
212 {
213 int x, y, dx, dy;
214 SimView *ed;
215
216 if (argc != 4) {
217 return TCL_ERROR;
218 }
219
220 if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
221 (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
222 return TCL_ERROR;
223 }
224
225 if ((ed = (SimView *)view->track_info) != NULL) {
226 dx = x - view->last_x;
227 dy = y - view->last_y;
228 if (dx || dy) {
229 view->last_x = x;
230 view->last_y = y;
231 dx = dx * 16 / 3;
232 dy = dy * 16 / 3;
233
234 ed->skip = 0;
235 DoPanBy(ed, dx, dy); Kick();
236 }
237 }
238 return TCL_OK;
239 }
240
241 MapCmdVisible(VIEW_ARGS)
242 {
243 int visible;
244
245 if ((argc != 2) && (argc != 3)) {
246 return TCL_ERROR;
247 }
248
249 if (argc == 3) {
250 if ((Tcl_GetInt(interp, argv[2], &visible) != TCL_OK) ||
251 (visible < 0) || (visible > 1)) {
252 return TCL_ERROR;
253 }
254
255 visible = visible && Tk_IsMapped(view->tkwin);
256 view->visible = visible;
257 }
258
259 sprintf(interp->result, "%d", view->visible);
260 return TCL_OK;
261 }
262
263 MapCmdViewAt(VIEW_ARGS)
264 {
265 int x, y;
266
267 if (argc != 4) {
268 return TCL_ERROR;
269 }
270
271 if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
272 (x < 0) || (x >= WORLD_X) ||
273 (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) ||
274 (y < 0) || (y >= WORLD_Y)) {
275 return TCL_ERROR;
276 }
277
278 sprintf(interp->result, "Sorry Not Implemented Yet"); /* XXX */
279 return TCL_OK;
280 }
281
282
283 map_command_init()
284 {
285 int new;
286 extern int TileViewCmd(CLIENT_ARGS);
287
288 Tcl_CreateCommand(tk_mainInterp, "mapview", TileViewCmd,
289 (ClientData)MainWindow, (void (*)()) NULL);
290
291 Tcl_InitHashTable(&MapCmds, TCL_STRING_KEYS);
292
293 #define MAP_CMD(name) HASHED_CMD(Map, name)
294
295 MAP_CMD(configure);
296 MAP_CMD(position);
297 MAP_CMD(size);
298 MAP_CMD(MapState);
299 MAP_CMD(ShowEditors);
300 MAP_CMD(PanStart);
301 MAP_CMD(PanTo);
302 MAP_CMD(Visible);
303 MAP_CMD(ViewAt);
304 }
305
306
307 int
308 DoMapCmd(CLIENT_ARGS)
309 {
310 SimView *view = (SimView *) clientData;
311 Tcl_HashEntry *ent;
312 int result = TCL_OK;
313 int (*cmd)();
314
315 if (argc < 2) {
316 return TCL_ERROR;
317 }
318
319 if (ent = Tcl_FindHashEntry(&MapCmds, argv[1])) {
320 cmd = (int (*)())ent->clientData;
321 Tk_Preserve((ClientData) view);
322 result = cmd(view, interp, argc, argv);
323 Tk_Release((ClientData) view);
324 } else {
325 Tcl_AppendResult(interp, "unknown command name: \"",
326 argv[0], " ", argv[1], "\".", (char *) NULL);
327 result = TCL_ERROR;
328 }
329 return result;
330 }
331
332
333 /*************************************************************************/
334
335 DoNewMap(SimView *view)
336 {
337 sim->maps++; view->next = sim->map; sim->map = view;
338 /* NewMap = 1; */
339 view->invalid = 1;
340 }
341
342
343 int DoUpdateMap(SimView *view)
344 {
345 int dx, dy, i;
346
347 view->updates++;
348
349 // fprintf(stderr, "UpdateMaps sim_skips %d skips %d skip %d visible %d\n", sim_skips, view->skips, view->skip, view->visible);
350
351 if (!view->visible) {
352 return 0;
353 }
354
355 if ((!ShakeNow) &&
356 (!view->update) &&
357 (sim_skips ||
358 view->skips)) {
359 if (sim_skips) {
360 if (sim_skip > 0) {
361 return 0;
362 }
363 } else {
364 if (view->skip > 0) {
365 --view->skip;
366 return 0;
367 } else {
368 view->skip = view->skips;
369 }
370 }
371 }
372
373 view->update = 0;
374 view->skip = 0;
375
376 // view->invalid = 1;
377
378 if (view->invalid || NewMap || ShakeNow) {
379
380 view->invalid = 0;
381
382 switch (view->type) {
383
384 case X_Mem_View:
385 MemDrawMap(view);
386 break;
387
388 case X_Wire_View:
389 WireDrawMap(view);
390 break;
391 }
392
393 }
394
395 /* XXX: don't do this stuff if just redrawing overlay */
396
397 for (dx = dy = i = 0; i < ShakeNow; i++) {
398 dx += Rand(16) - 8;
399 dy += Rand(16) - 8;
400 }
401
402 XCopyArea(view->x->dpy, view->pixmap, view->pixmap2, view->x->gc,
403 dx, dy, view->w_width, view->w_height, 0, 0);
404 DrawMapInk(view);
405
406 /* XXX: do this if just redrawing overlay */
407
408 XCopyArea(view->x->dpy, view->pixmap2,
409 Tk_WindowId(view->tkwin), view->x->gc,
410 0, 0, view->w_width, view->w_height, 0, 0);
411
412 if (view->show_editors) {
413 DrawMapEditorViews(view);
414 }
415
416 return 1;
417 }
418
419
420 DrawMapEditorViews(SimView *view)
421 {
422 Pixmap pm = Tk_WindowId(view->tkwin);
423 struct SimView *ed;
424 int left, right, top, bottom, width, height;
425 int mine;
426
427 XSetLineAttributes(view->x->dpy, view->x->gc, 1,
428 LineSolid, CapButt, JoinBevel);
429
430 for (ed = sim->editor; ed != NULL; ed = ed->next) {
431 if ((ed->x != view->x) || (ed->show_me == 0))
432 continue;
433
434 width = ed->w_width;
435 height = ed->w_height;
436 left = ed->pan_x - (width / 2);
437 top = ed->pan_y - (height / 2);
438 right = left + width;
439 bottom = top + height;
440
441 left = left * 3 / 16;
442 top = top * 3 / 16;
443 right = right * 3 / 16;
444 bottom = bottom * 3 / 16;
445 width = right - left;
446 height = bottom - top;
447
448 XSetForeground(view->x->dpy, view->x->gc,
449 view->pixels[COLOR_WHITE]);
450 XDrawRectangle(view->x->dpy, pm, view->x->gc,
451 left - 3, top - 3, width + 3, height + 3);
452
453 XSetForeground(view->x->dpy, view->x->gc,
454 view->pixels[COLOR_BLACK]);
455 XDrawRectangle(view->x->dpy, pm, view->x->gc,
456 left - 1, top - 1, width + 3, height + 3);
457
458 XSetForeground(view->x->dpy, view->x->gc,
459 view->pixels[COLOR_YELLOW]);
460 XDrawRectangle(view->x->dpy, pm, view->x->gc,
461 left - 2, top - 2, width + 3, height + 3);
462 }
463 }
464
465
466 /*
467 * Sending the whole image is 108108 bytes.
468 * Sending points is 4.4 bytes per point.
469 * One image is as big as 24570 points.
470 * But we have to sort these dang things.
471 */
472
473 #define MAX_PIX 256
474 int max_pix = MAX_PIX;
475
476 struct Pix {
477 long color;
478 short x, y;
479 };
480
481 struct Pix pix[MAX_PIX];
482
483
484 CompareColor(struct Pix *p1, struct Pix *p2)
485 {
486 register char c1 = p1->color, c2 = p2->color;
487
488 if (c1 == c2)
489 return (0);
490 return ((c1 < c2) ? -1 : 1);
491 }
492
493
494 WireDrawMap(SimView *view)
495 {
496 int different, x, y, i, last, pts;
497 unsigned char *old, *new;
498 XPoint *points;
499
500 if (!view->x->color) {
501 MemDrawMap(view);
502 return;
503 }
504
505 memcpy(view->other_data, view->data, view->line_bytes * view->m_height); /* XXX: handle depth */
506 MemDrawMap(view);
507
508 old = view->other_data; new = view->data; /* XXX: handle depth */
509 different = 0;
510
511 /* Scan the pixels that have changed */
512 for (y = 0; y < view->m_height; y++) {
513 for (x = 0; x < view->m_width; x++) {
514 if (old[x] != new[x]) {
515 if (different >= max_pix) {
516 /* Wow, lots of the pixels have changed.
517 Maybe we ought to just do it the hard way. */
518 XPutImage(view->x->dpy, view->pixmap, view->x->gc, view->image,
519 0, 0, 0, 0, view->m_width, view->m_height);
520 return;
521 }
522 pix[different].color = new[x];
523 pix[different].x = x;
524 pix[different].y = y;
525 different++;
526 }
527 }
528 old += view->line_bytes; new += view->line_bytes; /* XXX: handle depth */
529 }
530
531 /* Whew, the images are identical! */
532 if (different == 0)
533 return;
534
535 #if 1
536
537 /* Always draw the whole pixmap, for now. */
538 XPutImage(view->x->dpy, view->pixmap, view->x->gc, view->image,
539 0, 0, 0, 0, view->m_width, view->m_height);
540
541 #else
542
543 /* TODO: Fix this. I disabled this incremental drawing code for now since it seems to be buggy. */
544
545 /* Sort the changed pixels by their color */
546 qsort(pix, different, sizeof (struct Pix), (int (*)())CompareColor);
547
548 /* Draw the points of each color that have changed */
549 points = (XPoint *)malloc(sizeof (XPoint) * different);
550 last = 0; pts = 0;
551 for (i = 0; i <= different; i++) {
552 if ((i == different) ||
553 (pix[i].color != pix[last].color)) {
554 XSetForeground(view->x->dpy, view->x->gc, pix[last].color);
555 XDrawPoints(view->x->dpy, view->pixmap, view->x->gc,
556 points, pts, CoordModeOrigin);
557 if (i == different)
558 break;
559 pts = 0;
560 last = i;
561 }
562 points[pts].x = pix[i].x;
563 points[pts].y = pix[i].y;
564 pts++;
565 }
566 free(points);
567
568 #endif
569
570 }
571
572
573 DrawMapInk(SimView *view)
574 {
575 Pixmap pm = view->pixmap2;
576 SimView *v;
577 Ink *ink, *ink2 = NewInk();
578 int i, X, Y, x, y;
579
580 XSetLineAttributes(view->x->dpy, view->x->gc, 0,
581 LineSolid, CapButt, JoinBevel);
582
583 for (ink = sim->overlay; ink != NULL; ink = ink->next) {
584 X = ink->x; x = (X * 3) >>4;
585 Y = ink->y; y = (Y * 3) >>4;
586
587 if (ink->length <= 1) {
588 XSetForeground(view->x->dpy, view->x->gc,
589 view->pixels[ink->color]);
590 XFillArc(view->x->dpy, pm, view->x->gc,
591 ink->x - 1, ink->y - 1, 1, 1, 0, 360 * 64);
592 } else {
593 StartInk(ink2, x, y);
594
595 for (i = 1; i < ink->length; i++) {
596 X += ink->points[i].x; x = (X * 3) >>4;
597 Y += ink->points[i].y; y = (Y * 3) >>4;
598 AddInk(ink2, x, y);
599 }
600
601 XSetForeground(view->x->dpy, view->x->gc,
602 view->pixels[ink->color]);
603 XDrawLines(view->x->dpy, pm, view->x->gc,
604 ink2->points, ink2->length, CoordModePrevious);
605 }
606 }
607
608 FreeInk(ink2);
609 }
610
611
Impressum, Datenschutz