Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Maniac Patch (Metabug for all releases) #1818

Open
19 of 27 tasks
Ghabry opened this issue Jun 14, 2019 · 34 comments
Open
19 of 27 tasks

Support Maniac Patch (Metabug for all releases) #1818

Ghabry opened this issue Jun 14, 2019 · 34 comments

Comments

@Ghabry
Copy link
Member

Ghabry commented Jun 14, 2019

Website: https://bingshan1024.github.io/steam2003_maniacs/

Because Maniac Patch is a moving target I decided to close the old issues and just update this one issue here when new features are added, otherwise this slowly becomes messy.

Old issues: #1424, #1557, #1608
Some of these old issues contain more information not moved here yet.

New commands

This command retrieves the same information that is displayed on the default load screen.
It stores the date, time, level and HP in variables and the face graphic in a picture.
Character names are not currently supported.

If the save data does not exist, the date variable will be set to 0.

The face graphic is treated as a 4*4 sprite sheet.
So if you have a file with more than 16 patterns, it will not be displayed correctly.
On the other hand, it doesn't matter if the file is out of the standard as long as it can be divided into 4 parts.

The original picture settings are used, except for the file and sprite sheet related items.
Please use the appropriate picture number to make the picture off-screen or transparent beforehand.

The order of the date (YYMMDD) is the same in the English version.

Numeric Arguments:

  • 00: How to specify the save number (0: constant 1: variable)
  • 01: Save number
  • 02: Variable to receive the save date (YYMMDD)
  • 03: Variable to receive the save time (HHMMSS)
  • 04: Variable that receives the first character's level
  • 05: Variable that receives the first character's HP
  • 06: Reserved items (fixed at 0)
  • 07: How to specify the picture number (0: constant 1: variable)
  • 08: Picture number to receive the face graphic of member 1 (first character)
  • 09: Picture number to receive the face graphic of member 2.
  • 10: Picture number to receive the face graphic of member 3
  • 11: Picture number to receive the face graphic of member 4

This command performs the same operation as the default save function.
It is not possible to use a save number less than 0.

You can optionally store the result of the execution in a variable.
The value is 1 on success and 0 on failure.
Note that the assignment to the variable is done after the save has been executed.

Numeric arguments:

  • 00: How to specify the save number (0: constant 1: variable)
  • 01: Save number
  • 02: Whether to receive the success or failure of the save (0: not received 1: received)
  • 03: Variable to receive success or failure

Not implemented: Black screen with fade-in

This command is equivalent to the default load function.
Save numbers less than 0 cannot be used (the operation is ignored).

Optionally, it checks the file before execution and aborts the load if the result is invalid.
The check is equivalent to the "Get Save Information" command.
If you are sure you have the right file, you can disable it, but it is not recommended. -The

Numeric arguments:

  • 00: How to specify the save number (0: constant 1: variable)
  • 01: Save number
  • 02: Whether or not to check before execution (0:Enable 1:Disable)

  • End Load Process, Code: 3004

Not used anymore

This command cancels the dark state after loading.
It is not available in the new game. The darkness will disappear naturally after 1 frame.


This command gets the relative position of the mouse in the window.
It is based on the resolution of 320*240.

The value will not work correctly if the window is full screen and DirectDraw is specified as the rendering method. (This is probably not a problem, as few machines currently have this setting in effect.

Numeric arguments:

  • 00: Variable to receive X-coordinates
  • 01: Variable to receive Y-coordinates

  • Set Mouse Position, Code: 3006. No support planned: System specific, not exposed by SDL

This command sets the relative position of the mouse in the window.
It is based on a resolution of 320*240.
The x-coordinate is rounded to 0-319 and the y-coordinate is rounded to 0-239 to avoid inducing false clicks.

The values are not correctly handled when the machine is in full screen and DirectDraw is specified as the rendering method.
(This is probably not a problem as few machines currently have this setting enabled.

Numeric arguments:

  • 00: How to specify the coordinates (0:constant 1:variable)
  • 01: X coordinate
  • 02: Y coordinate

Supported but the implementation is not perfect ;)

Generates a picture from the specified string.
Once generated, it can be treated like a normal picture.
Disabling the background, border, gradient and shadow will reduce the cost of generation.

Control characters:

There are several control characters available in the string.
Some control characters are available in the string, e.g. the value in [] can be referenced by \V

  • $a-zA-Z: Draws a special character.
  • \: \ alone is drawn.
  • \C[n]: Change the character colour to the nth character.
  • \D[n]: Aligns the variable value with at least n digits (zero filled).
  • \N[n]: Draws the name of the nth character.
  • \T[n]: Not implemented at the moment.
  • \V[n]: Draws the value of the variable numbered n.

Display position-> coordinates: Specifies the meaning of the position value.
(Traditional pictures are fixed at center coordinates.)

Picture size:
Specify the size of the picture to be generated.
If the value type is set to "Automatic" or the value is set to 0, the picture will be adjusted to the appropriate size.

Font name:
Specify the font to use for drawing.
If this field is left blank, the system settings of the database will be used.

Font size:
The size of the font used for drawing.
Valid values are 1-255, default is 12.

Character spacing:
This specifies the amount of space to be left between each character.
Valid values are 0-255, default is 0.

Line spacing:
The number of spaces per line.
Valid values are 0-255, default is 4.

Bold:
Applies Bold to the font.

System graphics: Specify the graphics to use for drawing.
Specifies the graphics used for drawing.
<Select to refer to the system settings of the database.

Wallpaper:
Specifies the background type for the window.

Draw border: specifies whether the window should have a border or not.
Specifies whether the window should have a frame or not.
If either the height or width is less than 8px, the frame will not be drawn, regardless of the setting.

Enable outside margins:
This specifies whether or not the border area is taken into account when calculating the picture size and text drawing position.
If enabled, the area is 8px on each side and 10px on the top and bottom.

Enabling text gradients:
Enables or disables text gradients, as used in other features such as text display.
If disabled, the text will be drawn from the top left corner (0, 0) of each 16*16px colour.

Enable text shadow:
Enables or disables text shadows, as used in other features such as text display.

Preview restrictions:
Position: Fixed at top left coordinate (0, 0).
Size: - automatically calculated unless specified as a constant.
Magnification: - fixed at 100% (the preview itself can be doubled).
Transparency: Fixed at 0%.
Effects: All effects are disabled.
Font size: 12 unless specified as a constant.

String arguments: (Delimited by 0x01 byte)

  • *1 String to display *1 File name *1 Font name
  • *1...A prefix of type byte indicating the type of value to follow. Currently only 0x01 is valid.

Numeric arguments:

  • 00: Type of value
    • 00~03: How to specify the picture ID (constant=0, variable, variable number variable)
    • 04~07: How to specify the display position (constant=0, variable, variable number)
    • 08~11: How to specify the magnification ratio (Constant=0, Variable, Variable)
    • 12~15: How to set the transparency (Constant=0, Variable, Variable)
    • 16~19: How to specify the picture size (Constant=0, Variable, Variable)
    • 20~23: How to specify the font size (Constant=0, Variable, Variable)
    • 24~27: How to specify the coordinate type (center=0, top-left, bottom-left, top-right, bottom-right, top, bottom-left, left, right)
  • 01: Picture ID
  • 02: Position X
  • 03: Position Y
  • 04: Zoom factor
  • 05: Transparency
  • 06: Red hue
  • 07: Green tones
  • 08: Blue tones
  • 09: Saturation
  • 10: Types of special effects
  • 11: Levels of the special effect
  • 12: Additional effects
    • 00~03: Blend mode (None=0, Multiply, Add, Overlay)
    • 04 : Flip horizontally
    • 05 : Vertical flip
  • 13: Option 1
    • 00~07: Scroll with map
    • 08~11: Background type (none=0, enlarge, tile)
    • 12 : Border not drawn
    • 13 : Gradient disabled
    • 14 : Shadows disabled
    • 15 : Bold text
    • 16 : Margins disabled
    • 24~27: Type with value 1 (in case of angle)
    • 28~31: type of value 2 (if angle is specified)
  • 14: Option 2
    • 00~07: Enable transparent colour
    • 08~15: Character spacing
    • 16~23: Line spacing
  • 15: Layers on the map
  • 16: Layers in battle
  • 17: Delete condition / Screen effect to apply
  • 18: Picture width
  • 19: Picture height
  • 20: Font size
  • 21: Value 2 (if angle is specified)

This command retrieves the position and size of the picture being displayed.
Special effects such as rotation will not be reflected.

Numeric arguments:

  • 00: How to specify the picture ID (constant=0, variable, variable number variable)
  • 01: Stage of the picture (Original image=0, During move, After move)
  • 02: Type of value (centre[xy]wh=0, xywh, ltrb)
  • 03: picture id
  • 04: Variable 1 to receive values
  • 05: Variable 2 to receive values
  • 06: Variable 3 to receive values
  • 07: Variable 4 to receive values

  • Control Battle, Code: 3009

This command allows the user to control some of the defensive battle processes.
This command is only valid for use during the battle, and is deactivated (without all control) at the end of the battle.
It should be used in conjunction with a common event that is conditional on the start of the battle.
Also, weighted commands should not be used within a common event that is being processed.

There are currently five different actions to choose from

ATB Gauge Increase:
This controls the natural increase in the gauge of characters, including enemies, every frame.
The following four variables are used to get or set the value

  1. unit type (get): friendly = 0, enemy = 1
  2. unit number (get) : index (not ID) for both friendly and enemy
  3. current gauge(get) : over 300000 can act
  4. amount of gauge to be increased (get/set)

Damage Pop:
Controls the notation when some damage or recovery occurs.
If you specify a common event for this, the default notation will not be drawn.
There are six variables that can be used to get or set the value

  1. type of unit (get) : friendly=0, enemy=1
  2. unit number (get): index (not ID) for both friend and foe
  3. display position X (get)
  4. display position Y (get)
  5. type of value (get) : damage=0, heal=1, evade=2
  6. value (get)

Targeting:
This is called just before a character's action to control the target of the action.
There are six variables that can be used to get or set values

  1. type of unit (get): friendly=0, enemy=1
  2. unit number (get): index (not ID) for both friend and foe
  3. type of action (get) : *1
  4. value of action (get) : *2
  5. type of target(get) : opponent=0, opponent=1, own=2, own=3
  6. target's number (get/set): index (not ID) for both friend and foe *3

*1:

  • 0: Basic action
  • 1: Special skill
  • 2: Transformation
  • 3: Use item

*2:

  • For Basic Actions: Normal = 0, Continuous = 1, Defensive = 2, Watchful = 3, Forceful = 4, Self-Destruct = 5, Escape = 6, Do Nothing = 7
  • For Special Skills: Special Skill ID
  • For Transfiguration: Enemy Character ID
  • For item use: Item ID

*3:

  • If the target is yourself or the whole group, the setting is invalid.

State Granting
This function is called when a character is granted a state.
The following three variables are used to get or set the value. 1.

  1. unit type (get): friendly=0, enemy=1
  2. unit number (get) : index (not ID) for both friend and foe
  3. state ID (get)

Value change other than damage
This is called when values other than HP change.
The arguments are aligned so that they can be handled in the same event as the damage pop.
The following 6 variables are used to get or set the value

  1. unit type (get): friendly=0, enemy=1
  2. unit number (get): index (not ID) for both friend and foe
  3. display position X (get)
  4. display position Y (get)
  5. type of value (get) : MP=3, Attack=4, Defense=5, Mental=6, Agility=7
  6. value (get)

Numeric arguments:

  • 00: Type of process to control (ATB=0, pop, target)
  • 01: How to specify the Common Event ID (constant=0, variable, variable number variable)
  • 02: Common event ID to use for control
  • 03: Beginning of the variable used to get/set the value

  • Control ATB Gauge, Code: 3010

This command is used to control the gauge of characters, including enemies and allies.
The effective range of the gauge is 0~300000.

Numeric Arguments:

  • 00: Type of target (protagonist ID=0, member, party, enemy character, whole enemy)
  • 01: How to specify the target (constant=0, variable, variable number variable)
  • 02: Value of the target
  • 03: Operation (set=0, add, subtract)
  • 04: Type of gauge value (value=0, percentage)
  • 05: How to specify a gauge value (constant=0, variable, variable number)
  • 06: Gauge value

  • Change Battle Commmand EX, Code: 3011

This command is used to enable/disable the change of formation item in the battle command and to edit the party command.

(1) Combat commands

This command can only be used during a battle and will be deactivated after the battle is over.
It should be used in conjunction with the Common Event, which requires the start of combat.

This command can be used to create a situation where there are no combat commands at all. In that case, the game will switch to AI action when you try to choose an action.

Added in 190220: When there are no commands, selecting a transparent item will change the formation

Party Commands:
You can choose any 4 commands from "Fight", "Auto", "Run", "Win" and "Defeat".
Victory" and "Defeat" will literally force you to win or lose the battle.
If you choose all five, the first four will be used, if you choose none, the default three will be used.
If you select only one of them, the screen transition itself will disappear.

Numeric arguments:

  • 00: Whether or not to use the change formation command (enable=0, disable)
  • 01: Flag for party command
    • 00: "Fight" command
    • 01: "Auto" command
    • 02: "Run" command
    • 03: "Win" command
    • 04: "Defeat" command

  • Get Battle Info, Code: 3012

Retrieves various values that appear during combat.
It does not do anything when not in combat.

The information is output consecutively, starting with the specified variable number.
Character ID/Member/Enemy Character

Ability Correction Value
Gets the value of the character's ability to move up and down during combat, in order of attack, defense, mental strength, and agility.

State:
The total number of all conditions and the number of turns elapsed since the condition was inflicted.
The number of turns is 1 when the condition is inflicted and 0 when it is not.
For example, if the database contains three conditions (1: death, 2: poison, 3: darkness), and you want to get the character's state immediately after the darkness
v[specified variable] = 3
v[variable+1] = 0
v[variable+2] = 0
v[variable+3] = 1
The result is

Attributes:
Gets the total number of values for the current attribute resistance and the status of each resistance.
The values are 2 for increased resistance, 1 for normal resistance and 0 for decreased resistance.
The value of each resistance may be omitted, in which case it is treated as the default value (1).
For example, if you have three attributes in the database (1: sword, 2: spear, 3: strike) and you want to get the character attributes for reduced sword resistance and increased strike resistance
v[specified variable] = 3
v[variable +1] = 0
v[variable +2] = 1
v[variable+3] = 2
The result will be

Other:
The following information about the specified character is obtained in order.
Image centre coordinates X
Image centre coordinates Y
Actionable state (disabled=0, possible)
Defensive state (None=0, Yes)
charge state (none=0, yes)
Appearance state (none=0, yes)

Numerical arguments:

  • 00: Target type (Protagonist ID=0, Member, Party, Enemy character, Enemy as a whole)
  • 01: Type of information. Protagonist ID/Member/Enemy Character: (Ability Correction=0, State, Attribute, Other). Party/enemy: (total number=0, survivors, active).
  • 02: How to specify the target (constant=0, variable, variable number).
  • 03: value of target
  • 04: beginning of variable to receive value

An array of consecutively numbered variables can be manipulated.
It is also possible to operate on a single variable by setting the length of the array to 1.

  • Copy: The value is copied from target 1 to target 2.
  • Exchange: The contents of target 1 and target 2 are exchanged.
  • Sort (ascending order)
  • Sort (descending order): Sort object 1 in ascending or descending order.
  • Shuffle: Subject 1 is sorted randomly.
  • Enumeration: The value for the size is stored in object 1 while adding it by 1 from an initial value.
  • Addition~Shr: It is similar to the operation command of the variable. The difference is that "operand" is also an array.

Numeric arguments:

  • 00: Operation (Copy=0, Exchange, Sort Ascending, Sort Descending, Shuffle, Enumerate, Add, Subtract, Multiply, Divide, Surplus, Or, And, Xor, Shl, Shr)
  • 01: Type of each value
    • 00~03: Object 1 (variable=0, variable number)
    • 04~07: Size (constant=0, variable, variable number)
    • 08~11: Object 2
      • For enumeration (constant=0, variable, variable number)
      • Other (variable=0, variable number)
  • 02: Value of object 1
  • 03: Value of size
  • 04: Value of object 2

The Joypad part is not supported by EasyRPG Player as we provide our own remapping in the configuration scene

This command cannot wait for a key to be pressed.
If you assign keys to the joypad, it will be possible to include the pad when getting the keyboard status.

Get keyboard status list:
Get the status of keys in the keyboard list of the form at once.

Get Keyboard Status List (Disable Assignment):
Get the status of all keys in the keyboard list of the form.
Even if the joypad is assigned, this is ignored.

Get status by key code (allocation disabled):
You can get the status of keys by specifying the key code (0-255) directly.
Use this function when you want to get the status of a key that is not in the list.

Get joypad status list:
Get the status of buttons in the joypad list of the form at once.

Get Joypad Assignment:
Get the current joypad assignment status.
If the button is not assigned, -1 is returned.

Setting joypad assignments:
Assign a joypad button to one of the keys in the key list.

Numeric arguments:

  • 00: Operation details (from 0 to 5 in the order listed above)
  • 01: Start of the variable array used to get or set the value
  • 02: (*only when key code is specified) How to specify the code
    (constant = 0, variable, variable number variable)
  • 03: (*only when keycode is specified) Code

  • Rewrite Map, Code: 3015

This command rewrites the tiles on the map where the hero is.
Changes made by this command are not reflected in the save and will disappear when you move the map.

Numeric arguments:

  • 00: Type of each value
    • 00~03: Tile ID
      - When single (constant=0, variable, variable number variable)
      - For a range (variable=0, variable number)
    • 04~07: Left end of change range (constant=0, variable, variable number)
    • 0811: Top of change range (Constant=0, Variable, Variable)
      - 12
      15: Width of change range (Constant=0, Variable, Variable)
      - 16~19: Changed range height (Constant=0, Variable, Variable)
  • 01: How to specify a tile (ID=0, Range ID)
  • 02: Layer (lower layer=0, upper layer)
  • 03: Value of tile ID or beginning of variable array
  • 04: Value of the left end of the change range
  • 05: Top value of the modified range
  • 06: Value of the width of the modified range
  • 07: Value of the height of the modified range
  • 08: Auto tile (enable=0, disable=1)

This command deals with the common save data that can be read and written from each save data.
Currently, only switches and variables are supported.
The file name is fixed to "Save.lgs".

Open:
Loads the shared save data from a file.
This operation is ignored when the file is loaded.

Close:
Release the loaded shared save data.
This operation is ignored if the file is not loaded.
Note that this operation does not save the changes.

Save:
Saves the contents of the loaded shared save data to a file.
This operation will be ignored if the file is not loaded.

Save and close: After "Save", "Close" will be executed.

Copy from shared save:
Loads the specified values from a loaded shared save into your own save.
If you do this when the save is not loaded, it will execute 'Open' before processing.

Copy to shared save:
Writes the specified value from your own save data to the loaded shared save data.
If you do this when the save is not loaded, it will execute "Open" before processing.

Numerical arguments

  • 00: Operation details (from 0 to 5 in the above order)

For copy operations only:

  • 01: Type of each value
    • 00~03: First number of the current save (constant=0, see variable)
    • 04~07: First number of shared save (constant=0, see variable)
    • 08~11: Size (constant=0, variable, variable number variable)
  • 02: Type of data (Switch=0, Variable)
  • 03: Value of current save
  • 04: Value of shared save
  • 05: Value of size

  • Change Picture ID, Code: 3017

As the name suggests, this command changes the ID of the picture.

If you use the "Ignore out-of-range errors" setting, moving from/to an out-of-range ID will be replaced by a simple delete operation.

Move:
Change the picture ID of (size) from (target1) to (target2).
If (Target2) is in use, it will be erased.

Exchange:
The picture ID of (size) from (target1) is replaced with (target2).

Slide:
Move the picture ID of (size) from (target1) by (distance).

Numeric arguments

  • 00: Operation (Move=0, Swap, Slide)
  • 01: Type of each value
    • 00~03: Target 1 (constant=0, variable, variable number variable)
    • 04~07: Size (constant=0, variable, variable number)
    • 08~11: Object 2 (Constant=0, Variable, Variable number)
  • 02: Value of object 1
  • 03: Value of size
  • 04: Value of target 2 or distance
  • 05: Ignore out-of-range errors (enable=0, disable)

  • Set Game Option, Code: 3018

This command is used to set the overall behaviour of the game.

Inactivity behaviour:
Whether or not to pause the game when a window becomes inactive.
The default is wait. This setting is reflected in the save data.

Fatal Mix:
This is mainly for debugging purposes.

FPS:
The speed at which the game will run, cannot be less than 1.
This setting is not reflected in the save data.

Test Play:
This is the same as the "TestPlay" start-up option.
This setting will be reflected in the saved data.

Right Shift to skip messages:
While holding down the right shift key, all messages will be displayed momentarily and will close automatically.
This setting is reflected in the save data.

Maximum number of pictures:
This is the maximum number of pictures you can have.
This number has a significant effect on the speed of the game and the load time of saved data.
This setting will be reflected in the saved data.

Frame skipping:
Changes the default drawing process from every frame to once every n frames.
This setting is reflected in the save data.

Numeric arguments:

  • 00: Type of value for argument 02 (constant = 0, variable, variable number variable)
  • 01: Type (Inactive=0, Fatal, Picture, Frame skip)
  • 02:
    • Inactive: Action (Weight=0, Run)
    • Fatal: FPS value
    • Picture: maximum value
    • Skip: value (none=0, 1/5, 1/3, 1/2)
  • 03: Fatal:
    • 00~03: Test play (Do not change=0, Disable, Enable)
    • 04~04: Sentence skip

EasyRPG implementation difference: Instead of invoking the command directly it pushes a new frame with a single new command and invokes it (is like a CallEvent with a single command).

This command is intended for advanced users. Please make sure you fully understand the command specifications before using it.

Invokes any command (with exceptions) with any argument.
If a non-existent code is specified, the behaviour depends on the value.

Unavailable commands:

  • Calling a command

Commands that do not work:

  • Set label

Commands that do not work properly:

  • Show choices
  • Handling of battles *only if branching
  • Shop Handling" *only when branching
  • Handling of the inn.
  • Conditional branching
  • Repetition
  • Display of text from the second line onwards
  • Annotations from the second line onwards

String arguments: Code dependent
Numeric Arguments: Code dependent

  • 00: Type of each value
    • 00~03: Code (constant=0, variable, variable number variable)
    • 04~07: First numerical argument (constant=0, variable, variable number)
    • 08~11: Number of numerical arguments (constant=0, variable, variable number)
  • 01: Code
  • 02: Leading number of numerical arguments
  • 03: Number of numerical arguments

Modified commands

Recycles the now unused "bottom transparency" (arg 14) setting as a bit field (seriously...) but the bit field starts at 256 which means it can't be confused with the transparency where the maximum value that makes sense is 255.

  • Blend Mode: Multiple: Bit 9 (256)
  • Blend Mode: Add: Bit 10 (512)
  • Blend Mode: Overlay: Bit 9 + 10 (768)
  • Flip H: Bit 13 (4096)
  • Flip V: Bit 14 (8192)

The origin uses the upper bits of arg 1 which is the "ValueOrVariable" indicator for X/Y.

Rotation:

  • Arg 12 (effect) set to 3
  • Arg 13 & Arg 15: Angle1 / Angle2
  • Arg 16 encodes ValueOrVariable in a bitmask: 0 Constant, 1 Variable, 2 Variable ID. Shifted by 4 for 2nd option.


see #1818 (comment)


  • Conditional Branch Maniac Patch: Final contribution #2797 (Condition "Just Loaded" is not implemented yet!)
  • The method of specifying it by the variable reference was added to the switch.
  • The method of specifying it by the variable of the variable number to the left side value of the variable is added.
  • The method specified by the variable of the variable number was added to the right side value of the variable. Supported
  • Judgment of whether it is just after loading was added.
  • Judgment of whether the joypad is active or not was added.
  • Added judgement whether the window is active or not

Added support for mouse input (left/right centre click, wheel up/down scroll).
Wheel scroll is only valid under the condition of "wait until pressed" because there is no push down state.

The way how the Key input is encoded is a bit strange because it recycles other parameters but by looking at the ones it uses I see some logic.
When the original checkbox is ticked the value becomes 3 (which means it's a bitfield)

Wheel down: Arg 10 = 2 (check_down)
Wheel up: Arg 13 = 2 (check_up)
Mouse left: Arg 3 = 2 (check_decision)
Mouse right: Arg 4 = 2 (check_cancel)
Mouse middle: Arg 9 = 2 (check_shift)


  • Control Variables Maniac Patch: Final contribution #2797 Limited support for the Expression format. Rest is fully supported
  • A range of variable values has been added to the operation target.
  • When a > b in a batch operation, the result is now treated as b~a.
  • Logical OR (or), logical product (and), exclusive logical OR (xor), and logical left-right shift (shl, shr) are added to the operation contents. Proper overflow handling #2734
  • added ID and ATB gauge entries for each character in "operand" (hero and enemy)
  • added items for current date, time and elapsed frame in "operand" section
  • added a switch in "operand" which will be treated as 1 when ON and 0 when OFF
  • added variables for each of "operand" items, hero, event, and enemy character
  • added a party member to "operand". Same as the main character, except that they are specified by an index in the party.
  • added a maths function to "operand".
  • get current event id Support game Ipomoea #3032

In the following explanation, argument 1 is a, argument 2 is b, and multiplier is c.

  • Add, Sub, Mul, Div, Mod, Or, And, Xor, Shl, Shr Proper overflow handling #2734
  • a (operator) b performs the same operation as in "Description".
  • Pow: Calculates a to the b power.
  • Sqrt: Calculates the square root of a * c. Decimal points are rounded down.
  • Sin: Calculates the sine of (a / b) degrees * c. Decimal points are rounded down.
  • Cos: Calculates the cosine * c in (a / b) degrees. Decimal points are truncated.
  • Atan2: Calculates the inverse tangent * c of a(y-coordinates), b(x-coordinates). Decimal points are truncated.
  • Min: Returns the smaller of a, b.
  • Max: Returns the greater of a and b.
  • Abs: Returns the absolute value of a.
  • Random: returns a random number in the range a~b. Same as "random" in "operand".
  • adds ternary operation to "operand".
  • adds patch version to "operand".
  • date in lower 20bit (YYMMDD).
  • The most significant bit represents im/pf (im=0, pf).
  • The rest is reserved space.
  • This item was implemented in 190920, so all bits will be 0 in earlier versions.

How to get the value of a date

  • @> Manipulation of variables: [0001] Assignment, Maniacs version
  • @> Variable manipulation: [0001] And, -1 >> 12

How to get the im/pf value

  • @> Manipulation of Variables: [0001] Assignment, Maniacs' Version
  • @> Manipulation of Variables: [0001] Shr, 31

Add expression format to target and operand


Added method to specify common event by variable number variable.


Added the ability to specify conditions. The switch is treated as a number with 1 for ON and 0 for OFF.
The number of iterations and the index will not be set correctly if a label jump brings you inside the loop.

Infinity: Conventional infinite loop.
Number of times:

  • X Times:
    • Repeats for a specified number of times. You can optionally print an index starting from 0 to a variable.
  • Count up
    • Iterate through the range a to b, adding indices by 1.
    • For example, 2~5 would be a loop of 4 times: 2, 3, 4, 5.
    • You can optionally output the current index to a variable.
  • Count Down:
    • Iterates over the range a to b, decrementing the index by 1.
    • For example, 3~1 would be a loop of 3, 2, 1.
    • You can optionally output the current index to a variable.

While:

  • Repeats as long as the condition is true. Each loop is checked before execution.
  • Comparisons can be specified in the same way as for variable conditionals.
  • You can optionally print an index starting from 0 to the variable.

Do While:

  • Repeats as long as the condition is true. This is done after each loop.
  • Comparisons can be specified in the same way as for variable conditionals.
  • You can optionally print an index starting from 0 to the variable.

see #1818 (comment)


Fixes behaviour when running inside a multiple loop.


  • Battle Processing

Added option to disable flashing before battle starts.

Additional features supported from newer releases

@Ghabry
Copy link
Member Author

Ghabry commented Jul 3, 2019

New versions 190625 and 190630 released. No new features, just unspecified fixes for the new commands.

@Ghabry
Copy link
Member Author

Ghabry commented Sep 4, 2019

New version 190904. There is now a website for the patch: https://bingshan1024.github.io/steam2003_maniacs/

Just a quick summary of notable changes as my time is limited right now:

  • 32 bit framebuffer (files still limited to 8bit)
  • KeyInputProcEx: Can read the keyboard and joypad, does not support "wait for input" (guess this was too difficult to write ;))
  • Rewrite Map: Change tiles, they are not saved and lost when the map scrolls. Sounds quite... experimental o.O
  • Global Save: Allows writing of switches and variables in a "shared savefile"

@Ghabry
Copy link
Member Author

Ghabry commented Apr 15, 2020

For later: There is some Command dispatch table in the CHERRY segment (guess this was relocated from the normal Code segment, havn't analyzed RPG_RT that much yet).

CHERRY:004F40F8 eventDispatchTable dd offset CmdOpenLoadMenu
CHERRY:004F40F8
CHERRY:004F40FC dd offset CmdExitGame
CHERRY:004F4100 dd offset CmdToggleATBWaitMode
CHERRY:004F4104 dd offset CmdToggleFullscreen
CHERRY:004F4108 dd offset CmdOpenVideoOptions
[...] Normal commands here [...]
Starting at 0x78
CHERRY:004F42D8 dd offset CmdManiacGetSaveInfo
CHERRY:004F42DC dd offset CmdManiacSave
CHERRY:004F42E0 dd offset CmdManiacLoad
CHERRY:004F42E4 dd offset CmdManiacEndLoadProcess
CHERRY:004F42E8 dd offset CmdManiacGetMousePos
CHERRY:004F42EC dd offset CmdManiacSetMousePos
CHERRY:004F42F0 dd offset CmdManiacShowStringPic
CHERRY:004F42F4 dd offset CmdManiacGetPicInfo
CHERRY:004F42F8 dd offset CmdManiacCtrlBattle
CHERRY:004F42FC dd offset CmdManiacCtrlAtbGauge
CHERRY:004F4300 dd offset CmdManiacBattleCmdEx
CHERRY:004F4304 dd offset CmdManiacGetBattleInfo
CHERRY:004F4308 dd offset CmdManiacCtrlVarArray
CHERRY:004F430C dd offset CmdManiacKeyInputEx
CHERRY:004F4310 dd offset CmdManiacRewriteMap
CHERRY:004F4314 dd offset CmdManiacCtrlGlobalSave
CHERRY:004F4318 dd offset CmdManiacChangePicID
CHERRY:004F431C dd offset CmdManiacSetGameOption
CHERRY:004F4320 dd offset CmdManiacCallCommand
CHERRY:004F4324 dd offset sub_4FE970 <- No idea what that is

@Ghabry
Copy link
Member Author

Ghabry commented Mar 12, 2021

About the savedata:
GetSaveInfo simply uses "..\FaceSet" to point to the Face and ShowStringPicture encodes the string parameter list via \x01: &#xe001;aaaa&#xe001;SystemC&#xe001;RPG2000G&#xe000;. This looks ugly but one has to admit that it makes sense. Event commands only support single text arguments and this is a good short-term workaround when you do not have sourcecode access.

@Ghabry
Copy link
Member Author

Ghabry commented Mar 12, 2021

I asked BingShan months ago about a documentation for the "Variable Expression" feature. The expression is compiled to an op code list by the editor and these opcodes are interpreted by the engine:

@> command arguments:
Composition of numeric arguments when the target and "operand" are expressions in a variable operation

arg0: fixed value 4, meaning that the target is an expression
arg1: the starting index of the target expression (in this case argX+1)
arg2: not used
arg3: operation content
arg4: fixed value meaning that "operand" is an expression21
arg5: length of the "operand" expression
arg6 ... argX: "operand" expression
argX+1: length of the target expression
argX+2 ... argY: target expression



@> expression: 
At the moment an expression consists of a single node



@> node: 
A node consists of one ID (1 byte) and zero or more arguments immediately following it.
The number and type of arguments depends on the ID



@> Node ID list: 
[ID] name (number of arguments): description


[00] null(0): 
    Default node to be used if the required arguments are missing.
    Evaluation value is 0

[01] decimal integer(1): 
    arg0: 1-byte (0 ... 255) integer 255) integer

[02] Decimal integer(1): 
    arg0: integer with 2 bytes (0 .. 65535) integer

[03] Decimal integer(1): 
    arg0: integer with 4byte(-2147483648 ... 2147483647) integer

[04] Hexadecimal integer (1): 
    arg0: integer with 1byte(0 ... 255) integer

[05] Hexadecimal integer (1): 
    arg0: integer with 2 bytes (0 .. 65535) integer

[06] Hexadecimal integer(1): 
    arg0: integer with 4byte(-2147483648 ... 2147483647) integer

[07] (?) : reserved

[08] variable reference (1): 
    arg0: node v[arg0].

[09] switch reference(1): 
    arg0: node s[arg0].

[10] (?) : reserved
[11] (?) : reserved
[12] (?) : reserved

[13] Variable number variable reference(1): 
    arg0: node v[v[arg0]]

[14] Variable number switch reference(1): 
    arg0: node s[v[arg0]]
    
[15] (?) : reserved
[16] (?) : reserved
[17] (?) : reserved
[18] (?) : reserved

[19] Pseudo-array(*):  
    arg0: Number of 1-byte elements. However, if the most significant bit of this value (0x80) stands, the next 4 bytes are used as the number of elements.
    arg1... : Node Element
    
[20] (?) : reserved
[21] (?) : reserved
[22] (?) : reserved
[23] (?) : reserved

[24] Sign reversal(1): 
    arg0: node (-arg0)

[25] Negation(1):
    arg0: node (!arg0)

[26] Bit-flip(1): 
    arg0: node (~arg0)
    
[27] (?) : reserved
[28] (?) : reserved : reserved
[29] (?) : reserved
[30] (?) : reserved
[31] (?) : reserved
[32] (?) : reserved
[33] (?) : reserved

[34] Assignment(2): 
    arg0: 
    arg1: node (arg0 = arg1)
    
[35] In-place addition (2): 
    arg0:
    arg1: node (arg0 += arg1)
    
[36] In-place subtraction (2): 
    arg0:
    arg1: node (arg0 -= arg1)

[37] In-place multiplication(2): 
    arg0: 
    arg1: node (arg0 *= arg1)

[38] In-place division(2): 
    arg0: 
    arg1: node (arg0 /= arg1)

[39] In-place remainder (2): 
    arg0: 
    arg1: node (arg0 %= arg1)

[40] In-place bitwise disjunction(2): 
    arg0: 
    arg1: node (arg0 |= arg1)

[41] In-place bitwise disjunction (2): 
    arg0: 
    arg1: node (arg0 &= arg1)

[42] In-place bitwise exclusive or (2): 
    arg0: 
    arg1: node (arg0 ^= arg1)

[43] In-place left bit shift(2): 
    arg0: 
    arg1: node (arg0 <<= arg1)

[44] In-place right bit shift (2): 
    arg0: 
    arg1: node (arg0 >>= arg1)

[45] (?) : reserved
[46] (?) : reserved : reserved
[47] (?) : reserved

[48] Addition (2): 
    arg0: 
    arg1: node (arg0 + arg1)

[49] Subtraction(2): 
    arg0: 
    arg1: node (arg0 - arg1)

[50] Multiplication(2): 
    arg0: 
    arg1: node (arg0 * arg1)

[51] division(2): 
    arg0: 
    arg1: node (arg0 / arg1)

[52] Surplus(2): 
    arg0: 
    arg1: node (arg0 % arg1)

[53] Bitwise OR (2): 
    arg0: 
    arg1: node (arg0 | arg1)

[54] Bitwise conjunction (2): 
    arg0: 
    arg1: node (arg0 & arg1)

[55] Bitwise exclusive disjunction(2): 
    arg0: 
    arg1: node (arg0 ^ arg1)

[56] Left bit shift(2): 
    arg0: 
    arg1: node (arg0 << arg1)

[57] Right bit shift (2): 
    arg0: 
    arg1: node (arg0 >> arg1)

[58] Compare Same as ~(2): 
    arg0: 
    arg1: Node (arg0 == arg1)

[59] Compare Same as (2): 
    arg0: 
    arg1: node (arg0 >= arg1)

[60] Comparison - Less than or equal to (2): 
    arg0: 
    arg1: node (arg0 <= arg1)

[61] Compare - Greater than (2): 
    arg0: 
    arg1: node (arg0 > arg1)

[62] Compare - Less than (2): 
    arg0: 
    arg1: node (arg0 < arg1)

[63] Compare - other than (2): 
    arg0: 
    arg1: node (arg0 ! = arg1)

[64] Logical OR (2): 
    arg0: 
    arg1: node (arg0 || arg1)

[65] Logical product (2): 
    arg0: 
    arg1: node (arg0 && arg1)

[66] Range (2): 
    arg0: 
    arg1: node (arg0 ... arg1)

[67] Subscript access(2): 
    arg0:
    arg1: node arg0[arg1].

[68] (?) : reserved
[69] (?) : reserved
[70] (?) : reserved
[71] (?) : reserved

[72] Ternary operation(3):  
    arg0:
    arg1:
    arg2: node (arg0 ? arg1 : arg2)

[73] (?) : reserved
[74] (?) : reserved
[75] (?) : reserved
[76] (?) : reserved
[77] (?) : reserved

[78] Built-in functions (*): 
    See below.


    
@> Arguments for built-in functions:
arg0: 1-byte type of function (ID).
arg1: 1byte Number of arguments. However, if the most significant bit of this value (0x80) stands, the next 4 bytes are used as the number of arguments.
arg2... : node Arguments given to the function to call



@> Type of built-in function:
[ID] name (number of arguments)

[00] rnd(2)
[01] item(2)
[02] event(2) 
[03] actor(2) 
[04] member(2) 
[05] enemy(2) 
[06] misc(1) 
[07] pow(2) 
[08] sqrt(2) 
[09] sin(3) 
[10] cos(3)
[11] atan2(3) 
[12] min(2) 
[13] max(2) 
[14] abs(1)
[15] clamp(3) 
[16] muldiv(3) 
[17] divmul(3) 
[18] between(3)

Translated with www.DeepL.com/Translator (free version)

@Ghabry
Copy link
Member Author

Ghabry commented Mar 16, 2021

Chunk information:

SaveSystem:

Set by command "Set Game Option":

88: FrameSkip (kinda useless)

  • 0 = None
  • 1 = 1/5
  • 2 = 1/3
  • 3 = 1/2

89: Picture limit (useless, ours is dynamic)

8A: Fatal Mix stuff 4 Byte:

XX XB XC XD

B:

  • 0 = Off
  • 4 = Skip message with RShift

C:

  • 0 = TestPlay Retain
  • 2 = TestPlay OFF
  • 4 = TestPlay ON

D:

  • 1 = Inactive Run
  • 0 = Wait

8B: Unknown
16 Bytes of FF

SaveEventExecFrame:
0E: Unknown
10: Unknown

12: 2x Little Endian Int32 (-_-) Modified by Loop command

    1. Current Loop Value
    1. Loop End (the loop end is already evaluated when the loop begins)
      For While/Do While the values are undefined (makes sense, as these are not counting loops)

@Ghabry
Copy link
Member Author

Ghabry commented Mar 26, 2021

About the new common event things:

  • The "Battle Start" condition is imo useful and also not really dangerous (executes before the Turn 0 battle page)
  • "Battle (Parallel) is unsurprisingly totally unsafe. Took me minutes to find a way to crash it with a null pointer read.

@CherryDT
Copy link

For later: There is some Command dispatch table in the CHERRY segment (guess this was relocated from the normal Code segment, havn't analyzed RPG_RT that much yet).

This makes sense because I left an area dedicated for patching in the CHERRY segment (with a note embedded in the segment that says patches should go here). :)

@Ghabry
Copy link
Member Author

Ghabry commented May 30, 2021

I redid the entire initial post via DeepL (was a Google Translate before) and improved the formating. Also updated to the latest version before the recompile approach (this is 210414).

I see no reason in supporting anything post 210414 because this was reverse-compiled allowing huge changes to the engine.


Good news: I was able to find an older branch where I worked on Maniacs again. Thought I lost it but was just on a different PC :)

