[ZScript] Problems Extending A_SpawnItemEx

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
User avatar
22alpha22
Posts: 291
Joined: Fri Feb 21, 2014 5:04 pm
Graphics Processor: nVidia with Vulkan support
Location: Montana, USA

[ZScript] Problems Extending A_SpawnItemEx

Post by 22alpha22 »

This is a somewhat difficult problem to explain because I don't know exactly what is wrong so I'll start by explaining what I'm trying to accomplish. I'm trying to extend a A_SpawnItemEx by copying it to a new function and adding the additional bits I want to it. I'm trying to add the ability to set a spawned actor's pitch and roll through parameters rather than being limited to inheriting the calling actor's pitch and roll. I'm also trying to add a flag that will automatically take the spawned actor's pitch into account when setting the actor's velocity, this will be useful projectiles for instance.

I've been working with and learning ZScript for about two or three years now and have built a fairly reasonable foundation with it, so I know how to go about adding these additional features to A_SpawnItemEx in theory. The problem is my math skills. I've managed to add the ability to set the spawned actor's pitch and roll but I lack the necessary math skills to figure out how to set an actor's velocity if pitch should be taken into account for said velocity. I've attempted to figure it out myself and sometimes it seems to work right and other times the spawned actor's velocity is completely out of whack. The last feature I want is for the spawned actor's positional offsets to take the actor's pitch into account if a flag is passed. For example, if the actor is spawned pitching up at 45 degrees, the X offset should raise the actor as well as move it forward by the appropriate amount. I have absolutely no idea how to implement this so I haven't even tried.

Here is what I've got so far, if anyone can help me figure out what I've done wrong with the velocity calculations, I would greatly appreciate it. Extra kudos if someone can show me how to have the spawned offsets take into account the actor's pitch.
Spoiler:
User avatar
22alpha22
Posts: 291
Joined: Fri Feb 21, 2014 5:04 pm
Graphics Processor: nVidia with Vulkan support
Location: Montana, USA

Re: [ZScript] Problems Extending A_SpawnItemEx

Post by 22alpha22 »

I've been banging my head against this and I think I've mostly figured it out myself. I've had a look at the GZDoom source to see how certain functions are done and have looked at some other smarter people's projects, ZWeapon library was quite useful. After quite a bit of testing, it looks like my function works properly, I can now spawn objects with offsets that are affected by pitch as well as velocity that is affected by pitch. That being said, if someone better at maths would have a look at my function to see if there are any mistakes, I would very much appreciate it. I don't actually understand much of the math in my function, I just frakensteined it together from other functions that did similar things to what I wanted and kept messing with it until it worked so there a bound to be some mistakes or oversights.

Code: Select all

