Initial commit
This commit is contained in:
550
engines/twine/scene/actor.cpp
Normal file
550
engines/twine/scene/actor.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/actor.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/parser/entity.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/hqr.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/extra.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Actor::Actor(TwinEEngine *engine) : _engine(engine) {
|
||||
}
|
||||
|
||||
void Actor::restartPerso() {
|
||||
ActorStruct *sceneHero = _engine->_scene->_sceneHero;
|
||||
sceneHero->_move = ControlMode::kManual;
|
||||
memset(&sceneHero->_workFlags, 0, sizeof(sceneHero->_workFlags));
|
||||
memset(&sceneHero->_flags, 0, sizeof(sceneHero->_flags));
|
||||
|
||||
sceneHero->_flags.bComputeCollisionWithObj = 1;
|
||||
sceneHero->_flags.bComputeCollisionWithBricks = 1;
|
||||
sceneHero->_flags.bCheckZone = 1;
|
||||
sceneHero->_flags.bCanDrown = 1;
|
||||
sceneHero->_flags.bObjFallable = 1;
|
||||
|
||||
sceneHero->_armor = 1;
|
||||
sceneHero->_offsetTrack = -1;
|
||||
sceneHero->_labelTrack = -1;
|
||||
sceneHero->_offsetLife = 0;
|
||||
sceneHero->_zoneSce = -1;
|
||||
sceneHero->_beta = _previousHeroAngle;
|
||||
|
||||
_engine->_movements->initRealAngle(sceneHero->_beta, sceneHero->_beta, LBAAngles::ANGLE_0, &sceneHero->realAngle);
|
||||
setBehaviour(_previousHeroBehaviour);
|
||||
|
||||
_cropBottomScreen = 0;
|
||||
}
|
||||
|
||||
void Actor::loadBehaviourEntity(ActorStruct *actor, EntityData &entityData, int16 &bodyAnimIndex, int32 index) {
|
||||
_engine->_resources->loadEntityData(entityData, index);
|
||||
actor->_entityDataPtr = &entityData;
|
||||
bodyAnimIndex = entityData.getAnimIndex(AnimationTypes::kStanding);
|
||||
if (bodyAnimIndex == -1) {
|
||||
error("Could not find animation data for 3d data with index %i", index);
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::loadHeroEntities() {
|
||||
ActorStruct *sceneHero = _engine->_scene->_sceneHero;
|
||||
loadBehaviourEntity(sceneHero, _heroEntityATHLETIC, _heroAnimIdxATHLETIC, FILE3DHQR_HEROATHLETIC);
|
||||
loadBehaviourEntity(sceneHero, _heroEntityAGGRESSIVE, _heroAnimIdxAGGRESSIVE, FILE3DHQR_HEROAGGRESSIVE);
|
||||
loadBehaviourEntity(sceneHero, _heroEntityDISCRETE, _heroAnimIdxDISCRETE, FILE3DHQR_HERODISCRETE);
|
||||
loadBehaviourEntity(sceneHero, _heroEntityPROTOPACK, _heroAnimIdxPROTOPACK, FILE3DHQR_HEROPROTOPACK);
|
||||
loadBehaviourEntity(sceneHero, _heroEntityNORMAL, _heroAnimIdxNORMAL, FILE3DHQR_HERONORMAL);
|
||||
|
||||
_engine->_animations->_currentActorAnimExtraPtr = AnimationTypes::kStanding;
|
||||
sceneHero->_ptrAnimAction = _engine->_animations->_currentActorAnimExtraPtr;
|
||||
}
|
||||
|
||||
void Actor::setBehaviour(HeroBehaviourType behaviour) {
|
||||
ActorStruct *sceneHero = _engine->_scene->_sceneHero;
|
||||
switch (behaviour) {
|
||||
case HeroBehaviourType::kNormal:
|
||||
_heroBehaviour = behaviour;
|
||||
sceneHero->_entityDataPtr = &_heroEntityNORMAL;
|
||||
break;
|
||||
case HeroBehaviourType::kAthletic:
|
||||
_heroBehaviour = behaviour;
|
||||
sceneHero->_entityDataPtr = &_heroEntityATHLETIC;
|
||||
break;
|
||||
case HeroBehaviourType::kAggressive:
|
||||
_heroBehaviour = behaviour;
|
||||
sceneHero->_entityDataPtr = &_heroEntityAGGRESSIVE;
|
||||
break;
|
||||
case HeroBehaviourType::kDiscrete:
|
||||
_heroBehaviour = behaviour;
|
||||
sceneHero->_entityDataPtr = &_heroEntityDISCRETE;
|
||||
break;
|
||||
case HeroBehaviourType::kProtoPack:
|
||||
_heroBehaviour = behaviour;
|
||||
sceneHero->_entityDataPtr = &_heroEntityPROTOPACK;
|
||||
break;
|
||||
case HeroBehaviourType::kMax:
|
||||
break;
|
||||
}
|
||||
|
||||
const BodyType bodyIdx = sceneHero->_genBody;
|
||||
|
||||
sceneHero->_body = -1;
|
||||
sceneHero->_genBody = BodyType::btNone;
|
||||
|
||||
initBody(bodyIdx, OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
sceneHero->_genAnim = AnimationTypes::kAnimNone;
|
||||
sceneHero->_flagAnim = AnimType::kAnimationTypeRepeat;
|
||||
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, OWN_ACTOR_SCENE_INDEX);
|
||||
}
|
||||
|
||||
void Actor::setFrame(int32 actorIdx, uint32 frame) {
|
||||
#if 0
|
||||
// TODO: converted from asm - not yet adapted
|
||||
ActorStruct *obj = _engine->_scene->getActor(actorIdx);
|
||||
T_PTR_NUM tempNextBody = obj->NextBody;
|
||||
void *tempNextTexture = obj->NextTexture;
|
||||
|
||||
if (frame >= obj->NbFrames) {
|
||||
return;
|
||||
}
|
||||
|
||||
obj->_body = tempNextBody;
|
||||
obj->Texture = tempNextTexture;
|
||||
|
||||
T_PTR_NUM tempAnim = obj->_anim;
|
||||
void (*TransFctAnim)() = nullptr; // Couldn't find this yet
|
||||
|
||||
if (TransFctAnim != nullptr) {
|
||||
uint32 ebp = frame;
|
||||
TransFctAnim();
|
||||
tempAnim = (T_PTR_NUM)(void *)tempAnim.Ptr;
|
||||
frame = ebp;
|
||||
}
|
||||
|
||||
obj->Interpolator = 0;
|
||||
obj->LastAnimStepX = 0;
|
||||
obj->LastAnimStepY = 0;
|
||||
obj->LastAnimStepZ = 0;
|
||||
|
||||
uint16 nbGroups = *((uint16 *)(tempAnim.Ptr + 2));
|
||||
|
||||
obj->LastAnimStepAlpha = 0;
|
||||
obj->LastAnimStepBeta = 0;
|
||||
obj->LastAnimStepGamma = 0;
|
||||
obj->LastOfsIsPtr = 0;
|
||||
|
||||
uint32 lastOfsFrame = nbGroups * 8 + 8; // infos frame + 4 WORDs per group
|
||||
|
||||
obj->LastNbGroups = nbGroups;
|
||||
obj->NextNbGroups = nbGroups;
|
||||
obj->NbGroups = nbGroups;
|
||||
|
||||
lastOfsFrame *= frame;
|
||||
uint32 timerRefHR = 0; // Replace with actual TimerRefHR
|
||||
|
||||
lastOfsFrame += 8; // Skip header
|
||||
|
||||
obj->LastTimer = timerRefHR;
|
||||
obj->Time = timerRefHR;
|
||||
obj->Status = 1; // STATUS_FRAME
|
||||
obj->LastOfsFrame = lastOfsFrame;
|
||||
obj->LastFrame = frame;
|
||||
|
||||
uint32 ecx = nbGroups * 2 - 2; // 2 DWORDs per group, no group 0
|
||||
T_PTR_NUM ebpPtr = tempAnim;
|
||||
tempAnim.Ptr = tempAnim.Ptr + lastOfsFrame + 16;
|
||||
|
||||
memcpy(obj->CurrentFrame, tempAnim.Ptr, ecx);
|
||||
|
||||
if (++frame == obj->NbFrames) {
|
||||
uint16 time = *((uint16 *)(ebpPtr.Ptr + 8));
|
||||
frame = 0;
|
||||
tempAnim.Ptr = (void *)(8);
|
||||
} else {
|
||||
uint16 time = *((uint16 *)tempAnim.Ptr);
|
||||
tempAnim.Ptr -= ebpPtr.Ptr;
|
||||
tempAnim.Num += obj->LastTimer;
|
||||
obj->NextFrame = frame;
|
||||
obj->NextOfsFrame = (uint32)(tempAnim.Ptr);
|
||||
obj->NextTimer = time;
|
||||
obj->Master = *((uint16 *)(tempAnim.Ptr + 8));
|
||||
obj->Status = 1; // STATUS_FRAME
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Actor::initSprite(int32 spriteNum, int32 actorIdx) {
|
||||
ActorStruct *localActor = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
localActor->_sprite = spriteNum; // lba2
|
||||
|
||||
if (!localActor->_flags.bSprite3D) {
|
||||
return;
|
||||
}
|
||||
if (spriteNum != -1 && localActor->_body != spriteNum) {
|
||||
const BoundingBox *spritebbox = _engine->_resources->_spriteBoundingBox.bbox(spriteNum);
|
||||
localActor->_body = spriteNum;
|
||||
localActor->_boundingBox = *spritebbox;
|
||||
}
|
||||
}
|
||||
|
||||
TextId Actor::getTextIdForBehaviour() const {
|
||||
if (_heroBehaviour == HeroBehaviourType::kAggressive && _combatAuto) {
|
||||
return TextId::kBehaviourAggressiveAuto;
|
||||
}
|
||||
// the other values are matching the text ids
|
||||
return (TextId)(int32)_heroBehaviour;
|
||||
}
|
||||
|
||||
int32 Actor::searchBody(BodyType bodyIdx, int32 actorIdx, ActorBoundingBox &actorBoundingBox) {
|
||||
if (bodyIdx == BodyType::btNone) {
|
||||
return -1;
|
||||
}
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
const EntityBody *body = actor->_entityDataPtr->getEntityBody((int)bodyIdx);
|
||||
if (body == nullptr) {
|
||||
warning("Failed to get entity body for body idx %i", (int)bodyIdx);
|
||||
return -1;
|
||||
}
|
||||
actorBoundingBox = body->actorBoundingBox;
|
||||
return (int)bodyIdx;
|
||||
}
|
||||
|
||||
void Actor::initBody(BodyType gennewbody, int16 actorIdx) {
|
||||
ActorStruct *localActor = _engine->_scene->getActor(actorIdx);
|
||||
if (localActor->_flags.bSprite3D) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug(1, "Load body %i for actor %i", (int)gennewbody, actorIdx);
|
||||
|
||||
if (IS_HERO(actorIdx) && _heroBehaviour == HeroBehaviourType::kProtoPack && gennewbody != BodyType::btTunic && gennewbody != BodyType::btNormal) {
|
||||
setBehaviour(HeroBehaviourType::kNormal);
|
||||
}
|
||||
|
||||
ActorBoundingBox actorBoundingBox;
|
||||
const int32 newBody = searchBody(gennewbody, actorIdx, actorBoundingBox);
|
||||
if (newBody == -1) {
|
||||
localActor->_genBody = BodyType::btNone;
|
||||
localActor->_body = -1;
|
||||
localActor->_boundingBox = BoundingBox();
|
||||
debug("Failed to initialize body %i for actor %i", (int)gennewbody, actorIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (localActor->_body == newBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 oldBody = localActor->_body;
|
||||
localActor->_body = newBody;
|
||||
localActor->_genBody = gennewbody;
|
||||
|
||||
if (actorBoundingBox.hasBoundingBox) {
|
||||
localActor->_boundingBox = actorBoundingBox.bbox;
|
||||
} else {
|
||||
const BodyData &bd = localActor->_entityDataPtr->getBody(localActor->_body);
|
||||
localActor->_boundingBox = bd.bbox;
|
||||
|
||||
int32 size = 0;
|
||||
const int32 distX = bd.bbox.maxs.x - bd.bbox.mins.x;
|
||||
const int32 distZ = bd.bbox.maxs.z - bd.bbox.mins.z;
|
||||
if (localActor->_flags.bUseMiniZv) {
|
||||
// take smaller for bound
|
||||
if (distX < distZ)
|
||||
size = distX / 2;
|
||||
else
|
||||
size = distZ / 2;
|
||||
} else {
|
||||
// take average for bound
|
||||
size = (distZ + distX) / 4;
|
||||
}
|
||||
|
||||
localActor->_boundingBox.mins.x = -size;
|
||||
localActor->_boundingBox.maxs.x = size;
|
||||
localActor->_boundingBox.mins.z = -size;
|
||||
localActor->_boundingBox.maxs.z = size;
|
||||
}
|
||||
if (oldBody != -1 && localActor->_anim != -1) {
|
||||
copyInterAnim(localActor->_entityDataPtr->getBody(oldBody), localActor->_entityDataPtr->getBody(localActor->_body));
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::copyInterAnim(const BodyData &src, BodyData &dest) {
|
||||
if (!src.isAnimated() || !dest.isAnimated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dest._animTimerData = src._animTimerData;
|
||||
|
||||
const int16 numBones = MIN<int16>((int16)src.getNumBones(), (int16)dest.getNumBones());
|
||||
for (int16 i = 0; i < numBones; ++i) {
|
||||
const BoneFrame *srcBoneFrame = src.getBoneState(i);
|
||||
BoneFrame *destBoneFrame = dest.getBoneState(i);
|
||||
*destBoneFrame = *srcBoneFrame;
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::startInitObj(int16 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
if (actor->_flags.bSprite3D) {
|
||||
if (actor->_hitForce != 0) {
|
||||
actor->_workFlags.bIsHitting = 1;
|
||||
}
|
||||
|
||||
actor->_body = -1;
|
||||
|
||||
initSprite(actor->_sprite, actorIdx);
|
||||
|
||||
_engine->_movements->initRealAngle(LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, &actor->realAngle);
|
||||
|
||||
if (actor->_flags.bSpriteClip) {
|
||||
actor->_animStep = actor->posObj();
|
||||
}
|
||||
} else {
|
||||
actor->_body = -1;
|
||||
|
||||
debug(1, "Init actor %i with model %i", actorIdx, (int)actor->_genBody);
|
||||
initBody(actor->_genBody, actorIdx);
|
||||
|
||||
actor->_anim = -1;
|
||||
actor->_flagAnim = AnimType::kAnimationTypeRepeat;
|
||||
|
||||
if (actor->_body != -1) {
|
||||
_engine->_animations->initAnim(actor->_genAnim, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
|
||||
_engine->_movements->initRealAngle(actor->_beta, actor->_beta, LBAAngles::ANGLE_0, &actor->realAngle);
|
||||
}
|
||||
|
||||
actor->_offsetTrack = -1;
|
||||
actor->_labelTrack = -1;
|
||||
actor->_offsetLife = 0;
|
||||
}
|
||||
|
||||
void Actor::initObject(int16 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
*actor = ActorStruct(_engine->getMaxLife());
|
||||
|
||||
actor->_actorIdx = actorIdx;
|
||||
actor->_posObj = IVec3(0, SIZE_BRICK_Y, 0);
|
||||
|
||||
memset(&actor->_flags, 0, sizeof(StaticFlagsStruct));
|
||||
memset(&actor->_workFlags, 0, sizeof(DynamicFlagsStruct));
|
||||
memset(&actor->_bonusParameter, 0, sizeof(BonusParameter));
|
||||
|
||||
_engine->_movements->initRealAngle(LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, &actor->realAngle);
|
||||
}
|
||||
|
||||
void Actor::hitObj(int32 actorIdx, int32 actorIdxAttacked, int32 hitforce, int32 angle) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdxAttacked);
|
||||
if (actor->_lifePoint <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_HERO(actorIdxAttacked) && _engine->_debugState->_godMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
actor->_hitBy = actorIdx;
|
||||
debugC(1, TwinE::kDebugCollision, "Actor %d was hit by %d", actorIdxAttacked, actorIdx);
|
||||
|
||||
if (actor->_armor <= hitforce) {
|
||||
if (actor->_genAnim == AnimationTypes::kBigHit || actor->_genAnim == AnimationTypes::kHit2) {
|
||||
if (actor->_nextGenAnim != AnimationTypes::kStanding) {
|
||||
const int32 tmpAnimPos = actor->_frame;
|
||||
actor->_frame = 1;
|
||||
_engine->_animations->processAnimActions(actorIdxAttacked);
|
||||
actor->_frame = tmpAnimPos;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (angle != -1) {
|
||||
_engine->_movements->initRealAngle(angle, angle, LBAAngles::ANGLE_0, &actor->realAngle);
|
||||
}
|
||||
|
||||
if (_engine->getRandomNumber() & 1) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kHit2, AnimType::kAnimationInsert, AnimationTypes::kNoAnim, actorIdxAttacked);
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kBigHit, AnimType::kAnimationInsert, AnimationTypes::kNoAnim, actorIdxAttacked);
|
||||
}
|
||||
}
|
||||
|
||||
_engine->_extra->initSpecial(actor->_posObj.x, actor->_posObj.y + 1000, actor->_posObj.z, ExtraSpecialType::kHitStars);
|
||||
|
||||
if (IS_HERO(actorIdxAttacked)) {
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
}
|
||||
// TODO: in the original sources this in an else block - dotemu release doesn't have this (so we are going after dotmeu here)
|
||||
// else {
|
||||
actor->_lifePoint -= hitforce;
|
||||
// }
|
||||
if (actor->_lifePoint < 0) {
|
||||
actor->_lifePoint = 0;
|
||||
}
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kHit, AnimType::kAnimationInsert, AnimationTypes::kNoAnim, actorIdxAttacked);
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::checkCarrier(int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (!actor->_flags.bIsCarrierActor) {
|
||||
return;
|
||||
}
|
||||
for (int32 a = 0; a < _engine->_scene->_nbObjets; a++) {
|
||||
ActorStruct *otherActor = _engine->_scene->getActor(a);
|
||||
if (otherActor->_carryBy == actorIdx) {
|
||||
otherActor->_carryBy = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::giveExtraBonus(int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
const int bonusSprite = _engine->_extra->getBonusSprite(actor->_bonusParameter);
|
||||
if (bonusSprite == -1) {
|
||||
return;
|
||||
}
|
||||
if (actor->_workFlags.bIsDead) {
|
||||
_engine->_extra->addExtraBonus(actor->posObj(), LBAAngles::ANGLE_90, LBAAngles::ANGLE_0, bonusSprite, actor->_bonusAmount);
|
||||
_engine->_sound->mixSample3D(Samples::ItemPopup, 0x1000, 1, actor->posObj(), actorIdx);
|
||||
} else {
|
||||
const ActorStruct *sceneHero = _engine->_scene->_sceneHero;
|
||||
const int32 angle = _engine->_movements->getAngle(actor->posObj(), sceneHero->posObj());
|
||||
const IVec3 pos(actor->_posObj.x, actor->_posObj.y + actor->_boundingBox.maxs.y, actor->_posObj.z);
|
||||
_engine->_extra->addExtraBonus(pos, LBAAngles::ANGLE_70, angle, bonusSprite, actor->_bonusAmount);
|
||||
_engine->_sound->mixSample3D(Samples::ItemPopup, 0x1000, 1, pos, actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// Lba2
|
||||
#define START_AROUND_BETA 1024
|
||||
#define END_AROUND_BETA 3072
|
||||
#define STEP_AROUND_BETA 128 // 16 pos testees
|
||||
#define GetAngle2D(x0, z0, x1, z1) GetAngleVector2D((x1) - (x0), (z1) - (z0))
|
||||
|
||||
void Actor::posObjectAroundAnother(uint8 numsrc, uint8 numtopos) {
|
||||
#if 0
|
||||
ActorStruct *objsrc;
|
||||
ActorStruct *objtopos;
|
||||
int32 beta, dist, dist2;
|
||||
int32 step;
|
||||
|
||||
objsrc = _engine->_scene->getActor(numsrc);
|
||||
objtopos = _engine->_scene->getActor(numtopos);
|
||||
|
||||
int32 xb = objsrc->Obj.X;
|
||||
int32 zb = objsrc->Obj.Z;
|
||||
|
||||
objtopos->Obj.Y = objsrc->Obj.Y;
|
||||
|
||||
dist = MAX(objsrc->XMin, objsrc->XMax);
|
||||
dist = MAX(dist, objsrc->ZMin);
|
||||
dist = MAX(dist, objsrc->ZMax);
|
||||
|
||||
dist2 = MAX(objtopos->XMin, objtopos->XMax);
|
||||
dist2 = MAX(dist2, objtopos->ZMin);
|
||||
dist2 = MAX(dist2, objtopos->ZMax);
|
||||
|
||||
dist += dist / 2 + dist2 + dist2 / 2;
|
||||
|
||||
beta = ClampAngle(objsrc->Obj.Beta + START_AROUND_BETA);
|
||||
|
||||
for (step = 0; step < (4096 / STEP_AROUND_BETA); step++, beta += STEP_AROUND_BETA) {
|
||||
beta &= 4095;
|
||||
_engine->_renderer->rotate(0, dist, beta);
|
||||
|
||||
objtopos->Obj.X = xb + X0;
|
||||
objtopos->Obj.Z = zb + Z0;
|
||||
|
||||
if (_engine->_collision->checkValidObjPos(numtopos, numsrc)) {
|
||||
// accepte position
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
objtopos->Obj.Beta = ClampAngle(GetAngle2D(xb, zb, objtopos->Obj.X, objtopos->Obj.Z));
|
||||
#endif
|
||||
}
|
||||
|
||||
int16 RealValue::getRealValueFromTime(int32 time) {
|
||||
if (timeValue) {
|
||||
const int32 delta = time - memoTicks;
|
||||
|
||||
if (delta >= timeValue) { // rotation is finished
|
||||
timeValue = 0;
|
||||
return endValue;
|
||||
}
|
||||
|
||||
int32 t = ((endValue - startValue) * delta) / timeValue;
|
||||
t += startValue;
|
||||
|
||||
return (int16)t;
|
||||
}
|
||||
|
||||
return endValue;
|
||||
}
|
||||
|
||||
int16 RealValue::getRealAngle(int32 time) {
|
||||
if (timeValue) {
|
||||
int32 delta = time - memoTicks;
|
||||
if (delta < timeValue) {
|
||||
int32 t = NormalizeAngle(endValue - startValue);
|
||||
t = (t * delta) / timeValue;
|
||||
t += startValue;
|
||||
return (int16)t;
|
||||
}
|
||||
|
||||
timeValue = 0;
|
||||
}
|
||||
|
||||
return endValue;
|
||||
}
|
||||
|
||||
bool ActorStruct::isAttackAnimationActive() const {
|
||||
return _genAnim == AnimationTypes::kRightPunch || _genAnim == AnimationTypes::kLeftPunch || _genAnim == AnimationTypes::kKick;
|
||||
}
|
||||
|
||||
bool ActorStruct::isAttackWeaponAnimationActive() const {
|
||||
return _genAnim == AnimationTypes::kSabreAttack || _genAnim == AnimationTypes::kThrowBall || _genAnim == AnimationTypes::kSabreUnknown;
|
||||
}
|
||||
|
||||
bool ActorStruct::isJumpAnimationActive() const {
|
||||
return _genAnim == AnimationTypes::kJump;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
378
engines/twine/scene/actor.h
Normal file
378
engines/twine/scene/actor.h
Normal file
@@ -0,0 +1,378 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_ACTOR_H
|
||||
#define TWINE_SCENE_ACTOR_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/parser/anim.h"
|
||||
#include "twine/parser/body.h"
|
||||
#include "twine/parser/entity.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
/** Total number of sprites allowed in the game */
|
||||
#define NUM_SPRITES 425 // 200 for lba1
|
||||
|
||||
/** Total number of bodies allowed in the game */
|
||||
#define NUM_BODIES 469 // 131 for lba1
|
||||
|
||||
/** Actors move structure */
|
||||
struct RealValue {
|
||||
int16 startValue = 0;
|
||||
int16 endValue = 0;
|
||||
int16 timeValue = 0;
|
||||
int32 memoTicks = 0;
|
||||
|
||||
/**
|
||||
* Get actor real angle
|
||||
* @param time engine time used for interpolation
|
||||
*/
|
||||
int16 getRealValueFromTime(int32 time);
|
||||
int16 getRealAngle(int32 time);
|
||||
};
|
||||
|
||||
/** Actors static flags structure */
|
||||
struct StaticFlagsStruct {
|
||||
uint32 bComputeCollisionWithObj : 1; // 0x000001 CHECK_OBJ_COL
|
||||
uint32 bComputeCollisionWithBricks : 1; // 0x000002 CHECK_BRICK_COL
|
||||
uint32 bCheckZone : 1; // 0x000004 CHECK_ZONE - testing of scenaric areas
|
||||
uint32 bSpriteClip : 1; // 0x000008 SPRITE_CLIP - (doors) fixed clip area
|
||||
uint32 bCanBePushed : 1; // 0x000010 PUSHABLE
|
||||
uint32 bComputeLowCollision : 1; // 0x000020 COL_BASSE
|
||||
uint32 bCanDrown : 1; // 0x000040 CHECK_CODE_JEU
|
||||
uint32 bComputeCollisionWithFloor : 1; // 0x000080 CHECK_WATER_COL
|
||||
uint32 bUnk0100 : 1; // 0x000100
|
||||
uint32 bIsInvisible : 1; // 0x000200 INVISIBLE - not drawn but all computed
|
||||
uint32 bSprite3D : 1; // 0x000400 SPRITE_3D - a sprite not a 3D object
|
||||
uint32 bObjFallable : 1; // 0x000800 OBJ_FALLABLE
|
||||
uint32 bNoShadow : 1; // 0x001000 NO_SHADOW - no auto shadow
|
||||
uint32 bIsBackgrounded : 1; // 0x002000 OBJ_BACKGROUND - is embedded in the decor the 1st time
|
||||
uint32 bIsCarrierActor : 1; // 0x004000 OBJ_CARRIER - can carry and move an obj
|
||||
// take smaller value for bound, or if not set take average for bound
|
||||
uint32 bUseMiniZv : 1; // 0x008000 MINI_ZV - square on smaller dimension (if 3D object)
|
||||
uint32 bHasInvalidPosition : 1; // 0x010000 POS_INVALIDE - carrier considered as an invalid position
|
||||
uint32 bNoElectricShock : 1; // 0x020000 NO_CHOC - does not trigger electric shock animation
|
||||
uint32 bHasSpriteAnim3D : 1; // 0x040000 ANIM_3DS - 3DS animation (extension of 3D sprite)
|
||||
uint32 bNoPreClipping : 1; // 0x080000 NO_PRE_CLIP - does not pre-clip the object (for large objects)
|
||||
uint32 bHasZBuffer : 1; // 0x100000 OBJ_ZBUFFER - displays object in ZBuffer (exterior only!)
|
||||
uint32 bHasZBufferInWater : 1; // 0x200000 OBJ_IN_WATER - displays object in ZBuffer in water (exterior only!)
|
||||
};
|
||||
|
||||
/** Actors dynamic flags structure */
|
||||
struct DynamicFlagsStruct {
|
||||
uint32 bWaitHitFrame : 1; // 0x0001 WAIT_HIT_FRAME - wait for hit frame
|
||||
uint32 bIsHitting : 1; // 0x0002 OK_HIT - hit frame anim
|
||||
uint32 bAnimEnded : 1; // 0x0004 ANIM_END - anim ended in the current loop (will be looped in the next engine loop)
|
||||
uint32 bAnimNewFrame : 1; // 0x0008 NEW_FRAME - new frame anim reached
|
||||
uint32 bWasDrawn : 1; // 0x0010 WAS_DRAWN - actor has been drawn in this loop
|
||||
uint32 bIsDead : 1; // 0x0020 OBJ_DEAD - is dead
|
||||
uint32 bIsSpriteMoving : 1; // 0x0040 AUTO_STOP_DOOR - door is opening or closing (wait to reach the destination position)
|
||||
uint32 bIsRotationByAnim : 1; // 0x0080 ANIM_MASTER_ROT - actor rotation is managed by its animation not by the engine
|
||||
uint32 bIsFalling : 1; // 0x0100 FALLING - is falling on scene
|
||||
uint32 bIsTargetable : 1; // 0x0200 IS_TARGETABLE (lba1) OK_SUPER_HIT (lba2)
|
||||
uint32 bIsBlinking : 1; // 0x0400 IS_BLINKING (lba1) FRAME_SHIELD (lba2)
|
||||
uint32 bWasWalkingBeforeFalling : 1; // 0x0800 DRAW_SHADOW (lba2) - bWasWalkingBeforeFalling in lba1
|
||||
uint32 bANIM_MASTER_GRAVITY : 1; // 0x1000 ANIM_MASTER_GRAVITY (lba2)
|
||||
uint32 bSKATING : 1; // 0x2000 SKATING (lba2) Ouch! I slip in a forbidden collision
|
||||
uint32 bOK_RENVOIE : 1; // 0x4000 OK_RENVOIE (lba2) ready to send back a projectile
|
||||
uint32 bLEFT_JUMP : 1; // 0x8000 LEFT_JUMP (lba2) ready to jump from the left foot
|
||||
uint32 bRIGHT_JUMP : 1; // RIGHT_JUMP (1<<16) // (lba2) ready to jump from the right foot
|
||||
uint32 bWAIT_SUPER_HIT : 1; // WAIT_SUPER_HIT (1<<17) // (lba2) waiting for the end of the animation before giving another super hit
|
||||
uint32 bTRACK_MASTER_ROT : 1; // TRACK_MASTER_ROT (1<<18) // (lba2) it's the track that manages the direction
|
||||
uint32 bFLY_JETPACK : 1; // FLY_JETPACK (1<<19) // (lba2) flying with the Jetpack
|
||||
uint32 bDONT_PICK_CODE_JEU : 1; // DONT_PICK_CODE_JEU (1<<20) // (lba2) Cheat - Conveyor Belt Zones
|
||||
uint32 bMANUAL_INTER_FRAME : 1; // MANUAL_INTER_FRAME (1<<21) // (lba2) Manually performs the ObjectSetInterFrame()
|
||||
uint32 bWAIT_COORD : 1; // WAIT_COORD (1<<22) // (lba2) waiting to have been displayed to pass the coordinates from one point to an extra
|
||||
uint32 bCHECK_FALLING : 1; // CHECK_FALLING (1<<23) // (lba2) forces object to test FALLING during a frame
|
||||
};
|
||||
|
||||
/**
|
||||
* Bonus type flags - a bitfield value, of which the bits mean:
|
||||
* bit 8: clover leaf,
|
||||
* bit 7: small key,
|
||||
* bit 6: magic,
|
||||
* bit 5: life,
|
||||
* bit 4: money,
|
||||
* If more than one type of bonus is selected, the actual type of bonus
|
||||
* will be chosen randomly each time player uses Action.
|
||||
*/
|
||||
struct BonusParameter {
|
||||
uint16 givenNothing : 1;
|
||||
uint16 unk2 : 1; // unused in lba1
|
||||
uint16 unk3 : 1; // unused in lba1
|
||||
uint16 unk4 : 1; // unused in lba1
|
||||
uint16 kashes : 1;
|
||||
uint16 lifepoints : 1;
|
||||
uint16 magicpoints : 1;
|
||||
uint16 key : 1;
|
||||
uint16 cloverleaf : 1;
|
||||
uint16 unused : 7;
|
||||
};
|
||||
|
||||
/**
|
||||
* Actors structure
|
||||
*
|
||||
* Such as characters, doors, moving platforms, invisible actors, ...
|
||||
*/
|
||||
class ActorStruct { // T_OBJET
|
||||
private:
|
||||
ShapeType _col = ShapeType::kNone; // collision
|
||||
bool _brickCausesDamage = false;
|
||||
int32 _maxLife;
|
||||
|
||||
public:
|
||||
ActorStruct(int maxLife = 0) : _lifePoint(maxLife), _maxLife(maxLife) {}
|
||||
StaticFlagsStruct _flags;
|
||||
DynamicFlagsStruct _workFlags;
|
||||
|
||||
EntityData _entityData;
|
||||
inline ShapeType brickShape() const { return _col; }
|
||||
inline void setCollision(ShapeType shapeType) {
|
||||
_col = shapeType;
|
||||
_brickCausesDamage = false;
|
||||
}
|
||||
inline void setBrickCausesDamage() { _brickCausesDamage = true; }
|
||||
inline bool brickCausesDamage() { return _brickCausesDamage; }
|
||||
|
||||
void addLife(int32 val);
|
||||
|
||||
void setLife(int32 val);
|
||||
|
||||
bool isAttackWeaponAnimationActive() const;
|
||||
bool isAttackAnimationActive() const;
|
||||
bool isJumpAnimationActive() const;
|
||||
|
||||
const IVec3 &posObj() const;
|
||||
|
||||
int32 _body = -1; // costumeIndex - index into bodyTable
|
||||
BodyType _genBody = BodyType::btNormal;
|
||||
BodyType _saveGenBody = BodyType::btNormal; // lba2
|
||||
AnimationTypes _genAnim = AnimationTypes::kAnimNone;
|
||||
AnimationTypes _nextGenAnim = AnimationTypes::kStanding;
|
||||
AnimationTypes _ptrAnimAction = AnimationTypes::kAnimNone;
|
||||
int32 _sprite = 0;
|
||||
EntityData *_entityDataPtr = nullptr;
|
||||
|
||||
int16 _actorIdx = 0; // own actor index
|
||||
IVec3 _posObj; // PosObjX, PosObjY, PosObjZ
|
||||
|
||||
// T_ANIM_3DS - Coord.A3DS
|
||||
struct A3DSAnim {
|
||||
int32 Num;
|
||||
int32 Deb;
|
||||
int32 Fin;
|
||||
} A3DS;
|
||||
|
||||
int32 _hitForce = 0;
|
||||
int32 _hitBy = -1;
|
||||
BonusParameter _bonusParameter;
|
||||
int32 _beta = 0; // facing angle of actor. Minumum is 0 (SW). Going counter clock wise
|
||||
int32 _srot = 40; // speed of rotation
|
||||
ControlMode _move = ControlMode::kNoMove; // Move
|
||||
int32 _delayInMillis = 0; // Info
|
||||
int32 _cropLeft = 0; // Info
|
||||
int32 _cropTop = 0; // Info1
|
||||
int32 _cropRight = 0; // Info2
|
||||
int32 _cropBottom = 0; // Info3
|
||||
int32 _followedActor = 0; // same as Info3
|
||||
int32 _bonusAmount = 0;
|
||||
int32 _talkColor = COLOR_BLACK;
|
||||
int32 _armor = 1;
|
||||
int32 _lifePoint = 0;
|
||||
|
||||
/** Process actor coordinate Nxw, Nyw, Nzw */
|
||||
IVec3 _processActor;
|
||||
IVec3 _oldPos; // OldPosX, OldPosY, OldPosZ
|
||||
|
||||
int32 _offsetTrack = -1;
|
||||
uint8 *_ptrTrack = nullptr;
|
||||
int32 _moveScriptSize = 0;
|
||||
|
||||
int32 _offsetLife = 0;
|
||||
int32 _saveOffsetLife = 0; // lba2
|
||||
uint8 *_lifeScript = nullptr;
|
||||
int32 _lifeScriptSize = 0;
|
||||
|
||||
int32 _labelTrack = 0; // script label index
|
||||
int32 _offsetLabelTrack = 0; // pointer to LABEL offset
|
||||
int32 _memoLabelTrack = 0;
|
||||
|
||||
/**
|
||||
* colliding actor id
|
||||
*/
|
||||
int32 _objCol = -1;
|
||||
/**
|
||||
* actor id we are standing on
|
||||
*/
|
||||
int32 _carryBy = -1;
|
||||
int32 _zoneSce = -1;
|
||||
|
||||
int32 _animStepBeta = 0;
|
||||
IVec3 _animStep;
|
||||
int32 _anim = -1;
|
||||
int32 _doorWidth = 0;
|
||||
int32 _frame = 0;
|
||||
AnimType _flagAnim = AnimType::kAnimationTypeRepeat;
|
||||
int32 _spriteActorRotation = 0;
|
||||
uint8 _brickSound = 0U; // CodeJeu
|
||||
int32 SampleAlways = 0; // lba2
|
||||
uint8 SampleVolume = 0; // lba2
|
||||
// SizeSHit contains the number of the brick under the wagon - hack
|
||||
int16 SizeSHit; // lba2 - always square
|
||||
|
||||
// T_OBJ_3D Obj; // lba2
|
||||
// T_GROUP_INFO CurrentFrame[30]; // lba2
|
||||
|
||||
BoundingBox _boundingBox; // Xmin, YMin, Zmin, Xmax, Ymax, Zmax
|
||||
RealValue realAngle;
|
||||
};
|
||||
|
||||
inline const IVec3 &ActorStruct::posObj() const {
|
||||
return _posObj;
|
||||
}
|
||||
|
||||
inline void ActorStruct::addLife(int32 val) {
|
||||
setLife(_lifePoint + val);
|
||||
}
|
||||
|
||||
inline void ActorStruct::setLife(int32 val) {
|
||||
_lifePoint = val;
|
||||
if (_lifePoint > _maxLife) {
|
||||
_lifePoint = _maxLife;
|
||||
}
|
||||
}
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
class Actor {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
/** Hero 3D entity for normal behaviour */
|
||||
EntityData _heroEntityNORMAL;
|
||||
/** Hero 3D entity for athletic behaviour */
|
||||
EntityData _heroEntityATHLETIC;
|
||||
/** Hero 3D entity for aggressive behaviour */
|
||||
EntityData _heroEntityAGGRESSIVE;
|
||||
/** Hero 3D entity for discrete behaviour */
|
||||
EntityData _heroEntityDISCRETE;
|
||||
/** Hero 3D entity for protopack behaviour */
|
||||
EntityData _heroEntityPROTOPACK;
|
||||
|
||||
/**
|
||||
* Initialize 3D actor body
|
||||
* @param bodyIdx 3D actor body index
|
||||
* @param actorIdx 3D actor index
|
||||
*/
|
||||
int32 searchBody(BodyType bodyIdx, int32 actorIdx, ActorBoundingBox &actorBoundingBox);
|
||||
|
||||
void loadBehaviourEntity(ActorStruct *actor, EntityData &entityData, int16 &bodyAnimIndex, int32 index);
|
||||
|
||||
void copyInterAnim(const BodyData &src, BodyData &dest);
|
||||
|
||||
public:
|
||||
Actor(TwinEEngine *engine);
|
||||
|
||||
HeroBehaviourType _heroBehaviour = HeroBehaviourType::kNormal; // Comportement
|
||||
HeroBehaviourType _saveHeroBehaviour = HeroBehaviourType::kNormal; // SaveComportementHero (lba2)
|
||||
/** Hero auto aggressive mode */
|
||||
bool _combatAuto = true;
|
||||
/** Previous Hero behaviour */
|
||||
HeroBehaviourType _previousHeroBehaviour = HeroBehaviourType::kNormal;
|
||||
/** Previous Hero angle */
|
||||
int16 _previousHeroAngle = 0;
|
||||
|
||||
int16 _cropBottomScreen = 0; // TODO: usage differ in original sources
|
||||
|
||||
/** Hero current anim for normal behaviour */
|
||||
int16 _heroAnimIdxNORMAL = 0;
|
||||
/** Hero current anim for athletic behaviour */
|
||||
int16 _heroAnimIdxATHLETIC = 0;
|
||||
/** Hero current anim for aggressive behaviour */
|
||||
int16 _heroAnimIdxAGGRESSIVE = 0;
|
||||
/** Hero current anim for discrete behaviour */
|
||||
int16 _heroAnimIdxDISCRETE = 0;
|
||||
/** Hero current anim for protopack behaviour */
|
||||
int16 _heroAnimIdxPROTOPACK = 0;
|
||||
|
||||
/** Hero anim for behaviour menu */
|
||||
int16 _heroAnimIdx[4];
|
||||
|
||||
void initSprite(int32 spriteNum, int32 actorIdx);
|
||||
void setFrame(int32 actorIdx, uint32 frame);
|
||||
|
||||
/** Restart hero variables while opening new scenes */
|
||||
void restartPerso();
|
||||
|
||||
/** Load hero 3D body and animations */
|
||||
void loadHeroEntities();
|
||||
|
||||
TextId getTextIdForBehaviour() const;
|
||||
|
||||
/**
|
||||
* Set hero behaviour
|
||||
* @param behaviour behaviour value to set
|
||||
*/
|
||||
void setBehaviour(HeroBehaviourType behaviour); // SetComportement
|
||||
|
||||
/**
|
||||
* Initialize 3D actor
|
||||
* @param bodyIdx 3D actor body index
|
||||
* @param actorIdx 3D actor index
|
||||
*/
|
||||
void initBody(BodyType bodyIdx, int16 actorIdx);
|
||||
|
||||
/**
|
||||
* Initialize actors
|
||||
* @param actorIdx actor index to init
|
||||
*/
|
||||
void startInitObj(int16 actorIdx);
|
||||
|
||||
/**
|
||||
* Reset actor
|
||||
* @param actorIdx actor index to init
|
||||
*/
|
||||
void initObject(int16 actorIdx);
|
||||
|
||||
/**
|
||||
* Process hit actor
|
||||
* @param actorIdx actor hitting index
|
||||
* @param actorIdxAttacked actor attacked index
|
||||
* @param strengthOfHit actor hitting strength of hit
|
||||
* @param angle angle of actor hitting
|
||||
*/
|
||||
void hitObj(int32 actorIdx, int32 actorIdxAttacked, int32 strengthOfHit, int32 angle);
|
||||
|
||||
/** Process actor carrier */
|
||||
void checkCarrier(int32 actorIdx);
|
||||
|
||||
/** Process actor extra bonus */
|
||||
void giveExtraBonus(int32 actorIdx);
|
||||
|
||||
// Lba2
|
||||
void posObjectAroundAnother(uint8 numsrc, uint8 numtopos); // PosObjetAroundAnother
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
848
engines/twine/scene/animations.cpp
Normal file
848
engines/twine/scene/animations.cpp
Normal file
@@ -0,0 +1,848 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/animations.h"
|
||||
#include "common/util.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/parser/anim.h"
|
||||
#include "twine/parser/entity.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/extra.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
static const int32 magicLevelStrengthOfHit[] = {
|
||||
MagicballStrengthType::kNoBallStrength,
|
||||
MagicballStrengthType::kYellowBallStrength,
|
||||
MagicballStrengthType::kGreenBallStrength,
|
||||
MagicballStrengthType::kRedBallStrength,
|
||||
MagicballStrengthType::kFireBallStrength,
|
||||
0};
|
||||
|
||||
Animations::Animations(TwinEEngine *engine) : _engine(engine) {
|
||||
}
|
||||
|
||||
int32 Animations::searchAnim(AnimationTypes animIdx, int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
const int32 bodyAnimIndex = actor->_entityDataPtr->getAnimIndex(animIdx);
|
||||
if (bodyAnimIndex != -1) {
|
||||
_currentActorAnimExtraPtr = animIdx;
|
||||
}
|
||||
return bodyAnimIndex;
|
||||
}
|
||||
|
||||
int16 Animations::patchInterAngle(int32 deltaTime, int32 keyFrameLength, int16 newAngle1, int16 lastAngle1) const {
|
||||
const int16 lastAngle = ClampAngle(lastAngle1);
|
||||
const int16 nextAngle = ClampAngle(newAngle1);
|
||||
|
||||
int16 angleDiff = nextAngle - lastAngle;
|
||||
|
||||
int16 computedAngle;
|
||||
if (angleDiff) {
|
||||
if (angleDiff < -LBAAngles::ANGLE_180) {
|
||||
angleDiff += LBAAngles::ANGLE_360;
|
||||
} else if (angleDiff > LBAAngles::ANGLE_180) {
|
||||
angleDiff -= LBAAngles::ANGLE_360;
|
||||
}
|
||||
|
||||
computedAngle = lastAngle + (angleDiff * deltaTime) / keyFrameLength;
|
||||
} else {
|
||||
computedAngle = lastAngle;
|
||||
}
|
||||
|
||||
return ClampAngle(computedAngle);
|
||||
}
|
||||
|
||||
int16 Animations::patchInterStep(int32 deltaTime, int32 keyFrameLength, int16 newPos, int16 lastPos) const {
|
||||
int16 distance = newPos - lastPos;
|
||||
|
||||
int16 computedPos;
|
||||
if (distance) {
|
||||
computedPos = lastPos + (distance * deltaTime) / keyFrameLength;
|
||||
} else {
|
||||
computedPos = lastPos;
|
||||
}
|
||||
|
||||
return computedPos;
|
||||
}
|
||||
|
||||
bool Animations::doSetInterAnimObjet(int32 framedest, const AnimData &animData, BodyData &pBody, AnimTimerDataStruct *ptranimdest, bool global) {
|
||||
if (!pBody.isAnimated()) {
|
||||
return false;
|
||||
}
|
||||
const KeyFrame *keyFrame = animData.getKeyframe(framedest);
|
||||
|
||||
const int16 numBones = pBody.getNumBones();
|
||||
|
||||
int32 numOfBonesInAnim = animData.getNumBoneframes();
|
||||
if (numOfBonesInAnim > numBones) {
|
||||
numOfBonesInAnim = numBones;
|
||||
}
|
||||
const int32 timeDest = keyFrame->length;
|
||||
|
||||
const KeyFrame *lastKeyFramePtr = ptranimdest->ptr;
|
||||
int32 remainingFrameTime = ptranimdest->time;
|
||||
if (lastKeyFramePtr == nullptr) {
|
||||
lastKeyFramePtr = keyFrame;
|
||||
remainingFrameTime = timeDest;
|
||||
}
|
||||
const int32 time = _engine->timerRef - remainingFrameTime;
|
||||
if (time >= timeDest) {
|
||||
ptranimdest->ptr = keyFrame;
|
||||
|
||||
if (global) {
|
||||
ptranimdest->time = _engine->timerRef;
|
||||
|
||||
_animStep.x = keyFrame->x;
|
||||
_animStep.y = keyFrame->y;
|
||||
_animStep.z = keyFrame->z;
|
||||
_animMasterRot = keyFrame->animMasterRot;
|
||||
_animStepAlpha = ToAngle(keyFrame->animStepAlpha);
|
||||
_animStepBeta = ToAngle(keyFrame->animStepBeta);
|
||||
_animStepGamma = ToAngle(keyFrame->animStepGamma);
|
||||
}
|
||||
|
||||
copyKeyFrameToState(keyFrame, pBody, numOfBonesInAnim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (global) {
|
||||
_animStep.x = keyFrame->x;
|
||||
_animStep.y = keyFrame->y;
|
||||
_animStep.z = keyFrame->z;
|
||||
_animMasterRot = keyFrame->animMasterRot;
|
||||
_animStepAlpha = (keyFrame->animStepAlpha * time) / timeDest;
|
||||
_animStepBeta = (keyFrame->animStepBeta * time) / timeDest;
|
||||
_animStepGamma = (keyFrame->animStepGamma * time) / timeDest;
|
||||
}
|
||||
if (numOfBonesInAnim <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16 boneIdx = 1;
|
||||
int16 tmpNumOfPoints = MIN<int16>(lastKeyFramePtr->boneframes.size() - 1, numOfBonesInAnim - 1);
|
||||
do {
|
||||
BoneFrame *boneState = pBody.getBoneState(boneIdx);
|
||||
const BoneFrame &boneFrame = keyFrame->boneframes[boneIdx];
|
||||
const BoneFrame &lastBoneFrame = lastKeyFramePtr->boneframes[boneIdx];
|
||||
|
||||
boneState->type = boneFrame.type;
|
||||
switch (boneFrame.type) {
|
||||
case BoneType::TYPE_ROTATE:
|
||||
boneState->x = patchInterAngle(time, timeDest, boneFrame.x, lastBoneFrame.x);
|
||||
boneState->y = patchInterAngle(time, timeDest, boneFrame.y, lastBoneFrame.y);
|
||||
boneState->z = patchInterAngle(time, timeDest, boneFrame.z, lastBoneFrame.z);
|
||||
break;
|
||||
case BoneType::TYPE_TRANSLATE:
|
||||
case BoneType::TYPE_ZOOM:
|
||||
boneState->x = patchInterStep(time, timeDest, boneFrame.x, lastBoneFrame.x);
|
||||
boneState->y = patchInterStep(time, timeDest, boneFrame.y, lastBoneFrame.y);
|
||||
boneState->z = patchInterStep(time, timeDest, boneFrame.z, lastBoneFrame.z);
|
||||
break;
|
||||
default:
|
||||
error("Unsupported animation rotation mode %d", boneFrame.type);
|
||||
}
|
||||
|
||||
++boneIdx;
|
||||
} while (--tmpNumOfPoints);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Animations::setAnimObjet(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
|
||||
if (!bodyData.isAnimated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 numOfKeyframeInAnim = animData.getKeyframes().size();
|
||||
if (keyframeIdx < 0 || keyframeIdx >= numOfKeyframeInAnim) {
|
||||
return;
|
||||
}
|
||||
|
||||
const KeyFrame *keyFrame = animData.getKeyframe(keyframeIdx);
|
||||
|
||||
_animStep.x = keyFrame->x;
|
||||
_animStep.y = keyFrame->y;
|
||||
_animStep.z = keyFrame->z;
|
||||
|
||||
_animMasterRot = keyFrame->animMasterRot;
|
||||
_animStepBeta = ToAngle(keyFrame->animStepBeta);
|
||||
|
||||
animTimerDataPtr->ptr = animData.getKeyframe(keyframeIdx);
|
||||
animTimerDataPtr->time = _engine->timerRef;
|
||||
|
||||
const int16 numBones = bodyData.getNumBones();
|
||||
|
||||
int16 numOfBonesInAnim = animData.getNumBoneframes();
|
||||
if (numOfBonesInAnim > numBones) {
|
||||
numOfBonesInAnim = numBones;
|
||||
}
|
||||
|
||||
copyKeyFrameToState(keyFrame, bodyData, numOfBonesInAnim);
|
||||
}
|
||||
|
||||
void Animations::stockInterAnim(const BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
|
||||
if (!bodyData.isAnimated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_animKeyframeBufIdx >= ARRAYSIZE(_animKeyframeBuf)) {
|
||||
_animKeyframeBufIdx = 0;
|
||||
}
|
||||
animTimerDataPtr->time = _engine->timerRef;
|
||||
KeyFrame *keyframe = &_animKeyframeBuf[_animKeyframeBufIdx++];
|
||||
animTimerDataPtr->ptr = keyframe;
|
||||
copyStateToKeyFrame(keyframe, bodyData);
|
||||
}
|
||||
|
||||
void Animations::copyStateToKeyFrame(KeyFrame *keyframe, const BodyData &bodyData) const {
|
||||
const int32 numBones = bodyData.getNumBones();
|
||||
keyframe->boneframes.clear();
|
||||
keyframe->boneframes.reserve(numBones);
|
||||
for (int32 i = 0; i < numBones; ++i) {
|
||||
const BoneFrame *boneState = bodyData.getBoneState(i);
|
||||
keyframe->boneframes.push_back(*boneState);
|
||||
}
|
||||
}
|
||||
|
||||
void Animations::copyKeyFrameToState(const KeyFrame *keyframe, BodyData &bodyData, int32 numBones) const {
|
||||
for (int32 i = 0; i < numBones; ++i) {
|
||||
BoneFrame *boneState = bodyData.getBoneState(i);
|
||||
*boneState = keyframe->boneframes[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool Animations::setInterDepObjet(int32 keyframeIdx, const AnimData &animData, AnimTimerDataStruct *animTimerDataPtr) {
|
||||
const KeyFrame *keyFrame = animData.getKeyframe(keyframeIdx);
|
||||
const int32 timeDest = keyFrame->length;
|
||||
|
||||
int32 remainingFrameTime = animTimerDataPtr->time;
|
||||
if (animTimerDataPtr->ptr == nullptr) {
|
||||
remainingFrameTime = timeDest;
|
||||
}
|
||||
|
||||
const int32 time = _engine->timerRef - remainingFrameTime;
|
||||
|
||||
_animMasterRot = keyFrame->animMasterRot;
|
||||
|
||||
if (time >= timeDest) {
|
||||
_animStep.x = keyFrame->x;
|
||||
_animStep.y = keyFrame->y;
|
||||
_animStep.z = keyFrame->z;
|
||||
_animStepAlpha = ToAngle(keyFrame->animStepAlpha);
|
||||
_animStepBeta = ToAngle(keyFrame->animStepBeta);
|
||||
_animStepGamma = ToAngle(keyFrame->animStepGamma);
|
||||
animTimerDataPtr->ptr = animData.getKeyframe(keyframeIdx);
|
||||
animTimerDataPtr->time = _engine->timerRef;
|
||||
return true; // finished animation
|
||||
}
|
||||
|
||||
_animStep.x = (keyFrame->x * time) / timeDest;
|
||||
_animStep.y = (keyFrame->y * time) / timeDest;
|
||||
_animStep.z = (keyFrame->z * time) / timeDest;
|
||||
_animStepAlpha = ToAngle((keyFrame->animStepAlpha * time) / timeDest);
|
||||
_animStepBeta = ToAngle((keyFrame->animStepBeta * time) / timeDest);
|
||||
_animStepGamma = ToAngle((keyFrame->animStepGamma * time) / timeDest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Animations::processAnimActions(int32 actorIdx) { // GereAnimAction
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_entityDataPtr == nullptr || actor->_ptrAnimAction == AnimationTypes::kAnimNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Common::Array<EntityAnim::Action> *actions = actor->_entityDataPtr->getActions(actor->_ptrAnimAction);
|
||||
if (actions == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (const EntityAnim::Action &action : *actions) {
|
||||
debugC(1, TwinE::kDebugAnimation, "Execute animation action %d for actor %d", (int)action.type, actorIdx);
|
||||
switch (action.type) {
|
||||
case ActionType::ACTION_HITTING:
|
||||
if (action.animFrame - 1 == actor->_frame) {
|
||||
actor->_hitForce = action.strength;
|
||||
actor->_workFlags.bIsHitting = 1;
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_SAMPLE:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_sound->mixSample3D(action.sampleIndex, 0x1000, 1, actor->posObj(), actorIdx);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_SAMPLE_FREQ:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(action.frequency) - (action.frequency / 2);
|
||||
_engine->_sound->mixSample3D(action.sampleIndex, pitchBend, 1, actor->posObj(), actorIdx);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_EXTRA_BONUS:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_extra->throwExtra(actorIdx, actor->_posObj.x, actor->_posObj.y + action.yHeight, actor->_posObj.z, action.spriteIndex, action.xAngle, actor->_beta + action.yAngle, action.xRotPoint, action.extraAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_MAGIC_BALL:
|
||||
if (_engine->_gameState->_magicBall == -1 && action.animFrame == actor->_frame) {
|
||||
_engine->_extra->addExtraThrowMagicball(actor->_posObj.x, actor->_posObj.y + action.yHeight, actor->_posObj.z, action.xAngle, actor->_beta + action.yAngle, action.xRotPoint, action.extraAngle);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_SAMPLE_REPEAT:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_sound->mixSample3D(action.sampleIndex, 0x1000, action.repeat, actor->posObj(), actorIdx);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_SEARCH:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_extra->addExtraAiming(actorIdx, actor->_posObj.x, actor->_posObj.y + action.yHeight, actor->_posObj.z, action.spriteIndex, action.targetActor, action.finalAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_ALPHA:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_extra->throwExtra(actorIdx, actor->_posObj.x, actor->_posObj.y + action.yHeight, actor->_posObj.z, action.spriteIndex, action.xAngle, actor->_beta + action.yAngle, action.xRotPoint, action.extraAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_SAMPLE_STOP:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
_engine->_sound->stopSample(action.sampleIndex);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_LEFT_STEP:
|
||||
if (action.animFrame == actor->_frame && (actor->_brickSound & 0xF0U) != 0xF0U) {
|
||||
const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorBegin;
|
||||
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(1000) - 500;
|
||||
_engine->_sound->mixSample3D(sampleIdx, pitchBend, 1, actor->posObj(), actorIdx);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_RIGHT_STEP:
|
||||
if (action.animFrame == actor->_frame && (actor->_brickSound & 0xF0U) != 0xF0U) {
|
||||
const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorRightBegin;
|
||||
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(1000) - 500;
|
||||
_engine->_sound->mixSample3D(sampleIdx, pitchBend, 1, actor->posObj(), actorIdx);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_HERO_HITTING:
|
||||
if (action.animFrame - 1 == actor->_frame) {
|
||||
actor->_hitForce = magicLevelStrengthOfHit[_engine->_gameState->_magicLevelIdx];
|
||||
actor->_workFlags.bIsHitting = 1;
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_3D:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(action.distanceX, action.distanceZ, actor->_beta);
|
||||
|
||||
const int32 throwX = destPos.x + actor->_posObj.x;
|
||||
const int32 throwY = action.distanceY + actor->_posObj.y;
|
||||
const int32 throwZ = destPos.y + actor->_posObj.z;
|
||||
|
||||
_engine->_extra->throwExtra(actorIdx, throwX, throwY, throwZ, action.spriteIndex,
|
||||
action.xAngle, action.yAngle + actor->_beta, action.xRotPoint, action.extraAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_3D_ALPHA:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
const int32 distance = getDistance2D(actor->posObj(), _engine->_scene->_sceneHero->posObj());
|
||||
const int32 newAngle = _engine->_movements->getAngle(actor->_posObj.y, 0, _engine->_scene->_sceneHero->_posObj.y, distance);
|
||||
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(action.distanceX, action.distanceZ, actor->_beta);
|
||||
|
||||
const int32 throwX = destPos.x + actor->_posObj.x;
|
||||
const int32 throwY = action.distanceY + actor->_posObj.y;
|
||||
const int32 throwZ = destPos.y + actor->_posObj.z;
|
||||
|
||||
_engine->_extra->throwExtra(actorIdx, throwX, throwY, throwZ, action.spriteIndex,
|
||||
action.xAngle + newAngle, action.yAngle + actor->_beta, action.xRotPoint, action.extraAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_3D_SEARCH:
|
||||
if (action.animFrame == actor->_frame) {
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(action.distanceX, action.distanceZ, actor->_beta);
|
||||
const int32 x = actor->_posObj.x + destPos.x;
|
||||
const int32 y = actor->_posObj.y + action.distanceY;
|
||||
const int32 z = actor->_posObj.z + destPos.y;
|
||||
_engine->_extra->addExtraAiming(actorIdx, x, y, z, action.spriteIndex,
|
||||
action.targetActor, action.finalAngle, action.strength);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_THROW_3D_MAGIC:
|
||||
if (_engine->_gameState->_magicBall == -1 && action.animFrame == actor->_frame) {
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(action.distanceX, action.distanceZ, actor->_beta);
|
||||
const int32 x = actor->_posObj.x + destPos.x;
|
||||
const int32 y = actor->_posObj.y + action.distanceY;
|
||||
const int32 z = actor->_posObj.z + destPos.y;
|
||||
_engine->_extra->addExtraThrowMagicball(x, y, z, action.xAngle, actor->_beta, action.yAngle, action.finalAngle);
|
||||
}
|
||||
break;
|
||||
case ActionType::ACTION_ZV:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Animations::initAnim(AnimationTypes genNewAnim, AnimType flag, AnimationTypes genNextAnim, int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_body == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor->_flags.bSprite3D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (genNewAnim == actor->_genAnim && actor->_anim != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (genNextAnim == AnimationTypes::kNoAnim && actor->_flagAnim != AnimType::kAnimationAllThen) {
|
||||
genNextAnim = actor->_genAnim;
|
||||
}
|
||||
|
||||
int32 newanim = searchAnim(genNewAnim, actorIdx);
|
||||
|
||||
if (newanim == -1) {
|
||||
newanim = searchAnim(AnimationTypes::kStanding, actorIdx);
|
||||
if (newanim == -1) {
|
||||
error("Could not find anim index for 'standing' (actor %i)", actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if (flag != AnimType::kAnimationSet && actor->_flagAnim == AnimType::kAnimationAllThen) {
|
||||
actor->_nextGenAnim = genNewAnim;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flag == AnimType::kAnimationInsert) {
|
||||
flag = AnimType::kAnimationAllThen;
|
||||
|
||||
genNextAnim = actor->_genAnim;
|
||||
|
||||
if (genNextAnim == AnimationTypes::kThrowBall || genNextAnim == AnimationTypes::kFall || genNextAnim == AnimationTypes::kLanding || genNextAnim == AnimationTypes::kLandingHit) {
|
||||
genNextAnim = AnimationTypes::kStanding;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag == AnimType::kAnimationSet) {
|
||||
flag = AnimType::kAnimationAllThen;
|
||||
}
|
||||
|
||||
BodyData &bodyData = actor->_entityDataPtr->getBody(actor->_body);
|
||||
if (actor->_anim == -1) {
|
||||
// if no previous animation
|
||||
setAnimObjet(0, _engine->_resources->_animData[newanim], bodyData, &bodyData._animTimerData);
|
||||
} else {
|
||||
// interpolation between animations
|
||||
stockInterAnim(bodyData, &bodyData._animTimerData);
|
||||
}
|
||||
|
||||
actor->_anim = newanim;
|
||||
actor->_genAnim = genNewAnim;
|
||||
actor->_nextGenAnim = genNextAnim;
|
||||
actor->_ptrAnimAction = _currentActorAnimExtraPtr;
|
||||
|
||||
actor->_flagAnim = flag;
|
||||
actor->_frame = 0;
|
||||
|
||||
actor->_workFlags.bIsHitting = 0;
|
||||
actor->_workFlags.bAnimEnded = 0;
|
||||
actor->_workFlags.bAnimNewFrame = 1;
|
||||
|
||||
processAnimActions(actorIdx);
|
||||
|
||||
actor->_animStepBeta = LBAAngles::ANGLE_0;
|
||||
actor->_animStep = IVec3();
|
||||
|
||||
debugC(1, TwinE::kDebugAnimation, "Change animation for actor %d to %d", actorIdx, newanim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Animations::doAnim(int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
if (actor->_body == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const IVec3 &oldPos = actor->_oldPos;
|
||||
|
||||
IVec3 &processActor = actor->_processActor;
|
||||
if (actor->_flags.bSprite3D) {
|
||||
if (actor->_hitForce) {
|
||||
actor->_workFlags.bIsHitting = 1;
|
||||
}
|
||||
|
||||
processActor = actor->posObj();
|
||||
|
||||
if (!actor->_workFlags.bIsFalling) {
|
||||
if (actor->_srot) {
|
||||
int32 xAxisRotation = actor->realAngle.getRealValueFromTime(_engine->timerRef);
|
||||
if (!xAxisRotation) {
|
||||
if (actor->realAngle.endValue > 0) {
|
||||
xAxisRotation = 1;
|
||||
} else {
|
||||
xAxisRotation = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const IVec2 xRotPos = _engine->_renderer->rotate(xAxisRotation, 0, actor->_spriteActorRotation);
|
||||
|
||||
processActor.y = actor->_posObj.y - xRotPos.y;
|
||||
|
||||
const IVec2 destPos = _engine->_renderer->rotate(0, xRotPos.x, actor->_beta);
|
||||
|
||||
processActor.x = actor->_posObj.x + destPos.x;
|
||||
processActor.z = actor->_posObj.z + destPos.y;
|
||||
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, actor->_srot, LBAAngles::ANGLE_17, &actor->realAngle);
|
||||
|
||||
if (actor->_workFlags.bIsSpriteMoving) {
|
||||
if (actor->_doorWidth) { // open door
|
||||
if (getDistance2D(processActor.x, processActor.z, actor->_animStep.x, actor->_animStep.z) >= actor->_doorWidth) {
|
||||
if (actor->_beta == LBAAngles::ANGLE_0) { // down
|
||||
processActor.z = actor->_animStep.z + actor->_doorWidth;
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_90) { // right
|
||||
processActor.x = actor->_animStep.x + actor->_doorWidth;
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_180) { // up
|
||||
processActor.z = actor->_animStep.z - actor->_doorWidth;
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_270) { // left
|
||||
processActor.x = actor->_animStep.x - actor->_doorWidth;
|
||||
}
|
||||
|
||||
actor->_workFlags.bIsSpriteMoving = 0;
|
||||
actor->_srot = 0;
|
||||
}
|
||||
} else { // close door
|
||||
bool updatePos = false;
|
||||
|
||||
if (actor->_beta == LBAAngles::ANGLE_0) { // down
|
||||
if (processActor.z <= actor->_animStep.z) {
|
||||
updatePos = true;
|
||||
}
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_90) { // right
|
||||
if (processActor.x <= actor->_animStep.x) {
|
||||
updatePos = true;
|
||||
}
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_180) { // up
|
||||
if (processActor.z >= actor->_animStep.z) {
|
||||
updatePos = true;
|
||||
}
|
||||
} else if (actor->_beta == LBAAngles::ANGLE_270) { // left
|
||||
if (processActor.x >= actor->_animStep.x) {
|
||||
updatePos = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updatePos) {
|
||||
processActor = actor->_animStep;
|
||||
|
||||
actor->_workFlags.bIsSpriteMoving = 0;
|
||||
actor->_srot = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->_flags.bCanBePushed) {
|
||||
processActor += actor->_animStep;
|
||||
|
||||
if (actor->_flags.bUseMiniZv) {
|
||||
processActor.x = ((processActor.x / (SIZE_BRICK_XZ / 4)) * (SIZE_BRICK_XZ / 4));
|
||||
processActor.z = ((processActor.z / (SIZE_BRICK_XZ / 4)) * (SIZE_BRICK_XZ / 4));
|
||||
}
|
||||
|
||||
actor->_animStep = IVec3();
|
||||
}
|
||||
}
|
||||
} else { // 3D actor
|
||||
if (actor->_anim != -1) {
|
||||
const AnimData &animData = _engine->_resources->_animData[actor->_anim];
|
||||
|
||||
bool keyFramePassed = false;
|
||||
BodyData &bodyData = actor->_entityDataPtr->getBody(actor->_body);
|
||||
if (bodyData.isAnimated()) {
|
||||
keyFramePassed = setInterDepObjet(actor->_frame, animData, &bodyData._animTimerData);
|
||||
}
|
||||
|
||||
if (_animMasterRot) {
|
||||
actor->_workFlags.bIsRotationByAnim = 1;
|
||||
} else {
|
||||
actor->_workFlags.bIsRotationByAnim = 0;
|
||||
}
|
||||
|
||||
actor->_beta = ClampAngle(actor->_beta + _animStepBeta - actor->_animStepBeta);
|
||||
actor->_animStepBeta = _animStepBeta;
|
||||
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(_animStep.x, _animStep.z, actor->_beta);
|
||||
|
||||
_animStep.x = destPos.x;
|
||||
_animStep.z = destPos.y;
|
||||
|
||||
processActor = actor->posObj() + _animStep - actor->_animStep;
|
||||
|
||||
actor->_animStep = _animStep;
|
||||
|
||||
actor->_workFlags.bAnimEnded = 0;
|
||||
actor->_workFlags.bAnimNewFrame = 0;
|
||||
|
||||
if (keyFramePassed) {
|
||||
actor->_frame++;
|
||||
actor->_workFlags.bAnimNewFrame = 1;
|
||||
|
||||
// if actor have animation actions to process
|
||||
processAnimActions(actorIdx);
|
||||
|
||||
int16 numKeyframe = actor->_frame;
|
||||
if (numKeyframe == (int16)animData.getNbFramesAnim()) {
|
||||
actor->_workFlags.bIsHitting = 0;
|
||||
|
||||
if (actor->_flagAnim == AnimType::kAnimationTypeRepeat) {
|
||||
actor->_frame = animData.getLoopFrame();
|
||||
} else {
|
||||
actor->_genAnim = actor->_nextGenAnim;
|
||||
actor->_anim = searchAnim(actor->_genAnim, actorIdx);
|
||||
|
||||
if (actor->_anim == -1) {
|
||||
actor->_anim = searchAnim(AnimationTypes::kStanding, actorIdx);
|
||||
actor->_genAnim = AnimationTypes::kStanding;
|
||||
}
|
||||
|
||||
actor->_ptrAnimAction = _currentActorAnimExtraPtr;
|
||||
|
||||
actor->_flagAnim = AnimType::kAnimationTypeRepeat;
|
||||
actor->_frame = 0;
|
||||
actor->_hitForce = 0;
|
||||
}
|
||||
|
||||
processAnimActions(actorIdx);
|
||||
|
||||
actor->_workFlags.bAnimEnded = 1;
|
||||
}
|
||||
|
||||
actor->_animStepBeta = LBAAngles::ANGLE_0;
|
||||
|
||||
actor->_animStep = IVec3();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collision* collision = _engine->_collision;
|
||||
// actor standing on another actor
|
||||
if (actor->_carryBy != -1) {
|
||||
const ActorStruct *standOnActor = _engine->_scene->getActor(actor->_carryBy);
|
||||
processActor -= standOnActor->_oldPos;
|
||||
processActor += standOnActor->posObj();
|
||||
|
||||
if (!collision->checkZvOnZv(actorIdx, actor->_carryBy)) {
|
||||
actor->_carryBy = -1; // no longer standing on other actor
|
||||
}
|
||||
}
|
||||
|
||||
// actor falling Y speed
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
processActor = oldPos;
|
||||
processActor.y += _engine->_stepFalling; // add step to fall
|
||||
}
|
||||
|
||||
// actor collisions with bricks
|
||||
uint32 col1 = 0; /** Cause damage in current processed actor */
|
||||
if (actor->_flags.bComputeCollisionWithBricks) {
|
||||
ShapeType col = _engine->_grid->worldColBrick(oldPos);
|
||||
|
||||
if (col != ShapeType::kNone) {
|
||||
if (col == ShapeType::kSolid) {
|
||||
processActor.y = (processActor.y / SIZE_BRICK_Y) * SIZE_BRICK_Y + SIZE_BRICK_Y; // go upper
|
||||
actor->_posObj.y = processActor.y;
|
||||
} else {
|
||||
collision->reajustPos(processActor, col);
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->_flags.bComputeCollisionWithObj) {
|
||||
collision->checkObjCol(actorIdx);
|
||||
}
|
||||
|
||||
if (actor->_carryBy != -1 && actor->_workFlags.bIsFalling) {
|
||||
collision->receptionObj(actorIdx);
|
||||
}
|
||||
|
||||
collision->setCollisionPos(processActor);
|
||||
|
||||
if (IS_HERO(actorIdx) && !actor->_flags.bComputeLowCollision) {
|
||||
// check hero collisions with bricks
|
||||
col1 |= collision->doCornerReajustTwinkel(actor, actor->_boundingBox.mins.x, actor->_boundingBox.mins.y, actor->_boundingBox.mins.z, 1);
|
||||
col1 |= collision->doCornerReajustTwinkel(actor, actor->_boundingBox.maxs.x, actor->_boundingBox.mins.y, actor->_boundingBox.mins.z, 2);
|
||||
col1 |= collision->doCornerReajustTwinkel(actor, actor->_boundingBox.maxs.x, actor->_boundingBox.mins.y, actor->_boundingBox.maxs.z, 4);
|
||||
col1 |= collision->doCornerReajustTwinkel(actor, actor->_boundingBox.mins.x, actor->_boundingBox.mins.y, actor->_boundingBox.maxs.z, 8);
|
||||
} else {
|
||||
// check other actors collisions with bricks
|
||||
col1 |= collision->doCornerReajust(actor, actor->_boundingBox.mins.x, actor->_boundingBox.mins.y, actor->_boundingBox.mins.z, 1);
|
||||
col1 |= collision->doCornerReajust(actor, actor->_boundingBox.maxs.x, actor->_boundingBox.mins.y, actor->_boundingBox.mins.z, 2);
|
||||
col1 |= collision->doCornerReajust(actor, actor->_boundingBox.maxs.x, actor->_boundingBox.mins.y, actor->_boundingBox.maxs.z, 4);
|
||||
col1 |= collision->doCornerReajust(actor, actor->_boundingBox.mins.x, actor->_boundingBox.mins.y, actor->_boundingBox.maxs.z, 8);
|
||||
}
|
||||
|
||||
// process wall hit while running
|
||||
if (col1 && !actor->_workFlags.bIsFalling && IS_HERO(actorIdx) && _engine->_actor->_heroBehaviour == HeroBehaviourType::kAthletic && actor->_genAnim == AnimationTypes::kForward) {
|
||||
IVec2 destPos = _engine->_renderer->rotate(actor->_boundingBox.mins.x, actor->_boundingBox.mins.z, actor->_beta + LBAAngles::ANGLE_315 + LBAAngles::ANGLE_180);
|
||||
|
||||
destPos.x += processActor.x;
|
||||
destPos.y += processActor.z;
|
||||
|
||||
if (destPos.x >= 0 && destPos.y >= 0 && destPos.x <= SCENE_SIZE_MAX && destPos.y <= SCENE_SIZE_MAX) {
|
||||
if (_engine->_grid->worldColBrick(destPos.x, processActor.y + SIZE_BRICK_Y, destPos.y) != ShapeType::kNone && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
|
||||
_engine->_extra->initSpecial(actor->_posObj.x, actor->_posObj.y + 1000, actor->_posObj.z, ExtraSpecialType::kHitStars);
|
||||
initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx);
|
||||
|
||||
if (IS_HERO(actorIdx)) {
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
}
|
||||
|
||||
actor->addLife(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
col = _engine->_grid->worldColBrick(processActor);
|
||||
actor->setCollision(col);
|
||||
|
||||
if (col != ShapeType::kNone) {
|
||||
if (col == ShapeType::kSolid) {
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
collision->receptionObj(actorIdx);
|
||||
processActor.y = (collision->_collision.y * SIZE_BRICK_Y) + SIZE_BRICK_Y;
|
||||
} else {
|
||||
if (IS_HERO(actorIdx) && _engine->_actor->_heroBehaviour == HeroBehaviourType::kAthletic && actor->_genAnim == AnimationTypes::kForward && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
|
||||
_engine->_extra->initSpecial(actor->_posObj.x, actor->_posObj.y + 1000, actor->_posObj.z, ExtraSpecialType::kHitStars);
|
||||
initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx);
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
actor->addLife(-1);
|
||||
}
|
||||
|
||||
// no Z coordinate issue
|
||||
if (_engine->_grid->worldColBrick(processActor.x, processActor.y, oldPos.z) != ShapeType::kNone) {
|
||||
if (_engine->_grid->worldColBrick(oldPos.x, processActor.y, processActor.z) != ShapeType::kNone) {
|
||||
return;
|
||||
} else {
|
||||
processActor.x = oldPos.x;
|
||||
}
|
||||
} else {
|
||||
processActor.z = oldPos.z;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
collision->receptionObj(actorIdx);
|
||||
}
|
||||
|
||||
collision->reajustPos(processActor, col);
|
||||
}
|
||||
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
debugC(1, TwinE::kDebugCollision, "Actor %d reset falling", actorIdx);
|
||||
}
|
||||
actor->_workFlags.bIsFalling = 0;
|
||||
} else {
|
||||
if (actor->_flags.bObjFallable && actor->_carryBy == -1) {
|
||||
col = _engine->_grid->worldColBrick(processActor.x, processActor.y - 1, processActor.z);
|
||||
|
||||
if (col != ShapeType::kNone) {
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
collision->receptionObj(actorIdx);
|
||||
}
|
||||
|
||||
collision->reajustPos(processActor, col);
|
||||
} else {
|
||||
if (!actor->_workFlags.bIsRotationByAnim) {
|
||||
debugC(1, TwinE::kDebugCollision, "Actor %d is falling", actorIdx);
|
||||
actor->_workFlags.bIsFalling = 1;
|
||||
|
||||
if (IS_HERO(actorIdx) && _engine->_scene->_startYFalling == 0) {
|
||||
_engine->_scene->_startYFalling = processActor.y;
|
||||
int32 y = processActor.y - 1 - SIZE_BRICK_Y;
|
||||
while (y > 0 && ShapeType::kNone == _engine->_grid->worldColBrick(processActor.x, y, processActor.z)) {
|
||||
y -= SIZE_BRICK_Y;
|
||||
}
|
||||
|
||||
y = (y + SIZE_BRICK_Y) & ~(SIZE_BRICK_Y - 1);
|
||||
int32 fallHeight = processActor.y - y;
|
||||
|
||||
if (fallHeight <= (2 * SIZE_BRICK_Y) && actor->_genAnim == AnimationTypes::kForward) {
|
||||
actor->_workFlags.bWasWalkingBeforeFalling = 1;
|
||||
} else {
|
||||
initAnim(AnimationTypes::kFall, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
} else {
|
||||
initAnim(AnimationTypes::kFall, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if under the map, than die
|
||||
if (collision->_collision.y == -1) {
|
||||
actor->setLife(0);
|
||||
}
|
||||
} else {
|
||||
if (actor->_flags.bComputeCollisionWithObj) {
|
||||
collision->checkObjCol(actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if (col1) {
|
||||
actor->setBrickCausesDamage();
|
||||
}
|
||||
|
||||
// check and fix actor bounding position
|
||||
if (processActor.x < 0) {
|
||||
processActor.x = 0;
|
||||
}
|
||||
|
||||
if (processActor.y < 0) {
|
||||
processActor.y = 0;
|
||||
}
|
||||
|
||||
if (processActor.z < 0) {
|
||||
processActor.z = 0;
|
||||
}
|
||||
|
||||
if (processActor.x > SCENE_SIZE_MAX) {
|
||||
processActor.x = SCENE_SIZE_MAX;
|
||||
}
|
||||
|
||||
if (processActor.z > SCENE_SIZE_MAX) {
|
||||
processActor.z = SCENE_SIZE_MAX;
|
||||
}
|
||||
|
||||
actor->_posObj = processActor;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
133
engines/twine/scene/animations.h
Normal file
133
engines/twine/scene/animations.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_ANIMATIONS_H
|
||||
#define TWINE_SCENE_ANIMATIONS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/parser/anim.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
struct AnimTimerDataStruct;
|
||||
class BodyData;
|
||||
class TwinEEngine;
|
||||
|
||||
class Animations {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
int16 patchInterAngle(int32 deltaTime, int32 keyFrameLength, int16 newAngle1, int16 lastAngle1) const;
|
||||
int16 patchInterStep(int32 deltaTime, int32 keyFrameLength, int16 newPos, int16 lastPos) const;
|
||||
|
||||
/**
|
||||
* Verify animation at keyframe
|
||||
* @param keyframeIdx Animation key frame index
|
||||
* @param animData Animation data
|
||||
* @param animTimerDataPtr Animation time data
|
||||
*/
|
||||
bool setInterDepObjet(int32 keyframeIdx, const AnimData &animData, AnimTimerDataStruct *animTimerDataPtr);
|
||||
|
||||
void copyKeyFrameToState(const KeyFrame *keyframe, BodyData &bodyData, int32 numBones) const;
|
||||
void copyStateToKeyFrame(KeyFrame *keyframe, const BodyData &bodyData) const;
|
||||
|
||||
bool doSetInterAnimObjet(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPt, bool global);
|
||||
|
||||
int _animKeyframeBufIdx = 0;
|
||||
KeyFrame _animKeyframeBuf[32];
|
||||
|
||||
/** Rotation by anim and not by engine */
|
||||
int16 _animMasterRot = 0;
|
||||
/** Last rotation angle */
|
||||
int16 _animStepBeta = 0;
|
||||
int16 _animStepAlpha = 0;
|
||||
int16 _animStepGamma = 0;
|
||||
|
||||
/** Current step coordinates */
|
||||
IVec3 _animStep;
|
||||
|
||||
public:
|
||||
Animations(TwinEEngine *engine);
|
||||
|
||||
/** Current actor anim extra pointer */
|
||||
AnimationTypes _currentActorAnimExtraPtr = AnimationTypes::kAnimNone;
|
||||
|
||||
/**
|
||||
* Set animation keyframe
|
||||
* @param keyframIdx Animation keyframe index
|
||||
* @param animData Animation data
|
||||
* @param bodyData Body model data
|
||||
* @param animTimerDataPtr Animation time data
|
||||
*/
|
||||
void setAnimObjet(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr);
|
||||
|
||||
/**
|
||||
* Set new body animation
|
||||
* @param keyframeIdx Animation key frame index
|
||||
* @param animData Animation data
|
||||
* @param bodyData Body model data
|
||||
* @param animTimerDataPtr Animation time data
|
||||
*/
|
||||
bool setInterAnimObjet(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
|
||||
return doSetInterAnimObjet(keyframeIdx, animData, bodyData, animTimerDataPtr, true);
|
||||
}
|
||||
|
||||
bool setInterAnimObjet2(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
|
||||
return doSetInterAnimObjet(keyframeIdx, animData, bodyData, animTimerDataPtr, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entity anim index (This is taken from File3D entities)
|
||||
* @param animIdx Entity animation index
|
||||
* @param actorIdx Actor index
|
||||
*/
|
||||
int32 searchAnim(AnimationTypes animIdx, int32 actorIdx = OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
/**
|
||||
* Stock animation - copy the next keyFrame from a different buffer
|
||||
* @param bodyData Body model data
|
||||
* @param animTimerDataPtr Animation time data
|
||||
*/
|
||||
void stockInterAnim(const BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr);
|
||||
|
||||
/**
|
||||
* Initialize animation
|
||||
* @param newAnim animation to init
|
||||
* @param animType animation type
|
||||
* @param animExtra animation actions extra data
|
||||
* @param actorIdx actor index
|
||||
*/
|
||||
bool initAnim(AnimationTypes newAnim, AnimType animType, AnimationTypes animExtra, int32 actorIdx); // InitAnim
|
||||
|
||||
/**
|
||||
* Process acotr animation actions
|
||||
* @param actorIdx Actor index
|
||||
*/
|
||||
void processAnimActions(int32 actorIdx);
|
||||
|
||||
/**
|
||||
* Process main loop actor animations
|
||||
* @param actorIdx Actor index
|
||||
*/
|
||||
void doAnim(int32 actorIdx);
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
#endif
|
||||
709
engines/twine/scene/buggy.cpp
Normal file
709
engines/twine/scene/buggy.cpp
Normal file
@@ -0,0 +1,709 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/buggy.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
#define MAX_SAMPLE_PITCH 11000
|
||||
#define MIN_SAMPLE_PITCH2 5000
|
||||
#define MAX_SAMPLE_PITCH2 8500
|
||||
#define MAX_SPEED 3800
|
||||
#define TEMPO_GEAR 1200 // speed change
|
||||
#define SAMPLE_BUGGY 109
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
void Buggy::initBuggy(uint8 numobj, uint32 flaginit) {
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(numobj);
|
||||
|
||||
// So that the objects follow their tracks without being interrupted
|
||||
// by the buggy (too bad, it will be pushed)
|
||||
ptrobj->_flags.bCanBePushed = true;
|
||||
ptrobj->_flags.bCanDrown = true;
|
||||
|
||||
if (flaginit == 2 // we force the repositioning of the buggy
|
||||
|| (flaginit && !NumBuggy)) // first initialization
|
||||
// because the empty buggy cannot be Twinsen
|
||||
{
|
||||
ptb->Cube = _engine->_scene->_numCube; // Port-Ludo (Desert)
|
||||
|
||||
ptb->X = ptrobj->_posObj.x;
|
||||
ptb->Y = ptrobj->_posObj.y;
|
||||
ptb->Z = ptrobj->_posObj.z;
|
||||
|
||||
ptb->Beta = ptrobj->_beta;
|
||||
|
||||
_engine->_actor->initBody(BodyType::btNormal, numobj);
|
||||
|
||||
NumBuggy = (uint8)(numobj | BUGGY_PRESENT);
|
||||
} else if (NumBuggy) {
|
||||
if (_engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX)->_move != ControlMode::kBuggyManual && _engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX)->_move != ControlMode::kBuggy) {
|
||||
int32 x, y;
|
||||
|
||||
if (_engine->_scene->_numCube == ptb->Cube) {
|
||||
ptrobj->_posObj.x = ptb->X;
|
||||
ptrobj->_posObj.y = ptb->Y;
|
||||
ptrobj->_posObj.z = ptb->Z;
|
||||
|
||||
ptrobj->_beta = ptb->Beta;
|
||||
|
||||
_engine->_actor->initBody(BodyType::btNormal, numobj);
|
||||
} else if (_engine->_scene->loadSceneCubeXY(ptb->Cube, &x, &y)) {
|
||||
x -= _engine->_scene->_currentCubeX;
|
||||
y -= _engine->_scene->_currentCubeY;
|
||||
|
||||
ptrobj->_posObj.x = ptb->X + x * 32768;
|
||||
ptrobj->_posObj.y = ptb->Y;
|
||||
ptrobj->_posObj.z = ptb->Z + y * 32768;
|
||||
|
||||
ptrobj->_beta = ptb->Beta;
|
||||
|
||||
ptrobj->_flags.bNoShadow = 1;
|
||||
ptrobj->_flags.bIsBackgrounded = 1;
|
||||
ptrobj->_flags.bNoElectricShock = 1;
|
||||
ptrobj->_flags.bHasZBuffer = 1;
|
||||
|
||||
_engine->_actor->initBody(BodyType::btNormal, numobj);
|
||||
} else {
|
||||
_engine->_actor->initBody(BodyType::btNone, numobj);
|
||||
}
|
||||
|
||||
_engine->_movements->clearRealAngle(ptrobj);
|
||||
} else {
|
||||
_engine->_actor->initBody(BodyType::btNone, numobj);
|
||||
}
|
||||
|
||||
NumBuggy = (uint8)(numobj | BUGGY_PRESENT);
|
||||
} else {
|
||||
_engine->_actor->initBody(BodyType::btNone, numobj);
|
||||
}
|
||||
}
|
||||
|
||||
void Buggy::resetBuggy() {
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
|
||||
NumBuggy = 0;
|
||||
ptb->Cube = 0;
|
||||
}
|
||||
|
||||
void Buggy::takeBuggy() {
|
||||
int32 sample;
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX);
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
|
||||
ptb->SpeedRot = 1024;
|
||||
// TODO: ptb->LastTimer = TimerRefHR;
|
||||
|
||||
// TODO: ObjectClear(&ptrobj);
|
||||
|
||||
// Shielding in case the Buggy moved (being pushed, for example).
|
||||
ptb->X = _engine->_scene->getActor(NUM_BUGGY)->_posObj.x;
|
||||
ptb->Y = _engine->_scene->getActor(NUM_BUGGY)->_posObj.y;
|
||||
ptb->Z = _engine->_scene->getActor(NUM_BUGGY)->_posObj.z;
|
||||
|
||||
ptrobj->_posObj.x = ptb->X;
|
||||
ptrobj->_posObj.y = ptb->Y;
|
||||
ptrobj->_posObj.z = ptb->Z;
|
||||
ptrobj->_beta = ptb->Beta;
|
||||
_engine->_movements->clearRealAngle(ptrobj); // To avoid crushing the beta.
|
||||
|
||||
ptrobj->_workFlags.bMANUAL_INTER_FRAME = true;
|
||||
ptrobj->_flags.bHasZBuffer = true;
|
||||
|
||||
// TODO: _engine->_actor->setBehaviour(HeroBehaviourType::kBUGGY);
|
||||
|
||||
// Switch Buggy Scenario to NoBody.
|
||||
_engine->_actor->initBody(BodyType::btNone, NUM_BUGGY);
|
||||
|
||||
if (ptrobj->SampleAlways) {
|
||||
_engine->_sound->stopSample(ptrobj->SampleAlways);
|
||||
ptrobj->SampleAlways = 0;
|
||||
}
|
||||
|
||||
sample = SAMPLE_BUGGY;
|
||||
|
||||
if (_engine->_sound->isSamplePlaying(sample)) {
|
||||
_engine->_sound->stopSample(sample);
|
||||
}
|
||||
|
||||
ptrobj->SampleVolume = 20;
|
||||
|
||||
// TODO: ParmSampleVolume = ptrobj->SampleVolume;
|
||||
|
||||
Gear = 0;
|
||||
TimerGear = 0;
|
||||
|
||||
// TODO: ptrobj->SampleAlways = _engine->_sound->playSample(SAMPLE_BUGGY, 4096, 0, 0,
|
||||
// ptrobj->_posObj.x, ptrobj->_posObj.y, ptrobj->_posObj.z);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void ObjectClear(T_OBJ *ptb3d) {
|
||||
memset(ptb3d, 0, sizeof(T_OBJ));
|
||||
ptb3d.OBJ_3D.Body, -1
|
||||
ptb3d.OBJ_3D.NextBody, -1
|
||||
ptb3d.OBJ_3D.Texture, -1
|
||||
ptb3d.OBJ_3D.NextTexture, -1
|
||||
ptb3d.OBJ_3D.Anim, -1
|
||||
}
|
||||
#endif
|
||||
|
||||
void Buggy::leaveBuggy(HeroBehaviourType behaviour) {
|
||||
int32 sample;
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX);
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
|
||||
sample = SAMPLE_BUGGY;
|
||||
|
||||
if (_engine->_sound->isSamplePlaying(sample)) {
|
||||
_engine->_sound->stopSample(sample);
|
||||
ptrobj->SampleAlways = 0;
|
||||
}
|
||||
|
||||
ptb->X = ptrobj->_posObj.x;
|
||||
ptb->Y = ptrobj->_posObj.y;
|
||||
ptb->Z = ptrobj->_posObj.z;
|
||||
ptb->Beta = ptrobj->_beta;
|
||||
ptb->Cube = _engine->_scene->_numCube;
|
||||
|
||||
// TODO: ObjectClear(ptrobj);
|
||||
|
||||
ptrobj->_workFlags.bMANUAL_INTER_FRAME = 0;
|
||||
ptrobj->_flags.bHasZBuffer = 0;
|
||||
|
||||
_engine->_actor->initBody(BodyType::btTunic, OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
_engine->_actor->setBehaviour(behaviour);
|
||||
|
||||
// Restore scenario buggy.
|
||||
|
||||
ptrobj = _engine->_scene->getActor(NUM_BUGGY);
|
||||
|
||||
ptrobj->_posObj.x = ptb->X;
|
||||
ptrobj->_posObj.y = ptb->Y;
|
||||
ptrobj->_posObj.z = ptb->Z;
|
||||
ptrobj->_beta = ptb->Beta;
|
||||
|
||||
ptrobj->_brickSound = _engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX)->_brickSound;
|
||||
|
||||
_engine->_movements->clearRealAngle(ptrobj); // To avoid crushing the beta
|
||||
|
||||
_engine->_actor->initBody(BodyType::btNormal, NUM_BUGGY);
|
||||
|
||||
// Search for a free position for Twinsen nearby.
|
||||
_engine->_actor->posObjectAroundAnother(NUM_BUGGY, OWN_ACTOR_SCENE_INDEX);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
struct T_HALF_POLY {
|
||||
uint32 Bank : 4; // coul bank poly
|
||||
uint32 TexFlag : 2; // flag texture 00 rien 01 triste 10 flat 11 gouraud
|
||||
uint32 PolyFlag : 2; // flag poly 00 rien 01 flat 10 gouraud 11 dither
|
||||
uint32 SampleStep : 4; // sample pas twinsen
|
||||
uint32 CodeJeu : 4; // code jeu
|
||||
uint32 Sens : 1; // sens diagonale
|
||||
uint32 Col : 1;
|
||||
uint32 Dummy : 1;
|
||||
uint32 IndexTex : 13; // index texture 8192
|
||||
}; // 1 long
|
||||
|
||||
struct T_HALF_TEX {
|
||||
uint16 Tx0;
|
||||
uint16 Ty0;
|
||||
uint16 Tx1;
|
||||
uint16 Ty1;
|
||||
uint16 Tx2;
|
||||
uint16 Ty2;
|
||||
}; // 2 Longs
|
||||
|
||||
int32 CalculAltitudeObjet(int32 x, int32 z, int32 cj) {
|
||||
int32 y0, y1, y2, y3;
|
||||
int32 dx, dz;
|
||||
int32 dz0, dz1;
|
||||
|
||||
dx = x >> 9; // div512
|
||||
dz = z >> 9; // div512
|
||||
|
||||
if ((dx < 0) || (dx > 63))
|
||||
return -1;
|
||||
if ((dz < 0) || (dz > 63))
|
||||
return -1;
|
||||
|
||||
x &= 511;
|
||||
z &= 511;
|
||||
|
||||
dz0 = dz * 65;
|
||||
dz1 = dz0 + 65;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
if (cj == -1) {
|
||||
T_HALF_POLY *mappoly = &MapPolyGround[dz * 64 * 2 + dx * 2];
|
||||
|
||||
if (mappoly->Sens == 0) // poly séparé par ligne reliant point 0 et 2
|
||||
{
|
||||
if (x >= z) // poly de droite
|
||||
{
|
||||
mappoly++;
|
||||
}
|
||||
} else // poly séparé par ligne reliant point 1 et 3
|
||||
{
|
||||
if (511 - x <= z) // poly de droite
|
||||
{
|
||||
mappoly++;
|
||||
}
|
||||
}
|
||||
|
||||
cj = (uint8)mappoly->CodeJeu;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
y0 = MapSommetY[dz0 + dx];
|
||||
y1 = MapSommetY[dz1 + dx];
|
||||
y2 = MapSommetY[dz1 + (dx + 1)];
|
||||
y3 = MapSommetY[dz0 + (dx + 1)];
|
||||
|
||||
if (cj == CJ_FOOT_WATER OR cj == CJ_WATER) {
|
||||
uint8 *i = &MapIntensity[dz0 + dx];
|
||||
|
||||
y0 += (i[0] >> 4) * -200;
|
||||
y1 += (i[65] >> 4) * -200;
|
||||
y2 += (i[65 + 1] >> 4) * -200;
|
||||
y3 += (i[1] >> 4) * -200;
|
||||
}
|
||||
if (MapPolyGround[dz * 64 * 2 + dx * 2].Sens == 0) // poly séparé par ligne reliant point 0 et 2
|
||||
{
|
||||
if (x < z) // poly de gauche
|
||||
{
|
||||
return (y0 + ((y1 - y0) * z + (y2 - y1) * x) / 512);
|
||||
} else // poly de droite
|
||||
{
|
||||
return (y0 + ((y3 - y0) * x + (y2 - y3) * z) / 512);
|
||||
}
|
||||
} else // poly séparé par ligne reliant point 1 et 3
|
||||
{
|
||||
if (511 - x > z) // poly de gauche
|
||||
{
|
||||
return (y0 + ((y3 - y0) * x + (y1 - y0) * z) / 512);
|
||||
} else // poly de droite
|
||||
{
|
||||
return (y1 + ((y2 - y1) * x + (y3 - y2) * (511 - z)) / 512);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Buggy::doAnimBuggy(ActorStruct *ptrobj) {
|
||||
#if 0
|
||||
int32 x1, y1, z1, yw;
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
T_OBJ_3D *ptb3d = &ptrobj->Obj;
|
||||
|
||||
// wheels rot
|
||||
int32 c, d;
|
||||
|
||||
int32 x, y, z;
|
||||
|
||||
// Trick to avoid crushing the groups in AffOneObject().
|
||||
ObjectSetInterFrame(ptb3d);
|
||||
|
||||
if (ptrobj->_workFlags.bIsFalling || ptrobj->_workFlags.bANIM_MASTER_GRAVITY) {
|
||||
return;
|
||||
}
|
||||
|
||||
LongRotate(0, ptb->SpeedInc * 1024, ptb3d->Beta);
|
||||
ptrobj->_processActor.x = ptb3d->X + X0 / 1024;
|
||||
ptrobj->_processActor.z = ptb3d->Z + Z0 / 1024;
|
||||
|
||||
// Ideal altitude
|
||||
yw = CalculAltitudeObjet(ptrobj->_processActor.x, ptrobj->_processActor.z, -1); // Ground Y for XZ
|
||||
|
||||
// test altitude #2: Forbidden triangles
|
||||
|
||||
// Front left wheel direction
|
||||
ptb3d->CurrentFrame[3].Beta = (int16)ptb->BetaWheel;
|
||||
// Front right wheel direction
|
||||
ptb3d->CurrentFrame[6].Beta = (int16)ptb->BetaWheel;
|
||||
|
||||
// Management of 4 separate wheels.
|
||||
|
||||
// front right wheel
|
||||
|
||||
LongRotate(-400, 400, ptb3d->Beta);
|
||||
x = ptrobj->_processActor.x + X0;
|
||||
z = ptrobj->_processActor.z + Z0;
|
||||
y = yw;
|
||||
|
||||
if (x >= 0 && x < 32768 && z >= 0 && z < 32768) {
|
||||
y += CalculAltitudeObjet(x, z, -1);
|
||||
}
|
||||
|
||||
c = (260 * 31415) / 1000; // Circumference * 10
|
||||
d = Distance3D(ptb->Wheel[0].X, ptb->Wheel[0].Y, ptb->Wheel[0].Z, x, y, z);
|
||||
|
||||
if (ptb->Speed >= 0) {
|
||||
ptb->Wheel[0].Angle += (4096 * 10 * d) / c;
|
||||
} else {
|
||||
ptb->Wheel[0].Angle -= (4096 * 10 * d) / c;
|
||||
}
|
||||
|
||||
ptb->Wheel[0].X = x;
|
||||
ptb->Wheel[0].Y = y;
|
||||
ptb->Wheel[0].Z = z;
|
||||
|
||||
// front left wheel
|
||||
|
||||
LongRotate(400, 400, ptb3d->Beta);
|
||||
x = ptrobj->_processActor.x + X0;
|
||||
z = ptrobj->_processActor.z + Z0;
|
||||
y = yw;
|
||||
|
||||
if (x >= 0 && x < 32768 && z >= 0 && z < 32768) {
|
||||
y += CalculAltitudeObjet(x, z, -1);
|
||||
}
|
||||
|
||||
c = (260 * 31415) / 1000; // Circumference * 10
|
||||
d = Distance3D(ptb->Wheel[1].X, ptb->Wheel[1].Y, ptb->Wheel[1].Z, x, y, z);
|
||||
|
||||
if (ptb->Speed >= 0) {
|
||||
ptb->Wheel[1].Angle += (4096 * 10 * d) / c;
|
||||
} else {
|
||||
ptb->Wheel[1].Angle -= (4096 * 10 * d) / c;
|
||||
}
|
||||
|
||||
ptb->Wheel[1].X = x;
|
||||
ptb->Wheel[1].Y = y;
|
||||
ptb->Wheel[1].Z = z;
|
||||
|
||||
// back left wheel
|
||||
|
||||
LongRotate(400, -350, ptb3d->Beta);
|
||||
x = ptrobj->_processActor.x + X0;
|
||||
z = ptrobj->_processActor.z + Z0;
|
||||
y = yw;
|
||||
|
||||
if (x >= 0 && x < 32768 && z >= 0 && z < 32768) {
|
||||
y += CalculAltitudeObjet(x, z, -1);
|
||||
}
|
||||
|
||||
c = (360 * 31415) / 1000; // Circumference * 10
|
||||
d = Distance3D(ptb->Wheel[2].X, ptb->Wheel[2].Y, ptb->Wheel[2].Z, x, y, z);
|
||||
|
||||
if (ptb->Speed >= 0) {
|
||||
ptb->Wheel[2].Angle += (4096 * 10 * d) / c;
|
||||
} else {
|
||||
ptb->Wheel[2].Angle -= (4096 * 10 * d) / c;
|
||||
}
|
||||
|
||||
ptb->Wheel[2].X = x;
|
||||
ptb->Wheel[2].Y = y;
|
||||
ptb->Wheel[2].Z = z;
|
||||
|
||||
// back right wheel
|
||||
|
||||
LongRotate(-400, -350, ptb3d->Beta);
|
||||
x = ptrobj->_processActor.x + X0;
|
||||
z = ptrobj->_processActor.z + Z0;
|
||||
y = yw;
|
||||
|
||||
if (x >= 0 && x < 32768 && z >= 0 && z < 32768) {
|
||||
y += CalculAltitudeObjet(x, z, -1);
|
||||
}
|
||||
|
||||
c = (360 * 31415) / 1000; // Circumference * 10
|
||||
d = Distance3D(ptb->Wheel[3].X, ptb->Wheel[3].Y, ptb->Wheel[3].Z, x, y, z);
|
||||
|
||||
if (ptb->Speed >= 0) {
|
||||
ptb->Wheel[3].Angle += (4096 * 10 * d) / c;
|
||||
} else {
|
||||
ptb->Wheel[3].Angle -= (4096 * 10 * d) / c;
|
||||
}
|
||||
|
||||
ptb->Wheel[3].X = x;
|
||||
ptb->Wheel[3].Y = y;
|
||||
ptb->Wheel[3].Z = z;
|
||||
|
||||
// front right wheel
|
||||
ptb3d->CurrentFrame[4].Alpha = (int16)ptb->Wheel[1].Angle;
|
||||
// front left wheel
|
||||
ptb3d->CurrentFrame[7].Alpha = (int16)ptb->Wheel[0].Angle;
|
||||
// back left wheel
|
||||
ptb3d->CurrentFrame[11].Alpha = (int16)ptb->Wheel[2].Angle;
|
||||
// back right wheel
|
||||
ptb3d->CurrentFrame[9].Alpha = (int16)ptb->Wheel[3].Angle;
|
||||
|
||||
// Car inclination (pitch)
|
||||
ptb3d->CurrentFrame[1].Type = 0;
|
||||
|
||||
LongRotate(0, 400, ptb3d->Beta);
|
||||
x1 = X0;
|
||||
z1 = Z0;
|
||||
LongRotate(0, -400, ptb3d->Beta);
|
||||
|
||||
if (Nxw + x1 >= 0 && Nxw + x1 < 32768 && Nzw + z1 >= 0 && Nzw + z1 < 32768) {
|
||||
y = CalculAltitudeObjet(Nxw + x1, Nzw + z1, -1);
|
||||
} else {
|
||||
y = yw;
|
||||
}
|
||||
|
||||
if (Nxw + X0 >= 0 && Nxw + X0 < 32768 && Nzw + Z0 >= 0 && Nzw + Z0 < 32768) {
|
||||
y1 = CalculAltitudeObjet(Nxw + X0, Nzw + Z0, -1);
|
||||
} else {
|
||||
y1 = yw;
|
||||
}
|
||||
|
||||
ptb3d->CurrentFrame[1].Alpha = (int16)(1024 - GetAngle2D(0, y, 800, y1));
|
||||
ptb->Alpha = ptb3d->CurrentFrame[1].Alpha;
|
||||
|
||||
// Car inclination (roll)
|
||||
LongRotate(400, 0, ptb3d->Beta);
|
||||
x1 = X0;
|
||||
z1 = Z0;
|
||||
LongRotate(-400, 0, ptb3d->Beta);
|
||||
|
||||
if (Nxw + X0 >= 0 && Nxw + X0 < 32768 && Nzw + Z0 >= 0 && Nzw + Z0 < 32768) {
|
||||
y = CalculAltitudeObjet(Nxw + X0, Nzw + Z0, -1);
|
||||
} else {
|
||||
y = yw;
|
||||
}
|
||||
|
||||
if (Nxw + x1 >= 0 && Nxw + x1 < 32768 && Nzw + z1 >= 0 && Nzw + z1 < 32768) {
|
||||
y1 = CalculAltitudeObjet(Nxw + x1, Nzw + z1, -1);
|
||||
} else {
|
||||
y1 = yw;
|
||||
}
|
||||
|
||||
ptb3d->CurrentFrame[1].Gamma = (int16)GetAngle2D(y, 0, y1, 800);
|
||||
|
||||
// Steering wheel
|
||||
ptb3d->CurrentFrame[12].Gamma = (int16)-ptb->BetaWheel;
|
||||
|
||||
// Twinsen's head
|
||||
ptb3d->CurrentFrame[14].Beta = (int16)ptb->BetaWheel;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Buggy::moveBuggy(ActorStruct *ptrobj) {
|
||||
#if 0
|
||||
S_BUGGY *ptb = &ListBuggy[0];
|
||||
T_OBJ_3D *ptb3d = &ptrobj->Obj;
|
||||
int32 pitch = 0;
|
||||
int32 flagattack = false;
|
||||
|
||||
int32 speedinc;
|
||||
int32 rotlevel;
|
||||
int32 timerhr, deltatimer;
|
||||
|
||||
timerhr = TimerRefHR;
|
||||
deltatimer = timerhr - ptb->LastTimer;
|
||||
|
||||
if ((Input & I_THROW) && (PtrComportement->Flags & CF_WEAPON)) {
|
||||
// Are we in mage?
|
||||
if (TabInv[FLAG_TUNIQUE].IdObj3D == 0) {
|
||||
_engine->_actor->initBody(BodyType::btTunicTir, OWN_ACTOR_SCENE_INDEX);
|
||||
} else {
|
||||
_engine->_actor->initBody(BodyType::btMageTir, OWN_ACTOR_SCENE_INDEX);
|
||||
}
|
||||
|
||||
_engine->_animations->initAnim(AnimationTypes::kThrowBall, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
/* control direction pendant Aiming */
|
||||
if (!ptrobj->_workFlags.bIsRotationByAnim) {
|
||||
ptb3d->Beta += GetDeltaMove(&ptrobj->BoundAngle.Move);
|
||||
ptb3d->Beta &= 4095;
|
||||
|
||||
_engine->_movements->initRealAngleConst(ptrobj);
|
||||
}
|
||||
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
flagattack = true;
|
||||
} else {
|
||||
if (LastInput & I_THROW) {
|
||||
// We finished shooting with the buggy,
|
||||
// we close the hood
|
||||
_engine->_actor->initBody(BodyType::btTunic, OWN_ACTOR_SCENE_INDEX);
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
}
|
||||
}
|
||||
|
||||
if (!flagattack && !ptrobj->_workFlags.bIsFalling && !ptrobj->_workFlags.bANIM_MASTER_GRAVITY) {
|
||||
_engine->_movements->clearRealAngle(ptrobj);
|
||||
|
||||
if (_engine->_movements->_lastJoyFlag && (((Input & I_JOY) != LastMyJoy) || ((Input & I_FIRE) != LastMyFire))) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
Pushing = false;
|
||||
}
|
||||
|
||||
_engine->_movements->_lastJoyFlag = false;
|
||||
|
||||
// Pushing contains the number of the object being pushed
|
||||
// So 1000 is an impossible value used as an initialization flag
|
||||
// no animation
|
||||
if (Pushing == 1000) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
Pushing = false;
|
||||
}
|
||||
|
||||
if (Input & I_UP) {
|
||||
if (Pushing) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kPush, ANIM_TEMPO, OWN_ACTOR_SCENE_INDEX);
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
}
|
||||
} else if (Input & I_DOWN) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kBackward, AnimType::kAnimationTypeRepeat, OWN_ACTOR_SCENE_INDEX);
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptrobj->_workFlags.bIsFalling && !ptrobj->_workFlags.bANIM_MASTER_GRAVITY) {
|
||||
// check speed command
|
||||
if ((Input & I_UP) // accelerating
|
||||
&& !flagattack) {
|
||||
ptb->Speed += deltatimer * 4;
|
||||
|
||||
if (!TimerGear)
|
||||
TimerGear = TimerRefHR + TEMPO_GEAR;
|
||||
else {
|
||||
if (Gear < 0)
|
||||
Gear = 0;
|
||||
|
||||
if (TimerRefHR > TimerGear && Gear < 2) {
|
||||
Gear++;
|
||||
TimerGear = TimerRefHR + TEMPO_GEAR;
|
||||
}
|
||||
}
|
||||
} else if ((Input & I_DOWN) // brake / reverse
|
||||
&& !flagattack) {
|
||||
ptb->Speed -= deltatimer * 12;
|
||||
Gear = -1;
|
||||
TimerGear = 0;
|
||||
} else // slow down
|
||||
{
|
||||
if (ptb->Speed > 0) {
|
||||
ptb->Speed -= deltatimer * 7;
|
||||
if (ptb->Speed < 0) {
|
||||
ptb->Speed = 0;
|
||||
}
|
||||
}
|
||||
if (ptb->Speed < 0) {
|
||||
ptb->Speed += deltatimer * 7;
|
||||
if (ptb->Speed > 0) {
|
||||
ptb->Speed = 0;
|
||||
}
|
||||
}
|
||||
Gear = 0;
|
||||
TimerGear = 0;
|
||||
}
|
||||
|
||||
if (ptb->Speed < -2000)
|
||||
ptb->Speed = -2000;
|
||||
if (ptb->Speed > MAX_SPEED)
|
||||
ptb->Speed = MAX_SPEED;
|
||||
|
||||
speedinc = ptb->Speed * deltatimer / 1000;
|
||||
} else {
|
||||
speedinc = 0;
|
||||
}
|
||||
|
||||
// check dir
|
||||
|
||||
if (!flagattack) {
|
||||
if (Input & I_RIGHT) {
|
||||
ptb->BetaWheel = -300;
|
||||
if (ptb->Speed) {
|
||||
rotlevel = -ptb->SpeedRot * speedinc / ptb->Speed;
|
||||
} else {
|
||||
rotlevel = 0;
|
||||
}
|
||||
} else if (Input & I_LEFT) {
|
||||
ptb->BetaWheel = 300;
|
||||
if (ptb->Speed) {
|
||||
rotlevel = ptb->SpeedRot * speedinc / ptb->Speed;
|
||||
} else {
|
||||
rotlevel = 0;
|
||||
}
|
||||
} else {
|
||||
ptb->BetaWheel = 0;
|
||||
rotlevel = 0;
|
||||
}
|
||||
|
||||
if (ptrobj->_staticFlags.bSKATING) {
|
||||
ptb->Speed = 3000;
|
||||
speedinc = ptb->Speed * deltatimer / 1000;
|
||||
} else {
|
||||
if (ptb->Speed >= 0) {
|
||||
ptb3d->Beta += rotlevel;
|
||||
} else {
|
||||
ptb3d->Beta -= rotlevel;
|
||||
}
|
||||
|
||||
ptb3d->Beta = ClampAngle(ptb3d->Beta);
|
||||
}
|
||||
} else {
|
||||
ptb->BetaWheel = 0;
|
||||
}
|
||||
|
||||
LastMyJoy = Input & I_JOY;
|
||||
LastMyFire = Input & I_FIRE;
|
||||
LastInput = Input;
|
||||
|
||||
ptb->LastTimer = timerhr;
|
||||
ptb->SpeedInc = speedinc;
|
||||
|
||||
if (ptrobj->SampleAlways && _engine->_sound->isSamplePlaying(ptrobj->SampleAlways)) {
|
||||
int32 pitch;
|
||||
|
||||
switch (Gear) {
|
||||
case -1:
|
||||
pitch = boundRuleThree(3000, MAX_SAMPLE_PITCH2, MAX_SPEED, ABS(ptb->Speed));
|
||||
break;
|
||||
|
||||
case 0:
|
||||
pitch = boundRuleThree(3000, MAX_SAMPLE_PITCH, MAX_SPEED, ABS(ptb->Speed));
|
||||
if (pitch >= MAX_SAMPLE_PITCH)
|
||||
TimerGear = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pitch = boundRuleThree(MAX_SAMPLE_PITCH2, MIN_SAMPLE_PITCH2, TEMPO_GEAR, TimerGear - TimerRefHR);
|
||||
break;
|
||||
|
||||
default:
|
||||
pitch = MAX_SAMPLE_PITCH2;
|
||||
}
|
||||
|
||||
_engine->_sound->ChangePitchbendSample(ptrobj->SampleAlways, pitch);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
81
engines/twine/scene/buggy.h
Normal file
81
engines/twine/scene/buggy.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_BUGGY_H
|
||||
#define TWINE_SCENE_BUGGY_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/scene/actor.h"
|
||||
|
||||
#define BUGGY_PRESENT 0x80
|
||||
#define NUM_BUGGY ((uint8)(NumBuggy & ~(BUGGY_PRESENT)))
|
||||
#define IsBuggyPresent() (NumBuggy & BUGGY_PRESENT)
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class Buggy {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
int32 Gear = 0;
|
||||
int32 TimerGear;
|
||||
|
||||
public:
|
||||
#define MAX_BUGGYS 2
|
||||
struct S_ONE_WHEEL {
|
||||
int32 Angle = 0;
|
||||
int32 X = 0;
|
||||
int32 Y = 0;
|
||||
int32 Z = 0;
|
||||
};
|
||||
|
||||
struct S_BUGGY {
|
||||
int32 X = 0;
|
||||
int32 Y = 0;
|
||||
int32 Z = 0;
|
||||
int32 Cube = 0;
|
||||
int32 Beta = 0;
|
||||
int32 Alpha = 0;
|
||||
int32 Gamma = 0;
|
||||
S_ONE_WHEEL Wheel[4];
|
||||
int32 BetaWheel = 0;
|
||||
int32 SpeedInc = 0;
|
||||
int32 SpeedRot = 0;
|
||||
int32 Speed = 0;
|
||||
int32 LastTimer = 0;
|
||||
};
|
||||
|
||||
// TODO: rename and hide
|
||||
S_BUGGY ListBuggy[MAX_BUGGYS];
|
||||
uint8 NumBuggy;
|
||||
|
||||
Buggy(TwinEEngine *engine) : _engine(engine) {}
|
||||
void initBuggy(uint8 numobj, uint32 flaginit);
|
||||
void resetBuggy();
|
||||
void takeBuggy();
|
||||
void leaveBuggy(HeroBehaviourType behaviour);
|
||||
void doAnimBuggy(ActorStruct *ptrobj);
|
||||
void moveBuggy(ActorStruct *ptrobj);
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
524
engines/twine/scene/collision.cpp
Normal file
524
engines/twine/scene/collision.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/collision.h"
|
||||
#include "common/util.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/extra.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Collision::Collision(TwinEEngine *engine) : _engine(engine) {
|
||||
}
|
||||
|
||||
bool Collision::checkZvOnZv(int32 actorIdx1, int32 actorIdx2) const {
|
||||
const ActorStruct *actor1 = _engine->_scene->getActor(actorIdx1);
|
||||
const ActorStruct *actor2 = _engine->_scene->getActor(actorIdx2);
|
||||
|
||||
const IVec3 &processActor = actor1->_processActor;
|
||||
const IVec3 &mins1 = processActor + actor1->_boundingBox.mins;
|
||||
const IVec3 &maxs1 = processActor + actor1->_boundingBox.maxs;
|
||||
|
||||
const IVec3 &mins2 = actor2->posObj() + actor2->_boundingBox.mins;
|
||||
const IVec3 &maxs2 = actor2->posObj() + actor2->_boundingBox.maxs;
|
||||
|
||||
if (mins1.x >= maxs2.x) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxs1.x <= mins2.x) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mins1.y > (maxs2.y + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mins1.y <= (maxs2.y - SIZE_BRICK_Y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxs1.y <= mins2.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mins1.z >= maxs2.z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxs1.z <= mins2.z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Collision::reajustPos(IVec3 &processActor, ShapeType brickShape) const {
|
||||
if (brickShape <= ShapeType::kSolid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 xw = (_collision.x * SIZE_BRICK_XZ) - DEMI_BRICK_XZ; // upper left corner of the brick
|
||||
const int32 yw = _collision.y * SIZE_BRICK_Y;
|
||||
const int32 zw = (_collision.z * SIZE_BRICK_XZ) - DEMI_BRICK_XZ;
|
||||
|
||||
// double-side stairs
|
||||
switch (brickShape) {
|
||||
case ShapeType::kDoubleSideStairsTop1:
|
||||
if (processActor.x - xw < processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsTopRight;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsTopLeft;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsBottom1:
|
||||
if (processActor.x - xw < processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsBottomRight;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsBottomLeft;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsTop2:
|
||||
if (processActor.x - xw < processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsTopLeft;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsTopRight;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsBottom2:
|
||||
if (processActor.x - xw < processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsBottomLeft;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsBottomRight;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsLeft1:
|
||||
if (SIZE_BRICK_XZ - (processActor.x - xw) > processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsBottomLeft;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsTopLeft;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsRight1:
|
||||
if (SIZE_BRICK_XZ - (processActor.x - xw) > processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsBottomRight;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsTopRight;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsLeft2:
|
||||
if (SIZE_BRICK_XZ - (processActor.x - xw) > processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsTopLeft;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsBottomLeft;
|
||||
}
|
||||
break;
|
||||
case ShapeType::kDoubleSideStairsRight2:
|
||||
if (SIZE_BRICK_XZ - (processActor.x - xw) > processActor.z - zw) {
|
||||
brickShape = ShapeType::kStairsTopRight;
|
||||
} else {
|
||||
brickShape = ShapeType::kStairsBottomRight;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (brickShape) {
|
||||
case ShapeType::kStairsTopLeft:
|
||||
processActor.y = yw + boundRuleThree(0, SIZE_BRICK_Y, SIZE_BRICK_XZ, processActor.x - xw);
|
||||
break;
|
||||
case ShapeType::kStairsTopRight:
|
||||
processActor.y = yw + boundRuleThree(0, SIZE_BRICK_Y, SIZE_BRICK_XZ, processActor.z - zw);
|
||||
break;
|
||||
case ShapeType::kStairsBottomLeft:
|
||||
processActor.y = yw + boundRuleThree(SIZE_BRICK_Y, 0, SIZE_BRICK_XZ, processActor.z - zw);
|
||||
break;
|
||||
case ShapeType::kStairsBottomRight:
|
||||
processActor.y = yw + boundRuleThree(SIZE_BRICK_Y, 0, SIZE_BRICK_XZ, processActor.x - xw);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Collision::handlePushing(IVec3 &processActor, const IVec3 &minsTest, const IVec3 &maxsTest, ActorStruct *ptrobj, ActorStruct *ptrobjt) {
|
||||
const int32 newAngle = _engine->_movements->getAngle(processActor, ptrobjt->posObj());
|
||||
|
||||
// protect against chain reactions
|
||||
if (ptrobjt->_flags.bCanBePushed && !ptrobj->_flags.bCanBePushed) {
|
||||
ptrobjt->_animStep.y = 0;
|
||||
|
||||
if (ptrobjt->_flags.bUseMiniZv) {
|
||||
if (newAngle >= LBAAngles::ANGLE_45 && newAngle < LBAAngles::ANGLE_135 && ptrobj->_beta >= LBAAngles::ANGLE_45 && ptrobj->_beta < LBAAngles::ANGLE_135) {
|
||||
ptrobjt->_animStep.x = SIZE_BRICK_XZ / 4 + SIZE_BRICK_XZ / 8;
|
||||
}
|
||||
if (newAngle >= LBAAngles::ANGLE_135 && newAngle < LBAAngles::ANGLE_225 && ptrobj->_beta >= LBAAngles::ANGLE_135 && ptrobj->_beta < LBAAngles::ANGLE_225) {
|
||||
ptrobjt->_animStep.z = -SIZE_BRICK_XZ / 4 + SIZE_BRICK_XZ / 8;
|
||||
}
|
||||
if (newAngle >= LBAAngles::ANGLE_225 && newAngle < LBAAngles::ANGLE_315 && ptrobj->_beta >= LBAAngles::ANGLE_225 && ptrobj->_beta < LBAAngles::ANGLE_315) {
|
||||
ptrobjt->_animStep.x = -SIZE_BRICK_XZ / 4 + SIZE_BRICK_XZ / 8;
|
||||
}
|
||||
if ((newAngle >= LBAAngles::ANGLE_315 || newAngle < LBAAngles::ANGLE_45) && (ptrobj->_beta >= LBAAngles::ANGLE_315 || ptrobj->_beta < LBAAngles::ANGLE_45)) {
|
||||
ptrobjt->_animStep.z = SIZE_BRICK_XZ / 4 + SIZE_BRICK_XZ / 8;
|
||||
}
|
||||
} else {
|
||||
// induced displacement before readjustment?
|
||||
ptrobjt->_animStep.x = processActor.x - ptrobj->_oldPos.x;
|
||||
ptrobjt->_animStep.z = processActor.z - ptrobj->_oldPos.z;
|
||||
}
|
||||
}
|
||||
|
||||
// so patch tempo
|
||||
if ((ptrobjt->_boundingBox.maxs.x - ptrobjt->_boundingBox.mins.x == ptrobjt->_boundingBox.maxs.z - ptrobjt->_boundingBox.mins.z) &&
|
||||
(ptrobj->_boundingBox.maxs.x - ptrobj->_boundingBox.mins.x == ptrobj->_boundingBox.maxs.z - ptrobj->_boundingBox.mins.z)) {
|
||||
if (newAngle >= LBAAngles::ANGLE_45 && newAngle < LBAAngles::ANGLE_135) {
|
||||
processActor.x = minsTest.x - ptrobj->_boundingBox.maxs.x;
|
||||
}
|
||||
if (newAngle >= LBAAngles::ANGLE_135 && newAngle < LBAAngles::ANGLE_225) {
|
||||
processActor.z = maxsTest.z - ptrobj->_boundingBox.mins.z;
|
||||
}
|
||||
if (newAngle >= LBAAngles::ANGLE_225 && newAngle < LBAAngles::ANGLE_315) {
|
||||
processActor.x = maxsTest.x - ptrobj->_boundingBox.mins.x;
|
||||
}
|
||||
if (newAngle >= LBAAngles::ANGLE_315 || newAngle < LBAAngles::ANGLE_45) {
|
||||
processActor.z = minsTest.z - ptrobj->_boundingBox.maxs.z;
|
||||
}
|
||||
} else if (!ptrobj->_workFlags.bIsFalling) {
|
||||
// refuse pos
|
||||
processActor = ptrobj->_oldPos;
|
||||
}
|
||||
}
|
||||
|
||||
bool Collision::checkValidObjPos(int32 actorIdx) {
|
||||
const ActorStruct *ptrobj = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
const IVec3 m0 = ptrobj->posObj() + ptrobj->_boundingBox.mins;
|
||||
const IVec3 m1 = ptrobj->posObj() + ptrobj->_boundingBox.maxs;
|
||||
|
||||
if (m0.x < 0 || m0.x > SCENE_SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (m1.x < 0 || m1.x > SCENE_SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (m0.z < 0 || m0.z > SCENE_SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (m1.z < 0 || m1.z > SCENE_SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Grid *grid = _engine->_grid;
|
||||
if (grid->worldColBrickFull(m0.x, m0.y, m0.z, ptrobj->_boundingBox.maxs.y, actorIdx) != ShapeType::kNone) {
|
||||
return false;
|
||||
}
|
||||
if (grid->worldColBrickFull(m1.x, m0.y, m0.z, ptrobj->_boundingBox.maxs.y, actorIdx) != ShapeType::kNone) {
|
||||
return false;
|
||||
}
|
||||
if (grid->worldColBrickFull(m1.x, m0.y, m1.z, ptrobj->_boundingBox.maxs.y, actorIdx) != ShapeType::kNone) {
|
||||
return false;
|
||||
}
|
||||
if (grid->worldColBrickFull(m0.x, m0.y, m1.z, ptrobj->_boundingBox.maxs.y, actorIdx) != ShapeType::kNone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 n = 0; n < _engine->_scene->_nbObjets; ++n) {
|
||||
const ActorStruct *ptrobjt = _engine->_scene->getActor(n);
|
||||
if (n != actorIdx && ptrobjt->_body != -1 && !ptrobj->_flags.bIsInvisible && ptrobjt->_carryBy != actorIdx) {
|
||||
const IVec3 &t0 = ptrobjt->posObj() + ptrobjt->_boundingBox.mins;
|
||||
const IVec3 &t1 = ptrobjt->posObj() + ptrobjt->_boundingBox.maxs;
|
||||
if (m0.x < t1.x && m1.x > t0.x && m0.y < t1.y && m1.y > t0.y && m0.z < t1.z && m1.z > t0.z) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 Collision::checkObjCol(int32 actorIdx) {
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(actorIdx);
|
||||
|
||||
IVec3 &processActor = ptrobj->_processActor;
|
||||
IVec3 mins = processActor + ptrobj->_boundingBox.mins;
|
||||
IVec3 maxs = processActor + ptrobj->_boundingBox.maxs;
|
||||
|
||||
int32 oldObjCol = ptrobj->_objCol;
|
||||
ptrobj->_objCol = -1;
|
||||
|
||||
for (int32 a = 0; a < _engine->_scene->_nbObjets; a++) {
|
||||
ActorStruct *ptrobjt = _engine->_scene->getActor(a);
|
||||
|
||||
// avoid current processed actor
|
||||
if (a != actorIdx && ptrobjt->_body != -1 && !ptrobj->_flags.bIsInvisible && ptrobjt->_carryBy != actorIdx) {
|
||||
const IVec3 &minsTest = ptrobjt->posObj() + ptrobjt->_boundingBox.mins;
|
||||
const IVec3 &maxsTest = ptrobjt->posObj() + ptrobjt->_boundingBox.maxs;
|
||||
|
||||
if (mins.x < maxsTest.x && maxs.x > minsTest.x && mins.y < maxsTest.y && maxs.y > minsTest.y && mins.z < maxsTest.z && maxs.z > minsTest.z) {
|
||||
ptrobj->_objCol = a; // mark as collision with actor a
|
||||
if (a != oldObjCol) {
|
||||
debugC(1, TwinE::kDebugCollision, "Actor %d is colliding with %d", actorIdx, a);
|
||||
}
|
||||
if (ptrobjt->_flags.bIsCarrierActor) {
|
||||
if (ptrobj->_workFlags.bIsFalling) {
|
||||
// I touch a carrier
|
||||
processActor.y = maxsTest.y - ptrobj->_boundingBox.mins.y + 1;
|
||||
ptrobj->_carryBy = a;
|
||||
continue;
|
||||
} else if (checkZvOnZv(actorIdx, a)) {
|
||||
// I walk on a carrier
|
||||
processActor.y = maxsTest.y - ptrobj->_boundingBox.mins.y + 1;
|
||||
ptrobj->_carryBy = a;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// I step on someone
|
||||
if (checkZvOnZv(actorIdx, a)) {
|
||||
_engine->_actor->hitObj(actorIdx, a, 1, -1);
|
||||
}
|
||||
}
|
||||
handlePushing(processActor, minsTest, maxsTest, ptrobj, ptrobjt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test moves ZV further if hit
|
||||
if (ptrobj->_workFlags.bIsHitting) {
|
||||
const IVec2 &destPos = _engine->_renderer->rotate(0, 200, ptrobj->_beta);
|
||||
mins = processActor + ptrobj->_boundingBox.mins;
|
||||
mins.x += destPos.x;
|
||||
mins.z += destPos.y;
|
||||
|
||||
maxs = processActor + ptrobj->_boundingBox.maxs;
|
||||
maxs.x += destPos.x;
|
||||
maxs.z += destPos.y;
|
||||
|
||||
for (int32 a = 0; a < _engine->_scene->_nbObjets; a++) {
|
||||
const ActorStruct *actorTest = _engine->_scene->getActor(a);
|
||||
|
||||
// avoid current processed actor
|
||||
if (a != actorIdx && actorTest->_body != -1 && !actorTest->_flags.bIsInvisible && actorTest->_carryBy != actorIdx) {
|
||||
const IVec3 minsTest = actorTest->posObj() + actorTest->_boundingBox.mins;
|
||||
const IVec3 maxsTest = actorTest->posObj() + actorTest->_boundingBox.maxs;
|
||||
if (mins.x < maxsTest.x && maxs.x > minsTest.x && mins.y < maxsTest.y && maxs.y > minsTest.y && mins.z < maxsTest.z && maxs.z > minsTest.z) {
|
||||
_engine->_actor->hitObj(actorIdx, a, ptrobj->_hitForce, ptrobj->_beta + LBAAngles::ANGLE_180);
|
||||
ptrobj->_workFlags.bIsHitting = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptrobj->_objCol;
|
||||
}
|
||||
|
||||
void Collision::setCollisionPos(const IVec3 &pos) {
|
||||
_processCollision = pos;
|
||||
}
|
||||
|
||||
uint32 Collision::doCornerReajustTwinkel(ActorStruct *actor, int32 x, int32 y, int32 z, int32 damageMask) {
|
||||
IVec3 &processActor = actor->_processActor;
|
||||
const IVec3 &oldPos = actor->_oldPos;
|
||||
ShapeType orgcol = _engine->_grid->worldColBrick(processActor);
|
||||
uint32 _col1 = 0;
|
||||
|
||||
processActor.x += x;
|
||||
processActor.y += y;
|
||||
processActor.z += z;
|
||||
|
||||
if (processActor.x >= 0 && processActor.z >= 0 && processActor.x <= SCENE_SIZE_MAX && processActor.z <= SCENE_SIZE_MAX) {
|
||||
const BoundingBox &bbox = actor->_boundingBox;
|
||||
reajustPos(processActor, orgcol);
|
||||
ShapeType col = _engine->_grid->worldColBrickFull(processActor, bbox.maxs.y, OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
if (col == ShapeType::kSolid) {
|
||||
_col1 |= damageMask;
|
||||
if (_engine->_grid->worldColBrickFull(processActor.x, processActor.y, oldPos.z + z, bbox.maxs.y, OWN_ACTOR_SCENE_INDEX) == ShapeType::kSolid) {
|
||||
if (_engine->_grid->worldColBrickFull(x + oldPos.x, processActor.y, processActor.z, bbox.maxs.y, OWN_ACTOR_SCENE_INDEX) != ShapeType::kSolid) {
|
||||
_processCollision.x = oldPos.x;
|
||||
}
|
||||
} else {
|
||||
_processCollision.z = oldPos.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processActor = _processCollision;
|
||||
return _col1;
|
||||
}
|
||||
|
||||
uint32 Collision::doCornerReajust(ActorStruct *actor, int32 x, int32 y, int32 z, int32 damageMask) {
|
||||
IVec3 &processActor = actor->_processActor;
|
||||
const IVec3 &previousActor = actor->_oldPos;
|
||||
ShapeType orgcol = _engine->_grid->worldColBrick(processActor);
|
||||
uint32 _col1 = 0;
|
||||
|
||||
processActor.x += x;
|
||||
processActor.y += y;
|
||||
processActor.z += z;
|
||||
|
||||
if (processActor.x >= 0 && processActor.z >= 0 && processActor.x <= SCENE_SIZE_MAX && processActor.z <= SCENE_SIZE_MAX) {
|
||||
reajustPos(processActor, orgcol);
|
||||
ShapeType col = _engine->_grid->worldColBrick(processActor);
|
||||
|
||||
if (col == ShapeType::kSolid) {
|
||||
_col1 |= damageMask;
|
||||
if (_engine->_grid->worldColBrick(processActor.x, processActor.y, previousActor.z + z) == ShapeType::kSolid) {
|
||||
if (_engine->_grid->worldColBrick(x + previousActor.x, processActor.y, processActor.z) != ShapeType::kSolid) {
|
||||
_processCollision.x = previousActor.x;
|
||||
}
|
||||
} else {
|
||||
_processCollision.z = previousActor.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processActor = _processCollision;
|
||||
return _col1;
|
||||
}
|
||||
|
||||
void Collision::receptionObj(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (IS_HERO(actorIdx)) {
|
||||
const IVec3 &processActor = actor->_processActor;
|
||||
const int32 fall = _engine->_scene->_startYFalling - processActor.y;
|
||||
|
||||
if (fall >= SIZE_BRICK_Y * 8) {
|
||||
const IVec3 &actorPos = actor->posObj();
|
||||
_engine->_extra->initSpecial(actorPos.x, actorPos.y + 1000, actorPos.z, ExtraSpecialType::kHitStars);
|
||||
if (fall >= SIZE_BRICK_Y * 16) {
|
||||
actor->setLife(0);
|
||||
} else {
|
||||
actor->addLife(-1);
|
||||
}
|
||||
_engine->_animations->initAnim(AnimationTypes::kLandingHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx);
|
||||
} else if (fall > 2 * SIZE_BRICK_Y) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx);
|
||||
} else {
|
||||
if (actor->_workFlags.bWasWalkingBeforeFalling) {
|
||||
// try to not interrupt walk animation if Twinsen falls down from small height
|
||||
_engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeRepeat, AnimationTypes::kStanding, actorIdx);
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kStanding, actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
_engine->_scene->_startYFalling = 0;
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationAllThen, actor->_nextGenAnim, actorIdx);
|
||||
}
|
||||
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
debugC(1, TwinE::kDebugCollision, "Actor %d reset falling", actorIdx);
|
||||
}
|
||||
|
||||
actor->_workFlags.bIsFalling = 0;
|
||||
actor->_workFlags.bWasWalkingBeforeFalling = 0;
|
||||
}
|
||||
|
||||
int32 Collision::extraCheckObjCol(ExtraListStruct *extra, int32 actorIdx) {
|
||||
const BoundingBox *bbox = _engine->_resources->_spriteBoundingBox.bbox(extra->sprite);
|
||||
const IVec3 mins = bbox->mins + extra->pos;
|
||||
const IVec3 maxs = bbox->maxs + extra->pos;
|
||||
|
||||
for (int32 a = 0; a < _engine->_scene->_nbObjets; a++) {
|
||||
const ActorStruct *actorTest = _engine->_scene->getActor(a);
|
||||
|
||||
if (a != actorIdx && actorTest->_body != -1) {
|
||||
const IVec3 minsTest = actorTest->posObj() + actorTest->_boundingBox.mins;
|
||||
const IVec3 maxsTest = actorTest->posObj() + actorTest->_boundingBox.maxs;
|
||||
|
||||
if (mins.x < maxsTest.x && maxs.x > minsTest.x && mins.y < maxsTest.y && maxs.y > minsTest.y && mins.z < maxsTest.z && maxs.z > minsTest.z) {
|
||||
if (extra->strengthOfHit != 0) {
|
||||
_engine->_actor->hitObj(actorIdx, a, extra->strengthOfHit, -1);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Collision::fullWorldColBrick(int32 x, int32 y, int32 z, const IVec3 &oldPos) {
|
||||
if (_engine->_grid->worldColBrick(oldPos) != ShapeType::kNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int32 averageX = ABS(x + oldPos.x) / 2;
|
||||
const int32 averageY = ABS(y + oldPos.y) / 2;
|
||||
const int32 averageZ = ABS(z + oldPos.z) / 2;
|
||||
|
||||
if (_engine->_grid->worldColBrick(averageX, averageY, averageZ) != ShapeType::kNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_engine->_grid->worldColBrick(ABS(oldPos.x + averageX) / 2, ABS(oldPos.y + averageY) / 2, ABS(oldPos.z + averageZ) / 2) != ShapeType::kNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_engine->_grid->worldColBrick(ABS(x + averageX) / 2, ABS(y + averageY) / 2, ABS(z + averageZ) / 2) != ShapeType::kNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 Collision::extraCheckExtraCol(ExtraListStruct *extra, int32 extraIdx) const {
|
||||
int32 index = extra->sprite;
|
||||
const BoundingBox *bbox = _engine->_resources->_spriteBoundingBox.bbox(index);
|
||||
const IVec3 mins = bbox->mins + extra->pos;
|
||||
const IVec3 maxs = bbox->maxs + extra->pos;
|
||||
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
const ExtraListStruct *extraTest = &_engine->_extra->_extraList[i];
|
||||
if (i != extraIdx && extraTest->sprite != -1) {
|
||||
const BoundingBox *testbbox = _engine->_resources->_spriteBoundingBox.bbox(++index);
|
||||
const IVec3 minsTest = testbbox->mins + extraTest->pos;
|
||||
const IVec3 maxsTest = testbbox->maxs + extraTest->pos;
|
||||
|
||||
if (mins.x >= minsTest.x) {
|
||||
continue;
|
||||
}
|
||||
if (mins.x < maxsTest.x && maxs.x > minsTest.x && mins.y < maxsTest.y && maxs.y > minsTest.y && mins.z < maxsTest.z && maxs.z > minsTest.z) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Collision::doImpact(int32 num, int32 x, int32 y, int32 z, int32 owner) {
|
||||
debugC(3, kDebugLevels::kDebugCollision, "Collision::doImpact(%i, %i, %i, %i, %i)", num, x, y, z, owner);
|
||||
// TODO: Implement me
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
110
engines/twine/scene/collision.h
Normal file
110
engines/twine/scene/collision.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_COLLISION_H
|
||||
#define TWINE_SCENE_COLLISION_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class ActorStruct;
|
||||
struct ExtraListStruct;
|
||||
class TwinEEngine;
|
||||
|
||||
class Collision {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
void handlePushing(IVec3 &processActor, const IVec3 &minsTest, const IVec3 &maxsTest, ActorStruct *actor, ActorStruct *actorTest);
|
||||
|
||||
/** Actor collision coordinate */
|
||||
IVec3 _processCollision; // SaveNxw, SaveNyw, SaveNzw
|
||||
public:
|
||||
Collision(TwinEEngine *engine);
|
||||
/** Actor collision coordinate */
|
||||
IVec3 _collision; // YMap
|
||||
|
||||
/**
|
||||
* Check if actor 1 is standing in actor 2
|
||||
* @param actorIdx1 Actor 1 index
|
||||
* @param actorIdx2 Actor 2 index
|
||||
*/
|
||||
bool checkZvOnZv(int32 actorIdx1, int32 actorIdx2) const;
|
||||
|
||||
void doImpact(int32 num, int32 x, int32 y, int32 z, int32 owner);
|
||||
|
||||
/**
|
||||
* Reajust actor position in scene according with brick shape bellow actor
|
||||
* @param brickShape Shape of brick bellow the actor
|
||||
*/
|
||||
void reajustPos(IVec3 &processActor, ShapeType brickShape) const;
|
||||
|
||||
/**
|
||||
* Check collision with actors
|
||||
* @param actorIx Current process actor index
|
||||
*/
|
||||
int32 checkObjCol(int32 actorIdx);
|
||||
bool checkValidObjPos(int32 actorIdx);
|
||||
|
||||
void setCollisionPos(const IVec3 &pos);
|
||||
/**
|
||||
* Check Hero collision with bricks
|
||||
* @param x Hero X coordinate
|
||||
* @param y Hero Y coordinate
|
||||
* @param z Hero Z coordinate
|
||||
* @param damageMask Cause damage mask
|
||||
*/
|
||||
uint32 doCornerReajustTwinkel(ActorStruct *actor, int32 x, int32 y, int32 z, int32 damageMask);
|
||||
|
||||
/**
|
||||
* Check other actor collision with bricks
|
||||
* @param x Actor X coordinate
|
||||
* @param y Actor Y coordinate
|
||||
* @param z Actor Z coordinate
|
||||
* @param damageMask Cause damage mask
|
||||
*/
|
||||
uint32 doCornerReajust(ActorStruct *actor, int32 x, int32 y, int32 z, int32 damageMask);
|
||||
|
||||
/** Make actor to stop falling */
|
||||
void receptionObj(int actorIdx);
|
||||
|
||||
/**
|
||||
* Check extra collision with actors
|
||||
* @param extra to process
|
||||
* @param actorIdx actor to check collision
|
||||
*/
|
||||
int32 extraCheckObjCol(ExtraListStruct *extra, int32 actorIdx);
|
||||
|
||||
/** Check extra collision with bricks */
|
||||
bool fullWorldColBrick(int32 x, int32 y, int32 z, const IVec3 &oldPos);
|
||||
|
||||
/**
|
||||
* Check extra collision with another extra
|
||||
* @param extra to process
|
||||
* @param extraIdx extra index to check collision
|
||||
*/
|
||||
int32 extraCheckExtraCol(ExtraListStruct *extra, int32 extraIdx) const;
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
#endif
|
||||
149
engines/twine/scene/dart.cpp
Normal file
149
engines/twine/scene/dart.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/dart.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/scene.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
void Dart::InitDarts() {
|
||||
int32 x0, x1, y0, y1, z0, z1;
|
||||
#if 0
|
||||
uint8 *ptrbody;
|
||||
T_BODY_HEADER *ptr;
|
||||
|
||||
ptrbody = (uint8 *)GivePtrObjFix(BODY_3D_DART);
|
||||
if (!ptrbody) {
|
||||
char tmpFilePath[ADELINE_MAX_PATH];
|
||||
GetResPath(tmpFilePath, ADELINE_MAX_PATH, OBJFIX_HQR_NAME);
|
||||
TheEndCheckFile(tmpFilePath);
|
||||
}
|
||||
// Calcule ZV des flechettes
|
||||
ptr = (T_BODY_HEADER *)ptrbody;
|
||||
|
||||
x0 = ptr->XMin;
|
||||
x1 = ptr->XMax;
|
||||
y0 = ptr->YMin;
|
||||
y1 = ptr->YMax;
|
||||
z0 = ptr->ZMin;
|
||||
z1 = ptr->ZMax;
|
||||
#else
|
||||
x0 = x1 = y0 = y1 = z0 = z1 = 0;
|
||||
#endif
|
||||
|
||||
// Average
|
||||
int32 size = ((x1 - x0) + (z1 - z0)) / 4;
|
||||
|
||||
T_DART *ptrd = ListDart;
|
||||
|
||||
for (uint32 t = 0; t < MAX_DARTS; t++, ptrd++) {
|
||||
ptrd->Body = BODY_3D_DART;
|
||||
|
||||
ptrd->XMin = -size;
|
||||
ptrd->XMax = size;
|
||||
ptrd->YMin = y0;
|
||||
ptrd->YMax = y1;
|
||||
ptrd->ZMin = -size;
|
||||
ptrd->ZMax = size;
|
||||
|
||||
ptrd->Flags = 0;
|
||||
ptrd->NumCube = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Dart::GetDart() {
|
||||
T_DART *ptrd;
|
||||
int32 t;
|
||||
|
||||
ptrd = ListDart;
|
||||
|
||||
for (t = 0; t < MAX_DARTS; t++, ptrd++) {
|
||||
if (ptrd->Flags & DART_TAKEN) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Dart::TakeAllDarts() {
|
||||
T_DART *ptrd;
|
||||
int32 n;
|
||||
|
||||
ptrd = ListDart;
|
||||
|
||||
for (n = 0; n < MAX_DARTS; n++, ptrd++) {
|
||||
ptrd->Flags |= DART_TAKEN;
|
||||
}
|
||||
|
||||
_engine->_gameState->setDarts(MAX_DARTS);
|
||||
}
|
||||
|
||||
void Dart::CheckDartCol(ActorStruct *ptrobj) {
|
||||
int32 n;
|
||||
T_DART *ptrd;
|
||||
int32 x0, y0, z0, x1, y1, z1;
|
||||
int32 xt0, yt0, zt0, xt1, yt1, zt1;
|
||||
|
||||
if (ptrobj->_flags.bIsInvisible)
|
||||
return;
|
||||
|
||||
x0 = ptrobj->_posObj.x + ptrobj->_boundingBox.mins.x;
|
||||
x1 = ptrobj->_posObj.x + ptrobj->_boundingBox.maxs.x;
|
||||
y0 = ptrobj->_posObj.y + ptrobj->_boundingBox.mins.y;
|
||||
y1 = ptrobj->_posObj.y + ptrobj->_boundingBox.maxs.y;
|
||||
z0 = ptrobj->_posObj.z + ptrobj->_boundingBox.mins.z;
|
||||
z1 = ptrobj->_posObj.z + ptrobj->_boundingBox.maxs.z;
|
||||
|
||||
ptrd = ListDart;
|
||||
|
||||
for (n = 0; n < MAX_DARTS; n++, ptrd++) {
|
||||
if (ptrd->NumCube == _engine->_scene->_numCube && !(ptrd->Flags & DART_TAKEN)) {
|
||||
xt0 = ptrd->PosX + ptrd->XMin;
|
||||
xt1 = ptrd->PosX + ptrd->XMax;
|
||||
yt0 = ptrd->PosY + ptrd->YMin;
|
||||
yt1 = ptrd->PosY + ptrd->YMax;
|
||||
zt0 = ptrd->PosZ + ptrd->ZMin;
|
||||
zt1 = ptrd->PosZ + ptrd->ZMax;
|
||||
|
||||
if (x0 < xt1 && x1 > xt0 && y0 < yt1 && y1 > yt0 && z0 < zt1 && z1 > zt0) {
|
||||
ptrd->Flags |= DART_TAKEN;
|
||||
|
||||
_engine->_gameState->addDart();
|
||||
|
||||
#if 0
|
||||
_engine->_sound->playSample(SAMPLE_BONUS_TROUVE, 0x1000, 0, 1,
|
||||
ptrd->PosX, ptrd->PosY, ptrd->PosZ);
|
||||
|
||||
_engine->_redraw->addOverlay(OverlayType::koSprite | INCRUST_YCLIP,
|
||||
SPRITE_DART,
|
||||
15, 30,
|
||||
0, 0, 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
69
engines/twine/scene/dart.h
Normal file
69
engines/twine/scene/dart.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_DART_H
|
||||
#define TWINE_SCENE_DART_H
|
||||
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
#define MAX_DARTS 3
|
||||
#define BODY_3D_DART 61
|
||||
// dart flags
|
||||
#define DART_TAKEN (1 << 0)
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class Dart {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
public:
|
||||
struct T_DART {
|
||||
int32 PosX = 0;
|
||||
int32 PosY = 0;
|
||||
int32 PosZ = 0;
|
||||
int32 Alpha = 0;
|
||||
int32 Beta = 0;
|
||||
int32 Body = 0;
|
||||
int32 NumCube = 0; // Number of the cube in which the dart is located
|
||||
uint32 Flags = 0u;
|
||||
|
||||
int32 XMin = 0; // ZV of the darts
|
||||
int32 YMin = 0;
|
||||
int32 ZMin = 0;
|
||||
int32 XMax = 0;
|
||||
int32 YMax = 0;
|
||||
int32 ZMax = 0;
|
||||
};
|
||||
T_DART ListDart[MAX_DARTS];
|
||||
|
||||
Dart(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
void InitDarts();
|
||||
int32 GetDart();
|
||||
void TakeAllDarts();
|
||||
void CheckDartCol(ActorStruct *ptrobj);
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
824
engines/twine/scene/extra.cpp
Normal file
824
engines/twine/scene/extra.cpp
Normal file
@@ -0,0 +1,824 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/extra.h"
|
||||
#include "common/util.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
/** Hit Stars shape info */
|
||||
static const ShapeData hitStarsData[]{
|
||||
{4, -6},
|
||||
{19, -6},
|
||||
{7, 2},
|
||||
{12, 16},
|
||||
{0, 7},
|
||||
{-12, 16},
|
||||
{-7, 2},
|
||||
{-19, -6},
|
||||
{-4, -6}};
|
||||
|
||||
/** Explode Cloud shape info */
|
||||
static const ShapeData explodeCloudData[]{
|
||||
{0, -20},
|
||||
{6, -16},
|
||||
{8, -10},
|
||||
{14, -12},
|
||||
{20, -4},
|
||||
{18, 4},
|
||||
{12, 4},
|
||||
{16, 8},
|
||||
{8, 16},
|
||||
{2, 12},
|
||||
{-4, 18},
|
||||
{-10, 16},
|
||||
{-12, 8},
|
||||
{-16, 10},
|
||||
{-20, 4},
|
||||
{-12, -8},
|
||||
{-6, -6},
|
||||
{-10, -12}};
|
||||
|
||||
const ExtraShape hitStarsShape { ARRAYSIZE(hitStarsData), hitStarsData };
|
||||
const ExtraShape explodeCloudShape { ARRAYSIZE(explodeCloudData), explodeCloudData };
|
||||
|
||||
Extra::Extra(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
int32 Extra::extraSearch(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 targetActor, int32 maxSpeed, int32 strengthOfHit) { // ExtraSearch
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = spriteIdx;
|
||||
extra->type = ExtraType::SEARCH_OBJ;
|
||||
extra->info1 = 0;
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
extra->payload.actorIdx = actorIdx;
|
||||
extra->spawnTime = targetActor;
|
||||
extra->destPos.z = maxSpeed;
|
||||
extra->strengthOfHit = strengthOfHit;
|
||||
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, maxSpeed, LBAAngles::ANGLE_17, &extra->trackActorMove);
|
||||
const ActorStruct *actor = _engine->_scene->getActor(targetActor);
|
||||
extra->angle = _engine->_movements->getAngle(extra->pos, actor->posObj());
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 Extra::addExtraExplode(int32 x, int32 y, int32 z) {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = SPRITEHQR_EXPLOSION_FIRST_FRAME;
|
||||
extra->type = ExtraType::TIME_OUT | ExtraType::EXPLOSION;
|
||||
extra->info1 = 0;
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
extra->payload.lifeTime = 40;
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
extra->strengthOfHit = 0;
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Extra::clearExtra() {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
extra->sprite = -1;
|
||||
extra->info1 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Extra::initFly(ExtraListStruct *extra, int32 xAngle, int32 yAngle, int32 x, int32 extraAngle) {
|
||||
extra->type |= ExtraType::FLY;
|
||||
|
||||
extra->lastPos = extra->pos;
|
||||
|
||||
IVec2 destPos = _engine->_renderer->rotate(x, 0, xAngle);
|
||||
|
||||
extra->destPos.y = -destPos.y;
|
||||
|
||||
destPos = _engine->_renderer->rotate(0, destPos.x, yAngle);
|
||||
|
||||
extra->destPos.x = destPos.x;
|
||||
extra->destPos.z = destPos.y;
|
||||
|
||||
extra->angle = extraAngle;
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
}
|
||||
|
||||
int32 Extra::initSpecial(int32 x, int32 y, int32 z, ExtraSpecialType type) {
|
||||
const int16 flag = EXTRA_SPECIAL_MASK + (int16)type;
|
||||
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = flag;
|
||||
extra->info1 = 0;
|
||||
|
||||
if (type == ExtraSpecialType::kHitStars) {
|
||||
extra->type = ExtraType::TIME_OUT | ExtraType::END_COL;
|
||||
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
|
||||
initFly(extra, _engine->getRandomNumber(LBAAngles::ANGLE_90) + LBAAngles::ANGLE_45, _engine->getRandomNumber(LBAAngles::ANGLE_360), 50, 20);
|
||||
|
||||
extra->strengthOfHit = 0;
|
||||
extra->payload.lifeTime = 100;
|
||||
} else if (type == ExtraSpecialType::kExplodeCloud) {
|
||||
extra->type = ExtraType::TIME_OUT;
|
||||
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
|
||||
extra->strengthOfHit = 0;
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
extra->payload.lifeTime = 5;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Extra::getBonusSprite(BonusParameter bonusParameter) const {
|
||||
int numBonus = 0;
|
||||
int8 bonusSprites[5];
|
||||
if (bonusParameter.kashes) {
|
||||
bonusSprites[numBonus++] = SPRITEHQR_KASHES;
|
||||
}
|
||||
if (bonusParameter.lifepoints) {
|
||||
bonusSprites[numBonus++] = SPRITEHQR_LIFEPOINTS;
|
||||
}
|
||||
if (bonusParameter.magicpoints) {
|
||||
bonusSprites[numBonus++] = SPRITEHQR_MAGICPOINTS;
|
||||
}
|
||||
if (bonusParameter.key) {
|
||||
bonusSprites[numBonus++] = SPRITEHQR_KEY;
|
||||
}
|
||||
if (bonusParameter.cloverleaf) {
|
||||
bonusSprites[numBonus++] = SPRITEHQR_CLOVERLEAF;
|
||||
}
|
||||
|
||||
if (numBonus == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int bonusIndex = _engine->getRandomNumber(numBonus);
|
||||
assert(bonusIndex >= 0);
|
||||
assert(bonusIndex < numBonus);
|
||||
int8 bonusSprite = bonusSprites[bonusIndex];
|
||||
// if bonus is magic and no magic level yet, then give life points
|
||||
if (!_engine->_gameState->_magicLevelIdx && bonusSprite == SPRITEHQR_MAGICPOINTS) {
|
||||
bonusSprite = SPRITEHQR_LIFEPOINTS;
|
||||
}
|
||||
|
||||
return bonusSprite;
|
||||
}
|
||||
|
||||
int32 Extra::addExtraBonus(int32 x, int32 y, int32 z, int32 xAngle, int32 yAngle, int32 type, int32 bonusAmount) {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = type;
|
||||
extra->type = ExtraType::STOP_COL | ExtraType::TAKABLE | ExtraType::WAIT_SOME_TIME;
|
||||
|
||||
if (type != SPRITEHQR_KEY) {
|
||||
extra->type |= ExtraType::TIME_OUT | ExtraType::FLASH;
|
||||
}
|
||||
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
|
||||
initFly(extra, xAngle, yAngle, 40, ToAngle(15));
|
||||
|
||||
extra->strengthOfHit = 0;
|
||||
extra->payload.lifeTime = _engine->toSeconds(20);
|
||||
extra->info1 = bonusAmount;
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 Extra::throwExtra(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 xAngle, int32 yAngle, int32 xRotPoint, int32 extraAngle, int32 strengthOfHit) {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = spriteIdx;
|
||||
extra->type = ExtraType::END_OBJ | ExtraType::END_COL | ExtraType::IMPACT | ExtraType::WAIT_NO_COL;
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
|
||||
initFly(extra, xAngle, yAngle, xRotPoint, extraAngle);
|
||||
|
||||
extra->strengthOfHit = strengthOfHit;
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
extra->payload.actorIdx = actorIdx;
|
||||
extra->info1 = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 Extra::addExtraAiming(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 targetActorIdx, int32 finalAngle, int32 strengthOfHit) {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = spriteIdx;
|
||||
extra->type = ExtraType::SEARCH_OBJ;
|
||||
extra->info1 = 0;
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
extra->payload.actorIdx = actorIdx;
|
||||
extra->spawnTime = targetActorIdx;
|
||||
extra->destPos.z = finalAngle;
|
||||
extra->strengthOfHit = strengthOfHit;
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, finalAngle, LBAAngles::ANGLE_17, &extra->trackActorMove);
|
||||
const ActorStruct *actor = _engine->_scene->getActor(targetActorIdx);
|
||||
extra->angle = _engine->_movements->getAngle(extra->pos, actor->posObj());
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 Extra::searchBonusKey() const {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
const ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite == SPRITEHQR_KEY) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 Extra::extraSearchKey(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 extraIdx) {
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite != -1) {
|
||||
continue;
|
||||
}
|
||||
extra->sprite = spriteIdx;
|
||||
extra->type = ExtraType::MAGIC_BALL_KEY;
|
||||
extra->info1 = 0;
|
||||
extra->pos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->pos.z = z;
|
||||
extra->payload.extraIdx = extraIdx;
|
||||
extra->destPos.z = 4000;
|
||||
extra->strengthOfHit = 0;
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, 4000, LBAAngles::ANGLE_17, &extra->trackActorMove);
|
||||
extra->angle = _engine->_movements->getAngle(extra->pos, _extraList[extraIdx].pos);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Extra::addExtraThrowMagicball(int32 x, int32 y, int32 z, int32 xAngle, int32 yAngle, int32 xRotPoint, int32 extraAngle) {
|
||||
int32 ballSprite = -1;
|
||||
int32 ballStrength = 0;
|
||||
|
||||
switch (_engine->_gameState->_magicLevelIdx) {
|
||||
case 0:
|
||||
case 1:
|
||||
ballSprite = SPRITEHQR_MAGICBALL_YELLOW;
|
||||
ballStrength = 4;
|
||||
break;
|
||||
case 2:
|
||||
ballSprite = SPRITEHQR_MAGICBALL_GREEN;
|
||||
ballStrength = 6;
|
||||
break;
|
||||
case 3:
|
||||
ballSprite = SPRITEHQR_MAGICBALL_RED;
|
||||
ballStrength = 8;
|
||||
break;
|
||||
case 4:
|
||||
ballSprite = SPRITEHQR_MAGICBALL_FIRE;
|
||||
ballStrength = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
_engine->_gameState->_magicBallType = ((_engine->_gameState->_magicPoint - 1) / 20) + 1;
|
||||
if (_engine->_gameState->_magicPoint == 0) {
|
||||
_engine->_gameState->_magicBallType = 0;
|
||||
}
|
||||
|
||||
const int32 extraIdx = searchBonusKey();
|
||||
if (extraIdx != -1) { // there is a key to aim
|
||||
_engine->_gameState->_magicBallType = 5;
|
||||
}
|
||||
|
||||
switch (_engine->_gameState->_magicBallType) {
|
||||
case 0:
|
||||
_engine->_gameState->_magicBall = throwExtra(OWN_ACTOR_SCENE_INDEX, x, y, z, ballSprite, xAngle, yAngle, xRotPoint, extraAngle, ballStrength);
|
||||
break;
|
||||
case 1:
|
||||
_engine->_gameState->_magicBallCount = 4;
|
||||
_engine->_gameState->_magicBall = throwExtra(OWN_ACTOR_SCENE_INDEX, x, y, z, ballSprite, xAngle, yAngle, xRotPoint, extraAngle, ballStrength);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
_engine->_gameState->_magicBallType = 1;
|
||||
_engine->_gameState->_magicBallCount = 4;
|
||||
_engine->_gameState->_magicBall = throwExtra(OWN_ACTOR_SCENE_INDEX, x, y, z, ballSprite, xAngle, yAngle, xRotPoint, extraAngle, ballStrength);
|
||||
break;
|
||||
case 5:
|
||||
assert(extraIdx != -1);
|
||||
_engine->_gameState->_magicBall = extraSearchKey(OWN_ACTOR_SCENE_INDEX, x, y, z, ballSprite, extraIdx);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_engine->_gameState->_magicPoint > 0) {
|
||||
_engine->_gameState->_magicPoint--;
|
||||
}
|
||||
}
|
||||
|
||||
void Extra::aff2DShape(const ExtraShape &shapeTable, int32 x, int32 y, int32 color, int32 angle, int32 zoom, Common::Rect &renderRect) {
|
||||
int shapeDataIndex = 0;
|
||||
int16 shapeX = shapeTable.data[shapeDataIndex].x * zoom / 16;
|
||||
int16 shapeZ = shapeTable.data[shapeDataIndex].z * zoom / 16;
|
||||
|
||||
++shapeDataIndex;
|
||||
|
||||
_engine->clearScreenMinMax(renderRect);
|
||||
|
||||
IVec2 destPos = _engine->_renderer->rotate(shapeX, shapeZ, angle);
|
||||
|
||||
const int32 computedX = destPos.x + x;
|
||||
const int32 computedY = destPos.y + y;
|
||||
|
||||
_engine->adjustScreenMax(renderRect, computedX, computedY);
|
||||
|
||||
int32 currentX = computedX;
|
||||
int32 currentY = computedY;
|
||||
|
||||
for (int32 numEntries = 1; numEntries < shapeTable.n; ++numEntries) {
|
||||
shapeX = shapeTable.data[shapeDataIndex].x * zoom / 16;
|
||||
shapeZ = shapeTable.data[shapeDataIndex].z * zoom / 16;
|
||||
++shapeDataIndex;
|
||||
|
||||
const int32 oldComputedX = currentX;
|
||||
const int32 oldComputedY = currentY;
|
||||
|
||||
destPos = _engine->_renderer->rotate(shapeX, shapeZ, angle);
|
||||
|
||||
currentX = destPos.x + x;
|
||||
currentY = destPos.y + y;
|
||||
|
||||
_engine->adjustScreenMax(renderRect, currentX, currentY);
|
||||
_engine->_interface->drawLine(oldComputedX, oldComputedY, currentX, currentY, color);
|
||||
}
|
||||
_engine->_interface->drawLine(currentX, currentY, computedX, computedY, color);
|
||||
}
|
||||
|
||||
void Extra::affSpecial(int32 extraIdx, int32 x, int32 y, Common::Rect &renderRect) {
|
||||
ExtraListStruct *extra = &_extraList[extraIdx];
|
||||
ExtraSpecialType specialType = (ExtraSpecialType)(extra->sprite & (EXTRA_SPECIAL_MASK - 1));
|
||||
|
||||
switch (specialType) {
|
||||
case ExtraSpecialType::kHitStars:
|
||||
aff2DShape(hitStarsShape, x, y, COLOR_WHITE, (_engine->timerRef * 32) & LBAAngles::ANGLE_270, 4, renderRect);
|
||||
break;
|
||||
case ExtraSpecialType::kExplodeCloud: {
|
||||
int32 zoom = 1 + _engine->timerRef - extra->spawnTime;
|
||||
|
||||
if (zoom > 32) {
|
||||
zoom = 32;
|
||||
}
|
||||
|
||||
aff2DShape(explodeCloudShape, x, y, COLOR_WHITE, LBAAngles::ANGLE_0, zoom, renderRect);
|
||||
break;
|
||||
}
|
||||
case ExtraSpecialType::kFountain:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Extra::bounceExtra(ExtraListStruct *extra, int32 x, int32 y, int32 z) {
|
||||
if (_engine->_grid->worldColBrick(x, extra->pos.y, z) != ShapeType::kNone) {
|
||||
extra->destPos.y = -extra->destPos.y;
|
||||
}
|
||||
if (_engine->_grid->worldColBrick(extra->pos.x, y, z) != ShapeType::kNone) {
|
||||
extra->destPos.x = -extra->destPos.x;
|
||||
}
|
||||
if (_engine->_grid->worldColBrick(x, y, extra->pos.z) != ShapeType::kNone) {
|
||||
extra->destPos.z = -extra->destPos.z;
|
||||
}
|
||||
|
||||
extra->pos.x = x;
|
||||
extra->lastPos.x = x;
|
||||
extra->pos.y = y;
|
||||
extra->lastPos.y = y;
|
||||
extra->pos.z = z;
|
||||
extra->lastPos.z = z;
|
||||
|
||||
extra->spawnTime = _engine->timerRef;
|
||||
}
|
||||
|
||||
void Extra::gereExtras() {
|
||||
int32 currentExtraX = 0;
|
||||
int32 currentExtraY = 0;
|
||||
int32 currentExtraZ = 0;
|
||||
|
||||
for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
|
||||
ExtraListStruct *extra = &_extraList[i];
|
||||
if (extra->sprite == -1) {
|
||||
continue;
|
||||
}
|
||||
// process extra life time
|
||||
if (extra->type & ExtraType::TIME_OUT) {
|
||||
if (extra->payload.lifeTime + extra->spawnTime <= _engine->timerRef) {
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// reset extra
|
||||
if (extra->type & ExtraType::ONE_FRAME) {
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
const int32 deltaT = _engine->timerRef - extra->spawnTime;
|
||||
|
||||
if (extra->type & ExtraType::EXPLOSION) {
|
||||
extra->sprite = boundRuleThree(SPRITEHQR_EXPLOSION_FIRST_FRAME, 100, 30, deltaT);
|
||||
continue;
|
||||
}
|
||||
// process extra moving
|
||||
if (extra->type & ExtraType::FLY) {
|
||||
currentExtraX = extra->pos.x;
|
||||
currentExtraY = extra->pos.y;
|
||||
currentExtraZ = extra->pos.z;
|
||||
|
||||
const int32 currentExtraSpeedX = extra->destPos.x * deltaT;
|
||||
extra->pos.x = currentExtraSpeedX + extra->lastPos.x;
|
||||
|
||||
const int32 currentExtraSpeedY = extra->destPos.y * deltaT;
|
||||
extra->pos.y = currentExtraSpeedY + extra->lastPos.y - ABS(extra->angle * deltaT * deltaT / 16);
|
||||
|
||||
extra->pos.z = extra->destPos.z * deltaT + extra->lastPos.z;
|
||||
|
||||
// check if extra is out of scene
|
||||
if (extra->pos.y < 0 || extra->pos.x < 0 || extra->pos.x > SCENE_SIZE_MAX || extra->pos.z < 0 || extra->pos.z > SCENE_SIZE_MAX) {
|
||||
// if extra is Magic Ball
|
||||
if (i == _engine->_gameState->_magicBall) {
|
||||
int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
|
||||
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_GREEN) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
|
||||
}
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_RED) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
|
||||
}
|
||||
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z,
|
||||
spriteIdx, OWN_ACTOR_SCENE_INDEX, 10000, 0);
|
||||
}
|
||||
|
||||
// if can take extra on ground
|
||||
if (extra->type & ExtraType::TAKABLE) {
|
||||
extra->type &= ~(ExtraType::FLY | ExtraType::STOP_COL);
|
||||
} else {
|
||||
extra->sprite = -1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (extra->type & ExtraType::WAIT_SOME_TIME) {
|
||||
if (_engine->timerRef - extra->spawnTime > 40) {
|
||||
extra->type &= ~ExtraType::WAIT_SOME_TIME;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// process actor target hit
|
||||
if (extra->type & ExtraType::SEARCH_OBJ) {
|
||||
int32 actorIdxAttacked = extra->spawnTime;
|
||||
int32 actorIdx = extra->payload.actorIdx;
|
||||
|
||||
const ActorStruct *actor = _engine->_scene->getActor(actorIdxAttacked);
|
||||
currentExtraX = actor->_posObj.x;
|
||||
currentExtraY = actor->_posObj.y + 1000;
|
||||
currentExtraZ = actor->_posObj.z;
|
||||
|
||||
const int32 tmpAngle = _engine->_movements->getAngle(extra->pos, actor->posObj());
|
||||
const int32 angle = ClampAngle(tmpAngle - extra->angle);
|
||||
|
||||
if (angle > LBAAngles::ANGLE_140 && angle < LBAAngles::ANGLE_210) {
|
||||
if (extra->strengthOfHit) {
|
||||
_engine->_actor->hitObj(actorIdx, actorIdxAttacked, extra->strengthOfHit, -1);
|
||||
}
|
||||
|
||||
if (i == _engine->_gameState->_magicBall) {
|
||||
_engine->_gameState->_magicBall = -1;
|
||||
}
|
||||
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32 angle2 = _engine->_movements->getAngle(extra->pos.y, 0, currentExtraY, _engine->_movements->_targetActorDistance);
|
||||
int32 pos = extra->trackActorMove.getRealValueFromTime(_engine->timerRef);
|
||||
if (!pos) {
|
||||
pos = 1;
|
||||
}
|
||||
|
||||
IVec2 destPos = _engine->_renderer->rotate(pos, 0, angle2);
|
||||
extra->pos.y -= destPos.y;
|
||||
|
||||
destPos = _engine->_renderer->rotate(0, destPos.x, tmpAngle);
|
||||
extra->pos.x += destPos.x;
|
||||
extra->pos.z += destPos.y;
|
||||
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, extra->destPos.z, LBAAngles::ANGLE_17, &extra->trackActorMove);
|
||||
|
||||
if (actorIdxAttacked == _engine->_collision->extraCheckObjCol(extra, actorIdx)) {
|
||||
if (i == _engine->_gameState->_magicBall) {
|
||||
_engine->_gameState->_magicBall = -1;
|
||||
}
|
||||
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// process magic ball extra aiming for key
|
||||
if (extra->type & ExtraType::MAGIC_BALL_KEY) {
|
||||
ExtraListStruct *extraKey = &_extraList[extra->payload.extraIdx];
|
||||
const int32 extraIdx = extra->payload.extraIdx;
|
||||
|
||||
const int32 tmpAngle = _engine->_movements->getAngle(extra->pos, extraKey->pos);
|
||||
const int32 angle = ClampAngle(tmpAngle - extra->angle);
|
||||
|
||||
if (angle > LBAAngles::ANGLE_140 && angle < LBAAngles::ANGLE_210) {
|
||||
_engine->_sound->mixSample3D(Samples::ItemFound, 0x1000, 1, _engine->_scene->_sceneHero->posObj(), OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
if (extraKey->info1 > 1) {
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(extraKey->pos - _engine->_grid->_worldCube);
|
||||
_engine->_redraw->addOverlay(OverlayType::koNumber, extraKey->info1, projPos.x, projPos.y, COLOR_BLACK, OverlayPosType::koNormal, 2);
|
||||
}
|
||||
|
||||
_engine->_redraw->addOverlay(OverlayType::koSprite, SPRITEHQR_KEY, 10, 30, 0, OverlayPosType::koNormal, 2);
|
||||
|
||||
_engine->_gameState->addKeys(extraKey->info1);
|
||||
extraKey->sprite = -1;
|
||||
|
||||
extra->sprite = -1;
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z, SPRITEHQR_KEY, 0, 8000, 0);
|
||||
continue;
|
||||
}
|
||||
const int32 angle2 = _engine->_movements->getAngle(extra->pos.y, 0, extraKey->pos.y, _engine->_movements->_targetActorDistance);
|
||||
int32 pos = extra->trackActorMove.getRealValueFromTime(_engine->timerRef);
|
||||
|
||||
if (!pos) {
|
||||
pos = 1;
|
||||
}
|
||||
|
||||
IVec2 destPos = _engine->_renderer->rotate(pos, 0, angle2);
|
||||
extra->pos.y -= destPos.y;
|
||||
|
||||
destPos = _engine->_renderer->rotate(0, destPos.x, tmpAngle);
|
||||
extra->pos.x += destPos.x;
|
||||
extra->pos.z += destPos.y;
|
||||
|
||||
_engine->_movements->initRealValue(LBAAngles::ANGLE_0, extra->destPos.z, LBAAngles::ANGLE_17, &extra->trackActorMove);
|
||||
|
||||
if (extraIdx == _engine->_collision->extraCheckExtraCol(extra, _engine->_gameState->_magicBall)) {
|
||||
_engine->_sound->mixSample3D(Samples::ItemFound, 0x1000, 1, _engine->_scene->_sceneHero->posObj(), OWN_ACTOR_SCENE_INDEX);
|
||||
|
||||
if (extraKey->info1 > 1) {
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(extraKey->pos - _engine->_grid->_worldCube);
|
||||
_engine->_redraw->addOverlay(OverlayType::koNumber, extraKey->info1, projPos.x, projPos.y, COLOR_BLACK, OverlayPosType::koNormal, 2);
|
||||
}
|
||||
|
||||
_engine->_redraw->addOverlay(OverlayType::koSprite, SPRITEHQR_KEY, 10, 30, 0, OverlayPosType::koNormal, 2);
|
||||
|
||||
_engine->_gameState->addKeys(extraKey->info1);
|
||||
extraKey->sprite = -1;
|
||||
|
||||
extra->sprite = -1;
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z, SPRITEHQR_KEY, 0, 8000, 0);
|
||||
continue;
|
||||
}
|
||||
if (extraKey->sprite == -1) {
|
||||
int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
|
||||
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_GREEN) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
|
||||
}
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_RED) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
|
||||
}
|
||||
|
||||
extra->sprite = -1;
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z,
|
||||
spriteIdx, 0, 8000, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// process extra collision with actors
|
||||
if (extra->type & ExtraType::END_OBJ) {
|
||||
if (_engine->_collision->extraCheckObjCol(extra, extra->payload.actorIdx) != -1) {
|
||||
// if extra is Magic Ball
|
||||
if (i == _engine->_gameState->_magicBall) {
|
||||
int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
|
||||
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_GREEN) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
|
||||
}
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_RED) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
|
||||
}
|
||||
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z,
|
||||
spriteIdx, 0, 10000, 0);
|
||||
}
|
||||
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// process extra collision with scene ground
|
||||
if (extra->type & ExtraType::END_COL) {
|
||||
bool flagcol = false;
|
||||
|
||||
if (_engine->_collision->fullWorldColBrick(currentExtraX, currentExtraY, currentExtraZ, extra->pos)) {
|
||||
// if not touch the ground
|
||||
if (!(extra->type & ExtraType::WAIT_NO_COL)) {
|
||||
flagcol = true;
|
||||
}
|
||||
} else {
|
||||
// if touch the ground
|
||||
if (extra->type & ExtraType::WAIT_NO_COL) {
|
||||
extra->type &= ~ExtraType::WAIT_NO_COL; // set flag out of ground
|
||||
}
|
||||
}
|
||||
|
||||
if (flagcol) {
|
||||
// show explode cloud
|
||||
if (extra->type & ExtraType::IMPACT) {
|
||||
initSpecial(currentExtraX, currentExtraY, currentExtraZ, ExtraSpecialType::kExplodeCloud);
|
||||
}
|
||||
// if extra is magic ball
|
||||
if (i == _engine->_gameState->_magicBall) {
|
||||
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(300) - 150;
|
||||
_engine->_sound->mixSample3D(Samples::Hit, pitchBend, 1, extra->pos, -1);
|
||||
|
||||
// can't bounce with not magic points
|
||||
if (_engine->_gameState->_magicBallType <= 0) {
|
||||
int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
|
||||
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_GREEN) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
|
||||
}
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_RED) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
|
||||
}
|
||||
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z, spriteIdx, 0, 10000, 0);
|
||||
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if has magic points
|
||||
if (_engine->_gameState->_magicBallType == 1) {
|
||||
if (!_engine->_gameState->_magicBallCount--) {
|
||||
int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
|
||||
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_GREEN) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
|
||||
}
|
||||
if (extra->sprite == SPRITEHQR_MAGICBALL_RED) {
|
||||
spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
|
||||
}
|
||||
|
||||
_engine->_gameState->_magicBall = extraSearch(-1, extra->pos.x, extra->pos.y, extra->pos.z, spriteIdx, 0, 10000, 0);
|
||||
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
bounceExtra(extra, currentExtraX, currentExtraY, currentExtraZ);
|
||||
}
|
||||
} else {
|
||||
extra->sprite = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// extra stop moving while collision with bricks
|
||||
if (extra->type & ExtraType::STOP_COL) {
|
||||
bool process = false;
|
||||
|
||||
if (_engine->_collision->fullWorldColBrick(currentExtraX, currentExtraY, currentExtraZ, extra->pos)) {
|
||||
// if not touch the ground
|
||||
if (!(extra->type & ExtraType::WAIT_NO_COL)) {
|
||||
process = true;
|
||||
}
|
||||
} else {
|
||||
// if touch the ground
|
||||
if (extra->type & ExtraType::WAIT_NO_COL) {
|
||||
extra->type &= ~ExtraType::WAIT_NO_COL; // set flag out of ground
|
||||
}
|
||||
}
|
||||
|
||||
if (process) {
|
||||
const BoundingBox *bbox = _engine->_resources->_spriteBoundingBox.bbox(extra->sprite);
|
||||
extra->pos.y = (_engine->_collision->_collision.y * SIZE_BRICK_Y) + SIZE_BRICK_Y - bbox->mins.y;
|
||||
extra->type &= ~(ExtraType::STOP_COL | ExtraType::FLY);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// get extras on ground
|
||||
if ((extra->type & ExtraType::TAKABLE) && !(extra->type & ExtraType::FLY)) {
|
||||
// if hero touch extra
|
||||
if (_engine->_collision->extraCheckObjCol(extra, -1) == 0) {
|
||||
_engine->_sound->mixSample3D(Samples::ItemFound, 0x1000, 1, extra->pos, -1);
|
||||
|
||||
if (extra->info1 > 1) {
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(extra->pos - _engine->_grid->_worldCube);
|
||||
const int16 fontColor = COLOR_158;
|
||||
_engine->_redraw->addOverlay(OverlayType::koNumber, extra->info1, projPos.x, projPos.y, fontColor, OverlayPosType::koNormal, 2);
|
||||
}
|
||||
|
||||
_engine->_redraw->addOverlay(OverlayType::koSprite, extra->sprite, 10, 30, 0, OverlayPosType::koNormal, 2);
|
||||
|
||||
if (extra->sprite == SPRITEHQR_KASHES) {
|
||||
_engine->_gameState->addKashes(extra->info1);
|
||||
} else if (extra->sprite == SPRITEHQR_LIFEPOINTS) {
|
||||
_engine->_scene->_sceneHero->addLife(extra->info1);
|
||||
} else if (extra->sprite == SPRITEHQR_MAGICPOINTS && _engine->_gameState->_magicLevelIdx) {
|
||||
_engine->_gameState->addMagicPoints(extra->info1 * 2);
|
||||
} else if (extra->sprite == SPRITEHQR_KEY) {
|
||||
_engine->_gameState->addKeys(extra->info1);
|
||||
} else if (extra->sprite == SPRITEHQR_CLOVERLEAF) {
|
||||
_engine->_gameState->addLeafs(extra->info1);
|
||||
}
|
||||
|
||||
extra->sprite = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
139
engines/twine/scene/extra.h
Normal file
139
engines/twine/scene/extra.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_EXTRA_H
|
||||
#define TWINE_SCENE_EXTRA_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "twine/scene/actor.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
#define EXTRA_MAX_ENTRIES 50
|
||||
|
||||
#define EXTRA_SPECIAL_MASK 0x8000
|
||||
|
||||
struct ShapeData {
|
||||
int16 x;
|
||||
int16 z;
|
||||
};
|
||||
|
||||
struct ExtraShape {
|
||||
int n;
|
||||
const ShapeData *data;
|
||||
};
|
||||
|
||||
enum ExtraType {
|
||||
TIME_OUT = 1 << 0, // 0x0001
|
||||
FLY = 1 << 1, // 0x0002
|
||||
END_OBJ = 1 << 2, // 0x0004
|
||||
END_COL = 1 << 3, // 0x0008
|
||||
STOP_COL = 1 << 4, // 0x0010
|
||||
TAKABLE = 1 << 5, // 0x0020
|
||||
FLASH = 1 << 6, // 0x0040
|
||||
SEARCH_OBJ = 1 << 7, // 0x0080
|
||||
IMPACT = 1 << 8, // 0x0100
|
||||
MAGIC_BALL_KEY = 1 << 9, // 0x0200
|
||||
TIME_IN = 1 << 10, // 0x0400
|
||||
ONE_FRAME = 1 << 11, // 0x0800
|
||||
EXPLOSION = 1 << 12, // 0x1000 EXTRA_EXPLO
|
||||
WAIT_NO_COL = 1 << 13, // 0x2000 EXTRA_WAIT_NO_COL
|
||||
WAIT_SOME_TIME = 1 << 14, // 0x4000
|
||||
COMPUTE_TRAJ = 1 << 15 // 0x8000 used in dotemu enhanced to render the magic ball trajectories
|
||||
};
|
||||
|
||||
struct ExtraListStruct {
|
||||
int16 sprite = 0; /**< a value of -1 indicates that this instance is free to use */
|
||||
IVec3 pos;
|
||||
IVec3 lastPos;
|
||||
IVec3 destPos;
|
||||
|
||||
RealValue trackActorMove;
|
||||
|
||||
uint16 type = 0; /**< ExtraType bitmask */
|
||||
int16 angle = 0; // weight
|
||||
int32 spawnTime = 0; // memo timer 50hz
|
||||
union payload { // field_ 1C
|
||||
int16 lifeTime;
|
||||
int16 actorIdx;
|
||||
int16 extraIdx;
|
||||
int16 unknown;
|
||||
} payload{0};
|
||||
int16 strengthOfHit = 0; // apply damage if != 0
|
||||
int16 info1 = 0; // various - number for zone giver
|
||||
};
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
class Extra {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
void initFly(ExtraListStruct *extra, int32 xAngle, int32 yAngle, int32 x, int32 extraAngle);
|
||||
void bounceExtra(ExtraListStruct *extra, int32 x, int32 y, int32 z);
|
||||
int32 searchBonusKey() const;
|
||||
int32 extraSearchKey(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 extraIdx);
|
||||
void aff2DShape(const ExtraShape &shapeTable, int32 x, int32 y, int32 color, int32 angle, int32 zoom, Common::Rect &renderRect);
|
||||
|
||||
public:
|
||||
Extra(TwinEEngine *engine);
|
||||
ExtraListStruct _extraList[EXTRA_MAX_ENTRIES];
|
||||
|
||||
int32 extraSearch(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 targetActor, int32 maxSpeed, int32 strengthOfHit);
|
||||
|
||||
/**
|
||||
* Add extra explosion
|
||||
* @param x Explosion X coordinate
|
||||
* @param y Explosion Y coordinate
|
||||
* @param z Explosion Z coordinate
|
||||
*/
|
||||
int32 addExtraExplode(int32 x, int32 y, int32 z);
|
||||
|
||||
inline int32 extraExplo(const IVec3 &pos) {
|
||||
return addExtraExplode(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
/** Reset all used extras */
|
||||
void clearExtra();
|
||||
|
||||
int32 initSpecial(int32 x, int32 y, int32 z, ExtraSpecialType type);
|
||||
int32 addExtraBonus(int32 x, int32 y, int32 z, int32 xAngle, int32 yAngle, int32 type, int32 bonusAmount);
|
||||
|
||||
inline int32 addExtraBonus(const IVec3 &pos, int32 xAngle, int32 yAngle, int32 type, int32 bonusAmount) {
|
||||
return addExtraBonus(pos.x, pos.y, pos.z, xAngle, yAngle, type, bonusAmount);
|
||||
}
|
||||
|
||||
int32 throwExtra(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 xAngle, int32 yAngle, int32 xRotPoint, int32 extraAngle, int32 strengthOfHit);
|
||||
int32 addExtraAiming(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx, int32 targetActorIdx, int32 finalAngle, int32 strengthOfHit);
|
||||
void addExtraThrowMagicball(int32 x, int32 y, int32 z, int32 xAngle, int32 yAngle, int32 xRotPoint, int32 extraAngle);
|
||||
|
||||
void affSpecial(int32 extraIdx, int32 x, int32 y, Common::Rect &renderRect);
|
||||
|
||||
int getBonusSprite(BonusParameter bonusParameter) const;
|
||||
|
||||
/** Process extras */
|
||||
void gereExtras();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
681
engines/twine/scene/gamestate.cpp
Normal file
681
engines/twine/scene/gamestate.cpp
Normal file
@@ -0,0 +1,681 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "twine/audio/music.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/menu/menu.h"
|
||||
#include "twine/menu/menuoptions.h"
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/extra.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/text.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
#define SIZE_FOUND_OBJ 130
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
GameState::GameState(TwinEEngine *engine) : _engine(engine) {
|
||||
clearGameFlags();
|
||||
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
|
||||
Common::fill(&_holomapFlags[0], &_holomapFlags[MAX_HOLO_POS_2], 0);
|
||||
Common::fill(&_gameListChoice[0], &_gameListChoice[10], TextId::kNone);
|
||||
}
|
||||
|
||||
void GameState::init3DGame() {
|
||||
_engine->_renderer->setIsoProjection(_engine->width() / 2 - 8 - 1, _engine->height() / 2, SIZE_BRICK_XZ);
|
||||
_engine->_renderer->setPosCamera(0, 0, 0);
|
||||
_engine->_renderer->setAngleCamera(LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0);
|
||||
_engine->_renderer->setLightVector(_engine->_scene->_alphaLight, _engine->_scene->_betaLight, LBAAngles::ANGLE_0);
|
||||
}
|
||||
|
||||
void GameState::initGameStateVars() {
|
||||
debug(2, "Init game state variables");
|
||||
_engine->_extra->clearExtra();
|
||||
|
||||
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
|
||||
_engine->_redraw->overlayList[i].num = -1;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < ARRAYSIZE(_engine->_scene->_listFlagCube); i++) {
|
||||
_engine->_scene->_listFlagCube[i] = 0;
|
||||
}
|
||||
|
||||
clearGameFlags();
|
||||
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
|
||||
|
||||
_engine->_scene->initSceneVars();
|
||||
|
||||
Common::fill(&_holomapFlags[0], &_holomapFlags[MAX_HOLO_POS_2], 0);
|
||||
}
|
||||
|
||||
void GameState::initHeroVars() {
|
||||
_engine->_actor->initObject(OWN_ACTOR_SCENE_INDEX); // reset Hero
|
||||
|
||||
_magicBall = -1;
|
||||
|
||||
_inventoryNumLeafsBox = 2;
|
||||
_inventoryNumLeafs = 2;
|
||||
_goldPieces = 0;
|
||||
_nbLittleKeys = 0;
|
||||
_magicPoint = 0;
|
||||
|
||||
_weapon = false;
|
||||
|
||||
_engine->_scene->_sceneHero->_genBody = BodyType::btNormal;
|
||||
_engine->_scene->_sceneHero->setLife(_engine->getMaxLife());
|
||||
_engine->_scene->_sceneHero->_talkColor = COLOR_BRIGHT_BLUE;
|
||||
}
|
||||
|
||||
void GameState::initEngineVars() {
|
||||
debug(2, "Init engine variables");
|
||||
_engine->_interface->unsetClip();
|
||||
|
||||
_engine->_scene->_alphaLight = LBAAngles::ANGLE_315;
|
||||
_engine->_scene->_betaLight = LBAAngles::ANGLE_334;
|
||||
init3DGame();
|
||||
initGameStateVars();
|
||||
initHeroVars();
|
||||
|
||||
_engine->_scene->_sceneStart.x = 16 * SIZE_BRICK_XZ;
|
||||
_engine->_scene->_sceneStart.y = 24 * SIZE_BRICK_Y;
|
||||
_engine->_scene->_sceneStart.z = 16 * SIZE_BRICK_XZ;
|
||||
|
||||
_engine->_scene->_numCube = SCENE_CEILING_GRID_FADE_1;
|
||||
_engine->_scene->_newCube = LBA1SceneId::Citadel_Island_Prison;
|
||||
_engine->_sceneLoopState = SceneLoopState::Continue;
|
||||
_engine->_scene->_mecaPenguinIdx = -1;
|
||||
_engine->_menuOptions->flagCredits = false;
|
||||
|
||||
_inventoryNumLeafs = 0;
|
||||
_inventoryNumLeafsBox = 2;
|
||||
_magicPoint = 0;
|
||||
_goldPieces = 0;
|
||||
_nbLittleKeys = 0;
|
||||
_inventoryNumGas = 0;
|
||||
|
||||
_engine->_actor->_cropBottomScreen = 0;
|
||||
|
||||
_magicLevelIdx = 0;
|
||||
_weapon = false;
|
||||
|
||||
setChapter(0);
|
||||
|
||||
_engine->_scene->_sceneTextBank = TextBankId::Options_and_menus;
|
||||
_engine->_scene->_numObjFollow = OWN_ACTOR_SCENE_INDEX;
|
||||
_engine->_actor->_heroBehaviour = HeroBehaviourType::kNormal;
|
||||
_engine->_actor->_previousHeroAngle = 0;
|
||||
_engine->_actor->_previousHeroBehaviour = HeroBehaviourType::kNormal;
|
||||
}
|
||||
|
||||
// https://web.archive.org/web/*/http://lbafileinfo.kazekr.net/index.php?title=LBA1:Savegame
|
||||
bool GameState::loadGame(Common::SeekableReadStream *file) {
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_engine->isLBA1()) {
|
||||
warning("Loading not implemented for lba2");
|
||||
return false;
|
||||
}
|
||||
|
||||
debug(2, "Load game");
|
||||
const byte saveFileVersion = file->readByte();
|
||||
// 4 is dotemu enhanced version of lba1
|
||||
if (saveFileVersion != 3 && saveFileVersion != 4) {
|
||||
warning("Could not load savegame - wrong magic byte");
|
||||
return false;
|
||||
}
|
||||
|
||||
initEngineVars();
|
||||
|
||||
int playerNameIdx = 0;
|
||||
do {
|
||||
const byte c = file->readByte();
|
||||
_engine->_menuOptions->_saveGameName[playerNameIdx++] = c;
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
if (playerNameIdx >= ARRAYSIZE(_engine->_menuOptions->_saveGameName)) {
|
||||
warning("Failed to load savegame. Invalid playername.");
|
||||
return false;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
byte numGameFlags = file->readByte();
|
||||
if (numGameFlags != NUM_GAME_FLAGS_LBA1) {
|
||||
warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS_LBA1, numGameFlags);
|
||||
return false;
|
||||
}
|
||||
for (uint8 i = 0; i < numGameFlags; ++i) {
|
||||
setGameFlag(i, file->readByte());
|
||||
}
|
||||
_engine->_scene->_newCube = file->readByte(); // scene index
|
||||
setChapter(file->readByte());
|
||||
|
||||
_engine->_actor->_heroBehaviour = (HeroBehaviourType)file->readByte();
|
||||
_engine->_actor->_previousHeroBehaviour = _engine->_actor->_heroBehaviour;
|
||||
_engine->_scene->_sceneHero->setLife(file->readByte());
|
||||
setKashes(file->readSint16LE());
|
||||
_magicLevelIdx = file->readByte();
|
||||
setMagicPoints(file->readByte());
|
||||
setLeafBoxes(file->readByte());
|
||||
_engine->_scene->_sceneStart.x = file->readSint16LE();
|
||||
_engine->_scene->_sceneStart.y = file->readSint16LE();
|
||||
_engine->_scene->_sceneStart.z = file->readSint16LE();
|
||||
_engine->_scene->_sceneHero->_beta = ToAngle(file->readSint16LE());
|
||||
_engine->_actor->_previousHeroAngle = _engine->_scene->_sceneHero->_beta;
|
||||
_engine->_scene->_sceneHero->_genBody = (BodyType)file->readByte();
|
||||
|
||||
const byte numHolomapFlags = file->readByte(); // number of holomap locations
|
||||
if (numHolomapFlags != _engine->numHoloPos()) {
|
||||
warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, _engine->numHoloPos());
|
||||
return false;
|
||||
}
|
||||
file->read(_holomapFlags, _engine->numHoloPos());
|
||||
|
||||
setGas(file->readByte());
|
||||
|
||||
const byte numInventoryFlags = file->readByte(); // number of used inventory items, always 28
|
||||
if (numInventoryFlags != NUM_INVENTORY_ITEMS) {
|
||||
warning("Failed to load inventoryFlags. Got %u, expected %i", numInventoryFlags, NUM_INVENTORY_ITEMS);
|
||||
return false;
|
||||
}
|
||||
file->read(_inventoryFlags, NUM_INVENTORY_ITEMS);
|
||||
|
||||
setLeafs(file->readByte());
|
||||
_weapon = file->readByte();
|
||||
|
||||
if (saveFileVersion == 4) {
|
||||
// the time the game was played
|
||||
file->readUint32LE();
|
||||
file->readUint32LE();
|
||||
}
|
||||
|
||||
_engine->_scene->_numCube = SCENE_CEILING_GRID_FADE_1;
|
||||
_engine->_scene->_flagChgCube = ScenePositionType::kReborn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameState::saveGame(Common::WriteStream *file) {
|
||||
debug(2, "Save game");
|
||||
if (!_engine->isLBA1()) {
|
||||
warning("Saving not implemented for lba2");
|
||||
return false;
|
||||
}
|
||||
if (_engine->_menuOptions->_saveGameName[0] == '\0') {
|
||||
Common::strlcpy(_engine->_menuOptions->_saveGameName, "TwinEngineSave", sizeof(_engine->_menuOptions->_saveGameName));
|
||||
}
|
||||
|
||||
int32 sceneIdx = _engine->_scene->_numCube;
|
||||
if (sceneIdx == Polar_Island_end_scene || sceneIdx == Citadel_Island_end_sequence_1 || sceneIdx == Citadel_Island_end_sequence_2 || sceneIdx == Credits_List_Sequence) {
|
||||
/* inventoryMagicPoints = 0x50 */
|
||||
/* herobehaviour = 0 */
|
||||
/* newheropos.x = 0xffff */
|
||||
sceneIdx = Polar_Island_Final_Battle;
|
||||
}
|
||||
|
||||
file->writeByte(0x03);
|
||||
file->writeString(_engine->_menuOptions->_saveGameName);
|
||||
file->writeByte('\0');
|
||||
file->writeByte(NUM_GAME_FLAGS_LBA1);
|
||||
for (uint8 i = 0; i < NUM_GAME_FLAGS_LBA1; ++i) {
|
||||
file->writeByte((uint8)hasGameFlag(i));
|
||||
}
|
||||
file->writeByte(sceneIdx);
|
||||
file->writeByte(getChapter());
|
||||
file->writeByte((byte)_engine->_actor->_heroBehaviour);
|
||||
file->writeByte(_engine->_scene->_sceneHero->_lifePoint);
|
||||
file->writeSint16LE(_goldPieces);
|
||||
file->writeByte(_magicLevelIdx);
|
||||
file->writeByte(_magicPoint);
|
||||
file->writeByte(_inventoryNumLeafsBox);
|
||||
// we don't save the whole scene state - so we have to make sure that the hero is
|
||||
// respawned at the start of the scene - and not at its current position
|
||||
file->writeSint16LE(_engine->_scene->_sceneStart.x);
|
||||
file->writeSint16LE(_engine->_scene->_sceneStart.y);
|
||||
file->writeSint16LE(_engine->_scene->_sceneStart.z);
|
||||
file->writeSint16LE(FromAngle(_engine->_scene->_sceneHero->_beta));
|
||||
file->writeByte((uint8)_engine->_scene->_sceneHero->_genBody);
|
||||
|
||||
// number of holomap locations
|
||||
file->writeByte(_engine->numHoloPos());
|
||||
file->write(_holomapFlags, _engine->numHoloPos());
|
||||
|
||||
file->writeByte(_inventoryNumGas);
|
||||
|
||||
// number of inventory items
|
||||
file->writeByte(NUM_INVENTORY_ITEMS);
|
||||
file->write(_inventoryFlags, NUM_INVENTORY_ITEMS);
|
||||
|
||||
file->writeByte(_inventoryNumLeafs);
|
||||
file->writeByte(_weapon ? 1 : 0);
|
||||
file->writeByte(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameState::setChapter(int16 chapter) {
|
||||
if (_engine->isLBA1()) {
|
||||
_gameChapter = chapter;
|
||||
return;
|
||||
}
|
||||
setGameFlag(253, chapter);
|
||||
}
|
||||
|
||||
int16 GameState::getChapter() const {
|
||||
if (_engine->isLBA1()) {
|
||||
return _gameChapter;
|
||||
}
|
||||
return _listFlagGame[253];
|
||||
}
|
||||
|
||||
void GameState::setGameFlag(uint8 index, int16 value) {
|
||||
if (_listFlagGame[index] == value) {
|
||||
return;
|
||||
}
|
||||
debug(2, "Set gameStateFlags[%u]=%u", index, value);
|
||||
_listFlagGame[index] = value;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((index == GAMEFLAG_VIDEO_BAFFE || index == GAMEFLAG_VIDEO_BAFFE2 || index == GAMEFLAG_VIDEO_BAFFE3 || index == GAMEFLAG_VIDEO_BAFFE5) &&
|
||||
_listFlagGame[GAMEFLAG_VIDEO_BAFFE] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE2] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE3] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE5] != 0) {
|
||||
// all 4 slap videos
|
||||
_engine->unlockAchievement("LBA_ACH_012");
|
||||
} else if (index == GAMEFLAG_VIDEO_BATEAU2) {
|
||||
// second video of ferry trip
|
||||
_engine->unlockAchievement("LBA_ACH_010");
|
||||
} else if (index == (uint8)InventoryItems::kiUseSabre) {
|
||||
_engine->unlockAchievement("LBA_ACH_002");
|
||||
} else if (index == (uint8)InventoryItems::kBottleOfSyrup) {
|
||||
_engine->unlockAchievement("LBA_ACH_007");
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::doFoundObj(InventoryItems item) {
|
||||
_engine->_grid->centerOnActor(_engine->_scene->_sceneHero);
|
||||
|
||||
// Hide hero in scene
|
||||
_engine->_scene->_sceneHero->_flags.bIsInvisible = 1;
|
||||
_engine->_redraw->drawScene(true);
|
||||
_engine->_scene->_sceneHero->_flags.bIsInvisible = 0;
|
||||
|
||||
_engine->saveFrontBuffer();
|
||||
|
||||
IVec3 itemCamera;
|
||||
itemCamera.x = _engine->_grid->_startCube.x * SIZE_BRICK_XZ;
|
||||
itemCamera.y = _engine->_grid->_startCube.y * SIZE_BRICK_Y;
|
||||
itemCamera.z = _engine->_grid->_startCube.z * SIZE_BRICK_XZ;
|
||||
|
||||
BodyData &bodyData = _engine->_scene->_sceneHero->_entityDataPtr->getBody(_engine->_scene->_sceneHero->_body);
|
||||
const IVec3 bodyPos = _engine->_scene->_sceneHero->_posObj - itemCamera;
|
||||
Common::Rect modelRect;
|
||||
_engine->_renderer->renderIsoModel(bodyPos, LBAAngles::ANGLE_0, LBAAngles::ANGLE_45, LBAAngles::ANGLE_0, bodyData, modelRect);
|
||||
_engine->_interface->setClip(modelRect);
|
||||
|
||||
const int32 itemX = (_engine->_scene->_sceneHero->_posObj.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
int32 itemY = _engine->_scene->_sceneHero->_posObj.y / SIZE_BRICK_Y;
|
||||
if (_engine->_scene->_sceneHero->brickShape() != ShapeType::kNone) {
|
||||
itemY++;
|
||||
}
|
||||
const int32 itemZ = (_engine->_scene->_sceneHero->_posObj.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
|
||||
_engine->_grid->drawOverBrick(itemX, itemY, itemZ);
|
||||
|
||||
IVec3 projPos = _engine->_renderer->projectPoint(bodyPos);
|
||||
projPos.y -= 150;
|
||||
|
||||
const int32 boxTopLeftX = projPos.x - (SIZE_FOUND_OBJ / 2);
|
||||
const int32 boxTopLeftY = projPos.y - (SIZE_FOUND_OBJ / 2);
|
||||
const int32 boxBottomRightX = projPos.x + (SIZE_FOUND_OBJ / 2);
|
||||
const int32 boxBottomRightY = projPos.y + (SIZE_FOUND_OBJ / 2);
|
||||
const Common::Rect boxRect(boxTopLeftX, boxTopLeftY, boxBottomRightX, boxBottomRightY);
|
||||
_engine->_sound->mixSample(Samples::BigItemFound, 0x1000, 1, 128, 128);
|
||||
|
||||
// process vox play
|
||||
_engine->_music->stopMusic();
|
||||
_engine->_text->initDial(TextBankId::Inventory_Intro_and_Holomap);
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
_engine->_text->initItemFoundText(item);
|
||||
_engine->_text->initDialWindow();
|
||||
|
||||
ProgressiveTextState textState = ProgressiveTextState::ContinueRunning;
|
||||
|
||||
_engine->_text->initVoxToPlayTextId((TextId)item);
|
||||
|
||||
const int32 bodyAnimIdx = _engine->_animations->searchAnim(AnimationTypes::kFoundItem, OWN_ACTOR_SCENE_INDEX);
|
||||
const AnimData &ptranim = _engine->_resources->_animData[bodyAnimIdx];
|
||||
|
||||
_engine->_animations->stockInterAnim(bodyData, &bodyData._animTimerData);
|
||||
|
||||
uint frameanim = 0;
|
||||
|
||||
_engine->_redraw->_nbOptPhysBox = 0;
|
||||
|
||||
AnimTimerDataStruct animTimerData;
|
||||
ScopedKeyMap uiKeyMap(_engine, uiKeyMapId);
|
||||
int16 itemAngle = LBAAngles::ANGLE_0;
|
||||
for (;;) {
|
||||
FrameMarker frame(_engine, 66);
|
||||
_engine->_interface->unsetClip();
|
||||
_engine->_redraw->_nbPhysBox = 0;
|
||||
_engine->_redraw->clsBoxes();
|
||||
_engine->_interface->shadeBox(boxRect, 4);
|
||||
|
||||
_engine->_interface->setClip(boxRect);
|
||||
|
||||
itemAngle += LBAAngles::ANGLE_2;
|
||||
|
||||
_engine->_renderer->draw3dObject(projPos.x, projPos.y, _engine->_resources->_inventoryTable[item], itemAngle, 10000);
|
||||
|
||||
_engine->_menu->drawRectBorders(boxRect);
|
||||
_engine->_redraw->addPhysBox(boxRect);
|
||||
_engine->_interface->unsetClip();
|
||||
init3DGame();
|
||||
|
||||
if (_engine->_animations->setInterAnimObjet(frameanim, ptranim, bodyData, &animTimerData)) {
|
||||
frameanim++; // keyframe
|
||||
if (frameanim >= ptranim.getNbFramesAnim()) {
|
||||
frameanim = ptranim.getLoopFrame();
|
||||
}
|
||||
}
|
||||
|
||||
_engine->_renderer->renderIsoModel(bodyPos, LBAAngles::ANGLE_0, LBAAngles::ANGLE_45, LBAAngles::ANGLE_0, bodyData, modelRect);
|
||||
_engine->_interface->setClip(modelRect);
|
||||
_engine->_grid->drawOverBrick(itemX, itemY, itemZ);
|
||||
_engine->_redraw->addPhysBox(modelRect);
|
||||
|
||||
if (textState == ProgressiveTextState::ContinueRunning) {
|
||||
_engine->_interface->unsetClip();
|
||||
textState = _engine->_text->nextDialChar();
|
||||
} else {
|
||||
_engine->_text->fadeInRemainingChars();
|
||||
}
|
||||
|
||||
_engine->_redraw->flipBoxes();
|
||||
|
||||
_engine->readKeys();
|
||||
if (_engine->_input->toggleAbortAction()) {
|
||||
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_engine->_input->toggleActionIfActive(TwinEActionType::UINextPage)) {
|
||||
if (textState == ProgressiveTextState::End) {
|
||||
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
|
||||
break;
|
||||
}
|
||||
if (textState == ProgressiveTextState::NextPage) {
|
||||
textState = ProgressiveTextState::ContinueRunning;
|
||||
}
|
||||
}
|
||||
|
||||
_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry);
|
||||
|
||||
// advance the timer to play animations
|
||||
_engine->timerRef++;
|
||||
debugC(3, kDebugLevels::kDebugTimers, "FoundObj time: %i", _engine->timerRef);
|
||||
}
|
||||
|
||||
while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
|
||||
FrameMarker frame(_engine);
|
||||
_engine->readKeys();
|
||||
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init3DGame();
|
||||
_engine->_text->initSceneTextBank();
|
||||
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
|
||||
}
|
||||
|
||||
void GameState::gameAskChoice(TextId choiceIdx) {
|
||||
_engine->saveFrontBuffer();
|
||||
|
||||
_gameChoicesSettings.reset();
|
||||
_gameChoicesSettings.setTextBankId((TextBankId)((int)_engine->_scene->_sceneTextBank + (int)TextBankId::Citadel_Island));
|
||||
|
||||
// filled via script
|
||||
for (int32 i = 0; i < _gameNbChoices; i++) {
|
||||
_gameChoicesSettings.addButton(_gameListChoice[i], 0);
|
||||
}
|
||||
|
||||
_engine->_text->drawAskQuestion(choiceIdx);
|
||||
|
||||
_engine->_menu->doGameMenu(&_gameChoicesSettings);
|
||||
const int16 activeButton = _gameChoicesSettings.getActiveButton();
|
||||
_gameChoice = _gameListChoice[activeButton];
|
||||
|
||||
// get right VOX entry index
|
||||
if (_engine->_text->initVoxToPlayTextId(_gameChoice)) {
|
||||
while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
|
||||
FrameMarker frame(_engine);
|
||||
if (_engine->shouldQuit()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
|
||||
|
||||
_engine->_text->_hasHiddenVox = false;
|
||||
_engine->_text->_voxHiddenIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::processGameoverAnimation() {
|
||||
const int32 tmpLbaTime = _engine->timerRef;
|
||||
|
||||
_engine->testRestoreModeSVGA(false);
|
||||
// workaround to fix hero redraw after drowning
|
||||
_engine->_scene->_sceneHero->_flags.bIsInvisible = 1;
|
||||
_engine->_redraw->drawScene(true);
|
||||
_engine->_scene->_sceneHero->_flags.bIsInvisible = 0;
|
||||
|
||||
// TODO: inSceneryView
|
||||
_engine->setPalette(_engine->_screens->_ptrPal);
|
||||
_engine->saveFrontBuffer();
|
||||
BodyData gameOverPtr;
|
||||
if (!gameOverPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_GAMEOVERMDL, _engine->isLBA1())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_engine->_sound->stopSamples();
|
||||
_engine->_music->stopMusicMidi(); // stop fade music
|
||||
_engine->_renderer->setProjection(_engine->width() / 2, _engine->height() / 2, 128, 200, 200);
|
||||
int32 startLbaTime = _engine->timerRef;
|
||||
const Common::Rect &rect = _engine->centerOnScreen(_engine->width() / 2, _engine->height() / 2);
|
||||
_engine->_interface->setClip(rect);
|
||||
|
||||
int32 zoom = 50000;
|
||||
Common::Rect dummy;
|
||||
while (!_engine->_input->toggleAbortAction() && (_engine->timerRef - startLbaTime) <= _engine->toSeconds(10)) {
|
||||
FrameMarker frame(_engine, 66);
|
||||
_engine->readKeys();
|
||||
if (_engine->shouldQuit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
zoom = boundRuleThree(40000, 3200, _engine->toSeconds(10), _engine->timerRef - startLbaTime);
|
||||
const int32 angle = ruleThree32(1, LBAAngles::ANGLE_360, _engine->toSeconds(2), (_engine->timerRef - startLbaTime) % _engine->toSeconds(2));
|
||||
|
||||
_engine->blitWorkToFront(rect);
|
||||
_engine->_renderer->setFollowCamera(0, 0, 0, 0, -angle, 0, zoom);
|
||||
_engine->_renderer->affObjetIso(0, 0, 0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, gameOverPtr, dummy);
|
||||
|
||||
_engine->timerRef++;
|
||||
debugC(3, kDebugLevels::kDebugTimers, "GameOver time: %i", _engine->timerRef);
|
||||
}
|
||||
|
||||
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(2000) - (2000 / 2);
|
||||
_engine->_sound->mixSample(Samples::Explode, pitchBend, 1, 128, 128);
|
||||
_engine->blitWorkToFront(rect);
|
||||
_engine->_renderer->setFollowCamera(0, 0, 0, 0, 0, 0, zoom);
|
||||
_engine->_renderer->affObjetIso(0, 0, 0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, gameOverPtr, dummy);
|
||||
|
||||
_engine->delaySkip(2000);
|
||||
|
||||
_engine->_interface->unsetClip();
|
||||
_engine->restoreFrontBuffer();
|
||||
init3DGame();
|
||||
|
||||
_engine->timerRef = tmpLbaTime;
|
||||
}
|
||||
|
||||
void GameState::giveUp() {
|
||||
_engine->_sound->stopSamples();
|
||||
// TODO: is an autosave desired on giving up? I don't think so. What did the original game do here?
|
||||
//_engine->autoSave();
|
||||
initGameStateVars();
|
||||
_engine->_scene->stopRunningGame();
|
||||
}
|
||||
|
||||
int16 GameState::setGas(int16 value) {
|
||||
_inventoryNumGas = CLIP<int16>(value, 0, 100);
|
||||
return _inventoryNumGas;
|
||||
}
|
||||
|
||||
void GameState::addGas(int16 value) {
|
||||
setGas(_inventoryNumGas + value);
|
||||
}
|
||||
|
||||
// late game items from lba1 classic new game +
|
||||
void GameState::handleLateGameItems() {
|
||||
if (!_endGameItems) {
|
||||
return;
|
||||
}
|
||||
debug("Give end game items");
|
||||
_endGameItems = false;
|
||||
_magicLevelIdx = 4;
|
||||
setMaxMagicPoints();
|
||||
giveItem(InventoryItems::kiUseSabre);
|
||||
giveItem(InventoryItems::kiProtoPack);
|
||||
giveItem(InventoryItems::kiHolomap);
|
||||
giveItem(InventoryItems::kiTunic);
|
||||
giveItem(InventoryItems::kiMagicBall);
|
||||
giveItem(InventoryItems::kSendellsMedallion);
|
||||
giveItem(InventoryItems::kiPenguin);
|
||||
giveItem(InventoryItems::kGasItem);
|
||||
giveItem(InventoryItems::kiCloverLeaf);
|
||||
addGas(10);
|
||||
}
|
||||
|
||||
int16 GameState::setKashes(int16 value) {
|
||||
_goldPieces = CLIP<int16>(value, 0, 999);
|
||||
if (_engine->_gameState->_goldPieces >= 500) {
|
||||
_engine->unlockAchievement("LBA_ACH_011");
|
||||
}
|
||||
return _goldPieces;
|
||||
}
|
||||
|
||||
int16 GameState::setZlitos(int16 value) {
|
||||
_zlitosPieces = CLIP<int16>(value, 0, 999);
|
||||
return _zlitosPieces;
|
||||
}
|
||||
|
||||
int16 GameState::setKeys(int16 value) {
|
||||
_nbLittleKeys = MAX<int16>(0, value);
|
||||
return _nbLittleKeys;
|
||||
}
|
||||
|
||||
void GameState::addKeys(int16 val) {
|
||||
setKeys(_nbLittleKeys + val);
|
||||
}
|
||||
|
||||
void GameState::addKashes(int16 val) {
|
||||
setKashes(_goldPieces + val);
|
||||
}
|
||||
|
||||
int16 GameState::setMagicPoints(int16 val) {
|
||||
_magicPoint = val;
|
||||
if (_magicPoint > _magicLevelIdx * 20) {
|
||||
_magicPoint = _magicLevelIdx * 20;
|
||||
} else if (_magicPoint < 0) {
|
||||
_magicPoint = 0;
|
||||
}
|
||||
return _magicPoint;
|
||||
}
|
||||
|
||||
int16 GameState::setMaxMagicPoints() {
|
||||
_magicPoint = _magicLevelIdx * 20;
|
||||
return _magicPoint;
|
||||
}
|
||||
|
||||
void GameState::addMagicPoints(int16 val) {
|
||||
setMagicPoints(_magicPoint + val);
|
||||
}
|
||||
|
||||
int16 GameState::setLeafs(int16 val) {
|
||||
_inventoryNumLeafs = val;
|
||||
if (_inventoryNumLeafs > _inventoryNumLeafsBox) {
|
||||
_inventoryNumLeafs = _inventoryNumLeafsBox;
|
||||
}
|
||||
return _inventoryNumLeafs;
|
||||
}
|
||||
|
||||
void GameState::addLeafs(int16 val) {
|
||||
setLeafs(_inventoryNumLeafs + val);
|
||||
}
|
||||
|
||||
int16 GameState::setLeafBoxes(int16 val) {
|
||||
_inventoryNumLeafsBox = val;
|
||||
if (_inventoryNumLeafsBox > 10) {
|
||||
_inventoryNumLeafsBox = 10;
|
||||
}
|
||||
if (_inventoryNumLeafsBox == 5) {
|
||||
_engine->unlockAchievement("LBA_ACH_003");
|
||||
}
|
||||
return _inventoryNumLeafsBox;
|
||||
}
|
||||
|
||||
void GameState::addLeafBoxes(int16 val) {
|
||||
setLeafBoxes(_inventoryNumLeafsBox + val);
|
||||
}
|
||||
|
||||
void GameState::clearGameFlags() {
|
||||
debug(2, "Clear all gameStateFlags");
|
||||
Common::fill(&_listFlagGame[0], &_listFlagGame[NUM_GAME_FLAGS], 0);
|
||||
}
|
||||
|
||||
int16 GameState::hasGameFlag(uint8 index) const {
|
||||
debug(6, "Query gameStateFlags[%u]=%u", index, _listFlagGame[index]);
|
||||
return _listFlagGame[index];
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
219
engines/twine/scene/gamestate.h
Normal file
219
engines/twine/scene/gamestate.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_GAMESTATE_H
|
||||
#define TWINE_SCENE_GAMESTATE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/menu/menu.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
/** Magicball strength*/
|
||||
enum MagicballStrengthType {
|
||||
kNoBallStrength = 2,
|
||||
kYellowBallStrength = 3,
|
||||
kGreenBallStrength = 4,
|
||||
kRedBallStrength = 6,
|
||||
kFireBallStrength = 8
|
||||
};
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
class GameState {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
void initGameStateVars();
|
||||
void initHeroVars();
|
||||
|
||||
MenuSettings _gameChoicesSettings;
|
||||
|
||||
/**
|
||||
* LBA engine game flags to save quest states
|
||||
*
|
||||
* 0-27: inventory related
|
||||
* 28-158: story related
|
||||
* 159..199: unused
|
||||
* 200-219: video related
|
||||
* 220..255: unused
|
||||
*
|
||||
* 35: If 0, a zommed sequence of opening the ventilation shaft will be played when Twinsen escapes
|
||||
* his house after arresting Zoe. Set to 1 after the sequence (also if Twinsen is killed during the arrest).
|
||||
* 47: Value of 1 indicates that Twinsen has opened the door to the Citadel Island Tavern's basement.
|
||||
* The door will be always open from now on.
|
||||
* 70: Set to 1 if inventory items are taken from Twinsen when he goes to jail (inventory is empty),
|
||||
* set to 0 after he gets back his stuff.
|
||||
* 92: Set to 1 if the green grobo in the Citadel Island Tavern has told Twinsen about taking Zoe to the
|
||||
* port and leaving for another island.
|
||||
* 107: Set to 1 after Twinsen kills yellow groboclone in the Citadel Island Tavern (after the Tavern has
|
||||
* been closed down). Makes the Tavern open again and groboclone not appear any more.
|
||||
*/
|
||||
int16 _listFlagGame[NUM_GAME_FLAGS]; // ListVarGame
|
||||
// only lba1 - lba2 uses 253 gameflag
|
||||
int16 _gameChapter = 0;
|
||||
|
||||
public:
|
||||
GameState(TwinEEngine *engine);
|
||||
|
||||
/**
|
||||
* LBA engine chapter
|
||||
* 0: Imprisoned
|
||||
* 1: Escape from the citadel
|
||||
* 2: Zoe got captured
|
||||
* 3: - looking for a young girl
|
||||
* 4: - looking for a "friend"
|
||||
* 5: The legend of Sendell
|
||||
* 6: The book of Bu
|
||||
* 7: Pirate LeBorne
|
||||
* 8: - "good day"
|
||||
* 9: - "good day"
|
||||
* 10: - ?? nothing
|
||||
* 11: - ?? nothing
|
||||
* 12: - ?? nothing
|
||||
* 13: - looking for plans
|
||||
* 14: - still looking for plans
|
||||
* 15: The final showdown - "good day"
|
||||
*/
|
||||
void setChapter(int16 chapter);
|
||||
int16 getChapter() const;
|
||||
|
||||
/** Magic ball type index */
|
||||
int16 _magicBall = 0;
|
||||
/** Magic ball num bounce */
|
||||
int16 _magicBallType = 0;
|
||||
/** Magic ball auxiliar bounce number */
|
||||
int16 _magicBallCount = 0; // magicBallParam
|
||||
/** Magic level index */
|
||||
int16 _magicLevelIdx = 0;
|
||||
|
||||
/** Store the number of inventory keys */
|
||||
int16 _nbLittleKeys = 0;
|
||||
/** Store the number of inventory kashes */
|
||||
int16 _goldPieces = 0;
|
||||
int16 _zlitosPieces = 0;
|
||||
/** Store the number of inventory clover leafs boxes */
|
||||
int16 _inventoryNumLeafsBox = 0;
|
||||
/** Store the number of inventory clover leafs */
|
||||
int16 _inventoryNumLeafs = 0;
|
||||
/** Store the number of inventory magic points */
|
||||
int16 _magicPoint = 0;
|
||||
/** Store the number of gas */
|
||||
int16 _inventoryNumGas = 0;
|
||||
|
||||
/** Its using FunFrock Sabre */
|
||||
bool _weapon = false;
|
||||
bool _endGameItems = false;
|
||||
|
||||
/**
|
||||
* Inventory used flags
|
||||
* 0 means never used, 1 means already used and automatic re-use
|
||||
*/
|
||||
uint8 _inventoryFlags[NUM_INVENTORY_ITEMS];
|
||||
|
||||
uint8 _holomapFlags[MAX_HOLO_POS_2];
|
||||
|
||||
char _sceneName[30] {};
|
||||
|
||||
TextId _gameListChoice[10]; // inGameMenuData
|
||||
int32 _gameNbChoices = 0; // numOfOptionsInChoice
|
||||
TextId _gameChoice = TextId::kNone; // inGameMenuAnswer
|
||||
|
||||
void setDarts(int16 value) {
|
||||
setGameFlag(InventoryItems::kiDart, value);
|
||||
}
|
||||
|
||||
void addDart() {
|
||||
int16 old = _listFlagGame[InventoryItems::kiDart];
|
||||
++old;
|
||||
setGameFlag(InventoryItems::kiDart, old);
|
||||
}
|
||||
|
||||
inline bool inventoryDisabled() const {
|
||||
return hasGameFlag(GAMEFLAG_INVENTORY_DISABLED) != 0;
|
||||
}
|
||||
|
||||
inline bool hasOpenedFunfrocksSafe() const {
|
||||
return hasGameFlag(30) != 0;
|
||||
}
|
||||
|
||||
// Arrived on the hamalayi with the rebels
|
||||
inline bool hasArrivedHamalayi() const {
|
||||
return hasGameFlag(90) != 0;
|
||||
}
|
||||
|
||||
inline bool hasItem(InventoryItems item) const {
|
||||
return hasGameFlag(item) != 0;
|
||||
}
|
||||
|
||||
inline void giveItem(InventoryItems item) {
|
||||
setGameFlag(item, 1);
|
||||
}
|
||||
|
||||
inline void removeItem(InventoryItems item) {
|
||||
setGameFlag(item, 0);
|
||||
}
|
||||
|
||||
void clearGameFlags();
|
||||
|
||||
int16 hasGameFlag(uint8 index) const;
|
||||
|
||||
void setGameFlag(uint8 index, int16 value);
|
||||
|
||||
int16 setKeys(int16 value);
|
||||
int16 setGas(int16 value);
|
||||
int16 setLeafs(int16 value);
|
||||
int16 setKashes(int16 value);
|
||||
int16 setZlitos(int16 value);
|
||||
int16 setMagicPoints(int16 val);
|
||||
int16 setMaxMagicPoints();
|
||||
int16 setLeafBoxes(int16 val);
|
||||
|
||||
void handleLateGameItems();
|
||||
|
||||
void addGas(int16 value);
|
||||
void addKeys(int16 val);
|
||||
void addKashes(int16 val);
|
||||
void addMagicPoints(int16 val);
|
||||
void addLeafs(int16 val);
|
||||
void addLeafBoxes(int16 val);
|
||||
|
||||
/** Initialize all engine variables */
|
||||
void initEngineVars();
|
||||
|
||||
/** Initialize engine 3D projections */
|
||||
void init3DGame();
|
||||
|
||||
void doFoundObj(InventoryItems item);
|
||||
|
||||
void giveUp();
|
||||
bool loadGame(Common::SeekableReadStream *file);
|
||||
bool saveGame(Common::WriteStream *file);
|
||||
|
||||
void gameAskChoice(TextId choiceIdx);
|
||||
|
||||
void processGameoverAnimation();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
115
engines/twine/scene/graph.h
Normal file
115
engines/twine/scene/graph.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_GRAPH_H
|
||||
#define TWINE_SCENE_GRAPH_H
|
||||
|
||||
#include "graphics/screen.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
// WARNING: Rewrite this function to have better performance
|
||||
inline bool drawGraph(int32 posX, int32 posY, const uint8 *pGraph, bool isSprite, Graphics::Screen &frontVideoBuffer, const Common::Rect &clip) { // AffGraph
|
||||
if (!clip.isValidRect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32 left = posX + pGraph[2];
|
||||
if (left >= clip.right) {
|
||||
return false;
|
||||
}
|
||||
const int32 top = posY + pGraph[3];
|
||||
if (top >= clip.bottom) {
|
||||
return false;
|
||||
}
|
||||
const int32 right = pGraph[0] + left;
|
||||
if (right < clip.left) {
|
||||
return false;
|
||||
}
|
||||
const int32 bottom = pGraph[1] + top;
|
||||
if (bottom < clip.top) {
|
||||
return false;
|
||||
}
|
||||
const int32 maxY = MIN(bottom, (int32)clip.bottom);
|
||||
|
||||
pGraph += 4; // skip the header
|
||||
|
||||
int32 x = left;
|
||||
|
||||
// if (left >= textWindowLeft-2 && top >= textWindowTop-2 && right <= textWindowRight-2 && bottom <= textWindowBottom-2) // crop
|
||||
{
|
||||
for (int32 y = top; y < maxY; ++y) {
|
||||
const uint8 rleAmount = *pGraph++;
|
||||
for (int32 run = 0; run < rleAmount; ++run) {
|
||||
const uint8 rleMask = *pGraph++;
|
||||
const uint8 iterations = bits(rleMask, 0, 6) + 1;
|
||||
const uint8 opCode = bits(rleMask, 6, 2);
|
||||
if (opCode == 0) {
|
||||
x += iterations;
|
||||
continue;
|
||||
}
|
||||
if (y < clip.top || x >= clip.right || x + iterations < clip.left) {
|
||||
if (opCode == 1) {
|
||||
pGraph += iterations;
|
||||
} else {
|
||||
++pGraph;
|
||||
}
|
||||
x += iterations;
|
||||
continue;
|
||||
}
|
||||
if (opCode == 1) { // 0x40
|
||||
uint8 *out = (uint8 *)frontVideoBuffer.getBasePtr(x, y);
|
||||
for (uint8 i = 0; i < iterations; i++) {
|
||||
if (x >= clip.left && x < clip.right) {
|
||||
*out = *pGraph;
|
||||
}
|
||||
|
||||
++out;
|
||||
++x;
|
||||
++pGraph;
|
||||
}
|
||||
} else {
|
||||
const uint8 pixel = *pGraph++;
|
||||
uint8 *out = (uint8 *)frontVideoBuffer.getBasePtr(x, y);
|
||||
for (uint8 i = 0; i < iterations; i++) {
|
||||
if (x >= clip.left && x < clip.right) {
|
||||
*out = pixel;
|
||||
}
|
||||
|
||||
++out;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
}
|
||||
x = left;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect rect(left, top, right, bottom);
|
||||
frontVideoBuffer.addDirtyRect(rect);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif // TWINE_SCENE_GRAPH_H
|
||||
798
engines/twine/scene/grid.cpp
Normal file
798
engines/twine/scene/grid.cpp
Normal file
@@ -0,0 +1,798 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/parser/blocklibrary.h"
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/graph.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
#define CELLING_GRIDS_START_INDEX 120
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Grid::Grid(TwinEEngine *engine) : _engine(engine) {
|
||||
_blockBufferSize = SIZE_CUBE_X * SIZE_CUBE_Z * SIZE_CUBE_Y * sizeof(BlockEntry);
|
||||
_bufCube = (uint8 *)malloc(_blockBufferSize);
|
||||
}
|
||||
|
||||
Grid::~Grid() {
|
||||
free(_bufCube);
|
||||
for (int32 i = 0; i < ARRAYSIZE(_brickMaskTable); i++) {
|
||||
free(_brickMaskTable[i]);
|
||||
}
|
||||
for (int32 i = 0; i < ARRAYSIZE(_bufferBrick); i++) {
|
||||
free(_bufferBrick[i]);
|
||||
}
|
||||
free(_currentGrid);
|
||||
free(_nbBrickColon);
|
||||
free(_listBrickColon);
|
||||
}
|
||||
|
||||
void Grid::init(int32 w, int32 h) {
|
||||
const int32 numbrickentries = (1 + (w + 24) / 24);
|
||||
const size_t brickDataBufferSize = numbrickentries * MAX_BRICKS * sizeof(BrickEntry);
|
||||
_listBrickColon = (BrickEntry *)malloc(brickDataBufferSize);
|
||||
_brickInfoBufferSize = numbrickentries * sizeof(int16);
|
||||
_nbBrickColon = (int16 *)malloc(_brickInfoBufferSize);
|
||||
}
|
||||
|
||||
void Grid::copyMask(int32 index, int32 x, int32 y, const Graphics::ManagedSurface &buffer) {
|
||||
if (_engine->_debugState->_disableGridRendering) {
|
||||
return;
|
||||
}
|
||||
uint8 *ptr = _brickMaskTable[index];
|
||||
|
||||
int32 left = x + *(ptr + 2);
|
||||
int32 top = y + *(ptr + 3);
|
||||
int32 right = *ptr + left - 1;
|
||||
int32 bottom = *(ptr + 1) + top - 1;
|
||||
|
||||
if (left > _engine->_interface->_clip.right || right < _engine->_interface->_clip.left || bottom < _engine->_interface->_clip.top || top > _engine->_interface->_clip.bottom) {
|
||||
return;
|
||||
}
|
||||
|
||||
ptr += 4;
|
||||
|
||||
int32 absX = left;
|
||||
int32 absY = top;
|
||||
|
||||
int32 vSize = (bottom - top) + 1;
|
||||
|
||||
if (vSize <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32 offset = -((right - left) - _engine->width()) - 1;
|
||||
|
||||
right++;
|
||||
bottom++;
|
||||
|
||||
// if line on top aren't in the blitting area...
|
||||
if (absY < _engine->_interface->_clip.top) {
|
||||
int numOfLineToRemove = _engine->_interface->_clip.top - absY;
|
||||
|
||||
vSize -= numOfLineToRemove;
|
||||
if (vSize <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
absY += numOfLineToRemove;
|
||||
|
||||
do {
|
||||
int lineDataSize;
|
||||
|
||||
lineDataSize = *(ptr++);
|
||||
ptr += lineDataSize;
|
||||
} while (--numOfLineToRemove);
|
||||
}
|
||||
|
||||
// reduce the vSize to remove lines on bottom
|
||||
if (absY + vSize - 1 > _engine->_interface->_clip.bottom) {
|
||||
vSize = _engine->_interface->_clip.bottom - absY + 1;
|
||||
if (vSize <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *outPtr = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(left, absY);
|
||||
const uint8 *inPtr = (const uint8 *)buffer.getBasePtr(left, absY);
|
||||
|
||||
do {
|
||||
int32 height = *(ptr++);
|
||||
|
||||
do {
|
||||
int32 width = *(ptr++); // skip size
|
||||
outPtr += width;
|
||||
inPtr += width;
|
||||
|
||||
absX += width;
|
||||
|
||||
height--;
|
||||
if (!height) {
|
||||
break;
|
||||
}
|
||||
|
||||
width = *(ptr++); // copy size
|
||||
|
||||
for (int32 j = 0; j < width; j++) {
|
||||
if (absX >= _engine->_interface->_clip.left && absX <= _engine->_interface->_clip.right) {
|
||||
*outPtr = *inPtr;
|
||||
}
|
||||
|
||||
absX++;
|
||||
outPtr++;
|
||||
inPtr++;
|
||||
}
|
||||
} while (--height);
|
||||
|
||||
absX = left;
|
||||
|
||||
outPtr += offset;
|
||||
inPtr += offset;
|
||||
} while (--vSize);
|
||||
}
|
||||
|
||||
const BrickEntry *Grid::getBrickEntry(int32 j, int32 i) const {
|
||||
return &_listBrickColon[j * MAX_BRICKS + i];
|
||||
}
|
||||
|
||||
void Grid::drawOverBrick(int32 x, int32 y, int32 z) {
|
||||
const int32 startCol = ((_engine->_interface->_clip.left + 24) / 24) - 1;
|
||||
const int32 endCol = ((_engine->_interface->_clip.right + 24) / 24);
|
||||
|
||||
for (int32 col = startCol; col <= endCol; col++) {
|
||||
for (int32 i = 0; i < _nbBrickColon[col]; i++) {
|
||||
const BrickEntry *currBrickEntry = getBrickEntry(col, i);
|
||||
|
||||
if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
|
||||
if (currBrickEntry->x + currBrickEntry->z > z + x) {
|
||||
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::drawOverBrick3(int32 x, int32 y, int32 z) {
|
||||
const int32 startCol = ((_engine->_interface->_clip.left + 24) / 24) - 1;
|
||||
const int32 endCol = (_engine->_interface->_clip.right + 24) / 24;
|
||||
|
||||
for (int32 col = startCol; col <= endCol; col++) {
|
||||
for (int32 i = 0; i < _nbBrickColon[col]; i++) {
|
||||
const BrickEntry *currBrickEntry = getBrickEntry(col, i);
|
||||
|
||||
if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
|
||||
if (currBrickEntry->x == x && currBrickEntry->z == z) {
|
||||
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
|
||||
}
|
||||
|
||||
if (currBrickEntry->x > x || currBrickEntry->z > z) {
|
||||
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::processGridMask(const uint8 *buffer, uint8 *ptr) {
|
||||
const uint8 width = *buffer++;
|
||||
uint8 height = *buffer++;
|
||||
const uint8 offsetX = *buffer++;
|
||||
const uint8 offsetY = *buffer++;
|
||||
const int32 maxY = offsetY + height;
|
||||
|
||||
*ptr++ = width;
|
||||
*ptr++ = height;
|
||||
*ptr++ = offsetX;
|
||||
*ptr++ = offsetY;
|
||||
|
||||
uint8 *targetPtrPos = ptr;
|
||||
|
||||
for (int32 y = offsetY; y < maxY; ++y) {
|
||||
uint8 numOfBlock = 0;
|
||||
uint8 opaquePixels = 0;
|
||||
uint8 *numOfBlockTargetPtr = targetPtrPos;
|
||||
|
||||
targetPtrPos++;
|
||||
|
||||
const uint8 numRuns = *buffer++;
|
||||
|
||||
// the first time isn't skip. the skip size is 0 in that case
|
||||
if (bits(*buffer, 6, 2) != 0) {
|
||||
*targetPtrPos++ = 0;
|
||||
numOfBlock++;
|
||||
}
|
||||
|
||||
for (uint8 run = 0; run < numRuns; ++run) {
|
||||
const uint8 runSpec = *buffer++;
|
||||
const uint8 runLength = bits(runSpec, 0, 6) + 1;
|
||||
const uint8 type = bits(runSpec, 6, 2);
|
||||
if (type == 2) {
|
||||
opaquePixels += runLength;
|
||||
buffer++;
|
||||
} else if (type == 1) { // 0x40
|
||||
opaquePixels += runLength;
|
||||
buffer += runLength;
|
||||
} else { // skip (type 3)
|
||||
if (opaquePixels) {
|
||||
*targetPtrPos++ = opaquePixels; // write down the number of pixel passed so far
|
||||
numOfBlock++;
|
||||
opaquePixels = 0;
|
||||
}
|
||||
*targetPtrPos++ = runLength; // write skip
|
||||
numOfBlock++;
|
||||
}
|
||||
}
|
||||
|
||||
if (opaquePixels) {
|
||||
*targetPtrPos++ = opaquePixels;
|
||||
numOfBlock++;
|
||||
|
||||
opaquePixels = 0;
|
||||
}
|
||||
|
||||
*numOfBlockTargetPtr = numOfBlock;
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::createGridMask() {
|
||||
for (int32 b = 0; b < NUM_BRICKS; b++) {
|
||||
if (!_brickUsageTable[b]) {
|
||||
continue;
|
||||
}
|
||||
if (_brickMaskTable[b]) {
|
||||
free(_brickMaskTable[b]);
|
||||
}
|
||||
_brickMaskTable[b] = (uint8 *)malloc(_brickSizeTable[b]);
|
||||
processGridMask(_bufferBrick[b], _brickMaskTable[b]);
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::getSpriteSize(int32 offset, int32 *width, int32 *height, const uint8 *spritePtr) {
|
||||
spritePtr += READ_LE_INT32(spritePtr + offset * 4);
|
||||
|
||||
*width = *spritePtr;
|
||||
*height = *(spritePtr + 1);
|
||||
}
|
||||
|
||||
void Grid::loadGridBricks() {
|
||||
uint32 firstBrick = 60000;
|
||||
uint32 lastBrick = 0;
|
||||
uint32 currentBllEntryIdx = 1;
|
||||
|
||||
memset(_brickSizeTable, 0, sizeof(_brickSizeTable));
|
||||
memset(_brickUsageTable, 0, sizeof(_brickUsageTable));
|
||||
|
||||
// get block libraries usage bits
|
||||
const uint8 *ptrToBllBits = _currentGrid + (_currentGridSize - 32);
|
||||
|
||||
// for all bits under the 32bytes (256bits)
|
||||
for (uint32 i = 1; i < 256; i++) {
|
||||
const uint8 currentBitByte = *(ptrToBllBits + (i / 8));
|
||||
const uint8 currentBitMask = 1 << (7 - (i & 7));
|
||||
|
||||
if (currentBitByte & currentBitMask) {
|
||||
const BlockData *currentBllPtr = getBlockLibrary(currentBllEntryIdx);
|
||||
for (const BlockDataEntry &entry : currentBllPtr->entries) {
|
||||
uint16 brickIdx = entry.brickIdx;
|
||||
if (!brickIdx) {
|
||||
continue;
|
||||
}
|
||||
brickIdx--;
|
||||
if (brickIdx <= firstBrick) {
|
||||
firstBrick = brickIdx;
|
||||
}
|
||||
|
||||
if (brickIdx > lastBrick) {
|
||||
lastBrick = brickIdx;
|
||||
}
|
||||
|
||||
_brickUsageTable[brickIdx] = 1;
|
||||
}
|
||||
}
|
||||
++currentBllEntryIdx;
|
||||
}
|
||||
|
||||
for (uint32 i = firstBrick; i <= lastBrick; i++) {
|
||||
if (!_brickUsageTable[i]) {
|
||||
free(_bufferBrick[i]);
|
||||
_bufferBrick[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
_brickSizeTable[i] = HQR::getAllocEntry(&_bufferBrick[i], Resources::HQR_LBA_BRK_FILE, i);
|
||||
if (_brickSizeTable[i] == 0) {
|
||||
warning("Failed to load isometric brick index %i", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::decompColumn(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) { // DecompColonne
|
||||
Common::MemoryReadStream stream(gridEntry, gridEntrySize);
|
||||
Common::MemoryWriteStream outstream(dest, destSize);
|
||||
int32 brickCount = stream.readByte();
|
||||
|
||||
do {
|
||||
const int32 flag = stream.readByte();
|
||||
const int32 blockCount = bits(flag, 0, 6) + 1;
|
||||
const int32 type = bits(flag, 6, 2);
|
||||
if (type == 0) {
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.writeUint16LE(0);
|
||||
}
|
||||
} else if (type == 1) { // 0x40
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.writeUint16LE(stream.readUint16LE());
|
||||
}
|
||||
} else {
|
||||
const uint16 gridIdx = stream.readUint16LE();
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.writeUint16LE(gridIdx);
|
||||
}
|
||||
}
|
||||
assert(!outstream.err());
|
||||
assert(!stream.err());
|
||||
} while (--brickCount);
|
||||
}
|
||||
|
||||
void Grid::calcGraphMsk(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) {
|
||||
Common::MemoryReadStream stream(gridEntry, gridEntrySize);
|
||||
Common::SeekableMemoryWriteStream outstream(dest, destSize);
|
||||
int32 brickCount = stream.readByte();
|
||||
|
||||
do {
|
||||
const int32 flag = stream.readByte();
|
||||
const int32 blockCount = bits(flag, 0, 6) + 1;
|
||||
const int32 type = bits(flag, 6, 2);
|
||||
|
||||
if (type == 0) {
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.seek(outstream.pos() + 2);
|
||||
}
|
||||
} else if (type == 1) {
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.writeUint16LE(stream.readUint16LE());
|
||||
}
|
||||
} else {
|
||||
const uint16 gridIdx = stream.readUint16LE();
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
outstream.writeUint16LE(gridIdx);
|
||||
}
|
||||
}
|
||||
assert(!outstream.err());
|
||||
assert(!stream.err());
|
||||
} while (--brickCount);
|
||||
}
|
||||
|
||||
void Grid::copyMapToCube() {
|
||||
int32 blockOffset = 0;
|
||||
|
||||
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
|
||||
const int32 gridIdx = z * SIZE_CUBE_X;
|
||||
|
||||
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
|
||||
const int32 gridOffset = READ_LE_UINT16(_currentGrid + 2 * (x + gridIdx));
|
||||
decompColumn(_currentGrid + gridOffset, _currentGridSize - gridOffset, _bufCube + blockOffset, _blockBufferSize - blockOffset);
|
||||
blockOffset += 2 * SIZE_CUBE_Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::createCellingGridMap(const uint8 *gridPtr, int32 gridPtrSize) { // MixteMapToCube
|
||||
int32 currGridOffset = 0;
|
||||
int32 blockOffset = 0;
|
||||
|
||||
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
|
||||
const uint8 *tempGridPtr = gridPtr + currGridOffset;
|
||||
|
||||
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
|
||||
const int gridOffset = READ_LE_UINT16(tempGridPtr);
|
||||
tempGridPtr += 2;
|
||||
calcGraphMsk(gridPtr + gridOffset, gridPtrSize - gridOffset, _bufCube + blockOffset, _blockBufferSize - blockOffset);
|
||||
blockOffset += 2 * SIZE_CUBE_Y;
|
||||
}
|
||||
currGridOffset += SIZE_CUBE_X + SIZE_CUBE_Z;
|
||||
}
|
||||
}
|
||||
|
||||
bool Grid::initGrid(int32 index) {
|
||||
// load grids from file
|
||||
_currentGridSize = HQR::getAllocEntry(&_currentGrid, Resources::HQR_LBA_GRI_FILE, index);
|
||||
if (_currentGridSize == 0) {
|
||||
warning("Failed to load grid index: %i", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load layouts from file
|
||||
if (!_currentBlockLibrary.loadFromHQR(Resources::HQR_LBA_BLL_FILE, index, _engine->isLBA1())) {
|
||||
warning("Failed to load block library index: %i", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
loadGridBricks();
|
||||
|
||||
createGridMask();
|
||||
|
||||
copyMapToCube();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Grid::initCellingGrid(int32 index) { // IncrustGrm
|
||||
uint8 *gridPtr = nullptr;
|
||||
|
||||
// load grids from file
|
||||
const int realIndex = index + CELLING_GRIDS_START_INDEX;
|
||||
const int32 gridSize = HQR::getAllocEntry(&gridPtr, Resources::HQR_LBA_GRI_FILE, realIndex);
|
||||
if (gridSize == 0) {
|
||||
warning("Failed to load grid index %i", realIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
createCellingGridMap(gridPtr, gridSize);
|
||||
free(gridPtr);
|
||||
_engine->_redraw->_firstTime = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Grid::drawGraph(int32 index, int32 posX, int32 posY) { // AffGraph
|
||||
return drawGraph(posX, posY, _bufferBrick[index], false);
|
||||
}
|
||||
|
||||
bool Grid::drawGraph(int32 index, int32 posX, int32 posY, const uint8 *ptr) { // AffGraph
|
||||
ptr = ptr + READ_LE_INT32(ptr + index * 4); // GetGraph, GetMask
|
||||
return drawGraph(posX, posY, ptr, true);
|
||||
}
|
||||
|
||||
bool Grid::drawSprite(int32 posX, int32 posY, const SpriteData &ptr, int spriteIndex) {
|
||||
const int32 left = posX + ptr.offsetX(spriteIndex);
|
||||
if (left >= _engine->_interface->_clip.right) {
|
||||
return false;
|
||||
}
|
||||
const int32 right = ptr.surface(spriteIndex).w + left;
|
||||
if (right < _engine->_interface->_clip.left) {
|
||||
return false;
|
||||
}
|
||||
const int32 top = posY + ptr.offsetY(spriteIndex);
|
||||
if (top >= _engine->_interface->_clip.bottom) {
|
||||
return false;
|
||||
}
|
||||
const int32 bottom = ptr.surface(spriteIndex).h + top;
|
||||
if (bottom < _engine->_interface->_clip.top) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Common::Point pos(left, top);
|
||||
_engine->_frontVideoBuffer.transBlitFrom(ptr.surface(spriteIndex), pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Grid::drawGraph(int32 posX, int32 posY, const uint8 *pGraph, bool isSprite) {
|
||||
if (_engine->_debugState->_disableGridRendering) {
|
||||
return false;
|
||||
}
|
||||
const Common::Rect &clip = _engine->_interface->_clip;
|
||||
TwineScreen &frontVideoBuffer = _engine->_frontVideoBuffer;
|
||||
return TwinE::drawGraph(posX, posY, pGraph, isSprite, frontVideoBuffer, clip);
|
||||
}
|
||||
|
||||
const uint8 *Grid::getBlockBufferGround(const IVec3 &pos, int32 &ground) {
|
||||
const IVec3 &collision = updateCollisionCoordinates(pos.x, pos.y, pos.z);
|
||||
const uint8 *ptr = _bufCube + collision.y * 2 + collision.x * SIZE_CUBE_Y * 2 + collision.z * SIZE_CUBE_X * SIZE_CUBE_Y * 2;
|
||||
|
||||
int32 collisionY = collision.y;
|
||||
while (collisionY) {
|
||||
if (READ_LE_INT16(ptr)) { // found the ground - sizeof(BlockEntry);
|
||||
break;
|
||||
}
|
||||
collisionY--;
|
||||
ptr -= sizeof(int16);
|
||||
}
|
||||
|
||||
_engine->_collision->_collision.y = collisionY;
|
||||
ground = (int16)((collisionY + 1) * SIZE_BRICK_Y);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const BlockDataEntry *Grid::getAdrBlock(int32 blockIdx, int32 brickIdx) const {
|
||||
const BlockData *blockPtr = getBlockLibrary(blockIdx);
|
||||
return &blockPtr->entries[brickIdx];
|
||||
}
|
||||
|
||||
const BlockData *Grid::getBlockLibrary(int32 blockIdx) const {
|
||||
return _currentBlockLibrary.getLayout(blockIdx - 1);
|
||||
}
|
||||
|
||||
void Grid::map2Screen(int32 x, int32 y, int32 z, int32 &posx, int32 &posy) const {
|
||||
posx = (x - z) * 24 + _engine->width() / 2 - SIZE_CUBE_X / 2;
|
||||
posy = ((x + z) * 12) - (y * 15) + _engine->height() / 2 - SIZE_CUBE_Y;
|
||||
}
|
||||
|
||||
void Grid::drawBrickBlock(int32 blockIdx, int32 brickBlockIdx, int32 x, int32 y, int32 z) { // AffBrickBlock
|
||||
const BlockDataEntry *blockPtr = getAdrBlock(blockIdx, brickBlockIdx);
|
||||
const uint8 brickShape = blockPtr->brickShape;
|
||||
const uint8 brickSound = blockPtr->brickType;
|
||||
const uint16 numBrick = blockPtr->brickIdx;
|
||||
if (!numBrick) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32 brickPixelPosX = 0;
|
||||
int32 brickPixelPosY = 0;
|
||||
|
||||
map2Screen(x - _startCube.x, y - _startCube.y, z - _startCube.z, brickPixelPosX, brickPixelPosY);
|
||||
|
||||
if (brickPixelPosX < -24) {
|
||||
return;
|
||||
}
|
||||
if (brickPixelPosX >= _engine->width()) {
|
||||
return;
|
||||
}
|
||||
if (brickPixelPosY < -38) {
|
||||
return;
|
||||
}
|
||||
if (brickPixelPosY >= _engine->height()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the background brick
|
||||
drawGraph(numBrick - 1, brickPixelPosX, brickPixelPosY);
|
||||
|
||||
int32 col = (brickPixelPosX + 24) / 24;
|
||||
|
||||
if (_nbBrickColon[col] >= MAX_BRICKS) {
|
||||
warning("GRID: brick buffer exceeded");
|
||||
return;
|
||||
}
|
||||
|
||||
BrickEntry *pColonBrick = &_listBrickColon[col * MAX_BRICKS + _nbBrickColon[col]];
|
||||
|
||||
pColonBrick->x = x;
|
||||
pColonBrick->y = y;
|
||||
pColonBrick->z = z;
|
||||
pColonBrick->posX = brickPixelPosX;
|
||||
pColonBrick->posY = brickPixelPosY;
|
||||
pColonBrick->index = numBrick - 1;
|
||||
pColonBrick->shape = brickShape;
|
||||
pColonBrick->sound = brickSound;
|
||||
|
||||
_nbBrickColon[col]++;
|
||||
}
|
||||
|
||||
void Grid::redrawGrid() { // AffGrille
|
||||
_worldCube.x = _startCube.x * SIZE_BRICK_XZ;
|
||||
_worldCube.y = _startCube.y * SIZE_BRICK_Y;
|
||||
_worldCube.z = _startCube.z * SIZE_BRICK_XZ;
|
||||
|
||||
const IVec3 &projPos = _engine->_renderer->projectPoint(-_worldCube);
|
||||
_engine->_redraw->_projPosScreen.x = projPos.x;
|
||||
_engine->_redraw->_projPosScreen.y = projPos.y;
|
||||
|
||||
memset(_nbBrickColon, 0, _brickInfoBufferSize);
|
||||
|
||||
if (!_engine->_scene->_flagRenderGrid) {
|
||||
return;
|
||||
}
|
||||
|
||||
_engine->_screens->clearScreen();
|
||||
|
||||
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
|
||||
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
|
||||
for (int32 y = 0; y < SIZE_CUBE_Y; y++) {
|
||||
const BlockEntry entry = getBlockEntry(x, y, z);
|
||||
if (entry.blockIdx) {
|
||||
drawBrickBlock(entry.blockIdx, entry.brickBlockIdx, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockEntry Grid::getBlockEntry(int32 xmap, int32 ymap, int32 zmap) const {
|
||||
const uint8 *pCube = _bufCube;
|
||||
const int32 size = 2; // sizeof(BlockEntry);
|
||||
pCube += xmap * SIZE_CUBE_Y * size;
|
||||
pCube += ymap * size;
|
||||
pCube += zmap * (SIZE_CUBE_X * SIZE_CUBE_Y * size);
|
||||
|
||||
BlockEntry entry;
|
||||
entry.blockIdx = *pCube;
|
||||
entry.brickBlockIdx = *(pCube + 1);
|
||||
return entry;
|
||||
}
|
||||
|
||||
ShapeType Grid::worldColBrick(int32 x, int32 y, int32 z) {
|
||||
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
|
||||
|
||||
if (collision.y <= -1) {
|
||||
return ShapeType::kSolid;
|
||||
}
|
||||
|
||||
if (collision.x < 0 || collision.x >= SIZE_CUBE_X) {
|
||||
return ShapeType::kNone;
|
||||
}
|
||||
|
||||
if (collision.y < 0 || collision.y >= SIZE_CUBE_Y) {
|
||||
return ShapeType::kNone;
|
||||
}
|
||||
|
||||
if (collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
|
||||
return ShapeType::kNone;
|
||||
}
|
||||
|
||||
const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
|
||||
if (entry.blockIdx) {
|
||||
const BlockDataEntry *blockPtr = getAdrBlock(entry.blockIdx, entry.brickBlockIdx);
|
||||
return (ShapeType)blockPtr->brickShape;
|
||||
}
|
||||
return (ShapeType)entry.brickBlockIdx; // eventually transparent color
|
||||
}
|
||||
|
||||
const IVec3 &Grid::updateCollisionCoordinates(int32 x, int32 y, int32 z) {
|
||||
_engine->_collision->_collision.x = (x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
_engine->_collision->_collision.y = y / SIZE_BRICK_Y;
|
||||
_engine->_collision->_collision.z = (z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
|
||||
return _engine->_collision->_collision;
|
||||
}
|
||||
|
||||
bool Grid::shouldCheckWaterCol(int32 actorIdx) const {
|
||||
if (actorIdx == OWN_ACTOR_SCENE_INDEX) {
|
||||
ActorStruct *ptrobj = _engine->_scene->getActor(actorIdx);
|
||||
if (_engine->_actor->_heroBehaviour != HeroBehaviourType::kProtoPack
|
||||
&& ptrobj->_flags.bComputeCollisionWithFloor
|
||||
&& !ptrobj->_flags.bIsInvisible
|
||||
&& !ptrobj->_workFlags.bIsFalling
|
||||
&& ptrobj->_carryBy == -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ShapeType Grid::worldColBrickFull(int32 x, int32 y, int32 z, int32 y2, int32 actorIdx) {
|
||||
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
|
||||
|
||||
if (collision.y <= -1) {
|
||||
return ShapeType::kSolid;
|
||||
}
|
||||
|
||||
if (collision.x < 0 || collision.x >= SIZE_CUBE_X || collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
|
||||
return ShapeType::kNone;
|
||||
}
|
||||
|
||||
bool checkWater = shouldCheckWaterCol(actorIdx);
|
||||
uint8 *pCube = _bufCube;
|
||||
pCube += collision.x * SIZE_CUBE_Y * 2;
|
||||
pCube += collision.y * 2;
|
||||
pCube += collision.z * (SIZE_CUBE_X * SIZE_CUBE_Y * 2);
|
||||
|
||||
uint8 block = *pCube;
|
||||
|
||||
ShapeType brickShape;
|
||||
const uint8 tmpBrickIdx = *(pCube + 1);
|
||||
if (block) {
|
||||
const BlockDataEntry *blockPtr = getAdrBlock(block, tmpBrickIdx);
|
||||
if (checkWater && blockPtr->brickType == WATER_BRICK) {
|
||||
brickShape = ShapeType::kSolid; // full collision
|
||||
} else {
|
||||
brickShape = (ShapeType)blockPtr->brickShape;
|
||||
}
|
||||
} else {
|
||||
brickShape = (ShapeType)tmpBrickIdx; // maybe transparency
|
||||
if (checkWater) {
|
||||
uint8 *pCode = pCube;
|
||||
for (y = collision.y - 1; y >= 0; y--) {
|
||||
pCode -= 2;
|
||||
uint8 code = *pCode;
|
||||
if (code) {
|
||||
const BlockDataEntry *blockPtr = getAdrBlock(block, 0);
|
||||
if (blockPtr->brickType == WATER_BRICK) {
|
||||
// Special check mount funfrock
|
||||
if (_engine->_scene->_numCube != LBA1SceneId::Polar_Island_on_the_rocky_peak) {
|
||||
// full collision
|
||||
return ShapeType::kSolid;
|
||||
}
|
||||
}
|
||||
break; // stop parsing at first encountered brick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 ymax = (y2 + (SIZE_BRICK_Y - 1)) / SIZE_BRICK_Y;
|
||||
// check full height
|
||||
for (y = collision.y; ymax > 0 && y < (SIZE_CUBE_Y - 1); --ymax, y++) {
|
||||
pCube += 2;
|
||||
if (READ_LE_INT16(pCube)) {
|
||||
return ShapeType::kSolid;
|
||||
}
|
||||
}
|
||||
|
||||
return brickShape;
|
||||
}
|
||||
|
||||
uint8 Grid::worldCodeBrick(int32 x, int32 y, int32 z) {
|
||||
uint8 code = 0xF0U;
|
||||
if (y > -1) {
|
||||
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
|
||||
|
||||
const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
|
||||
if (entry.blockIdx) {
|
||||
const BlockDataEntry *blockPtr = getAdrBlock(entry.blockIdx, entry.brickBlockIdx);
|
||||
code = blockPtr->brickType;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void Grid::centerOnActor(const ActorStruct *actor) {
|
||||
_startCube.x = (actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
|
||||
_startCube.y = (actor->_posObj.y + SIZE_BRICK_Y) / SIZE_BRICK_Y;
|
||||
_startCube.z = (actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
|
||||
_engine->_redraw->_firstTime = true;
|
||||
}
|
||||
|
||||
void Grid::centerScreenOnActor() {
|
||||
if (_engine->_cameraZone) {
|
||||
return;
|
||||
}
|
||||
if (_engine->_debugState->_useFreeCamera) {
|
||||
return;
|
||||
}
|
||||
|
||||
ActorStruct *actor = _engine->_scene->getActor(_engine->_scene->_numObjFollow);
|
||||
const IVec3 projPos = _engine->_renderer->projectPoint(actor->_posObj.x - (_startCube.x * SIZE_BRICK_XZ),
|
||||
actor->_posObj.y - (_startCube.y * SIZE_BRICK_Y),
|
||||
actor->_posObj.z - (_startCube.z * SIZE_BRICK_XZ));
|
||||
// TODO: these border values should get scaled for higher resolutions
|
||||
if (projPos.x < 80 || projPos.x >= _engine->width() - 60 || projPos.y < 80 || projPos.y >= _engine->height() - 50) {
|
||||
_startCube.x = ((actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ) + (((actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ) - _startCube.x) / 2;
|
||||
_startCube.y = actor->_posObj.y / SIZE_BRICK_Y;
|
||||
_startCube.z = ((actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ) + (((actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ) - _startCube.z) / 2;
|
||||
|
||||
if (_startCube.x >= SIZE_CUBE_X) {
|
||||
_startCube.x = SIZE_CUBE_X - 1;
|
||||
}
|
||||
|
||||
if (_startCube.z >= SIZE_CUBE_Z) {
|
||||
_startCube.z = SIZE_CUBE_Z - 1;
|
||||
}
|
||||
|
||||
_engine->_redraw->_firstTime = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
308
engines/twine/scene/grid.h
Normal file
308
engines/twine/scene/grid.h
Normal file
@@ -0,0 +1,308 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_GRID_H
|
||||
#define TWINE_SCENE_GRID_H
|
||||
|
||||
#define WATER_BRICK (0xF1) // CJ_WATER
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/parser/blocklibrary.h"
|
||||
#include "twine/parser/sprite.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace Graphics {
|
||||
class ManagedSurface;
|
||||
}
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class ActorStruct;
|
||||
|
||||
/** Block fragment entry */
|
||||
struct BlockEntry {
|
||||
/** Block library index */
|
||||
uint8 blockIdx = 0;
|
||||
/** Brick index inside the block library */
|
||||
uint8 brickBlockIdx = 0;
|
||||
};
|
||||
/** Brick entry data */
|
||||
struct BrickEntry { // T_COLONB
|
||||
/** Brick X position in screen */
|
||||
int16 x = 0; //z
|
||||
/** Brick Y position in screen */
|
||||
int16 y = 0;
|
||||
/** Brick Z position in screen */
|
||||
int16 z = 0; // x
|
||||
/** Brick pixel X position */
|
||||
int16 posX = 0;
|
||||
/** Brick pixel Y position */
|
||||
int16 posY = 0;
|
||||
/** Brick index */
|
||||
int16 index = 0;
|
||||
/** Brick shape type */
|
||||
uint8 shape = 0;
|
||||
/** Brick sound type */
|
||||
uint8 sound = 0;
|
||||
};
|
||||
|
||||
/** Total number of bricks allowed in the game */
|
||||
#define NUM_BRICKS 9000
|
||||
|
||||
/** Grid X size */
|
||||
#define SIZE_CUBE_X 64
|
||||
/** Grid Y size */
|
||||
#define SIZE_CUBE_Y 25
|
||||
/** Grid Z size */
|
||||
#define SIZE_CUBE_Z SIZE_CUBE_X
|
||||
|
||||
#define ISO_SCALE 512
|
||||
#define SIZE_BRICK_XZ 512
|
||||
#define SIZE_BRICK_Y 256
|
||||
#define DEMI_BRICK_XZ 256
|
||||
#define DEMI_BRICK_Y 128
|
||||
// short max 32767 0x7FFF
|
||||
// 32256 0x7E00
|
||||
// 32000 0x7D00
|
||||
#define SCENE_SIZE_MAX (SIZE_BRICK_XZ * (SIZE_CUBE_X - 1))
|
||||
// short min -32768
|
||||
#define SCENE_SIZE_MIN (-SIZE_BRICK_XZ * SIZE_CUBE_X)
|
||||
#define SCENE_SIZE_HALF (SIZE_BRICK_XZ * SIZE_CUBE_X / 2)
|
||||
#define SCENE_SIZE_HALFF (SIZE_BRICK_XZ * SIZE_CUBE_X / 2.0f)
|
||||
|
||||
#define MAX_BRICKS 150
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
class Grid {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
/**
|
||||
* Draw a specific brick in the grid column according with the block index
|
||||
* @param blockIdx block library index
|
||||
* @param brickBlockIdx brick index inside the block
|
||||
* @param x column x position
|
||||
* @param y column y position
|
||||
* @param z column z position
|
||||
*/
|
||||
void drawBrickBlock(int32 blockIdx, int32 brickBlockIdx, int32 x, int32 y, int32 z);
|
||||
/**
|
||||
* Get brick position in the screen
|
||||
* @param x column x position in the current camera
|
||||
* @param y column y position in the current camera
|
||||
* @param z column z position in the current camera
|
||||
*/
|
||||
void map2Screen(int32 x, int32 y, int32 z, int32 &_brickPixelPosX, int32 &_brickPixelPosY) const;
|
||||
/**
|
||||
* Create celling grid map from celling grid to block library buffer
|
||||
* @param gridPtr celling grid buffer pointer
|
||||
*/
|
||||
void createCellingGridMap(const uint8 *gridPtr, int32 gridPtrSize);
|
||||
/**
|
||||
* Create grid Y column in block buffer
|
||||
* @param gridEntry current grid index
|
||||
* @param dest destination block buffer
|
||||
*/
|
||||
void calcGraphMsk(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize);
|
||||
/**
|
||||
* Create grid Y column in block buffer
|
||||
* @param gridEntry current grid index
|
||||
* @param dest destination block buffer
|
||||
*/
|
||||
void decompColumn(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize);
|
||||
/**
|
||||
* Load grid bricks according with block librarie usage
|
||||
*/
|
||||
void loadGridBricks();
|
||||
/** Create grid masks to allow display actors over the bricks */
|
||||
void createGridMask();
|
||||
/**
|
||||
* Process brick masks to allow actors to display over the bricks
|
||||
* @param buffer brick pointer buffer
|
||||
* @param ptr brick mask pointer buffer
|
||||
*/
|
||||
void processGridMask(const uint8 *buffer, uint8 *ptr);
|
||||
/**
|
||||
* Copy grid mask to allow actors to display over the bricks
|
||||
* @param index current brick index
|
||||
* @param x grid X coordinate
|
||||
* @param y grid Y coordinate
|
||||
* @param buffer work video buffer
|
||||
*/
|
||||
void copyMask(int32 index, int32 x, int32 y, const Graphics::ManagedSurface &buffer);
|
||||
|
||||
/** Table with all loaded bricks */
|
||||
uint8 *_bufferBrick[NUM_BRICKS]{nullptr};
|
||||
/** Table with all loaded bricks masks */
|
||||
uint8 *_brickMaskTable[NUM_BRICKS]{nullptr};
|
||||
/** Table with all loaded bricks sizes */
|
||||
uint32 _brickSizeTable[NUM_BRICKS]{0};
|
||||
/** Table with all loaded bricks usage */
|
||||
uint8 _brickUsageTable[NUM_BRICKS]{0};
|
||||
|
||||
/** Current grid pointer */
|
||||
int32 _currentGridSize = 0;
|
||||
uint8 *_currentGrid = nullptr;
|
||||
/** Current block library */
|
||||
BlockLibraryData _currentBlockLibrary;
|
||||
|
||||
/** Brick data buffer */
|
||||
BrickEntry *_listBrickColon = nullptr;
|
||||
/** Brick info buffer */
|
||||
int16 *_nbBrickColon = nullptr;
|
||||
int32 _brickInfoBufferSize = 0;
|
||||
|
||||
/** Celling grid brick block buffer */
|
||||
int32 _blockBufferSize = 0;
|
||||
uint8 *_bufCube = nullptr;
|
||||
|
||||
const BrickEntry* getBrickEntry(int32 j, int32 i) const;
|
||||
|
||||
const IVec3 &updateCollisionCoordinates(int32 x, int32 y, int32 z);
|
||||
|
||||
BlockEntry getBlockEntry(int32 xmap, int32 ymap, int32 zmap) const;
|
||||
|
||||
bool shouldCheckWaterCol(int32 actorIdx) const;
|
||||
public:
|
||||
Grid(TwinEEngine *engine);
|
||||
~Grid();
|
||||
|
||||
void init(int32 w, int32 h);
|
||||
|
||||
/**
|
||||
* search down until either ground is found or lower border of the cube is reached
|
||||
*/
|
||||
const uint8 *getBlockBufferGround(const IVec3 &pos, int32 &ground);
|
||||
|
||||
/** New grid camera x, y and z coordinates */
|
||||
IVec3 _startCube; // StartXCube, StartYCube, StartZCube
|
||||
|
||||
/** Current grid camera x, y and z coordinates */
|
||||
IVec3 _worldCube; // WorldXCube WorldYCube
|
||||
|
||||
int32 _addBetaCam = 0;
|
||||
|
||||
/** Flag to know if the engine is using celling grids */
|
||||
int16 _zoneGrm = 0;
|
||||
/** Current celling grid index */
|
||||
int16 _indexGrm = 0;
|
||||
|
||||
/**
|
||||
* Draw 3D actor over bricks
|
||||
* @param x actor.x coordinate
|
||||
* @param y actor.y coordinate
|
||||
* @param z actor.z coordinate
|
||||
*/
|
||||
void drawOverBrick(int32 x, int32 y, int32 z);
|
||||
|
||||
/**
|
||||
* Draw sprite actor over bricks
|
||||
* @param x actor.x coordinate
|
||||
* @param y actor.y coordinate
|
||||
* @param z actor.z coordinate
|
||||
*/
|
||||
void drawOverBrick3(int32 x, int32 y, int32 z);
|
||||
|
||||
/**
|
||||
* Get sprite width and height sizes
|
||||
* @param offset sprite pointer offset
|
||||
* @param width sprite width size
|
||||
* @param height sprite height size
|
||||
* @param spritePtr sprite buffer pointer
|
||||
*/
|
||||
void getSpriteSize(int32 offset, int32 *width, int32 *height, const uint8 *spritePtr);
|
||||
|
||||
/** recenter screen on followed actor automatically */
|
||||
void centerScreenOnActor();
|
||||
void centerOnActor(const ActorStruct* actor);
|
||||
|
||||
/**
|
||||
* Draw brick sprite in the screen
|
||||
* @param index brick index to draw
|
||||
* @param posX brick X position to draw
|
||||
* @param posY brick Y position to draw
|
||||
*/
|
||||
bool drawGraph(int32 index, int32 posX, int32 posY);
|
||||
|
||||
/**
|
||||
* Draw sprite in the screen
|
||||
* @param index sprite index to draw
|
||||
* @param posX sprite X position to draw
|
||||
* @param posY sprite Y position to draw
|
||||
* @param spritePtr sprite buffer pointer to draw
|
||||
*/
|
||||
bool drawGraph(int32 index, int32 posX, int32 posY, const uint8 *spritePtr);
|
||||
bool drawSprite(int32 posX, int32 posY, const SpriteData &ptr, int spriteIndex = 0);
|
||||
|
||||
/**
|
||||
* Draw sprite or bricks in the screen according with the type
|
||||
* @param posX sprite X position to draw
|
||||
* @param posY sprite Y position to draw
|
||||
* @param pGraph sprite buffer pointer to draw
|
||||
* @param isSprite allows to identify if the sprite to display is brick or a single sprite
|
||||
*/
|
||||
bool drawGraph(int32 posX, int32 posY, const uint8 *pGraph, bool isSprite);
|
||||
|
||||
/**
|
||||
* Get block library
|
||||
* @param blockIdx block library index
|
||||
* @return pointer to the current block index
|
||||
*/
|
||||
const BlockData *getBlockLibrary(int32 blockIdx) const;
|
||||
const BlockDataEntry* getAdrBlock(int32 blockIdx, int32 tmpBrickIdx) const;
|
||||
|
||||
/** Create grid map from current grid to block library buffer */
|
||||
void copyMapToCube();
|
||||
|
||||
/**
|
||||
* Initialize grid (background scenearios)
|
||||
* @param index grid index number
|
||||
*/
|
||||
bool initGrid(int32 index);
|
||||
|
||||
/**
|
||||
* Initialize celling grid (background scenearios)
|
||||
* @param index grid index number
|
||||
*/
|
||||
bool initCellingGrid(int32 index);
|
||||
|
||||
/** Redraw grid background */
|
||||
void redrawGrid();
|
||||
|
||||
ShapeType worldColBrick(int32 x, int32 y, int32 z);
|
||||
|
||||
ShapeType worldColBrickFull(int32 x, int32 y, int32 z, int32 y2, int32 actorIdx);
|
||||
|
||||
uint8 worldCodeBrick(int32 x, int32 y, int32 z);
|
||||
|
||||
inline ShapeType worldColBrick(const IVec3 &pos) {
|
||||
return worldColBrick(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
inline ShapeType worldColBrickFull(const IVec3 &pos, int32 y2, int32 actorIdx) {
|
||||
return worldColBrickFull(pos.x, pos.y, pos.z, y2, actorIdx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
521
engines/twine/scene/movements.cpp
Normal file
521
engines/twine/scene/movements.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/movements.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/shadeangletab.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/collision.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/text.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Movements::Movements(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
IVec3 Movements::getShadow(const IVec3 &pos) { // GetShadow
|
||||
IVec3 shadowCoord;
|
||||
const uint8 *ptr = _engine->_grid->getBlockBufferGround(pos, shadowCoord.y);
|
||||
shadowCoord.x = pos.x;
|
||||
shadowCoord.z = pos.z;
|
||||
|
||||
ShapeType shadowCollisionType;
|
||||
const int32 blockIdx = *ptr;
|
||||
if (blockIdx) {
|
||||
const int32 brickIdx = *(ptr + 1);
|
||||
const BlockDataEntry *blockPtr = _engine->_grid->getAdrBlock(blockIdx, brickIdx);
|
||||
shadowCollisionType = (ShapeType)blockPtr->brickShape;
|
||||
} else {
|
||||
shadowCollisionType = ShapeType::kNone;
|
||||
}
|
||||
_engine->_collision->reajustPos(shadowCoord, shadowCollisionType);
|
||||
return shadowCoord;
|
||||
}
|
||||
|
||||
void Movements::initRealAngle(int16 startAngle, int16 endAngle, int16 stepAngle, RealValue *movePtr) {
|
||||
movePtr->startValue = ClampAngle(startAngle);
|
||||
movePtr->endValue = ClampAngle(endAngle);
|
||||
movePtr->timeValue = ClampAngle(stepAngle);
|
||||
movePtr->memoTicks = _engine->timerRef;
|
||||
}
|
||||
|
||||
void Movements::clearRealAngle(ActorStruct *actorPtr) {
|
||||
initRealAngle(actorPtr->_beta, actorPtr->_beta, LBAAngles::ANGLE_0, &actorPtr->realAngle);
|
||||
}
|
||||
|
||||
void Movements::initRealValue(int16 startAngle, int16 endAngle, int16 stepAngle, RealValue *movePtr) {
|
||||
movePtr->startValue = startAngle;
|
||||
movePtr->endValue = endAngle;
|
||||
movePtr->timeValue = stepAngle;
|
||||
movePtr->memoTicks = _engine->timerRef;
|
||||
}
|
||||
|
||||
int32 Movements::getAngle(int32 x0, int32 z0, int32 x1, int32 z1) {
|
||||
#if 1
|
||||
int32 difZ = z1 - z0;
|
||||
const int32 newZ = difZ * difZ;
|
||||
|
||||
int32 difX = x1 - x0;
|
||||
const int32 newX = difX * difX;
|
||||
|
||||
bool flag;
|
||||
// Exchange X and Z
|
||||
if (newX < newZ) {
|
||||
const int32 tmpEx = difX;
|
||||
difX = difZ;
|
||||
difZ = tmpEx;
|
||||
|
||||
flag = true;
|
||||
} else {
|
||||
flag = false;
|
||||
}
|
||||
|
||||
_targetActorDistance = (int32)sqrt((float)(newX + newZ));
|
||||
|
||||
if (!_targetActorDistance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32 destAngle = (difZ * SCENE_SIZE_HALF) / _targetActorDistance;
|
||||
|
||||
int32 startAngle = LBAAngles::ANGLE_0;
|
||||
// stopAngle = LBAAngles::ANGLE_90;
|
||||
const int16 *shadeAngleTab3(&sinTab[LBAAngles::ANGLE_135]);
|
||||
while (shadeAngleTab3[startAngle] > destAngle) {
|
||||
startAngle++;
|
||||
}
|
||||
|
||||
if (shadeAngleTab3[startAngle] != destAngle) {
|
||||
if ((shadeAngleTab3[startAngle - 1] + shadeAngleTab3[startAngle]) / 2 <= destAngle) {
|
||||
startAngle--;
|
||||
}
|
||||
}
|
||||
|
||||
int32 finalAngle = LBAAngles::ANGLE_45 + startAngle;
|
||||
|
||||
if (difX <= 0) {
|
||||
finalAngle = -finalAngle;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
finalAngle = -finalAngle + LBAAngles::ANGLE_90;
|
||||
}
|
||||
|
||||
return ClampAngle(finalAngle);
|
||||
#else
|
||||
z1 -= z0;
|
||||
x1 -= x0;
|
||||
const int32 x2 = x1 * x1;
|
||||
const int32 z2 = z1 * z1;
|
||||
|
||||
_targetActorDistance = (int32)sqrt((float)(x2 + z2));
|
||||
if (!_targetActorDistance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (z2 > x2) {
|
||||
const int32 tmpEx = z1;
|
||||
x1 = z1 | 1; // flag = 1
|
||||
z1 = tmpEx;
|
||||
} else {
|
||||
x1 &= -2; // flag = 0
|
||||
}
|
||||
|
||||
const int32 tmp = (z1 * SCENE_SIZE_HALF) / _targetActorDistance;
|
||||
|
||||
int32 start = LBAAngles::ANGLE_135;
|
||||
int32 end = LBAAngles::ANGLE_135 + LBAAngles::ANGLE_90;
|
||||
int32 diff = 0;
|
||||
|
||||
while (start < (end - 1)) {
|
||||
int32 angle = (start + end) >> 1;
|
||||
diff = tmp - sinTab[angle];
|
||||
if (diff > 0) {
|
||||
end = angle;
|
||||
} else {
|
||||
start = angle;
|
||||
if (diff == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (diff) {
|
||||
if (tmp <= ((sinTab[start] + sinTab[end]) >> 1)) {
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
int32 angle = start - LBAAngles::ANGLE_90;
|
||||
if (x1 < 0) {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
if (x1 & 1) {
|
||||
angle = LBAAngles::ANGLE_90 - angle;
|
||||
}
|
||||
|
||||
return ClampAngle(angle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Movements::initRealAngleConst(int32 start, int32 end, int32 duration, RealValue *movePtr) const { // ManualRealAngle
|
||||
const int16 cstart = ClampAngle(start);
|
||||
const int16 cend = ClampAngle(end);
|
||||
|
||||
movePtr->startValue = cstart;
|
||||
movePtr->endValue = cend;
|
||||
|
||||
const int16 numOfStep = (cstart - cend) * 64;
|
||||
int32 t = ABS(numOfStep);
|
||||
t /= 64;
|
||||
|
||||
t *= duration;
|
||||
t /= 256;
|
||||
|
||||
movePtr->timeValue = (int16)t;
|
||||
movePtr->memoTicks = _engine->timerRef;
|
||||
}
|
||||
|
||||
void Movements::ChangedCursorKeys::update(TwinEEngine *engine) {
|
||||
if (engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
|
||||
leftChange = leftDown == 0;
|
||||
leftDown = 1;
|
||||
} else {
|
||||
leftChange = leftDown;
|
||||
leftDown = 0;
|
||||
}
|
||||
|
||||
if (engine->_input->isActionActive(TwinEActionType::TurnRight)) {
|
||||
rightChange = rightDown == 0;
|
||||
rightDown = 1;
|
||||
} else {
|
||||
rightChange = rightDown;
|
||||
rightDown = 0;
|
||||
}
|
||||
|
||||
if (engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
|
||||
backwardChange = backwardDown == 0;
|
||||
backwardDown = 1;
|
||||
} else {
|
||||
backwardChange = backwardDown;
|
||||
backwardDown = 0;
|
||||
}
|
||||
|
||||
if (engine->_input->isActionActive(TwinEActionType::MoveForward)) {
|
||||
forwardChange = forwardDown == 0;
|
||||
forwardDown = 1;
|
||||
} else {
|
||||
forwardChange = forwardDown;
|
||||
forwardDown = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Movements::update() {
|
||||
_previousChangedCursorKeys = _changedCursorKeys;
|
||||
_previousLoopActionKey = _heroActionKey;
|
||||
|
||||
_heroActionKey = _engine->_input->isHeroActionActive();
|
||||
_changedCursorKeys.update(_engine);
|
||||
}
|
||||
|
||||
void Movements::processBehaviourExecution(int actorIdx) {
|
||||
switch (_engine->_actor->_heroBehaviour) {
|
||||
case HeroBehaviourType::kNormal:
|
||||
_actionNormal = true;
|
||||
break;
|
||||
case HeroBehaviourType::kAthletic:
|
||||
_engine->_animations->initAnim(AnimationTypes::kJump, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
break;
|
||||
case HeroBehaviourType::kAggressive:
|
||||
if (_engine->_actor->_combatAuto) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
_lastJoyFlag = true;
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
// TODO: previousLoopActionKey must be handled properly
|
||||
if (!_previousLoopActionKey || actor->_genAnim == AnimationTypes::kStanding) {
|
||||
const int32 aggresiveMode = _engine->getRandomNumber(3);
|
||||
|
||||
switch (aggresiveMode) {
|
||||
case 0:
|
||||
_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
break;
|
||||
case 1:
|
||||
_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
break;
|
||||
case 2:
|
||||
_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HeroBehaviourType::kDiscrete:
|
||||
_engine->_animations->initAnim(AnimationTypes::kHide, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
break;
|
||||
case HeroBehaviourType::kProtoPack:
|
||||
case HeroBehaviourType::kMax:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Movements::processAttackExecution(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (!_engine->_gameState->_weapon) {
|
||||
// Use Magic Ball
|
||||
if (_engine->_gameState->hasItem(InventoryItems::kiMagicBall)) {
|
||||
if (_engine->_gameState->_magicBall == -1) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kThrowBall, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
}
|
||||
|
||||
_lastJoyFlag = true;
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
return true;
|
||||
}
|
||||
} else if (_engine->_gameState->hasItem(InventoryItems::kiUseSabre)) {
|
||||
if (actor->_genBody != BodyType::btSabre) {
|
||||
_engine->_actor->initBody(BodyType::btSabre, actorIdx);
|
||||
}
|
||||
|
||||
_engine->_animations->initAnim(AnimationTypes::kSabreAttack, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
|
||||
|
||||
_lastJoyFlag = true;
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Movements::processManualMovementExecution(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->isAttackAnimationActive()) {
|
||||
return;
|
||||
}
|
||||
if (actor->isJumpAnimationActive()) {
|
||||
return;
|
||||
}
|
||||
if (actor->isAttackWeaponAnimationActive()) {
|
||||
return;
|
||||
}
|
||||
if (!_changedCursorKeys || _actionNormal) {
|
||||
// if walking should get stopped
|
||||
if (!_engine->_input->isActionActive(TwinEActionType::MoveForward) && !_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
|
||||
if (_lastJoyFlag && (_heroActionKey != _previousLoopActionKey || _changedCursorKeys != _previousChangedCursorKeys)) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
_lastJoyFlag = false;
|
||||
|
||||
if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
|
||||
if (!_engine->_scene->_flagClimbing) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
_lastJoyFlag = true;
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kBackward, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
_lastJoyFlag = true;
|
||||
}
|
||||
|
||||
if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
|
||||
if (actor->_genAnim == AnimationTypes::kStanding) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kTurnLeft, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
} else {
|
||||
if (!actor->_workFlags.bIsRotationByAnim) {
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
}
|
||||
}
|
||||
_lastJoyFlag = true;
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
|
||||
if (actor->_genAnim == AnimationTypes::kStanding) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kTurnRight, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
} else {
|
||||
if (!actor->_workFlags.bIsRotationByAnim) {
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
}
|
||||
}
|
||||
_lastJoyFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Movements::processManualRotationExecution(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (!_engine->_actor->_combatAuto && actor->isAttackAnimationActive()) {
|
||||
// it is allowed to rotate in auto aggressive mode - but not in manual mode.
|
||||
return;
|
||||
}
|
||||
if (actor->isJumpAnimationActive()) {
|
||||
return;
|
||||
}
|
||||
int16 tempAngle;
|
||||
if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
|
||||
tempAngle = LBAAngles::ANGLE_90;
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
|
||||
tempAngle = -LBAAngles::ANGLE_90;
|
||||
} else {
|
||||
tempAngle = LBAAngles::ANGLE_0;
|
||||
}
|
||||
|
||||
initRealAngleConst(actor->_beta, actor->_beta + tempAngle, actor->_srot, &actor->realAngle);
|
||||
}
|
||||
|
||||
void Movements::processManualAction(int actorIdx) {
|
||||
if (IS_HERO(actorIdx)) {
|
||||
_actionNormal = false;
|
||||
if (_engine->_input->isHeroActionActive()) {
|
||||
processBehaviourExecution(actorIdx);
|
||||
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::SpecialAction)) {
|
||||
_actionNormal = true;
|
||||
}
|
||||
// MyFire & F_ALT
|
||||
if (_engine->_input->isActionActive(TwinEActionType::ThrowMagicBall) && !_engine->_gameState->inventoryDisabled()) {
|
||||
processAttackExecution(actorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
processManualMovementExecution(actorIdx);
|
||||
processManualRotationExecution(actorIdx);
|
||||
}
|
||||
|
||||
void Movements::processFollowAction(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
const ActorStruct *followedActor = _engine->_scene->getActor(actor->_followedActor);
|
||||
int32 newAngle = getAngle(actor->posObj(), followedActor->posObj());
|
||||
if (actor->_flags.bSprite3D) {
|
||||
actor->_beta = newAngle;
|
||||
} else {
|
||||
initRealAngleConst(actor->_beta, newAngle, actor->_srot, &actor->realAngle);
|
||||
}
|
||||
}
|
||||
|
||||
void Movements::processRandomAction(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_workFlags.bIsRotationByAnim) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor->brickCausesDamage()) {
|
||||
const int32 angle = ClampAngle(actor->_beta + (_engine->getRandomNumber() & (LBAAngles::ANGLE_180 - 1)) - LBAAngles::ANGLE_90 + LBAAngles::ANGLE_180);
|
||||
initRealAngleConst(actor->_beta, angle, actor->_srot, &actor->realAngle);
|
||||
actor->_delayInMillis = _engine->timerRef + _engine->getRandomNumber(_engine->toSeconds(6)) + _engine->toSeconds(6);
|
||||
_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
}
|
||||
|
||||
if (!actor->realAngle.timeValue) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx);
|
||||
if (_engine->timerRef > actor->_delayInMillis) {
|
||||
const int32 angle = ClampAngle(actor->_beta + (_engine->getRandomNumber() & (LBAAngles::ANGLE_180 - 1)) - LBAAngles::ANGLE_90);
|
||||
initRealAngleConst(actor->_beta, angle, actor->_srot, &actor->realAngle);
|
||||
actor->_delayInMillis = _engine->timerRef + _engine->getRandomNumber(_engine->toSeconds(6)) + _engine->toSeconds(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Movements::processTrackAction(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_offsetTrack == -1) {
|
||||
actor->_offsetTrack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Movements::processSameXZAction(int actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
const ActorStruct *followedActor = _engine->_scene->getActor(actor->_followedActor);
|
||||
actor->_posObj.x = followedActor->_posObj.x;
|
||||
actor->_posObj.z = followedActor->_posObj.z;
|
||||
}
|
||||
|
||||
void Movements::manualRealAngle(ActorStruct *actor) {
|
||||
int16 tempAngle = LBAAngles::ANGLE_0;
|
||||
if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
|
||||
tempAngle = LBAAngles::ANGLE_90;
|
||||
} else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
|
||||
tempAngle = -LBAAngles::ANGLE_90;
|
||||
}
|
||||
|
||||
initRealAngleConst(actor->_beta, actor->_beta + tempAngle, actor->_srot, &actor->realAngle);
|
||||
}
|
||||
|
||||
void Movements::doDir(int32 actorIdx) {
|
||||
ActorStruct *actor = _engine->_scene->getActor(actorIdx);
|
||||
if (actor->_body == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor->_workFlags.bIsFalling) {
|
||||
if (actor->_move == ControlMode::kManual) {
|
||||
manualRealAngle(actor);
|
||||
// TODO: _lastJoyFlag = _joyFlag;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!actor->_flags.bSprite3D && actor->_move != ControlMode::kManual) {
|
||||
actor->_beta = actor->realAngle.getRealAngle(_engine->timerRef);
|
||||
}
|
||||
|
||||
switch (actor->_move) {
|
||||
case ControlMode::kManual:
|
||||
processManualAction(actorIdx);
|
||||
break;
|
||||
case ControlMode::kFollow:
|
||||
processFollowAction(actorIdx);
|
||||
break;
|
||||
case ControlMode::kRandom:
|
||||
processRandomAction(actorIdx);
|
||||
break;
|
||||
case ControlMode::kTrack:
|
||||
processTrackAction(actorIdx);
|
||||
break;
|
||||
case ControlMode::kSameXZ:
|
||||
// TODO: see lSET_DIRMODE and lSET_DIRMODE_OBJ opcodes
|
||||
processSameXZAction(actorIdx);
|
||||
break;
|
||||
/**
|
||||
* The Actor's Track Script is stopped. Track Script execution may be started with Life Script of
|
||||
* the Actor or other Actors (with SET_TRACK(_OBJ) command). This mode does not mean the Actor
|
||||
* will literally not move, but rather that it's Track Script (also called Move Script) is
|
||||
* initially stopped. The Actor may move if it is assigned a moving animation.
|
||||
*/
|
||||
case ControlMode::kNoMove:
|
||||
case ControlMode::kFollow2: // unused
|
||||
case ControlMode::kTrackAttack: // unused
|
||||
break;
|
||||
default:
|
||||
warning("Unknown control mode %d", (int)actor->_move);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
201
engines/twine/scene/movements.h
Normal file
201
engines/twine/scene/movements.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_MOVEMENTS_H
|
||||
#define TWINE_SCENE_MOVEMENTS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class TwinEEngine;
|
||||
class ActorStruct;
|
||||
struct RealValue;
|
||||
|
||||
class Movements {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
struct ChangedCursorKeys {
|
||||
uint8 forwardChange = 0;
|
||||
uint8 backwardChange = 0;
|
||||
uint8 leftChange = 0;
|
||||
uint8 rightChange = 0;
|
||||
uint8 forwardDown = 0;
|
||||
uint8 backwardDown = 0;
|
||||
uint8 leftDown = 0;
|
||||
uint8 rightDown = 0;
|
||||
|
||||
void update(TwinEEngine *engine);
|
||||
|
||||
inline bool operator==(const ChangedCursorKeys &rhs) const {
|
||||
return forwardChange == rhs.forwardChange && backwardChange == rhs.backwardChange && leftChange == rhs.leftChange && rightChange == rhs.rightChange;
|
||||
}
|
||||
|
||||
inline operator bool() const {
|
||||
return forwardChange && backwardChange && leftChange && rightChange;
|
||||
}
|
||||
|
||||
inline bool operator!=(const ChangedCursorKeys &rhs) const {
|
||||
return forwardChange != rhs.forwardChange || backwardChange != rhs.backwardChange || leftChange != rhs.leftChange || rightChange != rhs.rightChange;
|
||||
}
|
||||
};
|
||||
|
||||
// enter, space, ...
|
||||
int16 _heroActionKey = 0;
|
||||
int32 _previousLoopActionKey = 0;
|
||||
// cursor keys
|
||||
ChangedCursorKeys _changedCursorKeys;
|
||||
ChangedCursorKeys _previousChangedCursorKeys;
|
||||
|
||||
/**
|
||||
* The Actor is controlled by the player. This works well only for the Hero Actor in general.
|
||||
* To use it for other Actors they would have to have necessary animations that would be also
|
||||
* correctly indexed. The primary purpose for this mode is to re-enable player's control over
|
||||
* the Hero after it has been disabled for some reasons.
|
||||
*/
|
||||
void processManualAction(int actorIdx);
|
||||
/**
|
||||
* The Actor tries to move towards the target Actor. This only means that it will always face
|
||||
* in its direction (as fast as the Rotation delay property allows). To make it really follow
|
||||
* anything it must be assigned a moving Animation first, and the Actor will not stop by itself
|
||||
* after reaching the target. To make a real following, the Actor's animation must be changed
|
||||
* for example to standing animation when the Actor is near the target, and changed back to a
|
||||
* moving animation when it's far from it. The Follow mode handles only the facing angle.
|
||||
*/
|
||||
void processFollowAction(int actorIdx);
|
||||
/**
|
||||
* Makes the Actor walk and turn by random angles and at random moments. In original game it is
|
||||
* only used for Nitro-Mecha-Penguins, but it can be used for any 3-D Actor that has standing
|
||||
* and walking animation (with virtual indexes 0 and 1 respectively). This mode requires the
|
||||
* Randomize interval (Info1) property to be less or equal to 117, otherwise the Actor will just
|
||||
* walk without turning. Exact meaning of the property is not known.
|
||||
*/
|
||||
void processRandomAction(int actorIdx);
|
||||
/**
|
||||
* The Actor's Track Script is run from the first command, and when it reaches END or STOP it
|
||||
* starts over again.
|
||||
*/
|
||||
void processTrackAction(int actorIdx);
|
||||
/**
|
||||
* This mode is used to make an Actor follow specified Actor's X and Z (horizontal) coordinates.
|
||||
* This is mainly used for Sprite Actors to be always above other Sprite Actors (like platforms).
|
||||
* Unlike the Follow mode, this mode sets the Actor's position. If the Actor is a Sprite Actor,
|
||||
* its speed is not taken into consideration in this mode.
|
||||
*/
|
||||
void processSameXZAction(int actorIdx);
|
||||
|
||||
void processBehaviourExecution(int actorIdx);
|
||||
bool processAttackExecution(int actorIdx);
|
||||
void processManualMovementExecution(int actorIdx);
|
||||
void processManualRotationExecution(int actorIdx);
|
||||
|
||||
/**
|
||||
* @return A value of @c true means that the actor should e.g. start reading a sign or checking
|
||||
* a locker for loot or secrets or talking to an npc - this can get triggered by the SpecialAction binding
|
||||
* in any behaviour mode
|
||||
*/
|
||||
bool _actionNormal = false;
|
||||
void manualRealAngle(ActorStruct *actor);
|
||||
|
||||
public:
|
||||
Movements(TwinEEngine *engine);
|
||||
|
||||
void setActionNormal(bool actionNormal);
|
||||
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Hero executes the current action of the trigger zone
|
||||
*/
|
||||
bool actionNormal() const;
|
||||
|
||||
bool _lastJoyFlag = false;
|
||||
|
||||
int32 _targetActorDistance = 0;
|
||||
|
||||
/**
|
||||
* Get shadow position
|
||||
* @param pos Shadow coordinates
|
||||
*/
|
||||
IVec3 getShadow(const IVec3 &pos);
|
||||
|
||||
/**
|
||||
* Set actor safe angle
|
||||
* @param startAngle start angle
|
||||
* @param endAngle end angle
|
||||
* @param stepAngle number of steps
|
||||
* @param movePtr time pointer to update
|
||||
*/
|
||||
void initRealAngle(int16 startAngle, int16 endAngle, int16 stepAngle, RealValue *movePtr);
|
||||
|
||||
/**
|
||||
* Clear actors safe angle
|
||||
* @param actorPtr actor pointer
|
||||
*/
|
||||
void clearRealAngle(ActorStruct *actorPtr);
|
||||
|
||||
/**
|
||||
* Set actor safe angle
|
||||
* @param startAngle start angle
|
||||
* @param endAngle end angle
|
||||
* @param stepAngle number of steps
|
||||
* @param movePtr time pointer to update
|
||||
*/
|
||||
void initRealValue(int16 startAngle, int16 endAngle, int16 stepAngle, RealValue *movePtr);
|
||||
|
||||
/**
|
||||
* Get actor angle
|
||||
* @param x1 Actor 1 X
|
||||
* @param z1 Actor 1 Z
|
||||
* @param x2 Actor 2 X
|
||||
* @param z2 Actor 2 Z
|
||||
*/
|
||||
int32 getAngle(int32 x1, int32 z1, int32 x2, int32 z2);
|
||||
|
||||
inline int32 getAngle(const IVec3& v1, const IVec3 &v2) {
|
||||
return getAngle(v1.x, v1.z, v2.x, v2.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move actor around the scene
|
||||
* @param start Current actor angle
|
||||
* @param end Angle to rotate
|
||||
* @param duration Rotate speed
|
||||
* @param movePtr Pointer to process movements
|
||||
*/
|
||||
void initRealAngleConst(int32 start, int32 end, int32 duration, RealValue *movePtr) const;
|
||||
|
||||
void doDir(int32 actorIdx);
|
||||
};
|
||||
|
||||
inline void Movements::setActionNormal(bool actionNormal) {
|
||||
_actionNormal = actionNormal;
|
||||
}
|
||||
|
||||
inline bool Movements::actionNormal() const {
|
||||
return _actionNormal;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
170
engines/twine/scene/rain.cpp
Normal file
170
engines/twine/scene/rain.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/rain.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
#define RAIN_VX 200
|
||||
#define RAIN_VY 2500
|
||||
#define RAIN_WEIGHT 30
|
||||
#define RAIN_STOP 0
|
||||
#define RAIN_DELTA_X 128
|
||||
#define RAIN_DELTA_Y 256
|
||||
#define RAIN_DELTA_Z 128
|
||||
|
||||
void Rain::InitOneRain(T_RAIN *pt) {
|
||||
IVec3 cameraPos = _engine->_renderer->getCameraPosition();
|
||||
|
||||
int32 rndy = _engine->getRandomNumber(cameraPos.y + 10000);
|
||||
|
||||
pt->YRain = cameraPos.y + rndy;
|
||||
|
||||
rndy = rndy / 2 + 15000;
|
||||
|
||||
pt->XRain = cameraPos.x - rndy + _engine->getRandomNumber(30000);
|
||||
pt->ZRain = cameraPos.z - rndy + _engine->getRandomNumber(30000);
|
||||
pt->Timer = 0;
|
||||
}
|
||||
|
||||
void Rain::InitRain() {
|
||||
for (int32 i = 0; i < MAX_RAIN; i++) {
|
||||
InitOneRain(&TabRain[i]);
|
||||
}
|
||||
|
||||
LastTimer = 0;
|
||||
}
|
||||
|
||||
void Rain::GereRain() {
|
||||
int32 temp = _engine->timerRef;
|
||||
DeltaRain = LastTimer ? (temp - LastTimer) * 10 : 0;
|
||||
LastTimer = temp;
|
||||
|
||||
for (int32 i = 0; i < MAX_RAIN; i++) {
|
||||
if (!TabRain[i].Timer) {
|
||||
TabRain[i].XRain += DeltaRain / 2;
|
||||
TabRain[i].ZRain += DeltaRain / 2;
|
||||
TabRain[i].YRain -= DeltaRain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rain::ClearImpactRain() {
|
||||
for (int32 i = 0; i < MAX_RAIN; i++) {
|
||||
if (TabRain[i].Timer) {
|
||||
InitOneRain(&TabRain[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rain::AffRain() {
|
||||
int32 lFactorX = _engine->_renderer->getLFactorX();
|
||||
int32 lFactorY = _engine->_renderer->getLFactorY();
|
||||
IVec3 cameraRot = _engine->_renderer->getCameraRotation();
|
||||
int32 cameraZr = cameraRot.z;
|
||||
|
||||
// ClipZFar approximation
|
||||
int32 clipZFar = 14000; // Default value from CREDITS.CPP
|
||||
int32 startZFog = 5000; // Default value from CREDITS.CPP
|
||||
|
||||
for (int32 i = 0; i < MAX_RAIN; i++) {
|
||||
if (TabRain[i].Timer) {
|
||||
int32 dt = LastTimer - TabRain[i].Timer;
|
||||
|
||||
int32 c = TabRain[i].XRain >> 16;
|
||||
int32 x = (int16)(TabRain[i].XRain & 0xFFFF);
|
||||
int32 y = TabRain[i].YRain;
|
||||
int32 z = TabRain[i].ZRain;
|
||||
|
||||
int32 xp, yp;
|
||||
yp = (RAIN_VY - RAIN_WEIGHT * dt) * dt / 256;
|
||||
if (yp < 0) {
|
||||
yp = 0;
|
||||
xp = RAIN_VX * RAIN_VY / RAIN_WEIGHT / 256;
|
||||
} else {
|
||||
xp = RAIN_VX * dt / 256;
|
||||
yp = (yp * lFactorY) / z;
|
||||
}
|
||||
|
||||
xp = (xp * lFactorX) / z;
|
||||
|
||||
int32 x0 = x - xp;
|
||||
int32 x1 = x + xp;
|
||||
|
||||
// int32 y0 = y - yp;
|
||||
// int32 y1 = y;
|
||||
|
||||
// z = ruleThree32(0, 65535, clipZFar, z);
|
||||
|
||||
// Draw splash
|
||||
_engine->_workVideoBuffer.drawLine(x, y, x0, y - yp, c);
|
||||
_engine->_workVideoBuffer.drawLine(x, y, x1, y - yp, c);
|
||||
|
||||
if (dt && !yp) {
|
||||
InitOneRain(&TabRain[i]);
|
||||
}
|
||||
} else {
|
||||
if (TabRain[i].YRain <= RAIN_STOP) {
|
||||
InitOneRain(&TabRain[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
IVec3 p1 = _engine->_renderer->longWorldRot(TabRain[i].XRain - RAIN_DELTA_X, TabRain[i].YRain + RAIN_DELTA_Y, TabRain[i].ZRain - RAIN_DELTA_Z);
|
||||
IVec3 proj1 = _engine->_renderer->projectPoint(p1);
|
||||
|
||||
int32 xp = proj1.x;
|
||||
int32 yp = proj1.y;
|
||||
int32 z0 = ruleThree32(0, 65535, clipZFar, cameraZr - p1.z);
|
||||
|
||||
IVec3 p2 = _engine->_renderer->longWorldRot(TabRain[i].XRain, TabRain[i].YRain, TabRain[i].ZRain);
|
||||
IVec3 proj2 = _engine->_renderer->projectPoint(p2);
|
||||
|
||||
int32 Z0 = cameraZr - p2.z;
|
||||
// int32 z1 = ruleThree32(0, 65535, clipZFar, Z0);
|
||||
|
||||
int32 c = boundRuleThree(16 * 3 + 10, 16 * 3 + 3, clipZFar - startZFog, Z0);
|
||||
|
||||
// Draw rain drop
|
||||
_engine->_workVideoBuffer.drawLine(xp, yp, proj2.x, proj2.y, c);
|
||||
|
||||
// Check collision with ground
|
||||
int32 groundHeight = 0;
|
||||
IVec3 pos(TabRain[i].XRain, TabRain[i].YRain, TabRain[i].ZRain);
|
||||
_engine->_grid->getBlockBufferGround(pos, groundHeight);
|
||||
|
||||
if (TabRain[i].YRain <= groundHeight) {
|
||||
// Splash
|
||||
TabRain[i].XRain = ((xp & 0xFFFF) | (c << 16));
|
||||
TabRain[i].YRain = yp;
|
||||
TabRain[i].ZRain = z0;
|
||||
TabRain[i].Timer = LastTimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rain::Rain(TwinEEngine *engine) : _engine(engine) {
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
59
engines/twine/scene/rain.h
Normal file
59
engines/twine/scene/rain.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_RAIN_H
|
||||
#define TWINE_SCENE_RAIN_H
|
||||
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
#define MAX_RAIN 200
|
||||
|
||||
class Rain {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
int32 LastTimer = 0;
|
||||
int32 DeltaRain = 0;
|
||||
|
||||
public:
|
||||
struct T_RAIN {
|
||||
int32 XRain = 0;
|
||||
int32 YRain = 0;
|
||||
int32 ZRain = 0;
|
||||
int32 Timer = 0;
|
||||
};
|
||||
|
||||
T_RAIN TabRain[MAX_RAIN];
|
||||
|
||||
Rain(TwinEEngine *engine);
|
||||
|
||||
void InitOneRain(T_RAIN *pt);
|
||||
void InitRain();
|
||||
void GereRain();
|
||||
void ClearImpactRain();
|
||||
void AffRain();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
885
engines/twine/scene/scene.cpp
Normal file
885
engines/twine/scene/scene.cpp
Normal file
@@ -0,0 +1,885 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/scene/rain.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "engines/enhancements.h"
|
||||
#include "twine/audio/music.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/debugger/debug_state.h"
|
||||
#include "twine/holomap.h"
|
||||
#include "twine/renderer/redraw.h"
|
||||
#include "twine/renderer/renderer.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/scene/animations.h"
|
||||
#include "twine/scene/extra.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/scene/movements.h"
|
||||
#include "twine/text.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Scene::~Scene() {
|
||||
free(_currentScene);
|
||||
}
|
||||
|
||||
void Scene::setActorStaticFlags(ActorStruct *act, uint32 staticFlags) {
|
||||
if (staticFlags & 0x1) {
|
||||
act->_flags.bComputeCollisionWithObj = 1;
|
||||
}
|
||||
if (staticFlags & 0x2) {
|
||||
act->_flags.bComputeCollisionWithBricks = 1;
|
||||
}
|
||||
if (staticFlags & 0x4) {
|
||||
act->_flags.bCheckZone = 1;
|
||||
}
|
||||
if (staticFlags & 0x8) {
|
||||
act->_flags.bSpriteClip = 1;
|
||||
}
|
||||
if (staticFlags & 0x10) {
|
||||
act->_flags.bCanBePushed = 1;
|
||||
}
|
||||
if (staticFlags & 0x20) {
|
||||
act->_flags.bComputeLowCollision = 1;
|
||||
}
|
||||
if (staticFlags & 0x40) {
|
||||
act->_flags.bCanDrown = 1;
|
||||
}
|
||||
if (staticFlags & 0x80) {
|
||||
act->_flags.bComputeCollisionWithFloor = 1;
|
||||
}
|
||||
|
||||
if (staticFlags & 0x100) {
|
||||
act->_flags.bUnk0100 = 1;
|
||||
}
|
||||
if (staticFlags & 0x200) {
|
||||
act->_flags.bIsInvisible = 1;
|
||||
}
|
||||
if (staticFlags & 0x400) {
|
||||
act->_flags.bSprite3D = 1;
|
||||
}
|
||||
if (staticFlags & 0x800) {
|
||||
act->_flags.bObjFallable = 1;
|
||||
}
|
||||
if (staticFlags & 0x1000) {
|
||||
act->_flags.bNoShadow = 1;
|
||||
}
|
||||
if (staticFlags & 0x2000) {
|
||||
act->_flags.bIsBackgrounded = 1;
|
||||
}
|
||||
if (staticFlags & 0x4000) {
|
||||
act->_flags.bIsCarrierActor = 1;
|
||||
}
|
||||
if (staticFlags & 0x8000) {
|
||||
act->_flags.bUseMiniZv = 1;
|
||||
}
|
||||
if (staticFlags & 0x10000) {
|
||||
act->_flags.bHasInvalidPosition = 1;
|
||||
}
|
||||
if (staticFlags & 0x20000) {
|
||||
act->_flags.bNoElectricShock = 1;
|
||||
}
|
||||
if (staticFlags & 0x40000) {
|
||||
act->_flags.bHasSpriteAnim3D = 1;
|
||||
}
|
||||
if (staticFlags & 0x80000) {
|
||||
act->_flags.bNoPreClipping = 1;
|
||||
}
|
||||
if (staticFlags & 0x100000) {
|
||||
act->_flags.bHasZBuffer = 1;
|
||||
}
|
||||
if (staticFlags & 0x200000) {
|
||||
act->_flags.bHasZBufferInWater = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::setBonusParameterFlags(ActorStruct *act, uint16 bonusFlags) {
|
||||
if (bonusFlags & 0x1) {
|
||||
act->_bonusParameter.givenNothing = 1;
|
||||
}
|
||||
if (bonusFlags & 0x2) {
|
||||
act->_bonusParameter.unk2 = 1;
|
||||
}
|
||||
if (bonusFlags & 0x4) {
|
||||
act->_bonusParameter.unk3 = 1;
|
||||
}
|
||||
if (bonusFlags & 0x8) {
|
||||
act->_bonusParameter.unk4 = 1;
|
||||
}
|
||||
if (bonusFlags & 0x10) {
|
||||
act->_bonusParameter.kashes = 1;
|
||||
}
|
||||
if (bonusFlags & 0x20) {
|
||||
act->_bonusParameter.lifepoints = 1;
|
||||
}
|
||||
if (bonusFlags & 0x40) {
|
||||
act->_bonusParameter.magicpoints = 1;
|
||||
}
|
||||
if (bonusFlags & 0x80) {
|
||||
act->_bonusParameter.key = 1;
|
||||
}
|
||||
if (bonusFlags & 0x100) {
|
||||
act->_bonusParameter.cloverleaf = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scene::loadSceneCubeXY(int numcube, int32 *cubex, int32 *cubey) {
|
||||
uint8 *scene = nullptr;
|
||||
// numcube+1 because at 0 is SizeCube.MAX (size of the largest .SCC)
|
||||
const int32 sceneSize = HQR::getAllocEntry(&scene, Resources::HQR_SCENE_FILE, numcube + 1);
|
||||
if (sceneSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
Common::MemoryReadStream stream(scene, sceneSize, DisposeAfterUse::YES);
|
||||
|
||||
*cubex = *cubey = 0;
|
||||
|
||||
// World info: INFO_WORLD
|
||||
const uint8 island = stream.readByte();
|
||||
|
||||
// Used only for 3DExt
|
||||
const int32 x = stream.readByte();
|
||||
const int32 y = stream.readByte();
|
||||
|
||||
/*uint8 shadowlvl =*/stream.readByte();
|
||||
/*uint8 modelaby =*/stream.readByte();
|
||||
const uint8 cubemode = stream.readByte();
|
||||
|
||||
if (cubemode == CUBE_EXTERIEUR && island == _island && ABS(x - _currentCubeX) <= 1 && ABS(y - _currentCubeY) <= 1) {
|
||||
*cubex = x;
|
||||
*cubey = y;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Scene::loadModel(ActorStruct &actor, int32 modelIndex, bool lba1) {
|
||||
actor._body = modelIndex;
|
||||
if (!actor._flags.bSprite3D) {
|
||||
debug(1, "Init actor with model %i", modelIndex);
|
||||
_engine->_resources->loadEntityData(actor._entityData, modelIndex);
|
||||
actor._entityDataPtr = &actor._entityData;
|
||||
} else {
|
||||
actor._entityDataPtr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scene::loadSceneLBA2() {
|
||||
Common::MemoryReadStream stream(_currentScene, _currentSceneSize);
|
||||
_island = stream.readByte();
|
||||
_sceneTextBank = (TextBankId)_island;
|
||||
_currentCubeX = stream.readByte();
|
||||
_currentCubeY = stream.readByte();
|
||||
_shadowLevel = stream.readByte();
|
||||
_modeLabyrinthe = stream.readByte();
|
||||
_isOutsideScene = stream.readByte();
|
||||
|
||||
/*uint8 n =*/ stream.readByte();
|
||||
|
||||
_alphaLight = ClampAngle(stream.readSint16LE());
|
||||
_betaLight = ClampAngle(stream.readSint16LE());
|
||||
debug(2, "Using %i and %i as light vectors", _alphaLight, _betaLight);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_sampleAmbiance[i] = stream.readUint16LE();
|
||||
_sampleRepeat[i] = stream.readUint16LE();
|
||||
_sampleRound[i] = stream.readUint16LE();
|
||||
_sampleFrequency[i] = stream.readUint16LE();
|
||||
_sampleVolume[i] = stream.readUint16LE();
|
||||
}
|
||||
|
||||
_sampleMinDelay = stream.readUint16LE();
|
||||
_sampleMinDelayRnd = stream.readUint16LE();
|
||||
|
||||
_cubeJingle = stream.readByte();
|
||||
|
||||
// load hero properties
|
||||
_sceneHeroPos.x = stream.readSint16LE();
|
||||
_sceneHeroPos.y = stream.readSint16LE();
|
||||
_sceneHeroPos.z = stream.readSint16LE();
|
||||
|
||||
_sceneHero->_moveScriptSize = (int16)stream.readUint16LE();
|
||||
_sceneHero->_ptrTrack = _currentScene + stream.pos();
|
||||
stream.skip(_sceneHero->_moveScriptSize);
|
||||
|
||||
_sceneHero->_lifeScriptSize = (int16)stream.readUint16LE();
|
||||
_sceneHero->_lifeScript = _currentScene + stream.pos();
|
||||
stream.skip(_sceneHero->_lifeScriptSize);
|
||||
|
||||
_nbObjets = (int16)stream.readUint16LE();
|
||||
int cnt = 1;
|
||||
for (int32 a = 1; a < _nbObjets; a++, cnt++) {
|
||||
_engine->_actor->initObject(a);
|
||||
ActorStruct *act = &_sceneActors[a];
|
||||
setActorStaticFlags(act, stream.readUint32LE());
|
||||
|
||||
loadModel(*act, (int16)stream.readUint16LE(), false);
|
||||
|
||||
act->_genBody = (BodyType)stream.readByte();
|
||||
act->_genAnim = (AnimationTypes)stream.readSint16LE();
|
||||
act->_sprite = (int16)stream.readUint16LE();
|
||||
act->_posObj.x = (int16)stream.readUint16LE();
|
||||
act->_posObj.y = (int16)stream.readUint16LE();
|
||||
act->_posObj.z = (int16)stream.readUint16LE();
|
||||
act->_oldPos = act->posObj();
|
||||
act->_hitForce = stream.readByte();
|
||||
setBonusParameterFlags(act, stream.readUint16LE());
|
||||
act->_beta = (int16)stream.readUint16LE();
|
||||
act->_srot = (int16)stream.readUint16LE();
|
||||
act->_move = (ControlMode)stream.readByte(); // move
|
||||
act->_cropLeft = stream.readSint16LE();
|
||||
act->_delayInMillis = act->_cropLeft; // TODO: this might not be needed
|
||||
act->_cropTop = stream.readSint16LE();
|
||||
act->_cropRight = stream.readSint16LE();
|
||||
act->_cropBottom = stream.readSint16LE();
|
||||
act->_followedActor = act->_cropBottom; // TODO: is this needed? and valid?
|
||||
act->_bonusAmount = stream.readSint16LE();
|
||||
act->_talkColor = stream.readByte();
|
||||
if (act->_flags.bHasSpriteAnim3D) {
|
||||
/*act->spriteAnim3DNumber = */stream.readSint32LE();
|
||||
/*act->spriteSizeHit = */stream.readSint16LE();
|
||||
/*act->cropBottom = act->spriteSizeHit;*/
|
||||
}
|
||||
act->_armor = stream.readByte();
|
||||
act->setLife(stream.readByte());
|
||||
|
||||
act->_moveScriptSize = (int16)stream.readUint16LE();
|
||||
act->_ptrTrack = _currentScene + stream.pos();
|
||||
stream.skip(act->_moveScriptSize);
|
||||
|
||||
act->_lifeScriptSize = (int16)stream.readUint16LE();
|
||||
act->_lifeScript = _currentScene + stream.pos();
|
||||
stream.skip(act->_lifeScriptSize);
|
||||
|
||||
if (_engine->_debugState->_onlyLoadActor != -1 && _engine->_debugState->_onlyLoadActor != cnt) {
|
||||
_nbObjets--;
|
||||
a--;
|
||||
}
|
||||
}
|
||||
|
||||
/* uint32 checksum = */stream.readUint32LE();
|
||||
|
||||
_sceneNumZones = (int16)stream.readUint16LE();
|
||||
for (int32 i = 0; i < _sceneNumZones; i++) {
|
||||
ZoneStruct *zone = &_sceneZones[i];
|
||||
zone->mins.x = stream.readSint32LE();
|
||||
zone->mins.y = stream.readSint32LE();
|
||||
zone->mins.z = stream.readSint32LE();
|
||||
|
||||
zone->maxs.x = stream.readSint32LE();
|
||||
zone->maxs.y = stream.readSint32LE();
|
||||
zone->maxs.z = stream.readSint32LE();
|
||||
|
||||
zone->infoData.generic.info0 = stream.readSint32LE();
|
||||
zone->infoData.generic.info1 = stream.readSint32LE();
|
||||
zone->infoData.generic.info2 = stream.readSint32LE();
|
||||
zone->infoData.generic.info3 = stream.readSint32LE();
|
||||
zone->infoData.generic.info4 = stream.readSint32LE();
|
||||
zone->infoData.generic.info5 = stream.readSint32LE();
|
||||
zone->infoData.generic.info6 = stream.readSint32LE();
|
||||
zone->infoData.generic.info7 = stream.readSint32LE();
|
||||
|
||||
zone->type = (ZoneType)stream.readUint16LE();
|
||||
zone->num = stream.readSint16LE();
|
||||
}
|
||||
|
||||
_sceneNumTracks = (int16)stream.readUint16LE();
|
||||
for (int32 i = 0; i < _sceneNumTracks; i++) {
|
||||
IVec3 *point = &_sceneTracks[i];
|
||||
point->x = stream.readSint32LE();
|
||||
point->y = stream.readSint32LE();
|
||||
point->z = stream.readSint32LE();
|
||||
}
|
||||
|
||||
uint16 sceneNumPatches = stream.readUint32LE();
|
||||
for (uint16 i = 0; i < sceneNumPatches; i++) {
|
||||
/*size = */stream.readUint16LE();
|
||||
/*offset = */stream.readUint16LE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// LoadScene
|
||||
bool Scene::loadSceneLBA1() {
|
||||
Common::MemoryReadStream stream(_currentScene, _currentSceneSize);
|
||||
|
||||
// load scene ambience properties
|
||||
_sceneTextBank = (TextBankId)stream.readByte();
|
||||
_currentGameOverScene = stream.readByte();
|
||||
stream.skip(4);
|
||||
|
||||
// FIXME: Workaround to fix lighting issue - not using proper dark light
|
||||
// Using 1215 and 1087 as light vectors - scene 8
|
||||
_alphaLight = ClampAngle((int16)stream.readUint16LE());
|
||||
_betaLight = ClampAngle((int16)stream.readUint16LE());
|
||||
debug(2, "Using %i and %i as light vectors", _alphaLight, _betaLight);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_sampleAmbiance[i] = stream.readUint16LE();
|
||||
_sampleRepeat[i] = stream.readUint16LE();
|
||||
_sampleRound[i] = stream.readUint16LE();
|
||||
}
|
||||
|
||||
_sampleMinDelay = stream.readUint16LE();
|
||||
_sampleMinDelayRnd = stream.readUint16LE();
|
||||
|
||||
_cubeJingle = stream.readByte();
|
||||
|
||||
// load hero properties
|
||||
_sceneHeroPos.x = (int16)stream.readUint16LE();
|
||||
_sceneHeroPos.y = (int16)stream.readUint16LE();
|
||||
_sceneHeroPos.z = (int16)stream.readUint16LE();
|
||||
|
||||
_sceneHero->_moveScriptSize = (int16)stream.readUint16LE();
|
||||
_sceneHero->_ptrTrack = _currentScene + stream.pos();
|
||||
stream.skip(_sceneHero->_moveScriptSize);
|
||||
|
||||
_sceneHero->_lifeScriptSize = (int16)stream.readUint16LE();
|
||||
_sceneHero->_lifeScript = _currentScene + stream.pos();
|
||||
stream.skip(_sceneHero->_lifeScriptSize);
|
||||
|
||||
_nbObjets = (int16)stream.readUint16LE();
|
||||
int cnt = 1;
|
||||
for (int32 a = 1; a < _nbObjets; a++, cnt++) {
|
||||
_engine->_actor->initObject(a);
|
||||
|
||||
ActorStruct *act = &_sceneActors[a];
|
||||
setActorStaticFlags(act, stream.readUint16LE());
|
||||
|
||||
loadModel(*act, stream.readUint16LE(), true);
|
||||
|
||||
act->_genBody = (BodyType)stream.readByte();
|
||||
act->_genAnim = (AnimationTypes)stream.readByte();
|
||||
act->_sprite = (int16)stream.readUint16LE();
|
||||
act->_posObj.x = (int16)stream.readUint16LE();
|
||||
act->_posObj.y = (int16)stream.readUint16LE();
|
||||
act->_posObj.z = (int16)stream.readUint16LE();
|
||||
act->_oldPos = act->posObj();
|
||||
act->_hitForce = stream.readByte();
|
||||
setBonusParameterFlags(act, stream.readUint16LE());
|
||||
act->_bonusParameter.givenNothing = 0;
|
||||
act->_beta = (int16)stream.readUint16LE();
|
||||
act->_srot = (int16)stream.readUint16LE();
|
||||
act->_move = (ControlMode)stream.readUint16LE();
|
||||
act->_cropLeft = stream.readSint16LE();
|
||||
act->_delayInMillis = act->_cropLeft; // TODO: this might not be needed
|
||||
act->_cropTop = stream.readSint16LE();
|
||||
act->_cropRight = stream.readSint16LE();
|
||||
act->_cropBottom = stream.readSint16LE();
|
||||
act->_followedActor = act->_cropBottom; // TODO: is this needed? and valid?
|
||||
act->_bonusAmount = stream.readByte();
|
||||
act->_talkColor = stream.readByte();
|
||||
act->_armor = stream.readByte();
|
||||
act->setLife(stream.readByte());
|
||||
|
||||
act->_moveScriptSize = (int16)stream.readUint16LE();
|
||||
act->_ptrTrack = _currentScene + stream.pos();
|
||||
stream.skip(act->_moveScriptSize);
|
||||
|
||||
act->_lifeScriptSize = (int16)stream.readUint16LE();
|
||||
act->_lifeScript = _currentScene + stream.pos();
|
||||
stream.skip(act->_lifeScriptSize);
|
||||
|
||||
if (_engine->_debugState->_onlyLoadActor != -1 && _engine->_debugState->_onlyLoadActor != cnt) {
|
||||
_nbObjets--;
|
||||
a--;
|
||||
}
|
||||
}
|
||||
|
||||
_sceneNumZones = (int16)stream.readUint16LE();
|
||||
for (int32 i = 0; i < _sceneNumZones; i++) {
|
||||
ZoneStruct *zone = &_sceneZones[i];
|
||||
zone->mins.x = stream.readSint16LE();
|
||||
zone->mins.y = stream.readSint16LE();
|
||||
zone->mins.z = stream.readSint16LE();
|
||||
|
||||
zone->maxs.x = stream.readSint16LE();
|
||||
zone->maxs.y = stream.readSint16LE();
|
||||
zone->maxs.z = stream.readSint16LE();
|
||||
|
||||
zone->type = (ZoneType)stream.readUint16LE();
|
||||
zone->num = stream.readSint16LE();
|
||||
|
||||
zone->infoData.generic.info0 = stream.readSint16LE();
|
||||
zone->infoData.generic.info1 = stream.readSint16LE();
|
||||
zone->infoData.generic.info2 = stream.readSint16LE();
|
||||
zone->infoData.generic.info3 = stream.readSint16LE();
|
||||
}
|
||||
|
||||
_sceneNumTracks = stream.readUint16LE();
|
||||
for (int32 i = 0; i < _sceneNumTracks; i++) {
|
||||
IVec3 *point = &_sceneTracks[i];
|
||||
point->x = stream.readSint16LE();
|
||||
point->y = stream.readSint16LE();
|
||||
point->z = stream.readSint16LE();
|
||||
}
|
||||
|
||||
if (_engine->isLBA1()) {
|
||||
if (_engine->enhancementEnabled(kEnhMinorBugFixes)) {
|
||||
if (_numCube == LBA1SceneId::Hamalayi_Mountains_landing_place) {
|
||||
// move the mine a little bit, as it's too close to the change cube zone
|
||||
_sceneActors[21]._posObj.x = _sceneActors[21]._oldPos.x = 6656 + 256;
|
||||
_sceneActors[21]._posObj.z = _sceneActors[21]._oldPos.z = 768;
|
||||
}
|
||||
#if 0
|
||||
else if (_numCube == LBA1SceneId::Tippet_Island_Secret_passage_scene_1) {
|
||||
_sceneZones[6].maxs.z = 3616;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (_engine->enhancementEnabled(kEnhGameBreakingBugFixes)) {
|
||||
if (_numCube == LBA1SceneId::Principal_Island_outside_the_fortress) {
|
||||
// https://bugs.scummvm.org/ticket/13818
|
||||
_sceneActors[29]._posObj.z = _sceneActors[29]._oldPos.z = 1795;
|
||||
} else if (_numCube == LBA1SceneId::Principal_Island_inside_the_fortress) {
|
||||
// https://bugs.scummvm.org/ticket/13819
|
||||
// Set this zone to something invalid to fix a getting-stuck-bug
|
||||
// the original value was ZoneType::kGrid (3)
|
||||
_sceneZones[11].type = ZoneType::kFunFrockFix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scene::loadScene(int32 index) {
|
||||
// load scene from file
|
||||
if (_engine->isLBA2()) {
|
||||
index++;
|
||||
}
|
||||
_currentSceneSize = HQR::getAllocEntry(&_currentScene, Resources::HQR_SCENE_FILE, index);
|
||||
if (_currentSceneSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_engine->isLBA1()) {
|
||||
return loadSceneLBA1();
|
||||
} else if (_engine->isLBA2()) {
|
||||
_engine->_rain->InitRain();
|
||||
return loadSceneLBA2();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Scene::clearScene() {
|
||||
_engine->_extra->clearExtra();
|
||||
|
||||
// ClearFlagsCube
|
||||
for (int32 i = 0; i < ARRAYSIZE(_listFlagCube); i++) {
|
||||
_listFlagCube[i] = 0;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
|
||||
_engine->_redraw->overlayList[i].num = -1;
|
||||
}
|
||||
|
||||
_engine->_screens->_flagPalettePcx = false;
|
||||
}
|
||||
|
||||
void Scene::reloadCurrentScene() {
|
||||
_newCube = _numCube;
|
||||
}
|
||||
|
||||
void Scene::dumpSceneScript(const char *type, int actorIdx, const uint8* script, int size) const {
|
||||
Common::String fname = Common::String::format("./dumps/%i-%i.%s", _numCube, actorIdx, type);
|
||||
Common::DumpFile out;
|
||||
if (!out.open(fname.c_str(), true)) {
|
||||
warning("Scene::dumpSceneScript(): Can not open dump file %s", fname.c_str());
|
||||
} else {
|
||||
out.write(script, size);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::dumpSceneScripts() const {
|
||||
for (int32 a = 0; a < _nbObjets; ++a) {
|
||||
const ActorStruct &actor = _sceneActors[a];
|
||||
dumpSceneScript("life", a, actor._lifeScript, actor._lifeScriptSize);
|
||||
dumpSceneScript("move", a, actor._ptrTrack, actor._moveScriptSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::changeCube() {
|
||||
if (_engine->isLBA1()) {
|
||||
if (_engine->enhancementEnabled(kEnhMinorBugFixes)) {
|
||||
if (_numCube == LBA1SceneId::Citadel_Island_Harbor && _newCube == LBA1SceneId::Principal_Island_Harbor) {
|
||||
if (_sceneNumZones >= 15 && _sceneNumTracks >= 8) {
|
||||
const ZoneStruct *zone = &_sceneZones[15];
|
||||
const IVec3 &track = _sceneTracks[8];
|
||||
IVec3 &pos = _zoneHeroPos;
|
||||
pos.x = zone->infoData.ChangeScene.x - zone->mins.x + track.x;
|
||||
pos.y = zone->infoData.ChangeScene.y - zone->mins.y + track.y;
|
||||
pos.z = zone->infoData.ChangeScene.z - zone->mins.z + track.z;
|
||||
_engine->_scene->_flagChgCube = ScenePositionType::kZone;
|
||||
debug(2, "Using zone position %i:%i:%i", pos.x, pos.y, pos.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change twinsen house destroyed hard-coded
|
||||
if (_newCube == LBA1SceneId::Citadel_Island_near_twinsens_house && _engine->_gameState->hasOpenedFunfrocksSafe()) {
|
||||
_newCube = LBA1SceneId::Citadel_Island_Twinsens_house_destroyed;
|
||||
}
|
||||
}
|
||||
|
||||
// local backup previous scene
|
||||
_oldcube = _numCube;
|
||||
_numCube = _newCube;
|
||||
|
||||
snprintf(_engine->_gameState->_sceneName, sizeof(_engine->_gameState->_sceneName), "%i %s", _numCube, _engine->_holomap->getLocationName(_numCube));
|
||||
debug(2, "Entering scene %s (came from %i)", _engine->_gameState->_sceneName, _oldcube);
|
||||
|
||||
if (_engine->isLBA1()) {
|
||||
if (_newCube == LBA1SceneId::Polar_Island_end_scene) {
|
||||
_engine->unlockAchievement("LBA_ACH_001");
|
||||
// if you finish the game in less than 4 hours
|
||||
if (_engine->getTotalPlayTime() <= 1000 * 60 * 60 * 4) {
|
||||
_engine->unlockAchievement("LBA_ACH_005");
|
||||
}
|
||||
} else if (_newCube == LBA1SceneId::Brundle_Island_Secret_room) {
|
||||
_engine->unlockAchievement("LBA_ACH_006");
|
||||
}
|
||||
}
|
||||
|
||||
_engine->_sound->stopSamples();
|
||||
|
||||
clearScene();
|
||||
_engine->_actor->loadHeroEntities();
|
||||
|
||||
_sceneHero->_move = ControlMode::kManual;
|
||||
_sceneHero->_zoneSce = -1;
|
||||
_sceneHero->_offsetLife = 0;
|
||||
_sceneHero->_offsetTrack = -1;
|
||||
_sceneHero->_labelTrack = -1;
|
||||
|
||||
loadScene(_newCube);
|
||||
if (ConfMan.getBool("dump_scripts")) {
|
||||
dumpSceneScripts();
|
||||
}
|
||||
|
||||
if (_numHolomapTraj != -1) {
|
||||
_engine->testRestoreModeSVGA(false);
|
||||
_engine->_screens->setBlackPal();
|
||||
_engine->_holomap->holoTraj(_numHolomapTraj);
|
||||
_numHolomapTraj = -1;
|
||||
_engine->_screens->_flagFade = true;
|
||||
} else {
|
||||
// TODO lbawin can do a fade here (if activated)
|
||||
// _engine->_screens->_flagFade = true;
|
||||
|
||||
}
|
||||
|
||||
if (_newCube == LBA1SceneId::Citadel_Island_end_sequence_1 || _newCube == LBA1SceneId::Citadel_Island_end_sequence_2) {
|
||||
_sceneTextBank = TextBankId::Tippet_Island;
|
||||
}
|
||||
|
||||
_engine->_text->initSceneTextBank();
|
||||
|
||||
if (_cubeJingle != 255) {
|
||||
// _engine->_music->fadeMusicMidi(1);
|
||||
}
|
||||
|
||||
_engine->_grid->initGrid(_newCube);
|
||||
|
||||
if (_flagChgCube == ScenePositionType::kZone) {
|
||||
_sceneStart = _zoneHeroPos;
|
||||
} else if (_flagChgCube == ScenePositionType::kScene || _flagChgCube == ScenePositionType::kNoPosition) {
|
||||
_sceneStart = _sceneHeroPos;
|
||||
}
|
||||
|
||||
_sceneHero->_posObj = _sceneStart;
|
||||
_startYFalling = _sceneStart.y;
|
||||
|
||||
_engine->_renderer->setLightVector(_alphaLight, _betaLight, LBAAngles::ANGLE_0);
|
||||
|
||||
if (_oldcube != SCENE_CEILING_GRID_FADE_1 && _oldcube != _newCube) {
|
||||
_engine->_actor->_previousHeroBehaviour = _engine->_actor->_heroBehaviour;
|
||||
_engine->_actor->_previousHeroAngle = _sceneHero->_beta;
|
||||
_engine->autoSave();
|
||||
}
|
||||
|
||||
_engine->_actor->restartPerso();
|
||||
|
||||
// StartInitAllObjs
|
||||
for (int32 a = 1; a < _nbObjets; a++) {
|
||||
_engine->_actor->startInitObj(a);
|
||||
}
|
||||
|
||||
_engine->_gameState->_nbLittleKeys = 0;
|
||||
_engine->_gameState->_magicBall = -1;
|
||||
_engine->_movements->_lastJoyFlag = true;
|
||||
_engine->_grid->_zoneGrm = -1;
|
||||
_engine->_grid->_indexGrm = -1;
|
||||
_engine->_redraw->_firstTime = true;
|
||||
_engine->_cameraZone = false;
|
||||
_newCube = SCENE_CEILING_GRID_FADE_1;
|
||||
_flagChgCube = ScenePositionType::kNoPosition;
|
||||
_flagRenderGrid = true;
|
||||
|
||||
_samplePlayed = 2 * 4 * 8;
|
||||
_timerNextAmbiance = 0;
|
||||
|
||||
ActorStruct *followedActor = getActor(_numObjFollow);
|
||||
_engine->_grid->centerOnActor(followedActor);
|
||||
|
||||
_engine->_screens->_flagFade = true;
|
||||
_engine->_renderer->setLightVector(_alphaLight, _betaLight, LBAAngles::ANGLE_0);
|
||||
|
||||
_zoneHeroPos = IVec3();
|
||||
|
||||
debug(2, "Scene %i music track id: %i", _numCube, _cubeJingle);
|
||||
if (_cubeJingle != 255) {
|
||||
_engine->_music->playMusic(_cubeJingle);
|
||||
}
|
||||
|
||||
_engine->_gameState->handleLateGameItems();
|
||||
}
|
||||
|
||||
ActorStruct *Scene::getActor(int32 actorIdx) {
|
||||
if (actorIdx < 0 || actorIdx >= NUM_MAX_ACTORS) {
|
||||
error("Invalid actor id given: %i", actorIdx);
|
||||
}
|
||||
return &_sceneActors[actorIdx];
|
||||
}
|
||||
|
||||
void Scene::initSceneVars() {
|
||||
_sampleAmbiance[0] = -1;
|
||||
_sampleAmbiance[1] = -1;
|
||||
_sampleAmbiance[2] = -1;
|
||||
_sampleAmbiance[3] = -1;
|
||||
|
||||
_sampleRepeat[0] = 0;
|
||||
_sampleRepeat[1] = 0;
|
||||
_sampleRepeat[2] = 0;
|
||||
_sampleRepeat[3] = 0;
|
||||
|
||||
_sampleRound[0] = 0;
|
||||
_sampleRound[1] = 0;
|
||||
_sampleRound[2] = 0;
|
||||
_sampleRound[3] = 0;
|
||||
|
||||
_nbObjets = 0;
|
||||
_sceneNumZones = 0;
|
||||
_sceneNumTracks = 0;
|
||||
}
|
||||
|
||||
void Scene::playSceneMusic() {
|
||||
if (_engine->isLBA1()) {
|
||||
if (_numCube == LBA1SceneId::Tippet_Island_Twinsun_Cafe && _engine->_gameState->hasArrivedHamalayi()) {
|
||||
if (_engine->isCDROM()) {
|
||||
_engine->_music->playCdTrack(8);
|
||||
} else {
|
||||
_engine->_music->playMusic(_cubeJingle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_engine->_music->playMidiFile(_cubeJingle);
|
||||
}
|
||||
|
||||
void Scene::processEnvironmentSound() {
|
||||
if (_engine->timerRef < _timerNextAmbiance) {
|
||||
return;
|
||||
}
|
||||
int16 currentAmb = _engine->getRandomNumber(4); // random ambiance
|
||||
|
||||
for (int32 s = 0; s < 4; s++) {
|
||||
if (!(_samplePlayed & (1 << currentAmb))) { // if not already played
|
||||
_samplePlayed |= (1 << currentAmb); // make sample played
|
||||
|
||||
if (_samplePlayed == 15) { // reset if all samples played
|
||||
_samplePlayed = 0;
|
||||
}
|
||||
|
||||
const int16 sampleIdx = _sampleAmbiance[currentAmb];
|
||||
if (sampleIdx != -1) {
|
||||
int16 decal = _sampleRound[currentAmb];
|
||||
int16 repeat = _sampleRepeat[currentAmb];
|
||||
|
||||
const uint16 pitchbend = 0x1000 + _engine->getRandomNumber(decal) - (decal / 2);
|
||||
_engine->_sound->mixSample(sampleIdx, pitchbend, repeat, 110, 110);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentAmb++; // try next ambiance
|
||||
currentAmb &= 3; // loop in all 4 ambiances
|
||||
}
|
||||
|
||||
// compute next ambiance timer
|
||||
_timerNextAmbiance = _engine->timerRef + _engine->toSeconds(_engine->getRandomNumber(_sampleMinDelayRnd) + _sampleMinDelay);
|
||||
}
|
||||
|
||||
void Scene::processZoneExtraBonus(ZoneStruct *zone) {
|
||||
if (zone->infoData.Bonus.used) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int bonusSprite = _engine->_extra->getBonusSprite(zone->infoData.Bonus.typesFlag);
|
||||
if (bonusSprite == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 amount = zone->infoData.Bonus.amount;
|
||||
const int32 x = (zone->maxs.x + zone->mins.x) / 2;
|
||||
const int32 z = (zone->maxs.z + zone->mins.z) / 2;
|
||||
const int32 angle = _engine->_movements->getAngle(x, z, _sceneHero->_posObj.x, _sceneHero->_posObj.z);
|
||||
const int32 index = _engine->_extra->addExtraBonus(x, zone->maxs.y, z, LBAAngles::ANGLE_63, angle, bonusSprite, amount);
|
||||
|
||||
if (index != -1) {
|
||||
_engine->_extra->_extraList[index].type |= ExtraType::TIME_IN;
|
||||
zone->infoData.Bonus.used = 1; // set as used
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::checkZoneSce(int32 actorIdx) {
|
||||
ActorStruct *actor = &_sceneActors[actorIdx];
|
||||
|
||||
int32 currentX = actor->_posObj.x;
|
||||
int32 currentY = actor->_posObj.y;
|
||||
int32 currentZ = actor->_posObj.z;
|
||||
|
||||
actor->_zoneSce = -1;
|
||||
bool flaggrm = false;
|
||||
|
||||
if (IS_HERO(actorIdx)) {
|
||||
_flagClimbing = false;
|
||||
}
|
||||
|
||||
for (int32 z = 0; z < _sceneNumZones; z++) {
|
||||
ZoneStruct *zone = &_sceneZones[z];
|
||||
|
||||
// check if actor is in zone
|
||||
if ((currentX >= zone->mins.x && currentX <= zone->maxs.x) &&
|
||||
(currentY >= zone->mins.y && currentY <= zone->maxs.y) &&
|
||||
(currentZ >= zone->mins.z && currentZ <= zone->maxs.z)) {
|
||||
switch (zone->type) {
|
||||
default:
|
||||
warning("lba2 zone types not yet implemented");
|
||||
break;
|
||||
case ZoneType::kFunFrockFix:
|
||||
break;
|
||||
case ZoneType::kCube:
|
||||
if (IS_HERO(actorIdx) && actor->_lifePoint > 0) {
|
||||
_newCube = zone->num;
|
||||
_zoneHeroPos.x = actor->_posObj.x - zone->mins.x + zone->infoData.ChangeScene.x;
|
||||
_zoneHeroPos.y = actor->_posObj.y - zone->mins.y + zone->infoData.ChangeScene.y;
|
||||
_zoneHeroPos.z = actor->_posObj.z - zone->mins.z + zone->infoData.ChangeScene.z;
|
||||
_flagChgCube = ScenePositionType::kZone;
|
||||
}
|
||||
break;
|
||||
case ZoneType::kCamera:
|
||||
if (_numObjFollow == actorIdx && !_engine->_debugState->_useFreeCamera) {
|
||||
_engine->_cameraZone = true;
|
||||
if (_engine->_grid->_startCube.x != zone->infoData.CameraView.x || _engine->_grid->_startCube.y != zone->infoData.CameraView.y || _engine->_grid->_startCube.z != zone->infoData.CameraView.z) {
|
||||
_engine->_grid->_startCube.x = zone->infoData.CameraView.x;
|
||||
_engine->_grid->_startCube.y = zone->infoData.CameraView.y;
|
||||
_engine->_grid->_startCube.z = zone->infoData.CameraView.z;
|
||||
_engine->_redraw->_firstTime = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZoneType::kSceneric:
|
||||
actor->_zoneSce = zone->num;
|
||||
break;
|
||||
case ZoneType::kGrid:
|
||||
if (_numObjFollow == actorIdx) {
|
||||
flaggrm = true;
|
||||
if (_engine->_grid->_zoneGrm != zone->num) {
|
||||
if (_engine->_grid->_zoneGrm != -1) {
|
||||
_engine->_grid->copyMapToCube();
|
||||
}
|
||||
|
||||
_engine->_grid->_zoneGrm = zone->num;
|
||||
_engine->_grid->_indexGrm = z;
|
||||
_engine->saveTimer(false);
|
||||
_engine->_grid->initCellingGrid(_engine->_grid->_zoneGrm);
|
||||
_engine->restoreTimer();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZoneType::kObject:
|
||||
if (IS_HERO(actorIdx) && _engine->_movements->actionNormal()) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kAction, AnimType::kAnimationThen, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
|
||||
processZoneExtraBonus(zone);
|
||||
}
|
||||
break;
|
||||
case ZoneType::kText:
|
||||
if (IS_HERO(actorIdx) && _engine->_movements->actionNormal()) {
|
||||
_engine->saveTimer(false);
|
||||
_engine->testRestoreModeSVGA(true);
|
||||
_engine->_text->setFontCrossColor(zone->infoData.DisplayText.textColor);
|
||||
_talkingActor = actorIdx;
|
||||
_engine->_text->drawTextProgressive((TextId)zone->num);
|
||||
_engine->restoreTimer();
|
||||
_engine->_redraw->drawScene(true);
|
||||
}
|
||||
break;
|
||||
case ZoneType::kLadder:
|
||||
if (IS_HERO(actorIdx) && _engine->_actor->_heroBehaviour != HeroBehaviourType::kProtoPack && (actor->_genAnim == AnimationTypes::kForward || actor->_genAnim == AnimationTypes::kTopLadder || actor->_genAnim == AnimationTypes::kClimbLadder)) {
|
||||
IVec2 destPos = _engine->_renderer->rotate(actor->_boundingBox.mins.x, actor->_boundingBox.mins.z, actor->_beta + LBAAngles::ANGLE_360 + LBAAngles::ANGLE_135);
|
||||
destPos.x += actor->_processActor.x;
|
||||
destPos.y += actor->_processActor.z;
|
||||
|
||||
if (destPos.x >= 0 && destPos.y >= 0 && destPos.x <= SCENE_SIZE_MAX && destPos.y <= SCENE_SIZE_MAX) {
|
||||
if (_engine->_grid->worldColBrick(destPos.x, actor->_posObj.y + SIZE_BRICK_Y, destPos.y) != ShapeType::kNone) {
|
||||
_flagClimbing = true;
|
||||
if (actor->_posObj.y >= (zone->mins.y + zone->maxs.y) / 2) {
|
||||
_engine->_animations->initAnim(AnimationTypes::kTopLadder, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx); // reached end of ladder
|
||||
} else {
|
||||
_engine->_animations->initAnim(AnimationTypes::kClimbLadder, AnimType::kAnimationTypeRepeat, AnimationTypes::kNoAnim, actorIdx); // go up in ladder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flaggrm && actorIdx == _numObjFollow && _engine->_grid->_zoneGrm != -1) {
|
||||
_engine->_grid->_zoneGrm = -1;
|
||||
_engine->_grid->_indexGrm = -1;
|
||||
_engine->_grid->copyMapToCube();
|
||||
_engine->_redraw->_firstTime = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::stopRunningGame() {
|
||||
free(_currentScene);
|
||||
_currentScene = nullptr;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
251
engines/twine/scene/scene.h
Normal file
251
engines/twine/scene/scene.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_SCENE_H
|
||||
#define TWINE_SCENE_SCENE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/shared.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
#define NUM_SCENES_FLAGS 80
|
||||
|
||||
#define NUM_MAX_ACTORS 100
|
||||
#define NUM_MAX_ZONES 100
|
||||
#define NUM_MAX_TRACKS 200
|
||||
|
||||
enum class ScenePositionType {
|
||||
kNoPosition = 0,
|
||||
kZone = 1,
|
||||
kScene = 2,
|
||||
kReborn = 3
|
||||
};
|
||||
|
||||
// ZONES
|
||||
|
||||
#define ZONE_INIT_ON 1
|
||||
#define ZONE_ON 2
|
||||
#define ZONE_ACTIVE 4
|
||||
#define ZONE_OBLIGATOIRE 8
|
||||
|
||||
/**
|
||||
* Special actions, like change scene, climbing a ladder, ...
|
||||
*/
|
||||
struct ZoneStruct {
|
||||
IVec3 mins;
|
||||
IVec3 maxs;
|
||||
ZoneType type = ZoneType::kCube;
|
||||
int32 num;
|
||||
union {
|
||||
struct {
|
||||
int32 x;
|
||||
int32 y;
|
||||
int32 z;
|
||||
} ChangeScene;
|
||||
struct {
|
||||
int32 x;
|
||||
int32 y;
|
||||
int32 z;
|
||||
} CameraView;
|
||||
/** show a text (e.g. when reading a sign) */
|
||||
struct {
|
||||
int32 textColor; /*!< text color (see @c ActorStruct::talkColor) */
|
||||
} DisplayText;
|
||||
struct {
|
||||
BonusParameter typesFlag;
|
||||
int32 amount;
|
||||
/**
|
||||
* Already used
|
||||
*/
|
||||
int32 used;
|
||||
} Bonus;
|
||||
struct {
|
||||
int32 info0;
|
||||
int32 info1;
|
||||
int32 info2;
|
||||
int32 info3;
|
||||
int32 info4;
|
||||
int32 info5;
|
||||
int32 info6;
|
||||
int32 info7;
|
||||
} generic;
|
||||
} infoData;
|
||||
};
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
/**
|
||||
* scene 0: 23 actors
|
||||
*
|
||||
* scene 1: 14 actors
|
||||
* actor 1 - car
|
||||
* actor 2 - elephant
|
||||
* actor 3 - soldier at the house
|
||||
* actor 4 - patrolling soldier before gate
|
||||
* actor 5 - soldier after gate
|
||||
* actor 6 - ??
|
||||
* actor 7 - ??
|
||||
* actor 8 - left gate
|
||||
* actor 9 - ??
|
||||
* actor 10 - door after leaving truck
|
||||
* actor 11 - door subway
|
||||
* actor 12 - guy at rubbish
|
||||
* actor 13 - ??
|
||||
*/
|
||||
class Scene {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
void loadModel(ActorStruct &actor, int32 modelIndex, bool lba1);
|
||||
|
||||
/** Process zone extra bonus */
|
||||
void processZoneExtraBonus(ZoneStruct *zone);
|
||||
void setActorStaticFlags(ActorStruct *act, uint32 staticFlags);
|
||||
void setBonusParameterFlags(ActorStruct *act, uint16 bonusFlags);
|
||||
bool loadSceneLBA1();
|
||||
bool loadSceneLBA2();
|
||||
|
||||
/** Initialize new scene */
|
||||
bool loadScene(int32 index);
|
||||
/** Reset scene */
|
||||
void clearScene();
|
||||
|
||||
// the first actor is the own hero
|
||||
ActorStruct _sceneActors[NUM_MAX_ACTORS]; // ListObjet
|
||||
int32 _currentSceneSize = 0;
|
||||
bool _isOutsideScene = false; // lba2
|
||||
|
||||
/** Timer for the next sample ambience in scene */
|
||||
int32 _timerNextAmbiance = 0;
|
||||
int16 _sampleAmbiance[4]{0};
|
||||
int16 _sampleRepeat[4]{0};
|
||||
int16 _sampleRound[4]{0};
|
||||
int16 _sampleFrequency[4]{0}; // lba2
|
||||
int16 _sampleVolume[4]{0}; // lba2
|
||||
int16 _sampleMinDelay = 0; // SecondMin
|
||||
int16 _sampleMinDelayRnd = 0; // SecondEcart
|
||||
|
||||
int16 _samplePlayed = 0;
|
||||
|
||||
public:
|
||||
int16 _cubeJingle = 0;
|
||||
private:
|
||||
IVec3 _sceneHeroPos; // CubeStartX, CubeStartY, CubeStartZ
|
||||
IVec3 _zoneHeroPos; // NewPosX, NewPosY, NewPosZ
|
||||
|
||||
int32 _currentGameOverScene = 0;
|
||||
|
||||
uint8 *_currentScene = nullptr;
|
||||
void dumpSceneScripts() const;
|
||||
void dumpSceneScript(const char *type, int actorIdx, const uint8* script, int size) const;
|
||||
public:
|
||||
Scene(TwinEEngine *engine) : _engine(engine) {}
|
||||
~Scene();
|
||||
|
||||
int32 _newCube = LBA1SceneId::Citadel_Island_Prison;
|
||||
int32 _numCube = LBA1SceneId::Citadel_Island_Prison; // NumCube
|
||||
int32 _oldcube = LBA1SceneId::Citadel_Island_Prison;
|
||||
|
||||
int32 _planet = -1;
|
||||
|
||||
int32 _numHolomapTraj = -1;
|
||||
|
||||
TextBankId _sceneTextBank = TextBankId::None;
|
||||
int32 _alphaLight = 0;
|
||||
int32 _betaLight = 0;
|
||||
|
||||
uint8 _island = 0;
|
||||
uint8 _shadowLevel = 0; // lba2
|
||||
uint8 _modeLabyrinthe = 0; // lba2
|
||||
uint8 _cinemaMode = 0; // lba2
|
||||
uint8 _currentCubeX = 0; // lba2
|
||||
uint8 _currentCubeY = 0; // lba2
|
||||
|
||||
IVec3 _sceneStart;
|
||||
|
||||
/** Hero Y coordinate before fall */
|
||||
int16 _startYFalling = 0;
|
||||
|
||||
/** Hero type of position in scene */
|
||||
ScenePositionType _flagChgCube = ScenePositionType::kNoPosition; // twinsenPositionModeInNewCube
|
||||
|
||||
// ACTORS
|
||||
int32 _nbObjets = 0;
|
||||
ActorStruct *_sceneHero = nullptr;
|
||||
|
||||
/** Meca penguin actor index */
|
||||
int16 _mecaPenguinIdx = 0;
|
||||
|
||||
/** Current followed actor in scene */
|
||||
int16 _numObjFollow = OWN_ACTOR_SCENE_INDEX;
|
||||
/** Current actor in zone - climbing a ladder */
|
||||
bool _flagClimbing = false;
|
||||
/** Current actor manipulated in scripts */
|
||||
int16 _currentScriptValue = 0;
|
||||
|
||||
int16 _talkingActor = 0;
|
||||
|
||||
// TRACKS Tell the actor where to go
|
||||
int32 _sceneNumTracks = 0;
|
||||
IVec3 _sceneTracks[NUM_MAX_TRACKS];
|
||||
|
||||
bool _flagRenderGrid = true; // FlagAffGrille
|
||||
|
||||
uint8 _listFlagCube[NUM_SCENES_FLAGS]{0}; // ListVarCube
|
||||
|
||||
int32 _sceneNumZones = 0; // NbZones
|
||||
ZoneStruct _sceneZones[NUM_MAX_ZONES]; // ListZone
|
||||
|
||||
ActorStruct *getActor(int32 actorIdx); // ListObjet
|
||||
|
||||
void playSceneMusic();
|
||||
|
||||
void reloadCurrentScene();
|
||||
|
||||
/** Change to another scene */
|
||||
void changeCube();
|
||||
|
||||
/** For the buggy to get the 2D coordinates of an exterior cube in the map */
|
||||
bool loadSceneCubeXY(int sceneNum, int32 *cubeX, int32 *cubeY);
|
||||
|
||||
/** Process scene environment sound */
|
||||
void processEnvironmentSound(); // GereAmbiance
|
||||
void initSceneVars();
|
||||
|
||||
bool isGameRunning() const;
|
||||
void stopRunningGame();
|
||||
|
||||
/**
|
||||
* Process actor zones
|
||||
* @param actorIdx Process actor index
|
||||
*/
|
||||
void checkZoneSce(int32 actorIdx);
|
||||
};
|
||||
|
||||
inline bool Scene::isGameRunning() const {
|
||||
return _currentScene != nullptr;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
42
engines/twine/scene/wagon.cpp
Normal file
42
engines/twine/scene/wagon.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/scene/wagon.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
void Wagon::DoAnimWagon() {
|
||||
}
|
||||
|
||||
void Wagon::DoDirWagon(ActorStruct *ptrobj) {
|
||||
}
|
||||
|
||||
int32 Wagon::GetNumBrickWagon(int32 brick) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Wagon::AdjustEssieuWagonAvant(int32 brickw) {
|
||||
}
|
||||
|
||||
void Wagon::AdjustEssieuWagonArriere(int32 brickw) {
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
46
engines/twine/scene/wagon.h
Normal file
46
engines/twine/scene/wagon.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TWINE_SCENE_WAGON_H
|
||||
#define TWINE_SCENE_WAGON_H
|
||||
|
||||
#include "twine/scene/actor.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class Wagon {
|
||||
private:
|
||||
//TwinEEngine *_engine;
|
||||
|
||||
public:
|
||||
Wagon(TwinEEngine *engine) /* : _engine(engine) */ {}
|
||||
|
||||
void DoAnimWagon();
|
||||
void DoDirWagon(ActorStruct *ptrobj);
|
||||
int32 GetNumBrickWagon(int32 brick);
|
||||
void AdjustEssieuWagonAvant(int32 brickw);
|
||||
void AdjustEssieuWagonArriere(int32 brickw);
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user