influ.c 56.9 KB
Newer Older
1
/*
2 3 4
 *
 *   Copyright (c) 1994, 2002, 2003 Johannes Prix
 *   Copyright (c) 1994, 2002 Reinhard Prix
5
 *   Copyright (c) 2004-2010 Arthur Huillet
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 *
 *  This file is part of Freedroid
 *
 *  Freedroid is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Freedroid is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
21 22
 *  along with Freedroid; see the file COPYING. If not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
 *  MA  02111-1307  USA
 *
 */

/**
 * This file contains all features, movement, firing, collision and
 * extras of the influencer.
 */

#define _influ_c

#include "system.h"

#include "defs.h"
#include "struct.h"
#include "global.h"
#include "proto.h"
#include "widgets/widgets.h"

#define BEST_MELEE_DISTANCE (0.8)
#define BEST_CHAT_DISTANCE (BEST_MELEE_DISTANCE+0.2)
#define DISTANCE_TOLERANCE (0.00002)

#define LEVEL_JUMP_DEBUG 1

static void CheckForTuxOutOfMap();
static void AnalyzePlayersMouseClick();

static int no_left_button_press_in_previous_analyze_mouse_click = FALSE;

/**
 *
 *
 */
float calc_distance(float pos1_x, float pos1_y, float pos2_x, float pos2_y)
{
	return sqrt((pos1_x - pos2_x) * (pos1_x - pos2_x) + (pos1_y - pos2_y) * (pos1_y - pos2_y));
};

/**
 *
 *
 */
float vect_len(moderately_finepoint our_vector)
{
	return (sqrt(powf(our_vector.x, 2) + powf(our_vector.y, 2)));
};				// float vect_len ( moderately_finepoint our_vector )

static float get_tux_running_speed(void)
{
	if (GameConfig.cheat_double_speed)
		return 2 * TUX_RUNNING_SPEED;

	return TUX_RUNNING_SPEED;
}

/**
 * This function adapts the influencers current speed to the maximal speed
 * possible for the influencer.
 *
 * This function also stops Tux from moving while fighting.
 */
static void limit_tux_speed()
{
	/* Stop all movement if Tux is currently inside attack animation.  This is
	 * to stop Tux from moving without his legs animating.
	 *
	 * NOTE REGARDING MELEE ATTACK.  Tux starts to swing before he has reached
	 * BEST_MELEE_DISTANCE.  If we stop before reaching this distance, Tux will
	 * continue to try to move to this distance between swings, which causes a
	 * jerking motion towards the enemy.  To stop this from happening, we force
	 * Tux NOT to stand still when he is first charging a new target.  He stands
	 * still ONLY when he changes target during a swing. */
	static enemy *previous_target;
	enemy *current_target = enemy_resolve_address(Me.current_enemy_target_n,
												  &Me.current_enemy_target_addr);

	if (Me.weapon_item.type >= 0) {
101
		int has_melee = ItemMap[Me.weapon_item.type].weapon_is_melee;
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
		if (Me.weapon_swing_time != -1 && (!has_melee
		   || (has_melee && (previous_target != current_target || current_target == NULL))))
		{
			Me.speed.x = 0;
			Me.speed.y = 0;
			return;
		} else {
			previous_target = current_target;
		}
	}

	/* Limit the speed when Tux is not attacking. */
	float speed = Me.speed.x * Me.speed.x + Me.speed.y * Me.speed.y;
	float running_speed = get_tux_running_speed();
	if (speed > running_speed * running_speed) {
		float ratio = (running_speed * running_speed) / speed;
		Me.speed.x *= ratio;
		Me.speed.y *= ratio;
	}
}

/**
124
 * When the player has requested an attack motion, we start the
125 126 127 128 129 130 131
 * corresponding code, that should try to attack, if that's currently
 * possible.
 */
void tux_wants_to_attack_now(int use_mouse_cursor_for_targeting)
{
	// Maybe the player requested an attack before the reload/retract
	// phase is completed.  In that case, we don't attack.
132

133 134 135
	if (Me.busy_time > 0) {
		return;
	}
136

137 138
	// If the Tux has a weapon and this weapon requires some ammunition, then
	// 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 142 143 144 145 146 147 148 149
	if (weapon_with_ammo) {
		if (Me.weapon_item.ammo_clip <= 0) {
			// So no ammunition... We should say so and reload...
			No_Ammo_Sound();
			// TRANSLATORS: Console msg when a weapon is empty
			append_new_game_message(_("%s empty, reloading..."), D_(item_specs_get_name(Me.weapon_item.type)));
			TuxReloadWeapon();

			return;
150 151 152
		}
	}

153 154 155 156 157 158 159 160
	// Try to attack

	int hit_something = perform_tux_attack(use_mouse_cursor_for_targeting);

	// If the attack failed, and the attack did not use ammunition, nothing
	// else is to be done. Same if the attack was done with bare fist.

	if ((!hit_something && !weapon_with_ammo) || (Me.weapon_item.type < 0)) {
161 162
		return;
	}
163 164 165 166 167 168

	// The weapon was used (i.e. a bullet was fired by a ranged weapon, or a
	// melee shot actually touched an enemy) and therefore the weapon looses
	// some of it's durability

	if (weapon_with_ammo || hit_something)
169 170
		DamageWeapon(&(Me.weapon_item));

171 172 173
	// Also if it uses ammunition, one charge is to be removed
	// But, the weapon can have been destroyed by the call to DamageWeapon(),
	// so the weapon's type is to be checked again.
174

175 176 177
	if (weapon_with_ammo)
		Me.weapon_item.ammo_clip--;
}
178 179

/**
180
 * The Tux might have cross a level's boundary. In that case, we
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
 * must move the Tux silently to the corresponding other level.
 */
void correct_tux_position_according_to_jump()
{
	gps old_mouse_move_target;
	gps oldpos = { Me.pos.x, Me.pos.y, Me.pos.z };
	gps newpos;

	// If current Tux position is inside current level, there's nothing to change
	//
	if (pos_inside_level(Me.pos.x, Me.pos.y, CURLEVEL()))
		return;

	// Else, try to retrieve the actual position
	//
	int pos_valid = resolve_virtual_position(&newpos, &oldpos);

	if (!pos_valid) {
		// We were not able to compute the actual position...
		CheckForTuxOutOfMap();
		return;
	}
	// Tux is on another level, teleport it
	// (note: Teleport() resets Me.mouse_move_target, so we have to restore it)
	//
	old_mouse_move_target.x = Me.mouse_move_target.x;
	old_mouse_move_target.y = Me.mouse_move_target.y;
	old_mouse_move_target.z = Me.mouse_move_target.z;

	Teleport(newpos.z, newpos.x, newpos.y, FALSE, FALSE);

	Me.mouse_move_target.x = old_mouse_move_target.x;
	Me.mouse_move_target.y = old_mouse_move_target.y;
	Me.mouse_move_target.z = old_mouse_move_target.z;

	// Update the mouse target position, if needed
	//
	// The purpose is to define mouse_move_target relatively to new Tux's level.
	// However, if Tux had to move from one level to one of its diagonal neighbor, it has
	// eventually not yet reach the final destination level.
	// So we first have to get the real mouse_target position, and then transform it into a
	// virtual position according to Tux's new level.
	//
	if (old_mouse_move_target.z != -1) {
		int rtn = resolve_virtual_position(&Me.mouse_move_target, &old_mouse_move_target);
		if (rtn)
			update_virtual_position(&Me.mouse_move_target, &Me.mouse_move_target, newpos.z);
		if (!rtn || (Me.mouse_move_target.x == -1)
				 || !SinglePointColldet(Me.mouse_move_target.x, Me.mouse_move_target.y, Me.mouse_move_target.z, NULL)) {
			Me.mouse_move_target.x = (-1);
			Me.mouse_move_target.y = (-1);
			Me.mouse_move_target.z = (-1);
		}
	}

	// Update the intermediate waypoints
	//
	// Intermediate waypoints are defined relatively to Tux's current level.
	// They thus also have to be updated<Fluzz>
	//
	int i;
	for (i = 0; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++)
	{
		if (Me.next_intermediate_point[i].x == -1)
			break;

		gps old_point = { Me.next_intermediate_point[i].x, Me.next_intermediate_point[i].y, oldpos.z };
		gps new_point;

		int rtn = resolve_virtual_position(&new_point, &old_point);
		if (rtn)
			update_virtual_position(&new_point, &new_point, newpos.z);

		if (!rtn || new_point.x == -1) {
			clear_out_intermediate_points(&newpos, Me.next_intermediate_point, MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX);
			break;
		}

		Me.next_intermediate_point[i].x = new_point.x;
		Me.next_intermediate_point[i].y = new_point.y;
	}

	// Even the Tux must not leave the map!  A sanity check is done
	// here...
	//
	CheckForTuxOutOfMap();

}				// correct_tux_position_according_to_jump ( )

