Add projects including README.md(s), .gitignore(s) and the actual project files.
Signed-off-by: Michael Fabian Dirks <michael.dirks@project-kube.de>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
Global sTestTxt$
|
||||
sTestTxt$ = sTestTxt$ + "|fFF0000H|fFF7E00a|fFFFF00l|f7EFF00l|f00FF00o |f00FF7EW|f00FFFFe|f007EFFl|f0000FFt|f7E00FF!|fFF00FF!|fFF007E!"+Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + "|fFFFFFFDies ist ein Text der |fFF0000Rot |f00FF00Grün |fFFFFFFund |f0000FFBlau |fFFFFFFist." + Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + "|f33FF33Man kann alle mögli|f0000FFchen Farben machen!" + Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + "|f000000|bFF0000Sogar|b-1-1-1 |bFF7E00der|b-1-1-1 |bFFFF00Hinter|b7EFF00grund|b-1-1-1 |b00FF00kann|b-1-1-1 |b00FF7Egesetzt|b-1-1-1 |b00FFFFwerden|b-1-1-1!" + Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + "|fAAAAFF|b-1-1-1Dies kann für mehr|f444400|bAAAAFFzeilige Selektion|fAAAAFF|b-1-1-1 verwendet werden!" + Chr(10)
|
||||
sTestTxt$ = sTestTxt$ + "|fFFFFFFD|fFF7E00amit wäre der |b7E7E7E|fFFFFFFB|fFF7E00eispieltext|b-1-1-1 beendet|fFFFFFF!"
|
||||
|
||||
Graphics 400,300,32,2
|
||||
SetBuffer BackBuffer()
|
||||
|
||||
Global timer = CreateTimer(30)
|
||||
|
||||
While Not KeyHit(1)
|
||||
Cls
|
||||
AdvText(200,150, sTestTxt, MouseX()/200.0, MouseY()/150.0, 1)
|
||||
|
||||
Flip 0
|
||||
WaitTimer timer
|
||||
Wend
|
||||
|
||||
Include "AdvText.bb"
|
||||
@@ -0,0 +1,193 @@
|
||||
;AdvText Function
|
||||
Dim SplittedString$(0)
|
||||
Dim sTextLines$(0,1)
|
||||
Global SplitCount, AdvText_X, AdvText_Y, AdvText_Width, AdvText_Height
|
||||
Function AdvText(iX, iY, sText$, iCenterX#=0, iCenterY#=0, fLineSpace#=1)
|
||||
Local iTextLines = 1, iLine = 0
|
||||
|
||||
AdvText_X = 0:AdvText_Y = 0:AdvText_Width = 0:AdvText_Height = 0
|
||||
|
||||
SplitString(sText, Chr(10))
|
||||
iTextLines = SplitCount-1
|
||||
|
||||
Dim sTextLines$(iTextLines,1)
|
||||
For iLine = 0 To iTextLines
|
||||
sTextLines(iLine, 0) = SplittedString$(iLine)
|
||||
Next
|
||||
|
||||
;Parse the Text so that we get only the visible text into the second slot.
|
||||
Local iLastClr = 1, tabLength = 0
|
||||
For iLine = 0 To iTextLines
|
||||
iLastClr = 1
|
||||
For iPos = 1 To Len(sTextLines(iLine,0))
|
||||
sChar$ = Mid(sTextLines(iLine,0), iPos, 1)
|
||||
If sChar = "|" And (Mid(sTextLines(iLine,0), iPos+1, 1) = "f" Or Mid(sTextLines(iLine,0), iPos+1, 1) = "b")
|
||||
sTextLines(iLine,1) = sTextLines(iLine,1) + Mid(sTextLines(iLine,0),iLastClr,iPos-iLastClr)
|
||||
iLastClr = iPos+8
|
||||
ElseIf sChar = Chr(9)
|
||||
sTextLines(iLine,1) = sTextLines(iLine,1) + Mid(sTextLines(iLine,0),iLastClr,iPos-iLastClr)
|
||||
iLastClr = iPos
|
||||
|
||||
;Remove Tab Character
|
||||
sTextLines(iLine,0) = Left(sTextLines(iLine,0), iPos-1) + Right(sTextLines(iLine,0), Len(sTextLines(iLine,0))-iPos)
|
||||
|
||||
tabLength = (4-(Len(sTextLines(iLine,1)) Mod 4))
|
||||
For iTabSpace = 1 To tabLength
|
||||
sTextLines(iLine,0) = Left(sTextLines(iLine,0), iPos-1) + " " + Right(sTextLines(iLine,0), Len(sTextLines(iLine,0))-iPos+1)
|
||||
Next
|
||||
ElseIf sChar = Chr(11)
|
||||
sTextLines(iLine,1) = sTextLines(iLine,1) + Mid(sTextLines(iLine,0),iLastClr,iPos-iLastClr)
|
||||
iLastClr = iPos
|
||||
|
||||
;Remove Tab Character
|
||||
sTextLines(iLine,0) = Left(sTextLines(iLine,0), iPos-1) + Right(sTextLines(iLine,0), Len(sTextLines(iLine,0))-iPos)
|
||||
|
||||
tabLength = (8-(Len(sTextLines(iLine,1)) Mod 8))
|
||||
For iTabSpace = 1 To tabLength
|
||||
sTextLines(iLine,0) = Left(sTextLines(iLine,0), iPos-1) + " " + Right(sTextLines(iLine,0), Len(sTextLines(iLine,0))-iPos+1)
|
||||
Next
|
||||
ElseIf iPos = Len(sTextLines(iLine,0))
|
||||
sTextLines(iLine,1) = sTextLines(iLine,1) + Mid(sTextLines(iLine,0),iLastClr,iPos-iLastClr+1)
|
||||
EndIf
|
||||
Next
|
||||
; Return width.
|
||||
If (AdvText_Width < StringWidth(sTextLines(iLine,1))) Then AdvText_Width = StringWidth(sTextLines(iLine,1))
|
||||
Next
|
||||
|
||||
; Return Height
|
||||
If (AdvText_Height < (FontHeight()*(iTextLines+1)*fLineSpace)) Then AdvText_Height = (FontHeight()*(iTextLines+1)*fLineSpace)
|
||||
|
||||
;Real Text Processing
|
||||
Local iRedO = ColorRed(), iGreenO = ColorGreen(), iBlueO = ColorBlue() ; Original Foreground Color
|
||||
Local iRed = ColorRed(), iGreen = ColorGreen(), iBlue = ColorBlue() ;Foreground
|
||||
Local iBGRed = -1, iBGGreen = -1, iBGBlue = -1 ;Background
|
||||
Local icX, icY, icsX, scText$, iRealPos
|
||||
For iLine = 0 To iTextLines
|
||||
sText$ = sTextLines(iLine,0)
|
||||
icsX = -(StringWidth(sTextLines(iLine,1))*0.5)*iCenterX
|
||||
icX = icsX
|
||||
icY = ( -(FontHeight()*(iTextLines+1)*0.5*fLineSpace*iCenterY) + (iLine * FontHeight() * fLineSpace) )
|
||||
|
||||
; Return X and Y starting point.
|
||||
If (AdvText_X > iX + icX) Then AdvText_X = iX + icX
|
||||
If (AdvText_Y > iY + icY) Then AdvText_Y = iY + icY
|
||||
|
||||
iRealPos = 1
|
||||
For iPos = 1 To Len(sText)
|
||||
sChar$ = Mid(sText, iPos, 1)
|
||||
If sChar = "|" And Mid(sText, iPos+1, 1) = "f" ;Foreground Change
|
||||
If iBGRed > -1 And iBGGreen > -1 And iBGBlue > -1
|
||||
Color iBGRed, iBGGreen, iBGBlue
|
||||
Rect iX+icX, iY+icY, StringWidth(scText), FontHeight()*fLineSpace
|
||||
EndIf
|
||||
Color iRed, iGreen, iBlue
|
||||
Text iX+icX, iY+icY, scText$
|
||||
|
||||
icX = icX + StringWidth(scText$)
|
||||
scText = ""
|
||||
|
||||
If Mid(sText, iPos+2, 2) = "-1"
|
||||
iRed = iRedO
|
||||
iGreen = iGreenO
|
||||
iBlue = iBlueO
|
||||
Else
|
||||
iRed = HexB(Mid(sText, iPos+2, 2))
|
||||
iGreen = HexB(Mid(sText, iPos+4, 2))
|
||||
iBlue = HexB(Mid(sText, iPos+6, 2))
|
||||
EndIf
|
||||
|
||||
sText = Left(sText, iPos)+Mid(sText, iPos+8)
|
||||
ElseIf sChar = "|" And Mid(sText, iPos+1, 1) = "b" ;Background Change
|
||||
If iBGRed > -1 And iBGGreen > -1 And iBGBlue > -1
|
||||
Color iBGRed, iBGGreen, iBGBlue
|
||||
Rect iX+icX, iY+icY, StringWidth(scText), FontHeight()*fLineSpace
|
||||
EndIf
|
||||
Color iRed, iGreen, iBlue
|
||||
Text iX+icX, iY+icY, scText$
|
||||
|
||||
icX = icX + StringWidth(scText$)
|
||||
scText = ""
|
||||
|
||||
If Mid(sText, iPos+2, 2) = "-1"
|
||||
iBGRed = -1
|
||||
iBGGreen = -1
|
||||
iBGBlue = -1
|
||||
Else
|
||||
iBGRed = HexB(Mid(sText, iPos+2, 2))
|
||||
iBGGreen = HexB(Mid(sText, iPos+4, 2))
|
||||
iBGBlue = HexB(Mid(sText, iPos+6, 2))
|
||||
EndIf
|
||||
sText = Left(sText, iPos)+Mid(sText, iPos+8)
|
||||
Else
|
||||
scText$ = scText$ + sChar$
|
||||
iRealPos = iRealPos + 1
|
||||
EndIf
|
||||
|
||||
If iPos = Len(sText)
|
||||
If iBGRed > -1 And iBGGreen > -1 And iBGBlue > -1
|
||||
Color iBGRed, iBGGreen, iBGBlue
|
||||
Rect iX+icX, iY+icY, StringWidth(scText), FontHeight()*fLineSpace
|
||||
EndIf
|
||||
Color iRed, iGreen, iBlue
|
||||
Text iX+icX, iY+icY, scText
|
||||
scText = ""
|
||||
EndIf
|
||||
Next
|
||||
scText$ = ""
|
||||
Next
|
||||
End Function
|
||||
|
||||
Function HexB#(Hexzahl$)
|
||||
Local Integer_Result#
|
||||
If Left$(Hexzahl$,1)="$" Then Hexzahl$=Mid$(Hexzahl$,2)
|
||||
For i=1 To Len(Hexzahl$)
|
||||
tmp1$=Upper$(Mid$(Hexzahl$,i,1)):tmp2=tmp1$
|
||||
If tmp2=0 And tmp1$<>"0" Then tmp2=Asc(tmp1$)-55
|
||||
Integer_Result=Integer_Result*16:Integer_Result=Integer_Result+tmp2
|
||||
Next
|
||||
Return Integer_Result
|
||||
End Function
|
||||
|
||||
Function SplitString(In$, StringSplitter$ = "|")
|
||||
Local InLength% = Len(In)
|
||||
Local SplitLength% = Len(StringSplitter)
|
||||
Local CountPos%, InPos%, SplitIndex%
|
||||
Local SplitTest$, LineText$
|
||||
|
||||
; Count how many Lines there are and resize Dim.
|
||||
SplitCount = 0
|
||||
For CountPos = 1 To InLength-(SplitLength-1)
|
||||
SplitTest = Mid(In,CountPos,1)
|
||||
If SplitTest = StringSplitter Then SplitCount = SplitCount + 1
|
||||
Next
|
||||
Dim SplittedString(SplitCount)
|
||||
|
||||
; Split the Text onto multiple lines.
|
||||
While Not InPos = Len(In)
|
||||
; Increment Position
|
||||
InPos = InPos + 1
|
||||
|
||||
; Grab a piece of the text.
|
||||
SplitTest = Mid(In, InPos, SplitLength)
|
||||
Local Char$ = Left(SplitTest, 1)
|
||||
|
||||
; Check if the current Text matches the splitter or if we are near the end.
|
||||
If SplitTest = StringSplitter Or InPos = InLength
|
||||
; Append the current character if it doesn't match the Splitter.
|
||||
If InPos = InLength And SplitTest <> StringSplitter Then LineText = LineText + Char
|
||||
|
||||
; Store the Line.
|
||||
SplittedString(SplitIndex) = LineText
|
||||
|
||||
; Increment split index.
|
||||
SplitIndex = SplitIndex + 1
|
||||
|
||||
; Reset LineText
|
||||
LineText = ""
|
||||
Else
|
||||
LineText = LineText + Char
|
||||
EndIf
|
||||
Wend
|
||||
End Function
|
||||
;~IDEal Editor Parameters:
|
||||
;~C#Blitz3D
|
||||
@@ -0,0 +1,8 @@
|
||||
Advanced Text
|
||||
=======================
|
||||
|
||||
This project adds an advanced text function to BlitzBasic. While somewhat slower, it supports many features and if you cache the result you can get quite good results. It was written for a project that needed such features in the chat, but has been abandoned.
|
||||
|
||||
License
|
||||
=======
|
||||
Advanced Text by Michael Fabian Dirks is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
@@ -0,0 +1,258 @@
|
||||
;----------------------------------------------------------------
|
||||
;-- Types
|
||||
;----------------------------------------------------------------
|
||||
Type TList
|
||||
Field FirstEntry.TListEntry
|
||||
Field LastEntry.TListEntry
|
||||
|
||||
Field Iterator.TListEntry
|
||||
End Type
|
||||
|
||||
Type TListEntry
|
||||
Field Value%
|
||||
|
||||
Field PreviousEntry.TListEntry
|
||||
Field NextEntry.TListEntry
|
||||
End Type
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Functions
|
||||
;----------------------------------------------------------------
|
||||
Function TList_Create.TList()
|
||||
Local lList.TList = New TList
|
||||
|
||||
lList\FirstEntry = Null
|
||||
lList\LastEntry = Null
|
||||
lList\Iterator = Null
|
||||
|
||||
Return lList
|
||||
End Function
|
||||
|
||||
Function TList_Destroy(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to destroy non-existing list."
|
||||
|
||||
; Delete all entries
|
||||
pList\Iterator = pList\FirstEntry
|
||||
While pList\Iterator <> Null
|
||||
Local lNextEntry.TListEntry = pList\Iterator\NextEntry
|
||||
Delete pList\Iterator
|
||||
pList\Iterator = lNextEntry
|
||||
Wend
|
||||
End Function
|
||||
|
||||
Function TList_Reset(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to reset non-existing list."
|
||||
|
||||
pList\Iterator = Null
|
||||
End Function
|
||||
|
||||
Function TList_First%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
pList\Iterator = pList\FirstEntry
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Last%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
pList\Iterator = pList\LastEntry
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Next%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
pList\Iterator = pList\Iterator\NextEntry
|
||||
Else
|
||||
pList\Iterator = pList\FirstEntry
|
||||
EndIf
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Previous%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
pList\Iterator = pList\Iterator\PreviousEntry
|
||||
Else
|
||||
pList\Iterator = pList\LastEntry
|
||||
EndIf
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_HasFirst(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
Return (pList\FirstEntry <> Null)
|
||||
End Function
|
||||
|
||||
Function TList_HasLast(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
Return (pList\LastEntry <> Null)
|
||||
End Function
|
||||
|
||||
Function TList_HasNext(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
Return (pList\Iterator\NextEntry <> Null)
|
||||
Else
|
||||
Return (pList\FirstEntry <> Null)
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function TList_HasPrevious(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
Return (pList\Iterator\PreviousEntry <> Null)
|
||||
Else
|
||||
Return (pList\LastEntry <> Null)
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function TList_AddFirst(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\FirstEntry <> Null Then
|
||||
lEntry\NextEntry = pList\FirstEntry
|
||||
lEntry\NextEntry\PreviousEntry = lEntry
|
||||
EndIf
|
||||
|
||||
pList\FirstEntry = lEntry
|
||||
If pList\LastEntry = Null Then pList\LastEntry = lEntry
|
||||
End Function
|
||||
|
||||
Function TList_AddLast(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\LastEntry <> Null Then
|
||||
lEntry\PreviousEntry = pList\LastEntry
|
||||
lEntry\PreviousEntry\NextEntry = lEntry
|
||||
EndIf
|
||||
|
||||
If pList\FirstEntry = Null pList\FirstEntry = lEntry
|
||||
pList\LastEntry = lEntry
|
||||
End Function
|
||||
|
||||
Function TList_InsertBefore(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\Iterator\PreviousEntry <> Null Then
|
||||
lEntry\PreviousEntry = pList\Iterator\PreviousEntry
|
||||
pList\Iterator\PreviousEntry\NextEntry = lEntry
|
||||
Else
|
||||
pList\FirstEntry = lEntry
|
||||
EndIf
|
||||
pList\Iterator\PreviousEntry = lEntry
|
||||
|
||||
If pList\Iterator\NextEntry = Null Then
|
||||
pList\LastEntry = lEntry
|
||||
EndIf
|
||||
lEntry\NextEntry = pList\Iterator
|
||||
End Function
|
||||
|
||||
Function TList_InsertAfter(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\Iterator\NextEntry <> Null Then
|
||||
lEntry\NextEntry = pList\Iterator\NextEntry
|
||||
pList\Iterator\NextEntry\PreviousEntry = lEntry
|
||||
Else
|
||||
pList\LastEntry = lEntry
|
||||
EndIf
|
||||
pList\Iterator\NextEntry = lEntry
|
||||
|
||||
If pList\Iterator\PreviousEntry = Null Then
|
||||
pList\FirstEntry = lEntry
|
||||
EndIf
|
||||
lEntry\PreviousEntry = pList\Iterator
|
||||
End Function
|
||||
|
||||
Function TList_Replace(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
pList\Iterator\Value = Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteFirst(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\FirstEntry = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = pList\FirstEntry
|
||||
pList\FirstEntry\NextEntry\PreviousEntry = Null
|
||||
pList\FirstEntry = pList\FirstEntry\NextEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\FirstEntry <> Null Then Return pList\FirstEntry\Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteLast(pList.Tlist)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\LastEntry = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = pList\LastEntry
|
||||
pList\LastEntry\PreviousEntry\NextEntry = Null
|
||||
pList\LastEntry = pList\LastEntry\PreviousEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\LastEntry <> Null Then Return pList\LastEntry\Value
|
||||
End Function
|
||||
|
||||
Function TList_Delete(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
If pList\FirstEntry = pList\Iterator Then pList\FirstEntry = pList\Iterator\NextEntry
|
||||
If pList\LastEntry = pList\Iterator Then pList\LastEntry = pList\Iterator\PreviousEntry
|
||||
|
||||
If pList\Iterator\NextEntry <> Null Then pList\Iterator\NextEntry\PreviousEntry = pList\Iterator\PreviousEntry
|
||||
If pList\Iterator\PreviousEntry <> Null Then pList\Iterator\PreviousEntry\NextEntry = pList\Iterator\NextEntry
|
||||
|
||||
Local lEntry.TListEntry = pList\Iterator
|
||||
pList\Iterator = pList\Iterator\NextEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteValue(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
If pList\FirstEntry <> Null Then
|
||||
Local lEntry.TListEntry = pList\FirstEntry
|
||||
While lEntry <> Null
|
||||
If lEntry\Value = Value Then
|
||||
If lEntry\PreviousEntry <> Null Then lEntry\PreviousEntry\NextEntry = lEntry\NextEntry
|
||||
If lEntry\NextEntry <> Null Then lEntry\NextEntry\PreviousEntry = lEntry\PreviousEntry
|
||||
|
||||
If pList\FirstEntry = lEntry Then pList\FirstEntry = lEntry\NextEntry
|
||||
If pList\LastEntry = lEntry Then pList\LastEntry = lEntry\PreviousEntry
|
||||
|
||||
Local lDelEntry.TListEntry = lEntry
|
||||
lEntry = lEntry\NextEntry
|
||||
Delete lDelEntry
|
||||
Else
|
||||
lEntry = lEntry\NextEntry
|
||||
EndIf
|
||||
Wend
|
||||
EndIf
|
||||
End Function
|
||||
@@ -0,0 +1,9 @@
|
||||
LinkedList Emulation
|
||||
=======================
|
||||
|
||||
Since the early BlitzBasic languages didn't have any kind of lists aside from the global ones, I had to make something out of nothing. This library adds the ability to have linked lists inside of those languages.
|
||||
It was initially ment to use FastPointer or a similar library, but that failed horrifically, as I wasn't able to cast back into the original type and memory leaks happened. It's somewhat fast, but solutions tailored to a single type still work faster 90% of the time.
|
||||
|
||||
License
|
||||
=======
|
||||
LinkedList Emulation by Michael Fabian Dirks is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
@@ -0,0 +1,258 @@
|
||||
;----------------------------------------------------------------
|
||||
;-- Types
|
||||
;----------------------------------------------------------------
|
||||
Type TList
|
||||
Field FirstEntry.TListEntry
|
||||
Field LastEntry.TListEntry
|
||||
|
||||
Field Iterator.TListEntry
|
||||
End Type
|
||||
|
||||
Type TListEntry
|
||||
Field Value%
|
||||
|
||||
Field PreviousEntry.TListEntry
|
||||
Field NextEntry.TListEntry
|
||||
End Type
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Functions
|
||||
;----------------------------------------------------------------
|
||||
Function TList_Create.TList()
|
||||
Local lList.TList = New TList
|
||||
|
||||
lList\FirstEntry = Null
|
||||
lList\LastEntry = Null
|
||||
lList\Iterator = Null
|
||||
|
||||
Return lList
|
||||
End Function
|
||||
|
||||
Function TList_Destroy(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to destroy non-existing list."
|
||||
|
||||
; Delete all entries
|
||||
pList\Iterator = pList\FirstEntry
|
||||
While pList\Iterator <> Null
|
||||
Local lNextEntry.TListEntry = pList\Iterator\NextEntry
|
||||
Delete pList\Iterator
|
||||
pList\Iterator = lNextEntry
|
||||
Wend
|
||||
End Function
|
||||
|
||||
Function TList_Reset(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to reset non-existing list."
|
||||
|
||||
pList\Iterator = Null
|
||||
End Function
|
||||
|
||||
Function TList_First%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
pList\Iterator = pList\FirstEntry
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Last%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
pList\Iterator = pList\LastEntry
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Next%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
pList\Iterator = pList\Iterator\NextEntry
|
||||
Else
|
||||
pList\Iterator = pList\FirstEntry
|
||||
EndIf
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_Previous%(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
pList\Iterator = pList\Iterator\PreviousEntry
|
||||
Else
|
||||
pList\Iterator = pList\LastEntry
|
||||
EndIf
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_HasFirst(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
Return (pList\FirstEntry <> Null)
|
||||
End Function
|
||||
|
||||
Function TList_HasLast(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
Return (pList\LastEntry <> Null)
|
||||
End Function
|
||||
|
||||
Function TList_HasNext(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
Return (pList\Iterator\NextEntry <> Null)
|
||||
Else
|
||||
Return (pList\FirstEntry <> Null)
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function TList_HasPrevious(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to iterate non-existing list."
|
||||
|
||||
If pList\Iterator <> Null Then
|
||||
Return (pList\Iterator\PreviousEntry <> Null)
|
||||
Else
|
||||
Return (pList\LastEntry <> Null)
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function TList_AddFirst(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\FirstEntry <> Null Then
|
||||
lEntry\NextEntry = pList\FirstEntry
|
||||
lEntry\NextEntry\PreviousEntry = lEntry
|
||||
EndIf
|
||||
|
||||
pList\FirstEntry = lEntry
|
||||
If pList\LastEntry = Null Then pList\LastEntry = lEntry
|
||||
End Function
|
||||
|
||||
Function TList_AddLast(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\LastEntry <> Null Then
|
||||
lEntry\PreviousEntry = pList\LastEntry
|
||||
lEntry\PreviousEntry\NextEntry = lEntry
|
||||
EndIf
|
||||
|
||||
If pList\FirstEntry = Null pList\FirstEntry = lEntry
|
||||
pList\LastEntry = lEntry
|
||||
End Function
|
||||
|
||||
Function TList_InsertBefore(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\Iterator\PreviousEntry <> Null Then
|
||||
lEntry\PreviousEntry = pList\Iterator\PreviousEntry
|
||||
pList\Iterator\PreviousEntry\NextEntry = lEntry
|
||||
Else
|
||||
pList\FirstEntry = lEntry
|
||||
EndIf
|
||||
pList\Iterator\PreviousEntry = lEntry
|
||||
|
||||
If pList\Iterator\NextEntry = Null Then
|
||||
pList\LastEntry = lEntry
|
||||
EndIf
|
||||
lEntry\NextEntry = pList\Iterator
|
||||
End Function
|
||||
|
||||
Function TList_InsertAfter(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = New TListEntry
|
||||
lEntry\Value = Value
|
||||
|
||||
If pList\Iterator\NextEntry <> Null Then
|
||||
lEntry\NextEntry = pList\Iterator\NextEntry
|
||||
pList\Iterator\NextEntry\PreviousEntry = lEntry
|
||||
Else
|
||||
pList\LastEntry = lEntry
|
||||
EndIf
|
||||
pList\Iterator\NextEntry = lEntry
|
||||
|
||||
If pList\Iterator\PreviousEntry = Null Then
|
||||
pList\FirstEntry = lEntry
|
||||
EndIf
|
||||
lEntry\PreviousEntry = pList\Iterator
|
||||
End Function
|
||||
|
||||
Function TList_Replace(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
pList\Iterator\Value = Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteFirst(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\FirstEntry = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = pList\FirstEntry
|
||||
pList\FirstEntry\NextEntry\PreviousEntry = Null
|
||||
pList\FirstEntry = pList\FirstEntry\NextEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\FirstEntry <> Null Then Return pList\FirstEntry\Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteLast(pList.Tlist)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\LastEntry = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
Local lEntry.TListEntry = pList\LastEntry
|
||||
pList\LastEntry\PreviousEntry\NextEntry = Null
|
||||
pList\LastEntry = pList\LastEntry\PreviousEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\LastEntry <> Null Then Return pList\LastEntry\Value
|
||||
End Function
|
||||
|
||||
Function TList_Delete(pList.TList)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
If pList\Iterator = Null Then RuntimeError "TList: TList: Tried to change non-existing list entry."
|
||||
|
||||
If pList\FirstEntry = pList\Iterator Then pList\FirstEntry = pList\Iterator\NextEntry
|
||||
If pList\LastEntry = pList\Iterator Then pList\LastEntry = pList\Iterator\PreviousEntry
|
||||
|
||||
If pList\Iterator\NextEntry <> Null Then pList\Iterator\NextEntry\PreviousEntry = pList\Iterator\PreviousEntry
|
||||
If pList\Iterator\PreviousEntry <> Null Then pList\Iterator\PreviousEntry\NextEntry = pList\Iterator\NextEntry
|
||||
|
||||
Local lEntry.TListEntry = pList\Iterator
|
||||
pList\Iterator = pList\Iterator\NextEntry
|
||||
Delete lEntry
|
||||
|
||||
If pList\Iterator <> Null Then Return pList\Iterator\Value
|
||||
End Function
|
||||
|
||||
Function TList_DeleteValue(pList.TList, Value%)
|
||||
If pList = Null Then RuntimeError "TList: Tried to insert into non-existing list."
|
||||
|
||||
If pList\FirstEntry <> Null Then
|
||||
Local lEntry.TListEntry = pList\FirstEntry
|
||||
While lEntry <> Null
|
||||
If lEntry\Value = Value Then
|
||||
If lEntry\PreviousEntry <> Null Then lEntry\PreviousEntry\NextEntry = lEntry\NextEntry
|
||||
If lEntry\NextEntry <> Null Then lEntry\NextEntry\PreviousEntry = lEntry\PreviousEntry
|
||||
|
||||
If pList\FirstEntry = lEntry Then pList\FirstEntry = lEntry\NextEntry
|
||||
If pList\LastEntry = lEntry Then pList\LastEntry = lEntry\PreviousEntry
|
||||
|
||||
Local lDelEntry.TListEntry = lEntry
|
||||
lEntry = lEntry\NextEntry
|
||||
Delete lDelEntry
|
||||
Else
|
||||
lEntry = lEntry\NextEntry
|
||||
EndIf
|
||||
Wend
|
||||
EndIf
|
||||
End Function
|
||||
@@ -0,0 +1,27 @@
|
||||
Global Logger_Stream
|
||||
|
||||
Function Logger_Initialize(File$)
|
||||
File = "Logs/" + File
|
||||
CreateDir("./Logs/")
|
||||
If FileType("./Logs/") <> 2 Then RuntimeError("Unable To create Log File."+Chr(10)+" If the program is running in a protected directory,"+Chr(10)+" consider running it as an administrator.")
|
||||
|
||||
Logger_Stream = OpenFile(File)
|
||||
If Logger_Stream = 0
|
||||
Logger_Stream = WriteFile(File)
|
||||
If Logger_Stream = 0 Then RuntimeError("Unable to create log file."+Chr(10)+" If the program is running in a protected directory,"+Chr(10)+" consider running it as an administrator.")
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function Logger_Info(Module$, Message$)
|
||||
WriteLine Logger_Stream, "[" + CurrentTime() + "] [Info] " + Module + ": " + Message
|
||||
End Function
|
||||
|
||||
Function Logger_Warning(Module$, Message$)
|
||||
WriteLine Logger_Stream, "[" + CurrentTime() + "] [Warn] " + Module + ": " + Message
|
||||
End Function
|
||||
|
||||
Function Logger_Error(Module$, Message$)
|
||||
WriteLine Logger_Stream, "[" + CurrentTime() + "] [Errr] " + Module + ": " + Message
|
||||
End Function
|
||||
;~IDEal Editor Parameters:
|
||||
;~C#Blitz3D
|
||||
@@ -0,0 +1,665 @@
|
||||
;----------------------------------------------------------------
|
||||
;-- Packet Descriptors
|
||||
;----------------------------------------------------------------
|
||||
|
||||
;-- Any Packet
|
||||
;Off Size Desc
|
||||
; 0 1 Packet Id
|
||||
|
||||
;-- Login
|
||||
;Off Size Desc
|
||||
; 1 2 Unique Id (Server) / UDP Port (Client)
|
||||
; 3 2 Version
|
||||
; 5 16 Name (Always 16B, Only allows bytes above 32)
|
||||
; 21 4F Initial Position X
|
||||
; 25 4F Initial Position Y
|
||||
; 29 4F Initial Position Z
|
||||
; 33 4F Initial Rotation X
|
||||
; 37 4F Initial Rotation Y
|
||||
; 41 4F Initial Rotation Z
|
||||
|
||||
;-- Logout
|
||||
;Off Size Desc
|
||||
; 1 2 Unique Id
|
||||
|
||||
;-- Kick
|
||||
;Off Size Desc
|
||||
; 1 2 Reason Length
|
||||
; 3 ^ Reason
|
||||
|
||||
;-- Data
|
||||
; 1 2 Unique Id
|
||||
; 3 1 Data Flags (See BNET_DATAFLAG_*)
|
||||
;... 4F Position X
|
||||
;... 4F Position Y
|
||||
;... 4F Position Z
|
||||
;... 2 Rotation X
|
||||
;... 2 Rotation Y
|
||||
;... 2 Rotation Z
|
||||
;... 2 Velocity X
|
||||
;... 2 Velocity Y
|
||||
;... 2 Velocity Z
|
||||
;... 2 Aim X
|
||||
;... 2 Aim Y
|
||||
|
||||
;-- Action (Yet Unsupported)
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Constants
|
||||
;----------------------------------------------------------------
|
||||
; BlitzNet Version
|
||||
Const BNET_VERSION_MAJOR% = 0
|
||||
Const BNET_VERSION_MINOR% = 1
|
||||
; Size of Buffer for Network Packets, 64KB should be enough for now.
|
||||
Const BNET_BUFFER_SIZE% = (64 * 1024)
|
||||
; Flags for data changes. Only when the bit is set this data is available.
|
||||
Const BNET_DATAFLAG_POSX = $00000001
|
||||
Const BNET_DATAFLAG_POSY = $00000010
|
||||
Const BNET_DATAFLAG_POSZ = $00000100
|
||||
Const BNET_DATAFLAG_ROTX = $00001000
|
||||
Const BNET_DATAFLAG_ROTY = $00010000
|
||||
Const BNET_DATAFLAG_ROTZ = $00100000
|
||||
Const BNET_DATAFLAG_VELOCITY = $01000000
|
||||
Const BNET_DATAFLAG_AIM = $10000000
|
||||
; Packet Ids, for identification of each one.
|
||||
Const BNET_PACKET_LOGIN = 0
|
||||
Const BNET_PACKET_LOGOUT = 1
|
||||
Const BNET_PACKET_KICK = 2
|
||||
Const BNET_PACKET_DATA = 3
|
||||
Const BNET_PACKET_ACTION = 4
|
||||
; How many updates should we send out each minute?
|
||||
Const BNET_DATA_COUNT = 150
|
||||
; How many keyframes should we send out each minute?
|
||||
Const BNET_DATA_COUNT_KEYFRAME = 3
|
||||
; How much has the value to change to be considered different?
|
||||
Const BNET_DATA_THRESHOLD_POS# = 1.0
|
||||
Const BNET_DATA_THRESHOLD_ROT# = 1.0
|
||||
Const BNET_DATA_THRESHOLD_VEL# = 1.0
|
||||
Const BNET_DATA_THRESHOLD_AIM# = 1.0
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Globals
|
||||
;----------------------------------------------------------------
|
||||
; Connections to Sector Servers.
|
||||
Global BNet_Sector_TCP% = 0
|
||||
Global BNet_Sector_UDP% = 0
|
||||
; Our UniqueId and Player instance so we know which Player is us.
|
||||
Global BNet_UniqueId% = -1
|
||||
Global BNet_Player.BNetPlayer = Null
|
||||
; Buffer for Network Packets.
|
||||
Global BNet_Buffer% = CreateBank(BNET_BUFFER_SIZE)
|
||||
; Set to true when Kicked from the server, or similar.
|
||||
Global BNet_Kick% = False
|
||||
Global BNet_Kick_Reason$ = "Not Kicked"
|
||||
; Update Times
|
||||
Global BNet_Data_Time = 60000 / BNET_DATA_COUNT
|
||||
Global BNet_Data_Time_KeyFrame = 60000 / BNET_DATA_COUNT_KEYFRAME
|
||||
Global BNet_Data_LastUpdate = 0
|
||||
Global BNet_Data_LastKeyFrame = 0
|
||||
; Array for all known players. Makes access a bit faster.
|
||||
Dim BNet_Players.BNetPlayer(65535)
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Types
|
||||
;----------------------------------------------------------------
|
||||
Type BNetPlayer
|
||||
Field Name$ = "Invalid Player"
|
||||
|
||||
; Position, Rotation, Velocity and Aim
|
||||
Field PositionX#, PositionY#, PositionZ#
|
||||
Field RotationX#, RotationY#, RotationZ#
|
||||
Field VelocityX#, VelocityY#, VelocityZ#
|
||||
Field AimX#, AimY#
|
||||
|
||||
; Internal Data
|
||||
Field m_UniqueId%
|
||||
End Type
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Functions
|
||||
;----------------------------------------------------------------
|
||||
Function BNet_Initialize()
|
||||
TCPTimeouts 100, 100
|
||||
End Function
|
||||
|
||||
Function BNet_Connect(Ip$ = "127.0.0.1", IPort% = 27000, DPort = 27001)
|
||||
BNet_Kick% = False
|
||||
BNet_Kick_Reason$ = "Not Kicked"
|
||||
|
||||
BNet_Sector_TCP = OpenTCPStream(Ip, IPort)
|
||||
If BNet_Sector_TCP Then
|
||||
BNet_Sector_UDP = CreateUDPStream()
|
||||
If BNet_Sector_UDP Then
|
||||
Return True
|
||||
Else
|
||||
CloseTCPStream BNet_Sector_TCP
|
||||
BNet_Sector_UDP = 0
|
||||
BNet_Sector_TCP = 0
|
||||
EndIf
|
||||
Else
|
||||
BNet_Sector_UDP = 0
|
||||
BNet_Sector_TCP = 0
|
||||
EndIf
|
||||
Return False
|
||||
End Function
|
||||
|
||||
Function BNet_Disconnect()
|
||||
If BNet_UniqueId > -1 Then
|
||||
; Remove all existing players
|
||||
For Player.BNetPlayer = Each BNetPlayer
|
||||
Local UniqueId = Player\m_UniqueId
|
||||
|
||||
CB_BNet_DeletePlayer(UniqueId, Player)
|
||||
|
||||
Delete BNet_Players(UniqueId):BNet_Players(UniqueId) = Null
|
||||
Next
|
||||
|
||||
; Logout
|
||||
BNet_Logout()
|
||||
BNet_UniqueId = -1
|
||||
EndIf
|
||||
If BNet_Sector_UDP Then CloseUDPStream(BNet_Sector_UDP):BNet_Sector_UDP = 0
|
||||
If BNet_Sector_TCP Then CloseTCPStream(BNet_Sector_TCP):BNet_Sector_TCP = 0
|
||||
End Function
|
||||
|
||||
Function BNet_Connected()
|
||||
If BNet_Sector_TCP = 0 Then Return False
|
||||
If BNet_Sector_UDP = 0 Then Return False
|
||||
If Eof(BNet_Sector_TCP) <> 0 Then Return False
|
||||
|
||||
Return True
|
||||
End Function
|
||||
|
||||
Function BNet_Login(Name$, PosX# = 0, PosY# = 0, PosZ# = 0, RotX# = 0, RotY# = 0, RotZ# = 0)
|
||||
If BNet_Connected() And BNet_UniqueId = -1 Then
|
||||
; Packet Id: Login
|
||||
PokeByte BNet_Buffer, 0, BNET_PACKET_LOGIN
|
||||
; Write UDP Port
|
||||
PokeShort BNet_Buffer, 1, UDPStreamPort(BNet_Sector_UDP)
|
||||
; Write Client Version
|
||||
PokeShort BNet_Buffer, 3, BNET_VERSION_MAJOR Shl 8 + BNET_VERSION_MINOR
|
||||
; Write Name
|
||||
Local NameLength% = Len(Name)
|
||||
For NamePos = 1 To NameLength:PokeByte BNet_Buffer, (5 + NamePos - 1), Asc(Mid(Name, NamePos, 1)):Next
|
||||
For NamePos = NameLength To 16:PokeByte BNet_Buffer, (5 + NamePos - 1), 0:Next
|
||||
; Write Initial Position
|
||||
PokeFloat BNet_Buffer, 21, PosX
|
||||
PokeFloat BNet_Buffer, 25, PosY
|
||||
PokeFloat BNet_Buffer, 29, PosZ
|
||||
; Write Initial Rotation
|
||||
PokeFloat BNet_Buffer, 33, RotX
|
||||
PokeFloat BNet_Buffer, 37, RotX
|
||||
PokeFloat BNet_Buffer, 41, RotX
|
||||
|
||||
; Write to Stream
|
||||
WriteBytes BNet_Buffer, BNet_Sector_TCP, 0, 47
|
||||
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function BNet_Logout()
|
||||
If BNet_Connected() And BNet_UniqueId > -1 Then
|
||||
; Packet Id: Logout
|
||||
PokeByte BNet_Buffer, 0, BNET_PACKET_LOGOUT
|
||||
; Write our own UniqueId
|
||||
PokeShort BNet_Buffer, 1, BNet_UniqueId
|
||||
|
||||
; Write to Stream
|
||||
WriteBytes BNet_Buffer, BNet_Sector_TCP, 0, 3
|
||||
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function BNet_Update()
|
||||
Local UniqueId, PacketSize, PacketId, DataFlags, Offset, TempPlayer.BNetPlayer
|
||||
If BNet_Connected() Then
|
||||
; TCP
|
||||
While Not Eof(BNet_Sector_TCP) And ReadAvail(BNet_Sector_TCP) > 0
|
||||
PacketSize = ReadAvail(BNet_Sector_TCP)
|
||||
If PacketSize > BNET_BUFFER_SIZE Then PacketSize = BNET_BUFFER_SIZE
|
||||
ReadBytes(BNet_Buffer, BNet_Sector_TCP, 0, PacketSize)
|
||||
|
||||
PacketId = PeekByte(BNet_Buffer, 0)
|
||||
Select PacketId
|
||||
Case BNET_PACKET_LOGIN
|
||||
UniqueId = PeekShort(BNet_Buffer, 1)
|
||||
|
||||
BNet_Players(UniqueId) = New BNetPlayer
|
||||
; Read Name
|
||||
For NamePos = 1 To 16
|
||||
Local NameChar = PeekByte(BNet_Buffer, (5 + NamePos - 1))
|
||||
If NameChar < 32 Then Exit
|
||||
BNet_Players(UniqueId)\Name$ = BNet_Players(UniqueId)\Name$ + Chr(NameChar)
|
||||
Next
|
||||
; Read initital Position
|
||||
BNet_Players(UniqueId)\PositionX = PeekFloat(BNet_Buffer, 21)
|
||||
BNet_Players(UniqueId)\PositionY = PeekFloat(BNet_Buffer, 25)
|
||||
BNet_Players(UniqueId)\PositionZ = PeekFloat(BNet_Buffer, 29)
|
||||
; Read initial Rotation
|
||||
BNet_Players(UniqueId)\RotationX = PeekFloat(BNet_Buffer, 33)
|
||||
BNet_Players(UniqueId)\RotationY = PeekFloat(BNet_Buffer, 37)
|
||||
BNet_Players(UniqueId)\RotationZ = PeekFloat(BNet_Buffer, 41)
|
||||
; Assign Unique Id
|
||||
BNet_Players(UniqueId)\m_UniqueId = UniqueId
|
||||
|
||||
; Assign local UniqueId if we don't have one.
|
||||
If BNet_UniqueId > -1 Then
|
||||
BNet_UniqueId = UniqueId
|
||||
Else
|
||||
CB_BNet_CreatePlayer(UniqueId, BNet_Players(BNet_UniqueId))
|
||||
EndIf
|
||||
Case BNET_PACKET_LOGOUT
|
||||
UniqueId = PeekShort(BNet_Buffer, 1)
|
||||
|
||||
; A logout packet for ourselves will make us logout.
|
||||
If BNet_UniqueId = UniqueId Then
|
||||
BNet_Disconnect():Return True
|
||||
ElseIf BNet_Players(UniqueId) <> Null Then
|
||||
; Otherwise, it is another player removed from visible space.
|
||||
CB_BNet_DeletePlayer(Unique, BNet_Players(UniqueId))
|
||||
|
||||
Delete BNet_Players(UniqueId):BNet_Players(UniqueId) = Null
|
||||
Else
|
||||
DebugLog "BNet: Logout for non-existing player."
|
||||
EndIf
|
||||
Case BNET_PACKET_KICK
|
||||
BNet_Kick = True
|
||||
|
||||
; Read reason for Kick
|
||||
Local ReasonLength% = PeekShort(BNet_Buffer, 1)
|
||||
For ReasonPos = 1 To ReasonLength
|
||||
Local ReasonChar = PeekByte(BNet_Buffer, (3 + ReasonPos - 1))
|
||||
If ReasonChar < 32 Then Exit
|
||||
BNet_Kick_Reason = BNet_Kick_Reason + Chr(NameChar)
|
||||
Next
|
||||
|
||||
BNet_Disconnect():Return True
|
||||
End Select
|
||||
Wend
|
||||
|
||||
; UDP
|
||||
While Not Eof(BNet_Sector_UDP) And ReadAvail(BNet_Sector_UDP) > 0
|
||||
PacketSize = ReadAvail(BNet_Sector_UDP)
|
||||
If PacketSize > BNET_BUFFER_SIZE Then PacketSize = BNET_BUFFER_SIZE
|
||||
ReadBytes(BNet_Buffer, BNet_Sector_UDP, 0, PacketSize)
|
||||
|
||||
PacketId = PeekByte(BNet_Buffer, 0)
|
||||
Select PacketId
|
||||
Case BNET_PACKET_DATA
|
||||
If BNet_UniqueId > -1 Then
|
||||
UniqueId = PeekShort(BNet_Buffer, 1)
|
||||
If BNet_Players(UniqueId) <> Null Then
|
||||
DataFlags = PeekShort(BNet_Buffer, 3)
|
||||
Offset% = 5
|
||||
|
||||
If DataFlags And BNET_DATAFLAGS_POSX Then BNet_Players(UniqueId)\PositionX = PeekInt(BNet_Buffer, Offset):Offset = Offset + 4
|
||||
If DataFlags And BNET_DATAFLAGS_POSY Then BNet_Players(UniqueId)\PositionY = PeekInt(BNet_Buffer, Offset):Offset = Offset + 4
|
||||
If DataFlags And BNET_DATAFLAGS_POSZ Then BNet_Players(UniqueId)\PositionZ = PeekInt(BNet_Buffer, Offset):Offset = Offset + 4
|
||||
If DataFlags And BNET_DATAFLAGS_ROTX Then BNet_Players(UniqueId)\RotationX = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 360.0:Offset = Offset + 2
|
||||
If DataFlags And BNET_DATAFLAGS_ROTY Then BNet_Players(UniqueId)\RotationY = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 360.0:Offset = Offset + 2
|
||||
If DataFlags And BNET_DATAFLAGS_ROTZ Then BNet_Players(UniqueId)\RotationZ = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 360.0:Offset = Offset + 2
|
||||
If DataFlags And BNET_DATAFLAGS_VELOCITY Then
|
||||
BNet_Players(UniqueId)\VelocityX = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 255.0
|
||||
BNet_Players(UniqueId)\VelocityY = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 255.0
|
||||
BNet_Players(UniqueId)\VelocityZ = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 255.0
|
||||
|
||||
Offset = Offset + 6
|
||||
EndIf
|
||||
If DataFlags And BNET_DATAFLAGS_AIM Then
|
||||
BNet_Players(UniqueId)\AimX = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 360.0
|
||||
BNet_Players(UniqueId)\AimY = BNet_FloatFromShort(PeekShort(BNet_Buffer, Offset)) * 360.0
|
||||
|
||||
Offset = Offset + 4
|
||||
EndIf
|
||||
|
||||
; Tell Client to update visual stuff
|
||||
CB_BNet_UpdatePlayer(UniqueId, BNet_Players(UniqueId))
|
||||
EndIf
|
||||
EndIf
|
||||
; Case BNET_PACKET_ACTION
|
||||
; If BNet_UniqueId > -1 Then
|
||||
;
|
||||
; EndIf
|
||||
End Select
|
||||
Wend
|
||||
|
||||
; If we are logged in, tell the server our current data.
|
||||
If BNet_UniqueId > -1 Then
|
||||
Local Time = MilliSecs()
|
||||
|
||||
; Send out whatever is needed.
|
||||
If Time - BNet_Data_LastKeyFrame > BNet_Data_Time_KeyFrame Then
|
||||
; Force Client to update Player object
|
||||
CB_BNet_SendUpdate(BNet_UniqueId, BNet_Player)
|
||||
|
||||
; Packet Id
|
||||
PokeByte BNet_Buffer, 0, BNET_PACKET_DATA
|
||||
; Unique Id
|
||||
PokeShort BNet_Buffer, 1, BNet_UniqueId
|
||||
; Data Flags (KeyFrame always has this at 255, since it contains all data)
|
||||
PokeByte BNet_Buffer, 3, $11111111
|
||||
; Position
|
||||
PokeFloat BNet_Buffer, 4, BNet_Player\PositionX
|
||||
PokeFloat BNet_Buffer, 8, BNet_Player\PositionY
|
||||
PokeFloat BNet_Buffer,12, BNet_Player\PositionZ
|
||||
; Rotation
|
||||
PokeShort BNet_Buffer,16, BNet_ShortFromFloat(BNet_Player\RotationX / 360.0)
|
||||
PokeShort BNet_Buffer,18, BNet_ShortFromFloat(BNet_Player\RotationY / 360.0)
|
||||
PokeShort BNet_Buffer,20, BNet_ShortFromFloat(BNet_Player\RotationZ / 360.0)
|
||||
; Velocity
|
||||
PokeShort BNet_Buffer,22, BNet_ShortFromFloat(BNet_Player\VelocityX / 256.0)
|
||||
PokeShort BNet_Buffer,24, BNet_ShortFromFloat(BNet_Player\VelocityY / 256.0)
|
||||
PokeShort BNet_Buffer,26, BNet_ShortFromFloat(BNet_Player\VelocityZ / 256.0)
|
||||
; Aim
|
||||
PokeShort BNet_Buffer,28, BNet_ShortFromFloat(BNet_Player\AimX / 360.0)
|
||||
PokeShort BNet_Buffer,30, BNet_ShortFromFloat(BNet_Player\AimY / 360.0)
|
||||
|
||||
; Send Packet
|
||||
WriteBytes BNet_Buffer, BNet_Sector_UDP, 0, 32
|
||||
|
||||
; Swap Player objects.
|
||||
TempPlayer = BNet_Player
|
||||
BNet_Player = BNet_Players(BNet_UniqueId)
|
||||
BNet_Players(BNet_UnqiueId) = TempPlayer
|
||||
|
||||
; Set last keyframe and update time to now.
|
||||
BNet_Data_LastKeyFrame = Time
|
||||
BNet_Data_LastUpdate = Time
|
||||
ElseIf Time - BNet_Data_LastUpdate > BNet_Data_Time Then
|
||||
DataFlags = 0
|
||||
Offset = 4
|
||||
|
||||
; Force Client to update Player object
|
||||
CB_BNet_SendUpdate(BNet_UniqueId, BNet_Player)
|
||||
|
||||
; Packet Id
|
||||
PokeByte BNet_Buffer, 0, BNET_PACKET_DATA
|
||||
; Unique Id
|
||||
PokeShort BNet_Buffer, 1, BNet_UniqueId
|
||||
|
||||
; Position
|
||||
If Abs(BNet_Player\PositionX - BNet_Players(BNet_UniqueId)\PositionX) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_POSX
|
||||
PokeFloat BNet_Buffer, Offset, BNet_Player\PositionX:Offset = Offset + 4
|
||||
EndIf
|
||||
If Abs(BNet_Player\PositionY - BNet_Players(BNet_UniqueId)\PositionY) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_POSY
|
||||
PokeFloat BNet_Buffer, Offset, BNet_Player\PositionY:Offset = Offset + 4
|
||||
EndIf
|
||||
If Abs(BNet_Player\PositionZ - BNet_Players(BNet_UniqueId)\PositionZ) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_POSZ
|
||||
PokeFloat BNet_Buffer, Offset, BNet_Player\PositionZ:Offset = Offset + 4
|
||||
EndIf
|
||||
; Rotation
|
||||
If Abs(BNet_Player\RotationX - BNet_Players(BNet_UniqueId)\RotationX) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_ROTX
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\RotationX / 360.0):Offset = Offset + 2
|
||||
EndIf
|
||||
If Abs(BNet_Player\RotationY - BNet_Players(BNet_UniqueId)\RotationY) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_ROTY
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\RotationY / 360.0):Offset = Offset + 2
|
||||
EndIf
|
||||
If Abs(BNet_Player\RotationZ - BNet_Players(BNet_UniqueId)\RotationZ) > BNET_DATA_THRESHOLD_POS Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_ROTZ
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\RotationZ / 360.0):Offset = Offset + 2
|
||||
EndIf
|
||||
; Velocity
|
||||
If Abs(BNet_Player\VelocityX - BNet_Players(BNet_UniqueId)\VelocityX) + Abs(BNet_Player\VelocityY - BNet_Players(BNet_UniqueId)\VelocityY) + Abs(BNet_Player\VelocityZ - BNet_Players(BNet_UniqueId)\VelocityZ) > BNET_DATA_THRESHOLD_VEL Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_VELOCITY
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\VelocityX / 256.0):Offset = Offset + 2
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\VelocityY / 256.0):Offset = Offset + 2
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\VelocityZ / 256.0):Offset = Offset + 2
|
||||
EndIf
|
||||
; Aim
|
||||
If Abs(BNet_Player\AimX - BNet_Players(BNet_UniqueId)\AimX) + Abs(BNet_Player\AimY - BNet_Players(BNet_UniqueId)\AimY) > BNET_DATA_THRESHOLD_AIM Then
|
||||
DataFlags = DataFlags Or BNET_DATAFLAG_AIM
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\AimX / 360.0):Offset = Offset + 2
|
||||
PokeFloat BNet_Buffer, Offset, BNet_ShortFromFloat(BNet_Player\AimY / 360.0):Offset = Offset + 2
|
||||
EndIf
|
||||
|
||||
; Data Flags
|
||||
PokeByte BNet_Buffer, 3, DataFlags
|
||||
|
||||
; Send Packet
|
||||
WriteBytes BNet_Buffer, BNet_Sector_UDP, 0, Offset
|
||||
|
||||
; Swap Player objects.
|
||||
TempPlayer = BNet_Player
|
||||
BNet_Player = BNet_Players(BNet_UniqueId)
|
||||
BNet_Players(BNet_UnqiueId) = TempPlayer
|
||||
|
||||
; Set last update time to now.
|
||||
BNet_Data_LastUpdate = Time
|
||||
EndIf
|
||||
EndIf
|
||||
Else
|
||||
Return False
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function BNet_FloatFromShort#(Value%)
|
||||
Return BNet_Math_ClipF((Value / 65536.0), 0.0, 1.0)
|
||||
End Function
|
||||
|
||||
Function BNet_ShortFromFloat%(Value#)
|
||||
Return BNet_Math_ClipF((Value * 65536.0), 0.0, 1.0)
|
||||
End Function
|
||||
|
||||
Function BNet_Math_MinimumF#(Value#, Min#)
|
||||
If Value < Min Then Return Min
|
||||
Return Value
|
||||
End Function
|
||||
|
||||
Function BNet_Math_MaximumF#(Value#, Max#)
|
||||
If Value > Max Then Return Max
|
||||
Return Value
|
||||
End Function
|
||||
|
||||
Function BNet_Math_ClampF#(Value#, Min#, Max#)
|
||||
If Value < Min Then Return Min
|
||||
If Value > Max Then Return Max
|
||||
Return Value
|
||||
End Function
|
||||
|
||||
Function BNet_Math_ClipF#(Value#, Min#, Max#)
|
||||
Local Out#, Diff#
|
||||
Diff = Max - Min:Out = Value - Min
|
||||
If (Out >= Diff) Or (Out < 0) Then Out = Out - (Floor(Out/Diff) * Diff)
|
||||
Return Min + Out
|
||||
End Function
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Callbacks
|
||||
;----------------------------------------------------------------
|
||||
;Function CB_BNet_CreatePlayer(UniqueId, Player.BNetPlayer)
|
||||
;End Function
|
||||
|
||||
;Function CB_BNet_UpdatePlayer(UniqueId, Player.BNetPlayer)
|
||||
;End Function
|
||||
|
||||
;Function CB_BNet_DeletePlayer(UniqueId, Player.BNetPlayer)
|
||||
;End Function
|
||||
|
||||
;Function CB_BNet_SendUpdate(UniqueId, Player.BNetPlayer)
|
||||
;End Function
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Example
|
||||
;----------------------------------------------------------------
|
||||
Graphics3D 800, 600, 32, 2
|
||||
SetBuffer BackBuffer()
|
||||
SeedRnd MilliSecs()
|
||||
|
||||
Local FrameTimer = CreateTimer(60)
|
||||
Local TimeToLogin = MilliSecs()
|
||||
|
||||
Dim Players.TPlayer(65535)
|
||||
Local Player.TPlayer = Null
|
||||
|
||||
Global TestX#, TestY#, TestZ#
|
||||
Global TestRX#, TestRY#, TestRZ#
|
||||
Global TestVX#, TestVY#, TestVZ#
|
||||
Global TestAX#, TestAY#
|
||||
Local ShipVeloLR#, ShipVeloUD#, ShipVeloFB#
|
||||
Const ShipMaxVeloLR# = 5.0, ShipMaxVeloUD# = 5.0, ShipMaxVeloFB = 30.0
|
||||
|
||||
; Init
|
||||
TestX = Rnd(-2048, 2048)
|
||||
TestY = Rnd(-2048, 2048)
|
||||
TestZ = Rnd(-2048, 2048)
|
||||
TestRX = Rnd(0, 360)
|
||||
TestRY = Rnd(0, 360)
|
||||
TestRZ = Rnd(0, 360)
|
||||
TestVX = 0
|
||||
TestVY = 0
|
||||
TestVZ = 0
|
||||
TestAX = 0
|
||||
TestAY = 0
|
||||
|
||||
|
||||
Local Cam = CreateCamera()
|
||||
Local Conv = CreatePivot()
|
||||
|
||||
Print "[INF] Connecting..."
|
||||
BNet_Initialize()
|
||||
BNet_Connect()
|
||||
If BNet_Connected() Then
|
||||
Print "[INF] Logging in..."
|
||||
BNet_Login("Test " + Rand(0, 65535), TestX, TestY, TestZ, TestRX, TestRY, TestRZ)
|
||||
|
||||
Local LoopExit = False
|
||||
Local Load = False
|
||||
Repeat
|
||||
If Load = False And BNet_UniqueId = -1 And (MilliSecs() - TimeToLogin > 30000) Then
|
||||
Print "[ERR] Failed to login."
|
||||
LoopExit = True
|
||||
ElseIf Load = False And BNet_UniqueId > -1 Then
|
||||
Print "[INF] Logged in."
|
||||
|
||||
Player = New TPlayer
|
||||
Player\Mesh = CreateCone()
|
||||
Player\Player = BNet_Player
|
||||
Players(BNet_UniqueId) = Player
|
||||
EntityParent Cam, Player\Mesh
|
||||
|
||||
Load = True
|
||||
ElseIf Load = True And BNet_UniqueId > -1
|
||||
; Player Input
|
||||
; Velocity
|
||||
ShipVeloFB = BNet_Math_ClampF(ShipVeloFB + (KeyDown(17) - KeyDown(31)) * 2.5, -ShipMaxVeloFB, ShipMaxVeloFB)
|
||||
ShipVeloLR = BNet_Math_ClampF(ShipVeloLR + (KeyDown(32) - KeyDown(30)) * 1.5, -ShipMaxVeloLR, ShipMaxVeloLR)
|
||||
ShipVeloUD = BNet_Math_ClampF(ShipVeloUD + (KeyDown(19) - KeyDown(33)) * 1.5, -ShipMaxVeloUD, ShipMaxVeloUD)
|
||||
; Rotation
|
||||
If KeyDown(57) Then
|
||||
If KeyHit(57) Then MoveMouse 512, 384
|
||||
TestRX = TestRX + ((MouseX() / 512) - 1.0)
|
||||
TestRY = TestRY + ((MouseY() / 384) - 1.0)
|
||||
EndIf
|
||||
TestRZ = TestRZ + (KeyDown(18) - KeyDown(16)) * 1.0
|
||||
|
||||
; Update Game
|
||||
; Slowly scale down Velocity.
|
||||
ShipVeloFB = ShipVeloFB * 0.9
|
||||
ShipVeloLR = ShipVeloLR * 0.8
|
||||
ShipVeloUD = ShipVeloUD * 0.8
|
||||
; Convert Local Velocity to Global Velocity
|
||||
TFormPoint ShipVeloLR, ShipVeloUD, ShipVeloFB, Player\Mesh, Conv
|
||||
TestVX = TFormedX()
|
||||
TestVY = TFormedY()
|
||||
TestVZ = TFormedZ()
|
||||
; Move player by Velocity.
|
||||
TestX = TestX + TestVX
|
||||
TestY = TestY + TestVY
|
||||
TestZ = TestZ + TestVZ
|
||||
|
||||
; Draw Game
|
||||
; Update Local Mesh
|
||||
PositionEntity Player\Mesh, TestX, TestY, TestZ
|
||||
RotateEntity Player\Mesh, TestRX, TestRY, TestRZ
|
||||
; Update conversion point
|
||||
PositionEntity Conv, TestX, TestY, TestZ
|
||||
|
||||
RenderWorld
|
||||
Flip 0
|
||||
EndIf
|
||||
|
||||
If BNet_Update() Then
|
||||
LoopExit = True
|
||||
EndIf
|
||||
WaitTimer FrameTimer
|
||||
Until (LoopExit = True)
|
||||
If BNet_Kick = True Then Print "[INF] Kicked: " + BNet_Kick_Reason Else Print "Logging out."
|
||||
|
||||
BNet_Disconnect()
|
||||
Else
|
||||
Print "[ERR] Failed to connect."
|
||||
EndIf
|
||||
End
|
||||
|
||||
Function CB_BNet_CreatePlayer(UniqueId, Player.BNetPlayer)
|
||||
DebugLog "[INF] New Player: " + Player\Name + "[" + UniqueId + "]"
|
||||
DebugLog " Pos: " + Player\PositionX + ", " + Player\PositionY + ", " + Player\PositionZ
|
||||
DebugLog " Rot: " + Player\RotationX + ", " + Player\RotationY + ", " + Player\RotationZ
|
||||
|
||||
If Players(UniqueId) = Null Then Players(UniqueId) = New TPlayer
|
||||
If Players(UniqueId)\Mesh <> 0 Then Players(UniqueId)\Mesh = CreateCone()
|
||||
Players(UniqueId)\Player = Player
|
||||
|
||||
PositionEntity Players(UniqueId)\Mesh, Player\PositionX, Player\PositionY, Player\PositionZ
|
||||
RotateEntity Players(UniqueId)\Mesh, Player\RotationX, Player\RotationY, Player\RotationZ
|
||||
End Function
|
||||
|
||||
Function CB_BNet_UpdatePlayer(UniqueId, Player.BNetPlayer)
|
||||
If Players(UniqueId) <> Null Then
|
||||
DebugLog "[INF] Update Player: " + Player\Name + "[" + UniqueId + "]"
|
||||
DebugLog " Pos: " + Player\PositionX + ", " + Player\PositionY + ", " + Player\PositionZ
|
||||
DebugLog " Rot: " + Player\RotationX + ", " + Player\RotationY + ", " + Player\RotationZ
|
||||
|
||||
PositionEntity Players(UniqueId)\Mesh, Player\PositionX, Player\PositionY, Player\PositionZ
|
||||
RotateEntity Players(UniqueId)\Mesh, Player\RotationX, Player\RotationY, Player\RotationZ
|
||||
Else
|
||||
DebugLog "[ERR] Player does not exist: " + Player\Name + "[" + UniqueId + "]"
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function CB_BNet_DeletePlayer(UniqueId, Player.BNetPlayer)
|
||||
If Players(UniqueId) <> Null Then
|
||||
DebugLog "[INF] Delete Player: " + UniqueId + "," + Player\Name
|
||||
|
||||
FreeEntity Players(UniqueId)\Mesh
|
||||
Delete Players(UniqueId)
|
||||
Else
|
||||
DebugLog "[ERR] Player does not exist: " + Player\Name + "[" + UniqueId + "]"
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
Function CB_BNet_SendUpdate(UniqueId, Player.BNetPlayer)
|
||||
DebugLog "[INF] Update Self"
|
||||
|
||||
Player\PositionX = TestX
|
||||
Player\PositionY = TestY
|
||||
Player\PositionZ = TestZ
|
||||
|
||||
Player\VelocityX = TestVX
|
||||
Player\VelocityY = TestVY
|
||||
Player\VelocityZ = TestVZ
|
||||
|
||||
Player\RotationX = TestRX
|
||||
Player\RotationY = TestRY
|
||||
Player\RotationZ = TestRZ
|
||||
|
||||
Player\AimX = TestAX
|
||||
Player\AimY = TestAY
|
||||
End Function
|
||||
|
||||
Type TPlayer
|
||||
Field Mesh
|
||||
|
||||
Field Player.BNetPlayer
|
||||
End Type
|
||||
@@ -0,0 +1,8 @@
|
||||
Random Stuff
|
||||
=======================
|
||||
|
||||
Contains remaining projects that don't require their own folder or were modified to fit a specific project. Most of this is from Sirius Online and BlitzHit, so expect nothing to work.
|
||||
|
||||
License
|
||||
=======
|
||||
Random Stuff by Michael Fabian Dirks is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
@@ -0,0 +1,44 @@
|
||||
AppTitle "TrueMotion"
|
||||
Include "TrueMotion.bb"
|
||||
|
||||
Graphics3D 1024,768,0,2
|
||||
SetBuffer BackBuffer()
|
||||
SeedRnd MilliSecs()
|
||||
|
||||
Local eCamera = CreateCamera()
|
||||
|
||||
Local tCube = CreateTexture(128,128)
|
||||
SetBuffer TextureBuffer(tCube)
|
||||
Color 255, 255, 0
|
||||
Rect 0, 0, 64, 64
|
||||
Rect 64, 64, 64, 64
|
||||
Color 0, 127, 255
|
||||
Rect 64, 0, 64, 64
|
||||
Rect 0, 64, 64, 64
|
||||
|
||||
SetBuffer BackBuffer()
|
||||
|
||||
Local eCube = CreateCube()
|
||||
PositionEntity eCube, 0, 0, 3
|
||||
EntityTexture eCube, tCube
|
||||
|
||||
Local tInstance.TrueMotion = TrueMotion_Create(eCamera)
|
||||
|
||||
Timer = CreateTimer(60)
|
||||
|
||||
Global Msec
|
||||
While Not KeyHit(1)
|
||||
Cls
|
||||
|
||||
Msec = Msec + 10
|
||||
RotateEntity eCube, Cos(Msec/4.0)*30, 0, EntityRoll(eCube) + 16
|
||||
PositionEntity eCube, 0, Sin(Msec/4.0)*2, 4
|
||||
|
||||
TrueMotion_RenderWorld(tInstance)
|
||||
Flip
|
||||
WaitTimer(Timer)
|
||||
Wend
|
||||
|
||||
End
|
||||
;~IDEal Editor Parameters:
|
||||
;~C#Blitz3D
|
||||
@@ -0,0 +1,207 @@
|
||||
Type Tradelane
|
||||
Field P1.TVector, P2.TVector, Dir.TVector
|
||||
Field From$, Target$
|
||||
Field FromSpot.Spotmark, TargetSpot.Spotmark
|
||||
|
||||
Field Gates.TList, Lasers.TList
|
||||
Field RingsTop.TList, RingsBot.TList
|
||||
Field RingsInnerTop.TList, RingsInnerBot.TList
|
||||
Field TubesTop.TList, TubesBot.TList
|
||||
|
||||
Field PivotTubes
|
||||
Field PivotRings
|
||||
|
||||
Field MeshA, MeshB, OBBa, OBBb
|
||||
Field XTubeA, XTubeB
|
||||
Field Range
|
||||
End Type
|
||||
|
||||
Const Tradelane_Speed# = 50.0
|
||||
Const Tradelane_SpeedForce# = 0.125
|
||||
Const Tradelane_Force# = 0.1
|
||||
|
||||
Const Tradelane_GateDistance# = 5000.0
|
||||
Const Tradelane_Offset# = 144
|
||||
Const Tradelane_Size# = 115
|
||||
|
||||
Function CreateTradelane(StartX,StartY,StartZ, EndX,EndY,EndZ, From$, Target$)
|
||||
Local T.Tradelane = New Tradelane
|
||||
Local TempVec.TVector
|
||||
|
||||
; Information
|
||||
T\From = From:T\Target = Target
|
||||
|
||||
; Start, End and Direction Vector
|
||||
T\P1 = TVector_Create(StartX, StartY, StartZ)
|
||||
T\P2 = TVector_Create(EndX, EndY, EndZ)
|
||||
TempVec = TVector_Subtract(T\P2, T\P1)
|
||||
T\Dir = TVector_Normalize(TempVec):Delete TempVec
|
||||
Local Range# = TVector_Distance(T\P1, T\P2)
|
||||
|
||||
; Initialize LinkedLists
|
||||
T\Gates = TList_Create()
|
||||
T\Lasers = TList_Create()
|
||||
T\RingsTop = TList_Create()
|
||||
T\RingsBot = TList_Create()
|
||||
T\RingsInnerTop = TList_Create()
|
||||
T\RingsInnerBot = TList_Create()
|
||||
T\TubesTop = TList_Create()
|
||||
T\TubesBot = TList_Create()
|
||||
|
||||
T\PivotRings = CreatePivot(PVTxEFFECT)
|
||||
T\PivotTubes = CreatePivot(PVTxEFFECT)
|
||||
|
||||
; Create Spots
|
||||
T\FromSpot.Spotmark = CreateSpot(T\P1\X, T\P1\Y, T\P1\Z, 21, (From + ">>>" + Target))
|
||||
T\FromSpot.Spotmark = CreateSpot(T\P2\X, T\P2\Y, T\P2\Z, 21, (Target + ">>>" + From))
|
||||
|
||||
; Create OBB Collisions
|
||||
TVector_Angle(T\Dir)
|
||||
Local TX#, TY#, TZ#
|
||||
TX = (T\P1\X + T\P2\X) / 2.0
|
||||
TY = (T\P1\Y + T\P2\Y) / 2.0
|
||||
TZ = (T\P1\Z + T\P2\Z) / 2.0
|
||||
|
||||
Local TVecUp.TVector = TVector_Rotate(T\Dir, -90, 0, 0)
|
||||
T\OBBa=CreateOBB(TX, TY, TZ, TVector_Pitch-90, TVector_Yaw, 0, 115, 115, Range/2)
|
||||
T\OBBb=CreateOBB(TX, TY, TZ, TVector_Pitch-90, TVector_Yaw, 0, 115, 115, Range/2)
|
||||
MoveEntity T\OBBa, 0, Tradelane_Offset, 0
|
||||
MoveEntity T\OBBb, 0, -Tradelane_Offset, 0
|
||||
Delete TVecUp
|
||||
|
||||
; Create Gates, Sprites and Rings
|
||||
Local Count = Ceil(Range / Tradelane_GateDistance)
|
||||
Local Stp# = Range / Count
|
||||
For n = 0 To Count
|
||||
; Calculate Position
|
||||
TempVec = TVector_MultiplyScalar(T\Dir, n*Stp)
|
||||
Local Pos.TVector = TVector_Add(T\P1, TempVec)
|
||||
|
||||
; Create Mesh
|
||||
Local Mesh = CopyEntity(TLxMSH, PVTxNORMAL)
|
||||
PositionEntity Mesh, Pos\X, Pos\Y, Pos\Z
|
||||
AlignToVector Mesh, T\Dir\X, T\Dir\Y, T\Dir\Z, 0, 1
|
||||
RotateEntity Mesh, EntityPitch(Mesh, 1), EntityYaw(Mesh, 1), 0, 1
|
||||
EntityAutoFade Mesh,14500,15000
|
||||
TList_AddLast(T\Gates, Mesh)
|
||||
|
||||
; Create Laser
|
||||
Local Laser = CopyEntity(TLxMSX, Mesh)
|
||||
EntityAutoFade Laser,9500,10000
|
||||
TList_AddLast(T\Lasers, Laser)
|
||||
|
||||
; Create Rings
|
||||
Local RingTop = CopyEntity(TLxENT, Mesh)
|
||||
Local RingBot = CopyEntity(TLxENT, Mesh)
|
||||
MoveEntity RingTop, 0, Tradelane_Offset, 0
|
||||
MoveEntity RingBot, 0, -Tradelane_Offset, 0
|
||||
ScaleSprite RingTop, 125, 125:SpriteViewMode RingTop, 2:EntityColor RingTop, 64, 198, 255:EntityAlpha RingTop, 0.5:EntityAutoFade RingTop, 4500, 5000:EntityBlend RingTop, 3:EntityFX RingTop, 1+16
|
||||
ScaleSprite RingBot, 125, 125:SpriteViewMode RingBot, 2:EntityColor RingBot, 255, 0, 0:EntityAlpha RingBot, 0.5:EntityAutoFade RingBot, 4500, 5000:EntityBlend RingBot, 3:EntityFX RingBot, 1+16
|
||||
TList_AddLast(T\RingsTop, RingTop)
|
||||
TList_AddLast(T\RingsBot, RingBot)
|
||||
|
||||
Local RingInTop = CopyEntity(TLxENT2, Mesh)
|
||||
Local RingInBot = CopyEntity(TLxENT2, Mesh)
|
||||
MoveEntity RingInTop, 0, Tradelane_Offset, 0
|
||||
MoveEntity RingInBot, 0, -Tradelane_Offset, 0
|
||||
ScaleSprite RingInTop, 125, 125:SpriteViewMode RingInTop, 2:EntityAlpha RingInTop, 0.5:EntityAutoFade RingInTop, 4500, 5000:EntityBlend RingInTop, 3:EntityFX RingInTop, 1+16
|
||||
ScaleSprite RingInBot, 125, 125:SpriteViewMode RingInBot, 2:EntityAlpha RingInBot, 0.5:EntityAutoFade RingInBot, 4500, 5000:EntityBlend RingInBot, 3:EntityFX RingInBot, 1+16
|
||||
TList_AddLast(T\RingsInnerTop, RingInTop)
|
||||
TList_AddLast(T\RingsInnerBot, RingInBot)
|
||||
|
||||
; Create Refraction Tubes
|
||||
If n < Count
|
||||
Local Tube
|
||||
Tube = CreateCylinder(24, 0, RingInTop)
|
||||
ScaleEntity Tube, Tradelane_Size, Stp/2, Tradelane_Size
|
||||
TurnEntity Tube, -90, 0, 0
|
||||
MoveEntity Tube, 0, -Stp/2, 0
|
||||
EntityParent Tube, T\PivotTubes
|
||||
EntityAutoFade Tube, 5000, 10000
|
||||
EntityFX Tube, 1+8+16
|
||||
EntityTexture Tube,StarBTex,0,0
|
||||
EntityTexture Tube,ProjectTex,0,1
|
||||
EntityAlpha Tube, 0.35
|
||||
TList_AddLast(T\TubesTop, Tube)
|
||||
|
||||
Tube = CreateCylinder(24, 0, RingInBot)
|
||||
ScaleEntity Tube, Tradelane_Size, Stp/2, Tradelane_Size
|
||||
TurnEntity Tube, 90, 0, 0
|
||||
MoveEntity Tube, 0, Stp/2, 0
|
||||
EntityParent Tube, T\PivotTubes
|
||||
EntityAutoFade Tube, 5000, 10000
|
||||
EntityFX Tube, 1+8+16
|
||||
EntityTexture Tube,StarBTex,0,0
|
||||
EntityTexture Tube,ProjectTex,0,1
|
||||
EntityAlpha Tube, 0.35
|
||||
TList_AddLast(T\TubesBot, Tube)
|
||||
EndIf
|
||||
|
||||
; Delete remaining temporary data
|
||||
Delete Pos:Delete TempVec
|
||||
Next
|
||||
End Function
|
||||
|
||||
Function UpdateTradelane()
|
||||
;set test on
|
||||
TLxTST=0
|
||||
|
||||
; Create Camera Vector
|
||||
Local vCam.TVector = New TVector
|
||||
Local vGate.TVector = New TVector
|
||||
Local vTmp.TVector = Null
|
||||
Local vTmp2.TVector = Null
|
||||
vCam\X = EntityX(cCamera, 1)
|
||||
vCam\Y = EntityY(cCamera, 1)
|
||||
vCam\Z = EntityZ(cCamera, 1)
|
||||
|
||||
For TL.Tradelane = Each Tradelane
|
||||
; Rings
|
||||
TList_Reset(TL\RingsTop):TList_Reset(TL\RingsInnerTop)
|
||||
TList_Reset(TL\RingsBot):TList_Reset(TL\RingsInnerBot)
|
||||
TList_Reset(TL\Gates)
|
||||
While TList_HasNext(TL\Gates)
|
||||
Local RT = TList_Next(TL\RingsTop)
|
||||
Local RTI = TList_Next(TL\RingsInnerTop)
|
||||
Local RB = TList_Next(TL\RingsBot)
|
||||
Local RBI = TList_Next(TL\RingsInnerBot)
|
||||
Local Gate = TList_Next(TL\Gates)
|
||||
|
||||
TurnEntity RT, 0, 0, -.1
|
||||
TurnEntity RB, 0, 0, .1
|
||||
|
||||
vGate\X = EntityX(Gate, 1)
|
||||
vGate\Y = EntityY(Gate, 1)
|
||||
vGate\Z = EntityZ(Gate, 1)
|
||||
|
||||
vTmp = TVector_Subtract(vGate, vCam)
|
||||
vTmp2 = TVector_Normalize(vTmp):Delete vTmp
|
||||
|
||||
Local Dot# = TVector_Dot(TL\Dir, vTmp2):Delete vTmp2
|
||||
If Dot >= 0 Then
|
||||
EntityColor RT, 64, 198, 255
|
||||
EntityColor RB, 255, 0, 0
|
||||
Else
|
||||
EntityColor RB, 64, 198, 255
|
||||
EntityColor RT, 255, 0, 0
|
||||
EndIf
|
||||
Wend
|
||||
|
||||
; Traveling
|
||||
If EntityInOBB(TL\OBBa,pvShip)
|
||||
AlignToVector pvShip, TL\Dir\X, TL\Dir\Y, TL\Dir\Z, 0, Tradelane_Force
|
||||
ShipSpeedZ = (ShipSpeedZ * (1-Tradelane_SpeedForce)) + (Tradelane_Speed * Tradelane_SpeedForce)
|
||||
TLxTST=1
|
||||
EndIf
|
||||
If EntityInOBB(TL\OBBB,pvShip)
|
||||
AlignToVector pvShip, -TL\Dir\X, -TL\Dir\Y, -TL\Dir\Z, 0, Tradelane_Force
|
||||
ShipSpeedZ = (ShipSpeedZ * (1-Tradelane_SpeedForce)) + (Tradelane_Speed * Tradelane_SpeedForce)
|
||||
TLxTST=1
|
||||
EndIf
|
||||
Next
|
||||
Delete vCam:Delete vGate
|
||||
|
||||
PositionTexture StarBTex, Sin(MilliSecs() / 10000.0), (MilliSecs() / 10000.0) Mod 1
|
||||
|
||||
End Function
|
||||
;~IDEal Editor Parameters:
|
||||
@@ -0,0 +1,51 @@
|
||||
Type TrueMotion
|
||||
Field Camera% = 0
|
||||
Field Mesh% = 0
|
||||
End Type
|
||||
|
||||
Function TrueMotion_Create.TrueMotion(Camera%)
|
||||
;Create TrueMotion Instance
|
||||
tInstance.TrueMotion = New TrueMotion
|
||||
|
||||
; Camera can't be Null or invalid
|
||||
If Camera = 0 Then
|
||||
RuntimeError "TrueMotion: Camera is Null."
|
||||
Else
|
||||
If EntityClass(Camera) <> "Camera" Then
|
||||
RuntimeError "TrueMotion: Camera is not of type <Camera>."
|
||||
Else
|
||||
tInstance\Camera = Camera
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
; Create Mesh
|
||||
tInstance\Mesh = CreateMesh(tInstance\Camera)
|
||||
sSurface = CreateSurface(tInstance\Mesh)
|
||||
AddVertex(sSurface, -1, 1, 0, 0, 0)
|
||||
AddVertex(sSurface, 1, 1, 0, 1, 0)
|
||||
AddVertex(sSurface, 1, -1, 0, 1, 1)
|
||||
AddVertex(sSurface, -1, -1, 0, 0, 1)
|
||||
AddTriangle(sSurface, 0, 1, 2)
|
||||
AddTriangle(sSurface, 0, 2, 3)
|
||||
|
||||
EntityOrder(tInstance\Mesh, -1)
|
||||
EntityColor(tInstance\Mesh, 0, 0, 0)
|
||||
PositionEntity(tInstance\Mesh, 0, 0, 1)
|
||||
HideEntity(tInstance\Mesh)
|
||||
|
||||
Return tInstance
|
||||
End Function
|
||||
|
||||
; Call this before after you have done your changes. It is a good practice to only let this effect affect nearby entities.
|
||||
Function TrueMotion_RenderWorld(tInstance.TrueMotion, Steps%=12)
|
||||
ShowEntity(tInstance\Mesh)
|
||||
EntityAlpha(tInstance\Mesh, 1.0/Steps)
|
||||
CameraClsMode(tInstance\Camera, 0, 1)
|
||||
For curStep = 0 To Steps - 1
|
||||
RenderWorld curStep/Float(Steps)
|
||||
Next
|
||||
HideEntity(tInstance\Mesh)
|
||||
RenderWorld 1
|
||||
CameraClsMode(tInstance\Camera, 1, 1)
|
||||
CaptureWorld
|
||||
End Function
|
||||
@@ -0,0 +1,161 @@
|
||||
;VectorMath.bb
|
||||
|
||||
Type TVector
|
||||
Field X#,Y#,Z#
|
||||
End Type
|
||||
|
||||
Global VectorForward.TVector = TVector_Create( 0, 0, 1)
|
||||
Global VectorBackward.TVector = TVector_Create( 0, 0, -1)
|
||||
Global VectorLeft.TVector = TVector_Create(-1, 0, 0)
|
||||
Global VectorRight.TVector = TVector_Create( 1, 0, 0)
|
||||
Global VectorUp.TVector = TVector_Create( 0, 1, 0)
|
||||
Global VectorDown.TVector = TVector_Create( 0, -1, 0)
|
||||
|
||||
Function TVector_Create.TVector(X#,Y#,Z#)
|
||||
Local R.TVector = New TVector
|
||||
R\X = X
|
||||
R\Y = Y
|
||||
R\Z = Z
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_Copy.TVector(A.TVector)
|
||||
Return TVector_Create(A\X,A\Y,A\Z)
|
||||
End Function
|
||||
|
||||
Function TVector_Add.TVector(A.TVector, B.TVector)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X + B\X
|
||||
R\Y = A\Y + B\Y
|
||||
R\Z = A\Z + B\Z
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_AddScalar.TVector(A.TVector, B#)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X + B
|
||||
R\Y = A\Y + B
|
||||
R\Z = A\Z + B
|
||||
Return R
|
||||
End Function
|
||||
|
||||
Function TVector_Subtract.TVector(A.TVector, B.TVector)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X - B\X
|
||||
R\Y = A\Y - B\Y
|
||||
R\Z = A\Z - B\Z
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_SubtractScalar.TVector(A.TVector, B#)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X - B
|
||||
R\Y = A\Y - B
|
||||
R\Z = A\Z - B
|
||||
Return R
|
||||
End Function
|
||||
|
||||
Function TVector_Multiply.TVector(A.TVector, B.TVector)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X * B\X
|
||||
R\Y = A\Y * B\Y
|
||||
R\Z = A\Z * B\Z
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_MultiplyScalar.TVector(A.TVector, B#)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X * B
|
||||
R\Y = A\Y * B
|
||||
R\Z = A\Z * B
|
||||
Return R
|
||||
End Function
|
||||
|
||||
Function TVector_Divide.TVector(A.TVector, B.TVector)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X / B\X
|
||||
R\Y = A\Y / B\Y
|
||||
R\Z = A\Z / B\Z
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_DivideScalar.TVector(A.TVector, B#)
|
||||
Local R.TVector = New TVector
|
||||
R\X = A\X / B
|
||||
R\Y = A\Y / B
|
||||
R\Z = A\Z / B
|
||||
Return R
|
||||
End Function
|
||||
|
||||
Function TVector_Normalize.TVector(A.TVector, MultiPass%=True)
|
||||
Local R1.TVector, R.TVector
|
||||
R = TVector_DivideScalar(A, TVector_Length(A))
|
||||
If MultiPass Then
|
||||
R1 = R
|
||||
R = TVector_DivideScalar(R1, TVector_Length(R1))
|
||||
Delete R1
|
||||
EndIf
|
||||
Return R
|
||||
End Function
|
||||
Function TVector_Rotate.TVector(A.TVector, Pitch#, Yaw#, Roll#)
|
||||
Local M1.TVector = TVector_Create(Cos(Roll) * Cos(Yaw), -Sin(Roll), Sin(Yaw))
|
||||
Local M2.TVector = TVector_Create(Sin(Roll), Cos(Roll) * Cos(Pitch), -Sin(Pitch))
|
||||
Local M3.TVector = TVector_Create(-Sin(Yaw), Sin(Pitch), Cos(Yaw) * Cos(Pitch))
|
||||
|
||||
Local R.TVector = New TVector
|
||||
R\X = (A\X * M1\X) + (A\Y * M1\Y) + (A\Z * M1\Z)
|
||||
R\Y = (A\X * M2\X) + (A\Y * M2\Y) + (A\Z * M2\Z)
|
||||
R\Z = (A\X * M3\X) + (A\Y * M3\Y) + (A\Z * M3\Z)
|
||||
Delete M1:Delete M2:Delete M3:Return R
|
||||
End Function
|
||||
Function TVector_RotateAround.TVector(A.TVector, B.TVector, Pitch#, Yaw#, Roll#)
|
||||
Local R1.TVector = TVector_Subtract(A, B)
|
||||
Local R2.TVector = TVector_Rotate(R1, Pitch, Yaw, Roll)
|
||||
Local R3.TVector = TVector_Add(R2, B)
|
||||
Delete R1:Delete R2:Return R3
|
||||
End Function
|
||||
Function TVector_RotateAroundScalar.TVector(A.TVector, X#, Y#, Z#, Pitch#, Yaw#, Roll#)
|
||||
Local B.TVector = TVector_Create(X,Y,Z)
|
||||
Local R.TVector = TVector_RotateAround(A, B, Pitch, Yaw, Roll)
|
||||
Delete B:Return R
|
||||
End Function
|
||||
|
||||
Function TVector_Dot#(A.TVector, B.TVector)
|
||||
Return ((A\X*B\X)+(A\Y*B\Y)+(A\Z*B\Z))
|
||||
End Function
|
||||
Function TVector_Cross.TVector(A.TVector, B.TVector)
|
||||
Local R.TVector = New TVector
|
||||
R\X = (A\Y*B\Z) - (A\Z*B\Y)
|
||||
R\Y = (A\Z*B\X) - (A\X*B\Z)
|
||||
R\Z = (A\X*B\Y) - (A\Y*B\X)
|
||||
Return R
|
||||
End Function
|
||||
|
||||
Global TVector_Pitch#, TVector_Yaw#
|
||||
Function TVector_Angle(A.TVector) ; X
|
||||
TVector_Pitch = VectorPitch(A\X, A\Y, A\Z);-ATan2(A\Y, Sqr((A\X*A\X) + (A\Z*A\Z))) + 90
|
||||
TVector_Yaw# = VectorYaw(A\X, A\Y, A\Z);ATan2(A\Z, A\X) - 90
|
||||
End Function
|
||||
|
||||
Function TVector_PitchFrom#(A.TVector, B.TVector) ; X
|
||||
;Return ATan2(A\Y-B\Y, A\Z-B\Z)
|
||||
Return VectorPitch(A\X-B\X, A\Y-B\Y, A\Z-B\Z)
|
||||
End Function
|
||||
Function TVector_YawFrom#(A.TVector, B.TVector) ; Y
|
||||
;Return ATan2(A\Z-B\Z, A\X-B\X)
|
||||
Return VectorYaw(A\X-B\X, A\Y-B\Y, A\Z-B\Z)
|
||||
End Function
|
||||
;Function TVector_RollFrom#(A.TVector, B.TVector) ; Z
|
||||
; Return ATan2(A\Y-B\Y, A\X-B\X)
|
||||
;End Function
|
||||
|
||||
Function TVector_Length#(A.TVector)
|
||||
Return Sqr((A\X*A\X)+(A\Y*A\Y)+(A\Z*A\Z))
|
||||
End Function
|
||||
Function TVector_Distance#(A.TVector, B.TVector)
|
||||
Local X# = (A\X-B\X)
|
||||
Local Y# = (A\Y-B\Y)
|
||||
Local Z# = (A\Z-B\Z)
|
||||
Return Sqr(X*X+Y*Y+Z*Z)
|
||||
End Function
|
||||
|
||||
Function TVector_ToString$(A.TVector)
|
||||
Return "{X:"+A\X+";Y:"+A\Y+";Z:"+A\Z+"}"
|
||||
End Function
|
||||
;~IDEal Editor Parameters:
|
||||
;~C#Blitz3D
|
||||
@@ -0,0 +1,8 @@
|
||||
Sirius Online Inventory
|
||||
=======================
|
||||
|
||||
This was ment to be in the Sirius Online project, but the lead devloper never got further than complaining. So it has the side-effect of not working.I planned to optimize it to use as little memory and network bandwidth as possible, but the project was abandoned before I could go that far. Right now, it probably doesn't even work.
|
||||
|
||||
License
|
||||
=======
|
||||
Sirius Online Inventory by Michael Fabian Dirks is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
@@ -0,0 +1,407 @@
|
||||
;----------------------------------------------------------------
|
||||
;-- Constants
|
||||
;----------------------------------------------------------------
|
||||
Const ITEM_MAXIMUMID = 65535
|
||||
; Item Classes
|
||||
Const ITEM_CLASS_WEAPON = 1
|
||||
Const ITEM_CLASS_SHIELD = 2
|
||||
Const ITEM_CLASS_ARMOR = 3
|
||||
Const ITEM_CLASS_ENGINE = 4
|
||||
Const ITEM_CLASS_POWERCORE = 5
|
||||
Const ITEM_CLASS_RESOURCE = 6
|
||||
Const ITEM_CLASS_UPGRADE = 7
|
||||
Const ITEM_CLASS_MININGMODULE = 8
|
||||
Const ITEM_CLASS_ELITIUM = 255
|
||||
; Return Codes
|
||||
Const INVENTORY_RC_OK = 0
|
||||
Const INVENTORY_RC_INVALIDID = -1
|
||||
Const INVENTORY_RC_INVALIDAMOUNT = -2
|
||||
Const INVENTORY_RC_UNKNOWNITEM = -3
|
||||
Const INVENTORY_RC_ITEMTOOBIG = -4
|
||||
Const INVENTORY_RC_ITEMNOTFOUND = -5
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Types
|
||||
;----------------------------------------------------------------
|
||||
; Item Definition
|
||||
Type TItem
|
||||
Field ID%
|
||||
|
||||
Field Name$ ; Name of the Item
|
||||
Field Description$ ; Description of the Item
|
||||
Field Image ; Handle of the Image for the Item
|
||||
|
||||
Field Size% ; Size in cubic decimeters. Divide by 1000 to get the size in cube meters, which is displayed.
|
||||
Field Rarity% ; Rarity
|
||||
|
||||
Field Class% ; Class of the Item.
|
||||
Field AttributeID0%
|
||||
Field AttributeID1%
|
||||
Field AttributeID2%
|
||||
Field AttributeID3%
|
||||
|
||||
; Attributes for Class: Weapon
|
||||
; - Short/255 Electric Damage (Attribute 0 High)
|
||||
; - Short/255 Physical Damage (Attribute 0 Low)
|
||||
; - Short/255 Rate of Fire (Attribute 1 High)
|
||||
; - Short Energy (Attribute 3 High)
|
||||
; - Short/255 CPU (Attribute 3 Low)
|
||||
; Attributes for Class: Shield
|
||||
; - Int Shield Maximum (Attribute 0)
|
||||
; - Short/255 Electric Resist (Attribute 1 High)
|
||||
; - Short/255 Physical Resist (Attribute 1 Low)
|
||||
; - Short Recharge Amount (Attribute 2 High)
|
||||
; - Short Recharge Rate (Attribute 2 Low)
|
||||
; - Short/255 CPU (Attribute 3 Low)
|
||||
; Attributes for Class: Armor
|
||||
; - Int Armor Maximum (Attribute 0)
|
||||
; - Short/255 Speed Modifier (Attribute 1 High)
|
||||
; - Short/255 Turn Modifier (Attribute 1 Low)
|
||||
; - Short/255 Signature Mod. (Attribute 2 High)
|
||||
; Attributes for Class: Engine
|
||||
; - Short/255 Maximum Speed (Attribute 0 High)
|
||||
; - Short/255 Maximum Boost (Attribute 0 Low)
|
||||
; - Short Trace Type (Attribute 2 High)
|
||||
; - Short/255 Trace Threshold (Attribute 2 Low)
|
||||
; - Short Energy (Attribute 3 High)
|
||||
; - Short/255 CPU (Attribute 3 Low)
|
||||
; Attributes for Class: PowerCore
|
||||
; - Int Energy Maximum (Attribute 0)
|
||||
; - Short Energy Amount (Attribute 1 High)
|
||||
; - Short Energy Rate (Attribute 1 Low)
|
||||
; - Byte Elec. Signature (Attribute 2 High 1)
|
||||
; - Byte Scramble Str. (Attribute 2 High 0)
|
||||
; - Short/255 CPU (Attribute 3 Low)
|
||||
; Attributes for Class: Resource
|
||||
; - Byte Processing Tier (Attribute 0 Low 1)
|
||||
; - Byte Load Type (Attribute 0 Low 0)
|
||||
; - Int Half-life Time (Attribute 3)
|
||||
; Attributes for Class: Upgrade
|
||||
; - Byte/127 Armor Bonus (Attribute 0 High 1)
|
||||
; - Byte/127 Shield Bonus (Attribute 0 High 0)
|
||||
; - Byte/127 Speed Bonus (Attribute 0 Low 1)
|
||||
; - Byte/127 Cargo Bonus (Attribute 0 Low 0)
|
||||
; - Byte/127 Elec. Sig. Mod. (Attribute 1 High 1)
|
||||
; - Byte/127 Grav. Sig. Mod. (Attribute 1 High 0)
|
||||
; - Byte/127 Elec. Dmg. Mod. (Attribute 1 Low 1)
|
||||
; - Byte/127 Phys. Dmg. Mod. (Attribute 1 Low 0)
|
||||
; - Byte/127 El. ShRes Mod. (Attribute 2 High 1)
|
||||
; - Byte/127 Ph. ShRes Mod. (Attribute 2 High 0)
|
||||
; - Byte/127 El. ArRes Mod. (Attribute 2 Low 1)
|
||||
; - Byte/127 Ph. ArRes Mod. (Attribute 2 Low 0)
|
||||
; - Byte Scramble Str. (Attribute 3 High 1)
|
||||
; - Byte/127 Yield Bonus (Attribute 3 High 0)
|
||||
; - Byte Energy Max (Attribute 3 Low 1)
|
||||
; - Byte/127 CPU Modifier (Attribute 3 Low 0)
|
||||
; Attributes for Class: Mining Module
|
||||
; -
|
||||
; Attributes for Class: Elitium
|
||||
; -
|
||||
End Type
|
||||
Dim Items.TItem(ITEM_MAXIMUMID)
|
||||
|
||||
; Inventory
|
||||
Type TInventory
|
||||
Field Size% ; Size in cubic meters. Divide by 1000 to get the size in cube meters, which is displayed.
|
||||
Field Used% ; Used space in cubic decimeters. Divide by 1000 to get the size in cube meters, which is displayed.
|
||||
|
||||
; Array containing all Items using their ItemID as index.
|
||||
Field Items.TInventoryItem[ITEM_MAXIMUMID]
|
||||
End Type
|
||||
|
||||
Type TInventoryItem
|
||||
Field Amount% ; How many of this Item are stored?
|
||||
End Type
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Functions
|
||||
;----------------------------------------------------------------
|
||||
; Create a new inventory.
|
||||
Function TInventory_Create.TInventory(Size%)
|
||||
If Size <= 0 Then RuntimeError "TInventory: Size is equal to or less than 0."
|
||||
|
||||
Local lInventory.TInventory = New TInventory
|
||||
lInventory\Size = Size
|
||||
lInventory\Used = 0
|
||||
|
||||
Return lInventory
|
||||
End Function
|
||||
|
||||
; Safely destroy an existing inventory.
|
||||
Function TInventory_Destroy(pInventory.TInventory)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
|
||||
For lIndex = 0 To (ITEM_MAXIMUMID - 1)
|
||||
If pInventory\Items[lIndex] <> Null Then
|
||||
Delete pInventory\Items[lIndex]
|
||||
pInventory\Items[lIndex] = Null
|
||||
EndIf
|
||||
Next
|
||||
|
||||
Delete pInventory
|
||||
End Function
|
||||
|
||||
; Retrieve the amount of items in the inventory with the given ItemID.
|
||||
Function TInventory_GetItemAmount(pInventory.TInventory, ItemID%)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
|
||||
If ItemID >= 0 And ItemID < ITEM_MAXIMUMID Then
|
||||
If Items(ItemID) <> Null Then
|
||||
If pInventory\Items[ItemID] <> Null
|
||||
Return pInventory\Items[ItemID]\Amount
|
||||
Else
|
||||
Return INVENTORY_RC_ITEMNOTFOUND
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_UNKNOWNITEM
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_INVALIDID
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
; Set the amount of items in the inventory with the given ItemID.
|
||||
Function TInventory_SetItemAmount(pInventory.TInventory, ItemID%, Amount%)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
If Amount < 0 Then Return INVENTORY_RC_INVALIDAMOUNT
|
||||
|
||||
If ItemID >= 0 And ItemID < ITEM_MAXIMUMID Then
|
||||
If Items(ItemID) <> Null Then
|
||||
; Remove current Amount and Size from Inventory
|
||||
If pInventory\Items[ItemID] <> Null Then
|
||||
pInventory\Used = pInventory\Used - (Items(ItemID)\Size * pInventory\Items[ItemID]\Amount)
|
||||
If Amount = 0 Then
|
||||
Delete pInventory\Items[ItemID]
|
||||
pInventory\Items[ItemID] = Null
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
If Amount > 0
|
||||
If pInventory\Items[ItemID] = Null pInventory\Items[ItemID] = New TInventoryItem
|
||||
|
||||
Local lAmount = Amount
|
||||
If pInventory\Used + (Items(ItemID)\Size * lAmount) > PInventory\Size Then lAmount = (pInventory\Size - pInventory\Used) / Items(ItemID)\Size
|
||||
|
||||
pInventory\Items[ItemID]\Amount = lAmount
|
||||
pInventory\Used = pInventory\Used + (Items(ItemID)\Size * lAmount)
|
||||
|
||||
Return lAmount
|
||||
Else
|
||||
Return INVENTORY_RC_OK
|
||||
EndIf
|
||||
If pInventory\Items[ItemID] <> Null Then lSize = lSize - (Items(ItemID)\Size * pInventory\Items[ItemID]\Amount)
|
||||
Else
|
||||
Return INVENTORY_RC_UNKNOWNITEM
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_INVALIDID
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
; Add an item to the inventory.
|
||||
; Returns the amount of items added or a negative value (error code)
|
||||
Function TInventory_AddItem(pInventory.TInventory, ItemID%, Amount%=1)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
If Amount <= 0 Then Return INVENTORY_RC_INVALIDAMOUNT
|
||||
|
||||
If ItemID >= 0 And ItemID < ITEM_MAXIMUMID Then
|
||||
If Items(ItemID ) <> Null Then
|
||||
Local lAmount = Amount
|
||||
|
||||
If pInventory\Used + (lAmount * Items(ItemID)\Size) > pInventory\Size Then lAmount = (pInventory\Size - pInventory\Used) / Items(ItemID)\Size
|
||||
|
||||
If lAmount > 0 Then
|
||||
If pInventory\Items[ItemID] = Null Then pInventory\Items[ItemID] = New TInventoryItem
|
||||
pInventory\Items[ItemID]\Amount = pInventory\Items[ItemID]\Amount + lAmount
|
||||
pInventory\Used = pInventory\Used + (lAmount * Items(ItemID)\Size)
|
||||
EndIf
|
||||
|
||||
Return lAmount
|
||||
Else
|
||||
Return INVENTORY_RC_UNKNOWNITEM
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_INVALIDID
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
; Remove an item from the inventory.
|
||||
; Returns the amount of items removed or a negative value (error code).
|
||||
Function TInventory_RemoveItem(pInventory.TInventory, ItemID%, Amount%=1)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
If Amount <= 0 Then Return INVENTORY_RC_INVALIDAMOUNT
|
||||
|
||||
If ItemID >= 0 And ItemID < ITEM_MAXIMUMID Then
|
||||
If Items(ItemID) <> Null Then
|
||||
If pInventory\Items[ItemID] <> Null Then
|
||||
Local lAmount = pInventory\Items[ItemID]\Amount
|
||||
pInventory\Items[ItemID]\Amount = pInventory\Items[ItemID]\Amount - Amount
|
||||
|
||||
If pInventory\Items[ItemID]\Amount <= 0 Then
|
||||
pInventory\Used = pInventory\Used - (lAmount * Items(ItemID)\Size)
|
||||
; Delete Item Entry (Reduces Memory Usage)
|
||||
Delete pInventory\Items[ItemID]:pInventory\Items[ItemID] = Null
|
||||
|
||||
Return lAmount
|
||||
Else
|
||||
pInventory\Used = pInventory\Used - (Amount * Items(ItemID)\Size)
|
||||
|
||||
Return Amount
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_ITEMNOTFOUND
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_UNKNOWNITEM
|
||||
EndIf
|
||||
Else
|
||||
Return INVENTORY_RC_INVALIDID
|
||||
EndIf
|
||||
End Function
|
||||
|
||||
; For when you edit the inventory without using the above functions.
|
||||
Function TInventory_RecalculateUsedSpace(pInventory.TInventory)
|
||||
If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
|
||||
pInventory\Used = 0
|
||||
For lIndex = 0 To (ITEM_MAXIMUMID - 1)
|
||||
If Items(lIndex) <> Null And pInventory\Items[lIndex] <> Null Then
|
||||
pInventory\Used = pInventory\Used + (pInventory\Items[lIndex]\Amount * Items(lIndex)\Size)
|
||||
EndIf
|
||||
Next
|
||||
End Function
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Extras - Require 'LinkedListEmulation.bb'!
|
||||
;----------------------------------------------------------------
|
||||
;Type TInventoryItemLink
|
||||
; Field ItemID%
|
||||
; Field Amount%
|
||||
;End Type
|
||||
;
|
||||
; ; Retrieve a LinkedList containing all items in this inventory.
|
||||
; ; The returned list must be deleted using TInventory_DeleteItemList(...).
|
||||
; ; Modifications on this list can only be applied to the inventory using TInventory_SetItemList(...).
|
||||
;Function TInventory_GetItemList.TList(pInventory.TInventory)
|
||||
; If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
;
|
||||
; Local lList.TList = TList_Create()
|
||||
; For lIndex = 0 To (ITEM_MAXIMUMID - 1)
|
||||
; If Items(lIndex) <> Null And pInventory\Items[lIndex] <> Null Then
|
||||
; Local lInvItemLink.TInventoryItemLink = New TInventoryItemLink
|
||||
; lInvItemLink\ItemID = lIndex
|
||||
; lInvItemLink\Amount = pInventory\Items[lIndex]\Amount
|
||||
; TList_AddLast(lInvItemLink)
|
||||
; EndIf
|
||||
; Next
|
||||
;
|
||||
; Return lList
|
||||
;End Function
|
||||
;
|
||||
;Function TInventory_SetItemList(pInventory.TInventory, pList.TList)
|
||||
; If pInventory = Null Then RuntimeError "TInventory: Inventory does not exist."
|
||||
; If pList = Null Then RuntimeError "TInventory: List does not exist."
|
||||
;
|
||||
; pInventory\Used = 0
|
||||
; For lIndex = 0 To (ITEM_MAXIMUMID - 1)
|
||||
; If Items(lIndex) <> Null And pInventory\Items[lIndex] <> Null Then
|
||||
; Delete pInventory\Items[lIndex]
|
||||
; pInventory\Items[lIndex] = Null
|
||||
; EndIf
|
||||
; Next
|
||||
;
|
||||
; Local lInvItemLink.TInventoryItemLink = Object.TInventoryItemLink(TList_First(pList))
|
||||
; While lInvItemLink <> Null
|
||||
; If lInvItemLink\ItemID >= 0 And lInvItemLink\ItemID < ITEM_MAXIMUMID Then
|
||||
; If Items(lInvItemLink\ItemID) <> Null And lInvItemLink\Amount > 0 Then
|
||||
; pInventory\Items[lInvItemLink\ItemID] = New TInventoryItem
|
||||
; pInventory\Items[lInvItemLink\ItemID]\Amount = lInvItemLink\Amount
|
||||
;
|
||||
; pInventory\Used = pInventory\Used + (lInvItemLink\Amount * Items(lInvItemLink\ItemID)\Size)
|
||||
; EndIf
|
||||
; EndIf
|
||||
; lInvItemLink = Object.TInventoryItemLink(TList_Next(pList))
|
||||
; Wend
|
||||
;End Function
|
||||
;
|
||||
;Function TInventory_DeleteItemList(pList.TList)
|
||||
; If pList = Null Then RuntimeError "TInventory: List does not exist."
|
||||
;
|
||||
; Local lInvItemLink.TInventoryItemLink = Object.TInventoryItemLink(TList_First(pList))
|
||||
; While lInvItemLink <> Null
|
||||
; Delete lInvItemLink
|
||||
; lInvItemLink = Object.TInventoryItemLink(TList_Delete(pList))
|
||||
; Wend
|
||||
;
|
||||
; TList_Destroy(pList)
|
||||
;End Function
|
||||
|
||||
;----------------------------------------------------------------
|
||||
;-- Example
|
||||
;----------------------------------------------------------------
|
||||
SeedRnd(MilliSecs())
|
||||
|
||||
; Test Item
|
||||
Items(0) = New TItem
|
||||
Items(0)\Name = "1dm³ Item"
|
||||
Items(0)\Description = ""
|
||||
Items(0)\Size = 1
|
||||
|
||||
; Test Item
|
||||
Items(1) = New TItem
|
||||
Items(1)\Name = "10dm3 Item"
|
||||
Items(1)\Description = ""
|
||||
Items(1)\Size = 10
|
||||
|
||||
; Test Item
|
||||
Items(2) = New TItem
|
||||
Items(2)\Name = "100dm3 Item"
|
||||
Items(2)\Description = ""
|
||||
Items(2)\Size = 100
|
||||
|
||||
; Test Item
|
||||
Items(3) = New TItem
|
||||
Items(3)\Name = "1000dm3 Item"
|
||||
Items(3)\Description = ""
|
||||
Items(3)\Size = 1000
|
||||
|
||||
Local MyInv.TInventory = TInventory_Create(10000)
|
||||
While Not KeyHit(1)
|
||||
Cls
|
||||
Locate 0,0
|
||||
PrintInv(MyInv)
|
||||
|
||||
If Rand(0,1000) Mod 4 < 2
|
||||
TInventory_AddItem(MyInv, Rand(0,3), Rand(0,100))
|
||||
TInventory_RemoveItem(MyInv, Rand(0,3), Rand(0,100))
|
||||
Else
|
||||
TInventory_SetItemAmount(MyInv, Rand(0,3), Rand(0,100))
|
||||
TInventory_SetItemAmount(MyInv, Rand(0,3), Rand(0,100))
|
||||
EndIf
|
||||
|
||||
WaitKey()
|
||||
Flip 0
|
||||
Wend
|
||||
TInventory_Destroy(MyInv)
|
||||
End
|
||||
|
||||
Function PrintInv(Inv.TInventory)
|
||||
Print "Inventory " + Handle(Inv)
|
||||
Print " Size: " + (Inv\Size/1000) + "." + (Inv\Size Mod 1000) + " m³"
|
||||
Print " Used: " + (Inv\Used/1000) + "." + (Inv\Used Mod 1000) + " m³ / " + Int((Inv\Used/Float(Inv\Size))*100) + "%"
|
||||
Print " Remaining: " + ((Inv\Size - Inv\Used)/1000) + "." + ((Inv\Size - Inv\Used) Mod 1000) + " m³ / " + Int(((Inv\Size - Inv\Used)/Float(Inv\Size))*100) + "%"
|
||||
Print " Items:"
|
||||
For lIndex = 0 To (ITEM_MAXIMUMID - 1)
|
||||
If Items(lIndex) <> Null And TInventory_GetItemAmount(Inv, lIndex) > 0 Then
|
||||
Print " - " + Items(lIndex)\Name + " x" + TInventory_GetItemAmount(Inv, lIndex)
|
||||
EndIf
|
||||
Next
|
||||
; Alternatively, using Extras
|
||||
;Local lList.TList = TInventory_GetItemList(Inv)
|
||||
;Local lEntry.TInventoryItemLink = Object.TInventoryItemLink(TList_First(lList))
|
||||
;While lEntry <> Null
|
||||
; Print " - " + Items(lEntry\ItemID)\Name + " x" + lEntry\Amount
|
||||
; lEntry = Object.TInventoryItemLink(TList_Next(lList))
|
||||
;Wend
|
||||
;TInventory_DestroyItemList(lList)
|
||||
End Function
|
||||
Reference in New Issue
Block a user