Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Global variables
- bluePlanes = 60
- redPlanes = 60
- bluePilots = 30
- redPilots = 30
- blueTargetCount = 0
- redTargetCount = 0
- blueTargetZones = {}
- redTargetZones = {}
- gameOver = false;
- TIME_BETWEEN_ATTACK_MESSAGES = 15
- TIME_BETWEEN_MISSION_STATS = 180
- publishStatsFunction = nil
- -- FUNCTION DEFINITIONS ====================================
- -- When a plane becomes occupied, update that group on the mission status.
- -- I'd prefer to go by plane, but this is the best I can do. I guess it's nice
- -- to know when somebody joins your flight.
- function OnPlayerEnterUnit(event)
- -- Sanity check to make sure player isn't entering an empty unit or something I dunno.
- if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT and event.initiator ~= nil then
- local playerName = event.initiator:getPlayerName()
- local callsign = event.initiator:getCallsign()
- local unitName = event.initiator:getDesc().displayName
- local groupID = event.initiator:getGroup():getID()
- -- Full status text
- local stats = string.format("%s has occupied %s %s.\nBlue team has %u planes and %u pilots left.\nRed team has %u planes and %u pilots left", playerName, unitName, callsign, bluePlanes, bluePilots, redPlanes, redPilots)
- trigger.action.outTextForGroup(groupID, stats, 15.0, false)
- end
- end
- -- When a vehicle gets destroyed, remove it from the appropriate side.
- -- Note that everything other than planes are "dead" when they are destroyed, not "crashed."
- function OnVehicleDestroyed(event)
- -- Sanity to make sure the initiator exists. Sometimes a vehicle gets destroyed twice.
- if event.id == world.event.S_EVENT_DEAD and event.initiator ~= nil and event.initiator:getCoalition() ~= nil then
- DestroyGroundTarget(event.initiator, event.initiator:getCoalition())
- end
- end
- -- When a plane is destroyed, remove it from the appropriate side.
- -- Note that all aicraft are "crashed" when they are destroyed, not "dead."
- function OnAircraftCrashed(event)
- -- Sanity check to make sure the initiator exists. Weird things can happen.
- if event.id == world.event.S_EVENT_CRASH and event.initiator ~= nil then
- DestroyPlane(event.initiator:getCoalition())
- end
- end
- -- When a pilot is killed, remove them from the appropriate side.
- function OnPilotKilled(event)
- -- Sanity check to make sure the initiator exists. Weird things can happen.
- if event.id == world.event.S_EVENT_PILOT_DEAD and event.initiator ~= nil then
- DestroyPilot(event.initiator:getCoalition())
- end
- end
- -- If a player leaves a plane while in the air, count it as a lost plane.
- -- When they leave a plane that's on the ground, they won't get penalized.
- function OnPlayerExitUnit(event)
- -- Initiator can be nil when the player crashes or dies.
- if event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator ~= nil and event.initiator:inAir() then
- local callsign = event.initiator:getCallsign()
- local unitName = event.initiator:getDesc().displayName
- -- Remove the appropriate side's plane and pilot.
- DestroyPlane(event.initiator:getCoalition())
- DestroyPilot(event.initiator:getCoalition())
- -- Tell everybody what happened. Sometimes you just need to disconnect,
- -- but if you're being a poor sport, everybody will knooooooooooooooow.
- local outText = string.format("%s left their slot %s while flying.", callsign, unitName)
- end
- end
- -- Remove the appropriate side's plane and notify all the players.
- function DestroyPlane(side)
- if side == coalition.side.RED then
- redPlanes = redPlanes - 1
- trigger.action.outText("Red team aircraft destroyed, " .. redPlanes .. " remaining.", 15.0, false)
- CheckObjectiveWinConditions()
- elseif side == coalition.side.BLUE then
- bluePlanes = bluePlanes - 1
- trigger.action.outText("Blue team aircraft destroyed, " .. bluePlanes .. " remaining.", 15.0, false)
- CheckObjectiveWinConditions()
- else
- trigger.action.outText("Invalid faction for plane destroy.", 15.0, false)
- end
- end
- -- When a ground target is destroyed, they get removed from that zone's unit count.
- function DestroyGroundTarget(unit, side)
- local reportText = ""
- local zoneDestroyedName = ""
- local zoneUnderAttackMGRS = ""
- local targetZones
- local messagePublished = true
- local attackedZone
- -- Determine which target zone table to use.
- if side == coalition.side.RED then
- targetZones = redTargetZones
- elseif side == coalition.side.BLUE then
- targetZones = blueTargetZones
- end
- -- Go through that side's target zones to determine which zone this one was in.
- for i, zone in ipairs(targetZones)
- do
- -- If this is the zone, then save its name and position.
- if WithinDistance(unit:getPoint().x, unit:getPoint().z, zone.point.x, zone.point.z, zone.radius) then
- zoneDestroyedName = zone.name
- -- Convert from engine coordinates to MGRS through lat/long.
- local zoneLat, zoneLong = coord.LOtoLL(zone.point)
- local zoneMGRS = coord.LLtoMGRS(zoneLat, zoneLong)
- -- DCS's raw MGRS northing and easting are numbers, not strings. So if they are printed
- -- directly, you get incorrect values. (e.g. 035 prints to 35) This will print out the
- -- whole thing like MG1234512345, then use substrings of that to construct something with
- -- the format MG11.
- zoneUnderAttackMGRS = string.format("%s%05u%05u", zoneMGRS.MGRSDigraph, zoneMGRS.Easting, zoneMGRS.Northing)
- zoneUnderAttackMGRS = string.sub(zoneUnderAttackMGRS, 1, 3) .. string.sub(zoneUnderAttackMGRS, 8, 8)
- -- Save this to fire a flare later on.
- attackedZone = zone
- break
- end
- end
- -- Only do target destroyed logic if they were in a target zone.
- if zoneDestroyedName ~= "" then
- -- Red target destroyed.
- if side == coalition.side.RED then
- redTargetZones[zoneDestroyedName] = redTargetZones[zoneDestroyedName] -1
- reportText = string.format("RED target destroyed in \"%s\" at MGRS %s!\nZone status is now %s (%u/%u).", zoneDestroyedName, zoneUnderAttackMGRS, PercentTargetsLeftInZone(redTargetZones, zoneDestroyedName), redTargetZones[zoneDestroyedName], redTargetZones[zoneDestroyedName .. "init"])
- redTargetCount = redTargetCount - 1
- -- Only print attack messages if the last attack was TIME_BETWEEN_ATTACK_MESSAGES seconds ago.
- messagePublished = PublishTargetDestroyedText(reportText, redTargetZones[zoneDestroyedName .. "lastAttackTime"])
- redTargetZones[zoneDestroyedName .. "lastAttackTime"] = timer.getTime()
- -- If this zone was completely destroyed, publish a special message.
- if redTargetZones[zoneDestroyedName] == 0 then
- trigger.action.outText("\nAll units in red target area " .. zoneDestroyedName .. " have been completely destroyed!\n", 30.0, false)
- end
- -- Blue target destroyed.
- elseif side == coalition.side.BLUE then
- blueTargetZones[zoneDestroyedName] = blueTargetZones[zoneDestroyedName] -1
- reportText = string.format("BLUE target destroyed in zone \"%s\" at MGRS %s!\nZone status is now %s (%u/%u).", zoneDestroyedName, zoneUnderAttackMGRS, PercentTargetsLeftInZone(blueTargetZones, zoneDestroyedName), blueTargetZones[zoneDestroyedName], blueTargetZones[zoneDestroyedName .. "init"])
- blueTargetCount = blueTargetCount - 1
- -- Only print attack messages if the last attack was TIME_BETWEEN_ATTACK_MESSAGES seconds ago.
- messagePublished = PublishTargetDestroyedText(reportText, blueTargetZones[zoneDestroyedName .. "lastAttackTime"])
- blueTargetZones[zoneDestroyedName .. "lastAttackTime"] = timer.getTime()
- -- If this zone was completely destroyed, publish a special message.
- if blueTargetZones[zoneDestroyedName] == 0 then
- trigger.action.outText("\nAll units in blue target area " .. zoneDestroyedName .. " have been completely destroyed!\n", 30.0, false)
- end
- else
- reportText = "Invalid faction for ground object destroy."
- end
- -- Shoot a flare if a message was published.
- if messagePublished == true then
- --trigger.action.signalFlare(mist.utils.makeVec3GL(mist.getRandPointInCircle(attackedZone.point, attackedZone.radius)), 3, 0)
- trigger.action.signalFlare(unit:getPoint(), 3, 0)
- end
- -- Check if there's nothing else and if so, this is a win.
- CheckObjectiveWinConditions()
- end
- end
- function PublishTargetDestroyedText(text, lastAttackTime)
- local newMessagePublished = false;
- if (timer.getTime() - lastAttackTime > TIME_BETWEEN_ATTACK_MESSAGES) then
- trigger.action.outText(text, 15.0, false)
- newMessagePublished = true
- end
- return newMessagePublished
- end
- -- Remove the appropriate side's plane and notify all the players.
- function DestroyPilot(side)
- if side == coalition.side.RED then
- redPilots = redPilots - 1
- trigger.action.outText("Red team pilot killed, " .. redPilots .. " remaining.", 15.0, false)
- CheckObjectiveWinConditions()
- elseif side == coalition.side.BLUE then
- bluePilots = bluePilots - 1
- trigger.action.outText("Blue team pilot killed, " .. bluePilots .. " remaining.", 15.0, false)
- CheckObjectiveWinConditions()
- else
- trigger.action.outText("Invalid faction for pilot kill.", 15.0, false)
- end
- end
- -- Returns a string of the format 27% (usual case) or 100% (when 100% of units are left in zone).
- function PercentTargetsLeftInZone(targetZones, zoneDestroyedName)
- local percentKilled = (targetZones[zoneDestroyedName] / targetZones[zoneDestroyedName .. "init"]) * 100
- percentKilled = string.format("%0.0f%%", percentKilled)
- return percentKilled
- end
- -- Get total ground targets.
- function InitGroundTargetCounts()
- local blueTargetAreaCount = 0
- local redTargetAreaCount = 0
- -- Save zones by their color.
- for i, zone in ipairs(mist.DBs.zonesByNum)
- do
- if ZoneIsBlue(zone.color) then
- table.insert(blueTargetZones, zone)
- blueTargetZones[zone.name] = CountUnitsInZone(zone, "blue")
- blueTargetZones[zone.name .. "init"] = blueTargetZones[zone.name]
- blueTargetAreaCount = blueTargetAreaCount + 1
- blueTargetCount = blueTargetCount + blueTargetZones[zone.name]
- blueTargetZones[zone.name .. "lastAttackTime"] = timer.getTime()
- -- Spawn a blue smoke flare on the zone.
- SpawnFlaresInCircle(mist.utils.zoneToVec3(zone), zone.radius, trigger.smokeColor.Blue)
- -- DEBUG
- --trigger.action.outText("Initialized zone " .. zone.name .. " with " .. blueTargetZones[zone.name] .. " units.", 15.0, false)
- elseif ZoneIsRed(zone.color) then
- table.insert(redTargetZones, zone)
- redTargetZones[zone.name] = CountUnitsInZone(zone, "red")
- redTargetZones[zone.name .. "init"] = redTargetZones[zone.name]
- redTargetAreaCount = redTargetAreaCount + 1
- redTargetCount = redTargetCount + redTargetZones[zone.name]
- redTargetZones[zone.name .. "lastAttackTime"] = timer.getTime()
- -- Spawn a red smoke flare on the zone.
- SpawnFlaresInCircle(mist.utils.zoneToVec3(zone), zone.radius, trigger.smokeColor.Red)
- -- DEBUG
- --trigger.action.outText("Initialized zone " .. zone.name .. " with " .. redTargetZones[zone.name] .. " units.", 15.0, false)
- end
- end
- trigger.action.outText("Mission initialized with " .. blueTargetAreaCount .. " blue and " .. redTargetAreaCount .. " red target areas, with " .. redTargetCount .. " red targets, and " .. blueTargetCount .. " blue targets.", 15.0, false)
- end
- -- Creates 6 flares in a circle pattern.
- function SpawnFlaresInCircle(point, radius, color)
- local offset = {x, y, z}
- for i = 0, 5, 1 do
- offset.x = point.x + (math.cos(math.rad(i * 60)) * radius)
- offset.y = 0
- offset.z = point.z + (math.sin(math.rad(i * 60)) * radius)
- -- Smoke flares aren't persistent, so create the flares repeatedly every 5 minutes.
- mist.scheduleFunction(trigger.action.smoke, {mist.utils.makeVec3GL(offset), color}, timer.getTime(), 300)
- end
- end
- function CountUnitsInZone(zone, side)
- countInZone = 0
- for i, unit in ipairs(mist.DBs.unitsByNum)
- do
- -- Only check ground units and units of the appropriate side.
- -- Also exclude FARPs because they're indestructible.
- if unit.coalition == side and (unit.category == "vehicle" or (unit.category == "static" and unit.type ~= "FARP") or unit.category == "ship") then
- if WithinDistance(unit.point.x, unit.point.y, zone.point.x, zone.point.z, zone.radius) then
- countInZone = countInZone + 1
- end
- end
- end
- return countInZone
- end
- function WithinDistance(x1, y1, x2, y2, distance)
- local xd = x1 - x2
- local yd = y1 - y2
- local distanceToZoneCenter = math.sqrt(xd*xd + yd*yd)
- if distanceToZoneCenter < distance then
- return true
- else
- return false
- end
- end
- function ZoneIsRed(color)
- if color[1] == 1 and color[2] == 0 and color[3] == 0 then
- return true
- else
- return false
- end
- end
- function ZoneIsBlue(color)
- if color[1] == 0 and color[2] == 0 and color[3] == 1 then
- return true
- else
- return false
- end
- end
- -- Mission update text.
- function PublishMissionStatsToAll()
- -- Timer.
- local stats = "Mission time: " .. SecondsToClock(timer.getTime())
- trigger.action.outText(stats, 30.0, false)
- -- Blue team report.
- stats = "Blue team report:\n\n"
- stats = stats .. GenerateSideReport(blueTargetZones, bluePilots, bluePlanes)
- trigger.action.outText(stats, 30.0, false)
- -- Red team report.
- stats = "Red team report:\n\n"
- stats = stats .. GenerateSideReport(redTargetZones, redPilots, redPlanes)
- trigger.action.outText(stats, 30.0, false)
- -- Publish the final string and queue up the next update.
- publishStatsFunction = mist.scheduleFunction(PublishMissionStatsToAll, {}, timer.getTime() + TIME_BETWEEN_MISSION_STATS)
- end
- -- Generates a string of the format:
- -- Soviet FARP is at 100%
- -- 25 pilots and 50 planes are available.
- function GenerateSideReport(targetZones, pilots, planes)
- local stats = ""
- local percentTGT = ""
- for i, zone in ipairs(targetZones) do
- percentTGT = PercentTargetsLeftInZone(targetZones, zone.name)
- if percentTGT == "0%" then
- stats = stats .. "\"" .. zone.name .. "\" has been completely destroyed!\n"
- else
- stats = stats .. string.format("\"%s\" is at %s effectiveness. (%u/%u)\n", zone.name, percentTGT, targetZones[zone.name], targetZones[zone.name .. "init"])
- end
- end
- return stats .. string.format("\n%u pilots and %u planes are available.", pilots, planes)
- end
- -- Adapted from TJ_Tigger post from http://www.indigorose.com/forums/archive/index.php/t-14669.html
- function SecondsToClock(sSeconds)
- local nSeconds = tonumber(sSeconds)
- --return nil;
- if nSeconds == 0 then
- return "00:00:00";
- else
- nHours = string.format("%02.f", math.floor(nSeconds/3600));
- nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
- nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
- return nHours..":"..nMins..":"..nSecs
- end
- end
- function CheckObjectiveWinConditions()
- local winText = ""
- -- When a pilot and plane die at the same time, they can generate duplicate game over screens.
- -- Protect against that.
- if gameOver == false then
- -- DEBUG
- --outString = string.format("Performing test: Red has %u units left", redTargetCount)
- --trigger.action.outText(outString, 30.0, false)
- -- Blue win conditions
- if redTargetCount == 0 or redPilots == 0 or redPlanes == 0 then
- gameOver = true
- if (redPilots == 0) then
- winText = "============================================\n============================================\n\nBLUE HAS WON THE ROUND!\nAll red pilots were killed!\n\n============================================\n============================================"
- elseif (redPlanes == 0) then
- winText = "============================================\n============================================\n\nBLUE HAS WON THE ROUND!\nAll red aircaft were destroyed!\n\n============================================\n============================================"
- else
- winText = "============================================\n============================================\n\nBLUE HAS WON THE ROUND!\nAll red ground targets were destroyed!\n\n============================================\n============================================"
- end
- end
- -- Red win conditions
- if blueTargetCount == 0 or bluePilots == 0 or bluePlanes == 0 then
- gameOver = true
- if (bluePilots == 0) then
- winText = "============================================\n============================================\n\nRED HAS WON THE ROUND!\nAll blue pilots were killed!\n\n============================================\n============================================"
- elseif (bluePlanes == 0) then
- winText = "============================================\n============================================\n\nRED HAS WON THE ROUND!\nAll blue aircaft were destroyed!\n\n============================================\n============================================"
- else
- winText = "============================================\n============================================\n\nRED HAS WON THE ROUND!\nAll blue ground targets were destroyed!\n\n============================================\n============================================"
- end
- end
- -- End the mission if somebody won.
- if gameOver then
- -- Win text
- trigger.action.outText(winText, 30.0, true)
- -- Mission end stats
- --local stats = "Mission time: " .. SecondsToClock(timer.getTime()) .. "\Final mission report:\n"
- --trigger.action.outText(stats, 30.0, false)
- local stats = "\Final mission stats:"
- trigger.action.outText(stats, 30.0, false)
- -- Blue team report.
- stats = "Blue team:\n\n"
- stats = stats .. GenerateSideReport(blueTargetZones, bluePilots, bluePlanes)
- trigger.action.outText(stats, 30.0, false)
- -- Red team report.
- stats = "Red team:\n\n"
- stats = stats .. GenerateSideReport(redTargetZones, redPilots, redPlanes)
- trigger.action.outText(stats, 30.0, false)
- mist.removeFunction(publishStatsFunction)
- mist.scheduleFunction(trigger.action.outText, {"Ending the mission in 30 seconds.", 30.0, false}, timer.getTime() + 5)
- mist.scheduleFunction(EndTheMission, {}, timer.getTime() + 35)
- end
- end
- end
- -- Set the flag "1" to true. In the mission itself, a trigger needs to check when this is set to true
- -- and then end the mission.
- function EndTheMission()
- trigger.action.setUserFlag("1", true)
- end
- function NumberOfElementsInTable(iTable)
- local count = 0
- for i, element in ipairs(iTable) do
- count = count + 1
- end
- return count
- end
- -- END FUNCTION DEFINITIONS ================================
- -- MISSION START
- mist.addEventHandler(OnPlayerEnterUnit)
- mist.addEventHandler(OnPlayerExitUnit)
- mist.addEventHandler(OnVehicleDestroyed)
- mist.addEventHandler(OnAircraftCrashed)
- mist.addEventHandler(OnPilotKilled)
- InitGroundTargetCounts()
- -- Every minute, update mission stats.
- publishStatsFunction = mist.scheduleFunction(PublishMissionStatsToAll, {}, timer.getTime())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement