/********************************
  netpuyo(server)
  game.c --- game operation
  (C) Sep 24, 1997 ROYALPANDA
*******************************/

#include<stdio.h>
#include<unistd.h>
#include<time.h>
#include"protocol.h"
#include"server.h"


static int Next_puyo[200];


static void chat( int p )
{
  unsigned char buf[64];

  recv_str( p,buf,64 );
  send_init( MSG_TALK );
  send_snum( p );
  send_str( buf );
  send_msg( 0 );
  send_msg( 1 );
}


static int make_next( void )
{
  int i,j;
  int c[5];

  for( i = 0 ; i < 5 ; i++ )
    c[i] = 40;

  for( i = 0 ; i < 200 ; i++ ){
    while( c[(j = random() % 5)] == 0 )
      ;
    Next_puyo[i] = j+1;
    c[j]--;
  }
}

 
static void init_player( void )
{
  int i,x,y;

  for( i = 0 ; i < 2 ; i++ ){
    for( y = 0 ; y < 14 ; y++ )
      for( x = 0 ; x < 8 ; x++ )
	P[i].map[x][y] = 0;

    P[i].next[0] = Next_puyo[0];
    P[i].next[1] = Next_puyo[1];

    P[i].stock = 0;
    P[i].jama = 0;
    P[i].drop = 2;

    P[i].point = 0;
    P[i].del_all = 0;

    send_init( MSG_GAME );
    send_snum( i );
    send_snum( Next_puyo[0] );
    send_snum( Next_puyo[1] );
    send_snum( Next_puyo[2] );
    send_snum( Next_puyo[3] );
    send_msg( i );
  }
}


static int fall_puyo( int p )
{
  int x,y;
  int flag;

  flag = 0;
  for( y = 12 ; y > 0 ; y-- )
    for( x = 1 ; x < 7 ; x++ )
      if( P[p].map[x][y] == 0 && P[p].map[x][y-1] != 0){
	P[p].map[x][y] = P[p].map[x][y-1];
	P[p].map[x][y-1] = 0;
	flag = 1;
      }
  return flag;
}


static void move( int p )
{
  int x0,y0,x1,y1;

  x0 = recv_snum( p );
  if( x0 < 1 || x0 > 6 )
    return;
  y0 = recv_snum( p );
  if( y0 < 0 || y0 > 12 )
    return;
  x1 = recv_snum( p );
  if( x1 < 1 || x1 > 6 )
    return;
  y1 = recv_snum( p );
  if( y1 < 0 || y1 > 12 )
    return;
  
  P[p].x[0] = x0;
  P[p].y[0] = y0;
  P[p].x[1] = x1;
  P[p].y[1] = y1;
  
  send_init( MSG_PUYO );
  send_snum( P[p].puyo[0] );
  send_snum( x0 );
  send_snum( y0 );
  send_snum( P[p].puyo[1] );
  send_snum( x1 );
  send_snum( y1 );
  send_msg( 1 - p );
}

static void stop_move( int p )
{
  P[p].map[P[p].x[0]][P[p].y[0]] = P[p].puyo[0];
  P[p].map[P[p].x[1]][P[p].y[1]] = P[p].puyo[1];

  send_init( MSG_STND );
  send_snum( P[p].puyo[0] );
  send_snum( P[p].x[0] );
  send_snum( P[p].y[0] );
  send_snum( P[p].puyo[1] );
  send_snum( P[p].x[1] );
  send_snum( P[p].y[1] );
  send_msg( 1-p );

  while( fall_puyo( p ) )
    ;

  P[p].sc_multi = 0;
  P[p].mode = MD_FALL;
  P[p].wait = 0;
  if( P[p].del_all ){
    P[p].del_all = 0;
    P[p].point += Rate*30;
  }
}


