This commit is contained in:
lifestorm
2024-08-04 23:12:27 +03:00
parent 0e770b2b49
commit ba1fc01b16
7084 changed files with 2173495 additions and 14 deletions

View File

@@ -0,0 +1,141 @@
--[[
| 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/
--]]
local PLUGIN = PLUGIN
PLUGIN.targetedLandlineEndpointID = nil
PLUGIN.targetedLandlinePBX = nil
PLUGIN.targetedLandlineExt = nil
PLUGIN.targetedLandlineName = nil
do
surface.CreateFont("phoneMonoDebug", {
font = "Consolas",
size = 16,
extended = true,
weight = 1200
})
end
function PLUGIN:InitializedPlugins()
local color = Color(255, 0, 255)
local function drawConnectorESP(client, entity, x, y, factor)
ix.util.DrawText(
"Landline Phone",
x,
y - math.max(10, 32 * factor),
color,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_CENTER,
nil,
math.max(255 * factor, 80)
)
end
ix.observer:RegisterESPType("landline_phone", drawConnectorESP, "Landline Phone")
end
net.Receive("EnterLandlineDial", function()
PLUGIN.targetedLandlineEndpointID = net.ReadInt(15)
PLUGIN.targetedLandlinePBX = net.ReadInt(5)
PLUGIN.targetedLandlineExt = net.ReadInt(11)
PLUGIN.targetedLandlineName = net.ReadString()
PLUGIN.landlineEntIdx = net.ReadInt(17)
if (PLUGIN.panel) then
LocalPlayer():Notify("Obecnie używasz telefonu!")
else
vgui.Create("ixLandlineDial")
end
end)
net.Receive("ixConnectedCallStatusChange", function()
local active = net.ReadBool()
local inCall = net.ReadBool()
if (active) then
PLUGIN.offHook = true
end
if (active and inCall) then
PLUGIN.otherSideActive = true
PLUGIN.otherSideRinging = false
PLUGIN.currentCallStatus = ""
net.Start("RunGetPeerName")
net.SendToServer()
timer.Simple(0.25, function()
-- wait for the vgui panel to come up (if we're picking up in this state)
LocalPlayer():StopSound("landline_ringtone.wav")
LocalPlayer():StopSound("landline_dialtone.wav")
end)
else
PLUGIN.otherSideRinging = false
PLUGIN.otherSideActive = false
PLUGIN.currentCallStatus = "OCZEKIWANIE"
PLUGIN.currentCallPeerName = "Nieznany"
if (PLUGIN.panel) then
LocalPlayer():StopSound("landline_dialtone.wav")
LocalPlayer():EmitSound("landline_dialtone.wav", 40, 100, 1, CHAN_STATIC)
end
end
end)
net.Receive("OnGetPeerName", function()
PLUGIN.currentCallPeerName = net.ReadString()
end)
net.Receive("LineTestChat", function()
local text = net.ReadString()
local chatIcon = ix.util.GetMaterial("willardnetworks/chat/message_icon.png")
local color = Color(105, 157, 178)
local formattedText = string.format("[PHONE] %s: \"%s\"", "Test linii", text or "Błąd!")
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(chatIcon, color, formattedText)
else
chat.AddText(color, formattedText)
end
end)
net.Receive("LineStatusUpdate", function()
PLUGIN.currentLineStatus = net.ReadString()
if (PLUGIN.currentLineStatus ~= ix.phone.switch.DialStatus.Success and
PLUGIN.currentLineStatus ~= ix.phone.switch.DialStatus.DebugMode) then
LocalPlayer():StopSound("landline_ringtone.wav")
LocalPlayer():EmitSound("landline_dialtone.wav", 40, 100, 1, CHAN_STATIC)
elseif (PLUGIN.currentLineStatus == ix.phone.switch.DialStatus.LineBusy) then
LocalPlayer():EmitSound("landline_line_busy.wav", 40, 100, 1, CHAN_STATIC)
elseif (PLUGIN.currentLineStatus == ix.phone.switch.DialStatus.Success or
PLUGIN.currentLineStatus == ix.phone.switch.DialStatus.DebugMode) then
timer.Simple(0.25, function()
-- wait for the vgui panel to come up (if we're picking up in this state)
LocalPlayer():StopSound("landline_dialtone.wav")
end)
end
end)
net.Receive("ForceHangupLandlinePhone", function()
if (PLUGIN.panel and PLUGIN.panel.Close) then
PLUGIN.panel:Close()
PLUGIN.offHook = false
PLUGIN.otherSideRinging = false
PLUGIN.otherSideActive = false
PLUGIN.currentCallStatus = "OCZEKIWANIE"
PLUGIN.currentCallPeerName = "Nieznany"
net.Start("RunHangupLandline")
net.SendToServer()
end
end)

View File

@@ -0,0 +1,321 @@
--[[
| 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/
--]]
local PLUGIN = PLUGIN
PLUGIN.offHook = false
PLUGIN.otherSideRinging = false
PLUGIN.otherSideActive = false
PLUGIN.currentCallStatus = "OCZEKIWANIE"
PLUGIN.currentCallPeerName = "Nieznany"
PLUGIN.currentLineStatus = ""
local dialSeq = {}
local buttonMap = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"}
local PANEL = {}
function dialSeq:insert(signal)
self[#self+1] = signal
end
function dialSeq:reset()
for k, signal in ipairs(self) do
self[k] = nil
end
end
function dialSeq:asStr()
local dialSeqStr = ""
for _, signal in ipairs(self) do
dialSeqStr = dialSeqStr..signal
end
return dialSeqStr
end
function dialSeq:playDTMFTone(signal)
local _signal = signal
if (signal == "*") then _signal = "asterisk" end
if (signal == "#") then _signal = "pound" end
surface.PlaySound("dtmftones/dtmfgen_".._signal..".wav")
end
function PANEL:Init()
PLUGIN.currentLineStatus = ""
if (IsValid(PLUGIN.panel)) then
PLUGIN.panel:Remove()
end
self:SetSize(500, 780)
self:Center()
self:CenterHorizontal(0.6)
self:SetBackgroundBlur(false)
self:SetDeleteOnClose(true)
self:SetTitle(" ")
self:ShowCloseButton(false)
self:IsDraggable(true)
self.verStamp = self:Add("DLabel")
self.verStamp:Dock(TOP)
self.verStamp:DockMargin(0, 7, 55, 0)
self.verStamp:SetFont("DermaDefaultBold")
self.verStamp:SetHeight(30)
self.verStamp:SetColor(Color(215, 240, 231))
self.verStamp:SetText(L("UU pbxOS BETA v0.254ga - NODEID - "..tostring(PLUGIN.targetedLandlineEndpointID)))
self.verStamp:SetContentAlignment(9)
self.status = self:Add("DLabel")
self.status:Dock(TOP)
self.status:DockMargin(0, -10, 0, 0)
self.status:SetFont("DermaDefaultBold")
self.status:SetHeight(30)
self.status:SetColor(Color(215, 240, 231))
local _connStatus = "DISCONNECTED!"
local _curPBX = "N/A"
if (PLUGIN.targetedLandlinePBX and PLUGIN.targetedLandlinePBX > 0) then
_connStatus = "ACTIVE"
_curPBX = "0"..tostring(PLUGIN.targetedLandlinePBX)
end
self.status:SetText(L("Connection Status: ".._connStatus))
self.status:SetContentAlignment(5)
self.name = self:Add("DLabel")
self.name:Dock(TOP)
self.name:DockMargin(0, 4, 75, 0)
self.name:SetText(L(tostring(PLUGIN.targetedLandlineName)))
self.name:SetHeight(35)
self.name:SetFont("CloseCaption_Bold")
self.name:SetColor(Color(15, 13, 44, 220))
self.name:SetContentAlignment(9)
self.pBX = self:Add("DLabel")
self.pBX:Dock(TOP)
self.pBX:DockMargin(75, 4, 0, 0)
self.pBX:SetText(L("PBX :> ".._curPBX))
self.pBX:SetHeight(35)
self.pBX:SetFont("CloseCaption_Bold")
self.pBX:SetColor(Color(15, 13, 44, 220))
self.pBX:SetContentAlignment(7)
self.ext = self:Add("DLabel")
self.ext:Dock(TOP)
self.ext:DockMargin(75, 4, 0, 0)
self.ext:SetText(L("EXT :> "..tostring(PLUGIN.targetedLandlineExt)))
self.ext:SetHeight(35)
self.ext:SetFont("CloseCaption_Bold")
self.ext:SetColor(Color(15, 13, 44, 220))
self.ext:SetContentAlignment(7)
self.dialSeqText = self:Add("DLabel")
self.dialSeqText:Dock(TOP)
self.dialSeqText:DockMargin(75, 4, 0, 0)
self.dialSeqText:SetText(L("DIAL :> "))
self.dialSeqText:SetHeight(50)
self.dialSeqText:SetFont("CloseCaption_Bold")
self.dialSeqText:SetColor(Color(15, 13, 44, 220))
self.dialSeqText:SetContentAlignment(7)
local close = vgui.Create("DButton")
close:SetText(L("close"))
close:SetSize(70, 70)
close.DoClick = function()
dialSeq:playDTMFTone("#")
self:Close()
PLUGIN.offHook = false
PLUGIN.otherSideRinging = false
PLUGIN.otherSideActive = false
PLUGIN.currentCallStatus = "OCZEKIWANIE"
PLUGIN.currentCallPeerName = "Nieznany"
net.Start("RunHangupLandline")
net.SendToServer()
LocalPlayer():StopSound("landline_ringtone.wav")
LocalPlayer():StopSound("landline_dialtone.wav")
end
local dial = vgui.Create("DButton")
dial:SetText(L("Dial"))
dial:SetColor(Color(0, 255, 0))
dial:SetSize(70, 70)
dial.DoClick = function()
if (PLUGIN.otherSideRinging or PLUGIN.otherSideActive or
PLUGIN.currentCallStatus ~= "OCZEKIWANIE") then
return
end
net.Start("BeginDialToPeer")
net.WriteString(dialSeq:asStr())
net.WriteInt(PLUGIN.targetedLandlinePBX, 5)
net.WriteInt(PLUGIN.targetedLandlineExt, 11)
net.SendToServer()
net.Start("LandlineKeyPress")
net.WriteInt(PLUGIN.landlineEntIdx, 17)
net.SendToServer()
PLUGIN.offHook = true
PLUGIN.otherSideRinging = true
PLUGIN.otherSideActive = false
PLUGIN.currentCallStatus = "RINGING"
-- doing this instead of surface.PlaySound so we can stop it
LocalPlayer():EmitSound("landline_ringtone.wav", 40, 100, 1, CHAN_STATIC)
end
local reset = vgui.Create("DButton")
reset:SetText(L("Reset"))
reset:SetColor(Color(255, 0, 0))
reset:SetSize(70, 70)
reset.DoClick = function()
dialSeq:reset()
dialSeq:playDTMFTone("0")
self.dialSeqText:SetText(L("DIAL :> "))
net.Start("LandlineKeyPress")
net.WriteInt(PLUGIN.landlineEntIdx, 17)
net.SendToServer()
end
self.optsGrid = self:Add("DGrid")
self.optsGrid:SetPos(350, 400)
self.optsGrid:SetCols(1)
self.optsGrid:SetColWide(80)
self.optsGrid:SetRowHeight(80)
self.optsGrid:AddItem(reset)
self.optsGrid:AddItem(dial)
self.optsGrid:AddItem(close)
self.numberGrid = self:Add("DGrid")
self.numberGrid:SetPos(100, 400)
self.numberGrid:SetCols(3)
self.numberGrid:SetColWide(80)
self.numberGrid:SetRowHeight(80)
for _, key in ipairs(buttonMap) do
local button = vgui.Create("DButton")
button:SetText(key)
button:SetSize(70, 70)
button:SetFont("DermaLarge")
button.DoClick = function()
dialSeq:playDTMFTone(key)
dialSeq:insert(key)
self.dialSeqText:SetText(L("DIAL :> "..dialSeq:asStr()))
LocalPlayer():StopSound("landline_dialtone.wav")
net.Start("LandlineKeyPress")
net.WriteInt(PLUGIN.landlineEntIdx, 17)
net.SendToServer()
end
self.numberGrid:AddItem(button)
end
self:MakePopup()
self:SetKeyboardInputEnabled(false)
PLUGIN.panel = self
LocalPlayer():EmitSound("landline_dialtone.wav", 40, 100, 1, CHAN_STATIC)
end
function PANEL:OnRemove()
PLUGIN.panel = nil
dialSeq:reset()
LocalPlayer():StopSound("landline_dialtone.wav")
LocalPlayer():StopSound("landline_line_busy.wav")
end
-- YES I know this is a bit of a mess but, what can you do?? its DERMA
function PANEL:Paint(w, h)
-- background
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 45, 200))
surface.SetDrawColor(61, 61, 77, 255)
surface.DrawOutlinedRect(0, 0, w, h, 2)
-- "screen"
draw.RoundedBox(6, 60, 20, 390, 350, Color(215, 240, 231))
-- outline
surface.SetDrawColor(15, 13, 44, 255)
surface.DrawOutlinedRect(65, 30, 380, 320, 4)
-- boxes:
local drawOutlineBoxPair = function (x, y, w, h, thick, darker, black)
surface.SetDrawColor(150, 150, 140, 100)
if (darker) then
surface.SetDrawColor(150, 150, 140, 150)
end
if (black) then
surface.SetDrawColor(0, 0, 0, 255)
end
surface.DrawRect(x, y, w, h)
surface.SetDrawColor(15, 13, 44, 255)
surface.DrawOutlinedRect(x, y, w, h, thick)
end
-- top box
drawOutlineBoxPair(65, 30, 376, 55, 3, false, true)
-- name
drawOutlineBoxPair(67, 85, 376, 38, 2)
-- exchange
drawOutlineBoxPair(67, 123, 376, 38, 2, true)
-- ext
drawOutlineBoxPair(67, 161, 376, 38, 2)
-- dial
drawOutlineBoxPair(67, 199, 376, 38, 2, true)
-- status
drawOutlineBoxPair(65, 295, 376, 55, 3, false, true)
-- line status text
surface.SetFont("phoneMonoDebug")
surface.SetTextColor(15, 13, 44, 220)
local lineStatusHeight = 13
local lineStatusLineNo = 1
for lineStatusLine in string.gmatch(PLUGIN.currentLineStatus, "[^\n]+") do
surface.SetTextPos(80, 227 + (lineStatusHeight * lineStatusLineNo))
surface.DrawText(lineStatusLine)
lineStatusLineNo = lineStatusLineNo + 1
end
-- call status text (YES i know)
surface.SetFont("CloseCaption_Normal")
surface.SetTextColor(215, 240, 231, 255)
surface.SetTextPos(75, 300)
surface.DrawText("STATUS :> ")
surface.SetFont("CloseCaption_Bold")
if (PLUGIN.targetedLandlinePBX and PLUGIN.targetedLandlinePBX == 0) then
surface.DrawText("NOT SET UP")
else
surface.DrawText(tostring(PLUGIN.currentCallStatus))
end
surface.SetTextPos(190, 300)
if (PLUGIN.otherSideActive == true and PLUGIN.currentCallStatus == "") then
surface.SetFont("CloseCaption_Normal")
surface.DrawText(tostring(PLUGIN.currentCallPeerName) or "Nieznany")
surface.SetFont("CloseCaption_Bold")
end
-- time text
surface.SetTextColor(215, 240, 231, 255)
surface.SetTextPos(70, 35)
local ostime = os.time()
surface.SetFont("DermaDefaultBold")
surface.DrawText(os.date("%H:%M:%S", ostime))
surface.SetTextPos(70, 50)
surface.DrawText(ix.config.Get("day").."/"..ix.config.Get("month").."/"..ix.config.Get("year"))
end
vgui.Register("ixLandlineDial", PANEL, "DFrame")

View File

@@ -0,0 +1,287 @@
--[[
| 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.Type = "anim"
ENT.Base = "base_gmodentity"
ENT.PrintName = "Landline Phone"
ENT.Author = "M!NT"
ENT.Category = "HL2 RP"
ENT.Contact = ""
ENT.Purpose = ""
ENT.Instructions = ""
ENT.Spawnable = false
ENT.AdminOnly = true
ENT.Holdable = true
ENT.offHook = false
ENT.inUseBy = nil
ENT.isRinging = false
ENT.defaultRingTime = 60 -- how long the phone will ring (in seconds)
ENT.endpointID = nil
ENT.ringCallback = nil -- FnOnce that is called when the phone stops ringing for whatever reason
ENT.hangUpCallback = nil -- FnOnce that is called when the user runs hang up during a call
ENT.currentName = "Unknown" -- public name as stored currently in the PBX
ENT.currentPBX = 0 -- PBX this entity is attached to
ENT.currentExtension = nil -- extension in the PBX
function ENT:GetIndicatorPos()
local btnPos = self:GetPos()
btnPos = btnPos + self:GetForward() * 4.3
btnPos = btnPos + self:GetRight() * -1.1
btnPos = btnPos + self:GetUp() * 2.0
return btnPos
end
if SERVER then
util.AddNetworkString("UpdateLandlineEntStatus")
util.AddNetworkString("EnterLandlineDial")
util.AddNetworkString("ForceHangupLandlinePhone")
function ENT:Initialize()
self:SetModel("models/props/cs_office/phone.mdl")
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
if IsValid(phys) then
phys:EnableMotion(true)
phys:Wake()
end
self.endpointID = ix.phone.switch.endpoints:Register(self)
self.currentExtension = math.random(100, 999)
local usrCheckTimerName = "LandlineEnt"..tostring(self:EntIndex()).."UserCheck"
timer.Create(usrCheckTimerName, 3, 0, function()
-- check to see if, for some reason, inUseBy is still set but the usr is gone
-- if so, use the magical powers of lua to hang ourselves up
if (IsValid(self.inUseBy) and self.inUseBy:IsPlayer()) then
local plys = ix.phone.switch.endpoints:GetPlayersInRadius(self.endpointID, 30)
if (!plys or table.Count(plys) < 1 or
!IsValid(plys[self.inUseBy:SteamID64()])) then
net.Start("ForceHangupLandlinePhone")
net.Send(self.inUseBy)
self:HangUp()
end
else
-- we shouldn't be offHook if we dont have inUseBy set
if (!IsValid(self.inUseBy) and self.offHook) then
self:HangUp()
end
end
end)
self:CallOnRemove("OnRemoveLandlineCleanup", function(ent)
if (self.currentExtension and self.currentPBX) then
return
end
local connID = ix.phone.switch:GetActiveConnection(self.currentPBX,
self.currentExtension)
if (connID) then
ix.phone.switch:Disconnect(connID)
end
timer.Remove(usrCheckTimerName)
end)
end
function ENT:PerformPickup(client)
if timer.Exists("ixCharacterInteraction" .. client:SteamID()) then return end
client:PerformInteraction(.5, self, function(_)
client:GetCharacter():GetInventory():Add("landline_phone")
self:Remove()
end)
end
function ENT:Use(activator)
if (self.nextUse and self.nextUse > CurTime()) then
return
end
self.nextUse = CurTime() + 1
if (activator:GetMoveType() == MOVETYPE_FLY) then
return self:PerformPickup(activator)
end
if (!self.offHook) then
-- User is trying to pick up the phone
self.inUseBy = activator
self:SetModel("models/props/cs_office/phone_p1.mdl")
ix.phone.switch:SetCharVars(self.inUseBy:GetCharacter(),
true, self.currentPBX, self.currentExtension)
ix.phone.switch.endpoints:AddListener(self.endpointID, self.inUseBy)
if (self.isRinging) then
timer.Remove("PhoneRinging"..self.endpointID)
self:StopSound("landline_ringtone.wav")
self.isRinging = false
self:broadcastStatusOnChange()
local _, _ = pcall(self.ringCallback, true)
self.ringCallback = nil
end
self:EmitSound("landline_hangup.wav", 60, 100, 1, CHAN_STATIC)
timer.Simple(0.1, function() -- dont ask me why we have to wait one frame. we just do. thanks garry
self.offHook = true
net.Start("EnterLandlineDial")
net.WriteInt(tonumber(self.endpointID), 15)
net.WriteInt(tonumber(self.currentPBX), 5)
net.WriteInt(tonumber(self.currentExtension), 11)
net.WriteString(self.currentName)
net.WriteInt(self:EntIndex(), 17)
net.Send(self.inUseBy)
end)
end
end
function ENT:EnterRing(callback)
-- this entity getting 'called'
self:EmitSound("landline_ringtone.wav", 75, 100, 1, CHAN_STATIC)
self.isRinging = true
self.ringCallback = callback
self:broadcastStatusOnChange()
timer.Create("PhoneRinging"..self.endpointID, self.defaultRingTime, 1, function ()
-- phone has rung too long
self.isRinging = false
self.offHook = false
self:broadcastStatusOnChange()
local _, _ = pcall(self.ringCallback, false)
self.ringCallback = nil
end)
end
function ENT:hangupDuringRing()
if (!self.isRinging or !timer.Exists("PhoneRinging"..self.endpointID)) then
return nil
end
timer.Remove("PhoneRinging"..self.endpointID)
self:StopSound("landline_ringtone.wav")
self:EmitSound("landline_hangup.wav", 60, 100, 1, CHAN_STATIC)
self.isRinging = false
self.offHook = false
self:broadcastStatusOnChange()
local _, _ = pcall(self.ringCallback, false)
end
function ENT:hangupDuringOffHook()
self:EmitSound("landline_hangup.wav", 60, 100, 1, CHAN_STATIC)
if (self.inUseBy) then
ix.phone.switch:ResetCharVars(self.inUseBy:GetCharacter())
ix.phone.switch.endpoints:RmListener(self.endpointID, self.inUseBy)
end
self.offHook = false
self:broadcastStatusOnChange()
if (self.hangUpCallback) then
local _, _ = pcall(self.hangUpCallback)
self.hangUpCallback = nil
end
end
function ENT:ButtonPress()
-- play a button clack sound because atle wants it
local soundName = "landline_clack_"..tostring(math.random(1, 4))..".wav"
self:EmitSound(soundName, 50, 100, 1, CHAN_STATIC)
end
function ENT:HangUp()
self:SetModel("models/props/cs_office/phone.mdl")
if (!self.isRinging and !self.offHook) then
self:EmitSound("landline_hangup.wav", 70, 100, 1, CHAN_STATIC)
return
end
if (self.offHook) then
self:hangupDuringOffHook()
end
if (self.isRinging) then
self:hangupDuringRing()
end
self.inUseBy = nil
end
function ENT:broadcastStatusOnChange()
net.Start("UpdateLandlineEntStatus")
net.WriteBool(self.isRinging)
net.WriteBool(self.offHook)
net.WriteInt(self.currentPBX, 11)
net.WriteInt(self.currentExtension, 15)
net.Broadcast()
end
else
local glowMaterial = ix.util.GetMaterial("sprites/glow04_noz")
local colorGreen = Color(0, 255, 0, 255)
local colorRed = Color(255, 50, 50, 255)
local isRinging = false
local offHook = false
local nextFlashTime = CurTime()
net.Receive("UpdateLandlineEntStatus", function()
isRinging = net.ReadBool()
offHook = net.ReadBool()
-- reset the indicator status flashing
colorRed.a = 255
if (isRinging) then
nextFlashTime = CurTime() + 1
end
end)
function ENT:Draw()
self:DrawModel()
local btnPos = self:GetIndicatorPos()
render.SetMaterial(glowMaterial)
if (isRinging) then
-- slow flash
if (CurTime() > nextFlashTime) then
colorRed.a = 255
if (CurTime() > nextFlashTime + 0.5) then
nextFlashTime = CurTime() + 0.5
end
else
colorRed.a = 0
end
render.DrawSprite(btnPos, 1, 1, colorRed)
elseif (offHook) then
render.DrawSprite(btnPos, 1, 1, colorRed)
else
render.DrawSprite(btnPos, 1, 1, colorGreen)
end
end
end

View File

@@ -0,0 +1,48 @@
--[[
| 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/
--]]
local PLUGIN = PLUGIN
ITEM.name = "Telefon stacjonarny"
ITEM.model = Model("models/props/cs_office/phone.mdl")
ITEM.description = "Najzwyklejszy telefon stacjonarny."
ITEM.width = 2
ITEM.height = 2
ITEM.category = "Technology"
ITEM.functions.Deploy = {
name = "Deploy",
OnRun = function(itemTable)
local client = itemTable.player
if client.CantPlace then
client:NotifyLocalized("Musisz poczekać zanim będziesz mógł to postawić!..")
return false
end
client.CantPlace = true
timer.Simple(3, function()
if client then
client.CantPlace = false
end
end)
local phone = ents.Create("landline_phone")
local tr = client:GetEyeTrace()
local dist = client:EyePos():Distance(tr.HitPos)
phone:SetPos(client:EyePos() + (tr.Normal * math.Clamp(dist, 0, 75)))
phone:Spawn()
ix.saveEnts:SaveEntity(phone)
return true
end
}

View File

@@ -0,0 +1,198 @@
--[[
| 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/
--]]
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
-- a volatile table for caching ongoing connections
ix.phone.switch.connections = ix.phone.switch.connections or {}
function ix.phone.switch:ConnectionValid(connID)
if (self.connections[connID] == false) then
return false
end
return istable(self.connections[connID])
end
function ix.phone.switch:buildNewConnectionNode(connID, extID, extNum)
-- helper function to create source requests for connections
-- constructs a table that can be used by ix.phone.switch:connect()
if (!self:ConnectionValid(connID)) then
return
end
if (!self.connections[connID].nodes) then
self.connections[connID].nodes = {}
end
local nodeID = #self.connections[connID].nodes + 1
self.connections[connID].nodes[nodeID] = {}
self.connections[connID].nodes[nodeID]["exchange"] = extID
self.connections[connID].nodes[nodeID]["extension"] = extNum
end
function ix.phone.switch:buildNewConnection()
-- helper function to that creates a new connection
-- attempt to reuse a freshly terminated connection
for id, conn in ipairs(self.connections) do
if (conn == false) then
self.connections[id] = {}
return id
end
end
-- no terminated connections
connectionID = #self.connections + 1
self.connections[connectionID] = {}
return connectionID
end
function ix.phone.switch:Disconnect(connID, noNotify)
-- disconnects provided connection at connID
-- if notify is set, then it wont notify listeners that the connection is terminated
if (!istable(self.connections[connID])) then
return
end
local _nodes = self.connections[connID].nodes
for _, node in ipairs(_nodes) do
local recievers = self:getReceivers(node.exchange, node.extension)
if (!istable(recievers) or table.Count(recievers) < 1) then
continue
end
for _, reciever in ipairs(recievers) do
local ent = self.endpoints:GetEndpoint(reciever.endID)
if (ent and ent.HangUp and ent.isRinging) then
ent:HangUp()
end
end
local listeners = self:getListenersFromRecvs(recievers)
if (!listeners or table.Count(listeners) < 1) then
continue
end
for _, listener in ipairs(listeners) do
self:DisconnectClient(listener)
end
end
self.connections[connID] = false
end
-- disconnects the client's status and notifies them of said status change
-- IMPORTANT: Make sure you destroy the connection too!
function ix.phone.switch:DisconnectClient(client)
if (!client or !IsValid(client)) then
return ErrorNoHaltWithStack("Attempt to disconnect invalid client "..tostring(client))
end
local character = client:GetCharacter()
if (!istable(character)) then
return ErrorNoHaltWithStack("Attempt to disconnect client with invalid character "..tostring(character))
end
self:ResetCharVars(character)
self:NotifyStatusChange(client, false, false)
end
function ix.phone.switch:NotifyStatusChange(client, bCallActive, bInCall)
net.Start("ixConnectedCallStatusChange")
net.WriteBool(bCallActive)
net.WriteBool(bInCall)
net.Send(client)
end
function ix.phone.switch:NotifyAllListenersOfStatusChange(endID, bCallActive, bInCall)
local listeners = self.endpoints:GetListeners(endID)
if (!listeners or table.Count(listeners) < 1) then
return -- no need to do anything; no listeners
end
for _, listener in ipairs(listeners) do
-- notify all listeners of their call status change
self:NotifyStatusChange(listener, bCallActive, bInCall)
end
end
function ix.phone.switch:getListenersFromRecvs(recievers)
local listeners = {}
for k, recv in ipairs(recievers) do
-- there will almost always be one reciever.. but treating this as a list in case we ever do 'conference calls'
local _listeners = self.endpoints:GetListeners(recv.endID)
if (istable(_listeners)) then
for _, listener in ipairs(_listeners) do
listeners[table.Count(listeners) + 1] = listener
end
end
end
return listeners
end
function ix.phone.switch:getReceivers(extID, extNum)
local conn = self:GetActiveConnection(extID, extNum)
if (!istable(conn)) then
return
end
return self:getSourceRecieversFromConnection(conn.targetConnID,
conn.sourceNodeID)
end
function ix.phone.switch:GetListeners(extID, extNum)
return self:getListenersFromRecvs(self:getReceivers(extID, extNum))
end
function ix.phone.switch:GetReceivers(extID, extNum)
local conn = self:GetActiveConnection(extID, extNum)
if (!istable(conn)) then
return
end
return self:getSourceRecieversFromConnection(conn.targetConnID,
conn.sourceNodeID)
end
-- returns the active connection in the form of {"targetConnID", "sourceNodeID"} if one is present
function ix.phone.switch:GetActiveConnection(extID, extNum)
for connID, conn in ipairs(self.connections) do
if (conn == false) then
continue
end
for nodeID, node in ipairs(conn.nodes) do
if (node["exchange"] == extID and node["extension"] == extNum) then
-- source is present in this connection
return {targetConnID = connID, sourceNodeID = nodeID}
end
end
end
end
-- returns the actively connected (except for the source) recievers for a given connection
function ix.phone.switch:getSourceRecieversFromConnection(connID, sourceNodeID)
local res = {}
for nodeID, node in ipairs(self.connections[connID].nodes) do
if (nodeID != sourceNodeID) then
-- we want to return this as it exists in the exchange as that will give us
-- extra details the node tree does not contain such as name and endID
res[#res + 1] = self:GetDest(node["exchange"], node["extension"])
end
end
return res
end

View File

@@ -0,0 +1,123 @@
--[[
| 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/
--]]
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
-- a flat list of all the entities. each entity has an associated 'listeners' table which contains all of the players actively connected to that entity
ix.phone.switch.endpoints = ix.phone.switch.endpoints or {}
function ix.phone.switch.endpoints:exists(id)
return self[id] != nil
end
function ix.phone.switch.endpoints:entExists(entIdx)
for id, ent in ipairs(self) do
if (entIdx == ent:EntIndex()) then
return id
end
end
return nil
end
function ix.phone.switch.endpoints:Register(ent)
-- assigns an ID. if the ent already exists then it will return the existing id
-- we need to have our own ID here rather than the index because the entity index might change but
-- in that case the id shouldn't
local entExists = self:entExists(ent:EntIndex())
if (entExists != nil) then
return nil
end
local newID = math.random(1000, 9999)
if (self:exists(newID)) then
return nil
end
self[newID] = ent
return newID
end
function ix.phone.switch.endpoints:DeRegister(id)
self[id] = nil
end
function ix.phone.switch.endpoints:GetEndpoint(id)
-- returns the associated entity table
if (self:exists(id)) then
return self[id]
end
end
function ix.phone.switch.endpoints:AddListener(id, client)
if (!istable(self[id].listeners)) then
self[id].listeners = {}
end
for _, listener in ipairs(self[id].listeners) do
if (listener == client) then
return
end
end
self[id].listeners[table.Count(self[id].listeners) + 1] = client
end
function ix.phone.switch.endpoints:RmListener(id, client)
if (!istable(self[id].listeners)) then
return
end
for k, listener in ipairs(self[id].listeners) do
if (listener == client) then
self[id].listeners[k] = nil
end
end
end
function ix.phone.switch.endpoints:GetListeners(id)
return self[id].listeners
end
function ix.phone.switch.endpoints:RingEndpoint(id, callback)
-- rings and endpoint and, if the phone is picked up, it will call callback as true. otherwise false
-- if the destination is unavailable or busy then it will return nil
local ent = self:GetEndpoint(id)
if (ent.inUse or ent.isRinging) then
return nil
end
ent:EnterRing(callback)
end
function ix.phone.switch.endpoints:GetPlayersInRadiusFromPos(pos, radius)
local entsInside = ents.FindInSphere(pos, radius)
local res = {}
for _, _ent in ipairs(entsInside) do
if (_ent:IsPlayer() and _ent.GetCharacter and _ent:GetCharacter()) then
res[_ent:SteamID64()] = _ent
end
end
return res
end
-- returns a list of players within X radius of the endpoint
function ix.phone.switch.endpoints:GetPlayersInRadius(id, radius)
local ent = self:GetEndpoint(id)
if (!ent) then
return
end
return self:GetPlayersInRadiusFromPos(ent:GetPos(), radius)
end

View File

@@ -0,0 +1,100 @@
--[[
| 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/
--]]
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
-- a table used for managing pbxs and their members
ix.phone.switch.exchanges = ix.phone.switch.exchanges or {}
--[[ The following is some boiler plate code for creating and managing extensions and exchanges
In practice these things are just keys in a table and in the DB but it is probably best
if you use the functions here and not directly modify ix.phone.switch.exchanges directly!
This is necessary to keep the switching code readable
]]
function ix.phone.switch:AddExchange(exID)
if (self:ExchangeExists(exID)) then
return false
end
self.exchanges[exID] = {}
return true
end
function ix.phone.switch:RmExchange(exID)
if (!self:ExchangeExists(exID)) then
return false
end
self.exchanges[exID] = nil
return true
end
function ix.phone.switch:ExchangeExists(exID)
return self.exchanges[exID] != nil
end
function ix.phone.switch:DestExists(exID, extNum)
if (self.exchanges[exID] != nil) then
return self.exchanges[exID][extNum] != nil
else
return false
end
end
function ix.phone.switch:AddDest(exID, extNum, extName, endID)
-- returns false if destination exists or exchange doesn't
-- set noDB if you do not wish to store this destination to the database
if (self:DestExists(exID, extNum)) then
return false
end
self.exchanges[exID][extNum] = {}
self.exchanges[exID][extNum]["name"] = extName or ""
self.exchanges[exID][extNum]["endID"] = endID
return true
end
function ix.phone.switch:GetDest(exID, extNum)
if (!self:DestExists(exID, extNum)) then
return false
end
return self.exchanges[exID][extNum]
end
function ix.phone.switch:RmDest(exID, extNum)
-- returns false if destination does not exist
if (!self:DestExists(exID, extNum)) then
return false
end
self.exchanges[exID][extNum] = nil
return true
end
function ix.phone.switch:MvDest(fromExID, fromExtNum, toExID, toExtNum)
if (!self:RmDest(fromExID, fromExtNum)) then
return false
end
return self:AddDest(toExID, toExtNum)
end
function ix.phone.switch:SetName(exID, extNum, name)
if (!self:DestExists(exID, extNum)) then
return false
end
self.endpoints[self.exchanges[exID][extNum]["endID"]].currentName = name
return true
end

View File

@@ -0,0 +1,402 @@
--[[
| 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/
--]]
-- this is a loose implementation of a virtual private business exchange (vPBX)
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
ix.phone.switch.lineTestPBX = 9
ix.phone.switch.lineTestExt = 999 -- reserved for testing
do
-- set up ent to use for 2 way call testing
timer.Simple(10, function()
local lineTestEnt = ents.Create("landline_phone")
lineTestEnt:SetPos(Vector(0, 0, 0))
lineTestEnt:Spawn()
lineTestEnt:SetNoDraw(true)
-- if we dont do this then our landline point ent will fall through the world
-- and get deleted
local _phys = lineTestEnt:GetPhysicsObject()
_phys:EnableGravity(false)
_phys:EnableMotion(false)
_phys:Sleep()
lineTestEnt.currentName = "Line Test"
lineTestEnt.currentPBX = ix.phone.switch.lineTestPBX
lineTestEnt.currentExtension = ix.phone.switch.lineTestExt
-- yes we need a real entity for this. doesn't have to be a landline but might as well
ix.phone.switch.lineTestEnt = lineTestEnt
end)
end
-- (optional)
-- takes in a dial sequence in the format of (exchange)(extension)
-- ex: 1 234
-- it returns it into a table as {"exchange", "extension"}
function ix.phone.switch:decodeSeq(dialSeq)
-- dial sequences must be strings, but must be a real number as well and must be 4 digits
if (type(dialSeq) != "string" or tonumber(dialSeq) == nil) then
return nil
elseif (string.len(dialSeq) > 4 or string.len(dialSeq) < 3) then
return nil
end
local exchange = nil
if (string.len(dialSeq) != 3) then -- otherwise it is a local dial (to endpoint in own exchange)
exchange = tonumber(string.sub(dialSeq, 0, 1))
if (exchange == nil or exchange < 1) then
return nil
end
end
-- the remaining digits should be the extension
local ext = tonumber(string.sub(dialSeq, 2, 4))
if (ext == nil or ext < 1) then
return nil
end
return {["exchange"] = exchange, ["extension"] = ext}
end
function ix.phone.switch:BuildNewTwoWayConnection(sourceExc, sourceExt, destExc, destExt)
local connID = self:buildNewConnection()
self:buildNewConnectionNode(connID, sourceExc, sourceExt)
self:buildNewConnectionNode(connID, destExc, destExt)
return connID
end
function ix.phone.switch:Dial(sourceExc, sourceExt, dialSeq)
if (!self:DestExists(sourceExc, sourceExt)) then
return self.DialStatus.SourceNotExist
end
--[[
Decode the user provided dial sequence into a switchable pair of of pbx and ext
]]
if (!istable(dialSeq) and #dialSeq < 1) then
return self.DialStatus.NoDialSeq
end
local decodedDest = self:decodeSeq(dialSeq)
if (!istable(decodedDest)) then
return self.DialStatus.CannotDecodeDial
end
if (decodedDest.exchange == self.lineTestPBX and decodedDest.extension == self.lineTestExt) then
self:StartLineTest(sourceExc, sourceExt)
return self.DialStatus.DebugMode
end
if (decodedDest.exchange == nil) then
decodedDest.exchange = sourceExc
end
if (!self:DestExists(decodedDest.exchange, decodedDest.extension)) then
return self.DialStatus.NumberNotFound
end
--[[
Get the endpoint IDs and corresponding entities for source & destination
]]
local destination = self:GetDest(decodedDest.exchange, decodedDest.extension)
local source = self:GetDest(sourceExc, sourceExt)
if (!destination.endID or !source.endID) then
return self.DialStatus.EndpointNotFound
end
local destEnt = self.endpoints:GetEndpoint(tonumber(destination.endID))
local sourceEnt = self.endpoints:GetEndpoint(tonumber(source.endID))
--[[
Check if the line is busy
]]
local _conn = self:GetActiveConnection(decodedDest.exchange, decodedDest.extension)
if (_conn and istable(_conn) or destEnt.offHook) then
return self.DialStatus.LineBusy
end
--[[
Build new connection for source & dest with the pbxs and exts.
]]
local connID = self:BuildNewTwoWayConnection(sourceExc, sourceExt,
decodedDest.exchange, decodedDest.extension)
--[[
Setup the callbacks and start the call
]]
local ringCallback = function(status)
if (!status) then -- call did not go through so we need to clean up
self:Disconnect(connID)
end
self:NotifyAllListenersOfStatusChange(tonumber(destination.endID), status, status)
self:NotifyAllListenersOfStatusChange(tonumber(source.endID), status, status)
end
destEnt:EnterRing(ringCallback) -- set the target as ringing
local hangUpCallback = function(status)
-- cleanup
self:Disconnect(connID)
end
destEnt.hangUpCallback = hangUpCallback
sourceEnt.hangUpCallback = hangUpCallback
return self.DialStatus.Success
end
-- returns back a list of player entities that are listening to the phone this character is speaking into
function ix.phone.switch:GetCharacterActiveListeners(character)
if (!istable(character)) then
return
end
local connMD = character:GetLandlineConnection()
if (!connMD) then
return
end
return self:GetListeners(connMD.exchange, connMD.extension)
end
function ix.phone.switch:GetPlayerActiveListeners(client)
local character = client:GetCharacter()
if (!istable(character)) then
return nil
end
return self:GetCharacterActiveListeners(character)
end
-- rudely hangs up every single active call related to this character
-- typically used when the player disconnects or switches chars mid call
function ix.phone.switch:DisconnectActiveCallIfPresentOnClient(client)
local character = client:GetCharacter()
if (!istable(character)) then
return
end
local connMD = character:GetLandlineConnection()
if (!istable(connMD) and !connMD["active"]) then
-- probably ran hangup on a phone someone else was speaking on
-- we should allow this in the future (maybe?) but for now we exit
client:NotifyLocalized("You are not speaking on the phone.")
return
end
-- terminate any existing connections here
local conn = self:GetActiveConnection(connMD["exchange"], connMD["extension"])
if (!istable(conn)) then
client:NotifyLocalized("Error: AttemptedHangupOnActivePhoneNoConn")
-- This shouldn't be possible but if it happens then there is some lingering issue with
-- this character's var being active when they are not in an active connection
self:ResetCharVars(character)
return
end
self:Disconnect(conn["targetConnID"])
end
-- returns whether or not the 'listener' is in an active phone call with 'speaker'
function ix.phone.switch:ListenerCanHearSpeaker(speaker, listener)
local speakerChar = speaker:GetCharacter()
local listeners = self:GetCharacterActiveListeners(speakerChar)
if (!istable(listeners)) then
-- doubly make sure that the call activity is set correctly on the caller
speaker:NotifyLocalized("You are not currently on a phone call!")
ErrorNoHaltWithStack("Speaker ("..tostring(speaker:GetName())..") has invalid listener list!")
self:ResetCharVars(speakerChar)
return false
end
for _, _listener in ipairs(listeners) do
if (_listener == listener) then
return true
end
end
return false
end
--[[
Some helpers for setting the correct things in the correct order
]]
-- Reset ix.character.landlineConnection variables to default state
function ix.phone.switch:ResetCharVars(character)
character:SetLandlineConnection({
active = false,
exchange = nil,
extension = nil
})
end
-- Set ix.character.landlineConnection variables
function ix.phone.switch:SetCharVars(character, bActive, exc, ext)
character:SetLandlineConnection({
active = bActive,
exchange = exc,
extension = ext
})
end
-- Create a fake destination for testing purposes
-- Do nothing if one exists already
function ix.phone.switch:initLineTestNumber()
if (!self:ExchangeExists(self.lineTestPBX)) then
self:AddExchange(self.lineTestPBX)
end
if (!self.lineTestEnt.endpointID) then
self.lineTestEnt.endpointID = self.endpoints:Register(self.lineTestEnt)
print("Line Test Initalized! EndID: "..tostring(self.lineTestEnt.endpointID))
end
local destination = self:GetDest(self.lineTestPBX, self.lineTestExt)
if (!destination) then
self:AddDest(self.lineTestPBX, self.lineTestExt, "Line Test", self.lineTestEnt.endpointID)
end
end
-- used for debugging purposes (by doing lua_run in rcon)
-- calls a specific landline
function ix.phone.switch:DebugSinglePartyCall(exchange, ext)
if (!self:DestExists(exchange, ext)) then
ErrorNoHalt("Destination does not exist!")
return -- source does not exist or is not valid
end
self:initLineTestNumber()
local connID = self:buildNewConnection()
self:buildNewConnectionNode(connID, exchange, ext)
self:buildNewConnectionNode(connID, self.lineTestPBX, self.lineTestExt)
local dest = self:GetDest(exchange, ext)
if (!istable(dest)) then
self:Disconnect(connID) -- source dissapeared for some reason
ErrorNoHalt("Destination does not exist, or is invalid!")
return
end
print("Destination Endpoint Found!: "..table.ToString(dest, "Destination Endpoint", true))
local destEnt = self.endpoints:GetEndpoint(tonumber(dest.endID))
print("Destination Entity Found! ID: "..tostring(destEnt:EntIndex()))
destEnt:EnterRing(function(status)
if (!status) then -- call did not go through so we need to clean up
self:Disconnect(connID)
end
self:NotifyAllListenersOfStatusChange(tonumber(dest.endID), status, status)
local client = destEnt.inUseBy
net.Start("LineTestChat")
net.WriteString("This is a test to determine connection stability. It will automatically disconnect in 10 seconds.")
net.Send(client)
net.Start("LineStatusUpdate")
net.WriteString(self.DialStatus.DebugMode)
net.Send(client)
timer.Simple(15, function()
net.Start("LineTestChat")
net.WriteString("Line test has completed. Disconnecting now.")
net.Send(client)
self:Disconnect(connID)
end)
end)
local hangUpCallback = function()
-- cleanup
self:Disconnect(connID)
end
destEnt.hangUpCallback = hangUpCallback
end
-- used for debugging purposes
-- allows landline to make a call out to a test entity placed at map root
function ix.phone.switch:StartLineTest(exchange, ext)
if (!self:DestExists(exchange, ext)) then
ErrorNoHalt("Source does not exist!")
return -- source does not exist or is not valid
end
self:initLineTestNumber()
local connID = self:BuildNewTwoWayConnection(exchange, ext, self.lineTestPBX, self.lineTestExt)
local conn = self:GetActiveConnection(exchange, ext)
if (!istable(conn)) then
ErrorNoHalt("Failed to construct connection nodes! ", tostring(connID))
return
end
print("Connection nodes constructed!: "..table.ToString(conn, "ConnID: "..tostring(connID), true))
local source = self:GetDest(exchange, ext)
if (!istable(source)) then
self:Disconnect(connID) -- source dissapeared for some reason
ErrorNoHalt("Source does not exist, or is invalid!")
return
end
print("Source Endpoint Found!: "..table.ToString(source, "Source Endpoint", true))
local sourceEnt = self.endpoints:GetEndpoint(tonumber(source["endID"]))
print("Source Entity Found! ID: "..tostring(sourceEnt:EntIndex()))
local listeners = self:GetListeners(self.lineTestPBX, self.lineTestExt)
local client = listeners[1]
if (!client) then
self:Disconnect(connID)
print("Destination listener pool: "..table.ToString(listeners, "Listener Pool", true))
ErrorNoHalt("Destination has no listeners!")
return
end
local hangUpCallback = function()
-- cleanup
self:Disconnect(connID)
end
sourceEnt.hangUpCallback = hangUpCallback
print("Line Test Listener Found! ID: "..tostring(client))
timer.Simple(2, function()
net.Start("ixConnectedCallStatusChange")
net.WriteBool(true)
net.WriteBool(true)
net.Send(client)
net.Start("LineTestChat")
net.WriteString("This is a test to determine connection stability. It will automatically disconnect in 10 seconds.")
net.Send(client)
end)
timer.Simple(15, function()
net.Start("LineTestChat")
net.WriteString("Line test has completed. Disconnecting now.")
net.Send(client)
net.Start("ixConnectedCallStatusChange")
net.WriteBool(false)
net.WriteBool(false)
net.Send(client)
self:Disconnect(connID)
end)
end

View File

@@ -0,0 +1,339 @@
--[[
| 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/
--]]
--[[
ring ring ring ring - LUA PHONE
,==.-------.
( ) ==== \
|| | [][][] |
,8|| | [][][] |
8 || | [][][] |
8 ( ) O O O /
'88`=='-------'
]]
local PLUGIN = PLUGIN
PLUGIN.name = "Phones"
PLUGIN.description = "Adds landlines, pagers, and ways to route them"
PLUGIN.author = "M!NT"
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
ix.util.Include("cl_hooks.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("sv_plugin.lua")
CAMI.RegisterPrivilege({
Name = "Helix - Use Phones",
MinAccess = "user"
})
ix.command.Add("HangupPhone", {
alias = "HP",
description = "Odłóż telefon, na który patrzysz.",
arguments = {},
privilege = "Use Phones",
OnRun = function(self, client)
PLUGIN:runHangupOnClient(client)
end
})
CAMI.RegisterPrivilege({
Name = "Helix - Manage Phones",
MinAccess = "admin"
})
ix.command.Add("EditLandline", {
description = "Edytuj telefon stacjonarny, na który obecnie patrzysz.",
arguments = {
ix.type.number,
ix.type.number,
ix.type.text
},
privilege = "Manage Phones",
OnRun = function(self, client, pbx, extension, publicName)
if (!pbx or !extension or !publicName or string.len(publicName) < 1) then
client:NotifyLocalized("Podałeś nieprawidłowy argument")
end
-- perform input validations
if (pbx < 1 or pbx > 9) then
client:NotifyLocalized("Podany PBX jest nieprawidłowy! (musi być liczbą pomiędzy 1 a 10)")
return
end
if (!ix.phone.switch:ExchangeExists(pbx)) then
client:NotifyLocalized("Podany PBX '"..tostring(pbx).."' nie istnieje.")
return
end
if (extension < 100 or extension > 999) then
client:NotifyLocalized("Podane rozszerzenie jest nieprawidłowe! (musi być liczbą pomiędzy 100 a 1000")
return
end
if (string.len(publicName) < 1) then
client:NotifyLocalized("Podana nazwa publiczna jest nieprawidłowa! (musi mieć długość większą niż 1)")
return
end
local data = {}
data.start = client:GetShootPos()
data.endpos = data.start + client:GetAimVector() * 96
data.filter = client
local target = util.TraceLine(data).Entity
if (!IsValid(target) or target.PrintName != "Landline Phone") then
client:NotifyLocalized("Nie patrzysz obecnie na telefon.")
return
end
if (ix.phone.switch:DestExists(target.currentPBX, target.currentExtension)) then
print("exists")
ix.phone.switch:RmDest(target.currentPBX, target.currentExtension)
end
if (!ix.phone.switch:AddDest(pbx, extension, publicName, target.endpointID)) then
client:NotifyLocalized("Nie można zarejestrować telefonu przy użyciu podanych argumentów")
return
end
target.currentName = publicName
target.currentPBX = pbx
target.currentExtension = extension
client:NotifyLocalized("Pomyślnie zaktualizowano telefon stacjonarny.")
end
})
ix.command.Add("AddPBX", {
description = "Dodaj nową centralę PBX, jeśli jeszcze nie istnieje.",
arguments = {ix.type.number},
privilege = "Manage Phones",
OnRun = function(self, client, pbx)
if (!pbx or pbx < 1 or pbx > 9) then
client:NotifyLocalized("Podany PBX jest nieprawidłowy! (musi być liczbą pomiędzy 1 a 10)")
return
end
if (ix.phone.switch:AddExchange(pbx)) then
client:NotifyLocalized("PBX '"..tostring(pbx).."' został pomyślnie utworzony!")
else
client:NotifyLocalized("PBX '"..tostring(pbx).."' już istnieje!")
return
end
end
})
ix.command.Add("RemovePBX", {
description = "Usuń istniejącą centralę PBX (zatrzyma wszystkie aktywne połączenia na tej centrali PBX i wyrejestruje wszystkie aktualnie podłączone telefony!)",
arguments = {ix.type.number},
privilege = "Manage Phones",
OnRun = function(self, client, pbx)
if (!pbx or pbx < 1 or pbx > 9) then
client:NotifyLocalized("Podany PBX jest nieprawidłowy! (musi być liczbą pomiędzy 1 a 10)")
return
end
if (!ix.phone.switch:ExchangeExists(pbx)) then
client:NotifyLocalized("PBX '"..tostring(pbx).."' nie istnieje!")
return
end
-- stop all active connections
for ext, md in ipairs(ix.phone.switch.exchanges[pbx]) do
local connMd = ix.phone.switch:GetActiveConnection(pbx, ext)
if (istable(connMd)) then
ix.phone.switch:Disconnect(connMD.targetConnID)
end
end
ix.phone.switch:RmExchange(pbx)
client:NotifyLocalized("PBX '"..tostring(pbx).."' został zdekonstruowany.")
end
})
ix.char.RegisterVar("landlineConnection", {
field = "landlineConnection",
fieldType = ix.type.table,
default = {
active = false,
exchange = nil,
extension = nil
},
bNoDisplay = true,
isLocal = true,
OnSet = function(self, value)
local client = self:GetPlayer()
if (!IsValid(client)) then
return nil
end
self.vars.landlineConnection = {
active = value["active"],
exchange = value["exchange"],
extension = value["extension"]
}
end,
OnGet = function(self, default)
local connMetaData = self.vars.landlineConnection
return connMetaData
end,
OnAdjust = function(self, client, data, value, newData)
newData.landlineConnection = value
end
})
do
local phoneChatCommands = {
phonesay = {
alias = "PS",
description = "Mów do telefonu, jeśli go trzymasz.",
range = ix.config.Get("chatRange", 280),
},
phonewhisper = {
alias = "PW",
description = "Szepcz do telefonu, jeśli go trzymasz.",
range = ix.config.Get("chatRange", 280) / 4,
},
phoneyell = {
alias = "PY",
description = "Krzycz do telefonu, jeśli go trzymasz.",
range = ix.config.Get("chatRange", 280) * 4,
}
}
for phoneChatCommand, commandSettings in pairs(phoneChatCommands) do
ix.command.Add(phoneChatCommand, {
alias = commandSettings.alias,
description = commandSettings.description,
arguments = ix.type.text,
privilege = "Use Phones",
OnRun = function(self, client, message)
local character = client:GetCharacter()
if (!istable(character)) then
return
end
if (character:GetLandlineConnection().active) then
local recvs = ix.phone.switch:GetCharacterActiveListeners(character)
if (!recvs) then
recvs = {}
end
local eavesDropRecvs = ix.phone.switch.endpoints:GetPlayersInRadiusFromPos(
client:GetPos(), commandSettings.range)
if (istable(eavesDropRecvs)) then
table.Add(recvs, eavesDropRecvs)
end
-- doing this here because, for some reason, chat CanHear just isn't working
-- "If you want it done right, do it yourself"
ix.chat.Send(client, phoneChatCommand, message, false, recvs)
end
end
})
end
end
do
-- used to simulate a distortion effect on certain letters when using phoneyell
local chatDistortionChars = {
O = "Ő̸̗",
F = "F̸̲̂̉ ̷̖̇̀",
P = "P̵̧̝̫̘͛͜F̶̆̃ ̴̩̫̎̌͜͝ ̴͈̈́",
CK = "C̴̣̖̕͜K̵̘̲͂̈́",
K = "K̵̘̲͂̈́",
T = "T̸̮͒͝͝",
X = "Ẍ̷̻́_̷̛̰",
SH = "S̷̥̓ ̶̖͂Ḣ̴̗ ̴̘̔",
Z = "Z̶̗̒̃̅",
S = "S̶͖̐̿ ̷̤̰͂͌",
H = " ̵̬̻͇̎ ̵͈̦͍̔̅̊̇͝H"
}
-- TODO: get atle to add a real icon for this to willardcuntent
local chatIcon = ix.util.GetMaterial("willardnetworks/chat/message_icon.png")
local chatSettings = {
phonewhisper = {
range = ix.config.Get("chatRange", 280) / 4,
color = Color(149, 196, 215),
indicator = "chatWhispering",
format = "[PHONE] %s: (whisper) \"%s\""
},
phonesay = {
range = ix.config.Get("chatRange", 280),
color = Color(105, 157, 178),
indicator = "chatTalking",
format = "[PHONE] %s: \"%s\""
},
phoneyell = {
range = ix.config.Get("chatRange", 280) * 4,
color = Color(166, 89, 89),
indicator = "chatYelling",
format = "[PHONE] %s: \"%s\""
}
}
for chatType, chatTypeData in pairs(chatSettings) do
ix.chat.Register(chatType, {
color = chatTypeData.color,
indicator = chatTypeData.indicator,
format = chatTypeData.format,
CanSay = function(self, speaker, text)
return speaker:GetCharacter():GetLandlineConnection()["active"]
end,
OnChatAdd = function(self, speaker, text, anonymous, data)
local name = anonymous and
L"someone" or
hook.Run("GetCharacterName", speaker, "ic") or
(IsValid(speaker) and speaker:Name() or "Console")
local rText = ix.chat.Format(text)
if (chatType == "phoneyell") then
rText = rText:upper()
for char, repl in pairs(chatDistortionChars) do
rText = rText:gsub(tostring(char), tostring(repl))
end
-- ix.chat.Format() will always add punctuation.
-- Replace that with an exclamation mark for greater effect :)
rText = rText:sub(1, -2)
rText = rText.."!"
end
rText = string.format(chatTypeData.format, name, rText)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(chatIcon, self.color, rText)
else
chat.AddText(self.color, rText)
end
end
})
end
end
do
-- dial statuses which will be displayed on the target phone's derma
ix.phone.switch.DialStatus = {
SourceNotExist = "Err #186A0:>\n Source does not exist as a destination.\n Is it registered in the PSTN?",
NoDialSeq = "Err #1D4C0:>\n No dial sequence provided.",
CannotDecodeDial = "Err #1E078:>\n Invalid dial sequence provided.",
NumberNotFound = "Err #1E208:>\n Destination not registered in the PSTN;\n Cannot find a route.",
EndpointNotFound = "Err #1E23A:>\n Route exists, but is missing a valid endpoint;\n Cannot reach endpoint.",
DebugMode = "Entering debug mode.",
Success = "Connection established.",
LineBusy = "Line busy. Please try again later",
NotSetup = "Err #1E240:>\n Cannot establish connection.\n Please verifiy connection to PBX."
}
end

View File

@@ -0,0 +1,147 @@
--[[
| 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/
--]]
local PLUGIN = PLUGIN
util.AddNetworkString("BeginDialToPeer")
util.AddNetworkString("ixConnectedCallStatusChange")
util.AddNetworkString("RunHangupLandline")
util.AddNetworkString("RunGetPeerName")
util.AddNetworkString("OnGetPeerName")
util.AddNetworkString("LineTestChat")
util.AddNetworkString("LineStatusUpdate")
util.AddNetworkString("LandlineKeyPress")
net.Receive("LandlineKeyPress", function (len, client)
local ent = Entity(net.ReadInt(17))
if (ent and ent.inUseBy == client and ent.ButtonPress) then
ent:ButtonPress()
end
end)
net.Receive("BeginDialToPeer", function (len, client)
local dialSeq = net.ReadString()
if (string.len(dialSeq) < 3 or string.len(dialSeq) > 4 or tonumber(dialSeq) == nil) then
return
end
local exchange = net.ReadInt(5)
local extension = net.ReadInt(11)
local character = client:GetCharacter()
local vars = character:GetLandlineConnection()
-- verify that this is coming from the correct place (or that no one is trying to manually call the hook)
if (vars["exchange"] != exchange and vars["extension"] != extension) then
return
end
local status = ix.phone.switch:Dial(exchange, extension, dialSeq)
if (status) then
net.Start("LineStatusUpdate")
net.WriteString(status)
net.Send(client)
if (status != ix.phone.switch.DialStatus.Success and
status != ix.phone.switch.DialStatus.DebugMode) then
net.Start("ixConnectedCallStatusChange")
net.WriteBool(true)
net.WriteBool(false)
net.Send(client)
end
end
end)
net.Receive("RunHangupLandline", function (len, client)
PLUGIN:runHangupOnClient(client)
end)
net.Receive("RunGetPeerName", function (len, client)
if (!client or !IsValid(client)) then
return
end
local char = client:GetCharacter()
local charMD = char:GetLandlineConnection()
if (charMD.active) then
if (!charMD.extension or !charMD.exchange) then
return
end
recievers = ix.phone.switch:GetReceivers(charMD.exchange, charMD.extension)
if (!recievers or #recievers < 1) then
return
end
net.Start("OnGetPeerName")
net.WriteString(recievers[1]["name"])
net.Send(client)
end
end)
function PLUGIN:runHangupOnClient(client)
if (!client or !IsValid(client)) then
return
end
local data = {}
data.start = client:GetShootPos()
data.endpos = data.start + client:GetAimVector() * 96
data.filter = client
local target = util.TraceLine(data).Entity
if (!target or target.PrintName != "Landline Phone") then
return
end
target:HangUp()
end
-- PHONE CLEANUP
-- this function literally just dumps all current ongoing connections that happen to have this client in them
local function cleanupActivePhoneConnectionsForClient(client)
if (!client or !IsValid(client)) then
return
end
local char = client:GetCharacter()
local charCallMD = nil
-- why do I have to do all this checking?? UGH thanks helix
if (char and char.GetLandlineConnection) then
charCallMD = char:GetLandlineConnection()
end
if (istable(charCallMD) and charCallMD["active"]) then
ix.phone.switch:DisconnectActiveCallIfPresentOnClient(client)
end
if (char) then
char:SetLandlineConnection({
active = false,
exchange = nil,
extension = nil
})
end
end
-- peak laziness:
function PLUGIN:PlayerDisconnected(client)
cleanupActivePhoneConnectionsForClient(client)
end
function PLUGIN:OnCharacterFallover(client, entity, bFallenOver)
cleanupActivePhoneConnectionsForClient(client)
end
function PLUGIN:PlayerDeath(client)
cleanupActivePhoneConnectionsForClient(client)
end
function PLUGIN:PlayerLoadedCharacter(client, character, lastChar)
cleanupActivePhoneConnectionsForClient(client)
end

View File

@@ -0,0 +1,49 @@
--[[
| 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/
--]]
ix.phone = ix.phone or {}
ix.phone.switch = ix.phone.switch or {}
local PLUGIN = PLUGIN
function PLUGIN:RegisterSaveEnts()
ix.saveEnts:RegisterEntity("landline_phone", true, true, true, {
OnSave = function(entity, data) --OnSave
data.endpointID = entity.endpointID
data.extension = entity.currentExtension
data.exchange = entity.currentPBX
data.name = entity.currentName
end,
OnRestore = function(entity, data) --OnRestore
local exID = tonumber(data.exchange)
local ext = tonumber(data.extension)
local name = data.name
if (!exID or !ext) then
ErrorNoHalt("Landline save data missing endpoint or extension. Ext: "..tostring(ext).." Pbx: "..tostring(exID))
return
end
local newEndID = ix.phone.switch.endpoints:Register(entity)
if (!newEndID) then
-- ent exists already as an endpoint
ErrorNoHalt("Landline multiply registered as endpoint! EndID: "..tostring(newEndID))
return
end
entity.endpointID = newEndID
ix.phone.switch:AddExchange(exID)
ix.phone.switch:AddDest(exID, ext, name, newEndID)
entity.currentExtension = ext
entity.currentPBX = exID
entity.currentName = name
end
})
end