commit c16628e18d4cea717cc738175259f6e7d95f455b Author: aNNiMON Date: Tue Feb 13 22:08:19 2024 +0200 Initial commit diff --git a/src/com/malcolmsoft/wumpus/ColorConstants.java b/src/com/malcolmsoft/wumpus/ColorConstants.java new file mode 100644 index 0000000..f7f5704 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/ColorConstants.java @@ -0,0 +1,36 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +/** + * + * @author Malcolm + */ +public interface ColorConstants { + int TEXT_COLOR = 0xF9B16B; + int HIGHLIGHTED_TEXT_COLOUR = 0xF9D2AD; + int BACKGROUNG_COLOR = 0x774323; + int HIGHLIGHTED_BORDER_COLOR = 0xB1343A; + int SELECTED_BACKGROUND_COLOR = 0x98562D; + int DARKER_BORDER = 0xA14F12; + int LIGHTER_BORDER = 0xD06D27; + int SLIDER_BACKGROUND = 0x964F20; + int SLIDER_COLOR = 0xD9722E; +} diff --git a/src/com/malcolmsoft/wumpus/GameCanvas.java b/src/com/malcolmsoft/wumpus/GameCanvas.java new file mode 100644 index 0000000..0a45199 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/GameCanvas.java @@ -0,0 +1,644 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +import com.malcolmsoft.wumpus.maps.InvalidRoomIdException; +import com.malcolmsoft.wumpus.model.CaveModel; +import com.malcolmsoft.wumpus.model.Event; +import com.malcolmsoft.wumpus.model.EventDefeat; +import com.malcolmsoft.wumpus.model.EventEnd; +import com.malcolmsoft.wumpus.model.EventFeel; +import com.malcolmsoft.wumpus.model.EventLocationChange; +import com.malcolmsoft.wumpus.model.EventVictory; +import com.malcolmsoft.wumpus.model.EventWumpusMoved; +import com.malcolmsoft.wumpus.model.PlayerAction; +import com.malcolmsoft.wumpus.model.PlayerActionMove; +import com.malcolmsoft.wumpus.model.PlayerActionShoot; +import java.util.Vector; +import javax.microedition.lcdui.Canvas; +import javax.microedition.lcdui.Font; +import javax.microedition.lcdui.Graphics; + +/** + * + * @author Malcolm + */ +public class GameCanvas extends Canvas implements GameStrings, ColorConstants { + private final int width; + private final int height; + private final int delimiterPos; + private final int statusLinePos; + private final int sliderStripPos; + private final int sliderStripHeight; + + private int sliderBottomPos; + private int sliderHeight; + + private static final int SLIDER_STRIP_WIDTH = 5; + private static final int TEXT_MARGIN = 5; + + private boolean consoleSelected = false; + private int highlightedItem = ITEM_PANEL; +// private static final int ITEM_NONE = 0; + private static final int ITEM_CONSOLE = 1; + private static final int ITEM_PANEL = 2; + + private Font smallFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL); + private Font mediumFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); + private int smallFontHeight = smallFont.getHeight(); + private int mediumFontHeight = mediumFont.getHeight(); + + private Vector messages = new Vector(); + private final int messagesPerScreen; + private int messagesSkipped; //If we scrolled beyond them + + //Status line + private int currRoomId; + private int[] connectedRoomsIds; + + private String status; + + //Model references + private CaveModel caveModel; + private String currPlayerId; + + //Controller (panel) + private volatile boolean blockInput = false; + + private int inputState = STATE_ACTION_TYPE; + + private static final int STATE_ACTION_TYPE = 0; + private static final int STATE_MOVE_NEXT_ROOM = 1; + private static final int STATE_SHOOT_CHARGES_USED = 2; + private static final int STATE_SHOOT_NEXT_ROOM = 3; + private static final int STATE_START_NEW_GAME = 4; + + private String question = QUESTION_SELECT_ACTION; + + private String[] options = OPTIONS_SELECT_ACTION; + private int selectedOption; + + private static final String[] OPTIONS_SELECT_ACTION = {OPTION_GO, OPTION_SHOOT}; + private static final String[] OPTIONS_START_NEW_GAME = {OPTION_PLAY_AGAIN, OPTION_QUIT}; + + private NumberField currNumberField = null; //Is shown in shooting + private NumberField chargesField = new NumberField(1); + private NumberField roomField = new NumberField(2); + private int numberFieldLeftPos; + + private int[] roomsToShoot; + private int shotRoomPos; + + private HuntTheWumpus gameStateListener; + + public GameCanvas(CaveModel caveModel, String playerId, HuntTheWumpus gameStateListener) { + this.gameStateListener = gameStateListener; + + setFullScreenMode(true); + + width = getWidth(); + height = getHeight(); + statusLinePos = height - smallFontHeight; + delimiterPos = statusLinePos - 3 - smallFontHeight * 3 - mediumFontHeight; + sliderStripPos = width - 3 - SLIDER_STRIP_WIDTH; + sliderStripHeight = delimiterPos - 6; + messagesPerScreen = (delimiterPos - 5) / smallFontHeight; + + numberFieldLeftPos = (width - roomField.getWidth()) / 2; + + startNewGame(caveModel, playerId); + } + + public void startNewGame(CaveModel caveModel, String playerId) { + if (caveModel == null || playerId == null) + throw new NullPointerException("Action listener or current player can't be null"); + + this.caveModel = caveModel; + this.currPlayerId = playerId; + + init(); + } + + private void init() { + consoleSelected = false; + highlightedItem = ITEM_PANEL; + + messages.removeAllElements(); + messagesSkipped = 0; + + blockInput = false; + inputState = STATE_ACTION_TYPE; + question = QUESTION_SELECT_ACTION; + options = OPTIONS_SELECT_ACTION; + selectedOption = 0; + + currNumberField = null; + chargesField.clear(); + roomField.clear(); + } + + protected void paint(Graphics g) { + g.setColor(BACKGROUNG_COLOR); + g.fillRect(0, 0, getWidth(), getHeight()); + + if (consoleSelected) { + g.setColor(SELECTED_BACKGROUND_COLOR); + g.fillRect(2, 2, width - 5, delimiterPos - 3); + } + + g.setColor(DARKER_BORDER); + g.drawRect(0, 0, width - 1, height - 1); + g.drawRect(2, 2, width - 5, delimiterPos - 3); + g.drawRect(2, delimiterPos + 1, width - 5, statusLinePos - delimiterPos - 2); + g.drawRect(2, statusLinePos + 1, width - 5, height - statusLinePos - 4); + + g.setColor(LIGHTER_BORDER); + g.drawRect(1, 1, width - 3, height - 3); + g.drawLine(2, statusLinePos, width - 2, statusLinePos); + g.drawLine(2, delimiterPos, width - 2, delimiterPos); + + g.setColor(HIGHLIGHTED_BORDER_COLOR); + switch (highlightedItem) { + case ITEM_CONSOLE: + paintThickRectangle(g, 2, 2, width - 5, delimiterPos - 3); + break; + case ITEM_PANEL: + paintThickRectangle(g, 2, delimiterPos + 1, width - 5, statusLinePos - delimiterPos - 2); + break; + } + + //Messages + g.setColor(TEXT_COLOR); + g.setFont(smallFont); + int messagesToShow = Math.min(messages.size() - messagesSkipped, messagesPerScreen); + int uppermostMessagePos = messagesSkipped + messagesToShow; + for (int lineBottomPos = delimiterPos, messagePos = messagesSkipped; messagePos < uppermostMessagePos; + lineBottomPos -= smallFontHeight, messagePos++) { + String message = (String) messages.elementAt(messagePos); + g.drawString(message, TEXT_MARGIN, lineBottomPos, Graphics.LEFT | Graphics.BOTTOM); + } + + if (isConsoleScrollable()) { + g.setColor(SLIDER_BACKGROUND); + g.fillRect(sliderStripPos, 4, SLIDER_STRIP_WIDTH, sliderStripHeight); + g.setColor(SLIDER_COLOR); + g.fillRect(sliderStripPos, sliderBottomPos - sliderHeight, SLIDER_STRIP_WIDTH, sliderHeight); + } + + //Panel + int linePos = delimiterPos + 1; + + g.setColor(TEXT_COLOR); + g.setFont(mediumFont); + if (question != null) { + g.drawString(question, TEXT_MARGIN, linePos, 0); + linePos += mediumFontHeight; + } + + if (currNumberField != null) { + currNumberField.paint(g, numberFieldLeftPos, linePos); + } else { + g.setFont(smallFont); + for (int optionPos = 0; optionPos < options.length; optionPos++, linePos += smallFontHeight) { + g.setColor(selectedOption == optionPos ? HIGHLIGHTED_TEXT_COLOUR : TEXT_COLOR); + g.drawString(options[optionPos], TEXT_MARGIN, linePos, 0); + } + } + + //Status + g.setFont(smallFont); + g.setColor(TEXT_COLOR); + g.drawString(status, TEXT_MARGIN, statusLinePos, 0); + } + + private void paintThickRectangle(Graphics g, int x, int y, int width, int height) { + g.drawRect(x, y, width, height); + g.drawRect(x + 1, y + 1, width - 2, height - 2); +// g.drawRect(x + 2, y + 2, width - 4, height - 4); + } + + private boolean isConsoleScrollable() { + return messages.size() > messagesPerScreen; + } + + private void updateSliderParameters() { + if (!isConsoleScrollable()) return; + + int messagesTotal = messages.size(); + sliderHeight = (int) ((double) sliderStripHeight * messagesPerScreen / messagesTotal + 0.5D); + sliderBottomPos = delimiterPos - 2 - (int) ((double) sliderStripHeight * messagesSkipped / + messagesTotal + 0.5D); + } + + private void resetConsoleSliderPosition() { + if (messagesSkipped != 0) { + messagesSkipped = 0; + } + + updateSliderParameters(); + } + + protected void keyPressed(int keyCode) { + if (keyCode == -6 || keyCode == -7) { //Soft keys + gameStateListener.quitCurrentGame(); + return; + } + + if (highlightedItem == ITEM_CONSOLE) { //Scroll if selected or move selector + int action = getGameAction(keyCode); + switch (action) { + case FIRE: + consoleSelected = !consoleSelected; + repaint(); + break; + case DOWN: + if (consoleSelected) { + if (messagesSkipped > 0) { + messagesSkipped--; //Decrement by one if not 0 yet + updateSliderParameters(); + repaint(); + } + } else { + highlightedItem = ITEM_PANEL; //Move selector to panel + repaint(); + } + break; + case UP: + if (consoleSelected && messages.size() - messagesSkipped > messagesPerScreen) { + messagesSkipped++; + updateSliderParameters(); + repaint(); + } + break; + } + } else if (highlightedItem == ITEM_PANEL) { + if (blockInput) return; //Don't accept input while events from the last action are being processed + + if (currNumberField != null) { + switch (keyCode) { + case KEY_NUM0: + case KEY_NUM1: + case KEY_NUM2: + case KEY_NUM3: + case KEY_NUM4: + case KEY_NUM5: + case KEY_NUM6: + case KEY_NUM7: + case KEY_NUM8: + case KEY_NUM9: + currNumberField.numberEntered(keyCode - KEY_NUM0); + repaint(); + break; + + case KEY_POUND: + case -8: //Clear key + currNumberField.deleteLastChar(); + repaint(); + break; + case -5: //Fire + int value = currNumberField.getValue(); + + switch (inputState) { + case STATE_SHOOT_CHARGES_USED: + roomsToShoot = null; + roomsToShoot = new int[Math.min(caveModel.getPlayerCharges(currPlayerId), + value)]; + shotRoomPos = 0; + inputState = STATE_SHOOT_NEXT_ROOM; + updatePanelAndRepaint(); + break; + case STATE_SHOOT_NEXT_ROOM: + roomsToShoot[shotRoomPos++] = value; + if (shotRoomPos == roomsToShoot.length) { + inputState = STATE_ACTION_TYPE; + updatePanelAndRepaint(); + + dispatchPlayerAction(new PlayerActionShoot(currPlayerId, roomsToShoot)); + } else { + updatePanelAndRepaint(); + } + break; + default: + throw new IllegalStateException("Number field shown in illegal state" + + inputState); + } + } + } else { + int action = getGameAction(keyCode); + + switch (action) { + case FIRE: + switch (inputState) { + case STATE_ACTION_TYPE: + switch (selectedOption) { + case 0: //Move + inputState = STATE_MOVE_NEXT_ROOM; + updatePanelAndRepaint(); + break; + case 1: //Shoot + inputState = STATE_SHOOT_CHARGES_USED; + updatePanelAndRepaint(); + break; + } + break; + case STATE_MOVE_NEXT_ROOM: + inputState = STATE_ACTION_TYPE; + updatePanelAndRepaint(); + dispatchPlayerAction(new PlayerActionMove(currPlayerId, + connectedRoomsIds[selectedOption])); + break; + case STATE_START_NEW_GAME: + switch (selectedOption) { + case 0: //Play again + gameStateListener.restart(); + break; + case 1: //Quit + gameStateListener.quitCurrentGame(); + break; + } + break; + } + break; + case DOWN: + if (selectedOption < options.length - 1) { + selectedOption++; + repaint(); + } + break; + case UP: + if (selectedOption > 0) { + selectedOption--; + } else { + highlightedItem = ITEM_CONSOLE; + } + repaint(); + break; + } + } + } else throw new IllegalStateException("Unknown highlighted item: " + highlightedItem); + } + + private void updatePanelAndRepaint() { + switch (inputState) { + case STATE_ACTION_TYPE: + options = OPTIONS_SELECT_ACTION; + selectedOption = 0; + question = QUESTION_SELECT_ACTION; + currNumberField = null; + break; + case STATE_MOVE_NEXT_ROOM: + //Generate options + options = null; + options = new String[connectedRoomsIds.length]; + for (int passage = 0; passage < connectedRoomsIds.length; passage++) { + int nextId = connectedRoomsIds[passage]; + options[passage] = OPTION_GO_TO_ROOM + nextId; + } + + selectedOption = 0; + question = QUESTION_WHERE_TO; + currNumberField = null; + break; + case STATE_SHOOT_CHARGES_USED: + question = QUESTION_HOW_MANY_CHARGES; + currNumberField = chargesField; + break; + case STATE_SHOOT_NEXT_ROOM: + updateRoomShootingQuestion(); + currNumberField = roomField; + roomField.clear(); + break; + case STATE_START_NEW_GAME: + question = QUESTION_START_NEW_GAME; + options = OPTIONS_START_NEW_GAME; + selectedOption = 0; + currNumberField = null; + break; + } + + System.out.println("Current state: " + inputState); + repaint(); + } + + private void updateStatus() { + StringBuffer buf = new StringBuffer(); + buf.append(STATUS_ROOM); + buf.append(currRoomId); + buf.append(STATUS_PASSAGES); + + for (int passage = 0; passage < connectedRoomsIds.length; passage++) { + buf.append(connectedRoomsIds[passage]); + buf.append(", "); + } + + buf.append(caveModel.getPlayerCharges(currPlayerId)); + buf.append(STATUS_CHARGES); + + status = buf.toString(); + } + + private void updateRoomShootingQuestion() { + question = QUESTION_CHARGE_START + (shotRoomPos + 1) + QUESTION_CHARGE_ENDING; + } + + private void dispatchPlayerAction(final PlayerAction action) { + blockInput = true; + new Thread() { //Dispatch in a different thread so that UI is not blocked + public void run() { + try { + caveModel.nextGameCycle(action); + } catch (InvalidRoomIdException ex) { //Whoops, wrong room + ex.printStackTrace(); + inputState = STATE_ACTION_TYPE; //Init input + } finally { + blockInput = false; + } + } + }.start(); + } + + public void viewGameEvent(Event event) { + System.out.println(event); + + switch (event.getEventType()) { + case Event.EVENT_PLAYER_MOVED: + EventLocationChange locationEvent = (EventLocationChange) event; + currRoomId = locationEvent.getNewRoomId(); + connectedRoomsIds = locationEvent.getConnectedRoomsIds(); + updateStatus(); + + String movingMessage = null; + switch (locationEvent.getCause()) { + case EventLocationChange.CAUSE_SPAWN: + movingMessage = MOVED_SPAWNED_IN_NEW_ROOM; + break; + case EventLocationChange.CAUSE_PLAYER: + movingMessage = MOVED_WALKED_TO_NEW_ROOM; + break; + case EventLocationChange.CAUSE_BATS: + addMessage(MOVED_BATS_SNATCHED); //Additional message + movingMessage = MOVED_DRAGGED_TO_NEW_ROOM; + break; + } + addMessage(movingMessage + currRoomId); + + StringBuffer buf = new StringBuffer(); + buf.append(MOVED_PASSAGES_LEAD_TO); + for (int passage = 0; passage < connectedRoomsIds.length - 1; passage++) { + buf.append(connectedRoomsIds[passage]); + buf.append(", "); + } + buf.append(connectedRoomsIds[connectedRoomsIds.length - 1]); + + addMessage(buf.toString()); + break; + case Event.EVENT_PLAYER_SHOT: + updateStatus(); + addMessage(SHOT_MISSED); + break; + case Event.EVENT_PLAYER_FEELS: + EventFeel feelEvent = (EventFeel) event; + if (feelEvent.isPitNearby()) addMessage(FEEL_DRAFT); + if (feelEvent.areBatsNearby()) addMessage(FEEL_BATS); + if (feelEvent.isWumpusNearby()) addMessage(FEEL_WUMPUS); + break; + case Event.EVENT_WUMPUS_ESCAPED: + EventWumpusMoved wumpusEvent = (EventWumpusMoved) event; + if (wumpusEvent.wasWumpusEncountered()) { + addMessage(WUMPUS_BUMPED_INTO); + } + + addMessage(WUMPUS_ESCAPED); + break; + case Event.EVENT_VICTORY: + EventVictory victoryEvent = (EventVictory) event; + + addMessage(END_WUMPUS_SHOT); + addMessage(VICTORY_YOU_HAVE_WON); + addWumpusLocationMessage(victoryEvent); + addMessage(VICTORY_WUMPUS_WILL_GET_TO_YOU); + + inputState = STATE_START_NEW_GAME; + updatePanelAndRepaint(); + return; + case Event.EVENT_DEFEAT: + EventDefeat lastEvent = (EventDefeat) event; + + if (lastEvent.isWumpusShot()) { + addMessage(END_WUMPUS_SHOT); + } + + String message = null; + switch (lastEvent.getCause()) { + case EventDefeat.CAUSE_CHARGES_DEPLETED: + message = DEFEAT_CHARGES_DEPLETED; + break; + case EventDefeat.CAUSE_FELL_INTO_PIT: + message = DEFEAT_FELL_INTO_PIT; + break; + case EventDefeat.CAUSE_PLAYER_SHOT_HIMSELF: + message = DEFEAT_PLAYER_SHOT_HIMSELF; + break; + case EventDefeat.CAUSE_WUMPUS_MOVED_INTO_CAVE: + message = DEFEAT_WUMPUS_MOVED_INTO_CAVE; + break; + case EventDefeat.CAUSE_WUMPUS_WAS_IN_CAVE: + message = DEFEAT_WUMPUS_WAS_IN_THE_CAVE; + break; + } + addMessage(message); + + if (lastEvent.getCause() != EventDefeat.CAUSE_WUMPUS_MOVED_INTO_CAVE) { + addWumpusLocationMessage(lastEvent); + } + + inputState = STATE_START_NEW_GAME; + updatePanelAndRepaint(); + return; + } + + repaint(); + } + + private void addMessage(String message) { + messages.insertElementAt(message, 0); + resetConsoleSliderPosition(); + } + + private void addWumpusLocationMessage(EventEnd lastEvent) { + int wumpusRoomId = lastEvent.getWumpusRoomId(); + if (wumpusRoomId > 0) { //It is known + addMessage(END_WUMPUS_LOCATION + wumpusRoomId); + } + } + + private class NumberField { + private Font font = mediumFont; + private int width; + private int height = font.getHeight(); + + private StringBuffer text = new StringBuffer(); + private final int maxNumbers; + + public NumberField(int maxNumbers) { + this.width = font.charWidth('8') * maxNumbers + 2; + this.maxNumbers = maxNumbers; + } + + public void paint(Graphics g, int x, int y) { + g.setColor(LIGHTER_BORDER); + g.drawRect(x, y, width, height); + + g.setColor(TEXT_COLOR); + g.drawString(text.toString(), x + width, y, Graphics.TOP | Graphics.RIGHT); + } + + public void numberEntered(int ch) { + if (ch > 9 || ch < 0) throw new IllegalArgumentException("Not a number: " + ch); //Bad number + + if ((ch == 0 && text.length() == 0) || //First number shouldn't be zero + text.length() == maxNumbers) //And no more than maxNumbers numbers can be entered + return; + + text.append(ch); + } + + public int getValue() { //If nothing is entered, let value be zero + return text.length() > 0 ? Integer.parseInt(text.toString()) : 0; + } + + public void deleteLastChar() { + if (text.length() == 0) return; + text.deleteCharAt(text.length() - 1); + } + + public void clear() { + text.delete(0, text.length()); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/GameStrings.java b/src/com/malcolmsoft/wumpus/GameStrings.java new file mode 100644 index 0000000..1c6a5e8 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/GameStrings.java @@ -0,0 +1,78 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +/** + * + * @author Malcolm + */ +public interface GameStrings { + String MENU_HUNT_THE_WUMPUS = "Охота на Вампуса"; + String MENU_RESUME = "Возобновить"; + String MENU_NEW_GAME = "Новая игра"; + String MENU_QUIT = "Выход"; + String MENU_MAP_SELECTION_TITLE = "Выбор карты"; + + String ALERT_TITLE = "Ошибка!"; + + String QUESTION_CHARGE_ENDING = ", куда стрелять?"; + String QUESTION_CHARGE_START = "Заряд "; + String QUESTION_HOW_MANY_CHARGES = "Сколько зарядов?"; + String QUESTION_SELECT_ACTION = "Следующее действие:"; + String QUESTION_START_NEW_GAME = "Сыграть еще?"; + String QUESTION_WHERE_TO = "В какую комнату?"; + + String OPTION_GO = "Идти"; + String OPTION_SHOOT = "Стрелять"; + String OPTION_GO_TO_ROOM = "Идти в комнату "; + String OPTION_PLAY_AGAIN = "Играть снова!"; + String OPTION_QUIT = "Выйти"; + + String STATUS_CHARGES = " зар."; + String STATUS_PASSAGES = ", соед. с "; + String STATUS_ROOM = "К. "; + + String MOVED_SPAWNED_IN_NEW_ROOM = "Ты в комнате "; + String MOVED_WALKED_TO_NEW_ROOM = "Ты пришел в комнату "; + String MOVED_BATS_SNATCHED = "Тебя схватили летучие мыши!"; + String MOVED_DRAGGED_TO_NEW_ROOM = "Тебя притащили в комнату "; + String MOVED_PASSAGES_LEAD_TO = "Проходы ведут в "; + + String SHOT_MISSED = "Ты промахнулся"; + + String FEEL_DRAFT = "Ты чувствуешь сквозняк"; + String FEEL_BATS = "Слышно хлопанье крыльев"; + String FEEL_WUMPUS = "Ты чуешь, что вампус близко!"; + + String WUMPUS_BUMPED_INTO = "Ты наткнулся на вампуса!"; + String WUMPUS_ESCAPED = "Вампус сбежал из комнаты"; + + String END_WUMPUS_SHOT = "Ты подстрелил вампуса!"; + String END_WUMPUS_LOCATION = "Вампус был в комнате "; + + String DEFEAT_PLAYER_SHOT_HIMSELF = "Увы, ты подстрелил себя..."; + String DEFEAT_CHARGES_DEPLETED = "Ты истратил все заряды..."; + String DEFEAT_FELL_INTO_PIT = "Иииааа! Ты упал в яму..."; + String DEFEAT_WUMPUS_WAS_IN_THE_CAVE = "Вампус тебя съел..."; + String DEFEAT_WUMPUS_MOVED_INTO_CAVE = "Пришел вампус и съел тебя..."; + + String VICTORY_YOU_HAVE_WON = "Ты выиграл!"; + String VICTORY_WUMPUS_WILL_GET_TO_YOU = "Вампус еще доберется до тебя!"; +} diff --git a/src/com/malcolmsoft/wumpus/HuntTheWumpus.java b/src/com/malcolmsoft/wumpus/HuntTheWumpus.java new file mode 100644 index 0000000..1091a82 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/HuntTheWumpus.java @@ -0,0 +1,135 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +import com.malcolmsoft.wumpus.maps.Map; +import com.malcolmsoft.wumpus.maps.MapReader; +import com.malcolmsoft.wumpus.model.CaveModel; +import java.io.InputStream; +import javax.microedition.lcdui.Alert; +import javax.microedition.lcdui.Display; +import javax.microedition.midlet.MIDlet; + +/** + * @author Malcolm + */ +public class HuntTheWumpus extends MIDlet implements ItemSelectionListener, GameStrings { + private Display display = Display.getDisplay(this); + + private GameCanvas canvas; + private CaveModel model = new CaveModel(); + private Map currentMap; + + private MenuItem resume = new MenuItem(MENU_RESUME); + private MenuItem newGame = new MenuItem(MENU_NEW_GAME); + private MenuItem exit = new MenuItem(MENU_QUIT); + private MenuItem[] mainMenuItems = { + resume, + newGame, + exit + }; + private Menu mainMenu = new Menu(MENU_HUNT_THE_WUMPUS, mainMenuItems); + + private Menu mapSelectionMenu; + private static final String MAZE_MAPS_PATH = "/com/malcolmsoft/wumpus/maps/mazemaps.xml"; + + private Alert exceptionAlert = new Alert(ALERT_TITLE); + + public HuntTheWumpus() { + try { + InputStream is = getMapStream(); + String[] mapNames = MapReader.getInstance().readMapNames(is); + is.close(); + + MenuItem[] mapItems = new MenuItem[mapNames.length]; + for (int mapPos = 0; mapPos < mapItems.length; mapPos++) { + mapItems[mapPos] = new MenuItem(mapNames[mapPos]); + } + mapSelectionMenu = new Menu(MENU_MAP_SELECTION_TITLE, mapItems); + mapSelectionMenu.setItemSelectionListener(this); + + mainMenu.setItemEnabled(resume, false); + mainMenu.setSelectedItem(newGame); + mainMenu.setItemSelectionListener(this); + display.setCurrent(mainMenu); + } catch (Exception ex) { //Exceptions should never occur if app is built and started properly + viewException(ex); + } + } + + public void startApp() {} + + public void pauseApp() {} + + public void destroyApp(boolean unconditional) {} + + public void restart() { + canvas.startNewGame(model, model.getPlayerId()); + model.startNewGame(currentMap); + } + + public void quitCurrentGame() { + boolean gameActive = model.isGameActive(); + mainMenu.setItemEnabled(resume, gameActive); + mainMenu.setSelectedItem(gameActive ? resume : newGame); + display.setCurrent(mainMenu); + } + + public void itemSelected(Menu itemList, MenuItem item) { + if (itemList == mainMenu) { + if (item == resume) { + display.setCurrent(canvas); + } else if (item == newGame) { + display.setCurrent(mapSelectionMenu); + } else if (item == exit) { + notifyDestroyed(); + } + } else if (itemList == mapSelectionMenu) { + try { + InputStream is = getMapStream(); + //item.getText() is not very nice way to do this as it ties view and the model, but it is simple + currentMap = MapReader.getInstance().readMap(is, item.getText()); + is.close(); + + if (canvas == null) { + canvas = new GameCanvas(model, model.getPlayerId(), this);//Reference from controller to model + model.setEventListener(canvas); //Establish reference from model to view + } else { + canvas.startNewGame(model, model.getPlayerId()); + } + + model.startNewGame(currentMap); + display.setCurrent(canvas); + } catch (Exception ex) { + viewException(ex); + } + } + } + + private InputStream getMapStream() { + return getClass().getResourceAsStream(MAZE_MAPS_PATH); + } + + private void viewException(Exception ex) { + ex.printStackTrace(); + exceptionAlert.setString(ex.toString()); + display.setCurrent(exceptionAlert); + } +} diff --git a/src/com/malcolmsoft/wumpus/ItemSelectionListener.java b/src/com/malcolmsoft/wumpus/ItemSelectionListener.java new file mode 100644 index 0000000..2981803 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/ItemSelectionListener.java @@ -0,0 +1,28 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +/** + * + * @author Malcolm + */ +public interface ItemSelectionListener { + void itemSelected(Menu itemList, MenuItem item); +} diff --git a/src/com/malcolmsoft/wumpus/Menu.java b/src/com/malcolmsoft/wumpus/Menu.java new file mode 100644 index 0000000..6116075 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/Menu.java @@ -0,0 +1,167 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +import java.util.Vector; +import javax.microedition.lcdui.Canvas; +import javax.microedition.lcdui.Font; +import javax.microedition.lcdui.Graphics; + +/** + * + * @author Malcolm + */ +public class Menu extends Canvas implements ColorConstants { + protected final int width; + protected final int height; + protected final int horCenter; + + protected int firstItemPos; + + protected Font largeFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE); + protected Font mediumFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); + + private Font titleFont = largeFont; + private Font textFont = mediumFont; + private int textFontHeight = textFont.getHeight(); + + private String title; + private MenuItem[] items; + private Vector disabledItems = new Vector(); + + private int selectedItem = 0; + + private ItemSelectionListener listener; + + public Menu(String title, MenuItem[] items) { + this.title = title; + this.items = items; + + setFullScreenMode(true); + width = getWidth(); + height = getHeight(); + horCenter = width / 2; + + int textSizeTotal = textFontHeight * items.length; + firstItemPos = (height - largeFont.getHeight() - textSizeTotal) / 2; + } + + protected void paint(Graphics g) { + g.setColor(BACKGROUNG_COLOR); + g.fillRect(0, 0, width, height); + + g.setColor(TEXT_COLOR); + g.setFont(titleFont); + g.drawString(title, horCenter, 0, Graphics.HCENTER | Graphics.TOP); + + g.setFont(textFont); + for (int itemPos = 0, linePos = firstItemPos; itemPos < items.length; + itemPos++, linePos += textFontHeight) { + MenuItem item = items[itemPos]; + if (disabledItems.contains(item)) continue; + + g.setColor(itemPos == selectedItem ? HIGHLIGHTED_TEXT_COLOUR : TEXT_COLOR); + g.drawString(item.getText(), horCenter, linePos, Graphics.HCENTER | Graphics.TOP); + } + } + + protected void keyPressed(int keyCode) { + switch (getGameAction(keyCode)) { + case DOWN: + if (selectedItem == items.length - 1) break; + + //Find nearest enabled item + int newSelectedItem; + boolean nextElementFound = false; + for (newSelectedItem = selectedItem + 1; newSelectedItem < items.length; newSelectedItem++) { + if (!isItemDisabled(newSelectedItem)) { + nextElementFound = true; + break; + } + } + if (!nextElementFound) break; + + selectedItem = newSelectedItem; + repaint(); + break; + case UP: + if (selectedItem == 0) break; + + //Find nearest enabled item + nextElementFound = false; + for (newSelectedItem = selectedItem - 1; newSelectedItem >= 0; newSelectedItem--) { + if (!isItemDisabled(newSelectedItem)) { + nextElementFound = true; + break; + } + } + if (!nextElementFound) break; + + selectedItem--; + repaint(); + break; + case FIRE: + listener.itemSelected(this, items[selectedItem]); + break; + } + } + + private boolean isItemDisabled(int itemPos) { + return disabledItems.contains(items[itemPos]); + } + + public void setItemEnabled(MenuItem item, boolean enabled) { + if (enabled) { + int itemPos = disabledItems.indexOf(item); + if (itemPos == -1) { + return; //No such disabled items + } else { + disabledItems.removeElementAt(itemPos); + } + } else { //We are disabling the item + if (disabledItems.contains(item)) return; //It has already been disabled + + for (int itemPos = 0; itemPos < items.length; itemPos++) {//This is merely a check for item existence + if (items[itemPos] == item) { + disabledItems.addElement(item); + return; + } + } + throw new IllegalArgumentException("Item " + item + " wasn't found in the list"); + } + } + + public void setSelectedItem(MenuItem item) { + if (disabledItems.contains(item)) + throw new IllegalArgumentException("Item " + item + " was disabled"); + + for (int itemPos = 0; itemPos < items.length; itemPos++) { + if (items[itemPos] == item) { + selectedItem = itemPos; + return; + } + } + throw new IllegalArgumentException("Item " + item + " wasn't found in the list"); + } + + public void setItemSelectionListener(ItemSelectionListener listener) { + this.listener = listener; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/MenuItem.java b/src/com/malcolmsoft/wumpus/MenuItem.java new file mode 100644 index 0000000..fa650a6 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/MenuItem.java @@ -0,0 +1,40 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +/** + * + * @author Malcolm + */ +public class MenuItem { + private String text; + + public MenuItem(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public String toString() { + return text; + } +} diff --git a/src/com/malcolmsoft/wumpus/ReturnListener.java b/src/com/malcolmsoft/wumpus/ReturnListener.java new file mode 100644 index 0000000..d253525 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/ReturnListener.java @@ -0,0 +1,30 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus; + +import javax.microedition.lcdui.Displayable; + +/** + * + * @author Malcolm + */ +public interface ReturnListener { + public void returnFromDisplayable(Displayable d); +} diff --git a/src/com/malcolmsoft/wumpus/icon.png b/src/com/malcolmsoft/wumpus/icon.png new file mode 100644 index 0000000..fb186c0 Binary files /dev/null and b/src/com/malcolmsoft/wumpus/icon.png differ diff --git a/src/com/malcolmsoft/wumpus/maps/InvalidRoomIdException.java b/src/com/malcolmsoft/wumpus/maps/InvalidRoomIdException.java new file mode 100644 index 0000000..9403871 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/InvalidRoomIdException.java @@ -0,0 +1,42 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.maps; + +/** + * + * @author Malcolm + */ +public class InvalidRoomIdException extends IllegalArgumentException { + + /** + * Creates a new instance of InvalidRoomIdException without detail message. + */ + public InvalidRoomIdException() { + } + + + /** + * Constructs an instance of InvalidRoomIdException with the specified detail message. + * @param msg the detail message. + */ + public InvalidRoomIdException(String msg) { + super(msg); + } +} diff --git a/src/com/malcolmsoft/wumpus/maps/Map.java b/src/com/malcolmsoft/wumpus/maps/Map.java new file mode 100644 index 0000000..93a9a58 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/Map.java @@ -0,0 +1,101 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.maps; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +/** + * + * @author Malcolm + */ +public class Map { + private String name; + private Hashtable roomsNet = new Hashtable(); + + private int oneWayPassagesTotal; + private int twoWayPassagesTotal; + + public Map(String name, Vector rooms) { + this.name = name; + + Enumeration roomsEn = rooms.elements(); + while (roomsEn.hasMoreElements()) { + Room room = (Room) roomsEn.nextElement(); + roomsNet.put(new Integer(room.id), room); + } + + //Now link the rooms + roomsEn = null; + roomsEn = roomsNet.elements(); + while (roomsEn.hasMoreElements()) { + Room room = (Room) roomsEn.nextElement(); + Integer roomId = new Integer(room.id); + Enumeration roomIdsEn = room.connectedRoomsIds.elements(); + for (int passagePos = 0; roomIdsEn.hasMoreElements(); passagePos++) { + Room connectedRoom = (Room) roomsNet.get(roomIdsEn.nextElement()); + room.connectedRooms[passagePos] = connectedRoom; + + if (connectedRoom.connectedRoomsIds.contains(roomId)) { + twoWayPassagesTotal++; + } else { + oneWayPassagesTotal++; +// System.out.println(roomId + ", connected room: " + connectedRoom.id); + } + } + } + twoWayPassagesTotal /= 2; + + //And some check for ids + roomsEn = null; + roomsEn = roomsNet.elements(); + while (roomsEn.hasMoreElements()) { + Room room = (Room) roomsEn.nextElement(); + if (room.id <= 0 || room.id > roomsNet.size()) + throw new IllegalArgumentException("Invalid or inconsistent id: " + room.id); + } + } + + public String getName() { + return name; + } + + public Room getRoom(int id) { + Room room = (Room) roomsNet.get(new Integer(id)); + if (room == null) + throw new InvalidRoomIdException("Room " + id + " doesn't exist"); + return room; + } + + public int getRoomsCount() { + return roomsNet.size(); + } + + public String toString() { + return "Map \"" + name + "\" with " + roomsNet.size() + " rooms, " + + convertToString(twoWayPassagesTotal) + " two-way passages and " + + convertToString(oneWayPassagesTotal) + " one-way passages"; + } + + private String convertToString(int number) { + return number == 0 ? "no" : String.valueOf(number); + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/maps/MapReader.java b/src/com/malcolmsoft/wumpus/maps/MapReader.java new file mode 100644 index 0000000..d6460f7 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/MapReader.java @@ -0,0 +1,171 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.maps; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * + * @author Malcolm + */ +public class MapReader { + private SAXParser parser; + + private static MapReader instance; + + private MapReader() throws ParserConfigurationException, SAXException { + parser = SAXParserFactory.newInstance().newSAXParser(); + } + + public static MapReader getInstance() throws SAXException, ParserConfigurationException { + if (instance == null) + instance = new MapReader(); + return instance; + } + + public String[] readMapNames(InputStream is) throws IOException { + NamesHandler handler = new NamesHandler(); + try { + parser.parse(is, handler); + } catch (SAXException ex) { + ex.printStackTrace(); + } + return handler.getMapNames(); + } + + public Map readMap(InputStream is, String mapName) throws IOException { + MapHandler handler = new MapHandler(mapName); + try { + parser.parse(is, handler); + } catch (SAXException ex) { + ex.printStackTrace(); + } + Map map = handler.getMap(); + if (map == null) throw new IllegalArgumentException("Map with name \"" + mapName + "\" wasn't found"); + return map; + } + + private class NamesHandler extends DefaultHandler { + private Vector mapNames; + + public NamesHandler() { + mapNames = new Vector(); + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if ("maze".equals(qName)) { + mapNames.addElement(attributes.getValue("name")); + } + } + + public String[] getMapNames() { + String[] output = new String[mapNames.size()]; + mapNames.copyInto(output); + return output; + } + } + + private class MapHandler extends DefaultHandler { + private String mapName; + private Map map; + + private Vector rooms; + private int currRoomId; + private Vector connectedRoomsIds; + + private int state; + + private static final int STATE_SKIPPING = 0; + private static final int STATE_READING_MAP = 1; + private static final int STATE_READING_ROOM = 2; + private static final int STATE_READING_PASSAGE = 3; + + public MapHandler(String mapName) { + this.mapName = mapName; + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + switch (state) { + case STATE_SKIPPING: + if (map == null && "maze".equals(qName) && attributes.getValue("name").equals(mapName)) { + state = STATE_READING_MAP; + rooms = new Vector(); + } + break; + case STATE_READING_MAP: + if ("room".equals(qName)) { + currRoomId = Integer.parseInt(attributes.getValue("id")); + connectedRoomsIds = new Vector(); + state = STATE_READING_ROOM; + } + break; + case STATE_READING_ROOM: + if ("passage".equals(qName)) { + state = STATE_READING_PASSAGE; + } + break; + } + } + + public void characters(char[] ch, int start, int length) throws SAXException { + if (state == STATE_READING_PASSAGE) { + connectedRoomsIds.addElement(Integer.valueOf(new String(ch, start, length))); + } + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + switch (state) { + case STATE_READING_MAP: + if ("maze".equals(qName)) { + map = new Map(mapName, rooms); + rooms = null; + state = STATE_SKIPPING; + } + break; + case STATE_READING_ROOM: + if ("room".equals(qName)) { + rooms.addElement(new Room(currRoomId, connectedRoomsIds)); + connectedRoomsIds = null; + state = STATE_READING_MAP; + } + break; + case STATE_READING_PASSAGE: + if ("passage".equals(qName)) { //Should always be true + state = STATE_READING_ROOM; + } + break; + } + } + + private Map getMap() { + return map; + } + } +} diff --git a/src/com/malcolmsoft/wumpus/maps/Room.java b/src/com/malcolmsoft/wumpus/maps/Room.java new file mode 100644 index 0000000..d944bb4 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/Room.java @@ -0,0 +1,124 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.maps; + +import com.malcolmsoft.wumpus.model.GameObject; +import java.util.Enumeration; +import java.util.Vector; + +/** + * + * @author Malcolm + */ +public class Room { + int id; + Room[] connectedRooms = new Room[MAX_PASSAGES_NUM]; //Null passages must be at the end of this array + Vector connectedRoomsIds; + + Vector objects = new Vector(); + + public static final int MAX_PASSAGES_NUM = 3; + + public Room(int id, Vector connectedRoomsIds) { + this.id = id; + this.connectedRoomsIds = connectedRoomsIds; + } + + public int getId() { + return id; + } + + public boolean connectsTo(Room otherRoom) { + for (int roomPos = 0; roomPos < connectedRooms.length; roomPos++) { + if (connectedRooms[roomPos] == otherRoom) return true; + } + return false; + } + + public int getPassagesCount() { + int passagesCount = connectedRooms.length; + for (int roomPos = connectedRooms.length - 1; roomPos >= 0; roomPos--) { + if (connectedRooms[roomPos] != null) break; + passagesCount--; + } + return passagesCount; + } + + public Room getNextRoom(int passage) { + return connectedRooms[passage]; + } + + public int[] getConnectedRoomsIds() { + int[] ids = new int[connectedRoomsIds.size()]; + for (int passage = 0; passage < ids.length; passage++) { + ids[passage] = ((Integer) connectedRoomsIds.elementAt(passage)).intValue(); + } + return ids; + } + + public void addObject(GameObject object) { + objects.addElement(object); + } + + public boolean isEmpty() { + return objects.isEmpty(); + } + + public boolean hasObject(GameObject object) { + return objects.contains(object); + } + + public boolean hasObjectType(Class clazz) { + Enumeration en = objects.elements(); + while (en.hasMoreElements()) { + if (clazz.isInstance(en.nextElement())) + return true; + } + return false; + } + + public Enumeration contents() { + return objects.elements(); + } + + public void removeObject(GameObject object) { + objects.removeElement(object); + } + + public void removeAllObjects() { + objects.removeAllElements(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Room "); + buf.append(id); + buf.append(", connected to "); + for (int passage = 0; passage < connectedRooms.length; passage++) { + Room room = connectedRooms[passage]; + if (room != null) { + buf.append(room.id); + buf.append(", "); + } else break; + } + buf.delete(buf.length() - 2, buf.length()); //Remove last comma and space + return buf.toString(); + } +} diff --git a/src/com/malcolmsoft/wumpus/maps/mazemaps.xml b/src/com/malcolmsoft/wumpus/maps/mazemaps.xml new file mode 100644 index 0000000..c391474 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/mazemaps.xml @@ -0,0 +1,643 @@ +п»ї + + + + + + + 2 + 5 + 8 + + + 1 + 3 + 10 + + + 2 + 4 + 12 + + + 3 + 5 + 14 + + + 1 + 4 + 6 + + + 5 + 7 + 15 + + + 6 + 8 + 17 + + + 1 + 7 + 9 + + + 8 + 10 + 18 + + + 2 + 9 + 11 + + + 10 + 12 + 19 + + + 3 + 11 + 13 + + + 12 + 14 + 20 + + + 4 + 13 + 15 + + + 6 + 14 + 16 + + + 15 + 17 + 20 + + + 7 + 16 + 18 + + + 9 + 17 + 19 + + + 11 + 18 + 20 + + + 13 + 16 + 19 + + + + + 20 + 2 + 3 + + + 19 + 1 + 4 + + + 1 + 4 + 5 + + + 2 + 3 + 6 + + + 3 + 6 + 7 + + + 4 + 5 + 8 + + + 5 + 8 + 9 + + + 6 + 7 + 10 + + + 7 + 10 + 11 + + + 8 + 9 + 12 + + + 9 + 12 + 13 + + + 10 + 11 + 14 + + + 11 + 14 + 15 + + + 12 + 13 + 16 + + + 13 + 16 + 17 + + + 14 + 15 + 18 + + + 15 + 18 + 19 + + + 16 + 17 + 20 + + + 2 + 17 + 20 + + + 1 + 18 + 19 + + + + + 2 + 3 + 20 + + + 1 + 3 + 4 + + + 1 + 2 + 4 + + + 2 + 3 + 5 + + + 4 + 6 + 7 + + + 5 + 7 + 8 + + + 5 + 6 + 8 + + + 6 + 7 + 9 + + + 8 + 10 + 11 + + + 9 + 11 + 12 + + + 9 + 10 + 12 + + + 10 + 11 + 13 + + + 12 + 14 + 15 + + + 13 + 15 + 16 + + + 13 + 14 + 16 + + + 14 + 15 + 17 + + + 16 + 18 + 19 + + + 17 + 19 + 20 + + + 17 + 18 + 20 + + + 1 + 18 + 19 + + + + + 6 + 10 + 16 + + + 6 + 7 + 17 + + + 7 + 8 + 18 + + + 8 + 9 + 19 + + + 9 + 10 + 20 + + + 1 + 2 + 15 + + + 2 + 3 + 11 + + + 3 + 4 + 12 + + + 4 + 5 + 13 + + + 1 + 5 + 14 + + + 7 + 16 + 20 + + + 8 + 16 + 17 + + + 9 + 17 + 18 + + + 10 + 18 + 19 + + + 6 + 19 + 20 + + + 1 + 11 + 12 + + + 2 + 12 + 13 + + + 3 + 13 + 14 + + + 4 + 14 + 15 + + + 5 + 11 + 15 + + + + + 1 + 1 + 5 + + + 2 + 2 + 5 + + + 3 + 3 + 6 + + + 4 + 4 + 6 + + + 1 + 2 + 7 + + + 3 + 4 + 7 + + + 5 + 6 + 10 + + + 8 + 9 + 9 + + + 8 + 8 + 10 + + + 7 + 9 + 11 + + + 10 + 13 + 14 + + + 12 + 13 + 13 + + + 11 + 12 + 12 + + + 11 + 15 + 16 + + + 14 + 17 + 18 + + + 14 + 19 + 20 + + + 15 + 17 + 17 + + + 15 + 18 + 18 + + + 16 + 19 + 19 + + + 16 + 20 + 20 + + + + + 5 + 4 + 8 + + + 1 + 5 + 6 + + + 2 + 6 + 7 + + + 3 + 7 + 8 + + + 8 + 9 + 12 + + + 5 + 9 + 10 + + + 6 + 10 + 11 + + + 7 + 11 + 12 + + + 12 + 13 + 16 + + + 9 + 13 + 14 + + + 10 + 14 + 15 + + + 11 + 15 + 16 + + + 16 + 17 + 20 + + + 13 + 17 + 18 + + + 14 + 18 + 19 + + + 15 + 19 + 20 + + + 1 + 4 + 20 + + + 1 + 2 + 17 + + + 2 + 3 + 18 + + + 3 + 4 + 19 + + + \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/maps/mazemaps.xsd b/src/com/malcolmsoft/wumpus/maps/mazemaps.xsd new file mode 100644 index 0000000..04a27bd --- /dev/null +++ b/src/com/malcolmsoft/wumpus/maps/mazemaps.xsd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/Bats.java b/src/com/malcolmsoft/wumpus/model/Bats.java new file mode 100644 index 0000000..f494645 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/Bats.java @@ -0,0 +1,26 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class Bats extends GameObject {} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/CaveModel.java b/src/com/malcolmsoft/wumpus/model/CaveModel.java new file mode 100644 index 0000000..6adcbce --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/CaveModel.java @@ -0,0 +1,235 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +import com.malcolmsoft.wumpus.GameCanvas; +import com.malcolmsoft.wumpus.maps.Map; +import com.malcolmsoft.wumpus.maps.Room; +import java.util.Vector; + +/** + * + * @author Malcolm + */ +public class CaveModel { + //Game model + private volatile boolean gameActive = false; + + private Player player = new Player("Player"); + private Wumpus wumpus = new Wumpus(); + private Pit[] pits = new Pit[PITS_COUNT]; + private Bats[] bats = new Bats[BATS_COUNT]; + + private Map map; + + private static final int PITS_COUNT = 2; + private static final int BATS_COUNT = 2; + + private static final int HAZARD_NONE = 0; + private static final int HAZARD_PIT = 1; + private static final int HAZARD_WUMPUS = 2; + + private GameCanvas eventListener; + + public CaveModel() { + for (int pitPos = 0; pitPos < pits.length; pitPos++) { + pits[pitPos] = new Pit(); + } + + for (int batsPos = 0; batsPos < bats.length; batsPos++) { + bats[batsPos] = new Bats(); + } + } + + public void startNewGame(Map map) { //Assumes that map is loaded + this.map = map; + + player.setRandomLocation(map); + player.resetWeapons(); + + wumpus.setRandomLocation(map); + wumpus.sleep(); + + for (int pitPos = 0; pitPos < pits.length; pitPos++) { + pits[pitPos].setRandomLocation(map); + } + + for (int batsPos = 0; batsPos < bats.length; batsPos++) { + bats[batsPos].setRandomLocation(map); + } + + gameActive = true; + + if (eventListener != null) showStartingEvents(); //Check could be omitted, but why waste CPU cycles? + } + + public void setEventListener(GameCanvas eventListener) { + if (eventListener == null) + throw new NullPointerException("Event listener can't be null"); + + this.eventListener = eventListener; + } + + private void showStartingEvents() { + viewGameEvent(new EventLocationChange(player.getLocation(), EventLocationChange.CAUSE_SPAWN)); + checkFeelings(); + } + + public synchronized void nextGameCycle(PlayerAction action) { + if (!gameActive) throw new IllegalStateException("Game has not started yet!"); + + //First player makes his move + boolean wumpusEncountered = false; + switch (action.getActionType()) { + case PlayerAction.ACTION_MOVE: + int newRoomId = ((PlayerActionMove) action).getNewRoomId(); + //If room is invalid, exception is thrown and nothing else is done + Room enteredRoom = map.getRoom(newRoomId); + if (!player.getLocation().connectsTo(enteredRoom)) { + throw new IllegalArgumentException("You can't move from room " + + player.getLocation().getId() + " to room " + enteredRoom.getId() + "!"); + } + + viewGameEvent(new EventLocationChange(enteredRoom, EventLocationChange.CAUSE_PLAYER)); + int hazard = moveAndCheckHazards(player, enteredRoom); + switch (hazard) { + case HAZARD_PIT: + gameActive = false; + viewGameEvent(new EventDefeat(EventDefeat.CAUSE_FELL_INTO_PIT, + wumpus.getLocation().getId())); + return; + case HAZARD_WUMPUS: + wumpusEncountered = true; + wumpus.wakeUp(); //Woke up because player bumped into him + break; + } + break; + case PlayerAction.ACTION_SHOOT: + Vector shotObjects = player.shoot(map, ((PlayerActionShoot) action).getRoomsToShoot()); + + boolean wumpusShot = shotObjects.contains(wumpus); + int wumpusLocationId = wumpus.getLocation().getId(); + + Event endGameEvent = null; + if (shotObjects.contains(player)) { //Player shot himself + endGameEvent = new EventDefeat(EventDefeat.CAUSE_PLAYER_SHOT_HIMSELF, wumpusLocationId, + wumpusShot); + } else if (wumpusShot) { //Player shot wumpus! + endGameEvent = new EventVictory(wumpusLocationId); + } else if (player.chargesDepleted()) { + endGameEvent = new EventDefeat(EventDefeat.CAUSE_CHARGES_DEPLETED, wumpusLocationId, + wumpusShot); + } + + if (endGameEvent != null) { //Something has happened and the game should end + gameActive = false; + viewGameEvent(endGameEvent); + return; + } + + viewGameEvent(new EventShot()); + wumpus.wakeUp(); //Wakes up because of shooting + break; + } + + //Now if wumpus woke up, it moves + if (wumpus.isAwake()) { + wumpus.moveAtRandom(); + if (player.getLocation().hasObject(wumpus)) { + gameActive = false; + viewGameEvent(new EventDefeat(wumpusEncountered ? + EventDefeat.CAUSE_WUMPUS_WAS_IN_CAVE : EventDefeat.CAUSE_WUMPUS_MOVED_INTO_CAVE)); + return; + } else { + if (wumpusEncountered) { //Don't report anything if wumpus wasn't encountered, he is stealthy + viewGameEvent(new EventWumpusMoved(wumpusEncountered)); + } + } + + wumpus.sleep(); + } + + if (action.getActionType() == PlayerAction.ACTION_MOVE) + checkFeelings(); + } + + private int moveAndCheckHazards(Player player, Room enteredRoom) { + player.setLocation(enteredRoom); + + if (enteredRoom.hasObjectType(Pit.class)) { + return HAZARD_PIT; + } else if (enteredRoom.hasObjectType(Bats.class)) { + player.setRandomLocation(map, false); + Room newRoom = player.getLocation(); + viewGameEvent(new EventLocationChange(newRoom, EventLocationChange.CAUSE_BATS)); + return moveAndCheckHazards(player, newRoom); + } else if (enteredRoom.hasObjectType(Wumpus.class)) { + return HAZARD_WUMPUS; + } + + return HAZARD_NONE; + } + + private void checkFeelings() { + boolean pitsNearby = false; + boolean batsNearby = false; + boolean wumpusNearby = false; + + Room currRoom = player.getLocation(); + for (int passage = currRoom.getPassagesCount() - 1; passage >= 0; passage--) { + Room connectedRoom = currRoom.getNextRoom(passage); + if (connectedRoom.hasObjectType(Pit.class)) { + pitsNearby |= true; + } + + if (connectedRoom.hasObjectType(Bats.class)) { + batsNearby |= true; + } + + if (connectedRoom.hasObjectType(Wumpus.class)) { + wumpusNearby |= true; + } + } + + viewGameEvent(new EventFeel(pitsNearby, batsNearby, wumpusNearby)); + } + + private void viewGameEvent(Event event) { + if (eventListener != null) { + eventListener.viewGameEvent(event); + } + } + + public String getPlayerId() { //Currently only one player exists + return player.getId(); + } + + public Map getMap() { + return map; + } + + public int getPlayerCharges(String playerId) { + return player.getChargesLeft(); + } + + public boolean isGameActive() { + return gameActive; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/Event.java b/src/com/malcolmsoft/wumpus/model/Event.java new file mode 100644 index 0000000..332a14c --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/Event.java @@ -0,0 +1,43 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class Event { + private int eventType; + + public static final int EVENT_PLAYER_MOVED = 1; + public static final int EVENT_PLAYER_SHOT = 2; + public static final int EVENT_PLAYER_FEELS = 3; + public static final int EVENT_WUMPUS_ESCAPED = 4; + public static final int EVENT_VICTORY = -1; + public static final int EVENT_DEFEAT = -2; + + public Event(int eventType) { + this.eventType = eventType; + } + + public int getEventType() { + return eventType; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/EventDefeat.java b/src/com/malcolmsoft/wumpus/model/EventDefeat.java new file mode 100644 index 0000000..5b28380 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventDefeat.java @@ -0,0 +1,91 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventDefeat extends EventEnd { + private boolean wumpusShot = false; //For a rare case when player shot himself but managed to kill wumpus + + public static final int CAUSE_PLAYER_SHOT_HIMSELF = 1; + public static final int CAUSE_CHARGES_DEPLETED = 2; + public static final int CAUSE_FELL_INTO_PIT = 3; + public static final int CAUSE_WUMPUS_WAS_IN_CAVE = 4; + public static final int CAUSE_WUMPUS_MOVED_INTO_CAVE = 5; + + private int cause; + + public EventDefeat(int cause, int wumpusRoomId, boolean wumpusShot) { + super(EVENT_DEFEAT, wumpusRoomId); + this.cause = cause; + this.wumpusShot = wumpusShot; + } + + public EventDefeat(int cause, int wumpusRoomId) { + this(cause, wumpusRoomId, false); + } + + public EventDefeat(int cause) { + this(cause, -1); + } + + public int getCause() { + return cause; + } + + public boolean isWumpusShot() { + return wumpusShot; + } + + public String toString() { + StringBuffer result = new StringBuffer(); + switch (cause) { + case CAUSE_PLAYER_SHOT_HIMSELF: + if (wumpusShot) { + result.append("You have shot wumpus! "); + } + result.append("Unfortunately, you shot yourself."); + break; + case CAUSE_CHARGES_DEPLETED: + result.append("You have no charges left."); + break; + case CAUSE_FELL_INTO_PIT: + result.append("Yeeeeiiii! You fell into a pit."); + break; + case CAUSE_WUMPUS_MOVED_INTO_CAVE: + result.append("Wumpus moved into your room and ate you."); + break; + case CAUSE_WUMPUS_WAS_IN_CAVE: + result.append("Wumpus decided to stay in the room and ate you."); + break; + } + result.append(" You lose..."); + + int wumpusRoomId = getWumpusRoomId(); + if (wumpusRoomId > 0) { + result.append(" Wumpus was in room "); + result.append(wumpusRoomId); + } + + return result.toString(); + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/EventEnd.java b/src/com/malcolmsoft/wumpus/model/EventEnd.java new file mode 100644 index 0000000..d46c60d --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventEnd.java @@ -0,0 +1,41 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventEnd extends Event { + private final int wumpusRoomId; + + public EventEnd(int eventType, int wumpusRoomId) { + super(eventType); + this.wumpusRoomId = wumpusRoomId; + } + + public EventEnd(int eventType) { + this(eventType, 0); + } + + public int getWumpusRoomId() { + return wumpusRoomId > 0 ? wumpusRoomId : -1; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/EventFeel.java b/src/com/malcolmsoft/wumpus/model/EventFeel.java new file mode 100644 index 0000000..2da91a7 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventFeel.java @@ -0,0 +1,57 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventFeel extends Event { + private boolean pitNearby; + private boolean batsNearby; + private boolean wumpusNearby; + + public EventFeel(boolean pitNearby, boolean batsNearby, boolean wumpusNearby) { + super(EVENT_PLAYER_FEELS); + this.pitNearby = pitNearby; + this.batsNearby = batsNearby; + this.wumpusNearby = wumpusNearby; + } + + public boolean isPitNearby() { + return pitNearby; + } + + public boolean areBatsNearby() { + return batsNearby; + } + + public boolean isWumpusNearby() { + return wumpusNearby; + } + + public String toString() { + if (pitNearby || batsNearby || wumpusNearby) { + return (pitNearby ? "You feel a draft. " : "") + + (batsNearby ? "You hear bats flapping their wings. " : "") + + (wumpusNearby ? "You feel wumpus is near!" : ""); + } else return "You feel nothing"; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/EventLocationChange.java b/src/com/malcolmsoft/wumpus/model/EventLocationChange.java new file mode 100644 index 0000000..9498ce2 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventLocationChange.java @@ -0,0 +1,82 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +import com.malcolmsoft.wumpus.maps.Room; + +/** + * + * @author Malcolm + */ +public class EventLocationChange extends Event { + public static final int CAUSE_PLAYER = 0; + public static final int CAUSE_BATS = 1; + public static final int CAUSE_SPAWN = 2; + + private int newRoomId; + private int[] connectedRoomsIds; + private int cause; + + public EventLocationChange(Room newRoom, int cause) { + super(EVENT_PLAYER_MOVED); + this.newRoomId = newRoom.getId(); + this.connectedRoomsIds = newRoom.getConnectedRoomsIds(); + this.cause = cause; + } + + public int getNewRoomId() { + return newRoomId; + } + + public int[] getConnectedRoomsIds() { + return connectedRoomsIds; + } + + public int getCause() { + return cause; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(connectedRoomsIds[0]); + if (connectedRoomsIds.length > 1) { + for (int passagePos = 1; passagePos < connectedRoomsIds.length - 1; passagePos++) { + buf.append(", "); + buf.append(connectedRoomsIds[passagePos]); + } + buf.append(" and "); + buf.append(connectedRoomsIds[connectedRoomsIds.length - 1]); + } + + String messageStart = null; + switch (cause) { + case CAUSE_PLAYER: + messageStart = "You moved into room "; + break; + case CAUSE_BATS: + messageStart = "Bats snatched you and dragged into room "; + break; + case CAUSE_SPAWN: + messageStart = "You start in the room "; + break; + } + return messageStart + newRoomId + " with passages leading to " + buf.toString() + "."; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/EventShot.java b/src/com/malcolmsoft/wumpus/model/EventShot.java new file mode 100644 index 0000000..904d169 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventShot.java @@ -0,0 +1,34 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventShot extends Event { + public EventShot() { + super(EVENT_PLAYER_SHOT); + } + + public String toString() { + return "You have missed"; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/EventVictory.java b/src/com/malcolmsoft/wumpus/model/EventVictory.java new file mode 100644 index 0000000..2250925 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventVictory.java @@ -0,0 +1,40 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventVictory extends EventEnd { + public EventVictory(int wumpusRoomId) { + super(EVENT_VICTORY, wumpusRoomId); + } + + public EventVictory() { + this(-1); + } + + public String toString() { + int wumpusRoomId = getWumpusRoomId(); + return "You have shot wumpus!" + (wumpusRoomId > 0 ? " Wumpus was in room " + + Integer.toString(wumpusRoomId) : "") + " You win!"; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/EventWumpusMoved.java b/src/com/malcolmsoft/wumpus/model/EventWumpusMoved.java new file mode 100644 index 0000000..83c0e7b --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/EventWumpusMoved.java @@ -0,0 +1,41 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class EventWumpusMoved extends Event { + private boolean wumpusEncountered; + + public EventWumpusMoved(boolean wumpusEncountered) { + super(EVENT_WUMPUS_ESCAPED); + this.wumpusEncountered = wumpusEncountered; + } + + public boolean wasWumpusEncountered() { + return wumpusEncountered; + } + + public String toString() { + return (wumpusEncountered ? "You bump into wumpus! " : "") + "Wumpus moved to another cave."; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/GameObject.java b/src/com/malcolmsoft/wumpus/model/GameObject.java new file mode 100644 index 0000000..bd8bb7b --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/GameObject.java @@ -0,0 +1,77 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +import com.malcolmsoft.wumpus.maps.Map; +import com.malcolmsoft.wumpus.maps.Room; +import java.util.Random; + +/** + * + * @author Malcolm + */ +public class GameObject { + private Room location; + + private boolean shootable; + + protected static final Random RANDOM = new Random(); + + public GameObject(boolean shootable) { + this.shootable = shootable; + } + + public GameObject() { + this.shootable = false; + } + + public Room getLocation() { + return location; + } + + public void setRandomLocation(Map map, boolean searchEmptyLocation) { + if (location != null) { + location.removeObject(this); + } + + do { + int newId = RANDOM.nextInt(map.getRoomsCount()) + 1; + location = map.getRoom(newId); + } while (searchEmptyLocation && !location.isEmpty()); + location.addObject(this); //Add backwards link to this object + } + + public void setRandomLocation(Map map) { + setRandomLocation(map, true); + } + + public void setLocation(Room room) { + if (location != null) { + location.removeObject(this); + } + + location = room; + location.addObject(this); + } + + public boolean isShootable() { + return shootable; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/Pit.java b/src/com/malcolmsoft/wumpus/model/Pit.java new file mode 100644 index 0000000..a4e9f01 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/Pit.java @@ -0,0 +1,26 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class Pit extends GameObject {} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/Player.java b/src/com/malcolmsoft/wumpus/model/Player.java new file mode 100644 index 0000000..614fa59 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/Player.java @@ -0,0 +1,103 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +import com.malcolmsoft.wumpus.maps.InvalidRoomIdException; +import com.malcolmsoft.wumpus.maps.Map; +import com.malcolmsoft.wumpus.maps.Room; +import java.util.Enumeration; +import java.util.Vector; + +/** + * + * @author Malcolm + */ +public class Player extends GameObject { + private String id; + + private int chargesLeft; + public static final int STARTING_CHARGES = 5; + + public Player(String id) { + super(true); + this.id = id; + resetWeapons(); + } + + Vector shoot(Map map, int[] roomIds) { //From current location + Vector shotObjects = new Vector(); + + int chargesUsed = Math.min(chargesLeft, roomIds.length); + + Room previousRoom = null; + Room currRoom = getLocation(); + for (int roomPos = 0; roomPos < chargesUsed; roomPos++) { + Room nextRoom = null; + try { + nextRoom = map.getRoom(roomIds[roomPos]); + } catch (InvalidRoomIdException ex) {} + + //You can't shoot through non-existent passage or back and forth, so... + if (nextRoom == null || nextRoom == previousRoom || !currRoom.connectsTo(nextRoom)) { + int passagesTotal = currRoom.getPassagesCount(); + int randomPassage = RANDOM.nextInt(passagesTotal); //Choose random passage + nextRoom = currRoom.getNextRoom(randomPassage); + + if (nextRoom == previousRoom) { //Impossible, so we fix this + if (randomPassage == passagesTotal - 1) { + randomPassage--; + } else { + randomPassage++; + } + nextRoom = currRoom.getNextRoom(randomPassage); + } + } + previousRoom = currRoom; + currRoom = nextRoom; + + Enumeration contentsEn = currRoom.contents(); + while (contentsEn.hasMoreElements()) { + GameObject nextObject = (GameObject)contentsEn.nextElement(); + if (nextObject.isShootable()) { + shotObjects.addElement(nextObject); + } + } + } + + chargesLeft -= chargesUsed; //Only if shooting is successful + return shotObjects; + } + + public String getId() { + return id; + } + + public int getChargesLeft() { + return chargesLeft; + } + + public boolean chargesDepleted() { + return chargesLeft <= 0; + } + + public void resetWeapons() { + chargesLeft = STARTING_CHARGES; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/PlayerAction.java b/src/com/malcolmsoft/wumpus/model/PlayerAction.java new file mode 100644 index 0000000..1004f7a --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/PlayerAction.java @@ -0,0 +1,45 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class PlayerAction { + private String playerId; + private int actionType; + + static final int ACTION_MOVE = 0; + static final int ACTION_SHOOT = 1; + + public PlayerAction(String playerId, int actionType) { + this.playerId = playerId; + this.actionType = actionType; + } + + public String getPlayerId() { + return playerId; + } + + public int getActionType() { + return actionType; + } +} diff --git a/src/com/malcolmsoft/wumpus/model/PlayerActionMove.java b/src/com/malcolmsoft/wumpus/model/PlayerActionMove.java new file mode 100644 index 0000000..b6950a9 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/PlayerActionMove.java @@ -0,0 +1,37 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class PlayerActionMove extends PlayerAction { + private int newRoomId; + + public PlayerActionMove(String playerId, int newRoomId) { + super(playerId, ACTION_MOVE); + this.newRoomId = newRoomId; + } + + public int getNewRoomId() { + return newRoomId; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/PlayerActionShoot.java b/src/com/malcolmsoft/wumpus/model/PlayerActionShoot.java new file mode 100644 index 0000000..84b1837 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/PlayerActionShoot.java @@ -0,0 +1,37 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +/** + * + * @author Malcolm + */ +public class PlayerActionShoot extends PlayerAction { + private int[] roomsToShoot; + + public PlayerActionShoot(String playerId, int[] roomsToShoot) { + super(playerId, ACTION_SHOOT); + this.roomsToShoot = roomsToShoot; + } + + public int[] getRoomsToShoot() { + return roomsToShoot; + } +} \ No newline at end of file diff --git a/src/com/malcolmsoft/wumpus/model/Wumpus.java b/src/com/malcolmsoft/wumpus/model/Wumpus.java new file mode 100644 index 0000000..a2758b5 --- /dev/null +++ b/src/com/malcolmsoft/wumpus/model/Wumpus.java @@ -0,0 +1,54 @@ +/* + Copyright © 2009 Alexander Gazarov + + This file is part of Hunt The Wumpus, Java ME application based on BASIC game by Gregory Yob. + + Hunt The Wumpus is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Hunt The Wumpus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Hunt The Wumpus. If not, see . +*/ + +package com.malcolmsoft.wumpus.model; + +import com.malcolmsoft.wumpus.maps.Room; + +/** + * + * @author Malcolm + */ +public class Wumpus extends GameObject { + private boolean awake = false; + + public Wumpus() { + super(true); + } + + public void moveAtRandom() { + if (RANDOM.nextInt(4) == 0) return; //1 in 4 chance that wumpus won't move (he's lazy) + + Room currRoom = getLocation(); + int randomPassage = RANDOM.nextInt(currRoom.getPassagesCount()); + setLocation(currRoom.getNextRoom(randomPassage)); + } + + public void sleep() { + awake = false; + } + + public void wakeUp() { + awake = true; + } + + public boolean isAwake() { + return awake; + } +} \ No newline at end of file