The-Powder-Toy/src/simulation/Simulation.cpp

4668 lines
120 KiB
C++
Raw Normal View History

//#include <cstdlib>
#include <cmath>
#include <math.h>
#if !defined(_MSC_VER)
2012-08-14 12:06:18 -05:00
#include <strings.h>
#else
#include <windows.h>
#endif
2012-01-08 11:39:03 -06:00
#include "Config.h"
#include "Simulation.h"
#include "Elements.h"
2012-05-07 11:59:50 -05:00
//#include "ElementFunctions.h"
2012-01-08 11:39:03 -06:00
#include "Air.h"
#include "Gravity.h"
2012-05-07 11:59:50 -05:00
#include "elements/Element.h"
//#include "graphics/Renderer.h"
//#include "graphics/Graphics.h"
#include "Misc.h"
#include "Tools.h"
#include "game/Brush.h"
#include "client/GameSave.h"
#include "Sample.h"
2012-08-16 16:03:40 -05:00
#include "Snapshot.h"
//#include "StorageClasses.h"
2012-04-16 07:58:20 -05:00
#undef LUACONSOLE
//#include "cat/LuaScriptHelper.h"
int Simulation::Load(GameSave * save)
{
return Load(0, 0, save);
}
int Simulation::Load(int fullX, int fullY, GameSave * save)
{
int blockX, blockY, x, y, r;
if(!save) return 0;
save->Expand();
//Align to blockMap
blockX = fullX/CELL;
blockY = fullY/CELL;
fullX = blockX*CELL;
fullY = blockY*CELL;
int i;
for(int n = 0; n < NPART && n < save->particlesCount; n++)
{
Particle tempPart = save->particles[n];
tempPart.x += (float)fullX;
tempPart.y += (float)fullY;
x = int(tempPart.x + 0.5f);
y = int(tempPart.y + 0.5f);
if ((player.spwn == 1 && tempPart.type==PT_STKM) || (player2.spwn == 1 && tempPart.type==PT_STKM2))
continue;
if (!elements[tempPart.type].Enabled)
continue;
if(r = pmap[y][x])
{
//Replace existing
parts[r>>8] = tempPart;
2012-08-18 11:31:31 -05:00
i = r>>8;
pmap[y][x] = 0;
elementCount[parts[r>>8].type]--;
elementCount[tempPart.type]++;
}
else
{
//Allocate new particle
if (pfree == -1)
break;
i = pfree;
pfree = parts[i].life;
if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
parts[i] = tempPart;
elementCount[tempPart.type]++;
}
2012-08-18 11:31:31 -05:00
2012-09-14 16:23:35 -05:00
if (parts[i].type == PT_STKM)
2012-08-18 11:31:31 -05:00
{
Element_STKM::STKM_init_legs(this, &player, i);
player.spwn = 1;
player.elem = PT_DUST;
}
2012-09-14 16:23:35 -05:00
else if (parts[i].type == PT_STKM2)
2012-08-18 11:31:31 -05:00
{
Element_STKM::STKM_init_legs(this, &player2, i);
player2.spwn = 1;
player2.elem = PT_DUST;
}
2012-09-14 16:23:35 -05:00
else if (parts[i].type == PT_FIGH)
2012-08-18 11:31:31 -05:00
{
//TODO: 100 should be replaced with a macro
2012-09-14 16:23:35 -05:00
for(int fcount = 0; fcount < 100; fcount++)
2012-08-18 11:31:31 -05:00
{
2012-09-14 16:23:35 -05:00
if(!fighters[fcount].spwn)
{
fighcount++;
//currentPart.tmp = fcount;
parts[i].tmp = fcount;
fighters[fcount].spwn = 1;
fighters[fcount].elem = PT_DUST;
Element_STKM::STKM_init_legs(this, &(fighters[fcount]), i);
break;
}
2012-08-18 11:31:31 -05:00
}
}
}
parts_lastActiveIndex = NPART-1;
2012-06-12 19:21:33 -05:00
force_stacking_check = 1;
Element_PPIP::ppip_changed = 1;
for(int i = 0; i < save->signs.size() && signs.size() < MAXSIGNS; i++)
{
sign tempSign = save->signs[i];
tempSign.x += fullX;
tempSign.y += fullY;
signs.push_back(tempSign);
}
for(int saveBlockX = 0; saveBlockX < save->blockWidth; saveBlockX++)
{
for(int saveBlockY = 0; saveBlockY < save->blockHeight; saveBlockY++)
{
if(save->blockMap[saveBlockY][saveBlockX])
{
bmap[saveBlockY+blockY][saveBlockX+blockX] = save->blockMap[saveBlockY][saveBlockX];
fvx[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelX[saveBlockY][saveBlockX];
fvy[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelY[saveBlockY][saveBlockX];
}
}
}
2012-08-05 08:03:54 -05:00
gravWallChanged = true;
return 0;
}
GameSave * Simulation::Save()
{
return Save(0, 0, XRES, YRES);
}
GameSave * Simulation::Save(int fullX, int fullY, int fullX2, int fullY2)
{
int blockX, blockY, blockX2, blockY2, fullW, fullH, blockW, blockH;
//Normalise incoming coords
int swapTemp;
if(fullY>fullY2)
{
swapTemp = fullY;
fullY = fullY2;
fullY2 = swapTemp;
}
if(fullX>fullX2)
{
swapTemp = fullX;
fullX = fullX2;
fullX2 = swapTemp;
}
//Align coords to blockMap
blockX = fullX/CELL;
blockY = fullY/CELL;
blockX2 = fullX2/CELL;
blockY2 = fullY2/CELL;
fullX = blockX*CELL;
fullY = blockY*CELL;
fullX2 = blockX2*CELL;
fullY2 = blockY2*CELL;
blockW = blockX2-blockX;
blockH = blockY2-blockY;
fullW = fullX2-fullX;
fullH = fullY2-fullY;
GameSave * newSave = new GameSave(blockW, blockH);
for(int i = 0; i < NPART; i++)
{
int x, y;
x = int(parts[i].x + 0.5f);
y = int(parts[i].y + 0.5f);
if(parts[i].type && x >= fullX && y >= fullY && x < fullX2 && y < fullY2)
{
Particle tempPart = parts[i];
tempPart.x -= fullX;
tempPart.y -= fullY;
if(elements[tempPart.type].Enabled)
*newSave << tempPart;
}
}
for(int i = 0; i < MAXSIGNS && i < signs.size(); i++)
{
if(signs[i].text.length() && signs[i].x >= fullX && signs[i].y >= fullY && signs[i].x < fullX2 && signs[i].y < fullY2)
{
sign tempSign = signs[i];
tempSign.x -= fullX;
tempSign.y -= fullY;
*newSave << tempSign;
}
}
for(int saveBlockX = 0; saveBlockX < newSave->blockWidth; saveBlockX++)
{
for(int saveBlockY = 0; saveBlockY < newSave->blockHeight; saveBlockY++)
{
if(bmap[saveBlockY+blockY][saveBlockX+blockX])
{
newSave->blockMap[saveBlockY][saveBlockX] = bmap[saveBlockY+blockY][saveBlockX+blockX];
newSave->fanVelX[saveBlockY][saveBlockX] = fvx[saveBlockY+blockY][saveBlockX+blockX];
newSave->fanVelY[saveBlockY][saveBlockX] = fvy[saveBlockY+blockY][saveBlockX+blockX];
}
}
}
return newSave;
}
2012-08-16 16:03:40 -05:00
Snapshot * Simulation::CreateSnapshot()
{
Snapshot * snap = new Snapshot();
snap->AirPressure.insert(snap->AirPressure.begin(), &pv[0][0], &pv[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->AirVelocityX.insert(snap->AirVelocityX.begin(), &vx[0][0], &vx[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->AirVelocityY.insert(snap->AirVelocityY.begin(), &vy[0][0], &vy[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->Particles.insert(snap->Particles.begin(), parts, parts+NPART);
snap->PortalParticles.insert(snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[CHANNELS-1][8-1][80-1]);
snap->WirelessData.insert(snap->WirelessData.begin(), &wireless[0][0], &wireless[CHANNELS-1][2-1]);
snap->GravVelocityX.insert(snap->GravVelocityX.begin(), gravx, gravx+((XRES/CELL)*(YRES/CELL)));
snap->GravVelocityY.insert(snap->GravVelocityY.begin(), gravy, gravy+((XRES/CELL)*(YRES/CELL)));
snap->GravValue.insert(snap->GravValue.begin(), gravp, gravp+((XRES/CELL)*(YRES/CELL)));
snap->GravMap.insert(snap->GravMap.begin(), gravmap, gravmap+((XRES/CELL)*(YRES/CELL)));
snap->BlockMap.insert(snap->BlockMap.begin(), &bmap[0][0], &bmap[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->ElecMap.insert(snap->ElecMap.begin(), &emap[0][0], &emap[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->FanVelocityX.insert(snap->FanVelocityX.begin(), &fvx[0][0], &fvx[0][0]+((XRES/CELL)*(YRES/CELL)));
snap->FanVelocityY.insert(snap->FanVelocityY.begin(), &fvy[0][0], &fvy[0][0]+((XRES/CELL)*(YRES/CELL)));
return snap;
}
void Simulation::Restore(const Snapshot & snap)
{
parts_lastActiveIndex = NPART-1;
2012-08-16 16:03:40 -05:00
std::copy(snap.AirPressure.begin(), snap.AirPressure.end(), &pv[0][0]);
std::copy(snap.AirVelocityX.begin(), snap.AirVelocityX.end(), &vx[0][0]);
std::copy(snap.AirVelocityY.begin(), snap.AirVelocityY.end(), &vy[0][0]);
std::copy(snap.Particles.begin(), snap.Particles.end(), parts);
std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]);
std::copy(snap.WirelessData.begin(), snap.WirelessData.end(), &wireless[0][0]);
std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), gravx);
std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), gravy);
std::copy(snap.GravValue.begin(), snap.GravValue.end(), gravp);
std::copy(snap.GravMap.begin(), snap.GravMap.end(), gravmap);
std::copy(snap.BlockMap.begin(), snap.BlockMap.end(), &bmap[0][0]);
std::copy(snap.ElecMap.begin(), snap.ElecMap.end(), &emap[0][0]);
std::copy(snap.FanVelocityX.begin(), snap.FanVelocityX.end(), &fvx[0][0]);
std::copy(snap.FanVelocityY.begin(), snap.FanVelocityY.end(), &fvy[0][0]);
}
/*int Simulation::Load(unsigned char * data, int dataLength)
{
return SaveLoader::Load(data, dataLength, this, true, 0, 0);
}
int Simulation::Load(int x, int y, unsigned char * data, int dataLength)
{
return SaveLoader::Load(data, dataLength, this, false, x, y);
}
unsigned char * Simulation::Save(int & dataLength)
{
return SaveLoader::Build(dataLength, this, 0, 0, XRES, YRES);
}
2012-01-08 11:39:03 -06:00
unsigned char * Simulation::Save(int x1, int y1, int x2, int y2, int & dataLength)
{
return SaveLoader::Build(dataLength, this, x1, y1, x2-x1, y2-y1);
}*/
2012-01-08 11:39:03 -06:00
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++)
{
2012-08-05 08:03:54 -05:00
if(bmap[(cy+area_y)/CELL][(cx+area_x)/CELL] == WL_GRAV)
gravWallChanged = true;
2012-01-08 11:39:03 -06:00
bmap[(cy+area_y)/CELL][(cx+area_x)/CELL] = 0;
delete_part(cx+area_x, cy+area_y, 0);
}
}
}
2012-05-12 07:21:04 -05:00
void Simulation::CreateBox(int x1, int y1, int x2, int y2, int c, int flags)
2012-01-08 11:39:03 -06:00
{
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++)
2012-05-12 07:21:04 -05:00
CreateParts(i, j, 0, 0, c, flags);
}
void Simulation::CreateWallBox(int x1, int y1, int x2, int y2, int c, int flags)
{
int i, j;
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++)
CreateWalls(i, j, 0, 0, c, flags);
2012-01-08 11:39:03 -06:00
}
int Simulation::flood_prop_2(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype, int parttype, char * bitmap)
2012-01-08 11:39:03 -06:00
{
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;
switch (proptype) {
case StructProperty::Float:
*((float*)(((char*)&parts[i])+propoffset)) = *((float*)propvalue);
break;
case StructProperty::ParticleType:
case StructProperty::Integer:
*((int*)(((char*)&parts[i])+propoffset)) = *((int*)propvalue);
break;
case StructProperty::UInteger:
*((unsigned int*)(((char*)&parts[i])+propoffset)) = *((unsigned int*)propvalue);
break;
default:
break;
2012-01-08 11:39:03 -06:00
}
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, StructProperty::PropertyType proptype)
2012-01-08 11:39:03 -06:00
{
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;
}
2012-07-24 08:58:39 -05:00
SimulationSample Simulation::Get(int x, int y)
{
2012-07-24 08:58:39 -05:00
SimulationSample sample;
sample.PositionX = x;
sample.PositionY = y;
if(pmap[y][x])
{
2012-07-24 08:58:39 -05:00
sample.particle = parts[pmap[y][x]>>8];
sample.ParticleID = pmap[y][x]>>8;
}
else if(photons[y][x])
{
2012-07-24 08:58:39 -05:00
sample.particle = parts[photons[y][x]>>8];
sample.ParticleID = photons[y][x]>>8;
}
if (bmap[y/CELL][x/CELL])
{
sample.WallType = bmap[y/CELL][x/CELL];
}
2012-07-24 08:58:39 -05:00
sample.AirPressure = pv[y/CELL][x/CELL];
sample.AirTemperature = hv[y/CELL][x/CELL];
sample.AirVelocityX = vx[y/CELL][x/CELL];
sample.AirVelocityY = vy[y/CELL][x/CELL];
if(grav->ngrav_enable)
{
sample.Gravity = gravp[(y/CELL)*(XRES/CELL)+(x/CELL)];
sample.GravityVelocityX = gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
sample.GravityVelocityY = gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
}
return sample;
}
#define PMAP_CMP_CONDUCTIVE(pmap, t) (((pmap)&0xFF)==(t) || (((pmap)&0xFF)==PT_SPRK && parts[(pmap)>>8].ctype==(t)))
int Simulation::FloodINST(int x, int y, int fullc, int cm)
{
int c = fullc&0xFF;
int x1, x2, dy = (c<PT_NUM)?1:CELL;
int co = c;
int coord_stack_limit = XRES*YRES;
unsigned short (*coord_stack)[2];
int coord_stack_size = 0;
int created_something = 0;
if (c>=PT_NUM)
return 0;
if (cm==-1)
{
if (c==0)
{
cm = pmap[y][x]&0xFF;
if (!cm)
return 0;
}
else
cm = 0;
}
if ((pmap[y][x]&0xFF)!=cm || parts[pmap[y][x]>>8].life!=0)
return 1;
coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit);
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y;
coord_stack_size++;
do
{
coord_stack_size--;
x = coord_stack[coord_stack_size][0];
y = coord_stack[coord_stack_size][1];
x1 = x2 = x;
// go left as far as possible
while (x1>=CELL)
{
if ((pmap[y][x1-1]&0xFF)!=cm || parts[pmap[y][x1-1]>>8].life!=0)
{
break;
}
x1--;
}
// go right as far as possible
while (x2<XRES-CELL)
{
if ((pmap[y][x2+1]&0xFF)!=cm || parts[pmap[y][x2+1]>>8].life!=0)
{
break;
}
x2++;
}
// fill span
for (x=x1; x<=x2; x++)
{
if (create_part(-1, x, y, fullc)>=0)
created_something = 1;
}
// add vertically adjacent pixels to stack
// (wire crossing for INST)
if (y>=CELL+1 && x1==x2 &&
PMAP_CMP_CONDUCTIVE(pmap[y-1][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-1][x1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-1][x1+1], cm) &&
!PMAP_CMP_CONDUCTIVE(pmap[y-2][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-2][x1], cm) && !PMAP_CMP_CONDUCTIVE(pmap[y-2][x1+1], cm))
{
// travelling vertically up, skipping a horizontal line
if ((pmap[y-2][x1]&0xFF)==cm && !parts[pmap[y-2][x1]>>8].life)
{
coord_stack[coord_stack_size][0] = x1;
coord_stack[coord_stack_size][1] = y-2;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
}
}
else if (y>=CELL+1)
{
for (x=x1; x<=x2; x++)
{
if ((pmap[y-1][x]&0xFF)==cm && !parts[pmap[y-1][x]>>8].life)
{
if (x==x1 || x==x2 || y>=YRES-CELL-1 || !PMAP_CMP_CONDUCTIVE(pmap[y+1][x], cm))
{
// if at the end of a horizontal section, or if it's a T junction
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y-1;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
}
}
}
}
if (y<YRES-CELL-1 && x1==x2 &&
PMAP_CMP_CONDUCTIVE(pmap[y+1][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+1][x1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+1][x1+1], cm) &&
!PMAP_CMP_CONDUCTIVE(pmap[y+2][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+2][x1], cm) && !PMAP_CMP_CONDUCTIVE(pmap[y+2][x1+1], cm))
{
// travelling vertically down, skipping a horizontal line
if ((pmap[y+2][x1]&0xFF)==cm && !parts[pmap[y+2][x1]>>8].life)
{
coord_stack[coord_stack_size][0] = x1;
coord_stack[coord_stack_size][1] = y+2;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
}
}
else if (y<YRES-CELL-1)
{
for (x=x1; x<=x2; x++)
{
if ((pmap[y+1][x]&0xFF)==cm && !parts[pmap[y+1][x]>>8].life)
{
if (x==x1 || x==x2 || y<0 || !PMAP_CMP_CONDUCTIVE(pmap[y-1][x], cm))
{
// if at the end of a horizontal section, or if it's a T junction
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y+1;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
}
}
}
}
} while (coord_stack_size>0);
free(coord_stack);
return created_something;
}
2012-05-12 07:21:04 -05:00
int Simulation::FloodParts(int x, int y, int fullc, int cm, int bm, int flags)
2012-01-08 11:39:03 -06:00
{
int c = fullc&0xFF;
int x1, x2, dy = (c<PT_NUM)?1:CELL;
2012-01-08 11:39:03 -06:00
int co = c;
int coord_stack_limit = XRES*YRES;
unsigned short (*coord_stack)[2];
int coord_stack_size = 0;
int created_something = 0;
2012-01-08 11:39:03 -06:00
if (c==SPC_PROP)
return 0;
if (cm==-1)
{
if (c==0)
{
cm = pmap[y][x]&0xFF;
if (!cm)
return 0;
}
else
cm = 0;
}
if (bm==-1)
{
2012-08-05 10:11:43 -05:00
bm = bmap[y/CELL][x/CELL];
2012-01-08 11:39:03 -06:00
}
if (((pmap[y][x]&0xFF)!=cm || bmap[y/CELL][x/CELL]!=bm ))
2012-01-08 11:39:03 -06:00
return 1;
coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit);
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y;
coord_stack_size++;
do
2012-01-08 11:39:03 -06:00
{
coord_stack_size--;
x = coord_stack[coord_stack_size][0];
y = coord_stack[coord_stack_size][1];
x1 = x2 = x;
// go left as far as possible
while (x1>=CELL)
2012-01-08 11:39:03 -06:00
{
if ((pmap[y][x1-1]&0xFF)!=cm || bmap[y/CELL][(x1-1)/CELL]!=bm)
{
break;
}
x1--;
2012-01-08 11:39:03 -06:00
}
// go right as far as possible
while (x2<XRES-CELL)
2012-01-08 11:39:03 -06:00
{
if ((pmap[y][x2+1]&0xFF)!=cm || bmap[y/CELL][(x2+1)/CELL]!=bm)
{
break;
}
x2++;
2012-01-08 11:39:03 -06:00
}
// fill span
for (x=x1; x<=x2; x++)
2012-01-08 11:39:03 -06:00
{
if (CreateParts(x, y, 0, 0, fullc, flags))
created_something = 1;
2012-01-08 11:39:03 -06:00
}
if (y>=CELL+dy)
for (x=x1; x<=x2; x++)
if ((pmap[y-dy][x]&0xFF)==cm && bmap[(y-dy)/CELL][x/CELL]==bm)
2012-01-08 11:39:03 -06:00
{
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y-dy;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
2012-01-08 11:39:03 -06:00
}
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)
{
coord_stack[coord_stack_size][0] = x;
coord_stack[coord_stack_size][1] = y+dy;
coord_stack_size++;
if (coord_stack_size>=coord_stack_limit)
{
free(coord_stack);
return -1;
}
}
} while (coord_stack_size>0);
free(coord_stack);
return created_something;
2012-01-08 11:39:03 -06:00
}
2012-05-12 07:21:04 -05:00
int Simulation::FloodWalls(int x, int y, int c, int cm, int bm, int flags)
{
int x1, x2, dy = CELL;
int co = c;
if (cm==-1)
{
2012-08-05 10:11:43 -05:00
cm = pmap[y][x]&0xFF;
2012-05-12 07:21:04 -05:00
}
if (bm==-1)
{
if (c==WL_ERASE)
{
bm = bmap[y/CELL][x/CELL];
if (!bm)
return 0;
}
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 (!CreateWalls(x, y, 0, 0, c, flags))
return 0;
}
// fill children
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 (!FloodWalls(x, y-dy, c, 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 (!FloodWalls(x, y+dy, c, cm, bm, flags))
return 0;
2012-08-05 10:11:43 -05:00
return 1;
2012-05-12 07:21:04 -05:00
}
2012-01-08 11:39:03 -06:00
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)
{
2012-05-07 11:59:50 -05:00
if ((elements[(pmap[y][x1-1]&0xFF)].Falldown)!=2)
2012-01-08 11:39:03 -06:00
{
break;
}
x1--;
}
while (x2<XRES-CELL)
{
2012-05-07 11:59:50 -05:00
if ((elements[(pmap[y][x2+1]&0xFF)].Falldown)!=2)
2012-01-08 11:39:03 -06:00
{
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++)
2012-05-07 11:59:50 -05:00
if ((elements[(pmap[y-1][x]&0xFF)].Falldown)==2 && parts[pmap[y-1][x]>>8].tmp2 == check)
2012-01-08 11:39:03 -06:00
if (!flood_water(x, y-1, i, originaly, check))
return 0;
if (y<YRES-CELL-1)
for (x=x1; x<=x2; x++)
2012-05-07 11:59:50 -05:00
if ((elements[(pmap[y+1][x]&0xFF)].Falldown)==2 && parts[pmap[y+1][x]>>8].tmp2 == check)
2012-01-08 11:39:03 -06:00
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;
}
2012-08-05 12:35:12 -05:00
void Simulation::SetEdgeMode(int newEdgeMode)
{
edgeMode = newEdgeMode;
switch(edgeMode)
{
case 0:
for(int i = 0; i<(XRES/CELL); i++)
{
bmap[0][i] = 0;
bmap[YRES/CELL-1][i] = 0;
}
for(int i = 1; i<((YRES/CELL)-1); i++)
{
bmap[i][0] = 0;
bmap[i][XRES/CELL-1] = 0;
}
break;
case 1:
int i;
for(i=0; i<(XRES/CELL); i++)
{
bmap[0][i] = WL_WALL;
bmap[YRES/CELL-1][i] = WL_WALL;
}
for(i=1; i<((YRES/CELL)-1); i++)
{
bmap[i][0] = WL_WALL;
bmap[i][XRES/CELL-1] = WL_WALL;
}
break;
default:
SetEdgeMode(0);
}
}
2012-03-05 09:24:52 -06:00
void Simulation::ApplyDecoration(int x, int y, int colR_, int colG_, int colB_, int colA_, int mode)
{
2012-03-05 09:24:52 -06:00
int rp;
float tr, tg, tb, ta, colR = colR_, colG = colG_, colB = colB_, colA = colA_;
float strength = 0.01f;
rp = pmap[y][x];
if (!rp)
return;
2012-03-05 09:24:52 -06:00
ta = (parts[rp>>8].dcolour>>24)&0xFF;
tr = (parts[rp>>8].dcolour>>16)&0xFF;
tg = (parts[rp>>8].dcolour>>8)&0xFF;
tb = (parts[rp>>8].dcolour)&0xFF;
ta /= 255.0f; tr /= 255.0f; tg /= 255.0f; tb /= 255.0f;
colR /= 255.0f; colG /= 255.0f; colB /= 255.0f; colA /= 255.0f;
if (mode == DECO_DRAW)
{
2012-03-05 09:24:52 -06:00
ta = colA;
tr = colR;
tg = colG;
tb = colB;
}
else if (mode == DECO_CLEAR)
{
ta = tr = tg = tb = 0.0f;
}
2012-03-05 09:24:52 -06:00
else if (mode == DECO_ADD)
{
ta += (colA*strength)*colA;
tr += (colR*strength)*colA;
tg += (colG*strength)*colA;
tb += (colB*strength)*colA;
2012-03-05 09:24:52 -06:00
}
else if (mode == DECO_SUBTRACT)
{
ta -= (colA*strength)*colA;
tr -= (colR*strength)*colA;
tg -= (colG*strength)*colA;
tb -= (colB*strength)*colA;
2012-03-05 09:24:52 -06:00
}
else if (mode == DECO_MULTIPLY)
{
tr *= 1.0f+(colR*strength)*colA;
tg *= 1.0f+(colG*strength)*colA;
tb *= 1.0f+(colB*strength)*colA;
2012-03-05 09:24:52 -06:00
}
else if (mode == DECO_DIVIDE)
{
tr /= 1.0f+(colR*strength)*colA;
tg /= 1.0f+(colG*strength)*colA;
tb /= 1.0f+(colB*strength)*colA;
2012-03-05 09:24:52 -06:00
}
2012-04-18 10:40:43 -05:00
else if (mode == DECO_SMUDGE)
{
float tas = ta, trs = tr, tgs = tg, tbs = tb;
int rx, ry;
float num = 1.0f;
for (rx=-1; rx<2; rx++)
for (ry=-1; ry<2; ry++)
2012-04-18 10:40:43 -05:00
{
if ((pmap[y+ry][x+rx]&0xFF) && parts[pmap[y+ry][x+rx]>>8].dcolour)
{
2012-07-24 09:56:01 -05:00
Particle part = parts[pmap[y+ry][x+rx]>>8];
2012-04-18 10:40:43 -05:00
num++;
tas += ((float)((part.dcolour>>24)&0xFF))/255.0f;
trs += ((float)((part.dcolour>>16)&0xFF))/255.0f;
tgs += ((float)((part.dcolour>>8)&0xFF))/255.0f;
tbs += ((float)((part.dcolour)&0xFF))/255.0f;
2012-04-18 10:40:43 -05:00
}
}
if (num == 0)
return;
ta = ((tas/num)*0.1f) + (ta*0.9f);
tr = ((trs/num)*0.1f) + (tr*0.9f);
tg = ((tgs/num)*0.1f) + (tg*0.9f);
tb = ((tbs/num)*0.1f) + (tb*0.9f);
2012-04-18 10:40:43 -05:00
}
2012-03-05 09:24:52 -06:00
colA_ = ta*255.0f;
colR_ = tr*255.0f;
colG_ = tg*255.0f;
colB_ = tb*255.0f;
2012-03-05 09:24:52 -06:00
if(colA_ > 255)
colA_ = 255;
else if(colA_ < 0)
colA_ = 0;
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;
parts[rp>>8].dcolour = ((colA_<<24)|(colR_<<16)|(colG_<<8)|colB_);
}
void Simulation::ApplyDecorationPoint(int positionX, int positionY, int colR, int colG, int colB, int colA, int mode, Brush * cBrush)
{
int i, j;
if(cBrush)
{
int radiusX, radiusY, sizeX, sizeY;
radiusX = cBrush->GetRadius().X;
radiusY = cBrush->GetRadius().Y;
sizeX = cBrush->GetSize().X;
sizeY = cBrush->GetSize().Y;
unsigned char *bitmap = cBrush->GetBitmap();
for(int y = 0; y < sizeY; y++)
{
for(int x = 0; x < sizeX; x++)
{
if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
{
ApplyDecoration(positionX+(x-radiusX), positionY+(y-radiusY), colR, colG, colB, colA, mode);
}
}
}
}
}
void Simulation::ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode)
{
int i, j;
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++)
ApplyDecoration(i, j, colR, colG, colB, colA, mode);
}
void Simulation::ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush * cBrush)
{
int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
float e, de;
if(cBrush)
{
rx = cBrush->GetRadius().X;
ry = cBrush->GetRadius().Y;
}
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)
ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush);
else
ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush);
e += de;
if (e >= 0.5f)
{
y += sy;
if (!(rx+ry))
{
if (cp)
ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush);
else
ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush);
}
e -= 1.0f;
}
}
}
int Simulation::Tool(int x, int y, int tool, float strength)
{
if(tools[tool])
{
Particle * cpart = NULL;
int r;
if(r = pmap[y][x])
cpart = &(parts[r>>8]);
else if(r = photons[y][x])
cpart = &(parts[r>>8]);
return tools[tool]->Perform(this, cpart, x, y, strength);
}
return 0;
}
int Simulation::ToolBrush(int positionX, int positionY, int tool, Brush * cBrush, float strength)
{
if(cBrush)
{
int radiusX, radiusY, sizeX, sizeY;
radiusX = cBrush->GetRadius().X;
radiusY = cBrush->GetRadius().Y;
sizeX = cBrush->GetSize().X;
sizeY = cBrush->GetSize().Y;
unsigned char *bitmap = cBrush->GetBitmap();
for(int y = 0; y < sizeY; y++)
for(int x = 0; x < sizeX; x++)
if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
Tool(positionX+(x-radiusX), positionY+(y-radiusY), tool, strength);
}
return 0;
}
void Simulation::ToolLine(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength)
{
int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
float e, de;
rx = cBrush->GetRadius().X;
ry = cBrush->GetRadius().Y;
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)
ToolBrush(y, x, tool, cBrush, strength);
else
ToolBrush(x, y, tool, cBrush, strength);
e += de;
if (e >= 0.5f)
{
y += sy;
if ((!(rx+ry)) && ((y1<y2) ? (y<=y2) : (y>=y2)))
{
if (cp)
ToolBrush(y, x, tool, cBrush, strength);
else
ToolBrush(x, y, tool, cBrush, strength);
}
e -= 1.0f;
}
}
}
void Simulation::ToolBox(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength)
{
int i, j;
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++)
Tool(i, j, tool, strength);
}
2012-05-15 14:01:20 -05:00
int Simulation::CreateParts(int positionX, int positionY, int c, Brush * cBrush)
2012-01-08 11:39:03 -06:00
{
if(cBrush)
{
2012-05-15 14:01:20 -05:00
int radiusX, radiusY, sizeX, sizeY;
radiusX = cBrush->GetRadius().X;
radiusY = cBrush->GetRadius().Y;
sizeX = cBrush->GetSize().X;
sizeY = cBrush->GetSize().Y;
unsigned char *bitmap = cBrush->GetBitmap();
if(c == PT_NONE)
{
for(int y = 0; y < sizeY; y++)
{
for(int x = 0; x < sizeX; x++)
{
if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
{
delete_part(positionX+(x-radiusX), positionY+(y-radiusY), 0);
}
}
}
}
else
{
for(int y = 0; y < sizeY; y++)
{
for(int x = 0; x < sizeX; x++)
{
if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
{
create_part(-2, positionX+(x-radiusX), positionY+(y-radiusY), c);
}
}
}
}
}
2012-05-15 14:01:20 -05:00
return 0;
}
int Simulation::CreateParts(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;
2012-01-08 11:39:03 -06:00
int wall = c - 100;
if (c==SPC_WIND || c==PT_FIGH)
return 0;
2012-05-12 07:21:04 -05:00
2012-01-08 11:39:03 -06:00
if (c==PT_LIGH)
{
2012-05-15 14:01:20 -05:00
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;
2012-01-08 11:39:03 -06:00
}
2012-05-12 07:21:04 -05:00
2012-01-08 11:39:03 -06:00
//eraser
if (c == 0)
2012-01-08 11:39:03 -06:00
{
if (rx==0&&ry==0)
{
delete_part(x, y, 0);
}
else
{
2012-01-08 11:39:03 -06:00
for (j=-ry; j<=ry; j++)
for (i=-rx; i<=rx; i++)
delete_part(x+i, y+j, 0);
}
2012-01-08 11:39:03 -06:00
return 1;
}
2012-05-12 07:21:04 -05:00
2012-01-08 11:39:03 -06:00
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
{
2012-01-08 11:39:03 -06:00
for (j=-ry; j<=ry; j++)
for (i=-rx; i<=rx; i++)
{
if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES)
continue;
create_part(-2, x+i, y+j, c);
}
}
2012-01-08 11:39:03 -06:00
return 1;
}
2012-05-12 07:21:04 -05:00
2012-01-08 11:39:03 -06:00
//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
2012-01-08 11:39:03 -06:00
{
for (j=-ry; j<=ry; j++)
for (i=-rx; i<=rx; i++)
if (create_part_add_props(-2, x+i, y+j, c, rx, ry)==-1)
f = 1;
2012-01-08 11:39:03 -06:00
}
return !f;
2012-01-08 11:39:03 -06:00
}
2012-05-12 07:21:04 -05:00
int Simulation::CreateWalls(int x, int y, int rx, int ry, int c, int flags, Brush * cBrush)
{
int i, j, r, f = 0, u, v, oy, ox, b = 0, dw = 0, stemp = 0, p;//n;
if(cBrush)
{
rx = cBrush->GetRadius().X;
ry = cBrush->GetRadius().Y;
}
int wall = c;
if (wall == WL_ERASE)
b = 0;
else
b = wall;
ry = ry/CELL;
rx = rx/CELL;
x = x/CELL;
y = y/CELL;
2012-08-10 12:59:05 -05:00
x -= rx;///2;
y -= ry;///2;
for (ox=x; ox<=x+rx+rx; ox++)
2012-05-12 07:21:04 -05:00
{
2012-08-10 12:59:05 -05:00
for (oy=y; oy<=y+ry+ry; oy++)
2012-05-12 07:21:04 -05:00
{
if (ox>=0&&ox<XRES/CELL&&oy>=0&&oy<YRES/CELL)
{
i = ox;
j = oy;
if (b==WL_FAN)
{
fvx[j][i] = 0.0f;
fvy[j][i] = 0.0f;
}
2012-08-05 08:03:54 -05:00
if (b==WL_GRAV || bmap[j][i]==WL_GRAV)
{
gravWallChanged = true;
}
2012-05-12 07:21:04 -05:00
if (b==WL_STREAM)
{
2012-08-29 19:50:18 -05:00
i = x + rx;///2;
j = y + ry;///2;
2012-05-12 07:21:04 -05:00
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;
}
bmap[j][i] = b;
}
}
}
return 1;
}
2012-05-15 14:01:20 -05:00
void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c, Brush * cBrush)
{
int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
rx = cBrush->GetRadius().X;
ry = cBrush->GetRadius().Y;
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)
CreateParts(y, x, c, cBrush);
else
CreateParts(x, y, c, cBrush);
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)
CreateParts(y, x, c, cBrush);
else
CreateParts(x, y, c, cBrush);
}
e -= 1.0f;
}
}
}
void Simulation::CreateLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags)
2012-01-08 11:39:03 -06:00
{
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)
2012-05-15 14:01:20 -05:00
CreateParts(y, x, rx, ry, c, flags);
2012-01-08 11:39:03 -06:00
else
2012-05-15 14:01:20 -05:00
CreateParts(x, y, rx, ry, c, flags);
2012-01-08 11:39:03 -06:00
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))
2012-05-12 07:21:04 -05:00
&& ((y1<y2) ? (y<=y2) : (y>=y2)))
{
if (cp)
2012-05-15 14:01:20 -05:00
CreateParts(y, x, rx, ry, c, flags);
2012-05-12 07:21:04 -05:00
else
2012-05-15 14:01:20 -05:00
CreateParts(x, y, rx, ry, c, flags);
2012-05-12 07:21:04 -05:00
}
e -= 1.0f;
}
}
}
void Simulation::CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags, Brush * cBrush)
{
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)
CreateWalls(y, x, rx, ry, c, flags, cBrush);
else
CreateWalls(x, y, rx, ry, c, flags, cBrush);
e += de;
if (e >= 0.5f)
{
y += sy;
if (!(rx+ry) && ((y1<y2) ? (y<=y2) : (y>=y2)))
2012-01-08 11:39:03 -06:00
{
if (cp)
2012-05-12 07:21:04 -05:00
CreateWalls(y, x, rx, ry, c, flags, cBrush);
2012-01-08 11:39:03 -06:00
else
2012-05-12 07:21:04 -05:00
CreateWalls(x, y, rx, ry, c, flags, cBrush);
2012-01-08 11:39:03 -06:00
}
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));
2012-01-08 11:39:03 -06:00
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));
float (*vxo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
float (*vyo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
float (*vxn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
float (*vyn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
float (*pvo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
float (*pvn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
2012-01-08 11:39:03 -06:00
int i, x, y, nx, ny, w, h, nw, nh;
vector2d pos, tmp, ctl, cbr;
vector2d vel;
2012-01-08 11:39:03 -06:00
vector2d cornerso[4];
unsigned char *odatac = (unsigned char *)odata;
//if (parse_save(odata, *size, 0, 0, 0, bmapo, vxo, vyo, pvo, fvxo, fvyo, signst, partst, pmapt)) //TODO: Implement
2012-01-08 11:39:03 -06:00
{
free(bmapo);
free(bmapn);
free(partst);
free(signst);
free(pmapt);
free(fvxo);
free(fvyo);
free(fvxn);
free(fvyn);
free(vxo);
free(vyo);
free(vxn);
free(vyn);
free(pvo);
free(pvn);
2012-01-08 11:39:03 -06:00
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;
vel = v2d_new(partst[i].vx, partst[i].vy);
vel = m2d_multiply_v2d(transform, vel);
partst[i].vx = vel.x;
partst[i].vy = vel.y;
2012-01-08 11:39:03 -06:00
}
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/CELL || ny<0 || ny>=nh/CELL)
2012-01-08 11:39:03 -06:00
continue;
if (bmapo[y][x])
{
bmapn[ny][nx] = bmapo[y][x];
if (bmapo[y][x]==WL_FAN)
{
vel = v2d_new(fvxo[y][x], fvyo[y][x]);
vel = m2d_multiply_v2d(transform, vel);
fvxn[ny][nx] = vel.x;
fvyn[ny][nx] = vel.y;
2012-01-08 11:39:03 -06:00
}
}
vel = v2d_new(vxo[y][x], vyo[y][x]);
vel = m2d_multiply_v2d(transform, vel);
vxn[ny][nx] = vel.x;
vyn[ny][nx] = vel.y;
pvn[ny][nx] = pvo[y][x];
2012-01-08 11:39:03 -06:00
}
//ndata = build_save(size,0,0,nw,nh,bmapn,vxn,vyn,pvn,fvxn,fvyn,signst,partst); //TODO: IMPLEMENT
2012-01-08 11:39:03 -06:00
free(bmapo);
free(bmapn);
free(partst);
free(signst);
free(pmapt);
free(fvxo);
free(fvyo);
free(fvxn);
free(fvyn);
free(vxo);
free(vyo);
free(vxn);
free(vyn);
free(pvo);
free(pvn);
2012-01-08 11:39:03 -06:00
return ndata;
}
TPT_NO_INLINE void Simulation::orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[])
2012-01-08 11:39:03 -06:00
{
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;
}
TPT_NO_INLINE void Simulation::orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[])
2012-01-08 11:39:03 -06:00
{
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;
}
2012-05-12 07:21:04 -05:00
CreateLine(xmid[i], ymid[i], xmid[i+1], ymid[i+1], 0, 0, type, flags);
2012-01-08 11:39:03 -06:00
}
free(xmid);
free(ymid);
}
void Simulation::clear_sim(void)
{
int i, x, y;
2012-08-21 07:00:31 -05:00
emp_decor = 0;
2012-05-12 16:28:45 -05:00
signs.clear();
currentTick = 0;
2012-01-08 11:39:03 -06:00
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(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));
std::fill(elementCount, elementCount+PT_NUM, 0);
2012-01-08 11:39:03 -06:00
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(grav)
grav->Clear();
if(air)
air->Clear();
2012-08-05 12:35:12 -05:00
SetEdgeMode(edgeMode);
2012-01-08 11:39:03 -06:00
}
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, stkm_move;
2012-01-08 11:39:03 -06:00
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
2012-05-07 11:59:50 -05:00
if (elements[t].Weight <= elements[rt].Weight || rt==PT_GEL) can_move[t][rt] = 0;
if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTPASS))
2012-01-08 11:39:03 -06:00
can_move[t][rt] = 2;
2012-05-07 11:59:50 -05:00
if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTABSORB))
can_move[t][rt] = 1;
2012-05-07 11:59:50 -05:00
if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTPENETRATE))
2012-01-08 11:39:03 -06:00
can_move[t][rt] = 1;
2012-05-07 11:59:50 -05:00
if ((elements[t].Properties&PROP_NEUTPENETRATE) && rt==PT_NEUT)
2012-01-08 11:39:03 -06:00
can_move[t][rt] = 0;
2012-05-07 11:59:50 -05:00
if ((elements[t].Properties&TYPE_ENERGY) && (elements[rt].Properties&TYPE_ENERGY))
2012-01-08 11:39:03 -06:00
can_move[t][rt] = 2;
}
}
can_move[PT_DEST][PT_DMND] = 0;
can_move[PT_DEST][PT_CLNE] = 0;
can_move[PT_DEST][PT_PCLN] = 0;
can_move[PT_DEST][PT_BCLN] = 0;
can_move[PT_DEST][PT_PBCN] = 0;
2012-01-08 11:39:03 -06:00
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;
stkm_move = 0;
if (elements[t].Properties & (TYPE_LIQUID | TYPE_GAS))
stkm_move = 2;
if (!t || t==PT_PRTO || t==PT_SPAWN || t==PT_SPAWN2)
stkm_move = 2;
can_move[PT_STKM][t] = stkm_move;
can_move[PT_STKM2][t] = stkm_move;
can_move[PT_FIGH][t] = stkm_move;
2012-01-08 11:39:03 -06:00
}
for (t=0;t<PT_NUM;t++)
{
// make them eat things
can_move[t][PT_BHOL] = 1;
can_move[t][PT_NBHL] = 1;
can_move[t][PT_STKM] = 0;
can_move[t][PT_STKM2] = 0;
can_move[t][PT_FIGH] = 0;
2012-01-08 11:39:03 -06:00
//INVIS behaviour varies with pressure
can_move[t][PT_INVIS] = 3;
//stop CNCT being displaced by other particles
can_move[t][PT_CNCT] = 0;
//void behaviour varies with powered state and ctype
2012-01-08 11:39:03 -06:00
can_move[t][PT_PVOD] = 3;
can_move[t][PT_VOID] = 3;
2012-01-08 11:39:03 -06:00
}
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;
2012-06-12 18:57:02 -05:00
can_move[PT_ELEC][PT_EXOT] = 2;
can_move[PT_NEUT][PT_EXOT] = 2;
2012-01-08 11:39:03 -06:00
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;
can_move[PT_THDR][PT_THDR] = 2;
can_move[PT_EMBR][PT_EMBR] = 2;
2012-01-08 11:39:03 -06:00
}
/*
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)
{
if(!parts[r>>8].ctype || (parts[r>>8].ctype==pt)!=(parts[r>>8].tmp&1))
result = 1;
else
result = 0;
}
2012-01-08 11:39:03 -06:00
else result = 0;
}
if ((r&0xFF)==PT_VOID)
{
if(!parts[r>>8].ctype || (parts[r>>8].ctype==pt)!=(parts[r>>8].tmp&1))
result = 1;
else
result = 0;
}
2012-01-08 11:39:03 -06:00
}
if (bmap[ny/CELL][nx/CELL])
{
2012-05-07 11:59:50 -05:00
if (bmap[ny/CELL][nx/CELL]==WL_ALLOWGAS && !(elements[pt].Properties&TYPE_GAS))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE)
2012-01-08 11:39:03 -06:00
return 0;
2012-05-07 11:59:50 -05:00
if (bmap[ny/CELL][nx/CELL]==WL_ALLOWENERGY && !(elements[pt].Properties&TYPE_ENERGY))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE)
2012-01-08 11:39:03 -06:00
return 0;
2012-05-07 11:59:50 -05:00
if (bmap[ny/CELL][nx/CELL]==WL_ALLOWLIQUID && elements[pt].Falldown!=2)
2012-01-08 11:39:03 -06:00
return 0;
2012-05-07 11:59:50 -05:00
if (bmap[ny/CELL][nx/CELL]==WL_ALLOWSOLID && elements[pt].Falldown!=1)
2012-01-08 11:39:03 -06:00
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] && !(elements[pt].Properties&TYPE_SOLID) && !(elements[r&0xFF].Properties&TYPE_SOLID))
2012-01-08 11:39:03 -06:00
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);
/* 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 (!(elements[parts[i].type].Properties & TYPE_ENERGY))
2012-01-08 11:39:03 -06:00
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;
2012-05-07 11:59:50 -05:00
if ((r & 0xFF) < PT_NUM && elements[r&0xFF].HeatConduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10) && (r&0xFF)!=PT_FILT)
2012-01-08 11:39:03 -06:00
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 && (elements[parts[i].type].Properties & TYPE_ENERGY))
2012-01-08 11:39:03 -06:00
{
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
2012-05-07 11:59:50 -05:00
if (parts[i].type == PT_NEUT && (elements[r & 0xFF].Properties & PROP_NEUTABSORB))
{
kill_part(i);
return 0;
}
2012-01-08 11:39:03 -06:00
if ((r&0xFF)==PT_VOID || (r&0xFF)==PT_PVOD) //this is where void eats particles
{
//void ctype already checked in eval_move
kill_part(i);
2012-01-08 11:39:03 -06:00
return 0;
}
if ((r&0xFF)==PT_BHOL || (r&0xFF)==PT_NBHL) //this is where blackhole eats particles
{
if (!legacy_enable)
{
parts[r>>8].temp = restrict_flt(parts[r>>8].temp+parts[i].temp/2, MIN_TEMP, MAX_TEMP);//3.0f;
}
kill_part(i);
2012-01-08 11:39:03 -06:00
return 0;
}
if (((r&0xFF)==PT_WHOL||(r&0xFF)==PT_NWHL) && parts[i].type==PT_ANAR) //whitehole eats anar
{
if (!legacy_enable)
{
parts[r>>8].temp = restrict_flt(parts[r>>8].temp- (MAX_TEMP-parts[i].temp)/2, MIN_TEMP, MAX_TEMP);
}
kill_part(i);
2012-01-08 11:39:03 -06:00
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 (s && !(elements[s&0xFF].Properties&PROP_NEUTPENETRATE))
2012-01-08 11:39:03 -06:00
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), result;
if (parts[i].type == PT_NONE)
return 0;
result = try_move(i, x, y, nx, ny);
2012-01-08 11:39:03 -06:00
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 (elements[t].Properties & TYPE_ENERGY)
2012-01-08 11:39:03 -06:00
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;
// Remove from pmap even if type==0, otherwise infinite recursion occurs when flood fill deleting
// a particle which sets type to 0 without calling kill_part (such as LIFE)
x = (int)(parts[i].x+0.5f);
y = (int)(parts[i].y+0.5f);
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;
}
if (parts[i].type == PT_NONE)
return;
if(parts[i].type > 0 && parts[i].type < PT_NUM && elementCount[parts[i].type] && parts[i].type)
2012-01-08 11:39:03 -06:00
elementCount[parts[i].type]--;
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);
}
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;
2012-05-07 11:59:50 -05:00
if (!elements[t].Enabled)
2012-01-08 11:39:03 -06:00
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 (elements[t].Properties & TYPE_ENERGY)
2012-01-08 11:39:03 -06:00
{
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;
}
}
//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.
//tv = Type (8 bits) + Var (24 bits), var is usually 0
int Simulation::create_part(int p, int x, int y, int tv)
2012-01-08 11:39:03 -06:00
{
int i;
int t = tv & 0xFF;
int v = (tv >> 8) & 0xFFFFFF;
2012-01-08 11:39:03 -06:00
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))
2012-01-08 11:39:03 -06:00
return -1;
2012-05-07 11:59:50 -05:00
if (t>=0 && t<PT_NUM && !elements[t].Enabled)
2012-01-08 11:39:03 -06:00
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;
}
2012-05-07 11:59:50 -05:00
if (!((pmap[y][x]&0xFF)==PT_INST||(elements[pmap[y][x]&0xFF].Properties&PROP_CONDUCTS)))
2012-01-08 11:39:03 -06:00
return -1;
if (parts[pmap[y][x]>>8].life!=0)
return -1;
if (p==-2 && (pmap[y][x]&0xFF)==PT_INST)
{
FloodINST(x, y, PT_SPRK, PT_INST);
return pmap[y][x]>>8;
}
2012-01-08 11:39:03 -06:00
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 there is a particle, only allow creation if the new particle can occupy the same space as the existing particle
// If there isn't a particle but there is a wall, check whether the new particle is allowed to be in it
// (not "!=2" for wall check because eval_move returns 1 for moving into empty space)
// If there's no particle and no wall, assume creation is allowed
if (pmap[y][x] ? (eval_move(t, x, y, NULL)!=2) : (bmap[y/CELL][x/CELL] && eval_move(t, x, y, NULL)==0))
2012-01-08 11:39:03 -06:00
{
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 ((
2012-05-07 11:59:50 -05:00
((pmap[y][x]&0xFF)==PT_STOR&&!(elements[t].Properties&TYPE_SOLID))||
2012-01-08 11:39:03 -06:00
(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;
}
else if ((pmap[y][x]&0xFF) == PT_DTEC && (pmap[y][x]&0xFF) != t)
{
parts[pmap[y][x]>>8].ctype = t;
if (t==PT_LIFE && v<NGOLALT)
parts[pmap[y][x]>>8].tmp = v;
}
2012-01-08 11:39:03 -06:00
return -1;
}
if (photons[y][x] && (elements[t].Properties & TYPE_ENERGY))
2012-01-08 11:39:03 -06:00
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;
parts[i].flags = 0;
2012-01-08 11:39:03 -06:00
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;
2012-05-07 11:59:50 -05:00
parts[i].temp = elements[t].Temperature;
2012-01-08 11:39:03 -06:00
parts[i].tmp = 0;
parts[i].tmp2 = 0;
}
switch (t)
2012-01-08 11:39:03 -06:00
{
case PT_SOAP:
parts[i].tmp = -1;
parts[i].tmp2 = -1;
break;
case PT_ACID: case PT_CAUS:
parts[i].life = 75;
break;
/*Testing
case PT_WOOD:
parts[i].life = 150;
break;
End Testing*/
case PT_WARP:
parts[i].life = rand()%95+70;
break;
case PT_FUSE:
parts[i].life = 50;
parts[i].tmp = 50;
break;
case PT_LIFE:
if (v<NGOLALT)
{
parts[i].tmp = grule[v+1][9] - 1;
parts[i].ctype = v;
}
break;
case PT_DEUT:
parts[i].life = 10;
break;
case PT_MERC:
parts[i].tmp = 10;
break;
case PT_BRAY:
parts[i].life = 30;
break;
case PT_GPMP: case PT_PUMP:
parts[i].life = 10;
break;
case PT_SING:
parts[i].life = rand()%50+60;
break;
case PT_QRTZ:
parts[i].tmp = (rand()%11);
break;
case PT_PQRT:
parts[i].tmp = (rand()%11);
break;
case PT_CLST:
parts[i].tmp = (rand()%7);
break;
case PT_FSEP:
parts[i].life = 50;
break;
case PT_COAL:
parts[i].life = 110;
parts[i].tmp = 50;
break;
case PT_IGNT:
parts[i].life = 3;
break;
case PT_FRZW:
parts[i].life = 100;
break;
case PT_PPIP:
case PT_PIPE:
parts[i].life = 60;
break;
case PT_BCOL:
parts[i].life = 110;
break;
case PT_FIRE:
parts[i].life = rand()%50+120;
break;
case PT_PLSM:
parts[i].life = rand()%150+50;
break;
case PT_HFLM:
parts[i].life = rand()%150+50;
break;
case PT_LAVA:
parts[i].life = rand()%120+240;
break;
case PT_NBLE:
parts[i].life = 0;
break;
case PT_ICEI:
parts[i].ctype = PT_WATR;
break;
case PT_MORT:
parts[i].vx = 2;
break;
2012-06-12 18:57:02 -05:00
case PT_EXOT:
parts[i].life = 1000;
parts[i].tmp = 244;
break;
case PT_EMBR:
parts[i].life = 50;
break;
case PT_TESC:
parts[i].tmp = v;
if (parts[i].tmp > 300)
parts[i].tmp=300;
break;
case 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 = elements[t].Temperature;
Element_STKM::STKM_init_legs(this, &player, i);
player.spwn = 1;
player.elem = PT_DUST;
}
else
{
return -1;
}
create_part(-3,x,y,PT_SPAWN);
elementCount[PT_SPAWN] = 1;
break;
case 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 = elements[t].Temperature;
Element_STKM::STKM_init_legs(this, &player2, i);
player2.spwn = 1;
player2.elem = PT_DUST;
}
else
{
return -1;
}
create_part(-3,x,y,PT_SPAWN2);
elementCount[PT_SPAWN2] = 1;
break;
case PT_BIZR: case PT_BIZRG: case PT_BIZRS:
parts[i].ctype = 0x47FFFF;
break;
case PT_DTEC:
parts[i].tmp2 = 2;
break;
default:
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 = elements[t].Temperature;
Element_STKM::STKM_init_legs(this, &fighters[fcount], i);
fighters[fcount].spwn = 1;
fighters[fcount].elem = PT_DUST;
fighcount++;
return i;
}
return -1;
}
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_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_TRON)
{
int randhue = rand()%360;
int randomdir = rand()%4;
parts[i].tmp = 1|(randomdir<<5)|(randhue<<7);//set as a head and a direction
parts[i].tmp2 = 4;//tail
parts[i].life = 5;
}
if (t==PT_LIGH)
{
float gx, gy, gsize;
if (p!=-2)
{
parts[i].life=30;
parts[i].temp=parts[i].life*150.0f; // temperature of the lighting shows the power of the lighting
}
GetGravityField(x, y, 1.0f, 1.0f, gx, gy);
gsize = gx*gx+gy*gy;
if (gsize<0.0016f)
{
float angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
gsize = sqrtf(gsize);
// randomness in weak gravity fields (more randomness with weaker fields)
gx += cosf(angle)*(0.04f-gsize);
gy += sinf(angle)*(0.04f-gsize);
}
parts[i].tmp = (((int)(atan2f(-gy, gx)*(180.0f/M_PI)))+rand()%40-20+360)%360;
parts[i].tmp2 = 4;
}
break;
2012-01-08 11:39:03 -06:00
}
//and finally set the pmap/photon maps to the newly created particle
if (elements[t].Properties & TYPE_ENERGY)
2012-01-08 11:39:03 -06:00
photons[y][x] = t|(i<<8);
else if (t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH)
2012-01-08 11:39:03 -06:00
pmap[y][x] = t|(i<<8);
//Fancy dust effects for powder types
2012-05-07 11:59:50 -05:00
if((elements[t].Properties & TYPE_PART) && pretty_powder)
2012-01-08 11:39:03 -06:00
{
int colr, colg, colb, randa;
randa = (rand()%30)-15;
2012-05-07 11:59:50 -05:00
colr = (PIXR(elements[t].Colour)+sandcolour_r+(rand()%20)-10+randa);
colg = (PIXG(elements[t].Colour)+sandcolour_g+(rand()%20)-10+randa);
colb = (PIXB(elements[t].Colour)+sandcolour_b+(rand()%20)-10+randa);
2012-01-08 11:39:03 -06:00
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::GetGravityField(int x, int y, float particleGrav, float newtonGrav, float & pGravX, float & pGravY)
{
pGravX = newtonGrav*gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
pGravY = newtonGrav*gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
switch (gravityMode)
{
default:
case 0: //normal, vertical gravity
pGravY += particleGrav;
break;
case 1: //no gravity
break;
case 2: //radial gravity
if (x-XCNTR != 0 || y-YCNTR != 0)
{
float pGravMult = particleGrav/sqrtf((x-XCNTR)*(x-XCNTR) + (y-YCNTR)*(y-YCNTR));
pGravX -= pGravMult * (float)(x - XCNTR);
pGravY -= pGravMult * (float)(y - YCNTR);
}
}
}
2012-01-08 11:39:03 -06:00
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;
unsigned int elem_properties;
2012-01-08 11:39:03 -06:00
float pGravX, pGravY, pGravD;
2012-06-12 19:21:33 -05:00
int excessive_stacking_found = 0;
2012-01-08 11:39:03 -06:00
currentTick++;
2012-02-02 05:55:43 -06:00
if (lighting_recreate>0)
{
for (i=0; i<=parts_lastActiveIndex; i++)
{
if (parts[i].type==PT_LIGH && parts[i].tmp2>0)
{
lighting_ok=0;
break;
}
}
}
2012-01-08 11:39:03 -06:00
if (lighting_ok)
lighting_recreate--;
2012-01-08 11:39:03 -06:00
if (lighting_recreate<0)
lighting_recreate=1;
2012-01-08 11:39:03 -06:00
if (lighting_recreate>21)
lighting_recreate=21;
2012-01-08 11:39:03 -06:00
2012-02-02 05:55:43 -06:00
//if (sys_pause&&!framerender)//do nothing if paused
// return;
2012-01-08 11:39:03 -06:00
if (force_stacking_check || (rand()%10)==0)
{
force_stacking_check = 0;
excessive_stacking_found = 0;
for (y=0; y<YRES; y++)
{
for (x=0; x<XRES; x++)
{
// Use a threshold, since some particle stacking can be normal (e.g. BIZR + FILT)
// Setting pmap_count[y][x] > NPART means BHOL will form in that spot
if (pmap_count[y][x]>5)
{
if (bmap[y/CELL][x/CELL]==WL_EHOLE)
{
// Allow more stacking in E-hole
if (pmap_count[y][x]>1500)
{
pmap_count[y][x] = pmap_count[y][x] + NPART;
excessive_stacking_found = 1;
}
}
else if (pmap_count[y][x]>1500 || (rand()%1600)<=(pmap_count[y][x]+100))
{
pmap_count[y][x] = pmap_count[y][x] + NPART;
excessive_stacking_found = 1;
}
}
}
}
if (excessive_stacking_found)
{
for (i=0; i<=parts_lastActiveIndex; i++)
{
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 && !(elements[t].Properties&TYPE_ENERGY))
{
if (pmap_count[y][x]>=NPART)
{
if (pmap_count[y][x]>NPART)
{
create_part(i, x, y, PT_NBHL);
parts[i].temp = MAX_TEMP;
parts[i].tmp = pmap_count[y][x]-NPART;//strength of grav field
if (parts[i].tmp>51200) parts[i].tmp = 51200;
pmap_count[y][x] = NPART;
}
else
{
kill_part(i);
}
}
}
}
}
}
}
2012-06-12 19:21:33 -05:00
2012-01-08 11:39:03 -06:00
//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;
2012-01-08 11:39:03 -06:00
if(parts[r>>8].type==PT_WIRE)
parts[r>>8].tmp=parts[r>>8].ctype;
}
2012-01-08 11:39:03 -06:00
}
}
if (Element_PPIP::ppip_changed)
{
for (i=0; i<=parts_lastActiveIndex; i++)
{
if (parts[i].type==PT_PPIP)
{
parts[i].tmp |= (parts[i].tmp&0xE0000000)>>3;
parts[i].tmp &= ~0xE0000000;
}
}
Element_PPIP::ppip_changed = 0;
}
2012-01-08 11:39:03 -06:00
//game of life!
if (elementCount[PT_LIFE]>0&&++CGOL>=GSPEED)//GSPEED is frames per generation
{
int createdsomething = 0;
CGOL=0;
ISGOL=0;
for (ny=CELL; ny<YRES-CELL; ny++)
2012-01-08 11:39:03 -06:00
{//go through every particle and set neighbor map
for (nx=CELL; nx<XRES-CELL; nx++)
2012-01-08 11:39:03 -06:00
{
r = pmap[ny][nx];
if (!r)
{
gol[ny][nx] = 0;
2012-01-08 11:39:03 -06:00
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[ny][nx] = golnum;
for ( nnx=-1; nnx<2; nnx++)
2012-01-08 11:39:03 -06:00
{
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
2012-01-08 11:39:03 -06:00
{
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[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][golnum] ++;
gol2[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][0] ++;
}
2012-01-08 11:39:03 -06:00
}
}
} else {
parts[r>>8].tmp --;
if (parts[r>>8].tmp<=0)
parts[r>>8].type = PT_NONE;//using kill_part makes it not work
2012-01-08 11:39:03 -06:00
}
}
//}
}
}
}
for (ny=CELL; ny<YRES-CELL; ny++)
2012-01-08 11:39:03 -06:00
{ //go through every particle again, but check neighbor map, then update particles
for (nx=CELL; nx<XRES-CELL; nx++)
2012-01-08 11:39:03 -06:00
{
r = pmap[ny][nx];
neighbors = gol2[ny][nx][0];
2012-01-08 11:39:03 -06:00
if (neighbors==0 || !((r&0xFF)==PT_LIFE || !(r&0xFF)))
continue;
for ( golnum = 1; golnum<=NGOL; golnum++)
{
goldelete = neighbors;
if (gol[ny][nx]==0&&grule[golnum][goldelete]>=2&&gol2[ny][nx][golnum]>=(goldelete%2)+goldelete/2)
2012-01-08 11:39:03 -06:00
{
if (create_part(-1, nx, ny, PT_LIFE|((golnum-1)<<8)))
createdsomething = 1;
}
else if (gol[ny][nx]==golnum&&(grule[golnum][goldelete-1]==0||grule[golnum][goldelete-1]==2))//subtract 1 because it counted itself
2012-01-08 11:39:03 -06:00
{
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[ny][nx][z] = 0;//this improves performance A LOT compared to the memset, i was getting ~23 more fps with this.
2012-01-08 11:39:03 -06:00
}
}
//memset(gol2, 0, sizeof(gol2));
}
if (ISWIRE>0)//wifi channel reseting
2012-01-08 11:39:03 -06:00
{
for ( q = 0; q<(int)(MAX_TEMP-73.15f)/100+2; q++)
{
wireless[q][0] = wireless[q][1];
wireless[q][1] = 0;
}
ISWIRE--;
2012-01-08 11:39:03 -06:00
}
bool elementRecount = !(currentTick%180);
if(elementRecount)
{
std::fill(elementCount, elementCount+PT_NUM, 0);
}
2012-01-08 11:39:03 -06:00
for (i=0; i<=parts_lastActiveIndex; i++)
if (parts[i].type)
{
t = parts[i].type;
2012-08-18 11:13:13 -05:00
if (t<0 || t>=PT_NUM || !elements[t].Enabled)
2012-01-08 11:39:03 -06:00
{
kill_part(i);
continue;
}
elementCount[t]++;
2012-05-07 11:59:50 -05:00
elem_properties = elements[t].Properties;
if (parts[i].life>0 && (elem_properties&PROP_LIFE_DEC))
2012-01-08 11:39:03 -06:00
{
// automatically decrease life
parts[i].life--;
if (parts[i].life<=0 && (elem_properties&(PROP_LIFE_KILL_DEC|PROP_LIFE_KILL)))
2012-01-08 11:39:03 -06:00
{
// kill on change to no life
kill_part(i);
continue;
}
}
else if (parts[i].life<=0 && (elem_properties&PROP_LIFE_KILL))
2012-01-08 11:39:03 -06:00
{
// kill if no life
kill_part(i);
continue;
}
}
//the main particle loop function, goes over all particles.
for (i=0; i<=parts_lastActiveIndex; i++)
if (parts[i].type)
{
t = parts[i].type;
2012-01-08 11:39:03 -06:00
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 && elements[t].Falldown!=2) ||
(bmap[y/CELL][x/CELL]==WL_ALLOWSOLID && elements[t].Falldown!=1) ||
(bmap[y/CELL][x/CELL]==WL_ALLOWGAS && !(elements[t].Properties&TYPE_GAS)) || //&& elements[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 && !(elements[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;
}
2012-01-08 11:39:03 -06:00
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
2012-05-07 11:59:50 -05:00
vx[y/CELL][x/CELL] = vx[y/CELL][x/CELL]*elements[t].AirLoss + elements[t].AirDrag*parts[i].vx;
vy[y/CELL][x/CELL] = vy[y/CELL][x/CELL]*elements[t].AirLoss + elements[t].AirDrag*parts[i].vy;
2012-01-08 11:39:03 -06:00
if (t==PT_GAS||t==PT_NBLE)
{
if (pv[y/CELL][x/CELL]<3.5f)
2012-05-07 11:59:50 -05:00
pv[y/CELL][x/CELL] += elements[t].HotAir*(3.5f-pv[y/CELL][x/CELL]);
2012-01-08 11:39:03 -06:00
if (y+CELL<YRES && pv[y/CELL+1][x/CELL]<3.5f)
2012-05-07 11:59:50 -05:00
pv[y/CELL+1][x/CELL] += elements[t].HotAir*(3.5f-pv[y/CELL+1][x/CELL]);
2012-01-08 11:39:03 -06:00
if (x+CELL<XRES)
{
if (pv[y/CELL][x/CELL+1]<3.5f)
2012-05-07 11:59:50 -05:00
pv[y/CELL][x/CELL+1] += elements[t].HotAir*(3.5f-pv[y/CELL][x/CELL+1]);
2012-01-08 11:39:03 -06:00
if (y+CELL<YRES && pv[y/CELL+1][x/CELL+1]<3.5f)
2012-05-07 11:59:50 -05:00
pv[y/CELL+1][x/CELL+1] += elements[t].HotAir*(3.5f-pv[y/CELL+1][x/CELL+1]);
2012-01-08 11:39:03 -06:00
}
}
else//add the hotair variable to the pressure map, like black hole, or white hole.
{
2012-05-07 11:59:50 -05:00
pv[y/CELL][x/CELL] += elements[t].HotAir;
2012-01-08 11:39:03 -06:00
if (y+CELL<YRES)
2012-05-07 11:59:50 -05:00
pv[y/CELL+1][x/CELL] += elements[t].HotAir;
2012-01-08 11:39:03 -06:00
if (x+CELL<XRES)
{
2012-05-07 11:59:50 -05:00
pv[y/CELL][x/CELL+1] += elements[t].HotAir;
2012-01-08 11:39:03 -06:00
if (y+CELL<YRES)
2012-05-07 11:59:50 -05:00
pv[y/CELL+1][x/CELL+1] += elements[t].HotAir;
2012-01-08 11:39:03 -06:00
}
}
//Gravity mode by Moach
switch (gravityMode)
{
default:
case 0:
pGravX = 0.0f;
pGravY = elements[t].Gravity;
break;
case 1:
pGravX = pGravY = 0.0f;
break;
case 2:
pGravD = 0.01f - hypotf((x - XCNTR), (y - YCNTR));
pGravX = elements[t].Gravity * ((float)(x - XCNTR) / pGravD);
pGravY = elements[t].Gravity * ((float)(y - YCNTR) / pGravD);
break;
2012-01-08 11:39:03 -06:00
}
//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)];
}
2012-05-07 11:59:50 -05:00
else if(t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH && !(elements[t].Properties & TYPE_SOLID))
2012-01-08 11:39:03 -06:00
{
pGravX += gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
pGravY += gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
}
//velocity updates for the particle
2012-04-19 10:52:35 -05:00
if (!(parts[i].flags&FLAG_MOVABLE))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Loss;
parts[i].vy *= elements[t].Loss;
2012-04-19 10:52:35 -05:00
}
2012-01-08 11:39:03 -06:00
//particle gets velocity from the vx and vy maps
2012-05-07 11:59:50 -05:00
parts[i].vx += elements[t].Advection*vx[y/CELL][x/CELL] + pGravX;
parts[i].vy += elements[t].Advection*vy[y/CELL][x/CELL] + pGravY;
2012-01-08 11:39:03 -06:00
2012-05-07 11:59:50 -05:00
if (elements[t].Diffusion)//the random diffusion that gasses have
2012-01-08 11:39:03 -06:00
{
#ifdef REALISTIC
//The magic number controlls diffusion speed
2012-05-07 11:59:50 -05:00
parts[i].vx += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
parts[i].vy += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
#else
parts[i].vx += elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
parts[i].vy += elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
#endif
2012-01-08 11:39:03 -06:00
}
j = surround_space = nt = 0;//if nt is greater than 1 after this, then there is a particle around the current particle, that is NOT the current particle's type, for water movement.
2012-01-08 11:39:03 -06:00
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++;//there is empty space
2012-01-08 11:39:03 -06:00
if ((r&0xFF)!=t)
nt++;//there is nothing or a different particle
2012-01-08 11:39:03 -06:00
}
}
float gel_scale = 1.0f;
if (t==PT_GEL)
gel_scale = parts[i].tmp*2.55f;
2012-01-08 11:39:03 -06:00
if (!legacy_enable)
{
if (y-2 >= 0 && y-2 < YRES && (elements[t].Properties&TYPE_LIQUID) && (t!=PT_GEL || gel_scale>(1+rand()%255))) {//some heat convection for liquids
2012-01-08 11:39:03 -06:00
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 REALISTIC
2012-05-07 11:59:50 -05:00
if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale))
2012-01-08 11:39:03 -06:00
{
float c_Cm = 0.0f;
#else
if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale)>(rand()%250))
{
float c_Cm = 0.0f;
#endif
if (aheat_enable && !(elements[t].Properties&PROP_NOAMBHEAT))
2012-01-08 11:39:03 -06:00
{
#ifdef REALISTIC
2012-05-07 11:59:50 -05:00
c_heat = parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight) + hv[y/CELL][x/CELL]*100*(pv[y/CELL][x/CELL]+273.15f)/256;
c_Cm = 96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight) + 100*(pv[y/CELL][x/CELL]+273.15f)/256;
pt = c_heat/c_Cm;
pt = restrict_flt(pt, -MAX_TEMP+MIN_TEMP, MAX_TEMP-MIN_TEMP);
parts[i].temp = pt;
//Pressure increase from heat (temporary)
pv[y/CELL][x/CELL] += (pt-hv[y/CELL][x/CELL])*0.004;
hv[y/CELL][x/CELL] = pt;
#else
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;
#endif
2012-01-08 11:39:03 -06:00
}
c_heat = 0.0f;
c_Cm = 0.0f;
2012-01-08 11:39:03 -06:00
for (j=0; j<8; j++)
{
surround_hconduct[j] = i;
r = surround[j];
if (!r)
continue;
rt = r&0xFF;
2012-05-07 11:59:50 -05:00
if (rt&&elements[rt].HeatConduct&&(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)))
2012-01-08 11:39:03 -06:00
{
surround_hconduct[j] = r>>8;
#ifdef REALISTIC
if (rt==PT_GEL)
gel_scale = parts[r>>8].tmp*2.55f;
else gel_scale = 1.0f;
2012-05-07 11:59:50 -05:00
c_heat += parts[r>>8].temp*96.645/elements[rt].HeatConduct*gel_scale*fabs(elements[rt].Weight);
c_Cm += 96.645/elements[rt].HeatConduct*gel_scale*fabs(elements[rt].Weight);
#else
c_heat += parts[r>>8].temp;
#endif
2012-01-08 11:39:03 -06:00
h_count++;
}
}
#ifdef REALISTIC
if (t==PT_GEL)
gel_scale = parts[i].tmp*2.55f;
else gel_scale = 1.0f;
2012-01-08 11:39:03 -06:00
if (t == PT_PHOT)
pt = (c_heat+parts[i].temp*96.645)/(c_Cm+96.645);
else
2012-05-07 11:59:50 -05:00
pt = (c_heat+parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight))/(c_Cm+96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight));
2012-01-08 11:39:03 -06:00
2012-05-07 11:59:50 -05:00
c_heat += parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight);
c_Cm += 96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight);
parts[i].temp = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
#else
pt = (c_heat+parts[i].temp)/(h_count+1);
pt = parts[i].temp = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
for (j=0; j<8; j++)
{
parts[surround_hconduct[j]].temp = pt;
}
#endif
2012-01-08 11:39:03 -06:00
ctemph = ctempl = pt;
// change boiling point with pressure
2012-05-07 11:59:50 -05:00
if ((elements[t].State==ST_LIQUID && elements[t].HighTemperatureTransition>-1 && elements[t].HighTemperatureTransition<PT_NUM && elements[elements[t].HighTemperatureTransition].State==ST_GAS)
|| t==PT_LNTG || t==PT_SLTW)
2012-01-08 11:39:03 -06:00
ctemph -= 2.0f*pv[y/CELL][x/CELL];
2012-05-07 11:59:50 -05:00
else if ((elements[t].State==ST_GAS && elements[t].LowTemperatureTransition>-1 && elements[t].LowTemperatureTransition<PT_NUM && elements[elements[t].LowTemperatureTransition].State==ST_LIQUID)
|| t==PT_WTRV)
2012-01-08 11:39:03 -06:00
ctempl -= 2.0f*pv[y/CELL][x/CELL];
s = 1;
//A fix for ice with ctype = 0
if ((t==PT_ICEI || t==PT_SNOW) && (parts[i].ctype==0 || parts[i].ctype>=PT_NUM || parts[i].ctype==PT_ICEI || parts[i].ctype==PT_SNOW))
parts[i].ctype = PT_WATR;
2012-05-07 11:59:50 -05:00
if (ctemph>elements[t].HighTemperature&&elements[t].HighTemperatureTransition>-1) {
2012-01-08 11:39:03 -06:00
// particle type change due to high temperature
#ifdef REALISTIC
float dbt = ctempl - pt;
2012-05-07 11:59:50 -05:00
if (elements[t].HighTemperatureTransition!=PT_NUM)
{
if (platent[t] <= (c_heat - (elements[t].HighTemperature - dbt)*c_Cm))
{
pt = (c_heat - platent[t])/c_Cm;
2012-05-07 11:59:50 -05:00
t = elements[t].HighTemperatureTransition;
}
else
{
2012-05-07 11:59:50 -05:00
parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP);
s = 0;
}
}
#else
if (elements[t].HighTemperatureTransition!=PT_NUM)
t = elements[t].HighTemperatureTransition;
#endif
else if (t==PT_ICEI || t==PT_SNOW) {
if (parts[i].ctype<PT_NUM&&parts[i].ctype!=t) {
if (elements[parts[i].ctype].LowTemperatureTransition==t&&pt<=elements[parts[i].ctype].LowTemperature) s = 0;
2012-01-08 11:39:03 -06:00
else {
#ifdef REALISTIC
//One ice table value for all it's kinds
if (platent[t] <= (c_heat - (elements[parts[i].ctype].LowTemperature - dbt)*c_Cm))
{
pt = (c_heat - platent[t])/c_Cm;
t = parts[i].ctype;
parts[i].ctype = PT_NONE;
parts[i].life = 0;
}
else
{
2012-05-07 11:59:50 -05:00
parts[i].temp = restrict_flt(elements[parts[i].ctype].LowTemperature - dbt, MIN_TEMP, MAX_TEMP);
s = 0;
}
#else
t = parts[i].ctype;
parts[i].ctype = PT_NONE;
parts[i].life = 0;
#endif
2012-01-08 11:39:03 -06:00
}
}
else s = 0;
}
else if (t==PT_SLTW) {
#ifdef REALISTIC
if (platent[t] <= (c_heat - (elements[t].HighTemperature - dbt)*c_Cm))
{
pt = (c_heat - platent[t])/c_Cm;
if (rand()%4==0) t = PT_SALT;
else t = PT_WTRV;
}
else
{
2012-05-07 11:59:50 -05:00
parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP);
s = 0;
}
#else
if (rand()%4==0) t = PT_SALT;
else t = PT_WTRV;
#endif
2012-01-08 11:39:03 -06:00
}
else s = 0;
2012-05-07 11:59:50 -05:00
} else if (ctempl<elements[t].LowTemperature&&elements[t].LowTemperatureTransition>-1) {
2012-01-08 11:39:03 -06:00
// particle type change due to low temperature
#ifdef REALISTIC
float dbt = ctempl - pt;
2012-05-07 11:59:50 -05:00
if (elements[t].LowTemperatureTransition!=PT_NUM)
{
if (platent[elements[t].LowTemperatureTransition] >= (c_heat - (elements[t].LowTemperature - dbt)*c_Cm))
{
pt = (c_heat + platent[elements[t].LowTemperatureTransition])/c_Cm;
2012-05-07 11:59:50 -05:00
t = elements[t].LowTemperatureTransition;
}
else
{
2012-05-07 11:59:50 -05:00
parts[i].temp = restrict_flt(elements[t].LowTemperature - dbt, MIN_TEMP, MAX_TEMP);
s = 0;
}
}
#else
if (elements[t].LowTemperatureTransition!=PT_NUM)
t = elements[t].LowTemperatureTransition;
#endif
2012-01-08 11:39:03 -06:00
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) {
2012-05-07 11:59:50 -05:00
if (parts[i].ctype==PT_THRM&&pt>=elements[PT_BMTL].HighTemperature) s = 0;
else if (elements[parts[i].ctype].HighTemperatureTransition==PT_LAVA) {
if (pt>=elements[parts[i].ctype].HighTemperature) s = 0;
2012-01-08 11:39:03 -06:00
}
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;
#ifdef REALISTIC
pt = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
for (j=0; j<8; j++)
{
parts[surround_hconduct[j]].temp = pt;
}
#endif
2012-01-08 11:39:03 -06:00
if (s) { // particle type change occurred
if (t==PT_ICEI||t==PT_LAVA||t==PT_SNOW)
2012-01-08 11:39:03 -06:00
parts[i].ctype = parts[i].type;
if (!(t==PT_ICEI&&parts[i].ctype==PT_FRZW)) parts[i].life = 0;
2012-05-07 11:59:50 -05:00
if (elements[t].State==ST_GAS&&elements[parts[i].type].State!=ST_GAS)
2012-01-08 11:39:03 -06:00
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;
}
}
}
else parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP);
2012-01-08 11:39:03 -06:00
}
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
2012-05-07 11:59:50 -05:00
if ((elements[t].Properties&PROP_CONDUCTS) || t==PT_SPRK)
2012-01-08 11:39:03 -06:00
{
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
2012-05-07 11:59:50 -05:00
if ((elements[t].Explosive&2) && pv[y/CELL][x/CELL]>2.5f)
2012-01-08 11:39:03 -06:00
{
parts[i].life = rand()%80+180;
2012-05-07 11:59:50 -05:00
parts[i].temp = restrict_flt(elements[PT_FIRE].Temperature + (elements[t].Flammable/2), MIN_TEMP, MAX_TEMP);
2012-01-08 11:39:03 -06:00
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)]);
2012-05-07 11:59:50 -05:00
if (pv[y/CELL][x/CELL]>elements[t].HighPressure&&elements[t].HighPressureTransition>-1) {
2012-01-08 11:39:03 -06:00
// particle type change due to high pressure
2012-05-07 11:59:50 -05:00
if (elements[t].HighPressureTransition!=PT_NUM)
t = elements[t].HighPressureTransition;
2012-01-08 11:39:03 -06:00
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;
2012-05-07 11:59:50 -05:00
} else if (pv[y/CELL][x/CELL]<elements[t].LowPressure&&elements[t].LowPressureTransition>-1) {
2012-01-08 11:39:03 -06:00
// particle type change due to low pressure
2012-05-07 11:59:50 -05:00
if (elements[t].LowPressureTransition!=PT_NUM)
t = elements[t].LowPressureTransition;
2012-01-08 11:39:03 -06:00
else s = 0;
2012-05-07 11:59:50 -05:00
} else if (gravtot>(elements[t].HighPressure/4.0f)&&elements[t].HighPressureTransition>-1) {
2012-01-08 11:39:03 -06:00
// particle type change due to high gravity
2012-05-07 11:59:50 -05:00
if (elements[t].HighPressureTransition!=PT_NUM)
t = elements[t].HighPressureTransition;
2012-01-08 11:39:03 -06:00
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
2012-05-07 11:59:50 -05:00
if (elements[t].Update && lua_el_mode[t] != 2)
2012-01-08 11:39:03 -06:00
#else
2012-05-07 11:59:50 -05:00
if (elements[t].Update)
2012-01-08 11:39:03 -06:00
#endif
{
2012-05-07 11:59:50 -05:00
if ((*(elements[t].Update))(this, i,x,y,surround_space,nt, parts, pmap))
2012-01-08 11:39:03 -06:00
continue;
else if (t==PT_WARP)
{
// Warp does some movement in its update func, update variables to avoid incorrect data in pmap
x = (int)(parts[i].x+0.5f);
y = (int)(parts[i].y+0.5f);
}
2012-01-08 11:39:03 -06:00
}
#ifdef LUACONSOLE
if(lua_el_mode[t])
{
if(luacon_part_update(t,i,x,y,surround_space,nt))
continue;
// Need to update variables, in case they've been changed by Lua
x = (int)(parts[i].x+0.5f);
y = (int)(parts[i].y+0.5f);
2012-01-08 11:39:03 -06:00
}
#endif
2012-08-25 10:41:34 -05:00
2012-08-20 07:41:03 -05:00
if(legacy_enable)//if heat sim is off
Element::legacyUpdate(this, i,x,y,surround_space,nt, parts, pmap);
2012-01-08 11:39:03 -06:00
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;
mv = fmaxf(fabsf(parts[i].vx), fabsf(parts[i].vy));
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 || !eval_move(t,fin_x,fin_y,NULL))))
2012-01-08 11:39:03 -06:00
{
// 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;
}
if (bmap[fin_y/CELL][fin_x/CELL]==WL_DETECT && emap[fin_y/CELL][fin_x/CELL]<8)
set_emap(fin_x/CELL, fin_y/CELL);
2012-01-08 11:39:03 -06:00
}
}
stagnant = parts[i].flags & FLAG_STAGNANT;
parts[i].flags &= ~FLAG_STAGNANT;
if (t==PT_STKM || t==PT_STKM2 || t==PT_FIGH)
{
int nx, ny;
//head movement, let head pass through anything
parts[i].x += parts[i].vx;
parts[i].y += parts[i].vy;
nx = (int)((float)parts[i].x+0.5f);
ny = (int)((float)parts[i].y+0.5f);
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(i);
continue;
}
if (elements[t].Properties & TYPE_ENERGY)
photons[ny][nx] = t|(i<<8);
else if (t)
pmap[ny][nx] = t|(i<<8);
}
}
else if (elements[t].Properties & TYPE_ENERGY)
{
2012-01-08 11:39:03 -06:00
if (t == PT_PHOT) {
if (parts[i].flags&FLAG_SKIPMOVE)
{
parts[i].flags &= ~FLAG_SKIPMOVE;
continue;
}
2012-01-08 11:39:03 -06:00
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) && parts[i].type) {
2012-01-08 11:39:03 -06:00
kill_part(i);
continue;
}
}
else if (!do_move(i, x, y, fin_xf, fin_yf))
{
if (parts[i].type == PT_NONE)
continue;
2012-01-08 11:39:03 -06:00
// 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 || (r&0xFF) == PT_PPIP) && !(parts[r>>8].tmp&0xFF))
2012-01-08 11:39:03 -06:00
{
parts[r>>8].tmp = (parts[r>>8].tmp&~0xFF) | parts[i].type;
parts[r>>8].temp = parts[i].temp;
parts[r>>8].tmp2 = parts[i].life;
2012-01-08 11:39:03 -06:00
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&0x3FFFFFFF)&&t!=PT_NEUT&&t!=PT_ELEC) {
2012-01-08 11:39:03 -06:00
kill_part(i);
continue;
}
}
}
2012-05-07 11:59:50 -05:00
else if (elements[t].Falldown==0)
2012-01-08 11:39:03 -06:00
{
// gasses and solids (but not powders)
if (!do_move(i, x, y, fin_xf, fin_yf))
{
if (parts[i].type == PT_NONE)
continue;
2012-01-08 11:39:03 -06:00
// can't move there, so bounce off
// TODO
// TODO: Work out what previous TODO was for
2012-01-08 11:39:03 -06:00
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))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
else if (do_move(i, x, y, 0.25f+fin_x, 0.25f+(float)(2*y-fin_y)))
{
2012-05-07 11:59:50 -05:00
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
else
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
}
}
else
{
2012-05-07 11:59:50 -05:00
if (water_equal_test && elements[t].Falldown == 2 && 1>= rand()%400)//checking stagnant is cool, but then it doesn't update when you change it later.
2012-01-08 11:39:03 -06:00
{
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 (parts[i].type == PT_NONE)
continue;
2012-01-08 11:39:03 -06:00
if (fin_x!=x && do_move(i, x, y, fin_xf, clear_yf))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
else if (fin_y!=y && do_move(i, x, y, clear_xf, fin_yf))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
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))
2012-01-08 11:39:03 -06:00
{
// 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))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
goto movedone;
}
swappage = dx;
dx = dy*r;
dy = -swappage*r;
if (do_move(i, x, y, clear_xf+dx, clear_yf+dy))
{
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
goto movedone;
}
}
if (elements[t].Falldown>1 && !grav->ngrav_enable && gravityMode==0 && parts[i].vy>fabsf(parts[i].vx))
2012-01-08 11:39:03 -06:00
{
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;
if (t==PT_GEL)
rt = parts[i].tmp*0.20f+5.0f;
2012-01-08 11:39:03 -06:00
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)))
2012-01-08 11:39:03 -06:00
{
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)))
2012-01-08 11:39:03 -06:00
{
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;
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
2012-05-07 11:59:50 -05:00
else if (elements[t].Falldown>1 && fabsf(pGravX*parts[i].vx+pGravY*parts[i].vy)>fabsf(pGravY*parts[i].vx-pGravX*parts[i].vy))
2012-01-08 11:39:03 -06:00
{
2012-05-07 11:59:50 -05:00
float nxf, nyf, prev_pGravX, prev_pGravY, ptGrav = elements[t].Gravity;
2012-01-08 11:39:03 -06:00
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;
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
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;
2012-05-07 11:59:50 -05:00
parts[i].vx *= elements[t].Collision;
parts[i].vy *= elements[t].Collision;
2012-01-08 11:39:03 -06:00
}
}
}
}
movedone:
continue;
}
}
int Simulation::GetParticleType(std::string type)
{
int i = -1;
char * txt = (char*)type.c_str();
// alternative names for some elements
if (strcasecmp(txt,"C4")==0) i = PT_PLEX;
else if (strcasecmp(txt,"C5")==0) i = PT_C5;
else if (strcasecmp(txt,"NONE")==0) i = PT_NONE;
for (i=1; i<PT_NUM; i++) {
if (strcasecmp(txt, elements[i].Name)==0 && strlen(elements[i].Name) && elements[i].Enabled)
{
return i;
}
}
return -1;
}
2012-01-08 11:39:03 -06:00
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
2012-02-02 05:55:43 -06:00
if(!sys_pause||framerender)
2012-04-16 07:58:20 -05:00
{
2012-02-02 05:55:43 -06:00
air->update_air();
2012-04-16 07:58:20 -05:00
if(aheat_enable)
air->update_airh();
if(grav->ngrav_enable)
{
grav->gravity_update_async();
//Get updated buffer pointers for gravity
gravx = grav->gravx;
gravy = grav->gravy;
gravp = grav->gravp;
gravmap = grav->gravmap;
2012-08-05 08:03:54 -05:00
if(gravWallChanged)
{
grav->gravity_mask();
gravWallChanged = false;
}
}
2012-08-21 07:00:31 -05:00
if(emp_decor>0)
emp_decor -= emp_decor/25+2;
if(emp_decor < 0)
emp_decor = 0;
2012-04-16 07:58:20 -05:00
}
2012-01-11 16:59:45 -06:00
2012-01-08 11:39:03 -06:00
memset(pmap, 0, sizeof(pmap));
2012-06-12 19:21:33 -05:00
memset(pmap_count, 0, sizeof(pmap_count));
2012-01-08 11:39:03 -06:00
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 (elements[t].Properties & TYPE_ENERGY)
2012-01-08 11:39:03 -06:00
photons[y][x] = t|(i<<8);
else
2012-06-12 19:21:33 -05:00
{
// Particles are sometimes allowed to go inside INVS and FILT
// To make particles collide correctly when inside these elements, these elements must not overwrite an existing pmap entry from particles inside them
if (!pmap[y][x] || (t!=PT_INVIS && t!= PT_FILT))
pmap[y][x] = t|(i<<8);
// (there are a few exceptions, including energy particles - currently no limit on stacking those)
if (t!=PT_THDR && t!=PT_EMBR && t!=PT_FIGH)
pmap_count[y][x]++;
2012-06-12 19:21:33 -05:00
}
2012-01-08 11:39:03 -06:00
}
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] --;
air->bmap_blockair[y][x] = (bmap[y][x]==WL_WALL || bmap[y][x]==WL_WALLELEC || (bmap[y][x]==WL_EWALL && !emap[y][x]));
air->bmap_blockairh[y][x] = (bmap[y][x]==WL_WALL || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_GRAV || (bmap[y][x]==WL_EWALL && !emap[y][x]));
2012-01-08 11:39:03 -06:00
}
}
}
2012-02-02 05:55:43 -06:00
if(!sys_pause||framerender)
update_particles_i(0, 1);
2012-01-08 11:39:03 -06:00
2012-02-02 05:55:43 -06:00
if(framerender)
framerender--;
2012-01-08 11:39:03 -06:00
// 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()
{
delete[] platent;
delete grav;
delete air;
2012-06-18 09:32:15 -05:00
for(int i = 0; i < tools.size(); i++)
delete tools[i];
}
Simulation::Simulation():
sys_pause(0)
2012-01-08 11:39:03 -06:00
{
int tportal_rx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
int tportal_ry[] = {-1,-1,-1, 0, 1, 1, 1, 0};
memcpy(portal_rx, tportal_rx, sizeof(tportal_rx));
memcpy(portal_ry, tportal_ry, sizeof(tportal_ry));
2012-04-20 13:21:10 -05:00
currentTick = 0;
std::fill(elementCount, elementCount+PT_NUM, 0);
2012-01-08 11:39:03 -06:00
//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(*this);
2012-01-08 11:39:03 -06:00
//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;
2012-01-24 14:19:19 -06:00
int menuCount;
menu_section * msectionsT = LoadMenus(menuCount);
memcpy(msections, msectionsT, menuCount * sizeof(menu_section));
2012-01-25 12:42:35 -06:00
free(msectionsT);
2012-01-24 14:19:19 -06:00
2012-01-24 15:19:29 -06:00
int wallCount;
wall_type * wtypesT = LoadWalls(wallCount);
memcpy(wtypes, wtypesT, wallCount * sizeof(wall_type));
2012-01-25 12:42:35 -06:00
free(wtypesT);
2012-01-24 15:19:29 -06:00
platent = new unsigned[PT_NUM];
int latentCount;
unsigned int * platentT = LoadLatent(latentCount);
memcpy(platent, platentT, latentCount * sizeof(unsigned int));
free(platentT);
//elements = new Element[PT_NUM];
2012-05-07 11:59:50 -05:00
std::vector<Element> elementList = GetElements();
for(int i = 0; i < PT_NUM; i++)
2012-05-07 11:59:50 -05:00
{
if(i < elementList.size())
elements[i] = elementList[i];
else
2012-08-18 11:19:39 -05:00
elements[i] = Element();
2012-05-07 11:59:50 -05:00
}
tools = GetTools();
2012-01-25 12:42:35 -06:00
2012-02-01 18:33:10 -06:00
int golRulesCount;
int * golRulesT = LoadGOLRules(golRulesCount);
memcpy(grule, golRulesT, sizeof(int) * (golRulesCount*10));
free(golRulesT);
2012-04-09 05:46:42 -05:00
int golTypesCount;
int * golTypesT = LoadGOLTypes(golTypesCount);
memcpy(goltype, golTypesT, sizeof(int) * (golTypesCount));
free(golTypesT);
2012-02-01 18:33:10 -06:00
int golMenuCount;
gol_menu * golMenuT = LoadGOLMenu(golMenuCount);
memcpy(gmenu, golMenuT, sizeof(gol_menu) * golMenuCount);
free(golMenuT);
2012-01-08 11:39:03 -06:00
init_can_move();
clear_sim();
2012-04-16 07:58:20 -05:00
grav->gravity_mask();
}