CB Game Launcher

Oletko tehnyt jotain, mistä muut voisivat hyötyä. Postita vinkit tänne.
Post Reply
User avatar
Jonez
Devoted Member
Posts: 575
Joined: Mon Aug 27, 2007 8:37 pm

CB Game Launcher

Post by Jonez »

Pitkästä aikaa taas näpräsin CB:llä. Tällä kertaa sain aikaiseksi yksinkertaisen launcherin. Idea kuitenkin on, että tämä on pieni ohjelma jota levitetään itse cb-pelin yhteydessä. Pelaaja voi sitten valita launcherista haluamansa näyttötilan ja käynnistää pelin. Ajatuksena oli, että jos nyt jollekin tulee mieleen antaa käyttäjilleen mahdollisuuden valita useamman näyttötilan väliltä, voi säästää pari tuntia elämästään ja käyttää tätä ohjelmaa :)

Edit. Nimi muutettu vähän järkevämmäksi (starter -> launcher). Jopa yksi resoluutio lisätty.

Launcher lähdekoodi. Pelintekijän siis kuuluu säätää alkuarvot haluamikseen (selitetään kommenteissa), laittaa näyttötilat jotka haluaa tarjota pelaajille, kääntää launcheri exeksi ja jakaa pelinsä kanssa.

Code: Select all

Const SCREEN_WIDTH = 300
Const SCREEN_HEIGHT = 400

//SCREEN SCREEN_WIDTH, SCREEN_HEIGHT
SAFEEXIT OFF

REMSTART
/*  
    MUUTTUJAT:
    - SetWindow
        Muista asettaa startterin titleksi vaikka pelisi nimi
    - YFLIP
        CB:n ominaisuuksia... Jotkut cb-kääntäjät flippaavat y-akselin väärinpäin kun kuvaan kirjoitetaan.
        Eli jos PopUp-ikkunassa ei näy tekstiä, vaihda tämä vastakkaismerkkiseksi (-1 tai 1)
    - SHOW_UNAVAILABLE_MODES (1/0)
        Kertoo voiko pelaaja nähdä (ja käyttää) myös sellaisia näyttötiloja ikkuna / resize-ruudussa, jota hänen koneensa ei
        tue kokoruudun tilassa
    - WINDOW_MODE_AVAILABLE (1/0)
        Voiko pelaaja valita ikkunatilan peliin
    - FULLSCREEN_MODE_AVAILABLE (1/0)
        Voiko pelaaja valita kokoruudun tilan
    - RESIZABLE_MODE_AVAILABLE (1/0)
        Voiko pelaaja valita resized -tilan
    - SMOOTH2D_FORCE (1, 2, 3)
        0 = Käyttäjä saa valita onko Smooth2D ON tai OFF
        1 = Smooth2D aina OFF
        2 = Smooth2D aina ON
    - gGfxFile
        Tiedosto johon tiedot tallennetaan, ja josta peli ne lukee
    - gGameName
        Pelisi nimi. Tämän täytyy vastata oikean pelin nimeä, jotta launcher osaa käynnistää pelin
    - Funktio: CreateScreenList()
        Sisältää funktiokutsuja "AddCandidate(width, height)". Poista ja lisää niitä vaihtoehtoja joita haluat pelaajilla olevan.
    
    HUOM!
    KATSO KOODIN VIIMEISILLÄ RIVEILLÄ OLEVAT HUOMAUTUKSET
*/
REMEND

SetWindow "Insert Game Name Here"

//Jos popUp-ikkunassa ei näy tekstiä, muuta tämä vastakkaismerkkiseksi (eli -1 tai 1)
Const YFLIP                         = -1 

//Antaa käyttäjälle mahdollisuuden käyttää sellaista ikkunamodea jota kokoruudun tila ei tukisi.
//Tämä on automaattisesti 0 jos ainoastaan FULLSCREEN_MODE_AVAILABLE = 1
Const SHOW_UNAVAILABLE_MODES        = 1

//Mitä modeja käyttäjä voi valita
Const WINDOW_MODE_AVAILABLE         = 1
Const FULLSCREEN_MODE_AVAILABLE     = 1
Const RESIZABLE_MODE_AVAILABLE      = 1

//arvo 0 = käyttäjä saa päättää
//arvo 1 = smooth2d aina OFF
//arvo 2 = smooth2d aina ON
Const SMOOTH2D_FORCE                = 0

