1 // Untahris (Common Playground)
2 // copyright (C) 2006 longuens@users.sourceforge.net
3 // Released under GNU General Public License (see the file COPYING)
4 // --------------------------------------------------------------
5 // Castle & Cannonball.
6 
7 #include <math.h>
8 
9 bool flags[128][128];
10 
11 int qs, qe;
12 int qx[20000], qy[20000];
13 
14 int bre;
15 int brx[20000], bry[20000];
16 
17 struct OCannonball : TPlayObject {
18   public:
19   int32 fx, fy, tx, ty, stime;
20 
getOxOCannonball21   int getOx() {
22     return Ctr(fx)+512;
23     }
24 
getOyOCannonball25   int getOy() {
26     return Ctr(fy)+512;
27     }
28 
OCannonballOCannonball29   OCannonball(int _owner, int _fx, int _fy, int _tx, int _ty) :
30     fx(_fx), fy(_fy), tx(_tx), ty(_ty) {
31     owner = _owner;
32     stime = g.time;
33     time = stime + (calcDistance(getOx()-tx, getOy()-ty)*spd()) / 500;
34     g.objects.push_back(this);
35     }
OCannonballOCannonball36   OCannonball(TBuffer &b) {
37     b.get(owner); b.get(time); b.get(stime);
38     b.get(fx); b.get(fy); b.get(tx); b.get(ty);
39     }
saveOCannonball40   void save(TBuffer &b) {
41     b.putChar(7);
42     b.put(owner); b.put(time); b.put(stime);
43     b.put(fx); b.put(fy); b.put(tx); b.put(ty);
44     }
goOCannonball45   void go() {
46     if(g.map[fy][fx] == cCannonA)
47       g.mapSet(fx,fy, cCannonF, g.owner[fy][fx]);
48 
49     for(int o=0; o<size(g.objects); o++) if(g.objects[o])
50       g.objects[o]->reactToCannon(this);
51 
52     destroyAt(DeCtr(tx), DeCtr(ty), owner);
53     g.deleteObject(this);
54     }
drawOCannonball55   void draw() {
56     int x = getOx();
57     int y = getOy();
58     x = x + ((tx-x) * (g.time-stime)) / (time-stime);
59     y = y + ((ty-y) * (g.time-stime)) / (time-stime);
60     DIc(icBall).drawAtCentered(CTG(x), CTG(y));
61     }
62   };
63 
64 struct OCastle : TPlayObject {
65   public:
66   int32 bx, by; // brick coordinates
67   int32 cx, cy; // cannon coordinates
68   int32 tx, ty; // target coordinates
69   int32 fx, fy; // cannon-to-fire coordinates
70   bool brick[3][3];
71   int32 utime;
72   int32 mode;
73 
ref1OCastle74   void ref1() {
75     for(int x=0; x<3; x++) swap(brick[0][x], brick[2][x]);
76     }
77 
ref2OCastle78   void ref2() {
79     for(int x=0; x<3; x++) for(int y=0; y<x; y++)
80       swap(brick[y][x], brick[x][y]);
81     }
82 
createNewBrickOCastle83   bool createNewBrick() {
84     char* bricks[11] = {
85       ".#..#..##",
86       ".#..#..##",
87       ".#..#.##.",
88       ".#.##....",
89       ".#.##....",
90       ".#.##....",
91       ".#..#..#.",
92       ".#..#..#.",
93       "##..#.##.",
94       "##..#.##.",
95       "....#...."};
96 
97     int r = g.rand() % 11;
98     for(int y=0; y<3; y++) for(int x=0; x<3; x++)
99       brick[y][x] = bricks[r][y*3+x] == '#';
100     for(int u=g.rand() % 4; u<4; u++) ref1(), ref2();
101     }
102 
OCastleOCastle103   OCastle(int _owner, int _x, int _y) {
104     owner = _owner;
105     time = g.time;
106     utime= g.time;
107     fx = 0;
108     fy = 0;
109     cx = _x;
110     cy = _y;
111     bx = _x-1;
112     by = _y-1;
113     tx = Ctr(_x);
114     ty = Ctr(_y);
115     mode = 0;
116     createNewBrick();
117     g.objects.push_back(this);
118     }
119 
OCastleOCastle120   OCastle(TBuffer &b) {
121     b.get(owner); b.get(time); b.get(mode);
122     b.get(bx); b.get(by); b.get(cx); b.get(cy); b.get(tx); b.get(ty);
123     b.get(fx); b.get(fy); b.get(utime);
124     for(int ky=0; ky<3; ky++)
125     for(int kx=0; kx<3; kx++) b.get(brick[ky][kx]);
126     }
saveOCastle127   void save(TBuffer &b) {
128     b.putChar(6);
129     b.put(owner); b.put(time); b.put(mode);
130     b.put(bx); b.put(by); b.put(cx); b.put(cy); b.put(tx); b.put(ty);
131     b.put(fx); b.put(fy); b.put(utime);
132     for(int ky=0; ky<3; ky++)
133     for(int kx=0; kx<3; kx++) b.put(brick[ky][kx]);
134     }
135 
checkEnclosedOCastle136   void checkEnclosed(int rx, int ry) {
137     if(!isCastlable(g.map[ry][rx])) return;
138     if(g.owner[ry][rx] == owner) return; // already ours...
139     qs = 0;
140     qe = 1;
141     qx[0] = rx; qy[0] = ry;
142     bool ok = true;
143     while(qs<qe) {
144       int rx = qx[qs];
145       int ry = qy[qs];
146       qs++;
147       if(g.map[ry][rx] == cPermaWall) {ok = false; break;}
148       if(isCastlable(g.map[ry][rx])) {
149         for(int lx=rx-1; lx<=rx+1; lx++)
150         for(int ly=ry-1; ly<=ry+1; ly++) {
151           if(flags[ly][lx]) continue;
152           flags[ly][lx] = true;
153           qx[qe] = lx; qy[qe] = ly;
154           qe++;
155           }
156         }
157       }
158     if(ok) {
159       for(int q=0; q<qe; q++) {
160         rx = qx[q]; ry = qy[q];
161         if(isCastlable(g.map[ry][rx])) g.mapSet(rx,ry, g.map[ry][rx], owner);
162         flags[ry][rx] = false;
163         }
164       }
165     else for(int q=0; q<qe; q++) flags[qy[q]][qx[q]] = false;
166     }
167 
placeBrickOCastle168   void placeBrick() {
169     bool ok = false;
170     for(int ky=0; ky<3; ky++)
171     for(int kx=0; kx<3; kx++) if(brick[ky][kx]) {
172       if(g.map[by+ky][bx+kx] != cFree) return;
173       for(int ly=-2; ly<3; ly++)
174       for(int lx=-2; lx<3; lx++)
175       if(by+ky+ly >= 0 && by+ky+ly < g.sizeY &&
176         bx+kx+lx >= 0 && bx+kx+lx < g.sizeX &&
177         g.owner[by+ky+ly][bx+kx+lx] == owner
178         )
179         ok = true;
180       }
181     if(ok) {
182       d.playSoundAt(d.sndPlace, bx+1, by+1);
183       for(int ky=0; ky<3; ky++)
184       for(int kx=0; kx<3; kx++) if(brick[ky][kx]) {
185         g.mapSet(bx+kx, by+ky, cCastleWall, owner);
186         for(int tx=0; tx<3; tx++)
187         for(int ty=0; ty<3; ty++)
188           checkEnclosed(bx+kx+tx-1, by+ky+ty-1);
189         }
190       }
191     createNewBrick();
192     }
193 
placeCannonOCastle194   void placeCannon() {
195     for(int ky=0; ky<2; ky++)
196     for(int kx=0; kx<2; kx++)
197       if(g.map[cy+ky][cx+kx] != cFree || g.owner[cy+ky][cx+kx] != owner)
198         return;
199     if(ow()->useAmmo(gc.ammoPerCannon)) {
200       d.playSoundAt(d.sndPlace, fx, fy);
201       placeBig(cx, cy, cCannonF, owner);
202       }
203     }
204 
fireCannonOCastle205   void fireCannon() {
206     if(fx == 999 || fy == 999) return;
207     if(g.map[fy][fx] != cCannonF) return;
208     if(g.owner[fy][fx] != owner) return;
209     if(ow()->useAmmo()) {
210       g.mapSet(fx,fy, cCannonA, owner);
211       d.playSoundAt(d.sndShoot, fx, fy);
212       new OCannonball(owner, fx, fy, tx, ty);
213       }
214     }
215 
reactToKeyOCastle216   void reactToKey(PlayerKey pk, bool pressed) {
217     if(!pressed) return;
218     if(pk == keyEarth) {
219       mode++;
220       mode %= 3;
221       }
222     else switch(mode) {
223       case 0:
224         switch(pk) {
225           case keyLeft:
226             if(bx>0) bx--;
227             break;
228           case keyRight:
229             if(bx<g.sizeX-3) bx++;
230             break;
231           case keyDown:
232             if(by<g.sizeY-3) by++;
233             break;
234           case keyUp:
235             if(by>0) by--;
236             break;
237           case keyFire:
238             placeBrick();
239             break;
240           case keyAir:
241             ref1(); ref2();
242             break;
243           case keyWater:
244             ref2(); ref1();
245             break;
246           }
247         break;
248       case 1:
249         switch(pk) {
250           case keyLeft:
251             if(cx>1) cx--;
252             break;
253           case keyRight:
254             if(cx<g.sizeX-2) cx++;
255             break;
256           case keyDown:
257             if(cy<g.sizeY-2) cy++;
258             break;
259           case keyUp:
260             if(cy>1) cy--;
261             break;
262           case keyFire:
263           case keyAir:
264           case keyWater:
265             placeCannon();
266             break;
267           }
268         break;
269       case 2:
270         switch(pk) {
271           case keyFire:
272             fireCannon();
273             break;
274           }
275         break;
276       }
277     }
278 
cannonDistanceOCastle279   int cannonDistance(int x, int y) {
280     return calcDistance(tx-Ctr(x)-512, ty-Ctr(y)-512);
281     }
282 
manageCastleOCastle283   void manageCastle() {
284     int levels=0, scores=0, ammos=0;
285     bre = 0;
286     fx = 999, fy = 999;
287     for(int y=1; y<g.sizeY-1; y++)
288     for(int x=1; x<g.sizeX-1; x++)
289       if(g.owner[y][x] == owner && isCastlable(g.map[y][x])) {
290         if(g.map[y][x] == cBonusLevel) levels++;
291         if(g.map[y][x] == cBonusScore) scores++;
292         if(g.map[y][x] == cBonusAmmo)  ammos++;
293         if(g.map[y][x] == cCannonF) {
294           if(cannonDistance(x,y) < cannonDistance(fx, fy))
295             fx=x, fy=y;
296           }
297         for(int ay=y-1; ay<=y+1; ay++)
298         for(int ax=x-1; ax<=x+1; ax++) {
299           if(isCastlable(g.map[ay][ax]) && g.owner[ay][ax] == 0) {
300             // maybe everything is OK...
301             checkEnclosed(ax, ay);
302             // ... or maybe not
303             if(g.owner[ay][ax] == 0) {
304               brx[bre] = x; bry[bre] = y; bre++;
305               }
306             }
307           }
308         }
309     for(int t=0; t<bre; t++) {
310       int ax = brx[t], ay = bry[t];
311       g.mapSet(ax, ay, g.map[ay][ax], 0);
312       }
313     ow()->level = levels;
314     int32& tv (ow()->typeval);
315     if(scores == 0) {
316       tv -= gc.castleRotSpeed;
317       if(tv <= 0) {
318         // dies without sound
319         killedBy(0);
320         ow()->ammo = 0;
321         return;
322         }
323       }
324     scores *= gc.castlePowerBase;
325     if(tv < scores) {
326       tv += gc.castleRegenSpeed;
327       if(tv > scores) tv = scores;
328       }
329     ow()->ammo += ammos * ec().bonusAmmo;
330     }
331 
goOCastle332   void go() {
333     if(time >= utime) { manageCastle(); utime = time + 200; }
334     if(mode == 2) {
335       int spd = 128;
336       if(ow()->key[keyAir]) spd *= 2;
337       if(ow()->key[keyWater]) spd *= 2;
338       if(ow()->key[keyRight] && tx < g.sizeX*1024 - 2048)
339         tx += spd;
340       if(ow()->key[keyDown] && ty < g.sizeY*1024 - 2048)
341         ty += spd;
342       if(ow()->key[keyLeft] && tx >= 2048)
343         tx -= spd;
344       if(ow()->key[keyUp] && ty >= 2048)
345         ty -= spd;
346       }
347     time += 10;
348     }
349 
drawOCastle350   void draw() {
351     switch(mode) {
352       case 0:
353         for(int y=0; y<3; y++)
354         for(int x=0; x<3; x++)
355         if(brick[y][x]) {
356           DIc(icCastle).drawAtCentered(
357             d.Res * (x+bx) + d.Res/2,
358             d.Res * (y+by) + d.Res/2
359             );
360           }
361         break;
362       case 1:
363         DIc(icCannonP).drawAtCentered(
364           d.Res * cx + d.Res,
365           d.Res * cy + d.Res
366           );
367         break;
368       case 2:
369         DIc(icTarget).drawAtCentered(CTG(tx), CTG(ty));
370         break;
371       }
372     }
373   };
374