cobinstance.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,724 行 · 第 1/4 页

CPP
1,724
字号
				break;
			case ATurn:
				done = TurnToward(pieces[(*cur)->piece].rot[(*cur)->axis], (*cur)->dest, (*cur)->speed / (1000 / deltaTime));
				break;
			case ASpin:
				done = DoSpin(pieces[(*cur)->piece].rot[(*cur)->axis], (*cur)->dest, (*cur)->speed, (*cur)->accel, 1000 / deltaTime);
				break;
		}

		//Tell listeners to unblock?
		if (done) {
			UnblockAll(*cur);
			delete *cur;
			anims.erase(cur);
		}

	}

	if (anims.size() == 0)
		return -1;
	else
		return 0;
}

//Optimize this?
//Returns anims list
struct CCobInstance::AnimInfo *CCobInstance::FindAnim(AnimType type, int piece, int axis)
{
	for (list<struct AnimInfo *>::iterator i = anims.begin(); i != anims.end(); ++i) {
		if (((*i)->type == type) && ((*i)->piece == piece) && ((*i)->axis == axis))
			return *i;
	}
	return NULL;
}

// Returns true if an animation was found and deleted
void CCobInstance::RemoveAnim(AnimType type, int piece, int axis)
{
	for (list<struct AnimInfo *>::iterator i = anims.begin(); i != anims.end(); ++i) {
		if (((*i)->type == type) && ((*i)->piece == piece) && ((*i)->axis == axis)) {

			// We need to unblock threads waiting on this animation, otherwise they will be lost in the void
			UnblockAll(*i);

			delete *i;
			anims.erase(i);

			// If this was the last animation, remove from currently animating list
			if (anims.size() == 0) {
				GCobEngine.RemoveInstance(this);
			}
			return;
		}
	}
}

//Overwrites old information. This means that threads blocking on turn completion
//will now wait for this new turn instead. Not sure if this is the expected behaviour
//Other option would be to kill them. Or perhaps unblock them.
void CCobInstance::AddAnim(AnimType type, int piece, int axis, int speed, int dest, int accel, bool interpolated)
{
	struct AnimInfo *ai;

	//Turns override spins.. Not sure about the other way around? If so the system should probably be redesigned
	//to only have two types of anims.. turns and moves, with spin as a bool
	if (type == ATurn)
		RemoveAnim(ASpin, piece, axis);
	if (type == ASpin)
		RemoveAnim(ATurn, piece, axis);

	ai = FindAnim(type, piece, axis);
	if (!ai) {
		ai = SAFE_NEW struct AnimInfo;
		ai->type = type;
		ai->piece = piece;
		ai->axis = axis;
		anims.push_back(ai);

		//If we were not animating before, inform the engine of this so it can schedule us
		if (anims.size() == 1) {
			GCobEngine.AddInstance(this);
		}

		// Check to make sure the piece exists
		if (piece >= pieces.size()) {
			logOutput.Print("Invalid piece in anim %d (%d)", piece, pieces.size());
		}
	}
	ai->speed = speed;
	ai->dest = dest;
	ai->accel = accel;
	ai->interpolated = interpolated;
}

void CCobInstance::Spin(int piece, int axis, int speed, int accel)
{
	struct AnimInfo *ai;
	ai = FindAnim(ASpin, piece, axis);

	//logOutput.Print("Spin called %d %d %d %d", piece, axis, speed, accel);

	//If we are already spinning, we may have to decelerate to the new speed
	if (ai) {
		ai->dest = speed;
		if (accel > 0) {
			if (ai->speed > ai->dest)
				ai->accel = -accel;
			else
				ai->accel = accel;
		}
		else {
			//Go there instantly. Or have a defaul accel?
			ai->speed = speed;
			ai->accel = 0;
		}
	}
	else {
		//No accel means we start at desired speed instantly
		if (accel == 0)
			AddAnim(ASpin, piece, axis, speed, speed, accel);
		else
			AddAnim(ASpin, piece, axis, 0, speed, accel);
	}
}

