1 /**
2 * Copyright © Novelate 2020
3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 * Website: https://novelate.com/
6 * ------
7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language.
8 * It can be used freely for both personal and commercial projects.
9 * ------
10 * Module Description:
11 * This is the module for displaying the main menu.
12 */
13 module novelate.mainmenu;
14 
15 import std.conv : to;
16 
17 import dsfml.window : Keyboard, Mouse;
18 import dsfml.system : Vector2f;
19 
20 import novelate.config;
21 import novelate.imagecomponent;
22 import novelate.animatedimage;
23 import novelate.media;
24 import novelate.label;
25 import novelate.colormanager;
26 import novelate.layer;
27 
28 import novelate.core : getLayer, LayerType, changeScreen, Screen;
29 import novelate.state :   exitGame, playScene;
30 
31 public alias MouseButton = Mouse.Button;
32 public alias Key = Keyboard.Key;
33 
34 package(novelate):
35 /// Shows the main menu.
36 void showMainMenu()
37 {
38   auto backgroundLayer = getLayer(LayerType.background);
39 
40   {
41     auto oldBackground = cast(ImageComponent)backgroundLayer.getComponent("background");
42 
43     if (oldBackground)
44     {
45       backgroundLayer.addComponent(oldBackground, "background_old");
46       backgroundLayer.removeComponent("background");
47       oldBackground.fadeOut(20);
48     }
49   }
50 
51   {
52     auto oldBackground = cast(AnimatedImage)backgroundLayer.getComponent("background");
53 
54     if (oldBackground)
55     {
56       backgroundLayer.addComponent(oldBackground, "background_old");
57       backgroundLayer.removeComponent("background");
58       oldBackground.fadeOut(20);
59     }
60   }
61 
62   auto backgroundFrames = config.menuBackground;
63 
64   if (backgroundFrames && backgroundFrames.frames && backgroundFrames.frames.length)
65   {
66     string[] backgroundImages = [];
67 
68     auto frameSpeed = backgroundFrames.frames[0].nextFrameTime;
69 
70     foreach (frame; backgroundFrames.frames)
71     {
72       backgroundImages ~= getMediaFile(frame.image).relativePath(backgroundLayer.width);
73     }
74 
75     if (backgroundImages && backgroundImages.length)
76     {
77       auto image = new AnimatedImage(backgroundImages);
78       image.animationSpeed = frameSpeed;
79       image.fadeIn(20);
80       image.fadedIn = ()
81       {
82         backgroundLayer.removeComponent("background_old");
83       };
84       image.fullScreen = true;
85       image.refresh(backgroundLayer.width, backgroundLayer.height);
86 
87       backgroundLayer.addComponent(image, "background");
88     }
89   }
90 
91   auto frontLayer = getLayer(LayerType.front);
92 
93   auto logoFrames = config.menuLogoImage;
94 
95   if (logoFrames && logoFrames.frames && logoFrames.frames.length)
96   {
97     string[] logoImages = [];
98 
99     auto frameSpeed = logoFrames.frames[0].nextFrameTime;
100 
101     foreach (frame; logoFrames.frames)
102     {
103       logoImages ~= getMediaFile(frame.image).relativePath(frontLayer.width);
104     }
105 
106     if (logoImages && logoImages.length)
107     {
108       auto image = new AnimatedImage(logoImages);
109       image.animationSpeed = frameSpeed;
110       image.fadeIn(20);
111       image.position = Vector2f(config.menuLogoImageX800, config.menuLogoImageY800);
112 
113       frontLayer.addComponent(image, "logo");
114     }
115   }
116 
117   AltValue!(ptrdiff_t, string) menuBoxOffsetX;
118   AltValue!(ptrdiff_t, string) menuBoxOffsetY;
119 
120   if (backgroundLayer.width == 800)
121   {
122     menuBoxOffsetX = config.menuBox_X800;
123     menuBoxOffsetY = config.menuBox_Y800;
124   }
125   else if (backgroundLayer.width == 1024)
126   {
127     menuBoxOffsetX = config.menuBox_X1024;
128     menuBoxOffsetY = config.menuBox_Y1024;
129   }
130   else if (backgroundLayer.width == 1280)
131   {
132     menuBoxOffsetX = config.menuBox_X1280;
133     menuBoxOffsetY = config.menuBox_Y1280;
134   }
135 
136   ptrdiff_t offsetX = 0;
137   ptrdiff_t offsetY = 0;
138 
139   if (menuBoxOffsetX.value2 && menuBoxOffsetX.value2.length)
140   {
141     switch (menuBoxOffsetX.value2)
142     {
143       case "C":
144       case "Center":
145         offsetX = (backgroundLayer.width / 2);
146         break;
147 
148       default: break;
149     }
150   }
151   else
152   {
153     offsetX = menuBoxOffsetX.value1;
154   }
155 
156   Label[] labels = [];
157 
158   labels ~= createLabel(config.menuItem_Play.text, (b, ref s)
159   {
160     changeScreen(Screen.scene, [playScene]);
161   }, offsetX);
162 
163   labels ~= createLabel(config.menuItem_Load.text, (b, ref s)
164   {
165     changeScreen(Screen.load);
166   }, offsetX);
167 
168   labels ~= createLabel(config.menuItem_Save.text, (b, ref s)
169   {
170     changeScreen(Screen.save);
171   }, offsetX);
172 
173   labels ~= createLabel(config.menuItem_About.text, (b, ref s)
174   {
175     changeScreen(Screen.about);
176   }, offsetX);
177 
178   labels ~= createLabel(config.menuItem_Characters.text, (b, ref s)
179   {
180     changeScreen(Screen.characters);
181   }, offsetX);
182 
183   labels ~= createLabel(config.menuItem_Exit.text, (b, ref s)
184   {
185     exitGame = true;
186   }, offsetX);
187 
188   if (menuBoxOffsetY.value2 && menuBoxOffsetY.value2.length)
189   {
190     size_t totalHeight = 0;
191 
192     foreach (label; labels)
193     {
194       totalHeight += label.height;
195       totalHeight += cast(size_t)(cast(double)label.fontSize * 0.5);
196     }
197 
198     switch (menuBoxOffsetY.value2)
199     {
200       case "B":
201       case "Bottom":
202         totalHeight += cast(size_t)(cast(double)labels[0].fontSize * 1.5);
203 
204         offsetY = (backgroundLayer.height - totalHeight);
205         break;
206       case "T":
207       case "Top":
208         offsetY = cast(ptrdiff_t)(cast(double)labels[0].fontSize * 1.5);
209         break;
210 
211       case "C":
212       case "Center":
213         offsetY = cast(ptrdiff_t)((backgroundLayer.height / 2) - (totalHeight / 2));
214         break;
215 
216       default: break;
217     }
218   }
219   else
220   {
221     offsetY = menuBoxOffsetY.value1;
222   }
223 
224   foreach (label; labels)
225   {
226     label.position = Vector2f(label.x, offsetY);
227 
228     offsetY += label.height;
229     offsetY += cast(size_t)(cast(double)label.fontSize * 0.5);
230     getLayer(LayerType.dialogueBoxInteraction)
231       .addComponent(label, to!string(label.text));
232   }
233 
234 }
235 
236 /**
237 * Creates a label for the menu.
238 * Params:
239 *   text = The text of the label.
240 *   mouseRelease = Handler for when the label is pressed.
241 *   offsetX = The x offset of the label.
242 */
243 private Label createLabel(string text, void delegate(MouseButton button, ref bool stopEvent) mouseRelease, ptrdiff_t offsetX)
244 {
245   auto label = new Label;
246   label.color = colorFromString(config.defaultDialogueColor);
247   label.fontSize = 48;
248   if (config.defaultFont && config.defaultFont.length)
249   {
250     label.fontName = config.defaultFont;
251   }
252   label.text = to!dstring(text);
253   label.position = Vector2f(offsetX - (label.width / 2), 50000);
254   label.mouseRelease = mouseRelease;
255 
256   return label;
257 }