void HouseExt::ExtData::SetFirestormState(bool Active) { HouseClass *pHouse = this->AttachedToObject; HouseExt::ExtData* pData = HouseExt::ExtMap.Find(pHouse); if(pData->FirewallActive == Active) { return; } pData->FirewallActive = Active; UpdateAnyFirestormActive(); DynamicVectorClass<CellStruct> AffectedCoords; for(int i = 0; i < pHouse->Buildings.Count; ++i) { BuildingClass *B = pHouse->Buildings[i]; BuildingTypeExt::ExtData *pBuildTypeData = BuildingTypeExt::ExtMap.Find(B->Type); if(pBuildTypeData->Firewall_Is) { BuildingExt::ExtData * pBldData = BuildingExt::ExtMap.Find(B); pBldData->UpdateFirewall(); CellStruct temp = B->GetMapCoords(); AffectedCoords.AddItem(temp); } } MapClass::Instance->Update_Pathfinding_1(); MapClass::Instance->Update_Pathfinding_2(&AffectedCoords); };
void HouseExt::ExtData::SetFirestormState(bool const active) { auto const pHouse = this->OwnerObject(); auto const pData = HouseExt::ExtMap.Find(pHouse); if(pData->FirewallActive == active) { return; } pData->FirewallActive = active; UpdateAnyFirestormActive(active); DynamicVectorClass<CellStruct> AffectedCoords; for(auto const& pBld : pHouse->Buildings) { auto const pTypeData = BuildingTypeExt::ExtMap.Find(pBld->Type); if(pTypeData->Firewall_Is) { auto const pExt = BuildingExt::ExtMap.Find(pBld); pExt->UpdateFirewall(); auto const temp = pBld->GetMapCoords(); AffectedCoords.AddItem(temp); } } MapClass::Instance->Update_Pathfinding_1(); MapClass::Instance->Update_Pathfinding_2(AffectedCoords); };
static void ParseList(DynamicVectorClass<T> &List, CCINIClass * pINI, const char *section, const char *key) { if(pINI->ReadString(section, key, Ares::readDefval, Ares::readBuffer)) { List.Clear(); char* context = nullptr; for(char *cur = strtok_s(Ares::readBuffer, Ares::readDelims, &context); cur; cur = strtok_s(nullptr, Ares::readDelims, &context)) { T buffer = T(); if(Parser<T>::TryParse(cur, &buffer)) { List.AddItem(buffer); } else if(!std::is_pointer<T>() || !INIClass::IsBlank(cur)) { Debug::INIParseFailed(section, key, cur); } } } };
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; }
void ChronoWarpStateMachine::Update() { int passed = this->TimePassed(); if(passed == 1) { // redraw all buildings for(int i=0; i<this->Buildings.Count; ++i) { ChronoWarpContainer& Container = this->Buildings.Items[i]; if(Container.pBld) { Container.pBld->UpdatePlacement(PlacementType::Redraw); } } } else if(passed == this->Duration - 1) { // copy the array so items can't get invalidated DynamicVectorClass<ChronoWarpContainer> buildings; for(int i=0; i<this->Buildings.Count; ++i) { buildings.AddItem(this->Buildings.GetItem(i)); } this->Buildings.Clear(); // remove all buildings from the map at once for(int i=0; i<buildings.Count; ++i) { ChronoWarpContainer& Container = buildings.Items[i]; Container.pBld->Remove(); Container.pBld->ActuallyPlacedOnMap = false; } // bring back all buildings for(int i=0; i<buildings.Count; ++i) { ChronoWarpContainer& Container = buildings.Items[i]; if(BuildingClass* pBld = Container.pBld) { if(!pBld->TemporalTargetingMe) { // use some logic to place this unit on some other // cell if the target cell is occupied. this emulates // the behavior of other units. bool success = false; int count = CellSpread::NumCells(10); int idx = 0; do { CellStruct cellNew = CellSpread::GetCell(idx) + Container.target; CellClass* pNewCell = MapClass::Instance->GetCellAt(cellNew); CoordStruct coordsNew = pNewCell->GetCoordsWithBridge(); if(pBld->Type->CanCreateHere(&cellNew, 0)) { if(pBld->Put(coordsNew, Direction::North)) { success = true; break; } } ++idx; } while(Container.isVehicle && (idx<count)); if(!success) { // put it back where it was ++Unsorted::IKnowWhatImDoing; pBld->Put(Container.origin, Direction::North); pBld->Place(false); --Unsorted::IKnowWhatImDoing; } // chronoshift ends pBld->BeingWarpedOut = false; pBld->Owner->RecheckPower = true; pBld->Owner->RecheckTechTree = true; pBld->EnableTemporal(); pBld->UpdatePlacement(PlacementType::Redraw); BuildingExt::ExtData* pBldExt = BuildingExt::ExtMap.Find(pBld); pBldExt->AboutToChronoshift = false; if(!success) { if(SWTypeExt::ExtData *pExt = SWTypeExt::ExtMap.Find(this->Super->Type)) { // destroy (buildings only if they are supposed to) if(Container.isVehicle || pExt->Chronosphere_BlowUnplaceable) { int damage = pBld->Type->Strength; pBld->ReceiveDamage(&damage, 0, RulesClass::Instance->C4Warhead, nullptr, true, true, this->Super->Owner); } } } } } } } else if(passed == this->Duration) { Super->Owner->RecheckPower = true; Super->Owner->RecheckTechTree = true; Super->Owner->RecheckRadar = true; } }
/* Every house can have several planes defined. If a plane is not defined by a house, this falls back to the side's planes defined for this SW. If that fails also it falls back to this SW's default paradrop. If that also fails, the paradrop defined by the house is used. \param pHouse The owner of this super weapon. \param pCell The paradrop target cell. \author AlexB \date 2010-07-19 */ bool SW_ParaDrop::SendParadrop(SuperClass* pThis, CellClass* pCell) { // sanity if(!pThis || !pCell) { return false; } SuperWeaponTypeClass *pSW = pThis->Type; SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(pSW); HouseClass *pHouse = pThis->Owner; // these are fallback values if the SW doesn't define them AircraftTypeClass* pFallbackPlane = NULL; TypeList<TechnoTypeClass*> *pFallbackTypes = NULL; TypeList<int> *pFallbackNum = NULL; // get the paradrop list without creating a new value auto GetParadropPlanes = [pData](AbstractTypeClass* pKey) -> DynamicVectorClass<ParadropPlane*>* { if(pData->ParaDrop.find(pKey) == pData->ParaDrop.end()) { return NULL; } return &pData->ParaDrop[pKey]; }; // use paradrop lists from house, side and default DynamicVectorClass<ParadropPlane*>* drops[3]; drops[0] = GetParadropPlanes(pHouse->Type); drops[1] = GetParadropPlanes(SideClass::Array->GetItem(pHouse->Type->SideIndex)); drops[2] = GetParadropPlanes(NULL); // how many planes shall we launch? int count = 0; for(int i=0; i<3; ++i) { if(drops[i]) { count = drops[i]->Count; break; } } // assemble each plane and its contents for(int i=0; i<count; ++i) { // i = index of plane TypeList<TechnoTypeClass*> *pParaDrop = NULL; TypeList<int> *pParaDropNum = NULL; AircraftTypeClass* pParaDropPlane = NULL; // try the planes in order of precedence: // * country, explicit plane // * side, explicit plane // * default, explict plane // * country, default plane // * side, default plane // * default, default plane // * fill gaps with data from house/side/rules for(int j=1; j>=0; --j) { // factor 1 or 0: "plane * j" => "plane" or "0" (default) for(int k=0; k<3; ++k) { // index in the "drops" array // only do something if there is data missing if(!(pParaDrop && pParaDropNum && pParaDropPlane)) { // get the country/side-specific plane list DynamicVectorClass<ParadropPlane*> *planes = drops[k]; if(!planes) { continue; } // get the plane at specified index int index = i * j; if(planes->ValidIndex(index)) { if(ParadropPlane* pPlane = planes->GetItem(index)) { // get the contents, if not already set if(!pParaDrop || !pParaDropNum) { if((pPlane->pTypes.Count != 0) && (pPlane->pNum.Count != 0)) { pParaDrop = &pPlane->pTypes; pParaDropNum = &pPlane->pNum; } } // get the airplane, if it isn't set already if(!pParaDropPlane) { if(AircraftTypeClass* pTAircraft = pPlane->pAircraft) { pParaDropPlane = pTAircraft; } } } } } } } // fallback for types and nums if(!pParaDrop || !pParaDropNum) { if(!pFallbackTypes || !pFallbackNum) { if(HouseTypeExt::ExtData *pExt = HouseTypeExt::ExtMap.Find(pHouse->Type)) { pExt->GetParadropContent(&pFallbackTypes, &pFallbackNum); } } pParaDrop = pFallbackTypes; pParaDropNum = pFallbackNum; } // house fallback for the plane if(!pParaDropPlane) { if(!pFallbackPlane) { if(HouseTypeExt::ExtData *pExt = HouseTypeExt::ExtMap.Find(pHouse->Type)) { pFallbackPlane = pExt->GetParadropPlane(); } } pParaDropPlane = pFallbackPlane; } // finally, send the plane if(pParaDrop && pParaDropNum && pParaDropPlane) { Ares::SendPDPlane( pHouse, pCell, pParaDropPlane, pParaDrop, pParaDropNum); } } return true; }