diff options
Diffstat (limited to 'engines/agos/verb.cpp')
-rw-r--r-- | engines/agos/verb.cpp | 906 |
1 files changed, 906 insertions, 0 deletions
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp new file mode 100644 index 0000000000..014ca36df7 --- /dev/null +++ b/engines/agos/verb.cpp @@ -0,0 +1,906 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// Verb and hitarea handling +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +static const char *const russian_verb_names[] = { + "Ietj _", + "Qnotrft< pa", + "Nt_r[t<", + "Ecjdat<", + "Q=fst<", + "C^]t<", + "Ha_r[t<", + "Isqom<^ocat<", + "Docorjt<", + "Qp]t<", + "Neft<", + "Eat<" +}; + +static const char *const hebrew_verb_names[] = { + "LJ @L", + "DQZKL RL", + "TZG", + "DFF", + "@KEL", + "DXM", + "QBEX", + "DYZNY", + "CAX @L", + "DQX", + "LAY", + "ZO" +}; + +static const char *const spanish_verb_names[] = { + "Caminar", + "Mirar", + "Abrir", + "Mover", + "Consumir", + "Coger", + "Cerrar", + "Usar", + "Hablar", + "Quitar", + "Llevar", + "Dar" +}; + +static const char *const italian_verb_names[] = { + "Vai verso", + "Osserva", + "Apri", + "Sposta", + "Mangia", + "Raccogli", + "Chiudi", + "Usa", + "Parla a", + "Togli", + "Indossa", + "Dai" +}; + +static const char *const french_verb_names[] = { + "Aller vers", + "Regarder", + "Ouvrir", + "D/placer", + "Consommer", + "Prendre", + "Fermer", + "Utiliser", + "Parler ;", + "Enlever", + "Mettre", + "Donner" +}; + +static const char *const german_verb_names[] = { + "Gehe zu", + "Schau an", + ";ffne", + "Bewege", + "Verzehre", + "Nimm", + "Schlie+e", + "Benutze", + "Rede mit", + "Entferne", + "Trage", + "Gib" +}; + +static const char *const english_verb_names[] = { + "Walk to", + "Look at", + "Open", + "Move", + "Consume", + "Pick up", + "Close", + "Use", + "Talk to", + "Remove", + "Wear", + "Give" +}; + +static const char *const russian_verb_prep_names[] = { + "", "", "", "", + "", "", "", "s yfn?", + "", "", "", "_onu ?" +}; + +static const char *const hebrew_verb_prep_names[] = { + "", "", "", "", + "", "", "", "RM ND ?", + "", "", "", "LNI ?" +}; + +static const char *const spanish_verb_prep_names[] = { + "", "", "", "", + "", "", "", "^con qu/?", + "", "", "", "^a qui/n?" +}; + +static const char *const italian_verb_prep_names[] = { + "", "", "", "", + "", "", "", "con cosa ?", + "", "", "", "a chi ?" +}; + +static const char *const french_verb_prep_names[] = { + "", "", "", "", + "", "", "", "avec quoi ?", + "", "", "", "; qui ?" +}; + +static const char *const german_verb_prep_names[] = { + "", "", "", "", + "", "", "", "mit was ?", + "", "", "", "zu wem ?" +}; + +static const char *const english_verb_prep_names[] = { + "", "", "", "", + "", "", "", "with what ?", + "", "", "", "to whom ?" +}; + +void SimonEngine::clearName() { + HitArea *last; + HitArea *ha; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + stopAnimateSimon2(2, 6); + _lastNameOn = NULL; + _animatePointer = 0; + _mouseAnim = 1; + return; + } + + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79)) { + sendSync(202); + _lastNameOn = NULL; + return; + } + } + + last = _currentVerbBox; + + if (last == _lastVerbOn) + return; + + resetNameWindow(); + _lastVerbOn = last; + + if (last != NULL && (ha = findBox(200)) && (ha->flags & kBFBoxDead) && !(last->flags & kBFBoxDead)) + printVerbOf(last->id); +} + +void SimonEngine::printVerbOf(uint hitarea_id) { + const char *txt; + const char * const *verb_names; + const char * const *verb_prep_names; + + hitarea_id -= 101; + + if (_showPreposition) { + switch (_language) { + case Common::RU_RUS: + verb_prep_names = russian_verb_prep_names; + break; + case Common::HB_ISR: + verb_prep_names = hebrew_verb_prep_names; + break; + case Common::ES_ESP: + verb_prep_names = spanish_verb_prep_names; + break; + case Common::IT_ITA: + verb_prep_names = italian_verb_prep_names; + break; + case Common::FR_FRA: + verb_prep_names = french_verb_prep_names; + break; + case Common::DE_DEU: + verb_prep_names = german_verb_prep_names; + break; + default: + verb_prep_names = english_verb_prep_names; + break; + } + CHECK_BOUNDS(hitarea_id, english_verb_prep_names); + txt = verb_prep_names[hitarea_id]; + } else { + switch (_language) { + case Common::RU_RUS: + verb_names = russian_verb_names; + break; + case Common::HB_ISR: + verb_names = hebrew_verb_names; + break; + case Common::ES_ESP: + verb_names = spanish_verb_names; + break; + case Common::IT_ITA: + verb_names = italian_verb_names; + break; + case Common::FR_FRA: + verb_names = french_verb_names; + break; + case Common::DE_DEU: + verb_names = german_verb_names; + break; + default: + verb_names = english_verb_names; + break; + } + CHECK_BOUNDS(hitarea_id, english_verb_names); + txt = verb_names[hitarea_id]; + } + showActionString((const byte *)txt); +} + +void SimonEngine::showActionString(const byte *string) { + WindowBlock *window; + uint x; + + window = _windowArray[1]; + if (window == NULL || window->text_color == 0) + return; + + // Arisme : hack for long strings in the French version + if ((strlen((const char*)string) - 1) <= 53) + x = (53 - (strlen((const char *)string) - 1)) * 3; + else + x = 0; + + window->textColumn = x / 8; + window->textColumnOffset = x & 7; + + for (; *string; string++) + windowPutChar(window, *string); +} + +void SimonEngine::handleVerbClicked(uint verb) { + Subroutine *sub; + int result; + + _objectItem = _hitAreaObjectItem; + if (_objectItem == _dummyItem2) { + _objectItem = me(); + } + if (_objectItem == _dummyItem3) { + _objectItem = derefItem(me()->parent); + } + + _subjectItem = _hitAreaSubjectItem; + if (_subjectItem == _dummyItem2) { + _subjectItem = me(); + } + if (_subjectItem == _dummyItem3) { + _subjectItem = derefItem(me()->parent); + } + + if (_subjectItem) { + _scriptNoun1 = _subjectItem->noun; + _scriptAdj1 = _subjectItem->adjective; + } else { + _scriptNoun1 = -1; + _scriptAdj1 = -1; + } + + if (_objectItem) { + _scriptNoun2 = _objectItem->noun; + _scriptAdj2 = _objectItem->adjective; + } else { + _scriptNoun2 = -1; + _scriptAdj2 = -1; + } + + _scriptVerb = _verbHitArea; + + sub = getSubroutineByID(0); + if (sub == NULL) + return; + + result = startSubroutine(sub); + if (result == -1) + showMessageFormat("I don't understand"); + + _runScriptReturn1 = false; + + sub = getSubroutineByID(100); + if (sub) + startSubroutine(sub); + + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) + _runScriptReturn1 = false; + + permitInput(); +} + +void SimonEngine::resetNameWindow() { + WindowBlock *window; + + if (getGameType() == GType_SIMON2 && getBitFlag(79)) + return; + + window = _windowArray[1]; + if (window != NULL && window->text_color != 0) + clearWindow(window); + + _lastNameOn = NULL; + _lastVerbOn = NULL; +} + +HitArea *SimonEngine::findBox(uint hitarea_id) { + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + + do { + if (ha->id == hitarea_id && ha->flags != 0) + return ha; + } while (ha++, --count); + return NULL; +} + +HitArea *SimonEngine::findEmptyHitArea() { + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + + do { + if (ha->flags == 0) + return ha; + } while (ha++, --count); + return NULL; +} + +void SimonEngine::delete_hitarea_by_index(uint index) { + CHECK_BOUNDS(index, _hitAreas); + _hitAreas[index].flags = 0; +} + +void SimonEngine::enableBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) + ha->flags &= ~kBFBoxDead; +} + +void SimonEngine::disableBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + ha->flags |= kBFBoxDead; + ha->flags &= ~kBFBoxSelected; + if (hitarea == 102) + resetVerbs(); + } +} + +void SimonEngine::moveBox(uint hitarea, int x, int y) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + ha->x += x; + ha->y += y; + } else { + ha->x = x; + ha->y = y; + } + } +} + +void SimonEngine::undefineBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + ha->flags = 0; + if (ha == _lastNameOn) + clearName(); + _needHitAreaRecalc++; + } +} + +bool SimonEngine::isBoxDead(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha == NULL) + return false; + return (ha->flags & kBFBoxDead) == 0; +} + +void SimonEngine::defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *item_ptr) { + HitArea *ha; + undefineBox(id); + + ha = findEmptyHitArea(); + ha->x = x; + ha->y = y; + ha->width = width; + ha->height = height; + ha->flags = flags | kBFBoxInUse; + ha->id = ha->priority = id; + ha->verb = verb; + ha->item_ptr = item_ptr; + + if ((getGameType() == GType_FF || getGameType() == GType_PP) && + (ha->flags & kBFHyperBox)) { + ha->data = _hyperLink; + ha->priority = 50; + } + + _needHitAreaRecalc++; +} + +void SimonEngine::resetVerbs() { + if (getGameType() == GType_PP) { + _verbHitArea = 300; + return; + } else if (getGameType() == GType_FF) { + _verbHitArea = 300; + int cursor = 0; + int animMax = 16; + + if (getBitFlag(203)) { + cursor = 14; + animMax = 9; + } else if (getBitFlag(204)) { + cursor = 15; + animMax = 9; + } else if (getBitFlag(207)) { + cursor = 26; + animMax = 2; + } + + _mouseCursor = cursor; + _mouseAnimMax = animMax; + _mouseAnim = 1; + _needHitAreaRecalc++; + + if (getBitFlag(99)) { + setVerb(NULL); + } + } else { + uint id; + HitArea *ha; + + if (getGameType() == GType_SIMON2) { + id = 2; + if (!getBitFlag(79)) + id = (_mouseY >= 136) ? 102 : 101; + } else { + id = (_mouseY >= 136) ? 102 : 101; + } + + _defaultVerb = id; + + ha = findBox(id); + if (ha == NULL) + return; + + if (ha->flags & kBFBoxDead) { + _defaultVerb = 999; + _currentVerbBox = NULL; + } else { + _verbHitArea = ha->verb; + setVerb(ha); + } + } +} + +void SimonEngine::setVerb(HitArea *ha) { + if (getGameType() == GType_PP) { + return; + } else if (getGameType() == GType_FF) { + int cursor = _mouseCursor; + if (_noRightClick) + return; + + if (cursor > 13) + cursor = 0; + cursor++; + if (cursor == 5) + cursor = 1; + if (cursor == 4) { + if (getBitFlag(72)) { + cursor = 1; + } + } else if (cursor == 2) { + if (getBitFlag(99)) { + cursor = 3; + } + } + + _mouseCursor = cursor; + _mouseAnimMax = (cursor == 4) ? 14: 16; + _mouseAnim = 1; + _needHitAreaRecalc++; + _verbHitArea = cursor + 300; + } else { + HitArea *tmp = _currentVerbBox; + + if (ha == tmp) + return; + + if (getGameType() == GType_SIMON1) { + if (tmp != NULL) { + tmp->flags |= kBFInvertTouch; + invertBox(tmp, 213, 208, 213, 10); + } + + if (ha->flags & kBFBoxSelected) + invertBox(ha, 218, 213, 213, 5); + else + invertBox(ha, 223, 218, 218, 10); + + ha->flags &= ~(kBFBoxSelected + kBFInvertTouch); + } else { + if (ha->id < 101) + return; + _mouseCursor = ha->id - 101; + _needHitAreaRecalc++; + } + _currentVerbBox = ha; + } +} + +void SimonEngine::hitarea_leave(HitArea *ha, bool state) { + if (getGameType() == GType_FF) { + invertBox_FF(ha, state); + } else if (getGameType() == GType_SIMON2) { + invertBox(ha, 231, 229, 230, 1); + } else { + invertBox(ha, 223, 213, 218, 5); + } +} + +void SimonEngine::leaveHitAreaById(uint hitarea_id) { + HitArea *ha = findBox(hitarea_id); + if (ha) + hitarea_leave(ha); +} + +void SimonEngine::checkUp(WindowBlock *window) { + uint16 j, k; + + if (((_variableArray[31] - _variableArray[30]) == 40) && (_variableArray[31] > 52)) { + k = (((_variableArray[31] / 52) - 2) % 3); + j = k * 6; + if (!isBoxDead(j + 201)) { + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, 0, window->iconPtr->classMask); + loadSprite(4, 9, k + 34, 0, 0, 0); + } + } + if ((_variableArray[31] - _variableArray[30]) == 76) { + k = ((_variableArray[31] / 52) % 3); + j = k * 6; + if (isBoxDead(j + 201)) { + loadSprite(4, 9, k + 31, 0, 0, 0); + undefineBox(j + 201); + undefineBox(j + 202); + undefineBox(j + 203); + undefineBox(j + 204); + undefineBox(j + 205); + undefineBox(j + 206); + } + _variableArray[31] -= 52; + _iOverflow = 1; + } +} + +void SimonEngine::checkDown(WindowBlock *window) { + uint16 j, k; + + if (((_variableArray[31] - _variableArray[30]) == 24) && (_iOverflow == 1)) { + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, 0, window->iconPtr->classMask); + k = ((_variableArray[31] / 52) % 3); + loadSprite(4, 9, k + 25, 0, 0, 0); + _variableArray[31] += 52; + } + if (((_variableArray[31] - _variableArray[30]) == 40) && (_variableArray[30] > 52)) { + k = (((_variableArray[31] / 52) + 1) % 3); + j = k * 6; + if (isBoxDead(j + 201)) { + loadSprite(4, 9, k + 28, 0, 0, 0); + undefineBox(j + 201); + undefineBox(j + 202); + undefineBox(j + 203); + undefineBox(j + 204); + undefineBox(j + 205); + undefineBox(j + 206); + } + } +} + +void SimonEngine::inventoryUp(WindowBlock *window) { + if (getGameType() == GType_FF) { + _marks = 0; + checkUp(window); + loadSprite(4, 9, 21, 0 ,0, 0); + while (1) { + if (_currentBoxNumber != 0x7FFB || !getBitFlag(89)) + break; + checkUp(window); + delay(1); + } + waitForMark(2); + checkUp(window); + sendSync(922); + waitForMark(1); + checkUp(window); + } else { + if (window->iconPtr->line == 0) + return; + + mouseOff(); + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line - 1, window->iconPtr->classMask); + mouseOn(); + } +} + +void SimonEngine::inventoryDown(WindowBlock *window) { + if (getGameType() == GType_FF) { + _marks = 0; + checkDown(window); + loadSprite(4, 9, 23, 0, 0, 0); + while (1) { + if (_currentBoxNumber != 0x7FFC || !getBitFlag(89)) + break; + checkDown(window); + delay(1); + } + waitForMark(2); + checkDown(window); + sendSync(924); + waitForMark(1); + checkDown(window); + } else { + mouseOff(); + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line + 1, window->iconPtr->classMask); + mouseOn(); + } +} + +void SimonEngine::boxController(uint x, uint y, uint mode) { + HitArea *best_ha; + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + uint16 priority = 0; + uint16 x_ = x; + uint16 y_ = y; + + if (getGameType() == GType_FF) { + x_ += _scrollX; + y_ += _scrollY; + } + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79) || y < 134) { + x_ += _scrollX * 8; + } + } + + best_ha = NULL; + + do { + if (ha->flags & kBFBoxInUse) { + if (!(ha->flags & kBFBoxDead)) { + if (x_ >= ha->x && y_ >= ha->y && + x_ - ha->x < ha->width && y_ - ha->y < ha->height && priority <= ha->priority) { + priority = ha->priority; + best_ha = ha; + } else { + if (ha->flags & kBFBoxSelected) { + hitarea_leave(ha , true); + ha->flags &= ~kBFBoxSelected; + } + } + } else { + ha->flags &= ~kBFBoxSelected; + } + } + } while (ha++, --count); + + _currentBoxNumber = 0; + + if (best_ha == NULL) { + clearName(); + return; + } + + _currentBoxNumber = best_ha->id; + + if (mode != 0 && mode != 3) { + _lastHitArea = best_ha; + if (getGameType() == GType_PP) { + _variableArray[400] = x; + _variableArray[401] = y; + } else { + _variableArray[1] = x; + _variableArray[2] = y; + } + } + + if (best_ha->flags & kBFNoTouchName) { + clearName(); + } else if (best_ha != _lastNameOn) { + displayName(best_ha); + } + + if (best_ha->flags & kBFInvertTouch && !(best_ha->flags & kBFBoxSelected)) { + hitarea_leave(best_ha, false); + best_ha->flags |= kBFBoxSelected; + } + + return; +} + +void SimonEngine::displayName(HitArea *ha) { + bool result; + int x = 0, y = 0; + + if (getGameType() == GType_PP) { + if (ha->flags & kBFHyperBox) { + _lastNameOn = ha; + return; + } + if (findBox(50)) + return; + + y = ha->y; + y -= 17; + if (y < 0) + y = 0; + y += 2; + x = ha->width / 2 + ha->x; + } else if (getGameType() == GType_FF) { + if (ha->flags & kBFHyperBox) { + _lastNameOn = ha; + return; + } + if (findBox(50)) + return; + + if (getBitFlag(99)) + _animatePointer = ((ha->flags & kBFTextBox) == 0); + else + _animatePointer = 1; + + if (!getBitFlag(73)) + return; + + y = ha->y; + if (getBitFlag(99) && y > 288) + y = 288; + y -= 17; + if (y < 0) + y = 0; + y += 2; + x = ha->width / 2 + ha->x; + } else { + resetNameWindow(); + } + + if (ha->flags & kBFTextBox) { + result = printTextOf(ha->flags / 256, x, y); + } else { + result = printNameOf(ha->item_ptr, x, y); + } + + if (result) + _lastNameOn = ha; +} + +void SimonEngine::invertBox_FF(HitArea *ha, bool state) { + if (getBitFlag(205) || getBitFlag(206)) { + if (state != 0) { + _mouseAnimMax = _oldMouseAnimMax; + _mouseCursor = _oldMouseCursor; + } else if (_mouseCursor != 18) { + _oldMouseCursor = _mouseCursor; + _animatePointer = 0; + _oldMouseAnimMax = _mouseAnimMax; + _mouseAnimMax = 2; + _mouseCursor = 18; + } + } else { + if (getBitFlag(207)) { + if (state != 0) { + _noRightClick = 0; + resetVerbs(); + } else { + int cursor = ha->id + 9; + if (cursor >= 23) + cursor = 21; + _mouseCursor = cursor; + _mouseAnimMax = 8; + _noRightClick = 1; + } + } else { + VgaSprite *vsp = _vgaSprites; + + int id = ha->id - 43; + while (vsp->id) { + if (vsp->id == id && vsp->zoneNum == 2) { + if (state == 0) + vsp->flags |= kDFShaded; + else + vsp->flags &= ~kDFShaded; + break; + } + vsp++; + } + } + } +} + +void SimonEngine::invertBox(HitArea * ha, byte a, byte b, byte c, byte d) { + byte *src, color; + int w, h, i; + + _lockWord |= 0x8000; + src = getFrontBuf() + ha->y * _dxSurfacePitch + ha->x; + + _hitarea_unk_3 = true; + + w = ha->width; + h = ha->height; + + // Works around bug in original Simon the Sorcerer 2 + // Animations continue in background when load/save dialog is open + // often causing the savegame name highlighter to be cut short + if (!(h > 0 && w > 0 && ha->x + w <= _screenWidth && ha->y + h <= _screenHeight)) { + debug(1,"Invalid coordinates in invertBox (%d,%d,%d,%d)", ha->x, ha->y, ha->width, ha->height); + _lockWord &= ~0x8000; + return; + } + + do { + for (i = 0; i != w; ++i) { + color = src[i]; + if (a >= color && b < color) { + if (c >= color) + color += d; + else + color -= d; + src[i] = color; + } + } + src += _dxSurfacePitch; + } while (--h); + + _lockWord &= ~0x8000; +} + +} // End of namespace Simon |