//Tiedosto johon kirjoitetaan, ja josta peli lukee 
Global gGfxFile As String, gGameName As String
gGfxFile = "launchergfx.dat"
gGameName = "MyGame.exe"

//Luodaan screen vaihtoehdot
Function CreateScreenList()

    //Lisätään listaan manuaalisesti kaikki halutut screen-modet
    AddCandidate(640, 480)
    AddCandidate(800, 600)
    AddCandidate(1024, 768)
    AddCandidate(1280, 800)   
    AddCandidate(1280, 1024)
    AddCandidate(1440, 900)
    AddCandidate(1600, 1200)
    AddCandidate(1900, 1200)
    AddCandidate(1920, 1080)
    AddCandidate(1680, 1050)

    //Katsotaan mitkä käy
    CheckCompatibility()
    
EndFunction


//*********************************************************************************
//  Tästä eteenpäin startterin omaa koodia, jota ei tarvitse välttämättä muuttaa
// (muista katsoa koodin alimmat rivit)
//*********************************************************************************

Type tScreenList
    Field width
    Field height
    Field bit
    Field isSupported As Byte
EndType

Type tButtons
    Field x
    Field y
    Field width
    Field height
    Field txt As String
    Field isPushable As Byte    
    Field state As Byte
EndType

Type tPopUpList
    Field img
    Field x
    Field y
    Field width
    Field height
    Field isPopped
    Field scrolly
    Field listSize
EndType

CreateScreenList()

Global gClsR, gClsG, gClsB

Global gHeadFont, gNameFont, gNormFont

Global gCurrentScreenMode, gWndMode, gSmooth2D
gCurrentScreenMode = ConvertToInteger(First(tScreenList))

btnExit = CreateButton( 10, 360, 80, 30, "Exit")
btnSave = CreateButton( 110, 360, 80, 30, "Save")
btnLaunch = CreateButton( 210, 360, 80, 30, "Save and launch")

btnWndMode = CreateButton( 200, 10, 80, 20, "", 0)
wndMode = NextWndMode(-1, btnWndMode)

btnSmooth2d = CreateButton( 200, 40, 80, 20, "", 0)
modeSmooth2d = NextSmooth2dMode(0, btnSmooth2d)

StartScreen()

popUp = CreatePopUpList(10, 10, 150, 300)

ClsColor2(230, 230, 240)
Color cbBlack
Repeat

    DrawBackground()
    
    DrawPopUpList(popUp)

    If DrawButton(btnExit) Then End
    
    If DrawButton(btnSave) Then Save()

    If DrawButton(btnLaunch) Then
        Save()
        If FileExists(gGameName) = False Or IsDirectory(gGameName) Then
            MakeError "Couldn't find the game! Check that Launcher is in the original directory" + Chr(13) + Chr(10) + "(You can make a shortcut if you want to change the directory)"
        Else
            Execute gGameName
            End
        EndIf
    EndIf
    
    cs.tScreenList = ConvertToType(gCurrentScreenMode)
    If DrawButton(btnWndMode) Or (wndMode = 0 And cs\isSupported = False) Then
        wndMode = NextWndMode(wndMode, btnWndMode)
    EndIf
    
    If DrawButton(btnSmooth2d) Then
        modeSmooth2d = NextSmooth2dMode(modeSmooth2D, btnSmooth2d)
    EndIf

    DrawScreen
Forever

Function Save()
    cs.tScreenList = ConvertToType(gCurrentScreenMode)
    
    f = OpenToWrite(gGfxFile)
        WriteInt f, cs\width
        WriteInt f, cs\height
        WriteInt f, cs\bit
        WriteInt f, gWndMode
        WriteInt f, gSmooth2D
    CloseFile f
EndFunction

Function ClsColor2(r, g, b)
    gClsR = r
    gClsG = g
    gClsB = b
    ClsColor r, g, b
EndFunction