void CCobInstance::StopSpin(int piece, int axis, int decel)
{
	struct AnimInfo *ai;
	ai = FindAnim(ASpin, piece, axis);
	if (!ai)
		return;

	if (decel == 0) {
		RemoveAnim(ASpin, piece, axis);
	}
	else
		AddAnim(ASpin, piece, axis, ai->speed, 0, -decel);
}

void CCobInstance::Turn(int piece, int axis, int speed, int destination, bool interpolated)
{
	AddAnim(ATurn, piece, axis, speed, destination % 65536, 0, interpolated);
}

void CCobInstance::Move(int piece, int axis, int speed, int destination, bool interpolated)
{
	AddAnim(AMove, piece, axis, speed, destination, 0, interpolated);
}

void CCobInstance::MoveNow(int piece, int axis, int destination)
{
	pieces[piece].coords[axis] = destination;
	pieces[piece].updated = true;
}

void CCobInstance::TurnNow(int piece, int axis, int destination)
{
	pieces[piece].rot[axis] = destination;
	pieces[piece].updated = true;
	//logOutput.Print("moving %s on axis %d to %d", script.pieceNames[piece].c_str(), axis, destination);
}

void CCobInstance::SetVisibility(int piece, bool visible)
{
	if (pieces[piece].visible != visible) {
		pieces[piece].visible = visible;
		pieces[piece].updated = true;
	}
}

