From 5702bc8e8271ee6099da9b82899377c4d0eba90b Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sat, 31 May 2025 10:06:44 +0300 Subject: [PATCH] tank turrets tracking targets --- Assets/Scripts/GameObjects/Units/unitData.lua | 32 +++++- unit.cpp | 98 ++++++++++++++++--- unit.h | 11 ++- vehicle.cpp | 20 ++-- 4 files changed, 129 insertions(+), 32 deletions(-) diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index aa0e210..8379695 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -90,6 +90,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 100, maxRange = 10, damage = 50, @@ -161,10 +162,16 @@ units = { weapons = { { type = WeaponClass.SHELL, + orderType = OrderType.ATTACK, projectile = {id = ProjectileId.SHELL, pos = {x = 0.06, y = 5.82, z = 11.4}, rot = {w = 1, x = 0, y = 0, z = 0}}, rateOfFire = 1000, damage = 200, maxRange = 25, + node = { + name = 'Turret', + rotationSpeed = .05, + maxFireAngle = .1 + }, fireFx = { { vfx = false, @@ -215,6 +222,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 2000, damage = 5000, maxRange = 30, @@ -265,7 +273,7 @@ units = { }, { weapons = { - {type = WeaponClass.HITSCAN, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/WarMechs/fire.ogg', damage = 20, maxRange = 8}, + {type = WeaponClass.HITSCAN, orderType = OrderType.ATTACK, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/WarMechs/fire.ogg', damage = 20, maxRange = 8}, }, buildableUnits = { {id = UnitId.LAND_FACTORY, buildable = true, trigger = 76}, @@ -304,7 +312,7 @@ units = { garrisonCategory = 3 }, { - weapons = {{type = WeaponClass.HITSCAN, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/WarMechs/fire.ogg', damage = 50, maxRange = 20}}, + weapons = {{type = WeaponClass.HITSCAN, orderType = OrderType.ATTACK, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/WarMechs/fire.ogg', damage = 50, maxRange = 20}}, unitClass = UnitClass.TRANSPORT, unitType = UnitType.HOVER, isVehicle = true, @@ -329,7 +337,7 @@ units = { garrisonCategory = 3 }, { - weapons = {{type = WeaponClass.HITSCAN, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/Tanks/attack.ogg', damage = 100, maxRange = 25}}, + weapons = {{type = WeaponClass.HITSCAN, orderType = OrderType.ATTACK, rateOfFire = 2000, fireSfx = PATH .. 'Sounds/Units/Tanks/attack.ogg', damage = 100, maxRange = 25}}, unitClass = UnitClass.TRANSPORT, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, @@ -390,7 +398,8 @@ units = { { weapons = { { - type = WeaponClass.HITSCAN, + type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 200, damage = 300, maxRange = 15, @@ -442,6 +451,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 200, fireFx = { { @@ -493,6 +503,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 200, damage = 300, maxRange = 15, @@ -543,6 +554,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 200, fireSfx = PATH .. 'Sounds/Units/Cruisers/fire.ogg', damage = 300, @@ -594,6 +606,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 100, damage = 80, maxRange = 100, @@ -643,6 +656,7 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 450, damage = 500, maxRange = 100, @@ -669,6 +683,7 @@ units = { }, { type = WeaponClass.CRUISE_MISSILE, + orderType = OrderType.LAUNCH, projectile = {id = ProjectileId.CRUISE_MISSILE, pos = {x = 0, y = 15, z = 0}, rot = {w = .707, x = -.707, y = 0, z = 0}}, rateOfFire = 500, maxRange = 30, @@ -707,6 +722,7 @@ units = { weapons = { { type = WeaponClass.TORPEDO, + orderType = OrderType.ATTACK, rateOfFire = 1000, fireFx = { { @@ -721,6 +737,7 @@ units = { }, { type = WeaponClass.CRUISE_MISSILE, + orderType = OrderType.LAUNCH, rateOfFire = 500, maxRange = 30, fireFx = { @@ -759,6 +776,7 @@ units = { weapons = { { type = WeaponClass.TORPEDO, + orderType = OrderType.ATTACK, projectile = {id = ProjectileId.TORPEDO, pos = {x = 0, y = -.27, z = 4.6}, rot = {w = 1, x = 0, y = 0, z = 0}}, damage = 200, maxRange = 20, @@ -885,9 +903,15 @@ units = { weapons = { { type = WeaponClass.HITSCAN, + orderType = OrderType.ATTACK, rateOfFire = 100, damage = 50, maxRange = 50, + node = { + name = 'TurretBody', + rotationSpeed = .05, + maxFireAngle = .1 + }, fireFx = { { vfx = true, diff --git a/unit.cpp b/unit.cpp index 18c1fe1..12a5661 100755 --- a/unit.cpp +++ b/unit.cpp @@ -41,12 +41,27 @@ namespace battleship{ { sol::table weaponTable = unitTable["weapons"][wid + 1]; maxRange = weaponTable["maxRange"]; + orderType = (Order::TYPE)weaponTable["orderType"]; initProjectileData(weaponTable); + sol::optional nodeTblOpt = unitTable["weapons"][wid + 1]["node"]; + + if(nodeTblOpt != sol::nullopt) initNode(weaponTable); + fireFx = initFx(weaponTable, "fireFx", true); if(fireFx) FxManager::getSingleton()->addFx(fireFx); } + + void Unit::Weapon::initNode(sol::table weaponTable){ + sol::table nodeTbl = weaponTable["node"]; + maxFireAngle = nodeTbl["maxFireAngle"]; + rotSpeed = nodeTbl["rotationSpeed"]; + + string name = nodeTbl["name"]; + node = unit->getModel()->findDescendant(name, true); + } + void Unit::Weapon::initProjectileData(sol::table weaponTable){ string projTableKey = "projectile"; sol::optional proj = weaponTable[projTableKey]; @@ -177,23 +192,14 @@ namespace battleship{ flashNode->setVisible(false); Node *parNode = (attached ? unit->getModel() : Root::getSingleton()->getRootNode()); - sol::optional parOpt = compTbl["parent"]; + sol::optional parNameOpt = compTbl["parent"]; - if(parOpt != sol::nullopt){ - vector descendants; - unit->getModel()->getDescendants(descendants); + if(parNameOpt != sol::nullopt){ string parName = compTbl["parent"]; - parNode = nullptr; - - for(Node *desc : descendants) - if(desc->getName() == parName){ - parNode = desc; - break; - } + parNode = unit->getModel()->findDescendant(parName, true); } parNode->attachChild(flashNode); - fxComponents.push_back(FxManager::Fx::Component((void*)flashNode, vfx, duration, compPos, offset)); } else{ @@ -212,7 +218,31 @@ namespace battleship{ if(fireFx) fm->removeFx(fireFx); } - void Unit::Weapon::update(){} + //TODO replace unit pos with absolute weapon pos for withinAngle + void Unit::Weapon::update(){ + if(node){ + dirVec = rot * unit->getDirVec(); + leftVec = rot * unit->getLeftVec(); + upVec = rot * unit->getUpVec(); + } + + int numOrders = unit->getNumOrders(); + int ordTp = (numOrders > 0 ? (int)unit->getOrder(0).type : -1); + Unit *targUnit = (ordTp != -1 ? unit->getOrder(0).targets[0].unit : nullptr); + + if(ordTp == (int)orderType){ + Vector3 targPos = (targUnit ? targUnit->getPos() : unit->getOrder(0).targets[0].pos); + float targDist = unit->getPos().getDistanceFrom(targPos); + + bool withinRange = (minRange <= targDist && targDist <= maxRange); + bool withinAngle = (node ? Vector3(dirVec.x, 0, dirVec.z).norm().getAngleBetween((targPos - unit->getPos()).norm()) <= maxFireAngle : true); + + if((Order::TYPE)ordTp == Order::TYPE::ATTACK && withinRange && withinAngle) + fire(unit->getOrder(0)); + } + else + trackTarget(unit->getPos() + unit->getDirVec()); + } void Unit::Weapon::useFx(FxManager::Fx *fx, Vector3 targPos, bool fire){ if(fx && !fire) @@ -278,14 +308,36 @@ namespace battleship{ Vector3 leftVec = unit->getLeftVec(); Vector3 upVec = unit->getUpVec(); Vector3 dirVec = unit->getDirVec(); - Vector3 p = unit->getPos() + leftVec * projPos.x + upVec * projPos.y + dirVec * projPos.z; + Quaternion r = projRot * unit->getRot(); + Vector3 p = unit->getPos() + leftVec * projPos.x + upVec * projPos.y + dirVec * projPos.z; + + if(node){ + r = node->localToGlobalOrientation(projRot); + p = node->localToGlobalPosition(projPos); + } + unit->getPlayer()->addProjectile(GameObjectFactory::createProjectile(unit, projId, p, r)); } lastFireTime = getTime(); } + void Unit::Weapon::trackTarget(Vector3 targPos){ + if(!node) return; + + Vector3 targDir = targPos - unit->getPos(); + Vector3 targDirHor = Vector3(targDir.x, 0, targDir.z).norm(); + Vector3 dirVecHor = Vector3(dirVec.x, 0, dirVec.z).norm(); + + float angle = dirVecHor.getAngleBetween(targDirHor); + bool right = (Vector3(leftVec.x, 0, leftVec.z).norm().getAngleBetween(targDirHor) < PI / 2); + float rotAngle = (rotSpeed < angle ? rotSpeed : angle); + + rot = Quaternion((right ? 1 : -1) * rotAngle, Vector3::VEC_J) * rot; + node->setOrientation(rot); + } + Unit::Unit(Player *player, int id, Vector3 pos, Quaternion rot, State st) : GameObject(GameObject::Type::UNIT, id, player, pos, rot), state(st){ selectable = true; @@ -681,12 +733,18 @@ namespace battleship{ removeOrder(0); } + + vector attackWeapons = getWeaponsByOrder(Order::TYPE::ATTACK); + Vector3 targPos = (order.targets[0].unit ? order.targets[0].unit->getPos() : order.targets[0].pos); + + for(Weapon *weapon : attackWeapons) + weapon->trackTarget(targPos); } bool Unit::validateOrder(Order order){ switch (order.type) { case Order::TYPE::ATTACK: - return !weapons.empty(); + return !getWeaponsByOrder(Order::TYPE::ATTACK).empty(); case Order::TYPE::BUILD: return unitClass == UnitClass::ENGINEER; case Order::TYPE::PATROL: @@ -721,6 +779,16 @@ namespace battleship{ } } + vector Unit::getWeaponsByOrder(Order::TYPE type){ + vector weaps; + + for(Weapon *w : weapons) + if(w->getOrderType() == type) + weaps.push_back(w); + + return weapons; + } + void Unit::receiveOrder(Order order, bool add) { if(!validateOrder(order)) return; diff --git a/unit.h b/unit.h index 118cd69..07601c5 100755 --- a/unit.h +++ b/unit.h @@ -94,6 +94,7 @@ namespace battleship{ ~Weapon(); virtual void update(); virtual void fire(Order); + void trackTarget(vb01::Vector3); inline Type getType(){return type;} inline int getProjectileId(){return projId;} inline int getRateOfFire(){return rateOfFire;} @@ -101,18 +102,23 @@ namespace battleship{ inline int getMinRange(){return minRange;} inline int getMaxRange(){return maxRange;} inline Unit* getUnit(){return unit;} + inline Order::TYPE getOrderType(){return orderType;} private: Type type; int id, projId = -1, rateOfFire, damage = 0; - float minRange = 0, maxRange; + float minRange = 0, maxRange, rotSpeed, maxFireAngle; vb01::Vector3 projPos; - vb01::Quaternion projRot; + vb01::Quaternion projRot, rot = vb01::Quaternion::QUAT_W; vb01::s64 lastFireTime = 0; Unit *unit = nullptr; FxManager::Fx *fireFx = nullptr; + vb01::Node *node = nullptr; + Order::TYPE orderType; + vb01::Vector3 upVec = vb01::Vector3(0, 1, 0), dirVec = vb01::Vector3(0, 0, 1), leftVec = vb01::Vector3(1, 0, 0); static std::string LASER_FLAG; void initProjectileData(sol::table); + void initNode(sol::table); FxManager::Fx* initFx(sol::table, std::string, bool); void useFx(FxManager::Fx*, vb01::Vector3, bool); inline bool canFire(){return vb01::getTime() - lastFireTime > rateOfFire;} @@ -215,6 +221,7 @@ namespace battleship{ void removeBar(vb01::Node*); vb01::Node* createBar(vb01::Vector2, vb01::Vector2, vb01::Vector4); void displayUnitStats(vb01::Node*, vb01::Node*, int, int, bool, vb01::Vector2 offset = vb01::Vector2::VEC_ZERO); + std::vector getWeaponsByOrder(Order::TYPE); inline std::vector getArmorTypes(){return armorTypes;} }; } diff --git a/vehicle.cpp b/vehicle.cpp index 2d1bfcb..15caff3 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -316,12 +316,19 @@ namespace battleship{ Unit::attack(order); int currNumOrders = orders.size(); - if(weapons.empty() || prevNumOrders != currNumOrders) return; + if(prevNumOrders != currNumOrders) return; Order::Target target = order.targets[0]; Vector3 targVec = (target.unit ? target.unit->getPos() : target.pos) - pos; float distToTarg = targVec.getLength(); - Weapon *weapon = weapons[0]; + + vector attackWeapons = getWeaponsByOrder(Order::TYPE::ATTACK); + Weapon *weapon = attackWeapons[0]; + + for(Weapon *w : attackWeapons) + if(w->getMaxRange() > weapon->getMaxRange()) + weapon = w; + float minDist = weapon->getMaxRange(); if(order.playerAssigned || (!order.playerAssigned && state == Unit::State::CHASE)){ @@ -334,15 +341,6 @@ namespace battleship{ removeOrder(0); return; } - - float angleToTarg = targVec.norm().getAngleBetween(dirVec); - - if(distToTarg <= weapon->getMaxRange()){ - if(angleToTarg <= anglePrecision) - weapon->fire(order); - else if(angleToTarg > anglePrecision) - turn(calculateRotation(targVec.norm(), angleToTarg, maxTurnAngle)); - } }