@@ -21,7 +21,7 @@ var float spawnChanceBaseScale, spawnChanceScaler;
2121var int minSpawnDistance ;
2222
2323// monster spawn interval/cycle management
24- var float spawnInterval , currentSpawnInterval ;
24+ var float spawnInterval ;
2525
2626// keeps track of players to avoid regular looping
2727var Pawn maybeEnemyPlayers [32 ];
@@ -30,11 +30,16 @@ var int PlayerCountInterval;
3030
3131var Texture TeleportEffectTexture ;
3232
33+ var float minMonsterScale ;
34+ var float miniThreshold ;
35+ var localized String miniPrefix ;
36+
3337function PostBeginPlay () {
3438 local FlagBase flag ;
3539 local FlagBase bothFlags [2 ];
3640 local int maxRange , range ;
3741 local NavigationPoint nav ;
42+ local Mover mover ;
3843
3944 foreach AllActors (class 'FlagBase' , flag ) {
4045 if (flag .team == 0 ) bothFlags [0 ] = flag ;
@@ -73,6 +78,14 @@ function PostBeginPlay() {
7378
7479 spawnChanceScaler = Max (1000 , minSpawnDistance ) / spawnChanceBaseScale ;
7580
81+ // Update movers so monsters can use them
82+ foreach AllActors (class 'Mover' , mover ) {
83+ if (mover .BumpType == BT_PlayerBump ) mover .BumpType = BT_PawnBump ;
84+ }
85+
86+ // Set the monster spawn timer going
87+ SetTimer (spawnInterval , True );
88+
7689 Super .PostBeginPlay ();
7790}
7891
@@ -140,16 +153,11 @@ function CoercePawn(ScriptedPawn pawn) {
140153 }
141154}
142155
143- function Tick ( float delta ) {
156+ event Timer ( ) {
144157 if (game .bGameStarted ) {
145- currentSpawnInterval += delta ;
146- if (currentSpawnInterval >= spawnInterval ) {
147- spawnMonsters ();
148- currentSpawnInterval = 0 ;
149- }
158+ spawnMonsters ();
159+ Super .Timer ();
150160 }
151-
152- Super .Tick (delta );
153161}
154162
155163function NavigationPoint findNearSpawn () {
@@ -252,31 +260,72 @@ function ScriptedPawn spawnMonsterAt(
252260 // special condition to allow monsters to pass through each other, so they don't block the objective
253261 newMonster .bBlockActors = false ;
254262
255- SetSpawnOrders (newMonster , monsterTarget , tag != '');
256-
257263 newMonster .SetMovementPhysics ();
258- if (newMonster .Physics == PHYS_Walking ) newMonster .SetPhysics (PHYS_Falling );
264+
265+ SetSpawnOrders (newMonster , monsterTarget , tag != '');
259266
260267 SpawnEffect (newMonster );
261268 SpawnUnsticker (newMonster );
262269
270+ // set physics after orders, since orders navigation determination factors in physics
271+ if (newMonster .Physics == PHYS_Walking ) newMonster .SetPhysics (PHYS_Falling );
272+
263273 return newMonster ;
264274}
265275
276+ function float ResizePawn (ScriptedPawn pawn , float scale ) {
277+ pawn .SetCollisionSize (pawn .CollisionRadius * scale , pawn .CollisionHeight * scale );
278+ pawn .DrawScale = pawn .DrawScale * scale ;
279+ pawn .Health = pawn .Health * scale ;
280+ pawn .GroundSpeed = pawn .GroundSpeed * scale ;
281+ pawn .AirSpeed = pawn .AirSpeed * scale ;
282+
283+ return pawn .CollisionRadius / pawn .Default .CollisionRadius ;
284+ }
285+
266286function SetSpawnOrders (ScriptedPawn pawn , Actor monsterTarget , bool isRunner ) {
267287 local Pawn maybePlayer ;
288+ local bool foundPath ;
289+ local Float monsterScale ;
268290
269291 // make them advance towards the objective
270292 pawn .OrderObject = monsterTarget ;
271293 pawn .OrderTag = monsterTarget .Tag ;
272294
273295 if (isRunner || FRand () > 0.7 ) {
274- pawn .AlarmTag = monsterTarget .Tag ;
275- pawn .Orders = 'TriggerAlarm' ;
276- // when an alarm is triggered, TriggerAlarm causes AccessedNone on Enemy, so assign a random enemy
277- if (playerCount > 0 ) {
278- maybePlayer = maybeEnemyPlayers [Rand (playerCount )];
279- if (maybePlayer != None ) pawn .SetEnemy (maybePlayer );
296+ monsterScale = 1.0 ;
297+
298+ foundPath = pawn .FindBestPathToward (monsterTarget );
299+ // there is no path to the target, try scaling down the monster to see if it can fit through doorways
300+ while (monsterScale >= minMonsterScale && !foundPath ) {
301+ pawn .ClearPaths ();
302+ monsterScale = ResizePawn (pawn , 0.9 );
303+ foundPath = pawn .FindBestPathToward (monsterTarget );
304+ }
305+
306+ if (foundPath ) {
307+ // when an alarm is triggered, TriggerAlarm causes AccessedNone on Enemy, so assign a random enemy
308+ if (playerCount > 0 ) {
309+ maybePlayer = maybeEnemyPlayers [Rand (playerCount )];
310+ if (maybePlayer != None ) pawn .SetEnemy (maybePlayer );
311+ }
312+
313+ pawn .AlarmTag = monsterTarget .Tag ;
314+ pawn .Orders = 'TriggerAlarm' ;
315+ } else {
316+ // we didn't find a path to the alarm point, maybe undo the scaling, otherwise keep the mini monster for fun
317+ if (FRand () > 0.5 ) {
318+ monsterScale = 2 - monsterScale ;
319+ ResizePawn (pawn , monsterScale );
320+ }
321+
322+ // at least lets go try to find a player
323+ pawn .Orders = 'Roaming' ;
324+ }
325+
326+ // set the mini name if the monster is small
327+ if (monsterScale <= miniThreshold ) {
328+ pawn .MenuName = miniPrefix @ pawn .MenuName ;
280329 }
281330 } else {
282331 pawn .Orders = 'Roaming' ;
@@ -309,4 +358,8 @@ defaultproperties {
309358 runnerTag ="MHDRunner"
310359
311360 TeleportEffectTexture =Texture 'Botpack.Skins.MuzzyPulse'
361+
362+ minMonsterScale =0.5
363+ miniThreshold =0.7
364+ miniPrefix ="Mini"
312365}
0 commit comments