/* tree0.c (tree.c) - Tree - Don Yang (uguu.org) Original source My latest (last?) ASCII intro! Special thanks to Elliott Lee's tank.c, without which these trees would not have existed. Thanks man! Instructions: Run without arguments, preferably in fullscreen text mode. To change tree, press any key (except ctrl+alt+del, etc.) once. To exit, press any key twice (or any two keys). To exit fast, hit (your own) keyboard repeatedly. Features: Many different trees! Alternate color sets near full moon! Accurate moon age too!! Antialiased lines and curves! Light source shaded trees!! Just joking... Simplified build instructions: Detailed build instructions: MSC 6.0: MSC 6.0: cl tree.c forcedos cl /Ox /G2rs tree.c System notes: b800:0000 = VGA mode 3 text buffer (23L << 27) 0040:006c = System clock (same as 0000:046c, 1132L) Enable high-intensity backgrounds: mov ax, 1003h mov bx, 0 ; Use 1 to restore blinking int 10h Get system date: mov ah, 2ah int 21h ; CX = year, DH = month, DL = day, AL = weekday 07/02/99: main InitRand 07/06/99: DrawPoint InitTree DrawLine Render RenderDust RenderFruit RenderTree RenderLeaf 07/16/99: RegenerateDust InitParticles GeneratePine1 GenerateElm1 MoonAge 07/19/99: ClearScreen RestoreScreen 07/20/99: GeneratePine2 GenerateElm2 RenderMoon 07/21/99: InitMoon 07/22/99: DestroyTree AddCut 07/23/99: ClearKey Blit */ /* Includes */ #include #include /* Constants */ #define PI 3.14159265358979323846264338327950288419716939937510 #define PARTICLE_COUNT 2001 #define PARTICLE_DUST 0 #define PARTICLE_LEAF 1 #define PARTICLE_FRUIT 2 #define PARTICLE_TREE 3 #define COLOR_BRANCH0 0 #define COLOR_BRANCH1 1 #define COLOR_ROOT0 2 #define COLOR_ROOT1 3 #define COLOR_LEAF0 4 #define COLOR_LEAF1 5 #define COLOR_LEAF2 6 #define COLOR_DUST0 7 #define COLOR_DUST1 8 #define SCREEN_RENDER 0 #define SCREEN_ALPHA 1 #define SCREEN_SAVE 2 #define MAX_VELOCITY_X 32 #define MAX_VELOCITY_Y 32 #define X_PRECISION 4 #define Y_PRECISION 8 #define MAX_FADE_IN_TIME 128 #define MOON_PRECISION 3 /* Macros */ #define color(n, i) tpal[altcolor][COLOR_##n##i] #define swap(s, a, b) \ iy = particle[a].s; \ particle[a].s = particle[b].s; \ particle[b].s = iy; /* System data */ int far *screen = (int far *)(23L << 27); /* Physical screen */ volatile long far *clock = (long far *)1132L; /* System clock */ long tick; /* Synchronizer */ long rseed; /* Random number seed */ int altcolor; /* Use alternate color set */ /* Screen data */ int page[3][2048]; /* Screen buffers */ int row[25]; /* Row offsets */ /* Particle data */ int tpal[2][9] = { /* Color palette */ {0x7600, 0xf600, 0x7800, 0xf700, 0x2700, 0xaf00, 0xac00, 0x0300, 0x0100}, {0x7800, 0xf700, 0x7300, 0xfb00, 0x1700, 0x9f00, 0x9d00, 0x0300, 0x0200} }; int pcount; /* Particle count */ int dcount; /* Dynamic particle count */ /* Moon data */ int mpal[5] = {0, 8, 7, 11, 15}; /* Color palette */ int moon[5][10]; /* Moon image */ int moonage; /* Moon age */ int moonpos; /* Moon position */ /* Extra particles (if out of screen particles) */ int Extra[3] = {'D', 'O', 'N'}; /* Slash palette */ int spal[6][2] = { {0x0fdf, 0x0000}, {0x78dc, 0x0000}, {0x78df, 0x0000}, {0x0fdc, 0x0000}, {0x07dc, 0x08df}, {0x08dc, 0x07df} }; /* General data */ int ix, iy, iz, sx, sy, sz, rx, bx[4], by[4]; double fx, fy, tx, ty; /* Particles */ struct _particle { int ptype; int cx, cy, dx, dy, rx, ry; int color; } particle[PARTICLE_COUNT]; /* Prototypes */ void AddCut(void); void Blit(void); void ClearKey(void); void ClearScreen(void); void DestroyTree(void); void DrawLine(int x, int y, int i, int j, int t, int c); void DrawPoint(int x, int y, int t, int c); void GenerateElm1(void); void GenerateElm2(void); void GeneratePine1(void); void GeneratePine2(void); void InitMoon(void); void InitTree(void); void InitParticles(void); void MoonAge(void); void RegenerateDust(void); void Render(void); void RenderDust(void); void RenderFruit(void); void RenderLeaf(void); void RenderMoon(void); void RenderTree(void); void RestoreScreen(void); int rnd(void); /******************************************************************** main */ void cdecl main(void) { /* Seed random numbers */ rseed = *clock; /* Initialize offsets */ iy = -80; for(ix = 0; ix < 25;) row[ix++] = iy += 80; _asm { /* Enable high-intensity background colors */ mov ax, 1003h xor bx, bx int 10h /* Get system date */ mov ah, 2ah int 21h mov iz, cx mov iy, dx } ix = iy >> 8; iy &= 255; /* Calculate moon age ix = month, iy = date, iz = year */ MoonAge(); altcolor = (moonage - 15 >= -1 && moonage - 15 <= 1) ? 1 : 0; /* Prepare moon image */ InitMoon(); /* Save/clear screen */ ClearScreen(); /* Main loop */ do { InitTree(); InitParticles(); while( !kbhit() ) { Render(); RegenerateDust(); } DestroyTree(); } while( rx <= 1 ); /* Restore original screen */ RestoreScreen(); /* Enable blinking */ _asm { mov ax, 1003h mov bx, 1 int 10h } } /* main() */ /******************************************************************** AddCut Slash tree. * * Globals: sx, sy, sz, ix, iy, iz, rx, bx[], by[], page[]. * Calls: rnd. */ void AddCut(void) { /* Set slash entry/exit heights sx = left, sy = right */ if( rx++ & 1 ) { sx = 16 * 6 + rnd() % (7 * 6); sy = 14 * 6 - rnd() % (7 * 6); } else { sx = 14 * 6 - rnd() % (7 * 6); sy = 16 * 6 + rnd() % (7 * 6); } /* Flash */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = -1; Blit(); /* Animate slash */ iz = rnd() & 1; for(sz = iz ? 0 : 7; sz < 8 && sz >= 0; sz += iz ? 1 : -1) { /* Clear screen */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = 0; /* Draw slash */ for(ix = (sz < 4 ? 0 : sz * 20 - 80); ix < (sz < 4 ? sz * 20 + 20 : 80); ix++) { iy = (ix * (sy - sx)) / 80 + sx; page[SCREEN_RENDER][row[iy / 6] + ix] = spal[iy % 6][0]; page[SCREEN_RENDER][row[iy / 6 + 1] + ix] = spal[iy % 6][1]; } /* Redraw screen */ Blit(); } /* Set slice velocity */ bx[ix = rx - 1] = rnd() % 16 + 8; by[ix] = rnd() % 16 + 8; if( !(rx & 1) ) bx[ix] = -bx[ix]; /* Mark area */ sx /= 6; sy /= 6; for(ix = 0; ix < 80; ix++) { for(iy = (ix * (sy - sx)) / 80 + sx; iy >= 0;) page[SCREEN_ALPHA][row[iy--] + ix] = rx; } } /* AddCut() */ /********************************************************************** Blit Synchronize and dump screen. * * Globals: ix, clock, tick, screen, page[]. * Calls: None. */ void Blit(void) { /* Wait for timer */ while( tick == *clock ); tick = *clock; /* Dump screen */ for(ix = 0; ix < 2000;) screen[ix++] = page[SCREEN_RENDER][ix]; } /* Blit() */ /****************************************************************** ClearKey Clear keystroke. * * Globals: None. * Calls: None. */ void ClearKey(void) { if( !getch() ) getch(); } /* ClearKey() */ /*************************************************************** ClearScreen Clear screen. * * Globals: ix, page[], pcount, dcount, particle[]. * Calls: None. */ void ClearScreen(void) { /* Save screen */ for(ix = 0; ix < 2000;) page[SCREEN_SAVE][ix++] = screen[ix]; /* Assign screen particles */ for(ix = pcount = 0; ix < 2000; ix++) { if( (screen[ix] & 255) != 32 || (screen[ix] & 0xf000) ) { particle[pcount].cx = ix % 80; particle[pcount].cy = ix / 80; particle[pcount].color = screen[ix]; particle[pcount++].rx = rnd() % 64 + 1; } } /* Remove particles */ for(dcount = pcount; dcount && !kbhit();) { /* Clear screen buffer */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = 0; /* Render particles */ for(ix = pcount - 1; ix >= 0; ix--) { if( particle[ix].rx ) { /* Draw particle */ page[SCREEN_RENDER][row[particle[ix].cy] + particle[ix].cx] = particle[ix].color; /* Start particle */ if( !(--particle[ix].rx) ) { particle[ix].dx = rnd() % 4 + 6; particle[ix].dy = rnd() % 4; } } else { /* Draw particle */ if( particle[ix].cy >= 0 && particle[ix].cy < 25 && particle[ix].cx >= 0 && particle[ix].cx < 80 ) { page[SCREEN_RENDER][row[particle[ix].cy] + particle[ix].cx] = particle[ix].color; } else { if( particle[ix].dx ) { dcount--; particle[ix].dx = particle[ix].dy = 0; } } /* Animate particle */ particle[ix].cx -= particle[ix].dx; particle[ix].cy += particle[ix].dy; } } /* Redraw screen */ Blit(); } /* Clear keystroke */ if( kbhit() ) ClearKey(); } /* ClearScreen() */ /*************************************************************** DestroyTree Clear tree. * * Globals: ix, iy, iz, rx, tx, bx[], by[], pcount, dcount, particle[], * page[]. * Calls: AddCut, Render. */ void DestroyTree(void) { /* Clear keystroke */ ClearKey(); /* Clear screen map */ for(ix = rx = 0; ix < 2000;) page[SCREEN_ALPHA][ix++] = 0; /* Stop particle fade in / assign particle color */ for(ix = 0; ix < pcount; ix++) { if( particle[ix].ptype ) { particle[ix].rx = particle[ix].ry = 0; particle[ix].color = ( particle[ix].ptype == PARTICLE_TREE ? ((particle[ix].color & 0x8000) ? 0xf700 : 0x7f00) : ((particle[ix].color & 0x8000) ? 0x7800 : 0x8700) ) | (particle[ix].color & 255); } } /* Clear dust particles */ if( rnd() & 1 ) { rx++; tx = 1; } else { tx = 0; } AddCut(); for(dcount = 1; dcount;) { /* Slash tree */ if( kbhit() ) { if( rx < 3 + (int)tx ) { /* Clear keystroke */ ClearKey(); /* Slash */ AddCut(); } else { break; } } else { Render(); } /* Count dust particles */ for(ix = dcount = 0; ix < pcount; ix++) { if( !particle[ix].ptype ) { if( particle[ix].rx >= 0 && particle[ix].ry < (25 << Y_PRECISION) ) dcount++; else particle[ix].dx = particle[ix].dy = 0; } } } /* Assign particle coordinates */ for(ix = 0; ix < 2000;) { particle[ix].rx = particle[ix].cx << X_PRECISION; particle[ix++].ry = particle[ix].cy << Y_PRECISION; } /* Flash */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = -1; Blit(); /* Remove slices */ for(iz = 1; iz;) { /* Clear screen */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = 0; /* Draw moon */ RenderMoon(); /* Draw / count particle */ for(ix = iz = 0; ix < pcount; ix++) { if( particle[ix].ptype ) { particle[ix].cx = particle[ix].rx >> X_PRECISION; particle[ix].cy = particle[ix].ry >> Y_PRECISION; if( particle[ix].cy >= 0 && particle[ix].cy < 25 && particle[ix].cx >= 0 && particle[ix].cx < 80 ) { iz++; page[SCREEN_RENDER][row[particle[ix].cy] + particle[ix].cx] = particle[ix].color; if( !(iy = page[SCREEN_ALPHA][particle[ix].dx]) ) { particle[ix].ry += 1 << (Y_PRECISION - 2); } else { particle[ix].rx += bx[--iy]; particle[ix].ry += by[iy]; } } } } /* Redraw screen */ Blit(); /* Abort slice */ if( kbhit() ) { ClearKey(); break; } } if( tx > 0 ) rx--; } /* DestroyTree() */ /****************************************************************** DrawLine Draw straight line. * * Globals: fx, fy, iz. * Calls: DrawPoint. */ void DrawLine(int x, int y, int i, int j, int t, int c) { fx = (i - x) / 64.0; fy = (j - y) / 64.0; for(iz = 0; iz <= 64; iz++) DrawPoint((int)(iz * fx) + x, (int)(iz * fy) + y, t, c); } /* DrawLine() */ /***************************************************************** DrawPoint Draw point. * * Globals: page[], row[], pcount, particle[]. * Calls: None. */ void DrawPoint(int x, int y, int t, int c) { /* Check pixel availability */ if( y >= 0 && y < 25 && x >= 0 && x < 80 ) { if( page[SCREEN_ALPHA][row[y] + x] ) return; /* Draw pixel */ page[SCREEN_ALPHA][row[y] + x] = 1; /* Set parameters */ particle[pcount].cx = x; particle[pcount].cy = y; particle[pcount].ptype = t; particle[pcount++].color = c; } } /* DrawPoint() */ /************************************************************** GenerateElm1 Generate elm tree (type 1). * * Globals: ix, iy, sx, sy, sz, rx, fx, fy, bx[], by[]. * Calls: rnd, DrawLine, DrawPoint. */ void GenerateElm1(void) { /* Draw main branch */ ix = sx = rnd() % 18 + 30; /* (sx, sy) = top of branch */ sy = rnd() % 4 + 11; rx = sx + 8; /* ix = left bound, rx = right bound */ for(iy = sy; iy < 25; iy++) { /* Draw branch */ DrawLine(rx - (rx - ix) / 3, iy, rx, iy, PARTICLE_TREE, color(BRANCH, 0)); DrawLine(ix, iy, rx, iy, PARTICLE_TREE, color(BRANCH, 1)); /* Bend branch */ if( (sz = rnd()) & 3 ) { if( sz & 4 ) { ix--; if( sz & 8 ) rx--; } else { rx++; if( sz & 8 ) ix++; } if( rx - ix < 4 ) sz = (sz & 1) ? ix-- : rx++; if( rx - ix > 15 ) sz = (sz & 1) ? ix++ : rx--; } /* Stop drawing at root */ if( iy > 20 && iy < 24 ) { if( rnd() & 1 ) break; } } /* Draw root */ for(; iy < 25; iy++) { for(sz = ix; sz <= rx;) { DrawPoint(sz++, iy, PARTICLE_TREE, rnd() & 3 ? color(ROOT, 1) : color(ROOT, 0)); } ix -= rnd() % 7 + 6; rx += rnd() % 7 + 6; } /* Draw other branches */ for(sz = 0; sz < 4; sz++) { /* (sx, sy) = top of main branch (bx, by) = top of small branch */ rx = sz * 8 + rnd() % 5 + 2; bx[sz] = (int)(sx + 25 * cos((PI * rx) / 32) + sz * 2); by[sz] = (int)(sy - 10 * sin((PI * rx) / 32) + 2); for(rx = 0; rx < 4; rx++) { ix = rnd() % 3 - 1 + (sx + bx[sz]) / 2 + sz; iy = rnd() % 3 - 1 + (sy + by[sz]) / 2; DrawLine(rnd() % 3 - 1 + bx[sz], rnd() % 3 - 1 + by[sz], ix, iy, PARTICLE_TREE, rx - 1 ? color(BRANCH, 1) : color(BRANCH, 0)); DrawLine(sx + sz * 2, sy, ix, iy, PARTICLE_TREE, rx - 1 ? color(BRANCH, 1) : color(BRANCH, 0)); } } /* Draw shade */ for(sx = 0; sx < 4; sx++) { for(sy = 0; sy++ < 40;) { if( rnd() & 1 ) { fx = (5 - sy) * PI / 64; DrawPoint(bx[sx] + (int)(14 * cos(fx)), by[sx] - (int)(7 * sin(fx)), PARTICLE_LEAF, color(LEAF, 0)); } } } /* Draw leaves */ for(sx = 0; sx < 4; sx++) { for(sy = 0; sy++ < 512;) { fx = sy * PI / 256; fy = (rnd() & 0xfff) / 300; rx = rnd() & 7; DrawPoint(bx[sx] + (int)(fy * cos(fx)), by[sx] + (int)(fy * sin(fx) * .4), rx ? PARTICLE_LEAF : PARTICLE_FRUIT, rx ? color(LEAF, 1) : color(LEAF, 2)); } } } /* GenerateElm1() */ /************************************************************** GenerateElm2 Generate elm tree (type 2). * * Globals: ix, iy, sx, sy, sz, rx, fx. * Calls: rnd, DrawLine, DrawPoint. */ void GenerateElm2(void) { /* Draw main branch */ ix = sx = rnd() % 14 + 31; /* (sx, sy) = top of branch */ sy = rnd() % 6 + 12; rx = sx + 7; /* ix = left bound, rx = right bound */ for(iy = sy; iy < 25; iy++) { /* Draw branch */ DrawLine(rx - (rx - ix) / 3, iy, rx, iy, PARTICLE_TREE, color(BRANCH, 0)); DrawLine(ix, iy, rx, iy, PARTICLE_TREE, color(BRANCH, 1)); /* Bend branch */ if( (sz = rnd()) & 3 ) { if( sz & 4 ) { ix--; if( sz & 8 ) rx--; } else { rx++; if( sz & 8 ) ix++; } if( rx - ix < 4 ) sz = (sz & 1) ? ix-- : rx++; if( rx - ix > 15 ) sz = (sz & 1) ? ix++ : rx--; } /* Stop drawing at root */ if( iy > 20 && iy < 24 ) { if( rnd() & 1 ) break; } } /* Draw root */ for(; iy < 25; iy++) { for(sz = ix; sz <= rx;) { DrawPoint(sz++, iy, PARTICLE_TREE, rnd() & 3 ? color(ROOT, 1) : color(ROOT, 0)); } ix -= rnd() % 7 + 6; rx += rnd() % 7 + 6; } /* Set other branch locations */ for(ix = 0; ix < 4;) { rx = ix * 8 + rnd() % 5 + 2; bx[ix] = (int)(sx + 25 * cos((PI * rx) / 32) + ix * 2); by[ix++] = (int)(sy - 10 * sin((PI * rx) / 32) + 2); } /* Draw foreground leaves */ for(ix = 0; ix < 4; ix++) { for(iy = 0; iy++ < 32;) { fx = PI * (rnd() % 256) / 128; iz = rnd() % 8; DrawPoint((int)(iz * cos(fx)) + bx[ix] + ix * 2 + rnd() % 2, (int)(iz * sin(fx) / 2) + by[ix], PARTICLE_LEAF, color(LEAF, 1)); } } /* Draw branches */ for(ix = 0; ix < 4; ix++) { DrawLine(bx[ix], by[ix], sx + ix * 2, sy, PARTICLE_TREE, iy ? color(BRANCH, 1) : color(BRANCH, 0)); for(iy = 0; iy++ < 3;) { fx = PI * (rnd() % 256) / 128; iz = rnd() % 4 + 6; DrawLine((int)(iz * cos(fx)) + bx[ix], (int)(iz * sin(fx) / 2) + by[ix], sx + ix * 2, sy, PARTICLE_TREE, color(BRANCH, 1)); } } /* Draw shade */ for(ix = 0; ix < 4; ix++) { for(iy = 0; iy++ < 40;) { if( rnd() & 1 ) { fx = (5 - iy) * PI / 64; DrawPoint(bx[ix] + (int)(14 * cos(fx)), by[ix] - (int)(7 * sin(fx)), PARTICLE_LEAF, color(LEAF, 0)); } } } /* Draw background leaves */ for(ix = 0; ix < 4; ix++) { for(iy = 0; iy++ < 512;) { fx = iy * PI / 256; fy = (rnd() & 0xfff) / 300; DrawPoint(bx[ix] + (int)(fy * cos(fx)), by[ix] + (int)(fy * sin(fx) / 2), PARTICLE_LEAF, color(LEAF, 1)); } } } /* GenerateElm2() */ /************************************************************* GeneratePine1 Generate pine tree (type 1). * * Globals: ix, sx, sy, sz, rx; * Calls: rnd, DrawPoint, DrawLine. */ void GeneratePine1(void) { /* Draw main branch */ sx = (rnd() % 16) + 32; /* (sx, sy) = top of branch */ sy = (rnd() % 2) + 4; sz = (rnd() % 16) + 32; /* (sz, 24) = bottom of branch */ for(ix = 5; ix > 2; ix--) DrawLine(sx + (ix / 3), sy, sz + ix, 24, PARTICLE_TREE, color(BRANCH, 0)); for(; ix > -5; ix--) DrawLine(sx + (ix / 3), sy, sz + ix, 24, PARTICLE_TREE, color(BRANCH, 1)); /* Draw leaves run (error increment) = sz - sx rise (error denominator) = 24 - sy center = (sx + y * run / rise, y) */ for(iy = 0; iy < 21; iy++) { ix = 3 * (iy < 10 ? iy : iy - 6) + 1; /* ix = width */ /* Draw fruit */ if( rnd() & 1 ) { DrawPoint(sx + (iy * (sz - sx)) / (24 - sy) + ix / 2 - rnd() % ix, iy, PARTICLE_FRUIT, color(LEAF, 2)); } /* Draw leaves */ for(iz = 0; iz++ < ix;) DrawPoint((rx = sx + (iy * (sz - sx)) / (24 - sy) + ix / 2) - rnd() % ix - 1, iy, PARTICLE_LEAF, color(LEAF, 1)); /* Draw shade */ for(; ix > -1; ix--) { if( rnd() & 3 ) DrawPoint(rx - ix, iy, PARTICLE_LEAF, color(LEAF, 0)); } } } /* GeneratePine1() */ /************************************************************* GeneratePine2 Generate pine tree (type 2). * * Globals: ix, iy, iz, bx[], by[], rx, sx, sy, sz, fx, fy, tx, ty. * Calls: rnd, DrawLine, DrawPoint. */ void GeneratePine2(void) { if( !(sz = rnd() & 1) ) { /* One tree */ bx[0] = rnd() % 10 + 35; /* 0 = top of tree */ by[0] = rnd() % 4 + 9; bx[2] = rnd() % 10 + 35; /* 2 = bottom of tree */ sx = 16; sy = 11; } else { /* Two trees */ bx[0] = rnd() % 8 + 16; by[0] = rnd() % 4 + 10; bx[1] = rnd() % 8 + 46; by[1] = rnd() % 4 + 10; bx[2] = rnd() % 8 + 16; bx[3] = rnd() % 8 + 46; sx = 10; sy = 10; } /* Draw trees */ for(; sz >= 0; sz--) { /* Draw main branch */ for(ix = 4; ix > 2; ix--) DrawLine(bx[sz] + (ix / 3), by[sz], bx[sz + 2] + ix, 24, PARTICLE_TREE, color(BRANCH, 0)); for(; ix > -4; ix--) DrawLine(bx[sz] + (ix / 3), by[sz], bx[sz + 2] + ix, 24, PARTICLE_TREE, color(BRANCH, 1)); /* Calculate tree angle */ fx = atan2(24 - by[sz], bx[sz] - bx[sz + 2]); /* Calculate rotation matrix entries */ fy = sin(fx); fx = cos(fx); /* Contour tree */ for(iz = 0; iz < 256; iz++) { if( rnd() & 1 ) continue; tx = sx * sin(iz * PI / 128); ty = ((iz < 192 && iz > 64) ? 3 * sy : sy) * cos(iz * PI / 128); ix = (int)(tx * fy - ty * fx) + (bx[sz] + bx[sz + 2]) / 2; iy = ((int)(tx * fx + ty * fy) + by[sz] + 24) / 2; DrawPoint(ix, iy, PARTICLE_LEAF, (iz > 96 && iz < 224) ? color(LEAF, 1) : color(LEAF, 0)); } /* Draw leaves */ for(iz = 0; iz < 256; iz++) { /* Calculate contour coordinates */ tx = sx * sin(iz * PI / 128); ty = ((iz < 192 && iz > 64) ? 3 * sy : sy) * cos(iz * PI / 128); ix = (int)(tx * fy - ty * fx) + (bx[sz] + bx[sz + 2]) / 2; iy = ((int)(tx * fx + ty * fy) + by[sz] + 24) / 2; /* Interpolate */ for(rx = 0; rx++ < 8;) { tx = (rnd() % 32) / 32.0; if( rnd() & 7 ) { DrawPoint(ix - (int)(tx * (ix - (bx[sz] + bx[sz + 2]) / 2)), iy - (int)(tx * (iy - (by[sz] + 24) / 2)), PARTICLE_LEAF, rnd() % 4 ? color(LEAF, 1) : color(LEAF, 0)); } else { DrawPoint(ix - (int)(tx * (ix - (bx[sz] + bx[sz + 2]) / 2)), iy - (int)(tx * (iy - (by[sz] + 24) / 2)), PARTICLE_FRUIT, color(LEAF, 2)); } } } } } /* GeneratePine2() */ /****************************************************************** InitMoon Render moon image. * * Globals: moonage, mpal, moon[][], ix, iy, iz, page[], fx, fy, rx, sx. * Calls: None. */ void InitMoon(void) { /* Render moon image */ if( !moonage ) { /* New moon */ for(iz = 0; iz < 400;) page[SCREEN_ALPHA][iz++] = 0; } else if( moonage == 15 ) { /* Full moon */ for(iy = iz = 0; iy < 20; iy++) { for(ix = -10; ix < 10; ix++) { page[SCREEN_ALPHA][iz++] = ((ix + 0.5) * (ix + 0.5) + (iy - 9.5) * (iy - 9.5)) <= 90.25 ? 1 : 0; } } } else { /* In between stuff */ /* Clear image */ for(ix = 0; ix < 400;) page[SCREEN_ALPHA][ix++] = 0; /* Calculate scales */ if( moonage < 15 ) { fx = (15 - moonage) / 7.5 - 1; fy = 1; } else { fx = -1; fy = (30 - moonage) / 7.5 - 1; } /* Render image */ for(iy = 0; iy < 20; iy++) { /* Calculate bounds */ sx = (int)(fx * sqrt(90.25 - (iy - 9.5) * (iy - 9.5))) + 10; rx = (int)(fy * sqrt(90.25 - (iy - 9.5) * (iy - 9.5))) + 10; /* Draw scanline */ for(ix = sx; ix <= rx; ix++) page[SCREEN_ALPHA][iy * 20 + ix] = 1; } } /* Supersample */ for(iy = 0; iy < 5; iy++) { for(ix = 0; ix < 10; ix++) { iz = iy * 80 + ix * 2; sx = page[SCREEN_ALPHA][iz] /* sx = top pixel */ + page[SCREEN_ALPHA][iz + 1] + page[SCREEN_ALPHA][iz + 20] + page[SCREEN_ALPHA][iz + 21]; rx = page[SCREEN_ALPHA][iz + 40] /* rx = bottom pixel */ + page[SCREEN_ALPHA][iz + 41] + page[SCREEN_ALPHA][iz + 60] + page[SCREEN_ALPHA][iz + 61]; moon[iy][ix] = !(sx + rx) ? 0 : ((sx > rx) ? ((mpal[sx] << 12) | (mpal[rx] << 8) | 220) : ((mpal[rx] << 12) | (mpal[sx] << 8) | 223)); } } /* Set moon position */ moonpos = -12 << MOON_PRECISION; } /* InitMoon() */ /****************************************************************** InitTree Create tree. * * Globals: pcount, ix, page[], moonpos. * Calls: rnd, GenerateElm1, GenerateElm2, GeneratePine1, GeneratePine2. */ void InitTree(void) { /* Reset screen and particle count */ for(ix = pcount = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = (page[SCREEN_ALPHA][ix] = 0) - 1; /* Redraw screen */ Blit(); /* Create tree */ if( (ix = rnd()) & 3 ) { if( ix & 4 ) GenerateElm1(); else GenerateElm2(); } else { if( ix & 4 ) GeneratePine1(); else GeneratePine2(); } } /* InitTree() */ /************************************************************* InitParticles Set particle member values. * * Globals: ix, iy, iz, pcount, dcount, particle[], page[]. * Calls: rnd. */ void InitParticles(void) { /* Shell sort particles iz = span ix = index iy = swap flag */ for(iz = 1024; iz > 0; iz /= 2) { do { for(ix = iy = 0; ix < pcount - iz; ix++) { if( (row[particle[ix].cy] + particle[ix].cx) > (row[particle[ix + iz].cy] + particle[ix + iz].cx) ) { swap(ptype, ix, ix + iz); swap(cx, ix, ix + iz); swap(cy, ix, ix + iz); swap(color, ix, ix + iz); /* iy != 0 */ } } } while( iy ); } /* Assign characters to static particles ix = particle index iy = screen index iz = set if out of screen characters */ for(ix = iy = iz = 0; ix < pcount; ix++) { /* Get next screen character */ for(; iy < 2000 && (page[SCREEN_SAVE][iy] & 255) == 32; iy++); /* Assign screen character, if available */ particle[ix].color |= (iy == 2000) ? Extra[rnd() % 3] : (page[SCREEN_SAVE][iy++] & 255); /* Set particle fade in time */ particle[ix].rx = rnd() % MAX_FADE_IN_TIME + 1; particle[ix].ry = rnd() % MAX_FADE_IN_TIME + MAX_FADE_IN_TIME; } /* Assign dust particles (until screen particles run out) */ for(; !iz || (ix - pcount < 16); ix++) { for(; iy < 2000 && (page[SCREEN_SAVE][iy] & 255) == 32; iy++); particle[ix].cy = particle[ix].ptype = 0; particle[ix].color = (rnd() & 1 ? color(DUST, 0) : color(DUST, 1)) | ((iy == 2000) ? iz = Extra[rnd() % 3] : (page[SCREEN_SAVE][iy++] & 255)); particle[ix].rx = -1999; } /* Shift dust particles to background dcount = dust particle count */ dcount = ix - pcount; pcount = ix; for(iz = dcount / 2; iz; iz--) { for(ix = pcount - 1; ix > 0; ix--) { swap(ptype, ix, ix - 1); swap(cx, ix, ix - 1); swap(cy, ix, ix - 1); swap(rx, ix, ix - 1); swap(ry, ix, ix - 1); swap(color, ix, ix - 1); } } /* Set particle offsets */ for(ix = 0; ix < pcount;) particle[ix++].dx = row[particle[ix].cy] + particle[ix].cx; } /* InitParticles() */ /******************************************************************* MoonAge Calculate moon age (accurate for years after 1900AD). * * Globals: ix, iy, iz, moonage. * Calls: None. */ void MoonAge(void) { int ages[] = {18, 0, 11, 22, 3, 14, 25, 6, 17, 28, 9, 20, 1, 12, 23, 4, 15, 26, 7}, offsets[] = {-1, 1, 0, 1, 2, 3, 4, 5, 7, 7, 9, 9}; /* ix = month, iy = day, iz = year moonage = moon age (0 = new, 15 = full, 29 = new) */ if( iy == 31 ) iy = 1; moonage = (ages[(iz + 1) % 19] + iy + offsets[ix - 1]) % 30; } /* MoonAge() */ /************************************************************ RegenerateDust Regenerate dust particles. * * Globals: ix, pcount, particle[]. * Calls: rnd. */ void RegenerateDust(void) { for(ix = 0; ix < pcount; ix++) { /* Check for out of range particles */ if( particle[ix].rx < 0 || particle[ix].ry > (25 << Y_PRECISION) ) { /* Generate particle velocity */ particle[ix].dx = (rnd() % MAX_VELOCITY_X) + 4; particle[ix].dy = (rnd() % MAX_VELOCITY_Y) + 8; if( rnd() & 1 ) { /* New particle appears from top */ particle[ix].rx = rnd() % (80 << X_PRECISION); particle[ix].ry = -2 << Y_PRECISION; } else { /* New particle appears from right */ particle[ix].rx = 81 << X_PRECISION; particle[ix].ry = rnd() % (25 << Y_PRECISION); } } } } /* RegenerateDust() */ /******************************************************************** Render Draw screen. * * Globals: ix, pcount, screen[], page[]. * Calls: RenderTree, RenderLeaf, RenderFruit, RenderDust. */ void Render(void) { /* Clear screen */ for(ix = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = 0; /* Draw moon */ RenderMoon(); /* Draw particles */ for(ix = 0; ix < pcount; ix++) { if( particle[ix].ptype & 1 ) { if( particle[ix].ptype & 2 ) RenderTree(); else RenderLeaf(); } else { if( particle[ix].ptype & 2 ) RenderFruit(); else RenderDust(); } } /* Redraw screen */ Blit(); } /* Render() */ /**************************************************************** RenderDust Draw dust particle. * * Globals: ix, sx, particle[], page[], row[]. * Calls: None. */ void RenderDust(void) { /* Set particle position */ particle[ix].cx = particle[ix].rx >> X_PRECISION; particle[ix].cy = particle[ix].ry >> Y_PRECISION; /* Draw particle */ if( particle[ix].cy >= 0 && particle[ix].cy < 25 && particle[ix].cx >= 0 && particle[ix].cx < 80 ) { sx = row[particle[ix].cy] + particle[ix].cx; page[SCREEN_RENDER][sx] = ( (page[SCREEN_RENDER][sx] & 0x0fff) && (page[SCREEN_RENDER][sx] & 0xf000) ) ? (page[SCREEN_RENDER][sx] & 0xf000) | 0x800 | particle[ix].color : particle[ix].color; } /* Animate particle */ particle[ix].rx -= particle[ix].dx; particle[ix].ry += particle[ix].dy; } /* RenderDust() */ /*************************************************************** RenderFruit Draw fruit particle. * * Globals: ix, particle[], page[], row[]. * Calls: None. */ void RenderFruit(void) { if( particle[ix].rx ) { /* Fade in */ if( particle[ix].ry ) { particle[ix].ry--; page[SCREEN_RENDER][particle[ix].dx] = (particle[ix].color & 255) | ((rnd() % MAX_FADE_IN_TIME > particle[ix].ry) ? 0xf700 : 0xff00); } else { particle[ix].rx--; page[SCREEN_RENDER][particle[ix].dx] = (rnd() % MAX_FADE_IN_TIME > particle[ix].rx) ? ((rnd() & 3) ? particle[ix].color : particle[ix].color ^ 0x100) : ((particle[ix].color & 255) | 0xf700); } } else { /* Draw particle */ page[SCREEN_RENDER][particle[ix].dx] = rnd() & 3 ? particle[ix].color : particle[ix].color ^ 0x100; } } /* RenderFruit() */ /**************************************************************** RenderLeaf Draw leaf particle. * * Globals: ix, sx, particle[], page[], row[]. * Calls: None. */ void RenderLeaf(void) { if( particle[ix].rx ) { /* Fade in */ if( particle[ix].ry ) { particle[ix].ry--; page[SCREEN_RENDER][particle[ix].dx] = (particle[ix].color & 255) | ((rnd() % MAX_FADE_IN_TIME > particle[ix].ry) ? 0xf700 : 0xff00); } else { particle[ix].rx--; page[SCREEN_RENDER][particle[ix].dx] = (rnd() % MAX_FADE_IN_TIME > particle[ix].rx) ? ((rnd() & 7) ? particle[ix].color : particle[ix].color ^ 0x800) : ((particle[ix].color & 255) | 0xf700); } } else { /* Draw particle */ page[SCREEN_RENDER][particle[ix].dx] = (rnd() & 7) ? particle[ix].color : particle[ix].color ^ 0x800; } } /* RenderLeaf() */ /**************************************************************** RenderMoon Draw moon image. * * Globals: ix, iy, iz, page, moonpos, moon[][]. * Calls: None. */ void RenderMoon(void) { iz = moonpos >> MOON_PRECISION; for(ix = 0; ix < 10; ix++) { if( iz + ix >= 0 && iz + ix < 80 ) { for(iy = 0; iy < 5; iy++) page[SCREEN_RENDER][row[iy + 2] + ix + iz] = moon[iy][ix]; } } if( moonpos++ > (82 << MOON_PRECISION) ) moonpos = -12 << MOON_PRECISION; } /* RenderMoon() */ /**************************************************************** RenderTree Draw tree particle. * * Globals: ix, particle[], page[], row[]. * Calls: None. */ void RenderTree(void) { if( particle[ix].rx ) { /* Fade in */ if( particle[ix].ry ) { particle[ix].ry--; page[SCREEN_RENDER][particle[ix].dx] = (particle[ix].color & 255) | ((rnd() % MAX_FADE_IN_TIME > particle[ix].ry) ? 0xf700 : 0xff00); } else { particle[ix].rx--; page[SCREEN_RENDER][particle[ix].dx] = (rnd() % MAX_FADE_IN_TIME > particle[ix].rx) ? particle[ix].color : ((particle[ix].color & 255) | 0xf700); } } else { /* Draw particle */ page[SCREEN_RENDER][particle[ix].dx] = particle[ix].color; } } /* RenderTree() */ /************************************************************* RestoreScreen Restore screen. * * Globals: ix, iy, iz, page[], pcount, dcount, particle[]. * Calls: rnd. */ void RestoreScreen(void) { /* Assign screen particles */ for(ix = pcount = 0; ix < 2000; ix++) { if( (page[SCREEN_SAVE][ix] & 255) != 32 || (page[SCREEN_SAVE][ix] & 0xf000) ) { particle[pcount].rx = rnd() % 4 + 2; particle[pcount].ry = rnd() % 6 + 2; particle[pcount].dx = rnd() % 32 + 20; particle[pcount].dy = rnd() % 8; particle[pcount].cx = (ix % 80) << particle[pcount].rx; particle[pcount].cy = (ix / 80) << particle[pcount].ry; particle[pcount++].color = page[SCREEN_SAVE][ix]; } } /* Restore particles */ for(dcount = 1; dcount && !kbhit();) { /* Clear screen */ for(ix = dcount = 0; ix < 2000;) page[SCREEN_RENDER][ix++] = 0; /* Draw particles */ for(iz = pcount - 1; iz >= 0; iz--) { if( particle[iz].dx ) { dcount++; /* Set particle position */ ix = (particle[iz].cx + (iy = particle[iz].dx * particle[iz].dx)) >> particle[iz].rx; iy = (particle[iz].cy - (particle[iz].dy ? iy : 0)) >> particle[iz].ry; /* Draw particle */ if( iy >= 0 && iy < 25 && ix >=0 && ix < 80 ) page[SCREEN_RENDER][row[iy] + ix] = particle[iz].color; /* Animate particle */ if( !(--particle[iz].dx) ) { particle[iz].dy = row[particle[iz].cy >> particle[iz].ry] + (particle[iz].cx >> particle[iz].rx); } } else { page[SCREEN_RENDER][particle[iz].dy] = particle[iz].color; } } /* Redraw screen */ Blit(); } /* Clear keystroke */ if( kbhit() ) ClearKey(); /* Restore screen */ for(ix = 0; ix < 2000;) screen[ix++] = page[SCREEN_SAVE][ix]; } /* RestoreScreen() */ /*********************************************************************** rnd Generate random number (power residue function). * * Globals: rseed. * Calls: None. */ int rnd(void) { rseed = rseed * 0x343fdL + 0x269ec3L; return (int)(rseed >> 16) & 0x7fff; } /* rnd() */