void CCobInstance::EmitSfx(int type, int piece)
{
	if (!unit->localmodel->PieceExists(piece)) {
		GCobEngine.ShowScriptError("Invalid piecenumber for emit-sfx");
		return;
	}

#ifndef _CONSOLE
	ENTER_MIXED;
	if(ph->particleSaturation>1 && type<1024){		//skip adding particles when we have to many (make sure below can be unsynced)
		ENTER_SYNCED;
		return;
	}

	float3 relPos;
	float3 relDir(0,1,0);
	unit->localmodel->GetEmitDirPos(piece, relPos, relDir);
	//relPos = unit->localmodel->GetPiecePos(piece);
//float3 relPos = unit->localmodel->GetPiecePos(piece);

	float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x;

	float alpha = 0.3f+gu->usRandFloat()*0.2f;
	float alphaFalloff = 0.004f;
	float fadeupTime=4;

	//Hovers need special care
	if (unit->unitDef->canhover) {
		fadeupTime=8;
		alpha = 0.15f+gu->usRandFloat()*0.2f;
		alphaFalloff = 0.008f;
	}

	//Make sure wakes are only emitted on water
	if ((type >= 2) && (type <= 5)) {
		if (ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 0){
			ENTER_SYNCED;
			return;
		}
	}

	switch (type) {
		case 4:
		case 5:		{	//reverse wake
			//float3 relDir = -unit->localmodel->GetPieceDirection(piece) * 0.2f;
			relDir *= 0.2f;
			float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x;
			SAFE_NEW CWakeProjectile(pos+gu->usRandVector()*2,dir*0.4f,6+gu->usRandFloat()*4,0.15f+gu->usRandFloat()*0.3f,unit, alpha, alphaFalloff,fadeupTime);
			break;}
		case 3:			//wake 2, in TA it lives longer..
		case 2:		{	//regular ship wake
			//float3 relDir = unit->localmodel->GetPieceDirection(piece) * 0.2f;
			relDir *= 0.2f;
			float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x;
			SAFE_NEW CWakeProjectile(pos+gu->usRandVector()*2,dir*0.4f,6+gu->usRandFloat()*4,0.15f+gu->usRandFloat()*0.3f,unit, alpha, alphaFalloff,fadeupTime);
			break;}
		case 259:	{	//submarine bubble. does not provide direction through piece vertices..
			float3 pspeed=gu->usRandVector()*0.1f;
			pspeed.y+=0.2f;
			SAFE_NEW CBubbleProjectile(pos+gu->usRandVector()*2,pspeed,40+gu->usRandFloat()*30,1+gu->usRandFloat()*2,0.01f,unit,0.3f+gu->usRandFloat()*0.3f);
			break;}
		case 257:	//damaged unit smoke
			SAFE_NEW CSmokeProjectile(pos,gu->usRandVector()*0.5f+UpVector*1.1f,60,4,0.5f,unit,0.5f);
		case 258:		//damaged unit smoke
			SAFE_NEW CSmokeProjectile(pos,gu->usRandVector()*0.5f+UpVector*1.1f,60,4,0.5f,unit,0.6f);
			break;
		case 0:{		//vtol
			//relDir = unit->localmodel->GetPieceDirection(piece) * 0.2f;
			relDir *= 0.2f;
			float3 dir = unit->frontdir * relDir.z + unit->updir * -fabs(relDir.y) + unit->rightdir * relDir.x;
			CHeatCloudProjectile* hc=SAFE_NEW CHeatCloudProjectile(pos, unit->speed*0.7f+dir * 0.5f, 10 + gu->usRandFloat() * 5, 3 + gu->usRandFloat() * 2, unit);
			hc->size=3;
			break;}
		default:
			//logOutput.Print("Unknown sfx: %d", type);
			if (type & 1024)	//emit defined explosiongenerator
			{
				unsigned index = type - 1024;
				if (index >= unit->unitDef->sfxExplGens.size() || unit->unitDef->sfxExplGens[index] == NULL) {
					GCobEngine.ShowScriptError("Invalid explosion generator index for emit-sfx");
					break;
				}
				//float3 relDir = -unit->localmodel->GetPieceDirection(piece) * 0.2f;
				float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x;
				dir.Normalize();
				unit->unitDef->sfxExplGens[index]->Explosion(pos, unit->cegDamage, 1, unit, 0, 0, dir);
			}
			else if (type & 2048)  //make a weapon fire from the piece
			{
				unsigned index = type - 2048;
				if (index >= unit->weapons.size() || unit->weapons[index] == NULL) {
					GCobEngine.ShowScriptError("Invalid weapon index for emit-sfx");
					break;
				}
				//this is very hackish and probably has a lot of side effects, but might be usefull for something
				//float3 relDir =-unit->localmodel->GetPieceDirection(piece);
				float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x;
				dir.Normalize();

				float3 targetPos = unit->weapons[index]->targetPos;
				float3 weaponMuzzlePos = unit->weapons[index]->weaponMuzzlePos;

				unit->weapons[index]->targetPos = pos+dir;
				unit->weapons[index]->weaponMuzzlePos = pos;

				unit->weapons[index]->Fire();

				unit->weapons[index]->targetPos = targetPos;
				unit->weapons[index]->weaponMuzzlePos = weaponMuzzlePos;
			}
			else if (type & 4096) {
				unsigned index = type - 4096;
				if (index >= unit->weapons.size() || unit->weapons[index] == NULL) {
					GCobEngine.ShowScriptError("Invalid weapon index for emit-sfx");
					break;
				}
				// detonate weapon from piece
				const WeaponDef* weaponDef = unit->weapons[index]->weaponDef;
				if (weaponDef->soundhit.getID(0) > 0) {
					sound->PlaySample(weaponDef->soundhit.getID(0), unit, weaponDef->soundhit.getVolume(0));
				}

				helper->Explosion(
					pos, weaponDef->damages, weaponDef->areaOfEffect, weaponDef->edgeEffectiveness,
					weaponDef->explosionSpeed, unit, true, 1.0f, false, weaponDef->explosionGenerator,
					NULL, float3(0, 0, 0), weaponDef->id
				);
			}
			break;
	}


	ENTER_SYNCED;
#endif
}

void CCobInstance::AttachUnit(int piece, int u)
{
	// -1 is valid, indicates that the unit should be hidden
	if ((piece >= 0) && (!unit->localmodel->PieceExists(piece))) {
		GCobEngine.ShowScriptError("Invalid piecenumber for attach");
		return;
	}

#ifndef _CONSOLE
	CTransportUnit* tu=dynamic_cast<CTransportUnit*>(unit);

	if(tu && uh->units[u]){
		//logOutput.Print("attach");
		tu->AttachUnit(uh->units[u],piece);
	}
#endif
}

