This commit is contained in:
spencer-ki 2022-02-04 12:13:08 -08:00
parent fe4b241efc
commit 315b226014
30 changed files with 180 additions and 79 deletions

View File

@ -177,22 +177,28 @@ class Window(QMainWindow, Ui_MainWindow):
if result["success"]:
print(result["filename"] + "' successfully generated in " + result["directory"])
self.statusbar.showMessage(result["filename"] + "' successfully generated in " + result["directory"], 10000)
msg = QMessageBox()
msg.setWindowTitle("Mission Generated")
msg.setText("Awesome, your mission is ready! It's located in this directory: \n" +
self.m.output_dir + "\n" +
"\n" +
"Next, you should use the DCS Mission Editor to fine tune unit placements. Don't be afraid to edit the missions that this generator produces. \n" +
"\n" +
"There are no hidden script changes, everything is visible in the ME. Triggers have been created to help you to add your own actions based on active zone and game status. \n" +
"\n" +
"Units can be changed or moved without issue. Player slots can be changed or moved without issue. \n" +
"\n" +
"Don't forget, you can also create your own templates that can include any mission options, objects, or even scripts. \n" +
"\n" +
"Have fun! \n"
)
x = msg.exec_()
msg = QMessageBox()
msg.setWindowTitle("Mission Generated")
msg.setText("Awesome, your mission is ready! It's located in this directory: \n" +
self.m.output_dir + "\n" +
"\n" +
"Next, you should use the DCS Mission Editor to fine tune unit placements. Don't be afraid to edit the missions that this generator produces. \n" +
"\n" +
"There are no hidden script changes, everything is visible in the ME. Triggers have been created to help you to add your own actions based on active zone and game status. \n" +
"\n" +
"Units can be changed or moved without issue. Player slots can be changed or moved without issue. \n" +
"\n" +
"Don't forget, you can also create your own templates that can include any mission options, objects, or even scripts. \n" +
"\n" +
"Have fun! \n"
)
x = msg.exec_()
elif not result["success"]:
print(result["failure_msg"])
msg = QMessageBox()
msg.setWindowTitle("Error")
msg.setText(result["failure_msg"])
x = msg.exec_()

View File

@ -139,12 +139,14 @@ class Ui_MainWindow(object):
font.setPointSize(11)
self.awacs_checkBox.setFont(font)
self.awacs_checkBox.setStatusTip("")
self.awacs_checkBox.setChecked(True)
self.awacs_checkBox.setObjectName("awacs_checkBox")
self.tankers_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.tankers_checkBox.setGeometry(QtCore.QRect(970, 450, 251, 31))
font = QtGui.QFont()
font.setPointSize(11)
self.tankers_checkBox.setFont(font)
self.tankers_checkBox.setChecked(True)
self.tankers_checkBox.setObjectName("tankers_checkBox")
self.apcs_spawn_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.apcs_spawn_checkBox.setGeometry(QtCore.QRect(500, 400, 251, 31))
@ -242,7 +244,7 @@ class Ui_MainWindow(object):
self.e_attack_planes_spinBox.setFont(font)
self.e_attack_planes_spinBox.setMinimum(0)
self.e_attack_planes_spinBox.setMaximum(50)
self.e_attack_planes_spinBox.setProperty("value", 2)
self.e_attack_planes_spinBox.setProperty("value", 1)
self.e_attack_planes_spinBox.setObjectName("e_attack_planes_spinBox")
self.zone_sams_checkBox = QtWidgets.QCheckBox(self.centralwidget)
self.zone_sams_checkBox.setGeometry(QtCore.QRect(970, 480, 201, 31))
@ -350,11 +352,11 @@ class Ui_MainWindow(object):
self.force_offroad_checkBox.setStatusTip(_translate("MainWindow", "May help prevent long travel times or pathfinding issues. Tip: You can change this dynamically from mission triggers."))
self.force_offroad_checkBox.setText(_translate("MainWindow", "Force Offroad"))
self.defense_checkBox.setText(_translate("MainWindow", "Defensive Mode"))
self.e_attack_helos_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.e_attack_helos_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack helicopter group spawns."))
self.scenario_label_7.setText(_translate("MainWindow", "Enemy Attack Helicopters"))
self.scenario_label_8.setText(_translate("MainWindow", "Enemy Attack Planes"))
self.e_attack_planes_spinBox.setStatusTip(_translate("MainWindow", "This value is multiplied by the number of spawn zones in the mission template."))
self.zone_sams_checkBox.setStatusTip(_translate("MainWindow", "Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed by a large explosion at zone center. "))
self.e_attack_planes_spinBox.setStatusTip(_translate("MainWindow", "Approximate number of enemy attack plane group spawns."))
self.zone_sams_checkBox.setStatusTip(_translate("MainWindow", "Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed."))
self.zone_sams_checkBox.setText(_translate("MainWindow", "Inactive Zone SAMs"))
self.action_generateMission.setText(_translate("MainWindow", "_generateMission"))
self.action_scenarioSelected.setText(_translate("MainWindow", "_scenarioSelected"))