Function DrawPopUpList(list)
    cp.tPopUpList = ConvertToType(list)

    If mouseOn(cp\x, cp\y, cp\x + cp\width - 20, cp\y + 20) Then
        If MouseHit(1) Then
            cp\isPopped = Not cp\isPopped
        EndIf

    Else
        If MouseHit(1) And mouseOn(cp\x, cp\y, cp\x + cp\width, cp\y + cp\height) = False Then
           cp\isPopped = False
        EndIf
    EndIf

    Color cbWhite
    Box cp\x, cp\y, cp\width - 20, 20
   
    If cp\isPopped Then
        
        If mouseOn(cp\x, cp\y + 20, cp\x + cp\width - 20, cp\y + cp\height) Then         
            mouseOnPopUp = True
        EndIf
        
        If mouseOnPopUp Then cp\scrolly = cp\scrolly - MouseMoveZ() * 13
        cp\scrolly = Max(13, Min(cp\scrolly, (cp\listSize + 1 ) * 13 + 20 - cp\height))
        
        DrawToImage cp\img
            ClsColor cbWhite
            Cls
            ClsColor gClsR, gClsG, gClsB
            
            i = 0
            For is.tScreenList = Each tScreenList
                i + 1
                
                Color cbBlack
                If is\isSupported = False Then Color 200, 100, 100
                
                If mouseOnPopUp Then
                    If mouseOn(cp\x, 20 + cp\y + 13 * i- cp\scrolly, cp\x + cp\width - 20, 20 + cp\y + 13 * (i + 1) - cp\scrolly) Then
                        If MouseHit(1) Then gCurrentScreenMode = ConvertToInteger(is)
                        Color 200, 200, 200
                    EndIf
                EndIf

                Text 5, ((13 * i - cp\scrolly) * YFLIP), is\width + "x" + is\height + ", " + is\bit
            Next is
            cp\listSize = i
        DrawToScreen
        
        DrawImage cp\img, cp\x, cp\y + 20
        Color cbWhite
        Box cp\x + cp\width - 20, cp\y, 20, cp\height
        
        Color cbBlack
        Box cp\x, cp\y + 20, cp\width - 20, cp\height - 20, 0
        Box cp\x + cp\width - 20, cp\y, 20, cp\height, 0

    EndIf

    Color cbBlack
    cs.tScreenList = ConvertToType(gCurrentScreenMode)
    Text cp\x + 5, cp\y + 5, cs\width + "x" + cs\height + ", " + cs\bit

    Box cp\x, cp\y, cp\width - 20, 20, 0
EndFunction

Function CreatePopUpList(x, y, width, height)
    np.tPopUpList = New (tPopUpList)
    np\x = x
    np\y = y
    np\width = width
    np\height = height
    np\isPopped = False
    
    np\img = MakeImage(width - 20, height - 20)
    DrawToImage np\img
        ClsColor cbWhite
        Cls
    DrawToScreen

    Return ConvertToInteger(np)
EndFunction

Function NextSmooth2dMode(modeSmooth2D, btnSmooth2d)

    If SMOOTH2D_FORCE = 1 Then
        modeSmooth2D = 0
    ElseIf SMOOTH2D_FORCE = 2 Then
        modeSmooth2D = 1
    Else
        modeSmooth2d = Not modeSmooth2D
    EndIf

    If modeSmooth2d Then
        SetButtonText(btnSmooth2D, "Smooth2D: ON")
    Else
        SetButtonText(btnSmooth2D, "Smooth2D: OFF")
    EndIf
    
    gSmooth2D = modeSmooth2d
    Return modeSmooth2D
EndFunction

Function NextWndMode(wndMode, btnWndMode)
    checkSum = WINDOW_MODE_AVAILABLE + FULLSCREEN_MODE_AVAILABLE + RESIZABLE_MODE_AVAILABLE 
    If checkSum < 1 Or checkSum > 3 Then MakeError "No Window mode is available! D:" + Chr(13) + Chr(10) + "(developer: check that all MODE_AVAILABLE-constants are either 0 o"+"r 1," + Chr(13) + Chr(10) + "and that not all are 0)" 
    
    wndMode + 1
    If wndMode > 2 Then wndMode = 0
    
    If wndMode = 0 Then
                  
        cs.tScreenList = ConvertToType(gCurrentScreenMode)
            
        If FULLSCREEN_MODE_AVAILABLE And cs\isSupported Then
            SetButtonText(btnWndMode, "Fullscreen")
        Else
            wndMode = NextWndMode(wndMode, btnWndMode)
        EndIf
    ElseIf wndMode = 1 Then
        If WINDOW_MODE_AVAILABLE Then
            SetButtonText(btnWndMode, "Windowed")
        Else
            wndMode = NextWndMode(wndMode, btnWndMode)
        EndIf
    ElseIf wndMode = 2 Then
        If RESIZABLE_MODE_AVAILABLE Then
            SetButtonText(btnWndMode, "Resizable")
        Else
            wndMode = NextWndMode(wndMode, btnWndMode)
        EndIf    
    EndIf
    
    gWndMode = wndMode
    Return wndMode
