Exemple #1
0
void Planet::SetSystem(int sys) {
    //Logger().debugStream() << "Planet::MoveTo(UniverseObject* object)";
    UniverseObject::SetSystem(sys);
    for (std::set<int>::const_iterator it = m_buildings.begin(); it != m_buildings.end(); ++it) {
        UniverseObject* obj = GetUniverseObject(*it);
        if (!obj) {
            Logger().errorStream() << "Planet::SetSystem couldn't get building object with id " << *it;
            continue;
        }
        obj->SetSystem(sys);
    }
}
Exemple #2
0
void Planet::Conquer(int conquerer) {
    if (conquerer == ALL_EMPIRES)
        return;

    m_just_conquered = true;
    Empire* empire = Empires().Lookup(conquerer);
    if (!empire) {
        Logger().errorStream() << "Planet::Conquer: attempted to conquer a planet with an invalid conquerer with id: " << conquerer;
        return;
    }

    // deal with things on production queue located at this planet
    empire->ConquerBuildsAtLocation(ID());

    // deal with UniverseObjects (eg. buildings) located on this planet
    std::vector<UniverseObject*> contained_objects = this->FindObjects();
    for (std::vector<UniverseObject*>::iterator it = contained_objects.begin(); it != contained_objects.end(); ++it) {
        UniverseObject* obj = *it;

        // Buildings:
        if (Building* building = universe_object_cast<Building*>(obj)) {
            const BuildingType* type = building->GetBuildingType();

            // determine what to do with building of this type...
            const CaptureResult cap_result = type->GetCaptureResult(obj->Owner(), conquerer, this->ID(), false);

            if (cap_result == CR_CAPTURE) {
                // replace ownership
                obj->SetOwner(conquerer);
            } else if (cap_result == CR_DESTROY) {
                // destroy object
                Logger().debugStream() << "Planet::Conquer destroying object: " << obj->Name();
                GetUniverse().Destroy(obj->ID());
                obj = 0;
            } else if (cap_result == CR_RETAIN) {
                // do nothing
            }
        }

        // TODO: deal with any other UniverseObject subclasses...?
    }

    // replace ownership
    SetOwner(conquerer);
}
Exemple #3
0
void AutoResolveCombat(CombatInfo& combat_info) {
    if (combat_info.objects.Empty())
        return;

    const System* system = combat_info.objects.Object<System>(combat_info.system_id);
    if (!system)
        Logger().errorStream() << "AutoResolveCombat couldn't get system with id " << combat_info.system_id;
    else
        Logger().debugStream() << "AutoResolveCombat at " << system->Name();

    if (GetOptionsDB().Get<bool>("verbose-logging")) {
        Logger().debugStream() << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%";
        Logger().debugStream() << "AutoResolveCombat objects before resolution: " << combat_info.objects.Dump();
    }

    // reasonably unpredictable but reproducible random seeding
    const int base_seed = combat_info.objects.begin()->first + CurrentTurn();


    // compile list of valid objects to attack or be attacked in this combat
    std::set<int> valid_target_object_ids;                          // all objects that can be attacked
    std::set<int> valid_attacker_object_ids;                        // all objects that can attack
    std::map<int, std::set<int> > empire_valid_attacker_object_ids; // objects that can attack that each empire owns
    float monster_detection = 0.0;

    for (ObjectMap::iterator it = combat_info.objects.begin(); it != combat_info.objects.end(); ++it) {
        const UniverseObject* obj = it->second;
        //Logger().debugStream() << "Considerting object " << obj->Name() << " owned by " << obj->Owner();
        if (ObjectCanAttack(obj)) {
            //Logger().debugStream() << "... can attack";
            valid_attacker_object_ids.insert(it->first);
            empire_valid_attacker_object_ids[obj->Owner()].insert(it->first);
        }
        if (ObjectCanBeAttacked(obj)) {
            //Logger().debugStream() << "... can be attacked";
            valid_target_object_ids.insert(it->first);
        }
        if (obj->Unowned() && obj->ObjectType() == OBJ_SHIP)
            monster_detection = std::max(monster_detection, obj->CurrentMeterValue(METER_DETECTION));
    }


    // map from empire to set of IDs of objects that empire's objects
    // could potentially target.
    std::map<int, std::set<int> > empire_valid_target_object_ids;   // objects that each empire can attack
    for (std::set<int>::const_iterator target_it = valid_target_object_ids.begin();
         target_it != valid_target_object_ids.end(); ++target_it)
    {
        int object_id = *target_it;
        const UniverseObject* obj = combat_info.objects.Object(object_id);
        //Logger().debugStream() << "Considering attackability of object " << obj->Name() << " owned by " << obj->Owner();

        // for all empires, can they attack this object?
        for (std::set<int>::const_iterator empire_it = combat_info.empire_ids.begin();
             empire_it != combat_info.empire_ids.end(); ++empire_it)
        {
            int attacking_empire_id = *empire_it;
            if (attacking_empire_id == ALL_EMPIRES) {
                if (ObjectAttackableByMonsters(obj, monster_detection)) {
                    //Logger().debugStream() << "object: " << obj->Name() << " attackable by monsters";
                    empire_valid_target_object_ids[ALL_EMPIRES].insert(object_id);
                }

            } else {
                // call function to find if empires can attack objects...
                if (ObjectAttackableByEmpire(obj, attacking_empire_id)) {
                    //Logger().debugStream() << "object: " << obj->Name() << " attackable by empire " << attacking_empire_id;
                    empire_valid_target_object_ids[attacking_empire_id].insert(object_id);
                }
            }
        }
    }


    // Each combat "round" a randomly-selected object in the battle attacks
    // something, if it is able to do so.  The number of rounds scales with the
    // number of objects, so the total actions per object is independent of
    // number of objects in the battle
    const int NUM_COMBAT_ROUNDS = 3*valid_attacker_object_ids.size();

    for (int round = 1; round <= NUM_COMBAT_ROUNDS; ++round) {
        Seed(base_seed + round);    // ensure each combat round produces different results

        // ensure something can attack and something can be attacked
        if (valid_attacker_object_ids.empty()) {
            Logger().debugStream() << "Nothing left can attack; combat over";
            break;
        }
        if (empire_valid_target_object_ids.empty()) {
            Logger().debugStream() << "Nothing left can be attacked; combat over";
            break;
        }
        // empires may have valid targets, but nothing to attack with.  If all
        // empires have no attackers or no valid targers, combat is over
        bool someone_can_attack_something = false;
        for (std::map<int, std::set<int> >::const_iterator attacker_it = empire_valid_attacker_object_ids.begin();
             attacker_it != empire_valid_attacker_object_ids.end(); ++attacker_it)
        {
            if (empire_valid_target_object_ids.find(attacker_it->first) != empire_valid_target_object_ids.end()) {
                someone_can_attack_something = true;
                break;
            }
        }
        if (!someone_can_attack_something) {
            Logger().debugStream() << "No empire has valid targets and something to attack with; combat over.";
            break;
        }

        Logger().debugStream() << "Combat at " << system->Name() << " (" << combat_info.system_id << ") Round " << round;

        // select attacking object in battle
        SmallIntDistType attacker_id_num_dist = SmallIntDist(0, valid_attacker_object_ids.size() - 1);
        std::set<int>::const_iterator attacker_it = valid_attacker_object_ids.begin();
        std::advance(attacker_it, attacker_id_num_dist());
        assert(attacker_it != valid_attacker_object_ids.end());
        int attacker_id = *attacker_it;

        UniverseObject* attacker = combat_info.objects.Object(attacker_id);
        if (!attacker) {
            Logger().errorStream() << "AutoResolveCombat couldn't get object with id " << attacker_id;
            continue;
        }
        Logger().debugStream() << "Attacker: " << attacker->Name();


        Ship* attack_ship = universe_object_cast<Ship*>(attacker);
        Planet* attack_planet = universe_object_cast<Planet*>(attacker);

        // loop over weapons of attacking object.  each gets a shot at a
        // randomly selected target object
        std::vector<PartAttackInfo> weapons;

        if (attack_ship) {
            weapons = ShipWeaponsStrengths(attack_ship);
            for (std::vector<PartAttackInfo>::const_iterator part_it = weapons.begin();
                 part_it != weapons.end(); ++part_it)
            {
                Logger().debugStream() << "weapon: " << part_it->part_type_name <<
                                          " attack: " << part_it->part_attack;
            }
        } else if (attack_planet) { // treat planet defenses as short range
            weapons.push_back(PartAttackInfo(PC_SHORT_RANGE, "", attack_planet->CurrentMeterValue(METER_DEFENSE)));
        }

        if (weapons.empty()) {
            Logger().debugStream() << "no weapons' can't attack";
            continue;   // no ability to attack!
        }

        for (std::vector<PartAttackInfo>::const_iterator weapon_it = weapons.begin();
             weapon_it != weapons.end(); ++weapon_it)
        {
            // select object from valid targets for this object's owner   TODO: with this weapon...
            Logger().debugStream() << "Attacking with weapon " << weapon_it->part_type_name << " with power " << weapon_it->part_attack;

            // get valid targets set for attacker owner.  need to do this for
            // each weapon that is attacking, as the previous shot might have
            // destroyed something
            int attacker_owner_id = attacker->Owner();

            std::map<int, std::set<int> >::iterator target_vec_it = empire_valid_target_object_ids.find(attacker_owner_id);
            if (target_vec_it == empire_valid_target_object_ids.end()) {
                Logger().debugStream() << "No targets for attacker with id: " << attacker_owner_id;
                break;
            }

            const std::set<int>& valid_target_ids = target_vec_it->second;
            if (valid_target_ids.empty())
                break; // should be redundant with this entry being erased when emptied


            // select target object
            SmallIntDistType target_id_num_dist = SmallIntDist(0, valid_target_ids.size() - 1);
            std::set<int>::const_iterator target_it = valid_target_ids.begin();
            std::advance(target_it, target_id_num_dist());
            assert(target_it != valid_target_ids.end());
            int target_id = *target_it;

            UniverseObject* target = combat_info.objects.Object(target_id);
            if (!target) {
                Logger().errorStream() << "AutoResolveCombat couldn't get target object with id " << target_id;
                continue;
            }
            Logger().debugStream() << "Target: " << target->Name();


            // do actual attacks, and mark attackers as valid targets for attacked object's owners
            if (attack_ship) {
                if (Ship* target_ship = universe_object_cast<Ship*>(target)) {
                    AttackShipShip(attack_ship, weapon_it->part_attack, target_ship, combat_info.damaged_object_ids);
                    empire_valid_target_object_ids[target_ship->Owner()].insert(attacker_id);
                } else if (Planet* target_planet = universe_object_cast<Planet*>(target)) {
                    AttackShipPlanet(attack_ship, weapon_it->part_attack,  target_planet, combat_info.damaged_object_ids);
                    empire_valid_target_object_ids[target_planet->Owner()].insert(attacker_id);
                }
            } else if (attack_planet) {
                if (Ship* target_ship = universe_object_cast<Ship*>(target)) {
                    AttackPlanetShip(attack_planet, target_ship, combat_info.damaged_object_ids);
                    empire_valid_target_object_ids[target_ship->Owner()].insert(attacker_id);
                } else if (Planet* target_planet = universe_object_cast<Planet*>(target)) {
                    AttackPlanetPlanet(attack_planet, target_planet, combat_info.damaged_object_ids);
                    empire_valid_target_object_ids[target_planet->Owner()].insert(attacker_id);
                }
            }


            // check for destruction of target object
            if (target->ObjectType() == OBJ_SHIP) {
                if (target->CurrentMeterValue(METER_STRUCTURE) <= 0.0) {
                    Logger().debugStream() << "!! Target Ship is destroyed!";
                    // object id destroyed
                    combat_info.destroyed_object_ids.insert(target_id);
                    // all empires in battle know object was destroyed
                    for (std::set<int>::const_iterator it = combat_info.empire_ids.begin();
                         it != combat_info.empire_ids.end(); ++it)
                    {
                        int empire_id = *it;
                        if (empire_id != ALL_EMPIRES)
                            combat_info.destroyed_object_knowers[empire_id].insert(target_id);
                    }

                    // remove destroyed ship's ID from lists of valid attackers and targets
                    valid_attacker_object_ids.erase(target_id);
                    valid_target_object_ids.erase(target_id);   // probably not necessary as this set isn't used in this loop

                    for (target_vec_it = empire_valid_target_object_ids.begin();
                         target_vec_it != empire_valid_target_object_ids.end(); ++target_vec_it)
                    { target_vec_it->second.erase(target_id); }

                    for (target_vec_it = empire_valid_attacker_object_ids.begin();
                         target_vec_it != empire_valid_attacker_object_ids.end(); ++target_vec_it)
                    { target_vec_it->second.erase(target_id); } // TODO: only erase from owner's entry in this list
                }

            } else if (target->ObjectType() == OBJ_PLANET) {
                if (target->CurrentMeterValue(METER_SHIELD) <= 0.0 &&
                    target->CurrentMeterValue(METER_DEFENSE) <= 0.0 &&
                    target->CurrentMeterValue(METER_CONSTRUCTION) <= 0.0)
                {
                    Logger().debugStream() << "!! Target Planet is knocked out of battle";

                    // remove disabled planet's ID from lists of valid attackers and targets
                    valid_attacker_object_ids.erase(target_id);
                    valid_target_object_ids.erase(target_id);   // probably not necessary as this set isn't used in this loop

                    for (target_vec_it = empire_valid_target_object_ids.begin();
                         target_vec_it != empire_valid_target_object_ids.end(); ++target_vec_it)
                    { target_vec_it->second.erase(target_id); }

                    for (target_vec_it = empire_valid_attacker_object_ids.begin();
                         target_vec_it != empire_valid_attacker_object_ids.end(); ++target_vec_it)
                    { target_vec_it->second.erase(target_id); } // TODO: only erase from owner's entry in this list
                }
            }

            // check if any empire has no remaining target or attacker objects.
            // If so, remove that empire's entry
            std::map<int, std::set<int> > temp = empire_valid_target_object_ids;
            for (target_vec_it = empire_valid_target_object_ids.begin();
                 target_vec_it != empire_valid_target_object_ids.end(); ++target_vec_it)
            {
                if (target_vec_it->second.empty()) {
                    temp.erase(target_vec_it->first);
                    Logger().debugStream() << "No valid targets left for empire with id: " << target_vec_it->first;
                }
            }
            empire_valid_target_object_ids = temp;

            temp = empire_valid_attacker_object_ids;
            for (target_vec_it = empire_valid_attacker_object_ids.begin();
                 target_vec_it != empire_valid_attacker_object_ids.end(); ++target_vec_it)
            {
                if (target_vec_it->second.empty()) {
                    temp.erase(target_vec_it->first);
                    Logger().debugStream() << "No valid attacking objects left for empire with id: " << target_vec_it->first;
                }
            }
            empire_valid_attacker_object_ids = temp;
        } // end for over weapons
    } // end for over combat arounds

    // ensure every participant knows what happened.
    // TODO: assemble list of objects to copy for each empire.  this should
    //       include objects the empire already knows about with standard
    //       visibility system, and also any objects the empire knows are
    //       destroyed or
    for (std::map<int, ObjectMap>::iterator it = combat_info.empire_known_objects.begin();
         it != combat_info.empire_known_objects.end(); ++it)
    { it->second.Copy(combat_info.objects); }

    if (GetOptionsDB().Get<bool>("verbose-logging"))
        Logger().debugStream() << "AutoResolveCombat objects after resolution: " << combat_info.objects.Dump();
}