Positions = { flags = { CPos.New(23,11), CPos.New(33,5), CPos.New(23,51), CPos.New(33,45), CPos.New(5,28), CPos.New(11,28), CPos.New(45,28), CPos.New(51,28), CPos.New(20,20), CPos.New(28,20), CPos.New(36,15), CPos.New(37,23), CPos.New(19,33), CPos.New(28,36), CPos.New(36,36), CPos.New(20,41) } , bigflags = { CPos.New(8,8), CPos.New(8,48), CPos.New(48,8), CPos.New(48,48) } , q1d11 = { CPos.New(1,51), CPos.New(1,46), CPos.New(1,41), CPos.New(6,41), CPos.New(6,51), CPos.New(11,51), CPos.New(11,46), CPos.New(11,41), CPos.New(51,51), CPos.New(51,46), CPos.New(51,41), CPos.New(46,41), CPos.New(46,51), CPos.New(41,51), CPos.New(41,46), CPos.New(41,41), CPos.New(1,1), CPos.New(6,1), CPos.New(11,1) , CPos.New(11,6), CPos.New(11,11), CPos.New(6,11), CPos.New(1,6), CPos.New(1,11), CPos.New(51,1), CPos.New(51,6), CPos.New(51,11), CPos.New(46,11), CPos.New(46,1), CPos.New(41,1), CPos.New(41,6), CPos.New(41,11) } , q1d12 = { CPos.New(16,53), CPos.New(1,38), CPos.New(13,38), CPos.New(16,46), CPos.New(38,41), CPos.New(38,48), CPos.New(38,8), CPos.New(38,1), CPos.New(16,6), CPos.New(16,13), CPos.New(1,16), CPos.New(13,16), CPos.New(13,19), CPos.New(1,19), CPos.New(13,35), CPos.New(1,35), CPos.New(1,33), CPos.New(13,33), CPos.New(13,21) , CPos.New(1,21), CPos.New(53,38), CPos.New(53,35), CPos.New(53,33), CPos.New(41,38), CPos.New(41,35), CPos.New(41,33), CPos.New(53,16), CPos.New(53,19), CPos.New(53,21), CPos.New(41,16), CPos.New(41,19), CPos.New(41,21), CPos.New(40,21), CPos.New(39,35), CPos.New(15,19), CPos.New(23,43), CPos.New(23,40), CPos.New(23,37), CPos.New(31,22), CPos.New(31,19), CPos.New(31,16), CPos.New(31,13), CPos.New(31,11), CPos.New(14,33), CPos.New(16,27), CPos.New(19,27), CPos.New(20,27), CPos.New(23,34), CPos.New(23,32), CPos.New(34,27), CPos.New(38,27), CPos.New(36,27), CPos.New(31,35), CPos.New(23,19) } , q1d04 = { CPos.New(28,1), CPos.New(33,1), CPos.New(33,6), CPos.New(28,6), CPos.New(19,6), CPos.New(24,6), CPos.New(19,11), CPos.New(24,11), CPos.New(19,46), CPos.New(24,46), CPos.New(19,51), CPos.New(24,51), CPos.New(28,41), CPos.New(33,41), CPos.New(28,46), CPos.New(33,46), CPos.New(1,24), CPos.New(6,24), CPos.New(11,24), CPos.New(1,28), CPos.New(6,28), CPos.New(11,28), CPos.New(41,24), CPos.New(46,24), CPos.New(51,24), CPos.New(41,28), CPos.New(46,28), CPos.New(51,28), CPos.New(23,25), CPos.New(29,25), CPos.New(23,27), CPos.New(29,27), CPos.New(26,27), CPos.New(26,25) } , q1d09 = { CPos.New(18,18), CPos.New(34,13), CPos.New(26,18), CPos.New(18,39), CPos.New(34,34), CPos.New(26,34), CPos.New(17,31), CPos.New(35,21) } , q1d10 = { CPos.New(6,46), CPos.New(6,6), CPos.New(46,6), CPos.New(46,46) } , nix01 = { CPos.New(16,32), CPos.New(16,31), CPos.New(16,30), CPos.New(17,30), CPos.New(18,30), CPos.New(19,30), CPos.New(20,30), CPos.New(21,30), CPos.New(22,30), CPos.New(22,31), CPos.New(22,32), CPos.New(22,33), CPos.New(22,34), CPos.New(22,35), CPos.New(34,21), CPos.New(34,22), CPos.New(34,23), CPos.New(34,24), CPos.New(34,25) , CPos.New(34,26), CPos.New(35,26), CPos.New(36,26), CPos.New(37,26), CPos.New(38,26), CPos.New(39,26), CPos.New(40,26), CPos.New(40,25), CPos.New(40,24) } , nix02 = { CPos.New(16,16), CPos.New(18,16), CPos.New(20,16), CPos.New(21,16), CPos.New(26,16), CPos.New(28,16), CPos.New(29,16), CPos.New(29,14), CPos.New(29,12), CPos.New(29,11), CPos.New(34,11), CPos.New(36,11), CPos.New(38,11), CPos.New(39,11), CPos.New(39,13), CPos.New(39,15), CPos.New(39,16), CPos.New(26,23), CPos.New(28,23), CPos.New(29,23), CPos.New(26,44), CPos.New(26,42), CPos.New(26,39), CPos.New(26,40), CPos.New(28,39), CPos.New(29,39), CPos.New(34,39), CPos.New(36,39), CPos.New(38,39), CPos.New(39,39), CPos.New(26,32), CPos.New(28,32), CPos.New(29,32), CPos.New(16,39), CPos.New(16,41), CPos.New(16,43), CPos.New(16,44), CPos.New(18,44), CPos.New(20,44), CPos.New(21,44), CPos.New(39,38), CPos.New(39,33), CPos.New(16,22), CPos.New(16,17) } , nix03 = { CPos.New(16,1), CPos.New(19,1), CPos.New(22,1), CPos.New(25,1), CPos.New(16,3), CPos.New(19,3), CPos.New(22,3), CPos.New(25,3), CPos.New(16,9), CPos.New(16,10), CPos.New(4,16), CPos.New(7,16), CPos.New(10,16), CPos.New(4,19), CPos.New(7,19), CPos.New(10,19), CPos.New(4,21), CPos.New(7,21), CPos.New(10,21) , CPos.New(38,4), CPos.New(38,5), CPos.New(44,16), CPos.New(47,16), CPos.New(50,16), CPos.New(44,19), CPos.New(47,19), CPos.New(50,19), CPos.New(44,21), CPos.New(47,21), CPos.New(50,21), CPos.New(34,18), CPos.New(37,18), CPos.New(38,18), CPos.New(16,23), CPos.New(19,23), CPos.New(16,24), CPos.New(19,24), CPos.New(4,33), CPos.New(7,33), CPos.New(10,33), CPos.New(4,36), CPos.New(7,36), CPos.New(10,36), CPos.New(4,38), CPos.New(7,38), CPos.New(10,38), CPos.New(16,36), CPos.New(19,36), CPos.New(20,36), CPos.New(34,31), CPos.New(37,31), CPos.New(38,31), CPos.New(34,30), CPos.New(37,30), CPos.New(38,30), CPos.New(20,23), CPos.New(20,24), CPos.New(23,16), CPos.New(23,22), CPos.New(31,32), CPos.New(31,38), CPos.New(44,33), CPos.New(47,33), CPos.New(50,33), CPos.New(44,36), CPos.New(47,36), CPos.New(50,36), CPos.New(44,38), CPos.New(47,38), CPos.New(50,38), CPos.New(38,44), CPos.New(38,45), CPos.New(16,49), CPos.New(16,50), CPos.New(29,51), CPos.New(32,51), CPos.New(38,51), CPos.New(35,51), CPos.New(29,53), CPos.New(32,53), CPos.New(35,53), CPos.New(38,53) } } function CreateMap() for i,v in pairs(Positions.flags) do Actor.Create('ctflag', true, { Owner = Neutral, Location = v }) end for i,v in pairs(Positions.bigflags) do Actor.Create('ctflagra', true, { Owner = Neutral, Location = v }) end for i,v in pairs(Positions.q1d11) do Actor.Create('q1d11', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.q1d12) do Actor.Create('q1d12', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.q1d04) do Actor.Create('q1d04', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.q1d09) do Actor.Create('q1d09', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.q1d10) do Actor.Create('q1d10', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.nix01) do Actor.Create('nix01', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.nix02) do Actor.Create('nix02', true, { Owner = France, Location = v }) end for i,v in pairs(Positions.nix03) do Actor.Create('nix03', true, { Owner = France, Location = v }) end end CTFFLagtypes = {"ctflag", "ctflagra", "ctmidflag"} aiTicker = DateTime.Seconds(8) -- 0:4 function GetNearFlag (flag) -- function for flag trigger and capture Trigger.OnEnteredProximityTrigger(flag.CenterPosition, WDist.FromCells(2), function(a, id) local mine = flag.Owner local yours = a.Owner if mine ~= yours and mine.IsAlliedWith(yours) ~= true and yours ~= Player.GetPlayer("Neutral") and yours ~= Player.GetPlayer("Creeps") and Actor.CruiseAltitude(a.Type) == 0 and a.Type ~= "1tnk.husk" and a.Type ~= "2tnk.husk" and a.Type ~= "3tnk.husk" and a.Type ~= "4tnk.husk" and a.Type ~= "harv.fullhusk" and a.Type ~= "harv.emptyhusk" and a.Type ~= "mcv.husk" and a.Type ~= "mgg.husk" and a.Type ~= "camera" and a.Type ~= "camera.paradrop" and a.Type ~= "camera.spyplane" then flag.Owner = yours end end) end num = 0 function joinMyTables(t1, t2) -- simple join tables for k,v in ipairs(t2) do table.insert(t1, v) num = num + 1 end return t1 end Team1Won = function() Multi0.MarkCompletedObjective(KillbaseP1) Multi1.MarkCompletedObjective(KillbaseP2) Multi2.MarkFailedObjective(KillbaseP3) Multi3.MarkFailedObjective(KillbaseP4) end Team2Won = function() Multi2.MarkCompletedObjective(KillbaseP3) Multi3.MarkCompletedObjective(KillbaseP4) Multi0.MarkFailedObjective(KillbaseP1) Multi1.MarkFailedObjective(KillbaseP2) end RemoveMap = function() allactors = France.GetActors() for i,v in pairs(allactors) do v.Destroy() end end MCVincome = function() Middleflag = Actor.Create("ctmidflag", true, { Location = CPos.New(28,28), Owner = Neutral }) newmcv0 = Actor.Create("fact", true, { Location = CPos.New(3,7), Owner = Multi0 }) Trigger.OnKilledOrCaptured(newmcv0, function() Multi0.MarkFailedObjective(KillbaseP1) end) newmcv1 = Actor.Create("fact", true, { Location = CPos.New(3,47), Owner = Multi1 }) Trigger.OnKilledOrCaptured(newmcv1, function() Multi1.MarkFailedObjective(KillbaseP2) end) newmcv2 = Actor.Create("fact", true, { Location = CPos.New(51,7), Owner = Multi2 }) Trigger.OnKilledOrCaptured(newmcv2, function() Multi2.MarkFailedObjective(KillbaseP3) end) newmcv3 = Actor.Create("fact", true, { Location = CPos.New(51,47), Owner = Multi3 }) Trigger.OnKilledOrCaptured(newmcv3, function() Multi3.MarkFailedObjective(KillbaseP4) end) Team1Mainbases = { newmcv0, newmcv1 } Team2Mainbases = { newmcv2, newmcv3 } Trigger.OnAllKilledOrCaptured(Team1Mainbases, Team2Won) Trigger.OnAllKilledOrCaptured(Team2Mainbases, Team1Won) Trigger.AfterDelay((Utils.RandomInteger(10, 120)), function() m0e11 = Actor.Create("e1", true, { Location = CPos.New(1,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi0 }) m0e11.Move(CPos.New(7,7)) m1e11 = Actor.Create("e1", true, { Location = CPos.New(1,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi1 }) m1e11.Move(CPos.New(7,48)) m2e11 = Actor.Create("e1", true, { Location = CPos.New(55,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi2 }) m2e11.Move(CPos.New(49,8)) m3e11 = Actor.Create("e1", true, { Location = CPos.New(55,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi3 }) m3e11.Move(CPos.New(47,48)) end) Trigger.AfterDelay((Utils.RandomInteger(10, 120)), function() m0e12 = Actor.Create("e3", true, { Location = CPos.New(1,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi0 }) m0e12.Move(CPos.New(7,7)) m1e12 = Actor.Create("e3", true, { Location = CPos.New(1,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi1 }) m1e12.Move(CPos.New(7,48)) m2e12 = Actor.Create("e3", true, { Location = CPos.New(55,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi2 }) m2e12.Move(CPos.New(49,8)) m3e12 = Actor.Create("e3", true, { Location = CPos.New(55,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi3 }) m3e12.Move(CPos.New(47,48)) end) Trigger.AfterDelay((Utils.RandomInteger(10, 120)), function() m0e13 = Actor.Create("e1", true, { Location = CPos.New(1,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi0 }) m0e13.Move(CPos.New(7,7)) m1e13 = Actor.Create("e1", true, { Location = CPos.New(1,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi1 }) m1e13.Move(CPos.New(7,48)) m2e13 = Actor.Create("e1", true, { Location = CPos.New(55,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi2 }) m2e13.Move(CPos.New(49,8)) m3e13 = Actor.Create("e1", true, { Location = CPos.New(55,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi3 }) m3e13.Move(CPos.New(47,48)) end) Trigger.AfterDelay((Utils.RandomInteger(10, 120)), function() m0e14 = Actor.Create("e3", true, { Location = CPos.New(1,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi0 }) m0e14.Move(CPos.New(7,7)) m1e14 = Actor.Create("e3", true, { Location = CPos.New(1,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi1 }) m1e14.Move(CPos.New(7,48)) m2e14 = Actor.Create("e3", true, { Location = CPos.New(55,8) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi2 }) m2e14.Move(CPos.New(49,8)) m3e14 = Actor.Create("e3", true, { Location = CPos.New(55,48) + CVec.New(0, Utils.RandomInteger(-5, 5)), Owner = Multi3 }) m3e14.Move(CPos.New(47,48)) end) end AllInfantry = { "dog", "e1", "e2", "e3", "e4", "e6", "spy", "medi", "mech", "hijacker", "shok", "c1", "c2", "c3", "c4", "c5", "c6", "c7" } Middlelandingplaces = { CPos.New(31,30), CPos.New(25,26), CPos.New(31,26), CPos.New(25,30) } HeliLeftorRight = { CPos.New(1,1), CPos.New(55,1) } Middlereinforcements = function() Rndincome = Utils.Random(HeliLeftorRight) Rndlanding = Utils.Random(Middlelandingplaces) Rndoutry = Utils.Random(HeliLeftorRight) Rndinf1 = Utils.Random(AllInfantry) Rndinf2 = Utils.Random(AllInfantry) Rndinf3 = Utils.Random(AllInfantry) Rndinf4 = Utils.Random(AllInfantry) Rndinf5 = Utils.Random(AllInfantry) Rndinf6 = Utils.Random(AllInfantry) Rndinf7 = Utils.Random(AllInfantry) if Middleflag.Owner ~= Neutral and Middleflag.Owner ~= France then Reinforcements.ReinforceWithTransport(Middleflag.Owner, "tran.insertion", {Rndinf1, Rndinf2, Rndinf3, Rndinf4, Rndinf5, Rndinf6, Rndinf7}, {Rndincome + CVec.New(0, Utils.RandomInteger(0, 54)), Rndlanding}, {Rndoutry + CVec.New(0, Utils.RandomInteger(0, 54))}) end Trigger.AfterDelay(DateTime.Seconds(30), function() Middlereinforcements() end) end Allfactions = { "ukraine", "russia", "germany", "france", "england" } SetupPlayers = function() France = Player.GetPlayer("france") Neutral = Player.GetPlayer("Neutral") Multi0 = Player.GetPlayer("Multi0") if Multi0 == nil then Multi0 = Player.GetPlayer("Multi1") if Multi0 == nil then anyplayer = Utils.Random(Allfactions) Multi0 = Player.GetPlayer(anyplayer) end end Multi1 = Player.GetPlayer("Multi1") if Multi1 == nil then Multi1 = Player.GetPlayer("Multi0") if Multi1 == nil then anyplayer = Utils.Random(Allfactions) Multi1 = Player.GetPlayer(anyplayer) end end Multi2 = Player.GetPlayer("Multi2") if Multi2 == nil then Multi2 = Player.GetPlayer("Multi3") if Multi2 == nil then anyplayer = Utils.Random(Allfactions) Multi2 = Player.GetPlayer(anyplayer) end end Multi3 = Player.GetPlayer("Multi3") if Multi3 == nil then Multi3 = Player.GetPlayer("Multi2") if Multi3 == nil then anyplayer = Utils.Random(Allfactions) Multi3 = Player.GetPlayer(anyplayer) end end end SetCamera = function(Multi) local mainbase = Multi.GetActorsByType("fact")[1] Camera.Position = mainbase.CenterPosition end Light = 0 turnOn = function() Lighting.Ambient = Light if Light < 1 then Light = Light + 0.01 Trigger.AfterDelay(1, function() turnOn() end) end end function WorldLoaded() SetupPlayers() CreateMap() MCVincome() Middlereinforcements() GetandsetFlags() turnOn() KillbaseP1 = Multi0.AddPrimaryObjective("Kill the enemys Mainbase") KillbaseP2 = Multi1.AddPrimaryObjective("Kill the enemys Mainbase") KillbaseP3 = Multi2.AddPrimaryObjective("Kill the enemys Mainbase") KillbaseP4 = Multi3.AddPrimaryObjective("Kill the enemys Mainbase") Trigger.AfterDelay(1, function() if Multi0.IsLocalPlayer then SetCamera(Multi0) elseif Multi1.IsLocalPlayer then SetCamera(Multi1) elseif Multi2.IsLocalPlayer then SetCamera(Multi2) elseif Multi3.IsLocalPlayer then SetCamera(Multi3) end end) Trigger.AfterDelay(DateTime.Seconds(3), function() -- player announcements Media.DisplayMessage("capture flags to get money and space to place your structures") end) Trigger.AfterDelay(DateTime.Seconds(8), function() Media.DisplayMessage("capture the middleflag and receive reinfordements") end) Trigger.AfterDelay(DateTime.Seconds(15), function() Media.DisplayMessage("Destroy the enemys mainbases to win the game") end) Trigger.AfterDelay(DateTime.Seconds(3), function() --AI Enter Closed area Trigger local aiflaggers = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(bld) return bld.Type == CTFFLagtypes[1] or bld.Type == CTFFLagtypes[2]or bld.Type == CTFFLagtypes[3] end) for i = 1, #aiflaggers do if aiflaggers[i] ~= nil then Trigger.OnEnteredProximityTrigger(aiflaggers[i].CenterPosition, WDist.FromCells(18), function(a, id) local mine = aiflaggers[i].Owner local yours = a.Owner if mine ~= yours and mine.IsAlliedWith(yours) == false and yours.IsBot and a.HasProperty("AttackMove") and Actor.CruiseAltitude(a.Type) == 0 and a.IsDead == false then a.Stop() a.AttackMove(aiflaggers[i].Location) end end) Trigger.OnExitedProximityTrigger(aiflaggers[i].CenterPosition, WDist.FromCells(5), function(a, id) local mine = aiflaggers[i].Owner local yours = a.Owner if mine ~= yours and mine.IsAlliedWith(yours) == false and yours.IsBot and a.HasProperty("AttackMove") and Actor.CruiseAltitude(a.Type) == 0 and a.IsDead == false then a.Stop() a.AttackMove(aiflaggers[i].Location) end end) end end end) end GetandsetFlags = function() CTFlags = {} Trigger.AfterDelay(DateTime.Seconds(1), function() -- set up Flags local neut = Player.GetPlayer("Neutral") for i=1,#CTFFLagtypes do local T1 = neut.GetActorsByType(CTFFLagtypes[i]) if T1 ~= nil then joinMyTables(CTFlags, T1) end end end) Trigger.AfterDelay(DateTime.Seconds(2), function() -- fuse flags for k,v in ipairs(CTFlags) do GetNearFlag(v) end return CTFlags end) end Tick = function() if aiTicker > 0 and aiTicker then aiTicker = aiTicker - 1 elseif aiTicker == 0 then Trigger.AfterDelay(DateTime.Seconds(1), function() aiTicker = DateTime.Minutes(0) + DateTime.Seconds(5) -- 0:5 end) end --new AI scripz if aiTicker == 1 then for i=1,8 do local ai = Player.GetPlayer("Multi" .. tostring(i-1)) if ai ~= nil and ai.IsBot then local aiunits = ai.GetGroundAttackers() -- get Atack units local attackpoint = nil local randomn = Utils.RandomInteger(1,100) -- random Roll local lowtarget = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(bld) -- getting sight flags return (bld.Type == CTFFLagtypes[1]) and bld.Owner ~= ai and ai.IsAlliedWith(bld.Owner) == false end) local medtarget = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(bld) -- getting Infantry flags return (bld.Type == CTFFLagtypes[1] or bld.Type == CTFFLagtypes[2]) and bld.Owner ~= ai and ai.IsAlliedWith(bld.Owner) == false end) local Hightarget = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(bld) -- getting Tang flags return (bld.Type == CTFFLagtypes[1] or bld.Type == CTFFLagtypes[2] or bld.Type == CTFFLagtypes[3]) and bld.Owner ~= ai and ai.IsAlliedWith(bld.Owner) == false end) local Mosttarget = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(bld) -- getting Heavy flags return (bld.Type == CTFFLagtypes[2] or bld.Type == CTFFLagtypes[3]) and bld.Owner ~= ai and ai.IsAlliedWith(bld.Owner) == false end) --Media.DisplayMessage("Ai decides to Attack now and Rolled: " .. tostring(randomn),"") -- Delete aafterwards if randomn > 73 and #Mosttarget ~= 0 then -- decide on priority and random roll attackpoint = Mosttarget[Utils.RandomInteger(1,#Mosttarget)] elseif( randomn > 35 and randomn < 74 ) or (#Mosttarget == 0 and #Hightarget ~= 0)then attackpoint = Hightarget[Utils.RandomInteger(1,#Hightarget)] elseif (randomn > 13 and randomn < 36) or (#Hightarget == 0 and #medtarget ~= 0) then attackpoint = medtarget[Utils.RandomInteger(1,#medtarget)] else attackpoint = lowtarget[Utils.RandomInteger(1,#lowtarget)] end if attackpoint ~= nil then -- check and force to attack choosen point --Media.DisplayMessage("Ai decides to Attack: " .. attackpoint.Type,"") -- Delete aafterwards local whos = attackpoint.Owner if whos ~= ai and whos.IsAlliedWith(ai) == false then for v=1, 40 do local unit = aiunits[v] if unit ~= nil and unit.IsIdle then unit.AttackMove(attackpoint.Location) end end end end end end end end