pingus-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Documentation, v .03


From: John August
Subject: Documentation, v .03
Date: Sun, 9 May 2004 14:02:59 +1000
User-agent: Mutt/1.0.1i

Thanks to Gervase for comments. My email has been been bouncing, so it
took a while to get to me.

Anyway, here's another version. The weak point at present is how we click
on the actions at the left of the screen and set the action name in the
button object.

John August.

--

Pingus, a code description / narrative , V.03

# Getting information

Quite apart from gdb, strace (and perhaps, DRT), a good way to get an idea
of what pingus does when it starts is to use the command line :

./pingus --debug actions 

# Introduction

We categorise pingus operations into several stages :

1. Setting up the screen from the description files. [some coverage]

2. Having the scenario run, involving :

   -generating pingus [some]
   -developing those pingus [some]
   -noting whether they have died or exited, finishing the level [nothing]

3. User interaction

   -cosmetic interaction (action boxes highlighting when the mouse is
    over them; the target pingu highlighting with a box around it) [none]
   -music (which is non interactive) [none]     
   -cursor state (being set to one of the left actions) [a little]
   -setting a characteristic to a pingu [some]
   -game level operations : pause, exit [none]
   -setting up the buttons and using ClanLib [some]

# Directory Structure 

The following exists below src :

.:
./actions:
./caimagemanipulation:
./colliders:
./editor:
./editorobjs:
./gui:
./input:
./input/axes:
./input/buttons:
./input/pointers:
./input/scrollers:
./movers:
./particles:
./sound:
./win32:
./worldmap:
./worldobjs:
./worldobjs/entrances:
./worldobjsdata:

Here's some more detail :

.:

The main code

./actions:

The files here are called for each state a pingu are in, as part of the
periodic update

./caimagemanipulation:

?

./colliders:

?

./editor:

The editor 

./editorobjs:

Editor logic; the pieces you can use in the editor, like entrances,
groundblocks...

./gui:

Elements of the screen setup are contained here

./input:
./input/axes:
./input/buttons:
./input/pointers:
./input/scrollers:

These directories setup the user inputs

./movers:

?

./particles:

Related to tiny objects like the exploding parts of a bomber, raindrops,
snow...

./sound:

Sound - including music 

./win32:

Windows compatibility

./worldmap:

These are files representing the island, which coordinates the 
introductory levels

./worldobjs:

game logic

./worldobjsdata:

The shared part of editorobjs and worldobjs

./data is one level below src, and contains :

./controller
./data
./demos
./images
./levels
./music
./prefabs
./sounds
./themes
./worldmaps

And are not covered in detail at this point.

./levels contains :

./multiplayer:
./playable:
./test:
./tutorial:
./volcano:
./wip:

Which contain the xml files for the different levels. It would seem the
most interesting directory is 'playable'.

# Update

Every active object in the background, and every pingu, has a method 
Update(). This is called by the main program for every such object in
order to advance the game.

# Setting up the screen

In order to set up a screen, you have the background, the buttons on the
left, the buttons at the bottom (fast, pause, exit) and the pingu generator.

pingus_main.cxx is where the interesting action starts.  The
coordinating routine is PingusMain::start_game () ;

First there is initial setup and registration.  After identifying the
level file to load, pingus instantiates the class :

new StartScreen(PLFResMgr::load_plf_from_filename(levelfile)),
                 true);

Which sets up the screen with the given "levelfile", an xml description of
that level. (There are also .plf files, pingus level files, which might be
used in 0.6.1)

This load_plf_from_filename provides a pointer to that file, and deals with
caching.

The class GUIScreen is a Screen with a GUI manager. StartScreen is an 
object of the GUIScreen type, and an instance is generated by the code
above.

start_screen.cxx contains a description of the StartScreen object, the
corresponding method to the code above [I think] is repeated below :

StartScreen::StartScreen(PLFHandle arg_plf)
  : plf(arg_plf)
{
  StartScreenComponent* comp = new StartScreenComponent(plf);
  gui_manager->add(comp);
  gui_manager->add(new StartScreenOkButton(this));
  gui_manager->add(new StartScreenAbortButton(this));
}

arg_plf is equal to plf, this is set in
PingusLevelDesc::PingusLevelDesc, this is a naming convention to
separate an argument variable from a member variable

