コード例 #1
0
ファイル: combat.cpp プロジェクト: BG1/warzone2100
/* Fire a weapon at something */
bool combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, int weapon_slot)
{
	WEAPON_STATS	*psStats;
	UDWORD			firePause;
	SDWORD			longRange;
	int				compIndex;

	CHECK_OBJECT(psAttacker);
	CHECK_OBJECT(psTarget);
	ASSERT(psWeap != NULL, "Invalid weapon pointer");

	/* Watermelon:dont shoot if the weapon_slot of a vtol is empty */
	if (psAttacker->type == OBJ_DROID && isVtolDroid(((DROID *)psAttacker)))
	{
		if (((DROID *)psAttacker)->sMove.iAttackRuns[weapon_slot] >= getNumAttackRuns(((DROID *)psAttacker), weapon_slot))
		{
			objTrace(psAttacker->id, "VTOL slot %d is empty", weapon_slot);
			return false;
		}
	}

	/* Get the stats for the weapon */
	compIndex = psWeap->nStat;
	ASSERT_OR_RETURN( false , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
	psStats = asWeaponStats + compIndex;

	// check valid weapon/prop combination
	if (!validTarget(psAttacker, psTarget, weapon_slot))
	{
		return false;
	}

	unsigned fireTime = gameTime - deltaGameTime;  // Can fire earliest at the start of the tick.

	/*see if reload-able weapon and out of ammo*/
	if (psStats->reloadTime && !psWeap->ammo)
	{
		fireTime = std::max(fireTime, psWeap->lastFired + weaponReloadTime(psStats, psAttacker->player));
		if (gameTime <= fireTime)
		{
			return false;
		}
		//reset the ammo level
		psWeap->ammo = psStats->numRounds;
	}

	/* See when the weapon last fired to control it's rate of fire */
	firePause = weaponFirePause(psStats, psAttacker->player);
	firePause = std::max(firePause, 1u);  // Don't shoot infinitely many shots at once.
	fireTime = std::max(fireTime, psWeap->lastFired + firePause);

	if (gameTime <= fireTime)
	{
		/* Too soon to fire again */
		return false;
	}

	if (psTarget->visible[psAttacker->player] != UBYTE_MAX)
	{
		// Can't see it - can't hit it
		objTrace(psAttacker->id, "combFire(%u[%s]->%u): Object has no indirect sight of target", psAttacker->id, psStats->pName, psTarget->id);
		return false;
	}

	/* Check we can see the target */
	if (psAttacker->type == OBJ_DROID && !isVtolDroid((DROID *)psAttacker)
	    && (proj_Direct(psStats) || actionInsideMinRange((DROID *)psAttacker, psTarget, psStats)))
	{
		if(!lineOfFire(psAttacker, psTarget, weapon_slot, true))
		{
			// Can't see the target - can't hit it with direct fire
			objTrace(psAttacker->id, "combFire(%u[%s]->%u): Droid has no direct line of sight to target",
			      psAttacker->id, ((DROID *)psAttacker)->aName, psTarget->id);
			return false;
		}
	}
	else if ((psAttacker->type == OBJ_STRUCTURE) &&
			 (((STRUCTURE *)psAttacker)->pStructureType->height == 1) &&
			 proj_Direct(psStats))
	{
		// a bunker can't shoot through walls
		if (!lineOfFire(psAttacker, psTarget, weapon_slot, true))
		{
			// Can't see the target - can't hit it with direct fire
			objTrace(psAttacker->id, "combFire(%u[%s]->%u): Structure has no direct line of sight to target",
			      psAttacker->id, ((STRUCTURE *)psAttacker)->pStructureType->pName, psTarget->id);
			return false;
		}
	}
	else if ( proj_Direct(psStats) )
	{
		// VTOL or tall building
		if (!lineOfFire(psAttacker, psTarget, weapon_slot, false))
		{
			// Can't see the target - can't hit it with direct fire
			objTrace(psAttacker->id, "combFire(%u[%s]->%u): Tall object has no direct line of sight to target",
			      psAttacker->id, psStats->pName, psTarget->id);
			return false;
		}
	}

	Vector3i deltaPos = psTarget->pos - psAttacker->pos;

	// if the turret doesn't turn, check if the attacker is in alignment with the target
	if (psAttacker->type == OBJ_DROID && !psStats->rotate)
	{
		uint16_t targetDir = iAtan2(removeZ(deltaPos));
		int dirDiff = abs(angleDelta(targetDir - psAttacker->rot.direction));
		if (dirDiff > FIXED_TURRET_DIR)
		{
			return false;
		}
	}

	/* Now see if the target is in range  - also check not too near */
	int dist = iHypot(removeZ(deltaPos));
	longRange = proj_GetLongRange(psStats);

	/* modification by CorvusCorax - calculate shooting angle */
	int min_angle = 0;
	// only calculate for indirect shots
	if (!proj_Direct(psStats) && dist > 0)
	{
		min_angle = arcOfFire(psAttacker,psTarget,weapon_slot,true);

		// prevent extremely steep shots
		min_angle = std::min(min_angle, DEG(PROJ_ULTIMATE_PITCH));

		// adjust maximum range of unit if forced to shoot very steep
		if (min_angle > DEG(PROJ_MAX_PITCH))
		{
			//do not allow increase of max range though
			if (iSin(2*min_angle) < iSin(2*DEG(PROJ_MAX_PITCH)))  // If PROJ_MAX_PITCH == 45, then always iSin(2*min_angle) <= iSin(2*DEG(PROJ_MAX_PITCH)), and the test is redundant.
			{
				longRange = longRange * iSin(2*min_angle) / iSin(2*DEG(PROJ_MAX_PITCH));
			}
		}
	}

	int baseHitChance = 0;
	if ((dist <= psStats->shortRange)  && (dist >= psStats->minRange))
	{
		// get weapon chance to hit in the short range
		baseHitChance = weaponShortHit(psStats,psAttacker->player);
	}
	else if ((dist <= longRange && dist >= psStats->minRange)
	         || (psAttacker->type == OBJ_DROID
	             && !proj_Direct(psStats)
	             && actionInsideMinRange((DROID *)psAttacker, psTarget, psStats)))
	{
		// get weapon chance to hit in the long range
		baseHitChance = weaponLongHit(psStats,psAttacker->player);

		// adapt for height adjusted artillery shots
		if (min_angle > DEG(PROJ_MAX_PITCH))
		{
			baseHitChance = baseHitChance * iCos(min_angle) / iCos(DEG(PROJ_MAX_PITCH));
		}
	}
	else
	{
		/* Out of range */
		objTrace(psAttacker->id, "combFire(%u[%s]->%u): Out of range", psAttacker->id, psStats->pName, psTarget->id);
		return false;
	}

	// apply experience accuracy modifiers to the base
	//hit chance, not to the final hit chance
	int resultHitChance = baseHitChance;

	// add the attacker's experience
	if (psAttacker->type == OBJ_DROID)
	{
		SDWORD	level = getDroidEffectiveLevel((DROID *) psAttacker);

		// increase total accuracy by EXP_ACCURACY_BONUS % for each experience level
		resultHitChance += EXP_ACCURACY_BONUS * level * baseHitChance / 100;
	}

	// subtract the defender's experience
	if (psTarget->type == OBJ_DROID)
	{
		SDWORD	level = getDroidEffectiveLevel((DROID *) psTarget);

		// decrease weapon accuracy by EXP_ACCURACY_BONUS % for each experience level
		resultHitChance -= EXP_ACCURACY_BONUS * level * baseHitChance / 100;

	}

	// fire while moving modifiers
	if (psAttacker->type == OBJ_DROID &&
		((DROID *)psAttacker)->sMove.Status != MOVEINACTIVE)
	{
		switch (psStats->fireOnMove)
		{
		case FOM_NO:
			// Can't fire while moving
			return false;
			break;
		case FOM_PARTIAL:
			resultHitChance = FOM_PARTIAL_ACCURACY_PENALTY * resultHitChance / 100;
			break;
		case FOM_YES:
			// can fire while moving
			break;
		}
	}

	/* -------!!! From that point we are sure that we are firing !!!------- */

	// Add a random delay to the next shot.
	// TODO Add deltaFireTime to the time it takes to fire next. If just adding to psWeap->lastFired, it might put it in the future, causing assertions. And if not sometimes putting it in the future, the fire rate would be lower than advertised.
	//int fireJitter = firePause/100;  // ±1% variation in fire rate.
	//int deltaFireTime = gameRand(fireJitter*2 + 1) - fireJitter;

	/* note when the weapon fired */
	psWeap->lastFired = fireTime;

	/* reduce ammo if salvo */
	if (psStats->reloadTime)
	{
		psWeap->ammo--;
	}

	// increment the shots counter
	psWeap->shotsFired++;

	// predicted X,Y offset per sec
	Vector3i predict = psTarget->pos;

	//Watermelon:Target prediction
	if (isDroid(psTarget))
	{
		DROID *psDroid = castDroid(psTarget);

		int32_t flightTime;
		if (proj_Direct(psStats) || dist <= psStats->minRange)
		{
			flightTime = dist * GAME_TICKS_PER_SEC / psStats->flightSpeed;
		}
		else
		{
			int32_t vXY, vZ;  // Unused, we just want the flight time.
			flightTime = projCalcIndirectVelocities(dist, deltaPos.z, psStats->flightSpeed, &vXY, &vZ, min_angle);
		}

		if (psTarget->lastHitWeapon == WSC_EMP)
		{
			int empTime = EMP_DISABLE_TIME - (gameTime - psTarget->timeLastHit);
			CLIP(empTime, 0, EMP_DISABLE_TIME);
			if (empTime >= EMP_DISABLE_TIME * 9/10)
			{
				flightTime = 0;  /* Just hit.  Assume they'll get hit again */
			}
			else
			{
				flightTime = MAX(0, flightTime - empTime);
			}
		}

		predict += Vector3i(iSinCosR(psDroid->sMove.moveDir, psDroid->sMove.speed*flightTime / GAME_TICKS_PER_SEC), 0);
		if (!isFlying(psDroid))
		{
			predict.z = map_Height(removeZ(predict));  // Predict that the object will be on the ground.
		}
	}

	/* Fire off the bullet to the miss location. The miss is only visible if the player owns the target. (Why? - Per) */
	// What bVisible really does is to make the projectile audible even if it misses you. Since the target is NULL, proj_SendProjectile can't check if it was fired at you.
	bool bVisibleAnyway = psTarget->player == selectedPlayer;

	// see if we were lucky to hit the target
	bool isHit = gameRand(100) <= resultHitChance;
	if (isHit)
	{
		/* Kerrrbaaang !!!!! a hit */
		objTrace(psAttacker->id, "combFire: [%s]->%u: resultHitChance=%d, visibility=%d", psStats->pName, psTarget->id, resultHitChance, (int)psTarget->visible[psAttacker->player]);
		syncDebug("hit=(%d,%d,%d)", predict.x, predict.y, predict.z);
	}
	else /* Deal with a missed shot */
	{
		const int minOffset = 5;

		int missDist = 2 * (100 - resultHitChance) + minOffset;
		Vector3i miss = Vector3i(iSinCosR(gameRand(DEG(360)), missDist), 0);
		predict += miss;

		psTarget = NULL;  // Missed the target, so don't expect to hit it.

		objTrace(psAttacker->id, "combFire: Missed shot by (%4d,%4d)", miss.x, miss.y);
		syncDebug("miss=(%d,%d,%d)", predict.x, predict.y, predict.z);
	}

	// Make sure we don't pass any negative or out of bounds numbers to proj_SendProjectile
	CLIP(predict.x, 0, world_coord(mapWidth - 1));
	CLIP(predict.y, 0, world_coord(mapHeight - 1));

	proj_SendProjectileAngled(psWeap, psAttacker, psAttacker->player, predict, psTarget, bVisibleAnyway, weapon_slot, min_angle, fireTime);
	return true;
}
コード例 #2
0
ファイル: HopperEntity.cpp プロジェクト: daemon777/MCServer
/// Moves pickups from above this hopper into it. Returns true if the contents have changed.
bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
{
	UNUSED(a_CurrentTick);

	class cHopperPickupSearchCallback :
		public cEntityCallback
	{
	public:
		cHopperPickupSearchCallback(const Vector3i & a_Pos, cItemGrid & a_Contents) :
			m_Pos(a_Pos),
			m_bFoundPickupsAbove(false),
			m_Contents(a_Contents)
		{
		}

		virtual bool Item(cEntity * a_Entity) override
		{
			ASSERT(a_Entity != NULL);

			if (!a_Entity->IsPickup() || a_Entity->IsDestroyed())
			{
				return false;
			}

			Vector3f EntityPos = a_Entity->GetPosition();
			Vector3f BlockPos(m_Pos.x + 0.5f, static_cast<float>(m_Pos.y) + 1, m_Pos.z + 0.5f);  // One block above hopper, and search from center outwards
			double Distance = (EntityPos - BlockPos).Length();

			if (Distance < 0.5)
			{
				if (TrySuckPickupIn(static_cast<cPickup *>(a_Entity)))
				{
					return false;
				}
			}

			return false;
		}

		bool TrySuckPickupIn(cPickup * a_Pickup)
		{
			cItem & Item = a_Pickup->GetItem();

			for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
			{
				if (m_Contents.IsSlotEmpty(i))
				{
					m_bFoundPickupsAbove = true;
					m_Contents.SetSlot(i, Item);
					a_Pickup->Destroy();  // Kill pickup

					return true;
				}
				else if (m_Contents.GetSlot(i).IsEqual(Item) && !m_Contents.GetSlot(i).IsFullStack())
				{
					m_bFoundPickupsAbove = true;

					int PreviousCount = m_Contents.GetSlot(i).m_ItemCount;

					Item.m_ItemCount -= m_Contents.ChangeSlotCount(i, Item.m_ItemCount) - PreviousCount;  // Set count to however many items were added

					if (Item.IsEmpty())
					{
						a_Pickup->Destroy();  // Kill pickup if all items were added
					}
					return true;
				}
			}
			return false;
		}

		bool FoundPickupsAbove(void) const
		{
			return m_bFoundPickupsAbove;
		}

	protected:
		Vector3i m_Pos;
		bool m_bFoundPickupsAbove;
		cItemGrid & m_Contents;
	};

	cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents);
	a_Chunk.ForEachEntity(HopperPickupSearchCallback);

	return HopperPickupSearchCallback.FoundPickupsAbove();
}
コード例 #3
0
ファイル: warcam.cpp プロジェクト: C1annad/warzone2100
static void updateCameraAcceleration(UBYTE update)
{
	Vector3i concern = swapYZ(trackingCamera.target->pos);
	Vector2i behind(0, 0); /* Irrelevant for normal radar tracking */
	bool bFlying = false;

	/*
		This is where we check what it is we're tracking.
		Were we to track a building or location - this is
		where it'd be set up
	*/
	/*
		If we're tracking a droid, then we need to track slightly in front
		of it in order that the droid appears down the screen a bit. This means
		that we need to find an offset point from it relative to it's present
		direction
	*/
	if (trackingCamera.target->type == OBJ_DROID)
	{
		const int angle = 90 - abs((player.r.x / 182) % 90);

		const DROID *psDroid = (DROID *)trackingCamera.target;
		const PROPULSION_STATS *psPropStats = &asPropulsionStats[psDroid->asBits[COMP_PROPULSION]];

		if (psPropStats->propulsionType == PROPULSION_TYPE_LIFT)
		{
			bFlying = true;
		}

		/* Present direction is important */
		if (getNumDroidsSelected() > 2)
		{
			unsigned group = trackingCamera.target->selected ? GROUP_SELECTED : trackingCamera.target->group;

			uint16_t multiAngle = getAverageTrackAngle(group, true);
			getTrackingConcerns(&concern.x, &concern.y, &concern.z, group, true);

			behind = iSinCosR(multiAngle, CAM_DEFAULT_OFFSET);
		}
		else
		{
			behind = iSinCosR(trackingCamera.target->rot.direction, CAM_DEFAULT_OFFSET);
		}

		concern.y += angle * 5;
	}

	Vector3i realPos = concern - Vector3i(-behind.x, 0, -behind.y);
	Vector3f separation = realPos - trackingCamera.position;
	Vector3f acceleration;
	if (!bFlying)
	{
		acceleration = separation * ACCEL_CONSTANT - trackingCamera.velocity * VELOCITY_CONSTANT;
	}
	else
	{
		separation.y /= 2.0f;
		acceleration = separation * (ACCEL_CONSTANT * 4) - trackingCamera.velocity * (VELOCITY_CONSTANT * 2);
	}

	if (update & X_UPDATE)
	{
		/* Need to update acceleration along x axis */
		trackingCamera.acceleration.x = acceleration.x;
	}

	if (update & Y_UPDATE)
	{
		/* Need to update acceleration along y axis */
		trackingCamera.acceleration.y = acceleration.y;
	}

	if (update & Z_UPDATE)
	{
		/* Need to update acceleration along z axis */
		trackingCamera.acceleration.z = acceleration.z;
	}
}
コード例 #4
0
  bool MeshGenerator::marchingCube(const Vector3i &pos)
  {
    float afCubeValue[8];
    Vector3f asEdgeVertex[12];
    Vector3f asEdgeNorm[12];

    // Calculate the position in the Cube
    Vector3f fPos(pos.x() * m_stepSize + m_min.x(),
                  pos.y() * m_stepSize + m_min.y(),
                  pos.z() * m_stepSize + m_min.z());

    //Make a local copy of the values at the cube's corners
    for(int i = 0; i < 8; ++i) {
      afCubeValue[i] = m_cube->value(Vector3i(pos + Vector3i(a2iVertexOffset[i])));
    }

    //Find which vertices are inside of the surface and which are outside
    long iFlagIndex = 0;
    for(int i = 0; i < 8; ++i) {
      if(afCubeValue[i] <= m_iso) {
        iFlagIndex |= 1<<i;
      }
    }

    //Find which edges are intersected by the surface
    long iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];

    // No intersections if the cube is entirely inside or outside of the surface
    if(iEdgeFlags == 0) {
      return false;
    }

    //Find the point of intersection of the surface with each edge
    //Then find the normal to the surface at those points
    for(int i = 0; i < 12; ++i) {
      //if there is an intersection on this edge
      if(iEdgeFlags & (1<<i)) {
        float fOffset = offset(afCubeValue[a2iEdgeConnection[i][0]],
                               afCubeValue[a2iEdgeConnection[i][1]]);

        asEdgeVertex[i] = Vector3f(
          fPos.x() + (a2fVertexOffset[a2iEdgeConnection[i][0]][0]
                      + fOffset * a2fEdgeDirection[i][0]) * m_stepSize,
          fPos.y() + (a2fVertexOffset[a2iEdgeConnection[i][0]][1]
                      + fOffset * a2fEdgeDirection[i][1]) * m_stepSize,
          fPos.z() + (a2fVertexOffset[a2iEdgeConnection[i][0]][2]
                      + fOffset * a2fEdgeDirection[i][2]) * m_stepSize);

        /// FIXME Optimize this to only calculate normals when required
        asEdgeNorm[i] = normal(asEdgeVertex[i]);
      }
    }

    // Store the triangles that were found, there can be up to five per cube
    for(int i = 0; i < 5; ++i) {
      if(a2iTriangleConnectionTable[iFlagIndex][3*i] < 0)
        break;
      int iVertex = 0;
      iEdgeFlags = a2iTriangleConnectionTable[iFlagIndex][3*i];
      // Make sure we get the triangle winding the right way around!
      if (!m_reverseWinding) {
        for(int j = 0; j < 3; ++j) {
          iVertex = a2iTriangleConnectionTable[iFlagIndex][3*i+j];
          m_indices.push_back(m_vertices.size());
          m_normals.push_back(asEdgeNorm[iVertex]);
          m_vertices.push_back(asEdgeVertex[iVertex]);
        }
      }
      else {
        for(int j = 2; j >= 0; --j) {
          iVertex = a2iTriangleConnectionTable[iFlagIndex][3*i+j];
          m_indices.push_back(m_vertices.size());
          m_normals.push_back(-asEdgeNorm[iVertex]);
          m_vertices.push_back(asEdgeVertex[iVertex]);
        }
      }

    }
    return true;
  }