/**
 * This function initializes the influencers position history, which is
 * a ring buffer and is needed for throwing the influencer back (only one
 * or two positions would be needed for that) and for influencers followers
 * to be able to track the influencers path (10000 or so positions are used
 * for that, and that's why it is a ring buffer).
 */
void InitInfluPositionHistory()
{
	int RingPosition;

	for (RingPosition = 0; RingPosition < MAX_INFLU_POSITION_HISTORY; RingPosition++) {
		Me.Position_History_Ring_Buffer[RingPosition].x = Me.pos.x;
		Me.Position_History_Ring_Buffer[RingPosition].y = Me.pos.y;
		Me.Position_History_Ring_Buffer[RingPosition].z = Me.pos.z;
	}
}				// void InitInfluPositionHistory( void )

float GetInfluPositionHistoryX(int HowLongPast)
{
	int RingPosition;

	HowLongPast >>= 1;

	RingPosition = Me.current_zero_ring_index - HowLongPast;

	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety

298
	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
299 300 301 302 303 304 305 306 307 308 309 310 311

	return Me.Position_History_Ring_Buffer[RingPosition].x;
}

float GetInfluPositionHistoryY(int HowLongPast)
{
	int RingPosition;

	HowLongPast >>= 1;
	RingPosition = Me.current_zero_ring_index - HowLongPast;

	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety

312
	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
313 314 315 316 317 318 319 320 321 322 323 324 325

	return Me.Position_History_Ring_Buffer[RingPosition].y;
}

float GetInfluPositionHistoryZ(int HowLongPast)
{
	int RingPosition;

	HowLongPast >>= 1;
	RingPosition = Me.current_zero_ring_index - HowLongPast;

	RingPosition += MAX_INFLU_POSITION_HISTORY;	// We don't want any negative values, for safety

326
	RingPosition %= MAX_INFLU_POSITION_HISTORY;	// We do MODULO for the Ring buffer length
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

	return Me.Position_History_Ring_Buffer[RingPosition].z;
}

/**
 * This function should check if the Tux is still ok, i.e. if he is still
 * alive or if the death sequence should be initiated.
 */
void CheckIfCharacterIsStillOk()
{

	// Now we check if the main character is really still ok.
	//
	if (Me.energy <= 0) {
		ThouArtDefeated();

		DebugPrintf(1, "\n%s():  Alternate end of function reached.", __FUNCTION__);
		return;
	}

};				// void CheckIfCharacterIsStillOk ( )

/**
 * Even the Tux must not leave the map!  A sanity check is done here...
 */
static void CheckForTuxOutOfMap()
{
	level *MoveLevel = curShip.AllLevels[Me.pos.z];

	// Now perhaps the influencer is out of bounds, i.e. outside of the map.
	//
	if (!pos_inside_level(Me.pos.x, Me.pos.y, MoveLevel)) {
		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 362
A player's Tux was found outside the map.\n\
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 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
	}
};				// void CheckForTuxOutOfMap ( )

/**
 * If an enemy was specified as the mouse move target, this enemy will
 * maybe move here and there.  But this means that also the mouse move
 * target of the influencer must adapt, which is done in this function.
 */
void tux_get_move_target_and_attack(gps * movetgt)
{
	moderately_finepoint RemainingWay;
	float RemainingWayLength;

	// if there is a mouse move target, we are not going to move towards the enemy
	if (Me.mouse_move_target.x != (-1)) {
		// If a combo action is pending, the mouse_move_target was defined relatively
		// to the item's level. We need here to define the position relatively to Tux's level.
		update_virtual_position(movetgt, &Me.mouse_move_target, Me.pos.z);
		return;
	}

	enemy *t = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);

	if (!t || (t->energy <= 0))	//No enemy or dead enemy, remove enemy
	{
		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
		movetgt->x = -1;
		movetgt->y = -1;
		movetgt->z = -1;
		return;
	}

	update_virtual_position(&t->virt_pos, &t->pos, Me.pos.z);

	// If we have a ranged weapon in hand, there is no need to approach the
	// enemy in question.  We just try to fire a shot, and return.
	//
	if (Me.weapon_item.type != (-1)) {
402
		if (!ItemMap[Me.weapon_item.type].weapon_is_melee) {	//ranged weapon
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
			if (!is_friendly(t->faction, FACTION_SELF))
				tux_wants_to_attack_now(FALSE);
			movetgt->x = -1;
			movetgt->y = -1;
			movetgt->z = -1;
			return;
		}
	}
	// Move to melee distance
	//
	RemainingWay.x = t->virt_pos.x - Me.pos.x;
	RemainingWay.y = t->virt_pos.y - Me.pos.y;

	RemainingWayLength = sqrt(RemainingWay.x * RemainingWay.x + RemainingWay.y * RemainingWay.y);

	if (RemainingWayLength > 0.05) {
		RemainingWay.x = (RemainingWay.x / RemainingWayLength) * (RemainingWayLength - (BEST_MELEE_DISTANCE - 0.1));
		RemainingWay.y = (RemainingWay.y / RemainingWayLength) * (RemainingWayLength - (BEST_MELEE_DISTANCE - 0.1));
	}

	if ((RemainingWayLength <= BEST_MELEE_DISTANCE * sqrt(2) + 0.01) && (!is_friendly(t->faction, FACTION_SELF))) {
		tux_wants_to_attack_now(FALSE);
	}
	// New move target.
	movetgt->x = Me.pos.x + RemainingWay.x;
	movetgt->y = Me.pos.y + RemainingWay.y;
429
	movetgt->z = Me.pos.z;
430 431 432 433 434 435 436 437 438

	return;
}				// void tux_get_move_target_and_attack( )

/**
 * Actually move Tux towards the target.
 */
static void move_tux_according_to_his_speed()
{
439

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
	float planned_step_x;
	float planned_step_y;

	// Now we move influence according to current speed.  But there has been a problem
	// reported from people, that the influencer would (*very* rarely) jump through walls
	// and even out of the ship.  This has *never* occurred on my fast machine.  Therefore
	// I assume that the problem is related to sometimes very low frame rates on these machines.
	// So, we do a sanity check not to make steps too big.
	//
	// And on machines with FPS << 20, it will certainly alter the game behavior, so people
	// should really start using a Pentium or better machine.
	//
	planned_step_x = Me.speed.x * Frame_Time();
	planned_step_y = Me.speed.y * Frame_Time();

	Me.pos.x += planned_step_x;
	Me.pos.y += planned_step_y;

458 459 460
	if (((int)GetInfluPositionHistoryX(0) != (int)Me.pos.x || ((int)GetInfluPositionHistoryY(0) != (int)Me.pos.y))) {
		event_position_changed(Me.pos, FALSE);
	}
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 466
	// simply away from the offending obstacles center)
	//
467
	if ((fabs(Me.speed.x) < 0.1) && (fabs(Me.speed.y) < 0.1)) {
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
		// So there is no speed, so we check for passability...
		//
		if (!SinglePointColldet(Me.pos.x, Me.pos.y, Me.pos.z, &WalkablePassFilter)) {
			// Now it's time to launch the stuck-fallback handling...
			//
			DebugPrintf(-3, "\nTux looks stuck...ESCAPING just for this frame...");
			float new_x = Me.pos.x;
			float new_y = Me.pos.y;
			int rtn = EscapeFromObstacle(&new_x, &new_y, Me.pos.z, &WalkablePassFilter);
			if (!rtn) {
				DebugPrintf(-3, "\nNo escape position found around Tux... Looking in position history...");
				// First : look for a suitable position in Tux's position_history
				int i;
				float old_x;
				float old_y;
				for (i = 1; i < 10; i++) {
					if (GetInfluPositionHistoryZ(10 * i) == Me.pos.z) {
						old_x = GetInfluPositionHistoryX(10 * i);
						old_y = GetInfluPositionHistoryY(10 * i);
						if (old_x != Me.pos.x && old_y != Me.pos.y
						    && SinglePointColldet(old_x, old_y, Me.pos.z, &WalkablePassFilter)) {
							// Found...
							Me.pos.x = old_x;
							Me.pos.y = old_y;
							break;
						}
					}
				}
				// If no luck, last fallback
				if (i == 10) {
					DebugPrintf(-3, "\nNo luck with position_history, last fallback...");
					// Get a random direction, and move by random length from 0.5 to 1.5.
					// With some luck, Tux will escape now, or in the future tries
501 502
					Me.pos.x += ((0.5 + (float)MyRandom(10) / 10.0) * (MyRandom(10) < 5)) ? 1 : -1;
					Me.pos.y += ((0.5 + (float)MyRandom(10) / 10.0) * (MyRandom(10) < 5)) ? 1 : -1;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
				}
			} else {
				Me.pos.x = new_x;
				Me.pos.y = new_y;
			}
		}
	}
}

/**
 * This function contains dumb movement code that, without any checks nor
 * refinement, calculates the direction and speed in which Tux has to move to
 * reach the target position.
 *
 * Returns TRUE if the target has been sufficiently approximated.
 */
static int move_tux_towards_raw_position(float x, float y)
{
	moderately_finepoint RemainingWay;
	moderately_finepoint planned_step;
	float squared_length, length;

	if (Me.energy <= 0)
		return FALSE;

	RemainingWay.x = -Me.pos.x + x;
	RemainingWay.y = -Me.pos.y + y;

	squared_length = RemainingWay.x * RemainingWay.x + RemainingWay.y * RemainingWay.y;

	// Maybe the remaining way is VERY small!  Then we must not do
	// a division at all.  We also need not do any movement, so the
	// speed can be eliminated and we're done here.
	//
	if (squared_length < DIST_TO_INTERM_POINT * DIST_TO_INTERM_POINT) {
		Me.speed.x = 0;
		Me.speed.y = 0;
		return (TRUE);
	}
	// Now depending on whether the running key is pressed or not,
	// we have the Tux go on running speed or on walking speed.
	//
	length = sqrt(squared_length);

	if (Me.running_power <= 0) {
		Me.running_must_rest = TRUE;
	}

	if (Me.running_must_rest) {
		GameConfig.autorun_activated = 0;
		planned_step.x = RemainingWay.x * TUX_WALKING_SPEED / length;
		planned_step.y = RemainingWay.y * TUX_WALKING_SPEED / length;
	}

	if ((LeftCtrlPressed() || GameConfig.autorun_activated) &&
		!(LeftCtrlPressed() && GameConfig.autorun_activated) && (!Me.running_must_rest)) {
		float running_speed = get_tux_running_speed();
		planned_step.x = RemainingWay.x * running_speed / length;
		planned_step.y = RemainingWay.y * running_speed / length;
	} else {
		planned_step.x = RemainingWay.x * TUX_WALKING_SPEED / length;
		planned_step.y = RemainingWay.y * TUX_WALKING_SPEED / length;
	}

	// Now that the speed is set, we can start to make the step
	//
	Me.speed.x = planned_step.x;
	Me.speed.y = planned_step.y;
	Me.meters_traveled += 0.1;
	// If we might step over the target,
	// we reduce the speed.
	//
	if (fabsf(planned_step.x * Frame_Time()) >= fabsf(RemainingWay.x))
		Me.speed.x = RemainingWay.x / Frame_Time();
	if (fabsf(planned_step.y * Frame_Time()) >= fabsf(RemainingWay.y))
		Me.speed.y = RemainingWay.y / Frame_Time();

	// In case we have reached our target, we can remove this mouse_move_target again,
	// but also if we have been thrown onto a different level, we cancel our current
	// mouse move target...
	//
	if (((fabsf(RemainingWay.y) <= DISTANCE_TOLERANCE) && (fabsf(RemainingWay.x) <= DISTANCE_TOLERANCE))) {
		return (TRUE);
	}

	return (FALSE);
}

/**
 *
 *
 */
static void move_tux_towards_intermediate_point(void)
{
	int i;

	/* If we have no intermediate point, Tux has arrived at the target. */
	if (Me.next_intermediate_point[0].x == -1) {
		/* First, stop Tux. */
		Me.speed.x = 0;
		Me.speed.y = 0;

605 606 607 608 609
		if (Me.mouse_move_target.z == -1) {
			// Nothing was targeted
			return;
		}

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
		/* We might have a combo_action, that can occur on the end of any
		 * course, like e.g. open a chest or pick up some item. */
		level *lvl = curShip.AllLevels[Me.mouse_move_target.z];
		switch (Me.mouse_move_target_combo_action_type) {
		case NO_COMBO_ACTION_SET:
			break;
		case COMBO_ACTION_OBSTACLE:
			get_obstacle_spec(lvl->obstacle_list[Me.mouse_move_target_combo_action_parameter].type)->action_fn(
                    lvl,
                    Me.mouse_move_target_combo_action_parameter);
			break;
		case COMBO_ACTION_PICK_UP_ITEM:
			// If Tux arrived at destination, pick up the item and give it to the player
			if (check_for_items_to_pickup(lvl, Me.mouse_move_target_combo_action_parameter)) {
				item *it = &lvl->ItemList[Me.mouse_move_target_combo_action_parameter];

				if (GameConfig.Inventory_Visible) {
					// Special case: when the inventory screen is open, and there
					// is no enough room to place the item, put it in player's hand
					if (!try_give_item(it)) {
						item_held_in_hand = it;
					}
				} else {
					// Put the item into player's inventory, or drop it on the floor
					// if there is no enough room.
					give_item(it);
				}
			}

			break;
		default:
641
			error_message(__FUNCTION__, "Unhandled combo action for intermediate course encountered!", PLEASE_INFORM | IS_FATAL);
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
			break;
		}

		Me.mouse_move_target.x = -1;
		Me.mouse_move_target.y = -1;
		Me.mouse_move_target.z = -1;
		Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
		return;
	}

	/* Stop Tux when he's close enough to an item to pick it up. */
	if (Me.mouse_move_target_combo_action_type == COMBO_ACTION_PICK_UP_ITEM) {
		float distance = calc_distance(Me.pos.x, Me.pos.y,
									   Me.mouse_move_target.x, Me.mouse_move_target.y);
		if (distance < ITEM_TAKE_DIST &&
			DirectLineColldet(Me.pos.x, Me.pos.y, Me.mouse_move_target.x,
							  Me.mouse_move_target.y, Me.pos.z, NULL))
		{
			for (i = 0; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++) {
				Me.next_intermediate_point[i].x = -1;
				Me.next_intermediate_point[i].y = -1;
			}
			return;
		}
	}

	// Move Tux towards the next intermediate course point
	if (move_tux_towards_raw_position(Me.next_intermediate_point[0].x, Me.next_intermediate_point[0].y)) {
		DebugPrintf(DEBUG_TUX_PATHFINDING, "\nMOVING ON TO NEXT INTERMEDIATE WAYPOINT! ");
		for (i = 1; i < MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX; i++) {
			Me.next_intermediate_point[i - 1].x = Me.next_intermediate_point[i].x;
			Me.next_intermediate_point[i - 1].y = Me.next_intermediate_point[i].y;
		}
	}
}

/**
 * This function moves the influencer, adjusts his speed according to
680
 * keys pressed and also adjusts his status and current "phase" of his
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
 * rotation.
 */
