1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 	Created for non-duplication of code between mac and SDL ports.
21 	(Loren Petrich and others)
22 
23 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
24 	Disabled network_speaker_idle_proc under Carbon
25 
26 Mar 08, 2002 (Woody Zenfell):
27     Enabled network_microphone_idle_proc (in global_idle_proc()) under SDL
28 */
29 
30 bool CheatsActive = false;
31 bool chat_input_mode = false;
32 
33 #include "cseries.h"
34 
35 #include "XML_ParseTreeRoot.h"
36 #include "interface.h"
37 #include "world.h"
38 #include "screen.h"
39 #include "map.h"
40 #include "shell.h"
41 #include "preferences.h"
42 #include "vbl.h"
43 #include "player.h"
44 #include "Music.h"
45 #include "items.h"
46 #include "network_sound.h"
47 #include "TextStrings.h"
48 #include "InfoTree.h"
49 
50 #include <ctype.h>
51 
52 
53 extern void process_new_item_for_reloading(short player_index, short item_type);
54 extern bool try_and_add_player_item(short player_index,	short type);
55 extern void mark_shield_display_as_dirty();
56 extern void mark_oxygen_display_as_dirty();
57 extern void accelerate_monster(short monster_index,	world_distance vertical_velocity,
58 							   angle direction, world_distance velocity);
59 extern void network_speaker_idle_proc(void);
60 extern void update_interface(short time_elapsed);
61 
62 
63 // Cheat-keyword definition: tag and associated text string
64 
65 #define MAXIMUM_KEYWORD_LENGTH 20
66 
67 struct keyword_data
68 {
69 	short tag;
70 	char keyword[MAXIMUM_KEYWORD_LENGTH+1]; /* in uppercase */
71 };
72 
73 
74 enum // cheat tags
75 {
76 	_tag_health,
77 	_tag_oxygen,
78 	_tag_map,
79 	_tag_fusion,
80 	_tag_invincible,
81 	_tag_invisible,
82 	_tag_extravision,
83 	_tag_infravision,
84 	_tag_pistol,
85 	_tag_rifle,
86 	_tag_missile,	// LP change: corrected spelling
87 	_tag_toaster,  // flame-thrower
88 	_tag_pzbxay,
89 	_tag_shotgun,	// LP addition
90 	_tag_smg,		// LP addition
91 	_tag_ammo,
92 	_tag_pathways,
93 	_tag_view,
94 	_tag_jump,
95 	_tag_aslag,
96 	_tag_save
97 };
98 
99 void AddItemsToPlayer(short ItemType, short MaxNumber);
100 
101 // Here, only one is added, unless the number of items is at least as great as MaxNumber
102 void AddOneItemToPlayer(short ItemType, short MaxNumber);
103 
104 // LP addition: XML support for controlling whether cheats are active
105 keyword_data *original_keywords = NULL;
106 
107 static keyword_data keywords[]=
108 {
109 	{_tag_health, "NRG"},
110 	{_tag_oxygen, "OTWO"},
111 	{_tag_map, "MAP"},
112 	{_tag_invisible, "BYE"},
113 	{_tag_invincible, "NUKE"},
114 	{_tag_infravision, "SEE"},
115 	{_tag_extravision, "WOW"},
116 	{_tag_pistol, "MAG"},
117 	{_tag_rifle, "RIF"},
118 	{_tag_missile, "POW"},
119 	{_tag_toaster, "TOAST"},
120 	{_tag_fusion, "MELT"},
121 	{_tag_shotgun, "PUFF"},
122 	{_tag_smg, "ZIP"},
123 	{_tag_pzbxay, "PZBXAY"}, // the alien shotgon, in the phfor's language
124 	{_tag_ammo, "AMMO"},
125 	{_tag_jump, "QWE"},
126 	{_tag_aslag, "SHIT"},
127 	{_tag_save, "YOURMOM"}
128 };
129 
130 
131 
AddItemsToPlayer(short ItemType,short MaxNumber)132 void AddItemsToPlayer(short ItemType, short MaxNumber)
133 {
134 	for (int i=0; i<MaxNumber; i++)
135 		try_and_add_player_item(local_player_index,ItemType);
136 }
137 
AddOneItemToPlayer(short ItemType,short MaxNumber)138 void AddOneItemToPlayer(short ItemType, short MaxNumber)
139 {
140 	local_player->items[ItemType] = MAX(local_player->items[ItemType],0);
141 	if (local_player->items[ItemType] < MaxNumber)
142 		try_and_add_player_item(local_player_index,ItemType);
143 }
144 
145 
handle_keyword(int tag)146 void handle_keyword(int tag)
147 {
148 
149 	switch (tag)
150 	{
151 		case _tag_health:
152 			if (local_player->suit_energy<PLAYER_MAXIMUM_SUIT_ENERGY)
153 			{
154 				local_player->suit_energy= PLAYER_MAXIMUM_SUIT_ENERGY;
155 			}
156 			else
157 			{
158 				if (local_player->suit_energy<2*PLAYER_MAXIMUM_SUIT_ENERGY)
159 				{
160 					local_player->suit_energy= 2*PLAYER_MAXIMUM_SUIT_ENERGY;
161 				}
162 				else
163 				{
164 					local_player->suit_energy= MAX(local_player->suit_energy, 3*PLAYER_MAXIMUM_SUIT_ENERGY);
165 				}
166 			}
167 			mark_shield_display_as_dirty();
168 			break;
169 		case _tag_oxygen:
170 			local_player->suit_oxygen= MAX(local_player->suit_oxygen,PLAYER_MAXIMUM_SUIT_OXYGEN);
171 			mark_oxygen_display_as_dirty();
172 			break;
173 		case _tag_map:
174 			dynamic_world->game_information.game_options^= (_overhead_map_shows_items|_overhead_map_shows_monsters|_overhead_map_shows_projectiles);
175 			break;
176 		case _tag_invincible:
177 			process_player_powerup(local_player_index, _i_invincibility_powerup);
178 			break;
179 		case _tag_invisible:
180 			process_player_powerup(local_player_index, _i_invisibility_powerup);
181 			break;
182 		case _tag_infravision:
183 			process_player_powerup(local_player_index, _i_infravision_powerup);
184 			break;
185 		case _tag_extravision:
186 			process_player_powerup(local_player_index, _i_extravision_powerup);
187 			break;
188 		case _tag_jump:
189 			accelerate_monster(local_player->monster_index, WORLD_ONE/10, 0, 0);
190 			break;
191 		// LP: changed these cheats and added new ones:
192 		case _tag_pistol:
193 			AddOneItemToPlayer(_i_magnum,2);
194 			AddItemsToPlayer(_i_magnum_magazine,10);
195 			break;
196 		case _tag_rifle:
197 			AddItemsToPlayer(_i_assault_rifle,10);
198 			AddItemsToPlayer(_i_assault_rifle_magazine,10);
199 			AddItemsToPlayer(_i_assault_grenade_magazine,10);
200 			break;
201 		case _tag_missile:
202 			AddItemsToPlayer(_i_missile_launcher,1);
203 			AddItemsToPlayer(_i_missile_launcher_magazine,10);
204 			break;
205 		case _tag_toaster:
206 			AddItemsToPlayer(_i_flamethrower,1);
207 			AddItemsToPlayer(_i_flamethrower_canister,10);
208 			break;
209 		case _tag_fusion:
210 			AddItemsToPlayer(_i_plasma_pistol,1);
211 			AddItemsToPlayer(_i_plasma_magazine,10);
212 			break;
213 		case _tag_pzbxay:
214 			AddItemsToPlayer(_i_alien_shotgun,1);
215 			break;
216 		case _tag_shotgun:
217 			AddOneItemToPlayer(_i_shotgun,2);
218 			AddItemsToPlayer(_i_shotgun_magazine,10);
219 			break;
220 		case _tag_smg:
221 			AddOneItemToPlayer(_i_smg,2);
222 			AddItemsToPlayer(_i_smg_ammo,10);
223 			break;
224 		case _tag_save:
225 			save_game();
226 			break;
227 		// LP guess as to what might be good: ammo-only version of "aslag"
228 		case _tag_ammo:
229 			{
230 				short items[]= { _i_assault_rifle, _i_magnum, _i_missile_launcher, _i_flamethrower,
231 					_i_plasma_pistol, _i_alien_shotgun, _i_shotgun,
232 					_i_assault_rifle_magazine, _i_assault_grenade_magazine,
233 					_i_magnum_magazine, _i_missile_launcher_magazine, _i_flamethrower_canister,
234 					_i_plasma_magazine, _i_shotgun_magazine, _i_shotgun, _i_smg, _i_smg_ammo};
235 
236 				for(unsigned index= 0; index<sizeof(items)/sizeof(short); ++index)
237 				{
238 					switch(get_item_kind(items[index]))
239 					{
240 						case _ammunition:
241 							AddItemsToPlayer(items[index],10);
242 							break;
243 					}
244 				}
245 			}
246 			break;
247 		case _tag_aslag:
248 			{
249 				// LP change: added the SMG and its ammo
250 				short items[]= { _i_assault_rifle, _i_magnum, _i_missile_launcher, _i_flamethrower,
251 					_i_plasma_pistol, _i_alien_shotgun, _i_shotgun,
252 					_i_assault_rifle_magazine, _i_assault_grenade_magazine,
253 					_i_magnum_magazine, _i_missile_launcher_magazine, _i_flamethrower_canister,
254 					_i_plasma_magazine, _i_shotgun_magazine, _i_shotgun, _i_smg, _i_smg_ammo};
255 
256 				for(unsigned index= 0; index<sizeof(items)/sizeof(short); ++index)
257 				{
258 					switch(get_item_kind(items[index]))
259 					{
260 						case _weapon:
261 							if(items[index]==_i_shotgun || items[index]==_i_magnum)
262 							{
263 								AddOneItemToPlayer(items[index],2);
264 							} else {
265 								AddItemsToPlayer(items[index],1);
266 							}
267 							break;
268 
269 						case _ammunition:
270 							AddItemsToPlayer(items[index],10);
271 							break;
272 
273 						case _powerup:
274 						case _weapon_powerup:
275 							break;
276 
277 						default:
278 							break;
279 					}
280 					process_new_item_for_reloading(local_player_index, items[index]);
281 				}
282 			}
283 			local_player->suit_energy = MAX(local_player->suit_energy, 3*PLAYER_MAXIMUM_SUIT_ENERGY);
284 			update_interface(NONE);
285 			break;
286 
287 		default:
288 			break;
289 	}
290 
291 	return;
292 
293 }
294 
295 /*
296  *  Called regularly during event loops
297  */
298 
global_idle_proc(void)299 void global_idle_proc(void)
300 {
301 	Music::instance()->Idle();
302 	network_speaker_idle_proc();
303 	network_microphone_idle_proc();
304 	SoundManager::instance()->Idle();
305 }
306 
307 /*
308  *  Somebody wants to do something important; free as much temporary memory as possible
309  */
310 
free_and_unlock_memory(void)311 void free_and_unlock_memory(void)
312 {
313 	SoundManager::instance()->StopAllSounds();
314 }
315 
316 /*
317  *  Special version of malloc() used for level transitions, frees some
318  *  memory if possible
319  */
320 
level_transition_malloc(size_t size)321 void *level_transition_malloc(
322 	size_t size)
323 {
324 	void *ptr= malloc(size);
325 	if (!ptr)
326 	{
327 		SoundManager::instance()->UnloadAllSounds();
328 
329 		ptr= malloc(size);
330 		if (!ptr)
331 		{
332 			unload_all_collections();
333 
334 			ptr= malloc(size);
335 		}
336 	}
337 
338 	return ptr;
339 }
340 
341 // LP change: implementing Benad's "cheats always on"
342 // Originally, cheats were not on in the "FINAL" version
343 //
344 #define NUMBER_OF_KEYWORDS (sizeof(keywords)/sizeof(keyword_data))
345 
346 static char keyword_buffer[MAXIMUM_KEYWORD_LENGTH+1];
347 
process_keyword_key(char key)348 int process_keyword_key(
349 	char key)
350 {
351 	short tag = NONE;
352 
353 	// copy the buffer down and insert the new character
354 	for (unsigned i=0;i<MAXIMUM_KEYWORD_LENGTH-1;++i)
355 	{
356 		keyword_buffer[i]= keyword_buffer[i+1];
357 	}
358 	keyword_buffer[MAXIMUM_KEYWORD_LENGTH-1]= toupper(key);
359 	keyword_buffer[MAXIMUM_KEYWORD_LENGTH]= 0;
360 
361 	// any matches?
362 	for (unsigned i=0; i<NUMBER_OF_KEYWORDS; ++i)
363 	{
364 		if (!strcmp(keywords[i].keyword, keyword_buffer+MAXIMUM_KEYWORD_LENGTH-strlen(keywords[i].keyword)))
365 		{
366 			// wipe the buffer if we have a match
367 			memset(keyword_buffer, 0, MAXIMUM_KEYWORD_LENGTH);
368 			tag= keywords[i].tag;
369 			break;
370 		}
371 	}
372 
373 	return tag;
374 }
375 
reset_mml_cheats()376 void reset_mml_cheats()
377 {
378 	CheatsActive = false;
379 	if (original_keywords)
380 	{
381 		for (int i = 0; i < NUMBER_OF_KEYWORDS; i++)
382 			keywords[i] = original_keywords[i];
383 	}
384 }
385 
parse_mml_cheats(const InfoTree & root)386 void parse_mml_cheats(const InfoTree& root)
387 {
388 	// back up old values first
389 	if (!original_keywords)
390 	{
391 		original_keywords = (keyword_data *) malloc(sizeof(keywords));
392 		assert(original_keywords);
393 		for (int i = 0; i < NUMBER_OF_KEYWORDS; i++)
394 			original_keywords[i] = keywords[i];
395 	}
396 
397 	root.read_attr("on", CheatsActive);
398 
399 	BOOST_FOREACH(InfoTree ktree, root.children_named("keyword"))
400 	{
401 		int16 index;
402 		if (!ktree.read_indexed("index", index, NUMBER_OF_KEYWORDS))
403 			continue;
404 
405 		std::string word = ktree.get_value(std::string(""));
406 		char *dest = keywords[index].keyword;
407 		size_t decoded_len = DeUTF8_C(word.c_str(), word.size(), dest, MAXIMUM_KEYWORD_LENGTH);
408 		for (size_t c = 0; c < decoded_len; c++, dest++)
409 			*dest = toupper(*dest);
410 	}
411 }
412