/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 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, see . * */ #include "ngi/ngi.h" #include "ngi/objects.h" #include "ngi/behavior.h" #include "ngi/statics.h" #include "ngi/messages.h" namespace NGI { BehaviorManager::BehaviorManager() { _scene = nullptr; _isActive = 1; } BehaviorManager::~BehaviorManager() { clear(); } void BehaviorManager::clear() { _behaviors.clear(); } void BehaviorManager::initBehavior(Scene *sc, GameVar *var) { debugC(2, kDebugBehavior, "BehaviorManager::initBehavior(%d, %s)", sc->_sceneId, transCyrillic(var->_varName)); clear(); _scene = sc; GameVar *behvar = var->getSubVarByName("BEHAVIOR"); if (!behvar) return; debugC(3, kDebugBehavior, "BehaviorManager::initBehavior. have Variable"); for (GameVar *subvar = behvar->_subVars; subvar; subvar = subvar->_nextVarObj) { debugC(3, kDebugBehavior, "BehaviorManager::initBehavior. subVar %s", transCyrillic(subvar->_varName)); if (subvar->_varName == "AMBIENT") { _behaviors.push_back(BehaviorInfo()); _behaviors.back().initAmbientBehavior(subvar, sc); } else { StaticANIObject *ani = sc->getStaticANIObject1ByName(subvar->_varName, -1); if (ani) { for (uint i = 0; i < sc->_staticANIObjectList1.size(); i++) { if (sc->_staticANIObjectList1[i]->_id == ani->_id) { _behaviors.push_back(BehaviorInfo()); BehaviorInfo &behinfo = _behaviors.back(); behinfo.initObjectBehavior(subvar, sc, ani); behinfo._ani = sc->_staticANIObjectList1[i]; } } } } } } void BehaviorManager::updateBehaviors() { if (!_isActive) return; debugC(6, kDebugBehavior, "BehaviorManager::updateBehaviors()"); for (uint i = 0; i < _behaviors.size(); i++) { BehaviorInfo &beh = _behaviors[i]; if (!beh._ani) { beh._counter++; if (beh._counter >= beh._counterMax) updateBehavior(beh, beh._behaviorAnims[0]); continue; } if (beh._ani->_movement || !(beh._ani->_flags & 4) || (beh._ani->_flags & 2)) { beh._staticsId = 0; continue; } if (beh._ani->_statics->_staticsId == beh._staticsId) { beh._counter++; if (beh._counter >= beh._counterMax) { if (beh._subIndex >= 0 && !(beh._flags & 1) && beh._ani->_messageQueueId <= 0) { assert(beh._ani); updateStaticAniBehavior(*beh._ani, beh._counter, beh._behaviorAnims[beh._subIndex]); } } } else { beh._staticsId = beh._ani->_statics->_staticsId; beh._counter = 0; beh._subIndex = -1; for (int j = 0; j < beh._animsCount; j++) if (beh._behaviorAnims[j]._staticsId == beh._staticsId) { beh._subIndex = j; break; } } } } void BehaviorManager::updateBehavior(BehaviorInfo &behaviorInfo, BehaviorAnim &entry) { debugC(7, kDebugBehavior, "BehaviorManager::updateBehavior() moves: %d", entry._behaviorMoves.size()); for (uint i = 0; i < entry._behaviorMoves.size(); i++) { BehaviorMove &bhi = entry._behaviorMoves[i]; if (!(bhi._flags & 1)) { if (bhi._flags & 2) { MessageQueue *mq = new MessageQueue(bhi._messageQueue, 0, 1); mq->sendNextCommand(); bhi._flags &= 0xFFFFFFFD; } else if (behaviorInfo._counter >= bhi._delay && bhi._percent && g_nmi->_rnd.getRandomNumber(32767) <= entry._behaviorMoves[i]._percent) { MessageQueue *mq = new MessageQueue(bhi._messageQueue, 0, 1); mq->sendNextCommand(); behaviorInfo._counter = 0; } } } } void BehaviorManager::updateStaticAniBehavior(StaticANIObject &ani, int delay, const BehaviorAnim &beh) { debugC(6, kDebugBehavior, "BehaviorManager::updateStaticAniBehavior(%s)", transCyrillic(ani._objectName)); MessageQueue *mq = nullptr; if (beh._flags & 1) { uint rnd = g_nmi->_rnd.getRandomNumber(32767); uint runPercent = 0; for (uint i = 0; i < beh._behaviorMoves.size(); i++) { if (!(beh._behaviorMoves[i]._flags & 1) && beh._behaviorMoves[i]._percent) { if ((rnd >= runPercent && rnd <= runPercent + beh._behaviorMoves[i]._percent) || i == beh._behaviorMoves.size() - 1) { mq = new MessageQueue(beh._behaviorMoves[i]._messageQueue, 0, 1); break; } runPercent += beh._behaviorMoves[i]._percent; } } } else { for (uint i = 0; i < beh._behaviorMoves.size(); i++) { if (!(beh._behaviorMoves[i]._flags & 1) && delay >= beh._behaviorMoves[i]._delay) { if (beh._behaviorMoves[i]._percent) { if (g_nmi->_rnd.getRandomNumber(32767) <= beh._behaviorMoves[i]._percent) { mq = new MessageQueue(beh._behaviorMoves[i]._messageQueue, 0, 1); break; } } } } } if (mq) { mq->setParamInt(-1, ani._odelay); if (!mq->chain(&ani)) { g_nmi->_globalMessageQueueList->deleteQueueById(mq->_id); } } } bool BehaviorManager::setBehaviorEnabled(StaticANIObject *obj, int aniId, int quId, int flag) { BehaviorMove *entry = getBehaviorMoveByMessageQueueDataId(obj, aniId, quId); if (entry) { if (flag) entry->_flags &= 0xFFFFFFFE; else entry->_flags |= 1; } else return false; return true; } void BehaviorManager::setFlagByStaticAniObject(StaticANIObject *ani, int flag) { for (uint i = 0; i < _behaviors.size(); i++) { BehaviorInfo &beh = _behaviors[i]; if (ani == beh._ani) { if (flag) beh._flags &= 0xfffffffe; else beh._flags |= 1; } } } BehaviorMove *BehaviorManager::getBehaviorMoveByMessageQueueDataId(StaticANIObject *ani, int id1, int id2) { for (uint i = 0; i < _behaviors.size(); i++) { if (_behaviors[i]._ani == ani) { for (uint j = 0; j < _behaviors[i]._behaviorAnims.size(); j++) { if (_behaviors[i]._behaviorAnims[j]._staticsId == id1) { for (uint k = 0; k < _behaviors[i]._behaviorAnims[j]._behaviorMoves.size(); k++) { if (_behaviors[i]._behaviorAnims[j]._behaviorMoves[k]._messageQueue->_dataId == id2) return &_behaviors[i]._behaviorAnims[j]._behaviorMoves[k]; } } } } } return nullptr; } void BehaviorInfo::clear() { _ani = nullptr; _staticsId = 0; _counter = 0; _counterMax = 0; _flags = 0; _subIndex = 0; _animsCount = 0; _behaviorAnims.clear(); } void BehaviorInfo::initAmbientBehavior(GameVar *var, Scene *sc) { debugC(4, kDebugBehavior, "BehaviorInfo::initAmbientBehavior(%s)", transCyrillic(var->_varName)); clear(); _animsCount = 1; _counterMax = -1; _behaviorAnims.push_back(BehaviorAnim()); BehaviorAnim &bi = _behaviorAnims.back(); int movesCount = var->getSubVarsCount(); bi._behaviorMoves.reserve(movesCount); for (int i = 0; i < movesCount; i++) { int delay; bi._behaviorMoves.push_back(BehaviorMove(var->getSubVarByIndex(i), sc, &delay)); BehaviorMove &move = bi._behaviorMoves.back(); if (move._delay < _counterMax) _counterMax = move._delay; } } void BehaviorInfo::initObjectBehavior(GameVar *var, Scene *sc, StaticANIObject *ani) { Common::String s((char *)transCyrillic(var->_varName)); debugC(4, kDebugBehavior, "BehaviorInfo::initObjectBehavior(%s, %d, %s)", s.c_str(), sc->_sceneId, transCyrillic(ani->_objectName)); clear(); _animsCount = var->getSubVarsCount(); _counterMax = -1; while (var->_varType == 2) { if (strcmp(var->_value.stringValue, "ROOT")) break; GameVar *v1 = g_nmi->getGameLoaderGameVar()->getSubVarByName("BEHAVIOR")->getSubVarByName(ani->getName()); if (v1 == var) return; sc = g_nmi->accessScene(ani->_sceneId); clear(); var = v1; _animsCount = var->getSubVarsCount(); _counterMax = -1; } for (int i = 0; i < _animsCount; i++) { int maxDelay = 0; _behaviorAnims.push_back(BehaviorAnim(var->getSubVarByIndex(i), sc, ani, &maxDelay)); if (maxDelay < _counterMax) _counterMax = maxDelay; } } BehaviorAnim::BehaviorAnim() { _staticsId = 0; _flags = 0; } BehaviorAnim::BehaviorAnim(GameVar *var, Scene *sc, StaticANIObject *ani, int *minDelay) { _staticsId = 0; *minDelay = 0xffffffff; int totalPercent = 0; _flags = 0; Statics *st = ani->getStaticsByName(var->_varName); if (st) _staticsId = st->_staticsId; int movesCount = var->getSubVarsCount(); if (movesCount) { _behaviorMoves.reserve(movesCount); for (int i = 0; i < movesCount; i++) { GameVar *subvar = var->getSubVarByIndex(i); int delay = 0; _behaviorMoves.push_back(BehaviorMove(subvar, sc, &delay)); BehaviorMove &move = _behaviorMoves.back(); totalPercent += delay; if (move._delay < *minDelay) *minDelay = move._delay; } if (!*minDelay && totalPercent == 1000) _flags |= 1; } } BehaviorMove::BehaviorMove(GameVar *subvar, Scene *sc, int *delay) { _messageQueue = nullptr; _delay = 0; _percent = 0; _flags = 0; _messageQueue = sc->getMessageQueueByName(subvar->_varName); GameVar *vart = subvar->getSubVarByName("dwDelay"); if (vart) _delay = vart->_value.intValue; *delay = 0; vart = subvar->getSubVarByName("dwPercent"); if (vart) { _percent = 0x7FFF * vart->_value.intValue / 1000; *delay = vart->_value.intValue; } vart = subvar->getSubVarByName("dwFlags"); if (vart && vart->_varType == 2 && strstr(vart->_value.stringValue, "QDESC_AUTOSTART")) _flags |= 2; } } // End of namespace NGI