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 {
String groupID = args[0];
String levelName = args[1];
// MarioGame game = new MarioGame();
//
//// String levelPath = String.format("/app/levels/group%s/%s.txt", groupID, levelName); // For web
//// String repPath = String.format("/files/tmp.rep"); // For web
//
// String levelPath = String.format("./levels/group%s/%s.txt", groupID, levelName); // For local
// String repPath = String.format("./reps/%s_sav.rep", levelName); // For local
MarioGame game = new MarioGame();
// String levelPath = String.format("/app/levels/group%s/%s.txt", groupID, levelName); // 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 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);
// 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 repPath = String.format("/files/%s_sav.rep", levelName); // For web
MarioGame game = new MarioGame();
game.setLives(lives);
MarioResult tmpResult = game.playGame(new HumanAgent(),getLevel(levelPath), 200, repPath);
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){
String levelPath = String.format("/app/levels/%s.lvl", 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 MarioAgent agent = null;
private MarioWorld world = null;
private int initialLives;
/**
* Create a mario game to be played
@ -86,6 +87,9 @@ public class MarioGame {
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
@ -144,6 +148,7 @@ public class MarioGame {
if (visual) {
this.world.initializeVisuals(this.render.getGraphicsConfiguration());
}
this.world.lives = this.initialLives;
this.world.mario.isLarge = marioState > 0;
this.world.mario.isFire = marioState > 1;
this.world.update(new boolean[MarioActions.numberOfActions()]);
@ -206,16 +211,19 @@ public class MarioGame {
return new MarioResult(this.world, gameEvents, agentEvents);
}
public static void showNewWindow(JFrame relativeWindow) {
// 创建一个新窗口
JFrame newJFrame = new JFrame("新的窗口");
// 鍒涘缓涓<EFBFBD>柊绐楀彛
JFrame newJFrame = new JFrame("鏂扮殑绐楀彛");
newJFrame.setSize(250, 250);
// 把新窗口的位置设置到 relativeWindow 窗口的中心
// 鎶婃柊绐楀彛鐨勪綅缃 relativeWindow 绐楀彛鐨勪腑蹇<EFBFBD>
newJFrame.setLocationRelativeTo(relativeWindow);
newJFrame.setVisible(true);
}
public void setLives(int lives) {
this.initialLives = lives;
}
}

View File

@ -21,6 +21,7 @@ public class MarioLevel {
private int[][] levelTiles;
private SpriteType[][] spriteTemplates;
private int[][] lastSpawnTime;
private boolean[][] solidMap;
private MarioTilemap graphics;
private MarioImage flag;
@ -41,6 +42,7 @@ public class MarioLevel {
this.levelTiles = new int[lines[0].length()][lines.length];
this.spriteTemplates = new SpriteType[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 x = 0; x < lines[y].length(); x++) {
this.levelTiles[x][y] = 0;
@ -54,6 +56,7 @@ public class MarioLevel {
for (int y = 0; y < lines.length; y++) {
for (int x = 0; x < lines[y].length(); x++) {
Character c = lines[y].charAt(x);
this.solidMap[x][y] = this.isSolid(c);
switch (c) {
case 'M':
this.marioTileX = x;
@ -283,10 +286,12 @@ public class MarioLevel {
level.exitTileY = this.exitTileY;
level.levelTiles = 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 y = 0; y < level.levelTiles[x].length; y++) {
level.levelTiles[x][y] = this.levelTiles[x][y];
level.lastSpawnTime[x][y] = this.lastSpawnTime[x][y];
level.solidMap[x][y] = this.solidMap[x][y];
}
}
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);
}
}
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.GraphicsConfiguration;
import java.util.ArrayList;
import engine.effects.*;
import engine.graphics.MarioBackground;
import engine.helper.EventType;
@ -13,6 +12,9 @@ import engine.helper.TileFeature;
import engine.sprites.*;
public class MarioWorld {
public static final int onlineTimerMax = 100000;
public GameStatus gameStatus;
public int pauseTimer = 0;
public int fireballsOnScreen = 0;
@ -24,9 +26,12 @@ public class MarioWorld {
public boolean visuals;
public int currentTick;
//Status
public int coins, lives;
public int coins, lives, kills, deaths, jumps, items;
public int airStart, airTime;
public ArrayList<MarioEvent> lastFrameEvents;
// private int segTime = -1;
// private int passedSegs = 0;
private MarioEvent[] killEvents;
private ArrayList<MarioSprite> sprites;
private ArrayList<Shell> shellsToCheck;
@ -37,6 +42,8 @@ public class MarioWorld {
private ArrayList<MarioEffect> effects;
private MarioBackground[] backgrounds = new MarioBackground[2];
// private boolean revivable = false;
// private int totalEnemies;
public MarioWorld(MarioEvent[] killEvents) {
this.pauseTimer = 0;
@ -49,6 +56,9 @@ public class MarioWorld {
this.effects = new ArrayList<>();
this.lastFrameEvents = new ArrayList<>();
this.killEvents = killEvents;
this.lives = 0;
this.kills = 0;
this.deaths = 0;
}
public void initializeVisuals(GraphicsConfiguration graphicsConfig) {
@ -93,8 +103,21 @@ public class MarioWorld {
this.mario.alive = true;
this.mario.world = this;
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() {
ArrayList<MarioSprite> enemies = new ArrayList<>();
for (MarioSprite sprite : sprites) {
@ -141,6 +164,16 @@ public class MarioWorld {
if (this.mario.isFire) {
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));
}
@ -187,6 +220,27 @@ public class MarioWorld {
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) {
int[][] ret = new int[MarioGame.tileWidth][MarioGame.tileHeight];
int centerXInMap = (int) centerX / 16;
@ -304,6 +358,10 @@ public class MarioWorld {
if (this.currentTimer > 0) {
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) {
this.currentTimer = 0;
this.timeout();
@ -332,11 +390,18 @@ public class MarioWorld {
for (MarioSprite sprite : sprites) {
if (sprite.x < cameraX - 64 || sprite.x > cameraX + MarioGame.width + 64 || sprite.y > this.level.height + 32) {
if (sprite.type == SpriteType.MARIO) {
this.lose();
if (this.lives > 0) {
this.mario.getDrop();
this.revive();
}
else
this.lose();
}
this.removeSprite(sprite);
if (this.isEnemy(sprite) && sprite.y > MarioGame.height + 32) {
this.addEvent(EventType.FALL_KILL, sprite.type.getValue());
else{
this.removeSprite(sprite);
if (this.isEnemy(sprite) && sprite.y > MarioGame.height + 32) {
this.addEvent(EventType.FALL_KILL, sprite.type.getValue());
}
}
continue;
}
@ -433,6 +498,9 @@ public class MarioWorld {
if (this.killEvents != null) {
for (MarioEvent k : this.killEvents) {
if (this.lastFrameEvents.contains(k)) {
// if (this.revivable)
// this.revive();
// else
this.lose();
}
}
@ -526,4 +594,13 @@ public class MarioWorld {
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 {
//FIXME web/local
//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(System.getProperty("user.dir") + "/img/" + imageName); // Local test
// File file = new File("/app/img/" + imageName); // For web
BufferedImage source = ImageIO.read(file);
Image image = gc.createCompatibleImage(source.getWidth(), source.getHeight(), Transparency.BITMASK);
Graphics2D g = (Graphics2D) image.getGraphics();

View File

@ -404,13 +404,38 @@ public class Mario extends MarioSprite {
this.isLarge = false;
}
invulnerableTime = 32;
} else {
if (this.world != null) {
} else if (this.world != null) {
if (this.world.lives <= 0) {
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() {
if (!this.alive) {
return;