package domain; import core.Script; import static core.VergeEngine.*; import static core.Script.*; // Merge of g_entity.cpp and g_entity.h public class Entity { public final static int NORTH = 1; public final static int SOUTH = 2; public final static int WEST = 3; public final static int EAST = 4; public final static int NW = 5; public final static int NE = 6; public final static int SW = 7; public final static int SE = 8; public static int FOLLOWDISTANCE = 16; public final static int ENT_AUTOFACE = 1; public final static int ENT_OBSTRUCTS = 2; public final static int ENT_OBSTRUCTED = 4; public final static int ENT_MOTIONLESS = 0; public final static int ENT_MOVESCRIPT = 1; public final static int ENT_WANDERZONE = 2; public final static int ENT_WANDERBOX = 3; // Loaded inside the map public CHR chr; private int x, y; // Unsigned short public String chrname = ""; // filename public boolean visible = true, active = true, autoface = true; public boolean obstruction = true; public boolean obstructable = true; public int lucent; int waypointx, waypointy; public int speed; int speedct; public byte movecode; int delay, wdelay; public int face; int framect; public int specframe; int frame; int wx1, wy1, wx2, wy2; int movemult; String description; // Overkill - 2006-05-21 public String hookrender; public String movescript; public String script; String movestr; int moveofs; public int getx() { if(current_map!=null && current_map.getHorizontalWrapable() && (this.x/16 > current_map.getWidth()<<4 || this.x < 0)) { this.x = (this.x + (current_map.getWidth()<<8)) % (current_map.getWidth()<<8); this.waypointx = (this.waypointx + (current_map.getWidth()<<4)) % (current_map.getWidth()<<4); } return this.x/16; } public int gety() { if(current_map!=null && current_map.getVerticalWrapable() && (this.y/16 > current_map.getHeight()<<4 || this.y < 0)) { this.y = (this.y + (current_map.getHeight()<<8)) % (current_map.getHeight()<<8); this.waypointy = (this.waypointy + (current_map.getHeight()<<4)) % (current_map.getHeight()<<4); } return this.y/16; } private int getOriginalX() { return this.x; } private int getOriginalY() { return this.y; } private void setoriginalx(int x) { this.x = x; getx(); } private void setoriginaly(int y) { this.y = y; gety(); } //[Rafael, the Esper] public void setx(int x) { this.x = x * 16; clear_waypoints(); } public void sety(int y) { this.y = y * 16; clear_waypoints(); } public void incx() { this.incx(1); } public void incy() { this.incy(1); } public void incx(int i) { this.x+= i * 16; } public void incy(int i) { this.y+= i * 16; } public void clear_waypoints() { set_waypoint(getx(), gety()); delay = 0; } // END [Rafael, the Esper] public int getwaypointx() { return this.waypointx; } public int getwaypointy() { return this.waypointy; } void setwaypointx(int waypointx) { this.waypointx = waypointx; } void setwaypointy(int waypointy) { this.waypointy = waypointy; } public int index; Entity follower, follow; int pathx[] = new int[FOLLOWDISTANCE]; int pathy[] = new int[FOLLOWDISTANCE]; int pathf[] = new int[FOLLOWDISTANCE]; public Entity getFollower() { return this.follower; } public void setface(int face) { this.face = face; } public String toString() { return "Entity (" + getx() + ", " + gety() + ") dir:" + face + " isObsT:" + obstructable + " isObs:" + obstruction + " autoF:" + autoface + " " + " speed:" + speed + " movMode:" + movecode + " wanders: (" + wx1 +","+ wy1 +"," + wx2 +"," + wy2 + ") wDelay:" + wdelay + " movescript:" + movescript + " filename:" + chrname + " desc:" + description + " actEvent:" + script; } // Used by the engine public Entity(int x, int y, String chrfn) { follower = null; follow = null; delay = 0; lucent = 0; wdelay = 75; setxy(x, y); setspeed(100); speedct = 0; chrname = chrfn; if(chrfn!=null) // [Rafael, the Esper] chr = new CHR(chrfn); // RequestCHR(chrfn); visible = true; active = true; specframe = 0; movecode = 0; moveofs = 0; framect = 0; frame = 0; face = SOUTH; hookrender = ""; script = ""; description = ""; obstructable = true; obstruction = true; for (int i=0; i<FOLLOWDISTANCE; i++) { pathx[i] = x*16; pathy[i] = y*16; pathf[i] = SOUTH; } } public void setxy(int x1, int y1) { setoriginalx(x1 * 16); setoriginaly(y1 * 16); if (follower != null) follower.setxy(x1, y1); set_waypoint(x1, y1); for (int i=0; i<FOLLOWDISTANCE; i++) { pathx[i] = getOriginalX(); pathy[i] = getOriginalY(); pathf[i] = SOUTH; } } int getspeed() { return speed; } void setspeed(int s) { speed = s; // We don't reset the speedct here, because // 1) Is is keeping track of distance already moved but not acted on // (ie any partial movement made but not turned into a tick) // 2) If we reset speedct, setting the speed frequently will slow // the character down by discarding the partial bits if (follower != null) follower.setspeed(s); } public void set_waypoint(int x1, int y1) { setwaypointx(x1); setwaypointy(y1); switch ((int) Math.signum(y1-gety())) { case -1: face = NORTH; break; case 0: break; case 1: face = SOUTH; break; } switch ((int)Math.signum(x1-getx())) { case -1: face = WEST; break; case 0: break; case 1: face = EAST; break; } } public void set_waypoint_relative(int x1, int y1, boolean changeface) { setwaypointx(getwaypointx() + x1); setwaypointy(getwaypointy() + y1); if(changeface) { switch ((int) Math.signum(y1)) { case -1: face = NORTH; break; case 0: break; case 1: face = SOUTH; break; } switch ((int) Math.signum(x1)) { case -1: face = WEST; break; case 0: break; case 1: face = EAST; break; } } } public boolean ready() { return (getx() == getwaypointx() && gety() == getwaypointy()); } boolean leaderidle(){ if (follow!=null) { return follow.leaderidle(); } return (getx() == getwaypointx() && gety() == getwaypointy()); } // called to sync up with leader's frame // of course, if the two people have different- // length walk cycles, they might have the same framect, // but they won't sync visually, which is OK int get_leader_framect() { if(follow!=null) return follow.get_leader_framect(); return framect; } void set_framect_follow(int f) { if(follower!=null) { follower.set_framect_follow(f); } framect = f; } public void stalk(Entity e) { follow = e; e.follower = this; /* Rafael,the Esper: obsolete code: this is resolved in new Entity() * for (int i=0; i<FOLLOWDISTANCE; i++) { pathx[i] = follow.pathx[FOLLOWDISTANCE-1]; pathy[i] = follow.pathy[FOLLOWDISTANCE-1]; pathf[i] = SOUTH; }*/ //setoriginalx(follow.pathx[FOLLOWDISTANCE-1]); //setoriginaly(follow.pathy[FOLLOWDISTANCE-1]); set_waypoint(getx(), gety()); movecode = 0; obstruction = false; obstructable = false; // clear delay info from wandering delay = 0; // sync our (and followers') framect with the leader set_framect_follow(get_leader_framect()); } // This is called when we are going to change // to a kind of movement that isn't stalking to // ensure we are not trying to stalk at the same time public void clear_stalk() { if(follow!=null) { follow.follower = null; follow = null; } } void move_tick() { int dx = getwaypointx() - getx(); int dy = getwaypointy() - gety(); if (this != myself && follow==null && obstructable) { // do obstruction checking */ switch (face) { case NORTH: if (ObstructDirTick(NORTH)) return; break; case SOUTH: if (ObstructDirTick(SOUTH)) return; break; case WEST: if (ObstructDirTick(WEST)) return; break; case EAST: if (ObstructDirTick(EAST)) return; break; default: // Rafael: Do nothing. error("move_tick() - bad face value!!"); } } framect++; // update pathxy for following for (int i=pathx.length-2; i>=0; i--) { pathx[i+1] = pathx[i]; pathy[i+1] = pathy[i]; pathf[i+1] = pathf[i]; } pathx[0] = getOriginalX(); pathy[0] = getOriginalY(); pathf[0] = face; // if following, grab new position from leader // We now keep track of our own framect, (rather // than using the leader's framect) // which is synced with the leader in stalk(), // but then runs free after that so animations // of different lengths are ok in a stalking chain. if (follow != null) { setoriginalx(follow.pathx[FOLLOWDISTANCE-1]); setoriginaly(follow.pathy[FOLLOWDISTANCE-1]); face = follow.pathf[FOLLOWDISTANCE-1]; set_waypoint(getx(), gety()); if (follower != null) follower.move_tick(); return; } // else move if (dx != 0){ setoriginalx((int) (getOriginalX() + (Math.signum(dx) * 16))); } if (dy != 0) setoriginaly((int) (getOriginalY() + (Math.signum(dy) * 16))); if (follower != null) follower.move_tick(); } public void think() { int num_ticks; if (!active) return; if (delay>systemtime) { framect = 0; return; } speedct += speed; num_ticks = speedct / 100; speedct %= 100; while (num_ticks > 0) { num_ticks--; if (ready()) { switch (movecode) { case 0: if (this == myself && invc==0) ProcessControls(); break; case 1: do_wanderzone(); break; case 2: do_wanderbox(); break; case 3: do_movescript(); break; default: System.err.println("think(), unknown movecode value"); } } if (!ready()) move_tick(); } } boolean ObstructDirTick(int d) { __grue_actor_index = this.index; int x, y; int ex = getx(); int ey = gety(); if (!obstructable) return false; switch (d) { case NORTH: for (x=ex; x<ex+chr.hw; x++) if (ObstructAt(x, ey-1)) return true; break; case SOUTH: for (x=ex; x<ex+chr.hw; x++) if (ObstructAt(x, ey+chr.hh)) return true; break; case WEST: for (y=ey; y<ey+chr.hh; y++) if (ObstructAt(ex-1, y)) return true; break; case EAST: for (y=ey; y<ey+chr.hh; y++) if (ObstructAt(ex+chr.hw, y)) return true; break; } return false; } boolean ObstructDir(int d) { __grue_actor_index = this.index; int i, x, y; int ex = getx(); int ey = gety(); if (!obstructable) return false; switch (d) { case NORTH: for (i=0; i<chr.hh; i++) for (x=ex; x<ex+chr.hw; x++) if (ObstructAt(x, ey-i-1)) return true; break; case SOUTH: for (i=0; i<chr.hh; i++) for (x=ex; x<ex+chr.hw; x++) if (ObstructAt(x, ey+i+chr.hh)) return true; break; case WEST: for (i=0; i<chr.hw; i++) for (y=ey; y<ey+chr.hh; y++) if (ObstructAt(ex-i-1, y)) return true; break; case EAST: for (i=0; i<chr.hw; i++) for (y=ey; y<ey+chr.hh; y++) if (ObstructAt(ex+chr.hw+i, y)) return true; break; } return false; } void do_wanderzone() { boolean ub=false, db=false, lb=false, rb=false; int ex = getx()/16; int ey = gety()/16; int myzone = current_map.getzone(ex, ey); if (ObstructDir(EAST) || current_map.getzone(ex+1, ey) != myzone) rb=true; if (ObstructDir(WEST) || current_map.getzone(ex-1, ey) != myzone) lb=true; if (ObstructDir(SOUTH) || current_map.getzone(ex, ey+1) != myzone) db=true; if (ObstructDir(NORTH) || current_map.getzone(ex, ey-1) != myzone) ub=true; if (rb && lb && db && ub) return; // Can't move in any direction move_wander(rb, lb, db, ub); // Rafael, the Esper (refactoring to avoid duplicate code) } void do_wanderbox() { boolean ub=false, db=false, lb=false, rb=false; int ex = getx()/16; int ey = gety()/16; if (ObstructDir(EAST) || ex+1 > wx2) rb=true; if (ObstructDir(WEST) || ex-1 < wx1) lb=true; if (ObstructDir(SOUTH) || ey+1 > wy2) db=true; if (ObstructDir(NORTH) || ey-1 < wy1) ub=true; if (rb && lb && db && ub) return; // Can't move in any direction move_wander(rb, lb, db, ub); } // Method by Rafael, the Esper, to avoid duplicate code and add some specific behavior. private void move_wander(boolean rb, boolean lb, boolean db, boolean ub) { delay = systemtime + (Script.random(1, 3) == 1 ? 0 : wdelay); // Rafael, the Esper (Added random chance of stopping) while (true) { int i = Script.random(1, 4 + 4); // Rafael, the Esper: changed to 1-4 + (extra), if(i>4) { // keep the same direction, with (extra)/(extra+4) % chance i = face; } switch (i) { case EAST: if (rb) break; set_waypoint_relative(16, 0, true); return; case WEST: if (lb) break; set_waypoint_relative(-16, 0, true); return; case SOUTH: if (db) break; set_waypoint_relative(0, 16, true); return; case NORTH: if (ub) break; set_waypoint_relative(0, -16, true); return; } } } public void do_movescript() { char vc2me[] = { 2, 1, 3, 4 }; int arg; // movements factors // These are set to -1,0 or 1 to signify in // which directions movement should occur int vertfac = 0, horizfac = 0; // reset to tile-based at the start of a movestring if(moveofs == 0) { movemult = 16; } else if (moveofs >= movestr.length()) { movecode = 0; framect = 0; // [Rafael, the Esper] } if(movestr==null || movestr.trim().isEmpty() || moveofs >= movestr.length()) // last if by [Rafael, the Esper] return; while (moveofs < movestr.length() && ( // [Rafael, the Esper] (movestr.charAt(moveofs) >= '0' && movestr.charAt(moveofs) <= '9') || movestr.charAt(moveofs) == ' ' || movestr.charAt(moveofs) == '-')) moveofs++; boolean done = false; int found_move = 0; // number of LRUD letters we found while(!done && found_move < 2 && moveofs < movestr.length()) { switch(Character.toUpperCase(movestr.charAt(moveofs))) { case 'L': if(found_move==0 && face != WEST) setface(WEST); moveofs++; horizfac = -1; found_move++; break; case 'R': if(found_move==0 && face != EAST) setface(EAST); moveofs++; horizfac = 1; found_move++; break; case 'U': if(found_move==0 && face != NORTH) setface(NORTH); moveofs++; vertfac = -1; found_move++; break; case 'D': if(found_move==0 && face != SOUTH) setface(SOUTH); moveofs++; vertfac = 1; found_move++; break; default: done = true; } } if(!(moveofs < movestr.length())) return; if(found_move!=0) { arg = get_int(movestr, moveofs); // we've already set facing, don't do it again set_waypoint_relative(horizfac*arg*movemult, vertfac*arg*movemult, false); } else { // no directions, check other possible letters: switch(Character.toUpperCase(movestr.charAt(moveofs))) { case 'S': moveofs++; setspeed(get_int(movestr, moveofs)); break; case 'W': moveofs++; delay = systemtime + get_int(movestr, moveofs); break; case 'F': moveofs++; setface(vc2me[get_int(movestr, moveofs)]); break; case 'B': moveofs = 0; break; case 'X': moveofs++; arg = get_int(movestr, moveofs); set_waypoint(arg*16, gety()); break; case 'Y': moveofs++; arg = get_int(movestr, moveofs); set_waypoint(getx(), arg*16); break; case 'Z': moveofs++; specframe = get_int(movestr, moveofs); break; case 'P': movemult = 1; moveofs++; break; case 'T': movemult = 16; moveofs++; break; case 'H': case '0': movemult = 0; moveofs = 0; movecode = 0; framect = 0; return; default: System.err.println("do_movescript(), unidentify movescript command"); } } } public int get_int(String s, int offset) { int digit_size = 0; //[Rafael, the Esper] if(Character.isDigit(s.charAt(offset))) { digit_size++; if(offset+1 < s.length() && Character.isDigit(s.charAt(offset+1))) { digit_size++; if(offset+2 < s.length() && Character.isDigit(s.charAt(offset+2))) { digit_size++; if(offset+3 < s.length() && Character.isDigit(s.charAt(offset+3))) { digit_size++; } } } } int ret = Integer.parseInt(movestr.substring(moveofs, moveofs+digit_size).trim()); moveofs+=digit_size; return ret; } public void set_chr(String fname) { chr = new CHR(fname); // [Rafael, the Esper] RequestCHR(fname); specframe = 0; framect = 0; frame = 0; } public void draw(VImage dest) { if (!visible) return; // if we're idle, reset the framect //if ((follow==null && ready()) || (follow!=null && leaderidle())) //framect = 0; // Commented by Rafael, the Esper (Why is this useful?) if (specframe > 0) frame = specframe; else { if (follow==null) { if (ready() || framect == 0) { // framect condition by Rafael, the Esper frame = chr.idle[face]; } else { frame = chr.getFrame(face, framect); } } else { if (leaderidle()) { frame = chr.idle[face]; } else { frame = chr.getFrame(face, framect); } } } int zx = getx() - xwin, zy = gety() - ywin; // Adapted by [Rafael, the Esper] if(current_map != null && current_map.getHorizontalWrapable()) zx = (zx + (current_map.getWidth()<<4)) % (current_map.getWidth()<<4); if(current_map != null && current_map.getVerticalWrapable()) zy = (zy + (current_map.getHeight()<<4)) % (current_map.getHeight()<<4); //System.out.println(this.chrname + " " + zx + "," + zy + " " + getx() + "," + gety() + " " + xwin + "," + ywin); if (hookrender != null && !hookrender.isEmpty()) { event_entity = index; callfunction(hookrender); return; } if (chr != null) chr.render(zx, zy, frame, dest); //if(this.y < 1600 && (this.chrname.contains("ent") || this.chrname.contains("ENT"))) //System.out.println("RBP " + ready() + " " + frame + ", " + this.framect); } public void setWanderZone() { clear_stalk(); set_waypoint(getx(), gety()); movecode = 1; } public void setWanderBox(int x1, int y1, int x2, int y2) { clear_stalk(); set_waypoint(getx(), gety()); wx1 = x1; wy1 = y1; wx2 = x2; wy2 = y2; movecode = 2; } public void setMoveScript(String s) { clear_stalk(); set_waypoint(getx(), gety()); movestr = s; moveofs = 0; movecode = 3; } public void setWanderDelay(int n) { wdelay = n; } public void setMotionless() { clear_stalk(); set_waypoint(getx(), gety()); movecode = 0; delay = 0; } public int getHotX() { return this.chr.hx; } public int getHotY() { return this.chr.hy; } public int getHotW() { return this.chr.hw; } public int getHotH() { return this.chr.hh; } }