• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Techtree Contest #14 - Reforge, Don't Refund [Optionally Paired]

Status
Not open for further replies.
Level 9
Joined
Aug 16, 2019
Messages
66
My troop pool (Are there enough units if many of them have 2 forms?):
  • Acolyte (T1) - classic undead worker.
  • Crypt Fiend (Т1) - standard unit that can be upgraded to a weaver or venom.
  • Arachnid Warrior (Т1) - a substitute for ghouls, can harvest wood, and also build special buildings of Nerubian (I will reveal the details later).
  • Impaler (Т2) - (my little pride) the ground version is useless and designed to change the location, but if he digs in, he turns into a killing machine. In a buried state, he attacks with spikes in a straight line direction, and damages buildings with a separate targeted attack.
  • Crypt Lord (Т2) - A powerful Nerub with good health, after eating it eats up corpses, recovers damage and summons up to 2 Carrion Beetles.
  • Vizier (T2) - The Nerubian Mage allows you to infect living opponents with cadaveric spiders, send a dark flock, and burn target mana.
  • Shadow (T2) - The peculiarity of the Nerubians is that they have poor eyesight during the day, which makes it possible to level this reconnaissance unit.
  • Nerubian queen (T3) - So far, not all abilities are thought out, but it is known that it is able to generate combat units directly on the battlefield, but the price of this increase in the final cost of the unit.
AnubarakWIP2.jpg


A word from the author
: a unit, like their abilities, does not represent anything extraordinary. However, they enter into symbiosis with each other, creating unusual combinations on the battlefield.

P.S. Next time I will tell you more about Talents, another mechanic that migrated from my project, which is a common feature of all non-standard races.
 

Attachments

  • AnubarakWIP2.jpg
    AnubarakWIP2.jpg
    1.8 MB · Views: 156
Level 27
Joined
May 18, 2018
Messages
397
Alright, this is the last WIP of the units. I'm starting now with buildings and heroes, but I'm planning to show one last more WIP before my final entry.

NagaWIP4.PNG

Nullifier: Anti-spellcaster unit with short ranged attacks.
-Arcane Curse: Dispel effects on target enemy and silence it. Also increases the effect of Mana Break while silenced.
-Mana Break: Each attack against an enemy with mana have a chance of reduce a percentage of its current mana. If target is silenced by Arcane Curse, the percentage will increase and it will also receive a part as damage.

Stormcaller: T3 offensive spellcaster.
-Lightning Minion: Summons a lightning elemental that deal magic damage to attack enemies. It greatly beneficts from the rest of Stormcaller's spells.
-Static Shock: Overloads the weapons of allied units with lightning power, giving them a chance of deal bonus magic damage on attacks and greatly slow attacked units for 1 second.
-Thunderstorm: Calls down an electrical storm at target area to strike enemy units.

Blazewing: Light flying creature, good against multiple aerial enemies.
-Gale Winds: Aerial attacks can reach 2 extra flying enemies.

Quetzal: Heavy flying creature, exceptional against land wounded targets.
-Acid Breath: Attacks deal bonus damage based on a percentage of target's missing HP. Also affects buildings.
 
Logo_v3.png

Among the Night Elves, few garner greater clout and respect than the Priestesses of Elune
and the Druids of the Cenarion Circle, but within the shadow of these mighty figures is a
secretive society of druids that have chosen to follow the path of shadow. They are errant
Sages that forever seek knowledge from those that have no voice. Unlike their fellows, they
do not sleep, for the quest of their search goes beyond the Emerald Dream.



The Whispering Grove can train up to three units simultaneously, but for
every additional queue beyond the first one, you will have to provide an
increasing amount of Wisp Power. For the sake of brevity, Wisp Power gain
was increased and the training times were reduced in this demo.



@Mythic please add myself and @Wazzz as contestants. This is our first WIP for the Secret Grove.
 
Last edited:
View attachment 349556

Among the Night Elves, fewer garner greater clout and respect than the Priestesses of Elune
and the Druids of the Cenarion Circle, but within the shadow of these mighty figures is a
secretive society of druids that have chosen to follow the path of shadow. They are errant
Sages that forever seek knowledge from those that have no voice. Unlike their fellows, they
do not sleep, for the quest of their search goes beyond the Emerald Dream.



The Whispering Grove can train up to three units simultaneously, but for
every additional queue beyond the first one, you will have to provide an
increasing amount of Wisp Power. For the sake of brevity, Wisp Power gain
was increased and the training times were reduced in this demo.



@Mythic please add myself and @Wazzz as contestants. This is our first WIP for the Secret Grove.
Oh my, a direct rival, great!

This looks extremely similar to what I've been working on for the last 2 days!

Just posting the general theme I'm going for, so that nobody can say I'm ripping Spellbound off (each skin took me 1-2 hours so it would have been impossible) :D

This is not a WIP, that will come later today (although you can see the UI I did):

full
 
@Wazzz Aaah, my predictions came true. Guess you did took my recommendation:
6frames.png



@Spellbound Heyy that training system is quite similar to mine. Seems like you got it working too. Help me out here, please.
How did you hide the numbers on the units' icons in the training building? I can't get seem to get rid of it. I tried using Train instead of Sell, but the training bar is a problem too.

And I saw this too. No meme can be laid undetected to the self-proclaimed Meme Overlord that is myself.
Logo_v3eeeee.png
 
Last edited:
Those units as actually being trained, but whenever the order is given to train them (training the unit is essentially passing the rawcode of the unit (eg 'hfoo' for footman) as an order), I tell the building to cancel. If cancelling doesn't work, you have to order cancel after a zero-second timer.

Once I'm able to catch the training order, I can then move on to activating/deactivating a custom queue.
 
Level 3
Joined
Mar 10, 2020
Messages
15
Added a little gimmick. My pigfarms (replacement for burrows) can actually train pigs. A pig costs 1 food and a couple coins and creates 2 food in return. Early fast and cheap food production but with a "hidden" cost. Since they take up 1 food out of the 2 they create, they do count against upkeep threshold and the unit cap.
This means your gonna have to slaughter them midgame and replace with regular pigfarms to get your army maxed out.
Alternatively, if you went the "spring of spirits" path, you'll have the option to morph them into a combat unit (a quilboar or something. Is still in development)
 
Level 22
Joined
Nov 4, 2010
Messages
6,232
Introducing... the Last Revolt

-- By @Directive255 and @Daffa






"When freedom fell as Void took reign, we shall stand till the bitter fall."


An entirely new faction joins the fray !

After an epic battle with Fallen's undead legions, a fatal defeat struck all of the elves. The Battle of Shadowspire was lost, and with Mind Zero acticated, nearly all of the world has now fallen under the dark lich's will...

But not all hope is lost.

A few months before the Battle of Shadowspire, one certain general from amongst the elven factions backed away to create an underground bastion on the farthest side of the planet. He has gathered all of the mightiest and smartest elves, building up a strong revolt against Fallen and his army of the dead. Some surviving humans and dwarves have also joined the ranks of the Revolt as well. By putting their technology and sorcery to the extreme, they have managed to resist Mind Zero.

Now, after decades of hiding and preparing, they are ready to avenge Shadowspire at all costs. They will not rest until Fallen and Mind Zero are put to an end, once and for all.​



Showcase for Today

Genesis Blade
BTNArmorGolem.png
  • Role: Anti-Land Unit
  • Attacks: Land Units; Buildings

  • Abilities:
    1. Broken Saber [Passive] :
      Reduces damage against buildings by 30%.
    2. Burning Shield [Active] [Requires Research] :
      Engulfs this unit in flames by burning its mana, harming nearby enemy land units.
    3. Crippling Blow [Passive] [Requires Research] :
      Gives a chance to do triple damage on an attack.

  • Produced At: Manufactory


Heavy humanoid robot armed with a powerful blade. Deals heavy damage to non-buildings. Can be upgraded with Burning Shield, allowing the Genesis Blade to activate fire armor harming nearby enemies on land. The blade can also be upgraded to give a chance dealing triple damage.

* * *

Blademage
141335-1459d5747c90624ff81e2f7f66a36c84.jpg
  • Role: Offensive Spellcaster
  • Attacks: Land Units; Buildings

  • Abilities:
    1. Nano Infection [Active] :
      Infects an enemy with a parasite that spawns one healing cloud on death.
    2. Revolution [Active] [Requires Research] :
      Grants critical chance to an allied unit over a long duration.
    3. Shocking Lightning [Passive] [Requires Research] :
      Upgrades the Blademage's base attack, and gives chance for area stun.

  • Produced At: Magi Sanitarium


