Ticket #979: entity_randomization_svn_25feb12.patch

File entity_randomization_svn_25feb12.patch, 10.3 KB (added by vts, 12 years ago)
  • source/graphics/ObjectBase.cpp

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    335335
    336336        // Remember which props were chosen, so we can call CalculateVariationKey on them
    337337        // at the end.
    338         CObjectBase::Variant& var ((*grp)[match]);
    339         for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     338        Variant& var ((*grp)[match]);
     339        for (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    340340        {
    341341            // Erase all existing props which are overridden by this variant:
    342             for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     342            for (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    343343                chosenProps.erase(it->m_PropPointName);
    344344            // and then insert the new ones:
    345             for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     345            for (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    346346                if (! it->m_ModelName.empty())
    347347                    chosenProps.insert(make_pair(it->m_PropPointName, it->m_ModelName));
    348348        }
     
    437437{
    438438    rng_t rng;
    439439    rng.seed(seed);
    440     return CalculateRandomVariation(rng, initialSelections);
     440
     441    std::set<CStr> remainingSelections = CalculateRandomRemainingSelections(rng, std::vector<std::set<CStr> >(1, initialSelections));
     442    remainingSelections.insert(initialSelections.begin(), initialSelections.end());
     443
     444    return remainingSelections; // now actually a complete set of selections
    441445}
    442446
    443 std::set<CStr> CObjectBase::CalculateRandomVariation(rng_t& rng, const std::set<CStr>& initialSelections)
     447std::set<CStr> CObjectBase::CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr> >& initialSelections)
    444448{
    445     std::set<CStr> selections = initialSelections;
     449    rng_t rng;
     450    rng.seed(seed);
     451    return CalculateRandomRemainingSelections(rng, initialSelections);
     452}
    446453
    447     std::multimap<CStr, CStrW> chosenProps;
     454std::set<CStr> CObjectBase::CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections)
     455{
     456    using std::set;
     457    using std::vector;
     458    using std::multimap;
    448459
     460    set<CStr> remainingSelections;
     461    multimap<CStr, CStrW> chosenProps;
     462
    449463    // Calculate a complete list of selections, so there is at least one
    450464    // (and in most cases only one) per group.
    451465    // In each group, if one of the variants has a name matching a string in
     
    456470    // When choosing randomly, make use of each variant's frequency. If all
    457471    // variants have frequency 0, treat them as if they were 1.
    458472
    459     for (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_VariantGroups.begin();
     473    for (vector<vector<Variant> >::iterator grp = m_VariantGroups.begin();
    460474        grp != m_VariantGroups.end();
    461475        ++grp)
    462476    {
     
    475489        else
    476490        {
    477491            // See if a variant (or several, but we only care about the first)
    478             // is already matched by the selections we've made
     492            // is already matched by the selections we've made, keeping their
     493            // priority order into account
    479494
    480             for (size_t i = 0; i < grp->size(); ++i)
     495            for (size_t s = 0; s < initialSelections.size(); ++s)
    481496            {
    482                 if (selections.count((*grp)[i].m_VariantName))
     497                for (size_t i = 0; i < grp->size(); ++i)
    483498                {
    484                     match = (int)i;
     499                    if (initialSelections[s].count((*grp)[i].m_VariantName))
     500                    {
     501                        match = (int)i;
     502                        break;
     503                    }
     504                }
     505
     506                if (match >= 0)
    485507                    break;
    486                 }
    487508            }
    488509
    489510            // If there was one, we don't need to do anything now because there's
     
    509530                    randNum -= (allZero ? 1 : (*grp)[i].m_Frequency);
    510531                    if (randNum < 0)
    511532                    {
    512                         selections.insert((*grp)[i].m_VariantName);
    513                         // (If this change to 'selections' interferes with earlier
    514                         // choices, then we'll get some non-fatal inconsistencies
    515                         // that just break the randomness. But that shouldn't
    516                         // happen, much.)
     533                        remainingSelections.insert((*grp)[i].m_VariantName);
     534                        // (If this change to 'remainingSelections' interferes with earlier choices, then
     535                        // we'll get some non-fatal inconsistencies that just break the randomness. But that
     536                        // shouldn't happen, much.)
     537                        // (As an example, suppose you have a group with variants "a" and "b", and another
     538                        // with variants "a" and "c"; now if random selection choses "b" for the first
     539                        // and "a" for the second, then the selection of "a" from the second group will
     540                        // cause "a" to be used in the first instead of the "b").
    517541                        match = (int)i;
    518542                        break;
    519543                    }
     
    526550
    527551        // Remember which props were chosen, so we can call CalculateRandomVariation on them
    528552        // at the end.
    529         CObjectBase::Variant& var ((*grp)[match]);
    530         for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     553        Variant& var ((*grp)[match]);
     554        for (vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    531555        {
    532556            // Erase all existing props which are overridden by this variant:
    533             for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     557            for (vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    534558                chosenProps.erase(it->m_PropPointName);
    535559            // and then insert the new ones:
    536             for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
     560            for (vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
    537561                if (! it->m_ModelName.empty())
    538562                    chosenProps.insert(make_pair(it->m_PropPointName, it->m_ModelName));
    539563        }
    540564    }
    541565
    542566    // Load each prop, and add their required selections to ours:
    543     for (std::multimap<CStr, CStrW>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)
     567    for (multimap<CStr, CStrW>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)
    544568    {
    545569        CObjectBase* prop = m_ObjectManager.FindObjectBase(it->second);
    546570        if (prop)
    547571        {
    548             std::set<CStr> propSelections = prop->CalculateRandomVariation(rng, selections);
    549             // selections = union(propSelections, selections)
    550             std::set<CStr> newSelections;
    551             std::set_union(propSelections.begin(), propSelections.end(),
    552                 selections.begin(), selections.end(),
    553                 std::inserter(newSelections, newSelections.begin()));
    554             selections.swap(newSelections);
     572            vector<set<CStr> > propInitialSelections = initialSelections;
     573            if (remainingSelections.size() > 0)
     574                propInitialSelections.push_back(remainingSelections);
    555575
     576            set<CStr> propRemainingSelections = prop->CalculateRandomRemainingSelections(rng, propInitialSelections);
     577            remainingSelections.insert(propRemainingSelections.begin(), propRemainingSelections.end());
     578
    556579            // Add the prop's used files to our own (recursively) so we can hotload
    557580            // when any prop is changed
    558581            m_UsedFiles.insert(prop->m_UsedFiles.begin(), prop->m_UsedFiles.end());
    559582        }
    560583    }
    561584
    562     return selections;
     585    return remainingSelections;
    563586}
    564587
    565588std::vector<std::vector<CStr> > CObjectBase::GetVariantGroups() const
  • source/graphics/ObjectBase.h

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    113113    // and choosing randomly where a choice is necessary.
    114114    std::set<CStr> CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections);
    115115
     116    // Given a prioritized vector of selection string sets that partially specify
     117    // a variation, calculates a remaining set of selection strings such that the resulting
     118    // set merged with the initial selections fully specifies an exact variation of
     119    // the actor. The resulting selections are selected randomly, but only where a choice
     120    // is necessary (i.e. where there are multiple variants but the initial selections,
     121    // applied in priority order, fail to select one).
     122    std::set<CStr> CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr> >& initialSelections);
     123
    116124    // Get a list of variant groups for this object, plus for all possible
    117125    // props. Duplicated groups are removed, if several props share the same
    118126    // variant names.
     
    160168    // so use a better one that appears to avoid those patterns
    161169    typedef boost::mt19937 rng_t;
    162170
    163     std::set<CStr> CalculateRandomVariation(rng_t& rng, const std::set<CStr>& initialSelections);
     171    std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections);
    164172
    165173    std::vector< std::vector<Variant> > m_VariantGroups;
    166174    CObjectManager& m_ObjectManager;
  • source/graphics/Unit.cpp

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    105105    selections.push_back(m_EntitySelections);
    106106    selections.push_back(m_ActorSelections);
    107107
     108    // randomly select any remain selections necessary to completely identify a variation (e.g., the new selection
     109    // made might define some additional props that require a random variant choice). Also, FindObjectVariation
     110    // expects the selectors passed to it to be complete.
     111    // see http://trac.wildfiregames.com/ticket/979
     112   
     113    // Use the entity ID as randomization seed (same as when the unit was first created)
     114    std::set<CStr> remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_ID, selections);
     115    if (remainingSelections.size() > 0)
     116        selections.push_back(remainingSelections);
     117
    108118    // If these selections give a different object, change this unit to use it
    109119    CObjectEntry* newObject = m_ObjectManager.FindObjectVariation(m_Object->m_Base, selections);
    110120    if (newObject && newObject != m_Object)