Font patch from Andrea Corna - improved keyframe support - SMUSH looping support - improved IMUSE stability - additional error checking
This commit is contained in:
parent
6bebeb9fa2
commit
b537213d94
30 changed files with 897 additions and 321 deletions
20
README
20
README
|
@ -1,5 +1,5 @@
|
||||||
Residual: A LucasArts 3D game interpreter Version: 0.04-CVS
|
Residual: A LucasArts 3D game interpreter Version: 0.04-CVS
|
||||||
(C) 2003-2005 The ScummVM-Residual team Last Updated: 03 Apr 2005
|
(C) 2003-2005 The ScummVM-Residual team Last Updated: 17 Jul 2005
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
What is Residual?
|
What is Residual?
|
||||||
|
@ -55,8 +55,15 @@ renderer (-soft).
|
||||||
What is the state of Residual?
|
What is the state of Residual?
|
||||||
-------------------------------
|
-------------------------------
|
||||||
Basic gameplay works, including cutscenes. Some of the game is playable,
|
Basic gameplay works, including cutscenes. Some of the game is playable,
|
||||||
but many features are either missing or unstable. There are no menus,
|
but many features are either missing or unstable. There is no abilitity
|
||||||
save/load features, lighting, etc. Crashes are likely.
|
to save/load, lighting, etc. Crashes are likely.
|
||||||
|
|
||||||
|
Game currently playable to:
|
||||||
|
Manny reaps Meche (Mercedes Colomar)
|
||||||
|
Caveats:
|
||||||
|
1) Random crash leaving the scene with the balloon handler
|
||||||
|
2) Must press "Esc" after listening to Meche, conversation doesn't
|
||||||
|
continue on its own
|
||||||
|
|
||||||
What are the default keys?
|
What are the default keys?
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -65,7 +72,8 @@ Arrow keys : Movement
|
||||||
Shift : Hold to run
|
Shift : Hold to run
|
||||||
Enter : Selects items in inventory, conversation, etc
|
Enter : Selects items in inventory, conversation, etc
|
||||||
Escape : Skips cutscenes, exits certain screens
|
Escape : Skips cutscenes, exits certain screens
|
||||||
q : Quit
|
q : Exit Dialog Menu
|
||||||
|
Ctrl + c : Force Quit (from command-line)
|
||||||
|
|
||||||
Development/debug keys from the original game
|
Development/debug keys from the original game
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
@ -95,7 +103,6 @@ Don't. Residual is very alpha and we KNOW it doesn't work right.
|
||||||
|
|
||||||
What else should I know?
|
What else should I know?
|
||||||
------------------------
|
------------------------
|
||||||
* Exit with 'q', and don't press f1, as things will go loopy.
|
|
||||||
* See TODO for other stuff
|
* See TODO for other stuff
|
||||||
|
|
||||||
Credits:
|
Credits:
|
||||||
|
@ -109,7 +116,8 @@ Contributors:
|
||||||
Vincent Hamm Various engine code
|
Vincent Hamm Various engine code
|
||||||
Lionel 'bbrox' Ulmer OpenGL optimisations
|
Lionel 'bbrox' Ulmer OpenGL optimisations
|
||||||
Ori 'salty-horse' Avtalion Lipsync, LAF support
|
Ori 'salty-horse' Avtalion Lipsync, LAF support
|
||||||
Erich 'Compholio' Hoover x86-64, subtitles fixes, menu support
|
Erich 'Compholio' Hoover x86-64, various fixes and comments, menu support, improved state support
|
||||||
|
Andrea 'Yak Bizzarro' Corna Improved font support
|
||||||
|
|
||||||
Special Thanks To:
|
Special Thanks To:
|
||||||
------------------
|
------------------
|
||||||
|
|
7
TODO
7
TODO
|
@ -1,17 +1,22 @@
|
||||||
Residual TODO list (in rough order of priority):
|
Residual TODO list (in rough order of priority):
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
Assigned tasks:
|
Assigned tasks:
|
||||||
|
* Make game playable to the petrified forest (compholio)
|
||||||
* Finish text drawing support (salty-horse, aquadran)
|
* Finish text drawing support (salty-horse, aquadran)
|
||||||
* Cross platform GUI for debug input dialogs and path selection (ender)
|
* Cross platform GUI for debug input dialogs and path selection (ender)
|
||||||
* Improved walk box code (frob)
|
* Improved walk box code (frob)
|
||||||
* Implement FadeInChore and FadeOutChore (frob)
|
* Implement FadeInChore and FadeOutChore (frob)
|
||||||
* Implement texture mapping with light shading in TinyGL (aquadran)
|
* Implement texture mapping with light shading in TinyGL (aquadran)
|
||||||
|
* Improve menu support (compholio)
|
||||||
|
* Improve SMUSH looping support (compholio)
|
||||||
|
|
||||||
Unassigned (help wanted):
|
Unassigned (help wanted):
|
||||||
|
* Fix random problem with Manny changing between scenes, switching between
|
||||||
|
fe.set and st.set is a good example of where it happens a lot
|
||||||
* Add configure script (Custom ala main ScummVM, NOT autoconf)
|
* Add configure script (Custom ala main ScummVM, NOT autoconf)
|
||||||
* Proper light setup in drivers
|
* Proper light setup in drivers
|
||||||
* Finish Save/Load support for rest of Engine (Lua and iMuse done)
|
* Finish Save/Load support for rest of Engine (Lua and iMuse done)
|
||||||
* Implement 2D primitives
|
* Finish 2D primitive code
|
||||||
* Proper vsscanf implementation in textsplit.cpp for platforms without it (MSVC, etc)
|
* Proper vsscanf implementation in textsplit.cpp for platforms without it (MSVC, etc)
|
||||||
* Make SMUSH work on Linux/PPC (whats wrong with it, exactly? - ender :)
|
* Make SMUSH work on Linux/PPC (whats wrong with it, exactly? - ender :)
|
||||||
* Finish panning in 3d position code
|
* Finish panning in 3d position code
|
||||||
|
|
46
actor.cpp
46
actor.cpp
|
@ -34,7 +34,7 @@
|
||||||
Actor::Actor(const char *name) :
|
Actor::Actor(const char *name) :
|
||||||
_name(name), _talkColor(255, 255, 255), _pos(0, 0, 0),
|
_name(name), _talkColor(255, 255, 255), _pos(0, 0, 0),
|
||||||
_pitch(0), _yaw(0), _roll(0), _walkRate(0), _turnRate(0),
|
_pitch(0), _yaw(0), _roll(0), _walkRate(0), _turnRate(0),
|
||||||
_reflectionAngle(80),
|
_reflectionAngle(80), _setName(""), _setNameTmp(""),
|
||||||
_visible(true), _lipSynch(NULL), _turning(false), _walking(false),
|
_visible(true), _lipSynch(NULL), _turning(false), _walking(false),
|
||||||
_restCostume(NULL), _restChore(-1),
|
_restCostume(NULL), _restChore(-1),
|
||||||
_walkCostume(NULL), _walkChore(-1), _walkedLast(false), _walkedCur(false),
|
_walkCostume(NULL), _walkChore(-1), _walkedLast(false), _walkedCur(false),
|
||||||
|
@ -278,9 +278,11 @@ void Actor::sayLine(const char *msg, const char *msgId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// During movies, SayLine is called for text display only
|
// During Fullscreen movies SayLine is called for text display only
|
||||||
if (!g_smush->isPlaying()) {
|
// However, normal SMUSH movies may call SayLine, for example:
|
||||||
|
// When Domino yells at Manny (a SMUSH movie) he does it with
|
||||||
|
// a SayLine request rather than as part of the movie!
|
||||||
|
if (!g_smush->isPlaying() || g_engine->getMode() == ENGINE_MODE_NORMAL) {
|
||||||
std::string soundName = msgId;
|
std::string soundName = msgId;
|
||||||
std::string soundLip = msgId;
|
std::string soundLip = msgId;
|
||||||
soundName += ".wav";
|
soundName += ".wav";
|
||||||
|
@ -306,7 +308,11 @@ void Actor::sayLine(const char *msg, const char *msgId) {
|
||||||
// Also, some lip synch files have no entries
|
// Also, some lip synch files have no entries
|
||||||
// In these cases, revert to using the mumble chore.
|
// In these cases, revert to using the mumble chore.
|
||||||
_lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str());
|
_lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str());
|
||||||
|
// If there's no lip synch file then load the mumble chore if it exists
|
||||||
|
// (the mumble chore doesn't exist with the cat races announcer)
|
||||||
|
if (_lipSynch == NULL && _mumbleChore != -1)
|
||||||
|
_mumbleCostume->playChoreLooping(_mumbleChore);
|
||||||
|
|
||||||
_talkAnim = -1;
|
_talkAnim = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +331,7 @@ void Actor::sayLine(const char *msg, const char *msgId) {
|
||||||
// of the screen
|
// of the screen
|
||||||
if (!visible() || !inSet(g_engine->currScene()->name())) {
|
if (!visible() || !inSet(g_engine->currScene()->name())) {
|
||||||
_sayLineText->setX(640 / 2);
|
_sayLineText->setX(640 / 2);
|
||||||
_sayLineText->setY(440);
|
_sayLineText->setY(420);
|
||||||
} else {
|
} else {
|
||||||
// render at the top for active actors for now
|
// render at the top for active actors for now
|
||||||
_sayLineText->setX(640 / 2);
|
_sayLineText->setX(640 / 2);
|
||||||
|
@ -336,11 +342,16 @@ void Actor::sayLine(const char *msg, const char *msgId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Actor::talking() {
|
bool Actor::talking() {
|
||||||
|
// If there's no sound file then we're obviously not talking
|
||||||
|
if (strlen(_talkSoundName.c_str()) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return g_imuse->getSoundStatus(_talkSoundName.c_str());
|
return g_imuse->getSoundStatus(_talkSoundName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::shutUp() {
|
void Actor::shutUp() {
|
||||||
g_imuse->stopSound(_talkSoundName.c_str());
|
// Don't stop the sound, the call to stop the sound
|
||||||
|
// is made by the game
|
||||||
_talkSoundName = "";
|
_talkSoundName = "";
|
||||||
if (_lipSynch != NULL) {
|
if (_lipSynch != NULL) {
|
||||||
if ((_talkAnim != -1) && (_talkChore[_talkAnim] >= 0))
|
if ((_talkAnim != -1) && (_talkChore[_talkAnim] >= 0))
|
||||||
|
@ -387,6 +398,18 @@ void Actor::popCostume() {
|
||||||
freeCostumeChore(_costumeStack.back(), _talkCostume[i], _talkChore[i]);
|
freeCostumeChore(_costumeStack.back(), _talkCostume[i], _talkChore[i]);
|
||||||
delete _costumeStack.back();
|
delete _costumeStack.back();
|
||||||
_costumeStack.pop_back();
|
_costumeStack.pop_back();
|
||||||
|
Costume *newCost;
|
||||||
|
if (_costumeStack.empty())
|
||||||
|
newCost = NULL;
|
||||||
|
else
|
||||||
|
newCost = _costumeStack.back();
|
||||||
|
if (newCost == NULL) {
|
||||||
|
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Popped (freed) the last costume for an actor.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Attempted to pop (free) a costume when the stack is empty!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +510,14 @@ void Actor::update() {
|
||||||
|
|
||||||
// Update lip synching
|
// Update lip synching
|
||||||
if (_lipSynch != NULL) {
|
if (_lipSynch != NULL) {
|
||||||
int posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str());
|
int posSound;
|
||||||
|
|
||||||
|
// While getPosIn60HzTicks will return "-1" to indicate that the
|
||||||
|
// sound is no longer playing, it is more appropriate to check first
|
||||||
|
if(g_imuse->getSoundStatus(_talkSoundName.c_str()))
|
||||||
|
posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str());
|
||||||
|
else
|
||||||
|
posSound = -1;
|
||||||
if (posSound != -1) {
|
if (posSound != -1) {
|
||||||
int anim = _lipSynch->getAnim(posSound);
|
int anim = _lipSynch->getAnim(posSound);
|
||||||
if (_talkAnim != anim) {
|
if (_talkAnim != anim) {
|
||||||
|
|
11
actor.h
11
actor.h
|
@ -54,7 +54,13 @@ public:
|
||||||
float roll() const { return _roll; }
|
float roll() const { return _roll; }
|
||||||
void setVisibility(bool val) { _visible = val; }
|
void setVisibility(bool val) { _visible = val; }
|
||||||
bool visible() const { return _visible; }
|
bool visible() const { return _visible; }
|
||||||
void putInSet(const char *name) { _setName = name; }
|
// Don't actually change the set immediately, see engine.cpp for details
|
||||||
|
void putInSet(const char *name) { _setNameTmp = name; }
|
||||||
|
void putInSet() {
|
||||||
|
if (_setName != _setNameTmp) {
|
||||||
|
_setName = _setNameTmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
void setTurnRate(float rate) { _turnRate = rate; }
|
void setTurnRate(float rate) { _turnRate = rate; }
|
||||||
float turnRate() const { return _turnRate; }
|
float turnRate() const { return _turnRate; }
|
||||||
void setWalkRate(float rate) { _walkRate = rate; }
|
void setWalkRate(float rate) { _walkRate = rate; }
|
||||||
|
@ -128,7 +134,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _name;
|
std::string _name;
|
||||||
std::string _setName;
|
std::string _setName; // The actual current set
|
||||||
|
std::string _setNameTmp; // The temporary name for the set
|
||||||
Color _talkColor;
|
Color _talkColor;
|
||||||
Vector3d _pos;
|
Vector3d _pos;
|
||||||
float _pitch, _yaw, _roll;
|
float _pitch, _yaw, _roll;
|
||||||
|
|
64
costume.cpp
64
costume.cpp
|
@ -565,7 +565,10 @@ public:
|
||||||
void setMapName(char *) { }
|
void setMapName(char *) { }
|
||||||
void setKey(int val);
|
void setKey(int val);
|
||||||
void reset();
|
void reset();
|
||||||
~SoundComponent() { }
|
~SoundComponent() {
|
||||||
|
// Stop the sound if it's in progress
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _soundName;
|
std::string _soundName;
|
||||||
|
@ -584,14 +587,12 @@ SoundComponent::SoundComponent(Costume::Component *parent, int parentID, const c
|
||||||
void SoundComponent::setKey(int val) {
|
void SoundComponent::setKey(int val) {
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case 0: // "Play"
|
case 0: // "Play"
|
||||||
if (!g_imuse->getSoundStatus(_soundName.c_str())) {
|
// No longer a need to check the sound status, if it's already playing
|
||||||
// g_imuse->stopSound(_soundName.c_str());
|
// then it will just use the existing handle
|
||||||
// } else {
|
g_imuse->startSfx(_soundName.c_str());
|
||||||
g_imuse->startSfx(_soundName.c_str());
|
if (g_engine->currScene() && g_currentUpdatedActor) {
|
||||||
if (g_engine->currScene() && g_currentUpdatedActor) {
|
Vector3d pos = g_currentUpdatedActor->pos();
|
||||||
Vector3d pos = g_currentUpdatedActor->pos();
|
g_engine->currScene()->setSoundPosition(_soundName.c_str(), pos);
|
||||||
g_engine->currScene()->setSoundPosition(_soundName.c_str(), pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // "Stop"
|
case 1: // "Stop"
|
||||||
|
@ -607,13 +608,15 @@ void SoundComponent::setKey(int val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundComponent::reset() {
|
void SoundComponent::reset() {
|
||||||
g_imuse->stopSound(_soundName.c_str());
|
// A lot of the sound components this gets called against aren't actually running
|
||||||
|
if(g_imuse->getSoundStatus(_soundName.c_str()))
|
||||||
|
g_imuse->stopSound(_soundName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Costume::Costume(const char *filename, const char *data, int len, Costume *prevCost) :
|
Costume::Costume(const char *filename, const char *data, int len, Costume *prevCost) :
|
||||||
_fname(filename), _colormap(DEFAULT_COLORMAP) {
|
_fname(filename), _colormap(DEFAULT_COLORMAP) {
|
||||||
TextSplitter ts(data, len);
|
TextSplitter ts(data, len);
|
||||||
|
|
||||||
ts.expectString("costume v0.1");
|
ts.expectString("costume v0.1");
|
||||||
ts.expectString("section tags");
|
ts.expectString("section tags");
|
||||||
int numTags;
|
int numTags;
|
||||||
|
@ -685,8 +688,11 @@ Costume::Costume(const char *filename, const char *data, int len, Costume *prevC
|
||||||
|
|
||||||
Costume::~Costume() {
|
Costume::~Costume() {
|
||||||
stopChores();
|
stopChores();
|
||||||
for (int i = _numComponents - 1; i >= 0; i--)
|
for (int i = _numComponents - 1; i >= 0; i--) {
|
||||||
delete _components[i];
|
// The "Sprite" component can be NULL
|
||||||
|
if (_components[i] != NULL)
|
||||||
|
delete _components[i];
|
||||||
|
}
|
||||||
delete[] _chores;
|
delete[] _chores;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,6 +701,7 @@ Costume::Component::Component(Component *parent, int parentID, char *tag) {
|
||||||
_cost = NULL;
|
_cost = NULL;
|
||||||
setParent(parent);
|
setParent(parent);
|
||||||
memcpy(_tag, tag, 4);
|
memcpy(_tag, tag, 4);
|
||||||
|
_tag[5] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Costume::Component::setParent(Component *newParent) {
|
void Costume::Component::setParent(Component *newParent) {
|
||||||
|
@ -833,6 +840,37 @@ Costume::Component *Costume::loadComponent (char tag[4], Costume::Component *par
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Model::HierNode *Costume::getModelNodes()
|
||||||
|
{
|
||||||
|
for(int i=0;i<_numComponents;i++) {
|
||||||
|
if (_components[i] == NULL)
|
||||||
|
continue;
|
||||||
|
// Needs to handle Main Models (pigeons) and normal Models
|
||||||
|
// (when Manny climbs the rope)
|
||||||
|
if (std::memcmp(_components[i]->tag(), "mmdl", 4) == 0)
|
||||||
|
return dynamic_cast<ModelComponent *>(_components[i])->hierarchy();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Costume::playChoreLooping(int num) {
|
||||||
|
if (num < 0 || num >= _numChores) {
|
||||||
|
if (debugLevel == DEBUG_CHORES || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Requested chore number %d is outside the range of chores (0-%d)!", num, _numChores);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_chores[num].playLooping();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Costume::playChore(int num) {
|
||||||
|
if (num < 0 || num >= _numChores) {
|
||||||
|
if (debugLevel == DEBUG_CHORES || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Requested chore number %d is outside the range of chores (0-%d)!", num, _numChores);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_chores[num].play();
|
||||||
|
}
|
||||||
|
|
||||||
void Costume::stopChores() {
|
void Costume::stopChores() {
|
||||||
for (int i = 0; i < _numChores; i++)
|
for (int i = 0; i < _numChores; i++)
|
||||||
_chores[i].stop();
|
_chores[i].stop();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define COSTUME_H
|
#define COSTUME_H
|
||||||
|
|
||||||
#include "matrix4.h"
|
#include "matrix4.h"
|
||||||
|
#include "model.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -32,13 +33,13 @@ public:
|
||||||
~Costume();
|
~Costume();
|
||||||
|
|
||||||
const char *filename() const { return _fname.c_str(); }
|
const char *filename() const { return _fname.c_str(); }
|
||||||
|
void playChore(int num);
|
||||||
void playChore(int num) { _chores[num].play(); }
|
void playChoreLooping(int num);
|
||||||
void playChoreLooping(int num) { _chores[num].playLooping(); }
|
|
||||||
void setChoreLastFrame(int num) { _chores[num].setLastFrame(); }
|
void setChoreLastFrame(int num) { _chores[num].setLastFrame(); }
|
||||||
void setChoreLooping(int num, bool val) { _chores[num].setLooping(val); }
|
void setChoreLooping(int num, bool val) { _chores[num].setLooping(val); }
|
||||||
void stopChore(int num) { _chores[num].stop(); }
|
void stopChore(int num) { _chores[num].stop(); }
|
||||||
char *getColormap() { return _colormap; }
|
char *getColormap() { return _colormap; }
|
||||||
|
Model::HierNode *getModelNodes();
|
||||||
void setColormap(char *map) {
|
void setColormap(char *map) {
|
||||||
_colormap = map;
|
_colormap = map;
|
||||||
for(int i=0;i<_numComponents;i++) {
|
for(int i=0;i<_numComponents;i++) {
|
||||||
|
@ -81,7 +82,7 @@ std::memcmp(_components[i]->tag(), "mat ", 4) == 0
|
||||||
virtual ~Component() { }
|
virtual ~Component() { }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char _tag[4];
|
char _tag[5];
|
||||||
int _parentID;
|
int _parentID;
|
||||||
Component *_parent, *_child, *_sibling;
|
Component *_parent, *_child, *_sibling;
|
||||||
Matrix4 _matrix;
|
Matrix4 _matrix;
|
||||||
|
|
5
debug.h
5
debug.h
|
@ -30,6 +30,7 @@ enum enDebugLevels {
|
||||||
DEBUG_MODEL,
|
DEBUG_MODEL,
|
||||||
DEBUG_STUB,
|
DEBUG_STUB,
|
||||||
DEBUG_SMUSH,
|
DEBUG_SMUSH,
|
||||||
|
DEBUG_IMUSE,
|
||||||
DEBUG_CHORES,
|
DEBUG_CHORES,
|
||||||
DEBUG_ALL
|
DEBUG_ALL
|
||||||
};
|
};
|
||||||
|
@ -44,6 +45,7 @@ static const char *debug_levels[] = {
|
||||||
"MODEL",
|
"MODEL",
|
||||||
"STUB",
|
"STUB",
|
||||||
"SMUSH",
|
"SMUSH",
|
||||||
|
"IMUSE",
|
||||||
"CHORE",
|
"CHORE",
|
||||||
"ALL"
|
"ALL"
|
||||||
};
|
};
|
||||||
|
@ -56,7 +58,8 @@ static const char *debug_descriptions[] = {
|
||||||
"Bitmap debug messages will be printed",
|
"Bitmap debug messages will be printed",
|
||||||
"Model debug messages will be printed",
|
"Model debug messages will be printed",
|
||||||
"Stub (missing function) debug messages will be printed",
|
"Stub (missing function) debug messages will be printed",
|
||||||
"SMUSH debug messages will be printed",
|
"SMUSH (video) debug messages will be printed",
|
||||||
|
"IMUSE (audio) debug messages will be printed",
|
||||||
"Chore debug messages will be printed",
|
"Chore debug messages will be printed",
|
||||||
"All debug messages will be printed",
|
"All debug messages will be printed",
|
||||||
};
|
};
|
||||||
|
|
|
@ -368,8 +368,13 @@ void DriverGL::drawBitmap(const Bitmap *bitmap) {
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
} else if (bitmap->_format == 5) { // ZBuffer image
|
} else if (bitmap->_format == 5) { // ZBuffer image
|
||||||
// Only draw the manual zbuffer when enabled
|
// Only draw the manual zbuffer when enabled
|
||||||
if (ZBUFFER_GLOBAL)
|
if (ZBUFFER_GLOBAL) {
|
||||||
drawDepthBitmap(bitmap->_x, bitmap->_y, bitmap->_width, bitmap->_height, bitmap->_data[bitmap->_currImage - 1]);
|
if (bitmap->_currImage - 1 < bitmap->_numImages) {
|
||||||
|
drawDepthBitmap(bitmap->_x, bitmap->_y, bitmap->_width, bitmap->_height, bitmap->_data[bitmap->_currImage - 1]);
|
||||||
|
} else {
|
||||||
|
warning("zbuffer image has index out of bounds! %d/%d\n", bitmap->_currImage, bitmap->_numImages);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
glEnable(GL_LIGHTING);
|
glEnable(GL_LIGHTING);
|
||||||
}
|
}
|
||||||
|
@ -508,7 +513,6 @@ void DriverGL::drawSmushFrame(int offsetX, int offsetY) {
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
// draw
|
// draw
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glDepthMask(GL_FALSE);
|
glDepthMask(GL_FALSE);
|
||||||
|
|
131
engine.cpp
131
engine.cpp
|
@ -92,6 +92,10 @@ Engine::Engine() :
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::handleButton(int operation, int key) {
|
void Engine::handleButton(int operation, int key) {
|
||||||
|
// If we're not supposed to handle the key then don't
|
||||||
|
if (!_controlsEnabled[key])
|
||||||
|
return;
|
||||||
|
|
||||||
lua_beginblock();
|
lua_beginblock();
|
||||||
lua_Object menu = getEventHandler("menuHandler");
|
lua_Object menu = getEventHandler("menuHandler");
|
||||||
if (menu != LUA_NOOBJECT && !lua_isnil(menu)) {
|
if (menu != LUA_NOOBJECT && !lua_isnil(menu)) {
|
||||||
|
@ -139,27 +143,75 @@ void Engine::mainLoop() {
|
||||||
// Process events
|
// Process events
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_KEYDOWN && _controlsEnabled[event.key.keysym.sym])
|
// Handle any button operations
|
||||||
handleButton(SDL_KEYDOWN, event.key.keysym.sym);
|
if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
|
||||||
if (event.type == SDL_KEYUP && _controlsEnabled[event.key.keysym.sym]) {
|
handleButton(event.type, event.key.keysym.sym);
|
||||||
handleButton(SDL_KEYUP, event.key.keysym.sym);
|
// Check for "Hard" quit"
|
||||||
}
|
if (event.type == SDL_QUIT)
|
||||||
if (event.type == SDL_QUIT) {
|
return;
|
||||||
lua_beginblock();
|
|
||||||
lua_Object handler = getEventHandler("exitHandler");
|
|
||||||
if (handler != LUA_NOOBJECT)
|
|
||||||
lua_callfunction(handler);
|
|
||||||
lua_endblock();
|
|
||||||
}
|
|
||||||
if (event.type == SDL_KEYDOWN) {
|
if (event.type == SDL_KEYDOWN) {
|
||||||
if (event.key.keysym.sym == SDLK_z)
|
if (event.key.keysym.sym == SDLK_z
|
||||||
g_resourceloader->loadKeyframe("ma_card_hold.key");
|
&& (event.key.keysym.mod & KMOD_CTRL)) {
|
||||||
|
void *resource;
|
||||||
|
int c, i = 0;
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
// Tool for debugging the loading of a particular resource without
|
||||||
|
// having to actually make it all the way to it in the game
|
||||||
|
fprintf(stderr, "Enter resource to load (extension specifies type): ");
|
||||||
|
while (i < 512 && (c = fgetc(stdin)) != EOF && c != '\n')
|
||||||
|
buf[i++] = c;
|
||||||
|
buf[i] = '\0';
|
||||||
|
if (strncmp(buf, "exp:", 4) == 0)
|
||||||
|
// Export a resource in order to view it directly
|
||||||
|
resource = (void *) g_resourceloader->exportResource(&buf[4]);
|
||||||
|
else if (strstr(buf, ".key"))
|
||||||
|
resource = (void *) g_resourceloader->loadKeyframe(buf);
|
||||||
|
else if (strstr(buf, ".zbm") || strstr(buf, ".bm"))
|
||||||
|
resource = (void *) g_resourceloader->loadBitmap(buf);
|
||||||
|
else if (strstr(buf, ".cmp"))
|
||||||
|
resource = (void *) g_resourceloader->loadColormap(buf);
|
||||||
|
else if (strstr(buf, ".cos"))
|
||||||
|
resource = (void *) g_resourceloader->loadCostume(buf, NULL);
|
||||||
|
else if (strstr(buf, ".lip"))
|
||||||
|
resource = (void *) g_resourceloader->loadLipSynch(buf);
|
||||||
|
else if (strstr(buf, ".snm"))
|
||||||
|
resource = (void *) g_smush->play(buf, 0, 0);
|
||||||
|
else if (strstr(buf, ".wav") || strstr(buf, ".imu")) {
|
||||||
|
g_imuse->startSfx(buf);
|
||||||
|
resource = (void *) 1;
|
||||||
|
} else if (strstr(buf, ".mat")) {
|
||||||
|
CMap *cmap = g_resourceloader->loadColormap("item.cmp");
|
||||||
|
|
||||||
|
warning("Default colormap applied to resources loaded in this fashion!");
|
||||||
|
resource = (void *) g_resourceloader->loadMaterial(buf, *cmap);
|
||||||
|
} else {
|
||||||
|
warning("Resource type not understood!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (resource == NULL)
|
||||||
|
warning("Requested resouce (%s) not found!");
|
||||||
|
}
|
||||||
if ((event.key.keysym.sym == SDLK_RETURN ||
|
if ((event.key.keysym.sym == SDLK_RETURN ||
|
||||||
event.key.keysym.sym == SDLK_KP_ENTER) &&
|
event.key.keysym.sym == SDLK_KP_ENTER) &&
|
||||||
(event.key.keysym.mod & KMOD_ALT))
|
(event.key.keysym.mod & KMOD_ALT)) {
|
||||||
g_driver->toggleFullscreenMode();
|
g_driver->toggleFullscreenMode();
|
||||||
if (event.key.keysym.sym == SDLK_q)
|
}
|
||||||
return;
|
if (event.key.keysym.sym == SDLK_q) {
|
||||||
|
lua_Object menu = getEventHandler("menuHandler");
|
||||||
|
|
||||||
|
// Can't handle the exit menu on top of the normal one
|
||||||
|
// at this time, so exit flat-out if we're in a menu
|
||||||
|
if (lua_isnil(menu)) {
|
||||||
|
printf("NOTICE: The left/right arrow keys do not update the quit screen at this time, use Y/N keys instead!\n");
|
||||||
|
lua_beginblock();
|
||||||
|
lua_Object handler = getEventHandler("exitHandler");
|
||||||
|
if (handler != LUA_NOOBJECT)
|
||||||
|
lua_callfunction(handler);
|
||||||
|
lua_endblock();
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +222,13 @@ void Engine::mainLoop() {
|
||||||
savegameSave();
|
savegameSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It appears that the lua tasks should run before rendering,
|
||||||
|
// if you watch the game loading screen you'll see that it
|
||||||
|
// turns out better to run this beforehand
|
||||||
|
if (!_menuMode)
|
||||||
|
// Run asynchronous tasks
|
||||||
|
lua_runtasks();
|
||||||
|
|
||||||
if (_mode == ENGINE_MODE_SMUSH) {
|
if (_mode == ENGINE_MODE_SMUSH) {
|
||||||
if (g_smush->isPlaying()) {
|
if (g_smush->isPlaying()) {
|
||||||
_movieTime = g_smush->getMovieTime();
|
_movieTime = g_smush->getMovieTime();
|
||||||
|
@ -186,9 +245,16 @@ void Engine::mainLoop() {
|
||||||
} else if (_mode == ENGINE_MODE_NORMAL) {
|
} else if (_mode == ENGINE_MODE_NORMAL) {
|
||||||
g_driver->clearScreen();
|
g_driver->clearScreen();
|
||||||
|
|
||||||
// Update actor costumes
|
// Update actor costumes & sets
|
||||||
for (ActorListType::iterator i = _actors.begin(); i != _actors.end(); i++) {
|
for (ActorListType::iterator i = _actors.begin(); i != _actors.end(); i++) {
|
||||||
Actor *a = *i;
|
Actor *a = *i;
|
||||||
|
|
||||||
|
// Activate the new set
|
||||||
|
// While this doesn't seem to affect anything this should be done here
|
||||||
|
// instead of inside the lua_runtasks loop, otherwise certain functions
|
||||||
|
// may request a set that was just deactivated
|
||||||
|
a->putInSet();
|
||||||
|
// Update the actor's costumes
|
||||||
g_currentUpdatedActor = *i;
|
g_currentUpdatedActor = *i;
|
||||||
if (_currScene != NULL && a->inSet(_currScene->name()) && a->visible())
|
if (_currScene != NULL && a->inSet(_currScene->name()) && a->visible())
|
||||||
a->update();
|
a->update();
|
||||||
|
@ -201,6 +267,10 @@ void Engine::mainLoop() {
|
||||||
|
|
||||||
// Draw underlying scene components
|
// Draw underlying scene components
|
||||||
if (_currScene != NULL) {
|
if (_currScene != NULL) {
|
||||||
|
// Background objects are drawn underneath everything except the background
|
||||||
|
// There are a bunch of these, especially in the tube-switcher room
|
||||||
|
_currScene->drawBitmaps(ObjectState::OBJSTATE_BACKGROUND);
|
||||||
|
// Underlay objects are just above the background
|
||||||
_currScene->drawBitmaps(ObjectState::OBJSTATE_UNDERLAY);
|
_currScene->drawBitmaps(ObjectState::OBJSTATE_UNDERLAY);
|
||||||
// State objects are drawn on top of other things, such as the flag
|
// State objects are drawn on top of other things, such as the flag
|
||||||
// on Manny's message tube
|
// on Manny's message tube
|
||||||
|
@ -301,10 +371,6 @@ void Engine::mainLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_menuMode)
|
|
||||||
// Run asynchronous tasks
|
|
||||||
lua_runtasks();
|
|
||||||
|
|
||||||
if (g_imuseState != -1) {
|
if (g_imuseState != -1) {
|
||||||
g_imuse->setMusicState(g_imuseState);
|
g_imuse->setMusicState(g_imuseState);
|
||||||
g_imuseState = -1;
|
g_imuseState = -1;
|
||||||
|
@ -474,6 +540,7 @@ void Engine::setSceneLock(const char *name, bool lockStatus) {
|
||||||
|
|
||||||
void Engine::setScene(const char *name) {
|
void Engine::setScene(const char *name) {
|
||||||
Scene *scene = findScene(name);
|
Scene *scene = findScene(name);
|
||||||
|
Scene *lastScene = _currScene;
|
||||||
|
|
||||||
// If the scene already exists then use the existing data
|
// If the scene already exists then use the existing data
|
||||||
if (scene != NULL) {
|
if (scene != NULL) {
|
||||||
|
@ -483,23 +550,27 @@ void Engine::setScene(const char *name) {
|
||||||
Block *b = g_resourceloader->getFileBlock(name);
|
Block *b = g_resourceloader->getFileBlock(name);
|
||||||
if (b == NULL)
|
if (b == NULL)
|
||||||
warning("Could not find scene file %s\n", name);
|
warning("Could not find scene file %s\n", name);
|
||||||
if (_currScene != NULL && !_currScene->locked) {
|
|
||||||
removeScene(_currScene);
|
|
||||||
delete _currScene;
|
|
||||||
}
|
|
||||||
_currScene = new Scene(name, b->data(), b->len());
|
_currScene = new Scene(name, b->data(), b->len());
|
||||||
registerScene(_currScene);
|
registerScene(_currScene);
|
||||||
_currScene->setSoundParameters(20, 127);
|
_currScene->setSoundParameters(20, 127);
|
||||||
|
// should delete the old scene after creating the new one
|
||||||
|
if (lastScene != NULL && !lastScene->locked) {
|
||||||
|
removeScene(lastScene);
|
||||||
|
delete lastScene;
|
||||||
|
}
|
||||||
delete b;
|
delete b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setScene(Scene *scene) {
|
void Engine::setScene(Scene *scene) {
|
||||||
if (_currScene != NULL && !_currScene->locked) {
|
Scene *lastScene = _currScene;
|
||||||
removeScene(_currScene);
|
|
||||||
delete _currScene;
|
|
||||||
}
|
|
||||||
_currScene = scene;
|
_currScene = scene;
|
||||||
_currScene->setSoundParameters(20, 127);
|
_currScene->setSoundParameters(20, 127);
|
||||||
|
// should delete the old scene after setting the new one
|
||||||
|
if (lastScene != NULL && !lastScene->locked) {
|
||||||
|
removeScene(lastScene);
|
||||||
|
delete lastScene;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setTextSpeed(int speed) {
|
void Engine::setTextSpeed(int speed) {
|
||||||
|
|
10
engine.h
10
engine.h
|
@ -94,6 +94,7 @@ class Engine {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void setMode(int mode) { _mode = mode; }
|
void setMode(int mode) { _mode = mode; }
|
||||||
|
int getMode() { return _mode; }
|
||||||
void setSpeechMode(int mode) { _speechMode = mode; }
|
void setSpeechMode(int mode) { _speechMode = mode; }
|
||||||
int getSpeechMode() { return _speechMode; }
|
int getSpeechMode() { return _speechMode; }
|
||||||
|
|
||||||
|
@ -101,7 +102,14 @@ public:
|
||||||
unsigned frameStart() const { return _frameStart; }
|
unsigned frameStart() const { return _frameStart; }
|
||||||
unsigned frameTime() const { return _frameTime; }
|
unsigned frameTime() const { return _frameTime; }
|
||||||
|
|
||||||
float perSecond(float rate) const { return rate * _frameTime / 1000; }
|
float perSecond(float rate) const {
|
||||||
|
// The actor "Doug" at the Kitty races has no _turnRate set by default
|
||||||
|
// so we need to return some sort of time that's non-zero for a rate
|
||||||
|
// value of zero
|
||||||
|
if (rate == 0.0)
|
||||||
|
rate = 100.0;
|
||||||
|
return rate * _frameTime / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
int getTextSpeed() { return _textSpeed; }
|
int getTextSpeed() { return _textSpeed; }
|
||||||
void setTextSpeed(int speed);
|
void setTextSpeed(int speed);
|
||||||
|
|
27
font.cpp
27
font.cpp
|
@ -82,6 +82,33 @@ Font::~Font() {
|
||||||
free(_fontData);
|
free(_fontData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16 Font::getCharIndex(unsigned char c)
|
||||||
|
{
|
||||||
|
uint16 c2 = uint16(c);
|
||||||
|
|
||||||
|
// In order to ensure the correct character codes for
|
||||||
|
// accented characters it is necessary to check the
|
||||||
|
// requested code against the index of characters for
|
||||||
|
// the font. Previously, signed characters were
|
||||||
|
// causing the problem but it might be possible for
|
||||||
|
// an invalid character to be called for other reasons.
|
||||||
|
//
|
||||||
|
// Example: Without this fix when Manny greets Eva
|
||||||
|
// for the first time and he says "Buenos Días" the
|
||||||
|
// 'í' character will either show up as a different
|
||||||
|
// character or it crashes the game.
|
||||||
|
for (uint i = 0; i < _numChars; ++i) {
|
||||||
|
if (_charIndex[i] == c2)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("The requsted character (code 0x%x) does not correspond to anything in the font data!\n", c2);
|
||||||
|
// If we couldn't find the character then default to
|
||||||
|
// the first character in the font so that something
|
||||||
|
// gets loaded to prevent the game from crashing
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Hardcoded default font for GUI, etc
|
// Hardcoded default font for GUI, etc
|
||||||
const uint8 Font::emerFont[][13] = {
|
const uint8 Font::emerFont[][13] = {
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
|
13
font.h
13
font.h
|
@ -28,16 +28,17 @@ public:
|
||||||
Font(const char *filename, const char *data, int len);
|
Font(const char *filename, const char *data, int len);
|
||||||
~Font();
|
~Font();
|
||||||
|
|
||||||
int32 getCharWidth(char c) { return _charHeaders[_charIndex[c]].width; }
|
int32 getCharWidth(unsigned char c) { return _charHeaders[getCharIndex(c)].width; }
|
||||||
int32 getCharHeight(char c) { return _charHeaders[_charIndex[c]].height; }
|
int32 getCharHeight(unsigned char c) { return _charHeaders[getCharIndex(c)].height; }
|
||||||
int32 getCharLogicalWidth(char c) { return _charHeaders[_charIndex[c]].logicalWidth; }
|
int32 getCharLogicalWidth(unsigned char c) { return _charHeaders[getCharIndex(c)].logicalWidth; }
|
||||||
int32 getCharStartingCol(char c) { return _charHeaders[_charIndex[c]].startingCol; }
|
int32 getCharStartingCol(unsigned char c) { return _charHeaders[getCharIndex(c)].startingCol; }
|
||||||
int32 getCharStartingLine(char c) { return _charHeaders[_charIndex[c]].startingLine; }
|
int32 getCharStartingLine(unsigned char c) { return _charHeaders[getCharIndex(c)].startingLine; }
|
||||||
const byte *getCharData(char c) { return _fontData + (_charHeaders[_charIndex[c]].offset); }
|
const byte *getCharData(unsigned char c) { return _fontData + (_charHeaders[getCharIndex(c)].offset); }
|
||||||
|
|
||||||
static const uint8 Font::emerFont[][13];
|
static const uint8 Font::emerFont[][13];
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
uint16 getCharIndex(unsigned char c);
|
||||||
struct CharHeader {
|
struct CharHeader {
|
||||||
int32 offset;
|
int32 offset;
|
||||||
int32 unknown;
|
int32 unknown;
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
#include "imuse/imuse_sndmgr.h"
|
#include "imuse/imuse_sndmgr.h"
|
||||||
#include "imuse/imuse_mcmp_mgr.h"
|
#include "imuse/imuse_mcmp_mgr.h"
|
||||||
|
|
||||||
#define MAX_IMUSE_TRACKS 8
|
#include "imuse/limits.h"
|
||||||
#define MAX_IMUSE_FADETRACKS 8
|
|
||||||
|
|
||||||
struct ImuseTable {
|
struct ImuseTable {
|
||||||
byte opcode;
|
byte opcode;
|
||||||
|
@ -134,6 +133,7 @@ public:
|
||||||
int getGroupSfxVolume() { return _volSfx; }
|
int getGroupSfxVolume() { return _volSfx; }
|
||||||
int getGroupMusicVolume() { return _volMusic; }
|
int getGroupMusicVolume() { return _volMusic; }
|
||||||
|
|
||||||
|
Track *findTrack(const char *soundName);
|
||||||
void setPriority(const char *soundName, int priority);
|
void setPriority(const char *soundName, int priority);
|
||||||
void setVolume(const char *soundName, int volume);
|
void setVolume(const char *soundName, int volume);
|
||||||
int getVolume(const char *soundName);
|
int getVolume(const char *soundName);
|
||||||
|
@ -151,7 +151,7 @@ public:
|
||||||
void flushTracks();
|
void flushTracks();
|
||||||
bool isVoicePlaying();
|
bool isVoicePlaying();
|
||||||
char *getCurMusicSoundName();
|
char *getCurMusicSoundName();
|
||||||
bool getSoundStatus(const char *soundName) const;
|
bool getSoundStatus(const char *soundName);
|
||||||
int32 getPosIn60HzTicks(const char *soundName);
|
int32 getPosIn60HzTicks(const char *soundName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -105,12 +105,14 @@ void Imuse::playMusic(const ImuseTable *table, int atribPos, bool sequence) {
|
||||||
} else {
|
} else {
|
||||||
char *soundName = getCurMusicSoundName();
|
char *soundName = getCurMusicSoundName();
|
||||||
int pan;
|
int pan;
|
||||||
|
|
||||||
if (table->pan == 0)
|
if (table->pan == 0)
|
||||||
pan = 64;
|
pan = 64;
|
||||||
else
|
else
|
||||||
pan = table->pan;
|
pan = table->pan;
|
||||||
if ((table->opcode == 3) && (!sequence) && (strcmp(soundName, table->filename) == 0) &&
|
if (soundName != NULL && (table->opcode == 3) && (!sequence)
|
||||||
(table->atribPos != 0) && (table->atribPos == _stateMusicTable[_curMusicState].atribPos)) {
|
&& (strcmp(soundName, table->filename) == 0) && (table->atribPos != 0)
|
||||||
|
&& table->atribPos == _stateMusicTable[_curMusicState].atribPos) {
|
||||||
setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay);
|
setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay);
|
||||||
setFadePan(soundName, pan, table->fadeOut60TicksDelay);
|
setFadePan(soundName, pan, table->fadeOut60TicksDelay);
|
||||||
setHookId(soundName, hookId);
|
setHookId(soundName, hookId);
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include "imuse/imuse_sndmgr.h"
|
#include "imuse/imuse_sndmgr.h"
|
||||||
|
|
||||||
void Imuse::flushTracks() {
|
void Imuse::flushTracks() {
|
||||||
|
// flushTracks should not lock the stack since all the functions
|
||||||
|
// that call it already do (stopAllSounds, startSound)
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && track->readyToRemove) {
|
if (track->used && track->readyToRemove) {
|
||||||
|
@ -41,6 +43,7 @@ void Imuse::flushTracks() {
|
||||||
_sound->closeSound(track->soundHandle);
|
_sound->closeSound(track->soundHandle);
|
||||||
track->soundHandle = NULL;
|
track->soundHandle = NULL;
|
||||||
track->used = false;
|
track->used = false;
|
||||||
|
strcpy(track->soundName, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +52,8 @@ void Imuse::flushTracks() {
|
||||||
|
|
||||||
void Imuse::refreshScripts() {
|
void Imuse::refreshScripts() {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
StackLock lock(_mutex);
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
||||||
|
@ -74,18 +79,25 @@ void Imuse::startSfx(const char *soundName, int priority) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 Imuse::getPosIn60HzTicks(const char *soundName) {
|
int32 Imuse::getPosIn60HzTicks(const char *soundName) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *getTrack = NULL;
|
||||||
Track *track = _track[l];
|
|
||||||
if (track->handle.isActive() && (strcmp(track->soundName, soundName) == 0)) {
|
getTrack = findTrack(soundName);
|
||||||
int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->iteration / 12);
|
// Warn the user if the track was not found
|
||||||
return pos;
|
if (getTrack == NULL) {
|
||||||
}
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Music track '%s' could not be found to get ticks!", soundName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getTrack->handle.isActive()) {
|
||||||
|
int32 pos = (5 * (getTrack->dataOffset + getTrack->regionOffset)) / (getTrack->iteration / 12);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Imuse::isVoicePlaying() {
|
bool Imuse::isVoicePlaying() {
|
||||||
|
StackLock lock(_mutex);
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->volGroupId == IMUSE_VOLGRP_VOICE) {
|
if (track->volGroupId == IMUSE_VOLGRP_VOICE) {
|
||||||
|
@ -98,30 +110,45 @@ bool Imuse::isVoicePlaying() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Imuse::getSoundStatus(const char *soundName) const {
|
bool Imuse::getSoundStatus(const char *soundName) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *statusTrack = NULL;
|
||||||
Track *track = _track[l];
|
|
||||||
if (strcmp(track->soundName, soundName) == 0) {
|
// If there's no name then don't try to get the status!
|
||||||
if (track->handle.isActive()) {
|
if (strlen(soundName) == 0)
|
||||||
return true;
|
return false;
|
||||||
}
|
|
||||||
}
|
statusTrack = findTrack(soundName);
|
||||||
|
// Warn the user if the track was not found
|
||||||
|
if (statusTrack == NULL) {
|
||||||
|
// This debug warning should be "light" since this function gets called
|
||||||
|
// on occassion to see if a sound has stopped yet
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Music track '%s' could not be found to get status, assume inactive.\n", soundName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return statusTrack->handle.isActive();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::stopSound(const char *soundName) {
|
void Imuse::stopSound(const char *soundName) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *removeTrack = NULL;
|
||||||
Track *track = _track[l];
|
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
removeTrack = findTrack(soundName);
|
||||||
track->toBeRemoved = true;
|
// Warn the user if the track was not found
|
||||||
}
|
if (removeTrack == NULL) {
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Music track '%s' could not be found to stop!", soundName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
removeTrack->toBeRemoved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::stopAllSounds() {
|
void Imuse::stopAllSounds() {
|
||||||
for(;;) {
|
int i;
|
||||||
|
StackLock lock(_mutex);
|
||||||
|
// Make 5 attempts to close out all the sounds before failing
|
||||||
|
// At this time it is inappropriate to say we are stable enough
|
||||||
|
// to just let this run forever
|
||||||
|
for(i = 5; i > 0; i--) {
|
||||||
bool foundNotRemoved = false;
|
bool foundNotRemoved = false;
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
|
@ -135,6 +162,10 @@ void Imuse::stopAllSounds() {
|
||||||
flushTracks();
|
flushTracks();
|
||||||
SDL_Delay(50);
|
SDL_Delay(50);
|
||||||
}
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Imuse::stopAllSounds() did not stop everything!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::pause(bool p) {
|
void Imuse::pause(bool p) {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "mixer/mixer.h"
|
#include "mixer/mixer.h"
|
||||||
#include "mixer/audiostream.h"
|
#include "mixer/audiostream.h"
|
||||||
|
#include "imuse/limits.h"
|
||||||
|
|
||||||
class McmpMgr;
|
class McmpMgr;
|
||||||
class Block;
|
class Block;
|
||||||
|
@ -32,11 +33,18 @@ class Block;
|
||||||
class ImuseSndMgr {
|
class ImuseSndMgr {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
#define MAX_IMUSE_SOUNDS 16
|
// MAX_IMUSE_SOUNDS should not be hardcoded, it should represent
|
||||||
|
// the maximum number of different tracks, see limits.h
|
||||||
|
#define MAX_IMUSE_SOUNDS MAX_IMUSE_TRACKS+MAX_IMUSE_FADETRACKS
|
||||||
|
|
||||||
#define IMUSE_VOLGRP_VOICE 1
|
// The numbering below fixes talking to Domino in his office
|
||||||
#define IMUSE_VOLGRP_SFX 2
|
// and it also allows Manny to get the info for Mercedes
|
||||||
#define IMUSE_VOLGRP_MUSIC 3
|
// Colomar, without this the game hangs at these points!
|
||||||
|
#define IMUSE_VOLGRP_BGND 0
|
||||||
|
#define IMUSE_VOLGRP_ACTION 1
|
||||||
|
#define IMUSE_VOLGRP_SFX 2
|
||||||
|
#define IMUSE_VOLGRP_MUSIC 3
|
||||||
|
#define IMUSE_VOLGRP_VOICE 4
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Region {
|
struct Region {
|
||||||
|
|
|
@ -65,7 +65,7 @@ ImuseTable grimStateMusicTable[] = {
|
||||||
{2, 1083, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"},
|
{2, 1083, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"},
|
||||||
{2, 1084, 0, 0, 60, 80, 0, "1084 - Foggy Cactus.IMC"},
|
{2, 1084, 0, 0, 60, 80, 0, "1084 - Foggy Cactus.IMC"},
|
||||||
{2, 1085, 0, 0, 60, 105, 0, "1085 - Rubamat Exterior.IMC"},
|
{2, 1085, 0, 0, 60, 105, 0, "1085 - Rubamat Exterior.IMC"},
|
||||||
{2, 1086, 0, 4, 60, 80, 30, "1086 - Blue Hector.IMC"},
|
{2, 1086, 0, 4, 60, 80, 30, "1087 - Blue Hector.IMC"},
|
||||||
{2, 1100, 0, 0, 60, 127, 0, "1109 - Cafe Exterior.IMC"},
|
{2, 1100, 0, 0, 60, 127, 0, "1109 - Cafe Exterior.IMC"},
|
||||||
{3, 1101, 45, 5, 24, 60, 0, "1101 - Cafe Office.IMC"},
|
{3, 1101, 45, 5, 24, 60, 0, "1101 - Cafe Office.IMC"},
|
||||||
{3, 1102, 45, 0, 24, 127, 0, "1102 - Cafe Intercom.IMC"},
|
{3, 1102, 45, 0, 24, 127, 0, "1102 - Cafe Intercom.IMC"},
|
||||||
|
@ -105,8 +105,8 @@ ImuseTable grimStateMusicTable[] = {
|
||||||
{2, 1147, 0, 0, 60, 127, 0, "1147 - LOL Security Int.IMC"},
|
{2, 1147, 0, 0, 60, 127, 0, "1147 - LOL Security Int.IMC"},
|
||||||
{2, 1148, 0, 0, 60, 127, 0, "1148 - Carla's Life.IMC"},
|
{2, 1148, 0, 0, 60, 127, 0, "1148 - Carla's Life.IMC"},
|
||||||
{2, 1149, 0, 0, 24, 127, 0, "1149 - Bomb.IMC"},
|
{2, 1149, 0, 0, 24, 127, 0, "1149 - Bomb.IMC"},
|
||||||
{3, 1150, 83, 0, 60, 105, 0, "1150 - Track Stairs.IMC"},
|
{3, 1150, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
|
||||||
{3, 1151, 83, 0, 60, 105, 0, "1151 - Track Stairs.IMC"},
|
{3, 1151, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
|
||||||
{3, 1152, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
|
{3, 1152, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
|
||||||
{2, 1153, 0, 0, 24, 127, 0, "1153 - Track Base.IMC"},
|
{2, 1153, 0, 0, 24, 127, 0, "1153 - Track Base.IMC"},
|
||||||
{2, 1154, 0, 0, 60, 127, 0, "1154 - Kitty Hall.IMC"},
|
{2, 1154, 0, 0, 60, 127, 0, "1154 - Kitty Hall.IMC"},
|
||||||
|
|
|
@ -28,50 +28,70 @@
|
||||||
int Imuse::allocSlot(int priority) {
|
int Imuse::allocSlot(int priority) {
|
||||||
int l, lowest_priority = 127;
|
int l, lowest_priority = 127;
|
||||||
int trackId = -1;
|
int trackId = -1;
|
||||||
|
|
||||||
|
// allocSlot called by startSound so no locking is necessary
|
||||||
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
if (!_track[l]->used) {
|
if (!_track[l]->used) {
|
||||||
trackId = l;
|
return l; // Found an unused track
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackId == -1) {
|
warning("Imuse::startSound(): All slots are full");
|
||||||
warning("Imuse::startSound(): All slots are full");
|
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *track = _track[l];
|
||||||
Track *track = _track[l];
|
if (track->used && !track->toBeRemoved && lowest_priority > track->priority) {
|
||||||
if (track->used && !track->toBeRemoved && lowest_priority > track->priority) {
|
lowest_priority = track->priority;
|
||||||
lowest_priority = track->priority;
|
trackId = l;
|
||||||
trackId = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lowest_priority <= priority) {
|
|
||||||
assert(trackId != -1);
|
|
||||||
_track[trackId]->toBeRemoved = true;
|
|
||||||
warning("Imuse::startSound(): Removed sound %s from track %d", _track[trackId]->soundName, trackId);
|
|
||||||
} else {
|
|
||||||
warning("Imuse::startSound(): Priority sound too low");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (lowest_priority <= priority) {
|
||||||
|
assert(trackId != -1);
|
||||||
|
_track[trackId]->toBeRemoved = true;
|
||||||
|
warning("Imuse::startSound(): Removed sound %s from track %d", _track[trackId]->soundName, trackId);
|
||||||
|
} else {
|
||||||
|
warning("Imuse::startSound(): Priority sound too low");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return trackId;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority) {
|
bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority) {
|
||||||
int l = allocSlot(priority);
|
Track *track = NULL;
|
||||||
|
int i, l = -1;
|
||||||
|
|
||||||
|
StackLock lock(_mutex);
|
||||||
|
// If the track is already playing then there is absolutely no
|
||||||
|
// reason to start it again, the existing track should be modified
|
||||||
|
// instead of starting a new copy of the track
|
||||||
|
for (i = 0; i < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; i++) {
|
||||||
|
// Filenames are case insensitive, see findTrack
|
||||||
|
if (!strcasecmp(_track[i]->soundName, soundName)) {
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Imuse::startSound(): Track '%s' already playing.\n", soundName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l = allocSlot(priority);
|
||||||
if (l == -1) {
|
if (l == -1) {
|
||||||
warning("Imuse::startSound() Can't start sound - no free slots");
|
warning("Imuse::startSound(): Can't start sound - no free slots");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
track = _track[l];
|
||||||
Track *track = _track[l];
|
i = 5;
|
||||||
while (track->used) {
|
// At this time it is inappropriate to assume that this will always
|
||||||
|
// succeed, so set a limit of 5 tries on running flushTracks
|
||||||
|
while (track->used && i > 0) {
|
||||||
// The designated track is not yet available. So, we call flushTracks()
|
// The designated track is not yet available. So, we call flushTracks()
|
||||||
// to get it processed (and thus made ready for us). Since the actual
|
// to get it processed (and thus made ready for us). Since the actual
|
||||||
// processing is done by another thread, we also call parseEvents to
|
// processing is done by another thread, we also call parseEvents to
|
||||||
// give it some time (and to avoid busy waiting/looping).
|
// give it some time (and to avoid busy waiting/looping).
|
||||||
flushTracks();
|
flushTracks();
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("Imuse::startSound(): flushTracks was unnable to free up a track!");
|
||||||
}
|
}
|
||||||
|
|
||||||
track->pan = pan * 1000;
|
track->pan = pan * 1000;
|
||||||
|
@ -142,59 +162,82 @@ bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int vo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setPriority(const char *soundName, int priority) {
|
Imuse::Track *Imuse::findTrack(const char *soundName) {
|
||||||
assert ((priority >= 0) && (priority <= 127));
|
StackLock lock(_mutex);
|
||||||
|
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
|
||||||
track->priority = priority;
|
// Since the audio (at least for Eva's keystrokes) can be referenced
|
||||||
|
// two ways: keyboard.IMU and keyboard.imu, make a case insensitive
|
||||||
|
// search for the track to make sure we can find it
|
||||||
|
if (strcasecmp(track->soundName, soundName) == 0) {
|
||||||
|
return track;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Imuse::setPriority(const char *soundName, int priority) {
|
||||||
|
Track *changeTrack = NULL;
|
||||||
|
assert ((priority >= 0) && (priority <= 127));
|
||||||
|
|
||||||
|
changeTrack = findTrack(soundName);
|
||||||
|
// Check to make sure we found the track
|
||||||
|
if (changeTrack == NULL) {
|
||||||
|
warning("Unable to find track '%s' to change priority!", soundName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeTrack->priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setVolume(const char *soundName, int volume) {
|
void Imuse::setVolume(const char *soundName, int volume) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *changeTrack;
|
||||||
Track *track = _track[l];
|
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
changeTrack = findTrack(soundName);
|
||||||
track->vol = volume * 1000;
|
if (changeTrack == NULL) {
|
||||||
}
|
warning("Unable to find track '%s' to change volume!", soundName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
changeTrack->vol = volume * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setPan(const char *soundName, int pan) {
|
void Imuse::setPan(const char *soundName, int pan) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *changeTrack;
|
||||||
Track *track = _track[l];
|
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
changeTrack = findTrack(soundName);
|
||||||
track->pan = pan;
|
if (changeTrack == NULL) {
|
||||||
}
|
warning("Unable to find track '%s' to change volume!", soundName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
changeTrack->pan = pan;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Imuse::getVolume(const char *soundName) {
|
int Imuse::getVolume(const char *soundName) {
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
Track *getTrack;
|
||||||
Track *track = _track[l];
|
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
getTrack = findTrack(soundName);
|
||||||
return track->vol / 1000;
|
if (getTrack == NULL) {
|
||||||
}
|
warning("Unable to find track '%s' to get volume!", soundName);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
return getTrack->vol / 1000;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setHookId(const char *soundName, int hookId) {
|
void Imuse::setHookId(const char *soundName, int hookId) {
|
||||||
StackLock lock(_mutex);
|
Track *changeTrack;
|
||||||
|
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
changeTrack = findTrack(soundName);
|
||||||
Track *track = _track[l];
|
if (changeTrack == NULL) {
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
warning("Unable to find track '%s' to change hook id!", soundName);
|
||||||
track->curHookId = hookId;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
changeTrack->curHookId = hookId;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Imuse::getCountPlayedTracks(const char *soundName) {
|
int Imuse::getCountPlayedTracks(const char *soundName) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
StackLock lock(_mutex);
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
||||||
|
@ -206,59 +249,61 @@ int Imuse::getCountPlayedTracks(const char *soundName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) {
|
void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) {
|
||||||
|
Track *changeTrack;
|
||||||
assert((volGroupId >= 1) && (volGroupId <= 4));
|
assert((volGroupId >= 1) && (volGroupId <= 4));
|
||||||
|
|
||||||
if (volGroupId == 4)
|
if (volGroupId == 4)
|
||||||
volGroupId = 3;
|
volGroupId = 3;
|
||||||
|
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
changeTrack = findTrack(soundName);
|
||||||
Track *track = _track[l];
|
if (changeTrack == NULL) {
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
warning("Unable to find track '%s' to change volume group id!", soundName);
|
||||||
track->volGroupId = volGroupId;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
changeTrack->volGroupId = volGroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) {
|
void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) {
|
||||||
StackLock lock(_mutex);
|
Track *changeTrack;
|
||||||
|
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
changeTrack = findTrack(soundName);
|
||||||
Track *track = _track[l];
|
if (changeTrack == NULL) {
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
warning("Unable to find track '%s' to change fade volume!", soundName);
|
||||||
track->volFadeDelay = duration;
|
return;
|
||||||
track->volFadeDest = destVolume * 1000;
|
|
||||||
track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * duration);
|
|
||||||
track->volFadeUsed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
changeTrack->volFadeDelay = duration;
|
||||||
|
changeTrack->volFadeDest = destVolume * 1000;
|
||||||
|
changeTrack->volFadeStep = (changeTrack->volFadeDest - changeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * duration);
|
||||||
|
changeTrack->volFadeUsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::setFadePan(const char *soundName, int destPan, int duration) {
|
void Imuse::setFadePan(const char *soundName, int destPan, int duration) {
|
||||||
StackLock lock(_mutex);
|
Track *changeTrack;
|
||||||
|
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
changeTrack = findTrack(soundName);
|
||||||
Track *track = _track[l];
|
if (changeTrack == NULL) {
|
||||||
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
|
warning("Unable to find track '%s' to change fade pan!", soundName);
|
||||||
track->panFadeDelay = duration;
|
return;
|
||||||
track->panFadeDest = destPan * 1000;
|
|
||||||
track->panFadeStep = (track->panFadeDest - track->pan) * 60 * (1000 / _callbackFps) / (1000 * duration);
|
|
||||||
track->panFadeUsed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
changeTrack->panFadeDelay = duration;
|
||||||
|
changeTrack->panFadeDest = destPan * 1000;
|
||||||
|
changeTrack->panFadeStep = (changeTrack->panFadeDest - changeTrack->pan) * 60 * (1000 / _callbackFps) / (1000 * duration);
|
||||||
|
changeTrack->panFadeUsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *Imuse::getCurMusicSoundName() {
|
char *Imuse::getCurMusicSoundName() {
|
||||||
|
StackLock lock(_mutex);
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
||||||
return track->soundName;
|
return track->soundName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Imuse::fadeOutMusic(int duration) {
|
void Imuse::fadeOutMusic(int duration) {
|
||||||
|
StackLock lock(_mutex);
|
||||||
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
|
||||||
Track *track = _track[l];
|
Track *track = _track[l];
|
||||||
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
|
||||||
|
|
25
imuse/limits.h
Normal file
25
imuse/limits.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||||
|
// Copyright (C) 2003-2005 The ScummVM-Residual Team (www.scummvm.org)
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
#ifndef IMUSE_LIMITS_H
|
||||||
|
#define IMUSE_LIMITS_H
|
||||||
|
|
||||||
|
//#define MAX_IMUSE_TRACKS 8
|
||||||
|
#define MAX_IMUSE_TRACKS 20
|
||||||
|
#define MAX_IMUSE_FADETRACKS 8
|
||||||
|
|
||||||
|
#endif
|
56
keyframe.cpp
56
keyframe.cpp
|
@ -35,11 +35,28 @@ KeyframeAnim::KeyframeAnim(const char *filename, const char *data, int len) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeAnim::loadBinary(const char *data, int len) {
|
void KeyframeAnim::loadBinary(const char *data, int len) {
|
||||||
|
// First four bytes are the FYEK Keyframe identifier code
|
||||||
|
// Next 36 bytes are the filename
|
||||||
|
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) {
|
||||||
|
char filebuf[37];
|
||||||
|
|
||||||
|
memcpy(filebuf, data + 4, 36);
|
||||||
|
filebuf[37] = 0;
|
||||||
|
printf("Loading Keyframe '%s'.\n", filebuf);
|
||||||
|
}
|
||||||
|
// Next four bytes are the flags
|
||||||
_flags = READ_LE_UINT32(data + 40);
|
_flags = READ_LE_UINT32(data + 40);
|
||||||
|
// Next four bytes are a duplicate of _numJoints (?)
|
||||||
|
// Next four bytes are the type
|
||||||
_type = READ_LE_UINT32(data + 48);
|
_type = READ_LE_UINT32(data + 48);
|
||||||
|
// Next four bytes are the frames per second
|
||||||
_fps = get_float(data + 52);
|
_fps = get_float(data + 52);
|
||||||
|
// Next four bytes are the number of frames
|
||||||
_numFrames = READ_LE_UINT32(data + 56);
|
_numFrames = READ_LE_UINT32(data + 56);
|
||||||
|
// Next four bytes are the number of joints
|
||||||
_numJoints = READ_LE_UINT32(data + 60);
|
_numJoints = READ_LE_UINT32(data + 60);
|
||||||
|
// Next four bytes are unknown (?)
|
||||||
|
// Next four bytes are the number of markers
|
||||||
_numMarkers = READ_LE_UINT32(data + 68);
|
_numMarkers = READ_LE_UINT32(data + 68);
|
||||||
_markers = new Marker[_numMarkers];
|
_markers = new Marker[_numMarkers];
|
||||||
for (int i = 0; i < _numMarkers; i++) {
|
for (int i = 0; i < _numMarkers; i++) {
|
||||||
|
@ -51,16 +68,22 @@ void KeyframeAnim::loadBinary(const char *data, int len) {
|
||||||
for (int i = 0; i < _numJoints; i++)
|
for (int i = 0; i < _numJoints; i++)
|
||||||
_nodes[i] = NULL;
|
_nodes[i] = NULL;
|
||||||
const char *dataEnd = data + len;
|
const char *dataEnd = data + len;
|
||||||
data += 180;
|
// The first 136 bytes are for the header, this was originally
|
||||||
|
// listed as 180 bytes since the first operation is usually a
|
||||||
|
// "null" key, however ma_card_hold.key showed that this is
|
||||||
|
// not always the case so we should not skip this operation
|
||||||
|
data += 136;
|
||||||
while (data < dataEnd) {
|
while (data < dataEnd) {
|
||||||
// ma_card_hold.key crashes at this part without checking
|
int nodeNum;
|
||||||
// to make sure nodeNum is valid, unfortunately I believe
|
// The first 32 bytes (of a keyframe) are the name handle
|
||||||
// whatever data we're losing from this file is what prevents
|
// The next four bytes are the node number identifier
|
||||||
// the game from continuing after Manny reads the message
|
nodeNum = READ_LE_UINT32(data + 32);
|
||||||
//
|
|
||||||
// TODO: Find out what really goes wrong when we read
|
// Because of the issue above ma_card_hold.key used to crash
|
||||||
// the data in ma_card_hold.key and fix it
|
// at this part without checking to make sure nodeNum is a
|
||||||
int nodeNum = READ_LE_UINT32(data + 32);
|
// valid number, we'll leave this in just in case something
|
||||||
|
// else is still wrong but it should now load correctly in
|
||||||
|
// all cases
|
||||||
if (nodeNum >= _numJoints) {
|
if (nodeNum >= _numJoints) {
|
||||||
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) {
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) {
|
||||||
warning("A node number was greater than the maximum number of nodes (%d/%d)", nodeNum, _numJoints);
|
warning("A node number was greater than the maximum number of nodes (%d/%d)", nodeNum, _numJoints);
|
||||||
|
@ -117,10 +140,10 @@ void KeyframeAnim::animate(Model::HierNode *nodes, float time, int priority1, in
|
||||||
if (frame > _numFrames)
|
if (frame > _numFrames)
|
||||||
frame = _numFrames;
|
frame = _numFrames;
|
||||||
|
|
||||||
for (int i = 0; i < _numJoints; i++)
|
for (int i = 0; i < _numJoints; i++) {
|
||||||
if (_nodes[i] != NULL)
|
if (_nodes[i] != NULL)
|
||||||
|
_nodes[i]->animate(nodes[i], frame, ((_type & nodes[i]._type) != 0 ? priority2 : priority1));
|
||||||
_nodes[i]->animate(nodes[i], frame, ((_type & nodes[i]._type) != 0 ? priority2 : priority1));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
|
void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
|
||||||
|
@ -138,13 +161,18 @@ void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) {
|
void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) {
|
||||||
std::memcpy(_meshName, data, 32);
|
// If the name handle is entirely null (like ma_rest.key)
|
||||||
|
// then we shouldn't try to set the name
|
||||||
|
if (READ_LE_UINT32(data) == 0)
|
||||||
|
std::memcpy(_meshName, "(null)", 32);
|
||||||
|
else
|
||||||
|
std::memcpy(_meshName, data, 32);
|
||||||
_numEntries = READ_LE_UINT32(data + 36);
|
_numEntries = READ_LE_UINT32(data + 36);
|
||||||
data += 44;
|
data += 44;
|
||||||
_entries = new KeyframeEntry[_numEntries];
|
_entries = new KeyframeEntry[_numEntries];
|
||||||
for (int i = 0; i < _numEntries; i++)
|
for (int i = 0; i < _numEntries; i++)
|
||||||
_entries[i].loadBinary(data);
|
_entries[i].loadBinary(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
|
void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
|
||||||
ts.scanString("mesh name %s", 1, _meshName);
|
ts.scanString("mesh name %s", 1, _meshName);
|
||||||
|
|
295
lua.cpp
295
lua.cpp
|
@ -139,9 +139,26 @@ static inline Bitmap *check_bitmapobject(int num) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int check_int(int num) {
|
static inline double check_double(int num) {
|
||||||
double val = luaL_check_number(num);
|
double val;
|
||||||
|
|
||||||
|
// Have found some instances, such as in Rubacava if you jump there,
|
||||||
|
// where doubles of "zero" are called as nil
|
||||||
|
if(lua_isnil(lua_getparam(num)))
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return luaL_check_number(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int check_int(int num) {
|
||||||
|
double val;
|
||||||
|
|
||||||
|
// Have found some instances, such as in Rubacava and the tube-switcher
|
||||||
|
// room, where integers of "zero" are called as nil
|
||||||
|
if(lua_isnil(lua_getparam(num)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
val = luaL_check_number(num);
|
||||||
return int(round(val));
|
return int(round(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +217,7 @@ static void PrintDebug() {
|
||||||
|
|
||||||
msg.insert(0, "Debug: ");
|
msg.insert(0, "Debug: ");
|
||||||
std::fputs(msg.c_str(), stderr);
|
std::fputs(msg.c_str(), stderr);
|
||||||
|
msg.append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,21 +524,13 @@ static void GetActorPos() {
|
||||||
|
|
||||||
static void SetActorRot() {
|
static void SetActorRot() {
|
||||||
float pitch, yaw, roll;
|
float pitch, yaw, roll;
|
||||||
lua_Object param3;
|
|
||||||
Actor *act;
|
Actor *act;
|
||||||
|
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
param3 = lua_getparam(3);
|
|
||||||
act = check_actor(1);
|
act = check_actor(1);
|
||||||
pitch = luaL_check_number(2);
|
pitch = check_double(2);
|
||||||
// param3 can be nil, the tube-switcher scene appears
|
yaw = check_double(3);
|
||||||
// to call SetActorRot will nil for param3 intentionally
|
roll = check_double(4);
|
||||||
if (lua_isnil(param3))
|
|
||||||
yaw = 0;
|
|
||||||
else
|
|
||||||
yaw = luaL_check_number(3);
|
|
||||||
|
|
||||||
roll = luaL_check_number(4);
|
|
||||||
if (getbool(5))
|
if (getbool(5))
|
||||||
act->turnTo(pitch, yaw, roll);
|
act->turnTo(pitch, yaw, roll);
|
||||||
else
|
else
|
||||||
|
@ -582,14 +592,18 @@ static void GetActorYawToPoint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PutActorInSet() {
|
static void PutActorInSet() {
|
||||||
const char *set = "";
|
|
||||||
Actor *act;
|
Actor *act;
|
||||||
|
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
act = check_actor(1);
|
act = check_actor(1);
|
||||||
if (!lua_isnil(lua_getparam(2)))
|
if (!lua_isnil(lua_getparam(2))) {
|
||||||
|
const char *set;
|
||||||
|
|
||||||
set = luaL_check_string(2);
|
set = luaL_check_string(2);
|
||||||
act->putInSet(set);
|
// Make sure the actor isn't already in the set
|
||||||
|
if (!act->inSet(set))
|
||||||
|
act->putInSet(set);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetActorWalkRate() {
|
static void SetActorWalkRate() {
|
||||||
|
@ -742,6 +756,64 @@ stubWarning("TurnActorTo");
|
||||||
static void PointActorAt() {
|
static void PointActorAt() {
|
||||||
stubWarning("PointActorAt");
|
stubWarning("PointActorAt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the location of one of the actor's nodes, this is
|
||||||
|
* used by Glottis to watch where Manny is located in
|
||||||
|
* order to hand him the work order. This function is
|
||||||
|
* also important for when Velasco hands Manny the logbook
|
||||||
|
* in Rubacava
|
||||||
|
*/
|
||||||
|
static void GetActorNodeLocation() {
|
||||||
|
Model::HierNode *allNodes;
|
||||||
|
Costume *c;
|
||||||
|
Actor *act;
|
||||||
|
int node;
|
||||||
|
|
||||||
|
// Should this actually do anything?
|
||||||
|
DEBUG_FUNCTION();
|
||||||
|
act = check_actor(1);
|
||||||
|
node = check_int(2);
|
||||||
|
c = act->currentCostume();
|
||||||
|
if (c == NULL) {
|
||||||
|
lua_pushnil();
|
||||||
|
lua_pushnil();
|
||||||
|
lua_pushnil();
|
||||||
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("GetActorNodeLocation() when actor has no costume (which means no nodes)!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
allNodes = c->getModelNodes();
|
||||||
|
if (allNodes == NULL) {
|
||||||
|
lua_pushnil();
|
||||||
|
lua_pushnil();
|
||||||
|
lua_pushnil();
|
||||||
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("GetActorNodeLocation() when actor has no nodes!");
|
||||||
|
}
|
||||||
|
lua_pushnumber(allNodes[node]._pos.x());
|
||||||
|
lua_pushnumber(allNodes[node]._pos.y());
|
||||||
|
lua_pushnumber(allNodes[node]._pos.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetActorWalkDominate() {
|
||||||
|
lua_Object param2;
|
||||||
|
Actor *act;
|
||||||
|
|
||||||
|
// Should this actually do anything?
|
||||||
|
DEBUG_FUNCTION();
|
||||||
|
act = check_actor(1);
|
||||||
|
if (act == NULL) {
|
||||||
|
lua_pushnil();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
param2 = lua_getparam(2);
|
||||||
|
if (lua_isnil(param2))
|
||||||
|
lua_pushnil();
|
||||||
|
else if (lua_isnumber(param2))
|
||||||
|
lua_pushnumber(lua_getnumber(param2));
|
||||||
|
else
|
||||||
|
warning("Unknown SetActorWalkDominate parameter!");
|
||||||
|
}
|
||||||
static void SetActorColormap() {
|
static void SetActorColormap() {
|
||||||
char *mapname;
|
char *mapname;
|
||||||
CMap *_cmap;
|
CMap *_cmap;
|
||||||
|
@ -794,10 +866,13 @@ static void GetActorCostume() {
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
act = check_actor(1);
|
act = check_actor(1);
|
||||||
c = act->currentCostume();
|
c = act->currentCostume();
|
||||||
if (c == NULL)
|
if (c == NULL) {
|
||||||
lua_pushnil();
|
lua_pushnil();
|
||||||
else
|
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
lua_pushstring(const_cast<char *>(c->filename()));
|
printf("GetActorCostume() on '%s' when actor has no costume!", act->name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lua_pushstring(const_cast<char *>(c->filename()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PopActorCostume() {
|
static void PopActorCostume() {
|
||||||
|
@ -957,9 +1032,6 @@ static void ActorLookAt() {
|
||||||
|
|
||||||
if (lua_isnumber(y))
|
if (lua_isnumber(y))
|
||||||
act->setLookAtRate(luaL_check_number(3));
|
act->setLookAtRate(luaL_check_number(3));
|
||||||
|
|
||||||
act->setLooking(true);
|
|
||||||
return;
|
|
||||||
} else if ( lua_isnumber(x)) { // look at xyz
|
} else if ( lua_isnumber(x)) { // look at xyz
|
||||||
Vector3d vector;
|
Vector3d vector;
|
||||||
float fX;
|
float fX;
|
||||||
|
@ -988,13 +1060,13 @@ static void ActorLookAt() {
|
||||||
|
|
||||||
if (lua_isnumber(y))
|
if (lua_isnumber(y))
|
||||||
act->setLookAtRate(luaL_check_number(3));
|
act->setLookAtRate(luaL_check_number(3));
|
||||||
} else if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
} else {
|
||||||
warning("ActorLookAt: Don't know what to look at!");
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
warning("ActorLookAt: Don't know what to look at!");
|
||||||
|
}
|
||||||
|
|
||||||
act->setLooking(true);
|
act->setLooking(true);
|
||||||
// Fixes random bug when changing scenes (?) after we've done a jump
|
lua_pushnumber(1);
|
||||||
// (this function is close to the failure point)
|
|
||||||
lua_pushnil();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetActorLookRate() {
|
static void SetActorLookRate() {
|
||||||
|
@ -1274,16 +1346,15 @@ static void IsActorInSector(void) {
|
||||||
numSectors = g_engine->currScene()->getSectorCount();
|
numSectors = g_engine->currScene()->getSectorCount();
|
||||||
for (i = 0; i < numSectors; i++) {
|
for (i = 0; i < numSectors; i++) {
|
||||||
Sector *sector = g_engine->currScene()->getSectorBase(i);
|
Sector *sector = g_engine->currScene()->getSectorBase(i);
|
||||||
|
|
||||||
if (sector->visible() && strmatch(sector->name(), name)) {
|
if (sector->visible() && strmatch(sector->name(), name)) {
|
||||||
if (sector->isPointInSector(act->pos())) {
|
if (sector->isPointInSector(act->pos())) {
|
||||||
lua_pushnumber(sector->id());
|
lua_pushnumber(sector->id());
|
||||||
lua_pushstring((char *)sector->name());
|
lua_pushstring((char *)sector->name());
|
||||||
lua_pushnumber(sector->type());
|
lua_pushnumber(sector->type());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pushnil();
|
lua_pushnil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1431,9 +1502,13 @@ static void ImStartSound() {
|
||||||
soundName = luaL_check_string(1);
|
soundName = luaL_check_string(1);
|
||||||
priority = check_int(2);
|
priority = check_int(2);
|
||||||
group = check_int(3);
|
group = check_int(3);
|
||||||
|
|
||||||
|
// Start the sound with the appropriate settings
|
||||||
if (g_imuse->startSound(soundName, group, 0, 127, 0, priority)) {
|
if (g_imuse->startSound(soundName, group, 0, 127, 0, priority)) {
|
||||||
lua_pushstring(soundName);
|
lua_pushstring(soundName);
|
||||||
} else {
|
} else {
|
||||||
|
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL)
|
||||||
|
error("ImStartSound failed to start '%s'", soundName);
|
||||||
lua_pushnil();
|
lua_pushnil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1859,6 +1934,7 @@ void getTextObjectParams(TextObject *textObject, lua_Object table_obj) {
|
||||||
|
|
||||||
/* Clean the buffer of text objects and primitives
|
/* Clean the buffer of text objects and primitives
|
||||||
* this is known to be used when changing between menus
|
* this is known to be used when changing between menus
|
||||||
|
* and when loading some cutscenes
|
||||||
*/
|
*/
|
||||||
static void CleanBuffer() {
|
static void CleanBuffer() {
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
|
@ -1897,6 +1973,18 @@ char *itemText(lua_Object itemTable, int menuItem) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function sends the SDL signal to
|
||||||
|
* go ahead and exit the game
|
||||||
|
*/
|
||||||
|
static void Exit() {
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
DEBUG_FUNCTION();
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
if (SDL_PushEvent(&event) != 0)
|
||||||
|
error("Unable to push exit event!");
|
||||||
|
}
|
||||||
|
|
||||||
/* This function provides all of the menu
|
/* This function provides all of the menu
|
||||||
* handling that has been observed
|
* handling that has been observed
|
||||||
*/
|
*/
|
||||||
|
@ -1920,35 +2008,6 @@ static void menuHandler() {
|
||||||
// get the keycode
|
// get the keycode
|
||||||
key = atoi(lua_getstring(keycode));
|
key = atoi(lua_getstring(keycode));
|
||||||
|
|
||||||
// get the item list for most menus
|
|
||||||
itemTable = getTableValue(menuTable, "menu");
|
|
||||||
if (!lua_istable(itemTable)) {
|
|
||||||
// 3D Acceleration Menu
|
|
||||||
itemTable = getTableValue(menuTable, "active_menu");
|
|
||||||
if (!lua_istable(itemTable)) {
|
|
||||||
// Control Help menu
|
|
||||||
lua_Object nextPage = getTableValue(menuTable, "next_page");
|
|
||||||
lua_Object prevPage = getTableValue(menuTable, "prev_page");
|
|
||||||
|
|
||||||
if (lua_isfunction(nextPage) && key == SDLK_RIGHT) {
|
|
||||||
lua_beginblock();
|
|
||||||
lua_pushobject(menuTable);
|
|
||||||
lua_callfunction(nextPage);
|
|
||||||
lua_endblock();
|
|
||||||
} else if (lua_isfunction(prevPage) && key == SDLK_LEFT) {
|
|
||||||
lua_beginblock();
|
|
||||||
lua_pushobject(menuTable);
|
|
||||||
lua_callfunction(prevPage);
|
|
||||||
lua_endblock();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the current item
|
|
||||||
menuItem = atoi(lua_getstring(getTableValue(itemTable, "cur_item")));
|
|
||||||
menuItems = atoi(lua_getstring(getTableValue(itemTable, "num_items")));
|
|
||||||
|
|
||||||
// handle hotkeys
|
// handle hotkeys
|
||||||
if (key >= SDLK_a && key <= SDLK_z) {
|
if (key >= SDLK_a && key <= SDLK_z) {
|
||||||
char keychar[2];
|
char keychar[2];
|
||||||
|
@ -1965,11 +2024,71 @@ static void menuHandler() {
|
||||||
key = 0;
|
key = 0;
|
||||||
return; // stop processing (we don't need to handle normal keystrokes)
|
return; // stop processing (we don't need to handle normal keystrokes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle the correct type of menu
|
||||||
|
if (!lua_isnil(getTableValue(menuTable, "menu"))) {
|
||||||
|
// Get the item list for most menus
|
||||||
|
itemTable = getTableValue(menuTable, "menu");
|
||||||
|
} else if (!lua_isnil(getTableValue(menuTable, "active_menu"))) {
|
||||||
|
// 3D Acceleration Menu
|
||||||
|
itemTable = getTableValue(menuTable, "active_menu");
|
||||||
|
} else if (!lua_isnil(getTableValue(menuTable, "num_choices"))) {
|
||||||
|
// Exit Menu
|
||||||
|
int current_choice = atoi(lua_getstring(getTableValue(menuTable, "current_choice")));
|
||||||
|
int num_choices = atoi(lua_getstring(getTableValue(menuTable, "num_choices")));
|
||||||
|
|
||||||
|
if (key == SDLK_RIGHT)
|
||||||
|
current_choice++;
|
||||||
|
else if (key == SDLK_LEFT)
|
||||||
|
current_choice--;
|
||||||
|
|
||||||
|
if (current_choice < 0)
|
||||||
|
current_choice = 0;
|
||||||
|
else if (current_choice >= num_choices)
|
||||||
|
current_choice = num_choices-1;
|
||||||
|
|
||||||
|
setTableValue(menuTable, "current_choice", current_choice);
|
||||||
|
// TODO: Need to figure out how to update the screen under this
|
||||||
|
// type of menu operation!
|
||||||
|
return;
|
||||||
|
} else if (lua_isfunction(getTableValue(menuTable, "next_page"))
|
||||||
|
|| lua_isfunction(getTableValue(menuTable, "prev_page"))) {
|
||||||
|
// Control Help menu
|
||||||
|
lua_Object nextPage = getTableValue(menuTable, "next_page");
|
||||||
|
lua_Object prevPage = getTableValue(menuTable, "prev_page");
|
||||||
|
|
||||||
|
if (lua_isfunction(nextPage) && key == SDLK_RIGHT) {
|
||||||
|
lua_beginblock();
|
||||||
|
lua_pushobject(menuTable);
|
||||||
|
lua_callfunction(nextPage);
|
||||||
|
lua_endblock();
|
||||||
|
} else if (lua_isfunction(prevPage) && key == SDLK_LEFT) {
|
||||||
|
lua_beginblock();
|
||||||
|
lua_pushobject(menuTable);
|
||||||
|
lua_callfunction(prevPage);
|
||||||
|
lua_endblock();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
warning("Unhandled type of menu!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the current item
|
||||||
|
menuItem = atoi(lua_getstring(getTableValue(itemTable, "cur_item")));
|
||||||
|
menuItems = atoi(lua_getstring(getTableValue(itemTable, "num_items")));
|
||||||
|
|
||||||
// hack the "&Return to Game" command so it actually works
|
// hack the "&Return to Game" command so it actually works
|
||||||
if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx247/"))
|
if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx247/"))
|
||||||
key = SDLK_ESCAPE;
|
key = SDLK_ESCAPE;
|
||||||
|
|
||||||
|
// hack the "&Quit" command so it actually works
|
||||||
|
// (at this time it cannot open the exit menu on top of the normal one)
|
||||||
|
if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx248/")) {
|
||||||
|
Exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* if we're running the menu then we need to manually handle
|
/* if we're running the menu then we need to manually handle
|
||||||
* a lot of the operations necessary to use the menu
|
* a lot of the operations necessary to use the menu
|
||||||
*/
|
*/
|
||||||
|
@ -1982,17 +2101,12 @@ static void menuHandler() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lua_Object close = getTableFunction(menuTable, "cancel");
|
lua_Object close = getTableFunction(menuTable, "cancel");
|
||||||
lua_Object destroy = getTableFunction(menuTable, "destroy");
|
|
||||||
|
|
||||||
lua_beginblock();
|
lua_beginblock();
|
||||||
lua_pushobject(menuTable);
|
lua_pushobject(menuTable);
|
||||||
lua_pushnil();
|
lua_pushnil();
|
||||||
lua_callfunction(close);
|
lua_callfunction(close);
|
||||||
lua_endblock();
|
lua_endblock();
|
||||||
lua_beginblock();
|
|
||||||
lua_pushnumber(menuItem);
|
|
||||||
lua_callfunction(destroy);
|
|
||||||
lua_endblock();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
|
@ -2031,16 +2145,7 @@ static void menuHandler() {
|
||||||
lua_endblock();
|
lua_endblock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean the requested menu
|
|
||||||
*/
|
|
||||||
static void destroyMenu() {
|
|
||||||
DEBUG_FUNCTION();
|
|
||||||
CleanBuffer();
|
|
||||||
lua_Object system_table = lua_getglobal("system");
|
|
||||||
setTableValue(system_table, "menuHandler", (lua_Object) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for an existing object by a certain name
|
/* Check for an existing object by a certain name
|
||||||
* this function is used by several functions that look
|
* this function is used by several functions that look
|
||||||
* for text objects to see if they need to be created/modified/destroyed.
|
* for text objects to see if they need to be created/modified/destroyed.
|
||||||
|
@ -2252,14 +2357,18 @@ static void StartMovie() {
|
||||||
pushbool(g_smush->play(luaL_check_string(1), x, y));
|
pushbool(g_smush->play(luaL_check_string(1), x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fullscreen movie playing query and normal movie
|
||||||
|
* query should actually detect correctly and not
|
||||||
|
* just return true whenever ANY movie is playing
|
||||||
|
*/
|
||||||
static void IsFullscreenMoviePlaying() {
|
static void IsFullscreenMoviePlaying() {
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
pushbool(g_smush->isPlaying());
|
pushbool(g_smush->isPlaying() && g_engine->getMode() == ENGINE_MODE_SMUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IsMoviePlaying() {
|
static void IsMoviePlaying() {
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
pushbool(g_smush->isPlaying());
|
pushbool(g_smush->isPlaying() && g_engine->getMode() == ENGINE_MODE_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StopMovie() {
|
static void StopMovie() {
|
||||||
|
@ -2411,11 +2520,6 @@ static void GetDiskFreeSpace() {
|
||||||
|
|
||||||
// Objectstate functions
|
// Objectstate functions
|
||||||
static void NewObjectState() {
|
static void NewObjectState() {
|
||||||
enum ObjectPosition {
|
|
||||||
OBJSTATE_UNDERLAY = 1,
|
|
||||||
OBJSTATE_OVERLAY = 2,
|
|
||||||
OBJSTATE_STATE = 3
|
|
||||||
};
|
|
||||||
ObjectState *state = NULL;
|
ObjectState *state = NULL;
|
||||||
ObjectState::Position pos;
|
ObjectState::Position pos;
|
||||||
char *bitmap, *zbitmap;
|
char *bitmap, *zbitmap;
|
||||||
|
@ -2423,6 +2527,7 @@ static void NewObjectState() {
|
||||||
int setupID;
|
int setupID;
|
||||||
|
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
|
// Called with "nil" if you jump to zone "sp"
|
||||||
setupID = check_int(1); // Setup ID
|
setupID = check_int(1); // Setup ID
|
||||||
pos = check_objstate_pos(2); // When to draw
|
pos = check_objstate_pos(2); // When to draw
|
||||||
bitmap = luaL_check_string(3); // Bitmap
|
bitmap = luaL_check_string(3); // Bitmap
|
||||||
|
@ -2683,18 +2788,30 @@ static void RenderModeUser() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For now this should just destroy any text
|
||||||
|
* objects that have been created and unlink
|
||||||
|
* the menu (this may be more appropriate under
|
||||||
|
* RenderModeUser, determine later)
|
||||||
|
*/
|
||||||
|
static void SetGamma() {
|
||||||
|
lua_Object system_table = lua_getglobal("system");
|
||||||
|
|
||||||
|
// TODO: Make this un-dim the screen
|
||||||
|
stubWarning("SetGamma");
|
||||||
|
|
||||||
|
CleanBuffer();
|
||||||
|
// Un-Install Menu Key Handler
|
||||||
|
lua_pushobject(system_table);
|
||||||
|
lua_pushstring(const_cast<char *>("menuHandler"));
|
||||||
|
lua_pushnil();
|
||||||
|
lua_settable();
|
||||||
|
}
|
||||||
|
|
||||||
static void Display() {
|
static void Display() {
|
||||||
lua_Object system_table;
|
lua_Object system_table;
|
||||||
|
|
||||||
DEBUG_FUNCTION();
|
DEBUG_FUNCTION();
|
||||||
system_table = lua_getglobal("system");
|
system_table = lua_getglobal("system");
|
||||||
// Install Menu Destroy Handler
|
|
||||||
lua_pushobject(system_table);
|
|
||||||
lua_pushstring(const_cast<char *>("userPaintHandler"));
|
|
||||||
lua_pushobject(lua_gettable());
|
|
||||||
lua_pushstring(const_cast<char *>("destroy"));
|
|
||||||
lua_pushcfunction(destroyMenu);
|
|
||||||
lua_settable();
|
|
||||||
// Install Menu Key Handler
|
// Install Menu Key Handler
|
||||||
lua_pushobject(system_table);
|
lua_pushobject(system_table);
|
||||||
lua_pushstring(const_cast<char *>("menuHandler"));
|
lua_pushstring(const_cast<char *>("menuHandler"));
|
||||||
|
@ -2838,7 +2955,6 @@ STUB_FUNC(ActivateActorShadow)
|
||||||
STUB_FUNC(SetShadowColor)
|
STUB_FUNC(SetShadowColor)
|
||||||
STUB_FUNC(DimRegion)
|
STUB_FUNC(DimRegion)
|
||||||
STUB_FUNC(ForceRefresh)
|
STUB_FUNC(ForceRefresh)
|
||||||
STUB_FUNC(SetGamma)
|
|
||||||
STUB_FUNC(LightMgrStartup)
|
STUB_FUNC(LightMgrStartup)
|
||||||
STUB_FUNC(SetLightIntensity)
|
STUB_FUNC(SetLightIntensity)
|
||||||
STUB_FUNC(SetLightPosition)
|
STUB_FUNC(SetLightPosition)
|
||||||
|
@ -2862,12 +2978,10 @@ STUB_FUNC(SetActorPitch)
|
||||||
STUB_FUNC(GetPointSector)
|
STUB_FUNC(GetPointSector)
|
||||||
STUB_FUNC(IsPointInSector)
|
STUB_FUNC(IsPointInSector)
|
||||||
STUB_FUNC(SetActorFrustrumCull)
|
STUB_FUNC(SetActorFrustrumCull)
|
||||||
STUB_FUNC(SetActorWalkDominate)
|
|
||||||
STUB_FUNC(GetCameraActor)
|
STUB_FUNC(GetCameraActor)
|
||||||
STUB_FUNC(DriveActorTo)
|
STUB_FUNC(DriveActorTo)
|
||||||
STUB_FUNC(WalkActorVector)
|
STUB_FUNC(WalkActorVector)
|
||||||
STUB_FUNC(GetActorRect)
|
STUB_FUNC(GetActorRect)
|
||||||
STUB_FUNC(GetActorNodeLocation)
|
|
||||||
STUB_FUNC(SetActorTimeScale)
|
STUB_FUNC(SetActorTimeScale)
|
||||||
STUB_FUNC(SetActorScale)
|
STUB_FUNC(SetActorScale)
|
||||||
STUB_FUNC(GetTranslationMode)
|
STUB_FUNC(GetTranslationMode)
|
||||||
|
@ -2876,7 +2990,6 @@ STUB_FUNC(PrintLine)
|
||||||
STUB_FUNC(KillPrimitive)
|
STUB_FUNC(KillPrimitive)
|
||||||
STUB_FUNC(WalkActorToAvoiding)
|
STUB_FUNC(WalkActorToAvoiding)
|
||||||
STUB_FUNC(GetActorChores)
|
STUB_FUNC(GetActorChores)
|
||||||
STUB_FUNC(Exit)
|
|
||||||
STUB_FUNC(SetCameraPosition)
|
STUB_FUNC(SetCameraPosition)
|
||||||
STUB_FUNC(GetCameraFOV)
|
STUB_FUNC(GetCameraFOV)
|
||||||
STUB_FUNC(SetCameraFOV)
|
STUB_FUNC(SetCameraFOV)
|
||||||
|
|
11
model.cpp
11
model.cpp
|
@ -263,10 +263,12 @@ void Model::loadText(TextSplitter &ts, const CMap &cmap) {
|
||||||
_materials = new ResPtr<Material>[_numMaterials];
|
_materials = new ResPtr<Material>[_numMaterials];
|
||||||
_materialNames = new char[_numMaterials][32];
|
_materialNames = new char[_numMaterials][32];
|
||||||
for (int i = 0; i < _numMaterials; i++) {
|
for (int i = 0; i < _numMaterials; i++) {
|
||||||
|
char materialName[32];
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
ts.scanString("%d: %32s", 2, &num, _materialNames[num]);
|
ts.scanString("%d: %32s", 2, &num, materialName);
|
||||||
_materials[num] = g_resourceloader->loadMaterial(_materialNames[num], cmap);
|
_materials[num] = g_resourceloader->loadMaterial(materialName, cmap);
|
||||||
|
strcpy(_materialNames[num], materialName);
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.expectString("section: geometrydef");
|
ts.expectString("section: geometrydef");
|
||||||
|
@ -401,16 +403,17 @@ void Model::Mesh::loadText(TextSplitter &ts, ResPtr<Material> *materials) {
|
||||||
_faces = new Face[_numFaces];
|
_faces = new Face[_numFaces];
|
||||||
_materialid = new int[_numFaces];
|
_materialid = new int[_numFaces];
|
||||||
for (int i = 0; i < _numFaces; i++) {
|
for (int i = 0; i < _numFaces; i++) {
|
||||||
int num, type, geo, light, tex, verts;
|
int num, materialid, type, geo, light, tex, verts;
|
||||||
float extralight;
|
float extralight;
|
||||||
int readlen;
|
int readlen;
|
||||||
|
|
||||||
if (ts.eof())
|
if (ts.eof())
|
||||||
error("Expected face data, got EOF\n");
|
error("Expected face data, got EOF\n");
|
||||||
|
|
||||||
if (std::sscanf(ts.currentLine(), " %d: %d %i %d %d %d %f %d%n", &num, &_materialid[num], &type, &geo, &light, &tex, &extralight, &verts, &readlen) < 8)
|
if (std::sscanf(ts.currentLine(), " %d: %d %i %d %d %d %f %d%n", &num, &materialid, &type, &geo, &light, &tex, &extralight, &verts, &readlen) < 8)
|
||||||
error("Expected face data, got `%s'\n", ts.currentLine());
|
error("Expected face data, got `%s'\n", ts.currentLine());
|
||||||
|
|
||||||
|
_materialid[num] = materialid;
|
||||||
_faces[num]._material = materials[_materialid[num]];
|
_faces[num]._material = materials[_materialid[num]];
|
||||||
_faces[num]._type = type;
|
_faces[num]._type = type;
|
||||||
_faces[num]._geo = geo;
|
_faces[num]._geo = geo;
|
||||||
|
|
|
@ -28,9 +28,12 @@
|
||||||
class ObjectState {
|
class ObjectState {
|
||||||
public:
|
public:
|
||||||
enum Position {
|
enum Position {
|
||||||
|
OBJSTATE_BACKGROUND = 0,
|
||||||
OBJSTATE_UNDERLAY = 1,
|
OBJSTATE_UNDERLAY = 1,
|
||||||
OBJSTATE_OVERLAY = 2,
|
OBJSTATE_OVERLAY = 2,
|
||||||
OBJSTATE_STATE = 3
|
OBJSTATE_STATE = 3
|
||||||
|
// TODO: Find out what ObjectState 6 is supposed to be
|
||||||
|
// OBJSTATE_UNKNOWN = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectState(int setupID, ObjectState::Position pos, const char *bitmap, const char *zbitmap, bool visible);
|
ObjectState(int setupID, ObjectState::Position pos, const char *bitmap, const char *zbitmap, bool visible);
|
||||||
|
|
12
resource.cpp
12
resource.cpp
|
@ -285,6 +285,18 @@ Model *ResourceLoader::loadModel(const char *filename, const CMap &c) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ResourceLoader::exportResource(const char *filename) {
|
||||||
|
FILE *myFile = fopen(filename, "w");
|
||||||
|
Block *b = getFileBlock(filename);
|
||||||
|
|
||||||
|
if (b == NULL)
|
||||||
|
return false;
|
||||||
|
fwrite(b->data(), b->len(), 1, myFile);
|
||||||
|
fclose(myFile);
|
||||||
|
delete b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceLoader::uncache(const char *filename) {
|
void ResourceLoader::uncache(const char *filename) {
|
||||||
std::string fname = filename;
|
std::string fname = filename;
|
||||||
makeLower(fname);
|
makeLower(fname);
|
||||||
|
|
|
@ -91,6 +91,7 @@ public:
|
||||||
std::FILE *openNewStream(const char *filename) const;
|
std::FILE *openNewStream(const char *filename) const;
|
||||||
int fileLength(const char *filename) const;
|
int fileLength(const char *filename) const;
|
||||||
|
|
||||||
|
bool exportResource(const char *filename);
|
||||||
Bitmap *loadBitmap(const char *fname);
|
Bitmap *loadBitmap(const char *fname);
|
||||||
CMap *loadColormap(const char *fname);
|
CMap *loadColormap(const char *fname);
|
||||||
Costume *loadCostume(const char *fname, Costume *prevCost);
|
Costume *loadCostume(const char *fname, Costume *prevCost);
|
||||||
|
|
24
scene.cpp
24
scene.cpp
|
@ -111,17 +111,25 @@ void Scene::Setup::load(TextSplitter &ts) {
|
||||||
|
|
||||||
ts.scanString(" background %256s", 1, buf);
|
ts.scanString(" background %256s", 1, buf);
|
||||||
_bkgndBm = g_resourceloader->loadBitmap(buf);
|
_bkgndBm = g_resourceloader->loadBitmap(buf);
|
||||||
if(debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
if (_bkgndBm == NULL) {
|
||||||
printf("Loading scene bitmap: %s\n", buf);
|
if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Unable to load scene bitmap: %s\n", buf);
|
||||||
|
} else {
|
||||||
|
if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Loaded scene bitmap: %s\n", buf);
|
||||||
|
}
|
||||||
|
|
||||||
// ZBuffer is optional
|
// ZBuffer is optional
|
||||||
if (!ts.checkString("zbuffer")) {
|
if (!ts.checkString("zbuffer")) {
|
||||||
_bkgndZBm = NULL;
|
_bkgndZBm = NULL;
|
||||||
} else {
|
} else {
|
||||||
ts.scanString(" zbuffer %256s", 1, buf);
|
ts.scanString(" zbuffer %256s", 1, buf);
|
||||||
_bkgndZBm = g_resourceloader->loadBitmap(buf);
|
// Don't even try to load if it's the "none" bitmap
|
||||||
if(debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
if (strcmp(buf, "<none>.lbm") != 0) {
|
||||||
printf("Loading scene z-buffer bitmap: %s\n", buf);
|
_bkgndZBm = g_resourceloader->loadBitmap(buf);
|
||||||
|
if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Loading scene z-buffer bitmap: %s\n", buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.scanString(" position %f %f %f", 3, &_pos.x(), &_pos.y(), &_pos.z());
|
ts.scanString(" position %f %f %f", 3, &_pos.x(), &_pos.y(), &_pos.z());
|
||||||
|
@ -186,6 +194,12 @@ void Scene::setupLights() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::setSetup(int num) {
|
void Scene::setSetup(int num) {
|
||||||
|
// Looks like num is zero-based so >= should work to find values
|
||||||
|
// that are out of the range of valid setups
|
||||||
|
if (num >= _numSetups || num < 0) {
|
||||||
|
error("Failed to change scene setup, value out of range!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
_currSetup = _setups + num;
|
_currSetup = _setups + num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
scene.h
10
scene.h
|
@ -47,10 +47,12 @@ public:
|
||||||
_currSetup->_bkgndZBm->draw();
|
_currSetup->_bkgndZBm->draw();
|
||||||
|
|
||||||
if (_currSetup->_bkgndBm == NULL) {
|
if (_currSetup->_bkgndBm == NULL) {
|
||||||
error("Null background for setup %s in %s", _currSetup->_name.c_str(), _name.c_str());
|
// This should fail softly, for some reason jumping to the signpost (sg) will load
|
||||||
return;
|
// the scene in such a way that the background isn't immediately available
|
||||||
}
|
warning("Background hasn't loaded yet for setup %s in %s!", _currSetup->_name.c_str(), _name.c_str());
|
||||||
_currSetup->_bkgndBm->draw();
|
} else {
|
||||||
|
_currSetup->_bkgndBm->draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void drawBitmaps(ObjectState::Position stage);
|
void drawBitmaps(ObjectState::Position stage);
|
||||||
void setupCamera() {
|
void setupCamera() {
|
||||||
|
|
108
smush.cpp
108
smush.cpp
|
@ -29,6 +29,9 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
|
||||||
|
#define BUFFER_SIZE 16385
|
||||||
|
|
||||||
Smush *g_smush;
|
Smush *g_smush;
|
||||||
static uint16 smushDestTable[5786];
|
static uint16 smushDestTable[5786];
|
||||||
|
|
||||||
|
@ -49,6 +52,7 @@ Smush::Smush() {
|
||||||
_videoFinished = false;
|
_videoFinished = false;
|
||||||
_videoPause = true;
|
_videoPause = true;
|
||||||
_updateNeeded = false;
|
_updateNeeded = false;
|
||||||
|
_startPos = NULL;
|
||||||
_stream = NULL;
|
_stream = NULL;
|
||||||
_movieTime = 0;
|
_movieTime = 0;
|
||||||
_frame = 0;
|
_frame = 0;
|
||||||
|
@ -65,6 +69,7 @@ void Smush::init() {
|
||||||
_videoFinished = false;
|
_videoFinished = false;
|
||||||
_videoPause = false;
|
_videoPause = false;
|
||||||
_updateNeeded = false;
|
_updateNeeded = false;
|
||||||
|
_videoLooping = false;
|
||||||
|
|
||||||
assert(!_internalBuffer);
|
assert(!_internalBuffer);
|
||||||
assert(!_externalBuffer);
|
assert(!_externalBuffer);
|
||||||
|
@ -88,6 +93,7 @@ void Smush::deinit() {
|
||||||
_externalBuffer = NULL;
|
_externalBuffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_videoLooping = false;
|
||||||
_videoFinished = true;
|
_videoFinished = true;
|
||||||
_videoPause = true;
|
_videoPause = true;
|
||||||
if (_stream) {
|
if (_stream) {
|
||||||
|
@ -129,10 +135,34 @@ void Smush::handleFrame() {
|
||||||
|
|
||||||
tag = _file.readUint32BE();
|
tag = _file.readUint32BE();
|
||||||
if (tag == MKID_BE('ANNO')) {
|
if (tag == MKID_BE('ANNO')) {
|
||||||
printf("Announcement!\n");
|
char *anno;
|
||||||
|
byte *data;
|
||||||
|
|
||||||
size = _file.readUint32BE();
|
size = _file.readUint32BE();
|
||||||
for (int l = 0; l < size; l++)
|
data = (byte *)malloc(size);
|
||||||
_file.readByte();
|
_file.read(data, size);
|
||||||
|
anno = (char *)data;
|
||||||
|
if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER)-1) == 0) {
|
||||||
|
char *annoData = anno + sizeof(ANNO_HEADER);
|
||||||
|
int loop;
|
||||||
|
|
||||||
|
// Examples:
|
||||||
|
// Water streaming around boat from Manny's balcony
|
||||||
|
// MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
|
||||||
|
// Water in front of the Blue Casket
|
||||||
|
// MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
|
||||||
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Announcement data: %s\n", anno);
|
||||||
|
sscanf(annoData, "%*d;%*d;%*d;%d;%*d;%*d;%*d;%*d;%*d;%*d;%*d;", &loop);
|
||||||
|
_videoLooping = (bool) loop;
|
||||||
|
_startPos = _file.getPos();
|
||||||
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("_videoLooping: %d\n", _videoLooping);
|
||||||
|
} else {
|
||||||
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("Announcement header not understood: %s\n", anno);
|
||||||
|
}
|
||||||
|
free(anno);
|
||||||
tag = _file.readUint32BE();
|
tag = _file.readUint32BE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,12 +194,19 @@ printf("Announcement!\n");
|
||||||
_updateNeeded = true;
|
_updateNeeded = true;
|
||||||
|
|
||||||
_frame++;
|
_frame++;
|
||||||
if (_frame == _nbframes) {
|
|
||||||
_videoFinished = true;
|
|
||||||
g_engine->setMode(ENGINE_MODE_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
_movieTime += _speed / 1000;
|
_movieTime += _speed / 1000;
|
||||||
|
if (_frame == _nbframes) {
|
||||||
|
// If we're not supposed to loop (or looping fails) then end the video
|
||||||
|
if(!_videoLooping || !_file.setPos(_startPos)) {
|
||||||
|
_videoFinished = true;
|
||||||
|
g_engine->setMode(ENGINE_MODE_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If the position change succeeded then reset the frame number
|
||||||
|
// for some reason we need to set it to 1 instead of zero or we
|
||||||
|
// won't render a frame
|
||||||
|
_frame = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smush::handleFramesHeader() {
|
void Smush::handleFramesHeader() {
|
||||||
|
@ -260,9 +297,42 @@ zlibFile::~zlibFile() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SAVEPOS *zlibFile::getPos() {
|
||||||
|
struct SAVEPOS *pos;
|
||||||
|
long position = std::ftell(_handle);
|
||||||
|
|
||||||
|
if (position == -1 && debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) {
|
||||||
|
warning("zlibFile::open() unable to find start position!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pos = (struct SAVEPOS *) malloc(sizeof(struct SAVEPOS));
|
||||||
|
pos->filePos = position;
|
||||||
|
inflateCopy(&pos->streamBuf, &_stream);
|
||||||
|
pos->tmpBuf = (char *)calloc(1, BUFFER_SIZE);
|
||||||
|
memcpy(pos->tmpBuf, _inBuf, BUFFER_SIZE);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool zlibFile::setPos(struct SAVEPOS *pos) {
|
||||||
|
int ret;
|
||||||
|
if (_handle == NULL || pos == NULL) {
|
||||||
|
warning("Unable to rewind SMUSH movie (invalid handle)!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ret = std::fseek(_handle, pos->filePos, SEEK_SET);
|
||||||
|
if (ret == -1) {
|
||||||
|
warning("Unable to rewind SMUSH movie (seek failed)! %m");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(_inBuf, pos->tmpBuf, BUFFER_SIZE);
|
||||||
|
inflateCopy(&_stream, &pos->streamBuf);
|
||||||
|
_fileDone = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool zlibFile::open(const char *filename) {
|
bool zlibFile::open(const char *filename) {
|
||||||
char flags = 0;
|
char flags = 0;
|
||||||
_inBuf = (char *)calloc(1, 16385);
|
_inBuf = (char *)calloc(1, BUFFER_SIZE);
|
||||||
|
|
||||||
if (_handle) {
|
if (_handle) {
|
||||||
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
||||||
|
@ -287,8 +357,12 @@ bool zlibFile::open(const char *filename) {
|
||||||
fread(_inBuf, 6, sizeof(char), _handle); // XFlags
|
fread(_inBuf, 6, sizeof(char), _handle); // XFlags
|
||||||
|
|
||||||
// Xtra & Comment
|
// Xtra & Comment
|
||||||
if (((((flags & 0x04) != 0) || ((flags & 0x10) != 0))) && (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL))
|
if (((flags & 0x04) != 0) || ((flags & 0x10) != 0)) {
|
||||||
error("zlibFile::open() Unsupported header flag");
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL) {
|
||||||
|
error("zlibFile::open() Unsupported header flag");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ((flags & 0x08) != 0) { // Orig. Name
|
if ((flags & 0x08) != 0) { // Orig. Name
|
||||||
do {
|
do {
|
||||||
|
@ -299,7 +373,7 @@ bool zlibFile::open(const char *filename) {
|
||||||
if ((flags & 0x02) != 0) // CRC
|
if ((flags & 0x02) != 0) // CRC
|
||||||
fread(_inBuf, 2, sizeof(char), _handle);
|
fread(_inBuf, 2, sizeof(char), _handle);
|
||||||
|
|
||||||
memset(_inBuf, 0, 16384); // Zero buffer (debug)
|
memset(_inBuf, 0, BUFFER_SIZE-1); // Zero buffer (debug)
|
||||||
_stream.zalloc = NULL;
|
_stream.zalloc = NULL;
|
||||||
_stream.zfree = NULL;
|
_stream.zfree = NULL;
|
||||||
_stream.opaque = Z_NULL;
|
_stream.opaque = Z_NULL;
|
||||||
|
@ -310,7 +384,7 @@ bool zlibFile::open(const char *filename) {
|
||||||
_stream.next_in = NULL;
|
_stream.next_in = NULL;
|
||||||
_stream.next_out = NULL;
|
_stream.next_out = NULL;
|
||||||
_stream.avail_in = 0;
|
_stream.avail_in = 0;
|
||||||
_stream.avail_out = 16384;
|
_stream.avail_out = BUFFER_SIZE-1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +424,7 @@ uint32 zlibFile::read(void *ptr, uint32 len) {
|
||||||
_fileDone = false;
|
_fileDone = false;
|
||||||
while (_stream.avail_out != 0) {
|
while (_stream.avail_out != 0) {
|
||||||
if (_stream.avail_in == 0) { // !eof
|
if (_stream.avail_in == 0) { // !eof
|
||||||
_stream.avail_in = fread(_inBuf, 1, 16384, _handle);
|
_stream.avail_in = fread(_inBuf, 1, BUFFER_SIZE-1, _handle);
|
||||||
if (_stream.avail_in == 0) {
|
if (_stream.avail_in == 0) {
|
||||||
fileEOF = true;
|
fileEOF = true;
|
||||||
break;
|
break;
|
||||||
|
@ -360,8 +434,10 @@ uint32 zlibFile::read(void *ptr, uint32 len) {
|
||||||
|
|
||||||
result = inflate(&_stream, Z_NO_FLUSH);
|
result = inflate(&_stream, Z_NO_FLUSH);
|
||||||
if (result == Z_STREAM_END) { // EOF
|
if (result == Z_STREAM_END) { // EOF
|
||||||
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
// "Stream end" is zlib's way of saying that it's done after the current call,
|
||||||
warning("zlibFile::read() Stream ended");
|
// so as long as no calls are made after we've received this message we're OK
|
||||||
|
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
||||||
|
printf("zlibFile::read() Stream ended\n");
|
||||||
_fileDone = true;
|
_fileDone = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
10
smush.h
10
smush.h
|
@ -29,6 +29,12 @@
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
struct SAVEPOS {
|
||||||
|
long filePos;
|
||||||
|
z_stream streamBuf;
|
||||||
|
char *tmpBuf;
|
||||||
|
};
|
||||||
|
|
||||||
class zlibFile {
|
class zlibFile {
|
||||||
private:
|
private:
|
||||||
FILE *_handle;
|
FILE *_handle;
|
||||||
|
@ -39,7 +45,9 @@ private:
|
||||||
public:
|
public:
|
||||||
zlibFile();
|
zlibFile();
|
||||||
~zlibFile();
|
~zlibFile();
|
||||||
|
bool setPos(struct SAVEPOS *pos);
|
||||||
bool open(const char *filename);
|
bool open(const char *filename);
|
||||||
|
struct SAVEPOS *getPos();
|
||||||
void close();
|
void close();
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
|
|
||||||
|
@ -67,6 +75,8 @@ private:
|
||||||
int _freq;
|
int _freq;
|
||||||
bool _videoFinished;
|
bool _videoFinished;
|
||||||
bool _videoPause;
|
bool _videoPause;
|
||||||
|
bool _videoLooping;
|
||||||
|
struct SAVEPOS *_startPos;
|
||||||
int _x, _y;
|
int _x, _y;
|
||||||
int _width, _height;
|
int _width, _height;
|
||||||
byte *_internalBuffer, *_externalBuffer;
|
byte *_internalBuffer, *_externalBuffer;
|
||||||
|
|
BIN
tools/util.o
BIN
tools/util.o
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue