diff options
Diffstat (limited to 'engines/sci/engine/kmovement.c')
-rw-r--r-- | engines/sci/engine/kmovement.c | 580 |
1 files changed, 0 insertions, 580 deletions
diff --git a/engines/sci/engine/kmovement.c b/engines/sci/engine/kmovement.c deleted file mode 100644 index 1234a9028a..0000000000 --- a/engines/sci/engine/kmovement.c +++ /dev/null @@ -1,580 +0,0 @@ -/*************************************************************************** - kmovement.c Copyright (C) 2001 Christoph Reichenbach - - - This program may be modified and copied freely according to the terms of - the GNU general public license (GPL), as long as the above copyright - notice and the licensing information contained herein are preserved. - - Please refer to www.gnu.org for licensing details. - - This work is provided AS IS, without warranty of any kind, expressed or - implied, including but not limited to the warranties of merchantibility, - noninfringement, and fitness for a specific purpose. The author will not - be held liable for any damage caused by this work or derivatives of it. - - By using this source code, you agree to the licensing terms as stated - above. - - - Please contact the maintainer for bug reports or inquiries. - - Current Maintainer: - - Christoph Reichenbach (CR) <jameson@linuxgames.com> - -***************************************************************************/ - -#include "sci/include/sciresource.h" -#include "sci/include/engine.h" - -/* -Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to (dx,dy), with gravity gy. -The gravity is assumed to be non-negative. - -If this was ordinary continuous physics, we would compute the desired (floating point!) -velocity vector (vx,vy) as follows, under the assumption that vx and vy are linearly correlated -by some constant factor c, i.e. vy = c * vx: - dx = t * vx - dy = t * vy + gy * t^2 / 2 -=> dy = c * dx + gy * (dx/vx)^2 / 2 -=> |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) ) -Here, the sign of vx must be chosen equal to the sign of dx, obviously. - -Clearly, this square root only makes sense in our context if the denominator is positive, -or equivalently, (dy - c * dx) must be positive. For simplicity and by symmetry -along the x-axis, we assume dx to be positive for all computations, and only adjust for -its sign in the end. Switching the sign of c appropriately, we set tmp := (dy + c * dx) -and compute c so that this term becomes positive. - -Remark #1: If the jump is straight up, i.e. dx == 0, then we should not assume the above -linear correlation vy = c * vx of the velocities (as vx will be 0, but vy shouldn't be, -unless we drop). - - -Remark #2: We are actually in a discrete setup. The motion is computed iteratively: each iteration, -we add vx and vy to the position, then add gy to vy. So the real formula is the following -(where t is ideally close to an int): - - dx = t * vx - dy = t * vy + gy * t*(t-1) / 2 - -But the solution resulting from that is a lot more complicated, so we use the above approximation instead. - -Still, what we compute in the end is of course not a real velocity anymore, but an integer approximation, -used in an iterative stepping algorithm -*/ -reg_t -kSetJump(state_t *s, int funct_nr, int argc, reg_t *argv) -{ - // Input data - reg_t object = argv[0]; - int dx = SKPV(1); - int dy = SKPV(2); - int gy = SKPV(3); - - // Derived data - int c; - int tmp; - int vx = 0; // x velocity - int vy = 0; // y velocity - - int dxWasNegative = (dx < 0); - dx = abs(dx); - - assert(gy >= 0); - - if (dx == 0) { - // Upward jump. Value of c doesn't really matter - c = 1; - } else { - // Compute a suitable value for c respectively tmp. - // The important thing to consider here is that we want the resulting - // *discrete* x/y velocities to be not-too-big integers, for a smooth - // curve (i.e. we could just set vx=dx, vy=dy, and be done, but that - // is hardly what you would call a parabolic jump, would ya? ;-). - // - // So, we make sure that 2.0*tmp will be bigger than dx (that way, - // we ensure vx will be less than sqrt(gy * dx)). - if (dx + dy < 0) { - // dy is negative and |dy| > |dx| - c = (2*abs(dy)) / dx; - //tmp = abs(dy); // ALMOST the resulting value, except for obvious rounding issues - } else { - // dy is either positive, or |dy| <= |dx| - c = (dx*3/2 - dy) / dx; - - // We force c to be strictly positive - if (c < 1) - c = 1; - - //tmp = dx*3/2; // ALMOST the resulting value, except for obvious rounding issues - - // FIXME: Where is the 3 coming from? Maybe they hard/coded, by "accident", that usually gy=3 ? - // Then this choice of will make t equal to roughly sqrt(dx) - } - } - // POST: c >= 1 - tmp = c * dx + dy; - // POST: (dx != 0) ==> abs(tmp) > abs(dx) - // POST: (dx != 0) ==> abs(tmp) ~>=~ abs(dy) - - - SCIkdebug(SCIkBRESEN, "c: %d, tmp: %d\n", c, tmp); - - // Compute x step - if (tmp != 0) - vx = (int)(dx * sqrt(gy / (2.0 * tmp))); - else - vx = 0; - - // Restore the left/right direction: dx and vx should have the same sign. - if (dxWasNegative) - vx = -vx; - - if ((dy < 0) && (vx == 0)) { - // Special case: If this was a jump (almost) straight upward, i.e. dy < 0 (upward), - // and vx == 0 (i.e. no horizontal movement, at least not after rounding), then we - // compute vy directly. - // For this, we drop the assumption on the linear correlation of vx and vy (obviously). - - // FIXME: This choice of vy makes t roughly (2+sqrt(2))/gy * sqrt(dy); - // so if gy==3, then t is roughly sqrt(dy)... - vy = (int)sqrt(gy * abs(2 * dy)) + 1; - } else { - // As stated above, the vertical direction is correlated to the horizontal by the - // (non-zero) factor c. - // Strictly speaking, we should probably be using the value of vx *before* rounding - // it to an integer... Ah well - vy = c * vx; - } - - // Always force vy to be upwards - vy = -abs(vy); - - SCIkdebug(SCIkBRESEN, "SetJump for object at "PREG"\n", PRINT_REG(object)); - SCIkdebug(SCIkBRESEN, "xStep: %d, yStep: %d\n", vx, vy); - - PUT_SEL32V(object, xStep, vx); - PUT_SEL32V(object, yStep, vy); - - return s->r_acc; -} - -#define _K_BRESEN_AXIS_X 0 -#define _K_BRESEN_AXIS_Y 1 - -void -initialize_bresen(state_t *s, int funct_nr, int argc, reg_t *argv, reg_t mover, int step_factor, - int deltax, int deltay) -{ - reg_t client = GET_SEL32(mover, client); - int stepx = GET_SEL32SV(client, xStep) * step_factor; - int stepy = GET_SEL32SV(client, yStep) * step_factor; - int numsteps_x = stepx? (abs(deltax) + stepx-1) / stepx : 0; - int numsteps_y = stepy? (abs(deltay) + stepy-1) / stepy : 0; - int bdi, i1; - int numsteps; - int deltax_step; - int deltay_step; - - if (numsteps_x > numsteps_y) { - numsteps = numsteps_x; - deltax_step = (deltax < 0)? -stepx : stepx; - deltay_step = numsteps? deltay / numsteps : deltay; - } else { /* numsteps_x <= numsteps_y */ - numsteps = numsteps_y; - deltay_step = (deltay < 0)? -stepy : stepy; - deltax_step = numsteps? deltax / numsteps : deltax; - } - - /* if (abs(deltax) > abs(deltay)) {*/ /* Bresenham on y */ - if (numsteps_y < numsteps_x) { - - PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_Y); - PUT_SEL32V(mover, b_incr, (deltay < 0)? -1 : 1); - /* - i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step); - bdi = -abs(deltax); - */ - i1 = 2*(abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step); - bdi = -abs(deltax); - - } else { /* Bresenham on x */ - - PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_X); - PUT_SEL32V(mover, b_incr, (deltax < 0)? -1 : 1); - /* - i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step); - bdi = -abs(deltay); - */ - i1 = 2*(abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step); - bdi = -abs(deltay); - - } - - PUT_SEL32V(mover, dx, deltax_step); - PUT_SEL32V(mover, dy, deltay_step); - - SCIkdebug(SCIkBRESEN, "Init bresen for mover "PREG": d=(%d,%d)\n", PRINT_REG(mover), deltax, deltay); - SCIkdebug(SCIkBRESEN, " steps=%d, mv=(%d, %d), i1= %d, i2=%d\n", - numsteps, deltax_step, deltay_step, i1, bdi*2); - -/* PUT_SEL32V(mover, b_movCnt, numsteps); *//* Needed for HQ1/Ogre? */ - PUT_SEL32V(mover, b_di, bdi); - PUT_SEL32V(mover, b_i1, i1); - PUT_SEL32V(mover, b_i2, bdi * 2); - -} - -reg_t -kInitBresen(state_t *s, int funct_nr, int argc, reg_t *argv) -{ - reg_t mover = argv[0]; - reg_t client = GET_SEL32(mover, client); - - int deltax = GET_SEL32SV(mover, x) - GET_SEL32SV(client, x); - int deltay = GET_SEL32SV(mover, y) - GET_SEL32SV(client, y); - - initialize_bresen(s, funct_nr, argc, argv, mover, KP_UINT(KP_ALT(1, make_reg(0, 1))), deltax, deltay); - - return s->r_acc; -} - - -#define MOVING_ON_X (((axis == _K_BRESEN_AXIS_X)&&bi1) || dx) -#define MOVING_ON_Y (((axis == _K_BRESEN_AXIS_Y)&&bi1) || dy) - -static enum { - IGNORE_MOVECNT, - INCREMENT_MOVECNT, - UNINITIALIZED -} handle_movecnt = UNINITIALIZED; - -int parse_reg_t(state_t *s, char *str, reg_t *dest); /* In scriptconsole.c */ - -static int -checksum_bytes(byte *data, int size) -{ - int result = 0; - int i; - - for (i = 0; i < size; i++) - { - result += *data; - data++; - } - - return result; -} - -static void -bresenham_autodetect(state_t *s) -{ - reg_t motion_class; - - if (!parse_reg_t(s, "?Motion", &motion_class)) - { - object_t *obj = obj_get(s, motion_class); - reg_t fptr; - byte *buf; - - if (obj == NULL) - { - SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); - handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ - return; - } - - if (lookup_selector(s, motion_class, s->selector_map.doit, NULL, &fptr) != SELECTOR_METHOD) - { - SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); - handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ - return; - } - - buf = s->seg_manager.heap[fptr.segment]->data.script.buf + fptr.offset; - handle_movecnt = (SCI_VERSION_MAJOR(s->version) == 0 || - checksum_bytes(buf, 8) == 0x216) ? INCREMENT_MOVECNT : IGNORE_MOVECNT; - sciprintf("b-moveCnt action based on checksum: %s\n", handle_movecnt == IGNORE_MOVECNT ? - "ignore" : "increment"); - } else - { - SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); - handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ - } -} - -reg_t -kDoBresen(state_t *s, int funct_nr, int argc, reg_t *argv) -{ - reg_t mover = argv[0]; - reg_t client = GET_SEL32(mover, client); - - int x = GET_SEL32SV(client, x); - int y = GET_SEL32SV(client, y); - int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis; - word signal = GET_SEL32V(client, signal); - int completed = 0; - int max_movcnt = GET_SEL32V(client, moveSpeed); - - if (SCI_VERSION_MAJOR(s->version)>0) - signal&=~_K_VIEW_SIG_FLAG_HIT_OBSTACLE; - - if (handle_movecnt == UNINITIALIZED) - bresenham_autodetect(s); - - PUT_SEL32(client, signal, make_reg(0, signal)); /* This is a NOP for SCI0 */ - oldx = x; - oldy = y; - destx = GET_SEL32SV(mover, x); - desty = GET_SEL32SV(mover, y); - dx = GET_SEL32SV(mover, dx); - dy = GET_SEL32SV(mover, dy); - bdi = GET_SEL32SV(mover, b_di); - bi1 = GET_SEL32SV(mover, b_i1); - bi2 = GET_SEL32SV(mover, b_i2); - movcnt = GET_SEL32V(mover, b_movCnt); - bdelta = GET_SEL32SV(mover, b_incr); - axis = GET_SEL32SV(mover, b_xAxis); - -// sciprintf("movecnt %d, move speed %d\n", movcnt, max_movcnt); - - if (handle_movecnt) - { - if (max_movcnt > movcnt) - { - ++movcnt; - PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */ - return NULL_REG; - } - else - { - movcnt = 0; - PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */ - } - } - - if ((bdi += bi1) > 0) { - bdi += bi2; - - if (axis == _K_BRESEN_AXIS_X) - dx += bdelta; - else - dy += bdelta; - } - - PUT_SEL32V(mover, b_di, bdi); - - x += dx; - y += dy; - - if ((MOVING_ON_X - && (((x < destx) && (oldx >= destx)) /* Moving left, exceeded? */ - || - ((x > destx) && (oldx <= destx)) /* Moving right, exceeded? */ - || - ((x == destx) && (abs(dx) > abs(dy))) /* Moving fast, reached? */ - /* Treat this last case specially- when doing sub-pixel movements - ** on the other axis, we could still be far away from the destination */ - ) - ) - || (MOVING_ON_Y - && (((y < desty) && (oldy >= desty)) /* Moving upwards, exceeded? */ - || - ((y > desty) && (oldy <= desty)) /* Moving downwards, exceeded? */ - || - ((y == desty) && (abs(dy) >= abs(dx))) /* Moving fast, reached? */ - ) - ) - ) - /* Whew... in short: If we have reached or passed our target position */ - { - x = destx; - y = desty; - completed = 1; - - SCIkdebug(SCIkBRESEN, "Finished mover "PREG"\n", PRINT_REG(mover)); - } - - - PUT_SEL32V(client, x, x); - PUT_SEL32V(client, y, y); - - SCIkdebug(SCIkBRESEN, "New data: (x,y)=(%d,%d), di=%d\n", x, y, bdi); - - if (s->version >= SCI_VERSION_FTU_INVERSE_CANBEHERE) - invoke_selector(INV_SEL(client, cantBeHere, 0), 0); else - invoke_selector(INV_SEL(client, canBeHere, 0), 0); - - s->r_acc = not_register(s, s->r_acc); - - if (!s->r_acc.offset) { /* Contains the return value */ - - signal = GET_SEL32V(client, signal); - - PUT_SEL32V(client, x, oldx); - PUT_SEL32V(client, y, oldy); - - PUT_SEL32V(client, signal, (signal | _K_VIEW_SIG_FLAG_HIT_OBSTACLE)); - - SCIkdebug(SCIkBRESEN, "Finished mover "PREG" by collision\n", PRINT_REG(mover)); - completed = 1; - } - - if (SCI_VERSION_MAJOR(s->version)>0) - if (completed) - invoke_selector(INV_SEL(mover, moveDone, 0), 0); - - return make_reg(0, completed); -} - -extern void -_k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr, - int argc, reg_t *argv); -/* From kgraphics.c, used as alternative looper */ - -int -is_heap_object(state_t *s, reg_t pos); -/* From kscripts.c */ - -extern int -get_angle(int xrel, int yrel); -/* from kmath.c, used for calculating angles */ - - -reg_t -kDoAvoider(state_t *s, int funct_nr, int argc, reg_t *argv) -{ - reg_t avoider = argv[0]; - reg_t client, looper, mover; - int angle; - int dx, dy; - int destx, desty; - - - s->r_acc = make_reg(0, -1); - - if (!is_heap_object(s, avoider)) { - SCIkwarn(SCIkWARNING, "DoAvoider() where avoider "PREG" is not an object\n", PRINT_REG(avoider)); - return NULL_REG; - } - - client = GET_SEL32(avoider, client); - - if (!is_heap_object(s, client)) { - SCIkwarn(SCIkWARNING, "DoAvoider() where client "PREG" is not an object\n", PRINT_REG(client)); - return NULL_REG; - } - - looper = GET_SEL32(client, looper); - - mover = GET_SEL32(client, mover); - - if (!is_heap_object(s, mover)) { - if (mover.segment) { - SCIkwarn(SCIkWARNING, "DoAvoider() where mover "PREG" is not an object\n", PRINT_REG(mover)); - } - return s->r_acc; - } - - destx = GET_SEL32V(mover, x); - desty = GET_SEL32V(mover, y); - - SCIkdebug(SCIkBRESEN, "Doing avoider %04x (dest=%d,%d)\n", avoider, destx, desty); - - if (invoke_selector(INV_SEL(mover, doit, 1) , 0)) { - SCIkwarn(SCIkERROR, "Mover "PREG" of avoider "PREG - " doesn't have a doit() funcselector\n", - PRINT_REG(mover), PRINT_REG(avoider)); - return NULL_REG; - } - - mover = GET_SEL32(client, mover); - if (!mover.segment) /* Mover has been disposed? */ - return s->r_acc; /* Return gracefully. */ - - if (invoke_selector(INV_SEL(client, isBlocked, 1) , 0)) { - SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't" - " have an isBlocked() funcselector\n", PRINT_REG(client), PRINT_REG(avoider)); - return NULL_REG; - } - - dx = destx - GET_SEL32V(client, x); - dy = desty - GET_SEL32V(client, y); - angle = get_angle(dx, dy); - - SCIkdebug(SCIkBRESEN, "Movement (%d,%d), angle %d is %sblocked\n", - dx, dy, angle, (s->r_acc.offset)? " ": "not "); - - if (s->r_acc.offset) { /* isBlocked() returned non-zero */ - int rotation = (rand() & 1)? 45 : (360-45); /* Clockwise/counterclockwise */ - int oldx = GET_SEL32V(client, x); - int oldy = GET_SEL32V(client, y); - int xstep = GET_SEL32V(client, xStep); - int ystep = GET_SEL32V(client, yStep); - int moves; - - SCIkdebug(SCIkBRESEN, " avoider "PREG"\n", PRINT_REG(avoider)); - - for (moves = 0; moves < 8; moves++) { - int move_x = (int) (sin(angle * PI / 180.0) * (xstep)); - int move_y = (int) (-cos(angle * PI / 180.0) * (ystep)); - - PUT_SEL32V(client, x, oldx + move_x); - PUT_SEL32V(client, y, oldy + move_y); - - SCIkdebug(SCIkBRESEN, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)\n", - oldx, oldy, angle, move_x, move_y); - - if (invoke_selector(INV_SEL(client, canBeHere, 1) , 0)) { - SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't" - " have a canBeHere() funcselector\n", - PRINT_REG(client), PRINT_REG(avoider)); - return NULL_REG; - } - - PUT_SEL32V(client, x, oldx); - PUT_SEL32V(client, y, oldy); - - if (s->r_acc.offset) { /* We can be here */ - SCIkdebug(SCIkBRESEN, "Success\n"); - PUT_SEL32V(client, heading, angle); - - return make_reg(0, angle); - } - - angle += rotation; - - if (angle > 360) - angle -= 360; - } - - SCIkwarn(SCIkWARNING, "DoAvoider failed for avoider "PREG"\n", - PRINT_REG(avoider)); - - } else { - int heading = GET_SEL32V(client, heading); - - if (heading == -1) - return s->r_acc; /* No change */ - - PUT_SEL32V(client, heading, angle); - - s->r_acc = make_reg(0, angle); - - if (looper.segment) { - if (invoke_selector(INV_SEL(looper, doit, 1), 2, angle, client)) { - SCIkwarn(SCIkERROR, "Looper "PREG" of avoider "PREG" doesn't" - " have a doit() funcselector\n", - PRINT_REG(looper), PRINT_REG(avoider)); - } else return s->r_acc; - } else - /* No looper? Fall back to DirLoop */ - - _k_dirloop(client, (word)angle, s, funct_nr, argc, argv); - } - - return s->r_acc; -} - |