chat_msg = Media.DisplayMessage choose = Utils.Random rand = function(inclusive_low,inclusive_high) return Utils.RandomInteger(inclusive_low,inclusive_high+1) end after_delay_s = function(sec,func) Trigger.AfterDelay(25*sec,func) end one_cell = 1024 -- how many WPos units one CPos unit is num_player_slots=3 wave_spawn_interval = 60 first_wave_delay_s = 10 current_wave_num = 0 mis_obj_descr="Protect your Tech Center at all costs" all_prim_objectives={} victory_wave_num=15 the_call_counter = 0 call_info_interval = 60*25 -- print once every minute PrintCallInfo = function() --print("calls/tick "..the_call_counter/call_info_interval) the_call_counter=0 Trigger.AfterDelay(call_info_interval,PrintCallInfo) end -- Map.RandomEdgeCell() returns coordinates that are outside the playable area get_edge_cell = function() if choose({true,false}) then return CPos.New(rand(map_edge_w,map_edge_e),choose({map_edge_n,map_edge_s})) else return CPos.New(choose({map_edge_w,map_edge_e}),rand(map_edge_n,map_edge_s)) end end rand_cvec = function(r) local a=math.pi/1024*rand(-1024,1024) return CVec.New(math.cos(a)*r,math.sin(a)*r) end -- Returns subset of given actors of given type select_by_type = function(actors,types) local x={} for junk,a in ipairs(actors) do if types[a.Type] then x[#x+1]=a end end return x end end_mission = function(win) for p in pairs(all_prim_objectives) do local obj=all_prim_objectives[p] if win then p.MarkCompletedObjective(obj) Media.PlaySpeechNotification(p, "Win") else p.MarkFailedObjective(obj) Media.PlaySpeechNotification(p, "Lose") end end all_prim_objectives={} if win then chat_msg(mis_obj_descr,"Objective completed") else chat_msg(mis_obj_descr,"Objective failed") end end fail_mission = function() end_mission(false) end complete_mission = function() end_mission(true) end -- Returns subset of given actors that are owned by a player select_player_owned = function(actors) local x={} local creeps=Player.GetPlayer("Creeps") local neutral=Player.GetPlayer("Neutral") for junk,a in ipairs(actors) do if ( a.Owner ~= creeps ) and ( a.Owner ~= neutral ) then x[#x+1]=a end end return x end locate_target = function() -- monsters are sent to attack this location local r=8 return CPos.New(66+rand(-r,r),54+rand(-r,r)) end do_wander = function(actor) actor.Hunt() the_call_counter=the_call_counter+1 end fly_wander = function(actor) -- AttackWander makes planes go outside map actor.Move(actor.Location+rand_cvec(10)) the_call_counter=the_call_counter+1 end do_retaliate = function(self,enemy) -- Sometimes the attacker dies before OnDamaged is called (usually bikes that shoot slow missiles) -- so Attack(enemy) might cause a crash if enemy.IsInWorld and not enemy.IsDead then self.Stop() self.Attack(enemy) end the_call_counter=the_call_counter+1 end -- Viceroid behaviour seek_daddy = function(vice) local o=vice.Location local r=10 -- how far do the giants attract viceroids local t=CVec.New(r,r) local dads=Map.ActorsInBox(Map.CenterOfCell(o-t),Map.CenterOfCell(o+t),function(a) return a.Type=="viceman" end) if #dads > 0 then vice.Guard(choose(dads)) else vice.Move(o+rand_cvec(2)) vice.Wait(rand(25,50)) end the_call_counter=the_call_counter+1 end -- unit_list is a dictionary where key is unit type and value is unit count spawn_paradrop = function(dst,unit_list,owner) local plane=Actor.Create("c17",true,{Owner=owner,Location=get_edge_cell()}) for unit_type in pairs(unit_list) do for n=1,unit_list[unit_type] do plane.LoadPassenger(Actor.Create(unit_type,false,{Owner=owner,Location=dst})) end end plane.Move(dst+rand_cvec(10)) plane.Paradrop(dst) end -- Spawn a wave of monsters spawn_monsters = function() local prev_wave=current_wave_num current_wave_num=current_wave_num+1 if prev_wave == victory_wave_num then complete_mission() return end if prev_wave > victory_wave_num then return end local N=current_wave_num*0.5 local num_giants=math.floor(0.2+N*0.5) local num_flying=math.floor(N*0.6) local num_raptors=math.floor(3+(N+2)*0.6) local num_vice=math.floor(4+0.8*(N+3)) local num_paradrops=math.floor(0.5+0.24*N) chat_msg("Wave "..current_wave_num.."/"..victory_wave_num.." spawned") --[[print("Monster counts:") print("giants="..num_giants.." + vice="..(num_giants*2).." + minivice="..num_giants*6) print("flying="..num_flying) print("raptors="..num_raptors) print("vice="..num_vice) print("paradrops="..num_paradrops)--]] for i=1,num_giants do local pos=get_edge_cell() local dest=locate_target() local master=Actor.Create("viceman",true,{Owner=monster_owner,Location=pos}) master.Wait(25*5) master.AttackMove(dest) Trigger.OnIdle(master,do_wander) -- Accompany the giant with a bunch of viceroids for y=1,2 do local vice=Actor.Create("vice",true,{Owner=monster_owner,Location=pos}) vice.Wait(rand(0,25*3)) vice.AttackMove(dest) Trigger.OnIdle(vice,seek_daddy) end for y=1,8 do local vice=Actor.Create("minivice",true,{Owner=monster_owner,Location=pos}) vice.Wait(rand(0,25*3)) vice.AttackMove(dest) Trigger.OnIdle(vice,seek_daddy) end end for i=1,num_flying do local fly=Actor.Create("vicefly",true,{Owner=monster_owner,Location=get_edge_cell()}) fly.AttackMove(locate_target()) Trigger.OnIdle(fly,fly_wander) Trigger.OnDamaged(fly,do_retaliate) end for i=1,1+num_raptors/4 do local raptor_pos=get_edge_cell() local breed=choose({"rapt","raptb"}) local delay=rand(50,125) for k=1,4 do local rap=Actor.Create(breed,true,{Owner=monster_owner,Location=raptor_pos}) rap.Wait(delay) rap.AttackMove(locate_target()) Trigger.OnIdle(rap,do_wander) --! end end for i=1,num_vice do local vice=Actor.Create("vice",true,{Owner=monster_owner,Location=get_edge_cell()}) vice.Wait(rand(5,50)) vice.AttackMove(locate_target()) Trigger.OnIdle(vice,seek_daddy) end for i=1,num_paradrops do spawn_paradrop(locate_target(),{vice=3},monster_owner) end after_delay_s(wave_spawn_interval,spawn_monsters) end -- Makes enemy spawn areas visible test_rap = function() for i=1,50 do local pos=get_edge_cell() local rap=Actor.Create("rapt",true,{Owner=monster_owner,Location=pos}) rap.Wait(25*10) rap.AttackMove(locate_target()) end end init_objectives = function() all_prim_objectives={} for i=0,num_player_slots-1 do p=Player.GetPlayer("Multi"..i) if p then all_prim_objectives[p] = p.AddPrimaryObjective(mis_obj_descr) end end Trigger.OnKilled(TheHouse,fail_mission) chat_msg(mis_obj_descr,"New primary objective") end Tick = function() for pl in pairs(all_prim_objectives) do if pl.HasNoRequiredUnits() then pl.MarkFailedObjective(all_prim_objectives[pl]) all_prim_objectives[pl]=nil end end end WorldLoaded = function() map_edge_w=Map.TopLeft.X/one_cell map_edge_e=Map.BottomRight.X/one_cell-1 map_edge_n=Map.TopLeft.Y/one_cell map_edge_s=Map.BottomRight.Y/one_cell-1 monster_owner=Player.GetPlayer("Creeps") init_objectives() after_delay_s(first_wave_delay_s,spawn_monsters) --test_rap() Trigger.AfterDelay(call_info_interval,PrintCallInfo) end