protected int DoCommandInternal(int numargs, int[] a)
{
if (numargs < 1)
return -1;
int i;
byte cmd = (byte)(a[0] & 0xFF);
byte param = (byte)(a[0] >> 8);
Player player = null;
if (!_initialized && (cmd != 0 || param != 0))
return -1;
// {
// var str = string.Format("DoCommand - {0} ({1}/{2})", a[0], (int)param, (int)cmd);
// for (i = 1; i < numargs; ++i)
// str += string.Format(", {0}", a[i]);
// Debug.WriteLine(str);
// }
if (param == 0)
{
switch (cmd)
{
case 6:
if (a[1] > 127)
return -1;
else
{
Debug.WriteLine("IMuse DoCommand(6) - SetImuseMasterVolume ({0})", a[1]);
return SetImuseMasterVolume((uint)((a[1] << 1) | (a[1] != 0 ? 0 : 1))); // Convert from 0-127 to 0-255
}
case 7:
Debug.WriteLine("IMuse DoCommand(7) - GetMasterVolume ({0})", a[1]);
return _master_volume / 2; // Convert from 0-255 to 0-127
case 8:
return StartSoundInternal(a[1]) ? 0 : -1;
case 9:
return StopSoundInternal(a[1]);
case 10: // FIXME: Sam and Max - Not sure if this is correct
return StopAllSoundsInternal();
case 11:
return StopAllSoundsInternal();
case 12:
// Sam & Max: Player-scope commands
player = FindActivePlayer(a[1]);
if (player == null)
return -1;
switch (a[3])
{
case 6:
// Set player volume.
return player.SetVolume((byte)a[4]);
default:
// Console.Error.WriteLine("IMuseInternal::DoCommand(12) unsupported sub-command {0}", a[3]);
break;
}
return -1;
case 13:
return GetSoundStatusInternal(a[1], true);
case 14:
// Sam and Max: Parameter fade
player = FindActivePlayer(a[1]);
if (player != null)
return player.AddParameterFader((ParameterFaderType)a[3], a[4], a[5]);
return -1;
case 15:
// Sam & Max: Set hook for a "maybe" jump
player = FindActivePlayer(a[1]);
if (player != null)
{
player.SetHook(0, (byte)a[3], 0);
return 0;
}
return -1;
case 16:
Debug.WriteLine("IMuse DoCommand(16) - SetVolChan ({0}, {1})", a[1], a[2]);
return SetVolchan(a[1], a[2]);
case 17:
if (_game_id != GameId.SamNMax)
{
Debug.WriteLine("IMuse DoCommand(17) - setChannelVolume ({0}, {1})", a[1], a[2]);
return SetChannelVolume((uint)a[1], (uint)a[2]);
}
else
{
if (a[4] != 0)
{
int[] b = new int[16];
for (i = 0; i < numargs; ++i)
b[i] = a[i];
return ImSetTrigger(b[1], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11]);
}
else
{
return ImClearTrigger(a[1], a[3]);
}
}
case 18:
if (_game_id != GameId.SamNMax)
{
return SetVolchanEntry(a[1], (uint)a[2]);
}
else
{
// Sam & Max: ImCheckTrigger.
// According to Mike's notes to Ender,
// this function returns the number of triggers
// associated with a particular player ID and
// trigger ID.
a[0] = 0;
for (i = 0; i < _snm_triggers.Length; ++i)
{
if (_snm_triggers[i].Sound == a[1] && _snm_triggers[i].Id != 0 &&
(a[3] == -1 || _snm_triggers[i].Id == a[3]))
{
++a[0];
}
}
return a[0];
}
case 19:
// Sam & Max: ImClearTrigger
// This should clear a trigger that's been set up
// with ImSetTrigger(cmd == 17). Seems to work....
return ImClearTrigger(a[1], a[3]);
case 20:
// Sam & Max: Deferred Command
AddDeferredCommand(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
return 0;
case 2:
case 3:
return 0;
default:
// Console.Error.WriteLine("DoCommand({0} [{1}/{2}], {3}, {4}, {5}, {6}, {7}, {8}, {9}) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
break;
}
}
else if (param == 1)
{
if (((1 << cmd) & 0x783FFF) != 0)
{
player = FindActivePlayer(a[1]);
if (player == null)
return -1;
if (((1 << cmd) & (1 << 11 | 1 << 22)) != 0)
{
Contract.Assert(a[2] >= 0 && a[2] <= 15);
// TODO: vs: check if it's correct...
player = player.GetPart((byte)a[2]).Player;
if (player == null)
return -1;
}
}
switch (cmd)
{
case 0:
if (_game_id == GameId.SamNMax)
{
if (a[3] == 1) // Measure number
return (int)(((player.GetBeatIndex() - 1) >> 2) + 1);
else if (a[3] == 2) // Beat number
return (int)player.GetBeatIndex();
return -1;
}
else
{
return player.GetParam(a[2], (byte)a[3]);
}
case 1:
if (_game_id == GameId.SamNMax)
{
// FIXME: Could someone verify this?
//
// This jump instruction is known to be used in
// the following cases:
//
// 1) Going anywhere on the USA map
// 2) Winning the Wak-A-Rat game
// 3) Losing or quitting the Wak-A-Rat game
// 4) Conroy hitting Max with a golf club
//
// For all these cases the position parameters
// are always the same: 2, 1, 0, 0.
//
// 5) When leaving the bigfoot party. The
// position parameters are: 3, 4, 300, 0
// 6) At Frog Rock, when the UFO appears. The
// position parameters are: 10, 4, 400, 1
//
// The last two cases used to be buggy, so I
// have made a change to how the last two
// position parameters are handled. I still do
// not know if it's correct, but it sounds
// good to me at least.
Debug.WriteLine("DoCommand({0} [{1}/{2}], {3}, {4}, {5}, {6}, {7}, {8}, {9})", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
player.Jump((uint)(a[3] - 1), (uint)((a[4] - 1) * 4 + a[5]), (uint)(a[6] + ((a[7] * player.GetTicksPerBeat()) >> 2)));
}
else
player.SetPriority(a[2]);
return 0;
case 2:
return player.SetVolume((byte)a[2]);
case 3:
player.SetPan(a[2]);
return 0;
case 4:
return player.SetTranspose((byte)a[2], a[3]);
case 5:
player.SetDetune(a[2]);
return 0;
case 6:
// WORKAROUND for bug #1324106. When playing the
// "flourishes" as Rapp's body appears from his ashes,
// MI2 sets up triggers to pause the music, in case the
// animation plays too slowly, and then the music is
// manually unpaused for the next part of the music.
//
// In ScummVM, the animation finishes slightly too
// quickly, and the pause command is run *after* the
// unpause command. So we work around it by ignoring
// all attempts at pausing this particular sound.
//
// I could have sworn this wasn't needed after the
// recent timer change, but now it looks like it's
// still needed after all.
if (_game_id != GameId.Monkey2 || player.Id != 183 || a[2] != 0)
{
player.SetSpeed((byte)a[2]);
}
return 0;
case 7:
return player.Jump((uint)a[2], (uint)a[3], (uint)a[4]) ? 0 : -1;
case 8:
return player.Scan((uint)a[2], (uint)a[3], (uint)a[4]);
case 9:
return player.SetLoop((uint)a[2], (uint)a[3], (uint)a[4], (uint)a[5], (uint)a[6]) ? 0 : -1;
case 10:
player.ClearLoop();
return 0;
case 11:
// TODO: vs: check if it's correct...
player.Part.SetOnOff(a[3] != 0);
return 0;
case 12:
return player.SetHook((byte)a[2], (byte)a[3], (byte)a[4]);
case 13:
return player.AddParameterFader(ParameterFaderType.Volume, a[2], a[3]);
case 14:
return EnqueueTrigger(a[1], a[2]);
case 15:
return EnqueueCommand(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
case 16:
return ClearQueue();
case 19:
return player.GetParam(a[2], (byte)a[3]);
case 20:
return player.SetHook((byte)a[2], (byte)a[3], (byte)a[4]);
case 21:
return -1;
case 22:
// TODO: vs: check if it's correct
player.Part.Volume = a[3];
return 0;
case 23:
return QueryQueue(a[1]);
case 24:
return 0;
default:
// Console.Error.WriteLine("DoCommand({0} [{1}/{2}], {3}, {4}, {5}, {6}, {7}, {8}, {9}) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
return -1;
}
}
return -1;
}