https://github.com/Ghabry/easyrpg-player/tree/maniac-events

That branch implements:

  • New command: GetSaveInfo
  • New command: Save (not compatible, yields back to the Update loop and uses the Async feature for saving)
  • New command: Load (also via Async yield)
  • New command: Control Var Array
  • Modifed command: Control Variables: Added Date, Time, Frames and Patch-Version (210414)
  • Planned: Modified command: Read Mouse state via Key Input Proc

@Ghabry
Copy link
Member Author

Ghabry commented Aug 30, 2021

Also interesting are the new loop commands that can do more than just "endless loop". It also adds Count up/down and (do) while. Pretty useful if you ask me. The commands completely replace the old ones, here are the function addresses:

  • Begin Loop: 4FF870
  • Repeat Loop: 4FF9E0 (thats the event at the bottom of a loop)
  • Break Loop: 4FF710

And here what they do (not motivated to code right now, so just documenting it for later)

Loop

Param 0 is the loop type. 0 (or no argument) is a normal, endless loop.

For all other cases when too short it allocates "8 * (depth + 1)" byte for maniac_loop_info and sets maniac_loop_info_size to "depth + 1". (This was already known and is in liblcf). These loop info contain a current and a end value for each depth (this allows having loops inside loops properly).

  • Arg 1: Type (Value, Variable, etc.)
  • Arg 2: Index for Arg1

