Hi, I'm trying to make a bounty system, and I'm hitting walls.
Here's the code in cl_bounties
[code]
net.Receive( "BountyTablesAll", function( length, client )
net.ReadEntity().BountyTable = net.ReadTable()
end )
function ClientInit()
ClientMasterBountyTable = {}
ClientBountyTable = {}
end
hook.Add( "Initialize", "ClientInit", ClientInit )
function Bounties()
local frame = vgui.Create( "DFrame" )
frame:SetPos( ScrW() * 0.125, ScrH() * 0.25 )
frame:SetSize( ScrW()*0.75, ScrH()/2 )
frame:SetTitle( "Bounties" )
frame:SetVisible( true )
frame:SetDraggable( true )
frame:ShowCloseButton( true )
frame:MakePopup()
local setframe = vgui.Create( "DCollapsibleCategory", frame )
setframe:SetPos( 25, 50 )
setframe:SetSize( frame:GetWide()/5 , frame:GetTall()/3 )
setframe:SetExpanded( 0 )
setframe:SetLabel( "Set a Bounty" )
local setlist = vgui.Create( "DPanelList", setframe )
setlist:SetPos( 0, 30 )
setlist:SetSize( setframe:GetWide(), setframe:GetTall()*2.3 )
setlist:SetSpacing( 5 )
setlist:EnableHorizontal( false )
setlist:EnableVerticalScrollbar( true )
local SetSlider = vgui.Create( "DNumSlider", setframe )
SetSlider:SetPos( 25, 20 )
SetSlider:SetSize( 150, 10 ) -- Keep the second number at 100
SetSlider:SetText( "Amount" )
SetSlider:SetMin( 0 ) -- Minimum number of the slider
SetSlider:SetMax( Money ) -- Maximum number of the slider
SetSlider:SetValue( 0 )
SetSlider:SetDecimals( 0 ) -- Sets a decimal. Zero means it's a whole number
SetSlider:SizeToContents()
setlist:AddItem( SetSlider )
local SetNameText = vgui.Create( "DTextEntry", setframe )
SetNameText:SetPos( 20, 80 )
SetNameText:SetTall( 20 )
SetNameText:SetWide( 450 )
SetNameText:SetEnterAllowed( true )
SetNameText:SetText( "Name" )
SetNameText.OnEnter = function()
for k, v in pairs( player.GetAll() ) do
if string.find( string.lower( v:Name() ), string.lower( SetNameText:GetText() ) ) != nil then
if v.Bounty == nil then
v.Bounty = 0
end
v.Bounty = v.Bounty + math.Round( SetSlider:GetValue() )
if table.HasValue( ClientBountyTable, v ) == false then
table.insert( ClientBountyTable, v )
end
net.Start( "BountyToAdd" )
net.WriteEntity( v )
net.WriteInt( math.Round( SetSlider:GetValue() ), 32 )
net.SendToServer()
print( "v is", v )
end
end
end
SetNameText:SizeToContents()
setlist:AddItem( SetNameText )
SetButton = vgui.Create( "DButton", setframe )
SetButton:SetPos( 100, 30 )
SetButton:SetSize( 100, 25 )
SetButton:SetText( "OK" )
SetButton.DoClick = function()
for k, v in pairs( player.GetAll() ) do
if string.find( string.lower( v:Name() ), string.lower( SetNameText:GetText() ) ) != nil then
if v.Bounty == nil then
v.Bounty = 0
end
v.Bounty = v.Bounty + math.Round( SetSlider:GetValue() )
if table.HasValue( ClientBountyTable, v ) == false then
table.insert( ClientBountyTable, v )
end
net.Start( "BountyToAdd" )
net.WriteEntity( v )
net.WriteInt( math.Round( SetSlider:GetValue() ), 32 )
net.SendToServer()
end
end
end
setlist:AddItem( SetButton )
local allframe = vgui.Create( "DCollapsibleCategory", frame )
allframe:SetPos( 25 + frame:GetWide()/5, 50 )
allframe:SetSize( frame:GetWide()/5 , frame:GetTall()/3 )
allframe:SetExpanded( 0 )
allframe:SetLabel( "All" )
local alllist = vgui.Create( "DPanelList", allframe )
alllist:SetPos( 0, 30 )
alllist:SetSize( allframe:GetWide(), allframe:GetTall()*2.3 )
alllist:SetSpacing( 5 )
alllist:EnableHorizontal( false )
alllist:EnableVerticalScrollbar( true )
for k, ply in pairs( player.GetAll() ) do
if ply.BountyTable != nil then
for k, v in pairs( ply.BountyTable ) do
local allPanel = vgui.Create( "DPanel", allframe )
allPanel:SetPos( 10, 30 ) -- Set the position of the panel
allPanel:SetSize( allframe:GetWide(), 50 ) -- Set the size of the panel
local allLabel = vgui.Create( "DLabel", allPanel )
allLabel:SetPos( 10, 10 ) -- Set the position of the label
allLabel:SetText( "Bounty from "..ply:Nick().." on "..v:Nick().." of value "..v.Bounty ) -- Set the text of the label
allLabel:SizeToContents() -- Size the label to fit the text in it
allLabel:SetDark( 1 ) -- Set the colour of the text inside the label to a darker one
end
end
end
etc etc
[/code]
and in sv_bounties...
[code]
net.Receive( "BountyToAdd", function( length, client )
local addEnt = net.ReadEntity()
local intEnt = client.addEnt
local bounty = net.ReadInt( 32 )
if intEnt.Bounty == nil then
intEnt.Bounty = bounty
elseif intEnt.Bounty != nil then
intEnt.Bounty = intEnt.Bounty + bounty
end
if table.HasValue( client.BountyTable, intEnt ) == false then
table.insert( client.BountyTable, intEnt )
table.insert( MasterBountyTable, intEnt )
end
--print( "serverside bountylist is" )
--PrintTable( client.BountyTable )
--print( "addent has a bounty of", addEnt.Bounty )
net.Start( "BountyTablesAll" )
net.WriteTable( MasterBountyTable )
net.Broadcast()
end )
function GamemodeInitBounty()
MasterBountyTable = {}
end
hook.Add( "Initialize", "GamemodeInitBountyHook", GamemodeInitBounty )
[/code]
I'm having trouble because I need three things to interface all at once- the player who sets the bounty, the player the bounty is called on, and the actual bounty value.
So far I've tried stuff like giving each player on the clientside a table of all the bounties they've set, and attached an integer to each table value for the price. It seems buggy.
On the serverside, it's even worse. I've tried attaching the bountied player to the bounty setter, then attached the bounty value to the bountied player. Unsurprisingly, this returns an error :v:
It's tricky, because I can't simply add a bounty value to each player, I have to add a bounty value from each player to each player.
Ugh, sorry if this doesn't make any sense. I'm tying my brain in knots. Any ideas?
Basically, not many will just take a big chunk of code posted here and debug it for you.
Here's the logic you essentially need to follow for a hit-system.
Client:
In your VGUI when the hit is created; if the player doesn't have the cash, stop the script otherwise have it net.SendToServer( ) information about the hit such as the target, the amount, etc.
Server:
In the net.Receive function( len, ply ), read the target player, the amount, etc. Make sure that ply has the cash to pay for the hit; remove the cash here ( [I]( you may want to remove the cash from the player right now ) --if the hit is completed then you just have to add-money to the player who killed the target instead of having to check if the hirer still has the money, etc...[/I] ), if the player doesn't have cash stop the script. Once done you can net.Broadcast the hit to everyone, or just net.Send( { ply, target, unpack( hitmen-team-players ) } ); -- in either case, network whichever information such as the target, the player who wants the hit, the money... Add the hit to the server bounty-table
Client:
In the net.Receive function( len ), read the target, the hirer, the value and display a notification, or add it to the bounty table so all hits can be read.
Server:
On PlayerDeath, if the victim is a hit-target, give the killer the money if the killer is not also the victim and if the killer is a player. do a net.Broadcast to all to update the bounty-table. Notify the hirer that the hit is completed using the built-in notify system.
Client:
On net.Receive function( len ), update the bounty table and remove the completed hit.
You may choose to use 1 network message id, or different ones for each. If you choose to use 1, I recommend sending an ENUM with it such as:
HIT_CREATED = 0 ( from client to server the first time and from server to client if it passes server checks and is properly set up)
HIT_COMPLETED = 1 ( from PlayerDeath to clients to remove the hit )
[editline]13th June 2014[/editline]
[QUOTE=ChiefNinja;45095255]
I'm having trouble because I need three things to interface all at once- the player who sets the bounty, the player the bounty is called on, and the actual bounty value.
So far I've tried stuff like giving each player on the clientside a table of all the bounties they've set, and attached an integer to each table value for the price. It seems buggy.
On the serverside, it's even worse. I've tried attaching the bountied player to the bounty setter, then attached the bounty value to the bountied player. Unsurprisingly, this returns an error :v:
It's tricky, because I can't simply add a bounty value to each player, I have to add a bounty value from each player to each player.
Ugh, sorry if this doesn't make any sense. I'm tying my brain in knots. Any ideas?[/QUOTE]
1) Read through the logic; it simplifies things for you.
2) When you create manage the table, you can do something like:
[lua]table.insert( BOUNTIES_TABLE, { target = PlayerX, bounty = 10000, hiredBy = PlayerY } );[/lua]
When the hit is complete you can then remove it by searching. Alternatively you can make the table like this ( assuming only 1 hit on a person can be active at any given time. If this is the case then it can be simplified - you'll need to add logic to the first server check, check if there is a hit active on the player, if not notify just like the money check )
[lua]if ( BOUNTIES_TABLE[ target:SteamID( ) ] ) then
-- error_bounty_exists - notify and retur
else
// We can proceed
BOUNTIES_TABLE[ target:SteamID( ) ] = { bounty = 1000, hiredBy = PlayerY };
end[/lua]
Doing it this way lets you QUICKLY check the table to see if a hit exists ( you can add the error logic to server and client just like money ), on the player death you can quickly see if the player is the target without searching, etc...
3) simplify logic
4) Broadcast, then everyone shares the same bounty table. Don't forget to add logic so when a player initially joins the server, they get the bounty table too
5) simplify..
Cheers Acecool. That's really helped me think it through. Also, you can have multiple values in a single table entry? :suicide: I wish I'd found that out sooner.
As a followup question, this is the clientside code to display the master bounty table.
[code]
for k, v in pairs( ClientMasterBountyTable ) do
local allPanel = vgui.Create( "DPanel", allframe )
allPanel:SetPos( 10, 30 )
allPanel:SetSize( allframe:GetWide(), 50 )
local allLabel = vgui.Create( "DLabel", allPanel )
allLabel:SetPos( 10, 10 )
allLabel:SetText( "Bounty from "..client:Nick().." on "..target:Nick().." of value "..amount ) -- Set the text of the label
allLabel:SizeToContents()
allLabel:SetDark( 1 )
end
[/code]
from the serverside...
[code]
net.Receive( "BountyToAdd", function( length, client )
target = net.ReadEntity()
amount = net.ReadInt( 32 )
print( "net int is", net.ReadInt( 32 ), "and amount is", amount )
if client:GetMoney() >= amount then
client:AddMoney( -amount )
table.insert( MasterBountyTable, { target, amount, client } )
PrintTable( MasterBountyTable )
end
net.Start( "BountyTablesAll" )
net.WriteTable( MasterBountyTable )
net.Broadcast()
end )
[/code]
I know that the hit table gets sent to the client correctly, since PrintTable on the clientside shows that the table is the same on client and server. But creating a label with the target, client and amount throws up nil value errors. Given that the table has been sent, why can't I retrieve the pieces of data?
Thanks again.
[QUOTE=ChiefNinja;45111860]Cheers Acecool. That's really helped me think it through. Also, you can have multiple values in a single table entry? :suicide: I wish I'd found that out sooner.
As a followup question, this is the clientside code to display the master bounty table.
[code]
for k, v in pairs( ClientMasterBountyTable ) do
local allPanel = vgui.Create( "DPanel", allframe )
allPanel:SetPos( 10, 30 )
allPanel:SetSize( allframe:GetWide(), 50 )
local allLabel = vgui.Create( "DLabel", allPanel )
allLabel:SetPos( 10, 10 )
allLabel:SetText( "Bounty from "..client:Nick().." on "..target:Nick().." of value "..amount ) -- Set the text of the label
allLabel:SizeToContents()
allLabel:SetDark( 1 )
end
[/code]
from the serverside...
[code]
net.Receive( "BountyToAdd", function( length, client )
target = net.ReadEntity()
amount = net.ReadInt( 32 )
print( "net int is", net.ReadInt( 32 ), "and amount is", amount )
if client:GetMoney() >= amount then
client:AddMoney( -amount )
table.insert( MasterBountyTable, { target, amount, client } )
PrintTable( MasterBountyTable )
end
net.Start( "BountyTablesAll" )
net.WriteTable( MasterBountyTable )
net.Broadcast()
end )
[/code]
I know that the hit table gets sent to the client correctly, since PrintTable on the clientside shows that the table is the same on client and server. But creating a label with the target, client and amount throws up nil value errors. Given that the table has been sent, why can't I retrieve the pieces of data?
Thanks again.[/QUOTE]
That's a good way to exploit money on servers.
[lua]
net.Start("BountyToAdd")
net.WriteEntity(Entity(0))
net.WriteInt(-10000000, 32)
net.SendToServer()
[/lua]
You're trusting the client, never do that.
Okay... but I added
[code]
if client:GetMoney() >= amount then
client:AddMoney( -amount )
table.insert( MasterBountyTable, { target, amount, client } )
PrintTable( MasterBountyTable )
end
[/code]
to the serverside code. Shouldn't that just stop the transaction if the player's money is not sufficient?
[QUOTE=ChiefNinja;45112309]Okay... but I added
[code]
if client:GetMoney() >= amount then
client:AddMoney( -amount )
table.insert( MasterBountyTable, { target, amount, client } )
PrintTable( MasterBountyTable )
end
[/code]
to the serverside code. Shouldn't that just stop the transaction if the player's money is not sufficient?[/QUOTE]
and (amount >= 1) then
Cheers, just added that. Any insight into whats causing the nil errors when the bounty table is sent to the client?
-snip
Hmm, now whenever I start the gamemode, I'm getting the error
[code]
[Wiremod] lua/wire/server/wirelib.lua:1145: attempt to index a nil value
1. v - lua/wire/server/wirelib.lua:1145
2. unknown - lua/includes/modules/hook.lua:84
[/code]
and my initialize hook in sv_bounties doesn't seem to call, since the game tells me 'MasterBountyTable' is a nil value...
Is the lua error likely to be to do with anything I'm doing?
EDIT: Yeah, just checked, I only get the wiremod error running my gamemode, and for some reason the initialize hook isn't calling. Everything else in sv_bounties seems to work fine.
Sorry, you need to Log In to post a reply to this thread.