Offensive spellcaster dressed in a technological suit. Initially has Nano Infection, which infects an enemy with a parasite that spawns healing cloud on death. Can also learn Revolution and Shocking Lightning.



Pros
  • Above-average durability
  • Extremely powerful troops
  • Mechanical advantage (most units are mechanical)
  • Use of Support Powers to turn the tides of battle
  • Multiple sources of income
  • Map-wide Tier Upgrades

Cons
  • Expensive and costly
  • Difficult food production (requires early tier upgrade/tech advance)
  • Intensive food usage
  • Slow training
  • Complicated early game
 
Last edited:
Just dropping some WIP on Death and Taxes:
  • Contractors now have a new look based on the Faceless one Mindflayer model.

  • Contractors now have an aura passive that makes Ghoulish Engineers and Retireable Shades take 60% less damage from all sources.

  • Abilities for this race now have a Damage Type Indicator. These range from Physical Damage, Magic Damage and Pure Damage

  • A new unit: Shadow-lector

    • Shadow-lector is a Tier 1 melee unit that can learn Gold Rush, and Shadow's Call.

      • Gold Rush is a point click ability that targets 3 random enemy units, dealing 25 Pure Damage and gaining (250*25/(target's max health)) gold and (200*25/(target's max health)) lumber per strike.

        Pure Damage ignores "whosyourdaddy".

      • Shadow's Call is Shadowmeld but at daytime. (Still working on this).
  • Contractor's New Look:
    WC3ScrnShot_031920_162748_02.png


  • Contractor's New Ability:
    WC3ScrnShot_031920_162752_03.png
 
Well I did promise the second WIP before, but I'm simply not finished with the aesthetic nor the actual techtree part of the faction.
I will try to do it this week, with the whole corona thing there's nothing else to do really...

But for now, I want to explain the basic premise of Emerald Nightmare and how it works:

 
Level 2
Joined
Mar 19, 2020
Messages
1
Hey everyone,

I'd like to participate, if entry is still possible.

I haven't touched the WE since 2005/06 but when I installed WC3 yesterday I found my way around it quickly.

First of all I have a question concerning the rules, does it have to be a total conversion, or can some units, which fit the theme be kept?

So my Idea was to have a "Horde" that more closely follows the likes of Mongols/Huns/Dothraki.
A lot of these elemts already exist as part of the Orcs, so I hope that idea is unique enough.

I'm still brainstorming, so nothing is fixed yet but currently I plan to implement the following:

General Mechanics:

Slave Worker:
The slaves (maybe elf-worker), work the mines and fell trees.
Due to slaves being slaves, they have some sort of detriment, like slower working rate or similar.

Raiders and Traders:
To compensate the weak slaves, the Horde, have traders. (Mongols had an excellent trading network)
Trader units earn ressources in regular intervals, however, the amount/rate depends on the traders distance to the main HQ.
Additionally or instead of, I am thinking resource generation through plunder (as raiders already do), however I feel that is hard to balance as a core mechanic-

Normads:
(The ideas comes from Age of Emires2)
When houses are destroyed, the player does not lose the food supply

Siege Engineers:

Catapults can "unpack"(root) which will remove the units food cost, turning them into defesive towers.
Tower can "pack"(unroot), if enough food is available and turin into a catapult.
Catapults can be trained as units and towers can be build by slaves.

Military Tactics: (Some could be Hero abilities)

Charge:
Melee units get a damage bonus for a few seconds when attacking, after being out of combat for a few seconds.
The bonus will depend on the units speed and armor.

Overrun:

Units get a speed bonus from adjacent friendly military units.

Hit and Run:
Horse archers can shoot while moving, when enough mana is available.

Steppe Lancers:
When moving, the unit has a chance to generate a temporary steppe, an ubersplat, that grants speed bonus to friendly military units.

Units:
All units except slaves,traders and siege units are mounted. I am thinking of keeping tauren, due to their "horseishness". -> maybe Tauren as second siege Unit
So far I only changed some models around and edited some a bit (Reteras Model Studio) to add Hero-Glow, or Team Colors.
I have no experience modeling, so I'm not sure how much I can do there.

Heroes:

Khan
Former Slave (won the trust of the Horde and is now a highranking general)
+
(Far Seer)
(Tauren Chieftain)



That's it so far, a lot of ideas, not so much done yet ;)

my first WIP

TheHordeProgress1.PNG
 
So I asked about the 100MB map limit, and I really should bring it up here, since I'm not the only one doing a Reforged entry.

There's nothing in the Reforged patch notes, but it would make sense for the file size to be way larger.

So I tested my test map with a some heavy music and .dds files: 248MB total.
It worked with a computer enemy perfectly in online mode.

Currently I'm at 105MB for my entry, I might go as high as 150MB, as there seems to be no issue with this file size.
 
Level 35
Joined
Feb 5, 2009
Messages
4,552
@Moonman I talked about it to @Mythic, and he has updated the post to current battle.net limitations (as they are known so far):

The map must be playable in battle net. (current max map size is 256 MB, for example)

Actually pretty glad you brought it up, I had no idea the map size limitation had been bumped up, although I thought it would be odd if it hadn't been.
 
Level 3
Joined
Mar 10, 2020
Messages
15
Weird as hell. Been spending 2 eves figuring out how to make my spell activate a trigger on a specific unit type without any luck. Random patch comes along and all of a sudden the damn thing just works!

Another one then that you guys may be able to help me with. I have a unit with a passive silence aura. Enemy unit shows the exclamation mark and when selected claims to have the silence debuff, yet still tbey cast purge without any problem.

Any idea what could cause this?
 
Weird as hell. Been spending 2 eves figuring out how to make my spell activate a trigger on a specific unit type without any luck. Random patch comes along and all of a sudden the damn thing just works!

Another one then that you guys may be able to help me with. I have a unit with a passive silence aura. Enemy unit shows the exclamation mark and when selected claims to have the silence debuff, yet still tbey cast purge without any problem.

Any idea what could cause this?

Well, we won't be able to help much if you don't show us the triggers.
 
Level 35
Joined
Feb 5, 2009
Messages
4,552
Ngl I don't even know how you'd get started on making a silence aura, but I do know that just because something shows the buff effect, doesn't necessarily mean that the ability effect is on the unit. Buffs are just things you put on to differentiate between ability effects, they aren't the ability's effect itself.
 

Kyrbi0

Arena Moderator
Level 44
Joined
Jul 29, 2008
Messages
9,487
A "Silence Aura" would have to be triggered in some way, as the Silence ability is merely active; there's no way to 'passively' have it's effects that I'm aware of.

However, it can be as simple as a dummy Aura (with the buff & SFX & all that), then a trigger that goes something like:
E - Unit casts a spell
C - Casting unit has buff 'Silence Aura'
A - Stop casting unit from casting.

It gets tricky with Autocast spells (the AI player might not know to un-autocast, so their Silenced Priests & such would stand around trying to cast constantly), but hey.
 
Well, if you're feeling adventurous: AuraSys - a flexible custom aura system
...or have a dummy unit constantly cast a 900 AoE Silence around your hero every second and have the buff last for around as long.

A silence aura sound really OP tho, if you ask me. Unless the aura is meant to be smaller and not meant to be permanently active?
 
Level 3
Joined
Mar 10, 2020
Messages
15
Indeed I did. Take aura, switch buff for silence debuff, change target friend to enemy.
It seemed a logical path but apparently the "buff" is little more then a hollow tag while the true silence mechanic is acted out in the background. Guess I have still a lot to learn about the basics of the mechanics behind the abilities. Will try again tonight with your advices. I'm at work now so won't be able to test any theories. It's an ability meant for a bodyguard unit, a dampener of sorts. It's not a hero. Maybe I should make it target both friend AND foe for balance reasons.
 
Level 3
Joined
Mar 10, 2020
Messages
15
Jeej! Got it to wor. XD I have a dummy unit spawn in every 1.5 seconds at the location of every Big Tusk (the bodyguard unit), cast the silence centered on themselves and then expire themselves.
Learned a lot and felt really dumb for mixing up "matching unit" for "picked unit". It now is a selfcentered periodically cast (de-)buff which effectively makes it into a pulsating aura with a lingering effect of approximately a second.

