void CGameServer::ClientEnterGame(int iClient) { TMsg(sprintf(tstring("Client %d (") + GameNetwork()->GetClientNickname(iClient) + ") entering game.\n", iClient)); if (GetGame()) GetGame()->OnClientEnterGame(iClient); for (size_t i = 0; i < GameServer()->GetMaxEntities(); i++) { CBaseEntity* pEntity = CBaseEntity::GetEntity(i); if (!pEntity) continue; ::CreateEntity.RunCommand(sprintf(tstring("%s %d %d"), pEntity->GetClassName(), pEntity->GetHandle(), pEntity->GetSpawnSeed()), iClient); } CGameServerNetwork::UpdateNetworkVariables(iClient, true); // Update entities after all creations have been run, so we don't refer to entities that haven't been created yet. for (size_t i = 0; i < GameServer()->GetMaxEntities(); i++) { CBaseEntity* pEntity = CBaseEntity::GetEntity(i); if (!pEntity) continue; pEntity->ClientUpdate(iClient); } GameNetwork()->CallFunction(iClient, "EnterGame"); GameNetwork()->CallFunction(iClient, "LoadingDone"); }
void CLobbyPanel::BeginGameCallback(int iConnection, INetworkListener*, class CNetworkParameters*) { TAssert(iConnection == CONNECTION_LOBBY); CDigitanksLevel* pLevel = CDigitanksGame::GetLevel(CGameLobbyClient::L_GetInfoValue(_T("level_file"))); if (!pLevel) return; CVar::SetCVar(_T("game_level"), pLevel->GetFile()); DigitanksWindow()->GetLobbyPanel()->SetVisible(false); const char* pszPort = DigitanksWindow()->GetCommandLineSwitchValue("--port"); int iPort = pszPort?atoi(pszPort):0; if (LobbyNetwork()->IsHost()) GameNetwork()->CreateHost(iPort); else GameNetwork()->ConnectToHost(convertstring<tchar, char>(DigitanksWindow()->GetLobbyPanel()->m_sHost).c_str(), iPort); if (GameNetwork()->IsConnected()) DigitanksWindow()->Restart(GAMETYPE_FROM_LOBBY); else DigitanksWindow()->Restart(GAMETYPE_MENU); }
void CGameServer::RegisterNetworkFunctions() { GameNetwork()->RegisterFunction("UV", this, UpdateValueCallback, 2, NET_HANDLE, NET_HANDLE); GameNetwork()->RegisterFunction("ClientInfo", this, ClientInfoCallback, 2, NET_INT, NET_FLOAT); GameNetwork()->RegisterFunction("DestroyEntity", this, DestroyEntityCallback, 1, NET_INT); GameNetwork()->RegisterFunction("LoadingDone", this, LoadingDoneCallback, 0); }
void CStructure::InstallUpdate(size_t x, size_t y) { CNetworkParameters p; p.ui1 = GetHandle(); p.ui2 = x; p.ui3 = y; if (GameNetwork()->IsHost()) InstallUpdate(&p); else GameNetwork()->CallFunctionParameters(NETWORK_TOSERVER, "InstallUpdate", &p); }
void CGameServer::DestroyAllEntities(const tvector<tstring>& asSpare, bool bRemakeGame) { if (!GameNetwork()->IsHost() && !IsLoading()) return; if (m_pWorkListener) m_pWorkListener->SetAction("Locating dead nodes", GameServer()->GetMaxEntities()); for (size_t i = 0; i < GameServer()->GetMaxEntities(); i++) { CBaseEntity* pEntity = CBaseEntity::GetEntity(i); if (!pEntity) continue; bool bSpare = false; for (size_t j = 0; j < asSpare.size(); j++) { if (asSpare[j] == pEntity->GetClassName()) { bSpare = true; break; } } if (bSpare) continue; pEntity->Delete(); if (m_pWorkListener) m_pWorkListener->WorkProgress(i); } if (m_pWorkListener) m_pWorkListener->SetAction("Clearing buffers", GameServer()->m_ahDeletedEntities.size()); for (size_t i = 0; i < GameServer()->m_ahDeletedEntities.size(); i++) { delete GameServer()->m_ahDeletedEntities[i]; if (m_pWorkListener) m_pWorkListener->WorkProgress(i); } GameServer()->m_ahDeletedEntities.clear(); if (CBaseEntity::GetNumEntities() == 0) CBaseEntity::s_iNextEntityListIndex = 0; if (bRemakeGame && GameNetwork()->IsHost()) m_hGame = CreateGame(); }
void CGameServer::Delete(CBaseEntity* pEntity) { TAssert(GameNetwork()->IsHost() || IsLoading()); if (!(GameNetwork()->IsHost() || IsLoading())) TMsg("WARNING: CGameServer::Delete() when not host or not loading.\n"); if (GameNetwork()->IsHost()) GameNetwork()->CallFunction(NETWORK_TOCLIENTS, "DestroyEntity", pEntity->GetHandle()); CNetworkParameters p; p.i1 = (int)pEntity->GetHandle(); DestroyEntity(CONNECTION_GAME, &p); }
void CGameServer::ClientDisconnect(int iClient) { if (!GameNetwork()->IsHost() && iClient == GameNetwork()->GetClientID()) { TMsg("Disconnected from server.\n"); } else { TMsg(sprintf(tstring("Client %d (") + GameNetwork()->GetClientNickname(iClient) + ") disconnected.\n", iClient)); CApplication::Get()->OnClientDisconnect(iClient); if (GetGame()) GetGame()->OnClientDisconnect(iClient); } }
void CCPU::BeginRogueProduction() { if (IsProducing()) return; if (DigitanksGame()->GetConstructionCost(UNIT_SCOUT) > GetDigitanksPlayer()->GetPower()) return; CNetworkParameters p; p.ui1 = GetHandle(); if (GameNetwork()->IsHost()) BeginRogueProduction(&p); else GameNetwork()->CallFunctionParameters(NETWORK_TOSERVER, "BeginRogueProduction", &p); }
void CStructure::BeginUpgrade() { if (!CanStructureUpgrade()) return; if (GetDigitanksPlayer()->GetPower() < UpgradeCost()) return; CNetworkParameters p; p.ui1 = GetHandle(); if (GameNetwork()->IsHost()) BeginUpgrade(&p); else GameNetwork()->CallFunctionParameters(NETWORK_TOSERVER, "BeginUpgrade", &p); }
void CLobbyPanel::ConnectToLocalLobby(const tstring& sHost) { m_sHost = sHost; m_bOnline = true; CGameLobbyClient::SetLobbyUpdateCallback(&LobbyUpdateCallback); CGameLobbyClient::SetLobbyJoinCallback(&LobbyJoinCallback); CGameLobbyClient::SetLobbyLeaveCallback(&LobbyLeaveCallback); CGameLobbyClient::SetBeginGameCallback(&BeginGameCallback); const char* pszPort = DigitanksWindow()->GetCommandLineSwitchValue("--lobby-port"); int iPort = pszPort?atoi(pszPort):0; GameNetwork()->Disconnect(); LobbyNetwork()->SetCallbacks(NULL, NULL, CGameLobbyClient::ClientEnterGame, CGameLobbyClient::ClientDisconnect); LobbyNetwork()->ConnectToHost(convertstring<tchar, char>(sHost).c_str(), iPort); if (!LobbyNetwork()->IsConnected()) return; LobbyNetwork()->SetLoading(false); m_pDockPanel->SetDockedPanel(new CInfoPanel()); DigitanksWindow()->GetMainMenu()->SetVisible(false); SetVisible(true); m_bLayout = true; }
void CSupplier::RemoveChild(CStructure* pChild) { if (!GameNetwork()->IsHost()) return; if (!pChild) return; CNetworkParameters p; p.ui1 = GetHandle(); p.ui2 = pChild->GetHandle(); AddChild(&p); GameNetwork()->CallFunctionParameters(NETWORK_TOCLIENTS, "RemoveChild", &p); }
void KickPlayer(class CCommand* pCommand, tvector<tstring>& asTokens, const tstring& sCommand) { if (!asTokens.size()) return; GameNetwork()->DisconnectClient(stoi(asTokens[0])); }
void CGameServer::ClientConnect(int iClient) { GameNetwork()->CallFunction(iClient, "ClientInfo", iClient, GetGameTime()); if (GetGame()) GetGame()->OnClientConnect(iClient); }
CGameServer::~CGameServer() { GameNetwork()->SetCallbacks(NULL, NULL, NULL, NULL); if (m_pWorkListener) m_pWorkListener->BeginProgress(); if (m_pWorkListener) m_pWorkListener->SetAction("Scrubbing database", CBaseEntity::GetEntityRegistration().size()); DestroyAllEntities(tvector<tstring>()); GamePhysics()->RemoveAllEntities(); for (size_t i = 0; i < m_apLevels.size(); i++) m_apLevels[i].reset(); if (m_pCameraManager) delete m_pCameraManager; if (m_pWorkListener) m_pWorkListener->EndProgress(); TAssert(s_pGameServer == this); s_pGameServer = NULL; }
void CGameServer::Initialize() { if (m_pWorkListener) m_pWorkListener->BeginProgress(); m_bGotClientInfo = false; m_bLoading = true; TMsg("Initializing game server\n"); ReadLevels(); GameNetwork()->ClearRegisteredFunctions(); RegisterNetworkFunctions(); DestroyAllEntities(tvector<tstring>(), true); CParticleSystemLibrary::ClearInstances(); if (!m_pCameraManager) m_pCameraManager = new CCameraManager(); if (m_pWorkListener) m_pWorkListener->EndProgress(); if (m_pWorkListener) m_pWorkListener->SetAction("Pending network actions", 0); }
void CGameServer::SetPlayerNickname(const tstring& sNickname) { m_sNickname = sNickname; if (GameNetwork()->IsHost() || m_bGotClientInfo) SendNickname.RunCommand(m_sNickname); }
CEntityHandle<CBaseEntity> CGameServer::Create(const char* pszEntityName) { TAssert(GameNetwork()->IsHost()); if (!GameNetwork()->ShouldRunClientFunction()) return CEntityHandle<CBaseEntity>(); CEntityHandle<CBaseEntity> hEntity(CreateEntity(pszEntityName)); TAssert(!GameNetwork()->IsConnected()); // The below causes entities to be created twice on the server. Wasn't a // problem with Digitanks, but is a problem now that I'm adding physics. // If I ever go back to multiplayer, the code needs to be split into a // client portion and a server portion to help minimize bugs like this. //::CreateEntity.RunCommand(sprintf(tstring("%s %d %d"), pszEntityName, hEntity->GetHandle(), hEntity->GetSpawnSeed())); if (IsLoading()) AddToPrecacheList(pszEntityName); return hEntity; }
bool CCPU::BeginConstruction() { if (!IsPreviewBuildValid()) return false; if (GetPowerToConstruct(m_ePreviewStructure, GetPreviewBuild()) > GetDigitanksPlayer()->GetPower()) return false; CNetworkParameters p; p.ui1 = GetHandle(); p.i2 = m_ePreviewStructure; p.fl3 = GetPreviewBuild().x; p.fl4 = GetPreviewBuild().y; p.fl5 = GetPreviewBuild().z; if (GameNetwork()->IsHost()) BeginConstruction(&p); else GameNetwork()->CallFunctionParameters(NETWORK_TOSERVER, "BeginConstruction", &p); bool bSuccess = false; // This is used for the bot to see if a build was successful. if (GameNetwork()->IsHost()) bSuccess = (p.ui1 != ~0); else bSuccess = true; if (bSuccess && GetDigitanksPlayer()) { int iRunners = RandomInt(15, 10); for (int i = 0; i < iRunners; i++) DigitanksGame()->GetTerrain()->AddRunner(GetPreviewBuild(), GetDigitanksPlayer()->GetColor(), 1); } return bSuccess; }
void CStructure::SetSupplier(const class CSupplier* pSupplier) { if (!GameNetwork()->IsHost()) return; m_hSupplier = pSupplier; if (!m_hSupplyLine && m_hSupplier != NULL) m_hSupplyLine = GameServer()->Create<CSupplyLine>("CSupplyLine"); if (m_hSupplyLine != NULL && !m_hSupplier) GameServer()->Delete(m_hSupplyLine); if (m_hSupplyLine != NULL && m_hSupplier != NULL) m_hSupplyLine->SetEntities(m_hSupplier, this); }
void CGameWindow::Run() { while (IsOpen()) { CProfiler::BeginFrame(); if (true) { TPROF("CGameWindow::Run"); PreFrame(); if (GameServer()->IsHalting()) { //DestroyGame(); //CreateGame(m_eRestartAction); } float flTime = GetTime(); if (GameServer()) { if (GameServer()->IsLoading()) { // Pump the network CNetwork::Think(); RenderLoading(); continue; } else if (GameServer()->IsClient() && !GameNetwork()->IsConnected()) { //DestroyGame(); //CreateGame(GAMETYPE_MENU); } else { GameServer()->Think(flTime); Render(); } } PostFrame(); } CProfiler::Render(); SwapBuffers(); } }
void CLobbyPanel::CreateLobby(bool bOnline) { m_bOnline = bOnline; CGameLobbyClient::SetLobbyUpdateCallback(&LobbyUpdateCallback); CGameLobbyClient::SetLobbyJoinCallback(&LobbyJoinCallback); CGameLobbyClient::SetLobbyLeaveCallback(&LobbyLeaveCallback); CGameLobbyClient::SetBeginGameCallback(&BeginGameCallback); const char* pszPort = DigitanksWindow()->GetCommandLineSwitchValue("--lobby-port"); int iPort = pszPort?atoi(pszPort):0; m_iLobby = CGameLobbyServer::CreateLobby(); CGameLobbyServer::SetListener(DigitanksLobbyListener()); if (m_bOnline) { GameNetwork()->Disconnect(); LobbyNetwork()->Disconnect(); LobbyNetwork()->SetCallbacks(NULL, NULL, CGameLobbyServer::ClientEnterGame, CGameLobbyServer::ClientDisconnect); LobbyNetwork()->CreateHost(iPort); } CGameLobbyClient::S_JoinLobby(m_iLobby); CGameLobbyClient::S_UpdatePlayer(_T("host"), _T("1")); CGameLobbyClient::S_UpdateLobby(_T("gametype"), tsprintf(tstring("%d"), (gametype_t)lobby_gametype.GetInt())); if (!m_bOnline) { CGameLobbyClient::S_AddBot(); CGameLobbyClient::S_AddBot(); CGameLobbyClient::S_AddBot(); } for (size_t i = 0; i < CGameLobbyClient::L_GetNumPlayers(); i++) CGameLobbyClient::S_UpdatePlayer(CGameLobbyClient::L_GetPlayer(i)->iID, _T("color"), tsprintf(tstring("%d"), i)); if ((gametype_t)lobby_gametype.GetInt() == GAMETYPE_ARTILLERY) m_pDockPanel->SetDockedPanel(new CArtilleryGamePanel(true)); else m_pDockPanel->SetDockedPanel(new CStrategyGamePanel(true)); SetVisible(true); DigitanksWindow()->GetMainMenu()->SetVisible(false); m_bLayout = true; }
void CSupplyLine::Intercept(float flIntercept) { if (!GameNetwork()->IsHost()) return; m_flIntegrity -= flIntercept; if (m_flIntegrity < 0) m_flIntegrity = 0; m_bDelayRecharge = true; if (GetEntity() && m_flIntegrity < MinimumIntegrity() && dynamic_cast<CStructure*>(GetEntity()) && GetEntity()->GetOwner()) { ToDigitanksPlayer(GetEntity()->GetOwner())->RemoveUnit(static_cast<CStructure*>(GetEntity())); DigitanksGame()->OnDisabled(GetEntity(), NULL, NULL); } }
void CStructure::BeginConstruction(Vector vecConstructionOrigin) { m_iTurnsToConstruct = GetTurnsToConstruct(vecConstructionOrigin); m_bConstructing = true; FindGround(); if (GetModel()) { Vector vecScaffoldingSize = GetModel()->m_aabbVisBoundingBox.Size(); m_flScaffoldingSize = vecScaffoldingSize.Length()/2; } GameNetwork()->CallFunction(NETWORK_TOCLIENTS, "BeginStructureConstruction", GetHandle()); BeginStructureConstruction(NULL); }
void CCPU::StartTurn() { BaseClass::StartTurn(); if (m_bProducing) { m_iTurnsToProduceRogue -= (size_t)1; if (m_iTurnsToProduceRogue == (size_t)0) { if (GameNetwork()->IsHost()) { CDigitank* pTank = GameServer()->Create<CScout>("CScout"); pTank->SetGlobalOrigin(GetGlobalOrigin()); pTank->CalculateVisibility(); GetDigitanksPlayer()->AddUnit(pTank); for (size_t x = 0; x < UPDATE_GRID_SIZE; x++) { for (size_t y = 0; y < UPDATE_GRID_SIZE; y++) { if (GetDigitanksPlayer()->HasDownloadedUpdate(x, y)) pTank->DownloadComplete(x, y); } } // Face him toward the center. pTank->Move(pTank->GetGlobalOrigin() + -GetGlobalOrigin().Normalized()*15); pTank->Turn(VectorAngles(-GetGlobalOrigin().Normalized())); pTank->StartTurn(); } m_bProducing = false; GetDigitanksPlayer()->AppendTurnInfo("Production finished on Rogue"); GetDigitanksPlayer()->AddActionItem(this, ACTIONTYPE_UNITREADY); } else { GetDigitanksPlayer()->AppendTurnInfo(tsprintf(tstring("Producing Rogue (%d turns left)"), m_iTurnsToProduceRogue.Get())); } } }
void CCPU::OnDeleted() { if (!GameNetwork()->IsHost()) return; if (!GetDigitanksPlayer()) return; tvector<CBaseEntity*> apDeleteThese; for (size_t i = 0; i < GetDigitanksPlayer()->GetNumUnits(); i++) { CBaseEntity* pMember = GetDigitanksPlayer()->GetUnit(i); if (pMember == this) continue; apDeleteThese.push_back(pMember); } if (GameServer()->IsLoading()) return; for (size_t i = 0; i < apDeleteThese.size(); i++) { CBaseEntity* pMember = apDeleteThese[i]; if (!pMember) continue; CStructure* pStructure = dynamic_cast<CStructure*>(pMember); // Delete? I meant... repurpose. if (pStructure && !pStructure->IsConstructing()) { GetDigitanksPlayer()->RemoveUnit(pStructure); } else { bool bColorSwap = (pStructure || dynamic_cast<CDigitank*>(pMember)); Color clrTeam = GetDigitanksPlayer()->GetColor(); CModelDissolver::AddModel(pMember, bColorSwap?&clrTeam:NULL); pMember->Delete(); } } }
void ShowStatus(class CCommand* pCommand, tvector<tstring>& asTokens, const tstring& sCommand) { TMsg(tstring("Level: ") + CVar::GetCVarValue("game_level") + "\n"); TMsg(sprintf(tstring("Clients: %d Entities: %d/%d\n"), GameNetwork()->GetClientsConnected(), CBaseEntity::GetNumEntities(), GameServer()->GetMaxEntities())); for (size_t i = 0; i < Game()->GetNumPlayers(); i++) { const CPlayer* pPlayer = Game()->GetPlayer(i); if (!pPlayer) continue; if (pPlayer->GetClient() < 0) TMsg("Local: "); else TMsg(sprintf(tstring("%d: "), pPlayer->GetClient())); TMsg(pPlayer->GetPlayerName()); TMsg("\n"); } }
void CGameServer::ClientInfo(int iConnection, CNetworkParameters* p) { if (m_iClient != p->i1) CGame::ClearLocalPlayers(NULL); m_iClient = p->i1; float flNewGameTime = p->fl2; if (flNewGameTime - m_flGameTime > 0.1f) TMsg(sprintf(tstring("New game time from server %.1f different!\n"), flNewGameTime - m_flGameTime)); m_flGameTime = flNewGameTime; // Can't send any client commands until we've gotten the client info because we need m_iClient filled out properly. if (!m_bGotClientInfo) { GameNetwork()->SetRunningClientFunctions(false); SendNickname.RunCommand(m_sNickname); } m_bGotClientInfo = true; }
void CMiniBuffer::UpgradeComplete() { if (!GameNetwork()->IsHost()) return; CBuffer* pBuffer = GameServer()->Create<CBuffer>("CBuffer"); pBuffer->SetConstructing(false); pBuffer->SetGlobalOrigin(GetGlobalOrigin()); GetPlayerOwner()->AddUnit(pBuffer); pBuffer->SetSupplier(GetSupplier()); GetSupplier()->AddChild(pBuffer); pBuffer->GiveDataStrength(pBuffer->InitialDataStrength() - InitialDataStrength()); // Give the difference pBuffer->GiveDataStrength(m_iDataStrength - InitialDataStrength()); // Give what I've earned so far pBuffer->AddFleetPoints(pBuffer->InitialFleetPoints() - InitialFleetPoints()); pBuffer->AddBandwidth(pBuffer->InitialBandwidth() - InitialBandwidth()); pBuffer->AddEnergyBonus(pBuffer->InitialEnergyBonus() - InitialEnergyBonus()); pBuffer->AddRechargeBonus(pBuffer->InitialRechargeBonus() - InitialRechargeBonus()); pBuffer->CalculateVisibility(); for (size_t x = 0; x < UPDATE_GRID_SIZE; x++) { for (size_t y = 0; y < UPDATE_GRID_SIZE; y++) { if (GetDigitanksPlayer()->HasDownloadedUpdate(x, y)) pBuffer->InstallUpdate(x, y); } } for (size_t i = 0; i < m_ahChildren.size(); i++) { m_ahChildren[i]->SetSupplier(pBuffer); pBuffer->AddChild(m_ahChildren[i]); } Delete(); GetDigitanksPlayer()->AddActionItem(pBuffer, ACTIONTYPE_UPGRADE); }
void CSupplyLine::StartTurn() { BaseClass::StartTurn(); if (!m_hEntity) return; // Supplier got blowed up? if (!m_hSupplier) return; if (!GameNetwork()->IsHost()) return; if (!m_bDelayRecharge) { m_flIntegrity += 0.2f; if (m_flIntegrity > 1) m_flIntegrity = 1; } m_bDelayRecharge = false; }
void CDigitanksEntity::Think() { BaseClass::Think(); if (m_flNextDirtyOrigin > 0 && GameServer()->GetGameTime() > m_flNextDirtyOrigin) { DirtyVisibility(); m_flNextDirtyOrigin = 0; } if (m_flNextDirtyArea > 0 && GameServer()->GetGameTime() > m_flNextDirtyArea) { CDigitanksEntity* pOther = this; while (true) { pOther = CBaseEntity::FindClosest<CDigitanksEntity>(GetGlobalOrigin(), pOther); if (!pOther) break; if (pOther == this) continue; if (pOther->Distance(GetGlobalOrigin()) > VisibleRange() + DigitanksGame()->FogPenetrationDistance()) break; pOther->DirtyVisibility(); } m_flNextDirtyArea = 0; } if (GameNetwork()->IsHost() && !IsAlive() && GameServer()->GetGameTime() > m_flTimeKilled + 1.0f) { GameServer()->Delete(this); if (DigitanksGame()->GetTerrain()->IsPointOverHole(GetGlobalOrigin())) { CWreckage* pWreckage = CreateWreckage(); if (pWreckage) { pWreckage->FellIntoHole(); if (DigitanksGame()->GetGameType() == GAMETYPE_ARTILLERY) pWreckage->SetScale(2); } } else if (DigitanksGame()->GetGameType() == GAMETYPE_ARTILLERY) { switch (RandomInt(0, 8)) { case 0: case 6: case 7: case 8: default: { for (size_t i = 0; i < 8; i++) { CDebris* pDebris = GameServer()->Create<CDebris>("CDebris"); pDebris->SetGlobalOrigin(GetGlobalOrigin()); } CWreckage* pWreckage = CreateWreckage(); pWreckage->SetScale(2); DigitanksGame()->GetOverheadCamera()->Shake(GetGlobalOrigin(), 3); break; } case 1: { CProjectile* pProjectile = GameServer()->Create<CLargeShell>("CLargeShell"); pProjectile->SetOwner(NULL); pProjectile->SetGlobalOrigin(GetGlobalOrigin()); pProjectile->Explode(); break; } case 2: { CProjectile* pProjectile = GameServer()->Create<CAOEShell>("CAOEShell"); pProjectile->SetOwner(NULL); pProjectile->SetGlobalOrigin(GetGlobalOrigin()); pProjectile->Explode(); break; } case 3: { CProjectile* pProjectile = GameServer()->Create<CClusterBomb>("CClusterBomb"); pProjectile->SetOwner(NULL); pProjectile->SetGlobalOrigin(GetGlobalOrigin()); pProjectile->Explode(); break; } case 4: { CProjectile* pProjectile = GameServer()->Create<CEarthshaker>("CEarthshaker"); pProjectile->SetOwner(NULL); pProjectile->SetGlobalOrigin(GetGlobalOrigin()); pProjectile->Explode(); break; } case 5: { CProjectile* pProjectile = GameServer()->Create<CTractorBomb>("CTractorBomb"); pProjectile->SetOwner(NULL); pProjectile->SetGlobalOrigin(GetGlobalOrigin()); pProjectile->Explode(); break; } } } else { // Strategy mode CreateWreckage(); } } m_hCageParticles.SetActive(IsImprisoned() && GetVisibility() > 0.1f); }