code: 9ferno

ref: b5959ac65bcd0d673e4e850f5a1dced6742536a8
dir: /appl/spree/engines/bounce.b/

View raw version
implement Gatherengine;

include "sys.m";
	sys: Sys;
include "draw.m";
	draw: Draw;
	Point, Rect: import draw;
include "sets.m";
	sets: Sets;
	Set, All, None, A, B: import sets;
include "../spree.m";
	spree: Spree;
	Attributes, Range, Object, Clique, Member: import spree;
include "../gather.m";

clique: ref Clique;

W, H: con 500;
INSET: con 20;
D: con 30;
BATLEN: con 100.0;
GOALSIZE: con 0.1;

MAXPLAYERS: con 32;
nmembers := 0;

Line: adt {
	p1, p2: Point;
	seg: fn(l: self Line, s1, s2: real): Line;
};

Dmember: adt {
	p:		ref Member;
	score:	int;
	bat:		ref Object;
};

Eusage: con "bad command usage";
colours := array[4] of {"blue", "orange", "yellow", "white"};
batpos: array of Line;
borderpos: array of Line;

members: array of Dmember;
arena: ref Object;
clienttype(): string
{
	return "bounce";
}

maxmembers(): int
{
	return 4;
}

readfile(nil: int, nil: big, nil: int): array of byte
{
	return nil;
}

init(srvmod: Spree, g: ref Clique, nil: list of string, nil: int): string
{
	sys = load Sys Sys->PATH;
	draw = load Draw Draw->PATH;
	clique = g;
	spree = srvmod;

	sets = load Sets Sets->PATH;
	if (sets == nil) {
		sys->print("spit: cannot load %s: %r\n", Sets->PATH);
		return "bad module";
	}
	sets->init();

	r := Rect((0, 0), (W, H));
	walls := sides(r.inset(INSET));
	addlines(segs(walls, 0.0, 0.5 - GOALSIZE), nil);
	addlines(segs(walls, 0.5 + GOALSIZE, 1.0), nil);

	batpos = l2a(segs(sides(r.inset(INSET + 50)), 0.1, 0.9));
	borderpos = l2a(sides(r.inset(-1)));

	arena = clique.newobject(nil, All, "arena");
	arena.setattr("arenasize", string W + " " + string H, All);

	return nil;
}

propose(members: array of string): string
{
	if (len members < 2)
		return "need at least two members";
	if (len members > 4)
		return "too many members";
	return nil;
}

archive()
{
}		

start(pl: array of ref Member, archived: int)
{
	if (archived) {
	} else {
		members = array[len pl] of Dmember;
		for (i := 0; i < len pl; i++) {
			p := pl[i];
			bat := addline(batpos[i], nil);
			bat.setattr("pos", "10 " + string (10.0 + BATLEN), All);
			bat.setattr("owner", p.name, All);
			addline(borderpos[i], ("owner", p.name) :: nil);
			arena.setattr("member" + string i, p.name + " " + colours[i], All);
			members[i] = (p, 0, bat);
		}
		r := Rect((0, 0), (W, H)).inset(INSET + 1);
		goals := l2a(sides(r));
		for (i = len members; i < len batpos; i++) {
			addline(goals[i], nil);
			addline(borderpos[i], ("owner", pl[0].name) :: nil);
		}
	}
}

addline(lp: (Point, Point), attrs: list of (string, string)): ref Object
{
	(p1, p2) := lp;
	l := clique.newobject(nil, All, "line");
	l.setattr("coords", p2s(p1) + " " + p2s(p2), All);
	l.setattr("id", string l.id, All);
	for (; attrs != nil; attrs = tl attrs) {
		(attr, val) := hd attrs;
		l.setattr(attr, val, All);
	}
	return l;
}


p2s(p: Point): string
{
	return string p.x + " " + string p.y;
}

command(member: ref Member, cmd: string): string
{
	ord := order(member);
	sys->print("cmd: %s", cmd);
	{
		(n, toks) := sys->tokenize(cmd, " \n");
		assert(n > 0, "unknown command");
		case hd toks {
		"newball" =>
			# newball batid p.x p.y v.x v.y speed
			assert(n == 7, Eusage);
			bat := member.obj(int hd tl toks);
			assert(bat != nil, "no such bat");
			ball := clique.newobject(nil, All, "ball");
			ball.setattr("state", string bat.id +  " " + string ord +
				" " + concat(tl tl toks) + " " + string sys->millisec(), All);
		"lost" =>
			# lost ballid
			assert(n == 2, Eusage);
			o := member.obj(int hd tl toks);
			assert(o != nil, "bad object");
			assert(o.getattr("state") != nil, "can only lose balls");
			o.delete();
		"state" =>
			# state ballid lasthit owner p.x p.y v.x v.y s time
			assert(n == 10, Eusage);
			assert(ord >= 0, "you are not playing");
			o := member.obj(int hd tl toks);
			assert(o != nil, "object does not exist");
			o.setattr("state", concat(tl tl toks), All);
			members[ord].score++;
			arena.setattr("score" + string ord, string members[ord].score, All);
		"bat" =>
			# bat pos
			assert(n == 2, Eusage);
			s1 := real hd tl toks;
			members[ord].bat.setattr("pos", hd tl toks + " " + string (s1 + BATLEN), All);
		"time" =>
			# time millisec
			assert(n == 2, Eusage);
			tm := int hd tl toks;
			offset := sys->millisec() - tm;
			clique.action("time " + string offset + " " + string tm, nil, nil, None.add(member.id));
		* =>
			assert(0, "bad command");
		}
	} exception e {
	"parse:*" =>
		return e[6:];
	}
	return nil;
}

order(p: ref Member): int
{
	for (i := 0; i < len members; i++)
		if (members[i].p == p)
			return i;
	return -1;
}

assert(b: int, err: string)
{
	if (b == 0)
		raise "parse:" + err;
}

concat(v: list of string): string
{
	if (v == nil)
		return nil;
	s := hd v;
	for (v = tl v; v != nil; v = tl v)
		s += " " + hd v;
	return s;
}

Line.seg(l: self Line, s1, s2: real): Line
{
	(dx, dy) := (l.p2.x - l.p1.x, l.p2.y - l.p1.y);
	return (((l.p1.x + int (s1 * real dx)), l.p1.y + int (s1 * real dy)),
			((l.p1.x + int (s2 * real dx)), l.p1.y + int (s2 * real dy)));
}

sides(r: Rect): list of Line
{
	return ((r.min.x, r.min.y), (r.min.x, r.max.y)) ::
		((r.max.x, r.min.y), (r.max.x, r.max.y)) ::
		((r.min.x, r.min.y), (r.max.x, r.min.y)) ::
		((r.min.x, r.max.y), (r.max.x, r.max.y)) :: nil;
}

addlines(ll: list of Line, attrs: list of (string, string))
{
	for (; ll != nil; ll = tl ll)
		addline(hd ll, attrs);
}

segs(ll: list of Line, s1, s2: real): list of Line
{
	nll: list of Line;
	for (; ll != nil; ll = tl ll)
		nll = (hd ll).seg(s1, s2) :: nll;
	ll = nil;
	for (; nll != nil; nll = tl nll)
		ll = hd nll :: ll;
	return ll;
}

l2a(ll: list of Line): array of Line
{
	a := array[len ll] of Line;
	for (i := 0; ll != nil; ll = tl ll)
		a[i++] = hd ll;
	return a;
}