JuniorDjjr / CLEOPlus

http://www.mixmods.com.br/2020/03/CLEOPlus.html
MIT License
38 stars 4 forks source link

0E84: Get_Weaponinfo Never Fails to Find a Pointer #14

Open OrionSR opened 1 year ago

OrionSR commented 1 year ago

In an effort to understand and document the use of CLEO+ opcodes for the Sanny Builder Library, I tried and failed to make the get_weaponinfo conditional command return False. It always returns a pointer, even if it points to bogus data.

In particular, weapon skills other than 1 point to the wrong data if the weapon ID is not within the range of skilled weapons - pistol (22) thru tec9 (32).

Similarly, out of range weapon IDs will return invalid pointers as True.

Seemann offered this script at a template for managing get_weaponinfo as a conditional command with standard limits. I'm not sure how modified weapon.dat files or limit adjusters might change these limits.

{$CLEO .cs}
{$USE CLEO+}
0000:
const WEAPON_ID     = 22  //(PISTOL)
const SKILL_LEVEL   = 2 //(PRO)
int weap_info

if
    cleo_call @getweapinfo 2 WEAPON_ID SKILL_LEVEL weap_info
then
    weap_info+=0x20
    int ammo
    read_memory ammo = weap_info 2 false

    print_formatted_now "Weapon: %d~n~Skill: %d~n~Ammo: %d" time 3000 WEAPON_ID SKILL_LEVEL ammo
else
    print_formatted_now "Can't get weapon_info for Weapon: %d~n~Skill: %d"  time 3000 WEAPON_ID SKILL_LEVEL
end
terminate_this_custom_script

:getweapinfo
const weaponId = 0@
const skill = 1@
const ptr = 2@

if and
    weaponId >= 0 
    weaponId <= 46
    skill >= 0
    skill <= 3
then
    if skill == 1
    then
        get_weaponinfo weaponId 1 ptr
        cleo_return 1 ptr
    end

    if and
        weaponId >= 22 
        weaponId <= 32
    then

        if skill == 0
        then weaponId += 25
        end

        if skill == 2
        then weaponId += 36
        end

        if skill == 3
        then weaponId += 47
        end

        get_weaponinfo weaponId 1 ptr
        cleo_return 1 ptr
    end
end
cleo_return 0
JuniorDjjr commented 1 year ago

But this script isn't adapted to added weapons without replacing. Any idea how to make this really modular? The game itself does this limitation somewhere?

OrionSR commented 1 year ago

Not really. Added weapons without replacing is outside of my experience. As it was, it took me a while to come up to speed on a standard WeaponInfo struct enough to explain why I thought my results were... weird. However, Seemann posted this bit that suggests some of these limits are baked into the game code. Would added weapons change these offsets?

CWeaponInfo *__cdecl CWeaponInfo::GetWeaponInfo(eWeaponType weaponID, char skill)
{
  signed __int16 v2; // ax

  v2 = 47;
  switch ( skill )
  {
    case 0:
      return &aWeaponInfo[(weaponID + 25)];
    case 1:
      return &aWeaponInfo[weaponID];
    case 2:
      return &aWeaponInfo[(weaponID + 36)]; <---------------------- WEAPONID + 36
    case 3:
      v2 = weaponID + 47;
      break;
  }
  return &aWeaponInfo[v2]; <----------- default case when skill is not 0..3 (v2=47)
}

My other thought, although not exactly proper, would be to return False data when limits are breached.

I don't have any strong opinions about this, or even a use in mind. My goal is documentation, but I feel obliged to report bugs, or just quirks of the commands when I find them. Thanks for your continued support.

JuniorDjjr commented 1 year ago

Checking if result pointer is outside weaponinfo array isn't safe and will basically not help at all.

Since we are simply calling CWeaponInfo::GetWeaponInfo, nothing special on CLEO+ side, the programmer itself need to make sure that the skill value is correct (for example, value previously stored from GetWeaponSkill).

