Add revive feature

This commit is contained in:
Ziqi Wang 2022-10-30 21:56:48 +08:00
parent 3a0d15b8ea
commit e14261619e
6 changed files with 149 additions and 23 deletions

View File

@ -23,31 +23,35 @@ public class Play {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
String groupID = args[0]; String groupID = args[0];
String levelName = args[1]; String levelName = args[1];
// MarioGame game = new MarioGame(); MarioGame game = new MarioGame();
//
//// String levelPath = String.format("/app/levels/group%s/%s.txt", groupID, levelName); // For web // String levelPath = String.format("/app/levels/group%s/%s.txt", groupID, levelName); // For web
//// String repPath = String.format("/files/tmp.rep"); // For web // String repPath = String.format("/files/tmp.rep"); // For web
// game.setLives(10);
// String levelPath = String.format("./levels/group%s/%s.txt", groupID, levelName); // For local String levelPath = String.format("./levels/group%s/%s.txt", groupID, levelName); // For local
// String repPath = String.format("./reps/%s_sav.rep", levelName); // For local String repPath = String.format("./reps/%s_sav.rep", levelName); // For local
MarioResult r2 = game.playGame(getLevel(levelPath), repPath);
// MarioResult tmpResult = game.playGame(Replay.getRepAgentFromFile(repPath),getLevel(levelPath), 200, repPath); // MarioResult tmpResult = game.playGame(Replay.getRepAgentFromFile(repPath),getLevel(levelPath), 200, repPath);
// MarioGame game2 = new MarioGame(); // MarioGame game2 = new MarioGame();
// MarioResult r2 = game2.playGame(getLevel(levelPath),200,repPath)
} }
public static byte[] playGameMain(String groupID, String levelName){ public static byte[] playGameMain(String groupID, String levelName, int lives){
String levelPath = String.format("/app/levels/%s.lvl", levelName); // For web String levelPath = String.format("/app/levels/%s.lvl", levelName); // For web
String repPath = String.format("/files/%s_sav.rep", levelName); // For web String repPath = String.format("/files/%s_sav.rep", levelName); // For web
MarioGame game = new MarioGame(); MarioGame game = new MarioGame();
game.setLives(lives);
MarioResult tmpResult = game.playGame(new HumanAgent(),getLevel(levelPath), 200, repPath); MarioResult tmpResult = game.playGame(new HumanAgent(),getLevel(levelPath), 200, repPath);
return Replay.serializeAgentEvents(tmpResult.getAgentEvents()); return Replay.serializeAgentEvents(tmpResult.getAgentEvents());
} }
public static byte[] playGameMain(String groupID, String levelName){
return playGameMain(groupID, levelName, 0);
}
public static void replayGameMain(String groupID, String levelName){ public static void replayGameMain(String groupID, String levelName){
String levelPath = String.format("/app/levels/%s.lvl", levelName); // For web String levelPath = String.format("/app/levels/%s.lvl", levelName); // For web
String repPath = String.format("/files/%s_sav.rep", levelName); // For web String repPath = String.format("/files/%s_sav.rep", levelName); // For web

View File

@ -56,6 +56,7 @@ public class MarioGame {
private MarioRender render = null; private MarioRender render = null;
private MarioAgent agent = null; private MarioAgent agent = null;
private MarioWorld world = null; private MarioWorld world = null;
private int initialLives;
/** /**
* Create a mario game to be played * Create a mario game to be played
@ -86,6 +87,9 @@ public class MarioGame {
this.render.addKeyListener((KeyAdapter) this.agent); this.render.addKeyListener((KeyAdapter) this.agent);
} }
} }
public MarioResult playGame(String level, String resultPath) {
return this.runGame(new agents.HumanAgent(), level, 200, 0, true, 30, 2, resultPath);
}
/** /**
* Play a certain mario level * Play a certain mario level
@ -144,6 +148,7 @@ public class MarioGame {
if (visual) { if (visual) {
this.world.initializeVisuals(this.render.getGraphicsConfiguration()); this.world.initializeVisuals(this.render.getGraphicsConfiguration());
} }
this.world.lives = this.initialLives;
this.world.mario.isLarge = marioState > 0; this.world.mario.isLarge = marioState > 0;
this.world.mario.isFire = marioState > 1; this.world.mario.isFire = marioState > 1;
this.world.update(new boolean[MarioActions.numberOfActions()]); this.world.update(new boolean[MarioActions.numberOfActions()]);
@ -206,16 +211,19 @@ public class MarioGame {
return new MarioResult(this.world, gameEvents, agentEvents); return new MarioResult(this.world, gameEvents, agentEvents);
} }
public static void showNewWindow(JFrame relativeWindow) { public static void showNewWindow(JFrame relativeWindow) {
// 创建一个新窗口 // 鍒涘缓涓<EFBFBD>柊绐楀彛
JFrame newJFrame = new JFrame("新的窗口"); JFrame newJFrame = new JFrame("鏂扮殑绐楀彛");
newJFrame.setSize(250, 250); newJFrame.setSize(250, 250);
// 把新窗口的位置设置到 relativeWindow 窗口的中心 // 鎶婃柊绐楀彛鐨勪綅缃 relativeWindow 绐楀彛鐨勪腑蹇<EFBFBD>
newJFrame.setLocationRelativeTo(relativeWindow); newJFrame.setLocationRelativeTo(relativeWindow);
newJFrame.setVisible(true); newJFrame.setVisible(true);
} }
public void setLives(int lives) {
this.initialLives = lives;
}
} }

View File

@ -21,6 +21,7 @@ public class MarioLevel {
private int[][] levelTiles; private int[][] levelTiles;
private SpriteType[][] spriteTemplates; private SpriteType[][] spriteTemplates;
private int[][] lastSpawnTime; private int[][] lastSpawnTime;
private boolean[][] solidMap;
private MarioTilemap graphics; private MarioTilemap graphics;
private MarioImage flag; private MarioImage flag;
@ -41,6 +42,7 @@ public class MarioLevel {
this.levelTiles = new int[lines[0].length()][lines.length]; this.levelTiles = new int[lines[0].length()][lines.length];
this.spriteTemplates = new SpriteType[lines[0].length()][lines.length]; this.spriteTemplates = new SpriteType[lines[0].length()][lines.length];
this.lastSpawnTime = new int[lines[0].length()][lines.length]; this.lastSpawnTime = new int[lines[0].length()][lines.length];
this.solidMap = new boolean[lines[0].length()][lines.length];
for (int y = 0; y < lines.length; y++) { for (int y = 0; y < lines.length; y++) {
for (int x = 0; x < lines[y].length(); x++) { for (int x = 0; x < lines[y].length(); x++) {
this.levelTiles[x][y] = 0; this.levelTiles[x][y] = 0;
@ -54,6 +56,7 @@ public class MarioLevel {
for (int y = 0; y < lines.length; y++) { for (int y = 0; y < lines.length; y++) {
for (int x = 0; x < lines[y].length(); x++) { for (int x = 0; x < lines[y].length(); x++) {
Character c = lines[y].charAt(x); Character c = lines[y].charAt(x);
this.solidMap[x][y] = this.isSolid(c);
switch (c) { switch (c) {
case 'M': case 'M':
this.marioTileX = x; this.marioTileX = x;
@ -283,10 +286,12 @@ public class MarioLevel {
level.exitTileY = this.exitTileY; level.exitTileY = this.exitTileY;
level.levelTiles = new int[this.levelTiles.length][this.levelTiles[0].length]; level.levelTiles = new int[this.levelTiles.length][this.levelTiles[0].length];
level.lastSpawnTime = new int[this.levelTiles.length][this.levelTiles[0].length]; level.lastSpawnTime = new int[this.levelTiles.length][this.levelTiles[0].length];
level.solidMap = new boolean[this.levelTiles.length][this.levelTiles[0].length];
for (int x = 0; x < level.levelTiles.length; x++) { for (int x = 0; x < level.levelTiles.length; x++) {
for (int y = 0; y < level.levelTiles[x].length; y++) { for (int y = 0; y < level.levelTiles[x].length; y++) {
level.levelTiles[x][y] = this.levelTiles[x][y]; level.levelTiles[x][y] = this.levelTiles[x][y];
level.lastSpawnTime[x][y] = this.lastSpawnTime[x][y]; level.lastSpawnTime[x][y] = this.lastSpawnTime[x][y];
level.solidMap[x][y] = this.solidMap[x][y];
} }
} }
level.spriteTemplates = this.spriteTemplates; level.spriteTemplates = this.spriteTemplates;
@ -386,4 +391,11 @@ public class MarioLevel {
this.flag.render(og, this.exitTileX * 16 - 8 - cameraX, Math.max(1, this.exitTileY - 11) * 16 + 16 - cameraY); this.flag.render(og, this.exitTileX * 16 - 8 - cameraX, Math.max(1, this.exitTileY - 11) * 16 + 16 - cameraY);
} }
} }
public boolean standable(int xTile, int yTile) {
if (yTile >= this.tileHeight)
return false;
else
return !this.solidMap[xTile][yTile] && this.solidMap[xTile][yTile+1];
}
} }

View File

@ -3,7 +3,6 @@ package engine.core;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.GraphicsConfiguration; import java.awt.GraphicsConfiguration;
import java.util.ArrayList; import java.util.ArrayList;
import engine.effects.*; import engine.effects.*;
import engine.graphics.MarioBackground; import engine.graphics.MarioBackground;
import engine.helper.EventType; import engine.helper.EventType;
@ -13,6 +12,9 @@ import engine.helper.TileFeature;
import engine.sprites.*; import engine.sprites.*;
public class MarioWorld { public class MarioWorld {
public static final int onlineTimerMax = 100000;
public GameStatus gameStatus; public GameStatus gameStatus;
public int pauseTimer = 0; public int pauseTimer = 0;
public int fireballsOnScreen = 0; public int fireballsOnScreen = 0;
@ -24,9 +26,12 @@ public class MarioWorld {
public boolean visuals; public boolean visuals;
public int currentTick; public int currentTick;
//Status //Status
public int coins, lives; public int coins, lives, kills, deaths, jumps, items;
public int airStart, airTime;
public ArrayList<MarioEvent> lastFrameEvents; public ArrayList<MarioEvent> lastFrameEvents;
// private int segTime = -1;
// private int passedSegs = 0;
private MarioEvent[] killEvents; private MarioEvent[] killEvents;
private ArrayList<MarioSprite> sprites; private ArrayList<MarioSprite> sprites;
private ArrayList<Shell> shellsToCheck; private ArrayList<Shell> shellsToCheck;
@ -37,6 +42,8 @@ public class MarioWorld {
private ArrayList<MarioEffect> effects; private ArrayList<MarioEffect> effects;
private MarioBackground[] backgrounds = new MarioBackground[2]; private MarioBackground[] backgrounds = new MarioBackground[2];
// private boolean revivable = false;
// private int totalEnemies;
public MarioWorld(MarioEvent[] killEvents) { public MarioWorld(MarioEvent[] killEvents) {
this.pauseTimer = 0; this.pauseTimer = 0;
@ -49,6 +56,9 @@ public class MarioWorld {
this.effects = new ArrayList<>(); this.effects = new ArrayList<>();
this.lastFrameEvents = new ArrayList<>(); this.lastFrameEvents = new ArrayList<>();
this.killEvents = killEvents; this.killEvents = killEvents;
this.lives = 0;
this.kills = 0;
this.deaths = 0;
} }
public void initializeVisuals(GraphicsConfiguration graphicsConfig) { public void initializeVisuals(GraphicsConfiguration graphicsConfig) {
@ -93,8 +103,21 @@ public class MarioWorld {
this.mario.alive = true; this.mario.alive = true;
this.mario.world = this; this.mario.world = this;
this.sprites.add(this.mario); this.sprites.add(this.mario);
// totalEnemies = getEnemies().size();
} }
// public int getTotalEnemies() {
// return level.totalEnemies;
// }
// public int getEnemiesRemain() {
// int n = 0;
// for (int x = (int)(mario.x / 16) + MarioGame.width / 2; x < level.tileWidth; x++) {
// n += level.enemyNumList.get(x);
// }
// return n;
// }
public ArrayList<MarioSprite> getEnemies() { public ArrayList<MarioSprite> getEnemies() {
ArrayList<MarioSprite> enemies = new ArrayList<>(); ArrayList<MarioSprite> enemies = new ArrayList<>();
for (MarioSprite sprite : sprites) { for (MarioSprite sprite : sprites) {
@ -141,6 +164,16 @@ public class MarioWorld {
if (this.mario.isFire) { if (this.mario.isFire) {
marioState = 2; marioState = 2;
} }
if (eventType == EventType.STOMP_KILL || eventType == EventType.FIRE_KILL || eventType == EventType.SHELL_KILL)
this.kills++;
if (eventType == EventType.COLLECT && eventParam != MarioForwardModel.OBS_COIN)
this.items++;
if (eventType == EventType.JUMP) {
this.jumps++;
this.airStart = this.currentTick;
}
if (eventType == EventType.LAND)
this.airTime += (this.currentTick - this.airStart);
this.lastFrameEvents.add(new MarioEvent(eventType, eventParam, mario.x, mario.y, marioState, this.currentTick)); this.lastFrameEvents.add(new MarioEvent(eventType, eventParam, mario.x, mario.y, marioState, this.currentTick));
} }
@ -187,6 +220,27 @@ public class MarioWorld {
this.mario.alive = false; this.mario.alive = false;
} }
public void revive() {
int newTileX = (int) this.mario.x / 16;
int newTileY = (int) this.mario.y / 16;
try {
l: while (true) {
for (int y = this.level.tileHeight - 2; y >= 8; y--) {
if (this.level.standable(newTileX, y)) {
newTileY = y;
break l;
}
}
newTileX--;
}
} catch (ArrayIndexOutOfBoundsException e) {
this.lose();
return;
}
this.mario.x = (float)(newTileX * 16.0 + 8);
this.mario.y = (float)(newTileY * 16.0);
}
public int[][] getSceneObservation(float centerX, float centerY, int detail) { public int[][] getSceneObservation(float centerX, float centerY, int detail) {
int[][] ret = new int[MarioGame.tileWidth][MarioGame.tileHeight]; int[][] ret = new int[MarioGame.tileWidth][MarioGame.tileHeight];
int centerXInMap = (int) centerX / 16; int centerXInMap = (int) centerX / 16;
@ -304,6 +358,10 @@ public class MarioWorld {
if (this.currentTimer > 0) { if (this.currentTimer > 0) {
this.currentTimer -= 30; this.currentTimer -= 30;
// if (this.segTime > 0 && (int) (this.mario.x / 512) > this.passedSegs) {
// this.passedSegs = (int) (this.mario.x / 512);
// this.currentTimer = Math.min(this.currentTimer + this.segTime, 2 * this.segTime) ;
// }
if (this.currentTimer <= 0) { if (this.currentTimer <= 0) {
this.currentTimer = 0; this.currentTimer = 0;
this.timeout(); this.timeout();
@ -332,11 +390,18 @@ public class MarioWorld {
for (MarioSprite sprite : sprites) { for (MarioSprite sprite : sprites) {
if (sprite.x < cameraX - 64 || sprite.x > cameraX + MarioGame.width + 64 || sprite.y > this.level.height + 32) { if (sprite.x < cameraX - 64 || sprite.x > cameraX + MarioGame.width + 64 || sprite.y > this.level.height + 32) {
if (sprite.type == SpriteType.MARIO) { if (sprite.type == SpriteType.MARIO) {
this.lose(); if (this.lives > 0) {
this.mario.getDrop();
this.revive();
}
else
this.lose();
} }
this.removeSprite(sprite); else{
if (this.isEnemy(sprite) && sprite.y > MarioGame.height + 32) { this.removeSprite(sprite);
this.addEvent(EventType.FALL_KILL, sprite.type.getValue()); if (this.isEnemy(sprite) && sprite.y > MarioGame.height + 32) {
this.addEvent(EventType.FALL_KILL, sprite.type.getValue());
}
} }
continue; continue;
} }
@ -433,6 +498,9 @@ public class MarioWorld {
if (this.killEvents != null) { if (this.killEvents != null) {
for (MarioEvent k : this.killEvents) { for (MarioEvent k : this.killEvents) {
if (this.lastFrameEvents.contains(k)) { if (this.lastFrameEvents.contains(k)) {
// if (this.revivable)
// this.revive();
// else
this.lose(); this.lose();
} }
} }
@ -526,4 +594,13 @@ public class MarioWorld {
this.effects.get(i).render(og, cameraX, cameraY); this.effects.get(i).render(og, cameraX, cameraY);
} }
} }
// public void setSegTime(int segTime) {
// this.segTime = segTime;
// this.currentTimer = 2 * segTime;
// }
// public void setRevivable(boolean revivable) {
// this.revivable = revivable;
// }
} }

View File

@ -43,8 +43,8 @@ public class Assets {
private static Image getImage(GraphicsConfiguration gc, String imageName) throws IOException { private static Image getImage(GraphicsConfiguration gc, String imageName) throws IOException {
//FIXME web/local //FIXME web/local
//File file = new File(System.getProperty("user.dir") + "/img/" + imageName); // Local test File file = new File(System.getProperty("user.dir") + "/img/" + imageName); // Local test
File file = new File("/app/img/" + imageName); // For web // File file = new File("/app/img/" + imageName); // For web
BufferedImage source = ImageIO.read(file); BufferedImage source = ImageIO.read(file);
Image image = gc.createCompatibleImage(source.getWidth(), source.getHeight(), Transparency.BITMASK); Image image = gc.createCompatibleImage(source.getWidth(), source.getHeight(), Transparency.BITMASK);
Graphics2D g = (Graphics2D) image.getGraphics(); Graphics2D g = (Graphics2D) image.getGraphics();

View File

@ -404,13 +404,38 @@ public class Mario extends MarioSprite {
this.isLarge = false; this.isLarge = false;
} }
invulnerableTime = 32; invulnerableTime = 32;
} else { } else if (this.world != null) {
if (this.world != null) { if (this.world.lives <= 0) {
this.world.lose(); this.world.lose();
} else {
this.world.lives -= 1;
this.world.deaths += 1;
world.pauseTimer = 3 * POWERUP_TIME;
invulnerableTime = 32;
} }
} }
} }
public void getDrop() {
if (!this.alive)
return;
this.oldLarge = this.isLarge;
this.oldFire = this.isFire;
this.isFire = false;
this.isLarge = false;
if (this.world != null) {
if (this.world.lives <= 0) {
this.world.lose();
} else if (invulnerableTime <= 0) {
this.world.lives -= 1;
this.world.deaths += 1;
world.pauseTimer = 3 * POWERUP_TIME;
}
}
invulnerableTime = 72;
}
public void getFlower() { public void getFlower() {
if (!this.alive) { if (!this.alive) {
return; return;