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.
Please do not redistribute this system. This is a WC3C-exclusive resource, and I wish it to remain that way.
API
You create a struct of type CustomRace, with the following constructor: staticmethodcreatetakesstringName, raceRace, realHandicapreturnsCustomRace
Each of the parameters are described below:
stringName - The name for your race
raceRace - The standard race it will be based on (eg, RACE_HUMAN).
realHandicap - 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: staticmethodcreateAlltakesstringName, raceRacereturnsCustomRace
You can extend the registration of a custom race to more than one handicap with: methodregistertakesraceRace, realHandicapreturnsnothing
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:
integerWorkerId - The rawcode of the race's Worker (eg 'hpea' for Human Peasant)
integerPriority - 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.
integerQuantity - The amount of initial workers of that type.
methodaddHeroTypetakesintegerHeroIdreturnsnothing
integerHeroId - 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).
stringFilePath - 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:
playerPlayer - The owning player of the units passed to the callback function.
groupWorkers - A unit group with all the created workers created, regardless of type.
unitGoldmine - The nearest Goldmine found at the start location. If there isn't a Goldmine nearby, this parameter is null.
unitTownhall - The townhall created on initialization.
unitRandhero - 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.
playerPlayer - 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:
//==============================================================================// Custom Race System by Archmage Owenalacaster//==============================================================================//// Purpose:// - Creates the starting units for custom races and replaces the standard// Melee Initialization trigger. Custom races are selected by race and// handicap.//// Usage:// - Register a new custom race with CustomRace.create(name, RACE, handicap)// Handicaps: Valid handicap values are 1.0, 0.9, 0.8, 0.7, 0.6 and 0.5.// - Register a new custom race for all handicaps of a single race with// CustomRace.createAll(name, RACE)// - Extend the registration of a race with c.register(RACE, handicap)// - Set the townhall type with c.setTownHall(unitid)// - Add a new worker type with c.addWorkerType(unitid, priority, qty)// Priorities: c.NEAR_MINE spawns workers near the mine, where workers// typically spawn.// c.NEAR_HALL spawns workers near the town hall, where// Ghouls spawn.// - Add a random hero type with c.addHeroType(unitid)// - Set the ai script used by computer players with c.setAIScript(stringpath)// - Set a callback function with c.setCallback(CustomRaceCall.function)// Callbacks: The callback is executed after all the starting units for a// player are created, and its purpose is to provide enhanced// initial behaviour for a race. A good example of this with the// standard races would be the Undead Goldmine Haunting and// Night Elves Goldmine Entangling.// The callback function passes as arguments all the units// generated in addition to the nearest goldmine detected.// Please note that if a random hero is not created, the last// argument will have a null value, so always do a check.// - Get a player's custom race name string with GetPlayerCustomRaceName(player)//// Notes:// - Supports a maximum of 24 custom races.// - Maximum for worker and hero types are configurable.//// Requirements:// - JassHelper version 0.9.E.0 or newer (older versions may still work).//// Installation:// - Create a new trigger called CustomRaceSystem.// - Convert it to custom text and replace all the code with this code.//// Special Thanks:// - Alevice: He practically co-wrote the code.// - cosmicat: His formula for circular unit formation.// Co-developing the single-array registry.////==============================================================================libraryCustomRaceSysteminitializerInit//===========================================================================// CONFIGURATION SECTION//===========================================================================globals// Unit Type ConstantsprivateconstantintegerMAX_WORKERTYPES = 4privateconstantintegerMAX_HEROTYPES = 4endglobals//===========================================================================// END CONFIGURATION SECTION//===========================================================================functioninterfaceCustomRaceCalltakesplayerplay, groupworkers, unitgoldmine, unittownhall, unitrandheroreturnsnothingprivatefunctionr2Stakesracerreturnsstringifr == RACE_HUMANthenreturn"Human"elseifr == RACE_ORCthenreturn"Orc"elseifr == RACE_UNDEADthenreturn"Undead"elseifr == RACE_NIGHTELFthenreturn"Night Elf"endifreturn"Unknown"endfunctionprivatefunctionr2Itakesracerreturnsintegerifr == RACE_HUMANthenreturn1elseifr == RACE_ORCthenreturn2elseifr == RACE_UNDEADthenreturn3elseifr == RACE_NIGHTELFthenreturn4endifreturn5endfunctionglobals// Victory Defeat VariablesprivatestringarrayKEY_STRUCTUREprivateintegerKEY_STRUCTURE_COUNT = 0endglobals//===========================================================================// STRUCT DATA//===========================================================================privatekeywordcreateStartingUnitsstructCustomRacestringname// Town Hall VariableintegertownhallType = 0//string townhallName// Town Hall name is not currently supported.// Worker VariablesintegertotalWorkerTypes = 0integerarrayworkerType[MAX_WORKERTYPES]
integerarrayworkerPriority[MAX_WORKERTYPES]
integerarrayworkerQty[MAX_WORKERTYPES]
// Random Hero VariablesintegertotalHeroTypes = 0integerarrayheroType[MAX_HEROTYPES]
// AI Script Directory String Variablestringaiscript = ""// Callback VariableprivateCustomRaceCallc// Registry VariablestaticintegerarrayREGISTRY// Spawn Priority VariablesstaticintegerNEAR_MINE = 0staticintegerNEAR_HALL = 1staticmethodgettakesracer, realhreturnsCustomRacereturnCustomRace(.REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))])
endmethodmethodregistertakesracer, realhreturnsbooleanlocalCustomRacec = CustomRace.get(r,h)
ifc != 0thendebugcallBJDebugMsg("|cffff0000Registration of "+.name+" failed due to conflict with "+c.name+" registered for "+r2S(r)+" race Handicap "+R2S(h))
returnfalseendifset.REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))] = integer(this)
returntrueendmethodstaticmethodcreatetakesstringname, racer, realhreturnsCustomRacelocalCustomRacec = CustomRace.allocate()
setc.name = nameifnotc.register(r,h) thencallc.destroy()
return0endifreturncendmethodstaticmethodcreateAlltakesstringname, racerreturnsCustomRacelocalCustomRacec = CustomRace.allocate()
setc.name = nameifnotc.register(r,1.0) andnotc.register(r,0.9) andnotc.register(r,0.8) andnotc.register(r,0.7) andnotc.register(r,0.6) andnotc.register(r,0.5) thencallc.destroy()
return0endifreturncendmethodmethodsetTownHalltakesintegerhallidreturnsnothingset.townhallType = hallidsetKEY_STRUCTURE[KEY_STRUCTURE_COUNT] = UnitId2String(hallid)
setKEY_STRUCTURE_COUNT = KEY_STRUCTURE_COUNT+1endmethodmethodaddWorkerTypetakesintegerworkerid, integerpriority, integerquantityreturnsnothingset.workerType[.totalWorkerTypes] = workeridset.workerPriority[.totalWorkerTypes] = priorityset.workerQty[.totalWorkerTypes] = quantityset.totalWorkerTypes = .totalWorkerTypes+1endmethodmethodaddHeroTypetakesintegerheroidreturnsnothinglocalintegeri = 0set.heroType[.totalHeroTypes] = heroidset.totalHeroTypes = .totalHeroTypes+1loopcallSetPlayerTechMaxAllowed(Player(i),heroid,1)
seti = i+1exitwheni == bj_MAX_PLAYERSendloopendmethodprivatemethodgetRandomHeroTypetakesnothingreturnsintegerlocalintegerrandomindex = GetRandomInt(0,.totalHeroTypes-1)
return.heroType[randomindex]
endmethodmethodsetAIScripttakesstringsreturnsnothingset.aiscript = sendmethodmethodsetCallbacktakesCustomRaceCallcallbreturnsnothingset.c = callbendmethodprivatemethodcreateRandomHerotakesplayerp, locationlocreturnsunitlocalunith = CreateUnitAtLoc(p, .getRandomHeroType(), loc, bj_UNIT_FACING)
ifbj_meleeGrantHeroItemsthencallMeleeGrantItemsToHero(h)
endifreturnhendmethodmethodcreateStartingUnitstakesplayerpreturnsnothinglocallocationstartLoc = GetPlayerStartLocationLoc(p)
locallocationnearMineLoc = startLoclocallocationnearTownLoc = startLoclocallocationspawnLoc = startLoclocallocationheroLoc = startLoclocalunitnearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
localunitmyTownhall = nulllocalunitmyRandHero = nulllocalgroupworkerGroup = CreateGroup()
localintegerworkertypeindex = 0localintegerworkerqty = 0localintegerspawnPriority = 0ifnearestMine != nullthensetnearMineLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,320,0)
setnearTownLoc = MeleeGetProjectedLoc(startLoc,GetUnitLoc(nearestMine),288,0)
setheroLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,384,45)
endifsetmyTownhall = CreateUnitAtLoc(p,.townhallType,startLoc,bj_UNIT_FACING)
loopexitwhenworkertypeindex == .totalWorkerTypessetspawnPriority = .workerPriority[workertypeindex]
if (spawnPriority==.NEAR_HALL) thensetspawnLoc = nearTownLocelseif(spawnPriority==.NEAR_MINE) thensetspawnLoc = nearMineLocendifloopcallGroupAddUnit(workerGroup, CreateUnitAtLoc(p,.workerType[workertypeindex],PolarProjectionBJ(spawnLoc,65,(I2R(workerqty)*(360.00 / I2R(.workerQty[workertypeindex]))) + 90),bj_UNIT_FACING))
setworkerqty = workerqty + 1exitwhenworkerqty >= .workerQty[workertypeindex]
endloopcallRemoveLocation(spawnLoc)
setworkerqty = 0setworkertypeindex = workertypeindex+1endloopif (IsMapFlagSet(MAP_RANDOM_HERO) and.totalHeroTypes>0 ) thensetmyRandHero = .createRandomHero(p,heroLoc)
elsecallSetPlayerState(p,PLAYER_STATE_RESOURCE_HERO_TOKENS,bj_MELEE_STARTING_HERO_TOKENS)
endifif(.c!=0) thencall.c.evaluate(p,workerGroup,nearestMine,myTownhall,myRandHero)
elsecallDestroyGroup(workerGroup)
endififnearMineLoc != startLocthencallRemoveLocation(nearMineLoc)
callRemoveLocation(nearTownLoc)
callRemoveLocation(heroLoc)
endifcallRemoveLocation(startLoc)
setstartLoc = nullsetnearMineLoc = nullsetnearTownLoc = nullsetspawnLoc = nullsetheroLoc = nullsetnearestMine = nullsetmyTownhall = nullsetmyRandHero = nullsetworkerGroup = nullendmethodendstructglobalsprivatestringarrayPLAYER_RACEendglobalsfunctionGetPlayerCustomRaceNametakesplayerpreturnsstringreturnPLAYER_RACE[GetPlayerId(p)]
endfunction//===========================================================================// UNIT CREATION SECTION//===========================================================================privatefunctionCreateStartingUnitsForAllPlayerstakesnothingreturnsnothinglocalintegerindex = 0localplayerindexPlayerlocalraceplayerRacelocalCustomRacecloopsetindexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) thensetplayerRace = GetPlayerRace(indexPlayer)
setc = CustomRace.get(playerRace,GetPlayerHandicap(indexPlayer)+0.01)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_USERor (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTERandc.aiscript != "" )) andc != 0thensetPLAYER_RACE[index] = c.namecallc.createStartingUnits(indexPlayer)
elseifplayerRace == RACE_HUMANthencallMeleeStartingUnitsHuman(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseifplayerRace == RACE_ORCthencallMeleeStartingUnitsOrc(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseifplayerRace == RACE_NIGHTELFthencallMeleeStartingUnitsNightElf(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseifplayerRace == RACE_UNDEADthencallMeleeStartingUnitsUndead(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elsecallMeleeStartingUnitsUnknownRace(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
endifendifsetindex = index + 1exitwhenindex == bj_MAX_PLAYERSendloopendfunction//===========================================================================// CUSTOM MELEE AI SECTION//===========================================================================privatefunctionCustomMeleeStartingAItakesnothingreturnsnothinglocalintegerindex = 0localplayerindexPlayerlocalraceindexRacelocalCustomRacecloopsetindexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) thensetindexRace = GetPlayerRace(indexPlayer)
setc = CustomRace.get(indexRace,GetPlayerHandicap(indexPlayer)+0.01)
callSetPlayerHandicap(indexPlayer,1.0)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then// Run a race-specific melee AI script.ifc != 0andc.aiscript != ""thencallStartMeleeAI(indexPlayer, c.aiscript)
elseif (indexRace == RACE_HUMAN) thencallPickMeleeAI(indexPlayer, "human.ai", null, null)
elseif (indexRace == RACE_ORC) thencallPickMeleeAI(indexPlayer, "orc.ai", null, null)
elseif (indexRace == RACE_UNDEAD) thencallPickMeleeAI(indexPlayer, "undead.ai", null, null)
callRecycleGuardPosition(bj_ghoul[index])
elseif (indexRace == RACE_NIGHTELF) thencallPickMeleeAI(indexPlayer, "elf.ai", null, null)
else// Unrecognized race.endifcallShareEverythingWithTeamAI(indexPlayer)
endifendifsetindex = index + 1exitwhenindex == bj_MAX_PLAYERSendloopendfunction//===========================================================================// VICTORY DEFEAT SECTION//===========================================================================privatefunctionCustomGetAllyKeyStructureCounttakesplayerwhichPlayerreturnsintegerlocalintegeri = 0localintegerkeyStructs = 0localintegerplayerIndex = 0localplayerindexPlayerloopsetindexPlayer = Player(playerIndex)
if (PlayersAreCoAllied(whichPlayer, indexPlayer)) thensetkeyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
setkeyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
setkeyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
setkeyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
loopsetkeyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, KEY_STRUCTURE[i], true, true)
seti = i+1exitwheni == KEY_STRUCTURE_COUNTendloopendifsetplayerIndex = playerIndex + 1exitwhenplayerIndex == bj_MAX_PLAYERSendloopreturnkeyStructsendfunctionprivatefunctionCustomPlayerIsCrippledtakesplayerwhichPlayerreturnsbooleanlocalintegerallyStructures = MeleeGetAllyStructureCount(whichPlayer)
localintegerallyKeyStructures = CustomGetAllyKeyStructureCount(whichPlayer)
return (allyStructures > 0) and (allyKeyStructures <= 0)
endfunctionprivatefunctionCustomCheckForCrippledPlayerstakesnothingreturnsnothinglocalintegerplayerIndexlocalplayerindexPlayerlocalbooleanisNowCrippledcallMeleeCheckForLosersAndVictors()
ifbj_finishSoonAllExposedthenreturnendifsetplayerIndex = 0loopsetindexPlayer = Player(playerIndex)
setisNowCrippled = CustomPlayerIsCrippled(indexPlayer)
if (notbj_playerIsCrippled[playerIndex] andisNowCrippled) thensetbj_playerIsCrippled[playerIndex] = truecallTimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, functionMeleeCrippledPlayerTimeout)
if (GetLocalPlayer() == indexPlayer) thencallTimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
callDisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_WARNING_HUMAN"))
endifelseif (bj_playerIsCrippled[playerIndex] andnotisNowCrippled) thensetbj_playerIsCrippled[playerIndex] = falsecallPauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) thencallTimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
if (MeleeGetAllyStructureCount(indexPlayer) > 0) thenif (bj_playerIsExposed[playerIndex]) thencallDisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
elsecallDisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
endifendifendifcallMeleeExposePlayer(indexPlayer, false)
endifsetplayerIndex = playerIndex + 1exitwhenplayerIndex == bj_MAX_PLAYERSendloopendfunctionprivatefunctionCustomInitVictoryDefeattakesnothingreturnsnothinglocaltriggerchecker = CreateTrigger()
localtriggertriglocalintegerindexlocalplayerindexPlayersetbj_finishSoonTimerDialog = CreateTimerDialog(null)
callTriggerAddAction(checker, functionCustomCheckForCrippledPlayers)
settrig = CreateTrigger()
callTriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
callTriggerAddAction(trig, functionMeleeTriggerTournamentFinishSoon)
settrig = CreateTrigger()
callTriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
callTriggerAddAction(trig, functionMeleeTriggerTournamentFinishNow)
setindex = 0loopsetindexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) thensetbj_meleeDefeated[index] = falsesetbj_meleeVictoried[index] = falsesetbj_playerIsCrippled[index] = falsesetbj_playerIsExposed[index] = falsesetbj_crippledTimer[index] = CreateTimer()
setbj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
callTimerDialogSetTitle(bj_crippledTimerWindows[index], MeleeGetCrippledTimerMessage(indexPlayer))
callTriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
callTriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
callTriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
callTriggerRegisterPlayerAllianceChange(checker, indexPlayer, ALLIANCE_PASSIVE)
callTriggerRegisterPlayerStateEvent(checker, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
settrig = CreateTrigger()
callTriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_DEFEAT)
callTriggerAddAction(trig, functionMeleeTriggerActionPlayerDefeated)
settrig = CreateTrigger()
callTriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
callTriggerAddAction(trig, functionMeleeTriggerActionPlayerLeft)
elsesetbj_meleeDefeated[index] = truesetbj_meleeVictoried[index] = falseif (IsPlayerObserver(indexPlayer)) thensettrig = CreateTrigger()
callTriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
callTriggerAddAction(trig, functionMeleeTriggerActionPlayerLeft)
endifendifsetindex = index + 1exitwhenindex == bj_MAX_PLAYERSendloopcallTimerStart(CreateTimer(), 2.0, false, functionCustomCheckForCrippledPlayers)
endfunctionprivatefunctionTimerActiontakesnothingreturnsnothingcallSetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
callMeleeStartingHeroLimit()
callMeleeGrantHeroItems()
callMeleeStartingResources()
callMeleeClearExcessUnits()
callCreateStartingUnitsForAllPlayers()
callCustomMeleeStartingAI()
callCustomInitVictoryDefeat()
callDestroyTimer(GetExpiredTimer())
endfunctionprivatefunctionInittakesnothingreturnsnothingcallTimerStart(CreateTimer(),0,false,functionTimerAction)
endfunctionendlibrary
Here is an example of implementation from the demo map. Naga are a playable race for Night Elf, Handicap 90%.
Custom Race Setup:
//====================================================================// HUMAN SETUP//====================================================================libraryHumanSetupinitializerInitrequiresCustomRaceSystemprivatefunctionInittakesnothingreturnsnothinglocalCustomRacec = CustomRace.create("Human",RACE_HUMAN,1.0)
callc.setTownHall('htow') // Town Hallcallc.addWorkerType('hpea',c.NEAR_MINE,5) // Peasantcallc.addHeroType('Hpal') // Paladincallc.addHeroType('Hamg') // Archmagecallc.addHeroType('Hmkg') // Mountain Kingcallc.addHeroType('Hblm') // Blood Magecallc.setAIScript("human.ai")
endfunctionendlibrary//====================================================================// ORC SETUP//====================================================================libraryOrcSetupinitializerInitrequiresCustomRaceSystemprivatefunctionInittakesnothingreturnsnothinglocalCustomRacec = CustomRace.create("Orc",RACE_ORC,1.0)
callc.setTownHall('ogre') // Great Hallcallc.addWorkerType('opeo',c.NEAR_MINE,5) // Peoncallc.addHeroType('Obla') // Blademastercallc.addHeroType('Ofar') // Far Seercallc.addHeroType('Otch') // Tauren Chieftaincallc.addHeroType('Oshd') // Shadow Huntercallc.setAIScript("orc.ai")
endfunctionendlibrary//====================================================================// UNDEAD SETUP//====================================================================libraryUndeadSetupinitializerInitrequiresCustomRaceSystemprivatefunctionWorkerHideToggletakesnothingreturnsnothingcallShowUnit(GetEnumUnit(),IsUnitHidden(GetEnumUnit()))
endfunctionprivatefunctionHauntGoldMinetakesplayerplay, groupworkers, unitgoldmine, unittownhall, unitrandheroreturnsnothingcallForGroup(workers,functionWorkerHideToggle)
callBlightGoldMineForPlayerBJ(goldmine,play)
callForGroup(workers,functionWorkerHideToggle)
callDestroyGroup(workers)
endfunctionprivatefunctionInittakesnothingreturnsnothinglocalCustomRacec = CustomRace.create("Undead",RACE_UNDEAD,1.0)
callc.setTownHall('unpl') // Necropoliscallc.addWorkerType('uaco',c.NEAR_MINE,3) // Acolytecallc.addWorkerType('ugho',c.NEAR_HALL,1) // Ghoulcallc.addHeroType('Udea') // Death Knightcallc.addHeroType('Ulic') // Lichcallc.addHeroType('Udre') // Dreadlordcallc.addHeroType('Ucrl') // Crypt Lordcallc.setCallback(CustomRaceCall.HauntGoldMine)
callc.setAIScript("undead.ai")
endfunctionendlibrary//====================================================================// NIGHT ELF SETUP//====================================================================libraryNightElfSetupinitializerInitrequiresCustomRaceSystemprivatefunctionEntangleGoldMinetakesplayerplay, groupworkers, unitgoldmine, unittownhall, unitrandheroreturnsnothingcallSetUnitPosition(townhall,GetUnitX(goldmine),GetUnitY(goldmine))
callIssueTargetOrder(townhall, "entangleinstant", goldmine)
callDestroyGroup(workers)
endfunctionprivatefunctionInittakesnothingreturnsnothinglocalCustomRacec = CustomRace.create("Night Elf",RACE_NIGHTELF,1.0)
callc.setTownHall('etol') // Tree of Lifecallc.addWorkerType('ewsp',c.NEAR_MINE,5) // Wispcallc.addHeroType('Ekee') // Keeper of the Grovecallc.addHeroType('Emoo') // Priestess of the Mooncallc.addHeroType('Edem') // Demon Huntercallc.addHeroType('Ewar') // Wardencallc.setCallback(CustomRaceCall.EntangleGoldMine)
callc.setAIScript("elf.ai")
endfunctionendlibrary//====================================================================// NAGA SETUP//====================================================================libraryNagaSetupinitializerInitrequiresCustomRaceSystemprivatefunctionInittakesnothingreturnsnothinglocalCustomRacec = CustomRace.create("Naga",RACE_NIGHTELF,0.9)
callc.setTownHall('nntt') // Temple of Tidescallc.addWorkerType('nmpe',c.NEAR_MINE,5) // Murgul Slavecallc.addHeroType('Hvsh') // Lady Vashjcallc.setAIScript("naga.ai")
endfunctionendlibrary
~Just got back from my mission. Glad to be back. Woot! :P~
=It was brought to my attention recently that the ":P" icon stands for "tongue-sticking out", and not the "smirky half-smile" I was using it for all these years. = Thusly I apologize for any unintentional juvenilization of my messages to anyone so afflicted.=
Yes, although the current design would require redundant registration. The code could easily be adapted to permit extended registration; I'll add a register method.
EDIT: I think this will reduce code redundancy to one line per extension.
register method:
methodregistertakesracer, realhreturnsnothinglocalCustomRacec = CustomRace.get(r,h)
ifc != 0thendebugcallBJDebugMsg("|cffff0000Registration of "+.name+" failed due to conflict with "+c.name+" registered for "+r2S(r)+" race Handicap "+R2S(h))
endifset.REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))] = integer(this)
endmethod
Originally Posted by http://ajaxian.com/archives/would-you-like-a-_-with-that-new-library-gives-js-what-it-should-have#comment-276203
- Dont solve problems that dont exist.
- Improve the wheel, dont reinvent it.
- Port the wheel if it doesnt exist in your environment.
- Integrate the wheel into your project.
- Make sure you can replace your wooden wheel for a rubber one if someone else invents it.
It's made to reset the Handicap level to 100% if you choose a certain Race/Handicap combo. The only problem with that is that people can't play as "Human 90%" if they actually did want to do so. :P
~Just got back from my mission. Glad to be back. Woot! :P~
=It was brought to my attention recently that the ":P" icon stands for "tongue-sticking out", and not the "smirky half-smile" I was using it for all these years. = Thusly I apologize for any unintentional juvenilization of my messages to anyone so afflicted.=