• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

No Town Hall Warning for Custom Race

Status
Not open for further replies.
Level 6
Joined
Jul 10, 2016
Messages
172
Im making a custom race but apparently my triggers to spawn or make the new faction's townhall and peasants shows that reveal warning.

I removed the default create starting units for player trigger as you can see below

upload_2019-6-2_18-8-29.png


And made a dialog button to choose your race

upload_2019-6-2_18-9-6.png


And actions when dialog button is clicked

upload_2019-6-2_18-9-30.png


and replaced the default starting unit etc etc with this

upload_2019-6-2_18-10-7.png


But like i said before, it still makes the No Town Hall Reveal Warning

Now if you are going to say the game constants etc etc its there too

upload_2019-6-2_18-11-27.png


How can I fix this? or at the very least make a new trigger which works. Thank you!
 
Level 8
Joined
May 21, 2019
Messages
435
First of all, you've removed the starting units, yet your prompts for chosing a race appears at 0.05 (and in multiplayer, would not pause the game)
This means that those conditions are triggered within that time.
Is the warning removed once your team has their new units generated?
If so, a simple fix would be to remove the "Enforce victory/defeat conditions for all players" action from the initialization, and run that later on when people have picked races instead.
 
Level 6
Joined
Jul 10, 2016
Messages
172
First of all, you've removed the starting units, yet your prompts for chosing a race appears at 0.05 (and in multiplayer, would not pause the game)
I changed that to 0 seconds and still shows the timer

Is the warning removed once your team has their new units generated?
not really

If so, a simple fix would be to remove the "Enforce victory/defeat conditions for all players" action from the initialization, and run that later on when people have picked races instead.
I tried this by making a new trigger where the enforce victory action would be activated after 10 seconds and it still shows the no town hall timer reveal etc etc
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Many of the melee init trigger functions have unit types/rawcodes hardcoded into them, and I would guess your problem has something to do with that. Another avenue to look at would be downloading someone else's custom race replacement map/mod and see how they set up the regular melee init stuff for custom races. There are definitely some such resources here at THW.
 
Level 8
Joined
May 21, 2019
Messages
435
it still shows the no town hall timer reveal etc etc
Another thing that strikes me is how you are using more factions than what's normally available. What I do not know for certain, is whether the values in the gameplay constants are actually fetched based on indices somewhere else in the game code. If that is the case, you may find that having 5 races as shown in your post, could break that.
Have you tried circumventing the issue using a dummy Town Hall that is removed once the race is chosen?
 
Level 6
Joined
Jul 10, 2016
Messages
172
Have you tried circumventing the issue using a dummy Town Hall that is removed once the race is chosen?
not really, but now im thinking of using Human as the default race and have every race trigger replace the peasants and townhall with designated units and buildings
 
Level 8
Joined
May 21, 2019
Messages
435
not really, but now im thinking of using Human as the default race and have every race trigger replace the peasants and townhall with designated units and buildings
That may work. I can't say for certain, as we haven't really found the root of the issue, but it's pretty likely to work.
I assume that you have based the custom Town Hall equivalents on actual Town Hall buildings, right?
 
Level 8
Joined
May 21, 2019
Messages
435
the replacement worked but it still has the no town hall timer
yes all of them came from great hall of orc

There's so many possible things that could interfere with this, that you may wanna take a rain check on your options.
Pyrogasms suggestion about checking out what others have done is quite good, but you may also want to consider a total workaround on the victory/defeat conditions.

There seems to be a lot of issues with stuff like this in general:
How to: Create a Full-working Custom Race
This thread says that all races MUST replace an existing one. Can't find the explicit reason why, but it definitely messes big-time with the AI to have far too custom races, and I can imagine that a lot of things in the melee code isn't written to handle custom units at all.

EDIT: It actually states the following:
The problem is that Melee V/D conditions consider only basic town hall units as real town halls, no matter what you have typed in gameplay constants. That means if you enable that function you will lose/win when you destroy/run out of buildings, but you will be also revealed 2 mins after the beginning of the game, getting unusual annoying messages.

So, you basically have to delete the victory/defeat conditions if you aren't using the base town halls.

All in all, I think I would grab the bull by the horns and write my own victory/defeat condition triggers. That way, you gain complete control and avoid any future issues. Fixing the AI may be nearly impossible if you aren't giving the computer players a custom race based largely on the standard race that they have rolled, and you also have the UI to take into account when choosing a race after the game has started.
 
Level 13
Joined
May 10, 2009
Messages
868
Unfortunately, Blizzard, for some reason, chose to check for the 4 basic town halls by their names, instead of checking by town-hall classification.

