Jump to content

Making NPCs speak..? [SOLVED]


HughJanus
 Share

Recommended Posts

   I offered it in the mapeditor thru the chaiscript implementation with a single line:

    Ped.PlayAmbientSpeech("FAREWELL_NO_SALE", "0078_U_M_M_BLWTRAINSTATIONWORKER_01");

But heres how you do it in c++ 

Spoiler

struct ScriptedSpeechParams
{
    const char* speechName;
    const char* voiceName;
    alignas(8) int v3;
    alignas(8) Hash speechParamHash;
    alignas(8) Entity entity;
    alignas(8) BOOL v6;
    alignas(8) int v7;
    alignas(8) int v8;
};


  void PlayAmbientSpeech(std::string speechline, std::string voice) {
       // Entity is not required..
      ScriptedSpeechParams params{ speechline.c_str(), voice.c_str(), 1, 0x67F3AB43, 0, true, 1, 1 };
      AUDIO::_PLAY_AMBIENT_SPEECH1(this->id, (Any*)&params);
  }

 

 

  • Like 2
Link to comment
Share on other sites

18 hours ago, Lambda said:

   I offered it in the mapeditor thru the chaiscript implementation with a single line:


    Ped.PlayAmbientSpeech("FAREWELL_NO_SALE", "0078_U_M_M_BLWTRAINSTATIONWORKER_01");

But heres how you do it in c++ 

  Reveal hidden contents


struct ScriptedSpeechParams
{
    const char* speechName;
    const char* voiceName;
    alignas(8) int v3;
    alignas(8) Hash speechParamHash;
    alignas(8) Entity entity;
    alignas(8) BOOL v6;
    alignas(8) int v7;
    alignas(8) int v8;
};


  void PlayAmbientSpeech(std::string speechline, std::string voice) {
       // Entity is not required..
      ScriptedSpeechParams params{ speechline.c_str(), voice.c_str(), 1, 0x67F3AB43, 0, true, 1, 1 };
      AUDIO::_PLAY_AMBIENT_SPEECH1(this->id, (Any*)&params);
  }

 

 

 

First off, thanks for responding!

Which entity plays the speech if none is passed to the function?

 

Edit: Oh, the one which is passed to the native function?

Edited by HughJanus
Link to comment
Share on other sites

Was problaby a bit unclear with that comment the ScriptedSpeechParams struct has an entity variable. That one is 0 in my example and that works just fine. In the native AUDIO::_PLAY_AMBIENT_SPEECH1 you pass the ped  you want to be the source of the sound (where i use this->id).

 

  • Like 1
Link to comment
Share on other sites

@Lambda OK, so I tried it now and my code looks as follows;

 

Helper file:

struct ScriptedSpeechParams
{
	const char* speechName;
	const char* voiceName;
	alignas(8) int v3;
	alignas(8) Hash speechParamHash;
	alignas(8) Entity entity;
	alignas(8) BOOL v6;
	alignas(8) int v7;
	alignas(8) int v8;
};

void PlayAmbientSpeech(Ped ped, std::string speechline, std::string voice) {
	ScriptedSpeechParams params{ speechline.c_str(), voice.c_str(), 1, 0x67F3AB43, 0, true, 1, 1 };
	AUDIO::x_PLAY_AMBIENT_SPEECH1(ped, (Any*)&params);
}

bool PedFearTest(Ped ped)
{	
	PlayAmbientSpeech(ped, "FAREWELL_NO_SALE", "0078_U_M_M_BLWTRAINSTATIONWORKER_01");
}

 

Adapted native function wrapper:

static BOOL _PLAY_AMBIENT_SPEECH1(Ped ped, char* speechName) { return invoke<BOOL>(0x8E04FEDD28D42462, ped, speechName); }
static BOOL x_PLAY_AMBIENT_SPEECH1(Ped ped, Any* p1) { return invoke<BOOL>(0x8E04FEDD28D42462, ped, p1); }

 

Main method (the relevant parts):

int yolo = 0;