View File

@ -662,7 +662,7 @@ p, li { white-space: pre-wrap; }
</font>
</property>
<property name="statusTip">
<string>This value is multiplied by the number of spawn zones in the mission template.</string>
<string>Approximate number of enemy attack helicopter group spawns.</string>
</property>
<property name="minimum">
<number>0</number>
@ -725,7 +725,7 @@ p, li { white-space: pre-wrap; }
</font>
</property>
<property name="statusTip">
<string>This value is multiplied by the number of spawn zones in the mission template.</string>
<string>Approximate number of enemy attack plane group spawns.</string>
</property>
<property name="minimum">
<number>0</number>
@ -734,7 +734,7 @@ p, li { white-space: pre-wrap; }
<number>50</number>
</property>
<property name="value">
<number>2</number>
<number>1</number>
</property>
</widget>
<widget class="QCheckBox" name="zone_sams_checkBox">
@ -752,7 +752,7 @@ p, li { white-space: pre-wrap; }
</font>
</property>
<property name="statusTip">
<string>Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed by a large explosion at zone center. </string>
<string>Inactive conflict zones will be protected by SAMs. When a zone is cleared, SAMs at next active zone will be destroyed.</string>
</property>
<property name="text">
<string>Inactive Zone SAMs</string>

View File

@ -10,16 +10,11 @@ import time
class RotorOpsMission:
conflict_zones = {}
staging_zones = {}
spawn_zones = {}
scripts = {}
def __init__(self):
self.m = dcs.mission.Mission()
self.old_blue = self.m.coalition.get("blue").dict()
self.old_red = self.m.coalition.get("red").dict()
os.chdir("../")
self.home_dir = os.getcwd()
self.scenarios_dir = self.home_dir + "\Generator\Scenarios"
@ -29,6 +24,11 @@ class RotorOpsMission:
self.output_dir = self.home_dir + "\Generator\Output"
self.assets_dir = self.home_dir + "\Generator/assets"
self.conflict_zones = {}
self.staging_zones = {}
self.spawn_zones = {}
self.scripts = {}
class RotorOpsZone:
def __init__(self, name: str, flag: int, position: dcs.point, size: int):
@ -94,9 +94,9 @@ class RotorOpsMission:
self.m.load_file(options["scenario_filename"])
#Load the default coalitions for simplicity
self.m.coalition.get("blue").load_from_dict(self.m, self.old_blue)
self.m.coalition.get("red").load_from_dict(self.m, self.old_red)
if not self.m.country("Russia") or not self.m.country("USA"):
failure_msg = "You must include a USA and Russia unit in the scenario template. See the instructions in " + self.scenarios_dir
return {"success": False, "failure_msg": failure_msg}
red_forces = self.getUnitsFromMiz(options["red_forces_filename"], "red")
@ -160,7 +160,7 @@ class RotorOpsMission:
self.addGroundGroups(blue_zones[zone_name], self.m.country('USA'), blue_forces,
options["blue_quantity"])
if options["zone_protect_sams"]:
if options["zone_protect_sams"] and options["defending"]:
for zone_name in blue_zones:
self.m.vehicle_group(
self.m.country('USA'),
@ -203,13 +203,14 @@ class RotorOpsMission:
if dcs.vehicles.vehicle_map[unit.type]:
unit_types.append(dcs.vehicles.vehicle_map[unit.type])
country = self.m.country(_country.name)
pos1 = zone.position.point_from_heading(5, 500)
#pos1 = zone.position.point_from_heading(5, 200)
#for i in range(0, quantity):
self.m.vehicle_group_platoon(
country,
zone.name + '-GND ' + str(a+1),
unit_types,
pos1.random_point_within(zone.size / 2, 500),
zone.position.random_point_within(zone.size / 1.2, 100),
#pos1.random_point_within(zone.size / 2.5, 100),
heading=random.randint(0, 359),
formation=dcs.unitgroup.VehicleGroup.Formation.Scattered,
)
@ -225,28 +226,70 @@ class RotorOpsMission:
def getParking(self, airport, aircraft):
slot = airport.free_parking_slot(aircraft)
slots = airport.free_parking_slots(aircraft)
if slot:
return airport
else:
print("No parking available for " + aircraft.id + " at " + airport.name)
return None
#Find parking spots on FARPs and carriers
def getUnitParking(self, aircraft):
return
def addSinglePlayerHelos(self, helotype):
carrier = self.m.country("USA").find_ship_group(name="HELO_CARRIER")
farp = self.m.country("USA").find_static_group("HELO_FARP")
friendly_airports = self.getCoalitionAirports("blue")
for airport_name in friendly_airports:
fg = self.m.flight_group_from_airport(self.m.country('USA'), "Player Helos", helotype,
self.m.terrain.airports[airport_name], group_size=2)
fg.units[0].set_player()
if carrier:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "CARRIER " + helotype.id, helotype, carrier, dcs.task.CAS, group_size=2)
elif farp:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "FARP " + helotype.id, helotype, farp, dcs.task.CAS, group_size=2)
fg.units[0].position = fg.units[0].position.point_from_heading(90, 30)
# invisible farps need manual unit placement for multiple units
if farp.units[0].type == 'Invisible FARP':
fg.points[0].action = dcs.point.PointAction.FromGroundArea
fg.points[0].type = "TakeOffGround"
fg.units[0].position = fg.units[0].position.point_from_heading(0, 30)
else:
for airport_name in friendly_airports:
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype,
self.getParking(self.m.terrain.airports[airport_name], helotype), group_size=2)
fg.units[0].set_player()
def addMultiplayerHelos(self):
carrier = self.m.country("USA").find_ship_group(name="HELO_CARRIER")
farp = self.m.country("USA").find_static_group("HELO_FARP")
friendly_airports = self.getCoalitionAirports("blue")
for airport_name in friendly_airports:
for helotype in RotorOpsUnits.client_helos:
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype,
self.getParking(self.m.terrain.airports[airport_name], helotype), group_size=1)
fg.units[0].set_client()
heading = 0
for helotype in RotorOpsUnits.client_helos:
if carrier:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "CARRIER " + helotype.id, helotype, carrier,
dcs.task.CAS, group_size=1)
elif farp:
fg = self.m.flight_group_from_unit(self.m.country('USA'), "FARP " + helotype.id, helotype, farp,
dcs.task.CAS, group_size=1)
#invisible farps need manual unit placement for multiple units
if farp.units[0].type == 'Invisible FARP':
fg.points[0].action = dcs.point.PointAction.FromGroundArea
fg.points[0].type = "TakeOffGround"
fg.units[0].position = fg.units[0].position.point_from_heading(heading, 30)
heading += 90
else:
for airport_name in friendly_airports:
fg = self.m.flight_group_from_airport(self.m.country('USA'), airport_name + " " + helotype.id, helotype,
self.getParking(self.m.terrain.airports[airport_name], helotype), group_size=1)
fg.units[0].set_client()
class TrainingScenario():
@ -361,6 +404,8 @@ class RotorOpsMission:
fg.add_runway_waypoint(enemy_airport)
fg.land_at(enemy_airport)
if options["e_attack_helos"]:
helo = random.choice(RotorOpsUnits.e_attack_helos)
afg = self.m.flight_group_from_airport(
@ -392,13 +437,13 @@ class RotorOpsMission:
game_flag = 100
#Add the first trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Scripts")
mytrig.rules.append(dcs.condition.TimeAfter(1))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["mist_4_4_90.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["Splash_Damage_2_0.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["CTLD.lua"]))
mytrig.actions.append(dcs.action.DoScriptFile(self.scripts["RotorOps.lua"]))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String((
trig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Scripts")
trig.rules.append(dcs.condition.TimeAfter(1))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["mist_4_4_90.lua"]))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["Splash_Damage_2_0.lua"]))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["CTLD.lua"]))
trig.actions.append(dcs.action.DoScriptFile(self.scripts["RotorOps.lua"]))
trig.actions.append(dcs.action.DoScript(dcs.action.String((
"--OPTIONS HERE!\n\n" +
"RotorOps.CTLD_crates = " + lb("crates") + "\n\n" +
"RotorOps.CTLD_sound_effects = true\n\n" +
@ -407,26 +452,26 @@ class RotorOpsMission:
"RotorOps.zone_status_display = " + lb("game_display") + "\n\n" +
"RotorOps.inf_spawns_per_zone = " + lb("inf_spawn_qty") + "\n\n" +
"RotorOps.apcs_spawn_infantry = " + lb("apc_spawns_inf") + " \n\n"))))
self.m.triggerrules.triggers.append(mytrig)
self.m.triggerrules.triggers.append(trig)
#Add the second trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Zones")
mytrig.rules.append(dcs.condition.TimeAfter(2))
trig = dcs.triggers.TriggerOnce(comment="RotorOps Setup Zones")
trig.rules.append(dcs.condition.TimeAfter(2))
for s_zone in self.staging_zones:
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.stagingZone('" + s_zone + "')")))
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.stagingZone('" + s_zone + "')")))
for c_zone in self.conflict_zones:
zone_flag = self.conflict_zones[c_zone].flag
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.addZone('" + c_zone + "'," + str(zone_flag) + ")")))
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.addZone('" + c_zone + "'," + str(zone_flag) + ")")))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.setupConflict('" + str(game_flag) + "')")))
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.setupConflict('" + str(game_flag) + "')")))
self.m.triggerrules.triggers.append(mytrig)
self.m.triggerrules.triggers.append(trig)
#Add the third trigger
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict Start")
mytrig.rules.append(dcs.condition.TimeAfter(10))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.startConflict(100)")))
self.m.triggerrules.triggers.append(mytrig)
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict Start")
trig.rules.append(dcs.condition.TimeAfter(10))
trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.startConflict(100)")))
self.m.triggerrules.triggers.append(trig)
#Add all zone-based triggers
for index, zone_name in enumerate(self.conflict_zones):
@ -453,7 +498,7 @@ class RotorOpsMission:
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag value represents the percentage of defending ground units remaining in zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("mist.respawnGroup('Enemy Attack Helicopters', true)")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackHelos()")))
self.m.triggerrules.triggers.append(z_weak_trig)
#Add attack plane triggers
@ -464,18 +509,18 @@ class RotorOpsMission:
z_weak_trig.rules.append(dcs.condition.FlagIsMore(zone.flag, 1))
z_weak_trig.rules.append(dcs.condition.FlagIsLess(zone.flag, random.randrange(20, 90)))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("---Flag value represents the percentage of defending ground units remaining in zone. ")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("mist.respawnGroup('Enemy Attack Planes', true)")))
z_weak_trig.actions.append(dcs.action.DoScript(dcs.action.String("RotorOps.spawnAttackPlanes()")))
self.m.triggerrules.triggers.append(z_weak_trig)
#Add game won/lost triggers
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON")
mytrig.rules.append(dcs.condition.FlagEquals(game_flag, 99))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is WON")))
self.m.triggerrules.triggers.append(mytrig)
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict WON")
trig.rules.append(dcs.condition.FlagEquals(game_flag, 99))
trig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is WON")))
self.m.triggerrules.triggers.append(trig)
mytrig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict LOST")
mytrig.rules.append(dcs.condition.FlagEquals(game_flag, 98))
mytrig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is LOST")))
self.m.triggerrules.triggers.append(mytrig)
trig = dcs.triggers.TriggerOnce(comment="RotorOps Conflict LOST")
trig.rules.append(dcs.condition.FlagEquals(game_flag, 98))
trig.actions.append(dcs.action.DoScript(dcs.action.String("---Add an action you want to happen when the game is LOST")))
self.m.triggerrules.triggers.append(trig)

