ref: d540dcf6834b4bec8a2d21f2fe95eccf49f97b03
dir: /appl/spree/engines/bounce.b/
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;
}