diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index cd05cfd..bb0b1d8 100644 Binary files a/Doc/DML Documentation.pdf and b/Doc/DML Documentation.pdf differ diff --git a/Doc/DML Quick Reference.pdf b/Doc/DML Quick Reference.pdf index 2d268cf..cdbf059 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index 9e5333a..326bee7 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -5,7 +5,7 @@ -- Copyright (c) 2021, 2022 by Christian Franz and cf/x AG -- cfxZones = {} -cfxZones.version = "2.7.8" +cfxZones.version = "2.7.9" --[[-- VERSION HISTORY - 2.2.4 - getCoalitionFromZoneProperty - getStringFromZoneProperty @@ -78,6 +78,8 @@ cfxZones.version = "2.7.8" - doPollFlag supports 'pulse' - pulseFlag - unpulse +- 2.7.9 - getFlagValue QoL for + - setFlagValue QoL for --]]-- @@ -1244,6 +1246,11 @@ function cfxZones.setFlagValue(theFlag, theValue, theZone) return end + -- some QoL: detect "" + if dcsCommon.containsString(theFlag, "") then + trigger.action.outText("+++Zone: warning - setFlag has '' flag name in zone <" .. zoneName .. ">", 30) + end + -- now do wildcard processing. we have alphanumeric if dcsCommon.stringStartsWith(theFlag, "*") then theFlag = zoneName .. theFlag @@ -1271,6 +1278,11 @@ function cfxZones.getFlagValue(theFlag, theZone) return tonumber(trigger.misc.getUserFlag(theFlag)) end + -- some QoL: detect "" + if dcsCommon.containsString(theFlag, "") then + trigger.action.outText("+++Zone: warning - getFlag has '' flag name in zone <" .. zoneName .. ">", 30) + end + -- now do wildcard processing. we have alphanumeric if dcsCommon.stringStartsWith(theFlag, "*") then theFlag = zoneName .. theFlag diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index 32e966e..05e70f8 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.6.3" +dcsCommon.version = "2.6.4" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -73,6 +73,7 @@ dcsCommon.version = "2.6.3" 2.6.1 - removed bug in rotateUnitData: cy --> cz param passing 2.6.2 - new combineTables() 2.6.3 - new tacan2freq() + 2.6.4 - new processHMS() --]]-- @@ -1992,11 +1993,44 @@ dcsCommon.version = "2.6.3" end return 1087000000 + offset -- mode x end + + function dcsCommon.processHMS(msg, delta) + local rS = math.floor(delta) + local remainS = tostring(rS) + local rM = math.floor(delta/60) + local remainM = tostring(rM) + local rH = math.floor(delta/3600) + local remainH = tostring(rH) + local hmsH = remainH + if rH < 10 then hmsH = "0" .. hmsH end + + local hmsCount = delta - (rH * 3600) -- mins left + local mins = math.floor (hmsCount / 60) + local hmsM = tostring(mins) + if mins < 10 then hmsM = "0" .. hmsM end + + hmsCount = hmsCount - (mins * 60) + local secs = math.floor(hmsCount) + local hmsS = tostring(secs) + if secs < 10 then hmsS = "0" .. hmsS end + + msg = string.gsub(msg, "", remainS) + msg = string.gsub(msg, "", remainM) + msg = string.gsub(msg, "", remainH) + + msg = string.gsub(msg, "<:s>", hmsS) + msg = string.gsub(msg, "<:m>", hmsM) + msg = string.gsub(msg, "<:h>", hmsH) + + return msg + end + -- -- -- V E C T O R M A T H -- -- + function dcsCommon.vAdd(a, b) local r = {} if not a then a = {x = 0, y = 0, z = 0} end diff --git a/modules/delicates.lua b/modules/delicates.lua index d23abcc..a746766 100644 --- a/modules/delicates.lua +++ b/modules/delicates.lua @@ -1,5 +1,5 @@ delicates = {} -delicates.version = "0.0.0" +delicates.version = "1.0.0" delicates.verbose = false delicates.ups = 1 delicates.requiredLibs = { @@ -7,9 +7,11 @@ delicates.requiredLibs = { "cfxZones", -- Zones, of course } delicates.theDelicates = {} +delicates.inventory = {} --[[-- Version History + 1.0.0 - initial version --]]-- function delicates.adddDelicates(theZone) @@ -35,13 +37,11 @@ function delicates.objectHandler(theObject, theCollector) return true end -function delicates.seeZoneInventory(theZone) - -- run a diag which objects are in the zone, and which cat they are - -- set up args - local allCats = {1, 2, 3, 4, 5, 6} +function delicates.makeZoneInventory(theZone) + + local allCats = {1, 3, 6} -- Object.Category UNIT=1, WEAPON=2, STATIC=3, BASE=4, SCENERY=5, Cargo=6 - delicates.inventory = "" - theZone.inventory = {} + for idx, aCat in pairs(allCats) do local p = cfxZones.getPoint(theZone) local lp = {x = p.x, y = p.z} @@ -60,17 +60,31 @@ function delicates.seeZoneInventory(theZone) -- now call search world.searchObjects(aCat, args, delicates.objectHandler, collector) -- process results - if #collector>0 then - trigger.action.outText("+++deli: zone " .. theZone.name, 30) + if #collector > 0 then + if theZone.verbose or delicates.verbose then + trigger.action.outText("+++deli: zone " .. theZone.name, 30) + end + for idy, anObject in pairs(collector) do local oName = anObject:getName() if type(oName) == 'number' then oName = tostring(oName) end - trigger.action.outText("+++deli: cat=".. aCat .. ":<" .. anObject:getName() .. ">", 30) + local oLife = anObject:getLife() + if theZone.verbose or delicates.verbose then + trigger.action.outText("+++deli: cat=".. aCat .. ":<" .. oName .. "> Life=" .. oLife, 30) + end local uP = anObject:getPoint() if cfxZones.isPointInsideZone(uP, theZone) then - table.insert(theZone.inventory, oName) + + local desc = {} + desc.cat = aCat + desc.oLife = oLife + desc.theZone = theZone + desc.oName = oName + delicates.inventory[oName] = desc else - trigger.action.outText("+++deli: (dropped)", 30) + if theZone.verbose or delicates.verbose then + trigger.action.outText("+++deli: (dropped)", 30) + end end end end @@ -78,11 +92,30 @@ function delicates.seeZoneInventory(theZone) end function delicates.createDelicatesWithZone(theZone) + theZone.power = cfxZones.getNumberFromZoneProperty(theZone, "power", 10) + + if cfxZones.hasProperty(theZone, "delicatesHit!") then + theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "delicatesHit!", "*") + end + if cfxZones.hasProperty(theZone, "f!") then + theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "f!", "*") + end + if cfxZones.hasProperty(theZone, "out!") then + theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "out!", "*") + end + + -- DML Method + theZone.delicateHitMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") + if cfxZones.hasProperty(theZone, "delicateMethod") then + theZone.delicateHitMethod = cfxZones.getStringFromZoneProperty(theZone, "delicatesMethod", "inc") + end + + + theZone.delicateRemove = cfxZones.getBoolFromZoneProperty(theZone, "remove", true) + -- read objects for this zone -- may want to filter by objects, can be passed in delicates - delicates.seeZoneInventory(theZone) - - + delicates.makeZoneInventory(theZone) if delicates.verbose or theZone.verbose then trigger.action.outText("+++deli: new delicates zone <".. theZone.name ..">", 30) @@ -90,19 +123,130 @@ function delicates.createDelicatesWithZone(theZone) end +-- +-- blow me! +-- +function delicates.scheduledBlow(args) + local desc = args.desc + if not desc then return end + local oName = desc.oName + + local theObject = nil + -- UNIT=1, WEAPON=2, STATIC=3, BASE=4, SCENERY=5, Cargo=6 + if desc.cat == 1 then + theObject = Unit.getByName(oName) + if not theObject then + theObject = StaticObject.getByName(oName) + end + elseif desc.cat == 3 or desc.cat == 6 then + theObject = StaticObject.getByName(oName) + else + -- can't handle at the moment + end + + local theZone = desc.theZone + local p = theObject:getPoint() + local power = desc.theZone.power + if desc.theZone.delicateRemove then + theObject:destroy() + end + + trigger.action.explosion(p, power) + + -- bang out! + if theZone.delicateHit then + cfxZones.pollFlag(theZone.delicateHit, theZone.delicateHitMethod, theZone) + if delicates.verbose or theZone.verbose then + trigger.action.outText("+++deli: banging delicateHit! with <" .. theZone.delicateHitMethod .. "> on <" .. theZone.delicateHit .. "> for " .. theZone.name, 30) + end + end +end + +function delicates.blowUpObject(desc, delay) + if not delay then delay = 0.5 end + if not desc then return end + local args = {} + args.desc = desc + timer.scheduleFunction(delicates.scheduledBlow, args, timer.getTime() + delay) + +end + + -- -- event handler -- function delicates:onEvent(theEvent) - trigger.action.outText("yup", 30) +-- trigger.action.outText("yup", 30) if not theEvent then return end + local theObj = theEvent.target + if not theObj then return end if theEvent.id ~= 2 and theEvent.id ~= 23 then return end -- only hit and shooting start events - if not theEvent.target then return end + + local oName = theObj:getName() + local desc = delicates.inventory[oName] + if desc then +-- trigger.action.outText("+++deli: REGISTERED HIT -- removing!", 30) + delicates.blowUpObject(desc) + -- remove it from further searches + delicates.inventory[oName] = nil + end - trigger.action.outText("+++deli: we hit " .. theEvent.target:getName(), 30) +-- trigger.action.outText("+++deli: we hit " .. oName, 30) end +-- +-- Update +--- + +function delicates.update() + -- call me in a second to poll triggers + timer.scheduleFunction(delicates.update, {}, timer.getTime() + 1/delicates.ups) + + -- see if any deli was damaged and filter for next iter + local newInventory = {} + for oName, oDesc in pairs(delicates.inventory) do + -- access the object + local theObj = nil + -- UNIT=1, WEAPON=2, STATIC=3, BASE=4, SCENERY=5, Cargo=6 + if oDesc.cat == 1 then + theObj = Unit.getByName(oName) + if not theObj then + -- DCS now changes objects to static + -- so see if we can get this under statics + theObj = StaticObject.getByName(oName) + if theObj then +-- trigger.action.outText("+++deli: Aha! caught smokin'!", 30) + end + end + elseif oDesc.cat == 3 or oDesc.cat == 6 then + theObj = StaticObject.getByName(oName) + else + -- can't handle at the moment + end + + if theObj then + local cLife = theObj:getLife() + if cLife >= oDesc.oLife then + -- transfer to next iter + newInventory[oName] = oDesc + else + -- blow stuff up + if oDesc.theZone.verbose or delicates.verbose then + trigger.action.outText(oName .. " was hit, will blow up, new health is at " .. oDesc.oLife .. ".", 30) + end + delicates.blowUpObject(oDesc) + end + else + -- nothing to do, don't transfer + if oDesc.theZone.verbose or delicates.verbose then + trigger.action.outText("+++deli: <" .. oName .. "> disappeared.", 30) + end + end + end + delicates.inventory = newInventory +end + -- -- Config & Start -- @@ -144,7 +288,7 @@ function delicates.start() end -- start update - --delicates.update() + delicates.update() -- listen for events world.addEventHandler(delicates) @@ -157,4 +301,7 @@ end if not delicates.start() then trigger.action.outText("cfx delicates aborted: missing libraries", 30) delicates = nil -end \ No newline at end of file +end + +-- To Do: +-- integrate with cloners and spawners \ No newline at end of file diff --git a/modules/messenger.lua b/modules/messenger.lua index 108a1a5..5a499c2 100644 --- a/modules/messenger.lua +++ b/modules/messenger.lua @@ -1,5 +1,5 @@ messenger = {} -messenger.version = "1.3.0" +messenger.version = "1.3.1" messenger.verbose = false messenger.requiredLibs = { "dcsCommon", -- always @@ -21,6 +21,8 @@ messenger.messengers = {} 1.2.0 - msgTriggerMethod (original Watchflag integration) 1.2.1 - qoL: = newline, = zone name, = value 1.3.0 - messenger? saves messageOut? attribute + 1.3.1 - message now can interpret value as time with <:h> <:m> <:s> + --]]-- function messenger.addMessenger(theZone) @@ -156,7 +158,9 @@ function messenger.getMessage(theZone) msg = string.gsub(msg, "*name", zName) msg = string.gsub(msg, "*value", zVal) msg = string.gsub(msg, "", zVal) - + local z = tonumber(zVal) + if not z then z = 0 end + msg = dcsCommon.processHMS(msg, z) return msg end diff --git a/modules/radioMenus.lua b/modules/radioMenus.lua new file mode 100644 index 0000000..a4be3b6 --- /dev/null +++ b/modules/radioMenus.lua @@ -0,0 +1,255 @@ +radioMenu = {} +radioMenu.version = "1.0.0" +radioMenu.verbose = false +radioMenu.ups = 1 +radioMenu.requiredLibs = { + "dcsCommon", -- always + "cfxZones", -- Zones, of course +} +radioMenu.menus = {} + +--[[-- + Version History + 1.0.0 Initial version + +--]]-- +function radioMenu.addRadioMenu(theZone) + table.insert(radioMenu.menus, theZone) +end + +function radioMenu.getRadioMenuByName(aName) + for idx, aZone in pairs(radioMenu.menus) do + if aName == aZone.name then return aZone end + end + if radioMenu.verbose then + trigger.action.outText("+++radioMenu: no radioMenu with name <" .. aName ..">", 30) + end + + return nil +end + +-- +-- read zone +-- +function radioMenu.createRadioMenuWithZone(theZone) + local rootName = cfxZones.getStringFromZoneProperty(theZone, "radioMenu", "") + + theZone.coalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0) + + if theZone.coalition == 0 then + theZone.rootMenu = missionCommands.addSubMenu(rootName, nil) + else + theZone.rootMenu = missionCommands.addSubMenuForCoalition(theZone.coalition, rootName, nil) + end + + -- now do the two options + local menuA = cfxZones.getStringFromZoneProperty(theZone, "itemA", "") + if theZone.coalition == 0 then + theZone.menuA = missionCommands.addCommand(menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"}) + else + theZone.menuA = missionCommands.addCommandForCoalition(theZone.coalition, menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"}) + end + + if cfxZones.hasProperty(theZone, "itemB") then + local menuB = cfxZones.getStringFromZoneProperty(theZone, "itemB", "") + if theZone.coalition == 0 then + theZone.menuB = missionCommands.addCommand(menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"}) + else + theZone.menuB = missionCommands.addCommandForCoalition(theZone.coalition, menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"}) + end + end + + if cfxZones.hasProperty(theZone, "itemC") then + local menuC = cfxZones.getStringFromZoneProperty(theZone, "itemC", "") + if theZone.coalition == 0 then + theZone.menuC = missionCommands.addCommand(menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"}) + else + theZone.menuC = missionCommands.addCommandForCoalition(theZone.coalition, menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"}) + end + end + + if cfxZones.hasProperty(theZone, "itemD") then + local menuD = cfxZones.getStringFromZoneProperty(theZone, "itemD", "") + if theZone.coalition == 0 then + theZone.menuD = missionCommands.addCommand(menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"}) + else + theZone.menuD = missionCommands.addCommandForCoalition(theZone.coalition, menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"}) + end + end + + + -- get the triggers & methods here + theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") + if cfxZones.hasProperty(theZone, "radioMethod") then + theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "radioMethod", "inc") + end + + theZone.itemAChosen = cfxZones.getStringFromZoneProperty(theZone, "A!", "*") + theZone.cooldownA = cfxZones.getNumberFromZoneProperty(theZone, "cooldownA", 0) + theZone.mcdA = 0 + theZone.busyA = cfxZones.getStringFromZoneProperty(theZone, "busyA", "Please stand by ( seconds)") + + theZone.itemBChosen = cfxZones.getStringFromZoneProperty(theZone, "B!", "*") + theZone.cooldownB = cfxZones.getNumberFromZoneProperty(theZone, "cooldownB", 0) + theZone.mcdB = 0 + theZone.busyB = cfxZones.getStringFromZoneProperty(theZone, "busyB", "Please stand by ( seconds)") + + theZone.itemCChosen = cfxZones.getStringFromZoneProperty(theZone, "C!", "*") + theZone.cooldownC = cfxZones.getNumberFromZoneProperty(theZone, "cooldownC", 0) + theZone.mcdC = 0 + theZone.busyC = cfxZones.getStringFromZoneProperty(theZone, "busyC", "Please stand by ( seconds)") + + theZone.itemDChosen = cfxZones.getStringFromZoneProperty(theZone, "D!", "*") + theZone.cooldownD = cfxZones.getNumberFromZoneProperty(theZone, "cooldownD", 0) + theZone.mcdD = 0 + theZone.busyD = cfxZones.getStringFromZoneProperty(theZone, "busyD", "Please stand by ( seconds)") + + if radioMenu.verbose or theZone.verbose then + trigger.action.outText("+++radioMenu: new radioMenu zone <".. theZone.name ..">", 30) + end + +end + +-- +-- Output processing +-- +function radioMenu.radioOutMessage(theMessage, theZone) + if not theZone then return end + c = theZone.coalition + if c > 0 then + trigger.action.outTextForCoalition(c, theMessage, 30) + else + trigger.action.outText(theMessage, 30) + end +end + +function radioMenu.processHMS(msg, delta) + -- moved to dcsCommon + return dcsCommon.processHMS(msg, delta) +end + + +-- +-- Menu Branching +-- +function radioMenu.redirectMenuX(args) + -- we use indirection to be able to debug code better + timer.scheduleFunction(radioMenu.doMenuX, args, timer.getTime() + 0.1) +end + +function radioMenu.doMenuX(args) + theZone = args[1] + theItemIndex = args[2] -- A, B , C .. ? + local cd = theZone.mcdA + local busy = theZone.busyA + local theFlag = theZone.itemAChosen + + -- decode A..X + if theItemIndex == "B"then + cd = theZone.mcdB + busy = theZone.busyB + theFlag = theZone.itemBChosen + elseif theItemIndex == "C" then + cd = theZone.mcdC + busy = theZone.busyC + theFlag = theZone.itemCChosen + elseif theItemIndex == "D" then + cd = theZone.mcdD + busy = theZone.busyD + theFlag = theZone.itemDChosen + end + + -- see if we are on cooldown + local now = timer.getTime() + if now < cd then + -- we are on cooldown. + local msg = radioMenu.processHMS(busy, cd - now) + radioMenu.radioOutMessage(msg, theZone) + return + end + + -- set new cooldown -- needs own decoder A..X + if theItemIndex == "A" then + theZone.mcdA = now + theZone.cooldownA + elseif theItemIndex == "B" then + theZone.mcdB = now + theZone.cooldownB + elseif theItemIndex == "C" then + theZone.mcdC = now + theZone.cooldownC + else + theZone.mcdD = now + theZone.cooldownD + end + + cfxZones.pollFlag(theFlag, theZone.radioMethod, theZone) + if theZone.verbose or radioMenu.verbose then + trigger.action.outText("+++menu: banging D! with <" .. theZone.radioMethod .. "> on <" .. theFlag .. "> for " .. theZone.name, 30) + end + +end +-- +-- Update -- required when we can enable/disable a zone's menu +-- +--[[-- +function radioMenu.update() + -- call me in a second to poll triggers + timer.scheduleFunction(radioMenu.update, {}, timer.getTime() + 1/radioMenu.ups) + +end +--]]-- + +-- +-- Config & Start +-- +function radioMenu.readConfigZone() + local theZone = cfxZones.getZoneByName("radioMenuConfig") + if not theZone then + if radioMenu.verbose then + trigger.action.outText("+++radioMenu: NO config zone!", 30) + end + return + end + + radioMenu.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + + if radioMenu.verbose then + trigger.action.outText("+++radioMenu: read config", 30) + end +end + +function radioMenu.start() + -- lib check + if not dcsCommon.libCheck then + trigger.action.outText("cfx radioMenu requires dcsCommon", 30) + return false + end + if not dcsCommon.libCheck("cfx radioMenu", radioMenu.requiredLibs) then + return false + end + + -- read config + radioMenu.readConfigZone() + + -- process radioMenu Zones + -- old style + local attrZones = cfxZones.getZonesWithAttributeNamed("radioMenu") + for k, aZone in pairs(attrZones) do + radioMenu.createRadioMenuWithZone(aZone) -- process attributes + radioMenu.addRadioMenu(aZone) -- add to list + end + + -- start update + --radioMenu.update() + + trigger.action.outText("cfx radioMenu v" .. radioMenu.version .. " started.", 30) + return true +end + +-- let's go! +if not radioMenu.start() then + trigger.action.outText("cfx radioMenu aborted: missing libraries", 30) + radioMenu = nil +end + +--[[-- + to do: turn on/off via flags + callbacks for the menus +--]]-- \ No newline at end of file diff --git a/modules/wiper.lua b/modules/wiper.lua index 1f2d837..5a7f29a 100644 --- a/modules/wiper.lua +++ b/modules/wiper.lua @@ -1,5 +1,5 @@ wiper = {} -wiper.version = "1.0.0" +wiper.version = "1.1.0" wiper.verbose = false wiper.ups = 1 wiper.requiredLibs = { @@ -10,6 +10,8 @@ wiper.wipers = {} --[[-- Version History 1.0.0 - Initial Version + 1.1.0 - added zone bounds check before wiping + --]]-- @@ -124,7 +126,13 @@ function wiper.seeZoneInventory(theZone) world.searchObjects(aCat, args, wiper.objectHandler, collector) wiper.inventory = wiper.inventory .. "Cat = " .. aCat .. ":" for idy, anObject in pairs(collector) do - wiper.inventory = wiper.inventory .. anObject:getName() .. " " + -- now also filter by position in zone + local uP = anObject:getPoint() + if (not cfxZones.isPointInsideZone(uP, theZone)) then + wiper.inventory = wiper.inventory .. "{" .. anObject:getName() .. "} " + else + wiper.inventory = wiper.inventory .. anObject:getName() .. " " + end end wiper.inventory = wiper.inventory .. "\n" end @@ -188,6 +196,15 @@ function wiper.isTriggered(theZone) end end + -- now also filter by position in zone + local uP = anObject:getPoint() + if doWipe and (not cfxZones.isPointInsideZone(uP, theZone)) then + doWipe = false + if wiper.verbose or theZone.verbose then + trigger.action.outText("+++wpr: <" .. anObject:getName() .."> not removed, outside zone <" .. theZone.name .. "> bounds.",30) + end + end + if doWipe then if wiper.verbose or theZone.verbose then trigger.action.outText("+++wpr: wiping " .. anObject:getName(), 30) diff --git a/tutorial & demo missions/demo - Forever-looping spawns.miz b/tutorial & demo missions/demo - Forever-looping spawns.miz new file mode 100644 index 0000000..f086f3f Binary files /dev/null and b/tutorial & demo missions/demo - Forever-looping spawns.miz differ diff --git a/tutorial & demo missions/demo - Reinforcements A La Carte.miz b/tutorial & demo missions/demo - Reinforcements A La Carte.miz new file mode 100644 index 0000000..d65a2a1 Binary files /dev/null and b/tutorial & demo missions/demo - Reinforcements A La Carte.miz differ diff --git a/tutorial & demo missions/demo - delicate subjects.miz b/tutorial & demo missions/demo - delicate subjects.miz new file mode 100644 index 0000000..2f844ec Binary files /dev/null and b/tutorial & demo missions/demo - delicate subjects.miz differ diff --git a/tutorial & demo missions/demo - viper with a double youu.miz b/tutorial & demo missions/demo - viper with a double youu.miz index c5280d9..e19780e 100644 Binary files a/tutorial & demo missions/demo - viper with a double youu.miz and b/tutorial & demo missions/demo - viper with a double youu.miz differ