
class ZLaserTripBombMissile : Actor
{

	override int SpecialMissileHit(Actor victim)
	{
		if(victim) {
			if (victim.bShootable && !victim.bINVULNERABLE && (!victim.bInvisible || (victim.bInvisible && victim.GetClassName()=="LaserTripBomb")) ) {
				let tripbomb = ZLaserTripBomb(Spawn("LaserTripBomb", self.pos));
				tripbomb.angle = self.angle;
				if(victim.GetClassName()=="LaserTripBomb" && victim.tracer)
					tripbomb.tracer = victim.tracer;
				else
					tripbomb.tracer = victim;
				tripbomb.target = self.target;

				tripbomb.posoff.x = -((self.pos.x-tripbomb.tracer.pos.x)*cos(self.angle)+(self.pos.y-tripbomb.tracer.pos.y)*sin(self.angle));
				tripbomb.posoff.y = -((self.pos.x-tripbomb.tracer.pos.x)*sin(self.angle)-(self.pos.y-tripbomb.tracer.pos.y)*cos(self.angle));
				tripbomb.posoff.z = self.pos.z - tripbomb.tracer.pos.z;
				tripbomb.angleoff = (self.angle - tripbomb.tracer.angle)+180;
				tripbomb.heightoff = tripbomb.tracer.height;
				tripbomb.sticktoactor = true;
				if(self.GetClassName()=="ThrownMineTripBomb") tripbomb.mine = true;

				self.destroy();
				return 0;
			} else {
				self.A_PlaySound("LaserTripBomb/Place2",5);

				let tripbomb = ZLaserTripBomb(Spawn("LaserTripBomb", self.pos));
				tripbomb.angle = self.angle;
				tripbomb.bNOGRAVITY = false;
				tripbomb.target = self.target;
				if(self.GetClassName()=="ThrownMineTripBomb") tripbomb.mine = true;

				self.destroy();
				return 0;
			}
		}
		return super.SpecialMissileHit(victim);
	}

	void A_SpawnTripBomb(string stripbomb)
	{
		bool b;
		actor tripbomb;
		[b, tripbomb] = self.A_SpawnItemEx(stripbomb);
		if(self.GetClassName()=="ThrownMineTripBomb") ZLaserTripBomb(tripbomb).mine = true;
	}

}

class ZLaserTripBomb : Actor
{
  const TBoffz = 5.0;
  Vector3 posoff;
  float angleoff;
  double heightoff;
  bool sticktoactor;
  actor tripbombfake;
  bool mine;

	override void Tick()
	{
		Super.Tick();

		if(health<1) return;

		if(sticktoactor) {
			if(!tripbombfake) {
				tripbombfake = Spawn("LaserTripBombFake",self.pos);
				tripbombfake.master = self;
				tripbombfake.angle = self.angle;
				self.bINVISIBLE = true;
			}
			if(tracer && tracer.health>0  && tracer.bINVULNERABLE==false) {
				float posx = posoff.x;
				float posy = posoff.y;
				float posz = max(posoff.z + (tracer.height-heightoff) - (tracer.GetFloorTerrain().footclip*!tracer.bFLOAT), 0);

				self.A_Warp(AAPTR_TRACER, posx, posy, posz, tracer.angle+angleoff, WARPF_INTERPOLATE|WARPF_NOCHECKPOSITION|WARPF_ABSOLUTEANGLE|WARPF_STOP|WARPF_BOB);

				if(tripbombfake) tripbombfake.A_Warp(AAPTR_MASTER, -0.50*tracer.radius, 0, 0, 0, WARPF_INTERPOLATE|WARPF_NOCHECKPOSITION|WARPF_STOP|WARPF_BOB);
			} else {
				if(tripbombfake) tripbombfake.destroy();
				self.bINVISIBLE = false;
				sticktoactor = false;
				tracer = null;
				self.A_PlaySound("LaserTripBomb/Place2",5);
				self.A_Gravity();
			}
		}
	}

	override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
    	{
		actor temptarget = target;

	        int i = super.DamageMobj(inflictor, source, damage, mod, flags, angle);

		target = temptarget;

		return i;
	}

	override void Die(Actor source, Actor inflictor, int dmgflags)
	{
		sticktoactor = false;
		tracer = null;
		if(tripbombfake) tripbombfake.destroy();
		super.Die(source, inflictor, dmgflags);
	}

	void A_TripbombActive(int mineticks, bool light=false, bool detect=true)
	{
		if(mine) {
			self.A_SetTics(mineticks);
			if(light) {
				self.frame++;
				int distoff = 0;
				if(tripbombfake && tracer) distoff = 0.50*tracer.radius;
				self.A_SpawnItem("RedFlare23Small",2-distoff,2);
			}
			if(detect) TripbombMine();
		} else {
			TripbombLaser();
		}
	}

	void TripbombMine()
	{
		BlockThingsIterator it = BlockThingsIterator.Create(self, 150);
		Actor mo = null;

		while ( it.Next() )
		{
			mo = it.thing;
			if (!mo) { continue; }
			if (mo == self || mo.health <= 0 || mo.bDormant || !mo.bShootable) { continue; }
			if (!mo.bISMONSTER || mo.bFriendly) { continue; }
			if (mo.bInvisible || mo.bINVULNERABLE) { continue; }
			if (!CheckSight(mo)) { continue; }
			double mo_distance = self.distance3DSquared(mo);
			if (mo_distance > 150*150) { continue; }
			self.A_Die();
			break;
		}
	}

  void TripbombLaser()
  {
    Actor a = self;
    double forwardoff = 0;
    if(tracer) forwardoff = 20;
    FTranslatedLineTarget t;
    Actor tempPuff = a.LineAttack( a.angle
                            	, 4000.0
                            	, 0
                            	, 0
                            	, "none"
                            	, "LaserTripPuff"
                            	, LAF_NOIMPACTDECAL | LAF_NORANDOMPUFFZ | LAF_NOINTERACT | LAF_OVERRIDEZ
                            	, victim: t
		    	    	, TBoffz
		    	    	, forwardoff);
    if (tempPuff == null) { return; }
    double distance=a.Distance3D(tempPuff);
    ShowLaser(a, tempPuff.pos, distance);

    if(t.linetarget){
	if( (t.linetarget.bISMONSTER && !t.linetarget.bFRIENDLY) || (t.linetarget.species=="HeadshotTarget") || (t.linetarget.species=="MetalShield") ){
		a.A_Die();
	}
    }
  }

  private void ShowLaser(Actor a, vector3 targetPos, double distance)
  {
    color   beamColor  = "ff 00 00";
    double  size       = 2.0 * 3;

    vector3 apostemp = a.pos;
    apostemp.z = apostemp.z + TBoffz;

    vector3 relPos = targetPos - apostemp;
    int     nSteps = int(distance / 10.0);

    if (nSteps == 0) { return; }

    double  xStep     = relPos.x / nSteps;
    double  yStep     = relPos.y / nSteps;
    double  zStep     = relPos.z / nSteps;
    double  alpha     = 1;
    int     drawSteps = nSteps - 0;

    for (int i = 1; i < min(drawSteps,105); ++i)
    {
      double xoff = xStep * i;
      double yoff = yStep * i;
      double zoff = zStep * i;

      a.A_SpawnParticle( beamColor, SPF_FULLBRIGHT, 1, size, 0.0
                       , xoff, yoff, zoff+TBoffz
                       , 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
                       , alpha
                       );
    }
  }
}