For "X Times"

  • Current = 0
  • End = Result of Arg1/2 lookup

Count Up

  • Current = Result of 1/2
  • End = Result of (1 >> 4)/3 (so the type parameter is for whatever reason used to define both types)

This is like "X Times" but the Begin value can be set.

Count Down

Like count up, but the values Current and End receive are swapped

While

Assignment like Count Up

When Current == End, Current = End = 0, also Skips shared logic, except for Arg4

Do While

Current = 0, End is kept as is
Skips shared logic, except for Arg4

Shared logic

When Current <= End: Arg 4 is a Variable that receives "Current "
Otherwise: Loop is skipped

Repeat Loop

The arguments from Loop are duplicated in here (makes sense, otherwise it would be necessary to scan for the loop command)

When Param 0 is 0 or missing it is the normal endless loop

In the other cases: It allocates as above if too short (likely safety reasons to prevent crashes)

"X Times" and "Count Up"

Increment Current by 1.
When Current > End: Ends the loop

Count Down

Decrements Current
When End > Current: Ends the loop

"While" and "Do While"

(not sure how the operator is encoded, easiest way here is just changing stuff and looking in lcf2xml output)

Contrary to the X Times and Count Up/Down where the value is resolved in the Loop command here the values are resolved everytime "Repeat Loop" is reached.