I'll simply limit original weapon types (22-32) because I see no reason not to do this, but it's the programmer's job to use a correct skill ID. I don't see logical cases in which a programmer would invent skill IDs from inside his head, this GET_WEAPONINFO is just a more low level version of GET_CURRENT_CHAR_WEAPONINFO, useful if you need weapon info from previously stored weapon from the char.

But when the skill ID is not known, it is recommended to use "1". In the new version of CLEO+ I will preserve return boolean, even if it is not really safe.

OrionSR commented 1 year ago

Alright then. I think the best option is to add a more detailed description of this opcode to SBL, documenting the quirks of misuse and the coders responsibility. I'm a little confused about the limit you intend to add but I'll wait for an update and rerun the tests anyway.

Thanks again for the support. I'm bound to have, not necessarily issues, but questions at least, regarding the documentation of some of the more complex CLEO+ opcodes. Are you available for consultation?

JuniorDjjr commented 1 year ago

I'm too busy but you can contact me (or even get answers from other people that also knows a bit how to use CLEO+) on #códigos MixMods Discord (the portuguese one, because it's more active, no problem talking in english there).

I started documenting CLEO+ in the Sanny Builder Library before, but while documenting I started to feel Covid symptoms and I felt very sick (weakness, at first I thought I was trying too hard and the page changes made me dizzy etc but no), so I soon stopped for weeks, and I ended up never going back to document it, even though I'm too busy developing my game and such.

But I took the time to do something that I really needed for CLEO+, which are a few dozen more commands that I missed, including a new system that makes it possible to create "render objects" (for peds or other objects) using .dff and .txd without the need to add model IDs . Everything has already been tested a lot, I'm using on Urbanize already and I should release it soon.

So soon you'll have a few dozen more commands to document (and of course, I'll make a summary in the post on the MixMods Forum like I did with the other commands).

OrionSR commented 1 year ago

I'll make a summary in the post on the MixMods Forum

Thanks for this, I found the link to How to create scripts with CLEO+. This will help a lot.

The critical information that I'm often missing is what type of data is being passed. In particular, it is a handle or a pointer? Tracking down enums can sometimes be a time consuming process. Anything you can do to clarify the data types would be appreciated.

I'll follow up on Discord when I run run into trouble. Thanks again.

JuniorDjjr commented 1 year ago

I'll make a summary in the post on the MixMods Forum

Thanks for this, I found the link to How to create scripts with CLEO+. This will help a lot.

The critical information that I'm often missing is what type of data is being passed. In particular, it is a handle or a pointer? Tracking down enums can sometimes be a time consuming process. Anything you can do to clarify the data types would be appreciated.

I'll follow up on Discord when I run run into trouble. Thanks again.

But on gta3script is already separated handle from pointer (in this case, it's called Entity, for example Entity="CHAR" means a handle of a char, if no Entity is given, it's just a pointer). But in fact everything created by CLEO+ is a pointer to a struct (so modders can easily hack it if needed, I'll always avoid changing struct format).

OrionSR commented 1 year ago

gta3script, another clue. I'll see if I can find the definitions to use as reference.

Did you catch my note on MixMods discord about is_char_really_in_air? It flags the char as in_air while driving, when is_char_in_air does not. Your description didn't mention the exception, which is all I have to judge intent. I didn't want to open another issue if it's working as intended.

JuniorDjjr commented 1 year ago

gta3script, another clue. I'll see if I can find the definitions to use as reference.

Did you catch my note on MixMods discord about is_char_really_in_air? It flags the char as in_air while driving, when is_char_in_air does not. Your description didn't mention the exception, which is all I have to judge intent. I didn't want to open another issue if it's working as intended.

I said to use the other channel, but replying here: Checking the source code, I think is fine the way it works, otherwise CLEO+ would need to check if inside car then redirect to check if the car is in air instead of char. Looks a bit intrusive when you think about the scripting logic. Under the hood, it simply checks if is hitting a solid surface (so I recommend to use this as description), so if you are on vehicle, the char is not hitting a solid surface and will return false. Also, there is IS_OBJECT_REALLY_IN_AIR too, but if the object is attached on a car, I think will return false too, and if we change the char to return true, would need to change the object to return true too etc.