And yes, for balance reasons it dampens friends and foes equally but ignores heroes, as you suggested.
 
Last edited:
Jeej! Got it to wor. XD I have a dummy unit spawn in every 1.5 seconds at the location of every Big Tusk (the bodyguard unit), cast the silence centered on themselves and then expire themselves.
Learned a lot and felt really dumb for mixing up "matching unit" for "picked unit". It now is a selfcentered periodically cast (de-)buff which effectively makes it into a pulsating aura with a lingering effect of approximately a second.

And yes, for balance reasons it dampens friends and foes equally but ignores heroes, as you suggested.

Spawning a dummy unit every 1.5 seconds sounds like it's gonna lag the game out eventually, as every unit takes a few KB of memory, despite being dead or removed. You can optimize it, though.
You should start the timer only when a Big Tusk unit has entered the map. That way, there won't be any dummy unit being spawned too early and serving no actual purpose. Likewise, you should also disable the timer trigger when there is no alive Big Tusk unit in the map.

Anyways, I have done quite a lot of progress, but all I have to show is the 4th hero, Justicar's model. The model was created by Direfury and Stefan.K. I spent quite a lot of time applying the right textures and making him match the other Seraphic Union units thematically. He will be the Agility hero for the race.

WIP9.png
 
Level 3
Joined
Mar 10, 2020
Messages
15
Actually, that won't be necessary. It spawns a dummy for every Big Tusk it can find. This means, if there are no Big Tusks, it spawns 0 dummies. If I have spare time when all else is done, I'll see if I can make due with recycling just one dummy per big Tusk.
 

WIP3.png

Finally posting the 3rd WIP for my entry. Assuming that some here might not have
Reforged, I have to ¨spoil¨ the features, instead of surprising you. This WIP's focus
are aesthetics. Next and last one will have the actual tech-tree (units and abilities).


Inspiration
full


The idea of the Emerald Nightmare is taken directly from the official Warcraft lore.
The Nightmare was mentioned and appeared
throughout most WoW expansions.
Its full manifestation was best seen in the Legion expansion's Raids and Dungeons.

The Nightmare represents the Old God corrupted Emerald Dream (a realm of nature magic).
In the heart of this corruption lies Il'gynoth a creation of the Old Gods. Your goal will be to
spread the Nightmare beyond the Dream by corrupting your enemies and Azeroth herself...

EM 1.jpg EM 2.jpg EM 3.jpg EM 4.jpg


Features
full