コード例 #5
0
ファイル: PieceGenerator.cpp プロジェクト: Didey/MCServer
		virtual Vector3i GetSize(void) const override
		{
			return Vector3i(m_Size, 5, m_Size);
		}
コード例 #6
0
void BasicMesh::Subdivide() {
  // Loop subdivision
  // NOTE The indices of the original set of vertices do not change after
  // subdivision. The new vertices are simply added to the set of vertices.
  // However, the faces change their indices after subdivision. See how new
  // faces are added to the face set for details.
  // In short, the new mesh is created as follows:
  //   [old vertices]
  //   [new vertices]
  //   [faces]

  // For each edge, compute its center point
  struct edge_t {
    edge_t() {}
    edge_t(int s, int t) : s(s), t(t) {}
    edge_t(const edge_t& e) : s(e.s), t(e.t) {}
    bool operator<(const edge_t& other) const {
      if(s < other.s) return true;
      else if( s > other.s ) return false;
      else return t < other.t;
    }
    int s, t;
  };

  struct face_edge_t {
    face_edge_t() {}
    face_edge_t(int fidx, edge_t e) : fidx(fidx), e(e) {}
    bool operator<(const face_edge_t& other) const {
      if(fidx < other.fidx) return true;
      else if(fidx > other.fidx) return false;
      return e < other.e;
    }
    int fidx;
    edge_t e;
  };

  const int num_faces = NumFaces();

  map<edge_t, Vector3d> midpoints;

  // iterate over all edges
  for (HalfEdgeMesh::EdgeIter e=hemesh.edges_begin(); e!=hemesh.edges_end(); ++e) {
    auto heh = hemesh.halfedge_handle(e, 0);
    auto hefh = hemesh.halfedge_handle(e, 1);

    auto v0h = hemesh.to_vertex_handle(heh);
    auto v1h = hemesh.to_vertex_handle(hefh);

    int v0idx = vhandles_map[v0h];
    int v1idx = vhandles_map[v1h];

    auto v0 = verts.row(v0idx);
    auto v1 = verts.row(v1idx);

    if(hemesh.is_boundary(*e)) {
      // simply compute the mid point
      midpoints.insert(make_pair(edge_t(v0idx, v1idx),
                                 0.5 * (v0 + v1)));
    } else {
      // use [1/8, 3/8, 3/8, 1/8] weights
      auto v2h = hemesh.to_vertex_handle(hemesh.next_halfedge_handle(heh));
      auto v3h = hemesh.to_vertex_handle(hemesh.next_halfedge_handle(hefh));

      int v2idx = vhandles_map[v2h];
      int v3idx = vhandles_map[v3h];

      auto v2 = verts.row(v2idx);
      auto v3 = verts.row(v3idx);

      midpoints.insert(make_pair(edge_t(v0idx, v1idx), (v0 * 3 + v1 * 3 + v2 + v3) / 8.0));
    }
  }

  // Now create a new set of vertices and faces
  const int num_verts = NumVertices() + midpoints.size();
  MatrixX3d new_verts(num_verts, 3);

  // Smooth these points
  for(int i=0;i<NumVertices();++i) {
    auto vh = vhandles[i];
    if(hemesh.is_boundary(vh)) {
      // use [1/8, 6/8, 1/8] weights
      auto heh = hemesh.halfedge_handle(vh);
      if(heh.is_valid()) {
        assert(hemesh.is_boundary(hemesh.edge_handle(heh)));

        auto prev_heh = hemesh.prev_halfedge_handle(heh);

        auto to_vh = hemesh.to_vertex_handle(heh);
        auto from_vh = hemesh.from_vertex_handle(prev_heh);

        Vector3d p = 6 * verts.row(i);
        p += verts.row(vhandles_map[to_vh]);
        p += verts.row(vhandles_map[from_vh]);
        p /= 8.0;
        new_verts.row(i) = p;
      }
    } else {
      // loop through the neighbors and count them
      int valence = 0;
      Vector3d p(0, 0, 0);
      for(auto vvit = hemesh.vv_iter(vh); vvit.is_valid(); ++vvit) {
        ++valence;
        p += verts.row(vhandles_map[*vvit]);
      }
      const double PI = 3.1415926535897;
      const double wa = (0.375 + 0.25 * cos(2.0 * PI / valence));
      const double w = (0.625 - wa * wa);
      p *= (w / valence);
      p += verts.row(i) * (1 - w);
      new_verts.row(i) = p;
    }
  }

  // Add the midpoints
  map<edge_t, int> midpoints_indices;
  int new_idx = NumVertices();
  for(auto p : midpoints) {
    midpoints_indices.insert(make_pair(p.first, new_idx));
    midpoints_indices.insert(make_pair(edge_t(p.first.t, p.first.s), new_idx));

    new_verts.row(new_idx) = p.second;
    ++new_idx;
  }

  // Process the texture coordinates
  map<face_edge_t, Vector2d> midpoints_texcoords;

  for(int fidx=0;fidx<NumFaces();++fidx){
    int j[] = {1, 2, 0};
    for(int i=0;i<3;++i) {
      int v0idx = faces(fidx, i);
      int v1idx = faces(fidx, j[i]);

      // if v0 = f[index_of(v0)], the tv0 = tf[index_of(v0)]
      int tv0idx = face_tex_index(fidx, i);
      // if v1 = f[index_of(v1)], the tv1 = tf[index_of(v1)]
      int tv1idx = face_tex_index(fidx, j[i]);

      auto t0 = texcoords.row(tv0idx);
      auto t1 = texcoords.row(tv1idx);

      // the texture coordinates is always the mid point
      midpoints_texcoords.insert(make_pair(face_edge_t(fidx, edge_t(v0idx, v1idx)),
                                           0.5 * (t0 + t1)));
    }
  }

  const int num_texcoords = texcoords.rows() + midpoints_texcoords.size();
  MatrixX2d new_texcoords(num_texcoords, 2);

  // Just copy the existing texture coordinates
  new_texcoords.topRows(texcoords.rows()) = texcoords;

  // Tex-coords for the mid points
  map<face_edge_t, int> midpoints_texcoords_indices;
  int new_texcoords_idx = texcoords.rows();
  for(auto p : midpoints_texcoords) {
    //cout << p.first.fidx << ": " << p.first.e.s << "->" << p.first.e.t << endl;
    //getchar();
    midpoints_texcoords_indices.insert(make_pair(p.first,
                                                 new_texcoords_idx));
    midpoints_texcoords_indices.insert(make_pair(face_edge_t(p.first.fidx, edge_t(p.first.e.t, p.first.e.s)),
                                                 new_texcoords_idx));

    new_texcoords.row(new_texcoords_idx) = p.second;
    ++new_texcoords_idx;
  }

  MatrixX3i new_faces(num_faces*4, 3);
  MatrixX3i new_face_tex_index(num_faces*4, 3);
  for(int i=0;i<num_faces;++i) {
    // vertex indices of the original triangle
    auto vidx0 = faces(i, 0);
    auto vidx1 = faces(i, 1);
    auto vidx2 = faces(i, 2);

    // texture coordinates indices of the original triangle
    auto tvidx0 = face_tex_index(i, 0);
    auto tvidx1 = face_tex_index(i, 1);
    auto tvidx2 = face_tex_index(i, 2);

    // indices of the mid points
    int nvidx01 = midpoints_indices[edge_t(vidx0, vidx1)];
    int nvidx12 = midpoints_indices[edge_t(vidx1, vidx2)];
    int nvidx20 = midpoints_indices[edge_t(vidx2, vidx0)];

    // indices of the texture coordinates of the mid points
    int tnvidx01 = midpoints_texcoords_indices.at(face_edge_t(i, edge_t(vidx0, vidx1)));
    int tnvidx12 = midpoints_texcoords_indices.at(face_edge_t(i, edge_t(vidx1, vidx2)));
    int tnvidx20 = midpoints_texcoords_indices.at(face_edge_t(i, edge_t(vidx2, vidx0)));

    // add the 4 new faces
    new_faces.row(i*4+0) = Vector3i(vidx0, nvidx01, nvidx20);
    new_faces.row(i*4+1) = Vector3i(nvidx20, nvidx01, nvidx12);
    new_faces.row(i*4+2) = Vector3i(nvidx20, nvidx12, vidx2);
    new_faces.row(i*4+3) = Vector3i(nvidx01, vidx1, nvidx12);

    new_face_tex_index.row(i*4+0) = Vector3i(tvidx0, tnvidx01, tnvidx20);
    new_face_tex_index.row(i*4+1) = Vector3i(tnvidx20, tnvidx01, tnvidx12);
    new_face_tex_index.row(i*4+2) = Vector3i(tnvidx20, tnvidx12, tvidx2);
    new_face_tex_index.row(i*4+3) = Vector3i(tnvidx01, tvidx1, tnvidx12);
  }

  verts = new_verts;
  faces = new_faces;
  texcoords = new_texcoords;
  face_tex_index = new_face_tex_index;

  // Update the normals after subdivision
  ComputeNormals();
}
コード例 #7
0
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	if (IsDestroyed())  // Mainly to stop detector rails triggering again after minecart is dead
	{
		return;
	}

	int PosY = POSY_TOINT;
	if ((PosY <= 0) || (PosY >= cChunkDef::Height))
	{
		// Outside the world, just process normal falling physics
		super::HandlePhysics(a_Dt, a_Chunk);
		BroadcastMovementUpdate();
		return;
	}
	
	int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
	cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
	if (Chunk == nullptr)
	{
		// Inside an unloaded chunk, bail out all processing
		return;
	}

	BLOCKTYPE InsideType;
	NIBBLETYPE InsideMeta;
	Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta);

	if (!IsBlockRail(InsideType))
	{
		// When a descending minecart hits a flat rail, it goes through the ground; check for this
		Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);
		if (IsBlockRail(InsideType))
		{
			// Push cart upwards
			AddPosY(1);
		}
	}

	bool WasDetectorRail = false;
	if (IsBlockRail(InsideType))
	{
		if (InsideType == E_BLOCK_RAIL)
		{
			SnapToRail(InsideMeta);
		}
		else
		{
			SnapToRail(InsideMeta & 0x07);
		}

		switch (InsideType)
		{
			case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break;
			case E_BLOCK_ACTIVATOR_RAIL: break;
			case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break;
			case E_BLOCK_DETECTOR_RAIL:
			{
				HandleDetectorRailPhysics(InsideMeta, a_Dt);
				WasDetectorRail = true;
				break;
			}
			default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break;
		}

		AddPosition(GetSpeed() * (a_Dt / 1000));  // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
	}
	else
	{
		// Not on rail, default physics
		SetPosY(floor(GetPosY()) + 0.35);  // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
		super::HandlePhysics(a_Dt, *Chunk);
	}

	if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition))
	{
		m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
		m_bIsOnDetectorRail = false;
	}
	else if (WasDetectorRail)
	{
		m_bIsOnDetectorRail = true;
		m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
	}
	
	// Broadcast positioning changes to client
	BroadcastMovementUpdate();
}
コード例 #8
0
ファイル: Monster.cpp プロジェクト: w00tc0d3/MCServer
void cMonster::TickPathFinding()
{
	const int PosX = POSX_TOINT;
	const int PosY = POSY_TOINT;
	const int PosZ = POSZ_TOINT;

	std::vector<Vector3d> m_PotentialCoordinates;
	m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ));

	static const struct  // Define which directions to try to move to
	{
		int x, z;
	} gCrossCoords[] =
	{
		{ 1, 0},
		{-1, 0},
		{ 0, 1},
		{ 0, -1},
	} ;
	
	if ((PosY - 1 < 0) || (PosY + 2 > cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */)
	{
		// Too low/high, can't really do anything
		FinishPathFinding();
		return;
	}

	for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
	{
		if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ)))
		{
			continue;
		}

		BLOCKTYPE BlockAtY = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ);
		BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
		BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
		int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ);
		BLOCKTYPE BlockAtLowestY = m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);

		if (
			(!cBlockInfo::IsSolid(BlockAtY)) &&
			(!cBlockInfo::IsSolid(BlockAtYP)) &&
			(!IsBlockLava(BlockAtLowestY)) &&
			(BlockAtLowestY != E_BLOCK_CACTUS) &&
			(PosY - LowestY < FALL_DAMAGE_HEIGHT)
			)
		{
			m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ));
		}
		else if (
			(cBlockInfo::IsSolid(BlockAtY)) &&
			(BlockAtY != E_BLOCK_CACTUS) &&
			(!cBlockInfo::IsSolid(BlockAtYP)) &&
			(!cBlockInfo::IsSolid(BlockAtYPP)) &&
			(BlockAtY != E_BLOCK_FENCE) &&
			(BlockAtY != E_BLOCK_FENCE_GATE)
			)
		{
			m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ));
		}
	}

	if (!m_PotentialCoordinates.empty())
	{
		Vector3f ShortestCoords = m_PotentialCoordinates.front();
		for (std::vector<Vector3d>::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr)
		{
			Vector3f Distance = m_FinalDestination - ShortestCoords;
			Vector3f Distance2 = m_FinalDestination - *itr;
			if (Distance.SqrLength() > Distance2.SqrLength())
			{
				ShortestCoords = *itr;
			}
		}

		m_Destination = ShortestCoords;
		m_Destination.z += 0.5f;
		m_Destination.x += 0.5f;
	}
	else
	{
		FinishPathFinding();
	}
}
コード例 #9
0
ファイル: component.cpp プロジェクト: ArtemusRus/warzone2100
void destroyFXDroid(DROID	*psDroid)
{
	for (int i = 0; i < 5; ++i)
	{
		iIMDShape *psImd = NULL;

		int maxHorizontalScatter = TILE_UNITS/4;
		int heightScatter = TILE_UNITS/5;
		Vector2i horizontalScatter = iSinCosR(rand(), rand()%maxHorizontalScatter);

		Vector3i pos = swapYZ(psDroid->pos + Vector3i(horizontalScatter, 16 + heightScatter));
		switch(i)
		{
		case 0:
			switch(psDroid->droidType)
			{
			case DROID_DEFAULT:
			case DROID_CYBORG:
			case DROID_CYBORG_SUPER:
			case DROID_CYBORG_CONSTRUCT:
			case DROID_CYBORG_REPAIR:
			case DROID_WEAPON:
			case DROID_COMMAND:
				if (psDroid->numWeaps > 0)
				{
					if(psDroid->asWeaps[0].nStat > 0)
					{
						psImd = WEAPON_MOUNT_IMD(psDroid, 0);
					}
				}
				break;
			default:
				break;
			}
			break;
		case 1:
			switch(psDroid->droidType)
			{
			case DROID_DEFAULT:
			case DROID_CYBORG:
			case DROID_CYBORG_SUPER:
			case DROID_CYBORG_CONSTRUCT:
			case DROID_CYBORG_REPAIR:
			case DROID_WEAPON:
			case DROID_COMMAND:
				if(psDroid->numWeaps)
				{
					// get main weapon
					psImd = WEAPON_IMD(psDroid, 0);
				}
				break;
			default:
				break;
			}
			break;
		}
		if (psImd == NULL)
		{
			psImd = getRandomDebrisImd();
		}
		// Tell the effect system that it needs to use this player's color for the next effect
		SetEffectForPlayer(psDroid->player);
		addEffect(&pos, EFFECT_GRAVITON, GRAVITON_TYPE_EMITTING_DR, true, psImd, getPlayerColour(psDroid->player));
	}
}
コード例 #10
0
/*
  this offset nulling algorithm is inspired by this paper from Bill Premerlani

  http://gentlenav.googlecode.com/files/MagnetometerOffsetNullingRevisited.pdf

  The base algorithm works well, but is quite sensitive to
  noise. After long discussions with Bill, the following changes were
  made:

    1) we keep a history buffer that effectively divides the mag
       vectors into a set of N streams. The algorithm is run on the
       streams separately

    2) within each stream we only calculate a change when the mag
       vector has changed by a significant amount.

  This gives us the property that we learn quickly if there is no
  noise, but still learn correctly (and slowly) in the face of lots of
  noise.
 */
