CSCI213
Autumn Session, 2007
Assignment 2: Simple class hierarchies
(You should complete the exercises for laboratory exercise 2 before attempting this assignment.)
The aims of this assignment are to:
The assignment involves writing a "Monster" abstract class and a set of subclasses that define specialized monster subclasses that can be used with supplied code for an "adventure" game like "Hack" or "Rogue".
On completion of this assignment you should be able to:
You are supplied with working code for a part of a simple "adventure" game. Most of you will have played such games, or had a sibling that played such games; so lengthy explanations shouldn't be needed. All such games are based on the same simple themes:
if can attack player's avatar then attack avatar else if can detect player's avatar then advance toward avatar else go about own businesseach different species of monster has a different approach to the tasks. Some monster species rely on meleeing and can attack an adjacent player, others can use ranged attacks. Some monster species use vision to detect the presence of an avatar; others may use different senses and so be able to detect the avatar even when there are dungeon walls that block vision.
If you are a player, you will be using sophisticated massively multiplayer on-line games like Guild Wars, Warcraft, or Everquest, or offline single player games like NeverWinter Nights. But these games date back a long way. If you were playing in the 1970s, you would have played "Colossal Cave" (a single user game), or later "Rogue/Hack". In the 1980s, you could have played "AberMud" (a massively multiplayer on-line game with a text interface).
I didn't have the time to create a Guild Wars like framework, and you don't have the time to learn about such a framework. So the adventure game that you will complete is similar to a late 1980s version of Rogue (Hack, ...) with a simple graphical interface. By default, the game will appear something like the image shown here:
The display shows the (fixed) layout of the dungeon with walls:
that limit most movements. (The dungeon is defined via a grid of
squares - a square may contain a wall or be an open square. An open
square can hold one item - treasure, player, monster. Actually, as
coded, a grid square can hold many treasures and monsters but only one
will be displayed.)
The player avatar is (currently) represented by this
image
(an alternative hero image may be used if this one is felt inappropriate). The image
represents one of the treasures.
The image:
represents one of the inhabitants of the dungeon.
The player controls movements of the avatar using the number keys - "8" moves the avatar up screen, "4" moves left, "3" moves diagonally down and right etc. The player acquires treasure items by causing the avatar to move to the same location; the player can attack a "monster" by moving the avatar to an adjacent location and then trying to move onto the monster's location.
The code that you are supplied with works. However, the dungeon has been depopulated. The classes that define the "Monster" abstraction and the concreter monster subclasses have all been removed.
The framework code supplied can be used essentially unchanged. Your
changes (details below) can be limited to adding the names of a few more
image files to the list of images of game elements (you will need
images to represent instances of your different monster subclasses), and
adding code that loads data defining the abilities and
starting locations of individual monsters. The code for working with
a collection of Monsters is already incorporated in the supplied code.
Your main work will be the definition of an abstract Monster class (that
extends one of the classes supplied) and concrete subclasses. The code
of the "Player" class that is supplied gives examples of how to
move around the dungeon and interact with dungeon items.
Once you have completed the specified requirements of the assignment you
may make further changes in the supplied framework code to enhance your
own version.
The framework is supplied as a NetBeans project (in /share/cs-pub/csci213/A2). The files include a few images, a data file, and the source code files for the classes provided.
The classes in the framework are:
JavaDoc documentation is available for the classes.
The following diagram shows the form of the class hierarchy for the game:
The following code fragment is from the DungeonGame class:
private void showInhabitants()
{
for(Monster m: myCollectionOfInhabitants)
m.show();
}
The DungeonGame has a private data member:
private Vector<Monster> myCollectionOfInhabitants;
This is filled with Monster objects as these get read in the loadItems
function called at startup. Of course, there are no instance of class Monster - it
is an abstraction; instead there are instances of Monster subclasses ("Ghost", "Zombie",
"Mugger", ...). The collection is heterogeneous (containing different types).
In the showInhabitants function, the object reference variable m
is polymorphic - referencing many different shaped objects. The dynamic dispatch
mechanism would be invoked - though in this case all Monster classes will use
the same implementation of show as defined in the base class DungeonItem.
private void moveInhabitants()
{
for(Monster m : myCollectionOfInhabitants)
m.run();
}
The moveInhabitants function works through the collection of Monsters giving
each its chance to do something.
The "run" function that gets invoked here is probably the same and would be
defined in abstract class Monster for it is this function that defines that
common behaviour - if can attack then attack else if can detect then advance
else carry on. However, the functions that define "can attack", "can
detect" would be abstract in class Monster having distinct definitions in
the Monster subclasses. So the functions that are invoked when run is called
are specific to the type of object that is executing the run function. This
is the dynamic dispatch behaviour - invoking the right behaviour for
the particular object accessed via the polymorphic reference Monster m.
You should be able to deploy the supplied NetBeans project and run the application. When the dungeon map is displayed, click on it with the mouse (to assign "keyboard focus"), then use numeric keys to move the player avatar. The "treasures" can be collected; when all are collected the program will terminate.
The layout of the dungeon and location of treasures is defined in
the data2.txt file in the project folder.
Read the code of Images.java to see where you will have to define additional data elements for other images. Read the code of DungeonGame.loadItems to see where you will have to define additional code for loading data on monsters. Read the code of Player.java to get some ideas of how the framework code works.
Define abstract class Monster extends ActiveItem. You
will have to define a run method and some other abstract
methods. (You may find that you need to redefine your Monster class a
couple of times as you explore different ways of factoring out code that
is common to specific subclasses of class Monster).
Define at least four distinct concrete subclasses of Monster that illustrate different implementations of the "can attack", "can detect", "attack", "advance", and "go about normal business" functions. You should be able to draw on your own experience of such games for ideas as to different behaviours. However, here are some suggestions for those who have never played:
Do not create subclasses that differ only parametrically. For example, the following would count as a single class:
(It was OK for the Perl guys with their Animals, but here you have to do things properly!)
When there are only parametric differences, you use the same class. This is illustrated by the code and data supplied. The data include "GOLD" and "HEALTH" objects - but these are both just Collectable objects that differ merely in the image used and the values in other fields.
Add some tracer statements to your classes (and to framework classes if that is more appropriate) so that interactions between player avatar and monsters is logged.
Implement your Monster subclasses, add data to the "data2.txt" data file defining instances of your classes, run the game with an inhabited dungeon.
You will have to read the JavaDoc
descriptions to find out all the functions in Player,
DungeonGame etc that you may want to use. JavaDoc only documents
public functions. You shouldn't need to bother about the private functions; you
would only be concerned with these if you were reworking the entire framework
to add features (such as Monsters that can move treasures around).
All Monster objects are given a reference to the DungeonGame object in their constructor. One of the methods of class DungeonGame returns a reference to the Player object. One of the methods of a Player object returns its current location (this method inheritted from the DungeonItem base class). So, using a couple of method calls, a Monster object can find the current location of the player's avatar.
The DungeonGame class has a utitility method clearLineOfSight;
this determines whether there is a clear line of sight between two points (no
intervening walls) and also fills in a supplied array with the points
along such a line if one exists. A Monster could use that function to
determine whether it could "see" the player. Of course, some types of
monster might sense the player in some other way and have a "canSee" function
that did not require a clear line of sight, instead relying simply on
distance between Monster and Player locations.
The Map provides functions like accessible that a Monster
could use to determine whether it could move to a particular square.
How you use these and other functions depends on the behaviours that you wish to define for your Monster classes.
You are to submit a PDF formatted report file. This report file (prepared in a word processor such as Word and "printed" to PDF file) should contain:
The due date for submission will be announced in lectures; the date will probably be around the end of week 6 of session (currently set for April 6th).
turnin -c csci213 -a 2 A2.pdf
Reminders as before