- Timestamp:
- 2012-07-05 14:47:54 (11 years ago)
- Location:
- 2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame
- Files:
-
- 2 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/Entity.cs
r3563 r3596 7 7 namespace Entity 8 8 { 9 public abstractclass EntityBase : PhysicsObject9 public class EntityBase : PhysicsObject 10 10 { 11 11 protected TheDungeonGame game; 12 protected int maxHealth;12 protected int health; 13 13 14 14 public EntityBase(TheDungeonGame game, Vector pos, Vector size, Shape shape) … … 16 16 { 17 17 this.game = game; 18 Tag = "Enemy"; 18 19 Position = pos; 19 20 } 20 21 21 public abstract void init();22 public virtual void init() { } 22 23 23 public void setMaxHealth(int health)24 public void hideEntity() 24 25 { 25 maxHealth = health; 26 if (Brain != null) 27 Brain.Active = false; 28 IsVisible = false; 26 29 } 27 30 31 public void showEntity() 32 { 33 if (Brain != null) 34 Brain.Active = true; 35 IsVisible = true; 36 } 37 38 public void setHealth(int hp) 39 { 40 health = hp; 41 } 42 43 public void hit(int damage) 44 { 45 health -= damage; 46 if (health <= 0) 47 Destroy(); 48 } 49 50 public EntityBase CopyEntity() 51 { 52 EntityBase result = new EntityBase(game, Position, Size, Shape); 53 result.Brain = Brain; 54 result.health = health; 55 return result; 56 } 28 57 } 29 58 } -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/EntityTemplates.cs
r3568 r3596 8 8 namespace Entity 9 9 { 10 public class Entities 11 { 12 public static EntityFly fly = new EntityFly((TheDungeonGame)TheDungeonGame.Instance, Vector.Zero, new Vector(40, 40), Shape.Circle); 13 public static EntityBase[] entities = { fly }; 14 } 15 16 10 17 public class TestEntity : EntityBase 11 18 { … … 26 33 : base(game, pos, size, shape) 27 34 { 35 Brain = new FollowerBrain(game.Player, 100); 36 Brain.Active = false; 37 CollisionIgnoreGroup = CollisionGroups.Rocks; 28 38 } 29 39 30 40 public override void init() 31 41 { 32 Brain = new FollowerBrain(game.Player,200); 33 Brain.Active = true; 42 34 43 } 44 35 45 } 36 46 } -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/LevelGenerator.cs
r3568 r3596 16 16 private List<Room> roomList = new List<Room>(); 17 17 public int level = 1; 18 private int roomsPlaced = 0; 19 private int roomAmount = 0; 18 20 19 21 public LevelGenerator(TheDungeonGame game, Vector size, Vector roomamount) … … 69 71 room.destroyLevel(); 70 72 } 73 roomAmount = 0; 74 roomsPlaced = 0; 71 75 } 72 76 73 77 public void generateRandomLevel(int minRooms, int maxRooms) 74 78 { 75 introomAmount = RandomGen.NextInt(minRooms, maxRooms);79 roomAmount = RandomGen.NextInt(minRooms, maxRooms); 76 80 Room lastRoom = null; 77 81 … … 99 103 else 100 104 { 101 checkedSides++;105 // checkedSides++; 102 106 sideChecked[dir] = true; 103 107 } … … 106 110 { 107 111 lastRoom = currentRoom; 112 roomsPlaced++; 108 113 break; 109 114 } 110 111 115 } 112 116 … … 114 118 } 115 119 116 117 118 120 private Room currentRoom; 119 121 private bool tryCreateRoom(int dir, Vector pos, Room lastRoom) 120 122 { 121 123 if (getRoomFromDirection(lastRoom, dir) != null) return false; 124 125 if (roomsPlaced == roomAmount-5) 126 { 127 currentRoom = RoomCreator.createRoom(game, VecMath.mul(pos, new Vector(TheDungeonGame.ROOMWIDTH + TheDungeonGame.ROOMTHICKNESS, -TheDungeonGame.ROOMHEIGHT - TheDungeonGame.ROOMTHICKNESS)), TheDungeonGame.roomSize, TheDungeonGame.ROOMTHICKNESS, 7); 128 return placeRoomAt(currentRoom, (int)pos.X, (int)pos.Y); 129 } 130 122 131 currentRoom = RoomCreator.createRoom(game, VecMath.mul(pos, new Vector(TheDungeonGame.ROOMWIDTH + TheDungeonGame.ROOMTHICKNESS, -TheDungeonGame.ROOMHEIGHT - TheDungeonGame.ROOMTHICKNESS)), TheDungeonGame.roomSize, TheDungeonGame.ROOMTHICKNESS, RandomGen.NextInt(6)+1); 123 //currentRoom = new EmptyRoom(game, VecMath.mul(pos, new Vector(TheDungeonGame.ROOMWIDTH + TheDungeonGame.ROOMTHICKNESS, -TheDungeonGame.ROOMHEIGHT - TheDungeonGame.ROOMTHICKNESS)), TheDungeonGame.roomSize, TheDungeonGame.ROOMTHICKNESS); 132 124 133 return placeRoomAt(currentRoom, (int)pos.X, (int)pos.Y); 125 134 } -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/Player.cs
r3563 r3596 14 14 public class Player : PhysicsObject 15 15 { 16 public const double playerWidth = 120 /3;17 public const double playerHeight = 180 /3;18 // private PhysicsObject head;16 public const double playerWidth = 120 / 3; 17 public const double playerHeight = 180 / 3; 18 // private PhysicsObject head; 19 19 public Vector movementVector = Vector.Zero; 20 20 public bool isMoving = false; 21 21 private Keyboard keyboard; 22 public double speed = 2 00;22 public double speed = 250; 23 23 private TheDungeonGame game; 24 24 public Room currentRoom; 25 25 public IntMeter playerHealth = new IntMeter(5, 0, 30); 26 26 private Item itemInInventory; 27 private AssaultRifle weapon; 28 29 private Angle UpAngle = Angle.FromDegrees(90); 30 private Angle RightAngle = Angle.Zero; 31 private Angle DownAngle = Angle.FromDegrees(270); 32 private Angle LeftAngle = Angle.FromDegrees(180); 27 33 28 34 public Player(TheDungeonGame game, Vector pos, Image texture) … … 35 41 Image = texture; 36 42 itemInInventory = new TestItem(TheDungeonGame.LoadImage("items/testItem"), "Test Item", false); 43 Tag = "Player"; 44 45 weapon = new AssaultRifle(playerWidth / 3, playerHeight / 3); 46 weapon.Top = Top; 47 weapon.Image = null; 48 weapon.Color = Color.Transparent; 49 weapon.CanHitOwner = false; 50 weapon.InfiniteAmmo = true; 51 weapon.MaxAmmoLifetime = TimeSpan.FromSeconds(0.5); 52 weapon.TimeBetweenUse = TimeSpan.FromSeconds(0.4); 53 weapon.ProjectileCollision = bulletCollided; 54 Add(weapon); 37 55 } 38 56 … … 48 66 keyboard.Listen(Key.S, ButtonState.Released, removeMovement, null, new Vector(0, -speed)); 49 67 keyboard.Listen(Key.D, ButtonState.Released, removeMovement, null, new Vector(speed, 0)); 68 69 keyboard.Listen(Key.Up, ButtonState.Down, shootAmmo, "Shoot up", UpAngle); 70 keyboard.Listen(Key.Down, ButtonState.Down, shootAmmo, "Shoot up", DownAngle); 71 keyboard.Listen(Key.Right, ButtonState.Down, shootAmmo, "Shoot up", RightAngle); 72 keyboard.Listen(Key.Left, ButtonState.Down, shootAmmo, "Shoot up", LeftAngle); 73 50 74 keyboard.Listen(Key.Space, ButtonState.Pressed, useItemInInventory, "Use Item"); 75 } 76 77 private void bulletCollided(PhysicsObject collidingObject, PhysicsObject targetObject) 78 { 79 80 if (targetObject.Tag.Equals("Enemy")) 81 { 82 EntityBase entity = (EntityBase)targetObject; 83 entity.hit(WeaponDamage); 84 collidingObject.Destroy(); 85 currentRoom.EntityAmount--; 86 } 87 else if(targetObject.Tag.Equals("Wall") || targetObject.Tag.Equals("ObjectRock")) 88 collidingObject.Destroy(); 89 } 90 91 public TimeSpan WeaponSpeed 92 { 93 get 94 { 95 return weapon.TimeBetweenUse; 96 } 97 set 98 { 99 weapon.TimeBetweenUse = value; 100 } 101 } 102 103 public TimeSpan BulletLife 104 { 105 get 106 { 107 return weapon.MaxAmmoLifetime; 108 } 109 set 110 { 111 weapon.MaxAmmoLifetime = value; 112 } 113 } 114 115 public int WeaponDamage 116 { 117 get; 118 set; 119 } 120 121 private void shootAmmo(Angle angle) 122 { 123 weapon.Angle = angle; 124 weapon.Shoot(); 51 125 } 52 126 … … 78 152 { 79 153 Room room; 154 if (target.Tag.Equals(RoomDirection.North) || target.Tag.Equals(RoomDirection.East) || target.Tag.Equals(RoomDirection.South) || target.Tag.Equals(RoomDirection.West)) 155 { 156 ObjectDoor door = (ObjectDoor)target; 157 if (door != null && door.IsLocked) return; 158 } 159 80 160 if (target.Tag.Equals(RoomDirection.North)) 81 161 { … … 83 163 game.moveToRoom(room, RoomDirection.North); 84 164 currentRoom = room; 85 }else 86 if(target.Tag.Equals(RoomDirection.East)) 165 } 166 else 167 if (target.Tag.Equals(RoomDirection.East)) 87 168 { 88 169 room = game.LevelGen.getRoomAt(currentRoom.PosOnGrid + RoomDirection.getOffsetFromDir(RoomDirection.East)); … … 91 172 } 92 173 else 93 if (target.Tag.Equals(RoomDirection.South))174 if (target.Tag.Equals(RoomDirection.South)) 94 175 { 95 176 room = game.LevelGen.getRoomAt(currentRoom.PosOnGrid + RoomDirection.getOffsetFromDir(RoomDirection.South)); … … 97 178 currentRoom = room; 98 179 } 99 else 100 if (target.Tag.Equals(RoomDirection.West))180 else 181 if (target.Tag.Equals(RoomDirection.West)) 101 182 { 102 183 room = game.LevelGen.getRoomAt(currentRoom.PosOnGrid + RoomDirection.getOffsetFromDir(RoomDirection.West)); … … 108 189 public void handleItemPickup(IPhysicsObject player, IPhysicsObject item) 109 190 { 110 ItemEntity itemEnt = 191 ItemEntity itemEnt = (ItemEntity)item; 111 192 itemEnt.bindedItem.onPickup(this); 112 193 item.Destroy(); … … 122 203 123 204 isMoving = false; 205 206 Console.WriteLine(currentRoom.EntityAmount); 207 208 if (currentRoom.EntityAmount == 0) 209 { 210 foreach (ObjectDoor door in currentRoom.doors) 211 { 212 if (door != null) 213 door.IsLocked = false; 214 } 215 } 216 else 217 { 218 foreach (ObjectDoor door in currentRoom.doors) 219 { 220 if (door != null) 221 door.IsLocked = true; 222 } 223 } 224 124 225 125 226 base.Update(time); -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/Room.cs
r3572 r3596 18 18 public bool isBuilt = false; 19 19 protected IPhysicsObject[,] insideObjects; 20 p rotected PhysicsObject[] doors = new PhysicsObject[4];20 public ObjectDoor[] doors = new ObjectDoor[4]; 21 21 protected double blockWidth, blockHeight; 22 22 protected double insideWidth, insideHeight; … … 49 49 public abstract void initRoom(); 50 50 51 public void spawnRandomEntities(int entityAmount) 52 { 53 int entitiesSpawned = 0; 54 while (entitiesSpawned < entityAmount) 55 { 56 EntityBase entity = RoomCreator.getEntity(Entities.entities.Length-1); 57 if (!addEntityAt(entity, RandomGen.NextInt(bWidth - 2) + 1, RandomGen.NextInt(bHeight - 2) + 1)) 58 continue; 59 entitiesSpawned++; 60 } 61 //for (int i = 0; i < entityAmount; i++) 62 //{ 63 // EntityBase entity = RoomCreator.getEntity(Entities.entities.Length-1); 64 // if(!addEntityAt(entity, RandomGen.NextInt(bWidth-2)+1, RandomGen.NextInt(bHeight-2)+1)) 65 66 //} 67 } 68 51 69 protected void createLevelDecorations() 52 70 { … … 74 92 { 75 93 if (obj != null) 76 Game.Add(obj, 1); 94 { 95 if (obj.Tag.Equals("Enemy")) 96 Game.Add(obj, 2); 97 else Game.Add(obj, 1); 98 } 99 77 100 } 78 101 … … 117 140 { 118 141 if (obj != null) 119 obj.IsVisible = false; 142 obj.IsVisible = false; 120 143 } 121 144 } … … 137 160 { 138 161 if (obj != null) 139 obj.IsVisible = true; 162 obj.IsVisible = true; 163 } 164 } 165 166 public void restoreBrains() 167 { 168 foreach (IPhysicsObject obj in insideObjects) 169 { 170 if (obj != null) 171 { 172 if (obj.Brain != null) 173 obj.Brain.Active = true; 174 } 175 } 176 } 177 178 public void killBrains() 179 { 180 foreach (IPhysicsObject obj in insideObjects) 181 { 182 if (obj != null) 183 { 184 if (obj.Tag.Equals("Enemy")) 185 obj.Brain.Active = false; 186 obj.Stop(); 187 } 140 188 } 141 189 } … … 143 191 public void addBlock(IPhysicsObject obj, int bx, int by) 144 192 { 145 if (bx >= bWidth || bx < 0 || by >= bHeight || by < 0 ) throw new Exception("Can't put block to " + bx + " , " + by);193 if (bx >= bWidth || bx < 0 || by >= bHeight || by < 0 || insideObjects[bx, by] != null) return; 146 194 obj.Left = insidePos.X + (bx * blockWidth); 147 195 obj.Top = insidePos.Y - (by * blockHeight); … … 162 210 } 163 211 164 public voidaddEntityAt(EntityBase ent, int bx, int by)165 { 166 if (bx >= bWidth || bx < 0 || by >= bHeight || by < 0 || insideObjects[bx, by] != null) throw new Exception("Can't put block to " + bx + " , " + by);167 EntityAmount++;212 public bool addEntityAt(EntityBase ent, int bx, int by) 213 { 214 if (bx >= bWidth || bx < 0 || by >= bHeight || by < 0 || insideObjects[bx, by] != null) return false; 215 ++EntityAmount; 168 216 ent.Left = insidePos.X + (bx * blockWidth); 169 ent.Top = insidePos.Y +(by * blockHeight);217 ent.Top = insidePos.Y - (by * blockHeight); 170 218 insideObjects[bx, by] = ent; 219 return true; 171 220 } 172 221 … … 187 236 if (room == null) continue; 188 237 189 PhysicsObjectdoor = null;238 ObjectDoor door = null; 190 239 191 240 if (i == RoomDirection.North) 192 241 { 193 door = PhysicsObject.CreateStaticObject(thickness, thickness);242 door = new ObjectDoor(thickness, thickness); 194 243 door.Position = Position + new Vector((width + thickness) / 2, -thickness / 2); 195 244 door.Image = doorTexture; … … 199 248 if (i == RoomDirection.East) 200 249 { 201 door = PhysicsObject.CreateStaticObject(thickness, thickness);250 door = new ObjectDoor(thickness, thickness); 202 251 door.Position = Position + new Vector(width + thickness / 2, -(height + thickness) / 2); 203 252 door.Image = doorTexture; … … 208 257 if (i == RoomDirection.South) 209 258 { 210 door = PhysicsObject.CreateStaticObject(thickness, thickness);259 door = new ObjectDoor(thickness, thickness); 211 260 door.Position = Position + new Vector((thickness + width) / 2, -height - thickness / 2); 212 261 door.Image = Image.Flip(doorTexture); … … 216 265 if (i == RoomDirection.West) 217 266 { 218 door = PhysicsObject.CreateStaticObject(thickness, thickness);267 door = new ObjectDoor(thickness, thickness); 219 268 door.Position = Position + new Vector(thickness / 2, -height / 2 - thickness / 2); 220 269 door.Image = doorTexture; … … 234 283 { 235 284 PhysicsObject result = null; 236 for (int i = 0; i < doors.Length; i++ 285 for (int i = 0; i < doors.Length; i++) 237 286 { 238 287 PhysicsObject door = doors[i]; … … 252 301 wallTop.Color = Color.Blue; 253 302 wallTop.Image = topBottom; 254 PhysicsObject wallLeft = createWall(VecMath.sub(Position, new Vector(0, thickness)), thickness, height );303 PhysicsObject wallLeft = createWall(VecMath.sub(Position, new Vector(0, thickness)), thickness, height - thickness); 255 304 wallLeft.Color = Color.Red; 256 305 wallLeft.Image = leftRight; 257 306 PhysicsObject wallDown = createWall(VecMath.sub(Position, new Vector(0, height)), width + thickness, thickness); 258 307 wallDown.Color = Color.Black; 259 if(topBottom != null) wallDown.Image = Image.Flip(topBottom);308 if (topBottom != null) wallDown.Image = Image.Flip(topBottom); 260 309 PhysicsObject wallRight = createWall(VecMath.add(Position, new Vector(width, -thickness)), thickness, height - thickness); 261 310 wallRight.Color = Color.Orange; 262 if(leftRight != null) wallRight.Image = Image.Mirror(leftRight);311 if (leftRight != null) wallRight.Image = Image.Mirror(leftRight); 263 312 roomDecorations.Add(wallTop); 264 313 roomDecorations.Add(wallLeft); … … 270 319 { 271 320 wall = PhysicsObject.CreateStaticObject(width, height); 321 wall.Tag = "Wall"; 272 322 wall.Left = pos.X; 273 323 wall.Top = pos.Y; -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/RoomTemplates.cs
r3568 r3596 38 38 addBlock(new ObjectRock(blockWidth, blockHeight), 0, bHeight - 1, new Vector(0, -50)); 39 39 addBlock(new ObjectRock(blockWidth, blockHeight), bWidth - 1, bHeight - 1, new Vector(0, -50)); 40 41 spawnRandomEntities(5); 40 42 } 41 43 } … … 61 63 addBlock(new ObjectRock(blockWidth, blockHeight), 8, 1 + i, new Vector(0, -50)); 62 64 } 65 66 spawnRandomEntities(5); 63 67 } 64 68 } … … 94 98 addBlock(new ObjectRock(blockWidth, blockHeight), i + 6, 4); 95 99 } 100 101 spawnRandomEntities(5); 96 102 } 97 103 } … … 116 122 } 117 123 } 124 125 spawnRandomEntities(5); 118 126 } 119 127 } … … 139 147 140 148 deleteBlock(bWidth / 2 - 1, bHeight / 2); 149 spawnRandomEntities(5); 141 150 } 142 151 } … … 159 168 addBlock(new ObjectRock(blockWidth, blockHeight), bx, by); 160 169 } 170 171 spawnRandomEntities(5); 172 } 173 } 174 175 class BossRoom : Room 176 { 177 public BossRoom(TheDungeonGame game, Vector pos, Vector size, double thickness) 178 : base(game, pos, size, thickness) 179 { 180 } 181 182 public override void initRoom() 183 { 184 createLevelDecorations(); 161 185 } 162 186 } … … 181 205 else if (room == 6) 182 206 result = new RoomType6(game, pos, size, thickness); 207 else if (room == 7) 208 result = new BossRoom(game, pos, size, thickness); 183 209 184 210 return result; 185 211 } 212 213 public static EntityBase getEntity(int id) 214 { 215 EntityBase ent = null; 216 if (id == 0) 217 ent = new EntityFly((TheDungeonGame)TheDungeonGame.Instance, Vector.Zero, new Vector(50, 50), Shape.Circle); 218 return ent; 219 } 186 220 } 187 221 } -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/TheDungeonGame.cs
r3572 r3596 41 41 Level.Height = ROOMHEIGHT * 20; 42 42 setupLevelTextures(1); 43 setupObjectTextures(); 44 45 generator = new LevelGenerator(this, Level.Size, new Vector(20, 20)); 46 generator.generateRandomLevel(5, 10); 47 generator.initDungeon(); 48 Room centerRoom = generator.getRoomAt((int)(generator.CenterRoom.X), (int)(generator.CenterRoom.Y)); 49 Camera.Position = centerRoom.Position + new Vector(ROOMWIDTH / 2 + ROOMTHICKNESS / 2, -ROOMHEIGHT / 2 + ROOMTHICKNESS / 4); 43 setupObjectTextures(); 50 44 51 45 player = new Player(this, Vector.Zero, playerPic); 46 47 generator = new LevelGenerator(this, Level.Size, new Vector(20, 20)); 48 generator.generateRandomLevel(10, 20); 49 generator.initDungeon(); 50 setupCamera(); 51 // Camera.Zoom(0.5); 52 52 setupPlayer(); 53 54 53 55 AddCollisionHandler(player, player.performCollision); 54 56 AddCollisionHandler(player, "Item", player.handleItemPickup); … … 97 99 oldPos = player.currentRoom.Position + new Vector(ROOMWIDTH / 2 + ROOMTHICKNESS / 2, -ROOMHEIGHT / 2 + ROOMTHICKNESS / 4); 98 100 newPos = room.Position + new Vector(ROOMWIDTH / 2 + ROOMTHICKNESS / 2, -ROOMHEIGHT / 2 + ROOMTHICKNESS / 4); 99 oldRoom = player.currentRoom; 101 oldRoom = player.currentRoom; 100 102 if (room.isBuilt) room.restoreLevel(); 101 103 else room.buildLevel(); 104 oldRoom.killBrains(); 102 105 moveCameraTo(newPos - oldPos); 103 106 player.Position = player.currentRoom.getDoor(dir).Position + RoomDirection.getOffsetFromWorldDir(dir) * 130; … … 123 126 moveCamera = false; 124 127 oldRoom.hideLevel(); 128 player.currentRoom.restoreBrains(); 125 129 } 126 130 } … … 130 134 player.Position = generator.getRoomAt(generator.CenterRoom).Position + new Vector(ROOMWIDTH / 2, -ROOMHEIGHT / 2); 131 135 player.currentRoom = generator.getRoomAt(generator.CenterRoom); 132 player.currentRoom.buildLevel(); 136 player.currentRoom.buildLevel(); 133 137 Add(player, 2); 138 } 139 140 private void setupCamera() 141 { 142 Room centerRoom = generator.getRoomAt((int)(generator.CenterRoom.X), (int)(generator.CenterRoom.Y)); 143 Camera.Position = centerRoom.Position + new Vector(ROOMWIDTH / 2 + ROOMTHICKNESS / 2, -ROOMHEIGHT / 2 + ROOMTHICKNESS / 4); 134 144 } 135 145 … … 138 148 generator.destroyDungeon(); 139 149 generator = new LevelGenerator(this, Level.Size, new Vector(20, 20)); 140 generator.generateRandomLevel(10, 15);150 generator.generateRandomLevel(10, 20); 141 151 generator.initDungeon(); 142 // generator.buildDungeon();152 player.Position = generator.getRoomAt(generator.CenterRoom).Position + new Vector(ROOMWIDTH / 2, -ROOMHEIGHT / 2); 143 153 player.currentRoom = generator.getRoomAt(generator.CenterRoom); 144 154 player.currentRoom.buildLevel(); 155 setupCamera(); 145 156 } 146 157 -
2012/27/DenisZ/TheDungeonGame/TheDungeonGame/TheDungeonGame/TheDungeonGame.csproj
r3568 r3596 120 120 <Compile Include="ItemEntity.cs" /> 121 121 <Compile Include="LevelGenerator.cs" /> 122 <Compile Include="MobTags.cs" /> 123 <Compile Include="ObjectDoor.cs" /> 122 124 <Compile Include="ObjectRock.cs" /> 123 125 <Compile Include="Room.cs" />
Note: See TracChangeset
for help on using the changeset viewer.