Bool, Actor A_SpawnItemExtended(Class<Actor> Actr, Double OffsetX = 0.0, Double OffsetY = 0.0, Double OffsetZ = 0.0, Double VelX = 0.0, Double VelY = 0.0, Double VelZ = 0.0, Double Angle = 0.0, Double Pitch = 0.0, Double Roll = 0.0, Int Flags = 0, Int Flags2 = 0, Int FailChance = 0, Int TID = 0)
	{
		Bool Result;
		Actor MapObject;
		Double CallerAngle;
		Double CallerPitch;
		Double CallerRoll;
		Double CallerPosZ;
		Double CallerVelZ;
		Vector3 Position;
		Double TSin;
		Double TCos;
		Double NewVelX;

		If (!Actr)
		{
			Return False, Null;
		}

		If (FailChance > 0 && Random[SpawnItemEx]() < FailChance)
		{
			Return True, Null;
		}

		If (DamageType == 'Massacre' && GetDefaultByType(Actr).bISMONSTER)
		{
			Return True, Null;
		}

		CallerAngle = Self.Angle;
		CallerPitch = Self.Pitch;
		CallerRoll = Self.Roll;
		CallerPosZ = Self.Pos.Z;
		CallerVelZ = Self.Vel.Z;

		If (Flags2 & SXF_OFFSETSFROMPITCH)
		{
			Vector3 BaseDirection;
			Double BaseAngle;
			Double BasePitch;
			Vector3 Right;
			Vector3 Up;
			Vector2 OffsetXZ;
			Vector3 Offset;

			BaseDirection = (Cos(CallerPitch) * Cos(CallerAngle),Cos(CallerPitch) * Sin(CallerAngle),-Sin(CallerPitch));
			Right = (Cos(CallerAngle - 90.0),Sin(CallerAngle - 90.0),0.0);
			Up = (Cos(CallerPitch - 90.0) * Cos(CallerAngle),Cos(CallerPitch - 90.0) * Sin(CallerAngle),-Sin(CallerPitch - 90.0));

			BaseDirection += Sin(Angle) * Right;
			BaseDirection += Sin(-Pitch) * Up;

			BaseAngle = VectorAngle(BaseDirection.X,BaseDirection.Y);
			BaseDirection.XY = RotateVector(BaseDirection.XY,-BaseAngle);
			BasePitch = -VectorAngle(BaseDirection.X,BaseDirection.Z);

			OffsetY *= -1;
			OffsetXZ = RotateVector((OffsetX,OffsetZ),-BasePitch);
			OffsetX = OffsetXZ.X;
			OffsetZ = OffsetXZ.Y;
			Offset = (OffsetX,OffsetY,OffsetZ);
			Offset.XY = RotateVector(Offset.XY,BaseAngle);

			If (Flags & SXF_ABSOLUTEPOSITION)
			{
				Position.XY = Vec2Offset(Offset.X,Offset.Y,False);
			}
			Else
			{
				Position.XY = (Self.Pos.X + Offset.X,Self.Pos.Y + Offset.Y);
			}

			Position.Z = CallerPosZ - FloorClip + GetBobOffset(0.0) + Offset.Z;
		}
		Else
		{
			If (!(Flags & SXF_ABSOLUTEANGLE))
			{
				Angle += CallerAngle;
			}

			TSin = Sin(Angle);
			TCos = Cos(Angle);

			If (Flags & SXF_ABSOLUTEPOSITION)
			{
				Position.XY = Vec2Offset(OffsetX,OffsetY,False);
			}
			Else
			{
				Position.XY = Vec2Offset(OffsetX * TCos + OffsetY * TSin,OffsetX * TSin - OffsetY * TCos,False);
			}

			Position.Z = CallerPosZ - FloorClip + GetBobOffset(0.0) + OffsetZ;
		}

		MapObject = Spawn(Actr,Position,ALLOW_REPLACE);
		Result = InitSpawnedItem(MapObject,Flags);

		If (Result)
		{
			If (Flags2 & SXF_OFFSETSFROMPITCH)
			{
				If (!(Flags & SXF_ABSOLUTEANGLE))
				{
					Angle += CallerAngle;
				}

				TSin = Sin(Angle);
				TCos = Cos(Angle);
			}

			If (!(Flags2 & SXF_ABSOLUTEPITCH))
			{
				Pitch = Clamp(Pitch + CallerPitch,-90.0,90.0);
			}

			If (!(Flags2 & SXF_ABSOLUTEROLL))
			{
				Roll += CallerRoll;
			}

			If (!(Flags & SXF_ABSOLUTEVELOCITY))
			{
				NewVelX = VelX * TCos + VelY * TSin;
				VelY = VelX * TSin - VelY * TCos;
				VelX = NewVelX;

				If (Flags2 & SXF_ADDZVELOCITY)
				{
						VelZ += CallerVelZ;
				}
			}

			MapObject.Angle = Angle;
			MapObject.Pitch = Pitch;
			MapObject.Roll = Roll;
			MapObject.Vel = (VelX,VelY,VelZ);

			If (Flags2 & SXF_VELOCITYFROMPITCH)
			{
				If (Flags2 & SXF_ADDZVELOCITY)
				{
					MapObject.Vel3DFromAngle(MapObject.Vel.XY.Length(),VectorAngle(VelX,VelY),MapObject.Pitch);
					MapObject.Vel.Z += VelZ;
				}
				Else
				{
						MapObject.Vel3DFromAngle(MapObject.Vel.Length(),VectorAngle(VelX,VelY),VectorAngle(MapObject.Vel.XY.Length(),VelZ) + Pitch);
				}
			}

			If (Flags & SXF_MULTIPLYSPEED)
			{
				MapObject.Vel *= MapObject.Speed;
			}

			If (Flags2 & SXF_PITCHFROMMOMENTUM)
			{
				MapObject.A_FaceMovementDirection(0.0,0.0,270.0,FMDF_NOANGLE,AAPTR_DEFAULT);
			}

			If (TID != 0)
			{
				MapObject.ChangeTID(TID);
			}
		}

		Return Result, MapObject;
	}

Return to “Scripting”