EndFunction

Function CheckCompatibility()
    For is.tScreenList = Each tScreenList
        If GFXModeExists(is\width, is\height, is\bit) = False Then
            If SHOW_UNAVAILABLE_MODES = True And (WINDOW_MODE_AVAILABLE = True Or RESIZABLE_MODE_AVAILABLE = True) Then
                is\isSupported = False
            Else
                Delete is
            EndIf
        EndIf
    Next is
EndFunction

Function AddCandidate(width, height)
    ns.tScreenList = New(tScreenList)
    ns\width = width
    ns\height = height
    ns\bit = 16
    ns\isSupported = True
    
    ns.tScreenList = New(tScreenList)
    ns\width = width
    ns\height = height
    ns\bit = 32
    ns\isSupported = True
EndFunction

Function StartScreen()
    startx = 400
    endx = SCREEN_WIDTH
    
    width = Abs(startx - endx)
    dirx = (startx < endx) - (endx < startx)
    
    starty = 300
    endy = SCREEN_HEIGHT
    
    height = Abs(starty - endy)
    diry = (starty < endy) - (endy < starty)

    x = 0
    y = 0
    roundTimer = Timer()
    While True

        loopTime = Timer() - roundTimer
        roundTimer = Timer()
        
        x + loopTime
        y + loopTime
        
        startx + dirx
        starty + diry
        
        If x < width Then startx = startx + loopTime * dirx
        If y < height Then starty = starty + loopTime * diry
        
        SCREEN startx, starty

        If x >= width And y >= height Then Exit
    Wend
    
    SCREEN SCREEN_WIDTH, SCREEN_HEIGHT
    gHeadFont = LoadFont("Garamond", 30, True)
    gNameFont = LoadFont("Garamond", 20, True)
    gNormFont = LoadFont("Arial", 12)
   // MakeError speedx + " " + speedy + " " + dirx + " " + diry
    i = 0
    roundTimer = Timer()
    While True

        ClsColor i, i, i
        Cls

        DrawBackground()

        loopTime = Timer() - roundTimer
        roundTimer = Timer()
        i = i + loopTime
        If i >= 230 Then Exit
        DrawScreen
    Wend

    SetFont gNormFont
EndFunction


Function DrawBackground()
    txt1$ = "CB Game Launcher"
    txt2$ = "By Huuhkis"
    SetFont gHeadFont
    drawX = ScreenWidth() / 2 - TextWidth(txt1) / 2
    drawY = ScreenHeight() * (1 / 3.0)
    
    Color 150, 150, 150
    Text drawX, drawY, txt1
    
    SetFont gNameFont
    Text drawX, drawY + 30, txt2
    
    SetFont gNormFont
    Color cbBlack
EndFunction

Function SetButtonText(button, txt As String)
    cb.tButtons = ConvertToType(button)
    cb\txt = txt
EndFunction

Function CreateButton(x, y, width, height, txt As String, isPushable = 1)
    nb.tButtons = New(tButtons)
    nb\x = x
    nb\y = y
    nb\width = width
    nb\height = height
    nb\txt = txt
    nb\isPushable = isPushable
    
    Return ConvertToInteger(nb)
EndFunction

Function DrawButton(id)
    cb.tButtons = ConvertToType(id)

    Color 200, 200, 200
    If mouseOn(cb\x, cb\y, cb\x + cb\width, cb\y + cb\height) Then
        
        If cb\state = 1 And cb\isPushable Then
            Color 180, 180, 180
        ElseIf cb\state = 0 Or cb\isPushable = False
           Color 210, 210, 210
        EndIf
        
        If MouseHit(1) Then
            cb\state = 1
        ElseIf MouseUp(1) Then
            If cb\state = 1 Then ret = 1
        EndIf
    EndIf
    
    If MouseUp(1) Then cb\state = 0
    
    Box cb\x, cb\y, cb\width, cb\height
    Color cbBlack
    Box cb\x, cb\y, cb\width, cb\height, 0
    
    Text cb\x + cb\width / 2 - TextWidth(cb\txt) / 2, cb\y + cb\height / 2 - TextHeight("I") / 2, cb\txt
    
    Return ret
    
