Save loading and reloading

This commit is contained in:
Simon Robertshaw 2012-01-26 16:18:43 +00:00
parent 9e1be78bc2
commit d520a70acf
9 changed files with 679 additions and 38 deletions

View File

@ -29,6 +29,7 @@
#define SERVER "powdertoy.co.uk"
#define SCRIPTSERVER "powdertoy.co.uk"
#define STATICSERVER "static.powdertoy.co.uk"
#define LOCAL_SAVE_DIR "Saves"

View File

@ -42,7 +42,28 @@ Client::~Client()
unsigned char * Client::GetSaveData(int saveID, int saveDate, int & dataLength)
{
lastError = "";
int dataStatus;
unsigned char * data;
dataLength = 0;
std::stringstream urlStream;
if(saveDate)
{
urlStream << "http://" << STATICSERVER << "/" << saveID << "_" << saveDate << ".cps";
}
else
{
urlStream << "http://" << STATICSERVER << "/" << saveID << ".cps";
}
data = (unsigned char *)http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
if(data && dataStatus == 200)
{
return data;
}
else if(data)
{
free(data);
}
return NULL;
}
@ -175,11 +196,12 @@ Save * Client::GetSave(int saveID, int saveDate)
Thumbnail * Client::GetPreview(int saveID, int saveDate)
{
std::stringstream urlStream;
urlStream << "http://" << SERVER << "/Get.api?Op=thumblarge&ID=" << saveID;
urlStream << "http://" << STATICSERVER << "/" << saveID;
if(saveDate)
{
urlStream << "&Date=" << saveDate;
urlStream << "_" << saveDate;
}
urlStream << "_large.pti";
pixel * thumbData;
char * data;
int status, data_size, imgw, imgh;
@ -322,11 +344,12 @@ Thumbnail * Client::GetThumbnail(int saveID, int saveDate)
if(thumbnailCache[i] && thumbnailCache[i]->ID == saveID && thumbnailCache[i]->Datestamp == saveDate)
return thumbnailCache[i];
}
urlStream << "http://" << SERVER << "/Get.api?Op=thumbsmall&ID=" << saveID;
urlStream << "http://" << STATICSERVER << "/" << saveID;
if(saveDate)
{
urlStream << "&Date=" << saveDate;
urlStream << "_" << saveDate;
}
urlStream << "_small.pti";
idStream << saveID << ":" << saveDate;
std::string idString = idStream.str();
bool found = false;

View File

@ -148,7 +148,7 @@ void GameController::DrawPoints(queue<ui::Point*> & pointQueue)
void GameController::Update()
{
//gameModel->GetSimulation()->update_particles();
gameModel->GetSimulation()->update_particles();
if(renderOptions && renderOptions->HasExited)
{
delete renderOptions;
@ -234,7 +234,8 @@ void GameController::ClearSim()
void GameController::ReloadSim()
{
//TODO: Implement
if(gameModel->GetSave() && gameModel->GetSave()->GetData())
gameModel->GetSimulation()->Load(gameModel->GetSave()->GetData(), gameModel->GetSave()->GetDataLength());
}

View File

@ -34,6 +34,14 @@ Save::Save(int _id, int date_, int _votesUp, int _votesDown, string _userName,
published_), data(NULL) {
}
Save::~Save()
{
if(data)
{
free(data);
}
}
void Save::SetName(string name) {
this->name = name;
}

View File

@ -23,6 +23,8 @@ public:
Save(int _id, int date_, int _votesUp, int _votesDown, string _userName, string _name, string description_, bool published_);
~Save();
string userName;
string name;

View File

@ -5,16 +5,32 @@
* Author: Simon
*/
#include <bzlib.h>
#include "SaveLoader.h"
int SaveLoader::LoadSave(unsigned char * data, int dataLength, Simulation * sim)
//!TODO: enum for LoadSave return
int SaveLoader::LoadSave(unsigned char * data, int dataLength, Simulation * sim, bool replace, int x, int y)
{
return 0;
unsigned char * saveData = data;
if (dataLength<16)
{
return 1;
}
if(saveData[0] == 'O' && saveData[1] == 'P' && saveData[2] == 'S')
{
return OPSLoadSave(data, dataLength, sim);
}
else if((saveData[0]==0x66 && saveData[1]==0x75 && saveData[2]==0x43) || (saveData[0]==0x50 && saveData[1]==0x53 && saveData[2]==0x76))
{
return PSVLoadSave(data, dataLength, sim, replace, x, y);
}
return 1;
}
unsigned char * SaveLoader::BuildSave(int & dataLength, Simulation * sim)
{
return 0;
return OPSBuildSave(dataLength, sim);
}
int SaveLoader::OPSLoadSave(unsigned char * data, int dataLength, Simulation * sim)
@ -27,9 +43,598 @@ unsigned char * SaveLoader::OPSBuildSave(int & dataLength, Simulation * sim)
return 0;
}
int SaveLoader::PSVLoadSave(unsigned char * data, int dataLength, Simulation * sim)
int SaveLoader::PSVLoadSave(unsigned char * data, int dataLength, Simulation * sim, bool replace, int x0, int y0)
{
unsigned char * d = NULL, * c = data;
int q,i,j,k,x,y,p=0,*m=NULL, ver, pty, ty, legacy_beta=0, tempGrav = 0;
int bx0=x0/CELL, by0=y0/CELL, bw, bh, w, h;
int nf=0, new_format = 0, ttv = 0;
Particle *parts = sim->parts;
int *fp = (int *)malloc(NPART*sizeof(int));
//New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
//This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error
if (dataLength<16)
return 1;
if (!(c[2]==0x43 && c[1]==0x75 && c[0]==0x66) && !(c[2]==0x76 && c[1]==0x53 && c[0]==0x50))
return 1;
if (c[2]==0x76 && c[1]==0x53 && c[0]==0x50) {
new_format = 1;
}
if (c[4]>SAVE_VERSION)
return 2;
ver = c[4];
if (ver<34)
{
sim->legacy_enable = 1;
}
else
{
if (ver>=44) {
sim->legacy_enable = c[3]&0x01;
if (!sim->sys_pause) {
sim->sys_pause = (c[3]>>1)&0x01;
}
if (ver>=46 && replace) {
sim->gravityMode = ((c[3]>>2)&0x03);// | ((c[3]>>2)&0x01);
sim->airMode = ((c[3]>>4)&0x07);// | ((c[3]>>4)&0x02) | ((c[3]>>4)&0x01);
}
if (ver>=49 && replace) {
tempGrav = ((c[3]>>7)&0x01);
}
} else {
if (c[3]==1||c[3]==0) {
sim->legacy_enable = c[3];
} else {
legacy_beta = 1;
}
}
}
bw = c[6];
bh = c[7];
if (bx0+bw > XRES/CELL)
bx0 = XRES/CELL - bw;
if (by0+bh > YRES/CELL)
by0 = YRES/CELL - bh;
if (bx0 < 0)
bx0 = 0;
if (by0 < 0)
by0 = 0;
if (c[5]!=CELL || bx0+bw>XRES/CELL || by0+bh>YRES/CELL)
return 3;
i = (unsigned)c[8];
i |= ((unsigned)c[9])<<8;
i |= ((unsigned)c[10])<<16;
i |= ((unsigned)c[11])<<24;
d = (unsigned char *)malloc(i);
if (!d)
return 1;
if (BZ2_bzBuffToBuffDecompress((char *)d, (unsigned *)&i, (char *)(c+12), dataLength-12, 0, 0))
return 1;
dataLength = i;
if (dataLength < bw*bh)
return 1;
// normalize coordinates
x0 = bx0*CELL;
y0 = by0*CELL;
w = bw *CELL;
h = bh *CELL;
if (replace)
{
if (ver<46) {
sim->gravityMode = 0;
sim->airMode = 0;
}
sim->clear_sim();
}
sim->parts_lastActiveIndex = NPART-1;
m = (int *)calloc(XRES*YRES, sizeof(int));
// make a catalog of free parts
//memset(pmap, 0, sizeof(pmap)); "Using sizeof for array given as function argument returns the size of pointer."
memset(sim->pmap, 0, sizeof(unsigned)*(XRES*YRES));
for (i=0; i<NPART; i++)
if (parts[i].type)
{
x = (int)(parts[i].x+0.5f);
y = (int)(parts[i].y+0.5f);
sim->pmap[y][x] = (i<<8)|1;
}
else
fp[nf++] = i;
// load the required air state
for (y=by0; y<by0+bh; y++)
for (x=bx0; x<bx0+bw; x++)
{
if (d[p])
{
//In old saves, ignore walls created by sign tool bug
//Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix
if (ver<71 && d[p]==WL_SIGN)
{
p++;
continue;
}
sim->bmap[y][x] = d[p];
if (sim->bmap[y][x]==1)
sim->bmap[y][x]=WL_WALL;
if (sim->bmap[y][x]==2)
sim->bmap[y][x]=WL_DESTROYALL;
if (sim->bmap[y][x]==3)
sim->bmap[y][x]=WL_ALLOWLIQUID;
if (sim->bmap[y][x]==4)
sim->bmap[y][x]=WL_FAN;
if (sim->bmap[y][x]==5)
sim->bmap[y][x]=WL_STREAM;
if (sim->bmap[y][x]==6)
sim->bmap[y][x]=WL_DETECT;
if (sim->bmap[y][x]==7)
sim->bmap[y][x]=WL_EWALL;
if (sim->bmap[y][x]==8)
sim->bmap[y][x]=WL_WALLELEC;
if (sim->bmap[y][x]==9)
sim->bmap[y][x]=WL_ALLOWAIR;
if (sim->bmap[y][x]==10)
sim->bmap[y][x]=WL_ALLOWSOLID;
if (sim->bmap[y][x]==11)
sim->bmap[y][x]=WL_ALLOWALLELEC;
if (sim->bmap[y][x]==12)
sim->bmap[y][x]=WL_EHOLE;
if (sim->bmap[y][x]==13)
sim->bmap[y][x]=WL_ALLOWGAS;
}
p++;
}
for (y=by0; y<by0+bh; y++)
for (x=bx0; x<bx0+bw; x++)
if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==WL_FAN)
{
if (p >= dataLength)
goto corrupt;
sim->fvx[y][x] = (d[p++]-127.0f)/64.0f;
}
for (y=by0; y<by0+bh; y++)
for (x=bx0; x<bx0+bw; x++)
if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==WL_FAN)
{
if (p >= dataLength)
goto corrupt;
sim->fvy[y][x] = (d[p++]-127.0f)/64.0f;
}
// load the particle map
i = 0;
pty = p;
for (y=y0; y<y0+h; y++)
for (x=x0; x<x0+w; x++)
{
if (p >= dataLength)
goto corrupt;
j=d[p++];
if (j >= PT_NUM) {
//TODO: Possibly some server side translation
j = PT_DUST;//goto corrupt;
}
sim->gol[x][y]=0;
if (j)
{
if (sim->pmap[y][x])
{
k = sim->pmap[y][x]>>8;
}
else if (i<nf)
{
k = fp[i];
i++;
}
else
{
m[(x-x0)+(y-y0)*w] = NPART+1;
continue;
}
memset(parts+k, 0, sizeof(Particle));
parts[k].type = j;
if (j == PT_COAL)
parts[k].tmp = 50;
if (j == PT_FUSE)
parts[k].tmp = 50;
if (j == PT_PHOT)
parts[k].ctype = 0x3fffffff;
if (j == PT_SOAP)
parts[k].ctype = 0;
if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS)
parts[k].ctype = 0x47FFFF;
parts[k].x = (float)x;
parts[k].y = (float)y;
m[(x-x0)+(y-y0)*w] = k+1;
}
}
// load particle properties
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
i--;
if (p+1 >= dataLength)
goto corrupt;
if (i < NPART)
{
parts[i].vx = (d[p++]-127.0f)/16.0f;
parts[i].vy = (d[p++]-127.0f)/16.0f;
}
else
p += 2;
}
}
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (ver>=44) {
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
ttv = (d[p++])<<8;
ttv |= (d[p++]);
parts[i-1].life = ttv;
} else {
p+=2;
}
} else {
if (p >= dataLength)
goto corrupt;
if (i <= NPART)
parts[i-1].life = d[p++]*4;
else
p++;
}
}
}
if (ver>=44) {
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
ttv = (d[p++])<<8;
ttv |= (d[p++]);
parts[i-1].tmp = ttv;
if (ver<53 && !parts[i-1].tmp)
for (q = 1; q<=NGOLALT; q++) {
if (parts[i-1].type==sim->goltype[q-1] && sim->grule[q][9]==2)
parts[i-1].tmp = sim->grule[q][9]-1;
}
if (ver>=51 && ver<53 && parts[i-1].type==PT_PBCN)
{
parts[i-1].tmp2 = parts[i-1].tmp;
parts[i-1].tmp = 0;
}
} else {
p+=2;
}
}
}
}
if (ver>=53) {
for (j=0; j<w*h; j++)
{
i = m[j];
ty = d[pty+j];
if (i && ty==PT_PBCN)
{
if (p >= dataLength)
goto corrupt;
if (i <= NPART)
parts[i-1].tmp2 = d[p++];
else
p++;
}
}
}
//Read ALPHA component
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (ver>=49) {
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
parts[i-1].dcolour = d[p++]<<24;
} else {
p++;
}
}
}
}
//Read RED component
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (ver>=49) {
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
parts[i-1].dcolour |= d[p++]<<16;
} else {
p++;
}
}
}
}
//Read GREEN component
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (ver>=49) {
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
parts[i-1].dcolour |= d[p++]<<8;
} else {
p++;
}
}
}
}
//Read BLUE component
for (j=0; j<w*h; j++)
{
i = m[j];
if (i)
{
if (ver>=49) {
if (p >= dataLength) {
goto corrupt;
}
if (i <= NPART) {
parts[i-1].dcolour |= d[p++];
} else {
p++;
}
}
}
}
for (j=0; j<w*h; j++)
{
i = m[j];
ty = d[pty+j];
if (i)
{
if (ver>=34&&legacy_beta==0)
{
if (p >= dataLength)
{
goto corrupt;
}
if (i <= NPART)
{
if (ver>=42) {
if (new_format) {
ttv = (d[p++])<<8;
ttv |= (d[p++]);
if (parts[i-1].type==PT_PUMP) {
parts[i-1].temp = ttv + 0.15;//fix PUMP saved at 0, so that it loads at 0.
} else {
parts[i-1].temp = ttv;
}
} else {
parts[i-1].temp = (d[p++]*((MAX_TEMP+(-MIN_TEMP))/255))+MIN_TEMP;
}
} else {
parts[i-1].temp = ((d[p++]*((O_MAX_TEMP+(-O_MIN_TEMP))/255))+O_MIN_TEMP)+273;
}
}
else
{
p++;
if (new_format) {
p++;
}
}
}
else
{
parts[i-1].temp = sim->ptypes[parts[i-1].type].heat;
}
}
}
for (j=0; j<w*h; j++)
{
int gnum = 0;
i = m[j];
ty = d[pty+j];
if (i && (ty==PT_CLNE || (ty==PT_PCLN && ver>=43) || (ty==PT_BCLN && ver>=44) || (ty==PT_SPRK && ver>=21) || (ty==PT_LAVA && ver>=34) || (ty==PT_PIPE && ver>=43) || (ty==PT_LIFE && ver>=51) || (ty==PT_PBCN && ver>=52) || (ty==PT_WIRE && ver>=55) || (ty==PT_STOR && ver>=59) || (ty==PT_CONV && ver>=60)))
{
if (p >= dataLength)
goto corrupt;
if (i <= NPART)
parts[i-1].ctype = d[p++];
else
p++;
}
//TODO: STKM_init_legs
// no more particle properties to load, so we can change type here without messing up loading
if (i && i<=NPART)
{
if ((sim->player.spwn == 1 && ty==PT_STKM) || (sim->player2.spwn == 1 && ty==PT_STKM2))
{
parts[i-1].type = PT_NONE;
}
else if (parts[i-1].type == PT_STKM)
{
//STKM_init_legs(&player, i-1);
sim->player.spwn = 1;
sim->player.elem = PT_DUST;
}
else if (parts[i-1].type == PT_STKM2)
{
//STKM_init_legs(&player2, i-1);
sim->player2.spwn = 1;
sim->player2.elem = PT_DUST;
}
else if (parts[i-1].type == PT_FIGH)
{
unsigned char fcount = 0;
while (fcount < 100 && fcount < (sim->fighcount+1) && sim->fighters[fcount].spwn==1) fcount++;
if (fcount < 100 && sim->fighters[fcount].spwn==0)
{
parts[i-1].tmp = fcount;
sim->fighters[fcount].spwn = 1;
sim->fighters[fcount].elem = PT_DUST;
sim->fighcount++;
//STKM_init_legs(&(fighters[fcount]), i-1);
}
}
if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&parts[i-1].life==0)))
{
// Replace invisible particles with something sensible and add decoration to hide it
x = (int)(parts[i-1].x+0.5f);
y = (int)(parts[i-1].y+0.5f);
parts[i-1].dcolour = 0xFF000000;
parts[i-1].type = PT_DMND;
}
if(ver<51 && ((ty>=78 && ty<=89) || (ty>=134 && ty<=146 && ty!=141))){
//Replace old GOL
parts[i-1].type = PT_LIFE;
for (gnum = 0; gnum<NGOLALT; gnum++){
if (ty==sim->goltype[gnum])
parts[i-1].ctype = gnum;
}
ty = PT_LIFE;
}
if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){
//Replace old GOL ctypes in clone
for (gnum = 0; gnum<NGOLALT; gnum++){
if (parts[i-1].ctype==sim->goltype[gnum])
{
parts[i-1].ctype = PT_LIFE;
parts[i-1].tmp = gnum;
}
}
}
if(ty==PT_LCRY){
if(ver<67)
{
//New LCRY uses TMP not life
if(parts[i-1].life>=10)
{
parts[i-1].life = 10;
parts[i-1].tmp2 = 10;
parts[i-1].tmp = 3;
}
else if(parts[i-1].life<=0)
{
parts[i-1].life = 0;
parts[i-1].tmp2 = 0;
parts[i-1].tmp = 0;
}
else if(parts[i-1].life < 10 && parts[i-1].life > 0)
{
parts[i-1].tmp = 1;
}
}
else
{
parts[i-1].tmp2 = parts[i-1].life;
}
}
if (!sim->ptypes[parts[i-1].type].enabled)
parts[i-1].type = PT_NONE;
}
}
#ifndef RENDERER
//Change the gravity state
if(sim->ngrav_enable != tempGrav && replace)
{
if(tempGrav)
sim->grav->start_grav_async();
else
sim->grav->stop_grav_async();
}
#endif
sim->grav->gravity_mask();
if (p >= dataLength)
goto version1;
j = d[p++];
for (i=0; i<j; i++)
{
if (p+6 > dataLength)
goto corrupt;
for (k=0; k<MAXSIGNS; k++)
if (!sim->signs[k].text[0])
break;
x = d[p++];
x |= ((unsigned)d[p++])<<8;
if (k<MAXSIGNS)
sim->signs[k].x = x+x0;
x = d[p++];
x |= ((unsigned)d[p++])<<8;
if (k<MAXSIGNS)
sim->signs[k].y = x+y0;
x = d[p++];
if (k<MAXSIGNS)
sim->signs[k].ju = x;
x = d[p++];
if (p+x > dataLength)
goto corrupt;
if (k<MAXSIGNS)
{
memcpy(sim->signs[k].text, d+p, x);
sim->signs[k].text[x] = 0;
//clean_text(signs[k].text, 158-14 /* Current max sign length */); //TODO: Text cleanup for signs
}
p += x;
}
version1:
if (m) free(m);
if (d) free(d);
if (fp) free(fp);
return 0;
corrupt:
if (m) free(m);
if (d) free(d);
if (fp) free(fp);
if (replace)
{
sim->legacy_enable = 0;
sim->clear_sim();
}
return 1;
}
unsigned char * PSVBuildSave(int & dataLength, Simulation * sim)

View File

@ -12,11 +12,11 @@
class SaveLoader {
public:
static int LoadSave(unsigned char * data, int dataLength, Simulation * sim);
static int LoadSave(unsigned char * data, int dataLength, Simulation * sim, bool replace, int x, int y);
static unsigned char * BuildSave(int & dataLength, Simulation * sim);
static int OPSLoadSave(unsigned char * data, int dataLength, Simulation * sim);
static unsigned char * OPSBuildSave(int & dataLength, Simulation * sim);
static int PSVLoadSave(unsigned char * data, int dataLength, Simulation * sim);
static int PSVLoadSave(unsigned char * data, int dataLength, Simulation * sim, bool replace, int x, int y);
static unsigned char * PSVBuildSave(int & dataLength, Simulation * sim);
};

View File

@ -10,7 +10,7 @@
int Simulation::Load(unsigned char * data, int dataLength)
{
return SaveLoader::LoadSave(data, dataLength, this);
return SaveLoader::LoadSave(data, dataLength, this, true, 0, 0);
}
unsigned char * Simulation::Save(int & dataLength)

View File

@ -14,6 +14,7 @@
#include "Elements.h"
#include "Misc.h"
#include "game/Brush.h"
#include "Gravity.h"
#include "SimulationData.h"
//#include "ElementFunctions.h"
@ -204,36 +205,36 @@ public:
int sandcolour_r;
int sandcolour_g;
int sandcolour_b; //TODO: Make a single variable
//TODO: Inlines for performance
int Load(unsigned char * data, int dataLength);
unsigned char * Save(int & dataLength);
int is_blocking(int t, int x, int y);
int is_boundary(int pt, int x, int y);
int find_next_boundary(int pt, int *x, int *y, int dm, int *em);
int pn_junction_sprk(int x, int y, int pt);
void photoelectric_effect(int nx, int ny);
unsigned direction_to_map(float dx, float dy, int t);
int do_move(int i, int x, int y, float nxf, float nyf);
int try_move(int i, int x, int y, int nx, int ny);
int eval_move(int pt, int nx, int ny, unsigned *rr);
inline int is_blocking(int t, int x, int y);
inline int is_boundary(int pt, int x, int y);
inline int find_next_boundary(int pt, int *x, int *y, int dm, int *em);
inline int pn_junction_sprk(int x, int y, int pt);
inline void photoelectric_effect(int nx, int ny);
inline unsigned direction_to_map(float dx, float dy, int t);
inline int do_move(int i, int x, int y, float nxf, float nyf);
inline int try_move(int i, int x, int y, int nx, int ny);
inline int eval_move(int pt, int nx, int ny, unsigned *rr);
void init_can_move();
void create_cherenkov_photon(int pp);
void create_gain_photon(int pp);
void kill_part(int i);
inline void kill_part(int i);
int flood_prop(int x, int y, size_t propoffset, void * propvalue, int proptype);
int flood_prop_2(int x, int y, size_t propoffset, void * propvalue, int proptype, int parttype, char * bitmap);
int flood_water(int x, int y, int i, int originaly, int check);
void detach(int i);
void part_change_type(int i, int x, int y, int t);
int create_part_add_props(int p, int x, int y, int tv, int rx, int ry);
inline void detach(int i);
inline void part_change_type(int i, int x, int y, int t);
inline int create_part_add_props(int p, int x, int y, int tv, int rx, int ry);
//int InCurrentBrush(int i, int j, int rx, int ry);
//int get_brush_flags();
int create_part(int p, int x, int y, int t);
void delete_part(int x, int y, int flags);
int is_wire(int x, int y);
int is_wire_off(int x, int y);
void set_emap(int x, int y);
int parts_avg(int ci, int ni, int t);
inline int create_part(int p, int x, int y, int t);
inline void delete_part(int x, int y, int flags);
inline int is_wire(int x, int y);
inline int is_wire_off(int x, int y);
inline void set_emap(int x, int y);
inline int parts_avg(int ci, int ni, int t);
void create_arc(int sx, int sy, int dx, int dy, int midpoints, int variance, int type, int flags);
int nearest_part(int ci, int t, int max_d);
void update_particles_i(int start, int inc);
@ -245,11 +246,11 @@ public:
int create_parts(int x, int y, int rx, int ry, int c, int flags, Brush * cBrush = NULL);
void create_line(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags, Brush * cBrush = NULL);
void *transform_save(void *odata, int *size, matrix2d transform, vector2d translate);
void orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]);
void orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[]);
int get_wavelength_bin(int *wm);
int get_normal(int pt, int x, int y, float dx, float dy, float *nx, float *ny);
int get_normal_interp(int pt, float x0, float y0, float dx, float dy, float *nx, float *ny);
inline void orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]);
inline void orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[]);
inline int get_wavelength_bin(int *wm);
inline int get_normal(int pt, int x, int y, float dx, float dy, float *nx, float *ny);
inline int get_normal_interp(int pt, float x0, float y0, float dx, float dy, float *nx, float *ny);
void clear_sim();
void UpdateParticles();
Simulation();