View File

@ -1,20 +1,35 @@
You can add your own scenarios in this directory and they will appear in the mission generator.
You can add your own scenarios in this directory and they will appear in the mission generator. See the other scenario .miz files for examples of what to include in your template.
A scenario .miz file MUST have:
1) Between 1-4 trigger zones called "ALPHA", "BRAVO", "CHARLIE", "DELTA"
2) At least one trigger zone with a name that starts with "STAGING".
3) A blue airport (recommend somewhere near your staging zone).
4) A red airport (recommend somewhere near your last conflict zone).
3) A blue airport (recommend somewhere near/on-side your staging zone).
4) A red airport (recommend somewhere near/on-side your last conflict zone).
5) At least one Russian unit or static object. Anything will work, even a cow. You can set "HIDDEN ON MAP".
6) At least one USA unit or static object. See previous point.
Optional:
You can add smaller infantry spawning zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name them like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc.
7) USA FARP called "HELO_FARP" for automatic player placement.
8) USA Carrier called "HELO_CARRIER" for automatic player placement.
9) Infantry spawn zones inside conflict zones. Add near buildings to simulate infantry hiding within. Name trigger zones like "ALPHA_SPAWN", "ALPHA_SPAWN_2, etc.
Tips:
-Position the center of conflict zones over an open area, as this position may be used to spawn units.
-The conflict game type can be played with blue forces on defense. In this mode the last conflict zone is the only troop pickup zone.
-Design your template so that it can be played in normal 'attacking' mode or 'defending' the conflict zone from enemy ground units starting from the staging zone.
-Keep the zones fairly close together, both for helicopter and ground unit travel times.
-You can place static objects and units in a scenario template.
-You can change mission briefing and other mission options in the template.
-Drop your templates in the RotorOps Discord if you'd like to have them added in a release for everyone. Maintain a similar naming convention and be sure to credit yourself in the .miz name.
-Airfields can be captured with ground units. You might consider placing conflict zones over neutral airfields and adding unarmed client slots.
-Airfields can be captured with ground units. You might consider placing conflict zones over neutral airfields for a rearming area, or perhaps unarmed client slots.
-Player/client slots are placed in this priority order: Carrier named "HELO_CARRIER", FARP named "HELO_FARP", and the blue airfield.
-Friendly AWACs and tankers will be placed at the blue airport if parking is available, otherwise they will start in the air.
-Enemy helicopters and planes will spawn at the red airport.
-Late activation FARPs might be useful for rearming far from the player spawn point.