When the condition for the two checked values is true (depending on the operator) then "current" is incremented by 1. End is never touched.

When the condition is false the loop is aborted.

Shared logic

When the loop is continued then the variable specified by arg4 received "current".

Break Loop

This doesn't touch the loop_info stuff, also no cleanup. It just fixed the bug that break does not break the correct loop when there are multiple loops active.

@Ghabry
Copy link
Member Author

Ghabry commented Aug 30, 2021

Erase Picture

at 4FD16C

Okay, no surprises here but better to take a look ;)

  • Arg 0: Value
  • Arg 1: Type of operation

Before 2k3E this only supported "Arg 0 = Picture ID"

Cherry added:

  • "Arg 1 = 1", then Arg 0 is Variable resolved to Picture ID
  • "Arg 1 = 2", then this is a range from [Arg0, Arg2]

BingShan added:

  • "Arg 1 = 3", Like the range, but Arg0 and Arg2 are Variables resolved to Values
  • "Arg 1 = 4", Variable indirect (V[V[Arg0]])
  • "Arg 1 = 5", Erase all pictures via a loop

@Ghabry
Copy link
Member Author

Ghabry commented Aug 30, 2021

New command: Control Var Array

The arguments are already documented by BingShan (see top comment)

tl;dr: Nothing surprising here (good)

