Skip to content

Commit da64322

Browse files
committed
Undid changes to spawner
1 parent 76afbf2 commit da64322

2 files changed

Lines changed: 155 additions & 158 deletions

File tree

Assets/Scripts/Game/Questing/Actions/CreateFoe.cs

Lines changed: 84 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ public override void Update(Task caller)
154154
}
155155

156156
// Try to deploy a pending spawns
157-
if(spawnInProgress){
157+
if (spawnInProgress)
158+
{
158159
TryPlacement();
159160
GameManager.Instance.RaiseOnEncounterEvent();
160161
}
@@ -179,7 +180,7 @@ void CreatePendingFoeSpawn(Foe foe)
179180
pendingFoesSpawned = 0;
180181
}
181182

182-
bool TryPlacement(bool placeEnemyBehindPlayer = true)
183+
void TryPlacement()
183184
{
184185
PlayerEnterExit playerEnterExit = GameManager.Instance.PlayerEnterExit;
185186

@@ -188,25 +189,25 @@ bool TryPlacement(bool placeEnemyBehindPlayer = true)
188189
if (isSendAction)
189190
{
190191
if (!GameManager.Instance.PlayerGPS.IsPlayerInLocationRect)
191-
return false;
192+
return;
192193
}
193194

194195
// Place in world near player depending on local area
195196
if (playerEnterExit.IsPlayerInsideBuilding)
196197
{
197-
return PlaceFoeBuildingInterior(pendingFoeGameObjects, playerEnterExit.Interior, placeEnemyBehindPlayer);
198+
PlaceFoeBuildingInterior(pendingFoeGameObjects, playerEnterExit.Interior);
198199
}
199200
else if (playerEnterExit.IsPlayerInsideDungeon)
200201
{
201-
return PlaceFoeDungeonInterior(pendingFoeGameObjects, playerEnterExit.Dungeon, placeEnemyBehindPlayer);
202+
PlaceFoeDungeonInterior(pendingFoeGameObjects, playerEnterExit.Dungeon);
202203
}
203204
else if (!playerEnterExit.IsPlayerInside && GameManager.Instance.PlayerGPS.IsPlayerInLocationRect)
204205
{
205-
return PlaceFoeExteriorLocation(pendingFoeGameObjects, GameManager.Instance.StreamingWorld.CurrentPlayerLocationObject, placeEnemyBehindPlayer);
206+
PlaceFoeExteriorLocation(pendingFoeGameObjects, GameManager.Instance.StreamingWorld.CurrentPlayerLocationObject);
206207
}
207208
else
208209
{
209-
return PlaceFoeWilderness(pendingFoeGameObjects, placeEnemyBehindPlayer);
210+
PlaceFoeWilderness(pendingFoeGameObjects);
210211
}
211212
}
212213

@@ -216,7 +217,7 @@ bool TryPlacement(bool placeEnemyBehindPlayer = true)
216217

