Exemple #1
0
bool SW_ParaDrop::Launch(SuperClass* pThis, CellStruct* pCoords, byte IsPlayer)
{
	if(pThis->IsCharged) {
		CellClass *pTarget = MapClass::Instance->GetCellAt(pCoords);

		// find the nearest cell the paradrop troopers can land on
		if(pTarget != MapClass::InvalidCell()) {
			if(pTarget->Tile_Is_Water()) {
				int a2 = 0;
				int a14 = 0;
				CellStruct *nearest = MapClass::Instance->Pathfinding_Find(&a2, pCoords, 0, -1, 0, 0, 1, 1, 0, 0, 0, 1, &a14, 0, 0);
				if(*nearest != SuperClass::DefaultCoords) {
					if(CellClass *pTemp = MapClass::Instance->GetCellAt(nearest)) {
						if(pTemp != MapClass::InvalidCell()) {
							if(!pTemp->Tile_Is_Water()) {
								pTarget = pTemp;
							}
						}
					}
				}
			}
		}

		// all set. send in the planes.
		return this->SendParadrop(pThis, pTarget);
	}

	return false;
}
Exemple #2
0
bool SW_Reveal::Activate(SuperClass* pThis, const CellStruct &Coords, bool IsPlayer)
{
    SuperWeaponTypeClass *pSW = pThis->Type;
    SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(pSW);

    if(pThis->IsCharged) {
        CellClass *pTarget = MapClass::Instance->GetCellAt(Coords);

        CoordStruct Crd = pTarget->GetCoords();
        auto range = GetRange(pData);

        // default way to reveal, but reveal one cell at a time.
        Helpers::Alex::for_each_in_rect_or_range<CellClass>(Coords, range.WidthOrRange, range.Height,
        [&](CellClass* pCell) -> bool {
            CoordStruct Crd2 = pCell->GetCoords();
            MapClass::Instance->RevealArea2(&Crd2, 1, pThis->Owner, 0, 0, 0, 0, 0);
            MapClass::Instance->RevealArea2(&Crd2, 1, pThis->Owner, 0, 0, 0, 0, 1);
            return true;
        }
                                                           );
    }

    return true;
}
Exemple #3
0
void PsychicDominatorStateMachine::Update()
{
	// waiting. lurking in the shadows.
	if(this->Deferment > 0) {
		if(--this->Deferment) {
			return;
		}
	}

	SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(this->Super->Type);

	switch(PsyDom::Status) {
	case PsychicDominatorStatus::FirstAnim:
		{
			// here are the contents of PsyDom::Start().
			CellClass *pTarget = MapClass::Instance->GetCellAt(this->Coords);
			CoordStruct coords = pTarget->GetCoords();
			coords.Z += pData->Dominator_FirstAnimHeight;

			AnimClass* pAnim = nullptr;
			if(AnimTypeClass* pAnimType = pData->Dominator_FirstAnim.Get(RulesClass::Instance->DominatorFirstAnim)) {
				pAnim = GameCreate<AnimClass>(pAnimType, coords);
			}
			PsyDom::Anim = pAnim;
		
			auto sound = pData->SW_ActivationSound.Get(RulesClass::Instance->PsychicDominatorActivateSound);
			if(sound != -1) {
				VocClass::PlayAt(sound, coords, nullptr);
			}

			pData->PrintMessage(pData->Message_Activate, this->Super->Owner);
			
			PsyDom::Status = PsychicDominatorStatus::Fire;

			// most likely LightUpdateTimer
			ScenarioClass::Instance->AmbientTimer.Start(1);
			ScenarioClass::UpdateLighting();

			return;
		}
	case PsychicDominatorStatus::Fire:
		{
			// wait for some percentage of the first anim to be
			// played until we strike.
			AnimClass* pAnim = PsyDom::Anim;
			if(pAnim) {
				int currentFrame = pAnim->Animation.Value;
				short frameCount = pAnim->Type->GetImage()->Frames;
				int percentage = pData->Dominator_FireAtPercentage.Get(RulesClass::Instance->DominatorFireAtPercentage);
				if(frameCount * percentage / 100 > currentFrame) {
					return;
				}
			}

			PsyDom::Fire();

			PsyDom::Status = PsychicDominatorStatus::SecondAnim;
			return;
		}
	case PsychicDominatorStatus::SecondAnim:
		{
			// wait for the second animation to finish. (there may be up to
			// 10 frames still to be played.)
			AnimClass* pAnim = PsyDom::Anim;
			if(pAnim) {
				int currentFrame = pAnim->Animation.Value;
				short frameCount = pAnim->Type->GetImage()->Frames;

				if(frameCount - currentFrame > 10) {
					return;
				}
			}

			PsyDom::Status = PsychicDominatorStatus::Reset;
			return;
		}
	case PsychicDominatorStatus::Reset:
		{
			// wait for the last frame... WTF? 
			AnimClass* pAnim = PsyDom::Anim;
			if(pAnim) {
				int currentFrame = pAnim->Animation.Value;
				short frameCount = pAnim->Type->GetImage()->Frames;

				if(frameCount - currentFrame > 1) {
					return;
				}
			}

			PsyDom::Status = PsychicDominatorStatus::Over;

			PsyDom::Coords = CellStruct::Empty;
			PsyDom::Anim = nullptr;
			ScenarioClass::UpdateLighting();

			return;
		}
	case PsychicDominatorStatus::Over:
		{
			// wait for the light to go away.
			if(ScenarioClass::Instance->AmbientCurrent != ScenarioClass::Instance->AmbientTarget) {
				return;
			}

			// clean up
			SW_PsychicDominator::CurrentPsyDom = nullptr;
			PsyDom::Status = PsychicDominatorStatus::Inactive;
			ScenarioClass::UpdateLighting();
			this->Clock.TimeLeft = 0;
		}
	}
}
Exemple #4
0
bool SWTypeExt::Launch(SuperClass* pThis, NewSWType* pSW, const CellStruct &Coords, bool IsPlayer) {
	if(SWTypeExt::ExtData *pData = SWTypeExt::ExtMap.Find(pThis->Type)) {

		// launch the SW, then play sounds and animations. if the SW isn't launched
		// nothing will be played.
		if(pSW->Activate(pThis, Coords, IsPlayer)) {
			SuperWeaponFlags::Value flags = pSW->Flags();

			if(flags & SuperWeaponFlags::PostClick) {
				// use the properties of the originally fired SW
				if(HouseExt::ExtData *pExt = HouseExt::ExtMap.Find(pThis->Owner)) {
					if(auto pLast = pThis->Owner->Supers.GetItemOrDefault(pExt->SWLastIndex)) {
						pThis = pLast;
						pData = SWTypeExt::ExtMap.Find(pThis->Type);
					}
				}
			}

			if(!Unsorted::MuteSWLaunches && (pData->EVA_Activated != -1) && !(flags & SuperWeaponFlags::NoEVA)) {
				VoxClass::PlayIndex(pData->EVA_Activated);
			}
			
			if(!(flags & SuperWeaponFlags::NoMoney)) {
				int Money_Amount = pData->Money_Amount;
				if(Money_Amount > 0) {
					Debug::Log("House %d gets %d credits\n", pThis->Owner->ArrayIndex, Money_Amount);
					pThis->Owner->GiveMoney(Money_Amount);
				} else if(Money_Amount < 0) {
					Debug::Log("House %d loses %d credits\n", pThis->Owner->ArrayIndex, -Money_Amount);
					pThis->Owner->TakeMoney(-Money_Amount);
				}
			}

			CellClass *pTarget = MapClass::Instance->GetCellAt(Coords);

			CoordStruct coords = pTarget->GetCoordsWithBridge();

			auto pAnim = pData->GetAnim();
			if(pAnim && !(flags & SuperWeaponFlags::NoAnim)) {
				coords.Z += pData->SW_AnimHeight;
				AnimClass *placeholder = GameCreate<AnimClass>(pAnim, coords);
				placeholder->Invisible = !pData->IsAnimVisible(pThis->Owner);
			}

			int sound = pData->GetSound();
			if(sound && !(flags & SuperWeaponFlags::NoSound)) {
				VocClass::PlayAt(sound, coords, nullptr);
			}

			if(pData->SW_RadarEvent && !(flags & SuperWeaponFlags::NoEvent)) {
				RadarEventClass::Create(RadarEventType::SuperweaponActivated, Coords);
			}

			if(!(flags & SuperWeaponFlags::NoMessage)) {
				pData->PrintMessage(pData->Message_Launch, pThis->Owner);
			}

			// this sw has been fired. clean up.
			int idxThis = pThis->Owner->Supers.FindItemIndex(pThis);
			if(IsPlayer && !(flags & SuperWeaponFlags::NoCleanup)) {
				// what's this? we reset the selected SW only for the player on this
				// computer, so others don't deselect it when firing simultaneously.
				// and we only do this, if this type's index is the current one, because
				// auto-firing might happen while the player still selects a target.
				// PostClick SWs do have a different type index, so they need to be
				// special cased, but they can't auto-fire anyhow.
				if(pThis->Owner == HouseClass::Player) {
					if(idxThis == Unsorted::CurrentSWType || (flags & SuperWeaponFlags::PostClick)) {
						Unsorted::CurrentSWType = -1;
					}
				}

				// do not play ready sound. this thing just got off.
				if(pData->EVA_Ready != -1) {
					VoxClass::SilenceIndex(pData->EVA_Ready);
				}
			}

			if(HouseExt::ExtData *pExt = HouseExt::ExtMap.Find(pThis->Owner)) {
				// post-click actions. AutoFire SWs cannot support this. Consider
				// two Chronospheres auto-firing (the second may be launched manually).
				// the ChronoWarp would chose the source selected last, because it
				// would overwrite the previous (unfired) SW's index.
				if(!(flags & SuperWeaponFlags::PostClick) && !pData->SW_AutoFire) {
					pExt->SWLastIndex = idxThis;
				}
			}
	
			return true;
		}
	}

	return false;
}
Exemple #5
0
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;
}
Exemple #6
0
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;
    }
}