569 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
			
		
		
	
	
			569 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
| /*
 | |
| **
 | |
| */
 | |
| #if defined _AESTHETIC_included
 | |
|  #endinput
 | |
| #endif
 | |
| #define _AESTHETIC_included
 | |
| #include <basic>
 | |
| #include <vscripts>
 | |
| #define CELL_SIZE 128.0
 | |
| 
 | |
| methodmap Chess < Basic
 | |
| {
 | |
| 	public Chess(int greenStart, int greenEnd, int purpleStart, int purpleEnd)
 | |
| 	{
 | |
| 		Basic myclass = new Basic();
 | |
| 		myclass.SetFloat("fGreenX", 2.0);
 | |
| 		myclass.SetFloat("fGreenY", 6.0);
 | |
| 		myclass.SetFloat("fPurpleX", 4.0);
 | |
| 		myclass.SetFloat("fPurpleY", 1.0);
 | |
| 		myclass.SetInt("iGreenStart", greenStart);
 | |
| 		myclass.SetInt("iGreenEnd", greenEnd);
 | |
| 		myclass.SetInt("iPurpleStart", purpleStart);
 | |
| 		myclass.SetInt("iPurpleEnd", purpleEnd);
 | |
| 		myclass.SetBool("bDead", false);
 | |
| 		return view_as<Chess>(myclass);
 | |
| 	}
 | |
| 
 | |
| 	property bool bDead
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetBool("bDead");
 | |
| 		}
 | |
| 		public set(bool val)
 | |
