/// <summary> /// This function iterates through the content of a cell and unloads every object into DB. /// </summary> /// <param name="c">The cell to be unloaded.</param> private void unloadCellContent(Cell c) { SQLiteCommand command = new SQLiteCommand(dbconn); //Clear all exisiting cell content from DB command.CommandText = "DELETE FROM cell_contents WHERE cell_id='" + c.CellID + "'"; command.ExecuteNonQuery(); //Setup Transaction SQLiteTransaction tr = dbconn.BeginTransaction(); command.Transaction = tr; //Save all items by retrieving all guids and iterating through //them. (Can't iterate through an IEnumerable and change it within the iteration!) string[] keys = ItemList.GetKeys().ToArray<string>(); for (int i = 0; i < keys.Length; i++) { Item kv = ItemList[keys[i]]; if (kv.X <= (c.X + wm.CELL_WIDTH) && kv.X >= c.X) { if (kv.Y <= (c.Y + wm.CELL_HEIGHT) && kv.Y >= c.Y) { if (kv.Z <= (c.Z + wm.CELL_DEPTH) && kv.Z >= c.Z) { command.CommandText = "INSERT INTO cell_contents (cell_id, content_type, content_guid) VALUES ('" + c.CellID + "', '" + (int)Util.DBLookupType.Item + "', '" + kv.GUID + "')"; command.ExecuteNonQuery(); UnloadItem(kv.GUID); } } } } //Save all creatures (refer item saving above) keys = new string[CreatureList.Count]; keys = CreatureList.GetKeys().ToArray<string>(); for (int i = 0; i < keys.Length; i++) { Creature cr = CreatureList[keys[i]]; //Only save AICreatures though (only "Creature" should be the player itself) if (cr.GetType() == typeof(AICreature)) { if (cr.X <= (c.X + wm.CELL_WIDTH) && cr.X >= c.X) { if (cr.Y <= (c.Y + wm.CELL_HEIGHT) && cr.Y >= c.Y) { if (cr.Z <= (c.Z + wm.CELL_DEPTH) && cr.Z >= c.Z) { command.CommandText = "INSERT INTO cell_contents (cell_id, content_type, content_guid) VALUES ('" + c.CellID + "', '" + (int)Util.DBLookupType.AICreature + "', '" + cr.GUID + "')"; command.ExecuteNonQuery(); UnloadAICreature(cr.GUID); } } } } } //Commit, cleanup tr.Commit(); command.Dispose(); }
/// <summary> /// Initializes a new WorldMap object, which provides cell retrieval and terrain generation functions. /// </summary> /// <param name="seed">The map seed.</param> /// <param name="dbconn">The connection to the object database.</param> /// <param name="parameters">The parameters for terrain generation.</param> public WorldMap(uint seed, SQLiteConnection dbconn, int[] parameters) { //Initialization this.dbconn = dbconn; this.seed = seed; rand = new TCODRandom(seed, TCODRandomType.ComplementaryMultiplyWithCarry); noise = new TCODNoise(2, rand); CELL_WIDTH = parameters[0]; CELL_HEIGHT = parameters[1]; CELL_DEPTH = parameters[2]; CELLS_X = parameters[3]; CELLS_Y = parameters[4]; CELLS_Z = parameters[5]; GLOBAL_WIDTH = CELLS_X * CELL_WIDTH; GLOBAL_HEIGHT = CELLS_Y * CELL_HEIGHT; GLOBAL_DEPTH = CELLS_Z * CELL_DEPTH; GROUND_LEVEL = parameters[6]; //Create Cells cells = new Cell[CELLS_X, CELLS_Y, CELLS_Z]; int id = 0; for (int x = 0; x < CELLS_X; x++) { for (int y = 0; y < CELLS_Y; y++) { for (int z = 0; z < CELLS_Z; z++) { cells[x, y, z] = new Cell(x * CELL_WIDTH, y * CELL_HEIGHT, z * CELL_DEPTH, id, this); id++; } } } //Create Tiles makeDefaultTileSetup(); loadTileDict(); makeTestDiffs(); }
/// <summary> /// This function handles the shifting, loading and unloading of cells. /// </summary> /// <param name="rel_x">The relative position of the new center cell (X).</param> /// <param name="rel_y">The relative position of the new center cell (Y).</param> /// <param name="rel_z">The relative position of the new center cell (Z).</param> /// <returns>true if successful, false if not</returns> private bool loadNewCells(int rel_x, int rel_y, int rel_z) { //NOTE: The following code seems really complicated when you look at it (it's very "clean" and "efficient", ergo totally not human readable!) //I will try to explain the meaning of each loop/vector by using the example "MOVE INTO CELL TO THE TOP (x-1) LEFT (y-1) OF THE CURRENT CENTER". //This would correspond to the parameters: rel_x = 0, rel_y = 0, rel_z = 1. //When moving into the "top left", the program must do the following operations: // 0) CHECK if the move is legal. // The program checks if all the cells that would have to be loaded (see III) ) actually exist, if not, it denies the move by // returning false. // I) UNLOAD the "old" cells, i.e. the cells that now fall out of the 3x3 "loaded cells" grid, to free up the memory they're holding. // For our example, that means all the "lowermost" and the "rightmost" cells must be unloaded. // II) SHIFT the remaining original cells. // Since we moved to the top left, the former top left (TL) cell is now the central cell. The top top (TT), the left left (LL) // and the former center (C) can also be preserved, but must be shifted too. // In the example the "shifting vector" (in the code: shift_vect) is {-1,-1,0} and the // "limiting vector" (in the code: limit_vect) is {0,0,-1}, meaning that all cells where X != 0 AND Y != 0 // (Z doesn't matter, it's "taken along", hence the -1 in the limit_vect) are overwritten with the cell at {X+(-1), Y+(-1), Z+0} // This shifts the TL, the TT, the LL and the C to the C, the RR, the BB and the BR, respectively. //III) LOAD the new cells. // All that remains is that we load the new cells, in our example case these would be the new TL, TT, TR, LL and BL, because we // moved both on the x- and on the y-axis. Again, this is handled by the shifting and the limiting vector. The limiting vector // now (figuratively) becomes the "limited"-vector (still {0,0,-1}) because only cells where X == 0 OR Y == 0 are newly loaded. // The loading uses the GetAdjacentCell method of the WorldMap-Class and utilized the shifting vector. //FOR DEBUG PURPOSES Stopwatch sw = new Stopwatch(); sw.Start(); //1. Setup Vectors int[] vect = { rel_x, rel_y, rel_z }; int[] unload_vect = { -1, -1, -1 }; int[] shift_vect = { 0, 0, 0 }; //1.1 Initialize the "unload vector" for (int i = 0; i < 3; i++) { if (vect[i] == 0) unload_vect[i] = 2; //If shift on the "i"-Axis is -1, or "0" in the relative coords, //then the cells with "i+1" must be unloaded. if (vect[i] == 1) unload_vect[i] = -1;//If shift on the "i"-Axis is 0, or "1" in the relative coords, //then this axis doesn't warrant any action. if (vect[i] == 2) unload_vect[i] = 0; //If shift on the "i"-Axis is +1, or "2" in the relative coords, //then the cells with "i-1" must be unloaded. } //1.2 Convert the "relative" coordinates of the new cell (stored in vect[]) into the necessary // shifting operation on each axis. for (int i = 0; i < 3; i++) { if (vect[i] == 0) shift_vect[i] = -1; if (vect[i] == 1) shift_vect[i] = 0; if (vect[i] == 2) shift_vect[i] = 1; } //1.3 This vector stores the information necessary to prevent the algorithm from trying // to shift non-loaded cells into the cell array. It is also used (conversely) to load // only the cells that need loading. int[] limit_vect = { -1, -1, -1 }; for (int i = 0; i < 3; i++) { if (vect[i] == 0) limit_vect[i] = 0; if (vect[i] == 1) limit_vect[i] = -1; if (vect[i] == 2) limit_vect[i] = 2; } //--------------------------------------------------------------------------------- //0) RUN PRE-ALGORITHM CHECK if cells to be loaded actually exists, // if not, deny the move. // // NOTE: This has to be run previous to every other operation, because the // initial state is not backed up for memory reasons, so "undo" later is impossible //--------------------------------------------------------------------------------- bool[] causes_validation = { false, false, false }; for (int x = 0; x < 3; x++) { if (x == limit_vect[0] || limit_vect[0] == -1) { causes_validation[0] = true; } for (int y = 0; y < 3; y++) { if (y == limit_vect[1] || limit_vect[1] == -1) { causes_validation[1] = true; } for (int z = 0; z < 3; z++) { if (z == limit_vect[2] || limit_vect[2] == -1) { causes_validation[2] = true; } if (!causes_validation[0] && !causes_validation[1] && !causes_validation[2]) continue; if (wm.GetAdjacentCell(shift_vect[0], shift_vect[1], shift_vect[2], cells[x, y, z]) == null) return false; causes_validation[2] = false; } causes_validation[1] = false; } causes_validation[0] = false; } initialized = false; //--------------------------------------------------------------------------------- //I) UNLOAD OLD CELLS, i.e. the cells on the "far end" of the coordinate block //--------------------------------------------------------------------------------- for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { if (x == unload_vect[0] || y == unload_vect[1] || z == unload_vect[2]) { unloadCellContent(cells[x, y, z]); cells[x, y, z].Unload(); } } } } //--------------------------------------------------------------------------------- //II) SHIFT remaining original cells in the appropriate direction //--------------------------------------------------------------------------------- //Setup temporary cells array (this is needed to ensure neither loading or shifting // is done on cells that have already been shifted or loaded) Cell[, ,] tempcells = new Cell[3, 3, 3]; Array.Copy(cells, tempcells, cells.Length); //Do the shifting for (int x = 0; x < 3; x++) { if (x != limit_vect[0]) { for (int y = 0; y < 3; y++) { if (y != limit_vect[1]) { for (int z = 0; z < 3; z++) { if (z != limit_vect[2]) { cells[x, y, z] = tempcells[x + shift_vect[0], y + shift_vect[1], z + shift_vect[2]]; } } } } } } //--------------------------------------------------------------------------------- //III) LOAD new cells //--------------------------------------------------------------------------------- //Do the loading int derp = 0; bool[] cause_load = { false, false, false }; for (int x = 0; x < 3; x++) { if (x == limit_vect[0]) { cause_load[0] = true; } for (int y = 0; y < 3; y++) { if (y == limit_vect[1]) { cause_load[1] = true; } for (int z = 0; z < 3; z++) { if (z == limit_vect[2]) { cause_load[2] = true; } if (!cause_load[0] && !cause_load[1] && !cause_load[2]) continue; loaded[x, y, z] = false; load_after[x, y, z] = true; cells[x, y, z] = wm.GetAdjacentCell(shift_vect[0], shift_vect[1], shift_vect[2], tempcells[x, y, z]); if (Program.game.MULTITHREADED_LOADING) { System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker(); bw.DoWork += new System.ComponentModel.DoWorkEventHandler(backgroundWorker_DoWork); bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); bw.RunWorkerAsync(cells[x, y, z]); } else { cells[x, y, z].Load(); loadCellContent(cells[x, y, z].CellID); } derp++; cause_load[2] = false; } cause_load[1] = false; } cause_load[0] = false; } tempcells = null; sw.Stop(); _out.SendMessage(derp + " new cells loaded: Loading took " + sw.ElapsedMilliseconds + "ms."); return true; }
/// <summary> /// This function returns the cell at the given relative position of the given cell. /// </summary> public Cell GetAdjacentCell(int dx, int dy, int dz, Cell c) { int rx = (c.X / CELL_WIDTH); //TRUNCATED! int ry = (c.Y/ CELL_HEIGHT); int rz = (c.Z / CELL_DEPTH); if (rx + dx >= 0 && ry + dy >= 0 && rz + dz >= 0) { try { return cells[rx + dx, ry + dy, rz + dz]; } catch { return null; } } return null; }