1 /*
2  *
3  *   Copyright (c) 1994, 2002, 2003 Johannes Prix
4  *   Copyright (c) 1994, 2002 Reinhard Prix
5  *   Copyright (c) 2004-2010 Arthur Huillet
6  *
7  *
8  *  This file is part of Freedroid
9  *
10  *  Freedroid is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  Freedroid is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with Freedroid; see the file COPYING. If not, write to the
22  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  *  MA  02111-1307  USA
24  *
25  */
26 
27 /**
28  * This file contains all features, movement, firing, collision and
29  * extras of the influencer.
30  */
31 
32 #define _influ_c
33 
34 #include "system.h"
35 
36 #include "defs.h"
37 #include "struct.h"
38 #include "global.h"
39 #include "proto.h"
40 #include "widgets/widgets.h"
41 
42 #define BEST_MELEE_DISTANCE (0.8)
43 #define BEST_CHAT_DISTANCE (BEST_MELEE_DISTANCE+0.2)
44 #define DISTANCE_TOLERANCE (0.00002)
45 
46 #define LEVEL_JUMP_DEBUG 1
47 
48 static void CheckForTuxOutOfMap();
49 static void AnalyzePlayersMouseClick();
50 
51 static int no_left_button_press_in_previous_analyze_mouse_click = FALSE;
52 
53 /**
54  *
55  *
56  */
calc_distance(float pos1_x,float pos1_y,float pos2_x,float pos2_y)57 float calc_distance(float pos1_x, float pos1_y, float pos2_x, float pos2_y)
58 {
59 	return sqrt((pos1_x - pos2_x) * (pos1_x - pos2_x) + (pos1_y - pos2_y) * (pos1_y - pos2_y));
60 };
61 
62 /**
63  *
64  *
65  */
vect_len(moderately_finepoint our_vector)66 float vect_len(moderately_finepoint our_vector)
67 {
68 	return (sqrt(powf(our_vector.x, 2) + powf(our_vector.y, 2)));
69 };				// float vect_len ( moderately_finepoint our_vector )
70 
get_tux_running_speed(void)71 static float get_tux_running_speed(void)
72 {
73 	if (GameConfig.cheat_double_speed)
74 		return 2 * TUX_RUNNING_SPEED;
75 
76 	return TUX_RUNNING_SPEED;
77 }
78 
79 /**
80  * This function adapts the influencers current speed to the maximal speed
81  * possible for the influencer.
82  *
83  * This function also stops Tux from moving while fighting.
84  */
limit_tux_speed()85 static void limit_tux_speed()
86 {
87 	/* Stop all movement if Tux is currently inside attack animation.  This is
88 	 * to stop Tux from moving without his legs animating.
89 	 *
90 	 * NOTE REGARDING MELEE ATTACK.  Tux starts to swing before he has reached
91 	 * BEST_MELEE_DISTANCE.  If we stop before reaching this distance, Tux will
92 	 * continue to try to move to this distance between swings, which causes a
93 	 * jerking motion towards the enemy.  To stop this from happening, we force
94 	 * Tux NOT to stand still when he is first charging a new target.  He stands
95 	 * still ONLY when he changes target during a swing. */
96 	static enemy *previous_target;
97 	enemy *current_target = enemy_resolve_address(Me.current_enemy_target_n,
98 												  &Me.current_enemy_target_addr);
99 
100 	if (Me.weapon_item.type >= 0) {
101 		int has_melee = ItemMap[Me.weapon_item.type].weapon_is_melee;
102 		if (Me.weapon_swing_time != -1 && (!has_melee
103 		   || (has_melee && (previous_target != current_target || current_target == NULL))))
104 		{
105 			Me.speed.x = 0;
106 			Me.speed.y = 0;
107 			return;
108 		} else {
109 			previous_target = current_target;
110 		}
111 	}
112 
113 	/* Limit the speed when Tux is not attacking. */
114 	float speed = Me.speed.x * Me.speed.x + Me.speed.y * Me.speed.y;
115 	float running_speed = get_tux_running_speed();
116 	if (speed > running_speed * running_speed) {
117 		float ratio = (running_speed * running_speed) / speed;
118 		Me.speed.x *= ratio;
119 		Me.speed.y *= ratio;
120 	}
121 }
122 
123 /**
124  * When the player has requested an attack motion, we start the
125  * corresponding code, that should try to attack, if that's currently
126  * possible.
127  */
tux_wants_to_attack_now(int use_mouse_cursor_for_targeting)128 void tux_wants_to_attack_now(int use_mouse_cursor_for_targeting)
129 {
130 	// Maybe the player requested an attack before the reload/retract
131 	// phase is completed.  In that case, we don't attack.
132 
133 	if (Me.busy_time > 0) {
134 		return;
135 	}
136 
137 	// If the Tux has a weapon and this weapon requires some ammunition, then
138 	// we have to check for enough ammunition first...
139 	int weapon_with_ammo = (Me.weapon_item.type >= 0) && (ItemMap[Me.weapon_item.type].weapon_ammo_type);
140 
141 	if (weapon_with_ammo) {
142 		if (Me.weapon_item.ammo_clip <= 0) {
143 			// So no ammunition... We should say so and reload...
144 			No_Ammo_Sound();
145 			// TRANSLATORS: Console msg when a weapon is empty
146 			append_new_game_message(_("%s empty, reloading..."), D_(item_specs_get_name(Me.weapon_item.type)));
147 			TuxReloadWeapon();
148 
149 			return;
150 		}
151 	}
152 
153 	// Try to attack
154 
155 	int hit_something = perform_tux_attack(use_mouse_cursor_for_targeting);
156 
157 	// If the attack failed, and the attack did not use ammunition, nothing
158 	// else is to be done. Same if the attack was done with bare fist.
159 
160 	if ((!hit_something && !weapon_with_ammo) || (Me.weapon_item.type < 0)) {
161 		return;
162 	}
163 
164 	// The weapon was used (i.e. a bullet was fired by a ranged weapon, or a
165 	// melee shot actually touched an enemy) and therefore the weapon looses
166 	// some of it's durability
167 
168 	if (weapon_with_ammo || hit_something)
169 		DamageWeapon(&(Me.weapon_item));
170 
171 	// Also if it uses ammunition, one charge is to be removed
172 	// But, the weapon can have been destroyed by the call to DamageWeapon(),
173 	// so the weapon's type is to be checked again.
174 
175 	if (weapon_with_ammo)
176 		Me.weapon_item.ammo_clip--;
177 }
178 
179 /**
180  * The Tux might have cross a level's boundary. In that case, we
181  * must move the Tux silently to the corresponding other level.
182  */
correct_tux_position_according_to_jump()183 void correct_tux_position_according_to_jump()
184 {
185 	gps old_mouse_move_target;
186 	gps oldpos = { Me.pos.x, Me.pos.y, Me.pos.z };
187 	gps newpos;
188 
189 	// If current Tux position is inside current level, there's nothing to change
190 	//
191 	if (pos_inside_level(Me.pos.x, Me.pos.y, CURLEVEL()))
192 		return;
193 
194 	// Else, try to retrieve the actual position
195 	//
196 	int pos_valid = resolve_virtual_position(&newpos, &oldpos);
197 
198 	if (!pos_valid) {
199 		// We were not able to compute the actual position...
200 		CheckForTuxOutOfMap();
201 		return;
202 	}
203 	// Tux is on another level, teleport it
204 	// (note: Teleport() resets Me.mouse_move_target, so we have to restore it)
205 	//
206 	old_mouse_move_target.x = Me.mouse_move_target.x;
207 	old_mouse_move_target.y = Me.mouse_move_target.y;
208 	old_mouse_move_target.z = Me.mouse_move_target.z;
209 
210 	Teleport(newpos.z, newpos.x, newpos.y, FALSE, FALSE);
211 
212 	Me.mouse_move_target.x = old_mouse_move_target.x;
213 	Me.mouse_move_target.y = old_mouse_move_target.y;
214 	Me.mouse_move_target.z = old_mouse_move_target.z;
215 
216 	// Update the mouse target position, if needed
217 	//
218 	// The purpose is to define mouse_move_target relatively to new Tux's level.
219 	// However, if Tux had to move from one level to one of its diagonal neighbor, it has
220 	// eventually not yet reach the final destination level.
221 	// So we first have to get the real mouse_target position, and then transform it into a
222 	// virtual position according to Tux's new level.
223 	//
224 	if (old_mouse_move_target.z != -1) {
225 		int rtn = resolve_virtual_position(&Me.mouse_move_target, &old_mouse_move_target);
226 		if (rtn)
227 			update_virtual_position(&Me.mouse_move_target, &Me.mouse_move_target, newpos.z);
228 		if (!rtn || (Me.mouse_move_target.x == -1)
229 				 || !SinglePointColldet(Me.mouse_move_target.x, Me.mouse_move_target.y, Me.mouse_move_target.z, NULL)) {
230 			Me.mouse_move_target.x = (-1);
231 			Me.mouse_move_target.y = (-1);
232 			Me.mouse_move_target.z = (-1);
233 		}
234 	}
235 
236 	// Update the intermediate waypoints
237 	//
238 	// Intermediate waypoints are defined relatively to Tux's current level.
239 	// They thus also have to be updated<Fluzz>
240 	//
241 	int i;
242 	for (i = 0; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++)
243 	{
244 		if (Me.next_intermediate_point[i].x == -1)
245 			break;
246 
247 		gps old_point = { Me.next_intermediate_point[i].x, Me.next_intermediate_point[i].y, oldpos.z };
248 		gps new_point;
249 
250 		int rtn = resolve_virtual_position(&new_point, &old_point);
251 		if (rtn)
252 			update_virtual_position(&new_point, &new_point, newpos.z);
253 
254 		if (!rtn || new_point.x == -1) {
255 			clear_out_intermediate_points(&newpos, Me.next_intermediate_point, MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX);
256 			break;
257 		}
258 
259 		Me.next_intermediate_point[i].x = new_point.x;
260 		Me.next_intermediate_point[i].y = new_point.y;
261 	}
262 
263 	// Even the Tux must not leave the map!  A sanity check is done
264 	// here...
265 	//
266 	CheckForTuxOutOfMap();
267 
268 }				// correct_tux_position_according_to_jump ( )
269 
270 /**
271  * This function initializes the influencers position history, which is
272  * a ring buffer and is needed for throwing the influencer back (only one
273  * or two positions would be needed for that) and for influencers followers
274  * to be able to track the influencers path (10000 or so positions are used
275  * for that, and that's why it is a ring buffer).
276  */
InitInfluPositionHistory()277 void InitInfluPositionHistory()
278 {
279 	int RingPosition;
280 
281 	for (RingPosition = 0; RingPosition < MAX_INFLU_POSITION_HISTORY; RingPosition++) {
282 		Me.Position_History_Ring_Buffer[RingPosition].x = Me.pos.x;
283 		Me.Position_History_Ring_Buffer[RingPosition].y = Me.pos.y;
284 		Me.Position_History_Ring_Buffer[RingPosition].z = Me.pos.z;
285 	}
286 }				// void InitInfluPositionHistory( void )
287 
GetInfluPositionHistoryX(int HowLongPast)288 float GetInfluPositionHistoryX(int HowLongPast)
289 {
290 	int RingPosition;
291 
292 	HowLongPast >>= 1;
293 
294 	RingPosition = Me.current_zero_ring_index - HowLongPast;
295 
296 	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety
297 
298 	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
299 
300 	return Me.Position_History_Ring_Buffer[RingPosition].x;
301 }
302 
GetInfluPositionHistoryY(int HowLongPast)303 float GetInfluPositionHistoryY(int HowLongPast)
304 {
305 	int RingPosition;
306 
307 	HowLongPast >>= 1;
308 	RingPosition = Me.current_zero_ring_index - HowLongPast;
309 
310 	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety
311 
312 	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
313 
314 	return Me.Position_History_Ring_Buffer[RingPosition].y;
315 }
316 
GetInfluPositionHistoryZ(int HowLongPast)317 float GetInfluPositionHistoryZ(int HowLongPast)
318 {
319 	int RingPosition;
320 
321 	HowLongPast >>= 1;
322 	RingPosition = Me.current_zero_ring_index - HowLongPast;
323 
324 	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety
325 
326 	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
327 
328 	return Me.Position_History_Ring_Buffer[RingPosition].z;
329 }
330 
331 /**
332  * This function should check if the Tux is still ok, i.e. if he is still
333  * alive or if the death sequence should be initiated.
334  */
CheckIfCharacterIsStillOk()335 void CheckIfCharacterIsStillOk()
336 {
337 
338 	// Now we check if the main character is really still ok.
339 	//
340 	if (Me.energy <= 0) {
341 		ThouArtDefeated();
342 
343 		DebugPrintf(1, "\n%s():  Alternate end of function reached.", __FUNCTION__);
344 		return;
345 	}
346 
347 };				// void CheckIfCharacterIsStillOk ( )
348 
349 /**
350  * Even the Tux must not leave the map!  A sanity check is done here...
351  */
CheckForTuxOutOfMap()352 static void CheckForTuxOutOfMap()
353 {
354 	level *MoveLevel = curShip.AllLevels[Me.pos.z];
355 
356 	// Now perhaps the influencer is out of bounds, i.e. outside of the map.
357 	//
358 	if (!pos_inside_level(Me.pos.x, Me.pos.y, MoveLevel)) {
359 		fprintf(stderr, "\n\nplayer's last position: X=%f, Y=%f, Z=%d.\n", Me.pos.x, Me.pos.y, Me.pos.z);
360 		error_message(__FUNCTION__, "\
361 A player's Tux was found outside the map.\n\
362 This indicates either a bug in the FreedroidRPG code or\n\
363 a bug in the currently used map system of FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
364 	}
365 };				// void CheckForTuxOutOfMap ( )
366 
367 /**
368  * If an enemy was specified as the mouse move target, this enemy will
369  * maybe move here and there.  But this means that also the mouse move
370  * target of the influencer must adapt, which is done in this function.
371  */
tux_get_move_target_and_attack(gps * movetgt)372 void tux_get_move_target_and_attack(gps * movetgt)
373 {
374 	moderately_finepoint RemainingWay;
375 	float RemainingWayLength;
376 
377 	// if there is a mouse move target, we are not going to move towards the enemy
378 	if (Me.mouse_move_target.x != (-1)) {
379 		// If a combo action is pending, the mouse_move_target was defined relatively
380 		// to the item's level. We need here to define the position relatively to Tux's level.
381 		update_virtual_position(movetgt, &Me.mouse_move_target, Me.pos.z);
382 		return;
383 	}
384 
385 	enemy *t = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);
386 
387 	if (!t || (t->energy <= 0))	//No enemy or dead enemy, remove enemy
388 	{
389 		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
390 		movetgt->x = -1;
391 		movetgt->y = -1;
392 		movetgt->z = -1;
393 		return;
394 	}
395 
396 	update_virtual_position(&t->virt_pos, &t->pos, Me.pos.z);
397 
398 	// If we have a ranged weapon in hand, there is no need to approach the
399 	// enemy in question.  We just try to fire a shot, and return.
400 	//
401 	if (Me.weapon_item.type != (-1)) {
402 		if (!ItemMap[Me.weapon_item.type].weapon_is_melee) {	//ranged weapon
403 			if (!is_friendly(t->faction, FACTION_SELF))
404 				tux_wants_to_attack_now(FALSE);
405 			movetgt->x = -1;
406 			movetgt->y = -1;
407 			movetgt->z = -1;
408 			return;
409 		}
410 	}
411 	// Move to melee distance
412 	//
413 	RemainingWay.x = t->virt_pos.x - Me.pos.x;
414 	RemainingWay.y = t->virt_pos.y - Me.pos.y;
415 
416 	RemainingWayLength = sqrt(RemainingWay.x * RemainingWay.x + RemainingWay.y * RemainingWay.y);
417 
418 	if (RemainingWayLength > 0.05) {
419 		RemainingWay.x = (RemainingWay.x / RemainingWayLength) * (RemainingWayLength - (BEST_MELEE_DISTANCE - 0.1));
420 		RemainingWay.y = (RemainingWay.y / RemainingWayLength) * (RemainingWayLength - (BEST_MELEE_DISTANCE - 0.1));
421 	}
422 
423 	if ((RemainingWayLength <= BEST_MELEE_DISTANCE * sqrt(2) + 0.01) && (!is_friendly(t->faction, FACTION_SELF))) {
424 		tux_wants_to_attack_now(FALSE);
425 	}
426 	// New move target.
427 	movetgt->x = Me.pos.x + RemainingWay.x;
428 	movetgt->y = Me.pos.y + RemainingWay.y;
429 	movetgt->z = Me.pos.z;
430 
431 	return;
432 }				// void tux_get_move_target_and_attack( )
433 
434 /**
435  * Actually move Tux towards the target.
436  */
move_tux_according_to_his_speed()437 static void move_tux_according_to_his_speed()
438 {
439 
440 	float planned_step_x;
441 	float planned_step_y;
442 
443 	// Now we move influence according to current speed.  But there has been a problem
444 	// reported from people, that the influencer would (*very* rarely) jump through walls
445 	// and even out of the ship.  This has *never* occurred on my fast machine.  Therefore
446 	// I assume that the problem is related to sometimes very low frame rates on these machines.
447 	// So, we do a sanity check not to make steps too big.
448 	//
449 	// And on machines with FPS << 20, it will certainly alter the game behavior, so people
450 	// should really start using a Pentium or better machine.
451 	//
452 	planned_step_x = Me.speed.x * Frame_Time();
453 	planned_step_y = Me.speed.y * Frame_Time();
454 
455 	Me.pos.x += planned_step_x;
456 	Me.pos.y += planned_step_y;
457 
458 	if (((int)GetInfluPositionHistoryX(0) != (int)Me.pos.x || ((int)GetInfluPositionHistoryY(0) != (int)Me.pos.y))) {
459 		event_position_changed(Me.pos, FALSE);
460 	}
461 
462 	// If the Tux got stuck, i.e. if he got no speed at all and still is
463 	// currently not in a 'passable' position, the fallback handling needs
464 	// to be applied to move the Tux out of the offending obstacle (i.e.
465 	// simply away from the offending obstacles center)
466 	//
467 	if ((fabs(Me.speed.x) < 0.1) && (fabs(Me.speed.y) < 0.1)) {
468 		// So there is no speed, so we check for passability...
469 		//
470 		if (!SinglePointColldet(Me.pos.x, Me.pos.y, Me.pos.z, &WalkablePassFilter)) {
471 			// Now it's time to launch the stuck-fallback handling...
472 			//
473 			DebugPrintf(-3, "\nTux looks stuck...ESCAPING just for this frame...");
474 			float new_x = Me.pos.x;
475 			float new_y = Me.pos.y;
476 			int rtn = EscapeFromObstacle(&new_x, &new_y, Me.pos.z, &WalkablePassFilter);
477 			if (!rtn) {
478 				DebugPrintf(-3, "\nNo escape position found around Tux... Looking in position history...");
479 				// First : look for a suitable position in Tux's position_history
480 				int i;
481 				float old_x;
482 				float old_y;
483 				for (i = 1; i < 10; i++) {
484 					if (GetInfluPositionHistoryZ(10 * i) == Me.pos.z) {
485 						old_x = GetInfluPositionHistoryX(10 * i);
486 						old_y = GetInfluPositionHistoryY(10 * i);
487 						if (old_x != Me.pos.x && old_y != Me.pos.y
488 						    && SinglePointColldet(old_x, old_y, Me.pos.z, &WalkablePassFilter)) {
489 							// Found...
490 							Me.pos.x = old_x;
491 							Me.pos.y = old_y;
492 							break;
493 						}
494 					}
495 				}
496 				// If no luck, last fallback
497 				if (i == 10) {
498 					DebugPrintf(-3, "\nNo luck with position_history, last fallback...");
499 					// Get a random direction, and move by random length from 0.5 to 1.5.
500 					// With some luck, Tux will escape now, or in the future tries
501 					Me.pos.x += ((0.5 + (float)MyRandom(10) / 10.0) * (MyRandom(10) < 5)) ? 1 : -1;
502 					Me.pos.y += ((0.5 + (float)MyRandom(10) / 10.0) * (MyRandom(10) < 5)) ? 1 : -1;
503 				}
504 			} else {
505 				Me.pos.x = new_x;
506 				Me.pos.y = new_y;
507 			}
508 		}
509 	}
510 }
511 
512 /**
513  * This function contains dumb movement code that, without any checks nor
514  * refinement, calculates the direction and speed in which Tux has to move to
515  * reach the target position.
516  *
517  * Returns TRUE if the target has been sufficiently approximated.
518  */
move_tux_towards_raw_position(float x,float y)519 static int move_tux_towards_raw_position(float x, float y)
520 {
521 	moderately_finepoint RemainingWay;
522 	moderately_finepoint planned_step;
523 	float squared_length, length;
524 
525 	if (Me.energy <= 0)
526 		return FALSE;
527 
528 	RemainingWay.x = -Me.pos.x + x;
529 	RemainingWay.y = -Me.pos.y + y;
530 
531 	squared_length = RemainingWay.x * RemainingWay.x + RemainingWay.y * RemainingWay.y;
532 
533 	// Maybe the remaining way is VERY small!  Then we must not do
534 	// a division at all.  We also need not do any movement, so the
535 	// speed can be eliminated and we're done here.
536 	//
537 	if (squared_length < DIST_TO_INTERM_POINT * DIST_TO_INTERM_POINT) {
538 		Me.speed.x = 0;
539 		Me.speed.y = 0;
540 		return (TRUE);
541 	}
542 	// Now depending on whether the running key is pressed or not,
543 	// we have the Tux go on running speed or on walking speed.
544 	//
545 	length = sqrt(squared_length);
546 
547 	if (Me.running_power <= 0) {
548 		Me.running_must_rest = TRUE;
549 	}
550 
551 	if (Me.running_must_rest) {
552 		GameConfig.autorun_activated = 0;
553 		planned_step.x = RemainingWay.x * TUX_WALKING_SPEED / length;
554 		planned_step.y = RemainingWay.y * TUX_WALKING_SPEED / length;
555 	}
556 
557 	if ((LeftCtrlPressed() || GameConfig.autorun_activated) &&
558 		!(LeftCtrlPressed() && GameConfig.autorun_activated) && (!Me.running_must_rest)) {
559 		float running_speed = get_tux_running_speed();
560 		planned_step.x = RemainingWay.x * running_speed / length;
561 		planned_step.y = RemainingWay.y * running_speed / length;
562 	} else {
563 		planned_step.x = RemainingWay.x * TUX_WALKING_SPEED / length;
564 		planned_step.y = RemainingWay.y * TUX_WALKING_SPEED / length;
565 	}
566 
567 	// Now that the speed is set, we can start to make the step
568 	//
569 	Me.speed.x = planned_step.x;
570 	Me.speed.y = planned_step.y;
571 	Me.meters_traveled += 0.1;
572 	// If we might step over the target,
573 	// we reduce the speed.
574 	//
575 	if (fabsf(planned_step.x * Frame_Time()) >= fabsf(RemainingWay.x))
576 		Me.speed.x = RemainingWay.x / Frame_Time();
577 	if (fabsf(planned_step.y * Frame_Time()) >= fabsf(RemainingWay.y))
578 		Me.speed.y = RemainingWay.y / Frame_Time();
579 
580 	// In case we have reached our target, we can remove this mouse_move_target again,
581 	// but also if we have been thrown onto a different level, we cancel our current
582 	// mouse move target...
583 	//
584 	if (((fabsf(RemainingWay.y) <= DISTANCE_TOLERANCE) && (fabsf(RemainingWay.x) <= DISTANCE_TOLERANCE))) {
585 		return (TRUE);
586 	}
587 
588 	return (FALSE);
589 }
590 
591 /**
592  *
593  *
594  */
move_tux_towards_intermediate_point(void)595 static void move_tux_towards_intermediate_point(void)
596 {
597 	int i;
598 
599 	/* If we have no intermediate point, Tux has arrived at the target. */
600 	if (Me.next_intermediate_point[0].x == -1) {
601 		/* First, stop Tux. */
602 		Me.speed.x = 0;
603 		Me.speed.y = 0;
604 
605 		if (Me.mouse_move_target.z == -1) {
606 			// Nothing was targeted
607 			return;
608 		}
609 
610 		/* We might have a combo_action, that can occur on the end of any
611 		 * course, like e.g. open a chest or pick up some item. */
612 		level *lvl = curShip.AllLevels[Me.mouse_move_target.z];
613 		switch (Me.mouse_move_target_combo_action_type) {
614 		case NO_COMBO_ACTION_SET:
615 			break;
616 		case COMBO_ACTION_OBSTACLE:
617 			get_obstacle_spec(lvl->obstacle_list[Me.mouse_move_target_combo_action_parameter].type)->action_fn(
618                     lvl,
619                     Me.mouse_move_target_combo_action_parameter);
620 			break;
621 		case COMBO_ACTION_PICK_UP_ITEM:
622 			// If Tux arrived at destination, pick up the item and give it to the player
623 			if (check_for_items_to_pickup(lvl, Me.mouse_move_target_combo_action_parameter)) {
624 				item *it = &lvl->ItemList[Me.mouse_move_target_combo_action_parameter];
625 
626 				if (GameConfig.Inventory_Visible) {
627 					// Special case: when the inventory screen is open, and there
628 					// is no enough room to place the item, put it in player's hand
629 					if (!try_give_item(it)) {
630 						item_held_in_hand = it;
631 					}
632 				} else {
633 					// Put the item into player's inventory, or drop it on the floor
634 					// if there is no enough room.
635 					give_item(it);
636 				}
637 			}
638 
639 			break;
640 		default:
641 			error_message(__FUNCTION__, "Unhandled combo action for intermediate course encountered!", PLEASE_INFORM | IS_FATAL);
642 			break;
643 		}
644 
645 		Me.mouse_move_target.x = -1;
646 		Me.mouse_move_target.y = -1;
647 		Me.mouse_move_target.z = -1;
648 		Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
649 		return;
650 	}
651 
652 	/* Stop Tux when he's close enough to an item to pick it up. */
653 	if (Me.mouse_move_target_combo_action_type == COMBO_ACTION_PICK_UP_ITEM) {
654 		float distance = calc_distance(Me.pos.x, Me.pos.y,
655 									   Me.mouse_move_target.x, Me.mouse_move_target.y);
656 		if (distance < ITEM_TAKE_DIST &&
657 			DirectLineColldet(Me.pos.x, Me.pos.y, Me.mouse_move_target.x,
658 							  Me.mouse_move_target.y, Me.pos.z, NULL))
659 		{
660 			for (i = 0; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++) {
661 				Me.next_intermediate_point[i].x = -1;
662 				Me.next_intermediate_point[i].y = -1;
663 			}
664 			return;
665 		}
666 	}
667 
668 	// Move Tux towards the next intermediate course point
669 	if (move_tux_towards_raw_position(Me.next_intermediate_point[0].x, Me.next_intermediate_point[0].y)) {
670 		DebugPrintf(DEBUG_TUX_PATHFINDING, "\nMOVING ON TO NEXT INTERMEDIATE WAYPOINT! ");
671 		for (i = 1; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++) {
672 			Me.next_intermediate_point[i - 1].x = Me.next_intermediate_point[i].x;
673 			Me.next_intermediate_point[i - 1].y = Me.next_intermediate_point[i].y;
674 		}
675 	}
676 }
677 
678 /**
679  * This function moves the influencer, adjusts his speed according to
680  * keys pressed and also adjusts his status and current "phase" of his
681  * rotation.
682  */
move_tux()683 void move_tux()
684 {
685 	static gps last_given_course_target = { -2, -2, -2 };
686 
687 	// check, if the influencer is still ok
688 	CheckIfCharacterIsStillOk();
689 
690 	// We store the influencers position for the history record and so that others
691 	// can follow his trail.
692 	//
693 	Me.current_zero_ring_index++;
694 	Me.current_zero_ring_index %= MAX_INFLU_POSITION_HISTORY;
695 	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].x = Me.pos.x;
696 	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].y = Me.pos.y;
697 	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].z = Me.pos.z;
698 
699 
700 	if (Me.paralyze_duration) {
701 		Me.speed.x = 0;
702 		Me.speed.y = 0;
703 		return;		//If tux is paralyzed, we do nothing more
704 	}
705 	// As a preparation for the later operations, we see if there is
706 	// a living droid set as a target, and if yes, we correct the move
707 	// target to something suiting that new droids position.
708 	//
709 	gps move_target;
710 
711 	tux_get_move_target_and_attack(&move_target);
712 
713 	if (move_target.x != -1) {
714 		// For optimisation purposes, we'll not do anything unless a new target
715 		// has been given.
716 		//
717 		if (!((fabsf(move_target.x - last_given_course_target.x) < 0.3) &&
718 		      (fabsf(move_target.y - last_given_course_target.y) < 0.3))) {
719 			freeway_context frw_ctx = { FALSE, {NULL, NULL} };
720 			pathfinder_context pf_ctx = { &WalkableWithMarginPassFilter, &frw_ctx };
721 			moderately_finepoint target_point = { move_target.x, move_target.y };
722 			if (!set_up_intermediate_course_between_positions
723 			    (&Me.pos, &target_point, &Me.next_intermediate_point[0], MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX, &pf_ctx)) {
724 				// A path was not found.
725 				// If a combo action was set, halt it.
726 				if (Me.mouse_move_target_combo_action_type != NO_COMBO_ACTION_SET) {
727 					Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
728 					Me.mouse_move_target.x = Me.pos.x;
729 					Me.mouse_move_target.y = Me.pos.y;
730 					Me.mouse_move_target.z = Me.pos.z;
731 				}
732 			} else {
733 				last_given_course_target.x = move_target.x;
734 				last_given_course_target.y = move_target.y;
735 				last_given_course_target.z = move_target.z;
736 			}
737 		}
738 	}
739 
740 	// If there is a mouse move target present, we move towards that.
741 	move_tux_towards_intermediate_point();
742 
743 	// Perhaps the player has pressed the right mouse button, indicating the use
744 	// of the currently selected special function or spell.
745 	//
746 	HandleCurrentlyActivatedSkill();
747 
748 	// Maybe we need to fire a bullet or set a new mouse move target
749 	// for the new move-to location
750 	// There are currently two different input systms in use - event based and state based.
751 	// In order to maintain compatibility between the two, a game_map widget is added on the
752 	// game main widget to detect how user input should be handled. Therefore, when the mouse
753 	// is over the game_map widget (the widget is not in its DEFAULT state), no further event handling
754 	// is done by the widget system and AnalyzePlayersMouseClick is called.
755 	if (game_map->state != DEFAULT)
756 		AnalyzePlayersMouseClick();
757 
758 	if (MouseLeftPressed())
759 		no_left_button_press_in_previous_analyze_mouse_click = FALSE;
760 	else
761 		no_left_button_press_in_previous_analyze_mouse_click = TRUE;
762 
763 	// During inventory operations, there should not be any (new) movement
764 	//
765 	if (item_held_in_hand != NULL) {
766 		Me.mouse_move_target.x = Me.pos.x;
767 		Me.mouse_move_target.y = Me.pos.y;
768 		Me.mouse_move_target.z = Me.pos.z;
769 		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
770 		return;
771 	}
772 
773 	limit_tux_speed();
774 
775 	move_tux_according_to_his_speed();
776 
777 	animate_tux();
778 }
779 
780 /**
781  * This function decrements Tux's health and increments the relevant statistic
782  * variable.
783  */
hit_tux(float damage)784 void hit_tux(float damage)
785 {
786 	if (Me.god_mode)
787 		return;
788 
789 	if (Me.energy < 0)
790 		return;
791 
792 	Me.energy -= damage;
793 
794 	if (damage > Me.energy / 10)
795 		tux_scream_sound();
796 }
797 
798 
799 /**
800  * This function computes to current animation keyframe of Tux (Me.phase).
801  *
802  * Depending of Tux's current state (attacking, standing, walking, running),
803  * an animation is chosen, defined by a keyframed specification and an "animation
804  * progress cursor". This cursor progresses from 0 to 1 (possibly looping for
805  * some animations).
806  *
807  * The current keyframe is then: first_keyframe + cursor * nb_keyframes
808  *
809  * The way a cursor progresses from 0 to 1 depends on the kind of the animation.
810  * For example, the attack animation is a duration-based animation, so its cursor
811  * progresses as time progresses. The walk animation is based on the distance
812  * covered by Tux, so its cursor is a function of distance.
813  *
814  * Note on walking/running animations:
815  * Those animations are running in loop, and we can change from walk to run
816  * in the middle of the animation. In order to have a seamless transition
817  * between the animations, we do not reset the progress cursor's value.
818  * So those animations share a common animation progress cursor (Me.walk_cycle_phase).
819  */
animate_tux()820 void animate_tux()
821 {
822 	static int play_step_sound = 0;
823 	int tux_is_running = FALSE;
824 
825 	// If Tux is paralyzed, show him as standing still.
826 	if (Me.paralyze_duration) {
827 		Me.walk_cycle_phase = 0.0;
828 		Me.phase = tux_anim.standing_keyframe;
829 
830 		return;
831 	}
832 
833 	// Handle the case of Tux just getting hit
834 	//
835 	// Note: We do not yet have keyframes for such an animation, so those
836 	// commented lines are only a place-holder for future extension.
837 
838 	/*
839 	if (Me.got_hit_time != -1) {
840 		Me.phase = .....
841 		return;
842 	}
843 	*/
844 
845 	// Attack animation
846 	// (duration-based animation, no loop)
847 
848 	if (Me.weapon_swing_time != -1) {
849 		if (tux_anim.attack.nb_keyframes != 0) {
850 			float progress_cursor = (float)Me.weapon_swing_time / tux_anim.attack.duration;
851 			Me.phase = tux_anim.attack.first_keyframe + progress_cursor * (float)(tux_anim.attack.nb_keyframes);
852 
853 			// Clamp to maximum keyframe value
854 			if ((int)Me.phase > tux_anim.attack.last_keyframe) {
855 				Me.phase = tux_anim.attack.last_keyframe;
856 			}
857 		} else {
858 			Me.phase = 0;
859 		}
860 
861 		// Reset walk/run animation progress cursor
862 		Me.walk_cycle_phase = 0.0;
863 
864 		// Stop the time counter when the end of the animation is reached
865 		if (Me.weapon_swing_time > tux_anim.attack.duration)
866 			Me.weapon_swing_time = -1;
867 
868 		return;
869 	}
870 
871 	// Breathe animation (launched when Tux's speed is very low).
872 	// Currently, there is no such animation, so we only display Tux at its
873 	// standing position.
874 
875 	if (fabs(Me.speed.x) + fabs(Me.speed.y) < 0.3) {
876 		Me.walk_cycle_phase = 0.0;
877 		Me.phase = tux_anim.standing_keyframe;
878 
879 		return;
880 	}
881 
882 	// Walk/run animation
883 	// (distance-based animation, loop)
884 	// The progress cursor is: distance_covered_by_tux / distance_covered_during_one_sequence
885 	//
886 	// There is no way to 'statically' compute the distance covered by Tux,
887 	// so it is computed 'dynamically', i.e. incrementally, by adding the
888 	// distance covered since last frame.
889 	// The progress cursor thus has to be stored (Me.walk_cycle_phase)
890 
891 	/* Choose animation spec depending on Tux speed */
892 	struct distancebased_animation *anim_spec;
893 
894 	float my_speed = sqrt(Me.speed.x * Me.speed.x + Me.speed.y * Me.speed.y);
895 
896 	if (my_speed <= (TUX_WALKING_SPEED + TUX_RUNNING_SPEED) * 0.5) {
897 		anim_spec = &(tux_anim.walk);
898 		tux_is_running = FALSE;
899 	} else {
900 		anim_spec = &(tux_anim.run);
901 		tux_is_running = TRUE;
902 	}
903 
904 	if (anim_spec->nb_keyframes != 0) {
905 		/* Set the progress cursor */
906 		Me.walk_cycle_phase += (Frame_Time() * my_speed) / anim_spec->distance;
907 
908 		/* Loop of progress cursor */
909 		if (tux_is_running) {
910 			if (play_step_sound == 0 && Me.walk_cycle_phase >= 0.3) {
911 				play_sound_v("effects/tux_footstep.ogg", 0.125);
912 				play_step_sound = 1;
913 			} else if (play_step_sound == 1 && Me.walk_cycle_phase >= 0.8) {
914 				play_sound_v("effects/tux_footstep.ogg", 0.125);
915 				play_step_sound = 2;
916 			}
917 		}
918 		while (Me.walk_cycle_phase > 1.0) {
919 			Me.walk_cycle_phase -= 1.0;
920 			play_step_sound = 0;
921 		}
922 
923 		/* Set current animation keyframe */
924 		Me.phase = anim_spec->first_keyframe + Me.walk_cycle_phase * anim_spec->nb_keyframes;
925 		if ((int)Me.phase > anim_spec->last_keyframe) {
926 			Me.phase = anim_spec->last_keyframe;
927 		}
928 	} else {
929 		Me.walk_cycle_phase = 0.0;
930 		Me.phase = tux_anim.standing_keyframe;
931 	}
932 }
933 
934 /**
935  * This function creates several explosions around the location where the
936  * influencer is (was) positioned.  It is used after the influencers
937  * death to make his death more spectacular.
938  */
start_tux_death_explosions(void)939 void start_tux_death_explosions(void)
940 {
941 	int i;
942 	int counter;
943 
944 	DebugPrintf(1, "\n%s(): Real function call confirmed.", __FUNCTION__);
945 
946 	// create a few shifted explosions...
947 	for (i = 0; i < 10; i++) {
948 
949 		// find a free blast
950 		counter = 0;
951 		while (AllBlasts[counter++].type != INFOUT) ;
952 		counter -= 1;
953 		if (counter >= MAXBLASTS) {
954 			error_message(__FUNCTION__, "Ran out of blasts!!!", PLEASE_INFORM | IS_FATAL);
955 		}
956 		AllBlasts[counter].type = DROIDBLAST;
957 		AllBlasts[counter].pos.x = Me.pos.x - 0.125 / 2 + MyRandom(10) * 0.05;
958 		AllBlasts[counter].pos.y = Me.pos.y - 0.125 / 2 + MyRandom(10) * 0.05;
959 		AllBlasts[counter].phase = i;
960 	}
961 
962 	DebugPrintf(1, "\n%s(): Usual end of function reached.", __FUNCTION__);
963 
964 };				// void start_tux_death_explosions ( void )
965 
966 /**
967  * This function opens a menu when tux dies, asking the
968  * player if he/she wants to load latest or backup game,
969  * quit to main menu or quit the game.
970  */
do_death_menu()971 void do_death_menu()
972 {
973 	char *MenuTexts[100];
974 	int done = FALSE;
975 	int MenuPosition = 1;
976 	int i;
977 
978 	game_status = INSIDE_MENU;
979 
980 	input_handle();
981 
982 	enum {
983 		LOAD_LATEST_POSITION = 1,
984 		LOAD_BACKUP_POSITION,
985 		QUIT_TO_MAIN_POSITION,
986 		QUIT_POSITION
987 	};
988 	while (!done) {
989 		i = 0;
990 		MenuTexts[i++] = _("Load Latest");
991 		MenuTexts[i++] = _("Load Backup");
992 		if (game_root_mode == ROOT_IS_GAME) {
993 			MenuTexts[i++] = _("Quit to Main Menu");
994 		} else { // if (game_root_mode == ROOT_IS_LVLEDIT) {
995 			MenuTexts[i++] = _("Return to Editor");
996 		}
997 		MenuTexts[i++] = _("Exit FreedroidRPG");
998 		MenuTexts[i++] = "";
999 
1000 		MenuPosition = DoMenuSelection("", MenuTexts, 1, "--GAME_BACKGROUND--", Menu_Font);
1001 
1002 		switch (MenuPosition) {
1003 		case LOAD_LATEST_POSITION:
1004 			load_game();
1005 			done = !done;
1006 			break;
1007 		case LOAD_BACKUP_POSITION:
1008 			load_backup_game();
1009 			done = !done;
1010 			break;
1011 		case QUIT_TO_MAIN_POSITION:
1012 			if (game_root_mode == ROOT_IS_GAME) {
1013 				GameOver = TRUE;
1014 			}
1015 			done = !done;
1016 			break;
1017 		case QUIT_POSITION:
1018 			Terminate(EXIT_SUCCESS);
1019 			break;
1020 		default:
1021 			done = !done;
1022 			break;
1023 		}
1024 	}
1025 }
1026 
1027 /**
1028  * This function checks if the influencer is currently colliding with an
1029  * enemy and throws him back in that case.
1030  */
check_tux_enemy_collision(void)1031 void check_tux_enemy_collision(void)
1032 {
1033 	float xdist;
1034 	float ydist;
1035 
1036 	enemy *erot, *nerot;
1037 	BROWSE_LEVEL_BOTS_SAFE(erot, nerot, Me.pos.z) {
1038 
1039 		if (erot->type == (-1) || erot->pure_wait)
1040 			continue;
1041 
1042 		// We determine the distance and back out immediately if there
1043 		// is still one whole square distance or even more...
1044 		//
1045 		xdist = Me.pos.x - erot->pos.x;
1046 		if (fabs(xdist) > 1)
1047 			continue;
1048 		ydist = Me.pos.y - erot->pos.y;
1049 		if (fabs(ydist) > 1)
1050 			continue;
1051 
1052 		// Now at this point we know, that we are pretty close.  It is time
1053 		// to calculate the exact distance and to see if the exact distance
1054 		// indicates a collision or not, in which case we can again back out
1055 		//
1056 		//
1057 		if ((fabsf(xdist) >= 2.0 * 0.25) || (fabsf(ydist) >= 2.0 * 0.25))
1058 			continue;
1059 
1060 		erot->pure_wait = WAIT_COLLISION;
1061 
1062 		short int swap = erot->nextwaypoint;
1063 		erot->nextwaypoint = erot->lastwaypoint;
1064 		erot->lastwaypoint = swap;
1065 
1066 	}
1067 
1068 };				// void check_tux_enemy_collision( void )
1069 
1070 /**
1071  * This function checks if there is some living droid below the current
1072  * mouse cursor and returns the index number of this droid in the array.
1073  *
1074  * Earlier we did this by computing the map location the mouse was pointing
1075  * to and using that for the computation of the distance to droid coordinates.
1076  * The problem with this method is, that some droids are very 'high' in
1077  * the sense that the graphics (e.g. 302 body) is very far away from the
1078  * 'foot' point, where the droid is in X-Y coordinates on the map.  Therefore
1079  * some correction has to be done to fix this.  We can't use the map position
1080  * of the mouse any more... except maybe to exclude some bots from the start.
1081  *
1082  */
GetLivingDroidBelowMouseCursor()1083 enemy *GetLivingDroidBelowMouseCursor()
1084 {
1085 	gps mouse_vpos, mouse_pos;
1086 	int RotationModel, RotationIndex;
1087 	struct image *our_image;
1088 	enemy *this_bot;
1089 
1090 	// no interaction with the game when the world is frozen
1091 	if (world_frozen())
1092 		return NULL;
1093 
1094 	mouse_vpos.x = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, TRUE);
1095 	mouse_vpos.y = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, FALSE);
1096 	mouse_vpos.z = Me.pos.z;
1097 
1098 	// Find the actual level (and related position) where the mouse cursor is pointing at.
1099 	if (!resolve_virtual_position(&mouse_pos, &mouse_vpos))
1100 		return NULL;
1101 
1102 	// Browse all bots on that actual level, to find if the mouse is hovering one of them
1103 	BROWSE_LEVEL_BOTS(this_bot, mouse_pos.z) {
1104 		if (fabsf(this_bot->pos.x - mouse_pos.x) >= 5.0)
1105 			continue;
1106 		if (fabsf(this_bot->pos.y - mouse_pos.y) >= 5.0)
1107 			continue;
1108 
1109 		// We properly set the direction this robot is facing.
1110 		//
1111 		RotationIndex = set_rotation_index_for_this_robot(this_bot);
1112 
1113 		// We properly set the rotation model number for this robot, i.e.
1114 		// which shape (like 302, 247 or proffa) to use for drawing this bot.
1115 		//
1116 		RotationModel = set_rotation_model_for_this_robot(this_bot);
1117 
1118 		our_image = &(enemy_images[RotationModel][RotationIndex][(int)this_bot->animation_phase]);
1119 
1120 		update_virtual_position(&this_bot->virt_pos, &this_bot->pos, Me.pos.z);
1121 		if (mouse_cursor_is_on_that_image(this_bot->virt_pos.x, this_bot->virt_pos.y, our_image)) {
1122 			return this_bot;
1123 		}
1124 	}
1125 
1126 	return (NULL);
1127 
1128 };				// int GetLivingDroidBelowMouseCursor ( )
1129 
1130 /**
1131  * This function fires a bullet from the influencer in some direction,
1132  * no matter whether this is 'allowed' or not, not questioning anything
1133  * and SILENTLY TRUSTING THAT THIS TUX HAS A RANGED WEAPON EQUIPPED.
1134  */
perform_tux_ranged_attack(short int weapon_type,bullet * bullet_parameters,moderately_finepoint target_location)1135 void perform_tux_ranged_attack(short int weapon_type, bullet *bullet_parameters,
1136 		                       moderately_finepoint target_location)
1137 {
1138 	// Standard ranged attack process is to fire a bullet in the direction
1139 	// of the target, and have it advance step by step until it reaches 'something'.
1140 	// The starting position of the bullet is the gun's muzzle (note that we do not
1141 	// know exactly the length of a gun, so we will use an 'average' constant value of
1142 	// 'muzzle_offset_factor').
1143 	//
1144 	// But, sometime the targeted object can be so near Tux that it is actually
1145 	// between Tux and the gun's muzzle. In "real life", this should not even
1146 	// create a ranged attack, but for simplicity we will fire a bullet in that case.
1147 	// The bullet being already at its target position, we immediately call the code
1148 	// that detects bullet collisions and create bullet's explosion and damages.
1149 	// And only if no collision is detected do we fire the bullet.
1150 
1151 	float muzzle_offset_factor = 0.5;
1152 
1153 	// Search for the first free bullet entry in the bullet list and initialize
1154 	// with default values
1155 
1156 	int bullet_index = find_free_bullet_index();
1157 	if (bullet_index == -1) {
1158 		// We are out of free bullet slots.
1159 		// This should not happen, an error message was displayed,
1160 		return;
1161 	}
1162 
1163 	struct bullet *new_bullet = &(AllBullets[bullet_index]);
1164 	if (bullet_parameters)
1165 		memcpy(new_bullet, bullet_parameters, sizeof(struct bullet));
1166 	else
1167 		bullet_init_for_player(new_bullet, ItemMap[weapon_type].weapon_bullet_type, weapon_type);
1168 
1169 	// Set up recharging time for the Tux...
1170 	// The firewait time is modified by the ranged weapon skill
1171 
1172 	Me.busy_time = ItemMap[weapon_type].weapon_attack_time;
1173 	Me.busy_time *= RangedRechargeMultiplierTable[Me.ranged_weapon_skill];
1174 	Me.busy_type = WEAPON_FIREWAIT;
1175 	Me.weapon_swing_time = 0; // restart swing animation
1176 
1177 	// First case: The target is too near for an actual shot.
1178 	// We try an immediate hit. If nothing is hurt, or if the bullet traversed
1179 	// the target, then we will fire a bullet using the 'second case'.
1180 
1181 	float const dist_epsilon = 0.05;
1182 
1183 	if ( (fabs(target_location.x - Me.pos.x) <= muzzle_offset_factor + dist_epsilon) &&
1184 	     (fabs(target_location.y - Me.pos.y) <= muzzle_offset_factor + dist_epsilon) ) {
1185 
1186 		new_bullet->pos.x = target_location.x;
1187 		new_bullet->pos.y = target_location.y;
1188 		CheckBulletCollisions(bullet_index);
1189 
1190 		/* If the bullet exploded, we're done */
1191 		if (new_bullet->type == INFOUT)
1192 			return;
1193 	}
1194 
1195 	// Second case: The target is not near Tux (or a first-case bullet traversed
1196 	// the target)
1197 	// We fire a bullet into the direction of the target.
1198 
1199 	// Compute bullet's attack vector.
1200 	// This is a vector from Tux's position to the target's position.
1201 
1202 	moderately_finepoint attack_vector = { target_location.x - Me.pos.x,
1203 	                                       target_location.y - Me.pos.y };
1204 	double attack_norm = sqrt(attack_vector.x * attack_vector.x + attack_vector.y *attack_vector.y);
1205 	attack_vector.x /= attack_norm;
1206 	attack_vector.y /= attack_norm;
1207 
1208 	// The bullet starts from the muzzle's position.
1209 	// As said in the heading comment, we do not have enough informations to
1210 	// compute it, so we just use a small offset in the attack direction.
1211 
1212 	moderately_finepoint muzzle_position = { Me.pos.x + muzzle_offset_factor * attack_vector.x,
1213 	                                         Me.pos.y + muzzle_offset_factor * attack_vector.y };
1214 
1215 	// Set the bullet parameters
1216 
1217 	new_bullet->pos.x = muzzle_position.x;
1218 	new_bullet->pos.y = muzzle_position.y;
1219 	new_bullet->speed.x = attack_vector.x * ItemMap[weapon_type].weapon_bullet_speed;
1220 	new_bullet->speed.y = attack_vector.y * ItemMap[weapon_type].weapon_bullet_speed;
1221 	new_bullet->angle = -(atan2(attack_vector.y, attack_vector.x) * 180 / M_PI + 90 + 45);
1222 }
1223 
1224 /**
1225  * In some cases, the mouse button will be pressed, but still some signs
1226  * might tell us, that this mouse button press was not intended as a move
1227  * or fire command to the Tux.  This function checks for these cases.
1228  */
ButtonPressWasNotMeantAsFire()1229 int ButtonPressWasNotMeantAsFire()
1230 {
1231 	// If the influencer is holding something from the inventory
1232 	// menu via the mouse, also just return
1233 	//
1234 	if (item_held_in_hand != NULL)
1235 		return (TRUE);
1236 	if (timeout_from_item_drop > 0)
1237 		return (TRUE);
1238 
1239 	// Maybe the player just pressed the mouse button but INSIDE one of the character/skills/inventory
1240 	// screens.  Then of course we will not interpret the intention to fire the weapon but rather
1241 	// return from here immediately.
1242 	//
1243 	if (MouseLeftPressed() &&
1244 	    (GameConfig.Inventory_Visible || GameConfig.CharacterScreen_Visible || GameConfig.SkillScreen_Visible
1245 	     || GameConfig.skill_explanation_screen_visible)
1246 	    && !MouseCursorIsInUserRect(GetMousePos_x(), GetMousePos_y())) {
1247 		DebugPrintf(0, "\nCursor outside user-rect:\n  User_Rect.x=%d, User_Rect.w=%d, User_Rect.y=%d, User_Rect.h=%d.",
1248 			    User_Rect.x, User_Rect.w, User_Rect.y, User_Rect.h);
1249 		DebugPrintf(0, "\nCursor position: X=%d, Y=%d.", input_axis.x, input_axis.y);
1250 		return (TRUE);
1251 	}
1252 
1253 	return (FALSE);
1254 
1255 };				// int ButtonPressWasNotMeantAsFire ( )
1256 
1257 /**
1258  * At some point in the analysis of the users mouse click, we'll be
1259  * certain, that a fireing/weapon swing was meant with the click.  Once
1260  * this is knows, this function can be called to do the mechanics of the
1261  * weapon use.
1262  *
1263  * Return 0 if attack failed.
1264  */
perform_tux_attack(int use_mouse_cursor_for_targeting)1265 int perform_tux_attack(int use_mouse_cursor_for_targeting)
1266 {
1267 	moderately_finepoint target_location = { -1, -1 };
1268 	float target_angle;
1269 
1270 	// The attack target location can be the targeted enemy (if one was set), or an
1271 	// enemy below the mouse cursor, or by default the current mouse position.
1272 	// In case of an 'un-targeted' attack (A-pressed), the mouse position defines the
1273 	// target location.
1274 
1275 	if (!APressed() && !use_mouse_cursor_for_targeting) {
1276 		enemy *targeted_enemy = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);
1277 		if (!targeted_enemy) {
1278 			targeted_enemy = GetLivingDroidBelowMouseCursor();
1279 		}
1280 		if (targeted_enemy) {
1281 			// Use enemy's position to compute target location
1282 			update_virtual_position(&targeted_enemy->virt_pos, &targeted_enemy->pos, Me.pos.z);
1283 			target_location.x = targeted_enemy->virt_pos.x;
1284 			target_location.y = targeted_enemy->virt_pos.y;
1285 		} else {
1286 			// Use mouse position to compute target location
1287 			use_mouse_cursor_for_targeting = TRUE;
1288 		}
1289 	} else {
1290 		use_mouse_cursor_for_targeting = TRUE;
1291 	}
1292 
1293 	// If target location is defined by the mouse position, check if there is
1294 	// an obstacle or an enemy under the mouse, otherwise use the mouse location
1295 	if (use_mouse_cursor_for_targeting) {
1296 
1297 		// By default, use mouse location
1298 		target_location.x = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, TRUE);
1299 		target_location.y = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, FALSE);
1300 
1301 		// If there is an obstacle under the mouse cursor, use it as target
1302 		level *obs_lvl = NULL;
1303 		gps targeted_obstacle_vpos = { -1, -1, -1 };
1304 		int targeted_obstacle_index = clickable_obstacle_below_mouse_cursor(&obs_lvl, FALSE);
1305 		if (targeted_obstacle_index != -1) {
1306 			update_virtual_position(&targeted_obstacle_vpos, &obs_lvl->obstacle_list[targeted_obstacle_index].pos, Me.pos.z);
1307 			target_location.x = targeted_obstacle_vpos.x;
1308 			target_location.y = targeted_obstacle_vpos.y;
1309 		}
1310 
1311 		// If there is an enemy under the mouse cursor, use it as target if the enemy is nearest (using iso-norm)
1312 		enemy *targeted_enemy = GetLivingDroidBelowMouseCursor();
1313 		if (targeted_enemy != NULL) {
1314 			update_virtual_position(&targeted_enemy->virt_pos, &targeted_enemy->pos, Me.pos.z);
1315 			if ((targeted_obstacle_index == -1) ||
1316 				((targeted_enemy->virt_pos.x + targeted_enemy->virt_pos.y) >= (targeted_obstacle_vpos.x + targeted_obstacle_vpos.y))) {
1317 				target_location.x = targeted_enemy->virt_pos.x;
1318 				target_location.y = targeted_enemy->virt_pos.y;
1319 			}
1320 		}
1321 
1322 	}
1323 
1324 	// Turn Tux to face its target
1325 
1326 	target_angle = -(atan2(Me.pos.y - target_location.y, Me.pos.x - target_location.x) * 180/M_PI - 90 + 22.5);
1327 	Me.angle = target_angle;
1328 
1329 	/**
1330 	 * First case: Attack with a melee weapon (or with fists).
1331 	 */
1332 
1333 	if (Me.weapon_item.type == -1 || ItemMap[Me.weapon_item.type].weapon_is_melee) {
1334 
1335 		int hit_something = FALSE;
1336 
1337 		// A melee attack is a swing gesture, the impact point is thus a bit in front of
1338 		// Tux. We have no idea of the size of the weapon, so we use an 'average' constant
1339 		// value of 0.8 (impact_offset_y).
1340 
1341 		const float impact_offset_y = 0.8;
1342 
1343 		moderately_finepoint impact_point = { 0, -impact_offset_y };
1344 		RotateVectorByAngle(&impact_point, Me.angle);
1345 		impact_point.x += Me.pos.x;
1346 		impact_point.y += Me.pos.y;
1347 
1348 		// Find all enemies which are in a small area around the impact point
1349 		// and setup up a melee shot.
1350 		// TODO: also look for enemies on neighbor levels.
1351 
1352 		const float impact_area_size = 0.5;
1353 
1354 		enemy *erot, *nerot;
1355 		BROWSE_LEVEL_BOTS_SAFE(erot, nerot, Me.pos.z) {
1356 			if ( (fabsf(erot->pos.x - impact_point.x) > impact_area_size) ||
1357 			     (fabsf(erot->pos.y - impact_point.y) > impact_area_size) ||
1358 			     !DirectLineColldet(Me.pos.x, Me.pos.y, erot->pos.x, erot->pos.y, Me.pos.z, NULL)
1359 			   )
1360 				continue;
1361 
1362 			// Set up a melee attack
1363 			int shot_index = find_free_melee_shot_index();
1364 			if (shot_index == -1) {
1365 				// We are out of free melee shot slots.
1366 				// This should not happen, an error message was displayed,
1367 				return 0;
1368 			}
1369 
1370 			melee_shot *new_shot = &(AllMeleeShots[shot_index]);
1371 
1372 			new_shot->attack_target_type = ATTACK_TARGET_IS_ENEMY;
1373 			new_shot->mine = TRUE;
1374 			new_shot->bot_target_n = erot->id;
1375 			new_shot->bot_target_addr = erot;
1376 			new_shot->to_hit = Me.to_hit;
1377 			new_shot->damage = Me.base_damage + MyRandom(Me.damage_modifier);
1378 			new_shot->owner = -1;	//no "bot class number" owner
1379 			new_shot->time_to_hit = tux_anim.attack.duration / 2;
1380 
1381 			// Slow or paralyze enemies if the player has bonuses with those effects.
1382 			erot->frozen += Me.slowing_melee_targets;
1383 			erot->paralysation_duration_left += Me.paralyzing_melee_targets;
1384 
1385 			hit_something = TRUE;
1386 		}
1387 
1388 		// Also, we should check if there was perhaps a chest or box
1389 		// or something that can be smashed up, cause in this case, we
1390 		// must open Pandora's box now.
1391 
1392 		if (smash_obstacle(impact_point.x, impact_point.y, Me.pos.z))
1393 			hit_something = TRUE;
1394 
1395 		// Finally we add a new wait-counter, so that swings cannot be started
1396 		// in too rapid succession. Adjust that delay to take player's skill
1397 		// into account.
1398 
1399 		Me.busy_type = WEAPON_FIREWAIT;
1400 		Me.busy_time = 0.5; // default value
1401 		if (Me.weapon_item.type != -1)
1402 			Me.busy_time = ItemMap[Me.weapon_item.type].weapon_attack_time;
1403 		Me.busy_time *= MeleeRechargeMultiplierTable[Me.melee_weapon_skill];
1404 		Me.weapon_swing_time = 0; // restart swing animation
1405 
1406 		// Play a sound feedback
1407 
1408 		if (hit_something)
1409 			play_melee_weapon_hit_something_sound();
1410 		else
1411 			play_melee_weapon_missed_sound(&Me.pos);
1412 
1413 		return hit_something;
1414 	}
1415 
1416 	/**
1417 	 * Second case: Attack with a ranged weapon.
1418 	 * Note: we know that weapon_item.type != -1
1419 	 */
1420 
1421 	perform_tux_ranged_attack(Me.weapon_item.type, NULL, target_location);
1422 
1423 	fire_bullet_sound(ItemMap[Me.weapon_item.type].weapon_bullet_type, &Me.pos);
1424 
1425 	// We do not know if the bullet will hit something, but the bullet was fired,
1426 	// so that's a success...
1427 
1428 	return TRUE;
1429 }
1430 
1431 /**
1432  * Reload the ammo clip
1433  *
1434  *
1435  */
1436 
TuxReloadWeapon()1437 void TuxReloadWeapon()
1438 {
1439 	if (Me.weapon_item.type == -1)
1440 		return;		// Do not reload Tux's fists.
1441 
1442 	if (Me.paralyze_duration)
1443 		return;		// Do not reload when paralyzed.
1444 
1445 	if (ItemMap[Me.weapon_item.type].weapon_ammo_clip_size == Me.weapon_item.ammo_clip)
1446 		return;		// Clip full, return without reloading.
1447 
1448 	int ammo_type = get_item_type_by_id(ItemMap[Me.weapon_item.type].weapon_ammo_type);
1449 
1450 	int count = CountItemtypeInInventory(ammo_type);
1451 	if (count > ItemMap[Me.weapon_item.type].weapon_ammo_clip_size - Me.weapon_item.ammo_clip)
1452 		count = ItemMap[Me.weapon_item.type].weapon_ammo_clip_size - Me.weapon_item.ammo_clip;
1453 
1454 	if (!count)		//no ammo found, tell the player that he "has it in the baba"
1455 	{
1456 		No_Ammo_Sound();
1457 		No_Ammo_Sound();
1458 		// TRANSLATORS: Out of <ammo type>
1459 		append_new_game_message(_("Out of [s]%s[v]!"), _(item_specs_get_name(ammo_type)));
1460 		return;
1461 	}
1462 	int i;
1463 	for (i = 0; i < count; i++)
1464 		DeleteOneInventoryItemsOfType(ammo_type);
1465 	Me.weapon_item.ammo_clip += count;
1466 	play_sound(ItemMap[Me.weapon_item.type].weapon_reloading_sound);
1467 	Me.busy_time = ItemMap[Me.weapon_item.type].weapon_reloading_time;
1468 	Me.busy_time *= RangedRechargeMultiplierTable[Me.ranged_weapon_skill];
1469 	Me.busy_type = WEAPON_RELOAD;
1470 }
1471 
1472 /**
1473  * When the player has left-clicked into the game area (i.e. the isometric
1474  * display of the game world), we need to check if maybe the click was
1475  * targeted on a droid.
1476  * In case that was so, we need to start a dialog or maybe launch an
1477  * attack movement.
1478  */
check_for_droids_to_attack_or_talk_with()1479 void check_for_droids_to_attack_or_talk_with()
1480 {
1481 	/* NOTA : the call to GetLivingDroidBelowMouseCursor() does set the virt_pos attribute
1482 	 * of the found droid to be the bot's position relatively to Tux current level
1483 	 */
1484 	enemy *droid_below_mouse_cursor = GetLivingDroidBelowMouseCursor();
1485 
1486 	// Set a move action unless there's a droid below the cursor,
1487 	// 'A' is pressed,
1488 	// or the player has clicked to pickup an item and hasn't released the LMB
1489 	if (droid_below_mouse_cursor == NULL && (!APressed()) &&
1490 		(no_left_button_press_in_previous_analyze_mouse_click ||
1491 		Me.mouse_move_target_combo_action_type != COMBO_ACTION_PICK_UP_ITEM)) {
1492 
1493 		Me.mouse_move_target.x = translate_pixel_to_map_location(input_axis.x, input_axis.y, TRUE);
1494 		Me.mouse_move_target.y = translate_pixel_to_map_location(input_axis.x, input_axis.y, FALSE);
1495 		Me.mouse_move_target.z = Me.pos.z;
1496 		if (!ShiftPressed()) {
1497 			enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
1498 		}
1499 
1500 		return;
1501 	} else if (APressed()) {
1502 		tux_wants_to_attack_now(TRUE);
1503 		return;
1504 	}
1505 
1506 	if (droid_below_mouse_cursor != NULL &&
1507 	    DirectLineColldet(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y, Me.pos.z,
1508 			      &VisiblePassFilter)) {
1509 
1510 		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, droid_below_mouse_cursor);
1511 
1512 		enemy *e = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);
1513 		if (is_friendly(e->faction, FACTION_SELF)) {
1514 			if (no_left_button_press_in_previous_analyze_mouse_click) {
1515 				chat_with_droid(enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr));
1516 				enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
1517 			}
1518 
1519 			return;
1520 		}
1521 
1522 		if (!ShiftPressed()) {
1523 			Me.mouse_move_target.x = -1;
1524 			Me.mouse_move_target.y = -1;
1525 		}
1526 
1527 		if (Me.weapon_item.type >= 0) {
1528 			if ((ItemMap[Me.weapon_item.type].weapon_is_melee) &&
1529 			    (calc_distance(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y)
1530 			     > BEST_MELEE_DISTANCE + 0.1)) {
1531 
1532 				return;
1533 			}
1534 		} else if (calc_distance(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y)
1535 			   > BEST_MELEE_DISTANCE + 0.1) {
1536 
1537 			return;
1538 		}
1539 		// But if we're close enough or there is a ranged weapon in Tux hands,
1540 		// then we can finally start the attack motion right away...
1541 		//
1542 		tux_wants_to_attack_now(TRUE);
1543 	}
1544 };				// void check_for_droids_to_attack ( )
1545 
1546 /**
1547  * If the user clicked his mouse, this might have several reasons.  It
1548  * might happen to open some windows, pick up some stuff, smash a box,
1549  * move somewhere or fire a shot or make a weapon swing.
1550  *
1551  * Therefore it is not so easy to decide what to do upon a users mouse
1552  * click and so this function analyzes the situation and decides what to
1553  * do.
1554  */
AnalyzePlayersMouseClick()1555 static void AnalyzePlayersMouseClick()
1556 {
1557 	int tmp;
1558 
1559 	// This flag avoids the mouse_move_target to change while the user presses
1560 	// LMB to start a combo action.
1561 	static int wait_mouseleft_release = FALSE;
1562 
1563 	// No action is associated to MouseLeftRelease event or state.
1564 	//
1565 
1566 	if (!MouseLeftPressed()) {
1567 		wait_mouseleft_release = FALSE;
1568 		return;
1569 	}
1570 
1571 	if (ButtonPressWasNotMeantAsFire())
1572 		return;
1573 	if (no_left_button_press_in_previous_analyze_mouse_click) {
1574 		level *obj_lvl = NULL;
1575 
1576 		Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
1577 
1578 		if ((tmp = clickable_obstacle_below_mouse_cursor(&obj_lvl, TRUE)) != -1) {
1579 			get_obstacle_spec(obj_lvl->obstacle_list[tmp].type)->action_fn(obj_lvl, tmp);
1580 			if (Me.mouse_move_target_combo_action_type != NO_COMBO_ACTION_SET)
1581 				wait_mouseleft_release = TRUE;
1582 			return;
1583 		}
1584 
1585 		// If the inventory screen is open, let it manage any possibly picked item.
1586 		// Else, if the player left-clicked on an item, check if the item can be
1587 		// picked up. If so, get it and give it to the player.
1588 		// Note: if the item is too far away from Tux, check_for_items_to_pickup()
1589 		// creates a combo action to reach the item.
1590 		if (GameConfig.Inventory_Visible == FALSE) {
1591 			if ((tmp = get_floor_item_index_under_mouse_cursor(&obj_lvl)) != -1) {
1592 				if (check_for_items_to_pickup(obj_lvl, tmp)) {
1593 					// The item can be picked up immediately , so give it to the player
1594 					give_item(&obj_lvl->ItemList[tmp]);
1595 					wait_mouseleft_release = TRUE;
1596 				}
1597 				return;
1598 			}
1599 		}
1600 	}
1601 	// Just after the beginning of a combo action, and while LMB is
1602 	// always pressed, mouse_move_target must not be changed (so that
1603 	// the player's character will actually move to the combo action's target)
1604 
1605 	if (!wait_mouseleft_release)
1606 		check_for_droids_to_attack_or_talk_with();
1607 }
1608 
1609 
1610 /**
1611  *  Handles arrow key based movements
1612  */
set_movement_with_keys(int move_x,int move_y)1613 void set_movement_with_keys(int move_x, int move_y)
1614 {
1615 	float floor_center = 0.5;
1616 	int move_amplitude = 1 + GameConfig.autorun_activated;
1617 
1618 	// Center the target waypoint, or establish a new target waypoint if there isn't one already
1619 	if (Me.mouse_move_target.z != -1) {
1620 		Me.mouse_move_target.x = floor(Me.mouse_move_target.x) + floor_center;
1621 		Me.mouse_move_target.y = floor(Me.mouse_move_target.y) + floor_center;
1622 	} else {
1623 		Me.mouse_move_target.z = Me.pos.z;
1624 		Me.mouse_move_target.x = floor(Me.pos.x) + floor_center;
1625 		Me.mouse_move_target.y = floor(Me.pos.y) + floor_center;
1626 	}
1627 
1628 	//Restricts moving target to within 2 units from current position
1629 	if (fabs(Me.pos.x - (Me.mouse_move_target.x + move_x * move_amplitude)) <= 2 &&
1630 			fabs(Me.pos.y - (Me.mouse_move_target.y + move_y * move_amplitude)) <= 2) {
1631 		// Determine move amount
1632 		Me.mouse_move_target.x += move_x * move_amplitude;
1633 		Me.mouse_move_target.y += move_y * move_amplitude;
1634 	}
1635 }
1636 
free_tux()1637 void free_tux()
1638 {
1639 	int i;
1640 
1641 	free(Me.character_name);
1642 	Me.character_name = NULL;
1643 
1644 	clear_tux_mission_info();
1645 
1646 	// We mark all the big screen messages for this character
1647 	// as out of date, so they can be overwritten with new
1648 	// messages...
1649 	//
1650 	Me.BigScreenMessageIndex = 0;
1651 	for (i = 0; i < MAX_BIG_SCREEN_MESSAGES; i++) {
1652 		if (Me.BigScreenMessage[i]) {
1653 			free(Me.BigScreenMessage[i]);
1654 			Me.BigScreenMessage[i] = NULL;
1655 		}
1656 	}
1657 
1658 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
1659 		if (Me.Inventory[i].type != -1) {
1660 			delete_upgrade_sockets(&Me.Inventory[i]);
1661 		}
1662 	}
1663 	delete_upgrade_sockets(&Me.weapon_item);
1664 	delete_upgrade_sockets(&Me.drive_item);
1665 	delete_upgrade_sockets(&Me.armour_item);
1666 	delete_upgrade_sockets(&Me.shield_item);
1667 	delete_upgrade_sockets(&Me.special_item);
1668 }
1669 
1670 /**
1671  * Reset the data in struct tux, in order to start a new game/load a game.
1672  */
init_tux()1673 void init_tux()
1674 {
1675 	int i;
1676 
1677 	free_tux();
1678 
1679 	memset(&Me, 0, sizeof(struct tux));
1680 
1681 	Me.current_game_date = 0.0;
1682 	Me.power_bonus_end_date = -1;	// negative dates are always in the past...
1683 	Me.dexterity_bonus_end_date = -1;
1684 
1685 	Me.speed.x = 0;
1686 	Me.speed.y = 0;
1687 
1688 	Me.pos.x = 0;
1689 	Me.pos.y = 0;
1690 	Me.pos.z = -1;
1691 
1692 	Me.mouse_move_target.x = -1;
1693 	Me.mouse_move_target.y = -1;
1694 	Me.mouse_move_target.z = -1;
1695 
1696 	Me.teleport_anchor.x = 0;
1697 	Me.teleport_anchor.y = 0;
1698 	Me.teleport_anchor.z = -1;
1699 
1700 	enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
1701 
1702 	Me.god_mode = FALSE;
1703 
1704 	Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
1705 	Me.mouse_move_target_combo_action_parameter = -1;
1706 
1707 	Me.map_maker_is_present = FALSE;
1708 
1709 	Me.temperature = 0.0;
1710 
1711 	Me.health_recovery_rate = 0.2;
1712 	Me.cooling_rate = 0.2;
1713 
1714 	Me.busy_time = 0;
1715 	Me.busy_type = NONE;
1716 
1717 	Me.phase = 0;
1718 	Me.angle = 180;
1719 	Me.walk_cycle_phase = 0;
1720 	Me.weapon_swing_time = -1;
1721 	Me.MissionTimeElapsed = 0;
1722 	Me.got_hit_time = -1;
1723 
1724 	Me.points_to_distribute = 0;
1725 
1726 	// reset statistics
1727 	Me.meters_traveled = 0;
1728 	for (i = 0; i < Number_Of_Droid_Types + 1; i++) {
1729 		Me.destroyed_bots[i]    = 0;
1730 		Me.damage_dealt[i]      = 0;
1731 		Me.TakeoverSuccesses[i] = 0;
1732 		Me.TakeoverFailures[i]  = 0;
1733 	}
1734 
1735 	for (i = 0; i < MAX_LEVELS; i++) {
1736 		Me.HaveBeenToLevel[i] = FALSE;
1737 		Me.time_since_last_visit_or_respawn[i] = -1;
1738 	}
1739 
1740 	Me.Experience = 0;
1741 	Me.exp_level = 0;
1742 	Me.Gold = 0;
1743 
1744 	Me.readied_skill = 0;
1745 	for (i = 0; i < number_of_skills; i++) {
1746 		Me.skill_level[i] = SpellSkillMap[i].present_at_startup;
1747 	}
1748 
1749 	GameConfig.spell_level_visible = 0;
1750 
1751 	Me.melee_weapon_skill = 0;
1752 	Me.ranged_weapon_skill = 0;
1753 	Me.spellcasting_skill = 0;
1754 
1755 	Me.running_power_bonus = 0;
1756 
1757 	for (i = 0; i < 10; i++) {
1758 		Me.program_shortcuts[i] = -1;
1759 	}
1760 
1761 	Me.paralyze_duration = 0;
1762 	Me.invisible_duration = 0;
1763 	Me.nmap_duration = 0;
1764 
1765 	Me.readied_skill = 0;
1766 
1767 	Me.quest_browser_changed = 0;
1768 
1769 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
1770 		init_item(&Me.Inventory[i]);
1771 	}
1772 	init_item(&Me.weapon_item);
1773 	init_item(&Me.armour_item);
1774 	init_item(&Me.shield_item);
1775 	init_item(&Me.special_item);
1776 	init_item(&Me.drive_item);
1777 	item_held_in_hand = NULL;
1778 
1779 	clear_out_intermediate_points(&Me.pos, Me.next_intermediate_point, MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX);
1780 	Me.next_intermediate_point[0].x = -1;
1781 	Me.next_intermediate_point[0].y = -1;
1782 
1783 	Me.TextToBeDisplayed = "";
1784 	Me.TextVisibleTime = 0;
1785 
1786 	Me.base_physique = 20;
1787 	Me.base_strength = 10;
1788 	Me.base_dexterity = 15;
1789 	Me.base_cooling = 25;
1790 
1791 	UpdateAllCharacterStats();
1792 
1793 	Me.energy = Me.maxenergy;
1794 	Me.running_power = Me.max_running_power;
1795 }
1796 #undef _influ_c
1797