void CollideWithTerrain(Player player, Vector3i position)
    {
        if (player != this.Controller)
        {
            return;
        }

        lock (this)
        {
            if (this.groundHits == null)
            {
                this.groundHits = new ThreadSafeHashSet <Vector3i>();
            }
        }

        WorldLayer playerActivity = WorldLayerManager.GetLayer(LayerNames.PlayerActivity);

        // destroy plants and spawn dirt within a radius under the hit position
        int radius = 1;

        for (int x = -radius; x <= radius; x++)
        {
            for (int z = -radius; z <= radius; z++)
            {
                var offsetpos = position + new Vector3i(x, -1, z);
                if (!this.groundHits.Add(offsetpos))
                {
                    continue;
                }

                var abovepos   = offsetpos + Vector3i.Up;
                var aboveblock = World.GetBlock(abovepos);
                var hitblock   = World.GetBlock(offsetpos);
                if (!aboveblock.Is <Solid>())
                {
                    // turn soil into dirt
                    if (hitblock.GetType() == typeof(GrassBlock) || hitblock.GetType() == typeof(ForestSoilBlock))
                    {
                        player.SpawnBlockEffect(offsetpos, typeof(DirtBlock), BlockEffect.Delete);
                        World.SetBlock <DirtBlock>(offsetpos);
                        BiomePusher.AddFrozenColumn(offsetpos.XZ);
                    }

                    // kill any above plants
                    if (aboveblock is PlantBlock)
                    {
                        // make sure there is a plant here, sometimes world/ecosim are out of sync
                        var plant = EcoSim.PlantSim.GetPlant(abovepos);
                        if (plant != null)
                        {
                            player.SpawnBlockEffect(abovepos, aboveblock.GetType(), BlockEffect.Delete);
                            EcoSim.PlantSim.DestroyPlant(plant, DeathType.Deforestation);
                        }
                        else
                        {
                            World.DeleteBlock(abovepos);
                        }
                    }

                    if (hitblock.Is <Solid>() && World.GetBlock(abovepos).Is <Empty>() && RandomUtil.Value < this.Species.ChanceToSpawnDebris)
                    {
                        Block placedBlock = World.SetBlock(typeof(TreeDebrisBlock), abovepos);
                        player.SpawnBlockEffect(abovepos, typeof(TreeDebrisBlock), BlockEffect.Place);
                        RoomData.QueueRoomTest(abovepos);
                    }
                }
            }
        }
    }