//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->GetObjectSpacePos(relPos); #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(nullptr, absPos, ZeroVector, 30, 30); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; if (pieces[piece]->original == nullptr) return; if (flags & PF_Shatter) { Shatter(piece, absPos, unit->speed); return; } // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed; float3 explSpeed((0.5f - gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f); if (unit->pos.y - CGround::GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) explSpeed.y = (0.5f - gs->randFloat()) * 6.0f; if (baseSpeed.SqLength() > 9.0f) { const float l = baseSpeed.Length(); const float l2 = 3.0f + math::sqrt(l - 3.0f); baseSpeed *= (l2 / l); } explSpeed += baseSpeed; const float partSat = projectileHandler->GetParticleSaturation(); int newFlags = 0; newFlags |= (PF_Explode * ((flags & PF_Explode ) != 0)); newFlags |= (PF_Smoke * ((flags & PF_Smoke ) != 0) && partSat < 0.95f); newFlags |= (PF_Fire * ((flags & PF_Fire ) != 0) && partSat < 0.95f); newFlags |= (PF_NoCEGTrail * ((flags & PF_NoCEGTrail) != 0)); newFlags |= (PF_Recursive * ((flags & PF_Recursive ) != 0)); new CPieceProjectile(unit, pieces[piece], absPos, explSpeed, newFlags, 0.5f); #endif }
int CUnitScript::ScriptToModel(int scriptPieceNum) const { if (!PieceExists(scriptPieceNum)) return -1; const LocalModelPiece* smp = GetScriptLocalModelPiece(scriptPieceNum); return (smp->GetLModelPieceIndex()); }
void CUnitScript::SetVisibility(int piece, bool visible) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber"); return; } pieces[piece]->scriptSetVisible = visible; }
void CUnitScript::MoveNow(int piece, int axis, float destination) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } LocalModelPiece* p = pieces[piece]; p->pos[axis] = pieces[piece]->original->offset[axis] + destination; }
void CUnitScript::TurnNow(int piece, int axis, float destination) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } LocalModelPiece* p = pieces[piece]; p->rot[axis] = destination; }
void CUnitScript::SetVisibility(int piece, bool visible) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } LocalModelPiece* p = pieces[piece]; if (p->visible != visible) { p->visible = visible; } }
void CUnitScript::AttachUnit(int piece, int u) { // -1 is valid, indicates that the unit should be hidden if ((piece >= 0) && (!PieceExists(piece))) { ShowUnitScriptError("Invalid piecenumber for attach"); return; } #ifndef _CONSOLE if (unit->unitDef->IsTransportUnit() && unitHandler->units[u]) { unit->AttachUnit(unitHandler->units[u], piece); } #endif }
void CUnitScript::ShowFlare(int piece) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for show(flare)"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->GetObjectSpacePos(relPos); const float3 dir = unit->lastMuzzleFlameDir; const float size = unit->lastMuzzleFlameSize; new CMuzzleFlame(absPos, unit->speed, dir, size); #endif }
void CUnitScript::AttachUnit(int piece, int u) { // -1 is valid, indicates that the unit should be hidden if ((piece >= 0) && (!PieceExists(piece))) { ShowScriptError("Invalid piecenumber for attach"); return; } #ifndef _CONSOLE CTransportUnit* tu = dynamic_cast<CTransportUnit*>(unit); if (tu && uh->units[u]) { tu->AttachUnit(uh->units[u], piece); } #endif }
void CUnitScript::MoveNow(int piece, int axis, float destination) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber"); return; } LocalModel* m = unit->localModel; LocalModelPiece* p = pieces[piece]; float3 pos = p->GetPosition(); pos[axis] = pieces[piece]->original->offset[axis] + destination; p->SetPosition(pos); m->PieceUpdated(piece); }
void CUnitScript::TurnNow(int piece, int axis, float destination) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber"); return; } LocalModel* m = unit->localModel; LocalModelPiece* p = pieces[piece]; float3 rot = p->GetRotation(); rot[axis] = destination; p->SetRotation(rot); m->PieceUpdated(piece); }
//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 CUnitScript::AddAnim(AnimType type, int piece, int axis, float speed, float dest, float accel, bool interpolated) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } float destf; if (type == AMove) { destf = pieces[piece]->original->offset[axis] + dest; } else { destf = dest; if (type == ATurn) { ClampRad(&destf); } } 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 //todo: optimize, atm RemoveAnim and FindAnim search twice through all anims if (type == ATurn) RemoveAnim(ASpin, piece, axis); if (type == ASpin) RemoveAnim(ATurn, piece, axis); ai = FindAnim(type, piece, axis); if (!ai) { // If we were not animating before, inform the engine of this so it can schedule us if (anims.empty()) { GUnitScriptEngine.AddInstance(this); } ai = new struct AnimInfo; ai->type = type; ai->piece = piece; ai->axis = axis; anims.push_back(ai); } ai->dest = destf; ai->speed = speed; ai->accel = accel; ai->interpolated = interpolated; }
void CUnitScript::ShowFlare(int piece) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for show(flare)"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; const float3 dir = unit->lastMuzzleFlameDir; const float size = unit->lastMuzzleFlameSize; new CMuzzleFlame(absPos, unit->speed, dir, size); #endif }
void CUnitScript::TurnSmooth(int piece, int axis, float destination, int delta, int deltaTime) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } AnimInfo *ai = FindAnim(ATurn, piece, axis); if (ai) { if (!ai->interpolated) { TurnNow(piece, axis, destination); return; } } // not sure the ClampRad() call is necessary here float cur = ClampRad(pieces[piece]->rot[axis]); float dist = streflop::fabsf(destination - cur); int timeFactor = (1000 * 1000) / (deltaTime * deltaTime); float speed = (dist * timeFactor) / delta; Turn(piece, axis, speed, destination, true); }
void CUnitScript::MoveSmooth(int piece, int axis, float destination, int delta, int deltaTime) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } //Make sure we do not overwrite animations of non-interpolated origin AnimInfo *ai = FindAnim(AMove, piece, axis); if (ai) { if (!ai->interpolated) { MoveNow(piece, axis, destination); return; } } float cur = pieces[piece]->pos[axis] - pieces[piece]->original->offset[axis]; float dist = streflop::fabsf(destination - cur); int timeFactor = (1000 * 1000) / (deltaTime * deltaTime); float speed = (dist * timeFactor) / delta; Move(piece, axis, speed, destination, true); }
int CUnitScript::GetUnitVal(int val, int p1, int p2, int p3, int p4) { // may happen in case one uses Spring.GetUnitCOBValue (Lua) on a unit with CNullUnitScript if (!unit) { ShowScriptError("Error: no unit (in GetUnitVal)"); return 0; } #ifndef _CONSOLE switch (val) { case ACTIVATION: if (unit->activated) return 1; else return 0; break; case STANDINGMOVEORDERS: return unit->moveState; break; case STANDINGFIREORDERS: return unit->fireState; break; case HEALTH: { if (p1 <= 0) return int((unit->health / unit->maxHealth) * 100.0f); const CUnit* u = uh->GetUnit(p1); if (u == NULL) return 0; else return int((u->health / u->maxHealth) * 100.0f); } case INBUILDSTANCE: if (unit->inBuildStance) return 1; else return 0; case BUSY: if (busy) return 1; else return 0; break; case PIECE_XZ: { if (!PieceExists(p1)) { ShowScriptError("Invalid piecenumber for get piece_xz"); break; } const float3 relPos = GetPiecePos(p1); const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; return PACKXZ(absPos.x, absPos.z); } case PIECE_Y: { if (!PieceExists(p1)) { ShowScriptError("Invalid piecenumber for get piece_y"); break; } const float3 relPos = GetPiecePos(p1); const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; return int(absPos.y * COBSCALE); } case UNIT_XZ: { if (p1 <= 0) return PACKXZ(unit->pos.x, unit->pos.z); const CUnit* u = uh->GetUnit(p1); if (u == NULL) return PACKXZ(0, 0); else return PACKXZ(u->pos.x, u->pos.z); } case UNIT_Y: { if (p1 <= 0) return int(unit->pos.y * COBSCALE); const CUnit* u = uh->GetUnit(p1); if (u == NULL) return 0; else return int(u->pos.y * COBSCALE); } case UNIT_HEIGHT: { if (p1 <= 0) return int(unit->radius * COBSCALE); const CUnit* u = uh->GetUnit(p1); if (u == NULL) return 0; else return int(u->radius * COBSCALE); } case XZ_ATAN: return int(RAD2TAANG*math::atan2((float)UNPACKX(p1), (float)UNPACKZ(p1)) + 32768 - unit->heading); case XZ_HYPOT: return int(math::hypot((float)UNPACKX(p1), (float)UNPACKZ(p1)) * COBSCALE); case ATAN: return int(RAD2TAANG*math::atan2((float)p1, (float)p2)); case HYPOT: return int(math::hypot((float)p1, (float)p2)); case GROUND_HEIGHT: return int(ground->GetHeightAboveWater(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE); case GROUND_WATER_HEIGHT: return int(ground->GetHeightReal(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE); case BUILD_PERCENT_LEFT: return int((1.0f - unit->buildProgress) * 100); case YARD_OPEN: if (yardOpen) return 1; else return 0; case BUGGER_OFF: break; case ARMORED: if (unit->armoredState) return 1; else return 0; case VETERAN_LEVEL: return int(100 * unit->experience); case CURRENT_SPEED: return int(unit->speed.Length() * COBSCALE); case ON_ROAD: return 0; case IN_WATER: return (unit->pos.y < 0.0f) ? 1 : 0; case MAX_ID: return uh->MaxUnits()-1; case MY_ID: return unit->id; case UNIT_TEAM: { const CUnit* u = uh->GetUnit(p1); return (u != NULL)? unit->team : 0; } case UNIT_ALLIED: { const CUnit* u = uh->GetUnit(p1); if (u != NULL) { return teamHandler->Ally(unit->allyteam, u->allyteam) ? 1 : 0; } return 0; } case UNIT_BUILD_PERCENT_LEFT: { const CUnit* u = uh->GetUnit(p1); if (u != NULL) { return int((1.0f - u->buildProgress) * 100); } return 0; } case MAX_SPEED: { return int(unit->moveType->GetMaxSpeed() * COBSCALE); } break; case REVERSING: { CGroundMoveType* gmt = dynamic_cast<CGroundMoveType*>(unit->moveType); return ((gmt != NULL)? int(gmt->IsReversing()): 0); } break; case CLOAKED: return !!unit->isCloaked; case WANT_CLOAK: return !!unit->wantCloak; case UPRIGHT: return !!unit->upright; case POW: return int(math::pow(((float)p1)/COBSCALE,((float)p2)/COBSCALE)*COBSCALE); case PRINT: LOG("Value 1: %d, 2: %d, 3: %d, 4: %d", p1, p2, p3, p4); break; case HEADING: { if (p1 <= 0) { return unit->heading; } const CUnit* u = uh->GetUnit(p1); if (u != NULL) { return u->heading; } return -1; } case TARGET_ID: { if (unit->weapons[p1 - 1]) { const CWeapon* weapon = unit->weapons[p1 - 1]; const TargetType tType = weapon->targetType; if (tType == Target_Unit) return unit->weapons[p1 - 1]->targetUnit->id; else if (tType == Target_None) return -1; else if (tType == Target_Pos) return -2; else // Target_Intercept return -3; } return -4; // weapon does not exist } case LAST_ATTACKER_ID: return unit->lastAttacker? unit->lastAttacker->id: -1; case LOS_RADIUS: return unit->realLosRadius; case AIR_LOS_RADIUS: return unit->realAirLosRadius; case RADAR_RADIUS: return unit->radarRadius; case JAMMER_RADIUS: return unit->jammerRadius; case SONAR_RADIUS: return unit->sonarRadius; case SONAR_JAM_RADIUS: return unit->sonarJamRadius; case SEISMIC_RADIUS: return unit->seismicRadius; case DO_SEISMIC_PING: float pingSize; if (p1 == 0) { pingSize = unit->seismicSignature; } else { pingSize = p1; } unit->DoSeismicPing(pingSize); break; case CURRENT_FUEL: return int(unit->currentFuel * float(COBSCALE)); case TRANSPORT_ID: return unit->transporter?unit->transporter->id:-1; case SHIELD_POWER: { if (unit->shieldWeapon == NULL) { return -1; } const CPlasmaRepulser* shield = (CPlasmaRepulser*) unit->shieldWeapon; return int(shield->curPower * float(COBSCALE)); } case STEALTH: { return unit->stealth ? 1 : 0; } case SONAR_STEALTH: { return unit->sonarStealth ? 1 : 0; } case CRASHING: return !!unit->crashing; case ALPHA_THRESHOLD: { return int(unit->alphaThreshold * 255); } case COB_ID: { if (p1 <= 0) { return unit->unitDef->cobID; } else { const CUnit* u = uh->GetUnit(p1); return ((u == NULL)? -1 : u->unitDef->cobID); } } case PLAY_SOUND: { // FIXME: this can currently only work for CCobInstance, because Lua can not get sound IDs // (however, for Lua scripts there is already LuaUnsyncedCtrl::PlaySoundFile) CCobInstance* cob = dynamic_cast<CCobInstance*>(this); if (cob == NULL) { return 1; } const CCobFile* script = cob->GetScriptAddr(); if (script == NULL) { return 1; } if ((p1 < 0) || (static_cast<size_t>(p1) >= script->sounds.size())) { return 1; } switch (p3) { //who hears the sound case 0: //ALOS if (!loshandler->InAirLos(unit->pos,gu->myAllyTeam)) { return 0; } break; case 1: //LOS if (!(unit->losStatus[gu->myAllyTeam] & LOS_INLOS)) { return 0; } break; case 2: //ALOS or radar if (!(loshandler->InAirLos(unit->pos,gu->myAllyTeam) || unit->losStatus[gu->myAllyTeam] & (LOS_INRADAR))) { return 0; } break; case 3: //LOS or radar if (!(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_INRADAR))) { return 0; } break; case 4: //everyone break; case 5: //allies if (unit->allyteam != gu->myAllyTeam) { return 0; } break; case 6: //team if (unit->team != gu->myTeam) { return 0; } break; case 7: //enemies if (unit->allyteam == gu->myAllyTeam) { return 0; } break; } if (p4 == 0) { Channels::General.PlaySample(script->sounds[p1], unit->pos, unit->speed, float(p2) / COBSCALE); } else { Channels::General.PlaySample(script->sounds[p1], float(p2) / COBSCALE); } return 0; } case SET_WEAPON_UNIT_TARGET: { const unsigned int weaponID = p1 - 1; const unsigned int targetID = p2; const bool userTarget = !!p3; if (weaponID >= unit->weapons.size()) { return 0; } CWeapon* weapon = unit->weapons[weaponID]; if (weapon == NULL) { return 0; } //! if targetID is 0, just sets weapon->haveUserTarget //! to false (and targetType to None) without attacking CUnit* target = (targetID > 0)? uh->GetUnit(targetID): NULL; return (weapon->AttackUnit(target, userTarget) ? 1 : 0); } case SET_WEAPON_GROUND_TARGET: { const int weaponID = p1 - 1; const float3 pos = float3(float(UNPACKX(p2)), float(p3) / float(COBSCALE), float(UNPACKZ(p2))); const bool userTarget = !!p4; if ((weaponID < 0) || (static_cast<size_t>(weaponID) >= unit->weapons.size())) { return 0; } CWeapon* weapon = unit->weapons[weaponID]; if (weapon == NULL) { return 0; } return weapon->AttackGround(pos, userTarget) ? 1 : 0; } case MIN: return std::min(p1, p2); case MAX: return std::max(p1, p2); case ABS: return abs(p1); case KSIN: return int(1024*math::sinf(TAANG2RAD*(float)p1)); case KCOS: return int(1024*math::cosf(TAANG2RAD*(float)p1)); case KTAN: return int(1024*math::tanf(TAANG2RAD*(float)p1)); case SQRT: return int(math::sqrt((float)p1)); case FLANK_B_MODE: return unit->flankingBonusMode; case FLANK_B_DIR: switch (p1) { case 1: return int(unit->flankingBonusDir.x * COBSCALE); case 2: return int(unit->flankingBonusDir.y * COBSCALE); case 3: return int(unit->flankingBonusDir.z * COBSCALE); case 4: unit->flankingBonusDir.x = (p2/(float)COBSCALE); return 0; case 5: unit->flankingBonusDir.y = (p2/(float)COBSCALE); return 0; case 6: unit->flankingBonusDir.z = (p2/(float)COBSCALE); return 0; case 7: unit->flankingBonusDir = float3(p2/(float)COBSCALE, p3/(float)COBSCALE, p4/(float)COBSCALE).Normalize(); return 0; default: return(-1); } case FLANK_B_MOBILITY_ADD: return int(unit->flankingBonusMobilityAdd * COBSCALE); case FLANK_B_MAX_DAMAGE: return int((unit->flankingBonusAvgDamage + unit->flankingBonusDifDamage) * COBSCALE); case FLANK_B_MIN_DAMAGE: return int((unit->flankingBonusAvgDamage - unit->flankingBonusDifDamage) * COBSCALE); case KILL_UNIT: { //! ID 0 is reserved for the script's owner CUnit* u = (p1 > 0)? uh->GetUnit(p1): this->unit; if (u == NULL) { return 0; } if (u->beingBuilt) { // no explosions and no corpse for units under construction u->KillUnit(false, true, NULL); } else { u->KillUnit(p2 != 0, p3 != 0, NULL); } return 1; } case WEAPON_RELOADSTATE: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return unit->weapons[p1-1]->reloadStatus; } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->reloadStatus; unit->weapons[np1 - 1]->reloadStatus = p2; return old; } else { return -1; } } case WEAPON_RELOADTIME: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return unit->weapons[p1-1]->reloadTime; } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->reloadTime; unit->weapons[np1 - 1]->reloadTime = p2; return old; } else { return -1; } } case WEAPON_ACCURACY: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return int(unit->weapons[p1-1]->accuracy * COBSCALE); } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->accuracy * COBSCALE; unit->weapons[np1 - 1]->accuracy = float(p2) / COBSCALE; return old; } else { return -1; } } case WEAPON_SPRAY: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return int(unit->weapons[p1-1]->sprayAngle * COBSCALE); } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->sprayAngle * COBSCALE; unit->weapons[np1 - 1]->sprayAngle = float(p2) / COBSCALE; return old; } else { return -1; } } case WEAPON_RANGE: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return int(unit->weapons[p1 - 1]->range * COBSCALE); } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->range * COBSCALE; unit->weapons[np1 - 1]->range = float(p2) / COBSCALE; return old; } else { return -1; } } case WEAPON_PROJECTILE_SPEED: { const int np1 = -p1; if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) { return int(unit->weapons[p1-1]->projectileSpeed * COBSCALE); } else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) { const int old = unit->weapons[np1 - 1]->projectileSpeed * COBSCALE; unit->weapons[np1 - 1]->projectileSpeed = float(p2) / COBSCALE; return old; } else { return -1; } } case GAME_FRAME: { return gs->frameNum; } default: if ((val >= GLOBAL_VAR_START) && (val <= GLOBAL_VAR_END)) { return globalVars[val - GLOBAL_VAR_START]; } else if ((val >= TEAM_VAR_START) && (val <= TEAM_VAR_END)) { return teamVars[unit->team][val - TEAM_VAR_START]; } else if ((val >= ALLY_VAR_START) && (val <= ALLY_VAR_END)) { return allyVars[unit->allyteam][val - ALLY_VAR_START]; } else if ((val >= UNIT_VAR_START) && (val <= UNIT_VAR_END)) { const int varID = val - UNIT_VAR_START; if (p1 == 0) { return unitVars[varID]; } else if (p1 > 0) { // get the unit var for another unit const CUnit* u = uh->GetUnit(p1); if (u != NULL && u->script != NULL) { return u->script->unitVars[varID]; } } else { // set the unit var for another unit p1 = -p1; CUnit* u = uh->GetUnit(p1); if (u != NULL && u->script != NULL) { u->script->unitVars[varID] = p2; return 1; } } return 0; } else { LOG_L(L_ERROR, "CobError: Unknown get constant %d (params = %d %d %d %d)", val, p1, p2, p3, p4); } } #endif return 0; }
//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(absPos, float3(0, 0, 0), 30, 30, NULL); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed + unit->residualImpulse * 0.5f; float sql = baseSpeed.SqLength(); if (sql > 9) { const float l = math::sqrt(sql); const float l2 = 3 + math::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.SqLength() > 12*12) { speed.Normalize(); speed *= 12; } /* TODO Push this back. Don't forget to pass the team (color). */ if (flags & PF_Shatter) { Shatter(piece, absPos, speed); } else { LocalModelPiece* pieceData = pieces[piece]; if (pieceData->original != NULL) { int newflags = PF_Fall; // if they don't fall they could live forever if (flags & PF_Explode) { newflags |= PF_Explode; } //if (flags & PF_Fall) { newflags |= PF_Fall; } if ((flags & PF_Smoke) && ph->particleSaturation < 1) { newflags |= PF_Smoke; } if ((flags & PF_Fire) && ph->particleSaturation < 0.95f) { newflags |= PF_Fire; } if (flags & PF_NoCEGTrail) { newflags |= PF_NoCEGTrail; } //LOG_L(L_DEBUG, "Exploding %s as %d", script.pieceNames[piece].c_str(), dl); new CPieceProjectile(absPos, speed, pieceData, newflags,unit,0.5f); } } #endif }
void CUnitScript::EmitSfx(int sfxType, int piece) { #ifndef _CONSOLE if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for emit-sfx"); return; } if (ph->particleSaturation > 1.0f && sfxType < SFX_CEG) { // skip adding (unsynced!) particles when we have too many return; } // Make sure wakes are only emitted on water if ((sfxType >= SFX_WAKE) && (sfxType <= SFX_REVERSE_WAKE_2)) { if (ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 0.0f) { return; } } float3 relPos = ZeroVector; float3 relDir = UpVector; if (!GetEmitDirPos(piece, relPos, relDir)) { ShowScriptError("emit-sfx: GetEmitDirPos failed"); return; } relDir.SafeNormalize(); const float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; const float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x; float alpha = 0.3f + gu->usRandFloat() * 0.2f; float alphaFalloff = 0.004f; float fadeupTime = 4; const UnitDef* ud = unit->unitDef; const MoveDef* md = ud->moveDef; // hovercraft need special care if (md != NULL && md->moveType == MoveDef::Hover_Move) { fadeupTime = 8.0f; alpha = 0.15f + gu->usRandFloat() * 0.2f; alphaFalloff = 0.008f; } switch (sfxType) { case SFX_REVERSE_WAKE: case SFX_REVERSE_WAKE_2: { //reverse wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_WAKE_2: //wake 2, in TA it lives longer.. case SFX_WAKE: { //regular ship wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_BUBBLE: { //submarine bubble. does not provide direction through piece vertices.. float3 pspeed = gu->usRandVector() * 0.1f; pspeed.y += 0.2f; new CBubbleProjectile( pos + gu->usRandVector() * 2.0f, pspeed, 40.0f + gu->usRandFloat() * 30.0f, 1.0f + gu->usRandFloat() * 2.0f, 0.01f, unit, 0.3f + gu->usRandFloat() * 0.3f ); } break; case SFX_WHITE_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.5f); break; case SFX_BLACK_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.6f); break; case SFX_VTOL: { const float3 speed = unit->speed * 0.7f + unit->frontdir * 0.5f * relDir.z + unit->updir * 0.5f * -math::fabs(relDir.y) + unit->rightdir * 0.5f * relDir.x; CHeatCloudProjectile* hc = new CHeatCloudProjectile( pos, speed, 10 + gu->usRandFloat() * 5, 3 + gu->usRandFloat() * 2, unit ); hc->size = 3; break; } default: { if (sfxType & SFX_CEG) { // emit defined explosiongenerator const unsigned index = sfxType - SFX_CEG; if (index >= unit->unitDef->sfxExplGens.size() || unit->unitDef->sfxExplGens[index] == NULL) { ShowScriptError("Invalid explosion generator index for emit-sfx"); break; } IExplosionGenerator* explGen = unit->unitDef->sfxExplGens[index]; explGen->Explosion(0, pos, unit->cegDamage, 1, unit, 0, 0, dir); } else if (sfxType & SFX_FIRE_WEAPON) { // make a weapon fire from the piece const unsigned index = sfxType - SFX_FIRE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } CWeapon* weapon = unit->weapons[index]; const float3 targetPos = weapon->targetPos; const float3 weaponMuzzlePos = weapon->weaponMuzzlePos; weapon->targetPos = pos + dir; weapon->weaponMuzzlePos = pos; weapon->Fire(); weapon->weaponMuzzlePos = weaponMuzzlePos; weapon->targetPos = targetPos; } else if (sfxType & SFX_DETONATE_WEAPON) { const unsigned index = sfxType - SFX_DETONATE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } // detonate weapon from piece const WeaponDef* weaponDef = unit->weapons[index]->weaponDef; CGameHelper::ExplosionParams params = { pos, ZeroVector, weaponDef->damages, weaponDef, unit, // owner NULL, // hitUnit NULL, // hitFeature weaponDef->craterAreaOfEffect, weaponDef->damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noSelfDamage, // ignoreOwner true, // damageGround }; helper->Explosion(params); } } break; } #endif }
//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 CUnitScript::AddAnim(AnimType type, int piece, int axis, float speed, float dest, float accel) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber"); return; } float destf = 0.0f; if (type == AMove) { destf = pieces[piece]->original->offset[axis] + dest; } else { destf = dest; if (type == ATurn) { ClampRad(&destf); } } std::list<AnimInfo*>::iterator animInfoIt; AnimInfo* ai = NULL; AnimType overrideType = ANone; // first find an animation of a type we override // 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 switch (type) { case ATurn: { overrideType = ASpin; animInfoIt = FindAnim(overrideType, piece, axis); } break; case ASpin: { overrideType = ATurn; animInfoIt = FindAnim(overrideType, piece, axis); } break; case AMove: { // ensure we never remove an animation of this type overrideType = AMove; animInfoIt = anims[overrideType].end(); } break; default: { } break; } if (animInfoIt != anims[overrideType].end()) RemoveAnim(overrideType, animInfoIt); // now find an animation of our own type animInfoIt = FindAnim(type, piece, axis); if (animInfoIt == anims[type].end()) { // If we were not animating before, inform the engine of this so it can schedule us // FIXME: this could be done in a cleaner way if (!HaveAnimations()) { GUnitScriptEngine.AddInstance(this); } ai = new AnimInfo(); ai->type = type; ai->piece = piece; ai->axis = axis; anims[type].push_back(ai); } else { ai = *animInfoIt; } ai->dest = destf; ai->speed = speed; ai->accel = accel; ai->done = false; }
void CUnitScript::EmitSfx(int type, int piece) { #ifndef _CONSOLE if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for emit-sfx"); return; } if (ph->particleSaturation > 1 && type < 1024) { // skip adding (unsynced!) particles when we have too many return; } float3 relPos = ZeroVector; float3 relDir = UpVector; if (!GetEmitDirPos(piece, relPos, relDir)) { ShowScriptError("emit-sfx: GetEmitDirPos failed"); return; } const float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; const float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.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.0f; 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) { return; } } switch (type) { case SFX_REVERSE_WAKE: case SFX_REVERSE_WAKE_2: { //reverse wake relDir *= -0.2f; new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_WAKE_2: //wake 2, in TA it lives longer.. case SFX_WAKE: { //regular ship wake relDir *= 0.2f; new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_BUBBLE: { //submarine bubble. does not provide direction through piece vertices.. float3 pspeed = gu->usRandVector() * 0.1f; pspeed.y += 0.2f; new CBubbleProjectile( pos + gu->usRandVector() * 2.0f, pspeed, 40.0f + gu->usRandFloat() * 30.0f, 1.0f + gu->usRandFloat() * 2.0f, 0.01f, unit, 0.3f + gu->usRandFloat() * 0.3f ); } break; case SFX_WHITE_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.5f); break; case SFX_BLACK_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.6f); break; case SFX_VTOL: { //vtol relDir *= 0.2f; const float3 udir = unit->frontdir * relDir.z + unit->updir * -fabs(relDir.y) + unit->rightdir * relDir.x; CHeatCloudProjectile* hc = new CHeatCloudProjectile( pos, unit->speed * 0.7f + udir * 0.5f, 10 + gu->usRandFloat() * 5, 3 + gu->usRandFloat() * 2, unit ); hc->size = 3; break; } default: { if (type & SFX_CEG) { // emit defined explosiongenerator const unsigned index = type - SFX_CEG; if (index >= unit->unitDef->sfxExplGens.size() || unit->unitDef->sfxExplGens[index] == NULL) { ShowScriptError("Invalid explosion generator index for emit-sfx"); break; } float3 ndir = dir; CExplosionGenerator* explGen = unit->unitDef->sfxExplGens[index]; explGen->Explosion(pos, unit->cegDamage, 1, unit, 0, 0, ndir.SafeNormalize()); } else if (type & SFX_FIRE_WEAPON) { // make a weapon fire from the piece const unsigned index = type - SFX_FIRE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } CWeapon* weapon = unit->weapons[index]; const float3 targetPos = weapon->targetPos; const float3 weaponMuzzlePos = weapon->weaponMuzzlePos; float3 ndir = dir; // don't override the weapon's target position // if it was not set internally (so that force- // fire keeps working as expected) if (!weapon->haveUserTarget) { weapon->targetPos = pos + ndir.SafeNormalize(); } weapon->weaponMuzzlePos = pos; weapon->Fire(); weapon->weaponMuzzlePos = weaponMuzzlePos; weapon->targetPos = targetPos; } else if (type & SFX_DETONATE_WEAPON) { const unsigned index = type - SFX_DETONATE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { 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) { Channels::Battle.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, weaponDef->noSelfDamage, weaponDef->impactOnly, weaponDef->explosionGenerator, NULL, float3(0, 0, 0), weaponDef->id ); } } break; } #endif }
void CUnitScript::EmitSfx(int sfxType, int piece) { #ifndef _CONSOLE if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for emit-sfx"); return; } if (projectileHandler->GetParticleSaturation() > 1.0f && sfxType < SFX_CEG) { // skip adding (unsynced!) particles when we have too many return; } // Make sure wakes are only emitted on water if ((sfxType >= SFX_WAKE) && (sfxType <= SFX_REVERSE_WAKE_2)) { if (CGround::GetApproximateHeight(unit->pos.x, unit->pos.z) > 0.0f) { return; } } float3 relPos = ZeroVector; float3 relDir = UpVector; if (!GetEmitDirPos(piece, relPos, relDir)) { ShowUnitScriptError("emit-sfx: GetEmitDirPos failed"); return; } relDir.SafeNormalize(); const float3 pos = unit->GetObjectSpacePos(relPos); const float3 dir = unit->GetObjectSpaceVec(relDir); float alpha = 0.3f + gu->RandFloat() * 0.2f; float alphaFalloff = 0.004f; float fadeupTime = 4; const UnitDef* ud = unit->unitDef; const MoveDef* md = unit->moveDef; // hovercraft need special care if (md != NULL && md->speedModClass == MoveDef::Hover) { fadeupTime = 8.0f; alpha = 0.15f + gu->RandFloat() * 0.2f; alphaFalloff = 0.008f; } switch (sfxType) { case SFX_REVERSE_WAKE: case SFX_REVERSE_WAKE_2: { //reverse wake new CWakeProjectile( unit, pos + gu->RandVector() * 2.0f, dir * 0.4f, 6.0f + gu->RandFloat() * 4.0f, 0.15f + gu->RandFloat() * 0.3f, alpha, alphaFalloff, fadeupTime ); break; } case SFX_WAKE_2: //wake 2, in TA it lives longer.. case SFX_WAKE: { //regular ship wake new CWakeProjectile( unit, pos + gu->RandVector() * 2.0f, dir * 0.4f, 6.0f + gu->RandFloat() * 4.0f, 0.15f + gu->RandFloat() * 0.3f, alpha, alphaFalloff, fadeupTime ); break; } case SFX_BUBBLE: { //submarine bubble. does not provide direction through piece vertices.. float3 pspeed = gu->RandVector() * 0.1f; pspeed.y += 0.2f; new CBubbleProjectile( unit, pos + gu->RandVector() * 2.0f, pspeed, 40.0f + gu->RandFloat() * GAME_SPEED, 1.0f + gu->RandFloat() * 2.0f, 0.01f, 0.3f + gu->RandFloat() * 0.3f ); } break; case SFX_WHITE_SMOKE: //damaged unit smoke new CSmokeProjectile(unit, pos, gu->RandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, 0.5f); break; case SFX_BLACK_SMOKE: //damaged unit smoke new CSmokeProjectile(unit, pos, gu->RandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, 0.6f); break; case SFX_VTOL: { const float3 speed = unit->speed * 0.7f + unit->frontdir * 0.5f * relDir.z + unit->updir * 0.5f * -math::fabs(relDir.y) + unit->rightdir * 0.5f * relDir.x; CHeatCloudProjectile* hc = new CHeatCloudProjectile( unit, pos, speed, 10 + gu->RandFloat() * 5, 3 + gu->RandFloat() * 2 ); hc->size = 3; break; } default: { if (sfxType & SFX_CEG) { // emit defined explosion-generator (can only be custom, not standard) // index is made valid by callee, an ID of -1 means CEG failed to load explGenHandler->GenExplosion(ud->GetModelExplosionGeneratorID(sfxType - SFX_CEG), pos, dir, unit->cegDamage, 1.0f, 0.0f, unit, NULL); } else if (sfxType & SFX_FIRE_WEAPON) { // make a weapon fire from the piece const unsigned index = sfxType - SFX_FIRE_WEAPON; if (index >= unit->weapons.size()) { ShowUnitScriptError("Invalid weapon index for emit-sfx"); break; } CWeapon* w = unit->weapons[index]; const SWeaponTarget origTarget = w->GetCurrentTarget(); const float3 origWeaponMuzzlePos = w->weaponMuzzlePos; w->SetAttackTarget(SWeaponTarget(pos + dir)); w->weaponMuzzlePos = pos; w->Fire(true); w->weaponMuzzlePos = origWeaponMuzzlePos; bool origRestored = w->Attack(origTarget); assert(origRestored); } else if (sfxType & SFX_DETONATE_WEAPON) { const unsigned index = sfxType - SFX_DETONATE_WEAPON; if (index >= unit->weapons.size()) { ShowUnitScriptError("Invalid weapon index for emit-sfx"); break; } // detonate weapon from piece const WeaponDef* weaponDef = unit->weapons[index]->weaponDef; CGameHelper::ExplosionParams params = { pos, ZeroVector, weaponDef->damages, weaponDef, unit, // owner NULL, // hitUnit NULL, // hitFeature weaponDef->craterAreaOfEffect, weaponDef->damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noSelfDamage, // ignoreOwner true, // damageGround -1u // projectileID }; helper->Explosion(params); } } break; } #endif }
//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->GetObjectSpacePos(relPos); #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(NULL, absPos, ZeroVector, 30, 30); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed; float3 explSpeed((0.5f - gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f); if (baseSpeed.SqLength() > 9) { const float l = baseSpeed.Length(); const float l2 = 3 + math::sqrt(l - 3); baseSpeed *= (l2 / l); } if (unit->pos.y - CGround::GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) { explSpeed.y = (0.5f - gs->randFloat()) * 6.0f; } explSpeed += baseSpeed; // limit projectile speed to 12 elmos/frame (why?) if (false && explSpeed.SqLength() > (12.0f*12.0f)) { explSpeed = (explSpeed.Normalize() * 12.0f); } if (flags & PF_Shatter) { Shatter(piece, absPos, explSpeed); return; } if (pieces[piece]->original == NULL) return; // projectiles that don't fall could live forever int newflags = PF_Fall; const float partSat = projectileHandler->GetParticleSaturation(); if (flags & PF_Explode) { newflags |= PF_Explode; } // if (flags & PF_Fall) { newflags |= PF_Fall; } if ((flags & PF_Smoke) && partSat < 1.0f) { newflags |= PF_Smoke; } if ((flags & PF_Fire) && partSat < 0.95f) { newflags |= PF_Fire; } if (flags & PF_NoCEGTrail) { newflags |= PF_NoCEGTrail; } if (flags & PF_Recursive) { newflags |= PF_Recursive; } new CPieceProjectile(unit, pieces[piece], absPos, explSpeed, newflags, 0.5f); #endif }