/// <summary> /// Setup new game variables. This involves creating the player, and generating the game map. /// </summary> /// <param name="player">Player</param> /// <param name="gameMap">Game map</param> public static void SetupGameVariables(ref EntityID player, ref GameMap gameMap) { EntityStore.Clear(); player = EntityFunctions.CreatePlayer(new Vector2i(-1, -1)); gameMap = new GameMap(new Vector2i(C.MAP_WIDTH, C.MAP_HEIGHT)); gameMap.MakeMap(C.MAX_ROOMS, C.ROOM_MIN_SIZE, C.ROOM_MAX_SIZE, C.MAP_WIDTH, C.MAP_HEIGHT, player); if (player.e.moveTrail != null) { player.e.moveTrail.Clear(); } }
/// <summary> /// Move towards target position, by one tile /// </summary> /// <param name="targetPos">Target position</param> public void MoveTowards(Vector2i targetPos) { var game = (RetroDungeoneerGame)RB.Game; var delta = targetPos - pos; var distance = delta.Magnitude(); // Normalize delta.x = Mathf.RoundToInt(delta.x / distance); delta.y = Mathf.RoundToInt(delta.y / distance); if (!(game.map.IsBlocked(pos + delta) || !EntityFunctions.GetBlockingEntityAtPos(pos + delta).isEmpty)) { pos += delta; } }
private void PlaceEntities(Rect2i room, int numOfItems, int numOfMonsters) { var mMonsterChances = EntityFunctions.GetMonsterChances(dungeonLevel); var mItemChances = EntityFunctions.GetItemChances(dungeonLevel); // Place monsters var existingMonsterPositions = new List <Vector2i>(); for (int i = 0; i < numOfMonsters; i++) { var randomPos = new Vector2i( Random.Range(room.min.x + 1, room.max.x), Random.Range(room.min.y + 1, room.max.y)); if (!existingMonsterPositions.Contains(randomPos)) { var monsterType = (MonsterType)RandomUtils.RandomChoiceIndex(mMonsterChances); var monster = EntityFunctions.CreateMonster(monsterType, randomPos); if (!monster.isEmpty) { existingMonsterPositions.Add(randomPos); } } } // Place items var existingItemPositions = new List <Vector2i>(); for (int i = 0; i < numOfItems; i++) { var randomPos = new Vector2i( Random.Range(room.min.x + 1, room.max.x), Random.Range(room.min.y + 1, room.max.y)); if (!existingItemPositions.Contains(randomPos)) { var itemType = (ItemType)RandomUtils.RandomChoiceIndex(mItemChances); var item = EntityFunctions.CreateItem(itemType, randomPos); if (!item.isEmpty) { existingItemPositions.Add(randomPos); } } } }
/// <summary> /// Initialize your game here. /// </summary> /// <returns>Return true if successful</returns> public bool Initialize() { assets.LoadAll(); // You can load a spritesheet here RB.SpriteSheetSet(assets.spriteSheet); RB.EffectSet(RB.Effect.Scanlines, 0.5f); EntityFunctions.Initialize(); S.InitializeAnims(); SoundSerializer.Initialize(); InitializeNewGame.InitializeConstants(); RenderFunctions.Initialize(); mSceneGame = new SceneGame(); mSceneMainMenu = new SceneMainMenu(); mSceneMessage = new SceneMessage(); // Generate tile grid, this is a one time operation, we can keep reusing the grid var gridColor = Color.white; for (int x = 0; x < RB.MapSize.width; x++) { for (int y = 0; y < RB.MapSize.height; y++) { RB.MapSpriteSet(C.LAYER_GRID, new Vector2i(x, y), S.GRID, gridColor); } } ChangeScene(SceneEnum.MAIN_MENU); RB.MapLayerSpriteSheetSet(C.LAYER_GRID, assets.spriteSheet); RB.MapLayerSpriteSheetSet(C.LAYER_TERRAIN, assets.spriteSheet); RB.MapLayerSpriteSheetSet(C.LAYER_VISIBILITY, assets.spriteSheet); // Collect any garbage created during initilization to avoid a performance hiccup later. System.GC.Collect(); return(true); }
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); }
/// <summary> /// Initialize your game here. /// </summary> /// <returns>Return true if successful</returns> public bool Initialize() { // You can load a spritesheet here RB.SpriteSheetSetup(0, "Demos/RetroDungeoneer/SpritePack", new Vector2i(16, 16)); RB.SpriteSheetSet(0); var fontSprite = RB.PackedSpriteGet(S.FONT_RETROBLIT_DROPSHADOW); var fontPos = new Vector2i(fontSprite.SourceRect.x + 1, fontSprite.SourceRect.y + 1); RB.FontSetup(C.FONT_RETROBLIT_DROPSHADOW, '!', (char)((int)'~' + 8), fontPos, 0, new Vector2i(6, 7), ((int)'~' + 8) - (int)'!' + 1, 1, 1, false); RB.MusicSetup(C.MUSIC_MAIN_MENU, "Demos/RetroDungeoneer/Music/ReturnToNowhere"); RB.MusicSetup(C.MUSIC_GAME, "Demos/RetroDungeoneer/Music/DungeonAmbience"); RB.MusicSetup(C.MUSIC_DEATH, "Demos/RetroDungeoneer/Music/DeathPiano"); RB.MusicSetup(C.MUSIC_FOREST, "Demos/RetroDungeoneer/Music/ForestAmbience"); RB.SoundSetup(C.SOUND_MONSTER_DEATH, "Demos/RetroDungeoneer/Sounds/MonsterDeath"); RB.SoundSetup(C.SOUND_PLAYER_DEATH, "Demos/RetroDungeoneer/Sounds/PlayerDeath"); RB.SoundSetup(C.SOUND_FOOT_STEP, "Demos/RetroDungeoneer/Sounds/FootStep"); RB.SoundSetup(C.SOUND_MONSTER_ATTACK, "Demos/RetroDungeoneer/Sounds/MonsterAttack"); RB.SoundSetup(C.SOUND_PLAYER_ATTACK, "Demos/RetroDungeoneer/Sounds/PlayerAttack"); RB.SoundSetup(C.SOUND_INVENTORY, "Demos/RetroDungeoneer/Sounds/Inventory"); RB.SoundSetup(C.SOUND_DRINK, "Demos/RetroDungeoneer/Sounds/Drink"); RB.SoundSetup(C.SOUND_MENU_OPEN, "Demos/RetroDungeoneer/Sounds/MenuOpen"); RB.SoundSetup(C.SOUND_MENU_CLOSE, "Demos/RetroDungeoneer/Sounds/MenuClose"); RB.SoundSetup(C.SOUND_STAIRS, "Demos/RetroDungeoneer/Sounds/Stairs"); RB.SoundSetup(C.SOUND_POINTER_SELECT, "Demos/RetroDungeoneer/Sounds/PointerSelect"); RB.SoundSetup(C.SOUND_SELECT_OPTION, "Demos/RetroDungeoneer/Sounds/SelectOption"); RB.SoundSetup(C.SOUND_LEVEL_UP, "Demos/RetroDungeoneer/Sounds/LevelUp"); RB.SoundSetup(C.SOUND_FIREBALL, "Demos/RetroDungeoneer/Sounds/Fireball"); RB.SoundSetup(C.SOUND_LIGHTNING, "Demos/RetroDungeoneer/Sounds/Lightning"); RB.SoundSetup(C.SOUND_CONFUSE, "Demos/RetroDungeoneer/Sounds/Confuse"); RB.SoundSetup(C.SOUND_CHEAT, "Demos/RetroDungeoneer/Sounds/CheatMode"); RB.SoundSetup(C.SOUND_AGGRO1, "Demos/RetroDungeoneer/Sounds/Aggro1"); RB.SoundSetup(C.SOUND_AGGRO2, "Demos/RetroDungeoneer/Sounds/Aggro2"); RB.SoundSetup(C.SOUND_PLAYER_FALL_YELL, "Demos/RetroDungeoneer/Sounds/PlayerFallYell"); RB.SoundSetup(C.SOUND_PORTAL, "Demos/RetroDungeoneer/Sounds/Portal"); RB.SoundSetup(C.SOUND_JUMP, "Demos/RetroDungeoneer/Sounds/Jump"); RB.SoundSetup(C.SOUND_BOW_SHOOT, "Demos/RetroDungeoneer/Sounds/BowShoot"); RB.SoundSetup(C.SOUND_BOW_HIT, "Demos/RetroDungeoneer/Sounds/BowHit"); RB.SoundSetup(C.SOUND_WEB, "Demos/RetroDungeoneer/Sounds/Web"); RB.SoundSetup(C.SOUND_TELEPORT, "Demos/RetroDungeoneer/Sounds/Teleport"); RB.SoundSetup(C.SOUND_SLIME, "Demos/RetroDungeoneer/Sounds/Slime"); RB.ShaderSetup(C.SHADER_VIGNETTE, "Demos/RetroDungeoneer/DrawVignette"); RB.EffectSet(RB.Effect.Scanlines, 0.5f); EntityFunctions.Initialize(); S.InitializeAnims(); InitializeNewGame.InitializeConstants(); RenderFunctions.Initialize(); mSceneGame = new SceneGame(); mSceneMainMenu = new SceneMainMenu(); mSceneMessage = new SceneMessage(); // Generate tile grid, this is a one time operation, we can keep reusing the grid var gridColor = Color.white; for (int x = 0; x < RB.MapSize.width; x++) { for (int y = 0; y < RB.MapSize.height; y++) { RB.MapSpriteSet(C.LAYER_GRID, new Vector2i(x, y), S.GRID, gridColor); } } ChangeScene(SceneEnum.MAIN_MENU); // Collect any garbage created during initilization to avoid a performance hiccup later. System.GC.Collect(); return(true); }
private void MakeForestMap(int mapWidth, int mapHeight, EntityID player) { backgroundColor = new Color32(0x25, 0x8f, 0x4a, 255); mMusic = C.MUSIC_FOREST; var playerEntity = EntityStore.Get(player); if (playerEntity == null) { return; } int width = Random.Range(14, 18); int height = Random.Range(14, 18); int x = (mapWidth / 2) - (width / 2); int y = (mapHeight / 2) - (height / 2); var newRoom = new Rect2i(x, y, width, height); CreateRoom(newRoom, RoomShape.EllipseFuzzy); var roomCenter = newRoom.center; int randExit = Random.Range(0, 4); Vector2i blockadePos = Vector2i.zero; Vector2i thugPos = Vector2i.zero; Vector2i gameExitPos = Vector2i.zero; Vector2i gameExitDir = Vector2i.zero; if (randExit == 0) { CreateHTunnel(roomCenter.x, 0, roomCenter.y); blockadePos = new Vector2i(roomCenter.x - (newRoom.width / 2) - 2, roomCenter.y); gameExitPos = blockadePos; gameExitPos.x -= 3; gameExitDir = new Vector2i(-1, 0); thugPos = blockadePos; thugPos.x += 4; } else if (randExit == 1) { CreateHTunnel(roomCenter.x, mapWidth - 1, roomCenter.y); blockadePos = new Vector2i(roomCenter.x + (newRoom.width / 2) + 2, roomCenter.y); gameExitPos = blockadePos; gameExitPos.x += 3; gameExitDir = new Vector2i(1, 0); thugPos = blockadePos; thugPos.x -= 4; } else if (randExit == 2) { CreateVTunnel(0, roomCenter.y, roomCenter.x); blockadePos = new Vector2i(roomCenter.x, roomCenter.y - (newRoom.height / 2) - 2); gameExitPos = blockadePos; gameExitPos.y -= 3; gameExitDir = new Vector2i(0, -1); thugPos = blockadePos; thugPos.y += 4; } else if (randExit == 3) { CreateVTunnel(mapHeight - 1, roomCenter.y, roomCenter.x); blockadePos = new Vector2i(roomCenter.x, roomCenter.y + (newRoom.height / 2) + 2); gameExitPos = blockadePos; gameExitPos.y += 3; gameExitDir = new Vector2i(0, 1); thugPos = blockadePos; thugPos.y -= 4; } // This is the first room, put the player in the center of it playerEntity.pos = RandomUtils.RandomRectPerimeterPos(new Rect2i(roomCenter.x - 1, roomCenter.y - 1, 3, 3)); EntityID thug = EntityID.empty; if (dungeonLevel == 0) { thug = EntityFunctions.CreateMonster(MonsterType.InvincibleThug, thugPos); EntityFunctions.CreateMonster(MonsterType.InvincibleBlockade, blockadePos); } else { thug = EntityFunctions.CreateMonster(MonsterType.Thug, thugPos); EntityFunctions.CreateMonster(MonsterType.Blockade, blockadePos); while (gameExitPos.x > 0 && gameExitPos.y > 0 && gameExitPos.x < size.width && gameExitPos.y < size.height) { EntityFunctions.CreateInteractable(InteractableType.GameExit, gameExitPos); gameExitPos += gameExitDir; } } // Give thug a dagger and armor var dagger = EntityFunctions.CreateItem(ItemType.Dagger, new Vector2i(-1, -1)); thug.e.inventory = new Inventory(1); thug.e.equipment = new Equipment(); thug.e.equipment.equipment[(int)dagger.e.equippable.slot] = dagger; var armor = EntityFunctions.CreateItem(ItemType.LeatherArmor, new Vector2i(-1, -1)); thug.e.equipment.equipment[(int)armor.e.equippable.slot] = armor; var well = EntityFunctions.CreateInteractable(InteractableType.Well, new Vector2i(roomCenter.x, roomCenter.y + 2)); well.e.stairs = new Stairs(dungeonLevel + 1); if (dungeonLevel == 0) { well.e.stairs.type = Stairs.StairType.WELL; } else { well.e.stairs.type = Stairs.StairType.WELL_CLOSED; } UpdateAllEntityMapPositions(); BeautifyForestMap(); RecomputeFov(player, C.FOV_RADIUS); var game = (RetroDungeoneerGame)RB.Game; var options = new List <SceneMessage.MessageBoxOption>(); options.Add(new SceneMessage.MessageBoxOption(C.FSTR.Set("Continue"), CloseMessageBox)); if (dungeonLevel == 0) { C.FSTR.Set(C.STR_COLOR_DIALOG); C.FSTR.Append("You've been travelling through the forest for hours and decide to rest at a nearby clearing."); C.FSTR.Append("\n\n"); C.FSTR.Append("As you settle down a man appears behind you. He swings his sword at a rope tied to a tree beside him. Branches and debris fall onto the path blocking your only exit. It appears you've been ambushed."); C.FSTR.Append("\n\n"); C.FSTR.Append(C.STR_COLOR_NAME); C.FSTR.Append("\"I'll take what you're carrying, once I'm done carving you up!\"").Append(C.STR_COLOR_DIALOG).Append(" the brigand says with a grimace."); C.FSTR.Append("\n\n"); C.FSTR.Append("Your eyes dart to an old well in the middle of the clearing. Your options seem clear... jump down the well, or die here."); game.ShowMessageBox( C.FSTR2.Set("Trapped"), C.FSTR, options); } else { C.FSTR.Set(C.STR_COLOR_DIALOG); C.FSTR.Append("You open your eyes, the swirling portal closes behind you. You find yourself back where it all started, in the forest clearing."); C.FSTR.Append("\n\n"); C.FSTR.Append("The brigand who ambushed you is still here, preparing his trap for the next victim."); C.FSTR.Append("\n\n"); C.FSTR.Append(C.STR_COLOR_NAME); C.FSTR.Append("\"How did you... nevermind, lets finish what we started!\"").Append(C.STR_COLOR_DIALOG).Append(" he says, startled by your sudden appearance."); C.FSTR.Append("\n\n"); C.FSTR.Append("You tighten your grip on your weapon, you won't be jumping into the well this time..."); game.ShowMessageBox( C.FSTR2.Set("A Sudden Return"), C.FSTR, options); } }
private void MakeDungeonMap(int maxRooms, int roomMinSize, int roomMaxSize, int mapWidth, int mapHeight, EntityID player) { backgroundColor = new Color32(0x47, 0x2d, 0x3c, 255); mMusic = C.MUSIC_GAME; var playerEntity = EntityStore.Get(player); if (playerEntity == null) { return; } List <Rect2i> rooms = new List <Rect2i>(); for (int r = 0; r < maxRooms; r++) { // Random width and height int width = 0; int height = 0; if (dungeonLevel < 5 || rooms.Count > 0) { width = Random.Range(roomMinSize, roomMaxSize + 1); height = Random.Range(roomMinSize, roomMaxSize + 1); } else { width = Random.Range(roomMinSize * 2, (roomMaxSize * 2) + 1); height = Random.Range(roomMinSize * 2, (roomMaxSize * 2) + 1); } // Random position int x = Random.Range(0, mapWidth - width); int y = Random.Range(0, mapHeight - height); var newRoom = new Rect2i(x, y, width, height); // Check if the room intersects with other rooms bool intersects = false; for (int i = 0; i < rooms.Count; i++) { if (newRoom.Intersects(rooms[i])) { intersects = true; break; } } // No intersections, it's safe to create this room if (!intersects) { CreateRoom(newRoom, RoomShape.Rectangle); var roomCenter = newRoom.center; if (rooms.Count > 0) { // All rooms after the first one should be connected with a tunnel to the previous room var prevRoomCenter = rooms[rooms.Count - 1].center; if (Random.Range(0, 2) == 1) { CreateHTunnel(prevRoomCenter.x, roomCenter.x, prevRoomCenter.y); CreateVTunnel(prevRoomCenter.y, roomCenter.y, roomCenter.x); } else { CreateVTunnel(prevRoomCenter.y, roomCenter.y, prevRoomCenter.x); CreateHTunnel(prevRoomCenter.x, roomCenter.x, roomCenter.y); } } // Add the new room to list of rooms rooms.Add(newRoom); } } // Populate all rooms except for first and last for (int i = 1; i < rooms.Count - 1; i++) { if (i != 0 && i != rooms.Count - 1) { var numOfMonsters = RandomUtils.FromDungeonLevel( new RandomUtils.Tuple <int, int>[] { new RandomUtils.Tuple <int, int>(2, 1), new RandomUtils.Tuple <int, int>(3, 3), new RandomUtils.Tuple <int, int>(5, 5) }, dungeonLevel); var numOfItems = RandomUtils.FromDungeonLevel( new RandomUtils.Tuple <int, int>[] { new RandomUtils.Tuple <int, int>(1, 1), new RandomUtils.Tuple <int, int>(2, 4) }, dungeonLevel); PlaceEntities(rooms[i], numOfItems, numOfMonsters); } } var firstRoom = rooms[0]; var lastRoom = rooms[rooms.Count - 1]; if (dungeonLevel < 5) { MakeStartRoom(player, firstRoom); var stairsDown = EntityFunctions.CreateInteractable(InteractableType.Stairs, lastRoom.center); stairsDown.e.stairs = new Stairs(dungeonLevel + 1); } else { // For final level make the player start room the last room, and boss room the first room // This makes it easier to ensure we can fit a nice big boss room before creating other rooms MakeBossRoom(firstRoom); MakeStartRoom(player, lastRoom); } UpdateAllEntityMapPositions(); BeautifyDungeonMap(); RecomputeFov(player, C.FOV_RADIUS); if (dungeonLevel == 1) { EffectManager.Instance.AddEffect(new EffectPlayerEntrance(player)); } }
/// <summary> /// Make test final boss room /// </summary> /// <param name="room">Room rect</param> public void MakeTestBossRoom(Rect2i room) { var boss = EntityFunctions.CreateMonster(MonsterType.Golem, new Vector2i(room.center.x, room.center.y - (room.height / 4))); boss.e.fighter.hp = 1; }
/// <summary> /// Make final boss room /// </summary> /// <param name="room">Room rect</param> public void MakeBossRoom(Rect2i room) { EntityFunctions.CreateMonster(MonsterType.Golem, new Vector2i(room.center.x, room.center.y - (room.height / 4))); }
private bool DoSkill(ResultSet resultSet, EntityID target) { if (mSkills.Count == 0) { return(false); } int skillIndex = RandomUtils.RandomChoiceIndex(mSkillChances); Skill randomSkill = mSkills[skillIndex]; // Check if there is a backup melee skill bool hasMeleeSkill = false; for (int i = 0; i < mSkills.Count; i++) { if (mSkills[i] == Skill.MeleeAttack) { hasMeleeSkill = true; break; } } var game = (RetroDungeoneerGame)RB.Game; bool skillResult = false; switch (randomSkill) { case Skill.MeleeAttack: return(SkillFunctions.MeleeAttack(resultSet, owner, target)); case Skill.CastWeb: skillResult = SkillFunctions.CastWeb(resultSet, owner, target.e.pos); break; case Skill.CastLightning: skillResult = SkillFunctions.CastLightning(resultSet, owner, target.e.pos, 6, 12); break; case Skill.ShootBow: { var arrow = owner.e.inventory.GetArrow(); if (!arrow.isEmpty) { skillResult = SkillFunctions.ShootBow(resultSet, owner, arrow, 6, 10, target.e.pos); if (skillResult) { owner.e.inventory.RemoveItem(null, arrow); EntityStore.DestroyEntity(arrow); } } } break; case Skill.CastRandomFireball: { int attempts = 40; while (attempts > 0) { var fireballPos = new Vector2i(target.e.pos.x, target.e.pos.y); fireballPos.x += Random.Range(-20, 21); fireballPos.y += Random.Range(-20, 21); if (game.map.IsInFOV(fireballPos)) { skillResult = SkillFunctions.CastFireball(resultSet, owner, 4, 10, fireballPos, false); break; } attempts--; } } break; case Skill.Teleport: { // Find a location to teleport on the furthest side of target from where the entity is now // Limited attempts are made, so this may not end up being furthest var furthestPos = new Vector2i(-1, -1); var furthestDist = 0.0f; int attempts = 40; Vector2i targetPos = target.e.pos; while (attempts > 0) { var randomPos = new Vector2i(Random.Range(targetPos.x - 1, targetPos.x + 2), Random.Range(targetPos.y - 1, targetPos.y + 2)); if (EntityFunctions.GetBlockingEntityAtPos(randomPos).isEmpty&& !game.map.IsBlocked(randomPos)) { float dist = (owner.e.pos - randomPos).SqrMagnitude(); if (dist > furthestDist) { furthestDist = dist; furthestPos = randomPos; } } attempts--; } if (furthestPos.x != -1) { skillResult = SkillFunctions.Teleport(resultSet, owner, furthestPos); } else { Debug.Log("Failed"); } } break; } if (skillResult == false && hasMeleeSkill) { skillResult = SkillFunctions.MeleeAttack(resultSet, owner, target); } return(skillResult); }
/// <summary> /// Refresh the floodmap, from the given origin /// </summary> /// <param name="origin">Origin position</param> /// <param name="maxDist">Maximum distance</param> public void Refresh(Vector2i origin, int maxDist) { var game = (RetroDungeoneerGame)RB.Game; // Square the distance so we can use squared distance comparison // instead of having to use square root maxDist = maxDist * maxDist; // Reset all costs to max for (int i = 0; i < mCosts.Length; i++) { mCosts[i] = int.MaxValue; } // Set cost at origin to 0 mCosts[origin.x + (origin.y * mSize.width)] = 0; int count = 0; mSearchNodes[count] = origin.x + (origin.y * mSize.width); count++; int firstNode = 0; int lastNode = 0; while (count > 0) { int i = mSearchNodes[firstNode]; firstNode++; count--; int cost = mCosts[i]; var pos = new Vector2i(i % mSize.width, i / mSize.width); // Expand flood into all cardinal directions. for (int d = 0; d < Direction.Cardinal.Length; d++) { var dir = Direction.Cardinal[d]; int newCost = cost + CARDINAL_COST; var dest = pos + dir; var deltaFromOrigin = origin - dest; if (!EntityFunctions.GetBlockingEntityAtPos(dest).isEmpty) { newCost += ENTITY_COST; } if (!game.map.IsBlocked(dest) && deltaFromOrigin.SqrMagnitude() <= maxDist) { int j = dest.x + (dest.y * mSize.width); if (mCosts[j] > newCost) { mCosts[j] = newCost; mSearchNodes[lastNode + 1] = j; lastNode++; count++; } } } // Expand flood into all diagonal directions. for (int d = 0; d < Direction.Diagonal.Length; d++) { var dir = Direction.Diagonal[d]; int newCost = cost + DIAGONAL_COST; var dest = pos + dir; var deltaFromOrigin = origin - dest; if (!EntityFunctions.GetBlockingEntityAtPos(dest).isEmpty) { newCost += ENTITY_COST; } if (!game.map.IsBlocked(dest) && deltaFromOrigin.SqrMagnitude() <= maxDist) { int j = dest.x + (dest.y * mSize.width); if (mCosts[j] > newCost) { mCosts[j] = newCost; mSearchNodes[lastNode + 1] = j; lastNode++; count++; } } } } }