void move_tux()
{
	static gps last_given_course_target = { -2, -2, -2 };

	// check, if the influencer is still ok
	CheckIfCharacterIsStillOk();

	// We store the influencers position for the history record and so that others
	// can follow his trail.
	//
	Me.current_zero_ring_index++;
	Me.current_zero_ring_index %= MAX_INFLU_POSITION_HISTORY;
	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].x = Me.pos.x;
	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].y = Me.pos.y;
	Me.Position_History_Ring_Buffer[Me.current_zero_ring_index].z = Me.pos.z;


	if (Me.paralyze_duration) {
		Me.speed.x = 0;
		Me.speed.y = 0;
		return;		//If tux is paralyzed, we do nothing more
	}
	// As a preparation for the later operations, we see if there is
	// a living droid set as a target, and if yes, we correct the move
	// target to something suiting that new droids position.
	//
	gps move_target;

	tux_get_move_target_and_attack(&move_target);

	if (move_target.x != -1) {
		// For optimisation purposes, we'll not do anything unless a new target
		// has been given.
		//
		if (!((fabsf(move_target.x - last_given_course_target.x) < 0.3) &&
		      (fabsf(move_target.y - last_given_course_target.y) < 0.3))) {
			freeway_context frw_ctx = { FALSE, {NULL, NULL} };
			pathfinder_context pf_ctx = { &WalkableWithMarginPassFilter, &frw_ctx };
			moderately_finepoint target_point = { move_target.x, move_target.y };
			if (!set_up_intermediate_course_between_positions
			    (&Me.pos, &target_point, &Me.next_intermediate_point[0], MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX, &pf_ctx)) {
				// A path was not found.
				// If a combo action was set, halt it.
				if (Me.mouse_move_target_combo_action_type != NO_COMBO_ACTION_SET) {
					Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
					Me.mouse_move_target.x = Me.pos.x;
					Me.mouse_move_target.y = Me.pos.y;
					Me.mouse_move_target.z = Me.pos.z;
				}
			} else {
				last_given_course_target.x = move_target.x;
				last_given_course_target.y = move_target.y;
				last_given_course_target.z = move_target.z;
			}
		}
	}

	// If there is a mouse move target present, we move towards that.
	move_tux_towards_intermediate_point();

	// Perhaps the player has pressed the right mouse button, indicating the use
	// of the currently selected special function or spell.
	//
	HandleCurrentlyActivatedSkill();

	// Maybe we need to fire a bullet or set a new mouse move target
	// for the new move-to location
	// There are currently two different input systms in use - event based and state based.
	// In order to maintain compatibility between the two, a game_map widget is added on the
	// game main widget to detect how user input should be handled. Therefore, when the mouse
	// is over the game_map widget (the widget is not in its DEFAULT state), no further event handling
	// is done by the widget system and AnalyzePlayersMouseClick is called.
	if (game_map->state != DEFAULT)
		AnalyzePlayersMouseClick();

	if (MouseLeftPressed())
		no_left_button_press_in_previous_analyze_mouse_click = FALSE;
	else
		no_left_button_press_in_previous_analyze_mouse_click = TRUE;

	// During inventory operations, there should not be any (new) movement
	//
	if (item_held_in_hand != NULL) {
		Me.mouse_move_target.x = Me.pos.x;
		Me.mouse_move_target.y = Me.pos.y;
		Me.mouse_move_target.z = Me.pos.z;
		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
		return;
	}

	limit_tux_speed();

	move_tux_according_to_his_speed();

	animate_tux();
}

/**
 * This function decrements Tux's health and increments the relevant statistic
 * variable.
 */
784
void hit_tux(float damage)
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
{
	if (Me.god_mode)
		return;

	if (Me.energy < 0)
		return;

	Me.energy -= damage;

	if (damage > Me.energy / 10)
		tux_scream_sound();
}


/**
 * This function computes to current animation keyframe of Tux (Me.phase).
 *
 * Depending of Tux's current state (attacking, standing, walking, running),
 * an animation is chosen, defined by a keyframed specification and an "animation
 * progress cursor". This cursor progresses from 0 to 1 (possibly looping for
 * some animations).
 *
 * The current keyframe is then: first_keyframe + cursor * nb_keyframes
 *
 * The way a cursor progresses from 0 to 1 depends on the kind of the animation.
 * For example, the attack animation is a duration-based animation, so its cursor
 * progresses as time progresses. The walk animation is based on the distance
 * covered by Tux, so its cursor is a function of distance.
 *
 * Note on walking/running animations:
 * Those animations are running in loop, and we can change from walk to run
 * in the middle of the animation. In order to have a seamless transition
 * between the animations, we do not reset the progress cursor's value.
 * So those animations share a common animation progress cursor (Me.walk_cycle_phase).
 */
void animate_tux()
{
822 823
	static int play_step_sound = 0;
	int tux_is_running = FALSE;
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874

	// If Tux is paralyzed, show him as standing still.
	if (Me.paralyze_duration) {
		Me.walk_cycle_phase = 0.0;
		Me.phase = tux_anim.standing_keyframe;

		return;
	}

	// Handle the case of Tux just getting hit
	//
	// Note: We do not yet have keyframes for such an animation, so those
	// commented lines are only a place-holder for future extension.

	/*
	if (Me.got_hit_time != -1) {
		Me.phase = .....
		return;
	}
	*/

	// Attack animation
	// (duration-based animation, no loop)

	if (Me.weapon_swing_time != -1) {
		if (tux_anim.attack.nb_keyframes != 0) {
			float progress_cursor = (float)Me.weapon_swing_time / tux_anim.attack.duration;
			Me.phase = tux_anim.attack.first_keyframe + progress_cursor * (float)(tux_anim.attack.nb_keyframes);

			// Clamp to maximum keyframe value
			if ((int)Me.phase > tux_anim.attack.last_keyframe) {
				Me.phase = tux_anim.attack.last_keyframe;
			}
		} else {
			Me.phase = 0;
		}

		// Reset walk/run animation progress cursor
		Me.walk_cycle_phase = 0.0;

		// Stop the time counter when the end of the animation is reached
		if (Me.weapon_swing_time > tux_anim.attack.duration)
			Me.weapon_swing_time = -1;

		return;
	}

	// Breathe animation (launched when Tux's speed is very low).
	// Currently, there is no such animation, so we only display Tux at its
	// standing position.

875
	if (fabs(Me.speed.x) + fabs(Me.speed.y) < 0.3) {
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
		Me.walk_cycle_phase = 0.0;
		Me.phase = tux_anim.standing_keyframe;

		return;
	}

	// Walk/run animation
	// (distance-based animation, loop)
	// The progress cursor is: distance_covered_by_tux / distance_covered_during_one_sequence
	//
	// There is no way to 'statically' compute the distance covered by Tux,
	// so it is computed 'dynamically', i.e. incrementally, by adding the
	// distance covered since last frame.
	// The progress cursor thus has to be stored (Me.walk_cycle_phase)

	/* Choose animation spec depending on Tux speed */
	struct distancebased_animation *anim_spec;

	float my_speed = sqrt(Me.speed.x * Me.speed.x + Me.speed.y * Me.speed.y);

	if (my_speed <= (TUX_WALKING_SPEED + TUX_RUNNING_SPEED) * 0.5) {
		anim_spec = &(tux_anim.walk);
898
		tux_is_running = FALSE;
899 900
	} else {
		anim_spec = &(tux_anim.run);
901
		tux_is_running = TRUE;
902 903 904 905 906 907 908
	}

	if (anim_spec->nb_keyframes != 0) {
		/* Set the progress cursor */
		Me.walk_cycle_phase += (Frame_Time() * my_speed) / anim_spec->distance;

		/* Loop of progress cursor */
909 910 911 912 913 914 915 916 917
		if (tux_is_running) {
			if (play_step_sound == 0 && Me.walk_cycle_phase >= 0.3) {
				play_sound_v("effects/tux_footstep.ogg", 0.125);
				play_step_sound = 1;
			} else if (play_step_sound == 1 && Me.walk_cycle_phase >= 0.8) {
				play_sound_v("effects/tux_footstep.ogg", 0.125);
				play_step_sound = 2;
			}
		}
918 919
		while (Me.walk_cycle_phase > 1.0) {
			Me.walk_cycle_phase -= 1.0;
920
			play_step_sound = 0;
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
		}

		/* Set current animation keyframe */
		Me.phase = anim_spec->first_keyframe + Me.walk_cycle_phase * anim_spec->nb_keyframes;
		if ((int)Me.phase > anim_spec->last_keyframe) {
			Me.phase = anim_spec->last_keyframe;
		}
	} else {
		Me.walk_cycle_phase = 0.0;
		Me.phase = tux_anim.standing_keyframe;
	}
}

