package core; import static core.Controls.*; import static core.Script.*; import static core.Sprite.RenderSpritesAboveEntity; import static core.Sprite.RenderSpritesBelowEntity; import static core.Sprite.sprites; import static core.VergeEngine.getGUI; import static domain.Entity.EAST; import static domain.Entity.NE; import static domain.Entity.NORTH; import static domain.Entity.NW; import static domain.Entity.SE; import static domain.Entity.SOUTH; import static domain.Entity.SW; import static domain.Entity.WEST; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import domain.Config; import domain.Entity; import domain.MapDynamic; import domain.MapVerge; import domain.VImage; public class VergeEngine extends Thread { public static boolean done, inscroller = false; public static int px, py; public static int lastentitythink; public static int lastspritethink = 0; public static boolean die; static GUI gui; public static GUI getGUI() { return gui; } /****************************** data ******************************/ // Rafael: new code protected static Config config = null; public static Class<?> systemclass; protected static String mapname; /****************************** code ******************************/ // main engine code static int AllocateEntity(int x, int y, String chr) { Entity e = new Entity(x, y, chr); e.index = numentities; entity.add(e); return numentities++; } protected static class EntityComparator implements Comparator<Entity> { public int compare(Entity ent1, Entity ent2) { return ent1.gety() - ent2.gety(); } } public static void RenderEntities(VImage dest) { List<Entity> entidx = new ArrayList<Entity>(); int entnum = 0; // Build a list of entities that are visible. // FIXME: Make it actually only be entities that are onscreen for (int i = 0; i < numentities; i++) { entidx.add(entity.get(i)); entnum++; } // Ysort that list, then draw. Collections.sort(entidx, new EntityComparator()); // qsort(entidx, entnum, 1, cmpent); for (int i = 0; i < entnum; i++) { RenderSpritesBelowEntity(i); // Rafael: entidx.get(i)); setlucent(entidx.get(i).lucent); entidx.get(i).draw(dest); setlucent(0); RenderSpritesAboveEntity(i); // Rafael: entidx.get(i)); } } static void ProcessEntities() { if (entitiespaused) return; for (int i = 0; i < numentities; i++) { entity.get(i).think(); } } static int EntityAt(int x, int y) { for (int i = 0; i < numentities; i++) { if (entity.get(i).active && x >= entity.get(i).getx() && x < entity.get(i).getx() + entity.get(i).chr.hw && y >= entity.get(i).gety() && y < entity.get(i).gety() + entity.get(i).chr.hh) return i; } return -1; } static int EntityObsAt(int x, int y) { for (int i = 0; i < numentities; i++) { if (entity.get(i).active && entity.get(i).obstruction && x >= entity.get(i).getx() && x < entity.get(i).getx() + entity.get(i).chr.hw && y >= entity.get(i).gety() && y < entity.get(i).gety() + entity.get(i).chr.hh) return i; } return -1; } static boolean isEntityCollisionCapturing() { return !_trigger_onEntityCollide.isEmpty(); } int __obstructionHappened = 0; public static boolean ObstructAt(int x, int y) { if (current_map.getobspixel(x, y)) { if (isEntityCollisionCapturing()) { event_tx = x / 16; event_ty = y / 16; event_entity = __grue_actor_index; event_zone = current_map.getzone(x / 16, y / 16); event_entity_hit = -1; onEntityCollision(); } return true; } int ent_idx = EntityObsAt(x, y); if (ent_idx > -1) { if (isEntityCollisionCapturing()) { event_tx = x / 16; event_ty = y / 16; event_entity = __grue_actor_index; event_zone = -1; event_entity_hit = ent_idx; onEntityCollision(); } return true; } return false; } // returns distance possible to move up to // the first obstruction in the given direction static int MaxPlayerMove(int d, int max) { __grue_actor_index = myself.index; int x, y; int ex = myself.getx(); int ey = myself.gety(); // check to see if the player is obstructable at all if (!myself.obstructable) return max; for (int check = 1; check <= max + 1; check++) { switch (d) { case NORTH: for (x = ex; x < ex + myself.chr.hw; x++) if (ObstructAt(x, ey - check)) return check - 1; break; case SOUTH: for (x = ex; x < ex + myself.chr.hw; x++) if (ObstructAt(x, ey + myself.chr.hh + check - 1)) return check - 1; break; case WEST: for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex - check, y)) return check - 1; break; case EAST: for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex + myself.chr.hw + check - 1, y)) return check - 1; break; case NW: for (x = ex; x < ex + myself.chr.hw; x++) if (ObstructAt(x - check, ey - check)) return check - 1; for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex - check, y - check)) return check - 1; break; case SW: for (x = ex; x < ex + myself.chr.hw; x++) if (ObstructAt(x - check, ey + myself.chr.hh + check - 1)) return check - 1; for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex - check, y + check)) return check - 1; break; case NE: for (x = ex; x < ex + myself.chr.hw; x++) if (ObstructAt(x + check, ey - check)) return check - 1; for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex + myself.chr.hw + check - 1, y - check)) return check - 1; break; case SE: for (x = ex; x < ex + myself.chr.hh; x++) if (ObstructAt(x + check, ey + myself.chr.hh + check - 1)) return check - 1; for (y = ey; y < ey + myself.chr.hh; y++) if (ObstructAt(ex + myself.chr.hw + check - 1, y + check)) return check - 1; break; } } return max; } static void onStep() { if (!_trigger_onStep.isEmpty()) { Script.callfunction(_trigger_onStep); } } static void afterStep() { if (!_trigger_afterStep.isEmpty()) { Script.callfunction(_trigger_afterStep); } } static void afterPlayerMove() { if (!_trigger_afterPlayerMove.isEmpty()) { Script.callfunction(_trigger_afterPlayerMove); } } static void beforeEntityActivation() { if (!_trigger_beforeEntityScript.isEmpty()) { Script.callfunction(_trigger_beforeEntityScript); } } static void afterEntityActivation() { if (!_trigger_afterEntityScript.isEmpty()) { Script.callfunction(_trigger_afterEntityScript); } } static void onEntityCollision() { if (isEntityCollisionCapturing()) { Script.callfunction(_trigger_onEntityCollide); } } public static void ProcessControls() { Controls.UpdateControls(); // No player movement can be done if there's no ready player, or if // there's a script active. if (myself == null || !myself.ready() || invc != 0) { return; } if (myself.movecode == 3) { // ScriptEngine:: playerentitymovecleanup(); } // kill contradictory input if (up && down) up = down = false; if (left && right) left = right = false; // if we're not supposed to be using diagonals, // prevent that, too. // We keep track of the last direction we moved in // and if we have diagonal input, we move along the same // axis of movement as before the conflict (horiz or vert.) // - Jesse 22-10-05 if (!playerdiagonals) { if ((up || down) && (left || right) && !smoothdiagonals) { if (lastplayerdir == WEST || lastplayerdir == EAST) up = down = false; else left = right = false; } else { if (left) { lastplayerdir = WEST; } else if (right) { lastplayerdir = EAST; } else if (up) { lastplayerdir = NORTH; } else if (down) { lastplayerdir = SOUTH; } else { lastplayerdir = 0; } } } // check diagonals first if (left && up) { myself.setface(WEST); int dist = MaxPlayerMove(NW, playerstep); if (dist != 0) { myself.set_waypoint_relative(-1 * dist, -1 * dist, true); return; } } if (right && up) { myself.setface(EAST); int dist = MaxPlayerMove(NE, playerstep); if (dist != 0) { myself.set_waypoint_relative(dist, -1 * dist, true); return; } } if (left && down) { myself.setface(WEST); int dist = MaxPlayerMove(SW, playerstep); if (dist != 0) { myself.set_waypoint_relative(-1 * dist, dist, true); return; } } if (right && down) { myself.setface(EAST); int dist = MaxPlayerMove(SE, playerstep); if (dist != 0) { myself.set_waypoint_relative(dist, dist, true); return; } } // check four cardinal directions last if (up) { myself.setface(NORTH); int dist = MaxPlayerMove(NORTH, playerstep); if (dist != 0) { myself.set_waypoint_relative(0, -1 * dist, true); return; } if (playerdiagonals) { // check for sliding along walls if we permit diagonals dist = MaxPlayerMove(NW, playerstep); if (dist != 0) { myself.setface(WEST); myself.set_waypoint_relative(-1 * dist, -1 * dist, true); return; } dist = MaxPlayerMove(NE, playerstep); if (dist != 0) { myself.setface(EAST); myself.set_waypoint_relative(dist, -1 * dist, true); return; } } } if (down) { myself.setface(SOUTH); int dist = MaxPlayerMove(SOUTH, playerstep); if (dist != 0) { myself.set_waypoint_relative(0, dist, true); return; } if (playerdiagonals) { // check for sliding along walls if we permit diagonals dist = MaxPlayerMove(SW, playerstep); if (dist != 0) { myself.setface(WEST); myself.set_waypoint_relative(-1 * dist, 1 * dist, true); return; } dist = MaxPlayerMove(SE, playerstep); if (dist != 0) { myself.setface(EAST); myself.set_waypoint_relative(dist, dist, true); return; } } } if (left) { myself.setface(WEST); int dist = MaxPlayerMove(WEST, playerstep); if (dist != 0) { myself.set_waypoint_relative(-1 * dist, 0, true); return; } if (playerdiagonals) { // check for sliding along walls if we permit diagonals dist = MaxPlayerMove(NW, playerstep); if (dist != 0) { myself.setface(WEST); myself.set_waypoint_relative(-1 * dist, -1 * dist, true); return; } dist = MaxPlayerMove(SW, playerstep); if (dist != 0) { myself.setface(WEST); myself.set_waypoint_relative(-1 * dist, 1 * dist, true); return; } } } if (right) { myself.setface(EAST); int dist = MaxPlayerMove(EAST, playerstep); if (dist != 0) { myself.set_waypoint_relative(dist, 0, true); return; } if (playerdiagonals) { // check for sliding along walls if we permit diagonals dist = MaxPlayerMove(NE, playerstep); if (dist != 0) { myself.setface(EAST); myself.set_waypoint_relative(dist, -1 * dist, true); return; } dist = MaxPlayerMove(SE, playerstep); if (dist != 0) { myself.setface(EAST); myself.set_waypoint_relative(dist, dist, true); return; } } } // Check for entity/zone activation if (b1) { int ex = 0, ey = 0; UnB1(); switch (myself.face) // face { case NORTH: ex = myself.getx() + (myself.chr.hw / 2); ey = myself.gety() - 1; break; case SOUTH: ex = myself.getx() + (myself.chr.hw / 2); ey = myself.gety() + myself.chr.hh + 1; break; case WEST: ex = myself.getx() - 1; ey = myself.gety() + (myself.chr.hh / 2); break; case EAST: ex = myself.getx() + myself.chr.hw + 1; ey = myself.gety() + (myself.chr.hh / 2); break; } int i = EntityAt(ex, ey); if (i != -1) { // FIXME && entity.get(i).movescript.length() > 0) { if (entity.get(i).autoface) { // FIXME && entity.get(i).ready()) { switch (myself.face) // face { case NORTH: entity.get(i).setface(SOUTH); break; case SOUTH: entity.get(i).setface(NORTH); break; case WEST: entity.get(i).setface(EAST); break; case EAST: entity.get(i).setface(WEST); break; default: System.err .println("ProcessControls() - uwahh? invalid myself.face parameter"); } } event_tx = entity.get(i).getx() / 16; event_ty = entity.get(i).gety() / 16; event_entity = i; int cur_timer = timer; beforeEntityActivation(); Script.callfunction(entity.get(i).script); entity.get(i).clear_waypoints(); // Rafael afterEntityActivation(); timer = cur_timer; return; } int cz = current_map.getzone(ex / 16, ey / 16); if (cz > 0 && current_map.getScriptZone(cz).length() > 0 && current_map.getMethodZone(cz) > 0) { int cur_timer = timer; event_zone = cz; event_tx = ex / 16; event_ty = ey / 16; event_entity = i; Script.callfunction(current_map.getScriptZone(cz)); timer = cur_timer; } } } static void MapScroller(VImage dest) { inscroller = true; int oldx = xwin; int oldy = ywin; int oldtimer = timer; int oldcamera = cameratracking; cameratracking = 0; clearLastKey(); // lastpressed = 0; while (getLastKeyChar() != 41) { if (getKey(KeyUp)) ywin--; if (getKey(KeyDown)) ywin++; if (getKey(KeyLeft)) xwin--; if (getKey(KeyRight)) xwin++; Controls.UpdateControls(); RenderMap(dest); showpage(); } clearLastKey(); // lastpressed = 0; clearKey(41); // keys[41] = 0; cameratracking = oldcamera; timer = oldtimer; ywin = oldy; xwin = oldx; inscroller = false; } private static void complyToLimits(VImage dest, int mapRight, int mapDown) { if (!current_map.getHorizontalWrapable()) { // Rafael: new code if (xwin + dest.width >= mapRight) xwin = mapRight - dest.width; if (xwin < 0) xwin = 0; } if (!current_map.getVerticalWrapable()) { // Rafael: new code if (ywin + dest.height >= mapDown) ywin = mapDown - dest.height; if (ywin < 0) ywin = 0; } } public static final int CAMERA_STATIC = 0; public static final int CAMERA_PLAYER = 1; public static final int CAMERA_ENTITY = 2; public static final int CAMERA_TRANSITION = 3; public static void RenderMap(VImage dest) { if (current_map == null) { return; } if (!inscroller && getLastKeyChar() == 41) MapScroller(dest); int rmap = (current_map.getWidth() * 16); int dmap = (current_map.getHeight() * 16); switch (cameratracking) { case CAMERA_STATIC: complyToLimits(dest, rmap, dmap); break; case CAMERA_PLAYER: if (myself != null) { xwin = (myself.getx() + myself.chr.hw / 2) - (dest.width / 2) -8; ywin = (myself.gety() + myself.chr.hh / 2) - (dest.height / 2) -24; } else { xwin = 0; ywin = 0; } complyToLimits(dest, rmap, dmap); break; case CAMERA_ENTITY: if (cameratracker >= numentities || cameratracker < 0) { xwin = 0; ywin = 0; } else { xwin = (entity.get(cameratracker).getx() + 8) - (dest.width / 2); ywin = (entity.get(cameratracker).gety() + 8) - (dest.height / 2); } complyToLimits(dest, rmap, dmap); break; case CAMERA_TRANSITION: // Rafael: New camera tracking mode = scrolling transition (Zelda-like) if (myself != null) { if(myself.getx() - xwin <= -8) { // scroll left setentitiespaused(true); myself.setx((myself.getx()/16)*16); xwin = (xwin/dest.width)*dest.width -4; while(xwin % dest.width != 0) { xwin-=4; current_map.render(xwin, ywin, dest); showpage(); } setentitiespaused(false); } if(myself.getx() - xwin >= dest.width) { // scroll right setentitiespaused(true); xwin = (xwin/dest.width)*dest.width +4; while(xwin % dest.width != 0) { xwin+=4; current_map.render(xwin, ywin, dest); showpage(); } setentitiespaused(false); } if(myself.gety() - ywin <= -8) { // scroll up setentitiespaused(true); myself.sety((myself.gety()/16)*16); ywin = (ywin/dest.height)*dest.height -4; while(ywin % dest.height != 0) { ywin-=4; current_map.render(xwin, ywin, dest); showpage(); } setentitiespaused(false); } if(myself.gety() - ywin >= dest.height) { // scroll down setentitiespaused(true); ywin = (ywin/dest.height)*dest.height +4; while(ywin % dest.height != 0) { ywin+=4; current_map.render(xwin, ywin, dest); showpage(); } setentitiespaused(false); } } else { xwin = 0; ywin = 0; } complyToLimits(dest, rmap, dmap); break; } // Doesn't work if systemtime is not updated! // RBP Map rendering skip to accelerate drawing //if(framecount>=2) { current_map.render(xwin, ywin, dest); //framecount=0; //} //framecount++; } //static int framecount = 0; static void CheckZone() { int cur_timer = timer; int cz = current_map.getzone(px, py); // the following line is probably now correct, since .percent is in // [0,255] // and so the max rnd() will produce is 254, which will still always // trigger // if .percent is 255, and the lowest is 0, which will never trigger, // even if // .percent is 0 int rnd = (int) (255 * Math.random()); if (rnd < current_map.getPercentZone(cz)) { event_zone = cz; Script.callfunction(current_map.getScriptZone(cz)); } timer = cur_timer; } public static void TimedProcessEntities() { if (entitiespaused) return; while (lastentitythink < systemtime) { if (done) break; if (myself != null) { px = (myself.getx() + (myself.chr.hw / 2)) / 16; py = (myself.gety() + (myself.chr.hh / 2)) / 16; } ProcessEntities(); if (invc == 0) ProcessControls(); if (myself != null && invc == 0) { if ((px != (myself.getx() + (myself.chr.hw / 2)) / 16) || (py != (myself.gety() + (myself.chr.hh / 2)) / 16)) { px = (myself.getx() + (myself.chr.hw / 2)) / 16; py = (myself.gety() + (myself.chr.hh / 2)) / 16; event_tx = px; event_ty = py; onStep(); CheckZone(); afterStep(); } } lastentitythink++; } } public static void TimedProcessSprites() { while (lastspritethink < systemtime) { for (int i = 0; i < sprites.size(); i++) { if (sprites.get(i).image == null) continue; if (sprites.get(i).wait > 0) { sprites.get(i).wait--; continue; } sprites.get(i).timer++; sprites.get(i).thinkctr++; if (sprites.get(i).thinkctr > sprites.get(i).thinkrate) { sprites.get(i).thinkctr = 0; event_sprite = i; Script.callfunction(sprites.get(i).thinkproc); } } lastspritethink++; } } public void run() { callfunction("autoexec"); while(mapname!=null && !mapname.isEmpty()) { log("Entering: " + mapname); engine_start(); // Game Loop while(!done) { updateControls(); //TimedProcessEntities(); while (!die) { updateControls(); if(virtualScreen==null) { screen.render(); } else { virtualScreen.render(); } if(!die) // redundant? showpage(); } } } } public static void engine_start() { numentities = 0; entity.clear(); player = -1; myself = null; xwin = ywin = 0; done = false; die = false; if(mapname.toLowerCase().endsWith(".map")) { current_map = new MapVerge(mapname); } else { current_map = new MapDynamic(mapname); } // CleanupCHRs(); timer = 0; lastentitythink = systemtime; lastspritethink = systemtime; } static int timeIncrement = 1; protected static void DefaultTimer() { systemtime +=timeIncrement; // if (engine_paused) // Rafael: Used only in debug // return; timer +=timeIncrement; hooktimer +=timeIncrement; } public static void setTimeIncrement(int i) { // Used to speed up some game timeIncrement = i; } // RBP Avoid FPS getting higher than needed, after spending lot of time loading public static void syncAfterLoading() { GUI.cycleTime = System.currentTimeMillis(); } public static void initVergeEngine(String[] args) { if (args !=null && args.length != 0) { mapname = args[0]; } // Verge (startup) config = new Config(load("verge.cfg")); // If the program is called without a particular map to execute, run // the default mapname specified in the Config file if (mapname == null || mapname.isEmpty()) { mapname = config.getMapname(); log("Mapname from config file: " + mapname); } // [Rafael, the Esper]: See http://www.cap-lore.com/code/java/JavaPixels.html // config.v3_xres = config.v3_xres * 2; // config.v3_yres = config.v3_yres * 2; screen = new VImage(config.getV3_xres(), config.getV3_yres()); // Unused: useful for frameskipping //finalScreen = new VImage(config.getV3_xres(), config.getV3_yres()); if (config.isWindowmode()) { gui = new GUI(config.getV3_xres(), config.getV3_yres()); } else { gui = new GUI(0, 0); } getGUI().updateCanvasSize(); } }