private static bool CheckTargetPosValid(ResultSet resultSet, Vector2i targetPos, EntityID user) { var game = (RetroDungeoneerGame)RB.Game; if (!game.map.IsInFOV(targetPos) || !game.map.IsInFOV(user.e.pos)) { if (user.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("You cannot target a tile outside of your field of view.")); } return(false); } var tile = game.map.terrain[targetPos.x, targetPos.y]; if (tile.blocked) { if (user.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("You cannot target a blocking tile.")); } return(false); } return(true); }
/// <summary> /// Toggle equipped status of entity, this will generate the proper result to potential add or remove the entity /// from the equipment array /// </summary> /// <param name="resultSet">Result</param> /// <param name="equippableEntity">Entity to equip</param> public void ToggleEquip(ResultSet resultSet, EntityID equippableEntity) { if (equippableEntity.e == null || equippableEntity.e.equippable == null) { return; } var slot = equippableEntity.e.equippable.slot; if (equipment[(int)slot] == equippableEntity) { resultSet.AddDequipped(owner, equippableEntity); resultSet.AddMessage( C.FSTR.Set(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- dequipped the ").Append(C.STR_COLOR_NAME).Append(equipment[(int)slot].e.name).Append("@-.")); equipment[(int)slot] = EntityID.empty; } else { if (!equipment[(int)slot].isEmpty) { resultSet.AddDequipped(owner, equipment[(int)slot]); resultSet.AddMessage( C.FSTR.Set(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- dequipped the ").Append(C.STR_COLOR_NAME).Append(equipment[(int)slot].e.name).Append("@-.")); } equipment[(int)slot] = equippableEntity; resultSet.AddEquipped(owner, equipment[(int)slot]); resultSet.AddMessage( C.FSTR.Set(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- equipped the ").Append(C.STR_COLOR_NAME).Append(equippableEntity.e.name).Append("@-.")); } }
/// <summary> /// Teleport /// </summary> /// <param name="resultSet">Result set</param> /// <param name="caster">Caster</param> /// <param name="targetPos">Target position</param> /// <returns>True if successful</returns> public static bool Teleport(ResultSet resultSet, EntityID caster, Vector2i targetPos) { var game = (RetroDungeoneerGame)RB.Game; if (!CheckTargetPosValid(resultSet, targetPos, caster)) { return(false); } if ((targetPos - caster.e.pos).Magnitude() > 8) { if (caster.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("Target location is too far to teleport to.")); return(false); } } if (!EntityFunctions.GetBlockingEntityAtPos(targetPos).isEmpty || game.map.IsBlocked(targetPos)) { if (caster.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("Target location is blocked, you can't teleport there!")); return(false); } } EffectManager.Instance.AddEffect(new EffectTeleport(caster.e.pos, targetPos)); caster.e.pos = targetPos; resultSet.AddMessage(C.FSTR.Set(C.STR_COLOR_NAME).Append(caster.e.name).Append("@- teleported!")); return(true); }
/// <summary> /// Add item to the inventory /// </summary> /// <param name="resultSet">Result</param> /// <param name="item">Item to add</param> public void AddItem(ResultSet resultSet, EntityID item) { int emptySlot = -1; for (int i = 0; i < items.Length; i++) { if (items[i].isEmpty) { emptySlot = i; break; } } if (emptySlot == -1) { if (resultSet != null) { resultSet.AddMessage(C.FSTR.Set("You cannot carry any more, your inventory is full!")); } } else { if (resultSet != null) { resultSet.AddMessage(C.FSTR.Set("You pickup the ").Append(C.STR_COLOR_NAME).Append(item.e.name).Append("@-!")); resultSet.AddItemAdded(item); } items[emptySlot] = item; } }
/// <summary> /// Make this entity attack another entity /// </summary> /// <param name="resultSet">Result</param> /// <param name="target">Target entity</param> public void Attack(ResultSet resultSet, EntityID target) { if (target.isEmpty) { return; } int damage = power - target.e.fighter.defense; if (damage > 0) { target.e.fighter.TakeDamage(resultSet, damage); resultSet.AddMessage( C.FSTR.Clear() .Append(C.STR_COLOR_NAME) .Append(owner.e.name) .Append("@- attacks ") .Append(C.STR_COLOR_NAME) .Append(target.e.name) .Append("@- for ") .Append(C.STR_COLOR_DAMAGE) .Append(damage) .Append("@- hit points.")); } else { if (Random.Range(0, 3) == 0) { resultSet.AddMessage( C.FSTR.Clear() .Append(C.STR_COLOR_NAME) .Append(owner.e.name) .Append("@- attacks ") .Append(C.STR_COLOR_NAME) .Append(target.e.name) .Append("@- but does no damage.")); } else { damage = 1; target.e.fighter.TakeDamage(resultSet, damage); resultSet.AddMessage( C.FSTR.Clear() .Append(C.STR_COLOR_NAME) .Append(owner.e.name) .Append("@- hits ") .Append(C.STR_COLOR_NAME) .Append(target.e.name) .Append("@- with a glancing blow for ") .Append(C.STR_COLOR_DAMAGE) .Append(damage) .Append("@- hit points.")); } } }
/// <summary> /// Cast confuse /// </summary> /// <param name="resultSet">Result set</param> /// <param name="caster">Caster</param> /// <param name="targetPos">Target position</param> /// <returns>True if successful</returns> public static bool CastConfuse(ResultSet resultSet, EntityID caster, Vector2i targetPos) { var game = (RetroDungeoneerGame)RB.Game; if (!CheckTargetPosValid(resultSet, targetPos, caster)) { return(false); } EntityID target = EntityID.empty; for (int i = 0; i < EntityStore.entities.Count; i++) { var eid = EntityStore.entities[i]; var e = eid.e; if (e.pos == targetPos) { target = eid; break; } } if (!target.isEmpty) { var confusedAi = new ConfusedMonster(); confusedAi.previousAi = target.e.ai; confusedAi.numberOfTurns = 10; target.e.ai = confusedAi; resultSet.AddMessage( C.FSTR.Set("The eyes of the ").Append(C.STR_COLOR_NAME).Append(target.e.name).Append("@- look vacant, as it starts to stumble around!")); EffectManager.Instance.AddEffect(new EffectConfuse(target)); RB.SoundPlay(game.assets.soundConfuse); return(true); } else { resultSet.AddMessage( C.FSTR.Set("There is no targetable enemy at that location.")); } return(false); }
/// <summary> /// Take a turn. /// </summary> /// <param name="resultSet">Result of taking a turn</param> /// <param name="target">Target entity being tracked by the Ai</param> public override void TakeTurn(ResultSet resultSet, EntityID target) { var game = (RetroDungeoneerGame)RB.Game; var monster = owner; // No aggro, see if we can aquire aggro if (!aggroed) { if (game.map.IsInFOV(monster.e.pos)) { float dist = monster.e.DistanceTo(target); if (dist <= aggroRange) { aggroed = true; aggroTimeout = AGGRO_MAX_TIMEOUT; resultSet.AddMessage(C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- spots you!")); EffectManager.Instance.AddEffect(new EffectAggro(monster)); int aggroSound = UnityEngine.Random.Range(0, 2) == 0 ? C.SOUND_AGGRO1 : C.SOUND_AGGRO2; SoundBank.Instance.SoundPlayDelayed(aggroSound, 0.66f, RandomUtils.RandomPitch(0.15f), UnityEngine.Random.Range(0, 8)); } } } else { if (aggroTimeout == 0) { resultSet.AddMessage(C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- gives up the chase.")); aggroed = false; } else { if (!game.map.IsInFOV(monster.e.pos) || monster.e.DistanceTo(target) > aggroRange) { aggroTimeout--; } else { aggroTimeout = AGGRO_MAX_TIMEOUT; } DoSkill(resultSet, target); } } }
/// <summary> /// Kill a monster /// </summary> /// <param name="resultSet">Result</param> /// <param name="monster">Monster entity</param> public static void KillMonster(ResultSet resultSet, EntityID monster) { var game = (RetroDungeoneerGame)RB.Game; var e = monster.e; if (e.ai != null) { e.ai.HandleDeath(resultSet); } e.sprite = S.BONES; e.color = new Color32(200, 200, 200, 255); e.blocks = false; e.fighter = null; e.ai = null; e.renderOrder = RenderFunctions.RenderOrder.CORPSE; // Delete all equipment, this prevents paperDoll rendering on the grave e.equipment = null; C.FSTR.Set(e.name); e.name.Set("remains of ").Append(C.FSTR); resultSet.AddMessage(C.FSTR2.Set(C.STR_COLOR_NAME).Append(C.FSTR).Append("@- is ").Append(C.STR_COLOR_DEAD).Append("@w145dead@w000@-!")); RB.SoundPlay(game.assets.soundMonsterDeath, 1.0f, RandomUtils.RandomPitch(0.1f)); }
/// <summary> /// Use item in the inventory, if its useable /// </summary> /// <param name="resultSet">Result</param> /// <param name="item">Item to use</param> /// <param name="useParams">Usage parameters</param> public void UseItem(ResultSet resultSet, EntityID item, Item.UseParams useParams) { var itemComp = item.e.item; // Not an item if (itemComp == null) { return; } var targetingReady = useParams.bool1; if (itemComp.targeting && !targetingReady && item.e.equippable == null) { resultSet.AddTargeting(item); } else { if (itemComp.useFunction == ItemFunction.NONE) { if (item.e.equippable != null) { resultSet.AddEquip(owner, item); } else { resultSet.AddMessage(C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(item.e.name).Append("@- cannot be used")); } } else { itemComp.Use(resultSet, owner, useParams); } } }
/// <summary> /// Heal self /// </summary> /// <param name="resultSet">Result set</param> /// <param name="user">User</param> /// <param name="healingAmount">Healing amount</param> /// <returns>True if successful</returns> public static bool Heal(ResultSet resultSet, EntityID user, int healingAmount) { // May need to check if entity is a Player before printing anything if (user.e.fighter.hp == user.e.fighter.maxHp) { if (user.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("You are already at full health.")); } } else { user.e.fighter.Heal(resultSet, healingAmount); resultSet.AddMessage(C.FSTR.Set("Your wounds start to feel better!")); RB.SoundPlay(C.SOUND_DRINK, 1, RandomUtils.RandomPitch(0.1f)); return(true); } return(false); }
/// <summary> /// Drop item on the ground from inventory /// </summary> /// <param name="resultSet">Result</param> /// <param name="item">Item to drop</param> public void DropItem(ResultSet resultSet, EntityID item) { DeleteItemFromList(item); item.e.pos = owner.e.pos; // Dequip item first if its currently equipped if (owner.e.equipment != null && item.e.equippable != null) { var slot = item.e.equippable.slot; if (owner.e.equipment.equipment[(int)slot] == item) { owner.e.equipment.ToggleEquip(resultSet, item); } } resultSet.AddItemDropped(item); resultSet.AddMessage(C.FSTR.Set("You dropped the ").Append(C.STR_COLOR_NAME).Append(item.e.name).Append("@-.")); }
/// <summary> /// Take a turn. /// </summary> /// <param name="resultSet">Result of taking a turn</param> /// <param name="target">Target entity being tracked by the Ai</param> public override void TakeTurn(ResultSet resultSet, EntityID target) { if (numberOfTurns > 0) { var randomPos = owner.e.pos; randomPos.x += Random.Range(-1, 2); randomPos.y += Random.Range(-1, 2); if (randomPos != owner.e.pos) { owner.e.MoveTowards(randomPos); } numberOfTurns--; } else { owner.e.ai = previousAi; resultSet.AddMessage(C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(owner.e.name).Append("@- is no longer confused!")); } }
/// <summary> /// Kill the player /// </summary> /// <param name="resultSet">Result</param> /// <param name="player">Player entity</param> public static void KillPlayer(ResultSet resultSet, EntityID player) { var pe = player.e; pe.sprite = S.GRAVESTONE; pe.color = new Color32(200, 200, 200, 255); C.FSTR.Set(pe.name); pe.name.Set("grave of ").Append(C.FSTR); pe.renderOrder = RenderFunctions.RenderOrder.CORPSE; // Delete all equipment, this prevents paperDoll rendering on the grave pe.equipment = null; resultSet.AddMessage(C.FSTR.Set("You ").Append(C.STR_COLOR_DEAD).Append("@w145died@w000@-!")); RB.SoundPlay(C.SOUND_PLAYER_DEATH, 1.0f); RB.MusicVolumeSet(1.0f); RB.MusicPlay(C.MUSIC_DEATH); EffectManager.Instance.AddEffect(new EffectPlayerDeath(player)); }
/// <summary> /// Cast lightning /// </summary> /// <param name="resultSet">Result set</param> /// <param name="caster">Caster</param> /// <param name="targetPos">Target pos</param> /// <param name="maxRange">Maximum spell range</param> /// <param name="damage">Damage</param> /// <returns>True if successful</returns> public static bool CastLightning(ResultSet resultSet, EntityID caster, Vector2i targetPos, int maxRange, int damage) { var game = (RetroDungeoneerGame)RB.Game; EntityID target = EntityID.empty; float closestDist = maxRange + 1; if (targetPos.x == -1) { // Find the closest target in range, and in FOV for (int i = 0; i < EntityStore.entities.Count; i++) { var eid = EntityStore.entities[i]; var e = eid.e; if (e.fighter != null && eid != caster && game.map.IsInFOV(e.pos)) { var dist = caster.e.DistanceTo(eid); if (dist < closestDist) { target = eid; closestDist = dist; } } } } else { for (int i = 0; i < EntityStore.entities.Count; i++) { var eid = EntityStore.entities[i]; var e = eid.e; if (e.pos == targetPos) { target = eid; break; } } } if (!target.isEmpty && CheckTargetPosValid(resultSet, target.e.pos, caster)) { int actualDamage = (damage + caster.e.fighter.power) - target.e.fighter.defense; if (actualDamage == 0) { actualDamage = 1; } target.e.fighter.TakeDamage(resultSet, actualDamage); resultSet.AddMessage( C.FSTR.Set("A lightning bolt strikes the ").Append(C.STR_COLOR_NAME).Append(target.e.name).Append("@- with a loud thunder! The damage is ").Append(C.STR_COLOR_DAMAGE).Append(actualDamage).Append("@-.")); EffectManager.Instance.AddEffect(new EffectLightning(caster, target)); RB.SoundPlay(game.assets.soundLightning); return(true); } else { resultSet.AddMessage(C.FSTR.Set("No enemy is close enough to strike.")); } return(false); }
/// <summary> /// Shoot bow /// </summary> /// <param name="resultSet">Result set</param> /// <param name="caster">Caster</param> /// <param name="arrow">Arrow to shoot</param> /// <param name="maxRange">Max bow range</param> /// <param name="damage">Damage</param> /// <param name="targetPos">Target position</param> /// <returns>True if successful</returns> public static bool ShootBow(ResultSet resultSet, EntityID caster, EntityID arrow, int maxRange, int damage, Vector2i targetPos) { var game = (RetroDungeoneerGame)RB.Game; EntityID target = EntityID.empty; float closestDist = maxRange + 1; if (targetPos.x == -1) { // Find the closest target in range, and in FOV for (int i = 0; i < EntityStore.entities.Count; i++) { var eid = EntityStore.entities[i]; var e = eid.e; if (e.fighter != null && eid != caster && game.map.IsInFOV(e.pos)) { var dist = caster.e.DistanceTo(eid); if (dist <= maxRange && dist < closestDist) { target = eid; closestDist = dist; } } } } else { for (int i = 0; i < EntityStore.entities.Count; i++) { var eid = EntityStore.entities[i]; var e = eid.e; if (e.pos == targetPos) { target = eid; break; } } if (target.isEmpty) { if (caster.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("There is no targetable enemy at that location.")); } return(false); } else if (caster.e.DistanceTo(target) > maxRange) { if (caster.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("The target is out of range of your weapon.")); } return(false); } } if (!target.isEmpty && CheckTargetPosValid(resultSet, target.e.pos, caster)) { int actualDamage = (damage + caster.e.fighter.power) - target.e.fighter.defense; if (actualDamage == 0) { actualDamage = 1; } target.e.fighter.TakeDamage(resultSet, actualDamage); resultSet.AddMessage( C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(arrow.e.name).Append("@- strikes the ").Append(C.STR_COLOR_NAME).Append(target.e.name).Append("@- with a thud. The damage is ").Append(C.STR_COLOR_DAMAGE).Append(actualDamage).Append("@-.")); EffectManager.Instance.AddEffect(new EffectThrow(caster, target, Vector2i.zero, S.ARROW_SHOT, S.ARROW_SHOT_45, arrow.e.color, game.assets.soundBowShoot, game.assets.soundBowHit)); return(true); } else { if (caster.e.isPlayer) { resultSet.AddMessage(C.FSTR.Set("No enemy is close enough to shoot.")); } } return(false); }
private void HandlePlayerTurnKeys(ResultSet resultSet) { // Check if we should block all player input until all keys are up // This is useful to block player input when exiting menus if (mBlockMoveUntilKeyUp) { if (!AnyPlayerKeyDown()) { mBlockMoveUntilKeyUp = false; } else { return; } } if (RB.ButtonPressed(RB.BTN_SYSTEM)) { resultSet.AddExit(); return; } if (RB.KeyPressed(KeyCode.I)) { resultSet.AddShowInventory(); } if (RB.KeyPressed(KeyCode.O)) { resultSet.AddDropInventory(); } if (RB.KeyPressed(KeyCode.P)) { resultSet.AddShowCharacterScreen(); } if (RB.KeyPressed(KeyCode.Return) || RB.KeyPressed(KeyCode.KeypadEnter)) { resultSet.AddTakeStairs(); } if (RB.KeyPressed(KeyCode.H)) { resultSet.AddShowHelp(); } Vector2i delta = Vector2i.zero; if (RB.KeyDown(KeyCode.W) || RB.KeyDown(KeyCode.Keypad8)) { delta.y--; } else if (RB.KeyDown(KeyCode.S) || RB.KeyDown(KeyCode.Keypad2)) { delta.y++; } else if (RB.KeyDown(KeyCode.A) || RB.KeyDown(KeyCode.Keypad4)) { delta.x--; } else if (RB.KeyDown(KeyCode.D) || RB.KeyDown(KeyCode.Keypad6)) { delta.x++; } else if (RB.KeyDown(KeyCode.Q) || RB.KeyDown(KeyCode.Keypad7)) { delta.x--; delta.y--; } else if (RB.KeyDown(KeyCode.E) || RB.KeyDown(KeyCode.Keypad9)) { delta.x++; delta.y--; } else if (RB.KeyDown(KeyCode.Z) || RB.KeyDown(KeyCode.Keypad1)) { delta.x--; delta.y++; } else if (RB.KeyDown(KeyCode.C) || RB.KeyDown(KeyCode.Keypad3)) { delta.x++; delta.y++; } if (delta.x > 1) { delta.x = 1; } else if (delta.x < -1) { delta.x = -1; } if (delta.y > 1) { delta.y = 1; } else if (delta.y < -1) { delta.y = -1; } if (delta.x == 0 && delta.y == 0) { mKeyRepeatStage = 0; } else { var ticksDelta = RB.Ticks - mLastMoveTimestamp; if (mKeyRepeatStage == 0 || (mKeyRepeatStage == 1 && ticksDelta > C.KEY_REPEAT_SPEED_STAGE1) || (mKeyRepeatStage > 1 && ticksDelta > C.KEY_REPEAT_SPEED_STAGE2)) { if (delta.x != 0 || delta.y != 0) { mKeyRepeatStage++; mLastMoveTimestamp = RB.Ticks; resultSet.AddMove(delta); } } } // Wait if (RB.KeyPressed(KeyCode.X) || RB.KeyPressed(KeyCode.Keypad5)) { resultSet.AddMove(Vector2i.zero); } if (RB.KeyPressed(KeyCode.G) || RB.KeyPressed(KeyCode.Keypad0)) { resultSet.AddPickup(mPlayer); } if (RB.KeyPressed(KeyCode.F)) { var ranged = mPlayer.e.equipment.equipment[(int)EquipmentSlot.Ranged]; var arrow = mPlayer.e.inventory.GetArrow(); if (!ranged.isEmpty && !arrow.isEmpty) { resultSet.AddTargeting(ranged); } else { if (ranged.isEmpty) { resultSet.AddMessage(C.FSTR.Set("You do not have a ranged weapon equipped.")); } else if (arrow.isEmpty) { resultSet.AddMessage(C.FSTR.Set("You do not have an arrow to shoot.")); } } } }
private void HandleResult(ResultSet resultSet, Result result, out bool tookTurn) { tookTurn = false; var game = (RetroDungeoneerGame)RB.Game; switch (result.type) { case Result.Type.Move: { Vector2i delta = result.veci1; if ((delta.x != 0 || delta.y != 0) && !mGameMap.IsBlocked(mPlayer.e.pos + delta)) { var destPos = mPlayer.e.pos + delta; var targetEntity = EntityFunctions.GetBlockingEntityAtPos(destPos); if (!targetEntity.isEmpty) { mPlayer.e.fighter.Attack(mResultSet, targetEntity); } else { var entities = EntityFunctions.GetEntitiesAtPos(mPlayer.e.pos); bool stuckInWeb = false; for (int i = 0; i < entities.Count; i++) { if (entities[i].e.groundTrigger != null && entities[i].e.groundTrigger.type == GroundTrigger.GroundTriggerType.Web) { stuckInWeb = true; break; } } // Attempt to free from web if (stuckInWeb) { if (Random.Range(0, 6) == 0) { stuckInWeb = false; resultSet.AddMessage(C.FSTR.Set("You manage to free yourself from the web.")); } } if (!stuckInWeb) { mPlayer.e.Move(delta); mFOVRecompute = true; RB.SoundPlay(game.assets.soundFootStep, 0.66f, RandomUtils.RandomPitch(0.1f)); var entitesAtDest = EntityFunctions.GetEntitiesAtPos(mPlayer.e.pos); for (int i = 0; i < entitesAtDest.Count; i++) { var trigger = entitesAtDest[i].e.groundTrigger; if (trigger != null) { trigger.Trigger(resultSet); } } } else { resultSet.AddMessage(C.FSTR.Set("You fail to free yourself from the web, keep trying.")); RB.SoundPlay(game.assets.soundWeb, 0.5f, RandomUtils.RandomPitch(0.1f)); } } mFloodRecompute = true; tookTurn = true; } else if (delta.x == 0 && delta.y == 0) { // Do nothing, wait tookTurn = true; } } break; case Result.Type.Exit: if (mGameState == GameState.SHOW_INVENTORY || mGameState == GameState.DROP_INVENTORY || mGameState == GameState.SHOW_HELP || mGameState == GameState.CHARACTER_SCREEN) { RB.SoundPlay(game.assets.soundMenuClose, 1, RandomUtils.RandomPitch(0.1f)); ChangeState(mPreviousGameState); } else if (mGameState == GameState.TARGETING) { resultSet.AddTargetingCancelled(); } else { // Exit back to main menu game.ChangeScene(SceneEnum.MAIN_MENU); } break; case Result.Type.Message: mConsole.Log(result.str1); break; case Result.Type.Dead: if (result.entity1 == mPlayer) { DeathFunctions.KillPlayer(mResultSet, result.entity1); ChangeState(GameState.PLAYER_DEAD); } else { DeathFunctions.KillMonster(mResultSet, result.entity1); } break; case Result.Type.Pickup: { var receiver = result.entity1; if (receiver.e.inventory == null) { break; } var entityList = EntityFunctions.GetEntitiesAtPos(receiver.e.pos); if (entityList != null) { for (int j = 0; j < entityList.Count; j++) { var e = entityList[j].e; if (e.item != null) { receiver.e.inventory.AddItem(resultSet, e.id); } } } } break; case Result.Type.ItemAdded: result.entity1.e.renderOrder = RenderFunctions.RenderOrder.HIDDEN; result.entity1.e.pos = new Vector2i(-1, -1); RB.SoundPlay(game.assets.soundInventory, 1, RandomUtils.RandomPitch(0.1f)); break; case Result.Type.ShowInventory: if (mGameState != GameState.SHOW_INVENTORY) { ChangeState(GameState.SHOW_INVENTORY); } else { ChangeState(mPreviousGameState); } break; case Result.Type.InventoryIndex: { var entity = result.entity1; var item = entity.e.inventory.items[result.int1]; mUseParams.Clear(); mUseParams.map = mGameMap; if (mGameState == GameState.SHOW_INVENTORY) { entity.e.inventory.UseItem(resultSet, item, mUseParams); } else if (mGameState == GameState.DROP_INVENTORY) { entity.e.inventory.DropItem(resultSet, item); } } break; case Result.Type.Consumed: { var entity = result.entity1; var item = result.entity2; entity.e.inventory.RemoveItem(resultSet, item); tookTurn = true; } break; case Result.Type.DropInventory: if (mGameState != GameState.DROP_INVENTORY) { ChangeState(GameState.DROP_INVENTORY); } else { ChangeState(mPreviousGameState); } break; case Result.Type.ItemDropped: result.entity1.e.renderOrder = RenderFunctions.RenderOrder.ITEM; tookTurn = true; RB.SoundPlay(game.assets.soundInventory, 1, RandomUtils.RandomPitch(0.1f)); break; case Result.Type.Equip: { var equipper = result.entity1; var item = result.entity2; if (equipper.e == null || equipper.e.equipment == null || item.e == null || item.e.equippable == null) { break; } equipper.e.equipment.ToggleEquip(resultSet, item); } break; case Result.Type.Equipped: { var equipper = result.entity1; var item = result.entity2; if (equipper.e == null || equipper.e.equipment == null || item.e == null || item.e.equippable == null) { break; } RB.SoundPlay(game.assets.soundInventory, 1, RandomUtils.RandomPitch(0.1f)); } break; case Result.Type.Dequipped: { var equipper = result.entity1; var item = result.entity2; if (equipper.e == null || equipper.e.equipment == null || item.e == null || item.e.equippable == null) { break; } RB.SoundPlay(game.assets.soundInventory, 1, RandomUtils.RandomPitch(0.1f)); } break; case Result.Type.Targeting: // Set previous state to players turn to go back to players turn if targeting is cancelled mPreviousGameState = GameState.PLAYER_TURN; ChangeState(GameState.TARGETING); mTargetingItem = result.entity1; mConsole.Log(mTargetingItem.e.item.targetingMessage); break; case Result.Type.LeftClick: { if (mGameState == GameState.TARGETING && !mTargetingItem.isEmpty) { var targetingPos = result.veci1; if (mTargetingItem.e.equippable != null && mTargetingItem.e.equippable.slot == EquipmentSlot.Ranged) { var arrow = mPlayer.e.inventory.GetArrow(); if (SkillFunctions.ShootBow(resultSet, mPlayer, arrow, mTargetingItem.e.item.int2, mTargetingItem.e.item.int1, targetingPos)) { mPlayer.e.inventory.RemoveItem(resultSet, arrow); EntityStore.DestroyEntity(arrow); } ChangeState(GameState.ENEMY_TURN); } else { mUseParams.Clear(); mUseParams.map = mGameMap; mUseParams.veci1 = targetingPos; mUseParams.bool1 = true; mPlayer.e.inventory.UseItem(resultSet, mTargetingItem, mUseParams); } } } break; case Result.Type.RightClick: if (mGameState == GameState.TARGETING) { resultSet.AddTargetingCancelled(); } break; case Result.Type.TargetingCancelled: ChangeState(GameState.PLAYER_TURN); mTargetingItem = EntityID.empty; mConsole.Log(C.FSTR.Set("Targeting cancelled.")); break; case Result.Type.ShowHelp: if (mGameState != GameState.SHOW_HELP) { ChangeState(GameState.SHOW_HELP); } else { ChangeState(mPreviousGameState); } break; case Result.Type.TakeStairs: { int i; bool foundStairs = false; var entityList = EntityFunctions.GetEntitiesAtPos(mPlayer.e.pos); if (entityList != null) { for (i = 0; i < entityList.Count; i++) { var e = entityList[i].e; if (e.stairs != null && e.stairs.type != Stairs.StairType.WELL_CLOSED) { EffectManager.Instance.Clear(); mGameMap.NextFloor(resultSet, mPlayer, mConsole); mFOVRecompute = true; mFloodRecompute = true; // Snap the camera to player mCamera.SetPos(mPlayer); foundStairs = true; if (e.stairs.type == Stairs.StairType.STAIRS) { RB.SoundPlay(game.assets.soundStairs); mPlayer.e.fighter.Heal(resultSet, mPlayer.e.fighter.maxHp / 2); mConsole.Log(C.FSTR.Set("You take a moment to rest, and recover your strength.")); } else if (e.stairs.type == Stairs.StairType.PORTAL) { RB.SoundPlay(game.assets.soundPortal); mPlayer.e.fighter.Heal(resultSet, mPlayer.e.fighter.maxHp / 2); mConsole.Log(C.FSTR.Set("You step into the mysterious portal...")); } else if (e.stairs.type == Stairs.StairType.WELL) { RB.SoundPlay(game.assets.soundJump); mConsole.Log(C.FSTR.Set("You jump down the well into the darkness below...")); } if (mCurrentMusic != mGameMap.music) { RB.MusicPlay(mGameMap.music); mCurrentMusic = mGameMap.music; } // Save the game after descending DataLoaders.SaveGame(C.SAVE_FILENAME, mPlayer, mGameMap, mConsole, mGameState); break; } else if (e.stairs != null && e.stairs.type == Stairs.StairType.WELL_CLOSED) { foundStairs = true; mConsole.Log(C.FSTR.Set("You look down the well. That won't be necessary, not this time...")); } } } if (!foundStairs) { mConsole.Log(C.FSTR.Set("There are no stairs here.")); } } break; case Result.Type.Xp: int xp = result.int1; var leveledUp = mPlayer.e.level.AddXp(xp); mConsole.Log(C.FSTR.Set("You gain ").Append(xp).Append(" experience points.")); if (leveledUp) { mConsole.Log(C.FSTR.Set("Your battle skills grow stronger! You reached level ").Append(mPlayer.e.level.currentLevel).Append("!")); mPreviousGameState = mGameState; ChangeState(GameState.LEVEL_UP); mMenuLevelUp.ClearOptions(); mMenuLevelUp.AddOption(C.FSTR.Set("Constitution (+20 HP, from ").Append(mPlayer.e.fighter.maxHp).Append(")")); mMenuLevelUp.AddOption(C.FSTR.Set("Strength (+1 attack, from ").Append(mPlayer.e.fighter.power).Append(")")); mMenuLevelUp.AddOption(C.FSTR.Set("Agility (+1 defense, from ").Append(mPlayer.e.fighter.defense).Append(")")); RB.SoundPlay(game.assets.soundLevelUp); } break; case Result.Type.LevelUp: var levelUp = (LevelUp)result.int1; switch (levelUp) { case LevelUp.Hp: mPlayer.e.fighter.baseMaxHp += 20; mPlayer.e.fighter.hp += 20; break; case LevelUp.Str: mPlayer.e.fighter.basePower += 1; break; case LevelUp.Def: mPlayer.e.fighter.baseDefense += 1; break; } mGameState = mPreviousGameState; break; case Result.Type.ShowCharacterScreen: mPreviousGameState = mGameState; ChangeState(GameState.CHARACTER_SCREEN); C.FSTR.Clear(); C.FSTR.Append("@FFFFFFLevel: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.level.currentLevel).Append("\n"); C.FSTR.Append("@FFFFFFExperience: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.level.currentXp).Append("\n"); C.FSTR.Append("@FFFFFFExperience to Level: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.level.ExperienceToNextLevel()).Append("\n"); C.FSTR.Append("@FFFFFFMaximum HP: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.fighter.maxHp).Append("\n"); C.FSTR.Append("@FFFFFFAttack: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.fighter.power).Append("\n"); C.FSTR.Append("@FFFFFFDefense: ").Append(C.STR_COLOR_NAME).Append(mPlayer.e.fighter.defense); mMenuCharacterScreen.SetSummary(C.FSTR); break; case Result.Type.BossDefeated: var boss = result.entity1; map.SpawnExitPortal(boss.e.pos); break; case Result.Type.GameWon: { var options = new List <SceneMessage.MessageBoxOption>(); options.Add(new SceneMessage.MessageBoxOption(C.FSTR.Set("Return to Main Menu"), CloseGameWonMessageBox)); C.FSTR.Set(C.STR_COLOR_DIALOG); C.FSTR.Append("You make it out of the forest clearing with adrenaline still pumping through your veins."); C.FSTR.Append("\n\n"); C.FSTR.Append("Your adventure flashes through your mind and you realize you're not the same person you were before. "); C.FSTR.Append("The concerns you once had seem insignificant now."); C.FSTR.Append("\n\n"); C.FSTR.Append("Moments later you calm down, and you find yourself wondering what lies at the bottom of other old wells..."); C.FSTR.Append("\n\n"); C.FSTR.Append(C.STR_COLOR_NAME); C.FSTR.Append("You've won!\nCongratulations!"); game.ShowMessageBox( C.FSTR2.Set("A New Life"), C.FSTR, options); break; } case Result.Type.FellDownWell: { var options = new List <SceneMessage.MessageBoxOption>(); options.Add(new SceneMessage.MessageBoxOption(C.FSTR.Set("Continue"), CloseMessageBox)); C.FSTR.Set(C.STR_COLOR_DIALOG); C.FSTR.Append("This is not what you expected. It seems that not only has the bottom of this well ran dry, it has collapsed into some old dungeon! "); C.FSTR.Append("There must be some way out of this place, there has to be."); C.FSTR.Append("\n\n"); C.FSTR.Append("A sound of skittering creatures can be heard coming from the nearby rooms. "); C.FSTR.Append("You spot a rusty dagger lying beside you."); C.FSTR.Append("\n\n"); C.FSTR.Append("Finding an exit may prove... difficult."); game.ShowMessageBox( C.FSTR2.Set("Bottom of the Well"), C.FSTR, options); break; } case Result.Type.BossEncounter: { var options = new List <SceneMessage.MessageBoxOption>(); options.Add(new SceneMessage.MessageBoxOption(C.FSTR.Set("Continue"), CloseMessageBox)); C.FSTR.Set(C.STR_COLOR_DIALOG); C.FSTR.Append("Before you stands a towering golem. It lurches forward, slowly at first, as if it has not moved in ages."); C.FSTR.Append("\n\n"); C.FSTR.Append(C.STR_COLOR_NAME); C.FSTR.Append("\"Who awakens the Gatekeeper?\"").Append(C.STR_COLOR_DIALOG).Append(" it belows."); C.FSTR.Append("\n\n"); C.FSTR.Append("A Gatekeeper is just what you were looking for. This is your chance to escape the dungeon! You steady yourself, this will not be an easy fight."); game.ShowMessageBox( C.FSTR2.Set("The Gatekeeper"), C.FSTR, options); break; } } }
/// <summary> /// Cast fireball /// </summary> /// <param name="resultSet">Result set</param> /// <param name="caster">Caster</param> /// <param name="radius">Radius of explosion</param> /// <param name="damage">Damage</param> /// <param name="targetPos">Target position</param> /// <param name="hurtsCaster">True if explosion should hurt caster</param> /// <returns>True if successful</returns> public static bool CastFireball(ResultSet resultSet, EntityID caster, int radius, int damage, Vector2i targetPos, bool hurtsCaster) { var game = (RetroDungeoneerGame)RB.Game; var map = game.map; if (!CheckTargetPosValid(resultSet, targetPos, caster)) { return(false); } resultSet.AddMessage( C.FSTR.Set("The fireball explodes, burning everything within ").Append(C.STR_COLOR_NAME).Append(radius).Append("@- tiles!")); // Apply damage to all entities in range for (int x = targetPos.x - radius; x <= targetPos.x + radius; x++) { if (x < 0 || x >= map.size.width) { continue; } for (int y = targetPos.y - radius; y <= targetPos.y + radius; y++) { if (y < 0 || y >= map.size.height) { continue; } var pos = new Vector2i(x, y); var delta = pos - targetPos; int dist = UnityEngine.Mathf.RoundToInt(delta.Magnitude()); if (dist > radius) { continue; } var tile = map.terrain[x, y]; if (!tile.blocked) { var entities = EntityFunctions.GetEntitiesAtPos(new Vector2i(x, y)); for (int i = 0; i < entities.Count; i++) { if (entities[i] == caster && !hurtsCaster) { continue; } var e = entities[i].e; if (e.fighter != null) { int actualDamage = (damage + caster.e.fighter.power) - e.fighter.defense; if (actualDamage == 0) { actualDamage = 1; } resultSet.AddMessage( C.FSTR.Set("The ").Append(C.STR_COLOR_NAME).Append(e.name).Append("@- got burned for ").Append(C.STR_COLOR_DAMAGE).Append(actualDamage).Append("@- hit points.")); e.fighter.TakeDamage(resultSet, actualDamage); } } EffectManager.Instance.AddEffect(new EffectFlame(pos, dist * 4)); } } } RB.SoundPlay(game.assets.soundFireBall); return(true); }