text: /* FCE Ultra - NES/Famicom Emulator
  *
  * Copyright notice for this file:
  *  Copyright (C) 2002 Xodnizel
  *
  * 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; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program 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
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
  
  #include <string.h>
  #include <stdlib.h>
  
  #include "share.h"
  #include "zapper.h"
  #include "../movie.h"
  
  ZAPPER ZD[2];
  
  static void ZapperFrapper(int w, uint8 *bg, uint8 *spr, uint32 linets, int final)
  {
  	int xs,xe;
  	int zx,zy;
  
  	if(!bg) // New line, so reset stuff.
  	{
  		ZD[w].zappo=0;
  		return;
  	}
  	xs=ZD[w].zappo;
  	xe=final;
  
  	zx=ZD[w].mzx;
  	zy=ZD[w].mzy;
  
  	if(xe>256) xe=256;
  
  	if(scanline>=(zy-4) && scanline<=(zy+4))
  	{
  		while(xs<xe)
  		{
  			uint8 a1,a2;
  			uint32 sum;
  			if(xs<=(zx+4) && xs>=(zx-4))
  			{
  				a1=bg[xs];
  				if(spr)
  				{
  					a2=spr[xs];
  
  					if(!(a2&0x80))
  						if(!(a2&0x40) || (a1&64))
  							a1=a2;
  				}
  				a1&=63;
  
  				sum=palo[a1].r+palo[a1].g+palo[a1].b;
  				if(sum>=100*3)
  				{
  					ZD[w].zaphit=((uint64)linets+(xs+16)*(PAL?15:16))/48+timestampbase; 
  					goto endo;
  				}
  			}   
  			xs++;
  		}
  	}
  endo:
  	ZD[w].zappo=final;
  
      //if this was a miss, clear out the hit
      if(ZD[w].mzb&2)
          ZD[w].zaphit=0;
          
  }      
  
  static INLINE int CheckColor(int w)
  {
  	FCEUPPU_LineUpdate();
  
      if(newppu)
      {
          int x = (int)ZD[w].mzx;
          int y = (int)ZD[w].mzy;
          int b = (int)ZD[w].mzb;
          bool  block = (b&2)!=0;
  
          int mousetime = y*256+x;
          int nowtime = scanline*256 + g_rasterpos;
  
          if(!block && mousetime < nowtime && mousetime >= nowtime - 384)
          {
              extern uint8 *XBuf;
              uint8 *pix = XBuf+(ZD[w].mzy<<8);
              uint8 a1 = pix[ZD[w].mzx];
              a1&=63;
              uint32 sum=palo[a1].r+palo[a1].g+palo[a1].b;
              //return ZD[w].zaphit = sum != 0;
              ZD[w].zaphit = (sum>=100*3)?1:0;
          }
          else
          {
              ZD[w].zaphit = 0;
          }
  
          return ZD[w].zaphit?0:1;
      }
  
  
      if((ZD[w].zaphit+100)>=(timestampbase+timestamp))
      {
          return 0;
      }
  
  	return 1;
  }
  
  static uint8 ReadZapperVS(int w)
  {
  	uint8 ret=0;
  
  	if(ZD[w].zap_readbit==4) ret=1;
  
  	if(ZD[w].zap_readbit==7)
  	{
  		if(ZD[w].bogo)
  			ret|=0x1;
  	}
  	if(ZD[w].zap_readbit==6)
  	{
  		if(!CheckColor(w))
  			ret|=0x1;
  	}
  	if(!fceuindbg)
  		ZD[w].zap_readbit++; 
  	return ret;
  }
  
  static void StrobeZapperVS(int w)
  {                        
  	ZD[w].zap_readbit=0;
  }
  
  static uint8 ReadZapper(int w)
  {
  	uint8 ret=0;
  	if(ZD[w].bogo)
  		ret|=0x10;
  	if(CheckColor(w))
  		ret|=0x8;
  	return ret;
  }
  
  static void DrawZapper(int w, uint8 *buf, int arg)
  {
  	FCEU_DrawGunSight(buf, ZD[w].mzx,ZD[w].mzy);
  }
  
  static void UpdateZapper(int w, void *data, int arg)
  {
  	uint32 *ptr=(uint32 *)data;
  
      bool newclicked = (ptr[2]&3)!=0;
      bool oldclicked = (ZD[w].lastInput)!=0;
  
  	if(ZD[w].bogo)
      {
  		ZD[w].bogo--;	
      }
  
      ZD[w].lastInput = ptr[2]&3;
  
      //woah.. this looks like broken bit logic.
  	if(newclicked && !oldclicked)
      {
  		ZD[w].bogo=5;
  	    ZD[w].mzb=ptr[2];
  	    ZD[w].mzx=ptr[0];
  	    ZD[w].mzy=ptr[1];
      }
  
  }
  
  static void LogZapper(int w, MovieRecord* mr)
  {
  	mr->zappers[w].x = ZD[w].mzx;
  	mr->zappers[w].y = ZD[w].mzy;
  	mr->zappers[w].b = ZD[w].mzb;
  	mr->zappers[w].bogo = ZD[w].bogo;
  	mr->zappers[w].zaphit = ZD[w].zaphit;
  }
  
  static void LoadZapper(int w, MovieRecord* mr)
  {
  	ZD[w].mzx = mr->zappers[w].x;
  	ZD[w].mzy = mr->zappers[w].y;
  	ZD[w].mzb = mr->zappers[w].b;
  	ZD[w].bogo = mr->zappers[w].bogo;
  	ZD[w].zaphit = mr->zappers[w].zaphit;
  }
  
  
  static INPUTC ZAPC={ReadZapper,0,0,UpdateZapper,ZapperFrapper,DrawZapper,LogZapper,LoadZapper};
  static INPUTC ZAPVSC={ReadZapperVS,0,StrobeZapperVS,UpdateZapper,ZapperFrapper,DrawZapper,LogZapper,LoadZapper};
  
  INPUTC *FCEU_InitZapper(int w)
  {
  	memset(&ZD[w],0,sizeof(ZAPPER));
  	if(GameInfo && GameInfo->type == GIT_VSUNI)
  		return(&ZAPVSC);
  	else
  		return(&ZAPC);
  }
  
  
  