/*  Sarien - A Sierra AGI resource interpreter engine
 *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
 *  
 *  $Id: motion.c,v 1.6 2001/08/05 16:02:48 cmatsuoka Exp $
 *
 *  This program 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; see docs/COPYING for further details.
 */

#include "sarien.h"
#include "agi.h"
#include "rand.h"

static int dir_table[9] = {
	8, 1, 2,
	7, 0, 3,
	6, 5, 4
};

static int check_step (int delta, int step)
{
	return (-step >= delta) ? 0 : (step <= delta) ? 2 : 1;
}

static int check_block (int x, int y)
{
	if (x <= game.block.x1 || x >= game.block.x2)
		return FALSE;

	if (y <= game.block.y1 || y >= game.block.y2)
		return FALSE;

	return TRUE;
}

static void changepos (struct vt_entry *v)
{
	int b, x, y;

	x = v->x_pos;
	y = v->y_pos;
	b = check_block (x, y);

	switch (v->direction) {
	case 1:
		y -= v->step_size;
		break;
	case 2:
		x += v->step_size;
		y -= v->step_size;
		break;
	case 3:
		x += v->step_size;
		break;
	case 4:
		x += v->step_size;
		y += v->step_size;
		break;
	case 5:
		y += v->step_size;
		break;
	case 6:
		x -= v->step_size;
		y += v->step_size;
		break;
	case 7:
		x -= v->step_size;
		break;
	case 8:
		x -= v->step_size;
		y -= v->step_size;
		break;
	}

	if (check_block (x, y) == b) {
		v->flags &= ~MOTION;
	} else {
		v->flags |= MOTION;
		v->direction = 0;
		if_is_ego_view (v)
			game.vars[V_ego_dir] = 0;
	}
}

static void motion_wander (struct vt_entry *v)
{
	if (v->parm1--) {
		if (~v->flags & DIDNT_MOVE)
			return;
	} 

	v->direction = rnd (9);
	
	if_is_ego_view (v) {
		game.vars[V_ego_dir] = v->direction;
		while (v->parm1 < 6) {
			v->parm1 = rnd (51);	/* huh? */
		}
	}
}

static void motion_followego (struct vt_entry *v)
{
	int ego_x, ego_y;
	int obj_x, obj_y;
	int dir;
	int k;

	ego_x = game.view_table[0].x_pos + game.view_table[0].x_size / 2;
	ego_y = game.view_table[0].y_pos;

	obj_x = v->x_pos + v->x_size / 2;
	obj_y = v->y_pos;

	/* Get direction to reach ego */
	dir = get_direction (obj_x, obj_y, ego_x, ego_y, v->parm1);

	/* Already at ego coordinates */
	if (dir == 0) {
		v->direction = 0;
		v->motion = MOTION_NORMAL;
		setflag (v->parm2, TRUE);
		return;
	}

	if (v->parm3 == 0xff) {
		v->parm3 = 0;
	} else if (v->flags & DIDNT_MOVE) {
		int d;

		while ((v->direction = rnd (9)) == 0) {}

		d = (abs (ego_y - obj_y) + abs (ego_x - obj_x)) / 2 + 1;

		if (d <= v->step_size) {
			v->parm3 = v->step_size;
			return;
		}

		while ((v->parm3 = rnd (d)) < v->step_size) {}
		return;
	}

	if (v->parm3 != 0) {

		/* this is ugly and I dont know why this works, but other line
		   does not! */

		k=v->parm3;
		k-=v->step_size;
		v->parm3=k;

		if((SINT8)v->parm3<0)
		/* DF : watcom complained about lvalue */
		/* if ( ((SINT8)v->parm3 -= v->step_size) < 0) */
			v->parm3 = 0;
	} else {
		v->direction = dir;
	}
}

static void motion_moveobj (struct vt_entry *v)
{
	v->direction = get_direction (v->x_pos, v->y_pos, v->parm1,
		v->parm2, v->step_size);

	/* Update V6 if ego */
	if_is_ego_view (v)
		game.vars[V_ego_dir] = v->direction;

	if (v->direction == 0)
		in_destination (v);
}

static void check_motion (struct vt_entry *v)
{
	switch (v->motion) {
	case MOTION_WANDER:
		motion_wander (v);
		break;
	case MOTION_FOLLOW_EGO:
		motion_followego (v);
		break;
	case MOTION_MOVE_OBJ:
		motion_moveobj (v);
		break;
	}

	if ((!game.block.active || ~v->flags & IGNORE_BLOCKS) && v->direction)
		changepos (v);
}

 
/*
 * Public functions
 */

/**
 *
 */
void check_all_motions ()
{
	struct vt_entry *v;

	for_each_vt_entry (v) {
		if ((v->flags & (ANIMATED|UPDATE|DRAWN)) ==
			(ANIMATED|UPDATE|DRAWN) && v->step_time_count == 1)
		{
			check_motion (v);
		}
      }
}

/**
 *
 */
void in_destination (struct vt_entry *v)
{
	if (v->motion == MOTION_MOVE_OBJ) {
		v->step_size = v->parm3;
		setflag (v->parm4, TRUE);
	}
	v->motion = MOTION_NORMAL;
	if_is_ego_view (v)
		game.player_control = TRUE;
}

/**
 * Wrapper for static function motion_moveobj().
 * This function is used by cmd_move_object() in the first motion cycle
 * after setting the motion mode to MOTION_MOVE_OBJ.
 */
void move_obj (struct vt_entry *v)
{
	motion_moveobj (v);
}

int get_direction (int x, int y, int x0, int y0, int s)
{
	return dir_table [check_step(x0 - x, s) + 3 * check_step(y0 - y, s)];
}

/* end: motion.c */

