summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon 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)
commitb0ea52690ba56a0d0602ad8674b7e5ab2ba3e778 (patch)
tree7d72e0509f4d2643d3be837a337d088ca5949c73 /src
downloadpowder-b0ea52690ba56a0d0602ad8674b7e5ab2ba3e778.zip
powder-b0ea52690ba56a0d0602ad8674b7e5ab2ba3e778.tar.gz
Initial
Diffstat (limited to 'src')
-rw-r--r--src/Air.cpp301
-rw-r--r--src/Graphics.cpp2359
-rw-r--r--src/Gravity.cpp488
-rw-r--r--src/Misc.cpp664
-rw-r--r--src/PowderToy.cpp95
-rw-r--r--src/Renderer.cpp1692
-rw-r--r--src/Simulation.cpp3591
-rw-r--r--src/interface/Button.cpp127
-rw-r--r--src/interface/Component.cpp89
-rw-r--r--src/interface/Panel.cpp23
-rw-r--r--src/interface/Sandbox.cpp57
-rw-r--r--src/interface/State.cpp232
-rw-r--r--src/interface/Window.cpp23
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 */