217218
// Place foe somewhere near player when inside a building
218219
// Building interiors have spawn nodes for this placement so we can roll out foes all at once
219-
bool PlaceFoeBuildingInterior(GameObject[] gameObjects, DaggerfallInterior interiorParent, bool placeEnemyBehindPlayer = true)
220+
void PlaceFoeBuildingInterior(GameObject[] gameObjects, DaggerfallInterior interiorParent)
220221
{
221222
// Must have a DaggerfallLocation parent
222223
if (interiorParent == null)
@@ -228,127 +229,115 @@ bool PlaceFoeBuildingInterior(GameObject[] gameObjects, DaggerfallInterior inter
228229
// Always place foes around player rather than use spawn points
229230
// Spawn points work well for "interior hunt" quests but less so for "directly attack the player"
230231
// Feel just placing freely will yield best results overall
231-
return PlaceFoeFreely(gameObjects, interiorParent.transform, 5, 20, placeEnemyBehindPlayer);
232+
PlaceFoeFreely(gameObjects, interiorParent.transform);
233+
return;
232234
}
233235

234236
// Place foe somewhere near player when inside a dungeon
235237
// Dungeons interiors are complex 3D environments with no navgrid/navmesh or known spawn nodes
236-
bool PlaceFoeDungeonInterior(GameObject[] gameObjects, DaggerfallDungeon dungeonParent, bool placeEnemyBehindPlayer = true)
238+
void PlaceFoeDungeonInterior(GameObject[] gameObjects, DaggerfallDungeon dungeonParent)
237239
{
238-
return PlaceFoeFreely(gameObjects, dungeonParent.transform, 5, 20, placeEnemyBehindPlayer);
240+
PlaceFoeFreely(gameObjects, dungeonParent.transform);
239241
}
240242

241243
// Place foe somewhere near player when outside a location navgrid is available
242244
// Navgrid placement helps foe avoid getting tangled in geometry like buildings
243-
bool PlaceFoeExteriorLocation(GameObject[] gameObjects, DaggerfallLocation locationParent, bool placeEnemyBehindPlayer = true)
245+
void PlaceFoeExteriorLocation(GameObject[] gameObjects, DaggerfallLocation locationParent)
244246
{
245-
return PlaceFoeFreely(gameObjects, locationParent.transform, 5, 20, placeEnemyBehindPlayer);
247+
PlaceFoeFreely(gameObjects, locationParent.transform);
246248
}
247249

248250
// Place foe somewhere near player when outside and no navgrid available
249251
// Wilderness environments are currently open so can be placed on ground anywhere within range
250-
bool PlaceFoeWilderness(GameObject[] gameObjects, bool placeEnemyBehindPlayer = true)
252+
void PlaceFoeWilderness(GameObject[] gameObjects)
251253
{
252254
// TODO this false will need to be true when start caching enemies
253255
GameManager.Instance.StreamingWorld.TrackLooseObject(gameObjects[pendingFoesSpawned], false, -1, -1, true);
254-
return PlaceFoeFreely(gameObjects, null, 8f, 25f, placeEnemyBehindPlayer, false);
256+
PlaceFoeFreely(gameObjects, null, 8f, 25f);
255257
}
256258

257259
// Uses raycasts to find next spawn position just outside of player's field of view
258-
bool PlaceFoeFreely(GameObject[] gameObjects, Transform parent, float minDistance = 5f, float maxDistance = 20f, bool placeEnemyBehindPlayer = true, bool lineOfSight = true, int spawnTries = 15)
260+
void PlaceFoeFreely(GameObject[] gameObjects, Transform parent, float minDistance = 5f, float maxDistance = 20f)
259261
{
260-
const float maxFloorDistance = 4f;
261262
const float overlapSphereRadius = 0.65f;
262-
263-
// override min/max distance in small interior spaces.
264-
var dfInterior = GameObject.FindObjectOfType<DaggerfallInterior>();
265-
if(dfInterior){
266-
Bounds bounds = new Bounds (dfInterior.transform.position, Vector3.one);
267-
Renderer[] renderers = dfInterior.GetComponentsInChildren<Renderer> ();
268-
foreach (Renderer renderer in renderers)
269-
{
270-
bounds.Encapsulate (renderer.bounds);
271-
}
272-
Vector3 playerPos = GameManager.Instance.PlayerController.transform.position;
273-
Vector3 boundsMin = bounds.min;
274-
Vector3 boundsMax = bounds.max;
275-
boundsMin.y = boundsMax.y = playerPos.y;
276-
maxDistance = Mathf.Max(Vector3.Distance(playerPos, boundsMin), Vector3.Distance(playerPos, boundsMax));
277-
minDistance = Mathf.Min(minDistance, maxDistance/4f);
278-
}
263+
const float separationDistance = 1.25f;
264+
const float maxFloorDistance = 4f;
279265

280266
// Must have received a valid array
281267
if (gameObjects == null || gameObjects.Length == 0)
282-
return false;
283-
284-
// Skip this foe if destroyed (e.g. player left building where pending)
285-
if (!gameObjects[pendingFoesSpawned])
286-
{
287-
pendingFoesSpawned++;
288-
return true;
289-
}
268+
return;
290269

291270
// Set parent - otherwise caller must set a parent
292271
if (parent)
293272
gameObjects[pendingFoesSpawned].transform.parent = parent;
294273

295-
var enemyCC = gameObjects[pendingFoesSpawned].GetComponent<CharacterController>();
296-
var playerCC = GameManager.Instance.PlayerController;
297-
// Set parent if none specified already
298-
if (!gameObjects[pendingFoesSpawned].transform.parent)
299-
gameObjects[pendingFoesSpawned].transform.parent = GameObjectHelper.GetBestParent();
300-
301-
for(int i =0; i < spawnTries; ++i){
302-
float fov = GameManager.Instance.MainCamera.fieldOfView;
303-
float randomAngle;
304-
if(!lineOfSight || i > spawnTries/3) // give up on line of sight checks after we've tried a few times
305-
randomAngle = UnityEngine.Random.Range(-180, 180);
306-
else if(!placeEnemyBehindPlayer)
307-
randomAngle = UnityEngine.Random.Range(-fov, fov);
308-
else
309-
randomAngle = UnityEngine.Random.Range(fov, 180f) * (UnityEngine.Random.value < 0.5f ? -1f : 1f);
310-
Quaternion rot = Quaternion.Euler(0, randomAngle, 0);
311-
Vector3 angle = (rot * Vector3.forward).normalized;
312-
Vector3 spawnDirection = GameManager.Instance.PlayerObject.transform.TransformDirection(angle).normalized;
313-
Vector3 playerFootPosition = playerCC.transform.position + playerCC.center - playerCC.height/2f * Vector3.down;
314-
Vector3 spawnPos = playerFootPosition + spawnDirection * UnityEngine.Random.Range(minDistance, maxDistance);
315-
enemyCC.transform.rotation = playerCC.transform.rotation;
316-
spawnPos += enemyCC.height/2f * Vector3.up + enemyCC.center;
317-
318-
// Must be able to find a surface below
319-
if (!Physics.Raycast(spawnPos, Vector3.down, out RaycastHit floorHit, maxFloorDistance, DFULayerMasks.CorporealMask))
320-
continue;
321-
322-
// Ensure this is open space
323-
spawnPos = floorHit.point + enemyCC.center + (enemyCC.height/2f + .5f)*Vector3.up;
324-
// Collider[] colliders = Physics.OverlapCapsule(spawnPos - enemyCC.height/2f * Vector3.up, spawnPos + enemyCC.height/2f * Vector3.up, enemyCC.radius, DFULayerMasks.CorporealMask);
325-
Collider[] colliders = Physics.OverlapSphere(spawnPos, overlapSphereRadius);
326-
if (colliders.Length > 0)
327-
continue;
328-
329-
Debug.Log($"CreateFoe: Found an enemy spawn point in {i} tries");
330-
331-
// This looks like a good spawn position
332-
pendingFoeGameObjects[pendingFoesSpawned].transform.position = spawnPos;
333-
FinalizeFoe(pendingFoeGameObjects[pendingFoesSpawned]);
334-
gameObjects[pendingFoesSpawned].transform.LookAt(GameManager.Instance.PlayerObject.transform.position);
335-
336-
// Send msg message on first spawn only
337-
if (msgMessageID != -1)
338-
{
339-
ParentQuest.ShowMessagePopup(msgMessageID, oncePerQuest:true);
340-
msgMessageID = -1;
341-
}
274+
// Select a left or right direction outside of camera FOV
275+
Quaternion rotation;
276+
float directionAngle = GameManager.Instance.MainCamera.fieldOfView;
277+
directionAngle += UnityEngine.Random.Range(0f, 4f);
278+
if (UnityEngine.Random.Range(0f, 1f) > 0.5f)
279+
rotation = Quaternion.Euler(0, -directionAngle, 0);
280+
else
281+
rotation = Quaternion.Euler(0, directionAngle, 0);
282+
283+
// Get direction vector and create a new ray
284+
Vector3 angle = (rotation * Vector3.forward).normalized;
285+
Vector3 spawnDirection = GameManager.Instance.PlayerObject.transform.TransformDirection(angle).normalized;
286+
Ray ray = new Ray(GameManager.Instance.PlayerObject.transform.position, spawnDirection);
287+
288+
// Check for a hit
289+
Vector3 currentPoint;
290+
RaycastHit initialHit;
291+
if (Physics.Raycast(ray, out initialHit, maxDistance))
292+
{
293+
float cos_normal = Vector3.Dot(- spawnDirection, initialHit.normal.normalized);
294+
if (cos_normal < 1e-6)
295+
return;
296+
float separationForward = separationDistance / cos_normal;
297+
298+
// Must be greater than minDistance
299+
float distanceSlack = initialHit.distance - separationForward - minDistance;
300+
if (distanceSlack < 0f)
301+
return;
302+
303+
// Separate out from hit point
304+
float extraDistance = UnityEngine.Random.Range(0f, Mathf.Min(2f, distanceSlack));
305+
currentPoint = initialHit.point - spawnDirection * (separationForward + extraDistance);
306+
}
307+
else
308+
{
309+
// Player might be in an open area (e.g. outdoors) pick a random point along spawn direction
310+
currentPoint = GameManager.Instance.PlayerObject.transform.position + spawnDirection * UnityEngine.Random.Range(minDistance, maxDistance);
311+
}
312+
313+
// Must be able to find a surface below
314+
RaycastHit floorHit;
315+
ray = new Ray(currentPoint, Vector3.down);
316+
if (!Physics.Raycast(ray, out floorHit, maxFloorDistance))
317+
return;
318+
319+
// Ensure this is open space
320+
Vector3 testPoint = floorHit.point + Vector3.up * separationDistance;
321+
Collider[] colliders = Physics.OverlapSphere(testPoint, overlapSphereRadius);
322+
if (colliders.Length > 0)
323+
return;
324+
325+
// This looks like a good spawn position
326+
pendingFoeGameObjects[pendingFoesSpawned].transform.position = testPoint;
327+
FinalizeFoe(pendingFoeGameObjects[pendingFoesSpawned]);
328+
gameObjects[pendingFoesSpawned].transform.LookAt(GameManager.Instance.PlayerObject.transform.position);
342329

343-
// Increment count
344-
pendingFoesSpawned++;
345-
return true;
330+
// Send msg message on first spawn only
331+
if (msgMessageID != -1)
332+
{
333+
ParentQuest.ShowMessagePopup(msgMessageID, oncePerQuest:true);
334+
msgMessageID = -1;
346335
}
347336

348-
// Couldn't find a spawn point
349-
Debug.Log($"CreateFoe: Couldn't find a valid enemy spawn point after {spawnTries} tries.");
350-
return false;
337+
// Increment count
338+
pendingFoesSpawned++;
351339
}
340+
352341
// Fine tunes foe position slightly based on mobility and enables GameObject
353342
void FinalizeFoe(GameObject go)
354343
{

0 commit comments

Comments
 (0)