• Bounty system
    8 replies, posted
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.