This basicly operates on two slices in the variables array.

When the resolved value of "slice1" is < 1 or the lengh of the slice is <= 0: Abort the command.

Note:
In all cases, even when aborting without write the Map events are refreshed
I omit notes about resizing the array.

Copy

When target (slice2) < 1: Abort.
Otherwise: memcpy from slice2 into slice1

Exchange

When target (slice2) < 1: Abort.
This swaps the values in the two slices (loop goes backward but this is irrelevant)

Sort (Ascending) & Sort (Descending)

Does a numeric sort
Slice2 is ignored

Shuffle

This loops backwards. For each element pick a random number from the slice and then swap current with the picked one.
Slice 2 is ignored

Enumerate

This loops backwards again (maybe is a compiler optimisation...)
Slice 2 is just a constant here.
This fills Slice 1 with incrementing numbers until len.

E.g. The constant is 2 and len is 3, then the result in the slice is 2, 3, 4.

Everything else (Math operations from Add to Shr)

This shares code:
The result is in slice2 (so different to "Copy" where the result is in slice1)

For Div and Mod: In the 0-case a 1 is assumed.

Operation order, relevant for Div, Mod, SHL, SHR: slice1_val OP slice2_val.

@Ghabry
Copy link
Member Author

Ghabry commented Aug 30, 2021

Here a list of stuff that I personally consider worth implementing.

Italic = Already implemented or PR open

  • Get Save Info
  • Save
  • Load
  • GetMousePosition
  • Show String Picture (likely not possible to get 100% perfect here, needs Freetype for Fonts)
  • Control Var Array (*)
  • Enhancements to "Show Picture"
  • Enhancements to "Move Picture"
  • Enhancements to "Erase Picture"
  • Enhancements to "Condition Branch" except "Joypad is active" and "Window is active" (*)
  • Enhancements to "Key Input Processing"
  • Enhancements to "Control Variables" except for the expression format because this is extremely complex
  • Enhancements to "Call Event"
  • Enhancements to "Loop" (*)
  • Bugfix in "Break Loop" (*)
  • Enhancements to "Battle processing"

Maybe, because they are simple but not too useful:

  • Get Picture Info
  • Change Picture ID

Maybe (very low priority):

  • Key Input Proc Ex (not portable)
  • Control Global Save (lots of work to add)

Not interested at all:

All this battle stuff because it uses hacky things like "parallel processed in battle".

2k3 battle is already terrible, I will not touch this.

  • Control Battle
  • Control ATB Gauge
  • Change Battle Command Ex
  • Get Battle Info
  • Rewrite Map (kinda cool, but too much of a hack)
  • Game Options
  • Call Command (totally unsafe command)

@Ghabry
Copy link
Member Author

Ghabry commented Aug 31, 2021

Example for the expression format:

Formula:
100 * 101 / 102 + actor(103, 104)

Storage:
byte meaning
48   +
51   /
50   *
1    int8
100  100
1    int8
101  101
1    int8
102  102
78   function
3    actor
2    taking 2 args
1    int8
103  103
1    int8
104  104

Unfortunately this is not stored in postfix notation which would make evaluating the expression easy:
postfix means values are pushed on the stack and operators consume from the stack and push the result on the stack:

byte meaning stack
1    int8
100  100     [100]
1    int8   
101  101     [100, 101]
50   *       [10100]
1    int8
102  102     [10100, 102]
51   /       [99]
1    int8   
103  103     [99, 103]
1    int8
104  104     [99, 103, 104]
78   function
3    actor   [99, 42] <- made up result
48   +       [141]    <- Final result

@Ghabry
Copy link
Member Author

Ghabry commented Sep 9, 2021

Unfinished patch for the fixed_angle: This is more tricky than expected because this seems to use 360 degree angles instead of 256 (wtf?), so needs custom code to work properly... I give up on this for now, my progress before reset:

diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp
index 95db656b0..6579520d8 100644
--- a/src/game_interpreter.cpp
+++ b/src/game_interpreter.cpp
@@ -2624,6 +2624,11 @@ bool Game_Interpreter::CommandShowPicture(lcf::rpg::EventCommand const& com) { /
 			}
 			params.flip_x = (flags & 16) == 16;
 			params.flip_y = (flags & 32) == 32;
+
+			if (params.effect_mode == lcf::rpg::SavePicture::Effect_maniac_fixed_angle) {
+				params.effect_power = ValueOrVariable(com.parameters[16] & 0xF, params.effect_power);
+				params.effect_power /= ValueOrVariable((com.parameters[16] & 0xF0) >> 4, com.parameters[15]);
+			}
 		}
 	}
 