My goal here is to make the faction as if Blizzard added it themselves. This means
pretty much every unit and icon will be custom made. I'm also sticking close to the
lore, adding music, changing the UI etc. There will be some spooky sound effects and
minor changes to the map
(only aesthetic, so the actual terrain won't be altered).



Models, Textures
full


I went for Blizzard's colour scheme: dark grey bark, glowing red to orange vines, a hint of purple
here and there. Additionally the Nightmare Cenarius model that comes with Reforged pre-purchase

(ye, ye I know...) has green leather and off-colour blue leaves, this adds come much needed colour.

Grove Defiler.jpg Ancient of Madness.jpg Den of Horrors.jpg Dream Crusher.jpg
Nightmare Dragon.jpg Druid Tree Form.jpg Satyr Trickster.jpg Druid of the Swarm.jpg
UI Preview.jpg

 
More updates on Death and Taxes.

  • The Thanatacolyte is a Tier 1 ranged unit that can instantly kill enemy units under 7% hit points. However, this ability comes at a cost, the Thanatacolyte might betray you if you're not careful. She is a paid assassin, after all.
    • The bar display will have some slight logical adjustments in the final version, such as not showing for other players.
  • The First Death and Taxes hero (or Villain), the Necrovizier
    • Currently working on his skills, the Necrovizier right now is a glorified ranged unit.
  • Made some slight adjustments to gathering gold and lumber.
    Now, gold collected from Mining Facilities will be worth 20/25/30.
    Lumber harvested from tress will be worth 20/35/50.

  • Finalized the Custom Race System (in Lua). Now, when the system has detected that the race a player belongs in has at least 2 factions, the system will automatically pause the game and display a dialog allowing said player to choose. Once all players have selected their factions, the game will automatically unpause.
    • However, the system has not (yet) considered the case when a player, who is made to select a faction, leaves.
    • Computer players will automatically choose the default race.

Will upload more footage on the Custom Race System, and might decide on adding a UI element to be displayed when a player has selected his or her choice.

Core Library:

Initialization Handling

System Setup


Lua:
--[[
    When using CustomMelee.add_race_setup, setupfunc expects 5 parameters:
        (whichPlayer, startLoc, doHeroes, doCamera, doPreload)
]]

CustomMelee = setmetatable({}, protected_t())
do
    local l_melee           = getmetatable(CustomMelee)
    local race_cond         = {}
    local player_fact       = {}
    local hero_list         = {index={}, race={}}
    local dialogs           = {players={}, playerPos={}}
    local town_hall_ids     = {index={}}
    local race_name         = {
        [RACE_HUMAN]        = "Human",
        [RACE_ORC]          = "Orc",
        [RACE_UNDEAD]       = "Undead",
        [RACE_NIGHTELF]     = "Night Elf",
        [RACE_DEMON]        = "Demon",
        [RACE_OTHER]        = "Other",
    }
    l_melee.race_cond       = race_cond
    l_melee.race_name       = race_name
    l_melee.player_fact     = player_fact
    l_melee.hero_list       = hero_list
    l_melee.dialogs         = dialogs
    l_melee.town_hall_ids   = town_hall_ids

    local function printAfter(msg)
        Initializer.register(function()
            doAfter(0.00, function() print(msg) end)
        end)
    end
    local function get_random_hero(whichplayer, heroLoc, race, index)
        local v = VersionGet()
        local roll
        if (v == VERSION_REIGN_OF_CHAOS) then
            roll = GetRandomInt(1,IMinBJ(3, #hero_list.race[race][index]))
        else
            roll = GetRandomInt(1,#hero_list.race[race][index])
        end
   
        -- Translate the roll into a unitid.
        local pick = hero_list.race[race][index][roll]
        local hero = CreateUnitAtLoc(whichplayer, pick, heroLoc, bj_UNIT_FACING)
        if bj_meleeGrantHeroItems then
            if (bj_meleeTwinkedHeroes[owner] < bj_MELEE_MAX_TWINKED_HEROES) then
                UnitAddItemById(hero, 'stwp')
                bj_meleeTwinkedHeroes[owner] = bj_meleeTwinkedHeroes[owner] + 1
            end
        end
        return hero
    end

    function l_melee.addHeroId(heroId, race, index)
        --  Default value for index
        index = index or 1
        if type(heroId) == "string" then heroId = FourCC(heroId) end
        if not hero_list.race[race] then
            hero_list.race[race] = {}
        end
        if not hero_list.race[race][index] then
            hero_list.race[race][index] = {}
        end
        if not hero_list.index[heroId] then
            hero_list[#hero_list + 1]   = heroId
            hero_list.index[heroId]     = #hero_list

            local i                         = #hero_list.race[race][index] + 1
            hero_list.race[race][index][i]  = heroId
        end
    end
    function l_melee.addRaceFaction(race, factionname)
        if type(factionname) ~= 'string' then return 0 end
        if not race_cond[race] then
            race_cond[race] = {index={}, setupfunc={}, melee_ai={}}
        end
        if not race_cond[race].index[factionname] then
            race_cond[race][#race_cond[race] + 1]   = factionname
            race_cond[race].index[factionname]      = #race_cond[race]
        end
        return race_cond[race].index[factionname]
    end
    function l_melee.raceFactionSetupById(race, index, setupfunc)
        if not is_function(setupfunc) then return end
        if not race_cond[race][index] then
            return 0
        end
        race_cond[race].setupfunc[index] = setupfunc
        return index
    end
    function l_melee.raceFactionSetup(race, factionname, setupfunc)
        return l_melee.raceFactionSetupById(race, race_cond[race].index[factionname] or 0, setupfunc)
    end
    function l_melee.setRaceFactionAI(race, factionname, melee_ai)
        if type(melee_ai) ~= 'string' then return end
        local index = race_cond[race].index[factionname] or 0
        if index ~= 0 then
            race_cond[race].melee_ai[index]     = melee_ai
        end
    end
    function l_melee.finalizeSetupFunction(race, factionname, func)
        if not is_function(func) then return DoNothing end
        if not race_cond[race] then
            printAfter("CustomMelee.finalizeSetupFunction >> Race Conditions Failed.")
            return DoNothing
        end
        local index = race_cond[race].index[factionname] or 0
        if index == 0 then
            printAfter("CustomMelee.finalizeSetupFunction >> Faction " .. factionname .. " does not exist for race: " .. race_name[race])
            return DoNothing
        end
        local f = function(whichplayer, startloc, doheroes, docamera, dopreload)
            local randomFlag            = IsMapFlagSet(MAP_RANDOM_HERO)
            local peonX, peonY, heroLoc = func(whichplayer, startloc, doheroes, docamera, dopreload)
            if doheroes then
                if randomFlag then
                    get_random_hero(whichplayer, heroLoc, race, index)
                else
                    SetPlayerState(whichplayer, PLAYER_STATE_RESOURCE_HERO_TOKENS, bj_MELEE_STARTING_HERO_TOKENS)
                end
            end
            if (docamera) then
                -- Center the camera on the initial Peasants.
                SetCameraPositionForPlayer(whichplayer, peonX, peonY)
                SetCameraQuickPositionForPlayer(whichplayer, peonX, peonY)
            end
            RemoveLocation(heroLoc)
        end
        return f
    end
    function l_melee.addTownHallId(...)
        local ids, j = {}, select('#', ...)
        if j > 0 then
            for i = 1,j do
                ids[i] = select(i, ...)
                ids[i] = ((type(ids[i]) == 'string') and FourCC(ids[i])) or ids[i]
            end
        end
        for i = 1,j do
            if not town_hall_ids.index[ids[i]] then
                town_hall_ids[#town_hall_ids + 1]   = ids[i]
                town_hall_ids.index[ids[i]]         = #town_hall_ids
            end
        end
    end
    function l_melee.removeTownHallId(...)
        local ids, j = {}, select('#', ...)
        --  Assume that the parameters passed are raw codes.
        if j > 0 then
            for i = 1,j do
                ids[i] = select(i, ...)
                ids[i] = ((type(ids[i]) == 'string') and FourCC(ids[i])) or ids[i]
            end
        end
        --  Begin removal (Do not sort here).
        local size, removed = #town_hall_ids, 0
        for i = 1,j do
            if town_hall_ids.index[ids[i]] then
                local k = town_hall_ids.index[ids[i]]
                town_hall_ids[k]            = nil
                town_hall_ids.index[ids[i]] = nil
                removed                     = removed + 1
            end
        end
        --  Sort the array (This should ideally be O(n), since the order of the registered ids is not
        --  based on comparisons).
        do
            local j = 1
            for i = 1,size do
                if not town_hall_ids[i] then
                    while not town_hall_ids[i + j] do
                        if i + j > size then break end
                        j = j + 1
                    end
                    town_hall_ids[i]                        = town_hall_ids[i + j]
                    town_hall_ids.index[town_hall_ids[i]]   = i
                    town_hall_ids[i + j]                    = nil
                end
            end
        end
    end
end


(The UnitDex portions of the code can be commented.)
Lua:
do
    local funcs             = {}
    local l_melee           = getmetatable(CustomMelee)
    local race_cond         = l_melee.race_cond
    local player_fact       = l_melee.player_fact
    local hero_list         = l_melee.hero_list
    local dialogs           = l_melee.dialogs
    local race_name         = l_melee.race_name
    local town_hall_ids     = l_melee.town_hall_ids
    dialogs.max_players     = 0
    l_melee.__metatable     = CustomMelee

    --  Special Dialog functions
    function funcs.queuePlayerForDialog(p, race, indexStartLoc, doheroes, docamera, dopreload)
        dialogs.players[#dialogs.players + 1]   = {p, race, indexStartLoc, __mode="kv"}
        dialogs.playerPos                    = #dialogs.players
        if not dialogs[race] then
            dialogs[race]       = {choice={}, listener=CreateTrigger(), choiceMap={}}
            dialogs[race].main  = DialogCreate()
            doAfter(0.00, function()
                TriggerRegisterDialogEvent(dialogs[race].listener, dialogs[race].main)
                TriggerAddCondition(dialogs[race].listener, Condition(function()
                    local p             = GetTriggerPlayer()
                    local button        = GetClickedButton()
                    local i             = dialogs.playerPos
                    local race          = dialogs.players[i][2]
                    local indexStartLoc = dialogs.players[i][3]
                    local faction       = dialogs[race].choiceMap[button]
   
                    DialogDisplay(p, dialogs[race].main, false)
                    player_fact[GetPlayerId(p)] = faction
                    race_cond[race].setupfunc[faction](p, indexStartLoc, true, true, true)
                    RemoveLocation(indexStartLoc)

                    while i < #dialogs.players do
                        local p2                                = dialogs.players[i + 1][1]
                        dialogs.players[i]                      = dialogs.players[i + 1]
                        dialogs.playerPos[p2]                   = i
                        i = i + 1
                    end
                    dialogs.playerPos    = nil
                    dialogs.players[i]      = nil
                   
                    if #dialogs.players <= 0 then
                        if dialogs.max_players > 1 then
                            PauseGame(false)
                        end
                    end
                end))   
                DialogClear(dialogs[race].main)
                DialogSetMessage(dialogs[race].main, "Select " .. race_name[race] .. " Faction")
                for index=1,#race_cond[race] do
                    local button = DialogAddButton(dialogs[race].main, race_cond[race][index], 0)
                    dialogs[race].choice[index]     = button
                    dialogs[race].choiceMap[button] = index
                end
            end)
        end
        doAfter(0.00, function()
            DialogDisplay(p, dialogs[race].main, true)
        end)
    end

    --  Melee-only mimic functions.
    function funcs.starting_vis()
        SetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
    end
    function funcs.starting_hero_limit()
        local p
        for player_id = 0,bj_MAX_PLAYERS-1 do
            p = Player(player_id)
            -- SetPlayerMaxHeroesAllowed(bj_MELEE_HERO_LIMIT, p)
            SetPlayerTechMaxAllowed(p, FourCC('HERO'), bj_MELEE_HERO_LIMIT)

            -- ReducePlayerTechMaxAllowed(p, <heroType>, bj_MELEE_HERO_TYPE_LIMIT)
            for index = 1,#hero_list do
                SetPlayerTechMaxAllowed(p, hero_list[index], bj_MELEE_HERO_TYPE_LIMIT)
            end
        end
    end
    function funcs.grant_hero_items()
        local t = CreateTrigger()
        for player_id = 0, bj_MAX_PLAYER_SLOTS-1 do
            bj_meleeTwinkedHeroes[player_id] = 0

            if player_id < bj_MAX_PLAYERS then
                TriggerRegisterPlayerUnitEvent(t, Player(player_id), EVENT_PLAYER_UNIT_TRAIN_FINISH, nil)
            end
        end
        TriggerAddCondition(t, Filter(function()
            local u     = GetTriggerUnit()
            if IsUnitType(u, UNIT_TYPE_HERO) then
                local owner = GetPlayerId(GetOwningPlayer(u))
                -- If we haven't twinked N heroes for this player yet, twink away.
                if (bj_meleeTwinkedHeroes[owner] < bj_MELEE_MAX_TWINKED_HEROES) then
                    UnitAddItemById(u, 'stwp')
                    bj_meleeTwinkedHeroes[owner] = bj_meleeTwinkedHeroes[owner] + 1
                end
            end
        end))
    end
    function funcs.starting_resources()
        local startingGold
        local startingLumber
        local v = VersionGet()

        if (v == VERSION_REIGN_OF_CHAOS) then
            startingGold, startingLumber = bj_MELEE_STARTING_GOLD_V0, bj_MELEE_STARTING_LUMBER_V0
        else
            startingGold, startingLumber = bj_MELEE_STARTING_GOLD_V1, bj_MELEE_STARTING_LUMBER_V1
        end

        local p
        for player_id = 0,bj_MAX_PLAYERS-1 do
            p = Player(player_id)
            if (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
                SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, startingGold)
                SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, startingLumber)
            end
        end
    end
    function funcs.clear_units()
        local p
        local loc
        local locX
        local locY
        local g     = CreateGroup()
        for player_id = 0,bj_MAX_PLAYERS-1 do
            p = Player(player_id)
            if (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
                loc         = GetPlayerStartLocation(p)
                locX, locY  = GetStartLocationX(loc), GetStartLocationY(loc)

                GroupEnumUnitsInRange(g, locX, locY, bj_MELEE_CLEAR_UNITS_RADIUS, nil)
                local enum_unit = FirstOfGroup(g)
                while enum_unit do
                    GroupRemoveUnit(g, enum_unit)
                    if GetOwningPlayer(enum_unit) == Player(PLAYER_NEUTRAL_AGGRESSIVE) then
                        RemoveUnit(enum_unit)
                    elseif (GetOwningPlayer(enum_unit) == Player(PLAYER_NEUTRAL_AGGRESSIVE)) and
                            not IsUnitType(enum_unit, UNIT_TYPE_STRUCTURE) then
                        RemoveUnit(enum_unit)
                    end
                    enum_unit = FirstOfGroup(g)
                end
            end
        end
        DestroyGroup(g)
    end
    function funcs.starting_units()
        local p
        for player_id = 0, bj_MAX_PLAYERS-1 do
            p = Player(player_id)
            if (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
                local indexStartLoc, indexRace = GetStartLocationLoc(GetPlayerStartLocation(p)), GetPlayerRace(p)
                -- Create initial race-specific starting units
                if #race_cond[indexRace] == 1 then
                    -- Check if the race has only one layout (or faction)
                    player_fact[player_id]  = 1
                    race_cond[indexRace].setupfunc[1](p, indexStartLoc, true, true, true)
                    RemoveLocation(indexStartLoc)

                    if GetPlayerController(p) == MAP_CONTROL_USER then
                        dialogs.max_players = dialogs.max_players + 1
                    end
                else
                    -- Computer units select normal race units by default.
                    if GetPlayerController(p) == MAP_CONTROL_COMPUTER then
                        player_fact[player_id]  = 1
                        race_cond[indexRace].setupfunc[1](p, indexStartLoc, true, true, true)
                        RemoveLocation(indexStartLoc)
                    elseif GetPlayerController(p) == MAP_CONTROL_USER then
                        --  Setup a Dialog System here.
                        --  If greater than 5, revamp using UI natives.
                        funcs.queuePlayerForDialog(p, indexRace, indexStartLoc, true, true, true)
                        dialogs.max_players = dialogs.max_players + 1
                    end
                end
            end
        end
        if #dialogs.players > 0 then
            if dialogs.max_players > 1 then
                doAfter(0.00, function()
                    PauseGame(true)
                end)
            end
        end
    end
    function funcs.starting_ai()
        for player_id = 0,bj_MAX_PLAYERS-1 do
            local indexPlayer = Player(player_id)
            if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
                local indexRace = GetPlayerRace(indexPlayer)
                if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
                    -- Run a race-specific melee AI script.
                    StartMeleeAI(indexPlayer, race_cond[indexRace].melee_ai[1])
                    if indexRace == RACE_UNDEAD then
                        RecycleGuardPosition(bj_ghoul[player_id])
                    end
                    ShareEverythingWithTeamAI(indexPlayer)
                end
            end
        end
    end
    function funcs.victory_defeat()
        local trig
        local triggerList       = {CreateTrigger(), CreateTrigger(), CreateTrigger(), CreateTrigger(), CreateTrigger(), CreateTrigger()}
        local 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)
        bj_finishSoonTimerDialog = CreateTimerDialog(nil)

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

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

        do
            local ENUM_GROUP = CreateGroup()
            local function CheckForVictors()
                local opponentlessPlayers   = CreateForce()
                local gameOver              = false
           
                -- Check to see if any players have opponents remaining.
                for playerIndex = 0, bj_MAX_PLAYERS - 1 do
                    if (not bj_meleeDefeated[playerIndex]) then
                        -- Determine whether or not this player has any remaining opponents.
                        for opponentIndex = 0, bj_MAX_PLAYERS - 1 do
                            -- If anyone has an opponent, noone can be victorious yet.
                            if MeleePlayerIsOpponent(playerIndex, opponentIndex) then
                                DestroyForce(opponentlessPlayers)
                                return CreateForce()
                            end
                        end
           
                        --[[
                        // Keep track of each opponentless player so that we can give
                        // them a victory later.
                        ]]
                        ForceAddPlayer(opponentlessPlayers, Player(playerIndex))
                        gameOver = true
                    end           
                end
                -- Set the game over global flag
                bj_meleeGameOver = gameOver
                return opponentlessPlayers
            end
            local function GetAllyKeyStructureCount(whichPlayer)
                local keyStructs  = 0
                -- Count the number of buildings controlled by all not-yet-defeated co-allies.
                for playerIndex = 0,bj_MAX_PLAYERS - 1 do
                    local indexPlayer = Player(playerIndex)
                    if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
                        GroupEnumUnitsOfPlayer(ENUM_GROUP, indexPlayer, Filter(function()
                            return type(town_hall_ids.index[GetUnitTypeId(GetFilterUnit())]) ~= 'nil'
                        end))
                        keyStructs     = keyStructs + BlzGroupGetSize(ENUM_GROUP)
                    end
                end
                return keyStructs
            end
            local function PlayerIsCrippled(player)
                -- Dead teams are not considered to be crippled.
                return (MeleeGetAllyStructureCount(player) > 0) and (GetAllyKeyStructureCount(player) <= 0)
            end
            local function CheckForCrippledPlayers()
                local crippledPlayers = CreateForce()
           
                -- The "finish soon" exposure of all players overrides any "crippled" exposure
                if bj_finishSoonAllExposed then
                    return
                end
           
                -- Check each player to see if he or she has been crippled or uncrippled.
                for playerIndex = 0,bj_MAX_PLAYERS - 1 do
                    local indexPlayer = Player(playerIndex)
                    local isNowCrippled = MeleePlayerIsCrippled(indexPlayer)
           
                    if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
           
                        -- Player became crippled; start their cripple timer.
                        bj_playerIsCrippled[playerIndex] = true
                         TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, MeleeCrippledPlayerTimeout)
           
                        if (GetLocalPlayer() == indexPlayer) then
                            -- Use only local code (no net traffic) within this block to avoid desyncs.
                            -- Show the timer window.
                            TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
                            -- Display a warning message.
                            DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, MeleeGetCrippledWarningMessage(indexPlayer))
                        end
                    elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
                        -- Player became uncrippled; stop their cripple timer.
                        bj_playerIsCrippled[playerIndex] = false
                        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.
                            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
                                    DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
                                else
                                    DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
                                end
                            end
                        end
           
                        -- If the player granted shared vision, deny that vision now.
                        MeleeExposePlayer(indexPlayer, false)           
                    end
                end
            end
            local function CheckForLosersAndVictors()
                local defeatedPlayers   = CreateForce()
                local victoriousPlayers

                -- If the game is already over, do nothing
                if (bj_meleeGameOver) then
                    return
                end

                --[[
                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
                    bj_meleeGameOver = true
                    return
                end

                -- Check each player to see if he or she has been defeated yet.
                for playerIndex = 0,bj_MAX_PLAYERS - 1 do
                    local indexPlayer = Player(playerIndex)
                    if (not bj_meleeDefeated[playerIndex] and not bj_meleeVictoried[playerIndex]) then
                        -- 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.
                            ForceAddPlayer(defeatedPlayers, Player(playerIndex))
                            -- Set their defeated flag now so MeleeCheckForVictors
                            -- can detect victors.
                            bj_meleeDefeated[playerIndex] = true
                        end
                    end
                end
                -- Now that the defeated flags are set, check if there are any victors
                victoriousPlayers = CheckForVictors()

                -- Defeat all defeated players
                ForForce(defeatedPlayers, MeleeDoDefeatEnum)
                -- Give victory to all victorious players
                ForForce(victoriousPlayers, MeleeDoVictoryEnum)

                DestroyForce(defeatedPlayers)
                DestroyForce(victoriousPlayers)
                -- If the game is over we should remove all observers
                if (bj_meleeGameOver) then
                    MeleeRemoveObservers()
                end
            end
            local function CheckLostUnit(whichunit)
                local lostUnitOwner = GetOwningPlayer(lostUnit)
                if (GetPlayerStructureCount(lostUnitOwner, true) <= 0) then
                    CheckForLosersAndVictors()
                end
                --[[
                // Check if the lost unit has crippled or uncrippled the player.
                // (A team with 0 units is dead, and thus considered uncrippled.)
                ]]
                CheckForCrippledPlayers()
            end
            local function CheckAddedUnit(whichunit)
                local addedUnitOwner = GetOwningPlayer(addedUnit)

                -- If the player was crippled, this unit may have uncrippled him/her.
                if (bj_playerIsCrippled[GetPlayerId(addedUnitOwner)]) then
                    CheckForCrippledPlayers()
                end
            end

            TriggerAddAction(triggerList[1], function()
                CheckLostUnit(GetCancelledStructure())
            end)
            TriggerAddAction(triggerList[2], function()
                if (IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE)) then
                    CheckLostUnit(GetTriggerUnit())
                end
            end)
            TriggerAddAction(triggerList[3], function()
                CheckAddedUnit(GetConstructingStructure())
            end)
            TriggerAddAction(triggerList[4], function()
                local thePlayer = GetTriggerPlayer()
                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.
                    ShareEverythingWithTeam(thePlayer)
                    if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
                        MeleeDoDefeat(thePlayer)
                    end
                else
                    -- If no living allies remain, swap all units and buildings over to
                    -- neutral_passive and proceed with death.
                    -- MakeUnitsPassiveForTeam(thePlayer)
                    if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
                        -- MeleeDoDefeat(thePlayer)
                    end
                end
                CheckForLosersAndVictors()
            end)
            TriggerAddAction(triggerList[5], function()
                local thePlayer = GetTriggerPlayer()
                -- Just show game over for observers when they leave
                if (IsPlayerObserver(thePlayer)) then
                    RemovePlayerPreserveUnitsBJ(thePlayer, PLAYER_GAME_RESULT_NEUTRAL, false)
                    return
                end
                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.
                    ShareEverythingWithTeam(thePlayer)
                    MeleeDoLeave(thePlayer)
                else
                    -- If no living allies remain, swap all units and buildings over to
                    -- neutral_passive and proceed with death.
                    MakeUnitsPassiveForTeam(thePlayer)
                    MeleeDoLeave(thePlayer)
                end
                CheckForLosersAndVictors()
            end)
            TriggerAddAction(triggerList[6], function()
                MeleeCheckForLosersAndVictors()
                MeleeCheckForCrippledPlayers()
            end)
        end

        -- Set up each player's mortality code.
        for index=0,bj_MAX_PLAYERS-1 do
            local indexPlayer = Player(index)

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

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

                -- Set a trigger to fire whenever a building is cancelled for this player.
                TriggerRegisterPlayerUnitEvent(triggerList[1], indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)

                -- Set a trigger to fire whenever a unit dies for this player.
                TriggerRegisterPlayerUnitEvent(triggerList[2], indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)

                -- Set a trigger to fire whenever a unit begins construction for this player
                TriggerRegisterPlayerUnitEvent(triggerList[3], indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
               
                -- Set a trigger to fire whenever this player defeats-out
                TriggerRegisterPlayerEvent(triggerList[4], indexPlayer, EVENT_PLAYER_DEFEAT)

                -- Set a trigger to fire whenever this player leaves
                TriggerRegisterPlayerEvent(triggerList[5], indexPlayer, EVENT_PLAYER_LEAVE)

                -- Set a trigger to fire whenever this player changes his/her alliances.
                TriggerRegisterPlayerAllianceChange(triggerList[6], indexPlayer, ALLIANCE_PASSIVE)
                TriggerRegisterPlayerStateEvent(triggerList[6], indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
            else
                bj_meleeDefeated[index] = true
                bj_meleeVictoried[index] = false

                -- Handle leave events for observers
                if (IsPlayerObserver(indexPlayer)) then
                    -- Set a trigger to fire whenever this player leaves
                    TriggerRegisterPlayerEvent(triggerList[6], indexPlayer, EVENT_PLAYER_LEAVE)
                    TriggerAddAction(triggerList[6], MeleeTriggerActionPlayerLeft)
                end
            end
        end
        UnitDex.register("ENTER_EVENT", function()
            if IsUnitType(UnitDex.eventUnit, UNIT_TYPE_STRUCTURE) then
                CheckAddedUnit(UnitDex.eventUnit)
            end
        end)
        UnitDex.register("LEAVE_EVENT", function()
            if IsUnitType(UnitDex.eventUnit, UNIT_TYPE_STRUCTURE) then
                CheckLostUnit(UnitDex.eventUnit)
            end
        end)
        -- 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.
        doAfter(2.0, false, MeleeTriggerActionAllianceChange)
    end

    function l_melee.initialization()
        local result = {}
        funcs.starting_vis()
        funcs.starting_hero_limit()
        funcs.grant_hero_items()
        funcs.starting_resources()
        funcs.clear_units()
        funcs.starting_units()
        funcs.starting_ai()
        funcs.victory_defeat()
    end
end

Lua:
CustomMeleeSetup = {
    unitSpacing = 64.00,
    minTreeDist = 3.50,
    minWispDist = 1.75,
}
do
    CustomMelee.addTownHallId('htow', 'hkee', 'hcas',
                              'ogre', 'ostr', 'ofrt',
                              'unpl', 'unp1', 'unp2',
                              'etol', 'etoa', 'etoe')
    --  Starting Base Layouts.
    local funcs     = CustomMeleeSetup

    funcs.human_id      = CustomMelee.addRaceFaction(RACE_HUMAN, "Alliance")
    funcs.orc_id        = CustomMelee.addRaceFaction(RACE_ORC, "Horde")
    funcs.undead_id     = CustomMelee.addRaceFaction(RACE_UNDEAD, "Scourge")
    funcs.elf_id        = CustomMelee.addRaceFaction(RACE_NIGHTELF, "Sentinel")
    funcs.demon_id      = CustomMelee.addRaceFaction(RACE_DEMON, "normal")
    funcs.other_id      = CustomMelee.addRaceFaction(RACE_OTHER, "normal")
   
    funcs.def       = function(whichplayer, startloc, hallId, peonId)
        if type(hallId) == 'string' then hallId = FourCC(hallId) end
        if type(peonId) == 'string' then peonId = FourCC(peonId) end
       
        local nearestMine = MeleeFindNearestMine(startloc, bj_MELEE_MINE_SEARCH_RADIUS)
        local peonX
        local peonY
        local heroLoc
        local townHall
        if (nearestMine ~= nil) then
        --   Spawn Town Hall at the start location.
            townHall            = CreateUnitAtLoc(whichplayer, hallId, startloc, bj_UNIT_FACING)
       
            -- Spawn Peasants near the mine.
            local mineLoc       = GetUnitLoc(nearestMine)
            local nearMineLoc   = MeleeGetProjectedLoc(mineLoc, startloc, 320, 0)
            peonX, peonY        = GetLocationX(nearMineLoc), GetLocationY(nearMineLoc)

            CreateUnit(whichplayer, peonId, peonX + 0.00 * funcs.unitSpacing, peonY + 1.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX + 1.00 * funcs.unitSpacing, peonY + 0.15 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX - 1.00 * funcs.unitSpacing, peonY + 0.15 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX + 0.60 * funcs.unitSpacing, peonY - 1.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX - 0.60 * funcs.unitSpacing, peonY - 1.00 * funcs.unitSpacing, bj_UNIT_FACING)

            -- Set random hero spawn point to be off to the side of the start location.
            heroLoc = MeleeGetProjectedLoc(mineLoc, startloc, 384, 45)
            RemoveLocation(mineLoc)
            RemoveLocation(nearMineLoc)
        else
            -- Spawn Town Hall at the start location.
            townHall = CreateUnitAtLoc(whichplayer, hallId, startloc, bj_UNIT_FACING)
       
            -- Spawn Peasants directly south of the town hall.
            peonX = GetLocationX(startloc)
            peonY = GetLocationY(startloc) - 224.00
            CreateUnit(whichplayer, peonId, peonX + 2.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX + 1.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX + 0.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX - 1.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, peonId, peonX - 2.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)

            -- Set random hero spawn point to be just south of the start location.
            heroLoc = Location(peonX, peonY - 2.00 * funcs.unitSpacing)
        end
        return peonX, peonY, heroLoc, townHall, nearestMine
    end
    funcs.def_human = function(whichplayer, startloc, doheroes, docamera, dopreload)
        if (dopreload) then
            Preloader("scripts\\HumanMelee.pld")
        end
        local peonX, peonY, heroLoc, townHall = funcs.def(whichplayer, startloc, 'htow', 'hpea')
        if (townHall ~= nil) then
            UnitAddAbility(townHall, FourCC('Amic'))
            UnitMakeAbilityPermanent(townHall, true, FourCC('Amic'))
        end
        return peonX, peonY, heroLoc
    end
    funcs.def_orc   = function(whichplayer, startloc, doheroes, docamera, dopreload)
        if (dopreload) then
            Preloader("scripts\\OrcMelee.pld")
        end
        local peonX, peonY, heroLoc = funcs.def(whichplayer, startloc, 'ogre', 'opeo')
        return peonX, peonY, heroLoc
    end
    funcs.def_nightelf  = function(whichplayer, startloc, doheroes, docamera, dopreload)
        if (dopreload) then
            Preloader( "scripts\\NightElfMelee.pld" )
        end
        local townhall
        local nearestMine = MeleeFindNearestMine(startloc, bj_MELEE_MINE_SEARCH_RADIUS)
        local heroLoc
        local peonX
        local peonY
        if (nearestMine ~= nil) then
            -- Spawn Tree of Life near the mine and have it entangle the mine.
            -- Project the Tree's coordinates from the gold mine, and then snap
            -- the X and Y values to within minTreeDist of the Gold Mine.
            local mineLoc       = GetUnitLoc(nearestMine)
            local nearMineLoc   = MeleeGetProjectedLoc(mineLoc, startloc, 650, 0)
            nearMineLoc         = MeleeGetLocWithinRect(nearMineLoc, GetRectFromCircleBJ(mineLoc, funcs.minTreeDist))

            townhall            = CreateUnitAtLoc(whichplayer, FourCC('etol'), nearMineLoc, bj_UNIT_FACING)
            IssueTargetOrder(townhall, "entangleinstant", nearestMine)
   
            -- Spawn Wisps at the start location.
            local wispLoc   = MeleeGetProjectedLoc(mineLoc, startloc, 280, 0)
            wispLoc         = MeleeGetLocWithinRect(wispLoc, GetRectFromCircleBJ(mineLoc, funcs.minWispDist))
            peonX           = GetLocationX(wispLoc)
            peonY           = GetLocationY(wispLoc)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 0.00 * funcs.unitSpacing, peonY + 1.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 1.00 * funcs.unitSpacing, peonY + 0.15 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX - 1.00 * funcs.unitSpacing, peonY + 0.15 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 0.58 * funcs.unitSpacing, peonY - 1.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX - 0.58 * funcs.unitSpacing, peonY - 1.00 * funcs.unitSpacing, bj_UNIT_FACING)
   
            -- Set random hero spawn point to be off to the side of the start location.
            heroLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine), startloc, 384, 45)
        else
            -- Spawn Tree of Life at the start location.
            CreateUnitAtLoc(whichplayer, FourCC('etol'), startloc, bj_UNIT_FACING)
   
            -- Spawn Wisps directly south of the town hall.
            peonX = GetLocationX(startloc)
            peonY = GetLocationY(startloc) - 224.00
            CreateUnit(whichplayer, FourCC('ewsp'), peonX - 2.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX - 1.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 0.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 1.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ewsp'), peonX + 2.00 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
   
            -- Set random hero spawn point to be just south of the start location.
            heroLoc = Location(peonX, peonY - 2.00 * funcs.unitSpacing)
        end
        return peonX, peonY, heroLoc
    end
    funcs.def_undead    = function(whichplayer, startloc, doheroes, docamera, dopreload)
        if (dopreload) then
            Preloader("scripts\\UndeadMelee.pld")
        end
        local peonX, peonY, heroLoc
        local nearestMine = MeleeFindNearestMine(startloc, bj_MELEE_MINE_SEARCH_RADIUS)
        if (nearestMine ~= nil) then
            -- Spawn Necropolis at the start location.
            CreateUnitAtLoc(whichplayer, FourCC('unpl'), startloc, bj_UNIT_FACING)
           
            -- Replace the nearest gold mine with a blighted version.
            nearestMine     = BlightGoldMineForPlayerBJ(nearestMine, whichplayer)
            local mineLoc   = GetUnitLoc(nearestMine)
            -- Spawn Ghoul near the Necropolis.
            nearTownLoc = MeleeGetProjectedLoc(startloc, mineLoc, 288, 0)
            ghoulX      = GetLocationX(nearTownLoc)
            ghoulY      = GetLocationY(nearTownLoc)
            bj_ghoul[GetPlayerId(whichplayer)] = CreateUnit(whichplayer, FourCC('ugho'), ghoulX + 0.00 * funcs.unitSpacing, ghoulY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)

            -- Spawn Acolytes near the mine.
            nearMineLoc = MeleeGetProjectedLoc(mineLoc, startloc, 320, 0)
            peonX       = GetLocationX(nearMineLoc)
            peonY       = GetLocationY(nearMineLoc)
            CreateUnit(whichplayer, FourCC('uaco'), peonX + 0.00 * funcs.unitSpacing, peonY + 0.50 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('uaco'), peonX + 0.65 * funcs.unitSpacing, peonY - 0.50 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('uaco'), peonX - 0.65 * funcs.unitSpacing, peonY - 0.50 * funcs.unitSpacing, bj_UNIT_FACING)

            -- Create a patch of blight around the gold mine.
            SetBlightLoc(whichplayer,nearMineLoc, 768, true)

            -- Set random hero spawn point to be off to the side of the start location.
            heroLoc = MeleeGetProjectedLoc(mineLoc, startloc, 384, 45)
            RemoveLocation(mineLoc)
            RemoveLocation(nearTownLoc)
        else
            -- Spawn Necropolis at the start location.
            CreateUnitAtLoc(whichplayer, FourCC('unpl'), startloc, bj_UNIT_FACING)
           
            -- Spawn Acolytes and Ghoul directly south of the Necropolis.
            peonX = GetLocationX(startloc)
            peonY = GetLocationY(startloc) - 224.00
            CreateUnit(whichplayer, FourCC('uaco'), peonX - 1.50 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('uaco'), peonX - 0.50 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('uaco'), peonX + 0.50 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)
            CreateUnit(whichplayer, FourCC('ugho'), peonX + 1.50 * funcs.unitSpacing, peonY + 0.00 * funcs.unitSpacing, bj_UNIT_FACING)

            -- Create a patch of blight around the start location.
             SetBlightLoc(whichplayer,startloc, 768, true)

            -- Set random hero spawn point to be just south of the start location.
            heroLoc = Location(peonX, peonY - 2.00 * funcs.unitSpacing)
        end
        return peonX, peonY, heroLoc
    end
   
    funcs.def_human     = CustomMelee.finalizeSetupFunction(RACE_HUMAN, "Alliance", funcs.def_human)
    funcs.def_orc       = CustomMelee.finalizeSetupFunction(RACE_ORC, "Horde", funcs.def_orc)
    funcs.def_undead    = CustomMelee.finalizeSetupFunction(RACE_UNDEAD, "Scourge", funcs.def_undead)
    funcs.def_nightelf  = CustomMelee.finalizeSetupFunction(RACE_NIGHTELF, "Sentinel", funcs.def_nightelf)
    funcs.def_demon     = MeleeStartingUnitsUnknownRace
    funcs.def_other     = MeleeStartingUnitsUnknownRace

    CustomMelee.raceFactionSetup(RACE_HUMAN, "Alliance", funcs.def_human)
    CustomMelee.raceFactionSetup(RACE_ORC, "Horde", funcs.def_orc)
    CustomMelee.raceFactionSetup(RACE_UNDEAD, "Scourge", funcs.def_undead)
    CustomMelee.raceFactionSetup(RACE_NIGHTELF, "Sentinel", funcs.def_nightelf)
    CustomMelee.raceFactionSetup(RACE_DEMON, "normal", funcs.def_demon)
    CustomMelee.raceFactionSetup(RACE_OTHER, "normal", funcs.def_other)

    CustomMelee.setRaceFactionAI(RACE_HUMAN, "Alliance", "human.ai")
    CustomMelee.setRaceFactionAI(RACE_ORC, "Horde", "orc.ai")
    CustomMelee.setRaceFactionAI(RACE_UNDEAD, "Scourge", "undead.ai")
    CustomMelee.setRaceFactionAI(RACE_NIGHTELF, "Sentinel", "elf.ai")

    --  Registering Hero Ids.
    --  Human
    CustomMelee.addHeroId('Hamg', RACE_HUMAN, funcs.human_id)
    CustomMelee.addHeroId('Hmkg', RACE_HUMAN, funcs.human_id)
    CustomMelee.addHeroId('Hpal', RACE_HUMAN, funcs.human_id)
    CustomMelee.addHeroId('Hblm', RACE_HUMAN, funcs.human_id)

    --  Orc
    CustomMelee.addHeroId('Obla', RACE_ORC, funcs.orc_id)
    CustomMelee.addHeroId('Ofar', RACE_ORC, funcs.orc_id)
    CustomMelee.addHeroId('Otch', RACE_ORC, funcs.orc_id)
    CustomMelee.addHeroId('Oshd', RACE_ORC, funcs.orc_id)

    --  Night Elf
    CustomMelee.addHeroId('Edem', RACE_NIGHTELF, funcs.nightelf_id)
    CustomMelee.addHeroId('Ekee', RACE_NIGHTELF, funcs.nightelf_id)
    CustomMelee.addHeroId('Emoo', RACE_NIGHTELF, funcs.nightelf_id)
    CustomMelee.addHeroId('Ewar', RACE_NIGHTELF, funcs.nightelf_id)

    --  Undead
    CustomMelee.addHeroId('Udea', RACE_UNDEAD, funcs.undead_id)
    CustomMelee.addHeroId('Udre', RACE_UNDEAD, funcs.undead_id)
    CustomMelee.addHeroId('Ulic', RACE_UNDEAD, funcs.undead_id)
    CustomMelee.addHeroId('Ucrl', RACE_UNDEAD, funcs.undead_id)

    --  Neutrals
    CustomMelee.addHeroId('Npbm', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nbrn', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nngs', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nplh', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nbst', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nalc', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Ntin', RACE_OTHER, funcs.other_id)
    CustomMelee.addHeroId('Nfir', RACE_OTHER, funcs.other_id)
end
 
Good WIPs so far, fellas. I'm a little bit worried about the deadline here, but I will try to keep up the pace.

Before I start, I'd like to note that Commoners have been changed to Lay Brothers, just because.
My race works without any actual Peasant unit. This made the Gold and Lumber harvesting a little different than the vanilla factions. I made a video demonstrating how the Lumber system would work, but I have yet to make a Gold harvesting system.

Oh, and the units you see on the right, they are the Cannoneers, the Mortar Team/Demolisher/Meat Wagon/Glaive Thrower equivalent for the Seraphic Union.
They can also plant Land Mines that will reveal the nearby area and will explode upon contact with enemies.

Due to how Hive processed GIFS, this page became troublesome to load. Therefore, I am removing it.
 
Last edited:
Level 9
Joined
Aug 16, 2019
Messages
66
The long-awaited WIP

Talent System!

(I apologize that the text is in Russian, before putting out the finished map all the text will be translated)

This system is inspired by Heroes of the Storm. Talents - improvements to the hero’s race, aimed at increasing the variety of game tactics. The emphasis of the balance of the game by this race is shifted to the dependence of the level of the hero, the leveling of talents at 1, 4, 7, 10 levels of the hero depends on this.
The screenshot shows 3 starting talents, the color indicates their belonging to a common tactic - aggression (red), balance (green), control (blue).
1. Mulching (green) - the complete destruction of a corpse through cannibalism brings a small supply of resources.
2. The legacy of the fallen empire (red) - get 1 non-Nerubian artifact immediately
3. A single network (blue) - get 1 necroarchide for the construction of a special building.

Thanks to Tasyen for the system, this is my dream since the appearance of frames in the SC2 editor.

WC3ScrnShot_033120_204719_001.png
 
Another WIP, I suppose.

Here's a preview of the Guardian Angel unit, a flying unit with strong but slow attacks against other air units. They are quite durable, but will loose a 1v1 melee fight against a Gargoyle (their counterpart) if not properly micro-ed (They have extra range whilst the Gargoyles are completely melee).


I do hope the video explains everything. I'm not sure if there's more to add.

Oh, and have a display of Justicar's ultimate as well:

 
Another WIP. This time, its' about the Hero, Necrovizier.


At the time of the video, I forgot to set the mana cost of the ultimate, so the Necrovizier may appear to be more OP than he looks. Damage values, lifesteal and more will be changed in the future.

EDIT:

Necrovizier's first skill's manacost increased from 110/95/80 to 110/110/110.
Necrovizier's second skill's manacost increased from 40/30/20 to 50/40/30.
Necrovizier's ultimate skill's manacost set to 300 from 0.
 
Last edited:
@MyPad Damn, that's one mana-hungry Hero. Maybe, a bit too mana-hungry, don't you think? 300 manacost for a line CC sounds quite expensive, especially if it's dispellable.

Yep. The mana cost had to be high, since it pierced spell immunity, while potentially giving the Hero a massive amount of lifesteal if the third skill was learned. I'll consider reducing the manacost to 275 - 250, but nerfing the duration of the ultimate just so that there is a smaller window of opportunity to complete a spell combo, which will maximize lifesteal.

By the way, the full combo of this Hero is: {First Skill -> Second Skill x 3 -> Ultimate -> First Skill -> Second Skill}
 
Level 11
Joined
Dec 16, 2018
Messages
365
Maybe you can considerate to make it a 315 mana cost, 5 seconds duration, but A can't-be-dispelled spell?
Yeah, I still alive. And I'm thinking on create a much less ambitious techtree (maybe a NE techtree. No... I am not saying the NE are less interesting than the race they evolved from... Of course I am not...) before the deadline. That would be hard, of course, but I start having much more time (ejem. quarantine, ejem.)
 
Level 3
Joined
Mar 10, 2020
Messages
15
Are you open to suggestions?

If you are then:
Mother nature is angry and cares not if your skin is green, pink or purple. Be you Tauren or Elf, all shall become fertilizer!

Create a tech tree of plants only. Use the base jungle stalker for worker
Large Stalker (broodmother) can periodically sprout "critters" using mushroom model from doodads and have 'wandering' turned on like fungal walking sporecloud mines.
Have "Entangled goldmine" be a building model without actually using a goldmine to create a vine-cage filled with captives. Have your "Tree of Life" surrounded by piles of rotting corpses for 'nutriants' to provide food resource.
Use Treants as base infantry and convert uprooted ancient protectors to siege engines.
Tiny uprooted ancients of lore would make for a great caster model.
Instead of towers, create a cheap barbed shrub to form barriers. Give it a melee attack and the ability to go underground to let your units pass before closing up again.
 
Level 11
Joined
Dec 16, 2018
Messages
365
I am, but sadly I am already starting a druid-based techtree, based in the forgotten druidical forms (druids of the fang, druids of the scythe, and maybe some completely invented forms) to see how it works. Anyway, I am thinking you're ideas are great, and can be coordinated with this "forgotten nature". I love that wall-tower idea, and i was already sad because my actual idea hasn't so many good tower options, per example.
Maybe this isn't lorefriendly, but I don't care that much.
 
Level 3
Joined
Mar 10, 2020
Messages
15
Lol! Lore friendly?! The examples in the opening post mentioned orcs as a "wizard society" :goblin_jawdrop: I don't think lore is really going to be an issue unless you yourself choose for it to be. I think a whole faction of different types of druids is a pretty cool idea. Go crazy with it.

Maybe a druid of the Plains/Horn to create a cavalry form? (thunder lizards come to mind)
A druid of the Earth to make a tunneldigger? (scorpion form perhaps)
 
Last edited:
Are you planning on making any more races except these guys after all of this? While I think your race is great and their flavor is amazing, it kind of don't fit into current melee races.

Mmm, not really. I only ever do races when there's a Techtree Contest going on.

You find my race not fitting? Do elaborate. Would love some feedback (though I'm not sure how my dispel quote have anything to do with this).
 
Status
Not open for further replies.
Top