/**
 * This function creates several explosions around the location where the
936
 * influencer is (was) positioned.  It is used after the influencers
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
 * death to make his death more spectacular.
 */
void start_tux_death_explosions(void)
{
	int i;
	int counter;

	DebugPrintf(1, "\n%s(): Real function call confirmed.", __FUNCTION__);

	// create a few shifted explosions...
	for (i = 0; i < 10; i++) {

		// find a free blast
		counter = 0;
		while (AllBlasts[counter++].type != INFOUT) ;
		counter -= 1;
		if (counter >= MAXBLASTS) {
954
			error_message(__FUNCTION__, "Ran out of blasts!!!", PLEASE_INFORM | IS_FATAL);
955 956 957 958 959 960 961 962 963 964 965
		}
		AllBlasts[counter].type = DROIDBLAST;
		AllBlasts[counter].pos.x = Me.pos.x - 0.125 / 2 + MyRandom(10) * 0.05;
		AllBlasts[counter].pos.y = Me.pos.y - 0.125 / 2 + MyRandom(10) * 0.05;
		AllBlasts[counter].phase = i;
	}

	DebugPrintf(1, "\n%s(): Usual end of function reached.", __FUNCTION__);

};				// void start_tux_death_explosions ( void )

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 970 971 972
 * quit to main menu or quit the game.
 */
void do_death_menu()
{
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
	char *MenuTexts[100];
	int done = FALSE;
	int MenuPosition = 1;
	int i;

	game_status = INSIDE_MENU;

	input_handle();

	enum {
		LOAD_LATEST_POSITION = 1,
		LOAD_BACKUP_POSITION,
		QUIT_TO_MAIN_POSITION,
		QUIT_POSITION
	};
	while (!done) {
		i = 0;
		MenuTexts[i++] = _("Load Latest");
		MenuTexts[i++] = _("Load Backup");
		if (game_root_mode == ROOT_IS_GAME) {
			MenuTexts[i++] = _("Quit to Main Menu");
		} else { // if (game_root_mode == ROOT_IS_LVLEDIT) {
			MenuTexts[i++] = _("Return to Editor");
		}
		MenuTexts[i++] = _("Exit FreedroidRPG");
		MenuTexts[i++] = "";

		MenuPosition = DoMenuSelection("", MenuTexts, 1, "--GAME_BACKGROUND--", Menu_Font);

		switch (MenuPosition) {
		case LOAD_LATEST_POSITION:
			load_game();
			done = !done;
			break;
		case LOAD_BACKUP_POSITION:
			load_backup_game();
			done = !done;
			break;
		case QUIT_TO_MAIN_POSITION:
			if (game_root_mode == ROOT_IS_GAME) {
				GameOver = TRUE;
			}
			done = !done;
			break;
		case QUIT_POSITION:
			Terminate(EXIT_SUCCESS);
			break;
		default:
			done = !done;
			break;
		}
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
	}
}

/**
 * This function checks if the influencer is currently colliding with an
 * enemy and throws him back in that case.
 */
void check_tux_enemy_collision(void)
{
	float xdist;
	float ydist;

	enemy *erot, *nerot;
	BROWSE_LEVEL_BOTS_SAFE(erot, nerot, Me.pos.z) {

		if (erot->type == (-1) || erot->pure_wait)
			continue;

		// We determine the distance and back out immediately if there
		// is still one whole square distance or even more...
		//
		xdist = Me.pos.x - erot->pos.x;
1046
		if (fabs(xdist) > 1)
1047 1048
			continue;
		ydist = Me.pos.y - erot->pos.y;
1049
		if (fabs(ydist) > 1)
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
			continue;

		// Now at this point we know, that we are pretty close.  It is time
		// to calculate the exact distance and to see if the exact distance
		// indicates a collision or not, in which case we can again back out
		//
		//
		if ((fabsf(xdist) >= 2.0 * 0.25) || (fabsf(ydist) >= 2.0 * 0.25))
			continue;

		erot->pure_wait = WAIT_COLLISION;

		short int swap = erot->nextwaypoint;
		erot->nextwaypoint = erot->lastwaypoint;
		erot->lastwaypoint = swap;

	}

};				// void check_tux_enemy_collision( void )

/**
 * This function checks if there is some living droid below the current
 * mouse cursor and returns the index number of this droid in the array.
1073
 *
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
 * Earlier we did this by computing the map location the mouse was pointing
 * to and using that for the computation of the distance to droid coordinates.
 * The problem with this method is, that some droids are very 'high' in
 * the sense that the graphics (e.g. 302 body) is very far away from the
 * 'foot' point, where the droid is in X-Y coordinates on the map.  Therefore
 * some correction has to be done to fix this.  We can't use the map position
 * of the mouse any more... except maybe to exclude some bots from the start.
 *
 */
enemy *GetLivingDroidBelowMouseCursor()
{
	gps mouse_vpos, mouse_pos;
	int RotationModel, RotationIndex;
	struct image *our_image;
	enemy *this_bot;

	// no interaction with the game when the world is frozen
	if (world_frozen())
		return NULL;

	mouse_vpos.x = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, TRUE);
	mouse_vpos.y = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, FALSE);
	mouse_vpos.z = Me.pos.z;

	// Find the actual level (and related position) where the mouse cursor is pointing at.
	if (!resolve_virtual_position(&mouse_pos, &mouse_vpos))
		return NULL;

	// Browse all bots on that actual level, to find if the mouse is hovering one of them
	BROWSE_LEVEL_BOTS(this_bot, mouse_pos.z) {
		if (fabsf(this_bot->pos.x - mouse_pos.x) >= 5.0)
			continue;
		if (fabsf(this_bot->pos.y - mouse_pos.y) >= 5.0)
			continue;

		// We properly set the direction this robot is facing.
		//
		RotationIndex = set_rotation_index_for_this_robot(this_bot);

		// We properly set the rotation model number for this robot, i.e.
		// which shape (like 302, 247 or proffa) to use for drawing this bot.
		//
		RotationModel = set_rotation_model_for_this_robot(this_bot);

		our_image = &(enemy_images[RotationModel][RotationIndex][(int)this_bot->animation_phase]);

		update_virtual_position(&this_bot->virt_pos, &this_bot->pos, Me.pos.z);
		if (mouse_cursor_is_on_that_image(this_bot->virt_pos.x, this_bot->virt_pos.y, our_image)) {
			return this_bot;
		}
	}

	return (NULL);

};				// int GetLivingDroidBelowMouseCursor ( )

/**
1131
 * This function fires a bullet from the influencer in some direction,
1132 1133 1134
 * no matter whether this is 'allowed' or not, not questioning anything
 * and SILENTLY TRUSTING THAT THIS TUX HAS A RANGED WEAPON EQUIPPED.
 */
1135 1136
void perform_tux_ranged_attack(short int weapon_type, bullet *bullet_parameters,
		                       moderately_finepoint target_location)
