123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- ** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
- **
- ** File: clusterIGC.cpp
- **
- ** Author:
- **
- ** Description:
- ** Implementation of the CclusterIGC class. This file was initially created by
- ** the ATL wizard for the core object.
- **
- ** Clusters are distinct places in the trek universe: nothing happening in one
- ** cluster can directly affect things in another cluster. Clusters (in addition to
- ** the ships, missiles, etc.) have a collection of static objects that represent things
- ** like planets.
- **
- ** History:
- */
- // clusterIGC.cpp : Implementation of CclusterIGC
- #include "pch.h"
- #include "clusterIGC.h"
- #include "modelIGC.h"
- #include <stdlib.h>
- #include <math.h>
- /////////////////////////////////////////////////////////////////////////////
- // CclusterIGC
- HRESULT CclusterIGC::Initialize(ImissionIGC* pMission, Time now, const void* data, int dataSize)
- {
- assert (pMission);
- m_pMission = pMission;
- ZRetailAssert (data && (dataSize == sizeof(m_data)));
- m_data = *((DataClusterIGC*)data);
- m_nPass = m_data.clusterID;
- m_lastUpdate = now;
- pMission->AddCluster(this);
- m_pClusterSite = pMission->GetIgcSite()->CreateClusterSite(this);
- if (m_data.posterName[0] != '\0')
- m_pClusterSite->SetEnvironmentGeo(m_data.posterName);
- if (m_data.planetName[0] != '\0')
- {
- Vector position;
- double sinLatitude = double(m_data.planetSinLatitude - 0.5);
- double cosLatitude = sqrt(1.0 - sinLatitude * sinLatitude);
- double longitude = double(m_data.planetLongitude * (2.0f * pi));
- position.x = float(cos(longitude) * cosLatitude);
- position.y = float(sin(longitude) * cosLatitude);
- position.z = float(sinLatitude);
- m_pClusterSite->AddPoster(m_data.planetName,
- position,
- float(m_data.planetRadius));
- }
- return S_OK;
- }
- void CclusterIGC::Terminate(void)
- {
- {
- //Models remove themselves from the list when terminated
- ModelLinkIGC* l;
- while (l = m_models.first()) //Not ==
- {
- l->data()->Terminate();
- }
- }
- m_kdrStatic.flush();
- m_kdrMoving.flush();
- assert (m_modelsPickable.n() == 0);
- assert (m_modelsCastRay.n() == 0);
- assert (m_stations.n() == 0);
- assert (m_models.n() == 0);
- assert (m_probes.n() == 0);
- assert (m_warps.n() == 0);
- assert (m_treasures.n() == 0);
- assert (m_asteroids.n() == 0);
- assert (m_mines.n() == 0);
- assert (m_pClusterSite);
- m_pClusterSite->Terminate();
- m_pClusterSite = NULL;
- m_pMission->DeleteCluster(this);
- }
- void CclusterIGC::Update(Time now)
- {
- if (now > m_lastUpdate)
- {
- float dt = now - m_lastUpdate;
- bool bStarted = m_pMission->GetMissionStage() == STAGE_STARTED;
- if (bStarted)
- {
- {
- //Have any stations launch docked drones
- for (StationLinkIGC* l = m_stations.first();
- (l != NULL);
- l = l->next())
- {
- IstationIGC* pstation = l->data();
- ShipLinkIGC* pslNext;
- for (ShipLinkIGC* psl = pstation->GetShips()->first();
- (psl != NULL);
- psl = pslNext)
- {
- IshipIGC* pship = psl->data();
- pslNext = psl->next(); //Get the next link now since the ship may launch
- if (pship->GetAutopilot() && (pship->GetPilotType() < c_ptPlayer))
- {
- //Docked non-players on autopilot never are observers/parents
- assert (pship->GetParentShip() == NULL);
- assert (pship->GetChildShips()->n() == 0);
- if (pship->OkToLaunch(now))
- pship->SetStation(NULL);
- }
- }
- }
- }
- {
- m_fCost = m_pMission->GetFloatConstant(c_fcidBaseClusterCost);
- float costLifepod = m_pMission->GetFloatConstant(c_fcidLifepodCost);
- float costTurret = m_pMission->GetFloatConstant(c_fcidTurretCost);
- float costPlayer = m_pMission->GetFloatConstant(c_fcidPlayerCost);
- float costDrone = m_pMission->GetFloatConstant(c_fcidDroneCost);
- //Have miners and builders do any pre-plotted moves. Allow ships to suicide.
- ShipLinkIGC* lNext;
- for (ShipLinkIGC* l = m_ships.first();
- (l != NULL);
- l = lNext)
- {
- IshipIGC* s = l->data();
- lNext = l->next();
- if (s->GetPilotType() < c_ptPlayer)
- m_fCost += costDrone;
- else if (s->GetParentShip() != NULL)
- m_fCost += costTurret;
- else
- {
- IhullTypeIGC* pht = s->GetBaseHullType();
- assert (pht);
- m_fCost += pht->HasCapability(c_habmLifepod)
- ? costLifepod
- : costPlayer;
- }
- s->PreplotShipMove(now);
- }
- if (m_fCost > 0.0f)
- {
- m_fCost *= dt / m_pMission->GetFloatConstant(c_fcidClusterDivisor);
- }
- {
- //Have all ships on autopilot plot their moves. Allow ships to suicide.
- ShipLinkIGC* lNext;
- for (ShipLinkIGC* l = m_ships.first();
- (l != NULL);
- l = lNext)
- {
- IshipIGC* s = l->data();
- lNext = l->next();
- s->PlotShipMove(now);
- }
- }
- }
- {
- //Have all ships execute their moves
- for (ShipLinkIGC* l = m_ships.first();
- (l != NULL);
- l = l->next())
- {
- IshipIGC* s = l->data();
- if (s->GetParentShip() == NULL)
- {
- s->ExecuteShipMove(now);
- }
- }
- }
- }
- else
- m_fCost = 0.0f;
- {
- //Call the update method on all the contained models
- //models might self-terminate in the update and nuke earlier models in the update loop
- //NYI debugging variables
- //ObjectType oldObjectType = NA;
- //ObjectType newObjectType = NA;
- ModelLinkIGC* lNext;
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = lNext)
- {
- //oldObjectType = newObjectType;
- //newObjectType = l->data()->GetObjectType();
- lNext = l->next();
- l->data()->Update(now);
- }
- }
- if (m_data.activeF && bStarted)
- {
- {
- //Update the bounding boxes for all moving objects & projectiles
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- l->data()->SetBB(m_lastUpdate, now, dt);
- }
- m_tMax = dt;
- }
- m_kdrStatic.update();
- m_kdrMoving.update();
- {
- //Cast rays through the KD tree for each object
- for (ModelLinkIGC* l = m_modelsCastRay.first();
- (l != NULL);
- l = l->next())
- {
- ImodelIGC* m = l->data();
- HitTest* ht = m->GetHitTest();
- if (!ht->GetDeadF())
- {
- m_kdrStatic.test(ht, &m_collisions);
- m_kdrMoving.test(ht, &m_collisions);
- }
- }
- }
- //Sort the collisions by the time they occur
- m_collisions.sort(0);
- //Process each collision (in order)
- {
- m_tOffset = 0.0f;
- for (m_collisionID = 0; (m_collisionID < m_collisions.n()); m_collisionID++)
- {
- const CollisionEntry& entry = m_collisions[m_collisionID];
- if (!(entry.m_pHitTest1->GetDeadF() || entry.m_pHitTest2->GetDeadF()))
- {
- Time timeCollision = m_lastUpdate + (m_tOffset + entry.m_tCollision);
- ImodelIGC* pModelHitTest1 = (ImodelIGC*)(entry.m_pHitTest1->GetData());
- assert (pModelHitTest1);
- ImodelIGC* pModelHitTest2 = (ImodelIGC*)(entry.m_pHitTest2->GetData());
- assert (pModelHitTest2);
- //Give each participant in the collision a chance to handle the collision
- //but give the "1st" model first dibs.
- if ((pModelHitTest1->GetCluster() == this) &&
- (pModelHitTest2->GetCluster() == this))
- {
- pModelHitTest1->HandleCollision(timeCollision, entry.m_tCollision, entry, pModelHitTest2);
- }
- }
- }
- m_collisions.purge();
- }
- {
- //Apply any damage from mines
- //Kids always follow parents in the ship list, so go from back to front
- //so that the killing a parent doesn't mean hitting dead elements in the list
- ShipLinkIGC* lTxen;
- for (ShipLinkIGC* l = m_ships.last();
- (l != NULL);
- l = lTxen)
- {
- IshipIGC* s = l->data();
- lTxen = l->txen();
- s->ApplyMineDamage();
- }
- }
- //Move each object & projectile
- {
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- l->data()->Move();
- }
- }
- if ((m_nPass++) % c_nPassesPerUpdate == 0)
- {
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- l->data()->UpdateSeenBySide();
- }
- m_pMission->GetIgcSite()->ClusterUpdateEvent(this);
- }
- }
- //Draw and resolve any explosions
- if (m_nExplosions != 0)
- {
- const int c_maxDmgs = 500;
- IdamageIGC* pdmgs[c_maxDmgs];
- int nDmgs = 0;
- //Copy the list of models in the sector that can be damaged into
- for (ModelLinkIGC* l = m_modelsPickable.first();
- (l != NULL);
- l = l->next())
- {
- ImodelIGC* pmodel = l->data();
- ObjectType type = pmodel->GetObjectType();
- //Not everything that can take damage can be affected by an explosion.
- if ((type == OT_ship) || (type == OT_asteroid) ||
- (type == OT_station) || (type == OT_missile) || (type == OT_probe))
- {
- pmodel->AddRef();
- pdmgs[nDmgs++] = (IdamageIGC*)pmodel;
- if (nDmgs == c_maxDmgs)
- break;
- }
- }
- ImineIGC* pmines[c_maxDmgs];
- int nMines = 0;
- {
- for (MineLinkIGC* l = m_mines.first(); (l != NULL); l = l->next())
- {
- ImineIGC* pm = l->data();
- pm->AddRef();
- pmines[nMines++] = pm;
- if (nMines == c_maxDmgs)
- break;
- }
- }
- int i = 0;
- do
- {
- ExplosionData& e = m_explosions[i];
- m_pClusterSite->AddExplosion(e.position, e.radius, e.explosionType);
- float dt = (e.time - m_lastUpdate) - m_tOffset;
- //Now, the painful part: applying damage to everything in the sector that could be hit
- {
- for (int j = 0; (j < nDmgs); j++)
- {
- IdamageIGC* pTarget = pdmgs[j];
- if (pTarget->GetCluster() == this) //Make sure it wasn't already destroyed
- {
- //The target is still around
- Vector p = pTarget->GetPosition() + dt * pTarget->GetVelocity();
- float d = (e.position - p).Length() - pTarget->GetRadius();
- if (d < e.radius)
- {
- float amount = e.amount;
- if (d > 0.0f)
- {
- float f = 1.0f - (d / e.radius);
- amount *= f * f;
- }
- pTarget->ReceiveDamage(e.damageType | c_dmgidNoWarn | c_dmgidNoDebris,
- amount,
- e.time,
- p, e.position,
- e.launcher);
- }
- }
- }
- }
- {
- for (int j = 0; (j < nMines); j++)
- {
- ImineIGC* pTarget = pmines[j];
- if (pTarget->GetCluster() == this) //Make sure it wasn't already destroyed
- {
- //The target is still around
- const Vector& p = pTarget->GetPosition();
- float d = (e.position - p).Length() - pTarget->GetRadius();
- if (d < e.radius)
- {
- float amount = e.amount;
- if (d > 0.0f)
- {
- float f = 1.0f - (d / e.radius);
- amount *= f * f;
- }
- pTarget->ReduceStrength(amount);
- }
- }
- }
- }
- if (e.launcher)
- e.launcher->Release();
- }
- while (++i < m_nExplosions);
- //Release all the cached pointers
- {
- while (--nDmgs >= 0)
- pdmgs[nDmgs]->Release();
- }
- {
- while (--nMines >= 0)
- pmines[nMines]->Release();
- }
- m_nExplosions = 0;
- }
- m_lastUpdate = now;
- }
- }
- int CclusterIGC::Export(void* data) const
- {
- if (data)
- {
- *((DataClusterIGC*)data) = m_data;
- //Selectively lie here: exported clusters are always inactive by default.
- ((DataClusterIGC*)data)->activeF = false;
- }
- return sizeof(m_data);
- }
- void CclusterIGC::AddModel(ImodelIGC* modelNew)
- {
- assert (modelNew);
- ZVerify(m_models.first(modelNew));
- modelNew->AddRef();
- m_pClusterSite->AddThingSite(modelNew->GetThingSite());
- {
- //Add the model to the collision set
- HitTest* ht = modelNew->GetHitTest();
- ModelAttributes mt = modelNew->GetAttributes();
- if ((mt & c_mtNotPickable) == 0)
- {
- ZVerify(m_modelsPickable.last(modelNew));
- modelNew->AddRef();
- }
- if (mt & c_mtCastRay)
- {
- ZVerify(m_modelsCastRay.last(modelNew));
- modelNew->AddRef();
- }
- if (mt & c_mtHitable)
- {
- ((mt & c_mtStatic)
- ? m_kdrStatic
- : m_kdrMoving).addHitTest(ht);
- }
- else
- ht->SetDeadF(false);
- ZRetailAssert(!ht->GetDeadF());
- }
- }
- void CclusterIGC::DeleteModel(ImodelIGC* modelOld)
- {
- assert (modelOld);
- {
- //Add the model to the collision set
- HitTest* ht = modelOld->GetHitTest();
- ModelAttributes mt = modelOld->GetAttributes();
- if ((mt & c_mtNotPickable) == 0)
- {
- DeleteIbaseIGC((BaseListIGC*)&m_modelsPickable, modelOld);
- }
- if (mt & c_mtCastRay)
- {
- DeleteIbaseIGC((BaseListIGC*)&m_modelsCastRay, modelOld);
- }
- if (mt & c_mtHitable)
- {
- ((mt & c_mtStatic)
- ? m_kdrStatic
- : m_kdrMoving).deleteHitTest(ht);
- }
- else
- ht->SetDeadF(true);
- ZRetailAssert(ht->GetDeadF());
- }
- m_pClusterSite->DeleteThingSite(modelOld->GetThingSite());
- DeleteIbaseIGC((BaseListIGC*)&m_models, modelOld);
- }
- ImodelIGC* CclusterIGC::GetModel(const char* name) const
- {
- assert (name);
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- ImodelIGC* m = l->data();
- if (_stricmp(m->GetName(), name) == 0)
- {
- return m;
- }
- }
- return NULL;
- }
- void CclusterIGC::RecalculateCollisions(float tOffset,
- ImodelIGC* pModel1,
- ImodelIGC* pModel2)
- {
- //Update the stop positions for the hit tests (& update their bounding boxes)
- assert ((pModel1->GetAttributes() & c_mtStatic) == 0);
- HitTest* pHitTest1 = pModel1->GetHitTest();
- if (pHitTest1)
- {
- if (pHitTest1->GetDeadF())
- pHitTest1 = NULL;
- else
- pHitTest1->SetStopPosition();
- }
- HitTest* pHitTest2;
- if ((pModel2 == NULL) || (pModel2->GetAttributes() & c_mtStatic))
- pHitTest2 = NULL;
- else
- {
- pHitTest2 = pModel2->GetHitTest();
- if (pHitTest2)
- {
- if (pHitTest2->GetDeadF())
- pHitTest2 = NULL;
- else
- pHitTest2->SetStopPosition();
- }
- }
- if (pHitTest1 || pHitTest2)
- {
- m_tOffset += tOffset;
- //Don't bother recalculating collisions if we are already outside the window in which collisions can occur.
- if (m_tOffset <= m_tMax)
- {
- {
- //Move all objects to their positions at the time of the collision
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- ImodelIGC* m = l->data();
- HitTest* pht = m->GetHitTest();
- //Move the models not involved in the collision
- if ((m != pModel1) && (m != pModel2))
- m->Move(tOffset);
- else
- pht->AdjustTimes(tOffset);
- //Update the bounding boxes for everything
- pht->UpdateBB();
- }
- }
- //Remove any collisions that involved either object involved in this collision
- m_collisions.flush(m_collisionID + 1, pHitTest1, pHitTest2);
- {
- //Check for any collisions between either ship and the rest of the stuff in the cluster
- for (ModelLinkIGC* l = m_models.first();
- (l != NULL);
- l = l->next())
- {
- ImodelIGC* m = l->data();
- if ((m != pModel1) && (m != pModel2) && (m->GetAttributes() & (c_mtCastRay | c_mtHitable)))
- {
- HitTest* ht = m->GetHitTest();
- if (!ht->GetDeadF())
- {
- if (ht->GetID() != c_htidStaticObject)
- {
- //ht is not a static object, so it can always go first (which also handles
- //the problem that projectiles (which ht might be) go before ships).
- //prevent collisions between projectiles and their launcher.
- if (pHitTest1 &&
- (ht->GetNoHit() != pHitTest1) &&
- (pHitTest1->GetNoHit() != ht))
- ht->Collide(pHitTest1, &m_collisions);
- if (pHitTest2 &&
- (ht->GetNoHit() != pHitTest2) &&
- (pHitTest2->GetNoHit() != ht))
- ht->Collide(pHitTest2, &m_collisions);
- }
- else
- {
- //in collisions between static objects and non-static objects, the
- //non-static object is always first
- if (pHitTest1 &&
- (ht->GetNoHit() != pHitTest1) &&
- (pHitTest1->GetNoHit() != ht))
- pHitTest1->Collide(ht, &m_collisions);
- if (pHitTest2 &&
- (ht->GetNoHit() != pHitTest2) &&
- (pHitTest2->GetNoHit() != ht))
- pHitTest2->Collide(ht, &m_collisions);
- }
- }
- }
- }
- }
- //Resort the collisions of the yet to be handled collisions
- m_collisions.sort(m_collisionID + 1);
- }
- }
- }
- IbuildingEffectIGC* CclusterIGC::CreateBuildingEffect(Time now,
- IasteroidIGC* pasteroid,
- IstationIGC* pstation,
- IshipIGC* pshipBuilder,
- float radiusAsteroid,
- float radiusStation,
- const Vector& positionStart,
- const Vector& positionStop)
- {
- DataBuildingEffectIGC dbe;
- dbe.timeStart = now;
- dbe.pasteroid = pasteroid;
- dbe.pstation = pstation;
- dbe.pcluster = this;
- dbe.pshipBuilder = pshipBuilder;
- dbe.radiusAsteroid = radiusAsteroid;
- dbe.radiusStation = radiusStation;
- dbe.positionStart = positionStart;
- dbe.positionStop = positionStop;
- IbuildingEffectIGC* pbe = (IbuildingEffectIGC*)(m_pMission->CreateObject(now, OT_buildingEffect, &dbe, sizeof(dbe)));
- assert (pbe);
- pbe->Release();
- return pbe;
- }
|