Zeno Posted June 13, 2013 Author Share Posted June 13, 2013 ![](http://s11.postimg.org/3o3a9n0sz/tutpic.png)* * ***The skinny:** You can use this code to create chests which any player can open once. Every chest by default is closed and will Call PlayerOpenChest on the server when stepped on it. After having opened the chest, the chest will appear open to the player and have no function when stepped on (editable to a message or other function). You use the map editor to choose which chest to place, its contents, and then to place chests as tile attributes. Although this could basically be done in most of the existing event systems, I find it much more polished and easier for development to have it as a standalone.**The _technical_ skinny:** Instanced chests creates a ChestOpen=false boolean per constant numchests per player. Placing the tile_type_chest attribute on any map tile saves the chest to the server. It also removes the attribute in the previous stored map,x,y location of that chest. When rendering, if there is an ChestOpen=false chest on the map it renders with one tile; when opened, another. When PlayerMove recognizes tile_type_chest, PlayerOpenChest is called if Player(Index).ChestOpen(tile.data1)=false. Chests data is put and got from their own chest#.dat files in their own \chests\ folder.Much of this code deals with mimicking existing systems for data storage and transfer. You can very easily make these chests randomized and avoid using 2/3 of this code, notably all the creating, storing, saving, loading, and exchanging presets.* * ***Both Client- and Server-Side**The first thing we are going to add is the variables we will need to store the information about our chests. In modTypes add:>! ```Public Chest(1 To MAX_CHESTS) As ChestRec>! ``````Private Type ChestRec Type As Long Data1 As Long Data2 As Long map As Long x As Byte y As ByteEnd Type ```Then at the end, right before your End Type in Type PlayerRec add the following line:>! ``` ChestOpen(1 To MAX_CHESTS) As Boolean 'Chests``` I have included five types of chests. We will store them, the link to chests from tiles, and our maximum index of chests in modConstants:>! Add the following lines where you see fit. I recommend leaving the first as a chunk and placing the latter two with the rest of the MAX_ and TILE_TYPE constants, but this is just tidiness.>! ```'Chest constantsPublic Const NumChests As Long = 300```>! ```>! Public Const CHEST_TYPE_STAT As Byte = 0Public Const CHEST_TYPE_ITEM As Byte = 1 Public Const CHEST_TYPE_GOLD As Byte = 2 Public Const CHEST_TYPE_EXP As Byte = 3 Public Const MAX_CHESTS As Long = 200 Public Const TILE_TYPE_CHEST As Byte = 17 'change 17 to be the last tile_type# +1```Now our enumerated constants related to packet handling. Head over to modEnumerations, and at the line before CMSG_COUNT add:>! ```>! CSendChest```And right before SMSG_COUNT add:>! ```>! SPlayerOpenChestSUpdateChest``` * * ***Server-side**Because we've created the variables for chests, if we want to permanently save what we put in them we'll need to update our modDatabase. >! ```' ***********' ** Chests **' ***********Sub SaveChests() Dim i As Long>! For i = 1 To MAX_CHESTS Call SaveChest(i) Next>! End Sub>! Sub SaveChest(ByVal ChestNum As Long) Dim filename As String Dim F As Long filename = App.Path & "\data\Chests\Chest" & ChestNum & ".dat" F = FreeFile Open filename For Binary As #F Put #F, , Chest(ChestNum) Close #FEnd Sub>! Sub LoadChests() Dim filename As String Dim i As Long Dim F As Long Call CheckChests>! For i = 1 To MAX_CHESTS filename = App.Path & "\data\chests\chest" & i & ".dat" F = FreeFile Open filename For Binary As #F Get #F, , Chest(i) Close #F Next>! End Sub>! Sub CheckChests() Dim i As Long>! For i = 1 To MAX_CHESTS>! If Not FileExist("\Data\Chests\Chest" & i & ".dat") Then Call SaveChest(i) End If>! Next>! End Sub>! Sub ClearChest(ByVal Index As Long) Call ZeroMemory(ByVal VarPtr(Chest(Index)), LenB(Chest(Index)))End Sub>! Sub ClearChests() Dim i As Long>! For i = 1 To MAX_CHESTS Call ClearChest(i) Next>! End Sub ``` To finish off the saving and loading feature of chests, in modGeneral add:>! in Sub InitServer>! ```ChkDir App.Path & "\Data\", "chests">! ```in Sub ClearGameData>! ``` Call SetStatus("Clearing chests...") Call ClearChests ```and in Sub LoadGameData>! ``` Call SetStatus("Loading chests...") Call LoadChests ``` Now that we've laid the data foundation for the chests, we can create the actual interaction with the chests:>! In modPlayer sub PlayerMove anywhere in series with the similar if clauses add>! ``` 'Check to see if it's a chest If .Type = TILE_TYPE_CHEST Then PlayerOpenChest Index, .Data1 End If>! ```And then add this sub to modPlayer as well>! ```Sub PlayerOpenChest(ByVal Index As Long, ByVal ChestNum As Long)Dim n As Long If Not isPlaying(Index) Then Exit Sub 'Do nothing with chests if player has opened it. Change this to a larger if/then with the select case as an else for an effect when the chest has already been received. If Player(Index).ChestOpen(ChestNum) = True Then Exit Sub Select Case Chest(ChestNum).Type Case CHEST_TYPE_GOLD n = Chest(ChestNum).Data1 * ((100 + Player(Index).Level) / 100) GiveInvItem Index, 1, n PlayerMsg Index, "You found " & n & " gold in the chest!", Yellow Case CHEST_TYPE_ITEM GiveInvItem Index, Chest(ChestNum).Data1, Chest(ChestNum).Data2 PlayerMsg Index, "You found " & Item(Chest(ChestNum).Data1).Name & " in the chest!", Yellow Case CHEST_TYPE_EXP n = Chest(ChestNum).Data1 * (100 + rand(0, Chest(ChestNum).Data2)) / 100 SetPlayerExp Index, (GetPlayerExp(Index) + n) PlayerMsg Index, "The chest seemed empty, or was it? You gain " & n & " experience!", Yellow Case CHEST_TYPE_STAT Player(Index).Points = Player(Index).Points + 1 PlayerMsg Index, "The chest seemed empty, or was it? You gained a stat point!", Yellow End Select Player(Index).ChestOpen(ChestNum) = True SendPlayerOpenChest Index, ChestNum>! End Sub>! ```And this in sub JoinGame, also modPlayer:>! ``` Call SendChests(Index)>! ``` The server has two more parts - now to add the packet handling:>! In modServerTCP add these four subs anywhere:>! ```Sub SendPlayerOpenChests(ByVal Index As Long)Dim i As Long For i = 1 To MAX_CHESTS If Player(Index).ChestOpen(i) = True Then SendPlayerOpenChest Index, i NextEnd Sub>! Sub SendPlayerOpenChest(ByVal Index As Long, ByVal ChestNum As Long) Dim buffer As clsBuffer Set buffer = New clsBuffer buffer.WriteLong SPlayerOpenChest buffer.WriteLong ChestNum SendDataTo Index, buffer.ToArray() Set buffer = NothingEnd Sub>! ```>! ```>! Sub SendUpdateChestTo(ByVal Index As Long, ByVal ChestNum As Long) Dim buffer As clsBuffer>! Set buffer = New clsBuffer buffer.WriteLong SUpdateChest buffer.WriteLong ChestNum buffer.WriteLong Chest(ChestNum).Type buffer.WriteLong Chest(ChestNum).Data1 buffer.WriteLong Chest(ChestNum).Data2buffer.WriteLong Chest(ChestNum).Mapbuffer.WriteByte Chest(ChestNum).xbuffer.WriteByte Chest(ChestNum).y SendDataTo Index, buffer.ToArray() Set buffer = NothingEnd Sub>! Sub SendUpdateChestToAll(ByVal ChestNum As Long) Dim buffer As clsBuffer>! Set buffer = New clsBuffer buffer.WriteLong SUpdateChest buffer.WriteLong ChestNum buffer.WriteLong Chest(ChestNum).Type buffer.WriteLong Chest(ChestNum).Data1 buffer.WriteLong Chest(ChestNum).Data2buffer.WriteLong Chest(ChestNum).Mapbuffer.WriteByte Chest(ChestNum).xbuffer.WriteByte Chest(ChestNum).y SendDataToAll buffer.ToArray() Set buffer = NothingEnd Sub```>! >! In modHandleData add anywhere:>! ```Sub HandleSaveChest(ByVal Index As Long, ByRef data() As Byte, ByVal StartAddr As Long, ByVal ExtraVar As Long) Dim buffer As clsBuffer, n As Long>! ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Exit Sub End If Set buffer = New clsBuffer buffer.WriteBytes data() n = buffer.ReadLong If n < 1 Or n > MAX_CHESTS Then Exit Sub>! ```'Remove previous instance If Chest(n).map > 0 Then map(Chest(n).map).Tile(Chest(n).x, Chest(n).y).Type = 0>! 'Update chest Chest(n).Type = buffer.ReadLong Chest(n).Data1 = buffer.ReadLong Chest(n).Data2 = buffer.ReadLong>! Chest(n).Map = buffer.ReadLong Chest(n).x = buffer.ReadByte Chest(n).y = buffer.ReadByte Set buffer = Nothing Call SendUpdateChestToAll(n) Call SaveChest(n) Call AddLog(GetPlayerName(Index) & " saving Chest #" & n & ".", ADMIN_LOG) End Sub>! And anywhere in modHandleData sub InitMessages add:>! ``` HandleDataSub(CSendChest) = GetAddress(AddressOf HandleSaveChest)``` * * ***Client-side**.in modGlobals add >! ```Public EditorChestType As Byte>! ``` in modGameEditors >! Anywhere in Sub MapEditorInit>! ``` 'set chest array frmEditor_Map.cmbChestIndex.Clear For i = 1 To MAX_CHESTS frmEditor_Map.cmbChestIndex.AddItem "Chest: " & i Next>! ```And within the With Map.Tile in subMapEditorMouseDown>! If frmEditor_Map.optChest.value Then 'Data1 has to go first because the renderer likes to jump in and give RTE9 .data1 = frmEditor_Map.cmbChestIndex.ListIndex + 1 .Type = TILE_TYPE_CHEST .Data2 = 0 .Data3 = 0 .Data4 = "" 'Map data must be sent to the server so that any old chest could be have the tile_type removed With Chest(.data1) .Map = Player(MyIndex).Map .x = CurX .y = CurY End With SendChest (.data1) End If Now for the graphics, in modGraphics:>! ```Public Sub DrawChest(ByVal x As Long, ByVal y As Long, ByVal Opened As Boolean)Dim rec As RECTDim i As Long>! ' If debug mode, handle error then exit out If Options.Debug = 1 Then On Error GoTo ErrorHandler If Opened = False Then RenderTexture Tex_Tileset(108), ConvertMapX(x * PIC_X), ConvertMapY(y * PIC_Y), 0, 96, 32, 32, 32, 32 Else RenderTexture Tex_Tileset(108), ConvertMapX(x * PIC_X), ConvertMapY(y * PIC_Y), 128, 96, 32, 32, 32, 32 End If ' Error handler Exit SubErrorHandler: HandleError "DrawChest", "modGraphics", Err.Number, Err.Description, Err.Source, Err.HelpContext Err.Clear Exit SubEnd Sub>! ```And first thing in the y-based render in sub Render_Graphics:>! ``` For x = 0 To Map.MaxX If Map.Tile(x, y).Type = TILE_TYPE_CHEST Then If Player(MyIndex).ChestOpen(Map.Tile(x, y).data1) = False Then DrawChest x, y, False Else DrawChest x, y, True End If End If Next>! ``` Now the packet handling of the client:>! In modHandleData Sub InitMessages add:>! ``` HandleDataSub(SPlayerOpenChest) = GetAddress(AddressOf HandlePlayerOpenChest) HandleDataSub(SUpdateChest) = GetAddress(AddressOf HandleUpdateChest)>! ```and then add these two subs anywhere in that module:>! ```Private Sub HandlePlayerOpenChest(ByVal Index As Long, ByRef data() As Byte, ByVal StartAddr As Long, ByVal ExtraVar As Long)Dim i As LongDim buffer As clsBuffer>! ' If debug mode, handle error then exit out If Options.Debug = 1 Then On Error GoTo ErrorHandler Set buffer = New clsBuffer buffer.WriteBytes data() i = buffer.ReadLong Player(Index).ChestOpen(i) = True ' Error handler Exit SubErrorHandler: HandleError "HandlePlayerOpenChest", "modHandleData", Err.Number, Err.Description, Err.Source, Err.HelpContext Err.Clear Exit SubEnd Sub>! ```>! ```>! Private Sub HandleUpdateChest(ByVal Index As Long, ByRef data() As Byte, ByVal StartAddr As Long, ByVal ExtraVar As Long)Dim n As LongDim buffer As clsBuffer>! ' If debug mode, handle error then exit out If Options.Debug = 1 Then On Error GoTo ErrorHandler Set buffer = New clsBuffer buffer.WriteBytes data() n = buffer.ReadLong Chest(n).Type = buffer.ReadLong Chest(n).data1 = buffer.ReadLong Chest(n).Data2 = buffer.ReadLong Chest(n).Map = buffer.ReadLong Chest(n).x = buffer.ReadByte Chest(n).y = buffer.ReadByte>! ' Error handler Exit SubErrorHandler: HandleError "HandleUpdateChest", "modHandleData", Err.Number, Err.Description, Err.Source, Err.HelpContext Err.Clear Exit SubEnd Sub```>! >! And now add this anywhere in modClientTCP>! ```Public Sub SendChest(ByVal Index As Long)Dim buffer As clsBuffer>! ' If debug mode, handle error then exit out If Options.Debug = 1 Then On Error GoTo ErrorHandler Set buffer = New clsBuffer buffer.WriteLong CSendChest buffer.WriteLong Index buffer.WriteLong Chest(Index).Type buffer.WriteLong Chest(Index).data1 buffer.WriteLong Chest(Index).Data2 buffer.WriteLong Chest(Index).Map buffer.WriteByte Chest(Index).x buffer.WriteByte Chest(Index).y SendData buffer.ToArray() Set buffer = Nothing ' Error handler Exit SubErrorHandler: HandleError "SendChest", "modClientTCP", Err.Number, Err.Description, Err.Source, Err.HelpContext Err.Clear Exit SubEnd Sub``` Now do a bit of form work in frmEditor_Map:>! As you can see, I've got a couple other chest types that you don't have. Don't use those unless you also happen to have jexp and cards.>! ![](http://s13.postimg.org/i0awnlq87/pictut2.jpg)>! And add the following code into frmEditor_Map:>! >! ```Private Sub cmbChestIndex_Click()Dim n As Long 'prevent rte9n = cmbChestIndex.ListIndex + 1 optChestType(Chest(n).Type).value = True If Chest(n).data1 > 0 Then txtChestData1.text = str(Chest(n).data1) If Chest(n).Data2 > 0 Then txtChestData2.text = str(Chest(n).Data2)End Sub>! Private Sub cmdChestCancel_Click() picAttributes.Visible = False fraChest.Visible = FalseEnd Sub>! Private Sub cmdChestOK_Click()Dim n As Longn = cmbChestIndex.ListIndex + 1If n < 1 Or n > MAX_CHESTS Then Exit Sub If Options.Debug = 1 Then On Error GoTo ErrorHandler Chest(n).Type = EditorChestType Chest(n).data1 = Val(txtChestData1.text) Chest(n).Data2 = Val(txtChestData2.text) fraChest.Visible = False picAttributes.Visible = False>! ErrorHandler: HandleError "cmdChestOK_Click", "frmEditor_Map", Err.Number, Err.Description, Err.Source, Err.HelpContext Err.Clear Exit SubEnd Sub>! Private Sub optChest_Click() picAttributes.Visible = True fraChest.Visible = TrueEnd Sub>! Private Sub optChestType_Click(Index As Integer) EditorChestType = IndexEnd Sub>! ``` And that should be it! Works for me and hopefully I didn't skip anything.For DX7 users, the switch between Render and Blt isn't too bad. Using the same logic of the if statement in DrawChest, make a BltChest sub and look at how other procedures use blitting.The graphics (tileset, x, and y values) for the two tiles for the chest in DrawChest just happen to be working numbers for me. You may need to change these to suit your tileset. The tileset value is its numeric filename. You can get the x,y of a tile easily by:* open your tileset in Paint* hover over a pixel on the tile you're looking at* Divide the x coordinate and y coordinate values by 32* drop the remainder (always round down) to get your x,y value for the tilesetHave fun. Happy dungeon digging.Edit: Fixed a couple little things. Link to comment Share on other sites More sharing options...
abhi2011 Posted June 13, 2013 Share Posted June 13, 2013 This can be done with the Event System. But since this would be comparatively faster I am gonna go with this. Link to comment Share on other sites More sharing options...
Zeno Posted June 13, 2013 Author Share Posted June 13, 2013 > Although this could basically be done in most of the existing event systems, I find it much more polished and easier for development to have it as a standalone.> This can be done with the Event System. But since this would be comparatively faster I am gonna go with this.Basically, yeah haha. I'd recommend the event system for anyone wanting only a couple chests. I'm not sure if you could make the tile different depending on who is looking at it. Either way, it quickly becomes very inefficient to create a script for every chest! Link to comment Share on other sites More sharing options...
abhi2011 Posted June 13, 2013 Share Posted June 13, 2013 > Basically, yeah haha. I'd recommend the event system for anyone wanting only a couple chests. I'm not sure if you could make the tile different depending on who is looking at it. Either way, it quickly becomes very inefficient to create a script for every chest!For the tile, you could make a var like chest opened or something. And just render accordingly. (If thats what you mean.) Link to comment Share on other sites More sharing options...
Zeno Posted June 13, 2013 Author Share Posted June 13, 2013 Yeah, I'm just unsure whether the events system can alter renders, other than shared values for sprites. Don't really know a lot about it… vb6 is easier than event system imo. Link to comment Share on other sites More sharing options...
abhi2011 Posted June 14, 2013 Share Posted June 14, 2013 > Yeah, I'm just unsure whether the events system can alter renders, other than shared values for sprites. Don't really know a lot about it… vb6 is easier than event system imo.I actually meant for coding. But yes, vb6 > event system. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now