Binary file not shown.

View File

@ -23,7 +23,7 @@ RotorOps.inf_spawns_per_zone = 3 --number of infantry groups to spawn per zone
--RotorOps settings that are safe to change only before calling setupConflict()
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral'} --players flying these will have ctld transport access
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral', 'UH-60L'} --players flying these will have ctld transport access
RotorOps.CTLD_crates = false
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
RotorOps.exclude_ai_group_name = "noai" --include this somewhere in a group name to exclude the group from being tasked in the active zone
@ -138,6 +138,13 @@ RotorOps.gameMsgs = {
{'ENEMY TOOK CHARLIE!', 'enemy_destroying_us.ogg'},
{'ENEMY TOOK DELTA!', 'enemy_destroying_us.ogg'},
},
attack_helos = {
{'ENEMY ATTACK HELICOPTERS INBOUND!', 'enemy_attack_choppers.ogg'},
},
attack_planes = {
{'ENEMY ATTACK PLANES INBOUND!', 'enemy_attack_planes.ogg'},
},
}
@ -1243,6 +1250,32 @@ function RotorOps.startConflict()
end
function RotorOps.triggerSpawn(groupName, msg)
local group = Group.getByName(groupName)
if group and group:isExist() == true and #group:getUnits() > 0 and group:getUnits()[1]:getLife() > 1 and group:getUnits()[1]:isActive() then
env.info("RotorOps tried to respawn "..groupName.." but it's already active.")
else
local new_group = mist.respawnGroup(groupName, true)
if new_group then
RotorOps.gameMsg(msg)
env.info("RotorOps spawned "..groupName)
return new_group
end
end
return nil
end
function RotorOps.spawnAttackHelos()
RotorOps.triggerSpawn("Enemy Attack Helicopters", RotorOps.gameMsgs.attack_helos)
end
function RotorOps.spawnAttackPlanes()
RotorOps.triggerSpawn("Enemy Attack Planes", RotorOps.gameMsgs.attack_planes)
end

Binary file not shown.

BIN
sound/downed_pilot.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.