diff options
| author | Simon Robertshaw <simon@hardwired.org.uk> | 2012-01-08 17:39:03 (GMT) |
|---|---|---|
| committer | Simon Robertshaw <simon@hardwired.org.uk> | 2012-01-08 17:39:03 (GMT) |
| commit | b0ea52690ba56a0d0602ad8674b7e5ab2ba3e778 (patch) | |
| tree | 7d72e0509f4d2643d3be837a337d088ca5949c73 /src | |
| download | powder-b0ea52690ba56a0d0602ad8674b7e5ab2ba3e778.zip powder-b0ea52690ba56a0d0602ad8674b7e5ab2ba3e778.tar.gz | |
Initial
Diffstat (limited to 'src')
| -rw-r--r-- | src/Air.cpp | 301 | ||||
| -rw-r--r-- | src/Graphics.cpp | 2359 | ||||
| -rw-r--r-- | src/Gravity.cpp | 488 | ||||
| -rw-r--r-- | src/Misc.cpp | 664 | ||||
| -rw-r--r-- | src/PowderToy.cpp | 95 | ||||
| -rw-r--r-- | src/Renderer.cpp | 1692 | ||||
| -rw-r--r-- | src/Simulation.cpp | 3591 | ||||
| -rw-r--r-- | src/interface/Button.cpp | 127 | ||||
| -rw-r--r-- | src/interface/Component.cpp | 89 | ||||
| -rw-r--r-- | src/interface/Panel.cpp | 23 | ||||
| -rw-r--r-- | src/interface/Sandbox.cpp | 57 | ||||
| -rw-r--r-- | src/interface/State.cpp | 232 | ||||
| -rw-r--r-- | src/interface/Window.cpp | 23 |
13 files changed, 9741 insertions, 0 deletions
diff --git a/src/Air.cpp b/src/Air.cpp new file mode 100644 index 0000000..2d8d1b5 --- /dev/null +++ b/src/Air.cpp @@ -0,0 +1,301 @@ +#include <math.h> +#include "Config.h" +#include "Air.h" +//#include <powder.h> +//#include <defines.h> +#include "gravity.h" + +/*float kernel[9]; + +float vx[YRES/CELL][XRES/CELL], ovx[YRES/CELL][XRES/CELL]; +float vy[YRES/CELL][XRES/CELL], ovy[YRES/CELL][XRES/CELL]; +float pv[YRES/CELL][XRES/CELL], opv[YRES/CELL][XRES/CELL]; +unsigned char bmap_blockair[YRES/CELL][XRES/CELL]; + +float cb_vx[YRES/CELL][XRES/CELL]; +float cb_vy[YRES/CELL][XRES/CELL]; +float cb_pv[YRES/CELL][XRES/CELL]; +float cb_hv[YRES/CELL][XRES/CELL]; + +float fvx[YRES/CELL][XRES/CELL], fvy[YRES/CELL][XRES/CELL]; + +float hv[YRES/CELL][XRES/CELL], ohv[YRES/CELL][XRES/CELL]; // For Ambient Heat */ + +void Air::make_kernel(void) //used for velocity +{ + int i, j; + float s = 0.0f; + for (j=-1; j<2; j++) + for (i=-1; i<2; i++) + { + kernel[(i+1)+3*(j+1)] = expf(-2.0f*(i*i+j*j)); + s += kernel[(i+1)+3*(j+1)]; + } + s = 1.0f / s; + for (j=-1; j<2; j++) + for (i=-1; i<2; i++) + kernel[(i+1)+3*(j+1)] *= s; +} +void Air::update_airh(void) +{ + int x, y, i, j; + float dh, dx, dy, f, tx, ty; + for (i=0; i<YRES/CELL; i++) //reduces pressure/velocity on the edges every frame + { + hv[i][0] = 295.15f; + hv[i][1] = 295.15f; + hv[i][XRES/CELL-3] = 295.15f; + hv[i][XRES/CELL-2] = 295.15f; + hv[i][XRES/CELL-1] = 295.15f; + } + for (i=0; i<XRES/CELL; i++) //reduces pressure/velocity on the edges every frame + { + hv[0][i] = 295.15f; + hv[1][i] = 295.15f; + hv[YRES/CELL-3][i] = 295.15f; + hv[YRES/CELL-2][i] = 295.15f; + hv[YRES/CELL-1][i] = 295.15f; + } + for (y=0; y<YRES/CELL; y++) //update velocity and pressure + { + for (x=0; x<XRES/CELL; x++) + { + dh = 0.0f; + dx = 0.0f; + dy = 0.0f; + for (j=-1; j<2; j++) + { + for (i=-1; i<2; i++) + { + if (y+j>0 && y+j<YRES/CELL-2 && + x+i>0 && x+i<XRES/CELL-2 && + bmap[y+j][x+i]!=WL_WALL && + bmap[y+j][x+i]!=WL_WALLELEC && + bmap[y+j][x+i]!=WL_GRAV && + (bmap[y+j][x+i]!=WL_EWALL || emap[y+j][x+i])) + { + f = kernel[i+1+(j+1)*3]; + dh += hv[y+j][x+i]*f; + dx += vx[y+j][x+i]*f; + dy += vy[y+j][x+i]*f; + } + else + { + f = kernel[i+1+(j+1)*3]; + dh += hv[y][x]*f; + dx += vx[y][x]*f; + dy += vy[y][x]*f; + } + } + } + tx = x - dx*0.7f; + ty = y - dy*0.7f; + i = (int)tx; + j = (int)ty; + tx -= i; + ty -= j; + if (i>=2 && i<XRES/CELL-3 && j>=2 && j<YRES/CELL-3) + { + dh *= 1.0f - AIR_VADV; + dh += AIR_VADV*(1.0f-tx)*(1.0f-ty)*hv[j][i]; + dh += AIR_VADV*tx*(1.0f-ty)*hv[j][i+1]; + dh += AIR_VADV*(1.0f-tx)*ty*hv[j+1][i]; + dh += AIR_VADV*tx*ty*hv[j+1][i+1]; + } + //if(!gravityMode) TODO: GET REAL VALUE + { //Vertical gravity only for the time being + float airdiff = dh-hv[y][x]; + pv[y][x] += airdiff/5000.0f; + if(airdiff>0) + vy[y][x] -= airdiff/5000.0f; + } + ohv[y][x] = dh; + } + } + memcpy(hv, ohv, sizeof(hv)); +} + +void Air::update_air(void) +{ + int x, y, i, j; + float dp, dx, dy, f, tx, ty; + + for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + { + bmap_blockair[y][x] = (bmap[y][x]==WL_WALL || bmap[y][x]==WL_WALLELEC || (bmap[y][x]==WL_EWALL && !emap[y][x])); + } + if (airMode != 4) { //airMode 4 is no air/pressure update + + for (i=0; i<YRES/CELL; i++) //reduces pressure/velocity on the edges every frame + { + pv[i][0] = pv[i][0]*0.8f; + pv[i][1] = pv[i][1]*0.8f; + pv[i][2] = pv[i][2]*0.8f; + pv[i][XRES/CELL-2] = pv[i][XRES/CELL-2]*0.8f; + pv[i][XRES/CELL-1] = pv[i][XRES/CELL-1]*0.8f; + vx[i][0] = vx[i][1]*0.9f; + vx[i][1] = vx[i][2]*0.9f; + vx[i][XRES/CELL-2] = vx[i][XRES/CELL-3]*0.9f; + vx[i][XRES/CELL-1] = vx[i][XRES/CELL-2]*0.9f; + vy[i][0] = vy[i][1]*0.9f; + vy[i][1] = vy[i][2]*0.9f; + vy[i][XRES/CELL-2] = vy[i][XRES/CELL-3]*0.9f; + vy[i][XRES/CELL-1] = vy[i][XRES/CELL-2]*0.9f; + } + for (i=0; i<XRES/CELL; i++) //reduces pressure/velocity on the edges every frame + { + pv[0][i] = pv[0][i]*0.8f; + pv[1][i] = pv[1][i]*0.8f; + pv[2][i] = pv[2][i]*0.8f; + pv[YRES/CELL-2][i] = pv[YRES/CELL-2][i]*0.8f; + pv[YRES/CELL-1][i] = pv[YRES/CELL-1][i]*0.8f; + vx[0][i] = vx[1][i]*0.9f; + vx[1][i] = vx[2][i]*0.9f; + vx[YRES/CELL-2][i] = vx[YRES/CELL-3][i]*0.9f; + vx[YRES/CELL-1][i] = vx[YRES/CELL-2][i]*0.9f; + vy[0][i] = vy[1][i]*0.9f; + vy[1][i] = vy[2][i]*0.9f; + vy[YRES/CELL-2][i] = vy[YRES/CELL-3][i]*0.9f; + vy[YRES/CELL-1][i] = vy[YRES/CELL-2][i]*0.9f; + } + + for (j=1; j<YRES/CELL; j++) //clear some velocities near walls + { + for (i=1; i<XRES/CELL; i++) + { + if (bmap_blockair[j][i]) + { + vx[j][i] = 0.0f; + vx[j][i-1] = 0.0f; + vy[j][i] = 0.0f; + vy[j-1][i] = 0.0f; + } + } + } + + for (y=1; y<YRES/CELL; y++) //pressure adjustments from velocity + for (x=1; x<XRES/CELL; x++) + { + dp = 0.0f; + dp += vx[y][x-1] - vx[y][x]; + dp += vy[y-1][x] - vy[y][x]; + pv[y][x] *= AIR_PLOSS; + pv[y][x] += dp*AIR_TSTEPP; + } + + for (y=0; y<YRES/CELL-1; y++) //velocity adjustments from pressure + for (x=0; x<XRES/CELL-1; x++) + { + dx = dy = 0.0f; + dx += pv[y][x] - pv[y][x+1]; + dy += pv[y][x] - pv[y+1][x]; + vx[y][x] *= AIR_VLOSS; + vy[y][x] *= AIR_VLOSS; + vx[y][x] += dx*AIR_TSTEPV; + vy[y][x] += dy*AIR_TSTEPV; + if (bmap_blockair[y][x] || bmap_blockair[y][x+1]) + vx[y][x] = 0; + if (bmap_blockair[y][x] || bmap_blockair[y+1][x]) + vy[y][x] = 0; + } + + for (y=0; y<YRES/CELL; y++) //update velocity and pressure + for (x=0; x<XRES/CELL; x++) + { + dx = 0.0f; + dy = 0.0f; + dp = 0.0f; + for (j=-1; j<2; j++) + for (i=-1; i<2; i++) + if (y+j>0 && y+j<YRES/CELL-1 && + x+i>0 && x+i<XRES/CELL-1 && + !bmap_blockair[y+j][x+i]) + { + f = kernel[i+1+(j+1)*3]; + dx += vx[y+j][x+i]*f; + dy += vy[y+j][x+i]*f; + dp += pv[y+j][x+i]*f; + } + else + { + f = kernel[i+1+(j+1)*3]; + dx += vx[y][x]*f; + dy += vy[y][x]*f; + dp += pv[y][x]*f; + } + + tx = x - dx*0.7f; + ty = y - dy*0.7f; + i = (int)tx; + j = (int)ty; + tx -= i; + ty -= j; + if (i>=2 && i<XRES/CELL-3 && + j>=2 && j<YRES/CELL-3) + { + dx *= 1.0f - AIR_VADV; + dy *= 1.0f - AIR_VADV; + + dx += AIR_VADV*(1.0f-tx)*(1.0f-ty)*vx[j][i]; + dy += AIR_VADV*(1.0f-tx)*(1.0f-ty)*vy[j][i]; + + dx += AIR_VADV*tx*(1.0f-ty)*vx[j][i+1]; + dy += AIR_VADV*tx*(1.0f-ty)*vy[j][i+1]; + + dx += AIR_VADV*(1.0f-tx)*ty*vx[j+1][i]; + dy += AIR_VADV*(1.0f-tx)*ty*vy[j+1][i]; + + dx += AIR_VADV*tx*ty*vx[j+1][i+1]; + dy += AIR_VADV*tx*ty*vy[j+1][i+1]; + } + + if (bmap[y][x] == WL_FAN) + { + dx += fvx[y][x]; + dy += fvy[y][x]; + } + // pressure/velocity caps + if (dp > 256.0f) dp = 256.0f; + if (dp < -256.0f) dp = -256.0f; + if (dx > 256.0f) dx = 256.0f; + if (dx < -256.0f) dx = -256.0f; + if (dy > 256.0f) dy = 256.0f; + if (dy < -256.0f) dy = -256.0f; + + + switch (airMode) + { + default: + case 0: //Default + break; + case 1: //0 Pressure + dp = 0.0f; + break; + case 2: //0 Velocity + dx = 0.0f; + dy = 0.0f; + break; + case 3: //0 Air + dx = 0.0f; + dy = 0.0f; + dp = 0.0f; + break; + case 4: //No Update + break; + } + + ovx[y][x] = dx; + ovy[y][x] = dy; + opv[y][x] = dp; + } + memcpy(vx, ovx, sizeof(vx)); + memcpy(vy, ovy, sizeof(vy)); + memcpy(pv, opv, sizeof(pv)); + } +} +Air::Air() +{ + //Simulation should do this. + make_kernel(); +} diff --git a/src/Graphics.cpp b/src/Graphics.cpp new file mode 100644 index 0000000..52fa4c9 --- /dev/null +++ b/src/Graphics.cpp @@ -0,0 +1,2359 @@ +#include <math.h> +#include <SDL/SDL.h> +#include <bzlib.h> +#include <string> + +#if defined(OGLR) +#ifdef MACOSX +#include <GL/glew.h> +#include <OpenGL/gl.h> +#include <OpenGL/glu.h> +#elif defined(WIN32) +#include <GL/glew.h> +#include <GL/gl.h> +#include <GL/glu.h> +#else +#include <GL/gl.h> +#include <GL/glu.h> +#endif +#endif + +#include "Config.h" +#include "air.h" +#include "gravity.h" +//#include "powder.h" +#define INCLUDE_PSTRUCT +#include "Simulation.h" +#include "Graphics.h" +#include "ElementGraphics.h" +#define INCLUDE_FONTDATA +#include "font.h" +#include "misc.h" +#include "hmap.h" + +//unsigned cmode = CM_FIRE; +unsigned int *render_modes; +unsigned int render_mode; +unsigned int colour_mode; +unsigned int *display_modes; +unsigned int display_mode; + +//SDL_Surface *sdl_scrn; +int sdl_scale = 1; + +#ifdef OGLR +GLuint zoomTex, vidBuf, airBuf, fireAlpha, glowAlpha, blurAlpha, partsFboTex, partsFbo, partsTFX, partsTFY, airPV, airVY, airVX; +GLuint fireProg, airProg_Pressure, airProg_Velocity, airProg_Cracker, lensProg; +#endif + +/* +int emp_decor = 0; +int sandcolour_r = 0; +int sandcolour_g = 0; +int sandcolour_b = 0; +int sandcolour_frame = 0; + +unsigned char fire_r[YRES/CELL][XRES/CELL]; +unsigned char fire_g[YRES/CELL][XRES/CELL]; +unsigned char fire_b[YRES/CELL][XRES/CELL]; + +unsigned int fire_alpha[CELL*3][CELL*3]; +pixel *pers_bg; + +char * flm_data; +int flm_data_points = 4; +pixel flm_data_colours[] = {PIXPACK(0xAF9F0F), PIXPACK(0xDFBF6F), PIXPACK(0x60300F), PIXPACK(0x000000)}; +float flm_data_pos[] = {1.0f, 0.9f, 0.5f, 0.0f}; + +char * plasma_data; +int plasma_data_points = 5; +pixel plasma_data_colours[] = {PIXPACK(0xAFFFFF), PIXPACK(0xAFFFFF), PIXPACK(0x301060), PIXPACK(0x301040), PIXPACK(0x000000)}; +float plasma_data_pos[] = {1.0f, 0.9f, 0.5f, 0.25, 0.0f};*/ + +char * Graphics::generate_gradient(pixel * colours, float * points, int pointcount, int size) +{ + int cp, i, j; + pixel ptemp; + char * newdata = (char*)malloc(size * 3); + float poss, pose, temp; + memset(newdata, 0, size*3); + //Sort the Colours and Points + for (i = (pointcount - 1); i > 0; i--) + { + for (j = 1; j <= i; j++) + { + if (points[j-1] > points[j]) + { + temp = points[j-1]; + points[j-1] = points[j]; + points[j] = temp; + + ptemp = colours[j-1]; + colours[j-1] = colours[j]; + colours[j] = ptemp; + } + } + } + i = 0; + j = 1; + poss = points[i]; + pose = points[j]; + for (cp = 0; cp < size; cp++) + { + float cpos = (float)cp / (float)size, ccpos, cccpos; + if(cpos > pose && j+1 < pointcount) + { + poss = points[++i]; + pose = points[++j]; + } + ccpos = cpos - poss; + cccpos = ccpos / (pose - poss); + if(cccpos > 1.0f) + cccpos = 1.0f; + newdata[(cp*3)] = PIXR(colours[i])*(1.0f-cccpos) + PIXR(colours[j])*(cccpos); + newdata[(cp*3)+1] = PIXG(colours[i])*(1.0f-cccpos) + PIXG(colours[j])*(cccpos); + newdata[(cp*3)+2] = PIXB(colours[i])*(1.0f-cccpos) + PIXB(colours[j])*(cccpos); + } + return newdata; +} + +void *Graphics::ptif_pack(pixel *src, int w, int h, int *result_size){ + int i = 0, datalen = (w*h)*3, cx = 0, cy = 0; + unsigned char *red_chan = (unsigned char*)calloc(1, w*h); + unsigned char *green_chan = (unsigned char*)calloc(1, w*h); + unsigned char *blue_chan = (unsigned char*)calloc(1, w*h); + unsigned char *data = (unsigned char*)malloc(((w*h)*3)+8); + unsigned char *result = (unsigned char*)malloc(((w*h)*3)+8); + + for(cx = 0; cx<w; cx++){ + for(cy = 0; cy<h; cy++){ + red_chan[w*(cy)+(cx)] = PIXR(src[w*(cy)+(cx)]); + green_chan[w*(cy)+(cx)] = PIXG(src[w*(cy)+(cx)]); + blue_chan[w*(cy)+(cx)] = PIXB(src[w*(cy)+(cx)]); + } + } + + memcpy(data, red_chan, w*h); + memcpy(data+(w*h), green_chan, w*h); + memcpy(data+((w*h)*2), blue_chan, w*h); + free(red_chan); + free(green_chan); + free(blue_chan); + + result[0] = 'P'; + result[1] = 'T'; + result[2] = 'i'; + result[3] = 1; + result[4] = w; + result[5] = w>>8; + result[6] = h; + result[7] = h>>8; + + i -= 8; + + if(BZ2_bzBuffToBuffCompress((char *)(result+8), (unsigned *)&i, (char *)data, datalen, 9, 0, 0) != 0){ + free(data); + free(result); + return NULL; + } + + *result_size = i+8; + free(data); + return result; +} + +pixel *Graphics::ptif_unpack(void *datain, int size, int *w, int *h){ + int width, height, i, cx, cy, resCode; + unsigned char *red_chan; + unsigned char *green_chan; + unsigned char *blue_chan; + unsigned char *data = (unsigned char*)datain; + unsigned char *undata; + pixel *result; + if(size<16){ + printf("Image empty\n"); + return NULL; + } + if(!(data[0]=='P' && data[1]=='T' && data[2]=='i')){ + printf("Image header invalid\n"); + return NULL; + } + width = data[4]|(data[5]<<8); + height = data[6]|(data[7]<<8); + + i = (width*height)*3; + undata = (unsigned char*)calloc(1, (width*height)*3); + red_chan = (unsigned char*)calloc(1, width*height); + green_chan = (unsigned char*)calloc(1, width*height); + blue_chan = (unsigned char *)calloc(1, width*height); + result = (pixel *)calloc(width*height, PIXELSIZE); + + resCode = BZ2_bzBuffToBuffDecompress((char *)undata, (unsigned *)&i, (char *)(data+8), size-8, 0, 0); + if (resCode){ + printf("Decompression failure, %d\n", resCode); + free(red_chan); + free(green_chan); + free(blue_chan); + free(undata); + free(result); + return NULL; + } + if(i != (width*height)*3){ + printf("Result buffer size mismatch, %d != %d\n", i, (width*height)*3); + free(red_chan); + free(green_chan); + free(blue_chan); + free(undata); + free(result); + return NULL; + } + memcpy(red_chan, undata, width*height); + memcpy(green_chan, undata+(width*height), width*height); + memcpy(blue_chan, undata+((width*height)*2), width*height); + + for(cx = 0; cx<width; cx++){ + for(cy = 0; cy<height; cy++){ + result[width*(cy)+(cx)] = PIXRGB(red_chan[width*(cy)+(cx)], green_chan[width*(cy)+(cx)], blue_chan[width*(cy)+(cx)]); + } + } + + *w = width; + *h = height; + free(red_chan); + free(green_chan); + free(blue_chan); + free(undata); + return result; +} + +pixel *Graphics::resample_img_nn(pixel * src, int sw, int sh, int rw, int rh) +{ + int y, x; + pixel *q = NULL; + q = (pixel *)malloc(rw*rh*PIXELSIZE); + for (y=0; y<rh; y++) + for (x=0; x<rw; x++){ + q[rw*y+x] = src[sw*(y*sh/rh)+(x*sw/rw)]; + } + return q; +} + +pixel *Graphics::resample_img(pixel *src, int sw, int sh, int rw, int rh) +{ + int y, x, fxceil, fyceil; + //int i,j,x,y,w,h,r,g,b,c; + pixel *q = NULL; + //TODO: Actual resampling, this is just cheap nearest pixel crap + if(rw == sw && rh == sh){ + //Don't resample + q = (pixel *)malloc(rw*rh*PIXELSIZE); + memcpy(q, src, rw*rh*PIXELSIZE); + } else if(rw > sw && rh > sh){ + float fx, fy, fyc, fxc; + double intp; + pixel tr, tl, br, bl; + q = (pixel *)malloc(rw*rh*PIXELSIZE); + //Bilinear interpolation for upscaling + for (y=0; y<rh; y++) + for (x=0; x<rw; x++) + { + fx = ((float)x)*((float)sw)/((float)rw); + fy = ((float)y)*((float)sh)/((float)rh); + fxc = modf(fx, &intp); + fyc = modf(fy, &intp); + fxceil = (int)ceil(fx); + fyceil = (int)ceil(fy); + if (fxceil>=sw) fxceil = sw-1; + if (fyceil>=sh) fyceil = sh-1; + tr = src[sw*(int)floor(fy)+fxceil]; + tl = src[sw*(int)floor(fy)+(int)floor(fx)]; + br = src[sw*fyceil+fxceil]; + bl = src[sw*fyceil+(int)floor(fx)]; + q[rw*y+x] = PIXRGB( + (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), + (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), + (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) + ); + } + } else { + //Stairstepping + float fx, fy, fyc, fxc; + double intp; + pixel tr, tl, br, bl; + int rrw = rw, rrh = rh; + pixel * oq; + oq = (pixel *)malloc(sw*sh*PIXELSIZE); + memcpy(oq, src, sw*sh*PIXELSIZE); + rw = sw; + rh = sh; + while(rrw != rw && rrh != rh){ + rw *= 0.7; + rh *= 0.7; + if(rw <= rrw || rh <= rrh){ + rw = rrw; + rh = rrh; + } + q = (pixel *)malloc(rw*rh*PIXELSIZE); + //Bilinear interpolation for upscaling + for (y=0; y<rh; y++) + for (x=0; x<rw; x++) + { + fx = ((float)x)*((float)sw)/((float)rw); + fy = ((float)y)*((float)sh)/((float)rh); + fxc = modf(fx, &intp); + fyc = modf(fy, &intp); + fxceil = (int)ceil(fx); + fyceil = (int)ceil(fy); + if (fxceil>=sw) fxceil = sw-1; + if (fyceil>=sh) fyceil = sh-1; + tr = oq[sw*(int)floor(fy)+fxceil]; + tl = oq[sw*(int)floor(fy)+(int)floor(fx)]; + br = oq[sw*fyceil+fxceil]; + bl = oq[sw*fyceil+(int)floor(fx)]; + q[rw*y+x] = PIXRGB( + (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), + (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), + (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) + ); + } + free(oq); + oq = q; + sw = rw; + sh = rh; + } + } + return q; +} + +pixel *Graphics::rescale_img(pixel *src, int sw, int sh, int *qw, int *qh, int f) +{ + int i,j,x,y,w,h,r,g,b,c; + pixel p, *q; + w = (sw+f-1)/f; + h = (sh+f-1)/f; + q = (pixel *)malloc(w*h*PIXELSIZE); + for (y=0; y<h; y++) + for (x=0; x<w; x++) + { + r = g = b = c = 0; + for (j=0; j<f; j++) + for (i=0; i<f; i++) + if (x*f+i<sw && y*f+j<sh) + { + p = src[(y*f+j)*sw + (x*f+i)]; + if (p) + { + r += PIXR(p); + g += PIXG(p); + b += PIXB(p); + c ++; + } + } + if (c>1) + { + r = (r+c/2)/c; + g = (g+c/2)/c; + b = (b+c/2)/c; + } + q[y*w+x] = PIXRGB(r, g, b); + } + *qw = w; + *qh = h; + return q; +} + +#ifdef OGLR +void clearScreen(float alpha) +{ + if(alpha > 0.999f) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + else + { + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(XRES, 0); + glVertex2f(XRES, YRES); + glVertex2f(0, YRES); + glEnd(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlendEquation(GL_FUNC_ADD); + } + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void clearScreenNP(float alpha) +{ + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void ogl_blit(int x, int y, int w, int h, pixel *src, int pitch, int scale) +{ + + //glDrawPixels(w,h,GL_BGRA,GL_UNSIGNED_BYTE,src); //Why does this still think it's ABGR? + glEnable( GL_TEXTURE_2D ); + glBindTexture(GL_TEXTURE_2D, vidBuf); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES+BARSIZE, YRES+MENUSIZE, GL_BGRA, GL_UNSIGNED_BYTE, src); + glBegin(GL_QUADS); + glTexCoord2d(1, 0); + glVertex3f((XRES+BARSIZE)*sdl_scale, (YRES+MENUSIZE)*sdl_scale, 1.0); + glTexCoord2d(0, 0); + glVertex3f(0, (YRES+MENUSIZE)*sdl_scale, 1.0); + glTexCoord2d(0, 1); + glVertex3f(0, 0, 1.0); + glTexCoord2d(1, 1); + glVertex3f((XRES+BARSIZE)*sdl_scale, 0, 1.0); + glEnd(); + + glDisable( GL_TEXTURE_2D ); + glFlush(); + SDL_GL_SwapBuffers (); +} +#endif + +//an easy way to draw a blob +void Graphics::drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb) +{ + blendpixel(x+1, y, cr, cg, cb, 112); + blendpixel(x-1, y, cr, cg, cb, 112); + blendpixel(x, y+1, cr, cg, cb, 112); + blendpixel(x, y-1, cr, cg, cb, 112); + + blendpixel(x+1, y-1, cr, cg, cb, 64); + blendpixel(x-1, y-1, cr, cg, cb, 64); + blendpixel(x+1, y+1, cr, cg, cb, 64); + blendpixel(x-1, y+1, cr, cg, cb, 64); +} + +//draws walls and elements for menu +/*int draw_tool_xy(pixel *vid_buf, int x, int y, int b, unsigned pc) +{ + int i, j, c; + pixel gc; + if (x > XRES-26 || x < 0) + return 26; + if ((b&0xFF) == PT_LIFE) + { + for (j=1; j<15; j++) + { + for (i=1; i<27; i++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + c = PIXB(pc) + 3*PIXG(pc) + 2*PIXR(pc); + if (c<544) + { + c = 255; + } + else + { + c = 0; + } + drawtext(vid_buf, x+14-textwidth((char *)gmenu[(b>>8)&0xFF].name)/2, y+4, (char *)gmenu[(b>>8)&0xFF].name, c, c, c, 255); + } + else if (b>=UI_WALLSTART) + { + int ds = 0; + if (b-UI_WALLSTART>=0 && b-UI_WALLSTART<UI_WALLCOUNT) + { + ds = wtypes[b-UI_WALLSTART].drawstyle; + gc = wtypes[b-UI_WALLSTART].eglow; + } + //x = (2+32*((b-22)/1)); + //y = YRES+2+40; + if (ds==1) + { + for (j=1; j<15; j+=2) + for (i=1+(1&(j>>1)); i<27; i+=2) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + else if (ds==2) + { + for (j=1; j<15; j+=2) + for (i=1; i<27; i+=2) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + else if (ds==3) + { + for (j=1; j<15; j++) + for (i=1; i<27; i++) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + else if (ds==4) + { + for (j=1; j<15; j++) + for (i=1; i<27; i++) + if(i%CELL == j%CELL) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + else if (i%CELL == (j%CELL)+1 || (i%CELL == 0 && j%CELL == CELL-1)) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = gc; + else + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = PIXPACK(0x202020); + } + else + switch (b) + { + case WL_WALLELEC+100: + for (j=1; j<15; j++) + { + for (i=1; i<27; i++) + { + if (!(i%2) && !(j%2)) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + else + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = PIXPACK(0x808080); + } + } + } + break; + case WL_EWALL+100: + for (j=1; j<15; j++) + { + for (i=1; i<6+j; i++) + { + if (!(i&j&1)) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + for (; i<27; i++) + { + if (i&j&1) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + } + break; + case WL_STREAM+100: + for (j=1; j<15; j++) + { + for (i=1; i<27; i++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = i==1||i==26||j==1||j==14 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000); + drawtext(vid_buf, x+4, y+3, "\x8D", 255, 255, 255, 255); + } + } + for (i=9; i<27; i++) + { + drawpixel(vid_buf, x+i, y+8+(int)(3.9f*cos(i*0.3f)), 255, 255, 255, 255); + } + break; + case WL_SIGN+100: + for (j=1; j<15; j++) + { + for (i=1; i<27; i++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = i==1||i==26||j==1||j==14 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000); + } + } + drawtext(vid_buf, x+9, y+3, "\xA1", 32, 64, 128, 255); + drawtext(vid_buf, x+9, y+3, "\xA0", 255, 255, 255, 255); + break; + case WL_ERASE+100: + for (j=1; j<15; j+=2) + { + for (i=1+(1&(j>>1)); i<13; i+=2) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + for (j=1; j<15; j++) + { + for (i=14; i<27; i++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + break; + case SPC_AIR: + case SPC_HEAT: + case SPC_COOL: + case SPC_VACUUM: + case SPC_WIND: + case SPC_PGRV: + case SPC_NGRV: + case SPC_PROP: + for (j=1; j<15; j++) + for (i=1; i<27; i++) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + c = PIXR(pc) + 3*PIXG(pc) + 2*PIXB(pc); + if (c<544) + { + c = 255; + } + else + { + c = 0; + } + if (b==SPC_AIR) + drawtext(vid_buf, x+14-textwidth("AIR")/2, y+4, "AIR", c, c, c, 255); + else if (b==SPC_HEAT) + drawtext(vid_buf, x+14-textwidth("HEAT")/2, y+4, "HEAT", c, c, c, 255); + else if (b==SPC_COOL) + drawtext(vid_buf, x+14-textwidth("COOL")/2, y+4, "COOL", c, c, c, 255); + else if (b==SPC_VACUUM) + drawtext(vid_buf, x+14-textwidth("VAC")/2, y+4, "VAC", c, c, c, 255); + else if (b==SPC_WIND) + drawtext(vid_buf, x+14-textwidth("WIND")/2, y+4, "WIND", c, c, c, 255); + else if (b==SPC_PGRV) + drawtext(vid_buf, x+14-textwidth("PGRV")/2, y+4, "PGRV", c, c, c, 255); + else if (b==SPC_NGRV) + drawtext(vid_buf, x+14-textwidth("NGRV")/2, y+4, "NGRV", c, c, c, 255); + else if (b==SPC_PROP) + drawtext(vid_buf, x+14-textwidth("PROP")/2, y+4, "PROP", c, c, c, 255); + break; + default: + for (j=1; j<15; j++) + for (i=1; i<27; i++) + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + if (b==WL_ERASE+100) + { + for (j=4; j<12; j++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+j+6)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x+j+7)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x-j+21)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x-j+22)] = PIXPACK(0xFF0000); + } + } + } + else + { + //x = 2+32*(b/2); + //y = YRES+2+20*(b%2); + for (j=1; j<15; j++) + { + for (i=1; i<27; i++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+i)] = pc; + } + } + if (b==0) + { + for (j=4; j<12; j++) + { + vid_buf[(XRES+BARSIZE)*(y+j)+(x+j+6)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x+j+7)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x-j+21)] = PIXPACK(0xFF0000); + vid_buf[(XRES+BARSIZE)*(y+j)+(x-j+22)] = PIXPACK(0xFF0000); + } + } + c = PIXB(ptypes[b].pcolors) + 3*PIXG(ptypes[b].pcolors) + 2*PIXR(ptypes[b].pcolors); + if (c<544) + { + c = 255; + } + else + { + c = 0; + } + drawtext(vid_buf, x+14-textwidth((char *)ptypes[b].name)/2, y+4, (char *)ptypes[b].name, c, c, c, 255); + } + return 26; +}*/ + +/*void draw_menu(pixel *vid_buf, int i, int hover) +{ + if (i==SEC&&SEC!=0) + drawrect(vid_buf, (XRES+BARSIZE)-16, (i*16)+YRES+MENUSIZE-16-(SC_TOTAL*16), 14, 14, 0, 255, 255, 255); + else + drawrect(vid_buf, (XRES+BARSIZE)-16, (i*16)+YRES+MENUSIZE-16-(SC_TOTAL*16), 14, 14, 255, 255, 255, 255); + if (hover==i) + { + fillrect(vid_buf, (XRES+BARSIZE)-16, (i*16)+YRES+MENUSIZE-16-(SC_TOTAL*16), 14, 14, 255, 255, 255, 255); + drawtext(vid_buf, (XRES+BARSIZE)-13, (i*16)+YRES+MENUSIZE-14-(SC_TOTAL*16), msections[i].icon, 0, 0, 0, 255); + } + else + { + drawtext(vid_buf, (XRES+BARSIZE)-13, (i*16)+YRES+MENUSIZE-14-(SC_TOTAL*16), msections[i].icon, 255, 255, 255, 255); + } +}*/ + +/*void draw_color_menu(pixel *vid_buf, int i, int hover) +{ + drawrect(vid_buf, (XRES+BARSIZE)-16, (i*16)+YRES+MENUSIZE-16-(DECO_SECTIONS*16), 14, 14, 255, 255, 255, 255); + if (hover==i) + { + fillrect(vid_buf, (XRES+BARSIZE)-16, (i*16)+YRES+MENUSIZE-16-(DECO_SECTIONS*16), 14, 14, 255, 255, 255, 255); + drawtext(vid_buf, (XRES+BARSIZE)-13, (i*16)+YRES+MENUSIZE-14-(DECO_SECTIONS*16), colorsections[i].icon, 0, 0, 0, 255); + } + else + { + drawtext(vid_buf, (XRES+BARSIZE)-13, (i*16)+YRES+MENUSIZE-14-(DECO_SECTIONS*16), colorsections[i].icon, 255, 255, 255, 255); + } +}*/ + +//draws a pixel, identical to blendpixel(), except blendpixel has OpenGL support +TPT_INLINE void Graphics::drawpixel(int x, int y, int r, int g, int b, int a) +{ +#ifdef PIXALPHA + pixel t; + if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE) + return; + if (a!=255) + { + t = vid[y*(XRES+BARSIZE)+x]; + r = (a*r + (255-a)*PIXR(t)) >> 8; + g = (a*g + (255-a)*PIXG(t)) >> 8; + b = (a*b + (255-a)*PIXB(t)) >> 8; + a = a > PIXA(t) ? a : PIXA(t); + } + vid[y*(XRES+BARSIZE)+x] = PIXRGBA(r,g,b,a); +#else + pixel t; + if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE) + return; + if (a!=255) + { + t = vid[y*(XRES+BARSIZE)+x]; + r = (a*r + (255-a)*PIXR(t)) >> 8; + g = (a*g + (255-a)*PIXG(t)) >> 8; + b = (a*b + (255-a)*PIXB(t)) >> 8; + } + vid[y*(XRES+BARSIZE)+x] = PIXRGB(r,g,b); +#endif +} + +inline int Graphics::drawchar(int x, int y, int c, int r, int g, int b, int a) +{ + int i, j, w, bn = 0, ba = 0; + char *rp = font_data + font_ptrs[c]; + w = *(rp++); + for (j=0; j<FONT_H; j++) + for (i=0; i<w; i++) + { + if (!bn) + { + ba = *(rp++); + bn = 8; + } + drawpixel(x+i, y+j, r, g, b, ((ba&3)*a)/3); + ba >>= 2; + bn -= 2; + } + return x + w; +} + +inline int Graphics::addchar(int x, int y, int c, int r, int g, int b, int a) +{ + int i, j, w, bn = 0, ba = 0; + char *rp = font_data + font_ptrs[c]; + w = *(rp++); + for (j=0; j<FONT_H; j++) + for (i=0; i<w; i++) + { + if (!bn) + { + ba = *(rp++); + bn = 8; + } + { + addpixel(x+i, y+j, r, g, b, ((ba&3)*a)/3); + } + ba >>= 2; + bn -= 2; + } + return x + w; +} + +int Graphics::drawtext(int x, int y, std::string &s, int r, int g, int b, int a) +{ + return drawtext(x, y, s.c_str(), r, g, b, a); +} + +int Graphics::drawtext(int x, int y, const char *s, int r, int g, int b, int a) +{ + int sx = x; + for (; *s; s++) + { + if (*s == '\n') + { + x = sx; + y += FONT_H+2; + } + else if (*s == '\b') + { + switch (s[1]) + { + case 'w': + r = g = b = 255; + break; + case 'g': + r = g = b = 192; + break; + case 'o': + r = 255; + g = 216; + b = 32; + break; + case 'r': + r = 255; + g = b = 0; + break; + case 'l': + r = 255; + g = b = 75; + break; + case 'b': + r = g = 0; + b = 255; + break; + case 't': + b = 255; + g = 170; + r = 32; + break; + } + s++; + } + else + x = drawchar(x, y, *(unsigned char *)s, r, g, b, a); + } + return x; +} + +//Draw text with an outline +int Graphics::drawtext_outline(int x, int y, const char *s, int r, int g, int b, int a, int olr, int olg, int olb, int ola) +{ + drawtext(x-1, y-1, s, olr, olg, olb, ola); + drawtext(x+1, y+1, s, olr, olg, olb, ola); + + drawtext(x-1, y+1, s, olr, olg, olb, ola); + drawtext(x+1, y-1, s, olr, olg, olb, ola); + + return drawtext(x, y, s, r, g, b, a); +} +int Graphics::drawtextwrap(int x, int y, int w, const char *s, int r, int g, int b, int a) +{ + int sx = x; + int rh = 12; + int rw = 0; + int cw = x; + int wordlen; + int charspace; + while (*s) + { + wordlen = strcspn(s," .,!?\n"); + charspace = textwidthx((char *)s, w-(x-cw)); + if (charspace<wordlen && wordlen && w-(x-cw)<w/3) + { + x = sx; + rw = 0; + y+=FONT_H+2; + rh+=FONT_H+2; + } + for (; *s && --wordlen>=-1; s++) + { + if (*s == '\n') + { + x = sx; + rw = 0; + y += FONT_H+2; + } + else if (*s == '\b') + { + switch (s[1]) + { + case 'w': + r = g = b = 255; + break; + case 'g': + r = g = b = 192; + break; + case 'o': + r = 255; + g = 216; + b = 32; + break; + case 'r': + r = 255; + g = b = 0; + break; + case 'l': + r = 255; + g = b = 75; + break; + case 'b': + r = g = 0; + b = 255; + break; + case 't': + b = 255; + g = 170; + r = 32; + break; + } + s++; + } + else + { + + if (x-cw>=w) + { + x = sx; + rw = 0; + y+=FONT_H+2; + rh+=FONT_H+2; + } + x = drawchar(x, y, *(unsigned char *)s, r, g, b, a); + } + } + } + + return rh; +} + +//draws a rectange, (x,y) are the top left coords. +void Graphics::drawrect(int x, int y, int w, int h, int r, int g, int b, int a) +{ + int i; + for (i=0; i<=w; i++) + { + drawpixel(x+i, y, r, g, b, a); + drawpixel(x+i, y+h, r, g, b, a); + } + for (i=1; i<h; i++) + { + drawpixel(x, y+i, r, g, b, a); + drawpixel(x+w, y+i, r, g, b, a); + } +} + +//draws a rectangle and fills it in as well. +void Graphics::fillrect(int x, int y, int w, int h, int r, int g, int b, int a) +{ + int i,j; + for (j=1; j<h; j++) + for (i=1; i<w; i++) + drawpixel(x+i, y+j, r, g, b, a); +} + +void Graphics::clearrect(int x, int y, int w, int h) +{ +#ifdef OGLR + fillrect(x, y, w, h, 0, 0, 0, 255); +#else + int i; + for (i=1; i<h; i++) + memset(vid+(x+1+(XRES+BARSIZE)*(y+i)), 0, PIXELSIZE*(w-1)); +#endif +} +//draws a line of dots, where h is the height. (why is this even here) +void Graphics::drawdots(int x, int y, int h, int r, int g, int b, int a) +{ + int i; + for (i=0; i<=h; i+=2) + drawpixel(x, y+i, r, g, b, a); +} + +int Graphics::textwidth(char *s) +{ + int x = 0; + for (; *s; s++) + x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + return x-1; +} + +int Graphics::drawtextmax(int x, int y, int w, char *s, int r, int g, int b, int a) +{ + int i; + w += x-5; + for (; *s; s++) + { + if (x+font_data[font_ptrs[(int)(*(unsigned char *)s)]]>=w && x+textwidth(s)>=w+5) + break; + x = drawchar(x, y, *(unsigned char *)s, r, g, b, a); + } + if (*s) + for (i=0; i<3; i++) + x = drawchar(x, y, '.', r, g, b, a); + return x; +} + +int Graphics::textnwidth(char *s, int n) +{ + int x = 0; + for (; *s; s++) + { + if (!n) + break; + x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + n--; + } + return x-1; +} +void Graphics::textnpos(char *s, int n, int w, int *cx, int *cy) +{ + int x = 0; + int y = 0; + int wordlen, charspace; + while (*s&&n) + { + wordlen = strcspn(s," .,!?\n"); + charspace = textwidthx(s, w-x); + if (charspace<wordlen && wordlen && w-x<w/3) + { + x = 0; + y += FONT_H+2; + } + for (; *s && --wordlen>=-1; s++) + { + if (!n) { + break; + } + x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + if (x>=w) + { + x = 0; + y += FONT_H+2; + } + n--; + } + } + *cx = x-1; + *cy = y; +} + +int Graphics::textwidthx(char *s, int w) +{ + int x=0,n=0,cw; + for (; *s; s++) + { + cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + if (x+(cw/2) >= w) + break; + x += cw; + n++; + } + return n; +} +int Graphics::textposxy(char *s, int width, int w, int h) +{ + int x=0,y=0,n=0,cw, wordlen, charspace; + while (*s) + { + wordlen = strcspn(s," .,!?\n"); + charspace = textwidthx(s, width-x); + if (charspace<wordlen && wordlen && width-x<width/3) + { + x = 0; + y += FONT_H+2; + } + for (; *s && --wordlen>=-1; s++) + { + cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + if ((x+(cw/2) >= w && y+6 >= h)||(y+6 >= h+FONT_H+2)) + return n++; + x += cw; + if (x>=width) { + x = 0; + y += FONT_H+2; + } + n++; + } + } + return n; +} +int Graphics::textwrapheight(char *s, int width) +{ + int x=0, height=FONT_H+2, cw; + int wordlen; + int charspace; + while (*s) + { + wordlen = strcspn(s," .,!?\n"); + charspace = textwidthx(s, width-x); + if (charspace<wordlen && wordlen && width-x<width/3) + { + x = 0; + height += FONT_H+2; + } + for (; *s && --wordlen>=-1; s++) + { + if (*s == '\n') + { + x = 0; + height += FONT_H+2; + } + else if (*s == '\b') + { + s++; + } + else + { + cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; + if (x+cw>=width) + { + x = 0; + height += FONT_H+2; + } + x += cw; + } + } + } + return height; +} + +//the most used function for drawing a pixel, because it has OpenGL support, which is not fully implemented. +TPT_INLINE void Graphics::blendpixel(int x, int y, int r, int g, int b, int a) +{ +#ifdef PIXALPHA + pixel t; + if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE) + return; + if (a!=255) + { + t = vid[y*(XRES+BARSIZE)+x]; + r = (a*r + (255-a)*PIXR(t)) >> 8; + g = (a*g + (255-a)*PIXG(t)) >> 8; + b = (a*b + (255-a)*PIXB(t)) >> 8; + a = a > PIXA(t) ? a : PIXA(t); + } + vid[y*(XRES+BARSIZE)+x] = PIXRGBA(r,g,b,a); +#else + pixel t; + if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE) + return; + if (a!=255) + { + t = vid[y*(XRES+BARSIZE)+x]; + r = (a*r + (255-a)*PIXR(t)) >> 8; + g = (a*g + (255-a)*PIXG(t)) >> 8; + b = (a*b + (255-a)*PIXB(t)) >> 8; + } + vid[y*(XRES+BARSIZE)+x] = PIXRGB(r,g,b); +#endif +} + +void Graphics::draw_icon(int x, int y, char ch, int flag) +{ + char t[2]; + t[0] = ch; + t[1] = 0; + if (flag) + { + fillrect(x-1, y-1, 17, 17, 255, 255, 255, 255); + drawtext(x+3, y+2, t, 0, 0, 0, 255); + } + else + { + drawrect(x, y, 15, 15, 255, 255, 255, 255); + drawtext(x+3, y+2, t, 255, 255, 255, 255); + } +} + +void Graphics::draw_line(int x1, int y1, int x2, int y2, int r, int g, int b, int a) //Draws a line +{ + int dx, dy, i, sx, sy, check, e, x, y; + + dx = abs(x1-x2); + dy = abs(y1-y2); + sx = isign(x2-x1); + sy = isign(y2-y1); + x = x1; + y = y1; + check = 0; + + if (dy>dx) + { + dx = dx+dy; + dy = dx-dy; + dx = dx-dy; + check = 1; + } + + e = (dy<<2)-dx; + for (i=0; i<=dx; i++) + { + if (x>=0 && y>=0 && x<a && y<YRES+MENUSIZE) + vid[x+y*a] =PIXRGB(r, g, b); + if (e>=0) + { + if (check==1) + x = x+sx; + else + y = y+sy; + e = e-(dx<<2); + } + if (check==1) + y = y+sy; + else + x = x+sx; + e = e+(dy<<2); + } +} + +//adds color to a pixel, does not overwrite. +void Graphics::addpixel(int x, int y, int r, int g, int b, int a) +{ + pixel t; + if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE) + return; + t = vid[y*(XRES+BARSIZE)+x]; + r = (a*r + 255*PIXR(t)) >> 8; + g = (a*g + 255*PIXG(t)) >> 8; + b = (a*b + 255*PIXB(t)) >> 8; + if (r>255) + r = 255; + if (g>255) + g = 255; + if (b>255) + b = 255; + vid[y*(XRES+BARSIZE)+x] = PIXRGB(r,g,b); +} + +//draws one of two colors, so that it is always clearly visible +void Graphics::xor_pixel(int x, int y) +{ + int c; + if (x<0 || y<0 || x>=XRES || y>=YRES) + return; + c = vid[y*(XRES+BARSIZE)+x]; + c = PIXB(c) + 3*PIXG(c) + 2*PIXR(c); + if (c<512) + vid[y*(XRES+BARSIZE)+x] = PIXPACK(0xC0C0C0); + else + vid[y*(XRES+BARSIZE)+x] = PIXPACK(0x404040); +} + +//same as xor_pixel, but draws a line of it +void Graphics::xor_line(int x1, int y1, int x2, int y2) +{ + int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy; + float e, de; + if (cp) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + e = 0.0f; + if (dx) + de = dy/(float)dx; + else + de = 0.0f; + y = y1; + sy = (y1<y2) ? 1 : -1; + for (x=x1; x<=x2; x++) + { + if (cp) + xor_pixel(y, x); + else + xor_pixel(x, y); + e += de; + if (e >= 0.5f) + { + y += sy; + e -= 1.0f; + } + } +} + +//same as blend_pixel, but draws a line of it +void Graphics::blend_line(int x1, int y1, int x2, int y2, int r, int g, int b, int a) +{ + int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy; + float e, de; + if (cp) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + e = 0.0f; + if (dx) + de = dy/(float)dx; + else + de = 0.0f; + y = y1; + sy = (y1<y2) ? 1 : -1; + for (x=x1; x<=x2; x++) + { + if (cp) + blendpixel(y, x, r, g, b, a); + else + blendpixel(x, y, r, g, b, a); + e += de; + if (e >= 0.5f) + { + y += sy; + e -= 1.0f; + } + } +} + +//same as xor_pixel, but draws a rectangle +void Graphics::xor_rect(int x, int y, int w, int h) +{ + int i; + for (i=0; i<w; i+=2) + { + xor_pixel(x+i, y); + xor_pixel(x+i, y+h-1); + } + for (i=2; i<h; i+=2) + { + xor_pixel(x, y+i); + xor_pixel(x+w-1, y+i); + } +} + + +//New function for drawing particles +#ifdef OGLR +GLuint fireV[(YRES*XRES)*2]; +GLfloat fireC[(YRES*XRES)*4]; +GLuint smokeV[(YRES*XRES)*2]; +GLfloat smokeC[(YRES*XRES)*4]; +GLuint blobV[(YRES*XRES)*2]; +GLfloat blobC[(YRES*XRES)*4]; +GLuint blurV[(YRES*XRES)*2]; +GLfloat blurC[(YRES*XRES)*4]; +GLuint glowV[(YRES*XRES)*2]; +GLfloat glowC[(YRES*XRES)*4]; +GLuint flatV[(YRES*XRES)*2]; +GLfloat flatC[(YRES*XRES)*4]; +GLuint addV[(YRES*XRES)*2]; +GLfloat addC[(YRES*XRES)*4]; +GLfloat lineV[(((YRES*XRES)*2)*6)]; +GLfloat lineC[(((YRES*XRES)*2)*6)]; +#endif + +#ifdef OGLR +void draw_parts_fbo() +{ + glEnable( GL_TEXTURE_2D ); + if(display_mode & DISPLAY_WARP) + { + float xres = XRES, yres = YRES; + glUseProgram(lensProg); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, partsFboTex); + glUniform1i(glGetUniformLocation(lensProg, "pTex"), 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, partsTFX); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_RED, GL_FLOAT, gravx); + glUniform1i(glGetUniformLocation(lensProg, "tfX"), 1); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, partsTFY); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_GREEN, GL_FLOAT, gravy); + glUniform1i(glGetUniformLocation(lensProg, "tfY"), 2); + glActiveTexture(GL_TEXTURE0); + glUniform1fv(glGetUniformLocation(lensProg, "xres"), 1, &xres); + glUniform1fv(glGetUniformLocation(lensProg, "yres"), 1, &yres); + } + else + { + glBindTexture(GL_TEXTURE_2D, partsFboTex); + glBlendFunc(GL_ONE, GL_ONE); + } + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2d(1, 0); + glVertex3f(XRES*sdl_scale, (YRES+MENUSIZE)*sdl_scale, 1.0); + glTexCoord2d(0, 0); + glVertex3f(0, (YRES+MENUSIZE)*sdl_scale, 1.0); + glTexCoord2d(0, 1); + glVertex3f(0, MENUSIZE*sdl_scale, 1.0); + glTexCoord2d(1, 1); + glVertex3f(XRES*sdl_scale, MENUSIZE*sdl_scale, 1.0); + glEnd(); + + if(display_mode & DISPLAY_WARP) + { + glUseProgram(0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + glDisable( GL_TEXTURE_2D ); +} +#endif + + +//draws the photon colors in the HUD +void Graphics::draw_wavelengths(int x, int y, int h, int wl) +{ + int i,cr,cg,cb,j; + int tmp; + fillrect(x-1,y-1,30+1,h+1,64,64,64,255); // coords -1 size +1 to work around bug in fillrect - TODO: fix fillrect + for (i=0; i<30; i++) + { + if ((wl>>i)&1) + { + // Need a spread of wavelengths to get a smooth spectrum, 5 bits seems to work reasonably well + if (i>2) tmp = 0x1F << (i-2); + else tmp = 0x1F >> (2-i); + cg = 0; + cb = 0; + cr = 0; + for (j=0; j<12; j++) { + cr += (tmp >> (j+18)) & 1; + cb += (tmp >> j) & 1; + } + for (j=0; j<13; j++) + cg += (tmp >> (j+9)) & 1; + tmp = 624/(cr+cg+cb+1); + cr *= tmp; + cg *= tmp; + cb *= tmp; + for (j=0; j<h; j++) blendpixel(x+29-i,y+j,cr>255?255:cr,cg>255?255:cg,cb>255?255:cb,255); + } + } +} + +pixel *Graphics::render_packed_rgb(void *image, int width, int height, int cmp_size) +{ + unsigned char *tmp; + pixel *res; + int i; + + tmp = (unsigned char *)malloc(width*height*3); + if (!tmp) + return NULL; + res = (pixel *)malloc(width*height*PIXELSIZE); + if (!res) + { + free(tmp); + return NULL; + } + + i = width*height*3; + if (BZ2_bzBuffToBuffDecompress((char *)tmp, (unsigned *)&i, (char *)image, cmp_size, 0, 0)) + { + free(res); + free(tmp); + return NULL; + } + + for (i=0; i<width*height; i++) + res[i] = PIXRGB(tmp[3*i], tmp[3*i+1], tmp[3*i+2]); + + free(tmp); + return res; +} + +void Graphics::draw_rgba_image(unsigned char *data, int x, int y, float alpha) +{ + unsigned char w, h; + int i, j; + unsigned char r, g, b, a; + if (!data) return; + w = *(data++)&0xFF; + h = *(data++)&0xFF; + for (j=0; j<h; j++) + { + for (i=0; i<w; i++) + { + r = *(data++)&0xFF; + g = *(data++)&0xFF; + b = *(data++)&0xFF; + a = *(data++)&0xFF; + drawpixel(x+i, y+j, r, g, b, a*alpha); + } + } +} + +void Graphics::draw_image(pixel *img, int x, int y, int w, int h, int a) +{ + int i, j, r, g, b; + if (!img) return; + for (j=0; j<h; j++) + for (i=0; i<w; i++) + { + r = PIXR(*img); + g = PIXG(*img); + b = PIXB(*img); + drawpixel(x+i, y+j, r, g, b, a); + img++; + } +} + +void Graphics::dim_copy(pixel *dst, pixel *src) //old persistent, unused +{ + int i,r,g,b; + for (i=0; i<XRES*YRES; i++) + { + r = PIXR(src[i]); + g = PIXG(src[i]); + b = PIXB(src[i]); + if (r>0) + r--; + if (g>0) + g--; + if (b>0) + b--; + dst[i] = PIXRGB(r,g,b); + } +} + +void Graphics::dim_copy_pers(pixel *dst, pixel *src) //for persistent view, reduces rgb slowly +{ + int i,r,g,b; + for (i=0; i<(XRES+BARSIZE)*YRES; i++) + { + r = PIXR(src[i]); + g = PIXG(src[i]); + b = PIXB(src[i]); + if (r>0) + r--; + if (g>0) + g--; + if (b>0) + b--; + dst[i] = PIXRGB(r,g,b); + } +} + +/*void render_zoom(pixel *img) //draws the zoom box +{ +#ifdef OGLR + int origBlendSrc, origBlendDst; + float zcx1, zcx0, zcy1, zcy0, yfactor, xfactor, i; //X-Factor is shit, btw + xfactor = 1.0f/(float)XRES; + yfactor = 1.0f/(float)YRES; + + zcx0 = (zoom_x)*xfactor; + zcx1 = (zoom_x+ZSIZE)*xfactor; + zcy0 = (zoom_y)*yfactor; + zcy1 = ((zoom_y+ZSIZE))*yfactor; + + glGetIntegerv(GL_BLEND_SRC, &origBlendSrc); + glGetIntegerv(GL_BLEND_DST, &origBlendDst); + glBlendFunc(GL_ONE, GL_ZERO); + + glEnable( GL_TEXTURE_2D ); + //glReadBuffer(GL_AUX0); + glBindTexture(GL_TEXTURE_2D, partsFboTex); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2d(zcx1, zcy1); + glVertex3f((zoom_wx+ZSIZE*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR))*sdl_scale, 1.0); + glTexCoord2d(zcx0, zcy1); + glVertex3f(zoom_wx*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR))*sdl_scale, 1.0); + glTexCoord2d(zcx0, zcy0); + glVertex3f(zoom_wx*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale, 1.0); + glTexCoord2d(zcx1, zcy0); + glVertex3f((zoom_wx+ZSIZE*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale, 1.0); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable( GL_TEXTURE_2D ); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glLineWidth(sdl_scale); + glEnable(GL_LINE_SMOOTH); + glBegin(GL_LINES); + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + for(i = 0; i < ZSIZE; i++) + { + glVertex2f((zoom_wx+ZSIZE*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR)+i*ZFACTOR)*sdl_scale); + glVertex2f(zoom_wx*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR)+i*ZFACTOR)*sdl_scale); + glVertex2f((zoom_wx+i*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR))*sdl_scale); + glVertex2f((zoom_wx+i*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale); + } + glEnd(); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_LINE_STRIP); + glVertex3i((zoom_wx-1)*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale, 0); + glVertex3i((zoom_wx-1)*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR))*sdl_scale, 0); + glVertex3i((zoom_wx+ZSIZE*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-(zoom_wy+ZSIZE*ZFACTOR))*sdl_scale, 0); + glVertex3i((zoom_wx+ZSIZE*ZFACTOR)*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale, 0); + glVertex3i((zoom_wx-1)*sdl_scale, (YRES+MENUSIZE-zoom_wy)*sdl_scale, 0); + glEnd(); + glDisable(GL_LINE_SMOOTH); + + glDisable(GL_LINE_SMOOTH); + + if(zoom_en) + { + glEnable(GL_COLOR_LOGIC_OP); + //glEnable(GL_LINE_SMOOTH); + glLogicOp(GL_XOR); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_LINE_STRIP); + glVertex3i((zoom_x-1)*sdl_scale, (YRES+MENUSIZE-(zoom_y-1))*sdl_scale, 0); + glVertex3i((zoom_x-1)*sdl_scale, (YRES+MENUSIZE-(zoom_y+ZSIZE))*sdl_scale, 0); + glVertex3i((zoom_x+ZSIZE)*sdl_scale, (YRES+MENUSIZE-(zoom_y+ZSIZE))*sdl_scale, 0); + glVertex3i((zoom_x+ZSIZE)*sdl_scale, (YRES+MENUSIZE-(zoom_y-1))*sdl_scale, 0); + glVertex3i((zoom_x-1)*sdl_scale, (YRES+MENUSIZE-(zoom_y-1))*sdl_scale, 0); + glEnd(); + glDisable(GL_COLOR_LOGIC_OP); + } + glLineWidth(1); + glBlendFunc(origBlendSrc, origBlendDst); +#else + int x, y, i, j; + pixel pix; + drawrect(img, zoom_wx-2, zoom_wy-2, ZSIZE*ZFACTOR+2, ZSIZE*ZFACTOR+2, 192, 192, 192, 255); + drawrect(img, zoom_wx-1, zoom_wy-1, ZSIZE*ZFACTOR, ZSIZE*ZFACTOR, 0, 0, 0, 255); + clearrect(img, zoom_wx, zoom_wy, ZSIZE*ZFACTOR, ZSIZE*ZFACTOR); + for (j=0; j<ZSIZE; j++) + for (i=0; i<ZSIZE; i++) + { + pix = img[(j+zoom_y)*(XRES+BARSIZE)+(i+zoom_x)]; + for (y=0; y<ZFACTOR-1; y++) + for (x=0; x<ZFACTOR-1; x++) + img[(j*ZFACTOR+y+zoom_wy)*(XRES+BARSIZE)+(i*ZFACTOR+x+zoom_wx)] = pix; + } + if (zoom_en) + { + for (j=-1; j<=ZSIZE; j++) + { + xor_pixel(zoom_x+j, zoom_y-1, img); + xor_pixel(zoom_x+j, zoom_y+ZSIZE, img); + } + for (j=0; j<ZSIZE; j++) + { + xor_pixel(zoom_x-1, zoom_y+j, img); + xor_pixel(zoom_x+ZSIZE, zoom_y+j, img); + } + } +#endif +}*/ + +/*int render_thumb(void *thumb, int size, int bzip2, pixel *vid_buf, int px, int py, int scl) +{ + unsigned char *d, *c = (unsigned char *)thumb; + int i,j,x,y,a,t,r,g,b,sx,sy; + + if (bzip2) + { + if (size<16) + return 1; + if (c[3]!=0x74 || c[2]!=0x49 || c[1]!=0x68 || c[0]!=0x53) + return 1; + if (c[4]>PT_NUM) + return 2; + if (c[5]!=CELL || c[6]!=XRES/CELL || c[7]!=YRES/CELL) + return 3; + i = XRES*YRES; + d = (unsigned char *)malloc(i); + if (!d) + return 1; + + if (BZ2_bzBuffToBuffDecompress((char *)d, (unsigned *)&i, (char *)(c+8), size-8, 0, 0)) + return 1; + size = i; + } + else + d = c; + + if (size < XRES*YRES) + { + if (bzip2) + free(d); + return 1; + } + + sy = 0; + for (y=0; y+scl<=YRES; y+=scl) + { + sx = 0; + for (x=0; x+scl<=XRES; x+=scl) + { + a = 0; + r = g = b = 0; + for (j=0; j<scl; j++) + for (i=0; i<scl; i++) + { + t = d[(y+j)*XRES+(x+i)]; + if (t==0xFF) + { + r += 256; + g += 256; + b += 256; + a += 2; + } + else if (t) + { + if (t>=PT_NUM) + goto corrupt; + r += PIXR(ptypes[t].pcolors); + g += PIXG(ptypes[t].pcolors); + b += PIXB(ptypes[t].pcolors); + a ++; + } + } + if (a) + { + a = 256/a; + r = (r*a)>>8; + g = (g*a)>>8; + b = (b*a)>>8; + } + + drawpixel(vid_buf, px+sx, py+sy, r, g, b, 255); + sx++; + } + sy++; + } + + if (bzip2) + free(d); + return 0; + +corrupt: + if (bzip2) + free(d); + return 1; +}*/ + +//draws the cursor +/*void Graphics::render_cursor(pixel *vid, int x, int y, int t, int rx, int ry) +{ +#ifdef OGLR + int i; + if (t<PT_NUM||(t&0xFF)==PT_LIFE||t==SPC_AIR||t==SPC_HEAT||t==SPC_COOL||t==SPC_VACUUM||t==SPC_WIND||t==SPC_PGRV||t==SPC_NGRV) + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + glBegin(GL_LINE_LOOP); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + y *= sdl_scale; + x *= sdl_scale; + ry *= sdl_scale; + rx *= sdl_scale; + if (CURRENT_BRUSH==SQUARE_BRUSH) + { + glVertex2f(x-rx+1, (y)-ry+1); + glVertex2f(x+rx+1, (y)-ry+1); + glVertex2f(x+rx+1, (y)+ry+1); + glVertex2f(x-rx+1, (y)+ry+1); + glVertex2f(x-rx+1, (y)-ry+1); + } + else if (CURRENT_BRUSH==CIRCLE_BRUSH) + { + for (i = 0; i < 360; i++) + { + float degInRad = i*(M_PI/180.0f); + glVertex2f((cos(degInRad)*rx)+x, (sin(degInRad)*ry)+y); + } + } + else if (CURRENT_BRUSH==TRI_BRUSH) + { + glVertex2f(x+1, (y)+ry+1); + glVertex2f(x+rx+1, (y)-ry+1); + glVertex2f(x-rx+1, (y)-ry+1); + glVertex2f(x+1, (y)+ry+1); + } + glEnd(); + glDisable(GL_COLOR_LOGIC_OP); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } +#else + int i,j,c; + if (t<PT_NUM||(t&0xFF)==PT_LIFE||t==SPC_AIR||t==SPC_HEAT||t==SPC_COOL||t==SPC_VACUUM||t==SPC_WIND||t==SPC_PGRV||t==SPC_NGRV) + { + if (rx<=0) + xor_pixel(x, y, vid); + else if (ry<=0) + xor_pixel(x, y, vid); + if (rx+ry<=0) + xor_pixel(x, y, vid); + else if (CURRENT_BRUSH==SQUARE_BRUSH) + { + for (j=0; j<=ry; j++) + for (i=0; i<=rx; i++) + if (i*j<=ry*rx && ((i+1)>rx || (j+1)>ry)) + { + xor_pixel(x+i, y+j, vid); + xor_pixel(x-i, y-j, vid); + if (i&&j)xor_pixel(x+i, y-j, vid); + if (i&&j)xor_pixel(x-i, y+j, vid); + } + } + else if (CURRENT_BRUSH==CIRCLE_BRUSH) + { + for (j=0; j<=ry; j++) + for (i=0; i<=rx; i++) + if (pow(i,2)*pow(ry,2)+pow(j,2)*pow(rx,2)<=pow(rx,2)*pow(ry,2) && + (pow(i+1,2)*pow(ry,2)+pow(j,2)*pow(rx,2)>pow(rx,2)*pow(ry,2) || + pow(i,2)*pow(ry,2)+pow(j+1,2)*pow(rx,2)>pow(rx,2)*pow(ry,2))) + { + xor_pixel(x+i, y+j, vid); + if (j) xor_pixel(x+i, y-j, vid); + if (i) xor_pixel(x-i, y+j, vid); + if (i&&j) xor_pixel(x-i, y-j, vid); + } + } + else if (CURRENT_BRUSH==TRI_BRUSH) + { + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=0; i++) + if ((j <= ry ) && ( j >= (((-2.0*ry)/(rx))*i)-ry ) && (j+1>ry || ( j-1 < (((-2.0*ry)/(rx))*i)-ry )) ) + { + xor_pixel(x+i, y+j, vid); + if (i) xor_pixel(x-i, y+j, vid); + } + } + } + else //wall cursor + { + int tc; + c = (rx/CELL) * CELL; + x = (x/CELL) * CELL; + y = (y/CELL) * CELL; + + tc = !((c%(CELL*2))==0); + + x -= c/2; + y -= c/2; + + x += tc*(CELL/2); + y += tc*(CELL/2); + + for (i=0; i<CELL+c; i++) + { + xor_pixel(x+i, y, vid); + xor_pixel(x+i, y+CELL+c-1, vid); + } + for (i=1; i<CELL+c-1; i++) + { + xor_pixel(x, y+i, vid); + xor_pixel(x+CELL+c-1, y+i, vid); + } + } +#endif +}*/ + +/*int sdl_opened = 0; +int sdl_open(void) +{ + int status; + if (SDL_Init(SDL_INIT_VIDEO)<0) + { + fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError()); + return 0; + } + atexit(SDL_Quit); +#if defined(OGLR) + sdl_scrn=SDL_SetVideoMode(XRES*sdl_scale + BARSIZE*sdl_scale,YRES*sdl_scale + MENUSIZE*sdl_scale,32,SDL_OPENGL); + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); + + if(sdl_opened) + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glOrtho(0, (XRES+BARSIZE)*sdl_scale, 0, (YRES+MENUSIZE)*sdl_scale, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + else + { +#ifdef WIN32 + status = glewInit(); + if(status != GLEW_OK) + { + fprintf(stderr, "Initializing Glew: %d\n", status); + return 0; + } +#endif + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glOrtho(0, (XRES+BARSIZE)*sdl_scale, 0, (YRES+MENUSIZE)*sdl_scale, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glRasterPos2i(0, (YRES+MENUSIZE)); + glPixelZoom(1, -1); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //FBO Texture + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &partsFboTex); + glBindTexture(GL_TEXTURE_2D, partsFboTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, XRES, YRES, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + //FBO + glGenFramebuffers(1, &partsFbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); + glEnable(GL_BLEND); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, partsFboTex, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding + glDisable(GL_TEXTURE_2D); + + //Texture for main UI + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &vidBuf); + glBindTexture(GL_TEXTURE_2D, vidBuf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, XRES+BARSIZE, YRES+MENUSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Texture for air to be drawn + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &airBuf); + glBindTexture(GL_TEXTURE_2D, airBuf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Zoom texture + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &zoomTex); + glBindTexture(GL_TEXTURE_2D, zoomTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Texture for velocity maps for gravity + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &partsTFX); + glBindTexture(GL_TEXTURE_2D, partsTFX); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES, YRES, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glGenTextures(1, &partsTFY); + glBindTexture(GL_TEXTURE_2D, partsTFY); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES, YRES, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Texture for velocity maps for air + //TODO: Combine all air maps into 3D array or structs + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &airVX); + glBindTexture(GL_TEXTURE_2D, airVX); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); + glGenTextures(1, &airVY); + glBindTexture(GL_TEXTURE_2D, airVY); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); + glGenTextures(1, &airPV); + glBindTexture(GL_TEXTURE_2D, airPV); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Fire alpha texture + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &fireAlpha); + glBindTexture(GL_TEXTURE_2D, fireAlpha); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, CELL*3, CELL*3, 0, GL_ALPHA, GL_FLOAT, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + //Glow alpha texture + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &glowAlpha); + glBindTexture(GL_TEXTURE_2D, glowAlpha); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 11, 11, 0, GL_ALPHA, GL_FLOAT, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + + //Blur Alpha texture + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &blurAlpha); + glBindTexture(GL_TEXTURE_2D, blurAlpha); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 7, 7, 0, GL_ALPHA, GL_FLOAT, NULL); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + loadShaders(); + } +#else +#ifdef PIX16 + if (kiosk_enable) + sdl_scrn=SDL_SetVideoMode(XRES*sdl_scale + BARSIZE*sdl_scale,YRES*sdl_scale + MENUSIZE*sdl_scale,16,SDL_FULLSCREEN|SDL_SWSURFACE); + else + sdl_scrn=SDL_SetVideoMode(XRES*sdl_scale + BARSIZE*sdl_scale,YRES*sdl_scale + MENUSIZE*sdl_scale,16,SDL_SWSURFACE); +#else + if (kiosk_enable) + sdl_scrn=SDL_SetVideoMode(XRES*sdl_scale + BARSIZE*sdl_scale,YRES*sdl_scale + MENUSIZE*sdl_scale,32,SDL_FULLSCREEN|SDL_SWSURFACE); + else + sdl_scrn=SDL_SetVideoMode(XRES*sdl_scale + BARSIZE*sdl_scale,YRES*sdl_scale + MENUSIZE*sdl_scale,32,SDL_SWSURFACE); +#endif +#endif + if (!sdl_scrn) + { + fprintf(stderr, "Creating window: %s\n", SDL_GetError()); + return 0; + } + SDL_WM_SetCaption("The Powder Toy", "Powder Toy"); + sdl_seticon(); + SDL_EnableUNICODE(1); + //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); +#if (defined(LIN32) || defined(LIN64)) && defined(SDL_VIDEO_DRIVER_X11) + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + SDL_VERSION(&sdl_wminfo.version); + SDL_GetWMInfo(&sdl_wminfo); + sdl_wminfo.info.x11.lock_func(); + XA_CLIPBOARD = XInternAtom(sdl_wminfo.info.x11.display, "CLIPBOARD", 1); + XA_TARGETS = XInternAtom(sdl_wminfo.info.x11.display, "TARGETS", 1); + sdl_wminfo.info.x11.unlock_func(); +#endif + sdl_opened = 1; + return 1; +} +#ifdef OGLR +void checkShader(GLuint shader, char * shname) +{ + GLuint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + char errorBuf[ GL_INFO_LOG_LENGTH]; + int errLen; + glGetShaderInfoLog(shader, GL_INFO_LOG_LENGTH, &errLen, errorBuf); + fprintf(stderr, "Failed to compile %s shader:\n%s\n", shname, errorBuf); + exit(1); + } +} +void checkProgram(GLuint program, char * progname) +{ + GLuint status; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + char errorBuf[ GL_INFO_LOG_LENGTH]; + int errLen; + glGetShaderInfoLog(program, GL_INFO_LOG_LENGTH, &errLen, errorBuf); + fprintf(stderr, "Failed to link %s program:\n%s\n", progname, errorBuf); + exit(1); + } +} +void loadShaders() +{ + GLuint vertexShader, fragmentShader; + + //Particle texture + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource( vertexShader, 1, &fireVertex, NULL); + glShaderSource( fragmentShader, 1, &fireFragment, NULL); + + glCompileShader( vertexShader ); + checkShader(vertexShader, "FV"); + glCompileShader( fragmentShader ); + checkShader(fragmentShader, "FF"); + + fireProg = glCreateProgram(); + glAttachShader( fireProg, vertexShader ); + glAttachShader( fireProg, fragmentShader ); + glLinkProgram( fireProg ); + checkProgram(fireProg, "F"); + + //Lensing + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource( vertexShader, 1, &lensVertex, NULL); + glShaderSource( fragmentShader, 1, &lensFragment, NULL); + + glCompileShader( vertexShader ); + checkShader(vertexShader, "LV"); + glCompileShader( fragmentShader ); + checkShader(fragmentShader, "LF"); + + lensProg = glCreateProgram(); + glAttachShader( lensProg, vertexShader ); + glAttachShader( lensProg, fragmentShader ); + glLinkProgram( lensProg ); + checkProgram(lensProg, "L"); + + //Air Velocity + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource( vertexShader, 1, &airVVertex, NULL); + glShaderSource( fragmentShader, 1, &airVFragment, NULL); + + glCompileShader( vertexShader ); + checkShader(vertexShader, "AVX"); + glCompileShader( fragmentShader ); + checkShader(fragmentShader, "AVF"); + + airProg_Velocity = glCreateProgram(); + glAttachShader( airProg_Velocity, vertexShader ); + glAttachShader( airProg_Velocity, fragmentShader ); + glLinkProgram( airProg_Velocity ); + checkProgram(airProg_Velocity, "AV"); + + //Air Pressure + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource( vertexShader, 1, &airPVertex, NULL); + glShaderSource( fragmentShader, 1, &airPFragment, NULL); + + glCompileShader( vertexShader ); + checkShader(vertexShader, "APV"); + glCompileShader( fragmentShader ); + checkShader(fragmentShader, "APF"); + + airProg_Pressure = glCreateProgram(); + glAttachShader( airProg_Pressure, vertexShader ); + glAttachShader( airProg_Pressure, fragmentShader ); + glLinkProgram( airProg_Pressure ); + checkProgram(airProg_Pressure, "AP"); + + //Air cracker + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource( vertexShader, 1, &airCVertex, NULL); + glShaderSource( fragmentShader, 1, &airCFragment, NULL); + + glCompileShader( vertexShader ); + checkShader(vertexShader, "ACV"); + glCompileShader( fragmentShader ); + checkShader(fragmentShader, "ACF"); + + airProg_Cracker = glCreateProgram(); + glAttachShader( airProg_Cracker, vertexShader ); + glAttachShader( airProg_Cracker, fragmentShader ); + glLinkProgram( airProg_Cracker ); + checkProgram(airProg_Cracker, "AC"); +} +#endif +int draw_debug_info(pixel* vid, Simulation * sim, int lm, int lx, int ly, int cx, int cy, int line_x, int line_y) +{ + Particle * parts = sim->parts; + char infobuf[256]; + if(debug_flags & DEBUG_PERFORMANCE_FRAME || debug_flags & DEBUG_PERFORMANCE_CALC) + { + int t1, t2, x = 0, i = debug_perf_istart; + float partiavg = 0, frameavg = 0; + while(i != debug_perf_iend) + { + partiavg += abs(debug_perf_partitime[i]/100000); + frameavg += abs(debug_perf_frametime[i]/100000); + if(debug_flags & DEBUG_PERFORMANCE_CALC) + t1 = abs(debug_perf_partitime[i]/100000); + else + t1 = 0; + + if(debug_flags & DEBUG_PERFORMANCE_FRAME) + t2 = abs(debug_perf_frametime[i]/100000); + else + t2 = 0; + + if(t1 > YRES) + t1 = YRES; + if(t1+t2 > YRES) + t2 = YRES-t1; + + if(t1>0) + draw_line(vid, x, YRES, x, YRES-t1, 0, 255, 120, XRES+BARSIZE); + if(t2>0) + draw_line(vid, x, YRES-t1, x, YRES-(t1+t2), 255, 120, 0, XRES+BARSIZE); + + i++; + x++; + i %= DEBUG_PERF_FRAMECOUNT; + } + + if(debug_flags & DEBUG_PERFORMANCE_CALC) + t1 = abs(partiavg / x); + else + t1 = 0; + + if(debug_flags & DEBUG_PERFORMANCE_FRAME) + t2 = abs(frameavg / x); + else + t2 = 0; + + if(t1 > YRES) + t1 = YRES; + if(t1+t2 > YRES) + t2 = YRES-t1; + + if(t1>0) + fillrect(vid, x, YRES-t1-1, 5, t1+2, 0, 255, 0, 255); + if(t2>0) + fillrect(vid, x, (YRES-t1)-t2-1, 5, t2+1, 255, 0, 0, 255); + } + if(debug_flags & DEBUG_DRAWTOOL) + { + if(lm == 1) //Line tool + { + blend_line(vid, 0, line_y, XRES, line_y, 255, 255, 255, 120); + blend_line(vid, line_x, 0, line_x, YRES, 255, 255, 255, 120); + + blend_line(vid, 0, ly, XRES, ly, 255, 255, 255, 120); + blend_line(vid, lx, 0, lx, YRES, 255, 255, 255, 120); + + sprintf(infobuf, "%d x %d", lx, ly); + drawtext_outline(vid, lx+(lx>line_x?3:-textwidth(infobuf)-3), ly+(ly<line_y?-10:3), infobuf, 255, 255, 255, 200, 0, 0, 0, 120); + + sprintf(infobuf, "%d x %d", line_x, line_y); + drawtext_outline(vid, line_x+(lx<line_x?3:-textwidth(infobuf)-2), line_y+(ly>line_y?-10:3), infobuf, 255, 255, 255, 200, 0, 0, 0, 120); + + sprintf(infobuf, "%d", abs(line_x-lx)); + drawtext_outline(vid, (line_x+lx)/2-textwidth(infobuf)/2, line_y+(ly>line_y?-10:3), infobuf, 255, 255, 255, 200, 0, 0, 0, 120); + + sprintf(infobuf, "%d", abs(line_y-ly)); + drawtext_outline(vid, line_x+(lx<line_x?3:-textwidth(infobuf)-2), (line_y+ly)/2-3, infobuf, 255, 255, 255, 200, 0, 0, 0, 120); + } + } + if(debug_flags & DEBUG_PARTS) + { + int i = 0, x = 0, y = 0, lpx = 0, lpy = 0; + sprintf(infobuf, "%d/%d (%.2f%%)", sim->parts_lastActiveIndex, NPART, (((float)sim->parts_lastActiveIndex)/((float)NPART))*100.0f); + for(i = 0; i < NPART; i++){ + if(parts[i].type){ + drawpixel(vid, x, y, 255, 255, 255, 180); + } else { + drawpixel(vid, x, y, 0, 0, 0, 180); + } + if(i == sim->parts_lastActiveIndex) + { + lpx = x; + lpy = y; + } + x++; + if(x>=XRES){ + y++; + x = 0; + } + } + draw_line(vid, 0, lpy, XRES, lpy, 0, 255, 120, XRES+BARSIZE); + draw_line(vid, lpx, 0, lpx, YRES, 0, 255, 120, XRES+BARSIZE); + drawpixel(vid, lpx, lpy, 255, 50, 50, 220); + + drawpixel(vid, lpx+1, lpy, 255, 50, 50, 120); + drawpixel(vid, lpx-1, lpy, 255, 50, 50, 120); + drawpixel(vid, lpx, lpy+1, 255, 50, 50, 120); + drawpixel(vid, lpx, lpy-1, 255, 50, 50, 120); + + fillrect(vid, 7, YRES-26, textwidth(infobuf)+5, 14, 0, 0, 0, 180); + drawtext(vid, 10, YRES-22, infobuf, 255, 255, 255, 255); + } +}*/ + +void Graphics::Clear() +{ + memset(vid, 0, PIXELSIZE * ((XRES+BARSIZE) * (YRES+MENUSIZE))); +} + +void Graphics::AttachSDLSurface(SDL_Surface * surface) +{ + sdl_scrn = surface; +} + +void Graphics::Blit() +{ + pixel * dst; + pixel * src = vid; + int j, x = 0, y = 0, w = XRES+BARSIZE, h = YRES+MENUSIZE, pitch = XRES+BARSIZE; + if (SDL_MUSTLOCK(sdl_scrn)) + if (SDL_LockSurface(sdl_scrn)<0) + return; + dst=(pixel *)sdl_scrn->pixels+y*sdl_scrn->pitch/PIXELSIZE+x; + for (j=0; j<h; j++) + { + memcpy(dst, src, w*PIXELSIZE); + dst+=sdl_scrn->pitch/PIXELSIZE; + src+=pitch; + } + if (SDL_MUSTLOCK(sdl_scrn)) + SDL_UnlockSurface(sdl_scrn); + SDL_UpdateRect(sdl_scrn,0,0,0,0); +} + +Graphics::Graphics() +{ + vid = (pixel *)malloc(PIXELSIZE * ((XRES+BARSIZE) * (YRES+MENUSIZE))); +} diff --git a/src/Gravity.cpp b/src/Gravity.cpp new file mode 100644 index 0000000..cc20b2f --- /dev/null +++ b/src/Gravity.cpp @@ -0,0 +1,488 @@ +#include <math.h> +#include <sys/types.h> +#include <pthread.h> +#include "Config.h" +#include "Gravity.h" +//#include "powder.h" + +#ifdef GRAVFFT +#include <fftw3.h> +#endif + +void Gravity::bilinear_interpolation(float *src, float *dst, int sw, int sh, int rw, int rh) +{ + int y, x, fxceil, fyceil; + float fx, fy, fyc, fxc; + double intp; + float tr, tl, br, bl; + //Bilinear interpolation for upscaling + for (y=0; y<rh; y++) + for (x=0; x<rw; x++) + { + fx = ((float)x)*((float)sw)/((float)rw); + fy = ((float)y)*((float)sh)/((float)rh); + fxc = modf(fx, &intp); + fyc = modf(fy, &intp); + fxceil = (int)ceil(fx); + fyceil = (int)ceil(fy); + if (fxceil>=sw) fxceil = sw-1; + if (fyceil>=sh) fyceil = sh-1; + tr = src[sw*(int)floor(fy)+fxceil]; + tl = src[sw*(int)floor(fy)+(int)floor(fx)]; + br = src[sw*fyceil+fxceil]; + bl = src[sw*fyceil+(int)floor(fx)]; + dst[rw*y+x] = ((tl*(1.0f-fxc))+(tr*(fxc)))*(1.0f-fyc) + ((bl*(1.0f-fxc))+(br*(fxc)))*(fyc); + } +} + +void Gravity::gravity_init() +{ + //Allocate full size Gravmaps + th_ogravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + th_gravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + th_gravy = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + th_gravx = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + th_gravp = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + gravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + gravy = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + gravx = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + gravp = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float)); + gravmask = (unsigned int *)calloc((XRES/CELL)*(YRES/CELL), sizeof(unsigned)); +} + +void Gravity::gravity_cleanup() +{ +#ifdef GRAVFFT + grav_fft_cleanup(); +#endif +} + +void Gravity::gravity_update_async() +{ + int result; + if(ngrav_enable) + { + pthread_mutex_lock(&gravmutex); + result = grav_ready; + if(result) //Did the gravity thread finish? + { + //if (!sys_pause||framerender){ //Only update if not paused + //Switch the full size gravmaps, we don't really need the two above any more + float *tmpf; + + if(th_gravchanged) + { + #if !defined(GRAVFFT) && defined(GRAV_DIFF) + memcpy(gravy, th_gravy, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memcpy(gravx, th_gravx, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memcpy(gravp, th_gravp, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + #else + tmpf = gravy; + gravy = th_gravy; + th_gravy = tmpf; + + tmpf = gravx; + gravx = th_gravx; + th_gravx = tmpf; + + tmpf = gravp; + gravp = th_gravp; + th_gravp = tmpf; + #endif + } + + tmpf = gravmap; + gravmap = th_gravmap; + th_gravmap = tmpf; + + grav_ready = 0; //Tell the other thread that we're ready for it to continue + pthread_cond_signal(&gravcv); + //} + } + pthread_mutex_unlock(&gravmutex); + //Apply the gravity mask + membwand(gravy, gravmask, (XRES/CELL)*(YRES/CELL)*sizeof(float), (XRES/CELL)*(YRES/CELL)*sizeof(unsigned)); + membwand(gravx, gravmask, (XRES/CELL)*(YRES/CELL)*sizeof(float), (XRES/CELL)*(YRES/CELL)*sizeof(unsigned)); + memset(gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + } +} + +void *Gravity::update_grav_async_helper(void * context) +{ + ((Gravity *)context)->update_grav_async(); +} + +void Gravity::update_grav_async() +{ + int done = 0; + int thread_done = 0; + memset(th_ogravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(th_gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(th_gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(th_gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(th_gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + //memset(th_gravy, 0, XRES*YRES*sizeof(float)); + //memset(th_gravx, 0, XRES*YRES*sizeof(float)); + //memset(th_gravp, 0, XRES*YRES*sizeof(float)); +#ifdef GRAVFFT + grav_fft_init(); +#endif + while(!thread_done){ + if(!done){ + update_grav(); + done = 1; + pthread_mutex_lock(&gravmutex); + + grav_ready = done; + thread_done = gravthread_done; + + pthread_mutex_unlock(&gravmutex); + } else { + pthread_mutex_lock(&gravmutex); + pthread_cond_wait(&gravcv, &gravmutex); + + done = grav_ready; + thread_done = gravthread_done; + + pthread_mutex_unlock(&gravmutex); + } + } + pthread_exit(NULL); +} + +void Gravity::start_grav_async() +{ + if(!ngrav_enable){ + gravthread_done = 0; + grav_ready = 0; + pthread_mutex_init (&gravmutex, NULL); + pthread_cond_init(&gravcv, NULL); + pthread_create(&gravthread, NULL, &Gravity::update_grav_async_helper, this); //Start asynchronous gravity simulation + ngrav_enable = 1; + } + memset(gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); +} + +void Gravity::stop_grav_async() +{ + if(ngrav_enable){ + pthread_mutex_lock(&gravmutex); + gravthread_done = 1; + pthread_cond_signal(&gravcv); + pthread_mutex_unlock(&gravmutex); + pthread_join(gravthread, NULL); + pthread_mutex_destroy(&gravmutex); //Destroy the mutex + ngrav_enable = 0; + } + //Clear the grav velocities + memset(gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); +} + +#ifdef GRAVFFT +int grav_fft_status = 0; +float *th_ptgravx, *th_ptgravy, *th_gravmapbig, *th_gravxbig, *th_gravybig; +fftwf_complex *th_ptgravxt, *th_ptgravyt, *th_gravmapbigt, *th_gravxbigt, *th_gravybigt; +fftwf_plan plan_gravmap, plan_gravx_inverse, plan_gravy_inverse; + +void Gravity::grav_fft_init() +{ + int xblock2 = XRES/CELL*2; + int yblock2 = YRES/CELL*2; + int x, y, fft_tsize = (xblock2/2+1)*yblock2; + float distance, scaleFactor; + fftwf_plan plan_ptgravx, plan_ptgravy; + if (grav_fft_status) return; + + //use fftw malloc function to ensure arrays are aligned, to get better performance + th_ptgravx = fftwf_malloc(xblock2*yblock2*sizeof(float)); + th_ptgravy = fftwf_malloc(xblock2*yblock2*sizeof(float)); + th_ptgravxt = fftwf_malloc(fft_tsize*sizeof(fftwf_complex)); + th_ptgravyt = fftwf_malloc(fft_tsize*sizeof(fftwf_complex)); + th_gravmapbig = fftwf_malloc(xblock2*yblock2*sizeof(float)); + th_gravmapbigt = fftwf_malloc(fft_tsize*sizeof(fftwf_complex)); + th_gravxbig = fftwf_malloc(xblock2*yblock2*sizeof(float)); + th_gravybig = fftwf_malloc(xblock2*yblock2*sizeof(float)); + th_gravxbigt = fftwf_malloc(fft_tsize*sizeof(fftwf_complex)); + th_gravybigt = fftwf_malloc(fft_tsize*sizeof(fftwf_complex)); + + //select best algorithm, could use FFTW_PATIENT or FFTW_EXHAUSTIVE but that increases the time taken to plan, and I don't see much increase in execution speed + plan_ptgravx = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravx, th_ptgravxt, FFTW_MEASURE); + plan_ptgravy = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravy, th_ptgravyt, FFTW_MEASURE); + plan_gravmap = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_gravmapbig, th_gravmapbigt, FFTW_MEASURE); + plan_gravx_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravxbigt, th_gravxbig, FFTW_MEASURE); + plan_gravy_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravybigt, th_gravybig, FFTW_MEASURE); + + //(XRES/CELL)*(YRES/CELL)*4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT + scaleFactor = -M_GRAV/((XRES/CELL)*(YRES/CELL)*4); + //calculate velocity map caused by a point mass + for (y=0; y<yblock2; y++) + { + for (x=0; x<xblock2; x++) + { + if (x==XRES/CELL && y==YRES/CELL) continue; + distance = sqrtf(pow(x-(XRES/CELL), 2) + pow(y-(YRES/CELL), 2)); + th_ptgravx[y*xblock2+x] = scaleFactor*(x-(XRES/CELL)) / pow(distance, 3); + th_ptgravy[y*xblock2+x] = scaleFactor*(y-(YRES/CELL)) / pow(distance, 3); + } + } + th_ptgravx[yblock2*xblock2/2+xblock2/2] = 0.0f; + th_ptgravy[yblock2*xblock2/2+xblock2/2] = 0.0f; + + //transform point mass velocity maps + fftwf_execute(plan_ptgravx); + fftwf_execute(plan_ptgravy); + fftwf_destroy_plan(plan_ptgravx); + fftwf_destroy_plan(plan_ptgravy); + fftwf_free(th_ptgravx); + fftwf_free(th_ptgravy); + + //clear padded gravmap + memset(th_gravmapbig,0,xblock2*yblock2*sizeof(float)); + + grav_fft_status = 1; +} + +void Gravity::grav_fft_cleanup() +{ + if (!grav_fft_status) return; + fftwf_free(th_ptgravxt); + fftwf_free(th_ptgravyt); + fftwf_free(th_gravmapbig); + fftwf_free(th_gravmapbigt); + fftwf_free(th_gravxbig); + fftwf_free(th_gravybig); + fftwf_free(th_gravxbigt); + fftwf_free(th_gravybigt); + fftwf_destroy_plan(plan_gravmap); + fftwf_destroy_plan(plan_gravx_inverse); + fftwf_destroy_plan(plan_gravy_inverse); + grav_fft_status = 0; +} + +void Gravity::update_grav() +{ + int x, y, changed = 0; + int xblock2 = XRES/CELL*2, yblock2 = YRES/CELL*2; + int i, fft_tsize = (xblock2/2+1)*yblock2; + float mr, mc, pr, pc, gr, gc; + for (y=0; y<YRES/CELL; y++) + { + if(changed) + break; + for (x=0; x<XRES/CELL; x++) + { + if(th_ogravmap[y*(XRES/CELL)+x]!=th_gravmap[y*(XRES/CELL)+x]){ + changed = 1; + break; + } + } + } + if(changed) + { + th_gravchanged = 1; + if (!grav_fft_status) grav_fft_init(); + + //copy gravmap into padded gravmap array + for (y=0; y<YRES/CELL; y++) + { + for (x=0; x<XRES/CELL; x++) + { + th_gravmapbig[(y+YRES/CELL)*xblock2+XRES/CELL+x] = th_gravmap[y*(XRES/CELL)+x]; + } + } + //transform gravmap + fftwf_execute(plan_gravmap); + //do convolution (multiply the complex numbers) + for (i=0; i<fft_tsize; i++) + { + mr = th_gravmapbigt[i][0]; + mc = th_gravmapbigt[i][1]; + pr = th_ptgravxt[i][0]; + pc = th_ptgravxt[i][1]; + gr = mr*pr-mc*pc; + gc = mr*pc+mc*pr; + th_gravxbigt[i][0] = gr; + th_gravxbigt[i][1] = gc; + pr = th_ptgravyt[i][0]; + pc = th_ptgravyt[i][1]; + gr = mr*pr-mc*pc; + gc = mr*pc+mc*pr; + th_gravybigt[i][0] = gr; + th_gravybigt[i][1] = gc; + } + //inverse transform, and copy from padded arrays into normal velocity maps + fftwf_execute(plan_gravx_inverse); + fftwf_execute(plan_gravy_inverse); + for (y=0; y<YRES/CELL; y++) + { + for (x=0; x<XRES/CELL; x++) + { + th_gravx[y*(XRES/CELL)+x] = th_gravxbig[y*xblock2+x]; + th_gravy[y*(XRES/CELL)+x] = th_gravybig[y*xblock2+x]; + th_gravp[y*(XRES/CELL)+x] = sqrtf(pow(th_gravxbig[y*xblock2+x],2)+pow(th_gravybig[y*xblock2+x],2)); + } + } + } + else + { + th_gravchanged = 0; + } + memcpy(th_ogravmap, th_gravmap, (XRES/CELL)*(YRES/CELL)*sizeof(float)); +} + +#else +// gravity without fast Fourier transforms + +void Gravity::update_grav(void) +{ + int x, y, i, j, changed = 0; + float val, distance; + th_gravchanged = 0; +#ifndef GRAV_DIFF + //Find any changed cells + for (i=0; i<YRES/CELL; i++) + { + if(changed) + break; + for (j=0; j<XRES/CELL; j++) + { + if(th_ogravmap[i*(XRES/CELL)+j]!=th_gravmap[i*(XRES/CELL)+j]){ + changed = 1; + break; + } + } + } + if(!changed) + goto fin; + memset(th_gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + memset(th_gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); +#endif + th_gravchanged = 1; + for (i = 0; i < YRES / CELL; i++) { + for (j = 0; j < XRES / CELL; j++) { +#ifdef GRAV_DIFF + if (th_ogravmap[i*(XRES/CELL)+j] != th_gravmap[i*(XRES/CELL)+j]) + { +#else + if (th_gravmap[i*(XRES/CELL)+j] > 0.0001f || th_gravmap[i*(XRES/CELL)+j]<-0.0001f) //Only calculate with populated or changed cells. + { +#endif + for (y = 0; y < YRES / CELL; y++) { + for (x = 0; x < XRES / CELL; x++) { + if (x == j && y == i)//Ensure it doesn't calculate with itself + continue; + distance = sqrt(pow(j - x, 2) + pow(i - y, 2)); +#ifdef GRAV_DIFF + val = th_gravmap[i*(XRES/CELL)+j] - th_ogravmap[i*(XRES/CELL)+j]; +#else + val = th_gravmap[i*(XRES/CELL)+j]; +#endif + th_gravx[y*(XRES/CELL)+x] += M_GRAV * val * (j - x) / pow(distance, 3); + th_gravy[y*(XRES/CELL)+x] += M_GRAV * val * (i - y) / pow(distance, 3); + th_gravp[y*(XRES/CELL)+x] += M_GRAV * val / pow(distance, 2); + } + } + } + } + } +fin: + memcpy(th_ogravmap, th_gravmap, (XRES/CELL)*(YRES/CELL)*sizeof(float)); +} +#endif + + + +void Gravity::grav_mask_r(int x, int y, char checkmap[YRES/CELL][XRES/CELL], char shape[YRES/CELL][XRES/CELL], char *shapeout) +{ + if(x < 0 || x >= XRES/CELL || y < 0 || y >= YRES/CELL) + return; + if(x == 0 || y ==0 || y == (YRES/CELL)-1 || x == (XRES/CELL)-1) + *shapeout = 1; + checkmap[y][x] = 1; + shape[y][x] = 1; + if(x-1 >= 0 && !checkmap[y][x-1] && bmap[y][x-1]!=WL_GRAV) + grav_mask_r(x-1, y, checkmap, shape, shapeout); + if(y-1 >= 0 && !checkmap[y-1][x] && bmap[y-1][x]!=WL_GRAV) + grav_mask_r(x, y-1, checkmap, shape, shapeout); + if(x+1 < XRES/CELL && !checkmap[y][x+1] && bmap[y][x+1]!=WL_GRAV) + grav_mask_r(x+1, y, checkmap, shape, shapeout); + if(y+1 < YRES/CELL && !checkmap[y+1][x] && bmap[y+1][x]!=WL_GRAV) + grav_mask_r(x, y+1, checkmap, shape, shapeout); + return; +} +void Gravity::mask_free(mask_el *c_mask_el){ + if(c_mask_el==NULL) + return; + if(c_mask_el->next!=NULL) + mask_free((mask_el*)c_mask_el->next); + free(c_mask_el->shape); + free(c_mask_el); +} +void Gravity::gravity_mask() +{ + char checkmap[YRES/CELL][XRES/CELL]; + int x = 0, y = 0, i, j; + unsigned maskvalue; + mask_el *t_mask_el = NULL; + mask_el *c_mask_el = NULL; + if(!gravmask) + return; + memset(checkmap, 0, sizeof(checkmap)); + for(x = 0; x < XRES/CELL; x++) + { + for(y = 0; y < YRES/CELL; y++) + { + if(bmap[y][x]!=WL_GRAV && checkmap[y][x] == 0) + { + //Create a new shape + if(t_mask_el==NULL){ + t_mask_el = (mask_el *)malloc(sizeof(mask_el)); + t_mask_el->shape = (char *)malloc((XRES/CELL)*(YRES/CELL)); + memset(t_mask_el->shape, 0, (XRES/CELL)*(YRES/CELL)); + t_mask_el->shapeout = 0; + t_mask_el->next = NULL; + c_mask_el = t_mask_el; + } else { + c_mask_el->next = (mask_el *)malloc(sizeof(mask_el)); + c_mask_el = (mask_el *)c_mask_el->next; + c_mask_el->shape = (char *)malloc((XRES/CELL)*(YRES/CELL)); + memset(c_mask_el->shape, 0, (XRES/CELL)*(YRES/CELL)); + c_mask_el->shapeout = 0; + c_mask_el->next = NULL; + } + //Fill the shape + grav_mask_r(x, y, (char (*)[XRES/CELL])checkmap, (char (*)[XRES/CELL])c_mask_el->shape, (char*)&c_mask_el->shapeout); + } + } + } + c_mask_el = t_mask_el; + memset(gravmask, 0, (XRES/CELL)*(YRES/CELL)*sizeof(unsigned)); + while(c_mask_el!=NULL) + { + char *cshape = c_mask_el->shape; + for(x = 0; x < XRES/CELL; x++) + { + for(y = 0; y < YRES/CELL; y++) + { + if(cshape[y*(XRES/CELL)+x]){ + if(c_mask_el->shapeout) + maskvalue = 0xFFFFFFFF; + else + maskvalue = 0x00000000; + gravmask[y*(XRES/CELL)+x] = maskvalue; + } + } + } + c_mask_el = (mask_el*)c_mask_el->next; + } + mask_free(t_mask_el); +} + +Gravity::Gravity() +{ + gravity_init(); +} diff --git a/src/Misc.cpp b/src/Misc.cpp new file mode 100644 index 0000000..44770d1 --- /dev/null +++ b/src/Misc.cpp @@ -0,0 +1,664 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <sys/types.h> +#include <math.h> +#include "misc.h" +#include "Config.h" +//#include "interface.h" +//#include "graphics.h" +//#include "powder.h" +//#include <icondoc.h> +//#include <update.h> +#if defined WIN32 +#include <shlobj.h> +#include <shlwapi.h> +#include <windows.h> +#else +#include <unistd.h> +#endif +#ifdef MACOSX +#include <ApplicationServices/ApplicationServices.h> +#endif + +char *clipboard_text = NULL; + +char *exe_name(void) +{ +#if defined WIN32 + char *name= (char *)malloc(64); + DWORD max=64, res; + while ((res = GetModuleFileName(NULL, name, max)) >= max) + { +#elif defined MACOSX + char *fn=malloc(64),*name=malloc(PATH_MAX); + uint32_t max=64, res; + if (_NSGetExecutablePath(fn, &max) != 0) + { + fn = realloc(fn, max); + _NSGetExecutablePath(fn, &max); + } + if (realpath(fn, name) == NULL) + { + free(fn); + free(name); + return NULL; + } + res = 1; +#else + char fn[64], *name=malloc(64); + size_t max=64, res; + sprintf(fn, "/proc/self/exe"); + memset(name, 0, max); + while ((res = readlink(fn, name, max)) >= max-1) + { +#endif +#ifndef MACOSX + max *= 2; + name = (char *)realloc(name, max); + memset(name, 0, max); + } +#endif + if (res <= 0) + { + free(name); + return NULL; + } + return name; +} + +//Signum function +int isign(float i) //TODO: INline or macro +{ + if (i<0) + return -1; + if (i>0) + return 1; + return 0; +} + +unsigned clamp_flt(float f, float min, float max) //TODO: Also inline/macro +{ + if (f<min) + return 0; + if (f>max) + return 255; + return (int)(255.0f*(f-min)/(max-min)); +} + +float restrict_flt(float f, float min, float max) //TODO Inline or macro or something +{ + if (f<min) + return min; + if (f>max) + return max; + return f; +} + +char *mystrdup(char *s) +{ + char *x; + if (s) + { + x = (char*)malloc(strlen(s)+1); + strcpy(x, s); + return x; + } + return s; +} + +void strlist_add(struct strlist **list, char *str) +{ + struct strlist *item = (struct strlist*)malloc(sizeof(struct strlist)); + item->str = mystrdup(str); + item->next = *list; + *list = item; +} + +int strlist_find(struct strlist **list, char *str) +{ + struct strlist *item; + for (item=*list; item; item=item->next) + if (!strcmp(item->str, str)) + return 1; + return 0; +} + +void strlist_free(struct strlist **list) +{ + struct strlist *item; + while (*list) + { + item = *list; + *list = (*list)->next; + free(item); + } +} + +void clean_text(char *text, int vwidth) +{ + int i = 0; + if(strlen(text)*10 > vwidth){ + text[vwidth/10] = 0; + } + for(i = 0; i < strlen(text); i++){ + if(! (text[i]>=' ' && text[i]<127)){ + text[i] = ' '; + } + } +} + +int sregexp(const char *str, char *pattern) +{ + int result; + regex_t patternc; + if (regcomp(&patternc, pattern, 0)!=0) + return 1; + result = regexec(&patternc, str, 0, NULL, 0); + regfree(&patternc); + return result; +} + +void save_string(FILE *f, char *str) +{ + int li = strlen(str); + unsigned char lb[2]; + lb[0] = li; + lb[1] = li >> 8; + fwrite(lb, 2, 1, f); + fwrite(str, li, 1, f); +} + +int load_string(FILE *f, char *str, int max) +{ + int li; + unsigned char lb[2]; + fread(lb, 2, 1, f); + li = lb[0] | (lb[1] << 8); + if (li > max) + { + str[0] = 0; + return 1; + } + fread(str, li, 1, f); + str[li] = 0; + return 0; +} + +void strcaturl(char *dst, char *src) +{ + char *d; + unsigned char *s; + + for (d=dst; *d; d++) ; + + for (s=(unsigned char *)src; *s; s++) + { + if ((*s>='0' && *s<='9') || + (*s>='a' && *s<='z') || + (*s>='A' && *s<='Z')) + *(d++) = *s; + else + { + *(d++) = '%'; + *(d++) = hex[*s>>4]; + *(d++) = hex[*s&15]; + } + } + *d = 0; +} + +void strappend(char *dst, char *src) +{ + char *d; + unsigned char *s; + + for (d=dst; *d; d++) ; + + for (s=(unsigned char *)src; *s; s++) + { + *(d++) = *s; + } + *d = 0; +} + +void *file_load(char *fn, int *size) +{ + FILE *f = fopen(fn, "rb"); + void *s; + + if (!f) + return NULL; + fseek(f, 0, SEEK_END); + *size = ftell(f); + fseek(f, 0, SEEK_SET); + s = malloc(*size); + if (!s) + { + fclose(f); + return NULL; + } + fread(s, *size, 1, f); + fclose(f); + return s; +} + +int cpu_check(void) +{ +#ifdef MACOSX + return 0; +#else +#ifdef X86 + unsigned af,bf,cf,df; + x86_cpuid(0, af, bf, cf, df); + if (bf==0x68747541 && cf==0x444D4163 && df==0x69746E65) + amd = 1; + x86_cpuid(1, af, bf, cf, df); +#ifdef X86_SSE + if (!(df&(1<<25))) + return 1; +#endif +#ifdef X86_SSE2 + if (!(df&(1<<26))) + return 1; +#endif +#ifdef X86_SSE3 + if (!(cf&1)) + return 1; +#endif +#endif +#endif + return 0; +} + +matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2) +{ + matrix2d result = { + m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d, + m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d + }; + return result; +} +vector2d m2d_multiply_v2d(matrix2d m, vector2d v) +{ + vector2d result = { + m.a*v.x+m.b*v.y, + m.c*v.x+m.d*v.y + }; + return result; +} +matrix2d m2d_multiply_float(matrix2d m, float s) +{ + matrix2d result = { + m.a*s, m.b*s, + m.c*s, m.d*s, + }; + return result; +} + +vector2d v2d_multiply_float(vector2d v, float s) +{ + vector2d result = { + v.x*s, + v.y*s + }; + return result; +} + +vector2d v2d_add(vector2d v1, vector2d v2) +{ + vector2d result = { + v1.x+v2.x, + v1.y+v2.y + }; + return result; +} +vector2d v2d_sub(vector2d v1, vector2d v2) +{ + vector2d result = { + v1.x-v2.x, + v1.y-v2.y + }; + return result; +} + +matrix2d m2d_new(float me0, float me1, float me2, float me3) +{ + matrix2d result = {me0,me1,me2,me3}; + return result; +} +vector2d v2d_new(float x, float y) +{ + vector2d result = {x, y}; + return result; +} + +void clipboard_push_text(char * text) +{ +#ifdef MACOSX + PasteboardRef newclipboard; + + if (PasteboardCreate(kPasteboardClipboard, &newclipboard)!=noErr) return; + if (PasteboardClear(newclipboard)!=noErr) return; + PasteboardSynchronize(newclipboard); + + CFDataRef data = CFDataCreate(kCFAllocatorDefault, text, strlen(text)); + PasteboardPutItemFlavor(newclipboard, (PasteboardItemID)1, CFSTR("com.apple.traditional-mac-plain-text"), data, 0); +#elif defined WIN32 + if (OpenClipboard(NULL)) + { + HGLOBAL cbuffer; + char * glbuffer; + + EmptyClipboard(); + + cbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text)+1); + glbuffer = (char*)GlobalLock(cbuffer); + + strcpy(glbuffer, text); + + GlobalUnlock(cbuffer); + SetClipboardData(CF_TEXT, cbuffer); + CloseClipboard(); + } +#elif (defined(LIN32) || defined(LIN64)) && defined(SDL_VIDEO_DRIVER_X11) + if (clipboard_text!=NULL) { + free(clipboard_text); + clipboard_text = NULL; + } + clipboard_text = mystrdup(text); + sdl_wminfo.info.x11.lock_func(); + XSetSelectionOwner(sdl_wminfo.info.x11.display, XA_CLIPBOARD, sdl_wminfo.info.x11.window, CurrentTime); + XFlush(sdl_wminfo.info.x11.display); + sdl_wminfo.info.x11.unlock_func(); +#else + printf("Not implemented: put text on clipboard \"%s\"\n", text); +#endif +} + +char * clipboard_pull_text() +{ +#ifdef MACOSX +#elif defined WIN32 + if (OpenClipboard(NULL)) + { + HANDLE cbuffer; + char * glbuffer; + + cbuffer = GetClipboardData(CF_TEXT); + glbuffer = (char*)GlobalLock(cbuffer); + GlobalUnlock(cbuffer); + CloseClipboard(); + if(glbuffer!=NULL){ + return mystrdup(glbuffer); + } else { + return ""; + } + } +#elif (defined(LIN32) || defined(LIN64)) && defined(SDL_VIDEO_DRIVER_X11) +#else + printf("Not implemented: get text from clipboard\n"); + return ""; +#endif +} + +int register_extension() +{ +#if defined WIN32 + int returnval; + LONG rresult; + HKEY newkey; + char *currentfilename = exe_name(); + char *iconname = NULL; + char *opencommand = NULL; + //char AppDataPath[MAX_PATH]; + char *AppDataPath = NULL; + iconname = (char*)malloc(strlen(currentfilename)+6); + sprintf(iconname, "%s,-102", currentfilename); + + //Create Roaming application data folder + /*if(!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, AppDataPath))) + { + returnval = 0; + goto finalise; + }*/ + + //AppDataPath = _getcwd(NULL, 0); + + //Move Game executable into application data folder + //TODO: Implement + + opencommand = (char*)malloc(strlen(currentfilename)+53+strlen(AppDataPath)); + /*if((strlen(AppDataPath)+strlen(APPDATA_SUBDIR "\\Powder Toy"))<MAX_PATH) + { + strappend(AppDataPath, APPDATA_SUBDIR); + _mkdir(AppDataPath); + strappend(AppDataPath, "\\Powder Toy"); + _mkdir(AppDataPath); + } else { + returnval = 0; + goto finalise; + }*/ + sprintf(opencommand, "\"%s\" open \"%%1\" ddir \"%s\"", currentfilename, AppDataPath); + + //Create extension entry + rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.cps", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL); + if (rresult != ERROR_SUCCESS) { + returnval = 0; + goto finalise; + } + rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1); + if (rresult != ERROR_SUCCESS) { + RegCloseKey(newkey); + returnval = 0; + goto finalise; + } + RegCloseKey(newkey); + + rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.stm", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL); + if (rresult != ERROR_SUCCESS) { + returnval = 0; + goto finalise; + } + rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1); + if (rresult != ERROR_SUCCESS) { + RegCloseKey(newkey); + returnval = 0; + goto finalise; + } + RegCloseKey(newkey); + + //Create program entry + rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL); + if (rresult != ERROR_SUCCESS) { + returnval = 0; + goto finalise; + } + rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"Powder Toy Save", strlen("Powder Toy Save")+1); + if (rresult != ERROR_SUCCESS) { + RegCloseKey(newkey); + returnval = 0; + goto finalise; + } + RegCloseKey(newkey); + + //Set DefaultIcon + rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\DefaultIcon", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL); + if (rresult != ERROR_SUCCESS) { + returnval = 0; + goto finalise; + } + rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)iconname, strlen(iconname)+1); + if (rresult != ERROR_SUCCESS) { + RegCloseKey(newkey); + returnval = 0; + goto finalise; + } + RegCloseKey(newkey); + + //Set Launch command + rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\shell\\open\\command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL); + if (rresult != ERROR_SUCCESS) { + returnval = 0; + goto finalise; + } + rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)opencommand, strlen(opencommand)+1); + if (rresult != ERROR_SUCCESS) { + RegCloseKey(newkey); + returnval = 0; + goto finalise; + } + RegCloseKey(newkey); + + returnval = 1; + finalise: + + if(iconname) free(iconname); + if(opencommand) free(opencommand); + if(currentfilename) free(currentfilename); + + return returnval; +#elif defined(LIN32) || defined(LIN64) + char *currentfilename = exe_name(); + FILE *f; + char *mimedata = +"<?xml version=\"1.0\"?>\n" +" <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>\n" +" <mime-type type=\"application/vnd.powdertoy.save\">\n" +" <comment>Powder Toy save</comment>\n" +" <glob pattern=\"*.cps\"/>\n" +" <glob pattern=\"*.stm\"/>\n" +" </mime-type>\n" +"</mime-info>\n"; + f = fopen("powdertoy-save.xml", "wb"); + if (!f) + return 0; + fwrite(mimedata, 1, strlen(mimedata), f); + fclose(f); + + char *desktopfiledata_tmp = +"[Desktop Entry]\n" +"Type=Application\n" +"Name=Powder Toy\n" +"Comment=Physics sandbox game\n" +"MimeType=application/vnd.powdertoy.save;\n" +"NoDisplay=true\n"; + char *desktopfiledata = malloc(strlen(desktopfiledata_tmp)+strlen(currentfilename)+100); + strcpy(desktopfiledata, desktopfiledata_tmp); + strappend(desktopfiledata, "Exec="); + strappend(desktopfiledata, currentfilename); + strappend(desktopfiledata, " open %f\n"); + f = fopen("powdertoy-tpt.desktop", "wb"); + if (!f) + return 0; + fwrite(desktopfiledata, 1, strlen(desktopfiledata), f); + fclose(f); + system("xdg-mime install powdertoy-save.xml"); + system("xdg-desktop-menu install powdertoy-tpt.desktop"); + f = fopen("powdertoy-save-32.png", "wb"); + if (!f) + return 0; + fwrite(icon_doc_32_png, 1, sizeof(icon_doc_32_png), f); + fclose(f); + f = fopen("powdertoy-save-16.png", "wb"); + if (!f) + return 0; + fwrite(icon_doc_16_png, 1, sizeof(icon_doc_16_png), f); + fclose(f); + system("xdg-icon-resource install --noupdate --context mimetypes --size 32 powdertoy-save-32.png application-vnd.powdertoy.save"); + system("xdg-icon-resource install --noupdate --context mimetypes --size 16 powdertoy-save-16.png application-vnd.powdertoy.save"); + system("xdg-icon-resource forceupdate"); + system("xdg-mime default powdertoy-tpt.desktop application/vnd.powdertoy.save"); + unlink("powdertoy-save-32.png"); + unlink("powdertoy-save-16.png"); + unlink("powdertoy-save.xml"); + unlink("powdertoy-tpt.desktop"); + return 1; +#elif defined MACOSX + return 0; +#endif +} + +void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB +{ + float hh, ss, vv, c, x; + int m; + hh = h/60.0f;//normalize values + ss = s/255.0f; + vv = v/255.0f; + c = vv * ss; + x = c * ( 1 - fabs(fmod(hh,2.0) -1) ); + if(hh<1){ + *r = (int)(c*255.0); + *g = (int)(x*255.0); + *b = 0; + } + else if(hh<2){ + *r = (int)(x*255.0); + *g = (int)(c*255.0); + *b = 0; + } + else if(hh<3){ + *r = 0; + *g = (int)(c*255.0); + *b = (int)(x*255.0); + } + else if(hh<4){ + *r = 0; + *g = (int)(x*255.0); + *b = (int)(c*255.0); + } + else if(hh<5){ + *r = (int)(x*255.0); + *g = 0; + *b = (int)(c*255.0); + } + else if(hh<6){ + *r = (int)(c*255.0); + *g = 0; + *b = (int)(x*255.0); + } + m = (int)((vv-c)*255.0); + *r += m; + *g += m; + *b += m; +} + +void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v)//convert 0-255 RGB values to 0-255(0-360 for H) HSV +{ + float rr, gg, bb, a,x,c,d; + rr = r/255.0f;//normalize values + gg = g/255.0f; + bb = b/255.0f; + a = fmin(rr,gg); + a = fmin(a,bb); + x = fmax(rr,gg); + x = fmax(x,bb); + if (a==x)//greyscale + { + *h = 0; + *s = 0; + *v = (int)(a*255.0); + } + else + { + c = (rr==a) ? gg-bb : ((bb==a) ? rr-gg : bb-rr); + d = (rr==a) ? 3 : ((bb==a) ? 1 : 5); + *h = (int)(60.0*(d - c/(x - a))); + *s = (int)(255.0*((x - a)/x)); + *v = (int)(255.0*x); + } +} + +void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize) +{ + size_t i; + unsigned char * dest = (unsigned char*)destv; + unsigned char * src = (unsigned char*)srcv; + for(i = 0; i < destsize; i++){ + dest[i] = dest[i] & src[i%srcsize]; + } +} +vector2d v2d_zero = {0,0}; +matrix2d m2d_identity = {1,0,0,1}; diff --git a/src/PowderToy.cpp b/src/PowderToy.cpp new file mode 100644 index 0000000..d634cce --- /dev/null +++ b/src/PowderToy.cpp @@ -0,0 +1,95 @@ + +#include <time.h> +#include <SDL/SDL.h> +#include "Config.h" +#include "Simulation.h" +#include "Renderer.h" +#include "Graphics.h" +#include "Air.h" + +#include "interface/Window.h" +#include "interface/Button.h" +#include "interface/Sandbox.h" + +SDL_Surface * SDLOpen() +{ + if (SDL_Init(SDL_INIT_VIDEO)<0) + { + fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError()); + return 0; + } + atexit(SDL_Quit); + return SDL_SetVideoMode(XRES + BARSIZE, YRES + MENUSIZE, 32, SDL_SWSURFACE); +} + +int SDLPoll(SDL_Event * event) +{ + while (SDL_PollEvent(event)) + { + switch (event->type) + { + case SDL_QUIT: + return 1; + } + } + return 0; +} + +int main(int argc, char * argv[]) +{ + int mouseX, mouseY, mouseButton, lastMouseButton; + + //Renderer * ren; + Graphics * g = new Graphics(); + g->AttachSDLSurface(SDLOpen()); + //Simulation * sim = new Simulation(); + //ren = new Renderer(g, sim); + + ui::Window * window = new ui::Window(); + ui::Sandbox * sandbox = new ui::Sandbox(); + ui::Button * button = new ui::Button(100, 100, 100, 100, "poP"); + window->Add(sandbox); + window->Add(button); + + SDL_Event event; + while(!SDLPoll(&event)) + { + mouseButton = SDL_GetMouseState(&mouseX, &mouseY); + switch(event.type) + { + case SDL_KEYDOWN: + break; + case SDL_KEYUP: + break; + case SDL_MOUSEMOTION: + window->OnMouseMove(event.motion.x, event.motion.y); + break; + case SDL_MOUSEBUTTONDOWN: + window->OnMouseDown(event.motion.x, event.motion.y, event.button.button); + break; + case SDL_MOUSEBUTTONUP: + window->OnMouseUp(event.motion.x, event.motion.y, event.button.button); + break; + } + window->Tick(1.0f); + window->Draw(g); + /*sim->update_particles(); + sim->air->update_air(); + mouseButton = SDL_GetMouseState(&mouseX, &mouseY); + if(mouseButton) + { + sim->create_parts(mouseX, mouseY, 4, 4, (rand()%4)+1, 0); + } + if(mouseButton==4 && !lastMouseButton) + { + sim->sys_pause = !sim->sys_pause; + } + //ren->render_parts(); + //ren->render_fire(); + + + ren->g->clearrect(0, 0, XRES+BARSIZE, YRES+MENUSIZE);*/ + g->Blit(); + g->Clear(); + } +} diff --git a/src/Renderer.cpp b/src/Renderer.cpp new file mode 100644 index 0000000..c1bbf3c --- /dev/null +++ b/src/Renderer.cpp @@ -0,0 +1,1692 @@ +/* + * Renderer.cpp + * + * Created on: Jan 7, 2012 + * Author: Simon + */ + +#include <math.h> +#include "Config.h" +#include "Renderer.h" +#include "Graphics.h" +#include "Elements.h" +#include "ElementFunctions.h" +#include "ElementGraphics.h" +#include "Air.h" +extern "C" +{ +#include "hmap.h" +} + +void Renderer::draw_walls() +{ + int x, y, i, j, cr, cg, cb; + unsigned char wt; + pixel pc; + pixel gc; + unsigned char (*bmap)[XRES/CELL] = sim->bmap; + unsigned char (*emap)[XRES/CELL] = sim->emap; + wall_type *wtypes = sim->wtypes; + pixel * vid = g->vid; + + for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + if (bmap[y][x]) + { + wt = bmap[y][x]-UI_ACTUALSTART; + if (wt<0 || wt>=UI_WALLCOUNT) + continue; + pc = wtypes[wt].colour; + gc = wtypes[wt].eglow; + + // standard wall patterns + if (wtypes[wt].drawstyle==1) + { + for (j=0; j<CELL; j+=2) + for (i=(j>>1)&1; i<CELL; i+=2) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + } + else if (wtypes[wt].drawstyle==2) + { + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + } + else if (wtypes[wt].drawstyle==3) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + } + else if (wtypes[wt].drawstyle==4) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if(i == j) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + else if (i == j+1 || (i == 0 && j == CELL-1)) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = gc; + else + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x202020); + } + + // special rendering for some walls + if (bmap[y][x]==WL_EWALL) + { + if (emap[y][x]) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if (i&j&1) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + } + else + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if (!(i&j&1)) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + } + } + else if (bmap[y][x]==WL_WALLELEC) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + { + if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = pc; + else + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x808080); + } + } + else if (bmap[y][x]==WL_EHOLE) + { + if (emap[y][x]) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x242424); + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x000000); + } + else + { + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x242424); + } + } + if (render_mode & PMODE_BLOB) + { + // when in blob view, draw some blobs... + if (wtypes[wt].drawstyle==1) + { + for (j=0; j<CELL; j+=2) + for (i=(j>>1)&1; i<CELL; i+=2) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); + } + else if (wtypes[wt].drawstyle==2) + { + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); + } + else if (wtypes[wt].drawstyle==3) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); + } + else if (wtypes[wt].drawstyle==4) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if(i == j) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); + else if (i == j+1 || (i == 0 && j == CELL-1)) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(gc), PIXG(gc), PIXB(gc)); + else + g->drawblob((x*CELL+i), (y*CELL+j), 0x20, 0x20, 0x20); + } + if (bmap[y][x]==WL_EWALL) + { + if (emap[y][x]) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if (i&j&1) + g->drawblob((x*CELL+i), (y*CELL+j), 0x80, 0x80, 0x80); + } + else + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + if (!(i&j&1)) + g->drawblob((x*CELL+i), (y*CELL+j), 0x80, 0x80, 0x80); + } + } + else if (bmap[y][x]==WL_WALLELEC) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + { + if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) + g->drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); + else + g->drawblob((x*CELL+i), (y*CELL+j), 0x80, 0x80, 0x80); + } + } + else if (bmap[y][x]==WL_EHOLE) + { + if (emap[y][x]) + { + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + g->drawblob((x*CELL+i), (y*CELL+j), 0x24, 0x24, 0x24); + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + vid[(y*CELL+j)*(XRES+BARSIZE)+(x*CELL+i)] = PIXPACK(0x000000); + } + else + { + for (j=0; j<CELL; j+=2) + for (i=0; i<CELL; i+=2) + g->drawblob((x*CELL+i), (y*CELL+j), 0x24, 0x24, 0x24); + } + } + } + if (wtypes[wt].eglow && emap[y][x]) + { + // glow if electrified + pc = wtypes[wt].eglow; + cr = fire_r[y][x] + PIXR(pc); + if (cr > 255) cr = 255; + fire_r[y][x] = cr; + cg = fire_g[y][x] + PIXG(pc); + if (cg > 255) cg = 255; + fire_g[y][x] = cg; + cb = fire_b[y][x] + PIXB(pc); + if (cb > 255) cb = 255; + fire_b[y][x] = cb; + + } + } +} + +void Renderer::get_sign_pos(int i, int *x0, int *y0, int *w, int *h) +{ + sign *signs = sim->signs; + //Changing width if sign have special content + if (strcmp(signs[i].text, "{p}")==0) + *w = Graphics::textwidth("Pressure: -000.00"); + + if (strcmp(signs[i].text, "{t}")==0) + *w = Graphics::textwidth("Temp: 0000.00"); + + if (sregexp(signs[i].text, "^{c:[0-9]*|.*}$")==0) + { + int sldr, startm; + char buff[256]; + memset(buff, 0, sizeof(buff)); + for (sldr=3; signs[i].text[sldr-1] != '|'; sldr++) + startm = sldr + 1; + + sldr = startm; + while (signs[i].text[sldr] != '}') + { + buff[sldr - startm] = signs[i].text[sldr]; + sldr++; + } + *w = Graphics::textwidth(buff) + 5; + } + + //Ususal width + if (strcmp(signs[i].text, "{p}") && strcmp(signs[i].text, "{t}") && sregexp(signs[i].text, "^{c:[0-9]*|.*}$")) + *w = Graphics::textwidth(signs[i].text) + 5; + *h = 14; + *x0 = (signs[i].ju == 2) ? signs[i].x - *w : + (signs[i].ju == 1) ? signs[i].x - *w/2 : signs[i].x; + *y0 = (signs[i].y > 18) ? signs[i].y - 18 : signs[i].y + 4; +} + +void Renderer::render_signs() +{ + int i, j, x, y, w, h, dx, dy,mx,my,b=1,bq; + sign *signs = sim->signs; + for (i=0; i<MAXSIGNS; i++) + if (signs[i].text[0]) + { + char buff[256]; //Buffer + get_sign_pos(i, &x, &y, &w, &h); + g->clearrect(x, y, w, h); + g->drawrect(x, y, w, h, 192, 192, 192, 255); + + //Displaying special information + if (strcmp(signs[i].text, "{p}")==0) + { + sprintf(buff, "Pressure: %3.2f", sim->pv[signs[i].y/CELL][signs[i].x/CELL]); //...pressure + g->drawtext(x+3, y+3, buff, 255, 255, 255, 255); + } + if (strcmp(signs[i].text, "{t}")==0) + { + if (sim->pmap[signs[i].y][signs[i].x]) + sprintf(buff, "Temp: %4.2f", sim->parts[sim->pmap[signs[i].y][signs[i].x]>>8].temp-273.15); //...tempirature + else + sprintf(buff, "Temp: 0.00"); //...tempirature + g->drawtext(x+3, y+3, buff, 255, 255, 255, 255); + } + + if (sregexp(signs[i].text, "^{c:[0-9]*|.*}$")==0) + { + int sldr, startm; + memset(buff, 0, sizeof(buff)); + for (sldr=3; signs[i].text[sldr-1] != '|'; sldr++) + startm = sldr + 1; + sldr = startm; + while (signs[i].text[sldr] != '}') + { + buff[sldr - startm] = signs[i].text[sldr]; + sldr++; + } + g->drawtext(x+3, y+3, buff, 0, 191, 255, 255); + } + + //Usual text + if (strcmp(signs[i].text, "{p}") && strcmp(signs[i].text, "{t}") && sregexp(signs[i].text, "^{c:[0-9]*|.*}$")) + g->drawtext(x+3, y+3, signs[i].text, 255, 255, 255, 255); + + x = signs[i].x; + y = signs[i].y; + dx = 1 - signs[i].ju; + dy = (signs[i].y > 18) ? -1 : 1; + for (j=0; j<4; j++) + { + g->drawpixel(x, y, 192, 192, 192, 255); + x+=dx; + y+=dy; + } + /*if (MSIGN==i) + { + bq = b; + b = SDL_GetMouseState(&mx, &my); + mx /= sdl_scale; + my /= sdl_scale; + signs[i].x = mx; + signs[i].y = my; + }*/ + } +} + +void Renderer::render_gravlensing() +{ + int nx, ny, rx, ry, gx, gy, bx, by, co; + int r, g, b; + pixel t; + pixel *src = this->g->vid; + pixel *dst = this->g->vid; + for(nx = 0; nx < XRES; nx++) + { + for(ny = 0; ny < YRES; ny++) + { + co = (ny/CELL)*(XRES/CELL)+(nx/CELL); + rx = (int)(nx-sim->gravx[co]*0.75f+0.5f); + ry = (int)(ny-sim->gravy[co]*0.75f+0.5f); + gx = (int)(nx-sim->gravx[co]*0.875f+0.5f); + gy = (int)(ny-sim->gravy[co]*0.875f+0.5f); + bx = (int)(nx-sim->gravx[co]+0.5f); + by = (int)(ny-sim->gravy[co]+0.5f); + if(rx > 0 && rx < XRES && ry > 0 && ry < YRES && gx > 0 && gx < XRES && gy > 0 && gy < YRES && bx > 0 && bx < XRES && by > 0 && by < YRES) + { + t = dst[ny*(XRES+BARSIZE)+nx]; + r = PIXR(src[ry*(XRES+BARSIZE)+rx]) + PIXR(t); + g = PIXG(src[gy*(XRES+BARSIZE)+gx]) + PIXG(t); + b = PIXB(src[by*(XRES+BARSIZE)+bx]) + PIXB(t); + if (r>255) + r = 255; + if (g>255) + g = 255; + if (b>255) + b = 255; + dst[ny*(XRES+BARSIZE)+nx] = PIXRGB(r,g,b); + // addpixel(dst, nx, ny, PIXR(src[ry*(XRES+BARSIZE)+rx]), PIXG(src[gy*(XRES+BARSIZE)+gx]), PIXB(src[by*(XRES+BARSIZE)+bx]), 255); + } + + /*rx = nx+(gravxf[(ny*XRES)+nx]*0.5f); + ry = ny+(gravyf[(ny*XRES)+nx]*0.5f); + gx = nx+(gravxf[(ny*XRES)+nx]*0.75f); + gy = ny+(gravyf[(ny*XRES)+nx]*0.75f); + bx = nx+(gravxf[(ny*XRES)+nx]); + by = ny+(gravyf[(ny*XRES)+nx]); + if(rx > 0 && rx < XRES && ry > 0 && ry < YRES && gravp[ny/CELL][nx/CELL]*0.5f > -8.0f) + addpixel(dst, rx, ry, PIXR(src[ry*(XRES+BARSIZE)+rx]), 0, 0, 255); + if(gx > 0 && gx < XRES && gy > 0 && gy < YRES && gravp[ny/CELL][nx/CELL]*0.75f > -8.0f) + addpixel(dst, gx, gy, 0, PIXG(src[ry*(XRES+BARSIZE)+rx]), 0, 255); + if(bx > 0 && bx < XRES && by > 0 && by < YRES && gravp[ny/CELL][nx/CELL] > -8.0f) + addpixel(dst, bx, by, 0, 0, PIXB(src[ry*(XRES+BARSIZE)+rx]), 255);*/ + } + } +} + +void Renderer::render_fire() +{ + int i,j,x,y,r,g,b,nx,ny; + for (j=0; j<YRES/CELL; j++) + for (i=0; i<XRES/CELL; i++) + { + r = fire_r[j][i]; + g = fire_g[j][i]; + b = fire_b[j][i]; + if (r || g || b) + for (y=-CELL+1; y<2*CELL; y++) + for (x=-CELL+1; x<2*CELL; x++) + this->g->addpixel(i*CELL+x, j*CELL+y, r, g, b, fire_alpha[y+CELL][x+CELL]); + r *= 8; + g *= 8; + b *= 8; + for (y=-1; y<2; y++) + for (x=-1; x<2; x++) + if ((x || y) && i+x>=0 && j+y>=0 && i+x<XRES/CELL && j+y<YRES/CELL) + { + r += fire_r[j+y][i+x]; + g += fire_g[j+y][i+x]; + b += fire_b[j+y][i+x]; + } + r /= 16; + g /= 16; + b /= 16; + fire_r[j][i] = r>4 ? r-4 : 0; + fire_g[j][i] = g>4 ? g-4 : 0; + fire_b[j][i] = b>4 ? b-4 : 0; + } +} + +void Renderer::prepare_alpha(int size, float intensity) +{ + //TODO: implement size + int x,y,i,j,c; + float multiplier = 255.0f*intensity; + float temp[CELL*3][CELL*3]; + float fire_alphaf[CELL*3][CELL*3]; + float glow_alphaf[11][11]; + float blur_alphaf[7][7]; + memset(temp, 0, sizeof(temp)); + for (x=0; x<CELL; x++) + for (y=0; y<CELL; y++) + for (i=-CELL; i<CELL; i++) + for (j=-CELL; j<CELL; j++) + temp[y+CELL+j][x+CELL+i] += expf(-0.1f*(i*i+j*j)); + for (x=0; x<CELL*3; x++) + for (y=0; y<CELL*3; y++) + fire_alpha[y][x] = (int)(multiplier*temp[y][x]/(CELL*CELL)); + +#ifdef OGLR + for (x=0; x<CELL*3; x++) + for (y=0; y<CELL*3; y++) + { + fire_alphaf[y][x] = intensity*temp[y][x]/((float)(CELL*CELL)); + } + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fireAlpha); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, CELL*3, CELL*3, GL_ALPHA, GL_FLOAT, fire_alphaf); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + memset(glow_alphaf, 0, sizeof(glow_alphaf)); + + c = 5; + + glow_alphaf[c][c-1] = 0.4f; + glow_alphaf[c][c+1] = 0.4f; + glow_alphaf[c-1][c] = 0.4f; + glow_alphaf[c+1][c] = 0.4f; + for (x = 1; x < 6; x++) { + glow_alphaf[c][c-x] += 0.02f; + glow_alphaf[c][c+x] += 0.02f; + glow_alphaf[c-x][c] += 0.02f; + glow_alphaf[c+x][c] += 0.02f; + for (y = 1; y < 6; y++) { + if(x + y > 7) + continue; + glow_alphaf[c+x][c-y] += 0.02f; + glow_alphaf[c-x][c+y] += 0.02f; + glow_alphaf[c+x][c+y] += 0.02f; + glow_alphaf[c-x][c-y] += 0.02f; + } + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, glowAlpha); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 11, 11, GL_ALPHA, GL_FLOAT, glow_alphaf); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + c = 3; + + for (x=-3; x<4; x++) + { + for (y=-3; y<4; y++) + { + if (abs(x)+abs(y) <2 && !(abs(x)==2||abs(y)==2)) + blur_alphaf[c+x][c-y] = 0.11f; + if (abs(x)+abs(y) <=3 && abs(x)+abs(y)) + blur_alphaf[c+x][c-y] = 0.08f; + if (abs(x)+abs(y) == 2) + blur_alphaf[c+x][c-y] = 0.04f; + } + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, blurAlpha); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 7, 7, GL_ALPHA, GL_FLOAT, blur_alphaf); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +#endif +} + +void Renderer::render_parts() +{ + int deca, decr, decg, decb, cola, colr, colg, colb, firea, firer, fireg, fireb, pixel_mode, q, i, t, nx, ny, x, y, caddress; + int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0}; + float gradv, flicker, fnx, fny; + Particle * parts = sim->parts; + part_transition *ptransitions = sim->ptransitions; + part_type *ptypes = sim->ptypes; +#ifdef OGLR + int cfireV = 0, cfireC = 0, cfire = 0; + int csmokeV = 0, csmokeC = 0, csmoke = 0; + int cblobV = 0, cblobC = 0, cblob = 0; + int cblurV = 0, cblurC = 0, cblur = 0; + int cglowV = 0, cglowC = 0, cglow = 0; + int cflatV = 0, cflatC = 0, cflat = 0; + int caddV = 0, caddC = 0, cadd = 0; + int clineV = 0, clineC = 0, cline = 0; + GLuint origBlendSrc, origBlendDst; + + glGetIntegerv(GL_BLEND_SRC, &origBlendSrc); + glGetIntegerv(GL_BLEND_DST, &origBlendDst); + //Render to the particle FBO + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); +#else + /*if (GRID_MODE)//draws the grid + { + for (ny=0; ny<YRES; ny++) + for (nx=0; nx<XRES; nx++) + { + if (ny%(4*GRID_MODE)==0) + blendpixel(nx, ny, 100, 100, 100, 80); + if (nx%(4*GRID_MODE)==0) + blendpixel(nx, ny, 100, 100, 100, 80); + } + }*/ +#endif + for(i = 0; i<=sim->parts_lastActiveIndex; i++) { + if (sim->parts[i].type) { + t = sim->parts[i].type; + + nx = (int)(sim->parts[i].x+0.5f); + ny = (int)(sim->parts[i].y+0.5f); + fnx = sim->parts[i].x; + fny = sim->parts[i].y; + + if((sim->photons[ny][nx]&0xFF) && !(ptypes[t].properties & TYPE_ENERGY)) + continue; + + //Defaults + pixel_mode = 0 | PMODE_FLAT; + cola = 255; + colr = PIXR(ptypes[t].pcolors); + colg = PIXG(ptypes[t].pcolors); + colb = PIXB(ptypes[t].pcolors); + firea = 0; + + deca = (sim->parts[i].dcolour>>24)&0xFF; + decr = (sim->parts[i].dcolour>>16)&0xFF; + decg = (sim->parts[i].dcolour>>8)&0xFF; + decb = (sim->parts[i].dcolour)&0xFF; + + /*if(display_mode == RENDER_NONE) + { + if(decorations_enable) + { + colr = (deca*decr + (255-deca)*colr) >> 8; + colg = (deca*decg + (255-deca)*colg) >> 8; + colb = (deca*decb + (255-deca)*colb) >> 8; + } +#ifdef OGLR + flatV[cflatV++] = nx; + flatV[cflatV++] = ny; + flatC[cflatC++] = ((float)colr)/255.0f; + flatC[cflatC++] = ((float)colg)/255.0f; + flatC[cflatC++] = ((float)colb)/255.0f; + flatC[cflatC++] = 1.0f; + cflat++; +#else + vid[ny*(XRES+BARSIZE)+nx] = PIXRGB(colr,colg,colb); +#endif + } + else*/ + { + if (graphicscache[t].isready) + { + pixel_mode = graphicscache[t].pixel_mode; + cola = graphicscache[t].cola; + colr = graphicscache[t].colr; + colg = graphicscache[t].colg; + colb = graphicscache[t].colb; + firea = graphicscache[t].firea; + firer = graphicscache[t].firer; + fireg = graphicscache[t].fireg; + fireb = graphicscache[t].fireb; + } + else + { + if (ptypes[t].graphics_func) + { + if ((*(ptypes[t].graphics_func))(this, &(sim->parts[i]), nx, ny, &pixel_mode, &cola, &colr, &colg, &colb, &firea, &firer, &fireg, &fireb)) //That's a lot of args, a struct might be better + { + graphicscache[t].isready = 1; + graphicscache[t].pixel_mode = pixel_mode; + graphicscache[t].cola = cola; + graphicscache[t].colr = colr; + graphicscache[t].colg = colg; + graphicscache[t].colb = colb; + graphicscache[t].firea = firea; + graphicscache[t].firer = firer; + graphicscache[t].fireg = fireg; + graphicscache[t].fireb = fireb; + } + } + else + { + if(graphics_DEFAULT(this, &(sim->parts[i]), nx, ny, &pixel_mode, &cola, &colr, &colg, &colb, &firea, &firer, &fireg, &fireb)) + { + graphicscache[t].isready = 1; + graphicscache[t].pixel_mode = pixel_mode; + graphicscache[t].cola = cola; + graphicscache[t].colr = colr; + graphicscache[t].colg = colg; + graphicscache[t].colb = colb; + graphicscache[t].firea = firea; + graphicscache[t].firer = firer; + graphicscache[t].fireg = fireg; + graphicscache[t].fireb = fireb; + } + } + } + if((ptypes[t].properties & PROP_HOT_GLOW) && sim->parts[i].temp>(ptransitions[t].thv-800.0f)) + { + gradv = 3.1415/(2*ptransitions[t].thv-(ptransitions[t].thv-800.0f)); + caddress = (sim->parts[i].temp>ptransitions[t].thv)?ptransitions[t].thv-(ptransitions[t].thv-800.0f):sim->parts[i].temp-(ptransitions[t].thv-800.0f); + colr += sin(gradv*caddress) * 226;; + colg += sin(gradv*caddress*4.55 +3.14) * 34; + colb += sin(gradv*caddress*2.22 +3.14) * 64; + } + + if((pixel_mode & FIRE_ADD) && !(render_mode & FIRE_ADD)) + pixel_mode |= PMODE_GLOW; + if((pixel_mode & FIRE_BLEND) && !(render_mode & FIRE_BLEND)) + pixel_mode |= PMODE_BLUR; + if((pixel_mode & PMODE_BLUR) && !(render_mode & PMODE_BLUR)) + pixel_mode |= PMODE_FLAT; + if((pixel_mode & PMODE_GLOW) && !(render_mode & PMODE_GLOW)) + pixel_mode |= PMODE_FLAT; + if (render_mode & PMODE_BLOB) + pixel_mode |= PMODE_BLOB; + + pixel_mode &= render_mode; + + //Alter colour based on display mode + if(colour_mode & COLOUR_HEAT) + { + caddress = restrict_flt((int)( restrict_flt((float)(sim->parts[i].temp+(-MIN_TEMP)), 0.0f, MAX_TEMP+(-MIN_TEMP)) / ((MAX_TEMP+(-MIN_TEMP))/1024) ) *3, 0.0f, (1024.0f*3)-3); + firea = 255; + firer = colr = (unsigned char)color_data[caddress]; + fireg = colg = (unsigned char)color_data[caddress+1]; + fireb = colb = (unsigned char)color_data[caddress+2]; + cola = 255; + if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR; + } + else if(colour_mode & COLOUR_LIFE) + { + gradv = 0.4f; + if (!(sim->parts[i].life<5)) + q = sqrt(sim->parts[i].life); + else + q = sim->parts[i].life; + colr = colg = colb = sin(gradv*q) * 100 + 128; + cola = 255; + if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR; + } + else if (colour_mode & COLOUR_GRAD) + { + float frequency = 0.05; + int q = sim->parts[i].temp-40; + colr = sin(frequency*q) * 16 + colr; + colg = sin(frequency*q) * 16 + colg; + colb = sin(frequency*q) * 16 + colb; + if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR; + } + + //Apply decoration colour + if(!colour_mode) + { + if(!(pixel_mode & NO_DECO) && decorations_enable) + { + colr = (deca*decr + (255-deca)*colr) >> 8; + colg = (deca*decg + (255-deca)*colg) >> 8; + colb = (deca*decb + (255-deca)*colb) >> 8; + } + + if((pixel_mode & DECO_FIRE) && decorations_enable) + { + firer = (deca*decr + (255-deca)*firer) >> 8; + fireg = (deca*decg + (255-deca)*fireg) >> 8; + fireb = (deca*decb + (255-deca)*fireb) >> 8; + } + } + + #ifndef OGLR + //All colours are now set, check ranges + if(colr>255) colr = 255; + else if(colr<0) colr = 0; + if(colg>255) colg = 255; + else if(colg<0) colg = 0; + if(colb>255) colb = 255; + else if(colb<0) colb = 0; + if(cola>255) cola = 255; + else if(cola<0) cola = 0; + #endif + + //Pixel rendering + if(pixel_mode & PSPEC_STICKMAN) + { + char buff[20]; //Buffer for HP + int s; + int legr, legg, legb; + playerst *cplayer; + if(t==PT_STKM) + cplayer = &sim->player; + else if(t==PT_STKM2) + cplayer = &sim->player2; + else if(t==PT_FIGH) + cplayer = &sim->fighters[(unsigned char)sim->parts[i].tmp]; + else + continue; + +/* if (mousex>(nx-3) && mousex<(nx+3) && mousey<(ny+3) && mousey>(ny-3)) //If mous is in the head + { + sprintf(buff, "%3d", sim->parts[i].life); //Show HP + g->drawtext(mousex-8-2*(sim->parts[i].life<100)-2*(sim->parts[i].life<10), mousey-12, buff, 255, 255, 255, 255); + }*/ + + if (colour_mode!=COLOUR_HEAT) + { + if (cplayer->elem<PT_NUM) + { + colr = PIXR(ptypes[cplayer->elem].pcolors); + colg = PIXG(ptypes[cplayer->elem].pcolors); + colb = PIXB(ptypes[cplayer->elem].pcolors); + } + else + { + colr = 0x80; + colg = 0x80; + colb = 0xFF; + } + } +#ifdef OGLR + glColor4f(((float)colr)/255.0f, ((float)colg)/255.0f, ((float)colb)/255.0f, 1.0f); + glBegin(GL_LINE_STRIP); + if(t==PT_FIGH) + { + glVertex2f(fnx, fny+2); + glVertex2f(fnx+2, fny); + glVertex2f(fnx, fny-2); + glVertex2f(fnx-2, fny); + glVertex2f(fnx, fny+2); + } + else + { + glVertex2f(fnx-2, fny-2); + glVertex2f(fnx+2, fny-2); + glVertex2f(fnx+2, fny+2); + glVertex2f(fnx-2, fny+2); + glVertex2f(fnx-2, fny-2); + } + glEnd(); + glBegin(GL_LINES); + + if (colour_mode!=COLOUR_HEAT) + { + if (t==PT_STKM2) + glColor4f(100.0f/255.0f, 100.0f/255.0f, 1.0f, 1.0f); + else + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + + glVertex2f(nx, ny+3); + glVertex2f(cplayer->legs[0], cplayer->legs[1]); + + glVertex2f(cplayer->legs[0], cplayer->legs[1]); + glVertex2f(cplayer->legs[4], cplayer->legs[5]); + + glVertex2f(nx, ny+3); + glVertex2f(cplayer->legs[8], cplayer->legs[9]); + + glVertex2f(cplayer->legs[8], cplayer->legs[9]); + glVertex2f(cplayer->legs[12], cplayer->legs[13]); + glEnd(); +#else + s = XRES+BARSIZE; + + if (t==PT_STKM2) + { + legr = 100; + legg = 100; + legb = 255; + } + else + { + legr = 255; + legg = 255; + legb = 255; + } + + if (colour_mode==COLOUR_HEAT) + { + legr = colr; + legg = colg; + legb = colb; + } + + //head + if(t==PT_FIGH) + { + g->draw_line(nx, ny+2, nx+2, ny, colr, colg, colb, s); + g->draw_line(nx+2, ny, nx, ny-2, colr, colg, colb, s); + g->draw_line(nx, ny-2, nx-2, ny, colr, colg, colb, s); + g->draw_line(nx-2, ny, nx, ny+2, colr, colg, colb, s); + } + else + { + g->draw_line(nx-2, ny+2, nx+2, ny+2, colr, colg, colb, s); + g->draw_line(nx-2, ny-2, nx+2, ny-2, colr, colg, colb, s); + g->draw_line(nx-2, ny-2, nx-2, ny+2, colr, colg, colb, s); + g->draw_line(nx+2, ny-2, nx+2, ny+2, colr, colg, colb, s); + } + //legs + g->draw_line(nx, ny+3, cplayer->legs[0], cplayer->legs[1], legr, legg, legb, s); + g->draw_line(cplayer->legs[0], cplayer->legs[1], cplayer->legs[4], cplayer->legs[5], legr, legg, legb, s); + g->draw_line(nx, ny+3, cplayer->legs[8], cplayer->legs[9], legr, legg, legb, s); + g->draw_line(cplayer->legs[8], cplayer->legs[9], cplayer->legs[12], cplayer->legs[13], legr, legg, legb, s); +#endif + } + if(pixel_mode & PMODE_FLAT) + { +#ifdef OGLR + flatV[cflatV++] = nx; + flatV[cflatV++] = ny; + flatC[cflatC++] = ((float)colr)/255.0f; + flatC[cflatC++] = ((float)colg)/255.0f; + flatC[cflatC++] = ((float)colb)/255.0f; + flatC[cflatC++] = 1.0f; + cflat++; +#else + g->vid[ny*(XRES+BARSIZE)+nx] = PIXRGB(colr,colg,colb); +#endif + } + if(pixel_mode & PMODE_BLEND) + { +#ifdef OGLR + flatV[cflatV++] = nx; + flatV[cflatV++] = ny; + flatC[cflatC++] = ((float)colr)/255.0f; + flatC[cflatC++] = ((float)colg)/255.0f; + flatC[cflatC++] = ((float)colb)/255.0f; + flatC[cflatC++] = ((float)cola)/255.0f; + cflat++; +#else + g->blendpixel(nx, ny, colr, colg, colb, cola); +#endif + } + if(pixel_mode & PMODE_ADD) + { +#ifdef OGLR + addV[caddV++] = nx; + addV[caddV++] = ny; + addC[caddC++] = ((float)colr)/255.0f; + addC[caddC++] = ((float)colg)/255.0f; + addC[caddC++] = ((float)colb)/255.0f; + addC[caddC++] = ((float)cola)/255.0f; + cadd++; +#else + g->addpixel(nx, ny, colr, colg, colb, cola); +#endif + } + if(pixel_mode & PMODE_BLOB) + { +#ifdef OGLR + blobV[cblobV++] = nx; + blobV[cblobV++] = ny; + blobC[cblobC++] = ((float)colr)/255.0f; + blobC[cblobC++] = ((float)colg)/255.0f; + blobC[cblobC++] = ((float)colb)/255.0f; + blobC[cblobC++] = 1.0f; + cblob++; +#else + g->vid[ny*(XRES+BARSIZE)+nx] = PIXRGB(colr,colg,colb); + + g->blendpixel(nx+1, ny, colr, colg, colb, 223); + g->blendpixel(nx-1, ny, colr, colg, colb, 223); + g->blendpixel(nx, ny+1, colr, colg, colb, 223); + g->blendpixel(nx, ny-1, colr, colg, colb, 223); + + g->blendpixel(nx+1, ny-1, colr, colg, colb, 112); + g->blendpixel(nx-1, ny-1, colr, colg, colb, 112); + g->blendpixel(nx+1, ny+1, colr, colg, colb, 112); + g->blendpixel(nx-1, ny+1, colr, colg, colb, 112); +#endif + } + if(pixel_mode & PMODE_GLOW) + { +#ifdef OGLR + glowV[cglowV++] = nx; + glowV[cglowV++] = ny; + glowC[cglowC++] = ((float)colr)/255.0f; + glowC[cglowC++] = ((float)colg)/255.0f; + glowC[cglowC++] = ((float)colb)/255.0f; + glowC[cglowC++] = 1.0f; + cglow++; +#else + g->addpixel(nx, ny, colr, colg, colb, 192); + g->addpixel(nx+1, ny, colr, colg, colb, 96); + g->addpixel(nx-1, ny, colr, colg, colb, 96); + g->addpixel(nx, ny+1, colr, colg, colb, 96); + g->addpixel(nx, ny-1, colr, colg, colb, 96); + + for (x = 1; x < 6; x++) { + g->addpixel(nx, ny-x, colr, colg, colb, 5); + g->addpixel(nx, ny+x, colr, colg, colb, 5); + g->addpixel(nx-x, ny, colr, colg, colb, 5); + g->addpixel(nx+x, ny, colr, colg, colb, 5); + for (y = 1; y < 6; y++) { + if(x + y > 7) + continue; + g->addpixel(nx+x, ny-y, colr, colg, colb, 5); + g->addpixel(nx-x, ny+y, colr, colg, colb, 5); + g->addpixel(nx+x, ny+y, colr, colg, colb, 5); + g->addpixel(nx-x, ny-y, colr, colg, colb, 5); + } + } +#endif + } + if(pixel_mode & PMODE_BLUR) + { +#ifdef OGLR + blurV[cblurV++] = nx; + blurV[cblurV++] = ny; + blurC[cblurC++] = ((float)colr)/255.0f; + blurC[cblurC++] = ((float)colg)/255.0f; + blurC[cblurC++] = ((float)colb)/255.0f; + blurC[cblurC++] = 1.0f; + cblur++; +#else + for (x=-3; x<4; x++) + { + for (y=-3; y<4; y++) + { + if (abs(x)+abs(y) <2 && !(abs(x)==2||abs(y)==2)) + g->blendpixel(x+nx, y+ny, colr, colg, colb, 30); + if (abs(x)+abs(y) <=3 && abs(x)+abs(y)) + g->blendpixel(x+nx, y+ny, colr, colg, colb, 20); + if (abs(x)+abs(y) == 2) + g->blendpixel(x+nx, y+ny, colr, colg, colb, 10); + } + } +#endif + } + if(pixel_mode & PMODE_SPARK) + { + flicker = rand()%20; +#ifdef OGLR + //Oh god, this is awful + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx-5; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/30; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx+5; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny-5; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/30; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny+5; + cline++; +#else + gradv = 4*sim->parts[i].life + flicker; + for (x = 0; gradv>0.5; x++) { + g->addpixel(nx+x, ny, colr, colg, colb, gradv); + g->addpixel(nx-x, ny, colr, colg, colb, gradv); + + g->addpixel(nx, ny+x, colr, colg, colb, gradv); + g->addpixel(nx, ny-x, colr, colg, colb, gradv); + gradv = gradv/1.5f; + } +#endif + } + if(pixel_mode & PMODE_FLARE) + { + flicker = rand()%20; +#ifdef OGLR + //Oh god, this is awful + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx-10; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/40; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx+10; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny-10; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/30; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny+10; + cline++; +#else + gradv = flicker + fabs(parts[i].vx)*17 + fabs(sim->parts[i].vy)*17; + g->blendpixel(nx, ny, colr, colg, colb, (gradv*4)>255?255:(gradv*4) ); + g->blendpixel(nx+1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx-1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx, ny+1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx, ny-1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + if (gradv>255) gradv=255; + g->blendpixel(nx+1, ny-1, colr, colg, colb, gradv); + g->blendpixel(nx-1, ny-1, colr, colg, colb, gradv); + g->blendpixel(nx+1, ny+1, colr, colg, colb, gradv); + g->blendpixel(nx-1, ny+1, colr, colg, colb, gradv); + for (x = 1; gradv>0.5; x++) { + g->addpixel(nx+x, ny, colr, colg, colb, gradv); + g->addpixel(nx-x, ny, colr, colg, colb, gradv); + g->addpixel(nx, ny+x, colr, colg, colb, gradv); + g->addpixel(nx, ny-x, colr, colg, colb, gradv); + gradv = gradv/1.2f; + } +#endif + } + if(pixel_mode & PMODE_LFLARE) + { + flicker = rand()%20; +#ifdef OGLR + //Oh god, this is awful + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx-70; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/30; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx+70; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny-70; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 1.0f - ((float)flicker)/50; + lineV[clineV++] = fnx; + lineV[clineV++] = fny; + cline++; + + lineC[clineC++] = ((float)colr)/255.0f; + lineC[clineC++] = ((float)colg)/255.0f; + lineC[clineC++] = ((float)colb)/255.0f; + lineC[clineC++] = 0.0f; + lineV[clineV++] = fnx; + lineV[clineV++] = fny+70; + cline++; +#else + gradv = flicker + fabs(parts[i].vx)*17 + fabs(parts[i].vy)*17; + g->blendpixel(nx, ny, colr, colg, colb, (gradv*4)>255?255:(gradv*4) ); + g->blendpixel(nx+1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx-1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx, ny+1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + g->blendpixel(nx, ny-1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) ); + if (gradv>255) gradv=255; + g->blendpixel(nx+1, ny-1, colr, colg, colb, gradv); + g->blendpixel(nx-1, ny-1, colr, colg, colb, gradv); + g->blendpixel(nx+1, ny+1, colr, colg, colb, gradv); + g->blendpixel(nx-1, ny+1, colr, colg, colb, gradv); + for (x = 1; gradv>0.5; x++) { + g->addpixel(nx+x, ny, colr, colg, colb, gradv); + g->addpixel(nx-x, ny, colr, colg, colb, gradv); + g->addpixel(nx, ny+x, colr, colg, colb, gradv); + g->addpixel(nx, ny-x, colr, colg, colb, gradv); + gradv = gradv/1.01f; + } +#endif + } + if (pixel_mode & EFFECT_GRAVIN) + { + int nxo = 0; + int nyo = 0; + int r; + int fire_rv = 0; + float drad = 0.0f; + float ddist = 0.0f; + sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); + for (r = 0; r < 4; r++) { + ddist = ((float)orbd[r])/16.0f; + drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f; + nxo = ddist*cos(drad); + nyo = ddist*sin(drad); + if (ny+nyo>0 && ny+nyo<YRES && nx+nxo>0 && nx+nxo<XRES) + g->addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]); + } + } + if (pixel_mode & EFFECT_GRAVOUT) + { + int nxo = 0; + int nyo = 0; + int r; + int fire_bv = 0; + float drad = 0.0f; + float ddist = 0.0f; + sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); + for (r = 0; r < 4; r++) { + ddist = ((float)orbd[r])/16.0f; + drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f; + nxo = ddist*cos(drad); + nyo = ddist*sin(drad); + if (ny+nyo>0 && ny+nyo<YRES && nx+nxo>0 && nx+nxo<XRES) + g->addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]); + } + } + //Fire effects + if(firea && (pixel_mode & FIRE_BLEND)) + { +#ifdef OGLR + smokeV[csmokeV++] = nx; + smokeV[csmokeV++] = ny; + smokeC[csmokeC++] = ((float)firer)/255.0f; + smokeC[csmokeC++] = ((float)fireg)/255.0f; + smokeC[csmokeC++] = ((float)fireb)/255.0f; + smokeC[csmokeC++] = ((float)firea)/255.0f; + csmoke++; +#else + firea /= 2; + fire_r[ny/CELL][nx/CELL] = (firea*firer + (255-firea)*fire_r[ny/CELL][nx/CELL]) >> 8; + fire_g[ny/CELL][nx/CELL] = (firea*fireg + (255-firea)*fire_g[ny/CELL][nx/CELL]) >> 8; + fire_b[ny/CELL][nx/CELL] = (firea*fireb + (255-firea)*fire_b[ny/CELL][nx/CELL]) >> 8; +#endif + } + if(firea && (pixel_mode & FIRE_ADD)) + { +#ifdef OGLR + fireV[cfireV++] = nx; + fireV[cfireV++] = ny; + fireC[cfireC++] = ((float)firer)/255.0f; + fireC[cfireC++] = ((float)fireg)/255.0f; + fireC[cfireC++] = ((float)fireb)/255.0f; + fireC[cfireC++] = ((float)firea)/255.0f; + cfire++; +#else + firea /= 8; + firer = ((firea*firer) >> 8) + fire_r[ny/CELL][nx/CELL]; + fireg = ((firea*fireg) >> 8) + fire_g[ny/CELL][nx/CELL]; + fireb = ((firea*fireb) >> 8) + fire_b[ny/CELL][nx/CELL]; + + if(firer>255) + firer = 255; + if(fireg>255) + fireg = 255; + if(fireb>255) + fireb = 255; + + fire_r[ny/CELL][nx/CELL] = firer; + fire_g[ny/CELL][nx/CELL] = fireg; + fire_b[ny/CELL][nx/CELL] = fireb; +#endif + } + } + } + } +#ifdef OGLR + + //Go into array mode + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(cflat) + { + // -- BEGIN FLAT -- // + //Set point size (size of fire texture) + glPointSize(1.0f); + + glColorPointer(4, GL_FLOAT, 0, &flatC[0]); + glVertexPointer(2, GL_INT, 0, &flatV[0]); + + glDrawArrays(GL_POINTS, 0, cflat); + + //Clear some stuff we set + // -- END FLAT -- // + } + + if(cblob) + { + // -- BEGIN BLOB -- // + glEnable( GL_POINT_SMOOTH ); //Blobs! + glPointSize(2.5f); + + glColorPointer(4, GL_FLOAT, 0, &blobC[0]); + glVertexPointer(2, GL_INT, 0, &blobV[0]); + + glDrawArrays(GL_POINTS, 0, cblob); + + //Clear some stuff we set + glDisable( GL_POINT_SMOOTH ); + // -- END BLOB -- // + } + + if(cglow || cblur) + { + // -- BEGIN GLOW -- // + //Start and prepare fire program + glEnable(GL_TEXTURE_2D); + glUseProgram(fireProg); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, glowAlpha); + glUniform1i(glGetUniformLocation(fireProg, "fireAlpha"), 0); + + glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); + + //Make sure we can use texture coords on points + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + + //Set point size (size of fire texture) + glPointSize(11.0f); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + if(cglow) + { + glColorPointer(4, GL_FLOAT, 0, &glowC[0]); + glVertexPointer(2, GL_INT, 0, &glowV[0]); + + glDrawArrays(GL_POINTS, 0, cglow); + } + + glPointSize(7.0f); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(cblur) + { + glBindTexture(GL_TEXTURE_2D, blurAlpha); + + glColorPointer(4, GL_FLOAT, 0, &blurC[0]); + glVertexPointer(2, GL_INT, 0, &blurV[0]); + + glDrawArrays(GL_POINTS, 0, cblur); + } + + //Clear some stuff we set + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + // -- END GLOW -- // + } + + if(cadd) + { + // -- BEGIN ADD -- // + //Set point size (size of fire texture) + glPointSize(1.0f); + + glColorPointer(4, GL_FLOAT, 0, &addC[0]); + glVertexPointer(2, GL_INT, 0, &addV[0]); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDrawArrays(GL_POINTS, 0, cadd); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //Clear some stuff we set + // -- END ADD -- // + } + + if(cline) + { + // -- BEGIN LINES -- // + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glEnable( GL_LINE_SMOOTH ); + glColorPointer(4, GL_FLOAT, 0, &lineC[0]); + glVertexPointer(2, GL_FLOAT, 0, &lineV[0]); + + glDrawArrays(GL_LINE_STRIP, 0, cline); + + //Clear some stuff we set + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_LINE_SMOOTH); + // -- END LINES -- // + } + + if(cfire || csmoke) + { + // -- BEGIN FIRE -- // + //Start and prepare fire program + glEnable(GL_TEXTURE_2D); + glUseProgram(fireProg); + //glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fireAlpha); + glUniform1i(glGetUniformLocation(fireProg, "fireAlpha"), 0); + + //Make sure we can use texture coords on points + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + + //Set point size (size of fire texture) + glPointSize(CELL*3); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + if(cfire) + { + glColorPointer(4, GL_FLOAT, 0, &fireC[0]); + glVertexPointer(2, GL_INT, 0, &fireV[0]); + + glDrawArrays(GL_POINTS, 0, cfire); + } + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(csmoke) + { + glColorPointer(4, GL_FLOAT, 0, &smokeC[0]); + glVertexPointer(2, GL_INT, 0, &smokeV[0]); + + glDrawArrays(GL_POINTS, 0, csmoke); + } + + //Clear some stuff we set + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + // -- END FIRE -- // + } + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + //Reset FBO + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + //Drawing the FBO onto the screen sounds like a cool idea now + + glBlendFunc(origBlendSrc, origBlendDst); +#endif +} + +void Renderer::prepare_graphicscache() +{ + graphicscache = (gcache_item *)malloc(sizeof(gcache_item)*PT_NUM); + memset(graphicscache, 0, sizeof(gcache_item)*PT_NUM); +} + +void Renderer::draw_other() // EMP effect +{ + int i, j; + //if (emp_decor>0 && !sys_pause) emp_decor-=emp_decor/25+2; TODO: Render should render only, do not change simulation state + if (emp_decor>40) emp_decor=40; + if (emp_decor<0) emp_decor = 0; + if (!(display_mode & DISPLAY_EFFE)) // no in nothing mode + return; + if (emp_decor>0) + { +#ifdef OGLR + float femp_decor = ((float)emp_decor)/255.0f; + /*int r=emp_decor*2.5, g=100+emp_decor*1.5, b=255; + int a=(1.0*emp_decor/110)*255; + if (r>255) r=255; + if (g>255) g=255; + if (b>255) g=255; + if (a>255) a=255;*/ + glBegin(GL_QUADS); + glColor4f(femp_decor*2.5f, 0.4f+femp_decor*1.5f, 1.0f+femp_decor*1.5f, femp_decor/0.44f); + glVertex2f(0, MENUSIZE); + glVertex2f(XRES, MENUSIZE); + glVertex2f(XRES, YRES+MENUSIZE); + glVertex2f(0, YRES+MENUSIZE); + glEnd(); +#else + int r=emp_decor*2.5, g=100+emp_decor*1.5, b=255; + int a=(1.0*emp_decor/110)*255; + if (r>255) r=255; + if (g>255) g=255; + if (b>255) g=255; + if (a>255) a=255; + for (j=0; j<YRES; j++) + for (i=0; i<XRES; i++) + { + this->g->drawpixel(i, j, r, g, b, a); + } +#endif + } +} + +void Renderer::draw_grav() +{ + int x, y, i, ca; + float nx, ny, dist; + + for (y=0; y<YRES/CELL; y++) + { + for (x=0; x<XRES/CELL; x++) + { + ca = y*(XRES/CELL)+x; + if(fabsf(sim->gravx[ca]) <= 0.001f && fabsf(sim->gravy[ca]) <= 0.001f) + continue; + nx = x*CELL; + ny = y*CELL; + dist = fabsf(sim->gravy[ca])+fabsf(sim->gravx[ca]); + for(i = 0; i < 4; i++) + { + nx -= sim->gravx[ca]*0.5f; + ny -= sim->gravy[ca]*0.5f; + g->addpixel((int)(nx+0.5f), (int)(ny+0.5f), 255, 255, 255, (int)(dist*20.0f)); + } + } + } +} + +void Renderer::draw_air() +{ +#ifndef OGLR + int x, y, i, j; + float (*pv)[XRES/CELL] = sim->air->pv; + float (*hv)[XRES/CELL] = sim->air->hv; + float (*vx)[XRES/CELL] = sim->air->vx; + float (*vy)[XRES/CELL] = sim->air->vy; + pixel c; + for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + { + if (display_mode & DISPLAY_AIRP) + { + if (pv[y][x] > 0.0f) + c = PIXRGB(clamp_flt(pv[y][x], 0.0f, 8.0f), 0, 0);//positive pressure is red! + else + c = PIXRGB(0, 0, clamp_flt(-pv[y][x], 0.0f, 8.0f));//negative pressure is blue! + } + else if (display_mode & DISPLAY_AIRV) + { + c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red + clamp_flt(pv[y][x], 0.0f, 8.0f),//pressure adds green + clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue + } + else if (display_mode & DISPLAY_AIRH) + { + float ttemp = hv[y][x]+(-MIN_TEMP); + int caddress = restrict_flt((int)( restrict_flt(ttemp, 0.0f, MAX_TEMP+(-MIN_TEMP)) / ((MAX_TEMP+(-MIN_TEMP))/1024) ) *3, 0.0f, (1024.0f*3)-3); + c = PIXRGB((int)((unsigned char)color_data[caddress]*0.7f), (int)((unsigned char)color_data[caddress+1]*0.7f), (int)((unsigned char)color_data[caddress+2]*0.7f)); + //c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red + // clamp_flt(hv[y][x], 0.0f, 1600.0f),//heat adds green + // clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue + } + else if (display_mode & DISPLAY_AIRC) + { + int r; + int g; + int b; + // velocity adds grey + r = clamp_flt(fabsf(vx[y][x]), 0.0f, 24.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 20.0f); + g = clamp_flt(fabsf(vx[y][x]), 0.0f, 20.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 24.0f); + b = clamp_flt(fabsf(vx[y][x]), 0.0f, 24.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 20.0f); + if (pv[y][x] > 0.0f) + { + r += clamp_flt(pv[y][x], 0.0f, 16.0f);//pressure adds red! + if (r>255) + r=255; + if (g>255) + g=255; + if (b>255) + b=255; + c = PIXRGB(r, g, b); + } + else + { + b += clamp_flt(-pv[y][x], 0.0f, 16.0f);//pressure adds blue! + if (r>255) + r=255; + if (g>255) + g=255; + if (b>255) + b=255; + c = PIXRGB(r, g, b); + } + } + for (j=0; j<CELL; j++)//draws the colors + for (i=0; i<CELL; i++) + g->vid[(x*CELL+i) + (y*CELL+j)*(XRES+BARSIZE)] = c; + } +#else + GLuint airProg; + if(display_mode & DISPLAY_AIRC) + { + airProg = airProg_Cracker; + } + else if(display_mode & DISPLAY_AIRV) + { + airProg = airProg_Velocity; + } + else if(display_mode & DISPLAY_AIRP) + { + airProg = airProg_Pressure; + } + else + { + return; + } + + glEnable( GL_TEXTURE_2D ); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo); + + glUseProgram(airProg); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, airVX); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_RED, GL_FLOAT, vx); + glUniform1i(glGetUniformLocation(airProg, "airX"), 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, airVY); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_GREEN, GL_FLOAT, vy); + glUniform1i(glGetUniformLocation(airProg, "airY"), 1); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, airPV); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_BLUE, GL_FLOAT, pv); + glUniform1i(glGetUniformLocation(airProg, "airP"), 2); + glActiveTexture(GL_TEXTURE0); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2d(1, 1); + glVertex3f(XRES*sdl_scale, YRES*sdl_scale, 1.0); + glTexCoord2d(0, 1); + glVertex3f(0, YRES*sdl_scale, 1.0); + glTexCoord2d(0, 0); + glVertex3f(0, 0, 1.0); + glTexCoord2d(1, 0); + glVertex3f(XRES*sdl_scale, 0, 1.0); + glEnd(); + + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDisable( GL_TEXTURE_2D ); +#endif +} + +void Renderer::draw_grav_zones() +{ + int x, y, i, j; + for (y=0; y<YRES/CELL; y++) + { + for (x=0; x<XRES/CELL; x++) + { + //if(sim->gravmask[y*(XRES/CELL)+x]) + { + for (j=0; j<CELL; j++)//draws the colors + for (i=0; i<CELL; i++) + if(i == j) + g->drawpixel(x*CELL+i, y*CELL+j, 255, 200, 0, 120); + else + g->drawpixel(x*CELL+i, y*CELL+j, 32, 32, 32, 120); + } + } + } +} + +void Renderer::init_display_modes() +{ + int i; + colour_mode = COLOUR_DEFAULT; + display_modes = (unsigned int*)calloc(1, sizeof(unsigned int)); + render_modes = (unsigned int*)calloc(2, sizeof(unsigned int)); + + display_modes[0] = 0; + render_modes[0] = RENDER_FIRE; + render_modes[1] = 0; + + display_mode = 0; + i = 0; + while(display_modes[i]) + { + display_mode |= display_modes[i]; + i++; + } + render_mode = 0; + i = 0; + while(render_modes[i]) + { + render_mode |= render_modes[i]; + i++; + } +} + +Renderer::Renderer(Graphics * g, Simulation * sim) +{ + this->g = g; + this->sim = sim; + + prepare_alpha(CELL, 1.0f); + init_display_modes(); + prepare_graphicscache(); + + int flm_data_points = 4; + pixel flm_data_colours[] = {PIXPACK(0xAF9F0F), PIXPACK(0xDFBF6F), PIXPACK(0x60300F), PIXPACK(0x000000)}; + float flm_data_pos[] = {1.0f, 0.9f, 0.5f, 0.0f}; + + int plasma_data_points = 5; + pixel plasma_data_colours[] = {PIXPACK(0xAFFFFF), PIXPACK(0xAFFFFF), PIXPACK(0x301060), PIXPACK(0x301040), PIXPACK(0x000000)}; + float plasma_data_pos[] = {1.0f, 0.9f, 0.5f, 0.25, 0.0f}; + + flm_data = Graphics::generate_gradient(flm_data_colours, flm_data_pos, flm_data_points, 200); + plasma_data = Graphics::generate_gradient(plasma_data_colours, plasma_data_pos, plasma_data_points, 200); +} diff --git a/src/Simulation.cpp b/src/Simulation.cpp new file mode 100644 index 0000000..7ec5c4e --- /dev/null +++ b/src/Simulation.cpp @@ -0,0 +1,3591 @@ +#include <cstdlib> +#include <math.h> +#define _cplusplus +#include "Config.h" +#include "Simulation.h" +#include "Elements.h" +#include "ElementFunctions.h" +#include "Air.h" +#include "Gravity.h" +//#include "powder.h" + +void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h) +{ + int cx = 0; + int cy = 0; + for (cy=0; cy<area_h; cy++) + { + for (cx=0; cx<area_w; cx++) + { + bmap[(cy+area_y)/CELL][(cx+area_x)/CELL] = 0; + delete_part(cx+area_x, cy+area_y, 0); + } + } +} + +void Simulation::create_box(int x1, int y1, int x2, int y2, int c, int flags) +{ + int i, j; + if (c==SPC_PROP) + return; + if (x1>x2) + { + i = x2; + x2 = x1; + x1 = i; + } + if (y1>y2) + { + j = y2; + y2 = y1; + y1 = j; + } + for (j=y1; j<=y2; j++) + for (i=x1; i<=x2; i++) + create_parts(i, j, 0, 0, c, flags); +} + +int Simulation::flood_prop_2(int x, int y, size_t propoffset, void * propvalue, int proptype, int parttype, char * bitmap) +{ + int x1, x2, i, dy = 1; + x1 = x2 = x; + while (x1>=CELL) + { + if ((pmap[y][x1-1]&0xFF)!=parttype || bitmap[(y*XRES)+x1-1]) + { + break; + } + x1--; + } + while (x2<XRES-CELL) + { + if ((pmap[y][x2+1]&0xFF)!=parttype || bitmap[(y*XRES)+x2+1]) + { + break; + } + x2++; + } + for (x=x1; x<=x2; x++) + { + i = pmap[y][x]>>8; + if(proptype==2){ + *((float*)(((char*)&parts[i])+propoffset)) = *((float*)propvalue); + } else if(proptype==0) { + *((int*)(((char*)&parts[i])+propoffset)) = *((int*)propvalue); + } else if(proptype==1) { + *((char*)(((char*)&parts[i])+propoffset)) = *((char*)propvalue); + } + bitmap[(y*XRES)+x] = 1; + } + if (y>=CELL+dy) + for (x=x1; x<=x2; x++) + if ((pmap[y-dy][x]&0xFF)==parttype && !bitmap[((y-dy)*XRES)+x]) + if (!flood_prop_2(x, y-dy, propoffset, propvalue, proptype, parttype, bitmap)) + return 0; + if (y<YRES-CELL-dy) + for (x=x1; x<=x2; x++) + if ((pmap[y+dy][x]&0xFF)==parttype && !bitmap[((y+dy)*XRES)+x]) + if (!flood_prop_2(x, y+dy, propoffset, propvalue, proptype, parttype, bitmap)) + return 0; + return 1; +} + +int Simulation::flood_prop(int x, int y, size_t propoffset, void * propvalue, int proptype) +{ + int r = 0; + char * bitmap = (char *)malloc(XRES*YRES); //Bitmap for checking + memset(bitmap, 0, XRES*YRES); + r = pmap[y][x]; + flood_prop_2(x, y, propoffset, propvalue, proptype, r&0xFF, bitmap); + free(bitmap); + return 0; +} + +int Simulation::flood_parts(int x, int y, int fullc, int cm, int bm, int flags) +{ + int c = fullc&0xFF; + int x1, x2, dy = (c<PT_NUM)?1:CELL; + int co = c; + if (c==SPC_PROP) + return 0; + if (cm==PT_INST&&co==PT_SPRK) + if ((pmap[y][x]&0xFF)==PT_SPRK) + return 0; + if (cm==-1) + { + if (c==0) + { + cm = pmap[y][x]&0xFF; + if (!cm) + return 0; + //if ((flags&BRUSH_REPLACEMODE) && cm!=SLALT) + // return 0; + } + else + cm = 0; + } + if (bm==-1) + { + if (c-UI_WALLSTART+UI_ACTUALSTART==WL_ERASE) + { + bm = bmap[y/CELL][x/CELL]; + if (!bm) + return 0; + if (bm==WL_WALL) + cm = 0xFF; + } + else + bm = 0; + } + + if (((pmap[y][x]&0xFF)!=cm || bmap[y/CELL][x/CELL]!=bm )/*||( (flags&BRUSH_SPECIFIC_DELETE) && cm!=SLALT)*/) + return 1; + + // go left as far as possible + x1 = x2 = x; + while (x1>=CELL) + { + if ((pmap[y][x1-1]&0xFF)!=cm || bmap[y/CELL][(x1-1)/CELL]!=bm) + { + break; + } + x1--; + } + while (x2<XRES-CELL) + { + if ((pmap[y][x2+1]&0xFF)!=cm || bmap[y/CELL][(x2+1)/CELL]!=bm) + { + break; + } + x2++; + } + + // fill span + for (x=x1; x<=x2; x++) + { + if (cm==PT_INST&&co==PT_SPRK) + { + if (create_part(-1,x, y, fullc)==-1) + return 0; + } + else if (!create_parts(x, y, 0, 0, fullc, flags)) + return 0; + } + // fill children + if (cm==PT_INST&&co==PT_SPRK)//wire crossing for INST + { + if (y>=CELL+dy && x1==x2 && + ((pmap[y-1][x1-1]&0xFF)==PT_INST||(pmap[y-1][x1-1]&0xFF)==PT_SPRK) && ((pmap[y-1][x1]&0xFF)==PT_INST||(pmap[y-1][x1]&0xFF)==PT_SPRK) && ((pmap[y-1][x1+1]&0xFF)==PT_INST || (pmap[y-1][x1+1]&0xFF)==PT_SPRK) && + (pmap[y-2][x1-1]&0xFF)!=PT_INST && ((pmap[y-2][x1]&0xFF)==PT_INST ||(pmap[y-2][x1]&0xFF)==PT_SPRK) && (pmap[y-2][x1+1]&0xFF)!=PT_INST) + flood_parts(x1, y-2, fullc, cm, bm, flags); + else if (y>=CELL+dy) + for (x=x1; x<=x2; x++) + if ((pmap[y-1][x]&0xFF)!=PT_SPRK) + { + if (x==x1 || x==x2 || y>=YRES-CELL-1 || + (pmap[y-1][x-1]&0xFF)==PT_INST || (pmap[y-1][x+1]&0xFF)==PT_INST || + (pmap[y+1][x-1]&0xFF)==PT_INST || ((pmap[y+1][x]&0xFF)!=PT_INST&&(pmap[y+1][x]&0xFF)!=PT_SPRK) || (pmap[y+1][x+1]&0xFF)==PT_INST) + flood_parts(x, y-dy, fullc, cm, bm, flags); + + } + + if (y<YRES-CELL-dy && x1==x2 && + ((pmap[y+1][x1-1]&0xFF)==PT_INST||(pmap[y+1][x1-1]&0xFF)==PT_SPRK) && ((pmap[y+1][x1]&0xFF)==PT_INST||(pmap[y+1][x1]&0xFF)==PT_SPRK) && ((pmap[y+1][x1+1]&0xFF)==PT_INST || (pmap[y+1][x1+1]&0xFF)==PT_SPRK) && + (pmap[y+2][x1-1]&0xFF)!=PT_INST && ((pmap[y+2][x1]&0xFF)==PT_INST ||(pmap[y+2][x1]&0xFF)==PT_SPRK) && (pmap[y+2][x1+1]&0xFF)!=PT_INST) + flood_parts(x1, y+2, fullc, cm, bm, flags); + else if (y<YRES-CELL-dy) + for (x=x1; x<=x2; x++) + if ((pmap[y+1][x]&0xFF)!=PT_SPRK) + { + if (x==x1 || x==x2 || y<0 || + (pmap[y+1][x-1]&0xFF)==PT_INST || (pmap[y+1][x+1]&0xFF)==PT_INST || + (pmap[y-1][x-1]&0xFF)==PT_INST || ((pmap[y-1][x]&0xFF)!=PT_INST&&(pmap[y-1][x]&0xFF)!=PT_SPRK) || (pmap[y-1][x+1]&0xFF)==PT_INST) + flood_parts(x, y+dy, fullc, cm, bm, flags); + + } + } + else + { + if (y>=CELL+dy) + for (x=x1; x<=x2; x++) + if ((pmap[y-dy][x]&0xFF)==cm && bmap[(y-dy)/CELL][x/CELL]==bm) + if (!flood_parts(x, y-dy, fullc, cm, bm, flags)) + return 0; + if (y<YRES-CELL-dy) + for (x=x1; x<=x2; x++) + if ((pmap[y+dy][x]&0xFF)==cm && bmap[(y+dy)/CELL][x/CELL]==bm) + if (!flood_parts(x, y+dy, fullc, cm, bm, flags)) + return 0; + } + if (!(cm==PT_INST&&co==PT_SPRK)) + return 1; + return 0; +} + +int Simulation::flood_water(int x, int y, int i, int originaly, int check) +{ + int x1 = 0,x2 = 0; + // go left as far as possible + x1 = x2 = x; + if (!pmap[y][x]) + return 1; + + while (x1>=CELL) + { + if ((ptypes[(pmap[y][x1-1]&0xFF)].falldown)!=2) + { + break; + } + x1--; + } + while (x2<XRES-CELL) + { + if ((ptypes[(pmap[y][x2+1]&0xFF)].falldown)!=2) + { + break; + } + x2++; + } + + // fill span + for (x=x1; x<=x2; x++) + { + parts[pmap[y][x]>>8].tmp2 = !check;//flag it as checked, maybe shouldn't use .tmp2 + //check above, maybe around other sides too? + if ( ((y-1) > originaly) && !pmap[y-1][x] && eval_move(parts[i].type, x, y-1, NULL)) + { + int oldx = (int)(parts[i].x + 0.5f); + int oldy = (int)(parts[i].y + 0.5f); + pmap[y-1][x] = pmap[oldy][oldx]; + pmap[oldy][oldx] = 0; + parts[i].x = x; + parts[i].y = y-1; + return 0; + } + } + // fill children + + if (y>=CELL+1) + for (x=x1; x<=x2; x++) + if ((ptypes[(pmap[y-1][x]&0xFF)].falldown)==2 && parts[pmap[y-1][x]>>8].tmp2 == check) + if (!flood_water(x, y-1, i, originaly, check)) + return 0; + if (y<YRES-CELL-1) + for (x=x1; x<=x2; x++) + if ((ptypes[(pmap[y+1][x]&0xFF)].falldown)==2 && parts[pmap[y+1][x]>>8].tmp2 == check) + if (!flood_water(x, y+1, i, originaly, check)) + return 0; + return 1; +} + +//wrapper around create_part to create TESC with correct tmp value +int Simulation::create_part_add_props(int p, int x, int y, int tv, int rx, int ry) +{ + p=create_part(p, x, y, tv); + if (tv==PT_TESC) + { + parts[p].tmp=rx*4+ry*4+7; + if (parts[p].tmp>300) + parts[p].tmp=300; + } + return p; +} + +//this creates particles from a brush, don't use if you want to create one particle +int Simulation::create_parts(int x, int y, int rx, int ry, int c, int flags) +{ + int i, j, r, f = 0, u, v, oy, ox, b = 0, dw = 0, stemp = 0, p;//n; + + int wall = c - 100; + if (c==SPC_WIND || c==PT_FIGH) + return 0; + + //if(c==SPC_PROP){ + // prop_edit_ui(vid_buf, x, y); + // return 0; + //} + for (r=UI_ACTUALSTART; r<=UI_ACTUALSTART+UI_WALLCOUNT; r++) + { + if (wall==r) + { + if (c == SPC_AIR || c == SPC_HEAT || c == SPC_COOL || c == SPC_VACUUM || c == SPC_PGRV || c == SPC_NGRV || wall == WL_SIGN) + break; + if (wall == WL_ERASE) + b = 0; + else + b = wall; + dw = 1; + } + } + if (c == WL_FANHELPER) + { + b = WL_FANHELPER; + dw = 1; + } + if (wall == WL_GRAV) + { + gravwl_timeout = 60; + } + if (c==PT_LIGH) + { + if (lighting_recreate>0 && rx+ry>0) + return 0; + p=create_part(-2, x, y, c); + if (p!=-1) + { + parts[p].life=rx+ry; + if (parts[p].life>55) + parts[p].life=55; + parts[p].temp=parts[p].life*150; // temperature of the lighting shows the power of the lighting + lighting_recreate+=parts[p].life/2+1; + return 1; + } + else return 0; + } + + if (dw==1) + { + ry = ry/CELL; + rx = rx/CELL; + x = x/CELL; + y = y/CELL; + x -= rx/2; + y -= ry/2; + for (ox=x; ox<=x+rx; ox++) + { + for (oy=y; oy<=y+rx; oy++) + { + if (ox>=0&&ox<XRES/CELL&&oy>=0&&oy<YRES/CELL) + { + i = ox; + j = oy; + /*if ((flags&BRUSH_SPECIFIC_DELETE) && b!=WL_FANHELPER) + { + if (bmap[j][i]==SLALT-100) + { + b = 0; + if (SLALT==WL_GRAV) gravwl_timeout = 60; + } + else + continue; + }*/ + if (b==WL_FAN) + { + fvx[j][i] = 0.0f; + fvy[j][i] = 0.0f; + } + if (b==WL_STREAM) + { + i = x + rx/2; + j = y + ry/2; + for (v=-1; v<2; v++) + for (u=-1; u<2; u++) + if (i+u>=0 && i+u<XRES/CELL && + j+v>=0 && j+v<YRES/CELL && + bmap[j+v][i+u] == WL_STREAM) + return 1; + bmap[j][i] = WL_STREAM; + continue; + } + if (b==0 && bmap[j][i]==WL_GRAV) gravwl_timeout = 60; + bmap[j][i] = b; + } + } + } + return 1; + } + + //eraser + if (c == 0/* && !(flags&BRUSH_REPLACEMODE)*/) + { + if (rx==0&&ry==0) + { + delete_part(x, y, 0); + } + else + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=rx; i++) + //if (InCurrentBrush(i ,j ,rx ,ry)) + delete_part(x+i, y+j, 0); + return 1; + } + + //specific deletion + /*if ((flags&BRUSH_SPECIFIC_DELETE)&& !(flags&BRUSH_REPLACEMODE)) + { + if (rx==0&&ry==0) + { + delete_part(x, y, flags); + } + else + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=rx; i++) + if (InCurrentBrush(i ,j ,rx ,ry)) + delete_part(x+i, y+j, flags); + return 1; + }*/ + + //why do these need a special if + if (c == SPC_AIR || c == SPC_HEAT || c == SPC_COOL || c == SPC_VACUUM || c == SPC_PGRV || c == SPC_NGRV) + { + if (rx==0&&ry==0) + { + create_part(-2, x, y, c); + } + else + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=rx; i++) + //if (InCurrentBrush(i ,j ,rx ,ry)) + { + if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES) + continue; + //if (!REPLACE_MODE) + create_part(-2, x+i, y+j, c); + /*else if ((pmap[y+j][x+i]&0xFF)==SLALT&&SLALT!=0) + create_part(-2, x+i, y+j, c);*/ + } + return 1; + } + + /*if (flags&BRUSH_REPLACEMODE) + { + if (rx==0&&ry==0) + { + if ((pmap[y][x]&0xFF)==SLALT || SLALT==0) + { + if ((pmap[y][x])) + { + delete_part(x, y, 0); + if (c!=0) + create_part_add_props(-2, x, y, c, rx, ry); + } + } + } + else + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=rx; i++) + if (InCurrentBrush(i ,j ,rx ,ry)) + { + if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES) + continue; + if ((pmap[y+j][x+i]&0xFF)!=SLALT&&SLALT!=0) + continue; + if ((pmap[y+j][x+i])) + { + delete_part(x+i, y+j, 0); + if (c!=0) + create_part_add_props(-2, x+i, y+j, c, rx, ry); + } + } + return 1; + + }*/ + //else, no special modes, draw element like normal. + if (rx==0&&ry==0)//workaround for 1pixel brush/floodfill crashing. todo: find a better fix later. + { + if (create_part_add_props(-2, x, y, c, rx, ry)==-1) + f = 1; + } + else + for (j=-ry; j<=ry; j++) + for (i=-rx; i<=rx; i++) + //if (InCurrentBrush(i ,j ,rx ,ry)) + if (create_part_add_props(-2, x+i, y+j, c, rx, ry)==-1) + f = 1; + return !f; +} +/*int Simulation::InCurrentBrush(int i, int j, int rx, int ry) +{ + switch(CURRENT_BRUSH) + { + case CIRCLE_BRUSH: + return (pow(i,2)*pow(ry,2)+pow(j,2)*pow(rx,2)<=pow(rx,2)*pow(ry,2)); + break; + case SQUARE_BRUSH: + return (i*j<=ry*rx); + break; + case TRI_BRUSH: + return (j <= ry ) && ( j >= (((-2.0*ry)/rx)*i) -ry) && ( j >= (((-2.0*ry)/(-rx))*i)-ry ) ; + break; + } + return 0; +} +int Simulation::get_brush_flags() +{ + int flags = 0; + if (REPLACE_MODE) + flags |= BRUSH_REPLACEMODE; + if (sdl_mod & KMOD_CAPS) + flags |= BRUSH_SPECIFIC_DELETE; + if ((sdl_mod & KMOD_LALT) && (sdl_mod & (KMOD_CTRL))) + flags |= BRUSH_SPECIFIC_DELETE; + return flags; +}*/ +void Simulation::create_line(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags) +{ + int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy; + float e, de; + if (c==SPC_PROP) + return; + if (cp) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + e = 0.0f; + if (dx) + de = dy/(float)dx; + else + de = 0.0f; + y = y1; + sy = (y1<y2) ? 1 : -1; + for (x=x1; x<=x2; x++) + { + if (cp) + create_parts(y, x, rx, ry, c, flags); + else + create_parts(x, y, rx, ry, c, flags); + e += de; + if (e >= 0.5f) + { + y += sy; + if ((c==WL_EHOLE+100 || c==WL_ALLOWGAS+100 || c==WL_ALLOWENERGY+100 || c==WL_ALLOWALLELEC+100 || c==WL_ALLOWSOLID+100 || c==WL_ALLOWAIR+100 || c==WL_WALL+100 || c==WL_DESTROYALL+100 || c==WL_ALLOWLIQUID+100 || c==WL_FAN+100 || c==WL_STREAM+100 || c==WL_DETECT+100 || c==WL_EWALL+100 || c==WL_WALLELEC+100 || !(rx+ry)) + && ((y1<y2) ? (y<=y2) : (y>=y2))) + { + if (cp) + create_parts(y, x, rx, ry, c, flags); + else + create_parts(x, y, rx, ry, c, flags); + } + e -= 1.0f; + } + } +} + +void *Simulation::transform_save(void *odata, int *size, matrix2d transform, vector2d translate) +{ + void *ndata; + unsigned char (*bmapo)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char)); + unsigned char (*bmapn)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char)); + Particle *partst = (Particle *)calloc(sizeof(Particle), NPART); + sign *signst = (sign *)calloc(MAXSIGNS, sizeof(sign)); + unsigned (*pmapt)[XRES] = (unsigned (*)[XRES])calloc(YRES*XRES, sizeof(unsigned)); + float (*fvxo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); + float (*fvyo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); + float (*fvxn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); + float (*fvyn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); + int i, x, y, nx, ny, w, h, nw, nh; + vector2d pos, tmp, ctl, cbr; + vector2d cornerso[4]; + unsigned char *odatac = (unsigned char *)odata; + //*if (parse_save(odata, *size, 0, 0, 0, bmapo, fvxo, fvyo, signst, partst, pmapt)) TODO: IMPLEMENT + { + free(bmapo); + free(bmapn); + free(partst); + free(signst); + free(pmapt); + free(fvxo); + free(fvyo); + free(fvxn); + free(fvyn); + return odata; + } + w = odatac[6]*CELL; + h = odatac[7]*CELL; + // undo any translation caused by rotation + cornerso[0] = v2d_new(0,0); + cornerso[1] = v2d_new(w-1,0); + cornerso[2] = v2d_new(0,h-1); + cornerso[3] = v2d_new(w-1,h-1); + for (i=0; i<4; i++) + { + tmp = m2d_multiply_v2d(transform,cornerso[i]); + if (i==0) ctl = cbr = tmp; // top left, bottom right corner + if (tmp.x<ctl.x) ctl.x = tmp.x; + if (tmp.y<ctl.y) ctl.y = tmp.y; + if (tmp.x>cbr.x) cbr.x = tmp.x; + if (tmp.y>cbr.y) cbr.y = tmp.y; + } + // casting as int doesn't quite do what we want with negative numbers, so use floor() + tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f)); + translate = v2d_sub(translate,tmp); + nw = floor(cbr.x+0.5f)-floor(ctl.x+0.5f)+1; + nh = floor(cbr.y+0.5f)-floor(ctl.y+0.5f)+1; + if (nw>XRES) nw = XRES; + if (nh>YRES) nh = YRES; + // rotate and translate signs, parts, walls + for (i=0; i<MAXSIGNS; i++) + { + if (!signst[i].text[0]) continue; + pos = v2d_new(signst[i].x, signst[i].y); + pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); + nx = floor(pos.x+0.5f); + ny = floor(pos.y+0.5f); + if (nx<0 || nx>=nw || ny<0 || ny>=nh) + { + signst[i].text[0] = 0; + continue; + } + signst[i].x = nx; + signst[i].y = ny; + } + for (i=0; i<NPART; i++) + { + if (!partst[i].type) continue; + pos = v2d_new(partst[i].x, partst[i].y); + pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); + nx = floor(pos.x+0.5f); + ny = floor(pos.y+0.5f); + if (nx<0 || nx>=nw || ny<0 || ny>=nh) + { + partst[i].type = PT_NONE; + continue; + } + partst[i].x = nx; + partst[i].y = ny; + } + for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + { + pos = v2d_new(x*CELL+CELL*0.4f, y*CELL+CELL*0.4f); + pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); + nx = pos.x/CELL; + ny = pos.y/CELL; + if (nx<0 || nx>=nw || ny<0 || ny>=nh) + continue; + if (bmapo[y][x]) + { + bmapn[ny][nx] = bmapo[y][x]; + if (bmapo[y][x]==WL_FAN) + { + fvxn[ny][nx] = fvxo[y][x]; + fvyn[ny][nx] = fvyo[y][x]; + } + } + } + //ndata = build_save(size,0,0,nw,nh,bmapn,fvxn,fvyn,signst,partst); TODO: IMPLEMENT + free(bmapo); + free(bmapn); + free(partst); + free(signst); + free(pmapt); + free(fvxo); + free(fvyo); + free(fvxn); + free(fvyn); + return ndata; +} + +void Simulation::orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]) +{ + resblock1[0] = (block1&0x000000FF); + resblock1[1] = (block1&0x0000FF00)>>8; + resblock1[2] = (block1&0x00FF0000)>>16; + resblock1[3] = (block1&0xFF000000)>>24; + + resblock2[0] = (block2&0x000000FF); + resblock2[1] = (block2&0x0000FF00)>>8; + resblock2[2] = (block2&0x00FF0000)>>16; + resblock2[3] = (block2&0xFF000000)>>24; +} + +void Simulation::orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[]) +{ + int block1tmp = 0; + int block2tmp = 0; + + block1tmp = (resblock1[0]&0xFF); + block1tmp |= (resblock1[1]&0xFF)<<8; + block1tmp |= (resblock1[2]&0xFF)<<16; + block1tmp |= (resblock1[3]&0xFF)<<24; + + block2tmp = (resblock2[0]&0xFF); + block2tmp |= (resblock2[1]&0xFF)<<8; + block2tmp |= (resblock2[2]&0xFF)<<16; + block2tmp |= (resblock2[3]&0xFF)<<24; + + *block1 = block1tmp; + *block2 = block2tmp; +} + +inline int Simulation::is_wire(int x, int y) +{ + return bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE; +} + +inline int Simulation::is_wire_off(int x, int y) +{ + return (bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE) && emap[y][x]<8; +} + +int Simulation::get_wavelength_bin(int *wm) +{ + int i, w0=30, wM=0; + + if (!*wm) + return -1; + + for (i=0; i<30; i++) + if (*wm & (1<<i)) { + if (i < w0) + w0 = i; + if (i > wM) + wM = i; + } + + if (wM-w0 < 5) + return (wM+w0)/2; + + i = rand() % (wM-w0-3); + i += w0; + + *wm &= 0x1F << i; + return i + 2; +} + +void Simulation::set_emap(int x, int y) +{ + int x1, x2; + + if (!is_wire_off(x, y)) + return; + + // go left as far as possible + x1 = x2 = x; + while (x1>0) + { + if (!is_wire_off(x1-1, y)) + break; + x1--; + } + while (x2<XRES/CELL-1) + { + if (!is_wire_off(x2+1, y)) + break; + x2++; + } + + // fill span + for (x=x1; x<=x2; x++) + emap[y][x] = 16; + + // fill children + + if (y>1 && x1==x2 && + is_wire(x1-1, y-1) && is_wire(x1, y-1) && is_wire(x1+1, y-1) && + !is_wire(x1-1, y-2) && is_wire(x1, y-2) && !is_wire(x1+1, y-2)) + set_emap(x1, y-2); + else if (y>0) + for (x=x1; x<=x2; x++) + if (is_wire_off(x, y-1)) + { + if (x==x1 || x==x2 || y>=YRES/CELL-1 || + is_wire(x-1, y-1) || is_wire(x+1, y-1) || + is_wire(x-1, y+1) || !is_wire(x, y+1) || is_wire(x+1, y+1)) + set_emap(x, y-1); + } + + if (y<YRES/CELL-2 && x1==x2 && + is_wire(x1-1, y+1) && is_wire(x1, y+1) && is_wire(x1+1, y+1) && + !is_wire(x1-1, y+2) && is_wire(x1, y+2) && !is_wire(x1+1, y+2)) + set_emap(x1, y+2); + else if (y<YRES/CELL-1) + for (x=x1; x<=x2; x++) + if (is_wire_off(x, y+1)) + { + if (x==x1 || x==x2 || y<0 || + is_wire(x-1, y+1) || is_wire(x+1, y+1) || + is_wire(x-1, y-1) || !is_wire(x, y-1) || is_wire(x+1, y-1)) + set_emap(x, y+1); + } +} + +int Simulation::parts_avg(int ci, int ni,int t) +{ + if (t==PT_INSL)//to keep electronics working + { + int pmr = pmap[((int)(parts[ci].y+0.5f) + (int)(parts[ni].y+0.5f))/2][((int)(parts[ci].x+0.5f) + (int)(parts[ni].x+0.5f))/2]; + if (pmr) + return parts[pmr>>8].type; + else + return PT_NONE; + } + else + { + int pmr2 = pmap[(int)((parts[ci].y + parts[ni].y)/2+0.5f)][(int)((parts[ci].x + parts[ni].x)/2+0.5f)];//seems to be more accurate. + if (pmr2) + { + if (parts[pmr2>>8].type==t) + return t; + } + else + return PT_NONE; + } + return PT_NONE; +} + + +int Simulation::nearest_part(int ci, int t, int max_d) +{ + int distance = (max_d!=-1)?max_d:MAX_DISTANCE; + int ndistance = 0; + int id = -1; + int i = 0; + int cx = (int)parts[ci].x; + int cy = (int)parts[ci].y; + for (i=0; i<=parts_lastActiveIndex; i++) + { + if ((parts[i].type==t||(t==-1&&parts[i].type))&&!parts[i].life&&i!=ci) + { + ndistance = abs(cx-parts[i].x)+abs(cy-parts[i].y);// Faster but less accurate Older: sqrt(pow(cx-parts[i].x, 2)+pow(cy-parts[i].y, 2)); + if (ndistance<distance) + { + distance = ndistance; + id = i; + } + } + } + return id; +} + +void Simulation::create_arc(int sx, int sy, int dx, int dy, int midpoints, int variance, int type, int flags) +{ + int i; + float xint, yint; + int *xmid, *ymid; + int voffset = variance/2; + xmid = (int *)calloc(midpoints + 2, sizeof(int)); + ymid = (int *)calloc(midpoints + 2, sizeof(int)); + xint = (float)(dx-sx)/(float)(midpoints+1.0f); + yint = (float)(dy-sy)/(float)(midpoints+1.0f); + xmid[0] = sx; + xmid[midpoints+1] = dx; + ymid[0] = sy; + ymid[midpoints+1] = dy; + + for(i = 1; i <= midpoints; i++) + { + ymid[i] = ymid[i-1]+yint; + xmid[i] = xmid[i-1]+xint; + } + + for(i = 0; i <= midpoints; i++) + { + if(i!=midpoints) + { + xmid[i+1] += (rand()%variance)-voffset; + ymid[i+1] += (rand()%variance)-voffset; + } + create_line(xmid[i], ymid[i], xmid[i+1], ymid[i+1], 0, 0, type, flags); + } + free(xmid); + free(ymid); +} + +void Simulation::clear_sim(void) +{ + int i, x, y; + if(signs) + memset(signs, 0, sizeof(signs)); + memset(bmap, 0, sizeof(bmap)); + memset(emap, 0, sizeof(emap)); + memset(parts, 0, sizeof(Particle)*NPART); + for (i=0; i<NPART-1; i++) + parts[i].life = i+1; + parts[NPART-1].life = -1; + pfree = 0; + parts_lastActiveIndex = 0; + memset(pmap, 0, sizeof(pmap)); + if(pv) + memset(pv, 0, sizeof(pv)); + if(vx) + memset(vx, 0, sizeof(vx)); + if(vy) + memset(vy, 0, sizeof(vy)); + if(fvx) + memset(fvx, 0, sizeof(fvx)); + if(fvy) + memset(fvy, 0, sizeof(fvy)); + memset(photons, 0, sizeof(photons)); + memset(wireless, 0, sizeof(wireless)); + memset(gol2, 0, sizeof(gol2)); + memset(portalp, 0, sizeof(portalp)); + memset(fighters, 0, sizeof(fighters)); + fighcount = 0; + player.spwn = 0; + player2.spwn = 0; + //memset(pers_bg, 0, (XRES+BARSIZE)*YRES*PIXELSIZE); + //memset(fire_r, 0, sizeof(fire_r)); + //memset(fire_g, 0, sizeof(fire_g)); + //memset(fire_b, 0, sizeof(fire_b)); + //if(gravmask) + //memset(gravmask, 0xFFFFFFFF, (XRES/CELL)*(YRES/CELL)*sizeof(unsigned)); + if(gravy) + memset(gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + if(gravx) + memset(gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + if(gravp) + memset(gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float)); + if(hv) + for(x = 0; x < XRES/CELL; x++){ + for(y = 0; y < YRES/CELL; y++){ + hv[y][x] = 273.15f+22.0f; //Set to room temperature + } + } +} +void Simulation::init_can_move() +{ + // can_move[moving type][type at destination] + // 0 = No move/Bounce + // 1 = Swap + // 2 = Both particles occupy the same space. + // 3 = Varies, go run some extra checks + int t, rt; + for (rt=0;rt<PT_NUM;rt++) + can_move[0][rt] = 0; // particles that don't exist shouldn't move... + for (t=1;t<PT_NUM;t++) + for (rt=0;rt<PT_NUM;rt++) + can_move[t][rt] = 1; + for (rt=1;rt<PT_NUM;rt++) + { + can_move[PT_PHOT][rt] = 2; + } + for (t=1;t<PT_NUM;t++) + { + for (rt=1;rt<PT_NUM;rt++) + { + // weight check, also prevents particles of same type displacing each other + if (ptypes[t].weight <= ptypes[rt].weight) can_move[t][rt] = 0; + if (t==PT_NEUT && (ptypes[rt].properties&PROP_NEUTPASS)) + can_move[t][rt] = 2; + if (t==PT_NEUT && (ptypes[rt].properties&PROP_NEUTPENETRATE)) + can_move[t][rt] = 1; + if ((ptypes[t].properties&PROP_NEUTPENETRATE) && rt==PT_NEUT) + can_move[t][rt] = 0; + if ((ptypes[t].properties&TYPE_ENERGY) && (ptypes[rt].properties&TYPE_ENERGY)) + can_move[t][rt] = 2; + } + } + can_move[PT_DEST][PT_DMND] = 0; + can_move[PT_BIZR][PT_FILT] = 2; + can_move[PT_BIZRG][PT_FILT] = 2; + for (t=0;t<PT_NUM;t++) + { + //spark shouldn't move + can_move[PT_SPRK][t] = 0; + //all stickman collisions are done in stickman update function + can_move[PT_STKM][t] = 2; + can_move[PT_STKM2][t] = 2; + can_move[PT_FIGH][t] = 2; + } + for (t=0;t<PT_NUM;t++) + { + // make them eat things + can_move[t][PT_VOID] = 1; + can_move[t][PT_BHOL] = 1; + can_move[t][PT_NBHL] = 1; + //all stickman collisions are done in stickman update function + can_move[t][PT_STKM] = 2; + can_move[t][PT_STKM2] = 2; + can_move[PT_FIGH][t] = 2; + //INVIS behaviour varies with pressure + can_move[t][PT_INVIS] = 3; + //stop CNCT being displaced by other particles + can_move[t][PT_CNCT] = 0; + //Powered void behaviour varies on powered state + can_move[t][PT_PVOD] = 3; + } + for (t=0;t<PT_NUM;t++) + { + if (t==PT_GLAS || t==PT_PHOT || t==PT_CLNE || t==PT_PCLN + || t==PT_GLOW || t==PT_WATR || t==PT_DSTW || t==PT_SLTW + || t==PT_ISOZ || t==PT_ISZS || t==PT_FILT || t==PT_INVIS + || t==PT_QRTZ || t==PT_PQRT) + can_move[PT_PHOT][t] = 2; + } + can_move[PT_ELEC][PT_LCRY] = 2; + can_move[PT_PHOT][PT_LCRY] = 3;//varies according to LCRY life + + can_move[PT_PHOT][PT_BIZR] = 2; + can_move[PT_ELEC][PT_BIZR] = 2; + can_move[PT_PHOT][PT_BIZRG] = 2; + can_move[PT_ELEC][PT_BIZRG] = 2; + can_move[PT_PHOT][PT_BIZRS] = 2; + can_move[PT_ELEC][PT_BIZRS] = 2; + + can_move[PT_NEUT][PT_INVIS] = 2; + //whol eats anar + can_move[PT_ANAR][PT_WHOL] = 1; + can_move[PT_ANAR][PT_NWHL] = 1; +} + +/* + RETURN-value explenation +1 = Swap +0 = No move/Bounce +2 = Both particles occupy the same space. + */ +int Simulation::eval_move(int pt, int nx, int ny, unsigned *rr) +{ + unsigned r; + int result; + + if (nx<0 || ny<0 || nx>=XRES || ny>=YRES) + return 0; + + r = pmap[ny][nx]; + if (r) + r = (r&~0xFF) | parts[r>>8].type; + if (rr) + *rr = r; + if (pt>=PT_NUM || (r&0xFF)>=PT_NUM) + return 0; + result = can_move[pt][r&0xFF]; + if (result==3) + { + if ((pt==PT_PHOT || pt==PT_ELEC) && (r&0xFF)==PT_LCRY) + result = (parts[r>>8].life > 5)? 2 : 0; + if ((r&0xFF)==PT_INVIS) + { + if (pv[ny/CELL][nx/CELL]>4.0f || pv[ny/CELL][nx/CELL]<-4.0f) result = 2; + else result = 0; + } + if ((r&0xFF)==PT_PVOD) + { + if (parts[r>>8].life == 10) result = 1; + else result = 0; + } + } + if (bmap[ny/CELL][nx/CELL]) + { + if (bmap[ny/CELL][nx/CELL]==WL_ALLOWGAS && !(ptypes[pt].properties&TYPE_GAS))// && ptypes[pt].falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_ALLOWENERGY && !(ptypes[pt].properties&TYPE_ENERGY))// && ptypes[pt].falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_ALLOWLIQUID && ptypes[pt].falldown!=2) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_ALLOWSOLID && ptypes[pt].falldown!=1) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_ALLOWAIR || bmap[ny/CELL][nx/CELL]==WL_WALL || bmap[ny/CELL][nx/CELL]==WL_WALLELEC) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_EWALL && !emap[ny/CELL][nx/CELL]) + return 0; + if (bmap[ny/CELL][nx/CELL]==WL_EHOLE && !emap[ny/CELL][nx/CELL]) + return 2; + } + return result; +} + +int Simulation::try_move(int i, int x, int y, int nx, int ny) +{ + unsigned r, e; + + if (x==nx && y==ny) + return 1; + if (nx<0 || ny<0 || nx>=XRES || ny>=YRES) + return 1; + + e = eval_move(parts[i].type, nx, ny, &r); + + if ((r&0xFF)==PT_BOMB && parts[i].type==PT_BOMB && parts[i].tmp == 1) + e = 2; + + /* half-silvered mirror */ + if (!e && parts[i].type==PT_PHOT && + (((r&0xFF)==PT_BMTL && rand()<RAND_MAX/2) || + (pmap[y][x]&0xFF)==PT_BMTL)) + e = 2; + + if (!e) //if no movement + { + if (parts[i].type!=PT_NEUT && parts[i].type!=PT_PHOT) + return 0; + if (!legacy_enable && parts[i].type==PT_PHOT && r)//PHOT heat conduction + { + if ((r & 0xFF) == PT_COAL || (r & 0xFF) == PT_BCOL) + parts[r>>8].temp = parts[i].temp; + + if ((r & 0xFF) < PT_NUM && ptypes[r&0xFF].hconduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10) && (r&0xFF)!=PT_FILT) + parts[i].temp = parts[r>>8].temp = restrict_flt((parts[r>>8].temp+parts[i].temp)/2, MIN_TEMP, MAX_TEMP); + } + if ((parts[i].type==PT_NEUT || parts[i].type==PT_ELEC) && ((r&0xFF)==PT_CLNE || (r&0xFF)==PT_PCLN || (r&0xFF)==PT_BCLN || (r&0xFF)==PT_PBCN)) { + if (!parts[r>>8].ctype) + parts[r>>8].ctype = parts[i].type; + } + if ((r&0xFF)==PT_PRTI && (parts[i].type==PT_PHOT || parts[i].type==PT_NEUT || parts[i].type==PT_ELEC)) + { + int nnx, count; + for (count=0; count<8; count++) + { + if (isign(x-nx)==isign(portal_rx[count]) && isign(y-ny)==isign(portal_ry[count])) + break; + } + count = count%8; + parts[r>>8].tmp = (int)((parts[r>>8].temp-73.15f)/100+1); + if (parts[r>>8].tmp>=CHANNELS) parts[r>>8].tmp = CHANNELS-1; + else if (parts[r>>8].tmp<0) parts[r>>8].tmp = 0; + for ( nnx=0; nnx<80; nnx++) + if (!portalp[parts[r>>8].tmp][count][nnx].type) + { + portalp[parts[r>>8].tmp][count][nnx] = parts[i]; + parts[i].type=PT_NONE; + break; + } + } + return 0; + } + + if (e == 2) //if occupy same space + { + if (parts[i].type == PT_PHOT && (r&0xFF)==PT_GLOW && !parts[r>>8].life) + if (rand() < RAND_MAX/30) + { + parts[r>>8].life = 120; + create_gain_photon(i); + } + if (parts[i].type == PT_PHOT && (r&0xFF)==PT_FILT) + { + int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f); + if (temp_bin < 0) temp_bin = 0; + if (temp_bin > 25) temp_bin = 25; + if(!parts[r>>8].tmp){ + parts[i].ctype = 0x1F << temp_bin; //Assign Colour + } else if(parts[r>>8].tmp==1){ + parts[i].ctype &= 0x1F << temp_bin; //Filter Colour + } else if(parts[r>>8].tmp==2){ + parts[i].ctype |= 0x1F << temp_bin; //Add Colour + } else if(parts[r>>8].tmp==3){ + parts[i].ctype &= ~(0x1F << temp_bin); //Subtract Colour + } + } + if (parts[i].type == PT_NEUT && (r&0xFF)==PT_GLAS) { + if (rand() < RAND_MAX/10) + create_cherenkov_photon(i); + } + if (parts[i].type == PT_PHOT && (r&0xFF)==PT_INVIS && pv[ny/CELL][nx/CELL]<=4.0f && pv[ny/CELL][nx/CELL]>=-4.0f) { + part_change_type(i,x,y,PT_NEUT); + parts[i].ctype = 0; + } + if ((parts[i].type==PT_BIZR||parts[i].type==PT_BIZRG) && (r&0xFF)==PT_FILT) + { + int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f); + if (temp_bin < 0) temp_bin = 0; + if (temp_bin > 25) temp_bin = 25; + parts[i].ctype = 0x1F << temp_bin; + } + return 1; + } + //else e=1 , we are trying to swap the particles, return 0 no swap/move, 1 is still overlap/move, because the swap takes place later + + if ((r&0xFF)==PT_VOID || (r&0xFF)==PT_PVOD) //this is where void eats particles + { + if (parts[i].type == PT_STKM) + { + player.spwn = 0; + } + if (parts[i].type == PT_STKM2) + { + player2.spwn = 0; + } + if (parts[i].type == PT_FIGH) + { + fighters[(unsigned char)parts[i].tmp].spwn = 0; + fighcount--; + } + parts[i].type=PT_NONE; + return 0; + } + if ((r&0xFF)==PT_BHOL || (r&0xFF)==PT_NBHL) //this is where blackhole eats particles + { + if (parts[i].type == PT_STKM) + { + player.spwn = 0; + } + if (parts[i].type == PT_STKM2) + { + player2.spwn = 0; + } + if (parts[i].type == PT_FIGH) + { + fighters[(unsigned char)parts[i].tmp].spwn = 0; + fighcount--; + } + parts[i].type=PT_NONE; + if (!legacy_enable) + { + parts[r>>8].temp = restrict_flt(parts[r>>8].temp+parts[i].temp/2, MIN_TEMP, MAX_TEMP);//3.0f; + } + + return 0; + } + if (((r&0xFF)==PT_WHOL||(r&0xFF)==PT_NWHL) && parts[i].type==PT_ANAR) //whitehole eats anar + { + parts[i].type=PT_NONE; + if (!legacy_enable) + { + parts[r>>8].temp = restrict_flt(parts[r>>8].temp- (MAX_TEMP-parts[i].temp)/2, MIN_TEMP, MAX_TEMP); + } + + return 0; + } + + if (parts[i].type==PT_CNCT && y<ny && (pmap[y+1][x]&0xFF)==PT_CNCT)//check below CNCT for another CNCT + return 0; + + if ((bmap[y/CELL][x/CELL]==WL_EHOLE && !emap[y/CELL][x/CELL]) && !(bmap[ny/CELL][nx/CELL]==WL_EHOLE && !emap[ny/CELL][nx/CELL])) + return 0; + + if(parts[i].type==PT_GBMB&&parts[i].life>0) + return 0; + + e = r >> 8; //e is now the particle number at r (pmap[ny][nx]) + if (r)//the swap part, if we make it this far, swap + { + if (parts[i].type==PT_NEUT) { + // target material is NEUTPENETRATE, meaning it gets moved around when neutron passes + unsigned s = pmap[y][x]; + if (!(ptypes[s&0xFF].properties&PROP_NEUTPENETRATE)) + return 1; // if the element currently underneath neutron isn't NEUTPENETRATE, don't move anything except the neutron + // if nothing is currently underneath neutron, only move target particle + if (s) + { + pmap[ny][nx] = (s&~(0xFF))|parts[s>>8].type; + parts[s>>8].x = nx; + parts[s>>8].y = ny; + } + else pmap[ny][nx] = 0; + parts[e].x = x; + parts[e].y = y; + pmap[y][x] = (e<<8)|parts[e].type; + return 1; + } + + if ((pmap[ny][nx]>>8)==e) pmap[ny][nx] = 0; + parts[e].x += x-nx; + parts[e].y += y-ny; + pmap[(int)(parts[e].y+0.5f)][(int)(parts[e].x+0.5f)] = (e<<8)|parts[e].type; + } + return 1; +} + +// try to move particle, and if successful update pmap and parts[i].x,y +int Simulation::do_move(int i, int x, int y, float nxf, float nyf) +{ + int nx = (int)(nxf+0.5f), ny = (int)(nyf+0.5f); + int result = try_move(i, x, y, nx, ny); + if (result) + { + int t = parts[i].type; + parts[i].x = nxf; + parts[i].y = nyf; + if (ny!=y || nx!=x) + { + if ((pmap[y][x]>>8)==i) pmap[y][x] = 0; + else if ((photons[y][x]>>8)==i) photons[y][x] = 0; + if (nx<CELL || nx>=XRES-CELL || ny<CELL || ny>=YRES-CELL)//kill_part if particle is out of bounds + { + kill_part(i); + return -1; + } + if (t==PT_PHOT||t==PT_NEUT||t==PT_ELEC) + photons[ny][nx] = t|(i<<8); + else if (t) + pmap[ny][nx] = t|(i<<8); + } + } + return result; +} + +int Simulation::pn_junction_sprk(int x, int y, int pt) +{ + unsigned r = pmap[y][x]; + if ((r & 0xFF) != pt) + return 0; + r >>= 8; + if (parts[r].type != pt) + return 0; + if (parts[r].life != 0) + return 0; + + parts[r].ctype = pt; + part_change_type(r,x,y,PT_SPRK); + parts[r].life = 4; + return 1; +} + +void Simulation::photoelectric_effect(int nx, int ny)//create sparks from PHOT when hitting PSCN and NSCN +{ + unsigned r = pmap[ny][nx]; + + if ((r&0xFF) == PT_PSCN) { + if ((pmap[ny][nx-1] & 0xFF) == PT_NSCN || + (pmap[ny][nx+1] & 0xFF) == PT_NSCN || + (pmap[ny-1][nx] & 0xFF) == PT_NSCN || + (pmap[ny+1][nx] & 0xFF) == PT_NSCN) + pn_junction_sprk(nx, ny, PT_PSCN); + } +} + +unsigned Simulation::direction_to_map(float dx, float dy, int t) +{ + // TODO: + // Adding extra directions causes some inaccuracies. + // Not adding them causes problems with some diagonal surfaces (photons absorbed instead of reflected). + // For now, don't add them. + // Solution may involve more intelligent setting of initial i0 value in find_next_boundary? + // or rewriting normal/boundary finding code + + return (dx >= 0) | + (((dx + dy) >= 0) << 1) | /* 567 */ + ((dy >= 0) << 2) | /* 4+0 */ + (((dy - dx) >= 0) << 3) | /* 321 */ + ((dx <= 0) << 4) | + (((dx + dy) <= 0) << 5) | + ((dy <= 0) << 6) | + (((dy - dx) <= 0) << 7); + /* + return (dx >= -0.001) | + (((dx + dy) >= -0.001) << 1) | // 567 + ((dy >= -0.001) << 2) | // 4+0 + (((dy - dx) >= -0.001) << 3) | // 321 + ((dx <= 0.001) << 4) | + (((dx + dy) <= 0.001) << 5) | + ((dy <= 0.001) << 6) | + (((dy - dx) <= 0.001) << 7); + }*/ +} + +int Simulation::is_blocking(int t, int x, int y) +{ + if (t & REFRACT) { + if (x<0 || y<0 || x>=XRES || y>=YRES) + return 0; + if ((pmap[y][x] & 0xFF) == PT_GLAS) + return 1; + return 0; + } + + return !eval_move(t, x, y, NULL); +} + +int Simulation::is_boundary(int pt, int x, int y) +{ + if (!is_blocking(pt,x,y)) + return 0; + if (is_blocking(pt,x,y-1) && is_blocking(pt,x,y+1) && is_blocking(pt,x-1,y) && is_blocking(pt,x+1,y)) + return 0; + return 1; +} + +int Simulation::find_next_boundary(int pt, int *x, int *y, int dm, int *em) +{ + static int dx[8] = {1,1,0,-1,-1,-1,0,1}; + static int dy[8] = {0,1,1,1,0,-1,-1,-1}; + static int de[8] = {0x83,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC1}; + int i, ii, i0; + + if (*x <= 0 || *x >= XRES-1 || *y <= 0 || *y >= YRES-1) + return 0; + + if (*em != -1) { + i0 = *em; + dm &= de[i0]; + } else + i0 = 0; + + for (ii=0; ii<8; ii++) { + i = (ii + i0) & 7; + if ((dm & (1 << i)) && is_boundary(pt, *x+dx[i], *y+dy[i])) { + *x += dx[i]; + *y += dy[i]; + *em = i; + return 1; + } + } + + return 0; +} + +int Simulation::get_normal(int pt, int x, int y, float dx, float dy, float *nx, float *ny) +{ + int ldm, rdm, lm, rm; + int lx, ly, lv, rx, ry, rv; + int i, j; + float r, ex, ey; + + if (!dx && !dy) + return 0; + + if (!is_boundary(pt, x, y)) + return 0; + + ldm = direction_to_map(-dy, dx, pt); + rdm = direction_to_map(dy, -dx, pt); + lx = rx = x; + ly = ry = y; + lv = rv = 1; + lm = rm = -1; + + j = 0; + for (i=0; i<SURF_RANGE; i++) { + if (lv) + lv = find_next_boundary(pt, &lx, &ly, ldm, &lm); + if (rv) + rv = find_next_boundary(pt, &rx, &ry, rdm, &rm); + j += lv + rv; + if (!lv && !rv) + break; + } + + if (j < NORMAL_MIN_EST) + return 0; + + if ((lx == rx) && (ly == ry)) + return 0; + + ex = rx - lx; + ey = ry - ly; + r = 1.0f/hypot(ex, ey); + *nx = ey * r; + *ny = -ex * r; + + return 1; +} + +int Simulation::get_normal_interp(int pt, float x0, float y0, float dx, float dy, float *nx, float *ny) +{ + int x, y, i; + + dx /= NORMAL_FRAC; + dy /= NORMAL_FRAC; + + for (i=0; i<NORMAL_INTERP; i++) { + x = (int)(x0 + 0.5f); + y = (int)(y0 + 0.5f); + if (is_boundary(pt, x, y)) + break; + x0 += dx; + y0 += dy; + } + if (i >= NORMAL_INTERP) + return 0; + + if (pt == PT_PHOT) + photoelectric_effect(x, y); + + return get_normal(pt, x, y, dx, dy, nx, ny); +} + +//For soap only +void Simulation::detach(int i) +{ + if ((parts[i].ctype&2) == 2) + { + if ((parts[parts[i].tmp].ctype&4) == 4) + parts[parts[i].tmp].ctype ^= 4; + } + + if ((parts[i].ctype&4) == 4) + { + if ((parts[parts[i].tmp2].ctype&2) == 2) + parts[parts[i].tmp2].ctype ^= 2; + } + + parts[i].ctype = 0; +} + +void Simulation::kill_part(int i)//kills particle number i +{ + int x, y; + + if(elementCount[parts[i].type] && parts[i].type) + elementCount[parts[i].type]--; + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + if (parts[i].type == PT_STKM) + { + player.spwn = 0; + } + if (parts[i].type == PT_STKM2) + { + player2.spwn = 0; + } + if (parts[i].type == PT_FIGH) + { + fighters[(unsigned char)parts[i].tmp].spwn = 0; + fighcount--; + } + if (parts[i].type == PT_SOAP) + { + detach(i); + } + if (x>=0 && y>=0 && x<XRES && y<YRES) { + if ((pmap[y][x]>>8)==i) + pmap[y][x] = 0; + else if ((photons[y][x]>>8)==i) + photons[y][x] = 0; + } + + parts[i].type = PT_NONE; + parts[i].life = pfree; + pfree = i; +} + +void Simulation::part_change_type(int i, int x, int y, int t)//changes the type of particle number i, to t. This also changes pmap at the same time. +{ + if (x<0 || y<0 || x>=XRES || y>=YRES || i>=NPART || t<0 || t>=PT_NUM) + return; + if (!ptypes[t].enabled) + t = PT_NONE; + + if (parts[i].type == PT_STKM) + player.spwn = 0; + + if (parts[i].type == PT_STKM2) + player2.spwn = 0; + + if (parts[i].type == PT_FIGH) + { + fighters[(unsigned char)parts[i].tmp].spwn = 0; + fighcount--; + } + + parts[i].type = t; + if (t==PT_PHOT || t==PT_NEUT || t==PT_ELEC) + { + photons[y][x] = t|(i<<8); + if ((pmap[y][x]>>8)==i) + pmap[y][x] = 0; + } + else + { + pmap[y][x] = t|(i<<8); + if ((photons[y][x]>>8)==i) + photons[y][x] = 0; + } +} + +int Simulation::create_part(int p, int x, int y, int tv)//the function for creating a particle, use p=-1 for creating a new particle, -2 is from a brush, or a particle number to replace a particle. +{ + int i; + + int t = tv & 0xFF; + int v = (tv >> 8) & 0xFF; + + if (x<0 || y<0 || x>=XRES || y>=YRES || ((t<0 || t>=PT_NUM)&&t!=SPC_HEAT&&t!=SPC_COOL&&t!=SPC_AIR&&t!=SPC_VACUUM&&t!=SPC_PGRV&&t!=SPC_NGRV)) + return -1; + if (t>=0 && t<PT_NUM && !ptypes[t].enabled) + return -1; + if(t==SPC_PROP) { + return -1; //Prop tool works on a mouse click basic, make sure it doesn't do anything here + } + + /*if (t==SPC_HEAT||t==SPC_COOL) + { + if ((pmap[y][x]&0xFF)!=PT_NONE&&(pmap[y][x]&0xFF)<PT_NUM) + { + if (t==SPC_HEAT&&parts[pmap[y][x]>>8].temp<MAX_TEMP) + { + if ((pmap[y][x]&0xFF)==PT_PUMP || (pmap[y][x]&0xFF)==PT_GPMP) { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 0.1f, MIN_TEMP, MAX_TEMP); + } else if ((sdl_mod & (KMOD_SHIFT)) && (sdl_mod & (KMOD_CTRL))) { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 50.0f, MIN_TEMP, MAX_TEMP); + } else { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 4.0f, MIN_TEMP, MAX_TEMP); + } + } + if (t==SPC_COOL&&parts[pmap[y][x]>>8].temp>MIN_TEMP) + { + if ((pmap[y][x]&0xFF)==PT_PUMP || (pmap[y][x]&0xFF)==PT_GPMP) { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 0.1f, MIN_TEMP, MAX_TEMP); + } else if ((sdl_mod & (KMOD_SHIFT)) && (sdl_mod & (KMOD_CTRL))) { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 50.0f, MIN_TEMP, MAX_TEMP); + } else { + parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 4.0f, MIN_TEMP, MAX_TEMP); + } + } + return pmap[y][x]>>8; + } + else + { + return -1; + } + }*/ + if (t==SPC_AIR) + { + pv[y/CELL][x/CELL] += 0.03f; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL] += 0.03f; + if (x+CELL<XRES) + { + pv[y/CELL][x/CELL+1] += 0.03f; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL+1] += 0.03f; + } + return -1; + } + if (t==SPC_VACUUM) + { + pv[y/CELL][x/CELL] -= 0.03f; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL] -= 0.03f; + if (x+CELL<XRES) + { + pv[y/CELL][x/CELL+1] -= 0.03f; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL+1] -= 0.03f; + } + return -1; + } + if (t==SPC_PGRV) + { + gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 5; + return -1; + } + if (t==SPC_NGRV) + { + gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = -5; + return -1; + } + + + if (t==PT_SPRK) + { + if((pmap[y][x]&0xFF)==PT_WIRE){ + parts[pmap[y][x]>>8].ctype=PT_DUST; + } + if (!((pmap[y][x]&0xFF)==PT_INST||(ptypes[pmap[y][x]&0xFF].properties&PROP_CONDUCTS))) + return -1; + if (parts[pmap[y][x]>>8].life!=0) + return -1; + parts[pmap[y][x]>>8].type = PT_SPRK; + parts[pmap[y][x]>>8].life = 4; + parts[pmap[y][x]>>8].ctype = pmap[y][x]&0xFF; + pmap[y][x] = (pmap[y][x]&~0xFF) | PT_SPRK; + return pmap[y][x]>>8; + } + if (t==PT_SPAWN&&elementCount[PT_SPAWN]) + return -1; + if (t==PT_SPAWN2&&elementCount[PT_SPAWN2]) + return -1; + if (p==-1)//creating from anything but brush + { + if (pmap[y][x] || (bmap[y/CELL][x/CELL] && !eval_move(t, x, y, NULL))) + { + if ((pmap[y][x]&0xFF)!=PT_SPAWN&&(pmap[y][x]&0xFF)!=PT_SPAWN2) + { + if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH) + { + return -1; + } + } + } + if (pfree == -1) + return -1; + i = pfree; + pfree = parts[i].life; + } + else if (p==-2)//creating from brush + { + if (pmap[y][x]) + { + if (( + ((pmap[y][x]&0xFF)==PT_STOR&&!(ptypes[t].properties&TYPE_SOLID))|| + (pmap[y][x]&0xFF)==PT_CLNE|| + (pmap[y][x]&0xFF)==PT_BCLN|| + (pmap[y][x]&0xFF)==PT_CONV|| + ((pmap[y][x]&0xFF)==PT_PCLN&&t!=PT_PSCN&&t!=PT_NSCN)|| + ((pmap[y][x]&0xFF)==PT_PBCN&&t!=PT_PSCN&&t!=PT_NSCN) + )&&( + t!=PT_CLNE&&t!=PT_PCLN&& + t!=PT_BCLN&&t!=PT_STKM&& + t!=PT_STKM2&&t!=PT_PBCN&& + t!=PT_STOR&&t!=PT_FIGH) + ) + { + parts[pmap[y][x]>>8].ctype = t; + if (t==PT_LIFE && v<NGOLALT && (pmap[y][x]&0xFF)!=PT_STOR) parts[pmap[y][x]>>8].tmp = v; + } + return -1; + } + if (photons[y][x] && (t==PT_PHOT||t==PT_NEUT||t==PT_ELEC)) + return -1; + if (pfree == -1) + return -1; + i = pfree; + pfree = parts[i].life; + } + else if (p==-3)//skip pmap checks, e.g. for sing explosion + { + if (pfree == -1) + return -1; + i = pfree; + pfree = parts[i].life; + } + else + { + int oldX = (int)(parts[p].x+0.5f); + int oldY = (int)(parts[p].y+0.5f); + if ((pmap[oldY][oldX]>>8)==p) + pmap[oldY][oldX] = 0; + if ((photons[oldY][oldX]>>8)==p) + photons[oldY][oldX] = 0; + i = p; + } + + if (i>parts_lastActiveIndex) parts_lastActiveIndex = i; + + parts[i].dcolour = 0; + if (t==PT_GLAS) + { + parts[i].pavg[1] = pv[y/CELL][x/CELL]; + } + else if (t==PT_QRTZ) + { + parts[i].pavg[1] = pv[y/CELL][x/CELL]; + } + else + { + parts[i].pavg[0] = 0.0f; + parts[i].pavg[1] = 0.0f; + } + if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH)//set everything to default values first, except for stickman. + { + parts[i].x = (float)x; + parts[i].y = (float)y; + parts[i].type = t; + parts[i].vx = 0; + parts[i].vy = 0; + parts[i].life = 0; + parts[i].ctype = 0; + parts[i].temp = ptypes[t].heat; + parts[i].tmp = 0; + parts[i].tmp2 = 0; + } + if (t==PT_LIGH && p==-2) + { + switch (gravityMode) + { + default: + case 0: + parts[i].tmp= 270+rand()%40-20; + break; + case 1: + parts[i].tmp = rand()%360; + break; + case 2: + parts[i].tmp = atan2(x-XCNTR, y-YCNTR)*(180.0f/M_PI)+90; + break; + } + parts[i].tmp2 = 4; + } + if (t==PT_SOAP) + { + parts[i].tmp = -1; + parts[i].tmp2 = -1; + } + //now set various properties that we want at spawn. + if (t==PT_ACID || t==PT_CAUS) + { + parts[i].life = 75; + } + /*Testing + if(t==PT_WOOD){ + parts[i].life = 150; + } + End Testing*/ + if (t==PT_WARP) { + parts[i].life = rand()%95+70; + } + if (t==PT_FUSE) { + parts[i].life = 50; + parts[i].tmp = 50; + } + /*if (ptypes[t].properties&PROP_LIFE) { + int r; + for (r = 0; r<NGOL; r++) + if (t==goltype[r]) + parts[i].tmp = grule[r+1][9] - 1; + }*/ + if (t==PT_LIFE && v<NGOLALT) + { + parts[i].tmp = grule[v+1][9] - 1; + parts[i].ctype = v; + } + + if (t==PT_DEUT) + parts[i].life = 10; + if (t==PT_MERC) + parts[i].tmp = 10; + if (t==PT_BRAY) + parts[i].life = 30; + if (t==PT_PUMP) + parts[i].life= 10; + if (t==PT_SING) + parts[i].life = rand()%50+60; + if (t==PT_QRTZ) + parts[i].tmp = (rand()%11); + if (t==PT_PQRT) + parts[i].tmp = (rand()%11); + if (t==PT_CLST) + parts[i].tmp = (rand()%7); + if (t==PT_FSEP) + parts[i].life = 50; + if (t==PT_COAL) { + parts[i].life = 110; + parts[i].tmp = 50; + } + if (t==PT_IGNT) { + parts[i].life = 3; + } + if (t==PT_FRZW) + parts[i].life = 100; + if (t==PT_PIPE) + parts[i].life = 60; + if (t==PT_BCOL) + parts[i].life = 110; + if (t==PT_FIRE) + parts[i].life = rand()%50+120; + if (t==PT_PLSM) + parts[i].life = rand()%150+50; + if (t==PT_HFLM) + parts[i].life = rand()%150+50; + if (t==PT_LAVA) + parts[i].life = rand()%120+240; + if (t==PT_NBLE) + parts[i].life = 0; + if (t==PT_ICEI) + parts[i].ctype = PT_WATR; + if (t==PT_NEUT) + { + float r = (rand()%128+128)/127.0f; + float a = (rand()%360)*3.14159f/180.0f; + parts[i].life = rand()%480+480; + parts[i].vx = r*cosf(a); + parts[i].vy = r*sinf(a); + } + if (t==PT_MORT) + { + parts[i].vx = 2; + } + if (t==PT_PHOT) + { + float a = (rand()%8) * 0.78540f; + parts[i].life = 680; + parts[i].ctype = 0x3FFFFFFF; + parts[i].vx = 3.0f*cosf(a); + parts[i].vy = 3.0f*sinf(a); + } + if (t==PT_ELEC) + { + float a = (rand()%360)*3.14159f/180.0f; + parts[i].life = 680; + parts[i].vx = 2.0f*cosf(a); + parts[i].vy = 2.0f*sinf(a); + } + if (t==PT_STKM) + { + if (player.spwn==0) + { + parts[i].x = (float)x; + parts[i].y = (float)y; + parts[i].type = PT_STKM; + parts[i].vx = 0; + parts[i].vy = 0; + parts[i].life = 100; + parts[i].ctype = 0; + parts[i].temp = ptypes[t].heat; + STKM_init_legs(this, &player, i); + player.spwn = 1; + } + else + { + return -1; + } + create_part(-1,x,y,PT_SPAWN); + } + if (t==PT_STKM2) + { + if (player2.spwn==0) + { + parts[i].x = (float)x; + parts[i].y = (float)y; + parts[i].type = PT_STKM2; + parts[i].vx = 0; + parts[i].vy = 0; + parts[i].life = 100; + parts[i].ctype = 0; + parts[i].temp = ptypes[t].heat; + STKM_init_legs(this, &player2, i); + player2.spwn = 1; + } + else + { + return -1; + } + create_part(-1,x,y,PT_SPAWN2); + } + if (t==PT_FIGH) + { + unsigned char fcount = 0; + while (fcount < 100 && fcount < (fighcount+1) && fighters[fcount].spwn==1) fcount++; + if (fcount < 100 && fighters[fcount].spwn==0) + { + parts[i].x = (float)x; + parts[i].y = (float)y; + parts[i].type = PT_FIGH; + parts[i].vx = 0; + parts[i].vy = 0; + parts[i].life = 100; + parts[i].ctype = 0; + parts[i].tmp = fcount; + parts[i].temp = ptypes[t].heat; + STKM_init_legs(this, &fighters[fcount], i); + fighters[fcount].spwn = 1; + fighters[fcount].elem = PT_DUST; + fighcount++; + + return i; + } + return -1; + } + if (t==PT_BIZR||t==PT_BIZRG||t==PT_BIZRS) + parts[i].ctype = 0x47FFFF; + //and finally set the pmap/photon maps to the newly created particle + if (t==PT_PHOT||t==PT_NEUT||t==PT_ELEC) + photons[y][x] = t|(i<<8); + if (t!=PT_STKM&&t!=PT_STKM2 && t!=PT_FIGH && t!=PT_PHOT && t!=PT_NEUT) + pmap[y][x] = t|(i<<8); + + //Fancy dust effects for powder types + if((ptypes[t].properties & TYPE_PART) && pretty_powder) + { + int colr, colg, colb, randa; + randa = (rand()%30)-15; + colr = (PIXR(ptypes[t].pcolors)+sandcolour_r+(rand()%20)-10+randa); + colg = (PIXG(ptypes[t].pcolors)+sandcolour_g+(rand()%20)-10+randa); + colb = (PIXB(ptypes[t].pcolors)+sandcolour_b+(rand()%20)-10+randa); + colr = colr>255 ? 255 : (colr<0 ? 0 : colr); + colg = colg>255 ? 255 : (colg<0 ? 0 : colg); + colb = colb>255 ? 255 : (colb<0 ? 0 : colb); + parts[i].dcolour = 0xFF000000 | (colr<<16) | (colg<<8) | colb; + } + elementCount[t]++; + return i; +} + +void Simulation::create_gain_photon(int pp)//photons from PHOT going through GLOW +{ + float xx, yy; + int i, lr, temp_bin, nx, ny; + + if (pfree == -1) + return; + i = pfree; + + lr = rand() % 2; + + if (lr) { + xx = parts[pp].x - 0.3*parts[pp].vy; + yy = parts[pp].y + 0.3*parts[pp].vx; + } else { + xx = parts[pp].x + 0.3*parts[pp].vy; + yy = parts[pp].y - 0.3*parts[pp].vx; + } + + nx = (int)(xx + 0.5f); + ny = (int)(yy + 0.5f); + + if (nx<0 || ny<0 || nx>=XRES || ny>=YRES) + return; + + if ((pmap[ny][nx] & 0xFF) != PT_GLOW) + return; + + pfree = parts[i].life; + if (i>parts_lastActiveIndex) parts_lastActiveIndex = i; + + parts[i].type = PT_PHOT; + parts[i].life = 680; + parts[i].x = xx; + parts[i].y = yy; + parts[i].vx = parts[pp].vx; + parts[i].vy = parts[pp].vy; + parts[i].temp = parts[pmap[ny][nx] >> 8].temp; + parts[i].tmp = 0; + parts[i].pavg[0] = parts[i].pavg[1] = 0.0f; + photons[ny][nx] = PT_PHOT|(i<<8); + + temp_bin = (int)((parts[i].temp-273.0f)*0.25f); + if (temp_bin < 0) temp_bin = 0; + if (temp_bin > 25) temp_bin = 25; + parts[i].ctype = 0x1F << temp_bin; +} + +void Simulation::create_cherenkov_photon(int pp)//photons from NEUT going through GLAS +{ + int i, lr, nx, ny; + float r, eff_ior; + + if (pfree == -1) + return; + i = pfree; + + nx = (int)(parts[pp].x + 0.5f); + ny = (int)(parts[pp].y + 0.5f); + if ((pmap[ny][nx] & 0xFF) != PT_GLAS) + return; + + if (hypotf(parts[pp].vx, parts[pp].vy) < 1.44f) + return; + + pfree = parts[i].life; + if (i>parts_lastActiveIndex) parts_lastActiveIndex = i; + + lr = rand() % 2; + + parts[i].type = PT_PHOT; + parts[i].ctype = 0x00000F80; + parts[i].life = 680; + parts[i].x = parts[pp].x; + parts[i].y = parts[pp].y; + parts[i].temp = parts[pmap[ny][nx] >> 8].temp; + parts[i].tmp = 0; + parts[i].pavg[0] = parts[i].pavg[1] = 0.0f; + photons[ny][nx] = PT_PHOT|(i<<8); + + if (lr) { + parts[i].vx = parts[pp].vx - 2.5f*parts[pp].vy; + parts[i].vy = parts[pp].vy + 2.5f*parts[pp].vx; + } else { + parts[i].vx = parts[pp].vx + 2.5f*parts[pp].vy; + parts[i].vy = parts[pp].vy - 2.5f*parts[pp].vx; + } + + /* photons have speed of light. no discussion. */ + r = 1.269 / hypotf(parts[i].vx, parts[i].vy); + parts[i].vx *= r; + parts[i].vy *= r; +} + +void Simulation::delete_part(int x, int y, int flags)//calls kill_part with the particle located at x,y +{ + unsigned i; + + if (x<0 || y<0 || x>=XRES || y>=YRES) + return; + if (photons[y][x]) { + i = photons[y][x]; + } else { + i = pmap[y][x]; + } + + if (!i) + return; + kill_part(i>>8); +} + +void Simulation::update_particles_i(int start, int inc) +{ + int i, j, x, y, t, nx, ny, r, surround_space, s, lt, rt, nt, nnx, nny, q, golnum, goldelete, z, neighbors, createdsomething; + float mv, dx, dy, ix, iy, lx, ly, nrx, nry, dp, ctemph, ctempl, gravtot; + int fin_x, fin_y, clear_x, clear_y, stagnant; + float fin_xf, fin_yf, clear_xf, clear_yf; + float nn, ct1, ct2, swappage; + float pt = R_TEMP; + float c_heat = 0.0f; + int h_count = 0; + int starti = (start*-1); + int surround[8]; + int surround_hconduct[8]; + int lighting_ok=1; + float pGravX, pGravY, pGravD; + + if (sys_pause&&lighting_recreate>0) + { + for (i=0; i<=parts_lastActiveIndex; i++) + { + if (parts[i].type==PT_LIGH && parts[i].tmp2>0) + { + lighting_ok=0; + break; + } + } + } + + if (lighting_ok) + lighting_recreate--; + + if (lighting_recreate<0) + lighting_recreate=1; + + if (lighting_recreate>21) + lighting_recreate=21; + + if (sys_pause&&!framerender)//do nothing if paused + return; + + //wire! + if(elementCount[PT_WIRE] > 0) + { + for (nx=0; nx<XRES; nx++) + { + for (ny=0; ny<YRES; ny++) + { + r = pmap[ny][nx]; + if (!r) + continue; + if(parts[r>>8].type==PT_WIRE) + parts[r>>8].tmp=parts[r>>8].ctype; + } + } + } + //game of life! + if (elementCount[PT_LIFE]>0&&++CGOL>=GSPEED)//GSPEED is frames per generation + { + int createdsomething = 0; + CGOL=0; + ISGOL=0; + for (nx=CELL; nx<XRES-CELL; nx++) + {//go through every particle and set neighbor map + for (ny=CELL; ny<YRES-CELL; ny++) + { + r = pmap[ny][nx]; + if (!r) + { + gol[nx][ny] = 0; + continue; + } + else + { + //for ( golnum=1; golnum<=NGOL; golnum++) //This shouldn't be necessary any more. + //{ + if (parts[r>>8].type==PT_LIFE/* && parts[r>>8].ctype==golnum-1*/) + { + golnum = parts[r>>8].ctype+1; + if (golnum<=0 || golnum>NGOLALT) { + parts[r>>8].type = PT_NONE; + continue; + } + if (parts[r>>8].tmp == grule[golnum][9]-1) { + gol[nx][ny] = golnum; + for ( nnx=-1; nnx<2; nnx++) + { + for ( nny=-1; nny<2; nny++)//it will count itself as its own neighbor, which is needed, but will have 1 extra for delete check + { + rt = pmap[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL]; + if (!rt || (rt&0xFF)==PT_LIFE) + { + gol2[((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][golnum] ++; + gol2[((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][0] ++; + } + } + } + } else { + parts[r>>8].tmp --; + if (parts[r>>8].tmp<=0) + parts[r>>8].type = PT_NONE;//using kill_part makes it not work + } + } + //} + } + } + } + for (nx=CELL; nx<XRES-CELL; nx++) + { //go through every particle again, but check neighbor map, then update particles + for (ny=CELL; ny<YRES-CELL; ny++) + { + r = pmap[ny][nx]; + neighbors = gol2[nx][ny][0]; + if (neighbors==0 || !((r&0xFF)==PT_LIFE || !(r&0xFF))) + continue; + for ( golnum = 1; golnum<=NGOL; golnum++) + { + goldelete = neighbors; + if (gol[nx][ny]==0&&grule[golnum][goldelete]>=2&&gol2[nx][ny][golnum]>=(goldelete%2)+goldelete/2) + { + if (create_part(-1, nx, ny, PT_LIFE|((golnum-1)<<8))) + createdsomething = 1; + } + else if (gol[nx][ny]==golnum&&(grule[golnum][goldelete-1]==0||grule[golnum][goldelete-1]==2))//subtract 1 because it counted itself + { + if (parts[r>>8].tmp==grule[golnum][9]-1) + parts[r>>8].tmp --; + } + if (r && parts[r>>8].tmp<=0) + parts[r>>8].type = PT_NONE;//using kill_part makes it not work + } + for ( z = 0; z<=NGOL; z++) + gol2[nx][ny][z] = 0;//this improves performance A LOT compared to the memset, i was getting ~23 more fps with this. + } + } + //memset(gol2, 0, sizeof(gol2)); + } + if (ISWIRE==1)//wifi channel reseting + { + for ( q = 0; q<(int)(MAX_TEMP-73.15f)/100+2; q++) + if (!wireless[q][1]) + { + wireless[q][0] = 0; + } + else + wireless[q][1] = 0; + } + //the main particle loop function, goes over all particles. + for (i=0; i<=parts_lastActiveIndex; i++) + if (parts[i].type) + { + lx = parts[i].x; + ly = parts[i].y; + t = parts[i].type; + if (t<0 || t>=PT_NUM) + { + kill_part(i); + continue; + } + //printf("parts[%d].type: %d\n", i, parts[i].type); + + if (parts[i].life>0 && (ptypes[t].properties&PROP_LIFE_DEC)) + { + // automatically decrease life + parts[i].life--; + if (parts[i].life<=0 && (ptypes[t].properties&(PROP_LIFE_KILL_DEC|PROP_LIFE_KILL))) + { + // kill on change to no life + kill_part(i); + continue; + } + } + else if (parts[i].life<=0 && (ptypes[t].properties&PROP_LIFE_KILL)) + { + // kill if no life + kill_part(i); + continue; + } + + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + + //this kills any particle out of the screen, or in a wall where it isn't supposed to go + if (x<CELL || y<CELL || x>=XRES-CELL || y>=YRES-CELL || + (bmap[y/CELL][x/CELL] && + (bmap[y/CELL][x/CELL]==WL_WALL || + bmap[y/CELL][x/CELL]==WL_WALLELEC || + bmap[y/CELL][x/CELL]==WL_ALLOWAIR || + (bmap[y/CELL][x/CELL]==WL_DESTROYALL) || + (bmap[y/CELL][x/CELL]==WL_ALLOWLIQUID && ptypes[t].falldown!=2) || + (bmap[y/CELL][x/CELL]==WL_ALLOWSOLID && ptypes[t].falldown!=1) || + (bmap[y/CELL][x/CELL]==WL_ALLOWGAS && !(ptypes[t].properties&TYPE_GAS)) || //&& ptypes[t].falldown!=0 && parts[i].type!=PT_FIRE && parts[i].type!=PT_SMKE && parts[i].type!=PT_HFLM) || + (bmap[y/CELL][x/CELL]==WL_ALLOWENERGY && !(ptypes[t].properties&TYPE_ENERGY)) || + (bmap[y/CELL][x/CELL]==WL_DETECT && (t==PT_METL || t==PT_SPRK)) || + (bmap[y/CELL][x/CELL]==WL_EWALL && !emap[y/CELL][x/CELL])) && (t!=PT_STKM) && (t!=PT_STKM2) && (t!=PT_FIGH))) + { + kill_part(i); + continue; + } + if (bmap[y/CELL][x/CELL]==WL_DETECT && emap[y/CELL][x/CELL]<8) + set_emap(x/CELL, y/CELL); + + //adding to velocity from the particle's velocity + vx[y/CELL][x/CELL] = vx[y/CELL][x/CELL]*ptypes[t].airloss + ptypes[t].airdrag*parts[i].vx; + vy[y/CELL][x/CELL] = vy[y/CELL][x/CELL]*ptypes[t].airloss + ptypes[t].airdrag*parts[i].vy; + + if (t==PT_GAS||t==PT_NBLE) + { + if (pv[y/CELL][x/CELL]<3.5f) + pv[y/CELL][x/CELL] += ptypes[t].hotair*(3.5f-pv[y/CELL][x/CELL]); + if (y+CELL<YRES && pv[y/CELL+1][x/CELL]<3.5f) + pv[y/CELL+1][x/CELL] += ptypes[t].hotair*(3.5f-pv[y/CELL+1][x/CELL]); + if (x+CELL<XRES) + { + if (pv[y/CELL][x/CELL+1]<3.5f) + pv[y/CELL][x/CELL+1] += ptypes[t].hotair*(3.5f-pv[y/CELL][x/CELL+1]); + if (y+CELL<YRES && pv[y/CELL+1][x/CELL+1]<3.5f) + pv[y/CELL+1][x/CELL+1] += ptypes[t].hotair*(3.5f-pv[y/CELL+1][x/CELL+1]); + } + } + else//add the hotair variable to the pressure map, like black hole, or white hole. + { + pv[y/CELL][x/CELL] += ptypes[t].hotair; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL] += ptypes[t].hotair; + if (x+CELL<XRES) + { + pv[y/CELL][x/CELL+1] += ptypes[t].hotair; + if (y+CELL<YRES) + pv[y/CELL+1][x/CELL+1] += ptypes[t].hotair; + } + } + + //Gravity mode by Moach + switch (gravityMode) + { + default: + case 0: + pGravX = 0.0f; + pGravY = ptypes[t].gravity; + break; + case 1: + pGravX = pGravY = 0.0f; + break; + case 2: + pGravD = 0.01f - hypotf((x - XCNTR), (y - YCNTR)); + pGravX = ptypes[t].gravity * ((float)(x - XCNTR) / pGravD); + pGravY = ptypes[t].gravity * ((float)(y - YCNTR) / pGravD); + break; + } + //Get some gravity from the gravity map + if (t==PT_ANAR) + { + // perhaps we should have a ptypes variable for this + pGravX -= gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]; + pGravY -= gravy[(y/CELL)*(XRES/CELL)+(x/CELL)]; + } + else if(t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH && !(ptypes[t].properties & TYPE_SOLID)) + { + pGravX += gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]; + pGravY += gravy[(y/CELL)*(XRES/CELL)+(x/CELL)]; + } + //velocity updates for the particle + parts[i].vx *= ptypes[t].loss; + parts[i].vy *= ptypes[t].loss; + //particle gets velocity from the vx and vy maps + parts[i].vx += ptypes[t].advection*vx[y/CELL][x/CELL] + pGravX; + parts[i].vy += ptypes[t].advection*vy[y/CELL][x/CELL] + pGravY; + + + if (ptypes[t].diffusion)//the random diffusion that gasses have + { + parts[i].vx += ptypes[t].diffusion*(rand()/(0.5f*RAND_MAX)-1.0f); + parts[i].vy += ptypes[t].diffusion*(rand()/(0.5f*RAND_MAX)-1.0f); + } + + j = surround_space = nt = 0;//if nt is 1 after this, then there is a particle around the current particle, that is NOT the current particle's type, for water movement. + for (nx=-1; nx<2; nx++) + for (ny=-1; ny<2; ny++) { + if (nx||ny) { + surround[j] = r = pmap[y+ny][x+nx]; + j++; + if (!(r&0xFF)) + surround_space = 1;//there is empty space + if ((r&0xFF)!=t) + nt = 1;//there is nothing or a different particle + } + } + + if (!legacy_enable) + { + if (y-2 >= 0 && y-2 < YRES && (ptypes[t].properties&TYPE_LIQUID)) {//some heat convection for liquids + r = pmap[y-2][x]; + if (!(!r || parts[i].type != (r&0xFF))) { + if (parts[i].temp>parts[r>>8].temp) { + swappage = parts[i].temp; + parts[i].temp = parts[r>>8].temp; + parts[r>>8].temp = swappage; + } + } + } + + //heat transfer code + h_count = 0; +#ifdef REALHEAT + if (t&&(t!=PT_HSWC||parts[i].life==10)) + { + float c_Cm = 0.0f; +#else + if (t&&(t!=PT_HSWC||parts[i].life==10)&&ptypes[t].hconduct>(rand()%250)) + { + float c_Cm = 0.0f; +#endif + if (aheat_enable) + { + c_heat = (hv[y/CELL][x/CELL]-parts[i].temp)*0.04; + c_heat = restrict_flt(c_heat, -MAX_TEMP+MIN_TEMP, MAX_TEMP-MIN_TEMP); + parts[i].temp += c_heat; + hv[y/CELL][x/CELL] -= c_heat; + } + c_heat = 0.0f; + for (j=0; j<8; j++) + { + surround_hconduct[j] = i; + r = surround[j]; + if (!r) + continue; + rt = r&0xFF; + if (rt&&ptypes[rt].hconduct&&(rt!=PT_HSWC||parts[r>>8].life==10) + &&(t!=PT_FILT||(rt!=PT_BRAY&&rt!=PT_BIZR&&rt!=PT_BIZRG)) + &&(rt!=PT_FILT||(t!=PT_BRAY&&t!=PT_PHOT&&t!=PT_BIZR&&t!=PT_BIZRG))) + { + surround_hconduct[j] = r>>8; +#ifdef REALHEAT + c_heat += parts[r>>8].temp*96.645/ptypes[rt].hconduct*fabs(ptypes[rt].weight); + c_Cm += 96.645/ptypes[rt].hconduct*fabs(ptypes[rt].weight); +#else + c_heat += parts[r>>8].temp; +#endif + h_count++; + } + } +#ifdef REALHEAT + if (t == PT_PHOT) + pt = (c_heat+parts[i].temp*96.645)/(c_Cm+96.645); + else + pt = (c_heat+parts[i].temp*96.645/ptypes[t].hconduct*fabs(ptypes[t].weight))/(c_Cm+96.645/ptypes[t].hconduct*fabs(ptypes[t].weight)); + +#else + pt = (c_heat+parts[i].temp)/(h_count+1); +#endif + pt = parts[i].temp = restrict_flt(pt, MIN_TEMP, MAX_TEMP); + for (j=0; j<8; j++) + { + parts[surround_hconduct[j]].temp = pt; + } + + ctemph = ctempl = pt; + // change boiling point with pressure + if ((ptypes[t].state==ST_LIQUID && ptransitions[t].tht>-1 && ptransitions[t].tht<PT_NUM && ptypes[ptransitions[t].tht].state==ST_GAS) + || t==PT_LNTG || t==PT_SLTW) + ctemph -= 2.0f*pv[y/CELL][x/CELL]; + else if ((ptypes[t].state==ST_GAS && ptransitions[t].tlt>-1 && ptransitions[t].tlt<PT_NUM && ptypes[ptransitions[t].tlt].state==ST_LIQUID) + || t==PT_WTRV) + ctempl -= 2.0f*pv[y/CELL][x/CELL]; + s = 1; + if (ctemph>ptransitions[t].thv&&ptransitions[t].tht>-1) { + // particle type change due to high temperature + if (ptransitions[t].tht!=PT_NUM) + t = ptransitions[t].tht; + else if (t==PT_ICEI) { + if (parts[i].ctype>0&&parts[i].ctype<PT_NUM&&parts[i].ctype!=PT_ICEI) { + if (ptransitions[parts[i].ctype].tlt==PT_ICEI&&pt<=ptransitions[parts[i].ctype].tlv) s = 0; + else { + t = parts[i].ctype; + parts[i].ctype = PT_NONE; + parts[i].life = 0; + } + } + else if (pt>274.0f) t = PT_WATR; + else s = 0; + } + else if (t==PT_SLTW) { + if (1>rand()%6) t = PT_SALT; + else t = PT_WTRV; + } + else s = 0; + } else if (ctempl<ptransitions[t].tlv&&ptransitions[t].tlt>-1) { + // particle type change due to low temperature + if (ptransitions[t].tlt!=PT_NUM) + t = ptransitions[t].tlt; + else if (t==PT_WTRV) { + if (pt<273.0f) t = PT_RIME; + else t = PT_DSTW; + } + else if (t==PT_LAVA) { + if (parts[i].ctype>0 && parts[i].ctype<PT_NUM && parts[i].ctype!=PT_LAVA) { + if (parts[i].ctype==PT_THRM&&pt>=ptransitions[PT_BMTL].thv) s = 0; + else if (ptransitions[parts[i].ctype].tht==PT_LAVA) { + if (pt>=ptransitions[parts[i].ctype].thv) s = 0; + } + else if (pt>=973.0f) s = 0; // freezing point for lava with any other (not listed in ptransitions as turning into lava) ctype + if (s) { + t = parts[i].ctype; + parts[i].ctype = PT_NONE; + if (t==PT_THRM) { + parts[i].tmp = 0; + t = PT_BMTL; + } + if (t==PT_PLUT) + { + parts[i].tmp = 0; + t = PT_LAVA; + } + } + } + else if (pt<973.0f) t = PT_STNE; + else s = 0; + } + else s = 0; + } + else s = 0; + if (s) { // particle type change occurred + if (t==PT_ICEI||t==PT_LAVA) + parts[i].ctype = parts[i].type; + if (!(t==PT_ICEI&&parts[i].ctype==PT_FRZW)) parts[i].life = 0; + if (ptypes[t].state==ST_GAS&&ptypes[parts[i].type].state!=ST_GAS) + pv[y/CELL][x/CELL] += 0.50f; + part_change_type(i,x,y,t); + if (t==PT_FIRE||t==PT_PLSM||t==PT_HFLM) + parts[i].life = rand()%50+120; + if (t==PT_LAVA) { + if (parts[i].ctype==PT_BRMT) parts[i].ctype = PT_BMTL; + else if (parts[i].ctype==PT_SAND) parts[i].ctype = PT_GLAS; + else if (parts[i].ctype==PT_BGLA) parts[i].ctype = PT_GLAS; + else if (parts[i].ctype==PT_PQRT) parts[i].ctype = PT_QRTZ; + parts[i].life = rand()%120+240; + } + if (t==PT_NONE) { + kill_part(i); + goto killed; + } + } + + pt = parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP); + if (t==PT_LAVA) { + parts[i].life = restrict_flt((parts[i].temp-700)/7, 0.0f, 400.0f); + if (parts[i].ctype==PT_THRM&&parts[i].tmp>0) + { + parts[i].tmp--; + parts[i].temp = 3500; + } + if (parts[i].ctype==PT_PLUT&&parts[i].tmp>0) + { + parts[i].tmp--; + parts[i].temp = MAX_TEMP; + } + } + } + } + + if (t==PT_LIFE) + { + parts[i].temp = restrict_flt(parts[i].temp-50.0f, MIN_TEMP, MAX_TEMP); + //ISGOL=1;//means there is a life particle on screen + } + if (t==PT_WIRE) + { + //wire_placed = 1; + } + //spark updates from walls + if ((ptypes[t].properties&PROP_CONDUCTS) || t==PT_SPRK) + { + nx = x % CELL; + if (nx == 0) + nx = x/CELL - 1; + else if (nx == CELL-1) + nx = x/CELL + 1; + else + nx = x/CELL; + ny = y % CELL; + if (ny == 0) + ny = y/CELL - 1; + else if (ny == CELL-1) + ny = y/CELL + 1; + else + ny = y/CELL; + if (nx>=0 && ny>=0 && nx<XRES/CELL && ny<YRES/CELL) + { + if (t!=PT_SPRK) + { + if (emap[ny][nx]==12 && !parts[i].life) + { + part_change_type(i,x,y,PT_SPRK); + parts[i].life = 4; + parts[i].ctype = t; + t = PT_SPRK; + } + } + else if (bmap[ny][nx]==WL_DETECT || bmap[ny][nx]==WL_EWALL || bmap[ny][nx]==WL_ALLOWLIQUID || bmap[ny][nx]==WL_WALLELEC || bmap[ny][nx]==WL_ALLOWALLELEC || bmap[ny][nx]==WL_EHOLE) + set_emap(nx, ny); + } + } + + //the basic explosion, from the .explosive variable + if ((ptypes[t].explosive&2) && pv[y/CELL][x/CELL]>2.5f) + { + parts[i].life = rand()%80+180; + parts[i].temp = restrict_flt(ptypes[PT_FIRE].heat + (ptypes[t].flammable/2), MIN_TEMP, MAX_TEMP); + t = PT_FIRE; + part_change_type(i,x,y,t); + pv[y/CELL][x/CELL] += 0.25f * CFDS; + } + + + s = 1; + gravtot = fabs(gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]); + if (pv[y/CELL][x/CELL]>ptransitions[t].phv&&ptransitions[t].pht>-1) { + // particle type change due to high pressure + if (ptransitions[t].pht!=PT_NUM) + t = ptransitions[t].pht; + else if (t==PT_BMTL) { + if (pv[y/CELL][x/CELL]>2.5f) + t = PT_BRMT; + else if (pv[y/CELL][x/CELL]>1.0f && parts[i].tmp==1) + t = PT_BRMT; + else s = 0; + } + else s = 0; + } else if (pv[y/CELL][x/CELL]<ptransitions[t].plv&&ptransitions[t].plt>-1) { + // particle type change due to low pressure + if (ptransitions[t].plt!=PT_NUM) + t = ptransitions[t].plt; + else s = 0; + } else if (gravtot>(ptransitions[t].phv/4.0f)&&ptransitions[t].pht>-1) { + // particle type change due to high gravity + if (ptransitions[t].pht!=PT_NUM) + t = ptransitions[t].pht; + else if (t==PT_BMTL) { + if (gravtot>0.625f) + t = PT_BRMT; + else if (gravtot>0.25f && parts[i].tmp==1) + t = PT_BRMT; + else s = 0; + } + else s = 0; + } else s = 0; + if (s) { // particle type change occurred + parts[i].life = 0; + part_change_type(i,x,y,t); + if (t==PT_FIRE) + parts[i].life = rand()%50+120; + if (t==PT_NONE) { + kill_part(i); + goto killed; + } + } + + //call the particle update function, if there is one +#ifdef LUACONSOLE + if (ptypes[t].update_func && lua_el_mode[t] != 2) +#else + if (ptypes[t].update_func) +#endif + { + if ((*(ptypes[t].update_func))(this, i,x,y,surround_space,nt, parts, pmap)) + continue; + } +#ifdef LUACONSOLE + if(lua_el_mode[t]) + { + if(luacon_part_update(t,i,x,y,surround_space,nt)) + continue; + } +#endif + if (legacy_enable)//if heat sim is off + update_legacy_all(this, i,x,y,surround_space,nt, parts, pmap); + +killed: + if (parts[i].type == PT_NONE)//if its dead, skip to next particle + continue; + + if (!parts[i].vx&&!parts[i].vy)//if its not moving, skip to next particle, movement code it next + continue; + +#if defined(WIN32) && !defined(__GNUC__) + mv = max(fabsf(parts[i].vx), fabsf(parts[i].vy)); +#else + mv = fmaxf(fabsf(parts[i].vx), fabsf(parts[i].vy)); +#endif + if (mv < ISTP) + { + clear_x = x; + clear_y = y; + clear_xf = parts[i].x; + clear_yf = parts[i].y; + fin_xf = clear_xf + parts[i].vx; + fin_yf = clear_yf + parts[i].vy; + fin_x = (int)(fin_xf+0.5f); + fin_y = (int)(fin_yf+0.5f); + } + else + { + // interpolate to see if there is anything in the way + dx = parts[i].vx*ISTP/mv; + dy = parts[i].vy*ISTP/mv; + fin_xf = parts[i].x; + fin_yf = parts[i].y; + while (1) + { + mv -= ISTP; + fin_xf += dx; + fin_yf += dy; + fin_x = (int)(fin_xf+0.5f); + fin_y = (int)(fin_yf+0.5f); + if (mv <= 0.0f) + { + // nothing found + fin_xf = parts[i].x + parts[i].vx; + fin_yf = parts[i].y + parts[i].vy; + fin_x = (int)(fin_xf+0.5f); + fin_y = (int)(fin_yf+0.5f); + clear_xf = fin_xf-dx; + clear_yf = fin_yf-dy; + clear_x = (int)(clear_xf+0.5f); + clear_y = (int)(clear_yf+0.5f); + break; + } + if (fin_x<CELL || fin_y<CELL || fin_x>=XRES-CELL || fin_y>=YRES-CELL || pmap[fin_y][fin_x] || (bmap[fin_y/CELL][fin_x/CELL] && (bmap[fin_y/CELL][fin_x/CELL]==WL_DESTROYALL || bmap[fin_y/CELL][fin_x/CELL]==WL_DETECT || !eval_move(t,fin_x,fin_y,NULL)))) + { + // found an obstacle + clear_xf = fin_xf-dx; + clear_yf = fin_yf-dy; + clear_x = (int)(clear_xf+0.5f); + clear_y = (int)(clear_yf+0.5f); + break; + } + + } + } + + stagnant = parts[i].flags & FLAG_STAGNANT; + parts[i].flags &= ~FLAG_STAGNANT; + + if ((t==PT_PHOT||t==PT_NEUT||t==PT_ELEC)) { + if (t == PT_PHOT) { + rt = pmap[fin_y][fin_x] & 0xFF; + lt = pmap[y][x] & 0xFF; + + r = eval_move(PT_PHOT, fin_x, fin_y, NULL); + if (((rt==PT_GLAS && lt!=PT_GLAS) || (rt!=PT_GLAS && lt==PT_GLAS)) && r) { + if (!get_normal_interp(REFRACT|t, parts[i].x, parts[i].y, parts[i].vx, parts[i].vy, &nrx, &nry)) { + kill_part(i); + continue; + } + + r = get_wavelength_bin(&parts[i].ctype); + if (r == -1) { + kill_part(i); + continue; + } + nn = GLASS_IOR - GLASS_DISP*(r-15)/15.0f; + nn *= nn; + nrx = -nrx; + nry = -nry; + if (rt==PT_GLAS && lt!=PT_GLAS) + nn = 1.0f/nn; + ct1 = parts[i].vx*nrx + parts[i].vy*nry; + ct2 = 1.0f - (nn*nn)*(1.0f-(ct1*ct1)); + if (ct2 < 0.0f) { + // total internal reflection + parts[i].vx -= 2.0f*ct1*nrx; + parts[i].vy -= 2.0f*ct1*nry; + fin_xf = parts[i].x; + fin_yf = parts[i].y; + fin_x = x; + fin_y = y; + } else { + // refraction + ct2 = sqrtf(ct2); + ct2 = ct2 - nn*ct1; + parts[i].vx = nn*parts[i].vx + ct2*nrx; + parts[i].vy = nn*parts[i].vy + ct2*nry; + } + } + } + if (stagnant)//FLAG_STAGNANT set, was reflected on previous frame + { + // cast coords as int then back to float for compatibility with existing saves + if (!do_move(i, x, y, (float)fin_x, (float)fin_y)) { + kill_part(i); + continue; + } + } + else if (!do_move(i, x, y, fin_xf, fin_yf)) + { + // reflection + parts[i].flags |= FLAG_STAGNANT; + if (t==PT_NEUT && 100>(rand()%1000)) + { + kill_part(i); + continue; + } + r = pmap[fin_y][fin_x]; + + if ((r & 0xFF) == PT_PIPE && !(parts[r>>8].tmp&0xFF)) + { + parts[r>>8].tmp = (parts[r>>8].tmp&~0xFF) | parts[i].type; + parts[r>>8].temp = parts[i].temp; + parts[r>>8].flags = parts[i].life; + parts[r>>8].pavg[0] = parts[i].tmp; + parts[r>>8].pavg[1] = parts[i].ctype; + kill_part(i); + continue; + } + + // this should be replaced with a particle type attribute ("photwl" or something) + if ((r & 0xFF) == PT_PSCN) parts[i].ctype = 0x00000000; + if ((r & 0xFF) == PT_NSCN) parts[i].ctype = 0x00000000; + if ((r & 0xFF) == PT_SPRK) parts[i].ctype = 0x00000000; + if ((r & 0xFF) == PT_COAL) parts[i].ctype = 0x00000000; + if ((r & 0xFF) == PT_BCOL) parts[i].ctype = 0x00000000; + if ((r & 0xFF) == PT_PLEX) parts[i].ctype &= 0x1F00003E; + if ((r & 0xFF) == PT_NITR) parts[i].ctype &= 0x0007C000; + if ((r & 0xFF) == PT_NBLE) parts[i].ctype &= 0x3FFF8000; + if ((r & 0xFF) == PT_LAVA) parts[i].ctype &= 0x3FF00000; + if ((r & 0xFF) == PT_ACID) parts[i].ctype &= 0x1FE001FE; + if ((r & 0xFF) == PT_DUST) parts[i].ctype &= 0x3FFFFFC0; + if ((r & 0xFF) == PT_SNOW) parts[i].ctype &= 0x03FFFFFF; + if ((r & 0xFF) == PT_GOO) parts[i].ctype &= 0x3FFAAA00; + if ((r & 0xFF) == PT_PLNT) parts[i].ctype &= 0x0007C000; + if ((r & 0xFF) == PT_PLUT) parts[i].ctype &= 0x001FCE00; + if ((r & 0xFF) == PT_URAN) parts[i].ctype &= 0x003FC000; + + if (get_normal_interp(t, parts[i].x, parts[i].y, parts[i].vx, parts[i].vy, &nrx, &nry)) { + dp = nrx*parts[i].vx + nry*parts[i].vy; + parts[i].vx -= 2.0f*dp*nrx; + parts[i].vy -= 2.0f*dp*nry; + // leave the actual movement until next frame so that reflection of fast particles and refraction happen correctly + } else { + if (t!=PT_NEUT) + kill_part(i); + continue; + } + if (!parts[i].ctype&&t!=PT_NEUT&&t!=PT_ELEC) { + kill_part(i); + continue; + } + } + } + else if (ptypes[t].falldown==0) + { + // gasses and solids (but not powders) + if (!do_move(i, x, y, fin_xf, fin_yf)) + { + // can't move there, so bounce off + // TODO + if (fin_x>x+ISTP) fin_x=x+ISTP; + if (fin_x<x-ISTP) fin_x=x-ISTP; + if (fin_y>y+ISTP) fin_y=y+ISTP; + if (fin_y<y-ISTP) fin_y=y-ISTP; + if (do_move(i, x, y, 0.25f+(float)(2*x-fin_x), 0.25f+fin_y)) + { + parts[i].vx *= ptypes[t].collision; + } + else if (do_move(i, x, y, 0.25f+fin_x, 0.25f+(float)(2*y-fin_y))) + { + parts[i].vy *= ptypes[t].collision; + } + else + { + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + } + } + else + { + if (water_equal_test && ptypes[t].falldown == 2 && 1>= rand()%400)//checking stagnant is cool, but then it doesn't update when you change it later. + { + if (!flood_water(x,y,i,y, parts[i].tmp2)) + goto movedone; + } + // liquids and powders + if (!do_move(i, x, y, fin_xf, fin_yf)) + { + if (fin_x!=x && do_move(i, x, y, fin_xf, clear_yf)) + { + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + else if (fin_y!=y && do_move(i, x, y, clear_xf, fin_yf)) + { + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + else + { + s = 1; + r = (rand()%2)*2-1; + if ((clear_x!=x || clear_y!=y || nt || surround_space) && + (fabsf(parts[i].vx)>0.01f || fabsf(parts[i].vy)>0.01f)) + { + // allow diagonal movement if target position is blocked + // but no point trying this if particle is stuck in a block of identical particles + dx = parts[i].vx - parts[i].vy*r; + dy = parts[i].vy + parts[i].vx*r; + if (fabsf(dy)>fabsf(dx)) + mv = fabsf(dy); + else + mv = fabsf(dx); + dx /= mv; + dy /= mv; + if (do_move(i, x, y, clear_xf+dx, clear_yf+dy)) + { + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + goto movedone; + } + swappage = dx; + dx = dy*r; + dy = -swappage*r; + if (do_move(i, x, y, clear_xf+dx, clear_yf+dy)) + { + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + goto movedone; + } + } + if (ptypes[t].falldown>1 && !ngrav_enable && gravityMode==0 && parts[i].vy>fabsf(parts[i].vx)) + { + s = 0; + // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame + if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle + rt = 30;//slight less water lag, although it changes how it moves a lot + else + rt = 10; + for (j=clear_x+r; j>=0 && j>=clear_x-rt && j<clear_x+rt && j<XRES; j+=r) + { + if (((pmap[fin_y][j]&0xFF)!=t || bmap[fin_y/CELL][j/CELL]) + && (s=do_move(i, x, y, (float)j, fin_yf))) + { + nx = (int)(parts[i].x+0.5f); + ny = (int)(parts[i].y+0.5f); + break; + } + if (fin_y!=clear_y && ((pmap[clear_y][j]&0xFF)!=t || bmap[clear_y/CELL][j/CELL]) + && (s=do_move(i, x, y, (float)j, clear_yf))) + { + nx = (int)(parts[i].x+0.5f); + ny = (int)(parts[i].y+0.5f); + break; + } + if ((pmap[clear_y][j]&0xFF)!=t || (bmap[clear_y/CELL][j/CELL] && bmap[clear_y/CELL][j/CELL]!=WL_STREAM)) + break; + } + if (parts[i].vy>0) + r = 1; + else + r = -1; + if (s==1) + for (j=ny+r; j>=0 && j<YRES && j>=ny-rt && j<ny+rt; j+=r) + { + if (((pmap[j][nx]&0xFF)!=t || bmap[j/CELL][nx/CELL]) && do_move(i, nx, ny, (float)nx, (float)j)) + break; + if ((pmap[j][nx]&255)!=t || (bmap[j/CELL][nx/CELL] && bmap[j/CELL][nx/CELL]!=WL_STREAM)) + break; + } + else if (s==-1) {} // particle is out of bounds + else if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {} + else parts[i].flags |= FLAG_STAGNANT; + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + else if (ptypes[t].falldown>1 && fabsf(pGravX*parts[i].vx+pGravY*parts[i].vy)>fabsf(pGravY*parts[i].vx-pGravX*parts[i].vy)) + { + float nxf, nyf, prev_pGravX, prev_pGravY, ptGrav = ptypes[t].gravity; + s = 0; + // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame + if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle + rt = 30;//slight less water lag, although it changes how it moves a lot + else + rt = 10; + nxf = clear_xf; + nyf = clear_yf; + for (j=0;j<rt;j++) + { + switch (gravityMode) + { + default: + case 0: + pGravX = 0.0f; + pGravY = ptGrav; + break; + case 1: + pGravX = pGravY = 0.0f; + break; + case 2: + pGravD = 0.01f - hypotf((nx - XCNTR), (ny - YCNTR)); + pGravX = ptGrav * ((float)(nx - XCNTR) / pGravD); + pGravY = ptGrav * ((float)(ny - YCNTR) / pGravD); + break; + } + pGravX += gravx[(ny/CELL)*(XRES/CELL)+(nx/CELL)]; + pGravY += gravy[(ny/CELL)*(XRES/CELL)+(nx/CELL)]; + if (fabsf(pGravY)>fabsf(pGravX)) + mv = fabsf(pGravY); + else + mv = fabsf(pGravX); + if (mv<0.0001f) break; + pGravX /= mv; + pGravY /= mv; + if (j) + { + nxf += r*(pGravY*2.0f-prev_pGravY); + nyf += -r*(pGravX*2.0f-prev_pGravX); + } + else + { + nxf += r*pGravY; + nyf += -r*pGravX; + } + prev_pGravX = pGravX; + prev_pGravY = pGravY; + nx = (int)(nxf+0.5f); + ny = (int)(nyf+0.5f); + if (nx<0 || ny<0 || nx>=XRES || ny >=YRES) + break; + if ((pmap[ny][nx]&0xFF)!=t || bmap[ny/CELL][nx/CELL]) + { + s = do_move(i, x, y, nxf, nyf); + if (s) + { + nx = (int)(parts[i].x+0.5f); + ny = (int)(parts[i].y+0.5f); + break; + } + if (bmap[ny/CELL][nx/CELL]!=WL_STREAM) + break; + } + } + if (s==1) + { + clear_x = nx; + clear_y = ny; + for (j=0;j<rt;j++) + { + switch (gravityMode) + { + default: + case 0: + pGravX = 0.0f; + pGravY = ptGrav; + break; + case 1: + pGravX = pGravY = 0.0f; + break; + case 2: + pGravD = 0.01f - hypotf((nx - XCNTR), (ny - YCNTR)); + pGravX = ptGrav * ((float)(nx - XCNTR) / pGravD); + pGravY = ptGrav * ((float)(ny - YCNTR) / pGravD); + break; + } + pGravX += gravx[(ny/CELL)*(XRES/CELL)+(nx/CELL)]; + pGravY += gravy[(ny/CELL)*(XRES/CELL)+(nx/CELL)]; + if (fabsf(pGravY)>fabsf(pGravX)) + mv = fabsf(pGravY); + else + mv = fabsf(pGravX); + if (mv<0.0001f) break; + pGravX /= mv; + pGravY /= mv; + nxf += pGravX; + nyf += pGravY; + nx = (int)(nxf+0.5f); + ny = (int)(nyf+0.5f); + if (nx<0 || ny<0 || nx>=XRES || ny>=YRES) + break; + if ((pmap[ny][nx]&0xFF)!=t || bmap[ny/CELL][nx/CELL]) + { + s = do_move(i, clear_x, clear_y, nxf, nyf); + if (s || bmap[ny/CELL][nx/CELL]!=WL_STREAM) + break; + } + } + } + else if (s==-1) {} // particle is out of bounds + else if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {} + else parts[i].flags |= FLAG_STAGNANT; + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + else + { + // if interpolation was done, try moving to last clear position + if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {} + else parts[i].flags |= FLAG_STAGNANT; + parts[i].vx *= ptypes[t].collision; + parts[i].vy *= ptypes[t].collision; + } + } + } + } +movedone: + continue; + } +} + +void Simulation::update_particles()//doesn't update the particles themselves, but some other things +{ + int i, j, x, y, t, nx, ny, r, cr,cg,cb, l = -1; + float lx, ly; + int lastPartUsed = 0; + int lastPartUnused = -1; +#ifdef MT + int pt = 0, pc = 0; + pthread_t *InterThreads; +#endif + + memset(pmap, 0, sizeof(pmap)); + memset(photons, 0, sizeof(photons)); + NUM_PARTS = 0; + for (i=0; i<=parts_lastActiveIndex; i++)//the particle loop that resets the pmap/photon maps every frame, to update them. + { + if (parts[i].type) + { + t = parts[i].type; + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + if (x>=0 && y>=0 && x<XRES && y<YRES) + { + if (t==PT_PHOT||t==PT_NEUT) + photons[y][x] = t|(i<<8); + else + pmap[y][x] = t|(i<<8); + } + lastPartUsed = i; + NUM_PARTS ++; + } + else + { + if (lastPartUnused<0) pfree = i; + else parts[lastPartUnused].life = i; + lastPartUnused = i; + } + } + if (lastPartUnused==-1) + { + if (parts_lastActiveIndex>=NPART-1) pfree = -1; + else pfree = parts_lastActiveIndex+1; + } + else + { + if (parts_lastActiveIndex>=NPART-1) parts[lastPartUnused].life = -1; + else parts[lastPartUnused].life = parts_lastActiveIndex+1; + } + parts_lastActiveIndex = lastPartUsed; + if (!sys_pause||framerender) + { + for (y=0; y<YRES/CELL; y++) + { + for (x=0; x<XRES/CELL; x++) + { + if (emap[y][x]) + emap[y][x] --; + } + } + } + + update_particles_i(0, 1); + + // this should probably be elsewhere + /*for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + if (bmap[y][x]==WL_STREAM) + { + lx = x*CELL + CELL*0.5f; + ly = y*CELL + CELL*0.5f; + for (t=0; t<1024; t++) + { + nx = (int)(lx+0.5f); + ny = (int)(ly+0.5f); + if (nx<0 || nx>=XRES || ny<0 || ny>=YRES) + break; + addpixel(vid, nx, ny, 255, 255, 255, 64); + i = nx/CELL; + j = ny/CELL; + lx += vx[j][i]*0.125f; + ly += vy[j][i]*0.125f; + if (bmap[j][i]==WL_STREAM && i!=x && j!=y) + break; + } + drawtext(vid, x*CELL, y*CELL-2, "\x8D", 255, 255, 255, 128); + } +*/ +} + +Simulation::Simulation() +{ + //Create and attach gravity simulation + grav = new Gravity(); + //Give air sim references to our data + grav->bmap = bmap; + //Gravity sim gives us maps to use + gravx = grav->gravx; + gravy = grav->gravy; + gravp = grav->gravp; + gravmap = grav->gravmap; + + //Create and attach air simulation + air = new Air(); + //Give air sim references to our data + air->bmap = bmap; + air->emap = emap; + air->fvx = fvx; + air->fvy = fvy; + //Air sim gives us maps to use + vx = air->vx; + vy = air->vy; + pv = air->pv; + hv = air->hv; + + //ptypes[0] = {"", PIXPACK(0x000000), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, 0, R_TEMP+0.0f +273.15f, 251, "Erases particles.", ST_NONE, 0, NULL, NULL}; + //ptypes[1] = {"DUST", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, 30, 1, 1, 85, 0, R_TEMP+0.0f +273.15f, 70, "Very light dust. Flammable.", ST_SOLID, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, NULL, &graphics_DUST}; + //ptypes[2] = {"WATR", PIXPACK(0x2030D0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, 0, R_TEMP-2.0f +273.15f, 29, "Liquid. Conducts electricity. Freezes. Extinguishes fires.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE, &update_WATR, NULL}; + +#define SC_WALL 0 +#define SC_ELEC 1 +#define SC_POWERED 2 +#define SC_EXPLOSIVE 3 +#define SC_GAS 4 +#define SC_LIQUID 5 +#define SC_POWDERS 6 +#define SC_SOLIDS 7 +#define SC_NUCLEAR 8 +#define SC_SPECIAL 9 +#define SC_LIFE 10 +#define SC_TOOL 11 +#define SC_CRACKER 13 +#define SC_CRACKER2 14 +#define SC_TOTAL 12 + + part_type ptypest[PT_NUM] = + { + //Name Colour Advec Airdrag Airloss Loss Collid Grav Diffus Hotair Fal Burn Exp Mel Hrd M Use Weight Section H Ins Description + {"", PIXPACK(0x000000), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Erases particles.", ST_NONE, 0, NULL, NULL}, + {"DUST", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, 30, 1, 1, 85, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Very light dust. Flammable.", ST_SOLID, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, NULL, &graphics_DUST}, + {"WATR", PIXPACK(0x2030D0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, SC_LIQUID, R_TEMP-2.0f +273.15f, 29, "Liquid. Conducts electricity. Freezes. Extinguishes fires.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE, &update_WATR, NULL}, + {"OIL", PIXPACK(0x404010), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 20, 0, 0, 5, 1, 1, 20, SC_LIQUID, R_TEMP+0.0f +273.15f, 42, "Liquid. Flammable.", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"FIRE", PIXPACK(0xFF1000), 0.9f, 0.04f * CFDS, 0.97f, 0.20f, 0.0f, -0.1f, 0.00f, 0.001f * CFDS, 1, 0, 0, 0, 1, 1, 1, 2, SC_EXPLOSIVE, R_TEMP+400.0f+273.15f, 88, "Ignites flammable materials. Heats air.", ST_GAS, TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL, &update_PYRO, &graphics_FIRE}, + {"STNE", PIXPACK(0xA0A0A0), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 5, 1, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 150, "Heavy particles. Meltable.", ST_SOLID, TYPE_PART, NULL, NULL}, + {"LAVA", PIXPACK(0xE05010), 0.3f, 0.02f * CFDS, 0.95f, 0.80f, 0.0f, 0.15f, 0.00f, 0.0003f * CFDS, 2, 0, 0, 0, 2, 1, 1, 45, SC_LIQUID, R_TEMP+1500.0f+273.15f, 60, "Heavy liquid. Ignites flammable materials. Solidifies when cold.", ST_LIQUID, TYPE_LIQUID|PROP_LIFE_DEC, &update_PYRO, &graphics_LAVA}, + {"GUN", PIXPACK(0xC0C0D0), 0.7f, 0.02f * CFDS, 0.94f, 0.80f, -0.1f, 0.1f, 0.00f, 0.000f * CFDS, 1, 600, 1, 0, 10, 1, 1, 85, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 97, "Light dust. Explosive.", ST_SOLID, TYPE_PART, NULL, NULL}, + {"NITR", PIXPACK(0x20E010), 0.5f, 0.02f * CFDS, 0.92f, 0.97f, 0.0f, 0.2f, 0.00f, 0.000f * CFDS, 2, 1000, 2, 0, 3, 1, 1, 23, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 50, "Liquid. Pressure sensitive explosive.", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"CLNE", PIXPACK(0xFFD010), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Solid. Duplicates any particles it touches.", ST_SOLID, TYPE_SOLID, &update_CLNE, NULL}, + {"GAS", PIXPACK(0xE0FF20), 1.0f, 0.01f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 0.75f, 0.001f * CFDS, 0, 600, 0, 0, 1, 1, 1, 1, SC_GAS, R_TEMP+2.0f +273.15f, 42, "Gas. Diffuses. Flammable. Liquefies under pressure.", ST_GAS, TYPE_GAS, NULL, NULL}, + {"C-4", PIXPACK(0xD080E0), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 1000, 2, 50, 1, 1, 1, 100, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 88, "Solid. Pressure sensitive explosive.", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE, NULL, NULL}, + {"GOO", PIXPACK(0x804000), 0.0f, 0.00f * CFDS, 0.97f, 0.50f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 12, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 75, "Solid. Deforms and disappears under pressure.", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_GOO, NULL}, + {"ICE", PIXPACK(0xA0C0FF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, -0.0003f* CFDS, 0, 0, 0, 0, 20, 1, 1, 100, SC_SOLIDS, R_TEMP-50.0f+273.15f, 46, "Solid. Freezes water. Crushes under pressure. Cools down air.", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_ICEI, NULL}, + {"METL", PIXPACK(0x404060), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Solid. Conducts electricity. Meltable.", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, NULL, NULL}, + {"SPRK", PIXPACK(0xFFFF80), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.001f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Electricity. Conducted by metal and water.", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_SPRK, &graphics_SPRK}, + {"SNOW", PIXPACK(0xC0E0FF), 0.7f, 0.01f * CFDS, 0.96f, 0.90f, -0.1f, 0.05f, 0.01f, -0.00005f* CFDS,1, 0, 0, 0, 20, 1, 1, 50, SC_POWDERS, R_TEMP-30.0f+273.15f, 46, "Light particles.", ST_SOLID, TYPE_PART|PROP_LIFE_DEC, &update_ICEI, NULL}, + {"WOOD", PIXPACK(0xC0A040), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 20, 0, 0, 15, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 164, "Solid. Flammable.", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE, NULL, NULL}, + {"NEUT", PIXPACK(0x20E0FF), 0.0f, 0.00f * CFDS, 1.00f, 1.00f, -0.99f, 0.0f, 0.01f, 0.002f * CFDS, 0, 0, 0, 0, 0, 1, 1, -1, SC_NUCLEAR, R_TEMP+4.0f +273.15f, 60, "Neutrons. Interact with matter in odd ways.", ST_GAS, TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_NEUT, &graphics_NEUT}, + {"PLUT", PIXPACK(0x407020), 0.4f, 0.01f * CFDS, 0.99f, 0.95f, 0.0f, 0.4f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 0, 1, 1, 90, SC_NUCLEAR, R_TEMP+4.0f +273.15f, 251, "Heavy particles. Fissile. Generates neutrons under pressure.", ST_SOLID, TYPE_PART|PROP_NEUTPENETRATE|PROP_RADIOACTIVE, &update_PLUT, NULL}, + {"PLNT", PIXPACK(0x0CAC00), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 20, 0, 0, 10, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 65, "Plant, drinks water and grows.", ST_SOLID, TYPE_SOLID|PROP_NEUTPENETRATE|PROP_LIFE_DEC, &update_PLNT, NULL}, + {"ACID", PIXPACK(0xED55FF), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 40, 0, 0, 1, 1, 1, 10, SC_LIQUID, R_TEMP+0.0f +273.15f, 34, "Dissolves almost everything.", ST_LIQUID, TYPE_LIQUID|PROP_DEADLY, &update_ACID, &graphics_ACID}, + {"VOID", PIXPACK(0x790B0B), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, -0.0003f* CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Hole, will drain away any particles.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"WTRV", PIXPACK(0xA0A0FF), 1.0f, 0.01f * CFDS, 0.99f, 0.30f, -0.1f, -0.1f, 0.75f, 0.0003f * CFDS, 0, 0, 0, 0, 4, 1, 1, 1, SC_GAS, R_TEMP+100.0f+273.15f, 48, "Steam, heats up air, produced from hot water.", ST_GAS, TYPE_GAS, &update_WTRV, NULL}, + {"CNCT", PIXPACK(0xC0C0C0), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 2, 2, 1, 1, 55, SC_POWDERS, R_TEMP+0.0f +273.15f, 100, "Concrete, stronger than stone.", ST_SOLID, TYPE_PART|PROP_HOT_GLOW, NULL, NULL}, + {"DSTW", PIXPACK(0x1020C0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, SC_LIQUID, R_TEMP-2.0f +273.15f, 23, "Distilled water, does not conduct electricity.", ST_LIQUID, TYPE_LIQUID|PROP_NEUTPENETRATE, &update_DSTW, NULL}, + {"SALT", PIXPACK(0xFFFFFF), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 5, 1, 1, 1, 75, SC_POWDERS, R_TEMP+0.0f +273.15f, 110, "Salt, dissolves in water.", ST_SOLID, TYPE_PART, NULL, NULL}, + {"SLTW", PIXPACK(0x4050F0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 35, SC_LIQUID, R_TEMP+0.0f +273.15f, 75, "Saltwater, conducts electricity, difficult to freeze.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE, &update_SLTW, NULL}, + {"DMND", PIXPACK(0xCCFFFF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 186, "Diamond. Indestructible.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"BMTL", PIXPACK(0x505070), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 251, "Breakable metal.", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, &update_BMTL, NULL}, + {"BRMT", PIXPACK(0x705060), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 2, 2, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 211, "Broken metal.", ST_SOLID, TYPE_PART|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, &update_BRMT, NULL}, + {"PHOT", PIXPACK(0xFFFFFF), 0.0f, 0.00f * CFDS, 1.00f, 1.00f, -0.99f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, -1, SC_NUCLEAR, R_TEMP+900.0f+273.15f, 251, "Photons. Travel in straight lines.", ST_GAS, TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_PHOT, &graphics_PHOT}, + {"URAN", PIXPACK(0x707020), 0.4f, 0.01f * CFDS, 0.99f, 0.95f, 0.0f, 0.4f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 0, 1, 1, 90, SC_NUCLEAR, R_TEMP+30.0f+273.15f, 251, "Heavy particles. Generates heat under pressure.", ST_SOLID, TYPE_PART | PROP_RADIOACTIVE, &update_URAN, NULL}, + {"WAX", PIXPACK(0xF0F0BB), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 10, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 44, "Wax. Melts at moderately high temperatures.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"MWAX", PIXPACK(0xE0E0AA), 0.3f, 0.02f * CFDS, 0.95f, 0.80f, 0.0f, 0.15f, 0.00f, 0.000001f* CFDS,2, 5, 0, 0, 2, 1, 1, 25, SC_LIQUID, R_TEMP+28.0f+273.15f, 44, "Liquid Wax.", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"PSCN", PIXPACK(0x805050), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "P-Type Silicon, Will transfer current to any conductor.", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"NSCN", PIXPACK(0x505080), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "N-Type Silicon, Will not transfer current to P-Type Silicon.", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"LN2", PIXPACK(0x80A0DF), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 0, 1, 1, 30, SC_LIQUID, 70.15f, 70, "Liquid Nitrogen. Very cold.", ST_SOLID, TYPE_LIQUID, NULL, NULL}, + {"INSL", PIXPACK(0x9EA3B6), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 7, 0, 0, 10, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 0, "Insulator, does not conduct heat or electricity.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"VACU", PIXPACK(0x303030), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, -0.01f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+70.0f+273.15f, 255, "Vacuum, sucks in other particles and heats up.", ST_NONE, TYPE_SOLID, NULL, NULL}, + {"VENT", PIXPACK(0xEFEFEF), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.010f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP-16.0f+273.15f, 255, "Air vent, creates pressure and pushes other particles away.", ST_NONE, TYPE_SOLID, NULL, NULL}, + {"RBDM", PIXPACK(0xCCCCCC), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 1000, 1, 50, 1, 1, 1, 100, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 240, "Rubidium, explosive, especially on contact with water, low melting point", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"LRBD", PIXPACK(0xAAAAAA), 0.3f, 0.02f * CFDS, 0.95f, 0.80f, 0.0f, 0.15f, 0.00f, 0.000001f* CFDS,2, 1000, 1, 0, 2, 1, 1, 45, SC_EXPLOSIVE, R_TEMP+45.0f+273.15f, 170, "Liquid Rubidium.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"NTCT", PIXPACK(0x505040), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Semi-conductor. Only conducts electricity when hot (More than 100C)", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, &update_NPTCT, NULL}, + {"SAND", PIXPACK(0xFFD090), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 5, 1, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 150, "Sand, Heavy particles. Meltable.", ST_SOLID, TYPE_PART, NULL, NULL}, + {"GLAS", PIXPACK(0x404040), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 150, "Solid. Meltable. Shatters under pressure", ST_SOLID, TYPE_SOLID | PROP_NEUTPASS | PROP_HOT_GLOW | PROP_SPARKSETTLE, &update_GLAS, NULL}, + {"PTCT", PIXPACK(0x405050), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Semi-conductor. Only conducts electricity when cold (Less than 100C)", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, &update_NPTCT, NULL}, + {"BGLA", PIXPACK(0x606060), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 5, 2, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 150, "Broken Glass, Heavy particles. Meltable. Bagels.", ST_SOLID, TYPE_PART | PROP_HOT_GLOW, NULL, NULL}, + {"THDR", PIXPACK(0xFFFFA0), 0.0f, 0.00f * CFDS, 1.0f, 0.30f, -0.99f, 0.6f, 0.62f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 1, SC_EXPLOSIVE, 9000.0f +273.15f, 1, "Lightning! Very hot, inflicts damage upon most materials, transfers current to metals.", ST_NONE, TYPE_ENERGY, &update_THDR, &graphics_THDR}, + {"PLSM", PIXPACK(0xBB99FF), 0.9f, 0.04f * CFDS, 0.97f, 0.20f, 0.0f, -0.1f, 0.30f, 0.001f * CFDS, 0, 0, 0, 0, 0, 1, 1, 1, SC_GAS, 10000.0f +273.15f, 5, "Plasma, extremely hot.", ST_NONE, TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL, &update_PYRO, &graphics_PLSM}, + {"ETRD", PIXPACK(0x404040), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Electrode. Creates a surface that allows Plasma arcs. (Use sparingly)", ST_NONE, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"NICE", PIXPACK(0xC0E0FF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, -0.0005f* CFDS, 0, 0, 0, 0, 20, 1, 1, 100, SC_SOLIDS, 35.0f, 46, "Nitrogen Ice.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"NBLE", PIXPACK(0xEB4917), 1.0f, 0.01f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 0.75f, 0.001f * CFDS, 0, 0, 0, 0, 1, 1, 1, 1, SC_GAS, R_TEMP+2.0f +273.15f, 106, "Noble Gas. Diffuses. Conductive. Ionizes into plasma when introduced to electricity", ST_GAS, TYPE_GAS|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"BTRY", PIXPACK(0x858505), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Solid. Generates Electricity.", ST_SOLID, TYPE_SOLID, &update_BTRY, NULL}, + {"LCRY", PIXPACK(0x505050), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Liquid Crystal. Changes colour when charged. (PSCN Charges, NSCN Discharges)", ST_SOLID, TYPE_SOLID, &update_LCRY, &graphics_LCRY}, + {"STKM", PIXPACK(0x000000), 0.5f, 0.00f * CFDS, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.00f * CFDS, 0, 0, 0, 0, 0, 1, 1, 50, SC_SPECIAL, R_TEMP+14.6f+273.15f, 0, "Stickman. Don't kill him!", ST_NONE, 0, &update_STKM, &graphics_STKM}, + {"SWCH", PIXPACK(0x103B11), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Solid. Only conducts when switched on. (PSCN switches on, NSCN switches off)", ST_SOLID, TYPE_SOLID, &update_SWCH, &graphics_SWCH}, + {"SMKE", PIXPACK(0x222222), 0.9f, 0.04f * CFDS, 0.97f, 0.20f, 0.0f, -0.1f, 0.00f, 0.001f * CFDS, 1, 0, 0, 0, 1, 1, 1, 1, SC_GAS, R_TEMP+320.0f+273.15f, 88, "Smoke", ST_SOLID, TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, NULL, &graphics_SMKE}, + {"DESL", PIXPACK(0x440000), 1.0f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.0f, 0.0f * CFDS, 2, 2, 0, 0, 5, 1, 1, 15, SC_LIQUID, R_TEMP+0.0f +273.15f, 42, "Liquid. Explodes under high pressure and temperatures", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"COAL", PIXPACK(0x222222), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.0f, 0.0f * CFDS, 0, 0, 0, 0, 20, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 200, "Solid. Burns slowly.", ST_SOLID, TYPE_SOLID, &update_COAL, &graphics_COAL}, + {"LOXY", PIXPACK(0x80A0EF), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 5000, 0, 0, 0, 1, 1, 30, SC_LIQUID, 80.0f, 70, "Liquid Oxygen. Very cold. Reacts with fire", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"OXYG", PIXPACK(0x80A0FF), 2.0f, 0.00f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 3.0f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 1, SC_GAS, R_TEMP+0.0f +273.15f, 70, "Gas. Ignites easily.", ST_GAS, TYPE_GAS, &update_O2, NULL}, + {"INWR", PIXPACK(0x544141), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Insulated Wire. Doesn't conduct to metal or semiconductors.", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC, NULL, NULL}, + {"YEST", PIXPACK(0xEEE0C0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 15, 0, 0, 30, 1, 1, 80, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Yeast, grows when warm (~37C).", ST_SOLID, TYPE_PART, &update_YEST, NULL}, + {"DYST", PIXPACK(0xBBB0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 20, 0, 0, 30, 0, 1, 80, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Dead Yeast.", ST_SOLID, TYPE_PART, NULL, NULL}, + {"THRM", PIXPACK(0xA08090), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 2, 2, 1, 1, 90, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 211, "Thermite. Burns at extremely high temperature.", ST_SOLID, TYPE_PART, &update_THRM, NULL}, + {"GLOW", PIXPACK(0x445464), 0.3f, 0.02f * CFDS, 0.98f, 0.80f, 0.0f, 0.15f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 2, 1, 1, 40, SC_LIQUID, R_TEMP+20.0f+273.15f, 44, "Glow, Glows under pressure", ST_LIQUID, TYPE_LIQUID|PROP_LIFE_DEC, &update_GLOW, &graphics_GLOW}, + {"BRCK", PIXPACK(0x808080), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 251, "Brick, breakable building material.", ST_SOLID, TYPE_SOLID|PROP_HOT_GLOW, NULL, NULL}, + {"CFLM", PIXPACK(0x8080FF), 0.9f, 0.04f * CFDS, 0.97f, 0.20f, 0.0f, -0.1f, 0.00f, 0.0005f * CFDS, 1, 0, 0, 0, 1, 1, 1, 2, SC_EXPLOSIVE, 0.0f, 88, "Sub-zero flame.", ST_LIQUID, TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL, NULL, &graphics_HFLM}, + {"FIRW", PIXPACK(0xFFA040), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, -0.99f, 0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 30, 1, 1, 55, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 70, "Fireworks!", ST_SOLID, TYPE_PART|PROP_LIFE_DEC, &update_FIRW, &graphics_FIRW}, + {"FUSE", PIXPACK(0x0A5706), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.0f, 0.0f * CFDS, 0, 0, 0, 0, 20, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 200, "Solid. Burns slowly. Ignites at somewhat high temperatures and electricity.", ST_SOLID, TYPE_SOLID, &update_FUSE, NULL}, + {"FSEP", PIXPACK(0x63AD5F), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 30, 1, 1, 70, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Fuse Powder. See FUSE.", ST_SOLID, TYPE_PART, &update_FSEP, NULL}, + {"AMTR", PIXPACK(0x808080), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.00f, 0.10f, 1.00f, 0.0000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_NUCLEAR, R_TEMP+0.0f +273.15f, 70, "Anti-Matter, Destroys a majority of particles", ST_NONE, TYPE_PART, &update_AMTR, NULL}, //Maybe TYPE_ENERGY? + {"BCOL", PIXPACK(0x333333), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.3f, 0.00f, 0.000f * CFDS, 1, 0, 0, 5, 2, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 150, "Broken Coal. Heavy particles. See COAL", ST_SOLID, TYPE_PART, &update_BCOL, NULL}, + {"PCLN", PIXPACK(0x3B3B0A), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Solid. When activated, duplicates any particles it touches.", ST_NONE, TYPE_SOLID, &update_PCLN, &graphics_PCLN}, + {"HSWC", PIXPACK(0x3B0A0A), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Heat switch. Conducts Heat only when activated", ST_NONE, TYPE_SOLID, &update_HSWC, &graphics_HSWC}, + {"IRON", PIXPACK(0x707070), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 50, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 251, "Rusts with salt, can be used for electrolysis of WATR", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, &update_IRON, NULL}, + {"MORT", PIXPACK(0xE0E0E0), 0.0f, 0.00f * CFDS, 1.00f, 1.00f, -0.99f, 0.0f, 0.01f, 0.002f * CFDS, 0, 0, 0, 0, 0, 1, 1, -1, SC_CRACKER2, R_TEMP+4.0f +273.15f, 60, "Steam Train.", ST_NONE, TYPE_PART, &update_MORT, NULL}, + {"LIFE", PIXPACK(0x0CAC00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 1, 100, SC_LIFE, 9000.0f, 40, "Game Of Life! B3/S23", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, &graphics_LIFE}, + {"DLAY", PIXPACK(0x753590), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_POWERED, 4.0f+273.15f, 0, "Conducts with temperature-dependent delay. (use HEAT/COOL).", ST_SOLID, TYPE_SOLID, &update_DLAY, &graphics_DLAY}, + {"CO2", PIXPACK(0x666666), 2.0f, 0.00f * CFDS, 0.99f, 0.30f, -0.1f, 0.1f, 1.0f, 0.000f * CFDS, 1, 0, 0, 0, 0, 1, 1, 1, SC_GAS, R_TEMP+273.15f, 88, "Carbon Dioxide", ST_GAS, TYPE_GAS, &update_CO2, NULL}, + {"DRIC", PIXPACK(0xE0E0E0), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, -0.0005f* CFDS, 0, 0, 0, 0, 20, 1, 1, 100, SC_SOLIDS, 172.65f, 2, "Dry Ice.", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"BUBW", PIXPACK(0x2030D0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, SC_LIQUID, R_TEMP-2.0f +273.15f, 29, "Carbonated water. Conducts electricity. Freezes. Extinguishes fires.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE, &update_CBNW, &graphics_CBNW}, + {"STOR", PIXPACK(0x50DFDF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Solid. Stores a single particle, releases when charged with PSCN, also passes to PIPE", ST_NONE, TYPE_SOLID, &update_STOR, &graphics_STOR}, + {"PVOD", PIXPACK(0x792020), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Solid. When activated, destroys entering particles", ST_NONE, TYPE_SOLID, &update_PVOD, &graphics_PVOD}, + {"CONV", PIXPACK(0x0AAB0A), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Solid. Converts whatever touches it into its ctype.", ST_NONE, TYPE_SOLID, &update_CONV, NULL}, + {"CAUS", PIXPACK(0x80FFA0), 2.0f, 0.00f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 1.50f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 1, SC_GAS, R_TEMP+0.0f +273.15f, 70, "Caustic Gas, acts like Acid", ST_GAS, TYPE_GAS|PROP_DEADLY, &update_CAUS, NULL}, + {"LIGH", PIXPACK(0xFFFFC0), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 0, "More realistic lightning. Set pen size to set the size of the lightning.", ST_SOLID, TYPE_SOLID, &update_LIGH, &graphics_LIGH}, + {"TESC", PIXPACK(0x707040), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Tesla coil!", ST_SOLID, TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, NULL, NULL}, + {"DEST", PIXPACK(0xFF3311), -0.05f, 0.00f * CFDS, 0.95f, 0.95f, -0.1f, 0.4f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 0, 1, 1, 101, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 150, "More destructive Bomb.", ST_SOLID, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_DEST, &graphics_DEST}, + {"SPNG", PIXPACK(0xFFBE30), 0.00f, 0.00f * CFDS, 0.00f, 1.00f, 0.00f, 0.0f, 0.00f, 0.000f * CFDS, 0, 20, 0, 1, 30, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 251, "A sponge, absorbs water.", ST_SOLID, TYPE_SOLID, &update_SPNG, &graphics_SPNG}, + {"RIME", PIXPACK(0xCCCCCC), 0.00f, 0.00f * CFDS, 0.00f, 1.00f, 0.00f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 30, 1, 1, 100, SC_CRACKER2, 243.15f, 100, "Not quite Ice", ST_SOLID, TYPE_SOLID, &update_RIME, NULL}, + {"FOG", PIXPACK(0xAAAAAA), 0.8f, 0.00f * CFDS, 0.4f, 0.70f, -0.1f, 0.0f, 0.99f, 0.000f * CFDS, 0, 0, 0, 0, 30, 1, 1, 1, SC_CRACKER2, 243.15f, 100, "Not quite Steam", ST_GAS, TYPE_GAS|PROP_LIFE_DEC, &update_FOG, NULL}, + {"BCLN", PIXPACK(0xFFD040), 0.0f, 0.00f * CFDS, 0.97f, 0.50f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 12, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Breakable Clone.", ST_NONE, TYPE_SOLID|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_BCLN, NULL}, + {"LOVE", PIXPACK(0xFF30FF), 0.0f, 0.00f * CFDS, 0.00f, 0.00f, 0.0f, 0.0f, 0.0f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_CRACKER2, 373.0f, 40, "Love...", ST_GAS, TYPE_SOLID, &update_MISC, NULL}, + {"DEUT", PIXPACK(0x00153F), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 31, SC_NUCLEAR, R_TEMP-2.0f +273.15f, 251, "Deuterium oxide. Volume changes with temp, radioactive with neutrons.", ST_LIQUID, TYPE_LIQUID|PROP_NEUTPENETRATE, &update_DEUT, &graphics_DEUT}, + {"WARP", PIXPACK(0x000000), 0.8f, 0.00f * CFDS, 0.9f, 0.70f, -0.1f, 0.0f, 3.00f, 0.000f * CFDS, 0, 0, 0, 0, 30, 1, 1, 1, SC_NUCLEAR, R_TEMP +273.15f, 100, "Displaces other elements.", ST_GAS, TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL, &update_WARP, NULL}, + {"PUMP", PIXPACK(0x0A0A3B), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 10, 1, 1, 100, SC_POWERED, 273.15f, 0, "Changes pressure to its temp when activated. (use HEAT/COOL).", ST_SOLID, TYPE_SOLID, &update_PUMP, &graphics_PUMP}, + {"FWRK", PIXPACK(0x666666), 0.4f, 0.01f * CFDS, 0.99f, 0.95f, 0.0f, 0.4f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 1, 1, 1, 97, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 100, "First fireworks made, activated by heat/neutrons.", ST_SOLID, TYPE_PART|PROP_LIFE_DEC, &update_FWRK, NULL}, + {"PIPE", PIXPACK(0x444444), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SOLIDS, 273.15f, 0, "Moves elements around, read FAQ on website for help.", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_PIPE, &graphics_PIPE}, + {"FRZZ", PIXPACK(0xC0E0FF), 0.7f, 0.01f * CFDS, 0.96f, 0.90f, -0.1f, 0.05f, 0.01f, -0.00005f* CFDS,1, 0, 0, 0, 20, 1, 1, 50, SC_POWDERS, 90.0f, 46, "FREEZE", ST_SOLID, TYPE_PART, &update_FRZZ, NULL}, + {"FRZW", PIXPACK(0x1020C0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, SC_CRACKER2, 120.0f, 29, "FREEZE WATER", ST_LIQUID, TYPE_LIQUID||PROP_LIFE_DEC, &update_FRZW, NULL}, + {"GRAV", PIXPACK(0xFFE0A0), 0.7f, 0.00f * CFDS, 1.00f, 1.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, 30, 1, 1, 85, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Very light dust. Changes colour based on velocity.", ST_SOLID, TYPE_PART, &update_MISC, &graphics_GRAV}, + {"BIZR", PIXPACK(0x00FF77), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 30, SC_LIQUID, R_TEMP+0.0f +273.15f, 29, "Bizarre... contradicts the normal state changes.", ST_LIQUID, TYPE_LIQUID, &update_BIZR, &graphics_BIZR}, + {"BIZG", PIXPACK(0x00FFBB), 1.0f, 0.01f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 2.75f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 1, SC_CRACKER2, R_TEMP-200.0f+273.15f, 42, "Bizarre gas", ST_GAS, TYPE_GAS, &update_BIZR, &graphics_BIZR}, + {"BIZS", PIXPACK(0x00E455), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_CRACKER2, R_TEMP+300.0f+273.15f, 251, "Bizarre solid", ST_SOLID, TYPE_SOLID, &update_BIZR, &graphics_BIZR}, + {"INST", PIXPACK(0x404039), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Instantly conducts, PSCN to charge, NSCN to take.", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, NULL, NULL}, + {"ISOZ", PIXPACK(0xAA30D0), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 0, 1, 1, 24, SC_NUCLEAR, R_TEMP-2.0f +273.15f, 29, "Radioactive liquid", ST_LIQUID, TYPE_LIQUID|PROP_NEUTPENETRATE, &update_ISZ, NULL}, + {"ISZS", PIXPACK(0x662089), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, -0.0007f* CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_NUCLEAR, 140.00f, 251, "Solid form of ISOZ, slowly decays.", ST_SOLID, TYPE_SOLID, &update_ISZ, NULL}, + {"PRTI", PIXPACK(0xEB5917), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, -0.005f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 0, "Portal IN. Things go in here, now with channels (same as WIFI)", ST_SOLID, TYPE_SOLID, &update_PRTI, &graphics_PRTI}, + {"PRTO", PIXPACK(0x0020EB), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.005f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 0, "Portal OUT. Things come out here, now with channels (same as WIFI)", ST_SOLID, TYPE_SOLID, &update_PRTO, &graphics_PRTO}, + {"PSTE", PIXPACK(0xAA99AA), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 31, SC_LIQUID, R_TEMP-2.0f +273.15f, 29, "Colloid, Hardens under pressure", ST_LIQUID, TYPE_LIQUID, NULL, NULL}, + {"PSTS", PIXPACK(0x776677), 0.0f, 0.00f * CFDS, 0.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 20, 0, 1, 100, SC_CRACKER, R_TEMP-2.0f +273.15f, 29, "Solid form of PSTE, temporary", ST_SOLID, TYPE_SOLID, NULL, NULL}, + {"ANAR", PIXPACK(0xFFFFEE), -0.7f, -0.02f * CFDS, 0.96f, 0.80f, 0.1f, -0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 30, 1, 1, 85, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Very light dust. Behaves opposite gravity", ST_SOLID, TYPE_PART, &update_ANAR, NULL}, + {"VINE", PIXPACK(0x079A00), 0.0f, 0.00f * CFDS, 0.95f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 20, 0, 0, 10, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 65, "Vine, grows", ST_SOLID, TYPE_SOLID, &update_VINE, NULL}, + {"INVS", PIXPACK(0x00CCCC), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 15, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 164, "Invisible to everything while under pressure.", ST_SOLID, TYPE_SOLID | PROP_NEUTPASS, NULL, &graphics_INVS}, + {"EQVE", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 30, 0, 1, 85, SC_CRACKER2, R_TEMP+0.0f +273.15f, 70, "Shared velocity test", ST_SOLID, TYPE_PART, NULL, NULL}, + {"SPWN2", PIXPACK(0xAAAAAA), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 0, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 0, "STK2 spawn point", ST_SOLID, TYPE_SOLID, &update_SPAWN2, NULL}, + {"SPWN", PIXPACK(0xAAAAAA), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 0, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 0, "STKM spawn point", ST_SOLID, TYPE_SOLID, &update_SPAWN, NULL}, + {"SHLD", PIXPACK(0xAAAAAA), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 0, "Shield, spark it to grow", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_SHLD1, NULL}, + {"SHD2", PIXPACK(0x777777), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 0, 1, 100, SC_CRACKER2, R_TEMP+0.0f +273.15f, 0, "Shield lvl 2", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_SHLD2, NULL}, + {"SHD3", PIXPACK(0x444444), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 0, 1, 100, SC_CRACKER2, R_TEMP+0.0f +273.15f, 0, "Shield lvl 3", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_SHLD3, NULL}, + {"SHD4", PIXPACK(0x212121), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 0, 1, 100, SC_CRACKER2, R_TEMP+0.0f +273.15f, 0, "Shield lvl 4", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_SHLD4, NULL}, + {"LOLZ", PIXPACK(0x569212), 0.0f, 0.00f * CFDS, 0.00f, 0.00f, 0.0f, 0.0f, 0.0f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_CRACKER2, 373.0f, 40, "Lolz", ST_GAS, TYPE_SOLID, &update_MISC, NULL}, + {"WIFI", PIXPACK(0x40A060), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 2, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 0, "Wireless transmitter, color coded.", ST_SOLID, TYPE_SOLID, &update_WIFI, &graphics_WIFI}, + {"FILT", PIXPACK(0x000056), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 251, "Filter for photons, changes the color.", ST_SOLID, TYPE_SOLID, NULL, &graphics_FILT}, + {"ARAY", PIXPACK(0xFFBB00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 0, "Ray Emitter. Rays create points when they collide", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_ARAY, NULL}, + {"BRAY", PIXPACK(0xFFFFFF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 0, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Ray Point. Rays create points when they collide", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC|PROP_LIFE_KILL, NULL, &graphics_BRAY}, + {"STK2", PIXPACK(0x000000), 0.5f, 0.00f * CFDS, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.00f * CFDS, 0, 0, 0, 0, 0, 1, 1, 50, SC_SPECIAL, R_TEMP+14.6f+273.15f, 0, "Stickman. Don't kill him!", ST_NONE, 0, &update_STKM2, &graphics_STKM2}, + {"BOMB", PIXPACK(0xFFF288), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 20, 1, 1, 30, SC_EXPLOSIVE, R_TEMP-2.0f +273.15f, 29, "Bomb.", ST_NONE, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC|PROP_SPARKSETTLE, &update_BOMB, &graphics_BOMB}, + {"C-5", PIXPACK(0x2050E0), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 88, "Cold explosive", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE, &update_C5, NULL}, + {"SING", PIXPACK(0x242424), 0.7f, 0.36f * CFDS, 0.96f, 0.80f, 0.1f, 0.12f, 0.00f, -0.001f * CFDS, 1, 0, 0, 0, 0, 1, 1, 86, SC_NUCLEAR, R_TEMP+0.0f +273.15f, 70, "Singularity", ST_SOLID, TYPE_PART|PROP_LIFE_DEC, &update_SING, NULL}, + {"QRTZ", PIXPACK(0xAADDDD), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SOLIDS, R_TEMP+0.0f +273.15f, 3, "Quartz, breakable mineral. Conducts but becomes brittle at lower temperatures.", ST_SOLID, TYPE_SOLID|PROP_HOT_GLOW|PROP_LIFE_DEC, &update_QRTZ, &graphics_QRTZ}, + {"PQRT", PIXPACK(0x88BBBB), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.27f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 0, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 3, "Broken quartz.", ST_SOLID, TYPE_PART| PROP_HOT_GLOW, &update_QRTZ, &graphics_QRTZ}, + {"EMP", PIXPACK(0x66AAFF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.0f, 0.0f * CFDS, 0, 0, 0, 0, 3, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 121, "Breaks activated electronics.", ST_SOLID, TYPE_SOLID|PROP_LIFE_DEC, &update_EMP, &graphics_EMP}, + {"BREL", PIXPACK(0x707060), 0.4f, 0.04f * CFDS, 0.94f, 0.95f, -0.1f, 0.18f, 0.00f, 0.000f * CFDS, 1, 0, 0, 2, 2, 1, 1, 90, SC_POWDERS, R_TEMP+0.0f +273.15f, 211, "Broken electronics", ST_SOLID, TYPE_PART|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW, NULL, NULL}, + {"ELEC", PIXPACK(0xDFEFFF), 0.0f, 0.00f * CFDS, 1.00f, 1.00f, -0.99f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, -1, SC_NUCLEAR, R_TEMP+200.0f+273.15f, 251, "Electrons", ST_GAS, TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_ELEC, &graphics_ELEC}, + {"ACEL", PIXPACK(0x0099CC), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Accelerator", ST_NONE, TYPE_SOLID, &update_ACEL, &graphics_ACEL}, + {"DCEL", PIXPACK(0x99CC00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Decelerator", ST_NONE, TYPE_SOLID, &update_DCEL, &graphics_DCEL}, + {"TNT", PIXPACK(0xC05050), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 88, "Explosive.", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE, &update_BANG, NULL}, + {"IGNC", PIXPACK(0xC0B050), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 100, SC_EXPLOSIVE, R_TEMP+0.0f +273.15f, 88, "Ignition cord.", ST_SOLID, TYPE_SOLID | PROP_NEUTPENETRATE | PROP_SPARKSETTLE | PROP_LIFE_KILL, &update_IGNT, NULL}, + {"BOYL", PIXPACK(0x0A3200), 1.0f, 0.01f * CFDS, 0.99f, 0.30f, -0.1f, 0.0f, 0.18f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, 1, SC_GAS, R_TEMP+2.0f +273.15f, 42, "Boyle, variable pressure gas. Expands when heated.", ST_GAS, TYPE_GAS, &update_BOYL, NULL}, + /*FREE*/{"LOTE", PIXPACK(0xFF0000), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_LIFE, 9000.0f, 40, "Behaves kinda like Living on the Edge S3458/B37/4", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, NULL}, + /*FREE*/{"FRG2", PIXPACK(0x00FF00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_LIFE, 9000.0f, 40, "Like Frogs rule S124/B3/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, NULL}, + /*FREE*/{"STAR", PIXPACK(0x0000FF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_LIFE, 9000.0f, 40, "Like Star Wars rule S3456/B278/6", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, NULL}, + /*FREE*/{"FROG", PIXPACK(0x00AA00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_LIFE, 9000.0f, 40, "Frogs S12/B34/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, NULL}, + /*FREE*/{"BRAN", PIXPACK(0xCCCC00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_LIFE, 9000.0f, 40, "Brian 6 S6/B246/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL, NULL}, + /*FREE*/{"WIND", PIXPACK(0x101010), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 0, 0, 100, SC_SPECIAL, 0.0f, 40, "", ST_NONE, ST_NONE, NULL, NULL}, + {"HYGN", PIXPACK(0x5070FF), 2.0f, 0.00f * CFDS, 0.99f, 0.30f, -0.10f, 0.00f, 3.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 1, SC_GAS, R_TEMP+0.0f +273.15f, 251, "Combines with O2 to make WATR", ST_GAS, TYPE_GAS, &update_H2, NULL}, + {"SOAP", PIXPACK(0xF5F5DC), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 35, SC_LIQUID, R_TEMP-2.0f +273.15f, 29, "Soap. Creates bubbles.", ST_LIQUID, TYPE_LIQUID|PROP_NEUTPENETRATE|PROP_LIFE_DEC, &update_SOAP, NULL}, + {"BHOL", PIXPACK(0x202020), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 186, "Black hole (Requires newtonian gravity)", ST_SOLID, TYPE_SOLID, &update_NBHL, NULL}, + {"WHOL", PIXPACK(0xFFFFFF), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 186, "White hole (Requires newtonian gravity)", ST_SOLID, TYPE_SOLID, &update_NWHL, NULL}, + {"MERC", PIXPACK(0x736B6D), 0.4f, 0.04f * CFDS, 0.94f, 0.80f, 0.0f, 0.3f, 0.00f, 0.000f * CFDS, 2, 0, 0, 0, 20, 1, 1, 91, SC_ELEC, R_TEMP+0.0f +273.15f, 251, "Mercury. Volume changes with temperature, Conductive.", ST_LIQUID, TYPE_LIQUID|PROP_CONDUCTS|PROP_NEUTABSORB|PROP_LIFE_DEC, &update_MERC, NULL}, + {"PBCN", PIXPACK(0x3B1D0A), 0.0f, 0.00f * CFDS, 0.97f, 0.50f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 12, 1, 1, 100, SC_POWERED, R_TEMP+0.0f +273.15f, 251, "Powered breakable clone", ST_NONE, TYPE_SOLID, &update_PBCN, &graphics_PBCN}, + {"GPMP", PIXPACK(0x0A3B3B), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 1, 1, 1, 1, 100, SC_POWERED, 0.0f +273.15f, 0, "Changes gravity to its temp when activated. (use HEAT/COOL).", ST_NONE, TYPE_SOLID, &update_GPMP, &graphics_GPMP}, + {"CLST", PIXPACK(0xE4A4A4), 0.7f, 0.02f * CFDS, 0.94f, 0.95f, 0.0f, 0.2f, 0.00f, 0.000f * CFDS, 1, 0, 0, 2, 2, 1, 1, 55, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Clay dust. Produces paste when mixed with water.", ST_SOLID, TYPE_PART, &update_CLST, &graphics_CLST}, + {"WIRE", PIXPACK(0xFFCC00), 0.0f, 0.00f * CFDS, 0.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 1, 100, SC_ELEC, R_TEMP+0.0f +273.15f, 250, "WireWorld wires.",ST_SOLID,TYPE_SOLID,&update_WIRE, &graphics_WIRE}, + {"GBMB", PIXPACK(0x1144BB), 0.6f, 0.01f * CFDS, 0.98f, 0.95f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 20, 1, 1, 30, SC_EXPLOSIVE, R_TEMP-2.0f +273.15f, 29, "Sticks to first object it touches then produces strong gravity push.", ST_NONE, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC, &update_GBMB, &graphics_GBMB}, + {"FIGH", PIXPACK(0x000000), 0.5f, 0.00f * CFDS, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.00f * CFDS, 0, 0, 0, 0, 0, 1, 1, 50, SC_SPECIAL, R_TEMP+14.6f+273.15f, 0, "Fighter. Tries to kill stickmen.", ST_NONE, 0, &update_FIGH, &graphics_FIGH}, + //Name Colour Advec Airdrag Airloss Loss Collid Grav Diffus Hotair Fal Burn Exp Mel Hrd M Use Weight Section H Ins Description + }; + memcpy(ptypes, ptypest, sizeof(part_type)*PT_NUM); + +#define IPL -257.0f +#define IPH 257.0f +#define ITL MIN_TEMP-1 +#define ITH MAX_TEMP+1 +// no transition (PT_NONE means kill part) +#define NT -1 +// special transition - lava ctypes etc need extra code, which is only found and run if ST is given +#define ST PT_NUM +part_transition ptransitionst[PT_NUM] = +{ // if low pressure if high pressure if low temperature if high temperature + // Name plv plt phv pht tlv tlt thv tht + /* NONE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* DUST */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WATR */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV}, + /* OIL */ {IPL, NT, IPH, NT, ITL, NT, 333.0f, PT_GAS}, + /* FIRE */ {IPL, NT, IPH, NT, ITL, NT, 2773.0f,PT_PLSM}, + /* STNE */ {IPL, NT, IPH, NT, ITL, NT, 983.0f, PT_LAVA}, + /* LAVA */ {IPL, NT, IPH, NT, 2573.15f,ST, ITH, NT}, // 2573.15f is highest melt pt of possible ctypes + /* GUN */ {IPL, NT, IPH, NT, ITL, NT, 673.0f, PT_FIRE}, + /* NITR */ {IPL, NT, IPH, NT, ITL, NT, 673.0f, PT_FIRE}, + /* CLNE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* GAS */ {IPL, NT, 6.0f, PT_OIL, ITL, NT, 573.0f, PT_FIRE}, + /* C-4 */ {IPL, NT, IPH, NT, ITL, NT, 673.0f, PT_FIRE}, + /* GOO */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ICE */ {IPL, NT, 0.8f, PT_SNOW, ITL, NT, 233.0f, ST}, + /* METL */ {IPL, NT, IPH, NT, ITL, NT, 1273.0f,PT_LAVA}, + /* SPRK */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SNOW */ {IPL, NT, IPH, NT, ITL, NT, 273.0f, PT_WATR}, + /* WOOD */ {IPL, NT, IPH, NT, ITL, NT, 873.0f, PT_FIRE}, + /* NEUT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PLUT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PLNT */ {IPL, NT, IPH, NT, ITL, NT, 573.0f, PT_FIRE}, + /* ACID */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* VOID */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WTRV */ {IPL, NT, IPH, NT, 371.0f, ST, ITH, NT}, + /* CNCT */ {IPL, NT, IPH, NT, ITL, NT, 1123.0f,PT_LAVA}, + /* DSTW */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV}, + /* SALT */ {IPL, NT, IPH, NT, ITL, NT, 1173.0f,PT_LAVA}, + /* SLTW */ {IPL, NT, IPH, NT, 233.0f, PT_ICEI, 483.0f, ST}, + /* DMND */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BMTL */ {IPL, NT, 1.0f, ST, ITL, NT, 1273.0f,PT_LAVA}, + /* BRMT */ {IPL, NT, IPH, NT, ITL, NT, 1273.0f,PT_LAVA}, + /* PHOT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* URAN */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WAX */ {IPL, NT, IPH, NT, ITL, NT, 319.0f, PT_MWAX}, + /* MWAX */ {IPL, NT, IPH, NT, 318.0f, PT_WAX, 673.0f, PT_FIRE}, + /* PSCN */ {IPL, NT, IPH, NT, ITL, NT, 1687.0f,PT_LAVA}, + /* NSCN */ {IPL, NT, IPH, NT, ITL, NT, 1687.0f,PT_LAVA}, + /* LN2 */ {IPL, NT, IPH, NT, 63.0f, PT_NICE, 77.0f, PT_NONE}, + /* INSL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* VACU */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* VENT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* RBDM */ {IPL, NT, IPH, NT, ITL, NT, 312.0f, PT_LRBD}, + /* LRBD */ {IPL, NT, IPH, NT, 311.0f, PT_RBDM, 961.0f, PT_FIRE}, + /* NTCT */ {IPL, NT, IPH, NT, ITL, NT, 1687.0f,PT_LAVA}, + /* SAND */ {IPL, NT, IPH, NT, ITL, NT, 1973.0f,PT_LAVA}, + /* GLAS */ {IPL, NT, IPH, NT, ITL, NT, 1973.0f,PT_LAVA}, + /* PTCT */ {IPL, NT, IPH, NT, ITL, NT, 1414.0f,PT_LAVA}, + /* BGLA */ {IPL, NT, IPH, NT, ITL, NT, 1973.0f,PT_LAVA}, + /* THDR */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PLSM */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ETRD */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* NICE */ {IPL, NT, IPH, NT, ITL, NT, 63.1f, PT_LNTG}, + /* NBLE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BTRY */ {IPL, NT, IPH, NT, ITL, NT, 2273.0f,PT_PLSM}, + /* LCRY */ {IPL, NT, IPH, NT, ITL, NT, 1273.0f,PT_BGLA}, + /* STKM */ {IPL, NT, IPH, NT, ITL, NT, 620.0f, PT_FIRE}, + /* SWCH */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SMKE */ {IPL, NT, IPH, NT, ITL, NT, 625.0f, PT_FIRE}, + /* DESL */ {IPL, NT, 5.0f, PT_FIRE, ITL, NT, 335.0f, PT_FIRE}, + /* COAL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* LO2 */ {IPL, NT, IPH, NT, ITL, NT, 90.1f, PT_O2}, + /* O2 */ {IPL, NT, IPH, NT, 90.0f, PT_LO2, ITH, NT}, + /* INWR */ {IPL, NT, IPH, NT, ITL, NT, 1687.0f,PT_LAVA}, + /* YEST */ {IPL, NT, IPH, NT, ITL, NT, 373.0f, PT_DYST}, + /* DYST */ {IPL, NT, IPH, NT, ITL, NT, 473.0f, PT_DUST}, + /* THRM */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* GLOW */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BRCK */ {IPL, NT, 8.8f, PT_STNE, ITL, NT, 1223.0f,PT_LAVA}, + /* CFLM */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FIRW */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FUSE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FSEP */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* AMTR */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BCOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PCLN */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* HSWC */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* IRON */ {IPL, NT, IPH, NT, ITL, NT, 1687.0f,PT_LAVA}, + /* MORT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* LIFE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* DLAY */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* CO2 */ {IPL, NT, IPH, NT, 194.65f,PT_DRIC, ITH, NT}, + /* DRIC */ {IPL, NT, IPH, NT, ITL, NT, 195.65f,PT_CO2}, + /* CBNW */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV}, + /* STOR */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* STOR */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SPNG */ {IPL, NT, IPH, NT, ITL, NT, 2730.0f,PT_FIRE}, + /* RIME */ {IPL, NT, IPH, NT, ITL, NT, 273.15f,PT_WATR}, + /* FOG */ {IPL, NT, IPH, NT, ITL, NT, 373.15f,PT_WTRV}, + /* BCLN */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* LOVE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* DEUT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WARP */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PUMP */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FWRK */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PIPE */ {IPL, NT, 10.0f, PT_BRMT, ITL, NT, ITH, NT}, + /* FRZZ */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FRZW */ {IPL, NT, IPH, NT, ITL, NT, 53.0f, PT_ICEI}, + /* GRAV */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BIZR */ {IPL, NT, IPH, NT, 100.0f, PT_BIZRG, 400.0f, PT_BIZRS}, + /* BIZRG*/ {IPL, NT, IPH, NT, ITL, NT, 100.0f, PT_BIZR},//, 400.0f, PT_BIZRS}, + /* BIZRS*/ {IPL, NT, IPH, NT, 400.0f, PT_BIZR, ITH, NT},// 100.0f, PT_BIZRG}, + /* INST */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ISOZ */ {IPL, NT, IPH, NT, 160.0f, PT_ISZS, ITH, NT}, + /* ISZS */ {IPL, NT, IPH, NT, ITL, NT, 300.0f, PT_ISOZ}, + /* PRTI */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PRTO */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PSTE */ {IPL, NT, 0.5f, PT_PSTS, ITL, NT, 747.0f, PT_BRCK}, + /* PSTS */ {0.5f, PT_PSTE, IPH, NT, ITL, NT, ITH, NT}, + /* ANAR */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* VINE */ {IPL, NT, IPH, NT, ITL, NT, 573.0f, PT_FIRE}, + /* INVS */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* EQVE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SPWN2*/ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SPAWN*/ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SHLD1*/ {IPL, NT, 7.0f, PT_NONE, ITL, NT, ITH, NT}, + /* SHLD2*/ {IPL, NT, 15.0f, PT_NONE, ITL, NT, ITH, NT}, + /* SHLD3*/ {IPL, NT, 25.0f, PT_NONE, ITL, NT, ITH, NT}, + /* SHLD4*/ {IPL, NT, 40.0f, PT_NONE, ITL, NT, ITH, NT}, + /* LOlZ */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WIFI */ {IPL, NT, 15.0f, PT_BRMT, ITL, NT, ITH, NT}, + /* FILT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ARAY */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BRAY */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* STKM2*/ {IPL, NT, IPH, NT, ITL, NT, 620.0f, PT_FIRE}, + /* BOMB */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* C-5 */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SING */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* QRTZ */ {IPL, NT, IPH, NT, ITL, NT, 2573.15f,PT_LAVA}, + /* PQRT */ {IPL, NT, IPH, NT, ITL, NT, 2573.15f,PT_LAVA}, + /* EMP */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* BREL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ELEC */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* ACEL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* DCEL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* TNT */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* IGNP */ {IPL, NT, IPH, NT, ITL, NT, 673.0f, PT_FIRE}, + /* BOYL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /*FREE*//* GOL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* WIND */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* H2 */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* SOAP */ {IPL, NT, IPH, NT, ITL, NT, ITL, NT}, + /* NBHL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* NWHL */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* MERC */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* PBCN */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* GPMP */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* CLST */ {IPL, NT, IPH, NT, ITL, NT, 1256.0f, PT_LAVA}, + /* WIRE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* GBMB */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT}, + /* FIGH */ {IPL, NT, IPH, NT, ITL, NT, 620.0f, PT_FIRE}, +}; +#undef IPL +#undef IPH +#undef ITL +#undef ITH +#undef NT +#undef ST*/ + memcpy(ptransitions, ptransitionst, sizeof(part_transition) * PT_NUM); + init_can_move(); + clear_sim(); +} diff --git a/src/interface/Button.cpp b/src/interface/Button.cpp new file mode 100644 index 0000000..a357c36 --- /dev/null +++ b/src/interface/Button.cpp @@ -0,0 +1,127 @@ +/* + * Button.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include <iostream> + +#include "interface/Button.h" +#include "Graphics.h" + +namespace ui { + +Button::Button(int x, int y, int width, int height, const std::string& buttonText): + Component(x, y, width, height), + Toggleable(false), + ButtonText(buttonText), + isMouseInside(false), + isButtonDown(false), + state(false) +{ + +} + +void Button::Draw(void* userdata) +{ + Graphics * g = reinterpret_cast<Graphics*>(userdata); + //TODO: Cache text location, that way we don't have the text alignment code here + if(isButtonDown) + { + g->fillrect(X, Y, Width, Height, 255, 255, 255, 255); + g->drawtext(X+(Width-Graphics::textwidth((char *)ButtonText.c_str()))/2, Y+(Height-10)/2, ButtonText, 0, 0, 0, 255); + } + else + { + if(isMouseInside) + g->fillrect(X, Y, Width, Height, 20, 20, 20, 255); + g->drawrect(X, Y, Width, Height, 255, 255, 255, 255); + g->drawtext(X+(Width-Graphics::textwidth((char *)ButtonText.c_str()))/2, Y+(Height-10)/2, ButtonText, 255, 255, 255, 255); + } + /*sf::RenderWindow* rw = reinterpret_cast<sf::RenderWindow*>(userdata); //it better be a RenderWindow or so help your god + + //Draw component here + sf::Text textGraphic(ButtonText); + textGraphic.SetCharacterSize(11); + if(isButtonDown) + textGraphic.SetColor(sf::Color::Black); + else + textGraphic.SetColor(sf::Color::White); + sf::FloatRect tempRect = textGraphic.GetRect(); + textGraphic.SetPosition(ceil(X + Width/2 - tempRect.Width/2), ceil(Y + Height/2 - tempRect.Height/2)); + + if(isMouseInside) + { + if(isButtonDown) + rw->Draw(sf::Shape::Rectangle(X+2, Y+2, Width-4, Width-4, sf::Color::White, 2.f, sf::Color::Black)); + else + rw->Draw(sf::Shape::Rectangle(X+2, Y+2, Width-4, Width-4, sf::Color::Black, 2.f, sf::Color::White)); + } + else + { + if(isButtonDown) + rw->Draw(sf::Shape::Rectangle(X+2, Y+2, Width-4, Width-4, sf::Color::White, 2.f, sf::Color::Black)); + else + rw->Draw(sf::Shape::Rectangle(X+1, Y+1, Width-2, Width-2, sf::Color::Black, 1.f, sf::Color::White)); + } + + rw->Draw(textGraphic);*/ +} + +void Button::OnMouseUnclick(int x, int y, unsigned int button) +{ + if(button != 1) + { + return; //left click only! + } + + if(isButtonDown) + { + if(state) + { + state = false; + } + else + { + if(Toggleable) + { + state = true; + } + DoAction(); + } + } + + isButtonDown = false; +} + +void Button::OnMouseUp(int x, int y, unsigned int button) //mouse unclick is called before this +{ + if(button != 1) return; //left click only! + + isButtonDown = false; +} + +void Button::OnMouseClick(int x, int y, unsigned int button) +{ + if(button != 1) return; //left click only! + + isButtonDown = true; +} + +void Button::OnMouseEnter(int x, int y, int dx, int dy) +{ + isMouseInside = true; +} + +void Button::OnMouseLeave(int x, int y, int dx, int dy) +{ + isMouseInside = false; +} + +void Button::DoAction() +{ + std::cout << "Do action!"<<std::endl; +} + +} /* namespace ui */ diff --git a/src/interface/Component.cpp b/src/interface/Component.cpp new file mode 100644 index 0000000..48a329b --- /dev/null +++ b/src/interface/Component.cpp @@ -0,0 +1,89 @@ +/* + * Component.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include "interface/Component.h" + +namespace ui { + +Component::Component(int x, int y, int width, int height): + X(x), + Y(y), + Width(width), + Height(height), + Enabled(true), + Visible(true) +{ +} + +Component::~Component() +{ +} + +void Component::Draw(void* userdata) +{ +} + +void Component::Tick(float dt) +{ +} + +void Component::OnKeyPress(int key, bool shift, bool ctrl, bool alt) +{ +} + +void Component::OnKeyRelease(int key, bool shift, bool ctrl, bool alt) +{ +} + +void Component::OnMouseEnter(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseLeave(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseClick(int localx, int localy, unsigned int button) +{ +} + +void Component::OnMouseUnclick(int localx, int localy, unsigned int button) +{ +} + +void Component::OnMouseDown(int localx, int localy, unsigned int button) +{ +} + +void Component::OnMouseHover(int localx, int localy) +{ +} + +void Component::OnMouseMoved(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseUp(int localx, int localy, unsigned int button) +{ +} + +void Component::OnMouseWheel(int localx, int localy, int d) +{ +} + +void Component::OnMouseWheelInside(int localx, int localy, int d) +{ +} + +void Component::OnMouseWheelFocused(int localx, int localy, int d) +{ +} +} /* namespace ui */ diff --git a/src/interface/Panel.cpp b/src/interface/Panel.cpp new file mode 100644 index 0000000..164bfa3 --- /dev/null +++ b/src/interface/Panel.cpp @@ -0,0 +1,23 @@ +/* + * Panel.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include "interface/Panel.h" + +namespace ui { + +Panel::Panel(int x, int y, int width, int height): + Component(x, y, width, height) +{ + // TODO Auto-generated constructor stub + +} + +Panel::~Panel() { + // TODO Auto-generated destructor stub +} + +} /* namespace ui */ diff --git a/src/interface/Sandbox.cpp b/src/interface/Sandbox.cpp new file mode 100644 index 0000000..c33571a --- /dev/null +++ b/src/interface/Sandbox.cpp @@ -0,0 +1,57 @@ +/* + * Sandbox.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include "Config.h" + +#include "interface/Sandbox.h" +#include "interface/Component.h" +#include "Renderer.h" + +namespace ui { + +Sandbox::Sandbox(): + Component(0, 0, XRES, YRES) +{ + sim = new Simulation(); +} + +void Sandbox::OnMouseMovedInside(int localx, int localy, int dx, int dy) +{ + if(isMouseDown) + { + sim->create_parts(localx, localy, 20, 20, 1, 0); + } +} + +void Sandbox::OnMouseDown(int localx, int localy, unsigned int button) +{ + isMouseDown = true; +} + +void Sandbox::OnMouseUp(int localx, int localy, unsigned int button) +{ + isMouseDown = false; +} + +void Sandbox::Draw(void* userdata) +{ + Graphics * g = reinterpret_cast<Graphics*>(userdata); + if(!ren) + ren = new Renderer(g, sim); + ren->render_parts(); +} + +void Sandbox::Tick(float delta) +{ + sim->update_particles(); +} + +Sandbox::~Sandbox() { + // TODO Auto-generated destructor stub +} + +} /* namespace ui */ diff --git a/src/interface/State.cpp b/src/interface/State.cpp new file mode 100644 index 0000000..e069e8f --- /dev/null +++ b/src/interface/State.cpp @@ -0,0 +1,232 @@ +/* + * State.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include <vector> +#include <iostream> +#include <cstring> + +#include "interface/State.h" + +namespace ui { + +State::State(int w, int h) : + mouseX(0), + mouseY(0), + mouseXP(0), + mouseYP(0), + width(w), + height(h) +{ +} + +State::~State() +{ + //Components.~vector(); // just in case // devnote : Nope.jpg Nate :3 -frankbro +} + +void State::Add(Component* child) +{ + Components.push_back(child); + child->Parent = this; +} + +void State::Remove(Component* child) +{ + for(int i = 0; i < Components.size(); i++) + if(Components[i] == child) + { + Components.erase(Components.begin() + i); + break; + } +} + +void State::Draw(void* userdata) +{ + //draw + for(int i = 0; i < Components.size(); i++) + { + if(Components[i]->Visible) + { + if(AllowExclusiveDrawing) + { + Components[i]->Draw(userdata); + } + else if( + Components[i]->X + Components[i]->Width >= 0 && + Components[i]->Y + Components[i]->Height >= 0 && + Components[i]->X < width && + Components[i]->Y < height ) + { + Components[i]->Draw(userdata); + } + } + } +} + +void State::Tick(float dt) +{ + //on mouse hover + for(int i = 0; i < Components.size(); i++) + if( mouseX >= Components[i]->X && + mouseY >= Components[i]->Y && + mouseX < Components[i]->X + Components[i]->Width && + mouseY < Components[i]->Y + Components[i]->Height ) + { + if(Components[i]->Enabled) + { + Components[i]->OnMouseHover(mouseX, mouseY); + } + break; + } + + //tick + for(int i = 0; i < Components.size(); i++) + Components[i]->Tick(dt); +} + +void State::OnKeyPress(int key, bool shift, bool ctrl, bool alt) +{ + //on key press + if(focusedComponent_ != NULL) + if(focusedComponent_->Enabled) + focusedComponent_->OnKeyPress(key, shift, ctrl, alt); +} + +void State::OnKeyRelease(int key, bool shift, bool ctrl, bool alt) +{ + //on key unpress + if(focusedComponent_ != NULL) + if(focusedComponent_->Enabled) + focusedComponent_->OnKeyRelease(key, shift, ctrl, alt); +} + +void State::OnMouseDown(int x, int y, unsigned int button) +{ + //on mouse click + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + if(x >= Components[i]->X && y >= Components[i]->Y && x < Components[i]->X + Components[i]->Width && y < Components[i]->Y + Components[i]->Height) + { + Components[i]->OnMouseClick(x - Components[i]->X, y - Components[i]->Y, button); + this->focusedComponent_ = Components[i]; //set this component as the focused component + break; + } + + //on mouse down + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + Components[i]->OnMouseDown(x - Components[i]->X, y - Components[i]->Y, button); +} + +void State::OnMouseMove(int x, int y) +{ + //update mouse coords + mouseX = x; + mouseY = y; + + //on mouse move (if true, and inside) + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + { + int localX = x - Components[i]->X; + int localY = y - Components[i]->Y; + int localXP = mouseXP - Components[i]->X; + int localYP = mouseYP - Components[i]->Y; + int dx = x - mouseXP; + int dy = x - mouseYP; + + Components[i]->OnMouseMoved(localX, localY, dx, dy); + + //is the mouse inside + if(localX >= 0 && + localY >= 0 && + localX < Components[i]->Width && + localY < Components[i]->Height ) + { + //was the mouse outside last tick? + if(localXP < 0 || + localXP >= Components[i]->Width || + localYP < 0 || + localYP >= Components[i]->Height ) + { + Components[i]->OnMouseEnter(localX, localY, dx, dy); + } + + Components[i]->OnMouseMovedInside(localX, localY, dx, dy); + + break; //found the top-most component under mouse, break that shit + } + //not inside, let's see if it used to be inside last tick + else if (localXP >= 0 && + localYP >= 0 && + localXP < Components[i]->Width && + localYP < Components[i]->Height ) + { + Components[i]->OnMouseLeave(localX, localY, x - mouseXP, y - mouseYP); + } + } + else //is locked + { + int localX = x - Components[i]->X; + int localY = y - Components[i]->Y; + + //is the mouse inside + if(localX >= 0 && + localY >= 0 && + localX < Components[i]->Width && + localY < Components[i]->Height ) + { + break; //it's the top-most component under the mouse, we don't want to go under it. + } + } + // end of for loop here + + //set the previous mouse coords + mouseXP = x; + mouseYP = y; +} + +void State::OnMouseUp(int x, int y, unsigned int button) +{ + //on mouse unclick + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + if(x >= Components[i]->X && y >= Components[i]->Y && x < Components[i]->X + Components[i]->Width && y < Components[i]->Y + Components[i]->Height) + { + Components[i]->OnMouseUnclick(x - Components[i]->X, y - Components[i]->Y, button); + break; + } + + //on mouse up + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + Components[i]->OnMouseUp(x - Components[i]->X, y - Components[i]->Y, button); +} + +void State::OnMouseWheel(int x, int y, int d) +{ + //focused mouse wheel + if(focusedComponent_ != NULL) + focusedComponent_->OnMouseWheelFocused(x - focusedComponent_->X, y - focusedComponent_->Y, d); + + //mouse wheel inside + for(int i = Components.size() - 1; i > -1 ; i--) + if(x >= Components[i]->X && y >= Components[i]->Y && x < Components[i]->X + Components[i]->Width && y < Components[i]->Y + Components[i]->Height) + { + if(Components[i]->Enabled) + Components[i]->OnMouseWheelInside(x - Components[i]->X, y - Components[i]->Y, d); + break; //found top-most component under mouse + } + + //on mouse wheel + for(int i = Components.size() - 1; i > -1 ; i--) + if(Components[i]->Enabled) + Components[i]->OnMouseWheel(x - Components[i]->X, y - Components[i]->Y, d); +} + + +} /* namespace ui */ diff --git a/src/interface/Window.cpp b/src/interface/Window.cpp new file mode 100644 index 0000000..624bf9a --- /dev/null +++ b/src/interface/Window.cpp @@ -0,0 +1,23 @@ +/* + * Window.cpp + * + * Created on: Jan 8, 2012 + * Author: Simon + */ + +#include "interface/Window.h" + +namespace ui { + +Window::Window(): + State(width, height) +{ + // TODO Auto-generated constructor stub + +} + +Window::~Window() { + // TODO Auto-generated destructor stub +} + +} /* namespace ui */ |