while (true)
{
	const int ARR_SIZE = 1024;
	Ped peds[ARR_SIZE];
	int count = worldGetAllPeds(peds, ARR_SIZE);
	
	for (int i = 0; i < count; i++)
	{
		if (yolo < GAMEPLAY::GET_GAME_TIMER() && peds[i] != PLAYER::PLAYER_PED_ID())
		{
			PedFearTest(peds[i]);
			yolo = GAMEPLAY::GET_GAME_TIMER() + 7000;
		}
	}
}

 

So what it should do is make the NPCs say the speech line every 7 seconds, but unfortunately it isnt working.

Does the line only work for the ped model belonging to the voice?

I tried setting the voice to 0, but that crashes script hook.

Link to comment
Share on other sites

There are several big warning signs here. Why do you call PedFearTest to only call another function? Not to mention it's return type is bool and you return nothing.

And it should be quite obvious that you cant set the voice to be 0 when it expects a string representing a voice.

Looking a bit closer i would not recommend you going thru the entire ped pool each frame to check the timer it's a massive waste of cpu time.

 

Edited by Lambda
  • Like 1
Link to comment
Share on other sites

@Lambda I didnt have much time yesterday, so I just quickly adjusted the helper file and didnt fiddle with the main script at all (and apparently overlooked that I forgot to change the bool to void.

The main script I posted here, was not copied and pasted, but I typed the stuff in here. The timer checkings are done before the ped loop, I dont know why I typed it that way yesterday, I just wanted to demonstrate what I do with your code.

I will try to optimize the thing on the weekend and get back with the results.

Please keep in mind, that I am a noob and do not code professionally.

Thanks for responding so quickly!

 

Edit: Is there a way to make NPCs speak each with their own voice (so a generic value for the voice parameter)?

Edited by HughJanus
Link to comment
Share on other sites

12 hours ago, HughJanus said:

 

Edit: Is there a way to make NPCs speak each with their own voice (so a generic value for the voice parameter)?


Not that i know of, you can set the ambient voice name for peds so it's likely that there is an for now unkown native to get the voice from a ped.

Edited by Lambda
  • Like 1
Link to comment
Share on other sites

On 11/2/2020 at 7:41 PM, Lambda said:


Not that i know of, you can set the ambient voice name for peds so it's likely that there is an for now unkown native to get the voice from a ped.

 

OK, so I tested it (didnt have much time, though) and I think it works. I used the voice and the line you gave me and tried it in Blackwater with the 7 seconds delay.

Only very few NPCs spoke the line (I suppose those who have this voice).

Thanks for clearing things up!

Unfortunately, this non-generic version does not help me. Maybe I can find some workaround, since it seems the voices have similar names to the NPC models?

 

Edit:

@Lambda LMS wrote a few posts back, that they implemented a method in the RagePluginHook where they dont pass a voice name and the native still works (with the respective voice of the ped passed to the function). Do you know how this could work in C++?

 

Edit 2:

Soooo, I tried a few things and this seems to work

struct ScriptedSpeechParams
{
	const char* speechName;
	const Any* voiceName;
	alignas(8) int v3;
	alignas(8) Hash speechParamHash;
	alignas(8) Entity entity;
	alignas(8) BOOL v6;
	alignas(8) int v7;
	alignas(8) int v8;
};

void PedFearTest(Ped ped)
{	
	string speechline = "LAW_HAIL";
	ScriptedSpeechParams params{ speechline.c_str(), 0, 1, 0x67F3AB43, 0, true, 1, 1 };
	AUDIO::x_PLAY_AMBIENT_SPEECH1(ped, (Any*)&params);
}

 

I picked the line "LAW_HAIL" because a lot of NPCs seem to have it and also there are two versions of it (LAW_HAIL_01, LAW_HAIL_02).

What's curious is, that only very few NPCs actually play that line (although I try to trigger it for each NPC) and they seem to repeat the same line over and over, so no switching between the two versions. If I use "LAW_HAIL_01" or the other one, nothing happens.

I will try a bit more in the coming weeks and see if can get every NPC to speak. If you have more wisdom to drop on me, please do so.

Thanks for all the help until now, guys!

 

Edit 3: Had a faulty code part in my loop. Now everything works as intended (except for the thing where NPCs only speak one version of the line over and over again - so still no way to trigger LAW_HAIL_02). Thank you 🙂

Edited by HughJanus
Link to comment
Share on other sites

  • 3 weeks later...
7 hours ago, MrEuphrates said:

Hi, I know this topic is solved but is there anyway I can use this in lua. Because lua version of PLAY_AMBIENT_SPEECH1 only accepts ped as param.

 

Cant you change the native wrapper so the method is invoked with the type you specify?

Link to comment
Share on other sites

2 hours ago, HughJanus said:

 

Cant you change the native wrapper so the method is invoked with the type you specify?

Since all of my resources are in lua I would like to do this in lua too. If there is no other way I can change it to c++. If you can think of any other way to do it in lua please tell. Thanks.

Link to comment
Share on other sites

8 hours ago, MrEuphrates said:

Since all of my resources are in lua I would like to do this in lua too. If there is no other way I can change it to c++. If you can think of any other way to do it in lua please tell. Thanks.

 

I was talking about lua.

How do you call natives? You have a library of wrapper functions, I suppose?

Is this library edit-able / can you edit the wrappers?

Link to comment
Share on other sites

2 hours ago, HughJanus said:

 

I was talking about lua.

How do you call natives? You have a library of wrapper functions, I suppose?

Is this library edit-able / can you edit the wrappers?

Oh, I see... I am creating a resource in redm and I don't know if I can do that.

Link to comment
Share on other sites

51 minutes ago, MrEuphrates said:

Oh, I see... I am creating a resource in redm and I don't know if I can do that.

 

I dont know redm. What are the files that come with it? Is there a file including the native functions you are able to use?

I would suggest searching there, if there is one.

With ABs script hook there comes a file with natives, for example. This is the one I edit to make natives work with other types (like described in this thread).

Link to comment
Share on other sites

3 hours ago, HughJanus said:

 

I dont know redm. What are the files that come with it? Is there a file including the native functions you are able to use?

I would suggest searching there, if there is one.

With ABs script hook there comes a file with natives, for example. This is the one I edit to make natives work with other types (like described in this thread).

 

There is this line in Natives

local retval --[[ boolean ]], params --[[ Any ]] = PlayPedAmbientSpeechNative( ped --[[ Ped ]] )

And this as a description

struct ScriptedSpeechParams
{
    const char* speechName;
    const char* voiceName;
    alignas(8) int v3;
    alignas(8) Hash speechParamHash;
    alignas(8) Entity entity;
    alignas(8) BOOL v6;
    alignas(8) int v7;
    alignas(8) int v8;
};

static_assert(sizeof(ScriptedSpeechParams) == 0x40, "incorrect ScriptedSpeechParams size");


Example:

ScriptedSpeechParams params{"RE_PH_RHD_V3_AGGRO", "0405_U_M_M_RhdSheriff_01", 1, 0x67F3AB43, 0, true, 1, 1};
_PLAY_AMBIENT_SPEECH1(PLAYER_PED_ID(), (Any*)&params);

 

When I found the code in the description here I thought there was a way to do it in lua for RedM but I guess not. I cannot give params to this function no matter what I do. I think this function just doesn't work in RedM.

 

 

Link to comment
Share on other sites

14 hours ago, MrEuphrates said:

 

There is this line in Natives


local retval --[[ boolean ]], params --[[ Any ]] = PlayPedAmbientSpeechNative( ped --[[ Ped ]] )

And this as a description


struct ScriptedSpeechParams
{
    const char* speechName;
    const char* voiceName;
    alignas(8) int v3;
    alignas(8) Hash speechParamHash;
    alignas(8) Entity entity;
    alignas(8) BOOL v6;
    alignas(8) int v7;
    alignas(8) int v8;
};

static_assert(sizeof(ScriptedSpeechParams) == 0x40, "incorrect ScriptedSpeechParams size");


Example:

ScriptedSpeechParams params{"RE_PH_RHD_V3_AGGRO", "0405_U_M_M_RhdSheriff_01", 1, 0x67F3AB43, 0, true, 1, 1};
_PLAY_AMBIENT_SPEECH1(PLAYER_PED_ID(), (Any*)&params);

 

When I found the code in the description here I thought there was a way to do it in lua for RedM but I guess not. I cannot give params to this function no matter what I do. I think this function just doesn't work in RedM.

 

 

 

How do you use natives in lua? Could you please post an example.

For me the description doesnt match the code above, because the code above seems to describe a wrapper named "PlayPedAmbientSpeechNative" while the example uses a wrapper called "_PLAY_AMBIENT_SPEECH1".

Link to comment
Share on other sites

3 hours ago, HughJanus said:

 

How do you use natives in lua? Could you please post an example.

For me the description doesnt match the code above, because the code above seems to describe a wrapper named "PlayPedAmbientSpeechNative" while the example uses a wrapper called "_PLAY_AMBIENT_SPEECH1".

 

This function 

PlayPedAmbientSpeechNative()

Used to be called.

PlayAmbientSpeech1()

(They recently changed the name of the function.)

 

And it basically invokes the native _PLAY_AMBIENT_SPEECH1. Here is a link to the native document: https://vespura.com/doc/natives/?_0x8E04FEDD28D42462

 

The problem is even though it's in the natives document I cannot pass any parameters into it. I see that you have to give it a struct that holds the parameters in c++ but you can't do that in lua.

 

And here is a code example that spawn peds at pre-defined positions:

Citizen.CreateThread(function()
    clerkHash = GetHashKey(store.ClerkModel)
    pos = Config.Stores[1].pos
    heading = Config.Stores[1].hdg
        if IsModelInCdimage(clerkHash) and IsModelValid(clerkHash) then
            RequestModel(clerkHash)
            while not HasModelLoaded(clerkHash) do
                RequestModel(clerkHash)
                Wait(0)
            end
            clerk = CreatePed(clerkHash, pos.x, pos.y, pos.z, hdg, 1, 1, 0, 0)
            SetModelAsNoLongerNeeded(clerkHash)
	    Citizen.InvokeNative(0x283978a15512b2fe, clerk, true) -- You can invoke natives like this too
	end
end)

 

Edited by MrEuphrates
Link to comment
Share on other sites

@MrEuphrates OK, so I took a look at the document you linked.

The definition of the native doesnt make sense to me.

Heres what I mean:

 

loading stream in C (return type is "bool", function name is "LOAD_STREAM", parameters are two char*):

BOOL LOAD_STREAM(char* streamName, char* soundSet);

loading stream in lua (return type is "bool", function name is "LoadStream", parameters are two strings):

local retval --[[ boolean ]] = LoadStream(

		streamName --[[ string ]], 
		soundSet --[[ string ]]
	)

ambient speech in C (return type is "bool", function name is "PLAY_PED_AMBIENT_SPEECH_NATIVE", parameters are Ped and Any*):

BOOL PLAY_PED_AMBIENT_SPEECH_NATIVE(Ped ped, Any* params);

ambient speech in lua (return type is "bool", function name is "PlayPedAmbientSpeechNative", parameters are ??):

local retval --[[ boolean ]], params --[[ Any ]] =
	PlayPedAmbientSpeechNative(
		ped --[[ Ped ]]
	)

 

I dont even know how to interpret the definition.

Link to comment
Share on other sites

52 minutes ago, HughJanus said:

@MrEuphrates OK, so I took a look at the document you linked.

The definition of the native doesnt make sense to me.

Heres what I mean:

 

loading stream in C (return type is "bool", function name is "LOAD_STREAM", parameters are two char*):

BOOL LOAD_STREAM(char* streamName, char* soundSet);

loading stream in lua (return type is "bool", function name is "LoadStream", parameters are two strings):

local retval --[[ boolean ]] = LoadStream(


		streamName --[[ string ]], 
		soundSet --[[ string ]]
	)

ambient speech in C (return type is "bool", function name is "PLAY_PED_AMBIENT_SPEECH_NATIVE", parameters are Ped and Any*):

BOOL PLAY_PED_AMBIENT_SPEECH_NATIVE(Ped ped, Any* params);

ambient speech in lua (return type is "bool", function name is "PlayPedAmbientSpeechNative", parameters are ??):


local retval --[[ boolean ]], params --[[ Any ]] =
	PlayPedAmbientSpeechNative(
		ped --[[ Ped ]]
	)

 

I dont even know how to interpret the definition.

Exactly! It doesn't make any sense. and it's not only lua I thought I could write it in c# but it doesn't work either. They put your c++ solution in description but it can't be achieved with what they gave. In both lua and c# params is a returned value by the native.

Edited by MrEuphrates
Link to comment
Share on other sites

55 minutes ago, MrEuphrates said:

Exactly! It doesn't make any sense. and it's not only lua I thought I could write it in c# but it doesn't work either. They put your c++ solution in description but it can't be achieved with what they gave. In both lua and c# params is a returned value by the native.

 

Is there an option in lua to invoke the native directly?

Like in c++ it would be

invoke<BOOL>(0x8E04FEDD28D42462, ped, params); 
Link to comment
Share on other sites

1 hour ago, HughJanus said:

 

Is there an option in lua to invoke the native directly?

Like in c++ it would be


invoke<BOOL>(0x8E04FEDD28D42462, ped, params); 

 

Yes you can use this line I had in my example

Citizen.InvokeNative(0x283978a15512b2fe, clerk, true) -- You can invoke natives like this too

 

Link to comment
Share on other sites

12 minutes ago, MrEuphrates said:

 

Yes you can use this line I had in my example


Citizen.InvokeNative(0x283978a15512b2fe, clerk, true) -- You can invoke natives like this too

 

 

Why dont you try it that way?

Pass the ped instead of the clerk and pass the struct instead of the bool (and use the hash of the speech native).

Link to comment
Share on other sites

17 minutes ago, HughJanus said:

 

Why dont you try it that way?

Pass the ped instead of the clerk and pass the struct instead of the bool (and use the hash of the speech native).

 

Ok so I tried this

params = {}
params.speechName = "FAREWELL"
params.voiceName = "0479_U_M_M_VALGUNSMITH_01"
params.v3 = 1
params.speechParamHash = 0x67F3AB43
params.entity = 0
params.v6 = true
params.v7 = 1
params.v8 = 1
Citizen.InvokeNative(0x8E04FEDD28D42462, cl, params)

and got this error

SCRIPT ERROR: Invalid Lua type in __data

Pointing to the line where I Invoked the native.

Link to comment
Share on other sites

1 minute ago, MrEuphrates said:

 

Ok so I tried this


params = {}
params.speechName = "FAREWELL"
params.voiceName = "0479_U_M_M_VALGUNSMITH_01"
params.v3 = 1
params.speechParamHash = 0x67F3AB43
params.entity = 0
params.v6 = true
params.v7 = 1
params.v8 = 1
Citizen.InvokeNative(0x8E04FEDD28D42462, cl, params)

and got this error


SCRIPT ERROR: Invalid Lua type in __data

Pointing to the line where I Invoked the native.

 

I dont know which types are supported by lua, but only these worked for me in C++

struct ScriptedSpeechParams
{
	const char* speechName;
	const Any* voiceName;
	alignas(8) int v3;
	alignas(8) Hash speechParamHash;
	alignas(8) Entity entity;
	alignas(8) BOOL v6;
	alignas(8) int v7;
	alignas(8) int v8;
};

void PedFearTest(Ped ped)
{	
	string speechline = "LAW_HAIL";
	ScriptedSpeechParams params{ speechline.c_str(), 0, 1, 0x67F3AB43, 0, true, 1, 1 };
	AUDIO::x_PLAY_AMBIENT_SPEECH1(ped, (Any*)&params);
}
Link to comment
Share on other sites

3 hours ago, HughJanus said:

 

I dont know which types are supported by lua, but only these worked for me in C++


struct ScriptedSpeechParams
{
	const char* speechName;
	const Any* voiceName;
	alignas(8) int v3;
	alignas(8) Hash speechParamHash;
	alignas(8) Entity entity;
	alignas(8) BOOL v6;
	alignas(8) int v7;
	alignas(8) int v8;
};

void PedFearTest(Ped ped)
{	
	string speechline = "LAW_HAIL";
	ScriptedSpeechParams params{ speechline.c_str(), 0, 1, 0x67F3AB43, 0, true, 1, 1 };
	AUDIO::x_PLAY_AMBIENT_SPEECH1(ped, (Any*)&params);
}

 

Ok thank you for your help I will try other things and if I find a solution I will share it here.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...