DATE := date
GIT := git
+MMLIB_PATH := ../mmlib
+
REVISION := $(shell $(DATE) +%Y%m%d)-$(shell $(GIT) 2>/dev/null describe --abbrev=1 --tags --always --match v)
TMPDIR := /var/tmp
#CFLAGS += -v -H -g -O0
#LDFLAGS += -v -g
# Alternative
-#CFLAGS += -g -O0
-#LDFLAGS += -g
+CFLAGS += -g -O0
+LDFLAGS += -g
# For inline functions with GCC5 or XCode
-CFLAGS += -std=gnu89 -O3
+#CFLAGS += -std=gnu89 -O3
# And to disable assertions
-CFLAGS += -DNDEBUG
+#CFLAGS += -DNDEBUG
# And to enable profiling
#CFLAGS += -pg
#LDFLAGS += -pg
-OBJS := $(addprefix src/,clock.o screen.o main.o)
+MMLIBS := $(addprefix $(MMLIB_PATH)/,mmrarray.o)
+OBJS := $(addprefix src/,screen.o collision.o nbrandom.o main.o)
BIN := ascender
# OS dependent settings follow
CURSES_CFLAGS :=
CURSES_LDFLAGS := -lcurses
-ifneq (,$(findstring NetBSD,$(OS)))
- CURSES_CFLAGS += -DTPARM_VARARGS
- CURSES_LDFLAGS += -lterminfo
-endif
-
-# Architecture independent settings follow
-#CFLAGS += -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234
-#CFLAGS += -DBYTE_ORDER=LITTLE_ENDIAN
CFLAGS += $(CURSES_CFLAGS)
LDFLAGS += $(CURSES_LDFLAGS) -lm
$(OBJS): src/config.h
%.o: %.c
- $(CC) -c $(CFLAGS) -I. -o $@ $<
+ $(CC) -c $(CFLAGS) -I. -I$(MMLIB_PATH) -o $@ $<
-$(BIN): $(OBJS)
- $(CC) -o $@ $(OBJS) $(LDFLAGS)
- $(STRIP) $@
+$(BIN): $(MMLIBS) $(OBJS)
+ $(CC) -o $@ $(MMLIBS) $(OBJS) $(LDFLAGS)
+# $(STRIP) $@
install:
$(INSTALL) -c -o 0 -g 0 -m 755 $(BIN) /usr/local/bin
clean:
- $(RM) -f $(BIN) $(BIN).core $(OBJS)
+ $(RM) -f $(BIN) $(BIN).core $(OBJS) $(MMLIBS)
+++ /dev/null
-/*
- * Copyright (c) 2023, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-#include <err.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/time.h>
-
-#include <clock.h>
-#include <config.h>
-
-
-bool clock_expired = false;
-
-
-static void sighandler(int);
-
-
-void
-clock_init(void)
-{
- struct sigaction act;
- struct itimerval itv;
-
- /* XXX Probably some are unnecessary here */
- /* Setup signal handler */
- act.sa_handler = sighandler;
- act.sa_flags = 0; /* ~SA_RESTART to detect interruptions */
- if (sigemptyset(&act.sa_mask) != 0)
- err(EXIT_FAILURE, "sigemptyset()");
- if (sigaction(SIGALRM, &act, NULL) != 0)
- err(EXIT_FAILURE, "sigaction(SIGALRM)");
- if (sigaction(SIGINT, &act, NULL) != 0)
- err(EXIT_FAILURE, "sigaction(SIGINT)");
- if (sigaction(SIGTERM, &act, NULL) != 0)
- err(EXIT_FAILURE, "sigaction(SIGTERM)");
- if (sigaction(SIGHUP, &act, NULL) != 0)
- err(EXIT_FAILURE, "sigaction(SIGHUP)");
-
- /* Setup interval timer */
- timerclear(&itv.it_interval);
- timerclear(&itv.it_value);
- itv.it_interval.tv_sec = 0;
- itv.it_interval.tv_usec = 1000000 / ANIM_FPS;
- itv.it_value.tv_sec = itv.it_interval.tv_sec;
- itv.it_value.tv_usec = itv.it_interval.tv_usec;
- if (setitimer(ITIMER_REAL, &itv, NULL) != 0)
- err(EXIT_FAILURE, "setitimer()");
-}
-
-static void
-sighandler(int sig)
-{
-
- switch (sig) {
- case SIGALRM:
- clock_expired = true;
- break;
- case SIGINT:
- case SIGTERM: /* FALLTHROUGH */
- case SIGHUP: /* FALLTHROUGH */
- exit(EXIT_SUCCESS);
- break;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2023, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-#ifndef __CLOCK_H__
-#define __CLOCK_H__
-
-
-#include <stdbool.h>
-
-
-extern void clock_init(void);
-
-
-extern bool clock_expired;
-
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2018, 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <curses.h>
+
+
+inline bool
+point_in_rect(int x, int y, int rx1, int ry1, int rx2, int ry2)
+{
+
+ return (x > rx1 && x < rx2 && y > ry1 && y < ry2);
+}
+
+inline bool
+rect_overlap(int r1x1, int r1y1, int r1x2, int r1y2,
+ int r2x1, int r2y1, int r2x2, int r2y2)
+{
+
+ return (
+ point_in_rect(r1x1, r1y1, r2x1, r2y1, r2x2, r2y2) ||
+ point_in_rect(r1x2, r1y1, r2x1, r2y1, r2x2, r2y2) ||
+ point_in_rect(r1x2, r1y2, r2x1, r2y1, r2x2, r2y2) ||
+ point_in_rect(r1x1, r1y2, r2x1, r2y1, r2x2, r2y2) ||
+ point_in_rect(r2x1, r2y1, r1x1, r1y1, r1x2, r1y2) ||
+ point_in_rect(r2x2, r2y1, r1x1, r1y1, r1x2, r1y2) ||
+ point_in_rect(r2x2, r2y2, r1x1, r1y1, r1x2, r1y2) ||
+ point_in_rect(r2x1, r2y2, r1x1, r1y1, r1x2, r1y2));
+}
+
+bool
+point_in_shape(WINDOW *pad, int x, int y)
+{
+ chtype c;
+
+ if ((c = mvwinch(pad, y, x) != ERR) &&
+ (c & A_CHARTEXT) == ' ')
+ return true;
+
+ return false;
+}
--- /dev/null
+/*
+ * Copyright (c) 2018, 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __COLLISION_H__
+#define __COLLISION_H__
+
+
+#include <stdbool.h>
+#include <curses.h>
+
+
+inline bool point_in_rect(int, int, int, int, int, int);
+inline bool rect_overlap(int, int, int, int, int, int, int, int);
+bool point_in_shape(WINDOW *, int, int);
+
+
+#endif
+/*
+ * Copyright (c) 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef __CONFIG_H__
#define __CONFIG_H__
#define ANIM_FPS 15
+/* XXX */
+#define DATA_PREFIX "./src"
+#define DATA_SHAPES "shapes.txt"
+#define DATA_SHAPESPATH "" DATA_PREFIX "/" DATA_SHAPES ""
+
+
#endif
/*
* Copyright (c) 2023, Matthew Mondor
- * ALL RIGHTS RESERVED.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
+#include <ctype.h>
#include <curses.h>
+#include <err.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
-#include <config.h>
-#include <clock.h>
#include <screen.h>
+#include <nbrandom.h>
+#include <config.h>
+#include <mmrarray.h>
-#define NOBJECTS 46
+#define NOBJECTS 56
+
+enum enum_shapes {
+ SHAPE_A = 0,
+ SHAPE_B,
+ SHAPE_SHIP,
+ SHAPE_MAX
+};
+
+/* Animation object */
enum aobject_type {
AOT_AVATAR = 0,
AOT_STAR,
- AOT_BUG
+ AOT_BUG,
+ AOT_SHAPE
};
-/* Animation object */
+/* Could be a union but not needed for this test */
typedef struct aobject {
- int t, x, y, fcol, bcol;
+ int t, x, y, col;
char c;
+ int s;
+ int d;
} aobject_t;
int main(void);
+static void shape_draw(int, int, int);
static void anim_init(void);
-static void anim_refresh_full(void);
-static void anim_refresh_update(void);
+static void anim_redraw(void);
+static void anim_update(void);
+
+static void rarray_empty(rarray_t *);
+static void read_shapes(void);
+/* Color related */
+static short ncolpairs = -1;
+
+/* Loaded shapes */
+WINDOW *spads[SHAPE_MAX];
+
/* Animation world */
-static char map[TERM_LINES][TERM_COLUMNS];
static aobject_t objects[NOBJECTS];
static aobject_t avatar = { AOT_AVATAR,
TERM_COLUMNS / 2, TERM_LINES / 2,
- 1, 0, '?' };
+ 0, '?', -1, -1 };
int
main(void)
{
- int c, uc;
-
- /* Setup interval timer and signal handler */
- clock_init();
-
- /* And screen */
screen_init();
+ /*
+ * XXX Useful but relies on an ncurses extension, also supported by
+ * NetBSD curses. init_pair() cannot accept -1 according to X/Open.
+ * But this special value means to keep the default foreground or
+ * background color of the terminal configuration. This also permits
+ * enforced color pairs to really only define the foreground or
+ * background color.
+ */
+ (void)use_default_colors();
+ if (start_color() == ERR)
+ err(EXIT_FAILURE, "start_color()");
+
+ /*
+ * Allocate a few basic pairs before loading shapes that will also
+ * allocate new pairs as needed.
+ */
+ (void)init_pair(++ncolpairs, -1, -1);
+ (void)init_pair(++ncolpairs, COLOR_WHITE, COLOR_BLACK);
+
+ /* Read shape definitions from file */
+ read_shapes();
+
/* Setup animation world */
anim_init();
- /* Initialize screen */
- ti_put(&ti_clear);
- ti_put(&ti_civis);
- (void)fflush(stdout);
-
/* Animate until user requests to quit with q */
- anim_refresh_full();
- clock_expired = false;
- uc = -1;
+ anim_redraw();
for (;;) {
-cont:
- if ((c = screen_getchar()) != ERR)
- uc = c;
- if (clock_expired) {
- clock_expired = false;
- int x = -1, y = -1;
-
- /* Process user input if any */
- /* XXX Should probably be in a function */
- switch (uc) {
- case -1:
- break;
- case 'q':
- goto end;
- break;
- case 12:
- anim_refresh_full();
- uc = -1;
- goto cont;
- break;
-
- case KEY_UP:
- case '8': /* FALLTHROUGH */
- case 'i': /* FALLTHROUGH */
- case 'a': /* FALLTHROUGH */
- if (avatar.y > 0) {
- y = avatar.y - 1;
- x = avatar.x;
- }
- break;
+ int x = -1, y = -1, c = -1, uc = -1;
- case KEY_DOWN:
- case '2': /* FALLTHROUGH */
- case 'k': /* FALLTHROUGH */
- case 'm': /* FALLTHROUGH */
- case 'z': /* FALLTHROUGH */
- if (avatar.y < TERM_LINES - 1) {
- y = avatar.y + 1;
- x = avatar.x;
- }
- break;
+ /* Sleep for most of the frame */
+ (void)usleep(1000000 / ANIM_FPS);
- case KEY_LEFT:
- case '4': /* FALLTHROUGH */
- case 'j': /* FALLTHROUGH */
- case ',': /* FALLTHROUGH */
- if (avatar.x > 0) {
- x = avatar.x - 1;
- y = avatar.y;
- }
- break;
+ /* Non-blocking check for user input */
+ if ((c = screen_getchar()) != ERR) {
+ uc = c;
+ /*
+ * If we don't flush and keyboard input/repeat is
+ * faster than our refresh rate, the input queue grows
+ * meaning that the avatar takes a while to react to
+ * immediate user input.
+ */
+ (void)flushinp();
+ }
- case KEY_RIGHT:
- case '6': /* FALLTHROUGH */
- case 'l': /* FALLTHROUGH */
- case '.': /* FALLTHROUGH */
- if (avatar.x < TERM_COLUMNS - 1) {
- x = avatar.x + 1;
- y = avatar.y;
- }
- break;
+ /* Process user input if any */
+ switch (uc) {
+ case -1:
+ break;
+ case 'q':
+ goto end;
+ break;
+
+ case KEY_UP:
+ case '8': /* FALLTHROUGH */
+ case 'i': /* FALLTHROUGH */
+ case 'a': /* FALLTHROUGH */
+ if (avatar.y > 0) {
+ y = avatar.y - 1;
+ x = avatar.x;
}
- /* Update avatar if necessary */
- if (x != -1 && y != -1) {
- /* XXX */
- avatar.x = x;
- avatar.y = y;
+ break;
+
+ case KEY_DOWN:
+ case '2': /* FALLTHROUGH */
+ case 'k': /* FALLTHROUGH */
+ case 'm': /* FALLTHROUGH */
+ case 'z': /* FALLTHROUGH */
+ if (avatar.y < TERM_LINES - 1) {
+ y = avatar.y + 1;
+ x = avatar.x;
}
+ break;
+
+ case KEY_LEFT:
+ case '4': /* FALLTHROUGH */
+ case 'j': /* FALLTHROUGH */
+ case ',': /* FALLTHROUGH */
+ if (avatar.x > 0) {
+ x = avatar.x - 1;
+ y = avatar.y;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case '6': /* FALLTHROUGH */
+ case 'l': /* FALLTHROUGH */
+ case '.': /* FALLTHROUGH */
+ if (avatar.x < TERM_COLUMNS - 1) {
+ x = avatar.x + 1;
+ y = avatar.y;
+ }
+ break;
+ }
- /* Animate */
- anim_refresh_update();
- uc = -1;
+ /* Update avatar if necessary */
+ if (x != -1 && y != -1) {
+ avatar.x = x;
+ avatar.y = y;
}
+
+ /* Animate */
+ anim_update();
+ anim_redraw();
+ uc = -1;
}
end:
exit(EXIT_SUCCESS);
}
+/* 2d text shape blitting routine */
static void
-anim_init(void)
+shape_draw(int shape, int y, int x)
{
- int x, y, i, col;
- for (y = 0; y < TERM_LINES; y++) {
- for (x = 0; x < TERM_COLUMNS; x++) {
- map[y][x] = ' ';
- }
- }
+ screen_blitpad(spads[shape], y, x);
+}
+
+static void
+anim_init(void)
+{
+ int i, col;
- srandom(13);
+ nbsrandom(13);
col = 0;
for (i = 0; i < NOBJECTS; i++) {
aobject_t *o = &objects[i];
+ /* 24 bugs, 20 stars, 10 shapes */
if (i <= 26) {
o->t = AOT_BUG;
o->c = 'A' + i;
- o->fcol = ++col % 7;
- o->bcol = ++col % 7;
- } else {
+ o->col = ++col % ncolpairs;
+ o->s = -1;
+ o->d = nbrandom() % 4;
+ } else if (i < 46) {
o->t = AOT_STAR;
o->c = '.';
- o->fcol = 2;
- o->bcol = 0;
+ o->col = 0;
+ o->s = -1;
+ o->d = -1;
+ } else {
+ o->t = AOT_SHAPE;
+ o->c = '\0';
+ o->col = -1;
+ o->s = nbrandom() % SHAPE_MAX;
+ o->d = nbrandom() % 4;
}
- o->x = random() % (TERM_COLUMNS - 1);
- o->y = random() % (TERM_LINES - 1);
+ o->x = nbrandom() % (TERM_COLUMNS - 1);
+ o->y = nbrandom() % (TERM_LINES - 1);
}
-
- /* XXX Avatar */
}
static void
-anim_refresh_full(void)
+anim_redraw(void)
{
- int x, y, i;
+ int i;
- ti_put(&ti_clear);
- ti_bgcolor(0);
- for (y = 0; y < TERM_LINES; y++) {
- for (x = 0; x < TERM_COLUMNS; x++) {
- (void)putchar(map[y][x]);
- }
- (void)fwrite("\r\n", 2, 1, stdout);
- }
+ /* Redraw screen with all objects */
+ (void)attrset(COLOR_PAIR(0));
+ (void)erase();
for (i = 0; i < NOBJECTS; i++) {
aobject_t *o = &objects[i];
- ti_goto(o->x, o->y);
- ti_fgcolor(o->fcol);
- ti_bgcolor(o->bcol);
- putchar(o->c);
+ if (o->t == AOT_SHAPE)
+ shape_draw(o->s, o->y, o->x);
+ else {
+ (void)attrset(COLOR_PAIR(o->col));
+ (void)mvaddch(o->y, o->x, o->c);
+ }
}
/* Avatar */
- ti_goto(avatar.x, avatar.y);
- ti_put(&ti_sgr0);
- ti_put(&ti_rev);
- (void)putchar(avatar.c);
- ti_put(&ti_sgr0);
+ (void)attr_on(A_REVERSE, NULL);
+ (void)mvaddch(avatar.y, avatar.x, avatar.c);
+ (void)attr_off(A_REVERSE, NULL);
- (void)fflush(stdout);
+ (void)doupdate();
}
static void
-anim_refresh_update(void)
+anim_update(void)
{
int i;
for (i = 0; i < NOBJECTS; i++) {
aobject_t *o = &objects[i];
- int x = o->x, y = o->y, d = random() % 4;
+ int x = o->x, y = o->y, d = nbrandom() % 16;
if (o->t == AOT_STAR) {
if (x > 0)
else
x = TERM_COLUMNS - 1;
} else {
- switch (d) {
+ /* Change direction 1/4 of the time */
+ if (d > -1 && d < 4)
+ o->d = d;
+ /* Move in current direction */
+ switch (o->d) {
case 0: /* Up */
if (y > 0)
y--;
break;
}
}
-
- /* Erase from old pos */
- map[o->y][o->x] = ' ';
- ti_goto(o->x, o->y);
- ti_bgcolor(0);
- (void)putchar(' ');
-
- /* Move/redraw at new pos */
- map[y][x] = o->c;
- ti_goto(x, y);
- ti_fgcolor(o->fcol);
- ti_bgcolor(o->bcol);
- (void)putchar(o->c);
o->x = x;
o->y = y;
}
+}
- /* Avatar */
- ti_goto(avatar.x, avatar.y);
- ti_put(&ti_sgr0);
- ti_put(&ti_rev);
- (void)putchar(avatar.c);
- ti_put(&ti_sgr0);
+#define SHAPECOLSMAX 80
+#define SHAPELINESMAX 80
+
+static void
+rarray_empty(rarray_t *rarray)
+{
+ int i;
+
+ for (i = 0; i < rarray->count; i++) {
+ if ((void *)rarray->array[i] != NULL)
+ free((void *)rarray->array[i]);
+ }
+ rarray_reset(rarray);
+}
+
+static void
+read_shapes(void)
+{
+ FILE *fh;
+ char line[SHAPECOLSMAX + 3], *cptr;
+ int nline = 0, nshapes = 0, len, xs, ys;
+
+ if ((fh = fopen(DATA_SHAPESPATH, "r")) == NULL)
+ err(EXIT_FAILURE, "Cannot open %s", DATA_SHAPESPATH);
+
+ /* About current shape */
+ rarray_t tlines, flines, blines, alines;
+
+ rarray_init(&tlines, SHAPELINESMAX);
+ rarray_init(&flines, SHAPELINESMAX);
+ rarray_init(&blines, SHAPELINESMAX);
+ rarray_init(&alines, SHAPELINESMAX);
+
+ for (;;) {
+ int x, y;
+ WINDOW *pad;
+
+ /* Attempt to read one shape worth's data */
+ for (;;) {
+ if (fgets(line, SHAPECOLSMAX, fh) == NULL)
+ break;
+ nline++;
+
+ /* Strip trailing whitespace characters */
+ for (len = strlen(line), cptr = &line[len - 1];
+ isspace((int)*cptr); cptr--, len--)
+ *cptr = '\0';
+
+ /* Comment, skip */
+ if (*line == '#')
+ continue;
+
+ /* End of shape marker */
+ if (*line == '\0')
+ break;
+
+ /* As a result of trimming fix empty-data lines */
+ {
+ char c = *line;
+
+ if ((c == 'T' || c == 'F' || c == 'B' ||
+ c == 'A') && line[1] == '\0') {
+ line[1] = ' ';
+ line[2] = '\0';
+ }
+ }
+
+ if (line[0] == 'T' && line[1] == ' ') {
+ rarray_append(&tlines,
+ (intptr_t)strdup(&line[2]));
+ continue;
+ }
+ if (line[0] == 'F' && line[1] == ' ') {
+ rarray_append(&flines,
+ (intptr_t)strdup(&line[2]));
+ continue;
+ }
+ if (line[0] == 'B' && line[1] == ' ') {
+ rarray_append(&blines,
+ (intptr_t)strdup(&line[2]));
+ continue;
+ }
+ if (line[0] == 'A' && line[1] == ' ') {
+ rarray_append(&alines,
+ (intptr_t)strdup(&line[2]));
+ continue;
+ }
+
+ err(EXIT_FAILURE,
+ "Unexpected shape format line %d in %s",
+ nline, DATA_SHAPESPATH);
+ }
+
+ /* Process lines and create new shape instance */
+
+ if (tlines.count < 1)
+ err(EXIT_FAILURE, "Empty shape %d in %s line %d",
+ nshapes, DATA_SHAPESPATH, nline);
+ if ((flines.count > 0 && flines.count != tlines.count) ||
+ (blines.count > 0 && blines.count != tlines.count) ||
+ (alines.count > 0 && alines.count != tlines.count))
+ err(EXIT_FAILURE,
+ "F B A present but not matching T lines count %d "
+ "for shape %d at line %d in %s",
+ tlines.count, nshapes, nline, DATA_SHAPESPATH);
+
+ /* Count shape dimensions */
+ for (xs = 0, y = 0; y < tlines.count; y++)
+ if (xs < (len = strlen((const char *)tlines.array[y])))
+ xs = len;
+ ys = tlines.count;
+
+ /* Allocate new pad */
+ if ((pad = newpad(ys, xs)) == NULL)
+ err(EXIT_FAILURE, "newpad()");
+ (void)werase(pad);
+
+ /* Fill with characters marked with attributes as needed */
+ /* XXX Also avoid to apply colors or attributes depending on
+ * options like mono/nocolor */
+ for (y = 0; y < tlines.count; y++) {
+ for (len = strlen((const char *)tlines.array[y]),
+ x = 0; x < len; x++) {
+ chtype ch;
+ int fg = -1, bg = -1;
+ char *cptr, c;
+
+ cptr = (char *)tlines.array[y];
+ ch = cptr[x];
+
+ if (flines.count != 0) {
+ cptr = (char *)flines.array[y];
+ if (strlen(cptr) >= x) {
+ c = cptr[x];
+ if (c >= '0' && c <= '7')
+ fg = c - '0';
+ }
+ }
+ if (blines.count != 0) {
+ cptr = (char *)blines.array[y];
+ if (strlen(cptr) >= x) {
+ c = cptr[x];
+ if (c >= '0' && c <= '7')
+ bg = c - '0';
+ }
+ }
+ if (alines.count != 0) {
+ cptr = (char *)alines.array[y];
+ if (strlen(cptr) >= x) {
+ c = cptr[x];
+ switch (c) {
+ case 'E':
+ ch |= A_BOLD;
+ break;
+ case 'I':
+ ch |= A_REVERSE;
+ break;
+ case 'U':
+ ch |= A_UNDERLINE;
+ break;
+ case 'B':
+ ch |= A_BLINK;
+ break;
+ }
+ }
+ }
+ /* Associate color pair, allocate if needed */
+ if (fg != -1 || bg != -1) {
+ short p = -1, i, f, b;
+
+ if (ncolpairs != -1) {
+ for (i = 0; i <= ncolpairs;
+ i++) {
+ (void)pair_content(i,
+ &f, &b);
+ if (fg == f &&
+ bg == b) {
+ p = i;
+ break;
+ };
+ }
+ }
+ if (p == -1) {
+ if (init_pair(++ncolpairs, fg,
+ bg) == ERR)
+ screen_error("init_pair()");
+ p = ncolpairs;
+ }
+ ch |= COLOR_PAIR(p);
+ }
+
+ /* Finally add character to pad */
+ (void)mvwaddch(pad, y, x, ch);
+ }
+ }
+ /* Finally add pad to shapes */
+ spads[nshapes++] = pad;
+
+ /* Reset for next shape */
+ rarray_empty(&tlines);
+ rarray_empty(&flines);
+ rarray_empty(&blines);
+ rarray_empty(&alines);
+
+ /* Enough shapes read, stop */
+ if (nshapes == SHAPE_MAX)
+ break;
+ }
+
+ (void)fclose(fh);
+ rarray_destroy(&tlines);
+ rarray_destroy(&flines);
+ rarray_destroy(&blines);
+ rarray_destroy(&alines);
+
+ if (nshapes != SHAPE_MAX)
+ err(EXIT_FAILURE, "Shapes short count in %s "
+ "(expected %d, read %d).", DATA_SHAPESPATH, SHAPE_MAX,
+ nshapes);
- (void)fflush(stdout);
+ /* Success */
}
--- /dev/null
+/* Adapted for embedding in Ascender game by Matthew Mondor, 2023 */
+/* Adapted for embedding in Espadon game by Matthew Mondor, 2018 */
+/* $NetBSD: random.c,v 1.4 2014/06/12 20:59:46 christos Exp $ */
+
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h> /* size_t */
+#include <stdlib.h>
+
+/* If reentrancy is needed, define these to lock/unlock a lock/mutex */
+#define NBRANDOM_LOCK() (void)0
+#define NBRANDOM_UNLOCK() (void)0
+
+static void srandom_unlocked(unsigned int);
+static long random_unlocked(void);
+
+/*
+ * random.c:
+ *
+ * An improved random number generation package. In addition to the standard
+ * rand()/srand() like interface, this package also has a special state info
+ * interface. The initstate() routine is called with a seed, an array of
+ * bytes, and a count of how many bytes are being passed in; this array is
+ * then initialized to contain information for random number generation with
+ * that much state information. Good sizes for the amount of state
+ * information are 32, 64, 128, and 256 bytes. The state can be switched by
+ * calling the setstate() routine with the same array as was initiallized
+ * with initstate(). By default, the package runs with 128 bytes of state
+ * information and generates far better random numbers than a linear
+ * congruential generator. If the amount of state information is less than
+ * 32 bytes, a simple linear congruential R.N.G. is used.
+ *
+ * Internally, the state information is treated as an array of ints; the
+ * zeroeth element of the array is the type of R.N.G. being used (small
+ * integer); the remainder of the array is the state information for the
+ * R.N.G. Thus, 32 bytes of state information will give 7 ints worth of
+ * state information, which will allow a degree seven polynomial. (Note:
+ * the zeroeth word of state information also has some other information
+ * stored in it -- see setstate() for details).
+ *
+ * The random number generation technique is a linear feedback shift register
+ * approach, employing trinomials (since there are fewer terms to sum up that
+ * way). In this approach, the least significant bit of all the numbers in
+ * the state table will act as a linear feedback shift register, and will
+ * have period 2^deg - 1 (where deg is the degree of the polynomial being
+ * used, assuming that the polynomial is irreducible and primitive). The
+ * higher order bits will have longer periods, since their values are also
+ * influenced by pseudo-random carries out of the lower bits. The total
+ * period of the generator is approximately deg*(2**deg - 1); thus doubling
+ * the amount of state information has a vast influence on the period of the
+ * generator. Note: the deg*(2**deg - 1) is an approximation only good for
+ * large deg, when the period of the shift register is the dominant factor.
+ * With deg equal to seven, the period is actually much longer than the
+ * 7*(2**7 - 1) predicted by this formula.
+ *
+ * Modified 28 December 1994 by Jacob S. Rosenberg.
+ * The following changes have been made:
+ * All references to the type u_int have been changed to unsigned long.
+ * All references to type int have been changed to type long. Other
+ * cleanups have been made as well. A warning for both initstate and
+ * setstate has been inserted to the effect that on Sparc platforms
+ * the 'arg_state' variable must be forced to begin on word boundaries.
+ * This can be easily done by casting a long integer array to char *.
+ * The overall logic has been left STRICTLY alone. This software was
+ * tested on both a VAX and Sun SpacsStation with exactly the same
+ * results. The new version and the original give IDENTICAL results.
+ * The new version is somewhat faster than the original. As the
+ * documentation says: "By default, the package runs with 128 bytes of
+ * state information and generates far better random numbers than a linear
+ * congruential generator. If the amount of state information is less than
+ * 32 bytes, a simple linear congruential R.N.G. is used." For a buffer of
+ * 128 bytes, this new version runs about 19 percent faster and for a 16
+ * byte buffer it is about 5 percent faster.
+ *
+ * Modified 07 January 2002 by Jason R. Thorpe.
+ * The following changes have been made:
+ * All the references to "long" have been changed back to "int". This
+ * fixes memory corruption problems on LP64 platforms.
+ */
+
+/*
+ * For each of the currently supported random number generators, we have a
+ * break value on the amount of state information (you need at least this
+ * many bytes of state info to support this random number generator), a degree
+ * for the polynomial (actually a trinomial) that the R.N.G. is based on, and
+ * the separation between the two lower order coefficients of the trinomial.
+ */
+#define TYPE_0 0 /* linear congruential */
+#define BREAK_0 8
+#define DEG_0 0
+#define SEP_0 0
+
+#define TYPE_1 1 /* x**7 + x**3 + 1 */
+#define BREAK_1 32
+#define DEG_1 7
+#define SEP_1 3
+
+#define TYPE_2 2 /* x**15 + x + 1 */
+#define BREAK_2 64
+#define DEG_2 15
+#define SEP_2 1
+
+#define TYPE_3 3 /* x**31 + x**3 + 1 */
+#define BREAK_3 128
+#define DEG_3 31
+#define SEP_3 3
+
+#define TYPE_4 4 /* x**63 + x + 1 */
+#define BREAK_4 256
+#define DEG_4 63
+#define SEP_4 1
+
+/*
+ * Array versions of the above information to make code run faster --
+ * relies on fact that TYPE_i == i.
+ */
+#define MAX_TYPES 5 /* max number of types above */
+
+static const int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
+static const int seps[MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
+
+/*
+ * Initially, everything is set up as if from:
+ *
+ * initstate(1, &randtbl, 128);
+ *
+ * Note that this initialization takes advantage of the fact that srandom()
+ * advances the front and rear pointers 10*rand_deg times, and hence the
+ * rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+ * element of the state information, which contains info about the current
+ * position of the rear pointer is just
+ *
+ * MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
+ */
+
+/* LINTED */
+static int randtbl[DEG_3 + 1] = {
+ TYPE_3,
+ 0x991539b1, 0x16a5bce3, 0x6774a4cd,
+ 0x3e01511e, 0x4e508aaa, 0x61048c05,
+ 0xf5500617, 0x846b7115, 0x6a19892c,
+ 0x896a97af, 0xdb48f936, 0x14898454,
+ 0x37ffd106, 0xb58bff9c, 0x59e17104,
+ 0xcf918a49, 0x09378c83, 0x52c7a471,
+ 0x8d293ea9, 0x1f4fc301, 0xc3db71be,
+ 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1,
+ 0x19edc328, 0x87bf4bdd, 0xc9b240e5,
+ 0xe9ee4b1b, 0x4382aee7, 0x535b6b41,
+ 0xf3bec5da
+};
+
+/*
+ * fptr and rptr are two pointers into the state info, a front and a rear
+ * pointer. These two pointers are always rand_sep places aparts, as they
+ * cycle cyclically through the state information. (Yes, this does mean we
+ * could get away with just one pointer, but the code for random() is more
+ * efficient this way). The pointers are left positioned as they would be
+ * from the call
+ *
+ * initstate(1, randtbl, 128);
+ *
+ * (The position of the rear pointer, rptr, is really 0 (as explained above
+ * in the initialization of randtbl) because the state table pointer is set
+ * to point to randtbl[1] (as explained below).
+ */
+static int *fptr = &randtbl[SEP_3 + 1];
+static int *rptr = &randtbl[1];
+
+/*
+ * The following things are the pointer to the state information table, the
+ * type of the current generator, the degree of the current polynomial being
+ * used, and the separation between the two pointers. Note that for efficiency
+ * of random(), we remember the first location of the state information, not
+ * the zeroeth. Hence it is valid to access state[-1], which is used to
+ * store the type of the R.N.G. Also, we remember the last location, since
+ * this is more efficient than indexing every time to find the address of
+ * the last element to see if the front and rear pointers have wrapped.
+ */
+static int *state = &randtbl[1];
+static int rand_type = TYPE_3;
+static int rand_deg = DEG_3;
+static int rand_sep = SEP_3;
+static int *end_ptr = &randtbl[DEG_3 + 1];
+
+/*
+ * srandom:
+ *
+ * Initialize the random number generator based on the given seed. If the
+ * type is the trivial no-state-information type, just remember the seed.
+ * Otherwise, initializes state[] based on the given "seed" via a linear
+ * congruential generator. Then, the pointers are set to known locations
+ * that are exactly rand_sep places apart. Lastly, it cycles the state
+ * information a given number of times to get rid of any initial dependencies
+ * introduced by the L.C.R.N.G. Note that the initialization of randtbl[]
+ * for default usage relies on values produced by this routine.
+ */
+static void
+srandom_unlocked(unsigned int x)
+{
+ int i;
+
+ if (rand_type == TYPE_0)
+ state[0] = x;
+ else {
+ state[0] = x;
+ for (i = 1; i < rand_deg; i++) {
+ int x1, hi, lo, t;
+
+ /*
+ * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
+ * From "Random number generators: good ones are hard
+ * to find", Park and Miller, Communications of the ACM,
+ * vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ x1 = state[i - 1];
+ hi = x1 / 127773;
+ lo = x1 % 127773;
+ t = 16807 * lo - 2836 * hi;
+ if (t <= 0)
+ t += 0x7fffffff;
+ state[i] = t;
+ }
+ fptr = &state[rand_sep];
+ rptr = &state[0];
+ for (i = 0; i < 10 * rand_deg; i++)
+ (void)random_unlocked();
+ }
+}
+
+void
+nbsrandom(unsigned int x)
+{
+
+ NBRANDOM_LOCK();
+ srandom_unlocked(x);
+ NBRANDOM_UNLOCK();
+}
+
+/*
+ * initstate:
+ *
+ * Initialize the state information in the given array of n bytes for future
+ * random number generation. Based on the number of bytes we are given, and
+ * the break values for the different R.N.G.'s, we choose the best (largest)
+ * one we can and set things up for it. srandom() is then called to
+ * initialize the state information.
+ *
+ * Note that on return from srandom(), we set state[-1] to be the type
+ * multiplexed with the current value of the rear pointer; this is so
+ * successive calls to initstate() won't lose this information and will be
+ * able to restart with setstate().
+ *
+ * Note: the first thing we do is save the current state, if any, just like
+ * setstate() so that it doesn't matter when initstate is called.
+ *
+ * Returns a pointer to the old state.
+ *
+ * Note: The Sparc platform requires that arg_state begin on an int
+ * word boundary; otherwise a bus error will occur. Even so, lint will
+ * complain about mis-alignment, but you should disregard these messages.
+ */
+char *
+nbinitstate(
+ unsigned int seed, /* seed for R.N.G. */
+ char *arg_state, /* pointer to state array */
+ size_t n) /* # bytes of state info */
+{
+ void *ostate = (void *)(&state[-1]);
+ int *int_arg_state;
+
+ int_arg_state = (int *)(void *)arg_state;
+
+ NBRANDOM_LOCK();
+ if (rand_type == TYPE_0)
+ state[-1] = rand_type;
+ else
+ state[-1] = MAX_TYPES * (int)(rptr - state) + rand_type;
+ if (n < BREAK_0) {
+ NBRANDOM_UNLOCK();
+ return NULL;
+ } else if (n < BREAK_1) {
+ rand_type = TYPE_0;
+ rand_deg = DEG_0;
+ rand_sep = SEP_0;
+ } else if (n < BREAK_2) {
+ rand_type = TYPE_1;
+ rand_deg = DEG_1;
+ rand_sep = SEP_1;
+ } else if (n < BREAK_3) {
+ rand_type = TYPE_2;
+ rand_deg = DEG_2;
+ rand_sep = SEP_2;
+ } else if (n < BREAK_4) {
+ rand_type = TYPE_3;
+ rand_deg = DEG_3;
+ rand_sep = SEP_3;
+ } else {
+ rand_type = TYPE_4;
+ rand_deg = DEG_4;
+ rand_sep = SEP_4;
+ }
+ state = (int *) (int_arg_state + 1); /* first location */
+ end_ptr = &state[rand_deg]; /* must set end_ptr before srandom */
+ srandom_unlocked(seed);
+ if (rand_type == TYPE_0)
+ int_arg_state[0] = rand_type;
+ else
+ int_arg_state[0] = MAX_TYPES * (int)(rptr - state) + rand_type;
+ NBRANDOM_UNLOCK();
+ return (char *)ostate;
+}
+
+/*
+ * setstate:
+ *
+ * Restore the state from the given state array.
+ *
+ * Note: it is important that we also remember the locations of the pointers
+ * in the current state information, and restore the locations of the pointers
+ * from the old state information. This is done by multiplexing the pointer
+ * location into the zeroeth word of the state information.
+ *
+ * Note that due to the order in which things are done, it is OK to call
+ * setstate() with the same state as the current state.
+ *
+ * Returns a pointer to the old state information.
+ *
+ * Note: The Sparc platform requires that arg_state begin on a long
+ * word boundary; otherwise a bus error will occur. Even so, lint will
+ * complain about mis-alignment, but you should disregard these messages.
+ */
+char *
+nbsetstate(char *arg_state) /* pointer to state array */
+{
+ int *new_state;
+ int type;
+ int rear;
+ void *ostate = (void *)(&state[-1]);
+
+ new_state = (int *)(void *)arg_state;
+ type = (int)(new_state[0] % MAX_TYPES);
+ rear = (int)(new_state[0] / MAX_TYPES);
+
+ NBRANDOM_LOCK();
+ if (rand_type == TYPE_0)
+ state[-1] = rand_type;
+ else
+ state[-1] = MAX_TYPES * (int)(rptr - state) + rand_type;
+ switch(type) {
+ case TYPE_0:
+ case TYPE_1:
+ case TYPE_2:
+ case TYPE_3:
+ case TYPE_4:
+ rand_type = type;
+ rand_deg = degrees[type];
+ rand_sep = seps[type];
+ break;
+ default:
+ NBRANDOM_UNLOCK();
+ return NULL;
+ }
+ state = (int *) (new_state + 1);
+ if (rand_type != TYPE_0) {
+ rptr = &state[rear];
+ fptr = &state[(rear + rand_sep) % rand_deg];
+ }
+ end_ptr = &state[rand_deg]; /* set end_ptr too */
+ NBRANDOM_UNLOCK();
+ return (char *)ostate;
+}
+
+/*
+ * random:
+ *
+ * If we are using the trivial TYPE_0 R.N.G., just do the old linear
+ * congruential bit. Otherwise, we do our fancy trinomial stuff, which is
+ * the same in all the other cases due to all the global variables that have
+ * been set up. The basic operation is to add the number at the rear pointer
+ * into the one at the front pointer. Then both pointers are advanced to
+ * the next location cyclically in the table. The value returned is the sum
+ * generated, reduced to 31 bits by throwing away the "least random" low bit.
+ *
+ * Note: the code takes advantage of the fact that both the front and
+ * rear pointers can't wrap on the same call by not testing the rear
+ * pointer if the front one has wrapped.
+ *
+ * Returns a 31-bit random number.
+ */
+static long
+random_unlocked(void)
+{
+ int i;
+ int *f, *r;
+
+ if (rand_type == TYPE_0) {
+ i = state[0];
+ state[0] = i = (i * 1103515245 + 12345) & 0x7fffffff;
+ } else {
+ /*
+ * Use local variables rather than static variables for speed.
+ */
+ f = fptr; r = rptr;
+ *f += *r;
+ /* chucking least random bit */
+ i = ((unsigned int)*f >> 1) & 0x7fffffff;
+ if (++f >= end_ptr) {
+ f = state;
+ ++r;
+ }
+ else if (++r >= end_ptr) {
+ r = state;
+ }
+
+ fptr = f; rptr = r;
+ }
+ return i;
+}
+
+long
+nbrandom(void)
+{
+ long r;
+
+ NBRANDOM_LOCK();
+ r = random_unlocked();
+ NBRANDOM_UNLOCK();
+ return r;
+}
+
+/* These are only so we avoid them */
+long
+random(void)
+{
+
+ abort();
+ /* NOTREACHED */
+ return -1;
+}
+
+/* ARGSUSED */
+void
+srandom(unsigned int seed)
+{
+
+ abort();
+ /* NOTREACHED */
+}
+
+/* ARGSUSED */
+char *
+initstate(unsigned int i, char *c, size_t s)
+{
+
+ abort();
+ /* NOTREACHED */
+ return NULL;
+}
+
+/* ARGSUSED */
+char *
+#ifdef __MACH__
+setstate(const char *c)
+#else
+setstate(char *c)
+#endif
+{
+
+ abort();
+ /* NOTREACHED */
+ return NULL;
+}
+
--- /dev/null
+#ifndef __NBRANDOM_H__
+#define __NBRANDOM_H__
+
+#include <sys/types.h>
+
+extern long nbrandom(void);
+extern void nbsrandom(unsigned int);
+extern char *nbinitstate(unsigned int, char *, size_t);
+extern char *nbsetstate(char *);
+
+extern long random(void);
+extern void srandom(unsigned int);
+extern char *initstate(unsigned int, char *, size_t);
+#ifdef __MACH__
+extern char *setstate(const char *);
+#else
+extern char *setstate(char *);
+#endif
+
+#endif
/*
* Copyright (c) 2023, Matthew Mondor
- * ALL RIGHTS RESERVED.
+ * All rights reserved.
*
- * Uses curses(3) for initialization and to decode user input sequences but
- * relies on terminfo(3) for the animation to avoid unnecessary overhead.
- * Puts the terminal in raw and noecho mode then uses blocking reads that
- * regularly get interrupted, returning ERR when animation clock tick
- * signals occur.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
*
- * Note that on NetBSD and Linux curses endwin(3) was enough to properly
- * restore the terminal state on exit, but not on OSX. savetty(3)/resetty(3)
- * using attempts did not solve the problem, so despite the redundancy
- * resorting to explicit termios use was the solution.
- *
- * tiparm(3) also seemed to be missing in OSX curses(3), but it has the
- * equivalent older tparm(3). For that to work on NetBSD without needing to
- * supply all parameters per X/Open, TPARM_VARARGS was defined to provide OSX
- * and Linux ncurses default behavior using varars.
- *
- * On NetBSD, both -lcurses and -lterminfo are required. On Linux and OSX,
- * -lcurses suffices, terminfo being part of ncurses.
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
#include <curses.h>
#include <errno.h>
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <term.h>
-#include <termios.h>
#include <unistd.h>
#include <screen.h>
#define ERR_BUFSIZE 1024
-static void ti_seq(ti_seq_t *, const char *);
static void screen_cleanup(void);
+static inline int int_min(int, int);
/* Error to display after screen cleanup if any */
static char error_buffer[ERR_BUFSIZE];
static int error_errno = 0;
-/* To explicitly save/restore tty(4) state despite curses(3) */
-static struct termios old_tios;
static bool curses_initialized = false;
-static bool terminfo_initialized = false;
static WINDOW *screen_window = NULL;
-/* Terminfo/termcap preloaded sequences */
-ti_seq_t ti_clear, ti_sgr0, ti_cup, ti_rev, ti_civis, ti_cnorm,
- ti_setaf, ti_setab;
void
screen_init(void)
{
- int i;
/* No error yet */
*error_buffer = '\0';
/* Setup curses */
- /* Remember tty(4) state we'll restore explicitly */
- if (tcgetattr(STDIN_FILENO, &old_tios) == -1)
- err(EXIT_FAILURE, "tcgetattr()");
/* Exit cleanup hook to restore normal terminal */
(void)atexit(screen_cleanup);
curses_initialized = true;
/* Use screen_error() from now on for errors */
- if (COLS < TERM_COLUMNS || LINES < TERM_LINES)
- screen_error("The terminal must support at least\r\n"
- "%d columns and %d lines for this program to work.",
+ if (COLS < TERM_COLUMNS || LINES < TERM_LINES) {
+ errno = 0;
+ screen_error("For this program to work the terminal must "
+ "support at least:\r\n"
+ "%d columns and %d lines.",
TERM_COLUMNS, TERM_LINES);
+ }
/* XXX Maybe add error checking here */
- (void)raw();
+ (void)cbreak();
(void)noecho();
(void)keypad(screen_window, TRUE);
- (void)timeout(-1);
- (void)clear();
- (void)refresh();
-
- /* Query terminfo for the sequences we need. */
- if (setupterm(NULL, STDIN_FILENO, &i) == -1 || i != 1)
- screen_error("setupterm()");
- ti_seq(&ti_clear, "clear"); /* Clear screen */
- ti_seq(&ti_sgr0, "sgr0"); /* Reset text attributes */
- ti_seq(&ti_cup, "cup"); /* Position cursor */
- ti_seq(&ti_rev, "rev"); /* Enable reverse text attribute */
- ti_seq(&ti_civis, "civis"); /* Invisible cursor */
- ti_seq(&ti_cnorm, "cnorm"); /* Normal cursor */
- ti_seq(&ti_setaf, "setaf"); /* Set foreground color attribute */
- ti_seq(&ti_setab, "setab"); /* Set background color attribute */
-
- /* Initialize screen, hide cursor */
- ti_put(&ti_clear);
- ti_put(&ti_civis);
- (void)fflush(stdout);
- terminfo_initialized = true;
+ (void)nonl();
+ (void)intrflush(stdscr, FALSE);
+ (void)timeout(0);
+
+ /*
+ * Enable hardware features if available.
+ * Apparently sometimes beneficial other times not.
+ * XXX Could be a command line option/configuration.
+ */
+ /*
+ if (has_ic())
+ idcok(screen_window, TRUE);
+ if (has_il())
+ idlok(screen_window, TRUE);
+ */
+
+ /* Disable cursor */
+ (void)curs_set(0);
+ (void)leaveok(screen_window, TRUE);
+
+ (void)erase();
+ (void)doupdate();
}
static void
screen_cleanup(void)
{
- if (terminfo_initialized) {
- ti_put(&ti_cnorm);
- ti_put(&ti_sgr0);
- }
-
- if (curses_initialized)
+ if (curses_initialized) {
+ (void)curs_set(1);
(void)endwin();
-
- /* Restore original tty(4) state we saved at init */
- (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_tios);
+ curses_initialized = false;
+ }
/* Display stored fatal error if needed */
if (*error_buffer != '\0') {
return wgetch(screen_window);
}
+int
+screen_getchar_block(void)
+{
+ int c;
+
+ if (!curses_initialized)
+ return ERR;
+
+ (void)curs_set(1);
+ (void)timeout(-1);
+
+ c = wgetch(screen_window);
+
+ (void)timeout(0);
+ (void)curs_set(0);
+
+ return c;
+}
+
/* Queue error to be displayed after screen cleanup then exit */
void
screen_error(const char *fmt, ...)
exit(EXIT_FAILURE);
}
-/* Query terminfo and prepare sequence */
-static void
-ti_seq(ti_seq_t *seq, const char *name)
-{
- char *s;
-
- s = tigetstr(name);
-
- /* -1 means unrecognized name, 0 means no definition */
- if (s == (char *)-1 || s == (char *)0)
- screen_error(
- "tigetstr(): failed to query terminfo for required "
- "capability '%s'", name);
-
- seq->seq = s;
- seq->len = strlen(s);
-}
-
-/* Use prepared terminfo sequence */
-void
-ti_put(ti_seq_t *seq)
-{
-
- (void)fwrite(seq->seq, seq->len, 1, stdout);
-}
-
-
-/* Misc. terminfo/termcap utilities */
-
-void
-ti_goto(int x, int y)
+static inline int
+int_min(int a, int b)
{
- const char *s;
- s = tparm(ti_cup.seq, y, x);
- (void)fwrite(s, strlen(s), 1, stdout);
+ return (a < b ? a : b);
}
+/* 2d text shape blitting routine */
void
-ti_fgcolor(int c)
+screen_blitpad(WINDOW *pad, int y, int x)
{
- const char *s;
-
- s = tparm(ti_setaf.seq, c);
- (void)fwrite(s, strlen(s), 1, stdout);
-}
+ int xs, ys, xo, yo;
-void
-ti_bgcolor(int c)
-{
- const char *s;
+ getmaxyx(pad, ys, xs);
+ xo = xs / 2;
+ yo = ys / 2;
- s = tparm(ti_setab.seq, c);
- (void)fwrite(s, strlen(s), 1, stdout);
+ (void)copywin(pad, screen_window, 0, 0, y - yo, x - xo,
+ int_min((y - yo) + ys - 1, TERM_LINES - 1),
+ int_min((x - xo) + xs - 1, TERM_COLUMNS - 1),
+ TRUE);
}
/*
* Copyright (c) 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2023, Matthew Mondor
* ALL RIGHTS RESERVED.
*/
-
#ifndef __SCREEN_H__
#define __SCREEN_H__
-#include <config.h>
-
-
-/* Prepared terminfo sequence */
-typedef struct ti_seq {
- const char *seq;
- size_t len;
-} ti_seq_t;
-
+#include <curses.h>
-extern void screen_init(void);
-extern int screen_getchar(void);
-extern void screen_error(const char *, ...);
-
-extern void ti_put(ti_seq_t *);
-extern void ti_goto(int, int);
-extern void ti_fgcolor(int);
-extern void ti_bgcolor(int);
+#include <config.h>
-extern ti_seq_t ti_clear, ti_sgr0, ti_cup, ti_rev, ti_civis, ti_cnorm,
- ti_setaf, ti_setab;
+void screen_init(void);
+int screen_getchar(void);
+void screen_error(const char *, ...);
+void screen_blitpad(WINDOW *, int, int);
#endif
--- /dev/null
+# Shapes intended to be easily modifiable.
+# Format:
+# - Lines starting with # are comments.
+# - Blank lines delimit shapes.
+# - All shapes must have T lines, defining their characters.
+# - Optional are F and B lines, for foreground and background colors,
+# respectively (0-7, space for default).
+# - Also optional are A lines, for attributes:
+# - E bold
+# - I inverse
+# - U underline
+# - B blink
+# - Space for normal text
+# - A space is required after T F B A lines before the data.
+# - F B A lines can only occur for already defined shapes with T and must hold
+# the same amount of lines than for T.
+T /\
+T |__|
+T | |
+F
+F 11
+F
+
+T __
+T |__>
+T |__>
+F
+F 22
+F
+
+# https://www.asciiart.eu/vehicles/boats
+T |~
+T |/ w
+T / ( (| \
+T /( (/ |) |\
+T ____ ( (/ (| | ) ,
+T |----\ (/ | /| |'\ /^;
+T \---*---Y--+-----+---+--/(
+T \------*---*--*---*--/
+T '~~ ~~~~~~~~~~~~~~~
+F 24
+F 2 3
+F 2
+F 2 2
+F 2 2 2
+F 1111 2 2 2 2
+F 333333333333333333333332
+F 33333333333333333333
+F
+B 6
+B
+B
+B
+B
+B
+B
+B 000000 000 00 000 00
+B
+A B
+A
+A
+A
+A
+A
+A E
+A E I E E
+A
--- /dev/null
+/*
+ * Copyright (c) 2015, 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Simple implementation of dynamically grown reusable arrays with fast
+ * population and reset. Using intptr_t it is generic and can be used for
+ * numbers or already allocated objects by supplying their pointer.
+ * Any memory allocation error is fatal in the current implementation.
+ * It was used in games, where with typical usage for per-frame processing
+ * allocations were rare. The fast reset implies that it's used for frequent
+ * temporary collection, use and flushing, where it suits better the needed
+ * algorithm than other data structures like FIFO/LIFO queues or linked
+ * lists. Limits are typically known in advance and growing is usually not
+ * needed, but is there for convenience.
+ */
+
+#include <stdlib.h>
+
+#include <mmrarray.h>
+
+
+static void rarray_grow(rarray_t *);
+
+
+void
+rarray_init(rarray_t *array, int isize)
+{
+
+ if ((array->array = malloc(sizeof(intptr_t) * isize)) == NULL)
+ abort(); /* XXX Fatal */
+ array->size = isize;
+ array->count = 0;
+}
+
+/* Very efficiently empty array */
+inline void
+rarray_reset(rarray_t *array)
+{
+
+ array->count = 0;
+}
+
+/* Grow buffer * 2 */
+static void
+rarray_grow(rarray_t *array)
+{
+ intptr_t *narray;
+
+ array->size *= 2;
+ if ((narray = realloc(array->array, array->size * sizeof(intptr_t)))
+ == NULL)
+ abort(); /* XXX Fatal */
+ array->array = narray;
+}
+
+/* Append new object to array, growing it internally if required */
+inline void
+rarray_append(rarray_t *array, intptr_t obj)
+{
+
+ /* Grow if full */
+ if (array->count == array->size)
+ rarray_grow(array);
+
+ /* Insert */
+ array->array[array->count++] = obj;
+}
+
+/* Free the internal array, disregarding contents. */
+void
+rarray_destroy(rarray_t *array)
+{
+
+ if (array->array != NULL) {
+ free(array->array);
+ array->array = NULL;
+ }
+ array->count = array->size = 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015, 2023, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MMRARRAY_H__
+#define __MMRARRAY_H__
+
+
+#include <stdint.h>
+#include <stdlib.h>
+
+
+typedef struct rarray {
+ intptr_t *array;
+ int size, count;
+} rarray_t;
+
+
+void rarray_init(rarray_t *, int);
+void rarray_reset(rarray_t *);
+void rarray_append(rarray_t *, intptr_t);
+void rarray_destroy(rarray_t *);
+
+
+#endif