From mipos3!intelca!amd!amdcad!ames!sri-spam!rutgers!mit-eddie!uw-beaver!tektronix!tekgen!tekred!games-request Thu Jun 18 10:10:57 PDT 1987 Article 2 of comp.sources.games: Path: td2cad!mipos3!intelca!amd!amdcad!ames!sri-spam!rutgers!mit-eddie!uw-beaver!tektronix!tekgen!tekred!games-request From: games-request@tekred.TEK.COM Newsgroups: comp.sources.games Subject: v01i055: sdi - missle command game for Suns, Part02/06 Message-ID: <1314@tekred.TEK.COM> Date: 17 Jun 87 18:31:38 GMT Sender: billr@tekred.TEK.COM Lines: 1934 Approved: billr@tekred.TEK.COM Submitted by: Mark Weiser Comp.sources.games: Volume 1, Issue 55 Archive-name: sdi/Part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh TODO <<'END_OF_TODO' X1. use rops into a single pixel for more efficient work in melting? X2. turn off clipping for all rops to run faster? (perhaps use a big canvas X under the window, so all blasts, etc. will fit in the canvas even X if not in the window). X4. Originally the idea was for this to be a networked game, with X one person shooting missiles and the other blasting them down. Do this. X7. warp all popups to fit on screen. X8. make the different kinds of blast be pretty in color. X11. have little launching sites that open up. X12. have the save file save more things, like the city icon used. X16. Have an ifdef to eliminate pies. X17. Have a .sdirc, which points to a save_game file. X18. Mirv at other times than the top of the city window. X22. fix scores. END_OF_TODO if test 748 -ne `wc -c circles.c <<'END_OF_circles.c' X/********************************* circles.c ************************/ X#include X#include X#include "sdi.h" X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* X * Here be everything associated with drawing circles, especially X * all the different kinds of blasts (which are done by clipping X * different pixrects to different shapes of circles.) All the X * blast pixrects are precomputed for every size to save time during X * game display updating. X */ X Xstatic short default_laser_blast[] = { X#include "laser.h" X}; Xmpr_static(laser_blast_pr, 64, 64, 1, default_laser_blast); X Xstatic short default_laser_kill[] = { X#include "laserkill.h" X}; Xmpr_static(laser_kill_pr, 64, 64, 1, default_laser_kill); X X Xstatic short default_city_blast[] = { X#include "mushroom.h" X}; Xmpr_static(city_blast_pr, 64, 64, 1, default_city_blast); X Xstatic short default_missile_kill[] = { X#include "missilekill.h" X}; Xmpr_static(missile_kill_pr, 64, 64, 1, default_missile_kill); X Xstatic short default_rocks[] = { X#include "rocks.h" X}; Xmpr_static(rocks_pr, 64, 64, 1, default_rocks); X Xstruct pixrect *circles[MAX_NUM_CIRCLES]; Xstruct pixrect *limited_circles[MAX_NUM_CIRCLES]; Xstruct pixrect *rock_limited_circles[MAX_NUM_CIRCLES]; X Xstatic struct { X int x, y; X } center = { 0, 0 }; X Xstatic struct fill_line { X int left; X int right; X int y; X} scan_lines[MAX_LINES], *free_line; X Xstatic void circle_fill_pair(), circle_fill(); X X X#define LIMIT(n) (((n) < 0) ? 0 : (((n) >= MAX_LINES) ? MAX_LINES-1 : (n))) X Xinit_circles() X{ X int i, size = 2*CIRCLE_SIZE_INC; X int limited_size = (MAX_NUM_CIRCLES-4)*CIRCLE_SIZE_INC + size; X int rock_limited_size = 2*CIRCLE_SIZE_INC + size; X for (i=0; i < MAX_NUM_CIRCLES; i++) { X if (size > MAX_CIRCLE) X size = MAX_CIRCLE; X circles[i] = make_circle(size); X limited_circles[i] = make_circle(size > limited_size ? limited_size : size); X rock_limited_circles[i] = make_circle(size > rock_limited_size ? rock_limited_size : size); X size += CIRCLE_SIZE_INC; X } X blankcircles = circles; X lasercircles = init_circ(3, circles); X laserkillcircles = init_circ(3, circles); X bigblastcircles = init_circ(MAX_NUM_CIRCLES, circles); X littleblastcircles = init_circ(MAX_NUM_CIRCLES-2, limited_circles); X blastkillcircles = init_circ(MAX_NUM_CIRCLES-3, circles); X citykillcircles = init_circ(MAX_NUM_CIRCLES, circles); X littlerockcircles = init_circ(4, rock_limited_circles); X bigrockcircles = init_circ(7, rock_limited_circles); X change_circ(lasercircles, &laser_blast_pr); X change_circ(laserkillcircles, &laser_kill_pr); X change_circ(citykillcircles, &city_blast_pr); X change_circ(blastkillcircles, &missile_kill_pr); X change_circ(littlerockcircles, &rocks_pr); X change_circ(bigrockcircles, &rocks_pr); X} X X/* X * Create a new circ structure, and initialize it to contain the pixrect X * list 'c'. X */ Xstruct circ * Xinit_circ(n, c) Xstruct pixrect **c; X{ X struct circ *r; X r = (struct circ *)calloc(sizeof(struct circ), 1); X r->masks = r->circles = c; X r->num_circles = n; X return r; X} X X/* X * Routine to replace a list of pixrects in a circ structure with a new X * list. The new list is constructed by 'and'ing parameter p with X * circles of the diameter contained in the old list. This is the X * basic routine used to construct all the different kinds of blasts. X * X * Calling change_circ several times on the same circ structure X * will waste memory because the calloc from previous calls is X * never freed, nor are the pixrects. It is not much memory, and nothing will die from it. X */ Xchange_circ(c, p) Xstruct circ *c; Xstruct pixrect *p; X{ X int i; X struct pixrect **newcircles; X int size, psize; X newcircles = (struct pixrect **)calloc(sizeof(struct pixrect *),c->num_circles); X psize = p->pr_size.x; X for (i = 0; i < c->num_circles; i++) { X size = c->masks[i]->pr_size.x; X newcircles[i] = mem_create(size, size, 1); X pr_rop(newcircles[i], 0, 0, size, size, PIX_SRC, c->masks[i], X 0, 0); X pr_rop(newcircles[i], 0, 0, size, size, PIX_SRC & PIX_DST, X p, psize/2 - size/2, psize/2 - size/2); X } X c->circles = newcircles; X X} X X/* X * Build a black circle of the specified diameter, and return it in X * a pixrect of just the right size to hold it. X */ Xstruct pixrect * Xmake_circle(diameter) Xint diameter; X{ X struct pixrect *pr = mem_create(diameter, diameter, 1); X center.x = diameter/2; X center.y = diameter/2; X free_line = scan_lines; X circle_accept(pr, LIMIT((diameter/2)-2)); X return pr; X} X X/* X * The routines below borrow ideas from the Sun Bresenham X * circle code in iconedit. X */ X X/* X * Draw a circle inside pr, with length as given here, and with center as X * set in global variable 'center'. X */ Xcircle_accept(pr, length) Xstruct pixrect *pr; X{ X int d, x, y; X X x = 0; X y = length; X d = 3 - 2*y; X X while ( x < y ) { X circle_fill_pair(pr, x, y); X if (d < 0) X d += 4*x + 6; X else { X d += 4*(x-y) +10; X y -= 1; X } X x++; X } X if (x == y) { X circle_fill_pair(pr, x, y); X } X circle_fill(pr); X} X Xstatic void Xcircle_fill_pair(pr, x, y) Xstruct pixrect *pr; Xregister int x, y; X{ X register struct fill_line *line_ptr = free_line; X X free_line += 4; X X line_ptr->left = LIMIT(center.x - x); X line_ptr->right = LIMIT(center.x + x); X line_ptr->y = LIMIT(center.y - y); X X ++line_ptr; X line_ptr->left = LIMIT(center.x - y); X line_ptr->right = LIMIT(center.x + y); X line_ptr->y = LIMIT(center.y - x); X X ++line_ptr; X line_ptr->left = LIMIT(center.x - y); X line_ptr->right = LIMIT(center.x + y); X line_ptr->y = LIMIT(center.y + x); X X ++line_ptr; X line_ptr->left = LIMIT(center.x - x); X line_ptr->right = LIMIT(center.x + x); X line_ptr->y = LIMIT(center.y + y); X} X Xstatic int Xcompare_lines(l1, l2) X register struct fill_line *l1, *l2; X{ X if (l1->y < l2->y) X return -1; X else if (l1->y > l2->y) X return 1; X else if (l1->left < l2->left || l1->right > l2->right) X return -1; X else if (l1->left > l2->left || l1->right < l2->right) X return 1; X else return 0; X} X Xstatic void Xcircle_fill(pr) Xstruct pixrect *pr; X{ X register struct fill_line *line_ptr = scan_lines; X register int len, y = -1; X X qsort(scan_lines, free_line - scan_lines, X sizeof(struct fill_line), compare_lines); X while (line_ptr < free_line) { X register int x0; X X if (line_ptr->y != y) { X x0 = line_ptr->left; X y = line_ptr->y; X len = line_ptr->right - x0 + 1; X pr_vector(pr, x0, y, x0+len, y, PIX_SET, 1); X } X line_ptr++; X } X} END_OF_circles.c if test 6673 -ne `wc -c cities.c <<'END_OF_cities.c' X/********************************** cities.c ************************/ X#include X#include X#include X#include "sdi.h" X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* X * Code to do things to cities. Much of the code here consists of X * passing a routine into doto_cities, which then calls that routine X * on each city. See the comment for doto_cities (near the end of the file) X * for more information. X */ X Xstatic short default_city_data[] = { X#include "default_city.h" X}; Xmpr_static(default_city_pr, 64, 64, 1, default_city_data); X Xstatic struct pixrect *city_pr_ptr = &default_city_pr; Xstatic colormap_t city_colormap, *city_colormap_ptr = NULL; X Xstatic short melted_city_data[] = { X#include "melt.h" X}; Xmpr_static(melted_city_pr, 64, 64, 1, melted_city_data); X X X#define MIN_SPACE 4 X#define MAX_CITIES 40 X Xstatic int city_space, excess, global_count; Xstatic long bits_in_city; X Xstatic int do_update(), do_count(), do_placement(), do_kill(), X do_update_all(), do_grow(), do_update_cities(), do_melt_all(); X Xstatic int cities_inited = 0; Xstatic short cities[MAX_CITIES]; X X Xstatic int growcount; /* ugh, another weird global. X * set by do_update_cities, used only by 'do_grow'. X */ X X/* X * Read a new city pixrect from a file. X */ Xinit_city_bits(filename) Xchar *filename; X{ X /* this routine is only called if there is non-default city. */ X if (filename != NULL) { X char error_msg[256]; X struct pixrect *tmp,*icon_load_mpr(); X if ((tmp = icon_load_mpr(filename, error_msg)) == NULL) { X printf("Could not get pr '%s'.\n", filename); X printf("%s",error_msg); X printf("Using default cities.\n"); X } else { X city_pr_ptr = tmp; X } X } X} X X/* X * Compute the positions of all the cities based on current screen size, X * and declare all the cities to be unmelted. X */ Xinit_cities() X{ X int i, old_num_cities = num_cities; X bits_in_city = count_bits(city_pr_ptr); X num_cities = ((max_x - MARGIN)/64) + 1; /* bigger than possible */ X city_space = 0; X X while (city_space < MIN_SPACE) { X num_cities -= 1; X city_space = ((max_x - MARGIN) - (num_cities * 64))/(num_cities-1); X }; X X excess = max_x - (MARGIN + 64*num_cities + city_space*(num_cities-1)); X for (i=old_num_cities; iinput.c <<'END_OF_input.c' X/************************************ input.c *************************/ X#include X#include X#include X#include X#include X#include "sdi.h" X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* X * interrupt handling is here, including mousing, resizing, etc. X */ X Xextern Panel launchpanel; X XNotify_value update_icon(); X Xvoid Xcanvas_resize_proc(canvas, width, height) XCanvas canvas; Xint width, height; X{ X void new_game_proc(); X void draw_background(); X struct pixwin *pw, *newpw;; X X if (canvas == citycanvas || canvas == launchcanvas) { X max_x = width-(2*FIELD_MARGIN)-4; X max_y = height-(2*FIELD_MARGIN)-4; X newpw = pw_region(canvas_pixwin(canvas), FIELD_MARGIN, FIELD_MARGIN, X max_x, max_y); X } X /* X * Can't do the pw_closes here, because lurking missiles may still X * be depending on them. So never do them--how many resizes are X * there per game, anyway? X */ X if (canvas == citycanvas) { X /* pw_close(citypw); */ X citypw = newpw; X num_cities = 0; X init_cities(); X } else if (canvas == launchcanvas) { X /* pw_close(launchpw); */ X launchpw = newpw; X } X} Xint running_icon_pictures = 0; Xstatic int old_frame_width = -1, old_frame_height = -1; X X/* X * This routine gets notifications even before SunView does. Almost the first X * thing it does is all 'notify_next_event_func', which lets SunView do X * its work. Resizes, opens, and closes are handled here. X */ XNotify_value Xsynch_event_proc(frame, event, arg, type) XFrame frame; XEvent *event; XNotify_arg arg; XNotify_event_type type; X{ X int new_game; X int closed; X static int oldclosed = 0; X Notify_value value; X X /* send on the notification */ X value = notify_next_event_func(frame, event, arg, type); X X /* start post-processing */ X closed = (int) window_get(frame, FRAME_CLOSED); X if (frame == controlframe) { X if ( closed && !oldclosed) { X /* not many events come in while closed, X * so if we are closed, we are probably X * just now closing. Close everyone. X */ X window_set(cityframe, WIN_SHOW, FALSE, X 0); X window_set(launchframe, WIN_SHOW, FALSE, X 0); X if (running) X suspend_proc(); X if (!running_icon_pictures) { X /* start the icon stuff */ X running_icon_pictures = 1; X do_with_delay(update_icon, 0, 1); X } X } else if (!closed) { X /* opening control frame, and so show everybody */ X if (! window_get(cityframe, WIN_SHOW)) X window_set(cityframe, WIN_SHOW, TRUE, 0); X if (! window_get(launchframe, WIN_SHOW)) X window_set(launchframe, WIN_SHOW, TRUE, 0); X if (running_icon_pictures) { X /* stop the icon stuff */ X running_icon_pictures = 0; X } X } X oldclosed = closed; X } X /* Check for a resize request. X If either playing field is resized they both are. X */ X if (event_id(event) == WIN_RESIZE && X (frame == cityframe || frame == launchframe)) { X int w = (int)window_get(frame, WIN_WIDTH); X int h = (int)window_get(frame, WIN_HEIGHT); X if ((old_frame_height == h && old_frame_width == w)) X goto done; X if ((old_frame_height == -1 && old_frame_width == -1) X || (!running) X || popup_warning(frame, event, "Ok to restart game?")) { X new_game = 1; X old_frame_height = h; X old_frame_width = w; X } else { X new_game = 0; X h = old_frame_height; X w = old_frame_width; X } X /* can't get too small, or else things start to break. */ X h = max(MINWIN, h); X w = max(MINWIN, w); X window_set(cityframe, WIN_WIDTH, w, WIN_HEIGHT, h, 0); X window_set(launchframe, WIN_WIDTH, w, WIN_HEIGHT, h, 0); X if (new_game) { X /* X * If resizing, it must be time to start a new game. X */ X do_with_delay(new_game_proc, 0, 100000); X } X } Xdone: X return value; X} X X/* X * Handle mousing. X */ X#define PINX(x) (max(0,min(max_x,x))) X#define PINY(y) (max(0,min(max_y,y))) X Xstatic int rock_x = -1, rock_y; XEvent rock_down_event; X Xvoid Xmain_event_proc(window, event, arg) XWindow window; XEvent *event; Xcaddr_t arg; X{ X extern Panel_item rock_item; X int times_around, offset, i; X int id = event_id(event); X int inc, left; X Pixwin *pw; X X /* X * The following totally silly call is a kludge to work-around X * a Sun bug which, if we *don't* refuse the input focus, loses the X * middle-button down transition when we first are handed the input focus. X */ X if (event_id(event) == KBD_REQUEST) { X window_refuse_kbd_focus(window); X return; X } X X if (! event_is_button(event)) return; X if (suspended) return; X X if (window == citycanvas) { X pw = citypw; X } else { X pw = launchpw; X } X X if (event_is_down(event)) { X if (!running) { X /* if not running, then try to start running. */ X start_next_round(); X } else { X switch(id) { X case MS_LEFT: { X left = (int)panel_get_value(interceptor_item); X if (left > 0) { X if (event_meta_is_down(event)) { X times_around = 2; X offset = event_shift_is_down(event) ? -31 : -20; X } else { X times_around = 1; X offset = 0; X } X for (i = 0; i < times_around; i += 1) { X if (event_shift_is_down(event)) { X start_blast(PINX(event_x(event)+offset), event_y(event), 0, 0, pw, bigblastcircles); X } else { X start_blast(PINX(event_x(event)+offset), event_y(event), 0, 0, pw, littleblastcircles); X } X offset = ABS(offset); X } X panel_set_value(interceptor_item, left-times_around); X update_cursor(); X } else { X need_a_bell = pw; X } X break; X } X case MS_RIGHT: { X left = (int)panel_get_value(laser_item); X if (left > 0) { X if (event_meta_is_down(event)) { X times_around = 2; X offset = event_shift_is_down(event) ? -100 : -64; X } else { X times_around = 1; X offset = 0; X } X for (i = 0; i < times_around; i += 1) { X if (event_shift_is_down(event)) { X start_laser(PINX(event_x(event)+offset), event_y(event), X pw, 3, 256); X } else { X start_laser(PINX(event_x(event)+offset), event_y(event), X pw, 6, 128); X } X offset = ABS(offset); X } X panel_set_value(laser_item, left-times_around); X update_cursor(); X } else { X need_a_bell = pw; X } X break; X } X case MS_MIDDLE: { X left = (int)panel_get_value(rock_item); X if (left > 0) { X rock_x = event_x(event); X rock_y = event_y(event); X rock_down_event = *event; X } else { X rock_x = -1; X need_a_bell = pw; X } X }} /* end of switch */ X } X } else if (/* implicit: event_is_up(event) && */ X running && id == MS_MIDDLE && rock_x != -1) { X /* throw some rocks */ X if (event_meta_is_down(&rock_down_event)) { X times_around = 2; X offset = -20; X } else { X times_around = 1; X offset = 0; X } X for (i = 0; i < times_around; i += 1) { X if (event_shift_is_down(&rock_down_event)) { X start_rocks(pw, rock_x, PINY(rock_y+offset), X event_x(event), PINY(event_y(event)+offset), X 3, bigrockcircles); X } else { X start_rocks(pw, rock_x, PINY(rock_y+offset), X event_x(event)+offset, PINY(event_y(event)+offset), X 5, littlerockcircles); X } X offset = ABS(offset); X } X rock_x = -1; X panel_set_value(rock_item, panel_get_value(rock_item)-times_around); X update_cursor(); X } X} END_OF_input.c if test 7435 -ne `wc -c main.c <<'END_OF_main.c' X/************************************ main.c *************************/ X#include "sdi.h" X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* X * A big, long, initialization routine. X */ X Xstatic short cursor_data[] = { X#include "cursor.h" X}; Xmpr_static(cursor_pr, 16, 16, 1, cursor_data); X Xstatic short icon_image[] = { X#include "city_icon1.h" X}; XDEFINE_ICON_FROM_IMAGE(icon, icon_image); X Xstatic short i_pic_array[] = { X#include "incoming_picture.h" X}; Xmpr_static(incoming_pic, 16, 16, 1, i_pic_array); X Xstatic short g_pic_array[] = { X#include "foe_ground_picture.h" X}; Xmpr_static(foe_ground_pic, 16, 16, 1, g_pic_array); X Xstatic void done_proc(); Xextern void main_event_proc(), canvas_resize_proc(); Xextern Notify_value canvas_input_proc(), asynch_event_proc(), synch_event_proc(); XNotify_value scheduler(), input_notify(); X X#define FONT_NAME "/usr/lib/fonts/fixedwidthfonts/serif.r.14" X Xstatic char tenblanks[] = " "; X XPanel launchpanel; X X/* X * In need of no comment: X */ Xmain(argc, argv) Xchar **argv; X{ X extern char *version; X Menu menu, null_menu_gen(); X char tmp_string[128]; X Panel controlpanel; X struct timeval tp; X char *s; X X /* randomize us */ X gettimeofday(&tp, 0); X srandom(tp.tv_sec); X X /* init score file from environment, if possible */ X if ((s = (char *)getenv("SDI_SCORES")) != NULL) X scorefile = s; X X init_circles(); X X init_city_bits(NULL); X X fixup_font(&argc, &argv, FONT_NAME); X X open_our_font(FONT_NAME); X sprintf(tmp_string, " SDI Control Panel%s%s%s%s%sby mark weiser", X tenblanks,tenblanks,tenblanks,tenblanks,tenblanks); X X s = (char *)get_name(); X if (s && s[0] != '\0') X strcpy(user_name, s); X X /* no background pre-write to our icon--otherwise we flicker. */ X icon.ic_flags = 0; X X /* make the control window, which serves as the base for all others. */ X controlframe = window_create(NULL, FRAME, X FRAME_ARGC_PTR_ARGV, &argc, argv, X FRAME_LABEL, tmp_string, X FRAME_ICON, &icon, X WIN_ERROR_MSG, "Can't create window.", X WIN_FONT, font, X WIN_X, 0, WIN_Y, 0, /* start at zero, move later. */ X 0); X X process_args(argc, argv); X X init_icons(); X X build_playing_fields(); X X controlpanel = window_create(controlframe, PANEL, X WIN_VERTICAL_SCROLLBAR, scrollbar_create(0), /* but provide a loophole */ X PANEL_LABEL_FONT, font, X PANEL_VALUE_FONT, font, X WIN_FONT, font, X WIN_ERROR_MSG, "Can't create window.", X /* magic numbers which seem to look good: */ X WIN_WIDTH, 730, X PANEL_ITEM_X_GAP, 30, X PANEL_ITEM_Y_GAP, 15, X 0); X init_control(controlpanel); X window_fit(controlpanel); X window_fit(controlframe); X X city_fd = (int)window_get(citycanvas, WIN_FD); X launch_fd = (int)window_get(launchcanvas, WIN_FD); X X max_x = (int)window_get(citycanvas, CANVAS_WIDTH); X max_y = (int)window_get(citycanvas, CANVAS_HEIGHT); X X { /* little block for a bunch of little variables */ X struct screen screen; X int playwidth = (int)window_get(cityframe, WIN_WIDTH); X int controlwidth = (int)window_get(controlframe, WIN_WIDTH); X int controlheight = (int)window_get(controlframe, WIN_HEIGHT); X int center; X win_screenget(city_fd, &screen); X center = screen.scr_rect.r_width/2; X X /* put the playing frames into position (control is at 0,0) */ X window_set(cityframe, WIN_X, center - playwidth, 0); X window_set(launchframe, WIN_X, center, 0); X X /* center the control frame */ X window_set(controlframe, WIN_X, center - controlwidth/2, X /* magic number which seems to look good: */ X WIN_Y, 200, 0); X X /* put the playing frames below it. */ X window_set(cityframe, WIN_Y, controlheight, 0); X window_set(launchframe, WIN_Y, controlheight, 0); X } /* end of little block */ X X /* X * menus in the playing fields are a distraction, but X * they can't be just menu-destroyed, because that leaves a X * dangling pointer inside sunview (and a core dump). X * So instead, we cleverly(?) modify the menu so that it will X * generate an empty contents. X * (Ychhhh!) X */ X menu = window_get(launchframe, WIN_MENU); X menu_set(menu, MENU_GEN_PROC, null_menu_gen, 0); X menu = window_get(cityframe, WIN_MENU); X menu_set(menu, MENU_GEN_PROC, null_menu_gen, 0); X X notify_interpose_event_func(launchframe, synch_event_proc, NOTIFY_SAFE); X notify_interpose_event_func(cityframe, synch_event_proc, NOTIFY_SAFE); X notify_interpose_event_func(controlframe, synch_event_proc, NOTIFY_SAFE); X X notify_set_scheduler_func(scheduler); X X notify_set_input_func(citycanvas, input_notify, city_fd); X notify_set_input_func(launchcanvas, input_notify, launch_fd); X X init_cursor(); X update_cursor(); X X window_main_loop(controlframe); X exit(0); X} X Xstatic void Xdone_proc(frame) XFrame frame; X{ X /* do nothing */ X} X Xbuild_playing_fields() X{ X Panel citypanel; X extern Panel_item foe_ground_item; X X cityframe = window_create(controlframe, FRAME, X FRAME_LABEL, " SDI Friend Cities", X FRAME_SHOW_LABEL, TRUE, X FRAME_DONE_PROC, done_proc, X WIN_ERROR_MSG, "Can't create window.", X WIN_FONT, font, X FRAME_SUBWINDOWS_ADJUSTABLE, FALSE, X 0); X X citypanel = window_create(cityframe, PANEL, 0); X ballistic_item = panel_create_item(citypanel, PANEL_SLIDER, X ATTR_LIST, panel_common, X PANEL_LABEL_STRING, "Incoming missiles: ", X 0); X window_fit_height(citypanel); X X citycanvas = window_create(cityframe, CANVAS, X WIN_CONSUME_PICK_EVENTS, WIN_UP_EVENTS, 0, X WIN_EVENT_PROC, main_event_proc, X WIN_CURSOR, cursor_create(CURSOR_IMAGE, &cursor_pr, X CURSOR_XHOT, 8, CURSOR_YHOT, 8, X CURSOR_OP, PIX_SRC ^ PIX_DST, X 0), X WIN_WIDTH, max_x+(2*FIELD_MARGIN), WIN_HEIGHT, max_y+(2*FIELD_MARGIN), X CANVAS_RESIZE_PROC, canvas_resize_proc, X CANVAS_RETAINED, TRUE, /* need retained for city computations */ X WIN_ERROR_MSG, "Can't create window.", X CANVAS_MARGIN, 0, X 0); X window_fit(cityframe); X citypw = pw_region(canvas_pixwin(citycanvas), FIELD_MARGIN, FIELD_MARGIN, X max_x, max_y); X X launchframe = window_create(controlframe, FRAME, X FRAME_LABEL, " SDI Foe Launch", X FRAME_SHOW_LABEL, TRUE, X FRAME_DONE_PROC, done_proc, X WIN_ERROR_MSG, "Can't create window.", X WIN_FONT, font, X FRAME_SUBWINDOWS_ADJUSTABLE, FALSE, X 0); X X launchpanel = window_create(launchframe, PANEL, 0); X foe_ground_item = panel_create_item(launchpanel, PANEL_SLIDER, X ATTR_LIST, panel_common, X PANEL_LABEL_STRING, "Missiles on the ground:", X 0); X window_fit_height(launchpanel); X launchcanvas = window_create(launchframe, CANVAS, X WIN_CONSUME_PICK_EVENTS, WIN_UP_EVENTS, 0, X WIN_EVENT_PROC, main_event_proc, X WIN_CURSOR, cursor_create(CURSOR_IMAGE, &cursor_pr, X CURSOR_XHOT, 8, CURSOR_YHOT, 8, X CURSOR_OP, PIX_SRC ^ PIX_DST, X 0), X WIN_WIDTH, max_x+(2*FIELD_MARGIN), WIN_HEIGHT, max_y+(2*FIELD_MARGIN), X CANVAS_RESIZE_PROC, canvas_resize_proc, X CANVAS_RETAINED, TRUE, X WIN_ERROR_MSG, "Can't create window.", X CANVAS_MARGIN, 0, X 0); X X window_fit(launchframe); X X launchpw = pw_region(canvas_pixwin(launchcanvas), FIELD_MARGIN, FIELD_MARGIN, X max_x, max_y); X X/* X * A noble sentiment, but too much trouble to handle resizing correctly. X window_set(launchcanvas, X WIN_HEIGHT, window_get(launchcanvas, WIN_HEIGHT), X WIN_Y, 0, X 0); X window_set(launchpanel, WIN_BELOW, launchcanvas, X 0); X X*/ X X draw_background(); X X} END_OF_main.c if test 7400 -ne `wc -c missile.c <<'END_OF_missile.c' X/********************************** missile.c ***********************/ X#include X#include X#include "sdi.h" X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* X * Code to start and update missiles lives here, including missiles X * traveling between the two windows. Launching of missiles X * is done in incoming.c. X */ X Xstatic struct missile *m_head = NULL; Xstatic Notify_value ballistic_timer(); X X/* X * Throw a missile onto a window. Only x is suppied, because direction X * determines whether the missile starts at the top or bottom of the window. X * Speed is in units of approximate pixels-per-timestep. Direction X * should be UP or DOWN, which are defined in sdi.h X */ Xstart_missile(x, direction, speed, pw) Xint x, direction, speed; XPixwin *pw; X{ X struct missile *mid = (struct missile *)malloc(sizeof(struct missile)); X int ratio, number_of_steps; X int final_x = (random() % (max_x - MARGIN)) + (MARGIN/2); X X panel_set_value(foe_item, panel_get_value(foe_item) + 1); X X mid->start_x = mid->x = x; X mid->speed = speed; X mid->refs = 1; X mid->destroyed = FALSE; X X number_of_steps = max(1,(ABS(final_x - mid->start_x) + max_y)/speed); X mid->inc_x = (final_x - mid->start_x)/number_of_steps; X mid->inc_y = max_y/number_of_steps; X if (mid->inc_y < 1) { X mid->inc_y += 1; X mid->inc_x -= 1; X } X X X /* I'm making this up as I go... */ X { X double desired = (double)(ABS(final_x - mid->start_x)/(double)max_y); X double actual = (double)ABS(mid->inc_x)/(double)ABS(mid->inc_y); X mid->slip = (double)1.0 / (desired - actual); X mid->slip_cnt = 0; X } X X if (direction == DOWN) { X mid->start_y = mid->y = 0; X } else { X mid->start_y = mid->y = max_y; X mid->inc_y = -mid->inc_y; X } X mid->pw = pw; X X inc_missile(mid); X X missile_count++; X mid->next = m_head; X m_head = mid; X} X X/* X * Move a missile ahead and see if hits anything. X * Helper routine passed into doto_missiles. X */ Xupdate_missile(mid) Xstruct missile *mid; X{ X inc_missile(mid); X if (intersect(mid)) { X start_blast(mid->x, mid->y, 0, 0, mid->pw, blastkillcircles); X destroy_missile(mid); X if (mid->pw == citypw) X bump_score(foe_value/5); X else bump_score(foe_value); X } else if (mid->inc_y > 0 && mid->y >= max_y-burst_distance) { X start_blast(mid->x, mid->y - 10, 0, 0, mid->pw, citykillcircles); X destroy_missile(mid); X } else if (mid->inc_y < 0 && mid->y <= 0) { X start_ballistic(mid->x, mid->speed); X destroy_missile(mid); X } else if (mid->x < 0 || mid->x > max_x) { X start_blast(mid->x, mid->y, 0, 0, mid->pw, blastkillcircles); X destroy_missile(mid); X } X return 0; X} X X/* X * Update the missile track. X */ Xinc_missile(mid) Xstruct missile *mid; X{ X /* Compute basic update */ X mid->old_x = mid->x; X mid->old_y = mid->y; X mid->x += mid->inc_x; X mid->y += mid->inc_y; X X /* Adjust skew for straighter lines */ X if (mid->slip && ++mid->slip_cnt >= ABS(mid->slip)) { X mid->slip_cnt = 0; X if (mid->slip > 0) { X mid->x += 1; X } else { X mid->y += 1; X } X } X X /* Draw missile trail */ X pw_vector(mid->pw, mid->old_x-1, mid->old_y, mid->x-1, mid->y, PIX_SRC, 1); X pw_vector(mid->pw, mid->old_x, mid->old_y, mid->x, mid->y, PIX_SRC, 1); X pw_vector(mid->pw, mid->old_x+1, mid->old_y, mid->x+1, mid->y, PIX_SRC, 1); X} X X/* X * Get rid of a missile by erasing its track, removing it from X * the missile display list, and freeing its structure. Explosion X * of the missile is the responsibility of the caller. X */ Xdestroy_missile(mid) Xstruct missile *mid; X{ X char buff[128]; X struct rect r; X X if (!mid->destroyed) { X panel_set_value(foe_item, panel_get_value(foe_item) - 1); X sprintf(buff, "%d", atol(panel_get_value(total_foe_item))+1); X panel_set_value(total_foe_item, buff); X mid->destroyed = TRUE; X pw_vector(mid->pw, mid->start_x - 2, mid->start_y, X mid->x - 2, mid->y, X PIX_NOT(PIX_SRC), 1); X pw_vector(mid->pw, mid->start_x - 1, mid->start_y, X mid->x - 1, mid->y, X PIX_NOT(PIX_SRC), 1); X pw_vector(mid->pw, mid->start_x, mid->start_y, X mid->x, mid->y, X PIX_NOT(PIX_SRC), 1); X pw_vector(mid->pw, mid->start_x + 1, mid->start_y, X mid->x + 1, mid->y, X PIX_NOT(PIX_SRC), 1); X pw_vector(mid->pw, mid->start_x + 2, mid->start_y, X mid->x + 2, mid->y, X PIX_NOT(PIX_SRC), 1); X X if (m_head == mid) { X m_head = mid->next; X } else { X struct missile *tmpmid = m_head; X while (tmpmid != NULL && tmpmid->next != mid) X tmpmid = tmpmid->next; X if (tmpmid != NULL) X tmpmid->next = mid->next; X } X missile_count--; X } X if (--mid->refs == 0) { X free(mid); X } X} X X/* X * Update the score by 'inc', augmented by skill level. X */ Xbump_score(inc) X{ X int score, skill; X float skill_multiplier; X char buf[128]; X skill = (int)panel_get_value(skill_item); X switch (skill) { X case 0: skill_multiplier = 1.0; break; X case 1: skill_multiplier = 1.5; break; X case 2: skill_multiplier = 3; break; X } X score = atol(panel_get_value(score_item)) + (int)(((float)inc)*skill_multiplier); X sprintf(buf,"%d", score); X panel_set_value(score_item, buf); X} X X/* X * Call 'func' for missiles in the display list. If func X * returns non-zero, stop. Search the missiles round-robin, X * so we don't always find the same ones. X */ Xdoto_missiles(func) Xint (*func)(); X{ X struct missile *ptr = m_head, *next; X while (ptr != NULL) { X next = ptr->next; /* in case 'func' destroys the missile */ X (*func)(ptr); X ptr = next; X } X} X X/* X * Track a missile when traveling between windows. X */ Xstruct ballistic_type {int x, speed}; Xstart_ballistic(x, speed) X{ X extern int ballistic_delay; X struct itimerval timer; X struct ballistic_type *xptr = (struct ballistic_type *)calloc(1,sizeof(struct ballistic_type)); X int old_value = (int)panel_get_value(ballistic_item); X xptr->x = x; X xptr->speed = speed; X panel_set_value(ballistic_item, old_value + 1); X X if (old_value == 0) { X ballistic_warning(); X } X timer.it_interval.tv_usec = 0; X timer.it_interval.tv_sec = 0; X timer.it_value.tv_usec = 0; X timer.it_value.tv_sec = ballistic_delay; X if (timer.it_value.tv_sec > 0) { X notify_set_itimer_func(xptr, ballistic_timer, ITIMER_REAL, &timer, NULL); X } else { X ballistic_timer(xptr, NULL); X } X} X X/* X * Called when the between-window flight time of a missile is up. X */ Xstatic Notify_value Xballistic_timer(xptr, which) Xstruct ballistic_type *xptr; Xint which; X{ X extern int ballistic_delay; X int val; X if (running) { X if (suspended) { X /* by rechecking at each in-flight interval for each missile, we X approximate remembering when the real relaunch rate. X */ X suspendor(ballistic_timer, xptr, which, ballistic_delay); X return NOTIFY_DONE; X } else { X val = (int)panel_get_value(ballistic_item); X if (val > 0) { X /* Three new missiles appear. */ X start_missile(xptr->x, DOWN, xptr->speed, citypw); X start_missile(xptr->x, DOWN, xptr->speed, citypw); X start_missile(xptr->x, DOWN, xptr->speed, citypw); X panel_set_value(ballistic_item, panel_get_value(ballistic_item)-1); X } X } X } X free(xptr); X return NOTIFY_DONE; X} X X/* X * Just what it says. X */ Xfree_all_missiles() X{ X free_foe(); X panel_set_value(ballistic_item, 0); X while(m_head != NULL) X destroy_missile(m_head); X} X Xdo_warn_bell() X{ X struct timeval tv; X tv.tv_sec = 0; X tv.tv_usec = 20000; /* very short bell */ X win_bell(window_get(cityframe, WIN_FD), tv, 0); X} X X#define WARN_INTERVAL 100000 X Xballistic_warning() X{ X do_with_delay(do_warn_bell, 0, WARN_INTERVAL); X do_with_delay(do_warn_bell, 0, 2*WARN_INTERVAL); X do_with_delay(do_warn_bell, 0, 3*WARN_INTERVAL); X} END_OF_missile.c if test 7766 -ne `wc -c pr_helpers.c <<'END_OF_pr_helpers.c' X/**************************** pr_helpers.c *************************/ X#include X X/* X * Copyright 1987 by Mark Weiser. X * Permission to reproduce and use in any manner whatsoever on Suns is granted X * so long as this copyright and other identifying marks of authorship X * in the code and the game remain intact and visible. Use of this code X * in other products is reserved to me--I'm working on Mac and IBM versions. X */ X X/* These routines are pure pixrect operators, used mostly for city operations */ X Xstatic short pattern[] = { X 0x8000, 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, X 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 X }; Xstatic short *end_of_pattern_ptr = &pattern[16]; X X X#define lefthalf(x) (((x) & 0xff00) >> 8) X#define righthalf(x) ((x) & 0xff) X X/* X * Does an incremental melt of old into target, replacing X * old, by dropping pixels down until they hit target X * pixels. Melts 'speed' pixels per call. Old and target should be X * memory pixrects. Probably won't work as written for color. X * X * Pretty dumb and slow, undoubtedly could be done better. X */ X X/* alg: drop topmost pixels by speed pixels. Any pixels which were on X in the old which matched on's in the target, still stay on. X Warning: the speed parameter does not work for any value but 1. X */ X X Xmelt(old, target, speed) Xstruct pixrect *old, *target; Xint speed; X{ X short x_count, y_count, max_x, count, base = 0, bit_count; X int w = old->pr_size.x, h = old->pr_size.y; X struct pixrect *final; X struct mpr_data *mpr_final, *mpr_old; X short num_shorts, speed_inc; X short *old_image, *final_image; X unsigned short *remember_all_bits; X unsigned short *remember_bits, *bit_ptr; X register short *pattern_ptr, *old_image_ptr, *final_image_ptr; X X if (w != target->pr_size.x || h != target->pr_size.y) { X /* never leave here */ X printf("melt requires equal size pixrects.\n"); X abort(); X } X X mpr_old = mpr_d(old); X X if (mpr_old->md_offset.x != 0 || mpr_old->md_offset.y != 0) { X /* Never leave here */ X printf("Can't handle region pixrects in 'melt'.\n"); X abort(); X }; X X final = mem_create(w, h, 1); X X /* see if there is any work to do */ X pr_rop(final, 0, 0, w, h, PIX_SRC, old, 0, 0); X pr_rop(final, 0, 0, w, h, PIX_NOT(PIX_SRC) & PIX_DST, target, 0, 0); X if (all_zero_bits(final)) { X pr_destroy(final); X return; X } X X /* remember what was ok from before */ X pr_rop(final, 0, 0, w, h, PIX_SRC, old, 0, 0); X pr_rop(final, 0, 0, w, h, PIX_SRC & PIX_DST, target, 0, 0); X X /* melt some bits */ X X mpr_final = mpr_d(final); X num_shorts = mpr_final->md_linebytes / 2; X speed_inc = num_shorts*speed; X final_image = mpr_final->md_image; X old_image = mpr_old->md_image; X remember_all_bits = (unsigned short *)calloc(num_shorts+1, 2); X remember_bits = (unsigned short *)calloc(w+1, 2); X X /* Melt the top */ X for(y_count = 0; y_count < h - speed; y_count += 1) { X if (all_ones(remember_all_bits, num_shorts)) X goto next; X bit_ptr = remember_bits; X old_image_ptr = &old_image[base]; X final_image_ptr = &final_image[base]; X for (x_count = 0; x_count < num_shorts; x_count += 1) { X remember_all_bits[x_count] |= *old_image_ptr; X for(pattern_ptr = pattern; pattern_ptr < end_of_pattern_ptr; pattern_ptr++) { X /* for each bit... */ X if (*old_image_ptr & *pattern_ptr) { X /* if you saw one in this 'x' position before... */ X if (*bit_ptr) { X /* just copy this one in place. */ X *final_image_ptr |= *pattern_ptr; X } else { X /* if this one is the first one here, melt it. */ X *bit_ptr = 1; X *(final_image_ptr+speed_inc) |= *pattern_ptr; X } X } X bit_ptr++; X } X old_image_ptr++; X final_image_ptr++; X } X base += num_shorts; X } X X /* Move the rest of the image as is */ Xnext: X for(; y_count < h - speed; y_count += 1) { X old_image_ptr = &old_image[base]; X final_image_ptr = &final_image[base]; X for (x_count = 0; x_count < num_shorts; x_count += 1) { X *final_image_ptr |= *old_image_ptr; X old_image_ptr++; X final_image_ptr++; X } X base += num_shorts; X } X X X /* return value in old */ X pr_rop(old, 0, 0, w, h, PIX_SRC, final, 0, 0); X pr_destroy(final); X free(remember_bits); X free(remember_all_bits); X} X X Xstatic lookup[] = { X#include "lookup.h" X}; X X/* X * Count the number of bits in a pixrect. X Not tested on, and may not work for, color. X */ Xcount_bits(pr) Xstruct pixrect *pr; /* should be a memory pixrect for dvc ind. */ X{ X register short x_count, y_count, max_x, count, base = 0, num_shorts; X struct mpr_data *mpr = mpr_d(pr); X if (mpr->md_offset.x != 0 || mpr->md_offset.y != 0) { X /* Never leave here */ X printf("Can't handle region pixrects in 'count_bits'.\n"); X abort(); X }; X count = 0; X num_shorts = mpr->md_linebytes / 2; X for(y_count = 0; y_count < pr->pr_size.y; y_count++) { X for (x_count = 0; x_count < num_shorts; x_count += 1) { X count += lookup[lefthalf(mpr->md_image[base+x_count])] X +lookup[righthalf(mpr->md_image[base+x_count])]; X } X base += num_shorts; X } X return count; X} X X/* X * See if a memory pixrect is all zero. X */ Xall_zero_bits(pr) Xstruct pixrect *pr; /* should be a memory pixrect */ X{ X register short x_count, y_count, max_x, count, base = 0, num_shorts; X struct mpr_data *mpr = mpr_d(pr); X if (mpr->md_offset.x != 0 || mpr->md_offset.y != 0) { X /* Never leave here */ X printf("Can't handle region pixrects in 'count_bits'.\n"); X abort(); X }; X count = 0; X num_shorts = mpr->md_linebytes / 2; X for(y_count = 0; y_count < pr->pr_size.y; y_count++) { X for (x_count = 0; x_count < num_shorts; x_count += 1) { X if (mpr->md_image[base+x_count]) X return 0; X } X base += num_shorts; X } X return 1; X} X X/* X * See if an array of shorts contains all ones. X */ Xall_ones(x, len) Xregister len; Xregister unsigned short *x; X{ X for(; len; len--) { X if (*x++ != 0xffff) { X return 0; X } X } X return 1; X} X X X/* X * Grow could, and possibly should, be made to work like melt, and X * grow only at the edge of black areas, but growing linearly up X * from the bottom looks ok too. (Which is not true in revese for melting!). X * Unfortunately they are now asymmetrical, because grow needs a 'position' X * parameter which says how far from the bottom we are. X * X * Grow only works for 64x64 bit pixrects (because of hardwired constants), X * unlike melt which can melt anything. X */ X Xgrow(old, target, position) Xstruct pixrect *old, *target; X{ X if (position < 1 || position > 64) X return; X pr_rop(old, 0, 64 - position, 64, position, PIX_SRC, X target, 0, 64 - position); X} X END_OF_pr_helpers.c if test 6495 -ne `wc -c sdi.man <<'END_OF_sdi.man' X.TH SDI 1 "Feb. 5, 1987" X.AT 3 X.SH NAME Xsdi \- a game of shooting missiles coming and going X.SH SYNOPSIS X.B sdi X[[options] ]... X.SH DESCRIPTION X.I sdi Xis based on the classic X.IR "missile command" , Xof arcade, Macintosh, and Alto fame. Your mouse buttons control defenses Xagainst the missiles flying by in two windows, and a third window is your Xcontrol panel. XMissiles are launched up in one window, Xand, if you don't kill them there, they (after a few seconds of off-screen Xballistic mode), start down on your cities in a second window. X.PP XYour left button aims a high-speed interceptor missile, which Xexplodes leaving a large growing cloud of debris which destroys anything Xattempting to pass through. XShifted interceptors have a larger cloud (but watch out near your Xown cities.) X.PP XYour right button activates a pop-up x-ray laser, which detonates a small Xatom bomb for energy, and then directs a beam at the six most-recently-launched missiles within range. XShifted lasers have twice the range, but for no more Xthan three missiles. XLasered missiles die with a fizzle. If a missile disappears off the top Xof the screen before being sufficiently lasered, it lives on. X.PP XYour middle button activates a rock-dropping satellite. Rocks are Xdropped on a line between button-down and button-up, and start to drop Xto the ground before they burn up in the atmosphere. XShifted rocks are fewer, but last longer before burn up. X.PP XMissiles killed by blasts and rocks are worth 5 points, but Xthose killed by lasers are worth 50 points. All missiles killed Xon the city playing field (as opposed to the launch playing field) Xare worth only one fifth as much. A bonus city is awarded after X5000 points, then 10000, then 20000, and so on by powers of two. XIf there is no place to build a bonus city, then the bonus is saved Xuntil needed. Each city left at the end of a turn is worth ten times Xthe current game level in points. X.PP XThe windows will flash and beep when you try to shoot with an empty button. XA triple-beep sounds as an air raid warning whenever a Xmissile enters ballistic mode between the screens, and there were Xpreviously no missiles there. X.PP XThe control window should Xbe used for all window opening and closing--the others will follow. XPlaying fields can be resized for some interesting variations, and the Xnumber of cities will be adjusted. However, this will always start Xa new game. There are three skill levels, everything is worth more Xpoints at higher skills. Skill level can only be changed between games. X.PP XGames can be saved and restored using the appropriate control panel Xbuttons, but only between rounds. Window position and size are Xamong the parameters remembered in a saved game. X.PP XShifted mouse buttons increase the 'size' Xof the defense, but trade-off something else. Meta moused buttons cause Xyour action to be done twice, spread to either side of the mouse position. XYour defenses are used up twice as fast as well. X.PP XIn the control window, Xthe three top 'sliders' show the 'ammo' associated with your three Xmouse buttons, the two bottom 'sliders' show the missiles-in-flight and Xmissiles-on-the-ground, respectively. XThe 'Things To Read' button pops up an assortment of reading options, Xincluding the manual entry. The different 'options' buttons also pop Xup subwindows. X.PP XThe game is automatically suspended when in a subwindow. If you want Xto retain the subwindow while playing, move the subwindow Xout of the way and explicitly 'resume' the game. XOnly one subwindow at a time, though. X.PP XFor the real junky, the 'non-stop' button restarts each Xround without waiting for confirmation. The big 'game over' message Xat the end of the game is also bypassed. X.PP XFor variety there are various options, Xeach followed by one or more parameters. Spaces separate options and parameters. X.TP X.B -c Xsets the cities to an arbitrary icon. (A "ziggy" icon is popular here.) X.TP X.B -d Xsets the delay between screen updates (in microseconds, default 150000), X.TP X.B -f Xlooks for a score file here. If this option is not present, then Xthe environment is checked for an SDI_SCORES variable, and if it Xis not present then a check is made for a /usr/game/lib/sdi_scores file, Xand if that is not present then an attempt is made to open a file Xcalled /tmp/sdi_scores (or other location specified by -DSCOREFILE when X.I sdi Xwas compiled.) X.I Sdi Xwill never create a score file. An easy way to start score-keeping is to Xissue the shell command 'touch /tmp/sdi_scores', then play. X.TP X.B -g XAdds the 'gamemaster' item to the control panel, which when selected makes Xall control panel items editable, and brings up a couple of more panels Xto control (scroll down to see them). Using this mode disables score file recording. X.TP X.B -h Xset the height of the playing fields. X.TP X.B -i Xmust be followed by two numbers, the icon type and the icon-update-time in tenths of seconds. Icon update time defaults to 5 (half a second). Icon types are 0 for normal sit-there-and-do-nothing icon, 1 (the default) for an Xicon with subtle amusing flavor, and 2 for wild bubbling icon. X.TP X.B -l Xset the starting level, from 0 to 49. X.TP X.B -p XSelect a type of pointer (i.e. cursor). XThe following parameter is 0 for normal cursor, 1 for dynamic cursor, and 2 for cross hairs. X.TP X.B -r Xthe following parameter is the name of a file from which to restore a saved game. X.TP X.B -s Xset the starting skill: 0 for novice, 1 for intermediate, 2 for expert. X.TP X.B -t Xset the maximum time for a game, in seconds. Using this option causes Xa new slider to appear which ticks down the remaining seconds. It also Xautomatically uses 'Non-stop' mode between rounds but not at the end of game. XWhen time runs out, the 'melt' button is implicitly pushed. X.TP X.B -w Xset the height of the playing fields. X.PP XThe following options, all starting with -b, specify different pixrects to be used in different Xkind of blasts: X.TP X.B -bb Xblast of an interceptor. X.TP X.B -bm Xblast of a missile blowing up via interceptor. X.TP X.B -bk Xblast of a missile blowing up via a laser. X.TP X.B -bl Xblast of a laser's bomb source. X.TP X.B -bc Xblast of a missile hitting a city. X.SH Bugs XWhen compiled against Sun 3.0 operating system libraries, the wrong window Xsizes are used if the game starts iconic ('-Wi' option). Works ok with Xthe 3.2 libraries, or if started non-iconic and then later closed. X.PP XThe low-res 'game-over' picture keeps the shar-file size down for shipping, Xbut doesn't look great. X.PP XToo many silly control panel buttons, like 'melt'. X.PP XFar, far too many options. X.SH Author XMark Weiser END_OF_sdi.man if test 6607 -ne `wc -c