void CCobInstance::DropUnit(int u)
{
#ifndef _CONSOLE
	CTransportUnit* tu=dynamic_cast<CTransportUnit*>(unit);

	if(tu && uh->units[u]){
		tu->DetachUnit(uh->units[u]);
	}
#endif
}

//Returns 1 if there was a turn to listen to
int CCobInstance::AddTurnListener(int piece, int axis, CCobThread *listener)
{
	struct AnimInfo *ai;
	ai = FindAnim(ATurn, piece, axis);
	if (ai) {
		ai->listeners.push_back(listener);
		return 1;
	}
	else
		return 0;
}

int CCobInstance::AddMoveListener(int piece, int axis, CCobThread *listener)
{
	struct AnimInfo *ai;
	ai = FindAnim(AMove, piece, axis);
	if (ai) {
		ai->listeners.push_back(listener);
		return 1;
	}
	else
		return 0;
}

void CCobInstance::Signal(int signal)
{
	for (list<CCobThread *>::iterator i = threads.begin(); i != threads.end(); ++i) {
		if ((signal & (*i)->signalMask) != 0) {
			(*i)->state = CCobThread::Dead;
			//logOutput.Print("Killing a thread %d %d", signal, (*i)->signalMask);
		}
	}
}
//Flags as defined by the cob standard
void CCobInstance::Explode(int piece, int flags)
{
	if (!unit->localmodel->PieceExists(piece)) {
		GCobEngine.ShowScriptError("Invalid piecenumber for explode");
		return;
	}


#ifndef _CONSOLE
	float3 pos = unit->localmodel->GetPiecePos(piece) + unit->pos;

#ifdef TRACE_SYNC
	tracefile << "Cob explosion: ";
	tracefile << pos.x << " " << pos.y << " " << pos.z << " " << piece << " " << flags << "\n";
#endif

	// Do an explosion at the location first
	SAFE_NEW CHeatCloudProjectile(pos, float3(0, 0, 0), 30, 30, NULL);

	// If this is true, no stuff should fly off
	if (flags & 32) return;

	// This means that we are going to do a full fledged piece explosion!
	// TODO: equalize the bitflags with those in PieceProjectile.h
	int newflags = 0;
	if (flags & 2) { newflags |= PP_Explode; } newflags |= PP_Fall;
//	if (flags & 4) { newflags |= PP_Fall; }
	if ((flags & 8) && ph->particleSaturation < 1) { newflags |= PP_Smoke; }
	if ((flags & 16) && ph->particleSaturation < 0.95f) { newflags |= PP_Fire; }
	if (flags & PP_NoCEGTrail) { newflags |= PP_NoCEGTrail; }

/*
	int newflags = 0;
	if (flags & PP_Explode) newflags |= PP_Explode;
	if (flags & PP_Fall) newflags |= PP_Fall;
	if ((flags & PP_Smoke) && ph->particleSaturation < 1) newflags |= PP_Smoke;
	if ((flags & PP_Fire) && ph->particleSaturation < 0.95f) newflags |= PP_Fire;
	if (flags & PP_NoCEGTrail) newflags |= PP_NoCEGTrail;
*/

	float3 baseSpeed = unit->speed + unit->residualImpulse * 0.5f;
	float l = baseSpeed.Length();

	if (l > 3) {
		float l2 = 3 + sqrt(l - 3);
		baseSpeed *= (l2 / l);
	}
	float3 speed((0.5f-gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f);
	if (unit->pos.y - ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) {
		speed.y = (0.5f - gs->randFloat()) * 6.0f;
	}
	speed += baseSpeed;
	if (speed.Length() > 12)
		speed = speed.Normalize() * 12;

	/* TODO Push this back. Don't forget to pass the team (color).  */

	LocalS3DO * pieceData = &( unit->localmodel->pieces[unit->localmodel->scritoa[piece]] );
	if (flags & 1) {		//Shatter
		ENTER_MIXED;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?