- Joined
- Nov 30, 2007
- Messages
- 1,202
I'm trying to hide a unit locally but it seems to be causing desync. Would it work if i unhid the unit for all before removing it?
What the code does is basically switch between different units to be displayed during picking phase.
Critical section:
Usage:
Proposed solution:
I'm also uncertain if its the special effect created on the unit or the removal of the previous dummy unit that causes desync.
What the code does is basically switch between different units to be displayed during picking phase.
Critical section:
JASS:
private function SetSelection takes player p, integer heroIndex returns nothing
local integer pid = GetPlayerId(p)
local SpellMenu menu = UserData(pid).menu
local SpellOption o
if UserData(pid).dummy != null then
call RemoveUnit(UserData(pid).dummy) // Will removing a locally hidden unit cause desync?
endif
set UserData(pid).heroId = heroIndex
set UserData(pid).dummy = CreateUnit(p, Hero(heroIndex).type, x, y, f)
call UnitAddAbility(UserData(pid).dummy, 'Aloc') // locust
if not (GetLocalPlayer() == p) then // Hide it for all except one
call ShowUnit(UserData(pid).dummy, false)
endif
if UserData(pid).option != 0 then
set o = menu.get(menu.indexOf(UserData(pid).option))
call o.setLocalEnabled(pid, true)
endif
set o = Hero(heroIndex).optionKey
call o.setLocalEnabled(pid, false)
set UserData(pid).option = o
if GetLocalPlayer() == p then
call PanCameraToTimed(GetUnitX(UserData(pid).dummy), GetUnitY(UserData(pid).dummy), 0)
endif
// Run callback
set lastPickedHeroType = Hero(heroIndex).type
if trgOnSelect != null then
call TriggerEvaluate(trgOnSelect)
endif
endfunction
JASS:
library HeroPickPlugin uses SpellOptionsMenu, RegisterPlayerEvent
/*
This is a simple Hero Pick system which provides a menu for selecting a hero.
*/
globals
private constant string MENU_TITLE = "Hero Selection"
private constant string RANDOM_HERO_BTN = "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp"
private constant string RANDOM_HERO_DISBTN = "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOff.blp"
private constant string CONFIRM_BTN = "ReplaceableTextures\\CommandButtons\\BTNSell.blp"
private real x
private real y
private real f
private integer lastPickedHeroType
private trigger trgOnPick = null
private trigger trgOnSelect = null
private integer numHeroes = 0
private unit u
private string s
endglobals
/*
Change to appropriate setup
*/
private function SetupSelectionZone takes nothing returns nothing
set x = GetRectCenterX(gg_rct_HeroSelection)
set y = GetRectCenterY(gg_rct_HeroSelection)
set f = 270
endfunction
// **************************************************
// *** System Code **********************************
// **************************************************
private function GetHeroName takes integer unitTypeId returns string
set u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), unitTypeId, 0, 0, 0)
set s = GetUnitName(u)
call RemoveUnit(u)
set u = null
return s
endfunction
private struct UserData extends array
unit dummy
integer heroId
integer option
SpellMenu menu
method clear takes nothing returns nothing
if .dummy != null then
call RemoveUnit(.dummy)
set .dummy = null
endif
if .menu != 0 then
call .menu.destroy()
set .menu = 0
call BJDebugMsg("Destroyed...")
endif
set .heroId = -1
set .option = 0
call BJDebugMsg("Clear completed...")
endmethod
// will remove any menus that are left open
private static method onLeave takes nothing returns nothing
local integer pid = GetPlayerId(GetTriggerPlayer())
if UserData(pid).menu != 0 then
call UserData(pid).clear()
endif
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
loop
exitwhen i == bj_MAX_PLAYERS
set UserData(i).heroId = -1
set i = i + 1
endloop
call SetupSelectionZone()
call RegisterAnyPlayerEvent(EVENT_PLAYER_LEAVE, function thistype.onLeave)
endmethod
endstruct
private struct Hero extends array
integer type
string btn
string disbtn
string label
string tooltip
integer optionKey
method setLabel takes string s returns thistype
set .label = s
return this
endmethod
method setType takes integer t returns thistype
set .type = t
call .setLabel(GetHeroName(t))
return this
endmethod
method setTooltip takes string s returns thistype
set .tooltip = s
return this
endmethod
method setBtn takes string s returns thistype
set .btn = s
return this
endmethod
method setDisbtn takes string s returns thistype
set .disbtn = s
return this
endmethod
endstruct
private function SetSelection takes player p, integer heroIndex returns nothing
local integer pid = GetPlayerId(p)
local SpellMenu menu = UserData(pid).menu
local SpellOption o
if UserData(pid).dummy != null then
call RemoveUnit(UserData(pid).dummy) // Will removing a locally hidden unit cause desync?
endif
set UserData(pid).heroId = heroIndex
set UserData(pid).dummy = CreateUnit(p, Hero(heroIndex).type, x, y, f)
call UnitAddAbility(UserData(pid).dummy, 'Aloc') // locust
if not (GetLocalPlayer() == p) then // Hide it for all except one
call ShowUnit(UserData(pid).dummy, false)
endif
if UserData(pid).option != 0 then
set o = menu.get(menu.indexOf(UserData(pid).option))
call o.setLocalEnabled(pid, true)
endif
set o = Hero(heroIndex).optionKey
call o.setLocalEnabled(pid, false)
set UserData(pid).option = o
if GetLocalPlayer() == p then
call PanCameraToTimed(GetUnitX(UserData(pid).dummy), GetUnitY(UserData(pid).dummy), 0)
endif
// Run callback
set lastPickedHeroType = Hero(heroIndex).type
if trgOnSelect != null then
call TriggerEvaluate(trgOnSelect)
endif
endfunction
private function GetRandomHero takes player p returns integer
local integer rdm
local integer pid = GetPlayerId(p)
if numHeroes > 1 then
loop
set rdm = GetRandomInt(0, numHeroes - 1)
if rdm != UserData(pid).heroId then
return rdm
endif
endloop
endif
return 0
endfunction
private function OnRandomPick takes nothing returns nothing
local player p = GetTriggerPlayer()
call SetSelection(p, GetRandomHero(p))
set p = null
endfunction
private function OnHeroPick takes nothing returns nothing
local SpellOption o = GetTriggerSpellOption()
call SetSelection(GetTriggerPlayer(), o.userData)
endfunction
private function OnConfirm takes nothing returns nothing
local player p = GetTriggerPlayer()
local integer pid = GetPlayerId(p)
call UserData(pid).menu.close(p)
set lastPickedHeroType = Hero(UserData(pid).heroId).type
call UserData(pid).clear()
// Run callback
if trgOnPick != null then
call TriggerEvaluate(trgOnPick)
endif
set p = null
endfunction
private function AddMenuOptions takes SpellMenu menu returns nothing
local integer i = 0
local Hero h
local SpellOption o
loop
exitwhen i >= numHeroes
set h = Hero(i)
set o = menu.createOption(0, h.label, h.tooltip, h.btn, h.disbtn, function OnHeroPick)
set h.optionKey = o
set o.userData = i
set i = i + 1
endloop
set o = menu.createOption(3, "Random Hero", "Choses a random hero for you.", RANDOM_HERO_BTN, RANDOM_HERO_DISBTN, function OnRandomPick)
call menu.lock(o, 3)
set o = menu.createOption(7, "Confirm Hero", " ", CONFIRM_BTN, CONFIRM_BTN, function OnConfirm)
call menu.lock(o, 7)
endfunction
// **************************************************
// *** Public Interface *****************************
// **************************************************
function AddPickableHero takes integer unitTypeId, string btn, string disbtn, string description returns nothing
call Hero(numHeroes).setType(unitTypeId).setTooltip(description).setBtn(btn).setDisbtn(disbtn)
set numHeroes = numHeroes + 1
endfunction
function OpenHeroPickMenu takes player p returns nothing
local integer pid = GetPlayerId(p)
if UserData(pid).menu == 0 then
set UserData(pid).menu = SpellMenu.create(MENU_TITLE, true)
call AddMenuOptions(UserData(pid).menu)
endif
call SetSelection(p, GetRandomHero(p))
call UserData(pid).menu.open(p)
endfunction
function OpenHeroPickMenuAll takes nothing returns nothing
local integer i = 0
local player p
loop
exitwhen i == bj_MAX_PLAYERS
set p = Player(i)
if (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and /*
*/ GetPlayerController(p) == MAP_CONTROL_USER) then
call OpenHeroPickMenu(p)
endif
set i = i + 1
endloop
set p = null
endfunction
function GetPickedHeroDummy takes player p returns unit
return UserData(GetPlayerId(p)).dummy
endfunction
function GetPickedHeroType takes nothing returns integer
return lastPickedHeroType
endfunction
function OnHeroPickEvent takes code c returns nothing
if trgOnPick == null then
set trgOnPick = CreateTrigger()
endif
call TriggerAddCondition(trgOnPick, Condition(c))
endfunction
function OnHeroPickChangeEvent takes code c returns nothing
if trgOnSelect == null then
set trgOnSelect = CreateTrigger()
endif
call TriggerAddCondition(trgOnSelect, Condition(c))
endfunction
endlibrary
JASS:
scope HeroPick initializer Init
/*
Opens the pick menu for all playing players
*/
private function StartHeroPick takes nothing returns nothing
call OpenHeroPickMenuAll()
endfunction
private function OnRepick takes nothing returns nothing
call OpenHeroPickMenu(GetTriggerPlayer())
endfunction
private function OnHeroPick takes nothing returns nothing
local player p = GetTriggerPlayer()
local integer unitTypeId = GetPickedHeroType()
local unit u = CreateUnit(p, unitTypeId, 0, 0, 0)
if GetLocalPlayer() == p then
call PanCameraToTimed(GetUnitX(u), GetUnitY(u), 0)
call SelectUnit(u, true)
endif
set p = null
set u = null
endfunction
private function OnHeroChange takes nothing returns nothing
local unit u = GetPickedHeroDummy(GetTriggerPlayer())
local effect eff = AddSpecialEffectTargetUnitBJ( "origin", u, "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl")
call DestroyEffect(eff)
set eff = null
set u = null
endfunction
private function Init takes nothing returns nothing
// Chat event
call Command.register("-repick", function OnRepick)
// Callback registration
call OnHeroPickEvent(function OnHeroPick)
call OnHeroPickChangeEvent(function OnHeroChange)
// Paladin
call AddPickableHero('Hpal',/*
*/"ReplaceableTextures\\CommandButtons\\BTNHeroPaladin.blp",/*
*/"ReplaceableTextures\\CommandButtonsDisabled\\DISBTNHeroPaladin.blp",/*
*/"Warrior Hero, exceptional at defense and augmenting nearby friendly troops. Can learn Holy Light, Divine Shield, Devotion Aura and Resurrection. |n|n|cffffcc00Attacks land units.|r")
// Blood Mage
call AddPickableHero('Hblm',/*
*/"ReplaceableTextures\\CommandButtons\\BTNHeroBloodElfPrince.blp",/*
*/"ReplaceableTextures\\CommandButtonsDisabled\\DISBTNHeroBloodElfPrince.blp", /*
*/"Mystical Hero, adept at controlling magic and ranged assaults. Can learn Flame Strike, Banish, Siphon Mana and Phoenix. |n|n|cffffcc00Attacks land and air units.|r")
// Archmage
call AddPickableHero('Hamg',/*
*/"ReplaceableTextures\\CommandButtons\\BTNHeroArchMage.blp",/*
*/"ReplaceableTextures\\CommandButtonsDisabled\\DISBTNHeroArchMage.blp",/*
*/"Mystical Hero, adept at ranged assaults. Can learn Blizzard, Summon Water Elemental, Brilliance Aura and Mass Teleport. |n|n|cffffcc00Attacks land and air units.|r")
call StartHeroPick()
endfunction
endscope
Proposed solution:
JASS:
method safeDummyRemoval takes nothing returns nothing
if GetLocalPlayer() == GetOwningPlayer(.dummy) then
call ShowUnit(.dummy, false) // Now everyone should have the same unit state
endif
call RemoveUnit(.dummy)
endmethod
I'm also uncertain if its the special effect created on the unit or the removal of the previous dummy unit that causes desync.
Last edited: