vJASS & Zinc Documentation
For the latest documentation about how it works vJASS and Zinc language layers for Warcraft III, please follow these links:
Jasshelper documentation - Zinc documentation - WC3 Optimizer documentation

Custom Race System

moyack · 532

0 Members and 1 Guest are viewing this topic.

Rating

Average Score - 5 / 5

« Created: December 28, 2017, 07:51:38 AM by moyack »

Custom Race System
on: April 14, 2012, 09:05:36 AM
Category: Execution, System, AI
Language: vJASS
Download Demo Map

Documentation
The purpose of this system is to provide a simple framework for the implementation of custom melee races. It accomplishes this by setting up key components Blizzard should have given since the beginning (at least in a more portable state): the race initialization and victory / defeat conditions.

The approach chosen was one discussed a long time ago, probably on the Power of Corruption boards: letting the user pick their race by means of choosing one of the standard races and setting the handicap to a given value, which would be translated by the system into a particular custom race.


"API"
You create a struct of type CustomRace, with the following constructor:
static method create takes string Name, race Race, real Handicap returns CustomRace

Each of the parameters are described below:
  • string Name  - The name for your race
  • race Race  - The standard race it will be based on (eg, RACE_HUMAN).
  • real Handicap  - The handicap value users need to set so they can pick the race. Accpetable values are 0.5, 0.6, onwards up to 1.0 (which means you can overwrite the standard races too)

You can also create and register a custom race for all handicaps of a single race with:
static method createAll takes string Name, race Race returns CustomRace


You can extend the registration of a custom race to more than one handicap with:
method register takes race Race, real Handicap returns nothing
 
 
 

Once created, you still need to register its townhall, the workers types (you can have more than one type of worker) and hero(es). You can do it by using the following methods:

method setTownHall takes integer TownHallId returns nothing
  • integer TownHallId   - The rawcode of the race's Townhall (eg 'ogre' for Orc Great Hall)

method addWorkerType takes integer WorkerId, integer Priority, integer Quantity returns nothing
  • integer WorkerId   - The rawcode of the race's Worker (eg 'hpea' for Human Peasant)
  • integer Priority  - The priority where the worker type unit will be created. Acceptable values are
    • CustomRace.NEAR_MINE  - Spawns them near the closest mine. Normally all worker units should use this value.
    • CustomRace.NEAR_HALL  - Spawns them near the starting Town Hall unit type. This is included if you want additional workers, in the same vein Ghouls are for the Undead race.
  • integer Quantity   - The amount of initial workers of that type.

method addHeroType takes integer HeroId returns nothing
  • integer HeroId  - The rawcode of the race's hero (eg 'Hpal' for Human Paladin)


If you've got an AI ready to be implemented, then you've already done the hard part. All this takes is a string for the file path and it takes care of the rest. Custom races that were registered but don't have AI scripts cannot be played by the computer. Of course, you can circumvent this (for whatever silly reason) by using this with a string that doesn't refer to an .ai file ("poop" for example).

method setAIScript takes string FilePath returns nothing
  • string FilePath  - The filepath of the .ai script, either in an MPQ or imported to the map


Finally, if your race needs extended initialization beyond the creation of worker units, you can do so by assigning a custom callback function that will run after all the initial units are created; this might be useful to replicate behaviour like the initial Haunted Goldmine the Undead start with, or the Entagled Goldmine for the Night Elves. For that you use the following method:

method setCallback takes CustomRaceCall callback returns nothing
  • CustomRaceCall callback  - A function that matches the CustomRaceCall function interface. More on that below.


The CustomRaceCall function interface is used for the aforementioned callback. It is defined as follows:

function interface CustomRaceCall takes player Player, group Workers, unit Goldmine, unit Townhall, unit Randhero returns nothing
  • player Player  - The owning player of the units passed to the callback function.
  • group Workers  - A unit group with all the created workers created, regardless of type.
  • unit Goldmine  - The nearest Goldmine found at the start location. If there isn't a Goldmine nearby, this parameter is null.
  • unit Townhall  - The townhall created on initialization.
  • unit Randhero  - If the "Use a Random Hero" option is checked before starting the game, this parameter points to the randomly created Hero; otherwise, it will be null.


If you need to get the custom race of a player, GetPlayerCustomRaceName will return a name string of the custom race. Hopefully, you aren't silly enough to name your custom races identically.

function GetPlayerCustomRaceName takes player Player returns string
  • player Player  - The player with a custom race. Players without a custom race associated with them will return an empty string.

 
Demo Map Credits
Playtesters
  • Alevice
  • Anopob
  • cosmicat
  • Dragoon
  • Michael Peppers
Models
  • Callahan  - Mur'gul House
  • DonDustin  - Blue Basilisk Missile
  • JetFangInferno  - Aqua Spike
  • jigrael  - Reptilian Sanctuary, Sea Drake
  • Phoenix - IV  - Naga Town Hall
  • MasterHaosis  - Mur'gul Labor Mill
  • Szythe  - Water Buff
Icons
  • Anachron  - Water Shockwave
  • bigapple90  - Water Tornado
  • Technomancer  - Drown
Scripts
  • Anitarf  - Stack
  • Vexorian  - SimError, Table, TimerUtils


Things to take into consideration
  • If a Goldmine cant be found within default melee lookup radius, all created workers will be located near the Town Hall. this is done accodirng to default melee behaviour.
  • You can now register custom races in their own separate libraries. They should require CustomRaceSystem and must run on initialization.
  • The system now supports AI. And the Naga are a great example of it, if I may say so.
  • If you define two or more custom races with the same base race and handicap, a debug message will be displayed in - game and whichever races were defined beyond the first will not be registered.

Custom Race System
Code: jass
  1. // ==============================================================================
  2.  //  Custom Race System by Archmage Owenalacaster
  3.  // ==============================================================================
  4.  //
  5.  //  Purpose:
  6.  //       - Creates the starting units for custom races and replaces the standard
  7.  //        Melee Initialization trigger. Custom races are selected by race and
  8.  //        handicap.
  9.  //
  10.  //  Usage:
  11.  //       - Register a new custom race with CustomRace.create(name, RACE, handicap)
  12.  //          Handicaps: Valid handicap values are 1.0, 0.9, 0.8, 0.7, 0.6 and 0.5.
  13.  //       - Register a new custom race for all handicaps of a single race with
  14.  //        CustomRace.createAll(name, RACE)
  15.  //       - Extend the registration of a race with c.register(RACE, handicap)
  16.  //       - Set the townhall type with c.setTownHall(unitid)
  17.  //       - Add a new worker type with c.addWorkerType(unitid, priority, qty)
  18.  //          Priorities: c.NEAR_MINE spawns workers near the mine, where workers
  19.  //                        typically spawn.
  20.  //                      c.NEAR_HALL spawns workers near the town hall, where
  21.  //                        Ghouls spawn.
  22.  //       - Add a random hero type with c.addHeroType(unitid)
  23.  //       - Set the ai script used by computer players with c.setAIScript(stringpath)
  24.  //       - Set a callback function with c.setCallback(CustomRaceCall.function)
  25.  //          Callbacks: The callback is executed after all the starting units for a
  26.  //                     player are created, and its purpose is to provide enhanced
  27.  //                     initial behaviour for a race. A good example of this with the
  28.  //                     standard races would be the Undead Goldmine Haunting and
  29.  //                     Night Elves Goldmine Entangling.
  30.  //                     The callback function passes as arguments all the units
  31.  //                     generated in addition to the nearest goldmine detected.
  32.  //                     Please note that if a random hero is not created, the last
  33.  //                     argument will have a null value, so always do a check.
  34.  //        - Get a player's custom race name string with GetPlayerCustomRaceName(player)
  35.  //
  36.  //  Notes:
  37.  //       - Supports a maximum of 24 custom races.
  38.  //       - Maximum for worker and hero types are configurable.
  39.  //
  40.  //  Requirements:
  41.  //       - JassHelper version 0.9.E.0 or newer (older versions may still work).
  42.  //
  43.  //  Installation:
  44.  //       - Create a new trigger called CustomRaceSystem.
  45.  //       - Convert it to custom text and replace all the code with this code.
  46.  //
  47.  //  Special Thanks:
  48.  //       - Alevice:  He practically co - wrote the code.
  49.  //       - cosmicat: His formula for circular unit formation.
  50.  //                  Co - developing the single - array registry.
  51.  //
  52.  // ==============================================================================
  53.  
  54. library CustomRaceSystem initializer Init
  55.    
  56.  // ===========================================================================
  57.  //                                CONFIGURATION SECTION
  58.  // ===========================================================================
  59.     globals
  60.          // Unit Type Constants
  61.         private constant integer MAX_WORKERTYPES = 4
  62.         private constant integer MAX_HEROTYPES = 4
  63.     endglobals
  64.  // ===========================================================================
  65.  //                                END CONFIGURATION SECTION
  66.  // ===========================================================================
  67.  
  68.     function interface CustomRaceCall takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
  69.    
  70.     private function r2S takes race r returns string
  71.         if r == RACE_HUMAN then
  72.             return "Human"
  73.         elseif r == RACE_ORC then
  74.             return "Orc"
  75.         elseif r == RACE_UNDEAD then
  76.             return "Undead"
  77.         elseif r == RACE_NIGHTELF then
  78.             return "Night Elf"
  79.         endif
  80.         return "Unknown"
  81.     endfunction
  82.    
  83.     private function r2I takes race r returns integer
  84.         if r == RACE_HUMAN then
  85.             return 1
  86.         elseif r == RACE_ORC then
  87.             return 2
  88.         elseif r == RACE_UNDEAD then
  89.             return 3
  90.         elseif r == RACE_NIGHTELF then
  91.             return 4
  92.         endif
  93.         return 5
  94.     endfunction
  95.    
  96.     globals
  97.          // Victory Defeat Variables
  98.         private string array KEY_STRUCTURE
  99.         private integer KEY_STRUCTURE_COUNT = 0
  100.     endglobals
  101.    
  102.  // ===========================================================================
  103.  //                                STRUCT DATA
  104.  // ===========================================================================
  105.     private keyword createStartingUnits
  106.    
  107.     struct CustomRace
  108.         string name
  109.        
  110.          // Town Hall Variable
  111.         integer townhallType = 0
  112.        // string  townhallName
  113.          // Town Hall name is not currently supported.
  114.        
  115.          // Worker Variables
  116.         integer totalWorkerTypes = 0
  117.         integer array workerType[MAX_WORKERTYPES]
  118.         integer array workerPriority[MAX_WORKERTYPES]
  119.         integer array workerQty[MAX_WORKERTYPES]
  120.        
  121.          // Random Hero Variables
  122.         integer totalHeroTypes = 0
  123.         integer array heroType[MAX_HEROTYPES]
  124.        
  125.          // AI Script Directory String Variable
  126.         string aiscript = ""
  127.        
  128.          // Callback Variable
  129.         private CustomRaceCall c
  130.        
  131.          // Registry Variable
  132.         static integer array REGISTRY
  133.        
  134.          // Spawn Priority Variables
  135.         static integer NEAR_MINE     = 0
  136.         static integer NEAR_HALL     = 1
  137.        
  138.         static method get takes race r, real h returns CustomRace
  139.             return CustomRace(.REGISTRY[((r2I(r) - 1) * 6) + (10 - R2I(h * 10.))])
  140.         endmethod
  141.        
  142.         method register takes race r, real h returns boolean
  143.             local CustomRace c = CustomRace.get(r, h)
  144.             if c != 0 then
  145.                 debug call BJDebugMsg("|cffff0000Registration of " + .name + " failed due to conflict with " + c.name + " registered for " + r2S(r) + " race Handicap " + R2S(h))
  146.                 return false
  147.             endif
  148.             set .REGISTRY[((r2I(r) - 1) * 6) + (10 - R2I(h * 10.))] = integer(this)
  149.             return true
  150.         endmethod
  151.        
  152.         static method create takes string name, race r, real h returns CustomRace
  153.             local CustomRace c = CustomRace.allocate()
  154.             set c.name = name
  155.             if not c.register(r, h) then
  156.                 call c.destroy()
  157.                 return 0
  158.             endif
  159.             return c
  160.         endmethod
  161.        
  162.         static method createAll takes string name, race r returns CustomRace
  163.             local CustomRace c = CustomRace.allocate()
  164.             set c.name = name
  165.             if not c.register(r, 1.0) and not c.register(r, 0.9) and not c.register(r, 0.8) and not c.register(r, 0.7) and not c.register(r, 0.6) and not c.register(r, 0.5) then
  166.                 call c.destroy()
  167.                 return 0
  168.             endif
  169.             return c
  170.         endmethod
  171.        
  172.         method setTownHall takes integer hallid returns nothing
  173.             set .townhallType = hallid
  174.             set KEY_STRUCTURE[KEY_STRUCTURE_COUNT] = UnitId2String(hallid)
  175.             set KEY_STRUCTURE_COUNT = KEY_STRUCTURE_COUNT + 1
  176.         endmethod
  177.        
  178.         method addWorkerType takes integer workerid, integer priority, integer quantity returns nothing
  179.             set .workerType[.totalWorkerTypes] = workerid
  180.             set .workerPriority[.totalWorkerTypes] = priority
  181.             set .workerQty[.totalWorkerTypes] = quantity
  182.             set .totalWorkerTypes = .totalWorkerTypes + 1
  183.         endmethod
  184.        
  185.         method addHeroType takes integer heroid returns nothing
  186.             local integer i = 0
  187.             set .heroType[.totalHeroTypes] = heroid
  188.             set .totalHeroTypes = .totalHeroTypes + 1
  189.             loop
  190.                 call SetPlayerTechMaxAllowed(Player(i), heroid, 1)
  191.                 set i = i + 1
  192.                 exitwhen i == bj_MAX_PLAYERS
  193.             endloop
  194.         endmethod
  195.        
  196.         private method getRandomHeroType takes nothing returns integer
  197.             local integer randomindex = GetRandomInt(0, .totalHeroTypes - 1)
  198.             return .heroType[randomindex]
  199.         endmethod
  200.        
  201.         method setAIScript takes string s returns nothing
  202.             set .aiscript = s
  203.         endmethod
  204.        
  205.         method setCallback takes CustomRaceCall callb returns nothing
  206.             set .c = callb
  207.         endmethod
  208.        
  209.         private method createRandomHero takes player p, location loc returns unit
  210.             local unit h = CreateUnitAtLoc(p, .getRandomHeroType(), loc, bj_UNIT_FACING)
  211.             if bj_meleeGrantHeroItems then
  212.                 call MeleeGrantItemsToHero(h)
  213.             endif
  214.             return h
  215.         endmethod
  216.        
  217.         method createStartingUnits takes player p returns nothing
  218.             local location   startLoc        = GetPlayerStartLocationLoc(p)
  219.             local location   nearMineLoc     = startLoc
  220.             local location   nearTownLoc     = startLoc
  221.             local location   spawnLoc        = startLoc
  222.             local location   heroLoc         = startLoc
  223.             local unit       nearestMine     = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
  224.             local unit       myTownhall      = null
  225.             local unit       myRandHero      = null
  226.             local group      workerGroup     = CreateGroup()
  227.             local integer    workertypeindex = 0
  228.             local integer    workerqty       = 0
  229.             local integer    spawnPriority   = 0
  230.             if nearestMine != null then
  231.                 set nearMineLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine), startLoc, 320, 0)
  232.                 set nearTownLoc = MeleeGetProjectedLoc(startLoc, GetUnitLoc(nearestMine), 288, 0)
  233.                 set heroLoc     = MeleeGetProjectedLoc(GetUnitLoc(nearestMine), startLoc, 384, 45)
  234.             endif
  235.             set myTownhall = CreateUnitAtLoc(p, .townhallType, startLoc, bj_UNIT_FACING)
  236.             loop
  237.                 exitwhen workertypeindex == .totalWorkerTypes
  238.                     set spawnPriority = .workerPriority[workertypeindex]
  239.                     if (spawnPriority==.NEAR_HALL) then
  240.                         set spawnLoc = nearTownLoc
  241.                     elseif(spawnPriority==.NEAR_MINE) then
  242.                         set spawnLoc = nearMineLoc
  243.                     endif
  244.                     loop
  245.                         call GroupAddUnit(workerGroup, CreateUnitAtLoc(p,.workerType[workertypeindex],PolarProjectionBJ(spawnLoc,65,(I2R(workerqty) * (360.00 / I2R(.workerQty[workertypeindex])))  +  90),bj_UNIT_FACING))
  246.                         set workerqty = workerqty  +  1
  247.                         exitwhen workerqty >= .workerQty[workertypeindex]
  248.                     endloop
  249.                     call RemoveLocation(spawnLoc)
  250.                     set workerqty = 0
  251.                 set workertypeindex = workertypeindex + 1
  252.             endloop
  253.             if (IsMapFlagSet(MAP_RANDOM_HERO) and .totalHeroTypes>0 ) then
  254.                 set myRandHero = .createRandomHero(p,heroLoc)
  255.             else
  256.                 call SetPlayerState(p,PLAYER_STATE_RESOURCE_HERO_TOKENS,bj_MELEE_STARTING_HERO_TOKENS)
  257.             endif
  258.             if(.c!=0) then
  259.                 call .c.evaluate(p,workerGroup,nearestMine,myTownhall,myRandHero)
  260.             else
  261.                 call DestroyGroup(workerGroup)
  262.             endif
  263.             if nearMineLoc != startLoc then
  264.                 call RemoveLocation(nearMineLoc)
  265.                 call RemoveLocation(nearTownLoc)
  266.                 call RemoveLocation(heroLoc)
  267.             endif
  268.             call RemoveLocation(startLoc)
  269.             set startLoc    = null
  270.             set nearMineLoc = null
  271.             set nearTownLoc = null
  272.             set spawnLoc    = null
  273.             set heroLoc     = null
  274.             set nearestMine = null
  275.             set myTownhall  = null
  276.             set myRandHero  = null
  277.             set workerGroup = null
  278.         endmethod
  279.      endstruct
  280.      
  281.      globals
  282.         private string array PLAYER_RACE
  283.      endglobals
  284.      
  285.      function GetPlayerCustomRaceName takes player p returns string
  286.         return PLAYER_RACE[GetPlayerId(p)]
  287.      endfunction
  288.    
  289. //===========================================================================
  290. //                         UNIT CREATION SECTION
  291. //===========================================================================
  292.     private function CreateStartingUnitsForAllPlayers takes nothing returns nothing
  293.         local integer    index = 0
  294.         local player     indexPlayer
  295.         local race       playerRace
  296.         local CustomRace c
  297.         loop
  298.             set indexPlayer = Player(index)
  299.             if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
  300.                 set playerRace = GetPlayerRace(indexPlayer)
  301.                 set c = CustomRace.get(playerRace,GetPlayerHandicap(indexPlayer) + 0.01)
  302.                 if (GetPlayerController(indexPlayer) == MAP_CONTROL_USER or (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER and c.aiscript != "" )) and c != 0 then
  303.                     set PLAYER_RACE[index] = c.name
  304.                     call c.createStartingUnits(indexPlayer)
  305.                 elseif playerRace == RACE_HUMAN then
  306.                     call MeleeStartingUnitsHuman(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
  307.                 elseif playerRace == RACE_ORC then
  308.                     call MeleeStartingUnitsOrc(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
  309.                 elseif playerRace == RACE_NIGHTELF then
  310.                     call MeleeStartingUnitsNightElf(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
  311.                 elseif playerRace == RACE_UNDEAD then
  312.                     call MeleeStartingUnitsUndead(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
  313.                 else
  314.                     call MeleeStartingUnitsUnknownRace(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
  315.                 endif
  316.             endif
  317.             set index = index  +  1
  318.             exitwhen index == bj_MAX_PLAYERS
  319.         endloop
  320.     endfunction
  321.    
  322. //===========================================================================
  323. //                         CUSTOM MELEE AI SECTION
  324. //===========================================================================
  325.     private function CustomMeleeStartingAI takes nothing returns nothing
  326.         local integer index = 0
  327.         local player  indexPlayer
  328.         local race    indexRace
  329.         local CustomRace c
  330.         loop
  331.             set indexPlayer = Player(index)
  332.             if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
  333.                 set indexRace = GetPlayerRace(indexPlayer)
  334.                 set c = CustomRace.get(indexRace,GetPlayerHandicap(indexPlayer) + 0.01)
  335.                 call SetPlayerHandicap(indexPlayer,1.0)
  336.                 if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
  337.                     // Run a race-specific melee AI script.
  338.                     if c != 0 and c.aiscript != "" then
  339.                         call StartMeleeAI(indexPlayer, c.aiscript)
  340.                     elseif (indexRace == RACE_HUMAN) then
  341.                         call PickMeleeAI(indexPlayer, "human.ai", null, null)
  342.                     elseif (indexRace == RACE_ORC) then
  343.                         call PickMeleeAI(indexPlayer, "orc.ai", null, null)
  344.                     elseif (indexRace == RACE_UNDEAD) then
  345.                         call PickMeleeAI(indexPlayer, "undead.ai", null, null)
  346.                         call RecycleGuardPosition(bj_ghoul[index])
  347.                     elseif (indexRace == RACE_NIGHTELF) then
  348.                         call PickMeleeAI(indexPlayer, "elf.ai", null, null)
  349.                     else
  350.                         // Unrecognized race.
  351.                     endif
  352.                     call ShareEverythingWithTeamAI(indexPlayer)
  353.                 endif
  354.             endif
  355.  
  356.             set index = index  +  1
  357.             exitwhen index == bj_MAX_PLAYERS
  358.         endloop
  359.     endfunction    
  360.  
  361. //===========================================================================
  362. //                         VICTORY DEFEAT SECTION
  363. //===========================================================================
  364.     private function CustomGetAllyKeyStructureCount takes player whichPlayer returns integer
  365.         local integer    i           = 0
  366.         local integer    keyStructs  = 0
  367.         local integer    playerIndex = 0
  368.         local player     indexPlayer
  369.         loop
  370.             set indexPlayer = Player(playerIndex)
  371.             if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
  372.                 set keyStructs = keyStructs  +  GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
  373.                 set keyStructs = keyStructs  +  GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
  374.                 set keyStructs = keyStructs  +  GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
  375.                 set keyStructs = keyStructs  +  GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
  376.                 loop
  377.                     set keyStructs = keyStructs  +  GetPlayerTypedUnitCount(indexPlayer, KEY_STRUCTURE[i], true, true)
  378.                     set i = i + 1
  379.                     exitwhen i == KEY_STRUCTURE_COUNT
  380.                 endloop
  381.             endif
  382.             set playerIndex = playerIndex  +  1
  383.             exitwhen playerIndex == bj_MAX_PLAYERS
  384.         endloop
  385.         return keyStructs
  386.     endfunction
  387.  
  388.     private function CustomPlayerIsCrippled takes player whichPlayer returns boolean
  389.         local integer allyStructures    = MeleeGetAllyStructureCount(whichPlayer)
  390.         local integer allyKeyStructures = CustomGetAllyKeyStructureCount(whichPlayer)
  391.         return (allyStructures > 0) and (allyKeyStructures <= 0)
  392.     endfunction
  393.  
  394.     private function CustomCheckForCrippledPlayers takes nothing returns nothing
  395.         local integer    playerIndex
  396.         local player     indexPlayer
  397.         local boolean    isNowCrippled
  398.         if bj_finishSoonAllExposed then
  399.             return
  400.         endif
  401.         set playerIndex = 0
  402.         loop
  403.             set indexPlayer = Player(playerIndex)
  404.             set isNowCrippled = CustomPlayerIsCrippled(indexPlayer)
  405.             if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
  406.                 set bj_playerIsCrippled[playerIndex] = true
  407.                 call TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, function MeleeCrippledPlayerTimeout)
  408.                 if (GetLocalPlayer() == indexPlayer) then
  409.                     call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
  410.                     call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_WARNING_HUMAN"))
  411.                 endif
  412.             elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
  413.                 set bj_playerIsCrippled[playerIndex] = false
  414.                 call PauseTimer(bj_crippledTimer[playerIndex])
  415.                 if (GetLocalPlayer() == indexPlayer) then
  416.                     call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
  417.                     if (MeleeGetAllyStructureCount(indexPlayer) > 0) then
  418.                         if (bj_playerIsExposed[playerIndex]) then
  419.                             call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
  420.                         else
  421.                             call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
  422.                         endif
  423.                     endif
  424.                 endif
  425.                 call MeleeExposePlayer(indexPlayer, false)
  426.             endif
  427.             set playerIndex = playerIndex  +  1
  428.             exitwhen playerIndex == bj_MAX_PLAYERS
  429.         endloop
  430.     endfunction
  431.  
  432.     private function CustomInitVictoryDefeat takes nothing returns nothing
  433.         local trigger    checker = CreateTrigger()
  434.         local trigger    trig
  435.         local integer    index
  436.         local player     indexPlayer
  437.         set bj_finishSoonTimerDialog = CreateTimerDialog(null)
  438.         call TriggerAddAction(checker, function CustomCheckForCrippledPlayers)
  439.         set trig = CreateTrigger()
  440.         call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
  441.         call TriggerAddAction(trig, function MeleeTriggerTournamentFinishSoon)
  442.         set trig = CreateTrigger()
  443.         call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
  444.         call TriggerAddAction(trig, function MeleeTriggerTournamentFinishNow)
  445.         set index = 0
  446.         loop
  447.             set indexPlayer = Player(index)
  448.             if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
  449.                 set bj_meleeDefeated[index] = false
  450.                 set bj_meleeVictoried[index] = false
  451.                 set bj_playerIsCrippled[index] = false
  452.                 set bj_playerIsExposed[index] = false
  453.                 set bj_crippledTimer[index] = CreateTimer()
  454.                 set bj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
  455.                 call TimerDialogSetTitle(bj_crippledTimerWindows[index], MeleeGetCrippledTimerMessage(indexPlayer))
  456.                 call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
  457.                 call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
  458.                 call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
  459.                 call TriggerRegisterPlayerAllianceChange(checker, indexPlayer, ALLIANCE_PASSIVE)
  460.                 call TriggerRegisterPlayerStateEvent(checker, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
  461.                 set trig = CreateTrigger()
  462.                 call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_DEFEAT)
  463.                 call TriggerAddAction(trig, function MeleeTriggerActionPlayerDefeated)
  464.                 set trig = CreateTrigger()
  465.                 call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
  466.                 call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
  467.              else
  468.                 set bj_meleeDefeated[index] = true
  469.                 set bj_meleeVictoried[index] = false
  470.                 if (IsPlayerObserver(indexPlayer)) then
  471.                     set trig = CreateTrigger()
  472.                     call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
  473.                     call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
  474.                 endif
  475.             endif
  476.             set index = index  +  1
  477.             exitwhen index == bj_MAX_PLAYERS
  478.         endloop
  479.         call TimerStart(CreateTimer(), 2.0, false, function CustomCheckForCrippledPlayers)
  480.     endfunction
  481.    
  482.     private function TimerAction takes nothing returns nothing
  483.         call SetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
  484.         call MeleeStartingHeroLimit()
  485.         call MeleeGrantHeroItems()
  486.         call MeleeStartingResources()
  487.         call MeleeClearExcessUnits()
  488.         call CreateStartingUnitsForAllPlayers()
  489.         call CustomMeleeStartingAI()
  490.         call CustomInitVictoryDefeat()
  491.         call DestroyTimer(GetExpiredTimer())
  492.     endfunction
  493.    
  494.     private function Init takes nothing returns nothing
  495.         call TimerStart(CreateTimer(),0,false,function TimerAction)
  496.     endfunction
  497.    
  498. endlibrary  

Here is an example of implementation from the demo map. Naga are a playable race for Night Elf, Handicap 90%.

Custom Race Setup
Code: jass
  1. //====================================================================
  2. //                           HUMAN SETUP
  3. //====================================================================
  4.  
  5. library HumanSetup initializer Init requires CustomRaceSystem
  6.    
  7.     private function Init takes nothing returns nothing
  8.         local CustomRace c = CustomRace.create("Human",RACE_HUMAN,1.0)
  9.         call c.setTownHall('htow')                  // Town Hall
  10.         call c.addWorkerType('hpea',c.NEAR_MINE,5)  // Peasant
  11.         call c.addHeroType('Hpal')                  // Paladin
  12.         call c.addHeroType('Hamg')                  // Archmage
  13.         call c.addHeroType('Hmkg')                  // Mountain King
  14.         call c.addHeroType('Hblm')                  // Blood Mage
  15.         call c.setAIScript("human.ai")
  16.     endfunction
  17.    
  18. endlibrary
  19.  
  20. //====================================================================
  21. //                            ORC SETUP
  22. //====================================================================
  23.  
  24. library OrcSetup initializer Init requires CustomRaceSystem
  25.    
  26.     private function Init takes nothing returns nothing
  27.         local CustomRace c = CustomRace.create("Orc",RACE_ORC,1.0)
  28.         call c.setTownHall('ogre')                  // Great Hall
  29.         call c.addWorkerType('opeo',c.NEAR_MINE,5)  // Peon
  30.         call c.addHeroType('Obla')                  // Blademaster
  31.         call c.addHeroType('Ofar')                  // Far Seer
  32.         call c.addHeroType('Otch')                  // Tauren Chieftain
  33.         call c.addHeroType('Oshd')                  // Shadow Hunter
  34.         call c.setAIScript("orc.ai")
  35.     endfunction
  36.    
  37. endlibrary
  38.  
  39. //====================================================================
  40. //                          UNDEAD SETUP
  41. //====================================================================
  42.  
  43. library UndeadSetup initializer Init requires CustomRaceSystem
  44.    
  45.     private function WorkerHideToggle takes nothing returns nothing
  46.     endfunction
  47.    
  48.     private function HauntGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
  49.         call ForGroup(workers,function WorkerHideToggle)
  50.         call BlightGoldMineForPlayerBJ(goldmine,play)
  51.         call ForGroup(workers,function WorkerHideToggle)
  52.         call DestroyGroup(workers)
  53.     endfunction
  54.    
  55.     private function Init takes nothing returns nothing
  56.         local CustomRace c = CustomRace.create("Undead",RACE_UNDEAD,1.0)
  57.         call c.setTownHall('unpl')                  // Necropolis
  58.         call c.addWorkerType('uaco',c.NEAR_MINE,3)  // Acolyte
  59.         call c.addWorkerType('ugho',c.NEAR_HALL,1)  // Ghoul
  60.         call c.addHeroType('Udea')                  // Death Knight
  61.         call c.addHeroType('Ulic')                  // Lich
  62.         call c.addHeroType('Udre')                  // Dreadlord
  63.         call c.addHeroType('Ucrl')                  // Crypt Lord
  64.         call c.setCallback(CustomRaceCall.HauntGoldMine)
  65.         call c.setAIScript("undead.ai")
  66.     endfunction
  67.    
  68. endlibrary
  69.  
  70. //====================================================================
  71. //                          NIGHT ELF SETUP
  72. //====================================================================
  73.  
  74. library NightElfSetup initializer Init requires CustomRaceSystem
  75.    
  76.     private function EntangleGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
  77.         call SetUnitPosition(townhall,GetUnitX(goldmine),GetUnitY(goldmine))
  78.         call IssueTargetOrder(townhall, "entangleinstant", goldmine)
  79.         call DestroyGroup(workers)
  80.     endfunction
  81.    
  82.     private function Init takes nothing returns nothing
  83.         local CustomRace c = CustomRace.create("Night Elf",RACE_NIGHTELF,1.0)
  84.         call c.setTownHall('etol')                  // Tree of Life
  85.         call c.addWorkerType('ewsp',c.NEAR_MINE,5)  // Wisp
  86.         call c.addHeroType('Ekee')                  // Keeper of the Grove
  87.         call c.addHeroType('Emoo')                  // Priestess of the Moon
  88.         call c.addHeroType('Edem')                  // Demon Hunter
  89.         call c.addHeroType('Ewar')                  // Warden
  90.         call c.setCallback(CustomRaceCall.EntangleGoldMine)
  91.         call c.setAIScript("elf.ai")
  92.     endfunction
  93.    
  94. endlibrary
  95.  
  96. //====================================================================
  97. //                           NAGA SETUP
  98. //====================================================================
  99.  
  100. library NagaSetup initializer Init requires CustomRaceSystem
  101.    
  102.     private function Init takes nothing returns nothing
  103.         local CustomRace c = CustomRace.create("Naga",RACE_NIGHTELF,0.9)
  104.         call c.setTownHall('nntt')                  // Temple of Tides
  105.         call c.addWorkerType('nmpe',c.NEAR_MINE,5)  // Murgul Slave
  106.         call c.addHeroType('Hvsh')                  // Lady Vashj
  107.         call c.setAIScript("naga.ai")
  108.     endfunction
  109.    
  110. endlibrary
« Last Edit: December 28, 2017, 11:01:36 AM by moyack »



 

Vivir aprendiendo.co - A place for learning stuff, in Spanish   Chaos Realm - The world of Game modders and wc3 addicts   Diplo, a gaming community   WC3JASS.com - The JASS Vault + vJASS and Zinc   Jetcraft - A Starcraft II mod