I have developed a working script decompiler with which i can reconstruct almost any old script. It is not 100% accurate but i already managed to reimplement whe CTF4x4 script for example. It works like the original. If any of you want something decompiled or changed in some scripts, contact me on my discord.
I can´t release the tool publicly because it would be of no use on its own without my AI integration, so message me on discord if you want sonething
Gonzo asked me to share the scripts so i created a repo.
Database of reconstructed and original script codes of the Vietcong game
I will upload as I reconstruct more code. For now there is just the CTF4x4 script which has been modified so it could be run both normally (New custom map and gamemode - client downloads it and plays on your server) or it can be run using script sideloading and cbf patching on a vanilla map so the client does not need to download anything. I will release the tools for that later. Bots now spawn correctly in waves with players and they dynamically "disconnect" as human players log in. It should also log killstreaks and other things, but that only works when the server and client have the same script. I did not test that function so it may or may not work however it does not disrupt the scripts function so i left it there.
if (SC_MP_EnumPlayers(enum_pl, &count, SC_MP_ENUMPLAYER_SIDE_ALL)){
for (i = 0; i < count; i++){
if (enum_pl[i].status == SC_MP_P_STATUS_INGAME){
if (enum_pl[i].side == pc_info.side){
SC_P_SetRadarColor(enum_pl[i].id, 0x440000ff); // blue for allies
} else {
SC_P_SetRadarColor(enum_pl[i].id, 0x44ff0000); // red for enemies
}
// Force class name for bots (member_id != 0 means AI player)
SC_P_GetInfo(enum_pl[i].id, &pl_info);
if (pl_info.member_id != 0){
SC_P_SetForceClassName(enum_pl[i].id, 1100);
}
}
}
}
}
case FLAG_PH_INBASE:
break;
case FLAG_PH_ONGROUND:
gInfo[i] = INFO_FL_RETURNED;
gFlagPhase[i] = FLAG_PH_INBASE;
SC_SRV_Item_Release(gOnGroundNetID[i]);
gOnGroundNetID[i] = 0;
break;
case FLAG_PH_ONPLAYER:
gFlagOnPlayer_handle[i] = 0;
gFlagPhase[i] = FLAG_PH_INBASE;
gInfo[i] = INFO_FL_RETURNED;
break;
}// switch(gFlagPhase[i])
}// for (i)
}// void ResetMission(void)
// Check_ABL disabled for 4x4 mode
void Check_ABL(dword pl_handle){
// Auto-balance disabled in 4x4 bot mode
}
// Recover dead AI bots to fill teams up to MIN_PLAYERS_PER_TEAM
void RecoverAllDeadAi(void){
dword i, count, side;
s_SC_MP_EnumPlayers enum_pl[64];
dword alive_count[2] = {0, 0};
dword bots_to_recover[2];
count = 64;
if (SC_MP_EnumPlayers(enum_pl, &count, SC_MP_ENUMPLAYER_SIDE_ALL)){
// First pass: count alive players per side (humans + bots)
for (i = 0; i < count; i++){
side = enum_pl[i].side;
if (side < 2 && enum_pl[i].status == SC_MP_P_STATUS_INGAME){
alive_count[side]++;
}
}
// Calculate how many bots to recover per side
bots_to_recover[0] = (alive_count[0] < MIN_PLAYERS_PER_TEAM) ?
(MIN_PLAYERS_PER_TEAM - alive_count[0]) : 0;
bots_to_recover[1] = (alive_count[1] < MIN_PLAYERS_PER_TEAM) ?
(MIN_PLAYERS_PER_TEAM - alive_count[1]) : 0;
// Second pass: recover dead bots up to the limit
for (i = 0; i < count; i++){
side = enum_pl[i].side;
if (side >= 2) continue;
if (enum_pl[i].status == SC_MP_P_STATUS_INGAMEDEATH){
if (bots_to_recover[side] > 0){
// Try to recover - returns TRUE only for AI players
if (SC_MP_RecoverAiPlayer(enum_pl[i].id, NULL, 0.0f)){
bots_to_recover[side]--;
}
}
}
}
}
}
// Get killstreak slot from player handle (simple hash)
dword GetStreakSlot(dword pl_handle){
return pl_handle & 63;
}
// Set killstreak info (flag events have priority)
void SetKillstreakInfo(dword info_value, dword killer_handle){
// Flag events (1-4) have priority - don't overwrite
if (gInfo[0] >= INFO_FL_STOLEN && gInfo[0] <= INFO_SCORE) return;
}// if (SC_MP_EnumPlayers(enum_pl,&j,SC_MP_ENUMPLAYER_SIDE_ALL))
for (j=0;j<2;j++)
for (i=0;i<gRecs[j];i++)
gRecTimer[j][i] -= info->elapsed_time;
gNextRecover -= info->elapsed_time;
if (gNextRecover < 0.0f){
gNextRecover = GetRecovTime();
// Oživit mrtvé AI ve stejné vlně jako lidi
if (gMission_started){
RecoverAllDeadAi();
sprintf(txt, "Reinforcements spawning!");
SC_GameInfoW(SC_AnsiToUni(txt, wtxt));
}
}
if (gMission_started){
for (i=0;i<2;i++){
valid = FALSE;
switch(gFlagPhase[i]){
case FLAG_PH_INBASE:
in_base:
vec = gOrigFlagPos[i];
valid = TRUE;
break;
case FLAG_PH_ONGROUND:
if (SC_Item_GetPos(gOnGroundNetID[i],&vec)) valid = TRUE;
else gOnGroundTimer[i] = 0.0f;
case FLAG_PH_INBASE:
if (plinfo.side!=i){
gInfo[i] = INFO_FL_STOLEN;
gFlagPhase[i] = FLAG_PH_ONPLAYER;
gFlagOnPlayer_handle[i] = SC_MP_GetHandleofPl(j);
// Notify bot about flag pickup (member_id != 0 means AI player)
if (plinfo.member_id != 0){
SC_P_ScriptMessage(j, BOT_MSG_FLAG_PICKUP, 0);
}
}
break;
case FLAG_PH_ONGROUND:
j = SC_ggi(GVAR_INFO+i);
if (j!=gCLN_Info[i]){
gCLN_Info[i] = j;
switch(j){
case INFO_FL_STOLEN:
swprintf(wtxt,SC_AnsiToUni("%s %s",wtxt2),SC_Wtxt(1010+i),SC_Wtxt(1067));
// play sound
if (gCLN_FlagOnPlayer[i]==SC_PC_Get()){
SC_SND_PlaySound2D(10424);
}
else{
SC_SND_PlaySound2D(10425);
}
break;
case INFO_FL_DROPPED:swprintf(wtxt,SC_AnsiToUni("%s %s",wtxt2),SC_Wtxt(1010+i),SC_Wtxt(1068));break;
case INFO_FL_RETURNED:swprintf(wtxt,SC_AnsiToUni("%s %s",wtxt2),SC_Wtxt(1010+i),SC_Wtxt(1069));break;
case INFO_SCORE:
// improve message, add player name
if (pl_val)
swprintf(wtxt,SC_AnsiToUni("%s (%S) %s",wtxt2),SC_Wtxt(1011-i),SC_P_GetName(pl_val),SC_Wtxt(1070));
else
swprintf(wtxt,SC_AnsiToUni("%s %s",wtxt2),SC_Wtxt(1011-i),SC_Wtxt(1070));
// play sound
SC_SND_PlaySound2D(11116+i);
break;
// Killstreak messages
case INFO_STREAK_ONROLL:
pl_val = SC_MP_GetPlofHandle(SC_ggi(GVAR_KILLSTREAK_PLAYER));
if (pl_val)
swprintf(wtxt,SC_AnsiToUni("%S is ON A ROLL!",wtxt2),SC_P_GetName(pl_val));
else
SC_AnsiToUni("ON A ROLL!", wtxt);
break;
case INFO_STREAK_RAMPAGE:
pl_val = SC_MP_GetPlofHandle(SC_ggi(GVAR_KILLSTREAK_PLAYER));
if (pl_val)
swprintf(wtxt,SC_AnsiToUni("%S is on a RAMPAGE!",wtxt2),SC_P_GetName(pl_val));
else
SC_AnsiToUni("RAMPAGE!", wtxt);
break;
case INFO_STREAK_UNSTOPPABLE:
pl_val = SC_MP_GetPlofHandle(SC_ggi(GVAR_KILLSTREAK_PLAYER));
if (pl_val)
swprintf(wtxt,SC_AnsiToUni("%S is UNSTOPPABLE!",wtxt2),SC_P_GetName(pl_val));
else
SC_AnsiToUni("UNSTOPPABLE!", wtxt);
break;
case INFO_STREAK_GODLIKE:
pl_val = SC_MP_GetPlofHandle(SC_ggi(GVAR_KILLSTREAK_PLAYER));
if (pl_val)
swprintf(wtxt,SC_AnsiToUni("%S is GODLIKE!",wtxt2),SC_P_GetName(pl_val));
else
SC_AnsiToUni("GODLIKE!", wtxt);
break;
}// switch(j)
SC_GameInfoW(wtxt);
}// if (j!=gCLNd_Info[i])
if ((pl_id)&&(gCLN_FlagOnPlayer[i]==pl_id)){
gCLN_MyFlagTimer += info->elapsed_time;
while (gCLN_MyFlagTimer > 1.0f) gCLN_MyFlagTimer -= 1.0f;
// preload flag items
SC_Item_Preload(145);
SC_Item_Preload(146);
// preload flag eqp items
SC_PreloadBES(PRELOADBES_FLAG_US,"G\\WEAPONS\\Vvh_flag\\Vvh_flag_US_3pv.BES");
SC_PreloadBES(PRELOADBES_FLAG_VC,"G\\WEAPONS\\Vvh_flag\\Vvh_flag_VC_3pv.BES");
nod = SC_NOD_Get(NULL,"flag_us");
if (!nod) SC_message("US flag not found 01");
gFlagNod[0] = SC_NOD_Get(nod,"Vlajka US");
if (!gFlagNod[0]) SC_message("VC flag not found 01");
SC_NOD_GetWorldPos(nod,&gOrigFlagPos[0]);
nod = SC_NOD_Get(NULL,"flag_vc");
if (!nod) SC_message("VC flag not found 01");
gFlagNod[1] = SC_NOD_Get(nod,"Vlajka VC");
if (!gFlagNod[1]) SC_message("VC flag not found 02");
SC_NOD_GetWorldPos(nod,&gOrigFlagPos[1]);
if (info->param1){
// it's server
SC_Log(3,"difficulty setting is %d",SC_ggi(SGI_DIFFICULTY));
// 4x4: Set bot counts
SC_sgi(GVAR_BOTCOUNT_US, 4);
SC_sgi(GVAR_BOTCOUNT_VC, 4);
I dont think its necessary to post the actual code here. I have already updated the script so it now supports 6 bots per side and the dynamic spawning is now more reliable. It is still buggy but hell it is much better than before.
I have also successfully reconstructed the actual CTF BOT script. Check it out in the repo
I just added my new complete CTF bot system with brief documentation and integration instructions.
Bots are much smarter and aggressive (like human players)
Bots dynamically deactivate as human players connect (not very reliable yet)
Bots connect into pairs (Leader - buddy)
Added support for custom camping waypoints
Six bots per side now supported. Every bot now has unique model and various weapons.
You can integrate this script version now into your map and make updates to the code later. You then create a CBF patch for your server - the bot logic runs completely on the server side so there is no need to update the map itself for the client. Even the individual Bot scripts can be stored only on the server side.
The client can actually run a vanilla map with pure CTF script - you only modify your map folder with these scripts and modified .sco file on the server side.
It can be applied to vanilla maps and replace the original CTF.scr in the INI folder (but this shifts the game memory offsets and tools using binary patches or hooks stop working - VCGuard etc.) This can be overcame with script sideloading - i will release the tool for that later.