Note that calls are made to StartScreenComponent, StartScreenOkButton
and StartScreenAbortButton.

StartScreenComponent is a GUI component that represents the background of
the StartScreen; it would seem that this is a string of references from
the setup file which coordinates what finer-detail elements appear on 
the StartScreenComponent.

The relevant StartScreenComponent method appears to be :

StartScreenComponent::StartScreenComponent(PLFHandle p)
  : plf(p)
{
  background = Sprite("menu/startscreenbg", "core");
  background.set_align_center();
  time_str = GameTime::ticks_to_realtime_string(plf->get_time());
}

The PLFHandle is a pointer to a PLF object, and holds all the
information needed for a level, gameobjects, time, name, etc.
Startscreen uses it to present that information and passes it further
down to the PingusGameSession, which then starts the level.

# WorldObjs

Files in the worldobs directory are called as part of the process of
parsing the level description file.

Worldobjs includes active objects, such as a conveyor belt and spike.

# Parsing the xml file 

[where this section fits in, and where it is called from, is not known ]

PLF::create() is a dispatch function to allow the use of different level
formats, not needed at the moment, but was [?] needed at the point where
pingus switched from .plf to .xml.

PLF::create() calls XMLPLF:XMLPLF, which then calls XMLPLF::parse_file()
[xml_plf.cxx], which reads a xml level file and fills a PLF object with
the given information.

This information is used to do things like load up the action
bar from the <action-list> segment of the xml description.

# Pingus state

As detailed in the file pingu.hxx, pingus have several states. One is the
main action of that pingu, called when it is updated.

You also persistent actions : once set for a given pingu, the pingu will
continue to behave consistently with that action.  Eg fall_action is
the action if the pingu falls.  Normally, this is set to zero, and no
change in action status takes place.  However, if it is set to floater,
when the faller action notes the speed has built up, because the
fall_action is set, it changes to a floater.

Similarly for wall_action : this starts off unset, and when a pingu hits
a wall, it only means it turns around. But, if this value is set the 
pingu can start climbing the wall.

The change of pingu state is initated through a call to
Pingu::request_set_action (PinguAction* act) in pingu.cxx. "act" has
both a target state and a context. For persistent actions like floater
and climber, the context is FALL_TRIGGERED or WALL_TRIGGERED, and the
appropriate value is set.

However, many action are not persistent, rather they are initiated just
at the correct moment (eg diggers, miners).  These target states have
the INSTANT context, and the main action of the pingu is changed.

[At this stage, the link of how a mouse click in the right location changes
the current "set state" through the toolbar on the left, and how the mouse
click is sent to the correct pingu and request_set_action is called, is
not described ]

# Setting an action via a click

As noted, Pingu::request_set_action (PinguAction* act) sets a given action.
The mouse click on that part of the screen must trigger this routine, and it
must also keep count of the remaining pingu actions of each type.

This is done by the PlayField class/object.

When update for Playfield is called (line 170, playfield.cxx), the routine
calls current_pingu_find with the current value for the mouse. This cycles
through every pingu, and returns that pingu if its the mouse is inside the
area that pingu covers.

When the primary mouse button is clicked,
PlayField::on_primary_button_press() is called.  If the last call to
update set current_pingu to an active value, then the routine calls
server->send_pingu_action_event(current_pingu, buttons->get_action_name()).

send_pingu_action_event (line 72, server.cxx) calls
action_holder.pop_action(action) - this checks that there is an action
available, and presumably decrements the number available.  If it is
able to decrement the number of those actions, it returns true.  In this
way, pingus ensures that you can only use the number of actions you are
allocated at the start of the screen.

(As part of consistency in the program, if the pingu cannot accept the
action, the action is "put back on the queue", so this does not decrement
the number of actions available.) 

(Somewhat confusingly, Playfield refers to a collection of levels.  But
this subtlety is not considered here.)

# Setting the "active action" on the main screen

We now understand how you can click on a given pingu and set that pingu to
be a climber, jumper or whatever. But, how does pingu know what action is
presently active ? From a user viewpoint, we know that you click on the
action in the list at the left, it is white highlighted.

send_pingu_action_event calls buttons->get_action_name() ; it would seem
that the buttons object keeps track of what the current action is.

