Here is a ToDo list related to technical enhancements of the code. Another ToDo's may be found here.
Teleport() is called whenever Tux has to go from one level to an other one. However, when Tux is only crossing a level's boundary, this is not a real "teleporting" (mainly because Tux goes from one level to one of its neighbor).
So a specific Teleport() should be written to be used when Tux crosses a level's boundary.
For example, in standard Teleport, we have to:
- call reset_visible_levels() and then get_visible_levels()
- reset the current waypath of Tux
- reset target position
And in a specific Teleport, we should have to :
- call get_visible_levels()
- adapt current waypath to the new level
- adapt target position to new level
Once done, we can remove all the calls to reset_visible_levels() that are executed just before calling Teleport()
Additional note: It seems that after Tux crossed a level's boundary, there is some glitch in the computation of Tux's speed. To be checked...
At each frame, a list of light sources is built. This list contains "static" light sources (obstacles emitting light) and "dynamic" ones (Tux and bots).
The static light sources are found by browsing all obstacles on all visible levels, at each frame. This is evidently sub-optimal.
A list of static light sources could be added to the level struct. This list can be built, for example, when a level becomes visible.
When a bot is wandering along waypoints, it stores the next waypoint to reach in a "nextwaypoint" attribute. The "nextwaypoint" is defined by an index in the list of the current level's waypoints.
After a bot has crossed a level's boundary (for example when the bot is hunting an enemy), the "nextwaypoint" value does then no more points to the right waypoint, the bot will then not get back to its old position, and moreover the bot can also traverses all its current new level in order to try to reach the new waypoint now pointed by "nextwaypoint".
Several solutions here:
- set a unique waypoint id (how??)
- store the next waypoint index + the level index
- reset "nextwaypoint" when a bot goes to a new level
This function is called many many times per frame. It could be worth to avoid calling normalize_vect(), which is quite CPU costly.
- When deleting a south line or a east colum, also delete the obstacles and items that are outside of the map, and adapt the position of the waypoints to place them inside the map.
- When deleting a north line or a west column, adapt the position of the waypoints to place them inside the map.
- More ideas may be found at Our specific Level Editor improvements Section.
Virtual gps positions
Blitting list generation
In order to sort-insert an object inside the blitting list, resolve_virtual_position() is called before the norm's computation. During the rendering of this object, resolve_virtual_position() is called a second time, to compute the position where to render the object (the blitting list only stores the object's index).
Proposal: store a gps or a moderately_finepoint inside the blitting list.
Since we removed level interfaces, a great bunch of calls to update_virtual_position() / resolve_virtual_position() was added. Thus, there should be a lot of calls to transform exactly the same position. This has to be checked by storing the parameters of each call, in a file (for one unique frame), sort that file, and count duplicated lines.
If it is found that it should be worth optimizing it (profiling could be needed, here), the idea could be to:
- change the gps struct, so that it contains an actual position, a virtual position, and a dirty flag
- add a gps_manager, that contains a list of gps positions (obstacles, items, bots...)
- have the gps-manager sets the dirty flag of all gps at the beginning of a frame
- ((TO BE CONTINUED...))
However, it should first be checked if all the fdrpg code can be changed so that it only uses virtual positions defined relatively to Tux's level. If not, it's probably possible to store virtual positions defined relatively to all visible levels.
Colldet (DLC) optimization
Given that an obstacle can be several tiles wide, and given that an obstacle is only stored in one tile (the tile containing the center of the obstacle), the DLC algorithm has to scan an area that is also several tiles wider than needed. This is ever worst for SLC. DLC/SLC being called many many times per frame, it's worth optimizing them.
The optimal code would be a line-tracing algorithm, to only scan the tiles traversed by the line (here, a 4-connected derivative of Bresenham would be needed). However, it then needs a change in the way we associate obstacles to tiles, so that each tile has a reference to all obstacles contained by this tile.
Additional note: When DLC is called by the pathfinder, it computes the intersection between the obstacles and a wide-line. This wide-line is simulated by artificially enlarging the bounding-box of the obstacles. So, a special care of this trick has to be taken.
Bullets collision detection
If a line-tracing algorithm is used in DLC, we can also use it to replace the current bullet collision detection.
Indeed, a collision detection based on line-tracing will be able to easily return the nearest object in collision, and that's exactly what is needed for bullets.
To be adapted since level interfaces were removed
The following section describes several things that should be adapted or changed due to the removal of the level interfaces
This function checks if Tux or a bot is near enough to a door to open it.
However, the code does only take into account characters that are on the same level than the door.
If a door is very near a level's border, then the code misses the characters on the neighbor levels that could potentially be near enough to open the door.
The game currently does not have an actual animation system. This means it is difficult to control exactly when and how the frames of an animation are displayed. fluzz will have to elaborate on this...
For various scripting and storytelling purposes, it would be very useful to have events occur after a certain arbitrary delay. This can be done through a function that takes two arguments: a number of seconds, and arbitrary lua code.
There are two main ways this issue can be approached:
- for every game loop, check the time and compare it to the number given to the function to determine if the delay for the arbitrary code is over
- use the C timer (libc setitimer)
The first method may lack accuracy and is affected by the framerate (an issue on weaker systems), while the second one is less reliable and may have strange side effects. We'll have to see what suits our needs.
Savegame compatibility between versions
Currently, changes made between game versions cause saved games made in previous versions to break. The causes for this are apparently many and varied. Changes to the game should maintain backwards compatibility with savegames.
- Game data should not be described in an index format, as changes to the indices cause breakage.
- Need to think about edge cases, such as changes to map data that can affect the player location (a room shrinking and leaving the player outside of the map) or quests that the player is in the middle of
- Other things?
Implementation of a list widget
- Have a widget to display a list of item with customized widget.
- Reuse widget to avoid to create 1000 widget to a list with 1000 items. Allow to reuse resource.
- Based on design on Android/ios
The list is initialized with a key and data pointers. To iterate, the widget calls:
void *next_item(void *data, void **key); void *prev_item(void *data, void **key);
The returned pointer is a element used later named item. A identifier or a struct.
To count all the items, the widget calls:
int count(void *data);
These method can be in a different struct (iterator/adapter)
The widget has two array:
- A linked list of displayed item widget.
- A linked list to keep widget already created.
When a new widget need to be displayed, it calls:
widget *create_widget(void *item);
Otherwise, it uses a widget from the list of unused widgets. In any case, it calls after on the item widget:
void update_widget(void *widget, void *item);
When the list is moved up, widgets are added at the start of the displayed widget list. When the list is moved down, widgets are added at the end of the displayed widget list.
When a widget is not displayed anymore, the widget is removed from the displayed widget list and added to the list of unused widget.
To get the full size of the list, it calls:
float widget_height(void* item);
Allow dynamic sizing of item widget. Must be a fast function.
- Use type to allow to differentiate different widget (a list by type).