/* Sarien - A Sierra AGI resource interpreter engine * Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka * * $Id: checks.c,v 1.17 2001/11/03 20:16:10 cmatsuoka Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; see docs/COPYING for further details. */ #include "sarien.h" #include "agi.h" static int dir_table_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 }; static int dir_table_y[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 }; static int check_position (struct vt_entry *v) { /* _D ("check position @ %d, %d", v->x_pos, v->y_pos); */ if ( v->x_pos < 0 || v->x_pos + v->x_size > _WIDTH || v->y_pos + v->y_size + 1 < 0 || /* v->y_pos < v->y_size || -- not in AGI 2.917 */ v->y_pos >= _HEIGHT || ((~v->flags & IGNORE_HORIZON) && v->y_pos <= game.horizon)) { /* _D (_D_WARN "check position failed: x=%d, y=%d, h=%d, w=%d", v->x_pos, v->y_pos, v->x_size, v->y_size); */ return 0; } return 1; } /** * Checks if there's another object on the way */ static int check_clutter (struct vt_entry *v) { struct vt_entry *u; if (v->flags & IGNORE_OBJECTS) return 0; for_each_vt_entry (u) { if ((u->flags & (ANIMATED|DRAWN)) != (ANIMATED|DRAWN)) continue; if (u->flags & IGNORE_OBJECTS) continue; if (v->entry == u->entry) continue; if (v->x_pos + v->x_size < u->x_pos) continue; if (v->x_pos > u->x_pos + u->x_size) continue; if (v->y_pos == u->y_pos) return 1; if (v->y_pos > u->y_pos) { if (v->y_pos2 < u->y_pos2) return 1; } if (v->y_pos >= u->y_pos) continue; if (v->y_pos <= u->y_pos) continue; return 1; } return 0; } static int check_priority (struct vt_entry *v) { int i, trigger, water, pass, pri; UINT8 *p0; if (~v->flags & FIXED_PRIORITY) { /* Priority bands */ v->priority = game.pri_table[v->y_pos]; } trigger = 0; water = 0; pass = 1; if (v->priority == 0x0f) goto _check_ego; water = 1; p0 = &game.sbuf[v->x_pos + v->y_pos * _WIDTH]; for (i = 0; i < v->x_size; i++, p0++) { pri = *p0 >> 4; if (pri == 0) { /* unconditional black. no go at all! */ pass = 0; break; } if (pri == 3) /* water surface */ continue; water = 0; if (pri == 1) { /* conditional blue */ if (v->flags & IGNORE_BLOCKS) continue; _D (_D_WARN "Blocks observed!"); pass = 0; break; } if (pri == 2) { /* trigger */ _D (_D_WARN "stepped on trigger"); trigger = 1; } } if (pass) { if (!water && v->flags & ON_WATER) pass = 0; if (water && v->flags & ON_LAND) pass = 0; } _check_ego: if (v->entry == 0) { setflag (F_ego_touched_p2, trigger ? TRUE : FALSE); setflag (F_ego_water, water ? TRUE : FALSE); } return pass; } /* * Public functions */ /** * */ void update_position () { struct vt_entry *v; int x, y, old_x, old_y, dir, step, border; game.vars[V_border_code] = 0; game.vars[V_border_touch_ego] = 0; game.vars[V_border_touch_obj] = 0; for_each_vt_entry (v) { if ((v->flags & (ANIMATED|UPDATE|DRAWN)) != (ANIMATED|UPDATE|DRAWN)) { continue; } if (v->step_time_count != 0) { if (--v->step_time_count != 0) continue; } v->step_time_count = v->step_time; x = old_x = v->x_pos; y = old_y = v->y_pos; /* If object has moved, update its position */ if (~v->flags & UPDATE_POS) { dir = v->direction; step = v->step_size; x += step * dir_table_x[dir]; y += step * dir_table_y[dir]; } /* Now check if it touched the borders */ border = 0; /* Check left/right borders */ if (x < 0) { x = 0; border = 4; } else if (v->entry == 0 && x == 0 && v->flags & ADJ_EGO_XY) { /* Extra test to walk west clicking the mouse */ x = 0; border = 4; } else if (x + v->x_size > _WIDTH) { x = 160 - v->x_size; border = 2; } /* Check top/bottom borders. */ if (y - v->y_size + 1 < 0) { y = v->y_size - 1; border = 1; } else if (y > _HEIGHT - 1) { y = _HEIGHT - 1; border = 3; } else if ((~v->flags & IGNORE_HORIZON) && y <= game.horizon) { y++; border = 1; } /* Test new position. rollback if test fails */ v->x_pos = x; v->y_pos = y; if (check_clutter (v) || !check_priority (v)) { v->x_pos = old_x; v->y_pos = old_y; border = 0; fix_position (v->entry); } if (border != 0) { if_is_ego_view (v) { game.vars[V_border_touch_ego] = border; } else { game.vars[V_border_code] = v->entry; game.vars[V_border_touch_obj] = border; } if (v->motion == MOTION_MOVE_OBJ) { in_destination (v); } } v->flags &= ~UPDATE_POS; } } /** * Adjust position of a sprite * This function adjusts the position of a sprite moving it until * certain criteria is matched. According to priority and control line * data, a sprite may not always appear at the location we specified. * This behaviour is also known as the "Budin-Sonneveld effect". * * @param n view table entry number */ void fix_position (int n) { struct vt_entry *v = &game.view_table[n]; int count, dir, tries; /* _D (_D_WARN "adjusting view table entry #%d (%d,%d)", n, v->x_pos, v->y_pos); */ /* test horizon */ dir = 0; count = tries = 1; while (!check_position(v) || check_clutter(v) || !check_priority(v)) { switch (dir) { case 0: /* west */ v->x_pos--; if (--count) continue; dir = 1; break; case 1: /* south */ v->y_pos++; if (--count) continue; dir = 2; tries++; break; case 2: /* east */ v->x_pos++; if (--count) continue; dir = 3; break; case 3: /* north */ v->y_pos--; if (--count) continue; dir = 0; tries++; break; } count = tries; } _D (_D_WARN "line_min_print = %d", game.line_min_print); /* _D (_D_WARN "view table entry #%d position adjusted to (%d,%d)", n, v->x_pos, v->y_pos); */ } /* end: checks.c */