From 12b682ee4d81da5b4ff1f5b85f70343be260dd7a Mon Sep 17 00:00:00 2001 From: Moony Date: Wed, 9 Mar 2022 13:59:44 -0600 Subject: [PATCH] Tile variants content (#6956) --- .../Tests/SaveLoadMapTest.cs | 8 +-- Content.MapRenderer/Painters/TilePainter.cs | 17 +++--- .../Commands/VariantizeCommand.cs | 49 ++++++++++++++++++ .../Tiles/FloorTileItemComponent.cs | 5 +- Content.Shared/Maps/ContentTileDefinition.cs | 7 +++ .../commands/variantize-command.ftl | 2 + Resources/Locale/en-US/shell.ftl | 1 + Resources/Prototypes/Tiles/floors.yml | 48 +++++++++++------ .../Tiles/Asteroid/asteroid_coarse_sand.png | Bin 0 -> 1239 bytes .../Tiles/Asteroid/asteroid_coarse_sand0.png | Bin 221 -> 0 bytes .../Tiles/Asteroid/asteroid_coarse_sand1.png | Bin 229 -> 0 bytes .../Tiles/Asteroid/asteroid_coarse_sand2.png | Bin 228 -> 0 bytes Resources/Textures/Tiles/bar.png | Bin 4691 -> 2011 bytes Resources/Textures/Tiles/dark.png | Bin 615 -> 1579 bytes Resources/Textures/Tiles/steel.png | Bin 824 -> 2428 bytes Resources/Textures/Tiles/white.png | Bin 495 -> 1594 bytes Resources/Textures/Tiles/wood.png | Bin 573 -> 1448 bytes 17 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 Content.Server/Administration/Commands/VariantizeCommand.cs create mode 100644 Resources/Locale/en-US/administration/commands/variantize-command.ftl create mode 100644 Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png delete mode 100644 Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png delete mode 100644 Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png delete mode 100644 Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand2.png diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index 977002168c..7156ab6da4 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -40,13 +40,13 @@ namespace Content.IntegrationTests.Tests var mapGrid = mapManager.CreateGrid(mapId); var mapGridEnt = mapGrid.GridEntityId; sEntities.GetComponent(mapGridEnt).WorldPosition = new Vector2(10, 10); - mapGrid.SetTile(new Vector2i(0,0), new Tile(1, 512)); + mapGrid.SetTile(new Vector2i(0,0), new Tile(1, (TileRenderFlag)1, 255)); } { var mapGrid = mapManager.CreateGrid(mapId); var mapGridEnt = mapGrid.GridEntityId; sEntities.GetComponent(mapGridEnt).WorldPosition = new Vector2(-8, -8); - mapGrid.SetTile(new Vector2i(0, 0), new Tile(2, 511)); + mapGrid.SetTile(new Vector2i(0, 0), new Tile(2, (TileRenderFlag)1, 254)); } mapLoader.SaveMap(mapId, mapPath); @@ -68,14 +68,14 @@ namespace Content.IntegrationTests.Tests Assert.That(mapGrid.WorldPosition, Is.EqualTo(new Vector2(10, 10))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, 512))); + Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag)1, 255))); } { if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(-8, -8), out var mapGrid)) Assert.Fail(); Assert.That(mapGrid.WorldPosition, Is.EqualTo(new Vector2(-8, -8))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, 511))); + Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag)1, 254))); } }); diff --git a/Content.MapRenderer/Painters/TilePainter.cs b/Content.MapRenderer/Painters/TilePainter.cs index e7a35017c0..19acd467fa 100644 --- a/Content.MapRenderer/Painters/TilePainter.cs +++ b/Content.MapRenderer/Painters/TilePainter.cs @@ -44,7 +44,7 @@ namespace Content.MapRenderer.Painters var x = (int) (tile.X + xOffset); var y = (int) (tile.Y + yOffset); var sprite = _sTileDefinitionManager[tile.Tile.TypeId].SpriteName; - var image = images[sprite]; + var image = images[sprite][tile.Tile.Variant]; gridCanvas.Mutate(o => o.DrawImage(image, new Point(x * tileSize, y * tileSize), 1)); @@ -54,7 +54,7 @@ namespace Content.MapRenderer.Painters Console.WriteLine($"{nameof(TilePainter)} painted {i} tiles on grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); } - private Dictionary GetTileImages( + private Dictionary> GetTileImages( ITileDefinitionManager tileDefinitionManager, IResourceCache resourceCache, int tileSize) @@ -62,11 +62,12 @@ namespace Content.MapRenderer.Painters var stopwatch = new Stopwatch(); stopwatch.Start(); - var images = new Dictionary(); + var images = new Dictionary>(); foreach (var definition in tileDefinitionManager) { var sprite = definition.SpriteName; + images[sprite] = new List(definition.Variants); if (string.IsNullOrEmpty(sprite)) { @@ -74,14 +75,18 @@ namespace Content.MapRenderer.Painters } using var stream = resourceCache.ContentFileRead($"{TilesPath}{sprite}.png"); - Image tileImage = Image.Load(stream); + Image tileSheet = Image.Load(stream); - if (tileImage.Width != tileSize || tileImage.Height != tileSize) + if (tileSheet.Width != tileSize * definition.Variants || tileSheet.Height != tileSize) { throw new NotSupportedException($"Unable to use tiles with a dimension other than {tileSize}x{tileSize}."); } - images[sprite] = tileImage; + for (var i = 0; i < definition.Variants; i++) + { + var tileImage = tileSheet.Clone(o => o.Crop(new Rectangle(tileSize * i, 0, 32, 32))); + images[sprite].Add(tileImage); + } } Console.WriteLine($"Indexed all tile images in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs new file mode 100644 index 0000000000..ed96906ed9 --- /dev/null +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -0,0 +1,49 @@ +using Content.Shared.Administration; +using Content.Shared.Maps; +using Robust.Shared.Console; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Mapping)] +public sealed class VariantizeCommand : IConsoleCommand +{ + + public string Command => "variantize"; + + public string Description => Loc.GetString("variantize-command-description"); + + public string Help => Loc.GetString("variantize-command-help-text"); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; + } + + var mapManager = IoCManager.Resolve(); + var random = IoCManager.Resolve(); + + if (!int.TryParse(args[0], out var targetId)) + { + shell.WriteError(Loc.GetString("shell-argument-must-be-number")); + return; + } + + var gridId = new GridId(targetId); + if (!mapManager.TryGetGrid(gridId, out var grid)) + { + shell.WriteError(Loc.GetString("shell-invalid-grid-id")); + return; + } + foreach (var tile in grid.GetAllTiles()) + { + var def = tile.GetContentTileDefinition(); + var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, random.Pick(def.PlacementVariants)); + grid.SetTile(tile.GridIndices, newTile); + } + } +} diff --git a/Content.Server/Tiles/FloorTileItemComponent.cs b/Content.Server/Tiles/FloorTileItemComponent.cs index 4370593110..a943b0fba4 100644 --- a/Content.Server/Tiles/FloorTileItemComponent.cs +++ b/Content.Server/Tiles/FloorTileItemComponent.cs @@ -12,6 +12,7 @@ using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Player; +using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; @@ -23,6 +24,7 @@ namespace Content.Server.Tiles { [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; [DataField("outputs", customTypeSerializer: typeof(PrototypeIdListSerializer))] private List? _outputTiles; @@ -50,7 +52,8 @@ namespace Content.Server.Tiles private void PlaceAt(IMapGrid mapGrid, EntityCoordinates location, ushort tileId, float offset = 0) { - mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId)); + var variant = _random.Pick(((ContentTileDefinition) _tileDefinitionManager[tileId]).PlacementVariants); + mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId, 0, variant)); SoundSystem.Play(Filter.Pvs(location), _placeTileSound.GetSound(), location, AudioHelpers.WithVariation(0.125f)); } diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 517fc6c94a..cb0557aa43 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -40,6 +40,13 @@ namespace Content.Shared.Maps [DataField("friction")] public float Friction { get; set; } + [DataField("variants")] public byte Variants { get; set; } = 1; + + /// + /// This controls what variants the `variantize` command is allowed to use. + /// + [DataField("placementVariants")] public byte[] PlacementVariants { get; set; } = new byte[1] { 0 }; + [DataField("thermalConductivity")] public float ThermalConductivity { get; set; } = 0.05f; // Heat capacity is opt-in, not opt-out. diff --git a/Resources/Locale/en-US/administration/commands/variantize-command.ftl b/Resources/Locale/en-US/administration/commands/variantize-command.ftl new file mode 100644 index 0000000000..6160a86494 --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/variantize-command.ftl @@ -0,0 +1,2 @@ +variantize-command-description = Randomizes all tile variants on a given grid. +variantize-command-help-text = variantize diff --git a/Resources/Locale/en-US/shell.ftl b/Resources/Locale/en-US/shell.ftl index 0c6cbcabf4..b912b9dc1b 100644 --- a/Resources/Locale/en-US/shell.ftl +++ b/Resources/Locale/en-US/shell.ftl @@ -21,6 +21,7 @@ shell-need-between-arguments = Need {$lower} to {$upper} arguments! shell-entity-is-not-mob = Target entity is not a mob! shell-invalid-entity-id = Invalid entity ID. +shell-invalid-grid-id = Invalid grid ID. shell-invalid-entity-uid = {$uid} is not a valid entity uid shell-entity-uid-must-be-number = EntityUid must be a number. shell-could-not-find-entity = Could not find entity {$entity} diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 0a4c777a64..9841145209 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -2,6 +2,8 @@ id: floor_steel name: steel floor texture: "steel" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -17,6 +19,8 @@ id: floor_wood name: wood texture: "wood" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -32,6 +36,8 @@ id: floor_white name: white floor texture: "white" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -47,6 +53,8 @@ id: floor_dark name: dark floor texture: "dark" + variants: 4 + placementVariants: [ 0, 1, 2, 3 ] base_turfs: - plating is_subfloor: false @@ -228,6 +236,8 @@ id: floor_bar name: bar floor texture: "bar" + variants: 4 + placementVariants: [ 0, 1, 2, 3 ] base_turfs: - plating is_subfloor: false @@ -298,7 +308,7 @@ item_drop: FloorTileItemLaundry thermalConductivity: 0.04 heatCapacity: 10000 - + # Carpets (non smoothing) - type: tile id: FloorArcadeBlue @@ -314,7 +324,7 @@ item_drop: FloorTileItemArcadeBlue thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorArcadeRed name: red arcade floor @@ -329,7 +339,7 @@ item_drop: FloorTileItemArcadeRed thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorEighties name: eighties floor @@ -360,7 +370,7 @@ item_drop: FloorTileItemShuttleWhite thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttleBlue name: blue shuttle floor @@ -375,7 +385,7 @@ item_drop: FloorTileItemShuttleBlue thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttleOrange name: orange shuttle floor @@ -390,7 +400,7 @@ item_drop: FloorTileItemShuttleOrange thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttlePurple name: purple shuttle floor @@ -544,7 +554,7 @@ item_drop: FloorTileItemGrass thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorGrassJungle name: jungle grass floor @@ -591,8 +601,10 @@ - type: tile id: floor_asteroid_coarse_sand0 - name: asteroid coarse sand 0 - texture: Asteroid/asteroid_coarse_sand0 + name: asteroid coarse sand + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -605,8 +617,10 @@ - type: tile id: floor_asteroid_coarse_sand1 - name: asteroid coarse sand 1 - texture: Asteroid/asteroid_coarse_sand1 + name: asteroid coarse sand [DO NOT USE] + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -619,8 +633,10 @@ - type: tile id: floor_asteroid_coarse_sand2 - name: asteroid coarse sand 2 - texture: Asteroid/asteroid_coarse_sand2 + name: asteroid coarse sand [DO NOT USE] + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -644,7 +660,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand1 name: asteroid ironsand @@ -658,7 +674,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand2 name: asteroid ironsand pebbles @@ -686,7 +702,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand4 name: asteroid ironsand rock diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9a96e2c27a2e645f675338d66d8b95e27e62b3 GIT binary patch literal 1239 zcmV;|1StE7P)Khj^YiwdGiX_lSpC zQO*;e5Jx#3koZ>UipOt)3m$(7=GpWD;1@lmLJKP`%!=F~o+6GIdYLuOBW@B>&6Nz* zQCefLkwy{;NKjD5CUiK6Q8g*E_P%)Cw~P{b7=0~Nz{ycSk-Sk-%hXk+s*$eJ)KL++ z|IgFbW~ROFp#;$V>bjp3KyV+ZRbBV@)OBlTK;Q+qHkgG04=|!0UsB@Kn@tX2m02%tQ!Au{sd&V>51-u!y{ml_J=qA0Hk4O z;H7z~&;S4c32;bRa{vG?BLDy{BLR4&KXw2B0}M$-K~!ko&05Prrpt$Ifs`3=O8 z+kON8f8>bxf`uYw6|#0bGjlay@;Ir^koWINm=#@1AeZyx%jU z&C|wT$lvDZ=NsiA^80yE_9g zC>%$mG!*5?x6yZuA&~u9AjyEBjnDwrX4M5)=tt*Kyaho&65_mPSpaNfMSh&wbcHfK zlrmw%1J5{*HHRgqYX9fA%jJq%b1&ty-h(K01}4v=vx!~F0n6TsQz|0WplxmA8rwVX zJsZX?Wg>G}VznNE{vMP18GI4%S7iB9kBoQ*}-E(i_I{Tcmpsv+_ zltqB`z4y#Zz-krZtsUKyvUf}QzjA<85_*Wspat1{A1=mq^v2I9e_V&6e0C1++glaH zx+9A~w2DD%^K2MUXb~$1WRmx^6GkZkq>GK_*~8na0!gVBnRk;?#FlcvM&GlrO zB{Uj%%m51B_HM0guN9?P@|AU{#8}1t*^u3bv9(h9m8Du$h&d`psJ+im5#H2s*`@XU)dKYScyj22vqK5Z#^T~l&4Y}N~JWeBimJH4c7i&Dq4GmGf@sxDtMWw zDygJcW)w=3w^WB4MxnE^FY*xYXjAc|isWkLr;*dLj9ylT?v+QFN=7~0ukgSq&rNfH zf+B`NjS$T+iBT`$A(8O}){FX_Q(RD$+aV3rkyNsBwXuqn`~r4@;Lbih3;qBA002ovPDHLkV1gc3 BNWuUB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png deleted file mode 100644 index 4710843571d9171589ae28b2f7bcd7796120867a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;*aCb)Ty2a@LYpK TO2za5UB}?*>gTe~DWM4f&iYcH diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png deleted file mode 100644 index 83631265468ad35588f12375e30429363937e7a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;*aCb)Ty2a@LYFn17+x{CzSF6q4$^2=t7We&474uy(^fta>?W-CT}N6tuWDWA<%6xpb@T>aYA#=1}QjCAjd chXs9PuKpbF!u-3a0_aKxPgg&ebxsLQ08gMTp|ZrHrKhj^YiwdGiX_lSpC zQO*;e5Jx#3koZ>UipOt)3m$(7=GpWD;1@lmLJKP`%!=F~o+6GIdYLuOBW@B>&6Nz* zQCefLkwy{;NKjD5CUiK6Q8g*E_P%)Cw~P{b7=0~Nz{ycSk-Sk-%hXk+s*$eJ)KL++ z|IgFbW~ROFp#;$V>bjp3KyV+ZRbBV@)OBlTK;Q+qHkgG04=|!0UsB@Kn@tX2m02%tQ!Au{sd&V>51-u!y{ml_J=qA0Hk4O z;H7z~&;S4c32;bRa{vGf6951U69E94oEQKA1`tU^K~#9!?OV-q+%^m*wY0sH9-Fu6 zp-yidKT{vecVF^0>D8%sC87LCK!PZ3>=Ec?W*lae-Do9#00`oTK>R;{|4DAg`1)Vx ze*gU0{rl~kdw6)*GlhG6d~|;tM`y*qzrWu#$J5i(hsO{25^jQ{qz2^1k>Yns-{oF8 zeEb8#zw+WgfB){h_tPb#>$*7KnC8QUf8#nF4z5$rS@6O9-SL>-0oE`K6P{dY7XB3a z4v~)c!0-Eh!h0+Jxj!%GS8~LA@*T31-)i9pVM#NJXNYibToHzO;0P+F?R#rDBn#s2(JzWwiQ1tgP!^M z+2_qhFe9xAn1Tw{_Z|ew@LGKlfP!VL!3AyJ-4z ztWys^%r~;PAyIo*%%=ioqIDdO1x~3R2ZKo&plLqYvuvh|RB=hretc1YvC9Gy?f0yN z>3y+68#L6y_e$s!9Xj9oVcT^kyAaYRnr1E#U zuu(KVsol%VyhsIM&*nH_9Ya{~7v}(;8O+kksfQ5aFQh;+If#HSN+}-}oCB~BO}!|F=({oMl+?PptXd&%U0Ax5gJGcy+Myl^e){@BjMph4wC-lp|Y>U-)+Xu(y_M zt!GP3`=jIU?WM5xQwV-?0oHo@o_aQov*Bq`PrrnJt$qSMto;-g{zHv=ddqrtE&P`K z1lE2E3;&@`J-uB$y9R#iegb{#+ z+D~EOKh&tFx2$K^#BbG4VC|={@E>Z_(_7ZFYvQ-+C$RQYSojY$>gg@(+3$sa&3*!F zKZS+=P@|sSvY!2(_}A(uu=Z0}_zyMe=`HKo?}>k{egbPhg@ymHx1Nkj7|N0O4~nQr tUAvzBQ{lVo_fwz}ScCsx{Qw^v|9@?zD~g&b(`Wzy002ovPDHLkV1hZ()qMZ} literal 4691 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 B{L=sc diff --git a/Resources/Textures/Tiles/steel.png b/Resources/Textures/Tiles/steel.png index 33572086b4f5d4e778470ac83a1c5a3136b34408..e2485a43d92a44f1672001ae083c7e1bb07d6476 100644 GIT binary patch literal 2428 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 literal 495 zcmV({)z#S8*xTFN*4EbH;Naoj z-sR=x;oRKZ+}s3PhLHdO0cc4?K~y-6g_B#7gdhw>W0OD-6?J;{ztzc0ZS9XqS(d=z z-jE=`Er!>SZZQBLB7G1ENCWLr$L69;Mc@73B5eg`4MUaB00*V5MLI&Gj#sjHRbo{cUBOPFrhHoX zDN+CltrBzrYJh5UD2N49t>-a)RdS;M=uycCt>pp09cT@#BMv;;nV#Jo?(uqzzZJg6 zaUF7QEoYgSrg>g^mq!wTD@jcZ6j*18b?xm^7_E|$b=HY>1=#j|tBkH71@Zn$C7VpB zv>+Q)K7b!55KVHLpi0P#+(No3fDty)=l6Sudh{kNWF136Ew=qF4w}6uX%-kedN+qF z2kWLtNRJI@ztw~I%)u`ymQMu{;Ij^LDe1V?u&fXQheiAmked~%FH*+dfA#-5fm?IB l#Y{cd&E&z zR*J+Y#BojsB)%28>hqi6lFwg)c|N-UNTR1)>R`2lS(%%}Q^YaTsIu01#7)AQwUVPe zMr#}nvPdHZ87ivSgaH=`$`;M6yD!=FEu%snMt>VMaB`GTCU2b7Ds|N;>!fS+8K{Z5 z|DUI$&rJK>!zrNu)$=|lfY3hBsC(Y;spmD$fZz*oZMFRsn$z4j>Gif2I|d>&uF zo!;$V^#6aQ?lftVPFp*T4X9dyFg6xQ9I+iFk3Aq*yz7Lgwsp4AH`ji8 z^6h2;7b6M|A^giP;*UW)bo;tv9Af(kXhw==jn?SbbyY2Px8*3Ps2 zI`*ojYu_e5wz4r^YWx&$nhg8kCbNYFOq_2v)nK~z+a5aF?gKuyvN2w2{KRSis{z(J zB0e1${~(V{Ij5WtJ#bx!IfeHT16o2(y5yWh3^>W+H;1p|1AY7#Zsn9u6UiwIrnhs! zDQJuzAo`q!%geL^CtCb!_@+M4O_7n~l#s#rtgoDTO#UR-X$4NQ_|4&)`d~GH)d2pM zT+$PKQ8gF6y70tt^<1_ZeqHQ+@S*PWH}yei0Ld92CxcurDI5_4tOg1A{kY5!6f7h@ zNBg%|gJd~laC1M0(Vz!Db+#^b+c?VHIOMRI`p4gYQ4*m22KzvRB#S60NH7KbH@V%1{cU zvYNBCFSjqM*X!b&`k?E-)d02*;O`yBHpy|js3q)+#?Y}%-vy{R;?=PSJ?h5k#+G`d ztU`v7%tKEqajcdPE#Yg$-g5j^pFh;ZraC-TR>%;uEzLt+yi2@&%rm+t#eqH?BegIu=Iy>>fqYk*)f+fiiV?tTd{{6UF zZY;xZ0{f8T*Tttg+=MqvZdyErg>~wzBRB)4Jqj7*C$u-^#wz$tU>|b)y7*QDSPkH> zr878fB5}Ohe}>#KI*<38V;^-%*{&pUUG7$y-r~MAPuSwJMHo}2Q(eVx3fB}nuL1b= zP;gHOzy2=Z!+4M`C)2wboFDr@k$Z^V-Z+_?Vy^n*{t-q4AAGSKsgz4c@f*T5#ilvM z$iH}t!*4{ckedle6z7)f%97=XN9<~4@f)tm@CbaVb3!QOzOLg}$6XCj1>0(Xu4@3? z58%2AUNX!{*+w?9#;qHD)5IQaP4SDcp96o_@i&Q2my3ue2N9?i7yKs$lE{ih5WxfI zr_G#n$aB)3=P3RJ@t7E25CrfrTf)0c%{pANLVVe)kKHW(uH$bTpF-iI16b)!B63O0 zK~asC)y#8a;!O7_x2HnPNc%GUy4ZE`cO8G*`2PV7ft`@Z3D+B1TS literal 573 zcmV-D0>b@?P)OD`8nFBe!l8$v4+ zNiP;kFBVBK7f?1CLo5_XFcwuf8B;hJOf(oyH5pSm8dN$PL@g9WD-=sJ7e*}82Q%3(Z(o4EVvJ!FY%s4{8eD8*B)F8)JbL}r zwjkvgvQ?huT=pUu1~!?7*xKHSe2(3k1GeuT_63iVW@nEB7H)=A*`K6{$m1BsfO*G= zz-j1)A?AV~WO{2(32||VBXKNiXONtnGNj}iafaB5eujUW*BZ|C?i8%9&&xYgaC(>z zfAwxOnqKGmMQW-@xmb8C@Ow8OXI?@EGOW80HQF7lBDuU$O3zy2jQ`f6WO`iA@gsMs42Tjr-J00000 LNkvXXu0mjf=xoly