Here's the Blizzard condition/function:
JASS:
function MeleeGetAllyKeyStructureCount takes player whichPlayer returns integer
    local integer    playerIndex
    local player     indexPlayer
    local integer    keyStructs

    // Count the number of buildings controlled by all not-yet-defeated co-allies.
    set keyStructs = 0
    set playerIndex = 0
    loop
        set indexPlayer = Player(playerIndex)
        if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
            set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
            set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
            set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
            set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
        endif
       
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYERS
    endloop

    return keyStructs
endfunction

It's easy to fix that function alone. However, you'll have to rewrite the entire "Victory/Defeat Conditions" trigger.

You can make use of Retera's code, or you can use mine. Both address that issue.


JASS:
//==============================================================================================//
// WARNING: YOU MUST PASTE THE FOLLOWING CODE IN A PLACE WHERE YOUR "INITIALIZATION TRIGGER"... //
// IS CAPABLE OF ACCESSING IT. IT'S RECOMMENDED THAT YOU PASTE IT IN YOUR MAP HEADER            //
//                                                                                              //
// The main purpose of this is to just get rid of the bug where custom units flagged as         //
// town hall doesn't count for the CripplePlayer function. I also removed a few leaks generated //
// by Blizzard.                                                                                 //
//                                                                                              //
//==============================================================================================//


// Count units flagged as UNIT_TYPE_TOWNHALL
function GetAllyKeyStructureCount takes player whichPlayer returns integer
    local unit fog = null
    local group g = CreateGroup()
    local integer result = 0
    local integer playerIndex = 0
 
    loop
        exitwhen playerIndex == bj_MAX_PLAYERS
        if PlayersAreCoAllied(whichPlayer, Player(playerIndex)) then
            call GroupEnumUnitsOfPlayer(g, Player(playerIndex), null)
            loop
                set fog = FirstOfGroup(g)
                exitwhen fog == null
                call GroupRemoveUnit(g, fog)
                if IsUnitType(fog, UNIT_TYPE_TOWNHALL) and not IsUnitType(fog, UNIT_TYPE_DEAD) and GetUnitTypeId(fog) != 0 then
                    set result = result + 1
                endif
            endloop
        endif
        set playerIndex = playerIndex + 1
    endloop
 
    call DestroyGroup(g)
    set g = null
    return result
endfunction

function CheckForVictors takes force opponentlessPlayers returns force
    local integer    playerIndex
    local integer    opponentIndex
    local boolean    gameOver = false
 
    // Check to see if any players have opponents remaining.
    set playerIndex = 0
    loop
        if (not bj_meleeDefeated[playerIndex]) then
            // Determine whether or not this player has any remaining opponents.
            set opponentIndex = 0
            loop
                // If anyone has an opponent, noone can be victorious yet.
                if MeleePlayerIsOpponent(playerIndex, opponentIndex) then
                    call DestroyForce(opponentlessPlayers)
                    set opponentlessPlayers = null
                    return null
                endif

                set opponentIndex = opponentIndex + 1
                exitwhen opponentIndex == bj_MAX_PLAYERS
            endloop
        
            // Keep track of each opponentless player so that we can give
            // them a victory later.
            call ForceAddPlayer(opponentlessPlayers, Player(playerIndex))
            set gameOver = true
        endif
    
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYERS
    endloop

    // Set the game over global flag
    set bj_meleeGameOver = gameOver

    return opponentlessPlayers
endfunction

// Find opponentless players
function CheckForLosersAndVictors takes nothing returns nothing
    local integer    playerIndex
    local player     indexPlayer
    local force      defeatedPlayers = CreateForce()
    local force      victoriousPlayers = null
    local boolean    gameOver = false

    // If the game is already over, do nothing
    if (bj_meleeGameOver) then
        return
    endif

    // If the game was disconnected then it is over, in this case we
    // don't want to report results for anyone as they will most likely
    // conflict with the actual game results
    if (GetIntegerGameState(GAME_STATE_DISCONNECTED) != 0) then
        set bj_meleeGameOver = true
        return
    endif

    // Check each player to see if he or she has been defeated yet.
    set playerIndex = 0
    loop
        set indexPlayer = Player(playerIndex)

        if (not bj_meleeDefeated[playerIndex] and not bj_meleeVictoried[playerIndex]) then
            //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "Player"+I2S(playerIndex)+" has "+I2S(MeleeGetAllyStructureCount(indexPlayer))+" ally buildings.")
            if (MeleeGetAllyStructureCount(indexPlayer) <= 0) then

                // Keep track of each defeated player so that we can give
                // them a defeat later.
                call ForceAddPlayer(defeatedPlayers, Player(playerIndex))

                // Set their defeated flag now so MeleeCheckForVictors
                // can detect victors.
                set bj_meleeDefeated[playerIndex] = true
            endif
        endif
        
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYERS
    endloop

    // Now that the defeated flags are set, check if there are any victors
    set victoriousPlayers = CheckForVictors(CreateForce())

    // Defeat all defeated players
    call ForForce(defeatedPlayers, function MeleeDoDefeatEnum)

    // Give victory to all victorious players
    call ForForce(victoriousPlayers, function MeleeDoVictoryEnum)

    // If the game is over we should remove all observers
    if (bj_meleeGameOver) then
        call MeleeRemoveObservers()
    endif
 
    // Destroy and null handles
    call DestroyForce(defeatedPlayers)
    if victoriousPlayers != null then
        call DestroyForce(victoriousPlayers)
        set victoriousPlayers = null
    endif
    set defeatedPlayers = null
    set indexPlayer = null
