commit 68c7c8cbf3d9be3606c9d8be353e01c3591f73d3 Author: Gnarwhal Date: Wed Aug 7 05:02:53 2024 +0000 First player sprite iterations diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d89f02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +out/ +*.iml + diff --git a/res/img/player/player.ai b/res/img/player/player.ai new file mode 100644 index 0000000..296258c --- /dev/null +++ b/res/img/player/player.ai @@ -0,0 +1,1370 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[8 0 R 9 0 R 10 0 R 11 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + player + + + Adobe Illustrator CC 22.1 (Windows) + 2020-04-18T12:02:33-07:00 + 2020-04-18T12:02:33-07:00 + 2020-04-18T12:02:33-07:00 + + + + 104 + 256 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAABoAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FVO5k9KB5T0jBdvku5/DATTOEeIgd6oCCARuDuDhYF2KuxV2KuxV2KuxV2KuxV 2KuxV2KsW88eYxZC00K1VpdX1xjDaxgGioCBK7HwCt/HtmNqMtVEfVJ3PZGh8TizS2xYdz7+gZPG gSNUG4UBR9ApmSHTyNm12KHYq7FXYq7FXYq7FXYq7FXYq7FWjHGXDlQXWoViNxXrQ40niNU3ih2K uxV2KuxV2KuxV2KuxV2Ktchy496VOGmPFvTeBk7FXYq7FXYq7FXYq7FXYq7FXYq7FUBe3q2d9C8u 0E49Ll2D1qK/PMjHj44muY3dRrNYNNngZ/3eT033S6I/Md27sVdirsVdirsVdirsVdirsVdirsVW TmBYmecqIkHNmegVQN6knYUyUbuhzYygJbEWwbUPzt/L6zuTbi8luSpo0lvBI8YI8GIAb5rUZu8X s5q5xvhA95FuSNPMsm8u+a/L/mK1Nzo94l0i/wB4gqkiE/zxuFdfpGa3V6HNp5cOSPD93z5NU4GP NNsxGLsVdirsVdirsVdirsVdirsVSHzLop16W30udmXSh+/v0UlTLQ0jiqOxIJPyzP0eo8AHIPr5 R8u8s4S4d+qa2Gmadp9strY20dtbqKCKJQq/TTrmJlzTyS4pEk+bEknmkOqeU7K21CPzBpES2eo2 5rcrCAiXEJP7xJFGxNNwfH6KZ+DXSlA4ch4oHlf8J6UzjMkUWT5rGt2KuxV2KuxV2KuxV2KuxV2K uxV2KuIrsemKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2 KuxV2KuxV2KuxV2KuxV2KuxV2KoW4vY7e6hjmPGO4qsbnpzH7JPuOmC2jJmEJAHlL70Vhb0Lf30d qiKT+/mYRwR92Y7fcO+Aloz5hAD+cdgisLe7FXYq7FXYq7FXYq7FXYq7FUPqGn2uoWj2t0nKJ/A0 IPYg9iMBFtOfBHLAxlyLHD5c832x9LT9f/0X9lbiJZHUeHI8q/hkOGXQuo/k/Vw2x5vT/SjZ+f8A YmOjeW/qU5vb27k1HUSKfWJNgoPZEqaffkhGnL0nZ3hy45yOTJ3np7gnWSdk7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYqtCEMTyJr2NKfqxVdirsVWuhYijstOwp/EHFV2KuxV2KoXULue2jVo LZ7p2NOCGlPcnfFURE7PGrMhQkVKN1H3YquxVZNJ6cTyU5cQTQe2KobTL+W8jZ5LZ7fiaLyIYMPE EYqjMVdirsVdirsVdirsVdirTKGUqa0PWhI/EYq1HGkaBEFFHSpJ/E4quxV2KqMVt6c8kvqyN6n+ 62aqL/qjtiqtirsVdirsVdirsVdirsVdirsVdirsVdirsVW8G5V5mn8opT9WKrsVdirTAkbHifHF XJz4jnTl3p0xVvFXYqhtRvjZW5m9F56dUjFWxVUtblLm3SdAVWQVAYUIxVVxVpjxUtStBWg64qhN P1E3nqVtpYOBoDItFb3U4qjMVdirsVdirsVdirsVdiq2SKOVCkihlPVT0xVbFCI2chmYMRxU04qA KBVAAoMVVMVdiqmkCJIzqzkv1DOzL9AYkD6MVVMVdirsVdirsVdirsVdirsVdirsVdirsVdiqxFl DEs/JewpQ/fiq/FXYq0OdTWgHYd8VbxV2KuxVDX9+tnEJDDLPXokK82+6oxVXikEsayAFQwrRhQj 5g4quxVZPMsMLysCwQVIUVJ+WKqOn363sHrLFLCK0CzLxPz6nFUTirsVdirsVdirsVdirsVQ01td veQzR3bRwICJbXghV/A8ivMH5HFUTirsVdiqxIURmYFiWNTyZmH0AkgfRiq/FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYqtAk5VLAr4Uofvriq7FXYqtYy1HBVI71JH8Diq7FXYq7FULqGowWMQklV2D GgWNeR+7FURFIskayLXiwqKgg/ccVXYqtkkWONpG+yoqae2KobT9Tt75HaJXTgxVlkXidsVReKux V2KuxV2KuxV2KuxVp1LKQGKk9GFKj7wRirUaFECli5H7TUqfupiq7FXEAih3B6jFVGGB43YmZ3Q/ ZjYIAvsvFV/GuKq2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVYruWoYyB41H9cVX4q7FVrOFIBB NfAE/qxVdirsVdiqGv8AUrSwiEty5RCaAgE/qxVXilSWNZENUYVU9P14quxVbJIsaM7miqKk+2Ko Ww1awv8Al9Wl5lCQykFTsadDiqMxV2KuxV2KuxV2KuxV2KrJYhItKlW6q4Ckg+I5Bh+GKtxqyoFZ zIR+21Kn/gQBiq7FULqunRalp1xYSySRR3CFHkhbhIAevFqGmKqFnoVlaXzXyF3uDEsAZyKCNTUK AoUfSd8Vf//Z + + + + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + xmp.did:ea0eb61e-3b69-ef40-a568-5e7220b96286 + uuid:a900d44c-404b-4b51-a13b-9df9f1dc6bab + proof:pdf + + xmp.iid:8809bf1b-7fcc-044f-a9e8-0aebb277bd70 + xmp.did:8809bf1b-7fcc-044f-a9e8-0aebb277bd70 + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + proof:pdf + + + + + saved + xmp.iid:8809bf1b-7fcc-044f-a9e8-0aebb277bd70 + 2020-04-18T11:30:15-07:00 + Adobe Illustrator CC 22.1 (Windows) + / + + + saved + xmp.iid:ea0eb61e-3b69-ef40-a568-5e7220b96286 + 2020-04-18T12:02:31-07:00 + Adobe Illustrator CC 22.1 (Windows) + / + + + + Basic RGB + Document + 1 + True + False + + 48.000000 + 120.000000 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 0 + 0 + 0 + + + RGB Red + RGB + PROCESS + 255 + 0 + 0 + + + RGB Yellow + RGB + PROCESS + 255 + 255 + 0 + + + RGB Green + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blue + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + + + + Cold + 1 + + + + C=56 M=0 Y=20 K=0 + RGB + PROCESS + 101 + 200 + 208 + + + C=51 M=43 Y=0 K=0 + RGB + PROCESS + 131 + 139 + 197 + + + C=26 M=41 Y=0 K=0 + RGB + PROCESS + 186 + 155 + 201 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + + Adobe PDF library 15.00 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 5 0 obj <>/Resources<>/Properties<>/Shading<>>>/TrimBox[0.0 0.0 48.0 120.0]/Type/Page>> endobj 6 0 obj <>/Resources<>/Properties<>>>/TrimBox[0.0 0.0 48.0 120.0]/Type/Page>> endobj 7 0 obj <>/Resources<>/ExtGState<>/Pattern<>/Properties<>>>/TrimBox[0.0 0.0 48.0 120.0]/Type/Page>> endobj 13 0 obj <>/Resources<>/ExtGState<>/Pattern<>/Properties<>>>/TrimBox[0.0 0.0 48.0 120.0]/Type/Page>> endobj 52 0 obj <>stream +HWю6 |W)"_ HE H}wEkٲ}`qgpvw^;=n)ާtu=P_-~g  +xdQj$Jj|' +si<+b +F+*G) *J`HE`A6Zh[Pt(3HR:gbaaG9<5[II5F2"')Z4`IBsI-XI(pG8 Qb%G.EP-y4 ݽ./yk ޒ 4Ίr%mA%U@!ȱ-ST=XQ4K.x`0î2P.JQ *GN\6.E6ʭtB*%匟AIp/"HY%P< RC YQ)lŵ6LfǬ}m3omkiݥ\s3 , nc!זJAPi^ƹLkɱ^v +1a(|Ћ $Ijt4Ĝ[I&Rcro&ͬAfbܭC-[ڀ%A2 )mwG)]kY#`ҬM\UGN gI:zh]!-1mf/+#jOϑ+9,Zf`3د-!1~k'fL!mr"3G=%m6Guo(%`<ƚ[09 G> endobj 8 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <> endobj 11 0 obj <> endobj 70 0 obj [/View/Design] endobj 71 0 obj <>>> endobj 68 0 obj [/View/Design] endobj 69 0 obj <>>> endobj 66 0 obj [/View/Design] endobj 67 0 obj <>>> endobj 64 0 obj [/View/Design] endobj 65 0 obj <>>> endobj 54 0 obj <> endobj 55 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <> endobj 72 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 27 0 obj <> endobj 30 0 obj [/Pattern] endobj 16 0 obj <> endobj 75 0 obj <> endobj 76 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 17.0 +%%AI8_CreatorVersion: 22.1.0 +%%For: (Russell, Sam) () +%%Title: (player.ai) +%%CreationDate: 4/18/2020 12:02 PM +%%Canvassize: 16383 +%%BoundingBox: 1 -238 95 -6 +%%HiResBoundingBox: 1.97899999999936 -237.422252025578 94.8241898220822 -6.19700000000012 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 13.0 +%AI12_BuildNumber: 312 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%RGBProcessColor: 0 0 0 ([Registration]) +%AI3_Cropmarks: 0 -120 48 0 +%AI3_TemplateBox: 28.5 -512.5 28.5 -512.5 +%AI3_TileBox: -267.840000152588 -441.839965820313 315.839965820313 321.840000152588 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 1 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 4 +%AI17_Begin_Content_if_version_gt:17 1 +%AI9_OpenToView: -114.75 -1 4 1360 851 18 0 0 116 174 0 0 0 1 1 0 1 1 0 0 +%AI17_Alternate_Content +%AI9_OpenToView: -114.75 -1 4 1360 851 18 0 0 116 174 0 0 0 1 1 0 1 1 0 0 +%AI17_End_Versioned_Content +%AI5_OpenViewLayers: 7777 +%%PageOrigin:-372 -812 +%AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.6 +%%EndComments + +endstream endobj 77 0 obj <>stream +%%BoundingBox: 1 -238 95 -6 +%%HiResBoundingBox: 1.97899999999936 -237.422252025578 94.8241898220822 -6.19700000000012 +%AI7_Thumbnail: 52 128 8 +%%BeginData: 2387 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FDFCFFFDFCFFFDC2FFCAFD34FFCAFD33FFCACAFD32FFCAFD33FFC3 +%CAFD31FFC3BCFD31FFCAFCCAFDFCFFFD9AFFAFFD32FFAFFD33FFAF8BFD32 +%FFAF67AFFD32FF6767FD33FFAFFD69FFA9A9A9FD2FFFCB734F49734F7FA9 +%FD2CFF73A3A9FFFFCB79A3FD2BFF79A9FD06FF79A9FD29FFCBA9FD08FFA3 +%FD2AFFCBFD09FFA9FDFCFFFDFCFFFDD5FFAFFD32FFAFAFFD08FF8AFD2AFF +%5FAEFD06FF5FAEFD2BFF3B8AAEFFFFAF5F8AFD2CFFAF3B3B3A3B188AFD2F +%FFAEAFAEFDFCFFFDFCFFFDFCFFFD16FFA8FD44FFA8FD0CFFA8A8FD24FFA8 +%FFA8FD0BFFA8FD34FFA8FD33FFA8A8FDFCFFFD77FFA8FD50FFA8FD0CFFA8 +%A8FD24FFA8A8A8FD0BFFA8FD26FFA8FD34FFA8FDFCFFFD83FFA8FD52FFA8 +%FD0CFFA8A8FD24FFA8A8A8FD0BFFA8FD34FFA8FD24FF7DFDFCFFFD87FFA8 +%FD50FFA8FD32FFA8A8A8FD0BFF7DA8FD5AFFA8FD0BFFA8FDFCFFFDE1FFA8 +%FD0BFFA8FD0FFFA8A8A8FD0BFF7DA8A8FD30FF7DA87DA8FD25FFA8FD0AFF +%FD04A8FD68FFFF +%%EndData + +endstream endobj 78 0 obj <>stream +%AI12_CompressedDataxieq%}0 5̫s^jxDQ(WRUDh?_ZsNխi߻%grgFFư"_ׯp1>~s7/~>ϸyO~:8~w/?\r'^~>CK/2Nj⢕_ȼ\D}R*;^T]m dz.\+syܿz7/_?=uGoϿ#Uzv=o.h._1lAoUxwxWԗoRoj_h0o^7݇/F:{ fF grZߣ˾\ԨJ>z"FwQCk9U?aiw»=^xF7?{oy^xl6cy8ng-hߊߑrq{+Ϯ0įWog/~mE<__ܞ~I_8/ +F"H!OUXgq9W^|{&ix}=I[X  7/0",ک<7/^mw^Ծ\Sgl!Ru?L([| ,_-BCpw6h,[jQho^s>k~{4W{ό݋Wk<lS瞿y?󅍀~7Ͽ|CIy3C:<޾]=خ{oAhyY] ˃-EON{/X5=Un?kCx?pp/租hߴnS|{w̯Y5hW/wo6\o^+?[AmԛSz˗_idNc4eL/,w^<a݋W__~6_-×xVθIj⋧O?\Zol//BW=e 9Sq&^?}⥎kDwbt܆ +ehPRxr B !@ qq\K~O`pL-裻vW:.q4 Or :Mntq'%:##ÍO+Os34fi3Oۙqœl|ٌ>cANsƼga뵺qWl_}ծxrg9+fxog#|\Ռu<1Cܯg|:TW^a`ksZ8WX=UeG-|ͫ(Ԩ>ίsGO{`)'zaz|P$+4H W n KacKqAzYu80eK7y$Npٮp\v0\At!K\tyyȂTȔ3quu}uB6tO.}nח׸-;uQn17]y-;/Ɠmr b8[tJ_wC XO%RU?f>nǁ'eG*xG*x +ᯞ]5|6}MNv^}^;C~2gC :: fD.4hɉ&Pz49D{5:(a%r:|(Lf~B,R֥Kٗd{#ƁbǼgIR55()o_Ju#\ĄAx0苤u+iy7 a#IRSO}J%4M+'J`xq37k:1~TRQlW9ڝŹ}L ||K e_J]{Kw~Vm\}5_ѩ$mu\ +SuI)^ԤQ,@GQ, 'F;u7/(vĠ,B/+θQr b1& !?7@ãנU$6iP]b d]F0RrD^B3P THD`dd:a8h7* + quOA>_5zififi@u.(5V 2sqb;<i:vhL)~_=\ϻ}-)rrMO)!Qlb({cբ ܚۼ+C *bP2uD*џ?:C?RMtkehQfgefVYŦli6w֧S_aVVڶ557&o}n&7j0 G,rDEf@, +\w? ?~>KmEmƵc~ecވ: +JK`"KIAsDR-4M))gJXeŧ&-4M>vC =Sm|"M넠nqYVuŻxqyIL%ʋE9j8\CaPʽ\ʴKt!$?}"9H2Ϙ]ߍܽzwFt})Š,Xю]YǴHѶ‰ B͸[?wzogڂ[[k+!Ԯ; }fh3uw:hew7ޠw7`6ojnssY{^ahn09/ @kB \ %p+tqQ8:Rgo:t6Pxe?v0\z#Ckxh׻cv c ? CW0y[Mc=;ݻ۱_MG}c34h%hB f.HD ehݷBxH6tOtXNض.:pcv2)wB$J=53oYBA^[h M#Y@e hd6a9njiē3v9.WՆ¯7sibpSl6Z;wl)"M̔5mD |E\ w]ʌr 7 +&h˹HRAHJ7WW``NKY^hu`m\B +ЮB +-*x?ZRdGnl(~2'vb1ALɅ$`q}p:Gt,%"k<`0޵s8պ/S[ 4 +WUf5oD"aCp7c}#v +CG.|D+j04og`(A(A! T`"ukvZqyja|qU{ٕێěm 9rNx/0 +̌I6sV s)G HG\,pMbf:i5nnoީ!X)"O+#NLk:`ZnC.|mv +jak`euj[Yq6}[CF@P[vK} [nG8#<'>dݽ{wy y y?ififi|`#~,!6)]"8tQ],|E ?vj Ӑ3>f]+y3>f}h<xX?f}HTHTp :"91nI9vF6#=vD>mNYV9dđ,[8[9ZI+0+HdxەSmI^1R=T%FX +Vt-#Ȃ bW.^[xwP3@pli, A)Ai4'e8).R # +?;G0=c'܎LN> uGcCz~_*Zgzl]Χe[9A,rD[,:b #,]HPTbnGUun7S̈́OF`oz3֛~ꇙ>X@?籣cSz3q1Y>!YBRt96r!BJ:g7Nhp~lK}B:f(fvA:ڵ!]b#ty>,@dI'0SA—M` Ԟ gBi@Ziki׾ +݅>Ѓ]҇CNWnzҳy*Ż]+.>>zޝA2mxhcO?sOj`r|F--}eIhW"+}m7.d7|}=֑#'F>=z=zɚi¯дXUNl̑:Z8ڮRk`u|>/ZC~Hhm83soRq.T9x'cQ kP'lqTNiZH{6zhF6̧`prqQzԴ5GGMQzԴk/?>jZg428WTZS״i7KL;M #}iGy9нvͳneS/l:Vv?=tG^J[]{}H|M9b>>ip<" +'ɂ>B)j5%NV DMYBѭ{>]ò{v݃瞣FYyhrĒ-g_tO-CS*:8ub4 +N}_S#>D\hTtzz{ aVr9Z݇-xa$Y뜼ib +z\Ly>Yw"AqG]zpޜqXb{ +=(mdؾ/{[omIyD%8vp ڽ ]uMcƼm:o6.jBG:_Bt;xL3}W^C? -?$Px]k(Bי4ʪg=4|69%(oFWk>zў$mԺ =>GLl7<5Ӝ)}y\ '5 pM\ՏM wz1SG.1:už⏪}t8\;eؽ|iWTNdmQ4챧S,m{wzoѠۓW>>|zKH^?h}1}#Wzgjw,zhw'du +%:[<ZTh%?UNwgMyGY9䇞p&qx% o}xe;~=~y>4w`tI?:H1? K +uuU"(ȹd HB]2)2)2?2W֣(;( T׾cMa5P5:`8dn4!ˋ `&6ܑr[zHP:rK%,@H4җ6R_F8ktApUԨ)ޏk&TrߛX_ڠф=TVYPq2}"m8p<»du9%/d7[p([lJmv$H3DܟqVgqV~_cV*ߞzޏ%m1E)ճupN?G>v) +ПpK \=Vl~Z>B1]m.6q^@xPc)=\ĚJMP,50 @B}C <ׯ~ū/^vzu&_OA06 l+0zrŔNgEY|Ue vy:tnћG/:F#m#Kş}`}߽%-?x1c/W)Mnޙ-6zi-?F`9s~Tvt=vEnT_/֗laɺ  Say:ˬX+yp;G,~@fEl#I :8Nj}H c"c2$&Zx!3 1a _ +;i-eBVN~O*]'wUjݱm]@s87lGnٙLO.mֲ| 嬠h3!~ /:xduE?{A9`!-6m +? +T/EAFk  3tB~rJ&k O3]㸚yCX+Y1;'0k+x{d^~l8'w)d +7WiW-ҸѰ$c@߱cˌZj-#a,*T-6ɍr3cnRh}r gkZ+JVlZiKx<2PP*xd29X1tLtvaǕ٩Ӫ_.|1l%1F ;vQE<˼"6i9|s.A^MO*dLMmO(Q-ߐ=8yVWx*A5h ;#CfM +α*&:v'mѽ?K?5բNZwa}?rSS*q,t^8wϽc\nYDx_9ڸvD +6[; wB>E*(  T[PdBCx.aHIl;%l۩Ò府V_ $>l۲fyރ{WTowN)s9a~/ON }:oP zE@Qǐs#8mrC *@«!&Z% FL֐+7}zGΏH +?qpk9H + X,p.:e[RV\)+nwtgfQg0BV؆~rM8:[Yd:"j1wOiFƌmqcfT3T% 8I)P9HՎfTz(8XkΦb6N͙rF9[ΠLZ֠]AL5DXFwG]d&~?pdʰzؕĆ7֛6rrv\=\@sCm)/5O,Q sؙROGZ_bZ\SҸ|BS1kh.ԓ~EZ!ӆ%}[QVD9%<أsGʬDSBӻ1~n򯝾rk|5<32䛵cJ߫U>z0G[.xxyuO8`)6)&h14%W@iGǿKlx]*VLy_ \'t=|G^k?; +޲y\oaVP/vr=q|4yB*Ir Bd3 ڒTpSߌwLFq*:̺:ڻkW:čzʭz_AWפ9uPܽ=P{MUStka-'Z,Jg?M}Z)]՞was=VOu:Xd`r` A?- `J>~՝u@/ԕN~zy>#@d96l5=udjr<5LsےΔ\ip1Mc9cW4Ը9t'e_xW/GGO΁,Ih TQ}:R0 +!z-+F +ߺ e3(ӣ'ܐ?JUdE|{}Hx0>&U|wqZ泬*@tP@WFr0v ` L xSa]J7WI@MMrGӏt6#q[H4H4NXkn/: G:_;5ڂ۷o^A/J}"a"kOz 9m)Krf:@AyF_翿cxOvÿS^0?;yч]n= +&x7oo^|WKׯ_~v>n~gWP1)~燿7qbޞzc'Idy".#RKKB j'a)L #4 |5L +Msu )kNN煭jx( q`Xn:@*|5pJϋ&n+ 2O ) +ՇR/k !H .gW.0EMxXzW.`pɷ쉭ZngCqGy5xP#Y=d䬗@svުcCbn6Tp 4ҼQyG] 1Oŕ c$AkE +j]B+ ?Sc%:@ s+38,ҦXxk(KpDp cTR I||CPpAR<"; \Y" 4]]\q"fk٠2". Bz =(CZ;]w + ]N +<,(2| +ÀN);KPgD\Yx]Ưb^I""ϳ˜kDߘ359 Cx0o|0-`濚Ȗ@So݉ "Ƣj!tEӀ;HdQ}|۸IǴ 0-e.kV$7pX%a?kckcտըhs2*eWtEP):%&"CcEA`npq +ϣպ v!Nb")4u=Q`Cē7 d !cZ\$a9 +>ؙE1fޘ!z~)O>P-^@S>* 6X<; dɸ.z[t[c:o  xDy~4j,؄ъ>D,ƭ4#O|e㘭 VE/.oܠL0|:u  "|Ѕo8d)Y9E `Jѻ-S )\Y։0S;cP@hJ~8<5qP1K 5 t x5Vk@@`砿ElU$2H1 c^!g`Ï ;Jpp$9>faϪc)CtN`1݆ |biFISe S^u[kQp/:OQ<8|AX̖T줉!ojPS Dℜ@9|TӘ`0Ve{)'1swԭpJ97l2y8qF "B"]`~',[1ndIK A_)<~T/[-](Q0`׏)ԢJ&G 4.5#͊DG>S`irP' + ?#3H1[9Jf9bM +t vVHޚ~V' 'Hhmdy +8[3ZA&27|LqwР|&5Rϧ\{T Wf9=Bc /(i2S Jn/%a/r:#'Gk3@RR+wa5dnh~y-ށu ~}U0LG ElhMs^-~ÓEX.Ue!SdyЊݖ@k +C6j' J?Z-Kl<0yp|Q Rz^5W@_i>R~c[ +ii\R$`̕q=+ut"wM2q``h=7n6 #S +Jx :g6mhʂ@! sldԺ `R9O~3 /. L`-O*=gJnZC di8z2٠dm0(Jy!<5frX69LK¼Ӂ1^{,1qh:8_+GbX |H@QIbmAosUtodҘ=2ГY<e &9 +1X_Q 6V&uFENUIWZ549h{~ljtMyʙ[/"ϫCRܿAux!)đ#i!r!FnM)R0`<:QBl5 $e$xRǂ+2lhU2&S9z.0\+e+JǖS|j 򎹔jl D6 ZC'ۯv< ii%%f#AיqtgytX}0_D!@/t8ݗ|׈(f2qOe @U%ݟh`aZVh큲%7TR5ͻPT $aU[/%FK f>V)Akv+2v yHY' #ц )M/bGbC TDž"ͬF*96U]Y kہQjr4j + + +:6Fnߡx@6ܥ/*KT:@eDl.6ƊdCa=nو17"Mֈ/i˛k ҃i ib2]DMܷ"^Ր @›Rnz ղ8HI@+ HX-}.uզKY..2`½2lH‰Q:R]t%|6-O6Hq76H/ tPf5[+uƅ!Aǂ,eZȸDAwxm1r>z>ג+Eb2-8t..v!Smұyo[foRdTif T" # )/¾S^ZVL|IA(a+@^^.8GMfLSlQl-YCX%cN6(uقWݤRy؀԰,HMj$$} +rO[2-Tnȓ6&*ޛo!'%>s44E:z:o߳ch%ptAfco'N渃O;n䜖0. N\Bg0X#Ȩ2,tej|o:, 2BI,tLnb  &J,eQ.Oq &YbzZc1MoI3A~Lw fCɘ[0~Sh3,{OdX!FaDdi.=ےoQ-,kݿ0o9ѥ+0DѤ L(osdHvx8z1@A<0X4AW +y]̫O!nRȧ)W`& +,eQQ#{@%;ӯ!F:(Q]'{m&S-GG;7Y{8EL|cI.!ʞ+-A.2(@LI %Ā/:*̤hhV8&jX9u 󍫰4ct-3J3`cYffKvMF9yYR'AK&c2N$G?] +˒Z +xE78{W4pm.CDb.Io }b"(].~!ȓNJO 5( MINhP9ƞ1NoП +&::(TBOO8@WtYκ-RȢKGO + c[:r1lp>!д5K$Tk(Ky|Aʤd^JV{8 [*vDAnN8B2ȝPC7@D:?y#7oϹBӂ:V'd,];%dLD9~\$Rz\U"<:H]'}[̈+? _'F+jѴA+kmzp&:ۦ Sg +o:dYAJ0Eo Ɍ*yЪ狠*X# A,4Dv5W8?NPf/Æmbmtz^kz56*Pqq#vvMϯCG2$ݨ6n JZnd/T3s:VӜ+JlɺRVLdcǛ]ɟtZH&XKz0rF٘rM<3a݈J/̦*dk!r$O (b~]'1:7 pymA! ӒwkU<M..ڃtK"-J[<Ԅ4@e 2>sE3g6<U7t06:ly<͡@8"Upx6ȻήTeržAݢOn y&BfSZ%z~u2 j7.)ɍ3!tTuT6<䰐 ,r[& L4MO# lYQ 4t9PTn1N(<7/&(9Ny fNj^!1% +AS:tB1 Pī~UL@2_9 + "6Dbu%ىoRG]aߥE*%NMk1;fO1s&H zzy2#G08EWXT0~nEI+EkF{#=CfBq ^ U.Fp"dJvT}f K6,k>z:8 "et2jzVpo4MvEB+R+ʼFhA b4ZI,xWC3񁅲J+NV4-R3Na&4\UA&= hR*5E:7TẢ)iҋk]|~ޕ &Q{c~$ Fe1K ́@祙d_,|rd>UF J-]\˂Mt_V PA!&rsJ +ƨz; ?#"Nm;AH^ (U4a2ynpBF] +/:AMdo ;8GsbEH !C<-w1.zBjeZ] o()oQeӋ!`IRRk5E(Ķa(o(!L B)ECzʙV:Zw( (-rQ`䨐*1=DfoB@VfKtI湐>6P!4B+B e aѓ5~oH 5@X +?p(V0C(uBTi5/Oъ'6܈Ҽj 5ea*`z],tHsĎ |4 ۏ ̢!̦r E% 1nc> G`>\yN5b0D>oB )Mo>T) O")i &TرH 5˲WPd3m鸁2[GTGl"ҭM9  d^":ȚU$*U1|ʳI=';H(7E~b(3`´RTb4Z;EM>-䄧 2^6(ȅ:?"wȧ?K"f@p Z;ejV`iY4ɉ12Khc3٪ at6Mh2j; ~6]Ѓ(E-#lfAj2I|g"5{Vv= 5R;n\'t>69D[L4k*y0s[T:':ٔPuŭDܫRZ>| rRS4A&ѝ̭V 5)I{n6c yѢp]P0p FdP5#BT<1dYj CqoMlTZEV&i 3%&unMPy"7uƍ:O#tBǪSޣo . +Ζ\ꅂo"V棯;όcvJ.v~,-z]0ɶ)"}3|DCppD3Ճ +cD);dx<)& +,;k`ʈC9hTZʹF{E0}b) S$t VdlpQd"kt3~BLIbfӈb' $m*VP>#MU[h/23Y\l7/ +DiTFEGE(jW",Cdy{{i,bY1BU*6iG_ AԬ76h_˜jbcvA$[2iw@ZD U +3hcbS0WǍ5Ĺ6I5 s&.r)WQ^t0Z17/ +gCJn؀"ܟZ$;] A.iQfI8\)ҎdZM&o Nya =C7(+EJ|ҝIQQlJU↴J@e)jgԊ7qRBPPc3PƊ/KO  B)VB0cJfIx@@CLd@Eќ"DHXq2.ecİ7Vbڽ^"Ab +`9KwC|7DZdQI $nZK^] Du=8Uy!1*:Q+Ry兙%X, 2:4PB%OMC6M))14L}Ԫ)GV Z[h&109Y`VFK3[,L1Βy`SjBR,#4[}s +[M^pFo Kq&e7@vQ;#t(CCJ14~=%?q^("mTr +TZ;6sVW3'BK.VxF_;?Xa&F%kvP̆"pengT5` $)j ֊;O(P5E-nս\WH!rǖ ;g qV?]0þxRjp$%RH=D% l஥At>Ȇk?1Y IM6fIlQtUSI֫<; b +(|oB06bTAĞsx6FƒԊv֦. &U_@"FeYg PfD?꒡I F8(F/Z'DnLI BuʓM@ 5JyD#q +VU5D7b+2sVe}^ŧxnzܚә셑yI<3&Qj #KIqt +hK%V RԪNXێj|Tں}h~h|_|͋W?򫯾7>nfi!ؽEBDHemRe X JdgaR(z|E1uH!. (tYdWN*$2&;_dW vEǫf[K ({Z(4SE1G&R%/;_%lA.7q.e&lRϳ\Ϣ=E8p&"ҍ۟N + 'P GOT" Yˤ(NCe+tf7s*IcyztwT{);4HC˩f-tOw*#fᑋxWuV-]^Pe)WsN#e**LF +d259L D+h`pD{" +9<Fz%0=/X4@ϱj́gr%D7Uئy%+]Ese4*JqBq4G+$S`PX sîp`Y tA ţUzJ)[FI1đچ7dn lN)Lug|laҜw0|Ӄ(\0;Oww-X/nUF]!D3; KGx2-bO.?ەA+ (@&)0>,J>!V[2#l2͏oV=u2d6La)6dN, #$܉+hXGge]S(ȭCcɌ 6|~+r˜i$"<1#KX蜅VbD[D`P2U6 =+s9iK(A1!%$P@VGVDܧ$ 7t\AY }|Pi>!lm Fg:leDGd0Bv-5(Jٙ BQ)WݹH76,Hϧ*VJ\90Sϱ41N`Z*$uĞG-0 +*e#%b- 7K0VB:(4+vA`GC+rYji3[(au je|T\wlMD6;q`&3K)06ZȆ!aZAA5!jГjg Bs5+eãgZ{ٷsaie b2Ä́V $PSWӗ0tIvK4=/lX,£b/X؈XsgH%N؂dE& FgTl҇A>͒ ]szV̀Օ7}w;\y%DC^b7{cي}Ri0kRjW'Ud;+$igН榞O\Q&\ 45=Bt6SO8Y-H9+JA 4$BMf;~.y >l'B*Frm=㰸]bX%:fV~mkԺ+*t{(r^lH0 Ì,Tjmezgr?];YM=iyd[&e'ɢ=j/#S-vV eN9*HZIMb4w;`9Oc"kf>Z} X+WmXޔ>xGAJ2Y{HoμfV + IjK:YhvEBdɪ[/eє ^ւ,szjd&@e ņ%SsUF*ft5HJ\Am1QcX98I[L˄D?9Y9/ׇr.>R&R$][RJAUCۦ~{˥,Ro#[S!])z m6FdrŽt5V)jPvWmy< +S!oKJ-!Qʘ&"RгPeʈٮ\C+,IUfӮU 4Z` cXkB&DpdrEYv7a ,Nu˫ݏ`$e Jό$A펳>FTUIsyҔqRQS/MČcS]#ݳ[皋WjOJvmr#Q]3=; +R`Б^d1+/Ж{fVwF \O;0w/?կ}{ճ_}7?{3u¿ճ=ۿ}ooo/9r |K4(& ^pOex +*1QFД C$K:htG^jÔEIXp1+'+P\r TWȈ_n*A)D'P$ K]@`$O>mIcY6,79Jw\P*EDGS?GRF*2L++#ޘO9q[2S MJjW,A$Er߷jUYv< RT%T)# + NE _lfg>Hp=7$VhRQ +K%eJ*%,'˖^[%p)qܙȲdVt41H@D<3FqUkT$gb@a}QC,D(@4}SjJ**H{ $R7cx#9&pʂL#C|={r0&[UgjlW&6 #RTIٕϵG)(^OkHH'jjBΥ);NLzץ/=đ1q' [V<3Zq2:O[WKF}-⦴|i?*dQHLpH(rJ<ދ÷IfByS 2ss x6FwMwR2+ϧ!λ+Dʶλ *"&}swI(^PhWޏ6Cf +ΣһB\ 7BR|~yD\G(^Bz蔒 \]EQ>m=wk2?*騬\mTl4^}H:mn.8k[w~LNdmg,n4fowΓd;MTGu#j=Ţzu,EGzE2RCŦ:k;8 +fG +lꥧ^PvKgo2Q&l'9t\9DQؔ{$TZj+m5JS =xW΅γFysi۔7WGUsL=ƭljGrs s֍ciQ\sV,', P9S@M)K> +/jb0̊hE7Cgԣ8*k)iWa\6FeqwQP|/B/uM)e7Ù`d̜g7-zK e4[2Qo˜X4of[ օ L~Tv*s]mĸA"Щ ^K&4҇lW!35u9bзUY䀹x3hUyjO>3ӂ%QQЦ*3k(sߘ1RYAmeR2\f^8YS߯/AJ+"knI콫bx\CyN75Q+M;fUZJ֥M~ B1sNr"MIE\8~.ilJϰQ˜Z'Rks +;^*YՊ'sa[I#1S^XYzbЍx\xX71 ӮW@0 OBEo+!2 O, 'uo[T zEQ-FPEQs@0( LnD5xS gG `f3*s +z&'!}: +4x(s5_±,)p$TK^ +n9z/U-qhL'G^bV52[Y͵F#' +@?\21'd\.cK0&dr쟲(X;Q/,!*cN13.gљ¹j]%`?.+0<b +zB񸂮 rISQ]^>`tUM\zh(k^ЕY=3so0Pgjbk|{WC-w.s 2WEv\(}\Tq]\K[EpeU=(%r-aىMz‰r}:q_OyK?mG5okeen¶li_vtOױezTʰT(:Gk$T4kNUհ+EsTVDj>2Mڦ]h-M?{+ͳfw}wheGye,aeߞDVծ}ܓ`@y+cgeeFFk_A2XZ>Y޽ju Xz +Tc<Z6Z&wB0g?h0١ +`Y((8;Ύf5͝?zn+;[ z]a/4q-c-c2v)}9b`;t ;5__(<`ŋ+?ޝ_W{^ו4y]EyA\{ו̂pu=?Fk:Cx=\Q%nf{9߹=F}Cʇ ז77V>LXi $&MdpɟZ~Z~ ZhUEƮ:Q}6,KAu7hl]TW+>\TpF޸ c:ZB^)HTI)\kJϻ.Ք05T4-L%/2Υaa$o> )) {aFxZ!9o}/QVA/f%.^OR&z?LDW3(뚈1'|C1WaV}B)^w_糰E\ΔJYjO]p-?S‰lnYyeQ)=j[t8f@pO!1!;.ޟl[S$2N%oj;;9}}|Rws >uJ+}|*rG~|*|ҍR y >9IbJ^B%d?>K1aȘ1XVpQ7S5'jPSbd<O|ݘoO9OL~t}|ZӘkTnJyLçnל&r +_XkCٓla>RΞ_IG4Uh}{ه2{~t<Ξ:nUug|5G7]gK$BM"> =}+Vr =U"M~3?zO%z~EawJ><;z&Lχ`-6"A^cZT2 owOQMxڿۿZdޙ4lm _>]#O$<@'*zﶝ@ r['H1$DtbI +j[u}U}5֩}tîd>n:W~:uYʠg[E/NUD4aFwPo'U"G+v)J{ٟVs]}w;rNk|pY}ߔI_?6au9 55,҇cnIs/M +l 6]] H˔ᒉ$%~9%&ѩ '̡pwGkf\:EK8,BRJ|te h{>mqm+m%% 0E׭Op#?)ABѹ8VOI]Ɏ5 $| 4~Iv՘_ iҟx!8<:GHD_k #t)_G2(kȕ$.0!aH#.X$ OG~/EkH&̼GA50nܑ _F 4 cN|4Bv!Qh&{1^6ܵ`[u^E5i]aoH>΋z Wr᳈*({ŭtUs,=4D|Uϻ֭a*'&yl݊pr[Eu $QOZ=?D9.>: V*.n{41,9 Y왩G?F"V >%'%8- +;ք_:w>iш9ChC}Z-*j<W{}Un5]Ql6aTƻ"QN^OqoS;lw9 M.J!0hS~ެd2!2̱♨*ʻU +悔L&7߁U~x5!Q)TSDHϜ/qcW |8$Ư.xVf#ubG\ɝU2#V_X%q&5?V#(H TC}x%׉Zp^D>/sZ*QX +ŨolKqHYoOD2F]^cDq0Q&5F&Po=ƈ#>׿1CҾ:|9PQcD@\DvU݁R㐨5OcDՖ\#f,+ch(R5@Q򪚳#$HS5uy-iQ3E$wHIP|n<0DjR9y㥗CpR'!QXuH/"Hj(lյEHf5(@ozKxlo kp6:">+*uH&h#|;DZP/@~!rT`q/a".QAdOcDt$Ibx" ,֟5 R_D ' umu:Ɯad Wylуr+d6->=".j^ EiŲPءeQ_=Y:D ?}9*ӎ&Q{~"wg'ћB#2M0poI:a}4 +Y.M/,&X#>o)[ʴMs=Ή_'2pHdsg҇on~^Ge9@^* ,!tr-hZOW̭'`$45FhQ !Q +44Hm<¹`2\=#Qpu}[$ʼ 4q^V LH4R>eH2x$r9Lx%r4m(D/ODM#:v,Q.!* GffofL_h+a(E@Ƹ^^bMldP^B!{MI Vi"go4A?x_wVЕ}&jEH+ԗi4IZj(^5H'pMdD Tܣ!){"ƈ6gWh8j"W/DEl"-J+Q|:|FW"ak,x/|%Oz8i]Wy}f1ut,!DKϗq:ښe(Hd/Dڇ8.-Q$>bn}>\2P hԖ8!j==q|@QWi%u962G%E)H{!Z0?UT·R? +܏07\"t5<N ]3Ð +i]H p͔)O!AQSXvH=}´F+1\ i\>eAȇK捶C^Y>|ae2^{A e*(?HJwa&su0TA)cZfH`F~s ] џ {\ӧ' vVtrkz9]_.>7?|@O$]?4sjaW]/6pT>ނXGV\w>߶s$dḁ}a"%5:UBFzr'uD۲*@S[IU[ݫC3N ᕇ47C?,n\iqD-+U?ℍ/c8DUyiE_8h^7\S^oW,ڽofy㖨MnuF2\g#V^fnHacm &z^g%4+ ƿ)Z^36 iÄ4=k-&-׺iF=dU d5Vc + v>T*sB_4ݔ?Ӷ5Iebf7na-ǎhL? јMBl{~4͞<2^! nؘ1;^&ӛiKEǘ*pǧ1}KdV6aC&o>V\ױ{5Xoc8YYL6 P Qg##>=QAaI:ѳ>va4A >aIX?_Fa4]ma|`0 +coe)t}f`Çe!kjaM =-YX56ZKp=q?\znuu?W3j8㇟)׾jõ?JR%o +_Z7*zzBX3TZzQ FEXáE[YwQ?=V^)Uw~Ԋ^0"Sgua(uQ;>RW7Ge/ר͢VeG&IʊuJZWI Lp{|Lı. bEiwts;N+\H~6U [ +@wt/w&~}v~2ʯLTYh(HX0ҺД0\Z%_7|+/x+4[ꥄ +?A}i!'d%!FI +hiiBE\ s4ԇ9`ߜ픙H9}G#lgf0߼#՟8-&Gm[wo#(}bi$edDVїmkW/jG-['`L@ &y7XRkIsz4Fr VD" 0 j/"ru>C&yTj n:! ؆;5NF)e(<%C? ՐJYrW]X%a-w|nh8!)t0TIHUp7J땜")GU;QM؉o],KhdθTyȓ* k^u^|/kDW-?Viqp뛦J -Pn֎Pylt ueN[,M(ajKa|kC~~yoX(Kl]F0"QE{6 r?EX ]IG +/(!M|z<3B[M}UDhd4 `뙇a"rA"|]>lc*Kj5 d"mrn2T6UG2ixp޿χ876Y] +mr4 ++Y'~@ѩ@K5U9Yp[z-UήJ%mg(I7r۪ :&(L;{Qo67_p%(klMy8q[=V/:t4 W8P%mF 6#QQ=SLVQcGnc.љmo Qrbam ~Vҹ UgymݛZR!>MV4}f̅G&rMLp||E UNln`X.4㚵f_#I>3"O.&I iFt}܊&T+WnUuk,K[6MJJv#3y،8'yjY],h}5tn񒻭7Nw虤Wu!ti>doZ#tXKkCU@Hi7l`~8$ zMLCvp <,@VRڨ0tC'TBp;*-iqH@p$hA}5`+R=#t7^ލf/@Й騸XIkA=3m؟`Y*Oنg ;–-B`m:XZX5/:Yx(Jjdvi WpH# >'LMfRR<$e#8N +<1ʊ['~Ŕ1g=秜kׅe8RYd.yKbDo:DQÈh&FX2e\0|GdȕqEF/c-3I`u_G$s7 }q*h80dY"ڙ ܶ%Y2^=9 f#4). U5 + zڑg\È E-msR?U +aзF 2 xÚ [%~4iEt8@bLj( `a@e>þ.v.3je)-Yސh_ +n|(mVE$^Փ?w%g.6|ݎ4u"zH&J۬^]:TMLʤZ=Z=OpU; QW 0Q"̓gɪR{q~ɫB'ݡ޿䥪0?BDC눬],4/ڏ|(k:{K8#gx: Qf\pPV 4 >iI'݁?rfp罐KRZhOjSe7jOe:(-@g1NĬ=4mɶ?LA!XؠIhyY#z7ݨ UT%@4@8gCQ+Q YGaM:/Uu' =]9#1b.8Ir3"= ųSsHꚗ':!>V:tb#9)t&/cQ?V ]k優 +*@iesFP՘{_F,Ipv DwEsR FED"ɲ- t.En# "6BHrL>יr Ya-g3Jf\NwYspVς/~֕uEFDτ~&EK Cp:Io ̺nyHv + +'YziJ!"}u3*,hki:Kǹ` v D< .c3]'j-xu~YPZ'E,ix=bc*oTY YukΰZgp,*P6){QSiQPw5USI.qmpa' +׈F4yycR=/y K$ Xْ҇]ILT@BhED&F@0%I,Myk ާlhMˤ]#AwaghEpb߶xr(5~!EeFҍ^=BƦ#kGAm۲l1n~,[d8%>0acb?l-5%M|u{3!JpW{iQ]7ugOH]MSTqI=Άookd[FU3}bبO`RI{XPTT +lۨRAy]eEqf-[y ׀&Z|yBw6R5t״3~ӒcC ]uHR^eZ7s>ljYOn[[BvVTQ뷡}tA5X$AB /IX'Rb3$5uw?32d[U] :,$:UgL;##HX ++"L1l9b/|r.!ϕɽFd?m9HAZ$jt÷/ +XR(e\%ӳ$җM>64= zU}JȽagTLg-e*§ +9q(>x2\6ڏ` +_*3=t@QmqeO0}DxMPPZ )!mU5)9Tp 3bxh)!b>aC-K^V+Q۪ N)'s54Ty$#%:2- MȀ[5GP!JgM0 + ,'IB<#V+>Ѡ~O&_$<ڤy1jҐ$ h:|PjVk6@*`t]}Z1X_+ # W8oօr92'0P,0;-~(nMWz10P PPw\Plh MQkKXLr),\.A!Nf~4*ѷF7U +||Qdhv]'YQ R +ӊ ՙg+Bӽ + 8%մh*K&#w5W'Λ Y +gI|)6 !ӯ;W~EF˘0Lǐ'= ؂uGRky?kS&v-i' 8 ;B|#XmggD3'h A~S/_N4[)mDMTRa"LtE6AI٣gO٫f!!ecEw]WHnkD ג\RGA,Sg7F݁دw)4~}lsr~2 qwe Hǔ7`€^CS1}k/XvH! +y\F '=j>p#^S$1P(& fR{*Yg#j 0i#t>g*w#ӨKtj?dKT쳽nj q&G@aW9DI^79Lu䧘2H To;ϖr})`9=5>nLv \s r l\w}aiV ]JĂmvW.GȸxiBzQ ڎfx/O3Fzc"'$W4LjV&j}U]*@0gsԔHjtAkM_!i}n HG?/z5z *K&7Gt600_mu@E왧{1 &'VjI<.W:;ȌVA<-O yRuTU-K}ۚUSuh >(̵8QqV"F/.p|8JRѱ +tʈ@aE_G-G <5&я"ʲYPY?_f O!2Yh'!d |\ROyӍ-%A!r;̚1"v]GY 'a ]=)R,h@d_wLEc4̨E`vNNP乬t(- r{w)Z]ZgDTI9!ROdBRżzm}M|rYզQ*}(e+m7 ˴F* ڣ2)zɜYBP+jdxRkrt2 J$؂+<׺R㡄?wU/UH@`PVU4Ȗ/RVeEIs ;#ȶ@ g? Prj R V"MuhpecI`[+ A +'!~=i=]7~bfpZa\VZ6={"XR^M3Mkj7Nϙ0^՞,? 0-KRCMpJVY| +r__bwgK(59IO?DQYK/I&+SSڠZ2>x\ ioBM +w鸚6t"º+|9.SB Bu2#Aj"tBRޔ<FjQkDl-^T H_'d%0M,Oj ծ=Yaw0a_1JJQi#B65vkDmϺ( +4Iٌ~Wm38sF vb.؏a. XT7D]EC&b +05"C \oԅBٰT"qEqV(pÿeS\z)F^0`[WM2 +/MwΛ.<#qT芷*Mf H&zU7wr*"O!/Lz#IpZV%.(kY^H#{$Pl͂{Wth50 2Cȡy&N+l> iV^o#|vmrC8Z~0iݫDU]oT2T9~ufӹƴc`rODpgwhiZQ@VC%\Pry%)ܤ&0lQQ_Nі GU}V)%k&dUW9* +أ-Ms^qf( fp:BUR]DҪ4U'J*Gtkh3QuSt#')FUO&9r꾾 U>Tf2^m0Q=ʭũX@]ذn.R +.^VUXȠME[%'T|S/zKvtj~,'=uBw RքjfbH"CfkƔ۾[󉂺5X0稚n'6`41g*Œ~$[ % k'7vG.6 OH$HKCW@g4XZ*Išs~Agajl;󸆞BJ}mf3DYWTn +6 +.ձj KeblBP\/ժnȃʐ]qbn $M7 +)>lOҐ)-iXӨ+foi&zgA +rrYxmv9 rWJ*o%xԔVap]sC Z 3GPl +ųV-XJ7 F٠)Vmq$~F*[dVvM`6^1p})A4]i@*PZ&+9I?GDB.T+Ez*&H6>xP^Zӆ8&H4KˁnԀjwj8h{_Lip՜}!rl^,sv = 뱆fc%]b0 +m3~Il] +/rOzBQ4,*{t#NhW_T!BD>Ckhm`r$i +?vmй&Yה҉#gw1NdJ4]T, [Jh7 jo^tFa_0[) Jzsrl;|”o nDS#gLJkU=Ӆ3^ǦCl!&_PV]|KdPaL1YlJw-RETuJJ%a.t ֕ALV,V27TV{S ]!jf0CEbgXϰlR8ii7U ι-7ڭ*+P-֯΃Ջuh.تr(6B8aws\xy$YR^Y +:=W6U0G>ԛYʥyzIMz r\,CsLi1+ArǕ{KDOnZAs5e2"@Dsܿ6ÿl0Ke"G>h='2Gg,gAu2 (0+N7q"= 9*Kaa4Lg5g s +>p)*Bf4Q+`9= B!;X9*,,#d+"⯒JXbo ?\(TLF^ Ȇҟ` AE!|UoX~|Ah_e2( hcC|q\=ӷMVE稄* P M~**#7 uJ]j?֚> +e=Ԕ{p3|;t"樟)V+F2?l+4_:tH9$HQPְU Zc4^6hNݐ`YsdVSYjHT6OU(5Hϲ0)*(!鲊 eCXe\,(ihT0m6FS{RxoHVH٨LtK\L=NԬ$orAZ/+*mfIPU̱bT"룶[㠲rLEFSlmШfRVYAT#XH "w\e*Jp)mbA 2`9/FR +s-f4JxE{4D}cG,Qnũ*MLMj!Ͱ, +Ŋ@vAi7ꠀUPk֤pL/wɦ5'+)vNIḎ +sp\txy1jsCgq&;IѴ *ayj{)WA%,A%|P?>Hne--D4ìeSBfEDG Eni7u9HO$$-a"u(:p?:i\/)skZٜD%۔ Z7Mi-Tw/N =Tub6-xUbQ%geYb^%t[5Tߣ:uvΔ D3*+TIQY>;[Z$㡖e-"b9Kbb+mkvo':$dGQVe!ӧ3] +2qaR; G@@"iE۳U0kcC8/Ċh뿜(ABGjG:ά e +> OykذӰ&E寖Y¹*dLZP!FTN"9W?3F8 lSX̭珧K/z%vC>7?J~%NhϫjJs 8FA5=-,p +W +1 Y :ätЫ;FF>AtK7M#(8RzCjIkzIWS7i f=FYK>~t~aç_ 使kC'MV0KAdUfDMs 1:V1I$ r0rR.GBUpB_):hXX?+A5,Of ˈ*}gX#F >*F6sHHM`FHVoa$ƑI0!Ka=/Oe)qMUM."*/amR)A$-b(qL0ĬB\Di!|5HXU]Lq_7;uވy_@$4v]lSoj_k5͵F38S:`*ϷIZqr(al)~,F.Za4FS/k1PpLw Q^/}5 +m:6=\O[40̍Ė#70BbN}H[ghjڝ/]Bn⣄9vjršT2~qLlI"ͼV>^QډЇlGJLkNk5go4g ?ި(=&[h_GA*-nYp2b*0| &7 /Yceح32aư,%a 15GM[a*X*-HL[iB.C#!Ǵ6!(aBȋ} +$)Q@(ŬzOڐI2<pESlh ӎв1z2k}G'K}}}JkFc,4OP|>/MN"fzeyzMA6 +[EZS!n1{sEg>ȡ|Ċ5Sdw 0%Frv4,]Fp51k hj`lAU,^"TxDJ -;kb(:SX#Ob(QA@5<ԉ-T:E5'[R(U?Mc=|GVYMͲuh +K?§&Gv2Ѕ (? +;_d.Y"l$ ,CdQ-z^ X&bt~"gD1|,Z|]PG̏ @|Ү? ~P*J ?N1}E V@Ȯv]x I(_B'zqQANOT +G`˔`dkPY#\Eˉ?Zh6dV;]PVV0L>5؆hBJAO#d6JKx#k};8Y'gi6ia(je}r>@V`fU kR{"=Ӷ'X˲eXn*{< oH٦zf]j۪j 5?:4є?为#".UP,>9`WNynyUǴ 'F$'FH"xȾJѡRF/ŽW"]pf0kv<1}%-$+C2s,뎄s)ˆl}v >x&}O~1p[eqvjQm{B翗;4`_wG7ag[]nfQ[ ~,f@; c6 qf#4wQ!(G\dnbIjϬϽkD<~̷DIr$:cozl¶e~͗ A./_ND\KoS,t+:Emn;m-tz.Bx2@<\n6ydFӓW8 U{"0hEYE^ +]'h?WriqB>c0={gm칮I%tq N/8)%W䡳Zw8 +ޗYcZrH&]~bH|1e?s +vl@nYQʹ5d6~g%%~6JJ*SFqJIWKRqv|̀x ņn|}UFqR%n +#$!QyKJid ,aJ/:n"_XdI_9dz%7sg$ΜF= DTSQiV8`?f4NTBGeKM&R4QLNyl2da0,N*oOB:_&dO΂ťj#:;JÖTQ JJOJ楙[AQD^"Ea9ݓ???/Tտw?o?߾_>z@pو F*"del' $MLؿϓ`yD%{G}krD5XnK5"DR)I>z3|vDn[Q]Wi)=R$IjiP16y]D`D`cTCHRr(FNvJ@\CQo)2MȩǦ:I +a$iސS2Ֆ8MWEWIzdss\+X֪];6IͦLrޟ& A|n@k7l{9QD(g;Ll6w60G!9 +?Gﲉ9@n-23f.R +-#D}\D (e͎B4)S?Ԓo]Fb/9ʬ恋a}=b|Z5?"щB$baƁQc3)yE5gCD%,˚8jHͥ}*Ł~!ʃf"/;/q@2o8_#-D`@A F抶dz nx J&OsR*tm4Ώ6&<=FМ<n> +8U9Hı=$sWKX{>xcP$Ɵː$!p`%/'ꝯ-;=ָ ؐ^KHf)hτ#i/ boqFqR\/Vl-) ¿7raQZRV̓TdvU˿!LwEym'xx:7cCy'VE{nS^OdVtxpth V**^M3Ҫ);úψKc<:@iU +yB-NiM8#^"*(KM Hd["KD8W]CC#+FM+/O#K5< $@{I #a{szW_kHPy HV_! !X&Vs KDEhNlƌ~1-UNɎwDv`1dvlzn\WxNg@HS(}!~o7-XjyTNrtj +yt77Zd7?O7hYCdZd_/Ge:%$#̌Е=T%_&*85NEl&(Fo YB6lE 5d !XDsj5W6gK1?g\tl/9/K}M'`8;,+M'U Aౚ"K9PҍZ(-ϪJzB)u&R7| +8F2 ]>) +>u;zaSP! R4ESv̿j @>P?9[2J44[MFtհzW#&9xB??wQgK>۾ȸst V]϶dk x? is@2t^B\c]nRR|U[nj=ax+|#(WDo:K%Lҹ u݂ 3Gg@L5d^{(;Eo*dS(Ōt7~dF sBTu@X5^BCY'̬rƏVmDM9$-WB=CGe!ßnɳZZ +!jŁn- *1Ӳ8 +uu٪+5*h?Ө8?=;^/\7߂ɦ>eZvS1abf_la`!aA +,#XQSn."L2(OU@DVQ[S]s~S]S@ĴݤR2/`"@BEu;z*:BnF2|t>6+RmEtJ4\gj2.?; S?,zJ_TlJ8jʍ[-i;r% sqw3c= 5\ݴqOTwVєCOc/!m¨7x;ۨ|PMISƓ^Nu{gD!ChN T09Qϔ(e:DqZ ? uhRxY(,`8k'\`Z1`k +:H2}}2<3ԫ ^vKϡ(r@yҁ6d;arؔwS?> LH碅rA%ЙI~mv EXNEmܬ@u7,"뉩v4`5 Ө2C'6^,k0;T;@O` 556r8r*ȉ]==DPiUF`^"%7$-mewa"\)$AG #lpē2:iqH'O y2 +ELּ/ۢQ&W)e#Gq/t%`yÄdXqC s:n$%)p@ Z`帐mNz< !k?]HtA3O7tc*P_IOԄݲKZ!5Ք9'i=bXrҫ ,j$IxZ^M>݉j}ñ C*Ѹ/,yů!zŠ!BdF0:BŚ,fZ´ YQ)JL~+K f>\ϰaۛnˆݶVN"- ^s=m }7i)0-\C#fV/1B+7YK{= l$}EJ2pGcڱMmٺȜbFVڼWft?ϲϵnwk}#6Fh̓k 0Ʌ8a"q7SN+ N{W+ V'=KF -`JG]2>x2Ktٺ5[ٰ1#ځق&Xе6JAGKHkab֥iZB+6p*hтObv .gjuC*G,:P䟲,^C7_Kr@`n])wפS̏\x|}hvCT%[f?$lRiw(y۸vGE<Ϙ9i"&I3r)yI_Ȝ[O|i"ѯ\3sdB-!|)91tçChmiQ5FE,¹(xmSb풒PDKY2.Q_ADkٌD׉@O{A&cR,(b$tGm8* p.i3)Za#UIov>L(#@*SSH ui9ŒR\?`ՁFQ>v'bUЀ|MQb 0MnqRDl8IDvj"{٬>Гg3FO14N:)#WY2Lr%${~YjrB":pKvR 6"kr9s[2,-/r]]a&FiSy;椼)0ЄiRCzƁBP;wi޵9.3"3wD?]Hh4#et\q'tsBKM ]$9ʍT(t`@2&}2fxǤ9~s-d$X@IœpqHmTWX5"kc8‰xƅAg@wY fp;Sp˓:t/LuP,EZR(JΊD&&"钣]狚|MC +iNPQgvOre6\ '3`ϐN-Jč U"U07LrkQ/ Ճĸ jf`K.5!di8t;^%1iЇXz(r%H'np^5Zfxԟ2,IxOW<sw*x0rj/[ėuZ``k> [aهQ(f;<$P:+\'YTW)wn: +m]v*TN:3`|9Dss&)zIUdn}B6Yp2>M45d3jA:C 7nFbt9d (#(<$h"2"%b ܻFpf <$ς a tWD͢fj>L:Fzc&8hE0,y]fh*dن[|3#X"$J)CrsbrR'sH:,|<>ȊL\zrv `2 #AazyhxHҀ zߕ00IKI-s"ÉS^0!]1ގt\a[0#W0<>!17ۦI(XU^)czנҁ_3;_5wJ3btaķ;# ꀫxw e84  L^ p_F`fQp̩ !L*M{4Lx +XqbzIj2 J<:ڈΒhb6!0+EJu@Ծ:b63OF/ |f'UP0_ ^ 46Jhw47 +80,4q02lvFWl6͇G`SՐ+}_R=80xvi4'v%M H-QZ|*(+%.4MdBM\%~)5.tKVFb5FBM?A0'2(I), 4V"QyK,1m2%hU(t +I7]_#"!6ŔDs"L>3|o=Nxswc@;"p`9xxI/pHAw064p +JLrՉBl|ۑC`m I˗SrDaFBEG3Wuъ>:^ X71U*#feKVf&G5."ej NDtA wcE$ne8VF#-.8YāI5-K"ȋ0FѮоC,v?YʁN}aVSxe) +HIU  +ZA,gDh`QNV㙅f "sUv:wXz̑"a0&A@1> lJ$Ȝ,qQsHuSj f S +rMDYJd< oXEk<c;"vC> AC&1LjI\ ="y@UA\Ntg-j3Ob3{|!'D ىh[K^BX .E @􅶥#sB$p“NQO"C0˧@ [5,Q̾}ysddM=Cd@(VE~Cf+P\d`En%*V̅H G35'eyÉgL8UẑUײHBrTƃݢt"MopC䏜{H647"5huE%~H!"f;H1ڔSS k*TpKY%P=@/ O (u}I + +&Ï ~X2#6aEP9ytHk8DQ8F 'Zv*Y2u#@ +/r5A%_3l4V $Bhxd3d xBЃyj!>Q|c#D +~5`w&/ 5JQR&=Yd;&,x$qD2 H~HCE )RƔoNE u2R\dG+|'6LF}s@^kaA*r2r/ȉR:5"ƠR.jŸ@B4ug2(SP MFf)MtOdv !yAQ#%`!*kyJ+x!ҝbU1/j uΧEgG^d$CLbvH35d1G4 +3hMB*WC@-? hD+_̽4ź79QȆYpP0ǘ*Ɠ{h8':P#֤C`Z$I "@ԍJJ8.$76=MN$Oh zDא.O-"PbO~ /T3p2kcGvIgWl󂧉#CHuo@F>FEœ@GaDnDW/Vš~bb&3<Ah'|t[7)#SˆA#Oԉ`<( :"I"/G-!G >L$Q|0KdPեe%31#wi!Ã@?Tj$m@"H`U + TBj*j!'WpԹ  *lG=v +28HjoP%}10QB* }#:[z>[P}p4"C$K!\ b̋6DX# ^z] +SL@MĔn/p'S1F|E9!d:ڶ3]Inj,0Ű,H^9IFR &:H<pT4+FꀓǑIf?U F:bG +&(RC@htFz>m2N?rn4 cfDd Lːت1]radHS5+c]: ʪĐ.h| Y`WRC +endstream endobj 79 0 obj <>stream +JgՇ<p: 1駎H0mY+kd CBfQW*̩E"Tg}Mx*S/:HpUd[9GTM3HdrE1 BLydIϪ~dǩS$>0ZgL + Bq ]\%OaL8{6\hM[h=h͙1UR 9x'VYץ3 BƓ80l&BLsP~X,dIj,+Ltw,S9q XL FtA <o=:O*@s:JA0=>$ԅǩiZAMC 8 rMMA]n +3HN?x]=5A@725 xO*f['go/֪L#C^'SK]{ &s//&ԇzp < FQ/6LIg*(d}.LBIBǐ\Fyj)M[PVM=?=n;6wɕ`a*L9K҄NMR W̻N,<lq/hg5:EV{T##1l5 +Mω)OS~kaO@嘤"s7dI–N0LrQ q+\Ke˛jЄ <%Ⱦ'^mSWXU;ke$. +Fx^;tӯ1f0PHG$B2ڄ$ inѵ}!OO ,Tb Fr;I:f yQ''r&4B}PMBYQ@0} j2fBbRI}Qj>| +v5\$.5v"AX +~~Ʃj(Vj$M٨kJh@߀xWyJA} +Wì9JuFbɄ +Ɲѩ̏IN,A-rtS/+D&1$d#a IKJBI !*Aޫ%J>)PqW|=k,LHIdʣĈ([˻:Ox l(kf #wFQ?^j-ҠKAcE`ͯ8+PJ^x(WX}$ +d@wD(omlv䁛ƤEՙ=}QVoT鑵9qaΘ(d(Ny1֟y%*Ky@O>şYg_fZ_V} ;H6 Vi?n7_?ŋԏ^^As0̘ +9~Y8^?ַ~\t oU x0${Bʯ(WΘl~ H,+paBL#BW7YXʑZ +ӹ~\NdXRM,V;_ +AA2 (y2]RSJ; d\OIy)o%IiW C)bvIbHf*c"4yƲx8W6 A[l,PZ58}kA>rгf7 Q)2K+,VxchֱAW puDwFIQucV,0-%@Ī߅}}1jaΈm8S_(M 5wm 570Zܥk?%=p)J˫] ßQj`SKe91JI-) O4<1E2ԍ-{]""@WmoBh/"GkNֽ=,U,O TBz; QL&L Q@2@9-̈́"SeyAI4Pw‚>w~qL[Aa\׽Yt՚ E Ox2R\UQ2Oݨ4(3՝}[vGGԧK |݃Qŵ,YnKf>~vy[q8 pVbo3&MF&ϬRδhV^kUVAmWd #WRX K.ZT3ko-TmB(1M ot6z}@ȓ6 b7F?άP=x =Tk3V[>/vF:kN0 *9siZPk~I:lX˥z+~ugߖF +bg&HaóZ K!bjduX#}g][ $wӂLwtθP)id5~An`\m=!̊T*|#)A'D-3y{!<*O2;TGC}"=hjgפ4k3-%1Ӛh!\׷x\EG\~v+Z?MigI;dA'- !v:CؠX=-S~`{+8 Pu;?xb(.)JFƇmrPq`S;KMA +C^ "Rk7ke +rw8:d_n xgj!#g[U&m2Ke}7szִ4`֬2D0 +!nߤ*zUhXJ[JrZc8h2͝k۲f|l?Dʁ3hֳ#9XPP ąAp%`B0gR~{&-`$cKF {]*Fm#Jиi93f̔QG:}hu|qPA[ Cq6?{/aׂ iԄ?Z(hyhL{Y>}0-.afsY ulݪq5Z r-ETrvad9\t7_lv+!JfćES8uaXV~jXIFb<HSV:H!S\MX.))r0tc2^U[3ҍ ;]9ۢwQ!gHK$%cZPV1JKa!7>糎E lͺV.٥[g8,D*δm! +JOn2M,ϵ@z _gތ =&Sm?bj +k3'cg hī;{rMdj&X:Sq\pLBK72_~f2>=LMȺ{@څ"DnmGf a9kD.`vc۟p;v<ɡָI625e\롚 hy|CgV^6ajcBOpBQX>1-+lEd-&DW՝}ݦ^UmFoE%pJan@ 1 ւ)g0'R812EQ,h^RJtPʝr7 0é"0)M U;iXP^T?rhSly|3/1W\$Oc2NZ5)Y7\ҲP'zCi/A"zR q`k\AD3eB#MGQxŅɔ9MϬxR.6]FI2vT +SMe(H665Tw,Oޓt%+11ˤ)Fyj6e!"9ͅg rewXe"oa^S.ۻҨf^4 vU/G|0%}k aŴhaċoh\@ מvKV`0 +Vk)z{F[p&QѾWzb֋gaJ!`DZpk5y*4vT%ߜQ^}\[cu--т,rKv+5L]]ҴJa.o0 +C攜!-чeaʊu$$2 * Bǘ /K h"yYc^هe 91fZMTւ^D@V qVRG0;16oj8#x<("o1hf~h +8+܀B!Y}ȊO|r\)0II$\ZGԭފ긖ۡeΒ9-޳AƄ;3dM[cR`7&-nx[&!IBfwX3+7aa_BN[S$xaeTis:j,sv:飼(E`ZVY4LxT.)ƹrI+'Lomhg"(fbkߌ=3e{11@Yw0?- U7Z( +M,"$'[Osc .pw*Y"j>>T?Xb1oȑQЬf`*CLb9r dkJ-)6d:m$qeE!˝noj[+7EI +CB[=7 +?! n&* 6mAPoB4>bJ{`'+qM(bJ:-C\ R,2: YRK־dCGB Z4JdݧwC 25-T}2$)[тmV\I)w/3֔j(GhףV(qY Ig5tڠ.cujDH,1U\ԿKIɣnL쑨i+uEdN*'M}0"Y70(tV8AT#dܯRHvY$L IGV74bemĜIݩX:'hD&3G5ePLn4(Z_BkfCj!Ŭonv{yԇ[ed$)YUB]Z(TłI#!z/1GK!h,_0(u?'|\!TbGaPS *h^h9`42,&Y5 "Ȇecګqp"KU%q)6 +Ns|_cKA,bRӰ/Tz9Ly11!漩Uy2?m2\&ZЪXu|S+O4۸9."^ҐiwQdUo,fO"JrNzFT;Ac!ky0QқEJ$L$ppDgckXGDB` B#v^04Z୪nᡙd33-/׀6ri>0H05cP(t&jݦS%t!!y6d\Dev@X2i+ߚSZRW'B&Mfƶ (̹aMG\MYoԌBV8#~fsEN# NF?+ATM QPpCs.Q]EiSd"U@@y Be?e8E= +'Ï+Re\8ΜOCD-tFP~SSشUtsӈƇ?;9N +M+hi˦ :v5 iqUD;;cY W0SDi1?w9Ro /&h[B8+7V@fsøDŵԱśO%% T,F\ 5+U./#D[K4IchjⴃZ%Y\ch3~0.̩Y%Ee=ִ-ṟ0 ӑkI 9rEWAڦ$oȁ1Z0}y#M(lDuplD[XlFiP6P2hw[5k0<8.YaЛ3vÝv{lF\NZ8 Ran2;P(B*y!\!f[Vͩ!@-T󎪋0kb Q\4|<MY>_ɂ=Zn *Fy2$`#v-L.Noy8n*j)7 jqS {u x S7Ɯ`$ ߹/fZ/`tʿpGy*7g$fK7jl'5ڤ, sЯiY7݈G +-JBg vbBZ 'm=<ޣj!k2x͡޳g&|Hc5WC +C"g/ \=M PGP)`"a4ࢬ-J~O(O˜|5Ocswlqxl +#- +G YS`6e̋{rm3у#”RDZOSGyr6T$Ù5C&:+uNZ>FuF fLeg_MR)eY,:ijSE/ $3T !s!K(R>fް[N1YoiJǂ432Sw]HRUF +:2.̏4ihZ4ܒKh} d+pΚ}cրGZ6 !opo881i81 +€-~PNFB2z@޽p.KAkxݖ1*j"9qy1};3=Qd&耙r7l:i +%pq y+`$wCd"8Z3~yoR LìfXt2[ X"ѹ<W`gG2M4"4Zнw^yxvE qÛE 2a2]fa9E N /B0s٦N'qpNՑR&+4/ =ȗ,=tWXvEדyGp`峺hq'-KپV( _ĸQ( EԜCC47.ݒ88V#ТGS\YTP"oyڅ!7k"Hi! 8emc[hlRU{j0vV3V -;aJ#ɉB72%/F!Bj1`1c'iM06<LOeOK)]-L#LR[0 'B[V-ڦ/D-w2MC;)"qG7|R9.CPM* +(V-9,t'mtFأzf쪆d)М\Ÿ0uE!w,XaXm039-nɰy3"wL'sCE ;̴ T$)3cf2!TLhb߆,W.5땙W:"Q9Ц:".m$E#i;?2-{{6hU9U=m=^8T 茣Xgd.em^OG9ZJD[5T4>8 4φp Sg? +?N*q~8uV19Q(k/+(̨Zn{w@m +1C;bx a2i:a~Q=E!%< ⨜gh#>,~zaK9}$76*On\1SmlR}b9XeO}p|v֩-~j*đ#8ʬY͆fpJXygsOۼgfB./CE6N~**Z]rx:#i6ΰ[|YP9Џ0ۥM&EXZtګ' B*l`NdgEa#H5DvƘG`r@-jx>flix)$`ˆpJ}[hR.WsFHi]s-Bsh54ҽ QZf9jV4 mm 9ZRJ6 Uh-ЏXg:UddƜI)[]}-hTTe,3efu]ۂW A0fQk#N/r7\Yg}F/ \GۅN~ cn!`EI >`ӆ{QL;5DC_kh|2ÍCY h8c ETQ?Yy͗, xZڐJ^`0Js+hPM#P^ƕ %f&CZHJ0wJAI4l +20: U٥D 15[+3Vk?HDHe"mʃ)`恳QxтyسͪJPhFXgΟ#S`PfC]+~IO?@B[ iƴz*>}' a:.F +`f8l$nَA!7٪]O} 7x |aӰA ]i )X*L;i)ZX).ԡ*K)tcGS`#t9´P,kJ.?+ج> YN#,բ" ԳyFIt̏6pf)~ېD7 lqQXF(z';΍&jKŴT4W4"h$Q+Ɩ (J}ֽHTJ]Y}ΘiRd7pa UW_m>IϬ<;* ^希iF.`^H3!MÙ,fMc-VU"\(58q(`×aR,8VFov0妶^QP6szZ1۾@/NCc4__"VF^PQ+ZN6RXG  \ .Rդ@(3hsrV,~k0UaP8I?FfK@ ԧf݇g<o s F ab\fjq;j<*r̹ vn1 "C͉9- F176KLERHT R(qٸ@G-g;"lۋ0)[L⅄3-vh%-lYKfGKcVxc{b eFFQhqWVxg ə `x ,6|C I lbNP_@!UbIn]m88brW\o33vO/qgz{9Xx#\`$Y mK1ZFCC`,b֊:l]PPYɧ}e'r9r!#" + X$VT&[qssgƼ ^g@?ZY8D,3}FE%Nw0Ccr-*^1q(T͍KϘ6ʳi.v|4Y6kqPH"vf Q`e|~ 6[8nD@FuiCj q4u`h(!U+rxr Z}j 0)jQ/x14,H9`_H>v.5U5{"&gVN3f+ +facX Ƀ? d?\5.ܴLvNĠ8 m qdYcQYL(|X\7iIVV{>̋њiiDhd y1 &K@=O0]+YY_Ii0mzS ,$fyiY.M_z^߽\K??z~~~xvjU/'nį3XX?{?_G_컕w?qWڇ| ߓ(q'd:?^=O~EjC@;^|cPO .U\) +̚O(~sf,DE.Gw9p/uGVc 9Mn8j=+S%ț jɯ?EZbvs=O\bJy?ր%GNd/& uPõE얩ӛe0~bZzI_~mNXs#3tZ\YtrwHw^[_ fJmKM:x(H+ܞ p.:O]QzS.GvL-J6 );aW)\c9^(l&ecq6 .~wM{‾%%.}|tk:f;~^!a'H=Vx"py@ rnG','tٵb ֓yOS8,$Lnߪu[!߹Oh?>>œ,AsBs}OnT]CZLÍs6}r43u7"hLl{xo}qZoI/|q/^h9٧O~|<'O~{tW?:^{z_|uuru;V& yG{.=o+Ecnڵzý_d7C#h.o㿭y0]x+urvcmuro?Wo_]|3(޸(7G]'&b7 wN?pO_GڀzDº6^=yRx㫇pL$NA?{u&tmVmȻ::hxQqP!'#5L?=ܿ8wuxohuwz{q~!ÝR ֞^ɘD7Zu|yqr~u7?]oҳ}63`c1fSdo=)LO;#BXo; ڈV׵mX]G6r ^\9;_`/3uW_<$Yv}|t|1u9ǰH?ig@G;߹fc&˵`i95._`٭c <W|ދhLի j}*1o\^|5eMj(G"<;MK:o:aЇdm$w; c}1e;@۶nm <IpO[ #s9$g}5NEϐ%f uo}{!u}ӯO6R~FV7Zã?ͭ[?k/4nH[mA[rc7opȭ 7DQ9`AZcS>mbm {mXh?xjr?77k_ot#WGG|c hX\&]]lx^l=lڏNoWI;5`hkn n`*$m SuMh;ϥ'/=_ϥ e\dm$m_s]_ع.\KMfܣ뻴ж{K廴ɾߥgȔ=_ߥ ].|~r%.mBxƾKGwimL|6Y-]`h[F4~6bm\gny~IAlnu~<VqGwDqG"K 1m\&~$6ZYw'OZݝ43;i~nwT)&WHs}y/OaD3&'篎_?P{ë/6"YX1Q]]<-zTkmGu!|d8*th+OhM>grp,9z3׭?V?G|OԸcgA}I6.d,?rfGlKHۋw'Wyqcґ}6gxujBh #z`FbDX+(=6Շr^\9;ڀ+nmt7oA|uբH%~{y||if?lɷW^\?亰9>:z>9{@k^xZik3GV_}qB=tTHe/[y3y?S#?u:6~svwzʼnND4Q;MND4Q;MSDI4QBj6A{4QGW:׾R9(؞w3N۟:ɖljy#=2H@g~N$h\}yxcw'?;yے;ݽomw pgb<_;y?V x#QCFCڀ~DV`?M)o$\7)o6NyS7۰J-NyS<*^^n܅ˣc(??7_XC|}yػp狓 Xu9>H@_]\=/@n^zA9|whk5ңuzۆ۶v/A~¡[VOvutlˋ k?ָvrۊ+S=R~*_~/3P2sQgT8o{ m7v8 n0U>">jZ >},lеNw#K|OwN>sT;t'xlsOVt$N@ ;u'ԝHq + mى i'>a!?/.^}{y%]f}yf4eH xpd ȶ5d8?勣ӋvhȵcmMFeآ|-XI7f&Fc-zY{iac ~*S4@om3w|Ɏ/ +yf}x"%o>h׿ӿۋA1hĠ?Js`v^[l.eǐ=gaQXEBݧ.なFoD*FbDYx+kc\Ƈ'WoΎ7C?=!yw'?;ylK N~nKv򓺗7vW󾒷QM˝U Asn~ln0]v\RwvvsOA^lWNwׯQbo~ݖ;7>6]o{q CB>m*Yя +7}C\s^tn |΋營siepyX$8oX+ Pm7 ? Zmٍ~o -f~?KǗ?|2*aK؉ZϏThjNVD#e#~\(!No^o*  4i|Xme>%%gOe|yyz޽]eE/|qeoe/oOΗO>şJ;I~7A8'n'p?;1_^ʻ՟Vիsy䯃O~b3׋V/䏺GR5ء._no Ү}[Bm5ҿK1+r kOJ3Jnaogr6tcpN u^;_xC]_d:;/ ZC4?Kc3Z}{yk /<_[_o6ޕޟRӪga +~ҟޏ?Ŵr{}gc9gW~r]XT6̭$m1vId螶86r,rGo$8ozRYΝwVI|wK菉~_Wי__u!+8e]R|Vs[%Noz|x*~xxAw.O^ﵑ|C˻KګW^}L#}s?p1+ '{)q"'NQDn!ڞMP~Sbo#OA$ۛ 'gAxIz߿[:7"˵sPVPVxbWV_ns;ƯWɉx9y-}7ӪثMl1Ow\}n~~_?p?; kj_y7u2b+ҍnr%LRJ +}gJܛ>Pėz-EkiZC\<*d*6zpvPOB׉_}VxYЩR A׽{>@b({.|ś/m o=rojzM-^$N|nJP~=^%ȟ]JnmӞO5OiDD +b$$~bcN-Xt^v>u/CvVYW=>|; f/om^_\Y첾_]|s?N/{BBsOoFLׯnox`~oYCfy߽m/NOC({[EA'$!6(KTSTDI| ws_7yp(roVDWpVaZ웷q(k Ur+{;WlEmι4ݩ{Bk~;:?`YosEAIw*&QۛBKeO};ʹZ|/Hff׽jLYG_{,Y`;07~;lL>#]tqw1VOrSuraJ]xr3ʮ(Z#e&OA/nYMї/a)ӬJ8^\z')'kKc_r[5}/Nr+$g7վ }i{-Qm +hK9tR[}zy}'Us%r> w΄IX蠠3rlbK;+.sǹ ;%]T(}S!"on,fgEN`/,\BP/>^̃8.jeLĔx"D"r"kDhqqĐEH":l;eƎG!B(nW%5B k\AQ.AD1* + d6Kbk~ ++6 u(TL!<54uOfY/O.qzMNX@,Ĉ:!zfF(x S f +XW8 *¤$XH< ,g%|>%L8 +#$bAܚ/` o`$h߀Kv0oY~3|Az-aPhtʔ%& P: ~> fSdHՐ;q"!@yA#谡?$hlIN`A@2axb2:QPnf +uv7A +JR2p$C*^G$+ t9D+|_`'@/$E-JpUf_$nx8O50>?aCS$i9& qZq +OdE>^pc4k H';BDIhàPl 0S mF_հld`9 ˈ_D22ۑ-}}Khp V@пibWoڗsxEXQ \UD _Mz͡;ifМņŐS:oi_mi_&Jc81Xbd!YJʳ}5#Lr|&ZN 0<-:և6H@\`8*1&˸2ѽq[eo\x1^:}-|^MxhTWV1ny1Hqu`k{C1\Xmx^s/PpI?jCЎ{!L%cQsbvˈ\0% {) f{"@Cgex 9C'z"+513 4) LTx +6'=LP|G CK]>+0E-W#pzkFvVoeJ&< 8~oh:/'@J-5牂dtW@IY%&JP[SNZ=U'ڰUzP:̀v)Eh77T[BS@c(*@H#P:uԒ=q74TkζtYkWwK *ya$Fk ְߩ|SyY^T`z06ٮdm[#t.TThzj,6J^:1rڄc< +h5w 4jNFwJ4cPS;)WoZѧr|5M9WTq Z(%\=kqü?Iɹ 9h6HT_7k@2!F{МH)NW~2+?疟lSi4 HW$/D__7D"HW$"QW$D OD2/2_WTJr!#'[QUq+1?Q#):+_PCp nJ7?WLbj b8Zo)^!!GE(}9rG(YH)Mޯ [0Pa%#kP_S~_~kF_>{#oi9C/[m&N-Q@Z)Ԩ $-,bJk }ޠ^tg߷>R+MG,*K*@;1/V4r"z +L'>%_WQ I>K/H J2Ib+bT*E/0 ĔLs mTROnz>Ƥ)WZX _C枎q"NlPBlfyMfj U[_vF!Ɲ-^~ԢWr,O'V~*V V]ڋwͼ98i$cmPމgNs4j*:{.@_&nd~, |b)h ^ucr&^7`ROIH,+b棨8WE\4F › {MC" [⨒)kXR@x,,|$0A`ʩ$'0e"U^"Uŏi\,!~X-ӸKDp,+ ED/֐@Ć%K ce*ow{s!LgAkBI -%A<p͓j+)cȗԑYt $HWO<TKG +r΁ؖ"TR#1N$ K)Jᠾ \dr`DLo,ZY&5E:48JD%@A K6Aī&aY)M +``r(@/^hֵ$'!QjZ}䴤1.U$q&F6#v@XTJ 8.sJ qV0$C0X="9%u5X D2Pw(F$|'~K OJIdBNU(D~Y $ESDYUD#'x%9*^l3$X,B6zKȈ(r +4VG("!P@KHvDɲԸ@Xo'7m2}OHka`+# rS[*8MD!X*OּU&8,IݨGd4#S(I nT~;#O?Ve9h +R%*(Sʰ }&hS$/?@,"fŗc$SE'&ճ|$ (C*TfbQiG/F[SS TAotM@[l Tvهb[MR 7?s:a HNV/wUN|,g$&+U@9-@@#÷◂D|'jD3(&S\XΑX3@r Cw;桁(@KۦREE)˽hRk +E&/ǫ*G(Gud" ,?`y+ڟC}}'Fe'AσB<_"ET XTVǗЗ!V#(^e2ݘ.EŐ[9"HN= +( + n9 ֔k-d~K0X*ATDu-fX- +L8 DXsh"F4J`alQ9Ђx{K&2eutahW# ,`0@Al.K$:$0B^=AN_ 8Jtb] W +7Q(GtyAgѣ qb޿h#Bbu@b@-&Օ,#G͡KHyMJ6@ЕʋcQD U8 OE,o:<F&+n-`*E^BY)!O*%G9UG%Ƈ@\5a .R֖!I5=2jy!zXM<51s_tw!j]-W/1/ƁHa2SgD5Dރ +L|Ԅ@[Y #;۫&t*#~`́䆯ߌ= ]t/`G^\߃6%Smڠv245^}쎊Q~P*UnNz@NjѠYW7 B:AkС=?#4Fh;|6nSDt0%_k 1[FZFIzku0m_b^i覉vzvkZE`o8zm "yC}/%ѭa ARb.5+@?fS\A`:^7^P`;fyB]acLIcMY ];́5(/Z:~o^$۪i@XYB_4; 2ް&Y%!ex3Han0zj#6-Ԩɤ%6Q+}f1-jE$`eXS'+xO2b>G54?&5Iq~h3 'ߵ@DR| s +(~5VꨚFMi<9L&MmԄ4Akba ~kpc.PjFV5ҩR⓼>*ԺՆUzr{u.qghZr8-jg$V{)wzvߟ"f(m]&NV՛GG/ctϒ(.M/& +6$N +;!/UG5]py׭@ Gn]_FȺyפy^XYVC32@oܒfy)e1鳝'7A o>[ϳAijt#<(#Mesگ Ƶk٘jg{V,xMÙHV&s agK{r*d<5=x}JV=D'fSWg;j@4b8g(GUw:URNc6׏z3hU i>z_4_vX*kK(kɞkHo͎*rܼt RuaqZO4t)g1n49P YoAК"q֩u: F( ڋZ$EI[/d6׵nk KBk%wZ}/p/0E>uEW*O5Y{quMi}liՆP(*JC+|X\v2N`h2XR?=.Aز‘ mhQm{ ח}O^cV;Xi1098(C$\=6Y>' ]YfCG`=7Ծ4nԱ5q%rp*ڽx7u *oϗE܍̰@:=05ЦjĞmmt(ȡQ`8_p*84..r$i# NHK_ qhu.p-PJ聺X+@]d$]H^S$æ"9(ZB->!qAYUNL}vl%V5iNK~ը.o }=bΛ][KV>FIm5'7p8qAzO@mg3P1܄2 ߐ$kMVnCz\%.᯹?{@. +MJwxb(`]@`.@`-ЩaZDlV O~>F7 +.h [ƛ 6t]6eF|,E}u2I\|m;Rw@0ѫBT>(n4ܧ6rs-m{n1dqC+ðAMB%/l]ݛ}w/9lpkA0BAhԎ1nȽ P~v>FG/d #/EFe 5i%Q X{9O.1cm=9~l_w\(E-)ys6ēl%=XDE7ьDC;E9^ +QSeJ?7c+VoJ4OW͎ڃpx51 I^E@Qs֭:~|.QO G],uuŌ 4,ºG#EtoV += U)'qtyOayxyvZRDcڬm`r"iD{2w.fJk3a7Z?RNEdce O*? kCn66mcϢ7kRv&髧Y4*`>!r&3'O-laGu;Gw&GStLS5:~9;|n}t`4QhUj$le*cawq8;r;N0W#ʬft?nZQc(Bk'S/wP.׶ʨcwh3dq(:7*e!~ nMO٭a;RˡJ|__N9vn§|Ϛ0^G?ƍ zXxx~c?%VsJWm10P^§έI5-l\ f`s^S+Rz?T@L&Z[FDǹN >}uߑӒ l]֋kєox*$jN(m;ٷ2la?rSDxWV2rI_7'oSH+,ҍtETeS qİO+D-^FFϤ0oɔMPi_^޸;n(d2(WQ;Io7Nj#NZ!<C`C;ت)`B]lw^._{Tm楷U?+W[?ٟ._K  rk& 8̼EgLVsGbq`GõH~<{]Qh9wy0ON΂\ +bZWU-7>n)?fyBt4{gzK/ +~O8UGK&oC«ie$fHTZ E9 z>aS-5P]0p4W9^r%nLaIGwmTʝS"O>l=T^Ocj0{T;&']G b⵿5u/wy +}Fh+"G 7$oS=MçKعEyZ|>orWkzDz zL:[t QbFt %Ym\ + :4?`Dkd{"q!W}=(OSlz& %W_ +SeԷ֕%5q=-teaymOw;)tjY7#O@0tӚa裠©3%u ґ[ v/WQi6`b+#hAQT GGzA0LI8pFEV,郶B=J؁IRLnO!XU&GQK +?~f"F,FaTDQh 7sׇrvy ~;GT z|UnQ1ͷfV)7+WKTo+1љC"fxPb gǘF(U_/Ex>иdY3q-NK:ZY&^Ke"/EZbsg\Ā: ܨchWtSr]1&:X/q|Áx?s!Ԝ΋E3uB!? &ىuZkfAXq5kx6B-}̟=TXUB?#Ƌ_r:r.茲~s:3[Ku`|9]z͕r;FRI\?:ȟeϸ:Zz.W[g9;is`A2J:%Wt {Ou~qaWàisØRrVf{pTuPuuQ.8W>~{fo#[j aꋐ;%'B{`UI 2JSMccNʭHb6^qJw6Fl b$~=QQ)X =2ؘg+i&'(wgPrQl| eE9zL\ (ѶqTQww,tZ[(0OOG0*S> #:o-"4~լϕr/5Kϧo/`ly1'R +o}Wݫw!/^sgHf$lc@0I6 M@VvO^V/+ww@Y\B1y )@̋6PFjy}iUz1,E6wZz_H}FGet +e^*} nfgGHdmN{~=NJ|6ձCA,5F O{5Qo-(M} 6.x>O7ifѭ(> kw"̎QI+]'_CTҊPǻR.>ʅK%9&ͯ~.\^Yn4?2m&Y/OsU?s/J;+{o~Aj?Z^_3hIJ['_mr?r[Pntq}hZN);b\JW׮"~ߧqhΗA͆@ k`]&oOr0;Xy{ ݦ@~qs\x 6d *WypbXɬ5HIJ~}?.7>-6H#opRl߀Tʝyv6LYG𩑠 RJ(9  iE\.mr[]m𵑓eX\/r?L::[/׃XD81gFΗρ^Rn {qPS.!ۏ9?j)ߑP7D%ܙ7(:.b>W:VqfNSFޠ{KIyԚ=Lw4*FO6fT_ ʥŞٻ8":x_a"X Kqؾ)EN7"[1*.@M6)i[ՙ +'+Q0>eJ8y=Dke+ںZK'!:YKV#HC{=nnì/471/ޭ7͢sK?( Aym>nYLPo{̄r0æ`W{u|&cx% p`shckChT/䒱2@?uyhHWOIyW|h, Ml} rm~T>0\B j7&\÷%߷!{eWCNCcKlM)u(uY)M<2mg둝\$`$OT>njVQjgNvamFy#49y~_Ɵxc8OnR A/8C +&*+_rݷ.L]>sD+R6L^]<ޭ;OUJ]^ɗRLDLހ3>STFJѻ|.[8µ}mTӯ6V;=}׏;g2v.nV.zȞ؝MWK6GGvTb8nqحas@ի]#/1Zяeq)vwf^6wo޶;ŧ +EnzG^6bNb0J9tOڜG{ +LtI-QeI4Ĥ ={ ւ))~!44R+ʯ'x|gj~5u<UdM^6ONP獌*Q6C][7uG7sAE`?ݓW|8@%`sNzYo߆.=o&%m3t0852<\6L֏U< +$P5[y*X=gިP,{X;m_:IATv#Ƭ(=̂)5>)ׂ͐|Pc՚!wT1 ˃KEPWGG4Tn rQ2sm1 V$hϾ-O\j)_ߏO'x OR՞V6(5G/.Ou·Av ոqi:2N"~ί_^NFQNr;; }}%h \OoVV]J! +i6 >OŇӧvxjps)L4V՜_߉_9?ͧI!=v|8> G +e0q5x + O %΅knTNAVwUATȫ^vuaof MqyP-FXp} *ghP},7 :X٠&$Z;oaX ,s;g1&C?kf߀E?d% oǨVvPAoPUt &wPP20BJiBuQP+Tt<=&'{Br7if7p +.#P^l+to04jNP/ 9hB3T44OjEm$&H)_`|tɼnnia4 2B8Tm2DKJ< )6 [_=. H7,Avb cm}%t׭yo?KP=rܖ=bvdOnLN:%c$y6J64_z,,S2)SB&wM侎.4}3I%]7[ruw>qB-:p0Ԯܾco-W"f,['C`7n΀x&JlhC#h˲)J'{vWtή=oGk n Y{H\8Ń}s06#`HlNi$j;cmyTY{$^թR|n8f(P}~RjF@A_>18Ꮳ޷( +OQN(ח̅xOg$s;EuH_ J|1=EvكTyĢ`g:[U Ҧ9\D V|&'ə)b7v ={063M)[[k$нp{lX6 P0$fvbі'3߽x7i]<9u#kg%ONnk|?$Z/;(ig^n~h(Bi3MɪDsR{q@D\pmNh>G^c~Pȹ+QHHtwAWaW[C_|#Jз8,+GPYͽ(|]/!9H/XB~ժb q8dD;YZd6q2Y!R֝BGlVvش[6un׽IH7Ҧ0/G{~0qPcv]ɡAaog ۫y%` ݰIfKiM-  {Ѥ뤷n΍ {hn~hDm>2Ȥ{c+]g[?!&L`W}WB{Jhc=#\}E6My6fC:y{LYf[{.OǟIM{;8(:(c/п~>A5/X[~(}s{VF9Πְ#7߷Z^4~GGfI9:nxC?+GNk8fourT2pя)/-Fh(7g[`rϡOvO#o-nET9l Q!S]bSS|O5mZТ0oD]jt粼 ]†LFInG{ٶl-(6Vq8 uҗ`w-rk" ˘B9gg +쁋q$;=].ޗlb?* w^Is峨ssHs̩Ť-M.Θˇs!||K%̑gِT?͐9: +Oov1m9*1YԚ簴1SVL^;KȜs.@*h˧|~͊*vLq3+SK0ծep:GƊ[Dk +[dwв.w&ɤ%ɣ5Kg8s"g-=Kj.0;L0S1qJ8HL4 cܑh# +w/Ŗ'y|<^qk,je/ݗ([9K9fdm)9yJ)RqL+c2ϲN'QO#<KQqQ)zs+؛E U暱35\vRf@EM7MKxQNwә5P⚄vd{>,vm=8eYYlV[uϺpXr;ydUv +en6t9 3|Horܶ, {iC4ysO fk棽sAG +GYMX*9촧њKh;m*GT"`/ܢ7Ú.z V5UeL7́*_'7WS9zs0UfIT6lyx̐ D{3#Ahuݘqr}0wl#KpDo,I7o$ +7va;_0h":YScw<1[Y@߳.'8g:͟t*P B$ԭTj: []{3o6nWĎ쏠}lV};oofޒǽ Swy̛f̳= 9ư0<͆vhy)\D{fEt:6off-33lvc3d-y'*L.VB/ 6%EǹTieypJwK.ܒOPكƒ9ϚRP-iw+MCBZ2?\'#T@sSN1'P:ܳSkPpZ^P&6CS¸ԣ G7ëLqSҴ͔|R#\o|;SyRZy.ݞz?U3 ħǜJB3բ.Mg6nmv^u&U81%mQ^ӜrN.2['Q^0wT^/cS[o]ILs6X)n)9;Q oaOkmz23=ܫ+Sº'=3}7۞UdlNP[r9>1^=f"K%L$م0K;U2qr>>,&op<}6]ԁE%ey%nG w/2λhE$Qe.|}vY}+'E'٭0XTb]VV܉}vY}Yhb*|>/jA}v]9oϜgՒwi4Ocq*͖焴'e-g'#"Jz}.+LKA]%y]>U2ۅ%=Ls_`bSJ+ϼJT擛-nsٍλ)dcUɚerP r59fdrxV 1ѻQ~PmmҁeCO 1B )<&?\M'Ƹ *.l`XkLG£_aCO˴3L/l6_? imY;?YKq:x8E1ˆŝ^RDž'OaMw${GE:{=Ǥr9NAI% oC=yNEg~((6ɴw<8]NپIEx{ 7.KrŠM49_(g5Bj~r$,\[.11Ǭ={UgqaȬMkgOĜp9>z}b"1 MBa{dtd_jlD , +~^k3y7o^sӌ4i1'ulANHquh7jŬy{g˻KinmԎ6y֣ 3ݪFձ͇esgtk寀bWL`ԓdwkXxhh2E01[*y͒AL1/eNOgj$n۲vzmztZ,HZt0\/y`hm3 ^M9Gѭ s*SKvPRo8>@EgtYSW~SKf^<{AAsi$rs(~nfo6NYƬ֬O{NZ;qfu$$3[vUZ?VW+' G TCXnYZO>i)㆏vf<]irt'`^B-*AOXѐEW6ZbȞ[v&uOy9,:;{Vm"n9Dt{ewTp?p銪 ς;咇L3}jΆaŧGLH,Mxfa|cw LbWp-r.Jn. O/ 6}ȿ!r[*^빴M%%wهc19wy`Z< s{HӢ_JLZ'fQObW0Bzʖ3[߾Mmv(O#=Ls>7]lw?G'g޳LHhTnͳK:W4wu=mi9R:cCҴ+9=pr~LuL#/5)qsK\ZTWnCX&W"Js%LQWZB۝{)M4ce +a%˽nrxj5wCjS 'd,ʻZSИ"nr,xBg-x^.2SCݫ"h_#];ās'-L䒚Pd(jy N뜣y ag)xhݴ= z15aYOz +jid'f-x8R.9r!悇,y)^pM9 [6g +ڳV*xMjCrG1 ׂS-*ZꜲ\RC׬͈f1,x8ES.;s22;?^]˴ +Nu,aj0<ŝs/Ȍ>Ԛ.2J߸> +Ny ЬnCYPO#Mo1~cvݍ\ +W;?:<\@.rq/xb<݉.R3b*sVdNۇ(~˼Ǻ~,$1枤?o+{?JTӏYF_89Th\_fVDy^N6^[˃z,rY>ȜRDOJ0{RL>Ze1/k :5꤂pn*y4$#tl ՚F`q}2|ySf*+0y F$bZ粒!Y*}%6bn1(_±dA 26c,zC2m9z%֟s,FFS8:CG⛣l,Yw4 +YC(RrS`|bj$*;fVW +QAm=؏c&1 +8à |w,>^F4 kfR4(^.|JhaߎU~ˤ9X}ju}1mB}hYD;ʨ#j7xsQgMM["'v9^ʧNr%I d%5=NGOh&1P*fCjDp%h:="qt] gqx-E72JDzTeV(cl=6 +F؎;ڢ} +}Fa/ ^Uچ^Wn;w!"!yr!{8&<*zՙ +{iϞ7EBك?w8vmdOdQ~Fw&xz?ѺŕE+!:dZ!B + X2rX_'2;>(Lvmv-"V!~^ᵝkbF9品U( +q +(|uo5({ rʨ2-LGDކ!eJND^R.{BZgV?7]1 7Qz=yHY(4;@}arLc[ILǔIP7LjQ렾n%}Tls^@,heJ:Frr~~WWB WO5}$/A|pXa(L 4R\t9F؟Cdaex)Y;I6Us|H@m()w: wU(2BE}  KXs]=v,5SHXK_5$  ~EtjuMxjw "T)zq 0|XA ɩ?B𡎇וx̑b kbI4>@joj}Bh'=iqx b+сQVBgV-MD.oR(ElM'FCM} hl8b,c\Gq6ծ _nM8)ZKi9)HGYfnJ MiL7< ;8DhJ3#cb"tشNba+GVՐfBkW05ε"`zt +MZ֬"6\ًGȞt3{y(A&} UJP2>"'sH}U):zl'eꥺT0^ߥfW93 ^o_c6UJ_w;׽rz~k.|oss89LBdCbLF|.w#/qrTx:H1c67~f1Q#+WnHp DE"K[i[=mv8m;ƽ5$j^1U_~jYhq2t0"!L¢e{u+F HTƫ|}6IdqpP~=62bL#b[T C&kAU` +h}Y +7``d*5>> NBz2e% Fv/994xpZE{ʒ!o'Hpi7,4'V%̺2PTtÑm^.f~T<V ;'S +k)y䇴.SfhG2/VG:^mgw==z$V0⮬ob\&3@oOb߰Zdl>J߸u`^d{FD=b{U ~l%e«IdOS]+ES6sHo币2%㏺'ȈOq*>E> <)~&#҇s^~+#pkKۜ8s]yc5/$q`081v20ĢҚhd_\!k4OD5 .W7]3h޽~\ݟobv[sqnrD|b, ]XEyӎ ,Ƣ:Tic\*cQEڴd `1UXEM*8::fvͤv^:[?O.}|տ//Dk8/_|5[a}p>0j]:\y-0 !?$}Wf,fR +t+:op_֧,,#'Hg@1_Yå0^ bA^(iIW#j&e3pnXntL;(ޏ1{9-0ᓟ,–s_剽%N.Q/oa|j9#}zu~CO΅ﮖnsYzƛ3|}qmɕ9ܾUW53gqJ:ӭ8WԱJF%qoAѹm=68լ_.N G?mlχl.-+?=lVkR%spuӎ,69j ]&mB9m,M{TlzϸC+RdnWO"Gv[Hg}Wk.lƟȭwFVo[cwޫRmB>?_yyz{Ó(<[Ϟq7w4us28vFMiק|)):XzWڌZfҞYD[η ryL"{>:D>.#tM&mZ 9HAj)lo_o?}%^}>3J3{/6c`tc7WkpG/^G75|?;㗭ħ_~|7}КɳHg[yهP2s?jMg/_|o|a+1mlh_?5hӏՅh$\7k:qn%5Ⱦ֧kSz_ b;W׿>/FRb$x47y.n՞]b EW۝~7.^4f3-K`LZ͒ Ϝmjl&6\/f3x $$f3\Ͳ Ϥ,IY?v6*_'(;7oyY?_e[gz53U[5uG{~"o-ҏtA_O_l + 1Ze_\RTẘnxdͯ>]W>_m+ۖt.[kdom?_XQ?!g,y=_k2oHR^"7wuan~" v=ٿ߽óg$#艏?[~x3n=zu~rs1L_'\J7\_$a,>?.q҃םW>/5~DM3bó%:YmPhLQQ7Lc5JVRZT‡0QQFҨXaDLmF,G}}{'w>ᕹX7*`SOTbǘ}#3ZW.x+v^Mf Qy;Ξ.zoCXBq¤[9a㟠'"U`8 Y֌z4HmG: <8{FWZ(k9toqJZeS\aq;oSR 3e9幛خNln']pZAj3amۉbwq󹩪󓋷_>49sw9fN.~{>f3?>|r_՜yۻv[6Qy8chCdiwMΝh+Pg[|JjЅ"#4'pLmUP.6nўaf|eJ<19ҥO.]qGj"BeE"zߺ;S 67~<WFyk\ ߽̿ۏw_vw=Oi6B]l[0G #Ti3O%CI B=TAԝˣ$"vVm`:rSr" e*R +"M_D! *Z=1B$$M6ز4VZ`4 +# b[j05(L4 +0>h0}!e`TeTQru!Eu@k}ۆhp,Cvfh&Fh)m62XB|wPt8l /&Ơ@w!nE( +q1pփɁoi iJP4lD)fZ)*H% >'Dm!#S(*,5rPFV P0ЕTRmD'CGID3—6CM 6I0B< J_@7@ PX`Q8`#63b8GxPV4;,l m<$01_D{9d mtt<9' +``,N+ +Nz̀R0z9 + ˏ!I[@ A DODID438RBf~؈ F cmP.o6$6.2@{`a Ma̻ELl +U`&,N{7c8 8akX" NDCm'`3Hze*:БDߦ@'mIʃV6d`,a@%u}t +N| \+' k8o!ݨ$^D3JRI蚘@W-@yS  Z8-uK>4@]Y_Ib(o+gF6 *P8s@\& d@ `t@Ф s'|!ߝ|O +$ BS&S#'sqi,i.& C("%dlmPC'0ZȀBeP =r+y"w@$u6#G+V +&-SYfûhͭ6C3:kZU;'3( ?}>zkUGI2-K&0yr. G$k!LW1QVPM2~G#9H]zUKjK +?EN$@ Y=i<PxAZ@[-<` H$ D`(. +nBYZctE07tփwtK.jAW~A4M[C0Ʈ aGm +d1uZ)(:ji +hJ 6v7.^Wq1z|KPT[E@ +b +3h:bxOki:mQ6u,qrBvK3w~iW&j~6N5wX PDp8)̂JQ*Y+0H 0t N^[ϐ .ġC5L>(ûfTNHt +hJ<a{ux STʨœv㍐R0cpLXռ6pU%Ryi +zo"NjM$jO`i"!9zBJN`gs;IUX`{:taTPBm7p6,&-VD]$7V<}ڈQD6Hҵy! -%8Үm)W::"Yp9j1Jk_ja m] k^)2*xt2EP o7B$ UW go*=5fuػFxx^|oq$~mu CLm~|mNg zیWµg^|qkN|iǎ/+#Yun#8PT(ʷn[O Gʗdy?RE%,ڏ=`v|9mxkj8Hy!jP)(PRѩePt#;ES}ʧC4DPy`4.U_D䝧DʯDO/V?1 +y1$}&! hB0,O t%o#8Xʷx[;~tq1JD|<'#.,}8KTELAOYmjKu"=TFZ:CNB[oYۉC,lC +Sx;MВ;CqR@m|'C  >:D-Yd{`.òE(֨!A,yXM1GJ?3:|l-Hk1*.9W +J{-Ixɶf s()OY2x&g83}.>bN'3$$XzhYք*5V,Uɍ5#@ɪHX2oV^f +[F0gORަ<^cRbF7LL+_9Hk2k,D7~ 4?20g&#>I R@ډ=s4 U&[Gͤ1/KUX` + l/s<.q۔-y=t$6mI3K/YǐlfJ&b͇_UACl:gd__m!rG$X 0T]0)`ƃXФf.l|ЮAU22I5d +&UE m? y=5Y8eYK02mShL%,E[5m G] p0t')pU%R +`=G Br3(/-P јlH]A4vydc^fVa8beȭKǸd5*`NʜɷQ쓎Uhm,\#% L@DbcRe `o`pYh xJ%Gפ2Yhm^UpyeUhѩ0A}I(FM,$4: zpKbdzǤ59jX033j1w)'?8,}/Ni$EFJRV3/pW4J$,Jv2+&JMꜬ,A^T4)h@@ayU +{fU +K)-ǭNA06&VN6 SDڅ)X"݇G kH9զz6R mz|Pl*NG0dO A׎FHv +.䇨c]B`0uP0njJXkSld+I)"m +=d/æ<@ %RbU`  Uu!ey /\9X}=e +o)pl5p޷ѵ)ƙB!$Y{Ќ}b~=li]*B/F R$YL}!o}pjG?LGӳCHejD[6ۑ^>|Z;jG[\YG(T*/B[E:PthFi}-|,~(RSn;T\{*ĕ/Z.V=nMVay(>B}@sbn-bY쑭B3;C,- +*Մ04TV& > Z\Hޡ4eϸ.`o]6  \n>7DJq\[Q< +`ak&T8 &ጅ")*xt{p}SU"EfhEmz Rû/7;/x E#yZUqѥl;x㶵};<>B$V7ܠ +khWj>@ХGr)\ey{޺bk,+&MꖺM|@׀$ ]ir%جߌ#r5Qv+HgaQIfX(7@Gʏ"31r[$u+bXi2'sx6g(oz[N Ja?E +Vg4mcL8KaXe]*J.<#1bk!H)=k)bWxCHQVy8 SaX3,0/I,7S v^@^S#nҘg,6efN1-2's"lD[f6 `b5- !f6޴l= ;c  [v`ێ I1Je ha(1Va 2Jb,O^Cbl*e v a~`}l5BR*{Vs{i [[Y%"VjFrN+.ƃG lܺ~OE*MX^Z#ia8T4ki*+p*&Z*e`ANJ92ݜh.1DVbby1?*IwESof;':mL4) ȸb an ΋ncmiq +yn'g'XFVK9GyreC< Sk$;!+V@ 깠$=h0!F_Bf-X-d-qo9Kc Y:LkP 6+!Dv8'H-#owOۇJx^J@[xH+@:4(D bauYR2ezbؽzh[ Q]Hѯd_2$w0Յ1Tg靑hŒ_Jfd6W6Z +N طաg :E7J0#9]ؙI;o7cǝ39%R\ITuݐTfVTTV +8[Ÿ f-C2㟣Ќa Xl!.f3 &V +COZ߈4w|"$&oOT"RdkAaS]H M+6#X 2 +S[lnm,LOe4\&)`k dG `udv)o+c[ 9Q;QN  b)sOabjS{͸ccՂ9 `2Mt*bR_sQ +ƖQ`bD^ͮĮ [xثzBF,e%KLE*]!Y7夅7T_rڂn*$,kgm`-qZ'dIz(pTAxxCܰGuZ6gld [BKƐBVŒ +#,PlYd|B #ljvd%"p eY67cy"_ecݬnjsΚMZq+`Ea0EBӗV JAhJZ5{ƍ jW| ǍvVN(*%+dW@m|tdR;Iԭjv`6 9C$fQbbkc|XyDF_uAcYxrkotg>?[Zk>LJM$FhFy׈$:gg؇ZbWC˦wemhK{ 4l#bT(͎k ֮ ZVL%20T؊XMtɗ΃ Cߩ14O24BMt*O־N}ydrvE(텁Ʀy*РqKKq|9|G^?>6 7EzW@¶f5 >gXvaNXz$t,┢I^- .JIS?c[:nj]u<ڛuk>YpeVR(QL :h m +G$0jan% +[\9'yN +΋esi{xtgvVVvMoSk/njTNY0Sk˥~CȢm=rE6,,=1TҒN֯n4"bvdP֘Xv+3~LH!}7Gt5ԌM;yC,9H8qkS"o9{T~e7-c(جUFa{9֞`q _kSB k{? ;u;u [3:mN`>g-m+W:o1(6E"Iwl7F0 5}12|ڮEAdK|` ciac^æWqL2cXmco5.^ o#RBa偪xګpS1:Gej :$>]캧 X]-uX38m=֊nf& 1]uꧽI {Û;Nv=OҖ-/ ~g]}x~rsWonpɥI&SܐycLN.;$.˯QO_3bóg8_AbYqp:)ox"(Ey\5=!XOq([:rhG< Q/܋&J].J 魊Fٟx5GW'3xTx/]}ɋW'{Y:͑04w#<;x?O^n gf5|ܳwJ7Z5#4ѾޓG_ac_1ߞ<\UZЎ*rD,|ec(*O/}QQC2+/Zj|a6!%Nqd:=$zlF;V-̺򡧑CKX$CZXKFzkG*Dm`] 7lC*ɐ Za𠘺BϿ״dYENwo"u/Q?v3- }5e{#uEo6!UCt)BvxƑm|[1SCm3Rv\sQԝ,˶ZB &]3IRn 5kᱪ3:>+Qc؞8ض#1Ƴ(swJ  9;Hziܱ֢5DG($J{p#;d!lғmA>v'я2,TqQ6ڢks:V0,b)J:/2ty2RؖũMUH6,"&G6p:O7,XlEqLm=VK%Cl5>֪9{ Tb\Ф@b.0SYG0鼵(k ѵe; ̦k¬f'lh_MF;9҄bq6#:1Xo ?jV.I"&Ξy305p2`76P@Yߞ=2r]=w=/5}Iɹz,][AWeW+ο32Tq߭{kTe Za0vl2 6bud#Z V3,&gҭJyH%Cmaz +Tma`-/{ +RaHS q|rT9 7E, *T3x'`ɥ -6X,r o"3J gFvP&%+XPxEOl0̹a?y a0vbxλ(`sshcx*iI0HNAm>GaY:`3fOV9@ᘜepRFW9(Q,u!=[ AJcqiƂaT)#h[kCK =6.yd'Ae0e3L5t8ȀNi8O'nt*%GϞ +,l uj-Efc d+.Ceׯz06 1\IJO%,u7> YnbH7WH<36z0K*=n*q rfZc 0W8i-HKxMYzy҇zAK ,>J" %S&dO ,A'pI=G,v>;²I '(,WyT0 r!û;]lTvGeeb#VR!#-W +࡮o,:IL-I~`+iU(`/" +V>9zX[ Kik ,L l *u=*Ғ|mFDDpt my%x<&4PΆq)MDsSYUI .1 +ʎ|E5jQJl=t8*L9x'?(BjViEb=/xΊ(%10m` OPi3AAM43kWԴ¦YڌG(0WpԹHf"!`~Ya'yni56\hkR24?a1_3X% ũt'Dβ1rjXHOކotJINnR*H܄=?lm%=T*@lX|Ana97%T8HcuU\QA'y+QrOc_h pIbY]1=):揞4ׄG'VKJpf URȷ +m+Ky+'yE{ +`p` +#g253`ȱP D6B@K1|*)Xt5 fG?,"BmX64"HALuYj} Hx.БIBVM[CL\8cb|.n[(awK!=!ރx7>0-p{ɽm!5pVew\V÷6ࠧ?T&Xueօ7 mYߋ6n*W KcV3LKKK/}1[tkVA+{ -*D'*vG$0Շq=ٛNaDg]psGE|tk_wóg߽>]{/+ܽW&K/|ﮝ=戚 +endstream endobj 28 0 obj <>stream +HMn0 >/`Z~(m +vE"Y]!Gc[5|G~0< x<~nӥӇ #.@c@ xy%(_ݹ 'N: *COHޢ u_SSވ7HóA aշn7|QGW \ª>dFW" ,zÒyBK1]Ad+%ȄrՃ^si%2r@3\ HڼMUTAaKsRԳ4l ź>7pm?nq=n4<-wv&\_-iQeg}r%-2-dvvVr'Zy&h9V  +WwfbjKTf%/}tcUZJ=dH^ΪE)35*7{[3Qg[fPQ|aSg'Y5 j!2__`w XW~W/ n +endstream endobj 29 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 45 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 50 0 obj <> endobj 51 0 obj <> endobj 81 0 obj <> endobj 86 0 obj <> endobj 87 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 82 0 obj <> endobj 90 0 obj <> endobj 91 0 obj <> endobj 92 0 obj <> endobj 85 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 80 0 obj <> endobj 95 0 obj <> endobj 84 0 obj <> endobj 96 0 obj <> endobj 97 0 obj <> endobj 83 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 100 0 obj <> endobj 101 0 obj <> endobj 102 0 obj <> endobj 25 0 obj <>stream +HԕKn0@>\31lC*ڬZ)q!vd3<[-fۂCW(AvP&^S(r KC4KO&JIg-?D}$ Jg(F0 "}?5ߘ"U2b~O=^x m( MJn*M+rη[>;ۈͬz!~h[7WnC٫J5*WP> endobj 14 0 obj <>stream +H9 y +5E'="X8sv06pWE{Z';Y,i5?k?*J~UnJ ]c/~%Z%JQ[FLi8["N\}^c)*߶8}Ɉ5%!Vbk2>@*c7~8b?DYuS,t +6~j-Q`@]zE5!jl3O}}?1R^ъJQI~^) o7ꃯlBM4|kP F0 vpι3] &":Y.Ё0gsųBϦ.g4oϴv+2k)AӝUς36ǃTKn.~$ic̑ijnsk_4cVO3]Sgލiŏ:\ {qPBG,-['wy*Ac1.9>) + FX+YVs+|UI/IXrcsh(ց\S2=GGRVXirz/U[ +endstream endobj 15 0 obj <> endobj 21 0 obj <> endobj 22 0 obj <> endobj 23 0 obj <> endobj 24 0 obj <> endobj 106 0 obj <> endobj 107 0 obj <> endobj 105 0 obj <> endobj 108 0 obj <> endobj 104 0 obj <> endobj 109 0 obj <> endobj 103 0 obj <> endobj 110 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <>/Shading<>>>/Subtype/Form>>stream +q +0 g +/GS0 gs +0 11.125 11.125 -0 35.9983253 8.7073746 cm +BX /Sh0 sh EX Q + +endstream endobj 116 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 120 0 obj <> endobj 113 0 obj <> endobj 121 0 obj <>/Shading<>>>/Subtype/Form>>stream +q +0 g +/GS0 gs +0 -11.125 -11.125 -0 35.9983253 50.9570007 cm +BX /Sh0 sh EX Q + +endstream endobj 122 0 obj <> endobj 123 0 obj <> endobj 124 0 obj <> endobj 112 0 obj <> endobj 125 0 obj <>/Shading<>>>/Subtype/Form>>stream +q +0 g +/GS0 gs +7.8665628 -7.8665628 -7.8665628 -7.8665628 25.2577057 66.4104919 cm +BX /Sh0 sh EX Q + +endstream endobj 126 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <> endobj 129 0 obj <> endobj 130 0 obj <> endobj 111 0 obj <> endobj 131 0 obj <>/Shading<>>>/Subtype/Form>>stream +q +0 g +/GS0 gs +-7.8665628 -7.8665628 -7.8665628 7.8665628 46.7382812 88.4104996 cm +BX /Sh0 sh EX Q + +endstream endobj 132 0 obj <> endobj 133 0 obj <> endobj 134 0 obj <> endobj 12 0 obj [11 0 R 10 0 R 9 0 R 8 0 R] endobj 135 0 obj <> endobj xref +0 136 +0000000000 65535 f +0000000016 00000 n +0000000185 00000 n +0000035498 00000 n +0000000000 00000 f +0000035568 00000 n +0000036050 00000 n +0000036443 00000 n +0000038620 00000 n +0000038687 00000 n +0000038754 00000 n +0000038822 00000 n +0000187280 00000 n +0000037095 00000 n +0000181190 00000 n +0000182192 00000 n +0000041213 00000 n +0000183721 00000 n +0000183837 00000 n +0000183953 00000 n +0000184069 00000 n +0000182257 00000 n +0000182404 00000 n +0000182551 00000 n +0000182698 00000 n +0000180727 00000 n +0000181125 00000 n +0000041073 00000 n +0000174364 00000 n +0000174981 00000 n +0000041186 00000 n +0000175046 00000 n +0000175194 00000 n +0000175304 00000 n +0000175452 00000 n +0000175562 00000 n +0000175710 00000 n +0000175820 00000 n +0000175968 00000 n +0000176078 00000 n +0000176188 00000 n +0000176309 00000 n +0000176429 00000 n +0000176553 00000 n +0000176701 00000 n +0000176825 00000 n +0000176935 00000 n +0000177054 00000 n +0000177174 00000 n +0000177296 00000 n +0000177420 00000 n +0000177568 00000 n +0000037627 00000 n +0000038555 00000 n +0000039357 00000 n +0000039503 00000 n +0000039649 00000 n +0000039772 00000 n +0000039894 00000 n +0000040040 00000 n +0000040186 00000 n +0000040332 00000 n +0000040455 00000 n +0000040577 00000 n +0000039241 00000 n +0000039272 00000 n +0000039125 00000 n +0000039156 00000 n +0000039009 00000 n +0000039040 00000 n +0000038893 00000 n +0000038924 00000 n +0000040723 00000 n +0000040869 00000 n +0000040964 00000 n +0000041287 00000 n +0000041483 00000 n +0000042859 00000 n +0000045484 00000 n +0000111073 00000 n +0000179209 00000 n +0000177678 00000 n +0000178298 00000 n +0000179928 00000 n +0000179508 00000 n +0000178812 00000 n +0000177824 00000 n +0000177990 00000 n +0000178080 00000 n +0000178189 00000 n +0000178444 00000 n +0000178602 00000 n +0000178707 00000 n +0000178958 00000 n +0000179100 00000 n +0000179355 00000 n +0000179654 00000 n +0000179819 00000 n +0000180074 00000 n +0000180290 00000 n +0000180397 00000 n +0000180507 00000 n +0000180617 00000 n +0000183533 00000 n +0000183315 00000 n +0000183079 00000 n +0000182845 00000 n +0000182968 00000 n +0000183202 00000 n +0000183442 00000 n +0000183630 00000 n +0000186590 00000 n +0000185720 00000 n +0000185026 00000 n +0000184185 00000 n +0000184240 00000 n +0000184526 00000 n +0000184604 00000 n +0000184753 00000 n +0000184876 00000 n +0000184951 00000 n +0000185081 00000 n +0000185370 00000 n +0000185448 00000 n +0000185597 00000 n +0000185775 00000 n +0000186086 00000 n +0000186164 00000 n +0000186313 00000 n +0000186440 00000 n +0000186515 00000 n +0000186645 00000 n +0000186956 00000 n +0000187034 00000 n +0000187183 00000 n +0000187324 00000 n +trailer +<<24140000A993D14199A57A7B45F00BBB>]>> +startxref +187512 +%%EOF diff --git a/res/img/player/player_arms.png b/res/img/player/player_arms.png new file mode 100644 index 0000000..2fa8db1 Binary files /dev/null and b/res/img/player/player_arms.png differ diff --git a/res/img/player/player_effects.png b/res/img/player/player_effects.png new file mode 100644 index 0000000..9e61675 Binary files /dev/null and b/res/img/player/player_effects.png differ diff --git a/res/img/player/player_head.png b/res/img/player/player_head.png new file mode 100644 index 0000000..a3ef4b4 Binary files /dev/null and b/res/img/player/player_head.png differ diff --git a/res/img/player/player_legs.png b/res/img/player/player_legs.png new file mode 100644 index 0000000..b55deab Binary files /dev/null and b/res/img/player/player_legs.png differ diff --git a/res/shaders/s2b/frag.gls b/res/shaders/s2b/frag.gls new file mode 100644 index 0000000..3358cdb --- /dev/null +++ b/res/shaders/s2b/frag.gls @@ -0,0 +1,19 @@ +#version 330 core + +uniform sampler2D layer0; +uniform sampler2D layer1; + +uniform float offset0 = 1; +uniform float offset1 = 1; + +in vec2 texCoords; + +out vec4 color; + +void main() { + vec4 color0 = texture(layer0, texCoords + vec2(0, offset0)); + vec4 color1 = texture(layer1, texCoords + vec2(0, offset1)); + color = mix(color0, color1, 1 - color0.a); + if (color.a == 0) + discard; +} \ No newline at end of file diff --git a/res/shaders/s2b/vert.gls b/res/shaders/s2b/vert.gls new file mode 100644 index 0000000..6c3699d --- /dev/null +++ b/res/shaders/s2b/vert.gls @@ -0,0 +1,13 @@ +#version 330 core + +uniform mat4 mvp; + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec2 itexCoords; + +out vec2 texCoords; + +void main() { + texCoords = itexCoords; + gl_Position = mvp * vec4(vertices, 1); +} \ No newline at end of file diff --git a/res/shaders/s2c/frag.gls b/res/shaders/s2c/frag.gls new file mode 100644 index 0000000..1481224 --- /dev/null +++ b/res/shaders/s2c/frag.gls @@ -0,0 +1,9 @@ +#version 330 core + +uniform vec4 iColor = vec4(0, 0.6, 0.9, 1); + +out vec4 color; + +void main() { + color = iColor; +} \ No newline at end of file diff --git a/res/shaders/s2c/vert.gls b/res/shaders/s2c/vert.gls new file mode 100644 index 0000000..f404630 --- /dev/null +++ b/res/shaders/s2c/vert.gls @@ -0,0 +1,9 @@ +#version 330 core + +uniform mat4 mvp; + +layout (location = 0) in vec3 vertices; + +void main() { + gl_Position = mvp * vec4(vertices, 1); +} \ No newline at end of file diff --git a/res/shaders/s2t/frag.gls b/res/shaders/s2t/frag.gls new file mode 100644 index 0000000..ad07ab5 --- /dev/null +++ b/res/shaders/s2t/frag.gls @@ -0,0 +1,13 @@ +#version 330 core + +uniform sampler2D sampler; + +in vec2 texCoords; + +out vec4 color; + +void main() { + color = texture(sampler, texCoords); + if (color.a == 0) + discard; +} \ No newline at end of file diff --git a/res/shaders/s2t/vert.gls b/res/shaders/s2t/vert.gls new file mode 100644 index 0000000..6c3699d --- /dev/null +++ b/res/shaders/s2t/vert.gls @@ -0,0 +1,13 @@ +#version 330 core + +uniform mat4 mvp; + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec2 itexCoords; + +out vec2 texCoords; + +void main() { + texCoords = itexCoords; + gl_Position = mvp * vec4(vertices, 1); +} \ No newline at end of file diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..e4ffd38 --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: com.gnarwhal.ld46.game.Main + + diff --git a/src/com/gnarwhal/ld46/engine/audio/ALManagement.java b/src/com/gnarwhal/ld46/engine/audio/ALManagement.java new file mode 100644 index 0000000..6edeff4 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/audio/ALManagement.java @@ -0,0 +1,37 @@ +package com.gnarwhal.ld46.engine.audio; + +import org.lwjgl.openal.AL; +import org.lwjgl.openal.ALC; +import org.lwjgl.openal.ALC10; +import org.lwjgl.openal.ALCCapabilities; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import static org.lwjgl.openal.ALC10.*; + +public class ALManagement { + + private long device, context; + private ALCCapabilities deviceCaps; + + public ALManagement() { + device = alcOpenDevice((ByteBuffer) null); + if (device == 0) + throw new IllegalStateException("Failed to open the default device."); + + deviceCaps = ALC.createCapabilities(device); + + context = alcCreateContext(device, (IntBuffer) null); + if (context == 0) + throw new IllegalStateException("Failed to create an OpenAL context."); + + alcMakeContextCurrent(context); + AL.createCapabilities(deviceCaps); + } + + public void destroy() { + ALC10.alcDestroyContext(context); + ALC10.alcCloseDevice(device); + } +} diff --git a/src/com/gnarwhal/ld46/engine/audio/Sound.java b/src/com/gnarwhal/ld46/engine/audio/Sound.java new file mode 100644 index 0000000..82e0366 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/audio/Sound.java @@ -0,0 +1,38 @@ +package com.gnarwhal.ld46.engine.audio; + +import org.lwjgl.openal.AL10; + +public class Sound { + + private int buffer; + private int sourceId; + + public Sound(String path) { + sourceId = AL10.alGenSources(); + buffer = AL10.alGenBuffers(); + WaveData waveData = WaveData.create(path); + AL10.alBufferData(buffer, waveData.format, waveData.data, waveData.samplerate); + AL10.alSourcei(sourceId, AL10.AL_BUFFER, buffer); + AL10.alSourcef(sourceId, AL10.AL_GAIN, 1); + AL10.alSourcef(sourceId, AL10.AL_PITCH, 1); + } + + public void play(boolean loop) { + AL10.alSourcei(sourceId, AL10.AL_LOOPING, loop ? 1 : 0); + AL10.alSource3f(sourceId, AL10.AL_POSITION, 0, 0, 0); + AL10.alSourcePlay(sourceId); + } + + public void stop() { + AL10.alSourceStop(sourceId); + } + + public void setVolume(float volume) { + AL10.alSourcef(sourceId, AL10.AL_GAIN, volume); + } + + public void destroy() { + AL10.alDeleteBuffers(buffer); + AL10.alDeleteSources(sourceId); + } +} diff --git a/src/com/gnarwhal/ld46/engine/audio/WaveData.java b/src/com/gnarwhal/ld46/engine/audio/WaveData.java new file mode 100644 index 0000000..a89fbd0 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/audio/WaveData.java @@ -0,0 +1,83 @@ +package com.gnarwhal.ld46.engine.audio; + +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL10; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.*; +import java.nio.ByteBuffer; + +public class WaveData { + + final int format; + final int samplerate; + final int totalBytes; + final int bytesPerFrame; + final ByteBuffer data; + + private final AudioInputStream audioStream; + private final byte[] dataArray; + + private WaveData(AudioInputStream stream) { + this.audioStream = stream; + AudioFormat audioFormat = stream.getFormat(); + format = getOpenAlFormat(audioFormat.getChannels(), audioFormat.getSampleSizeInBits()); + this.samplerate = (int) audioFormat.getSampleRate(); + this.bytesPerFrame = audioFormat.getFrameSize(); + this.totalBytes = (int) (stream.getFrameLength() * bytesPerFrame); + this.data = BufferUtils.createByteBuffer(totalBytes); + this.dataArray = new byte[totalBytes]; + loadData(); + } + + protected void dispose() { + try { + audioStream.close(); + data.clear(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private ByteBuffer loadData() { + try { + int bytesRead = audioStream.read(dataArray, 0, totalBytes); + data.clear(); + data.put(dataArray, 0, bytesRead); + data.flip(); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Couldn't read bytes from audio stream!"); + } + return data; + } + + public static WaveData create(String file) { + WaveData wavStream = null; + try { + InputStream stream = new FileInputStream(new File(file)); + InputStream bufferedInput = new BufferedInputStream(stream); + AudioInputStream audioStream = null; + audioStream = AudioSystem.getAudioInputStream(bufferedInput); + wavStream = new WaveData(audioStream); + } catch (UnsupportedAudioFileException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return wavStream; + } + + + private static int getOpenAlFormat(int channels, int bitsPerSample) { + if (channels == 1) { + return bitsPerSample == 8 ? AL10.AL_FORMAT_MONO8 : AL10.AL_FORMAT_MONO16; + } else { + return bitsPerSample == 8 ? AL10.AL_FORMAT_STEREO8 : AL10.AL_FORMAT_STEREO16; + } + } + +} diff --git a/src/com/gnarwhal/ld46/engine/display/Camera.java b/src/com/gnarwhal/ld46/engine/display/Camera.java new file mode 100644 index 0000000..1c88f50 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/display/Camera.java @@ -0,0 +1,100 @@ +package com.gnarwhal.ld46.engine.display; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class Camera { + + private Matrix4f projection, projView; + + private float width, height; + private Vector3f position; + private float rotation; + + public Camera(float width, float height) { + setDims(width, height); + position = new Vector3f(); + rotation = 0; + projView = new Matrix4f(); + } + + public void setDims(float width, float height) { + this.width = width; + this.height = height; + projection = new Matrix4f().setOrtho(0, width, height, 0, 0, 1); + } + + public void update() { + projection.translate(position.negate(new Vector3f()), projView).rotateZ(-rotation); + } + + public Matrix4f getProjection() { + return new Matrix4f(projection); + } + + public Matrix4f getMatrix() { + return new Matrix4f(projView); + } + + public float getX() { + return position.x; + } + + public float getY() { + return position.y; + } + + public Vector3f getPosition() { + return new Vector3f(position); + } + + public float getWidth() { + return width; + } + + public float getHeight() { + return height; + } + + public void setX(float x) { + position.x = x; + } + + public void setY(float y) { + position.y = y; + } + + public void setPosition(float x, float y) { + position.set(x, y, position.z); + } + + public void setPosition(Vector3f position) { + this.position.x = position.x; + this.position.y = position.y; + } + + public void setCenter(float x, float y) { + position.set(x - width / 2, y - height / 2, position.z); + } + + public void setCenter(Vector3f position) { + this.position.x = position.x - width / 2; + this.position.y = position.y - height / 2; + } + + public void translate(float x, float y, float z) { + position.add(x, y, z); + } + + public void translate(Vector3f transform) { + position.add(transform); + } + + public void setRotation(float angle) { + rotation = angle; + } + + public void rotate(float angle) { + rotation += angle; + } +} diff --git a/src/com/gnarwhal/ld46/engine/display/Framebuffer.java b/src/com/gnarwhal/ld46/engine/display/Framebuffer.java new file mode 100644 index 0000000..ad052f1 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/display/Framebuffer.java @@ -0,0 +1,93 @@ +package com.gnarwhal.ld46.engine.display; + +import com.gnarwhal.ld46.engine.texture.Texture; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL30.*; +import static org.lwjgl.opengl.GL32.glFramebufferTexture; + +public class Framebuffer { + + int fbo, rbo, width, height; + int colorBuf, depthTex; + float r, g, b, a; + + Framebuffer(int width, int height, float r, float g, float b, float a) { + this.width = width; + this.height = height; + + this.r = r; + this.g = g; + this.b = b; + this.a = a; + + fbo = glGenFramebuffers(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + + rbo = 0; + colorBuf = 0; + depthTex = 0; + } + + Framebuffer addColorAttachment(Texture texture) { + if (colorBuf == 0) { + int id = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + texture = new Texture(id, width, height); + colorBuf = 1; + } + return this; + } + + Framebuffer addDepthTextureAttachment(Texture texture) { + if (depthTex == 0) { + int id = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, id, 0); + texture = new Texture(id, width, height); + depthTex = 1; + } + return this; + } + + Framebuffer addDepthBufferAttachment() { + if (rbo == 0) { + rbo = glGenRenderbuffers(); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + } + return this; + } + + void bind() { + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, width, height); + + glClearColor(r, g, b, a); + } + + void unbind(Window window) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, window.getWidth(), window.getHeight()); + window.activateClearColor(); + } + + void clear() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + public void cleanup() { + if (rbo != 0) + glDeleteRenderbuffers(rbo); + glDeleteFramebuffers(fbo); + } +} diff --git a/src/com/gnarwhal/ld46/engine/display/Window.java b/src/com/gnarwhal/ld46/engine/display/Window.java new file mode 100644 index 0000000..630ce69 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/display/Window.java @@ -0,0 +1,194 @@ +package com.gnarwhal.ld46.engine.display; + +import org.joml.Vector3f; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWVidMode; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL.createCapabilities; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL13.GL_MULTISAMPLE; + +public class Window { + + public static int + SCREEN_WIDTH, + SCREEN_HEIGHT, + REFRESH_RATE; + + public static final int + BUTTON_RELEASED = 0, + BUTTON_UNPRESSED = 1, + BUTTON_PRESSED = 2, + BUTTON_HELD = 3, + BUTTON_REPEAT = 4; + + public static float SCALE; + + private long window; + private int width, height; + private boolean resized; + + private int[] mouseButtons = new int[GLFW_MOUSE_BUTTON_LAST + 1]; + private int[] keys = new int[GLFW_KEY_LAST + 1]; + + public Window(String title, boolean vSync) { + init(0, 0, title, vSync, false, false, false); + } + + public Window(String title, boolean vSync, boolean resizable, boolean decorated) { + init(800, 500, title, vSync, resizable, decorated, true); + } + + public Window(int width, int height, String title, boolean vSync, boolean resizable, boolean decorated) { + init(width, height, title, vSync, resizable, decorated, false); + } + + public void init(int lwidth, int lheight, String title, boolean vSync, boolean resizable, boolean decorated, boolean maximized) { + glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err)); + + for (int i = 0; i < mouseButtons.length; i++) + mouseButtons[i] = 0; + + if(!glfwInit()) { + System.err.println("GLFW failed to initialize!"); + System.exit(-1); + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + glfwWindowHint(GLFW_SAMPLES, 8); + glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, decorated ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_MAXIMIZED, maximized ? GLFW_TRUE : GLFW_FALSE); + + GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + SCREEN_WIDTH = vidMode.width(); + SCREEN_HEIGHT = vidMode.height(); + SCALE = SCREEN_HEIGHT / 1080f; + REFRESH_RATE = vidMode.refreshRate(); + if(lwidth == 0 || lheight == 0) { + width = vidMode.width(); + height = vidMode.height(); + window = glfwCreateWindow(width, height, title, glfwGetPrimaryMonitor(), 0); + } + else { + this.width = lwidth; + this.height = lheight; + window = glfwCreateWindow(width, height, title, 0, 0); + } + + glfwMakeContextCurrent(window); + createCapabilities(); + + glfwSwapInterval(vSync ? 1 : 0); + + glfwSetWindowSizeCallback(window, (long window, int w, int h) -> { + width = w; + height = h; + resized = true; + glViewport(0, 0, width, height); + }); + + glfwSetMouseButtonCallback(window, (long window, int button, int action, int mods) -> { + if (action == GLFW_RELEASE) + mouseButtons[button] = BUTTON_RELEASED; + if (action == GLFW_PRESS) + mouseButtons[button] = BUTTON_PRESSED; + if (action == GLFW_REPEAT) + mouseButtons[button] = BUTTON_REPEAT; + }); + + glfwSetKeyCallback(window, (long window, int key, int scancode, int action, int mods) -> { + if (key != -1) { + if (action == GLFW_RELEASE) + keys[key] = BUTTON_RELEASED; + if (action == GLFW_PRESS) + keys[key] = BUTTON_PRESSED; + if (action == GLFW_REPEAT) + keys[key] = BUTTON_REPEAT; + } + }); + + activateClearColor(); + + glEnable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_MULTISAMPLE); + + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + + int[] awidth = new int[1], aheight = new int[1]; + glfwGetWindowSize(window, awidth, aheight); + width = awidth[0]; + height = aheight[0]; + } + + public void update() { + for (int i = 0; i < mouseButtons.length; i++) + if (mouseButtons[i] == BUTTON_RELEASED || mouseButtons[i] == BUTTON_PRESSED) + ++mouseButtons[i]; + for (int i = 0; i < keys.length; i++) + if (keys[i] == BUTTON_RELEASED || keys[i] == BUTTON_PRESSED) + ++keys[i]; + resized = false; + glfwPollEvents(); + } + + public void activateClearColor() { + glClearColor(0, 0, 0.02f, 1); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void clear() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + public void swap() { + glfwSwapBuffers(window); + } + + public void close() { + glfwSetWindowShouldClose(window, true); + } + + public static void terminate() { + glfwTerminate(); + } + + public boolean shouldClose() { + return glfwWindowShouldClose(window); + } + + public int keyPressed(int keyCode) { + return keys[keyCode]; + } + + public Vector3f getMouseCoords(Camera camera) { + double[] x = new double[1], y = new double[1]; + glfwGetCursorPos(window, x, y); + Vector3f ret = new Vector3f((float) x[0], (float) y[0], 0); + return ret.mul(camera.getWidth() / this.width, camera.getHeight() / this.height, 1); + } + + public int mousePressed(int button) { + return mouseButtons[button]; + } + + public boolean wasResized() { + return resized; + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/Circle.java b/src/com/gnarwhal/ld46/engine/model/Circle.java new file mode 100644 index 0000000..2f283e2 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/Circle.java @@ -0,0 +1,120 @@ +package com.gnarwhal.ld46.engine.model; + +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.shaders.Shader; +import com.gnarwhal.ld46.engine.shaders.Shader2c; +import org.joml.Vector3f; + +public class Circle { + + private static Vao vao; + + private Camera camera; + private Shader2c shader; + private Vector3f position; + private float radius; + private float r, g, b, a; + + public Circle(Camera camera, float x, float y, float z, float radius) { + this.camera = camera; + position = new Vector3f(x, y, z); + this.radius = radius; + shader = Shader.SHADER2C; + r = 1; + g = 0; + b = 0; + a = 1; + if(vao == null) + initVao(); + } + + private void initVao() { + final int NUM_POINTS = 30; + float[] cVertices = new float[NUM_POINTS * 3]; + int[] cIndices = new int[(NUM_POINTS - 2) * 3]; + for (int i = 0; i < cVertices.length; i += 3) { + double angle = Math.PI * 2 * i / (NUM_POINTS * 3); + cVertices[i ] = (float) Math.cos(angle); + cVertices[i + 1] = (float) Math.sin(angle); + cVertices[i + 2] = 0; + } + for (int i = 0; i < cIndices.length; i += 3) { + cIndices[i ] = 0; + cIndices[i + 1] = i / 3 + 1; + cIndices[i + 2] = i / 3 + 2; + } + vao = new Vao(cVertices, cIndices); + } + + public void render() { + shader.enable(); + shader.setMVP(camera.getMatrix().translate(position).scale(radius)); + shader.setColor(r, g, b, a); + vao.render(); + } + + public Vector3f getPosition() { + return position; + } + + public void setX(float x) { + position.x = x; + } + + public void setY(float y) { + position.y = y; + } + + public void setZ(float z) { + position.z = z; + } + + public void setPosition(float x, float y) { + position.x = x; + position.y = y; + } + + public void setPosition(float x, float y, float z) { + position.x = x; + position.y = y; + position.z = z; + } + + public void setPosition(Vector3f position) { + this.position.set(position); + } + + public void translate(float x, float y) { + position.x += x; + position.y += y; + } + + public void translate(float x, float y, float z) { + position.x += x; + position.y += y; + position.z += z; + } + + public void translate(Vector3f position) { + this.position.add(position); + } + + public void setRadius(float radius) { + this.radius = radius; + } + + public void setDiameter(float diameter) { + this.radius = diameter / 2; + } + + public void setColor(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public boolean contains(Vector3f vector) { + return (position.sub(vector, new Vector3f()).lengthSquared() < radius * radius); + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/ColRect.java b/src/com/gnarwhal/ld46/engine/model/ColRect.java new file mode 100644 index 0000000..c0702ac --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/ColRect.java @@ -0,0 +1,49 @@ +package com.gnarwhal.ld46.engine.model; + +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.shaders.Shader; +import com.gnarwhal.ld46.engine.shaders.Shader2c; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class ColRect extends Rect { + + private Shader2c shader; + + protected float r, g, b, a; + + public ColRect(Camera camera, float x, float y, float z, float width, float height, float r, float g, float b, float a, boolean gui) { + super(camera, x, y, z, width, height, 0, gui); + shader = Shader.SHADER2C; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public void render() { + shader.enable(); + shader.setColor(r, g, b, a); + Matrix4f cmat = gui ? camera.getProjection() : camera.getMatrix(); + shader.setMVP(cmat.translate(position.add(width * scale / 2, height * scale / 2, 0, new Vector3f())).rotateZ(rotation * 3.1415927f / 180).scale(width * scale, height * scale, 1).translate(-0.5f, -0.5f, 0)); + vao.render(); + shader.disable(); + } + + public void setColor(float r, float g, float b) { + this.r = r; + this.g = g; + this.b = b; + } + + public void setColor(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public void setAlpha(float alpha) { + a = alpha; + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/Line.java b/src/com/gnarwhal/ld46/engine/model/Line.java new file mode 100644 index 0000000..00d259d --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/Line.java @@ -0,0 +1,117 @@ +package com.gnarwhal.ld46.engine.model; + +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.shaders.Shader; +import com.gnarwhal.ld46.engine.shaders.Shader2c; +import org.joml.Vector3f; + +public class Line { + + private static Vao cap, rect; + + private Camera camera; + private Shader2c shader; + + private Vector3f position; + private float angle, length, thickness; + private float r, g, b, a; + + public Line(Camera camera, float x1, float y1, float x2, float y2, float depth, float thickness) { + this.camera = camera; + shader = Shader.SHADER2C; + if(cap == null) + initVaos(); + this.thickness = thickness; + position = new Vector3f(x1, y1, depth); + setPoints(x1, y1, x2, y2); + r = 1; + g = 1; + b = 1; + a = 1; + } + + private void initVaos() { + float[] rVertices = { + 0, 0.5f, 0, + 0, -0.5f, 0, + 1, -0.5f, 0, + 1, 0.5f, 0 + }; + int rIndices[] = { + 0, 1, 3, + 1, 2, 3 + }; + rect = new Vao(rVertices, rIndices); + final int NUM_POINTS = 10; + float[] cVertices = new float[NUM_POINTS * 3]; + int[] cIndices = new int[(NUM_POINTS - 2) * 3]; + for (int i = 0; i < cVertices.length; i += 3) { + double angle = Math.PI * i / (NUM_POINTS * 3 - 3) + Math.PI / 2; + cVertices[i ] = (float) Math.cos(angle) / 2; + cVertices[i + 1] = (float) Math.sin(angle) / 2; + cVertices[i + 2] = 0; + } + for (int i = 0; i < cIndices.length; i += 3) { + cIndices[i ] = 0; + cIndices[i + 1] = i / 3 + 1; + cIndices[i + 2] = i / 3 + 2; + } + cap = new Vao(cVertices, cIndices); + } + + public void render() { + shader.enable(); + shader.setColor(r, g, b, a); + shader.setMVP(camera.getMatrix().translate(position).rotateZ(angle).scale(thickness)); + cap.render(); + shader.setMVP(camera.getMatrix().translate(position).rotateZ(angle).scale(length, thickness, 1)); + rect.render(); + shader.setMVP(camera.getMatrix().translate(position.add((float) (Math.cos(angle) * length), (float) (-Math.sin(Math.PI + angle) * length), 0, new Vector3f())).rotateZ((float) Math.PI).rotateZ(angle).scale(thickness)); + cap.render(); + shader.disable(); + } + + public void setAngle(float x, float y, float angle, float length) { + position.x = x; + position.y = y; + this.angle = angle; + this.length = length; + } + + public void setPoints(float x1, float y1, float x2, float y2) { + float xl = x2 - x1; + float yl = y2 - y1; + length = (float) Math.sqrt(xl * xl + yl * yl); + if(x1 != x2) { + angle = (float) Math.atan(yl / xl); + if(xl < 0) + angle += Math.PI; + setAngle(x1, y1, angle, length); + } + else if(y1 > y2) + setAngle(x1, y1, (float) Math.PI * 1.5f, length); + else if(y1 < y2) + setAngle(x1, y1, (float) Math.PI * 0.5f, length); + else + setAngle(x1, y1, 0, 0); + } + + public void setPoints(Vector3f p1, Vector3f p2) { + setPoints(p1.x, p1.y, p2.x, p2.y); + } + + public void setThickness(float thickness) { + this.thickness = thickness; + } + + public void setDepth(float z) { + position.z = z; + } + + public void setColor(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/Rect.java b/src/com/gnarwhal/ld46/engine/model/Rect.java new file mode 100644 index 0000000..79f3410 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/Rect.java @@ -0,0 +1,137 @@ +package com.gnarwhal.ld46.engine.model; + +import com.gnarwhal.ld46.engine.display.Camera; +import org.joml.Vector2f; +import org.joml.Vector3f; + +public class Rect { + + protected static Vao vao; + + protected Camera camera; + + protected float width, height; + protected Vector3f position; + protected float rotation, scale; + protected boolean gui; + + protected Rect(Camera camera, float x, float y, float z, float width, float height, float rotation, boolean gui) { + this.camera = camera; + this.width = width; + this.height = height; + position = new Vector3f(x, y, z); + scale = 1; + this.rotation = rotation; + this.gui = gui; + if(vao == null) { + float vertices[] = { + 1, 0, 0, // Top left + 1, 1, 0, // Bottom left + 0, 1, 0, // Bottom right + 0, 0, 0 // Top right + }; + int indices[] = { + 0, 1, 3, + 1, 2, 3 + }; + float[] texCoords = { + 1, 0, + 1, 1, + 0, 1, + 0, 0 + }; + vao = new Vao(vertices, indices); + vao.addAttrib(texCoords, 2); + } + } + + public float getX() { + return position.x; + } + + public float getY() { + return position.y; + } + + public Vector2f getPosition() { + return new Vector2f(position.x, position.y); + } + + public float getWidth() { + return width; + } + + public float getHeight() { + return height; + } + + public Vector2f getDimensions() { + return new Vector2f(width, height); + } + + public void setX(float x) { + position.x = x; + } + + public void setY(float y) { + position.y = y; + } + + public void set(float x, float y, float width, float height) { + position.x = x; + position.y = y; + this.width = width; + this.height = height; + } + + public void setWidth(float width) { + this.width = width; + } + + public void setHeight(float height) { + this.height = height; + } + + public void setPosition(float x, float y) { + position.set(x, y, position.z); + } + + public void setPosition(float x, float y, float z) { + position.set(x, y, z); + } + + public void setPosition(Vector3f position) { + this.position.x = position.x; + this.position.y = position.y; + this.position.z = position.z; + } + + public void setPosition(Vector2f position) { + this.position.x = position.x; + this.position.y = position.y; + } + + public void translate(Vector3f vector) { + position.add(vector); + } + + public void translate(float x, float y, float z) { + position.add(x, y, z); + } + + public void setRotation(float angle) { + rotation = angle; + } + + public void rotate(float angle) { + rotation += angle; + } + + public void setScale(float scale) { + this.scale = scale; + } + + public void setAngle(float angle) { + this.rotation = angle; + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/TexRect.java b/src/com/gnarwhal/ld46/engine/model/TexRect.java new file mode 100644 index 0000000..d9755d4 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/TexRect.java @@ -0,0 +1,44 @@ +package com.gnarwhal.ld46.engine.model; + +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.shaders.Shader; +import com.gnarwhal.ld46.engine.shaders.Shader2t; +import com.gnarwhal.ld46.engine.texture.Texture; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class TexRect extends Rect { + + private Texture texture; + private Shader2t shader = Shader.SHADER2T; + protected float direction = 1; + + public TexRect(Camera camera, String path, float x, float y, float z, float width, float height, float rotation, boolean gui) { + super(camera, x, y, z, width, height, rotation, gui); + texture = new Texture(path); + } + + public TexRect(Camera camera, Texture texture, float x, float y, float z, float width, float height, float rotation, boolean gui) { + super(camera, x, y, z, width, height, rotation, gui); + this.texture = texture; + } + + public void render() { + texture.bind(); + shader.enable(); + Matrix4f cmat = gui ? camera.getProjection() : camera.getMatrix(); + shader.setMVP(cmat.translate(position.add(width * scale / 2, height * scale / 2, 0, new Vector3f())).rotateZ(rotation).scale(width * scale * direction, height * scale, 1).translate(-0.5f, -0.5f, 0)); + vao.render(); + shader.disable(); + texture.unbind(); + } + + public void setCenter(float x, float y) { + position.x = x - width / 2; + position.y = y - height / 2; + } + + public void setTexture(Texture texture) { + this.texture = texture; + } +} diff --git a/src/com/gnarwhal/ld46/engine/model/Vao.java b/src/com/gnarwhal/ld46/engine/model/Vao.java new file mode 100644 index 0000000..c91f3b9 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/model/Vao.java @@ -0,0 +1,50 @@ +package com.gnarwhal.ld46.engine.model; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.*; + +public class Vao { + + private int numAttribs = 0; + + private int vao, ibo, count; + + private int[] vbos = new int[15]; + + public Vao(float[] vertices, int[] indices) { + vao = glGenVertexArrays(); + glBindVertexArray(vao); + addAttrib(vertices, 3); + ibo = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW); + count = indices.length; + } + + public void addAttrib(float[] data, int size) { + int vbo = glGenBuffers(); + vbos[numAttribs] = vbo; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW); + glVertexAttribPointer(numAttribs, size, GL_FLOAT, false, 0, 0); + ++numAttribs; + } + + public void render() { + glBindVertexArray(vao); + for(int i = 0; i < numAttribs; ++i) + glEnableVertexAttribArray(i); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0); + for(int i = 0; i < numAttribs; ++i) + glDisableVertexAttribArray(i); + } + + public void destroy() { + for(int vbo : vbos) + glDeleteBuffers(vbo); + glDeleteBuffers(ibo); + glDeleteVertexArrays(vao); + } +} diff --git a/src/com/gnarwhal/ld46/engine/properties/ImproperFormattingException.java b/src/com/gnarwhal/ld46/engine/properties/ImproperFormattingException.java new file mode 100644 index 0000000..fadb474 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/properties/ImproperFormattingException.java @@ -0,0 +1,8 @@ +package com.gnarwhal.ld46.engine.properties; + +public class ImproperFormattingException extends RuntimeException { + + public ImproperFormattingException(String message) { + super(message); + } +} diff --git a/src/com/gnarwhal/ld46/engine/properties/Properties.java b/src/com/gnarwhal/ld46/engine/properties/Properties.java new file mode 100644 index 0000000..2bbcaea --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/properties/Properties.java @@ -0,0 +1,104 @@ +package com.gnarwhal.ld46.engine.properties; + +public class Properties { + + private String name; + private PropNode head, cur; + + public Properties(String name) { + this.name = new String(name); + } + + public void add(PropNode node) { + if(head == null) { + head = node; + cur = node; + } + else { + cur.next = node; + cur = cur.next; + } + } + + private PropNode get(String key) throws UndeclaredPropertyException { + String[] keys = key.split("\\."); + PropNode mobile = head; + while (mobile != null) { + if(mobile.key.equals(keys[0])) { + if(keys.length > 1 && mobile instanceof BlockNode) + return ((BlockNode) mobile).data.get(key.substring(keys[0].length() + 1)); + else + return mobile; + } + mobile = mobile.next; + } + throw new UndeclaredPropertyException("Property '" + key + "' in properties '" + name + "' was not found!"); + } + + public String getAsString(String key) throws UndeclaredPropertyException { + return ((StringNode) get(key)).data; + } + + public int getAsInt(String key) throws UndeclaredPropertyException { + return ((IntNode) get(key)).data; + } + + public int[] getAsIntArray(String key) throws UndeclaredPropertyException { + PropNode node = get(key); + if(node instanceof IntNode) + return new int[] { ((IntNode) node).data }; + return ((IntArrayNode) get(key)).data; + } + + public double getAsDouble(String key) throws UndeclaredPropertyException { + PropNode node = get(key); + if(node instanceof IntNode) + return (double) ((IntNode) node).data; + return ((DoubleNode) get(key)).data; + } + + public double[] getAsDoubleArray(String key) throws UndeclaredPropertyException { + PropNode node = get(key); + if(node instanceof DoubleNode) + return new double[] { ((DoubleNode) node).data }; + if(node instanceof IntNode) + return new double[] { ((IntNode) node).data }; + if(node instanceof IntArrayNode) { + int[] ints = getAsIntArray(key); + double[] ret = new double[ints.length]; + for (int i = 0; i < ints.length; ++i) + ret[i] = ints[i]; + return ret; + } + return ((DoubleArrayNode) get(key)).data; + } + + public static class PropNode { + public String key; + public PropNode next; + } + + public static class BlockNode extends PropNode { + public Properties data; + } + + public static class StringNode extends PropNode { + public String data; + } + + public static class IntNode extends PropNode { + public int data; + } + + public static class IntArrayNode extends PropNode { + public int[] data; + } + + public static class DoubleNode extends PropNode { + public double data; + } + + public static class DoubleArrayNode extends PropNode { + public double[] data; + } +} diff --git a/src/com/gnarwhal/ld46/engine/properties/PropertyReader.java b/src/com/gnarwhal/ld46/engine/properties/PropertyReader.java new file mode 100644 index 0000000..ea1d9a5 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/properties/PropertyReader.java @@ -0,0 +1,91 @@ +package com.gnarwhal.ld46.engine.properties; + +import com.gnarwhal.ld46.engine.properties.Properties.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Scanner; + +public class PropertyReader { + + private static int lineNum; + private static String path; + + public static Properties readProperties(String path) { + Properties props = null; + try { + File file = new File(path); + Scanner scanner = new Scanner(file); + PropertyReader.path = path; + lineNum = 0; + props = readBlock(file.getName(), scanner).data; + scanner.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + System.exit(-1); + } + return props; + } + + private static BlockNode readBlock(String name, Scanner scanner) { + BlockNode props = new BlockNode(); + props.key = name; + props.data = new Properties(name); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + line = line.replaceAll("\\s", ""); + if(line.equals("}")) + break; + else if(line.length() < 2 || !line.substring(0, 2).equals("//")){ + String[] pair = line.split(":"); + if (pair.length != 2) + throw new ImproperFormattingException("Formatting exception on line " + line + " in file '" + path + "!"); + pair[1] = pair[1].replaceAll("\\s", ""); + if (pair[1].equals("{")) + props.data.add(readBlock(pair[0], scanner)); + else if (pair[1].matches("(\\d+|0x[\\da-f]+)")) { + IntNode node = new IntNode(); + node.key = pair[0]; + node.data = Integer.decode(pair[1]); + props.data.add(node); + } + else if (pair[1].matches("(\\d+|0x[\\d0-9]+)(,(\\d+|0x[\\d0-9]+))+")) { + String[] data = pair[1].split(","); + int[] ints = new int[data.length]; + for (int i = 0; i < ints.length; ++i) + ints[i] = Integer.decode(data[i]); + IntArrayNode node = new IntArrayNode(); + node.key = pair[0]; + node.data = ints; + props.data.add(node); + + } + else if (pair[1].matches("\\d+\\.\\d+")) { + DoubleNode node = new DoubleNode(); + node.key = pair[0]; + node.data = Double.parseDouble(pair[1]); + props.data.add(node); + } + else if (pair[1].matches("\\d+\\.\\d+(,\\d+\\.\\d+)+")) { + String[] data = pair[1].split(","); + double[] doubles = new double[data.length]; + for (int i = 0; i < doubles.length; ++i) + doubles[i] = Double.parseDouble(data[i]); + DoubleArrayNode node = new DoubleArrayNode(); + node.key = pair[0]; + node.data = doubles; + props.data.add(node); + + } + else { + StringNode node = new StringNode(); + node.key = pair[0]; + node.data = pair[1]; + props.data.add(node); + } + } + ++lineNum; + } + return props; + } +} diff --git a/src/com/gnarwhal/ld46/engine/properties/UndeclaredPropertyException.java b/src/com/gnarwhal/ld46/engine/properties/UndeclaredPropertyException.java new file mode 100644 index 0000000..2704992 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/properties/UndeclaredPropertyException.java @@ -0,0 +1,8 @@ +package com.gnarwhal.ld46.engine.properties; + +public class UndeclaredPropertyException extends Exception { + + public UndeclaredPropertyException(String exception) { + super(exception); + } +} diff --git a/src/com/gnarwhal/ld46/engine/shaders/Shader.java b/src/com/gnarwhal/ld46/engine/shaders/Shader.java new file mode 100644 index 0000000..cebf142 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/shaders/Shader.java @@ -0,0 +1,85 @@ +package com.gnarwhal.ld46.engine.shaders; + +import org.joml.Matrix4f; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import static org.lwjgl.opengl.GL20.*; + +public abstract class Shader { + + public static Shader2c SHADER2C; + public static Shader2t SHADER2T; + public static Shader2b SHADER2B; + + protected int program; + + protected int mvpLoc; + + protected Shader(String vertPath, String fragPath) { + program = glCreateProgram(); + + int vert = loadShader(vertPath, GL_VERTEX_SHADER); + int frag = loadShader(fragPath, GL_FRAGMENT_SHADER); + + glAttachShader(program, vert); + glAttachShader(program, frag); + + glLinkProgram(program); + + glDetachShader(program, vert); + glDetachShader(program, frag); + + glDeleteShader(vert); + glDeleteShader(frag); + + mvpLoc = glGetUniformLocation(program, "mvp"); + } + + private int loadShader(String path, int type) { + StringBuilder file = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(new File(path))); + String line; + while((line = reader.readLine()) != null) + file.append(line + '\n'); + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + String source = file.toString(); + int shader = glCreateShader(type); + glShaderSource(shader, source); + glCompileShader(shader); + if(glGetShaderi(shader, GL_COMPILE_STATUS) != 1) + throw new RuntimeException("Failed to compile shader: " + path + "! " + glGetShaderInfoLog(shader)); + return shader; + } + + protected abstract void getUniforms(); + + public void setMVP(Matrix4f matrix) { + glUniformMatrix4fv(mvpLoc, false, matrix.get(new float[16])); + } + + public void enable() { + glUseProgram(program); + } + + public void disable() { + glUseProgram(0); + } + + public void destroy() { + glDeleteProgram(program); + } + + public static void init() { + SHADER2C = new Shader2c(); + SHADER2T = new Shader2t(); + SHADER2B = new Shader2b(); + } +} diff --git a/src/com/gnarwhal/ld46/engine/shaders/Shader2b.java b/src/com/gnarwhal/ld46/engine/shaders/Shader2b.java new file mode 100644 index 0000000..d77b4f9 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/shaders/Shader2b.java @@ -0,0 +1,32 @@ +package com.gnarwhal.ld46.engine.shaders; + +import static org.lwjgl.opengl.GL20.*; + +public class Shader2b extends Shader { + + private int offset0Loc; + private int offset1Loc; + + protected Shader2b() { + super("res/shaders/s2b/vert.gls", "res/shaders/s2b/frag.gls"); + getUniforms(); + } + + public void setOffset(float offset0, float offset1) { + glUniform1f(offset0Loc, offset0); + glUniform1f(offset1Loc, offset1); + } + + @Override + protected void getUniforms() { + offset0Loc = glGetUniformLocation(program, "offset0"); + offset1Loc = glGetUniformLocation(program, "offset1"); + + int layer0 = glGetUniformLocation(program, "layer0"); + int layer1 = glGetUniformLocation(program, "layer1"); + + enable(); + glUniform1i(layer0, 0); + glUniform1i(layer1, 1); + } +} \ No newline at end of file diff --git a/src/com/gnarwhal/ld46/engine/shaders/Shader2c.java b/src/com/gnarwhal/ld46/engine/shaders/Shader2c.java new file mode 100644 index 0000000..eb15e2a --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/shaders/Shader2c.java @@ -0,0 +1,28 @@ +package com.gnarwhal.ld46.engine.shaders; + +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniform4f; + +public class Shader2c extends Shader { + + int colorLoc; + + protected Shader2c(String vert, String frag) { + super(vert, frag); + getUniforms(); + } + + protected Shader2c() { + super("res/shaders/s2c/vert.gls", "res/shaders/s2c/frag.gls"); + getUniforms(); + } + + @Override + protected void getUniforms() { + colorLoc = glGetUniformLocation(program, "iColor"); + } + + public void setColor(float r, float g, float b, float a) { + glUniform4f(colorLoc, r, g, b, a); + } +} diff --git a/src/com/gnarwhal/ld46/engine/shaders/Shader2t.java b/src/com/gnarwhal/ld46/engine/shaders/Shader2t.java new file mode 100644 index 0000000..429d4b2 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/shaders/Shader2t.java @@ -0,0 +1,12 @@ +package com.gnarwhal.ld46.engine.shaders; + +public class Shader2t extends Shader { + + protected Shader2t() { + super("res/shaders/s2t/vert.gls", "res/shaders/s2t/frag.gls"); + getUniforms(); + } + + @Override + protected void getUniforms() {} +} \ No newline at end of file diff --git a/src/com/gnarwhal/ld46/engine/texture/Texture.java b/src/com/gnarwhal/ld46/engine/texture/Texture.java new file mode 100644 index 0000000..bf1acf6 --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/texture/Texture.java @@ -0,0 +1,88 @@ +package com.gnarwhal.ld46.engine.texture; + +import org.lwjgl.BufferUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL13.GL_TEXTURE0; +import static org.lwjgl.opengl.GL13.glActiveTexture; + +public class Texture { + + protected int id, width, height; + + public Texture(String name) { + this(name, GL_CLAMP); + } + + public Texture(String name, int wrap) { + BufferedImage bi = null; + try { + bi = ImageIO.read(new File(name)); + } catch (IOException e) { + e.printStackTrace(); + } + if (bi != null) { + width = bi.getWidth(); + height = bi.getHeight(); + int[] pixels = bi.getRGB(0, 0, width, height, null, 0, width); + ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4); + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int pixel = pixels[i * width + j]; + buffer.put((byte)((pixel >> 16) & 0xFF)); // Red + buffer.put((byte)((pixel >> 8) & 0xFF)); // Green + buffer.put((byte)((pixel ) & 0xFF)); // Blue + buffer.put((byte)((pixel >> 24) & 0xFF)); // Alpha + } + } + buffer.flip(); + + id = glGenTextures(); + bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + unbind(); + } + } + + public Texture(int id, int width, int height) { + this.id = id; + this.width = width; + this.height = height; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void bind() { + bind(0); + } + + public void bind(int activeTexture) { + glActiveTexture(GL_TEXTURE0 + activeTexture); + glBindTexture(GL_TEXTURE_2D, id); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + + public void destroy() { + glDeleteTextures(id); + } +} diff --git a/src/com/gnarwhal/ld46/engine/texture/TextureSet.java b/src/com/gnarwhal/ld46/engine/texture/TextureSet.java new file mode 100644 index 0000000..e50312f --- /dev/null +++ b/src/com/gnarwhal/ld46/engine/texture/TextureSet.java @@ -0,0 +1,20 @@ +package com.gnarwhal.ld46.engine.texture; + +public class TextureSet { + + private Texture[] textures; + + public TextureSet(String[] paths) { + textures = new Texture[paths.length]; + for (int i = 0; i < textures.length; ++i) + textures[i] = new Texture(paths[i]); + } + + public int length() { + return textures.length; + } + + public Texture get(int index) { + return textures[index]; + } +} diff --git a/src/com/gnarwhal/ld46/game/GamePanel.java b/src/com/gnarwhal/ld46/game/GamePanel.java new file mode 100644 index 0000000..fbaebc5 --- /dev/null +++ b/src/com/gnarwhal/ld46/game/GamePanel.java @@ -0,0 +1,23 @@ +package com.gnarwhal.ld46.game; + +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.display.Window; + +public class GamePanel { + + private Window window; + private Camera camera; + + public GamePanel(Window window, Camera camera) { + this.window = window; + this.camera = camera; + } + + public void update() { + + } + + public void render() { + + } +} diff --git a/src/com/gnarwhal/ld46/game/Main.java b/src/com/gnarwhal/ld46/game/Main.java new file mode 100644 index 0000000..82d9f70 --- /dev/null +++ b/src/com/gnarwhal/ld46/game/Main.java @@ -0,0 +1,78 @@ +package com.gnarwhal.ld46.game; + +import com.gnarwhal.ld46.engine.audio.ALManagement; +import com.gnarwhal.ld46.engine.audio.Sound; +import com.gnarwhal.ld46.engine.display.Camera; +import com.gnarwhal.ld46.engine.display.Window; +import com.gnarwhal.ld46.engine.shaders.Shader; + +public class Main { + + public static int fps; + public static double dtime; + + private ALManagement al; + + private Window window; + private Camera camera; + + private GamePanel panel; + + public void start() { + init(); + int frames = 0; + long curTime, pastTime, pastSec, nspf = 1000000000 / Window.REFRESH_RATE; + pastTime = System.nanoTime(); + pastSec = pastTime; + while(!window.shouldClose()) { + curTime = System.nanoTime(); + if (curTime - pastTime > nspf) { + dtime = nspf / 1000000000d; + update(); + render(); + pastTime += nspf; + ++frames; + } + if (curTime - pastSec > 1000000000) { + fps = frames; + frames = 0; + pastSec += 1000000000; + } + if (nspf - curTime + pastTime > 10000000) try { + Thread.sleep(1); + } catch (Exception e) { + e.printStackTrace(); + } + } + al.destroy(); + Window.terminate(); + } + + private void init() { + al = new ALManagement(); + + final int WIN_WIDTH = 768, WIN_HEIGHT = 432; + window = new Window("Ludum Dare 46", true); + //window = new Window(WIN_WIDTH, WIN_HEIGHT, "Ludum Dare 46", true, true, true); + camera = new Camera(WIN_WIDTH, WIN_HEIGHT); + Shader.init(); + + panel = new GamePanel(window, camera); + } + + private void update() { + window.update(); + panel.update(); + camera.update(); + } + + private void render() { + window.clear(); + panel.render(); + window.swap(); + } + + public static void main(String[] args) { + new Main().start(); + } +}