bool SW_SonarPulse::Activate(SuperClass* pThis, const CellStruct &Coords, bool IsPlayer) { SuperWeaponTypeClass *pType = pThis->Type; SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(pType); if(!pData) { return false; } auto Detect = [&](TechnoClass* pTechno) -> bool { // is this thing affected at all? if(!pData->IsHouseAffected(pThis->Owner, pTechno->Owner)) { return true; } if(!pData->IsTechnoAffected(pTechno)) { return true; } // actually detect this if(pTechno->CloakState) { pTechno->Uncloak(1); pTechno->NeedsRedraw = 1; TechnoExt::ExtData *pExt = TechnoExt::ExtMap.Find(pTechno); if(pTechno) { pExt->CloakSkipTimer.Start(pData->Sonar_Delay); } } return true; }; auto range = GetRange(pData); if(range.WidthOrRange < 0) { // decloak everything regardless of ranges for(int i=0; i<TechnoClass::Array->Count; ++i) { Detect(TechnoClass::Array->GetItem(i)); } } else { // decloak everything in range Helpers::Alex::DistinctCollector<TechnoClass*> items; Helpers::Alex::for_each_in_rect_or_range<TechnoClass>(Coords, range.WidthOrRange, range.Height, std::ref(items)); items.for_each(Detect); // radar event only if this isn't full map sonar if(pData->SW_RadarEvent) { RadarEventClass::Create(RadarEventType::SuperweaponActivated, Coords); } } return true; }
bool SW_ChronoWarp::Activate(SuperClass* pThis, const CellStruct &Coords, bool IsPlayer) { // get the previous super weapon SuperClass* pSource = nullptr; if(HouseExt::ExtData *pExt = HouseExt::ExtMap.Find(pThis->Owner)) { pSource = pThis->Owner->Supers.GetItemOrDefault(pExt->SWLastIndex); } // use source super weapon properties if(pSource && (pSource->Type->Type == SuperWeaponType::ChronoSphere)) { if(SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(pSource->Type)) { Debug::Log("[ChronoWarp::Launch] Launching %s with %s as source.\n", pThis->Type->ID, pSource->Type->ID); // add radar events for source and target if(pData->SW_RadarEvent) { RadarEventClass::Create(RadarEventType::SuperweaponActivated, pSource->ChronoMapCoords); RadarEventClass::Create(RadarEventType::SuperweaponActivated, Coords); } // cell and coords calculations CellClass *pCellSource = MapClass::Instance->GetCellAt(pSource->ChronoMapCoords); CellClass *pCellTarget = MapClass::Instance->GetCellAt(Coords); CoordStruct coordsSource = pCellSource->GetCoordsWithBridge(); coordsSource.Z += pData->SW_AnimHeight; CoordStruct coordsTarget = pCellTarget->GetCoordsWithBridge(); coordsTarget.Z += pData->SW_AnimHeight; // Update animations SWTypeExt::ClearChronoAnim(pThis); if(auto pAnimType = pData->Chronosphere_BlastSrc.Get(RulesClass::Instance->ChronoBlast)) { GameCreate<AnimClass>(pAnimType, coordsSource); } if(auto pAnimType = pData->Chronosphere_BlastDest.Get(RulesClass::Instance->ChronoBlastDest)) { GameCreate<AnimClass>(pAnimType, coordsTarget); } DynamicVectorClass<ChronoWarpStateMachine::ChronoWarpContainer> RegisteredBuildings; auto Chronoport = [&](TechnoClass* pTechno) -> bool { // is this thing affected at all? if(!pData->IsHouseAffected(pThis->Owner, pTechno->Owner)) { return true; } if(!pData->IsTechnoAffected(pTechno)) { return true; } TechnoTypeClass *pType = pTechno->GetTechnoType(); TechnoTypeExt::ExtData *pExt = TechnoTypeExt::ExtMap.Find(pType); // can this techno be chronoshifted? if(!pExt->Chronoshift_Allow) { return true; } // differentiate between buildings and vehicle-type buildings bool IsVehicle = false; if(BuildingClass* pBld = specific_cast<BuildingClass*>(pTechno)) { // always ignore bridge repair huts if(pBld->Type->BridgeRepairHut) { return true; } // use "smart" detection of vehicular building types? if(pData->Chronosphere_ReconsiderBuildings) { IsVehicle = pExt->Chronoshift_IsVehicle; } // always let undeployers pass if all undeployers are affected if(!pData->Chronosphere_AffectUndeployable || !pBld->Type->UndeploysInto) { // we don't handle buildings and this is a real one if(!IsVehicle && !pData->Chronosphere_AffectBuildings) { return true; } // this is a vehicle in disguise and we don't handle them if(IsVehicle && !(pData->SW_AffectsTarget & SuperWeaponTarget::Unit)) { return true; } } else { // force vehicle placement rules IsVehicle = true; } } // some quick exclusion criteria if(pTechno->IsImmobilized || pTechno->IsInAir() || pTechno->IsBeingWarpedOut() || pTechno->IsWarpingIn()) { return true; } // unwarpable unit if(!pType->Warpable && !pData->Chronosphere_AffectUnwarpable) { return true; } // iron curtained units if(pTechno->IsIronCurtained() && !pData->Chronosphere_AffectIronCurtain) { return true; } // if this is a newly produced unit that still is in its // weapons factory, this skips it. if(pTechno->WhatAmI() == AbstractType::Unit) { TechnoClass* pLink = pTechno->GetNthLink(0); if(pLink) { if(BuildingClass* pLinkBld = specific_cast<BuildingClass*>(pLink)) { if(pLinkBld->Type->WeaponsFactory) { if(MapClass::Instance->GetCellAt(pTechno->Location)->GetBuilding() == pLinkBld) { return true; } } } } } // behind this point, the units are affected. // organics are destroyed as long as they aren't teleporters if(pType->Organic && pData->Chronosphere_KillOrganic) { if(!pType->Teleporter || pData->Chronosphere_KillTeleporters) { int strength = pType->Strength; pTechno->ReceiveDamage(&strength, 0, RulesClass::Instance->C4Warhead, nullptr, true, false, pSource->Owner); return true; } } // remove squids. terror drones stay inside. if(FootClass *pFoot = generic_cast<FootClass*>(pTechno)) { if(FootClass *pSquid = pFoot->ParasiteEatingMe) { if(pType->Naval) { if(ParasiteClass *pSquidParasite = pSquid->ParasiteImUsing) { pSquidParasite->SuppressionTimer.Start(500); pSquidParasite->ExitUnit(); } } } } // disconnect bunker and contents if(pTechno->BunkerLinkedItem) { if(BuildingClass *pBunkerLink = specific_cast<BuildingClass*>(pTechno->BunkerLinkedItem)) { // unit will be destroyed or chronoported. in every case the bunker will be empty. pBunkerLink->ClearBunker(); } else if(BuildingClass *pBunker = specific_cast<BuildingClass*>(pTechno)) { // the bunker leaves... pBunker->UnloadBunker(); pBunker->EmptyBunker(); } } // building specific preparations if(BuildingClass* pBld = specific_cast<BuildingClass*>(pTechno)) { // tell all linked units to get off pBld->SendToEachLink(rc_0D); pBld->SendToEachLink(rc_Exit); // destroy the building light source if(pBld->LightSource) { pBld->LightSource->Deactivate(); GameDelete(pBld->LightSource); pBld->LightSource = nullptr; } // shut down cloak generation if(pBld->Type->CloakGenerator && pBld->CloakRadius) { pBld->HasCloakingData = -1; pBld->NeedsRedraw = true; pBld->CloakRadius = 1; pBld->UpdateCloak(); } } // get the cells and coordinates CoordStruct coordsUnitSource = pTechno->GetCoords(); CoordStruct coordsUnitTarget = coordsUnitSource; CellStruct cellUnitTarget = pTechno->GetCell()->MapCoords - pSource->ChronoMapCoords + Coords; CellClass* pCellUnitTarget = MapClass::Instance->GetCellAt(cellUnitTarget); // move the unit to the new position coordsUnitTarget.X += (Coords.X - pSource->ChronoMapCoords.X) * 256; coordsUnitTarget.Y += (Coords.Y - pSource->ChronoMapCoords.Y) * 256; coordsUnitTarget = pCellUnitTarget->FixHeight(coordsUnitTarget); if(FootClass *pFoot = generic_cast<FootClass*>(pTechno)) { // clean up the unit's current cell pFoot->Locomotor->Mark_All_Occupation_Bits(0); pFoot->Locomotor->Force_Track(-1, coordsUnitSource); pFoot->MarkAllOccupationBits(coordsUnitSource); pFoot->FrozenStill = true; // piggyback the original locomotor onto a new teleport locomotor and // use that for the next move order. LocomotionClass::ChangeLocomotorTo(pFoot, LocomotionClass::CLSIDs::Teleport); // order unit to move to target location pFoot->IsImmobilized = true; pFoot->ChronoDestCoords = coordsUnitTarget; pFoot->SendToEachLink(rc_Exit); pFoot->ChronoWarpedByHouse = pThis->Owner; pFoot->SetDestination(pCellUnitTarget, true); } else if (BuildingClass *pBld = specific_cast<BuildingClass*>(pTechno)) { // begin the building chronoshift pBld->BecomeUntargetable(); for(int i = 0; i<BulletClass::Array->Count; ++i) { BulletClass* pBullet = BulletClass::Array->GetItem(i); if(pBullet->Target == pBld) { pBullet->LoseTarget(); } } // the buidling counts as warped until it reappears pBld->BeingWarpedOut = true; pBld->Owner->RecheckTechTree = true; pBld->Owner->RecheckPower = true; pBld->DisableTemporal(); pBld->UpdatePlacement(PlacementType::Redraw); BuildingExt::ExtData* pBldExt = BuildingExt::ExtMap.Find(pBld); pBldExt->AboutToChronoshift = true; // register for chronoshift ChronoWarpStateMachine::ChronoWarpContainer Container(pBld, cellUnitTarget, pBld->Location, IsVehicle); RegisteredBuildings.AddItem(Container); } return true; }; // collect every techno in this range only once. apply the Chronosphere. auto range = pData->GetRange(); Helpers::Alex::DistinctCollector<TechnoClass*> items; Helpers::Alex::for_each_in_rect_or_range<TechnoClass>(pSource->ChronoMapCoords, range.WidthOrRange, range.Height, std::ref(items)); items.for_each(Chronoport); if(RegisteredBuildings.Count) { this->newStateMachine(RulesClass::Instance->ChronoDelay + 1, Coords, pSource, this, &RegisteredBuildings); } return true; } } else { // idiots at work. Debug::Log("ChronoWarp typed super weapon triggered as standalone. Use ChronoSphere instead.\n"); } return false; }