void
Compass::null_offsets(void)
{
    if (_null_enable == false || _learn == 0) {
        // auto-calibration is disabled
        return;
    }

    // this gain is set so we converge on the offsets in about 5
    // minutes with a 10Hz compass
    const float gain = 0.01;
    const float max_change = 10.0;
    const float min_diff = 50.0;
    Vector3f ofs;

    ofs = _offset.get();

    if (!_null_init_done) {
        // first time through
        _null_init_done = true;
        for (uint8_t i=0; i<_mag_history_size; i++) {
            // fill the history buffer with the current mag vector,
            // with the offset removed
            _mag_history[i] = Vector3i((mag_x+0.5) - ofs.x, (mag_y+0.5) - ofs.y, (mag_z+0.5) - ofs.z);
        }
        _mag_history_index = 0;
        return;
    }

    Vector3f b1, b2, diff;
    float length;

    // get a past element
    b1 = Vector3f(_mag_history[_mag_history_index].x,
                  _mag_history[_mag_history_index].y,
                  _mag_history[_mag_history_index].z);
    // the history buffer doesn't have the offsets
    b1 += ofs;

    // get the current vector
    b2 = Vector3f(mag_x, mag_y, mag_z);

    // calculate the delta for this sample
    diff = b2 - b1;
    length = diff.length();
    if (length < min_diff) {
        // the mag vector hasn't changed enough - we don't get
        // enough information from this vector to use it.
        // Note that we don't put the current vector into the mag
        // history here. We want to wait for a larger rotation to
        // build up before calculating an offset change, as accuracy
        // of the offset change is highly dependent on the size of the
        // rotation.
        _mag_history_index = (_mag_history_index + 1) % _mag_history_size;
        return;
    }

    // put the vector in the history
    _mag_history[_mag_history_index] = Vector3i((mag_x+0.5) - ofs.x, (mag_y+0.5) - ofs.y, (mag_z+0.5) - ofs.z);
    _mag_history_index = (_mag_history_index + 1) % _mag_history_size;

    // equation 6 of Bills paper
    diff = diff * (gain * (b2.length() - b1.length()) / length);

    // limit the change from any one reading. This is to prevent
    // single crazy readings from throwing off the offsets for a long
    // time
    length = diff.length();
    if (length > max_change) {
        diff *= max_change / length;
    }

    // set the new offsets
    _offset.set(_offset.get() - diff);
}
コード例 #11
0
std::vector<Mesh> createTcpMeshes(const Graph<dim> &graph, const RobotBase &robot, double cubeExt = 10) {
    auto tcps = calcTcpList(graph, robot);
    Vector3 ext(cubeExt, cubeExt, cubeExt);

    std::vector<Mesh> meshes;
    for (auto &tcp : tcps) {
        Mesh mesh;

        mesh.vertices.push_back(Vector3(tcp[0], tcp[1], tcp[2]));
        mesh.vertices.push_back(Vector3(tcp[0] + ext[0], tcp[1], tcp[2]));
        mesh.vertices.push_back(Vector3(tcp[0] + ext[0], tcp[1] + ext[1], tcp[2]));
        mesh.vertices.push_back(Vector3(tcp[0], tcp[1] + ext[1], tcp[2]));
        mesh.vertices.push_back(Vector3(tcp[0], tcp[1], tcp[2] + ext[2]));
        mesh.vertices.push_back(Vector3(tcp[0] + ext[0], tcp[1], tcp[2] + ext[2]));
        mesh.vertices.push_back(Vector3(tcp[0] + ext[0], tcp[1] + ext[1], tcp[2] + ext[2]));
        mesh.vertices.push_back(Vector3(tcp[0], tcp[1] + ext[1], tcp[2] + ext[2]));

        size_t faceCount = 0;
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 2, faceCount + 1));
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 3, faceCount + 2));
        mesh.faces.push_back(Vector3i(faceCount + 1, faceCount + 2, faceCount + 6));
        mesh.faces.push_back(Vector3i(faceCount + 6, faceCount + 5, faceCount + 1));
        mesh.faces.push_back(Vector3i(faceCount + 4, faceCount + 5, faceCount + 6));
        mesh.faces.push_back(Vector3i(faceCount + 6, faceCount + 7, faceCount + 4));
        mesh.faces.push_back(Vector3i(faceCount + 2, faceCount + 3, faceCount + 6));
        mesh.faces.push_back(Vector3i(faceCount + 6, faceCount + 3, faceCount + 7));
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 7, faceCount + 3));
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 4, faceCount + 7));
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 1, faceCount + 5));
        mesh.faces.push_back(Vector3i(faceCount, faceCount + 5, faceCount + 4));

        meshes.push_back(mesh);
    }

    return meshes;
}
コード例 #12
0
	virtual Vector3i GetSize(void) const override
	{
		return Vector3i(m_SizeXZ, m_Height, m_SizeXZ);
	}