1137
{
1138 1139 1140 1141 1142
	// Standard ranged attack process is to fire a bullet in the direction
	// of the target, and have it advance step by step until it reaches 'something'.
	// The starting position of the bullet is the gun's muzzle (note that we do not
	// know exactly the length of a gun, so we will use an 'average' constant value of
	// 'muzzle_offset_factor').
1143
	//
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
	// But, sometime the targeted object can be so near Tux that it is actually
	// between Tux and the gun's muzzle. In "real life", this should not even
	// create a ranged attack, but for simplicity we will fire a bullet in that case.
	// The bullet being already at its target position, we immediately call the code
	// that detects bullet collisions and create bullet's explosion and damages.
	// And only if no collision is detected do we fire the bullet.

	float muzzle_offset_factor = 0.5;

	// Search for the first free bullet entry in the bullet list and initialize
	// with default values

	int bullet_index = find_free_bullet_index();
	if (bullet_index == -1) {
		// We are out of free bullet slots.
		// This should not happen, an error message was displayed,
		return;
	}
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 1170 1171 1172
	// Set up recharging time for the Tux...
	// The firewait time is modified by the ranged weapon skill

	Me.busy_time = ItemMap[weapon_type].weapon_attack_time;
1173 1174
	Me.busy_time *= RangedRechargeMultiplierTable[Me.ranged_weapon_skill];
	Me.busy_type = WEAPON_FIREWAIT;
1175
	Me.weapon_swing_time = 0; // restart swing animation
1176

1177 1178 1179
	// First case: The target is too near for an actual shot.
	// We try an immediate hit. If nothing is hurt, or if the bullet traversed
	// the target, then we will fire a bullet using the 'second case'.
1180

1181
	float const dist_epsilon = 0.05;
1182

1183 1184
	if ( (fabs(target_location.x - Me.pos.x) <= muzzle_offset_factor + dist_epsilon) &&
	     (fabs(target_location.y - Me.pos.y) <= muzzle_offset_factor + dist_epsilon) ) {
1185

1186 1187 1188
		new_bullet->pos.x = target_location.x;
		new_bullet->pos.y = target_location.y;
		CheckBulletCollisions(bullet_index);
1189

1190 1191 1192 1193
		/* If the bullet exploded, we're done */
		if (new_bullet->type == INFOUT)
			return;
	}
1194

1195 1196 1197
	// Second case: The target is not near Tux (or a first-case bullet traversed
	// the target)
	// We fire a bullet into the direction of the target.
1198

1199 1200
	// Compute bullet's attack vector.
	// This is a vector from Tux's position to the target's position.
1201

1202 1203 1204 1205 1206
	moderately_finepoint attack_vector = { target_location.x - Me.pos.x,
	                                       target_location.y - Me.pos.y };
	double attack_norm = sqrt(attack_vector.x * attack_vector.x + attack_vector.y *attack_vector.y);
	attack_vector.x /= attack_norm;
	attack_vector.y /= attack_norm;
1207

1208 1209 1210
	// The bullet starts from the muzzle's position.
	// As said in the heading comment, we do not have enough informations to
	// compute it, so we just use a small offset in the attack direction.
1211

1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
	moderately_finepoint muzzle_position = { Me.pos.x + muzzle_offset_factor * attack_vector.x,
	                                         Me.pos.y + muzzle_offset_factor * attack_vector.y };

	// Set the bullet parameters

	new_bullet->pos.x = muzzle_position.x;
	new_bullet->pos.y = muzzle_position.y;
	new_bullet->speed.x = attack_vector.x * ItemMap[weapon_type].weapon_bullet_speed;
	new_bullet->speed.y = attack_vector.y * ItemMap[weapon_type].weapon_bullet_speed;
	new_bullet->angle = -(atan2(attack_vector.y, attack_vector.x) * 180 / M_PI + 90 + 45);
}
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239

/**
 * In some cases, the mouse button will be pressed, but still some signs
 * might tell us, that this mouse button press was not intended as a move
 * or fire command to the Tux.  This function checks for these cases.
 */
int ButtonPressWasNotMeantAsFire()
{
	// If the influencer is holding something from the inventory
	// menu via the mouse, also just return
	//
	if (item_held_in_hand != NULL)
		return (TRUE);
	if (timeout_from_item_drop > 0)
		return (TRUE);

	// 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 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
	// return from here immediately.
	//
	if (MouseLeftPressed() &&
	    (GameConfig.Inventory_Visible || GameConfig.CharacterScreen_Visible || GameConfig.SkillScreen_Visible
	     || GameConfig.skill_explanation_screen_visible)
	    && !MouseCursorIsInUserRect(GetMousePos_x(), GetMousePos_y())) {
		DebugPrintf(0, "\nCursor outside user-rect:\n  User_Rect.x=%d, User_Rect.w=%d, User_Rect.y=%d, User_Rect.h=%d.",
			    User_Rect.x, User_Rect.w, User_Rect.y, User_Rect.h);
		DebugPrintf(0, "\nCursor position: X=%d, Y=%d.", input_axis.x, input_axis.y);
		return (TRUE);
	}

	return (FALSE);

};				// int ButtonPressWasNotMeantAsFire ( )

/**
1258
 * At some point in the analysis of the users mouse click, we'll be
1259 1260 1261
 * certain, that a fireing/weapon swing was meant with the click.  Once
 * this is knows, this function can be called to do the mechanics of the
 * weapon use.
1262 1263
 *
 * Return 0 if attack failed.
1264
 */
1265
int perform_tux_attack(int use_mouse_cursor_for_targeting)
1266
{
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
	moderately_finepoint target_location = { -1, -1 };
	float target_angle;

	// The attack target location can be the targeted enemy (if one was set), or an
	// enemy below the mouse cursor, or by default the current mouse position.
	// In case of an 'un-targeted' attack (A-pressed), the mouse position defines the
	// target location.

	if (!APressed() && !use_mouse_cursor_for_targeting) {
		enemy *targeted_enemy = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);
		if (!targeted_enemy) {
			targeted_enemy = GetLivingDroidBelowMouseCursor();
		}
		if (targeted_enemy) {
			// Use enemy's position to compute target location
			update_virtual_position(&targeted_enemy->virt_pos, &targeted_enemy->pos, Me.pos.z);
			target_location.x = targeted_enemy->virt_pos.x;
			target_location.y = targeted_enemy->virt_pos.y;
		} else {
			// Use mouse position to compute target location
			use_mouse_cursor_for_targeting = TRUE;
		}
	} else {
		use_mouse_cursor_for_targeting = TRUE;
	}
1292

1293 1294 1295
	// If target location is defined by the mouse position, check if there is
	// an obstacle or an enemy under the mouse, otherwise use the mouse location
	if (use_mouse_cursor_for_targeting) {
1296

1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309
		// By default, use mouse location
		target_location.x = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, TRUE);
		target_location.y = translate_pixel_to_map_location((float)input_axis.x, (float)input_axis.y, FALSE);

		// If there is an obstacle under the mouse cursor, use it as target
		level *obs_lvl = NULL;
		gps targeted_obstacle_vpos = { -1, -1, -1 };
		int targeted_obstacle_index = clickable_obstacle_below_mouse_cursor(&obs_lvl, FALSE);
		if (targeted_obstacle_index != -1) {
			update_virtual_position(&targeted_obstacle_vpos, &obs_lvl->obstacle_list[targeted_obstacle_index].pos, Me.pos.z);
			target_location.x = targeted_obstacle_vpos.x;
			target_location.y = targeted_obstacle_vpos.y;
		}
1310

1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
		// If there is an enemy under the mouse cursor, use it as target if the enemy is nearest (using iso-norm)
		enemy *targeted_enemy = GetLivingDroidBelowMouseCursor();
		if (targeted_enemy != NULL) {
			update_virtual_position(&targeted_enemy->virt_pos, &targeted_enemy->pos, Me.pos.z);
			if ((targeted_obstacle_index == -1) ||
				((targeted_enemy->virt_pos.x + targeted_enemy->virt_pos.y) >= (targeted_obstacle_vpos.x + targeted_obstacle_vpos.y))) {
				target_location.x = targeted_enemy->virt_pos.x;
				target_location.y = targeted_enemy->virt_pos.y;
			}
		}
1321 1322 1323

	}

1324
	// Turn Tux to face its target
1325

1326 1327
	target_angle = -(atan2(Me.pos.y - target_location.y, Me.pos.x - target_location.x) * 180/M_PI - 90 + 22.5);
	Me.angle = target_angle;
1328

1329 1330 1331
	/**
	 * First case: Attack with a melee weapon (or with fists).
	 */
1332

