File information

Last updated

Original upload

Created by

keanuWheeze

Uploaded by

NexusGuy999

Virus scan

Safe to use

Tags for this mod

323 comments

  1. Mambambo
    Mambambo
    • premium
    • 1 kudos
    I don't suppose there are any plans to expand this with mods for the Drone? I would love a system for adding on things like reduced battery cost for covert quickhacks, the ability to explode (possibly increasing damage equal to remaining battery power), Interacting with things like radios and computers. Perhaps to balance it, You could only have one or two mods at a time on it and they massively reduce battery capacity or range, or it takes much longer to recharge.
  2. skiswin
    skiswin
    • supporter
    • 0 kudos
     
  3. SturgillMalaGerart
    SturgillMalaGerart
    • member
    • 0 kudos
    Interesting mod!! I was wondering if there was the possibility of going down? ( up is ok )
    1. Sprayy
      Sprayy
      • supporter
      • 4 kudos
      Yes, with Shift you go Low, with Spacebar you go Up
  4. Sprayy
    Sprayy
    • supporter
    • 4 kudos
    The mod works flawlessly with the latest update, driving around and everything, thanks for the update
  5. Welc8me
    Welc8me
    • member
    • 1 kudos
    Can it help me attack enemies like a companion?
    1. Sprayy
      Sprayy
      • supporter
      • 4 kudos
      no, is a mini nano drone that you "drive" for spying, doing quickhacks or opening doors , check the video
  6. whenitscold
    whenitscold
    • member
    • 3 kudos
    This is incredible! What a cool addition to any netrunner's kit.
    1. SylvieTM
      SylvieTM
      • member
      • 1 kudos
      Hey, does this mod work well? I see alot of bug reports so I've been holding off on downloading it but is it good and work well?
    2. whenitscold
      whenitscold
      • member
      • 3 kudos
      I've had no issues with it yet although I have not used it much.
  7. ManInBlack7777
    ManInBlack7777
    • member
    • 0 kudos
    What lua file to change for infinite battery?
    EDIT 1: nevermind, I figured it out. It's the .json file that needs changing.
    In your Cyberpunk 2077\bin\x64\plugins\cyber_engine_tweaks\mods\nanoDrone\config\config.json. Look for "batteryMult": some number}.
    Change that number to whatever you want. Changed it to 50 for example and works. It's not infinite but lasts a very long time.
  8. Blood5kream
    Blood5kream
    • member
    • 0 kudos
    i dont know about this
    r6/inut/NanoDrone.xml
            <action name="Jump" map="Jump_Button" /> <!--  Added for NanoDrone mod -->
            <action name="Down" map="Crouch_Button" />

    but this working (just copy all)
    bin\x64\plugins\cyber_engine_tweaks\mods\nanoDrone\modules\input.lua
    local utils = require("modules/utils")
    local Cron = require("modules/Cron")
    local data = require("modules/data")
    local collision = require("modules/collision")
    local target = require("modules/TargetingHelper")
    local lang = require("modules/lang")
    drone = {}
    function drone:new(nd)
    local o = {}
    o.nd = nd
    o.spawned = false
    o.entID = nil
    o.forceTakeOver = false
    o.fullySpawned = false
    o.path = "base\\nano_drone\\drone.ent"
    o.handle = nil
    o.tier = nil
    o.pos = nil
    o.vel = nil
    o.accel = 0
    o.topSpeed = 0
    o.range = 0
    o.batteryPercent = nil
    o.rechargeID = nil
    o.rthID = nil
    o.pinID = nil
    o.ui = require("modules/ui")
    o.playerPos = nil
    o.playerRot = nil
    self.__index = self
       return setmetatable(o, self)
    end
    function drone:init()
    self.batteryPercent = data.getBatteryPercent()
    self.pos = Vector4.new(0, 0, 0, 0)
    self.vel = Vector4.new(0, 0, 0, 0)
    end
    function drone:spawn()
    if data.getTier() == 0 or Game.GetWorkspotSystem():IsActorInWorkspot(GetPlayer()) then
    return
    end
    if not self.spawned then
    if self.batteryPercent > 20 then
    self.spawned = true
    self.playerPos = GetPlayer():GetWorldPosition()
    self.playerRot = GetPlayer():GetWorldOrientation():ToEulerAngles()
    self.nd.input.noSave = true
    self:startDecharge()
    self.accel = data.getAccel()
    self.topSpeed = data.getTopSpeed()
    self.range = data.getRange()
    self.tier = data.getTier()
    local pos = collision.getSpawnPos()
    pos.z = pos.z + 1.5
    self.pos = pos
    self.entID = utils.spawnObject(self.path, pos, GetPlayer():GetWorldForward():ToRotation():ToQuat())
    self.forceTakeOver = true
    self.pinID = utils.placePin(utils.addVector(GetPlayer():GetWorldPosition(), Vector4.new(0, 0, 1.5, 0)), gamedataMappinVariant.ApartmentVariant)
    self.spawnCron = Cron.After(1, function ()
    self.fullySpawned = true -- Need this to filter out this cameras OnTakeControllOverDevice call
    end)
    else
    GetPlayer():SetWarningMessage(tostring(lang.getText(lang.battery_too_low) .. "(" .. self.batteryPercent .. "%/" .. "100%)"))
    end
    end
    end
    function drone:startDecharge()
    Cron.Every(data.getBatteryDrain(), function(timer)
    self.batteryPercent = self.batteryPercent - 1 -- * speed maybe?
    if self.batteryPercent <= 0 then
    self:despawn()
    self.batteryPercent = 0
    Cron.After(0.1, function ()
    GetPlayer():SetWarningMessage(lang.getText(lang.battery_empty))
    end)
    timer:Halt()
    end
    if not self.spawned then timer:Halt() end
    data.setBatteryPercent(self.batteryPercent)
    end)
    end
    function drone:startRecharge()
    self.rechargeID = Cron.Every(data.getBatterySpeed(), function(timer)
    if self.spawned then
    timer:Halt()
    self.rechargeID = nil
    end
    self.batteryPercent = self.batteryPercent + 1
    if self.batteryPercent >= 100 then
    timer:Halt()
    self.rechargeID = nil
    self.batteryPercent = 100
    GetPlayer():SetWarningMessage(lang.getText(lang.battery_full))
    end
    data.setBatteryPercent(self.batteryPercent)
    end)
    end
    function drone:despawn(release)
    StatusEffectHelper.RemoveStatusEffect(GetPlayer(), "GameplayRestriction.NoJump")
    if not self.spawned then return end
    local ctrl = true
    if release ~= nil then
    ctrl = release
    end
    if ctrl then TakeOverControlSystem.ReleaseControl() end
    self.spawned = false
    self.fullySpawned = false
    self.ui.ramCostTooHigh = false
    self.nd.input.noSave = false
    utils.removePin(self.pinID)
    if self.spawnCron then
    Cron.Halt(self.spawnCron)
    self.spawnCron = nil
    end
    if self.rthID then
    Cron.Halt(self.rthID)
    self.rthID = nil
    end
    Cron.NextTick(function ()
    self.handle:GetEntity():Destroy()
    end)
    Cron.After(1.5, function ()
    utils.showInputHint("QuickMelee", lang.getText(lang.input_hint), 1, true)
    end)
    end
    function drone:handleInput(deltaTime)
    local acc = deltaTime * self.accel
    local f = 1.8 -- Deaccaleration factor
    if self.nd.input.forward then
    self.vel.y = math.min(self.vel.y + acc * self.nd.input.analogForward, math.max(self.vel.y, self.topSpeed * self.nd.input.analogForward))
    elseif self.vel.y > 0 then
    self.vel.y = self.vel.y - acc * f
    self.vel.y = math.max(0, self.vel.y)
    end
    if self.nd.input.backwards then
    self.vel.y = math.max(self.vel.y - acc * self.nd.input.analogBackwards, math.min(self.vel.y, -self.topSpeed * self.nd.input.analogBackwards))
    elseif self.vel.y < 0 then
    self.vel.y = self.vel.y + acc * f
    self.vel.y = math.min(0, self.vel.y)
    end
    if self.nd.input.right then
    self.vel.x = math.min(self.vel.x + acc * self.nd.input.analogRight, math.max(self.vel.x, self.topSpeed * self.nd.input.analogRight))
    elseif self.vel.x > 0 then
    self.vel.x = self.vel.x - acc * f
    self.vel.x = math.max(0, self.vel.x)
    end
    if self.nd.input.left then
    self.vel.x = math.max(self.vel.x - acc * self.nd.input.analogLeft, math.min(self.vel.x, -self.topSpeed * self.nd.input.analogLeft))
    elseif self.vel.x < 0 then
    self.vel.x = self.vel.x + acc * f
    self.vel.x = math.min(0, self.vel.x)
    end
    if self.nd.input.up then
    self.vel.z = math.min(self.vel.z + acc * self.nd.input.analogUp, self.topSpeed)
    elseif self.vel.z > 0 then
    self.vel.z = self.vel.z - acc * f
    self.vel.z = math.max(0, self.vel.z)
    end
    if self.nd.input.down then
    self.vel.z = math.max(self.vel.z - acc * self.nd.input.analogDown, -self.topSpeed)
    elseif self.vel.z < 0 then
    self.vel.z = self.vel.z + acc * f
    self.vel.z = math.min(0, self.vel.z)
    end
    end
    function drone:move(deltaTime)
    local dir = Game.GetCameraSystem():GetActiveCameraForward()
    local dist = 100
    local globalVel = Vector4.new(0, 0, 0, 0)
    globalVel.x = globalVel.x + (dir.x * self.vel.y) * (deltaTime * 25)
    globalVel.y = globalVel.y + (dir.y * self.vel.y) * (deltaTime * 25)
    local dir = Game.GetCameraSystem():GetActiveCameraRight()
    globalVel.x = globalVel.x + (dir.x * self.vel.x) * (deltaTime * 25)
    globalVel.y = globalVel.y + (dir.y * self.vel.x) * (deltaTime * 25)
    globalVel.z = self.vel.z * 0.3
    local globalVelN = globalVel:Normalize()
    local hit, normal = target.GetLookAtPosition(utils.addVector(self.pos, utils.multVector(globalVelN, 0.7)), utils.addVector(self.pos, utils.multVector(globalVelN, 10)))
    if hit then
    dist = Vector4.Distance(hit, self.pos)
    end
    if (dist < 0.8) then
    globalVel = utils.multVector(globalVel, 0.9)
    normal = utils.multVector(Vector4.Vector3To4(normal), globalVel:Length())
    self.pos.x = self.pos.x + math.min((globalVel.x + normal.x), normal.y)
    self.pos.y = self.pos.y + math.max((globalVel.y + normal.y), normal.x)
    self.pos.z = self.pos.z + (globalVel.z + normal.z)
    else
    self.pos.x = self.pos.x + globalVel.x
    self.pos.y = self.pos.y + globalVel.y
    self.pos.z = self.pos.z + globalVel.z
    end
    Game.GetTeleportationFacility():Teleport(self.handle, self.pos, self.handle:GetWorldOrientation():ToEulerAngles())
    end
    function drone:handleHUD()
    if self.ui.hud then
    local dist = math.floor(Vector4.Distance(GetPlayer():GetWorldPosition(), self.pos))
    local rangeStr = lang.getText(lang.range) .. ": " .. dist .. "/" .. math.floor(self.range)
    local batteryStr = lang.getText(lang.drone_status) .. ": \n  " .. lang.getText(lang.battery) .. ": " .. math.floor(self.batteryPercent) .. "%"
    if self.ui.quickhacking then
    local qhBattery = ui.qhCost * data.getQuickhackMult() * 10
    if self.batteryPercent - qhBattery <= 0 then
    self.ui.ram_conflictText:SetVisible(true)
    self.ui.ramCostTooHigh = true
    else
    self.ui.ram_conflictText:SetVisible(false)
    self.ui.ramCostTooHigh = false
    end
    batteryStr = batteryStr .. " - " .. math.floor(qhBattery) .. "%"
    else
    self.ui.ram_conflictText:SetVisible(false)
    self.ui.ramCostTooHigh = false
    end
    inkTextRef.SetText(self.ui.hud.Date, tostring(batteryStr .. " \n  " .. rangeStr))
    end
    end
    function drone:handleRange()
    local time = 5
    if math.floor(Vector4.Distance(GetPlayer():GetWorldPosition(), self.pos)) > data.getRange() and not self.rthID then
    self.ui.max_range_text:SetVisible(true)
    self.ui.max_range_text:SetText(tostring(lang.getText(lang.range_warning) .. time .. "s"))
    self.rthID = Cron.Every(1.0, {tick = time}, function(timer)
    if timer.tick > 0 then
    timer.tick = timer.tick - 1
    self.ui.max_range_text:SetText(tostring(lang.getText(lang.range_warning) .. timer.tick .. "s"))
    else
    timer:Halt()
    self.ui.max_range_text:SetVisible(false)
    self:despawn()
    self.rthID = nil
    end
    end)
    elseif math.floor(Vector4.Distance(GetPlayer():GetWorldPosition(), self.pos)) < data.getRange() and self.rthID then
    self.ui.max_range_text:SetVisible(false)
    Cron.Halt(self.rthID)
    self.rthID = nil
    end
    end
    function drone:handleDoors()
    local target = Game.GetTargetingSystem():GetLookAtObject(GetPlayer(), false, true)
    if not (self.tier >= 3) then return end
    utils.createInteractionHub(lang.getText(lang.open_door), "Choice1", false)
    if not target then return end
    if Vector4.Distance(target:GetWorldPosition(), self.pos) > 2.9 then return end
    if target:GetClassName().value == "Door" then
    -- Handle UI
    if target:HasAnySkillCheckActive() then
    utils.createInteractionHub(lang.getText(lang.door_requires_skillcheck), "Choice1", true)
    else
    local ps = target:GetDevicePS()
    local state = ps:IsOpen()
    local costText = ""
    local cost = 0
    if ps:IsLocked() or ps:IsSealed() then
    cost = data.getQuickhackMult() * 10 * 3
    else
    cost = data.getQuickhackMult() * 8
    end
    cost = math.floor(cost)
    costText = string.format(" (%i%% " .. lang.getText(lang.battery_cost) .. ")", cost)
    if self.batteryPercent - cost <= 0 and costText ~= "" then
    self.ui.ram_conflictText:SetVisible(true)
    else
    self.ui.ram_conflictText:SetVisible(false)
    end
    if not state then
    utils.createInteractionHub(tostring(lang.getText(lang.open_door)) .. costText, "Choice1", true)
    else
    utils.createInteractionHub(tostring(lang.getText(lang.close_door)) .. costText, "Choice1", true)
    end
    -- Handle input
    if self.nd.input.interact and (self.batteryPercent - cost > 0) then
    self.nd.input.interact = false
    if ps:IsLocked() then
    ps:ToggleLockOnDoor()
    end
    if ps:IsSealed() then
    ps:ToggleSealOnDoor()
    end
    self.batteryPercent = self.batteryPercent - cost
    if not state then
    target:OpenDoor()
    else
    target:CloseDoor()
    end
    end
    end
    else
    -- Remove input UI
    utils.createInteractionHub(lang.getText(lang.open_door), "Choice1", false)
    utils.createInteractionHub(lang.getText(lang.close_door), "Choice1", false)
    utils.createInteractionHub(lang.getText(lang.door_requires_skillcheck), "Choice1", false)
    end
    end
    function drone:update(deltaTime)
    if self.spawned then
    if self.forceTakeOver then
    if Game.FindEntityByID(self.entID) then
    self.forceTakeOver = false
    self.handle = Game.FindEntityByID(self.entID)
    self.handle:OnToggleTakeOverControl(ToggleTakeOverControl.new())
    self.handle.maxPitch = 3
    Game.GetStatusEffectSystem():ApplyStatusEffect(GetPlayer():GetEntityID(), "GameplayRestriction.NoJump", GetPlayer():GetRecordID(), GetPlayer():GetEntityID())
    end
    end
    if self.fullySpawned then
    self:handleInput(deltaTime)
    self:move(deltaTime)
    self:handleRange()
    self:handleHUD()
    self:handleDoors()
    Game.GetTimeSystem():UnsetTimeDilation("focusMode")
    if not Game.FindEntityByID(self.entID) then
    self:despawn(true)
    end
    end
    Game.GetTeleportationFacility():Teleport(GetPlayer(), self.playerPos, self.playerRot)
    elseif not self.rechargeID and self.batteryPercent ~= 100 then
    self:startRecharge()
    end
    end
    return drone
    1. ManInBlack7777
      ManInBlack7777
      • member
      • 0 kudos
      Excuse my ignorance, but what exactly does this do?
  9. Ignacioo0
    Ignacioo0
    • member
    • 0 kudos
    It crashes when I open the game, I play with other mods, could you tell me which mods it is not compatible with or is it the version of this mod that is not currently updated to that of cyberpunk 2077?
  10. kalash1k
    kalash1k
    • member
    • 1 kudos
    Does nano droid can't move down Z axis? It seems there is even no key map for this in config. I mean, drone can move in all directions except going down. I mean, like dive in water by crouch key. Wtf?

    Upd. For some reason "ToggleSprint" is just not working for me to move drone down. I made some changes to bind it to "Crouch_Button". And it works.

    r6/inut/NanoDrone.xml
            <action name="Jump" map="Jump_Button" /> <!--  Added for NanoDrone mod -->
            <action name="Down" map="Crouch_Button" />

    bin\x64\plugins\cyber_engine_tweaks\mods\nanoDrone\modules\input.lua
            elseif actionName == 'ToggleSprint' then
                if actionType == 'BUTTON_PRESSED' then
                    input.down = true
                    input.analogDown = 1
                elseif actionType == 'BUTTON_RELEASED' then
                    input.down = false
                    input.analogDown = 0
                end
            elseif actionName == 'Down' then
                if actionType == 'BUTTON_PRESSED' then
                    input.down = true
                    input.analogDown = 1
                elseif actionType == 'BUTTON_RELEASED' then
                    input.down = false
                    input.analogDown = 0
                end

    And another annoying thing... A drone can look In all directions but can't look upper the "horizon". How can I fix it?
    1. ThatJDanielsKid
      ThatJDanielsKid
      • supporter
      • 0 kudos
      You're the goat lol, I decided to check here before messing around in the lua so that's for a quick fix. Also noticed the lack of up on the y-axis camera, if I get something working I'll respond here but just being able to go down may be enough of a fix.