static int killed( int p )
{
  int x,y,xx,yy,dir;
  int msk;
  int bit[13];
  int mode;

  int sc,stock;
  int t,tm;

  static int m_time[14] = {0,16,32,48,64,80,96,104,128,136,160,176,192,208};
  static int margin[15] = {256,196,128,96,64,48,32,24,16,12,8,6,4,3,2};

  for( y = 1 ; y < 13 ; y++ )
    bit[y] = 0;

  for( y = 1 ; y < 13 ; y++ ){
    for( x = 1, msk = 1 ; x < 7 ; x++,msk <<= 1 )
      if( P[p].map[x][y] == PY_KILLED ){
	P[p].map[x][y] = PY_NOTHING;
	bit[y] |= msk;
	for( dir = 0 ; dir < 4 ; dir++ ){
	  xx = x + Dir_x[dir];
	  yy = y + Dir_y[dir];
	  if( yy > 0 && P[p].map[xx][yy] == PY_OJAMA ){
	    P[p].map[xx][yy] = PY_NOTHING;
	    bit[yy] |= 1 << (xx-1);
	  }
	}
      }
  }

  send_init( MSG_KILL );
  send_snum( p );

  send_snum( P[p].sc_multi );

  for( y = 1 ; y < 13 ; y++ )
    send_snum( bit[y] );

  mode = 4;
  for( y = 0 ; y < 13 ; y++ )
    for( x = 1 ; x < 7 ; x++ )
      if( P[p].map[x][y] != PY_NOTHING ){
	mode = 0;
	y = 13;
	break;
      }

  if( mode )
    P[p].del_all = 1;
  
  tm = time(NULL) - Time_start - M_time;

  for( t = 0 ; t < 14 ; t++ )
    if( tm < m_time[t] )
      break;

  sc = P[p].point * 256;

  stock = sc  / (Rate * margin[t] );
  sc %= (Rate * margin[t] );

  P[p].point = sc / 256;

  if( P[p].stock )
    mode |= 2;

  if( P[p].stock < stock ){
    P[1-p].stock += stock - P[p].stock;
    P[p].stock = 0;
    mode |= 1;
  }else{
    P[p].stock -= stock;
  }

  send_snum( mode );
  send_lnum( P[0].stock );
  send_lnum( P[1].stock );

  send_msg( 0 );
  send_msg( 1 );

  while( fall_puyo( p ) )
    ;

}


static void attack( int p )
{

}


int start_move( int p )
{
  if( P[p].map[3][0] != PY_NOTHING || P[p].map[3][1] != PY_NOTHING )
    return 0;

  send_init( MSG_MOVE );

  P[p].puyo[0] = P[p].next[0];
  P[p].x[0] = 3;
  P[p].y[0] = 1;

  P[p].puyo[1] = P[p].next[1];
  P[p].x[1] = 3;
  P[p].y[1] = 0;

  P[p].next[0] = Next_puyo[P[p].drop++];
  P[p].next[1] = Next_puyo[P[p].drop++];
  if( P[p].drop == 200 )
    P[p].drop = 0;

  send_snum( P[p].puyo[0] );
  send_snum( P[p].puyo[1] );
  send_snum( P[p].next[0] );
  send_snum( P[p].next[1] );
  send_snum( Next_puyo[ P[p].drop ] );
  send_snum( Next_puyo[ P[p].drop+1 ] );
  send_msg( p );

  send_init( MSG_PUYO );
  send_snum( P[p].puyo[0] );
  send_snum( 3 );
  send_snum( 1 );
  send_snum( P[p].puyo[1] );
  send_snum( 3 );
  send_snum( 0 );
  send_msg( 1 - p );
  send_init( MSG_NEXT );
  send_snum( P[p].next[0] );
  send_snum( P[p].next[1] );
  send_snum( Next_puyo[P[p].drop ] );
  send_snum( Next_puyo[P[p].drop+1 ] );
  send_msg( 1-p );
  return 1;
}