End Function

Function mouseOn(x, y, x2, y2)
    If MouseX() > x And MouseX() < x2 Then
        If MouseY() > y And MouseY() < y2 Then
            Return True
        EndIf
    EndIf
EndFunction

REMSTART
/*
    Tästä alaspäin itse peli vaatimat funktiot, sekä yksi taulukko.
    
    - GetScreenMode()
        width, height, bit, windowmode, Smooth2D
        Kutsu tätä aivan pelisi alussa. Se hakee tiedot siitä millä näyttöasetuksilla peliä pelataan

    - SCREEN2()
        Sama kuin SCREEN, mutta ei vaadi parametreja. Asettaa näyttötilan (ja Smooth2D:n) startterin määrittelemiksi
*/
REMEND
REMSTART

Dim dCurrentScreenMode(4)
//Tätä kutsutaan ensimmäisenä. Hakee startterin määrittelemät tiedot, jos mahdollista
Function GetScreenMode(defWidth = 800, defHeight = 600, defBit = 32, defMode = 1, defSmooth = 0)
    gfxFile$ = "launchergfx.dat" 
    
    //Luetaan datatiedostosta mitä modea pitää käyttää
    If FileExists(gfxFile) And (Not IsDirectory(gfxFile)) Then
        f = OpenToRead(gfxFile)
            dCurrentScreenMode(0) = ReadInt(f)
            dCurrentScreenMode(1) = ReadInt(f)
            dCurrentScreenMode(2) = ReadInt(f)
            dCurrentScreenMode(3) = ReadInt(f)
            dCurrentScreenMode(4) = ReadInt(f)
        CloseFile f
    Else
        //Jos datatiedostoa ei ole, yritetään käyttää defaulttia. Jos se ei ole tuettu, poistutaan ja käynnistetään startteriohjelma
        If Not GFXModeExists(defWidth, defHeight, defBit) Then
            Execute "launcher.exe"
            MakeError "Screen mode n"+"ot supported" + (newline(2) + defWidth + "x" + defHeight + ", " + defBit + "bit" + newline(2) + "Executin Launcher.exe, so you can choose a proper mode.")
        EndIf
    
        dCurrentScreenMode(0) = defWidth
        dCurrentScreenMode(1) = defHeight
        dCurrentScreenMode(2) = defBit
        dCurrentScreenMode(3) = defMode
        dCurrentScreenMode(4) = defSmooth
        
        f = OpenToWrite(gfxFile)
            WriteInt f, dCurrentScreenMode(0)
            WriteInt f, dCurrentScreenMode(1)
            WriteInt f, dCurrentScreenMode(2)
            WriteInt f, dCurrentScreenMode(3)
            WriteInt f, dCurrentScreenMode(4)
        CloseFile f
    EndIf
EndFunction

//Asettaa näyttötilan startterin määrittelemäksi
Function SCREEN2()
    SCREEN dCurrentScreenMode(0), dCurrentScreenMode(1), dCurrentScreenMode(2), dCurrentScreenMode(3)
    If dCurrentScreenMode(4) = True Then Smooth2D ON
EndFunction

//Lue rivinvaihdon MakeErrorin tekstiin.
Function newline(lineamount As String)
    For i = 1 To lineamount
        ret$ = ret + Chr(13) + Chr(10) 
    Next i
    
    Return ret
EndFunction

REMEND
Itse peliin laitettavat funkkarit, joilla launcherin tallentamat tiedot saadaan hyötykäyttöön. Nämä löytyvät myös launcherin koodista.

Code: Select all

