[size=18px>This is NOT a beginner tutorial! The server is heavily modified and is not a full copy and paste tutorual! You have been warned!
First, http://www.dragonbound.com/classes.zip - download the needed classes and add them to the server project. This will allow you to add them as objects to use in scripting.
You need the scripting control referenced to do any scripting of course. Go to Project -> Components and check Microsoft Script Control 1.0. Add it to frmServer and name it ScriptControl.
Note: One major problem with using classes is you can't use an attribute called Type, so I renamed it to Typ. You'll have to change this in a lot of places.
Now in the server open up modTypes.
You can comment out the PlayerInvRec, PlayerRec, AccountRec, MapItemRec, MapNpcRec, SpellRec and TempTileRec UDTs (Types) as they won't be needed anymore. If you've changed any of these be sure to modify the corresponding classes.
Now you need to change some variable declarations to use classes. Here are the ones you need to change.
Public Map(1 To MAX_MAPS) As clsMap Public TempTile(1 To MAX_MAPS) As clsTempTile Public Player(1 To MAX_PLAYERS) As clsAccount Public MapItem(1 To MAX_MAPS, 1 To MAX_MAP_ITEMS) As clsMapItem Public MapNpc(1 To MAX_MAPS, 1 To MAX_MAP_NPCS) As clsMapNpc Public Spell(1 To MAX_SPELLS) As clsSpell
I'm only adding support for scripting NPCs. You can extended it to other things based off this implementation. Open up modDatabase and add the following to the end of SaveNpc.
Call PutVar(FileName, "NPC" & NpcNum, "Script", Trim(Npc(NpcNum).Script))
And then below
Npc(i).MAGI = GetVar(FileName, "NPC" & i, "MAGI")
in LoadNpcs add
Npc(i).Script = GetVar(FileName, "NPC" & i, "Script")
This allows us to set a script file for a NPC.
Since maps are now classes and not UDTs we'll need to alter the saving and loading functions. I'm just going to paste them in. It just puts the class variables into the UDT and saves it. Vice versa for loading. This way you don't have to start your maps over. :)
Sub SaveMap(ByVal MapNum As Long) Dim FileName As String Dim f As Long Dim J As Long 'ADDED Dim K As Long 'ADDED Dim MapTmp As MapRec 'ADDED
FileName = App.Path & "\maps\map" & MapNum & ".dat" f = FreeFile Open FileName For Binary As #f 'REMOVED 'Put #f, , Map(MapNum) 'ADDED With MapTmp .Name = Map(MapNum).Name .Revision = Map(MapNum).Revision .Moral = Map(MapNum).Moral .Up = Map(MapNum).Up .Down = Map(MapNum).Down .Left = Map(MapNum).Left .Right = Map(MapNum).Right .Music = Map(MapNum).Music .BootMap = Map(MapNum).BootMap .BootX = Map(MapNum).BootX .BootY = Map(MapNum).BootY .Shop = Map(MapNum).Shop .Indoors = Map(MapNum).Indoors For J = 0 To MAX_MAPX For K = 0 To MAX_MAPY .Tile(J, K).Ground = Map(MapNum).Tile(J, K).Ground .Tile(J, K).Mask = Map(MapNum).Tile(J, K).Mask .Tile(J, K).Anim = Map(MapNum).Tile(J, K).Anim .Tile(J, K).Fringe = Map(MapNum).Tile(J, K).Fringe .Tile(J, K).Type = Map(MapNum).Tile(J, K).Typ .Tile(J, K).Data1 = Map(MapNum).Tile(J, K).Data1 .Tile(J, K).Data2 = Map(MapNum).Tile(J, K).Data2 .Tile(J, K).Data3 = Map(MapNum).Tile(J, K).Data3 Next K Next J For J = 1 To MAX_MAP_NPCS .Npc(J) = Map(MapNum).Npc(J) Next J End With 'ADDED Put #f, , MapTmp Close #f End Sub
Sub SaveMaps() Dim i As Long
For i = 1 To MAX_MAPS Call SaveMap(i) Next i End Sub
Sub LoadMaps() Dim FileName As String Dim i As Long Dim J As Long 'ADDED Dim K As Long 'ADDED Dim f As Long Dim MapTmp As MapRec 'ADDED
Call CheckMaps For i = 1 To MAX_MAPS FileName = App.Path & "\maps\map" & i & ".dat" f = FreeFile Open FileName For Binary As #f 'REMOVED 'Get #f, , Map(i) 'ADDED Get #f, , MapTmp 'ADDED With Map(i) .Name = MapTmp.Name .Revision = MapTmp.Revision .Moral = MapTmp.Moral .Up = MapTmp.Up .Down = MapTmp.Down .Left = MapTmp.Left .Right = MapTmp.Right .Music = MapTmp.Music .BootMap = MapTmp.BootMap .BootX = MapTmp.BootX .BootY = MapTmp.BootY .Shop = MapTmp.Shop .Indoors = MapTmp.Indoors For J = 0 To MAX_MAPX For K = 0 To MAX_MAPY .Tile(J, K).Ground = MapTmp.Tile(J, K).Ground .Tile(J, K).Mask = MapTmp.Tile(J, K).Mask .Tile(J, K).Anim = MapTmp.Tile(J, K).Anim .Tile(J, K).Fringe = MapTmp.Tile(J, K).Fringe .Tile(J, K).Typ = MapTmp.Tile(J, K).Type .Tile(J, K).Data1 = MapTmp.Tile(J, K).Data1 .Tile(J, K).Data2 = MapTmp.Tile(J, K).Data2 .Tile(J, K).Data3 = MapTmp.Tile(J, K).Data3 Next K Next J For J = 1 To MAX_MAP_NPCS .Npc(J) = MapTmp.Npc(J) Next J End With Close #f DoEvents Next i End Sub
We need a sub to add all our class objects to the scripting control. This allows us to use them in scripts. Add this somewhere in modGeneral.
Private Sub Scripting_Init() Dim Account As New clsAccount Dim Item As New clsItem Dim Map As New clsMap Dim MapItem As New clsMapItem Dim MapNpc As New clsMapNpc Dim Player As New clsPlayer Dim PlayerInv As New clsPlayerInv Dim Spell As New clsSpell Dim TempTile As New clsTempTile Dim Tile As New clsTile Dim Funcs As New clsFunctions
' Add the objects to the scripting engine. frmServer.ScriptControl.AddObject "Account", Account, True frmServer.ScriptControl.AddObject "Item", Item, True frmServer.ScriptControl.AddObject "Map", Map, True frmServer.ScriptControl.AddObject "MapItem", MapItem, True frmServer.ScriptControl.AddObject "MapNpc", MapNpc, True frmServer.ScriptControl.AddObject "Player", Player, True frmServer.ScriptControl.AddObject "PlayerInv", PlayerInv, True frmServer.ScriptControl.AddObject "Spell", Spell, True frmServer.ScriptControl.AddObject "TempTile", TempTile, True frmServer.ScriptControl.AddObject "Tile", Tile, True frmServer.ScriptControl.AddObject "Funcs", Funcs, True ' Clean up. Set Account = Nothing Set Item = Nothing Set Map = Nothing Set MapItem = Nothing Set MapNpc = Nothing Set Player = Nothing Set PlayerInv = Nothing Set Spell = Nothing Set TempTile = Nothing Set Tile = Nothing Set Funcs = Nothing End Sub
I also wrote a simple function to load scripts.
Public Function OpenScript(ByVal strFilename As String) As String If Not FileExist("\scripts\" & strFilename) Then Exit Function
Dim intFileNum As Integer Dim strBuffer As String
intFileNum = FreeFile
Open App.Path & "\scripts\" & strFilename For Input As #intFileNum OpenScript = Input$(LOF(intFileNum), #intFileNum) Close #intFileNum End Function
Now in the top of InitServer also modGeneral add
Scripting_Init()
to initialize the scripting stuff.
Now for the memory intesive part of creating the objects. Put this right below Scripting_Init()
'ADDED Scripting_Init ' ' INIT ALL CLASS OBJECTS ' For i = 1 To MAX_MAPS Set Map(i) = New clsMap Set TempTile(i) = New clsTempTile Next i For i = 1 To MAX_PLAYERS Set Player(i) = New clsAccount Next i For i = 1 To MAX_MAPS For f = 1 To MAX_MAP_ITEMS Set MapItem(i, f) = New clsMapItem Next f Next i For i = 1 To MAX_MAPS For f = 1 To MAX_MAP_NPCS Set MapNpc(i, f) = New clsMapNpc Next f Next i For i = 1 To MAX_SPELLS Set Spell(i) = New clsSpell Next i
You could modify it to only create them when need. Or even better use collections instead of static arrays, but that's another tutorial. :) If you're not sure what this is doing, learn classes. :P
Now open modGameLogic and go to Sub SpawnNPC and right below
MapNpc(MapNum, MapNpcNum).Dir = Int(Rnd * 4)
add
On Error Resume Next If Npc(NpcNum).Script <> "" And frmServer.ScriptControl.Modules.Item("NPC_" & CStr(MapNum & "_" & MapNpcNum)) Is Nothing Then On Error GoTo 0 ' Create the scripting module for this NPC. frmServer.ScriptControl.Modules.Add "NPC_" & CStr(MapNum & "_" & MapNpcNum) MapNpc(MapNum, MapNpcNum).Script = frmServer.ScriptControl.Modules("NPC_" & CStr(MapNum & "_" & MapNpcNum)) ' Add the code to the new module. MapNpc(MapNum, MapNpcNum).Script.AddCode OpenScript(Npc(NpcNum).Script) End If On Error GoTo 0
This loads a NPCs script when it spawns, if it has one.
I'm going add in the capabilitiy of doing things when NPCs move and die.
For dying go to AttackNpc in modGameLogic and add
If MapNpc(MapNum, MapNpcNum).Script.Procedures.Count > 0 Then MapNpc(MapNum, MapNpcNum).Script.Run "Killed", Map(MapNum), MapNpc(MapNum, MapNpcNum), Player(Attacker) End If
right below
Call SendDataToMap(MapNum, "NPCDEAD" & SEP_CHAR & MapNpcNum & SEP_CHAR & END_CHAR)
And add
If MapNpc(MapNum, MapNpcNum).Script.Procedures.Count > 0 Then MapNpc(MapNum, MapNpcNum).Script.Run "Move", Map(MapNum), MapNpc(MapNum, MapNpcNum) End If
to the end of NpcMove.
When you call run if you want to pass parameters just do so as you would any others. Just make sure you add them in the script.
Now create a folder in the server root directory called scripts.
Here's a sample script.
Sub Move(Map, Npc) If Npc.X => 5 And Npc.X <= 10 And Npc.Y => 5 And Npc.Y <= 10 Then GlobalMsg Npc.Num & " has opened a portal to another world.", 3 End If End Sub
Sub Killed(Map, Npc, Killer) GlobalMsg "My comrades will avenge my death, " & Trim(Killer.Char(Killer.CharNum).Name) & "!", 3 End Sub
Don't type any variables in scripts! It doesn't work. (Eg. Blah As String)
Save it and then edit a NPC, Pirate for example. Add the line Script=whatever_u_called_it_above.txt
No need for a path, just the filename with the extension. You will probably want to incorporate this into the NPC editor instead of doing it by hand.
Now start up the server, login and add whatever NPC you edited above. You should see a message when it dies and walks within a certain area.
I believe this is it. It's been a while since I've used it. If this doesn't work with a few corrections I'll just upload my version of the server. Don't forget to change .Type's to .Typ's! Enjoy!
|