protected override void Initialize() { // graphics settings _graphics.PreferredBackBufferWidth = graphicState.windowWidth; _graphics.PreferredBackBufferHeight = graphicState.windowHeight; _graphics.ApplyChanges(); base.Initialize(); this.Window.AllowUserResizing = false; // elements setup Element.SetupElements(); // gamemaps setting up gameMap = new GameMap(graphicState.windowWidth / graphicState.particleSize, graphicState.windowHeight / graphicState.particleSize - 20); partMap = gameMap.GetParticleMap(); tempMap = gameMap.GetTemperatureMap(); /* fluidMap = gameMap.GetFluidMap(); */ // custom graphics class setup graphics = new Graphics(this, gameMap, spriteBatch); uiManager.Setup(this, partMap, tempMap); }
private void Diffuse() { ParticleMap partMap = gameMap.GetParticleMap(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { map[x, y] -= (map[x, y] - Element.elements[ElementID.AIR].STemp) * diffuseRate; if (map[x, y] <= -273.15f) { map[x, y] = -273.15f; } } } }
public void Update(ParticleMap partMap, TemperatureMap tempMap) { UpdateReaction(partMap, tempMap); if (!stable) { UpdatePosition(partMap); } else { stableTime++; } if (Element.elements[ID].MaxLifeTime > 0) { lifeTime++; } }
void UpdateReaction(ParticleMap partMap, TemperatureMap tempMap) { bool pass; if (!stable) { pass = stable; } else { // even if stable check for reactions regularly (tanks performance) pass = !(stableTime % 10 == 0); } ElementID result = Element.elements[ID].UpdateReaction(pos, lifeTime, pass, partMap, tempMap); if (result != this.ID) { SetStable(false); partMap.UnstableSurroundingParticles(this.pos); unstableTimeout = 0; if (result == ElementID.VOID) // void == deleted { partMap.DeleteLater(this.pos, 0); SetStable(true); //dont update pos after deleting } else if (result == ElementID.EXPLOSION) { partMap.DeleteLater(this.pos, 0); partMap.SpawnLater(ElementID.FIRE, this.pos, Element.elements[ID].ExplosivePwr); SetStable(true); //dont update pos after deleting } else { this.ID = result; this.lifeTime = 0; } } }
void UpdatePosition(ParticleMap partMap) { Point result = Element.elements[ID].UpdatePosition(pos, partMap); if (result != this.pos) { partMap.UnstableSurroundingParticles(this.pos); partMap.Swap(this.pos, result); unstableTimeout = 0; } else { if (unstableTimeout < 50) // wait for a while before making it stable { unstableTimeout++; } else { SetStable(true); this.stableTime = 0; } } }
void Propagate() { // propagate heat to neighbor squares simDir = (simDir + 1) % 2; ParticleMap partMap = gameMap.GetParticleMap(); int _y; int _x; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (simDir == 0) // dir switching { _x = x; _y = y; } else { _x = (width - 1) - x; _y = y; } // Get positions of current cell Point cellPos = new Point(_x, _y); if (partMap.Type(cellPos) == ElementID.WALL) { continue; // skip WALLS } // Get positions of neigbor cells Point neighPos; Point[] neighborPoints = FindNeighbor(cellPos); // cell/neighbor HT = heatTransfer amount float cellHT = Element.elements[partMap.Type(cellPos)].HeatTrans; float neighHT; for (int i = 0; i < 4; i++) { neighPos = neighborPoints[i]; if (neighPos.X == -1 || partMap.Type(neighPos) == ElementID.WALL) { continue; // Out of bounds / WALL } neighHT = Element.elements[partMap.Type(neighPos)].HeatTrans; float flow = map[neighPos.X, neighPos.Y] - map[cellPos.X, cellPos.Y]; if (flow > 0.0f) { flow *= neighHT; } else { flow *= cellHT; } if (i == 0 && flow < 0) // favors upward hot air motion { flow *= flowConstant * 50; } else { flow *= flowConstant; } map[neighPos.X, neighPos.Y] -= flow / neighHT; map[cellPos.X, cellPos.Y] += flow / cellHT; // kill temperature oscilations (can be really confusing) if ((flow > 0.0f && map[neighPos.X, neighPos.Y] < map[cellPos.X, cellPos.Y]) || ( flow <= 0.0f && map[neighPos.X, neighPos.Y] > map[cellPos.X, cellPos.Y])) { float total = (cellHT * map[cellPos.X, cellPos.Y]) + (neighHT * map[neighPos.X, neighPos.Y]); float avg = total / (cellHT + neighHT); map[neighPos.X, neighPos.Y] = avg; map[cellPos.X, cellPos.Y] = avg; } } } } }
public void DrawParticles(ParticleMap partMap) { shapes.Begin(); partMap.Render(shapes, particleSize); shapes.End(); }
public Point UpdatePosition(Point pos, ParticleMap partMap) { if (!this.move) { return(pos); } Point INVALID = new Point(-1, -1); List <Point> possiblePos = new List <Point>(); if (state == 0) // solids { Point DOWN = new Point(pos.X, pos.Y + 1); ElementID typeDOWN = partMap.Type(DOWN); if ((typeDOWN == ElementID.AIR) || (Element.elements[typeDOWN].weight < this.weight)) // heavier sinks { if (MainGame.random.NextDouble() <= 0.9f) // random sideways movement { return(DOWN); } else { Point DOWNLEFT = new Point(pos.X - 1, pos.Y + 1); DOWNLEFT = (partMap.Type(DOWNLEFT) == ElementID.AIR) ? DOWNLEFT : INVALID; Point DOWNRIGHT = new Point(pos.X + 1, pos.Y + 1); DOWNRIGHT = (partMap.Type(DOWNRIGHT) == ElementID.AIR) ? DOWNRIGHT : INVALID; if (DOWNLEFT == INVALID && DOWNRIGHT == INVALID) { return(DOWN); } else if (DOWNLEFT != INVALID && DOWNRIGHT != INVALID) { if (MainGame.random.NextDouble() <= 0.5f) { return(DOWNLEFT); } else { return(DOWNRIGHT); } } else if (DOWNLEFT != INVALID) { return(DOWNLEFT); } else { return(DOWNRIGHT); } } } Point testPos = new Point(); ElementID typeTestPos; for (int _y = 1; _y < 2; _y++) { for (int _x = -1; _x < 2; _x++) { testPos.X = pos.X + _x; testPos.Y = pos.Y + _y; typeTestPos = partMap.Type(testPos); if ((typeTestPos == ElementID.AIR) || (Element.elements[typeTestPos].weight < this.weight)) { possiblePos.Add(testPos); } } } } else if (state == 1) // LIQUID { Point DOWN = new Point(pos.X, pos.Y + 1); ElementID typeDOWN = partMap.Type(DOWN); if ((typeDOWN == ElementID.AIR) || (Element.elements[typeDOWN].weight < this.weight)) // heavier sinks { if (MainGame.random.NextDouble() <= 0.9f) // random sideways movement { return(DOWN); } else { Point DOWNLEFT = new Point(pos.X - 1, pos.Y + 1); DOWNLEFT = (partMap.Type(DOWNLEFT) == ElementID.AIR) ? DOWNLEFT : INVALID; Point DOWNRIGHT = new Point(pos.X + 1, pos.Y + 1); DOWNRIGHT = (partMap.Type(DOWNRIGHT) == ElementID.AIR) ? DOWNRIGHT : INVALID; if (DOWNLEFT == INVALID && DOWNRIGHT == INVALID) { return(DOWN); } else if (DOWNLEFT != INVALID && DOWNRIGHT != INVALID) { if (MainGame.random.NextDouble() <= 0.5f) { return(DOWNLEFT); } else { return(DOWNRIGHT); } } else if (DOWNLEFT != INVALID) { return(DOWNLEFT); } else { return(DOWNRIGHT); } } } Point testPos = new Point(); ElementID typeTestPos; for (int _y = 0; _y < 2; _y++) { for (int _x = -1; _x < 2; _x++) { testPos.X = pos.X + _x; testPos.Y = pos.Y + _y; typeTestPos = partMap.Type(testPos); if ((typeTestPos == ElementID.AIR) || (Element.elements[typeTestPos].weight < this.weight)) { possiblePos.Add(testPos); } } } } else if (state == 2) // GAS { Point UP = new Point(pos.X, pos.Y - 1); ElementID typeUP = partMap.Type(UP); if ((typeUP == ElementID.AIR) || (Element.elements[typeUP].weight < this.weight)) // lighter sinks { if (MainGame.random.NextDouble() <= 0.8f) // random sideways movement { return(UP); } else { Point UPLEFT = new Point(pos.X - 1, pos.Y - 1); UPLEFT = (partMap.Type(UPLEFT) == ElementID.AIR) ? UPLEFT : INVALID; Point UPRIGHT = new Point(pos.X + 1, pos.Y - 1); UPRIGHT = (partMap.Type(UPRIGHT) == ElementID.AIR) ? UPRIGHT : INVALID; if (UPLEFT == INVALID && UPRIGHT == INVALID) { return(UP); } else if (UPLEFT != INVALID && UPRIGHT != INVALID) { if (MainGame.random.NextDouble() <= 0.5f) { return(UPLEFT); } else { return(UPRIGHT); } } else if (UPLEFT != INVALID) { return(UPLEFT); } else { return(UPRIGHT); } } } Point testPos = new Point(); ElementID typeTestPos; for (int _y = 0; _y > -2; _y--) { for (int _x = -1; _x < 2; _x++) { testPos.X = pos.X + _x; testPos.Y = pos.Y + _y; typeTestPos = partMap.Type(testPos); if ((typeTestPos == ElementID.AIR) || (Element.elements[typeTestPos].weight < this.weight)) { possiblePos.Add(testPos); } } } } if (possiblePos.Count != 0) // choose random from possible positions { return(possiblePos[MainGame.random.Next(0, possiblePos.Count)]); } else { return(pos); } }
public ElementID UpdateReaction(Point pos, int lifeTime, bool stable, ParticleMap partMap, TemperatureMap tempMap) { if (!stable) { foreach (Reaction r in this.reactions) // evaluate all possible reactions { if (r.Eval(pos, partMap, out List <ElementID> result, out Point destroy)) { //spawn other results around this particle for (int i = 1; i < result.Count; i++) { if (partMap.Type(new Point(pos.X, pos.Y - 1)) == ElementID.AIR) { partMap.SpawnLater(result[i], new Point(pos.X, pos.Y - 1), 1); } else if (partMap.Type(new Point(pos.X, pos.Y + 1)) == ElementID.AIR) { partMap.SpawnLater(result[i], new Point(pos.X, pos.Y + 1), 1); } else if (partMap.Type(new Point(pos.X - 1, pos.Y)) == ElementID.AIR) { partMap.SpawnLater(result[i], new Point(pos.X - 1, pos.Y), 1); } else if (partMap.Type(new Point(pos.X + 1, pos.Y)) == ElementID.AIR) { partMap.SpawnLater(result[i], new Point(pos.X + 1, pos.Y), 1); } } if (partMap.InBounds(destroy)) { partMap.DeleteLater(destroy, 0); } return(result[0]); } } } // evaluate preset transitional reactons if (lowLevelTempTransition != null && tempMap.Get(pos) <= lowLevelTemp) { if (lowLevelTempTransition.Eval(pos, partMap, out List <ElementID> result, out Point destroy)) { tempMap.Set(pos, 0, tempMap.Get(pos) * 1.05f); return(result[0]); } } if (highLevelTempTransition != null && tempMap.Get(pos) >= highLevelTemp) { if (highLevelTempTransition.Eval(pos, partMap, out List <ElementID> result, out Point destroy)) { tempMap.Set(pos, 0, tempMap.Get(pos) * 0.95f); return(result[0]); } } if (endOfLifeTransition != null && lifeTime > this.maxLifeTime) { if (endOfLifeTransition.Eval(pos, partMap, out List <ElementID> result, out Point destroy)) { return(result[0]); } } return(this.ID); }
public void Setup(MainGame game, ParticleMap partMap, TemperatureMap tempMap) { this.partMap = partMap; this.tempMap = tempMap; ElementID[] specialCategory = new ElementID[] { ElementID.WALL, ElementID.FIRE, ElementID.HOT, ElementID.COLD, ElementID.ERASE, ElementID.ERASEP }; List <UIItem> solids = new List <UIItem>(); List <UIItem> liquids = new List <UIItem>(); List <UIItem> gasses = new List <UIItem>(); List <UIItem> specials = new List <UIItem>(); List <UIItem> elementSelectMenu = new List <UIItem>(); List <UIItem> drawStylesMenu = new List <UIItem>(); List <UIItem> optionsMenu = new List <UIItem>(); List <UIItem> fileFunctionsMenu = new List <UIItem>(); List <UIItem> defaultMenu = new List <UIItem>(); // X buttons Vector2 backButtonPos = new Vector2(graphicState.windowWidth - 30, graphicState.windowHeight - 95); int backButtonWidth = 25; elementSelectMenu.Add(new MenuButton(defaultMenu, "menu", "X", "smallButtonFont", backButtonPos, backButtonWidth, backButtonWidth, 2, Color.White, Color.DimGray, "default")); optionsMenu.Add(new MenuButton(defaultMenu, "menu", "X", "smallButtonFont", backButtonPos, backButtonWidth, backButtonWidth, 2, Color.White, Color.DimGray)); // elementButtons Vector2 startPosition = new Vector2(230, graphicState.windowHeight - 82); int butWidth = 70; int butHeight = 25; int borderWidth = 2; Vector2 butXOffset = new Vector2(butWidth + 6, 0); Vector2 butYOffset = new Vector2(0, butHeight + 6); foreach (ElementID id in specialCategory) { if (Element.elements.ContainsKey(id)) { Element elem = Element.elements[id]; specials.Add(new ElementButton(id, elem.Short, "smallButtonFont", startPosition + specials.Count * butXOffset, butWidth, butHeight, borderWidth, elem.Color, elem.Color)); } } foreach (ElementID id in Enum.GetValues(typeof(ElementID))) { if (Element.elements.ContainsKey(id) && id != ElementID.AIR && id != ElementID.VOID) { Element elem = Element.elements[id]; if (elem.UIExclude) { continue; } if (!specialCategory.Contains(id)) { switch (elem.State) { case 0: solids.Add(new ElementButton(id, elem.Short, "smallButtonFont", startPosition + (solids.Count % 7) * butXOffset + (solids.Count / 7) * butYOffset, butWidth, butHeight, borderWidth, elem.Color, elem.Color)); break; case 1: liquids.Add(new ElementButton(id, elem.Short, "smallButtonFont", startPosition + (liquids.Count % 7) * butXOffset + (liquids.Count / 7) * butYOffset, butWidth, butHeight, borderWidth, elem.Color, elem.Color)); break; case 2: gasses.Add(new ElementButton(id, elem.Short, "smallButtonFont", startPosition + (gasses.Count % 7) * butXOffset + (gasses.Count / 7) * butYOffset, butWidth, butHeight, borderWidth, elem.Color, elem.Color)); break; } } } } // ELEMENT SELECT MENU elementSelectMenu.Add(new MenuButton(solids, "active", "Solids", "buttonFont", new Vector2(7, graphicState.windowHeight - 85), 92, 30, 2, Color.White, Color.DimGray)); elementSelectMenu.Add(new MenuButton(liquids, "active", "Liquids", "buttonFont", new Vector2(109, graphicState.windowHeight - 85), 92, 30, 2, Color.White, Color.DimGray)); elementSelectMenu.Add(new MenuButton(gasses, "active", "Gasses", "buttonFont", new Vector2(7, graphicState.windowHeight - 45), 92, 30, 2, Color.White, Color.DimGray)); elementSelectMenu.Add(new MenuButton(specials, "active", "Specials", "buttonFont", new Vector2(109, graphicState.windowHeight - 45), 92, 30, 2, Color.White, Color.DimGray)); // DRAWSTYLES MENU startPosition = new Vector2(graphicState.windowWidth - 220, graphicState.windowHeight - 87); drawStylesMenu.Add(new DrawStyleButton(GraphicState.DRAWSTYLES.PARTICLE, "Particles - (F1)", "smallButtonFont", startPosition, 200, 35, borderWidth, Color.White, Color.Green)); drawStylesMenu.Add(new DrawStyleButton(GraphicState.DRAWSTYLES.TEMPERATURE, "Temperatures - (F2)", "smallButtonFont", startPosition + new Vector2(-10, 41), 220, 35, borderWidth, Color.White, Color.DarkRed)); // OPTIONS MENU startPosition = new Vector2(45, graphicState.windowHeight - 67); optionsMenu.Add(new RadioButton(gameState.SetDescriptor, "drawBoard", false, "Show map board", "smallButtonFont", startPosition, 185, 34, borderWidth, Color.White, Color.DimGray)); optionsMenu.Add(new RadioButton(gameState.SetDescriptor, "selectedId", false, "Show selected", "smallButtonFont", startPosition + new Vector2(200, 0), 160, 34, borderWidth, Color.White, Color.DimGray)); optionsMenu.Add(new RadioButton(gameState.SetDescriptor, "cellPos", false, "Cell pos", "smallButtonFont", startPosition + new Vector2(375, 0), 100, 34, borderWidth, Color.White, Color.DimGray)); optionsMenu.Add(new RadioButton(gameState.SetDescriptor, "cellId", false, "Cell ID", "smallButtonFont", startPosition + new Vector2(490, 0), 90, 34, borderWidth, Color.White, Color.DimGray)); optionsMenu.Add(new RadioButton(gameState.SetDescriptor, "cellTemp", false, "Cell temp", "smallButtonFont", startPosition + new Vector2(595, 0), 110, 34, borderWidth, Color.White, Color.DimGray)); // FILE FUNCTIONALITIES MENU startPosition = new Vector2(graphicState.windowWidth - 180, graphicState.windowHeight - 87); fileFunctionsMenu.Add(new LoadSaveButton(game.SaveGame, "Save project", "smallButtonFont", startPosition, 150, 35, borderWidth, Color.White, Color.DimGray)); fileFunctionsMenu.Add(new LoadSaveButton(game.LoadGame, "Load project", "smallButtonFont", startPosition + new Vector2(0, 41), 150, 35, borderWidth, Color.White, Color.DimGray)); // DEFALUT MENU WITH ALL OPTIONS startPosition = new Vector2(15, graphicState.windowHeight - 70); defaultMenu.Add(new MenuButton(elementSelectMenu, "menu", "Elements", "buttonFont", startPosition, 110, 40, borderWidth, Color.White, Color.DimGray, "elements")); defaultMenu.Add(new MenuButton(drawStylesMenu, "active", "Draw options", "buttonFont", startPosition + new Vector2(120, 0), 150, 40, borderWidth, Color.White, Color.DimGray)); defaultMenu.Add(new MenuButton(optionsMenu, "menu", "Options", "buttonFont", startPosition + new Vector2(280, 0), 100, 40, borderWidth, Color.White, Color.DimGray)); defaultMenu.Add(new MenuButton(fileFunctionsMenu, "active", "Files", "buttonFont", startPosition + new Vector2(390, 0), 100, 40, borderWidth, Color.White, Color.DimGray)); SetMenuElements(defaultMenu); }
public bool Eval(Point pos, ParticleMap partMap, out List <ElementID> result, out Point destroy) // true if reaction occured and out is the result element { result = new List <ElementID>() { FROM }; destroy = new Point(-1, -1); if (NEED == ElementID.VOID) // time/prob based reactions { if (random.NextDouble() <= probability) { result = TO; return(true); } } else if (NEED != ElementID.MOLTEN) // element based reactions { int occurence = 0; Point testPos = new Point(); ElementID type; for (int y = -1; y < 2; y++) { for (int x = -1; x < 2; x++) { testPos.X = pos.X + x; testPos.Y = pos.Y + y; type = partMap.Type(testPos); if (type == NEED) { occurence++; if (destroyOther) { destroy = testPos; } } } } if (occurence >= minNEEDAmount) { if (random.NextDouble() <= probability) { result = TO; return(true); } } } else // special reaction with molten elements { ElementID moltenElement = ElementID.VOID; int occurence = 0; Point testPos = new Point(); ElementID type; for (int y = -1; y < 2; y++) { for (int x = -1; x < 2; x++) { testPos.X = pos.X + x; testPos.Y = pos.Y + y; type = partMap.Type(testPos); if (type == ElementID.COPPERMELT || type == ElementID.TINMELT || type == ElementID.BRONZEMELT) { occurence++; moltenElement = type; if (destroyOther) { destroy = testPos; } } } } if (occurence >= minNEEDAmount) { if (random.NextDouble() <= probability) { if (TO[0] == ElementID.MOLTEN) { result = new List <ElementID>() { moltenElement } } ; else { result = TO; } return(true); } } } return(false); }
void Propagate() { simDir = (simDir + 1) % 2; ParticleMap partMap = gameMap.GetParticleMap(); int _y; int _x; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (simDir == 0) // dir switching { _x = x; _y = y; } else { _x = (width - 1) - x; _y = y; } // Get positions of current cell Point cellPos = new Point(_x, _y); if (partMap.Type(cellPos) == ElementID.WALL) { continue; // skip WALLS } float remainingMass = map[cellPos.X, cellPos.Y]; // Get positions of neigbor cells Point neighPos; Point[] neighborPoints = FindNeighbor(cellPos); for (int i = 0; i < 4; i++) { float flow = 0; neighPos = neighborPoints[i]; if (neighPos.X == -1) { continue; // out of bounds } if (partMap.Type(neighPos) == ElementID.WALL) { continue; // skip WALLS } if (remainingMass <= 0) { break; } if (i == 0) // below { flow = GetStable(remainingMass + map[neighPos.X, neighPos.Y]) - map[neighPos.X, neighPos.Y]; } else if (i == 1 || i == 2) // left/right { flow = (map[cellPos.X, cellPos.Y] - map[neighPos.X, neighPos.Y]) / 2; } else if (i == 3) // upwards { flow = remainingMass - GetStable(remainingMass + map[neighPos.X, neighPos.Y]); } /* if (flow > MinFlow) */ /* flow *= 0.5f; */ // constrain flow if (flow < 0) { flow = 0; } if (flow > remainingMass) { flow = remainingMass; } remainingMass -= flow; map[cellPos.X, cellPos.Y] -= flow; map[neighPos.X, neighPos.Y] += flow; } } } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { /* map[x,y] += newMap[x,y]; */ newMap[x, y] = 0; if (map[x, y] > MinMass) { elementMap[x, y] = ElementID.WATER; } else { elementMap[x, y] = ElementID.AIR; map[x, y] = 0; } } } }