1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/keyboard.h"
29 #include "engines/icb/mouse.h"
30 #include "engines/icb/debug.h"
31 #include "engines/icb/res_man.h"
32 #include "engines/icb/options_manager_pc.h"
33 #include "engines/icb/p4.h"
34 #include "engines/icb/global_vars.h"
35 #include "engines/icb/common/px_common.h"
36 #include "engines/icb/common/px_bitmap.h"
37 #include "engines/icb/stage_draw.h"
38 #include "engines/icb/mission.h"
39 #include "engines/icb/cluster_manager_pc.h"
40 #include "engines/icb/configfile.h"
41 
42 #include "common/str.h"
43 #include "common/config-manager.h"
44 #include "common/system.h"
45 #include "common/events.h"
46 
47 namespace ICB {
48 
49 bool gRegainedFocus = false;
50 bool gotTheFocus = false;
51 
52 char g_characters[] = "characters\\";
53 char gamelanguage[ENGINE_STRING_LEN] = "english";
54 bool8 camera_hack;
55 uint32 BACKGROUND_BUFFER_SIZE;
56 uint32 ANIMATION_BUFFER_SIZE;
57 uint32 BITMAP_BUFFER_SIZE;
58 uint32 SONICS_BUFFER_SIZE;
59 
60 // private session data (scripts+objects+walkgrids) : 200KB
61 #define PRIVATE_RESMAN_SIZE (400 * 1024)
62 
63 // local prototypes
64 void Mission_and_console();
65 
getConfigValueWithDefault(const ConfigFile & config,const Common::String & section,const Common::String & key,uint32 defaultValue)66 uint32 getConfigValueWithDefault(const ConfigFile &config, const Common::String &section, const Common::String &key, uint32 defaultValue) {
67 	if (scumm_stricmp("MusicVolume", key.c_str()) == 0) {
68 		return ConfMan.getInt("music_volume") / 2;
69 	} else if (scumm_stricmp("SpeechVolume", key.c_str()) == 0) {
70 		return ConfMan.getInt("speech_volume") / 2;
71 	} else if (scumm_stricmp("SfxVolume", key.c_str()) == 0) {
72 		return ConfMan.getInt("sfx_volume") / 2;
73 	} else if (scumm_stricmp("Game Completed", key.c_str()) == 0) {
74 		if (ConfMan.hasKey("game_completed"))
75 			return ConfMan.getBool("game_completed") ? 1 : 0;
76 		else
77 			return 0;
78 	} else if (scumm_stricmp("Subtitles", key.c_str()) == 0) {
79 		if (ConfMan.hasKey("subtitles"))
80 			return (uint32)ConfMan.getBool("subtitles");
81 	} else if (scumm_stricmp("Movie Library", section.c_str()) == 0) {
82 		Common::String movie = Common::String("movie_") + key;
83 		if (ConfMan.hasKey(movie))
84 			return (uint32)ConfMan.getBool(movie);
85 		else {
86 			uint32 result = config.readIntSetting(section, key, defaultValue);
87 			ConfMan.setBool(movie, result != 0);
88 		}
89 	} else if (scumm_stricmp("Controller Settings", section.c_str()) == 0 &&
90 		   scumm_stricmp("Method", key.c_str()) == 0) {
91 		if (ConfMan.hasKey("actor_relative"))
92 			return ConfMan.getBool("actor_relative") ? 0 : 1;
93 		else {
94 			uint32 result = config.readIntSetting(section, key, defaultValue);
95 			ConfMan.setBool("actor_relative", result == 0);
96 		}
97 	}
98 
99 	return config.readIntSetting(section, key, defaultValue);
100 }
101 
ReadConfigFromIniFile()102 void ReadConfigFromIniFile() {
103 	char configFile[1024];
104 	uint32 temp;
105 
106 	sprintf(configFile, CONFIG_INI_FILENAME);
107 
108 	ConfigFile config;
109 	pxString filename = configFile;
110 	filename.ConvertPath();
111 	config.readFile(filename.c_str());
112 
113 	// Music volume
114 	SetMusicVolume(getConfigValueWithDefault(config, "Option Settings", "MusicVolume", 127));
115 
116 	// Speech volume
117 	SetSpeechVolume(getConfigValueWithDefault(config, "Option Settings", "SpeechVolume", 127));
118 
119 	// Sfx volume
120 	SetSfxVolume(getConfigValueWithDefault(config, "Option Settings", "SfxVolume", 127));
121 
122 	// Has the game been completed previously
123 	temp = getConfigValueWithDefault(config, "Extras", "Game Completed", 0);
124 	// HACK: Enable all extras for now
125 	warning("Enabling all extras for development purposes");
126 	temp = 1;
127 	if (temp == 0)
128 		g_px->game_completed = FALSE8;
129 	else
130 		g_px->game_completed = TRUE8;
131 
132 	// Subtitle switch
133 	temp = getConfigValueWithDefault(config, "Video Settings", "Subtitles", 1);
134 	if (temp == 0)
135 		g_px->on_screen_text = FALSE8;
136 	else
137 		g_px->on_screen_text = TRUE8;
138 
139 	// Control method
140 	temp = getConfigValueWithDefault(config, "Controller Settings", "Method", ACTOR_RELATIVE);
141 	if ((__Actor_control_mode)temp == SCREEN_RELATIVE)
142 		g_icb_session->player.Set_control_mode(SCREEN_RELATIVE);
143 	else
144 		g_icb_session->player.Set_control_mode(ACTOR_RELATIVE);
145 
146 	// Set the default keys first in case the ini file mappings are invalid somehow
147 	SetDefaultKeys();
148 
149 	// Read the movie library settings
150 	for (uint32 i = 0; i < TOTAL_NUMBER_OF_MOVIES; i++) {
151 		temp = config.readIntSetting("Movie Library", pxVString("%X", HashString(g_movieLibrary[i].filename)), 0);
152 		if (temp == 0)
153 			g_movieLibrary[i].visible = FALSE8;
154 		else
155 			g_movieLibrary[i].visible = TRUE8;
156 	}
157 }
158 
Save_config_file()159 void Save_config_file() {
160 	ConfMan.setInt("music_volume", GetMusicVolume() * 2);
161 	ConfMan.setInt("speech_volume", GetMusicVolume() * 2);
162 	ConfMan.setInt("sfx_volume", GetMusicVolume() * 2);
163 	ConfMan.setBool("subtitles", g_px->on_screen_text != 0);
164 	ConfMan.setBool("game_completed", g_px->game_completed);
165 	ConfMan.setBool("actor_relative", g_icb_session->player.Get_control_mode() == ACTOR_RELATIVE);
166 
167 	// Write the movie library settings
168 	for (uint32 i = 0; i < TOTAL_NUMBER_OF_MOVIES; i++) {
169 		// Only write a setting when it's been achieved
170 		if (g_movieLibrary[i].visible) {
171 			char temp[1024];
172 			sprintf(temp, "%X", HashString(g_movieLibrary[i].filename));
173 			Common::String movie = Common::String("movie_") + temp;
174 			ConfMan.setBool(movie, true);
175 		}
176 	}
177 
178 	ConfMan.flushToDisk();
179 }
180 
InitEngine(const char * lpCmdLine)181 void InitEngine(const char *lpCmdLine) {
182 	CreateGlobalObjects();
183 
184 	// Set the character root directory to be the same as the normal root directory
185 	camera_hack = false; // defaults to off
186 
187 	// create the surface manaager
188 	surface_manager = new _surface_manager;
189 
190 	// Init the low level direct draw object
191 	surface_manager->Init_direct_draw();
192 
193 	// Init the sound engine
194 	if (!Init_Sound_Engine()) {
195 		Message_box("Sound device unavailable.");
196 		Zdebug("Failed to start the sound engine");
197 		noSoundEngine = TRUE8;
198 	}
199 
200 	// See how much memory we have in this machine
201 	Memory_stats();
202 
203 	// Animations
204 	rs1 = new res_man(ANIMATION_BUFFER_SIZE);
205 	rs1->Set_auto_timeframe_advance();
206 	rs_anims = rs1;
207 
208 	// Icons, Fonts and Remora
209 	rs3 = new res_man(BITMAP_BUFFER_SIZE);
210 	rs3->Set_auto_timeframe_advance();
211 	rs_icons = rs3;
212 	rs_remora = rs3;
213 	rs_font = rs3;
214 
215 	// Stage
216 	rs2 = new res_man(BACKGROUND_BUFFER_SIZE);
217 	rs2->Set_auto_timeframe_advance();
218 	rs_bg = rs2;
219 
220 	// Privates
221 	private_session_resman = new res_man(PRIVATE_RESMAN_SIZE);
222 
223 	// Initalize the Revolution Render Device
224 	InitRevRenderDevice();
225 
226 	// we run a special check for a font file - otherwise, res-open will fail triggering a con-fatal-error - not good
227 	sys_font_hash = HashString(SYS_FONT);
228 	font_cluster_hash = HashString(FONT_CLUSTER_PATH);
229 
230 	// inti direct input
231 	Init_direct_input();
232 
233 	// init global variables
234 	Zdebug("Init_globals");
235 	Init_globals();
236 	g_globalScriptVariables->SetVariable("missionelapsedtime", 0);
237 	g_globalScriptVariables->SetVariable("demo", 0);
238 	Zdebug("~Init_globals");
239 
240 	// ok, see if the special gameScript is present
241 	// if so set the stub mode to GameScript mode
242 	if (gs.Init_game_script() && strstr(lpCmdLine, "mission") == NULL) {
243 		// GameScript mode
244 		// unless there is a console.icb file we dont allow debugging
245 
246 		// set base mode of stub to gameScript processor
247 		g_stub->Set_current_stub_mode(__game_script);
248 	} else
249 		g_stub->Set_current_stub_mode(__mission_and_console);
250 
251 	// Initialise the runtime cluster manager
252 	g_theClusterManager->Initialise();
253 
254 	zdebug = FALSE8; // no console so switch off debugging (zoff)
255 
256 	ConfMan.setBool("actor_relative", true);
257 	ConfMan.setBool("subtitles", true);
258 
259 	// Load settings from our ini file (must be called AFTER everything has been setup)
260 	ReadConfigFromIniFile();
261 }
262 
quitEngine()263 void quitEngine() {
264 	Zdebug("\nap closed");
265 	Zdebug("Be Vigilant!\n");
266 
267 	if (g_mission)
268 		g_icb_mission->___delete_mission();
269 
270 	Close_Sound_Engine();
271 
272 	if (surface_manager)
273 		delete surface_manager;
274 
275 	Save_config_file(); // write user options ini file
276 	DestoryRevRenderDevice();
277 
278 	// Shutdown the runtime cluster manager
279 	g_theClusterManager->Shutdown();
280 
281 	DestroyGlobalObjects();
282 }
283 
mainLoopIteration()284 bool mainLoopIteration() {
285 	Common::Event event;
286 
287 	while (g_system->getEventManager()->pollEvent(event)) {
288 		switch (event.type) {
289 		case Common::EVENT_KEYDOWN: {
290 			// Pass ENTER and BACKSPACE KEYDOWN events to WriteKey() so the save menu in options_manager_pc.cpp can see them.
291 			if (event.kbd.keycode == Common::KEYCODE_RETURN) {
292 				WriteKey((char)event.kbd.keycode);
293 			} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
294 				WriteKey((char)event.kbd.keycode);
295 			} else {
296 				WriteKey(event.kbd.keycode);
297 			}
298 			setKeyState(event.kbd.keycode, true);
299 			break;
300 		}
301 		case Common::EVENT_KEYUP: {
302 			setKeyState(event.kbd.keycode, false);
303 			break;
304 		}
305 		case Common::EVENT_MOUSEMOVE:
306 			mousex = event.mouse.x;
307 			mousey = event.mouse.y;
308 			break;
309 		case Common::EVENT_LBUTTONDOWN:
310 			LogMouseEvent(RD_LEFTBUTTONDOWN);
311 			mousex = event.mouse.x;
312 			mousey = event.mouse.y;
313 			break;
314 		case Common::EVENT_RBUTTONDOWN:
315 			LogMouseEvent(RD_RIGHTBUTTONDOWN);
316 			mousex = event.mouse.x;
317 			mousey = event.mouse.y;
318 			break;
319 		case Common::EVENT_LBUTTONUP:
320 			LogMouseEvent(RD_LEFTBUTTONUP);
321 			mousex = event.mouse.x;
322 			mousey = event.mouse.y;
323 			break;
324 		case Common::EVENT_RBUTTONUP:
325 			LogMouseEvent(RD_RIGHTBUTTONUP);
326 			mousex = event.mouse.x;
327 			mousey = event.mouse.y;
328 			break;
329 
330 		case Common::EVENT_QUIT:
331 			quitEngine();
332 			return false;
333 		default:
334 			break;
335 		}
336 	}
337 	// Used to be triggered by else if(gotTheFocus).
338 	g_stub->Process_stub();
339 
340 	g_system->delayMillis(1);
341 	return true;
342 }
343 
Mission_and_console()344 void Mission_and_console() {
345 	// run the mission with console facility
346 	// this mode will run forever
347 
348 	uint32 mission_ret;
349 
350 	gameCycle += 1; // tick up the game cycle flag
351 
352 	// logic
353 	mission_ret = g_mission->Game_cycle();
354 
355 	if (mission_ret) {
356 		// the mission has terminated of its own accord - as apposed to a user quit
357 
358 		// if the player died then we bring up a restart/continue menu here
359 		c_game_object *ob = (c_game_object *)MS->objects->Fetch_item_by_number(MS->player.Fetch_player_id());
360 		int32 ret = ob->GetVariable("state");
361 		if (ob->GetIntegerVariable(ret)) {
362 			// Return to avoid deleting the mission
363 			g_stub->Push_stub_mode(__gameover_menu);
364 			return;
365 		}
366 
367 		g_icb_mission->___delete_mission();
368 
369 		if (!gs.Running_from_gamescript())
370 			Fatal_error("Thank you for playing In Cold Blood");
371 
372 		else
373 			g_stub->Pop_stub_mode(); // back to game script server
374 	} else
375 		g_mission->Create_display();
376 }
377 
Terminate_ap()378 void Terminate_ap() {
379 	// can be called from any service loop to exit the program and return to windows
380 
381 	Fatal_error("Terminate Ap");
382 }
383 
Fix_time()384 void _stub::Fix_time() {
385 	// wait until target time has elapsed
386 	// presumably 1/12 second - pc==80ms
387 	uint32 targetTime = (TARGET_TIME * 100) / cycle_speed;
388 	do {
389 		g_system->delayMillis(1);
390 	} while ((g_system->getMillis() - stub_timer_time) < targetTime);
391 }
392 
WhichCD(const char *)393 int32 WhichCD(const char * /* mission */) { return 1; }
394 
HasMMXTechnology()395 bool HasMMXTechnology() { return true; }
396 
LoadPlatformSpecific(Common::SeekableReadStream *)397 void _mission::LoadPlatformSpecific(Common::SeekableReadStream * /* fh */) {
398 	// No PC specific stuff in here (at the moment)
399 }
400 
SavePlatformSpecific(Common::WriteStream *)401 void _mission::SavePlatformSpecific(Common::WriteStream * /* fh */) {
402 	// No PC specific stuff in here (at the moment)
403 }
404 
405 } // End of namespace ICB
406