int CUnitHandler::TestBuildSquare(const float3& pos, const UnitDef* unitdef, CFeature*& feature, int allyteam) { if (pos.x < 0 || pos.x >= gs->mapx * SQUARE_SIZE || pos.z < 0 || pos.z >= gs->mapy * SQUARE_SIZE) { return 0; } int ret = 2; int yardxpos = int(pos.x + 4) / SQUARE_SIZE; int yardypos = int(pos.z + 4) / SQUARE_SIZE; CSolidObject* s; if ((s = groundBlockingObjectMap->GroundBlocked(yardypos * gs->mapx + yardxpos))) { CFeature* f; if ((f = dynamic_cast<CFeature*>(s))) { if ((allyteam < 0) || f->IsInLosForAllyTeam(allyteam)) { if (!f->def->reclaimable) { return 0; } feature = f; } } else if (!dynamic_cast<CUnit*>(s) || (allyteam < 0) || (((CUnit*) s)->losStatus[allyteam] & LOS_INLOS)) { if (s->immobile) { return 0; } else { ret = 1; } } } const float groundheight = ground->GetHeight2(pos.x, pos.z); if (!unitdef->floater || groundheight > 0.0f) { // if we are capable of floating, only test local // height difference if terrain is above sea-level const float* heightmap = readmap->GetHeightmap(); int x = (int) (pos.x / SQUARE_SIZE); int z = (int) (pos.z / SQUARE_SIZE); float orgh = readmap->orgheightmap[z * (gs->mapx + 1) + x]; float h = heightmap[z * (gs->mapx + 1) + x]; float hdif = unitdef->maxHeightDif; if (pos.y > orgh + hdif && pos.y > h + hdif) { return 0; } if (pos.y < orgh - hdif && pos.y < h - hdif) { return 0; } } if (!unitdef->floater && groundheight < -unitdef->maxWaterDepth) { // ground is deeper than our maxWaterDepth, cannot build here return 0; } if (groundheight > -unitdef->minWaterDepth) { // ground is shallower than our minWaterDepth, cannot build here return 0; } return ret; }
float CGameHelper::GuiTraceRayFeature(const float3& start, const float3& dir, float length, CFeature*& feature) { float nearHit = length; GML_RECMUTEX_LOCK(quad); // GuiTraceRayFeature std::vector<int> quads = qf->GetQuadsOnRay(start, dir, length); std::vector<int>::iterator qi; for (qi = quads.begin(); qi != quads.end(); ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); std::list<CFeature*>::const_iterator ui; // NOTE: switch this to custom volumes fully? // (not used for any LOF checks, maybe wasteful) for (ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) { continue; } if (f->noSelect) { continue; } CollisionVolume* cv = f->collisionVolume; const float3& midPosOffset = cv? cv->GetOffsets(): ZeroVector; const float3 dif = (f->midPos + midPosOffset) - start; float closeLength = dif.dot(dir); if (closeLength < 0) continue; if (closeLength > nearHit) continue; float3 closeVect = dif - dir * closeLength; if (closeVect.SqLength() < f->sqRadius) { nearHit = closeLength; feature = f; } } } return nearHit; }
CGameHelper::BuildSquareStatus CGameHelper::TestBuildSquare(const float3& pos, const float buildHeight, const UnitDef* unitdef, const MoveDef* moveDef, CFeature*& feature, int allyteam, bool synced) { if (!pos.IsInMap()) { return BUILDSQUARE_BLOCKED; } BuildSquareStatus ret = BUILDSQUARE_OPEN; const int yardxpos = int(pos.x + 4) / SQUARE_SIZE; const int yardypos = int(pos.z + 4) / SQUARE_SIZE; CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); if (s != NULL) { CFeature* f = dynamic_cast<CFeature*>(s); if (f != NULL) { if ((allyteam < 0) || f->IsInLosForAllyTeam(allyteam)) { if (!f->def->reclaimable) { ret = BUILDSQUARE_BLOCKED; } else { ret = BUILDSQUARE_RECLAIMABLE; feature = f; } } } else if (!dynamic_cast<CUnit*>(s) || (allyteam < 0) || (static_cast<CUnit*>(s)->losStatus[allyteam] & LOS_INLOS)) { if (s->immobile) { ret = BUILDSQUARE_BLOCKED; } else { ret = BUILDSQUARE_OCCUPIED; } } if ((ret == BUILDSQUARE_BLOCKED) || (ret == BUILDSQUARE_OCCUPIED)) { if (CMoveMath::IsNonBlocking(s, moveDef, pos, buildHeight)) { ret = BUILDSQUARE_OPEN; } } if (ret == BUILDSQUARE_BLOCKED) { return ret; } } const float groundHeight = ground->GetHeightReal(pos.x, pos.z, synced); if (!unitdef->floatOnWater || groundHeight > 0.0f) { // if we are capable of floating, only test local // height difference IF terrain is above sea-level const float* orgHeightMap = readmap->GetOriginalHeightMapSynced(); const float* curHeightMap = readmap->GetCornerHeightMapSynced(); #ifdef USE_UNSYNCED_HEIGHTMAP if (!synced) { orgHeightMap = readmap->GetCornerHeightMapUnsynced(); curHeightMap = readmap->GetCornerHeightMapUnsynced(); } #endif const int sqx = pos.x / SQUARE_SIZE; const int sqz = pos.z / SQUARE_SIZE; const float orgHgt = orgHeightMap[sqz * gs->mapxp1 + sqx]; const float curHgt = curHeightMap[sqz * gs->mapxp1 + sqx]; const float difHgt = unitdef->maxHeightDif; if (pos.y > std::max(orgHgt + difHgt, curHgt + difHgt)) { return BUILDSQUARE_BLOCKED; } if (pos.y < std::min(orgHgt - difHgt, curHgt - difHgt)) { return BUILDSQUARE_BLOCKED; } } if (!unitdef->IsAllowedTerrainHeight(moveDef, groundHeight)) ret = BUILDSQUARE_BLOCKED; return ret; }
void DrawQuad(int x, int y) { std::vector<IWorldObjectModelRenderer*>& opaqueModelRenderers = featureDrawer->opaqueModelRenderers; std::vector<IWorldObjectModelRenderer*>& cloakedModelRenderers = featureDrawer->cloakedModelRenderers; const CFeatureDrawer::DrawQuad* dq = &(*drawQuads)[y * drawQuadsX + x]; for (std::set<CFeature*>::const_iterator fi = dq->features.begin(); fi != dq->features.end(); ++fi) { CFeature* f = (*fi); if (f->IsInVoid()) continue; assert(f->def->drawType == DRAWTYPE_MODEL); if (gu->spectatingFullView || f->IsInLosForAllyTeam(gu->myAllyTeam)) { if (drawReflection) { float3 zeroPos; if (f->midPos.y < 0.0f) { zeroPos = f->midPos; } else { const float dif = f->midPos.y - camera->GetPos().y; zeroPos = camera->GetPos() * (f->midPos.y / dif) + f->midPos * (-camera->GetPos().y / dif); } if (ground->GetApproximateHeight(zeroPos.x, zeroPos.z, false) > f->drawRadius) { continue; } } if (drawRefraction) { if (f->pos.y > 0.0f) continue; } const float sqDist = (f->pos - camera->GetPos()).SqLength(); const float farLength = f->sqRadius * unitDrawer->unitDrawDistSqr; #ifdef USE_GML if (statFeatures && (f->reclaimLeft < 1.0f || f->resurrectProgress > 0.0f)) statFeatures->push_back(f); #endif if (sqDist < farLength) { float sqFadeDistE; float sqFadeDistB; if (farLength < sqFadeDistEnd) { sqFadeDistE = farLength; sqFadeDistB = farLength * sqFadeDistBegin / sqFadeDistEnd; } else { sqFadeDistE = sqFadeDistEnd; sqFadeDistB = sqFadeDistBegin; } if (sqDist < sqFadeDistB) { cloakedModelRenderers[MDL_TYPE(f)]->DelFeature(f); if (camera->InView(f->drawMidPos, f->drawRadius)) opaqueModelRenderers[MDL_TYPE(f)]->AddFeature(f); } else if (sqDist < sqFadeDistE) { const float falpha = 1.0f - (sqDist - sqFadeDistB) / (sqFadeDistE - sqFadeDistB); opaqueModelRenderers[MDL_TYPE(f)]->DelFeature(f); if (camera->InView(f->drawMidPos, f->drawRadius)) cloakedModelRenderers[MDL_TYPE(f)]->AddFeature(f, falpha); } } else { if (farFeatures) { farTextureHandler->Queue(f); } } } } }
int CAICallback::HandleCommand(int commandId, void* data) { switch (commandId) { case AIHCQuerySubVersionId: { return 1; // current version of Handle Command interface } break; case AIHCAddMapPointId: { const AIHCAddMapPoint* cmdData = static_cast<AIHCAddMapPoint*>(data); net->Send(CBaseNetProtocol::Get().SendMapDrawPoint(team, (short)cmdData->pos.x, (short)cmdData->pos.z, std::string(cmdData->label), false)); return 1; } break; case AIHCAddMapLineId: { const AIHCAddMapLine* cmdData = static_cast<AIHCAddMapLine*>(data); net->Send(CBaseNetProtocol::Get().SendMapDrawLine(team, (short)cmdData->posfrom.x, (short)cmdData->posfrom.z, (short)cmdData->posto.x, (short)cmdData->posto.z, false)); return 1; } break; case AIHCRemoveMapPointId: { const AIHCRemoveMapPoint* cmdData = static_cast<AIHCRemoveMapPoint*>(data); net->Send(CBaseNetProtocol::Get().SendMapErase(team, (short)cmdData->pos.x, (short)cmdData->pos.z)); return 1; } break; case AIHCSendStartPosId: { const AIHCSendStartPos* cmdData = static_cast<AIHCSendStartPos*>(data); SendStartPos(cmdData->ready, cmdData->pos); return 1; } break; case AIHCGetUnitDefByIdId: { // NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl return 0; } break; case AIHCGetWeaponDefByIdId: { // NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl return 0; } break; case AIHCGetFeatureDefByIdId: { // NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl return 0; } break; case AIHCTraceRayId: { AIHCTraceRay* cmdData = static_cast<AIHCTraceRay*>(data); if (CHECK_UNITID(cmdData->srcUID)) { const CUnit* srcUnit = unitHandler->units[cmdData->srcUID]; if (srcUnit != NULL) { CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; //FIXME add COLLISION_NOFEATURE? const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature); if (hitUnit != NULL) { myAllyTeamId = teamHandler->AllyTeam(team); const bool isUnitVisible = unit_IsInLos(hitUnit); if (isUnitVisible) { cmdData->rayLen = realLen; cmdData->hitUID = hitUnit->id; } } } } return 1; } break; case AIHCFeatureTraceRayId: { AIHCFeatureTraceRay* cmdData = static_cast<AIHCFeatureTraceRay*>(data); if (CHECK_UNITID(cmdData->srcUID)) { const CUnit* srcUnit = unitHandler->units[cmdData->srcUID]; if (srcUnit != NULL) { CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; //FIXME add COLLISION_NOENEMIES || COLLISION_NOFRIENDLIES || COLLISION_NONEUTRALS? const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature); if (hitFeature != NULL) { const bool isFeatureVisible = hitFeature->IsInLosForAllyTeam(teamHandler->AllyTeam(team)); if (isFeatureVisible) { cmdData->rayLen = realLen; cmdData->hitFID = hitFeature->id; } } } } return 1; } break; case AIHCPauseId: { AIHCPause* cmdData = static_cast<AIHCPause*>(data); net->Send(CBaseNetProtocol::Get().SendPause(gu->myPlayerNum, cmdData->enable)); LOG("Skirmish AI controlling team %i paused the game, reason: %s", team, cmdData->reason != NULL ? cmdData->reason : "UNSPECIFIED"); return 1; } break; case AIHCGetDataDirId: { // do nothing // this event will never end up here, as // it is handled in the C layer directly // see Clb_DataDirs_allocatePath in rts/ExternalAI/Interface/SSkirmishAICallback.h return 0; } break; case AIHCDebugDrawId: { AIHCDebugDraw* cmdData = static_cast<AIHCDebugDraw*>(data); switch (cmdData->cmdMode) { case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_GRAPH_POINT: { debugDrawerAI->AddGraphPoint(this->team, cmdData->lineId, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_GRAPH_POINTS: { debugDrawerAI->DelGraphPoints(this->team, cmdData->lineId, cmdData->numPoints); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_POS: { debugDrawerAI->SetGraphPos(this->team, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_SIZE: { debugDrawerAI->SetGraphSize(this->team, cmdData->w, cmdData->h); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_COLOR: { debugDrawerAI->SetGraphLineColor(this->team, cmdData->lineId, cmdData->color); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_LABEL: { debugDrawerAI->SetGraphLineLabel(this->team, cmdData->lineId, cmdData->label); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_OVERLAY_TEXTURE: { cmdData->texHandle = debugDrawerAI->AddOverlayTexture( this->team, cmdData->texData, int(cmdData->w), // interpret as absolute width int(cmdData->h) // interpret as absolute height ); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_UPDATE_OVERLAY_TEXTURE: { debugDrawerAI->UpdateOverlayTexture( this->team, cmdData->texHandle, cmdData->texData, int(cmdData->x), // interpret as absolute pixel col int(cmdData->y), // interpret as absolute pixel row int(cmdData->w), // interpret as absolute width int(cmdData->h) // interpret as absolute height ); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_OVERLAY_TEXTURE: { debugDrawerAI->DelOverlayTexture(this->team, cmdData->texHandle); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_POS: { debugDrawerAI->SetOverlayTexturePos(this->team, cmdData->texHandle, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_SIZE: { debugDrawerAI->SetOverlayTextureSize(this->team, cmdData->texHandle, cmdData->w, cmdData->h); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_LABEL: { debugDrawerAI->SetOverlayTextureLabel(this->team, cmdData->texHandle, cmdData->label); } break; default: { } break; } return 1; } break; default: { return 0; } } }
float GuiTraceRay( const float3& start, const float3& dir, float length, bool useRadar, const CUnit* exclude, CUnit*& hitUnit, CFeature*& hitFeature, bool groundOnly ) { hitUnit = NULL; hitFeature = NULL; if (dir == ZeroVector) return -1.0f; // ground intersection const float origlength = length; const float groundLength = ground->LineGroundCol(start, start + dir * origlength, false); float length2 = length; if (groundOnly) return groundLength; GML_RECMUTEX_LOCK(quad); // GuiTraceRay int* begQuad = NULL; int* endQuad = NULL; bool hitFactory = false; CollisionQuery cq; qf->GetQuadsOnRay(start, dir, length, begQuad, endQuad); std::list<CUnit*>::const_iterator ui; std::list<CFeature*>::const_iterator fi; for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = qf->GetQuad(*quadPtr); // Unit Intersection for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* unit = *ui; if (unit == exclude) continue; if ((unit->allyteam == gu->myAllyTeam) || gu->spectatingFullView || (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)) || (useRadar && radarhandler->InRadar(unit, gu->myAllyTeam))) { CollisionVolume cv(unit->collisionVolume); if (unit->isIcon) { // for iconified units, just pretend the collision // volume is a sphere of radius <unit->IconRadius> // randomize it (by scaling up to a factor of 4) so // unit size cannot be determined by hovering mouse // over radar blips cv.Init(unit->iconRadius * (1.0f + (gu->usRandFloat() * 3.0f))); } if (CCollisionHandler::MouseHit(unit, start, start + dir * origlength, &cv, &cq)) { // get the distance to the ray-volume ingress point const float3& ingressPos = (cq.b0)? cq.p0 : cq.p1; const float3& egressPos = (cq.b1)? cq.p1 : cq.p0; const float ingressDist = (ingressPos - start).dot(dir); // same as (intPos - start).Length() const float egressDist = ( egressPos - start).dot(dir); // same as (intPos2 - start).Length() const bool isFactory = unit->unitDef->IsFactoryUnit(); // give units in a factory higher priority than the factory itself if (!hitUnit || (isFactory && ((hitFactory && ingressDist < length) || (!hitFactory && egressDist < length))) || (!isFactory && ((hitFactory && ingressDist < length2) || (!hitFactory && ingressDist < length)))) { hitFactory = isFactory; length = ingressDist; length2 = egressDist; hitUnit = unit; hitFeature = NULL; } } } } // Feature Intersection // NOTE: switch this to custom volumes fully? // (not used for any LOF checks, maybe wasteful) for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) { CFeature* f = *fi; // FIXME add useradar? if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) continue; if (f->noSelect) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * origlength, &cq, true)) { const float3& ingressPos = (cq.b0)? cq.p0 : cq.p1; const float ingressDist = (ingressPos - start).dot(dir); // same as (intPos - start).Length() // we want the closest feature (intersection point) on the ray // give features in a factory (?) higher priority than the factory itself if (!hitUnit || ((hitFactory && ingressDist < length2) || (!hitFactory && ingressDist < length))) { hitFactory = false; length = ingressDist; hitFeature = f; hitUnit = NULL; } } } } if ((groundLength > 0.0f) && ((groundLength + 200.0f) < length)) { length = groundLength; hitUnit = NULL; hitFeature = NULL; } return length; }
float GuiTraceRay(const float3 &start, const float3 &dir, float length, bool useRadar, const CUnit* exclude, CUnit*& hitUnit, CFeature*& hitFeature) { hitUnit = NULL; hitFeature = NULL; if (dir == ZeroVector) { return -1.0f; } bool hover_factory = false; CollisionQuery cq; { GML_RECMUTEX_LOCK(quad); //! GuiTraceRay const vector<int> &quads = qf->GetQuadsOnRay(start, dir, length); std::list<CUnit*>::const_iterator ui; std::list<CFeature*>::const_iterator fi; for (vector<int>::const_iterator qi = quads.begin(); qi != quads.end(); ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); //! Unit Intersection for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* unit = *ui; if (unit == exclude) { continue; } if ((unit->allyteam == gu->myAllyTeam) || gu->spectatingFullView || (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)) || (useRadar && radarhandler->InRadar(unit, gu->myAllyTeam))) { CollisionVolume cv(unit->collisionVolume); if (unit->isIcon) { //! for iconified units, just pretend the collision //! volume is a sphere of radius <unit->IconRadius> cv.Init(unit->iconRadius); } if (CCollisionHandler::MouseHit(unit, start, start + dir * length, &cv, &cq)) { //! get the distance to the ray-volume ingress point const float3& intPos = (cq.b0)? cq.p0 : cq.p1; const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length() const bool isfactory = dynamic_cast<CFactory*>(unit); if (len < length) { if (!isfactory || !hitUnit || hover_factory) { hover_factory = isfactory; length = len; hitUnit = unit; hitFeature = NULL; } } else if (!isfactory && hover_factory) { //FIXME still check if the unit is BEHIND (and not IN) the factory! //! give an unit in a factory a higher priority than the factory itself hover_factory = isfactory; length = len; hitUnit = unit; hitFeature = NULL; } } } } //! Feature Intersection // NOTE: switch this to custom volumes fully? // (not used for any LOF checks, maybe wasteful) for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) { CFeature* f = *fi; if (!f->collisionVolume) { continue; } //FIXME add useradar? if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) { continue; } if (f->noSelect) { continue; } if (CCollisionHandler::Intersect(f, start, start + dir * length, &cq)) { const float3& intPos = (cq.b0)? cq.p0 : cq.p1; const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length() //! we want the closest feature (intersection point) on the ray if (len < length) { hover_factory = false; length = len; hitFeature = f; hitUnit = NULL; } else if (hover_factory) { //FIXME still check if the unit is BEHIND (and not IN) the factory! //! give features in a factory a higher priority than the factory itself hover_factory = false; length = len; hitFeature = f; hitUnit = NULL; } } } } } //! ground intersection float groundLen = ground->LineGroundCol(start, start + dir * length); if (groundLen > 0.0f) { if (groundLen+200.0f < length) { length = groundLen; hitUnit = NULL; hitFeature = NULL; } } return length; }
void CFeatureQuadDrawer::DrawQuad(int x, int y) { CFeatureDrawer::DrawQuad* dq = &(*drawQuads)[y * drawQuadsX + x]; for (set<CFeature*>::iterator fi = dq->features.begin(); fi != dq->features.end(); ++fi) { CFeature* f = (*fi); const FeatureDef* def = f->def; if (def->drawType == DRAWTYPE_MODEL && (gu->spectatingFullView || f->IsInLosForAllyTeam(gu->myAllyTeam))) { if (drawReflection) { float3 zeroPos; if (f->midPos.y < 0) { zeroPos = f->midPos; } else { float dif = f->midPos.y - camera->pos.y; zeroPos = camera->pos * (f->midPos.y / dif) + f->midPos * (-camera->pos.y / dif); } if (ground->GetApproximateHeight(zeroPos.x, zeroPos.z) > f->radius) { continue; } } if (drawRefraction) { if (f->pos.y > 0) continue; } float sqDist = (f->pos - camera->pos).SqLength2D(); float farLength = f->sqRadius * unitDrawDist * unitDrawDist; if(statFeatures && (f->reclaimLeft < 1.0f || f->resurrectProgress > 0.0f)) statFeatures->push_back(f); if (sqDist < farLength) { float sqFadeDistE; float sqFadeDistB; if(farLength < sqFadeDistEnd) { sqFadeDistE = farLength; sqFadeDistB = farLength * 0.75f * 0.75f; } else { sqFadeDistE = sqFadeDistEnd; sqFadeDistB = sqFadeDistBegin; } if(sqDist < sqFadeDistB) { f->tempalpha = 0.99f; if (f->model->type == MODELTYPE_3DO) { unitDrawer->DrawFeatureStatic(f); } else { unitDrawer->QueS3ODraw(f, f->model->textureType); } } else if(sqDist < sqFadeDistE) { f->tempalpha = 1.0f - (sqDist - sqFadeDistB) / (sqFadeDistE - sqFadeDistB); if (f->model->type == MODELTYPE_3DO) { featureDrawer->fadeFeatures.insert(f); } else { featureDrawer->fadeFeaturesS3O.insert(f); } } } else { if (farFeatures) farFeatures->push_back(f); } } } }
float GuiTraceRay( const float3& start, const float3& dir, const float length, const CUnit* exclude, CUnit*& hitUnit, CFeature*& hitFeature, bool useRadar, bool groundOnly, bool ignoreWater ) { hitUnit = NULL; hitFeature = NULL; if (dir == ZeroVector) return -1.0f; // ground intersection const float guiRayLength = length; const float groundRayLength = ground->LineGroundCol(start, start + dir * guiRayLength, false); const float waterRayLength = math::floor(math::fabs(start.y / std::min(dir.y, -0.00001f))); float minRayLength = groundRayLength; float minIngressDist = length; float minEgressDist = length; if (!ignoreWater) minRayLength = std::min(groundRayLength, waterRayLength); if (groundOnly) return minRayLength; GML_RECMUTEX_LOCK(quad); // GuiTraceRay int* begQuad = NULL; int* endQuad = NULL; bool hitFactory = false; CollisionQuery cq; quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad); std::list<CUnit*>::const_iterator ui; std::list<CFeature*>::const_iterator fi; for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); // Unit Intersection for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* unit = *ui; const bool unitIsEnemy = !teamHandler->Ally(unit->allyteam, gu->myAllyTeam); const bool unitOnRadar = (useRadar && radarhandler->InRadar(unit, gu->myAllyTeam)); const bool unitInSight = (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)); const bool unitVisible = !unitIsEnemy || unitOnRadar || unitInSight || gu->spectatingFullView; if (unit == exclude) continue; if (!unitVisible) continue; CollisionVolume cv(unit->collisionVolume); if (unit->isIcon || (!unitInSight && unitOnRadar && unitIsEnemy)) { // for iconified units, just pretend the collision // volume is a sphere of radius <unit->IconRadius> // (count radar blips as such too) cv.InitSphere(unit->iconRadius); } if (CCollisionHandler::MouseHit(unit, start, start + dir * guiRayLength, &cv, &cq)) { // get the distance to the ray-volume ingress point // (not likely to generate inside-hit special cases) const float ingressDist = cq.GetIngressPosDist(start, dir); const float egressDist = cq.GetEgressPosDist(start, dir); const bool isFactory = unit->unitDef->IsFactoryUnit(); const bool factoryHitBeforeUnit = ((hitFactory && ingressDist < minIngressDist) || (!hitFactory && egressDist < minIngressDist)); const bool unitHitInsideFactory = ((hitFactory && ingressDist < minEgressDist) || (!hitFactory && ingressDist < minIngressDist)); // give units in a factory higher priority than the factory itself if (hitUnit == NULL || (isFactory && factoryHitBeforeUnit) || (!isFactory && unitHitInsideFactory)) { hitFactory = isFactory; minIngressDist = ingressDist; minEgressDist = egressDist; hitUnit = unit; hitFeature = NULL; } } } // Feature Intersection // NOTE: switch this to custom volumes fully? // (not used for any LOF checks, maybe wasteful) for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) { CFeature* f = *fi; // FIXME add useradar? if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) continue; if (f->noSelect) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * guiRayLength, &cq, true)) { const float hitDist = cq.GetHitPosDist(start, dir); const bool factoryHitBeforeUnit = ( hitFactory && hitDist < minEgressDist); const bool unitHitInsideFactory = (!hitFactory && hitDist < minIngressDist); // we want the closest feature (intersection point) on the ray // give features in a factory (?) higher priority than the factory itself if (hitUnit == NULL || factoryHitBeforeUnit || unitHitInsideFactory) { hitFactory = false; minIngressDist = hitDist; hitFeature = f; hitUnit = NULL; } } } } if ((minRayLength > 0.0f) && ((minRayLength + 200.0f) < minIngressDist)) { minIngressDist = minRayLength; hitUnit = NULL; hitFeature = NULL; } return minIngressDist; }
int CAICallback::HandleCommand(int commandId, void* data) { switch (commandId) { case AIHCQuerySubVersionId: { return 1; // current version of Handle Command interface } break; case AIHCAddMapPointId: { const AIHCAddMapPoint* cmdData = static_cast<AIHCAddMapPoint*>(data); /* TODO: gu->myPlayerNum makes the command to look like as it comes from the local player, "team" should be used (but needs some major changes in other engine parts) */ clientNet->Send(CBaseNetProtocol::Get().SendMapDrawPoint(gu->myPlayerNum, (short)cmdData->pos.x, (short)cmdData->pos.z, std::string(cmdData->label), false)); return 1; } break; case AIHCAddMapLineId: { const AIHCAddMapLine* cmdData = static_cast<AIHCAddMapLine*>(data); // see TODO above clientNet->Send(CBaseNetProtocol::Get().SendMapDrawLine(gu->myPlayerNum, (short)cmdData->posfrom.x, (short)cmdData->posfrom.z, (short)cmdData->posto.x, (short)cmdData->posto.z, false)); return 1; } break; case AIHCRemoveMapPointId: { const AIHCRemoveMapPoint* cmdData = static_cast<AIHCRemoveMapPoint*>(data); // see TODO above clientNet->Send(CBaseNetProtocol::Get().SendMapErase(gu->myPlayerNum, (short)cmdData->pos.x, (short)cmdData->pos.z)); return 1; } break; case AIHCSendStartPosId: case AIHCGetUnitDefByIdId: case AIHCGetWeaponDefByIdId: case AIHCGetFeatureDefByIdId: case AIHCGetDataDirId: { // NOTE: these commands should never arrive, handled in SSkirmishAICallbackImpl assert(false); return 0; } break; case AIHCTraceRayId: { AIHCTraceRay* cmdData = static_cast<AIHCTraceRay*>(data); if (CHECK_UNITID(cmdData->srcUID)) { const CUnit* srcUnit = unitHandler->GetUnit(cmdData->srcUID); if (srcUnit != nullptr) { CUnit* hitUnit = nullptr; CFeature* hitFeature = nullptr; //FIXME add COLLISION_NOFEATURE? const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature); if (hitUnit != nullptr) { myAllyTeamId = teamHandler->AllyTeam(team); if (unit_IsInLos(hitUnit)) { cmdData->rayLen = realLen; cmdData->hitUID = hitUnit->id; } } } } return 1; } break; case AIHCFeatureTraceRayId: { AIHCFeatureTraceRay* cmdData = static_cast<AIHCFeatureTraceRay*>(data); if (CHECK_UNITID(cmdData->srcUID)) { const CUnit* srcUnit = unitHandler->GetUnit(cmdData->srcUID); if (srcUnit != nullptr) { CUnit* hitUnit = nullptr; CFeature* hitFeature = nullptr; //FIXME add COLLISION_NOENEMIES || COLLISION_NOFRIENDLIES || COLLISION_NONEUTRALS? const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature); if (hitFeature != nullptr) { if (hitFeature->IsInLosForAllyTeam(teamHandler->AllyTeam(team))) { cmdData->rayLen = realLen; cmdData->hitFID = hitFeature->id; } } } } return 1; } break; case AIHCPauseId: { AIHCPause* cmdData = static_cast<AIHCPause*>(data); clientNet->Send(CBaseNetProtocol::Get().SendPause(gu->myPlayerNum, cmdData->enable)); LOG("Skirmish AI controlling team %i paused the game, reason: %s", team, cmdData->reason != NULL ? cmdData->reason : "UNSPECIFIED"); return 1; } break; case AIHCDebugDrawId: { AIHCDebugDraw* cmdData = static_cast<AIHCDebugDraw*>(data); switch (cmdData->cmdMode) { case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_GRAPH_POINT: { debugDrawerAI->AddGraphPoint(this->team, cmdData->lineId, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_GRAPH_POINTS: { debugDrawerAI->DelGraphPoints(this->team, cmdData->lineId, cmdData->numPoints); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_POS: { debugDrawerAI->SetGraphPos(this->team, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_SIZE: { debugDrawerAI->SetGraphSize(this->team, cmdData->w, cmdData->h); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_COLOR: { debugDrawerAI->SetGraphLineColor(this->team, cmdData->lineId, cmdData->color); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_LABEL: { debugDrawerAI->SetGraphLineLabel(this->team, cmdData->lineId, cmdData->label); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_OVERLAY_TEXTURE: { cmdData->texHandle = debugDrawerAI->AddOverlayTexture( this->team, cmdData->texData, int(cmdData->w), // interpret as absolute width int(cmdData->h) // interpret as absolute height ); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_UPDATE_OVERLAY_TEXTURE: { debugDrawerAI->UpdateOverlayTexture( this->team, cmdData->texHandle, cmdData->texData, int(cmdData->x), // interpret as absolute pixel col int(cmdData->y), // interpret as absolute pixel row int(cmdData->w), // interpret as absolute width int(cmdData->h) // interpret as absolute height ); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_OVERLAY_TEXTURE: { debugDrawerAI->DelOverlayTexture(this->team, cmdData->texHandle); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_POS: { debugDrawerAI->SetOverlayTexturePos(this->team, cmdData->texHandle, cmdData->x, cmdData->y); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_SIZE: { debugDrawerAI->SetOverlayTextureSize(this->team, cmdData->texHandle, cmdData->w, cmdData->h); } break; case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_LABEL: { debugDrawerAI->SetOverlayTextureLabel(this->team, cmdData->texHandle, cmdData->label); } break; default: { } break; } return 1; } break; default: { return 0; } } }