javaandroidadventure

Game message is not displayed


I'm writing a small adventure game for Android. The code and demo is available from my repository. I have program trying to achieve a game message that is displayed with information which character enters the room. But the message is only displayed "sometimes" which is confusing.

The message is supposed to be "A terrifying skeleton warrior enters." and in the code this part is in the class Person.java. This game message is not always displayed and I wonder why?

package dev.game.adventure;

import java.util.*;

/**
 * ADT for persons which is completed which subclasses to creating actors with
 * specific properties
 */
public class Person {

    /**
     * Name of the Person.
     */
    public String name;


    /**
     * The World which this Person is associated with.
     */
    public World world;

    /**
     * Tells where this Person is at the moment.
     */
    public Place place;

    /**
     * The inventory, contains Things.
     */
    public Collection<Thing> things;
    FullscreenActivity target;
    /**
     * Shows how the Person looks like
     */
    public int image;

    public String getHelloWorldString() {
        return "HELLO WORLD";
    }

    /**
     * Create Person named `name' in world `world'.
     *
     * @param world The world where the Person is created.
     * @param name  Name of the Person.
     * @param app   An image used to display the Person.
     */
    public Person(World world, String name, int app, FullscreenActivity target) {
        this.world = world;
        this.name = name;
        this.image = app;
        this.target = target;
        place = world.defaultPlace();
        if (this.getName().equals("Skeleton")) {
            world.sayAtPlace(place,
                    "A terrifying skeleton warrior appears!", target);
        }
        place.enter(this, target);
        things = Collections.synchronizedCollection(new LinkedList<Thing>());
    }

    /**
     * Go directly, and quietly, to 'place'. That is to say, without posting a
     * message that you're doing so.
     *
     * @param place A string referring to the Place to go to.
     * @see #goTo(Place, FullscreenActivity)
     * @see #go
     */
    public void goTo(String place, FullscreenActivity target) {
        goTo(world.getPlace(place), target);
    }

    /**
     * Go directly, and quietly, to `whereTo'. That is to say, without posting a
     * message that you're doing so.
     *
     * @param whereTo The Place to go to. Can be null in which case nothing happens.
     * @see #goTo(Place, FullscreenActivity
     * @see #go
     */
    public void goTo(Place whereTo, FullscreenActivity target) {
        if (whereTo != null) {
            place.exit(this, target);
            whereTo.enter(this, target);

            if (this.getName().equals("Skeleton")) {
                world.sayAtPlace(whereTo,
                        "A terrifying skeleton warrior appears!", target);
            }

            world.update(place, target);
            // Record our new position.
            place = whereTo;
            // Also update Player's here.
            world.update(place, target);
        }
    }

    /**
     * Go through the door `door', verbosly. That is to say, post a message that
     * you re doing so. Don't complain if the door doesn't exist.
     *
     * @param door Name of the door to exit through.
     * @see #goTo(String)
     * @see #goTo(Place, FullscreenActivity
     */
    public void go(String door, FullscreenActivity target) {
        Place whereTo = place.getExit(door);
        if (whereTo != null) {
            if (!name.equals("You")) {
                world.sayAtPlace(place, name + " goes " + door, target);
            } else {
                world.sayAtPlace(place, name + " are going " + door, target);
            }
            goTo(whereTo, target);
        }
    }

    /**
     * Print `text' in the bottom of the text window. Prepend it with a cue
     * indicating who is speaking. The text will only show up for Players at the
     * same Place as the one who is speaking.
     *
     * @param text String to be displayed.
     */
    public void say(String text, FullscreenActivity target) {
        world.sayAtPlace(place, name + " says: " + text, target);
    }

    /**
     * Grab a Thing from this Place and place it in the inventory.
     *
     * @param thingName Name referring to a Thing to be grabbed.
     * @see #drop
     */
    public synchronized void grab(String thingName, FullscreenActivity target) {
        Thing item = place.removeThing(thingName);
        if (item != null) {
            things.add(item);
            world.sayAtPlace(place, name + " is taking " + thingName, target);
            world.update(place, target);
        }
    }

    /**
     * Drop a Thing the ground by deleting it from the inventory.
     *
     * @param thingName Name referring to a Thing to be dropped.
     * @see #grab
     */
    public synchronized void drop(String thingName, FullscreenActivity target) {
        Thing item = findThing(thingName);

        if (item != null) {
            things.remove(item);
            place.addThing(item);
            world.sayAtPlace(place, name + " is dropping " + thingName, target);
            world.update(place, target);
        }
    }

    /**
     * Find a Thing in our inventory.
     *
     * @param thingName The name of a thing to find.
     */
    public Thing findThing(String thingName) {
        synchronized (things) {
            Iterator<Thing> iterate = things.iterator();
            while (iterate.hasNext()) {
                Thing t = (Thing) iterate.next();
                if (t.name.equals(thingName))
                    return t;
            }
        }
        return null;
    }

    /**
     * Respond to a query from another person.
     *
     * @param questioner The Person querying.
     */
    public void query(Person questioner) {
        // Default case - don't say anything
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public World getWorld() {
        return world;
    }

    public void setWorld(World world) {
        this.world = world;
    }

    public Collection<Thing> getThings() {
        return things;
    }

    public void setThings(Collection<Thing> things) {
        this.things = things;
    }

    public int getImage() {
        return image;
    }

    public void setImage(int image) {
        this.image = image;
    }
}

Class World

package dev.game.adventure;

import java.util.*;

/**
 * The data structure that describes the world.
 */
public abstract class World { // "implements" Playable
    public Adventure owner;

    public AdventureGame ag;

    public AdventureGame getAg() {
        return ag;
    }

    /**
     * Players watching this World
     */
    public Collection<Player> players;

    public void setAg(AdventureGame ag) {
        this.ag = ag;
    }

    /**
     * Places in this World
     */
    public Map<String, Place> places;

    public Map<String, Place> getPlaces() {
        return places;
    }

    public void setPlaces(Map<String, Place> places) {
        this.places = places;
    }

    public Collection<Player> getPlayers() {
        return players;
    }

    public void setPlayers(Collection<Player> players) {
        this.players = players;
    }

    /**
     * Create a World. `a' is a reference to the Adventure itself. This reference is
     * used when loading images and sounds.
     *
     * @param a An instance of adventure.
     */
    public World(Adventure a) {
        places = new HashMap<String, Place>();
        players = new LinkedList<Player>();
        owner = a;
    }

    // Every World must implement this method which returns at what
    // Place in the World new Persons are created.
    abstract Place defaultPlace();

    /**
     * Create a Place with name `name' and picture file name "`name'.gif" and
     * add it to this World.
     *
     * @param name A name for the new place and the filename for the gif-image.
     */
    public void createPlace(String name, FullscreenActivity target, int pic) {
        createPlace(name, pic, target);
    }

    /**
     * Create a Place with name `name' and picture file name `picture' and add
     * it to this World.
     *
     * @param name    A name for the new place.
     * @param picture The filename of the image associated with the place.
     */
    public void createPlace(String name, int picture, FullscreenActivity target) {
        places.put(name, new Place(name, owner.loadPicture(picture, target)));
    }

    /**
     * Returns the Place in this world which has the name `name'.
     *
     * @param name Name of the place to be returned.
     * @return The place in name.
     * @throws NoSuchElementException if matching place does not exist.
     */
    public Place getPlace(String name) {
        Place p = (Place) places.get(name);
        if (p == null)
            throw new NoSuchElementException(name);
        return p;
    }

    /**
     * State that the Player `p' is watching this world.
     *
     * @param p The player to add to the list of players watching this world.
     */
    public void addPlayer(Player p) {
        players.add(p);
    }

    /**
     * Update the control window of any Players who are currently watching
     * `place'.
     *
     * @param place to update.
     */
    public void update(Place place, FullscreenActivity target) {
        synchronized (players) {
            Iterator<Player> ls = players.iterator();
            while (ls.hasNext()) {
                Player p = ls.next();
                if (p.currentPlace() == place)
                    p.update();
            }
        }
        place.draw(target, ag);
    }

    /**
     * Say `text' in the Place `place'. The text will become visible at the
     * bottom of the text window of any Players currently watching `place'.
     *
     * @param place The place where the string will be displayed.
     * @param text  The string to be displayed.
     */
    public void sayAtPlace(Place place, String text, FullscreenActivity target) {
        synchronized (players) {
            Iterator<Player> ls = players.iterator();
            while (ls.hasNext()) {
                Player p = ls.next();
                if (p.currentPlace() == place) {
                    p.say(text, target); //FIXME does this always work???
                }
            }
        }
    }

    /**
     * Play `sound' in the Place `place'. The sound will be played by any
     * Players currently watching `place'. Sound can for example be:
     * "songs/what_a_wonderful_world" -- suitable when your key is accepted
     * "effects/gong" -- suitable when Troll kills
     *
     * @param place Place at which the sound will be played.
     * @param sound Filename of the sound.
     */
    public void playAtPlace(Place place, String sound) {
        synchronized (players) {
            Iterator<Player> ls = players.iterator();
            while (ls.hasNext()) {
                Player p = ls.next();
                if (p.currentPlace() == place) {
                    //owner.playSound(sound);
                }
            }
        }
    }
}

The rest of the code, please check the repo.

The strange thing which debugging shows is that the following code is reached and prints the texts to the game:

if (person.getName().equals("You")) {

    String[] msgs = {
            "The horrible shrieks of the undead chill your bones.",
            "The maddening horrors of this place make your skin crawl.",
            "Death fills the very air of everywhere you turn.",
            "Frightening voices in the air whisper to you, imploring you to join their ranks.",
            "The pungent stength of lurking horrors fills your nostrils.",
            "Morbid visions glimpse before eyes.",
            "Howling screams of a distant Leviathan far below shiver your sanity and prophesize your doom.",
            "Ancient abominations and unspeakable horrors stir in their sleep as you sneak them by."};

    String rand = msgs[(int) (Math.random() * msgs.length)];
    person.getWorld().sayAtPlace(person.place, rand, target);
    // don't write out exit info if there is none
    if (!this.name.equals("Heaven")) {
        person.getWorld().sayAtPlace(person.place,
                "There are doors " + s, target);
    }
}

While the other codes in the same method don't print to the screen. Why???


Solution

  • Debugging can be an extremely useful tool when it comes to determining why things don't work. By simply printing to console you can isolate where the program/code stops functioning as you expect it to.

    With your code, you are observing that you are not consistently printing a game message when you expect it to. The first place to check would be whether you enter this if statement when you expect to. You mentioned that during debugging you isolate that it does. Hence the problem must be in the sayAtPlace() method.

    if (this.getName().equals("Skeleton")) {
        world.sayAtPlace(place,
            "A terrifying skeleton warrior appears!", target);
    }
    

    The sayAtPlace method, as shown, expects that there be a player at the provided place. If it finds no player, then there will be no output. While debugging you said that there are no players, hence you would expect no output to be shown. If there are no players, you will be unable to find a match withing the Player collection, which would be required to invoke the say method to display the message.

        public void sayAtPlace(Place place, String text, FullscreenActivity target) {
            synchronized (players) {
                Iterator<Player> ls = players.iterator();
                while (ls.hasNext()) {
                    Player p = ls.next();
                    if (p.currentPlace() == place) {
                        p.say(text, target); //FIXME does this always work???
                    }
                }
            }
        }