/*
 * redraw.c
 */
#include "copyright.h"

#include <stdio.h>
#include <signal.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "packets.h"

intrupt()
{
    if (testtime == -1) {
	struct reserved_spacket sp;

	    /* Give reasonable period of time to respond to query 
	     *  (and test code if they need to) 
	     */
#ifdef RSA
	RSA_Client = 0;
#endif
	testtime = 200;	
	makeReservedPacket(&sp);
	MCOPY(sp.data, testdata, RESERVED_SIZE);
	sendClientPacket(&sp);
    } else if (testtime != 0) {
	testtime--;
	if (testtime==0) {
	  if (bypassed) {                     /* Deal with .bypass entries */
	    testtime = 0;
	    pmessage2(0, MALL | MJOIN, "GOD->ALL", me->p_no,
                    "%s %.16s is not using an RSA client",
                    ranks[me->p_stats.st_rank].name,
                      me->p_name);
	    ERROR(3,(".bypass person : %s - logged on\n",me->p_login));
	    me->p_stats.st_flags |= ST_CYBORG; /* mark for reference */
	    new_warning(UNDEF,"You are in the .bypass file, be good");
          } else {
	    /* User failed to respond to verification query.  Bye! */
	    if (!binconfirm)
		me->p_stats.st_flags |= ST_CYBORG; /* mark for reference 7/27/91 TC */
	    else {
		me->p_explode = 10;
		me->p_whydead = KQUIT;
		me->p_status = PEXPLODE;
		ERROR(3,("User binary failed to verify\n"));
#ifdef RSA
#ifdef SHOW_RSA
		pmessage2(0, MALL | MJOIN, "GOD->ALL", me->p_no,
			"%s %.16s failed to verify",
			ranks[me->p_stats.st_rank].name,
                	me->p_name);
#endif
		if (RSA_Client==1)
		    new_warning(UNDEF,"No customized binaries.  Please use a blessed one.");
		else if (RSA_Client==2)
                    new_warning(UNDEF,"Wrong Client Version Number!");
                else
		    new_warning(UNDEF,"You need a spiffy new RSA client for this server!");
#else
		new_warning(UNDEF,"No customized binaries. Please use a blessed one.");
#endif
	    }
	}
      }
    }
    if (me->p_status == PFREE) {
	ERROR(1,("intrupt: exitGame() on PFREE (pid %i)\n",getpid()));
	exitGame();
    }

#ifdef OBSERVERS
    if (!Observer || me->p_status == PEXPLODE)
#endif
    {
	if (me->p_status == PEXPLODE || me->p_status == PDEAD) {
	    FD_ZERO(&inputMask);
	}
	if (((me->p_status == PDEAD) || (me->p_status == POUTFIT))
	      && (me->p_ntorp <= 0) && (me->p_nplasmatorp <= 0)) {
	    death();
	}
       
    }

    if (clue) {
	    if (clueFuse > CLUEINTERVAL) { 
		clueVerified = 0;
		clueFuse = 0;
		clueCount = 0;
	    } else {
		if (queues[QU_PICKUP].count != 0) clueFuse++;
	    }
	    if (!clueVerified) clue_check();
    }
    auto_features();
    updateClient();
}

#ifdef OBSERVERS
    /* observer is usually status POBSERV which is sort of like PDEAD */
    /* may get set to PDEAD or PEXPLODE by daemon events. deal with */
    /* those cases  reasonably gracefully */

