Topic post, bàn luận, đóng góp ý kiến, thắc mắc, giải đáp, bình phẩm về Custom Spell.

Thảo luận trong 'World Editor' bắt đầu bởi raivor, 7/1/11.

  1. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
     

    Các file đính kèm:

    Chỉnh sửa cuối: 3/3/11
  2. maixuanem

    maixuanem Thành viên cấp 4

    Tham gia ngày:
    22/8/10
    Bài viết:
    182
    Nơi ở:
    Tân Binh
    Ủng hộ topic... phong trào mapmaker cần có những hoạt động sôi nổi như thế này. Sẽ sớm pót spell khi làm xong :D
    ps: spell = vjass có đc hem?
    //Codart đang dùng tạm nick này :D
     
  3. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    vjass thì phải giải thích + với hình ảnh về tác dụng của từng phần trong vjass khi vào game.
     
  4. Nhoc357

    Nhoc357 Thành viên cấp 5

    Tham gia ngày:
    7/2/10
    Bài viết:
    235
    Ủng hộ chủ topic, anh em cùng đóng góp một số custom spell để newbie học hỏi, vd như em :) :)
     
  5. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Tên Spell: ZZBlink
    Mô tả: Hero di chuyển Zic-zac đến điểm được cast spell.
    [spoil][​IMG]
    [​IMG][/spoil]
    Các biến:
    - <Tên biến [có Array(kiểu mảng)]>:<Loại biến>
    - Caster[Array]: Unit
    - Group: Unit Group
    - Point[Array], Point2[Array], Point3[Array], Target[Array]: Point
    - Bien[Array], MUI, TempValue: Integer
    - Lightning[Array]: Lightning


    I. Phần chính
    Trigger 1:
    Mã:
    Event
          Unit - A unit Starts the effect of an ability
    Conditions
          (Ability being cast) Equal to Blink
    Actions
          Set MUI = (MUI + 1) //có nghĩa là ban đầu MUI = 0, sau 1 lần cast thì MUI = 0 + 1, khi đó MUI = 1, tiếp tục lần thứ 2 MUI = (MUI = 1) + 1 thì MUI = 2, rồi lần thứ 3 MUI = (MUI = 2(1 + 1 = 2)) + 1, lần thứ 4 MUI = (MUI = 3(2+1 = 3)) + 1, rồi lần thứ 5 MUI = 4 + 1 = 5, rồi lần thứ 6 MUI = 5 + 1 = 6....................
          If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                      MUI Equal to 500 //khi MUI >= 500
                Then - Actions
                      Set MUI = 0 //thì Set MUI = 0 và quá trình trên kia lại tiếp diễn
                Else - Actions
          Set Caster[MUI] = (Triggering unit) //Biến Caster là hero cast spell (là tênmàumè đấy)
          Set Point[MUI] = (Position of Caster[MUI]) //Vị trí của tênmàumè
          Set Target[MUI] = (Target point of ability being cast) //Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới.
          Set Bien[MUI] = 1 //Một biến được có tên là Biến và = 1, tuy vậy nhưng nó sắp làm nên kì tích (phóng đại lên thôi :P)
          Unit - Create 1 Dummy for Owner of Caster[MUI]) at Point[MUI] facing Default building facing degrees //như đã nóiở trên dòng này để: "//tạo 1 Dummy cho Caster tại vị trí của hắn để MUI thôi."
          Unit Group - Add (Last created unit) to Group //Cho dummy vào nhóm "tự kỉ vì MUI"
          Unit - Set the custom value of (Last created unit) to MUI //đặt giá trị "tự biên" bằng MUI, bằng cách này ta có thể giữ MUI lại mà không bị các MUI khác chen lấn làm có bạn chết có bạn bị thương => Spell bug, không MUI được.
          Trigger - Turn on Untitled Trigger 002 <gen> //Bật trigger 2
    Trigger 2:
    Mã:
    Event
          Time - Every 0.05 seconds of game time //Mỗi 0.05s của game
    Conditions
    Actions
          Unit Group - Pick every unit in Group and do (Actions) //tóm bọn tự kỉ trong nhóm "tự kỉ vì MUI".
                Loop - Actions
                      Set TempValue = [(Custom value of (Picked unit))] //Bác Tom bảo dùng cái này để thay cho cái dòng "[B][(Custom value of (Picked unit))][/B][COLOR="White"] <<< cái gì đây??? Như ở trên, nó là giá trị tự biên được đặt = MUI, tại sao lại là Picked unit (tên bị tóm đây mà), như ở trên ta đã add "Last create unit" vào nhóm "tự kỉ vì MUI" nên khi tóm unit trong nhóm "tự kỉ vì MUI" thì dính hắn. Và ở trên có dòng "Unit - Set the [B][U]custon value of (Last created unit)[/U][/B] to MUI" đấy đấy, nó đấy. Vậy thì tóm lại, Custom value of (Picked unit) = MUI tức là cast lần đầu tiên thì Custom value of (Picked unit) = 1, lần thứ 2 thì Custom value of (Picked unit) = 2.... Vậy tại sao không dùng MUI luôn cho nó lành? Thực ra là như vầy, nếu ta dùng MUI thì trong trigger 2 MUI sẽ bị thay đổi giá trị nhanh chóng, vì ở Trigger 2 hệ thống check liên tục mỗi 0.04s, cụ thể là ở đây "Time - Every 0.04 second of game time"<<< nếu vậy thì khi cast lần thứ 2 MUI = 2 rồi :|, nhưng nếu dùng custom value of khỉgìđấy thì cast lần 2 sẽ xuất hiện 2 giá trị "tự biên" là Custom value of (Picked unit) [COLOR="White"]thứ nhất[/COLOR] = 1 và Custom value of (Picked unit) [COLOR="White"]thứ 2[/COLOR] = 2 => cả 2 cái cùng hoạt động. Vậy tại sao MUI lại không giữ được mà Custom value of khỉgìđấy lại giữ được? À, là bởi vì người ta quy định vậy đấy, biết vậy thôi, khỏi thắc mắc. Quay lại đoạn code, Move = Move + 3 (vậy thôi?)"[/COLOR] Cám ơn bác rất nhiều, đúng là nhanh hơn thật ;))
                      Set Point[TempValue] = (Position of Caster[TempValue]) //biến Point = vị trí của tênmàumè, ta set nó ở trigger 2 để kiểm tra vị trí của hắn mỗi 0.05s vì hắn màu mè mà, chạy lung tung cả lên.
                      Set Bien[TempValue] = (Bien[TempValue] x -1) //chà, cái này khó à nha. Ta có: Biến = 1 (Giả thiết). Thay Biến = 1 vào biểu thức Biến = Biến x (-1) ta được: Biến = 1 x (-1) (sai) nhưng.... làm tiếp. Do đó Biến = -1. Lại thay Biến = -1 tiếp, ta lại được Biến = -1 x (-1) (lại sai) nhưng .... lại làm tiếp. => WE bị sai. Vậy Biến = 1 và sau 0.05s sẽ thành -1 rồi ngược lại vì 1 x (-1) = -1 rồi -1 x (-1) = 1 rồi 1 x (-1) = -1 rồi .... rất đơn giản nhưng nó sẽ tạo ra kì văn tích (phóng đại thôi) 
                      Set Point3[TempValue] = Target[TempValue] //Biến Point3 = "Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới." set tiếp để check tiếp.
                      Set Point2[TempValue] = (Point[TempValue] offset by 200 towards ((Angle from Point[TempValue] to Point3[TempValue]) + (45 x (Real(Bien[TempValue]))) degrees //Biến Point2 = điểm cách vị trí của tênmàumè 200km ở góc từ vị trí của tênmàumè đến "Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới." + 45 độ x với biến. Ở đây, do Biến luôn thay đổi từ - thành + và ngược lại nên 45 độ cũng thay đổi từ - thành + sau mỗi 0.05s. Mình sẽ cho 1 VD cụ thể về đoạn code này. Ta có : góc từ vị trí của tênmàumè đến "Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới." = 60 độ tức hướng 2h (I like it). => ta có cái hình :
    [​IMG]
    Thầy dạy toán bảo phải chấm mấy cái điểm đậm lên để có gì còn ăn gian được, bời vậy đừng thắc ắc tại sao nó như vậy nhé. Đó là trên lí thuyết của đoạn code. Đây là sơ đồ thực tế (ôi kì tích \m/):
    [​IMG]
    Mã:
                      Unit - Move Caster[TempValue] instantly to Point2[TempValue], facing Point3[TempValue] //cái này là lí do làm hắn chạy lung tung này.                       
                      If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            If - Conditions
                                  (Distance between Point[TempValue] and Point3[TempValue]) Less than or equal to 200.00 //Nếu khoảng cách từ tênmàumè đến "Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới." nhỏ hơn hoặc bằng 200.00 thì..... xuống dưới.
                            Then - Actions
                                  Unit - Move Caster[TempValue] instantly to Point3[TempValue], facing Point3[TempValue] //Ném hắn vào đúng chỗ của hắn ("Điểm mà tênmàumè di-chuyển-một-cách-màu-mè tới.") để không bị lệch.
                                  Unit Group - Remove (Picked unit) from Group //xóa tên hắn trong sổ điểm *gạch gạch*.
                                  Unit - Remove (Picked unit) from the game //đuổi tên tự kỉ đi vì hắn hết bệnh rồi.
                            Else - Actions
                      Custom script: call RemoveLocation(udg_Point[udg_TempValue])
                      Custom script: call RemoveLocation(udg_Point2[udg_TempValue])
                      Custom script: call RemoveLocation(udg_Point3[udg_TempValue])
                      Custom script: call RemoveLocation(udg_Target[udg_TempValue])
          If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                      (Number unit in Group) Equal to 0 //như trên, có gì xem lại #1 nhé ....//Khi cái nhóm "tự kỉ vì MUI" không còn đứa nào vì tụi nó đã hết bệnh.(Ôi! thời đại của bọn tự kỉ đã hết)
                Then - Actions
                      Trigger - Turn off (This trigger) //kết thúc cái sự màu mè. RemoveLeak không thì chúng ta sẽ được rèn luyện sự kiên nhẫn mỗi khi tênmàumè giở chứng.
                Else - Actions                          
    Xong phần trên ta vào game test thử xem thế nào.
    II. Thêm các Special Effect (Hiệu ứng đặc biệt).
    Trigger 1
    Mã:
    Event
          Unit - A unit Starts the effect of an ability
    Conditions
          (Ability being cast) Equal to Blink
    Actions
          Set MUI = (MUI + 1)
          If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                      MUI Equal to 500
                Then - Actions
                      Set MUI = 0
                Else - Actions
          Set Caster[MUI] = (Triggering unit)
          Set Point[MUI] = (Position of Caster[MUI])
          Set Target[MUI] = (Target point of ability being cast)
          Set Bien[MUI] = 1
          Unit - Order Caster[MUI] to stop
          Unit - Create 1 Dummy for Owner of Caster[MUI]) at Point[MUI] facing Default building facing degrees
          Unit - Set the custom value of (Last created unit) to Group
          [B]Lightning - Creat a Lightning Attack lightning effect from source Point[MUI] to target Point[MUI] //tạo Lightning Attack (cái đường sét như chain lightning ấy) từ vị trí của tênmàumè tới vị trí của tênmàumè (các bạn biết mình rãnh cỡ nào rồi đấy)
          Set Lightning[MUI] = (Last created lightning effect) //đặt biến Lightning = cái Lightning vừa tạo để mình vác sang trigger 2[/B]
          Trigger - Turn on Untitled Trigger 002 <gen>
    Trigger 2
    Mã:
    Event
          Time - Every 0.05 seconds of game time
    Conditions
    Actions
          Unit Group - Pick every unit in Group and do (Actions)
                Loop - Actions
                      Set TempValue = [(Custom value of (Picked unit))]
                      Set Point[TempValue] = (Position of Caster[TempValue])
                      Set Bien[TempValue] = (Bien[TempValue] x -1)
                      Set Point3[TempValue] = Target[TempValue]
                      Set Point2[TempValue] = (Point[TempValue] offset by 200 towards ((Angle from Point[TempValue] to Point3[TempValue]) + (45 x (Real(Bien[TempValue]))) degrees
                      Unit - Move Caster[TempValue] instantly to Point2[TempValue], facing Point3[TempValue]
                      [B]Special Effect - Create a special effect at Point[TempValue] using Abilities\Spells\Orc\MirrorImage\MirrorImageCaster.mdl //Ya, đây là cái sự màu mè của spell, tạo 1 effect ở vị trí của tênmàumè.
                      Special Effect - Destroy (Last created special effect) //Hủy effect
                      Lightning - Move Lightning[TempValue] to source Point[TempValue] and targer Point2[TempValue] //di chuyển cái lightning effect sang những vị trí khác, cái này mình cũng không hiễu rõ nữa. À là vầy, cái lightning effect này nè sẽ được chuyển từ cái chỗ mà aicũngbiếtrồiđấy để tới cái từ Point tới Point2 (vì Point2 được set trước khi Point được thay đổi nên sẽ tạo được 1 đường lightning (vì vị trí cách vị trí của tênmàumè 200.00km với 1 góc... được set trước khi vị trí của tênmàumè nên sẽ tạo được 1 đường lightning. (Đẹp, chẹp chẹp) 
                      Special Effect - Create a special effect at Point[TempValue] using Abilities\Weapons\Bolt\BoltImpact.mdl //Effect
                      Special Effect - Destroy (Last created special effect) //Hủy effect       
                      Special Effect - Create a special effect at Point2[TempValue] using Abilities\Spells\Orc\LightningBolt\LightningBoltMissile.mdl //Effect
                      Special Effect - Destroy (Last created special effect) //Hủy effect[/B]     
                      If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            If - Conditions
                                  (Distance between Point[TempValue] and Point 3[TempValue) Less than or equal to 200.00
                            Then - Actions
                                  [B]Lightning - Detroy Lightning[TempValue] //Hủy Lighning đi, không thì còn 1 đường sét đấy, trông vui lắm, nhưng đừng có dại mà để lại (như kiểu di chứng sau bệnh tật vậy đó, rất nguy hiểm)[/B]
                                  Unit - Move Caster[TempValue] instantly to Point 3[TempValue], facing Point3[TempValue]
                                  Unit - Remove (Picked unit) from the game
                                  Unit Group - Remove (Picked unit) from Group
                                  Custom script: call RemoveLocation(udg_Point[udg_TempValue])
                                  Custom script: call RemoveLocation(udg_Point2[udg_TempValue])
                                  Custom script: call RemoveLocation(udg_Point3[udg_TempValue])
                                  Custom script: call RemoveLocation(udg_Target[udg_TempValue])
                            Else - Actions
          If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                      (Number unit in Group) Equal to 0
                Then - Actions
                      Trigger - Turn off (This trigger)
                Else - Actions                          
    Hết, chúc thành công, mong các bạn post spell (Đep đẹp lên để mọi người cùng học hỏi, và cũng để (mở rộng tầm mắt, cũng như thể hiện được sự sáng tạo của người VN qua Game (Cụ thể là W3), cũng như cho thấy game là 1 phương tiện để phát triển tư duy (thiểu số thôi)) kho tàng spells VN "chất đống". Xin chân thành cảm ơn gia đình nhà trường và forum để em được như ngày hôm nay em rất tự hào về em)
     

    Các file đính kèm:

    Chỉnh sửa cuối: 11/1/11
  6. Tom_Kazansky

    Tom_Kazansky Thành viên BQT Moderator

    Tham gia ngày:
    28/12/06
    Bài viết:
    3,454
    Nơi ở:
    Hà Nội
    Nên cho (Custom value of (Picked unit)) vào một biến, như vậy sẽ:
    - gọn hơn
    - chạy nhanh hơn (vì mỗi lần (Custom value of (Picked unit)) là một lần gọi hàm, gọi nhiều lần rõ ràng chậm hơn gọi một lần)
    - và lúc code thì chọn một biến chắc chắn nhanh hơn chọn custom value... =))

    ngoài ra các point: Point, Target chả thấy Remove ở đâu cả -> leak

    Mã:
    Event
          Time - Every 0.05 seconds of game time
    Conditions
    Actions
          Unit Group - Pick every unit in Group and do (Actions)
                Loop - Actions
                      Set TempValue = (Custom value of (Picked unit))
                      Set Point[TempValue] = (Position of Caster[TempValue])
                      Set Bien[TempValue] = (Bien[TempValue] x -1)
                      Set Point3[TempValue] = Target[TempValue]
                      Set Point2[TempValue] = (Point[TempValue] offset by 200 towards ((Angle from Point[TempValue] to Point3[TempValue]) + (45 x (Real(Bien[TempValue]))) degrees
                      Unit - Move Caster[TempValue] instantly to Point2[TempValue], facing Point3[TempValue]                       
                      If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            If - Conditions
                                  (Distance between Point[TempValue] and Point 3[TempValue]) Less than or equal to 200.00
                            Then - Actions
                                  Unit - Move Caster[TempValue] instantly to Point 3[TempValue], facing Point3[TempValue]
                                  Unit - Remove (Picked unit) from the game
                                  Unit Group - Remove (Picked unit) from Group
                                  Custom Script: call RemoveLocation( Target[TempValue] )
                            Else - Actions
                      Custom Script: call RemoveLocation( Point[TempValue] )
                      Custom Script: call RemoveLocation( Point2[TempValue] )
          If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                      (Number unit in Group) Equal to 0
                Then - Actions
                      Trigger - Turn off (This trigger)
                Else - Actions                          
    
    p.s: cho vào quote làm gì :-/
     
    Chỉnh sửa cuối: 8/1/11
  7. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Chia làm 2 phần đấy bác, tại máy bị trục trặc chưa giải thích xong thì bấm nhầm nút gửi @@, lại còn bị crash thế là đi tong cái đống giải thích nãy giờ viết mỏi tay, em sẽ bổ sung sau. Nản quá. Bây giờ thì bận rồi, sẽ sớm edit. Mong các bác thông cảm.
     
    Chỉnh sửa cuối: 8/1/11
  8. maixuanem

    maixuanem Thành viên cấp 4

    Tham gia ngày:
    22/8/10
    Bài viết:
    182
    Nơi ở:
    Tân Binh
    [​IMG]
    Mã:
    scope FireMassing initializer init
    //>>>>>>>>>>>>>>>>>>>>>>>>>SETUP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
    //Đây là phần cài đặt cho spell:
    globals
        private constant integer SPELL_ID = 'A001'//ID của dummy ability mà bạn dùng, phải là 1 abi target ground hoặc target unit
        private constant integer DUMMY_UNIT_ID = 'e000'//cái này hình như thừa... cứ thử bỏ đi xem. Tại spell làm lâu rồi cũng chẳng nhớ LOL
        private constant integer EFF_UNIT_ID = 'e002'//Unit đóng vai trò missle
        private constant string EFF = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"//Hiệu ứng đặc biệt khi tên chạm enemy hoặc đã bay hết quãng đg cho phép
                                    //"Abilities\\Weapons\\Rifle\\RifleImpact.mdl"
                                    //"Abilities\\Spells\\Human\\SpellSteal\\SpellStealMissile.mdl"
                                    //"Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
                                    //"Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
        
    endglobals
    //Advance:
    private function speed takes nothing returns real
        return 0.01 //lager value, more slower (tốc độ của mũi tên, vì là time out của timer nên càng nhỏ thì mũi tên bay càng nhanh)
    endfunction
    
    private function range takes nothing returns real
        return 1500.00 //quãng đường tối đa ma mũi tên có thể bay
    endfunction
    
    private function radius takes nothing returns real
        return 100.00 //the radius for detecting collision (Bán kính phát hiện va chạm AOE/2 đó :)) )
    endfunction
    
    private function amount takes nothing returns integer
        return 10 //the numbers of arrow per cast (số lượng mũi tên)
    endfunction
    
    private function dam takes integer aLevel returns real
        return 100.00 * aLevel//dam per arrow (hàm tính dam dựa theo lvl skill)
    endfunction
    
    private function addOn takes unit trigUnit, unit dam2Unit returns nothing
        //this option is for advance user, you can add your code here to Make
        //more effect when the projectile impact to enemy. ex: knock back, dam over time... 
    
       // hàm này được gọi khi mũi tên va chạm với enemy, hàm này hiện để trống, muốn thêm cái gì vào đây thì thêm
       //à quên trigUnit là caster, dam2Unit là unit bị bắn trúng
    endfunction
    
    //>>>>>>>>>>>>>>>>>>>>>>>ENDSETUP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
    
    //=================Confuse zone==============//
    //Dont edit this bunk of code unless you
    //really wanna do
    //Phần dưới này để thư thư rồi giải thích tiếp nhé :D
    //===========================================//
    globals
        private constant real MOVE = 10.0 //distance that the projectile will move Per
                                          //*speed()* . You should'nt change this
        private constant real deviation = 10.0 //deviation between projectile
    endglobals
    
    
    
    
    private struct Projectile
        unit u//projectile
        unit uT// trigger unit
        real angle
        real moved = 0
        
        boolean stop = false
        
        method control takes nothing returns nothing
            local location moveLoc = PolarProjectionBJ(GetUnitLoc(.u), MOVE, .angle)
            //valriable for collision detect:
            local group cGrp = GetUnitsInRangeOfLocAll(radius(),moveLoc)
            local unit uDam
            
            //move the unit(projectile)
            call SetUnitPositionLoc(.u,moveLoc)
            
            //collision detect and dam enemy:
            set uDam = FirstOfGroup(cGrp)
            if uDam != null then
            
                loop
                    exitwhen uDam == null
                    if IsUnitEnemy(uDam,GetOwningPlayer(.uT)) and IsUnitAliveBJ(uDam) then 
                        
                        call UnitDamageTargetBJ(.uT, uDam, dam(GetUnitAbilityLevel(.uT,SPELL_ID)),ATTACK_TYPE_MAGIC,DAMAGE_TYPE_MAGIC)
                        call addOn(.uT,uDam)
                        set .stop = true//need improve
                    endif
                    
                    call GroupRemoveUnit(cGrp,uDam)
                    
                    set uDam = FirstOfGroup(cGrp)
                endloop
                
                
            endif
            
            
            //out of range detect:
            if .moved < range() then
                set .moved = .moved + MOVE//if still in range, then set the .moved value go up
            else 
                set .stop = true
            endif
            
            //play animation
            if .stop then
                call DestroyEffect(AddSpecialEffectLoc(EFF,moveLoc))
            endif
            
            //remove leak
            call RemoveLocation(moveLoc)
            call DestroyGroup(cGrp)
            set moveLoc = null
            set cGrp = null
            set uDam = null
        endmethod
        
        
    endstruct
    
    globals
        private integer Total = 0
        private timer T = CreateTimer()
        private Projectile array pArr
    endglobals
    
                                      
    
    private function timerLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i >= Total)
         
            if pArr[i].stop then
                call KillUnit(pArr[i].u)//kill the projectile to not display it any more
                call pArr[i].destroy()//Not need this instance any more
                set Total=Total-1//degrease the Total by 1
                if Total > 0 then
                    set pArr[i]=pArr[Total]
                    set i=i-1//if not the loops will forget to handle the pArr[Total] ^^
                else
                    call PauseTimer(T)
                endif
            else
                call pArr[i].control()
            endif
            
        
            set i = i + 1
        endloop
     
        //call BJDebugMsg("yeah")
    endfunction
    
    //extra function for formula caculate:
    private function angleCalculate takes real angle, integer iAddOne returns real
        local real result = 0
        //==========for div number of *amount()*:
        local real halfDev //deviation / 2
        local real cen1
        local real cen2
        local integer cen1Index
        local integer cen2Index
        //==========for odd number of *amount()*:
        local integer cenIndex
        local real cen
        
        //div:
        if ModuloInteger(amount(),2)==0 then
            //call BJDebugMsg("so chan")
            set halfDev = deviation/2
            set cen1 = angle - halfDev
            set cen2 = angle + halfDev
            set cen1Index = amount()/2
            set cen2Index = cen1Index + 1
            
            if iAddOne == cen1Index then
                set result = cen1
            elseif iAddOne < cen1Index then
                set result = cen1 - deviation*(cen1Index - iAddOne)
            elseif iAddOne == cen2Index then
                set result = cen2
            elseif iAddOne > cen2Index then
                set result = cen2 + deviation*(iAddOne - cen2Index)
            endif
            
            
        //odd:    
        else
            //call BJDebugMsg("so le")
            set cen = angle
            set cenIndex = ((amount() - 1)/2) + 1
            
            if iAddOne == cenIndex then
                set result = angle
            elseif iAddOne > cenIndex then
                set result = cen - (deviation*(iAddOne - cenIndex))
            else
                set result = cen + (deviation*(cenIndex - iAddOne))
            endif
        endif
        
        return result
    endfunction
    
    //====================end=====================//
    
    
    //Condition================================================================
    private function condition takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
    
    //Actions==================================================================
    
    
    private function Actions takes nothing returns nothing
        local player pT =  GetOwningPlayer(GetTriggerUnit())
        local location tar = GetSpellTargetLoc()
        local location self = GetUnitLoc(GetTriggerUnit())
        local real deg = AngleBetweenPoints(self,tar)
        local Projectile p
        //
        local integer iPlus = 1
        
    
        
        if Total == 0 then
            call TimerStart (T, speed(), true, function timerLoop)
        endif
       
        //
        loop
        exitwhen iPlus > amount()
            set p = Projectile.create()
            set p.u = CreateUnitAtLoc(pT,EFF_UNIT_ID,self,angleCalculate(deg,iPlus))
            set p.angle = angleCalculate(deg,iPlus)
            set p.uT = GetTriggerUnit()
            
            //call BJDebugMsg(R2S(angleCalculate(deg,iPlus)))
            //call BJDebugMsg(I2S(Total))
            
            set pArr[Total] = p
            set Total = Total + 1
        
            set iPlus = iPlus + 1
        endloop
        
        //remove leak
        set pT = null
        call RemoveLocation(tar)
        call RemoveLocation(self)
        set tar = null
        set self = null
     
    endfunction
    
    //Initializer==============================================================
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddAction( t, function Actions )
        call TriggerAddCondition(t,Condition(function condition))
    endfunction
    
    endscope
    
    demo map: http://dl.dropbox.com/u/14083092/Maper/Hero_FireRanger.w3x

    Spell đơn giản thôi, bắn 1 lượng cung tùy ý ra phía trước. Có thể thêm bất cứ hiệu ứng nào vào unit bị trúng tên (dam over time hay knock back.... tùy) :D
    Có gì giải thích sau nhé h đang bận với cái project
     
    Chỉnh sửa cuối: 8/1/11
  9. Tom_Kazansky

    Tom_Kazansky Thành viên BQT Moderator

    Tham gia ngày:
    28/12/06
    Bài viết:
    3,454
    Nơi ở:
    Hà Nội
    có đọc post #1 ko? code thì phải cho vào thẻ
    Mã:
     chứ? sửa lại đi
    
    và ko giải thích được thì đừng post
    test map còn ko có cơ, muốn 3pts spam ko? =))
     
  10. YAN[asian]

    YAN[asian] Thành viên cấp 5

    Tham gia ngày:
    27/3/07
    Bài viết:
    812
    Một số góp ý cho chiêu blink :
    [+] Good idea
    [+] Good code
    [-] Nhiều hiệu ứng thừa ko cần thiết, theo mình chỉ cần để cái effect phân thân và blink thôi( hoặc giữ cái LightningBoltMissile cũng đc), thêm cái sấm sét vào nghe tiếng nó...ghê ghê thế nào.
    [-] Sao cast spell xong hero lúc nào cũng quay mặt về phía bên phải thế? Mà gắn effect vào point như này có gây leak ko nhỉ?
    Thêm nữa là nên để time là 0.03s thì hợp hơn, 0.05 move hơi chậm :).

    P/s : Mình thích cái topic này \m/!
     
    Chỉnh sửa cuối: 9/1/11
  11. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Vâng, mình là người yêu-cái-đẹp và ồn-ào nên bạn thông cảm.
     
  12. Tom_Kazansky

    Tom_Kazansky Thành viên BQT Moderator

    Tham gia ngày:
    28/12/06
    Bài viết:
    3,454
    Nơi ở:
    Hà Nội
    Nhận xét thêm về spell ZZBlink:

    • Trigger 1:
      sao lại có thể: Unit - Set the custom value of (Last created unit) to Group đặt custom value = unit group? chỗ đó phải là biến MUI chớ nhỉ?
    • Trigger 2:
      • chưa đặt TempValue
      • đoạn remove dummy rồi remove dummy khỏi group, nên remove dummy khỏi group trước khi remove dummy.
      • xóa leak thế này ko ổn
        Set Point[TempValue] = (Position of Caster[TempValue])
        Set Point2[TempValue] = ...
        mỗi lần chạy là một lần tạo point mới, vậy mà có mỗi một lần xóa (lúc trigger này dừng hẳn vì ko còn dummy), vậy leak rất nhiều

        Point3Target do chỉ được tạo ra một lần nên chỉ cần một lần xóa, cái này không sai, và phải để trong cái If có xóa dummy mới đúng vì mỗi dummy sẽ có một "target point" ứng với nó.

        ngoài ra, cái "If" dùng để xóa point lại đặt ngoài loop - action của Unit Group - Pick... kia nên GetEnumUnit() đâu có tác dụng?
        và một lần nữa, dùng TempValue luôn có nhanh hơn ko?
     
  13. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Em đã sửa, cảm ơn bác. :|
    Mình đã update phần chú ý ở #1, các bạn đọc và tích cực tham gia nhé. Cố gắng 1 ngày 1 thành viên được 1 spell.
     
  14. meomeo3101

    meomeo3101 Thành viên cấp 5

    Tham gia ngày:
    3/2/07
    Bài viết:
    246
    Nơi ở:
    Hà Nội
    Cho mình hỏi với. Khi làm các bạn thường cho biến Mui=Mui+1 mà mình không hiểu nó có tác dụng gì? Có phải để tránh lag game không?
    À! Mình hiểu rồi. Cám ơn nhé skill đẹp lắm nhưng bạn để nhiều creep quá không tài nào test nổi skill. Bạn nên để nông dân còn các skill sau thì nên để 0 cd hoặc cho phép hồi cd chứ khó test skill quá :D.
     
    Chỉnh sửa cuối: 9/1/11
  15. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Mã:
    Set MUI = (MUI + 1) //có nghĩa là ban đầu MUI = 0, sau 1 lần cast thì MUI = 0 + 1, khi đó MUI = 1, tiếp tục lần thứ 2 MUI = (MUI = 1) + 1 thì MUI = 2, rồi lần thứ 3 MUI = (MUI = 2(1 + 1 = 2)) + 1, lần thứ 4 MUI = (MUI = 3(2+1 = 3)) + 1, rồi lần thứ 5 MUI = 4 + 1 = 5, rồi lần thứ 6 MUI = 5 + 1 = 6...................
    Có giải thích rất kĩ trên spell đấy bạn, phải đọc thì mới hiểu chứ. Và vì đó là Spell MUI nên dùng 1 biến Integer để đếm, mỗi lần cast biến Integer đó lại thay đổi giá trị =>các đoạn code ko bị trùng nhau là hoạt động độc lập => có thể dùng spell đó nhiều lần cùng 1 lúc mà không bị bug.
     
    Chỉnh sửa cuối: 9/1/11
  16. LeoNguyen112

    LeoNguyen112 Thành viên cấp 5

    Tham gia ngày:
    22/5/10
    Bài viết:
    1,438
    Nơi ở:
    TP.HCM
    MUI vậy thì các biến có array để MUI, array lên đến 500 à? Mình không quen làm MUI kiểu này (làm MUI có từng player thì mình biết) nên ai giải thích hộ.
     
  17. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Đúng rồi, lên đến 500 thì set MUI = 0. Tại thấy để 100 thì ít quá :P
     
  18. LeoNguyen112

    LeoNguyen112 Thành viên cấp 5

    Tham gia ngày:
    22/5/10
    Bài viết:
    1,438
    Nơi ở:
    TP.HCM
    Ké cái spell :P

    Tên Spell: Rock's Dance \:D/(vũ điệu của đá)
    MUI: yes
    Leak + lag: ít lag và ít leak (hi vọng thế :D)
    Mô tả:
    ENG:
    [spoil]Creates a rock that jumps forwardly and deals damage to units nearby. When arrives at the target point, the rock will jump back.

    Level 1 - 180 total damage.
    Level 2 - 240 total damage.
    Level 3 - 300 total damage.
    Level 4 - 360 total damage.[/spoil]
    VN:
    [spoil]Tạo 1 tảng đá "nhảy" về phía trước, gây sát thương lên các units trong tầm. Khi tới mục tiêu, tảng đá sẽ "nhảy" ngược về vị trí cũ.

    Level 1 - 180 sát thương (đi 90 về 90)
    Level 2 - 240 sát thương (đi 120 về 120)
    Level 3 - 300 sát thương (đi 150 về 150)
    Level 4 - 360 sát thương (đi 180 về 180)[/spoil]

    Cách làm:
    Cần các biến sau:
    MUI, TempValue: integer
    RD_Caster[array], RD_Unit[array]: unit
    RD_CasterPoint[array], RD_TargetPoint[array], RD_TempPoint[array]: point
    TempPoint, TempPoint2: point (cẩn thận với các biến point này, dễ nhầm lắm đấy)
    RD_Group. DR_TargetGroup[array], TempGroup: group
    Cuối cùng là thêm 1 chú dummy có model hòn đá, Leo dùng model volcano missile có sẵn trong WE, đường dẫn của model: Abilities\Spells\Other\Volcano\VolcanoMissile.mdl

    Trigger:
    Mã:
    Rocks Dance
        Events
            Unit - A unit Starts the effect of an ability
        Conditions
            (Ability being cast) Equal to (==) Rock's Dance 
        Actions
             [COLOR="Blue"][Hàm if này dùng để MUI, Leo không nói nhé (raivor nói ở mấy bài đầu rồi)][/COLOR]
             Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                    MUI Equal to (==) 100
                Then - Actions
                    Set MUI = 0
                Else - Actions
                    Set MUI = (MUI + 1)
            Set RD_Caster[MUI] = (Triggering unit) [COLOR="Blue"]// đặt caster vào biến, dùng để deal damage[/COLOR]
            Set TempPoint = (Position of RD_Caster[MUI]) [COLOR="Blue"]// dùng để đỡ leak[/COLOR]
            Set TempPoint2 = (Target point of ability being cast) [COLOR="Blue"]// như trên[/COLOR]
           [COLOR="Red"] Set TempPoint3 = (TempPoint offset by 20.00 towards (Angle from TempPoint to TempPoint2) degrees)[/COLOR] [COLOR="Blue"]// đây là vị trí mà dummy (tảng đá) xuất hiện[/COLOR]
            Unit - Create 1 Dummy for (Owner of RD_Caster[MUI]) at TempPoint3 facing Default building facing (270.0) degrees 
            Set RD_Unit[MUI] = (Last created unit)
            Unit - Add Crow Form to (Last created unit) [COLOR="Blue"]// add và remove ability crow form để dummy có thể bay[/COLOR]
            Unit - Remove Crow Form from (Last created unit)
            Animation - Play (Last created unit)'s birth animation [COLOR="Blue"]// vì volcano missile có animation birth là hình tảng đá nên ta chỉ dùng animation này[/COLOR]
            Unit - Set the custom value of (Last created unit) to MUI [COLOR="Blue"]// dùng để MUI[/COLOR]
            Unit Group - Add (Last created unit) to RD_Group [COLOR="Blue"]// add mấy chú đá vào group để lát "kiêu" cho dễ[/COLOR]
            Set RD_CasterPoint[MUI] = (Position of RD_Caster[MUI]) [COLOR="Blue"]// đây là point mà dummy sẽ quay lại[/COLOR]
            Set RD_TargetPoint[MUI] = (TempPoint offset by 800.00 towards (Angle from TempPoint to TempPoint2) degrees) [COLOR="Blue"]// point này là hướng đi và đích đến của dummy[/COLOR]
            Set RD_TempPoint[MUI] = (TempPoint offset by 300.00 towards (Angle from TempPoint to RD_TargetPoint[MUI]) degrees) [COLOR="Blue"]// point này là điểm chạm đất của dummy[/COLOR]
            Set RD_Damage[MUI] = (60.00 + ((Real((Level of Rock's Dance  for RD_Caster[MUI]))) x 30.00)) [COLOR="Blue"]// damage sẽ gây ra mỗi lần dummy đi qua
            [Và đây là phần xóa leak point][/COLOR]
            Custom script:   call RemoveLocation(udg_TempPoint)
            Custom script:   call RemoveLocation(udg_TempPoint2)
            Custom script:   call RemoveLocation(udg_TempPoint3)
    
    Mã:
    Rocks Dance Move
        Events
            Time - Every 0.05 seconds of game time
        Conditions
        Actions
            Unit Group - Pick every unit in RD_Group and do (Actions)
                Loop - Actions
                    [COLOR="Blue"] [Hàm if kiểm tra nếu không có dummy nào tồn tại thì reset MUI, xóa leak group][/COLOR]
                     Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        If - Conditions
                            (Picked unit) Equal to (==) No unit
                        Then - Actions
                            Set MUI = 0 [COLOR="Blue"]// reset MUI[/COLOR]
                            Custom script:   call DestroyGroup(udg_RD_Group) [COLOR="Blue"]// xóa leak[/COLOR]
                        Else - Actions
                            [COLOR="Blue"][Nếu có dummy tồn tại thì trigger sẽ thực hiện những lệnh sau][/COLOR]
                            Set TempPoint = (Position of (Picked unit))
                            Set TempValue = (Custom value of (Picked unit))
                            [COLOR="Blue"][Hàm if kiểm tra khoảng cách của dummy, nếu dummy đã tới RD_CasterPoint, tức là đã về nhà thì sẽ kill dummy + xóa leak][/COLOR]
                             Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                If - Conditions
                                    [COLOR="Red"](Distance between TempPoint and RD_CasterPoint[TempValue]) Less than or equal to (<=) 10.00[/COLOR] // dòng này, số 10 có thể thay đổi nhưng phải nhỏ hơn khoảng cách create dummy (dòng in đỏ) ở trigger trước 
                                Then - Actions
                                    Unit - Kill (Picked unit)
                                    [COLOR="Blue"][Xóa leak point][/COLOR]
                                    Custom script:   call RemoveLocation(udg_RD_TargetPoint[udg_TempValue])
                                    Custom script:   call RemoveLocation(udg_RD_TempPoint[udg_TempValue])
                                    Custom script:   call RemoveLocation(udg_TempPoint)
                                Else - Actions
                                     [COLOR="Blue"][Nếu điều kiện trên sai thì sẽ thực hiện hàm if này, nó làm cho dummy bay lên hoặc rớt xuống][/COLOR]
                                     Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                        If - Conditions
                                            [COLOR="Red"](Distance between TempPoint and RD_TempPoint[TempValue]) Greater than or equal to (>=) 150.00[/COLOR] [COLOR="Blue"]// bạn có thể thay 150 = số khác, nhưng nó phải = 1 nửa khoảng cách của RD_TempPoint, ở trigger đầu, Leo cho RD_TempPoint là 300 nên ở đây sẽ là 150[/COLOR]
                                        Then - Actions
                                            [COLOR="SeaGreen"]Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) + 50.00) at 0.00 [/COLOR][COLOR="Blue"]// bay lên[/COLOR]
                                        Else - Actions
                                            [COLOR="SeaGreen"]Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) - 50.00) at 0.00 [/COLOR][COLOR="Blue"]// hạ xuống[/COLOR]
                                     [COLOR="Blue"][Hàm if này kiểm tra nếu dummy chạm đất thì tạo effect, deal damage][/COLOR]
                                     Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                        If - Conditions
                                            (Distance between TempPoint and RD_TempPoint[TempValue]) Less than or equal to (<=) 10.00
                                        Then - Actions
                                            Set RD_TempPoint[TempValue] = (TempPoint offset by 300.00 towards (Angle from TempPoint to RD_TargetPoint[TempValue]) degrees) [COLOR="Blue"]// đặt lại vị trí RD_TempPoint, tức là dummy sẽ tiếp tục "nhảy"[/COLOR]
                                            [COLOR="Blue"][Phần effect này, Leo cố lắm mới nghĩ ra được 2 cái effect:D][/COLOR]
                                            Special Effect - Create a special effect at TempPoint using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
                                            Special Effect - Destroy (Last created special effect)
                                            Special Effect - Create a special effect at TempPoint using Abilities\Spells\Other\Doom\DoomDeath.mdl
                                            Special Effect - Destroy (Last created special effect)
                                            [COLOR="Blue"][Và phần deal damage][/COLOR]
                                            Set TempGroup = (Units within 250.00 of TempPoint matching (((((Matching unit) is A structure) Equal to (==) False) and (((Matching unit) is alive) Equal to (==) True)) and ((((Matching unit) belongs to an enemy of (Owner of (Picked unit))) Equal to (==) True) and (((Matching unit) is in RD_TargetGroup[TempValue] Equal to (==) False)
                                            [COLOR="Blue"][Action trên khá dài, có 4 conditions trong đó, 1 pick unit không là structure, 2 pick unit còn sống, 3 pick unit phải là kẻ thù của dummy( caster), 4 pick unit không nằm trong RD_TargetGroup (tránh gây damage nhiều lần cho 1 unit)][/COLOR]
                                            Unit Group - Pick every unit in TempGroup and do (Actions)
                                                Loop - Actions
                                                    Unit - Cause RD_Caster[TempValue] to damage (Picked unit), dealing RD_Damage[TempValue] damage of attack type Spells and damage type Normal [COLOR="Blue"]// deals damage đã đặt sẵn trong trigger trước[/COLOR]
                                                    Unit Group - Add (Picked unit) to RD_TargetGroup[TempValue]
                                            Custom script:   call DestroyGroup(udg_TempGroup) [COLOR="Blue"]// xóa leak group[/COLOR]
                                        Else - Actions
                                     [COLOR="Blue"][Hàm if sau dùng để cho dummy quay đầu về và deal damage lần 2][/COLOR]
                                     Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                        If - Conditions
                                            (Distance between TempPoint and RD_TargetPoint[TempValue]) Less than or equal to (<=) 10.00
                                        Then - Actions
                                            Set RD_TargetPoint[TempValue] = RD_CasterPoint[TempValue] [COLOR="Blue"]// đặt RD_TargetPoint là điểm khởi đầu của dummy, vì dummy chỉ move theo hướng của RD_TargetPoint[/COLOR]
                                            Unit Group - Remove all units from RD_TargetGroup[TempValue] [COLOR="Blue"]// xóa RD_TargetGroup để deal damage lần 2[/COLOR]
                                        Else - Actions
                                    [COLOR="SeaGreen"]Set TempPoint2 = (TempPoint offset by 25.00 towards (Angle from TempPoint to RD_TempPoint[TempValue]) degrees)[/COLOR]
                                    Unit - Move (Picked unit) instantly to TempPoint2 [COLOR="Blue"]// move dummy (kết hợp với action change flying height nên mới có cảm giác dummy "nhảy"
                                    [Xóa leak point][/COLOR]
                                    Custom script:   call RemoveLocation(udg_TempPoint)
                                    Custom script:   call RemoveLocation(udg_TempPoint2)
    
    Chú ý:
    -Phần màu đỏ là phần cần chú ý nhé.
    -Phần màu xanh quyết định tốc độ và độ cao của tảng đá
    Trigger dài dòng vậy nhưng có thể diễn tả như sau:
    - Khi cast sẽ tạo 1 dummy (tảng đá) ở vị trí unit, mục tiêu là RD_TargetPoint, dummy sẽ move dần tới đó, đoạn đường đi sẽ chia làm 3 (hay 4 nhỉ? quên rồi) đoạn ứng với những lần mà dummy sẽ nhảy, mỗi đoạn lại chia làm 2 ( 1 bay lên và 1 rớt xuống). Khi dummy đến được chỗ RD_TargetPoint, lúc này biến đó sẽ được đặt là vị trí cũ của dummy, nên nó sẽ quay về.

    Và đây là kết quả ;)) (Skill không đẹp lắm do Leo không thích màu mè nhiều, các bạn thêm effect vào cho nó nổi bật hơn nhé)
    [​IMG]
    Bạn muốn có demo? Nó đây (khi test nhấn Esc để kiểm tra MUI nhé)

    Cho Leo điểm nào :D
     
    Chỉnh sửa cuối: 9/1/11
  19. Tom_Kazansky

    Tom_Kazansky Thành viên BQT Moderator

    Tham gia ngày:
    28/12/06
    Bài viết:
    3,454
    Nơi ở:
    Hà Nội
    • Trigger Rocks Dance:
      - chưa remove TempPoint2
      - (Angle from TempPoint to (Target point of ability being cast)) leak
    • Trigger Rocks Dance Move:
      - Muốn change fly height ngay lập tức thì "rate" để 0 chứ để số to thế kia làm gì...
      - Dùng nhiều Picked unit thì cũng nên cho vào biến

    biến MUI kia có 100 là tự reset, vậy 101 lần cast cùng lúc thì lỗi à?
    tất nhiên khó có thể lên đc số lần đó tuy nhiên thế ko ổn, nên đặt biến MUI = 0 khi RD_Group không còn unit nào.
    ngoài ra, spell nào cũng thấy dùng biến MUI, ko sợ đánh nhau sao?
     
  20. raivor

    raivor Thành viên cấp 5

    Tham gia ngày:
    24/7/09
    Bài viết:
    1,337
    Theo nhận xét của bác Tom và riêng của em thì em sẽ chấm như sau:
    - Ưu:
    +Về ý tưởng: tốt (2đ)
    -Khuyết:
    +Về thẩm mĩ: khá (0.75đ) (spell không được đẹp mắt cho lắm, nhất là tảng đá X_X. Và nên làm tảng đá "nhảy" mượt hơn.)
    +Về code: tàm tạm (0.5đ) (chưa remove hết leak, trình bày khá rối mắt)
    +Về phần giải thích: không được cụ thể cho lắm (3đ) (cái này thì theo mình là vậy thôi)
    +Về nhận xét riêng của bạn: không thích lắm (0.25đ).
    Tổng điểm: 6.5đ
    Mong bạn cố gắng lần sau.
     

Chia sẻ trang này