//#include #include #include #include "Config.h" #include "Simulation.h" #include "Elements.h" //#include "ElementFunctions.h" #include "Air.h" #include "Gravity.h" #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" #include "Snapshot.h" //#include "StorageClasses.h" #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; 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]++; } if (tempPart.type == PT_STKM) { Element_STKM::STKM_init_legs(this, &player, i); player.spwn = 1; player.elem = PT_DUST; } else if (tempPart.type == PT_STKM2) { Element_STKM::STKM_init_legs(this, &player2, i); player2.spwn = 1; player2.elem = PT_DUST; } else if (tempPart.type == PT_FIGH) { //TODO: 100 should be replaced with a macro unsigned char fcount = 0; while (fcount < 100 && fcount < (fighcount+1) && fighters[fcount].spwn==1) fcount++; if (fcount < 100 && fighters[fcount].spwn==0) { tempPart.tmp = fcount; fighters[fcount].spwn = 1; fighters[fcount].elem = PT_DUST; fighcount++; Element_STKM::STKM_init_legs(this, &(fighters[fcount]), i); } } } parts_lastActiveIndex = NPART-1; force_stacking_check = 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]; } } } 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; } 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) { 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); } 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); }*/ 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; cyx2) { 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++) 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); } int Simulation::flood_prop_2(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype, int parttype, char * bitmap) { int x1, x2, i, dy = 1; x1 = x2 = x; while (x1>=CELL) { if ((pmap[y][x1-1]&0xFF)!=parttype || bitmap[(y*XRES)+x1-1]) { break; } x1--; } while (x2>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; } 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>8]; sample.ParticleID = pmap[y][x]>>8; } else if(photons[y][x]) { sample.particle = parts[photons[y][x]>>8]; sample.ParticleID = photons[y][x]>>8; } 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) 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>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>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>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; } int Simulation::FloodParts(int x, int y, int fullc, int cm, int bm, int flags) { int c = fullc&0xFF; int x1, x2, dy = (c=CELL) { if ((pmap[y][x1-1]&0xFF)!=cm || bmap[y/CELL][(x1-1)/CELL]!=bm) { break; } x1--; } // go right as far as possible while (x2=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; } } if (y=coord_stack_limit) { free(coord_stack); return -1; } } } while (coord_stack_size>0); free(coord_stack); return created_something; } 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) { cm = pmap[y][x]&0xFF; } 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=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=CELL) { if ((elements[(pmap[y][x1-1]&0xFF)].Falldown)!=2) { break; } x1--; } while (x2>8].tmp2 = !check;//flag it as checked, maybe shouldn't use .tmp2 //check above, maybe around other sides too? if ( ((y-1) > originaly) && !pmap[y-1][x] && eval_move(parts[i].type, x, y-1, NULL)) { int oldx = (int)(parts[i].x + 0.5f); int oldy = (int)(parts[i].y + 0.5f); pmap[y-1][x] = pmap[oldy][oldx]; pmap[oldy][oldx] = 0; parts[i].x = x; parts[i].y = y-1; return 0; } } // fill children if (y>=CELL+1) for (x=x1; x<=x2; x++) if ((elements[(pmap[y-1][x]&0xFF)].Falldown)==2 && parts[pmap[y-1][x]>>8].tmp2 == check) if (!flood_water(x, y-1, i, originaly, check)) return 0; if (y>8].tmp2 == check) if (!flood_water(x, y+1, i, originaly, check)) return 0; return 1; } //wrapper around create_part to create TESC with correct tmp value int Simulation::create_part_add_props(int p, int x, int y, int tv, int rx, int ry) { p=create_part(p, x, y, tv); if (tv==PT_TESC) { parts[p].tmp=rx*4+ry*4+7; if (parts[p].tmp>300) parts[p].tmp=300; } return p; } 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); } } void Simulation::ApplyDecoration(int x, int y, int colR_, int colG_, int colB_, int colA_, int mode) { int rp; float tr, tg, tb, ta, colR = colR_, colG = colG_, colB = colB_, colA = colA_; rp = pmap[y][x]; if (!rp) return; 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) { ta = colA; tr = colR; tg = colG; tb = colB; } else if (mode == DECO_CLEAR) { ta = tr = tg = tb = 0.0f; } else if (mode == DECO_ADD) { ta += (colA*0.1f)*colA; tr += (colR*0.1f)*colA; tg += (colG*0.1f)*colA; tb += (colB*0.1f)*colA; } else if (mode == DECO_SUBTRACT) { ta -= (colA*0.1f)*colA; tr -= (colR*0.1f)*colA; tg -= (colG*0.1f)*colA; tb -= (colB*0.1f)*colA; } else if (mode == DECO_MULTIPLY) { tr *= 1.0f+(colR*0.1f)*colA; tg *= 1.0f+(colG*0.1f)*colA; tb *= 1.0f+(colB*0.1f)*colA; } else if (mode == DECO_DIVIDE) { tr /= 1.0f+(colR*0.1f)*colA; tg /= 1.0f+(colG*0.1f)*colA; tb /= 1.0f+(colB*0.1f)*colA; } else if (mode == DECO_SMUDGE) { int rx, ry, num = 0; for (rx=-2; rx<3; rx++) for (ry=-2; ry<3; ry++) { if ((pmap[y+ry][x+rx]&0xFF) && parts[pmap[y+ry][x+rx]>>8].dcolour) { Particle part = parts[pmap[y+ry][x+rx]>>8]; num++; ta += float((part.dcolour>>24)&0xFF)/255.0f; tr += float((part.dcolour>>16)&0xFF)/255.0f; tg += float((part.dcolour>>8)&0xFF)/255.0f; tb += float((part.dcolour)&0xFF)/255.0f; } } if (num == 0) return; ta = ta/float(num+1); tr = tr/float(num+1); tg = tg/float(num+1); tb = tb/float(num+1); } colA_ = ta*255.0f; colR_ = tr*255.0f; colG_ = tg*255.0f; colB_ = tb*255.0f; 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= 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 x, int y, int tool, Brush * cBrush, float strength) { int rx, ry, j, i; if(!cBrush) return 0; rx = cBrush->GetRadius().X; ry = cBrush->GetRadius().Y; unsigned char *bitmap = cBrush->GetBitmap(); for (j=-ry; j<=ry; j++) for (i=-rx; i<=rx; i++) if(bitmap[(j+ry)*((rx*2)+1)+(i+rx+1)]) { if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES) continue; Tool(x+i, y+j, tool, strength); } } 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= 0.5f) { y += sy; if ((!(rx+ry)) && ((y1=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); } int Simulation::CreateParts(int positionX, int positionY, int c, Brush * cBrush) { 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(); 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); } } } } } 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; int wall = c - 100; if (c==SPC_WIND || c==PT_FIGH) return 0; if (c==PT_LIGH) { if (lighting_recreate>0 && rx+ry>0) return 0; p=create_part(-2, x, y, c); if (p!=-1) { parts[p].life=rx+ry; if (parts[p].life>55) parts[p].life=55; parts[p].temp=parts[p].life*150; // temperature of the lighting shows the power of the lighting lighting_recreate+=parts[p].life/2+1; return 1; } else return 0; } //eraser if (c == 0) { if (rx==0&&ry==0) { delete_part(x, y, 0); } else { for (j=-ry; j<=ry; j++) for (i=-rx; i<=rx; i++) delete_part(x+i, y+j, 0); } return 1; } if (c == SPC_AIR || c == SPC_HEAT || c == SPC_COOL || c == SPC_VACUUM || c == SPC_PGRV || c == SPC_NGRV) { if (rx==0&&ry==0) { create_part(-2, x, y, c); } else { for (j=-ry; j<=ry; j++) for (i=-rx; i<=rx; i++) { if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES) continue; create_part(-2, x+i, y+j, c); } } return 1; } //else, no special modes, draw element like normal. if (rx==0&&ry==0)//workaround for 1pixel brush/floodfill crashing. todo: find a better fix later. { if (create_part_add_props(-2, x, y, c, rx, ry)==-1) f = 1; } else { for (j=-ry; j<=ry; j++) for (i=-rx; i<=rx; i++) if (create_part_add_props(-2, x+i, y+j, c, rx, ry)==-1) f = 1; } return !f; } 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; x -= rx;///2; y -= ry;///2; for (ox=x; ox<=x+rx+rx; ox++) { for (oy=y; oy<=y+ry+ry; oy++) { if (ox>=0&&ox=0&&oy=0 && i+u=0 && j+vabs(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= 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))) { 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) { 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= 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))) { if (cp) CreateParts(y, x, rx, ry, c, flags); else CreateParts(x, y, rx, ry, c, flags); } 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= 0.5f) { y += sy; if (!(rx+ry) && ((y1=y2))) { if (cp) CreateWalls(y, x, rx, ry, c, flags, cBrush); else CreateWalls(x, y, rx, ry, c, flags, cBrush); } e -= 1.0f; } } } void *Simulation::transform_save(void *odata, int *size, matrix2d transform, vector2d translate) { void *ndata; unsigned char (*bmapo)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char)); unsigned char (*bmapn)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char)); Particle *partst = (Particle*)calloc(sizeof(Particle), NPART); sign *signst = (sign*)calloc(MAXSIGNS, sizeof(sign)); unsigned (*pmapt)[XRES] = (unsigned (*)[XRES])calloc(YRES*XRES, sizeof(unsigned)); float (*fvxo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); float (*fvyo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); float (*fvxn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); float (*fvyn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float)); 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)); int i, x, y, nx, ny, w, h, nw, nh; vector2d pos, tmp, ctl, cbr; vector2d vel; 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 { 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); 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.xcbr.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=nw || ny<0 || ny>=nh) { signst[i].text[0] = 0; continue; } signst[i].x = nx; signst[i].y = ny; } for (i=0; i=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; } for (y=0; y=nw/CELL || ny<0 || ny>=nh/CELL) 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; } } 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]; } //ndata = build_save(size,0,0,nw,nh,bmapn,vxn,vyn,pvn,fvxn,fvyn,signst,partst); //TODO: IMPLEMENT 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); return ndata; } inline void Simulation::orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]) { resblock1[0] = (block1&0x000000FF); resblock1[1] = (block1&0x0000FF00)>>8; resblock1[2] = (block1&0x00FF0000)>>16; resblock1[3] = (block1&0xFF000000)>>24; resblock2[0] = (block2&0x000000FF); resblock2[1] = (block2&0x0000FF00)>>8; resblock2[2] = (block2&0x00FF0000)>>16; resblock2[3] = (block2&0xFF000000)>>24; } inline void Simulation::orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[]) { int block1tmp = 0; int block2tmp = 0; block1tmp = (resblock1[0]&0xFF); block1tmp |= (resblock1[1]&0xFF)<<8; block1tmp |= (resblock1[2]&0xFF)<<16; block1tmp |= (resblock1[3]&0xFF)<<24; block2tmp = (resblock2[0]&0xFF); block2tmp |= (resblock2[1]&0xFF)<<8; block2tmp |= (resblock2[2]&0xFF)<<16; block2tmp |= (resblock2[3]&0xFF)<<24; *block1 = block1tmp; *block2 = block2tmp; } inline int Simulation::is_wire(int x, int y) { return bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE; } inline int Simulation::is_wire_off(int x, int y) { return (bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE) && emap[y][x]<8; } int Simulation::get_wavelength_bin(int *wm) { int i, w0=30, wM=0; if (!*wm) return -1; for (i=0; i<30; i++) if (*wm & (1< 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 (x21 && 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>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 (ndistanceClear(); if(air) air->Clear(); SetEdgeMode(edgeMode); } 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; for (rt=0;rt=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; } 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; } } if (bmap[ny/CELL][nx/CELL]) { if (bmap[ny/CELL][nx/CELL]==WL_ALLOWGAS && !(elements[pt].Properties&TYPE_GAS))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE) return 0; if (bmap[ny/CELL][nx/CELL]==WL_ALLOWENERGY && !(elements[pt].Properties&TYPE_ENERGY))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE) return 0; if (bmap[ny/CELL][nx/CELL]==WL_ALLOWLIQUID && elements[pt].Falldown!=2) return 0; if (bmap[ny/CELL][nx/CELL]==WL_ALLOWSOLID && elements[pt].Falldown!=1) return 0; if (bmap[ny/CELL][nx/CELL]==WL_ALLOWAIR || bmap[ny/CELL][nx/CELL]==WL_WALL || bmap[ny/CELL][nx/CELL]==WL_WALLELEC) return 0; if (bmap[ny/CELL][nx/CELL]==WL_EWALL && !emap[ny/CELL][nx/CELL]) return 0; if (bmap[ny/CELL][nx/CELL]==WL_EHOLE && !emap[ny/CELL][nx/CELL] && !(elements[pt].Properties&TYPE_SOLID) && !(elements[r&0xFF].Properties&TYPE_SOLID)) 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()>8].temp = parts[i].temp; if ((r & 0xFF) < PT_NUM && elements[r&0xFF].HeatConduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10) && (r&0xFF)!=PT_FILT) parts[i].temp = parts[r>>8].temp = restrict_flt((parts[r>>8].temp+parts[i].temp)/2, MIN_TEMP, MAX_TEMP); } if ((parts[i].type==PT_NEUT || parts[i].type==PT_ELEC) && ((r&0xFF)==PT_CLNE || (r&0xFF)==PT_PCLN || (r&0xFF)==PT_BCLN || (r&0xFF)==PT_PBCN)) { if (!parts[r>>8].ctype) parts[r>>8].ctype = parts[i].type; } if ((r&0xFF)==PT_PRTI && (elements[parts[i].type].Properties & TYPE_ENERGY)) { int nnx, count; for (count=0; count<8; count++) { if (isign(x-nx)==isign(portal_rx[count]) && isign(y-ny)==isign(portal_ry[count])) break; } count = count%8; parts[r>>8].tmp = (int)((parts[r>>8].temp-73.15f)/100+1); if (parts[r>>8].tmp>=CHANNELS) parts[r>>8].tmp = CHANNELS-1; else if (parts[r>>8].tmp<0) parts[r>>8].tmp = 0; for ( nnx=0; nnx<80; nnx++) if (!portalp[parts[r>>8].tmp][count][nnx].type) { portalp[parts[r>>8].tmp][count][nnx] = parts[i]; parts[i].type=PT_NONE; break; } } return 0; } if (e == 2) //if occupy same space { if (parts[i].type == PT_PHOT && (r&0xFF)==PT_GLOW && !parts[r>>8].life) if (rand() < RAND_MAX/30) { parts[r>>8].life = 120; create_gain_photon(i); } if (parts[i].type == PT_PHOT && (r&0xFF)==PT_FILT) { int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f); if (temp_bin < 0) temp_bin = 0; if (temp_bin > 25) temp_bin = 25; if(!parts[r>>8].tmp){ parts[i].ctype = 0x1F << temp_bin; //Assign Colour } else if(parts[r>>8].tmp==1){ parts[i].ctype &= 0x1F << temp_bin; //Filter Colour } else if(parts[r>>8].tmp==2){ parts[i].ctype |= 0x1F << temp_bin; //Add Colour } else if(parts[r>>8].tmp==3){ parts[i].ctype &= ~(0x1F << temp_bin); //Subtract Colour } } if (parts[i].type == PT_NEUT && (r&0xFF)==PT_GLAS) { if (rand() < RAND_MAX/10) create_cherenkov_photon(i); } if (parts[i].type == PT_PHOT && (r&0xFF)==PT_INVIS && pv[ny/CELL][nx/CELL]<=4.0f && pv[ny/CELL][nx/CELL]>=-4.0f) { part_change_type(i,x,y,PT_NEUT); parts[i].ctype = 0; } if ((parts[i].type==PT_BIZR||parts[i].type==PT_BIZRG) && (r&0xFF)==PT_FILT) { int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f); if (temp_bin < 0) temp_bin = 0; if (temp_bin > 25) temp_bin = 25; parts[i].ctype = 0x1F << temp_bin; } return 1; } //else e=1 , we are trying to swap the particles, return 0 no swap/move, 1 is still overlap/move, because the swap takes place later if (parts[i].type == PT_NEUT && (elements[r & 0xFF].Properties & PROP_NEUTABSORB)) { kill_part(i); return 0; } 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); 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); 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); return 0; } if (parts[i].type==PT_CNCT && y0) 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)) 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); 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=XRES-CELL || ny=YRES-CELL)//kill_part if particle is out of bounds { kill_part(i); return -1; } if (elements[t].Properties & TYPE_ENERGY) 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= 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>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) 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; if (!elements[t].Enabled) t = PT_NONE; if (parts[i].type == PT_STKM) player.spwn = 0; if (parts[i].type == PT_STKM2) player2.spwn = 0; if (parts[i].type == PT_FIGH) { fighters[(unsigned char)parts[i].tmp].spwn = 0; fighcount--; } parts[i].type = t; if (elements[t].Properties & TYPE_ENERGY) { 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) { int i; int t = tv & 0xFF; int v = (tv >> 8) & 0xFFFFFF; if (x<0 || y<0 || x>=XRES || y>=YRES || ((t<=0 || t>=PT_NUM)&&t!=SPC_HEAT&&t!=SPC_COOL&&t!=SPC_AIR&&t!=SPC_VACUUM&&t!=SPC_PGRV&&t!=SPC_NGRV)) return -1; if (t>=0 && t>8].temp>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>8].ctype=PT_DUST; } if (!((pmap[y][x]&0xFF)==PT_INST||(elements[pmap[y][x]&0xFF].Properties&PROP_CONDUCTS))) 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; } 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)) { if ((pmap[y][x]&0xFF)!=PT_SPAWN&&(pmap[y][x]&0xFF)!=PT_SPAWN2) { if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH) { return -1; } } } if (pfree == -1) return -1; i = pfree; pfree = parts[i].life; } else if (p==-2)//creating from brush { if (pmap[y][x]) { if (( ((pmap[y][x]&0xFF)==PT_STOR&&!(elements[t].Properties&TYPE_SOLID))|| (pmap[y][x]&0xFF)==PT_CLNE|| (pmap[y][x]&0xFF)==PT_BCLN|| (pmap[y][x]&0xFF)==PT_CONV|| ((pmap[y][x]&0xFF)==PT_PCLN&&t!=PT_PSCN&&t!=PT_NSCN)|| ((pmap[y][x]&0xFF)==PT_PBCN&&t!=PT_PSCN&&t!=PT_NSCN) )&&( t!=PT_CLNE&&t!=PT_PCLN&& t!=PT_BCLN&&t!=PT_STKM&& t!=PT_STKM2&&t!=PT_PBCN&& t!=PT_STOR&&t!=PT_FIGH) ) { parts[pmap[y][x]>>8].ctype = t; if (t==PT_LIFE && v>8].tmp = v; } return -1; } if (photons[y][x] && (elements[t].Properties & TYPE_ENERGY)) 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; if (t==PT_GLAS) { parts[i].pavg[1] = pv[y/CELL][x/CELL]; } else if (t==PT_QRTZ) { parts[i].pavg[1] = pv[y/CELL][x/CELL]; } else { parts[i].pavg[0] = 0.0f; parts[i].pavg[1] = 0.0f; } if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH)//set everything to default values first, except for stickman. { parts[i].x = (float)x; parts[i].y = (float)y; parts[i].type = t; parts[i].vx = 0; parts[i].vy = 0; parts[i].life = 0; parts[i].ctype = 0; parts[i].temp = elements[t].Temperature; parts[i].tmp = 0; parts[i].tmp2 = 0; } switch (t) { 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 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; 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; } //and finally set the pmap/photon maps to the newly created particle if (elements[t].Properties & TYPE_ENERGY) photons[y][x] = t|(i<<8); else if (t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH) pmap[y][x] = t|(i<<8); //Fancy dust effects for powder types if((elements[t].Properties & TYPE_PART) && pretty_powder) { int colr, colg, colb, randa; randa = (rand()%30)-15; 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); 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); } } } 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; float pGravX, pGravY, pGravD; int excessive_stacking_found = 0; currentTick++; 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; } } } if (lighting_ok) lighting_recreate--; if (lighting_recreate<0) lighting_recreate=1; if (lighting_recreate>21) lighting_recreate=21; //if (sys_pause&&!framerender)//do nothing if paused // return; if (force_stacking_check || (rand()%10)==0) { force_stacking_check = 0; excessive_stacking_found = 0; for (y=0; y 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=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); } } } } } } } //wire! if(elementCount[PT_WIRE] > 0) { for (nx=0; nx>8].type==PT_WIRE) parts[r>>8].tmp=parts[r>>8].ctype; } } } //game of life! if (elementCount[PT_LIFE]>0&&++CGOL>=GSPEED)//GSPEED is frames per generation { int createdsomething = 0; CGOL=0; ISGOL=0; for (ny=CELL; ny>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++) { for ( nny=-1; nny<2; nny++)//it will count itself as its own neighbor, which is needed, but will have 1 extra for delete check { rt = pmap[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL]; if (!rt || (rt&0xFF)==PT_LIFE) { gol2[((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] ++; } } } } else { parts[r>>8].tmp --; if (parts[r>>8].tmp<=0) parts[r>>8].type = PT_NONE;//using kill_part makes it not work } } //} } } } for (ny=CELL; ny=2&&gol2[ny][nx][golnum]>=(goldelete%2)+goldelete/2) { 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 { 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. } } //memset(gol2, 0, sizeof(gol2)); } if (ISWIRE>0)//wifi channel reseting { for ( q = 0; q<(int)(MAX_TEMP-73.15f)/100+2; q++) { wireless[q][0] = wireless[q][1]; wireless[q][1] = 0; } ISWIRE--; } bool elementRecount = !(currentTick%180); if(elementRecount) { std::fill(elementCount, elementCount+PT_NUM, 0); } for (i=0; i<=parts_lastActiveIndex; i++) if (parts[i].type) { t = parts[i].type; if (t<0 || t>=PT_NUM || !elements[t].Enabled) { kill_part(i); continue; } elementCount[t]++; elem_properties = elements[t].Properties; if (parts[i].life>0 && (elem_properties&PROP_LIFE_DEC)) { // automatically decrease life parts[i].life--; if (parts[i].life<=0 && (elem_properties&(PROP_LIFE_KILL_DEC|PROP_LIFE_KILL))) { // kill on change to no life kill_part(i); continue; } } else if (parts[i].life<=0 && (elem_properties&PROP_LIFE_KILL)) { // 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; 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=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; } if (bmap[y/CELL][x/CELL]==WL_DETECT && emap[y/CELL][x/CELL]<8) set_emap(x/CELL, y/CELL); //adding to velocity from the particle's velocity vx[y/CELL][x/CELL] = vx[y/CELL][x/CELL]*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; if (t==PT_GAS||t==PT_NBLE) { if (pv[y/CELL][x/CELL]<3.5f) pv[y/CELL][x/CELL] += elements[t].HotAir*(3.5f-pv[y/CELL][x/CELL]); if (y+CELL= 0 && y-2 < YRES && (elements[t].Properties&TYPE_LIQUID) && (t!=PT_GEL || gel_scale>(1+rand()%255))) {//some heat convection for liquids r = pmap[y-2][x]; if (!(!r || parts[i].type != (r&0xFF))) { if (parts[i].temp>parts[r>>8].temp) { swappage = parts[i].temp; parts[i].temp = parts[r>>8].temp; parts[r>>8].temp = swappage; } } } //heat transfer code h_count = 0; if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale)) { float c_Cm = 0.0f; if (aheat_enable && !(elements[t].Properties&PROP_NOAMBHEAT)) { 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; } c_heat = 0.0f; c_Cm = 0.0f; for (j=0; j<8; j++) { surround_hconduct[j] = i; r = surround[j]; if (!r) continue; rt = r&0xFF; 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))) { surround_hconduct[j] = r>>8; if (rt==PT_GEL) gel_scale = parts[r>>8].tmp*2.55f; else gel_scale = 1.0f; 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); h_count++; } } if (t==PT_GEL) gel_scale = parts[i].tmp*2.55f; else gel_scale = 1.0f; if (t == PT_PHOT) pt = (c_heat+parts[i].temp*96.645)/(c_Cm+96.645); else pt = (c_heat+parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight))/(c_Cm+96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight)); 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); ctemph = ctempl = pt; // change boiling point with pressure if ((elements[t].State==ST_LIQUID && elements[t].HighTemperatureTransition>-1 && elements[t].HighTemperatureTransition-1 && elements[t].LowTemperatureTransition=PT_NUM || parts[i].ctype==PT_ICEI || parts[i].ctype==PT_SNOW)) parts[i].ctype = PT_WATR; if (ctemph>elements[t].HighTemperature&&elements[t].HighTemperatureTransition>-1) { // particle type change due to high temperature float dbt = ctempl - pt; if (elements[t].HighTemperatureTransition!=PT_NUM) { if (elements[t].Enthalpy <= (c_heat - (elements[t].HighTemperature - dbt)*c_Cm)) { pt = (c_heat - elements[t].Enthalpy)/c_Cm; t = elements[t].HighTemperatureTransition; } else { parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP); s = 0; } } else if (t==PT_ICEI || t==PT_SNOW) { if (parts[i].ctyperand()%6) t = PT_SALT; else t = PT_WTRV; } else { parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP); s = 0; } } else s = 0; } else if (ctempl-1) { // particle type change due to low temperature float dbt = ctempl - pt; if (elements[t].LowTemperatureTransition!=PT_NUM) { if (elements[elements[t].LowTemperatureTransition].Enthalpy >= (c_heat - (elements[t].LowTemperature - dbt)*c_Cm)) { pt = (c_heat + elements[elements[t].LowTemperatureTransition].Enthalpy)/c_Cm; t = elements[t].LowTemperatureTransition; } else { parts[i].temp = restrict_flt(elements[t].LowTemperature - dbt, MIN_TEMP, MAX_TEMP); s = 0; } } 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=elements[PT_BMTL].HighTemperature) s = 0; else if (elements[parts[i].ctype].HighTemperatureTransition==PT_LAVA) { if (pt>=elements[parts[i].ctype].HighTemperature) s = 0; } else if (pt>=973.0f) s = 0; // freezing point for lava with any other (not listed in ptransitions as turning into lava) ctype if (s) { t = parts[i].ctype; parts[i].ctype = PT_NONE; if (t==PT_THRM) { parts[i].tmp = 0; t = PT_BMTL; } if (t==PT_PLUT) { parts[i].tmp = 0; t = PT_LAVA; } } } else if (pt<973.0f) t = PT_STNE; else s = 0; } else s = 0; } else s = 0; pt = restrict_flt(pt, MIN_TEMP, MAX_TEMP); for (j=0; j<8; j++) { parts[surround_hconduct[j]].temp = pt; } if (s) { // particle type change occurred if (t==PT_ICEI||t==PT_LAVA||t==PT_SNOW) parts[i].ctype = parts[i].type; if (!(t==PT_ICEI&&parts[i].ctype==PT_FRZW)) parts[i].life = 0; if (elements[t].State==ST_GAS&&elements[parts[i].type].State!=ST_GAS) pv[y/CELL][x/CELL] += 0.50f; part_change_type(i,x,y,t); if (t==PT_FIRE||t==PT_PLSM||t==PT_HFLM) parts[i].life = rand()%50+120; if (t==PT_LAVA) { if (parts[i].ctype==PT_BRMT) parts[i].ctype = PT_BMTL; else if (parts[i].ctype==PT_SAND) parts[i].ctype = PT_GLAS; else if (parts[i].ctype==PT_BGLA) parts[i].ctype = PT_GLAS; else if (parts[i].ctype==PT_PQRT) parts[i].ctype = PT_QRTZ; parts[i].life = rand()%120+240; } if (t==PT_NONE) { kill_part(i); goto killed; } } pt = parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP); if (t==PT_LAVA) { parts[i].life = restrict_flt((parts[i].temp-700)/7, 0.0f, 400.0f); if (parts[i].ctype==PT_THRM&&parts[i].tmp>0) { parts[i].tmp--; parts[i].temp = 3500; } if (parts[i].ctype==PT_PLUT&&parts[i].tmp>0) { parts[i].tmp--; parts[i].temp = MAX_TEMP; } } } else parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP); } if (t==PT_LIFE) { parts[i].temp = restrict_flt(parts[i].temp-50.0f, MIN_TEMP, MAX_TEMP); //ISGOL=1;//means there is a life particle on screen } if (t==PT_WIRE) { //wire_placed = 1; } //spark updates from walls if ((elements[t].Properties&PROP_CONDUCTS) || t==PT_SPRK) { nx = x % CELL; if (nx == 0) nx = x/CELL - 1; else if (nx == CELL-1) nx = x/CELL + 1; else nx = x/CELL; ny = y % CELL; if (ny == 0) ny = y/CELL - 1; else if (ny == CELL-1) ny = y/CELL + 1; else ny = y/CELL; if (nx>=0 && ny>=0 && nx2.5f) { parts[i].life = rand()%80+180; parts[i].temp = restrict_flt(elements[PT_FIRE].Temperature + (elements[t].Flammable/2), MIN_TEMP, MAX_TEMP); t = PT_FIRE; part_change_type(i,x,y,t); pv[y/CELL][x/CELL] += 0.25f * CFDS; } s = 1; gravtot = fabs(gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]); if (pv[y/CELL][x/CELL]>elements[t].HighPressure&&elements[t].HighPressureTransition>-1) { // particle type change due to high pressure if (elements[t].HighPressureTransition!=PT_NUM) t = elements[t].HighPressureTransition; else if (t==PT_BMTL) { if (pv[y/CELL][x/CELL]>2.5f) t = PT_BRMT; else if (pv[y/CELL][x/CELL]>1.0f && parts[i].tmp==1) t = PT_BRMT; else s = 0; } else s = 0; } else if (pv[y/CELL][x/CELL]-1) { // particle type change due to low pressure if (elements[t].LowPressureTransition!=PT_NUM) t = elements[t].LowPressureTransition; else s = 0; } else if (gravtot>(elements[t].HighPressure/4.0f)&&elements[t].HighPressureTransition>-1) { // particle type change due to high gravity if (elements[t].HighPressureTransition!=PT_NUM) t = elements[t].HighPressureTransition; else if (t==PT_BMTL) { if (gravtot>0.625f) t = PT_BRMT; else if (gravtot>0.25f && parts[i].tmp==1) t = PT_BRMT; else s = 0; } else s = 0; } else s = 0; if (s) { // particle type change occurred parts[i].life = 0; part_change_type(i,x,y,t); if (t==PT_FIRE) parts[i].life = rand()%50+120; if (t==PT_NONE) { kill_part(i); goto killed; } } //call the particle update function, if there is one #ifdef LUACONSOLE if (elements[t].Update && lua_el_mode[t] != 2) #else if (elements[t].Update) #endif { if ((*(elements[t].Update))(this, i,x,y,surround_space,nt, parts, pmap)) 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); } } #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); } #endif if(legacy_enable)//if heat sim is off Element::legacyUpdate(this, i,x,y,surround_space,nt, parts, pmap); killed: if (parts[i].type == PT_NONE)//if its dead, skip to next particle continue; if (!parts[i].vx&&!parts[i].vy)//if its not moving, skip to next particle, movement code it next continue; #if defined(WIN) && !defined(__GNUC__) mv = max(fabsf(parts[i].vx), fabsf(parts[i].vy)); #else mv = fmaxf(fabsf(parts[i].vx), fabsf(parts[i].vy)); #endif if (mv < ISTP) { clear_x = x; clear_y = y; clear_xf = parts[i].x; clear_yf = parts[i].y; fin_xf = clear_xf + parts[i].vx; fin_yf = clear_yf + parts[i].vy; fin_x = (int)(fin_xf+0.5f); fin_y = (int)(fin_yf+0.5f); } else { // interpolate to see if there is anything in the way dx = parts[i].vx*ISTP/mv; dy = parts[i].vy*ISTP/mv; fin_xf = parts[i].x; fin_yf = parts[i].y; while (1) { mv -= ISTP; fin_xf += dx; fin_yf += dy; fin_x = (int)(fin_xf+0.5f); fin_y = (int)(fin_yf+0.5f); if (mv <= 0.0f) { // nothing found fin_xf = parts[i].x + parts[i].vx; fin_yf = parts[i].y + parts[i].vy; fin_x = (int)(fin_xf+0.5f); fin_y = (int)(fin_yf+0.5f); clear_xf = fin_xf-dx; clear_yf = fin_yf-dy; clear_x = (int)(clear_xf+0.5f); clear_y = (int)(clear_yf+0.5f); break; } if (fin_x=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)))) { // 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); } } 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=XRES-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) { if (t == PT_PHOT) { if (parts[i].flags&FLAG_SKIPMOVE) { parts[i].flags &= ~FLAG_SKIPMOVE; continue; } 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) { kill_part(i); continue; } } else if (!do_move(i, x, y, fin_xf, fin_yf)) { if (parts[i].type == PT_NONE) continue; // reflection parts[i].flags |= FLAG_STAGNANT; if (t==PT_NEUT && 100>(rand()%1000)) { kill_part(i); continue; } r = pmap[fin_y][fin_x]; if ((r & 0xFF) == PT_PIPE && !(parts[r>>8].tmp&0xFF)) { parts[r>>8].tmp = (parts[r>>8].tmp&~0xFF) | parts[i].type; parts[r>>8].temp = parts[i].temp; parts[r>>8].flags = parts[i].life; parts[r>>8].pavg[0] = parts[i].tmp; parts[r>>8].pavg[1] = parts[i].ctype; kill_part(i); continue; } // this should be replaced with a particle type attribute ("photwl" or something) if ((r & 0xFF) == PT_PSCN) parts[i].ctype = 0x00000000; if ((r & 0xFF) == PT_NSCN) parts[i].ctype = 0x00000000; if ((r & 0xFF) == PT_SPRK) parts[i].ctype = 0x00000000; if ((r & 0xFF) == PT_COAL) parts[i].ctype = 0x00000000; if ((r & 0xFF) == PT_BCOL) parts[i].ctype = 0x00000000; if ((r & 0xFF) == PT_PLEX) parts[i].ctype &= 0x1F00003E; if ((r & 0xFF) == PT_NITR) parts[i].ctype &= 0x0007C000; if ((r & 0xFF) == PT_NBLE) parts[i].ctype &= 0x3FFF8000; if ((r & 0xFF) == PT_LAVA) parts[i].ctype &= 0x3FF00000; if ((r & 0xFF) == PT_ACID) parts[i].ctype &= 0x1FE001FE; if ((r & 0xFF) == PT_DUST) parts[i].ctype &= 0x3FFFFFC0; if ((r & 0xFF) == PT_SNOW) parts[i].ctype &= 0x03FFFFFF; if ((r & 0xFF) == PT_GOO) parts[i].ctype &= 0x3FFAAA00; if ((r & 0xFF) == PT_PLNT) parts[i].ctype &= 0x0007C000; if ((r & 0xFF) == PT_PLUT) parts[i].ctype &= 0x001FCE00; if ((r & 0xFF) == PT_URAN) parts[i].ctype &= 0x003FC000; if (get_normal_interp(t, parts[i].x, parts[i].y, parts[i].vx, parts[i].vy, &nrx, &nry)) { dp = nrx*parts[i].vx + nry*parts[i].vy; parts[i].vx -= 2.0f*dp*nrx; parts[i].vy -= 2.0f*dp*nry; // leave the actual movement until next frame so that reflection of fast particles and refraction happen correctly } else { if (t!=PT_NEUT) kill_part(i); continue; } if (!(parts[i].ctype&0x3FFFFFFF)&&t!=PT_NEUT&&t!=PT_ELEC) { kill_part(i); continue; } } } else if (elements[t].Falldown==0) { // gasses and solids (but not powders) if (!do_move(i, x, y, fin_xf, fin_yf)) { if (parts[i].type == PT_NONE) continue; // can't move there, so bounce off // TODO // TODO: Work out what previous TODO was for if (fin_x>x+ISTP) fin_x=x+ISTP; if (fin_xy+ISTP) fin_y=y+ISTP; if (fin_y= rand()%400)//checking stagnant is cool, but then it doesn't update when you change it later. { if (!flood_water(x,y,i,y, parts[i].tmp2)) goto movedone; } // liquids and powders if (!do_move(i, x, y, fin_xf, fin_yf)) { if (parts[i].type == PT_NONE) continue; if (fin_x!=x && do_move(i, x, y, fin_xf, clear_yf)) { parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; } else if (fin_y!=y && do_move(i, x, y, clear_xf, fin_yf)) { parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; } else { s = 1; r = (rand()%2)*2-1; if ((clear_x!=x || clear_y!=y || nt || surround_space) && (fabsf(parts[i].vx)>0.01f || fabsf(parts[i].vy)>0.01f)) { // allow diagonal movement if target position is blocked // but no point trying this if particle is stuck in a block of identical particles dx = parts[i].vx - parts[i].vy*r; dy = parts[i].vy + parts[i].vx*r; if (fabsf(dy)>fabsf(dx)) mv = fabsf(dy); else mv = fabsf(dx); dx /= mv; dy /= mv; if (do_move(i, x, y, clear_xf+dx, clear_yf+dy)) { parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; goto movedone; } swappage = dx; dx = dy*r; dy = -swappage*r; if (do_move(i, x, y, clear_xf+dx, clear_yf+dy)) { parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; goto movedone; } } if (elements[t].Falldown>1 && !grav->ngrav_enable && gravityMode==0 && parts[i].vy>fabsf(parts[i].vx)) { s = 0; // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle rt = 30;//slight less water lag, although it changes how it moves a lot else rt = 10; if (t==PT_GEL) rt = parts[i].tmp*0.20f+5.0f; for (j=clear_x+r; j>=0 && j>=clear_x-rt && j0) r = 1; else r = -1; if (s==1) for (j=ny+r; j>=0 && j=ny-rt && j1 && fabsf(pGravX*parts[i].vx+pGravY*parts[i].vy)>fabsf(pGravY*parts[i].vx-pGravX*parts[i].vy)) { float nxf, nyf, prev_pGravX, prev_pGravY, ptGrav = elements[t].Gravity; s = 0; // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle rt = 30;//slight less water lag, although it changes how it moves a lot else rt = 10; nxf = clear_xf; nyf = clear_yf; for (j=0;jfabsf(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;jfabsf(pGravX)) mv = fabsf(pGravY); else mv = fabsf(pGravX); if (mv<0.0001f) break; pGravX /= mv; pGravY /= mv; nxf += pGravX; nyf += pGravY; nx = (int)(nxf+0.5f); ny = (int)(nyf+0.5f); if (nx<0 || ny<0 || nx>=XRES || ny>=YRES) break; if ((pmap[ny][nx]&0xFF)!=t || bmap[ny/CELL][nx/CELL]) { s = do_move(i, clear_x, clear_y, nxf, nyf); if (s || bmap[ny/CELL][nx/CELL]!=WL_STREAM) break; } } } else if (s==-1) {} // particle is out of bounds else if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {} else parts[i].flags |= FLAG_STAGNANT; parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; } else { // if interpolation was done, try moving to last clear position if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {} else parts[i].flags |= FLAG_STAGNANT; parts[i].vx *= elements[t].Collision; parts[i].vy *= elements[t].Collision; } } } } 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; iupdate_air(); 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; if(gravWallChanged) { grav->gravity_mask(); gravWallChanged = false; } } } memset(pmap, 0, sizeof(pmap)); memset(pmap_count, 0, sizeof(pmap_count)); 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=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; ybmap_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])); } } } if(!sys_pause||framerender) update_particles_i(0, 1); if(framerender) framerender--; // this should probably be elsewhere /*for (y=0; y=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 grav; delete air; for(int i = 0; i < tools.size(); i++) delete tools[i]; } Simulation::Simulation(): sys_pause(0) { 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)); currentTick = 0; std::fill(elementCount, elementCount+PT_NUM, 0); //Create and attach gravity simulation grav = new Gravity(); //Give air sim references to our data grav->bmap = bmap; //Gravity sim gives us maps to use gravx = grav->gravx; gravy = grav->gravy; gravp = grav->gravp; gravmap = grav->gravmap; //Create and attach air simulation air = new Air(); //Give air sim references to our data air->bmap = bmap; air->emap = emap; air->fvx = fvx; air->fvy = fvy; //Air sim gives us maps to use vx = air->vx; vy = air->vy; pv = air->pv; hv = air->hv; int menuCount; menu_section * msectionsT = LoadMenus(menuCount); memcpy(msections, msectionsT, menuCount * sizeof(menu_section)); free(msectionsT); int wallCount; wall_type * wtypesT = LoadWalls(wallCount); memcpy(wtypes, wtypesT, wallCount * sizeof(wall_type)); free(wtypesT); //elements = new Element[PT_NUM]; std::vector elementList = GetElements(); for(int i = 0; i < PT_NUM; i++) { if(i < elementList.size()) elements[i] = elementList[i]; else elements[i] = Element(); } tools = GetTools(); int golRulesCount; int * golRulesT = LoadGOLRules(golRulesCount); memcpy(grule, golRulesT, sizeof(int) * (golRulesCount*10)); free(golRulesT); int golTypesCount; int * golTypesT = LoadGOLTypes(golTypesCount); memcpy(goltype, golTypesT, sizeof(int) * (golTypesCount)); free(golTypesT); int golMenuCount; gol_menu * golMenuT = LoadGOLMenu(golMenuCount); memcpy(gmenu, golMenuT, sizeof(gol_menu) * golMenuCount); free(golMenuT); init_can_move(); clear_sim(); grav->gravity_mask(); }