/// SDL 2D stuff, drawing the masses and the debug HUD #include "video2d.h" #include "field.h" #include "debug.h" #include "config.h" #include "input.h" #include "effects.h" #include "math2d.h" #include "main.h" #include "field.h" #include "SDL.h" #include "font2d.h" #include "particle.h" #include "engine.h" // FIXME: should not have to include this? #include "massalgos.h" #include #include /* globals */ SDL_Surface * screen; SDL_Surface * statusLine; #define DISPLAY_SCREENX 256 #define DISPLAY_SCREENY 256 //#define DISPLAY_SCREENX 512 //#define DISPLAY_SCREENY 512 /* the displayed screen rectangle being drawn, in mass coordinates */ Rect screenRect = {0,0,DISPLAY_SCREENX,DISPLAY_SCREENY}; /* in mass coordinates; OPTIMIZE: perhaps use more than one rect */ Rect dirtyRect; /* additional data for each mass */ typedef struct { void (*freeVideo2dDisplayData)(DummyDisplayData * data); Rect oldMaxRect; } DisplayData; SDL_Rect Rect2SDL_Rect(Rect r) { SDL_Rect res; res.x = r.x; res.y = r.y; res.w = r.w; res.h = r.h; return res; } Rect SDL_Rect2Rect(SDL_Rect r) { Rect res; res.x = r.x; res.y = r.y; res.w = r.w; res.h = r.h; return res; } /* r, g, b are unsigned chars, 255/255/255 is white */ #define SetPixel(x, y, rgb) (*((Uint16*)((char*)screen->pixels + (y)*screen->pitch) + (x)) = (((rgb[0])>>3) << 10) + (((rgb[1])>>3) << 5) + (((rgb[2])>>3) << 0) ) /* #define SetPixel(x, y, c) SetPixel(x, y, c.r, c.g, c.b) */ #define GetPixel(x, y, rgb) { \ Uint16 GetPixel_pixel = *((Uint16*)((char*)screen->pixels + (y)*screen->pitch) + (x)); \ rgb[0] = ( (GetPixel_pixel >> 10) & 31 ) << 3; \ rgb[1] = ( (GetPixel_pixel >> 5) & 31 ) << 3; \ rgb[2] = ( (GetPixel_pixel >> 0) & 31 ) << 3; \ } void InvertPixel(int x, int y) { int j; Color c; GetPixel(x, y, c); for (j=0; j<3; j++) c[j] = 255-c[j]; SetPixel(x, y, c); } /* misc prototypes */ unsigned char shadowCalc(int x, int y); unsigned char shadowCalcSlow(int x, int y); void shadowPrepare(void); void DrawDebug(void); void DebugPostprocessPixel(Mass * m, int x, int y, Color color /* variable */, int shadow); void AddDirtyRect(Rect r); int InitVideo2d() { int sdlflags; ResetKeys(); if (SDL_InitSubSystem(SDL_INIT_VIDEO)) { printf("Could not initialize the SDL video subsystem: %s\n", SDL_GetError()); return -1; } sdlflags = SDL_SWSURFACE; if (config.fullscreen) sdlflags |= SDL_FULLSCREEN; screen = SDL_SetVideoMode(DISPLAY_SCREENX, DISPLAY_SCREENY, DISPLAY_BPP, sdlflags); if (!screen) { printf("Could not set video mode: %s\n", SDL_GetError()); return -2; } SDL_WM_SetCaption("Mass " VERSIONNAME, NULL /* TODO: icon */); AddDirtyRect(screenRect); shadowPrepare(); statusLine = SDL_CreateRGBSurface(SDL_HWSURFACE, DISPLAY_SCREENX, 16, DISPLAY_BPP, 31 << 0, 31 << 5, 31 << 10, 0); font_init(FindData("Dustismo_Roman.ttf"), 20); font_setcolor(255, 255, 255, 0, 0, 0); SDL_FillRect(statusLine, NULL, 0); font_setsurface(statusLine); font_printf(0, 0, "Debug mode (use the mouse)"); DebugLevel(1, "SDL 2D frontend ready.\n"); return 0; } void DoneVideo2d() { if (particleTraces) FreeParticleTraces(); font_done(); SDL_FreeSurface(statusLine); SDL_QuitSubSystem(SDL_INIT_VIDEO); DebugLevel(1, "SDL 2D frontend terminated.\n"); } unsigned char shadowTable[256][256]; unsigned char shadowCalcSlow(int x, int y) { double res, x1, y1; int r; /* OPTIMIZE with shadow table */ /* TODO: perhaps move this to effects.c */ /* HEIGHT_NORM ^= 1 pixel in x or y direction */ x1 = (double)x/HEIGHT_NORM; y1 = (double)y/HEIGHT_NORM; /* calculated for a light direction (1, 1, -1) */ res = (1/sqrt(x1*x1+y1*y1+1))*(1/sqrt(1+1+1)); assert(res >= 0); res *= (+x1+y1+1); res = (res + 1) * 127.5; r = res; assert(r<=255 && r >= 0); //if (r > 255) r = 255; //if (r < 0) r = 0; return r; } /* the maximum height difference - everytihing steeper is displayed all the same */ #define SHADOW_RANGE (HEIGHT_NORM*2) unsigned char shadowCalc(int x, int y) { x = x * 128 / (SHADOW_RANGE); y = y * 128 / (SHADOW_RANGE); x += 128; y += 128; if (x < 0) x = 0; if (x > 255) x = 255; if (y < 0) y = 0; if (y > 255) y = 255; /* OPTIMIZE: this operation seems to take long, perhaps a recalculation would be faster because it does not have to access memory */ return shadowTable[y][x]; } void shadowPrepare() { int x, y; for (y=-128; y<=127; y++) { for (x=-128; x<=127; x++) { shadowTable[y+128][x+128] = shadowCalcSlow(x*SHADOW_RANGE/128, y*SHADOW_RANGE/128); } } } void DrawMass(Mass * m, Rect clip) { int x, y, i; FieldPoint * p; int starttime = 0; if (config.profile) starttime = SDL_GetTicks(); for (y=clip.y; yowner == m) { int shadow = 255; /* shuld always get set */ Color color; switch (m->displayType) { case DT_NORMAL: /* Calculate shadow value */ /* TODO: think about the pixels used here that belong to an other mass */ /* OPTIMIZE: this is the performance killer line, as well as the for loop that follows. */ shadow = shadowCalc(FieldXY(x+1,y)->height - p->height, FieldXY(x,y+1)->height - p->height); for (i=0; i<3; i++) color[i] = (shadow * (m->baseColor[i])) >> 8; /* OK, now follow some if's and but's */ if (p->height < m->borderColorHeight) { int intensity; intensity = (m->borderColorHeight - p->height) * 16 / HEIGHT_NORM; if (intensity > 255) intensity = 255; for (i=0; i<3; i++) color[i] = (color[i] * (255-intensity) + ((shadow * (m->secondColor[i])) >> 8) * intensity)/256; } if (m->drawElipsoid) { Point v, v2; Color c2; int d, d2; v = wrap_VectorFromTo(AddVectors(GetMassCenter(m), AngleToVector(m->heading, 24)), XYToPoint(x, y)); v2 = wrap_VectorFromTo(AddVectors(GetMassCenter(m), AngleToVector(m->heading, -0)), XYToPoint(x, y)); d = v.x*v.x + v.y*v.y; d2 = v2.x*v2.x + v2.y*v2.y; //d2 = bias - (sqrt(d)+sqrt(d2)) + p->height / (HEIGHT_NORM/8); // CPU intense! Floating point operations (sqrt) should be avoided. d2 = 130 - (sqrt(d*100)+sqrt(d2*100)) + p->height / (HEIGHT_NORM/8); if (d2 > 0) { RGBSET(c2, 155, 155, 155); for (i=0; i<3; i++) color[i] = shadow*c2[i] >> 8; } } /* anti aliasing (assumes black background color) */ #define HEIGHT_FADEOUT (HEIGHT_NORM*5) if (p->height < HEIGHT_FADEOUT) { for (i=0; i<3; i++) { color[i] = (((p->height*256) / HEIGHT_FADEOUT) * (int)color[i] ) / 256; } } break; case DT_FLAT: memcpy(color, m->baseColor, sizeof(Color)); break; default: DebugLevel(0, "Warning: I do not know how to draw mass type %d\n", m->displayType); RGBSET(color, 255, 255, 255); } if (debugMode) { DebugPostprocessPixel(m, x, y, color /* variable */, shadow); } { // now draw the pixel int sx, sy; sx = (x-screenRect.x)&FIELDWRAP; sy = (y-screenRect.y)&FIELDWRAP; assert(sx >= 0 && sy >= 0); assert(sx < DISPLAY_SCREENX && sy < DISPLAY_SCREENY); SetPixel(sx, sy, color); } } else if (!p->owner) { Color c; // Need to re-blacken possibly removed pixels int sx, sy; sx = (x-screenRect.x)&FIELDWRAP; sy = (y-screenRect.y)&FIELDWRAP; RGBSET(c, 0, 0, 0); SetPixel(sx, sy, c); } } } if (config.profile) { Mass * class; int t; t = SDL_GetTicks() - starttime; class = MassClassByName(m->class); assert(class); class->timeDisplay += t; } } void AddDirtyRect(Rect r) { //printf("AddDirtyRect(%s)\n", RectString(r)); wrap_ExpandRectToIncludeRect(&dirtyRect, r); } #define SCREEN_TO_MASS_COORDINATES(p) \ { \ p.x += screenRect.x; \ p.y += screenRect.y; \ } #define MASS_TO_SCREEN_COORDINATES(p) \ { \ Point res = wrap_VectorFromTo(XYToPoint(screenRect.x, screenRect.y), XYToPoint(p.x, p.y)); \ p.x = res.x; \ p.y = res.y; \ } void DrawGlobalFog() { /* OPTIMIZE: use bitmaps with alpha blending */ int x, y, i; for (y=0; y 0) { SetPixel(x, y, fogColor); } else { Color c; int alpha; alpha = 255 - MIN(sqrt(-dist), 255); //alpha = 255 - MIN(-dist/32, 255); alpha = MAX(alpha, 1); alpha += rand() % alpha; // FIXME: use DrawAlphaPixel instead alpha = MIN(alpha, 255); GetPixel(x, y, c); for (i=0; i<3; i++) { c[i] = (alpha*fogColor[i] + (255-alpha)*c[i]) >> 8; } SetPixel(x, y, c); } } } AddDirtyRect(screenRect); } // (x, y) are in mass coordinates; alpha<=0 transparent >=255 full occupacy inline void DrawAlphaPixel(int x, int y, int alpha, Color c2) { Color c; int i; if (alpha <= 0) return; x = (x-screenRect.x)&FIELDWRAP; y = (y-screenRect.y)&FIELDWRAP; if (x<0 || y<0 || x>=DISPLAY_SCREENX || y>=DISPLAY_SCREENY) return; if (alpha >= 255) { SetPixel(x, y, c2); return; } GetPixel(x, y, c); for (i=0; i<3; i++) { c[i] = ((255-alpha) * c[i] + alpha* c2[i]) / 256; } SetPixel(x, y, c); } /* Draw a broad line from start to end. To make sure that the ends are plug&play compatible with the next line being drawn, the parameters startVector/endVector are vectors that point away from their start/end point of the line in an angle that depends on the preceding/following line. ! <- here is where startVector points to (from startpoint x) ! preceding line ! 0000000000000000!0 000000000000000!000 00000000000000x00000 <- x is the start point 0000000000000!0000000 000000000000!000000000 <- current line ! 000000000 ! 000000000 000000000 When you turn startVector/endVector clockwisely by 90 degree it will point to the side that has to be drawn. So in the example above, endVector has to point from the end (not visible) of the current line about to bottom left. So if you want to draw the next line, you just have to negate endVector and use it as startVector of the new line. */ void DrawBroadLine(Point start, Point end, Point startVector, Point endVector, E_Lightning * l) { //very unoptimized, stale Version Point dirVector; int inverted; float broadness = l->r; dirVector = nowrap_VectorFromTo(start, end); if (abs(dirVector.x) >= abs(dirVector.y)) { inverted = 0; } else { int tmp; inverted = 1; tmp = start.x; start.x = start.y; start.y = tmp; tmp = end.x; end.x = end.y; end.y = tmp; tmp = dirVector.x; dirVector.x = dirVector.y; dirVector.y = tmp; //if these vectors were only mirrored, you could not any more turn them by 90 degree clockwisely to make them point to the line to be drawn startVector = XYToPoint(-startVector.y, -startVector.x); endVector = XYToPoint(-endVector.y, -endVector.x); } assert(abs(dirVector.x) >= abs(dirVector.y)); if (dirVector.x < 0) { Point tmp; tmp = start; start = end; end = tmp; tmp = startVector; startVector = endVector; endVector = tmp; dirVector = XYToPoint(-dirVector.x, -dirVector.y); } if (dirVector.x == 0) return; //start = end assert(dirVector.x > 0); { float orthogonalBroadness = sqrt((float)(dirVector.y*dirVector.y)/(float)(dirVector.x*dirVector.x)+1) * (float)broadness; //line algorithm int startx = start.x - broadness; int endx = end.x + broadness; int x, y; float gradient = (float)(end.y-start.y) / (end.x-start.x); float posy = start.y - broadness*gradient; for (x=startx; x<=endx; x++, posy += gradient) { for (y = (int)(posy - orthogonalBroadness/2) + 1; y <= (int)(posy + orthogonalBroadness/2); y++) { //check the crop lines if (startVector.x * (y-start.y) - startVector.y * (x-start.x) <= 0 && endVector.x * (y-end.y) - endVector.y * (x-end.x) <= 0) { int alpha; float pos; //ok, this point is not cropped //calculate distance to the center of the line (0=at the center, 1=at either border of the line) pos = abs((float)(y) - posy) / (float)(orthogonalBroadness/2); alpha = (1-pos) * 256; if (inverted) { DrawAlphaPixel(y, x, alpha, l->c); } else { DrawAlphaPixel(x, y, alpha, l->c); } } } } } } void DrawFog(E_Fog * f) { int x, y, cx, cy; int radius = f->radius; // OPTIMIZE: probably very slow (and ugly anyway) cx = f->pos256.x/256; cy = f->pos256.y/256; for (x=0; xmaxstrength/2-abs(f->strength-f->maxstrength/2)) / sqr(f->maxstrength/2); //alpha += rand() % 128 - 64; alpha += rand() % 32 - 16; DrawAlphaPixel(cx + x, cy + y, alpha, f->c); if (x!=0) DrawAlphaPixel(cx - x, cy + y, alpha, f->c); if (y!=0) DrawAlphaPixel(cx + x, cy - y, alpha, f->c); if (x!=0&&y!=0) DrawAlphaPixel(cx - x, cy - y, alpha, f->c); } } } // OPTIMIZE: don't redraw the whole screen, the spark might not even have been on the screen AddDirtyRect(screenRect); } void DrawSpark(E_Spark * s) { int x, y; int mul; // OPTIMIZE: probably very slow (and ugly anyway) if (s->radius <= 0) return; mul = 256 / sqr(s->radius); for (x=0; xradius; x++) { for (y=0; yradius; y++) { int alpha = 256 - (sqr(x) + sqr(y)) * mul; alpha += rand() % 128 - 64; DrawAlphaPixel(s->x + x, s->y + y, alpha, s->c); DrawAlphaPixel(s->x - x, s->y + y, alpha, s->c); DrawAlphaPixel(s->x + x, s->y - y, alpha, s->c); DrawAlphaPixel(s->x - x, s->y - y, alpha, s->c); } } // OPTIMIZE: don't redraw the whole screen, the spark might not even have been on the screen AddDirtyRect(screenRect); } void DrawLightning(E_Lightning * l) { int i; Point startVector, endVector; //Point start, end; assert(l->numPoints >= 2); for (i=0; inumPoints - 1; i++) { //calculate startVector if (i==0) { Point v; v = nowrap_VectorFromTo(l->points[0], l->points[1]); //turn it by 90 degree counterclockwisely startVector.x = -v.y; startVector.y = v.x; } else { //turn it by 180 degree startVector.x = -endVector.x; startVector.y = -endVector.y; } //calculate endVector if (i+2numPoints) { Point a, b; int la, lb; a = nowrap_VectorFromTo(l->points[i], l->points[i+1]); b = nowrap_VectorFromTo(l->points[i+1], l->points[i+2]); la = sqrt(sqr(a.x * 8) + sqr(a.y * 8)); lb = sqrt(sqr(b.x * 8) + sqr(b.y * 8)); // hack: noone will notice... if (la == 0) la = 1; if (lb == 0) lb = 1; a.x = 256 * a.x / la; a.y = 256 * a.y / la; b.x = 256 * b.x / la; b.y = 256 * b.y / lb; //to avoid a division by the length, I assume here that the //lines have always about the same length endVector = AddVectors(a, b); //assert(endVector.x != 0 || endVector.y != 0); } else { endVector = nowrap_VectorFromTo(l->points[i], l->points[i+1]); } if (endVector.x == 0 && endVector.y == 0) { // hack; noone will notice the uglyness :) endVector.x = 10; endVector.y = 10; } //turn it by 90 degree clockwisely endVector = XYToPoint(endVector.y, -endVector.x); DrawBroadLine(l->points[i], l->points[i+1], startVector, endVector, l); } /* OPTIMIZE: don't redraw the whole screen */ AddDirtyRect(screenRect); } void DrawEffects() { int i; if (fogRadius) DrawGlobalFog(); for (i=0; effects[i]; i++) { switch (effects[i]->type) { case E_LIGHTNING: DrawLightning((E_Lightning*)effects[i]); break; case E_SPARK: DrawSpark((E_Spark*)effects[i]); break; case E_FOG: DrawFog((E_Fog*)effects[i]); break; default: DebugLevel(2, "Warning: Video2D cannot display effect %d\n", effects[i]->type); } } //DrawMouse(); } void FreeDisplayData(DummyDisplayData * data) { DisplayData * d; d = (DisplayData*)data; AddDirtyRect(d->oldMaxRect); xfree(d); } void DrawFrame2d() { int mx, my; List * visibleMasses; Mass * m; SDL_Rect lastScreenRect = {0,0,DISPLAY_SCREENX,DISPLAY_SCREENY}; SDL_Rect dst; // the borders newly scrolled in Rect b1 = {0,0,DISPLAY_SCREENX,DISPLAY_SCREENY}; Rect b2 = {0,0,DISPLAY_SCREENX,DISPLAY_SCREENY}; lastScreenRect.x = screenRect.x; lastScreenRect.y = screenRect.y; lastScreenRect.w = screenRect.w; lastScreenRect.h = screenRect.h; // top left corner of the screen in mass coordinates screenRect.x = displayCenter.x - DISPLAY_SCREENX/2; screenRect.y = displayCenter.y - DISPLAY_SCREENY/2; // scrolling mx = lastScreenRect.x - screenRect.x; my = lastScreenRect.y - screenRect.y; mx += FIELDWRAP/2; mx &= FIELDWRAP; mx -= FIELDWRAP/2; my += FIELDWRAP/2; my &= FIELDWRAP; my -= FIELDWRAP/2; dst.x = mx; dst.y = my; //printf("Movement: %d, %d\n", mx, my); // SDL seems to do all the clipping the way I want it SDL_BlitSurface(screen, NULL, screen, &dst); // fill the newly scrolled in area black if (mx >= 0) { b1.x = 0; b1.w = mx; } else { b1.x = DISPLAY_SCREENX + mx; b1.w = -mx; } if (my >= 0) { b2.y = 0; b2.h = my; } else { b2.y = DISPLAY_SCREENY + my; b2.h = -my; } { SDL_Rect sdl_b1 = Rect2SDL_Rect(b1); SDL_Rect sdl_b2 = Rect2SDL_Rect(b2); SDL_FillRect(screen, &sdl_b1, 0); SDL_FillRect(screen, &sdl_b2, 0); b1 = SDL_Rect2Rect(sdl_b1); b2 = SDL_Rect2Rect(sdl_b2); } SCREEN_TO_MASS_COORDINATES(b1); SCREEN_TO_MASS_COORDINATES(b2); MASS_TO_SCREEN_COORDINATES(dirtyRect); { SDL_Rect sdl_dirtyRect = Rect2SDL_Rect(dirtyRect); SDL_FillRect(screen, &sdl_dirtyRect, 0); } SCREEN_TO_MASS_COORDINATES(dirtyRect); // prepare to draw pixels if ( SDL_LockSurface(screen) < 0 ) Die("Cold not lock screen surface!\n"); // FIXME: there is probably a bug when a mass moves out of the screen between two frames; // the mass is not processed here and thus it won't get removed from the screen. visibleMasses = GetMassesInRect(screenRect); FOR_ITEM_IN_LIST(m, visibleMasses) { Rect clip; clip = wrap_SmallestCommonRect(m->maxRect, screenRect); if (!m->mustRedraw && wrap_DoRectsOverlap(clip, dirtyRect)) m->mustRedraw = 1; if (m->mustRedraw && clip.w && clip.h) { DisplayData * d; d = (DisplayData*)m->displayData; if (d) { Rect sc; // screen clip // include the old rect to make sure all stale pixels get removed sc = wrap_SmallestCommonRect(d->oldMaxRect, screenRect); //printf("sc = %s\n", RectString(sc)); //printf("clip = %s\n", RectString(clip)); // hm... I somehow feel this check is necessary if (sc.w && sc.h) wrap_ExpandRectToIncludeRect(&clip, sc); //printf("clip2= %s\n", RectString(clip)); } DrawMass(m, clip); if (!d) { //printf("New DisplayData for mass %X\n", (int)m); d = calloc(1, sizeof(DisplayData)); d->freeVideo2dDisplayData = FreeDisplayData; m->displayData = (DummyDisplayData*)d; } d->oldMaxRect = m->maxRect; m->mustRedraw = 0; } else if (wrap_DoRectsOverlap(clip, b1) || wrap_DoRectsOverlap(clip, b2)) { // the mass is scrolling in but otherwise inactive, just draw at the borders DrawMass(m, wrap_SmallestCommonRect(clip, b1)); DrawMass(m, wrap_SmallestCommonRect(clip, b2)); } } list_free(visibleMasses); // dirty rect is for the effects and for destroyed masses dirtyRect.w = 0; dirtyRect.h = 0; if (debugMode) { DrawDebug(); AddDirtyRect(screenRect); } else { DrawEffects(); } // done drawing pixels SDL_UnlockSurface(screen); if (debugMode) { dst.x = 0; dst.y = screen->h - statusLine->h; SDL_BlitSurface(statusLine, NULL, screen, &dst); } SDL_UpdateRect(screen, 0, 0, 0, 0); } void DebugPostprocessPixel(Mass * m, int x, int y, Color color /* variable */, int shadow) { int i; FieldPoint * p = FieldXY(x, y); switch (key.debugCycle->hits % 4) { case 0: /* no debug color change */ break; case 1: /* HEIGHT_NORM*4 stripes */ if ((p->height / (HEIGHT_NORM*4)) % 2 == 0) { color[0] = color[1] = color[2] = 255; } else { color[0] = color[1] = color[2] = 100; } break; case 2: /* HEIGHT_NORM*16 stripes */ if ((p->height / (HEIGHT_NORM*16)) % 2 == 0) { color[1] = 255; } else { color[1] = 0; } break; case 3: /* simple shadow value */ for (i=0; i<3; i++) color[i] = ((int)m->debugColor[i] * shadow) >> 8; break; default: assert(0); } if (m == watchedMass) { for (i=0; i<3; i++) { color[i] = MIN(color[i] + 100, 255); } } } inline void DrawParticle(int x, int y, Color color) { Point p; p.x = x; p.y = y; MASS_TO_SCREEN_COORDINATES(p); if (p.x >= 1 && p.y >= 1 && p.x < DISPLAY_SCREENX-1 && p.y < DISPLAY_SCREENY-1) { // * // *** // * SetPixel(p.x , p.y , color); SetPixel(p.x+1, p.y , color); SetPixel(p.x , p.y+1, color); SetPixel(p.x-1, p.y , color); SetPixel(p.x , p.y-1, color); // InvertPixel? } } void DrawDebug() { /* mouse in the field */ Point fmouse; Mass * mouseMass; fmouse.x = screenRect.x + mouse.pos.x; fmouse.y = screenRect.y + mouse.pos.y; mouseMass = FieldPoint(XYToPoint(fmouse.x, fmouse.y))->owner; if (mouse.left.hit) { if (watchedMass) watchedMass->refcount--; watchedMass = mouseMass; if (watchedMass) { watchedMass->refcount++; PrintMassInformation(watchedMass); } SDL_FillRect(statusLine, NULL, 0); font_setsurface(statusLine); if (watchedMass) { font_printf(0, 0, "%s age %d", watchedMass->class, watchedMass->age); } } if (CountPlayers() == 1) { // This bypasses the network protocol and the logfile // FIXME: which is a bad thing, there should be engine commands for this stuff // (for video2d.c the engine should be read-only) if (mouseMass) { int speedadd = 0; if (key.growMass->pressed) { mouseMass->bufferMass += HEIGHT_NORM*mouseMass->numPixels; speedadd = 5; } if (key.shrinkMass->pressed) { mouseMass->bufferMass -= HEIGHT_NORM*mouseMass->numPixels; speedadd = 5; } if (key.speedUpMass->pressed) speedadd = 5; if (key.speedDownMass->pressed) speedadd = -5; if (speedadd) { mouseMass->speed = MAX(0, mouseMass->speed + speedadd); DebugLevel(2, "speed = %d\n", mouseMass->speed); } if (key.deepFryMass->pressed) { mouseMass->speed = 0; mouseMass->wakeOnPlayer = 1; } if (key.killMass->pressed) RemoveMassFromField(mouseMass); } if (key.placeMass1->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass1); if (key.placeMass2->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass2); if (key.placeMass3->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass3); if (key.placeMass4->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass4); if (key.placeMass5->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass5); if (key.placeMass6->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass6); if (key.placeMass7->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass7); if (key.placeMass8->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass8); if (key.placeMass9->hit) PlaceMassAt(fmouse.x, fmouse.y, engine.placeableMass9); } /* Particles */ if (particleTraces) { ParticleTrace * ptt; //printf("Drawing %d tracepoints.\n", list_len(particleTraces)); FOR_ITEM_IN_LIST(ptt, particleTraces) { DrawParticle(ptt->pos.x, ptt->pos.y, ptt->color); } } else { List * visibleParticles; Particle * pt; //visibleParticles = GetParticlesInRect(screenRect); visibleParticles = GetAllParticles(); FOR_ITEM_IN_LIST(pt, visibleParticles) { DrawParticle(pt->x, pt->y, pt->debugColor); } list_free(visibleParticles); } if (key.debugParticles->pressed && !particleTraces) InitParticleTraces(); if (!key.debugParticles->pressed && particleTraces) FreeParticleTraces(); /* Mass-o-Meter */ { Color bg = {0,0,0}, border = {255,255,255}; int x, y; Rect position; position.x = DISPLAY_SCREENX-80-3; position.y = 3; position.w = 80; position.h = 80; for (x=position.x; x= DISPLAY_SCREENX || py >= DISPLAY_SCREENY) { memcpy(fg, bg, sizeof(fg)); height = 0; } else { FieldPoint * fp; //first highlight (invert) the point monitored if (!nowrap_IsPointInRect(XYToPoint(px, py), position) && !key.debugRect->toggled) { InvertPixel(px, py); } //now get height and color fp = FieldXY(px + screenRect.x, py + screenRect.y); if (!fp->owner) { if (!fp->height) { memcpy(fg, bg, sizeof(fg)); } else { // special mark for pixels with height but no owner RGBSET(fg, 128, 128, 128); brighttip = 1; } } else { memcpy(fg, fp->owner->debugColor, sizeof(fg)); } height = fp->height; if (height < 0) { //there should never be a negative height value, so mark it red RGBSET(fg, 255, 50, 50); height = 1; } else { int zoom; zoom = key.debugZin->hits - key.debugZout->hits; /* standard display (zoom 0): HEIGHT_NORM ^= 1 pixel */ height /= (double)(HEIGHT_NORM); if (zoom > 0) height *= 2 << zoom; if (zoom < 0) height /= 2 << (-zoom); } } //now display it for (y=position.y; yshapePoints[i].pos; wrap_FitPointIntoRect(&p, screenRect); p.x -= screenRect.x; p.y -= screenRect.y; p.x &= FIELDWRAP; p.y &= FIELDWRAP; InvertPixel(p.x, p.y); } if (key.debugRect->toggled) { //r.x--; r.y--; r.w += 2; r.h += 2; r = wrap_SmallestCommonRect(m->maxRect, screenRect); r.x -= screenRect.x; r.y -= screenRect.y; for (i=r.x; i