コード例 #13
0
bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
{
	// If we have a section above, check if there's fluid above this block that would feed it:
	if (a_RelY < cChunkDef::Height - 1)
	{
		if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ)))
		{
			// This block is fed from above, no more processing needed
			FLOG("  Fed from above");
			return false;
		}
	}

	// Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block):
	if (a_MyMeta != 8)
	{
		BLOCKTYPE BlockType;
		NIBBLETYPE BlockMeta;
		static const Vector3i Coords[] =
		{
			Vector3i( 1, 0,  0),
			Vector3i(-1, 0,  0),
			Vector3i( 0, 0,  1),
			Vector3i( 0, 0, -1),
		} ;
		for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
		{
			if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
			{
				continue;
			}
			if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta))
			{
				// This block is fed, no more processing needed
				FLOG("  Fed from {%d, %d, %d}, type %d, meta %d",
					a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x,
					a_RelY,
					a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z,
					BlockType, BlockMeta
				);
				return false;
			}
		}  // for i - Coords[]
	}  // if not fed from above
	
	// Block is not fed, decrease by m_Falloff levels:
	if (a_MyMeta >= 8)
	{
		FLOG("  Not fed and downwards, turning into non-downwards meta %d", m_Falloff);
		a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff);
	}
	else
	{
		a_MyMeta += m_Falloff;
		if (a_MyMeta < 8)
		{
			FLOG("  Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta);
			a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta);
		}
		else
		{
			FLOG("  Not fed, meta %d, erasing altogether", a_MyMeta);
			a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
		}
	}
	return true;
}
コード例 #14
0
ファイル: BlockPiston.cpp プロジェクト: 1285done/cuberite
bool cBlockPistonHandler::CanPushBlock(
	const Vector3i & a_BlockPos, cWorld * a_World, bool a_RequirePushable,
	Vector3iSet & a_BlocksPushed, const Vector3i & a_PushDir
)
{
	const static std::array<Vector3i, 6> pushingDirs =
	{
		{
			Vector3i(-1,  0,  0), Vector3i(1, 0, 0),
			Vector3i( 0, -1,  0), Vector3i(0, 1, 0),
			Vector3i( 0,  0, -1), Vector3i(0, 0, 1)
		}
	};

	BLOCKTYPE currBlock;
	NIBBLETYPE currMeta;
	a_World->GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, currBlock, currMeta);

	if (currBlock == E_BLOCK_AIR)
	{
		// Air can be pushed
		return true;
	}

	if (!a_RequirePushable && cBlockInfo::IsPistonBreakable(currBlock))
	{
		// Block should not be broken, when it's not in the pushing direction
		return true;
	}

	if (!CanPush(currBlock, currMeta))
	{
		// When it's not required to push this block, don't fail
		return !a_RequirePushable;
	}

	if (a_BlocksPushed.size() >= PISTON_MAX_PUSH_DISTANCE)
	{
		// Do not allow to push too much blocks
		return false;
	}

	if (!a_BlocksPushed.insert(a_BlockPos).second || cBlockInfo::IsPistonBreakable(currBlock))
	{
		return true;  // Element exist already
	}

	if (currBlock == E_BLOCK_SLIME_BLOCK)
	{
		// Try to push the other directions
		for (const auto & testDir : pushingDirs)
		{
			if (!CanPushBlock(a_BlockPos + testDir, a_World, false, a_BlocksPushed, a_PushDir))
			{
				// When it's not possible for a direction, then fail
				return false;
			}
		}
	}

	// Try to push the block in front of this block
	return CanPushBlock(a_BlockPos + a_PushDir, a_World, true, a_BlocksPushed, a_PushDir);
}
コード例 #15
0
  ForceDecomposition::ForceDecomposition(SimInfo* info, InteractionManager* iMan) : info_(info), interactionMan_(iMan), needVelocities_(false) {

    sman_ = info_->getSnapshotManager();
    storageLayout_ = sman_->getStorageLayout();
    ff_ = info_->getForceField();
    userChoseCutoff_ = false;

    usePeriodicBoundaryConditions_ = info->getSimParams()->getUsePeriodicBoundaryConditions();

    Globals* simParams_ = info_->getSimParams();    
    if (simParams_->havePrintHeatFlux()) {
      if (simParams_->getPrintHeatFlux()) {
        needVelocities_ = true;
      }
    }

    if (simParams_->haveSkinThickness()) {
      skinThickness_ = simParams_->getSkinThickness();
    } else {      
      skinThickness_ = 1.0;
      sprintf(painCave.errMsg,
              "ForceDecomposition: No value was set for the skinThickness.\n"
              "\tOpenMD will use a default value of %f Angstroms\n"
              "\tfor this simulation\n", skinThickness_);
      painCave.severity = OPENMD_INFO;
      painCave.isFatal = 0;
      simError();
    }             

    // cellOffsets are the partial space for the cell lists used in
    // constructing the neighbor lists
    cellOffsets_.clear();
    cellOffsets_.push_back( Vector3i(0, 0, 0) );
    cellOffsets_.push_back( Vector3i(1, 0, 0) );
    cellOffsets_.push_back( Vector3i(1, 1, 0) );
    cellOffsets_.push_back( Vector3i(0, 1, 0) );
    cellOffsets_.push_back( Vector3i(-1,1, 0) );
    cellOffsets_.push_back( Vector3i(0, 0, 1) );
    cellOffsets_.push_back( Vector3i(1, 0, 1) );
    cellOffsets_.push_back( Vector3i(1, 1, 1) );
    cellOffsets_.push_back( Vector3i(0, 1, 1) );
    cellOffsets_.push_back( Vector3i(-1,1, 1) );
    cellOffsets_.push_back( Vector3i(-1,0, 1) );
    cellOffsets_.push_back( Vector3i(-1,-1,1) );
    cellOffsets_.push_back( Vector3i(0, -1,1) );
    cellOffsets_.push_back( Vector3i(1, -1,1) );
  }