Mirage Source http://web.miragesource.net/forums/ |
|
Achilles System (Item Required To Kill NPC) http://web.miragesource.net/forums/viewtopic.php?f=210&t=674 |
Page 1 of 1 |
Author: | Obsidian [ Sun Oct 22, 2006 5:45 am ] |
Post subject: | Achilles System (Item Required To Kill NPC) |
Difficulty: 1/5 - I Walked Through Even The Simplest of Code so Anyone Can Add This Code. Just Make Sure you Read!!!!!! First off, lets start with the most obvious two variables that needed to be added, are they (and by they, i mean the npc) achilles, and if they are, what weapon is their weakness? So on the Client and Server Add the Following Two Variables to the NPCRec Code: IsAchilles as Byte ' This is a Yes/No and it saves more space to use a byte, not a boolean Achilles as Long ' This will be the ItemNum that is able to defeat them Now since the "common" piece of the puzzle has been added, lets start with the client Code: ::::::::::::::::: :: Client Side :: ::::::::::::::::: Now That We Have That Setup, lets open frmNpcEditor and get the system ready to go. First add a checkbox to the form, and name it chkAchilles. Next, make a Combo Box, and name it cmbAchilles. In the Object Properties for the combo box you just created, make sure you set the "Style" to "2 - Dropdown List". Also, make sure that the Combo box is not enabled (in the properties list, set Enabled to false). Now, if you click twice on the Checkbox that you just created (chkAchilles) it should open your code and create a new sub, called Code: Private Sub chkAchilles_Click() End Sub If it does NOT create this sub, then near the top, just below the menu toolbar, you will see two drop down lists, one that says chkAchilles and the other may say something like Double Click, all you have to do is drop-down that list and select "Click", when you do, it will create the sub for you, or you can copy/paste the one from the code i provided above. Now in that sub, if the box is checked, we want the combo box to be enabled, so the admin can actually select which weapon is going to be used to kill this particular NPC. So in the sub we add this code Code: If chkAchilles.Value = 1 Then cmbAchilles.Enabled = True Else cmbAchilles.Enabled = False End If If the box is checked, it enables it, if the box isn't checked, it disables it. That's Simple enough, right? Moving along, we need to tell the Editor How it should handle the variable after it is recieved or before it is sent. So search for the Sub "NpcEditorInit". After locating the sub, which should look similar to the following Code: On Error Resume Next frmNpcEditor.picSprites.Picture = LoadPicture(App.Path & "\sprites.bmp") frmNpcEditor.txtName.Text = Trim(Npc(EditorIndex).Name) frmNpcEditor.txtAttackSay.Text = Trim(Npc(EditorIndex).AttackSay) frmNpcEditor.scrlSprite.Value = Npc(EditorIndex).Sprite frmNpcEditor.txtSpawnSecs.Text = STR(Npc(EditorIndex).SpawnSecs) frmNpcEditor.cmbBehavior.ListIndex = Npc(EditorIndex).Behavior frmNpcEditor.scrlRange.Value = Npc(EditorIndex).Range frmNpcEditor.txtChance.Text = STR(Npc(EditorIndex).DropChance) frmNpcEditor.scrlNum.Value = Npc(EditorIndex).DropItem frmNpcEditor.scrlValue.Value = Npc(EditorIndex).DropItemValue frmNpcEditor.scrlSTR.Value = Npc(EditorIndex).STR frmNpcEditor.scrlDEF.Value = Npc(EditorIndex).DEF frmNpcEditor.scrlSPEED.Value = Npc(EditorIndex).SPEED frmNpcEditor.scrlMAGI.Value = Npc(EditorIndex).MAGI frmNpcEditor.Show vbModal The first thing you want to do is at the top, below the "On Error Resume Next", add Code: dim i as long you may also want to remove the On Error Resume Next (it's problematic when trying to debug, but unrelated to what we're doing, you can leave it if you like). Now, below the line Code: frmNpcEditor.scrlMAGI.Value = Npc(EditorIndex).MAGI we need to add Code: frmNpcEditor.chkAchilles.Value = Npc(EditorIndex).IsAchilles frmNpcEditor.cmbAchilles.clear for i = 1 to max_items If item(i).type = ITEM_TYPE_WEAPON Then frmnpceditor.cmbachilles.additem i & ": " & Trim$(Item(i).Name) Else frmnpceitor.cmbachilles.additem i & ":" end if next i frmNpcEditor.cmbAchilles.ListIndex = Npc(EditorIndex).Achilles If Npc(EditorIndex).IsAchilles = 1 then frmNpcEditor.cmbAchilles.Enabled = True Else frmNpcEditor.cmbAchilles.Enabled = False End if Let Me Break This Down Line by Line, The First Line, will either Check, or Uncheck the CheckBox we created Earlier, if the enemy was already saved, and that box had been checked. Next, the Combo Box we created needed to be cleared, so we created the line to empty the box. The Next 3 Lines are what's called a For Loop. This loop (which starts at 1), will go through, and add everything contained in it, until it reaches its max value, in this case, the value for MAX_ITEMS, which is by default, 255. (i.e., It will start at #1, and then it will start going until it finds the "next", once it finds the next, it goes back to the beginning, and then does the same thing for #2, all of the way up until the maximum value that the user specifies. If you have trouble understanding the concept of For Loops, i'm sure there are several members here who are more qualified than i am to explain it to you.) The Line Within The For Loop, frmnpceditor.cmbachilles.additem i & ": " & Trim$(Item(i).Name), simply adds the number that the loop is currently on, a semicolon and a space, and then the name of the item that the loop is on (e.g., if we're on loop number one, it would add this to the combo box.... 1: ItemOneName) Now, all of that will happen when the editor loads, but what about after we're done? The Sub Below our Current Sub Should Be Code: Public Sub NpcEditorOk() If it is not, you can search for it. In The Sub, Just before the line that reads Code: Call SendSaveNpc(EditorIndex) Add these two new lines Code: Npc(EditorIndex).IsAchilles = frmNpcEditor.chkAchilles.Value Npc(EditorIndex).Achilles = frmNpcEditor.cmbAchilles.listindex + 1 Your New Sub Should Look Liek This Code: Public Sub NpcEditorOk() Npc(EditorIndex).Name = frmNpcEditor.txtName.Text Npc(EditorIndex).AttackSay = frmNpcEditor.txtAttackSay.Text Npc(EditorIndex).Sprite = frmNpcEditor.scrlSprite.Value Npc(EditorIndex).SpawnSecs = Val(frmNpcEditor.txtSpawnSecs.Text) Npc(EditorIndex).Behavior = frmNpcEditor.cmbBehavior.ListIndex Npc(EditorIndex).Range = frmNpcEditor.scrlRange.Value Npc(EditorIndex).DropChance = Val(frmNpcEditor.txtChance.Text) Npc(EditorIndex).DropItem = frmNpcEditor.scrlNum.Value Npc(EditorIndex).DropItemValue = frmNpcEditor.scrlValue.Value Npc(EditorIndex).STR = frmNpcEditor.scrlSTR.Value Npc(EditorIndex).DEF = frmNpcEditor.scrlDEF.Value Npc(EditorIndex).SPEED = frmNpcEditor.scrlSPEED.Value Npc(EditorIndex).MAGI = frmNpcEditor.scrlMAGI.Value Npc(EditorIndex).IsAchilles = frmNpcEditor.chkAchilles.Value Npc(EditorIndex).Achilles = frmNpcEditor.cmbAchilles.listindex + 1 Call SendSaveNpc(EditorIndex) InNpcEditor = False Unload frmNpcEditor End Sub That sets up the tone for our next stanza. Right now that NPC according to the client has that information, but it isn't being sent to the server to be stored, so we need to do that so find Sub SendSaveNpc(ByVal NpcNum As Long). When you've located it, you'll see a long line of code that's preceded by a "Packet =", this whole sub is the code that sends all of your NPC information to the server to be saved! *Brief Explanation of Mirage's Packet System*, each packet is sent as a string. When the packet is recieved, it is divided up, based on the location of the "SEPERATOR", and then it continues to the next portion of the packet, until it reaches the "ENDING". This are defined in Mirage as a Single Character (Char), and Look like the following: SEPERATOR: SEP_CHAR ENDING: END_CHAR If you do not seperate each value, when your packet is recieved by the server, it will "slur" it all together, so it is vital that you seperate each value, and have a seperator, just before the end of the packet. Now That We Understand how Mirage uses packets, we can knock this out easily, right? our original packet looks similar to this: Code: Packet = "SAVENPC" & SEP_CHAR & NpcNum & SEP_CHAR & Trim(Npc(NpcNum).Name) & SEP_CHAR & Trim(Npc(NpcNum).AttackSay) & SEP_CHAR & Npc(NpcNum).Sprite & SEP_CHAR & Npc(NpcNum).SpawnSecs & SEP_CHAR & Npc(NpcNum).Behavior & SEP_CHAR & Npc(NpcNum).Range & SEP_CHAR & Npc(NpcNum).DropChance & SEP_CHAR & Npc(NpcNum).DropItem & SEP_CHAR & Npc(NpcNum).DropItemValue & SEP_CHAR & Npc(NpcNum).STR & SEP_CHAR & Npc(NpcNum).DEF & SEP_CHAR & Npc(NpcNum).SPEED & SEP_CHAR & Npc(NpcNum).MAGI & SEP_CHAR & END_CHAR all we have to do, is before the packet is ended, add our two new variables (IsAchilles, and Achilles)! So the new packet should look similar to this: Code: Packet = "SAVENPC" & SEP_CHAR & NpcNum & SEP_CHAR & Trim(Npc(NpcNum).Name) & SEP_CHAR & Trim(Npc(NpcNum).AttackSay) & SEP_CHAR & Npc(NpcNum).Sprite & SEP_CHAR & Npc(NpcNum).SpawnSecs & SEP_CHAR & Npc(NpcNum).Behavior & SEP_CHAR & Npc(NpcNum).Range & SEP_CHAR & Npc(NpcNum).DropChance & SEP_CHAR & Npc(NpcNum).DropItem & SEP_CHAR & Npc(NpcNum).DropItemValue & SEP_CHAR & Npc(NpcNum).STR & SEP_CHAR & Npc(NpcNum).DEF & SEP_CHAR & Npc(NpcNum).SPEED & SEP_CHAR & Npc(NpcNum).MAGI & SEP_CHAR & Npc(NpcNum).IsAchilles & SEP_CHAR & Npc(NpcNum).Achilles & SEP_CHAR & END_CHAR Now, we have the packet actually being sent by the client, but the server has no idea how to handle it correctly since we added new variables to it! Code: ::::::::::::::::: :: Server Side :: ::::::::::::::::: Lets update that packet and make sure it works!!! *Another Brief Mirage Packet Explanation* When a packet is recieved, the only way that it can be understood is to read the "name" of the packet and seperate them out. If you look on your client, the name of the packet was "savenpc" (including quotes). When the packets are divided up as mentioned earlier, it starts at 0 (e.g., packet = "savenpc" & sep_char ......... the first thing to show up, is the packet name, and since it starts at 0, this is part 0 of the packet). Well that's all well and fine, but why was that relevant? Because now we have to update our "savenpc" on the server. so do a search for "savenpc", you're looking for the line of code that looks like: if lcase(parse(0)) = "savenpc" then once you've found that line you'll be looking at a code similar to this: Code: If LCase(Parse(0)) = "savenpc" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_DEVELOPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If n = Val(Parse(1)) ' Prevent hacking If n < 0 Or n > MAX_NPCS Then Call HackingAttempt(Index, "Invalid NPC Index") Exit Sub End If ' Update the npc Npc(n).Name = Parse(2) Npc(n).AttackSay = Parse(3) Npc(n).Sprite = Val(Parse(4)) Npc(n).SpawnSecs = Val(Parse(5)) Npc(n).Behavior = Val(Parse(6)) Npc(n).Range = Val(Parse(7)) Npc(n).DropChance = Val(Parse(8)) Npc(n).DropItem = Val(Parse(9)) Npc(n).DropItemValue = Val(Parse(10)) Npc(n).STR = Val(Parse(11)) Npc(n).DEF = Val(Parse(12)) Npc(n).SPEED = Val(Parse(13)) Npc(n).MAGI = Val(Parse(14)) ' Save it Call SendUpdateNpcToAll(n) Call SaveNpc(n) Call AddLog(GetPlayerName(Index) & " saved npc #" & n & ".", ADMIN_LOG) Exit Sub End If now, we added our new pieces to the packet after MAGI, remember? So all we have to do now is add these below magi Code: npc(n).IsAchilles = Val(Parse(15)) Npc(n).Achilles = Val(Parse(16)) So your result should be Code: If LCase(Parse(0)) = "savenpc" Then ' Prevent hacking If GetPlayerAccess(Index) < ADMIN_DEVELOPER Then Call HackingAttempt(Index, "Admin Cloning") Exit Sub End If n = Val(Parse(1)) ' Prevent hacking If n < 0 Or n > MAX_NPCS Then Call HackingAttempt(Index, "Invalid NPC Index") Exit Sub End If ' Update the npc Npc(n).Name = Parse(2) Npc(n).AttackSay = Parse(3) Npc(n).Sprite = Val(Parse(4)) Npc(n).SpawnSecs = Val(Parse(5)) Npc(n).Behavior = Val(Parse(6)) Npc(n).Range = Val(Parse(7)) Npc(n).DropChance = Val(Parse(8)) Npc(n).DropItem = Val(Parse(9)) Npc(n).DropItemValue = Val(Parse(10)) Npc(n).STR = Val(Parse(11)) Npc(n).DEF = Val(Parse(12)) Npc(n).SPEED = Val(Parse(13)) Npc(n).MAGI = Val(Parse(14)) Npc(n).IsAchilles = Val(Parse(15)) Npc(n).Achilles = Val(Parse(16)) ' Save it Call SendUpdateNpcToAll(n) Call SaveNpc(n) Call AddLog(GetPlayerName(Index) & " saved npc #" & n & ".", ADMIN_LOG) Exit Sub End If Now the server recieved the data, but it wasn't saved, yet. So find Code: Sub SaveNpc(ByVal NpcNum As Long) And we have to add our new information again. Can you guess where? If you said, below magi, that is correct. The Sub (after you add the new code) should look like this: Code: Sub SaveNpc(ByVal NpcNum As Long) Dim FileName As String FileName = App.Path & "\npcs.ini" Call PutVar(FileName, "NPC" & NpcNum, "Name", Trim(Npc(NpcNum).Name)) Call PutVar(FileName, "NPC" & NpcNum, "AttackSay", Trim(Npc(NpcNum).AttackSay)) Call PutVar(FileName, "NPC" & NpcNum, "Sprite", Trim(Npc(NpcNum).Sprite)) Call PutVar(FileName, "NPC" & NpcNum, "SpawnSecs", Trim(Npc(NpcNum).SpawnSecs)) Call PutVar(FileName, "NPC" & NpcNum, "Behavior", Trim(Npc(NpcNum).Behavior)) Call PutVar(FileName, "NPC" & NpcNum, "Range", Trim(Npc(NpcNum).Range)) Call PutVar(FileName, "NPC" & NpcNum, "DropChance", Trim(Npc(NpcNum).DropChance)) Call PutVar(FileName, "NPC" & NpcNum, "DropItem", Trim(Npc(NpcNum).DropItem)) Call PutVar(FileName, "NPC" & NpcNum, "DropItemValue", Trim(Npc(NpcNum).DropItemValue)) Call PutVar(FileName, "NPC" & NpcNum, "STR", Trim(Npc(NpcNum).STR)) Call PutVar(FileName, "NPC" & NpcNum, "DEF", Trim(Npc(NpcNum).DEF)) Call PutVar(FileName, "NPC" & NpcNum, "SPEED", Trim(Npc(NpcNum).SPEED)) Call PutVar(FileName, "NPC" & NpcNum, "MAGI", Trim(Npc(NpcNum).MAGI)) Call PutVar(FileName, "NPC" & NpcNum, "ISACHILLES", Trim(Npc(NpcNum).IsAchilles)) Call PutVar(FileName, "NPC" & NpcNum, "ACHILLES", Trim(Npc(NpcNum).Achilles)) End Sub So what we have so far is... It's Loaded On The Editor Sent From The Editor Recieved By The Server Saved By The Server There are three more things we need to do before we are done. Load it on the server, send it to the client, and then write the actual code to keep the npc from being killed. First let's load it, that's pretty simple.Still on the server, find this sub Code: Sub LoadNpcs() yes... this sub also has an on error resume next, you can remove it if you'd like, it may be problematic if you have to debug later. Again, that part is your choice. now we have to tell it to load our variables... so add it below magi again! the result (after you add the variables) should look like this: Code: Sub LoadNpcs() On Error Resume Next Dim FileName As String Dim i As Long Call CheckNpcs FileName = App.Path & "\npcs.ini" For i = 1 To MAX_NPCS Npc(i).Name = GetVar(FileName, "NPC" & i, "Name") Npc(i).AttackSay = GetVar(FileName, "NPC" & i, "AttackSay") Npc(i).Sprite = GetVar(FileName, "NPC" & i, "Sprite") Npc(i).SpawnSecs = GetVar(FileName, "NPC" & i, "SpawnSecs") Npc(i).Behavior = GetVar(FileName, "NPC" & i, "Behavior") Npc(i).Range = GetVar(FileName, "NPC" & i, "Range") Npc(i).DropChance = GetVar(FileName, "NPC" & i, "DropChance") Npc(i).DropItem = GetVar(FileName, "NPC" & i, "DropItem") Npc(i).DropItemValue = GetVar(FileName, "NPC" & i, "DropItemValue") Npc(i).STR = GetVar(FileName, "NPC" & i, "STR") Npc(i).DEF = GetVar(FileName, "NPC" & i, "DEF") Npc(i).SPEED = GetVar(FileName, "NPC" & i, "SPEED") Npc(i).MAGI = GetVar(FileName, "NPC" & i, "MAGI") Npc(i).IsAchilles = GetVar(FileName, "NPC" & i, "ISACHILLES") Npc(i).Achilles = GetVar(FileName, "NPC" & i, "ACHILLES") DoEvents Next i End Sub Now, let's add the code to tell the server to send the NPC Information to the client, and the client understand it. So Find this Sub Code: Sub SendEditNpcTo(ByVal Index As Long, ByVal NpcNum As Long) Remember how we managed the packet earlier? Same concept here, at the end, before the end_char, we need to add our new variables accordingly. The Final Result after adding the variables should look like this: Code: Sub SendEditNpcTo(ByVal Index As Long, ByVal NpcNum As Long) Dim Packet As String Packet = "EDITNPC" & SEP_CHAR & NpcNum & SEP_CHAR & Trim(Npc(NpcNum).Name) & SEP_CHAR & Trim(Npc(NpcNum).AttackSay) & SEP_CHAR & Npc(NpcNum).Sprite & SEP_CHAR & Npc(NpcNum).SpawnSecs & SEP_CHAR & Npc(NpcNum).Behavior & SEP_CHAR & Npc(NpcNum).Range & SEP_CHAR & Npc(NpcNum).DropChance & SEP_CHAR & Npc(NpcNum).DropItem & SEP_CHAR & Npc(NpcNum).DropItemValue & SEP_CHAR & Npc(NpcNum).STR & SEP_CHAR & Npc(NpcNum).DEF & SEP_CHAR & Npc(NpcNum).SPEED & SEP_CHAR & Npc(NpcNum).MAGI & & SEP_CHAR & Npc(NpcNum).IsAchilles & SEP_CHAR & Npc(NpcNum).Achilles & SEP_CHAR & END_CHAR Call SendDataTo(Index, Packet) End Sub Now it's sent to the client, but the client doesn't manage it correctly yet, so lets find it and fix it for the client. Code: ::::::::::::::::: :: Client Side :: ::::::::::::::::: Search for the line of code (remember, the packet name) that looks like this: Code: if lcase(parse(0)) = "editnpc" Now all we have to do is update that packet to work correctly, the final result should look like this: Code: ' ::::::::::::::::::::: ' :: Edit npc packet :: <- Used for item editor admins only ' ::::::::::::::::::::: If (LCase(Parse(0)) = "editnpc") Then n = Val(Parse(1)) ' Update the npc Npc(n).Name = Parse(2) Npc(n).AttackSay = Parse(3) Npc(n).Sprite = Val(Parse(4)) Npc(n).SpawnSecs = Val(Parse(5)) Npc(n).Behavior = Val(Parse(6)) Npc(n).Range = Val(Parse(7)) Npc(n).DropChance = Val(Parse(8)) Npc(n).DropItem = Val(Parse(9)) Npc(n).DropItemValue = Val(Parse(10)) Npc(n).STR = Val(Parse(11)) Npc(n).DEF = Val(Parse(12)) Npc(n).SPEED = Val(Parse(13)) Npc(n).MAGI = Val(Parse(14)) Npc(n).IsAchilles = Val(Parse(15)) Npc(n).Achilles = Val(Parse(16)) ' Initialize the npc editor Call NpcEditorInit Exit Sub End If Now all of the packets are perfectly handled!!! Let's move onto the actual code that prevents the NPCs from being killed... it's the easiest part! Code: ::::::::::::::::: :: Server Side :: ::::::::::::::::: Find the following sub: Code: Sub AttackNpc(ByVal Attacker As Long, ByVal MapNpcNum As Long, ByVal Damage As Long) Scroll down in the sub until you see the line that says Code: If Damage >= MapNpc(MapNum, MapNpcNum).HP Then Now we're going to add the following line BEFORE the line of code we just found: Code: If Npc(NpcNum).IsAchilles = 0 Or (Npc(NpcNum).IsAchilles = 1 And Npc(NpcNum).Achilles = GetPlayerInvItemNum(Attacker, GetPlayerWeaponSlot(Attacker))) Then this is basically saying, if the NPC can be killed by anything (isachilles = 0) or if the player attacks the NPC with the Achilles weapon, then the enemy will actually feel the effects of the weapon. Now at the bottom of this sub, there is a line of code that looks similar to the following: Code: end if ' Reset attack timer Player(Attacker).AttackTimer = GetTickCount Now, after that "End If", but before the reset the attack timer, add the following lines of code Code: Else
Call PlayerMsg(Attacker, "The Material of Your Weapon is Ineffective.", BrightRed) End If This ensures that if the player attacks an NPC is Achilles, then it will not hurt the enemy in any way. Any Questions/Comments Welcome. Sometimes i suck at explaining stuff, so if something needs to be cleared up please just let me know. |
Author: | Reece [ Sun Oct 22, 2006 10:30 am ] |
Post subject: | |
Awsome, why achellies though? Im 1/2 greek so that just got me wondering xD |
Author: | DeJaVu [ Sun Oct 22, 2006 1:57 pm ] |
Post subject: | |
Thats not really an achilles system.. he only died because he was hit in the ankle, it just HAPPENED to be an arrow. For all we know he coulda got hit by Zeus's bolt in the ankle and died. Lets rename this the 'Dracula' System.. you know with the woode nstake and stuff |
Author: | Obsidian [ Sun Oct 22, 2006 4:44 pm ] |
Post subject: | |
yeah i wasn't really sure why i picked achilles... i guess because he was just a hard ass that was hard to kill... and i didn't really put too much thought into it... i just started coding it. |
Author: | Spodi [ Sun Oct 22, 2006 5:07 pm ] |
Post subject: | |
What about that one Greek mythology character who's mom asked everything not to harm him when he was a baby, except for pine needles, and he ended up being killed by just that? |
Author: | James [ Sun Oct 22, 2006 6:00 pm ] |
Post subject: | |
...Achilles was dunked into the river Styx and was made invulnerable however, his mother held him by his heel and thusly is why it was his week point, it waas the only part of him that was not touched by the River. |
Author: | lordgivemick [ Mon Oct 23, 2006 12:23 am ] |
Post subject: | |
yes and thats why the arrow killed him. and if only she just droped him in the lake it would have been better lol. |
Author: | Matt [ Mon Oct 23, 2006 2:12 pm ] |
Post subject: | |
Or dipped her hand as well as all of him. |
Author: | Verrigan [ Mon Oct 23, 2006 2:21 pm ] |
Post subject: | |
Get back on topic. (The tutorial) He chose to call it Achilles, which was his decision... Leave it at that, or PM him. |
Author: | Dr. Spoon [ Wed Oct 25, 2006 1:12 pm ] |
Post subject: | |
small note if you are selecting a weapon to to be mroeeffective against annpc shouldn't you only list the weapons? it can be simply handled by adding a small if then statment inside the for next loop Code: If item(i).type = 1 then
'continue with the loop Else 'this is a place holder so that only the weapon type items will be named in the list frmnpceitor.cmbachilles.additem i & ":" End If |
Author: | Obsidian [ Wed Oct 25, 2006 8:08 pm ] |
Post subject: | |
that's a very good point, spoon. i'll update that right now. |
Page 1 of 1 | All times are UTC |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |