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 + -
显示快捷键?