static void jama_puyo( int p )
{
  int jama,line,top;
  int x,bit;

  if( P[p].stock > 30 ){
    jama = 30;
    P[p].stock -= 30;
  }else{
    jama = P[p].stock;
    P[p].stock = 0;
    P[p].jama = 0;
  }

  send_init( MSG_JAMA );
  send_snum( p );

  line = jama / 6;
  top = jama % 6;
  send_snum( line );

  for( bit = 0, jama = top ; jama ; jama-- ){
    do{
      x = 1 << (random()%6);
    }while( bit & x );
    bit |= x;
  }
  send_snum( bit );
  send_lnum( P[p].stock );
  send_msg( 0 );
  send_msg( 1 );

  for( ; line ; line-- ){
    for( x = 1 ; x < 7 ; x++ )
      if( P[p].map[x][0] == PY_NOTHING )
	P[p].map[x][0] = PY_OJAMA;
    fall_puyo(p);
  }

  for( x = 1, jama = 1; x < 7 ; x++, jama <<= 1 )
    if( (bit & jama) && P[p].map[x][0] == PY_NOTHING )
      P[p].map[x][0] = PY_OJAMA;


  while( fall_puyo( p ) )
    ;

}

static int restart( int p )
{
  int sc;

  P[p].wait = 0;
  switch( P[p].mode ){

  case MD_FALL:
    sc = kill_puyo( p );
    if( sc == 0 ){
      if( P[1-p].stock )
	P[1-p].jama = 1;
      if( P[p].stock && P[p].jama ){
	jama_puyo( p );
	P[p].mode = MD_JAMA;
      }else if( start_move( p ) )
	P[p].mode = MD_MOVE;
      else
	return 0; 
    }else{
      P[p].point += sc;
      killed( p );
      P[p].mode = MD_FALL;
    }
    break;

  case MD_JAMA:
    if( start_move( p ) )
      P[p].mode = MD_MOVE;
    else
      return 0;
    break;
  }
  return 1;
}


void wait_start( void )
{
  int msg;
  int start;
  int p;

  p = 0;
  msg = 0;

  for(;;){
    while( msg ){
      p = 1-p;
      if( msg & (1 << p) ){
	switch( recv_msg(p) ){
	case 0:
	  msg &= ~(1 << p );
	  break;

	case MSG_STRT:
	    return;
	  break;

	case MSG_CHAT:
	  chat( p );
	  break;

	case MSG_QUIT:
	  close( p );
	  send_init(MSG_QUIT);
	  send_msg( 1-p );
	  close( 1-p );
	  exit(0);
	  break;
	}
      }
    }
    fill_buf();
    msg = 3;
  }
}


void game( void )
{
  int p;
  int msg;
  int i;

  make_next();
  init_player();

  sleep(2);
  Time_start = time(NULL);

  start_move( 0 );
  P[0].mode = MD_MOVE;
  start_move( 1 );
  P[1].mode = MD_MOVE;

  msg = 0;
  p = 0;

  for(;;){
    while( msg ){
      p = 1-p;
      if( msg & ( 1 << p ) ){
	switch( recv_msg(p) ){
	case 0:
	  msg &= ~( 1 << p );
	  break;

	case MSG_PUTP:
	  if( P[p].mode == MD_MOVE )
	    move( p );
	  break;

	case MSG_STOP:
	  if( P[p].mode == MD_MOVE )
	    stop_move( p );
	  break;

	case MSG_OKAY:
	  i = recv_snum( p );
	  if( i < 0 || i > 1 )
	    break;
	  if( P[i].mode == MD_MOVE )
	    break;
	  P[i].wait |= 1 << p;
	  if( P[i].wait == 3 )
	    if( !restart( i ) ){
	      send_init(MSG_WINR);
	      send_snum( 1-i );
	      send_msg(0);
	      send_msg(1);
	      return;
	    }
	  break;

	case MSG_CHAT:
	  chat( p );
	  break;

	case MSG_QUIT:
	  close( p );
	  send_init(MSG_QUIT);
	  send_msg( 1-p );
	  close(1-p);
	  exit(0);
	  break;
	}
      }
    }
    fill_buf();
    msg = 3;
  }
}