| 		{
 | |
| 			this.SetBool("bDead", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float greenX
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fGreenX");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fGreenX", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float greenY
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fGreenY");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fGreenY", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float purpleX
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fPurpleX");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fPurpleX", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float purpleY
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fPurpleY");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fPurpleY", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int greenStart
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iGreenStart");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int greenEnd
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iGreenEnd");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int purpleStart
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iPurpleStart");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int purpleEnd
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iPurpleEnd");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public bool Check(float x, float y)
 | |
| 	{
 | |
| 		return (-1.0 < x && x < 8.0 && -1.0 < y && y < 8.0 && !((x == this.greenX && y == this.greenY) || (x == this.purpleX && y == this.purpleY)));
 | |
| 	}
 | |
| 
 | |
| 	public void UpdateGreen(float dx, float dy)
 | |
| 	{
 | |
| 		this.greenX += dx;
 | |
| 		this.greenY += dy;
 | |
| 	}
 | |
| 
 | |
| 	public void UpdateGreenEnd(float dx, float dy)
 | |
| 	{
 | |
| 		float currentPos[3];
 | |
| 		GetOrigin(this.greenEnd, currentPos);
 | |
| 		float moveVector[3] =  { 0.0, ... };
 | |
| 		moveVector[0] = dx * CELL_SIZE;
 | |
| 		moveVector[1] = dy * CELL_SIZE;
 | |
| 		float tmp[3];
 | |
| 		AddVectors(currentPos, moveVector, tmp);
 | |
| 		SetOrigin(this.greenEnd, tmp);
 | |
| 	}
 | |
| 
 | |
| 	public void MoveGreen(float dx, float dy)
 | |
| 	{
 | |
| 		if (this.Check(this.greenX + dx, this.greenY + dy))
 | |
| 		{
 | |
| 			this.UpdateGreen(dx, dy);
 | |
| 			this.UpdateGreenEnd(dx, dy);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void MoveStartToEnd(int start, int end)
 | |
| 	{
 | |
| 		float e_Orig[3];
 | |
| 		GetOrigin(end, e_Orig);
 | |
| 		SetOrigin(start, e_Orig);
 | |
| 	}
 | |
| 
 | |
| 	public void UpdatePurple(float dx, float dy)
 | |
| 	{
 | |
| 		this.purpleX += dx;
 | |
| 		this.purpleY += dy;
 | |
| 	}
 | |
| 
 | |
| 	public void UpdatePurpleEnd(float dx, float dy)
 | |
| 	{
 | |
| 		float currentPos[3];
 | |
| 		GetOrigin(this.purpleEnd, currentPos);
 | |
| 		float moveVector[3] =  { 0.0, ... };
 | |
| 		moveVector[0] = dx * CELL_SIZE;
 | |
| 		moveVector[1] = dy * CELL_SIZE;
 | |
| 		float tmp[3];
 | |
| 		AddVectors(currentPos, moveVector, tmp);
 | |
| 		SetOrigin(this.purpleEnd, tmp);
 | |
| 	}
 | |
| 
 | |
| 	public void MovePurple(float dx, float dy)
 | |
| 	{
 | |
| 		if (this.Check(this.purpleX + dx, this.purpleY + dy))
 | |
| 		{
 | |
| 			this.UpdatePurple(dx, dy);
 | |
| 			this.UpdatePurpleEnd(dx, dy);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void CheckInLove()
 | |
| 	{
 | |
| 		if(this.bDead)
 | |
| 			return;
 | |
| 		float dx = this.greenX - this.purpleX;
 | |
| 		float dy = this.greenY - this.purpleY;
 | |
| 		if (dx < 0.0) dx *= -1.0;
 | |
| 		if (dy < 0.0) dy *= -1.0;
 | |
| 		if (dx + dy == 1.0)
 | |
| 		{
 | |
| 			this.bDead = true;
 | |
| 			EntFire("TotalTrigger", "FireUser2", "", "0.0", -1);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const int DEFAULT_RETARGET_TICKS = 200;
 | |
| const float DEFAULT_MAX_VEL = 16.0;
 | |
| const float DEFAULT_MAX_ACC = 0.1;
 | |
| const float DEFAULT_RANGE = 100000.0;
 | |
| const float DEFAULT_PER_FRAME_MAX_VEL_DELTA = 0.0003333; // 2.0 / (120.0 * 50.0);
 | |
| const float DEFAULT_PER_FRAME_MAX_ACC_DELTA = 0.0000125; // 0.075 / (120.0 * 50.0);
 | |
| #define PI 3.14159
 | |
| 
 | |
| public bool SphereCollideWithWorld(const float orig[3], const float radius, float degree, float buffer[3], int entity)
 | |
| {
 | |
| 
 | |
| 	float inc = PI / degree;
 | |
| 	float  phiStop = PI - inc;
 | |
| 
 | |
| 	float minDist = 1.0;
 | |
| 	for (float phi = 0.0; phi < PI; phi += inc)
 | |
| 	{
 | |
|         // Avoid computing the full circle on the edge cases
 | |
|         float theta = 0.0;
 | |
|         if (phi == 0.0 || phi == phiStop)
 | |
|             theta = PI - inc;
 | |
| 
 | |
|         float cp = Cosine(phi);
 | |
|         float sp = Sine(phi);
 | |
|         for (; theta < 2 * PI; theta += inc)
 | |
|         {
 | |
|             float ct = Cosine(theta);
 | |
|             float st = Sine(theta);
 | |
|             float vec[3];
 | |
|             vec[0] = ct * sp;
 | |
|             vec[1] = st * sp;
 | |
|             vec[2] = cp;
 | |
|             ScaleVector(vec, radius);
 | |
| 
 | |
|             float end[3];
 | |
|             AddVectors(orig, vec, end);
 | |
|             float dist = TraceLine(orig, end, entity);
 | |
|             if ( 0 < dist && dist < minDist)
 | |
|             {
 | |
|             	ScaleVector(vec, dist);
 | |
|             	AddVectors(orig, vec, buffer);
 | |
|             	return true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| methodmap Eye < Basic
 | |
| {
 | |
| 	public Eye(int entity)
 | |
| 	{
 | |
| 		Basic myclass = new Basic();
 | |
| 		myclass.SetInt("iEntity", entity);
 | |
| 		myclass.SetInt("iTarget", -1);
 | |
| 		myclass.SetFloat("fBossRadius", 72.0);
 | |
| 		myclass.SetInt("iBossTeamTarget", 3);
 | |
| 		myclass.SetInt("iRetargetTicks", 0);
 | |
| 		myclass.SetFloat("fMaxVel", DEFAULT_MAX_VEL);
 | |
| 		myclass.SetFloat("fMaxAcc", DEFAULT_MAX_ACC);
 | |
| 		myclass.SetVector("vVec", { 0.0, 0.0, 0.0 } );
 | |
| 		myclass.SetVector("vAcc", { 0.0, 0.0, 0.0 } );
 | |
| 		myclass.SetFloat("fSavedVel", 0.0);
 | |
| 		myclass.SetFloat("fSavedAcc", 0.0);
 | |
| 		return view_as<Eye>(myclass);
 | |
| 	}
 | |
| 
 | |
| 	property int entity
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iEntity");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int target
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iTarget");
 | |
| 		}
 | |
| 		public set(int val)
 | |
| 		{
 | |
| 			this.SetInt("iTarget", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float bossRadius
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fBossRadius");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fBossRadius", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int bossTeamTarget
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iBossTeamTarget");
 | |
| 		}
 | |
| 		public set(int val)
 | |
| 		{
 | |
| 			this.SetInt("iBossTeamTarget", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property int retargetTicks
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetInt("iRetargetTicks");
 | |
| 		}
 | |
| 		public set(int val)
 | |
| 		{
 | |
| 			this.SetInt("iRetargetTicks", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float maxVel
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fMaxVel");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fMaxVel", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float maxAcc
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fMaxAcc");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fMaxAcc", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float savedVel
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fSavedVel");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fSavedVel", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	property float savedAcc
 | |
| 	{
 | |
| 		public get()
 | |
| 		{
 | |
| 			return this.GetFloat("fSavedAcc");
 | |
| 		}
 | |
| 		public set(float val)
 | |
| 		{
 | |
| 			this.SetFloat("fSavedAcc", val);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void GetVel(float vel[3])
 | |
| 	{
 | |
| 		this.GetVector("vVel", vel);
 | |
| 	}
 | |
| 
 | |
| 	public void SetVel(const float vel[3])
 | |
| 	{
 | |
| 		this.SetVector("vVel", vel);
 | |
| 	}
 | |
| 
 | |
| 	public void GetAcc(float acc[3])
 | |
| 	{
 | |
| 		this.GetVector("vAcc", acc);
 | |
| 	}
 | |
| 
 | |
| 	public void SetAcc(const float acc[3])
 | |
| 	{
 | |
| 		this.SetVector("vAcc", acc);
 | |
| 	}
 | |
| 
 | |
| 	public void Retarget()
 | |
| 	{
 | |
| 
 | |
|     	// Start the gig
 | |
|     	this.retargetTicks = 0;
 | |
|     	int tempPlayer = -1;
 | |
|     	int currentPlayer = -1;
 | |
|     	int checkedPlayers = 0;
 | |
|     	float distance = DEFAULT_RANGE;
 | |
| 
 | |
|     	// While there is no target or
 | |
|     	// players found as potential targets aren't humans
 | |
|     	while (checkedPlayers < MaxClients) {
 | |
|     	    currentPlayer = FindEntityByClassname(currentPlayer, "player");
 | |
| 
 | |
|     	    // Only humans are valid
 | |
|     	    if(currentPlayer != -1 &&
 | |
|     	       IsPlayerAlive(currentPlayer) &&
 | |
|     	       GetClientTeam(currentPlayer) == this.bossTeamTarget)
 | |
|     	    {
 | |
|     	        float tmp[3], s_orig[3], p_orig[3];
 | |
|     	        GetOrigin(this.entity, s_orig);
 | |
|     	        GetOrigin(currentPlayer, p_orig);
 | |
|     	        SubtractVectors(s_orig, p_orig, tmp);
 | |
|     	        float length = GetVectorLength(tmp);
 | |
| 
 | |
|     	        // Make sure we're close enough
 | |
|     	        if (length < distance)
 | |
|     	        {
 | |
|     	            tempPlayer = currentPlayer;
 | |
|     	            distance = length;
 | |
|     	        }
 | |
|     	    }
 | |
|     	    // Make sure we don't stay here forever
 | |
|     	    checkedPlayers++;
 | |
|     	}
 | |
| 
 | |
|     	// Try the last bet if no player was found
 | |
|     	this.target = tempPlayer;
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	public void Stop()
 | |
| 	{
 | |
|     	// Save the values
 | |
|     	this.savedVel = this.maxVel;
 | |
|     	this.savedAcc = this.maxAcc;
 | |
| 
 | |
|     	// Block movement (with a bit of velocity to avoid spinning)
 | |
|     	this.maxVel = 0.01;
 | |
|     	this.maxAcc = 0.0001;
 | |
| 	}
 | |
| 
 | |
| 	public void AllowMovement()
 | |
| 	{
 | |
|     	// Set either default or saved vals
 | |
|     	if (this.savedVel == 0.0) this.maxVel = DEFAULT_MAX_VEL;
 | |
|     	else this.maxVel = this.savedVel;
 | |
| 
 | |
|     	if (this.savedAcc == 0.0) this.maxAcc = DEFAULT_MAX_ACC;
 | |
|     	else this.maxAcc = this.savedAcc;
 | |
| 
 | |
|     	// Reset saved values
 | |
|     	this.savedVel = 0.0;
 | |
|     	this.savedAcc = 0.0;
 | |
| 	}
 | |
| 
 | |
| 	public void FakePhysicsMovement()
 | |
| 	{
 | |
| 
 | |
|     	// Get the center and end point
 | |
|     	float cnt[3];
 | |
|     	GetOrigin(this.entity, cnt);
 | |
| 
 | |
|     	// Make the boss face the target
 | |
|     	float tmp[3];
 | |
|     	this.GetVel(tmp);
 | |
|     	SetForwardVector(this.entity, tmp);
 | |
| 
 | |
|     	// If there is an impact, bounce
 | |
|     	float collPoint[3];
 | |
|     	if (SphereCollideWithWorld(cnt, this.bossRadius, 5.0, collPoint, this.entity))
 | |
|     	{
 | |
| 
 | |
|     		float vec[3];
 | |
|     		SubtractVectors(cnt, collPoint, vec);
 | |
|     		float norm[3], norm_copy[3];
 | |
|     		NormalizeVector(vec, norm);
 | |
|     		norm_copy[0] = norm[0];
 | |
|     		norm_copy[1] = norm[1];
 | |
|     		norm_copy[2] = norm[2];
 | |
|     		// Reflect velocity in a hacky way (not plane-accurate)
 | |
|     		float nn[3];
 | |
|     		NormalizeVector(norm, nn);
 | |
|     		this.GetVel(tmp);
 | |
|     		float dot = GetVectorDotProduct(tmp, nn);
 | |
|     		ScaleVector(norm_copy, 2 * dot);
 | |
|     		SubtractVectors(tmp, norm_copy, tmp);
 | |
|     		this.SetVel(tmp);
 | |
|     		EntFireByIndex(this.entity, "FireUser1", "", "0.0", this.target);
 | |
|     		// Move as far as we can
 | |
|     		ScaleVector(norm, this.bossRadius);
 | |
|     		AddVectors(cnt, norm, tmp);
 | |
|     		SetOrigin(this.entity, tmp);
 | |
|     	}
 | |
|     	else
 | |
|     	{
 | |
| 
 | |
|     		this.GetVel(tmp);
 | |
|     		AddVectors(cnt, tmp, tmp);
 | |
|     		SetOrigin(this.entity, tmp);
 | |
|     	}
 | |
|     }
 | |
| 
 | |
| 	public void Move()
 | |
| 	{
 | |
| 
 | |
|     	// If there is no target or the target is gone/dead, retarget
 | |
|     	if (this.target == -1 ||
 | |
|     	    !IsValidEntity(this.target) ||
 | |
|     	    !IsPlayerAlive(this.target) ||
 | |
|     	    this.retargetTicks++ >= DEFAULT_RETARGET_TICKS) this.Retarget();
 | |
| 
 | |
|     	// Perform the update with the data from the previous moment
 | |
|     	float vel[3], tmp[3], acc[3];
 | |
|     	this.GetVel(vel);
 | |
|     	this.GetAcc(acc);
 | |
|     	AddVectors(vel, acc, tmp);
 | |
|     	this.SetVel(tmp);
 | |
|     	float length = GetVectorLength(tmp);
 | |
|     	if (length > this.maxVel)
 | |
|     	{
 | |
|     		ScaleVector(tmp, this.maxVel / length);
 | |
|     		this.SetVel(tmp);
 | |
|     	}
 | |
|     	this.SetAcc( { 0.0, 0.0, 0.0 } );
 | |
| 
 | |
|     	// Apply the higher accuracy movement deltas
 | |
|     	this.maxVel += DEFAULT_PER_FRAME_MAX_VEL_DELTA;
 | |
|     	this.maxAcc += DEFAULT_PER_FRAME_MAX_ACC_DELTA;
 | |
| 
 | |
|     	// Try to move, finally
 | |
|     	this.FakePhysicsMovement();
 | |
| 
 | |
|     	// Keep going if no target's there still
 | |
|     	if (this.target == -1) return;
 | |
| 
 | |
|     	// Try moving towards our target if it exists, otherwise don't
 | |
| 
 | |
|     	float tgtPos[3];
 | |
|     	GetOrigin(this.target, tgtPos);
 | |
|     	float bssPos[3];
 | |
|     	GetOrigin(this.entity, bssPos);
 | |
|     	float dir[3];
 | |
|     	SubtractVectors(tgtPos, bssPos, dir);
 | |
| 
 | |
|     	// Compute the length for later and normalize it if needed
 | |
| 
 | |
|     	length = GetVectorLength(dir);
 | |
|     	if (length > this.maxVel)
 | |
|     		ScaleVector(dir, this.maxVel / length);
 | |
| 
 | |
|     	// Compute the acceleration
 | |
| 
 | |
|     	this.GetVel(vel);
 | |
|     	SubtractVectors(dir, vel, acc);
 | |
|     	length = GetVectorLength(acc);
 | |
|     	if (length > this.maxAcc)
 | |
|     	{
 | |
|     		ScaleVector(acc, this.maxAcc / length);
 | |
|     		this.SetAcc(acc);
 | |
|     	}
 | |
| 
 | |
| 	}
 | |
| }
 |