Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --Converted with ttyyuu12345's model to script plugin v4
- function sandbox(var,func)
- local env = getfenv(func)
- local newenv = setmetatable({},{
- __index = function(self,k)
- if k=="script" then
- return var
- else
- return env[k]
- end
- end,
- })
- setfenv(func,newenv)
- return func
- end
- cors = {}
- mas = Instance.new("Model",game:GetService("Lighting"))
- Tool0 = Instance.new("Tool")
- Part1 = Instance.new("Part")
- SpecialMesh2 = Instance.new("SpecialMesh")
- Sound3 = Instance.new("Sound")
- Sound4 = Instance.new("Sound")
- LocalScript5 = Instance.new("LocalScript")
- Script6 = Instance.new("Script")
- Sound7 = Instance.new("Sound")
- Sound8 = Instance.new("Sound")
- Script9 = Instance.new("Script")
- LocalScript10 = Instance.new("LocalScript")
- Animation11 = Instance.new("Animation")
- LocalScript12 = Instance.new("LocalScript")
- Part13 = Instance.new("Part")
- SpecialMesh14 = Instance.new("SpecialMesh")
- Script15 = Instance.new("Script")
- Script16 = Instance.new("Script")
- LocalScript17 = Instance.new("LocalScript")
- Tool0.Name = "RPG-7"
- Tool0.Parent = mas
- Tool0.GripPos = Vector3.new(0.0500000007, -0.600000024, -1)
- Tool0.ToolTip = "RPG-7"
- Tool0.CanBeDropped = false
- Part1.Name = "Handle"
- Part1.Parent = Tool0
- Part1.Transparency = 1
- Part1.Rotation = Vector3.new(92.1199951, 62, -91.8699951)
- Part1.FormFactor = Enum.FormFactor.Custom
- Part1.Size = Vector3.new(0.839999974, 1.14999998, 3.55000019)
- Part1.CFrame = CFrame.new(-70.7470474, 13.1006594, 24.1488132, -0.0153169353, 0.469224393, 0.882945836, 0.00813866127, 0.883078873, -0.469153851, -0.999849558, 0, -0.0173449218)
- Part1.BottomSurface = Enum.SurfaceType.Smooth
- Part1.TopSurface = Enum.SurfaceType.Smooth
- Part1.Position = Vector3.new(-70.7470474, 13.1006594, 24.1488132)
- Part1.Orientation = Vector3.new(27.9799995, 91.1299973, 0.529999971)
- SpecialMesh2.Parent = Part1
- SpecialMesh2.MeshId = "http://www.roblox.com/asset/?id=94690054"
- SpecialMesh2.TextureId = "http://www.roblox.com/asset/?id=94689966"
- SpecialMesh2.MeshType = Enum.MeshType.FileMesh
- Sound3.Name = "ReloadSound"
- Sound3.Parent = Part1
- Sound3.Pitch = 1.1000000238419
- Sound3.SoundId = "http://www.roblox.com/Asset?ID=132456167"
- Sound3.Volume = 1
- Sound4.Name = "FireSound"
- Sound4.Parent = Part1
- Sound4.SoundId = "http://www.roblox.com/Asset?ID=132456187"
- Sound4.Volume = 1
- LocalScript5.Name = "MouseIcon"
- LocalScript5.Parent = Tool0
- table.insert(cors,sandbox(LocalScript5,function()
- local MOUSE_ICON = 'rbxasset://textures/GunCursor.png'
- local RELOADING_ICON = 'rbxasset://textures/GunWaitCursor.png'
- local Tool = script.Parent
- local Mouse = nil
- local function UpdateIcon()
- Mouse.Icon = Tool.Enabled and MOUSE_ICON or RELOADING_ICON
- end
- local function OnEquipped(mouse)
- Mouse = mouse
- UpdateIcon()
- end
- local function OnChanged(property)
- if property == 'Enabled' then
- UpdateIcon()
- end
- end
- Tool.Equipped:connect(OnEquipped)
- Tool.Changed:connect(OnChanged)
- end))
- Script6.Name = "Launcher"
- Script6.Parent = Tool0
- table.insert(cors,sandbox(Script6,function()
- -----------------
- --| Constants |--
- -----------------
- local COOLDOWN = 4 -- Seconds until tool can be used again
- -- RocketPropulsion Fields
- local TARGET_RADIUS = 5
- local MAX_SPEED = 95
- local MAX_TORQUE = Vector3.new(4e6, 4e6, 0)
- local MAX_THRUST = 50000
- local THRUST_P = 500
- local THRUST_D = 50000
- local TARGET_OVERSHOOT_DISTANCE = 10000000
- local ROCKET_MESH_ID = 'http://www.roblox.com/asset/?id=94690081'
- local ROCKET_MESH_SCALE = Vector3.new(2.5, 2.5, 2)
- local ROCKET_PART_SIZE = Vector3.new(1, 1, 4)
- --------------------
- --| WaitForChild |--
- --------------------
- -- Waits for parent.child to exist, then returns it
- local function WaitForChild(parent, childName)
- assert(parent, "ERROR: WaitForChild: parent is nil")
- while not parent:FindFirstChild(childName) do parent.ChildAdded:wait() end
- return parent[childName]
- end
- -----------------
- --| Variables |--
- -----------------
- local DebrisService = Game:GetService('Debris')
- local PlayersService = Game:GetService('Players')
- local Tool = script.Parent
- local ToolHandle = Tool.Handle
- local RocketScript = WaitForChild(script, 'Rocket')
- local SwooshSound = WaitForChild(script, 'Swoosh')
- local BoomSound = WaitForChild(script, 'Boom')
- local ReloadSound = WaitForChild(ToolHandle, 'ReloadSound')
- local FireSound = WaitForChild(ToolHandle, 'FireSound')
- local MyModel = nil
- local MyPlayer = nil
- local BaseRocket = nil
- local RocketClone = nil
- -----------------
- --| Functions |--
- -----------------
- local function MakeBaseRocket()
- -- Set up the rocket part
- local rocket = Instance.new('Part')
- rocket.Name = 'Rocket'
- rocket.FormFactor = Enum.FormFactor.Custom --NOTE: This must be done before changing Size
- rocket.Size = ROCKET_PART_SIZE
- rocket.CanCollide = false
- rocket.BottomSurface = Enum.SurfaceType.Smooth
- rocket.TopSurface = Enum.SurfaceType.Smooth
- -- Add the mesh
- local mesh = Instance.new('SpecialMesh', rocket)
- mesh.MeshId = ROCKET_MESH_ID
- mesh.Scale = ROCKET_MESH_SCALE
- mesh.TextureId = ToolHandle.Mesh.TextureId
- -- Add fire
- local fire = Instance.new('Fire', rocket)
- fire.Heat = 3
- fire.Size = 2
- -- Add the propulsion
- local rocketPropulsion = Instance.new('RocketPropulsion', rocket)
- rocketPropulsion.CartoonFactor = 1
- rocketPropulsion.TargetRadius = TARGET_RADIUS
- rocketPropulsion.MaxSpeed = MAX_SPEED
- rocketPropulsion.MaxTorque = MAX_TORQUE
- rocketPropulsion.MaxThrust = MAX_THRUST
- rocketPropulsion.ThrustP = THRUST_P
- rocketPropulsion.ThrustD = THRUST_D
- -- Clone the sounds
- local swooshSoundClone = SwooshSound:Clone()
- swooshSoundClone.Parent = rocket
- local boomSoundClone = BoomSound:Clone()
- boomSoundClone.Parent = rocket
- -- Attach creator tags
- local creatorTag = Instance.new('ObjectValue', rocket)
- creatorTag.Name = 'creator' --NOTE: Must be called 'creator' for website stats
- creatorTag.Value = MyPlayer
- local iconTag = Instance.new('StringValue', creatorTag)
- iconTag.Name = 'icon'
- iconTag.Value = Tool.TextureId
- -- Finally, clone the rocket script and enable it
- local rocketScriptClone = RocketScript:Clone()
- rocketScriptClone.Parent = rocket
- rocketScriptClone.Disabled = false
- return rocket
- end
- local function OnEquipped()
- MyModel = Tool.Parent
- MyPlayer = PlayersService:GetPlayerFromCharacter(MyModel)
- BaseRocket = MakeBaseRocket()
- RocketClone = BaseRocket:Clone()
- end
- local function OnActivated(byFireButton)
- if Tool.Enabled and MyModel and MyModel:FindFirstChild('Humanoid') and MyModel.Humanoid.Health > 0 then
- Tool.Enabled = false
- -- Get the target position
- local targetPosition = MyModel.Humanoid.TargetPoint
- if byFireButton then -- Using Fire Button, shoot forwards
- targetPosition = MyModel.Humanoid.Torso.CFrame.lookVector * 1000
- end
- -- Position the rocket clone
- local spawnPosition = ToolHandle.Position + (ToolHandle.CFrame.lookVector * (ToolHandle.Size.z / 2))
- RocketClone.CFrame = CFrame.new(spawnPosition, targetPosition) --NOTE: This must be done before assigning Parent
- DebrisService:AddItem(RocketClone, 30)
- RocketClone.Parent = Workspace
- -- Assign target and launch!
- FireSound:Play()
- local rocketPropulsion = RocketClone:FindFirstChild('RocketPropulsion')
- if rocketPropulsion then
- local direction = (targetPosition - RocketClone.Position).unit
- rocketPropulsion.TargetOffset = RocketClone.Position + (direction * TARGET_OVERSHOOT_DISTANCE)
- rocketPropulsion:Fire()
- end
- wait(0) --TODO: Remove when sounds realize they can be played as soon as they enter the Workspace
- -- Swoosh!
- local swooshSound = RocketClone:FindFirstChild('Swoosh')
- if swooshSound then
- swooshSound:Play()
- end
- -- Prepare the next rocket to be fired
- RocketClone = BaseRocket:Clone()
- ReloadSound:Play()
- wait(COOLDOWN)
- -- Stop the reloading sound if it hasn't already finished
- ReloadSound:Stop()
- Tool.Enabled = true
- end
- end
- local function OnUnequipped()
- ReloadSound:Stop() --TODO: This does not work online
- end
- -- Also activate when the Fire Button is down
- local function OnChildAdded(child)
- if child.Name == 'FireButtonDown' then
- child.Changed:connect(function(newValue)
- if newValue == true then
- OnActivated(true)
- end
- end)
- end
- end
- --------------------
- --| Script Logic |--
- --------------------
- Tool.Equipped:connect(OnEquipped)
- Tool.Activated:connect(OnActivated)
- Tool.Unequipped:connect(OnUnequipped)
- Tool.ChildAdded:connect(OnChildAdded) --NOTE: Added for Fire Button
- end))
- Sound7.Name = "Boom"
- Sound7.Parent = Script6
- Sound7.SoundId = "http://www.roblox.com/Asset?ID=133680244"
- Sound7.Volume = 1
- Sound8.Name = "Swoosh"
- Sound8.Parent = Script6
- Sound8.Pitch = 1.2999999523163
- Sound8.SoundId = "rbxasset://sounds/Rocket whoosh 01.wav"
- Sound8.Volume = 0.69999998807907
- Sound8.Looped = true
- Script9.Name = "Rocket"
- Script9.Parent = Script6
- Script9.Disabled = true
- table.insert(cors,sandbox(Script9,function()
- -----------------
- --| Constants |--
- -----------------
- local BLAST_RADIUS = 6
- local BLAST_PRESSURE = 750000
- -- Rocket will fly through things named these
- local ROCKET_IGNORE_LIST = {rocket = 1, handle = 1, effect = 1, water = 1} --NOTE: Keys must be lowercase, values must evaluate to true
- --------------------
- --| WaitForChild |--
- --------------------
- -- Waits for parent.child to exist, then returns it
- local function WaitForChild(parent, childName)
- assert(parent, "ERROR: WaitForChild: parent is nil")
- while not parent:FindFirstChild(childName) do parent.ChildAdded:wait() end
- return parent[childName]
- end
- -----------------
- --| Variables |--
- -----------------
- local DebrisService = Game:GetService('Debris')
- local Rocket = script.Parent
- local CreatorTag = WaitForChild(Rocket, 'creator')
- local Connection = nil
- -----------------
- --| Functions |--
- -----------------
- -- Returns the ancestor that contains a Humanoid, if it exists
- local function FindCharacterAncestor(subject)
- if subject and subject ~= Workspace then
- local humanoid = subject:FindFirstChild('Humanoid')
- if humanoid then
- return subject, humanoid
- else
- return FindCharacterAncestor(subject.Parent)
- end
- end
- return nil
- end
- local function OnExplosionHit(hitPart)
- if hitPart then
- local _, humanoid = FindCharacterAncestor(hitPart.Parent)
- if humanoid and humanoid.Health > 0 then
- local hitBindable = humanoid:FindFirstChild('Hit')
- if hitBindable then
- hitBindable:Invoke(0, CreatorTag)
- else
- print("Could not find BindableFunction 'Hit'")
- end
- end
- end
- end
- local function OnTouched(otherPart)
- if Rocket and otherPart then
- -- Fly through anything in the ignore list
- if ROCKET_IGNORE_LIST[string.lower(otherPart.Name)] then
- return
- end
- -- Fly through the creator
- local myPlayer = CreatorTag.Value
- if myPlayer and myPlayer.Character and myPlayer.Character:IsAncestorOf(otherPart) then
- return
- end
- -- Create the explosion
- local explosion = Instance.new('Explosion')
- explosion.BlastPressure = BLAST_PRESSURE
- explosion.BlastRadius = BLAST_RADIUS
- explosion.Position = Rocket.Position
- explosion.Hit:connect(OnExplosionHit)
- explosion.Parent = Workspace
- -- Start playing the boom sound
- local boomSound = Rocket:FindFirstChild('Boom')
- if boomSound then
- boomSound:Play()
- end
- -- NOTE:
- -- If we just destroyed the rocket at this point, the boom sound would be destroyed too,
- -- so instead we will hide the rocket, keep it in the same spot, and schedule it for deletion
- -- Stop playing the swoosh sound
- local swooshSound = Rocket:FindFirstChild('Swoosh')
- if swooshSound then
- swooshSound:Stop()
- end
- -- Put out the fire
- local fire = Rocket:FindFirstChild('Fire')
- if fire then
- fire:Destroy()
- end
- Rocket.Transparency = 1
- Rocket.CanCollide = false
- Rocket.Anchored = true
- DebrisService:AddItem(Rocket, 3)
- -- Destroy the connection so this method won't be called again
- Connection:disconnect()
- end
- end
- --------------------
- --| Script Logic |--
- --------------------
- -- Arm the rocket and save the touch connection so we can disconnect it later
- Connection = Rocket.Touched:connect(OnTouched)
- end))
- LocalScript10.Name = "Animation"
- LocalScript10.Parent = Tool0
- table.insert(cors,sandbox(LocalScript10,function()
- --------------------
- --| WaitForChild |--
- --------------------
- -- Waits for parent.child to exist, then returns it
- local function WaitForChild(parent, childName)
- assert(parent, "ERROR: WaitForChild: parent is nil")
- while not parent:FindFirstChild(childName) do parent.ChildAdded:wait() end
- return parent[childName]
- end
- -----------------
- --| Variables |--
- -----------------
- local Tool = script.Parent
- local FireAndReloadAnimation = WaitForChild(script, 'FireAndReload')
- local FireAndReloadTrack = nil
- -----------------
- --| Functions |--
- -----------------
- local function OnEquipped()
- local myModel = Tool.Parent
- local humanoid = myModel:FindFirstChild('Humanoid')
- if humanoid then -- Preload animations
- FireAndReloadTrack = humanoid:LoadAnimation(FireAndReloadAnimation)
- end
- end
- local function OnChanged(property)
- if property == 'Enabled' and Tool.Enabled == false then
- -- Play fire and reload animation
- if FireAndReloadTrack then
- FireAndReloadTrack:Play()
- end
- end
- end
- local function OnUnequipped()
- -- Stop animations
- if FireAndReloadTrack then FireAndReloadTrack:Stop() end
- end
- --------------------
- --| Script Logic |--
- --------------------
- Tool.Equipped:connect(OnEquipped)
- Tool.Changed:connect(OnChanged)
- Tool.Unequipped:connect(OnUnequipped)
- end))
- Animation11.Name = "FireAndReload"
- Animation11.Parent = LocalScript10
- Animation11.AnimationId = "http://www.roblox.com/Asset?ID=94771598"
- LocalScript12.Name = "VisualizeReload"
- LocalScript12.Parent = Tool0
- table.insert(cors,sandbox(LocalScript12,function()
- -----------------
- --| Constants |--
- -----------------
- local ROCKET_MESH_ID = ''
- local ROCKET_MESH_SCALE = Vector3.new(1, 1, 1)
- local ANIM_TOTAL_TIME = 3.4 -- Total length of FireAndReload animation
- local ROCKET_SHOW_TIME = 1.5 -- Seconds after animation begins to show the rocket
- local ROCKET_HIDE_TIME = 2.2 -- Seconds after animation begins to hide the rocket
- -----------------
- --| Variables |--
- -----------------
- local Tool = script.Parent
- local ToolHandle = Tool.Handle
- local MyModel = nil
- local ReloadRocket = nil
- local StillEquipped = false
- -----------------
- --| Functions |--
- -----------------
- local function MakeReloadRocket()
- local reloadRocket = Instance.new('Part')
- reloadRocket.Name = "Ammo"
- reloadRocket.Transparency = 1
- reloadRocket.FormFactor = Enum.FormFactor.Custom --NOTE: This must be done before changing Size
- reloadRocket.Size = Vector3.new() -- As small as possible
- local mesh = Instance.new('SpecialMesh', reloadRocket)
- mesh.MeshId = ROCKET_MESH_ID
- mesh.Scale = ROCKET_MESH_SCALE
- mesh.TextureId = ToolHandle.Mesh.TextureId
- return reloadRocket
- end
- local function OnEquipped()
- MyModel = Tool.Parent
- ReloadRocket = MakeReloadRocket()
- end
- local function OnChanged(property)
- if property == 'Enabled' and Tool.Enabled == false then
- -- Show the next rocket going into the launcher
- StillEquipped = true
- wait(ROCKET_SHOW_TIME)
- if StillEquipped then
- local leftArm = MyModel:FindFirstChild('Left Arm')
- if leftArm then
- local weld = ReloadRocket:FindFirstChild('Weld')
- if not weld then
- weld = Instance.new('Weld')
- weld.Part0 = leftArm
- weld.Part1 = ReloadRocket
- weld.C1 = CFrame.new(Vector3.new(0, 1, 0))
- weld.Parent = ReloadRocket
- end
- ReloadRocket.Parent = MyModel
- end
- wait(ROCKET_HIDE_TIME - ROCKET_SHOW_TIME)
- if StillEquipped and ReloadRocket.Parent == MyModel then
- ReloadRocket.Parent = nil
- end
- end
- end
- end
- local function OnUnequipped()
- StillEquipped = false
- ReloadRocket:Destroy()
- ReloadRocket = nil
- end
- --------------------
- --| Script Logic |--
- --------------------
- Tool.Equipped:connect(OnEquipped)
- Tool.Changed:connect(OnChanged)
- Tool.Unequipped:connect(OnUnequipped)
- end))
- Part13.Name = "RPG-7"
- Part13.Parent = Tool0
- Part13.Rotation = Vector3.new(92.1199951, 62, -91.8699951)
- Part13.CanCollide = false
- Part13.FormFactor = Enum.FormFactor.Symmetric
- Part13.Size = Vector3.new(1, 1, 4)
- Part13.CFrame = CFrame.new(-71.2503738, 13.141613, 24.1718769, -0.0153171355, 0.469224393, 0.882945538, 0.00813870504, 0.883078873, -0.469153672, -0.99984926, -5.7471425e-08, -0.0173451193)
- Part13.BottomSurface = Enum.SurfaceType.Smooth
- Part13.TopSurface = Enum.SurfaceType.Smooth
- Part13.Position = Vector3.new(-71.2503738, 13.141613, 24.1718769)
- Part13.Orientation = Vector3.new(27.9799995, 91.1299973, 0.529999971)
- SpecialMesh14.Parent = Part13
- SpecialMesh14.MeshId = "http://www.roblox.com/asset/?id=88742477"
- SpecialMesh14.Scale = Vector3.new(2.5, 2.5, 2.5)
- SpecialMesh14.TextureId = "http://www.roblox.com/asset/?id=88745396"
- SpecialMesh14.MeshType = Enum.MeshType.FileMesh
- SpecialMesh14.Scale = Vector3.new(2.5, 2.5, 2.5)
- Script15.Name = "UltimateWeld"
- Script15.Parent = Tool0
- table.insert(cors,sandbox(Script15,function()
- --DO NOT USE BOTH WELDING SCRIPTS PROVIDED BY THIS MODEL
- --The regular script is recommended
- --[[
- Prevents welds from breaking/transforming when player uses the tool
- This recreates the EXACT weld every time
- This also prevents lag build up by clearing old welds, the tradition weld script just keeps making new
- ones, which can lead to weapons have crazy amounts of welds that dont work (I saw 6000 in a weapon once)
- ]]
- --[[Usage
- 1. Remove Old welding script (optional, only if updating a weapon and that weapon does not rely on that script)
- 2. Anchor all parts of tool and put inside of a tool object
- 3. Place this script in that tool (make sure you do this AFTER step 2, otherwise it may fail)
- 4. Treat like normal tool, nothing special has to be done with it
- ]]
- --[[The local script included in this model can only be used if
- 1. The weapon is being placed in the players backpack first (i.e. the weapon is in starterpack and moves to player backpack)
- OR
- 2. The weapon is previously welded (weapon can be placed in workspace and picked up then)
- ]]
- repeat wait() until script.Parent:FindFirstChild("Handle")
- local welds={}
- function ClearOldWelds(tbl)
- for _,v in pairs(tbl) do
- if v:IsA('Weld') then
- v:Destroy()
- end
- end
- end
- function Equipped()
- local handle=script.Parent:FindFirstChild('Handle')
- if not handle then return end
- local tble=handle:GetChildren()
- for _,v in pairs(script.Parent:GetChildren()) do
- if v:IsA('BasePart') and v~=handle then
- local c1
- for _1,v1 in pairs(welds) do
- if _1==v then
- c1=v1
- break
- end
- end
- if not c1 then
- welds[v]=v.CFrame:inverse()*handle.CFrame
- v.Anchored=false
- c1=welds[v]
- end
- local weld=Instance.new('Weld')
- weld.Part0=handle
- weld.Part1=v
- weld.C0=CFrame.new()
- weld.C1=c1
- weld.Parent=handle
- end
- end
- ClearOldWelds(tble)
- handle.Anchored=false
- end
- Equipped()
- script.Parent.Equipped:connect(Equipped)
- --Made by DonnyTheDemented
- end))
- Script16.Name = "Welding"
- Script16.Parent = Tool0
- table.insert(cors,sandbox(Script16,function()
- function Weld(x,y)
- local W = Instance.new("Weld")
- W.Part0 = x
- W.Part1 = y
- local CJ = CFrame.new(x.Position)
- local C0 = x.CFrame:inverse()*CJ
- local C1 = y.CFrame:inverse()*CJ
- W.C0 = C0
- W.C1 = C1
- W.Parent = x
- end
- function Get(A)
- if A.className == "Part" then
- Weld(script.Parent.Handle, A)
- A.Anchored = false
- else
- local C = A:GetChildren()
- for i=1, #C do
- Get(C[i])
- end
- end
- end
- function Finale()
- Get(script.Parent)
- end
- script.Parent.Equipped:connect(Finale)
- script.Parent.Unequipped:connect(Finale)
- Finale()
- end))
- LocalScript17.Name = "BackupWeld"
- LocalScript17.Parent = Tool0
- table.insert(cors,sandbox(LocalScript17,function()
- function Weld(x,y)
- local W = Instance.new("Weld")
- W.Part0 = x
- W.Part1 = y
- local CJ = CFrame.new(x.Position)
- local C0 = x.CFrame:inverse()*CJ
- local C1 = y.CFrame:inverse()*CJ
- W.C0 = C0
- W.C1 = C1
- W.Parent = x
- end
- function Get(A)
- if A.className == "Part" then
- Weld(script.Parent.Handle, A)
- A.Anchored = false
- else
- local C = A:GetChildren()
- for i=1, #C do
- Get(C[i])
- end
- end
- end
- function Finale()
- Get(script.Parent)
- end
- script.Parent.Equipped:connect(Finale)
- script.Parent.Unequipped:connect(Finale)
- Finale()
- end))
- for i,v in pairs(mas:GetChildren()) do
- v.Parent = game:GetService("Players").LocalPlayer.Backpack
- pcall(function() v:MakeJoints() end)
- end
- mas:Destroy()
- for i,v in pairs(cors) do
- spawn(function()
- pcall(v)
- end)
- end
Add Comment
Please, Sign In to add comment