Re: [tsc-devel] Who calls cLevel_Player::Update?

Quintus | Wed, 21 Oct 2015 11:12:53 UTC

datahead <…r@f…> writes:

> Post via forum by datahead <…9@x…>:
> Who calls cLevel_Player::Update?
> Is it somehow called by cLevel::Update, along with all the other
> actors there?  This is just a guess.

The update chain is a little complex. Let me guide you through the
relevant parts of the code. My line numbers are based on commit f5bc0d2.

The main loop is in cSceneManager::Play(), defined in
scene_manager.cpp. In that function we find that in line 107 it
retrieves the currently active scene (top of the scenes stack), which is
then in line 124 used to tell that scene it should now update itself.

    p_current_scene->Update(stage);

cScene::Update() is a virtual function, so the call is delegated down to
whatever class is the active scene. In your case, it’s
cLevelScene::Update(), a function defined in level_scene.cpp at line
178. Removing the debugging updates, this function looks like this:

    void cLevelScene::Update(sf::RenderWindow& stage)
    {
        gp_current_level->Update();
        stage.setView(gp_current_level->Get_View());
    }

You see that it retrieves the current level from the global pointer
`gp_current_level'. That pointer is assigned in
cLevelScene::Add_Level(), a function called whenever a level needs to be
loaded. Actually, one could also just look at the top of the
`m_active_levels' stack; the top of the `m_active_levels' stack is
required to be equivalent to `gp_current_level', which is nothing but a
shortcut, because the current level is (going to be) needed in some deep
sections of the TSC code. Again, the same concept was used in OpenGL/SDL
TSC already.

cLevelScene::Update() calls cLevel::Update() on the currently active
level thus. This function is defined in level.cpp at line 173. Excerpt:

    void cLevel::Update()
    {
        // Update all the actors.
        std::vector<cActor*>::iterator actiter;
        for(actiter=m_actors.begin(); actiter != m_actors.end(); actiter++)
            (*actiter)->Do_Update();
        [...]
    }

As you can see, it iterates through the list of all actors that are
registered in the level (m_actors), and calls cActor::Do_Update() on
them, which in turn calls the Update() methods of all actors. Now you
will of course ask how the level player got into `m_actors'. It even
even usually is the first element (index 0) in that vector (though that
depends on the position of the <player> item in the level XML, see below
— however, that element is usually the first one). This is the result of
the cActor::Add_Player() method, which is likely to be the function that
you missed when you tried to understand the call stack; like with
`gp_current_level' above, the reasoning behind the existance of the
`mp_level_player' variable is shortcutting the access so one does not
always have to extract it from the vector, but can just directly use the
pointer. It is defined in line 429 of level.cpp and looks like this:

    void cLevel::Add_Player(cLevel_Player* p_levelplayer)
    {
        if (mp_level_player) {
            throw(std::runtime_error("Tried to add multiple level players!"));
        }
    
        mp_level_player = p_levelplayer;
    
        mp_level_player->Added_To_Level(this, 0); // Reserved UID 0 for the level player
        m_actors.push_back(mp_level_player);
    }

There, in the last line, the cLevelPlayer instance is added to the
`m_levels' vector, which cLevel::Update() calls cActor::Do_Update() on
every frame as outlined above. The Add_Player() function is required to
be called by the one who constructs the level from the XML or from
memory; take a look at cLevel::Construct_Debugging_Level() at line 86 to
see the bare principle exposed without too much around (this is the
function that builds the level you see when you enter the "start" menu
point). In reality, the call to this function is hidden deeply inside
the XML loader. It is at cLevelLoader::Parse_Tag_Player() at the very
end of that function in line 252 of level_loader.cpp in case you
wonder. That function reads in the position data of the level player
from the XML, instanciates cLevel_Player with it, and then places the
new instance inside the cLevel instance that is being build by means of
cLevel::Add_Player().

Please note the difference to SDL/OpenGL TSC, which had only ever one
single instance of cLevel_Player, which has proven to be the source of
numerous weird bugs. If something goes wrong with the player’ſ state,
old TSC carried that state through all the levels until the user
terminated the entire game. With this setup, such errors will
automatically "heal" when exiting from the level as the cLevel_Player
instance is associated with the level and is thus destroyed when the
level is completed.

Does this clear up things for you?

Vale,
Quintus

-- 
#!/sbin/quintus
Blog: http://www.guelkerdev.de

GnuPG key: F1D8799FBCC8BC4F

By Thread
2015-10-21 07:21:18datahead[tsc-devel] Who calls cLevel_Player::Update?
2015-10-21 11:12:53QuintusRe: [tsc-devel] Who calls cLevel_Player::Update?
2015-10-24 13:17:39datahead[tsc-devel] Re: Who calls cLevel_Player::Update?
By Date
[tsc-devel] Who calls cLevel_Player::Update?datahead2015-10-21 07:21:18
Re: [tsc-devel] Who calls cLevel_Player::Update?Quintus2015-10-21 11:12:53
[tsc-devel] Re: Who calls cLevel_Player::Update?datahead2015-10-24 13:17:39