@@ -2698,6 +2703,11 @@ bool Game_Interpreter::CommandMovePicture(lcf::rpg::EventCommand const& com) { /
 			}
 			params.flip_x = (flags & 16) == 16;
 			params.flip_y = (flags & 32) == 32;
+
+			if (params.effect_mode == lcf::rpg::SavePicture::Effect_maniac_fixed_angle) {
+				params.effect_power = ValueOrVariable(com.parameters[16] & 0xF, params.effect_power);
+				params.effect_power /= ValueOrVariable((com.parameters[16] & 0xF0) >> 4, com.parameters[15]);
+			}
 		}
 	} else {
 		// Corner case when 2k maps are used in 2k3 (pre-1.10) and don't contain this chunk
diff --git a/src/game_pictures.cpp b/src/game_pictures.cpp
index f14be9b01..881eb8e0b 100644
--- a/src/game_pictures.cpp
+++ b/src/game_pictures.cpp
@@ -189,7 +189,11 @@ bool Game_Pictures::Picture::Show(const ShowParams& params) {
 	SyncCurrentToFinish<true>(data);
 	data.start_x = data.current_x;
 	data.start_y = data.current_y;
-	data.current_rotation = 0.0;
+	if (data.effect_mode == lcf::rpg::SavePicture::Effect_maniac_fixed_angle) {
+		data.current_rotation = data.finish_effect_power;
+	} else {
+		data.current_rotation = 0.0;
+	}
 	data.current_waver = 0;
 	data.time_left = 0;
 
@@ -423,18 +427,16 @@ void Game_Pictures::Picture::Update(bool is_battle) {
 		}
 	}
 
-	if (data.effect_mode != lcf::rpg::SavePicture::Effect_none) {
-		data.current_effect_power = interpolate(data.current_effect_power, data.finish_effect_power);
-	}
-
 	// Update rotation
 	if (data.effect_mode == lcf::rpg::SavePicture::Effect_rotation) {
 		data.current_rotation += data.current_effect_power;
+		data.current_effect_power = interpolate(data.current_effect_power, data.finish_effect_power);
 	}
 
 	// Update waver phase
 	if (data.effect_mode == lcf::rpg::SavePicture::Effect_wave) {
 		data.current_waver += 8;
+		data.current_effect_power = interpolate(data.current_effect_power, data.finish_effect_power);
 	}
 
 	// RPG Maker 2k3 1.12: Animated spritesheets
-- 

@Ghabry
Copy link
Member Author

Ghabry commented Feb 24, 2022

The expression format is actually not that hard to parse. Instead of a stack you need recursion (almost ready :))
Ghabry@e76212a

Also getting close to ShowPicInfo working...

@Ghabry
Copy link
Member Author

Ghabry commented Feb 26, 2022

They also changed how math works.

For sqrt they do sqrt(abs(value)) and if the value was negative before the result is multiplied with * -1.

wtf.

@XPMilkChan
Copy link

XPMilkChan commented Oct 13, 2022

I ran into some problems with this patch.
I've downloaded a sample project from here.
http://www.rmteka.pl/maniacs-patch-nie-gryzie-9-wykorzystanie-myszy/
patch Last Version patch_maniacs_211010
bandicam 2022-10-13 19-52-43-365
easyrpg_log.txt

@CherryDT
Copy link

I ran into some problems with this patch. I've downloaded a sample project from here. http://www.rmteka.pl/maniacs-patch-nie-gryzie-9-wykorzystanie-myszy/ patch Last Version patch_maniacs_211010 bandicam 2022-10-13 19-52-43-365 easyrpg_log.txt

As shown in the opening post, this command is not yet supported.

@Ghabry
Copy link
Member Author

Ghabry commented Oct 29, 2022

A problem with ShowStringPic is the font name: It uses the name of the installed font reported by the Windows Api. Not the filename.

Obtaining installed fonts on all platforms is tricky (not portable) and imo not worth it because this gives complaints like "I run this on Android but the text is wrong".

There could be some heuristic probing like this (assuming font is e.g. "Noto Sans"):

  • Font/Noto Sans.ttf
  • Font/NotoSans.ttf
  • Font/NotoSans-Regular.ttf (and -Bold when asking for bold)

@Ghabry
Copy link
Member Author

Ghabry commented Nov 10, 2022

🤓

https://github.com/Ghabry/easyrpg-player/tree/maniac/stringpic

screenshot_1

@Ghabry
Copy link
Member Author

Ghabry commented Dec 5, 2022

Imo BingShan confused the units in the size calculation of the window (pt vs. px).

E.g. when using a Font size of 32 pt (24 px) the Maniac Patch assumes for every line a height of 32 px. Which is a mistake of 8 px per line. By using these incorrect units everywhere the calculation perfectly matches up though.

Here an example rendering with a 32 pt (24 px) text and a gap between the lines (can be configured) of 8 px. Maniac version has lots of margin everywhere because of this error.

Player ↔️ Maniac

a

Note: This message is not supposed to be offensive (language barrier). It just documents a potential bug.

@Ghabry
Copy link
Member Author

Ghabry commented Dec 5, 2022

Though after understanding the problem this is Easy to emulate.

That is a quirk good to be fixed in "EasyRPG exclusive" games made with our shiny, nonexistant editor 😓

b

@Ghabry Ghabry pinned this issue Dec 8, 2022
@jetrotal
Copy link
Contributor

jetrotal commented Dec 8, 2022

New from TPC

Play SE/BGM supporting variables - and nested variables:

Play SE
Parameters:v[n] v[n] v[n] [long-string]

image


Play BGM
Parameters:v[n] v[n] v[n] v[n] [long-string]

image

@jetrotal
Copy link
Contributor

jetrotal commented Dec 8, 2022

New from TPC

Display Text Options supporting Box-Size - and Font + Font-Size:

Display Text Options
Parameters:
6th: box-width
7th: box-height

String: Font-Name
8th: 0 (???)
9th: - Font Size

image

@Ghabry
Copy link
Member Author

Ghabry commented Dec 8, 2022

About Play SE:

DDDD CCCC BBBB AAAA

0010 0010 0010 0000  (8736)
0010 0001 0000 0000  (8448)
0000 0010 0001 0000  (528)

In other commands 0 is "direct", "1" is "variable", "2" is "variable indirect". Making it possible to conclude which block is which.

So this obviously means:

  • BBBB is the 1st arg
  • CCCC is the 2nd arg
  • DDDD is the 3rd arg

AAAA is unused or a string variable. The order makes sense because this matches with the TPC parameter order.

(Guess the arg order is volume, pitch, pan???)

Will likely yield to the same conclusion for BGM.

@jetrotal
Copy link
Contributor

jetrotal commented Dec 9, 2022

the .opt args are, for SE:
opt(a, b, c) Volume, Tempo, Balance

for BGM:
.opt(a, b, c, d) Fade-in time, volume, tempo, balance
There is now an info buttton on the CSA page, to help identifying those params

image

@jetrotal
Copy link
Contributor

jetrotal commented May 6, 2023

Updates to Display Text Settings & Show Choices

Ok, this one seems important to give us a leap on creating Custom Menu Systems.
Some time ago, I saw this on a discord Channel:

image

The code to create something like this in TPC is basically:

@msg.opt {
    .opaq
    .middle
    .size 100, 150
    .font "", 0
}
@msg.choice {
    .case "A" {
        
    }
    .case "B" {
        
    }
    .case "C" {
        
    }
    .case "D" {
        
    }
    .case "E" {
        
    }
    .case "F" {
        
    }
    .case "G" {
        
    }
    .cancel 1
}

Currently easyRPG does not support those custom features inside Display Text Settings:
https://jetrotal.github.io/CSA/#Display%20Text%20Options
code looks similar to what is already done with ShowStringPics
(maybe the text box could share the same exact code from string pics + extra functionality for slowly displaying text and confirm button to next text)

Neither more options for Show Choices:
https://jetrotal.github.io/CSA/#Show%20Choices


I'd like to also suggest a few new features for each command:

In Display Text Settings it would be cool to have the power to customize it's position,
through e.g.: .position 100, 150.

