bool CWeapon::AdjustTargetVectorLength( CUnit* targetUnit, float3& targetPos, float3& targetVec, float3& targetDir) const { bool retCode = false; const float tbScale = math::fabsf(targetBorder); CollisionVolume* cvOld = targetUnit->collisionVolume; CollisionVolume cvNew = CollisionVolume(targetUnit->collisionVolume); CollisionQuery cq; // test for "collision" with a temporarily volume // (scaled uniformly by the absolute target-border // factor) cvNew.RescaleAxes(tbScale, tbScale, tbScale); cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_DISC); targetUnit->collisionVolume = &cvNew; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, ZeroVector, NULL)) { // our weapon muzzle is inside the target unit's volume; this // means we do not need to make any adjustments to targetVec targetVec = ZeroVector; } else { targetDir.SafeNormalize(); // otherwise, perform a raytrace to find the proper length correction // factor for non-spherical coldet volumes based on the ray's ingress // (for positive TB values) or egress (for negative TB values) position; // this either increases or decreases the length of <targetVec> but does // not change its direction cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_CONT); // make the ray-segment long enough so it can reach the far side of the // scaled collision volume (helps to ensure a ray-intersection is found) // // note: ray-intersection is NOT guaranteed if the volume itself has a // non-zero offset, since here we are "shooting" at the target UNIT's // midpoint const float3 targetOffset = targetDir * (cvNew.GetBoundingRadius() * 2.0f); const float3 targetRayPos = targetPos + targetOffset; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, targetRayPos, &cq)) { if (targetBorder > 0.0f) { targetVec -= (targetDir * ((targetPos - cq.p0).Length())); } if (targetBorder < 0.0f) { targetVec += (targetDir * ((cq.p1 - targetPos).Length())); } } retCode = true; } targetUnit->collisionVolume = cvOld; // true indicates we took the else-branch and targetDir is now normalized return retCode; }
S3DOPiece* C3DOParser::LoadPiece(S3DModel* model, int pos, S3DOPiece* parent, int* numobj, const std::vector<unsigned char>& fileBuf) { (*numobj)++; _3DObject me; int curOffset = pos; READ_3DOBJECT(me, fileBuf, curOffset); std::string s = GET_TEXT(me.OffsetToObjectName, fileBuf, curOffset); StringToLowerInPlace(s); S3DOPiece* piece = new S3DOPiece(); piece->name = s; piece->parent = parent; piece->offset.x = me.XFromParent * SCALE_FACTOR_3DO; piece->offset.y = me.YFromParent * SCALE_FACTOR_3DO; piece->offset.z = -me.ZFromParent * SCALE_FACTOR_3DO; piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); GetVertexes(&me, piece, fileBuf); GetPrimitives(piece, me.OffsetToPrimitiveArray, me.NumberOfPrimitives, ((pos == 0)? me.SelectionPrimitive: -1), fileBuf); piece->CalcNormals(); piece->SetMinMaxExtends(); piece->emitPos = ZeroVector; piece->emitDir = FwdVector; if (piece->vertexPos.size() >= 2) { piece->emitPos = piece->vertexPos[0]; piece->emitDir = piece->vertexPos[1] - piece->vertexPos[0]; } else if (piece->vertexPos.size() == 1) { piece->emitDir = piece->vertexPos[0]; } model->mins = float3::min(piece->goffset + piece->mins, model->mins); model->maxs = float3::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); if (me.OffsetToChildObject > 0) { piece->children.push_back(LoadPiece(model, me.OffsetToChildObject, piece, numobj, fileBuf)); } if (me.OffsetToSiblingObject > 0) { parent->children.push_back(LoadPiece(model, me.OffsetToSiblingObject, parent, numobj, fileBuf)); } return piece; }
// Iterate over the model and calculate its overall dimensions void CAssParser::CalculateModelDimensions(S3DModel* model, S3DModelPiece* piece) { CMatrix44f scaleRotMat; piece->ComposeTransform(scaleRotMat, ZeroVector, ZeroVector, piece->scales); // cannot set this until parent relations are known, so either here or in BuildPieceHierarchy() piece->goffset = scaleRotMat.Mul(piece->offset) + ((piece->parent != nullptr)? piece->parent->goffset: ZeroVector); // update model min/max extents model->mins = float3::min(piece->goffset + piece->mins, model->mins); model->maxs = float3::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); // Repeat with children for (unsigned int i = 0; i < piece->children.size(); i++) { CalculateModelDimensions(model, piece->children[i]); } }
CUnit* CUnitLoader::LoadUnit(const string& name, float3 pos, int team, bool build, int facing, const CUnit* builder) { // GML_RECMUTEX_LOCK(unit); // LoadUnit. Unitinit puts unit in the quadfield and activeUnits - GML_RECMUTEX_LOCK(sel); // LoadUnit. For anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit. - make sure other threads cannot access an incomplete unit CUnit* unit; SCOPED_TIMER("Unit loader"); const UnitDef* ud = unitDefHandler->GetUnitByName(name); if (!ud) { throw content_error("Couldn't find unittype " + name); } string type = ud->type; if (!build) { pos.y = ground->GetHeight2(pos.x, pos.z); if (ud->floater && pos.y < 0.0f) { // adjust to waterline iif we are submerged pos.y = -ud->waterline; } } if (team < 0) { team = MAX_TEAMS - 1; // FIXME use gs->gaiaTeamID ? (once it is always enabled) } if (type == "GroundUnit") { unit = SAFE_NEW CUnit; } else if (type == "Transport") { unit = SAFE_NEW CTransportUnit; } else if (type == "Building") { unit = SAFE_NEW CBuilding; } else if (type == "Factory") { unit = SAFE_NEW CFactory; } else if (type == "Builder") { unit = SAFE_NEW CBuilder; } else if (type == "Bomber" || type == "Fighter") { unit = SAFE_NEW CUnit; } else if (type == "MetalExtractor") { unit = SAFE_NEW CExtractorBuilding; } else { logOutput << "Unknown unit type " << type.c_str() << "\n"; return NULL; } unit->UnitInit(ud, team, pos); unit->beingBuilt = build; unit->xsize = ((facing & 1) == 0) ? ud->xsize : ud->zsize; unit->zsize = ((facing & 1) == 1) ? ud->xsize : ud->zsize; unit->buildFacing = facing; unit->power = ud->power; unit->maxHealth = ud->health; unit->health = ud->health; //unit->metalUpkeep = ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep = ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius = (int)(ud->controlRadius / SQUARE_SIZE); unit->losHeight = ud->losHeight; unit->metalCost = ud->metalCost; unit->energyCost = ud->energyCost; unit->buildTime = ud->buildTime; unit->aihint = ud->aihint; unit->tooltip = ud->humanName + " - " + ud->tooltip; unit->armoredMultiple = std::max(0.0001f, ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName = ud->wreckName; unit->realLosRadius = (int) (ud->losRadius); unit->realAirLosRadius = (int) (ud->airLosRadius); unit->upright = ud->upright; unit->radarRadius = ud->radarRadius / (SQUARE_SIZE * 8); unit->sonarRadius = ud->sonarRadius / (SQUARE_SIZE * 8); unit->jammerRadius = ud->jammerRadius / (SQUARE_SIZE * 8); unit->sonarJamRadius = ud->sonarJamRadius / (SQUARE_SIZE * 8); unit->seismicRadius = ud->seismicRadius / (SQUARE_SIZE * 8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity = unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth = ud->stealth; unit->sonarStealth = ud->sonarStealth; unit->category = ud->category; unit->armorType = ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed / 30.0f; unit->decloakDistance = ud->decloakDistance; unit->flankingBonusMode = ud->flankingBonusMode; unit->flankingBonusDir = ud->flankingBonusDir; unit->flankingBonusMobility = ud->flankingBonusMobilityAdd * 1000; unit->flankingBonusMobilityAdd = ud->flankingBonusMobilityAdd; unit->flankingBonusAvgDamage = (ud->flankingBonusMax + ud->flankingBonusMin) * 0.5f; unit->flankingBonusDifDamage = (ud->flankingBonusMax - ud->flankingBonusMin) * 0.5f; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->fireState >= 0) unit->fireState = ud->fireState; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if (type == "GroundUnit") { SAFE_NEW CMobileCAI(unit); } else if (type == "Transport") { SAFE_NEW CTransportCAI(unit); } else if (type == "Factory") { SAFE_NEW CFactoryCAI(unit); } else if (type == "Builder") { SAFE_NEW CBuilderCAI(unit); } else if (type == "Bomber") { if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else if(type == "Fighter"){ if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else { SAFE_NEW CCommandAI(unit); } if (ud->canmove && !ud->canfly && (type != "Factory")) { CGroundMoveType* mt = SAFE_NEW CGroundMoveType(unit); mt->maxSpeed = ud->speed / GAME_SPEED; mt->maxWantedSpeed = ud->speed / GAME_SPEED; mt->turnRate = ud->turnRate; mt->baseTurnRate = ud->turnRate; if (!mt->accRate) { logOutput << "acceleration of " << ud->name.c_str() << " is zero!!\n"; } mt->moveType = ud->moveType; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->floatOnWater = (ud->movedata->moveType == MoveData::Hover_Move || ud->movedata->moveType == MoveData::Ship_Move); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } unit->moveType = mt; // Ground-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); } else if (ud->canfly) { // Air-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType* mt = SAFE_NEW CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight + gs->randFloat() * 5; mt->orgWantedHeight = mt->wantedHeight; mt->dontLand = ud->DontLand(); mt->collide = ud->collide; mt->altitudeRate = ud->verticalSpeed; mt->bankingAllowed = ud->bankingAllowed; unit->moveType = mt; } else { CAirMoveType *mt = SAFE_NEW CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->collide = ud->collide; mt->wingAngle = ud->wingAngle; mt->crashDrag = 1 - ud->crashDrag; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = (ud->wantedHeight * 1.5f) + ((gs->randFloat() - 0.3f) * 15 * (mt->isFighter ? 2 : 1)); mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType = SAFE_NEW CMoveType(unit); unit->upright = true; } unit->energyTickMake = ud->energyMake; if (ud->tidalGenerator > 0) unit->energyTickMake += ud->tidalGenerator * mapInfo->map.tidalStrength; unit->model = LoadModel(ud); unit->SetRadius(unit->model->radius); // copy the UnitDef volume archetype data unit->collisionVolume = SAFE_NEW CollisionVolume(ud->collisionVolume); // if no "collisionVolumeScales" tag was defined in UnitDef, // the default scale for this volume will be a ZeroVector if (unit->collisionVolume->GetScale(COLVOL_AXIS_X) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Y) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Z) <= 1.0f) { // aircraft still get half-size spheres for coldet purposes // if no custom volume is defined (unit->model->radius and // unit->radius themselves are no longer altered) const float scaleFactor = (ud->canfly)? 0.5f: 1.0f; unit->collisionVolume->SetDefaultScale(unit->model->radius * scaleFactor); if (unit->collisionVolume->GetBoundingRadius() <= 30.0f) { // the interval-based method fails too easily for units // with small default volumes, force use of raytracing unit->collisionVolume->SetTestType(COLVOL_TEST_CONT); } } if (ud->floater) { // restrict our depth to our waterline unit->pos.y = std::max(-ud->waterline, ground->GetHeight2(unit->pos.x, unit->pos.z)); } else { unit->pos.y = ground->GetHeight2(unit->pos.x, unit->pos.z); } modelParser->CreateLocalModel(unit); unit->cob = SAFE_NEW CCobInstance(GCobEngine.GetCobFile("scripts/" + ud->cobFilename), unit); unit->weapons.reserve(ud->weapons.size()); for (unsigned int i = 0; i < ud->weapons.size(); i++) { unit->weapons.push_back(LoadWeapon(ud->weapons[i].def, unit, &ud->weapons[i])); } // Calculate the max() of the available weapon reloadtimes int relMax = 0; for (vector<CWeapon*>::iterator i = unit->weapons.begin(); i != unit->weapons.end(); ++i) { if ((*i)->reloadTime > relMax) relMax = (*i)->reloadTime; if (dynamic_cast<CBeamLaser*>(*i)) relMax = 150; } // convert ticks to milliseconds relMax *= 30; // TA does some special handling depending on weapon count if (unit->weapons.size() > 1) { relMax = std::max(relMax, 3000); } // Call initializing script functions unit->cob->Call(COBFN_Create); unit->cob->Call("SetMaxReloadTime", relMax); unit->heading = GetHeadingFromFacing(facing); unit->frontdir = GetVectorFromHeading(unit->heading); unit->updir = UpVector; unit->rightdir = unit->frontdir.cross(unit->updir); unit->yardMap = ud->yardmaps[facing]; unit->Init(builder); if (!build) { unit->FinishedBuilding(); } return unit; }
SS3OPiece* CS3OParser::LoadPiece(S3DModel* model, SS3OPiece* parent, unsigned char* buf, int offset) { model->numPieces++; // retrieve piece data Piece* fp = (Piece*)&buf[offset]; fp->swap(); // Does it matter we mess with the original buffer here? Don't hope so. Vertex* vertexList = reinterpret_cast<Vertex*>(&buf[fp->vertices]); int* indexList = reinterpret_cast<int*>(&buf[fp->vertexTable]); int* childList = reinterpret_cast<int*>(&buf[fp->children]); // create piece SS3OPiece* piece = new SS3OPiece(); piece->offset.x = fp->xoffset; piece->offset.y = fp->yoffset; piece->offset.z = fp->zoffset; piece->primType = fp->primitiveType; piece->name = (char*) &buf[fp->name]; piece->parent = parent; // retrieve vertices piece->SetVertexCount(fp->numVertices); for (int a = 0; a < fp->numVertices; ++a) { Vertex* v = (vertexList++); v->swap(); SS3OVertex sv; sv.pos = float3(v->xpos, v->ypos, v->zpos); sv.normal = float3(v->xnormal, v->ynormal, v->znormal).SafeANormalize(); sv.texCoords[0] = float2(v->texu, v->texv); sv.texCoords[1] = float2(v->texu, v->texv); piece->SetVertex(a, sv); } // retrieve draw indices piece->SetIndexCount(fp->vertexTableSize); for (int a = 0; a < fp->vertexTableSize; ++a) { const int vertexDrawIdx = swabDWord(*(indexList++)); piece->SetIndex(a, vertexDrawIdx); } // post process the piece { piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); piece->Trianglize(); piece->SetVertexTangents(); piece->SetMinMaxExtends(); model->mins = float3::min(piece->goffset + piece->mins, model->mins); model->maxs = float3::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); } // load children pieces piece->children.reserve(fp->numchildren); for (int a = 0; a < fp->numchildren; ++a) { int childOffset = swabDWord(*(childList++)); SS3OPiece* childPiece = LoadPiece(model, piece, buf, childOffset); piece->children.push_back(childPiece); } return piece; }
void CFeature::Initialize(const float3& _pos, const FeatureDef* _def, short int _heading, int facing, int _team, std::string fromUnit, const float3& speed) { pos = _pos; def = _def; defName = def->myName; heading = _heading; buildFacing = facing; team = _team; createdFromUnit = fromUnit; ChangeTeam(team); pos.CheckInBounds(); health = def->maxHealth; blocking = def->blocking; xsize = def->xsize; ysize = def->ysize; mass = def->mass; noSelect = def->noSelect; if (def->drawType == DRAWTYPE_3DO) { model = def->LoadModel(team); height = model->height; SetRadius(model->radius); midPos = pos + model->relMidPos; // copy the FeatureDef volume archetype data collisionVolume = SAFE_NEW CollisionVolume(def->collisionVolume); // CFeatureHandler left this volume's axis-scales uninitialized // (ie. no "collisionVolumeScales" tag was defined in FeatureDef) if (collisionVolume->GetScale(COLVOL_AXIS_X) <= 1.0f && collisionVolume->GetScale(COLVOL_AXIS_Y) <= 1.0f && collisionVolume->GetScale(COLVOL_AXIS_Z) <= 1.0f) { collisionVolume->SetDefaultScale(model->radius); } } else if (def->drawType == DRAWTYPE_TREE) { SetRadius(TREE_RADIUS); midPos = pos + (UpVector * TREE_RADIUS); height = 2 * TREE_RADIUS; // copy the FeatureDef volume archetype data collisionVolume = SAFE_NEW CollisionVolume(def->collisionVolume); collisionVolume->SetDefaultScale(TREE_RADIUS); } else { // geothermal (no collision volume) SetRadius(0.0f); midPos = pos; } featureHandler->AddFeature(this); qf->AddFeature(this); CalculateTransform(); if (blocking) { Block(); } if (def->floating) { finalHeight = ground->GetHeight(pos.x, pos.z); } else { finalHeight = ground->GetHeight2(pos.x, pos.z); } if (def->drawType == DRAWTYPE_TREE) { treeDrawer->AddTree(def->modelType, pos, 1); } if (speed != ZeroVector) { deathSpeed = speed; } }