Big update on this one in my libcod repo:
First of all, these functions will only work on bots now, i see no purpose for then on a real players.
setWalkDir(dir); // Unchanged
setLean(dir); Values: "left", "right", "none". Bot lean left/right.
setStance(stance); // Values: "stand", "crouch", "prone", "jump". Bot stance. (crouching etc.)
throwNade(bool); // Unchanged but improved it's behavior (can be used in any stance, etc.)
fireWeapon(bool); // Bot shooting.
meleeWeapon(bool); // Bot meeleing.
reloadWeapon(bool); // Bot reloading.
adsAim(bool); // Bot ADS.
switchToWeaponId(weapon id); // Bot switches to a specified weapon id (if bot doesn't have that weapon, just nothing happens.)
Bellow functions has been removed:
setWepType(weap type); // No idea what was it for, proably some misunderstanding.
setWalkAngle(dir); // Maybe will readd it with a proper name. But i haven't seen any real purpose of using it.
But let's go to a practice usage lol.
A weird idea came in to my mind:
Bot - Companion:
A bot that follows you and shoots whatever he sees, also bot is controlled by a quick messages, he can go to desired position and wait or just follow.
Bot easily kicks ass, can melee, smartly switches weapons, tries to move in combat, reloads when he can.
Edit: Okay, You'll need a Mitch's weapon functions for this (https://killtube.org/showthread.php?...oveSpeedScale)) also it may contain some code made by kung or izno and maybe others.
companion.gsc
PHP Code:
companionSpawn() // Call this to spawn a companion
{
companion = addTestClient();
if (isDefined(companion))
{
companion resetFunctions();
companion.isBot = true;
wait 0.5;
companion renameClient(self.name + "^7's Companion");
companion.pers["team"] = self.pers["team"];
companion.pers["weapon"] = self.pers["weapon"];
companion.owner = self;
self.companion = companion;
companion maps\mp\gametypes\tdm::spawnPlayer(); // <-------- YOUR GAMETYPE SPAWNPLAYER
companion thread companionLogic();
companion setOrigin(companion.owner.origin + (0, 0, 25));
// setCvar("g_antilag", 0);
}
}
companionLogic()
{
self endon("disconnect");
self.enemy = undefined;
self.state = spawnStruct();
self.state.following = 1;
self.state.movingtopos = undefined;
self.state.agressive = 1;
self.state.lastreloadtime = getTime();
self.state.lastweaponslot = "none";
while(isDefined(self))
{
if (isAlive(self))
{
if (!isDefined(self.owner))
{
kick2(self getEntityNumber(), "owner disconnected.");
// setCvar("g_antilag", 1);
}
if (!isAlive(self.owner))
{
kick2(self getEntityNumber(), "owner died.");
// setCvar("g_antilag", 1);
}
self ladderCheck();
if (isDefined(self.state.movingtopos))
{
self adsAim(false);
angles = vectorToAngles(vectorNormalize(self.state.movingtopos - self getRealEye()));
self setPlayerAngles(angles);
if (distance(self.origin, self.state.movingtopos) > 25)
{
self setWalkDir("forward");
}
else
{
// Bot reached target position
self.state.movingtopos = undefined;
self setWalkDir("none");
self notify("menuresponse", "quickstatements", "3");
}
}
else
{
if (self.state.following)
{
// Teleport companion back to its owner if they went to far away from each other
if (distance(self.owner.origin, self.origin) > 1000)
self setOrigin(self.owner.origin + (0, 0, 25));
}
self.enemy = self getNearestPlayerForBot(self getOpposingTeam(self.pers["team"]));
if (self.state.agressive && isDefined(self.enemy))
{
// Don't just stand there...
self thread randomMoving();
// Do not reload on enemy
self.state.lastreloadtime = getTime();
// Out of ammo in clip but other weapon has ammo, switch to it.
if (!self getWeaponSlotClipAmmo(self getCurrentWeaponSlot()) && self getWeaponSlotClipAmmo(self getOppositeWeaponSlot()))
self switchToWeaponId(maps\mp\gametypes\_weaponfunctions::getWeaponId(self getWeaponSlotWeapon(self getOppositeWeaponSlot())));
// No ammo for both clips, use main weapon. Edit: if it has ammo.
else if (!self getWeaponSlotClipAmmo(self getCurrentWeaponSlot()) && !self getWeaponSlotClipAmmo(self getOppositeWeaponSlot()) && botHasAmmo("primary"))
{
if (self getCurrentWeaponSlot() != "primary")
self switchToWeaponId(maps\mp\gametypes\_weaponfunctions::getWeaponId(self getWeaponSlotWeapon(self getOppositeWeaponSlot())));
}
// No ammo in one slot, but have it in other, switch to another weapon.
else if (!self botHasAmmo(self getCurrentWeaponSlot()) && self botHasAmmo(self getOppositeWeaponSlot()))
self switchToWeaponId(maps\mp\gametypes\_weaponfunctions::getWeaponId(self getWeaponSlotWeapon(self getOppositeWeaponSlot())));
angles = vectorToAngles(vectorNormalize(self.enemy getRealEye() - (self getRealEye() + (0, 0, 20))));
self setPlayerAngles(angles);
if (distance(self.origin, self.enemy.origin) < 75)
{
self adsAim(false);
self thread botMelee();
}
else if (self botHasAmmo(self getCurrentWeaponSlot()))
{
if (distance(self.origin, self.enemy.origin) > 350)
self adsAim(true);
else
self adsAim(false);
self thread botShoot();
}
else
self adsAim(false);
}
else
{
self adsAim(false);
if (getTime() > self.state.lastreloadtime + 3000)
{
if (self getWeaponSlotClipAmmo(self getCurrentWeaponSlot()) < maps\mp\gametypes\_weaponfunctions::weaponClipSize(self getWeaponSlotWeapon(self getCurrentWeaponSlot())))
{
// Try to reload now
self reloadWeapon(true);
// Reset reload timer
self.state.lastreloadtime = getTime();
}
else if ((self botHasAmmo(self getOppositeWeaponSlot())) && (self getWeaponSlotClipAmmo(self getOppositeWeaponSlot()) < maps\mp\gametypes\_weaponfunctions::weaponClipSize(self getWeaponSlotWeapon(self getOppositeWeaponSlot()))))
self switchToWeaponId(maps\mp\gametypes\_weaponfunctions::getWeaponId(self getWeaponSlotWeapon(self getOppositeWeaponSlot())));
}
angles = vectorToAngles(vectorNormalize(self.owner getRealEye() - self getRealEye()));
self setPlayerAngles(angles);
if (self.state.following && distance(self.origin, self.owner.origin) > 150)
{
self setWalkDir("forward");
}
else
self setWalkDir("none");
}
self.state.lastweaponslot = self getCurrentWeaponSlot();
}
}
else
{
kick2(self getEntityNumber(), "companion died.");
// setCvar("g_antilag", 1);
}
wait 0.05;
// Reload always false on endframe
self reloadWeapon(false);
// 0 - ignore state, always on endframe
self switchToWeaponId(0);
}
}
botHasAmmo(slot)
{
if (self getWeaponSlotClipAmmo(slot) + self getWeaponSlotAmmo(slot))
return true;
else
return false;
}
botMelee()
{
self endon("disconnect");
if (isDefined(self.meleeing))
return;
self.meleeing = 1;
self meleeWeapon(true);
wait 0.05;
self meleeWeapon(false);
wait 0.05;
self.meleeing = undefined;
}
botShoot()
{
self endon("disconnect");
if (isDefined(self.shooting))
return;
self.shooting = 1;
self fireWeapon(true);
wait 0.05;
self fireWeapon(false);
wait 0.05;
self.shooting = undefined;
}
resetFunctions()
{
self setWalkDir("none");
self setLean("none");
self setStance("stand");
self throwNade(false);
self fireWeapon(false);
self meleeWeapon(false);
self reloadWeapon(false);
self adsAim(false);
self switchToWeaponId(0);
}
randomMoving(number)
{
self endon("disconnect");
if (isDefined(self.movingincombat))
return;
self.movingincombat = 1;
number = randomInt(5);
switch(number)
{
case 0:
self setWalkDir("forward");
wait randomIntRange(3, 5);
break;
case 1:
self setWalkDir("left");
wait randomIntRange(3, 5);
break;
case 2:
self setWalkDir("right");
wait randomIntRange(3, 5);
break;
case 3:
self setWalkDir("back");
wait randomIntRange(3, 5);
break;
default:
self setWalkDir("none");
wait randomIntRange(3, 5);
break;
}
self.movingincombat = undefined;
}
ladderCheck()
{
if (self isOnLadder())
return;
if (self isMantling())
return;
if (self getCurrentWeapon() == "none")
self switchToWeaponId(maps\mp\gametypes\_weaponfunctions::getWeaponId(self getWeaponSlotWeapon(self.state.lastweaponslot)));
}
messageControl(saytext)
{
if (!isDefined(self.companion))
return;
switch(saytext)
{
case "QUICKMESSAGE_FOLLOW_ME":
case "QUICKMESSAGE_REGROUP":
self.companion.state.following = 1;
self.companion.state.movingtopos = undefined;
break;
case "QUICKMESSAGE_HOLD_THIS_POSITION":
self.companion.state.following = 0;
self.companion.state.movingtopos = undefined;
break;
case "QUICKMESSAGE_MOVE_IN":
self.companion.state.following = 0;
self.companion.state.movingtopos = self playerLookPosition(10000);
break;
case "QUICKMESSAGE_HOLD_YOUR_FIRE":
self.companion.state.agressive = 0;
break;
case "QUICKMESSAGE_SUPPRESSING_FIRE":
self.companion.state.agressive = 1;
break;
default:
break;
}
}
getNearestPlayerForBot(team)
{
players = getentarray("player", "classname");
playerNearestDistance = 9999999999;
playerNearest = undefined;
for (i = 0; i < players.size; i++)
{
if (players[i].pers["team"] != team || !isAlive(players[i]) || players[i] == self || !isReachable(players[i] getRealEye(), self getRealEye()))
continue;
distanceToThis = distanceSquared(players[i].origin, self.origin);
if (distanceToThis < playerNearestDistance)
{
playerNearestDistance = distanceToThis;
playerNearest = players[i];
}
}
return playerNearest;
}
getRealEye()
{
player = self;
if (player.pers["team"] == "spectator")
return player.origin;
stance = player getStance();
offset = 0;
switch (stance)
{
case "stand":
offset = 20;
break;
case "duck":
offset = 0;
break;
case "lie":
offset = -30;
break;
default:
break;
}
return player getEye() + (0, 0, offset);
}
getCurrentWeaponSlot()
{
if (self getCurrentWeapon() != self getWeaponSlotWeapon("primaryb"))
return "primary";
else if (self getCurrentWeapon() != self getWeaponSlotWeapon("primary"))
return "primaryb";
else
return "none";
}
getOpposingTeam(team)
{
switch(team)
{
case "allies":
return "axis";
case "axis":
return "allies";
default:
return "none";
}
}
getOppositeWeaponSlot()
{
if (self getCurrentWeaponSlot() == "primary")
return "primaryb";
else if (self getCurrentWeaponSlot() == "primaryb")
return "primary";
else
return "none";
}
playerLookPosition(maxdist)
{
player = self;
originStart = player getRealEye();
angles = player getPlayerAngles();
forward = anglesToForward(angles);
originEnd = originStart + vectorScale(forward, maxdist);
trace = bulletTrace(originStart, originEnd, false, undefined);
return trace["position"];
}
isReachable(start, end)
{
return bulletTracePassed(start, end, false, undefined);
}
Modified _weaponfunctions.gsc:
PHP Code:
loadWeaponFunctions()
{
setDefaultWeapon("mp40_mp");
loadedweapons = getLoadedWeapons();
for (i = 0; i < loadedweapons.size; i++)
{
weaponname = loadedweapons[i]; // i = weapon index
if (isDefined(level.weapons[weaponname]))
loadWeaponInfo(i, level.weapons[weaponname]);
}
}
loadWeaponInfo(i, array)
{
array.id = i;
array.dmg = getweapondamage(i);
array.meleedmg = getweaponmeleedamage(i);
array.firetime = getweaponfiretime(i);
array.meleetime = getweaponmeleetime(i);
array.reloadtime = getweaponreloadtime(i);
array.reloademptytime = getweaponreloademptytime(i);
assert(i <= 64); // no viewmodels after 64th weapon
}
getWeaponArray(weapon)
{
if (isDefined(level.weapons[weapon]) && isDefined(level.weapons[weapon].id))
return level.weapons[weapon];
else
return undefined;
}
updateWepDamage(weapon, dmg)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.dmg = dmg;
}
updateWepMeleeDamage(weapon, dmg)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.meleedmg = dmg;
}
updateWepFireTime(weapon, time)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.firetime = time;
}
updateWepMeleeTime(weapon, time)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.meleetime = time;
}
updateWepReloadTime(weapon, time)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.reloadtime = time;
}
updateWepReloadEmptyTime(weapon, time)
{
array = getWeaponArray(weapon);
if (isDefined(array))
array.reloademptytime = time;
}
getWeaponId(weapon)
{
if (isDefined(level.weapons[weapon]) && isDefined(level.weapons[weapon].id))
return level.weapons[weapon].id;
else
return -1;
}
weaponMaxAmmo(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponmaxammo(id);
}
weaponClipSize(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponclipsize(id);
}
weaponDamage(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweapondamage(id);
}
weaponMeleeDamage(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponmeleedamage(id);
}
weaponFireTime(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponfiretime(id);
}
weaponMeleeTime(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponmeleetime(id);
}
weaponReloadTime(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponreloadtime(id);
}
weaponReloadEmptyTime(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponreloademptytime(id);
}
weaponHitLocMultiplier(weapon)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
return getweaponhitlocmultiplier(id);
}
setWepDamage(weapon, dmg, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepDamage(weapon, dmg);
return setweapondamage(id, dmg);
}
setWepMeleeDamage(weapon, dmg, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepMeleeDamage(weapon, dmg);
return setweaponmeleedamage(id, dmg);
}
setWepFireTime(weapon, time, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepFireTime(weapon, time);
return setweaponfiretime(id, time);
}
setWepMeleeTime(weapon, time, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepMeleeTime(weapon, time);
return setweaponmeleetime(id, time);
}
setWepReloadTime(weapon, time, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepReloadTime(weapon, time);
return setweaponreloadtime(id, time);
}
setWepReloadEmptyTime(weapon, time, isdefault)
{
id = getWeaponId(weapon);
if (id == -1)
return 0;
if (isDefined(isdefault) && isdefault)
updateWepReloadEmptyTime(weapon, time);
return setweaponreloademptytime(id, time);
}
Your weapons.gsc (To load weapon Mitch's weapon functions)
PHP Code:
// After all precaches
loadWeaponFunctions();
Your _quickmessages.gsc (To control bot with quick messages)
PHP Code:
doQuickMessage(soundalias, saytext)
{
//...........//
self maps\mp\gametypes\_companion::messageControl(saytext);
}
Note: g_antilag must be turned OFF in order to make bot properly shoot.