ActorRegister={} TrackedPairs = {} TrackedProduction = {} ConditionRecord = {} AP_upgradestate={} --atreides powers upgrade state log AP_ConditionRecord={} --outpost airpower condition record OriginalOwner={} CrazeCount={} ReinforcementSquads = { {"vet_light_inf","vet_light_inf","vet_light_inf","vet_trooper","vet_trooper"}, {"quad","quad","mpsardaukar", "mpsardaukar"}, {"guild_agent"} } WorldLoaded = function() neut=Player.GetPlayer("Neutral") mp0=Player.GetPlayer("Multi0") mp1=Player.GetPlayer("Multi1") mp2=Player.GetPlayer("Multi2") mp3=Player.GetPlayer("Multi3") mp4=Player.GetPlayer("Multi4") mp5=Player.GetPlayer("Multi5") mp6=Player.GetPlayer("Multi6") mp7=Player.GetPlayer("Multi7") players={mp0,mp1,mp2,mp3,mp4,mp5,mp6,mp7}--non existent players will be "nil" and thus ignored by lua gd1=Player.GetPlayer("gas_dummy_1") gd2=Player.GetPlayer("gas_dummy_2") gd3=Player.GetPlayer("gas_dummy_3") Media.DisplayMessage("This map uses D2k Advanced rules.\nVisit moddb.com/mods/dune2k-advanced\nfor more information. Have fun!","Info",HSLColor.DarkGray) for _,player in pairs(players) do if player.HasPrerequisites({"techlevel.house"})and not player.HasPrerequisites({"techlevel.vehicles"}) then actors=player.GetActors() loc=CPos.New(actors[2].Location.X-1,actors[2].Location.Y-1) startpalace=nil if player.Faction=="harkonnen" then startpalace="palace.harkonnen" end if player.Faction=="ordos" then startpalace="palace.ordos" end if player.Faction=="atreides" then startpalace="palace.atreides" end Actor.Create("concreteb", true, { Owner = player, Location = loc }) Actor.Create(startpalace, true, { Owner = player, Location = loc }) end end end Tick = function() --check register for new actors and only call act.Type=="whatever" when necessary since it is expensive local new_actors = ActorsAddedToWorld(Map.ActorsInWorld,ActorRegister) for _,act in pairs(new_actors) do --ACTOR TYPE CHECKS local T=act.Type --needed for airdrop powers if (T=="waypoint_dummy_a" or T=="waypoint_dummy_h" or T=="waypoint_dummy_o") then dropAirReinforcements(act.Location,act) --for atreides improved airdrop powers executeAtreidesPowerUpgrade(act.Owner,"drop") --needed for Sardaukar Invasion Superpower elseif T=="waypoint_dummy_invasion" then commenceSardaukarInvasion(act.Location,act.Owner) --needed for TRxx-5b Superpower elseif T=="camera.gas_target_zone" then crazeArea(act,gd1,gd2,gd3) --needed for Paul Atreides "superpower" elseif T=="paul_atreides" then Trigger.OnRemovedFromWorld(act,function(self) local worms = Map.ActorsInCircle(self.CenterPosition, WDist.New(1),function(w) if w.Type=="sandworm" then return w else return nil end end) if worms[1] then transformPaulAtreides(self.Owner,self.Location) end end) --needed for improved Atreides air powers elseif T=="palace.atreides" then queueAtreidesPowerUpgrades(act.Owner) --Media.Debug("palace placed") Trigger.OnRemovedFromWorld(act, function(victim) if not victim.Owner.GetActorsByType("palace.atreides")[1] then queueAtreidesPowerDegrade(victim.Owner) --Media.Debug("after triggering") end end ) Trigger.OnCapture(act, function(capped,captor,oldOwner,newOwner) queueAtreidesPowerUpgrades(newOwner) end ) elseif T=="ornithopter" then executeAtreidesPowerUpgrade(act.Owner,"strike") elseif T=="upgrade.hightech" then executeAtreidesPowerUpgrade(act.Owner,"strike") elseif T=="upgrade.outpost" then executeAtreidesPowerUpgrade(act.Owner,"drop") --...handle silo replacement on silo upgrade elseif T=="upgrade.silo" then replace_silos(act.Owner) --...Handle saboteur entering a harvester which is getting picked up by a carryall before the Demolition charge explodes (Both will die) elseif (T == "harvester" or T == "advanced_harvester_h" or T == "advanced_harvester_a" or T == "advanced_harvester_o" or T == "harvester.starport") and TrackedPairs[tostring(act)]==nil then TrackedPairs[tostring(act)]='NoCarryall' Trigger.OnKilled(act, function(harvester,killer) local MaybeCarryall = TrackedPairs[tostring(harvester)] if MaybeCarryall~='NoCarryall' and MaybeCarryall~=nil then if MaybeCarryall.HasProperty('Kill') then MaybeCarryall.Kill() Media.PlaySound('EXPLLG2.WAV') end else end end ) Trigger.OnRemovedFromWorld(act, function(harv) local carryalls = Map.ActorsInCircle(harv.CenterPosition, WDist.New(1), function(carry) if (carry.Type=="carryall" or carry.Type=="carryall.starport") then return carry else return nil end end ) if carryalls[1]~=nil then TrackedPairs[tostring(harv)]=carryalls[1] end end ) Trigger.OnAddedToWorld(act,function(harv) local carryalls = Map.ActorsInCircle(harv.CenterPosition, WDist.New(1), function(carry) if (carry.Type=="carryall" or carry.Type=="carryall.starport") then return carry else return nil end end ) if carryalls[1]~=nil then TrackedPairs[tostring(harv)]='NoCarryall' end end ) --...handle guild agent unit-built icons on unit constructing buildings elseif (T=="barracks" or T=="light_factory" or T=="heavy_factory" or T=="high_tech_factory" or T=="starport") and TrackedProduction[tostring(act)]==nil then TrackedProduction[tostring(act)]= true Trigger.OnProduction(act, function(prod,prodded) grantProductionIcon(prod,prodded) end ) end end end function crazeArea(act,dummy1,dummy2,dummy3) --wait for bomb detonating on the ground... Trigger.AfterDelay(32,function() --make all units in area attack each other temporarily local crazyUnits = Map.ActorsInCircle(act.CenterPosition, WDist.New(3584),function(unit) if (unit.HasProperty("Move") and unit.Type~="paul_atreides" and unit.Type~="worm_rider") and not unit.HasProperty("Land") --dirty hack to discard buildings and aircraft from selection then return unit else return nil end end) if crazyUnits[1] --power didn't "miss" then local dummy={dummy1,dummy2,dummy3} for _,u in pairs(crazyUnits) do local ustr=tostring(u) if not OriginalOwner[ustr] --unit isn't under the effect of another craze then OriginalOwner[ustr]=u.Owner end --craze unit u.Owner=Utils.Random(dummy) u.Stop() if u. HasProperty("AttackMove") then u.AttackMove(u.Location) end --track the ammount of stacked crazes on this unit if CrazeCount[ustr] then CrazeCount[ustr]=CrazeCount[ustr]+1 else CrazeCount[ustr]=1 end --return to owner Trigger.AfterDelay(DateTime.Seconds(10),function() CrazeCount[ustr]=CrazeCount[ustr]-1 if CrazeCount[ustr]==0 then CrazeCount[ustr]=nil if u.IsInWorld then --and player wasn't defeated meanwhile local oldowner=OriginalOwner[tostring(u)] if oldowner.HasNoRequiredUnits() then oldowner=neut end u.Owner=oldowner OriginalOwner[ustr]=nil end end end) end end end) end function transformPaulAtreides(player,location) Trigger.AfterDelay(40,function() Actor.Create("worm_rider", true, { Owner = player, Location = location }) end) end function ActorsAddedToWorld(newlist,register) local new_actor_list={} for key,act in pairs(newlist) do if not register[tostring(act)] then register[tostring(act)]=true table.insert(new_actor_list,act) end end return new_actor_list end function queueAtreidesPowerDegrade(player) --Media.Debug("Degrade:") --Media.Debug(tostring(AP_upgradestate[tostring(player)])) local state = AP_upgradestate[tostring(player)] if state=="notstrike" or state=="notdrop" or state==nil then return else AP_upgradestate[tostring(player)]="queued_undo" end end function queueAtreidesPowerUpgrades(player) --Media.Debug("Upgrade:") --Media.Debug(tostring(AP_upgradestate[tostring(player)])) local state = AP_upgradestate[tostring(player)] if state=="strike" or state=="drop" or state=="both" then return else AP_upgradestate[tostring(player)]="queued" end end function executeAtreidesPowerUpgrade(player,power) --Media.Debug("Execute:") --Media.Debug(tostring(AP_upgradestate[tostring(player)])) local state = AP_upgradestate[tostring(player)] local undo=false if not state then return --no palace elseif state=="both" then return --power allready upgraded elseif state==power or state=="not"..power then return elseif state=="queued_undo" then AP_upgradestate[tostring(player)]="not"..power undo=true --no palace and no degrades done elseif state=="queued" then AP_upgradestate[tostring(player)]=power --palace, but no upgrades done elseif string.sub(state,1,3)=="not" and string.sub(state,4)~=power then AP_upgradestate[tostring(player)]=nil undo=true --other power upgrade degrade done elseif string.sub(state,1,3)~="not" and state~=power then AP_upgradestate[tostring(player)]="both" --other power upgrade done else Media.Debug("should not happen") end --Media.Debug(power.." executing "..tostring(AP_upgradestate[tostring(player)])) local PowerProviders if power=="strike" then PowerProviders = player.GetActorsByType("high_tech_factory") elseif power=="drop" then PowerProviders = player.GetActorsByType("outpost") end if not undo then for _,powerprovider in pairs(PowerProviders) do AP_ConditionRecord[tostring(powerprovider)] = powerprovider.GrantCondition("improved_"..power) end else for _,powerprovider in pairs(PowerProviders) do if AP_ConditionRecord[tostring(powerprovider)] then powerprovider.RevokeCondition(AP_ConditionRecord[tostring(powerprovider)]) AP_ConditionRecord[tostring(powerprovider)]=nil end end end end grantProductionIcon = function(producer,unit) if ConditionRecord[tostring(producer)] then producer.RevokeCondition(ConditionRecord[tostring(producer)]) ConditionRecord[tostring(producer)]=nil end local T=unit.Type if producer.Type == 'barracks' then if T=='light_inf' or T=='trooper' or T=='engineer' or T=='thumper' or T=='assassin' or T=='loyalist' or T=='mpsardaukar' or T=='saboteur' or T=='propaganda_corps' or T=='guild_agent' then ConditionRecord[tostring(producer)] = producer.GrantCondition(tostring(unit.Type)..'_ICO') end elseif producer.Type == 'light_factory' then if T=='raider' or T=='trike_a' or T=='quad_hmg' or T=='quad' or T=='stealth_raider' or T=='troop_crawler' then ConditionRecord[tostring(producer)] = producer.GrantCondition(tostring(unit.Type)..'_ICO') end elseif producer.Type == 'heavy_factory' then if T=='advanced_harvester_o' or T=='advanced_harvester_a' or T=='advanced_harvester_h' or T=='combat_tank_a' or T=='combat_tank_h' or T=='combat_tank_o' or T=='siege_tank_o' or T=='siege_tank_a' or T=='deviator' or T=='missile_tank_a' or T=='missile_tank_o' or T=='sonic_tank' or T=='devastator' or T=='mcv' or T=='missile_tank_h' or T=='siege_tank_h' then ConditionRecord[tostring(producer)] =producer.GrantCondition(tostring(unit.Type)..'_ICO') end elseif producer.Type == 'high_tech_factory' then if T=='carryall' or T=='light_thopter' then ConditionRecord[tostring(producer)] =producer.GrantCondition(tostring(unit.Type)..'_ICO') end elseif producer.Type == 'starport' then if T=='carryall.starport' or T=='harvester.starport' or T=='combat_tank_m.starport' or T=='missile_tank.starport' or T=='siege_tank.starport' or T=='trike.starport' or T=='quad.starport' or T=='mobile_crane.starport' then ConditionRecord[tostring(producer)] =producer.GrantCondition(string.sub(tostring(unit.Type),1,-10)..'_ICO') end end end function replace_silos(player) local res = player.Resources for _, actor in pairs(player.GetActorsByType("silo")) do local loc=actor.Location actor.Destroy() Actor.Create("advanced_silo", true, { Owner = player, Location = loc }) end --give the game time to contemplate consequences of rapid player.ResourceCapacity changes, then correct. Trigger.AfterDelay(DateTime.Seconds(0.1), function() player.Resources = res end ) end function dropAirReinforcements(location,dummy) local squad if dummy.Type=="waypoint_dummy_a" then squad=1 elseif dummy.Type=="waypoint_dummy_h" then squad=2 elseif dummy.Type=="waypoint_dummy_o" then squad=3 end --Carryall reinforcements local base = dummy.Owner.GetActorsByType("outpost")[1] local edge = getMapEdge(base.Location, location) local path = {edge,location} local units = Reinforcements.ReinforceWithTransport(dummy.Owner, "carryall.controllable", ReinforcementSquads[squad], path, {path[2], path[1]}) local carryall = units[1] Trigger.OnPassengerExited(carryall, function(carry, pass) if not carry.HasPassengers then carry.Move(path[1]) carry.Destroy() end end ) end function commenceSardaukarInvasion(location,player) --spend all of Baron Vladimir Harkonnens money, to the last cent local frigatecount=math.floor((player.Resources+player.Cash)/2000) local rest=(player.Resources+player.Cash)%2000 local invasionforce if rest < 500 then invasionforce={"mpsardaukar","mpsardaukar"} elseif rest < 1000 then invasionforce={"mpsardaukar","mpsardaukar","combat_tank_h"} elseif rest < 1500 then invasionforce={"mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","combat_tank_h"} else --full invasionforce={"mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","combat_tank_h"} end player.Resources=0 player.Cash=0 --Route Frigate reinforcements local base = player.GetActorsByType("palace.harkonnen")[1] local edge = getMapEdge(base.Location, location) local path = {edge,location} for i=0,frigatecount do local units = Reinforcements.ReinforceWithTransport(player, "frigate.controllable", invasionforce, path, {path[2], path[1]}) local frigate = units[1] Trigger.OnPassengerExited(frigate, function(frig, pass) if not frig.HasPassengers then frig.Move(path[1]) frig.Destroy() end end ) if i==0 then invasionforce={"mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","mpsardaukar","combat_tank_h"} end end end function getMapEdge(posB,posA) local bnds = {} bnds.Top=(Map.TopLeft.Y)/1024 bnds.Bottom=(Map.BottomRight.Y+1)/1024 bnds.Left=(Map.TopLeft.X)/1024 bnds.Right=(Map.BottomRight.X+1)/1024 --catch zero devision errors if posB.X-posA.X == 0 then if posB.Y<posA.Y then return CPos.New(posA.X,bnds.Top) else return CPos.New(posA.X,bnds.Bottom) end elseif posB.Y-posA.Y == 0 then if posB.X<posA.X then return CPos.New(bnds.Left,posA.Y) else return CPos.New(bnds.Right,posA.Y) end end --compute all boundary intersections local slope = (posB.Y-posA.Y)/(posB.X-posA.X) local topX = posA.X - (posA.Y-bnds.Top)/slope local bottomX= posA.X + (bnds.Bottom-posA.Y)/slope local rightY = posA.Y + (bnds.Right-posA.X)*slope local leftY = posA.Y - (posA.X-bnds.Left)*slope local top = CPos.New(math.floor(topX),bnds.Top) local bottom = CPos.New(math.floor(bottomX),bnds.Bottom) local right = CPos.New(bnds.Right, math.floor(rightY)) local left = CPos.New(bnds.Left, math.floor(leftY)) --decide which of them is reasonable local positiveDelX = 0 < (posB.X-posA.X) local positiveSlope= 0 < slope if positiveDelX and positiveSlope then if bottom.X<=bnds.Right then return bottom else return right end elseif positiveDelX and not positiveSlope then if top.X<=bnds.Right then return top else return right end elseif not positiveDelX and positiveSlope then if top.X>=bnds.Left then return top else return left end elseif not positiveDelX and not positiveSlope then if bottom.X>=bnds.Left then return bottom else return left end end Media.DisplayMessage(" in Function getMapEdge, no Edge matched, returning default. (0,0)","Error") return CPos.New(0,0) end