HailPanel::HailPanel(PlayerInfo &player, const StellarObject *object) : player(player), planet(object->GetPlanet()), sprite(object->GetSprite().GetSprite()), unit(object->Position().Unit()) { SetInterruptible(false); const Government *gov = player.GetSystem()->GetGovernment(); if(planet) header = gov->GetName() + " " + planet->Noun() + " \"" + planet->Name() + "\":"; if(planet && player.Flagship()) { for(const Mission &mission : player.Missions()) if(mission.HasClearance(planet) && mission.ClearanceMessage() != "auto" && mission.HasFullClearance()) { planet->Bribe(); message = mission.ClearanceMessage(); return; } if(planet->CanLand()) message = "You are cleared to land, " + player.Flagship()->Name() + "."; else { SetBribe(planet->GetBribeFraction()); if(bribe) message = "If you want to land here, it'll cost you " + Format::Number(bribe) + " credits."; else message = "I'm afraid we can't permit you to land here."; } } }
// Get a string to show if this mission is "blocked" from being offered // because it requires you to have more passenger or cargo space free. After // calling this function, any future calls to it will return an empty string // so that you do not display the same message multiple times. string Mission::BlockedMessage(const PlayerInfo &player) { if(blocked.empty()) return ""; int extraCrew = 0; if(player.Flagship()) extraCrew = player.Flagship()->Crew() - player.Flagship()->RequiredCrew(); int cargoNeeded = cargoSize - (player.Cargo().Free() + player.Cargo().CommoditiesSize()); int bunksNeeded = passengers - (player.Cargo().Bunks() + extraCrew); if(cargoNeeded < 0 && bunksNeeded < 0) return ""; map<string, string> subs; subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); ostringstream out; if(bunksNeeded > 0) out << (bunksNeeded == 1 ? "another bunk" : to_string(bunksNeeded) + " more bunks"); if(bunksNeeded > 0 && cargoNeeded > 0) out << " and "; if(cargoNeeded > 0) out << (cargoNeeded == 1 ? "another ton" : to_string(cargoNeeded) + " more tons") << " of cargo space"; subs["<capacity>"] = out.str(); string message = Format::Replace(blocked, subs); blocked.clear(); return message; }
bool Mission::HasSpace(const PlayerInfo &player) const { int extraCrew = 0; if(player.Flagship()) extraCrew = player.Flagship()->Crew() - player.Flagship()->RequiredCrew(); return (cargoSize <= player.Cargo().Free() + player.Cargo().CommoditiesSize() && passengers <= player.Cargo().Bunks() + extraCrew); }
ConversationPanel::ConversationPanel(PlayerInfo &player, const Conversation &conversation, const System *system) : player(player), conversation(conversation), scroll(0), system(system) { subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); Goto(0); }
HailPanel::HailPanel(PlayerInfo &player, const shared_ptr<Ship> &ship) : player(player), ship(ship), sprite(ship->GetSprite().GetSprite()), unit(2. * ship->Unit()) { SetInterruptible(false); const Government *gov = ship->GetGovernment(); header = gov->GetName() + " ship \"" + ship->Name() + "\":"; if(gov->IsEnemy()) { SetBribe(gov->GetBribeFraction()); if(bribe) message = "If you want us to leave you alone, it'll cost you " + Format::Number(bribe) + " credits."; } else if(ship->IsDisabled()) { const Ship *flagship = player.Flagship(); if(!flagship->JumpsRemaining() || flagship->IsDisabled()) message = "Sorry, we can't help you, because our ship is disabled."; else message = "Our ship has been disabled! Please come board our ship and patch us up!"; } else { // Is the player in any need of assistance? const Ship *flagship = player.Flagship(); // Check if the player is out of fuel. if(!flagship->JumpsRemaining()) { playerNeedsHelp = true; canGiveFuel = ship->CanRefuel(*flagship); } // Check if the player is disabled. if(flagship->IsDisabled()) { playerNeedsHelp = true; canRepair = true; } if(canGiveFuel || canRepair) message = "Looks like you've gotten yourself into a bit of trouble. " "Would you like us to "; if(canGiveFuel && canRepair) message += "patch you up and give you some fuel?"; else if(canGiveFuel) message += "give you some fuel?"; else if(canRepair) message += "patch you up?"; } if(message.empty()) message = ship->GetHail(); }
MapPanel::MapPanel(PlayerInfo &player, int commodity, const System *special) : player(player), distance(player), playerSystem(player.Flagship()->GetSystem()), selectedSystem(special ? special : player.Flagship()->GetSystem()), specialSystem(special), commodity(commodity) { SetIsFullScreen(true); center = Point(0., 0.) - selectedSystem->Position(); }
// If a player is given, the map will only use hyperspace paths known to the // player; that is, one end of the path has been visited. Also, if the // player's flagship has a jump drive, the jumps will be make use of it. DistanceMap::DistanceMap(const PlayerInfo &player, const System *center) : player(&player) { if(!player.Flagship()) return; if(!center) center = player.Flagship()->GetSystem(); if(!center) return; Init(center, player.Flagship()); }
// Constructor. ConversationPanel::ConversationPanel(PlayerInfo &player, const Conversation &conversation, const System *system, const Ship *ship) : player(player), conversation(conversation), scroll(0.), system(system) { // These substitutions need to be applied on the fly as each paragraph of // text is prepared for display. subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(ship) subs["<ship>"] = ship->Name(); else if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); // Begin at the start of the conversation. Goto(0); }
ShopPanel::ShopPanel(PlayerInfo &player, const vector<string> &categories) : player(player), planet(player.GetPlanet()), playerShip(player.Flagship()), categories(categories) { if(playerShip) playerShips.insert(playerShip); SetIsFullScreen(true); }
// Check if this action can be completed right now. It cannot be completed // if it takes away money or outfits that the player does not have. bool MissionAction::CanBeDone(const PlayerInfo &player) const { if(player.Accounts().Credits() < -payment) return false; const Ship *flagship = player.Flagship(); for(const auto &it : gifts) { if(it.second > 0) continue; // The outfit can be taken from the player's cargo or from the flagship. int available = player.Cargo().Get(it.first); for(const auto &ship : player.Ships()) available += ship->Cargo().Get(it.first); if(flagship) available += flagship->OutfitCount(it.first); // If the gift "count" is 0, that means to check that the player has at // least one of these items. if(available < -it.second + !it.second) return false; } return true; }
ShipInfoPanel::ShipInfoPanel(PlayerInfo &player, int index) : player(player), shipIt(player.Ships().begin()), canEdit(player.GetPlanet()) { SetInterruptible(false); // If a valid ship index was given, show that ship. if(static_cast<unsigned>(index) < player.Ships().size()) shipIt += index; else if(player.Flagship()) { // Find the player's flagship. It may not be first in the list, if the // first item in the list cannot be a flagship. while(shipIt != player.Ships().end() && shipIt->get() != player.Flagship()) ++shipIt; } UpdateInfo(); }
InfoPanel::InfoPanel(PlayerInfo &player, bool showFlagship) : player(player), shipIt(player.Ships().begin()), showShip(showFlagship), canEdit(player.GetPlanet()) { SetInterruptible(false); if(showFlagship) while(shipIt != player.Ships().end() && shipIt->get() != player.Flagship()) ++shipIt; UpdateInfo(); }
Engine::Engine(PlayerInfo &player) : player(player), calcTickTock(false), drawTickTock(false), terminate(false), step(0), flash(0.), doFlash(false), wasLeavingHyperspace(false), load(0.), loadCount(0), loadSum(0.) { // Start the thread for doing calculations. calcThread = thread(&Engine::ThreadEntryPoint, this); if(!player.IsLoaded() || !player.GetSystem()) return; // Preload any landscapes for this system. for(const StellarObject &object : player.GetSystem()->Objects()) if(object.GetPlanet()) GameData::Preload(object.GetPlanet()->Landscape()); // Now we know the player's current position. Draw the planets. Point center; if(player.GetPlanet()) { for(const StellarObject &object : player.GetSystem()->Objects()) if(object.GetPlanet() == player.GetPlanet()) center = object.Position(); } for(const StellarObject &object : player.GetSystem()->Objects()) if(!object.GetSprite().IsEmpty()) { Point position = object.Position(); Point unit = object.Unit(); position -= center; int type = object.IsStar() ? Radar::SPECIAL : !object.GetPlanet() ? Radar::INACTIVE : object.GetPlanet()->IsWormhole() ? Radar::ANOMALOUS : GameData::GetPolitics().HasDominated(object.GetPlanet()) ? Radar::PLAYER : object.GetPlanet()->CanLand() ? Radar::FRIENDLY : Radar::HOSTILE; double r = max(2., object.Radius() * .03 + .5); draw[calcTickTock].Add(object.GetSprite(), position, unit); radar[calcTickTock].Add(type, position, r, r - 1.); } // Add all neighboring systems to the radar. const Ship *flagship = player.Flagship(); const System *targetSystem = flagship ? flagship->GetTargetSystem() : nullptr; const vector<const System *> &links = (flagship && flagship->Attributes().Get("jump drive")) ? player.GetSystem()->Neighbors() : player.GetSystem()->Links(); for(const System *system : links) radar[calcTickTock].AddPointer( (system == targetSystem) ? Radar::SPECIAL : Radar::INACTIVE, system->Position() - player.GetSystem()->Position()); }
Engine::Engine(PlayerInfo &player) : player(player) { // Start the thread for doing calculations. calcThread = thread(&Engine::ThreadEntryPoint, this); if(!player.IsLoaded() || !player.GetSystem()) return; // Preload any landscapes for this system. for(const StellarObject &object : player.GetSystem()->Objects()) if(object.GetPlanet()) GameData::Preload(object.GetPlanet()->Landscape()); // Figure out what planet the player is landed on, if any. const StellarObject *object = player.GetStellarObject(); if(object) center = object->Position(); // Now we know the player's current position. Draw the planets. draw[calcTickTock].SetCenter(center); radar[calcTickTock].SetCenter(center); for(const StellarObject &object : player.GetSystem()->Objects()) if(object.HasSprite()) { draw[calcTickTock].Add(object); double r = max(2., object.Radius() * .03 + .5); radar[calcTickTock].Add(RadarType(object), object.Position(), r, r - 1.); } // Add all neighboring systems to the radar. const Ship *flagship = player.Flagship(); const System *targetSystem = flagship ? flagship->GetTargetSystem() : nullptr; const vector<const System *> &links = (flagship && flagship->Attributes().Get("jump drive")) ? player.GetSystem()->Neighbors() : player.GetSystem()->Links(); for(const System *system : links) radar[calcTickTock].AddPointer( (system == targetSystem) ? Radar::SPECIAL : Radar::INACTIVE, system->Position() - player.GetSystem()->Position()); }
void MissionAction::Do(PlayerInfo &player, UI *ui, const System *destination) const { bool isOffer = (trigger == "offer"); if(!conversation.IsEmpty() && ui) { ConversationPanel *panel = new ConversationPanel(player, conversation, destination); if(isOffer) panel->SetCallback(&player, &PlayerInfo::MissionCallback); ui->Push(panel); } else if(!dialogText.empty() && ui) { map<string, string> subs; subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); string text = Format::Replace(dialogText, subs); if(isOffer) ui->Push(new Dialog(text, player, destination)); else ui->Push(new Dialog(text)); } else if(isOffer && ui) player.MissionCallback(Conversation::ACCEPT); // If multiple outfits are being transferred, first remove them before // adding any new ones. for(const auto &it : gifts) if(it.second < 0) DoGift(player, it.first, it.second, ui); for(const auto &it : gifts) if(it.second > 0) DoGift(player, it.first, it.second, ui); if(payment) player.Accounts().AddCredits(payment); for(const auto &it : events) player.AddEvent(*GameData::Events().Get(it.first), player.GetDate() + it.second); if(!fail.empty()) { // Failing missions invalidates iterators into the player's mission list, // but does not immediately delete those missions. So, the safe way to // iterate over all missions is to make a copy of the list before we // begin to remove items from it. vector<const Mission *> failedMissions; for(const Mission &mission : player.Missions()) if(fail.count(mission.Identifier())) failedMissions.push_back(&mission); for(const Mission *mission : failedMissions) player.RemoveMission(Mission::FAIL, *mission, ui); } // Check if applying the conditions changes the player's reputations. player.SetReputationConditions(); conditions.Apply(player.Conditions()); player.CheckReputationConditions(); }
void AI::Step(const list<shared_ptr<Ship>> &ships, const PlayerInfo &player) { const Ship *flagship = player.Flagship(); step = (step + 1) & 31; int targetTurn = 0; for(const auto &it : ships) { // Skip any carried fighters or drones that are somehow in the list. if(!it->GetSystem()) continue; if(it.get() == flagship) { MovePlayer(*it, player, ships); continue; } bool isPresent = (it->GetSystem() == player.GetSystem()); bool isStranded = !it->JumpsRemaining() && it->Attributes().Get("fuel capacity") && !it->GetSystem()->IsInhabited(); if(isStranded || it->IsDisabled()) { if(it->IsDestroyed() || (it->IsDisabled() && it->IsYours()) || it->GetPersonality().IsDerelict()) continue; bool hasEnemy = false; Ship *firstAlly = nullptr; bool selectNext = false; Ship *nextAlly = nullptr; const Government *gov = it->GetGovernment(); for(const auto &ship : ships) { if(ship->IsDisabled() || !ship->IsTargetable() || ship->GetSystem() != it->GetSystem()) continue; const Government *otherGov = ship->GetGovernment(); // If any enemies of this ship are in system, it cannot call for help. if(otherGov->IsEnemy(gov) && isPresent) { hasEnemy = true; break; } if((otherGov->IsPlayer() && !gov->IsPlayer()) || ship.get() == flagship) continue; if(it->IsDisabled() ? (otherGov == gov) : (!otherGov->IsEnemy(gov))) { if(isStranded && !ship->CanRefuel(*it)) continue; if(!firstAlly) firstAlly = &*ship; else if(ship == it) selectNext = true; else if(selectNext && !nextAlly) nextAlly = &*ship; } } isStranded = false; if(!hasEnemy) { if(!nextAlly) nextAlly = firstAlly; if(nextAlly) { nextAlly->SetShipToAssist(it); isStranded = true; } } if(it->IsDisabled()) continue; } Command command; if(it->IsYours()) { if(isLaunching) command |= Command::DEPLOY; if(isCloaking) command |= Command::CLOAK; } const Personality &personality = it->GetPersonality(); shared_ptr<Ship> parent = it->GetParent(); if(isPresent && personality.IsSurveillance()) { DoSurveillance(*it, command, ships); it->SetCommands(command); continue; } // Fire any weapons that will hit the target. Only ships that are in // the current system can fire. shared_ptr<const Ship> target = it->GetTargetShip(); if(isPresent) { command |= AutoFire(*it, ships); // Each ship only switches targets twice a second, so that it can // focus on damaging one particular ship. targetTurn = (targetTurn + 1) & 31; if(targetTurn == step || !target || !target->IsTargetable() || (target->IsDisabled() && personality.Disables())) it->SetTargetShip(FindTarget(*it, ships)); } double targetDistance = numeric_limits<double>::infinity(); target = it->GetTargetShip(); if(target) targetDistance = target->Position().Distance(it->Position()); // Handle fighters: const string &category = it->Attributes().Category(); bool isDrone = (category == "Drone"); bool isFighter = (category == "Fighter"); if(isDrone || isFighter) { bool hasSpace = true; hasSpace &= parent && (!isDrone || parent->DroneBaysFree()); hasSpace &= parent && (!isFighter || parent->FighterBaysFree()); if(!hasSpace || parent->IsDestroyed() || parent->GetSystem() != it->GetSystem()) { // Handle orphaned fighters and drones. for(const auto &other : ships) if(other->GetGovernment() == it->GetGovernment() && !other->IsDisabled() && other->GetSystem() == it->GetSystem()) if((isDrone && other->DroneBaysFree()) || (isFighter && other->FighterBaysFree())) { it->SetParent(other); break; } } else if(parent && !(it->IsYours() ? isLaunching : parent->Commands().Has(Command::DEPLOY))) { it->SetTargetShip(parent); MoveTo(*it, command, parent->Position(), 40., .8); command |= Command::BOARD; it->SetCommands(command); continue; } } bool mustRecall = false; if(it->HasBays() && !(it->IsYours() ? isLaunching : it->Commands().Has(Command::DEPLOY)) && !target) for(const weak_ptr<const Ship> &ptr : it->GetEscorts()) { shared_ptr<const Ship> escort = ptr.lock(); if(escort && escort->CanBeCarried() && escort->GetSystem() == it->GetSystem() && !escort->IsDisabled()) { mustRecall = true; break; } } shared_ptr<Ship> shipToAssist = it->GetShipToAssist(); if(shipToAssist) { it->SetTargetShip(shipToAssist); if(shipToAssist->IsDestroyed() || shipToAssist->GetSystem() != it->GetSystem()) it->SetShipToAssist(shared_ptr<Ship>()); else if(!it->IsBoarding()) { MoveTo(*it, command, shipToAssist->Position(), 40., .8); command |= Command::BOARD; } it->SetCommands(command); continue; } bool isPlayerEscort = it->IsYours(); if((isPlayerEscort && holdPosition) || mustRecall || isStranded) { if(it->Velocity().Length() > .2 || !target) Stop(*it, command); else command.SetTurn(TurnToward(*it, TargetAim(*it))); } // Hostile "escorts" (i.e. NPCs that are trailing you) only revert to // escort behavior when in a different system from you. Otherwise, // the behavior depends on what the parent is doing, whether there // are hostile targets nearby, and whether the escort has any // immediate needs (like refueling). else if(!parent || parent->IsDestroyed() || (parent->IsDisabled() && !isPlayerEscort)) MoveIndependent(*it, command); else if(parent->GetSystem() != it->GetSystem()) { if(personality.IsStaying()) MoveIndependent(*it, command); else MoveEscort(*it, command); } // From here down, we're only dealing with ships that have a "parent" // which is in the same system as them. If you're an enemy of your // "parent," you don't take orders from them. else if(personality.IsStaying() || parent->GetGovernment()->IsEnemy(it->GetGovernment())) MoveIndependent(*it, command); // This is a friendly escort. If the parent is getting ready to // jump, always follow. else if(parent->Commands().Has(Command::JUMP)) MoveEscort(*it, command); // If the player is ordering escorts to gather, don't go off to fight. else if(isPlayerEscort && moveToMe) MoveEscort(*it, command); // On the other hand, if the player ordered you to attack, do so even // if you're usually more timid than that. else if(isPlayerEscort && sharedTarget.lock()) MoveIndependent(*it, command); // Timid ships always stay near their parent. else if(personality.IsTimid() && parent->Position().Distance(it->Position()) > 500.) MoveEscort(*it, command); // Otherwise, attack targets depending on how heroic you are. else if(target && (targetDistance < 2000. || personality.IsHeroic())) MoveIndependent(*it, command); // This ship does not feel like fighting. else MoveEscort(*it, command); // Apply the afterburner if you're in a heated battle and it will not // use up your last jump worth of fuel. if(it->Attributes().Get("afterburner thrust") && target && !target->IsDisabled() && target->IsTargetable() && target->GetSystem() == it->GetSystem()) { double fuel = it->Fuel() * it->Attributes().Get("fuel capacity"); if(fuel - it->Attributes().Get("afterburner fuel") >= it->JumpFuel()) if(command.Has(Command::FORWARD) && targetDistance < 1000.) command |= Command::AFTERBURNER; } // Your own ships cloak on your command; all others do it when the // AI considers it appropriate. if(!it->IsYours()) DoCloak(*it, command, ships); // Force ships that are overlapping each other to "scatter": DoScatter(*it, command, ships); it->SetCommands(command); } }
void MissionAction::Do(PlayerInfo &player, UI *ui, const System *destination) const { bool isOffer = (trigger == "offer"); if(!conversation.IsEmpty()) { ConversationPanel *panel = new ConversationPanel(player, conversation, destination); if(isOffer) panel->SetCallback(&player, &PlayerInfo::MissionCallback); ui->Push(panel); } else if(!dialogText.empty()) { map<string, string> subs; subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); string text = Format::Replace(dialogText, subs); if(isOffer) ui->Push(new Dialog(text, player, destination)); else ui->Push(new Dialog(text)); } else if(isOffer && ui) player.MissionCallback(Conversation::ACCEPT); Ship *flagship = player.Flagship(); for(const auto &it : gifts) { int count = it.second; string name = it.first->Name(); if(!count || name.empty()) continue; string message; if(abs(count) == 1) { char c = tolower(name.front()); bool isVowel = (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); message = (isVowel ? "An " : "A ") + name + " was "; } else message = to_string(abs(count)) + " " + name + "s were "; if(count > 0) message += "added to your "; else message += "removed from your "; bool didCargo = false; bool didShip = false; int cargoCount = player.Cargo().Get(it.first); if(count < 0 && cargoCount) { int moved = min(cargoCount, -count); count += moved; player.Cargo().Transfer(it.first, moved); didCargo = true; } while(flagship && count) { int moved = (count > 0) ? 1 : -1; if(flagship->Attributes().CanAdd(*it.first, moved)) { flagship->AddOutfit(it.first, moved); didShip = true; } else break; count -= moved; } if(count > 0) { player.Cargo().Transfer(it.first, -count); didCargo = true; if(count > 0) { string special = "The " + name + (count == 1 ? " was" : "s were"); special += " put in your cargo hold because there is not enough space to install "; special += (count == 1) ? "it" : "them"; special += " in your ship."; ui->Push(new Dialog(special)); } } if(didCargo && didShip) message += "cargo hold and your flagship."; else if(didCargo) message += "cargo hold."; else message += "flagship."; Messages::Add(message); } if(payment) player.Accounts().AddCredits(payment); for(const auto &it : events) player.AddEvent(*GameData::Events().Get(it.first), player.GetDate() + it.second); conditions.Apply(player.Conditions()); }
void AI::UpdateKeys(PlayerInfo &player, bool isActive) { shift = (SDL_GetModState() & KMOD_SHIFT); Command oldHeld = keyHeld; keyHeld.ReadKeyboard(); keyDown = keyHeld.AndNot(oldHeld); if(keyHeld.Has(AutopilotCancelKeys())) keyStuck.Clear(); if(keyStuck.Has(Command::JUMP) && !player.HasTravelPlan()) keyStuck.Clear(Command::JUMP); const Ship *flagship = player.Flagship(); if(!isActive || !flagship || flagship->IsDestroyed()) return; // Only toggle the "cloak" command if one of your ships has a cloaking device. if(keyDown.Has(Command::CLOAK)) for(const auto &it : player.Ships()) if(it->Attributes().Get("cloak")) { isCloaking = !isCloaking; Messages::Add(isCloaking ? "Engaging cloaking device." : "Disengaging cloaking device."); break; } // Toggle your secondary weapon. if(keyDown.Has(Command::SELECT)) player.SelectNext(); // The commands below here only apply if you have escorts or fighters. if(player.Ships().size() < 2) return; // Only toggle the "deploy" command if one of your ships has fighter bays. if(keyDown.Has(Command::DEPLOY)) for(const auto &it : player.Ships()) if(it->HasBays()) { isLaunching = !isLaunching; Messages::Add(isLaunching ? "Deploying fighters" : "Recalling fighters."); break; } shared_ptr<Ship> target = flagship->GetTargetShip(); if(keyDown.Has(Command::FIGHT) && target) { sharedTarget = target; holdPosition = false; moveToMe = false; Messages::Add("All your ships are focusing their fire on \"" + target->Name() + "\"."); } if(keyDown.Has(Command::HOLD)) { sharedTarget.reset(); holdPosition = !holdPosition; moveToMe = false; Messages::Add(holdPosition ? "Your fleet is holding position." : "Your fleet is no longer holding position."); } if(keyDown.Has(Command::GATHER)) { sharedTarget.reset(); holdPosition = false; moveToMe = !moveToMe; Messages::Add(moveToMe ? "Your fleet is gathering around your flagship." : "Your fleet is no longer gathering around your flagship."); } if(sharedTarget.lock() && sharedTarget.lock()->IsDisabled()) sharedTarget.reset(); }