git: 9front

Download patch

ref: 057ac29e3d28fff9b19ce20435e8b434bb80e7b0
parent: 826b2891b7cfff2e85fcfe70be64544e22ef3ef3
author: rodri <rgl@antares-labs.eu>
date: Wed Sep 10 16:12:35 EDT 2025

new game: rotzoomer

--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -23,6 +23,7 @@
 	turtle\
 	vocenc\
 	vocdec\
+	rotzoomer\
 
 OFILES=
 HFILES=
--- /dev/null
+++ b/sys/src/games/rotzoomer.c
@@ -1,0 +1,381 @@
+/*
+ * rotzoomer - an affinewarp demo
+ */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+
+typedef struct Strobos Strobos;
+typedef struct Backlight Backlight;
+
+struct Strobos
+{
+	Point p;
+	Point txtsz;
+	Image *c;
+	Image *back;
+	Image *txtim;
+	char *txt;
+	Warp txtwp;
+};
+
+struct Backlight
+{
+	Image *c;	/* bag of colors */
+	Image *b;	/* chosen one */
+	Warp w;		/* roulette */
+};
+
+Rectangle UR = {0,0,1,1};
+
+Image *bg;
+Backlight *bl;
+Image *sprite;
+Image *screenb;
+Mousectl *mctl;
+Keyboardctl *kctl;
+Channel *drawc;
+Warp warp;
+int nframes;
+Strobos **strobostab;
+int nstrobos;
+int smooth;
+
+void *
+emalloc(ulong size)
+{
+	void *p;
+
+	p = malloc(size);
+	if(p == nil)
+		sysfatal("malloc: %r");
+	setmalloctag(p, getcallerpc(&size));
+	return p;
+}
+
+Image *
+eallocimage(Display *disp, Rectangle r, ulong chan, int repl, ulong col)
+{
+	Image *i;
+
+	i = allocimage(disp, r, chan, repl, col);
+	if(i == nil)
+		sysfatal("allocimage: %r");
+	setmalloctag(i, getcallerpc(&disp));
+	return i;
+}
+
+Image *
+allocbasketweave(void)
+{
+	static uchar pattern[] = {
+		0x07, 0x8B, 0xDD, 0xB8,
+		0x70, 0xE8, 0xDD, 0x8E,
+	};
+	Image *i;
+
+	i = eallocimage(display, Rect(0,0,8,8), GREY1, 1, DNofill);
+	if(loadimage(i, i->r, pattern, 8) != 8)
+		sysfatal("loadimage: %r");
+	return i;
+}
+
+Backlight *
+allocbacklight(void)
+{
+	static ulong pattern[] = {
+		0x888888, 0xDDDDDD,
+		0xBBBBBB, 0xFFFFFF,
+	};
+	Backlight *b;
+
+	b = emalloc(sizeof(Backlight));
+	b->c = eallocimage(display, Rect(0,0,2,2), XRGB32, 1, DNofill);
+	b->b = eallocimage(display, UR, XRGB32, 1, DNofill);
+	if(loadimage(b->c, b->c->r, (uchar*)pattern, 2*2*4) != 2*2*4)
+		sysfatal("loadimage: %r");
+	return b;
+}
+
+void
+drawstats(void)
+{
+	static Point sp = {10,10};
+	char s[128];
+
+	snprint(s, sizeof s, "frames %d", nframes);
+	stringbg(screenb, sp, display->white, ZP, font, s, display->black, ZP);
+}
+
+Strobos *
+allocstrobos(char *txt, ulong col)
+{
+	Strobos *s;
+
+	s = emalloc(sizeof(Strobos));
+	s->c = eallocimage(display, UR, RGBA32, 1, col);
+	s->txt = txt;
+	s->txtsz = stringsize(font, txt);
+	s->txtim = eallocimage(display, Rpt(ZP, s->txtsz), RGBA32, 0, DTransparent);
+	string(s->txtim, ZP, s->c, ZP, font, txt);
+	s->back = eallocimage(display, Rpt(ZP, mulpt(s->txtsz, 4)), RGBA32, 0, DTransparent);
+	return s;
+}
+
+void
+initstrobos(void)
+{
+	static struct {
+		char *lbl;
+		ulong col;
+	} tab[] = {
+		"MAKE ME AN OFFER", DPaleyellow,
+		"ONE HUNDRED BILLION DOLLARS", DYellow,
+		"THE PRICEMASTER HAS SPOKEN", DDarkyellow,
+	};
+	int i;
+
+	nstrobos = nelem(tab);
+	strobostab = emalloc(nstrobos*sizeof(Strobos *));
+	for(i = 0; i < nstrobos; i++)
+		strobostab[i] = allocstrobos(tab[i].lbl, setalpha(tab[i].col, 0x7F));
+}
+
+void
+drawstrobos(Strobos *s)
+{
+	double ss, θ;
+
+	if(nframes % 75 == 0){	/* 2½ seconds */
+		s->p.x = 50 + frand()*(Dx(screenb->r)-50-50 - Dx(s->back->r));
+		s->p.y = 50 + frand()*(Dy(screenb->r)-50-50 - Dy(s->back->r));
+	}
+
+	if((nframes & 1) == 0){	/* 1/15 second */
+		ss = (frand() + 0.2)*(4/1.2);
+		θ = (frand()*10 - 10/2)*DEG;
+		Matrix S = {
+			ss, 0, 0,
+			0, ss, 0,
+			0, 0, 1,
+		}, R = {
+			cos(θ), -sin(θ), 0,
+			sin(θ), cos(θ), 0,
+			0, 0, 1,
+		}, T₀ = {
+			1, 0, -s->txtsz.x/2,
+			0, 1, -s->txtsz.y/2,
+			0, 0, 1,
+		}, T₁ = {
+			1, 0, s->txtsz.x/2,
+			0, 1, s->txtsz.y/2,
+			0, 0, 1,
+		};
+		mulm(R, T₀);
+		mulm(T₁, R);
+		mulm(S, T₁);
+		mkwarp(s->txtwp, S);
+	}
+
+	affinewarp(s->back, s->back->r, s->txtim, s->txtim->r.min, s->txtwp, smooth);
+	draw(screenb, rectaddpt(s->back->r, addpt(screenb->r.min, s->p)), s->back, nil, ZP);
+}
+
+void
+redraw(void)
+{
+	Rectangle cr0;
+	int i;
+
+	rlockdisplay(display);
+	affinewarp(bl->b, bl->b->r, bl->c, ZP, bl->w, 0);
+	draw(screenb, screenb->r, bl->b, bg, ZP);
+	cr0 = screenb->clipr;
+	replclipr(screenb, 0, insetrect(screenb->r, 50));
+	affinewarp(screenb, screenb->clipr, sprite, sprite->r.min, warp, smooth);
+	replclipr(screenb, 0, cr0);
+	drawstats();
+	for(i = 0; i < nstrobos; i++)
+		drawstrobos(strobostab[i]);
+	draw(screen, screen->r, screenb, nil, ZP);
+	flushimage(display, 1);
+	runlockdisplay(display);
+}
+
+void
+drawproc(void*)
+{
+	threadsetname("drawproc");
+
+	for(;;){
+		recv(drawc, nil);
+		redraw();
+		nframes++;
+	}
+}
+
+void
+update(double f)
+{
+	Point t;
+	double c, s, ss;
+
+	c = cos(f);
+	s = sin(f);
+	ss = sin(f*2 + 10);
+	t.x = s*Dx(sprite->r)/2;
+	t.y = 0;
+	Matrix R = {
+		c, -s, 0,
+		s,  c, 0,
+		0,  0, 1,
+	}, S = {
+		ss,  0, 0,
+		 0, ss, 0,
+		 0,  0, 1,
+	}, T₀ = {
+		1, 0, -Dx(sprite->r)/2,
+		0, 1, -Dy(sprite->r)/2,
+		0, 0, 1,
+	}, T₁ = {
+		1, 0, Dx(sprite->r)/2,
+		0, 1, Dy(sprite->r)/2,
+		0, 0, 1,
+	}, T = {
+		1, 0, t.x,
+		0, 1, t.y,
+		0, 0, 1,
+	};
+	mulm(S, T₀);
+	mulm(R, S);
+	mulm(T₁, R);
+	mulm(T, T₁);
+	mkwarp(warp, T);
+
+	/* spin it! */
+	t.x = Dx(bl->b->r)/2;
+	t.y = Dy(bl->b->r)/2;
+	Matrix R₁ = {
+		c, -s, 0,
+		s,  c, 0,
+		0,  0, 1,
+	};
+	T₀[0][2] = -t.x;
+	T₀[1][2] = -t.y;
+	mulm(R₁, T₀);
+	mkwarp(bl->w, R₁);
+}
+
+void
+clkproc(void*)
+{
+	uvlong t0, Δt;
+
+	threadsetname("clkproc");
+
+	for(;;){
+		t0 = nsec();
+		update(t0/1e9);
+		nbsend(drawc, nil);
+		Δt = nsec() - t0;
+		if(Δt < 33*1000*1000)
+			sleep(33 - Δt/1000000);
+	}
+}
+
+void
+mouse(void)
+{
+}
+
+void
+key(Rune r)
+{
+	switch(r){
+	case Kdel:
+	case 'q':
+		threadexitsall(nil);
+	}
+}
+
+void
+resize(void)
+{
+	static Point lastsz;
+	Point cursz;
+
+	lockdisplay(display);
+	if(getwindow(display, Refnone) < 0)
+		fprint(2, "can't reattach to window\n");
+	cursz = subpt(screen->r.max, screen->r.min);
+	if(!eqpt(cursz, lastsz)){
+		freeimage(screenb);
+		screenb = eallocimage(display, Rpt(ZP, cursz), screen->chan, 0, DBlack);
+		lastsz = cursz;
+	}
+	unlockdisplay(display);
+	nbsend(drawc, nil);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-s]\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	Rune r;
+
+	ARGBEGIN{
+	case 's':
+		smooth++;
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+
+	setfcr(getfcr() &~ FPINVAL);
+
+	if(initdraw(nil, nil, "rotzoomer") < 0)
+		sysfatal("initdraw: %r");
+	if((mctl = initmouse(nil, screen)) == nil)
+		sysfatal("initmouse: %r");
+	if((kctl = initkeyboard(nil)) == nil)
+		sysfatal("initkeyboard: %r");
+
+	bl = allocbacklight();
+	bg = allocbasketweave();
+	sprite = display->image;
+	screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DBlack);
+	initstrobos();
+
+	unlockdisplay(display);
+	drawc = chancreate(sizeof(void*), 1);
+	if(drawc == nil)
+		sysfatal("chancreate: %r");
+	proccreate(drawproc, nil, mainstacksize);
+	proccreate(clkproc, nil, mainstacksize);
+
+	enum {MOUSE, RESIZE, KEY};
+	Alt a[] = {
+		{mctl->c, &mctl->Mouse, CHANRCV},
+		{mctl->resizec, nil, CHANRCV},
+		{kctl->c, &r, CHANRCV},
+		{nil, nil, CHANEND}
+	};
+	for(;;)
+		switch(alt(a)){
+		case MOUSE: mouse(); break;
+		case RESIZE: resize(); break;
+		case KEY: key(r); break;
+		}
+}
--