I just accidentally found out how to do it without any mods or extra texture packs, Just 1 command.
First make your gun model replace a crossbow instead of a carrot on a stick so that you have the aim animation when the player holds the weapon and insert this command in a repeating command block in your world:
/replaceitem entity @p[nbt={SelectedItem:{id:"minecraft:crossbow",tag:{Charged:0b,Gun:1}}}] weapon.mainhand crossbow{ChargedProjectiles:[{id:"minecraft:arrow{disappearBug:1}",Count:1b}],Charged:1b,Gun:1}
This replaces a non charged crossbow with the Gun:1 tag with a loaded crossbow with a bugged arrow that won't appear when shot. The crossbow will activate scoreboards and will immediately reload making it the same as a carrot on a stick
The bugged arrow won't appear, make any noise or spawn particles
Give command for the crossbow (you can get it normally but you would have to shoot an arrow once to activate the above command without this):
/give @p minecraft:crossbow{ChargedProjectiles:[{id:"minecraft:arrow{disappearBug:1}",Count:1b}],Charged:1b,Gun:1}
This was tested in minecraft 1.16 but should work anywhere there is a crossbow
Btw you texture a crossbow a different way than you do a normal item so google that when you do change the models to crossbows (along with other things you are going to have to get 2 models one for the charged crossbow and a near copy for the normal crossbow, but you can make the normal crossbow one to simulate the recoil which makes it even better)