int check_observs()
{
    struct player *pl;
    struct planet *pln;

    if (!Observer) return 0;
    if (me->p_status != POBSERV) {
      if (me->p_status == PDEAD || me->p_status == PEXPLODE)
            death ();
        else{
	    if (me->p_status == PFREE){
	      ERROR(1,("check_observs:  changing a PFREE!\n"));
	    }
            me->p_status = POBSERV;
	}
    }
    if (FD_ISSET (CP_SPEED, &inputMask)) {
        /* only let observer do a limited set of actions. */
        FD_ZERO (&inputMask);
        FD_SET (CP_MESSAGE, &inputMask);
        FD_SET (CP_QUIT, &inputMask);
        FD_SET (CP_LOGIN, &inputMask);
        FD_SET (CP_OUTFIT, &inputMask);
        FD_SET (CP_PLANLOCK, &inputMask);
        FD_SET (CP_PLAYLOCK, &inputMask);
        FD_SET (CP_SOCKET, &inputMask);
        FD_SET (CP_OPTIONS, &inputMask);
        FD_SET (CP_BYE, &inputMask);
        FD_SET (CP_UPDATES, &inputMask);
        FD_SET (CP_RESERVED, &inputMask);
        FD_SET (CP_UDP_REQ, &inputMask);
        FD_SET (CP_SEQUENCE, &inputMask);
#ifdef RSA
        FD_SET (CP_RSA_KEY, &inputMask);
#endif
#ifdef PING
        FD_SET (CP_PING_RESPONSE, &inputMask);
#endif
#ifdef SHORT_PACKETS
        FD_SET (CP_S_REQ, &inputMask);
        FD_SET (CP_S_THRS, &inputMask);
        FD_SET (CP_S_MESSAGE, &inputMask);
        FD_SET (CP_S_RESERVED, &inputMask);
#endif
        FD_SET (MAX_CP_PACKETS, &inputMask);
    }
    /* handle quitting  -- it just happens for the observer */
    if (me->p_flags & PFSELFDEST) {
        me->p_status = POUTFIT;
        me->p_whydead = KQUIT;
        death ();
    }

    /* Check if I am locked onto a player */
    if (me->p_flags & PFPLOCK) {
        if ( (me->p_playerl < 0) || (me->p_playerl >= MAXPLAYER) ){
	    /* I am not locked onto a valid player */
            me->p_playerl = 0;         /* ensure a safe value */
	    me->p_flags &= ~(PFPLOCK); /* turn off player lock */
            me->p_x = -100000;         /* place me off the tactical */
            me->p_y = -100000;
	    return 0;
	}

	/* I am locked onto a valid player */
	pl = &players[me->p_playerl];
	if (pl->p_status != PALIVE) {
	  me->p_x = -100000;
	  me->p_y = -100000;
	  /* me->p_flags &= ~(PFPLOCK); */
	  new_warning (99,"Player lock lost while player dead.");
	}
	else if ( (pl->p_team != me->p_team) && (pl->p_team != 0)) {
	  /* allow locking on independent players (robots) */
	  new_warning (100,"Can only lock on own team.");
	  me->p_x = -100000;
	  me->p_y = -100000;
	  me->p_flags &= ~(PFPLOCK); /* turn off player lock */
	} else {
	  /* follow a particular player */
	  me->p_ship.s_type = pl->p_ship.s_type;
	  me->p_x = pl->p_x;
	  me->p_y = pl->p_y;
	  me->p_shield = pl->p_shield;
	  me->p_damage = pl->p_damage;
	  me->p_armies = pl->p_armies;
	  me->p_fuel = pl->p_fuel;
	  me->p_wtemp = pl->p_wtemp;
	  me->p_etemp = pl->p_etemp;
	  me->p_flags = (pl->p_flags & ~(PFSELFDEST | PFPLOCK | PFPLLOCK)) |
	    (me->p_flags & (PFSELFDEST | PFPLOCK | PFPLLOCK));
	  
	  /* always cloaked, state of person watched will be obvious */
	  /* me->p_flags |= PFCLOAK; */
	  me->p_dir = pl->p_dir;
	  me->p_flags |= PFOBSERV;  /* future clients may understand this */
	  me->p_tractor = pl->p_tractor;
	  
	  me->p_speed = pl->p_speed;
	  /* me->p_kills = pl->p_kills;  Very bad: will confuse enemies */
	}
	return 1;
    }  /* end if I am locked onto a player */
    
    /* Check if I am locked onto a planet */
    if (me->p_flags & PFPLLOCK) {
        if ( (me->p_planet < 0) || (me->p_planet >= MAXPLANETS) ){
	    /* I am not locked onto a valid planet */
  	    me->p_planet = 0;            /* ensure a safe value */
	    me->p_flags &= ~(PFPLLOCK);  /* turn off planet lock */
	    me->p_x = -100000;           /* place me off the tactical */
	    me->p_y = -100000;
	    return 0;
        }

	/* I am locked onto a valid planet */
	pln = &planets[me->p_planet];
	if (pln->pl_owner != me->p_team) {
	    if ( (me->p_x == pln->pl_x) && (me->p_y == pln->pl_y) )
	      /* we are sitting on the planet already, but it is not ours */
  	      new_warning(102,"Planet lock lost on change of ownership.");
	    else
	      new_warning(101,"You can only warp to your own team's planets!");
	    me->p_x = -100000;
	    me->p_y = -100000;
	    me->p_flags &= ~(PFPLLOCK); /* turn off planet lock */
	} else {
	    me->p_x = pln->pl_x;
	    me->p_y = pln->pl_y;
	}
	return 1;
    }   /* end if I am locked onto a player */

    /* We are not locked onto anything */
    me->p_x = -100000;
    me->p_y = -100000;

    return 0;
}
#endif /* OBSERVERS */