With Show Choices, would be a killer feature to be able to split it into multiple collumns,
I also wonder if could be possible to give to the player a scrollbar whenever the options are longer that the textbox itself.


Combining all that with the BMP Font we already have, could help us creating powerful interactive menus, faster and simpler than How is done currently in Maniacs.

@Mimigris
Copy link

Mimigris commented Sep 26, 2023

Just some bugs present with the patch that I'm listing here for documentation:

  • For some reason, the command Conditional Branch: Right after load is considered as false when loading a save from the load menu in Maniac and not from the Load command of the patch: this bug is (currently?) not replicated in the Player since this is clearly not intended.
  • The command Get Save Info, unlike the commands Load and Save from the same patch, allows to get info from a save with a negative or null value (requesting info of Save00.lsd (0), Save-7.lsd (-7) or Save-754.lsd (-754) will work if a save is present for said file for example, while the commands Load and Save wouldn't work in said case). Concerning the formatting of how the saves are named in this range, the save 0 is called Save00.lsd, and for any negative value the save will be called Save-X.lsd (even for values between -9 and -1, while between 1 and 9 it would be Save0X.lsd). This is not replicated in the Player currently, since in any case you cannot use the Save or Load command on saves with a value below 1.

@Ghabry
Copy link
Member Author

Ghabry commented Oct 22, 2023

With #3051 there is support for String Variables added.

Implementation differences:

  1. Bing Shan must have reimplemented the Message Box command parser. It behaves now completely different to vanilla:
  • An unprocessed/unknown command, is now rendered as \a[1] instead of [1].
  • The way how e.g. speed \s[1] is escaped/rendered changed

This shouldn't be a huge problem because unprocessed command codes are likely a game bug.

  1. We use UTF-8 as the internal encoding instead of the local codepage (e.g. shift-jis or 1252), this changes how the string length is calculated. In general the string length in a bytes is pretty worthless so I don't think this is a big deal.

  2. We use the C++ regex library. It could be that the implementation differs. This wasn't tested alot yet.

  3. Reading of binary files behaves different but this shouldn't be a problem as you are not supposed to access binary files with the text API.

@CherryDT
Copy link

Hm, as many issues in this repo showed, what is "not supposed to" be done is often used in creative ways, especially in Japanese games......

@EasyRPG EasyRPG deleted a comment from XPMilkChan Oct 22, 2023
@ToolMan2k
Copy link
Contributor

I was playing around with pictures, especially the ".scale2" argument. It allows to stretch a picture using width and height parameters. Using TPC, it looks like this:

pic[10].show {
    "TestStone"
    .pos v[1], v[2] .center
    .chromakey 1
    .scale2 v[3], v[4] // width, height
    .trans 0
    .rgbs 100, 100, 100, 100
    .mapLayer 7
    .eraseWhenTransfer
    .affectedByFlash
    .affectedByShake
}

I made a test project for testing interactions between various settings.
Do whatever you want with it. :)

PictureTest.zip

Here's my observations:

  • Normal, Static picture: Works as intended

  • Effects: (wave, rotation, angle): Width works as regular magnify, Height is ignored

  • Blending: Works as intended

  • Flip (H/V): Works as intended

  • Spritesheet, Static, Normal: Works as intended

  • SpriteSheet, Static, Effects: Works the same as Normal, Effects picture

  • Spritesheet, Loop, Normal: (this gets interesting)

    • If Width AND Height == 100: works as intended
    • If Width AND Height != 100: works as intended
    • If Width OR Height == 100: picture isn't properly cropped based on the current frame coordinates and size.
      For example, on a 8x8 sprite sheet with 32x32 resolution
      • If Width is set to 100: sprite is stretched horizontally based on its current x frame coordinate (32 * x)
      • If Height is set to 100: sprite is stretched vertically based on its current y frame coordinate (32 * y)
  • SpriteSheet, Loop, Effects: works the same as Normal, Effects picture

  • Display Position (from topLeft to bottomRight):
    Sprite is placed at the correct position as if Magnify was set to 100%
    However, from its current position, it scales from the center (as shown by the cursor)
    I tried setting both Width and Height to 200%, but the same thing happens.

So here's the kicker:

  • Scaling works as intended when not using Wave/Rotation effects
  • When using these effects, Width is used as the Magnify value
  • When using a looping SpriteSheet, if only one of Width or Height is different from 100, it gets funky.
  • Picture doesn't align properly when using Display Position

Bonus: I also managed to crash EasyRPG when moving a picture with Display Position set.

@ToolMan2k
Copy link
Contributor

I managed to implement separate Width/Height values for drawing pictures, it can even Save and Load as required!
#3180

There's still work to do, for instance saves from ManiacPatch and EasyRPG aren't Byte-identical, when saving/loading between versions, it skips a few unknown chunks:

Skipped Chunk 86 (1 byte) in lcf at 1F9 (SaveSystem)
01 
Skipped Chunk 87 (1 byte) in lcf at 1FD (SaveSystem)
04 
Reading Primitive of incorrect size 4 (expected 1) at 29D
SavePicture: Corrupted Chunk 0x06 (size: 4, pos: 0x29d): fixed_to_map : Read 7 bytes! Reseting...
Reading Primitive of incorrect size 3 (expected 1) at 2AD
SavePicture: Corrupted Chunk 0x09 (size: 3, pos: 0x2ad): use_transparent_color : Read 5 bytes! Reseting...
Skipped Chunk 0A (8 byte) in lcf at 2B2 (SavePicture)
00 00 00 00 00 60 73 40 
Skipped Chunk 24 (2 byte) in lcf at 2D0 (SavePicture)
82 36 
Skipped Chunk 0A (8 byte) in lcf at 345 (SavePicture)
00 00 00 00 00 60 73 40 
Skipped Chunk 1E (2 byte) in lcf at 362 (SavePicture)
82 20 
Skipped Chunk 24 (2 byte) in lcf at 37E (SavePicture)
82 36 
Debug: Loading Save Save01.lsd
Skipped Chunk 86 (1 byte) in lcf at 1F9 (SaveSystem)
01 
Skipped Chunk 87 (1 byte) in lcf at 1FD (SaveSystem)
04 
Reading Primitive of incorrect size 4 (expected 1) at 29D
SavePicture: Corrupted Chunk 0x06 (size: 4, pos: 0x29d): fixed_to_map : Read 7 bytes! Reseting...
Reading Primitive of incorrect size 3 (expected 1) at 2AD
SavePicture: Corrupted Chunk 0x09 (size: 3, pos: 0x2ad): use_transparent_color : Read 5 bytes! Reseting...
Skipped Chunk 0A (8 byte) in lcf at 2B2 (SavePicture)
00 00 00 00 00 60 73 40 
Skipped Chunk 24 (2 byte) in lcf at 2D0 (SavePicture)
82 36 
Skipped Chunk 0A (8 byte) in lcf at 345 (SavePicture)
00 00 00 00 00 60 73 40 
Skipped Chunk 1E (2 byte) in lcf at 362 (SavePicture)
82 20 
Skipped Chunk 24 (2 byte) in lcf at 37E (SavePicture)
82 36 
Debug: Savegame version 0 (RPG_RT or EasyRPG Player Pre-0.6.0)
Debug: Loaded Map Map0001.lmu
Debug: Tree: MAP0001

For everything else, it works as expected.

@ToolMan2k
Copy link
Contributor

ToolMan2k commented Jan 7, 2024

Just discovered that using the command ShowPicture with a StringVar crashes the game by showing picture ID -1.
Looking into the code, seems that parameter 17 is set like this:

     256        0
000000xx 000000xx
       ^        ^
       |        |
StringVar    PicID

EasyRPG does a ValueOrVariable check on this value, however it does not check if it's equal or higher than 256 (bitfield that Maniac Uses).

I've made this quick change in game_interpreter.cpp, for CommandShowPicture:

if (com.parameters[17 >= 256 && Player::IsPatchManiac()]) {
    pic_id = ValueOrVariableBitfield(com.parameters[17], 0, pic_id);
    params.name = ToString(CommandStringOrVariableBitfield(com, 17, 2, 30));
} else {
    pic_id = ValueOrVariable(com.parameters[17], pic_id);
}

Now it just works!

Edit: Pull request right there #3182

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants