0 Members and 1 Guest are viewing this topic.
(click to show/hide)Quote (click to show/hide)Quote (click to show/hide)Quote (click to show/hide)Quote
Endless Attack by TrillsterEndless Attack Randomizer - TrillsterBoss Backend - RusselMMSP Map Segments - CutmanMikeTesting - PeggTesting - BlewsTesting - LlamaTesting - ZappoolaTesting - YairTesting - FTX6004Original MMSP Project by CutmanMikeHammer Joe - KackebangoNeedleman Stab Animation - Maxine & KorbySniper Armor - Kackebango, updated by HansungkeeApache Joe - Kackebango, updated by FTX6004Bomb Thrown - KackebangoEvery other enemy included - CutmanMike
Can I play this mode offline?Yes! All you need to do is load up your files and load EAHUB in Survival Cooperative mode via Offline Skirmish in the menu.How do I develop new level segments? (click to show/hide)QuoteCreating new level segments is relatively painless as long as you're experienced with Doom Builder already and have some basic ACS knowledge. Just don't forget to load the Endless Attack file as a resource so you'll have access to all the enemies within the build.For the mapping side of things, you'll want to create a teleporter for your spawn point to sit on. All you need is a single SP Player 1 Start placed right on top of it, all players are gonna spawn in this spot and telefragging is disabled, so there's no need for more. For the end of your level, you'll do similar, just this time instead of a spawn point, you'll want to add an "Actor hits floor" activator. It should activate special 226 with a named script of "sp_nextlevel". Just be careful of any spaces else the script won't properly execute. (click to show/hide)QuoteTake care when designing your level segments such that they don't feel too daunting with length or difficulty. The standard of play is 3 lives per run, so you certainly don't want your segment to be the de facto run killer. However, make sure to include 1-Ups in your level if you want to encourage players to take risks for potential rewards. Don't forget to include a map card with your map as well, after all, players are gonna need the person to blame for when the run loses.Once your level segment is finished, implementing it into the Endless Attack segment pool is as easy as a single script. Make sure to drag the EA_MAPS.ACS source file into your own project, so that it can properly be imported into your own ACS file, but don't compile it. Once done, you'll want to create an ACS file that looks something like this TRL_IPRT.ACS,Quote from: TRL_IPRT.ACS#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMap("TRILL01");defineMap("TRILL02");defineMap("TRILL03");}This is the file that you'll wanna make sure that you compile every time you need to update it to introduce a new level segment. Make sure to create a LOADACS file as well, with the name of the compiled ACS file, so in this case, it would be a LOADACS file with the line "TRL_IPRT". This tells the game to make sure to load that compiled ACS with every startup.For a standard level segment, this is where you can stop and all will be well with its introduction. However, Endless Attack offers the option to include an increased difficulty layout for each map. The map variant is created the exact same way as before, but the ACS definition is a bit different. You can see an example of how you'd define TRILL01 and its variant EXTRLL01 in the following code,Quote from: TRL_IPRT.ACS#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMapWithEX("TRILL01", "EXTRLL01");defineMap("TRILL02");defineMap("TRILL03");}So every time TRILL01 is rolled for the next map, if the stage is also rolled to be a variant stage, it'll instead opt to use EXTRLL01 as the map.That's all there is to know for developing new level segments and there's plenty of existing content to reference if any issues are ran into, so good luck with the mapping!How do I make Sniper Joes stand still?A few of the enemies defined in Endless Attack have special behavior when you toggle their first argument. Here's a list of them, (click to show/hide)QuoteMetool - With its first argument toggled to 1, it'll immediately go back into its helmet after shooting rather than running around.Sniper Joe Shielder - With its first argument toggled to 1, it'll stand still between firing rounds.Sniper Joe Grenadier - With its first argument toggled to 1, it'll stand still between attacks. With its second argument toggled to 1, it'll only toss grenades without using the buster. Time Pendulum - The speed that the pendulum swings at is determined by its first argument.Hammer Joe - With its first argument toggled to 1, it'll stand still between tossing its hammers.Puyopon - With its first argument toggled to 1, it'll start on the ceiling wherever you placed it rather than beginning on the floor.Escaroo - With its first argument toggled to 1, it'll become invincible and only fire bombs around it in a spiral, good for creating a stationary hazard!Flamethrower Joe - With its first argument toggled to 1, it'll stand still between usages of the flamethrower.How do I develop new enemies for my maps? (click to show/hide)QuoteThe main important notes with enemy creation is to make use of the standardization already in place for enemies, otherwise your enemies might feel nonfunctional or out of place. To do this, make sure to inherit your enemy off of "GenericRobot" so that it receives the standard flags that most enemies in the project have. This makes sure that weapons will feel consistent against your enemies and they won't have any difficulties traversing your maps.Additionally, you'll want to make sure that each enemy you create calls "ACS_NamedExecuteAlways("sp_enemystart",0)" on its first frame, and preferably in a way such that it won't attempt to call the script again to reduce network usage. This script is responsible for multiple things, most importantly the HP scaling for additional players, and the pain feedback for when they receive damage. In the case that you want an enemy to not experience HP scaling, such as with respawning enemies, failing to call that script isn't a proper method of doing that. Instead, you should call "A_GiveInventory("HealthScaled",1)" first, then still proceed to call "ACS_NamedExecuteAlways("sp_enemystart",0)". For any instance of damage an enemy does, make sure that it is followed by "*ACS_NamedExecuteWithResult("sp_cbmHP")". This script checks whether or not a mod with higher base HP values, such as CBM, is loaded, and appropriately scales the damage so that it matches up. It allows your enemies to be compatible with more than just vanilla being ran with it. Additionally, make sure that it uses a damagetype of "Enemy", "Misc", or "FlingDamage". "Enemy" has standard hitstun, "Misc" has no hitstun, and "FlingDamage" applies a Wind Storm effect to the player. These ensure that your enemy won't be able to friendly fire any of their comrades on the field.Finally, make sure that you give your enemy a DoomEditor number that doesn't conflict with any existing enemies or props, alongside including "//$Category MM8BDM-Enemies" among its flags. This allows your enemy to be placed into maps and organized into a category with all other enemies from Endless Attack.For an example of how an enemy should look in this format, reference this Flamethrower Joe, (click to show/hide)Quoteactor FlamethrowerJoe : GenericRobot 30039{//$Category MM8BDM-EnemiesHeight 52Radius 32Obituary "%o was burnt to crisps by a Flamethrower Joe."Health 95translation "192:192=4:4", "198:198=227:227"maxstepheight 50+MISSILEEVENMORE+MISSILEMORE+LOOKALLAROUNDdropitem "WeaponEnergy", 16dropitem "SmallHealth", 16speed 8States{Spawn:TNT1 A 0TNT1 A 0 ACS_NamedExecuteAlways("sp_enemystart",0)Look:SNIP A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"See")loopSee:SNIP B 1SNIP B 1 A_JumpIf(Args[0]==1, "See2")SNIP B 1 //A_JumpIfTargetInLOS("ShieldUp") // THIS FUNCTION IS BUGGED ONLINE ARGH!SNIP BBBB 1 A_ChaseSNIP A 0// A_JumpIfTargetInLOS("ShieldUp")SNIP CCCC 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP DDDD 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP EEEE 1 A_ChaseGoto See+3See2:SNIP A 1 //A_JumpIfTargetInLOS("ShieldUp")Goto MissileShieldUp:Missile:TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetTNT1 A 0 A_GiveInventory("CutterFlag",1)TNT1 A 0 A_JumpIfInventory("CutterFlag",30,"ReadyFire")loopReadyFire:TNT1 A 0 A_TakeInventory("CutterFlag",999)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetSNIP A 0 A_JumpIfCloser(512,"ShieldFire")Goto ReadyFireShieldFire:SNIP FFFFF 5 A_FaceTargetSNIP F 0 A_FaceTargetSNIP F 0 A_PlaySoundEx("weapon/magmabazooka","Weapon")SNIP F 0 A_PlaySoundEx("weapon/heatshot","SoundSlot6")SNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 15SNIP F 1 A_JumpIf(Args[0]==0, "See")SNIP A 0 A_ClearTargetGoto LookDeath:CRAZ A 0 A_PainCRAZ A 0 A_FallTNT1 A 5 A_SpawnItem("EnemyDeathFX",0,32)TNT1 A -1stop}}actor JoeFlamethrower{PROJECTILE+BRIGHTDamage (3*ACS_NamedExecuteWithResult("sp_cbmHP"))Speed 30Obituary "%o was burnt to crisps by a Flamethrower Joe."damagetype "Enemy"Radius 20Height 20reactiontime 1scale 2.5States{Spawn:TNT1 A 0SpawnLoop:XE01 ABC 2 A_SpawnItemEx("JoeFlamethrowerFX",0,0,0,0,0,0,0,1)XE01 DEF 2 A_SpawnItemEx("JoeFlamethrowerFX2",0,0,0,0,0,0,0,1)XE01 F 1 A_CountdownwaitDeath:TNT1 A 1 A_SpawnItemEx("OilPitIgnite",0,0,8)stop}}actor JoeFlamethrowerFX : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 BCD 1 A_FadeOut(0.2)stop}}actor JoeFlamethrowerFX2 : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 EFG 1 A_FadeOut(0.2)stop}}If that's all you want out of your enemy, you can stop here. However, if you want your enemy to have harder AI and change colors as the run progresses, you'll need to define it as an EX enemy. This is done with some basic ACS definition in the same vein of defining a new level segment. You'll want to make sure to drag the EA_EX.ACS source file into your project, but again, don't compile it, it'll be used for importing instead. Then, you'll want to create a file that looks something like the following,Quote from: SP_IMPRT.ACS#library "sp_imprt"#include "zcommon.acs"#import "ea_ex.acs"#DEFINE MACHINEGUNJOE 10500#DEFINE MACHINEGUNJOESCOUT 10501#DEFINE MACHINEGUNJOEGRENADE 10502Script "sp_translations" OPEN CLIENTSIDE{Delay(25);CreateTranslation(MACHINEGUNJOE, 192:192=4:4, 198:198=42:42);CreateTranslation(MACHINEGUNJOESCOUT, 192:192=4:4, 198:198=171:171);CreateTranslation(MACHINEGUNJOEGRENADE, 192:192=4:4, 198:198=41:41);}Script "sp_imports" OPEN{Delay(5);defineEXEnemy("SniperJoeScout", "SniperJoeScoutEXPackage");defineEXEnemy("SniperJoeShielded", "SniperJoeShieldedEXPackage");defineEXEnemy("SniperJoeGrenader", "SniperJoeGrenaderEXPackage");defineEXEnemy("SniperArmor", "EXEnemyToggled");defineEXEnemy("MetEnemy", "EXEnemyToggled");}The defineEXEnemy function first specifies the actor name of the enemy, then the inventory item to give to that enemy to allow it to take on EX enemy traits. Since Sniper Armors only begin to walk when they've become an EX enemy, they only need the "EXEnemyToggled" flag that their AI checks for. However, you can see that since SniperJoeScout changes color as well, he is given a CustomInventory actor which you can see below,Quoteactor SniperJoeScoutEXPackage : CustomInventory{States{Pickup:TNT1 A 0 Thing_SetTranslation(0,10501)TNT1 A 0 A_GiveInventory("EXEnemyToggled",1)stop}}As a closing note, just make sure to design your enemies fairly so that they don't feel overbearing to deal with as a player. Bosses are where players are challenged in combat, so there's no need to create levels filled with crazy enemies out to take everyone's lives.How do I develop new bosses? (click to show/hide)QuoteCreating bosses is very similar to creating enemies, as in, it's just a matter of keeping to the standardization already set by the other bosses. You'll want to have your boss inherit off of BasicBoss and call its Spawn state and Intro states in a very similar way as to the other bosses do currently. This will ensure that the boss doesn't skip any important steps of the introduction sequence.Failure to follow that can lead to your boss not having proper pain feedback when hit, or not receiving the proper item to signify that their super is available for usage. You'd find yourself unable to "BossSuperAvailable50%" or "BossSuperAvailable25%".As with the enemies, any instance of damage the boss does should be followed by "*ACS_NamedExecuteWithResult("sp_cbmHP")", again, to ensure that their damage properly scales when mods such as CBM are in play. For an example of how a boss should look in this format, reference Needleman's boss, (click to show/hide)Quoteactor NeedlemanBoss : BasicBoss{Obituary "%o was pricked by \chNeedle Man\c-."speed 0States{Intro:NEED K 0NEED K 0 A_TakeInventory("BossIntroFlag", 1)NEED K 0 A_ChangeFlag("CANTSEEK", true)NEED K 0 A_ChangeFlag("INVULNERABLE", true)NEED K 1 A_JumpIf(floorz-z==0,"Intro2")waitIntro2:NEED A 5NEED I 5waitSpawn:NEED K 0NEED K 0 A_ChangeFlag("CANTSEEK", false)NEED K 0 A_ChangeFlag("INVULNERABLE", false)NEED K 0 A_JumpIfInventory("HealthScaled",1,"Spawn2")NEED K 0 ACS_NamedExecuteAlways("EA_BossManage", 0)NEED K 0 A_JumpIfInventory("BossIntroFlag", 1, "Intro")Spawn2:NEED K 0 A_ClearTargetNEED A 0 A_ChangeFlag("INVULNERABLE",0)NEED A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"Found")Goto Spawn2+2Found:NEED A 20Goto SeeSee:NEED A 10 A_FaceTargetNEED K 15 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)Goto Attack1SuperChance:NEED A 0 A_Jump(200,"Super")Goto SeeAttack1:NEED K 1CUTM J 0 A_JumpIf(floorz-z==0,"Land")NEED K 1NEED A 0 A_Jump(80,"Missile")NEED K 1loopMissile:NEED K 1NEED A 0 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 0 A_ChangeFlag("NOGRAVITY",1)NEED K 1 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED J 4 A_CustomMissile("NeedlemanNeedle",40,-28,random(-2,2))NEED K 4 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED L 4 A_CustomMissile("NeedlemanNeedle",40,28,random(-2,2))NEED K 4 A_FaceTargetNEED K 12 A_ChangeFlag("NOGRAVITY",0)Goto Attack1Land:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab")Goto LeapLand2:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab2")NEED A 0 A_JumpIfInventory("BossSuperAvailable50%",1,"SuperChance")Goto Spawn2Stab:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto LeapStab2:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto SeeLeap:NEED K 1 A_FaceTargetNEED A 1 //A_SetAngle(angle + random(-32,32))NEED A 0 A_ChangeVelocity(17.0,0.0,15.0,CVF_REPLACE|CVF_RELATIVE)NEED K 8Goto LeapingMelee:NEED K 5 A_SpawnItemEx("NeedlemanMelee")//A_CustomMeleeAttack(20)Goto LeapingLeaping:NEED K 1 A_JumpIf(floorz-z==0,"Land2")loopLeaping2:NEED K 1 A_JumpIf(floorz-z==0,"Spawn")loopSuper:NEED A 0 A_PlaySoundEx("enemy/bosssuper","Voice",0,1)NEED A 0 A_ChangeFlag("INVULNERABLE",1)NEED G 0 A_FaceTargetNEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 0 A_FaceTargetNEED K 10 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",1)NEED M 10 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)Goto SpinSpin:NEED Q 0 A_JumpIfInventory("CutterFlag",30,"Finish")NEED Q 0 A_PlaySoundEx("enemy/needlemanfire","Auto")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED Q 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice"NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED Q 0 A_GiveInventory("CutterFlag",1)loopFinish:NEED M 16 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",0)NEED M 0 A_TakeInventory("CutterFlag",999)NEED M 0 A_ChangeFlag("INVULNERABLE",0)Goto Leaping2}}actor NoPuff{-SOLID+NOINTERACTIONStates{Spawn:TNT1 A 0stop}}actor NeedlemanNeedle{PROJECTILE+RIPPERdamage (8*ACS_NamedExecuteWithResult("sp_cbmHP"))scale 2.5translation "13:246=4:4"damagetype "Enemy"Height 8Radius 8Speed 42States{Spawn:NHAR F 2loop}}actor NeedlemanMelee{+MISSILEdamage (0)scale 2.5damagetype "Enemy"Height 8Radius 8renderstyle noneStates{Spawn:NHAR A 2NHAR A 2 A_Explode(130*ACS_NamedExecuteWithResult("sp_cbmHP"),300,0)stop}}actor NeedlemanNeedle2 : NeedlemanNeedle{damage(6*ACS_NamedExecuteWithResult("sp_cbmHP"))}The next step in developing a boss would be to create a custom arena for it, it's important that this arena be a unique map from all other ones or at least have a different name, otherwise it'll overlap with other boss checks. To create this arena, just create a simple space for you to fight the boss in that has an SP Player 1 Start and a map spot with a tag of 1. The map spot is where the boss will always be spawned, so it's important to make sure that it exists and specifically has a tag of 1. You should also make sure that each sector that a player can land on within the arena has an "Actor hits floor" activator that calls the named script, "EA_BossActivate". Make sure that you don't leave any space when entering that script name or else it'll fail to be recognized. This is necessary to make sure that the boss is properly started once players land into the arena. (click to show/hide)QuoteThe final prerequisite of implementing your boss is to create a server variable in CVARINFO that can log whether or not your boss has been defeated. This is important so that players can properly gain the boss's weapon upon his death. It should look as follows,Quoteserver noarchive bool ea_needleman = false;Once those three aspects are in place, you can start the ACS definition needed to properly implement the boss. As like the other import files, you'll wanna start by dragging EA_BOSS.ACS into your project, but don't compile it. Then you'll want to create a new file that looks as such,Quote#library "sp_imprt"#include "zcommon.acs"#import "ea_boss.acs"Script "sp_imports" OPEN{Delay(5);defineBoss("CUTBOSS","ea_cutman","RollingCutterWep","RollingCutterWepC","CutmanBoss","RollingCutterWep","TripleCutterWep","CutterArmWep","GroundCutterWep","DuoCutterWep","GargantuanCutterWep","CutterTwinsWep","CondorSlicerWep","RollingCutterWep");defineBoss("HEABOSS","ea_heatman","AtomicFireWep","AtomicFireWepC","HeatmanBoss","AtomicFireWep","AtomicFlamesWep","AtomicDashWep","AtomicRadianceWep","AtomicExcursionWep","AtomicWaveWep","AtomicOverheatWep","AtomicChainWep","AtomicFireWep");defineBoss("NEEBOSS","ea_needleman","NeedleCannonWep","NeedleCannonWepC","NeedlemanBoss","NeedleCannonWep","NeedleBombWep","NeedleSprayerWep","PunctureNeedleWep","NeedleHammerWep","NeedleTwisterWep","NeedleRingWep","RevolverNeedleWep","NeedleCannonWep");defineBoss("TOABOSS","ea_toadman","RainFlushWep","RainFlushWepC","ToadmanBoss","RainFlushWep","FlushGrenadeWep","RainTorrentWep","RainShowerWep","JusticeRainWep","FloodFlushWep","FlushRestoreWep","RiotFlushWep","RainFlushWep");}You'll notice that each call of that function has a lot of parameters, and that's to support the Megaman Singleplayer Classes which need to know which copywep each class should get. As a quick cheat sheet for what each parameter means, refer to this list.Quote// 0 - Map code that your boss is fought on (Ex. CUTBOSS)// 1 - CVAR to toggle and check for your boss being defeated (Ex. ea_cutman)// 2 - Vanilla weapon name according to the engine (Ex. RollingCutterWep)// 3 - CBM weapon name according to the engine (Ex. RollingCutterWepC)// 4 - Boss actor to be spawned according to the engine (Ex. CutmanBoss)// 5 - Megaman weapon name according to the engine (Ex. RollingCutterWep)// 6 - Protoman weapon name according to the engine (Ex. TripleCutterWep)// 7 - Bass weapon name according to the engine (Ex. CutterArmWep)// 8 - Roll weapon name according to the engine (Ex. GroundCutterWep)// 9 - Duo weapon name according to the engine (Ex. DuoCutterWep)// 10 - DocRobot weapon name according to the engine (Ex. GargantuanCutterWep)// 11 - Darkman weapon name according to the engine (Ex. CutterTwinsWep)// 12 - Fakeman weapon name according to the engine (Ex. CondorSlicerWep)// 13 - BBAMegaman weapon name according to the engine (Ex. TrollingCutterWep)If you know that a weapon doesn't exist for your boss for a certain class, then it is best to resort to a weapon that can be found in vanilla until it's implemented. Make sure to give a shoutout to Blews#9696 so he can know that he's got new weapons to make for the Megaman Singleplayer classes.That's the bulk of what's needed for the implementation of a boss, so have fun creating new capstone challenges for players!Cossack Crusaders?I figured a quick Mission Mode inspired project would be the perfect thing to get myself and some other people around the community inspired for development again. Progress is going forward with resolve, but now I'm sure everyone will have this gameplay style in the front of their mind again.
Creating new level segments is relatively painless as long as you're experienced with Doom Builder already and have some basic ACS knowledge. Just don't forget to load the Endless Attack file as a resource so you'll have access to all the enemies within the build.For the mapping side of things, you'll want to create a teleporter for your spawn point to sit on. All you need is a single SP Player 1 Start placed right on top of it, all players are gonna spawn in this spot and telefragging is disabled, so there's no need for more. For the end of your level, you'll do similar, just this time instead of a spawn point, you'll want to add an "Actor hits floor" activator. It should activate special 226 with a named script of "sp_nextlevel". Just be careful of any spaces else the script won't properly execute. (click to show/hide)QuoteTake care when designing your level segments such that they don't feel too daunting with length or difficulty. The standard of play is 3 lives per run, so you certainly don't want your segment to be the de facto run killer. However, make sure to include 1-Ups in your level if you want to encourage players to take risks for potential rewards. Don't forget to include a map card with your map as well, after all, players are gonna need the person to blame for when the run loses.Once your level segment is finished, implementing it into the Endless Attack segment pool is as easy as a single script. Make sure to drag the EA_MAPS.ACS source file into your own project, so that it can properly be imported into your own ACS file, but don't compile it. Once done, you'll want to create an ACS file that looks something like this TRL_IPRT.ACS,Quote from: TRL_IPRT.ACS#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMap("TRILL01");defineMap("TRILL02");defineMap("TRILL03");}This is the file that you'll wanna make sure that you compile every time you need to update it to introduce a new level segment. Make sure to create a LOADACS file as well, with the name of the compiled ACS file, so in this case, it would be a LOADACS file with the line "TRL_IPRT". This tells the game to make sure to load that compiled ACS with every startup.For a standard level segment, this is where you can stop and all will be well with its introduction. However, Endless Attack offers the option to include an increased difficulty layout for each map. The map variant is created the exact same way as before, but the ACS definition is a bit different. You can see an example of how you'd define TRILL01 and its variant EXTRLL01 in the following code,Quote from: TRL_IPRT.ACS#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMapWithEX("TRILL01", "EXTRLL01");defineMap("TRILL02");defineMap("TRILL03");}So every time TRILL01 is rolled for the next map, if the stage is also rolled to be a variant stage, it'll instead opt to use EXTRLL01 as the map.That's all there is to know for developing new level segments and there's plenty of existing content to reference if any issues are ran into, so good luck with the mapping!
#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMap("TRILL01");defineMap("TRILL02");defineMap("TRILL03");}
#library "trl_iprt"#include "zcommon.acs"#import "ea_maps.acs"Script "trill_imports" OPEN{Delay(5);defineMapWithEX("TRILL01", "EXTRLL01");defineMap("TRILL02");defineMap("TRILL03");}
Metool - With its first argument toggled to 1, it'll immediately go back into its helmet after shooting rather than running around.Sniper Joe Shielder - With its first argument toggled to 1, it'll stand still between firing rounds.Sniper Joe Grenadier - With its first argument toggled to 1, it'll stand still between attacks. With its second argument toggled to 1, it'll only toss grenades without using the buster. Time Pendulum - The speed that the pendulum swings at is determined by its first argument.Hammer Joe - With its first argument toggled to 1, it'll stand still between tossing its hammers.Puyopon - With its first argument toggled to 1, it'll start on the ceiling wherever you placed it rather than beginning on the floor.Escaroo - With its first argument toggled to 1, it'll become invincible and only fire bombs around it in a spiral, good for creating a stationary hazard!Flamethrower Joe - With its first argument toggled to 1, it'll stand still between usages of the flamethrower.
The main important notes with enemy creation is to make use of the standardization already in place for enemies, otherwise your enemies might feel nonfunctional or out of place. To do this, make sure to inherit your enemy off of "GenericRobot" so that it receives the standard flags that most enemies in the project have. This makes sure that weapons will feel consistent against your enemies and they won't have any difficulties traversing your maps.Additionally, you'll want to make sure that each enemy you create calls "ACS_NamedExecuteAlways("sp_enemystart",0)" on its first frame, and preferably in a way such that it won't attempt to call the script again to reduce network usage. This script is responsible for multiple things, most importantly the HP scaling for additional players, and the pain feedback for when they receive damage. In the case that you want an enemy to not experience HP scaling, such as with respawning enemies, failing to call that script isn't a proper method of doing that. Instead, you should call "A_GiveInventory("HealthScaled",1)" first, then still proceed to call "ACS_NamedExecuteAlways("sp_enemystart",0)". For any instance of damage an enemy does, make sure that it is followed by "*ACS_NamedExecuteWithResult("sp_cbmHP")". This script checks whether or not a mod with higher base HP values, such as CBM, is loaded, and appropriately scales the damage so that it matches up. It allows your enemies to be compatible with more than just vanilla being ran with it. Additionally, make sure that it uses a damagetype of "Enemy", "Misc", or "FlingDamage". "Enemy" has standard hitstun, "Misc" has no hitstun, and "FlingDamage" applies a Wind Storm effect to the player. These ensure that your enemy won't be able to friendly fire any of their comrades on the field.Finally, make sure that you give your enemy a DoomEditor number that doesn't conflict with any existing enemies or props, alongside including "//$Category MM8BDM-Enemies" among its flags. This allows your enemy to be placed into maps and organized into a category with all other enemies from Endless Attack.For an example of how an enemy should look in this format, reference this Flamethrower Joe, (click to show/hide)Quoteactor FlamethrowerJoe : GenericRobot 30039{//$Category MM8BDM-EnemiesHeight 52Radius 32Obituary "%o was burnt to crisps by a Flamethrower Joe."Health 95translation "192:192=4:4", "198:198=227:227"maxstepheight 50+MISSILEEVENMORE+MISSILEMORE+LOOKALLAROUNDdropitem "WeaponEnergy", 16dropitem "SmallHealth", 16speed 8States{Spawn:TNT1 A 0TNT1 A 0 ACS_NamedExecuteAlways("sp_enemystart",0)Look:SNIP A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"See")loopSee:SNIP B 1SNIP B 1 A_JumpIf(Args[0]==1, "See2")SNIP B 1 //A_JumpIfTargetInLOS("ShieldUp") // THIS FUNCTION IS BUGGED ONLINE ARGH!SNIP BBBB 1 A_ChaseSNIP A 0// A_JumpIfTargetInLOS("ShieldUp")SNIP CCCC 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP DDDD 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP EEEE 1 A_ChaseGoto See+3See2:SNIP A 1 //A_JumpIfTargetInLOS("ShieldUp")Goto MissileShieldUp:Missile:TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetTNT1 A 0 A_GiveInventory("CutterFlag",1)TNT1 A 0 A_JumpIfInventory("CutterFlag",30,"ReadyFire")loopReadyFire:TNT1 A 0 A_TakeInventory("CutterFlag",999)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetSNIP A 0 A_JumpIfCloser(512,"ShieldFire")Goto ReadyFireShieldFire:SNIP FFFFF 5 A_FaceTargetSNIP F 0 A_FaceTargetSNIP F 0 A_PlaySoundEx("weapon/magmabazooka","Weapon")SNIP F 0 A_PlaySoundEx("weapon/heatshot","SoundSlot6")SNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 15SNIP F 1 A_JumpIf(Args[0]==0, "See")SNIP A 0 A_ClearTargetGoto LookDeath:CRAZ A 0 A_PainCRAZ A 0 A_FallTNT1 A 5 A_SpawnItem("EnemyDeathFX",0,32)TNT1 A -1stop}}actor JoeFlamethrower{PROJECTILE+BRIGHTDamage (3*ACS_NamedExecuteWithResult("sp_cbmHP"))Speed 30Obituary "%o was burnt to crisps by a Flamethrower Joe."damagetype "Enemy"Radius 20Height 20reactiontime 1scale 2.5States{Spawn:TNT1 A 0SpawnLoop:XE01 ABC 2 A_SpawnItemEx("JoeFlamethrowerFX",0,0,0,0,0,0,0,1)XE01 DEF 2 A_SpawnItemEx("JoeFlamethrowerFX2",0,0,0,0,0,0,0,1)XE01 F 1 A_CountdownwaitDeath:TNT1 A 1 A_SpawnItemEx("OilPitIgnite",0,0,8)stop}}actor JoeFlamethrowerFX : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 BCD 1 A_FadeOut(0.2)stop}}actor JoeFlamethrowerFX2 : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 EFG 1 A_FadeOut(0.2)stop}}If that's all you want out of your enemy, you can stop here. However, if you want your enemy to have harder AI and change colors as the run progresses, you'll need to define it as an EX enemy. This is done with some basic ACS definition in the same vein of defining a new level segment. You'll want to make sure to drag the EA_EX.ACS source file into your project, but again, don't compile it, it'll be used for importing instead. Then, you'll want to create a file that looks something like the following,Quote from: SP_IMPRT.ACS#library "sp_imprt"#include "zcommon.acs"#import "ea_ex.acs"#DEFINE MACHINEGUNJOE 10500#DEFINE MACHINEGUNJOESCOUT 10501#DEFINE MACHINEGUNJOEGRENADE 10502Script "sp_translations" OPEN CLIENTSIDE{Delay(25);CreateTranslation(MACHINEGUNJOE, 192:192=4:4, 198:198=42:42);CreateTranslation(MACHINEGUNJOESCOUT, 192:192=4:4, 198:198=171:171);CreateTranslation(MACHINEGUNJOEGRENADE, 192:192=4:4, 198:198=41:41);}Script "sp_imports" OPEN{Delay(5);defineEXEnemy("SniperJoeScout", "SniperJoeScoutEXPackage");defineEXEnemy("SniperJoeShielded", "SniperJoeShieldedEXPackage");defineEXEnemy("SniperJoeGrenader", "SniperJoeGrenaderEXPackage");defineEXEnemy("SniperArmor", "EXEnemyToggled");defineEXEnemy("MetEnemy", "EXEnemyToggled");}The defineEXEnemy function first specifies the actor name of the enemy, then the inventory item to give to that enemy to allow it to take on EX enemy traits. Since Sniper Armors only begin to walk when they've become an EX enemy, they only need the "EXEnemyToggled" flag that their AI checks for. However, you can see that since SniperJoeScout changes color as well, he is given a CustomInventory actor which you can see below,Quoteactor SniperJoeScoutEXPackage : CustomInventory{States{Pickup:TNT1 A 0 Thing_SetTranslation(0,10501)TNT1 A 0 A_GiveInventory("EXEnemyToggled",1)stop}}As a closing note, just make sure to design your enemies fairly so that they don't feel overbearing to deal with as a player. Bosses are where players are challenged in combat, so there's no need to create levels filled with crazy enemies out to take everyone's lives.
actor FlamethrowerJoe : GenericRobot 30039{//$Category MM8BDM-EnemiesHeight 52Radius 32Obituary "%o was burnt to crisps by a Flamethrower Joe."Health 95translation "192:192=4:4", "198:198=227:227"maxstepheight 50+MISSILEEVENMORE+MISSILEMORE+LOOKALLAROUNDdropitem "WeaponEnergy", 16dropitem "SmallHealth", 16speed 8States{Spawn:TNT1 A 0TNT1 A 0 ACS_NamedExecuteAlways("sp_enemystart",0)Look:SNIP A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"See")loopSee:SNIP B 1SNIP B 1 A_JumpIf(Args[0]==1, "See2")SNIP B 1 //A_JumpIfTargetInLOS("ShieldUp") // THIS FUNCTION IS BUGGED ONLINE ARGH!SNIP BBBB 1 A_ChaseSNIP A 0// A_JumpIfTargetInLOS("ShieldUp")SNIP CCCC 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP DDDD 1 A_ChaseSNIP A 0 //A_JumpIfTargetInLOS("ShieldUp")SNIP EEEE 1 A_ChaseGoto See+3See2:SNIP A 1 //A_JumpIfTargetInLOS("ShieldUp")Goto MissileShieldUp:Missile:TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetTNT1 A 0 A_GiveInventory("CutterFlag",1)TNT1 A 0 A_JumpIfInventory("CutterFlag",30,"ReadyFire")loopReadyFire:TNT1 A 0 A_TakeInventory("CutterFlag",999)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM, 0)TNT1 A 0 A_SpawnItemEx("SP_SniperJoeShieldFX",23,-17,8,momx, momy, momz, 0, SXF_NOCHECKPOSITION|SXF_ABSOLUTEMOMENTUM|SXF_TRANSFERTRANSLATION, 0)SNIP A 2 A_FaceTargetSNIP A 0 A_JumpIfCloser(512,"ShieldFire")Goto ReadyFireShieldFire:SNIP FFFFF 5 A_FaceTargetSNIP F 0 A_FaceTargetSNIP F 0 A_PlaySoundEx("weapon/magmabazooka","Weapon")SNIP F 0 A_PlaySoundEx("weapon/heatshot","SoundSlot6")SNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 0 A_FaceTargetSNIP F 2 A_CustomMissile("JoeFlamethrower",12,6,frandom(-3,3),0,frandom(-3,3))SNIP F 15SNIP F 1 A_JumpIf(Args[0]==0, "See")SNIP A 0 A_ClearTargetGoto LookDeath:CRAZ A 0 A_PainCRAZ A 0 A_FallTNT1 A 5 A_SpawnItem("EnemyDeathFX",0,32)TNT1 A -1stop}}actor JoeFlamethrower{PROJECTILE+BRIGHTDamage (3*ACS_NamedExecuteWithResult("sp_cbmHP"))Speed 30Obituary "%o was burnt to crisps by a Flamethrower Joe."damagetype "Enemy"Radius 20Height 20reactiontime 1scale 2.5States{Spawn:TNT1 A 0SpawnLoop:XE01 ABC 2 A_SpawnItemEx("JoeFlamethrowerFX",0,0,0,0,0,0,0,1)XE01 DEF 2 A_SpawnItemEx("JoeFlamethrowerFX2",0,0,0,0,0,0,0,1)XE01 F 1 A_CountdownwaitDeath:TNT1 A 1 A_SpawnItemEx("OilPitIgnite",0,0,8)stop}}actor JoeFlamethrowerFX : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 BCD 1 A_FadeOut(0.2)stop}}actor JoeFlamethrowerFX2 : BasicGraphicEffect{+BRIGHTrenderstyle addAlpha 0.8states{Spawn:XE01 A 0XE01 EFG 1 A_FadeOut(0.2)stop}}
#library "sp_imprt"#include "zcommon.acs"#import "ea_ex.acs"#DEFINE MACHINEGUNJOE 10500#DEFINE MACHINEGUNJOESCOUT 10501#DEFINE MACHINEGUNJOEGRENADE 10502Script "sp_translations" OPEN CLIENTSIDE{Delay(25);CreateTranslation(MACHINEGUNJOE, 192:192=4:4, 198:198=42:42);CreateTranslation(MACHINEGUNJOESCOUT, 192:192=4:4, 198:198=171:171);CreateTranslation(MACHINEGUNJOEGRENADE, 192:192=4:4, 198:198=41:41);}Script "sp_imports" OPEN{Delay(5);defineEXEnemy("SniperJoeScout", "SniperJoeScoutEXPackage");defineEXEnemy("SniperJoeShielded", "SniperJoeShieldedEXPackage");defineEXEnemy("SniperJoeGrenader", "SniperJoeGrenaderEXPackage");defineEXEnemy("SniperArmor", "EXEnemyToggled");defineEXEnemy("MetEnemy", "EXEnemyToggled");}
actor SniperJoeScoutEXPackage : CustomInventory{States{Pickup:TNT1 A 0 Thing_SetTranslation(0,10501)TNT1 A 0 A_GiveInventory("EXEnemyToggled",1)stop}}
Creating bosses is very similar to creating enemies, as in, it's just a matter of keeping to the standardization already set by the other bosses. You'll want to have your boss inherit off of BasicBoss and call its Spawn state and Intro states in a very similar way as to the other bosses do currently. This will ensure that the boss doesn't skip any important steps of the introduction sequence.Failure to follow that can lead to your boss not having proper pain feedback when hit, or not receiving the proper item to signify that their super is available for usage. You'd find yourself unable to "BossSuperAvailable50%" or "BossSuperAvailable25%".As with the enemies, any instance of damage the boss does should be followed by "*ACS_NamedExecuteWithResult("sp_cbmHP")", again, to ensure that their damage properly scales when mods such as CBM are in play. For an example of how a boss should look in this format, reference Needleman's boss, (click to show/hide)Quoteactor NeedlemanBoss : BasicBoss{Obituary "%o was pricked by \chNeedle Man\c-."speed 0States{Intro:NEED K 0NEED K 0 A_TakeInventory("BossIntroFlag", 1)NEED K 0 A_ChangeFlag("CANTSEEK", true)NEED K 0 A_ChangeFlag("INVULNERABLE", true)NEED K 1 A_JumpIf(floorz-z==0,"Intro2")waitIntro2:NEED A 5NEED I 5waitSpawn:NEED K 0NEED K 0 A_ChangeFlag("CANTSEEK", false)NEED K 0 A_ChangeFlag("INVULNERABLE", false)NEED K 0 A_JumpIfInventory("HealthScaled",1,"Spawn2")NEED K 0 ACS_NamedExecuteAlways("EA_BossManage", 0)NEED K 0 A_JumpIfInventory("BossIntroFlag", 1, "Intro")Spawn2:NEED K 0 A_ClearTargetNEED A 0 A_ChangeFlag("INVULNERABLE",0)NEED A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"Found")Goto Spawn2+2Found:NEED A 20Goto SeeSee:NEED A 10 A_FaceTargetNEED K 15 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)Goto Attack1SuperChance:NEED A 0 A_Jump(200,"Super")Goto SeeAttack1:NEED K 1CUTM J 0 A_JumpIf(floorz-z==0,"Land")NEED K 1NEED A 0 A_Jump(80,"Missile")NEED K 1loopMissile:NEED K 1NEED A 0 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 0 A_ChangeFlag("NOGRAVITY",1)NEED K 1 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED J 4 A_CustomMissile("NeedlemanNeedle",40,-28,random(-2,2))NEED K 4 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED L 4 A_CustomMissile("NeedlemanNeedle",40,28,random(-2,2))NEED K 4 A_FaceTargetNEED K 12 A_ChangeFlag("NOGRAVITY",0)Goto Attack1Land:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab")Goto LeapLand2:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab2")NEED A 0 A_JumpIfInventory("BossSuperAvailable50%",1,"SuperChance")Goto Spawn2Stab:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto LeapStab2:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto SeeLeap:NEED K 1 A_FaceTargetNEED A 1 //A_SetAngle(angle + random(-32,32))NEED A 0 A_ChangeVelocity(17.0,0.0,15.0,CVF_REPLACE|CVF_RELATIVE)NEED K 8Goto LeapingMelee:NEED K 5 A_SpawnItemEx("NeedlemanMelee")//A_CustomMeleeAttack(20)Goto LeapingLeaping:NEED K 1 A_JumpIf(floorz-z==0,"Land2")loopLeaping2:NEED K 1 A_JumpIf(floorz-z==0,"Spawn")loopSuper:NEED A 0 A_PlaySoundEx("enemy/bosssuper","Voice",0,1)NEED A 0 A_ChangeFlag("INVULNERABLE",1)NEED G 0 A_FaceTargetNEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 0 A_FaceTargetNEED K 10 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",1)NEED M 10 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)Goto SpinSpin:NEED Q 0 A_JumpIfInventory("CutterFlag",30,"Finish")NEED Q 0 A_PlaySoundEx("enemy/needlemanfire","Auto")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED Q 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice"NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED Q 0 A_GiveInventory("CutterFlag",1)loopFinish:NEED M 16 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",0)NEED M 0 A_TakeInventory("CutterFlag",999)NEED M 0 A_ChangeFlag("INVULNERABLE",0)Goto Leaping2}}actor NoPuff{-SOLID+NOINTERACTIONStates{Spawn:TNT1 A 0stop}}actor NeedlemanNeedle{PROJECTILE+RIPPERdamage (8*ACS_NamedExecuteWithResult("sp_cbmHP"))scale 2.5translation "13:246=4:4"damagetype "Enemy"Height 8Radius 8Speed 42States{Spawn:NHAR F 2loop}}actor NeedlemanMelee{+MISSILEdamage (0)scale 2.5damagetype "Enemy"Height 8Radius 8renderstyle noneStates{Spawn:NHAR A 2NHAR A 2 A_Explode(130*ACS_NamedExecuteWithResult("sp_cbmHP"),300,0)stop}}actor NeedlemanNeedle2 : NeedlemanNeedle{damage(6*ACS_NamedExecuteWithResult("sp_cbmHP"))}The next step in developing a boss would be to create a custom arena for it, it's important that this arena be a unique map from all other ones or at least have a different name, otherwise it'll overlap with other boss checks. To create this arena, just create a simple space for you to fight the boss in that has an SP Player 1 Start and a map spot with a tag of 1. The map spot is where the boss will always be spawned, so it's important to make sure that it exists and specifically has a tag of 1. You should also make sure that each sector that a player can land on within the arena has an "Actor hits floor" activator that calls the named script, "EA_BossActivate". Make sure that you don't leave any space when entering that script name or else it'll fail to be recognized. This is necessary to make sure that the boss is properly started once players land into the arena. (click to show/hide)QuoteThe final prerequisite of implementing your boss is to create a server variable in CVARINFO that can log whether or not your boss has been defeated. This is important so that players can properly gain the boss's weapon upon his death. It should look as follows,Quoteserver noarchive bool ea_needleman = false;Once those three aspects are in place, you can start the ACS definition needed to properly implement the boss. As like the other import files, you'll wanna start by dragging EA_BOSS.ACS into your project, but don't compile it. Then you'll want to create a new file that looks as such,Quote#library "sp_imprt"#include "zcommon.acs"#import "ea_boss.acs"Script "sp_imports" OPEN{Delay(5);defineBoss("CUTBOSS","ea_cutman","RollingCutterWep","RollingCutterWepC","CutmanBoss","RollingCutterWep","TripleCutterWep","CutterArmWep","GroundCutterWep","DuoCutterWep","GargantuanCutterWep","CutterTwinsWep","CondorSlicerWep","RollingCutterWep");defineBoss("HEABOSS","ea_heatman","AtomicFireWep","AtomicFireWepC","HeatmanBoss","AtomicFireWep","AtomicFlamesWep","AtomicDashWep","AtomicRadianceWep","AtomicExcursionWep","AtomicWaveWep","AtomicOverheatWep","AtomicChainWep","AtomicFireWep");defineBoss("NEEBOSS","ea_needleman","NeedleCannonWep","NeedleCannonWepC","NeedlemanBoss","NeedleCannonWep","NeedleBombWep","NeedleSprayerWep","PunctureNeedleWep","NeedleHammerWep","NeedleTwisterWep","NeedleRingWep","RevolverNeedleWep","NeedleCannonWep");defineBoss("TOABOSS","ea_toadman","RainFlushWep","RainFlushWepC","ToadmanBoss","RainFlushWep","FlushGrenadeWep","RainTorrentWep","RainShowerWep","JusticeRainWep","FloodFlushWep","FlushRestoreWep","RiotFlushWep","RainFlushWep");}You'll notice that each call of that function has a lot of parameters, and that's to support the Megaman Singleplayer Classes which need to know which copywep each class should get. As a quick cheat sheet for what each parameter means, refer to this list.Quote// 0 - Map code that your boss is fought on (Ex. CUTBOSS)// 1 - CVAR to toggle and check for your boss being defeated (Ex. ea_cutman)// 2 - Vanilla weapon name according to the engine (Ex. RollingCutterWep)// 3 - CBM weapon name according to the engine (Ex. RollingCutterWepC)// 4 - Boss actor to be spawned according to the engine (Ex. CutmanBoss)// 5 - Megaman weapon name according to the engine (Ex. RollingCutterWep)// 6 - Protoman weapon name according to the engine (Ex. TripleCutterWep)// 7 - Bass weapon name according to the engine (Ex. CutterArmWep)// 8 - Roll weapon name according to the engine (Ex. GroundCutterWep)// 9 - Duo weapon name according to the engine (Ex. DuoCutterWep)// 10 - DocRobot weapon name according to the engine (Ex. GargantuanCutterWep)// 11 - Darkman weapon name according to the engine (Ex. CutterTwinsWep)// 12 - Fakeman weapon name according to the engine (Ex. CondorSlicerWep)// 13 - BBAMegaman weapon name according to the engine (Ex. TrollingCutterWep)If you know that a weapon doesn't exist for your boss for a certain class, then it is best to resort to a weapon that can be found in vanilla until it's implemented. Make sure to give a shoutout to Blews#9696 so he can know that he's got new weapons to make for the Megaman Singleplayer classes.That's the bulk of what's needed for the implementation of a boss, so have fun creating new capstone challenges for players!
actor NeedlemanBoss : BasicBoss{Obituary "%o was pricked by \chNeedle Man\c-."speed 0States{Intro:NEED K 0NEED K 0 A_TakeInventory("BossIntroFlag", 1)NEED K 0 A_ChangeFlag("CANTSEEK", true)NEED K 0 A_ChangeFlag("INVULNERABLE", true)NEED K 1 A_JumpIf(floorz-z==0,"Intro2")waitIntro2:NEED A 5NEED I 5waitSpawn:NEED K 0NEED K 0 A_ChangeFlag("CANTSEEK", false)NEED K 0 A_ChangeFlag("INVULNERABLE", false)NEED K 0 A_JumpIfInventory("HealthScaled",1,"Spawn2")NEED K 0 ACS_NamedExecuteAlways("EA_BossManage", 0)NEED K 0 A_JumpIfInventory("BossIntroFlag", 1, "Intro")Spawn2:NEED K 0 A_ClearTargetNEED A 0 A_ChangeFlag("INVULNERABLE",0)NEED A 5 A_LookEx(LOF_NOSOUNDCHECK,0,0,0,360,"Found")Goto Spawn2+2Found:NEED A 20Goto SeeSee:NEED A 10 A_FaceTargetNEED K 15 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)Goto Attack1SuperChance:NEED A 0 A_Jump(200,"Super")Goto SeeAttack1:NEED K 1CUTM J 0 A_JumpIf(floorz-z==0,"Land")NEED K 1NEED A 0 A_Jump(80,"Missile")NEED K 1loopMissile:NEED K 1NEED A 0 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 0 A_ChangeFlag("NOGRAVITY",1)NEED K 1 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED J 4 A_CustomMissile("NeedlemanNeedle",40,-28,random(-2,2))NEED K 4 A_FaceTargetNEED K 0 A_PlaySoundEx("enemy/needlemanfire","Voice")NEED L 4 A_CustomMissile("NeedlemanNeedle",40,28,random(-2,2))NEED K 4 A_FaceTargetNEED K 12 A_ChangeFlag("NOGRAVITY",0)Goto Attack1Land:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab")Goto LeapLand2:NEED A 1NEED A 3 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED A 1NEED AAAA 2 A_LookEx(LOF_NOSOUNDCHECK,0,190,0,360,"Stab2")NEED A 0 A_JumpIfInventory("BossSuperAvailable50%",1,"SuperChance")Goto Spawn2Stab:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto LeapStab2:NEED P 5 A_FaceTargetNEED P 0 A_PlaySoundEx("weapon/slashclaw","Voice")NEED O 5 A_CustomBulletAttack(0,0,1,15*ACS_NamedExecuteWithResult("sp_cbmHP"),"NoPuff",250)NEED P 5 A_FaceTargetNEED A 15Goto SeeLeap:NEED K 1 A_FaceTargetNEED A 1 //A_SetAngle(angle + random(-32,32))NEED A 0 A_ChangeVelocity(17.0,0.0,15.0,CVF_REPLACE|CVF_RELATIVE)NEED K 8Goto LeapingMelee:NEED K 5 A_SpawnItemEx("NeedlemanMelee")//A_CustomMeleeAttack(20)Goto LeapingLeaping:NEED K 1 A_JumpIf(floorz-z==0,"Land2")loopLeaping2:NEED K 1 A_JumpIf(floorz-z==0,"Spawn")loopSuper:NEED A 0 A_PlaySoundEx("enemy/bosssuper","Voice",0,1)NEED A 0 A_ChangeFlag("INVULNERABLE",1)NEED G 0 A_FaceTargetNEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 4 Thing_SetTranslation(0,62)NEED A 4 Thing_SetTranslation(0,0)NEED A 0 A_FaceTargetNEED K 10 A_ChangeVelocity(0.0,0.0,19.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",1)NEED M 10 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)Goto SpinSpin:NEED Q 0 A_JumpIfInventory("CutterFlag",30,"Finish")NEED Q 0 A_PlaySoundEx("enemy/needlemanfire","Auto")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED Q 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QQ 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED Q 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice"NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED R 0 //A_PlaySoundEx("enemy/needlemanfire","Voice")NEED QR 0 A_SpawnItemEx("NeedlemanNeedle2",0,0,32,14,0,random(-3,-6),random(0,360))NEED R 1NEED Q 0 A_GiveInventory("CutterFlag",1)loopFinish:NEED M 16 A_ChangeVelocity(0.0,0.0,0.0,CVF_REPLACE|CVF_RELATIVE)NEED K 0 A_ChangeFlag("NOGRAVITY",0)NEED M 0 A_TakeInventory("CutterFlag",999)NEED M 0 A_ChangeFlag("INVULNERABLE",0)Goto Leaping2}}actor NoPuff{-SOLID+NOINTERACTIONStates{Spawn:TNT1 A 0stop}}actor NeedlemanNeedle{PROJECTILE+RIPPERdamage (8*ACS_NamedExecuteWithResult("sp_cbmHP"))scale 2.5translation "13:246=4:4"damagetype "Enemy"Height 8Radius 8Speed 42States{Spawn:NHAR F 2loop}}actor NeedlemanMelee{+MISSILEdamage (0)scale 2.5damagetype "Enemy"Height 8Radius 8renderstyle noneStates{Spawn:NHAR A 2NHAR A 2 A_Explode(130*ACS_NamedExecuteWithResult("sp_cbmHP"),300,0)stop}}actor NeedlemanNeedle2 : NeedlemanNeedle{damage(6*ACS_NamedExecuteWithResult("sp_cbmHP"))}
server noarchive bool ea_needleman = false;
#library "sp_imprt"#include "zcommon.acs"#import "ea_boss.acs"Script "sp_imports" OPEN{Delay(5);defineBoss("CUTBOSS","ea_cutman","RollingCutterWep","RollingCutterWepC","CutmanBoss","RollingCutterWep","TripleCutterWep","CutterArmWep","GroundCutterWep","DuoCutterWep","GargantuanCutterWep","CutterTwinsWep","CondorSlicerWep","RollingCutterWep");defineBoss("HEABOSS","ea_heatman","AtomicFireWep","AtomicFireWepC","HeatmanBoss","AtomicFireWep","AtomicFlamesWep","AtomicDashWep","AtomicRadianceWep","AtomicExcursionWep","AtomicWaveWep","AtomicOverheatWep","AtomicChainWep","AtomicFireWep");defineBoss("NEEBOSS","ea_needleman","NeedleCannonWep","NeedleCannonWepC","NeedlemanBoss","NeedleCannonWep","NeedleBombWep","NeedleSprayerWep","PunctureNeedleWep","NeedleHammerWep","NeedleTwisterWep","NeedleRingWep","RevolverNeedleWep","NeedleCannonWep");defineBoss("TOABOSS","ea_toadman","RainFlushWep","RainFlushWepC","ToadmanBoss","RainFlushWep","FlushGrenadeWep","RainTorrentWep","RainShowerWep","JusticeRainWep","FloodFlushWep","FlushRestoreWep","RiotFlushWep","RainFlushWep");}
// 0 - Map code that your boss is fought on (Ex. CUTBOSS)// 1 - CVAR to toggle and check for your boss being defeated (Ex. ea_cutman)// 2 - Vanilla weapon name according to the engine (Ex. RollingCutterWep)// 3 - CBM weapon name according to the engine (Ex. RollingCutterWepC)// 4 - Boss actor to be spawned according to the engine (Ex. CutmanBoss)// 5 - Megaman weapon name according to the engine (Ex. RollingCutterWep)// 6 - Protoman weapon name according to the engine (Ex. TripleCutterWep)// 7 - Bass weapon name according to the engine (Ex. CutterArmWep)// 8 - Roll weapon name according to the engine (Ex. GroundCutterWep)// 9 - Duo weapon name according to the engine (Ex. DuoCutterWep)// 10 - DocRobot weapon name according to the engine (Ex. GargantuanCutterWep)// 11 - Darkman weapon name according to the engine (Ex. CutterTwinsWep)// 12 - Fakeman weapon name according to the engine (Ex. CondorSlicerWep)// 13 - BBAMegaman weapon name according to the engine (Ex. TrollingCutterWep)
Brought back to recent times by Blews#9696, this addon reintroduces the Megaman, Protoman, Bass, and Roll class who all start with their own unique weapons and gain different weapons from each boss defeated. While Roll's weapons might be more supportive or poke based, Bass's are all for going straight in and causing mayhem. Megaman gets the vanilla weapons as usual, however. It's a staple way to play the mod if you're in for the old school authentic experience.Download Here!
For those looking for a set of harder difficulty segments that are sure to add some tension to the run, this pack of level segments created by Yair#5454 is sure to do the trick. It introduces level segments inspired by some classic MM8BDM platforming such as Obstacle and Ragestacle Course while introducing new and original level segments with various gimmicks.Download Here!
Created by FTX6004#0283, his pack aims to add some more laid-back robot master inspired level segments that mimic the style of the original MMSP stages. You can expect to see some familiar level set pieces used for all new platforming experiences.Download Here!
Created by MegaVile#8416, he offers one pack which implements extra enemies for any mapper to use, as well as a pack of his own that adds new bosses and segments from previous Mission Mode alternates.Download Enemies!Download Pack!