回复: 队伍平衡Ptahhotep's Team Balancer 1.7b8汉化版
/** Ptahhotep's Team Balancer (PTB)
* Version 1.7 BETA
*
* DATE: February 9, 2003
* AUTHOR: Ptahhotep (ptahhotep@planethalflife.com)
*
*PTB converted for AMX Mod
*/
#include <amxmod>
#include <amxmisc>
#define ACCESS_PTB ADMIN_RCON
// team ids
#define UNASSIGNED 0
#define TS 1
#define CTS 2
#define AUTO_TEAM 5
new PTB_VERSION[] = "1.7b (2)"
// team selection control
new bool:PTB_LIMITJOIN = true // set limits on team joining
new PTB_LIMITAFTER = 0 // number of rounds after which teams limiting begins
new PTB_LIMITMIN = 0 // number of minimum players on map for team limiting
new PTB_MAXSIZE = 10 // maximum team size per team
new PTB_MAXDIFF = 2 // maximum team size difference
new PTB_AUTOROUNDS = 3 // number of first rounds into match, which allow autojoin only
new PTB_WTJAUTO = 3 // wtj tries needed to become autojoined
new PTB_WTJKICK = 5 // wtj tries needed to become kicked
new bool:PTB_KICK = true // kick for wtj counts
new bool:PTB_SAVEWTJ = false // save wtjs to wtj.log
// team balancing actions
new bool:PTB_SWITCH = true // switch/transfer players
new PTB_SWITCHAFTER = 0 // number of rounds after which switching begins
new PTB_SWITCHMIN = 3 // number of minimum players on map for switching
new PTB_SWITCHFREQ = 1 // relative next possible switch round
new PTB_PLAYERFREQ = 3 // relative next possible switch round for player
new PTB_FORCESWITCH = 3 // number of tries after which PTB switches alive, if neccessary
new bool:PTB_DEADONLY = true // switch dead only
// messages
new bool:PTB_TELLWTJ = true // tell about wtj tries
new bool:PTB_ANNOUNCE = true // announce team status at beginning of round
new bool:PTB_SAYOK = true // announce team status, if teams are alright
new bool:PTB_TYPESAY = true // use typesay
// team strength limits
new PTB_MAXSTREAK = 2 // max. allowed team win streak
new PTB_MAXSCORE = 2 // max. allowed team score difference
new Float:PTB_MINRATING = 1.5 // minimum critical team rating
new Float:PTB_MAXRATING = 2.0 // maximum critical team rating
new Float:PTB_SUPERRATING = 3.0 // super critical team rating
new PTB_MAXINCIDENTS = 50 // maximum kills + deaths before the score is divided by PTB_SCALEDOWN
new PTB_SCALEDOWN = 2 // divisor for kills and deaths, when PTB_MAXINCIDENTS is reached
// sorted player indices are 0-based
new sortedTeams
new sortedValidTargets
new validTargetCounts
new teamKills
new teamDeaths
new teamScores
new winStreaks
new wtConditions
new winnerTeam
new loserTeam
new Float:ctKD
new Float:tKD
new Float:ctStrength
new Float:tStrength
new Float:ctRating
new Float:tRating
// player arrays are 1-based, there is no player 0
new bool:isBeingTransfered
new playerTeam
new lastRoundSwitched
new wtjCount
new teamCounts
new kills
new deaths
new roundCounter
new lastSwitchRound
new couldNotSwitchCounter
new lastTeamBalanceCheck
Float:fabs(Float:n)
return ( n < 0.0 ) ? -n : n
Float:fdivWorkaround(Float:nom, Float:denom){
if ( denom == 0.0) return nom
return fabs(nom / denom)
}
doTypesay(string[], duration, r, g, b) {
if (!PTB_TYPESAY) return
set_hudmessage(r, g, b, 0.05, 0.55, 0, 6.0, float(duration) , 0.5, 0.15, 2)
show_hudmessage(0, string )
}
say(string[]){
client_print(0,print_chat,string)
server_print(string)
}
bool:check_param_bool(param[])
return (equali(param, "on") || equal(param, "1")) ? true : false
Float:check_param_float(param[],Float:n){
new Float:a = floatstr(param)
if (a < n) a = n
return a
}
check_param_num(param[],n){
new a = strtonum(param)
if (a < n) a = n
return a
}
transferPlayer(id){
isBeingTransfered = true
engclient_cmd(id,"chooseteam")
engclient_cmd(id,"menuselect",(playerTeam==TS) ? "2" : "1")
engclient_cmd(id,"menuselect","5")
client_cmd(id,"slot1")
}
actAtEndOfRound(){
if (!PTB_SWITCH) return
// skip switching for the first few rounds
if (roundCounter <= PTB_SWITCHAFTER) return
// honor switch frequency setting
if (roundCounter - lastSwitchRound < PTB_SWITCHFREQ) return
// skip switching for a small number of players
if (get_playersnum() < PTB_SWITCHMIN) return
say("PTB: Round ended, checking teams.")
checkTeamBalance()
if (winnerTeam) {
sortTeam(CTS)
sortTeam(TS)
if (teamCounts <= teamCounts)
doSwitch()
else if (teamCounts < teamCounts)
doTransfer()
}
}
createValidTargets(theTeam, bool:deadonly) {
new n = 0
for (new i = 0; i < teamCounts; ++i) {
// Dead only condition
if ( deadonly && is_user_alive(sortedTeams) ) continue
// Already switched or in PTB_PLAYERFREQ time condition
if ((lastRoundSwitched] == roundCounter) ||
(roundCounter - lastRoundSwitched] < PTB_PLAYERFREQ)) continue
sortedValidTargets = sortedTeams
}
validTargetCounts = n
}
sortTeam(theTeam) {
// create list of players
new n = 0, a = get_maxplayers()
for (new i = 1; i <= a; ++i) {
// Get only members of specified team
if (playerTeam != theTeam) continue
sortedTeams = i
}
// do a selection sort
new swap, count = n
for (new i = count-1; i > 0; --i){
for (new k = i-1; k >= 0; --k){
// compare players (kills better then other or if equal then with less deaths)
if ( (kills]<kills])
|| ( (kills]==kills]) &&
(deaths]>deaths]))) {
// swap
swap = sortedTeams
sortedTeams = sortedTeams
sortedTeams = swap
}
}
}
}
Float:score(team, toBeAdded=0, toBeRemoved=0){
new Float:sumKD = 0.0
new a = get_maxplayers()
for (new i = 1; i <= a; ++i) {
if ( (playerTeam!=team&&i!=toBeAdded) || (i==toBeRemoved) )
continue
sumKD += fdivWorkaround(float(kills), float(deaths))
}
new Float:strength = float(teamCounts)
if (sumKD) strength *= sumKD
return strength
}
doSwitch() {
new text
//displayStatistics(0,true)
// don't switch, if at least one team is empty
if ( teamCounts == 0 || teamCounts == 0 ) {
copy(text,255, "PTB: Can't switch players, need players in each team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
// don't switch, if winner is alone (RULER!!!)
if (teamCounts == 1) {
copy(text,255, "PTB: Won't switch players, best player makes the winning team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
// don't switch, if both teams are full
if (teamCounts >= PTB_MAXSIZE && teamCounts >= PTB_MAXSIZE) {
copy(text,255, "PTB: Can't switch players, both teams are full.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
if (!PTB_DEADONLY || couldNotSwitchCounter > PTB_FORCESWITCH) {
// choose from random top or bottom x
createValidTargets(winnerTeam, false)
createValidTargets(loserTeam, false)
if (validTargetCounts == 0 || validTargetCounts == 0) {
++couldNotSwitchCounter
copy(text,255, "PTB: Can't switch players, need valid target in each team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
}
else {
//say("switch dead")
createValidTargets(winnerTeam, true)
createValidTargets(loserTeam, true)
if (validTargetCounts == 0 || validTargetCounts == 0) {
if (++couldNotSwitchCounter > PTB_FORCESWITCH) {
say("PTB: Couldn't switch dead, switching alive.")
doSwitch()
return
}
copy(text, 255,"PTB: Can't switch players, need valid target in each team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
}
// Now search through the possible 1 to 1 swaps to equalize the strength as much as possible
new Float:closestScore = fabs(score(winnerTeam) - score(loserTeam))
new Float:myScore, toLoser, toWinner
new winner = 0
new loser = 0
for (new w = 0; w < validTargetCounts; ++w) {
toLoser = sortedValidTargets
for (new l = 0; l < validTargetCounts; ++l) {
toWinner = sortedValidTargets
myScore = fabs(score(winnerTeam, toWinner, toLoser) - score(loserTeam, toLoser, toWinner))
if (myScore < closestScore) {
closestScore = myScore
winner = toLoser
loser = toWinner
}
}
}
if (winner == 0 && loser == 0) {
copy(text, 255,"PTB: No switch would improve team balancing.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
couldNotSwitchCounter = 0
lastSwitchRound = roundCounter
new winnerName, loserName
get_user_name(winner,winnerName,31)
get_user_name(loser,loserName,31)
// if one team is full, first move the the player from the full team ...
if (teamCounts >= PTB_MAXSIZE){
transferPlayer(winner)
transferPlayer(loser)
}
else {
transferPlayer(loser)
transferPlayer(winner)
}
format(text,255,"PTB: Switching %s with %s.",winnerName,loserName)
doTypesay(text, 5, 0, 255, 0)
say(text)
}
doTransfer() {
//displayStatistics(0,true)
new text
if (teamCounts == 0) {
copy(text,255, "PTB: Can't switch players, need players in each team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
if (teamCounts >= PTB_MAXSIZE) {
copy(text,255, "PTB: Can't transfer player, losing team is full.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
if (!PTB_DEADONLY || couldNotSwitchCounter > PTB_FORCESWITCH) {
createValidTargets(winnerTeam, false)
if (validTargetCounts == 0) {
copy(text,255, "PTB: Can't transfer player, no valid target in winning team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
++couldNotSwitchCounter
return
}
}
else {
//say("switch dead")
createValidTargets(winnerTeam, true)
if (validTargetCounts == 0) {
if (++couldNotSwitchCounter > PTB_FORCESWITCH) {
say("PTB: Couldn't transfer dead, transferring alive.")
doTransfer()
return
}
copy(text,255, "PTB: Can't transfer player, no valid target in winning team.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
}
new Float:closestScore = fabs(score(winnerTeam) - score(loserTeam))
new Float:myScore, toLoser
new winner = 0
for (new w = 0; w < validTargetCounts; ++w) {
toLoser = sortedValidTargets
myScore = fabs(score(winnerTeam, 0, toLoser) - score(loserTeam, toLoser, 0))
if (myScore < closestScore) {
closestScore = myScore
winner = toLoser
}
}
if (winner == 0) {
copy(text, 255,"PTB: No transfer would improve team balancing.")
doTypesay(text, 5, 0, 255, 0)
say(text)
return
}
couldNotSwitchCounter = 0
new winnerName
get_user_name(winner,winnerName,31)
transferPlayer(winner)
format(text,255,"PTB: Transfering %s to the %s",winnerName, (winnerTeam == CTS) ? "Ts" : "CTs")
doTypesay(text, 5, 0, 255, 0)
say(text)
}
checkTeamBalance() {
get_time("%m/%d/%Y - %H:%M:%S",lastTeamBalanceCheck,31 )
calcTeamScores()
ctStrength = score(CTS)
tStrength = score(TS)
ctRating = fdivWorkaround(ctStrength, tStrength)
tRating = fdivWorkaround(tStrength, ctStrength)
wtConditions = 0
wtConditions = 0
// compare scores for unequal rating scores
if (teamScores - teamScores > PTB_MAXSCORE && tRating >= PTB_MINRATING)
wtConditions++
if (teamScores - teamScores > PTB_MAXSCORE && ctRating >= PTB_MINRATING)
wtConditions++
// check streaks for unequal rating scores
if (winStreaks > PTB_MAXSTREAK && tRating >= PTB_MINRATING)
wtConditions++
if (winStreaks > PTB_MAXSTREAK && ctRating >= PTB_MINRATING)
wtConditions++
// check ratings
if (tRating >= PTB_MAXRATING)
wtConditions++
if (ctRating >= PTB_MAXRATING)
wtConditions++
// check ratings
if (tRating >= PTB_SUPERRATING)
wtConditions++
if (ctRating >= PTB_SUPERRATING)
wtConditions++
// check team sizes for unequal ratings
if (teamCounts > teamCounts && tRating >= PTB_MINRATING)
wtConditions++
if (teamCounts > teamCounts && ctRating >= PTB_MINRATING)
wtConditions++
// check conditions
if (wtConditions >= 2) {
winnerTeam = TS
loserTeam = CTS
}
else if (wtConditions >= 2) {
winnerTeam = CTS
loserTeam = TS
}
else {
winnerTeam = 0
loserTeam = 0
}
}
manageWtjFile(id) {
if (!PTB_SAVEWTJ) return
//say("Trying to write wtj.log ....")
if (wtjCount < 4) return
//say("wtj.log should be written to now ....")
new text, time, mapname, name, authid
get_time("%m/%d/%Y - %H:%M:%S",time,31 )
get_mapname(mapname,31)
get_user_name(id,name,31)
get_user_authid(id,authid,31)
format(text, 255, "%s %s <%s> %s", time, name, authid, mapname)
write_file("addons/amx/wtj.log", text)
}
public menuselect(id,key) {
// don't care where player joins
if (!PTB_LIMITJOIN) return PLUGIN_CONTINUE
// players is transfered so don't care with rest
if (isBeingTransfered) {
//say("TRANSFER")
isBeingTransfered = false
return PLUGIN_CONTINUE
}
//say("NO TRANSFER")
// skip limiting for a few rounds into the map
if (PTB_LIMITAFTER && roundCounter <= PTB_LIMITAFTER) return PLUGIN_CONTINUE
// skip limiting for a small number of players
if (get_playersnum() < PTB_LIMITMIN) return PLUGIN_CONTINUE
new iNewTeam = key + 1 // which key has been pressed
new iOldTeam = playerTeam
// disallow free team choices in the first rounds of a map
if (PTB_AUTOROUNDS&&(iOldTeam==UNASSIGNED)&&roundCounter<=PTB_AUTOROUNDS)
iNewTeam = AUTO_TEAM
// prevent unwanted rejoining of the same team ...
if (iNewTeam == iOldTeam) {
//say("Preventing rejoining of the same team.")
client_print(id,print_chat,"PTB: Joining to the same team is not allowed...")
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
checkTeamBalance()
//displayStatistics(0,true)
// Player for sure was in CT or T team and now is joining to the opposite team
if ((iNewTeam==CTS&&iOldTeam==TS)||(iNewTeam==TS&&iOldTeam==CTS)){
// If someone is in new team and old team weren't full
// and the winning team is a destination team or in
// new team is more players than in old then treat it as wtj
if ( teamCounts&&(teamCounts<PTB_MAXSIZE)&&
((iNewTeam==winnerTeam)||(teamCounts>=teamCounts)) ) {
// player is wtjing
new text,name
get_user_name(id,name,31)
// Kick wtj player if reached set limit
if (++wtjCount >= PTB_WTJKICK && PTB_KICK) {
format(text, 255, "PTB: Kicking %s for a (WTJ count: %d).", name, wtjCount)
doTypesay(text, 5, 0, 255, 0)
say(text)
server_cmd("kick #%d",get_user_userid(id))
return PLUGIN_HANDLED
}
// Announce about WTJ
if (PTB_TELLWTJ) {
if (iNewTeam == CTS) {
format(text, 255, "PTB: The CTs are strong enough, %s (WTJ count: %d).", name, wtjCount)
doTypesay(text, 5, 0, 50, 255)
}
else {
format(text, 255, "PTB: The Ts are strong enough, %s (WTJ count: %d).", name, wtjCount)
doTypesay(text, 5, 255, 50, 0)
}
say(text)
}
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
// check for maximum team size
if (teamCounts >= PTB_MAXSIZE) {
client_print(id,print_chat,"PTB: Maximum team size prohibits team change.")
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
// check team size difference limits
if ( teamCounts+1-teamCounts >= PTB_MAXDIFF ) {
client_print(id,print_chat,"PTB: Maximum team size difference prohibits team change.")
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE // OK! He can join to the oppsoite team!!!
}
// Player is choosing his team for the first time!
if (iNewTeam==CTS||iNewTeam == TS){
// Get opposite team
new opposingTeam = (iNewTeam==CTS)? TS : CTS
// Players is joinging to one team but the opposite is not full
// and his team is bettter then opposite or has more players
if ( teamCounts && teamCounts<PTB_MAXSIZE &&
(iNewTeam==winnerTeam||(!winnerTeam&&teamCounts>teamCounts))) {
new text,name
get_user_name(id,name,31)
if (++wtjCount >= PTB_WTJKICK && PTB_KICK) {
format(text, 255, "PTB: Kicking %s for a WTJ count of %d).", name, wtjCount)
doTypesay(text, 5, 0, 255, 0)
say(text)
server_cmd("kick #%d", get_user_userid(id))
return PLUGIN_HANDLED
}
if (iNewTeam==CTS) {
if (wtjCount>=PTB_WTJAUTO) {
manageWtjFile(id)
format(text, 255, "PTB: Forcing %s to the Ts (WTJ count: %d).", name, wtjCount)
engclient_cmd(id,"menuselect","1")
doTypesay(text, 5, 255, 50, 0)
say(text)
}
else if (PTB_TELLWTJ) {
format(text, 255, "PTB: The CTs are strong enough, %s (WTJ count: %d).", name, wtjCount)
doTypesay(text, 5, 0, 50, 255)
say(text)
engclient_cmd(id,"chooseteam") // display menu again
}
}
else {
if (wtjCount>=PTB_WTJAUTO) {
manageWtjFile(id)
format(text, 255, "PTB: Forcing %s to the CTs (WTJ count: %d).", name, wtjCount)
engclient_cmd(id,"menuselect","2")
doTypesay(text, 5, 0, 50, 255)
say(text)
}
else if (PTB_TELLWTJ) {
format(text, 255, "PTB: The Ts are strong enough, %s (WTJ count: %d).", name, wtjCount)
doTypesay(text, 5, 255, 50, 0)
say(text)
engclient_cmd(id,"chooseteam") // display menu again
}
}
return PLUGIN_HANDLED
}
// check for maximum team size
if (teamCounts >= PTB_MAXSIZE) {
client_print(id,print_chat,"PTB: Maximum team size prohibits team join.")
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
// check team size difference limits
if ( teamCounts-teamCounts >= PTB_MAXDIFF) {
client_print(id,print_chat,"PTB: Maximum team size difference prohibits team join.")
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE // OK! He can join to the oppsoite team!!!
}
// He is choosing the AUTO-SELECT but he was already in game (He wants to play fair!)
if (iNewTeam==AUTO_TEAM&&(iOldTeam==CTS||iOldTeam==TS)) {
//say("Changing team automatically.")
new opposingTeam = (iOldTeam==CTS) ? TS : CTS
if (teamCounts && ( (teamCounts>=PTB_MAXSIZE)
|| (iOldTeam==loserTeam) || (!loserTeam&&teamCounts<=teamCounts)
|| (teamCounts+1-teamCounts>=PTB_MAXDIFF)) ) {
client_print(id,print_chat,"PTB: You have better stay in your current team...")
return PLUGIN_HANDLED
}
client_print(id,print_chat,"PTB: You have been auto-assigned...")
engclient_cmd(id,"menuselect",(opposingTeam==CTS) ? "2" : "1")
return PLUGIN_HANDLED
}
// He is choosing the team for the first time with AUTO-SELECT (What a nice kid!)
if (iNewTeam==AUTO_TEAM) {
/* this is the "always smaller team" version
if (teamCounts < teamCounts || teamCounts >= PTB_MAXSIZE) iNewTeam = CTS
else if (teamCounts < teamCounts || teamCounts >= PTB_MAXSIZE) iNewTeam = TS
// both teams have same size ...
else if (winnerTeam && teamCounts<PTB_MAXSIZE) iNewTeam = loserTeam
else if (teamCounts >= PTB_MAXSIZE) iNewTeam = CTS
else if (teamCounts >= PTB_MAXSIZE) iNewTeam = TS
else iNewTeam = (random_num(0,100) < 50) ? CTS : TS
*/
// this version prefers the losing team (but still honors PTB_MAXDIFF)
if (teamCounts >= PTB_MAXSIZE) iNewTeam = TS
else if (teamCounts >= PTB_MAXSIZE) iNewTeam = CTS
else if (teamCounts-teamCounts >= PTB_MAXDIFF) iNewTeam = TS
else if (teamCounts-teamCounts >= PTB_MAXDIFF) iNewTeam = CTS
else if (winnerTeam) iNewTeam = loserTeam
else if (teamCounts<teamCounts) iNewTeam = CTS
else if (teamCounts<teamCounts) iNewTeam = TS
// both teams have same size ...
else iNewTeam = (random_num(0,100) < 50) ? CTS : TS
// check for maximum team size
if (teamCounts>=PTB_MAXSIZE) {
client_print(id,print_chat,"PTB: Maximum team size prohibits team join.") // ??? - only a spectator???
engclient_cmd(id,"chooseteam") // display menu again
return PLUGIN_HANDLED
}
client_print(id,print_chat,"PTB: You have been auto-assigned...")
engclient_cmd(id,"menuselect",(iNewTeam == CTS) ? "2" : "1")
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
public team_score(){
new arg
read_data(1,arg,1)
teamScores[ ( arg == 'T' ) ? TS : CTS ] = read_data(2)
}
public win_streaks(param[]){
new winner = param
new looser = param
if (winStreaks < 0) {
winStreaks = 1
winStreaks = -1
}
else {
winStreaks++
winStreaks--
}
actAtEndOfRound()
}
public round_end(){
new param
read_data(2,param,8)
if (param=='c') {//%!MRAD_ctwin
param = CTS
param = TS
}
else if (param=='t') {//%!MRAD_terwin
param = TS
param = CTS
}
else
return // %!MRAD_rounddraw (both teams have left the game)
set_task(1.5,"win_streaks",0,param,2)
}
public new_round() {
if ( get_cvar_num("mp_roundtime") * 60 != read_data(1) )
return
++roundCounter
announceStatus()
}
// Happen only at team select (also auto-join)
public team_join() {
new arg
read_data(3,arg,31)
lastRoundSwitched[ get_user_index(arg) ] = roundCounter
}
// Can happen at begin of round or team select
public team_assign() {
new arg, team
new i = read_data(1)
read_data(2,arg,1)
if ( arg == 'C')
team = CTS
else if ( arg == 'T' )
team = TS
else
team = UNASSIGNED
teamCounts]-- // Unregister from old team
teamCounts++ // Increase ammount in new team
playerTeam = team // Assign to new
}
public game_restart(){
roundCounter = 0
lastSwitchRound = 0
couldNotSwitchCounter = 0
teamKills = teamKills = teamKills = 0
teamDeaths = teamDeaths = teamDeaths = 0
teamScores = teamScores = teamScores = 0
winStreaks = winStreaks = winStreaks = 0
wtConditions = wtConditions = wtConditions = 0
validTargetCounts = validTargetCounts = validTargetCounts = 0
new a = get_maxplayers()
for (new i = 1; i <= a; ++i){
kills = 0
deaths = 0
wtjCount = 0
lastRoundSwitched = -999
}
}
public death_msg(){
new iWinner = read_data(1)
new iLoser = read_data(2)
if ( iWinner < 1 || iWinner > 32 || iLoser < 1 || iLoser > 32 )
return
if ( playerTeam == playerTeam )
return // no TKS!!!
kills++
deaths++
if (PTB_SCALEDOWN <= 1) return
if (kills + deaths >= PTB_MAXINCIDENTS) {
kills /= PTB_SCALEDOWN
deaths /= PTB_SCALEDOWN
}
if (kills + deaths >= PTB_MAXINCIDENTS) {
kills /= PTB_SCALEDOWN
deaths /= PTB_SCALEDOWN
}
}
calcTeamScores() {
teamDeaths = 0
teamDeaths = 0
teamDeaths = 0
teamKills = 0
teamKills = 0
teamKills = 0
new team, a = get_maxplayers()
for (new i = 1; i <= a; ++i) {
team = playerTeam
teamKills += kills
teamDeaths += deaths
}
ctKD = fdivWorkaround(float(teamKills), float(teamDeaths))
tKD = fdivWorkaround(float(teamKills), float(teamDeaths))
}
announceStatus() {
if (!PTB_ANNOUNCE) return
checkTeamBalance()
new text
if (winnerTeam == TS) {
format(text, 255, "PTB: The COUNTER-TERRORIST team could use some support.")
doTypesay(text, 5, 0, 50, 255)
say("PTB: The COUNTER-TERRORIST team could use some support.")
}
else if (winnerTeam == CTS) {
format(text, 255, "PTB: The TERRORIST team could use some support.")
doTypesay(text, 5, 255, 50, 0)
say("PTB: The TERRORIST team could use some support.")
}
else if (wtConditions > wtConditions) {
format(text, 255, "PTB: Observing TERRORIST team advantage.")
doTypesay(text, 5, 255, 50, 0)
say("PTB: Observing TERRORIST team advantage.")
}
else if (wtConditions > wtConditions) {
format(text, 255, "PTB: Observing COUNTER-TERRORIST team advantage.")
doTypesay(text, 5, 0, 50, 255)
say("PTB: Observing COUNTER-TERRORIST team advantage.")
}
else if (PTB_SAYOK) {
format(text, 255, "PTB: Teams look fine, no action required.")
doTypesay(text, 5, 200, 100, 0)
say("PTB: Teams look fine, no action required.")
}
}
public admin_ptb(id,level,cid) {
if (!cmd_access(id,level,cid,1))
return PLUGIN_HANDLED
new cmd, arg, lastcmd
if ( read_argv(1,cmd,31) == 0 ) { // no command - plain amx_ptb
console_print(id,"PTB: Ptahhotep's Team Balancer %s", PTB_VERSION)
console_print(id,"PTB: (ptahhotep@planethalflife.com)")
checkTeamBalance()
displayStatistics(id)
return PLUGIN_HANDLED
}
if (equali(cmd, "on") || equal(cmd, "1")) {
PTB_LIMITJOIN = true
PTB_SWITCH = true
PTB_ANNOUNCE = true
console_print(id,"PTB: Enabled all PTB actions.")
return PLUGIN_HANDLED
}
if (equali(cmd, "off") || equal(cmd, "0")) {
PTB_SWITCH = false
PTB_ANNOUNCE = false
PTB_LIMITJOIN = false
console_print(id,"PTB: Disabled all PTB actions.")
return PLUGIN_HANDLED
}
if (equal(cmd, "list") || equal(cmd, "help")) {
console_print(id,"PTB: Available Commands:")
console_print(id,"PTB: Team Join Control: ^"limitjoin^", ^"limitafter^", ^"limitmin^", ^"maxsize^", ^"autorounds^",")
console_print(id,"PTB: ^"maxdiff^", ^"wtjauto^", ^"wtjkick^", ^"kick^", ^"savewtj^"")
console_print(id,"PTB: Team Balancing Actions: ^"switch^", ^"switchafter^", ^"switchmin^", ^"switchfreq^", ^"playerfreq^",")
console_print(id,"PTB: ^"forceswitch^", ^"deadonly^"")
console_print(id,"PTB: Team Strength Limits: ^"maxstreak^", ^"maxscore^", ^"minrating^", ^"maxrating^", ^"superrating^"")
console_print(id,"PTB: Messages: ^"tellwtj^", ^"announce^", ^"sayok^", ^"typesay^"")
console_print(id,"PTB: Misc: ^"^", ^"status^", ^"list^", ^"help^", ^"on^", ^"off^", ^"save^", ^"load^"")
console_print(id,"PTB: To view all PTB settings, type ^"amx_ptb status^".")
console_print(id,"PTB: To view or change a single PTB setting, type ^"amx_ptb <setting> <on|off|value>^".")
console_print(id,"PTB: For PTB statistics, simply type ^"amx_ptb^".")
return PLUGIN_HANDLED
}
new arglen = read_argv(2,arg,31)
new status = equal(cmd, "status")
// team selection control
if ( status ) console_print(id,"PTB: ---------- Team Selection Control ----------")
// PTB_LIMITJOIN
if ( (lastcmd = equal(cmd, "limitjoin")) && arglen ) PTB_LIMITJOIN = check_param_bool(arg)
if ( status ||lastcmd ) console_print(id,"PTB: (limitjoin) WTJ prevention is %s.", PTB_LIMITJOIN ? "ON" : "OFF")
// PTB_LIMITAFTER
if ( (lastcmd = equal(cmd, "limitafter")) && arglen ) PTB_LIMITAFTER = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (limitafter) Team limiting starts after %d round(s).", PTB_LIMITAFTER)
// PTB_LIMITMIN
if ( (lastcmd = equal(cmd, "limitmin")) && arglen ) PTB_LIMITMIN = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (limitmin) Team limiting needs at least %d player(s).", PTB_LIMITMIN)
// PTB_MAXSIZE
if ( (lastcmd = equal(cmd, "maxsize")) && arglen ) PTB_MAXSIZE = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (maxsize) Maximum team size is %d player(s).", PTB_MAXSIZE)
// PTB_MAXDIFF
if ( (lastcmd = equal(cmd, "maxdiff")) && arglen ) PTB_MAXDIFF = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (maxdiff) Maximum team size difference is %d.", PTB_MAXDIFF)
// PTB_AUTOROUNDS
if ( (lastcmd = equal(cmd, "autorounds")) && arglen ) PTB_AUTOROUNDS = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id, "PTB: (autorounds) First %d rounds no free team choice.", PTB_AUTOROUNDS)
// PTB_WTJAUTO
if ( (lastcmd = equal(cmd, "wtjauto")) && arglen ) PTB_WTJAUTO = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (wtjauto) Auto-joining WTJ after %d tr(y/ies).", PTB_WTJAUTO)
// PTB_WTJKICK
if ( (lastcmd = equal(cmd, "wtjkick")) && arglen ) PTB_WTJKICK = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (wtjauto) Auto-kicking WTJ after %d tr(y/ies).", PTB_WTJKICK)
// PTB_KICK
if ( (lastcmd = equal(cmd, "kick")) && arglen ) PTB_KICK = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (kick) WTJ kicking is %s.", PTB_KICK ? "ON" : "OFF" )
// PTB_SAVEWTJ
if ((lastcmd = equal(cmd, "savewtj")) && arglen ) PTB_SAVEWTJ = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (savewtj) Saving to wtj.log is %s.", PTB_SAVEWTJ ? "ON" : "OFF");
// team balancing actions
if ( status ) console_print(id,"PTB: ---------- Team Balancing Actions ----------")
// PTB_SWITCH
if ( (lastcmd = equal(cmd, "switch")) && arglen ) PTB_SWITCH = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (switch) Team switching is %s.", PTB_SWITCH ? "ON" : "OFF")
// PTB_SWITCHAFTER
if ( (lastcmd = equal(cmd, "switchafter")) && arglen ) PTB_SWITCHAFTER = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (switchafter) Switching starts after %d round(s).", PTB_SWITCHAFTER)
// PTB_SWITCHMIN
if ( (lastcmd = equal(cmd, "switchmin")) && arglen ) PTB_SWITCHMIN = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (switchmin) Switching needs at least %d player(s).", PTB_SWITCHMIN)
// PTB_PLAYERFREQ
if ( (lastcmd = equal(cmd, "playerfreq")) && arglen ) PTB_PLAYERFREQ = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (playerfreq) Individual players are switched every %d round(s) at maximum.", PTB_PLAYERFREQ)
// PTB_SWITCHFREQ
if ((lastcmd = equal(cmd, "switchfreq")) && arglen ) PTB_SWITCHFREQ = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (switchfreq) Switch occurs every %d round(s) at maximum.", PTB_SWITCHFREQ)
// PTB_FORCESWITCH
if ( (lastcmd = equal(cmd, "forceswitch")) && arglen ) PTB_FORCESWITCH = check_param_num(arg,0)
if ( status || lastcmd ) console_print(id,"PTB: (forceswitch) Forcing switch after %d unsuccessful switch(es).", PTB_FORCESWITCH)
// PTB_DEADONLY
if ( (lastcmd =equal(cmd, "deadonly")) && arglen ) PTB_DEADONLY = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (deadonly) Switching dead only is %s.",PTB_DEADONLY ? "ON" : "OFF" )
// messages
if ( status ) console_print(id,"PTB: ---------- Messages ----------")
// PTB_TELLWTJ
if ( (lastcmd =equal(cmd, "tellwtj")) && arglen ) PTB_TELLWTJ = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (tellwtj) Telling about WTJ tries is %s.",PTB_TELLWTJ ? "ON" : "OFF")
// PTB_ANNOUNCE
if ( (lastcmd = equal(cmd, "announce")) && arglen ) PTB_ANNOUNCE = check_param_bool(arg)
if ( status || lastcmd )console_print(id,"PTB: (announce) Announcements are %s.",PTB_ANNOUNCE ? "ON" : "OFF")
// PTB_SAYOK
if ( (lastcmd = equal(cmd, "sayok")) && arglen ) PTB_SAYOK = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (sayok) ^"OK^" announcements are %s.",PTB_SAYOK ? "ON" : "OFF")
// PTB_TYPESAY
if ( (lastcmd = equal(cmd, "typesay")) && arglen ) PTB_TYPESAY = check_param_bool(arg)
if ( status || lastcmd ) console_print(id,"PTB: (typesay) typesay usage is %s.",PTB_TYPESAY ? "ON" : "OFF")
// team strength limits
if ( status ) console_print(id,"PTB: ---------- Team Strength Limits ----------")
// PTB_MAXSTREAK
if ( (lastcmd = equal(cmd, "maxstreak")) && arglen ) PTB_MAXSTREAK = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (maxstreak) Maximum accepted win streak is %d.", PTB_MAXSTREAK)
// PTB_MAXSCORE
if ( (lastcmd = equal(cmd, "maxscore")) && arglen ) PTB_MAXSCORE = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (maxscore) Maximum accepted team score difference is %d.", PTB_MAXSCORE)
// PTB_MINRATING
if ( (lastcmd = equal(cmd, "minrating")) && arglen ) PTB_MINRATING = check_param_float(arg,1.0)
if ( status || lastcmd ) console_print(id,"PTB: (minrating) Minimum critical strength rating is %.2f.",PTB_MINRATING)
// PTB_MAXRATING
if ( (lastcmd = equal(cmd, "maxrating")) && arglen ) PTB_MAXRATING = check_param_float(arg,1.0)
if ( status || lastcmd ) console_print(id,"PTB: (maxrating) Maximum critical strength rating is %.2f.",PTB_MAXRATING)
// PTB_SUPERRATING
if ( (lastcmd = equal(cmd, "superrating")) && arglen ) PTB_SUPERRATING = check_param_float(arg,1.0)
if ( status || lastcmd ) console_print(id,"PTB: (superrating) Super critical strength rating is %.2f.",PTB_SUPERRATING)
// PTB_MAXINCIDENTS
if ( (lastcmd = equal(cmd, "maxincidents")) && arglen ) PTB_MAXINCIDENTS = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (maxincidents) Maximum incidents before internal player score scale down is %d.", PTB_MAXINCIDENTS)
// PTB_SCALEDOWN
if ( (lastcmd =equal(cmd, "scaledown")) && arglen ) PTB_SCALEDOWN = check_param_num(arg,1)
if ( status || lastcmd ) console_print(id,"PTB: (scaledown) Integer scale down factor for player scores is %d.", PTB_SCALEDOWN)
// misc
if ( status ) {
console_print(id,"PTB: ---------- Misc ----------")
console_print(id,"PTB: To enable or disable PTB, type ^"admin_ptb <on|1|off|0>^".")
console_print(id,"PTB: To view or change a single PTB setting, type ^"amx_ptb <setting> <on|off|value>^".")
console_print(id,"PTB: To view a brief overview of PTB commands, type ^"amx_ptb help^" or ^"amx_ptb list^".")
console_print(id,"PTB: For PTB statistics, simply type ^"amx_ptb^".")
}
return PLUGIN_HANDLED
}
stock displayStatistics(id,bool:toLog = false) {
//say("displayStatistics")
new text
// time
format(text, 255, "PTB: Statistics generated at: %s", lastTeamBalanceCheck)
if (toLog) log_message(text)
console_print(id,text)
// connected players
format(text, 255, "PTB: Connected players: %d", get_playersnum())
if (toLog) log_message(text)
console_print(id,text)
// team sizes
format(text, 255, "PTB: Team sizes: CTs %d, Ts %d", teamCounts, teamCounts)
if (toLog) log_message(text)
console_print(id,text)
// team scores
format(text, 255, "PTB: Team scores: CTs %d, Ts %d", teamScores, teamScores)
if (toLog) log_message(text)
console_print(id,text)
// Kills:Deaths
format(text, 255, "PTB: Team kills:deaths: CTs %d:%d, Ts %d:%d", teamKills, teamDeaths, teamKills, teamDeaths)
if (toLog) log_message(text)
console_print(id,text)
// Kills/Deaths
format(text, 255, "PTB: Team kills/deaths: CTs %.2f, Ts %.2f", ctKD , tKD )
if (toLog) log_message(text)
console_print(id,text)
// strength
format(text, 255, "PTB: Team strengths: CTs %.2f, Ts %.2f",ctStrength , tStrength )
if (toLog) log_message(text)
console_print(id,text)
// rating
format(text, 255, "PTB: Team ratings: CTs %.2f, Ts %.2f",ctRating,tRating )
if (toLog) log_message(text)
console_print(id,text)
// won rounds
if (winStreaks > 0) {
format(text, 255, "PTB: Last %d round(s) won by CTs.", winStreaks)
if (toLog) log_message(text)
console_print(id,text)
}
else if (winStreaks > 0) {
format(text, 255, "PTB: Last %d round(s) won by Ts.", winStreaks)
if (toLog) log_message(text)
console_print(id,text)
}
// winning team
switch(winnerTeam){
case CTS: format(text, 255, "PTB: The CTs are the winning team.")
case TS: format(text, 255, "PTB: The Ts are the winning team.")
default: format(text, 255, "PTB: Teams are balanced.")
}
if (toLog) log_message(text)
console_print(id,text)
format(text, 255, "PTB: These statistics might be already outdated.")
if (toLog) log_message(text)
console_print(id,text)
format(text, 255, "PTB: To view a brief overview of PTB commands, type ^"amx_ptb help^" or ^"amx_ptb list^".")
if (toLog) log_message(text)
console_print(id,text)
format(text, 255, "PTB: To view all PTB settings, type ^"amx_ptb status^".")
if (toLog) log_message(text)
console_print(id,text)
}
public client_connect(id){
kills = 0
deaths = 0
isBeingTransfered = false
playerTeam = UNASSIGNED
lastRoundSwitched = -999
wtjCount = 0
return PLUGIN_CONTINUE
}
public client_disconnect(id) {
kills = 0
deaths = 0
isBeingTransfered = false
playerTeam = UNASSIGNED
lastRoundSwitched = -999
wtjCount = 0
// redundant team size check
teamCounts = 0
teamCounts = 0
teamCounts = 0
new a = get_maxplayers()
for (new i = 1; i <= a; ++i)
++teamCounts]
return PLUGIN_CONTINUE
}
public plugin_init(){
register_plugin("Team Balancer",PTB_VERSION,"Ptahhotep")
register_cvar("amx_ptb_version",PTB_VERSION,FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_UNLOGGED|FCVAR_SPONLY)
register_menucmd(register_menuid("Team_Select"),(1<<0)|(1<<1)|(1<<4),"menuselect")
register_menucmd(-2,(1<<0)|(1<<1)|(1<<4),"menuselect") // VGUI menu
register_event("SendAudio","round_end","a","2=%!MRAD_terwin","2=%!MRAD_ctwin","2=%!MRAD_rounddraw") // Round End
register_event("TeamScore","team_score","a") // Team Score
register_event("RoundTime", "new_round", "bc") // Round Time
register_event("DeathMsg","death_msg","a") // Kill
register_event("TeamInfo","team_assign","a") // Team Assigment (also UNASSIGNED and SPECTATOR)
register_event("TextMsg","team_join","a","1=1","2&Game_join_te","2&Game_join_ct") // Team Joining
register_event("TextMsg","game_restart","a","1=4","2&#Game_C","2&#Game_w") // Game restart
register_concmd("amx_ptb","admin_ptb",ACCESS_PTB,"- displays PTB options")
server_cmd("exec addons/amx/ptb.cfg")
return PLUGIN_CONTINUE
}
麻烦问一下 :这些源代码从哪里改,就是不丢武器,不自杀改变组织 我改了一些,测试有点困难...呵呵 :p
回复: 队伍平衡Ptahhotep's Team Balancer 1.7b8汉化版
队伍平衡功能,原来就有,也许没楼主那么理想,在使用此插件的话功能过于重复。回复: 队伍平衡Ptahhotep's Team Balancer 1.7b8汉化版
是的,这个插件,有点损害人气,因为它为了队伍平衡,还得让人自杀,怎么样才能不自杀,然后换组了olol说要改插件源代码,可是怎么改呢 :burn: 不知道能不能实现把一个厉害的玩家自动平衡到另一方