/* 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; }
/// 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(); }
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; } }
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; }
virtual Vector3i GetSize(void) const override { return Vector3i(m_Size, 5, m_Size); }
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(); }
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(); }
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(); } }
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)); } }
/* 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); }
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; }
virtual Vector3i GetSize(void) const override { return Vector3i(m_SizeXZ, m_Height, m_SizeXZ); }
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; }
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); }
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) ); }