1333
	if (Me.weapon_item.type == -1 || ItemMap[Me.weapon_item.type].weapon_is_melee) {
1334

1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
		int hit_something = FALSE;

		// A melee attack is a swing gesture, the impact point is thus a bit in front of
		// Tux. We have no idea of the size of the weapon, so we use an 'average' constant
		// value of 0.8 (impact_offset_y).

		const float impact_offset_y = 0.8;

		moderately_finepoint impact_point = { 0, -impact_offset_y };
		RotateVectorByAngle(&impact_point, Me.angle);
		impact_point.x += Me.pos.x;
		impact_point.y += Me.pos.y;

		// Find all enemies which are in a small area around the impact point
		// and setup up a melee shot.
		// TODO: also look for enemies on neighbor levels.

		const float impact_area_size = 0.5;
1353 1354 1355

		enemy *erot, *nerot;
		BROWSE_LEVEL_BOTS_SAFE(erot, nerot, Me.pos.z) {
1356 1357 1358 1359
			if ( (fabsf(erot->pos.x - impact_point.x) > impact_area_size) ||
			     (fabsf(erot->pos.y - impact_point.y) > impact_area_size) ||
			     !DirectLineColldet(Me.pos.x, Me.pos.y, erot->pos.x, erot->pos.y, Me.pos.z, NULL)
			   )
1360 1361
				continue;

1362
			// Set up a melee attack
1363
			int shot_index = find_free_melee_shot_index();
1364 1365 1366 1367 1368
			if (shot_index == -1) {
				// We are out of free melee shot slots.
				// This should not happen, an error message was displayed,
				return 0;
			}
1369

1370
			melee_shot *new_shot = &(AllMeleeShots[shot_index]);
1371

1372 1373 1374 1375 1376 1377 1378 1379
			new_shot->attack_target_type = ATTACK_TARGET_IS_ENEMY;
			new_shot->mine = TRUE;
			new_shot->bot_target_n = erot->id;
			new_shot->bot_target_addr = erot;
			new_shot->to_hit = Me.to_hit;
			new_shot->damage = Me.base_damage + MyRandom(Me.damage_modifier);
			new_shot->owner = -1;	//no "bot class number" owner
			new_shot->time_to_hit = tux_anim.attack.duration / 2;
1380 1381 1382 1383 1384

			// Slow or paralyze enemies if the player has bonuses with those effects.
			erot->frozen += Me.slowing_melee_targets;
			erot->paralysation_duration_left += Me.paralyzing_melee_targets;

1385
			hit_something = TRUE;
1386 1387 1388 1389 1390 1391
		}

		// Also, we should check if there was perhaps a chest or box
		// or something that can be smashed up, cause in this case, we
		// must open Pandora's box now.

1392 1393 1394 1395 1396 1397 1398
		if (smash_obstacle(impact_point.x, impact_point.y, Me.pos.z))
			hit_something = TRUE;

		// Finally we add a new wait-counter, so that swings cannot be started
		// in too rapid succession. Adjust that delay to take player's skill
		// into account.

1399
		Me.busy_type = WEAPON_FIREWAIT;
1400 1401 1402 1403 1404 1405 1406
		Me.busy_time = 0.5; // default value
		if (Me.weapon_item.type != -1)
			Me.busy_time = ItemMap[Me.weapon_item.type].weapon_attack_time;
		Me.busy_time *= MeleeRechargeMultiplierTable[Me.melee_weapon_skill];
		Me.weapon_swing_time = 0; // restart swing animation

		// Play a sound feedback
1407

1408
		if (hit_something)
1409 1410 1411 1412
			play_melee_weapon_hit_something_sound();
		else
			play_melee_weapon_missed_sound(&Me.pos);

1413
		return hit_something;
1414 1415
	}

1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429
	/**
	 * Second case: Attack with a ranged weapon.
	 * Note: we know that weapon_item.type != -1
	 */

	perform_tux_ranged_attack(Me.weapon_item.type, NULL, target_location);

	fire_bullet_sound(ItemMap[Me.weapon_item.type].weapon_bullet_type, &Me.pos);

	// We do not know if the bullet will hit something, but the bullet was fired,
	// so that's a success...

	return TRUE;
}
1430 1431 1432 1433 1434 1435 1436 1437 1438

/**
 * Reload the ammo clip
 *
 *
 */

void TuxReloadWeapon()
{
1439 1440
	if (Me.weapon_item.type == -1)
		return;		// Do not reload Tux's fists.
1441

1442 1443
	if (Me.paralyze_duration)
		return;		// Do not reload when paralyzed.
1444

1445 1446
	if (ItemMap[Me.weapon_item.type].weapon_ammo_clip_size == Me.weapon_item.ammo_clip)
		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 1451 1452
	int count = CountItemtypeInInventory(ammo_type);
	if (count > ItemMap[Me.weapon_item.type].weapon_ammo_clip_size - Me.weapon_item.ammo_clip)
		count = ItemMap[Me.weapon_item.type].weapon_ammo_clip_size - Me.weapon_item.ammo_clip;
1453 1454 1455 1456 1457

	if (!count)		//no ammo found, tell the player that he "has it in the baba"
	{
		No_Ammo_Sound();
		No_Ammo_Sound();
1458 1459
		// TRANSLATORS: Out of <ammo type>
		append_new_game_message(_("Out of [s]%s[v]!"), _(item_specs_get_name(ammo_type)));
1460 1461 1462 1463
		return;
	}
	int i;
	for (i = 0; i < count; i++)
1464
		DeleteOneInventoryItemsOfType(ammo_type);
1465
	Me.weapon_item.ammo_clip += count;
1466 1467
	play_sound(ItemMap[Me.weapon_item.type].weapon_reloading_sound);
	Me.busy_time = ItemMap[Me.weapon_item.type].weapon_reloading_time;
1468 1469 1470 1471 1472 1473 1474
	Me.busy_time *= RangedRechargeMultiplierTable[Me.ranged_weapon_skill];
	Me.busy_type = WEAPON_RELOAD;
}

/**
 * When the player has left-clicked into the game area (i.e. the isometric
 * display of the game world), we need to check if maybe the click was
1475 1476
 * targeted on a droid.
 * In case that was so, we need to start a dialog or maybe launch an
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514
 * attack movement.
 */
void check_for_droids_to_attack_or_talk_with()
{
	/* NOTA : the call to GetLivingDroidBelowMouseCursor() does set the virt_pos attribute
	 * of the found droid to be the bot's position relatively to Tux current level
	 */
	enemy *droid_below_mouse_cursor = GetLivingDroidBelowMouseCursor();

	// Set a move action unless there's a droid below the cursor,
	// 'A' is pressed,
	// or the player has clicked to pickup an item and hasn't released the LMB
	if (droid_below_mouse_cursor == NULL && (!APressed()) &&
		(no_left_button_press_in_previous_analyze_mouse_click ||
		Me.mouse_move_target_combo_action_type != COMBO_ACTION_PICK_UP_ITEM)) {

		Me.mouse_move_target.x = translate_pixel_to_map_location(input_axis.x, input_axis.y, TRUE);
		Me.mouse_move_target.y = translate_pixel_to_map_location(input_axis.x, input_axis.y, FALSE);
		Me.mouse_move_target.z = Me.pos.z;
		if (!ShiftPressed()) {
			enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
		}

		return;
	} else if (APressed()) {
		tux_wants_to_attack_now(TRUE);
		return;
	}

	if (droid_below_mouse_cursor != NULL &&
	    DirectLineColldet(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y, Me.pos.z,
			      &VisiblePassFilter)) {

		enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, droid_below_mouse_cursor);

		enemy *e = enemy_resolve_address(Me.current_enemy_target_n, &Me.current_enemy_target_addr);
		if (is_friendly(e->faction, FACTION_SELF)) {
			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 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
				enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
			}

			return;
		}

		if (!ShiftPressed()) {
			Me.mouse_move_target.x = -1;
			Me.mouse_move_target.y = -1;
		}

		if (Me.weapon_item.type >= 0) {
1528
			if ((ItemMap[Me.weapon_item.type].weapon_is_melee) &&
1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
			    (calc_distance(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y)
			     > BEST_MELEE_DISTANCE + 0.1)) {

				return;
			}
		} else if (calc_distance(Me.pos.x, Me.pos.y, droid_below_mouse_cursor->virt_pos.x, droid_below_mouse_cursor->virt_pos.y)
			   > BEST_MELEE_DISTANCE + 0.1) {

			return;
		}
		// But if we're close enough or there is a ranged weapon in Tux hands,
		// then we can finally start the attack motion right away...
		//
		tux_wants_to_attack_now(TRUE);
	}
1544
};				// void check_for_droids_to_attack ( )
1545 1546

/**
1547
 * If the user clicked his mouse, this might have several reasons.  It
1548 1549
 * might happen to open some windows, pick up some stuff, smash a box,
 * move somewhere or fire a shot or make a weapon swing.
1550
 *
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
 * Therefore it is not so easy to decide what to do upon a users mouse
 * click and so this function analyzes the situation and decides what to
 * do.
 */
