#include common_scripts\utility; #include animscripts\utility; #include maps\_utility; #include animscripts\battlechatter_ai; init_battleChatter() { } player_init() { } shutdown_battleChatter() { anim.countryIDs = undefined; anim.eventTypeMinWait = undefined; anim.eventActionMinWait = undefined; anim.eventTypeMinWait = undefined; anim.eventPriority = undefined; anim.eventDuration = undefined; anim.chatCount = undefined; anim.moveOrigin = undefined; anim.areas = undefined; anim.locations = undefined; anim.landmarks = undefined; anim.usedIDs = undefined; anim.chatInitialized = false; level.battlechatter = undefined; for (i = 0; i < anim.squadCreateFuncs.size; i++) { if (anim.squadCreateStrings[i] != "::init_squadBattleChatter") { continue; } if (i != (anim.squadCreateFuncs.size - 1)) { anim.squadCreateFuncs[i] = anim.squadCreateFuncs[anim.squadCreateFuncs.size - 1]; anim.squadCreateStrings[i] = anim.squadCreateStrings[anim.squadCreateStrings.size - 1]; } anim.squadCreateFuncs[anim.squadCreateFuncs.size - 1] = undefined; anim.squadCreateStrings[anim.squadCreateStrings.size - 1] = undefined; } level notify ("battlechatter disabled"); anim notify ("battlechatter disabled"); } resetSayTime(name) { self.nextSayTimes[name] = GetTime() + 50; } resetSayTimes() { self.nextSayTime = GetTime(); resetSayTime("threat"); resetSayTime("order"); resetSayTime("reaction"); resetSayTime("response"); resetSayTime("inform"); resetSayTime("custom"); } init_squadBattleChatter() { } shutdown_squadBattleChatter() { squad = self; squad.maxSpeakers = undefined; squad.nextSayTime = undefined; squad.nextSayTimes = undefined; squad.nextTypeSayTimes = undefined; squad.isMemberSaying = undefined; for (i = 0; i < squad.memberAddFuncs.size; i++) { if (squad.memberAddStrings[i] != "::addToSystem") { continue; } if (i != (squad.memberAddFuncs.size - 1)) { squad.memberAddFuncs[i] = squad.memberAddFuncs[squad.memberAddFuncs.size - 1]; squad.memberAddStrings[i] = squad.memberAddStrings[squad.memberAddStrings.size - 1]; } squad.memberAddFuncs[squad.memberAddFuncs.size - 1] = undefined; squad.memberAddStrings[squad.memberAddStrings.size - 1] = undefined; } for (i = 0; i < squad.memberRemoveFuncs.size; i++) { if (squad.memberRemoveStrings[i] != "::removeFromSystem" ) { continue; } if (i != (squad.memberRemoveFuncs.size - 1)) { squad.memberRemoveFuncs[i] = squad.memberRemoveFuncs[squad.memberRemoveFuncs.size - 1]; squad.memberRemoveStrings[i] = squad.memberRemoveStrings[squad.memberRemoveStrings.size - 1]; } squad.memberRemoveFuncs[squad.memberRemoveFuncs.size - 1] = undefined; squad.memberRemoveStrings[squad.memberRemoveStrings.size - 1] = undefined; } for (i = 0; i < squad.squadUpdateFuncs.size; i++) { if (squad.squadUpdateStrings[i] != "::initContact") { continue; } if (i != (squad.squadUpdateFuncs.size - 1)) { squad.squadUpdateFuncs[i] = squad.squadUpdateFuncs[squad.squadUpdateFuncs.size - 1]; squad.squadUpdateStrings[i] = squad.squadUpdateStrings[squad.squadUpdateStrings.size - 1]; } squad.squadUpdateFuncs[squad.squadUpdateFuncs.size - 1] = undefined; squad.squadUpdateStrings[squad.squadUpdateStrings.size - 1] = undefined; } for (i = 0; i < anim.squadIndex.size; i++) { squad shutdownContact (anim.squadIndex[i].squadName); } squad.chatInitialized = false; } initContact (squadName) { if (!IsDefined (self.squadList[squadName].calledOut)) { self.squadList[squadName].calledOut = false; } if (!IsDefined (self.squadList[squadName].firstContact)) { self.squadList[squadName].firstContact = 2000000000; } if (!IsDefined (self.squadList[squadName].lastContact)) { self.squadList[squadName].lastContact = 0; } } shutdownContact (squadName) { self.squadList[squadName].calledOut = undefined; self.squadList[squadName].firstContact = undefined; self.squadList[squadName].lastContact = undefined; } bcsEnabled() { return anim.chatInitialized; } bcsDebugWaiter() { } enableBattleChatter() { } disableBattleChatter() { } playBattleChatter() { } samePhrase(a,b) { assert(IsDefined(a)); assert(IsDefined(b)); if(a.soundAliases.size != b.soundAliases.size) { return false; } for (i = 0; i < a.soundAliases.size; i++) { if(a.soundAliases[i] != b.soundAliases[i]) { return false; } } return true; } initHistory() { level.bcHistoryCount = 100; level.bcHistoryPhrases = []; level.bcHistoryTimes = []; level.bcHistoryIndex = 0; } addPhraseToHistory(phrase) { assert(IsDefined(level.bcHistoryIndex)); i = level.bcHistoryIndex; assert(IsDefined(i)); level.bcHistoryPhrases[i] = phrase; level.bcHistoryTimes[i] = GetTime(); assert(IsDefined(i)); level.bcHistoryIndex = (i+1)%level.bcHistoryCount; } isDupePhrase(phrase, threshold) { for (i=0; i self.squad.nextTypeSayTimes[strAction][strType]) { return (false); } return (true); } doTypeLimit(strAction, strType) { if (!IsDefined (anim.eventTypeMinWait[strAction][strType])) { return; } self.squad.nextTypeSayTimes[strAction][strType] = GetTime() + anim.eventTypeMinWait[strAction][strType]; } bcIsSniper() { if ( IsPlayer( self ) ) { return (false); } return animscripts\combat_utility::isSniperRifle( self.weapon ); } isExposed() { } isClaimedNodeCover() { node = self bcGetClaimedNode(); if (!IsDefined (node)) { return (false); } if ((node.type[0] == "C") && (node.type[1] == "o") && (node.type[2] == "v")) { return (true); } return (false); } isNodeCover() { node = self.node; if (!IsDefined (node)) { return (false); } if ((node.type[0] == "C") && (node.type[1] == "o") && (node.type[2] == "v")) { return (true); } return (false); } isOfficer() { fullRank = self getRank(); if (fullRank == "sergeant" || fullRank == "lieutenant" || fullRank == "captain" || fullRank == "sergeant") { return (true); } return (false); } bcGetClaimedNode() { if ( IsPlayer( self ) ) { node =self.node; } else { node = self GetClaimedNode(); } } getName() { name = undefined; if ( self.team == "axis" ) { name = self.name; } else if ( self.team == "allies" ) { name = self.name; } if(!IsDefined(name) && IsDefined(self.script_friendname)) { name = self.script_friendname; } if (!IsDefined (name) || self.voice == "british" ) { return ( undefined ); } tokens = strtok( name, " " ); if ( tokens.size == 1 ) { return tokens[0]; } if (tokens.size >= 2) { return tokens[1]; } return undefined; } getRank() { return self.airank; } getClosestSpeaker (strAction, strType, officerOnly) { speakers = self getSpeakers (strAction, strType, officerOnly); speaker = getClosest(self.origin, speakers); return (speaker); } getSpeakers (strAction, strType, officersOnly) { speakers = []; soldiers = GetAIArray( self.team ); if (IsDefined (officersOnly) && officersOnly) { officers = []; for (i = 0; i < soldiers.size; i++) { if (soldiers[i] isOfficer()) { officers[officers.size] = soldiers[i]; } } soldiers = officers; } for (i = 0; i < soldiers.size; i++) { if (soldiers[i] == self) { continue; } if (!soldiers[i] bcCanSay(strAction, strType)) { continue; } speakers[speakers.size] = soldiers[i]; } return (speakers); } getArea() { areas = anim.areas; for (i = 0; i < areas.size; i++) { if (self istouching (areas[i]) && IsDefined (areas[i].script_area)) { return (areas[i]); } } return (undefined); } getLocation() { locations = anim.locations; for (i = 0; i < locations.size; i++) { if (self istouching (locations[i]) && IsDefined (locations[i].script_location)) { return (locations[i]); } } return (undefined); } getLandmark() { landmarks = anim.landmarks; for (i = 0; i < landmarks.size; i++) { if (self istouching (landmarks[i]) && IsDefined (landmarks[i].script_landmark)) { return (landmarks[i]); } } return (undefined); } getDirectionCompass (vOrigin, vPoint) { angles = VectorToAngles (vPoint - vOrigin); angle = angles[1]; northYaw = getnorthyaw(); angle -= northYaw; if (angle < 0) { angle += 360; } else if (angle > 360) { angle -= 360; } if (angle < 22.5 || angle > 337.5) { direction = "north"; } else if (angle < 67.5) { direction = "northwest"; } else if (angle < 112.5) { direction = "west"; } else if (angle < 157.5) { direction = "southwest"; } else if (angle < 202.5) { direction = "south"; } else if (angle < 247.5) { direction = "southeast"; } else if (angle < 292.5) { direction = "east"; } else if (angle < 337.5) { direction = "northeast"; } else { direction = "impossible"; } return (direction); } getDirectionReferenceSide (vOrigin, vPoint, vReference) { anglesToReference = VectorToAngles (vReference - vOrigin); anglesToPoint = VectorToAngles (vPoint - vOrigin); angle = anglesToReference[1] - anglesToPoint[1]; angle += 360; angle = int (angle) % 360; if (angle > 180) { angle -= 360; } if (angle > 2 && angle < 45) { side = "right"; } else if (angle < -2 && angle > -45) { side = "left"; } else { if (distance (vOrigin, vPoint) < distance (vOrigin, vReference)) { side = "front"; } else { side = "rear"; } } return (side); } getDirectionFacingFlank (vOrigin, vPoint, vFacing) { anglesToFacing = VectorToAngles (vFacing); anglesToPoint = VectorToAngles (vPoint - vOrigin); angle = anglesToFacing[1] - anglesToPoint[1]; angle += 360; angle = int (angle) % 360; if (angle > 315 || angle < 45) { direction = "front"; } else if (angle < 135) { direction = "right"; } else if (angle < 225) { direction = "rear"; } else { direction = "left"; } return (direction); } getVectorRightAngle (vDir) { return (vDir[1], 0 - vDir[0], vDir[2]); } getVectorArrayAverage (avAngles) { vDominantDir = (0,0,0); for (i = 0; i < avAngles.size; i++) { vDominantDir += avAngles[i]; } return (vDominantDir[0] / avAngles.size, vDominantDir[1] / avAngles.size, vDominantDir[2] / avAngles.size); } addAlias(name) { if(soundExists(name)) { self.soundAliases[self.soundAliases.size] = name; return true; } else { println("ERROR BC cannot find alias "+name); return false; } } createChatPhrase() { chatPhrase = SpawnStruct(); chatPhrase.owner = self; chatPhrase.soundAliases = []; chatPhrase.master = false; return chatPhrase; } canSeePoint(origin) { forward = AnglesToForward(self.angles); return (vectordot(forward, origin - self.origin) > 0.766); } pointInFov(origin) { forward = AnglesToForward(self.angles); return (vectordot(forward, origin - self.origin) > 0.766); } resetNextSayTimes( team, action ) { } bcGetName() { name = undefined; if ( self.team == "axis" ) { name = self.name; } else if ( self.team == "allies" ) { name = self.name; } if(!IsDefined(name) && IsDefined(self.script_friendname)) { name = self.script_friendname; } if (!IsDefined (name) || self.voice == "british" ) { return ( undefined ); } tokens = strtok( name, " " ); if ( tokens.size == 1 ) { return tokens[0]; } if (tokens.size >= 2) { return tokens[1]; } return undefined; } bcCanSay (eventAction, eventType, priority, modifier) { assert(IsDefined(eventAction)); assert(IsDefined(eventType)); isGrenade = false; if( IsDefined(eventAction) && eventAction == "inform" && IsDefined(eventType) && eventType == "incoming" && IsDefined(modifier) && modifier == "grenade" ) { isGrenade = true; } if( IsDefined(eventAction) && eventAction == "inform" && IsDefined(eventType) && eventType == "attack" && IsDefined(modifier) && modifier == "grenade" ) { isGrenade = true; } if( IsDefined(eventAction) && eventAction == "threat" && IsDefined(eventType) && eventType == "bansai" ) { isGrenade = true; } if (IsPlayer(self)) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because i'm a player "+self.bcname); } return false; } if(IsDefined(self.isTalking) && self.isTalking) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because i'm talking "+self.bcname); } return false; } if(IsDefined(self.isSpeaking) && self.isSpeaking) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because i'm speaking "+self.bcname); } return false; } if( !IsDefined( self.nextSayTimes ) ) { msg = "AI at origin " + self.origin + ", classname " + self.classname; msg += ", doesn't have the self.nextSayTimes array set up, which the battlechatter system needs! "; msg += "This likely means that animscripts\battlechatter_ai::removeFromSystem() was run "; msg += "on this guy, but he hasn't been deleted. "; msg += "Did you accidentally notify \'death\' without deleting the AI?"; ASSERTMSG( msg ); return false; } if (!IsDefined( self.battlechatter ) || !self.battlechatter) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because of self.battlechatter "+self.bcname); } return false; } if(!isGrenade && IsDefined(level.NumberOfImportantPeopleTalking) && level.NumberOfImportantPeopleTalking > 0) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because important people are talking "+level.NumberOfImportantPeopleTalking+" "+self.bcname); } return false; } timeout = 3000; if(self.team == "axis") { timeout = 1800; } else if(self.team == "allies") { timeout = 1000; } if(IsDefined(level.ImportantPeopleTalkingTime) && (GetTime()-level.ImportantPeopleTalkingTime) cullDist*cullDist) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say, too far from player "+self.bcname); } return false; } if (!isGrenade && GetTime() < self.nextSayTimes[eventAction]) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because of self wait "+self.bcname); } return (false); } if (!isGrenade && GetTime() < self.squad.nextSayTimes[eventAction]) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because of squad wait "+self.bcname+ " "+self.squad.squadName); } return (false); } if (!isGrenade && IsDefined (eventType) && typeLimited (eventAction, eventType)) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because of typeLimit "+self.bcname); } return (false); } if(IsDefined (eventType) && !IsDefined(anim.eventPriority[eventAction][eventType])) { println("BC priority not set for eventType "+eventType+ " "+eventAction); } else if (!isGrenade && IsDefined (eventType) && anim.eventPriority[eventAction][eventType] < self.bcs_minPriority) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG cannot say because of minPriority "+self.bcname); } return (false); } if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG can say because of self wait "+GetTime()+" "+anim.eventActionMinWait[eventAction]["self"]+" "+self.nextSayTimes[eventAction]+" "+self.bcname); println("BC DEBUG can say because of squad wait "+GetTime()+" "+anim.eventActionMinWait[eventAction]["squad"]+" "+self.squad.nextSayTimes[eventAction]+" "+self.bcname); } return (true); } bcPlayPhrase(eventAction, eventType, chatPhrase, decCallCount) { if(!IsDefined(decCallCount)) { decCallCount = false; } if (GetDvar( #"debug_bclotsoprint") == "on") { a = ""; for (i = 0; i < chatPhrase.soundAliases.size; i++) { a += chatPhrase.soundAliases[i] + " "; } println("BC DEBUG saying "+a); } if(chatPhrase.soundAliases.size == 0) { println("BC ERROR tried to play empty phrase" + self.bcname); return; } if(isDupePhrase(chatPhrase, 10000)) { if (GetDvar( #"debug_bclotsoprint") == "on") { println("BC DEBUG skipping because of identical phrase "+self.bcname); } return; } addPhraseToHistory(chatPhrase); squad = self.squad; team = self.team; self.isSpeaking = true; self.nextSayTimes[eventAction] = GetTime() + anim.eventActionMinWait[eventAction]["self"]; squad.nextSayTimes[eventAction] = GetTime() + anim.eventActionMinWait[eventAction]["squad"]; for (i = 0; i < chatPhrase.soundAliases.size; i++) { if(SoundExists(chatPhrase.soundAliases[i])) { self animscripts\face::PlayFaceThread(undefined, chatPhrase.soundAliases[i], .5, chatPhrase.soundAliases[i]); if(!IsDefined(self)) { break; } } else { println("ERROR BC: needed alias does not exist:"+chatPhrase.soundAliases[i]); wait(1.0); } } if (GetDvar( #"debug_bclotsoprint") == "on") { a = ""; for (i = 0; i < chatPhrase.soundAliases.size; i++) { a += chatPhrase.soundAliases[i] + " "; } println("BC DEBUG done saying "+a); } if(IsDefined(self)) { self.isSpeaking = false; self doTypeLimit (eventAction, eventType); if(IsDefined(self.bcCalling) && IsDefined(self.bcCalling.bcCallCount) && decCallCount) { self.bcCalling.bcCallCount[self.team] -= 1; } } } filter(potentialThreats, isThreat) { threats = []; for (i = 0; i < potentialThreats.size; i++) { players = GetPlayers(); for(p=0; p 1000) { return false; } if(distance(self.origin, player.origin) > 400) { return false; } return true; } isMg(player) { if(IsDefined(self.bcCalloutTime)) { if((GetTime() - self.bcCalloutTime) < 6000) { return false; } if((GetTime() - self.bcCalloutTime) > 30000) { self.bcCalloutTime = undefined; self.bcCallCount["axis"] = 0; self.bcCallCount["allies"] = 0; } } return self cansee(player) || distance(player.origin, self.origin) < 300; } isBanzai(player) { playerIsTarget = false; if(IsDefined(self.banzai) && self.banzai) { if(IsDefined(self.enemy) && IsPlayer(self.enemy)) { playerIsTarget = true; } if(IsDefined(self.target) && IsPlayer(self.target)) { playerIsTarget = true; } if(IsDefined(self.favoriteenemy) && IsPlayer(self.favoriteenemy)) { playerIsTarget = true; } if(!playerIsTarget) { return false; } if(!IsDefined(self.bcNoticeTime)) { self.bcNoticeTime = GetTime(); } if(GetTime() - self.bcNoticeTime < 1000) { return false; } if(IsDefined(self.bcCalloutTime)) { return (GetTime() - self.bcCalloutTime) < 4000; } return true; } return false; } isInfantry(player) { return self cansee(player) && distance(player.origin, self.origin) < 1000; } getAllTurrets(team) { t = []; turrets = array_combine(getentarray("misc_mg42", "classname"), getentarray("misc_turret", "classname")); for(i=0; i 3000; } } if(valid) { valid = self bcCanSay("inform", "supressed", 1.0, "generic"); } return valid; } trySurpressed(team) { players = GetPlayers(); sp = filter(team, ::isSurpressed); talker = getNearestTalker(players[0], undefined, sp, "inform", "supressed"); if(!IsDefined(talker)) { return false; } talker.bcSurpressedTime = GetTime(); phrase = talker createChatPhrase(); phrase addAlias( talker getAlias("inform", "supressed", 1.0, "generic") ); talker thread bcPlayPhrase("inform", "supressed", phrase, false); return true; } findGuyToYellAt(team,notme) { for(i=0; i 2000 ) { team[i].bcOrderTime = undefined; team[i].bcOrderType = undefined; team[i].bcOrderModifier = undefined; continue; } if(IsDefined(team[i].bcOrderTime) &&(GetTime() - team[i].bcOrderTime ) < 2000 &&team[i] bcCanSay("order", team[i].bcOrderType) ) { thread doOrder(team[i],findGuyToYellAt(team, team[i]), team[i].bcOrderType, team[i].bcOrderModifier); return true; } } return false; } hasKill(player) { return IsDefined(self.bcKillTime) && (GetTime() - self.bcKillTime) < 2000 && self bcCanSay("inform", "killfirm"); } tryKill(team) { dude = getClosestToPlayer(filter(team, ::hasKill)); if(!IsDefined(dude)) { return false; } modifier = "infantry"; if(RandomFloat(1.0) < .35) { modifier = "generic"; } phrase = dude createChatPhrase(); phrase addAlias( dude getAlias("inform", "killfirm", modifier)); dude thread bcPlayPhrase("inform", "killfirm", phrase, false); dude.bcKillTime = undefined; return true; } hasCasualty(player) { return IsDefined(self.bcFriendDeathTime) && (GetTime() - self.bcFriendDeathTime) < 3000 && self bcCanSay("reaction", "casualty"); } tryCasualty(team) { dude = getClosestToPlayer(filter(team, ::hasCasualty)); if(!IsDefined(dude)) { return false; } modifier = "generic"; phrase = dude createChatPhrase(); phrase addAlias( dude getAlias("reaction", "casualty", modifier)); dude thread bcPlayPhrase("reaction", "casualty", phrase, false); dude.bcFriendDeathTime = undefined; return true; } tryGrenade(player) { return IsDefined(self.bcThrewGrenadeTime) && (GetTime() - self.bcThrewGrenadeTime) < 2000 && self bcCanSay("inform", "attack"); } tryGrenadeInform(team) { dude = getClosestToPlayer(filter(team, ::hasCasualty)); if(!IsDefined(dude)) { return false; } modifier = "grenade"; phrase = dude createChatPhrase(); phrase addAlias( dude getAlias("inform", "attack", modifier)); dude thread bcPlayPhrase("inform", "attack", phrase, false); dude.bcThrewGrenadeTime = undefined; return true; } tryFireInform(team,others) { if(!IsDefined(level.bcOnFireTime)) { return false; } if((GetTime() - level.bcOnFireTime) >3000) { level.bcOnFireTime = undefined; level.bcOnFireOrg = undefined; return false; } if(IsDefined(level.bcOnFireLastSayTime) && (GetTime()-level.bcOnFireLastSayTime) < 10) { return false; } talker = getNearestTalker(level.bcOnFireOrg, undefined, team, "inform", "burning"); if(!IsDefined(talker)) { return false; } level.bcOnFireLastSayTime = GetTime(); phrase = talker createChatPhrase(); phrase addAlias( talker getAlias("inform", "burning")); level.bcOnFireTime = undefined; level.bcOnFireOrg = undefined; talker thread bcPlayPhrase("inform", "burning", phrase, false); return true; } bccycle(team,otherteam) { if (!bcsEnabled()) { return false; } if(tryThreat(GetAIArray (team), getentarray ("grenade", "classname"), 300, 1, ::isGrenade, "inform", "incoming", "grenade", true)) { return true; } if(team == "allies" && tryThreat(GetAIArray (team), GetAIArray(otherteam), 1000, 3, ::isBanzai, "threat", "banzai", undefined, false)) { return true; } wait(.01); if(team == "allies" && tryThreat(GetAIArray (team), getAllTurrets(otherteam), 3000, 1, ::isMg, "threat", "mg", undefined, true)) { return true; } if(tryOrder(GetAIArray(team))) { return true; } wait(.01); if(trySurpressed(GetAIArray(team))) { return true; } if(GetAIArray(otherteam).size > 1 && tryReload(GetAIArray(team),GetAIArray(otherteam))) { return true; } wait(.01); if(tryKill(GetAIArray(team))) { return true; } if(tryCasualty(GetAIArray(team))) { return true; } wait(.01); if(tryGrenadeInform(GetAIArray(team))) { return true; } wait(.01); us = GetAIArray(team); them = GetAIArray(otherteam); if(us.size + 4 < them.size && RandomFloat(1) < .3) { tryThreat(us, them, 2000, 1, ::isInfantry, "threat", "infantry", "multiple", false); } else { tryThreat(us, them, 2000, 1, ::isInfantry, "threat", "infantry", "generic", true); } return false; } bcthread(us,them,talkdelay, nontalkdelay) { while(1) { wait(nontalkdelay); while(bccycle(us,them)) { wait(RandomFloat(2*talkdelay)); } } } bcmain() { thread bcthread("axis", "allies", 2, 2); thread bcthread("allies", "axis", 1, .5); }