dialogbox.d 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. module dialogs.dialogbox;
  2. import raylib;
  3. import std.string;
  4. import std.stdio;
  5. import std.conv;
  6. import std.uni;
  7. import std.typecons;
  8. import std.algorithm;
  9. import variables;
  10. int currentPage = 0;
  11. float textDisplayProgress = 0.0f;
  12. bool textFullyDisplayed = false;
  13. // Texture variables
  14. bool texturesLoaded = false;
  15. float circleRotationAngle = 0.0f;
  16. // Border sizes for 9-slice scaling
  17. const int DIALOG_BORDER = 32; // Border size that won't be stretched
  18. const int CHOICE_BORDER = 32; // Border size for choice window
  19. void draw9SliceTexture(Texture2D tex, Rectangle dest, int borderSize, Color tint) {
  20. // Source rectangle is the whole texture
  21. Rectangle src = Rectangle(0, 0, tex.width, tex.height);
  22. // Prevent division by zero
  23. if (borderSize <= 0) borderSize = 1;
  24. if (borderSize * 2 >= src.width) borderSize = cast(int)src.width / 3;
  25. if (borderSize * 2 >= src.height) borderSize = cast(int)src.height / 3;
  26. // Calculate inner source rectangle (without borders)
  27. Rectangle innerSrc = Rectangle(
  28. borderSize,
  29. borderSize,
  30. src.width - borderSize * 2,
  31. src.height - borderSize * 2
  32. );
  33. // Calculate inner destination rectangle
  34. Rectangle innerDest = Rectangle(
  35. dest.x + borderSize,
  36. dest.y + borderSize,
  37. dest.width - borderSize * 2,
  38. dest.height - borderSize * 2
  39. );
  40. // Draw 9 parts:
  41. // 1. Top-left corner
  42. DrawTexturePro(tex,
  43. Rectangle(src.x, src.y, borderSize, borderSize),
  44. Rectangle(dest.x, dest.y, borderSize, borderSize),
  45. Vector2(0, 0), 0, tint);
  46. // 2. Top edge
  47. DrawTexturePro(tex,
  48. Rectangle(src.x + borderSize, src.y, innerSrc.width, borderSize),
  49. Rectangle(dest.x + borderSize, dest.y, innerDest.width, borderSize),
  50. Vector2(0, 0), 0, tint);
  51. // 3. Top-right corner
  52. DrawTexturePro(tex,
  53. Rectangle(src.x + src.width - borderSize, src.y, borderSize, borderSize),
  54. Rectangle(dest.x + dest.width - borderSize, dest.y, borderSize, borderSize),
  55. Vector2(0, 0), 0, tint);
  56. // 4. Left edge
  57. DrawTexturePro(tex,
  58. Rectangle(src.x, src.y + borderSize, borderSize, innerSrc.height),
  59. Rectangle(dest.x, dest.y + borderSize, borderSize, innerDest.height),
  60. Vector2(0, 0), 0, tint);
  61. // 5. Center (stretched)
  62. DrawTexturePro(tex,
  63. innerSrc,
  64. innerDest,
  65. Vector2(0, 0), 0, tint);
  66. // 6. Right edge
  67. DrawTexturePro(tex,
  68. Rectangle(src.x + src.width - borderSize, src.y + borderSize, borderSize, innerSrc.height),
  69. Rectangle(dest.x + dest.width - borderSize, dest.y + borderSize, borderSize, innerDest.height),
  70. Vector2(0, 0), 0, tint);
  71. // 7. Bottom-left corner
  72. DrawTexturePro(tex,
  73. Rectangle(src.x, src.y + src.height - borderSize, borderSize, borderSize),
  74. Rectangle(dest.x, dest.y + dest.height - borderSize, borderSize, borderSize),
  75. Vector2(0, 0), 0, tint);
  76. // 8. Bottom edge
  77. DrawTexturePro(tex,
  78. Rectangle(src.x + borderSize, src.y + src.height - borderSize, innerSrc.width, borderSize),
  79. Rectangle(dest.x + borderSize, dest.y + dest.height - borderSize, innerDest.width, borderSize),
  80. Vector2(0, 0), 0, tint);
  81. // 9. Bottom-right corner
  82. DrawTexturePro(tex,
  83. Rectangle(src.x + src.width - borderSize, src.y + src.height - borderSize, borderSize, borderSize),
  84. Rectangle(dest.x + dest.width - borderSize, dest.y + dest.height - borderSize, borderSize, borderSize),
  85. Vector2(0, 0), 0, tint);
  86. }
  87. void displayDialog(string[] pages, string[] choices, ref int selectedChoice, int choicePage, Font dialogFont,
  88. bool *showDialog, ref float textSpeed, Color dialogColor,
  89. Texture2D circle, Texture2D dialogBackgroundTex, Texture2D choiceWindowTex) {
  90. int pagesLength = cast(int)pages.length;
  91. int screenWidth = GetScreenWidth();
  92. int screenHeight = GetScreenHeight();
  93. const int screenPadding = 10;
  94. // Dialog background rectangle с отступами
  95. Rectangle dialogRect = Rectangle(
  96. screenPadding, // X с отступом
  97. screenHeight - screenHeight / 3 - screenPadding, // Y с отступом
  98. screenWidth - 2 * screenPadding, // Ширина с учетом отступов
  99. screenHeight / 3 // Высота (без изменения)
  100. );
  101. // Draw dialog background with 9-slice scaling
  102. draw9SliceTexture(
  103. dialogBackgroundTex,
  104. dialogRect,
  105. DIALOG_BORDER,
  106. Color(255, 255, 255, 220) // Slightly transparent
  107. );
  108. const float textLeftMargin = 33.0f;
  109. const float textTopMargin = 20.0f;
  110. float marginLeft = dialogRect.x + textLeftMargin; // Отступ от левого края фона
  111. float marginRight = screenWidth/6.5f;
  112. float marginTop = dialogRect.y + textTopMargin; // Отступ от верхнего края фона
  113. float textWidth = dialogRect.width - textLeftMargin - marginRight;
  114. float fontSize = 40.0f;
  115. float spacing = 1.0f;
  116. string currentText = pages[currentPage];
  117. int textLength = cast(int)currentText.length;
  118. if (IsKeyPressed(KeyboardKey.KEY_ENTER) && !textFullyDisplayed) {
  119. textDisplayProgress = textLength;
  120. textFullyDisplayed = true;
  121. }
  122. else if (IsKeyPressed(KeyboardKey.KEY_ENTER) && textFullyDisplayed) {
  123. currentPage += 1;
  124. textDisplayProgress = 0.0f;
  125. textFullyDisplayed = false;
  126. }
  127. else if (!textFullyDisplayed) {
  128. textDisplayProgress += textSpeed;
  129. if (textDisplayProgress >= textLength) {
  130. textDisplayProgress = textLength;
  131. textFullyDisplayed = true;
  132. }
  133. }
  134. int charsToShow = cast(int)textDisplayProgress;
  135. string displayedText = currentText[0 .. min(charsToShow, textLength)];
  136. string[] lines;
  137. string remainingText = displayedText;
  138. while (remainingText.length > 0) {
  139. int fitChars = 0;
  140. float width = 0.0f;
  141. while (fitChars < remainingText.length) {
  142. int nextChar = fitChars;
  143. while (nextChar < remainingText.length && !isWhite(remainingText[nextChar])) {
  144. nextChar++;
  145. }
  146. string word = remainingText[fitChars..nextChar];
  147. float wordWidth = MeasureTextEx(dialogFont, word.toStringz(), fontSize, spacing).x;
  148. if (width + wordWidth > textWidth && width > 0) {
  149. break;
  150. }
  151. width += wordWidth;
  152. fitChars = nextChar;
  153. while (fitChars < remainingText.length && isWhite(remainingText[fitChars])) {
  154. width += MeasureTextEx(dialogFont, " ".toStringz(), fontSize, spacing).x;
  155. fitChars++;
  156. }
  157. }
  158. if (fitChars == 0) {
  159. fitChars = 1;
  160. }
  161. lines ~= remainingText[0..fitChars];
  162. remainingText = remainingText[fitChars..$];
  163. }
  164. float lineHeight = MeasureTextEx(dialogFont, "A", fontSize, spacing).y * 1.4;
  165. for (int i = 0; i < lines.length; i++) {
  166. DrawTextEx(
  167. dialogFont,
  168. lines[i].toStringz(),
  169. Vector2(marginLeft+3, 3+(marginTop + i * lineHeight)),
  170. fontSize,
  171. spacing,
  172. Colors.BLACK
  173. );
  174. DrawTextEx(
  175. dialogFont,
  176. lines[i].toStringz(),
  177. Vector2(marginLeft, marginTop + i * lineHeight),
  178. fontSize,
  179. spacing,
  180. Colors.WHITE
  181. );
  182. }
  183. if (textFullyDisplayed && lines.length > 0) {
  184. // Обновляем угол вращения (например, 45 градусов в секунду)
  185. circleRotationAngle += 45.0f * GetFrameTime();
  186. if (circleRotationAngle >= 360.0f) {
  187. circleRotationAngle -= 360.0f;
  188. }
  189. // Получаем позицию и размер последней строки текста
  190. float lastLineWidth = 10 + MeasureTextEx(dialogFont, lines[$-1].toStringz(), fontSize, spacing).x;
  191. float lastLineY = marginTop + 20 + (lines.length - 1) * lineHeight;
  192. // Позиция круга справа от последней строки
  193. float circleX = marginLeft + lastLineWidth + 10; // 10 - отступ от текста
  194. float circleY = lastLineY;
  195. // Размер круга (можете настроить)
  196. float circleSize = 30.0f;
  197. Rectangle destRect = Rectangle(circleX, circleY, circleSize, circleSize);
  198. Rectangle sourceRect = Rectangle(0, 0, circle.width, circle.height);
  199. Vector2 origin = Vector2(circleSize/2, circleSize/2);
  200. DrawTexturePro(
  201. circle,
  202. sourceRect,
  203. destRect,
  204. origin,
  205. circleRotationAngle,
  206. Colors.WHITE
  207. );
  208. }
  209. if (currentPage == choicePage) {
  210. int verticalPadding = 30;
  211. int choiceHeight = 50;
  212. int choiceSpacing = 10;
  213. int choiceWindowWidth = screenWidth / 2;
  214. int choiceWindowHeight = cast(int)(verticalPadding * 2 + choices.length * choiceHeight + (choices.length - 1) * choiceSpacing);
  215. int choiceWindowX = (screenWidth - choiceWindowWidth) / 2;
  216. int choiceWindowY = (screenHeight - choiceWindowHeight) / 2;
  217. // Draw choice window with 9-slice scaling
  218. draw9SliceTexture(
  219. choiceWindowTex,
  220. Rectangle(choiceWindowX, choiceWindowY, choiceWindowWidth, choiceWindowHeight),
  221. CHOICE_BORDER,
  222. Colors.WHITE
  223. );
  224. Vector2 mousePos = GetMousePosition();
  225. bool mouseClicked = IsMouseButtonPressed(MouseButton.MOUSE_BUTTON_LEFT);
  226. for (int i = 0; i < choices.length; i++) {
  227. Rectangle choiceRect = Rectangle(
  228. choiceWindowX + 40,
  229. choiceWindowY + verticalPadding + i * (choiceHeight + choiceSpacing),
  230. choiceWindowWidth - 80,
  231. choiceHeight
  232. );
  233. bool isHovered = CheckCollisionPointRec(mousePos, choiceRect);
  234. if (isHovered) {
  235. selectedChoice = i;
  236. if (mouseClicked) {
  237. currentPage += 1;
  238. textDisplayProgress = 0.0f;
  239. textFullyDisplayed = false;
  240. }
  241. }
  242. Color color = (i == selectedChoice) ? Colors.YELLOW : (isHovered ? Colors.LIGHTGRAY : Colors.WHITE);
  243. if (isHovered || i == selectedChoice) {
  244. DrawRectangleRec(choiceRect, Color(60, 60, 60, 200));
  245. }
  246. Vector2 textSize = MeasureTextEx(dialogFont, choices[i].toStringz(), 39, spacing);
  247. Vector2 textPos = Vector2(
  248. choiceRect.x + (choiceRect.width - textSize.x) / 2,
  249. choiceRect.y + (choiceRect.height - textSize.y) / 2
  250. );
  251. DrawTextEx(
  252. dialogFont,
  253. choices[i].toStringz(),
  254. Vector2(textPos.x+3, textPos.y+3),
  255. 39,
  256. spacing,
  257. Colors.BLACK
  258. );
  259. DrawTextEx(
  260. dialogFont,
  261. choices[i].toStringz(),
  262. textPos,
  263. 39,
  264. spacing,
  265. color
  266. );
  267. }
  268. if (IsKeyPressed(KeyboardKey.KEY_DOWN)) {
  269. selectedChoice = cast(int)((selectedChoice + 1) % choices.length);
  270. }
  271. if (IsKeyPressed(KeyboardKey.KEY_UP)) {
  272. selectedChoice = cast(int)((selectedChoice - 1 + choices.length) % choices.length);
  273. }
  274. if (IsMouseButtonPressed(MouseButton.MOUSE_BUTTON_LEFT)) {
  275. currentPage += 1;
  276. textDisplayProgress = 0.0f;
  277. textFullyDisplayed = false;
  278. }
  279. }
  280. if ((IsKeyDown(KeyboardKey.KEY_LEFT_CONTROL) || IsKeyDown(KeyboardKey.KEY_RIGHT_CONTROL))
  281. && currentPage != choicePage && currentPage < pages.length) {
  282. currentPage += 1;
  283. }
  284. if (currentPage >= pagesLength) {
  285. currentPage = 0;
  286. textDisplayProgress = 0.0f;
  287. textFullyDisplayed = false;
  288. pages = [];
  289. textSpeed = 0.6f;
  290. *showDialog = false;
  291. return;
  292. }
  293. }