Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Mondo

Members
  • Posts

    44
  • Joined

  • Last visited

    Never

Posts posted by Mondo

  1. @Robin:

    > The centralisation problem comes because you ported over the DX8 font width method rather than using the DD7 native to EO.
    >
    > ```
    > Public Function EngineGetTextWidth(ByRef UseFont As String, ByVal text As String) As Integer
    > Dim LoopI As Integer
    >
    >     'Make sure we have text
    >     If LenB(text) = 0 Then Exit Function
    >    
    >     'Loop through the text
    >     For LoopI = 1 To Len(text)
    >         EngineGetTextWidth = EngineGetTextWidth + LetterWidth(Mid$(text, LoopI, 1))
    >     Next LoopI
    >
    > End Function
    >
    > Public Function LetterWidth(ByVal text As String) As Integer
    > ' You can use this function to specify the width of each letter you'll be using, if unspecified space will be 4 pixels.
    >
    >     Select Case text
    >         Case "!", "1", "I", "i", "|", "l", "¡", "'", ".", ",", ";", ":"
    >             LetterWidth = 2
    >         Case Else
    >             LetterWidth = 4
    >     End Select
    > End Function
    >
    > ```
    > Remove all that.
    >
    > Replace all calls with getWidth(). Pass through the DC and the string as the parameters. Will perfectly centralise everything.

    Yep, noticed that and posted it on reply #34… I've updated the tutorial reflecting that.

    Much appreciated.
  2. @tslusny:

    > this totorial still not working for me

    My friend, I told you before, you need to provide error number, error description, module, and subroutine or function as well as where the program breaks if you expect me to provide assistance…

    Just saying that it doesn't work for you does not get you any closer to making it work.
  3. Ok this is the corrected DrawChatBubble routine, it does center now.

    ```
    ' CHAT BUBBLE HACK
    Public Sub DrawChatBubble(ByVal Index As Long)
    Dim theArray() As String, x As Long, Y As Long, i As Long, MaxWidth As Long, xwidth As Long, yheight As Long, colour As Long, x3 As Long, y3 As Long

    Dim MMx As Long
    Dim MMy As Long

    Dim TOPLEFTrect As RECT
    Dim TOPCENTERrect As RECT
    Dim TOPRIGHTrect As RECT
    Dim MIDDLELEFTrect As RECT
    Dim MIDDLECENTERrect As RECT
    Dim MIDDLERIGHTrect As RECT
    Dim BOTTOMLEFTrect As RECT
    Dim BOTTOMCENTERrect As RECT
    Dim BOTTOMRIGHTrect As RECT
    Dim TIPrect As RECT

    ' DESIGNATE CHATBUBBLE SECTIONS FROM CHATBUBBLE IMAGE
    With TOPRIGHTrect
        .top = 0
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With TOPCENTERrect
        .top = 0
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With TOPLEFTrect
        .top = 0
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With MIDDLERIGHTrect
        .top = 4
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With MIDDLECENTERrect
        .top = 4
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With MIDDLELEFTrect
        .top = 4
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With BOTTOMRIGHTrect
        .top = 8
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With BOTTOMCENTERrect
        .top = 8
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With BOTTOMLEFTrect
        .top = 8
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With TIPrect
        .top = 12
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

        Call DDS_BackBuffer.SetForeColor(RGB(255, 255, 255))

        With chatBubble(Index)
            If .targetType = TARGET_TYPE_PLAYER Then
                ' it's a player
                If GetPlayerMap(.target) = GetPlayerMap(MyIndex) Then
                    ' change the colour depending on access
                    colour = QBColor(Yellow)

                    ' it's on our map - get co-ords
                    x = ConvertMapX((Player(.target).x * 32) + Player(.target).XOffset) + 16
                    Y = ConvertMapY((Player(.target).Y * 32) + Player(.target).YOffset) - 16

                    ' word wrap the text
                    WordWrap_Array .Msg, ChatBubbleWidth, theArray

                    ' find max width
                    For i = 1 To UBound(theArray)
                        If getWidth(TexthDC, theArray(i)) > MaxWidth Then MaxWidth = getWidth(TexthDC, theArray(i))
                    Next

                    ' calculate the new position xwidth relative to DDS_ChatBubble and yheight relative to DDS_ChatBubble
                    xwidth = 10 + MaxWidth ' the first five is just air.
                    yheight = 3 + (UBound(theArray) * 7) ' the first three are just air.

                    ' Compensate the yheight drift
                    Y = Y - yheight

                    ' render bubble

                    ' top left
                    ' RenderTexture Tex_GUI(37), xwidth - 9, yheight - 5, 0, 0, 9, 5, 9, 5
                    Call Engine_BltFast(x + (xwidth + 4), Y - (yheight - 4), DDS_ChatBubble, TOPLEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' top center
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, yheight - 5, 119, 0, 9, 5, 9, 5
                    For x3 = x - (xwidth - 8) To x + (xwidth)
                        Call Engine_BltFast(x3, Y - (yheight - 4), DDS_ChatBubble, TOPCENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next x3

                    ' top right
                    ' RenderTexture Tex_GUI(37), xwidth, yheight - 5, 9, 0, MaxWidth, 5, 5, 5
                    Call Engine_BltFast(x - (xwidth - 4), Y - (yheight - 4), DDS_ChatBubble, TOPRIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' middle left
                    ' RenderTexture Tex_GUI(37), xwidth - 9, y, 0, 19, 9, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        Call Engine_BltFast(x + (xwidth + 4), y3, DDS_ChatBubble, MIDDLELEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next y3

                    ' middle center
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, y, 119, 19, 9, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        For x3 = x - (xwidth - 8) To x + (xwidth)
                            Call Engine_BltFast(x3, y3, DDS_ChatBubble, MIDDLECENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                        Next x3
                    Next y3

                    ' middle right
                    ' RenderTexture Tex_GUI(37), xwidth, y, 9, 19, (MaxWidth \ 2) - 5, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        Call Engine_BltFast(x - (xwidth - 4), y3, DDS_ChatBubble, MIDDLERIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next y3

                    ' bottom left
                    ' RenderTexture Tex_GUI(37), xwidth + (MaxWidth \ 2) + 6, y, 9, 19, (MaxWidth \ 2) - 5, 6, 9, 6
                    Call Engine_BltFast(x + (xwidth + 4), Y + (yheight + 4), DDS_ChatBubble, BOTTOMLEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' bottom center
                    ' RenderTexture Tex_GUI(37), xwidth - 9, yheight, 0, 6, 9, (UBound(theArray) * 12), 9, 1
                    For x3 = x - (xwidth - 8) To x + (xwidth)
                        Call Engine_BltFast(x3, Y + (yheight + 4), DDS_ChatBubble, BOTTOMCENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next x3

                    ' bottom right
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, yheight, 119, 6, 9, (UBound(theArray) * 12), 9, 1
                    Call Engine_BltFast(x - (xwidth - 4), Y + (yheight + 4), DDS_ChatBubble, BOTTOMRIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' little pointy bit
                    ' RenderTexture Tex_GUI(37), x - 5, y, 58, 19, 11, 11, 11, 11
                    Call Engine_BltFast(x, Y + (yheight + 8), DDS_ChatBubble, TIPrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' Lock the backbuffer so we can draw text and names
                    TexthDC = DDS_BackBuffer.GetDC

                    ' render each line centralised
                    Y = Y - (yheight - 5)
                    For i = 1 To UBound(theArray)
                        DrawTextNoShadow TexthDC, x - (getWidth(TexthDC, theArray(i)) - 10), Y, theArray(i), QBColor(Black) ' .colour
                        Y = Y + 12
                    Next

                    ' Release DC
                    DDS_BackBuffer.ReleaseDC TexthDC

                End If
            End If

            ' check if it's timed out - close it if so
            If .Timer + 5000 < GetTickCount Then
                .active = False
            End If
        End With
    End Sub
    ' CHAT BUBBLE HACK
    ```
    The real change was only to call the old getwidth function, passing the texthdc and the message as parameters and it gets you back the real size, no guesstimating anymore.

    Try it out…
  4. @Lumiere:

    > Uhm when i write something and press enter the game closes,there is no error ,weird.

    It did that to me once, the game closes when its broken in a routine that has no error handler…

    When it happened to me it was that my game didn't know how to handle the server message to open a bubble... So it could be somewhere in handlechatbubble routine or the declarations of the SChatBubble in the serverpackets.
  5. @Domino_:

    > Smtn works
    >
    > ```
    >                 ' render each line centralised
    >                 Y = Y - (yheight - 5)
    >                 For i = 1 To UBound(theArray)
    >                     DrawTextNoShadow TexthDC, x - (xwidth - (x / xwidth)), Y, theArray(i), QBColor(Black)  ' .colour
    >                     Y = Y + 12
    >                 Next
    >
    > ```
    > P.S. _**THIS WORKS ONLY WITH ~ 5+ LETTERS!**_ :(

    Its a bit more complicated than that, you need to calculate the TextDC width, and then half that and substract that amount from the bubble's xwidth divided by half… or something fo the sort... ;)

    I'll review it tonight after work.
  6. @or3o:

    > Do you by chance know how to edit  the maps files so ya dont have to delete all you maps?

    Sorry unfortunately I have no clue on how to do that… I'd suggest Commenting Server Step 3\. hence you don't read the animation attribute (nor will it mess up when it tries to render)...

    Once the map is in memory you save it again, this WILL write the Animation record in each tile... Do this for all your maps and then uncomment the Step 3.

    Its a long shot but it might just work...
  7. @Erwin:

    > It looks like something that can be fixed by using Trim$ a few times, but I'm not sure that will work.

    Yeah the problem really is that the text width is "calculated" by guess estimating the pixel sizes of each letter, so you calculate the bubble size to be say 150 pixels, but the reality is that your text is only 100 pixels long (o's being wider that i's). And since the text is placed on the left border of the bubble you get 50 pixels of air.

    I've been thinking to fix it, just never came around it.

    The magic will happen at:

    ```
                    ' render each line centralised
                    y = y - (yheight - 5)
                    For i = 1 To UBound(theArray)
                        DrawTextNoShadow TexthDC, x - (xwidth - 10), y, theArray(i), QBColor(Black) ' .colour
                        y = y + 12
                    Next

    ```
    I just need to figure out how to better calculate x - (xwidth - 10).
  8. @tslusny:

    > i tried it another time and my server just freeze when i add animation to map, do you tested this if it really works? i think there is infinite loop or something like that

    I have it working on my on EO 2.0 and works quite well, even if the whole map is filled with animations… I think you need to set up a break point in the bltmapanimation routine and start debugging from there...
  9. @Erwin:

    > This still doesn't work perfect, I get Sub or Function not Defined at:
    > ```
    > Call DeleteObject(GameFont)
    > ```

    This requires that your EO 2.0 be loaded with the the Font Memory Leak fix ALREADY applied ( http://www.touchofdeathforums.com/smf/index.php/topic,71691.0.html) if you haven't applied the FIX then DO SO before you begin with this.

    The Font Memory Leak Fix has that declaration:

    ```
    Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
    ```
  10. @tslusny:

    > Do you tested this? becouse this code has some mess with frames for me and when i tryied to change frames on animation in animation editor my pcs graphic card crashed….. so i think u dont tested it :confused:

    Really, what mess did you find in the frames? Coz the tutorial only asked to create ONE new frame, and edit an existing one and all this it had to be done on frmedit_map, I never touched a thing in the animation editor…

    So if its crashing on you I'd suggest rolling back the changes or going back to a backup and running through the tutorial again.

    If you need assistance please provide printscreens of the errors, or error numbers and descriptions and the module, routine and line of code where you get the error.

    cheers
  11. Hello,

    This is my second tutorial so I haven't practiced this enough, I might still mess up. 

    Anyhow, I never found complete Map Animations Tutorial on the forums and certainly found no information about it except for (again) a quick suggestion from Robin on how to place the sprite animations in the map and again some comments from other programmers regarding this already being done on Robin's Amazing Package.

    Robin's Map Animation does not overload the server/client with network traffic, it just sends the map once, which includes all the animations in it, and they're all handled on client side.

    So I spent a couple hours again hacking the Amazing Package code looking for what I needed to make this work on EO 2.0 and now that I found it I thought I'd share it with the EO community.

    As always, credits for this superb piece of software engineering go to Robin Perris, author of Robin's Amazing Package ([http://www.touchofdeathforums.com/smf/index.php/topic,74117.0.html](http://www.touchofdeathforums.com/smf/index.php/topic,74117.0.html)) where I grabbed the Map Animation code from.

    On that note I invite again you to review Robin's bundle, its filled with tons of lessons and good stuff lying there ready for the grabbing if you are patient enough to look for it.

    Try not just to copy paste what you see, and go a step further and try to understand what the code actually does.

    Disclaimer: Please keep in mind this is a crude implementation and may probably need some major tweaking, again i hope it will help anyone who is looking for simple map animations and can't find how to make'em work.

    What you'll need:

    VB6 Enterprise Edition
    EO 2.0 Source Code.
    The ability to DEBUG, set a breakpoint and follow steps.
    Skills to use the "find" function, or have a good eye for the nested code.
    Tons, and tons, and tons of patience.

    So again lets get messy…

    **ON SERVER SIDE**

    **STEP1\. On modTypes in Type TileRec**

    Find:

    ```
        DirBlock As Byte

    ```Add BELOW That:

    ```
        Animation As Long

    ```
    **STEP 2\. On modHandleData in HandleMapData**

    Find:

    ```
                Map(mapNum).Tile(x, y).DirBlock = Buffer.ReadByte

    ```Add BELOW That:

    ```
    ' MAP ANIMATION HACK
                Map(mapNum).Tile(x, y).Animation = Buffer.ReadLong
    ' MAP ANIMATION HACK

    ```
    **STEP 3\. On modServerTCP in MapCache_Creat**

    Find:

    ```
                    Buffer.WriteByte .DirBlock

    ```Add BELOW that:

    ```
    ' MAP ANIMATION HACK
                    Buffer.WriteLong .Animation
    ' MAP ANIMATION HACK

    ```
    **ON CLIENT SIDE**

    **STEP 1\. on ModTypes un Public Type TileRec**

    Find:

    ```
        DirBlock As Byte

    ```
    Add BELOW that:

    ```
    ' MAP ANIMATION HACK
        Animation As MapAnimRec
    ' MAP ANIMATION HACK

    ```
    Before the Public Type TileRec add the following code:

    ```
    ' MAP ANIMATION HACK
    Public Type MapAnimRec
        Animation As Long
        Timer(0 To 1) As Long
        FrameIndex(0 To 1) As Long
    End Type
    ' MAP ANIMATION HACK

    ```
    **STEP 2\. On modHandleData in HandleMapData**

    Find:

    ```
                Map.Tile(x, y).DirBlock = Buffer.ReadByte

    ```
    Add BELOW that:

    ```
    ' MAP ANIMATION HACK
                Map.Tile(x, y).Animation.Animation = Buffer.ReadLong
    ' MAP ANIMATION HACK

    ```
    **STEP 3\. on ModDirectDraw7 add a new routine**

    ```
    ' MAP ANIMATION HACK
    Public Sub BltMapAnimation(ByVal x As Long, ByVal y As Long, ByVal layer As Long)
        Dim animNum As Long
        Dim Sprite As Integer
        Dim sRECT As DxVBLib.RECT
        Dim dRECT As DxVBLib.RECT
        Dim i As Long
        Dim width As Long, height As Long
        Dim FrameCount As Long

        animNum = Map.Tile(x, y).Animation.Animation

        If animNum = 0 Or animNum > MAX_ANIMATIONS Then Exit Sub
        Sprite = Animation(animNum).Sprite(layer)

        If Sprite < 1 Or Sprite > NumAnimations Then Exit Sub

        FrameCount = Animation(animNum).Frames(layer)

        AnimationTimer(Sprite) = GetTickCount + SurfaceTimerMax

        If DDS_Animation(Sprite) Is Nothing Then
            Call InitDDSurf("animations\" & Sprite, DDSD_Animation(Sprite), DDS_Animation(Sprite))
        End If

        ' total width divided by frame count
        width = DDSD_Animation(Sprite).lWidth / FrameCount
        height = DDSD_Animation(Sprite).lHeight

        sRECT.top = 0
        sRECT.Bottom = height
        sRECT.left = (Map.Tile(x, y).Animation.FrameIndex(layer)) * width
        sRECT.Right = sRECT.left + width

        x = ConvertMapX(x * 32) + 16 - (width / 2)
        y = ConvertMapY(y * 32) + 16 - (height / 2)

        ' Clip to screen
        If y < 0 Then

            With sRECT
                .top = .top - y
            End With

            y = 0
        End If

        If x < 0 Then

            With sRECT
                .left = .left - x
            End With

            x = 0
        End If

        If y + height > DDSD_BackBuffer.lHeight Then
            sRECT.Bottom = sRECT.Bottom - (y + height - DDSD_BackBuffer.lHeight)
        End If

        If x + width > DDSD_BackBuffer.lWidth Then
            sRECT.Right = sRECT.Right - (x + width - DDSD_BackBuffer.lWidth)
        End If

        Call Engine_BltFast(x, y, DDS_Animation(Sprite), sRECT, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
    End Sub
    ' MAP ANIMATION HACK

    ```
    **STEP 4\. On modDirectDraw7 modify Render_Graphics function add call to BltMapAnimation**

    Right after:

    ```
        ' Blit out the items

    ```
    And Before

    ```
    'draw animations

    ```
    Add the following code:

    ```
    ' MAP ANIMATION HACK
        ' draw low-level map animations this is layer 0 (BELOW PLAYER)
        For x = 0 To Map.MaxX
            For y = 0 To Map.MaxY
                BltMapAnimation x, y, 0
            Next
        Next
    ' MAP ANIMATION HACK

    ```
    Then Find:

    ```
        ' animations
        If NumAnimations > 0 Then

    ```

    Add ABOVE that:

    ```
    ' MAP ANIMATION HACK
        ' draw low-level map animations this is layer 1 above the player.
        For x = 0 To Map.MaxX
            For y = 0 To Map.MaxY
                BltMapAnimation x, y, 1
            Next
        Next
    ' MAP ANIMATION HACK

    ```
    **STEP 5\. On ModGameLogic add routine**

    ```
    ' MAP ANIMATION HACK
    Public Sub CheckMapAnim(ByVal x As Long, ByVal y As Long)
        Dim animNum As Long
        Dim layer As Long
        Dim loopTime As Long
        Dim FrameCount As Long

        animNum = Map.Tile(x, y).Animation.Animation

        ' if it doesn't exist then exit sub
        If animNum = 0 Or animNum > MAX_ANIMATIONS Then Exit Sub

        For layer = 0 To 1
            loopTime = Animation(animNum).loopTime(layer)
            FrameCount = Animation(animNum).Frames(layer) -1 ' This is to prevent an odd flicker in the animations.

            ' make sure we don't have an extra frame
            If Map.Tile(x, y).Animation.FrameIndex(layer) = 0 Then Map.Tile(x, y).Animation.FrameIndex(layer) = 1

            If Map.Tile(x, y).Animation.Timer(layer) + loopTime <= GetTickCount Then
                ' out of range?
                If Map.Tile(x, y).Animation.FrameIndex(layer) >= FrameCount Then
                    Map.Tile(x, y).Animation.FrameIndex(layer) = 0 ' Because we do have frame 0 on animations, don't we?
                Else
                    Map.Tile(x, y).Animation.FrameIndex(layer) = Map.Tile(x, y).Animation.FrameIndex(layer) + 1
                End If
                Map.Tile(x, y).Animation.Timer(layer) = GetTickCount
            End If
        Next
    End Sub
    ' MAP ANIMATION HACK

    ```
    **Step 6\. On modGameLogic modify routine GameLoop**

    Add the following code on the top of the routine

    ```
    Dim x as long
    Dim y as long

    ```
    Then Find :

    ```
                tmr25 = Tick + 25

    ```

    Add ABOVE that:

    ```
                ' MAP ANIMATION HACK
                For x = 0 To Map.MaxX
                    For y = 0 To Map.MaxY
                        CheckMapAnim x, y
                    Next
                Next
                ' MAP ANIMATION HACK

    ```

    **STEP 7\. On modGameEditors in function MapEditorMouseDown**

    Find:

    ```
                            setDirBlock Map.Tile(CurX, CurY).DirBlock, CByte(i), Not isDirBlocked(Map.Tile(CurX, CurY).DirBlock, CByte(i))
                            Exit Sub
                        End If
                    End If
                Next

    ```
    Add BELOW that:

    ```
    ' MAP ANIMATION HACK
            ElseIf frmEditor_Map.optAnim.Value Then
                ' set the animation on that tile
                Map.Tile(CurX, CurY).Animation.Animation = frmEditor_Map.lstAnimation.ListIndex + 1
            End If
    ' MAP ANIMATION HACK

    ```
    Note that you'll have an extra END IF after… remove it.

    Find:

    ```
                    .Data3 = 0
                End With

    ```
    Add BELOW that:

    ```
    ' MAP ANIMATION HACK
            ElseIf frmEditor_Map.optAnim.Value Then
                Map.Tile(CurX, CurY).Animation.Animation = 0
            End If
    ' MAP ANIMATION HACK

    ```
    Note that you'll have an extra END IF after this… remove it.

    **STEP 8\. on ModGameEditors in MapEditorInit**

    Find:

    ```
        ' Error handler
    End Sub

    ```
    Add ABOVE that:

    ```
    ' MAP ANIMATION HACK
        ' populate the anim list
        frmEditor_Map.lstAnimation.Clear
        For i = 1 To MAX_ANIMATIONS
            frmEditor_Map.lstAnimation.AddItem i & ": " & Animation(i).Name
        Next
        frmEditor_Map.lstAnimation.ListIndex = 0
    ' MAP ANIMATION HACK

    ```
    **STEP 9\. On frmEditor_Map add a new routines:**

    ```
    ' MAP ANIMATION HACK
    Private Sub cmdAnimClear_Click()
    Dim x As Long, y As Long
        For x = 0 To Map.MaxX
            For y = 0 To Map.MaxY
                Map.Tile(x, y).Animation.Animation = 0
            Next
        Next
    End Sub

    Private Sub cmdAnimFill_Click()
    Dim x As Long, y As Long
        For x = 0 To Map.MaxX
            For y = 0 To Map.MaxY
                Map.Tile(x, y).Animation.Animation = lstAnimation.ListIndex + 1
            Next
        Next
    End Sub

    Private Sub optAnim_Click()
        fraAnimation.Visible = True
    End Sub
    ' MAP ANIMATION HACK

    ```

    On Sub OptBlock_Click() Find :

    ```
    End Sub
    ```
    Add ABOVE that:

    ```
    ' MAP ANIMATION HACK
        fraAnimation.Visible = False
    ' MAP ANIMATION HACK
    ```
    On Sub OptLayer_Click() Find :

    ```
        ' Error handler
        Exit Sub
    ```
    Add ABOVE that:

    ```
    ' MAP ANIMATION HACK
        fraAnimation.Visible = False
    ' MAP ANIMATION HACK
    ```
    On Sub optAttribs_Click() Find :

    ```
        ' Error handler
        Exit Sub
    ```
    Add ABOVE that:

    ```
    ' MAP ANIMATION HACK
        fraAnimation.Visible = False
    ' MAP ANIMATION HACK
    ```
    **STEP 10\. MODIFY FORM frmEditor_Map**

    You're looking to get something that will look like this.

    ![](http://www.touchofdeathforums.com/smf/index.php?action=dlattach;topic=79806.0;attach=20963;image)

    Add a Radio button inside fraType frame call the RadioButton optAnim, set Caption to "Animation". you'll need to move stuff around to make room for this.
    Add a new Frame on the form, call it FraAnimation, set Caption to "Animation", set it on top of the picback picturebox
    Inside fraAnimation add the following controls.
    Add a ListBox, name it lstAnimation
    Add a Button, name it cmdAnimFill, set Caption to "Fill"
    Add a Button, name it cmdAnimClear, set Caption to "Clear"
    Add a Label, name it LabelAnimInstr, set Caption to "Select an animation from the list then place it on the map. Loop count will be ignored and the animation will continiously play. To close this box, simply select 'Layers', 'Attributes' or 'Block'."

    Set compiler to 350° and cook until done…

    **_The How's and Why's…_**

    So what we did here on the server side was:

    Modify the TileRec and add support for the Animation

    Modify the HandleMapData routine to read the message from the client that includes the animations.

    Modify the MapCache_Create routine to Write the message to the client that includes the animations.

    On the Client side we did:

    Modify the TileRec and add support for the Animation, and added a MapAnimRec to store animation data for the map.

    Modified the HandleMapData to read the message from the server where we get the Animation data.

    Added a routine to DirectDraw that Builds the Map Animation (BltMapAnimation)

    Modified the Render_Graphics routine to add the looks to Build the map animations for the two layers, below the player, and above the player.

    Added a new routine to the GameLogic that would check the map animations (checkAnimMap) and this one is in charge of moving forward the frames for each sprite animation on the map.

    Modified the GameLoop Routine to include a couple declarations and a call to the CheckAnimMap function for each tile, this way we make the sprites animate.

    Modified the MapEditorMouseDown so it knows what to do when we click the right or left mouse button and we are on the optAnim checkbox.

    Modified the MapEditorInit to make it load the Map Editor Animation list with all the goodies from the animations.

    And finally modified the Map Editor form, adding controls that we needed, and the code behind those controls.

    Again, if you have questions I'll be happy to try and answer them. If you have any problems retry all the steps in the tutorial a couple times, and if you run into some severe problems I'll do my best to assist you, but be reminded that **this tutorial is delivered AS-IS**… It might not work for you, but then again if you tweak it enough it just might.

    Hope this is useful for you all! :)

    Cheers!
  12. @crzy:

    > First its Cause and second shouldn't you automatically add the fix for the leak to the tutorial instead of forcing people to add it?

    First thank you for correcting my grammar the last thing I want is to confuse someone with my poor spelling.

    Second, no I won't do a tutorial of something that's already been explained elsewhere. However I will fix my noshadow routine to include the fix and indicate that its a prerequisite for this tutorial that you MUST have the Font Memory Leak fix in place in order for this to work.

    Thank you for your input.
  13. @Justn:

    > K gonna play around with it later thanks for sharing
    >
    > This is what I was asking about http://www.touchofdeathforums.com/smf/index.php/topic,71691.0.html

    Awesome! Now I can use a different font! :D

    I tested it the Chat Bubbles with the Font Fix and it works ok, however you'll need to modify the DrawTextNoShadow. Copy the Memory Leak Free DrawText function in it, and then remove the shadow effect again.
  14. @Justn:

    > Very cool mondo.. does this work/look any better with text rendering memory leak fix?

    Well it should work with any DrawText function you provide… You only need to change the DrawTextNoShadow with in the DrawChatBubble function with whatever you use to draw text now...

    I didn't know there was a text rendering memory leak fix!
  15. Hello,

    UPDATE: JULY 8th, 2012… If you already applied this tutorial and you're having centering text issues you MIGHT want to review it and apply it again, I've modified the tutorial to get rid of some old deprecated functions in favor for something better.

    However if you're already satisfied with what you have then by all means leave it AS IS.

    This is my first tutorial so I urge you to bear with me. 

    I never found a Chat Bubble tutorial and found no information on how to build one except for a quick suggestion from Robin on how to make it work and some comments regarding this already being available on CrystalShire.

    So I went and hacked my way through CrystalShire to get one for my game… And now that I got it I thought I'd be able to give a little helping hand back to the EO community, from whom I've learned a lot, by sharing what I learned.

    As always, credits for this superb piece of software engineering go to Robin Perris, author of EO and CrystalShire where I grabbed the Bubble Chat code from, and Debbie The Fabulous for her 7's Upgraded Minimap Tutorial  (http://www.touchofdeathforums.com/smf/index.php/topic,75443.0.html) from where I learned how to work the Direct Draw Surfaces.

    On that note I invite you to review as many tutorials as you can and try not just to copy/paste what you see in them. Go a step further and try to understand what the code actually does in hope that you can understand how pieces work, and how they'd behave if you combine them differently.

    In the end you'll get something like this...

    ![](http://www.touchofdeathforums.com/smf/index.php?action=dlattach;topic=79790.0;attach=20955;image)

    **Disclaimer: Please keep in mind this is a crude implementation and it needs major tweaking, I'm sharing it in hope that it will help anyone who is looking for simple the Bubble Chats to build from.**

    What you'll need:

    VB6 Enterprise Edition
    EO 2.0 Source Code _**with the Font Memory Leak fix ALREADY applied ( http://www.touchofdeathforums.com/smf/index.php/topic,71691.0.html) if you haven't applied it then DO SO before you begin with this.**_
    Debugging skills (yep, you'll need to know how to set a breakpoint and understand errors).
    Tons of patience.

    So lets get messy…

    **SERVER SIDE**

    **STEP 1\. On modEnumerations modify the ServerPackets**

    Find:

    ```
        ' Make sure SMSG_COUNT is below everything else
        SMSG_COUNT

    ```
    Add the following code ABOVE that:

    ```
        ' CHAT BUBBLE HACK
        SChatBubble
        ' CHAT BUBBLE HACK

    ```
    **STEP 2\. On ModHandleData Edit HandleSayMsg Sub**

    Find the following code at the end of the subroutine:

    ```
    Set Buffer = Nothing

    ```
    Add the following code ABOVE that.

    ```
        ' CHAT BUBBLE HACK
        Call SendChatBubble(GetPlayerMap(index), index, TARGET_TYPE_PLAYER, Msg, White)
        ' CHAT BUBBLE HACK

    ```
    **STEP 3\. On modServerTCP add Sub SendChatBubble anywhere in the module.**

    ```
    ' CHAT BUBBLE HACK
    Sub SendChatBubble(ByVal mapNum As Long, ByVal target As Long, ByVal targetType As Long, ByVal message As String, ByVal Colour As Long)
    Dim Buffer As clsBuffer

        Set Buffer = New clsBuffer
        Buffer.WriteLong SChatBubble
        Buffer.WriteLong target
        Buffer.WriteLong targetType
        Buffer.WriteString message
        Buffer.WriteLong Colour
        SendDataToMap mapNum, Buffer.ToArray()
        Set Buffer = Nothing
    End Sub
    ' CHAT BUBBLE HACK

    ```
    And thats it on this end.

    **CLIENT SIDE**

    MOST IMPORTANT Download the attached file "chatbubble.bmp" to your folder /data files/graphics

    **STEP 1\. On modEnumerations modify the ServerPackets**

    Find:

    ```
        ' Make sure SMSG_COUNT is below everything else
        SMSG_COUNT

    ```
    Add the following code ABOVE that:

    ```
        ' CHAT BUBBLE HACK
        SChatBubble
        ' CHAT BUBBLE HACK

    ```
    **STEP 2\. on modHandleData add the following code on the InitMessages**

    Find:

    ```
        ' Error handler
        Exit Sub

    ```
    Add the following code ABOVE that.

    ```
        ' CHAT BUBBLE HACK
        HandleDataSub(SChatBubble) = GetAddress(AddressOf HandleChatBubble)
        ' CHAT BUBBLE HACK

    ```
    **STEP 3\. On modHandleData add the following subroutine anywhere in the module.**

    ```
    ' CHAT BUBBLE HACK
    Private Sub HandleChatBubble(ByVal Index As Long, ByRef Data() As Byte, ByVal StartAddr As Long, ByVal ExtraVar As Long)
    Dim Buffer As clsBuffer, targetType As Long, target As Long, message As String, colour As Long

        Set Buffer = New clsBuffer
        Buffer.WriteBytes Data()

        target = Buffer.ReadLong
        targetType = Buffer.ReadLong
        message = Buffer.ReadString
        colour = Buffer.ReadLong

        AddChatBubble target, targetType, message, colour
        Set Buffer = Nothing
    End Sub
    ' CHAT BUBBLE HACK

    ```
    **STEP 4\. On modTEXT add the following sub at the bottom of the module**

    ```
    ' BUBBLE CHAT HACK
    Public Sub WordWrap_Array(ByVal text As String, ByVal MaxLineLen As Long, ByRef theArray() As String)
    Dim lineCount As Long, i As Long, size As Long, lastSpace As Long, b As Long

        'Too small of text
        If Len(text) < 2 Then
            ReDim theArray(1 To 1) As String
            theArray(1) = text
            Exit Sub
        End If

        ' default values
        b = 1
        lastSpace = 1
        size = 0

        For i = 1 To Len(text)
            ' if it's a space, store it
            Select Case Mid$(text, i, 1)
                Case " ": lastSpace = i
                Case "_": lastSpace = i
                Case "-": lastSpace = i
            End Select

            'Add up the size
            size = size + getWidth(TexthDC, Mid$(Text, i, 1))

            'Check for too large of a size
            If size > MaxLineLen Then
                'Check if the last space was too far back
                If i - lastSpace > 12 Then
                    'Too far away to the last space, so break at the last character
                    lineCount = lineCount + 1
                    ReDim Preserve theArray(1 To lineCount) As String
                    theArray(lineCount) = Trim$(Mid$(text, b, (i - 1) - b))
                    b = i - 1
                    size = 0
                Else
                    'Break at the last space to preserve the word
                    lineCount = lineCount + 1
                    ReDim Preserve theArray(1 To lineCount) As String
                    theArray(lineCount) = Trim$(Mid$(text, b, lastSpace - b))
                    b = lastSpace + 1

                    'Count all the words we ignored (the ones that weren't printed, but are before "i")
                    size = getWidth(TexthDC, Mid$(text, lastSpace, i - lastSpace))
                End If
            End If

            ' Remainder
            If i = Len(text) Then
                If b <> i Then
                    lineCount = lineCount + 1
                    ReDim Preserve theArray(1 To lineCount) As String
                    theArray(lineCount) = theArray(lineCount) & Mid$(text, b, i)
                End If
            End If
        Next
    End Sub

    Public Function WordWrap(ByVal text As String, ByVal MaxLineLen As Integer) As String
    Dim TempSplit() As String
    Dim TSLoop As Long
    Dim lastSpace As Long
    Dim size As Long
    Dim i As Long
    Dim b As Long

        'Too small of text
        If Len(text) < 2 Then
            WordWrap = text
            Exit Function
        End If

        'Check if there are any line breaks - if so, we will support them
        TempSplit = Split(text, vbNewLine)

        For TSLoop = 0 To UBound(TempSplit)

            'Clear the values for the new line
            size = 0
            b = 1
            lastSpace = 1

            'Add back in the vbNewLines
            If TSLoop < UBound(TempSplit()) Then TempSplit(TSLoop) = TempSplit(TSLoop) & vbNewLine

            'Only check lines with a space
            If InStr(1, TempSplit(TSLoop), " ") Or InStr(1, TempSplit(TSLoop), "-") Or InStr(1, TempSplit(TSLoop), "_") Then

                'Loop through all the characters
                For i = 1 To Len(TempSplit(TSLoop))

                    'If it is a space, store it so we can easily break at it
                    Select Case Mid$(TempSplit(TSLoop), i, 1)
                        Case " ": lastSpace = i
                        Case "_": lastSpace = i
                        Case "-": lastSpace = i
                    End Select

                    'Add up the size
                    size = size + getwidth(TexthDC, Mid$(TempSplit(TSLoop), i, 1)))

                    'Check for too large of a size
                    If size > MaxLineLen Then
                        'Check if the last space was too far back
                        If i - lastSpace > 12 Then
                            'Too far away to the last space, so break at the last character
                            WordWrap = WordWrap & Trim$(Mid$(TempSplit(TSLoop), b, (i - 1) - b)) & vbNewLine
                            b = i - 1
                            size = 0
                        Else
                            'Break at the last space to preserve the word
                            WordWrap = WordWrap & Trim$(Mid$(TempSplit(TSLoop), b, lastSpace - b)) & vbNewLine
                            b = lastSpace + 1

                            'Count all the words we ignored (the ones that weren't printed, but are before "i")
                            size = getwidth(TexthDC, Mid$(TempSplit(TSLoop), lastSpace, i - lastSpace))
                        End If
                    End If

                    'This handles the remainder
                    If i = Len(TempSplit(TSLoop)) Then
                        If b <> i Then
                            WordWrap = WordWrap & Mid$(TempSplit(TSLoop), b, i)
                        End If
                    End If
                Next i
            Else
                WordWrap = WordWrap & TempSplit(TSLoop)
            End If
        Next TSLoop
    End Function

    Public Function getWidth(ByVal DC As Long, ByVal text As String) As Long
        ' If debug mode, handle error then exit out
        If Options.Debug = 1 Then On Error GoTo errorhandler

        getWidth = frmMain.TextWidth(text) \ 2

        ' Error handler
        Exit Function
    errorhandler:
        HandleError "getWidth", "modText", Err.Number, Err.Description, Err.Source, Err.HelpContext
        Err.Clear
        Exit Function
    End Function

    ' CHANGE FONT FIX
    ' PLEASE NOTE THIS WILL FAIL MISERABLY IF YOU DIDN'T APPLY THE FONT MEMORY LEAK FIX FIRST
    ' CHAT BUBBLE HACK
    ' I ONLY DID THIS COZ THE CHATBUBBLE TEXT LOOKS BETTER WITHOUT SHADOW OVER WHITE BUBBLES!
    Public Sub DrawTextNoShadow(ByVal hdc As Long, ByVal x, ByVal y, ByVal text As String, color As Long)
        ' If debug mode, handle error then exit out
        Dim OldFont As Long ' HFONT

        If Options.Debug = 1 Then On Error GoTo errorhandler

        Call SetFont(FONT_NAME, FONT_SIZE)
        OldFont = SelectObject(hdc, GameFont)
        Call SetBkMode(hdc, vbTransparent)
        Call SetTextColor(hdc, color)
        Call TextOut(hdc, x, y, text, Len(text))
        Call SelectObject(hdc, OldFont)
        Call DeleteObject(GameFont)

        ' Error handler
        Exit Sub
    errorhandler:
        HandleError "DrawTextNoShadow", "modText", Err.Number, Err.Description, Err.Source, Err.HelpContext
        Err.Clear
        Exit Sub
    End Sub
    ' CHAT BUBBLE HACK
    ' CHANGE FONT FIX

    ```
    **STEP 5\. On modTypes add the following type anywhere**

    ```
    ' CHAT BUBBLE HACK
    Public Type ChatBubbleRec
        Msg As String
        colour As Long
        target As Long
        targetType As Byte
        timer As Long
        active As Boolean
    End Type
    ' CHAT BUBBLE HACK

    ```
    **Step 6\. On modGameLogic add this subroutine anywhere in the module**

    ```
    ' CHAT BUBBLE HACK
    Public Sub AddChatBubble(ByVal target As Long, ByVal targetType As Byte, ByVal Msg As String, ByVal colour As Long)
    Dim i As Long, Index As Long

        ' set the global index
        chatBubbleIndex = chatBubbleIndex + 1
        If chatBubbleIndex < 1 Or chatBubbleIndex > MAX_BYTE Then chatBubbleIndex = 1

        ' default to new bubble
        Index = chatBubbleIndex

        ' loop through and see if that player/npc already has a chat bubble
        For i = 1 To MAX_BYTE
            If chatBubble(i).targetType = targetType Then
                If chatBubble(i).target = target Then
                    ' reset master index
                    If chatBubbleIndex > 1 Then chatBubbleIndex = chatBubbleIndex - 1
                    ' we use this one now, yes?
                    Index = i
                    Exit For
                End If
            End If
        Next

        ' set the bubble up
        With chatBubble(Index)
            .target = target
            .targetType = targetType
            .Msg = Msg
            .colour = colour
            .timer = GetTickCount
            .active = True
        End With
    End Sub
    ' CHAT BUBBLE HACK

    ```
    **STEP 7\. On modConstants add the following constant anywhere in the module.**

    ```
    ' CHAT BUBBLE HACK
    Public Const ChatBubbleWidth As Long = 200
    Public Const Font_Default As String = "Default"
    ' CHAT BUBBLE HACK

    ```
    **STEP 8\. On modGlobals add the following global variables anywhere in the module.**

    ```
    ' chat bubble hack
    Public chatBubble(1 To MAX_BYTE) As ChatBubbleRec
    Public chatBubbleIndex As Long
    ' chat bubble hack

    ```
    **STEP 9\. On modDirectDraw7 add the following variables on the declarations section (anywhere on top  of the module).**

    ```
    ' CHAT BUBBLE HACK
    Public DDS_ChatBubble As DirectDrawSurface7
    Public DDSD_ChatBubble As DDSURFACEDESC2
    ' CHAT BUBBLE HACK

    ```
    **STEP 10\. On modDirectDraw7 add the following code in the InitSurfaces subroutine.**

    Find:

    ```
        ' count the blood sprites
        BloodCount = DDSD_Blood.lWidth / 32

    ```
    Add this code ABOVE that.

    ```
        ' CHAT BUBBLE HACK
        If FileExist(App.Path & "\data files\graphics\chatbubble.bmp", True) Then Call InitDDSurf("chatbubble", DDSD_ChatBubble, DDS_ChatBubble)
        ' CHAT BUBBLE HACK

    ```
    **STEP 11\. On modDirectDraw7 add the following code to the DestroyDirectDraw subroutine.**

    Find:

    ```
        Set DDS_BackBuffer = Nothing

    ```
    Add the following code ABOVE that.

    ```
        ' CHAT BUBBLE HACK
        Set DDS_ChatBubble = Nothing
        ZeroMemory ByVal VarPtr(DDSD_ChatBubble), LenB(DDSD_ChatBubble)
        ' CHAT BUBBLE HACK

    ```
    **STEP 12\. on modDirectDraw7 add the following code on the Render_Graphics subroutine**

    Find:

    ```
        ' Release DC
        DDS_BackBuffer.ReleaseDC TexthDC

    ```
    Add the following code BELOW that:

    ```
        ' CHAT BUBBLES HACK
        ' draw the messages at the very top!
        For i = 1 To MAX_BYTE
            If chatBubble(i).active Then
                DrawChatBubble i
            End If
        Next
        ' CHAT BUBBLES HACK

    ```
    **STEP 13\. On modDirectDraw7 add the following subroutine anywhere in the module.**

    ```
    ' CHAT BUBBLE HACK
    Public Sub DrawChatBubble(ByVal Index As Long)
    Dim theArray() As String, x As Long, Y As Long, i As Long, MaxWidth As Long, xwidth As Long, yheight As Long, colour As Long, x3 As Long, y3 As Long

    Dim MMx As Long
    Dim MMy As Long

    Dim TOPLEFTrect As RECT
    Dim TOPCENTERrect As RECT
    Dim TOPRIGHTrect As RECT
    Dim MIDDLELEFTrect As RECT
    Dim MIDDLECENTERrect As RECT
    Dim MIDDLERIGHTrect As RECT
    Dim BOTTOMLEFTrect As RECT
    Dim BOTTOMCENTERrect As RECT
    Dim BOTTOMRIGHTrect As RECT
    Dim TIPrect As RECT

    ' DESIGNATE CHATBUBBLE SECTIONS FROM CHATBUBBLE IMAGE
    With TOPRIGHTrect
        .top = 0
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With TOPCENTERrect
        .top = 0
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With TOPLEFTrect
        .top = 0
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With MIDDLERIGHTrect
        .top = 4
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With MIDDLECENTERrect
        .top = 4
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With MIDDLELEFTrect
        .top = 4
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With BOTTOMRIGHTrect
        .top = 8
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

    With BOTTOMCENTERrect
        .top = 8
        .Bottom = .top + 4
        .Left = 4
        .Right = .Left + 4
    End With

    With BOTTOMLEFTrect
        .top = 8
        .Bottom = .top + 4
        .Left = 8
        .Right = .Left + 4
    End With

    With TIPrect
        .top = 12
        .Bottom = .top + 4
        .Left = 0
        .Right = .Left + 4
    End With

        Call DDS_BackBuffer.SetForeColor(RGB(255, 255, 255))

        With chatBubble(Index)
            If .targetType = TARGET_TYPE_PLAYER Then
                ' it's a player
                If GetPlayerMap(.target) = GetPlayerMap(MyIndex) Then
                    ' change the colour depending on access
                    colour = QBColor(Yellow)

                    ' it's on our map - get co-ords
                    x = ConvertMapX((Player(.target).x * 32) + Player(.target).XOffset) + 16
                    Y = ConvertMapY((Player(.target).Y * 32) + Player(.target).YOffset) - 16

                    ' word wrap the text
                    WordWrap_Array .Msg, ChatBubbleWidth, theArray

                    ' find max width
                    For i = 1 To UBound(theArray)
                        If getWidth(TexthDC, theArray(i)) > MaxWidth Then MaxWidth = getWidth(TexthDC, theArray(i))
                    Next

                    ' calculate the new position xwidth relative to DDS_ChatBubble and yheight relative to DDS_ChatBubble
                    xwidth = 10 + MaxWidth ' the first five is just air.
                    yheight = 3 + (UBound(theArray) * 7) ' the first three are just air.

                    ' Compensate the yheight drift
                    Y = Y - yheight

                    ' render bubble

                    ' top left
                    ' RenderTexture Tex_GUI(37), xwidth - 9, yheight - 5, 0, 0, 9, 5, 9, 5
                    Call Engine_BltFast(x + (xwidth + 4), Y - (yheight - 4), DDS_ChatBubble, TOPLEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' top center
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, yheight - 5, 119, 0, 9, 5, 9, 5
                    For x3 = x - (xwidth - 8) To x + (xwidth)
                        Call Engine_BltFast(x3, Y - (yheight - 4), DDS_ChatBubble, TOPCENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next x3

                    ' top right
                    ' RenderTexture Tex_GUI(37), xwidth, yheight - 5, 9, 0, MaxWidth, 5, 5, 5
                    Call Engine_BltFast(x - (xwidth - 4), Y - (yheight - 4), DDS_ChatBubble, TOPRIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' middle left
                    ' RenderTexture Tex_GUI(37), xwidth - 9, y, 0, 19, 9, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        Call Engine_BltFast(x + (xwidth + 4), y3, DDS_ChatBubble, MIDDLELEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next y3

                    ' middle center
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, y, 119, 19, 9, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        For x3 = x - (xwidth - 8) To x + (xwidth)
                            Call Engine_BltFast(x3, y3, DDS_ChatBubble, MIDDLECENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                        Next x3
                    Next y3

                    ' middle right
                    ' RenderTexture Tex_GUI(37), xwidth, y, 9, 19, (MaxWidth \ 2) - 5, 6, 9, 6
                    For y3 = Y - (yheight - 8) To Y + (yheight)
                        Call Engine_BltFast(x - (xwidth - 4), y3, DDS_ChatBubble, MIDDLERIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next y3

                    ' bottom left
                    ' RenderTexture Tex_GUI(37), xwidth + (MaxWidth \ 2) + 6, y, 9, 19, (MaxWidth \ 2) - 5, 6, 9, 6
                    Call Engine_BltFast(x + (xwidth + 4), Y + (yheight + 4), DDS_ChatBubble, BOTTOMLEFTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' bottom center
                    ' RenderTexture Tex_GUI(37), xwidth - 9, yheight, 0, 6, 9, (UBound(theArray) * 12), 9, 1
                    For x3 = x - (xwidth - 8) To x + (xwidth)
                        Call Engine_BltFast(x3, Y + (yheight + 4), DDS_ChatBubble, BOTTOMCENTERrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
                    Next x3

                    ' bottom right
                    ' RenderTexture Tex_GUI(37), xwidth + MaxWidth, yheight, 119, 6, 9, (UBound(theArray) * 12), 9, 1
                    Call Engine_BltFast(x - (xwidth - 4), Y + (yheight + 4), DDS_ChatBubble, BOTTOMRIGHTrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' little pointy bit
                    ' RenderTexture Tex_GUI(37), x - 5, y, 58, 19, 11, 11, 11, 11
                    Call Engine_BltFast(x, Y + (yheight + 8), DDS_ChatBubble, TIPrect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)

                    ' Lock the backbuffer so we can draw text and names
                    TexthDC = DDS_BackBuffer.GetDC

                    ' render each line centralised
                    Y = Y - (yheight - 5)
                    For i = 1 To UBound(theArray)
                        DrawTextNoShadow TexthDC, x - (getWidth(TexthDC, theArray(i)) - 10), Y, theArray(i), QBColor(Black) ' .colour
                        Y = Y + 12
                    Next

                    ' Release DC
                    DDS_BackBuffer.ReleaseDC TexthDC

                End If
            End If

            ' check if it's timed out - close it if so
            If .Timer + 5000 < GetTickCount Then
                .active = False
            End If
        End With
    End Sub
    ' CHAT BUBBLE HACK

    ```
    And we're done!

    _**WARNING: I ADDED A DRAWTEXTNOSHADOW FUNCTION ~~COZ~~ BECAUSE THE CHATBUBBLE TEXT LOOKS BETTER WITHOUT SHADOW OVER WHITE BUBBLES! IF YOU HAVE NOT APPLIED THE  FONT MEMORY LEAK FIX YOU'LL NEED TO DO SO OTHERWISE THIS ROUTINE WILL FAIL!**_

    The How's and Why's…

    On the server side we did the following create a new package so we can send it to the client whenever we'll fire a message from a user into the map (handled by HandleSayMsg subroutine), and the function that sends this package and the message to the client.

    On the client side we created a new package so we can understand the server, created a Direct Draw Surface named BubbleChat where we'll do the drawing and texting.

    Added a couple constants so we know the chat bubble size and for compatibility with a function that expects a font name.

    Added a Chat bubble rec type, where we store message, player that owns it, color, etc.

    Added a function that handles the reception of the bubble chat package.

    Added a routine to add a new bubble if it does not exist or recycle an old bubble.

    Modified the rendering routine to add for the call to the chatboxes if they're active

    Added a function to split the length of the message to fit in 200 pixels which we said it was our biggest chat bubble.

    ~~This one deserves extra attention, since I didn't know how replicate the EngineGetTextWidth function on DD7 (yet!) the Word Wrapper routines would not work. So I build a routine named LetterWidth and this routine will provide an "estimate" pixel size per character (by default I set it to 4 pixels per letter) for characters that are "slim" like ": | ' , . : ;" etc… the bubble size is not 100% accurate, so you might want to build a better map of character sizes for it to be more precise...~~

    Added the getWidth function, which uses the textwidth function on the main form to calculate the size of the text, and then just splits that value in half to get a center, I believe you can actually tweak it to be more precise if you specify the form font to match the font you'll be using in your chatbubbles, but I leave that to you.

    Then added a draw the chat bubbles function that takes a tile, chops it in to 10 pieces each comprising a a side of the bubble, the center and the little tip of it. Each section of the chat bubble is a 4x4 pixel piece, you may modify this by changing the size and then the respective RECTs and recalculate your positions accordingly.

    And finally added a function that draws text without a shadow so that black text won't look funky with black shadow.

    And thats it!

    If you have any questions I'll be happy to try and respond them (provided you give me enough information and not just say "it does not work for me"), and if you run into some problems I'll do my best to assist, but **be reminded this tutorial is delivered AS-IS… it might not work for you, but if you tweak it enough it just might.**

    Cheers!
  16. I got it! :D

    I was locking the draw surface for text render and attempted to draw on it, so that didn't budge!

    It was a tough exercise, specially because I know jack of DD7, and in the end I could not replicate the EngineGetTextWidth function built from DD8 (I never figured out a way to get DD7 to give me back the relative width of each character), so I build a map of my own :P and round up the rest. I know its lame… but functional!

    Cheers! :)
  17. Hello Eclipse Community,

    Here is the deal, I'm building a small game in my spare time (ain't we all) to play with my D&D friends and I'd like to add a few extra things to the EO 2.0 engine (like we all do). Things that altho might be simple for the seasoned programmer that knows the code require programming skills for DirectDraw that just elude me.

    While I've studied and applied half a dozen tutorials shared by cool people in this forums I decided to tackle a bigger challenge… Acquiring the unattainable CrystalShire bubble chat on EO.

    So yeah, I went and got me a copy of the Robin's CS source code, studied it and I got all that I need it out of it, the packets, the handlers, the functions, and applied it to EO.  It all works flawlessly until I get to the call for the dreaded ChatBubbleDraw function...

    I get the text to pop up and dissapear, like the chat bubble would do... but the drawing of the chat bubble itself simply is not budging! :(

    As I said, in my limited DD7 knowledge I also made an effort to render the chat bubble (via the Engine_BltFast), I tried to put together the lessons I've learned on many tutorials and even tried my best to downgrade the DD8 code to DD7, but I just hit a mental dead end... And no bubble shows up!

    So rather than bang my head on a wall for weeks on end looking for all the wrong ways to make this work I'm willing to pay someone some money to take a look at the code I got and not only fix it and tell me where I ducked up, but to also to teach me the DD7 things I don't understand.

    So if there are any serious takers out there, you have some spare time, the knowledge, the patience and a reasonable price I'd love to hear from you.

    Cheers!
×
×
  • Create New...