View Full Version : help - radio emulation
commonbullet
19-10-2005, 06:27
hi,
amxmod x , bots stuff.
i was trying to emulate those radio commands like 'taking fire', etc.
i've written messages to BotVoice, SendAudio, TextMsg to 'throw' the parameters, and i managed to make the radio messages sound and display correctly, so it really looks like the "real" radio messages.
but bots won't understand them. (neither csbots nor podbots)
so i wonder if there's another variable must be set. maybe it would work for both bots...???:(
Pierre-Marie Baty
19-10-2005, 11:21
The bots needs to know the entity sending the message. What is your message sender edict_t * pointing to ?
commonbullet
19-10-2005, 15:54
I’m actually not sendind the edict information directly.
The pev list is too big, I can’t guess which is the one for the sender, or I don’t know if it’s implicitly passed by the messages. So here’s a sample code:
public Radio(id)
{
// BotVoice =135
// SendAudio = 100
// TextMsg = 77
server_print (g_playerLocation[id])
message_begin (MSG_ALL,135) // I don’t know if it should be MSG_ALL…
write_byte (1) // start radio message
write_byte (id) //user id
message_end ()
set_task (3,"clearMsg",id) //delay before clear msg
message_begin(MSG_ALL,100)
write_byte (id) // player id
write_string ("%!MRAD_HITASSIST") // sample taking fire
write_short (100) //voice pitch
message_end ()
new sid[3] // sender id to String
new uname[32] // sender name
format (sid,2,"%d",id)
get_user_name (id,uname,31)
message_begin( MSG_ALL,77)
write_byte (5) // i don't know why "5", i guess it's the message type
write_string (sid) // sender id string, i guess
write_string ("#Game_radio_location")
write_string (uname) //sender name
/* g_playerLocation is a global array that
contains the current user location
and is updated by "Location" event
*/
write_string (g_playerLocation[id])
write_string ("#Taking_fire") //sample taking fire
message_end ()
}
public clearMsg(id){
message_begin(MSG_ALL, 135)
write_byte(0)
write_byte(id)
message_end()
}
Thanks.
Pierre-Marie Baty
21-10-2005, 03:05
Oh, stupid me... you're talking about AMX scripting, not C++ code... :(
Sorry I can't really help. Perhaps people like KWo or others with a good bit of knowledge in AMX scripting can.
To help him, I really need two know a lot of things:
1. How do bot percept my radio command, when I use "Follow me" for example - I need to know what is the most important thing the bots need to receive to intercept is a radio message.
2. Write_byte, write_string, message_begin etc - I believe they are some HL SDK functions used directly from sma scripts without necessity converting them to any C++ HL functions - so any bot coder should be able to say anything more tahn me about them (I don't know HL SDK). I will ask some AMX or AMX X developer if it's true. I used such commands sequence to clear-up buy icon - to prevent people buying any stuff. But for some reason it was not working for bots (for human players it's working great) - probably it's because of metamod preventing to send some messages to bots.
3. Need more knowledge about metamod - what is allowed , what is blocked etc. What is possible to send to another mm plugin - what is not.
I think only Pierre with cooperation with Bailopan (AMX MOD X dev team, metamod maintainer) and T(+)rget (AMX dev team) can really help You. I will ask them to say us something more.
Pierre-Marie Baty
23-10-2005, 04:17
As for point #2, so far the main difference that I saw (but perhaps I haven't seen enough) between AMX scripts and C code is that in AMX scripts, they seem to never use the "edict" type, which is the fundamental entity type we use in HL. Don't know how they manage to do without... and I don't really want to know :D
BAILOPAN
23-10-2005, 10:34
Edicts are passed around by their entity index. AMX Mod X has the entity_[get|set]_[type] functions for pulling things out of the entvars struct.
Of course, player indices are themselves edict indices, so your statement didn't make much sense.
They don't pass around an edict type because Small is type-generic (it only has one data type, which is integral). That is why it uses entity indices.
Pierre-Marie Baty
24-10-2005, 21:37
I thought Small was a weak-typed language ?
Anyway, like I said, I don't know the AMX Mod X way of doing things at all, but provided there is a possibility to send and hook the same messages than in native code, it should be possible to send "fake" radio messages, and bots should be able to interpret them.
But like I said too, for the bots to interpret them correctly, they need to know the sender of the message, and the sender of the message must be a valid player edict. That's all :)
commonbullet
26-10-2005, 02:46
Which one is the most relevant message event for the radio? (I mean, the one that bots would interpret as someone sending radio message.)
SendAudio? TextMsg? Both?
I don’t know if it has something to do with that - I’m able to send those amx ‘fake messages’ when I’m dead, so maybe they’re not reliable enough for the bots…
Thank you.
Ps.
Just made a test and verified that the radio messages are displayed to both teams (terrorists and cts). I’ve seen no way to limit the message scope except by the destinations constants, but I don’t know how to use it.
Here’s the list from amxmod manual.
MSG_BROADCAST 0 //Unreliable message to everyone
MSG_ONE 1 //Reliable message to one client
MSG_ALL 2 //Reliable message to everyone
MSG_INIT 3 //Write to the init string
MSG_PVS 4 //Ents in PVS of origin
MSG_PAS 5 //Ents in PAS of origin
MSG_PVS_R 6 //Reliable to PVS
MSG_PAS_R 7 //Reliable to PAS
MSG_ONE_UNRELIABLE 8 //Unreliable to one client
MSG_SPEC 9 //Send to spectator proxies
I’ll be trying them…
Pierre-Marie Baty
26-10-2005, 05:00
No, it has nothing to do with the message scopes. Looping through all the clients and sending a MSG_ONE_UNRELIABLE to those you want to receive the radio message is the only and right way to do.
The messages most bots hook when it comes to radio messages are TextMsg. The discriminative is a string, usually starting with "#". Look in some bot source code (for example, POD-bot mm) to see what are the audio message strings that can be used.
edit: alternative method. Use PMTools to log in a text file (or just to display on the screen) what are the network messages that are sent when you fire a radio message yourself.
commonbullet
26-10-2005, 06:37
ok,
the messages scope is correct now, by looping the MSG_ONE_UNRELIABLE.
You're right, I'll try checking podbot source.
Also, csbots answers to podbot radio messages...
thanks.
commonbullet
26-10-2005, 07:29
maybe I'm wrong, but I guess podbots fake the radio-menus instead of writing the radio messages. From bot.cpp:
if (pBot->iRadioSelect < 10)
FakeClientCommand (pBot->pEdict, "radio1;menuselect %d\n", pBot->iRadioSelect);
else if (pBot->iRadioSelect < 20)
FakeClientCommand (pBot->pEdict, "radio2;menuselect %d\n", pBot->iRadioSelect - 10);
else
FakeClientCommand (pBot->pEdict, "radio3;menuselect %d\n", pBot->iRadioSelect - 20);
I guess it would work in amx, but i was trying to make the messages more customizable (sounds, etc).
I'd forgotten the other part: podbots don't answer to csbots.
commonbullet
26-10-2005, 16:10
Now trying to find how bots read users radio messages.
This is from dll.cpp.
I need a help to read this - I guess it's also checking the selected item from the radio-menu of the sender.
// Check Radio Commands (fix): do it here since metamod won't call our own ClientCommand()
iClientIndex = ENTINDEX (pFakeClient) - 1;
if (clients[iClientIndex].IsAlive && (iRadioSelect[iClientIndex] != 0) && (strncmp (g_argv, "menuselect", 10) == 0))
{
iRadioCommand = atoi (g_argv + 11);
if (iRadioCommand != 0)
{
iRadioCommand += 10 * (iRadioSelect[iClientIndex] - 1);
if ((iRadioCommand != RADIO_AFFIRMATIVE)
&& (iRadioCommand != RADIO_NEGATIVE)
&& (iRadioCommand != RADIO_REPORTINGIN))
{
for (i = 0; i < gpGlobals->maxClients; i++)
{
if (bots[i].is_used && (bots[i].bot_team == clients[iClientIndex].iTeam)
&& (pFakeClient != bots[i].pEdict))
{
if (bots[i].iRadioOrder == 0)
{
bots[i].iRadioOrder = iRadioCommand;
bots[i].pRadioEntity = pFakeClient;
}
}
}
}
g_rgfLastRadioTime[clients[iClientIndex].iTeam - 1] = gpGlobals->time;
}
iRadioSelect[iClientIndex] = 0;
}
else if (strncmp (g_argv, "radio", 5) == 0)
iRadioSelect[iClientIndex] = atoi (g_argv + 5);
// End Radio Commands
Pierre-Marie Baty
27-10-2005, 01:44
You're looking in the wrong place. Don't look at how the bots send radio messages, look at how the bots CATCH radio messages. It's in bot_client.cpp, where all of the network message hooking is done.
commonbullet
29-10-2005, 20:10
sorry, I've tried that but my poor c++ knowledge is not enough for finding it inside bot_client.cpp.
I'll keep up trying and I'll tell you if I make some progress.
Thanks.
Well - I'm not a C++ coder, but try to look into bot.cpp and find this function
void BotCheckMessageQueue (bot_t *pBot)
but You need to find also the function BotGetMessageQueue (it exists in the same file) to understand the first one. Hope it help You.
commonbullet
02-11-2005, 02:23
Thank you.
I couldn't get bots to work with it yet, but I'm still trying.
btw, I've written this very stupid plugin while messing with those radio messages.:D
http://www.amxmodx.org/forums/viewtopic.php?p=177886
commonbullet
02-11-2005, 04:54
I was trying it with PM_tools.
There's one thrown message whenever radio is activated that I didn't handle. It's type 23, signed as "tempentity?"
I've checked and my script doesn't send such message.
I couldn't guess what are the values for.
I've added one csbot to test that and there's the result. The type of message nor the player position seems to affect this values.
PMTOOLS (HUD): Entity #1 (commonbullet) SENDS MESSAGE type 23 ("tempentity?") in 1 ("MSG_ONE") from NULL
PMTOOLS (console): MESSAGE_BEGIN (MSG_ONE, tempentity?, NULL, Entity #1 (commonbullet))
PMTOOLS (console): WRITE_BYTE (124 [0x7c])
PMTOOLS (console): WRITE_BYTE (1 [0x1])
PMTOOLS (console): WRITE_COORD (35.00)
PMTOOLS (console): WRITE_SHORT (185)
PMTOOLS (console): WRITE_SHORT (15)
PMTOOLS (console): MESSAGE_END ()
PMTOOLS (HUD): Entity #2 (Hank) SENDS MESSAGE type 23 ("tempentity?") in 1 ("MSG_ONE") from NULL
PMTOOLS (console): MESSAGE_BEGIN (MSG_ONE, tempentity?, NULL, Entity #2 (Hank))
PMTOOLS (console): WRITE_BYTE (124 [0x7c])
PMTOOLS (console): WRITE_BYTE (1 [0x1])
PMTOOLS (console): WRITE_COORD (35.00)
PMTOOLS (console): WRITE_SHORT (185)
PMTOOLS (console): WRITE_SHORT (15)
PMTOOLS (console): MESSAGE_END ()
Pierre-Marie Baty
02-11-2005, 20:25
lol @ the plugin :D
Anyway. Here's how my bots (RACC) catch radio messages in Counter-Strike. Don't be confused by the syntax, the typedefs and the variable types as this is part of an engine-independent layer.
void HLEngine_NetworkMessage_TextMsg (void)
{
// this message tells a client that his player received a radio message, to enable him to
// play the appropriate radio sound sample file.
player_t *pPlayer;
char sound_file[256];
sound_t *radio_sound;
int i;
if (!((message[0].packet_type == PACKET_STRING)
&& (strcmp (message[0].szValuePassed, "#Game_radio") == 0)))
return; // cancel if message is NOT a radio message
pPlayer = &players[message_header.player_index]; // quick access to player
// is the player receiving this message a bot AND is it a radio message ?
if (pPlayer->is_racc_bot)
{
// identify the radio sample and choose the sound file that will be played at the bot's
// first radio menu
if (strcmp ("#Cover_me", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_coverme.wav", sizeof (sound_file)); // 'Cover me' radio message
else if (strcmp ("#You_take_the_point", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/takepoint.wav", sizeof (sound_file)); // 'You take the point' radio message
else if (strcmp ("#Hold_this_position", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/position.wav", sizeof (sound_file)); // 'Hold this position' radio message
else if (strcmp ("#Regroup_team", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/regroup.wav", sizeof (sound_file)); // 'Regroup team' radio message
else if (strcmp ("#Follow_me", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/followme.wav", sizeof (sound_file)); // 'Follow me' radio message
else if (strcmp ("#Taking_fire", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/fireassis.wav", sizeof (sound_file)); // 'Taking fire, need backup' radio message
// second radio menu
else if (strcmp ("#Go_go_go", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/com_go.wav", sizeof (sound_file)); // 'Go Go Go' radio message
else if (strcmp ("#Team_fall_back", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/fallback.wav", sizeof (sound_file)); // 'Team fall back' radio message
else if (strcmp ("#Stick_together_team", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/sticktog.wav", sizeof (sound_file)); // 'Stick together team' radio message
else if (strcmp ("#Get_in_position_and_wait", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/com_getinpos.wav", sizeof (sound_file)); // 'Stay in position and wait for my go' radio message
else if (strcmp ("#Storm_the_front", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/stormfront.wav", sizeof (sound_file)); // 'Storm The Front' radio message
else if (strcmp ("#Report_in_team", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/com_reportin.wav", sizeof (sound_file)); // 'Report In' radio message
// third radio menu
else if (strcmp ("#Affirmative", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_affirm.wav", sizeof (sound_file)); // 'Affirmative' radio message
else if (strcmp ("#Enemy_spotted", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_enemys.wav", sizeof (sound_file)); // 'Enemy spotted' radio message
else if (strcmp ("#Need_backup", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_backup.wav", sizeof (sound_file)); // 'Need backup' radio message
else if (strcmp ("#Sector_clear", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/clear.wav", sizeof (sound_file)); // 'Sector clear' radio message
else if (strcmp ("#In_position", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_inpos.wav", sizeof (sound_file)); // 'I'm in position' radio message
else if (strcmp ("#Reporting_in", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ct_reportingin.wav", sizeof (sound_file)); // 'Reporting in' radio message
else if (strcmp ("#Get_out_of_there", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/blow.wav", sizeof (sound_file)); // 'Get outta here' radio message
else if (strcmp ("#Negative", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/negative.wav", sizeof (sound_file)); // 'Negative' radio message
else if (strcmp ("#Enemy_down", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/enemydown.wav", sizeof (sound_file)); // 'Negative' radio message
// client-side radio messages
// I miss "A hostage has been rescued", dunno how to catch it (if you do, lemme know)
else if (strcmp ("#Bomb_Planted", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/bombpl.wav", sizeof (sound_file)); // 'Bomb planted' radio message
else if (strcmp ("#Bomb_Defused", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/bombdef.wav", sizeof (sound_file)); // 'Bomb defused' radio message
else if (strcmp ("#Round_Draw", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/rounddraw.wav", sizeof (sound_file)); // 'Round draw' radio message
else if (strcmp ("#Terrorists_Win", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/terwin.wav", sizeof (sound_file)); // 'Terrorists win' radio message
else if (strcmp ("#CTs_Win", message[2].szValuePassed) == 0)
SAFE_strncpy (sound_file, "radio/ctwin.wav", sizeof (sound_file)); // 'Counter-terrorists win' radio message
// find the sound we want in the global list
for (i = 0; i < GameConfig.sound_count; i++)
if (strcmp (GameConfig.sounds[i].file_path, sound_file) == 0)
radio_sound = &GameConfig.sounds[i]; // link a pointer to this sound's info slot
// have we found the sound we want to play ?
if (radio_sound != NULL)
BotFeedEar (pPlayer, radio_sound, &pPlayer->pEntity->v_eyeposition, 1.0); // bot will hear this radio sound
}
return;
}Basically all you need to remember is that when a radio message is received by a player, it's under the form of a TextMsg, the first string that is passed (text message type) is '#Game_Radio' and the message string that is passed is a short identifier for the message starting with '#'.
Hook TextMsgs, check for the presence of a Game_Radio message, and determine which type of message it is by strcmp'ing it.
vBulletin® v3.7.1, Copyright ©2000-2009, Jelsoft Enterprises Ltd.