PageRenderTime 63ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/ProgressEngine.ahk

http://github.com/Uberi/ProgressPlatformer
AutoHotKey | 799 lines | 592 code | 111 blank | 96 comment | 51 complexity | a8725038b1d4117e59bc4f8bdf389f98 MD5 | raw file
  1. #NoEnv
  2. /*
  3. Copyright 2011-2012 Anthony Zhang <azhang9@gmail.com>
  4. This file is part of ProgressPlatformer. Source code is available at <https://github.com/Uberi/ProgressPlatformer>.
  5. ProgressPlatformer is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU Affero General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Affero General Public License for more details.
  13. You should have received a copy of the GNU Affero General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. class ProgressEngine
  17. {
  18. __New(hWindow)
  19. {
  20. this.Layers := []
  21. this.FrameRate := 60
  22. this.hWindow := hWindow
  23. this.hDC := DllCall("GetDC","UPtr",hWindow,"UPtr")
  24. If !this.hDC
  25. throw Exception("Could not obtain window device context.")
  26. this.hMemoryDC := DllCall("CreateCompatibleDC","UPtr",this.hDC,"UPtr")
  27. If !this.hMemoryDC
  28. throw Exception("Could not create memory device context.")
  29. this.hOriginalBitmap := 0
  30. this.hBitmap := 0
  31. If !DllCall("SetBkMode","UPtr",this.hMemoryDC,"Int",1) ;TRANSPARENT
  32. throw Exception("Could not set background mode.")
  33. }
  34. __Delete()
  35. {
  36. If this.hOriginalBitmap && !DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",this.hOriginalBitmap,"UPtr") ;deselect the bitmap from the device context
  37. throw Exception("Could not deselect bitmap from memory device context.")
  38. If this.hBitmap && !DllCall("DeleteObject","UPtr",this.hBitmap) ;delete the bitmap
  39. throw Exception("Could not delete bitmap.")
  40. If !DllCall("DeleteObject","UPtr",this.hMemoryDC) ;delete the memory device context
  41. throw Exception("Could not delete memory device context.")
  42. If !DllCall("ReleaseDC","UPtr",this.hWindow,"UPtr",this.hDC) ;release the window device context
  43. throw Exception("Could not release window device context.")
  44. }
  45. Start(DeltaLimit = 0.05)
  46. {
  47. ;calculate the amount of time each iteration should take
  48. If this.FrameRate != 0
  49. FrameDelay := 1000 / this.FrameRate
  50. TickFrequency := 0, PreviousTicks := 0, CurrentTicks := 0, ElapsedTime := 0
  51. If !DllCall("QueryPerformanceFrequency","Int64*",TickFrequency) ;obtain ticks per second
  52. throw Exception("Could not obtain performance counter frequency.")
  53. If !DllCall("QueryPerformanceCounter","Int64*",PreviousTicks) ;obtain the performance counter value
  54. throw Exception("Could not obtain performance counter value.")
  55. Loop
  56. {
  57. ;calculate the total time elapsed since the last iteration
  58. If !DllCall("QueryPerformanceCounter","Int64*",CurrentTicks)
  59. throw Exception("Could not obtain performance counter value.")
  60. Delta := (CurrentTicks - PreviousTicks) / TickFrequency
  61. PreviousTicks := CurrentTicks
  62. ;clamp delta to the upper limit
  63. If (Delta > DeltaLimit)
  64. Delta := DeltaLimit
  65. Result := this.Update(Delta)
  66. If Result
  67. Return, Result
  68. ;calculate the time elapsed during stepping in milliseconds
  69. If !DllCall("QueryPerformanceCounter","Int64*",ElapsedTime)
  70. throw Exception("Could not obtain performance counter value.")
  71. ElapsedTime := ((ElapsedTime - CurrentTicks) / TickFrequency) * 1000
  72. ;sleep the amount of time required to limit the framerate to the desired value
  73. If (this.FrameRate != 0 && ElapsedTime < FrameDelay)
  74. Sleep, % Round(FrameDelay - ElapsedTime)
  75. }
  76. }
  77. class Layer
  78. {
  79. __New()
  80. {
  81. this.Visible := 1
  82. this.X := 0
  83. this.Y := 0
  84. this.W := 10
  85. this.H := 10
  86. this.Entities := []
  87. }
  88. }
  89. Update(Delta)
  90. {
  91. static Width1 := -1, Height1 := -1, Viewport := Object()
  92. ;obtain the dimensions of the client area
  93. VarSetCapacity(ClientRectangle,16)
  94. If !DllCall("GetClientRect","UPtr",this.hWindow,"UPtr",&ClientRectangle) ;wip: also support controls rather than just windows
  95. throw Exception("Could not obtain client area dimensions.")
  96. Width := NumGet(ClientRectangle,8,"Int"), Height := NumGet(ClientRectangle,12,"Int")
  97. ;update bitmap if window was resized
  98. If (Width != Width1 || Height != Height1)
  99. {
  100. ;deselect the old bitmap if present
  101. If this.hOriginalBitmap
  102. {
  103. If !DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",this.hOriginalBitmap,"UPtr")
  104. throw Exception("Could not select original bitmap into memory device context.")
  105. }
  106. ;create a new bitmap with the correct dimensions
  107. this.hBitmap := DllCall("CreateCompatibleBitmap","UPtr",this.hDC,"Int",Width,"Int",Height,"UPtr")
  108. If !this.hBitmap
  109. throw Exception("Could not create bitmap.")
  110. ;select the new bitmap into the device context
  111. this.hOriginalBitmap := DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",this.hBitmap,"UPtr")
  112. If !this.hOriginalBitmap
  113. throw Exception("Could not select bitmap into memory device context.")
  114. }
  115. Width1 := Width, Height1 := Height
  116. ;initialize the viewport
  117. Viewport.ScreenX := 0, Viewport.ScreenY := 0 ;wip: change to allow controls as viewports that are not at the top left corner of the client area
  118. Viewport.ScreenW := Width, Viewport.ScreenH := Height
  119. ;iterate through each layer
  120. For Index, Layer In this.Layers ;step entities
  121. {
  122. ;check for layer visibility
  123. If !Layer.Visible ;wip: two properties: Layer.Active and Layer.Visible, as well as for entities, which determine whether to step and whether to draw, respectively
  124. Continue
  125. ;set up the viewport
  126. Viewport.X := Layer.X, Viewport.Y := Layer.Y
  127. Viewport.W := 10, Viewport.H := 10
  128. ProportionX := Width * (Layer.W / 100), ProportionY := Height * (Layer.H / 100)
  129. ;set up properties of each entity in the layer
  130. For Key, Entity In Layer.Entities ;wip: log(n) occlusion culling here
  131. {
  132. ;set the screen coordinates of the bounding rectangle
  133. Entity.ScreenX := (Entity.X - Layer.X) * ProportionX, Entity.ScreenY := (Entity.Y - Layer.Y) * ProportionY
  134. Entity.ScreenW := Entity.W * ProportionX, Entity.ScreenH := Entity.H * ProportionY
  135. ;set the coordinate transformations of the bounding rectangle
  136. Entity.OffsetX := 0, Entity.OffsetY := 0
  137. Entity.ScaleX := ProportionX, Entity.ScaleY := ProportionY
  138. }
  139. ;start each entity in the layer
  140. For Key, Entity In Layer.Entities ;wip: log(n) occlusion culling here
  141. {
  142. Result := Entity.Start(Delta,Layer,Viewport) ;step the entity
  143. If Result
  144. Return, Result
  145. }
  146. ;step each entity in the layer
  147. For Key, Entity In Layer.Entities ;wip: log(n) occlusion culling here
  148. {
  149. Result := Entity.Step(Delta,Layer,Viewport) ;step the entity
  150. If Result
  151. Return, Result
  152. }
  153. ;end each entity in the layer
  154. For Key, Entity In Layer.Entities ;wip: log(n) occlusion culling here
  155. {
  156. Result := Entity.End(Delta,Layer,Viewport) ;step the entity
  157. If Result
  158. Return, Result
  159. }
  160. }
  161. ;iterate through each layer
  162. For Index, Layer In this.Layers
  163. {
  164. ;check for layer visibility
  165. If !Layer.Visible
  166. Continue
  167. ;set up the viewport
  168. Viewport.X := Layer.X, Viewport.Y := Layer.Y
  169. Viewport.W := Layer.W, Viewport.H := Layer.H
  170. ;iterate through each entity in the layer
  171. For Key, Entity In Layer.Entities ;wip: log(n) occlusion culling here
  172. Entity.Draw(this.hMemoryDC,Layer,Viewport) ;draw the entity
  173. }
  174. If !DllCall("BitBlt","UPtr",this.hDC,"Int",0,"Int",0,"Int",Width,"Int",Height,"UPtr",this.hMemoryDC,"Int",0,"Int",0,"UInt",0xCC0020) ;SRCCOPY
  175. throw Exception("Could not transfer pixel data to window device context.")
  176. Return, 0
  177. }
  178. }
  179. class ProgressEntities
  180. {
  181. class Basis
  182. {
  183. __New()
  184. {
  185. this.X := 0
  186. this.Y := 0
  187. this.W := 10
  188. this.H := 10
  189. this.Visible := 1
  190. this.ScreenX := 0
  191. this.ScreenY := 0
  192. this.ScreenW := 0
  193. this.ScreenH := 0
  194. }
  195. Start(Delta,Layer,Viewport)
  196. {
  197. }
  198. Step(Delta,Layer,Viewport)
  199. {
  200. }
  201. End(Delta,Layer,Viewport)
  202. {
  203. }
  204. Draw(hDC,Layer,Viewport)
  205. {
  206. }
  207. NearestEntities(Layer,Distance = 2)
  208. {
  209. ;wip: use occlusion culling to return only entities within the distance
  210. ;wip: accept a rectangle object instead of a distance
  211. Return, Layer.Entities
  212. }
  213. MouseHovering(Viewport)
  214. {
  215. CoordMode, Mouse, Client
  216. MouseGetPos, MouseX, MouseY
  217. MouseX -= Viewport.ScreenX
  218. MouseY -= Viewport.ScreenY
  219. If (MouseX >= this.ScreenX && MouseX <= (this.ScreenX + this.ScreenW)
  220. && MouseY >= this.ScreenY && MouseY <= (this.ScreenY + this.ScreenH))
  221. Return, 1
  222. Return, 0
  223. }
  224. Intersect(Rectangle,ByRef IntersectX = "",ByRef IntersectY = "")
  225. {
  226. Left1 := this.X, Left2 := Rectangle.X
  227. Right1 := Left1 + this.W, Right2 := Left2 + Rectangle.W
  228. Top1 := this.Y, Top2 := Rectangle.Y
  229. Bottom1 := Top1 + this.H, Bottom2 := Top2 + Rectangle.H
  230. ;check for collision
  231. If (Right1 < Left2 || Right2 < Left1 || Bottom1 < Top2 || Bottom2 < Top1)
  232. {
  233. IntersectX := 0, IntersectY := 0
  234. Return, 0 ;no collision occurred
  235. }
  236. ;find width of intersection
  237. If (Left1 < Left2)
  238. IntersectX := ((Right1 < Right2) ? Right1 : Right2) - Left2
  239. Else
  240. IntersectX := Left1 - ((Right1 < Right2) ? Right1 : Right2)
  241. ;find height of intersection
  242. If (Top1 < Top2)
  243. IntersectY := ((Bottom1 < Bottom2) ? Bottom1 : Bottom2) - Top2
  244. Else
  245. IntersectY := Top1 - ((Bottom1 < Bottom2) ? Bottom1 : Bottom2)
  246. Return, 1 ;collision occurred
  247. }
  248. Inside(Rectangle)
  249. {
  250. Return, this.X >= Rectangle.X
  251. && (this.X + this.W) <= (Rectangle.X + Rectangle.W)
  252. && this.Y >= Rectangle.Y
  253. && (this.Y + this.H) <= (Rectangle.Y + Rectangle.H)
  254. }
  255. }
  256. class Container extends ProgressEntities.Basis ;wip: occlusion culling for this
  257. {
  258. __New()
  259. {
  260. base.__New()
  261. this.Layers := []
  262. }
  263. Step(Delta,Layer,Viewport)
  264. {
  265. ;initialize the current viewport
  266. CurrentViewport := Object()
  267. CurrentViewport.ScreenX := this.ScreenX, CurrentViewport.ScreenY := this.ScreenY
  268. CurrentViewport.ScreenW := this.ScreenW, CurrentViewport.ScreenH := this.ScreenH
  269. ;iterate through each layer
  270. For Index, CurrentLayer In this.Layers
  271. {
  272. ;check for layer visibility
  273. If !CurrentLayer.Visible
  274. Continue
  275. ;calculate viewport transformations
  276. ProportionX := Viewport.ScreenW / CurrentLayer.W, ProportionY := Viewport.ScreenH / CurrentLayer.H
  277. RatioX := this.W / CurrentLayer.W, RatioY := this.H / CurrentLayer.H
  278. PositionX := this.OffsetX + (this.X + (CurrentLayer.X * RatioX)) * ProportionX, PositionY := this.OffsetY + (this.Y + (CurrentLayer.Y * RatioY)) * ProportionY
  279. ;set up the current viewport
  280. CurrentViewport.X := this.X + CurrentLayer.X, CurrentViewport.Y := this.Y + CurrentLayer.Y
  281. CurrentViewport.W := this.W, CurrentViewport.H := this.H
  282. ;set up properties of each object in the layer
  283. For Key, Entity In CurrentLayer.Entities
  284. {
  285. ;set the coordinate transformations of the bounding rectangle
  286. Entity.OffsetX := (this.OffsetX + this.X) * ProportionX, Entity.OffsetY := (this.OffsetY + this.Y) * ProportionY
  287. Entity.ScaleX := ProportionX * RatioX, Entity.ScaleY := ProportionY * RatioY
  288. ;set the screen coordinates of the bounding rectangle
  289. Entity.ScreenX := PositionX + (Entity.X * Entity.ScaleX), Entity.ScreenY := PositionY + (Entity.Y * Entity.ScaleY)
  290. Entity.ScreenW := Entity.W * Entity.ScaleX, Entity.ScreenH := Entity.H * Entity.ScaleY
  291. }
  292. ;start each entity in the layer
  293. For Key, Entity In CurrentLayer.Entities ;wip: log(n) occlusion culling here
  294. {
  295. Result := Entity.Start(Delta,CurrentLayer,CurrentViewport) ;step the entity
  296. If Result
  297. Return, Result
  298. }
  299. ;step each entity in the layer
  300. For Key, Entity In CurrentLayer.Entities ;wip: log(n) occlusion culling here
  301. {
  302. Result := Entity.Step(Delta,CurrentLayer,CurrentViewport) ;step the entity
  303. If Result
  304. Return, Result
  305. }
  306. ;end each entity in the layer
  307. For Key, Entity In CurrentLayer.Entities ;wip: log(n) occlusion culling here
  308. {
  309. Result := Entity.End(Delta,CurrentLayer,CurrentViewport) ;step the entity
  310. If Result
  311. Return, Result
  312. }
  313. }
  314. }
  315. Draw(hDC,Layer,Viewport)
  316. {
  317. ;initialize the current viewport
  318. CurrentViewport := Object()
  319. CurrentViewport.ScreenX := this.ScreenX, CurrentViewport.ScreenY := this.ScreenY
  320. CurrentViewport.ScreenW := this.ScreenW, CurrentViewport.ScreenH := this.ScreenH
  321. ;iterate through each layer
  322. For Index, CurrentLayer In this.Layers
  323. {
  324. ;check for layer visibility
  325. If !CurrentLayer.Visible
  326. Continue
  327. ;set up current viewport
  328. CurrentViewport.X := this.X + CurrentLayer.X, CurrentViewport.Y := this.Y + CurrentLayer.Y
  329. CurrentViewport.W := this.W, CurrentViewport.H := this.H
  330. ;iterate through each entity in the layer
  331. For Key, Entity In CurrentLayer.Entities ;wip: log(n) occlusion culling here
  332. Entity.Draw(hDC,CurrentLayer,CurrentViewport) ;draw the entity
  333. }
  334. }
  335. }
  336. class Rectangle extends ProgressEntities.Basis
  337. {
  338. __New()
  339. {
  340. ObjInsert(this,"",Object())
  341. base.__New()
  342. this.Color := 0xFFFFFF
  343. this.Physical := 0
  344. this.hPen := 0
  345. this.hBrush := 0
  346. }
  347. Draw(hDC,Layer,Viewport)
  348. {
  349. ;check for entity visibility
  350. If !this.Visible
  351. Return
  352. ;check for entity moving out of bounds
  353. If (this.ScreenX + this.ScreenW) < 0 || this.ScreenX > (Viewport.ScreenX + Viewport.ScreenW)
  354. || (this.ScreenY + this.ScreenH) < 0 || this.ScreenY > (Viewport.ScreenY + Viewport.ScreenH)
  355. Return
  356. ;update the color if it has changed
  357. If this.ColorModified
  358. {
  359. ;delete the old pen and brush if present
  360. If this.hPen && !DllCall("DeleteObject","UPtr",this.hPen)
  361. throw Exception("Could not delete pen.")
  362. If this.hBrush && !DllCall("DeleteObject","UPtr",this.hBrush)
  363. throw Exception("Could not delete brush.")
  364. ;create the pen and brush
  365. this.hPen := DllCall("CreatePen","Int",0,"Int",0,"UInt",this.Color,"UPtr") ;PS_SOLID
  366. If !this.hPen
  367. throw Exception("Could not create pen.")
  368. this.hBrush := DllCall("CreateSolidBrush","UInt",this.Color,"UPtr")
  369. If !this.hBrush
  370. throw Exception("Could not create brush.")
  371. this.ColorModified := 0 ;reset the color modified flag
  372. }
  373. ;select the pen and brush
  374. hOriginalPen := DllCall("SelectObject","UPtr",hDC,"UPtr",this.hPen,"UPtr")
  375. If !hOriginalPen
  376. throw Exception("Could not select pen into memory device context.")
  377. hOriginalBrush := DllCall("SelectObject","UPtr",hDC,"UPtr",this.hBrush,"UPtr")
  378. If !hOriginalBrush
  379. throw Exception("Could not select brush into memory device context.")
  380. ;draw the rectangle
  381. If !DllCall("Rectangle","UPtr",hDC
  382. ,"Int",Round(this.ScreenX)
  383. ,"Int",Round(this.ScreenY)
  384. ,"Int",Round(this.ScreenX + this.ScreenW)
  385. ,"Int",Round(this.ScreenY + this.ScreenH))
  386. throw Exception("Could not draw rectangle.")
  387. ;deselect the pen and brush
  388. If !DllCall("SelectObject","UPtr",hDC,"UPtr",hOriginalPen,"UPtr")
  389. throw Exception("Could not deselect pen from the memory device context.")
  390. If !DllCall("SelectObject","UPtr",hDC,"UPtr",hOriginalBrush,"UPtr")
  391. throw Exception("Could not deselect brush from the memory device context.")
  392. }
  393. __Get(Key)
  394. {
  395. If (Key != "")
  396. Return, this[""][Key]
  397. }
  398. __Set(Key,Value)
  399. {
  400. If (Key = "Color" && this[Key] != Value)
  401. this.ColorModified := 1
  402. ObjInsert(this[""],Key,Value)
  403. Return, this
  404. }
  405. __Delete()
  406. {
  407. If this.hPen && !DllCall("DeleteObject","UPtr",this.hPen)
  408. throw Exception("Could not delete pen.")
  409. If this.hBrush && !DllCall("DeleteObject","UPtr",this.hBrush)
  410. throw Exception("Could not delete brush.")
  411. }
  412. }
  413. class Image extends ProgressEntities.Basis
  414. {
  415. __New()
  416. {
  417. ObjInsert(this,"",Object())
  418. base.__New()
  419. this.Image := ""
  420. this.TransparentColor := 0xFFFFFF
  421. this.hBitmap := 0
  422. this.ImageModified := 1
  423. this.ImageW := 0
  424. this.ImageH := 0
  425. this.Physical := 0
  426. }
  427. Draw(hDC,Layer,Viewport)
  428. {
  429. ;check for entity visibility
  430. If !this.Visible
  431. Return
  432. ;check for entity moving out of bounds
  433. If (this.ScreenX + this.ScreenW) < 0 || this.ScreenX > (Viewport.ScreenX + Viewport.ScreenW)
  434. || (this.ScreenY + this.ScreenH) < 0 || this.ScreenY > (Viewport.ScreenY + Viewport.ScreenH)
  435. Return
  436. ;update the image if it has changed
  437. If this.ImageModified
  438. {
  439. ;delete the old bitmap if present
  440. If this.hBitmap && !DllCall("DeleteObject","UPtr",this.hBitmap)
  441. throw Exception("Could not delete bitmap.")
  442. ;load the bitmap
  443. this.hBitmap := DllCall("LoadImage","UPtr",0,"Str",this.Image,"UInt",0,"Int",0,"Int",0,"UInt",0x10,"UPtr") ;IMAGE_BITMAP, LR_LOADFROMFILE
  444. If !this.hBitmap
  445. throw Exception("Could not load bitmap.")
  446. ;retrieve the image dimensions
  447. Length := 20 + A_PtrSize, VarSetCapacity(Bitmap,Length)
  448. If !DllCall("GetObject","UPtr",this.hBitmap,"Int",Length,"UPtr",&Bitmap)
  449. throw Exception("Could not retrieve bitmap dimensions.")
  450. this.ImageW := NumGet(Bitmap,4,"Int"), this.ImageH := NumGet(Bitmap,8,"Int")
  451. this.ImageModified := 0 ;reset image modified flag
  452. }
  453. hTempDC := DllCall("CreateCompatibleDC","UPtr",hDC,"UPtr") ;create a temporary device context
  454. hOriginalBitmap := DllCall("SelectObject","UPtr",hTempDC,"UPtr",this.hBitmap,"UPtr") ;select the bitmap
  455. ;draw the image with a color drawn as transparent
  456. If !DllCall("Msimg32\TransparentBlt","UPtr",hDC
  457. ,"Int",Round(this.ScreenX)
  458. ,"Int",Round(this.ScreenY)
  459. ,"Int",Round(this.ScreenW)
  460. ,"Int",Round(this.ScreenH)
  461. ,"UPtr",hTempDC,"Int",0,"Int",0,"Int",this.ImageW,"Int",this.ImageH,"UInt",this.TransparentColor)
  462. throw Exception("Could not draw bitmap.")
  463. If !DllCall("SelectObject","UPtr",hTempDC,"UPtr",hOriginalBitmap,"UPtr") ;deselect the bitmap
  464. throw Exception("Could not deselect pen from the memory device context.")
  465. If !DllCall("DeleteDC","UPtr",hTempDC) ;delete the temporary device context
  466. throw Exception("Could not delete temporary device context.")
  467. }
  468. __Get(Key)
  469. {
  470. If (Key != "")
  471. Return, this[""][Key]
  472. }
  473. __Set(Key,Value)
  474. {
  475. If (Key = "Image" && this[Key] != Value)
  476. this.ImageModified := 1
  477. ObjInsert(this[""],Key,Value)
  478. Return, this
  479. }
  480. __Delete()
  481. {
  482. If this.hPen && !DllCall("DeleteObject","UPtr",this.hBitmap)
  483. throw Exception("Could not delete bitmap.")
  484. }
  485. }
  486. class Text extends ProgressEntities.Basis
  487. {
  488. __New()
  489. {
  490. ObjInsert(this,"",Object())
  491. base.__New()
  492. this.Text := "Text"
  493. this.Typeface := "Verdana"
  494. this.Align := "Center"
  495. this.Weight := 500
  496. this.Italic := 0
  497. this.Underline := 0
  498. this.Strikeout := 0
  499. this.hFont := 0
  500. this.PreviousViewportH := -1
  501. }
  502. Draw(hDC,Layer,Viewport)
  503. {
  504. ;check for entity visibility
  505. If !this.Visible
  506. Return
  507. ;check for entity moving out of bounds
  508. If (this.ScreenX + this.ScreenW) < 0 || (this.ScreenX - this.ScreenW) > (Viewport.ScreenX + Viewport.ScreenW)
  509. || (this.ScreenY + this.ScreenH) < 0 || (this.ScreenY - this.ScreenH) > (Viewport.ScreenY + Viewport.ScreenH)
  510. Return
  511. ;set the text alignment
  512. If (this.Align = "Left")
  513. AlignMode := 24 ;TA_LEFT | TA_BASELINE: align text to the left and the baseline
  514. Else If (this.Align = "Center")
  515. AlignMode := 30 ;TA_CENTER | TA_BASELINE: align text to the center and the baseline
  516. Else If (this.Align = "Right")
  517. AlignMode := 26 ;TA_RIGHT | TA_BASELINE: align text to the right and the baseline
  518. Else
  519. throw Exception("Invalid text alignment: " . this.Align . ".")
  520. DllCall("SetTextAlign","UPtr",hDC,"UInt",AlignMode)
  521. ;update the font if it has changed or if the viewport size has changed
  522. If this.FontModified || this.PreviousViewportH != Viewport.ScreenH
  523. {
  524. this.LineHeight := this.H * (Viewport.ScreenH / 10)
  525. ;delete the old font if present
  526. If this.hFont && !DllCall("DeleteObject","UPtr",this.hFont)
  527. throw Exception("Could not delete font.")
  528. ;create the font
  529. ;wip: doesn't work
  530. ;If this.H Is Not Number
  531. ;throw Exception("Invalid font size: " . this.H . ".")
  532. ;If this.Weight Is Not Integer
  533. ;throw Exception("Invalid font weight: " . this.Weight . ".")
  534. this.hFont := DllCall("CreateFont"
  535. ,"Int",Round(this.LineHeight) ;height
  536. ,"Int",0 ;width
  537. ,"Int",0 ;angle of string (0.1 degrees)
  538. ,"Int",0 ;angle of each character (0.1 degrees)
  539. ,"Int",this.Weight ;font weight
  540. ,"UInt",this.Italic ;font italic
  541. ,"UInt",this.Underline ;font underline
  542. ,"UInt",this.Strikeout ;font strikeout
  543. ,"UInt",1 ;DEFAULT_CHARSET: character set
  544. ,"UInt",0 ;OUT_DEFAULT_PRECIS: output precision
  545. ,"UInt",0 ;CLIP_DEFAULT_PRECIS: clipping precision
  546. ,"UInt",4 ;ANTIALIASED_QUALITY: output quality
  547. ,"UInt",0 ;DEFAULT_PITCH | (FF_DONTCARE << 16): font pitch and family
  548. ,"Str",this.Typeface ;typeface name
  549. ,"UPtr")
  550. If !this.hFont
  551. throw Exception("Could not create font.")
  552. this.FontModified := 0 ;reset font modified flag
  553. this.PreviousViewportH := Viewport.ScreenH ;reset the previous viewport width
  554. }
  555. ;set the text color
  556. If (DllCall("SetTextColor","UPtr",hDC,"UInt",this.Color) = 0xFFFFFFFF) ;CLR_INVALID
  557. throw Exception("Could not set text color.")
  558. ;select the font
  559. hOriginalFont := DllCall("SelectObject","UPtr",hDC,"UPtr",this.hFont,"UPtr")
  560. If !hOriginalFont
  561. throw Exception("Could not select font into memory device context.")
  562. ;draw the text
  563. Text := this.Text ;wip: Loop, Parse, this.Text, `n
  564. PositionY := this.ScreenY
  565. VarSetCapacity(Size,8), Width := 0, Height := 0
  566. Loop, Parse, Text, `n
  567. {
  568. ;draw the current line of text
  569. If !DllCall("TextOut","UPtr",hDC,"Int",Round(this.ScreenX),"Int",Round(PositionY),"Str",A_LoopField,"Int",StrLen(A_LoopField))
  570. throw Exception("Could not draw text.")
  571. PositionY += this.LineHeight ;move down by the height of the line
  572. ;obtain the dimensions of the text
  573. If !DllCall("GetTextExtentPoint32","UPtr",hDC,"Str",Text,"Int",StrLen(Text),"UPtr",&Size)
  574. throw Exception("Could not retrieve text dimensions.")
  575. Temp1 := NumGet(Size,0,"UInt"), (Temp1 > Width) ? (Width := Temp1) : ""
  576. }
  577. ;update entity dimensions
  578. this.W := Width / this.ScaleX
  579. this.ScreenW := Width
  580. this.ScreenH := Height
  581. ;deselect the font
  582. If !DllCall("SelectObject","UPtr",hDC,"UPtr",hOriginalFont,"UPtr")
  583. throw Exception("Could not deselect font from memory device context.")
  584. }
  585. __Get(Key)
  586. {
  587. If (Key != "")
  588. Return, this[""][Key]
  589. }
  590. __Set(Key,Value)
  591. {
  592. If ((Key = "H" || Key = "Weight" || Key = "Italic" || Key = "Underline" || Key = "Strikeout" || Key = "Typeface")
  593. && this[Key] != Value)
  594. ObjInsert(this[""],"FontModified",1)
  595. ObjInsert(this[""],Key,Value)
  596. Return, this
  597. }
  598. __Delete()
  599. {
  600. If this.hFont && !DllCall("DeleteObject","UPtr",this.hFont)
  601. throw Exception("Could not delete font.")
  602. }
  603. }
  604. class StaticRectangle extends ProgressEntities.Rectangle
  605. {
  606. __New()
  607. {
  608. base.__New()
  609. this.SpeedX := 0
  610. this.SpeedY := 0
  611. this.ForceX := 0
  612. this.ForceY := 0
  613. this.Density := 1
  614. this.Restitution := 0.7
  615. this.Friction := 1
  616. this.Physical := 1
  617. this.Dynamic := 0
  618. }
  619. }
  620. class DynamicRectangle extends ProgressEntities.StaticRectangle
  621. {
  622. __New()
  623. {
  624. base.__New()
  625. this.Dynamic := 1
  626. }
  627. Step(Delta,Layer,Viewport)
  628. {
  629. Friction := 0.05
  630. this.X += this.SpeedX * Delta, this.Y -= this.SpeedY * Delta ;process momentum
  631. this.IntersectX := 0, this.IntersectY := 0
  632. For Index, Entity In Layer.Entities ;wip: use spatial acceleration structure
  633. {
  634. If (Entity = this || !Entity.Physical) ;entity is the same as the current entity or is not physical
  635. Continue
  636. If this.Intersect(Entity,IntersectX,IntersectY) ;entity collided with the rectangle
  637. {
  638. Result := this.Collide(Delta,Entity,Layer,IntersectX,IntersectY) ;collision callback
  639. If Result
  640. Return, Result
  641. IntersectX := Abs(IntersectX), IntersectY := Abs(IntersectY)
  642. If (IntersectX >= IntersectY) ;collision along top or bottom side
  643. this.IntersectX += IntersectX, Entity.IntersectX += IntersectX ;wip: this just gets reset back to 0 when the entity is stepped itself
  644. Else
  645. this.IntersectY += IntersectY, Entity.IntersectY += IntersectY
  646. }
  647. }
  648. If this.IntersectX ;handle collision along top or bottom side
  649. this.SpeedX *= (Friction * this.IntersectX) ** Delta
  650. If this.IntersectY ;handle collision along left or right side
  651. this.SpeedY *= (Friction * this.IntersectY) ** Delta
  652. }
  653. Collide(Delta,Entity,Layer,IntersectX,IntersectY)
  654. {
  655. Restitution := this.Restitution * Entity.Restitution
  656. CurrentMass := this.W * this.H * this.Density
  657. EntityMass := Entity.W * Entity.H * Entity.Density
  658. If (Abs(IntersectX) >= Abs(IntersectY)) ;collision along top or bottom side
  659. {
  660. If Entity.Dynamic
  661. {
  662. Velocity := ((this.SpeedY - Entity.SpeedY) * Restitution) / ((1 / CurrentMass) + (1 / EntityMass))
  663. this.SpeedY := -Velocity / CurrentMass
  664. Entity.SpeedY := Velocity / EntityMass
  665. }
  666. Else
  667. this.SpeedY := -(this.SpeedY - Entity.SpeedY) * Restitution
  668. this.Y -= IntersectY ;move the entity out of the intersection area
  669. }
  670. Else ;collision along left or right side
  671. {
  672. If Entity.Dynamic
  673. {
  674. Velocity := ((this.SpeedX - Entity.SpeedX) * Restitution) / ((1 / CurrentMass) + (1 / EntityMass))
  675. this.SpeedX := -Velocity / CurrentMass
  676. Entity.SpeedX := Velocity / EntityMass
  677. }
  678. Else
  679. this.SpeedX := -(this.SpeedX - Entity.SpeedX) * Restitution
  680. this.X -= IntersectX ;move the entity out of the intersection area
  681. }
  682. }
  683. }
  684. }