endfunction

// CRIPPLE
function CheckForCrippledPlayers takes nothing returns nothing
    local integer    playerIndex
    local player     indexPlayer
    local boolean    isNowCrippled

    // The "finish soon" exposure of all players overrides any "crippled" exposure
    if bj_finishSoonAllExposed then
        return
    endif

    // Check each player to see if he or she has been crippled or uncrippled.
    set playerIndex = 0
    loop
        set indexPlayer = Player(playerIndex)
        set isNowCrippled = (MeleeGetAllyStructureCount(indexPlayer) > 0) and (GetAllyKeyStructureCount(indexPlayer) <= 0)

        if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then

            // Player became crippled; start their cripple timer.
            set bj_playerIsCrippled[playerIndex] = true
            call TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, function MeleeCrippledPlayerTimeout)

            if (GetLocalPlayer() == indexPlayer) then
                // Use only local code (no net traffic) within this block to avoid desyncs.

                // Show the timer window.
                call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)

                // Display a warning message.
                call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, MeleeGetCrippledWarningMessage(indexPlayer))
            endif

        elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then

            // Player became uncrippled; stop their cripple timer.
            set bj_playerIsCrippled[playerIndex] = false
            call PauseTimer(bj_crippledTimer[playerIndex])

            if (GetLocalPlayer() == indexPlayer) then
                // Use only local code (no net traffic) within this block to avoid desyncs.

                // Hide the timer window for this player.
                call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)

                // Display a confirmation message if the player's team is still alive.
                if (MeleeGetAllyStructureCount(indexPlayer) > 0) then
                    if (bj_playerIsExposed[playerIndex]) then
                        call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
                    else
                        call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
                    endif
                endif
            endif

            // If the player granted shared vision, deny that vision now.
            call MeleeExposePlayer(indexPlayer, false)

        endif
        
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYERS
    endloop
 
    set indexPlayer = null
endfunction

// UNIT DIES
function TriggerActionCheckLostUnit takes nothing returns nothing
    if IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
        // We only need to check for mortality if this was the last building.
        if (GetPlayerStructureCount(GetOwningPlayer(GetTriggerUnit()), true) <= 0) then
            call CheckForLosersAndVictors()
        endif

        // Check if the lost unit has crippled or uncrippled the player.
        // (A team with 0 units is dead, and thus considered uncrippled.)
        call CheckForCrippledPlayers()
    endif
endfunction

function TriggerActionAllianceChange takes nothing returns nothing
    call CheckForLosersAndVictors()
    call CheckForCrippledPlayers()
endfunction

function TriggerActionCheckAddedUnit takes nothing returns nothing
    // If the player was crippled, this unit may have uncrippled him/her.
    if (bj_playerIsCrippled[GetPlayerId(GetOwningPlayer(GetTriggerUnit()))]) then
        call CheckForCrippledPlayers()
    endif
endfunction

function TriggerActionPlayerDefeated takes nothing returns nothing
    local player thePlayer = GetTriggerPlayer()
    call CachePlayerHeroData(thePlayer)

    if (MeleeGetAllyCount(thePlayer) > 0) then
        // If at least one ally is still alive and kicking, share units with
        // them and proceed with death.
        call ShareEverythingWithTeam(thePlayer)
        if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
            call MeleeDoDefeat(thePlayer)
        endif
    else
        // If no living allies remain, swap all units and buildings over to
        // neutral_passive and proceed with death.
        call MakeUnitsPassiveForTeam(thePlayer)
        if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
            call MeleeDoDefeat(thePlayer)
        endif
    endif
    call CheckForLosersAndVictors()
    set thePlayer = null
endfunction

