END_TEST /******************************************************************************* * test for chunk_hash[_inc]() */ START_TEST(test_chunk_hash) { chunk_t chunk; u_int32_t hash_a, hash_b, hash_c; chunk = chunk_from_str("asdf"); /* output is randomized, so there are no test-vectors we could use */ hash_a = chunk_hash(chunk); hash_b = chunk_hash(chunk); ck_assert(hash_a == hash_b); hash_b = chunk_hash_inc(chunk, hash_a); ck_assert(hash_a != hash_b); hash_c = chunk_hash_inc(chunk, hash_a); ck_assert(hash_b == hash_c); }
void add_biology(chunk *c) { world_map_pos wmpos; world_region *wr, *wr_second; float strbest, strsecond; world_region *wr_neighborhood[9]; biome *local_biome; list *sp_list; frequent_species fqsp; chunk_neighborhood ch_nbh; cell_neighborhood cl_nbh; cell* cl; block_index idx; block substrate; global_pos glpos; ptrdiff_t seed_hash = prng(chunk_hash(c) + 616485); ptrdiff_t spacing_hash = prng(THE_WORLD->seed + 44544); if (c->chunk_flags & CF_HAS_BIOLOGY) { return; // Already has biology } fill_chunk_neighborhood(&(c->glcpos), &ch_nbh); if (ch_nbh.members[0] == NULL) { return; // Return without setting the CF_HAS_BIOLOGY flag. } // Look up the local biomes: glcpos__wmpos(&(c->glcpos), &wmpos); // TODO: Avoid using THE_WORLD here? wr = get_world_region(THE_WORLD, &wmpos); if (wr == NULL) { // TODO: Go farther here? return; // outside the world map: no biology } get_world_neighborhood_small(THE_WORLD, &wmpos, wr_neighborhood); // Use the center of our chunk to compute region contenders: idx.xyz.x = CHUNK_SIZE / 2; idx.xyz.y = CHUNK_SIZE / 2; idx.xyz.z = CHUNK_SIZE / 2; idx.xyz.w = 0; cidx__glpos(c, &idx, &glpos); compute_region_contenders( THE_WORLD, wr_neighborhood, &glpos, 1232331, &wr, &wr_second, &strbest, &strsecond ); // Mix biomes for this chunk: local_biome = create_merged_biome( wr, wr_second, strbest, strsecond ); // Add seeds: idx.xyz.w = 0; for (idx.xyz.x = 0; idx.xyz.x < CHUNK_SIZE; ++idx.xyz.x) { for (idx.xyz.y = 0; idx.xyz.y < CHUNK_SIZE; ++idx.xyz.y) { for (idx.xyz.z = 0; idx.xyz.z < CHUNK_SIZE; ++idx.xyz.z) { // Get global cell position for hashing: cidx__glpos(c, &idx, &glpos); // TODO: optimize this within a loop... fill_cell_neighborhood_exact(idx, &ch_nbh, &cl_nbh); cl = cl_nbh.members[NBH_CENTER]; // Array is in xyz order so up/down is +/- 1, north/south is +/- 3, and // east/west is +/- 9. // If our secondary is empty we can put a seed here: figure out what // distribution to draw from. sp_list = NULL; if (b_is_void(cl->blocks[1])) { if (b_id(cl->blocks[0]) == B_AIR) { // Ephemeral species seeds settle in the air directly on top of // dirt/sand/mud/etc. substrate = cl_nbh.members[NBH_CENTER - NBH_DIR_UD]->blocks[0]; if (b_is_natural_terrain(substrate)) { sp_list = local_biome->ephemeral_terrestrial_flora; } } else if (b_id(cl->blocks[0]) == B_WATER) { // Aquatic ephemeral species start in water above dirt/sand/mud/etc. substrate = cl_nbh.members[NBH_CENTER - NBH_DIR_UD]->blocks[0]; if (b_is_natural_terrain(substrate)) { sp_list = local_biome->ephemeral_aquatic_flora; } } else if (b_is_natural_terrain(cl->blocks[0])) { substrate = cl->blocks[0]; if ( b_id(cl_nbh.members[NBH_CENTER + NBH_DIR_UD]->blocks[0]) == B_AIR ) { // Most terrestrial species sprout in the ground with air above. // TODO: Subterranean species! // Select between spacings: if (wide_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->wide_spaced_terrestrial_flora; } else if (medium_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->medium_spaced_terrestrial_flora; } else if (close_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->close_spaced_terrestrial_flora; } else { sp_list = local_biome->ubiquitous_terrestrial_flora; } } else if ( b_id(cl_nbh.members[NBH_CENTER + NBH_DIR_UD]->blocks[0]) ==B_WATER ) { // Aquatic plants sprout from seeds in terrain below water. // Select between spacings: if (wide_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->wide_spaced_aquatic_flora; } else if (medium_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->medium_spaced_aquatic_flora; } else if (close_spacing_hit(glpos, spacing_hash)) { sp_list = local_biome->close_spaced_aquatic_flora; } else { sp_list = local_biome->ubiquitous_aquatic_flora; } } else if ( b_id(cl_nbh.members[NBH_CENTER - NBH_DIR_UD]->blocks[0]) == B_AIR ) { // Some plants can also grow down into air from ceilings. sp_list = local_biome->hanging_terrestrial_flora; } // TODO: Should things grow in underwater ceilings? } // Now we generate a seed from the species list we decided on: if (sp_list != NULL) { fqsp = pick_appropriate_frequent_species( sp_list, substrate, seed_hash ); if (frequent_species_species_type(fqsp) != SPT_NO_SPECIES) { cl->blocks[1] = b_make_species( seed_block_type(frequent_species_species_type(fqsp)), frequent_species_species(fqsp) ); // TODO: Real sprout timers... gri_set_sprout_timer(&(cl->blocks[1]), 1); seed_hash = prng( idx.xyz.x + idx.xyz.y + glpos.z + prng(seed_hash + glpos.x + glpos.y + idx.xyz.z) ); } } } } } } // Clean up the local biome info now that we're done with it: cleanup_biome(local_biome); // Grow plants a bit: // TODO: How many cycles to use? #ifdef DEBUG if (!grow_plants(c, 2)) { // At this point if growth fails (it really shouldn't) the best we can do // is emit a warning... fprintf( stderr, "WARNING: Growth failed after adding seeds during biology generation.\n" ); } #else grow_plants(c, 2); #endif // Set the CF_HAS_BIOLOGY flag for this chunk: c->chunk_flags |= CF_HAS_BIOLOGY; }