This is defined as  ActionName name; in action_button.hxx. However, its not
clear just how "name" is changed in response to button clicks and the
external environment. 

# Pingus development and actions

The Class PinguHolder has a list of all pingus, including those dead and
alive.  Code in worldobjs/entrance.cxx, which runs the entrance, calls
Pingu* pingu = world->get_pingus()->create_pingu(data->pos,
data->owner_id); from the routine Entrance::create_pingu () in order to
create pingus.

The method Entrance::create_pingu () is itself called from
Entrance::update () when a pingu is ready.

Each pingu is in one of several states (blocker, walker, climber) etc, as
described above, and the associated code for each state is contained in
the "actions" directory.

When the Update method for a given pingu is called, it calls the Update
method in the corresponding action file.

We consider the Walker case as an example.  A comment for the Walker::Update()
code, contained in actions/walker.cxx, is repeated below :

/* How should this code work?

  1) Check that the Pingu stands still on ground, if not turn it into
  a faller or drown. The reason we do so, is that we catch situations
  where a digger or a similar action removed the ground under the
  walker.

  2) If pingu is still on ground, we can preprare the next step

  3) Check if up-hill or down-hill is required
  */

In order to perform the above activity, the code checks what is present
one pixel to five pixels below the pingu; if not solid, it calls
pingu->set_action(Actions::Faller); and becomes a faller.

If a step is made, the code calls pingu->set_pos; an example when the x and y
locations are set is : pingu->set_pos(pingu->get_x() + pingu->direction,
pingu->get_y() - possible_y_step);

Each pingu action has a draw method, which draws the particular case of
pingu - walking left, walking right, and also incorporates any additions
to the graphic.

Now considering the "faller" code (actions/faller.cxx) - the pingu falls
without incident until it has reaches a reached a critical velocity.
Then, request_fall_action is called. This turns the faller into a floater,
or lets the faller continue falling. (this means the call is not made when the
pingu "falls" just a litte when it goes over a step.)

Regardless, when the faller has ground beneath it, it turns back into a
walker or dies because it impacts with too great a velocity.

# User inputs

[The link between these inputs and the way that clicks change states in
the program is not yet known]

In gui/screen_manager.cxx, ScreenManager::display () calls 
input_controller = new Input::Controller(controller_file); in order to
instantiate a controller.

The routine above calls Countroller::Controller (line 48) in
input/controller.cxx.

This routine checks through the xml file, and generates buttons based on
its content. Line 69 starts a while loop, which checks each of the cases
and calls PointerFactory, ScrollerFactory or ButtonFactory as appropriate.

But here we concentrate on ButtonFactory; for instance, line 108 creates
an "action-down", presumably an action on a click.

input/button_factory.cxx contains the routine Button* ButtonFactory::create,
called by line 108 of the previous routine. As we're focusing on a mouse
button, we see that line 51 calls mouse_button.

this is line 99 of the same file, Button* ButtonFactory::mouse_button ;
this routine calls MouseButton(button).

This is contained in input/buttons/mouse_button.cxx . We see from line 35
that it attaches CL_Mouse::sig_button_press and CL_Mouse::sig_button_release.

Importantly, this is the connection between Pingus and the ClanLib driver
for the mouse buttons.

The general ClanLib operation runs by polling, and it is necessary to call
the ClanLib update method.

This is found in, for example gui/screen_manager.cxx :

      // Let ClanLib fetch events
      CL_System::keep_alive ();

There is a time delay in dealing with events, as the events are only updated
as rapidly as this is called.

For some reason, the graphics updates without any noticeable time delay -
eg ticks appear on the button boxes, or the button boxes are highlighted.

# Clan Lib Interface, and X Window contact

While it is possible to edit Pingus without knowledge of the details of
the interface, it can be interesting to have some idea of where the code
"makes contact" with the X Window interface.  This section is mostly
optional, but such knowledge may be important when timing issues are
addressed (as compared to general hacking).

As noted, Pingus attaches CL_Mouse::sig_button_press and
CL_Mouse::sig_button_release; this is the connection between Pingus and
the ClanLib driver for the mouse buttons.

It is in the (ClanLib) file Display/Input/X11/mouse_x11.cpp that the routine
make contact with the underlying XWindow.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]