Jump to content

[SA Cleo] How do I detect that a char is fighting ?


vladvo

Recommended Posts

What is the best way to find that a char (not the $player_char) is fighting someone ? I mean fighting in any possible way - unarmed or using any weapon.

Basically - just get the plain fact that he wants to kill someone.

Searched for something like that in CPed, but failed to find anything useful.

Found some options but don't quite like them:

1. Event responses. 

074F:   actor 10@ ped_event == 31
074F:   actor 10@ ped_event == 9

2. 02E0:   actor 10@ firing_weapon

It is only for shooting, as far as I understand. I need unarmed and melee checks as well.

3. Check if char is playing specified anim. Could work but that's too many checks.

Link to comment
Share on other sites

const
    PEDSTATE_ATTACK = 16
    PEDSTATE_FIGHT = 17
end

0A96: 0@ = ped $PLAYER_ACTOR struct //  CPed *ped;
0@ += 0x530   //  m_nPedState   from the CPed class
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0  //  DWORD var0 = ped->m_nPedState;
//  if (ped->m_nPedState == PEDSTATE_ATTACK || ped->m_nPedState == PEDSTATE_FIGHT) {
if or
    0@ == PEDSTATE_ATTACK
    0@ == PEDSTATE_FIGHT
then
    //  
end //  }

All ped states are documented here.
Also read about CPed class structures.

Edited by Jack
  • Like 2
Link to comment
Share on other sites

I'm wondering if this new Cleo+ opcode might help. Never tried it.

0E44: get_char_kill_target_char 1@ store_to 2@

The idea being that if 2@ == 0 then they aren't attacking anyone, maybe.

Link to comment
Share on other sites

1 hour ago, OrionSR said:

I'm wondering if this new Cleo+ opcode might help. Never tried it.

I think this code does something like this (except the marker):
 

0A96: 25@ = ped 10@ struct //ped struct
25@ += 0x764 //attacker offset               
0A8D: 25@ = read_memory 25@ size 4 virtual_protect 0 //struct of attacker 
0AEA: 1@ = ped_struct 25@ handle //attacker handle
0187: 25@ = create_marker_above_actor 1@

Might be helpful, but it my case I just want to handle a situation, when a char is hit unintentionally. 
Like: 

1. Char standing, doing nothing. Well, actually waiting for his own scripted events.
2. A fight breaks up near him and he is hit.
3. He joins the fight and the script goes into "wait_for_fight_to_end_loop", so that fighting and char's script won't mix up. Looks stupid when they do. I tried.
I don't have any interest in how this unintentional fight will end. So, I don't need to handle all participants. Only the char I created. If he survives - just jump back to his script. If not - well, nobody cares. The script will find another one. 
The code I posted above brings complexity to the script. This char already has a few scripted fights and I want the game code to handle the rest.
Oh, and one of the problems is when char is fighting someone already and he is hit by a third char. It will bring even more confusion. The script will be changing the handles in 1@ and I think at some point it will crash.
What will I get from knowing that he's in a fight ? Example: a char is in a scripted fight with a cop. Someone else is hit and joins in. The "wait_for_fight_to_end_loop" checks if the char or the cop is killed (if defined -> if is_dead -> remove) and only then it checks if the char is fighting. He kills the cop and stays in this loop until situation with the third char will be resolved in any way. If the char will be killed by the third guy - the loop will still handle it. It doesn't matter who kills the char.  So I handle what I scripted and the game will handle all the random stuff.

Edited by vladvo
Link to comment
Share on other sites

9 hours ago, vladvo said:

I think this code does something like this (except the marker):
 

0A96: 25@ = ped 10@ struct //ped struct
25@ += 0x764 //attacker offset               
0A8D: 25@ = read_memory 25@ size 4 virtual_protect 0 //struct of attacker 
0AEA: 1@ = ped_struct 25@ handle //attacker handle
0187: 25@ = create_marker_above_actor 1@

0x764 refers to "m_pLastEntityDamaged" which means that a character (in this case) could be damaged by any weapon - not just a fist. 
You might consider adding this cond. check as well:

if 031D:   actor 25@ hit_by_weapon 0 // {}

where 0 is a fist.

 

9 hours ago, vladvo said:

So, I don't need to handle all participants. Only the char I created. If he survives - just jump back to his script. If not - well, nobody cares. The script will find another one. 

Use this method maybe:

const
    RANDOM_CHAR = 1
    MISSION_CHAR = 2 
end

if 056D:   actor 25@ defined
then   
    0AB1: cleo_call @isCharCreatedByAnyScript numPar 1 params 25@ store_to 0@
    if 0@ == MISSION_CHAR
    then
        //  script actor
        if 0118:   actor 25@ dead
        then
            01C2: remove_references_to_actor 25@    //  25@ variable is now free
        end
    else
        //  game actor 
    end
end

:isCharCreatedByAnyScript
0A96: 1@ = ped 0@ struct //  CPed *ped;
1@ += 0x484 //  m_nCreatedBy 
0A8D: 1@ = read_memory 1@ size 1 virtual_protect 0  //  ped->m_nCreatedBy;    
0AB2: cleo_return 1 1@

 You might consider creating your own array to store all of your characters in it. 

Edited by Jack
Link to comment
Share on other sites

@Jack

Your PedState script doesn't work for some reason. I tried to run it as a separate script - it shows "1" no matter what player_actor is doing.
 

{$CLEO .cs}
0000: NOP
:START
wait  0
if
0256: player $PLAYER_CHAR defined
jf @START

:main
wait 0
0A96: 0@ = ped $PLAYER_ACTOR struct //  CPed *ped;
0@ += 0x530   //  m_nPedState   from the CPed class
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0  //  DWORD var0 = ped->m_nPedState;
0AD1: show_formatted_text_highpriority "State %d" time 10 0@  
jump @main

 

Edited by vladvo
Link to comment
Share on other sites

In deed it doesn't work and I don't know why. Only a few ped states are functional even for an NPC.

 

Alternative:
cleo +

0E47: is_char_fighting $PLAYER_ACTOR    //  I've never tried it before.

Tested (scm with a c++ description):  

const
    _ZNK16CPedIntelligence15GetTaskFightingEv = 0x600F30
end
   
0A96: 0@ = ped $PLAYER_ACTOR struct //  CPed *playa;
0@ += 0x47C //  m_pIntelligence [CPed class]
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0  //  CPedIntelegence *playaIntel = playa->m_pIntelligence;
//  CTaskSimpleFight *playaFightTask = playa->m_pIntelligence->GetTaskFighting();
//  if (playaFightTask) { //    fight } else {  //  none}
0AA8: call_method_return _ZNK16CPedIntelligence15GetTaskFightingEv struct 0@ num_params 0 pop 0 store_to 1@
if 1@ <> 0
then
    //  playa is fighting right now.
else
    //  playa is not fighting.
end

 

Link to comment
Share on other sites

Thanks, @Jack. This one seem to work. I think I start to understand how this stuff works.

One more question - in your code _ZNK16CPedIntelligence15GetTaskFightingEv has address 0x600F30

I found a file somewhere and the same _ZNK16CPedIntelligence15GetTaskFightingEv function has address 0001:001FFF30. This 0x01FFF30 exectedly does not work. As well as 0x01FFF70 _ZNK16CPedIntelligence13GetTaskUseGunEv. How do I find the correct address for any listed function ? There are quite a lot of them. ) 
Damn, I need to learn C++.

Update. Found the address. IDA + .idb file.
Update 2. Was browsing through SASCM.INI. Guess what I found there:
0E46=1,is_char_using_gun %1d%
0E47=1,is_char_fighting %1d%
 

Edited by vladvo
Link to comment
Share on other sites

Well done. And here's a more complete answer (direct approach):
scm:

const
    TASK_SIMPLE_FIGHT = 1016
end

while true
    wait 0
    0AB1: cleo_call @getCharTask params 1 param $PLAYER_ACTOR store_to 0@
    if 0@ == TASK_SIMPLE_FIGHT
    then
        //  playa is fighting
    else
        //  playa is not fighting
    end
end
    
:getCharTask
0A96: 1@ = ped 0@ struct
1@ += 0x47C
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0
1@ += 0x18
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0
if 1@ <> 0
then
    1@ += 0
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0
    1@ += 0x10   
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  
    0AA7: call_function_return 1@ num_params 0 pop 0 1@
end
0AB2: cleo_return 1 1@

c++:

while (true) {
    Sleep(0);
    CPed *playa = FindPlayerPed();
    if (playa) {
        if (getCharTask(playa) == TASK_SIMPLE_FIGHT) {
            //  playa is fighting
        } else {
            //  playa is not fighting
        }
    }
}

int getCharTask(CPed *playa) {
    if (playa->m_pIntelligence->m_TaskMgr.m_aSecondaryTasks[0]) {
        return playa->m_pIntelligence->m_TaskMgr.m_aSecondaryTasks[0]->GetId();
    } else {
        return 0;
    }
}

This way there's no need to look for the functions at all - the code finds them by itself.
And as you can see - scm is a terrible thing to work with comparing with the mighty c++.

  • Like 1
Link to comment
Share on other sites

10 hours ago, Jack said:

And here's a more complete answer (direct approach):

Unfortunately, it does not work. Always returns 0. And digging deeper into the matter just rises even more questions.
Just for clarification.
Testing conditions: I have a manually created char that is doing some stuff defined in AS pack, like walking around and idling. No scripted responses to assault. I hit him and he starts to fight me (either armed or unarmed - depends on what he was given on creation).
As far as I understand - the game code handles him being hit. I tried inserting this code to get the task he's doing when attacking me.

2@ = 0
for  2@ = 0 to 4000 step 1
062E: get_actor 10@ task 2@ status_store_to 3@ // ret 7 if not found
//0AD1: show_formatted_text_highpriority "task: %d status: %d" time 500 2@ 3@
if or
3@ < 7
3@ > 7
then 
0AD1: show_formatted_text_highpriority "task: %d status: %d" time 1000 2@ 3@
wait 1000
end

It showed task 1560 and status was 1, sometimes it changed to 2. And a few times I saw task 1506. 
The strange thing is that my script has 05DE: AS_actor -1 walk_around_ped_path and 0605: actor -1 perform_animation in AS. It gives the char command to perform one of the action sequences and starts the task check. Even when actor was walking the task was 1560. 
Ok. I tried removing everything. Just spawn the char, so he was just standing there, doing nothing. No tasks appeared. Hit him a few times and he started to fight back. I figured that task 1506 is for unarmed or knife. And 2066 is for push. Like when he's pushing me away from himself. That's it. When he fires a pistol or an Uzi - no tasks appear. One more thing - the 1506 task appears only after he hit me once. In case when he's just swinging the knife in the air - no tasks appear. Wtf, really ? )))
 

@ZAZ Maybe you can clarify something on this whole topic ?

Edited by vladvo
Link to comment
Share on other sites

22 minutes ago, vladvo said:

Unfortunately, it does not work. Always returns 0. 

It does work. Always returns 1016 (TASK_SIMPLE_FIGHT) when the actor is fighting or preparing to fight. You're probably using it wrong.

Link to comment
Share on other sites

2 hours ago, Jack said:

It does work.

Yeah, sorry about that. Must have misplaced something. Everything is working now.
Added "if or" and "0 == 1017" so that it would detect gun fight. Looks like 1016 is unarmed/melee only.

Still, I am kinda interested, why the code I posted (the one with FOR-TO and 062E) in previous message don't work ?

Oh, and found this 0x0681720 _ZNK12CTaskManager13GetActiveTaskEv
Is there a way to put it to good use ?

Edited by vladvo
Link to comment
Share on other sites

13 hours ago, vladvo said:

Oh, and found this 0x0681720 _ZNK12CTaskManager13GetActiveTaskEv
Is there a way to put it to good use ?

That function uses a diferent parameter (CTaskManager *) so we need to get it first then call the function:

const
    _ZNK12CTaskManager13GetActiveTaskEv = 0x681720
end

0AB1: cleo_call @getCharActiveTask params 1 param $PLAYER_ACTOR store_to 0@
0AD1: show_formatted_text_highpriority "State %d" time 10 0@
    
:getCharActiveTask
0A96: 1@ = ped 0@ struct
1@ += 0x47C
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CPedIntelligence *pedIntel = playa->m_pIntelligence;
1@ += 4 //  CTaskManager * taskManager = pedIntel->m_TaskMgr;
//  ped->m_pIntelligence->m_TaskMgr.GetActiveTask();
0AA8: call_method_return _ZNK12CTaskManager13GetActiveTaskEv struct 1@ num_params 0 pop 0 store_to 1@ 
if 1@ <> 0
then
    1@ += 0
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CTaskVtable
    1@ += 0x10  //  GetID();  
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CTaskVtable->GetID();
    //  ped->m_pIntelligence->m_TaskMgr.GetActiveTask()->GetID(); 
    0AA8: call_method_return 1@ struct 1@ num_params 0 pop 0 store_to 1@ 
end
0AB2: cleo_return 1 1@

And there's another usefull:

const
    _ZNK12CTaskManager21GetSimplestActiveTaskEv = 0x6819D0
end

0AB1: cleo_call @getCharSimpleActiveTask params 1 param $PLAYER_ACTOR store_to 0@
0AD1: show_formatted_text_highpriority "State %d" time 10 0@
    
:getCharSimpleActiveTask
0A96: 1@ = ped 0@ struct
1@ += 0x47C
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CPedIntelligence *pedIntel = playa->m_pIntelligence;
1@ += 4 //  CTaskManager * taskManager = pedIntel->m_TaskMgr;  
//  ped->m_pIntelligence->m_TaskMgr.GetSimplestActiveTask();
0AA8: call_method_return _ZNK12CTaskManager21GetSimplestActiveTaskEv struct 1@ num_params 0 pop 0 store_to 1@ 
if 1@ <> 0
then
    1@ += 0
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CTaskVtable
    1@ += 0x10  //  GetID();  
    0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0  //  CTaskVtable->GetID();
    //  ped->m_pIntelligence->m_TaskMgr.GetSimplestActiveTask()->GetID();
    0AA8: call_method_return 1@ struct 1@ num_params 0 pop 0 store_to 1@ 
end
0AB2: cleo_return 1 1@

They return all kinds of tasks (especially "_ZNK12CTaskManager21GetSimplestActiveTaskEv" - more detail one). However not the fight tasks. 
I couldn't get them through the structures like I did in the previous code which is a better way.

Oh and uhm use the list from here.

 

EDIT:

You could also include all 3 ones to check for a task of your choosage (not tested):

const
    TASK_SIMPLE_CAR_DRIVE = 709 //  example task
end
0AB1: cleo_call @getCharTask params 1 param $PLAYER_ACTOR store_to 2@
0AB1: cleo_call @getCharActiveTask params 1 param $PLAYER_ACTOR store_to 3@
0AB1: cleo_call @getCharSimpleActiveTask params 1 param $PLAYER_ACTOR store_to 4@
if or
    2@ == TASK_SIMPLE_CAR_DRIVE
    3@ == TASK_SIMPLE_CAR_DRIVE
    4@ == TASK_SIMPLE_CAR_DRIVE
then   
    //  the actor is driving a vehicle
end

I think at least one of them will return a task you input.
And careful with the 0@ and 1@ since they're used as the function parameters. You can use them for a single function check but not like this.

Edited by Jack
Link to comment
Share on other sites

2 hours ago, Jack said:

However not the fight tasks.

Thanks a lot ! Much appreciated. This GetSimplestActiveTaskEv is great. Helps a lot to find out what the char is doing.
However, it showed me both fight tasks but this time they are a bit different:

TASK_SIMPLE_FIGHT_CTRL1019

TASK_SIMPLE_GUN_CTRL1020

 

Upd. It's not just great. It's incredible. Looks like it is a solution to everything. Found

TASK_SIMPLE_BE_HIT 1008
TASK_SIMPLE_BE_HIT_WHILE_MOVING 1009
and even 35584 which I think is similar to 0457: player $PLAYER_CHAR aiming_at_actor 10@ but is better because it works only when $PLAYER_ACTOR is in front of the char. 
So no more need for code like this:

if and
09ED: actor 10@ is_within_field_of_view_actor $PLAYER_ACTOR
0457: player $PLAYER_CHAR aiming_at_actor 10@
then
05E2: AS_actor 10@ kill_actor $PLAYER_ACTOR
jump @FIGHT_LOOP
end

 

Edited by vladvo
Addition
Link to comment
Share on other sites

@Jack @OrionSR

Maybe this is a bit noobish info but I thought you might be interested. The code I posted in the 4th message - this one:
 

0A96: 25@ = ped 10@ struct //ped struct
25@ += 0x764 //attacker offset               
0A8D: 25@ = read_memory 25@ size 4 virtual_protect 0 //struct of attacker 
0AEA: 1@ = ped_struct 25@ handle //attacker handle

is quite useful for me, so that the char 10@ will switch target if he's hit by another char.
Turned out it was leading to a crash if you hit him with a car. SCRLog was showing this:
 

00004183&1: [0A96] GET_CHAR_POINTER 2051 -> 0xE4F6598
00004191&1: [000A] l1(240084376) += 1892
00004199&1: [0A8D] READ_MEMORY 0xE4F6CFC 4 0 -> 240359720
00004211&1: [00D6] IF 0
00004215&0: [0019] l1(240359720) > 0    // TRUE
00004222&1: [004D] GOTO_IF_FALSE -4237
00004229&1: [0AEA] GET_PED_REF 0xE539928

CPed +0x764 has various descriptions over the net. Pointer to the ped that killed you, pointer to the ped that killed char or whatever.
As far as I understand - it's not the ped, but the ID of any entity that decreased char's health.
Solution is simple - get entity type with 0E13 (CLEO+) and handle the rest according to its type.
Entity.GetType(entity: any, var type: EntityType)
0E13: get_entity_type entity [any] store_to type [var EntityType]

   Other
   Building
   Object
   Ped
   Vehicle
0AB1: cleo_call @GetAttacker 1 10@ 26@ 27@

:GetAttacker
0A96: 1@ = ped 0@ struct //ped struct
1@ += 0x764 //attacker offset               
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 //struct of attacker
if 1@ > 0
    then
        0E13: get_entity 1@ type_to 2@
        if 2@ == 3 //ped
        then
        0AEA: 1@ = ped_struct 1@ handle //attacker handle
        end
        if 2@ == 4 //vehicle
        then
        0AEB: 1@ = vehicle_struct 1@ handle
    end
end
0AB2: cleo_return 2 1@ 2@


Another thing I discovered is a CLEO+ opcode:
0EFF: get_char_simplest_active_task 1@ id_to 2@ pointer_to 3@
1@ is the char in question

2@ is task ID

3@ is a pointer (not sure to what - the char or the task)

Oh, and a question popped up. How do I make a ped kill another ped that is driving a car ?
05E2: task_kill_char_on_foot killer [Char] target [Char] doesn't work it that case.

Edited by vladvo
Question added
Link to comment
Share on other sites

Yes - it's always a good thing to check if the entity struct exists before using it.

For the car attack you can use "TASK_DESTROY_CAR" defined as a scm command:

Spoiler
if and
    056D:   actor 0@ defined
    056D:   actor 1@ defined
then
    03C0: 2@ = actor 0@ car
    if 056E:   car 2@ defined
    then
        if 00DB:   actor 0@ in_car 2@
        then
            0672: AS_actor 1@ attack_car 2@
        else
            05E2: AS_actor 0@ kill_actor 1@
        end
    else
        05E2: AS_actor 0@ kill_actor 1@
    end
end

The command 0672 (TASK_DESTROY_CAR) uses "CTaskComplexDestroyCar__ctor" so it's most likely the one you're looking for.
Other then that we also have these:

Spoiler
__thiscall CTaskComplexCarDriveMissionKillPed::CTaskComplexCarDriveMissionKillPed(CVehicle *veh, CEntity *ped);
CPed::KillPedWithCar(CVehicle *veh, float, bool);
CTaskSimpleKillPedWithCar::DamageCarBonnet(CPed const* ped);
CTaskComplexKillCriminal__ctor(CPed *ped, 0);

The last one seems interesting since cops use it and they do shoot at player while he's inside a vehicle also.

And you can use the opcode list and descriptions from here. Click "view" to see the opcode description.

 

Edited by Jack
Link to comment
Share on other sites

5 hours ago, Jack said:

For the car attack you can use "TASK_DESTROY_CAR" defined as a scm command:

As an option:

:FIGHT_CAR_LOOP
046C: 24@ = car 26@ driver
if
00DF: actor 24@ driving
then
00AA: store_car 26@ position_to 3@ 4@ 5@
0668: AS_actor 10@ rotate_and_shoot_at 3@ 4@ 5@ -1 ms
jump @FIGHT_CAR_LOOP
end

Works nice on bikes. Haven't tested it on car yet. This way the char goes for the char, not the bike.

Link to comment
Share on other sites

You should check if the car is empty to avoid crash:

while true
    wait 0
    046C: 24@ = car 26@ driver
    if 056D:   actor 24@ defined
    then
        if 00DF: actor 24@ driving
        then
            00AA: store_car 26@ position_to 3@ 4@ 5@
            0668: AS_actor 10@ rotate_and_shoot_at 3@ 4@ 5@ -1 ms
        end
    end
end

... since the opcode 046C returns 0 if there's no driver.
Unless of course you already did the check somewhere before this code snippet. 

And high level code doesn't require the usage of labels. So it would be good to choose between high and low. So, you could use the one which you understand better.

Edited by Jack
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.