----------------------------------------------------------------------------------------- -- Start Activity ----------------------------------------------------------------------------------------- function Tartarus:StartActivity() print("START! -- Tartarus:StartActivity()!"); -- self.SweepZone = SceneMan.Scene:GetArea("SweepZone"); -- Zone to clear out of enemies self.FightPhase = { NOFIGHT = 0}; self.CurrentFightPhase = self.FightPhase.NOFIGHT; self.Factions = {CLONE = 1, DUMMY = 2 , RONIN = 3 , BEARS = 4 , BLAME = 5 , DSTR9 = 6 , ROBOT = 7 , BRWNC = 8 , SPACE = 9}; self.Modules = {}; self.LightWeaponSets = {}; self.HeavyWeaponSets = {}; self.ExplosiveSets = {}; self.ActorSets = {}; -- Different probabilities. -- 1 - Probability to get a heavy weapon -- 2 - Probability to get a secondary weapon -- 3 - Probability to get an explosive -- 4 - Probability to a second explosive -- Example: -- self.Probabilities[self.Factions.CLONE] = {0.35 , 0.30 , 0.60 , 0.30}; self.Probabilities = {}; self.Modules[self.Factions.CLONE] = "Coalition.rte"; self.Modules[self.Factions.DUMMY] = "Dummy.rte"; self.Modules[self.Factions.RONIN] = "Ronin.rte"; self.Modules[self.Factions.BEARS] = "BF.rte"; -- You need this additional rte to play this faction self.Modules[self.Factions.BLAME] = "SC.rte"; -- And this too self.Modules[self.Factions.DSTR9] = "D9.rte"; -- And this too self.Modules[self.Factions.ROBOT] = "Base.rte"; self.Modules[self.Factions.BRWNC] = "Browncoats.rte"; self.Modules[self.Factions.SPACE] = "EF.rte"; self.LightWeaponSets[self.Factions.CLONE] = {"Shotgun", "Compact Assault Rifle" , "Sniper Rifle" , "Grenade Launcher" } self.HeavyWeaponSets[self.Factions.CLONE] = {"Auto Shotgun", "Assault Rifle" , "Heavy Sniper Rifle" , "Grenade Launcher" , "Uber Cannon" , "Revolver Cannnon"} self.ExplosiveSets[self.Factions.CLONE] = {"Grenade"}; self.ActorSets[self.Factions.CLONE] = {"Soldier Light" , "Soldier Heavy"}; self.Probabilities[self.Factions.CLONE] = {0.35 , 0.30 , 0.30 , 0.15}; self.LightWeaponSets[self.Factions.DUMMY] = {"Rail Pistol", "Nailgun" , "Blaster" , "Sniper Rifle" , "Nailer Cannon" } self.HeavyWeaponSets[self.Factions.DUMMY] = {"Blaster" , "Sniper Rifle" , "Nailer Cannon" , "Repeater" , "Grenade Launcher" , "Destroyer Cannon" , "Annihiliator"} self.ExplosiveSets[self.Factions.DUMMY] = {"Impulse Grenade"}; self.ActorSets[self.Factions.DUMMY] = {"Dummy"}; self.Probabilities[self.Factions.DUMMY] = {0.65 , 0.35 , 0.30 , 0.15}; self.LightWeaponSets[self.Factions.RONIN] = {"Peacemaker", "TommyGun" , "Uzi" , "Shortgun" , "Rifle Long" , "Pumpgun" } self.HeavyWeaponSets[self.Factions.RONIN] = {"M16", "YAK47" , "M1600" , "Sniper Rifle" , "Spaz12" , "Bazooka" , "Spaz1200"} self.ExplosiveSets[self.Factions.RONIN] = {"Pineapple Grenade" , "Stick Grenade"}; self.ActorSets[self.Factions.RONIN] = {"Dafred" , "Mia" , "Dimitri" , "Sandra" , "Brutus" , "Gordon"}; self.Probabilities[self.Factions.RONIN] = {0.35 , 0.30 , 0.30 , 0.15}; -- Additional Bear Federation faction set. Feel free to add your own favourites. -- YOU NEED Bear Federation MOD INSTALLED TO USE THIS SET!!! self.LightWeaponSets[self.Factions.BEARS] = {"PPC-3", "AK-101" , "VSS", "SVDh", "RG-6"} self.HeavyWeaponSets[self.Factions.BEARS] = {"AK-101", "VSS" , "RPm-4k" , "SVDh" , "VGe Gauss Rifle" , "RPG-7" , "RG-6"} self.ExplosiveSets[self.Factions.BEARS] = {"F1 granade"}; self.ActorSets[self.Factions.BEARS] = {"BF Conscript 1" , "BF Conscript 2" , "BF Conscript 3" , "BF Spetsnaz 1" , "BF Spetsnaz 2" , "BF Spetsnaz 3" , "BF Officer"}; self.Probabilities[self.Factions.BEARS] = {0.55 , 0.30 , 0.30 , 0.15}; -- Additional BLAME! faction set. Fight against the fishermen. -- YOU NEED BLAME! MOD INSTALLED TO USE THIS SET!!! -- Issues: Fishermen can get stuck in transportation tubes self.LightWeaponSets[self.Factions.BLAME] = {"Fishermans Harpoon"} self.HeavyWeaponSets[self.Factions.BLAME] = {"Fishermans Harpoon"} self.ExplosiveSets[self.Factions.BLAME] = {""}; self.ActorSets[self.Factions.BLAME] = {"Fisher"}; self.Probabilities[self.Factions.BLAME] = {-1 , -1 , -1 , -1}; -- Fishermen already have garpoons and don't have any grenades so we don't give them any weapons -- Additional District 9! faction set. Fight against the fishermen. -- YOU NEED DISTRICT9! MOD INSTALLED TO USE THIS SET!!! -- Issues: Prawns too tall and can hardly move self.LightWeaponSets[self.Factions.DSTR9] = {"Repulsor" , "Gas Thrower" , "Wrap Gun" , "Grenade Launcher" , "Microwave"} -- Prawns have Tesla assault rifle and Arc Gun bu default self.HeavyWeaponSets[self.Factions.DSTR9] = {""} self.ExplosiveSets[self.Factions.DSTR9] = {"Explosive fuel canister"}; self.ActorSets[self.Factions.DSTR9] = {"Prawn"}; self.Probabilities[self.Factions.DSTR9] = {-1 , -1 , 0.20 , 0.20}; -- Additional Tradestar Robotics faction self.LightWeaponSets[self.Factions.ROBOT] = {"AK-47" , "Shotgun" , "SMG" , "Blaster Pistol"} self.HeavyWeaponSets[self.Factions.ROBOT] = {"Laser Rifle" , "Flak Cannon"} self.ExplosiveSets[self.Factions.ROBOT] = {"Blue Bomb", "Frag Grenade"}; self.ActorSets[self.Factions.ROBOT] = {"Robot 1" , "Robot 2"}; self.Probabilities[self.Factions.ROBOT] = {0.35 , 0.20 , 0.30 , 0.30}; -- Additional Browncoat faction -- Works only because of a miracle or bug in CC :) self.LightWeaponSets[self.Factions.BRWNC] = {"AK-47" , "Shotgun" , "SMG" , "Blaster Pistol"} self.HeavyWeaponSets[self.Factions.BRWNC] = {"Laser Rifle" , "Flak Cannon"} self.ExplosiveSets[self.Factions.BRWNC] = {"Frag Grenade"}; self.ActorSets[self.Factions.BRWNC] = {"Browncoat Light" , "Browncoat Heavy"}; self.Probabilities[self.Factions.BRWNC] = {0.55 , 0.30 , 0.40 , 0.40}; self.LightWeaponSets[self.Factions.SPACE] = {"Blunderpop"} self.HeavyWeaponSets[self.Factions.SPAcE] = {"Blunderpop" , "Blunderbuzz"} self.ExplosiveSets[self.Factions.SPACE] = {""}; self.ActorSets[self.Factions.SPACE] = {"Marine NCO" , "Bolter Marine" , "Flamer Marine" , "Heavy Plasma Marine" , "Assault Marine" , "Fast-Assault Support"}; self.Probabilities[self.Factions.SPACE] = {-1 , -1 , -1 , -1}; self.AttackFaction = self.Factions.CLONE; self.DefendFaction = self.Factions.SPACE; self.AttackSpawnTimer = Timer(); self.AttackSpawnTimer:Reset(); self.AttackSpawnInterval = 8000; self.DefendSpawnTimer = Timer(); self.DefendSpawnTimer:Reset(); self.DefendSpawnInterval = 10000; self.BrainhuntPercentage = 0.20; -- Amount of defenders that will be sent to hunt for enemy brain. self.BossSpawned = false; self.BossPreset = "Behemoth"; -- Special thanks for LowestFormOfWit for this guy :) self.BossModule = "Tartarus.rte" -- Bear Federation boss -- Too bad it explodes --self.BossPreset = "Armscheif Shrikov"; --self.BossModule = "BF.rte" -- Set up teams -- Team 2 is always CPU self.CPUTeam = Activity.TEAM_2; self.PlayerTeam = Activity.TEAM_1; self.RogueTeam = -1; self.AttackTeam = self.PlayerTeam; self.DefendTeam = self.CPUTeam; self.MaxAttackCount = 1000; -- Unlimited attackers. They die too fast self.MaxDefendCount = 17; -- Limited defenders to not totally overwhelm attackers self.braindead = {}; self.AssignedBrains = {}; self.MOIDLimit = 230; self.GenericTimer = Timer(); -- Part of the wall to be destroyed by blast self.WallBlast = SceneMan.Scene:GetArea("WallBlast"); -- Bunker area self.Bunker = SceneMan.Scene:GetArea("Bunker"); -- Transportation Tubes self.HTube = SceneMan.Scene:GetArea("HTube"); self.VTube = SceneMan.Scene:GetArea("VTube"); -- Big zones to optimize actor search. self.BigZones = {}; self.BigZones[1] = SceneMan.Scene:GetArea("Defence1"); self.BigZones[2] = SceneMan.Scene:GetArea("Defence2"); self.BigZones[3] = SceneMan.Scene:GetArea("Defence3"); self.BigZones[4] = SceneMan.Scene:GetArea("Defence4"); -- Chambers to look for attacking player self.Chambers = {}; self.Chambers[1] = SceneMan.Scene:GetArea("Chamber1"); self.Chambers[2] = SceneMan.Scene:GetArea("Chamber2"); self.Chambers[3] = SceneMan.Scene:GetArea("Chamber3"); self.Chambers[4] = SceneMan.Scene:GetArea("Chamber4"); -- Zones defining firing point checked for troops precence self.SubZones = {}; self.SubZones[1] = {}; self.SubZones[1][1] = SceneMan.Scene:GetArea("Defence1-2"); -- First zone has only two reachable firing points self.SubZones[1][2] = SceneMan.Scene:GetArea("Defence1-2"); self.SubZones[1][3] = SceneMan.Scene:GetArea("Defence1-3"); self.SubZones[1][4] = SceneMan.Scene:GetArea("Defence1-3"); self.SubZones[2] = {}; self.SubZones[2][1] = SceneMan.Scene:GetArea("Defence2-1"); self.SubZones[2][2] = SceneMan.Scene:GetArea("Defence2-2"); self.SubZones[2][3] = SceneMan.Scene:GetArea("Defence2-3"); self.SubZones[2][4] = SceneMan.Scene:GetArea("Defence2-4"); self.SubZones[3] = {}; self.SubZones[3][1] = SceneMan.Scene:GetArea("Defence3-1"); self.SubZones[3][2] = SceneMan.Scene:GetArea("Defence3-2"); self.SubZones[3][3] = SceneMan.Scene:GetArea("Defence3-3"); self.SubZones[3][4] = SceneMan.Scene:GetArea("Defence3-4"); self.SubZones[4] = {}; self.SubZones[4][1] = SceneMan.Scene:GetArea("Defence4-1"); self.SubZones[4][2] = SceneMan.Scene:GetArea("Defence4-2"); self.SubZones[4][3] = SceneMan.Scene:GetArea("Defence4-3"); self.SubZones[4][4] = SceneMan.Scene:GetArea("Defence4-4"); -- Destination points. Troops GOTO to these coordinates when they are teleported self.ZoneDestinations = {}; -- Coalition firing positions for actors at outpost -- Can be indexed with outpost positions variables self.ZoneDestinations[1] = {}; self.ZoneDestinations[1][1] = Vector(528 , 324); self.ZoneDestinations[1][2] = Vector(528 , 324); self.ZoneDestinations[1][3] = Vector(480 , 396); self.ZoneDestinations[1][4] = Vector(480 , 396); self.ZoneDestinations[2] = {}; self.ZoneDestinations[2][1] = Vector(756 , 204); self.ZoneDestinations[2][2] = Vector(756 , 276); self.ZoneDestinations[2][3] = Vector(756 , 348); self.ZoneDestinations[2][4] = Vector(684 , 408); self.ZoneDestinations[3] = {}; self.ZoneDestinations[3][1] = Vector(1356 , 216); self.ZoneDestinations[3][2] = Vector(1308 , 360); self.ZoneDestinations[3][3] = Vector(1512 , 360); self.ZoneDestinations[3][4] = Vector(1308 , 408); self.ZoneDestinations[4] = {}; self.ZoneDestinations[4][1] = Vector(1644 , 204); self.ZoneDestinations[4][2] = Vector(1644 , 276); self.ZoneDestinations[4][3] = Vector(1644 , 348); self.ZoneDestinations[4][4] = Vector(1644 , 420); -- Dispatch points which decide where they need to push troops self.Dispatchers = {}; self.Dispatchers[1] = SceneMan.Scene:GetArea("Dispatch2"); self.Dispatchers[2] = SceneMan.Scene:GetArea("Dispatch3"); self.Dispatchers[3] = SceneMan.Scene:GetArea("Dispatch4"); self.Dispatchers[4] = SceneMan.Scene:GetArea("Dispatch5"); self.Dispatchers[5] = SceneMan.Scene:GetArea("Dispatch6"); -- We need to add delays for dispatchers -- so they have some time to posh actors according to their decisions -- and not rip them apart -- If last dispatch time + delay is more then current time then -- dispatcher won't make new dispatch decisions and will just pass troops self.DispatchTimer = Timer(); self.DispatchDelay = 1000; self.LastDispatch = {}; self.LastDispatch[1] = self.DispatchTimer.ElapsedSimTimeMS; self.LastDispatch[2] = self.DispatchTimer.ElapsedSimTimeMS; self.LastDispatch[3] = self.DispatchTimer.ElapsedSimTimeMS; self.LastDispatch[4] = self.DispatchTimer.ElapsedSimTimeMS; self.LastDispatch[5] = self.DispatchTimer.ElapsedSimTimeMS; -- Dispatch desicions -- 0 - pass horizontally -- 1 - drop vertically self.DispatchDes = {}; self.DispatchDes[1] = 0; self.DispatchDes[2] = 0; self.DispatchDes[3] = 0; self.DispatchDes[4] = 0; self.DispatchDes[5] = 0; -- Assigners assign troops to specific firing points self.Assigners = {}; self.Assigners[1] = SceneMan.Scene:GetArea("Assign1"); self.Assigners[2] = SceneMan.Scene:GetArea("Assign2"); self.Assigners[3] = SceneMan.Scene:GetArea("Assign3"); self.Assigners[4] = SceneMan.Scene:GetArea("Assign4"); self.Assigners[5] = SceneMan.Scene:GetArea("Assign5"); self.Assigners[6] = SceneMan.Scene:GetArea("Assign6"); -- Empty firing points flags -- These considered empty only if zone are empty of CPU units self.IsZoneEmpty = {}; for i = 1 , 4 do self.IsZoneEmpty[i] = {}; for j = 1 , 4 do self.IsZoneEmpty[i][j] = true; end end -- Empty chamber -- These considered empty only if zone are empty of player units self.IsChamberPlayerEmpty = {}; for i = 1 , 4 do self.IsChamberPlayerEmpty[i] = true; end -- Chamber emptiness in percents -- These considered empty only if zone are empty of CPU-friendly units self.ChamberEmptyPercent = {}; for i = 1 , 4 do self.ChamberEmptyPercent[i] = 0; end -- Disabled chambers -- Chambers considered disabled if player destroyed generators for this chamber self.IsChamberOffline = {}; for i = 1 , 4 do self.IsChamberOffline[i] = false; end -- Time when troops should be sent to point next time -- We need to give troops some time to reach destination area before sending there again self.LastTroopsSendTime = {}; for i = 1 , 4 do self.LastTroopsSendTime[i] = {}; for j = 1 , 4 do -- self.LastTroopsSendTime[i][j] = self.DispatchTimer.ElapsedSimTimeMS; self.LastTroopsSendTime[i][j] = -1000000; end end -- Send troops to specific not more than 1 unit\14 secs self.TroopsSendInterval = 14000; self.PowerGeneratorsPos = {} self.PowerGeneratorsPos[1] = Vector(526 , 240); self.PowerGeneratorsPos[2] = Vector(1114 , 264); self.PowerGeneratorsPos[3] = Vector(2026 , 192); self.PowerGeneratorsPos[4] = Vector(2026 , 264); self.PowerGeneratorsPosOffset = {} self.PowerGeneratorsPosOffset[1] = Vector(0 , 20); self.PowerGeneratorsPosOffset[2] = Vector(0 , 38); self.PowerGeneratorsPosOffset[3] = Vector(-24 , 20); self.PowerGeneratorsPosOffset[4] = Vector(-24 , 38); self.PowerGenerators = {} for i = 1 , 4 do self.PowerGenerators[i] = {}; for j = 1, 4 do self.PowerGenerators[i][j] = CreateMOSRotating("Power Generator" , "Tartarus.rte"); self.PowerGenerators[i][j].Team = self.CPUTeam; self.PowerGenerators[i][j].Pos = self.PowerGeneratorsPos[i] + self.PowerGeneratorsPosOffset[j]; MovableMan:AddParticle(self.PowerGenerators[i][j]); end end -- Decision making or usefull stats self.AttackCount = 0; self.DefendCount = 0; self.UnitCount = 0; -- Generic timing and money parameters self.PlayerGold = 0; -- Player starting credits -- Flag to avoid constant triggering of winning routine self.CPUBrainDead = false; -- Spawn CPU brain self.CPUBrain = CreateActor("Dummy Controller" , "Dummy.rte"); self.CPUBrain.Team = self.DefendTeam; -- self.CPUBrain.Pos = Vector(3168 , 246); -- Debug self.CPUBrain.Pos = Vector(3368 , 246); -- Gameplay self.CPUBrain.AIMode = Actor.AIMODE_SENTRY; MovableMan:AddActor(self.CPUBrain); for player = 0, self.PlayerCount - 1 do -- Check if we already have a brain assigned if not self:GetPlayerBrain(player) then -- Set up players self.braindead[player] = false; local brain = CreateAHuman("Brain Robot" , "Base.rte"); -- brain:AddInventoryItem(CreateHDFirearm("Arc Gun" , "D9.rte")); -- Debug brain:AddInventoryItem(self:MakeWeapon(self.AttackFaction , true)); brain:AddInventoryItem(self:MakeWeapon(self.AttackFaction , true)); brain.Team = self.AttackTeam; brain.AIMode = Actor.AIMODE_SENTRY; --brain.Pos = Vector(3000 , 550); -- Debug brain.Pos = Vector(30 , 200 + player * 60); -- Gameplay MovableMan:AddActor(brain); self.AssignedBrains[player] = brain; -- Set the found brain to be the selected actor at start self:SetPlayerBrain(brain, player); -- Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode self:SetObservationTarget(self:GetPlayerBrain(player).Pos, player) end --if end --for -- Set up AI modes for the Actors that have been added to the scene by the scene definition for actor in MovableMan.AddedActors do if actor.Team == self.DefendTeam then actor.AIMode = Actor.AIMODE_SENTRY; -- Replace existing dummy actors with custom ones if self.DefendFaction ~= self.Factions.DUMMY then if actor.ClassName ~= "ADoor" and actor.PresetName ~= "Dummy Controller" then local lpos = actor.Pos; MovableMan:RemoveActor(actor); self:SpawnDefend(lpos); end end end if actor.Team == self.AttackTeam then actor.AIMode = Actor.AIMODE_SENTRY; end end self.BlastTimer = Timer(); self.BlastTimer:Reset(); self.BlastInterval = 50; self.BlastStartX = 210; self.BlastStartY = 451; self.BlastX = 0; self.BlastY = 0; -- Set the team's funds. self:SetTeamFunds(self.PlayerGold , self.AttackTeam); -- Reset all timers self.GenericTimer:Reset(); end ----------------------------------------------------------------------------------------- -- Pause Activity ----------------------------------------------------------------------------------------- function Tartarus:PauseActivity(pause) print("PAUSE! -- DummyAssault:PauseActivity()!"); end ----------------------------------------------------------------------------------------- -- End Activity ----------------------------------------------------------------------------------------- function Tartarus:EndActivity() print("END! -- DummyAssault:EndActivity()!"); end ----------------------------------------------------------------------------------------- -- Brain selection and gameover conditions check ----------------------------------------------------------------------------------------- function Tartarus:DoBrainSelection() if not (self.ActivityState == Activity.OVER) then -- Iterate through all human players for player = 0, self.PlayerCount - 1 do -- The current player's team local team = self:GetTeamOfPlayer(player); -- Make sure the game is not already ending -- Check if any player's brain is dead local brain = self:GetPlayerBrain(player); if not brain or not MovableMan:IsActor(brain) or not brain:HasObjectInGroup("Brains") then self:SetPlayerBrain(nil, player); -- Try to find a new unasigned brain this player can use instead, or if his old brain entered a craft local newBrain = MovableMan:GetUnassignedBrain(team); -- Found new brain actor, assign it and keep on truckin' if newBrain and self.braindead[player] == false then self:SetPlayerBrain(newBrain, player); self.AssignedBrains[player] = newBrain; self:SwitchToActor(newBrain, player, team); else FrameMan:SetScreenText("Commander died!", player, 333, -1, false); self.braindead[player] = true; -- Now see if all brains of self player's team are dead, and if so, end the game if not MovableMan:GetClosestBrainActor(team) then self.WinnerTeam = self:OtherTeam(team); ActivityMan:EndActivity(); end end else -- Update the observation target to the brain, so that if/when it dies, the view flies to it in observation mode self:SetObservationTarget(brain.Pos, player) end -- end --for end end ----------------------------------------------------------------------------------------- -- Make weapon. actorside - actor faction number , is light - weapon list flag to select weapon from ----------------------------------------------------------------------------------------- function Tartarus:MakeWeapon(actorside , islight) local weapon; local r1 , r2; weapon = nil; r1 = math.random(#self.LightWeaponSets[actorside]); r2 = math.random(#self.HeavyWeaponSets[actorside]); if islight then --print (self.LightWeaponSets[actorside][r1]); --print (self.Modules[actorside]); weapon = CreateHDFirearm(self.LightWeaponSets[actorside][r1] , self.Modules[actorside]); else --print (self.LightWeaponSets[actorside][r1]); --print (self.Modules[actorside]); weapon = CreateHDFirearm(self.HeavyWeaponSets[actorside][r2] , self.Modules[actorside]); end return weapon; end ----------------------------------------------------------------------------------------- -- Make explosive. actorside - actor faction number ----------------------------------------------------------------------------------------- function Tartarus:MakeExplosive(actorside) local weapon; local r; weapon = nil; r = math.random(#self.ExplosiveSets[actorside]); --print (self.ExplosiveSets[actorside][r]); --print (self.Modules[actorside]); weapon = CreateTDExplosive(self.ExplosiveSets[actorside][r] , self.Modules[actorside]); return weapon; end ----------------------------------------------------------------------------------------- -- Spawn troops of defending side ----------------------------------------------------------------------------------------- function Tartarus:SpawnDefend(custompos) local actor = nil; local r; if MovableMan:GetMOIDCount() < self.MOIDLimit and self.DefendCount < self.MaxDefendCount then r = math.random(#self.ActorSets[self.DefendFaction]); actor = CreateAHuman(self.ActorSets[self.DefendFaction][r] , self.Modules[self.DefendFaction]); -- probability to give a secondary weapon -- which is always light if math.random() < self.Probabilities[self.DefendFaction][2] then actor:AddInventoryItem(self:MakeWeapon(self.DefendFaction , true)); end -- probability to give a heavy weapon if math.random() < self.Probabilities[self.DefendFaction][1] then actor:AddInventoryItem(self:MakeWeapon(self.DefendFaction , false)); else actor:AddInventoryItem(self:MakeWeapon(self.DefendFaction , true)); end -- probability to give an explosive if math.random() < self.Probabilities[self.DefendFaction][3] then actor:AddInventoryItem(self:MakeExplosive(self.DefendFaction)); end -- probability to give a second explosive if math.random() < self.Probabilities[self.DefendFaction][4] then actor:AddInventoryItem(self:MakeExplosive(self.DefendFaction)); end actor.AIMode = Actor.AIMODE_SENTRY; actor.Team = self.DefendTeam; if custompos == nil then actor.Pos = Vector(2496 , 0); else actor.Pos = custompos; end MovableMan:AddActor(actor); end end ----------------------------------------------------------------------------------------- -- Spawn troops of defending side ----------------------------------------------------------------------------------------- function Tartarus:SpawnAttack() local actor = nil; local r; if MovableMan:GetMOIDCount() < self.MOIDLimit and self.AttackCount < self.MaxAttackCount then r = math.random(#self.ActorSets[self.AttackFaction]); actor = CreateAHuman(self.ActorSets[self.AttackFaction][r] , self.Modules[self.AttackFaction]); -- probability to give a secondary weapon -- which is always light if math.random() < self.Probabilities[self.AttackFaction][2] then actor:AddInventoryItem(self:MakeWeapon(self.AttackFaction , true)); end -- probability to give a heavy weapon if math.random() < self.Probabilities[self.AttackFaction][1] then actor:AddInventoryItem(self:MakeWeapon(self.AttackFaction , false)); else actor:AddInventoryItem(self:MakeWeapon(self.AttackFaction , true)); end -- probability to give an explosive if math.random() < self.Probabilities[self.AttackFaction][3] then actor:AddInventoryItem(self:MakeExplosive(self.AttackFaction)); end -- probability to give a second explosive if math.random() < self.Probabilities[self.AttackFaction][4] then actor:AddInventoryItem(self:MakeExplosive(self.AttackFaction)); end -- probability to give a digger if math.random() < 0.20 then actor:AddInventoryItem(CreateHDFirearm("Medium Digger" , "Base.rte")); end actor.AIMode = Actor.AIMODE_BRAINHUNT; actor.Team = self.AttackTeam; actor.Pos = Vector(10 , 520); MovableMan:AddActor(actor); end end ----------------------------------------------------------------------------------------- -- Update Activity ----------------------------------------------------------------------------------------- function Tartarus:UpdateActivity() -- Clear all objective markers, they get re-added each frame self:ClearObjectivePoints(); self:DoBrainSelection(); self:UpdateBaseDefenceStats(); -- This MUST be called before any CPU decision making routines self:Flow(); if self.DefendSpawnTimer:IsPastSimMS(self.DefendSpawnInterval) then if not self.CPUBrainDead then self:SpawnDefend(nil); self.DefendSpawnTimer:Reset(); end end if not self.GenericTimer:IsPastSimMS(13000) then --[[if math.random() < 0.40 then local Charge = CreateMOSRotating("Wall Charge", "Tartarus.rte") Charge.Pos = self.WallBlast:GetRandomPoint(); MovableMan:AddParticle(Charge) end--]]-- -- Blow the wall if self.BlastTimer:IsPastSimMS(self.BlastInterval) then local charge = CreateMOSRotating("Wall Charge", "Tartarus.rte") charge.Pos = Vector(self.BlastStartX + self.BlastX * 8 , self.BlastStartY + self.BlastY * 8); MovableMan:AddParticle(charge) self.BlastX = self.BlastX + 1; if self.BlastX == 12 then self.BlastX = 0; self.BlastY = self.BlastY + 1; if self.BlastY == 15 then self.BlastY = 0; end end self.BlastTimer:Reset(); end --Mark commander on start to warn player for player = 0, self.PlayerCount - 1 do if MovableMan:IsActor(self.AssignedBrains[player]) then if self.AssignedBrains[player]:HasObjectInGroup("Brains") then self:AddObjectivePoint("Protect commander!", self.AssignedBrains[player].AboveHUDPos, self.AttackTeam, GameActivity.ARROWDOWN); end end -- if end -- for -- Mark generators as objectives for i = 1 , 4 do self:AddObjectivePoint("Destroy to advance!", self.PowerGeneratorsPos[i] + Vector(-10,0), self.AttackTeam, GameActivity.ARROWDOWN); end else -- Spawn attackers only when the wall is destroyed or they'll mess with wrong waypoints if self.AttackSpawnTimer:IsPastSimMS(self.AttackSpawnInterval) then if not self.CPUBrainDead then self:SpawnAttack(); self.AttackSpawnTimer:Reset(); end end end self:CheckWinningConditions(); self:YSortObjectivePoints(); end ----------------------------------------------------------------------------------------- -- Move unit horizontally ----------------------------------------------------------------------------------------- function Tartarus:MoveHoriz(actor) actor.Pos = Vector(actor.Pos.X - 3 , 100); actor.Vel = Vector(-6 , 0); end ----------------------------------------------------------------------------------------- -- Move unit horizontally ----------------------------------------------------------------------------------------- function Tartarus:MoveVert(actor) actor.Pos = Vector(actor.Pos.X , actor.Pos.Y + 3); actor.Vel = Vector(0 , -2); end ----------------------------------------------------------------------------------------- -- Tubes Flow, crossing dispatch and firing point assignments ----------------------------------------------------------------------------------------- function Tartarus:Flow() for actor in MovableMan.Actors do -- Move units throught tubes and dispatch them on crossings if actor.Pos.Y < 312 then if self.HTube:IsInside(actor.Pos) then self:MoveHoriz(actor); end if self.VTube:IsInside(actor.Pos) then self:MoveVert(actor); end for i = 1 , #self.Dispatchers do if self.Dispatchers[i]:IsInside(actor.Pos) then -- Check if we're ready to make new dispatch decision or just pass troops along if self.LastDispatch[i] + self.DispatchDelay > self.DispatchTimer.ElapsedSimTimeMS then if self.DispatchDes[i] == 0 then self:MoveHoriz(actor); else self:MoveVert(actor); end else -- Make decision -- Assume that we want to pass unit by default self.DispatchDes[i] = 0; local mostempty; -- Most CPU-empty chamber local isotherchamberstaken; -- Flags if other chambers are taken by the player local chambertakennum; -- Number of chamber taken by player -- Simple random decision for brainhunting troops -- We simply spawn brain hunters from two last tubes with 15% probability if i == 4 or i == 5 then if math.random() < (self.BrainhuntPercentage / 2) then print ("Dispatched to 5-6 - By Random"); self.DispatchDes[i] = 1; else self.DispatchDes[i] = 0; end -- If next chamber is offline (disabled by player) then drop troops here if self.IsChamberOffline[4] then print ("Dispatched to 5-6 - Chambers Offline"); self.DispatchDes[i] = 1; end elseif i == 3 then -- Complex decsision based on base defence stats -- First we determine which chambers are occupied by the player -- Chambers close to the CPU brain receive troops more often -- Don't forget that 3rd dispatcher gives troops to 4th chamber and zones etc. -- Different dispatchers have different rules of dispatching if not self.IsChamberPlayerEmpty[4] then print ("Dispatched to 4 - Chamber Taken"); self.DispatchDes[i] = 1; else -- Check if player occupied other chambers. -- Dispatch troops to occupied chambers first and then to empty ones. isotherchamberstaken = false; for ii = 1 , 3 do if not self.IsChamberPlayerEmpty[ii] then isotherchamberstaken = true; chambertakennum = ii; break; end end if not isotherchamberstaken then -- Find which zone is the emptiest. Assume that current zone is mostempty = 4; for ii = 1 , 3 do if self.ChamberEmptyPercent[ii] > self.ChamberEmptyPercent[4] then mostempty = ii; end end if mostempty == 4 then print ("Dispatched to 4 - Most Empty"); self.DispatchDes[i] = 1; end else print (string.format("Aborted to 4 - Chamber %d taken" , chambertakennum)); self.DispatchDes[i] = 0; end end -- if -- If next chamber is offline (disabled by player) then drop troops here if self.IsChamberOffline[3] then print ("Dispatched to 4 - Chambers Offline"); self.DispatchDes[i] = 1; end elseif i == 2 then -- Chamber taken check if not self.IsChamberPlayerEmpty[3] then print ("Dispatched to 3 - Chamber Taken"); self.DispatchDes[i] = 1; else -- Other chambers check isotherchamberstaken = false; for ii = 1 , 2 do if not self.IsChamberPlayerEmpty[ii] then isotherchamberstaken = true; chambertakennum = ii; break; end end if not isotherchamberstaken then -- Empty zones mostempty = 3; for ii = 1 , 2 do if self.ChamberEmptyPercent[ii] > self.ChamberEmptyPercent[3] then mostempty = ii; end end if mostempty == 3 then print ("Dispatched to 3 - Most Empty"); self.DispatchDes[i] = 1; end else print (string.format("Aborted to 3 - Chamber %d taken" , chambertakennum)); self.DispatchDes[i] = 0; end end -- if -- If next chamber is offline (disabled by player) then drop troops here if self.IsChamberOffline[2] then print ("Dispatched to 3 - Chambers Offline"); self.DispatchDes[i] = 1; end elseif i == 1 then if not self.IsChamberPlayerEmpty[2] then print ("Dispatched to 2 - Chamber Taken"); self.DispatchDes[i] = 1; else if self.IsChamberPlayerEmpty[1] then if self.ChamberEmptyPercent[2] > self.ChamberEmptyPercent[1] then print ("Dispatched to 2 - Most Empty"); self.DispatchDes[i] = 1; end else print (string.format("Aborted to 2 - Chamber %d taken" , 1)); self.DispatchDes[i] = 0; end end -- if -- If next chamber is offline (disabled by player) then drop troops here if self.IsChamberOffline[1] then print ("Dispatched to 2 - Chambers Offline"); self.DispatchDes[i] = 1; end end -- All troops that miss all these checks go to point #1 self.LastDispatch[i] = self.DispatchTimer.ElapsedSimTimeMS; end -- if - else end -- if end -- for i end -- if -- Assign falling troops to specific firing points if actor.Team == self.DefendTeam and actor.AIMode == Actor.AIMODE_SENTRY then -- First 4 assigners assign troops to firing points -- 2 last assign a brain hunt mode for i = 1 , 4 do if self.Assigners[i]:IsInside(actor.Pos) then local isassigned; isassigned = false; for j = 1 , #self.IsZoneEmpty[i] do if self.IsZoneEmpty[i][j] then actor.AIMode = Actor.AIMODE_GOTO; actor:ClearAIWaypoints(); actor:AddAISceneWaypoint(self.ZoneDestinations[i][j]); self.LastTroopsSendTime[i][j] = self.DispatchTimer.ElapsedSimTimeMS; print (string.format("Assigned to %d - %d" , i , j)); isassigned = true; break; end -- if end -- for j -- If all points in selected zone are taken, assign to random point -- to avoid crowds of sentry troops if not isassigned then local r; r = math.random(#self.IsZoneEmpty[i]); actor.AIMode = Actor.AIMODE_GOTO; actor:ClearAIWaypoints(); actor:AddAISceneWaypoint(self.ZoneDestinations[i][r]); self.LastTroopsSendTime[i][r] = self.DispatchTimer.ElapsedSimTimeMS; print (string.format("Assigned to %d - %d by random" , i , r)); end end -- if end -- for i for i = 5 , 6 do if self.Assigners[i]:IsInside(actor.Pos) then actor.AIMode = Actor.AIMODE_BRAINHUNT; end end end -- if end-- for actor in MovableMan.Actors do end-- func ----------------------------------------------------------------------------------------- -- Update base defence stats to make dispatch decisions later ----------------------------------------------------------------------------------------- function Tartarus:UpdateBaseDefenceStats() self.AttackCount = 0; self.DefendCount = 0; self.UnitCount = 0; -- Reset all current states, they are refilled here -- Zones are considered non-empty if we have non-expired troops sent to this zone for i = 1 , 4 do for j = 1 , 4 do -- print (self.DispatchTimer.ElapsedSimTimeMS); if self.LastTroopsSendTime[i][j] + self.TroopsSendInterval > self.DispatchTimer.ElapsedSimTimeMS then self.IsZoneEmpty[i][j] = false; else self.IsZoneEmpty[i][j] = true; end end end for i = 1 , #self.IsChamberPlayerEmpty do self.IsChamberPlayerEmpty[i] = true; end for i = 1 , #self.ChamberEmptyPercent do self.ChamberEmptyPercent[i] = 0; end for actor in MovableMan.Actors do -- Check for empty firing points if actor.Team == self.DefendTeam then for i = 1 , #self.BigZones do if self.BigZones[i]:IsInside(actor.Pos) then for j = 1, #self.SubZones[i] do if self.SubZones[i][j]:IsInside(actor.Pos) then self.IsZoneEmpty[i][j] = false; end end -- for end -- if end -- for self.AttackCount = self.AttackCount + 1; end -- if if actor.Team == self.AttackTeam then -- Mark player occupied chambers for i = 1 , #self.Chambers do if self.Chambers[i]:IsInside(actor.Pos) then self.IsChamberPlayerEmpty[i] = false; end end self.DefendCount = self.DefendCount + 1; -- Spawn boss logic if not self.BossSpawned then if self.Bunker:IsInside(actor.Pos) then -- Give player a bonus or antibonis if he destroyed all generators local offlinecount = 0; for ii = 1 , #self.IsChamberOffline do if self.IsChamberOffline[ii] then offlinecount = offlinecount + 1; end end if offlinecount == #self.IsChamberOffline then print ("All generators down. Spawn rate decreased -70%."); self.DefendSpawnInterval = self.DefendSpawnInterval * 1.7; self.AttackSpawnInterval = self.AttackSpawnInterval * 1.3; else print ("Not all generators down. Spawn rate increased +30%."); self.DefendSpawnInterval = self.DefendSpawnInterval * 0.7; self.AttackSpawnInterval = self.AttackSpawnInterval * 1.6; end -- Brain under attack. Send more troops to find attacking brain self.BrainhuntPercentage = 0.90; -- Spawn BOSS local actor = CreateAHuman(self.BossPreset , self.BossModule); actor.AIMode = Actor.AIMODE_SENTRY; actor.Team = self.DefendTeam; actor.Pos = Vector(2904 , 0); MovableMan:AddActor(actor); self.BossSpawned = true; end end end end -- Recheck online generators for i = 1 , #self.PowerGenerators do if not self.IsChamberOffline[i] then -- Check if all four generators per chamber are destroyed local generatorsdead; generatorsdead = true; for j = 1 , #self.PowerGenerators[i] do if MovableMan:IsParticle(self.PowerGenerators[i][j]) then generatorsdead = false; else self.PowerGenerators[i][j] = nil; end end -- All generators dead, disable chamber if generatorsdead then self.IsChamberOffline[i] = true; print (string.format ("Chamber %d is offline!" , i)); end end end -- Recalculate chamber emptiness percentage local fillcount = {}; for i = 1, 4 do fillcount[i] = 0; for j = 1, 4 do if not self.IsZoneEmpty[i][j] then fillcount[i] = fillcount[i] + 1; end end self.ChamberEmptyPercent[i] = 100 - ((fillcount[i] / #self.SubZones[i]) * 100); end end ----------------------------------------------------------------------------------------- -- Check if we are victorious ----------------------------------------------------------------------------------------- function Tartarus:CheckWinningConditions() if self.CPUBrainDead == false then if not MovableMan:IsActor(self.CPUBrain)then -- Team 1 wins! self.CPUBrainDead = true; self.WinnerTeam = self.AttackTeam; ActivityMan:EndActivity(); -- Tell the script to stop running for brains as the game has ended. for player = 0, self.PlayerCount - 1 do self.braindead[player] = true; end else self:AddObjectivePoint("KILL", self.CPUBrain.AboveHUDPos, self.PlayerTeam, GameActivity.ARROWDOWN); end end end ----------------------------------------------------------------------------------------- -- That's all folks! -----------------------------------------------------------------------------------------