private void EnemyEntity_OnDeath(DaggerfallEntity entity) { // Disable enemy gameobject // Do not destroy as we must still save enemy state when dead gameObject.SetActive(false); // Show death message string deathMessage = HardStrings.thingJustDied; deathMessage = deathMessage.Replace("%s", mobile.Summary.Enemy.Name); DaggerfallUI.Instance.PopupMessage(deathMessage); // Generate lootable corpse marker DaggerfallLoot loot = GameObjectHelper.CreateLootableCorpseMarker( GameManager.Instance.PlayerObject, entityBehaviour.gameObject, enemyEntity, mobile.Summary.Enemy.CorpseTexture, DaggerfallUnity.NextUID); // Generate items loot.GenerateItems(); // Raise static event if (OnEnemyDeath != null) { OnEnemyDeath(this, null); } }
void OfferToPlayerWithQuestComplete(Item item) { // Quest successful ParentQuest.QuestSuccess = true; // Show quest complete message DaggerfallMessageBox messageBox = ParentQuest.ShowMessagePopup((int)QuestMachine.QuestMessages.QuestComplete); // If no item for reward then we are done if (item == null) { return; } // Release item so we can offer back to player // Sometimes a quest item is both carried by player then offered back to them // Example is Sx010 where "curse" is removed and player can keep item GameManager.Instance.PlayerEntity.ReleaseQuestItemForReoffer(ParentQuest.UID, item, true); // Create a dropped loot container window for player to loot their reward rewardLoot = GameObjectHelper.CreateDroppedLootContainer(GameManager.Instance.PlayerObject, DaggerfallUnity.NextUID); rewardLoot.ContainerImage = InventoryContainerImages.Merchant; rewardLoot.Items.AddItem(item.DaggerfallUnityItem); // Schedule loot window to open when player dismisses message messageBox.OnClose += QuestCompleteMessage_OnClose; }
/// <summary> /// Creates a generic loot container. /// </summary> /// <param name="containerType">Type of container.</param> /// <param name="containerImage">Icon to display in loot UI.</param> /// <param name="position">Position to spawn container.</param> /// <param name="parent">Parent GameObject.</param> /// <param name="textureArchive">Texture archive for billboard containers.</param> /// <param name="textureRecord">Texture record for billboard containers.</param> /// <param name="loadID">Unique LoadID for save system.</param> /// <returns>DaggerfallLoot.</returns> public static DaggerfallLoot CreateLootContainer( LootContainerTypes containerType, InventoryContainerImages containerImage, Vector3 position, Transform parent, int textureArchive, int textureRecord, ulong loadID = 0) { // Setup initial loot container prefab GameObject go = InstantiatePrefab(DaggerfallUnity.Instance.Option_LootContainerPrefab.gameObject, containerType.ToString(), parent, position); // Setup billboard component DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); dfBillboard.SetMaterial(textureArchive, textureRecord); // Setup DaggerfallLoot component to make lootable DaggerfallLoot loot = go.GetComponent <DaggerfallLoot>(); if (loot) { loot.LoadID = loadID; loot.ContainerType = containerType; loot.ContainerImage = containerImage; loot.TextureArchive = textureArchive; loot.TextureRecord = textureRecord; } // Now move up loot icon by half own size so bottom is aligned with position position.y += (dfBillboard.Summary.Size.y / 2f); loot.transform.position = position; return(loot); }
void Awake() { loot = GetComponent <DaggerfallLoot>(); if (!loot) { throw new Exception("DaggerfallLoot not found."); } }
// Check if raycast hit a lootable object private bool LootCheck(RaycastHit hitInfo, out DaggerfallLoot loot) { loot = hitInfo.transform.GetComponent<DaggerfallLoot>(); if (loot == null) return false; else return true; }
void CompleteDeath() { if (!entityBehaviour) { return; } // If enemy associated with quest system, make sure quest system is done with it first QuestResourceBehaviour questResourceBehaviour = GetComponent <QuestResourceBehaviour>(); if (questResourceBehaviour) { if (!questResourceBehaviour.IsFoeDead) { return; } } // Play body collapse sound if (DaggerfallUI.Instance.DaggerfallAudioSource) { AudioClip collapseSound = DaggerfallUI.Instance.DaggerfallAudioSource.GetAudioClip((int)SoundClips.BodyFall); AudioSource.PlayClipAtPoint(collapseSound, entityBehaviour.transform.position); } // Disable enemy gameobject // Do not destroy as we must still save enemy state when dead gameObject.SetActive(false); // Show death message string deathMessage = HardStrings.thingJustDied; deathMessage = deathMessage.Replace("%s", mobile.Summary.Enemy.Name); DaggerfallUI.Instance.PopupMessage(deathMessage); // Generate lootable corpse marker DaggerfallLoot loot = GameObjectHelper.CreateLootableCorpseMarker( GameManager.Instance.PlayerObject, entityBehaviour.gameObject, enemyEntity, mobile.Summary.Enemy.CorpseTexture, DaggerfallUnity.NextUID); // Generate items loot.GenerateItems(); entityBehaviour.CorpseLootContainer = loot; // Transfer any items owned by entity to loot container // Many quests will stash a reward in enemy inventory for player to find // This will be in addition to normal random loot table generation loot.Items.TransferAll(entityBehaviour.Entity.Items); // Raise static event if (OnEnemyDeath != null) { OnEnemyDeath(this, null); } }
void RayCheckForObstacle(Vector3 direction) { obstacleDetected = false; RaycastHit hit; int checkDistance = 2; Vector3 rayOrigin = transform.position + controller.center; rayOrigin.y -= controller.height / 3; foundUpwardSlope = false; foundDoor = false; Ray ray = new Ray(rayOrigin, direction); if (Physics.Raycast(ray, out hit, checkDistance)) { obstacleDetected = true; float firstDistance = hit.distance; rayOrigin.y += 0.5f; ray = new Ray(rayOrigin, direction); RaycastHit hit2; bool secondRayHit = Physics.Raycast(ray, out hit2, checkDistance); if (!secondRayHit || firstDistance < hit2.distance) { obstacleDetected = false; foundUpwardSlope = true; } DaggerfallEntityBehaviour entityBehaviour2 = hit.transform.GetComponent <DaggerfallEntityBehaviour>(); if (entityBehaviour2 == senses.Target) { obstacleDetected = false; } DaggerfallActionDoor door = hit.transform.GetComponent <DaggerfallActionDoor>(); if (door) { obstacleDetected = false; foundDoor = true; if (senses.TargetIsWithinYawAngle(5.625f, door.transform.position)) { senses.LastKnownDoor = door; senses.DistanceToDoor = Vector3.Distance(transform.position, door.transform.position); } } DaggerfallLoot loot = hit.transform.GetComponent <DaggerfallLoot>(); if (loot) { obstacleDetected = false; } } }
// Check if raycast hit a lootable object private bool LootCheck(RaycastHit hitInfo, out DaggerfallLoot loot) { loot = hitInfo.transform.GetComponent <DaggerfallLoot>(); if (loot == null) { return(false); } else { return(true); } }
public static bool GenerateLoot(DaggerfallLoot loot, int locationIndex) { string[] lootTableKeys = { "K", // Crypt "N", // Orc Stronghold "N", // Human Stronghold "N", // Prison "K", // Desecrated Temple "M", // Mine "M", // Natural Cave "Q", // Coven "K", // Vampire Haunt "U", // Laboratory "D", // Harpy Nest "N", // Ruined Castle "L", // Spider Nest "F", // Giant Stronghold "S", // Dragon's Den "N", // Barbarian Stronghold "M", // Volcanic Caves "L", // Scorpion Nest "N", // Cemetery }; // Get loot table key if (locationIndex < lootTableKeys.Length) { DaggerfallLoot.GenerateItems(lootTableKeys[locationIndex], loot.Items); // Randomly add map char key = lootTableKeys[locationIndex][0]; int alphabetIndex = key - 64; if (alphabetIndex >= 10 && alphabetIndex <= 15) // between keys J and O { int[] mapChances = { 2, 1, 1, 2, 2, 15 }; int mapChance = mapChances[alphabetIndex - 10]; DaggerfallLoot.RandomlyAddMap(mapChance, loot.Items); DaggerfallLoot.RandomlyAddPotion(4, loot.Items); DaggerfallLoot.RandomlyAddPotionRecipe(2, loot.Items); } OnLootSpawned?.Invoke(null, new TabledLootSpawnedEventArgs { LocationIndex = locationIndex, Key = lootTableKeys[locationIndex], Items = loot.Items }); return(true); } return(false); }
void RestoreExteriorPositionHandler(DaggerfallLoot loot, LootContainerData_v1 data, WorldContext lootContext) { // If loot context matches serialized world context then loot was saved after floating y change // Need to get relative difference between current and serialized world compensation to get actual y position if (lootContext == data.worldContext) { float diffY = GameManager.Instance.StreamingWorld.WorldCompensation.y - data.worldCompensation.y; loot.transform.position = data.currentPosition + new Vector3(0, diffY, 0); return; } // Otherwise we migrate a legacy exterior position by adjusting for world compensation loot.transform.position = data.currentPosition + GameManager.Instance.StreamingWorld.WorldCompensation; }
/// <summary> /// Destroys/Disables a loot container. /// Custom drop containers will be destroyed from world. /// Fixed containers will be disabled so their empty state continues to be serialized. /// </summary> /// <param name="loot">DaggerfallLoot.</param> public static void RemoveLootContainer(DaggerfallLoot loot) { // Corpse markers are not removed from world even if empty if (loot.ContainerType == LootContainerTypes.CorpseMarker) { return; } // Destroy or disable based on custom flag if (loot.customDrop) { GameObject.Destroy(loot.gameObject); } else { loot.gameObject.SetActive(false); } }
void OfferToPlayerWithQuestComplete(Item item) { // Show quest complete message DaggerfallMessageBox messageBox = ParentQuest.ShowMessagePopup((int)QuestMachine.QuestMessages.QuestComplete); // If no item for reward then we are done if (item == null) { return; } // Create a dropped loot container window for player to loot their reward rewardLoot = GameObjectHelper.CreateDroppedLootContainer(GameManager.Instance.PlayerObject, DaggerfallUnity.NextUID); rewardLoot.ContainerImage = InventoryContainerImages.Merchant; rewardLoot.Items.AddItem(item.DaggerfallUnityItem); // Schedule loot window to open when player dismisses message messageBox.OnClose += QuestCompleteMessage_OnClose; }
void RestoreInteriorPositionHandler(DaggerfallLoot loot, LootContainerData_v1 data, WorldContext lootContext) { // If loot context matches serialized world context then loot was saved after floating y change // Can simply restore local position relative to parent interior if (lootContext == data.worldContext) { loot.transform.localPosition = data.localPosition; return; } // Otherwise we need to migrate a legacy interior position to floating y if (GameManager.Instance.PlayerEnterExit.LastInteriorStartFlag) { // Loading interior uses serialized absolute position (as interior also serialized this way) loot.transform.position = data.currentPosition; } else { // Transition to interior must offset serialized absolute position by floating y compensation loot.transform.position = data.currentPosition + GameManager.Instance.StreamingWorld.WorldCompensation; } }
private static void DropAllItems() { UnequipAll(); GameObject player = GameManager.Instance.PlayerObject; List <DaggerfallUnityItem> dropList = new List <DaggerfallUnityItem>(); for (int i = 0; i < playerEntity.Items.Count; i++) { DaggerfallUnityItem item = playerEntity.Items.GetItem(i); if (item.QuestItemSymbol != null || item.IsQuestItem || item.IsSummoned || item.TemplateIndex == 132 || item.TemplateIndex == 93 || item.TemplateIndex == 94) { } else { if (item.IsEquipped) { item.currentCondition /= 2; } else { dropList.Add(item); } } } if (dropList.Count >= 1) { DaggerfallLoot equipPile = GameObjectHelper.CreateDroppedLootContainer(player, DaggerfallUnity.NextUID); equipPile.customDrop = true; equipPile.playerOwned = true; foreach (DaggerfallUnityItem item in dropList) { equipPile.Items.Transfer(item, playerEntity.Items); } DaggerfallUI.MessageBox("You tear off your clothes and armor."); } }
private static void DropAllItems() { GameObject player = GameManager.Instance.PlayerObject; dropCollection = new ItemCollection(); ItemCollection keepItemsCollection = new ItemCollection(); UnequipAll(); dropCollection.AddItems(playerEntity.Items.CloneAll()); for (int i = 0; i < dropCollection.Count; i++) { DaggerfallUnityItem item = dropCollection.GetItem(i); if (item.QuestItemSymbol != null || item.IsQuestItem || item.IsSummoned || item.TemplateIndex == 132 || item.TemplateIndex == 93 || item.TemplateIndex == 94) { if (item.IsEquipped) { item.UnequipItem(playerEntity); } keepItemsCollection.AddItem(item); dropCollection.RemoveItem(item); } } DaggerfallLoot equipPile = GameObjectHelper.CreateDroppedLootContainer(player, DaggerfallUnity.NextUID); equipPile.customDrop = true; equipPile.playerOwned = true; equipPile.Items.AddItems(dropCollection.CloneAll()); playerEntity.Items.Clear(); dropCollection.Clear(); for (int i = 0; i < keepItemsCollection.Count; i++) { DaggerfallUnityItem item = keepItemsCollection.GetItem(i); playerEntity.Items.AddItem(item); } keepItemsCollection.Clear(); }
public void RestoreLootContainerData(LootContainerData_v1[] lootContainers) { if (lootContainers == null || lootContainers.Length == 0) { return; } for (int i = 0; i < lootContainers.Length; i++) { // Skip null containers if (lootContainers[i] == null) { continue; } // Restore loot containers ulong key = lootContainers[i].loadID; if (SerializableLootContainers.ContainsKey(key)) { // Apply to known loot container that is part of scene build SerializableLootContainers[key].RestoreSaveData(lootContainers[i]); } else { // Add custom drop containers back to scene (e.g. dropped loot, slain foes) if (lootContainers[i].customDrop) { DaggerfallLoot customLootContainer = GameObjectHelper.CreateDroppedLootContainer(GameManager.Instance.PlayerObject, key, lootContainers[i].textureArchive, lootContainers[i].textureRecord); SerializableLootContainer serializableLootContainer = customLootContainer.GetComponent <SerializableLootContainer>(); if (serializableLootContainer) { serializableLootContainer.RestoreSaveData(lootContainers[i]); } //Debug.LogFormat("created loot container {0} containing {1} parent {2}", key, customLootContainer.Items.GetItem(0).shortName, customLootContainer.transform.parent.name); } } } }
WorldContext GetLootWorldContext(DaggerfallLoot loot) { // Must be a parented loot container if (!loot || !loot.transform.parent) { return(WorldContext.Nothing); } // Interior if (loot.transform.parent.GetComponentInParent <DaggerfallInterior>()) { return(WorldContext.Interior); } // Dungeon if (loot.transform.parent.GetComponentInParent <DaggerfallDungeon>()) { return(WorldContext.Dungeon); } // Exterior (loose world object) return(WorldContext.Exterior); }
void RestoreLootContainerData(LootContainerData_v1[] lootContainers) { if (lootContainers == null || lootContainers.Length == 0) { return; } for (int i = 0; i < lootContainers.Length; i++) { // Skip null containers if (lootContainers[i] == null) { continue; } // Restore loot containers ulong key = lootContainers[i].loadID; if (serializableLootContainers.ContainsKey(key)) { // Apply to known loot container that is part of scene build serializableLootContainers[key].RestoreSaveData(lootContainers[i]); } else { // Add custom drop containers back to scene (e.g. dropped loot, slain foes) if (lootContainers[i].customDrop) { DaggerfallLoot customLootContainer = GameObjectHelper.CreateDroppedLootContainer(GameManager.Instance.PlayerObject, key); SerializableLootContainer serializableLootContainer = customLootContainer.GetComponent <SerializableLootContainer>(); if (serializableLootContainer) { serializableLootContainer.RestoreSaveData(lootContainers[i]); } } } } }
void FindDetour(Vector3 motion) { Vector3 motion2d = motion; motion2d.y = 0; // First get whether we check clockwise or counterclockwise if (checkingClockWiseCounter == 0) { Vector3 toTarget = targetPos - transform.position; Vector3 directionToTarget = toTarget.normalized; float angleToTarget = Vector3.SignedAngle(directionToTarget, motion, Vector3.up); if (angleToTarget > 0) { checkingClockWise = false; } else { checkingClockWise = true; } if (checkingClockWise) { angleToTarget = 30; } else { angleToTarget = -30; } RaycastHit hit; Vector3 testAngle = Quaternion.AngleAxis(angleToTarget, Vector3.up) * motion; motion2d.y = 0; int checkDistance = 2; Vector3 rayOrigin = transform.position; rayOrigin.y -= controller.height / 4; if (targetPos.y > transform.position.y + controller.height / 2) { rayOrigin.y += controller.height / 2; } Ray ray = new Ray(rayOrigin, testAngle); if (Physics.Raycast(ray, out hit, checkDistance)) { bool testObstacleDetected = true; if (lastYPos < transform.position.y) { testObstacleDetected = false; } DaggerfallEntityBehaviour entityBehaviour2 = hit.transform.GetComponent <DaggerfallEntityBehaviour>(); if (entityBehaviour2 == entityBehaviour.Target) { testObstacleDetected = false; } DaggerfallActionDoor door = hit.transform.GetComponent <DaggerfallActionDoor>(); if (door) { testObstacleDetected = false; } DaggerfallLoot loot = hit.transform.GetComponent <DaggerfallLoot>(); if (loot) { testObstacleDetected = false; } if (testObstacleDetected) { // Tested 30 degrees in the clockwise/counter-clockwise direction we chose, // but hit something, so try other one. checkingClockWise = !checkingClockWise; } } checkingClockWiseCounter = 5; } else { checkingClockWiseCounter--; } float angle = 15; if (!checkingClockWise) { angle *= -1; } Vector3 detour; if (detourNumber == 0) { detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 1) { angle *= 2; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 2) { angle *= 3; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 3) { angle *= 4; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 4) { angle *= 5; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 5) { angle *= 6; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 6) { angle *= -1; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 7) { angle *= -2; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 8) { angle *= -3; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 9) { angle *= -4; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 10) { angle *= -5; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 11) { angle *= -6; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 12) { angle *= 7; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 13) { angle *= -7; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else if (detourNumber == 14) { angle *= 8; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } else { angle *= -8; detour = Quaternion.AngleAxis(angle, Vector3.up) * motion; } detourNumber++; if (detourNumber == 16) { detourNumber = 0; } tempMovePos = transform.position + detour.normalized * 3; tempMovePos.y = transform.position.y; lookingForDetour = true; moveInForAttack = true; }
/// <summary> /// Creates a loot container for enemies slain by the player. /// </summary> /// <param name="player">Player object, must have PlayerEnterExit attached.</param> /// <param name="enemy">Enemy object, must have EnemyMotor attached.</param> /// <param name="corpseTexture">Packed corpse texture index from entity summary.</param> /// <param name="loadID">Unique LoadID for save system.</param> /// <returns>DaggerfallLoot.</returns> public static DaggerfallLoot CreateLootableCorpseMarker(GameObject player, GameObject enemy, EnemyEntity enemyEntity, int corpseTexture, ulong loadID) { // Player must have a PlayerEnterExit component PlayerEnterExit playerEnterExit = player.GetComponent <PlayerEnterExit>(); if (!playerEnterExit) { throw new Exception("CreateLootableCorpseMarker() player game object must have PlayerEnterExit component."); } // Enemy must have an EnemyMotor component EnemyMotor enemyMotor = enemy.GetComponent <EnemyMotor>(); if (!enemyMotor) { throw new Exception("CreateLootableCorpseMarker() enemy game object must have EnemyMotor component."); } // Get parent by context Transform parent = null; if (GameManager.Instance.IsPlayerInside) { if (GameManager.Instance.IsPlayerInsideDungeon) { parent = playerEnterExit.Dungeon.transform; } else { parent = playerEnterExit.Interior.transform; } } else { parent = GameManager.Instance.StreamingTarget.transform; } // Get corpse marker texture indices int archive, record; EnemyBasics.ReverseCorpseTexture(corpseTexture, out archive, out record); // Find ground position below player Vector3 position = enemyMotor.FindGroundPosition(); // Create loot container DaggerfallLoot loot = CreateLootContainer( LootContainerTypes.CorpseMarker, InventoryContainerImages.Corpse2, position, parent, archive, record, loadID); // Set properties loot.LoadID = loadID; loot.LootTableKey = enemyEntity.MobileEnemy.LootTableKey; loot.playerOwned = false; loot.customDrop = true; // If dropped outside ask StreamingWorld to track loose object if (!GameManager.Instance.IsPlayerInside) { GameManager.Instance.StreamingWorld.TrackLooseObject(loot.gameObject); } return(loot); }
/// <summary> /// Sets enemy career and prepares entity settings. /// </summary> public void SetEnemyCareer(MobileEnemy mobileEnemy, EntityTypes entityType) { // Try custom career first career = GetCustomCareerTemplate(mobileEnemy.ID); if (career != null) { // Custom enemy careerIndex = mobileEnemy.ID; stats.SetPermanentFromCareer(career); if (entityType == EntityTypes.EnemyMonster) { // Default like a monster level = mobileEnemy.Level; maxHealth = Random.Range(mobileEnemy.MinHealth, mobileEnemy.MaxHealth + 1); for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } else { // Default like a class enemy level = GameManager.Instance.PlayerEntity.Level; maxHealth = FormulaHelper.RollEnemyClassMaxHealth(level, career.HitPointsPerLevel); } } else if (entityType == EntityTypes.EnemyMonster) { careerIndex = mobileEnemy.ID; career = GetMonsterCareerTemplate((MonsterCareers)careerIndex); stats.SetPermanentFromCareer(career); // Enemy monster has predefined level, health and armor values. // Armor values can be modified below by equipment. level = mobileEnemy.Level; maxHealth = UnityEngine.Random.Range(mobileEnemy.MinHealth, mobileEnemy.MaxHealth + 1); for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } else if (entityType == EntityTypes.EnemyClass) { careerIndex = mobileEnemy.ID - 128; career = GetClassCareerTemplate((ClassCareers)careerIndex); stats.SetPermanentFromCareer(career); // Enemy class is levelled to player and uses similar health rules // City guards are 3 to 6 levels above the player level = GameManager.Instance.PlayerEntity.Level; if (careerIndex == (int)MobileTypes.Knight_CityWatch - 128) { level += UnityEngine.Random.Range(3, 7); } maxHealth = FormulaHelper.RollEnemyClassMaxHealth(level, career.HitPointsPerLevel); } else { career = new DFCareer(); careerIndex = -1; return; } this.mobileEnemy = mobileEnemy; this.entityType = entityType; name = career.Name; minMetalToHit = mobileEnemy.MinMetalToHit; team = mobileEnemy.Team; short skillsLevel = (short)((level * 5) + 30); if (skillsLevel > 100) { skillsLevel = 100; } for (int i = 0; i <= DaggerfallSkills.Count; i++) { skills.SetPermanentSkillValue(i, skillsLevel); } // Generate loot table items DaggerfallLoot.GenerateItems(mobileEnemy.LootTableKey, items); // Enemy classes and some monsters use equipment if (careerIndex == (int)MonsterCareers.Orc || careerIndex == (int)MonsterCareers.OrcShaman) { SetEnemyEquipment(0); } else if (careerIndex == (int)MonsterCareers.Centaur || careerIndex == (int)MonsterCareers.OrcSergeant) { SetEnemyEquipment(1); } else if (careerIndex == (int)MonsterCareers.OrcWarlord) { SetEnemyEquipment(2); } else if (entityType == EntityTypes.EnemyClass) { SetEnemyEquipment(UnityEngine.Random.Range(0, 2)); // 0 or 1 } // Assign spell lists if (entityType == EntityTypes.EnemyMonster) { if (careerIndex == (int)MonsterCareers.Imp) { SetEnemySpells(ImpSpells); } else if (careerIndex == (int)MonsterCareers.Ghost) { SetEnemySpells(GhostSpells); } else if (careerIndex == (int)MonsterCareers.OrcShaman) { SetEnemySpells(OrcShamanSpells); } else if (careerIndex == (int)MonsterCareers.Wraith) { SetEnemySpells(WraithSpells); } else if (careerIndex == (int)MonsterCareers.FrostDaedra) { SetEnemySpells(FrostDaedraSpells); } else if (careerIndex == (int)MonsterCareers.FireDaedra) { SetEnemySpells(FireDaedraSpells); } else if (careerIndex == (int)MonsterCareers.Daedroth) { SetEnemySpells(DaedrothSpells); } else if (careerIndex == (int)MonsterCareers.Vampire) { SetEnemySpells(VampireSpells); } else if (careerIndex == (int)MonsterCareers.DaedraSeducer) { SetEnemySpells(SeducerSpells); } else if (careerIndex == (int)MonsterCareers.VampireAncient) { SetEnemySpells(VampireAncientSpells); } else if (careerIndex == (int)MonsterCareers.DaedraLord) { SetEnemySpells(DaedraLordSpells); } else if (careerIndex == (int)MonsterCareers.Lich) { SetEnemySpells(LichSpells); } else if (careerIndex == (int)MonsterCareers.AncientLich) { SetEnemySpells(AncientLichSpells); } } else if (entityType == EntityTypes.EnemyClass && (mobileEnemy.CastsMagic)) { int spellListLevel = level / 3; if (spellListLevel > 6) { spellListLevel = 6; } SetEnemySpells(EnemyClassSpells[spellListLevel]); } // Chance of adding map DaggerfallLoot.RandomlyAddMap(mobileEnemy.MapChance, items); if (!string.IsNullOrEmpty(mobileEnemy.LootTableKey)) { // Chance of adding potion DaggerfallLoot.RandomlyAddPotion(3, items); // Chance of adding potion recipe DaggerfallLoot.RandomlyAddPotionRecipe(2, items); } OnLootSpawned?.Invoke(this, new EnemyLootSpawnedEventArgs { MobileEnemy = mobileEnemy, EnemyCareer = career, Items = items }); FillVitalSigns(); }
void Awake() { loot = GetComponent<DaggerfallLoot>(); if (!loot) throw new Exception("DaggerfallLoot not found."); }
/// <summary> /// Sets enemy career and prepares entity settings. /// </summary> public void SetEnemyCareer(MobileEnemy mobileEnemy, EntityTypes entityType) { if (entityType == EntityTypes.EnemyMonster) { careerIndex = (int)mobileEnemy.ID; career = GetMonsterCareerTemplate((MonsterCareers)careerIndex); stats.SetPermanentFromCareer(career); // Enemy monster has predefined level, health and armor values. // Armor values can be modified below by equipment. level = mobileEnemy.Level; maxHealth = UnityEngine.Random.Range(mobileEnemy.MinHealth, mobileEnemy.MaxHealth + 1); for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } else if (entityType == EntityTypes.EnemyClass) { careerIndex = (int)mobileEnemy.ID - 128; career = GetClassCareerTemplate((ClassCareers)careerIndex); stats.SetPermanentFromCareer(career); // Enemy class is levelled to player and uses similar health rules level = GameManager.Instance.PlayerEntity.Level; maxHealth = FormulaHelper.RollEnemyClassMaxHealth(level, career.HitPointsPerLevel); } else { career = new DFCareer(); careerIndex = -1; return; } this.mobileEnemy = mobileEnemy; this.entityType = entityType; name = career.Name; minMetalToHit = mobileEnemy.MinMetalToHit; short skillsLevel = (short)((level * 5) + 30); if (skillsLevel > 100) { skillsLevel = 100; } for (int i = 0; i <= DaggerfallSkills.Count; i++) { skills.SetPermanentSkillValue(i, skillsLevel); } // Generate loot table items DaggerfallLoot.GenerateItems(mobileEnemy.LootTableKey, items); // Enemy classes and some monsters use equipment if (careerIndex == (int)MonsterCareers.Orc || careerIndex == (int)MonsterCareers.OrcShaman) { SetEnemyEquipment(0); } else if (careerIndex == (int)MonsterCareers.Centaur || careerIndex == (int)MonsterCareers.OrcSergeant) { SetEnemyEquipment(1); } else if (careerIndex == (int)MonsterCareers.OrcWarlord) { SetEnemyEquipment(2); } else if (entityType == EntityTypes.EnemyClass) { SetEnemyEquipment(UnityEngine.Random.Range(0, 2)); // 0 or 1 } // Chance of adding map DaggerfallLoot.RandomlyAddMap(mobileEnemy.MapChance, items); if (!string.IsNullOrEmpty(mobileEnemy.LootTableKey)) { // Chance of adding potion DaggerfallLoot.RandomlyAddPotion(3, items); // Chance of adding potion recipe DaggerfallLoot.RandomlyAddPotionRecipe(2, items); } FillVitalSigns(); }
/// <summary> /// Creates a loot container for items dropped by the player. /// </summary> /// <param name="player">Player object, must have PlayerEnterExit and PlayerMotor attached.</param> /// <param name="loadID">Unique LoadID for save system.</param> /// <returns>DaggerfallLoot.</returns> public static DaggerfallLoot CreateDroppedLootContainer(GameObject player, ulong loadID) { // Player must have a PlayerEnterExit component PlayerEnterExit playerEnterExit = player.GetComponent <PlayerEnterExit>(); if (!playerEnterExit) { throw new Exception("CreateDroppedLootContainer() player game object must have PlayerEnterExit component."); } // Player must have a PlayerMotor component PlayerMotor playerMotor = player.GetComponent <PlayerMotor>(); if (!playerMotor) { throw new Exception("CreateDroppedLootContainer() player game object must have PlayerMotor component."); } // Get parent by context Transform parent = null; if (GameManager.Instance.IsPlayerInside) { if (GameManager.Instance.IsPlayerInsideDungeon) { parent = playerEnterExit.Dungeon.transform; } else { parent = playerEnterExit.Interior.transform; } } else { parent = GameManager.Instance.StreamingTarget.transform; } // Randomise container texture int iconIndex = UnityEngine.Random.Range(0, DaggerfallLoot.randomTreasureIconIndices.Length); int iconRecord = DaggerfallLoot.randomTreasureIconIndices[iconIndex]; // Find ground position below player Vector3 position = playerMotor.FindGroundPosition(); // Create loot container DaggerfallLoot loot = CreateLootContainer( LootContainerTypes.DroppedLoot, InventoryContainerImages.Chest, position, parent, DaggerfallLoot.randomTreasureArchive, iconRecord, loadID); // Set properties loot.LoadID = loadID; loot.LootTableKey = string.Empty; loot.playerOwned = true; loot.customDrop = true; // If dropped outside ask StreamingWorld to track loose object if (!GameManager.Instance.IsPlayerInside) { GameManager.Instance.StreamingWorld.TrackLooseObject(loot.gameObject); } return(loot); }
/// <summary> /// Sets enemy career and prepares entity settings. /// </summary> public void SetEnemyCareer(MobileEnemy mobileEnemy, EntityTypes entityType) { if (entityType == EntityTypes.EnemyMonster) { careerIndex = mobileEnemy.ID; career = GetMonsterCareerTemplate((MonsterCareers)careerIndex); stats.SetPermanentFromCareer(career); // Enemy monster has predefined level, health and armor values. // Armor values can be modified below by equipment. level = mobileEnemy.Level; maxHealth = UnityEngine.Random.Range(mobileEnemy.MinHealth, mobileEnemy.MaxHealth + 1); for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } else if (entityType == EntityTypes.EnemyClass) { careerIndex = mobileEnemy.ID - 128; career = GetClassCareerTemplate((ClassCareers)careerIndex); stats.SetPermanentFromCareer(career); // I may have a better way to alter the attributes of individual enemy entities, this seems to be where it originates from, will possibly alter later on. // Enemy class is levelled to player and uses similar health rules // City guards are 3 to 6 levels above the player //level = GameManager.Instance.PlayerEntity.Level; // Definitely going to want to mess with this a lot eventually, this is apparently what makes the human enemies equal to the player level, will alter that. level = UnityEngine.Random.Range(1, 31); if (careerIndex == (int)MobileTypes.Knight_CityWatch) { level += UnityEngine.Random.Range(3, 7); } maxHealth = FormulaHelper.RollEnemyClassMaxHealth(level, career.HitPointsPerLevel); } else { career = new DFCareer(); careerIndex = -1; return; } this.mobileEnemy = mobileEnemy; this.entityType = entityType; name = career.Name; minMetalToHit = mobileEnemy.MinMetalToHit; team = mobileEnemy.Team; short skillsLevel = (short)((level * 5) + 30); if (skillsLevel > 100) { skillsLevel = 100; } for (int i = 0; i <= DaggerfallSkills.Count; i++) { skills.SetPermanentSkillValue(i, skillsLevel); } int[] personalityTraits = DaggerfallWorkshop.Utility.EnemyBasics.EnemyPersonalityTraitGenerator(this); // May put the method for the "context based" inventory modifiers here, but first i'll have to figure out how i'm going to do that exactly first. DaggerfallLoot.GenerateEnemyItems(items, personalityTraits, this); // Enemy classes and some monsters use equipment if (EquipmentUser()) { SetEnemyEquipment(personalityTraits); } // Assign spell lists if (entityType == EntityTypes.EnemyMonster) { if (careerIndex == (int)MonsterCareers.Imp) { SetEnemySpells(ImpSpells); } else if (careerIndex == (int)MonsterCareers.Ghost) { SetEnemySpells(GhostSpells); } else if (careerIndex == (int)MonsterCareers.OrcShaman) { SetEnemySpells(OrcShamanSpells); } else if (careerIndex == (int)MonsterCareers.Wraith) { SetEnemySpells(WraithSpells); } else if (careerIndex == (int)MonsterCareers.FrostDaedra) { SetEnemySpells(FrostDaedraSpells); } else if (careerIndex == (int)MonsterCareers.FireDaedra) { SetEnemySpells(FireDaedraSpells); } else if (careerIndex == (int)MonsterCareers.Daedroth) { SetEnemySpells(DaedrothSpells); } else if (careerIndex == (int)MonsterCareers.Vampire) { SetEnemySpells(VampireSpells); } else if (careerIndex == (int)MonsterCareers.DaedraSeducer) { SetEnemySpells(SeducerSpells); } else if (careerIndex == (int)MonsterCareers.VampireAncient) { SetEnemySpells(VampireAncientSpells); } else if (careerIndex == (int)MonsterCareers.DaedraLord) { SetEnemySpells(DaedraLordSpells); } else if (careerIndex == (int)MonsterCareers.Lich) { SetEnemySpells(LichSpells); } else if (careerIndex == (int)MonsterCareers.AncientLich) { SetEnemySpells(AncientLichSpells); } } else if (entityType == EntityTypes.EnemyClass && (mobileEnemy.CastsMagic)) { int spellListLevel = level / 3; if (spellListLevel > 6) { spellListLevel = 6; } SetEnemySpells(EnemyClassSpells[spellListLevel]); } FillVitalSigns(); // Could use this to set enemies health and other vitals at a lower level when they first spawn, to simulate them being already wounded or something. }
/// <summary> /// Destroys/Disables a loot container. /// Custom drop containers will be destroyed from world. /// Fixed containers will be disabled so their empty state continues to be serialized. /// </summary> /// <param name="loot">DaggerfallLoot.</param> public static void RemoveLootContainer(DaggerfallLoot loot) { // Corpse markers are not removed from world even if empty if (loot.ContainerType == LootContainerTypes.CorpseMarker) return; // Destroy or disable based on custom flag if (loot.customDrop) GameObject.Destroy(loot.gameObject); else loot.gameObject.SetActive(false); }
/// <summary> /// Check for a large fall, and proceed with move if none found. /// </summary> void MoveIfNoFallDetected(Vector3 motion) { // Check at classic rate to limit ray casts if (classicUpdate) { obstacleDetected = false; fallDetected = false; float currentYPos = transform.position.y; // First check if there is something to collide with directly in movement direction, such as upward sloping ground. // If there is, we assume we won't fall. RaycastHit hit; Vector3 motion2d = motion.normalized; motion2d.y = 0; int checkDistance = 2; Vector3 rayOrigin = transform.position; rayOrigin.y -= controller.height / 4; if (targetPos.y > transform.position.y + controller.height / 2) { rayOrigin.y += controller.height / 2; } Ray ray = new Ray(rayOrigin, motion2d); if (Physics.Raycast(ray, out hit, checkDistance)) { fallDetected = false; obstacleDetected = true; if (lastYPos < currentYPos) { obstacleDetected = false; } DaggerfallEntityBehaviour entityBehaviour2 = hit.transform.GetComponent <DaggerfallEntityBehaviour>(); if (entityBehaviour2 == entityBehaviour.Target) { obstacleDetected = false; } DaggerfallActionDoor door = hit.transform.GetComponent <DaggerfallActionDoor>(); if (door) { obstacleDetected = false; } DaggerfallLoot loot = hit.transform.GetComponent <DaggerfallLoot>(); if (loot) { obstacleDetected = false; } } // Nothing to collide with. Check for a long fall. else { motion2d *= checkDistance; ray = new Ray(rayOrigin + motion2d, Vector3.down); fallDetected = !Physics.Raycast(ray, out hit, 5); } if ((fallDetected || obstacleDetected) && DaggerfallUnity.Settings.EnhancedCombatAI) { FindDetour(motion); } lastYPos = currentYPos; } if (!fallDetected && !obstacleDetected) { controller.SimpleMove(motion); if (lookingForDetour) { lookingForDetour = false; avoidObstaclesTimer = .5f; lastTimeWasStuck = Time.time; detourNumber--; } } if (Time.time - lastTimeWasStuck > 3f) { detourNumber = 0; } }
void CompleteDeath() { if (!entityBehaviour) { return; } // If enemy associated with quest system, make sure quest system is done with it first QuestResourceBehaviour questResourceBehaviour = GetComponent <QuestResourceBehaviour>(); if (questResourceBehaviour) { if (!questResourceBehaviour.IsFoeDead) { return; } } // Disable enemy gameobject // Do not destroy as we must still save enemy state when dead gameObject.SetActive(false); // Show death message string deathMessage = TextManager.Instance.GetLocalizedText("thingJustDied"); deathMessage = deathMessage.Replace("%s", TextManager.Instance.GetLocalizedEnemyName(mobile.Summary.Enemy.ID)); DaggerfallUI.Instance.PopupMessage(deathMessage); // Generate lootable corpse marker DaggerfallLoot loot = GameObjectHelper.CreateLootableCorpseMarker( GameManager.Instance.PlayerObject, entityBehaviour.gameObject, enemyEntity, mobile.Summary.Enemy.CorpseTexture, DaggerfallUnity.NextUID); // This is still required so enemy equipment is not marked as equipped // This item collection is transferred to loot container below for (int i = (int)Items.EquipSlots.Head; i <= (int)Items.EquipSlots.Feet; i++) { Items.DaggerfallUnityItem item = enemyEntity.ItemEquipTable.GetItem((Items.EquipSlots)i); if (item != null) { enemyEntity.ItemEquipTable.UnequipItem((Items.EquipSlots)i); } } entityBehaviour.CorpseLootContainer = loot; // Transfer any items owned by entity to loot container // Many quests will stash a reward in enemy inventory for player to find // This will be in addition to normal random loot table generation loot.Items.TransferAll(entityBehaviour.Entity.Items); // Play body collapse sound if (DaggerfallUI.Instance.DaggerfallAudioSource) { DaggerfallUI.Instance.DaggerfallAudioSource.PlayClipAtPoint(SoundClips.BodyFall, loot.transform.position, 1f); } // Lower enemy alert state on player now that enemy is dead // If this is final enemy targeting player then alert state will remain clear // Other enemies still targeting player will continue to raise alert state every update EnemySenses senses = entityBehaviour.GetComponent <EnemySenses>(); if (senses && senses.Target == GameManager.Instance.PlayerEntityBehaviour) { GameManager.Instance.PlayerEntity.SetEnemyAlert(false); } // Raise static event if (OnEnemyDeath != null) { OnEnemyDeath(this, null); } }
public override void OnPop() { // Clear any loot target on exit if (lootTarget != null) { // Remove loot container if empty if (lootTarget.Items.Count == 0) GameObjectHelper.RemoveLootContainer(lootTarget); lootTarget.OnInventoryClose(); lootTarget = null; } // Generate serializable loot pile in world for dropped items if (droppedItems.Count > 0) { DaggerfallLoot droppedLootContainer = GameObjectHelper.CreateDroppedLootContainer(GameManager.Instance.PlayerObject, DaggerfallUnity.NextUID); droppedLootContainer.Items.TransferAll(droppedItems); } }
void CompleteDeath() { if (!entityBehaviour) { return; } // If enemy associated with quest system, make sure quest system is done with it first QuestResourceBehaviour questResourceBehaviour = GetComponent <QuestResourceBehaviour>(); if (questResourceBehaviour) { if (!questResourceBehaviour.IsFoeDead) { return; } } // Play body collapse sound if (DaggerfallUI.Instance.DaggerfallAudioSource) { AudioClip collapseSound = DaggerfallUI.Instance.DaggerfallAudioSource.GetAudioClip((int)SoundClips.BodyFall); AudioSource.PlayClipAtPoint(collapseSound, entityBehaviour.transform.position, 1.05f); } // Disable enemy gameobject // Do not destroy as we must still save enemy state when dead gameObject.SetActive(false); // Show death message string deathMessage = HardStrings.thingJustDied; deathMessage = deathMessage.Replace("%s", mobile.Summary.Enemy.Name); DaggerfallUI.Instance.PopupMessage(deathMessage); // Generate lootable corpse marker DaggerfallLoot loot = GameObjectHelper.CreateLootableCorpseMarker( GameManager.Instance.PlayerObject, entityBehaviour.gameObject, enemyEntity, mobile.Summary.Enemy.CorpseTexture, DaggerfallUnity.NextUID); // Generate items loot.GenerateItems(); // This is still required so enemy equipment is not marked as equipped // This item collection is transferred to loot container below for (int i = (int)Items.EquipSlots.Head; i <= (int)Items.EquipSlots.Feet; i++) { Items.DaggerfallUnityItem item = enemyEntity.ItemEquipTable.GetItem((Items.EquipSlots)i); if (item != null) { enemyEntity.ItemEquipTable.UnequipItem((Items.EquipSlots)i); } } // Chance of adding map loot.RandomlyAddMap(mobile.Summary.Enemy.MapChance); if (!string.IsNullOrEmpty(mobile.Summary.Enemy.LootTableKey)) { // Chance of adding potion loot.RandomlyAddPotion(3); // Chance of adding potion recipe loot.RandomlyAddPotionRecipe(2); } entityBehaviour.CorpseLootContainer = loot; // Transfer any items owned by entity to loot container // Many quests will stash a reward in enemy inventory for player to find // This will be in addition to normal random loot table generation loot.Items.TransferAll(entityBehaviour.Entity.Items); // Raise static event if (OnEnemyDeath != null) { OnEnemyDeath(this, null); } }