static void AnalyzePlayersMouseClick()
{
	int tmp;

	// This flag avoids the mouse_move_target to change while the user presses
	// LMB to start a combo action.
	static int wait_mouseleft_release = FALSE;

	// No action is associated to MouseLeftRelease event or state.
	//

	if (!MouseLeftPressed()) {
		wait_mouseleft_release = FALSE;
		return;
	}

	if (ButtonPressWasNotMeantAsFire())
		return;
	if (no_left_button_press_in_previous_analyze_mouse_click) {
		level *obj_lvl = NULL;

		Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;

1578
		if ((tmp = clickable_obstacle_below_mouse_cursor(&obj_lvl, TRUE)) != -1) {
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
			get_obstacle_spec(obj_lvl->obstacle_list[tmp].type)->action_fn(obj_lvl, tmp);
			if (Me.mouse_move_target_combo_action_type != NO_COMBO_ACTION_SET)
				wait_mouseleft_release = TRUE;
			return;
		}

		// If the inventory screen is open, let it manage any possibly picked item.
		// Else, if the player left-clicked on an item, check if the item can be
		// picked up. If so, get it and give it to the player.
		// Note: if the item is too far away from Tux, check_for_items_to_pickup()
		// creates a combo action to reach the item.
		if (GameConfig.Inventory_Visible == FALSE) {
			if ((tmp = get_floor_item_index_under_mouse_cursor(&obj_lvl)) != -1) {
				if (check_for_items_to_pickup(obj_lvl, tmp)) {
					// The item can be picked up immediately , so give it to the player
					give_item(&obj_lvl->ItemList[tmp]);
					wait_mouseleft_release = TRUE;
				}
				return;
			}
		}
	}
	// Just after the beginning of a combo action, and while LMB is
	// always pressed, mouse_move_target must not be changed (so that
	// the player's character will actually move to the combo action's target)

	if (!wait_mouseleft_release)
		check_for_droids_to_attack_or_talk_with();
}


/**
 *  Handles arrow key based movements
 */
void set_movement_with_keys(int move_x, int move_y)
{
	float floor_center = 0.5;
	int move_amplitude = 1 + GameConfig.autorun_activated;

	// Center the target waypoint, or establish a new target waypoint if there isn't one already
	if (Me.mouse_move_target.z != -1) {
		Me.mouse_move_target.x = floor(Me.mouse_move_target.x) + floor_center;
		Me.mouse_move_target.y = floor(Me.mouse_move_target.y) + floor_center;
	} else {
		Me.mouse_move_target.z = Me.pos.z;
		Me.mouse_move_target.x = floor(Me.pos.x) + floor_center;
		Me.mouse_move_target.y = floor(Me.pos.y) + floor_center;
	}

1628 1629 1630 1631 1632 1633 1634
	//Restricts moving target to within 2 units from current position
	if (fabs(Me.pos.x - (Me.mouse_move_target.x + move_x * move_amplitude)) <= 2 &&
			fabs(Me.pos.y - (Me.mouse_move_target.y + move_y * move_amplitude)) <= 2) {
		// Determine move amount
		Me.mouse_move_target.x += move_x * move_amplitude;
		Me.mouse_move_target.y += move_y * move_amplitude;
	}
1635 1636
}

1637
void free_tux()
1638 1639 1640 1641 1642 1643 1644 1645 1646
{
	int i;

	free(Me.character_name);
	Me.character_name = NULL;

	clear_tux_mission_info();

	// We mark all the big screen messages for this character
1647
	// as out of date, so they can be overwritten with new
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
	// messages...
	//
	Me.BigScreenMessageIndex = 0;
	for (i = 0; i < MAX_BIG_SCREEN_MESSAGES; i++) {
		if (Me.BigScreenMessage[i]) {
			free(Me.BigScreenMessage[i]);
			Me.BigScreenMessage[i] = NULL;
		}
	}

1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
		if (Me.Inventory[i].type != -1) {
			delete_upgrade_sockets(&Me.Inventory[i]);
		}
	}
	delete_upgrade_sockets(&Me.weapon_item);
	delete_upgrade_sockets(&Me.drive_item);
	delete_upgrade_sockets(&Me.armour_item);
	delete_upgrade_sockets(&Me.shield_item);
	delete_upgrade_sockets(&Me.special_item);
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
}

/**
 * Reset the data in struct tux, in order to start a new game/load a game.
 */
void init_tux()
{
	int i;

	free_tux();

	memset(&Me, 0, sizeof(struct tux));

	Me.current_game_date = 0.0;
	Me.power_bonus_end_date = -1;	// negative dates are always in the past...
	Me.dexterity_bonus_end_date = -1;

	Me.speed.x = 0;
	Me.speed.y = 0;

	Me.pos.x = 0;
	Me.pos.y = 0;
	Me.pos.z = -1;
1691

1692 1693 1694
	Me.mouse_move_target.x = -1;
	Me.mouse_move_target.y = -1;
	Me.mouse_move_target.z = -1;
1695

1696 1697
	Me.teleport_anchor.x = 0;
	Me.teleport_anchor.y = 0;
1698 1699
	Me.teleport_anchor.z = -1;

1700
	enemy_set_reference(&Me.current_enemy_target_n, &Me.current_enemy_target_addr, NULL);
1701

1702
	Me.god_mode = FALSE;
1703

1704 1705
	Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
	Me.mouse_move_target_combo_action_parameter = -1;
1706

1707 1708 1709 1710 1711 1712
	Me.map_maker_is_present = FALSE;

	Me.temperature = 0.0;

	Me.health_recovery_rate = 0.2;
	Me.cooling_rate = 0.2;
1713

1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724
	Me.busy_time = 0;
	Me.busy_type = NONE;

	Me.phase = 0;
	Me.angle = 180;
	Me.walk_cycle_phase = 0;
	Me.weapon_swing_time = -1;
	Me.MissionTimeElapsed = 0;
	Me.got_hit_time = -1;

	Me.points_to_distribute = 0;
1725 1726

	// reset statistics
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742
	Me.meters_traveled = 0;
	for (i = 0; i < Number_Of_Droid_Types + 1; i++) {
		Me.destroyed_bots[i]    = 0;
		Me.damage_dealt[i]      = 0;
		Me.TakeoverSuccesses[i] = 0;
		Me.TakeoverFailures[i]  = 0;
	}

	for (i = 0; i < MAX_LEVELS; i++) {
		Me.HaveBeenToLevel[i] = FALSE;
		Me.time_since_last_visit_or_respawn[i] = -1;
	}

	Me.Experience = 0;
	Me.exp_level = 0;
	Me.Gold = 0;
1743

1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781
	Me.readied_skill = 0;
	for (i = 0; i < number_of_skills; i++) {
		Me.skill_level[i] = SpellSkillMap[i].present_at_startup;
	}

	GameConfig.spell_level_visible = 0;

	Me.melee_weapon_skill = 0;
	Me.ranged_weapon_skill = 0;
	Me.spellcasting_skill = 0;

	Me.running_power_bonus = 0;

	for (i = 0; i < 10; i++) {
		Me.program_shortcuts[i] = -1;
	}

	Me.paralyze_duration = 0;
	Me.invisible_duration = 0;
	Me.nmap_duration = 0;

	Me.readied_skill = 0;

	Me.quest_browser_changed = 0;

	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
		init_item(&Me.Inventory[i]);
	}
	init_item(&Me.weapon_item);
	init_item(&Me.armour_item);
	init_item(&Me.shield_item);
	init_item(&Me.special_item);
	init_item(&Me.drive_item);
	item_held_in_hand = NULL;

	clear_out_intermediate_points(&Me.pos, Me.next_intermediate_point, MAX_INTERMEDIATE_WAYPOINTS_FOR_TUX);
	Me.next_intermediate_point[0].x = -1;
	Me.next_intermediate_point[0].y = -1;
1782

1783 1784 1785 1786 1787 1788 1789
	Me.TextToBeDisplayed = "";
	Me.TextVisibleTime = 0;

	Me.base_physique = 20;
	Me.base_strength = 10;
	Me.base_dexterity = 15;
	Me.base_cooling = 25;
1790

1791
	UpdateAllCharacterStats();
1792

1793 1794 1795 1796
	Me.energy = Me.maxenergy;
	Me.running_power = Me.max_running_power;
}
#undef _influ_c