/* These are routines that need to be done on interrupts but
   don't belong in the redraw routine and particularly don't
   belong in the daemon. */

auto_features()
{
    struct player *pl;
    struct planet *pln;
    u_char course;
    int troop_capacity=0;

#ifdef OBSERVERS
    check_observs();
#endif

    if (me->p_flags & PFSELFDEST) {
	if ((me->p_updates >= selfdest) ||
	    ((me->p_flags & PFGREEN) && (me->p_damage == 0)
		&& (me->p_shield == me->p_ship.s_maxshield))) {
	    me->p_flags &= ~PFSELFDEST;
	    me->p_explode = 10;
	    me->p_whydead = KQUIT;
	    me->p_status = PEXPLODE;
	} else {
	    switch ((selfdest - me->p_updates)/10) {
	    case 5: case 4:
		new_warning(UNDEF,"You notice everyone on the bridge is staring at you.");
		break;
	    default:
		new_warning(UNDEF, "Stand By ... Self Destruct in %d seconds",
			(selfdest - me->p_updates) / 10);
		break;
	    }
	}
    }

    /* provide a refit countdown 4/6/92 TC */

    if (me->p_flags & PFREFITTING) {
	static int lastRefitValue = 0; /* for smooth display */
	if (lastRefitValue != (rdelay - me->p_updates)/10) {
	    lastRefitValue = (rdelay - me->p_updates)/10; /* CSE to the rescue? */
	    switch (lastRefitValue) {
	      case 3:
	      case 2:
		new_warning(UNDEF, "Engineering:  Energizing transporters in %d seconds", lastRefitValue);
		break;
	      case 1:
		new_warning(UNDEF, "Engineering:  Energize. [ SFX: chimes ]");
		break;
	      case 0:
		switch (random()%5) {
		  case 0:
		    new_warning(UNDEF,"Wait, you forgot your toothbrush!");
		    break;
		  case 1:
		    new_warning(UNDEF,"Nothing like turning in a used ship for a new one.");
		    break;
		  case 2:
		    new_warning(UNDEF,"First officer:  Oh no, not you again... we're doomed!");
		    break;
		  case 3:
		    new_warning(UNDEF,"First officer:  Uh, I'd better run diagnostics on the escape pods.");
		    break;
		  case 4:
		    new_warning(UNDEF,"Shipyard controller:  This time, *please* be more careful, okay?");
		    break;
		}
		break;
	    }
	}
    }

    /* provide a war declaration countdown 4/6/92 TC */

    if (me->p_flags & PFWAR) {
	static int lastWarValue = 0;
	if (lastWarValue != (delay - me->p_updates)/10) {
	    lastWarValue = (delay - me->p_updates)/10; /* CSE to the rescue? */
	    switch (lastWarValue) {
	      case 9:
		new_warning(UNDEF,"Weapons officer:  Not again!  This is absurd...");
		break;
	      case 8:
		break;
	      case 7:
		new_warning(UNDEF,"Weapons officer:  ... the whole ship's computer is down?");
		break;
	      case 6:
		break;
	      case 5:
		new_warning(UNDEF,"Weapons officer:  Just to twiddle a few bits of the ship's memory?");
		break;
	      case 4:
		break;
	      case 3:
		new_warning(UNDEF,"Weapons officer:  Bah! [ bangs fist on inoperative console ]");
		break;
	      case 2:
		break;
	      case 1:
		new_warning(UNDEF,"First Officer:  Easy, big guy... it's just one of those mysterious");
		break;
	      case 0:
		switch (random()%5) {
		  case 0:
		    new_warning(UNDEF,"First Officer:  laws of the universe, like 'tires on the ether'.");
		    break;
		  case 1:
		    new_warning(UNDEF,"First Officer:  laws of the universe, like 'Klingon bitmaps are ugly'.");
		    break;
		  case 2:
		    new_warning(UNDEF,"First Officer:  laws of the universe, like 'all admirals have scummed'.");
		    break;
		  case 3:
		    new_warning(UNDEF,"First Officer:  laws of the universe, like 'Mucus Pig exists'.");
		    break;
		  case 4:
		    new_warning(UNDEF,"First Officer:  laws of the universe, like 'guests advance 5x faster'.");
		    break;
		}
		break;
	    }
	}
    }

    /* give certain information about bombing or beaming */
    if (me->p_flags & PFBOMB) {
	if (planets[me->p_planet].pl_armies < 5) {
	    new_warning(UNDEF, "Weapons Officer: Bombarding %s... Ineffective, %d armies left.",
			planets[me->p_planet].pl_name,    /* Planet name for stats 10/20/96 [007] */
		    planets[me->p_planet].pl_armies); /* nifty info feature 2/14/92 TMC */
	    me->p_flags &= ~PFBOMB;
	}
	else {
	    new_warning(UNDEF, "Weapons Officer: Bombarding %s...  Sensors read %d armies left.",
		planets[me->p_planet].pl_name,
		planets[me->p_planet].pl_armies);
	}
    }

    if (me->p_ship.s_type == ASSAULT)
	troop_capacity = (((me->p_kills * 3) > myship->s_maxarmies) ?
	    myship->s_maxarmies : (int) (me->p_kills * 3));
    else if (me->p_ship.s_type != STARBASE)
	troop_capacity = (((me->p_kills * 2) > myship->s_maxarmies) ?
	    myship->s_maxarmies : (int) (me->p_kills * 2));
    else
	troop_capacity = me->p_ship.s_maxarmies;
    if (me->p_flags & PFBEAMUP) {
        /* Messages rewritten for stats 10/20/96 [007] */
        char *txt;
	if (me->p_flags & PFORBIT) {
	  if (planets[me->p_planet].pl_armies < 5) {
	    txt = "Too few armies to beam up";
	    me->p_flags &= ~PFBEAMUP;
	  } else if (me->p_armies == troop_capacity) {
	    txt = "No more room on board for armies";
	    me->p_flags &= ~PFBEAMUP;
	  } else {
	    txt = "Beaming up";
	  }
	  new_warning(UNDEF, "%s: (%d/%d) %s has %d armies left",
		      txt,
		      me->p_armies,
		      troop_capacity,
		      planets[me->p_planet].pl_name,	
		      planets[me->p_planet].pl_armies);
	    
	} else if (me->p_flags & PFDOCK) {
	    if (players[me->p_docked].p_armies == 0) {
		txt = "Too few armies to beam up";
		me->p_flags &= ~PFBEAMUP;
	    } else if (me->p_armies == troop_capacity) {
		txt = "No more room on board for armies";
		me->p_flags &= ~PFBEAMUP;
	    } else {
	        txt = "Beaming up";
	    }
	    new_warning(UNDEF, "%s: (%d/%d) Starbase %s has %d armies left",
			txt,
			me->p_armies,
			troop_capacity,
			players[me->p_docked].p_name,
			players[me->p_docked].p_armies);
	}
    }

    if (me->p_flags & PFBEAMDOWN) {
        /* Messages rewritten for stats 10/20/96 [007] */
        char *txt;
	if (me->p_flags & PFORBIT) {
	  if (me->p_armies == 0) {
	    txt = "No more armies to beam down";
	    me->p_flags &= ~PFBEAMDOWN;
	  } else {
	    txt = "Beaming down";
	  }
	  new_warning(UNDEF, "%s: (%d/%d) %s has %d armies left",
		      txt,
		      me->p_armies,
		      troop_capacity,
		      planets[me->p_planet].pl_name,	
		      planets[me->p_planet].pl_armies);

	} else if (me->p_flags & PFDOCK) {
	    if (me->p_armies == 0) {
		txt = "No more armies to beam down";
		me->p_flags &= ~PFBEAMDOWN;
	    } else if (players[me->p_docked].p_armies ==
		players[me->p_docked].p_ship.s_maxarmies) {
	      txt = "All troop bunkers are full";
	      me->p_flags &= ~PFBEAMDOWN;
	    } else {
	        txt = "Transfering ground units";
	    }
	    new_warning(UNDEF, "%s: (%d/%d) Starbase %s has %d armies left",
			txt,
			me->p_armies,
			troop_capacity,
			players[me->p_docked].p_name,
			players[me->p_docked].p_armies);
	}
    }

    if (me->p_flags & PFREPAIR) {
	if ((me->p_damage == 0) && (me->p_shield == me->p_ship.s_maxshield))
	    me->p_flags &= ~PFREPAIR;
    }
    if ((me->p_flags & PFPLOCK) 
#ifdef OBSERVERS
		&& (!Observer)
#endif
	) {/* set course to player x */
	int dist;

	pl = &players[me->p_playerl];
	if (pl->p_status != PALIVE)
	    me->p_flags &= ~PFPLOCK;
	if (pl->p_ship.s_type == STARBASE) {
#ifdef SB_TRANSWARP
	if (me->p_flags & PFTWARP){
	  if (pl->p_status != PALIVE){
	     new_warning(UNDEF, "Starbase is dead; Transwarp aborted!", -1);
	     me->p_flags &= ~PFTWARP;
	     me->p_speed = 3;
	     set_speed(0);
	  }
	  if (pl->p_ship.s_type != STARBASE){
	     new_warning(UNDEF, "Starbase has been turned in; Transwarp aborted!", -1);
	     me->p_flags &= ~PFTWARP;
	     me->p_speed = 3;
	     set_speed(0);
	  }
	  if(me->p_etemp > me->p_ship.s_maxegntemp - 10){
	     new_warning(UNDEF,"Warp drive reaching critical temperature!", -1);
	     me->p_flags &= ~PFTWARP;
	     me->p_speed = 3;
	     set_speed(0);
	  }
 	  else
	  if (me->p_fuel < (int) (me->p_ship.s_maxfuel/8)){
	     new_warning(UNDEF, "Not enough fuel to continue transwarp!", -1);
	     me->p_flags &= ~PFTWARP;
	     me->p_speed = 3;
	     set_speed(0);
	  }
	  else if(!(me->p_flags & PFPLOCK)){
	     new_warning(UNDEF, "Lost player lock!", -1);
	     me->p_flags &= ~PFTWARP;
	     me->p_speed = 3;
	     set_speed(0);
	  }
	}
#endif
	    dist = hypot((double) (me->p_x - pl->p_x),
		(double) (me->p_y - pl->p_y));
	    if (dist-(DOCKDIST/2) < (11500 * me->p_speed * me->p_speed) /
		    me->p_ship.s_decint) {
		if (me->p_desspeed > 2) {
		    set_speed(me->p_desspeed-1);
		}
	    }
#ifdef SB_TRANSWARP
	if ((dist < 2*DOCKDIST) && (me->p_flags & PFTWARP)){
	    me->p_x = pl->p_x; me->p_y = pl->p_y;
	    me->p_speed=2;
	    me->p_flags &= ~(PFPLOCK);
	    orbit();
	    me->p_flags &= ~(PFTWARP);
	} else
#endif
	    if ((dist < DOCKDIST) && (me->p_speed <= 2))  {
		me->p_flags &= ~PFPLOCK;
		orbit();
	    }
	}
	if (me->p_flags & PFPLOCK) {
	    course = newcourse(pl->p_x, pl->p_y);
#ifdef SB_TRANSWARP
	    if (me->p_flags & PFTWARP)
		me->p_dir = course;
	    else
#endif
	    set_course(course);
	}
    }
    if ((me->p_flags & PFPLLOCK) 
#ifdef OBSERVERS
		&& (!Observer)
#endif
	) {/* set course to planet x */
	int dist;

	pln = &planets[me->p_planet];
	dist = hypot((double) (me->p_x - pln->pl_x),
	    (double) (me->p_y - pln->pl_y));

	/* This is magic.  It should be based on defines, but it slows
	   the ship down to warp two an appropriate distance from the
	   planet for orbit */

	if (dist-(ORBDIST/2) < (11500 * me->p_speed * me->p_speed) /
	        me->p_ship.s_decint) {
	    if (me->p_desspeed > 2) {
		set_speed(2);
	    }
	}

	if ((dist < ENTORBDIST) && (me->p_speed <= 2))  {
	    me->p_flags &= ~PFPLLOCK;
	    orbit();
	}
	else {
	    course = newcourse(pln->pl_x, pln->pl_y);
            /* jac: avoid superfluous midflight wobble */
            if ( (ABS(course-me->p_desdir) > 2) || (dist < ENTORBDIST*10) )
	    set_course(course);
	}
    }
}

newcourse(x, y)
int x, y;
{
	return((u_char) nint(atan2((double) (x - me->p_x),
	    (double) (me->p_y - y)) / 3.14159 * 128.));
}

