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