1 /*
2  *  Copyright (C) 2001-2013  The Exult Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
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 General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include <iostream>
24 #include <cstring>
25 
26 #include "SDL_events.h"
27 
28 #include "Gump_manager.h"
29 #include "Configuration.h"
30 #include "Gump_button.h"
31 #include "Gump_ToggleButton.h"
32 #include "GameplayOptions_gump.h"
33 #include "exult.h"
34 #include "exult_flx.h"
35 #include "game.h"
36 #include "gamewin.h"
37 #include "mouse.h"
38 #include "cheat.h"
39 #include "Face_stats.h"
40 #include "Text_button.h"
41 #include "Enabled_button.h"
42 #include "font.h"
43 #include "array_size.h"
44 
45 using std::string;
46 
47 static const int rowy[] = { 4, 16, 124, 28, 40, 52, 64, 76, 88, 100, 112, 148, 136 };
48 
49 static const int colx[] = { 35, 50, 120, 195, 192 };
50 
51 static const char *oktext = "OK";
52 static const char *canceltext = "CANCEL";
53 
54 static int framerates[] = { 2, 4, 6, 8, 10, -1 };
55 // -1 is placeholder for custom framerate
56 static const int num_default_rates = array_size(framerates) - 1;
57 
58 
framestring(int fr)59 static string framestring(int fr) {
60 	char buf[100];
61 	sprintf(buf, "%i fps", fr);
62 	return buf;
63 }
64 
65 static const char *pathfind_texts[3] = {"no", "single", "double"};
66 
67 using GameplayOptions_button = CallbackTextButton<GameplayOptions_gump>;
68 using GameplayTextToggle = CallbackToggleTextButton<GameplayOptions_gump>;
69 using GameplayEnabledToggle = CallbackEnabledButton<GameplayOptions_gump>;
70 
close()71 void GameplayOptions_gump::close() {
72 	save_settings();
73 	done = true;
74 }
75 
cancel()76 void GameplayOptions_gump::cancel() {
77 	done = true;
78 }
79 
build_buttons()80 void GameplayOptions_gump::build_buttons() {
81 	std::vector<std::string> stats = {"Disabled", "Left", "Middle", "Right"};
82 	buttons[id_facestats] = std::make_unique<GameplayTextToggle>(this, &GameplayOptions_gump::toggle_facestats,
83 	        std::move(stats), facestats, colx[3], rowy[0], 59);
84 
85 	std::vector<std::string> textbgcolor = {
86 		"Disabled",
87 		"Solid Light Gray", "Dark Purple", "Bright Yellow", "Light Blue",
88 		"Dark Green", "Dark Red", "Purple", "Orange", "Light Gray", "Green",
89 		"Yellow", "Pale Blue", "Dark Green", "Red", "Bright White",
90 		"Dark gray", "White"
91 	};
92 	buttons[id_text_bg] = std::make_unique<GameplayTextToggle>(this, &GameplayOptions_gump::toggle_text_bg,
93 	        std::move(textbgcolor), text_bg, colx[3] - 41, rowy[1], 100);
94 	if (sman->can_use_paperdolls() && (GAME_BG ||
95 	                                   Game::get_game_type() == EXULT_DEVEL_GAME))
96 		buttons[id_paperdolls] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_paperdolls,
97 		        paperdolls, colx[3], rowy[12], 59);
98 	buttons[id_fastmouse] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_fastmouse,
99 	        fastmouse, colx[3], rowy[3], 59);
100 	buttons[id_mouse3rd] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_mouse3rd,
101 	        mouse3rd, colx[3], rowy[4], 59);
102 	buttons[id_doubleclick] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_doubleclick,
103 	        doubleclick, colx[3], rowy[5], 59);
104 	buttons[id_rightclick_close] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_rightclick_close,
105 	        rightclick_close, colx[3], rowy[6], 59);
106 
107 	std::vector<std::string> pathfind_text = {"Disabled", "Single", "Double"};
108 	buttons[id_right_pathfind] = std::make_unique<GameplayTextToggle>(this, &GameplayOptions_gump::toggle_right_pathfind,
109 	        std::move(pathfind_text), right_pathfind, colx[3], rowy[7], 59);
110 	buttons[id_gumps_pause] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_gumps_pause,
111 	        gumps_pause, colx[3], rowy[8], 59);
112 	buttons[id_cheats] = std::make_unique<GameplayEnabledToggle>(this, &GameplayOptions_gump::toggle_cheats,
113 	        cheats, colx[3], rowy[9], 59);
114 	buttons[id_frames] = std::make_unique<GameplayTextToggle>(this, &GameplayOptions_gump::toggle_frames,
115 	        frametext, frames, colx[3], rowy[10], 59);
116 
117 	std::vector<std::string> smooth_text = {"Disabled", "25%", "50%", "75%", "100%"};
118 	buttons[id_smooth_scrolling] = std::make_unique<GameplayTextToggle>(this, &GameplayOptions_gump::toggle_smooth_scrolling,
119 	        std::move(smooth_text), smooth_scrolling, colx[3], rowy[2], 59);
120 }
121 
load_settings()122 void GameplayOptions_gump::load_settings() {
123 	fastmouse = gwin->get_fastmouse(true);
124 	mouse3rd = gwin->get_mouse3rd();
125 	cheats = cheat();
126 	if (gwin->is_in_exult_menu()) {
127 		config->value("config/gameplay/facestats", facestats, -1);
128 		facestats += 1;
129 	} else
130 		facestats = Face_stats::get_state() + 1;
131 	doubleclick = 0;
132 	paperdolls = false;
133 	string pdolls;
134 	paperdolls = sman->are_paperdolls_enabled();
135 	doubleclick = gwin->get_double_click_closes_gumps();
136 	rightclick_close = gumpman->can_right_click_close();
137 	right_pathfind = gwin->get_allow_right_pathfind();
138 	text_bg = gwin->get_text_bg() + 1;
139 	gumps_pause = !gumpman->gumps_dont_pause_game();
140 	int realframes = 1000 / gwin->get_std_delay();
141 
142 	frames = -1;
143 	framerates[num_default_rates] = realframes;
144 	for (int i = 0; i < num_default_rates; i++) {
145 		if (realframes == framerates[i]) {
146 			frames = i;
147 			break;
148 		}
149 	}
150 
151 	int num_framerates = num_default_rates;
152 	if (frames == -1) {
153 		num_framerates++;
154 		frames = num_default_rates;
155 	}
156 	frametext.clear();
157 	for (int i = 0; i < num_framerates; i++) {
158 		frametext.emplace_back(framestring(framerates[i]));
159 	}
160 	smooth_scrolling = gwin->is_lerping_enabled() / 25;
161 }
162 
GameplayOptions_gump()163 GameplayOptions_gump::GameplayOptions_gump() : Modal_gump(nullptr, EXULT_FLX_GAMEPLAYOPTIONS_SHP, SF_EXULT_FLX) {
164 	set_object_area(TileRect(0, 0, 0, 0), 8, 162);//++++++ ???
165 
166 	for (auto& btn : buttons)
167 		btn.reset();
168 
169 	load_settings();
170 
171 	build_buttons();
172 
173 	// Ok
174 	buttons[id_ok] = std::make_unique<GameplayOptions_button>(this, &GameplayOptions_gump::close,
175 	        oktext, colx[0], rowy[11]);
176 	// Cancel
177 	buttons[id_cancel] = std::make_unique<GameplayOptions_button>(this, &GameplayOptions_gump::cancel,
178 	        canceltext, colx[4], rowy[11]);
179 }
180 
save_settings()181 void GameplayOptions_gump::save_settings() {
182 	gwin->set_text_bg(text_bg - 1);
183 	config->set("config/gameplay/textbackground", text_bg - 1, false);
184 	int fps = framerates[frames];
185 	gwin->set_std_delay(1000 / fps);
186 	config->set("config/video/fps", fps, false);
187 	gwin->set_fastmouse(fastmouse != 0);
188 	config->set("config/gameplay/fastmouse", fastmouse ? "yes" : "no", false);
189 	gwin->set_mouse3rd(mouse3rd != 0);
190 	config->set("config/gameplay/mouse3rd", mouse3rd ? "yes" : "no", false);
191 	gwin->set_double_click_closes_gumps(doubleclick != 0);
192 	config->set("config/gameplay/double_click_closes_gumps",
193 	            doubleclick ? "yes" : "no", false);
194 	gumpman->set_right_click_close(rightclick_close != 0);
195 	config->set("config/gameplay/right_click_closes_gumps",
196 	            rightclick_close ? "yes" : "no" , false);
197 	cheat.set_enabled(cheats != 0);
198 	if (gwin->is_in_exult_menu())
199 		config->set("config/gameplay/facestats", facestats - 1 , false);
200 	else {
201 		while (facestats != Face_stats::get_state() + 1)
202 			Face_stats::AdvanceState();
203 		Face_stats::save_config(config);
204 	}
205 	if (sman->can_use_paperdolls() && (GAME_BG ||
206 	                                   Game::get_game_type() == EXULT_DEVEL_GAME)) {
207 		sman->set_paperdoll_status(paperdolls != 0);
208 		config->set("config/gameplay/bg_paperdolls",
209 		            paperdolls ? "yes" : "no", false);
210 	}
211 
212 	gwin->set_allow_right_pathfind(right_pathfind);
213 	config->set("config/gameplay/allow_right_pathfind", pathfind_texts[right_pathfind], false);
214 
215 	gumpman->set_gumps_dont_pause_game(!gumps_pause);
216 	config->set("config/gameplay/gumps_dont_pause_game", gumps_pause ? "no" : "yes", false);
217 
218 	if (smooth_scrolling < 0) smooth_scrolling = 0;
219 	else if (smooth_scrolling > 4) smooth_scrolling = 4;
220 	gwin->set_lerping_enabled(smooth_scrolling * 25);
221 	config->set("config/gameplay/smooth_scrolling", smooth_scrolling * 25, false);
222 	config->write_back();
223 }
224 
paint()225 void GameplayOptions_gump::paint() {
226 	Gump::paint();
227 	for (auto& btn : buttons)
228 		if (btn)
229 			btn->paint();
230 
231 	Font *font = fontManager.get_font("SMALL_BLACK_FONT");
232 	Image_window8 *iwin = gwin->get_win();
233 
234 	font->paint_text(iwin->get_ib8(), "Status Bars:", x + colx[0], y + rowy[0] + 1);
235 	font->paint_text(iwin->get_ib8(), "Text Background:", x + colx[0], y + rowy[1] + 1);
236 	if (buttons[id_paperdolls])
237 		font->paint_text(iwin->get_ib8(), "Paperdolls:", x + colx[0], y + rowy[12] + 1);
238 #ifndef __IPHONEOS__
239 	font->paint_text(iwin->get_ib8(), "Fullscreen Fast Mouse:", x + colx[0], y + rowy[3] + 1);
240 	font->paint_text(iwin->get_ib8(), "Use Middle Mouse Button:", x + colx[0], y + rowy[4] + 1);
241 #endif
242 	font->paint_text(iwin->get_ib8(), "Doubleclick closes Gumps:", x + colx[0], y + rowy[5] + 1);
243 	font->paint_text(iwin->get_ib8(), "Right click closes Gumps:", x + colx[0], y + rowy[6] + 1);
244 	font->paint_text(iwin->get_ib8(), "Right click Pathfinds:", x + colx[0], y + rowy[7] + 1);
245 	font->paint_text(iwin->get_ib8(), "Gumps pause game:", x + colx[0], y + rowy[8] + 1);
246 	font->paint_text(iwin->get_ib8(), "Cheats:", x + colx[0], y + rowy[9] + 1);
247 	font->paint_text(iwin->get_ib8(), "Speed:", x + colx[0], y + rowy[10] + 1);
248 	font->paint_text(iwin->get_ib8(), "Smooth scrolling:", x + colx[0], y + rowy[2] + 1);
249 	gwin->set_painted();
250 }
251 
mouse_down(int mx,int my,int button)252 bool GameplayOptions_gump::mouse_down(int mx, int my, int button) {
253 	// Only left and right buttons
254 	if (button != 1 && button != 3) return false;
255 
256 	// We'll eat the mouse down if we've already got a button down
257 	if (pushed) return true;
258 
259 	// First try checkmark
260 	pushed = Gump::on_button(mx, my);
261 
262 	// Try buttons at bottom.
263 	if (!pushed) {
264 		for (auto& btn : buttons) {
265 			if (btn && btn->on_button(mx, my)) {
266 				pushed = btn.get();
267 				break;
268 			}
269 		}
270 	}
271 
272 	if (pushed && !pushed->push(button))            // On a button?
273 		pushed = nullptr;
274 
275 	return button == 1 || pushed != nullptr;
276 }
277 
mouse_up(int mx,int my,int button)278 bool GameplayOptions_gump::mouse_up(int mx, int my, int button) {
279 	// Not Pushing a button?
280 	if (!pushed) return false;
281 
282 	if (pushed->get_pushed() != button) return button == 1;
283 
284 	bool res = false;
285 	pushed->unpush(button);
286 	if (pushed->on_button(mx, my))
287 		res = pushed->activate(button);
288 	pushed = nullptr;
289 	return res;
290 }
291