This HLL provides functionality for loading, managing, and rendering
animations stored in the MAD file format. A MAD file consists of a
header followed by a series of QNT image frames.
Previously, the debugger displayed `ref int` variables like this:
ref_int: <unsupported-data-type>
<void>: <unsupported-data-type>
This commit introduces `dbg_variable_to_string()` to dereference these
variables and display their underlying value. Both the command-line and
DAP debugger interfaces have been updated to use this new functionality.
This function is responsible for generating random dungeons for Pastel
Chime Continue.
The dgn_generate_drawfield() function implements the main logic. This
algorithm constructs the dungeon through a multi-step process involving
room creation, recursive expansion, wall placement, treasure room
generation, and corridor pruning/extension.
AutoDungeonE_Create() calls this function to create a layout based on
specified parameters, and then populates arrays with the proposed
coordinates of items, enemies, and traps.
This implementation strives to faithfully reproduce the behavior of
DrawField.dll, including its bugs, although it has not been strictly
verified.
In hll_call(), pointers to heap data passed by reference could become
invalid if the heap was reallocated during the call.
The previous implementation attempted to prevent this by calling
heap_guarantee() to pre-allocate heap space. However, this approach
does not work for certain functions in PastelChime2 HLL, which can
perform an unbounded number of heap allocations.
This commit removes heap_guarantee() and replaces it with a more robust
copy-in/copy-out strategy:
- Before the call (Copy-in): For reference arguments, the pointer value
is copied from the heap to a local variable on the stack. A pointer to
this local variable is then passed to the HLL function.
- After the call (Copy-out): The pointer value, which may have been
modified by the HLL function, is written back from the local variable
to its original slot in the heap.
This HLL implements the core gameplay logic for the 3D dungeon
exploration mode.
Key features:
- Collision and Movement:
- Implements 2D collision detection for player movement against walls.
- Field_CalcMove() function calculates the desired position and
adjusts it to slide along walls upon collision.
- Party and AI Mechanics:
- Field_UpdatePlayersUnit() manages party movement, creating a smooth,
snake-like following behavior for up to two companions.
- Field_UpdateEnemyPos() provides basic enemy AI, enabling them to
chase the player when in line-of-sight and within range, or return
to a base position otherwise.
- Interactive Environment:
- Field_UpdateDoors() implements automatic doors that open and close
based on player proximity.
- Field_CheckObjectHit() detects when the player enters the hit-range
of an object, triggering events.
- 3D Space UI:
- Adds dungeon_project_world_to_screen() to translate 3D world
coordinates into 2D screen positions.
- Field_UpdateObjectNamePlate() and Field_UpdateDoorNamePlate() use
this projection to display and manage interactive nameplates for
objects and doors.
- Data Persistence:
- DungeonDataSave() and DungeonDataLoad() serializes and deserializes
the complete dungeon state to .DSD files.
- Integrates dungeon data with the main save system by copying .DSD
files to/from slot-specific .DSA files.
Note: The most complex function in this HLL, the dungeon generator
AutoDungeonE_Create(), is not implemented in this change.
DrawField is a variant of DrawDungeon. Unlike the first-person
perspective of DrawDungeon, DrawField uses a third-person, bird's-eye
view camera.
Key features include:
- Character Rendering: A new system to render up to 256 billboarded
sprites (characters, enemies, etc.) in the 3D world. The renderer
supports controlling position, sprite animation, and visibility for
each character.
- DrawField HLL API: Functions for loading assets, managing characters,
and manipulating dungeon properties like door locks.
- Renderer: The core renderer is updated to support the DrawField mode.
This includes disabling fog, handling sprite sheets via UV
manipulation, and rendering ceilings as billboards.
- Map: The 2D map has been updated to reflect the DrawField style,
including colored door lock indicators.
* dtx_load_from_dtl() loads lightmap textures.
* dgn_calc_lightmap() calculates lightmap texture index for each cell
based on the state of the surrounding walls.
Early versions of System4 (Sys42VM.dll < 3.0) execute destructors for
global variables on exit.
Dungeons & Dolls saves its game state in the destructors of global
variables (~CDataDoll(), ~CDataItem(), ~CDataMain()), so the game was
not saved in xsystem4 before this.
This behavior has some quirks:
* Destructors for variables on the call stack are not called on exit.
* Destructors are called in the reverse order of the global variable
declarations.
* Inside a destructor, it is possible to access another global variable
whose destructor has already been called (!).
Since this is a problematic behavior that was removed in later versions
of Sys42VM, let's add minimal support for it as a game-specific hack.
Texture.dtl contains multiple sets of wall, floor, and ceiling texture
images. dtx_load_from_dtl() randomly selects a texture set using the
floor number as the random seed and returns it as a dtx structure.
In DrawDungeon2, dungeons are generated randomly using a dedicated
random number generator (mtrand43.c), which is a Mersenne Twister with
N=4, M=3.
The entry point for map generation is dgn_generate_drawdungeon2(),
which deterministically generates a floor map using the floor number as
the random seed.
I verified that generated floor layout, player starting position,
exit, and treasure chest locations match the original DrawDungeon2.dll
up to the 1000th floor.
Some classes in Dungeons & Dolls serialize themselves using
`File.Write(this)` in their destructors. Because `File.Write` takes the
argument by value, a copy of `this` is created. If the destructor is
called on the copied `this`, it falls into an an infinite loop.
Skinned models can have multiple texture images for animation. When a
material colormap in the .pol file is `image.png`, additional images are
stored with names like `image[1].png`, `image[2].png`, etc.
Animation frame information is stored in `<motion_name>.txa` file. This
is a text file containing newline-separated integers, specifying the
texture index for each motion frame.
TapirEngine.FindInstancePath() takes a starting point and an end point
and executes the A* algorithm on the collision mesh. The resulting path
can be obtained with TapirEngine.GetInstancePathLine().
Used for the treasure chest opening effect in Rance Quest.
Implemented using draw_plugin. The render() method of draw_plugin is no
longer dedicated to custom sprites, but can be used for any sprites to
override the default sprite rendering function.
It is called when the plugin is unbound from a sprite, or when the
sprite to which the plugin is bound is released.
This prevents memory leaks when sprites are destroyed without the plugin
being released, and simplifies lifetime management of the DrawDungeon
plugin.
It draws a quadrilateral with arbitrary vertex and texture coordinates.
This is used to properly implement DrawGraph.DrawDeformedSpriteBilinear,
and will be used to implement ChipmunkSpriteEngine.
SP_SetSpriteTransformPos in a subsequent patch.
- Add support for setBreakpoints request
- Add source locations to stackTrace response
- Source-level stepIn/stepOut/next executions
All of these only work if debug information is available. Otherwise they
work at the instruction level as before.
In SACT2, if two sprites have the same Z value, they are rendered in
increasing order of their sprite numbers. This behavior is documented in
the SDK manual.
However, in later versions of the sprite engine (SACTDX,
StoatSpriteEngine, ChipmunkSpriteEngine), sprites are drawn in the order
they were created.
This fixes an issue in Rance Quest Magnum which relies on this behavior.
This setting has a significant impact on system load, so users may want
xsystem4 to remember their preference.
This implementation uses a JSON format, while the AliceSoft's
implementation saves it to a binary file (WindowSetting.sav).
This only records the wait_vsync setting, since the other fields in
window_settings are currently no-op.
Before this, the resume save format defaulted to JSON, which could not
be loaded by System40.exe and save/load was sometimes too slow even on
desktop environments.
In xsystem4-android the RSM format has been the default for 9 months and
no problems have been reported. Let's make this the default on all
platforms.
This adds source-level debugging capabilities to the command-line
debugger, utilizing debug information generated by sys4dc. The debug
information format is documented at:
https://github.com/kichikuou/sys4dc/blob/master/docs/debug_info.md
The location of the debug information file can be specified with the
`--debug-info` command-line flag. If not specified, xsystem4 will
attempt to load it from `gamedir_path("src/debug_info.json")`.
When debug information is available, a breakpoint can be set at a
specific line in a file using `breakpoint <file> <line>`. Also, `step`
and `next` commands advance execution to the next source line (this is
naively implemented by repeating instruction-level stepping).
Used in Tsuma Shibori, and some indie System 4 games.
This HLL is a draw plugin that uses SACT2's "custom sprite" mechanism.
Custom sprites do not have a texture. Instead, the plugin's render()
callback draws them directly to the main surface.
Before this, the BoneTransforms uniform block consumed
(4 * 4 * sizeof(float) * MAX_BONES) = 19712 bytes, which exceeds 16384,
the minimum possible value of GL_MAX_UNIFORM_BLOCK_SIZE in OpenGL ES
3.2.
Since the bone matrices are affine, we can drop the 4th rows and pack
them into a mat4x3 array. Now the BoneTransforms block is 14784 bytes
and it should work with all OpenGL ES 3.2 implementations.
In TapirEngine, the following lighting features are not used:
- global ambient
- directional lights
- specular lights
- rim lights
- fog
(The setters / getters for these parameters are still there, but they
don't affect rendering.)
This implementation disables those features in GLSL using #ifdefs. The C
code that sets up the lighting remains almost unchanged --
glGetUniformLocation() will return -1 if the specified uniform variable
name is not found, and glUniform*(-1, ...) will be no-op.