mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
94
lua/entities/ent_musical_keyboard/cl_init.lua
Normal file
94
lua/entities/ent_musical_keyboard/cl_init.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
include( "shared.lua" )
|
||||
|
||||
local blackKeys = {
|
||||
[1] = true, [3] = true, [6] = true, [8] = true, [10] = true
|
||||
}
|
||||
|
||||
local keyColors = {
|
||||
default = Color( 255, 148, 77 ),
|
||||
automated = Color( 171, 0, 197 )
|
||||
}
|
||||
|
||||
local keyOffsets = {
|
||||
[2] = 0.2,
|
||||
[4] = 0.4,
|
||||
[5] = -0.1,
|
||||
[7] = 0.1,
|
||||
[9] = 0.4,
|
||||
[11] = 0.6
|
||||
}
|
||||
|
||||
local Remap = math.Remap
|
||||
local RealTime = RealTime
|
||||
|
||||
function ENT:Initialize()
|
||||
self.drawNotes = {}
|
||||
end
|
||||
|
||||
function ENT:EmitNote( note, velocity, instrument, automated )
|
||||
local data = MKeyboard.instruments[instrument]
|
||||
|
||||
if not data then return end
|
||||
if note < data.noteMin or note > data.noteMax then return end
|
||||
|
||||
sound.Play( string.format( data.path, note ), self:GetPos(), 80, 100, velocity / 127 )
|
||||
|
||||
local idx = note % 12
|
||||
local len = 8
|
||||
local height = -0.2
|
||||
local width = 1.6
|
||||
local x = -1.1
|
||||
|
||||
if blackKeys[idx] then
|
||||
len = 5
|
||||
height = 0.1
|
||||
width = 1
|
||||
x = -0.6
|
||||
end
|
||||
|
||||
if keyOffsets[idx] then
|
||||
x = x + keyOffsets[idx]
|
||||
end
|
||||
|
||||
self.drawNotes[note] = {
|
||||
x = Remap( note, 21, 108, -37, 36.7 ),
|
||||
t = RealTime() + 0.2,
|
||||
min = Vector( x, -1.5, -1 ),
|
||||
max = Vector( x + width, len, height ),
|
||||
color = keyColors[automated and "automated" or "default"]
|
||||
}
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
self:DrawModel()
|
||||
|
||||
local t = RealTime()
|
||||
local ang = self:GetAngles()
|
||||
|
||||
render.SetColorMaterial()
|
||||
|
||||
for note, p in pairs( self.drawNotes ) do
|
||||
if t > p.t then
|
||||
self.drawNotes[note] = nil
|
||||
else
|
||||
local color = p.color
|
||||
local alpha = 255 * ( ( p.t - t ) / 0.2 )
|
||||
|
||||
render.DrawBox(
|
||||
self:LocalToWorld( Vector( -p.x, 0, 0 ) ),
|
||||
ang, p.min, p.max,
|
||||
Color( color.r, color.g, color.b, alpha )
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
197
lua/entities/ent_musical_keyboard/init.lua
Normal file
197
lua/entities/ent_musical_keyboard/init.lua
Normal file
@@ -0,0 +1,197 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
AddCSLuaFile( "shared.lua" )
|
||||
include( "shared.lua" )
|
||||
|
||||
local MAX_USE_DISTANCE = 300 * 300
|
||||
|
||||
local function MakeKeyboardSpawner( ply, data )
|
||||
if IsValid( ply ) and not ply:CheckLimit( "musical_keyboards" ) then return end
|
||||
|
||||
local ent = ents.Create( data.Class )
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
ent:SetPos( data.Pos )
|
||||
ent:SetAngles( data.Angle )
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
ply:AddCount( "musical_keyboards", ent )
|
||||
|
||||
return ent
|
||||
end
|
||||
|
||||
duplicator.RegisterEntityClass( "ent_musical_keyboard", MakeKeyboardSpawner, "Data" )
|
||||
|
||||
function ENT:SpawnFunction( ply, tr )
|
||||
if tr.Hit then
|
||||
return MakeKeyboardSpawner( ply, {
|
||||
Pos = tr.HitPos,
|
||||
Angle = Angle( 0, ply:EyeAngles().y + 90, 0 ),
|
||||
Class = self.ClassName
|
||||
} )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel( "models/styledstrike/musical_keyboard.mdl" )
|
||||
self:PhysicsInit( SOLID_VPHYSICS )
|
||||
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||||
self:SetSolid( SOLID_VPHYSICS )
|
||||
self:SetUseType( SIMPLE_USE )
|
||||
self:DrawShadow( true )
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid( phys ) then phys:Wake() end
|
||||
|
||||
if WireLib then
|
||||
WireLib.CreateSpecialOutputs( self, { "NotePlayed" }, { "ARRAY" }, {
|
||||
[[Triggered when the user played a note.
|
||||
This array contains:
|
||||
[1] Note number,
|
||||
[2] Note velocity
|
||||
[3] Instrument index (The number near each name on the instruments list)]]
|
||||
} )
|
||||
|
||||
WireLib.CreateSpecialInputs( self, { "PlayNote" }, { "ARRAY" }, {
|
||||
[[Changing this input will play a note.
|
||||
The array should contain:
|
||||
[1] Note number (1-127)
|
||||
[2] Note velocity (1-127)
|
||||
[3] Instrument index (The number near each name on the instruments list)]]
|
||||
} )
|
||||
|
||||
self.reproduceQueue = {}
|
||||
self.transmitQueue = {}
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Use( ply )
|
||||
self:SetPlayer( ply )
|
||||
end
|
||||
|
||||
function ENT:SetPlayer( ply )
|
||||
if IsValid( self.Ply ) and not self.Ply:Alive() then
|
||||
self.Ply = nil
|
||||
end
|
||||
|
||||
if not IsValid( self.Ply ) and ply:Alive() then
|
||||
net.Start( "mkeyboard.set_current_keyboard", false )
|
||||
net.WriteEntity( self )
|
||||
net.Send( ply )
|
||||
|
||||
self.Ply = ply
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:RemovePlayer()
|
||||
if IsValid( self.Ply ) then
|
||||
net.Start( "mkeyboard.set_current_keyboard", false )
|
||||
net.WriteEntity( nil )
|
||||
net.Send( self.Ply )
|
||||
end
|
||||
|
||||
self.Ply = nil
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
self:NextThink( CurTime() )
|
||||
self:UpdateNotes()
|
||||
|
||||
if IsValid( self.Ply ) and (
|
||||
not self.Ply:Alive() or
|
||||
self.Ply:GetPos():DistToSqr( self:GetPos() ) > MAX_USE_DISTANCE
|
||||
) then
|
||||
self:RemovePlayer()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if not WireLib then
|
||||
function ENT:UpdateNotes() end
|
||||
function ENT:OnReceiveNotes() end
|
||||
return
|
||||
end
|
||||
|
||||
local function ValidateNumber( v, default, min, max )
|
||||
return math.Clamp( math.Round( tonumber( v ) or default ), min, max )
|
||||
end
|
||||
|
||||
function ENT:TriggerInput( name, value )
|
||||
if name ~= "PlayNote" then return end
|
||||
if not isnumber( value[1] ) then return end
|
||||
|
||||
local note = ValidateNumber( value[1], 0, 0, 127 )
|
||||
if note == 0 then return end
|
||||
|
||||
local velocity = ValidateNumber( value[2], 127, 1, 127 )
|
||||
local instrument = ValidateNumber( value[3], 1, 1, 127 ) -- Max. value is based on net.WriteUInt( 7 )
|
||||
|
||||
local queue = self.transmitQueue
|
||||
|
||||
-- Remember when we started putting notes
|
||||
-- on the queue, and when we should send them
|
||||
local t = SysTime()
|
||||
|
||||
if not self.queueTimer then
|
||||
self.queueTimer = t + 0.4
|
||||
self.queueStart = t
|
||||
end
|
||||
|
||||
-- Add notes to the queue unless the limit was reached
|
||||
local noteCount = #queue
|
||||
|
||||
if noteCount < MKeyboard.NET_MAX_NOTES then
|
||||
queue[noteCount + 1] = {
|
||||
note, velocity, instrument, t - self.queueStart
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local SysTime = SysTime
|
||||
|
||||
function ENT:OnReceiveNotes( notes )
|
||||
local t = SysTime()
|
||||
local queue = self.reproduceQueue
|
||||
|
||||
for i, n in ipairs( notes ) do
|
||||
-- i * 0.01 to prevent overriding stuff already on the queue
|
||||
queue[t + n[4] + ( i * 0.01 )] = { n[1], n[2], n[3] }
|
||||
end
|
||||
end
|
||||
|
||||
local GetKeys = table.GetKeys
|
||||
|
||||
function ENT:UpdateNotes()
|
||||
local now = SysTime()
|
||||
|
||||
-- If the queued notes are ready to be sent...
|
||||
if self.queueTimer and now > self.queueTimer then
|
||||
MKeyboard.BroadcastNotes( self.transmitQueue, self, true )
|
||||
|
||||
table.Empty( self.transmitQueue )
|
||||
self.queueTimer = nil
|
||||
end
|
||||
|
||||
-- Trigger the wire outputs while taking the time offsets into account
|
||||
local queue = self.reproduceQueue
|
||||
local timestamps = GetKeys( queue )
|
||||
|
||||
for _, t in ipairs( timestamps ) do
|
||||
if now > t then
|
||||
local n = queue[t]
|
||||
WireLib.TriggerOutput( self, "NotePlayed", { n[1], n[2], n[3] } )
|
||||
queue[t] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
22
lua/entities/ent_musical_keyboard/shared.lua
Normal file
22
lua/entities/ent_musical_keyboard/shared.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
ENT.Base = "base_anim"
|
||||
ENT.Type = "anim"
|
||||
|
||||
ENT.PrintName = "Musical Keyboard"
|
||||
ENT.Author = "StyledStrike"
|
||||
ENT.Contact = "StyledStrike#8032"
|
||||
ENT.Purpose = "Replacement for Playable Piano, with improvements"
|
||||
ENT.Instructions = "Press E to use the instrument, use the keyboard or a MIDI device (if available) to play."
|
||||
|
||||
ENT.Category = "Fun + Games"
|
||||
ENT.Spawnable = true
|
||||
ENT.AdminOnly = false
|
||||
Reference in New Issue
Block a user