For sometime now I've been living with a need of giving something back to the community that I've took so much from. This page is great source of various tutorials and I've saved tons of hours finding stuff that I need.
I've decided to write a tutorial on how to create boss fight as this is the topic I am really enjoying, and to be honest I didn't
find many
This tutorial will describe how to create a boss fight that will work in multiplayer environment and will be easy to customize.
The final effect you can see on the video. Let's start.
1. DATA STUFF
I don't want to go too deep into data detail, here are few tips that are easy to miss when trying to recreate what I did.
Boss Unit must have "Behavior: Response -> No Response" and "Combat: Default Acquire Level -> None" set. First one will make him not responding when attacked, and second one will make him not helping allied units when they are attacked. This will allow us to gain full control over boss unit with triggers.
Zergling adds should have "Tactical AI: None". Having there default value can lead to AI handling burrow/unburrow actions and can screw up your battle.
To create green acid pool I've used model Floating Creep Pad and set it's tint color in "Actor: Events". Make sure that this color has a label as it may not work when you leave it empty.
Rest is all about what you want the boss to do. I believe that all abilities that boss can/will use should be always done in data and only control and AI should be in triggers - but, if you find something difficult to recreate do not hesitate to make abilities triggers driven. Unless you add too much stuff and execute it too often you should be just fine.
2. PREPARATION
For each boss that I create, at first I prepare a record that will hold all variables connected to specified boss fight.
As you can easily notice it is divided to two parts: Generic and Specific. Generic is something that is applicable to every
boss that is created with this method. For example, you can be sure that every boss should have marker that will be telling
if it is engaged. In specific part we will put all stuff connected to boss abilities that is valid only for this specific boss.
Unfortunately there is no "inheritance" mechanism like in C++ or Java, but it is not a big deal.
We can live with just repeating this kind of a generic stuff.
Of course, this is not very strict. If you need f.e. three variables to hold three different boss units you can easily
change these.
BOSS - ACID ROACH
VARIABLES// ------- ------- ------- -------// GENERIC// These variables are common and should be in all boss records// ------- ------- ------- -------// array of players that are active on this bossactive=False<Boolean<maxPlayers>>// is boss engagedengaged=False<Boolean>// is boss alivealive=False<Boolean>// unit that represents the bossunit=NoUnit<Unit>// variable containing players that are fighting the boss// note that this group might be different than (Active Hero Players)// as not everyone might choose to engage the bossplayersFighting=(Emptyplayergroup)<PlayerGroup>// good practice is to provide text tips to players// all boss info will be in this variabletip=NoText<Text>// at the begining of the fight we might want to introduce// boss somehowtalks=True<Boolean>// phase at which boss is (generic)phase=0<Integer>// wait timer for next boss action (generic)wait=0.0<Real>// gate to the bossgate=NoUnit<Unit>// ------- -------// ------- ------- ------- -------// BOSS SPECIFIC// These variables are only for this particular boss// ------- ------- ------- -------// phase of boss (ability)burrowPhase=0<Integer>burrowUnits=(Emptyunitgroup)<UnitGroup>// number of lings that will be unburrowedunburrowLingsNumber=0<Integer>// ------- -------
3. ACTIVATION
Activation is the semi-starting phase. It launches when one of the players enters boss area. This will allow you to make all necessary setups before fight starts. It is used for following things:
creating revealers on whole boss area
showing a boss bar
changing the camera
displaying a tip or description about the boss
playing intro sequence for the boss
Fight didn't start yet, but we can prepare players and show them what they are about to face. It is still possible that
after reading description they might want to run away so we will leave that possibility as well.
Area setup looks like this:
As you can notice, Boss.AcidRoach.Activate is inside of Boss.AcidRoach.Area. Also, make sure that it won't be possible to attack boss without going through "Activate" area first. While this script should handle such situation just fine, you don't want that.
ACID ROACH - ACTIVATE
EVENTS// player entered area that is smaller then whole arenaUnit-AnyUnitEntersBoss.AcidRoach.ActivateLOCALVARIABLESCONDITIONS// activate should fire before boss is engaged// if someone won't be fast enough to enter this region together with the rest,// we will check for fighting players again when boss is attackedbossAcidRoach.engaged==False// boss must be alivebossAcidRoach.alive==True// player that entered is hero player((Ownerof(Triggeringunit))isin(ActiveHeroPlayers))==True// this player is not active on this boss yetbossAcidRoach.active[(Triggeringplayer)]==FalseACTIONS// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Activate, Player:...",(Text((Triggeringplayer))))))// -------// activate player on bossVariable-SetbossAcidRoach.active[(Triggeringplayer)]=True// -------// create revealers for this particular playerVisibility-CreateRevealersforplayergroup(2,(Playergroup((Triggeringplayer))),Boss.AcidRoach.Area)// -------// check if there is an intro for this fightGeneral-If(Conditions)thendo(Actions)elsedo(Actions)IfbossAcidRoach.talks==TrueThen// ------- ------- -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Intro Playing")))// we won't bother players with this intro againVariable-SetbossAcidRoach.talks=False// find all players for playing the intro// it won't show for the rest but this what happens when they are lazyVariable-SetbossAcidRoach.playersFighting=(PlayersHavingUnitsinRegion(Boss.AcidRoach.Area,(ActiveHeroPlayers)))// make sure fight won't start yetUnit-MakebossAcidRoach.unitInvulnerable// ------- ------- -------// play introCamera-ApplyCamera001forbossAcidRoach.playersFightingover1.0secondsGeneral-Wait1.0GameTimesecondsCamera-ApplyCamera002forbossAcidRoach.playersFightingover6.0secondsGeneral-Wait2.0GameTimesecondsSound-PlayMutalisk_PissedforbossAcidRoach.playersFightingonbossAcidRoach.unitwithZoffset0.0(at300.0%volume,skipthefirst0.0seconds)Animation-PlayFidgetanimationfor(ActorforbossAcidRoach.unit)asDefault,usingNoOptionsoptionsandDefaultTimeblendtimeUI-Display"ACID ROACH: <frightening roar>"forbossAcidRoach.playersFightingtoChatareaGeneral-Wait6.0GameTimesecondsCamera-Apply(Defaultgamecamera)forbossAcidRoach.playersFightingover2.0seconds(NoTarget)General-Wait4.0GameTimeseconds// ------- ------- -------// make boss attackable againUnit-MakebossAcidRoach.unitVulnerableElse// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Intro Skip")))// -------// -------// show boss bar// player number is used as Boss Bar ID to make it easierUI-Displaybossbar(Triggeringplayer)withtitle"ACID ROACH",portraitAssets\Textures\btn-unit-zerg-primalroach.ddsandmaximumvalue100for(Playergroup((Triggeringplayer)))UI-Setbossbar(Triggeringplayer)bosstobossAcidRoach.unit(Dorefreshthebossbar)// -------// this also good place to change the camera if you want// to have different view on particular fight// just remember to change it back to (Default Game Camera) when fight ends// -------
When player will decide to not fight the boss we must handle this situation as well:
destroy revealers
hide a boss bar
revert the camera
Note that deactivation happens when player leave Boss.AcidRoach.Area, not the activation area.
ACID ROACH - DEACTIVATE
EVENTSUnit-AnyUnitLeavesBoss.AcidRoach.AreaLOCALVARIABLESCONDITIONS// deactivate can only happen before boss is engagedbossAcidRoach.engaged==False// if boss is not alive this means that fight already ended// so no need to deactivatebossAcidRoach.alive==True// player that left is hero player((Ownerof(Triggeringunit))isin(ActiveHeroPlayers))==True// obviously player must be active on boss// before we deactivate himbossAcidRoach.active[(Triggeringplayer)]==True// no other unit of this player is left on fighting area(NumberofLivingunitsin(AnyunitsinBoss.AcidRoach.Areaownedbyplayer(Triggeringplayer)matchingExcluded:Structure,Missile,Item,Dead,Hidden,Invulnerable,withatmostAnyAmount))==0ACTIONS// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Deactivate, Playe...",(Text((Triggeringplayer))))))// -------// deactivate player on bossVariable-SetbossAcidRoach.active[(Triggeringplayer)]=False// -----// hide boss barUI-Hidebossbar(Triggeringplayer)UI-Setbossbar(Triggeringplayer)bosstoNoUnit(Dorefreshthebossbar)// -------// destroy revealersVisibility-DestroyRevealersforplayergroup(2,(Playergroup((Triggeringplayer))))// -------
4. ENGAGE
Engage is the real start phase. It is important to remember that all variables should be set here. Usually I tend to
reset them when fight ends and set them again here, even though it is clearly redundant.
When boss will be attacked, we will check again for all players in the fighting area and "activate" them
if necessary. This is to cover situations when not all players were fast enough to reach activation area.
Also, note that controller won't have any event that it will react to. It will be run from here,
only after everything is initialized properly.
ACID ROACH - ENGAGE
EVENTS// using this event instead of "unit is attacked" is better// because players can also use specific abilities to start the fight// which are not always registred as an attackUnit-AnyUnittakesFatalorNon-FatalAnydamage(fromAnyeffects)LOCALVARIABLESCONDITIONSbossAcidRoach.unit==(Triggeringunit)bossAcidRoach.engaged==FalsebossAcidRoach.alive==TrueACTIONS// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Engaged...")))// -------// mark as engagedVariable-SetbossAcidRoach.engaged=True// start with phase 1Variable-SetbossAcidRoach.phase=1// collect player group for whole areaVariable-SetbossAcidRoach.playersFighting=(PlayersHavingUnitsinRegion(Boss.AcidRoach.Area,(ActiveHeroPlayers)))// -------// turn on Zergling AI trigger, it will be needed only during this fightTrigger-TurnAcidRoach-ZerglingAddsAIOn// -------// set fight specific variablesVariable-SetbossAcidRoach.burrowPhase=0// -------// for all players that are in area but wasn't fast enough to reach activate area// let's activate the fight (no intro obviously)PlayerGroup-PickeachplayerinbossAcidRoach.playersFightinganddo(Actions)Actions// -------// wait with revive till fight endsVariable-Setplayers[(Pickedplayer)].haltRevive=True// -------// activate rest of the players that are in the fighting area// and create revealers and boss bar for themGeneral-If(Conditions)thendo(Actions)elsedo(Actions)IfbossAcidRoach.active[(Pickedplayer)]==FalseThen// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Force activate, P...",(Text((Pickedplayer))))))// -------Variable-SetbossAcidRoach.active[(Pickedplayer)]=TrueVisibility-CreateRevealersforplayergroup(2,(Playergroup((Pickedplayer))),Boss.AcidRoach.Area)// when copying from ACTIVATE trigger make sure you change (Triggering Player) to (Picked Player) in 3 places hereUI-Displaybossbar(Pickedplayer)withtitle"ACID ROACH",portraitAssets\Textures\btn-unit-zerg-primalroach.ddsandmaximumvalue100for(Playergroup((Pickedplayer)))UI-Setbossbar(Pickedplayer)bosstobossAcidRoach.unit(Dorefreshthebossbar)Else// -------// sounds for effectSound-PlayRoach_ReadyforbossAcidRoach.playersFightingonbossAcidRoach.unitwithZoffset0.0(at300.0%volume,skipthefirst0.0seconds)// -------// close area gateUnit-OrderbossAcidRoach.gateto(CloseGate)(ReplaceExistingOrders)// -------// run the controlerTrigger-RunAcidRoach-Controler(CheckConditions,Don'tWaituntilitfinishes)// -------
5. CONTROLLER
Controller is where the magic happens. Boss unit is handled completely by the script. No data, nor automatic AI should
be used as it is very hard to predict what will happen. The whole idea is based on having so called "idle loop". During
this time in the loop, boss will use his default attack and follow closest enemy (or whatever attack-move command will order him
to follow). When the "idle loop" will end, depending on boss phase held in proper variable, he will use one of his
deadly abilities. Also, it is possible to implement abilities that will launch depending on current boss HP.
To do that we will add checks inside of the loop. When specific ability is done, we will set loop timer to desired value,
change boss phase and return to the loop itself. It is very simple and gives many possibilities for customization.
ACID ROACH - CONTROLER
EVENTSLOCALVARIABLESCONDITIONSACTION// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Controler Starts")))// -------// set initial wait to 10 secondsVariable-SetbossAcidRoach.wait=10.0// -------// controler will run only till boss engaged// this trigger will be stopped anyway but just for making sure it won't go foreverGeneral-While(Conditions)aretrue,do(Actions)ConditionsbossAcidRoach.engaged==TrueActions// ------- ------- -------// IDLE LOOP// this loop will go while "wait" variable is bigger than 0// meaning that boss is not using any abilities yet// ------- ------- -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Idle Loop Starts,...",(Text(bossAcidRoach.wait)with2decimalplaces))))// ------- ------- -------General-While(Conditions)aretrue,do(Actions)ConditionsbossAcidRoach.wait>0.0Actions// ------- ------- -------// ------- ------- -------// ------- ------- -------// ------- ------- -------// HEALTH PERCENT ABILITIES// these kind of abilites can be started when idle is bigger than 0// ------- ------- -------// BURROWED SWARMLINGS// this ability will fire on 75%, 50% and 25% boss hpGeneral-If(Conditions)thendo(Actions)elsedo(Actions)IfOrConditionsAndConditionsbossAcidRoach.burrowPhase==0(bossAcidRoach.unitLife(Percent)(Current))<=75.0AndConditionsbossAcidRoach.burrowPhase==1(bossAcidRoach.unitLife(Percent)(Current))<=50.0AndConditionsbossAcidRoach.burrowPhase==2(bossAcidRoach.unitLife(Percent)(Current))<=25.0Then// ------- ------- -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Burrow Phase: ",(Text(bossAcidRoach.burrowPhase)))))// ------- ------- -------// make sure that given phase will launch only onceVariable-ModifybossAcidRoach.burrowPhase:+1// ------- ------- -------Unit-OrderbossAcidRoach.unitto(AcidRoach-Burrow)(ReplaceExistingOrders)// ------- ------- -------General-Switch(Actions)dependingonsettings.difficultyCasesGeneral-If(1)ActionsGeneral-Wait8.0GameTimesecondsVariable-SetbossAcidRoach.unburrowLingsNumber=8General-If(2)ActionsGeneral-Wait8.0GameTimesecondsVariable-SetbossAcidRoach.unburrowLingsNumber=10General-If(3)ActionsGeneral-Wait6.0GameTimesecondsVariable-SetbossAcidRoach.unburrowLingsNumber=10DefaultGeneral-Wait8.0GameTimesecondsVariable-SetbossAcidRoach.unburrowLingsNumber=10// ------- ------- -------General-Repeat(Actions)bossAcidRoach.unburrowLingsNumbertimesActionsUnit-Create1Boss-Zergling(AddBurrowed)forplayer5at(RandompointinBoss.AcidRoach.ZerglingAddSpawn)usingdefaultfacing(NoOptions)UnitGroup-Add(Lastcreatedunit)tobossAcidRoach.burrowUnitsUnit-Order(Lastcreatedunit)to(ZerglingAdd-Unburrow)(AfterExistingOrders)// ------- ------- -------// give player time to clean up zergling addsGeneral-Wait20.0GameTimeseconds// ------- ------- -------// don't forget to unburrow bossUnit-OrderbossAcidRoach.unitto(AcidRoach-Unburrow)(ReplaceExistingOrders)// ------- ------- -------// return to idle loop with new waitVariable-SetbossAcidRoach.wait=10.0// ------- ------- -------Else// ------- ------- -------// ------- ------- -------// ------- ------- -------// ------- ------- -------// IDLE CHECK// ------- ------- -------General-If(Conditions)thendo(Actions)elsedo(Actions)If(bossAcidRoach.unitisin(IdleUnitsforplayer5))==True(NumberofLivingunitsin(UnitsinBoss.AcidRoach.AreahavingallianceEnemywithplayer5matchingRequired:Visible;Excluded:Structure,Missile,Item,Dead,Hidden,Invulnerable,withatmostAnyAmount))>0ThenDebug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Order: Attack")))Unit-OrderbossAcidRoach.unitto(Attacktargeting(Positionof(RandomLivingunitfrom(UnitsinBoss.AcidRoach.AreahavingallianceEnemywithplayer7matchingRequired:Ground,Visible;Excluded:Player,Ally,Neutral,Structure,Missile,Item,Stasis,Dead,Hidden,Invulnerable,withatmostAnyAmount))))(ReplaceExistingOrders)Else// ------- ------- -------// ------- ------- -------// ------- ------- -------// ------- ------- -------// TIMER// ------- ------- -------General-Wait0.25GameTimesecondsVariable-ModifybossAcidRoach.wait:-0.25// ------- ------- -------// ------- ------- -------// ------- ------- -------// ------- ------- -------// PHASE ABILITIES// phase abilities are the ones that fire when "wait" is equal to 0.0// ------- ------- -------General-Switch(Actions)dependingonbossAcidRoach.phaseCases// ACID SPITGeneral-If(1)Actions// -------// it is good practice to give some kind of warning for players// random text works very well and adding specific sounds allows// players to feel the rythm of the fight and adapt more easilySendtransmission(advanced)to(Allplayers)from(bossAcidRoach.unitwithFlash(DoNotoverrideportrait)playingTalk)playingRoachPrimal_Pissedwithname"ACID ROACH: "andmessage"<frightening roar>"usingNoPortraitwith""playingTalk(SetTo4.0seconds,Don'tWaituntilitfinishes)Sound-PlayMutalisk_PissedforbossAcidRoach.playersFightingonbossAcidRoach.unitwithZoffset0.0(at300.0%volume,skipthefirst0.0seconds)UI-Display"ACID ROACH: <frightening roar>"forbossAcidRoach.playersFightingtoChatarea// -------// give order to boss to perform ability// we will choose furthest unit from boss locationDebug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Order: Acid Spit")))Unit-OrderbossAcidRoach.unitto(AcidRoach-AcidRoach-AcidSpittargeting(Positionof(RandomLivingunitfrom(UnitsinBoss.AcidRoach.AreahavingallianceEnemywithplayer5matchingExcluded:Structure,Missile,Item,Dead,Hidden,Invulnerable,withatmostAnyAmount))))(ReplaceExistingOrders)Unit-OrderbossAcidRoach.unitto(AcidRoach-AcidRoach-AcidSpittargeting(Positionof(FurthestUnitFromPoint((PositionofbossAcidRoach.unit),(UnitsinBoss.AcidRoach.AreahavingallianceEnemywithplayer7matchingExcluded:Structure,Missile,Item,Dead,Hidden,Invulnerable,withatmostAnyAmount)))))(ReplaceExistingOrders)// -------// allow boss to finish his ability// even if it will take longer it is no problem as// we will still go back to IDLE LOOPGeneral-Wait6.0GameTimeseconds// -------// set IDLE LOOP time and next phaseVariable-SetbossAcidRoach.wait=10.0Variable-SetbossAcidRoach.phase=1// -------General-If(2)Actions// -------// other ability could be used here// also it is possible to have here some switch random action to choose one of abilitiesGeneral-Switch(Actions)dependingon(Randomintegerbetween2and4)CasesGeneral-If(2)Actions// do ability 2General-If(3)Actions// do ability 3General-If(4)Actions// do ability 4Default// -------Default// ------- ------- -------// -------
6. END FIGHT
Boss fight can end two ways. Either players defeat the boss, or boss kill all players.
Simple as that.
ACID ROACH - PLAYERS DEFEATED
EVENTSUnit-AnyUnitdiesLOCALVARIABLESCONDITIONS// only check if we are fighting this particular bossbossAcidRoach.engaged==True// player that died is hero player((Triggeringplayer)isin(ActiveHeroPlayers))==True// player that died was fighting boss((Triggeringplayer)isinbossAcidRoach.playersFighting)==True// no one else left fighting the boss// with exception of:// - invunerable - doors at entrance// - items - if your game features items and inventory, players would be able to place them on the ground and bug out the fight// - structures - all special stuff that is summoned or placed by players (bunkers/ turrets / mines) is also not counting(NumberofLivingunitsin(UnitsinBoss.AcidRoach.AreahavingallianceEnemywithplayer5matchingExcluded:Structure,Missile,Item,Dead,Hidden,Invulnerable,Summoned,withatmostAnyAmount))==0ACTIONS// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Players Defeated")))// -------// reset all important variablesVariable-SetbossAcidRoach.engaged=FalseVariable-SetbossAcidRoach.phase=0// -------// turn off Zergling AI trigger to reduce number of callsTrigger-TurnAcidRoach-ZerglingAddsAIOff// -------// stop controler immiediatlyTrigger-StopallinstancesofAcidRoach-Controler// -------// in case of some special ability of the heroes (like f.e. planting mines)// it is good practice to make boss invunerable at this point// fight is already over and if something would now hit boss and kill him// that could cause other trigger (Boss Defeated) to fire and// everything to break downUnit-Add1InvulnerableHidden(Hit)tobossAcidRoach.unitfromplayer7// we order boss to go stop all his movement and other actions,// we will order him to move back to his starting point later onUnit-OrderbossAcidRoach.unitto(Stop)(ReplaceExistingOrders)// -------// wait for better effectGeneral-Wait2.0GameTimeseconds// -------// kill all adds and other stuff that might be leftover from the fightUnitGroup-Pickeachunitin(Boss-AcidRoach-AcidLake(PlacedUnit)unitsinBoss.AcidRoach.AreaownedbyplayerAnyPlayermatchingExcluded:Dead,withatmostAnyAmount)anddo(Actions)ActionsUnit-Kill(Pickedunit)UnitGroup-PickeachunitinbossAcidRoach.burrowUnitsanddo(Actions)ActionsUnit-Kill(Pickedunit)UnitGroup-Pickeachunitin(Boss-Zergling(AddBurrowed)unitsinBoss.AcidRoach.Areaownedbyplayer5matchingExcluded:Dead,withatmostAnyAmount)anddo(Actions)ActionsUnit-Kill(Pickedunit)UnitGroup-Pickeachunitin(AnyunitsinBoss.AcidRoach.AreaownedbyplayerAnyPlayermatchingRequired:Structure;Excluded:Missile,Item,Dead,Invulnerable,withatmostAnyAmount)anddo(Actions)ActionsUnit-Kill(Pickedunit)UnitGroup-RemoveallunitsfrombossAcidRoach.burrowUnits// -------// order boss to move at his starting point and turn towards entranceUnit-OrderbossAcidRoach.unitto(AcidRoach-Unburrow)(ReplaceExistingOrders)Unit-OrderbossAcidRoach.unitto(MovetargetingBoss.AcidRoach.Start)(AfterExistingOrders)Unit-OrderbossAcidRoach.unitto(Turntargeting(Boss.AcidRoach.Startoffsetby1.0towards270.0degrees))(AfterExistingOrders)// -------// reset players activity, destroy revealers and hide boss barsVisibility-DestroyRevealersforplayergroup(2,bossAcidRoach.playersFighting)PlayerGroup-PickeachplayerinbossAcidRoach.playersFightinganddo(Actions)ActionsVariable-Setplayers[(Pickedplayer)].haltRevive=FalseVariable-SetbossAcidRoach.active[(Pickedplayer)]=FalseUI-Hidebossbar(Pickedplayer)UI-Setbossbar(Pickedplayer)bosstoNoUnit(Dorefreshthebossbar)//-------// wait for better effectGeneral-Wait4.0GameTimeseconds// -------// open boss doorsUnit-OrderbossAcidRoach.gateto(OpenGate)(ReplaceExistingOrders)// -------// remove invunerability and reset boss health// boss is ready to fight againUnit-Remove1InvulnerableHidden(Hit)frombossAcidRoach.unitfromplayer7Unit-SetbossAcidRoach.unitLife(Percent)to100.0Unit-SetbossAcidRoach.unitShields(Percent)to100.0// -------
ACID ROACH - BOSS DEFEATED
EVENTSUnit-AnyUnitdiesLOCALVARIABLESCONDITIONS// good practice is not to check anything else here// in case anything bugs out, players still will be able to complete// the fight just by killing the boss unitbossAcidRoach.unit==(Triggeringunit)ACTIONS// -------Debug-DebugText((Combine("DEBUG # ","Boss Acid Roach - Boss Defeated")))// -------// reset all important variablesVariable-SetbossAcidRoach.alive=FalseVariable-SetbossAcidRoach.engaged=FalseVariable-SetbossAcidRoach.phase=100// -------// turn off Zergling AI trigger to reduce number of callsTrigger-TurnAcidRoach-ZerglingAddsAIOff// -------// stop controler immiediatlyTrigger-StopallinstancesofAcidRoach-Controler// -------// wait for better effectGeneral-Wait2.0GameTimeseconds// -------// remove all stuff that might have been summoned by the bossUnitGroup-Pickeachunitin(Boss-AcidRoach-AcidLake(PlacedUnit)unitsinBoss.AcidRoach.AreaownedbyplayerAnyPlayermatchingExcluded:Dead,withatmostAnyAmount)anddo(Actions)ActionsUnit-RemoveAllAcidRoach-AcidLake(StartShrinkVisual)from(Pickedunit)UnitGroup-PickeachunitinbossAcidRoach.burrowUnitsanddo(Actions)ActionsUnit-Kill(Pickedunit)// -------// wait for better effectGeneral-Wait3.0GameTimeseconds// -------// additional removal due to visualsUnitGroup-Pickeachunitin(Boss-AcidRoach-AcidLake(PlacedUnit)unitsinBoss.AcidRoach.AreaownedbyplayerAnyPlayermatchingExcluded:Dead,withatmostAnyAmount)anddo(Actions)ActionsUnit-Kill(Pickedunit)// -------// reset players activity, destroy revealers and hide boss barsVisibility-DestroyRevealersforplayergroup(2,bossAcidRoach.playersFighting)PlayerGroup-PickeachplayerinbossAcidRoach.playersFightinganddo(Actions)ActionsVariable-SetbossAcidRoach.active[(Pickedplayer)]=FalseUI-Hidebossbar(Pickedplayer)UI-Setbossbar(Pickedplayer)bosstoNoUnit(Dorefreshthebossbar)// -------// open boss doorsUnit-OrderbossAcidRoach.gateto(OpenGate)(ReplaceExistingOrders)// -------// show victory messageUI-Display"ACID ROACH DEFEATED!"forbossAcidRoach.playersFightingtoDirectivearea// -------General-Wait10.0GameTimeseconds// -------// remove victory messageUI-ClearDirectiveMessagesforbossAcidRoach.playersFighting// -------
7. TIPS
Never use "Break" action in Pick Each Player, Pick Each Unit, Pick Each Integer loops. This can lead to very hard to find bugs.
Not sure if this is true but just in case never use (Triggering ...) in a trigger after you had a "Wait" action. Other trigger might fire when you are waiting and the value will be overwritten.
Okay, so, this is it.
As usual you can find the map in the attachment. I know there are other sources to learn from (like campaign fight with Archangel, or three Zerus bosses) but they are not really applicable to multiplayer environment. I'm not claiming this is BEST METHOD EVER - it is just something that I came up with during my adventure with editor. F.e. I'm still wondering how SCU is handling boss battles, do they have similar template and just customize it every time?
That would be cool to know.
Anyway, I hope you will like it and someone will find it actually useful.
This is very interesting, and very detailed (much more so than me, but then again, I'm a simple guy).
I started doing boss battles myself with Amber Sun, and it was something I taught myself to do (sort of, anyway). With the first one, Phantom Locust (in mission 6), I didn't even know how to create dodgeable missiles, which is something that you could include a tutorial of in future installments, perhaps? After all, having attacks that the player can dodge is sorta the meat of boss fights.
I myself figured out how to work around it, by essentially giving the boss nearsighetdness (plus spotters, which helped him find me), but when I made the final fight in mission 10, I had figured it out (though just barely), and I am very glad I did.
Anyway, I digress. There are many ways to create a boss battle, and this is a fantastic tutorial to get you started. Keep up the good work.
However, I didn't want to focus on abilities. I wanted to show general trigger template that can be used to make sure that boss will do what you want. There are many great ideas for abilities and those contained here are only examples.
Question, how do you make the alert things that appear when the boss is about to use an abilt of something. Like a warning thing that tells you to move out of the way so you dont get hit by the ability, It shows where they are going to use the ability at/where its gonna hit. Like in the hots campaign:
To create an alert I would give the abilities a delay before the damage and have it create actors at the target points so that the player knows where the target points are.
RELIABLE BOSS BATTLES
(Multiplayer)
Hi there,
For sometime now I've been living with a need of giving something back to the community that I've took so much from. This page is great source of various tutorials and I've saved tons of hours finding stuff that I need. I've decided to write a tutorial on how to create boss fight as this is the topic I am really enjoying, and to be honest I didn't find many
This tutorial will describe how to create a boss fight that will work in multiplayer environment and will be easy to customize. The final effect you can see on the video.
Let's start.
1. DATA STUFF
I don't want to go too deep into data detail, here are few tips that are easy to miss when trying to recreate what I did.
2. PREPARATION
For each boss that I create, at first I prepare a record that will hold all variables connected to specified boss fight. As you can easily notice it is divided to two parts: Generic and Specific. Generic is something that is applicable to every boss that is created with this method. For example, you can be sure that every boss should have marker that will be telling if it is engaged. In specific part we will put all stuff connected to boss abilities that is valid only for this specific boss. Unfortunately there is no "inheritance" mechanism like in C
++
or Java, but it is not a big deal. We can live with just repeating this kind of a generic stuff.Of course, this is not very strict. If you need f.e. three variables to hold three different boss units you can easily change these.
BOSS - ACID ROACH
3. ACTIVATION
Activation is the semi-starting phase. It launches when one of the players enters boss area. This will allow you to make all necessary setups before fight starts. It is used for following things:
Fight didn't start yet, but we can prepare players and show them what they are about to face. It is still possible that after reading description they might want to run away so we will leave that possibility as well.
Area setup looks like this:
As you can notice, Boss.AcidRoach.Activate is inside of Boss.AcidRoach.Area. Also, make sure that it won't be possible to attack boss without going through "Activate" area first. While this script should handle such situation just fine, you don't want that.
ACID ROACH - ACTIVATE
When player will decide to not fight the boss we must handle this situation as well:
Note that deactivation happens when player leave Boss.AcidRoach.Area, not the activation area.
ACID ROACH - DEACTIVATE
4. ENGAGE
Engage is the real start phase. It is important to remember that all variables should be set here. Usually I tend to reset them when fight ends and set them again here, even though it is clearly redundant. When boss will be attacked, we will check again for all players in the fighting area and "activate" them if necessary. This is to cover situations when not all players were fast enough to reach activation area. Also, note that controller won't have any event that it will react to. It will be run from here, only after everything is initialized properly.
ACID ROACH - ENGAGE
5. CONTROLLER
Controller is where the magic happens. Boss unit is handled completely by the script. No data, nor automatic AI should be used as it is very hard to predict what will happen. The whole idea is based on having so called "idle loop". During this time in the loop, boss will use his default attack and follow closest enemy (or whatever attack-move command will order him to follow). When the "idle loop" will end, depending on boss phase held in proper variable, he will use one of his deadly abilities. Also, it is possible to implement abilities that will launch depending on current boss HP. To do that we will add checks inside of the loop. When specific ability is done, we will set loop timer to desired value, change boss phase and return to the loop itself. It is very simple and gives many possibilities for customization.
ACID ROACH - CONTROLER
6. END FIGHT
Boss fight can end two ways. Either players defeat the boss, or boss kill all players.
Simple as that.
ACID ROACH - PLAYERS DEFEATED
ACID ROACH - BOSS DEFEATED
7. TIPS
Okay, so, this is it.
As usual you can find the map in the attachment. I know there are other sources to learn from (like campaign fight with Archangel, or three Zerus bosses) but they are not really applicable to multiplayer environment. I'm not claiming this is BEST METHOD EVER - it is just something that I came up with during my adventure with editor. F.e. I'm still wondering how SCU is handling boss battles, do they have similar template and just customize it every time? That would be cool to know.
Anyway, I hope you will like it and someone will find it actually useful.
Thank you
Regards
Matic
Very nice, a great learning tool with plenty of documentation.
Skype: [email protected] Current Project: Custom Hero Arena! US: battlenet:://starcraft/map/1/263274 EU: battlenet:://starcraft/map/2/186418
This is very interesting, and very detailed (much more so than me, but then again, I'm a simple guy).
I started doing boss battles myself with Amber Sun, and it was something I taught myself to do (sort of, anyway). With the first one, Phantom Locust (in mission 6), I didn't even know how to create dodgeable missiles, which is something that you could include a tutorial of in future installments, perhaps? After all, having attacks that the player can dodge is sorta the meat of boss fights.
I myself figured out how to work around it, by essentially giving the boss nearsighetdness (plus spotters, which helped him find me), but when I made the final fight in mission 10, I had figured it out (though just barely), and I am very glad I did.
Anyway, I digress. There are many ways to create a boss battle, and this is a fantastic tutorial to get you started. Keep up the good work.
Thanks for kind words guys!
@EivindL: Go
I agree that having dodgeable missiles or something similar really gives that "boss fight feeling". Personally I've learned a lot from OneTwo's tutorial:
http://www.sc2mapster.com/forums/resources/tutorials/25781-diablo-fireball-spell-tutorial/
However, I didn't want to focus on abilities. I wanted to show general trigger template that can be used to make sure that boss will do what you want. There are many great ideas for abilities and those contained here are only examples.
Question, how do you make the alert things that appear when the boss is about to use an abilt of something. Like a warning thing that tells you to move out of the way so you dont get hit by the ability, It shows where they are going to use the ability at/where its gonna hit. Like in the hots campaign:
@Draxacon: Go
To create an alert I would give the abilities a delay before the damage and have it create actors at the target points so that the player knows where the target points are.
Is this up on the wiki? If not, can I link your tutorial in the wiki?
Thank you for the details, this is a big help!
Marie T. Freeman If you're too busy to give your neighbor a helping hand, then you're just too darned busy. https://www.facebook.com/wargirlmaps.maps
Spread the love join DISCORD
https://discord.gg/Jtzt8Su
@Hockleberry: Go
Sure thing Hockleberry!
I didn't put it there cause to be honest I didn't know how to do it...
Thanks!