Ok, so Robin pointed out his morning that my chest tut isn't posted and that I probably should, so here we goes
----===== Chest Tile tutorial =====---- Difficulty: 3/5 All this tutorial will do is allow chests to be placed on tiles, display them being opened and closed, allow you to block players from opening them a second time and update the chests. This isn’t exactly based on anything else, though I did advise someone to try copying what’s already in the code I’m doing it this way because I can implement features better like this =) Overview (Written after finishing this) When this is finished you should be able to create a chest with the help of a chest editor that will display 3 item lists that will also give the name, type of item and the picture once you’ve clicked on an item. You can also set if a player is able to open the chest more then once and if the chest is edited then a player is automatically able to reopen the new chest. Only problem with this is that if you use two or more of the same chests anywhere and one is opened, they are all set as opened until the time set to close after has elapsed, same for if the player is only allowed to open the chest once, even if they open the same map in a different location it will count as the same map. ^^^ ChestEditor
^^^ Chest Tile Ok, Lets start by adding a Chest type that will basically describe a new data type and what it will store (Like chest items etc) Add the following code to the bottom of modTypes in both server and client
Code: Type ChestRec ChestName As String ' ShortHandName ItemOne As Integer ItemTwo As Integer ItemThree As Integer StayOpen As Integer ' How long it stays open (Seconds) OpenOnce As Boolean ' Weither players can open it more then once Revision As Byte ' Chest Version (If chest is editted we might want player to open it again) ' --- Open Chest Anim OpenTileX As Integer OpenTileY As Integer ' --- Closed Chest Anim ClosedTileX As Integer ClosedTileY As Integer End Type
Now we need to add a constant that dictates how many different chests can be made add one time, so in both Client and server in modConstants find’MAX_PLAYERS’ and under it, add:
Code: Public Const MAX_CHESTS = 100 Now we need to actually make the variable that will store each chests detail so find in both Server and clients Code: Public Spell(1 To MAX_SPELLS) As SpellRec And add under it add
Code: Public Chest(1 To MAX_CHESTS) As ChestRec Now, add a new form to the client, and change the following properties Name - frmChestEditor BorderStyle - 1 - Fixed Single Caption - Chest Editor StartUpPosition - 2 - Center Screen Now, add a textbox somewhere (This will be the chest name) Name - txtName Text - (empty)
Now add three ListBoxes and for each one change the properties to Name - lstItem Index - Give the 1st listbox 1 2nd listbox 2 3rd listbox 3 Add 3 Labels, each one under the Listboxes above, then Change the properties to Name - lblItemName Alignment - 2 - Center Caption - Item Name Index - 1, 2, 3 (Corresponding to the listboxs above them)
Yet again, add another 3 labels under the above labels Name - lblItemType Alignment - 2 - Center Caption - Item Type Index - 1, 2, 3 (Corresponding to the labels above them) Now we need some Picture boxes for the items to blt into, so start this off by adding three picture boxes under the label you’ve just added and change the properties to these: Name - picItem Index - 1, 2, 3 (Same as above, give it the same index as the objects above it) Height - 480 (32 pixels) Width - 480 (32 Pixels) Yay, still more to come, add a checkbox and the change the properties to; Name - chkOpenAgain Caption - Open Once Time for the objects that will help with how the long the chest will be open for =) Add a Horizontal Scrollbar (or vertical which ever you prefer) Name - scrlTime LargeChange - 100 Max - 32767 Value - 10 Min - 1 Now a label Name - lblTime Caption - 10 And a textbox Name - txtTime Text - 10 And finally add two command buttons Name - cmdSave and cmdCancel Cancel - cmdCancel = true Default - cmdSave = true
Now all you need to do is add a few labels to show what each thing does. Once you’ve done that, you should have a form that looks something like this…
Now a bit of code for the form, this is all the code that will go into this form, but it’ll do for now Double Click on the cancel button and add
Code: Unload me Unload frmTiles Basic line just closes the form and clears it from memory (It also serves to close a form I’ll be adding later) Now we need the scrollbar to change the textbox and the label that display the same value so double click the scrollbar and add
Code: ' --- Change the values for the txt and lbls lblTime.Caption = scrlTime.Value txtTime.Text = scrlTime.Value
We also need to do the same for the text box but since we cannot place a limit on the text box, we need to add some range checks and check if the number is numeric, so double click the text box and put this code in, it should be self explanatory.
Code: ' --- Check if the time entered is within range and is actually a number If Not IsNumeric(txtTime.Text) Then txtTime.Text = 10 If txtTime.Text > scrlTime.Max Then txtTime.Text = scrlTime.Max If txtTime.Text < scrlTime.Min Then txtTime.Text = scrlTime.Min ' --- Change the values for the txt and lbls lblTime.Caption = txtTime.Text scrlTime.Value = txtTime.Text
Now we need a way to activate Chest Editor so let’s start with the text command, find
Code: ' Map Editor If LCase(Mid(MyText, 1, 10)) = "/mapeditor" Then Call SendRequestEditMap MyText = "" Exit Sub End If
And below it add;
Code: ' Chest Editor If LCase(Mid(MyText, 1, 12)) = "/chesteditor" Then Call SendRequestEditChest MyText = "" Exit Sub End If
Basically, it checks if the first 12 letters = /chesteditor and if it is, it will send a packet to the server requesting chest editor and will then clear the text onscreen.
Now we need code for the server to run once it’s received the packet so find in the server:
Code: ' ::::::::::::::::::::::::::::: ' :: Request edit map packet :: ' ::::::::::::::::::::::::::::: If LCase(Parse(0)) = "requesteditmap" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If Call SendDataTo(Index, "EDITMAP" & SEP_CHAR & END_CHAR) Exit Sub End If
And under it, add;
Code: ' ::::::::::::::::::::::::::::::: ' :: Request edit Chest packet :: ' ::::::::::::::::::::::::::::::: If LCase(Parse(0)) = "requesteditchest" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If Dim packet As String packet = "CHESTEDITOR" & SEP_CHAR For n = 1 To MAX_CHESTS packet = packet & Chest(n).ChestName & SEP_CHAR Next n Call SendDataTo(Index, packet & END_CHAR) Exit Sub End If
Now, we need to allow the client to do something when we receive the packet so find in the client:
Code: ' :::::::::::::::::::: ' :: Social packets :: ' :::::::::::::::::::: If (LCase(Parse(0)) = "saymsg") Or (LCase(Parse(0)) = "broadcastmsg") Or (LCase(Parse(0)) = "globalmsg") Or (LCase(Parse(0)) = "playermsg") Or (LCase(Parse(0)) = "mapmsg") Or (LCase(Parse(0)) = "adminmsg") Then Call AddText(Parse(1), Val(Parse(2))) Exit Sub End If
And under it add:
Code: ' ::::::::::::::::::::::::: ' :: Chest editor packet :: ' ::::::::::::::::::::::::: If (LCase(Parse(0)) = "chesteditor") Then InChestEditor = True '---Update chest names For n = 1 To MAX_CHESTS Chest(n).ChestName = Parse(n + 1) Next n frmIndex.Show frmIndex.lstIndex.Clear ' Add the names For i = 1 To MAX_CHESTS frmIndex.lstIndex.AddItem i & ": " & Trim(Chest(i).ChestName) Next i frmIndex.lstIndex.ListIndex = 0 Exit Sub End If
This pretty much opens up index form and lists all the chest names down in the list and also set a flag (InChestEditor) so we know what were meant to be editing. Now, we need to declare the flag so find: (Still in the client)
Code: Public InSpellEditor As Boolean And add:
Code: Public InChestEditor As Boolean
Now you also need to find
Code: InSpellEditor = False And add:
Code: InChestEditor = False And also find:
Code: If InSpellEditor = True Then Call SendData("EDITSPELL" & SEP_CHAR & EditorIndex & SEP_CHAR & END_CHAR) End If And under that, add
Code: If InChestEditor = True Then Call SendData("EDITCHEST" & SEP_CHAR & EditorIndex & SEP_CHAR & END_CHAR) End If
Now this is the part where the server actually sends the chest details to the client for editing so find what you added earlier in the server
Code: ' ::::::::::::::::::::::::::::::: ' :: Request edit Chest packet :: ' ::::::::::::::::::::::::::::::: If LCase(Parse(0)) = "requesteditchest" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If Dim packet As String packet = "CHESTEDITOR" & SEP_CHAR For n = 1 To MAX_CHESTS packet = packet & Chest(n).ChestName & SEP_CHAR Next n Call SendDataTo(Index, packet & END_CHAR) Exit Sub End If
Now underneath it add:
Code: ' ::::::::::::::::::::::: ' :: Edit chest packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "editchest" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If ' The item # n = Val(Parse(1)) ' Prevent hacking If n < 0 Or n > MAX_CHESTS Then Call HackingAttempt(Index, "Invalid Item Index") Exit Sub End If Call AddLog(GetPlayerName(Index) & " editing item #" & n & ".", ADMIN_LOG) Call SendEditChestTo(Index, n) End If
Also find
Code: Sub SendPlayerXY(ByVal Index As Long) Dim Packet As String Packet = "PLAYERXY" & SEP_CHAR & GetPlayerX(Index) & SEP_CHAR & GetPlayerY(Index) & SEP_CHAR & END_CHAR Call SendDataTo(Index, Packet) End Sub
And add:
Code: Sub SendEditChestTo(ByVal Index As Long, ByVal ChestNum As Long) Dim Packet As String Packet = "EDITCHEST" & SEP_CHAR & ChestNum & SEP_CHAR & Trim(Chest(ChestNum).ChestName) & SEP_CHAR & Chest(ChestNum).ItemOne & SEP_CHAR & Chest(ChestNum).ItemTwo & SEP_CHAR & Chest(ChestNum).ItemThree & SEP_CHAR & Chest(ChestNum).OpenOnce & SEP_CHAR & Chest(ChestNum).StayOpen & SEP_CHAR & Chest(ChestNum).ClosedTileX & SEP_CHAR & Chest(ChestNum).ClosedTileY & SEP_CHAR & Chest(ChestNum).OpenTileX & SEP_CHAR & Chest(ChestNum).OpenTileY & SEP_CHAR & END_CHAR Call SendDataTo(Index, Packet) End Sub
The code above just sends one of the chests details to the client ready to be viewed and edited. The client needs to interpret this, so find what you did earlier:
Code: ' ::::::::::::::::::::::::: ' :: Chest editor packet :: ' ::::::::::::::::::::::::: If (LCase(Parse(0)) = "chesteditor") Then InChestEditor = True '---Update chest names For n = 1 To MAX_CHESTS Chest(n).ChestName = Parse(n + 1) Next n frmIndex.Show frmIndex.lstIndex.Clear ' Add the names For i = 1 To MAX_CHESTS frmIndex.lstIndex.AddItem i & ": " & Trim(Chest(i).ChestName) Next i frmIndex.lstIndex.ListIndex = 0 Exit Sub End If And add under it:
Code: ' ::::::::::::::::::::::: ' :: Edit Chest packet :: ' ::::::::::::::::::::::: If (LCase(Parse(0)) = "editchest") Then n = Val(Parse(1)) ' Update the chest Chest(n).ChestName = Parse(2) Chest(n).ItemOne = Val(Parse(3)) Chest(n).ItemTwo = Val(Parse(4)) Chest(n).ItemThree = Val(Parse(5)) Chest(n).OpenOnce = Parse(6) Chest(n).StayOpen = Val(Parse(7)) Chest(n).ClosedTileX = Val(Parse(8)) Chest(n).ClosedTileY = Val(Parse(9)) Chest(n).OpenTileX = Val(Parse(10)) Chest(n).OpenTileY = Val(Parse(11)) ' Initialize the chest editor Call ChestEditorInit Exit Sub End If
Now, we need to create the sub ‘ChestEditorInit’ that we’ve called in the above piece of code, all the sub does is load the details onto the Chest Editor form then loads the form, so find the sub
Code: Public Sub ItemEditorBltItem() And underneath the sub, add this sub
Code: Public Sub ChestEditorInit() Dim n As Long, i As Long ' --- Set all the basic settings frmChestEditor.txtName = Chest(EditorIndex).ChestName If Chest(EditorIndex).OpenOnce = True Then frmChestEditor.chkOpenAgain = 1 If Chest(EditorIndex).StayOpen < frmChestEditor.scrlTime.Value Then Chest(EditorIndex).StayOpen = frmChestEditor.scrlTime.Min frmChestEditor.scrlTime.Value = Chest(EditorIndex).StayOpen ' --- Add the items to the listboxes For n = 1 To 3 frmChestEditor.lstItem(n).AddItem "No Item" For i = 1 To MAX_ITEMS frmChestEditor.lstItem(n).AddItem i & ": " & Item(i).Name Next i Next n frmChestEditor.lstItem(1).ListIndex = Chest(EditorIndex).ItemOne frmChestEditor.lstItem(2).ListIndex = Chest(EditorIndex).ItemTwo frmChestEditor.lstItem(3).ListIndex = Chest(EditorIndex).ItemThree frmChestEditor.Show End Sub
Ok, now we need to add some more code to the chest editor, this new bit of code will add the ability to view item picture, name and type when we click on it. First let’s deal with getting the item name. Double click on one of the listbox on the chest editor window and add this piece of code:
Code: If frmChestEditor.lstItem(Index).ListIndex > 0 Then lblItemName(Index).Caption = Trim(Item(lstItem(Index).ListIndex).Name) Else lblItemName(Index).Caption = "N/A" End If This just checks if an item is selected, if it is, it will retrieve the item name, if not it will just replace it with N/A Now we need to change the item type label, so under Code: lblItemName(Index).Caption = Trim(Item(lstItem(Index).ListIndex).Name) (Which you just added) Add this bit:Code: Select Case Item(lstItem(Index).ListIndex).Type Case 0 lblItemType(Index).Caption = "None" Case 1 lblItemType(Index).Caption = "Weapon" Case 2 lblItemType(Index).Caption = "Armour" Case 3 lblItemType(Index).Caption = "Helmet" Case 4 lblItemType(Index).Caption = "Shield" Case 5 lblItemType(Index).Caption = "Potion Add HP" Case 6 lblItemType(Index).Caption = "Potion Add MP" Case 7 lblItemType(Index).Caption = "Potion Add SP" Case 8 lblItemType(Index).Caption = "Potion SUB HP" Case 9 lblItemType(Index).Caption = "Potion SUB MP" Case 10 lblItemType(Index).Caption = "Potion SUB SP" Case 11 lblItemType(Index).Caption = "Key" Case 12 lblItemType(Index).Caption = "Currency" Case 13 lblItemType(Index).Caption = "Spell" End Select This may look like quite a bit but its nothing really, all it is checking what type it is, and depending on the type of item, will change the label to different text Also, under the other bit you just added Code: lblItemName(Index).Caption = "N/A" Add:Code: lblItemType(Index).Caption = "N/A" TADA Now the form displays the item details, but now we can go one step better and display a picture. I’m not going to do this the normal way where another copy of the items.bmp is loaded into a picture box since that wastes so much memory considering its already loaded into a direct draw surface, so I’m going to do some blting!! YAY!
*cough* Anyway, since a lot of you don’t understand how to blt, I’m going to try explain this as easy as I can (But don’t expect miracles, since I suck at explaining things) First, we’ll start by adding a timer, that every time is set off, will draw the item back into the picture boxes, so create a timer called ‘tmrBlt’ with an interval of 50. Then add this code
Code: Dim n As Integer Dim DC As Long ' Get the DD Item surface DC DC = DD_ItemSurf.GetDC For n = 1 To 3 If lstItem(n).ListIndex > 0 Then Call BitBlt(picItem(n).hdc, 0, 0, PIC_X, PIC_Y, DC, 0, Item(lstItem(n).ListIndex).Pic * PIC_Y, SRCCOPY) Else picItem(n).Refresh End If Next n ' Not 100% sure, but it unlocks the surface after you've gotten its dc Call DD_ItemSurf.ReleaseDC(DC)
Not much difficult to understand with this code DC = DD_ItemSurf.GetDC This is like an address to the surface which we need to be able to blt something directly from the direct draw surface, only problem is that by doing this, we lock up the surface (I’ll tell you how to unlock it later) For n = 1 To 3 If lstItem(n).ListIndex > 0 Then Call BitBlt(picItem(n).hdc, 0, 0, PIC_X, PIC_Y, DC, 0, Item(lstItem(n).ListIndex).Pic * PIC_Y, SRCCOPY) Else picItem(n).Refresh End If Next n That is the main section, all it does it runs through each picture box, checks if there’s an item selected in the list box, if there isn’t, it clears the picture box to make it blank by using the refresh function. If there is an item to be bltted then it will call bitblt. The first argument, picitem(n).hdc is basically the address for the picture box that were drawing to The next two arguments are the x,y coordinates and since we want to blt it at the very top corner of the picture box, we have it as 0,0 The next two following them are the width, and height, and for both I have used a constant which at default are both 32 (Same size as the picture box) Onto the next argument, this is where we supply the address to the source, which in this case is the direct draw surface, which if you remember, we saved to the variable DC Next two arguments are the source x,y. Since we start on the very left of the item sheet, x = 0, but since y changes depending on the item, we find out what picture number the item is assigned, then we multiply by 32(pic_y) pixels to get the actual location on the items sheet The final Argument tells it what type of blt to perform, there are many different types, depending on what effects you want but this one (SRCCOPY) just copies the image directly over as it is The final piece of text (Call DD_ItemSurf.ReleaseDC(DC)) is the part that unlocks the surface we used as a source Ok so we’ve done the bltting part, but we just need to quickly edit the cancel button before we go onto Double click cancel button and add this under unload me Code: InChestEditor = False
Ok, now we need to make another addition to the chest editor. The ability to select what the chest looks like while open or closed, so open up frmChestEditor and add two more picture boxes and a command button and change properties to these: First Picture Box;- Name - picOpen Borderstyle - 0 –None Width -480 (32 pixels, or equal to pic_x) Height - 480 (32 Pixels, or equal to pic_y)
Second Picture Box;- Name - picClose Borderstyle - 0 –None Width -480 (32 pixels, or equal to pic_x) Height - 480 (32 Pixels, or equal to pic_y) Command Button;- Name -cmdTile Caption - View Tiles
Now double click cmdTile and add this code
Code: frmTiles.Show
Now we need to add a new form that will display all the tiles, don’t ask why I decided to put it in a new form, just felt like it =D
Anyway, you need to add the following controls Timer = Name – tmrBlt interval – 50 Picture Box = Name – picTiles Height – 5760 Width – 3360 ScrollBar(Best horizontal) = Name – scrlTiles Now, I’m not going to tell you what the code does that I’m about to give you since its pretty explanatory so replace all the code in frmTiles with the following
Code: Option Explicit Private Sub Form_Load() ' --- Set scrl Limits same as main mapeditor scrlTiles.Max = frmMirage.scrlPicture.Max ' --- Open it on the side of the chest editor Me.Left = frmChestEditor.Left + frmChestEditor.Width Me.top = frmChestEditor.top End Sub Private Sub picTiles_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = 1 Then ChestOpenedX = Int(X / PIC_X) ChestOpenedY = Int(Y / PIC_Y) + scrlTiles.Value ElseIf Button = 2 Then ChestClosedX = Int(X / PIC_X) ChestClosedY = Int(Y / PIC_Y) + scrlTiles.Value End If End Sub Private Sub picTiles_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = 1 Then ChestOpenedX = Int(X / PIC_X) ChestOpenedY = Int(Y / PIC_Y) + scrlTiles.Value ElseIf Button = 2 Then ChestClosedX = Int(X / PIC_X) ChestClosedY = Int(Y / PIC_Y) + scrlTiles.Value End If End Sub Private Sub tmrBlt_Timer() Dim DC As Long DC = DD_TileSurf.GetDC Call BitBlt(picTiles.hdc, 0, 0, picTiles.Width, picTiles.Height, DC, 0, scrlTiles.Value * 32, SRCCOPY) DD_TileSurf.ReleaseDC (DC) End Sub
After you’ve done the above, find the sub you added earlir called Code: ChestEditorInit and somewhere in the sub, add the following code
Code: ChestClosedX = Chest(EditorIndex).ClosedTileX ChestClosedY = Chest(EditorIndex).ClosedTileY ChestopenedX = Chest(EditorIndex).OpenTileX Chestopenedy = Chest(EditorIndex).OpenTileY
Now’s the part where we start doing something, we now have to send the update to the server, and be able to save and load the chests from file. First lets send the update to the server, double click cmdSave and input this code
Code: With Chest(EditorIndex) .ChestName = txtName.Text .ItemOne = lstItem(1).ListIndex .ItemTwo = lstItem(2).ListIndex .ItemThree = lstItem(3).ListIndex If chkOpenAgain.Value = 1 Then .OpenOnce = True Else .OpenOnce = True End If .StayOpen = scrlTime.Value .ClosedTileX = ChestClosedX .ClosedTileY = ChestClosedY .OpenTileX = ChestOpenedX .OpenTileY = ChestOpenedY End With ' Send update =D Call SendSaveChest(EditorIndex) InChestEditor = False ' Unload form and the tiles form Unload frmTiles Unload Me
All this does is update the clients version of the chest, calls the sub that will read what’s saved and send it and finally unload the windows associated with the ChestEditor by using the unload command Now for the Sub that organises the info that’s to be send, find Code: Public Sub SendSaveItem(ByVal ItemNum As Long) and under the sub add this subCode: Public Sub SendSaveChest(ByVal chestnum As Long) Dim Packet As String With Chest(chestnum) Packet = "SAVECHEST" & SEP_CHAR & chestnum & SEP_CHAR & .ChestName & SEP_CHAR & .ItemOne & SEP_CHAR & .ItemTwo & SEP_CHAR & .ItemThree & SEP_CHAR & .OpenOnce & SEP_CHAR & .StayOpen & SEP_CHAR & .ClosedTileX & SEP_CHAR & Chest(chestnum).ClosedTileY & SEP_CHAR & .OpenTileX & SEP_CHAR & .OpenTileY & SEP_CHAR & END_CHAR End With Call SendData(Packet) End Sub
Now find what you added earlier
Code: ' ::::::::::::::::::::::: ' :: Edit chest packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "editchest" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If ' The item # n = Val(Parse(1)) ' Prevent hacking If n < 0 Or n > MAX_CHESTS Then Call HackingAttempt(Index, "Invalid Item Index") Exit Sub End If Call AddLog(GetPlayerName(Index) & " editing item #" & n & ".", ADMIN_LOG) Call SendEditChestTo(Index, n) End If
And under it, add
Code: ' ::::::::::::::::::::::: ' :: Save chest packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "savechest" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If n = Val(Parse(1)) If n < 0 Or n > MAX_CHESTS Then Call HackingAttempt(Index, "Invalid Chest Index") Exit Sub End If ' Update the chest Chest(n).ChestName = Parse(2) Chest(n).ItemOne = Val(Parse(3)) Chest(n).ItemTwo = Val(Parse(4)) Chest(n).ItemThree = Val(Parse(5)) Chest(n).OpenOnce = Parse(6) Chest(n).StayOpen = Val(Parse(7)) Chest(n).ClosedTileX = Val(Parse(8)) Chest(n).ClosedTileY = Val(Parse(9)) Chest(n).OpenTileX = Val(Parse(10)) Chest(n).OpenTileY = Val(Parse(11)) ' Save it Call Savechest(n) Call AddLog(GetPlayerName(Index) & " saved Chest #" & n & ".", ADMIN_LOG) Exit Sub End If
Yay, done the majority of the work, now all we got to do is check if a player can open it and gain a prize and then close it after.
First, we need to add two temp variables to the chest type server side, so find type ChestRec in the server and in the bottom add Code: ' --- Some temp stuff AlreadyOpen As Boolean TimeOpen As Long ' Stores the tick the chest was opened at
Also, in both the client and server, find Code: Public Const TILE_TYPE_KEYOPEN = 6 And under it add
Code: Public Const TILE_TYPE_CHEST = 7
We now need to add the chest tile to map editor with a new form to allow a chest to be placed on the map, so first add an option button onto the attributes frame called optChest and double click it and add the code
Code: frmMapchest.show vbmodal
For the people who don’t know what vbmodal does, it just pretty much loads the form on top of the owner (if not specified, like here, it’s just the form it was called from) and doesn’t allow control to the form owner until the window is closed.
Ok, so make a new form and change the following properties
Name - frmMapChest BorderStyle - Fixed Style ShowInTask - False
Now add the following controls and edited properties
ListBox Name - lstChest
3 Labels Name - lblItem Index - 1,2, 3
Two command buttons Name -cmdOK and cmdCancel Cancel -cmdCancel = true Default -cmdOk = true
Ok, in this form, will be a list with chest names and every time we click on a chest name, the 3 labels will update with what the chests contain so when we load the form, we need to make a request to the server for the chest names and items so add the following to the bottom of modClientTcp
Code: Public Sub SendChestInfoRequest() Dim packet As String packet = "REQUESTCHESTINFO" & SEP_CHAR & END_CHAR Call SendData(packet) End Sub
Then in the server at the bottom of modHandleData within the main sub add the following Code: ' ::::::::::::::::::::::: ' :: Chest Info packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "requestchestinfo" Then If GetPlayerAccess(Index) < ADMIN_MAPPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If packet = "REQUESTCHESTINFO" & SEP_CHAR For n = 1 To MAX_CHESTS packet = packet & Chest(n).ChestName & SEP_CHAR & Chest(n).ItemOne & SEP_CHAR & Chest(n).ItemTwo & SEP_CHAR & Chest(n).ItemThree & SEP_CHAR Next n packet = packet & END_CHAR Call SendDataTo(Index, packet) Exit Sub End If
This doesn’t do much, if the chest packet arrives, it will create a packet consisting of chest names and the items to be sent back to the client.
Now we need to do something similar to the client, so at the bottom of modHandleData in the client (Again in the main sub) add the following Code: ' ::::::::::::::::::::::: ' :: Chest Info packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "requestchestinfo" Then i = 1 For n = 1 To MAX_CHESTS Chest(n).ChestName = Parse(i) frmMapChest.lstChest.AddItem n & ": " & Trim(Chest(n).ChestName) Chest(n).ItemOne = Val(Parse(i + 1)) Chest(n).ItemTwo = Val(Parse(i + 2)) Chest(n).ItemThree = Val(Parse(i + 3)) i = i + 4 Next n Exit Sub End If
Ok, find in the client
Code: ' Used for map key opene ditor Public KeyOpenEditorX As Long Public KeyOpenEditorY As Long
And under it add the following variable
Code: ' Used for Chest ditor Public ChestListNum As Long
Now open up the frmMapChest form again and copy and paste the following code into it
Code: Option Explicit
Private Sub cmdCancel_Click() Unload Me End Sub
Private Sub cmdOk_Click() Unload Me End Sub
Private Sub Form_Load() ' --- Update chest info Call SendChestInfoRequest End Sub
Private Sub lstChest_Click() ChestListNum = lstChest.ListIndex + 1 If Chest(lstChest.ListIndex + 1).ItemOne = 0 Then lblItem(1).Caption = "N/A" Else lblItem(1).Caption = Trim(Item(Chest(lstChest.ListIndex + 1).ItemOne).Name) End If If Chest(lstChest.ListIndex + 1).ItemTwo = 0 Then lblItem(2).Caption = "N/A" Else lblItem(2).Caption = Trim(Item(Chest(lstChest.ListIndex + 1).ItemTwo).Name) End If If Chest(lstChest.ListIndex + 1).ItemThree = 0 Then lblItem(3).Caption = "N/A" Else lblItem(3).Caption = Trim(Item(Chest(lstChest.ListIndex + 1).ItemThree).Name) End If End Sub
Again, not much interesting there so I don’t think it warrants an explanation
Ok, find Code: If frmMirage.optKeyOpen.Value = True Then .Type = TILE_TYPE_KEYOPEN .Data1 = KeyOpenEditorX .Data2 = KeyOpenEditorY .Data3 = 0 End If
And under it add
Code: If frmMirage.optChest.Value = True And ChestListNum > 0 Then .Type = TILE_TYPE_CHEST .Data1 = ChestListNum .Data2 = 0 .Data3 = 0 End If
All this bit of code does is actually turn the tile type into a chest
Find:Code: If .Type = TILE_TYPE_KEYOPEN Then Call DrawText(TexthDC, x * PIC_X + 8, y * PIC_Y + 8, "O", QBColor(White))
And under it add
Code: If .Type = TILE_TYPE_CHEST Then Call DrawText(TexthDC, x * PIC_X + 8, y * PIC_Y + 8, "C", QBColor(Yellow))
Not much here but this is still quite important, after all what are the mappers going to do if they cant see where they’ve placed the chest tiles.
I’ll go into a bit of an explanation of what this does, the first argument, TexthDC is just the ‘address’ to the surface we are going to blt the text on.
The next two arguments are the x,y arguments (no need to explain them I think)
The following argument is the text that were going to blt, hence were bltting the letter c onto the tile
The final argument is the colour, you can either use rgb() to get a colour, or use the visual basic built in ones that you can call using qbcolor()
Yay, not much to go now, all we need to do is edit a bit of code to stop players walking over chests, code to blt the chests on map and a bit more to allow players to open the chests.
Let’s start with blocking players if they try to walk over the chests
Starting Client side, find in sub CanMove
Code: If Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) - 1).Type = TILE_TYPE_BLOCKED Then
Swap that with
Code: If Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) - 1).Type = TILE_TYPE_BLOCKED Or Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) - 1).Type = TILE_TYPE_CHEST Then
Find
Code: If Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) + 1).Type = TILE_TYPE_BLOCKED Then
Swap that with
Code: If Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) + 1).Type = TILE_TYPE_BLOCKED Or Map.Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex) + 1).Type = TILE_TYPE_CHEST Then
Again, find Code: If Map.Tile(GetPlayerX(MyIndex) - 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_BLOCKED Then
And change that with
Code: If Map.Tile(GetPlayerX(MyIndex) - 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_BLOCKED Or Map.Tile(GetPlayerX(MyIndex) - 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_CHEST Then
And find Code: If Map.Tile(GetPlayerX(MyIndex) + 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_BLOCKED Then
And change that with
Code: If Map.Tile(GetPlayerX(MyIndex) + 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_BLOCKED Or Map.Tile(GetPlayerX(MyIndex) + 1, GetPlayerY(MyIndex)).Type = TILE_TYPE_CHEST Then
That change alone is enough to stop most players, but if there’s someone with a hacked, or old client, they wont be stopped so we need to change server side aswell, so find in sub PlayerMove
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_BLOCKED Then
And change that with
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_BLOCKED And Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_CHEST Then
Same with
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_BLOCKED Then
And change that with
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_BLOCKED And Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_CHEST Then
Yet again, find
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED Then
swap with
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED And Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_CHEST Then
Yet again, find
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED Then
swap with
Code: If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED And Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_CHEST Then
Ok, time for the OPENING CHEST part, woooo000ooo Server side in modHandleData locate Code: If LCase(Parse(0)) = "attack" Then and add the following code Code: ' Check for chests infront of the player Select Case GetPlayerDir(Index) Case DIR_UP If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type = TILE_TYPE_CHEST Then ' Check what chest number it is and the open it Call OpenChest(Index, Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Data1) End If Case DIR_DOWN If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type = TILE_TYPE_CHEST Then ' Check what chest number it is and the open it Call OpenChest(Index, Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Data1) End If Case DIR_LEFT If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type = TILE_TYPE_CHEST Then ' Check what chest number it is and the open it Call OpenChest(Index, Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Data1) End If Case DIR_RIGHT If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type = TILE_TYPE_CHEST Then ' Check what chest number it is and the open it Call OpenChest(Index, Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Data1) End If End Select
All this does is looks for a chest in front of the player and calls the sub that will soon do all the item giving
Now we need to create the routine that will do all the opening chest business so open up modGameLogic server side and at the bottom at Code: Public Sub OpenChest(ByVal Index As Long, ByVal ChestNum As Long) Dim FileName As String Dim Msg As String Dim OneOrMore As Boolean
If ChestNum < 1 Then Exit Sub ' --- Check if the chest is already open If Chest(ChestNum).AlreadyOpen = True Then Exit Sub FileName = App.Path & "\Accounts\" & Trim(Player(Index).Login) & ".ini" ' --- Check if players already opened it (If revision = current revision then they've opened it) If Val(GetVar(FileName, "CHEST", STR(ChestNum))) = Chest(ChestNum).Revision And Chest(ChestNum).OpenOnce Then Call PlayerMsg(Index, "The Chests still Closed tight since you last opened it!", Green) Exit Sub End If ' --- Give items to player then construct a message to send player Call GiveItem(Index, Chest(ChestNum).ItemOne, 1) Call GiveItem(Index, Chest(ChestNum).ItemTwo, 1) Call GiveItem(Index, Chest(ChestNum).ItemThree, 1)
' --- Custom message if chest is empty (You evil soul =p) If Chest(ChestNum).ItemOne = 0 And Chest(ChestNum).ItemTwo = 0 And Chest(ChestNum).ItemThree = 0 Then Call PlayerMsg(Index, "You opened the chest but it appears that its empty after all that trouble you went to!", QBColor(BrightGreen)) Else Msg = "You opened the chest and found a " If Chest(ChestNum).ItemOne > 0 Then Msg = Msg & Trim(Item(Chest(ChestNum).ItemOne).Name) ' --- So I can create a grammatically correct message OneOrMore = True End If If Chest(ChestNum).ItemTwo > 0 Then If OneOrMore Then Msg = Msg & " along with a " Msg = Msg & Trim(Item(Chest(ChestNum).ItemTwo).Name) ' --- So I can create a grammatically correct message OneOrMore = True End If If Chest(ChestNum).ItemThree > 0 Then If OneOrMore Then Msg = Msg & " and " Msg = Msg & Trim(Item(Chest(ChestNum).ItemThree).Name) End If ' --- Now update Player file to say that its been opened Call PutVar(FileName, "CHEST", STR(ChestNum), STR(Chest(ChestNum).Revision)) Call PlayerMsg(Index, Msg, BrightGreen) ' --- Set it so the chest appears open (And record time opened) Chest(ChestNum).AlreadyOpen = True Chest(ChestNum).TimeOpen = GetTickCount ' Update everyone on the map Call SendDataToMap(GetPlayerMap(Index), "CHESTOPEN" & SEP_CHAR & ChestNum & SEP_CHAR & END_CHAR) End If End Sub
Pretty basic, checks if it’s a valid chest then checks if the chest is still open from someone else. It will then follow that by checking if a user has opened it before by checking what version the chest was when they last opened it (If they have) and if it’s the same as current version, then shouldn’t allow them (that is if the chest disallows it)
Now then, we now know the user can open the chest so lets do it, first we give the user the 3 items (If one of the items is blank, I wont do anything) and following that it will try construct a message depending on how many items were collected
Need to add a quick edit to the client, find the ChestRec type and add to the bottom
Code: AlreadyOpen As Boolean
Now add the following to the bottom of the main sub in modHandleData
Code: ' ::::::::::::::::::::::: ' :: Chest Open packet :: ' ::::::::::::::::::::::: If LCase(Parse(0)) = "chestopen" Then Chest(Val(Parse(1))).AlreadyOpen = True Exit Sub End If ' :::::::::::::::::::::::: ' :: Chest Close packet :: ' :::::::::::::::::::::::: If LCase(Parse(0)) = "chestclose" Then Chest(Val(Parse(1))).AlreadyOpen = False Exit Sub End If
These two packets handle the open and closed chest packets from the server which are used for deciding if a chest is to be blted with an open or closed tile.
Now for some actual bltting, add the following sub to the bottom of modGameLogic
Code: Public Sub BltChest(ByVal ChestNum As Long, ByVal X As Long, ByVal Y As Long) Dim rSrc As RECT
With Chest(ChestNum) If .AlreadyOpen = True Then rSrc.top = .OpenTileY * PIC_Y rSrc.Bottom = rsrc.top + PIC_Y rSrc.Left = .OpenTileX * PIC_X rSrc.Right = rsrc.Left + PIC_X Call DD_BackBuffer.BltFast(X * PIC_X, Y * PIC_Y, DD_TileSurf, rSrc, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY) Else rSrc.top = .ClosedTileY * PIC_Y rSrc.Bottom = rsrc.top + PIC_Y rSrc.Left = .ClosedTileX * PIC_X rSrc.Right = rsrc.Left + PIC_X Call DD_BackBuffer.BltFast(X * PIC_X, Y * PIC_Y, DD_TileSurf, rSrc, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY) End If End With End Sub
And then find
Code: Call BltTile(X, Y) And under it add
Code: If Map.Tile(X, Y).Type = TILE_TYPE_CHEST Then Call BltChest(Map.Tile(X, Y).Data1, X, Y) End If
Now for the final bit, getting the server to close the chests so find
Code: ' Check for disconnections For i = 1 To MAX_PLAYERS If frmServer.Socket(i).State > 7 Then Call CloseSocket(i) End If Next i
And add under it
Code: ' --- Check to Close Chests For i = 1 To MAX_CHESTS If Chest(i).AlreadyOpen = True Then TotalTime = Chest(i).StayOpen TotalTime = TotalTime * 1000 TotalTime = TotalTime + Chest(i).TimeOpen If TotalTime < GetTickCount Then Chest(i).AlreadyOpen = False Call SendDataToAll("CHESTCLOSE" & SEP_CHAR & i & SEP_CHAR & END_CHAR) End If End If Next i
Also declare TotalTime at the top of the sub
That’s the tut finished, horay, not much else to say, I started rushing this towards the end since this amazingly took me 3 nights =/ (mainly because I kept watching tv, doing coursework or playing games every 10 mins)
Ok, forgot one small part that updates the chests for players, find in the serverCode: Sub SendItems(ByVal Index As Long)
And under that sub add the following sub
Code: Sub SendChests(ByVal Index As Long) Dim packet As String Dim i As Long packet = "CHESTSDETAILS" & SEP_CHAR For i = 1 To MAX_CHESTS packet = packet & Chest(i).ClosedTileX & SEP_CHAR & Chest(i).ClosedTileY & SEP_CHAR & Chest(i).OpenTileX & SEP_CHAR & Chest(i).OpenTileY & SEP_CHAR Next i Call SendDataTo(Index, packet & END_CHAR) End Sub
Also find in modHandleData in the server Code: ' Save it Call SaveChest(n) And under it addCode: Call SendDataToAll("CHESTUPDATE" & n & SEP_CHAR & Chest(n).ClosedTileX & SEP_CHAR & Chest(n).ClosedTileY & SEP_CHAR & Chest(n).OpenTileX & SEP_CHAR & Chest(n).OpenTileY & SEP_CHAR & END_CHAR)
Now add to the bottom of modHandleData in the client (Within the main sub)
Code: ' ::::::::::::::::::::::::: ' :: Chest Detail packet :: ' ::::::::::::::::::::::::: If LCase(Parse(0)) = "chestsdetails" Then i = 1 For n = 1 To MAX_CHESTS Chest(n).ClosedTileX = Val(Parse(i)) Chest(n).ClosedTileY = Val(Parse(i + 1)) Chest(n).OpenTileX = Val(Parse(i + 2)) Chest(n).OpenTileY = Val(Parse(i + 3)) i = i + 4 Next n Exit Sub End If ' ::::::::::::::::::::::::: ' :: Chest Update packet :: ' ::::::::::::::::::::::::: If LCase(Parse(0)) = "chestupdate" Then Chest(Parse(1)).ClosedTileX = Parse(2) Chest(Parse(1)).ClosedTileY = Val(Parse(3)) Chest(Parse(1)).OpenTileX = Val(Parse(4)) Chest(Parse(1)).OpenTileY = Val(Parse(5)) Exit Sub End If
Attachments: |
chestmap6as.png [ 12.97 KiB | Viewed 8524 times ]
|
chesteditoraction1hb.png [ 53.72 KiB | Viewed 8527 times ]
|
chesteditor9tw.png [ 40.58 KiB | Viewed 8526 times ]
|
|