Dim dCurrentScreenMode(4)
//Tätä kutsutaan ensimmäisenä. Hakee launcherin määrittelemät tiedot, jos mahdollista
Function GetScreenMode(defWidth = 800, defHeight = 600, defBit = 32, defMode = 1, defSmooth = 0)
    gfxFile$ = "launchergfx.dat" 
    
    //Luetaan datatiedostosta mitä modea pitää käyttää
    If FileExists(gfxFile) And (Not IsDirectory(gfxFile)) Then
        f = OpenToRead(gfxFile)
            dCurrentScreenMode(0) = ReadInt(f)
            dCurrentScreenMode(1) = ReadInt(f)
            dCurrentScreenMode(2) = ReadInt(f)
            dCurrentScreenMode(3) = ReadInt(f)
            dCurrentScreenMode(4) = ReadInt(f)
        CloseFile f
    Else
        //Jos datatiedostoa ei ole, yritetään käyttää defaulttia. Jos se ei ole tuettu, poistutaan ja käynnistetään startteriohjelma
        If Not GFXModeExists(defWidth, defHeight, defBit) Then
            Execute "launcher.exe"
            MakeError "Screen mode n"+"ot supported" + (newline(2) + defWidth + "x" + defHeight + ", " + defBit + "bit" + newline(2) + "Executin Launcher.exe, so you can choose a proper mode.")
        EndIf
    
        dCurrentScreenMode(0) = defWidth
        dCurrentScreenMode(1) = defHeight
        dCurrentScreenMode(2) = defBit
        dCurrentScreenMode(3) = defMode
        dCurrentScreenMode(4) = defSmooth
        
        f = OpenToWrite(gfxFile)
            WriteInt f, dCurrentScreenMode(0)
            WriteInt f, dCurrentScreenMode(1)
            WriteInt f, dCurrentScreenMode(2)
            WriteInt f, dCurrentScreenMode(3)
            WriteInt f, dCurrentScreenMode(4)
        CloseFile f
    EndIf
EndFunction

//Asettaa näyttötilan startterin määrittelemäksi
Function SCREEN2()
    SCREEN dCurrentScreenMode(0), dCurrentScreenMode(1), dCurrentScreenMode(2), dCurrentScreenMode(3)
    If dCurrentScreenMode(4) = True Then Smooth2D ON
EndFunction

//Lue rivinvaihdon MakeErrorin tekstiin.
Function newline(lineamount As String)
    For i = 1 To lineamount
        ret$ = ret + Chr(13) + Chr(10) 
    Next i
    
    Return ret
EndFunction
Last edited by Jonez on Fri Mar 02, 2012 6:23 pm, edited 1 time in total.
-Vuoden 2008 aloittelijan ystävä -palkinnon voittaja-
Image <- protestipelikilpailun voittaja.
Space War
User avatar
axu
Devoted Member
Posts: 854
Joined: Tue Sep 18, 2007 6:50 pm

Re: CB Game Starter

Post by axu »

Tunnetaan myös nimellä launcher.
En nyt päässyt ajamaan koodia, mutta vaikuttaa toimivalta systeemiltä. Näyttötiloja tosin voisi olla enemmän, tuosta ei löytynyt ainakaan minun näytön natiivia 1680*1050. Niitähän varmaan voi lisätä helposti tuolla AddCandidate-funktiolla.
Jos tämä viesti on kirjoitettu alle 5 min. sitten, päivitä sivu. Se on saattanut jo muuttua :roll:
Image
MaGetzUb
Guru
Posts: 1715
Joined: Sun Sep 09, 2007 12:35 pm
Location: Alavus

Re: CB Game Starter

Post by MaGetzUb »

1920x1080 resoluutio puuttuu, se on aika yleinen 1920x1200 resoluution ohella. :) Btw, on muuten kätevä systeemi. :)
Solar Eclipse
Meneillä olevat Projektit:
We're in a simulation, and God is trying to debug us.
User avatar
Jonez
Devoted Member
Posts: 575
Joined: Mon Aug 27, 2007 8:37 pm

Re: CB Game Starter

Post by Jonez »

Joo, heitin vain jotain random resoluutioita sinne. Idea onkin, että itse pelintekijä laittaa sinne ne resoluutiot jotka haluaa loppukäyttäjällä olevan tarjonnassa. Olisinhan voinut sinne enemmänkin vaihtoehtoja laittaa, mutta olen laiska :). Lisään kuitenkin mainitsemanne resot.

Launcherhan sen nimi tosiaan on. Ei vain tullut mieleen koodaillessa. :)
-Vuoden 2008 aloittelijan ystävä -palkinnon voittaja-
Image <- protestipelikilpailun voittaja.
Space War
Post Reply