function TriggerActionPlayerLeft takes nothing returns nothing
    local player thePlayer = GetTriggerPlayer()

    // Just show game over for observers when they leave
    if (IsPlayerObserver(thePlayer)) then
        call RemovePlayerPreserveUnitsBJ(thePlayer, PLAYER_GAME_RESULT_NEUTRAL, false)
        set thePlayer = null
        return
    endif

    call CachePlayerHeroData(thePlayer)

    // This is the same as defeat except the player generates the message
    // "player left the game" as opposed to "player was defeated".

    if (MeleeGetAllyCount(thePlayer) > 0) then
        // If at least one ally is still alive and kicking, share units with
        // them and proceed with death.
        call ShareEverythingWithTeam(thePlayer)
        call MeleeDoLeave(thePlayer)
    else
        // If no living allies remain, swap all units and buildings over to
        // neutral_passive and proceed with death.
        call MakeUnitsPassiveForTeam(thePlayer)
        call MeleeDoLeave(thePlayer)
    endif
    call CheckForLosersAndVictors()
    set thePlayer = null
endfunction

function MeleeVictoryDefeat takes nothing returns nothing
    local trigger    trig
    local integer    index
    local player     indexPlayer

    // Create a timer window for the "finish soon" timeout period, it has no timer
    // because it is driven by real time (outside of the game state to avoid desyncs)
    set bj_finishSoonTimerDialog = CreateTimerDialog(null)

    // Set a trigger to fire when we receive a "finish soon" game event
    set trig = CreateTrigger()
    call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
    call TriggerAddAction(trig, function MeleeTriggerTournamentFinishSoon)

    // Set a trigger to fire when we receive a "finish now" game event
    set trig = CreateTrigger()
    call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
    call TriggerAddAction(trig, function MeleeTriggerTournamentFinishNow)

    // Set up each player's mortality code.
    set index = 0
    loop
        set indexPlayer = Player(index)

        // Make sure this player slot is playing.
        if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
            set bj_meleeDefeated[index] = false
            set bj_meleeVictoried[index] = false

            // Create a timer and timer window in case the player is crippled.
            set bj_playerIsCrippled[index] = false
            set bj_playerIsExposed[index] = false
            set bj_crippledTimer[index] = CreateTimer()
            set bj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
            call TimerDialogSetTitle(bj_crippledTimerWindows[index], MeleeGetCrippledTimerMessage(indexPlayer))

            // Set a trigger to fire whenever a building is cancelled for this player.
            set trig = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(trig, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
            call TriggerAddAction(trig, function TriggerActionCheckLostUnit)

            // Set a trigger to fire whenever a unit dies for this player.
            call TriggerRegisterPlayerUnitEvent(trig, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
            call TriggerAddAction(trig, function TriggerActionCheckLostUnit)

            // Set a trigger to fire whenever a unit begins construction for this player
            set trig = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(trig, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
            call TriggerAddAction(trig, function TriggerActionCheckAddedUnit)

            // Set a trigger to fire whenever this player defeats-out
            set trig = CreateTrigger()
            call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_DEFEAT)
            call TriggerAddAction(trig, function TriggerActionPlayerDefeated)

            // Set a trigger to fire whenever this player leaves
            set trig = CreateTrigger()
            call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
            call TriggerAddAction(trig, function TriggerActionPlayerLeft)

            // Set a trigger to fire whenever this player changes his/her alliances.
            set trig = CreateTrigger()
            call TriggerRegisterPlayerAllianceChange(trig, indexPlayer, ALLIANCE_PASSIVE)
            call TriggerRegisterPlayerStateEvent(trig, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
            call TriggerAddAction(trig, function TriggerActionAllianceChange)
        else
            set bj_meleeDefeated[index] = true
            set bj_meleeVictoried[index] = false

            // Handle leave events for observers
            if (IsPlayerObserver(indexPlayer)) then
                // Set a trigger to fire whenever this player leaves
                set trig = CreateTrigger()
                call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
                call TriggerAddAction(trig, function TriggerActionPlayerLeft)
            endif
        endif

        set index = index + 1
        exitwhen index == bj_MAX_PLAYERS
    endloop

    // Test for victory / defeat at startup, in case the user has already won / lost.
    // Allow for a short time to pass first, so that the map can finish loading.
    call TimerStart(CreateTimer(), 2.0, false, function TriggerActionAllianceChange)
endfunction

//==============================================================================================//

I'll also attach a map below that uses different unit types flagged as town hall.
 

Attachments

  • MeleeWinLoseConditions.w3x
    27.7 KB · Views: 30
Level 6
Joined
Jul 10, 2016
Messages
172
I tried your code by making a new trigger and pasting the entire JASS code you gave me and it did not enforce victory or defeat, and in the map you sent I copied the Custom script: call MeleeVictoryDefeat() to my map but instead it game me this errors

upload_2019-6-3_13-26-17.png


I'm using World editor 1.29c with Sharpcraft extended

Is there something I'm missing?
 
Status
Not open for further replies.
Top