diff options
247 files changed, 12321 insertions, 801 deletions
@@ -47,7 +47,7 @@ ScummVM Team AGI: Stuart George - Matthew Hoops + Matthew Hoops - (retired) Filippos Karapetis Martin Kiewitz Pawel Kolodziejski @@ -140,7 +140,7 @@ ScummVM Team Johannes Schickel Lastexpress: - Matthew Hoops + Matthew Hoops - (retired) Jordi Vilalta Prat Julien Templier @@ -158,7 +158,7 @@ ScummVM Team Mohawk: Bastien Bouclet - Matthew Hoops + Matthew Hoops - (retired) Filippos Karapetis Alyssa Milburn Eugene Sandulenko @@ -176,7 +176,7 @@ ScummVM Team peres Pegasus: - Matthew Hoops + Matthew Hoops - (retired) Queen: David Eriksson - (retired) @@ -281,6 +281,7 @@ ScummVM Team Android: Andre Heider Angus Lees + Lubomyr Lisen Dreamcast: Marcus Comstedt @@ -302,6 +303,9 @@ ScummVM Team Frantisek Dufka - (retired) Tarek Soliman + Nintendo 3DS: + Thomas Edvalson + Nintendo 64: Fabio Battaglia @@ -401,7 +405,8 @@ ScummVM Team Thierry Crozat - Numerous contributions to documentation Joachim Eberhard - Numerous contributions to documentation (retired) - Matthew Hoops - Wiki editor + Matthew Hoops - Numerous contributions to documentation + (retired) Retired Team Members -------------------- @@ -4,215 +4,404 @@ Copyright (C) 2001-2016 by the following: If you have contributed to this project then you deserve to be on this list. Contact us (see: AUTHORS) and we'll add you. -Tore Anderson +Manuel Alfayate Torbjorn Andersson +Tore Anderson +Matteo Angelino Chris Apers +Adrian Astley +Bertrand Augereau Ori Avtalion Nicolas Bacca +Dobo Balazs +Daniel Balsom +Yotam Barnoy Fabio Battaglia +Vincent Benony +Alex Bevilacqua +Laurent Blume Bastien Bouclet Arnaud Boutonne +Francois-R Boyer +Peter Bozso Jurgen Braam Ralph Brorsen James Brown +Henry Bush Stuart Caie +Rainer Canavan +Ben Castricum +Xiaojun Chen Jamieson Christian +Ryan Clark +William Claydon +Fabien Coeurjoly Marcus Comstedt +David Corrales-Lopez Paolo Costabel +Robert Crossfield Thierry Crozat +Vyacheslav Dikonov +Paul David Doherty +Martin Doucha Ivan Dubrov Frantisek Dufka +Sylvain Dupont +Joachim Eberhard +Thomas Edvalson Oystein Eftevaag -Kovacs Endre Janos David Eriksson +Thomas Fach-Pedersen +Yaroslav Fedevych Jerome Fisher +Hampus Flink +Hans-Joerg Frieden +Greg Frieger +Tom Frost Stuart George Paul Gilbert +Jean Marc Gimenez Robert Goeffringmann +Victor Gonzalez +GrajPoPolsku.pl Team +Chris Gray Jonathan Gray +Tobias Gunkel Benjamin Haisch Vincent Hamm +Ruediger Hanke +Matt Hargett +Andre Heider Sven Hesse Jochen Hoenicke Matthew Hoops Max Horn Travis Howell Janne Huttunen +Ravi I. Felix Jakschitsch +Kovacs Endre Janos Jeroen Janssen +Emmanuel Jeandel +Dmitry Jemerov +David Jensen Florian Kagerer +Keith Kaisershot Filippos Karapetis Andreas Karlsson +Denis Kasak +Chris Kehler +Robert Kelsen Ismail Khatib Oliver Kiehl Martin Kiewitz Pawel Kolodziejski +George Kormendi Mutwin Kraus +Stefan Kristiansson Andrew Kurushin +Daniel ter Laan +Hugo Labrande +Christopher T. Lansdown +Sergey Lapin Angus Lees +Rickard Lind +Max Lingua +Lubomyr Lisen +Ivan Lukyanov +Tomas Maidagan +Hubert Maier +Johannes Manhave +Lothar Serra Mari +Vicent Marti Claudio Matsuoka Thomas Mayer +Robert Megone +Vladimir Menshakov Alyssa Milburn Neil Millstone +Dark Minister Gregory Montoir +Peter Moraliyski +Carl Muckenhoupt +Alejandro Gomez de la Munoza +Sean Murray Kostas Nakos Mikesch Nepomuk +Jeremy Newman +Anders Baden Nielsen +Juha Niemimaki +Walter van Niftrik Nicolas Noble +Steffen Nyeland +Rune Orsval +Chris Page Willem Jan Palenstijn +Stefan Parviainen +Solomon Peachy Lars Persson Joost Peters Tim Phillips +Robey Pointer +Jordi Vilalta Prat +Magnus Reftel +Christoph Reichenbach +George Reid +Klaus Reimer +Andreas Roever Edward Rudd +Toni Saarela +Kari Salminen Eugene Sandulenko +Santiago G. Sanz +Simon Sawatzki +Daniel Schepler +Dominik Scherer Johannes Schickel +Luc Schrijvers +Zbynik Schwarz Keith Scroggins +Dan Serban +Lars Skovlund +Paul Smedley +Colin Snover Tarek Soliman +Einar Johan T. Somaaen +Andre Souza +Robert Spalek +Rink Springer Won Star +Markus Strangl Ludvig Strigeus Fedor Strizhniou David Symonds -Jordi Vilalta +Rainer De Temple +Julien Templier +Sean Terrell +Tobia Tesan +Scott Thomas +David Turner +Lionel Ulmer +Mikel Iturbe Urretxa +Hugues Valois +Petr Vyhnak +Chris Warren-Smith Robin Watts +Lukasz Watka +David Weinehall +Fredrik Wendel John Willis +Anton Yarcev +Bas Zoetekouw Jezar n0p peres Quietust +ScummBR Team +Raina Patches contributed by: Laura Abbott "sageofminerva" Vikram Aggarwal "youngelf" -the rara avis "theraraavis" +Norbert Bajko +Giovanni Bajo +Matan Bareket Dieter Baron "dillo" +Kevin Becker Alban Bedel "albeu" Bodo Bellut "bellut" -Bramvandijk "bramvandijk" +Nagy Bendeguz Andreas Bierfert "awjb" +Kaustav Biswas Elio Blanca "eblanca76" +Martin Bohm David Breakey "dbreakey" +Michael du Breuil "WickedShell" +Michael Brown Robert Buchholz "prendi" -Rainer Canavan "canavan" +Sander Buskens +Giulio Camuffo +Kevin Carnes Mathieu Carot "yokna" Stefano Ceccherini "jackburton" Travis S Coady "theealien" Josh Coalson "jcoalson" +Curt Coder Thomas Combeleran "hibernatus" +Patrick Combet Kees Cook "keescook" Carlos Corbacho "cathectic" +Andrea Corna Roberto Costa "fiix76" -dc france "erwan2004" -dewt "mncl" -Martin Doucha "next_ghost" +Eric Culp +Alexander Dergunov +Alexandre Detiste +Roman Donchenko +Heather Douglass Michael Drueing "doc_wagon" -Michael du Breuil "WickedShell" -dubsdj Matthew Duggan "stauff1" +Barry Duncan Olivier Duverne "richiefs" Andrei Dziahel "develop7" John Eckerdal "johneck" -Thomas Fach-Pedersen "madm00se" -Florent "flobo" -Florob "florob" +Abdeselam El-Haman +Henrik Engqvist Mike Frysinger "vapier" +Bence Gazder Chris Gelatt "kreeblah" Jens Georg "phako" Nicolas George "cigaes" +Martin Gerhardy Jonathan Gevaryahu "lord_nightmare" +Boris Gnezdilov Tobias Gruetzmacher "tobig" Damien Guard "damienguard" -Tobias Gunkel "tobigun" Matti Hamalainen "ccrtnsp" -Matt Hargett "matt_hargett" +Lauri Harsila Stefan Haubenthal "polluks" +Gavin Hayler Alexander Holler "holler" +Enrico Horn Falk Hueffner "mellum" Casey Hutchinson "nnooiissee" -j0tt +Tomas Jakobsson Gregor Jasny "gjasny" -Jellby "jellby" -Joerg "macdrega" Matt Johnson "mattjon" Nicolas Joly "njoly" -KeithS "keithscr" +Yusuke Kamiyamane +Martin Kennedy +Stephen Kennedy Sam Kenny "sam_k" Koen Kooi "koenkooi" +Christoph Korn +Christian Krause +Till Kresslein Zygmunt Krynicki "zygoon" Janne Kujanpaa "jukuja" +Neeraj Kumar +Oleksiy Kurochko Jay Lanagan "r0ni" Norbert Lange "nolange" Manuel Lauss "mlau2" Rolf Leggewie "leggewie" +Jim Leiterman +Matt Lewandowsky +Chenbo Li +Rob Loach Duncan Lock "dflock" Mark Lodato "itsr0y" Fridvin Logi "phillip_j_fry" -Lostech "lostech" +Michael Lojkovic +Borja Lorente Escobar Georg Lukas "ge0rg" +Artem Lukoyanov Michael Madsen "pidgeot" +Matthias Mailander +Narek Mailian +Christoph Mallon +Engin Manap Dmitry Marakasov "amdmi3" Alejandro Marzini "vgvgf" Connor McLeod "mcleod2032" Mickey McMurray "metafox" -Vladimir Menshakov "megath" Adam Metcalf "gamblore" +Nicola Mettifogo Frank Meyering "frank_m24" Gael Le Migno "kilobug" +Etienne Millon Andy Molloy "maloi" -Sean Murrau "lightcast" +Omer Mor Armin Mueller "arm_in" +Sean Murrau "lightcast" Andrea Musuruane "musuruan" KO Myung-Hun "lvzuufx" Markus Napp "meist3r" Peter Naulls "pnaulls" Christian Neumair "mannythegnome" -Nicos "anarxia" -Juha Niemimaki "capehill" +Hannes Niederhausen Markus Niemisto "niemisto" -ole +Bastien Nocera +Jody Northup +Julian Ospald +Christopher Page Chris Paras "paras_rasmatazz" Aubin Paul "outlyer" +Michael Pearce Vincent Pelletier "subdino" -phi1 -Pix2 "pix2" +Jussi Pitkanen Carsten Pohl "carstenpohl" +Tony Puccinelli Markus Pyykko "mankeli" -Richard "trinity78" -Felix Riemann "kirschsaft" +Rodrigo Rebello +Alexander Reim Thomas Richter "thorfdbg" +Felix Riemann "kirschsaft" Timo Roehling "t1m0" -Andreas Roever "roever" Jonathan Rogers "jonner" +Enrico Rolfi +Doron Rosenberg Marek Roth "logicdeluxe" +David Russo Uwe Ryssel "uweryssel" -Simon Sawatzki "simsaw" -Scarlatti "escarlate" -Daniel Schepler "dschepler" Florian Schmitt "fatpenguin" Mark Schreiber "mark7" Ben Shadwick "benshadwick" +Rodrigo Silva Jean-Yves Simon "lethalwp" Andrej Sinicyn "andrej4000" -Andre Souza "luke_br" -spookypeanut "spookypeanut" +Dmitry Smirnov Steve Stavropoulos "isnothere" Daniel Steinberger "amorphousshape" Sven Strothoff "dataslayer" Andrea Suatoni "mrhandler" -tbcarey -Tim "tipabu" +Max Tabachenko +DOSBox Team +Sarien Team +Joel Teichroeb +Jimmi Thogersen +Alexander Tkachov +Pino Toscano Luigi Toscano "ltosky" Xavier Trochu "xtrochu" +Vasyl Tsvirkunov Michal Tulacek "tutchek" Michael Udaltsov "cccp99" +Joni Vahamaki Kristof Vansant "lupusbe" +Aaryaman Vasishta Tim Walters "realmz" -David Weinehall "weine" Eric A. Welsh "eweish42" Yudhi Widyatama "yudhi97" +Jakub Wilk +Kieron Wilkinson Robert Wohlrab "moshroum" -Xanathar "xanathar" +James Woodcock Grant Yeager "glo_kidd" Benjamin W. Zale "junior_aepi" -Yotam Barnoy "bluddy" -Tom Frost "TomFrost" +Kamil Zbrog +Michal Ziabkowski +Bramvandijk "bramvandijk" +Canadacow +countingpine +Damien +dc france "erwan2004" +dewt "mncl" +dubsdj +Florent "flobo" +Florob "florob" +j0tt +Jellby "jellby" +Joerg "macdrega" +Lostech "lostech" +Nicos "anarxia" +ole +phi1 +Pix2 "pix2" +Richard "trinity78" +Scarlatti "escarlate" +the rara avis "theraraavis" +Tim "tipabu" +vandalo +Xanathar "xanathar" @@ -1,7 +1,7 @@ For a more comprehensive changelog of the latest experimental code, see: https://github.com/scummvm/scummvm/commits/ -1.9.0 (XXXX-XX-XX) +1.9.0 (YYYY-MM-DD) AGI: - Added support for Hercules rendering. Both green and amber modes are supported. @@ -10,7 +10,11 @@ For a more comprehensive changelog of the latest experimental code, see: - Added optional "pause, when entering commands" feature, that was only available in the original interpreter for Hercules rendering. -1.8.1 (XXXX-XX-XX) +1.8.1 (2016-05-25) + New ports: + - Added Nintendo 3DS port. + - Added Android SDL port. + General: - Removed TESTING flag from several supported games. - Added Chinese Pinyin translation. @@ -48,6 +52,7 @@ For a more comprehensive changelog of the latest experimental code, see: Lab: - Fixed lock-up during ending sequence. - Improved internal game controls. + - Fixed lock-up during some in-game animations. SAGA: - Fixed user interface colors in the French and German versions of I Have No @@ -835,7 +835,7 @@ then consult: <http://wiki.scummvm.org/index.php/HOWTO-Mac_Games> Although it primarily talks about SCUMM games, it mentions the -"HFVExplorer" utility which you need to extract the files. Note that you +"HFSExplorer" utility which you need to extract the files. Note that you have to put the speech data "Inherit the Earth Voices" in the same directory as the game data which is stored in: @@ -1205,7 +1205,7 @@ a Macintosh for this, accessing the CD/floppy data might be tricky. The reason for this is that the mac uses a special disk format called HFS which other systems usually do not support. However, there are various free tools which allow reading such HFS volumes. For example -"HFVExplorer" for Windows and "hfsutils" for Linux and other Unix-like +"HFSExplorer" for Windows and "hfsutils" for Linux and other Unix-like operating systems. Most of the newer games on the Macintosh shipped with only a single data diff --git a/audio/decoders/aiff.cpp b/audio/decoders/aiff.cpp index e1949ebb07..253b36dec0 100644 --- a/audio/decoders/aiff.cpp +++ b/audio/decoders/aiff.cpp @@ -129,6 +129,8 @@ RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, Dispos foundSSND = true; /* uint32 offset = */ stream->readUint32BE(); /* uint32 blockAlign = */ stream->readUint32BE(); + if (dataStream) + delete dataStream; dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse); break; case MKTAG('F', 'V', 'E', 'R'): @@ -154,7 +156,7 @@ RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, Dispos return 0; default: debug(1, "Skipping AIFF '%s' chunk", tag2str(tag)); - break; + break; } stream->seek(pos + length + (length & 1)); // ensure we're also word-aligned @@ -203,7 +205,7 @@ RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, Dispos if (codec == MKTAG('s', 'o', 'w', 't')) rawFlags |= Audio::FLAG_LITTLE_ENDIAN; - return makeRawStream(dataStream, rate, rawFlags); + return makeRawStream(dataStream, rate, rawFlags); } case MKTAG('i', 'm', 'a', '4'): // TODO: Use QT IMA ADPCM @@ -212,7 +214,7 @@ RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, Dispos case MKTAG('Q', 'D', 'M', '2'): // TODO: Need to figure out how to integrate this // (But hopefully never needed) - warning("Unhandled AIFF-C QDM2 compression"); + warning("Unhandled AIFF-C QDM2 compression"); break; case MKTAG('A', 'D', 'P', '4'): // ADP4 on 3DO diff --git a/backends/events/androidsdl/androidsdl-events.cpp b/backends/events/androidsdl/androidsdl-events.cpp new file mode 100644 index 0000000000..bd8045ec62 --- /dev/null +++ b/backends/events/androidsdl/androidsdl-events.cpp @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +#if defined(ANDROIDSDL) + +#include "backends/events/androidsdl/androidsdl-events.h" +#include "backends/platform/androidsdl/androidsdl-sdl.h" +#include <SDL_screenkeyboard.h> + +bool AndroidSdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) { + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = Common::EVENT_LBUTTONDOWN; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = Common::EVENT_RBUTTONDOWN; +#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN) + else if (ev.button.button == SDL_BUTTON_WHEELUP) + event.type = Common::EVENT_WHEELUP; + else if (ev.button.button == SDL_BUTTON_WHEELDOWN) + event.type = Common::EVENT_WHEELDOWN; +#endif +#if defined(SDL_BUTTON_MIDDLE) + else if (ev.button.button == SDL_BUTTON_MIDDLE) { + event.type = Common::EVENT_MBUTTONDOWN; + + static int show_onscreen=0; + if (show_onscreen==0) { + SDL_ANDROID_SetScreenKeyboardShown(0); + show_onscreen++; + } + else if (show_onscreen==1) { + SDL_ANDROID_SetScreenKeyboardShown(1); + show_onscreen++; + } + if (show_onscreen==2) + show_onscreen=0; + } +#endif + else + return false; + + processMouseEvent(event, ev.button.x, ev.button.y); + + return true; +} + +bool AndroidSdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) { + if (false) {} + + if (ev.key.keysym.sym == SDLK_LCTRL) { + ev.key.keysym.sym = SDLK_F5; + } else { + // Let the events fall through if we didn't change them, this may not be the best way to + // set it up, but i'm not sure how sdl would like it if we let if fall through then redid it though. + // and yes i have an huge terminal size so i dont wrap soon enough. + event.type = Common::EVENT_KEYDOWN; + event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; + event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode); + } + + return false; +} + +#endif diff --git a/backends/events/androidsdl/androidsdl-events.h b/backends/events/androidsdl/androidsdl-events.h new file mode 100644 index 0000000000..bca712e579 --- /dev/null +++ b/backends/events/androidsdl/androidsdl-events.h @@ -0,0 +1,37 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if !defined(BACKEND_EVENTS_SDL_ANDROIDSDL_H) && !defined(DISABLE_DEFAULT_EVENTMANAGER) +#define BACKEND_EVENTS_SDL_ANDROIDSDL_H + +#include "backends/events/sdl/sdl-events.h" + +/** + * SDL events manager for ANDROIDSDL + */ +class AndroidSdlEventSource : public SdlEventSource { +protected: + virtual bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event); + virtual bool remapKey(SDL_Event &ev, Common::Event &event); +}; + +#endif diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 7b56a0a955..00e2f25cbc 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -872,6 +872,21 @@ uint32 SdlEventSource::obtainUnicode(const SDL_keysym keySym) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Event events[2]; + // Update the event queue here to give SDL a chance to insert TEXTINPUT + // events for KEYDOWN events. Otherwise we have a high chance that on + // Windows the TEXTINPUT event is not in the event queue at this point. + // In this case we will get two events with ascii values due to mapKey + // and dispatchSDLEvent. This results in nasty double input of characters + // in the GUI. + // + // FIXME: This is all a bit fragile because in mapKey we derive the ascii + // value from the key code if no unicode value is given. This is legacy + // behavior and should be removed anyway. If that is removed, we might not + // even need to do this peeking here but instead can rely on the + // SDL_TEXTINPUT case in dispatchSDLEvent to introduce keydown/keyup with + // proper ASCII values (but with KEYCODE_INVALID as keycode). + SDL_PumpEvents(); + // In SDL2, the unicode field has been removed from the keysym struct. // Instead a SDL_TEXTINPUT event is generated on key combinations that // generates unicode. diff --git a/backends/graphics/androidsdl/androidsdl-graphics.cpp b/backends/graphics/androidsdl/androidsdl-graphics.cpp new file mode 100644 index 0000000000..23a1a86dd6 --- /dev/null +++ b/backends/graphics/androidsdl/androidsdl-graphics.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +#if defined(ANDROIDSDL) + +#include "backends/graphics/androidsdl/androidsdl-graphics.h" +#include "backends/events/androidsdl/androidsdl-events.h" +#include "common/mutex.h" +#include "common/textconsole.h" +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/scaler.h" +#include "graphics/scaler/aspect.h" +#include "graphics/scaler/downscaler.h" +#include "graphics/surface.h" + +AndroidSdlGraphicsManager::AndroidSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window) + : SurfaceSdlGraphicsManager(sdlEventSource, window) { +} + +#endif diff --git a/backends/graphics/androidsdl/androidsdl-graphics.h b/backends/graphics/androidsdl/androidsdl-graphics.h new file mode 100644 index 0000000000..b7ca7c1de8 --- /dev/null +++ b/backends/graphics/androidsdl/androidsdl-graphics.h @@ -0,0 +1,34 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_ANDROIDSDL_H +#define BACKENDS_GRAPHICS_SDL_ANDROIDSDL_H + +#include "backends/graphics/surfacesdl/surfacesdl-graphics.h" + +class AndroidSdlGraphicsManager : public SurfaceSdlGraphicsManager { +public: + AndroidSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window); + +}; + +#endif diff --git a/backends/module.mk b/backends/module.mk index 2e100d215d..7574db4009 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -122,6 +122,12 @@ MODULE_OBJS += \ taskbar/win32/win32-taskbar.o endif +ifeq ($(BACKEND),androidsdl) +MODULE_OBJS += \ + events/androidsdl/androidsdl-events.o \ + graphics/androidsdl/androidsdl-graphics.o +endif + ifdef AMIGAOS MODULE_OBJS += \ fs/amigaos4/amigaos4-fs.o \ diff --git a/backends/platform/3ds/3ds.mk b/backends/platform/3ds/3ds.mk new file mode 100644 index 0000000000..7ab58995f6 --- /dev/null +++ b/backends/platform/3ds/3ds.mk @@ -0,0 +1,64 @@ +TARGET := scummvm + +APP_TITLE := ScummVM +APP_DESCRIPTION := Point-and-click adventure game engines +APP_AUTHOR := ScummVM Team +APP_ICON := backends/platform/3ds/app/icon.png + +APP_RSF := backends/platform/3ds/app/scummvm.rsf +APP_BANNER_IMAGE:= backends/platform/3ds/app/banner.png +APP_BANNER_AUDIO:= backends/platform/3ds/app/banner.wav + +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft +CXXFLAGS += -std=gnu++11 +ASFLAGS += -mfloat-abi=hard +LDFLAGS += -specs=3dsx.specs $(ARCH) -L$(DEVKITPRO)/libctru/lib -L$(DEVKITPRO)/portlibs/3ds/lib + +.PHONY: clean_3ds + +clean: clean_3ds + +clean_3ds: + $(RM) $(TARGET).3dsx + $(RM) $(TARGET).cia + +$(TARGET).smdh: $(APP_ICON) + @bannertool makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i $(APP_ICON) -o $@ + @echo built ... $(notdir $@) + +$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh + @3dsxtool $< $@ --smdh=$(TARGET).smdh + @echo built ... $(notdir $@) + +$(TARGET).bnr: $(APP_BANNER_IMAGE) $(APP_BANNER_AUDIO) + @bannertool makebanner -o $@ -i $(APP_BANNER_IMAGE) -a $(APP_BANNER_AUDIO) + @echo built ... $(notdir $@) + +$(TARGET).cia: $(EXECUTABLE) $(APP_RSF) $(TARGET).smdh $(TARGET).bnr + @makerom -f cia -target t -exefslogo -o $@ -elf $(EXECUTABLE) -rsf $(APP_RSF) -banner $(TARGET).bnr -icon $(TARGET).smdh + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as + $(eval FILEPATH := $(patsubst %.shbin.o,%.shbin,$@)) + $(eval FILE := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) + picasso -o $(FILEPATH) $1 + bin2s $(FILEPATH) | $(AS) -o $@ + echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(FILEPATH) | tr . _)`.h + echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(FILEPATH) | tr . _)`.h + echo "extern const u32" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(FILEPATH) | tr . _)`.h +endef + +%.shbin.o : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) diff --git a/backends/platform/3ds/README b/backends/platform/3ds/README new file mode 100644 index 0000000000..516e694f64 --- /dev/null +++ b/backends/platform/3ds/README @@ -0,0 +1,185 @@ +ScummVM 3DS README +------------------------------------------------------------------------ + +Table of Contents: +------------------ +1.0) Installation + * 1.1 3DSX installation + * 1.2 CIA installation +2.0) Controls + * 2.1 Default key mappings + * 2.2 Hover mode + * 2.3 Drag mode +3.0) Supported Games +4.0) Compiling + * 4.1 Prerequisites + * * 4.1.1 Compiling third-party libraries + * 4.2 Compiling ScummVM + * 4.3 Warning for 3DSX build + + + +1.0) Installation +----------------- +There are two possible formats to be used: 3DSX and CIA (recommended). +The 3DSX format is exclusively used by the Homebrew Launcher and its derivatives. +The CIA format can be installed directly to the 3DS home menu and can be launched +using any CFW (Custom Firmware) of your choice. + +Installing the Homebrew Launcher or any CFW is beyond the scope of this README. +Look elsewhere to see how to install those if you do not already have them set up. + + +1.1) 3DSX installation +---------------- +The CIA format is recommended for stability and maximum game support. If that is +not an option, you will need one of a collection of 3DS titles installed on your +system in order to properly launch ScummVM as a 3DSX. This is because the +Homebrew Launcher hijacks other processes to run 3DSX homebrew, and ScummVM is a +particularly large homebrew that can't be launched with the resources provided +by standard system applications. + +You will need one of the following (installed or physically in cart slot): + +- Youtube +- Monster Hunter 4 Ultimate Special Demo +- Monster Hunter 4 Ultimate +- Monster Hunter 4G +- Super Smash Bros. for Nintendo 3DS Demo +- Super Smash Bros. for Nintendo 3DS Special Demo +- Super Smash Bros. for Nintendo 3DS + +Once you have one of the above, you need to merely extract all ScummVM 3DS files +to the root of your SD card so that all files reside in the /3ds/scummvm/ directory. + + +1.2) CIA installation +--------------------- +The CIA format requires a DSP binary dump saved on your SD card as /3ds/dspfirm.cdc +for proper audio support. You can search online to find software to dump this. +Not having this file will cause many problems with games that need audio, sometimes +even crashing, so this is NOT considered optional. + +Using any CIA installation software (search elsewhere for that), you need to install +the scummvm.cia file. Then, just like what is done with the 3DSX installation, you +need to extract all ScummVM 3DS files (scummvm.cia excluded) to the root of your SD +card so that all files reside in the /3ds/scummvm/ directory. + + + +2.0) Controls +------------- + +2.1) Default key mappings +------------------------- +The D-Pad and A/B/X/Y buttons have mirrored usage. So they do the same things +depending on if you're right or left-handed. + +| Buttons | Function | +|------------|--------------------------------| +| A / D-left | Left-click | +| X / D-up | Right-click | +| B / D-down | ESC (skips cutscenes and such) | +| L | Use virtual keyboard | +| R | Toggle hover/drag modes | +| Start | Open game menu | +| Select | Open 3DS config menu | +| Circle Pad | Move the cursor | + + +2.2) Hover mode +--------------- +When you use the touchscreen, you are simulating the mere moving of the mouse. You +can click only with taps, meaning it is impossible to drag stuff or hold down a +mouse button without using buttons mapped to right/left-click. + + +2.3) Drag mode +-------------- +Every time you touch and release the touchscreen, you are simulating the click and +release of the mouse buttons. At the moment, this is only a left-click. + + + +3.0) Supported Games +-------------------- +The full game engine compatibility list can be found here: +http://scummvm.org/compatibility/ + +While all the above games should run on the 3DS (report if they do not), there are +many games which are unplayable due to the lack of CPU speed on the 3DS. So if +you play any games that run really slow, this is not considered a bug, but rather +a hardware limitation. Though possible GPU optimizations are always in the works. +The New 3DS console has much better performance, but there are still many newer and +high-resolution games that cannot be played. A list of these unplayable games and +game engines will eventually be listed here. + + + +4.0) Compiling +-------------- + +4.1) Prerequisites +------------------ + - devkitARM (presumably with libctru, picasso and such) + - citro3d + - Optional: You should compile third-party libraries for the 3ds (commonly referred + to as portlibs in the devkitPRO community). Some games requires these to operate + properly. + + +4.1.1) Compiling third-party libraries +-------------------------------------- +Most libraries used can be compiled with same commands and configuration flags. + +It is assumed that you have these environment variables defined: + - DEVKITPRO Your root devkitPro directory + - DEVKITARM Your root devkitARM directory (probably same as $DEVKITPRO/devkitARM) + - CTRULIB Your root libctru directory (probably same as $DEVKITPRO/libctru) + +In the source directory of the library: + - $ export PORTLIBS=$DEVKITPRO/portlibs/armv6k + - $ export PATH=$DEVKITARM/bin:$PATH + - $ export PKG_CONFIG_PATH=$PORTLIBS/lib/pkgconfig + - $ export CFLAGS="-g -march=armv6k -mtune=mpcore -mfloat-abi=hard -O2 + -mword-relocations -ffunction-sections -fdata-sections" + - $ export CPPFLAGS="-I$PORTLIBS/include -I$CTRULIB/include" + - $ export LDFLAGS="-L$PORTLIBS/lib" + - $ mkdir -p $PORTLIBS + - $ ./configure --prefix=$PORTLIBS --host=arm-none-eabi --disable-shared + --enable-static + - $ make + - $ make install + +Useful libraries (and special config flags needed): + - zlib + - libpng + - libjpeg + - freetype2 --without-bzip2 --without-harfbuzz + - libmad + - tremor + - flac --disable-cpplibs --without-flac + - faad + + +4.2) Compiling ScummVM +---------------------- + - $ ./configure --host=3ds + - $ make + +Additionally compile to specific formats to be used on the 3ds: + - $ make scummvm.3dsx + - $ make scummvm.cia + + +4.3) Warning for 3DSX build +--------------------------- +The above configuration command will include all game engines by default and will +likely be too massive to run using the 3DSX format. Until dynamic modules are figured +out, you should configure engines like this for 3DSX builds: + + - $ ./configure --host=3ds --disable-all-engines--enable-engine=scumm-7-8,myst,riven, + sword1,sword2,sword25,sci,lure,sky,agi,agos + +Choose whatever engines you want, but if the ELF's .text section exceeds ~10MB, it +won't be playable unless it's a CIA. diff --git a/backends/platform/3ds/app/banner.png b/backends/platform/3ds/app/banner.png Binary files differnew file mode 100644 index 0000000000..a3b02150ec --- /dev/null +++ b/backends/platform/3ds/app/banner.png diff --git a/backends/platform/3ds/app/banner.wav b/backends/platform/3ds/app/banner.wav Binary files differnew file mode 100644 index 0000000000..e0b684b62f --- /dev/null +++ b/backends/platform/3ds/app/banner.wav diff --git a/backends/platform/3ds/app/icon.png b/backends/platform/3ds/app/icon.png Binary files differnew file mode 100644 index 0000000000..07022fbac1 --- /dev/null +++ b/backends/platform/3ds/app/icon.png diff --git a/backends/platform/3ds/app/scummvm.rsf b/backends/platform/3ds/app/scummvm.rsf new file mode 100644 index 0000000000..a4518949bb --- /dev/null +++ b/backends/platform/3ds/app/scummvm.rsf @@ -0,0 +1,219 @@ +BasicInfo: + Title : ScummVM + ProductCode : ScummVM + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +TitleInfo: + Category : Application + UniqueId : 0xFF321 + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : false # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: + CoreVersion : 2 + + # Exheader Format Version + DescVersion : 2 + + # Minimum Required Kernel Version (below is for 4.5.0) + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + + # ExtData + UseExtSaveData : false # enables ExtData + #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId + + # FS:USER Archive Access Permissions + # Uncomment as required + FileSystemAccess: + #- CategorySystemApplication + #- CategoryHardwareCheck + #- CategoryFileSystemTool + #- Debug + #- TwlCardBackup + #- TwlNandData + #- Boss + - DirectSdmc + #- Core + #- CtrNandRo + #- CtrNandRw + #- CtrNandRoWrite + #- CategorySystemSettings + #- CardBoard + #- ExportImportIvs + #- DirectSdmcWrite + #- SwitchCleanup + #- SaveDataMove + #- Shop + #- Shell + #- CategoryHomeMenu + + # Process Settings + MemoryType : Application # Application/System/Base + SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB + IdealProcessor : 0 + AffinityMask : 1 + Priority : 16 + MaxCpu : 0 # Let system decide + HandleTableSize : 0x200 + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : true + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : true + PermitMainFunctionArgument : true + CanShareDeviceMemory : true + RunnableOnSleep : false + SpecialMemoryArrange : true + + # New3DS Exclusive Process Settings + SystemModeExt : 124MB # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + # Virtual Address Mappings + IORegisterMapping: + - 1ff00000-1ff7ffff # DSP memory + MemoryMapping: + - 1f000000-1f5fffff:r # VRAM + + # Accessible SVCs, <Name>:<ID> + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + Backdoor: 123 + + # Service List + # Maximum 34 services (32 if firmware is prior to 9.3.0) + ServiceAccessControl: + - cfg:u + - fs:USER + - gsp::Gpu + - hid:USER + - ndm:u + - pxi:dev + - APT:U + - ac:u + - act:u + - am:net + - boss:U + - cam:u + - cecd:u + - dsp::DSP + - frd:u + - http:C + - ir:USER + - ir:u + - ir:rst + - ldr:ro + - mic:u + - news:u + - nim:aoc + - nwm::UDS + - ptm:u + - qtm:u + - soc:U + - ssl:C + - y2r:u + - gsp::Lcd + + +SystemControlInfo: + SaveDataSize: 0K + RemasterVersion: 0 + StackSize: 0x40000 + + # Modules that run services listed above should be included below + # Maximum 48 dependencies + # If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves) + # So act, nfc and qtm are commented for 4.x support. Uncomment if you need these. + # <module name>:<module titleid> + Dependency: + ac: 0x0004013000002402 + #act: 0x0004013000003802 + am: 0x0004013000001502 + boss: 0x0004013000003402 + camera: 0x0004013000001602 + cecd: 0x0004013000002602 + cfg: 0x0004013000001702 + codec: 0x0004013000001802 + csnd: 0x0004013000002702 + dlp: 0x0004013000002802 + dsp: 0x0004013000001a02 + friends: 0x0004013000003202 + gpio: 0x0004013000001b02 + gsp: 0x0004013000001c02 + hid: 0x0004013000001d02 + http: 0x0004013000002902 + i2c: 0x0004013000001e02 + ir: 0x0004013000003302 + mcu: 0x0004013000001f02 + mic: 0x0004013000002002 + ndm: 0x0004013000002b02 + news: 0x0004013000003502 + #nfc: 0x0004013000004002 + nim: 0x0004013000002c02 + nwm: 0x0004013000002d02 + pdn: 0x0004013000002102 + ps: 0x0004013000003102 + ptm: 0x0004013000002202 + #qtm: 0x0004013020004202 + ro: 0x0004013000003702 + socket: 0x0004013000002e02 + spi: 0x0004013000002302 + ssl: 0x0004013000002f02 diff --git a/backends/platform/3ds/config.cpp b/backends/platform/3ds/config.cpp new file mode 100644 index 0000000000..117b979d9f --- /dev/null +++ b/backends/platform/3ds/config.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "osystem.h" +#include "options-dialog.h" +#include "common/config-manager.h" +#include <3ds.h> + +namespace _3DS { + +Config config; +static Common::String prefix = "3ds_"; + +static bool confGetBool(Common::String key, bool defaultVal) { + if (ConfMan.hasKey(prefix + key)) + return ConfMan.getBool(prefix + key); + return defaultVal; +} + +static void confSetBool(Common::String key, bool val) { + ConfMan.setBool(prefix + key, val); +} + +static int confGetInt(Common::String key, int defaultVal) { + if (ConfMan.hasKey(prefix + key)) + return ConfMan.getInt(prefix + key); + return defaultVal; +} + +static void confSetInt(Common::String key, int val) { + ConfMan.setInt(prefix + key, val); +} + +void loadConfig() { + config.showCursor = confGetBool("showcursor", true); + config.snapToBorder = confGetBool("snaptoborder", true); + config.stretchToFit = confGetBool("stretchtofit", false); + config.sensitivity = confGetInt("sensitivity", -5); + config.screen = confGetInt("screen", kScreenBoth); + + // Turn off the backlight of any screen not used + if (R_SUCCEEDED(gspLcdInit())) { + if (config.screen == kScreenTop) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_TOP); + GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM); + } else if (config.screen == kScreenBottom) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM); + GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP); + } else + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + + OSystem_3DS *osys = (OSystem_3DS *)g_system; + osys->updateConfig(); +} + +void saveConfig() { + confSetBool("showcursor", config.showCursor); + confSetBool("snaptoborder", config.snapToBorder); + confSetBool("stretchtofit", config.stretchToFit); + confSetInt("sensitivity", config.sensitivity); + confSetInt("screen", config.screen); + ConfMan.flushToDisk(); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/config.h b/backends/platform/3ds/config.h new file mode 100644 index 0000000000..c8b75736ad --- /dev/null +++ b/backends/platform/3ds/config.h @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CONFIG_3DS_H +#define CONFIG_3DS_H + +#include "common/str.h" + +namespace _3DS { + +struct Config { + bool showCursor; + bool snapToBorder; + bool stretchToFit; + int sensitivity; + int screen; +}; + +extern Config config; + +void loadConfig(); +void saveConfig(); + +} // namespace _3DS + +#endif // CONFIG_3DS_H diff --git a/backends/platform/3ds/gui.cpp b/backends/platform/3ds/gui.cpp new file mode 100644 index 0000000000..0883d5a102 --- /dev/null +++ b/backends/platform/3ds/gui.cpp @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/3ds/gui.h" +#include "common/system.h" + +StatusMessageDialog* StatusMessageDialog::_opened = 0; + +StatusMessageDialog::StatusMessageDialog(const Common::String &message, uint32 duration) + : MessageDialog(message, 0, 0) { + _timer = g_system->getMillis() + duration; + if (_opened) + _opened->close(); + _opened = this; +} + +void StatusMessageDialog::handleTickle() { + MessageDialog::handleTickle(); + if (g_system->getMillis() > _timer) + close(); +} + +void StatusMessageDialog::close() { + GUI::Dialog::close(); + if (_opened) + _opened = 0; +} diff --git a/backends/platform/3ds/gui.h b/backends/platform/3ds/gui.h new file mode 100644 index 0000000000..66c6547139 --- /dev/null +++ b/backends/platform/3ds/gui.h @@ -0,0 +1,41 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_3DS_H +#define GUI_3DS_H + +#include "gui/message.h" + +class StatusMessageDialog : public GUI::MessageDialog { +public: + StatusMessageDialog(const Common::String &message, uint32 duration); + + void handleTickle(); + +protected: + virtual void close(); + + uint32 _timer; + static StatusMessageDialog* _opened; +}; + +#endif // GUI_3DS_H diff --git a/backends/platform/3ds/main.cpp b/backends/platform/3ds/main.cpp new file mode 100644 index 0000000000..6cc2c5cf5d --- /dev/null +++ b/backends/platform/3ds/main.cpp @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "osystem.h" +#include <3ds.h> + +int main(int argc, char *argv[]) { + // Initialize basic libctru stuff + gfxInitDefault(); + cfguInit(); + osSetSpeedupEnable(true); +// consoleInit(GFX_TOP, NULL); + + g_system = new _3DS::OSystem_3DS(); + assert(g_system); + + // Invoke the actual ScummVM main entry point +// if (argc > 2) +// res = scummvm_main(argc-2, &argv[2]); +// else +// res = scummvm_main(argc, argv); + scummvm_main(0, nullptr); + + delete dynamic_cast<_3DS::OSystem_3DS*>(g_system); + + // Turn on both screen backlights before exiting. + if (R_SUCCEEDED(gspLcdInit())) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + + cfguExit(); + gfxExit(); + return 0; +} diff --git a/backends/platform/3ds/module.mk b/backends/platform/3ds/module.mk new file mode 100644 index 0000000000..3eb15aef81 --- /dev/null +++ b/backends/platform/3ds/module.mk @@ -0,0 +1,18 @@ +MODULE := backends/platform/3ds + +MODULE_OBJS := \ + main.o \ + shader.shbin.o \ + sprite.o \ + gui.o \ + config.o \ + options-dialog.o \ + osystem.o \ + osystem-graphics.o \ + osystem-audio.o \ + osystem-events.o + +# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. +MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) +OBJS := $(MODULE_OBJS) $(OBJS) +MODULE_DIRS += $(sort $(dir $(MODULE_OBJS))) diff --git a/backends/platform/3ds/options-dialog.cpp b/backends/platform/3ds/options-dialog.cpp new file mode 100644 index 0000000000..0f8bfd0c66 --- /dev/null +++ b/backends/platform/3ds/options-dialog.cpp @@ -0,0 +1,98 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "options-dialog.h" +#include "config.h" +#include "gui/dialog.h" +#include "gui/gui-manager.h" +#include "gui/widgets/list.h" +#include "gui/widgets/tab.h" +#include "osystem.h" +#include "engines/scumm/scumm.h" +#include "gui/widgets/popup.h" + +#include "common/translation.h" + +namespace _3DS { + +bool optionMenuOpened = false; + +OptionsDialog::OptionsDialog() : GUI::Dialog(20, 20, 280, 200) { + + optionMenuOpened = true; + + new GUI::ButtonWidget(this, 120, 180, 72, 16, _("~C~lose"), 0, GUI::kCloseCmd); + new GUI::ButtonWidget(this, 200, 180, 72, 16, _("~S~ave"), 0, GUI::kOKCmd); + + _showCursorCheckbox = new GUI::CheckboxWidget(this, 5, 5, 130, 20, _("Show mouse cursor"), 0, 0, 'T'); + _showCursorCheckbox->setState(config.showCursor); + + _snapToBorderCheckbox = new GUI::CheckboxWidget(this, 5, 22, 130, 20, _("Snap to edges"), 0, 0, 'T'); + _snapToBorderCheckbox->setState(config.snapToBorder); + + _stretchToFitCheckbox = new GUI::CheckboxWidget(this, 140, 5, 130, 20, _("Stretch to fit"), 0, 0, 'T'); + _stretchToFitCheckbox->setState(config.stretchToFit); + + new GUI::StaticTextWidget(this, 0, 60, 110, 15, _("Use Screen:"), Graphics::kTextAlignRight); + _screenRadioGroup = new GUI::RadiobuttonGroup(this, kScreenRadioGroup); + _screenTopRadioWidget = new GUI::RadiobuttonWidget(this, 120, 50, 60, 20, _screenRadioGroup, kScreenTop, _("Top")); + _screenBottomRadioWidget = new GUI::RadiobuttonWidget(this, 190, 50, 80, 20, _screenRadioGroup, kScreenBottom, _("Bottom")); + _screenBothRadioWidget = new GUI::RadiobuttonWidget(this, 155, 70, 80, 20, _screenRadioGroup, kScreenBoth, _("Both")); + _screenRadioGroup->setValue(config.screen); + + new GUI::StaticTextWidget(this, 0, 100, 110, 15, _("C-Pad Sensitivity:"), Graphics::kTextAlignRight); + _sensitivity = new GUI::SliderWidget(this, 115, 100, 160, 15, "TODO: Add tooltip", 1); + _sensitivity->setMinValue(-15); + _sensitivity->setMaxValue(30); + _sensitivity->setValue(config.sensitivity); + _sensitivity->setFlags(GUI::WIDGET_CLEARBG); +} + +OptionsDialog::~OptionsDialog() { + optionMenuOpened = false; +} + +void OptionsDialog::updateConfigManager() { + config.showCursor = _showCursorCheckbox->getState(); + config.snapToBorder = _snapToBorderCheckbox->getState(); + config.stretchToFit = _stretchToFitCheckbox->getState(); + config.sensitivity = _sensitivity->getValue(); + config.screen = _screenRadioGroup->getValue(); + saveConfig(); + loadConfig(); +} + +void OptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case GUI::kOKCmd: + updateConfigManager(); + // Fall through + case GUI::kCloseCmd: + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +} // namespace _3DS diff --git a/backends/platform/3ds/options-dialog.h b/backends/platform/3ds/options-dialog.h new file mode 100644 index 0000000000..6673b88e7b --- /dev/null +++ b/backends/platform/3ds/options-dialog.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef OPTIONS_DIALOG_3DS_H +#define OPTIONS_DIALOG_3DS_H + + +#include "common/scummsys.h" +#include "common/str.h" +#include "gui/object.h" +#include "gui/widget.h" +#include "gui/dialog.h" +#include "gui/widgets/tab.h" +#include "scumm/dialogs.h" + +namespace _3DS { + +enum { + kSave = 0x10000000, + kScreenRadioGroup, + kScreenTop, + kScreenBottom, + kScreenBoth, +}; + +extern bool optionMenuOpened; + +class OptionsDialog : public GUI::Dialog { + +public: + OptionsDialog(); + ~OptionsDialog(); + +protected: + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + void updateConfigManager(); + + GUI::SliderWidget *_sensitivity; + GUI::CheckboxWidget *_showCursorCheckbox; + GUI::CheckboxWidget *_snapToBorderCheckbox; + GUI::CheckboxWidget *_stretchToFitCheckbox; + + GUI::RadiobuttonGroup *_screenRadioGroup; + GUI::RadiobuttonWidget *_screenTopRadioWidget; + GUI::RadiobuttonWidget *_screenBottomRadioWidget; + GUI::RadiobuttonWidget *_screenBothRadioWidget; +}; + +} // namespace _3DS + +#endif // OPTIONS_DIALOG_3DS_H diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp new file mode 100644 index 0000000000..17e419c36d --- /dev/null +++ b/backends/platform/3ds/osystem-audio.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "osystem.h" +#include "audio/mixer.h" + +namespace _3DS { + +static bool hasAudio = false; + +static void audioThreadFunc(void *arg) { + Audio::MixerImpl *mixer = (Audio::MixerImpl *)arg; + OSystem_3DS *osys = (OSystem_3DS *)g_system; + + int i; + const int channel = 0; + int bufferIndex = 0; + const int bufferCount = 3; + const int bufferSize = 80000; // Can't be too small, based on delayMillis duration + const int sampleRate = mixer->getOutputRate(); + int sampleLen = 0; + uint32 lastTime = osys->getMillis(true); + uint32 time = lastTime; + ndspWaveBuf buffers[bufferCount]; + + for (i = 0; i < bufferCount; ++i) { + memset(&buffers[i], 0, sizeof(ndspWaveBuf)); + buffers[i].data_vaddr = linearAlloc(bufferSize); + buffers[i].looping = false; + buffers[i].status = NDSP_WBUF_FREE; + } + + ndspChnReset(channel); + ndspChnSetInterp(channel, NDSP_INTERP_LINEAR); + ndspChnSetRate(channel, sampleRate); + ndspChnSetFormat(channel, NDSP_FORMAT_STEREO_PCM16); + + while (!osys->exiting) { + osys->delayMillis(100); // Note: Increasing the delay requires a bigger buffer + + time = osys->getMillis(true); + sampleLen = (time - lastTime) * 22 * 4; // sampleRate / 1000 * channelCount * sizeof(int16); + lastTime = time; + + if (!osys->sleeping && sampleLen > 0) { + bufferIndex++; + bufferIndex %= bufferCount; + ndspWaveBuf *buf = &buffers[bufferIndex]; + + buf->nsamples = mixer->mixCallback(buf->data_adpcm, sampleLen); + if (buf->nsamples > 0) { + DSP_FlushDataCache(buf->data_vaddr, bufferSize); + ndspChnWaveBufAdd(channel, buf); + } + } + } + + for (i = 0; i < bufferCount; ++i) + linearFree(buffers[i].data_pcm8); +} + +void OSystem_3DS::initAudio() { + _mixer = new Audio::MixerImpl(this, 22050); + + hasAudio = R_SUCCEEDED(ndspInit()); + _mixer->setReady(false); + + if (hasAudio) { + s32 prio = 0; + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + audioThread = threadCreate(&audioThreadFunc, _mixer, 32 * 1048, prio - 1, -2, false); + } +} + +void OSystem_3DS::destroyAudio() { + if (hasAudio) { + threadJoin(audioThread, U64_MAX); + threadFree(audioThread); + ndspExit(); + } + + delete _mixer; + _mixer = 0; +} + +Audio::Mixer *OSystem_3DS::getMixer() { + assert(_mixer); + return _mixer; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp new file mode 100644 index 0000000000..ae8a9b8b2b --- /dev/null +++ b/backends/platform/3ds/osystem-events.cpp @@ -0,0 +1,302 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/timer/default/default-timer.h" +#include "engines/engine.h" +#include "gui.h" +#include "options-dialog.h" +#include "config.h" +#include "osystem.h" + +namespace _3DS { + +static Common::Mutex *eventMutex; +static InputMode inputMode = MODE_DRAG; +static aptHookCookie cookie; +static bool optionMenuOpening = false; +static Common::String messageOSD; +static bool showMessageOSD = false; + +static void pushEventQueue(Common::Queue<Common::Event> *queue, Common::Event &event) { + Common::StackLock lock(*eventMutex); + queue->push(event); +} + +static void eventThreadFunc(void *arg) { + OSystem_3DS *osys = (OSystem_3DS *)g_system; + auto eventQueue = (Common::Queue<Common::Event> *)arg; + + uint32 touchStartTime = osys->getMillis(); + touchPosition lastTouch = {0, 0}; + bool isRightClick = false; + float cursorX = 0; + float cursorY = 0; + float cursorDeltaX = 0; + float cursorDeltaY = 0; + int circleDeadzone = 20; + int borderSnapZone = 6; + Common::Event event; + + while (!osys->exiting) { + do { + osys->delayMillis(10); + } while (osys->sleeping && !osys->exiting); + + hidScanInput(); + touchPosition touch; + circlePosition circle; + u32 held = hidKeysHeld(); + u32 keysPressed = hidKeysDown(); + u32 keysReleased = hidKeysUp(); + + // C-Pad used to control the cursor + hidCircleRead(&circle); + if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone) + circle.dx = 0; + if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone) + circle.dy = 0; + cursorDeltaX = (0.0002f + config.sensitivity / 100000.f) * circle.dx * abs(circle.dx); + cursorDeltaY = (0.0002f + config.sensitivity / 100000.f) * circle.dy * abs(circle.dy); + + // Touch screen events + if (held & KEY_TOUCH) { + hidTouchRead(&touch); + if (config.snapToBorder) { + if (touch.px < borderSnapZone) + touch.px = 0; + if (touch.px > 319 - borderSnapZone) + touch.px = 319; + if (touch.py < borderSnapZone) + touch.py = 0; + if (touch.py > 239 - borderSnapZone) + touch.py = 239; + } + cursorX = touch.px; + cursorY = touch.py; + osys->transformPoint(touch); + + osys->warpMouse(touch.px, touch.py); + event.mouse.x = touch.px; + event.mouse.y = touch.py; + + if (keysPressed & KEY_TOUCH) { + touchStartTime = osys->getMillis(); + isRightClick = (held & KEY_X || held & KEY_DUP); + if (inputMode == MODE_DRAG) { + event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; + pushEventQueue(eventQueue, event); + } + } else if (touch.px != lastTouch.px || touch.py != lastTouch.py) { + event.type = Common::EVENT_MOUSEMOVE; + pushEventQueue(eventQueue, event); + } + + lastTouch = touch; + } else if (keysReleased & KEY_TOUCH) { + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + if (inputMode == MODE_DRAG) { + event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; + pushEventQueue(eventQueue, event); + } else if (osys->getMillis() - touchStartTime < 200) { + // Process click in MODE_HOVER + event.type = Common::EVENT_MOUSEMOVE; + pushEventQueue(eventQueue, event); + event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; + pushEventQueue(eventQueue, event); + event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; + pushEventQueue(eventQueue, event); + } + } else if (cursorDeltaX != 0 || cursorDeltaY != 0) { + cursorX += cursorDeltaX; + cursorY -= cursorDeltaY; + if (cursorX < 0) cursorX = 0; + if (cursorY < 0) cursorY = 0; + if (cursorX > 320) cursorX = 320; + if (cursorY > 240) cursorY = 240; + lastTouch.px = cursorX; + lastTouch.py = cursorY; + osys->transformPoint(lastTouch); + osys->warpMouse(lastTouch.px, lastTouch.py); + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + event.type = Common::EVENT_MOUSEMOVE; + pushEventQueue(eventQueue, event); + } + + // Button events + if (keysPressed & KEY_R) { + if (inputMode == MODE_DRAG) { + inputMode = MODE_HOVER; + osys->displayMessageOnOSD("Hover Mode"); + } else { + inputMode = MODE_DRAG; + osys->displayMessageOnOSD("Drag Mode"); + } + } + if (keysPressed & KEY_A || keysPressed & KEY_DLEFT || keysReleased & KEY_A || keysReleased & KEY_DLEFT) { + // SIMULATE LEFT CLICK + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + if (keysPressed & KEY_A || keysPressed & KEY_DLEFT) + event.type = Common::EVENT_LBUTTONDOWN; + else + event.type = Common::EVENT_LBUTTONUP; + pushEventQueue(eventQueue, event); + } + if (keysPressed & KEY_X || keysPressed & KEY_DUP || keysReleased & KEY_X || keysReleased & KEY_DUP) { + // SIMULATE RIGHT CLICK + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + if (keysPressed & KEY_X || keysPressed & KEY_DUP) + event.type = Common::EVENT_RBUTTONDOWN; + else + event.type = Common::EVENT_RBUTTONUP; + pushEventQueue(eventQueue, event); + } + if (keysPressed & KEY_L) { + event.type = Common::EVENT_VIRTUAL_KEYBOARD; + pushEventQueue(eventQueue, event); + } + if (keysPressed & KEY_START) { + event.type = Common::EVENT_MAINMENU; + pushEventQueue(eventQueue, event); + } + if (keysPressed & KEY_SELECT) { + if (!optionMenuOpened) + optionMenuOpening = true; + } + if (keysPressed & KEY_B || keysReleased & KEY_B || keysPressed & KEY_DDOWN || keysReleased & KEY_DDOWN) { + if (keysPressed & KEY_B || keysPressed & KEY_DDOWN) + event.type = Common::EVENT_KEYDOWN; + else + event.type = Common::EVENT_KEYUP; + event.kbd.keycode = Common::KEYCODE_ESCAPE; + event.kbd.ascii = Common::ASCII_ESCAPE; + event.kbd.flags = 0; + pushEventQueue(eventQueue, event); + } + + // TODO: EVENT_PREDICTIVE_DIALOG + // EVENT_SCREEN_CHANGED + } +} + +static void aptHookFunc(APT_HookType hookType, void *param) { + OSystem_3DS *osys = (OSystem_3DS *)g_system; + + switch (hookType) { + case APTHOOK_ONSUSPEND: + case APTHOOK_ONSLEEP: + if (g_engine) + g_engine->pauseEngine(true); + osys->sleeping = true; + if (R_SUCCEEDED(gspLcdInit())) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + break; + case APTHOOK_ONRESTORE: + case APTHOOK_ONWAKEUP: + if (g_engine) + g_engine->pauseEngine(false); + osys->sleeping = false; + loadConfig(); + break; + default: { + Common::StackLock lock(*eventMutex); + Common::Event event; + event.type = Common::EVENT_QUIT; + g_system->getEventManager()->pushEvent(event); + } + } +} + +static void timerThreadFunc(void *arg) { + OSystem_3DS *osys = (OSystem_3DS *)arg; + DefaultTimerManager *tm = (DefaultTimerManager *)osys->getTimerManager(); + while (!osys->exiting) { + g_system->delayMillis(10); + tm->handler(); + } +} + +void OSystem_3DS::initEvents() { + eventMutex = new Common::Mutex(); + s32 prio = 0; + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + _timerThread = threadCreate(&timerThreadFunc, this, 32 * 1024, prio - 1, -2, false); + _eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 32 * 1024, prio - 1, -2, false); + + aptHook(&cookie, aptHookFunc, this); +} + +void OSystem_3DS::destroyEvents() { + threadJoin(_timerThread, U64_MAX); + threadFree(_timerThread); + + threadJoin(_eventThread, U64_MAX); + threadFree(_eventThread); + delete eventMutex; +} + +void OSystem_3DS::transformPoint(touchPosition &point) { + if (!_overlayVisible) { + point.px = static_cast<float>(point.px) / _gameBottomTexture.getScaleX() - _gameBottomX; + point.py = static_cast<float>(point.py) / _gameBottomTexture.getScaleY() - _gameBottomY; + } +} + +void OSystem_3DS::displayMessageOnOSD(const char *msg) { + messageOSD = msg; + showMessageOSD = true; +} + +bool OSystem_3DS::pollEvent(Common::Event &event) { + if (showMessageOSD) { + showMessageOSD = false; + StatusMessageDialog dialog(messageOSD, 800); + dialog.runModal(); + } + + aptMainLoop(); // Call apt hook when necessary + + if (optionMenuOpening) { + optionMenuOpening = false; + OptionsDialog dialog; + if (g_engine) + g_engine->pauseEngine(true); + dialog.runModal(); + if (g_engine) + g_engine->pauseEngine(false); + } + + Common::StackLock lock(*eventMutex); + + if (_eventQueue.empty()) + return false; + + event = _eventQueue.pop(); + return true; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp new file mode 100644 index 0000000000..0cfd70c9cd --- /dev/null +++ b/backends/platform/3ds/osystem-graphics.cpp @@ -0,0 +1,517 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This _program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This _program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this _program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + */ + +#include "backends/platform/3ds/osystem.h" +#include "backends/platform/3ds/shader_shbin.h" +#include "common/rect.h" +#include "options-dialog.h" +#include "config.h" + +// Used to transfer the final rendered display to the framebuffer +#define DISPLAY_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | \ + GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ + GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +namespace _3DS { + +void OSystem_3DS::initGraphics() { + _pfGame = Graphics::PixelFormat::createFormatCLUT8(); + _pfGameTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + // Initialize the render targets + _renderTargetTop = + C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x0000000, 0); + C3D_RenderTargetSetOutput(_renderTargetTop, GFX_TOP, GFX_LEFT, + DISPLAY_TRANSFER_FLAGS); + + _renderTargetBottom = + C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(_renderTargetBottom, C3D_CLEAR_ALL, 0x00000000, 0); + C3D_RenderTargetSetOutput(_renderTargetBottom, GFX_BOTTOM, GFX_LEFT, + DISPLAY_TRANSFER_FLAGS); + + // Load and bind simple default shader (shader.v.pica) + _dvlb = DVLB_ParseFile((u32*)shader_shbin, shader_shbin_size); + shaderProgramInit(&_program); + shaderProgramSetVsh(&_program, &_dvlb->DVLE[0]); + C3D_BindProgram(&_program); + + _projectionLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "projection"); + _modelviewLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "modelView"); + + C3D_AttrInfo *attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord + + Mtx_OrthoTilt(&_projectionTop, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0); + Mtx_OrthoTilt(&_projectionBottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0); + + C3D_TexEnv *env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); + + C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL); + C3D_CullFace(GPU_CULL_NONE); +} + +void OSystem_3DS::destroyGraphics() { + _gameScreen.free(); + _gameTopTexture.free(); + _gameBottomTexture.free(); + _overlay.free(); + + shaderProgramFree(&_program); + DVLB_Free(_dvlb); + + C3D_RenderTargetDelete(_renderTargetTop); + C3D_RenderTargetDelete(_renderTargetBottom); + + C3D_Fini(); +} + +bool OSystem_3DS::hasFeature(OSystem::Feature f) { + return (f == OSystem::kFeatureCursorPalette || + f == OSystem::kFeatureOverlaySupportsAlpha); +} + +void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureCursorPalette: + _cursorPaletteEnabled = enable; + flushCursor(); + break; + default: + break; + } +} + +bool OSystem_3DS::getFeatureState(OSystem::Feature f) { + switch (f) { + case OSystem::kFeatureCursorPalette: + return _cursorPaletteEnabled; + default: + return false; + } +} + +const OSystem::GraphicsMode * +OSystem_3DS::getSupportedGraphicsModes() const { + return s_graphicsModes; +} + +int OSystem_3DS::getDefaultGraphicsMode() const { + return GFX_LINEAR; +} + +bool OSystem_3DS::setGraphicsMode(int mode) { + return true; +} + +void OSystem_3DS::resetGraphicsScale() { + debug("resetGraphicsScale"); +} + +int OSystem_3DS::getGraphicsMode() const { + return GFX_LINEAR; +} +void OSystem_3DS::initSize(uint width, uint height, + const Graphics::PixelFormat *format) { + debug("3ds initsize w:%d h:%d", width, height); + _gameWidth = width; + _gameHeight = height; + _gameTopTexture.create(width, height, _pfGameTexture); + _overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture); + + if (format) { + debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());; + _pfGame = *format; + } + + _gameScreen.create(width, height, _pfGame); + + _focusDirty = true; + _focusRect = Common::Rect(_gameWidth, _gameHeight); + + updateSize(); +} + +void OSystem_3DS::updateSize() { + if (config.stretchToFit) { + _gameTopX = _gameTopY = _gameBottomX = _gameBottomY = 0; + _gameTopTexture.setScale(400.f / _gameWidth, 240.f / _gameHeight); + _gameBottomTexture.setScale(320.f / _gameWidth, 240.f / _gameHeight); + } else { + float ratio = static_cast<float>(_gameWidth) / _gameHeight; + + if (ratio > 400.f / 240.f) { + float r = 400.f / _gameWidth; + _gameTopTexture.setScale(r, r); + _gameTopX = 0; + _gameTopY = (240.f - r * _gameHeight) / 2.f; + } else { + float r = 240.f / _gameHeight; + _gameTopTexture.setScale(r, r); + _gameTopY = 0; + _gameTopX = (400.f - r * _gameWidth) / 2.f; + } + if (ratio > 320.f / 240.f) { + float r = 320.f / _gameWidth; + _gameBottomTexture.setScale(r, r); + _gameBottomX = 0; + _gameBottomY = (240.f - r * _gameHeight) / 2.f; + } else { + float r = 240.f / _gameHeight; + _gameBottomTexture.setScale(r, r); + _gameBottomY = 0; + _gameBottomX = (320.f - r * _gameWidth) / 2.f; + } + } + _gameTopTexture.setPosition(_gameTopX, _gameTopY); + _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY); + if (_overlayVisible) + _cursorTexture.setScale(1.f, 1.f); + else if (config.screen == kScreenTop) + _cursorTexture.setScale(_gameTopTexture.getScaleX(), _gameTopTexture.getScaleY()); + else + _cursorTexture.setScale(_gameBottomTexture.getScaleX(), _gameBottomTexture.getScaleY()); +} + +Common::List<Graphics::PixelFormat> OSystem_3DS::getSupportedFormats() const { + Common::List<Graphics::PixelFormat> list; + list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // GPU_RGBA8 + list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // GPU_RGB565 +// list.push_back(Graphics::PixelFormat(3, 0, 0, 0, 8, 0, 8, 16, 0)); // GPU_RGB8 + list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // RGB555 (needed for FMTOWNS?) + list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551 + list.push_back(Graphics::PixelFormat::createFormatCLUT8()); + return list; +} + +void OSystem_3DS::beginGFXTransaction() { + // +} +OSystem::TransactionError OSystem_3DS::endGFXTransaction() { + return OSystem::kTransactionSuccess; +} + +void OSystem_3DS::setPalette(const byte *colors, uint start, uint num) { + assert(start + num <= 256); + memcpy(_palette + 3 * start, colors, 3 * num); + + // Manually update all color that were changed + if (_gameScreen.format.bytesPerPixel == 1) { + flushGameScreen(); + } +} +void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) { + assert(start + num <= 256); + memcpy(colors, _palette + 3 * start, 3 * num); +} + +void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x, + int y, int w, int h) { + Common::Rect rect(x, y, x+w, y+h); + _gameScreen.copyRectToSurface(buf, pitch, x, y, w, h); + Graphics::Surface subSurface = _gameScreen.getSubArea(rect); + + Graphics::Surface *convertedSubSurface = subSurface.convertTo(_pfGameTexture, _palette); + _gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h)); + + convertedSubSurface->free(); + delete convertedSubSurface; + _gameTopTexture.markDirty(); +} + +void OSystem_3DS::flushGameScreen() { + Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette); + _gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + _gameTopTexture.markDirty(); + converted->free(); + delete converted; +} + +Graphics::Surface *OSystem_3DS::lockScreen() { + return &_gameScreen; +} +void OSystem_3DS::unlockScreen() { + flushGameScreen(); +} + +void OSystem_3DS::updateScreen() { + + if (sleeping || exiting) + return; + +// updateFocus(); + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + // Render top screen + C3D_FrameDrawOn(_renderTargetTop); + if (config.screen == kScreenTop || config.screen == kScreenBoth) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTopTexture.getMatrix()); + _gameTopTexture.render(); + _gameTopTexture.render(); + if (_overlayVisible && config.screen == kScreenTop) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); + _overlay.render(); + } + if (_cursorVisible && config.showCursor && config.screen == kScreenTop) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix()); + _cursorTexture.render(); + } + } + + // Render bottom screen + C3D_FrameDrawOn(_renderTargetBottom); + if (config.screen == kScreenBottom || config.screen == kScreenBoth) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameBottomTexture.getMatrix()); + _gameTopTexture.render(); + _gameTopTexture.render(); + if (_overlayVisible) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); + _overlay.render(); + } + if (_cursorVisible && config.showCursor) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix()); + _cursorTexture.render(); + } + } + C3D_FrameEnd(0); +} + +void OSystem_3DS::setShakePos(int shakeOffset) { + // TODO: implement this in overlay, top screen, and mouse too + _screenShakeOffset = shakeOffset; + _gameTopTexture.setPosition(_gameTopX, _gameTopY + _gameTopTexture.getScaleY() * shakeOffset); + _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY + _gameBottomTexture.getScaleY() * shakeOffset); +} + +void OSystem_3DS::setFocusRectangle(const Common::Rect &rect) { + debug("setfocus: %d %d %d %d", rect.left, rect.top, rect.width(), rect.height()); + _focusRect = rect; + _focusDirty = true; + _focusClearTime = 0; +} + +void OSystem_3DS::clearFocusRectangle() { + _focusClearTime = getMillis(); +} + +void OSystem_3DS::updateFocus() { + + if (_focusClearTime && getMillis() - _focusClearTime > 5000) { + _focusClearTime = 0; + _focusDirty = true; + _focusRect = Common::Rect(_gameWidth, _gameHeight); + } + + if (_focusDirty) { + float duration = 1.f / 20.f; // Focus animation in frame duration + float w = 400.f; + float h = 240.f; + float ratio = _focusRect.width() / _focusRect.height(); + if (ratio > w/h) { + _focusTargetScaleX = w / _focusRect.width(); + float newHeight = (float)_focusRect.width() / w/h; + _focusTargetScaleY = h / newHeight; + _focusTargetPosX = _focusTargetScaleX * _focusRect.left; + _focusTargetPosY = _focusTargetScaleY * ((float)_focusRect.top - (newHeight - _focusRect.height())/2.f); + } else { + _focusTargetScaleY = h / _focusRect.height(); + float newWidth = (float)_focusRect.height() * w/h; + _focusTargetScaleX = w / newWidth; + _focusTargetPosY = _focusTargetScaleY * _focusRect.top; + _focusTargetPosX = _focusTargetScaleX * ((float)_focusRect.left - (newWidth - _focusRect.width())/2.f); + } + if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight) + _focusTargetPosX = 0; + if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth) + _focusTargetPosY = 0; + _focusStepPosX = duration * (_focusTargetPosX - _focusPosX); + _focusStepPosY = duration * (_focusTargetPosY - _focusPosY); + _focusStepScaleX = duration * (_focusTargetScaleX - _focusScaleX); + _focusStepScaleY = duration * (_focusTargetScaleY - _focusScaleY); + } + + if (_focusDirty || _focusPosX != _focusTargetPosX || _focusPosY != _focusTargetPosY || + _focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) { + _focusDirty = false; + + if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX)) + _focusPosX = _focusTargetPosX; + else if (_focusPosX != _focusTargetPosX) + _focusPosX += _focusStepPosX; + + if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY)) + _focusPosY = _focusTargetPosY; + else if (_focusPosY != _focusTargetPosY) + _focusPosY += _focusStepPosY; + + if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX)) + _focusScaleX = _focusTargetScaleX; + else if (_focusScaleX != _focusTargetScaleX) + _focusScaleX += _focusStepScaleX; + + if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY)) + _focusScaleY = _focusTargetScaleY; + else if (_focusScaleY != _focusTargetScaleY) + _focusScaleY += _focusStepScaleY; + + Mtx_Identity(&_focusMatrix); + Mtx_Translate(&_focusMatrix, -_focusPosX, -_focusPosY, 0); + Mtx_Scale(&_focusMatrix, _focusScaleX, _focusScaleY, 1.f); + } +} + +void OSystem_3DS::showOverlay() { + _overlayVisible = true; + updateSize(); + updateScreen(); +} + +void OSystem_3DS::hideOverlay() { + _overlayVisible = false; + updateSize(); + updateScreen(); +} + +Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const { + return _pfGameTexture; +} + +void OSystem_3DS::clearOverlay() { + _overlay.clear(); +} + +void OSystem_3DS::grabOverlay(void *buf, int pitch) { + for (int y = 0; y < getOverlayHeight(); ++y) { + memcpy(buf, _overlay.getBasePtr(0, y), pitch); + } +} + +void OSystem_3DS::copyRectToOverlay(const void *buf, int pitch, int x, + int y, int w, int h) { + _overlay.copyRectToSurface(buf, pitch, x, y, w, h); + _overlay.markDirty(); +} + +int16 OSystem_3DS::getOverlayHeight() { + return 240; +} + +int16 OSystem_3DS::getOverlayWidth() { + return 320; +} + +bool OSystem_3DS::showMouse(bool visible) { + _cursorVisible = visible; + flushCursor(); + return !visible; +} + +void OSystem_3DS::warpMouse(int x, int y) { + _cursorX = x; + _cursorY = y; + warning("x:%d y:%d", x, y); + // TODO: adjust for _cursorScalable ? + int offsetx = 0; + int offsety = 0; + x -= _cursorHotspotX; + y -= _cursorHotspotY; + if (!_overlayVisible) { + offsetx += config.screen == kScreenTop ? _gameTopX : _gameBottomX; + offsety += config.screen == kScreenTop ? _gameTopY : _gameBottomY; + } + float scalex = config.screen == kScreenTop ? (float)_gameTopTexture.actualWidth / _gameWidth : 1.f; + float scaley = config.screen == kScreenTop ? (float)_gameTopTexture.actualHeight / _gameHeight : 1.f; + _cursorTexture.setPosition(scalex * x + offsetx, + scaley * y + offsety); +} + +void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) { + _cursorDeltaX = deltaX; + _cursorDeltaY = deltaY; +} + +void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h, + int hotspotX, int hotspotY, + uint32 keycolor, bool dontScale, + const Graphics::PixelFormat *format) { + _cursorScalable = !dontScale; + _cursorHotspotX = hotspotX; + _cursorHotspotY = hotspotY; + _cursorKeyColor = keycolor; + _pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format; + + if (w != _cursor.w || h != _cursor.h || _cursor.format != _pfCursor) { + _cursor.create(w, h, _pfCursor); + _cursorTexture.create(w, h, _pfGameTexture); + } + + _cursor.copyRectToSurface(buf, w, 0, 0, w, h); + flushCursor(); + + warpMouse(_cursorX, _cursorY); +} + +void OSystem_3DS::setCursorPalette(const byte *colors, uint start, uint num) { + assert(start + num <= 256); + memcpy(_cursorPalette + 3 * start, colors, 3 * num); + _cursorPaletteEnabled = true; + flushCursor(); +} + +void OSystem_3DS::flushCursor() { + if (_cursor.getPixels()) { + Graphics::Surface *converted = _cursor.convertTo(_pfGameTexture, _cursorPaletteEnabled ? _cursorPalette : _palette); + _cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + _cursorTexture.markDirty(); + converted->free(); + delete converted; + + if (_pfCursor.bytesPerPixel == 1) { + uint* dest = (uint*) _cursorTexture.getPixels(); + byte* src = (byte*) _cursor.getPixels(); + for (int y = 0; y < _cursor.h; ++y) { + for (int x = 0; x < _cursor.w; ++x) { + if (*src++ == _cursorKeyColor) + *dest++ = 0; + else + dest++; + } + dest += _cursorTexture.w - _cursorTexture.actualWidth; + } + } + } +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp new file mode 100644 index 0000000000..f6278eb16b --- /dev/null +++ b/backends/platform/3ds/osystem.cpp @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#define FORBIDDEN_SYMBOL_EXCEPTION_printf +#define FORBIDDEN_SYMBOL_EXCEPTION_time_h +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h + +#include "osystem.h" + +#include "backends/saves/default/default-saves.h" +#include "backends/timer/default/default-timer.h" +#include "backends/events/default/default-events.h" +#include "audio/mixer_intern.h" +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/str.h" +#include "config.h" + +#include "backends/fs/posix/posix-fs-factory.h" +#include "backends/fs/posix/posix-fs.h" +#include <unistd.h> +#include <time.h> + +namespace _3DS { + +OSystem_3DS::OSystem_3DS(): + _focusDirty(true), + _focusRect(Common::Rect(1, 1)), + _focusPosX(0), + _focusPosY(0), + _focusTargetPosX(0), + _focusTargetPosY(0), + _focusStepPosX(0), + _focusStepPosY(0), + _focusScaleX(1.f), + _focusScaleY(1.f), + _focusTargetScaleX(1.f), + _focusTargetScaleY(1.f), + _focusStepScaleX(0.f), + _focusStepScaleY(0.f), + _focusClearTime(0), + _cursorPaletteEnabled(false), + _cursorVisible(false), + _cursorScalable(false), + _cursorX(0), + _cursorY(0), + _cursorHotspotX(0), + _cursorHotspotY(0), + _gameTopX(0), + _gameTopY(0), + _gameBottomX(0), + _gameBottomY(0), + _gameWidth(320), + _gameHeight(240), + _overlayVisible(false), + exiting(false), + sleeping(false) +{ + chdir("sdmc:/"); + _fsFactory = new POSIXFilesystemFactory(); + Posix::assureDirectoryExists("/3ds/scummvm/saves/"); +} + +OSystem_3DS::~OSystem_3DS() { + exiting = true; + destroyEvents(); + destroyAudio(); + destroyGraphics(); + + delete _timerManager; + _timerManager = 0; +} + +void OSystem_3DS::quit() { + printf("OSystem_3DS::quit()\n"); +} + +void OSystem_3DS::initBackend() { + loadConfig(); + ConfMan.registerDefault("fullscreen", true); + ConfMan.registerDefault("aspect_ratio", true); + if (!ConfMan.hasKey("vkeybd_pack_name")) + ConfMan.set("vkeybd_pack_name", "vkeybd_small"); + if (!ConfMan.hasKey("vkeybdpath")) + ConfMan.set("vkeybdpath", "/3ds/scummvm/kb"); + if (!ConfMan.hasKey("themepath")) + ConfMan.set("themepath", "/3ds/scummvm"); + if (!ConfMan.hasKey("gui_theme")) + ConfMan.set("gui_theme", "builtin"); + + _timerManager = new DefaultTimerManager(); + _savefileManager = new DefaultSaveFileManager("/3ds/scummvm/saves/"); + + initGraphics(); + initAudio(); + initEvents(); + EventsBaseBackend::initBackend(); +} + +void OSystem_3DS::updateConfig() { + if (_gameScreen.getPixels()) { + updateSize(); + warpMouse(_cursorX, _cursorY); + } +} + +Common::String OSystem_3DS::getDefaultConfigFileName() { + return "/3ds/scummvm/scummvm.ini"; +} + +uint32 OSystem_3DS::getMillis(bool skipRecord) { + return svcGetSystemTick() / TICKS_PER_MSEC; +} + +void OSystem_3DS::delayMillis(uint msecs) { + svcSleepThread(msecs * 1000000); +} + +void OSystem_3DS::getTimeAndDate(TimeDate& td) const { + time_t curTime = time(0); + struct tm t = *localtime(&curTime); + td.tm_sec = t.tm_sec; + td.tm_min = t.tm_min; + td.tm_hour = t.tm_hour; + td.tm_mday = t.tm_mday; + td.tm_mon = t.tm_mon; + td.tm_year = t.tm_year; + td.tm_wday = t.tm_wday; +} + +OSystem::MutexRef OSystem_3DS::createMutex() { + RecursiveLock *mutex = new RecursiveLock(); + RecursiveLock_Init(mutex); + return (OSystem::MutexRef) mutex; +} +void OSystem_3DS::lockMutex(MutexRef mutex) { + RecursiveLock_Lock((RecursiveLock*)mutex); +} +void OSystem_3DS::unlockMutex(MutexRef mutex) { + RecursiveLock_Unlock((RecursiveLock*)mutex); +} +void OSystem_3DS::deleteMutex(MutexRef mutex) { + delete (RecursiveLock*)mutex; +} + +Common::String OSystem_3DS::getSystemLanguage() const { + u8 langcode; + CFGU_GetSystemLanguage(&langcode); + switch (langcode) { + case CFG_LANGUAGE_JP: return "ja_JP"; + case CFG_LANGUAGE_EN: return "en_US"; + case CFG_LANGUAGE_FR: return "fr_FR"; + case CFG_LANGUAGE_DE: return "de_DE"; + case CFG_LANGUAGE_IT: return "it_IT"; + case CFG_LANGUAGE_ES: return "es_ES"; + case CFG_LANGUAGE_ZH: return "zh_CN"; + case CFG_LANGUAGE_KO: return "ko_KR"; + case CFG_LANGUAGE_NL: return "nl_NL"; + case CFG_LANGUAGE_PT: return "pt_BR"; + case CFG_LANGUAGE_RU: return "ru_RU"; + case CFG_LANGUAGE_TW: return "zh_HK"; + default: return "en_US"; + } +} + +void OSystem_3DS::fatalError() { + printf("FatalError!\n"); +} + +void OSystem_3DS::logMessage(LogMessageType::Type type, const char *message) { + printf("3DS log: %s\n", message); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h new file mode 100644 index 0000000000..478085acba --- /dev/null +++ b/backends/platform/3ds/osystem.h @@ -0,0 +1,221 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PLATFORM_3DS_H +#define PLATFORM_3DS_H + +#include <citro3d.h> +#include "backends/mutex/mutex.h" +#include "backends/base-backend.h" +#include "graphics/palette.h" +#include "base/main.h" +#include "audio/mixer_intern.h" +#include "backends/graphics/graphics.h" +#include "backends/platform/3ds/sprite.h" +#include "common/rect.h" +#include "common/queue.h" + +#define TICKS_PER_MSEC 268123 + +namespace _3DS { + +enum { + GFX_LINEAR = 0, + GFX_NEAREST = 1 +}; + +enum InputMode { + MODE_HOVER, + MODE_DRAG, +}; + +static const OSystem::GraphicsMode s_graphicsModes[] = { + {"default", "Default Test", GFX_LINEAR}, + { 0, 0, 0 } +}; + +class OSystem_3DS : public EventsBaseBackend, public PaletteManager { +public: + OSystem_3DS(); + virtual ~OSystem_3DS(); + + volatile bool exiting; + volatile bool sleeping; + + virtual void initBackend(); + + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + virtual bool getFeatureState(OSystem::Feature f); + + virtual bool pollEvent(Common::Event &event); + + virtual uint32 getMillis(bool skipRecord = false); + virtual void delayMillis(uint msecs); + virtual void getTimeAndDate(TimeDate &t) const; + + virtual MutexRef createMutex(); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + virtual void logMessage(LogMessageType::Type type, const char *message); + + virtual Audio::Mixer *getMixer(); + virtual PaletteManager *getPaletteManager() { return this; } + virtual Common::String getSystemLanguage() const; + virtual void fatalError(); + virtual void quit(); + + virtual Common::String getDefaultConfigFileName(); + + // Graphics + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + int getDefaultGraphicsMode() const; + bool setGraphicsMode(int mode); + void resetGraphicsScale(); + int getGraphicsMode() const; + inline Graphics::PixelFormat getScreenFormat() const { return _pfGame; } + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; + void initSize(uint width, uint height, + const Graphics::PixelFormat *format = NULL); + virtual int getScreenChangeID() const { return 0; }; + + void beginGFXTransaction(); + OSystem::TransactionError endGFXTransaction(); + int16 getHeight(){ return _gameHeight; } + int16 getWidth(){ return _gameWidth; } + void setPalette(const byte *colors, uint start, uint num); + void grabPalette(byte *colors, uint start, uint num); + void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, + int h); + Graphics::Surface *lockScreen(); + void unlockScreen(); + void updateScreen(); + void setShakePos(int shakeOffset); + void setFocusRectangle(const Common::Rect &rect); + void clearFocusRectangle(); + void showOverlay(); + void hideOverlay(); + Graphics::PixelFormat getOverlayFormat() const; + void clearOverlay(); + void grabOverlay(void *buf, int pitch); + void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, + int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + virtual void displayMessageOnOSD(const char *msg); + + bool showMouse(bool visible); + void warpMouse(int x, int y); + void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, + int hotspotY, uint32 keycolor, bool dontScale = false, + const Graphics::PixelFormat *format = NULL); + void setCursorPalette(const byte *colors, uint start, uint num); + + // Transform point from touchscreen coords into gamescreen coords + void transformPoint(touchPosition &point); + + void setCursorDelta(float deltaX, float deltaY); + + void updateFocus(); + void updateConfig(); + void updateSize(); + +private: + void initGraphics(); + void destroyGraphics(); + void initAudio(); + void destroyAudio(); + void initEvents(); + void destroyEvents(); + + void flushGameScreen(); + void flushCursor(); + +protected: + Audio::MixerImpl *_mixer; + +private: + u16 _gameWidth, _gameHeight; + u16 _gameTopX, _gameTopY; + u16 _gameBottomX, _gameBottomY; + + // Audio + Thread audioThread; + + // Graphics + Graphics::PixelFormat _pfGame; + Graphics::PixelFormat _pfGameTexture; + Graphics::PixelFormat _pfCursor; + byte _palette[3 * 256]; + byte _cursorPalette[3 * 256]; + + Graphics::Surface _gameScreen; + Sprite _gameTopTexture; + Sprite _gameBottomTexture; + Sprite _overlay; + + int _screenShakeOffset; + bool _overlayVisible; + + DVLB_s *_dvlb; + shaderProgram_s _program; + int _projectionLocation; + int _modelviewLocation; + C3D_Mtx _projectionTop; + C3D_Mtx _projectionBottom; + C3D_RenderTarget* _renderTargetTop; + C3D_RenderTarget* _renderTargetBottom; + + // Focus + Common::Rect _focusRect; + bool _focusDirty; + C3D_Mtx _focusMatrix; + int _focusPosX, _focusPosY; + int _focusTargetPosX, _focusTargetPosY; + float _focusStepPosX, _focusStepPosY; + float _focusScaleX, _focusScaleY; + float _focusTargetScaleX, _focusTargetScaleY; + float _focusStepScaleX, _focusStepScaleY; + uint32 _focusClearTime; + + // Events + Thread _eventThread; + Thread _timerThread; + Common::Queue<Common::Event> _eventQueue; + + // Cursor + Graphics::Surface _cursor; + Sprite _cursorTexture; + bool _cursorPaletteEnabled; + bool _cursorVisible; + bool _cursorScalable; + float _cursorX, _cursorY; + float _cursorDeltaX, _cursorDeltaY; + int _cursorHotspotX, _cursorHotspotY; + uint32 _cursorKeyColor; +}; + +} // namespace _3DS + +#endif diff --git a/backends/platform/3ds/shader.v.pica b/backends/platform/3ds/shader.v.pica new file mode 100644 index 0000000000..2d18985622 --- /dev/null +++ b/backends/platform/3ds/shader.v.pica @@ -0,0 +1,59 @@ +;* ScummVM - Graphic Adventure Engine +;* +;* ScummVM is the legal property of its developers, whose names +;* are too numerous to list here. Please refer to the COPYRIGHT +;* file distributed with this source distribution. +;* +;* This program is free software; you can redistribute it and/or +;* modify it under the terms of the GNU General Public License +;* as published by the Free Software Foundation; either version 2 +;* of the License, or (at your option) any later version. +;* +;* This program is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License +;* along with this program; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +;* + +; Uniforms +.fvec projection[4], modelView[4] + +; Constants +.constf myconst(0.0, 1.0, -1.0, 0.1) +.alias zeros myconst.xxxx ; Vector full of zeros +.alias ones myconst.yyyy ; Vector full of ones + +; Outputs +.out outpos position +.out outtex texcoord0 + +; Inputs (defined as aliases for convenience) +.alias inpos v0 +.alias intex v1 + +.proc main + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, ones + + ; r1 = modelView * inpos + dp4 r1.x, modelView[0], r0 + dp4 r1.y, modelView[1], r0 + dp4 r1.z, modelView[2], r0 + dp4 r1.w, modelView[3], r0 + + ; outpos = projection * r1 + dp4 outpos.x, projection[0], r1 + dp4 outpos.y, projection[1], r1 + dp4 outpos.z, projection[2], r1 + dp4 outpos.w, projection[3], r1 + + mov outtex, intex + + end +.end + diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp new file mode 100644 index 0000000000..a0aee385a9 --- /dev/null +++ b/backends/platform/3ds/sprite.cpp @@ -0,0 +1,144 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/3ds/sprite.h" +#include "common/util.h" +#include <3ds.h> + +static uint nextHigher2(uint v) { + if (v == 0) + return 1; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return ++v; +} + +Sprite::Sprite() + : dirtyPixels(true) + , dirtyMatrix(true) + , actualWidth(0) + , actualHeight(0) + , posX(0) + , posY(0) + , scaleX(1.f) + , scaleY(1.f) +{ + Mtx_Identity(&modelview); + + vertices = (vertex *)linearAlloc(sizeof(vertex) * 4); +} + +Sprite::~Sprite() { + // +} + +void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) { + free(); + + actualWidth = width; + actualHeight = height; + format = f; + w = MAX(nextHigher2(width), 64u); + h = MAX(nextHigher2(height), 64u);; + pitch = w * format.bytesPerPixel; + dirtyPixels = true; + + if (width && height) { + pixels = linearAlloc(h * pitch); + C3D_TexInit(&texture, w, h, GPU_RGBA8); + C3D_TexSetFilter(&texture, GPU_LINEAR, GPU_LINEAR); + assert(pixels && texture.data); + clear(); + } + + float x = 0.f, y = 0.f; + float u = (float)width/w; + float v = (float)height/h; + vertex tmp[4] = { + {{x, y, 0.5f}, {0, 0}}, + {{x+width, y, 0.5f}, {u, 0}}, + {{x, y+height, 0.5f}, {0, v}}, + {{x+width, y+height, 0.5f}, {u, v}}, + }; + memcpy(vertices, tmp, sizeof(vertex) * 4); +} + + +void Sprite::free() { + linearFree(vertices); + linearFree(pixels); + C3D_TexDelete(&texture); + pixels = 0; + w = h = pitch = 0; + actualWidth = actualHeight = 0; + format = Graphics::PixelFormat(); +} + +void Sprite::convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette) { + // +} + +void Sprite::render() { + if (dirtyPixels) { + dirtyPixels = false; + GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel); + C3D_SafeDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS); + gspWaitForPPF(); + } + C3D_TexBind(0, &texture); + + C3D_BufInfo *bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(vertex), 2, 0x10); + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); +} + +void Sprite::clear(uint32 color) { + dirtyPixels = true; + memset(pixels, color, w * h * format.bytesPerPixel); +} + +void Sprite::setScale (float x, float y) { + scaleX = x; + scaleY = y; + dirtyMatrix = true; +} + +void Sprite::setPosition(int x, int y) { + posX = x; + posY = y; + dirtyMatrix = true; +} + +C3D_Mtx* Sprite::getMatrix() { + if (dirtyMatrix) { + dirtyMatrix = false; + Mtx_Identity(&modelview); + Mtx_Scale(&modelview, scaleX, scaleY, 1.f); + Mtx_Translate(&modelview, posX, posY, 0); + } + return &modelview; +} diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h new file mode 100644 index 0000000000..6d88ae4ce1 --- /dev/null +++ b/backends/platform/3ds/sprite.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GRAPHICS_SPRITE_3DS_H +#define GRAPHICS_SPRITE_3DS_H + +#include <citro3d.h> +#include "graphics/surface.h" + +#define TEXTURE_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \ + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +typedef struct { + float position[3]; + float texcoord[2]; +} vertex; + +class Sprite : public Graphics::Surface { +public: + Sprite(); + ~Sprite(); + void create(uint16 width, uint16 height, const Graphics::PixelFormat &format); + void free(); + void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0); + void render(); + void clear(uint32 color = 0); + void markDirty(){ dirtyPixels = true; } + + void setPosition(int x, int y); + void setScale(float x, float y); + float getScaleX(){ return scaleX; } + float getScaleY(){ return scaleY; } + C3D_Mtx* getMatrix(); + + uint16 actualWidth; + uint16 actualHeight; + +private: + bool dirtyPixels; + bool dirtyMatrix; + C3D_Mtx modelview; + C3D_Tex texture; + vertex* vertices; + int posX; + int posY; + float scaleX; + float scaleY; +}; + +#endif diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index e11ad0724e..38128c62b0 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -3,7 +3,7 @@ # These must be incremented for each market upload ANDROID_VERSIONCODE = 6 -ANDROID_TARGET_VERSION = 14 +ANDROID_TARGET_VERSION = 23 NDK_BUILD = $(ANDROID_NDK)/ndk-build APP_ABI=$(ABI) SDK_ANDROID = $(ANDROID_SDK)/tools/android @@ -20,6 +20,7 @@ RESOURCES = \ $(PATH_BUILD_RES)/layout/main.xml \ $(PATH_BUILD_RES)/drawable/scummvm.png \ $(PATH_BUILD_RES)/drawable/scummvm_big.png \ + $(PATH_BUILD_RES)/drawable-xhdpi/leanback_icon.png \ $(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp index 8039981a92..b146945a01 100644 --- a/backends/platform/android/events.cpp +++ b/backends/platform/android/events.cpp @@ -101,7 +101,9 @@ enum { JKEYCODE_MEDIA_NEXT = 87, JKEYCODE_MEDIA_PREVIOUS = 88, JKEYCODE_MEDIA_REWIND = 89, - JKEYCODE_MEDIA_FAST_FORWARD = 90 + JKEYCODE_MEDIA_FAST_FORWARD = 90, + JKEYCODE_MEDIA_PLAY = 126, + JKEYCODE_MEDIA_PAUSE = 127 }; // five-way navigation control @@ -380,6 +382,19 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, return; + case JKEYCODE_MEDIA_PAUSE: + case JKEYCODE_MEDIA_PLAY: + case JKEYCODE_MEDIA_PLAY_PAUSE: + if (arg1 == JACTION_DOWN) { + e.type = Common::EVENT_MAINMENU; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + case JKEYCODE_CAMERA: case JKEYCODE_SEARCH: if (arg1 == JACTION_DOWN) @@ -888,6 +903,10 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, e.kbd.ascii = Common::ASCII_ESCAPE; break; + case JKEYCODE_BUTTON_Y: + e.type = Common::EVENT_MAINMENU; + break; + default: LOGW("unmapped gamepad key: %d", arg2); return; diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java index 32c65d3395..e81000d8b1 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java @@ -119,6 +119,8 @@ public class ScummVMEvents implements case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_SEARCH: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: break; default: diff --git a/backends/platform/androidsdl/androidsdl-main.cpp b/backends/platform/androidsdl/androidsdl-main.cpp new file mode 100644 index 0000000000..26a73579c0 --- /dev/null +++ b/backends/platform/androidsdl/androidsdl-main.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/androidsdl/androidsdl-sdl.h" +#include "base/main.h" + +int main(int argc, char *argv[]) { + + // Create our OSystem instance + g_system = new OSystem_ANDROIDSDL(); + assert(g_system); + + // Pre initialize the backend + ((OSystem_POSIX *)g_system)->init(); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + + // Free OSystem + delete (OSystem_ANDROIDSDL *)g_system; + + return res; +} diff --git a/backends/platform/androidsdl/androidsdl-sdl.cpp b/backends/platform/androidsdl/androidsdl-sdl.cpp new file mode 100644 index 0000000000..5e0eaa0408 --- /dev/null +++ b/backends/platform/androidsdl/androidsdl-sdl.cpp @@ -0,0 +1,37 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/androidsdl/androidsdl-sdl.h" +#include "backends/events/androidsdl/androidsdl-events.h" +#include "backends/graphics/androidsdl/androidsdl-graphics.h" + +void OSystem_ANDROIDSDL::initBackend() { + // Create the backend custom managers + if (_eventSource == 0) + _eventSource = new AndroidSdlEventSource(); + + if (_graphicsManager == 0) + _graphicsManager = new AndroidSdlGraphicsManager(_eventSource, _window); + + // Call parent implementation of this method + OSystem_POSIX::initBackend(); +} diff --git a/backends/platform/androidsdl/androidsdl-sdl.h b/backends/platform/androidsdl/androidsdl-sdl.h new file mode 100644 index 0000000000..6ebe5022eb --- /dev/null +++ b/backends/platform/androidsdl/androidsdl-sdl.h @@ -0,0 +1,38 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PLATFORM_SDL_ANDROIDSDL_H +#define PLATFORM_SDL_ANDROIDSDL_H + +#include "backends/platform/sdl/posix/posix.h" + +class OSystem_ANDROIDSDL : public OSystem_POSIX { +public: + virtual void initBackend(); + +#ifdef ENABLE_KEYMAPPER + // FIXME: This just calls parent methods, is it needed? + virtual Common::HardwareInputSet *getHardwareInputSet(); +#endif +}; + +#endif diff --git a/backends/platform/androidsdl/androidsdl.mk b/backends/platform/androidsdl/androidsdl.mk new file mode 100644 index 0000000000..1defb81b97 --- /dev/null +++ b/backends/platform/androidsdl/androidsdl.mk @@ -0,0 +1,11 @@ +# Special target to create an AndroidSDL snapshot +androidsdl: + $(MKDIR) release + $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) release + $(INSTALL) -c -m 644 $(DIST_FILES_DOCS) release + $(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip release + zip -j scummvm190-git-appdata.zip release/* + split -d -b 1000000 scummvm190-git-appdata.zip scummvm190-git-appdata.zip0 + $(RM) -r scummvm190-git-appdata.zip + +.PHONY: androidsdl diff --git a/backends/platform/androidsdl/module.mk b/backends/platform/androidsdl/module.mk new file mode 100644 index 0000000000..df927163b8 --- /dev/null +++ b/backends/platform/androidsdl/module.mk @@ -0,0 +1,13 @@ +MODULE := backends/platform/androidsdl + +MODULE_OBJS := \ + androidsdl-main.o \ + androidsdl-sdl.o + +# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. +MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) +OBJS := $(MODULE_OBJS) $(OBJS) +MODULE_DIRS += $(sort $(dir $(MODULE_OBJS))) + +# Hack to ensure the SDL backend is built so we can use OSystem_SDL. +-include $(srcdir)/backends/platform/sdl/module.mk diff --git a/backends/platform/maemo/debian/changelog b/backends/platform/maemo/debian/changelog index 6b6d1aebd8..e4c5c58ccd 100644 --- a/backends/platform/maemo/debian/changelog +++ b/backends/platform/maemo/debian/changelog @@ -4,6 +4,12 @@ scummvm (1.9.0~git) unstable; urgency=low -- Tarek Soliman <tsoliman@scummvm.org> Fri, 26 Feb 2016 21:11:20 -0600 +scummvm (1.8.1) unstable; urgency=low + + * 1.8.1 release + + -- Tarek Soliman <tsoliman@scummvm.org> Fri, 20 May 2016 20:05:11 -0500 + scummvm (1.8.0) unstable; urgency=low * 1.8.0 release diff --git a/backends/platform/sdl/amigaos/amigaos-main.cpp b/backends/platform/sdl/amigaos/amigaos-main.cpp index 65da6bbf85..7bbf8d1fff 100644 --- a/backends/platform/sdl/amigaos/amigaos-main.cpp +++ b/backends/platform/sdl/amigaos/amigaos-main.cpp @@ -24,13 +24,44 @@ #if defined(__amigaos4__) +#include "backends/fs/amigaos4/amigaos4-fs.h" #include "backends/platform/sdl/amigaos/amigaos.h" #include "backends/plugins/sdl/sdl-provider.h" #include "base/main.h" int main(int argc, char *argv[]) { - // Set up a stack cookie to avoid crashes due to too few stack set by users + // The following will gather the application name and add the install path + // to a variable in AmigaOS4's ENV(ARC) system. It will be placed in AppPaths + // so that ScummVM can become AmiUpdate aware + const char *const appname = "ScummVM"; + + BPTR lock; + APTR oldwin; + + // Obtain a lock to the home directory + if ((lock = IDOS->GetProgramDir())) { + TEXT progpath[2048]; + TEXT apppath[1024] = "AppPaths"; + + if (IDOS->DevNameFromLock(lock, + progpath, + sizeof(progpath), + DN_FULLPATH)) { + + // Stop any "Insert volume..." type requesters + oldwin = IDOS->SetProcWindow((APTR)-1); + + // Finally, set the variable to the path the executable was run from + IDOS->AddPart( apppath, appname, 1024); + IDOS->SetVar( apppath, progpath, -1, GVF_GLOBAL_ONLY|GVF_SAVE_VAR ); + + // Turn system requesters back on + IDOS->SetProcWindow( oldwin ); + } + } + + // Set up a stack cookie to avoid crashes from a stack set too low static const char *stack_cookie __attribute__((used)) = "$STACK: 600000"; // Create our OSystem instance @@ -44,7 +75,7 @@ int main(int argc, char *argv[]) { PluginManager::instance().addPluginProvider(new SDLPluginProvider()); #endif - // Invoke the actual ScummVM main entry point: + // Invoke the actual ScummVM main entry point int res = scummvm_main(argc, argv); // Free OSystem diff --git a/backends/platform/sdl/posix/posix-main.cpp b/backends/platform/sdl/posix/posix-main.cpp index d07db11b0c..5deebb0ae3 100644 --- a/backends/platform/sdl/posix/posix-main.cpp +++ b/backends/platform/sdl/posix/posix-main.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) +#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(ANDROIDSDL) #include "backends/platform/sdl/posix/posix.h" #include "backends/plugins/sdl/sdl-provider.h" diff --git a/base/commandLine.cpp b/base/commandLine.cpp index a9116bf5f2..2c24c018ee 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -57,7 +57,7 @@ static const char USAGE_STRING[] = ; // DONT FIXME: DO NOT ORDER ALPHABETICALLY, THIS IS ORDERED BY IMPORTANCE/CATEGORY! :) -#if defined(__SYMBIAN32__) || defined(__GP32__) || defined(ANDROID) || defined(__DS__) +#if defined(__SYMBIAN32__) || defined(__GP32__) || defined(ANDROID) || defined(__DS__) || defined(__3DS__) static const char HELP_STRING[] = "NoUsageString"; // save more data segment space #else static const char HELP_STRING[] = diff --git a/base/main.cpp b/base/main.cpp index 587e32804f..1df90c2d57 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -151,8 +151,11 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const #endif // Verify that the game path refers to an actual directory - if (!(dir.exists() && dir.isDirectory())) + if (!dir.exists()) { + err = Common::kPathDoesNotExist; + } else if (!dir.isDirectory()) { err = Common::kPathNotDirectory; + } // Create the game engine if (err.getCode() == Common::kNoError) { diff --git a/common/algorithm.h b/common/algorithm.h index cbd6eae708..13cdd9f991 100644 --- a/common/algorithm.h +++ b/common/algorithm.h @@ -270,5 +270,26 @@ T gcd(T a, T b) { #pragma warning(pop) #endif +/** + * Replacement algorithm for iterables. + * + * Replaces all occurrences of "original" in [begin, end) with occurrences of "replaced". + * + * @param[in, out] begin: First element to be examined. + * @param[in] end: Last element in the seubsection. Not examined. + * @param[in] original: Elements to be replaced. + * @param[in] replaced: Element to replace occurrences of "original". + * + * @note Usage examples and unit tests may be found in "test/common/algorithm.h" + */ +template<class It, class Dat> +void replace(It begin, It end, const Dat &original, const Dat &replaced) { + for (; begin != end; ++begin) { + if (*begin == original) { + *begin = replaced; + } + } +} + } // End of namespace Common #endif diff --git a/common/array.h b/common/array.h index db1a62ba34..e9b97aa38a 100644 --- a/common/array.h +++ b/common/array.h @@ -361,6 +361,84 @@ protected: }; +/** + * Double linked list with sorted nodes. + */ +template<class T> +class SortedArray : public Array<T> { +public: + typedef T *iterator; + typedef uint size_type; + + SortedArray(int (*comparator)(const void *, const void *)) { + _comparator = comparator; + } + + /** + * Inserts element at the sorted position. + */ + void insert(const T &element) { + if (!this->_size) { + this->insert_aux(this->_storage, &element, &element + 1); + return; + } + + T *where = (T *)bsearchMin(element, this->front(), this->_size, sizeof(T), _comparator); + insert(where, element); + } + + T &operator[](size_type idx) { + error("Operation not allowed with SortedArray"); + } + + void insert_at(size_type idx, const T &element) { + error("Operation not allowed with SortedArray"); + } + + void insert_at(size_type idx, const Array<T> &array) { + error("Operation not allowed with SortedArray"); + } + + void insert(iterator pos, const T &element) { + error("Operation not allowed with SortedArray"); + } + + void push_back(const T &element) { + error("Operation not allowed with SortedArray"); + } + + void push_back(const Array<T> &array) { + error("Operation not allowed with SortedArray"); + } + +private: + // Based on code Copyright (C) 2008-2009 Ksplice, Inc. + // Author: Tim Abbott <tabbott@ksplice.com> + // Licensed under GPLv2+ + void *bsearchMin(void *key, void *base, uint num, uint size_, + int (*cmp)(const void *key, const void *elt)) { + uint start_ = 0, end_ = num; + int result; + + while (start_ < end_) { + uint mid = start_ + (end_ - start_) / 2; + + result = cmp(key, (byte *)base + mid * size_); + if (result < 0) + end_ = mid; + else if (result > 0) + start_ = mid + 1; + else + return (void *)((byte *)base + mid * size_); + } + + return (void *)((byte *)base + start_ * size_); + } + +private: + int (*_comparator)(const void *, const void *); +}; + } // End of namespace Common #endif diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp index 71f8272b44..04802aa0c8 100644 --- a/common/recorderfile.cpp +++ b/common/recorderfile.cpp @@ -30,6 +30,8 @@ #include "graphics/surface.h" #include "graphics/scaler.h" +#ifdef ENABLE_EVENTRECORDER + #define RECORD_VERSION 1 namespace Common { @@ -714,3 +716,5 @@ void PlaybackFile::checkRecordedMD5() { } + +#endif // ENABLE_EVENTRECORDER diff --git a/common/scummsys.h b/common/scummsys.h index 7c2978f173..3513ee2d7d 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -215,6 +215,10 @@ #include "config.h" #endif +// Now we need to adjust some settings when running tests +#ifdef COMPILING_TESTS +#undef ENABLE_EVENTRECORDER +#endif // In the following we configure various targets, in particular those // which can't use our "configure" tool and hence don't use config.h. @@ -251,6 +255,7 @@ #if defined(__DC__) || \ defined(__DS__) || \ + defined(__3DS__) || \ defined(__GP32__) || \ defined(IPHONE) || \ defined(__PLAYSTATION2__) || \ @@ -367,7 +372,7 @@ #endif #ifndef STRINGBUFLEN - #if defined(__N64__) || defined(__DS__) + #if defined(__N64__) || defined(__DS__) || defined(__3DS__) #define STRINGBUFLEN 256 #else #define STRINGBUFLEN 1024 diff --git a/common/str.cpp b/common/str.cpp index ae3a965c70..c41e958349 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -75,7 +75,7 @@ void String::initWithCStr(const char *str, uint32 len) { } String::String(const String &str) - : _size(str._size) { + : _size(str._size) { if (str.isStorageIntern()) { // String in internal storage: just copy it memcpy(_storage, str._storage, _builtinCapacity); @@ -91,7 +91,7 @@ String::String(const String &str) } String::String(char c) - : _size(0), _str(_storage) { + : _size(0), _str(_storage) { _storage[0] = c; _storage[1] = 0; @@ -132,24 +132,19 @@ void String::ensureCapacity(uint32 new_size, bool keep_old) { if (!isShared && new_size < curCapacity) return; - if (isShared && new_size < _builtinCapacity) { - // We share the storage, but there is enough internal storage: Use that. - newStorage = _storage; - newCapacity = _builtinCapacity; - } else { - // We need to allocate storage on the heap! - - // Compute a suitable new capacity limit - // If the current capacity is sufficient we use the same capacity - if (new_size < curCapacity) - newCapacity = curCapacity; - else - newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); - - // Allocate new storage - newStorage = new char[newCapacity]; - assert(newStorage); - } + // We need to allocate storage on the heap! + + // Compute a suitable new capacity limit + // If the current capacity is sufficient we use the same capacity + if (new_size < curCapacity) + newCapacity = curCapacity; + else + newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); + + // Allocate new storage + newStorage = new char[newCapacity]; + assert(newStorage); + // Copy old data if needed, elsewise reset the new storage. if (keep_old) { @@ -444,6 +439,58 @@ uint String::hash() const { return hashit(c_str()); } +void String::replace(uint32 pos, uint32 count, const String &str) { + replace(pos, count, str, 0, str._size); +} + +void String::replace(uint32 pos, uint32 count, const char *str) { + replace(pos, count, str, 0, strlen(str)); +} + +void String::replace(iterator begin, iterator end, const String &str) { + replace(begin - _str, end - begin, str._str, 0, str._size); +} + +void String::replace(iterator begin, iterator end, const char *str) { + replace(begin - _str, end - begin, str, 0, strlen(str)); +} + +void String::replace(uint32 posOri, uint32 countOri, const String &str, + uint32 posDest, uint32 countDest) { + replace(posOri, countOri, str._str, posDest, countDest); +} + +void String::replace(uint32 posOri, uint32 countOri, const char *str, + uint32 posDest, uint32 countDest) { + + ensureCapacity(_size + countDest - countOri, true); + + // Prepare string for the replaced text. + if (countOri < countDest) { + uint32 offset = countDest - countOri; ///< Offset to copy the characters + uint32 newSize = _size + offset; + _size = newSize; + + // Push the old characters to the end of the string + for (uint32 i = _size; i >= posOri + countDest; i--) + _str[i] = _str[i - offset]; + + } else if (countOri > countDest){ + uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back + + // Pull the remainder string back + for (uint32 i = posOri + countDest; i < _size; i++) + _str[i] = _str[i + offset]; + + _size -= offset; + } + + // Copy the replaced part of the string + for (uint32 i = 0; i < countDest; i++) + _str[posOri + i] = str[posDest + i]; + +} + // static String String::format(const char *fmt, ...) { String output; diff --git a/common/str.h b/common/str.h index 1b41c481c7..9ada8aaaa0 100644 --- a/common/str.h +++ b/common/str.h @@ -46,6 +46,17 @@ namespace Common { class String { public: static const uint32 npos = 0xFFFFFFFF; + + typedef char value_type; + /** + * Unsigned version of the underlying type. This can be used to cast + * individual string characters to bigger integer types without sign + * extension happening. + */ + typedef unsigned char unsigned_type; + typedef char * iterator; + typedef const char * const_iterator; + protected: /** * The size of the internal storage. Increasing this means less heap @@ -222,6 +233,38 @@ public: void trim(); uint hash() const; + + /**@{ + * Functions to replace some amount of chars with chars from some other string. + * + * @note The implementation follows that of the STL's std::string: + * http://www.cplusplus.com/reference/string/string/replace/ + * + * @param pos Starting position for the replace in the original string. + * @param count Number of chars to replace from the original string. + * @param str Source of the new chars. + * @param posOri Same as pos + * @param countOri Same as count + * @param posDest Initial position to read str from. + * @param countDest Number of chars to read from str. npos by default. + */ + // Replace 'count' bytes, starting from 'pos' with str. + void replace(uint32 pos, uint32 count, const String &str); + // The same as above, but accepts a C-like array of characters. + void replace(uint32 pos, uint32 count, const char *str); + // Replace the characters in [begin, end) with str._str. + void replace(iterator begin, iterator end, const String &str); + // Replace the characters in [begin, end) with str. + void replace(iterator begin, iterator end, const char *str); + // Replace _str[posOri, posOri + countOri) with + // str._str[posDest, posDest + countDest) + void replace(uint32 posOri, uint32 countOri, const String &str, + uint32 posDest, uint32 countDest); + // Replace _str[posOri, posOri + countOri) with + // str[posDest, posDest + countDest) + void replace(uint32 posOri, uint32 countOri, const char *str, + uint32 posDest, uint32 countDest); + /**@}*/ /** * Print formatted data into a String object. Similar to sprintf, @@ -238,15 +281,6 @@ public: static String vformat(const char *fmt, va_list args); public: - typedef char value_type; - /** - * Unsigned version of the underlying type. This can be used to cast - * individual string characters to bigger integer types without sign - * extension happening. - */ - typedef unsigned char unsigned_type; - typedef char * iterator; - typedef const char * const_iterator; iterator begin() { // Since the user could potentially @@ -440,7 +440,7 @@ get_system_exe_extension() { arm-riscos) _exeext=",ff8" ;; - dreamcast | ds | gamecube | n64 | ps2 | psp | wii) + 3ds | dreamcast | ds | gamecube | n64 | ps2 | psp | wii) _exeext=".elf" ;; gph-linux) @@ -843,9 +843,9 @@ Usage: $0 [OPTIONS]... Configuration: -h, --help display this help and exit - --backend=BACKEND backend to build (android, tizen, dc, dingux, ds, gcw0, + --backend=BACKEND backend to build (3ds, android, dc, dingux, ds, gcw0, gph, iphone, ios7, linuxmoto, maemo, n64, null, openpandora, - ps2, psp, samsungtv, sdl, webos, wii, wince) [sdl] + ps2, psp, samsungtv, sdl, tizen, webos, wii, wince) [sdl] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX @@ -871,10 +871,11 @@ Fine tuning of the installation directories: Special configuration feature: --host=HOST cross-compile to target HOST (arm-linux, ...) - special targets: android-arm for Android ARM + special targets: 3ds for Nintendo 3DS + android-arm for Android ARM android-mips for Android MIPS android-x86 for Android x86 - tizen for Samsung Tizen + androidsdl for Android with SDL backend caanoo for Caanoo dingux for Dingux raspberrypi for Raspberry Pi @@ -897,6 +898,7 @@ Special configuration feature: ps3 for PlayStation 3 psp for PlayStation Portable samsungtv for Samsung TV + tizen for Samsung Tizen webos for HP Palm WebOS wii for Nintendo Wii wince for Windows CE @@ -1309,6 +1311,11 @@ get_system_exe_extension $guessed_host NATIVEEXEEXT=$_exeext case $_host in +3ds) + _host_os=3ds + _host_cpu=arm + _host_alias=arm-none-eabi + ;; android | android-arm | android-v7a | android-arm-v7a | ouya) _host_os=android _host_cpu=arm @@ -1324,6 +1331,26 @@ android-x86) _host_cpu=i686 _host_alias=i686-linux-android ;; +androidsdl-armeabi | androidsdl-armeabi-v7a) + _host_os=androidsdl + _host_cpu=arm + _host_alias=arm-linux-androideabi + ;; +androidsdl-arm64-v8a) + _host_os=androidsdl + _host_cpu=aarch64 + _host_alias=aarch64-linux-android + ;; +androidsdl-mips) + _host_os=androidsdl + _host_cpu=mipsel + _host_alias=mipsel-linux-android + ;; +androidsdl-x86) + _host_os=androidsdl + _host_cpu=i686 + _host_alias=i686-linux-android + ;; arm-riscos) _host_os=riscos _host_cpu=arm @@ -1590,7 +1617,7 @@ android) exit 1 fi ;; -ds | gamecube | wii) +3ds | ds | gamecube | wii) if test -z "$DEVKITPRO"; then echo "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to devkitPRO>" exit 1 @@ -1846,7 +1873,7 @@ if test "$have_gcc" = yes ; then case $_host_os in # newlib-based system include files suppress non-C89 function # declarations under __STRICT_ANSI__ - amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince ) + 3ds | amigaos* | android | androidsdl | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince ) ;; *) append_var CXXFLAGS "-ansi" @@ -1882,7 +1909,7 @@ echo $_use_cxx11 # However, some platforms use GNU extensions in system header files, so # for these we must not use -pedantic. case $_host_os in -android | gamecube | psp | tizen | wii | webos) +android | androidsdl | gamecube | psp | tizen | wii | webos) ;; *) # ICC does not support pedantic, while GCC and clang do. @@ -2146,6 +2173,27 @@ esac echo_n "Checking hosttype... " echo $_host_os case $_host_os in + 3ds) + _optimization_level=-O2 + append_var DEFINES "-D__3DS__" + append_var DEFINES "-DARM" + append_var DEFINES "-DARM11" + append_var CXXFLAGS "-march=armv6k" + append_var CXXFLAGS "-mtune=mpcore" + append_var CXXFLAGS "-mword-relocations" + append_var CXXFLAGS "-mfloat-abi=hard" + append_var CXXFLAGS "-ffunction-sections" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var CXXFLAGS "-I$DEVKITPRO/libctru/include" + append_var CXXFLAGS "-I$DEVKITPRO/portlibs/3ds/include" + if test "$_dynamic_modules" = no ; then + append_var LDFLAGS "-Wl,--gc-sections" + else + append_var LDFLAGS "-Wl,--no-gc-sections" + fi + append_var LDFLAGS "-L$DEVKITPRO/portlibs/3ds/lib" + append_var LIBS "-lcitro3d -lctru" + ;; amigaos*) append_var LDFLAGS "-Wl,--export-dynamic" append_var LDFLAGS "-L/sdk/local/newlib/lib" @@ -2583,6 +2631,18 @@ if test -n "$_host"; then # Cross-compiling mode - add your target here if needed echo "Cross-compiling to $_host" case "$_host" in + 3ds) + append_var DEFINES "-DDISABLE_FANCY_THEMES" + append_var DEFINES "-DDISABLE_SID" + append_var DEFINES "-DDISABLE_NES_APU" + _backend="3ds" + _build_scalers=no + _vkeybd=yes + _mt32emu=no + # Should use Tremor instead of Vorbis + _vorbis=no + _port_mk="backends/platform/3ds/3ds.mk" + ;; android | android-arm | android-v7a | android-arm-v7a | android-mips | android-x86 | ouya) # we link a .so as default append_var LDFLAGS "-shared" @@ -2596,6 +2656,15 @@ if test -n "$_host"; then _mt32emu=no _timidity=no ;; + androidsdl | androidsdl-armeabi | androidsdl-armeabi-v7a | androidsdl-mips | androidsdl-x86 | androidsdl-arm64-v8a) + DEFINES="$DEFINES -DANDROIDSDL" + _unix=yes + _seq_midi=no + _mt32emu=no + _timidity=no + _backend="androidsdl" + _port_mk="backends/platform/androidsdl/androidsdl.mk" + ;; arm-linux|arm*-linux-gnueabi|arm-*-linux) ;; arm-riscos|linupy) @@ -3042,12 +3111,16 @@ fi # Backend related stuff # case $_backend in + 3ds) + ;; android) append_var DEFINES "-DREDUCE_MEMORY_USAGE" append_var CXXFLAGS "-Wa,--noexecstack" append_var LDFLAGS "-Wl,-z,noexecstack" append_var INCLUDES "-I$ANDROID_NDK/sources/cxx-stl/system/include" ;; + androidsdl) + ;; dc) append_var INCLUDES '-I$(srcdir)/backends/platform/dc' append_var INCLUDES '-isystem $(ronindir)/include' @@ -3144,6 +3217,8 @@ case $_backend in append_var LDFLAGS "-shared" append_var LDFLAGS "-fpic" ;; + sdl) + ;; tizen) # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included append_var DEFINES "-DTIZEN -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" @@ -3192,8 +3267,6 @@ case $_backend in append_var DEFINES "-DSDL_BACKEND" add_line_to_config_mk "SDL_BACKEND = 1" ;; - sdl) - ;; *) echo "support for $_backend backend not implemented in configure script yet" exit 1 @@ -3205,7 +3278,7 @@ append_var MODULES "backends/platform/$_backend" # Setup SDL specifics for SDL based backends # case $_backend in - dingux | gph | linuxmoto | maemo | openpandora | samsungtv | sdl) + androidsdl | dingux | gph | linuxmoto | maemo | openpandora | samsungtv | sdl) find_sdlconfig append_var INCLUDES "`$_sdlconfig --prefix="$_sdlpath" --cflags`" append_var LIBS "`$_sdlconfig --prefix="$_sdlpath" --libs`" @@ -3228,7 +3301,7 @@ esac # Enable 16bit support only for backends which support it # case $_backend in - android | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | samsungtv | sdl | tizen | webos | wii) + 3ds | android | androidsdl | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | samsungtv | sdl | tizen | webos | wii) if test "$_16bit" = auto ; then _16bit=yes else @@ -3307,7 +3380,7 @@ case $_host_os in amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp | wii | wince) _posix=no ;; - android | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) + 3ds | android | androidsdl | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) _posix=yes ;; os2-emx*) @@ -4561,6 +4634,14 @@ fi # after all of CXXFLAGS, LDFLAGS, LIBS etc. have been setup # case $_backend in + 3ds) + if test "$_freetype2" = yes -a "$_png" = yes; then + append_var LIBS "-lpng" + fi + if test "$_tremor" = yes -o "$_flac" = yes; then + append_var LIBS "-logg" + fi + ;; android) # ssp at this point so the cxxtests link if test "$_debug_build" = yes; then diff --git a/devtools/create_classicmacfonts.sh b/devtools/create_classicmacfonts.sh index 6942d07111..517f3f5638 100755 --- a/devtools/create_classicmacfonts.sh +++ b/devtools/create_classicmacfonts.sh @@ -4,7 +4,7 @@ # from it. Mac only, unfortunately. # # On Windows you perhaps can perform the extraction manually with use of -# HFV Explorer: https://web.archive.org/web/20011202005455/http://gamma.nic.fi/~lpesonen/HFVExplorer/ +# HFSxplorer: http://www.catacombae.org/hfsexplorer/ # # More information could be found in the vMac documentation: http://www.gryphel.com/c/image/ # diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp index e064e899de..294eadf92b 100644 --- a/devtools/create_kyradat/create_kyradat.cpp +++ b/devtools/create_kyradat/create_kyradat.cpp @@ -45,7 +45,7 @@ enum { - kKyraDatVersion = 87 + kKyraDatVersion = 88 }; const ExtractFilename extractFilenames[] = { @@ -119,7 +119,6 @@ const ExtractFilename extractFilenames[] = { // AUDIO filename table { k1AudioTracks, kStringList, false }, - { k1AudioTracks2, kStringList, false }, { k1AudioTracksIntro, kStringList, false }, // AMULET anim diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h index a6bee6f75c..1d58d7551f 100644 --- a/devtools/create_kyradat/create_kyradat.h +++ b/devtools/create_kyradat/create_kyradat.h @@ -131,7 +131,6 @@ enum kExtractID { k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, k1CreditsStrings, diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp index afe0c67dbf..e6f0b38c45 100644 --- a/devtools/create_kyradat/games.cpp +++ b/devtools/create_kyradat/games.cpp @@ -231,7 +231,6 @@ const int kyra1FloppyNeed[] = { k1NewGameString, k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, -1 }; @@ -317,7 +316,6 @@ const int kyra1FloppyOldNeed[] = { k1NewGameString, k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, -1 }; @@ -405,7 +403,6 @@ const int kyra1CDNeed[] = { k1NewGameString, k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, -1 }; diff --git a/devtools/create_kyradat/resources.cpp b/devtools/create_kyradat/resources.cpp index 4df6bb8fb8..246811f821 100644 --- a/devtools/create_kyradat/resources.cpp +++ b/devtools/create_kyradat/resources.cpp @@ -302,7 +302,6 @@ static const ResourceProvider resourceProviders[] = { { k1NewGameString, kKyra1, kPlatformDOS, kNoSpecial, EN_ANY, &k1NewGameStringDOSEnglishProvider }, { k1ConfigStrings, kKyra1, kPlatformDOS, kNoSpecial, EN_ANY, &k1ConfigStringsDOSEnglishProvider }, { k1AudioTracks, kKyra1, kPlatformDOS, kNoSpecial, UNK_LANG, &k1AudioTracksDOSProvider }, - { k1AudioTracks2, kKyra1, kPlatformDOS, kNoSpecial, UNK_LANG, &k1AudioTracks2DOSProvider }, { k1AudioTracksIntro, kKyra1, kPlatformDOS, kNoSpecial, UNK_LANG, &k1AudioTracksIntroDOSProvider }, { k1IntroStrings, kKyra1, kPlatformDOS, kNoSpecial, DE_DEU, &k1IntroStringsDOSGermanProvider }, { k1ItemNames, kKyra1, kPlatformDOS, kNoSpecial, DE_DEU, &k1ItemNamesDOSGermanProvider }, @@ -472,7 +471,6 @@ static const ResourceProvider resourceProviders[] = { { k1NewGameString, kKyra1, kPlatformDOS, kOldFloppy, RU_RUS, &k1NewGameStringDOSOldFloppyRussianProvider }, { k1ConfigStrings, kKyra1, kPlatformDOS, kOldFloppy, RU_RUS, &k1ConfigStringsDOSOldFloppyRussianProvider }, { k1AudioTracks, kKyra1, kPlatformDOS, kOldFloppy, UNK_LANG, &k1AudioTracksDOSOldFloppyProvider }, - { k1AudioTracks2, kKyra1, kPlatformDOS, kOldFloppy, UNK_LANG, &k1AudioTracks2DOSOldFloppyProvider }, { k1AudioTracksIntro, kKyra1, kPlatformDOS, kOldFloppy, UNK_LANG, &k1AudioTracksIntroDOSOldFloppyProvider }, { k1KallakWritingSeq, kKyra1, kPlatformDOS, kTalkieVersion, UNK_LANG, &k1KallakWritingSeqDOSCDProvider }, { k1MalcolmTreeSeq, kKyra1, kPlatformDOS, kTalkieVersion, UNK_LANG, &k1MalcolmTreeSeqDOSCDProvider }, @@ -556,7 +554,6 @@ static const ResourceProvider resourceProviders[] = { { k1NewGameString, kKyra1, kPlatformDOS, kTalkieVersion, EN_ANY, &k1NewGameStringDOSCDEnglishProvider }, { k1ConfigStrings, kKyra1, kPlatformDOS, kTalkieVersion, EN_ANY, &k1ConfigStringsDOSCDEnglishProvider }, { k1AudioTracks, kKyra1, kPlatformDOS, kTalkieVersion, UNK_LANG, &k1AudioTracksDOSCDProvider }, - { k1AudioTracks2, kKyra1, kPlatformDOS, kTalkieVersion, UNK_LANG, &k1AudioTracks2DOSCDProvider }, { k1AudioTracksIntro, kKyra1, kPlatformDOS, kTalkieVersion, UNK_LANG, &k1AudioTracksIntroDOSCDProvider }, { k1IntroStrings, kKyra1, kPlatformDOS, kTalkieVersion, DE_DEU, &k1IntroStringsDOSCDGermanProvider }, { k1ItemNames, kKyra1, kPlatformDOS, kTalkieVersion, DE_DEU, &k1ItemNamesDOSCDGermanProvider }, diff --git a/devtools/create_kyradat/resources/lok_dos.h b/devtools/create_kyradat/resources/lok_dos.h index e8d987fe1b..6bc9c2525f 100644 --- a/devtools/create_kyradat/resources/lok_dos.h +++ b/devtools/create_kyradat/resources/lok_dos.h @@ -1898,7 +1898,7 @@ static const byte k1OutroReunionSeqDOS[1351] = { static const ByteProvider k1OutroReunionSeqDOSProvider = { ARRAYSIZE(k1OutroReunionSeqDOS), k1OutroReunionSeqDOS }; -static const char *const k1AudioTracksDOS[8] = { +static const char *const k1AudioTracksDOS[9] = { "KYRA1A", "KYRA1B", "KYRA2A", @@ -1906,17 +1906,12 @@ static const char *const k1AudioTracksDOS[8] = { "KYRA4A", "KYRA4B", "KYRA5A", - "KYRA5B" + "KYRA5B", + "KYRAMISC" }; static const StringListProvider k1AudioTracksDOSProvider = { ARRAYSIZE(k1AudioTracksDOS), k1AudioTracksDOS }; -static const char *const k1AudioTracks2DOS[1] = { - "kyramisc" -}; - -static const StringListProvider k1AudioTracks2DOSProvider = { ARRAYSIZE(k1AudioTracks2DOS), k1AudioTracks2DOS }; - static const char *const k1AudioTracksIntroDOS[1] = { "intro" }; diff --git a/devtools/create_kyradat/resources/lok_dos_cd.h b/devtools/create_kyradat/resources/lok_dos_cd.h index 9550d53d61..dc7a521063 100644 --- a/devtools/create_kyradat/resources/lok_dos_cd.h +++ b/devtools/create_kyradat/resources/lok_dos_cd.h @@ -1969,7 +1969,7 @@ static const byte k1OutroReunionSeqDOSCD[1509] = { static const ByteProvider k1OutroReunionSeqDOSCDProvider = { ARRAYSIZE(k1OutroReunionSeqDOSCD), k1OutroReunionSeqDOSCD }; -static const char *const k1AudioTracksDOSCD[8] = { +static const char *const k1AudioTracksDOSCD[9] = { "KYRA1A", "KYRA1B", "KYRA2A", @@ -1977,17 +1977,12 @@ static const char *const k1AudioTracksDOSCD[8] = { "KYRA4A", "KYRA4B", "KYRA5A", - "KYRA5B" + "KYRA5B", + "KYRAMISC" }; static const StringListProvider k1AudioTracksDOSCDProvider = { ARRAYSIZE(k1AudioTracksDOSCD), k1AudioTracksDOSCD }; -static const char *const k1AudioTracks2DOSCD[1] = { - "kyramisc" -}; - -static const StringListProvider k1AudioTracks2DOSCDProvider = { ARRAYSIZE(k1AudioTracks2DOSCD), k1AudioTracks2DOSCD }; - static const char *const k1AudioTracksIntroDOSCD[2] = { "intro", "intro" diff --git a/devtools/create_kyradat/resources/lok_dos_oldfloppy.h b/devtools/create_kyradat/resources/lok_dos_oldfloppy.h index e19cb5a6ef..62b1530941 100644 --- a/devtools/create_kyradat/resources/lok_dos_oldfloppy.h +++ b/devtools/create_kyradat/resources/lok_dos_oldfloppy.h @@ -1884,7 +1884,7 @@ static const byte k1OutroReunionSeqDOSOldFloppy[1351] = { static const ByteProvider k1OutroReunionSeqDOSOldFloppyProvider = { ARRAYSIZE(k1OutroReunionSeqDOSOldFloppy), k1OutroReunionSeqDOSOldFloppy }; -static const char *const k1AudioTracksDOSOldFloppy[8] = { +static const char *const k1AudioTracksDOSOldFloppy[9] = { "KYRA1A", "KYRA1B", "KYRA2A", @@ -1892,17 +1892,12 @@ static const char *const k1AudioTracksDOSOldFloppy[8] = { "KYRA4A", "KYRA4B", "KYRA5A", - "KYRA5B" + "KYRA5B", + "KYRAMISC" }; static const StringListProvider k1AudioTracksDOSOldFloppyProvider = { ARRAYSIZE(k1AudioTracksDOSOldFloppy), k1AudioTracksDOSOldFloppy }; -static const char *const k1AudioTracks2DOSOldFloppy[1] = { - "kyramisc" -}; - -static const StringListProvider k1AudioTracks2DOSOldFloppyProvider = { ARRAYSIZE(k1AudioTracks2DOSOldFloppy), k1AudioTracks2DOSOldFloppy }; - static const char *const k1AudioTracksIntroDOSOldFloppy[1] = { "intro" }; diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 0242af1cfb..de5936b94d 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -938,17 +938,17 @@ TokenList tokenize(const std::string &input, char separator) { namespace { const Feature s_features[] = { // Libraries - { "libz", "USE_ZLIB", "zlib", true, "zlib (compression) support" }, - { "mad", "USE_MAD", "libmad", true, "libmad (MP3) support" }, + { "libz", "USE_ZLIB", "zlib", true, "zlib (compression) support" }, + { "mad", "USE_MAD", "libmad", true, "libmad (MP3) support" }, { "vorbis", "USE_VORBIS", "libvorbisfile_static libvorbis_static libogg_static", true, "Ogg Vorbis support" }, { "flac", "USE_FLAC", "libFLAC_static win_utf8_io_static", true, "FLAC support" }, - { "png", "USE_PNG", "libpng16", true, "libpng support" }, + { "png", "USE_PNG", "libpng16", true, "libpng support" }, { "faad", "USE_FAAD", "libfaad", false, "AAC support" }, { "mpeg2", "USE_MPEG2", "libmpeg2", false, "MPEG-2 support" }, - { "theora", "USE_THEORADEC", "libtheora_static", true, "Theora decoding support" }, - { "freetype", "USE_FREETYPE2", "freetype", true, "FreeType support" }, - { "jpeg", "USE_JPEG", "jpeg-static", true, "libjpeg support" }, - {"fluidsynth", "USE_FLUIDSYNTH", "libfluidsynth", true, "FluidSynth support" }, + { "theora", "USE_THEORADEC", "libtheora_static", true, "Theora decoding support" }, + { "freetype", "USE_FREETYPE2", "freetype", true, "FreeType support" }, + { "jpeg", "USE_JPEG", "jpeg-static", true, "libjpeg support" }, + {"fluidsynth", "USE_FLUIDSYNTH", "libfluidsynth", true, "FluidSynth support" }, // Feature flags { "bink", "USE_BINK", "", true, "Bink video support" }, diff --git a/devtools/credits.pl b/devtools/credits.pl index e306820f32..80f4c6d487 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -536,7 +536,7 @@ begin_credits("Credits"); begin_section("AGI"); add_person("Stuart George", "darkfiber", ""); - add_person("Matthew Hoops", "clone2727", ""); + add_person("Matthew Hoops", "clone2727", "(retired)"); add_person("Filippos Karapetis", "[md5]", ""); add_person("Martin Kiewitz", "m_kiewitz", ""); add_person("Paweł Kołodziejski", "aquadran", ""); @@ -647,7 +647,7 @@ begin_credits("Credits"); end_section(); begin_section("Lastexpress"); - add_person("Matthew Hoops", "clone2727", ""); + add_person("Matthew Hoops", "clone2727", "(retired)"); add_person("Jordi Vilalta Prat", "jvprat", ""); add_person("Julien Templier", "littleboy", ""); end_section(); @@ -669,7 +669,7 @@ begin_credits("Credits"); begin_section("Mohawk"); add_person("Bastien Bouclet", "bgk", ""); - add_person("Matthew Hoops", "clone2727", ""); + add_person("Matthew Hoops", "clone2727", "(retired)"); add_person("Filippos Karapetis", "[md5]", ""); add_person("Alyssa Milburn", "fuzzie", ""); add_person("Eugene Sandulenko", "sev", ""); @@ -691,7 +691,7 @@ begin_credits("Credits"); end_section(); begin_section("Pegasus"); - add_person("Matthew Hoops", "clone2727", ""); + add_person("Matthew Hoops", "clone2727", "(retired)"); end_section(); begin_section("Queen"); @@ -817,6 +817,7 @@ begin_credits("Credits"); begin_section("Android"); add_person("Andre Heider", "dhewg", ""); add_person("Angus Lees", "Gus", ""); + add_person("Lubomyr Lisen", "", ""); end_section(); begin_section("Dreamcast"); @@ -845,6 +846,10 @@ begin_credits("Credits"); add_person("Tarek Soliman", "tsoliman", ""); end_section(); + begin_section("Nintendo 3DS"); + add_person("Thomas Edvalson", "Cruel", ""); + end_section(); + begin_section("Nintendo 64"); add_person("Fabio Battaglia", "Hkz", ""); end_section(); @@ -953,7 +958,7 @@ begin_credits("Credits"); begin_persons(); add_person("Thierry Crozat", "criezy", "Numerous contributions to documentation"); add_person("Joachim Eberhard", "joachimeberhard", "Numerous contributions to documentation (retired)"); - add_person("Matthew Hoops", "clone2727", "Wiki editor"); + add_person("Matthew Hoops", "clone2727", "Numerous contributions to documentation (retired)"); end_persons(); end_section(); diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt index 235986a878..92754a27b4 100644 --- a/devtools/scumm-md5.txt +++ b/devtools/scumm-md5.txt @@ -237,6 +237,7 @@ monkey The Secret of Monkey Island 71523b539491527d9860f4407faf0411 7607 en DOS Demo EGA Demo - Fingolfin 771bc18ec6f93837b839c992b211904b -1 de DOS Demo EGA Demo - khalek 54a936ad06161ff7bfefcb96200f7bff 7617 en Amiga VGA VGA Demo - khalek + c0c9de81fb965e6cbe77f6e5631ca705 9135 en DOS SE Talkie Unofficial SE Talkie v1.02 rootfather pass Passport to Adventure e6cd81b25ab1453a8a6d3482118c391e 7857 en DOS - - v1.0 9/14/90 Fingolfin @@ -269,6 +270,7 @@ monkey2 Monkey Island 2: LeChuck's Revenge 430bc518017b6fac046f58bab6baad5d -1 jp FM-TOWNS FM-TOWNS - - Antti Leimi, Andrea Petrucci 387a544b8b10b26912d8413bab63a853 -1 en DOS - Demo non-interactive khalek + f4d20ab4ce19743a646cb48bd93aee72 10835 en DOS SE Talkie Unofficial SE Talkie v0.2 rootfather atlantis Indiana Jones and the Fate of Atlantis 3a03dab514e4038df192d8a8de469788 -1 en Amiga Floppy Floppy - dhewg diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml index d605d9b3cc..14161668fe 100644 --- a/dists/android/AndroidManifest.xml +++ b/dists/android/AndroidManifest.xml @@ -15,6 +15,7 @@ <application android:label="@string/app_name" android:description="@string/app_desc" + android:isGame="true" android:icon="@drawable/scummvm"> <activity android:name=".ScummVMActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" @@ -27,6 +28,14 @@ <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:banner="@drawable/leanback_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> + </intent-filter> + </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> @@ -42,4 +51,11 @@ <uses-configuration android:reqTouchScreen="stylus" android:reqKeyboardType="qwerty"/> + + <uses-feature android:name="android.hardware.touchscreen" + android:required="false" /> + + <uses-feature android:name="android.software.leanback" + android:required="false" /> + </manifest> diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in index d90e282e3d..de2f2d905e 100644 --- a/dists/android/AndroidManifest.xml.in +++ b/dists/android/AndroidManifest.xml.in @@ -15,6 +15,7 @@ <application android:label="@string/app_name" android:description="@string/app_desc" + android:isGame="true" android:icon="@drawable/scummvm"> <activity android:name=".ScummVMActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" @@ -27,6 +28,14 @@ <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:banner="@drawable/leanback_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> + </intent-filter> + </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> @@ -42,4 +51,11 @@ <uses-configuration android:reqTouchScreen="stylus" android:reqKeyboardType="qwerty"/> + + <uses-feature android:name="android.hardware.touchscreen" + android:required="false" /> + + <uses-feature android:name="android.software.leanback" + android:required="false" /> + </manifest> diff --git a/dists/android/res/drawable-xhdpi/leanback_icon.png b/dists/android/res/drawable-xhdpi/leanback_icon.png Binary files differnew file mode 100644 index 0000000000..28a7196b7f --- /dev/null +++ b/dists/android/res/drawable-xhdpi/leanback_icon.png diff --git a/dists/androidsdl/scummvm/AndroidAppSettings.cfg b/dists/androidsdl/scummvm/AndroidAppSettings.cfg new file mode 100644 index 0000000000..73d794e1ab --- /dev/null +++ b/dists/androidsdl/scummvm/AndroidAppSettings.cfg @@ -0,0 +1,230 @@ +# The application settings for Android libSDL port + +AppSettingVersion=19 + +# libSDL version to use (1.2 or 1.3, specify 1.3 for SDL2) +LibSdlVersion=1.2 + +# Specify application name (e.x. My Application) +AppName="ScummVM-SDL" + +# Specify reversed site name of application (e.x. com.mysite.myapp) +AppFullName=org.scummvm.sdl + +# Specify screen orientation: (v)ertical/(p)ortrait or (h)orizontal/(l)andscape +ScreenOrientation=h + +# Do not allow device to sleep when the application is in foreground, set this for video players or apps which use accelerometer +InhibitSuspend=y + +# Specify path to download application data in zip archive in the form 'Description|URL|MirrorURL^Description2|URL2|MirrorURL2^...' +# If you'll start Description with '!' symbol it will be enabled by default, other downloads should be selected by user from startup config menu +# If the URL in in the form ':dir/file.dat:http://URL/' it will be downloaded as binary BLOB to the application dir and not unzipped +# If the URL does not contain 'http://' it is treated as file from 'project/jni/application/src/AndroidData' dir - +# these files are put inside .apk package by build system +# Also please avoid 'https://' URLs, many Android devices do not have trust certificates and will fail to connect to SF.net over HTTPS +AppDataDownloadUrl="!!App data|scummvm190-git-appdata.zip" + +# Video color depth - 16 BPP is the fastest and supported for all modes, 24 bpp is supported only +# with SwVideoMode=y, SDL_OPENGL mode supports everything. (16)/(24)/(32) +VideoDepthBpp=32 + +# Enable OpenGL depth buffer (needed only for 3-d applications, small speed decrease) (y) or (n) +NeedDepthBuffer=y + +# Enable OpenGL stencil buffer (needed only for 3-d applications, small speed decrease) (y) or (n) +NeedStencilBuffer=y + +# Try to use GLES 2.x context - will revert to GLES 1.X if unsupported by device +# you need this option only if you're developing 3-d app (y) or (n) +NeedGles2=n + +# Application uses software video buffer - you're calling SDL_SetVideoMode() without SDL_HWSURFACE and without SDL_OPENGL, +# this will allow small speed optimization. Enable this even when you're using SDL_HWSURFACE. (y) or (n) +SwVideoMode=n + +# Application video output will be resized to fit into native device screen (y)/(n) +SdlVideoResize=y + +# Application resizing will keep 4:3 aspect ratio, with black bars at sides (y)/(n) +SdlVideoResizeKeepAspect=n + +# Application does not call SDL_Flip() or SDL_UpdateRects() appropriately, or draws from non-main thread - +# enabling the compatibility mode will force screen update every 100 milliseconds, which is laggy and inefficient (y) or (n) +CompatibilityHacks=n + +# Application initializes SDL audio/video inside static constructors (which is bad, you won't be able to run ndk-gdb) (y)/(n) +CompatibilityHacksStaticInit=n + +# On-screen Android soft text input emulates hardware keyboard, this will only work with Hackers Keyboard app (y)/(n) +CompatibilityHacksTextInputEmulatesHwKeyboard=y +TextInputKeyboard=1 + +# Hack for broken devices: prevent audio chopping, by sleeping a bit after pushing each audio chunk (y)/(n) +CompatibilityHacksPreventAudioChopping=n + +# Hack for broken apps: application ignores audio buffer size returned by SDL (y)/(n) +CompatibilityHacksAppIgnoresAudioBufferSize=n + +# Hack for VCMI: preload additional shared libraries before aplication start +CompatibilityHacksAdditionalPreloadedSharedLibraries="" + +# Hack for Free Heroes 2, which redraws the screen inside SDL_PumpEvents(): slow and compatible SDL event queue - +# do not use it with accelerometer/gyroscope, or your app may freeze at random (y)/(n) +CompatibilityHacksSlowCompatibleEventQueue=n + +# Save and restore OpenGL state when drawing on-screen keyboard for apps that use SDL_OPENGL +CompatibilityHacksTouchscreenKeyboardSaveRestoreOpenGLState=y + +# Application uses mouse (y) or (n), this will show mouse emulation dialog to the user +AppUsesMouse=y + +# Application needs two-button mouse, will also enable advanced point-and-click features (y) or (n) +AppNeedsTwoButtonMouse=y + +# Show SDL mouse cursor, for applications that do not draw cursor at all (y) or (n) +ShowMouseCursor=n + +# Force relative (laptop) mouse movement mode, useful when both on-screen keyboard and mouse are needed (y) or (n) +ForceRelativeMouseMode=n + +# Application needs arrow keys (y) or (n), will show on-screen dpad/joystick (y) or (n) +AppNeedsArrowKeys=n + +# Application needs text input (y) or (n), enables button for text input on screen +AppNeedsTextInput=y + +# Application uses joystick (y) or (n), the on-screen DPAD will be used as joystick 0 axes 0-1 +AppUsesJoystick=n + +# Application uses second on-screen joystick, as SDL joystick 0 axes 2-3 (y)/(n) +AppUsesSecondJoystick=n + +# Application uses accelerometer (y) or (n), the accelerometer will be used as joystick 1 axes 0-1 and 5-7 +AppUsesAccelerometer=n + +# Application uses gyroscope (y) or (n), the gyroscope will be used as joystick 1 axes 2-4 +AppUsesGyroscope=n + +# Application uses multitouch (y) or (n), multitouch events are passed as SDL_JOYBALLMOTION events for the joystick 0 +AppUsesMultitouch=y + +# Application records audio (it will use any available source, such a s microphone) +# API is defined in file SDL_android.h: int SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec); void SDL_ANDROID_CloseAudioRecording(void); +# This option will add additional permission to Android manifest (y)/(n) +AppRecordsAudio=n + +# Application implements Android-specific routines to put to background, and will not draw anything to screen +# between SDL_ACTIVEEVENT lost / gained notifications - you should check for them +# rigth after SDL_Flip(), if (n) then SDL_Flip() will block till app in background (y) or (n) +# This option is reported to be buggy, sometimes failing to restore video state +NonBlockingSwapBuffers=n + +# Redefine common hardware keys to SDL keysyms +# BACK hardware key is available on all devices, MENU is available on pre-ICS devices, other keys may be absent +# SEARCH and CALL by default return same keycode as DPAD_CENTER - one of those keys is available on most devices +# Use word NO_REMAP if you want to preserve native functionality for certain key (volume keys are 3-rd and 4-th) +# Keys: TOUCHSCREEN (works only when AppUsesMouse=n), DPAD_CENTER/SEARCH, VOLUMEUP, VOLUMEDOWN, MENU, BACK, CAMERA +RedefinedKeys="SPACE RETURN NO_REMAP NO_REMAP ESCAPE LCTRL F7 F4 F2 MOUSE_LEFT" + +# Number of virtual keyboard keys (currently 6 is maximum) +AppTouchscreenKeyboardKeysAmount=0 + +# Number of virtual keyboard keys that support autofire (currently 2 is maximum) +AppTouchscreenKeyboardKeysAmountAutoFire=0 + +# Redefine on-screen keyboard keys to SDL keysyms - 6 keyboard keys + 4 multitouch gestures (zoom in/out and rotate left/right) +RedefinedKeysScreenKb="MOUSE_RIGHT F7 LCTRL" + +# Names for on-screen keyboard keys, such as Fire, Jump, Run etc, separated by spaces, they are used in SDL config menu +RedefinedKeysScreenKbNames="MOUSE_RIGHT F7 LCTRL" + +# On-screen keys theme +# 0 = Ultimate Droid by Sean Stieber (green, with gamepad joystick) +# 1 = Simple Theme by Beholder (white, with gamepad joystick) +# 2 = Sun by Sirea (yellow, with round joystick) +# 3 = Keen by Gerstrong (multicolor, with round joystick) +TouchscreenKeysTheme=1 + +# Redefine gamepad keys to SDL keysyms, button order is: +# A B X Y L1 R1 L2 R2 LThumb RThumb +RedefinedKeysGamepad="MOUSE_RIGHT F7 LCTRL ESCAPE F5 SPACE RETURN MOUSE_LEFT" + +# How long to show startup menu button, in msec, 0 to disable startup menu +StartupMenuButtonTimeout=3000 + +# Menu items to hide from startup menu, available menu items: +# SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout +HiddenMenuOptions='SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMouse.DisplaySizeConfig' + +# Menu items to show at startup - this is Java code snippet, leave empty for default +# new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration() +# Available menu items: +# SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout +FirstStartMenuOptions='' + +# Enable multi-ABI binary, with hardware FPU support - it will also work on old devices, +# but .apk size is 2x bigger (y) / (n) / (x86) / (all) +MultiABI="armeabi-v7a" + +# Minimum amount of RAM application requires, in Mb, SDL will print warning to user if it's lower +AppMinimumRAM=256 + +# Application version code (integer) +AppVersionCode=19002 + +# Application user-visible version name (string) +AppVersionName="1.9.0git1661" + +# Reset SDL config when updating application to the new version (y) / (n) +ResetSdlConfigForThisVersion=y + +# Delete application data files when upgrading (specify file/dir paths separated by spaces) +DeleteFilesOnUpgrade="%" + +# Optional shared libraries to compile - removing some of them will save space +# MP3 support by libMAD is encumbered by patents and libMAD is GPL-ed +# Available libraries: mad (GPL-ed!) sdl_mixer sdl_image sdl_ttf sdl_net sdl_blitpool sdl_gfx sdl_sound intl xml2 lua jpeg png ogg flac tremor vorbis freetype xerces curl theora fluidsynth lzma lzo2 mikmod openal timidity zzip bzip2 yaml-cpp python boost_date_time boost_filesystem boost_iostreams boost_program_options boost_regex boost_signals boost_system boost_thread glu avcodec avdevice avfilter avformat avresample avutil swscale swresample bzip2 +CompiledLibraries="mad vorbis flac ogg jpeg png theora freetype faad" + +# Application uses custom build script AndroidBuild.sh instead of Android.mk (y) or (n) +CustomBuildScript=y + +# Aditional CFLAGS for application +AppCflags='' + +# Additional LDFLAGS for application +AppLdflags='' + +# If application has headers with the same name as system headers, this option tries to fix compiler flags to make it compilable +AppOverlapsSystemHeaders= + +# Build only following subdirs (empty will build all dirs, ignored with custom script) +AppSubdirsBuild='' + +# Exclude these files from build +AppBuildExclude='' + +# Application command line parameters, including app name as 0-th param +AppCmdline='' + +# Here you may type readme text, which will be shown during startup. Format is: +# Text in English, use \\\\n to separate lines^de:Text in Deutsch^ru:Text in Russian, and so on (that's four backslashes, nice isn't it?) +ReadmeText='^You may press "Home" now - the data will be downloaded in background' + +# Screen size is used by Google Play to prevent an app to be installed on devices with smaller screens +# Minimum screen size that application supports: (s)mall / (m)edium / (l)arge +MinimumScreenSize=s + +# Your AdMob Publisher ID, (n) if you don't want advertisements +AdmobPublisherId=n + +# Your AdMob test device ID, to receive a test ad +AdmobTestDeviceId= + +# Your AdMob banner size (BANNER/IAB_BANNER/IAB_LEADERBOARD/IAB_MRECT/IAB_WIDE_SKYSCRAPER/SMART_BANNER) +AdmobBannerSize= + +UseGlshim=n + +AccessSdCard=y
\ No newline at end of file diff --git a/dists/androidsdl/scummvm/AndroidBuild.sh b/dists/androidsdl/scummvm/AndroidBuild.sh new file mode 100644 index 0000000000..a7bf6ed446 --- /dev/null +++ b/dists/androidsdl/scummvm/AndroidBuild.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +LOCAL_PATH=`dirname $0` +LOCAL_PATH=`cd $LOCAL_PATH && pwd` + +#ln -sf libtremor.a $LOCAL_PATH/../../../obj/local/$1/libvorbisidec.a +ln -sf libflac.a $LOCAL_PATH/../../../obj/local/$1/libFLAC.a +ln -sf libvorbis.a $LOCAL_PATH/../../../obj/local/$1/libvorbisfile.a +ln -sf libtheora.so $LOCAL_PATH/../../../obj/local/$1/libtheoradec.so +ln -sf libglshim.a $LOCAL_PATH/../../../obj/local/$1/libGL.a + +if [ \! -f scummvm/config.mk ] ; then + ../setEnvironment-$1.sh sh -c "cd scummvm && env LIBS='-lflac -lvorbis -logg -lmad -lz -lgcc -ltheora -lpng -lfreetype -lfaad -lgnustl_static' ./configure --host=androidsdl-$1 --enable-zlib --enable-vorbis --enable-mad --enable-flac --enable-png --enable-theoradec --enable-vkeybd --enable-verbose-build --disable-readline --disable-nasm --disable-mt32emu --disable-timidity --disable-fluidsynth --opengl-mode=gles --enable-all-engines --datadir=. " +fi +../setEnvironment-$1.sh make -C scummvm -j2 && cp -f scummvm/scummvm libapplication-$1.so diff --git a/dists/androidsdl/scummvm/AndroidData/logo.png b/dists/androidsdl/scummvm/AndroidData/logo.png Binary files differnew file mode 100644 index 0000000000..553d97d25e --- /dev/null +++ b/dists/androidsdl/scummvm/AndroidData/logo.png diff --git a/dists/androidsdl/scummvm/DataBuild.sh b/dists/androidsdl/scummvm/DataBuild.sh new file mode 100755 index 0000000000..f38c82f8b1 --- /dev/null +++ b/dists/androidsdl/scummvm/DataBuild.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +LOCAL_PATH=`dirname $0` +LOCAL_PATH=`cd $LOCAL_PATH && pwd` + +rm AndroidData/* +make -C scummvm androidsdl +cp -f scummvm/scummvm*.z* AndroidData +rm scummvm/scummvm*.z*
\ No newline at end of file diff --git a/dists/androidsdl/scummvm/banner.png b/dists/androidsdl/scummvm/banner.png Binary files differnew file mode 100644 index 0000000000..28a7196b7f --- /dev/null +++ b/dists/androidsdl/scummvm/banner.png diff --git a/dists/androidsdl/scummvm/icon.png b/dists/androidsdl/scummvm/icon.png Binary files differnew file mode 100755 index 0000000000..03bc753aab --- /dev/null +++ b/dists/androidsdl/scummvm/icon.png diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat Binary files differindex 1b42cfbadc..d49d107f6f 100644 --- a/dists/engine-data/kyra.dat +++ b/dists/engine-data/kyra.dat diff --git a/dists/macosx/scummvm_appcast.xml b/dists/macosx/scummvm_appcast.xml index 3d47c94bbc..35fbc54908 100644 --- a/dists/macosx/scummvm_appcast.xml +++ b/dists/macosx/scummvm_appcast.xml @@ -6,6 +6,16 @@ <description>Most recent changes with links to updates.</description> <language>en</language> <item> + <title>Version 1.8.1</title> + <sparkle:releaseNotesLink> + https://scummvm.org/frs/scummvm/1.8.1/ReleaseNotes + </sparkle:releaseNotesLink> + <pubDate>Wed, 25 May 2016 19:26:00 +0000</pubDate> + <enclosure url="https://www.scummvm.org/frs/scummvm/1.8.1/scummvm-1.8.1-macosx.dmg" + sparkle:version="1.8.1" length="15791070" type="application/octet-stream" + sparkle:dsaSignature="MC0CFQDF0u/pGH51pMPzCbsv07eCNxuGDQIUdrKWVTznbF69fzuzIieR4Lc0U2Y=" /> + </item> + <item> <title>Version 1.8.0</title> <sparkle:releaseNotesLink> https://scummvm.org/frs/scummvm/1.8.0/ReleaseNotes diff --git a/doc/cz/PrectiMe b/doc/cz/PrectiMe index 3c03db9802..030ddac916 100644 --- a/doc/cz/PrectiMe +++ b/doc/cz/PrectiMe @@ -651,7 +651,7 @@ Abyste mohli spustit verzi pro Mac OS X od Wyrmkeep musУte data zkopУrovat zТ <http://wiki.scummvm.org/index.php/HOWTO-Mac_Games>
-I kdyХО se vТ tomto ФlУЁnku pУХЁe hlavnФ o hrУЁch SCUMM, je zde takУЉ zmУnФn nУЁstroj "HFVExplorer", kterУН potХebujete kТ extrakci souborХЏ. NezapomeХte, ХОe data ХeФi "Inherit the Earth Voices" musУte umУstit do stejnУЉho adresУЁХe, kde jsou uloХОena data hry:
+I kdyХО se vТ tomto ФlУЁnku pУХЁe hlavnФ o hrУЁch SCUMM, je zde takУЉ zmУnФn nУЁstroj "HFSExplorer", kterУН potХebujete kТ extrakci souborХЏ. NezapomeХte, ХОe data ХeФi "Inherit the Earth Voices" musУte umУstit do stejnУЉho adresУЁХe, kde jsou uloХОena data hry:
Inherit the Earth.app/Contents/Resources
@@ -915,7 +915,7 @@ Nebo mХЏХОete pouХОУt 'extract_mm_c64' zТ balУФku nУЁstrojХЏ pro extrahovУЁnУ 3.26) PoznУЁmky ke hrУЁm Macintosh:
----- ---------------------------
-VХЁechny adventury LucasArts zaloХОenУЉ na SCUMM, kromФ COMI, takУЉ existujУ ve verzУch pro in Macintosh. ScummVM mХЏХОe vФtХЁinu (vХЁechny?) pouХОУt, nicmУЉnФ, vТ nФkterУНch pХУpadech je nutnУЁ dodateФnУЁ prУЁce. NejdХУve, pokud pro toto nepouХОУvУЁte Macintosh, pХУstup kТ datХЏm na CD/disketФ mХЏХОe bУНt obtУХОnУН. DХЏvodem je to, ХОe Mac pouХОУvУЁ zvlУЁХЁtnУ formУЁt disku nazvanУН HFS, kterУН ostatnУ systУЉmy vФtХЁinou nepodporujУ. NicmУЉnФ existuje, nФkolik nУЁstrojХЏ, kterУЉ jsou zadarmo a umoХОХujУ ФУst takovУЉto svazky HFS. NapХУklad "HFVExplorer" pro Windows a "hfsutils" pro Linux a ostatnУ UnixovУЉ operaФnУ systУЉmy.
+VХЁechny adventury LucasArts zaloХОenУЉ na SCUMM, kromФ COMI, takУЉ existujУ ve verzУch pro in Macintosh. ScummVM mХЏХОe vФtХЁinu (vХЁechny?) pouХОУt, nicmУЉnФ, vТ nФkterУНch pХУpadech je nutnУЁ dodateФnУЁ prУЁce. NejdХУve, pokud pro toto nepouХОУvУЁte Macintosh, pХУstup kТ datХЏm na CD/disketФ mХЏХОe bУНt obtУХОnУН. DХЏvodem je to, ХОe Mac pouХОУvУЁ zvlУЁХЁtnУ formУЁt disku nazvanУН HFS, kterУН ostatnУ systУЉmy vФtХЁinou nepodporujУ. NicmУЉnФ existuje, nФkolik nУЁstrojХЏ, kterУЉ jsou zadarmo a umoХОХujУ ФУst takovУЉto svazky HFS. NapХУklad "HFSExplorer" pro Windows a "hfsutils" pro Linux a ostatnУ UnixovУЉ operaФnУ systУЉmy.
VФtХЁina novФjХЁУch her na Macintosh je dodУЁvУЁna pouze sТ jednУm datovУНm souborem (v nФkterУНch pХУpadech byl tento soubor uФinФn neviditelnУНm, takХОe moХОnУЁ budete potХebovat dodateФnУЉ nУЁstroje, abyste ho mohli zkopУrovat). ScummVM je schopen takovУНto soubor pouХОУt pХУmo; jednoduХЁe odkaХОte ScummVM na sloХОku obsahujУcУ tento soubor a mФlo by to fungovat (tak jako sТ kaХОdou podporovanou hrou).
diff --git a/doc/de/Liesmich b/doc/de/Liesmich index f37fc7d396..84eb6b53fd 100644 --- a/doc/de/Liesmich +++ b/doc/de/Liesmich @@ -878,7 +878,7 @@ einem PC arbeiten, lesen Sie hierfУМr: http://wiki.scummvm.org/index.php/HOWTO-Mac_Games Obwohl hier in erster Linie УМber SCUMM-Spiele gesprochen wird, findet das -Dienstprogramm тHFVExplorerт ErwУЄhnung, welches Sie benУЖtigen, um die Dateien zu +Dienstprogramm тHFSExplorerт ErwУЄhnung, welches Sie benУЖtigen, um die Dateien zu extrahieren. Beachten Sie, dass Sie die Sprachausgabedaten aus тInherit the Earth Voicesт im selben Verzeichnis ablegen mУМssen wie die Spieldaten, die sich an folgendem Ort befinden: @@ -1280,7 +1280,7 @@ kompliziert werden, auf die CD- oder Diskettendaten zuzugreifen. Der Grund hierfУМr ist, dass der Mac ein spezielles DatentrУЄgerformat nutzt, welches sich HFS nennt, und das andere Systeme normalerweise nicht unterstУМtzen. Es gibt jedoch zahlreiche kostenlose Tools, die es ermУЖglichen, einen solchen -HFS-DatentrУЄger zu lesen. Z. B. тHFVExplorerт fУМr Windows und тhfsutilsт fУМr +HFS-DatentrУЄger zu lesen. Z. B. тHFSExplorerт fУМr Windows und тhfsutilsт fУМr Linux und andere Betriebssysteme, die Unix УЄhnlich sind. Die meisten neueren Spiele fУМr den Macintosh wurden nur mit einer einzigen diff --git a/doc/de/Neues b/doc/de/Neues index e3d8f3a607..8015feb05b 100644 --- a/doc/de/Neues +++ b/doc/de/Neues @@ -11,7 +11,11 @@ Programmcodes finden Sie auf Englisch unter: Diese Funktion war im originalen Interpreter nur im Hercules-Darstellungsmodus verfУМgbar. -1.8.1 (DD.MM.YYYY) +1.8.1 (25.05.2016) + Neue Portierungen: + - Portierung fУМr den Nintendo 3DS hinzugefУМgt. + - Portierung fУМr Android SDL hinzugefУМgt. + Allgemein: - "TESTING"-Markierung von mehreren unterstУМtzten Spielen entfernt. - Chinesische Уbersetzung (Pinyin) der BenutzeroberflУЄche hinzugefУМgt. @@ -51,6 +55,7 @@ Programmcodes finden Sie auf Englisch unter: Lab: - AufhУЄngen wУЄhrend der End-Sequenz behoben. - Interne Spiel-Bedienelemente verbessert. + - AufhУЄngen bei einigen Animationen im Spiel behoben. SAGA: - Fehlerhafte Farben der Bedienelemente in der franzУЖsischen und deutschen diff --git a/engines/access/amazon/amazon_logic.cpp b/engines/access/amazon/amazon_logic.cpp index e78f92cda7..08006fe1b7 100644 --- a/engines/access/amazon/amazon_logic.cpp +++ b/engines/access/amazon/amazon_logic.cpp @@ -185,16 +185,24 @@ void CampScene::mWhileDoOpen() { _vm->_numAnimTimers = 0; _vm->_images.clear(); - if (_vm->_conversation == 2) { - // Cutscene at end of Chapter 6 - Resource *spriteData = _vm->_files->loadFile(28, 37); - _vm->_objectsTable[28] = new SpriteResource(_vm, spriteData); - delete spriteData; - - _vm->_animation->freeAnimationData(); - animResource = _vm->_files->loadFile(28, 38); - _vm->_animation->loadAnimations(animResource); - delete animResource; + if (_vm->isCD()) { + if (_vm->_conversation == 2) { + // Cutscene at end of Chapter 6 + Resource *spriteData = _vm->_files->loadFile(28, 37); + _vm->_objectsTable[28] = new SpriteResource(_vm, spriteData); + delete spriteData; + + _vm->_animation->freeAnimationData(); + animResource = _vm->_files->loadFile(28, 38); + _vm->_animation->loadAnimations(animResource); + delete animResource; + } + } else { + _vm->freeCells(); + _vm->_oldRects.clear(); + _vm->_newRects.clear(); + _vm->_numAnimTimers = 0; + _vm->_images.clear(); } } diff --git a/engines/access/bubble_box.cpp b/engines/access/bubble_box.cpp index ef32e96f59..e55701900a 100644 --- a/engines/access/bubble_box.cpp +++ b/engines/access/bubble_box.cpp @@ -165,7 +165,7 @@ void BubbleBox::printBubble(const Common::String &msg) { void BubbleBox::printBubble_v1(const Common::String &msg) { drawBubble(_bubbles.size() - 1); - + // Loop through drawing the lines Common::String s = msg; Common::String line; @@ -369,7 +369,7 @@ void BubbleBox::displayBoxData() { _vm->_screen->drawRect(); _vm->_events->showCursor(); } - + _vm->_events->hideCursor(); int oldPStartY = _boxPStartY; ++_boxPStartY; @@ -474,7 +474,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { --_vm->_screen->_orgX2; --_vm->_screen->_orgY2; _vm->_screen->_lColor = 0xF9; - + // Draw the inner border _vm->_screen->drawBox(); @@ -611,6 +611,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { _vm->_events->showCursor(); warning("TODO: pop values"); _vm->_screen->restoreScreen(); + delete icons; return retval_; } @@ -642,7 +643,9 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { } } } - + + delete icons; + _vm->_screen->restoreScreen(); _vm->_boxDataStart = _startItem; _vm->_boxSelectYOld = -1; @@ -732,7 +735,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { if (_type != TYPE_3) continue; - + if ((_vm->_events->_mousePos.x < tmpX) || (_vm->_events->_mousePos.x > tmpX + 144)) continue; diff --git a/engines/access/char.cpp b/engines/access/char.cpp index cbe1d5d3d9..f6d3033b1b 100644 --- a/engines/access/char.cpp +++ b/engines/access/char.cpp @@ -44,7 +44,7 @@ CharEntry::CharEntry(const byte *data, AccessEngine *vm) { if (vm->getGameID() == GType_MartianMemorandum) { int lastColor = s.readUint16LE(); _numColors = lastColor - _startColor; - } else + } else _numColors = s.readUint16LE(); // Load cells @@ -131,6 +131,7 @@ void CharManager::loadChar(int charId) { if (ce._animFile._fileNum != -1) { Resource *data = _vm->_files->loadFile(ce._animFile); _vm->_animation->loadAnimations(data); + delete data; } // Load script data diff --git a/engines/access/inventory.cpp b/engines/access/inventory.cpp index 0a962aa69a..e9874cd8d6 100644 --- a/engines/access/inventory.cpp +++ b/engines/access/inventory.cpp @@ -223,6 +223,7 @@ int InventoryManager::displayInv() { else _vm->_useItem = -1; + free(names); free(inv); return 0; } diff --git a/engines/access/room.cpp b/engines/access/room.cpp index a7192d330f..a41de63bf6 100644 --- a/engines/access/room.cpp +++ b/engines/access/room.cpp @@ -142,7 +142,7 @@ void Room::takePicture() { _vm->_player->_roomNumber = 7; _vm->_room->_function = FN_CLEAR1; return; - } else if (result >= 0) + } else if (result >= 0) _vm->_player->_move = (Direction)(result + 1); _vm->_player->_scrollFlag = false; @@ -715,6 +715,8 @@ void Room::executeCommand(int commandId) { screen.plotImage(spr, _selectCommand + 2, Common::Point(_rMouse[_selectCommand][0], (_vm->getGameID() == GType_MartianMemorandum) ? 184 : 176)); + delete spr; + _vm->_screen->restoreScreen(); _vm->_boxSelect = true; } diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 1baf0d912f..79d05c4b1d 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -20,8 +20,8 @@ * */ -#ifndef AGI_H -#define AGI_H +#ifndef AGI_AGI_H +#define AGI_AGI_H #include "common/scummsys.h" #include "common/error.h" @@ -991,4 +991,4 @@ private: } // End of namespace Agi -#endif /* AGI_H */ +#endif /* AGI_AGI_H */ diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 6d3563a451..c7d643d6a0 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -1178,13 +1178,17 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte #define SHAKE_HORIZONTAL_PIXELS 4 // Sierra used some EGA port trickery to do it, we have to do it by copying pixels around +// +// Shaking locations: +// - Fanmade "Enclosure" right during the intro +// - Space Quest 2 almost right at the start when getting captured (after walking into the space ship) void GfxMgr::shakeScreen(int16 repeatCount) { int shakeNr, shakeCount; uint8 *blackSpace; int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust); int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust); - if ((blackSpace = (uint8 *)calloc(shakeVerticalPixels * _displayScreenWidth, 1)) == NULL) + if ((blackSpace = (uint8 *)calloc(shakeHorizontalPixels * _displayScreenWidth, 1)) == NULL) return; shakeCount = repeatCount * 8; // effectively 4 shakes per repeat diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index fed07ea986..8a62fce86c 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -662,7 +662,7 @@ void cmdWordToString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { uint16 stringNr = parameter[0]; uint16 wordNr = parameter[1]; - strcpy(state->strings[stringNr], state->_vm->_words->getEgoWord(wordNr)); + Common::strlcpy(state->strings[stringNr], state->_vm->_words->getEgoWord(wordNr), MAX_STRINGLEN); } void cmdOpenDialogue(AgiGame *state, AgiEngine *vm, uint8 *parameter) { @@ -2014,7 +2014,7 @@ void cmdGetString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { // copy string to destination // TODO: not sure if set all the time or only when ENTER is pressed - strcpy(&state->_vm->_game.strings[stringDestNr][0], (char *)textMgr->_inputString); + Common::strlcpy(&state->_vm->_game.strings[stringDestNr][0], (char *)textMgr->_inputString, MAX_STRINGLEN); textMgr->charPos_Pop(); @@ -2102,7 +2102,7 @@ void cmdSetString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { // CM: to avoid crash in Groza (str = 150) if (stringNr > MAX_STRINGS) return; - strcpy(state->strings[stringNr], state->_curLogic->texts[textNr]); + Common::strlcpy(state->strings[stringNr], state->_curLogic->texts[textNr], MAX_STRINGLEN); } void cmdDisplay(AgiGame *state, AgiEngine *vm, uint8 *parameter) { diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp index 4b215edc63..4505668fd1 100644 --- a/engines/agi/op_test.cpp +++ b/engines/agi/op_test.cpp @@ -231,8 +231,8 @@ uint8 AgiEngine::testCompareStrings(uint8 s1, uint8 s2) { char ms2[MAX_STRINGLEN]; int j, k, l; - strcpy(ms1, _game.strings[s1]); - strcpy(ms2, _game.strings[s2]); + Common::strlcpy(ms1, _game.strings[s1], MAX_STRINGLEN); + Common::strlcpy(ms2, _game.strings[s2], MAX_STRINGLEN); l = strlen(ms1); for (k = 0, j = 0; k < l; k++) { diff --git a/engines/agi/preagi_mickey.cpp b/engines/agi/preagi_mickey.cpp index 620d5e0baf..e1545cdb68 100644 --- a/engines/agi/preagi_mickey.cpp +++ b/engines/agi/preagi_mickey.cpp @@ -1205,7 +1205,7 @@ void MickeyEngine::printStory() { clearScreen(IDA_DEFAULT); for (iRow = 0; iRow < 25; iRow++) { - strcpy(szLine, buffer + pBuf); + Common::strlcpy(szLine, buffer + pBuf, 41); drawStr(iRow, 0, IDA_DEFAULT, szLine); pBuf += strlen(szLine) + 1; } @@ -1213,7 +1213,7 @@ void MickeyEngine::printStory() { clearScreen(IDA_DEFAULT); for (iRow = 0; iRow < 21; iRow++) { - strcpy(szLine, buffer + pBuf); + Common::strlcpy(szLine, buffer + pBuf, 41); drawStr(iRow, 0, IDA_DEFAULT, szLine); pBuf += strlen(szLine) + 1; } diff --git a/engines/agi/preagi_winnie.cpp b/engines/agi/preagi_winnie.cpp index 87ac7c19c6..8fb9daca5e 100644 --- a/engines/agi/preagi_winnie.cpp +++ b/engines/agi/preagi_winnie.cpp @@ -292,7 +292,7 @@ int WinnieEngine::parser(int pc, int index, uint8 *buffer) { } // extract menu string - strcpy(szMenu, (char *)(buffer + pc)); + Common::strlcpy(szMenu, (char *)(buffer + pc), 121); XOR80(szMenu); break; default: diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 0658609cd0..aa46cf4e9f 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -118,7 +118,7 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de out->writeByte(2); // was _game.state, 2 = STATE_RUNNING - strcpy(gameIDstring, _game.id); + Common::strlcpy(gameIDstring, _game.id, 8); out->write(gameIDstring, 8); debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing game id (%s, %s)", gameIDstring, _game.id); @@ -689,9 +689,6 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { } } } - for (i = vtEntries; i < SCREENOBJECTS_MAX; i++) { - memset(&_game.screenObjTable[i], 0, sizeof(ScreenObjEntry)); - } // Fix some pointers in screenObjTable diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 110ba10632..274a654547 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -457,7 +457,7 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want // Caller wants to force specified width/height? set it if (forcedHeight) _messageState.textSize_Height = forcedHeight; - + if (forcedWidth) { if (wantedWidth) _messageState.textSize_Width = wantedWidth; @@ -1207,7 +1207,7 @@ char *TextMgr::stringPrintf(const char *originalText) { } assert(resultString.size() < sizeof(resultPrintfBuffer)); - strcpy(resultPrintfBuffer, resultString.c_str()); + Common::strlcpy(resultPrintfBuffer, resultString.c_str(), 2000); return resultPrintfBuffer; } diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index 66a7fa90b3..1ba0f56353 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -494,7 +494,7 @@ void AGOSEngine_Elvira2::userGame(bool load) { i = userGameGetKey(&b, 128); if (b) { - if (i <= 223) { + if (i <= 23) { if (!confirmOverWrite(window)) { listSaveGames(); continue; diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp index 4f6c62c48a..cc443f2f50 100644 --- a/engines/agos/string.cpp +++ b/engines/agos/string.cpp @@ -126,14 +126,14 @@ const byte *AGOSEngine::getStringPtrByID(uint16 stringId, bool upperCase) { _awaitTwoByteToken = 0; uncompressText(ptr); _textBuffer[_textCount] = 0; - strcpy((char *)dst, (const char *)_textBuffer); + Common::strlcpy((char *)dst, (const char *)_textBuffer, 180); } else { if (stringId < 0x8000) { stringPtr = _stringTabPtr[stringId]; } else { stringPtr = getLocalStringByID(stringId); } - strcpy((char *)dst, (const char *)stringPtr); + Common::strlcpy((char *)dst, (const char *)stringPtr, 180); } // WORKAROUND bug #1538873: The French version of Simon 1 and the @@ -796,7 +796,7 @@ void AGOSEngine_Feeble::printInteractText(uint16 num, const char *string) { if (*string2 == 0x00) { if (w == 0xFFFF) w = pixels; - strcpy(convertedString2, string); + Common::strlcpy(convertedString2, string, 320); break; } while (*string2 != ' ') { diff --git a/engines/agos/string_pn.cpp b/engines/agos/string_pn.cpp index 7a364f3ea9..06c8bbd98e 100644 --- a/engines/agos/string_pn.cpp +++ b/engines/agos/string_pn.cpp @@ -114,7 +114,7 @@ void AGOSEngine_PN::getObjectName(char *v, uint16 x) { } void AGOSEngine_PN::pcl(const char *s) { - strcat(_sb, s); + Common::strlcat(_sb, s, 80); if (strchr(s, '\n') == 0) { for (char *str = _sb; *str; str++) windowPutChar(_windowArray[_curWindow], *str); diff --git a/engines/cge/cge.h b/engines/cge/cge.h index c43358f252..d3f8a93c1d 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -20,8 +20,8 @@ * */ -#ifndef CGE_H -#define CGE_H +#ifndef CGE_CGE_H +#define CGE_CGE_H #include "common/random.h" #include "common/savefile.h" diff --git a/engines/cge2/cge2.h b/engines/cge2/cge2.h index fbe4cb3abc..18f919b5eb 100644 --- a/engines/cge2/cge2.h +++ b/engines/cge2/cge2.h @@ -25,8 +25,8 @@ * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon */ -#ifndef CGE2_H -#define CGE2_H +#ifndef CGE2_CGE2_H +#define CGE2_CGE2_H #include "common/random.h" #include "common/savefile.h" @@ -335,4 +335,4 @@ public: } // End of namespace CGE2 -#endif // CGE2_H +#endif // CGE2_CGE2_H diff --git a/engines/cine/cine.h b/engines/cine/cine.h index 7c0191b4d9..615e36d598 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -20,8 +20,8 @@ * */ -#ifndef CINE_H -#define CINE_H +#ifndef CINE_CINE_H +#define CINE_CINE_H #include "common/scummsys.h" diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 234494e655..d1a85e975a 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -20,8 +20,8 @@ * */ -#ifndef COMPOSER_H -#define COMPOSER_H +#ifndef COMPOSER_COMPOSER_H +#define COMPOSER_COMPOSER_H #include "common/ini-file.h" #include "common/random.h" diff --git a/engines/cruise/cell.cpp b/engines/cruise/cell.cpp index b7cef41764..46539463b8 100644 --- a/engines/cruise/cell.cpp +++ b/engines/cruise/cell.cpp @@ -129,15 +129,12 @@ void createTextObject(cellStruct *pObject, int overlayIdx, int messageIdx, int x cellStruct *pNewElement; cellStruct *si = pObject->next; - cellStruct *var_2; while (si) { pObject = si; si = si->next; } - var_2 = si; - pNewElement = (cellStruct *) MemAlloc(sizeof(cellStruct)); memset(pNewElement, 0, sizeof(cellStruct)); @@ -157,11 +154,7 @@ void createTextObject(cellStruct *pObject, int overlayIdx, int messageIdx, int x pNewElement->parentOverlay = parentOvl; pNewElement->gfxPtr = NULL; - if (var_2) { - cx = var_2; - } else { - cx = savePObject; - } + cx = savePObject; pNewElement->prev = cx->prev; cx->prev = pNewElement; diff --git a/engines/cruise/ctp.cpp b/engines/cruise/ctp.cpp index 9515b552e1..4458e39e91 100644 --- a/engines/cruise/ctp.cpp +++ b/engines/cruise/ctp.cpp @@ -307,7 +307,7 @@ int initCt(const char *ctpName) { MemFree(ptr); if (ctpName != currentCtpName) - strcpy(currentCtpName, ctpName); + Common::strlcpy(currentCtpName, ctpName, 40); numberOfWalkboxes = segementSizeTable[6] / 2; // get the number of walkboxes diff --git a/engines/cruise/dataLoader.cpp b/engines/cruise/dataLoader.cpp index 7d00d0871c..2eff82bc61 100644 --- a/engines/cruise/dataLoader.cpp +++ b/engines/cruise/dataLoader.cpp @@ -258,7 +258,10 @@ int loadFile(const char* name, int idx, int destIdx) { return res; } case type_FNT: { - return loadFNTSub(ptr, idx); + int res = loadFNTSub(ptr, idx); + MemFree(ptr); + + return res; } case type_SPL: { // Sound file diff --git a/engines/draci/draci.h b/engines/draci/draci.h index 540c288d3d..90afdbcf60 100644 --- a/engines/draci/draci.h +++ b/engines/draci/draci.h @@ -20,8 +20,8 @@ * */ -#ifndef DRACI_H -#define DRACI_H +#ifndef DRACI_DRACI_H +#define DRACI_DRACI_H #include "engines/engine.h" #include "common/random.h" @@ -126,4 +126,4 @@ static inline long scummvm_lround(double val) { return (long)floor(val + 0.5); } } // End of namespace Draci -#endif // DRACI_H +#endif // DRACI_DRACI_H diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h index acca2e5915..c879a83db7 100644 --- a/engines/drascula/drascula.h +++ b/engines/drascula/drascula.h @@ -20,8 +20,8 @@ * */ -#ifndef DRASCULA_H -#define DRASCULA_H +#ifndef DRASCULA_DRASCULA_H +#define DRASCULA_DRASCULA_H #include "common/scummsys.h" #include "common/archive.h" @@ -784,4 +784,4 @@ protected: } // End of namespace Drascula -#endif /* DRASCULA_H */ +#endif /* DRASCULA_DRASCULA_H */ diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h index 2e5fb424f8..45bacdba86 100644 --- a/engines/dreamweb/dreamweb.h +++ b/engines/dreamweb/dreamweb.h @@ -20,8 +20,8 @@ * */ -#ifndef DREAMWEB_H -#define DREAMWEB_H +#ifndef DREAMWEB_DREAMWEB_H +#define DREAMWEB_DREAMWEB_H #include "common/error.h" #include "common/keyboard.h" diff --git a/engines/fullpipe/motion.cpp b/engines/fullpipe/motion.cpp index 1a61cb742a..9cf18f3cc2 100644 --- a/engines/fullpipe/motion.cpp +++ b/engines/fullpipe/motion.cpp @@ -855,9 +855,9 @@ Common::Array<MovItem *> *MovGraph::getPaths(StaticANIObject *ani, int x, int y, if (sz > 0) { for (int j = 0; j < sz; j++) _items[idx]->movitems->push_back(movitems[j]); - - delete movitems; } + + delete movitems; } delete movarr; diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index d995f26d9f..b51a6382e6 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -26,6 +26,7 @@ #include "base/plugins.h" #include "common/config-manager.h" #include "audio/mididrv.h" +#include "audio/mixer.h" #include "gui/gui-manager.h" #include "gui/dialog.h" diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp index 45f573efcd..13d24dc05d 100644 --- a/engines/gob/inter_playtoons.cpp +++ b/engines/gob/inter_playtoons.cpp @@ -41,7 +41,6 @@ #include "gob/video.h" #include "gob/videoplayer.h" #include "gob/save/saveload.h" -#include "gob/sound/sound.h" namespace Gob { diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp index 656ca6f5c3..d379d5ab11 100644 --- a/engines/gob/inter_v4.cpp +++ b/engines/gob/inter_v4.cpp @@ -205,7 +205,7 @@ void Inter_v4::o4_playVmdOrMusic() { return; } else if (props.lastFrame == -9) { _vm->_sound->bgStop(); - _vm->_sound->bgSetPlayMode(BackgroundAtmosphere::kPlayModeRandom); + _vm->_sound->bgSetPlayMode(Sound::kPlayModeRandom); _vm->_sound->bgPlay(file.c_str(), "SND", SOUND_SND, props.palStart); return; } else if (props.lastFrame < 0) { diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp index a6e4da75e7..50910e77bd 100644 --- a/engines/gob/pregob/onceupon/onceupon.cpp +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -30,8 +30,6 @@ #include "gob/anifile.h" #include "gob/aniobject.h" -#include "gob/sound/sound.h" - #include "gob/pregob/txtfile.h" #include "gob/pregob/gctfile.h" diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h index 021cf2b3d6..108771a63a 100644 --- a/engines/gob/pregob/pregob.h +++ b/engines/gob/pregob/pregob.h @@ -29,14 +29,14 @@ #include "gob/util.h" #include "gob/aniobject.h" -#include "gob/sound/sounddesc.h" - #include "gob/pregob/txtfile.h" namespace Gob { class GobEngine; +class ANIFile; class Surface; +class SoundDesc; class GCTFile; diff --git a/engines/gob/sound/bgatmosphere.cpp b/engines/gob/sound/bgatmosphere.cpp index 21fb70278a..c7be1be96a 100644 --- a/engines/gob/sound/bgatmosphere.cpp +++ b/engines/gob/sound/bgatmosphere.cpp @@ -23,6 +23,7 @@ #include "common/array.h" #include "gob/sound/bgatmosphere.h" +#include "gob/sound/sound.h" #include "gob/sound/sounddesc.h" namespace Gob { @@ -30,7 +31,7 @@ namespace Gob { BackgroundAtmosphere::BackgroundAtmosphere(Audio::Mixer &mixer) : SoundMixer(mixer, Audio::Mixer::kMusicSoundType), _rnd("gobBA") { - _playMode = kPlayModeLinear; + _playMode = Sound::kPlayModeLinear; _queuePos = -1; _shaded = false; _shadable = true; @@ -56,7 +57,7 @@ void BackgroundAtmosphere::stopBA() { SoundMixer::stop(0); } -void BackgroundAtmosphere::setPlayMode(PlayMode mode) { +void BackgroundAtmosphere::setPlayMode(Sound::BackgroundPlayMode mode) { _playMode = mode; } @@ -100,11 +101,11 @@ void BackgroundAtmosphere::getNextQueuePos() { switch (_playMode) { - case kPlayModeLinear: + case Sound::kPlayModeLinear: _queuePos = (_queuePos + 1) % _queue.size(); break; - case kPlayModeRandom: + case Sound::kPlayModeRandom: _queuePos = _rnd.getRandomNumber(_queue.size() - 1); break; diff --git a/engines/gob/sound/bgatmosphere.h b/engines/gob/sound/bgatmosphere.h index c838a2c2bb..138b65a1c1 100644 --- a/engines/gob/sound/bgatmosphere.h +++ b/engines/gob/sound/bgatmosphere.h @@ -27,6 +27,7 @@ #include "common/mutex.h" #include "common/random.h" +#include "gob/sound/sound.h" #include "gob/sound/soundmixer.h" namespace Audio { @@ -39,18 +40,13 @@ class SoundDesc; class BackgroundAtmosphere : private SoundMixer { public: - enum PlayMode { - kPlayModeLinear, - kPlayModeRandom - }; - BackgroundAtmosphere(Audio::Mixer &mixer); ~BackgroundAtmosphere(); void playBA(); void stopBA(); - void setPlayMode(PlayMode mode); + void setPlayMode(Sound::BackgroundPlayMode mode); void queueSample(SoundDesc &sndDesc); void queueClear(); @@ -60,7 +56,7 @@ public: void unshade(); private: - PlayMode _playMode; + Sound::BackgroundPlayMode _playMode; Common::Array<SoundDesc *> _queue; int _queuePos; diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 22dfe9d3c3..000eafa031 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -28,6 +28,7 @@ #include "gob/game.h" #include "gob/inter.h" +#include "gob/sound/bgatmosphere.h" #include "gob/sound/pcspeaker.h" #include "gob/sound/soundblaster.h" #include "gob/sound/adlplayer.h" @@ -717,7 +718,7 @@ void Sound::bgStop() { _bgatmos->queueClear(); } -void Sound::bgSetPlayMode(BackgroundAtmosphere::PlayMode mode) { +void Sound::bgSetPlayMode(Sound::BackgroundPlayMode mode) { if (!_bgatmos) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index 6ebc323b18..f1fd46d24b 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -23,12 +23,13 @@ #ifndef GOB_SOUND_SOUND_H #define GOB_SOUND_SOUND_H +#include "common/str.h" #include "gob/sound/sounddesc.h" -#include "gob/sound/bgatmosphere.h" namespace Gob { class GobEngine; +class BackgroundAtmosphere; class PCSpeaker; class SoundBlaster; class ADLPlayer; @@ -39,6 +40,11 @@ class CDROM; class Sound { public: + enum BackgroundPlayMode { + kPlayModeLinear, + kPlayModeRandom + }; + static const int kSoundsCount = 60; Sound(GobEngine *vm); @@ -135,7 +141,7 @@ public: void bgPlay(const char *base, const char *ext, SoundType type, int count); void bgStop(); - void bgSetPlayMode(BackgroundAtmosphere::PlayMode mode); + void bgSetPlayMode(BackgroundPlayMode mode); void bgShade(); void bgUnshade(); diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp index e97848d27e..bbf4ef4162 100644 --- a/engines/gob/videoplayer.cpp +++ b/engines/gob/videoplayer.cpp @@ -21,6 +21,8 @@ */ +#include "video/coktel_decoder.h" + #include "gob/videoplayer.h" #include "gob/global.h" #include "gob/dataio.h" diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h index 02ed510ec5..1c39ecf2fa 100644 --- a/engines/gob/videoplayer.h +++ b/engines/gob/videoplayer.h @@ -29,11 +29,14 @@ #include "common/str.h" #include "graphics/surface.h" -#include "video/coktel_decoder.h" #include "gob/util.h" #include "gob/draw.h" +namespace Video { +class CoktelDecoder; +} + namespace Gob { class GobEngine; diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h index d442d39cb2..c2994d20cc 100644 --- a/engines/groovie/groovie.h +++ b/engines/groovie/groovie.h @@ -20,8 +20,8 @@ * */ -#ifndef GROOVIE_H -#define GROOVIE_H +#ifndef GROOVIE_GROOVIE_H +#define GROOVIE_GROOVIE_H #include "groovie/debug.h" #include "groovie/font.h" @@ -132,4 +132,4 @@ private: } // End of namespace Groovie -#endif // GROOVIE_H +#endif // GROOVIE_GROOVIE_H diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h index 27dfea8725..85209afe06 100644 --- a/engines/hugo/hugo.h +++ b/engines/hugo/hugo.h @@ -20,8 +20,8 @@ * */ -#ifndef HUGO_H -#define HUGO_H +#ifndef HUGO_HUGO_H +#define HUGO_HUGO_H #include "engines/engine.h" @@ -341,4 +341,4 @@ private: } // End of namespace Hugo -#endif // Hugo_H +#endif // HUGO_HUGO_H diff --git a/engines/kyra/items_lok.cpp b/engines/kyra/items_lok.cpp index 55a23b2a1a..3a2e631744 100644 --- a/engines/kyra/items_lok.cpp +++ b/engines/kyra/items_lok.cpp @@ -844,8 +844,6 @@ void KyraEngine_LoK::updatePlayerItemsForScene() { uint8 item = _currentCharacter->inventoryItems[i]; if (item >= 29 && item < 33) { ++item; - if (item > 33) - item = 33; _currentCharacter->inventoryItems[i] = item; redraw = true; } diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index 3ab08a4c7c..c3ebf6e5fe 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -203,7 +203,6 @@ enum KyraResources { k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, k1CreditsStrings, diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 1a2e2c093c..e99321ddcb 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -39,7 +39,7 @@ namespace Kyra { -#define RESFILE_VERSION 87 +#define RESFILE_VERSION 88 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { @@ -805,19 +805,11 @@ void KyraEngine_LoK::initStaticResource() { } // audio resource assignment - int size1, size2; - const char *const *soundfiles1 = _staticres->loadStrings(k1AudioTracks, size1); - const char *const *soundfiles2 = _staticres->loadStrings(k1AudioTracks2, size2); - int soundFilesSize = size1 + size2; + int soundFilesSize; + const char *const *soundFiles = _staticres->loadStrings(k1AudioTracks, soundFilesSize); int soundFilesIntroSize = 0; int cdaTableSize = 0; - const char **soundFiles = 0; - if (soundFilesSize) { - soundFiles = new const char*[soundFilesSize]; - for (int i = 0; i < soundFilesSize; i++) - soundFiles[i] = (i < size1) ? soundfiles1[i] : soundfiles2[i - size1]; - } const char *const *soundFilesIntro = _staticres->loadStrings(k1AudioTracksIntro, soundFilesIntroSize); const int32 *cdaTable = (const int32 *)_staticres->loadRawData(k1TownsCDATable, cdaTableSize); diff --git a/engines/lab/anim.cpp b/engines/lab/anim.cpp index 1190f0323b..2dc580735e 100644 --- a/engines/lab/anim.cpp +++ b/engines/lab/anim.cpp @@ -208,6 +208,8 @@ void Anim::diffNextFrame(bool onlyDiffData) { _vm->updateEvents(); _vm->waitTOF(); } + + _waitForEffect = false; } _size -= 8; @@ -217,7 +219,9 @@ void Anim::diffNextFrame(bool onlyDiffData) { _diffFile->skip(2); // Sound effects embedded in animations are started here. These are - // usually animation-specific, like door opening sounds, and are not looped + // usually animation-specific, like door opening sounds, and are not looped. + // The engine should wait for all such sounds to end. + _waitForEffect = true; _vm->_music->playSoundEffect(_sampleSpeed, _size, false, _diffFile); break; @@ -233,6 +237,8 @@ void Anim::diffNextFrame(bool onlyDiffData) { if (drawOnScreen) didTOF = true; } + + _waitForEffect = false; } _isPlaying = false; diff --git a/engines/lab/processroom.cpp b/engines/lab/processroom.cpp index 5093e8ef85..68e6e63c1d 100644 --- a/engines/lab/processroom.cpp +++ b/engines/lab/processroom.cpp @@ -254,16 +254,9 @@ void LabEngine::doActions(const ActionList &actionList) { _music->loadSoundEffect(action->_messages[0], true, false); break; - case kActionShowDiff: { - bool curWait = _anim->_waitForEffect; - // Pause the engine until the sound is finished - _anim->_waitForEffect = true; + case kActionShowDiff: _graphics->readPict(action->_messages[0], true); - - // Restore the previous value of _waitForEffect - _anim->_waitForEffect = curWait; break; - } case kActionShowDiffLooping: // used in scene 44 (heart of the labyrinth, minotaur) _graphics->readPict(action->_messages[0], false); diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp index b628b8dfe7..1b51dd2006 100644 --- a/engines/lastexpress/entities/gendarmes.cpp +++ b/engines/lastexpress/entities/gendarmes.cpp @@ -174,7 +174,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, doCompartment, CarIndex, EntityPosition) strcat((char *)¶meters1->seq1, (char *)¶ms->seq1); strcat((char *)¶meters1->seq2, (char *)¶ms->seq1); - strcat((char *)¶meters1->seq3, (char *)¶ms->seq1); + Common::strlcat((char *)¶meters1->seq3, (char *)¶ms->seq1, 9); // Beware, seq3 is smaller than seq1 if ((getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)params->param2) || getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)parameters2->param7) diff --git a/engines/lastexpress/lastexpress.h b/engines/lastexpress/lastexpress.h index b4098f3860..b33784b1e8 100644 --- a/engines/lastexpress/lastexpress.h +++ b/engines/lastexpress/lastexpress.h @@ -20,8 +20,8 @@ * */ -#ifndef LASTEXPRESS_H -#define LASTEXPRESS_H +#ifndef LASTEXPRESS_LASTEXPRESS_H +#define LASTEXPRESS_LASTEXPRESS_H #include "lastexpress/debug.h" #include "lastexpress/eventhandler.h" @@ -146,4 +146,4 @@ private: } // End of namespace LastExpress -#endif // LASTEXPRESS_H +#endif // LASTEXPRESS_LASTEXPRESS_H diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp index 38ca0ba54f..84cc91ec9a 100644 --- a/engines/lure/game.cpp +++ b/engines/lure/game.cpp @@ -538,7 +538,7 @@ void Game::handleRightClickMenu() { hotspot = res.getHotspot(room.hotspotId()); assert(hotspot); strings.getString(hotspot->nameId, statusLine); - strcat(statusLine, stringList.getString(S_FOR)); + Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE); statusLine += strlen(statusLine); itemId = PopupMenu::ShowItems(GET, player->roomNumber()); @@ -549,7 +549,7 @@ void Game::handleRightClickMenu() { hotspot = res.getHotspot(room.hotspotId()); assert(hotspot); strings.getString(hotspot->nameId, statusLine); - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); breakFlag = GetTellActions(); break; @@ -559,7 +559,7 @@ void Game::handleRightClickMenu() { case DRINK: hasItems = (res.numInventoryItems() != 0); if (!hasItems) - strcat(statusLine, stringList.getString(S_ACTION_NOTHING)); + Common::strlcat(statusLine, stringList.getString(S_ACTION_NOTHING), MAX_DESC_SIZE); statusLine += strlen(statusLine); room.update(); @@ -579,9 +579,9 @@ void Game::handleRightClickMenu() { assert(useHotspot); strings.getString(useHotspot->nameId, statusLine); if (action == GIVE) - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); else - strcat(statusLine, stringList.getString(S_ON)); + Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE); statusLine += strlen(statusLine); } else if ((action == DRINK) || (action == EXAMINE)) @@ -762,11 +762,11 @@ bool Game::GetTellActions() { // Second parameter action = (Action) commands[_numTellCommands * 3]; if (action == ASK) - strcat(statusLine, stringList.getString(S_FOR)); + Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE); else if (action == GIVE) - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); else if (action == USE) - strcat(statusLine, stringList.getString(S_ON)); + Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE); else { // All other commads don't need a second parameter ++paramIndex; diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp index fbf93e1e14..29e5d2832e 100644 --- a/engines/lure/hotspots.cpp +++ b/engines/lure/hotspots.cpp @@ -1898,8 +1898,8 @@ void Hotspot::doStatus(HotspotData *hotspot) { endAction(); strings.getString(room.roomNumber(), buffer); - strcat(buffer, "\n\n"); - strcat(buffer, stringList.getString(S_YOU_ARE_CARRYING)); + Common::strlcat(buffer, "\n\n", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString(S_YOU_ARE_CARRYING), MAX_DESC_SIZE); // Scan through the list and add in any items assigned to the player HotspotDataList &list = res.hotspotData(); @@ -1909,25 +1909,25 @@ void Hotspot::doStatus(HotspotData *hotspot) { if (rec.roomNumber == PLAYER_ID) { if (numItems++ == 0) - strcat(buffer, ": "); + Common::strlcat(buffer, ": ", MAX_DESC_SIZE); else - strcat(buffer, ", "); + Common::strlcat(buffer, ", ", MAX_DESC_SIZE); strings.getString(rec.nameId, buffer + strlen(buffer)); } } // If there were no items, add in the word 'nothing' if (numItems == 0) - strcat(buffer, stringList.getString(S_INV_NOTHING)); + Common::strlcat(buffer, stringList.getString(S_INV_NOTHING), MAX_DESC_SIZE); // If the player has money, add it in uint16 numGroats = res.fieldList().numGroats(); if (numGroats > 0) { - strcat(buffer, "\n\n"); - strcat(buffer, stringList.getString(S_YOU_HAVE)); - sprintf(buffer + strlen(buffer), "%d", numGroats); - strcat(buffer, " "); - strcat(buffer, stringList.getString((numGroats == 1) ? S_GROAT : S_GROATS)); + Common::strlcat(buffer, "\n\n", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString(S_YOU_HAVE), MAX_DESC_SIZE); + snprintf(buffer + strlen(buffer), MAX_DESC_SIZE - strlen(buffer), "%d", numGroats); + Common::strlcat(buffer, " ", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString((numGroats == 1) ? S_GROAT : S_GROATS), MAX_DESC_SIZE); // Make sure we're not overrunning } // Display the dialog diff --git a/engines/lure/lure.h b/engines/lure/lure.h index af00197c3f..71ce2d3cff 100644 --- a/engines/lure/lure.h +++ b/engines/lure/lure.h @@ -20,8 +20,8 @@ * */ -#ifndef LURE_H -#define LURE_H +#ifndef LURE_LURE_H +#define LURE_LURE_H #include "engines/engine.h" #include "common/rect.h" diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp index 3df119a9da..f7dc06033a 100644 --- a/engines/lure/scripts.cpp +++ b/engines/lure/scripts.cpp @@ -926,8 +926,8 @@ uint16 Script::execute(uint16 startOffset) { opcode >>= 1; if (gDebugLevel >= ERROR_DETAILED) - strcat(debugInfo, (opcode > S_OPCODE_RANDOM) ? "INVALID" : - scriptOpcodes[opcode]); + Common::strlcat(debugInfo, (opcode > S_OPCODE_RANDOM) ? "INVALID" : + scriptOpcodes[opcode], MAX_DESC_SIZE); if (hasParam) { // Flag to read next two bytes as active parameter @@ -1087,7 +1087,7 @@ uint16 Script::execute(uint16 startOffset) { else if (scriptMethodNames[param] == NULL) strcat(debugInfo, " UNKNOWN METHOD"); else { strcat(debugInfo, " "); - strcat(debugInfo, scriptMethodNames[param]); + Common::strlcat(debugInfo, scriptMethodNames[param], MAX_DESC_SIZE); } // Any params diff --git a/engines/made/sound.cpp b/engines/made/sound.cpp index 908d6e3b90..62559efa84 100644 --- a/engines/made/sound.cpp +++ b/engines/made/sound.cpp @@ -155,6 +155,7 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou }; soundEnergyItem.position = 0; + memset(deltaSoundBuffer, 0, 1024); if (soundEnergyArray) soundEnergyArray->clear(); diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h index 6fa733e38e..ac91dca971 100644 --- a/engines/mohawk/mohawk.h +++ b/engines/mohawk/mohawk.h @@ -20,8 +20,8 @@ * */ -#ifndef MOHAWK_H -#define MOHAWK_H +#ifndef MOHAWK_MOHAWK_H +#define MOHAWK_MOHAWK_H #include "common/scummsys.h" #include "common/array.h" diff --git a/engines/neverhood/diskplayerscene.cpp b/engines/neverhood/diskplayerscene.cpp index 96a935851c..e79f4c9d77 100644 --- a/engines/neverhood/diskplayerscene.cpp +++ b/engines/neverhood/diskplayerscene.cpp @@ -22,6 +22,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/mouse.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/diskplayerscene.h b/engines/neverhood/diskplayerscene.h index 2ae85b9a0b..dba10f3a46 100644 --- a/engines/neverhood/diskplayerscene.h +++ b/engines/neverhood/diskplayerscene.h @@ -26,11 +26,11 @@ #include "neverhood/neverhood.h" #include "neverhood/resourceman.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { class DiskplayerScene; +class SmackerPlayer; class AsDiskplayerSceneKey : public AnimatedSprite { public: diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp index 0f2a421d83..e58dd31f03 100644 --- a/engines/neverhood/menumodule.cpp +++ b/engines/neverhood/menumodule.cpp @@ -23,6 +23,8 @@ #include "common/config-manager.h" #include "common/translation.h" +#include "audio/mixer.h" + #include "gui/saveload.h" #include "neverhood/menumodule.h" diff --git a/engines/neverhood/modules/module1300.cpp b/engines/neverhood/modules/module1300.cpp index 60ff0411a6..65bd353576 100644 --- a/engines/neverhood/modules/module1300.cpp +++ b/engines/neverhood/modules/module1300.cpp @@ -23,6 +23,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/gamemodule.h" #include "neverhood/menumodule.h" +#include "neverhood/smackerplayer.h" #include "neverhood/modules/module1000_sprites.h" #include "neverhood/modules/module1200_sprites.h" #include "neverhood/modules/module1300.h" diff --git a/engines/neverhood/modules/module1300.h b/engines/neverhood/modules/module1300.h index 4a0ca6c062..8164a51d0d 100644 --- a/engines/neverhood/modules/module1300.h +++ b/engines/neverhood/modules/module1300.h @@ -26,10 +26,11 @@ #include "neverhood/neverhood.h" #include "neverhood/module.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { +class SmackerPlayer; + class Module1300 : public Module { public: Module1300(NeverhoodEngine *vm, Module *parentModule, int which); diff --git a/engines/neverhood/modules/module1300_sprites.h b/engines/neverhood/modules/module1300_sprites.h index 6f4faaa234..bf9f72a5a7 100644 --- a/engines/neverhood/modules/module1300_sprites.h +++ b/engines/neverhood/modules/module1300_sprites.h @@ -26,7 +26,6 @@ #include "neverhood/neverhood.h" #include "neverhood/module.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/modules/module2800.cpp b/engines/neverhood/modules/module2800.cpp index ab22390c7d..63d507d8fd 100644 --- a/engines/neverhood/modules/module2800.cpp +++ b/engines/neverhood/modules/module2800.cpp @@ -23,6 +23,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/gamemodule.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" #include "neverhood/modules/module1000_sprites.h" #include "neverhood/modules/module1200_sprites.h" #include "neverhood/modules/module1700_sprites.h" diff --git a/engines/neverhood/navigationscene.h b/engines/neverhood/navigationscene.h index 8e286effb9..e1dabfea3d 100644 --- a/engines/neverhood/navigationscene.h +++ b/engines/neverhood/navigationscene.h @@ -26,6 +26,7 @@ #include "neverhood/neverhood.h" #include "neverhood/resourceman.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp index c6cff86c72..0dc271997b 100644 --- a/engines/neverhood/neverhood.cpp +++ b/engines/neverhood/neverhood.cpp @@ -24,6 +24,8 @@ #include "common/config-manager.h" #include "common/textconsole.h" +#include "audio/mixer.h" + #include "base/plugins.h" #include "base/version.h" diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h index 9eac4ffc44..4c5f9c3303 100644 --- a/engines/neverhood/neverhood.h +++ b/engines/neverhood/neverhood.h @@ -20,8 +20,8 @@ * */ -#ifndef NEVERHOOD_H -#define NEVERHOOD_H +#ifndef NEVERHOOD_NEVERHOOD_H +#define NEVERHOOD_NEVERHOOD_H #include "common/scummsys.h" #include "common/events.h" @@ -149,4 +149,4 @@ private: } // End of namespace Neverhood -#endif /* NEVERHOOD_H */ +#endif /* NEVERHOOD_NEVERHOOD_H */ diff --git a/engines/neverhood/scene.cpp b/engines/neverhood/scene.cpp index 1a8e74da38..8ed988c0fc 100644 --- a/engines/neverhood/scene.cpp +++ b/engines/neverhood/scene.cpp @@ -22,6 +22,7 @@ #include "neverhood/console.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/scene.h b/engines/neverhood/scene.h index 98a7fa5090..1d1faf28bc 100644 --- a/engines/neverhood/scene.h +++ b/engines/neverhood/scene.h @@ -31,13 +31,13 @@ #include "neverhood/klaymen.h" #include "neverhood/module.h" #include "neverhood/palette.h" -#include "neverhood/smackerplayer.h" #include "neverhood/sprite.h" #include "neverhood/staticdata.h" namespace Neverhood { class Console; +class SmackerPlayer; class Scene : public Entity { public: diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp index cc735c4c16..5cc7998210 100644 --- a/engines/neverhood/screen.cpp +++ b/engines/neverhood/screen.cpp @@ -21,6 +21,7 @@ */ #include "graphics/palette.h" +#include "video/smk_decoder.h" #include "neverhood/screen.h" namespace Neverhood { diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h index 82ce90b245..91bbe12c66 100644 --- a/engines/neverhood/screen.h +++ b/engines/neverhood/screen.h @@ -25,11 +25,14 @@ #include "common/array.h" #include "graphics/surface.h" -#include "video/smk_decoder.h" #include "neverhood/neverhood.h" #include "neverhood/microtiles.h" #include "neverhood/graphics.h" +namespace Video { + class SmackerDecoder; +} + namespace Neverhood { struct RenderItem { diff --git a/engines/neverhood/smackerscene.cpp b/engines/neverhood/smackerscene.cpp index 2b43579130..50677d7d5c 100644 --- a/engines/neverhood/smackerscene.cpp +++ b/engines/neverhood/smackerscene.cpp @@ -21,6 +21,7 @@ */ #include "neverhood/smackerscene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/sound.cpp b/engines/neverhood/sound.cpp index b15bea4a64..db22b72289 100644 --- a/engines/neverhood/sound.cpp +++ b/engines/neverhood/sound.cpp @@ -21,10 +21,17 @@ */ #include "common/memstream.h" -#include "graphics/palette.h" +#include "audio/mixer.h" #include "neverhood/sound.h" +#include "neverhood/resource.h" #include "neverhood/resourceman.h" +// Convert volume from percent to 0..255 +#define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume)) + +// Convert panning from percent (50% equals center) to -127..0..+127 +#define PANNING(panning) (254 / 100 * (panning) - 127) + namespace Neverhood { SoundResource::SoundResource(NeverhoodEngine *vm) @@ -583,6 +590,11 @@ AudioResourceManSoundItem::AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 _volume(100), _panning(50) { _vm->_res->queryResource(_fileHash, _resourceHandle); + _soundHandle = new Audio::SoundHandle(); +} + +AudioResourceManSoundItem::~AudioResourceManSoundItem() { + delete _soundHandle; } void AudioResourceManSoundItem::loadSound() { @@ -594,22 +606,22 @@ void AudioResourceManSoundItem::loadSound() { } void AudioResourceManSoundItem::unloadSound() { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _vm->_res->unloadResource(_resourceHandle); _data = NULL; } void AudioResourceManSoundItem::setVolume(int16 volume) { _volume = MIN<int16>(volume, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume)); } void AudioResourceManSoundItem::setPan(int16 pan) { _panning = MIN<int16>(pan, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, PANNING(_panning)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, PANNING(_panning)); } void AudioResourceManSoundItem::playSound(bool looping) { @@ -619,7 +631,7 @@ void AudioResourceManSoundItem::playSound(bool looping) { const byte *shiftValue = _resourceHandle.extData(); Common::MemoryReadStream *stream = new Common::MemoryReadStream(_data, _resourceHandle.size(), DisposeAfterUse::NO); NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, looping, DisposeAfterUse::YES, stream); - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _soundHandle, audioStream, -1, VOLUME(_volume), PANNING(_panning)); debug(1, "playing sound %08X", _fileHash); _isPlaying = true; @@ -627,13 +639,13 @@ void AudioResourceManSoundItem::playSound(bool looping) { } void AudioResourceManSoundItem::stopSound() { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _isPlaying = false; } bool AudioResourceManSoundItem::isPlaying() { - return _vm->_mixer->isSoundHandleActive(_soundHandle); + return _vm->_mixer->isSoundHandleActive(*_soundHandle); } AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash) @@ -641,6 +653,11 @@ AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 _volume(100), _panning(50), _start(false), _isFadingIn(false), _isFadingOut(false), _isPlaying(false), _fadeVolume(0), _fadeVolumeStep(0) { + _soundHandle = new Audio::SoundHandle(); +} + +AudioResourceManMusicItem::~AudioResourceManMusicItem() { + delete _soundHandle; } void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) { @@ -658,7 +675,7 @@ void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) { } void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) { if (fadeVolumeStep != 0) { if (_isFadingIn) _isFadingIn = false; @@ -667,7 +684,7 @@ void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) { _isFadingOut = true; _fadeVolumeStep = fadeVolumeStep; } else { - _vm->_mixer->stopHandle(_soundHandle); + _vm->_mixer->stopHandle(*_soundHandle); } _isPlaying = false; } @@ -677,8 +694,8 @@ void AudioResourceManMusicItem::unloadMusic() { if (_isFadingOut) { _canRestart = true; } else { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _isPlaying = false; _terminate = true; } @@ -686,8 +703,8 @@ void AudioResourceManMusicItem::unloadMusic() { void AudioResourceManMusicItem::setVolume(int16 volume) { _volume = MIN<int16>(volume, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume)); } void AudioResourceManMusicItem::restart() { @@ -698,33 +715,33 @@ void AudioResourceManMusicItem::restart() { void AudioResourceManMusicItem::update() { - if (_start && !_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_start && !_vm->_mixer->isSoundHandleActive(*_soundHandle)) { ResourceHandle resourceHandle; _vm->_res->queryResource(_fileHash, resourceHandle); Common::SeekableReadStream *stream = _vm->_res->createStream(_fileHash); const byte *shiftValue = resourceHandle.extData(); NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, true, DisposeAfterUse::YES, stream); - _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle, audioStream, -1, VOLUME(_isFadingIn ? _fadeVolume : _volume), PANNING(_panning)); _start = false; _isPlaying = true; } - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) { if (_isFadingIn) { _fadeVolume += _fadeVolumeStep; if (_fadeVolume >= _volume) { _fadeVolume = _volume; _isFadingIn = false; } - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume)); + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume)); } if (_isFadingOut) { _fadeVolume -= _fadeVolumeStep; if (_fadeVolume < 0) _fadeVolume = 0; - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume)); + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume)); if (_fadeVolume == 0) { _isFadingOut = false; stopMusic(0); diff --git a/engines/neverhood/sound.h b/engines/neverhood/sound.h index 24947f0191..e5e4ec9216 100644 --- a/engines/neverhood/sound.h +++ b/engines/neverhood/sound.h @@ -24,23 +24,22 @@ #define NEVERHOOD_SOUND_H #include "audio/audiostream.h" -#include "audio/mixer.h" #include "common/array.h" -#include "graphics/surface.h" -#include "neverhood/neverhood.h" -#include "neverhood/resource.h" +#include "neverhood/resourceman.h" -namespace Neverhood { +namespace Common { +class SeekableReadStream; +} -// Convert volume from percent to 0..255 -#define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume)) +namespace Audio { +class SoundHandle; +} -// Convert panning from percent (50% equals center) to -127..0..+127 -#define PANNING(panning) (254 / 100 * (panning) - 127) +namespace Neverhood { +class NeverhoodEngine; class AudioResourceManSoundItem; class AudioResourceManMusicItem; -class AudioResourceMan; class SoundResource { public: @@ -214,6 +213,7 @@ private: class AudioResourceManSoundItem { public: AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 fileHash); + ~AudioResourceManSoundItem(); void loadSound(); void unloadSound(); void setVolume(int16 volume); @@ -230,12 +230,13 @@ protected: bool _isPlaying; int16 _volume; int16 _panning; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle *_soundHandle; }; class AudioResourceManMusicItem { public: AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash); + ~AudioResourceManMusicItem(); void playMusic(int16 fadeVolumeStep); void stopMusic(int16 fadeVolumeStep); void unloadMusic(); @@ -259,7 +260,7 @@ protected: bool _isFadingOut; int16 _fadeVolume; int16 _fadeVolumeStep; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle *_soundHandle; }; class AudioResourceMan { diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 6ea50584f8..c4839897ef 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -20,8 +20,8 @@ * */ -#ifndef PARALLACTION_H -#define PARALLACTION_H +#ifndef PARALLACTION_PARALLACTION_H +#define PARALLACTION_PARALLACTION_H #include "common/str.h" #include "common/stack.h" diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp index df5a75541c..7c4a8a98ba 100644 --- a/engines/pegasus/neighborhood/mars/mars.cpp +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -1950,7 +1950,7 @@ void Mars::pickedUpItem(Item *item) { } void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { - if (dropSpot->getObjectID() == kAttackRobotHotSpotID) { + if (dropSpot && dropSpot->getObjectID() == kAttackRobotHotSpotID) { _attackingItem = (InventoryItem *)item; startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput); loadLoopSound2(""); diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h index d88545a4d1..57ae910def 100644 --- a/engines/pegasus/pegasus.h +++ b/engines/pegasus/pegasus.h @@ -23,8 +23,8 @@ * */ -#ifndef PEGASUS_H -#define PEGASUS_H +#ifndef PEGASUS_PEGASUS_H +#define PEGASUS_PEGASUS_H #include "common/list.h" #include "common/macresman.h" diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp index b39d26e056..f1fd5a25d3 100644 --- a/engines/prince/prince.cpp +++ b/engines/prince/prince.cpp @@ -1543,20 +1543,18 @@ void PrinceEngine::showAnim(Anim &anim) { // make_special_shadow if ((anim._flags & 0x80)) { - if (animSurface) { - DrawNode newDrawNode; - newDrawNode.posX = x; - newDrawNode.posY = y + animSurface->h - anim._shadowBack; - newDrawNode.posZ = Hero::kHeroShadowZ; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.scaleValue = _scaleValue; - newDrawNode.originalRoomSurface = nullptr; - newDrawNode.data = this; - newDrawNode.drawFunction = &Hero::showHeroShadow; - newDrawNode.s = animSurface; - _drawNodeList.push_back(newDrawNode); - } + DrawNode newDrawNode; + newDrawNode.posX = x; + newDrawNode.posY = y + animSurface->h - anim._shadowBack; + newDrawNode.posZ = Hero::kHeroShadowZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.scaleValue = _scaleValue; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = this; + newDrawNode.drawFunction = &Hero::showHeroShadow; + newDrawNode.s = animSurface; + _drawNodeList.push_back(newDrawNode); } //ShowFrameCodeShadow diff --git a/engines/prince/prince.h b/engines/prince/prince.h index 6dce044a41..82fcb152fa 100644 --- a/engines/prince/prince.h +++ b/engines/prince/prince.h @@ -20,8 +20,8 @@ * */ -#ifndef PRINCE_H -#define PRINCE_H +#ifndef PRINCE_PRINCE_H +#define PRINCE_PRINCE_H #include "common/random.h" #include "common/system.h" diff --git a/engines/queen/queen.h b/engines/queen/queen.h index c00e1b3a70..789025c264 100644 --- a/engines/queen/queen.h +++ b/engines/queen/queen.h @@ -20,8 +20,8 @@ * */ -#ifndef QUEEN_H -#define QUEEN_H +#ifndef QUEEN_QUEEN_H +#define QUEEN_QUEEN_H #include "engines/engine.h" #include "common/random.h" diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index cb09d53762..b08534c7fa 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -1170,7 +1170,7 @@ void Interface::processStatusTextInput(Common::KeyState keystate) { _statusTextInputPos--; _statusTextInputString[_statusTextInputPos] = 0; default: - if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX) { + if (_statusTextInputPos > STATUS_TEXT_INPUT_MAX) { break; } if (Common::isAlnum(keystate.ascii) || (keystate.ascii == ' ')) { @@ -2299,6 +2299,9 @@ void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelBut break; } if (_vm->getGameId() == GID_ITE) { + if (textId > kTextEnterProtectAnswer) + error("This should not happen. Please report to ScummVM Team how you achieved this error."); + text = _vm->getTextString(textId); textFont = kKnownFontMedium; textShadowKnownColor = kKnownColorVerbTextShadow; diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 06cb411e5a..422eaa530d 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -20,8 +20,8 @@ * */ -#ifndef SAGA_H -#define SAGA_H +#ifndef SAGA_SAGA_H +#define SAGA_SAGA_H #include "engines/engine.h" diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp index efd4c371b1..5cb4b55899 100644 --- a/engines/saga/scene.cpp +++ b/engines/saga/scene.cpp @@ -969,9 +969,8 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { case SAGA_OBJECT: break; case SAGA_BG_IMAGE: // Scene background resource - if (_bg.loaded) { + if (_bg.loaded) error("Scene::processSceneResources() Multiple background resources encountered"); - } debug(3, "Loading background resource."); @@ -987,9 +986,9 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { memcpy(_bg.pal, palPointer, sizeof(_bg.pal)); break; case SAGA_BG_MASK: // Scene background mask resource - if (_bgMask.loaded) { + if (_bgMask.loaded) error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered"); - } + debug(3, "Loading BACKGROUND MASK resource."); _vm->decodeBGImage(resourceData, _bgMask.buffer, &_bgMask.w, &_bgMask.h, true); _bgMask.loaded = true; @@ -1014,47 +1013,38 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { _actionMap->load(resourceData); break; case SAGA_ISO_IMAGES: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric images resource."); _vm->_isoMap->loadImages(resourceData); break; case SAGA_ISO_MAP: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric map resource."); - _vm->_isoMap->loadMap(resourceData); break; case SAGA_ISO_PLATFORMS: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric platforms resource."); - _vm->_isoMap->loadPlatforms(resourceData); break; case SAGA_ISO_METATILES: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric metatiles resource."); - _vm->_isoMap->loadMetaTiles(resourceData); break; case SAGA_ANIM: { uint16 animId = resource->resourceType - 14; - debug(3, "Loading animation resource animId=%i", animId); - _vm->_anim->load(animId, resourceData); } break; @@ -1063,9 +1053,8 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { loadSceneEntryList(resourceData); break; case SAGA_ISO_MULTI: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric multi resource."); diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 5f087ebd1a..c01613268a 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -349,6 +349,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Conquests of Camelot - English Atari ST + // Game version 1.019.000 + // Floppy: INT#10.12.90 + // Executable reports "1.002.038" + {"camelot", "", { + {"resource.map", 0, "0f80a11867be91a158823887a49cf443", 7290}, + {"resource.001", 0, "162f66c42e4146ee63f78fba6f1a6757", 596773}, + {"resource.002", 0, "162f66c42e4146ee63f78fba6f1a6757", 724615}, + {"resource.003", 0, "162f66c42e4146ee63f78fba6f1a6757", 713351}, + {"resource.004", 0, "162f66c42e4146ee63f78fba6f1a6757", 718766}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Conquests of Camelot - English DOS // SCI interpreter version 0.000.685 {"camelot", "", { @@ -939,6 +952,22 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 1 - English Atari ST + // Game version 1.000.104, SCI interpreter version 1.002.024 + {"hoyle1", "", { + {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518127}, + {"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // Hoyle 1 - English Atari ST + // Game version 1.000.108, SCI interpreter version 1.002.026 + {"hoyle1", "", { + {"resource.map", 0, "ed8355f84752e49ffa1f0cf9eca4b28e", 4140}, + {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 517454}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS // SCI interpreter version 0.000.572 {"hoyle2", "", { @@ -982,6 +1011,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English Atari ST + // Game version 1.001.017 + // Executable scanning reports "1.002.034" + {"hoyle2", "", { + {"resource.map", 0, "13c8cc977598b6ad61d24c6296a090fd", 1356}, + {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 216280}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English Macintosh // Executable scanning reports "x.yyy.zzz" {"hoyle2", "", { @@ -1977,6 +2015,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English Atari ST + // Game version 1.001.006 + // Executable reports "1.000.159" 1988-12-02 12:22 p.m. + {"lsl2", "", { + {"resource.map", 0, "2fc3ce7da1346e4dadfee18606d814fc", 4758}, + {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 477342}, + {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406698}, + {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592433}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English DOS Non-Interactive Demo // Executable scanning reports "x.yyy.zzz" // SCI interpreter version 0.000.409 @@ -2034,6 +2083,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English Atari ST (Kixx) + // Game version 1.002.000 + // Executable reports "1.001.008" 1989-01-12 16:30 + {"lsl2", "", { + {"resource.map", 0, "2c9c3b0923e3764f5ab999bcb71c2d47", 4758}, + {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 477625}, + {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406935}, + {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592533}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English Amiga (from www.back2roots.org) // Executable scanning reports "1.002.032" // SCI interpreter version 0.000.685 @@ -2048,6 +2108,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English Atari ST + // Game version 1.021, 1990-01-27 + // Int#6.26.90 + // Executable scanning reports "1.002.026" + {"lsl3", "", { + {"resource.map", 0, "0b6bd3e039682830a51c5755c06591db", 5916}, + {"resource.001", 0, "f18441027154292836b973c655fa3175", 456722}, + {"resource.002", 0, "f18441027154292836b973c655fa3175", 578024}, + {"resource.003", 0, "f18441027154292836b973c655fa3175", 506807}, + {"resource.004", 0, "f18441027154292836b973c655fa3175", 513651}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English DOS (supplied by ssburnout in bug report #3049193) // 1.021 8x5.25" (label: Int#5.15.90) {"lsl3", "", { @@ -2916,6 +2989,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English Atari ST + // Game version 1.002.011 DS 1989-07-21 + // Executable reports "1.002.003" + {"pq2", "", { + {"resource.map", 0, "28a6f471c7900c2c92da40eecb615d9d", 4584}, + {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 509525}, + {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 546000}, + {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 591851}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English DOS (from FRG) // SCI interpreter version 0.000.395 {"pq2", "", { @@ -2935,6 +3019,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English Atari ST + // Game version 1.001.006 1989-01-16 13:30 + // Executable reports "1.001.009" + {"pq2", "", { + {"resource.map", 0, "8e1161c684b342742d30f938a4839a4b", 4518}, + {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 506563}, + {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 541261}, + {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 587511}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - Japanese PC-98 (also includes english language) // Executable scanning reports "x.yyy.zzz" // SCI interpreter version unknown @@ -3204,6 +3299,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English Atari ST + // Game version 1.137 + // Executable reports "1.002.028" + {"qfg1", "", { + {"resource.map", 0, "2a794066ad161acbedac8fa14e46905d", 6438}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 79204}, + {"resource.001", 0, "f7fc269d3db146830d6427d3e02d4187", 473547}, + {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635687}, + {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640438}, + {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644452}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS Demo // Executable scanning reports "0.000.685" {"qfg1", "Demo", { @@ -3283,6 +3391,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Quest for Glory 2 - English Amiga + // Game version 1.109 // Executable scanning reports "1.003.004" // SCI interpreter version 0.001.010 {"qfg2", "", { @@ -3705,6 +3814,18 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 3 - English Atari ST + // Game version 1.0Q 1989-27-03 17:00 + // Int#1.002.002 + // Executable reports "1.002.001" + {"sq3", "", { + {"resource.map", 0, "c36e322805949affd882a75803a6a54e", 5484}, + {"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 485146}, + {"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 720227}, + {"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 688524}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 3 - German Amiga (also includes english language) // Executable scanning reports "1.004.006" // SCI interpreter version 0.000.453 (just a guess) diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 8039c5f282..116ffdd5a2 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -2934,6 +2934,84 @@ static const uint16 qfg3PatchChiefPriority[] = { PATCH_END }; +// There are 3 points that can't be achieved in the game. They should've been +// awarded for telling Rakeesh and Kreesha (room 285) about the Simabni +// initiation. +// However the array of posibble messages the hero can tell in that room +// (local 156) is missing the "Tell about Initiation" message (#31) which +// awards these points. +// This patch adds the message to that array, thus allowing the hero to tell +// that message (after completing the initiation) and gain the 3 points. +// A side effect of increasing the local156 array is that the next local +// array is shifted and shrinks in size from 4 words to 3. The patch changes +// the 2 locations in the script that reference that array, to point to the new +// location ($aa --> $ab). It is safe to shrink the 2nd array to 3 words +// because only the first element in it is ever used. +// +// Note: You have to re-enter the room in case a saved game was loaded from a +// previous version of ScummVM and that saved game was made inside that room. +// +// Applies to: English, French, German, Italian, Spanish and the GOG release. +// Responsible method: heap in script 285 +// Fixes bug #7086 +static const uint16 qfg3SignatureMissingPoints1[] = { + // local[$9c] = [0 -41 -76 1 -30 -77 -33 -34 -35 -36 -37 -42 -80 999] + // local[$aa] = [0 0 0 0] + SIG_UINT16(0x0000), // 0 START MARKER + SIG_MAGICDWORD, + SIG_UINT16(0xFFD7), // -41 "Greet" + SIG_UINT16(0xFFB4), // -76 "Say Good-bye" + SIG_UINT16(0x0001), // 1 "Tell about Tarna" + SIG_UINT16(0xFFE2), // -30 "Tell about Simani" + SIG_UINT16(0xFFB3), // -77 "Tell about Prisoner" + SIG_UINT16(0xFFDF), // -33 "Dispelled Leopard Lady" + SIG_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + SIG_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + SIG_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + SIG_UINT16(0xFFDB), // -37 "Tell about Village" + SIG_UINT16(0xFFD6), // -42 "Greet" + SIG_UINT16(0xFFB0), // -80 "Say Good-bye" + SIG_UINT16(0x03E7), // 999 END MARKER + SIG_ADDTOOFFSET(+2), // local[$aa][0] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints1[] = { + PATCH_ADDTOOFFSET(+14), + PATCH_UINT16(0xFFE1), // -31 "Tell about Initiation" + PATCH_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDB), // -37 "Tell about Village" + PATCH_UINT16(0xFFD6), // -42 "Greet" + PATCH_UINT16(0xFFB0), // -80 "Say Good-bye" + PATCH_UINT16(0x03E7), // 999 END MARKER + PATCH_GETORIGINALBYTE(+28), // local[$aa][0].low + PATCH_GETORIGINALBYTE(+29), // local[$aa][0].high + PATCH_END +}; + +static const uint16 qfg3SignatureMissingPoints2a[] = { + SIG_MAGICDWORD, + 0x35, 0x00, // ldi 0 + 0xb3, 0xaa, // sali local[$aa] + SIG_END +}; + +static const uint16 qfg3SignatureMissingPoints2b[] = { + SIG_MAGICDWORD, + 0x36, // push + 0x5b, 0x02, 0xaa, // lea local[$aa] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints2[] = { + PATCH_ADDTOOFFSET(+3), + 0xab, // local[$aa] ==> local[$ab] + PATCH_END +}; + + // Partly WORKAROUND: // During combat, the game is not properly throttled. That's because the game uses // an inner loop for combat and does not iterate through the main loop. @@ -2995,14 +3073,17 @@ static const uint16 qfg3PatchCombatSpeedThrottling2[] = { // script, description, signature patch static const SciScriptPatcherEntry qfg3Signatures[] = { - { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, - { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, - { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, - { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, - { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, - { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, + { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, + { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, + { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, + { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, + { true, 285, "missing points for telling about initiation heap", 1, qfg3SignatureMissingPoints1, qfg3PatchMissingPoints1 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2a, qfg3PatchMissingPoints2 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b, qfg3PatchMissingPoints2 }, + { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, + { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 7df3d38163..c49a516d01 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -20,8 +20,8 @@ * */ -#ifndef SCI_H -#define SCI_H +#ifndef SCI_SCI_H +#define SCI_SCI_H #include "engines/engine.h" #include "common/macresman.h" @@ -460,4 +460,4 @@ const char *getSciVersionDesc(SciVersion version); } // End of namespace Sci -#endif // SCI_H +#endif // SCI_SCI_H diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 0867b20fc3..4c9d1221aa 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -186,6 +186,11 @@ Common::String ScummEngine_v70he::generateFilename(const int room) const { } if (_filenamePattern.genMethod == kGenHEPC || _filenamePattern.genMethod == kGenHEIOS) { + if (id == '3' && _game.id == GID_MOONBASE) { + result = Common::String::format("%s.u32", _filenamePattern.pattern); + break; + } + // For HE >= 98, we already called snprintf above. if (_game.heversion < 98 || room < 0) result = Common::String::format("%s.he%c", _filenamePattern.pattern, id); diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 5a994cb699..bb3e7f6ec3 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -245,9 +245,11 @@ static const GameSettings gameVariantsTable[] = { {"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"monkey", "SE Talkie", 0, GID_MONKEY, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_AUDIOTRACKS, UNK, GUIO0()}, {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO5(GUIO_NOSPEECH, GUIO_MIDITOWNS, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_NOASPECT)}, + {"monkey2", "SE Talkie",0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, {"atlantis", "Steam", "steam", GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h index a8d7e846a2..72c11c95a1 100644 --- a/engines/scumm/he/intern_he.h +++ b/engines/scumm/he/intern_he.h @@ -27,8 +27,6 @@ #ifdef ENABLE_HE #include "scumm/he/floodfill_he.h" #include "scumm/he/wiz_he.h" - -#include "scumm/he/moonbase/moonbase.h" #endif #include "scumm/actor_he.h" // For AuxBlock & AuxEntry @@ -186,8 +184,11 @@ protected: }; #ifdef ENABLE_HE +class Moonbase; + class ScummEngine_v71he : public ScummEngine_v70he { friend class Wiz; + friend class Moonbase; protected: bool _skipProcessActors; @@ -246,10 +247,6 @@ public: void queueAuxEntry(int actorNum, int subIndex); void remapHEPalette(const uint8 *src, uint8 *dst); - -public: - /* Moonbase stuff */ - Moonbase *_moonbase; }; class ScummEngine_v72he : public ScummEngine_v71he { @@ -429,6 +426,7 @@ protected: class ScummEngine_v90he : public ScummEngine_v80he { friend class LogicHE; + friend class Moonbase; friend class MoviePlayer; friend class Sprite; @@ -439,7 +437,7 @@ protected: byte filename[260]; int32 status; int32 flags; - int32 unk2; + int32 number; int32 wizResNum; }; @@ -457,6 +455,9 @@ protected: Sprite *_sprite; public: + Moonbase *_moonbase; + +public: ScummEngine_v90he(OSystem *syst, const DetectorResult &dr); ~ScummEngine_v90he(); @@ -552,8 +553,15 @@ protected: byte VAR_NUM_PALETTES; byte VAR_NUM_UNK; +public: // FIXME. TODO. Should be protected. Used by Moonbase byte VAR_U32_VERSION; byte VAR_U32_ARRAY_UNK; + byte VAR_U32_USER_VAR_A; + byte VAR_U32_USER_VAR_B; + byte VAR_U32_USER_VAR_C; + byte VAR_U32_USER_VAR_D; + byte VAR_U32_USER_VAR_E; + byte VAR_U32_USER_VAR_F; }; class ScummEngine_v99he : public ScummEngine_v90he { diff --git a/engines/scumm/he/logic/moonbase_logic.cpp b/engines/scumm/he/logic/moonbase_logic.cpp index 22b0b186e6..d5100cef71 100644 --- a/engines/scumm/he/logic/moonbase_logic.cpp +++ b/engines/scumm/he/logic/moonbase_logic.cpp @@ -22,6 +22,8 @@ #include "scumm/he/intern_he.h" #include "scumm/he/logic_he.h" +#include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" namespace Scumm { @@ -44,10 +46,10 @@ private: void op_dos_command(int op, int numArgs, int32 *args); void op_set_fow_sentinel(int32 *args); void op_set_fow_information(int op, int numArgs, int32 *args); - void op_set_fow_image(int op, int numArgs, int32 *args); + int op_set_fow_image(int op, int numArgs, int32 *args); void op_ai_test_kludge(int op, int numArgs, int32 *args); - void op_ai_master_control_program(int op, int numArgs, int32 *args); + int op_ai_master_control_program(int op, int numArgs, int32 *args); void op_ai_reset(int op, int numArgs, int32 *args); void op_ai_set_type(int op, int numArgs, int32 *args); void op_ai_clean_up(int op, int numArgs, int32 *args); @@ -76,6 +78,44 @@ int LogicHEmoonbase::versionID() { #define OP_AI_SET_TYPE 10003 #define OP_AI_CLEAN_UP 10004 +#define OP_NET_REMOTE_START_SCRIPT 1492 +#define OP_NET_DO_INIT_ALL 1493 +#define OP_NET_DO_INIT_PROVIDER 1494 +#define OP_NET_DO_INIT_SESSION 1495 +#define OP_NET_DO_INIT_USER 1496 +#define OP_NET_QUERY_PROVIDERS 1497 +#define OP_NET_GET_PROVIDER_NAME 1498 +#define OP_NET_SET_PROVIDER 1499 +#define OP_NET_CLOSE_PROVIDER 1500 +#define OP_NET_QUERY_SESSIONS 1501 +#define OP_NET_GET_SESSION_NAME 1502 +#define OP_NET_CREATE_SESSION 1503 +#define OP_NET_JOIN_SESSION 1504 +#define OP_NET_END_SESSION 1505 +#define OP_NET_ADD_USER 1506 +#define OP_NET_REMOVE_USER 1507 +#define OP_NET_WHO_SENT_THIS 1508 +#define OP_NET_REMOTE_SEND_ARRAY 1509 +#define OP_NET_WHO_AM_I 1510 +#define OP_NET_REMOTE_START_FUNCTION 1511 +#define OP_NET_GET_PLAYER_LONG_NAME 1512 +#define OP_NET_GET_PLAYER_SHORT_NAME 1513 +#define OP_NET_SET_PROVIDER_BY_NAME 1516 +#define OP_NET_HOST_TCPIP_GAME 1517 +#define OP_NET_JOIN_TCPIP_GAME 1518 +#define OP_NET_SET_FAKE_LAG 1555 +#define OP_NET_GET_HOST_NAME 1556 +#define OP_NET_GET_IP_FROM_NAME 1557 +#define OP_NET_GET_SESSION_PLAYER_COUNT 1558 +#define OP_NET_DISABLE_SESSION_PLAYER_JOIN 1559 +#define OP_NET_START_QUERY_SESSIONS 1560 +#define OP_NET_UPDATE_QUERY_SESSIONS 1561 +#define OP_NET_STOP_QUERY_SESSIONS 1562 +#define OP_NET_DESTROY_PLAYER 1563 +#define OP_NET_ENABLE_SESSION_PLAYER_JOIN 1564 +#define OP_NET_SET_AI_PLAYER_COUNT 1565 + + int32 LogicHEmoonbase::dispatch(int op, int numArgs, int32 *args) { switch (op) { case OP_CREATE_MULTI_STATE_WIZ: @@ -97,15 +137,13 @@ int32 LogicHEmoonbase::dispatch(int op, int numArgs, int32 *args) { op_set_fow_information(op, numArgs, args); break; case OP_SET_FOW_IMAGE: - op_set_fow_image(op, numArgs, args); - break; + return op_set_fow_image(op, numArgs, args); case OP_AI_TEST_KLUDGE: op_ai_test_kludge(op, numArgs, args); break; case OP_AI_MASTER_CONTROL_PROGRAM: - op_ai_master_control_program(op, numArgs, args); - break; + return op_ai_master_control_program(op, numArgs, args); case OP_AI_RESET: op_ai_reset(op, numArgs, args); break; @@ -152,13 +190,34 @@ void LogicHEmoonbase::op_set_fow_sentinel(int32 *args) { } void LogicHEmoonbase::op_set_fow_information(int op, int numArgs, int32 *args) { - warning("STUB: op_set_fow_information()"); - LogicHE::dispatch(op, numArgs, args); + Common::String str; + + str = Common::String::format("op_set_fow_information(%d", args[0]); + for (int i = 1; i < numArgs; i++) { + str += Common::String::format(", %d", args[i]); + } + str += ")"; + + debug(2, "%s", str.c_str()); + + _vm->_moonbase->setFOWInfo( + args[0], // array + args[1], // array down dimension + args[2], // array across dimension + args[3], // logical view X coordinate + args[4], // logical view Y coordinate + args[5], // screen draw clip rect x1 + args[6], // screen draw clip rect y1 + args[7], // screen draw clip rect x2 + args[8], // screen draw clip rect y2 + args[9], // techinque + args[10] // frame + ); } -void LogicHEmoonbase::op_set_fow_image(int op, int numArgs, int32 *args) { - warning("STUB: op_set_fow_image()"); - LogicHE::dispatch(op, numArgs, args); +int LogicHEmoonbase::op_set_fow_image(int op, int numArgs, int32 *args) { + debug(2, "op_set_fow_image(%d)", args[0]); + return _vm->_moonbase->setFOWImage(args[0]) ? 1 : 0; } void LogicHEmoonbase::op_ai_test_kludge(int op, int numArgs, int32 *args) { @@ -166,24 +225,24 @@ void LogicHEmoonbase::op_ai_test_kludge(int op, int numArgs, int32 *args) { LogicHE::dispatch(op, numArgs, args); } -void LogicHEmoonbase::op_ai_master_control_program(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_master_control_program()"); - LogicHE::dispatch(op, numArgs, args); +int LogicHEmoonbase::op_ai_master_control_program(int op, int numArgs, int32 *args) { + warning("op_ai_master_control_program()"); + return masterControlProgram(numArgs, args); } void LogicHEmoonbase::op_ai_reset(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_reset)"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_reset())"); + resetAI(_vm); } void LogicHEmoonbase::op_ai_set_type(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_set_type()"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_set_type()"); + setAIType(numArgs, args); } void LogicHEmoonbase::op_ai_clean_up(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_clean_up()"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_clean_up()"); + cleanUpAI(); } LogicHE *makeLogicHEmoonbase(ScummEngine_v90he *vm) { diff --git a/engines/scumm/he/moonbase/ai_defenseunit.cpp b/engines/scumm/he/moonbase/ai_defenseunit.cpp new file mode 100644 index 0000000000..ba180bac66 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.cpp @@ -0,0 +1,767 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/rect.h" +#include "common/util.h" +#include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/ai_defenseunit.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +DefenseUnit::DefenseUnit() { + _state = DUS_ON; + + _id = -1; + _distanceTo = 0; + _state = 0; + _radius = 0; + _armor = 0; + _cost = 0; +} + +DefenseUnit::DefenseUnit(DefenseUnit *inUnit) { + _id = inUnit->getID(); + _pos.x = inUnit->getPosX(); + _pos.y = inUnit->getPosY(); + _distanceTo = inUnit->getDistanceTo(); + _state = inUnit->getState(); + _radius = inUnit->getRadius(); + _armor = inUnit->getArmor(); + _cost = inUnit->getCost(); +} + +DefenseUnit::~DefenseUnit() { +} + +Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + int radius; + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + radius = getRadius(); + + if ((distance < radius) || (getState() == DUS_OFF)) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + ratio = MAX(0, (getRadius() / distance)); + targetPos->x = getPosX() - ratio * (getPosX() - sourceX); + targetPos->y = getPosY() - ratio * (getPosY() - sourceY); + } + + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { //emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 3); + double d = static_cast<double>(distance); + + //formulae for calculating one point of intersection of two circles + float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); + int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; + int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + + targetPos->x = x; + targetPos->y = y; + } else { + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int AntiAirUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_CLUSTER; + break; + + case 1: + return ITEM_EMP; + break; + + case 2: + if (getState() == DUS_OFF) { + if (getPlayerEnergy() > 6) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_VIRUS; + } + } + + if (getPlayerEnergy() > 2) { + if (!_vm->_rnd.getRandomNumber(1)) { + return ITEM_SPIKE; + } + } + + return ITEM_BOMB; + } + + return ITEM_CLUSTER; + break; + + default: + return ITEM_CLUSTER; + break; + } +} + +Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + if (getState() == DUS_OFF) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + ratio = MAX(0.0, 1.0 - (static_cast<float>(getRadius()) / static_cast<float>(distance - 20))); + { + int maxX = getMaxX(); + int maxY = getMaxY(); + int thisX = (static_cast<int>(sourceX + ratio * (getPosX() - sourceX)) + maxX) % maxX; + int thisY = (static_cast<int>(sourceY + ratio * (getPosY() - sourceY)) + maxY) % maxY; + targetPos->x = thisX; + targetPos->y = thisY; + } + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { //emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 10); + double d = static_cast<double>(distance); + + //formulae for calculating one point of intersection of two circles + float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); + int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; + int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + + targetPos->x = x; + targetPos->y = y; + } else { + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + } + + if (distance < getRadius()) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + } + + return targetPos; +} + +int ShieldUnit::selectWeapon(int index) { + warning("Shield weapon select"); + + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + int dist = getDistance(getPosX(), getPosY(), getHubX(myUnit), getHubY(myUnit)); + + if ((dist < (getRadius() - 20)) && (dist > 90)) { + return ITEM_SPIKE; + } + + switch (index) { + case 0: + if (getState() == DUS_OFF) { + if (getPlayerEnergy() < 3) { + return ITEM_BOMB; + } else { + return ITEM_SPIKE; + } + } + + return ITEM_EMP; + break; + + case 1: + if (dist < getRadius() + 150) { + return ITEM_EMP; + } else { + return ITEM_CRAWLER; + } + + break; + + default: + return ITEM_EMP; + break; + } +} + +Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_EMP: + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int MineUnit::selectWeapon(int index) { + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); + int x = getPosX(); + int y = getPosY(); + + int dist = getDistance(x, y, getHubX(myUnit), getHubY(myUnit)); + + if ((getState() == DUS_ON) && (dist < 110)) { + return ITEM_EMP; + } else { + return ITEM_BOMB; + } +} + + +Common::Point *HubUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int HubUnit::selectWeapon(int index) { + warning("Hub weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_SPIKE; + } + + if (!_vm->_rnd.getRandomNumber(4)) { + return ITEM_GUIDED; + } + + if (!_vm->_rnd.getRandomNumber(4)) { + return ITEM_MINE; + } + + if (!_vm->_rnd.getRandomNumber(9)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + + +Common::Point *TowerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_SPIKE: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int TowerUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_SPIKE; + break; + + default: + return ITEM_SPIKE; + break; + } +} + + +Common::Point *BridgeUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int BridgeUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_BOMB; + break; + + case 1: + return ITEM_CLUSTER; + break; + + default: + return ITEM_BOMB; + break; + } +} + + +Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int EnergyUnit::selectWeapon(int index) { + warning("Energy weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + +Common::Point *OffenseUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int OffenseUnit::selectWeapon(int index) { + warning("Offense weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + return ITEM_BOMB; +} + +Common::Point *CrawlerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) + distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int CrawlerUnit::selectWeapon(int index) { + warning("Crawler weapon select"); + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); + int dist = getDistance(getHubX(myUnit), getHubY(myUnit), getPosX(), getPosY()); + + int x = getPosX(); + int y = getPosY(); + int energy = getPlayerEnergy(); + int terrain = getTerrain(x, y); + + if (terrain != TERRAIN_TYPE_WATER) { + if ((energy > 2) && (dist < 220)) { + return ITEM_RECLAIMER; + } else { + return ITEM_BOMB; + } + } else { + if (energy > 6) { + return ITEM_CRAWLER; + } + + if (energy > 2) { + if (_vm->_rnd.getRandomNumber(1)) { + return ITEM_MINE; + } else { + return ITEM_TIME_EXPIRED; + } + } + } + + return SKIP_TURN; +} + +AntiAirUnit::AntiAirUnit() { + setRadius(190); + setArmor(3); + setCost(1); +} + +ShieldUnit::ShieldUnit() { + setRadius(170); + setArmor(3); + setCost(7); +} + +MineUnit::MineUnit() { + setRadius(80); + setArmor(1); + setCost(3); +} + +HubUnit::HubUnit() { + setRadius(1); + setArmor(5); + setCost(7); +} + +TowerUnit::TowerUnit() { + setRadius(1); + setArmor(3); + setCost(1); +} + +BridgeUnit::BridgeUnit() { + setRadius(1); + setArmor(3); + setCost(1); +} + +EnergyUnit::EnergyUnit() { + setRadius(1); + setArmor(5); + setCost(7); +} + +OffenseUnit::OffenseUnit() { + setRadius(1); + setArmor(3); + setCost(7); +} + +CrawlerUnit::CrawlerUnit() { + setRadius(1); + setArmor(3); + setCost(7); +} + +AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); + +} + +ShieldUnit::ShieldUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +MineUnit::MineUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +HubUnit::HubUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +TowerUnit::TowerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +BridgeUnit::BridgeUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +EnergyUnit::EnergyUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +OffenseUnit::OffenseUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_defenseunit.h b/engines/scumm/he/moonbase/ai_defenseunit.h new file mode 100644 index 0000000000..e658fc31ab --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.h @@ -0,0 +1,190 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H +#define SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H + +namespace Scumm { + +enum { + DUT_ANTI_AIR = 1, + DUT_SHIELD = 2, + DUT_MINE = 3, + DUT_HUB = 4, + DUT_TOWER = 5, + DUT_BRIDGE = 6, + DUT_ENERGY = 7, + DUT_OFFENSE = 8, + DUT_CRAWLER = 9 +}; + +enum { + DUS_ON = 1, + DUS_OFF = 2, + DUS_DESTROYED = 3 +}; + +class DefenseUnit { +private: + int _id; + Common::Point _pos; + int _distanceTo; + int _state; + int _radius; + int _armor; + int _cost; + +public: + DefenseUnit(); + DefenseUnit(DefenseUnit *inUnit); + + virtual ~DefenseUnit(); + + void setID(int id) { _id = id; } + void setDistanceTo(int distanceTo) { _distanceTo = distanceTo; } + void setState(int state) { _state = state; } + void setRadius(int radius) { _radius = radius; } + void setArmor(int armor) { _armor = armor; } + void setDamage(int damage) { _armor -= damage; } + void setPos(int x, int y) { + _pos.x = x; + _pos.y = y; + } + void setCost(int cost) { _cost = cost; } + + int getID() const { return _id; } + int getDistanceTo() const { return _distanceTo; } + int getState() const { return _state; } + int getRadius() const { return _radius; } + int getArmor() const { return _armor; } + int getPosX() const { return _pos.x; } + int getPosY() const { return _pos.y; } + int getCost() const { return _cost; } + + virtual int getType() const = 0; + + virtual Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) = 0; + virtual int selectWeapon(int index) = 0; +}; + +class AntiAirUnit : public DefenseUnit { +private: + +public: + AntiAirUnit(); + AntiAirUnit(DefenseUnit *inUnit); + int getType() const { return DUT_ANTI_AIR; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class ShieldUnit : public DefenseUnit { +private: + +public: + ShieldUnit(); + ShieldUnit(DefenseUnit *inUnit); + int getType() const { return DUT_SHIELD; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class MineUnit : public DefenseUnit { +private: + +public: + MineUnit(); + MineUnit(DefenseUnit *inUnit); + int getType() const { return DUT_MINE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class HubUnit : public DefenseUnit { +private: + +public: + HubUnit(); + HubUnit(DefenseUnit *inUnit); + int getType() const { return DUT_HUB; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class TowerUnit : public DefenseUnit { +private: + +public: + TowerUnit(); + TowerUnit(DefenseUnit *inUnit); + int getType() const { return DUT_TOWER; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class BridgeUnit : public DefenseUnit { +private: + +public: + BridgeUnit(); + BridgeUnit(DefenseUnit *inUnit); + int getType() const { return DUT_BRIDGE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class EnergyUnit : public DefenseUnit { +private: + +public: + EnergyUnit(); + EnergyUnit(DefenseUnit *inUnit); + int getType() const { return DUT_ENERGY; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class OffenseUnit : public DefenseUnit { +private: + +public: + OffenseUnit(); + OffenseUnit(DefenseUnit *inUnit); + int getType() const { return DUT_OFFENSE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class CrawlerUnit : public DefenseUnit { +private: + +public: + CrawlerUnit(); + CrawlerUnit(DefenseUnit *inUnit); + int getType() const { return DUT_CRAWLER; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_main.cpp b/engines/scumm/he/moonbase/ai_main.cpp new file mode 100644 index 0000000000..1e523e1b89 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.cpp @@ -0,0 +1,3074 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/intern_he.h" + +#include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" +#include "scumm/he/moonbase/ai_traveller.h" +#include "scumm/he/moonbase/ai_targetacquisition.h" +#include "scumm/he/moonbase/ai_types.h" +#include "scumm/he/moonbase/ai_pattern.h" + +namespace Scumm { + +ScummEngine_v90he *_vm; + +enum { + F_GET_SCUMM_DATA = 0, + F_GET_WORLD_DIST = 1, + F_GET_WORLD_ANGLE = 2, + F_GET_TERRAIN_TYPE = 3, + F_GET_CLOSEST_UNIT = 4, + F_SIMULATE_BUILDING_LAUNCH = 5, + F_GET_POWER_ANGLE_FROM_POINT = 6, + F_CHECK_IF_WATER_STATE = 7, + F_GET_UNITS_WITHIN_RADIUS = 8, + F_GET_LANDING_POINT = 9, + F_GET_ENEMY_UNITS_VISIBLE = 10, + F_CHECK_IF_WATER_SQUARE = 11, + F_GET_GROUND_ALTITUDE = 12, + F_CHECK_FOR_CORD_OVERLAP = 13, + F_CHECK_FOR_ANGLE_OVERLAP = 14, + F_ESTIMATE_NEXT_ROUND_ENERGY = 15, + F_CHECK_FOR_UNIT_OVERLAP = 16, + F_GET_COORDINATE_VISIBILITY = 17, + F_CHECK_FOR_ENERGY_SQUARE = 18, + F_AI_CHAT = 19 +}; + +enum { + D_GET_HUB_X = 1, + D_GET_HUB_Y = 2, + D_GET_WORLD_X_SIZE = 3, + D_GET_WORLD_Y_SIZE = 4, + D_GET_CURRENT_PLAYER = 5, + D_GET_MAX_POWER = 6, + D_GET_MIN_POWER = 7, + D_GET_TERRAIN_SQUARE_SIZE = 8, + D_GET_BUILDING_OWNER = 9, + D_GET_BUILDING_STATE = 10, + D_GET_BUILDING_TYPE = 11, + D_DEBUG_BREAK = 12, + D_GET_ENERGY_POOLS_ARRAY = 13, + D_GET_COORDINATE_VISIBILITY = 14, + D_GET_UNIT_VISIBILITY = 15, + D_GET_ENERGY_POOL_VISIBILITY = 16, + D_GET_NUMBER_OF_POOLS = 17, + D_GET_NUMBER_OF_PLAYERS = 18, + D_GET_BUILDING_ARMOR = 19, + D_GET_BUILDING_WORTH = 20, + D_GET_PLAYER_ENERGY = 21, + D_GET_PLAYER_MAX_TIME = 22, + D_GET_WIND_X_SPEED = 23, + D_GET_WIND_Y_SPEED = 24, + D_GET_TOTAL_WIND_SPEED = 25, + D_GET_WIND_X_SPEED_MAX = 26, + D_GET_WIND_Y_SPEED_MAX = 27, + D_GET_BIG_X_SIZE = 28, + D_GET_BIG_Y_SIZE = 29, + D_GET_ENERGY_POOL_WIDTH = 30, + D_GET_BUILDING_MAX_ARMOR = 31, + D_GET_TIMER_VALUE = 32, + D_GET_LAST_ATTACKED_X = 33, + D_GET_LAST_ATTACKED_Y = 34, + D_PRINT_DEBUG_TIMER = 35, + D_GET_PLAYER_TEAM = 36, + D_GET_BUILDING_TEAM = 37, + D_GET_FOW = 38, + D_GET_ANIM_SPEED = 39, + D_GET_BUILDING_STACK_PTR = 40, + D_GET_TURN_COUNTER = 41 +}; + +enum { + AI_TYPE_PLAYER_NUM = 0, + AI_TYPE_TYPE = 1 +}; + +enum { + ENERGY_MODE = 0, + OFFENSE_MODE = 1, + DEFENSE_MODE = 2 +}; + +enum { + LAUNCH_SOURCE_HUB = 0, + LAUNCH_UNIT = 1, + LAUNCH_ANGLE = 2, + LAUNCH_POWER = 3, + + MAX_LAUNCH_POWER = 560, + MAX_FIRING_DISTANCE = 560 +}; + +enum { + SCALE_X = 50, + SCALE_Y = 50, + SCALE_Z = 50, + + GRAVITY_CONSTANT = (MAX_LAUNCH_POWER *MAX_LAUNCH_POWER) / MAX_FIRING_DISTANCE, + + HEIGHT_LOW = 20, + + NODE_RADIUS = 7, + NODE_DIAMETER = NODE_RADIUS * 2 + 2, + NODE_DETECT_RADIUS = NODE_RADIUS - 1, + + BUILDING_HUB_RADIUS = 16 +}; + +enum { + STATE_CHOOSE_BEHAVIOR = 0, + STATE_CHOOSE_TARGET = 1, + STATE_ATTEMPT_SEARCH = 2, + STATE_INIT_APPROACH_TARGET = 3, + STATE_APPROACH_TARGET = 4, + STATE_INIT_ACQUIRE_TARGET = 5, + STATE_ACQUIRE_TARGET = 6, + STATE_ENERGIZE_TARGET = 7, + STATE_OFFEND_TARGET = 8, + STATE_DEFEND_TARGET = 9, + STATE_LAUNCH = 10, + STATE_CRAWLER_DECISION = 11, + + TREE_DEPTH = 2 +}; + +AIEntity *AItype[5]; + +int AIstate = STATE_CHOOSE_BEHAVIOR; +int behavior = 2; +static int energyHogType = 0; + +patternList *moveList[5]; + +int *storedLaunchAction[5] = {NULL}; + +const int *MCP_params; + +Common::Array<int> lastXCoord[5]; +Common::Array<int> lastYCoord[5]; + +void resetAI(ScummEngine_v90he *vm) { + _vm = vm; + + AIstate = STATE_CHOOSE_BEHAVIOR; + warning("----------------------> Resetting AI"); + + for (int i = 1; i != 5; i++) { + if (AItype[i]) { + delete AItype[i]; + AItype[i] = NULL; + } + + AItype[i] = new AIEntity(BRUTAKAS); + } + + for (int i = 1; i != 5; i++) { + if (moveList[i]) { + delete moveList[i]; + moveList[i] = NULL; + } + + moveList[i] = new patternList; + } +} + +void cleanUpAI() { + warning("----------------------> Cleaning Up AI"); + + for (int i = 1; i != 5; i++) { + if (AItype[i]) { + delete AItype[i]; + AItype[i] = NULL; + } + } + + for (int i = 1; i != 5; i++) { + if (moveList[i]) { + delete moveList[i]; + moveList[i] = NULL; + } + } +} + +void setAIType(const int paramCount, const int32 *params) { + if (AItype[params[AI_TYPE_PLAYER_NUM]]) { + delete AItype[params[AI_TYPE_PLAYER_NUM]]; + AItype[params[AI_TYPE_PLAYER_NUM]] = NULL; + } + + AItype[params[AI_TYPE_PLAYER_NUM]] = new AIEntity(params[AI_TYPE_TYPE]); + + if (params[AI_TYPE_TYPE] == ENERGY_HOG) { + energyHogType = 1; + } else { + energyHogType = 0; + } + + warning("AI for player %d is %s", params[AI_TYPE_PLAYER_NUM], AItype[params[AI_TYPE_PLAYER_NUM]]->getNameString()); +} + +int masterControlProgram(const int paramCount, const int32 *params) { + static Tree *myTree; + + static int index; + + MCP_params = params; + + static int lastSource[5]; + static int lastAngle[5]; + static int lastPower[5]; + + + static int sourceHub; + static int target; + + static int targetX; + static int targetY; + + static int _acquireTarget = 0; + + static int *launchAction = NULL; + static int *currentLaunchAction = NULL; + + static int OLflag = 0; + static int TAflag = 0; + + + Node *retNode; + static int retNodeFlag; + + // Memory cleanup in case of quit during game + if (_vm->readVar(_vm->VAR_U32_USER_VAR_F)) { + if (myTree != NULL) { + delete myTree; + myTree = NULL; + } + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + if (currentLaunchAction != NULL) { + delete currentLaunchAction; + currentLaunchAction = NULL; + } + + return 1; + } + + int currentPlayer = getCurrentPlayer(); + + int maxTime = getPlayerMaxTime(); + int timerValue = getTimerValue(3); + + // If timer has run out + if ((AIstate > STATE_CHOOSE_BEHAVIOR) && ((maxTime) && (timerValue > maxTime))) { + if (myTree != NULL) { + delete myTree; + myTree = NULL; + } + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + launchAction = new int[4]; + + if (currentLaunchAction != NULL) { + launchAction[LAUNCH_SOURCE_HUB] = currentLaunchAction[LAUNCH_SOURCE_HUB]; + launchAction[LAUNCH_UNIT] = currentLaunchAction[LAUNCH_UNIT]; + launchAction[LAUNCH_ANGLE] = currentLaunchAction[LAUNCH_ANGLE]; + launchAction[LAUNCH_POWER] = currentLaunchAction[LAUNCH_POWER]; + delete currentLaunchAction; + currentLaunchAction = NULL; + } else { + if (!_vm->_rnd.getRandomNumber(1)) + launchAction[1] = ITEM_TIME_EXPIRED; + else + launchAction[1] = SKIP_TURN; + } + + AIstate = STATE_LAUNCH; + } + + static int oldAIstate = 0; + + if (oldAIstate != AIstate) { + warning("<<%d>>", AIstate); + oldAIstate = AIstate; + } + + switch (AIstate) { + case STATE_CHOOSE_BEHAVIOR: + behavior = chooseBehavior(); + warning("Behavior mode: %d", behavior); + + if ((int)_vm->_rnd.getRandomNumber(99) < AItype[getCurrentPlayer()]->getBehaviorVariation() * AI_VAR_BASE_BEHAVIOR + 1) { + if (_vm->_rnd.getRandomNumber(1)) { + behavior--; + + if (behavior < ENERGY_MODE) behavior = DEFENSE_MODE; + } else { + behavior++; + + if (behavior > DEFENSE_MODE) behavior = ENERGY_MODE; + } + + warning("Alternative behavior: %d", behavior); + } + + if (behavior == ENERGY_MODE) + if (!getNumberOfPools()) + behavior = OFFENSE_MODE; + + if (AItype[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) + behavior = OFFENSE_MODE; + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + index = 0; + AIstate = STATE_CHOOSE_TARGET; + break; + + case STATE_CHOOSE_TARGET: + target = chooseTarget(behavior); + + if (!target) + target = chooseTarget(OFFENSE_MODE); + + if (behavior == ENERGY_MODE) { + int energyPoolScummArray = getEnergyPoolsArray(); + targetX = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_X); + targetY = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_Y); + } else { + targetX = getHubX(target); + targetY = getHubY(target); + } + + warning("Target (%d, %d) id: %d", targetX, targetY, target); + + if (getFOW()) + AIstate = STATE_ATTEMPT_SEARCH; + else + AIstate = STATE_INIT_APPROACH_TARGET; + + break; + + case STATE_ATTEMPT_SEARCH: + if (!getCoordinateVisibility(targetX, targetY, currentPlayer)) { + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + int targetAngle = calcAngle(getHubX(closestHub), getHubY(closestHub), targetX, targetY); + int testX = static_cast<int>(getHubX(closestHub) + (500 * cos(degToRad(targetAngle))) + getMaxX()) % getMaxX(); + int testY = static_cast<int>(getHubY(closestHub) + (500 * sin(degToRad(targetAngle))) + getMaxY()) % getMaxY(); + + int balloonFlag = 0; + + int unitsArray = getUnitsWithinRadius(testX, testY, 500); + int unitsCounter = 0; + + //see if any balloons are already in the area + int nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); + + while (nextBuilding) { + if (((getBuildingType(nextBuilding) == BUILDING_BALLOON) || (getBuildingType(nextBuilding) == BUILDING_TOWER)) && (getBuildingTeam(nextBuilding) == getPlayerTeam(currentPlayer))) { + balloonFlag = 1; + nextBuilding = 0; + continue; + } + + unitsCounter++; + nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); + } + + _vm->nukeArray(unitsArray); + + if (!balloonFlag) { + launchAction = new int[4]; + launchAction[LAUNCH_SOURCE_HUB] = closestHub; + + if (getPlayerEnergy() > 3) { + launchAction[LAUNCH_UNIT] = ITEM_BALLOON; + launchAction[LAUNCH_POWER] = getMaxPower(); + } else { + launchAction[LAUNCH_UNIT] = ITEM_TOWER; + launchAction[LAUNCH_POWER] = getMinPower(); + } + + launchAction[LAUNCH_ANGLE] = targetAngle + (_vm->_rnd.getRandomNumber(89) - 45); + + int newTargetPos = abs(fakeSimulateWeaponLaunch(getHubX(closestHub), getHubY(closestHub), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE])); + targetX = newTargetPos % getMaxX(); + targetY = newTargetPos / getMaxY(); + + AIstate = STATE_INIT_ACQUIRE_TARGET; + break; + } + } + + AIstate = STATE_INIT_APPROACH_TARGET; + break; + + case STATE_INIT_APPROACH_TARGET: + { + int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1); + + if (closestOL && (behavior == OFFENSE_MODE)) { + AIstate = STATE_OFFEND_TARGET; + break; + } + } + + // get closest hub...if attack mode and almost close enough, maybe throw an offense + if ((behavior == OFFENSE_MODE) && (getPlayerEnergy() > 6)) { + if (!_vm->_rnd.getRandomNumber(2)) { + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); + + if ((dist > 470) && (dist < 900)) { + int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 0); + + if (!closestOL) { + // Launch an OL + OLflag = 1; + targetX = getHubX(closestHub); + targetY = getHubY(closestHub); + + AIstate = STATE_DEFEND_TARGET; + break; + } + } + } + } + + if ((behavior == OFFENSE_MODE) && (AItype[currentPlayer]->getID() == RANGER) && (getPlayerEnergy() > 2)) { + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); + + if (dist < 750) { + AIstate = STATE_OFFEND_TARGET; + break; + } + } + + myTree = initApproachTarget(targetX, targetY, &retNode); + + // If no need to approach, apply appropriate behavior + if (retNode == myTree->getBaseNode()) { + switch (behavior) { + case 0: + AIstate = STATE_ENERGIZE_TARGET; + break; + + case 1: + AIstate = STATE_OFFEND_TARGET; + break; + + case 2: + AIstate = STATE_DEFEND_TARGET; + break; + + case -1: + AIstate = STATE_LAUNCH; + break; + } + + delete myTree; + myTree = NULL; + break; + } + + delete retNode; + retNode = NULL; + + if (getPlayerEnergy() < 7) { + if (!_vm->_rnd.getRandomNumber(3)) { + behavior = DEFENSE_MODE; + AIstate = STATE_CHOOSE_TARGET; + } else { + if (launchAction == NULL) { + launchAction = new int[4]; + } + + if (!_vm->_rnd.getRandomNumber(2)) { + launchAction[1] = ITEM_TIME_EXPIRED; + } else { + launchAction[1] = SKIP_TURN; + } + + AIstate = STATE_LAUNCH; + } + + delete myTree; + myTree = NULL; + break; + } + + AIstate = STATE_CRAWLER_DECISION; + break; + + // If behavior is offense, possibly just chuck a crawler + case STATE_CRAWLER_DECISION: + { + // Brace just here to scope throwCrawler + int throwCrawler = 0; + + if (behavior == OFFENSE_MODE) { + if (getPlayerEnergy() > 6) { + int crawlerTest = _vm->_rnd.getRandomNumber(9); + + if (!crawlerTest) + throwCrawler = 1; + } + } + + if (AItype[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) { + if (getPlayerEnergy() > 6) { + throwCrawler = 1; + } else { + launchAction = new int[4]; + + if (!_vm->_rnd.getRandomNumber(1)) + launchAction[1] = ITEM_TIME_EXPIRED; + else + launchAction[1] = SKIP_TURN; + + AIstate = STATE_LAUNCH; + delete myTree; + myTree = NULL; + } + } + + if (throwCrawler) { + sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); + int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY, 15); + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + launchAction = new int[4]; + launchAction[0] = sourceHub; + launchAction[1] = ITEM_CRAWLER; + warning("CRAWLER DECISION is launching a crawler"); + launchAction[2] = angle; + launchAction[3] = power; + retNodeFlag = 0; + + // Need to update target so acquire can work + int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); + targetX = targetCoords % getMaxX(); + targetY = targetCoords / getMaxX(); + targetX = (targetX + getMaxX()) % getMaxX(); + targetY = (targetY + getMaxY()) % getMaxY(); + + AIstate = STATE_INIT_ACQUIRE_TARGET; + delete myTree; + myTree = NULL; + } else { + AIstate = STATE_APPROACH_TARGET; + } + break; + } + + // ApproachTarget returns NULL if target is already reachable + case STATE_APPROACH_TARGET: + { + int x, y; + Node *currentNode = NULL; + launchAction = approachTarget(myTree, x, y, ¤tNode); + } + + if (launchAction != NULL) { + if (launchAction[0] == -1) { + warning("Creating fake target approach hub"); + TAflag = 1; + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); + targetX = getHubX(closestHub); + targetY = getHubY(closestHub); + + delete launchAction; + launchAction = NULL; + AIstate = STATE_DEFEND_TARGET; + delete myTree; + myTree = NULL; + break; + } + + // Need to update target so acquire can work + int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); + targetX = targetCoords % getMaxX(); + targetY = targetCoords / getMaxX(); + targetX = (targetX + getMaxX()) % getMaxX(); + targetY = (targetY + getMaxY()) % getMaxY(); + + AIstate = STATE_INIT_ACQUIRE_TARGET; + behavior = -1; + + delete myTree; + myTree = NULL; + } + + break; + + case STATE_ENERGIZE_TARGET: + launchAction = energizeTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + assert(launchAction[0] > 0); + + if (launchAction[1] == ITEM_HUB) { + index = 0; + retNodeFlag = 0; + AIstate = STATE_INIT_ACQUIRE_TARGET; + } else { + index = 0; + AIstate = STATE_INIT_ACQUIRE_TARGET; + } + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } else { + behavior = DEFENSE_MODE; + retNodeFlag = 0; + index = 0; + AIstate = STATE_CHOOSE_TARGET; + } + break; + + case STATE_OFFEND_TARGET: + launchAction = offendTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + retNodeFlag = 0; + AIstate = STATE_INIT_ACQUIRE_TARGET; + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } + break; + + case STATE_DEFEND_TARGET: + launchAction = defendTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + retNodeFlag = 0; + AIstate = STATE_INIT_ACQUIRE_TARGET; + + if (launchAction[LAUNCH_UNIT] != ITEM_BRIDGE) { + if (OLflag) { + OLflag = 0; + launchAction[LAUNCH_UNIT] = ITEM_OFFENSE; + } + + if (TAflag) { + TAflag = 0; + warning("replacing defense unit %d with a hub", launchAction[LAUNCH_UNIT]); + launchAction[LAUNCH_UNIT] = ITEM_HUB; + } + } + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } + break; + + case STATE_INIT_ACQUIRE_TARGET: + myTree = initAcquireTarget(targetX, targetY, &retNode); + + if (myTree == NULL) { + AIstate = STATE_LAUNCH; + break; + } + + // This next line is a questionable fix + if (retNode == myTree->getBaseNode()) + retNodeFlag = 1; + + _acquireTarget = 0; + + AIstate = STATE_ACQUIRE_TARGET; + break; + + case STATE_ACQUIRE_TARGET: { + // Here to scope tempLaunchAction + int *tempLaunchAction = NULL; + + int errCod = 0; + + _acquireTarget++; + + if (!retNodeFlag) { + tempLaunchAction = acquireTarget(targetX, targetY, myTree, errCod); + } else { + warning("NOT acquiring target!!!!!!!"); + _acquireTarget = 101; + } + + if (tempLaunchAction != NULL) { + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + launchAction = tempLaunchAction; + } + + // If no hubs are available for launching...turn must be skipped + if (launchAction != NULL) { + if (launchAction[LAUNCH_SOURCE_HUB] == 0) { + launchAction[LAUNCH_UNIT] = SKIP_TURN; + } + } + + if ((tempLaunchAction != NULL) || (errCod == 1) || (_acquireTarget > 100)) { + if (tempLaunchAction == NULL) { + warning("\nABORTING acquire target!!!!!"); + } + + assert(launchAction != NULL); + delete myTree; + myTree = NULL; + AIstate = STATE_LAUNCH; + } + } + break; + + default: + break; + } + + if (AIstate == STATE_LAUNCH) { + static float randomAttenuation = 1; + + if (((launchAction[LAUNCH_UNIT] == ITEM_REPAIR) || (launchAction[LAUNCH_UNIT] == ITEM_ANTIAIR) || (launchAction[LAUNCH_UNIT] == ITEM_BRIDGE) || (launchAction[LAUNCH_UNIT] == ITEM_TOWER) || (launchAction[LAUNCH_UNIT] == ITEM_RECLAIMER) || (launchAction[LAUNCH_UNIT] == ITEM_BALLOON) || (launchAction[LAUNCH_UNIT] == ITEM_MINE) || (launchAction[LAUNCH_UNIT] == ITEM_ENERGY) || (launchAction[LAUNCH_UNIT] == ITEM_SHIELD) || (launchAction[LAUNCH_UNIT] == ITEM_OFFENSE) || (launchAction[LAUNCH_UNIT] == ITEM_HUB)) && (getBuildingType(launchAction[LAUNCH_SOURCE_HUB]) == BUILDING_OFFENSIVE_LAUNCHER)) { + if (getPlayerEnergy() > 2) { + launchAction[LAUNCH_UNIT] = ITEM_GUIDED; + } else { + launchAction[LAUNCH_UNIT] = ITEM_BOMB; + } + } + + if ((lastSource[currentPlayer] == launchAction[LAUNCH_SOURCE_HUB]) && (lastAngle[currentPlayer] == launchAction[LAUNCH_ANGLE]) && (lastPower[currentPlayer] == launchAction[LAUNCH_POWER])) { + randomAttenuation -= .2f; + randomAttenuation = MAX(randomAttenuation, 0.0f); + warning("Attenuating..."); + } else { + randomAttenuation = 1; + } + + lastSource[currentPlayer] = launchAction[LAUNCH_SOURCE_HUB]; + lastAngle[currentPlayer] = launchAction[LAUNCH_ANGLE]; + lastPower[currentPlayer] = launchAction[LAUNCH_POWER]; + + _vm->writeVar(_vm->VAR_U32_USER_VAR_A, launchAction[LAUNCH_SOURCE_HUB]); + int energy = getPlayerEnergy(); + warning("Computer's energy: %d", energy); + + // Check if there's enough energy to launch this item + int n = (launchAction[1] / 6); + int energyRequired = (1 + n * n + n); + + if (((energy - energyRequired) < 0) || (!launchAction[LAUNCH_SOURCE_HUB])) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } else { + assert((launchAction[LAUNCH_UNIT] >= 0) && (launchAction[LAUNCH_UNIT] <= 18)); + + if ((launchAction[LAUNCH_UNIT] < 0) || (launchAction[LAUNCH_UNIT] > 18)) launchAction[LAUNCH_UNIT] = 0; + + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, launchAction[LAUNCH_UNIT]); + } + + if (launchAction[LAUNCH_UNIT] == ITEM_BOMB) { + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(4)) { + launchAction[LAUNCH_UNIT] = ITEM_GUIDED; + } + } + } + + { + // ANGLE setting + int angleAdjustment = 0; + angleAdjustment = _vm->_rnd.getRandomNumber(AItype[getCurrentPlayer()]->getAngleVariation() * AI_VAR_BASE_ANGLE) * 3.6; + //pos or neg choice + angleAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); + angleAdjustment *= randomAttenuation; + + int safeAngle = 0; + int lu = launchAction[LAUNCH_UNIT]; + + if ((lu == ITEM_ANTIAIR) || (lu == ITEM_TOWER) || (lu == ITEM_ENERGY) || (lu == ITEM_SHIELD) || (lu == ITEM_OFFENSE) || (lu == ITEM_HUB)) { + for (int i = 1; i < 90; i++) { + assert((launchAction[LAUNCH_ANGLE] < 1000) && (angleAdjustment < 360)); + + if (checkForAngleOverlap(launchAction[LAUNCH_SOURCE_HUB], launchAction[LAUNCH_ANGLE] + angleAdjustment)) { + angleAdjustment += (i % 2 ? i : -i); + } else { + safeAngle = 1; + i = 90; + } + } + } else { + safeAngle = 1; + } + + if (!safeAngle) angleAdjustment = 0; + + warning("Angle adjustment = %d", angleAdjustment); + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, launchAction[LAUNCH_ANGLE] + angleAdjustment); + } + + { + // POWER setting + int powerRangeFactor = (getMaxPower() - getMinPower()) / 100; + int powerAdjustment = static_cast<float>(_vm->_rnd.getRandomNumber(AItype[getCurrentPlayer()]->getPowerVariation() * AI_VAR_BASE_POWER)) * powerRangeFactor; + //pos or neg choice + powerAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); + powerAdjustment *= randomAttenuation; + + warning("Power adjustment = %d", powerAdjustment); + int newPower = MIN(getMaxPower(), launchAction[LAUNCH_POWER] + powerAdjustment); + newPower = MAX(getMinPower(), launchAction[LAUNCH_POWER] + powerAdjustment); + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, newPower); + + assert(_vm->readVar(_vm->VAR_U32_USER_VAR_B) != -1); + + if (launchAction[LAUNCH_UNIT] != SKIP_TURN) { + if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { + if (getBuildingState(launchAction[LAUNCH_SOURCE_HUB]) != 0) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } + + if ((launchAction[LAUNCH_UNIT] == SKIP_TURN) || (launchAction[LAUNCH_POWER] == 0)) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, -1); + } + } + + + if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { + int nearbyOpponents = getUnitsWithinRadius(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), 180); + int opponentCounter = 0; + int opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); + int defenseOn = 0; + int defenseOff = 0; + + while (opponentBuilding) { + if (getBuildingOwner(opponentBuilding)) { + if ((getBuildingType(opponentBuilding) == BUILDING_ANTI_AIR) && (getBuildingTeam(opponentBuilding) != getPlayerTeam(currentPlayer))) { + if (getBuildingState(opponentBuilding) == 0) { + defenseOn = 1; + } else { + defenseOff = 1; + } + } + } + + opponentCounter++; + opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); + } + + _vm->nukeArray(nearbyOpponents); + + if (defenseOn && defenseOff) { + if (!_vm->_rnd.getRandomNumber(1)) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_TIME_EXPIRED); + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } else { + if (defenseOn) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_CLUSTER); + int temp = _vm->_rnd.getRandomNumber(2); + int tempPower = 0; + + switch (temp) { + case 0: + tempPower = getMinPower(); + break; + + case 1: + tempPower = getMaxPower(); + break; + + default: + tempPower = launchAction[LAUNCH_POWER]; + } + + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, tempPower); + } + } + } + + delete[] launchAction; + launchAction = NULL; + + AIstate = STATE_CHOOSE_BEHAVIOR; + + int rSh, rU, rP, rA = 0; + rSh = _vm->readVar(_vm->VAR_U32_USER_VAR_A); + rU = _vm->readVar(_vm->VAR_U32_USER_VAR_B); + rP = _vm->readVar(_vm->VAR_U32_USER_VAR_D); + rA = _vm->readVar(_vm->VAR_U32_USER_VAR_C); + + warning("su: %d unit: %d power: %d angle: %d", rSh, rU, rP, rA); + + { + // Checking for patterns + if ((AItype[currentPlayer]->getID() != CRAWLER_CHUCKER) && + (AItype[currentPlayer]->getID() != ENERGY_HOG) && (getBuildingStackPtr() > 5)) + moveList[currentPlayer]->addPattern(rSh, rU, rP, rA); + + int patternFound = moveList[currentPlayer]->evaluatePattern(rSh, rU, rP, rA); + + if (!_vm->_rnd.getRandomNumber(9)) + patternFound = 0; + + if (patternFound) { + warning("------------------------------------------>Eliminating pattern"); + + if (_vm->_rnd.getRandomNumber(1)) { + behavior--; + + if (behavior < ENERGY_MODE) + behavior = DEFENSE_MODE; + } else { + behavior++; + + if (behavior > DEFENSE_MODE) + behavior = ENERGY_MODE; + } + + if (behavior == ENERGY_MODE) + if (!getNumberOfPools()) + behavior = OFFENSE_MODE; + + _vm->writeVar(_vm->VAR_U32_USER_VAR_A, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, 0); + AIstate = STATE_CHOOSE_TARGET; + } + } + + if ((rSh > 0) && (rSh < 501)) { + if ((rU == ITEM_ANTIAIR) || (rU == ITEM_TOWER) || (rU == ITEM_ENERGY) || (rU == ITEM_SHIELD) || (rU == ITEM_OFFENSE) || (rU == ITEM_HUB)) { + int tryCount = 0; + + while (checkForAngleOverlap(rSh, rA) && tryCount < 25) { + rA = _vm->_rnd.getRandomNumber(359); + tryCount++; + } + + if (tryCount < 25) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, rA); + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } + } + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_A, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, 0); + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, 0); + } + + // Sending behavior to SCUMM side for profiling + int selectedUnit = _vm->readVar(_vm->VAR_U32_USER_VAR_B); + + if (selectedUnit) { + if (selectedUnit > 0) + _vm->writeVar(_vm->VAR_U32_USER_VAR_E, behavior); + else + _vm->writeVar(_vm->VAR_U32_USER_VAR_E, -999); + } + + return 1; +} + +int chooseBehavior() { + static int dominantMode = 0; + + if (getBuildingStackPtr() < 5) + return OFFENSE_MODE; + + int currentPlayer = getCurrentPlayer(); + + int AIpersonality = AItype[currentPlayer]->getID(); + + switch (AIpersonality) { + case BRUTAKAS: + dominantMode = OFFENSE_MODE; + break; + + case AGI: + dominantMode = DEFENSE_MODE; + break; + + case EL_GATO: + dominantMode = ENERGY_MODE; + break; + + case PIXELAHT: + dominantMode = DEFENSE_MODE; + break; + + case CYBALL: + dominantMode = ENERGY_MODE; + break; + + case NEEP: + dominantMode = DEFENSE_MODE; + break; + + case WARCUPINE: + dominantMode = ENERGY_MODE; + break; + + case AONE: + dominantMode = DEFENSE_MODE; + break; + + case SPANDO: + dominantMode = ENERGY_MODE; + break; + + case ORBNU_LUNATEK: + dominantMode = ENERGY_MODE; + break; + + case CRAWLER_CHUCKER: + dominantMode = OFFENSE_MODE; + break; + + case ENERGY_HOG: + dominantMode = ENERGY_MODE; + { + int breakFlag = 0; + + for (int i = 500; i > 0; i--) + if ((getBuildingOwner(i) == currentPlayer) && (getBuildingType(i) == BUILDING_ENERGY_COLLECTOR)) + breakFlag = 1; + + if (!breakFlag) + return ENERGY_MODE; + } + break; + + case RANGER: + dominantMode = OFFENSE_MODE; + + //random chance of defense if really early in game, otherwise offense + if (_vm->_rnd.getRandomNumber(1) || getTurnCounter() > 4) + return OFFENSE_MODE; + + return DEFENSE_MODE; + break; + + default: //BRUTAKAS + dominantMode = OFFENSE_MODE; + break; + } + + // get a list of all the visible enemy units + int eneCon = 0; + int offCon = 0; + int defCon = 0; + + // energy mode + { + warning("Starting Energy Behavior Selection"); + int minEnergy = 8; + int maxEnergy = 14; + + if (dominantMode == ENERGY_MODE) { + eneCon = 3; + maxEnergy = 21; + } else { + eneCon = 5; + } + + + // loop through energy pool array + int energyPoolScummArray = getEnergyPoolsArray(); + int numPools = getNumberOfPools(); + + // Prevent energy from being chosen if not enough energy to create + int energyAmount = getPlayerEnergy(); + + if ((energyAmount < 7)) + numPools = 0; + + for (int i = 1; i <= numPools; i++) { + int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); + + int radius = energyPoolSize(i) / 2; + int poolMaxCount = getMaxCollectors(i); + + int energyCount = 0; + int energyUnits = getUnitsWithinRadius(poolX, poolY, radius + 30); + int energyCounter = 0; + int energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); + + + while (energyBuilding) { + energyCounter++; + + if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) != currentPlayer)) + energyCount = poolMaxCount; + + if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) == currentPlayer)) + energyCount++; + + energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); + } + + _vm->nukeArray(energyUnits); + + if (energyCount < poolMaxCount) { + int closestHub = getClosestUnit(poolX, poolY, 300, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + if (closestHub) { + eneCon = MIN(1, eneCon); + } else { + int secondClosestHub = getClosestUnit(poolX, poolY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + if (secondClosestHub) + eneCon = MIN(2, eneCon); + else + eneCon = MIN(3, eneCon); + } + } + } + + int totalEnergy = estimateNextRoundEnergy(currentPlayer); + + if (totalEnergy < minEnergy) + eneCon--; + + if (totalEnergy > maxEnergy) + eneCon += 2; + + if ((totalEnergy > 34) || (!numPools)) + eneCon = 10; + + if (dominantMode == ENERGY_MODE) + eneCon--; + } + + + // offense mode + { + warning("Starting Offense Behavior Selection"); + + if (dominantMode == OFFENSE_MODE) offCon = 3; + else offCon = 5; + + int enemyArray = getEnemyUnitsVisible(currentPlayer); + int enemyX = 0; + int enemyY = 0; + int hubX = 0; + int hubY = 0; + + int nearEnemyHub = 0; + + // cycle through the array of buildings + for (int i = 0; i < 500; i++) { + if (int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i, 0)) { + enemyX = getHubX(thisBuilding); + enemyY = getHubY(thisBuilding); + int closestHub = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int closestOL = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int dist = 0; + + if (closestOL) { + hubX = getHubX(closestOL); + hubY = getHubY(closestOL); + nearEnemyHub = 1; + } + + if (closestHub) { + hubX = getHubX(closestHub); + hubY = getHubY(closestHub); + dist = getDistance(hubX, hubY, enemyX, enemyY); + + if (dist < 480) + nearEnemyHub = 1; + } + + + if (closestHub || closestOL) { + int numDefenders = 0; + int defArray = getUnitsWithinRadius(enemyX, enemyY, 170); + int defCounter = 0; + int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + + while (defenseBuilding) { + defCounter++; + + if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || + (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && + (getBuildingOwner(defenseBuilding) != currentPlayer)) { + if (getBuildingState(defenseBuilding) == 0) + numDefenders++; + } + + defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->nukeArray(defArray); + + if (!numDefenders) { + int defArray2 = getUnitsWithinRadius(hubX, hubY, 170); + defCounter = 0; + int defenseBuilding2 = _vm->_moonbase->readFromArray(defArray2, 0, defCounter); + + while (defenseBuilding2) { + defCounter++; + + if (((getBuildingType(defenseBuilding2) == BUILDING_ANTI_AIR) || + (getBuildingType(defenseBuilding2) == BUILDING_SHIELD)) && + (getBuildingOwner(defenseBuilding2) != currentPlayer)) + if (getBuildingState(defenseBuilding2) == 0) + numDefenders++; + + defenseBuilding2 = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->nukeArray(defArray2); + + } + + if ((!numDefenders) && (nearEnemyHub)) { + if (thisBuilding > 495) + offCon = 1 + _vm->_rnd.getRandomNumber(1); + else + offCon = MIN(offCon, 2); + } else { + if (thisBuilding > 495) { + if (nearEnemyHub) { + offCon = MIN(offCon, 2); + break; + } else { + offCon = MIN(offCon, 3); + break; + } + } else { + offCon = MIN(offCon, 4); + } + } + } + + if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { + if (getClosestUnit(enemyX, enemyY, 350, currentPlayer, 1, 0, 0)) { + int closestHub1 = getClosestUnit(enemyX, enemyY, 460, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int closestMine = getClosestUnit(enemyX, enemyY, 80, 0, 0, BUILDING_EXPLOSIVE_MINE, 1); + + if (closestHub1 && !closestMine) + offCon = -1; + } + } + } else { + break; + } + } + + _vm->nukeArray(enemyArray); + offCon--; + } + + // defense mode + { + warning("Starting Defense Behavior Selection"); + + if (dominantMode == DEFENSE_MODE) + defCon = 3; + else + defCon = 5; + + int numDefenders = 0; + int openFlag = 0; + + int mainHubX = getHubX(0); + int mainHubY = getHubY(0); + int mainHub = getClosestUnit(mainHubX + 10, mainHubY, 20, currentPlayer, 1, BUILDING_MAIN_BASE, 0); + + int damageFlag = 0; + + // cycle through the array of buildings + for (int i = 500; i >= 1; i--) { + if ((i < 497) && (defCon < 3)) + break; + + if (getBuildingOwner(i) == currentPlayer) { + int type = getBuildingType(i); + int hubX = getHubX(i); + int hubY = getHubY(i); + + if (type == BUILDING_MAIN_BASE || type == BUILDING_ENERGY_COLLECTOR || type == BUILDING_OFFENSIVE_LAUNCHER) { + int nearEnemy = 0; + int closeBuildingsArray = getUnitsWithinRadius(hubX, hubY, 480); + int closeBuildingCounter = 0; + int closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); + + while (closeBuilding) { + closeBuildingCounter++; + + if ((getBuildingOwner(closeBuilding) != currentPlayer) && (getBuildingType(closeBuilding) == BUILDING_MAIN_BASE)) { + nearEnemy = 1; + break; + } + + closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); + } + + _vm->nukeArray(closeBuildingsArray); + + int defCounter = 0; + int defArray = getUnitsWithinRadius(hubX, hubY, 170); + int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + numDefenders = 0; + + while (defenseBuilding) { + defCounter++; + + if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && (getBuildingOwner(defenseBuilding) == currentPlayer)) { + //main base has enemies near, but is defended + if (getBuildingState(defenseBuilding) == 0) + numDefenders++; + } + + defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->nukeArray(defArray); + + if (numDefenders > 2) + defCon++; + + if (numDefenders < 2) + if (dominantMode == DEFENSE_MODE) + openFlag = 1; + + if (!numDefenders) { + if (nearEnemy) { + if (i == mainHub) { + defCon = 1; + break; + } else { + defCon = MIN(defCon, 2); + } + } else { + if (i == mainHub) + defCon = MIN(defCon, 3); + else + defCon = MIN(defCon, 4); + } + } + + if (getBuildingArmor(i) < getBuildingMaxArmor(i)) + damageFlag = 1; + } + } + } + + if (damageFlag && (defCon > 1)) + defCon--; + + if (!openFlag && defCon == 3) + defCon += 2; + } + + warning("%s-------------------------------> Energy: %d Offense: %d Defense: %d", AItype[currentPlayer]->getNameString(), eneCon, offCon, defCon); + + if (dominantMode == DEFENSE_MODE) + if ((defCon <= offCon) && (defCon <= eneCon)) + return DEFENSE_MODE; + + if (dominantMode == OFFENSE_MODE) + if ((offCon <= eneCon) && (offCon <= defCon)) + return OFFENSE_MODE; + + if (dominantMode == ENERGY_MODE) + if ((eneCon <= offCon) && (eneCon <= defCon)) + return ENERGY_MODE; + + if ((defCon <= offCon) && (defCon <= eneCon)) + return DEFENSE_MODE; + + if ((offCon <= eneCon) && (offCon <= defCon)) + return OFFENSE_MODE; + + if ((eneCon <= offCon) && (eneCon <= defCon)) + return ENERGY_MODE; + + return -1; +} + +int chooseTarget(int behavior1) { + int numPools = getNumberOfPools(); + int currentPlayer = getCurrentPlayer(); + + int selection = 0; + int selectionValues[50] = {0}; + int selectionDist = 10000000; + + if (behavior1 == ENERGY_MODE) { + // loop through energy pool array + int energyPoolScummArray = getEnergyPoolsArray(); + + for (int i = 1; i <= numPools; i++) { + // check # units on pool + int numPoolSpots = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_UNITS_ON); + + if (numPoolSpots == 0) { + // put this one in the middle + warning("Empty pool #%d", i); + selectionValues[i] = 2; + } else { + // get units w/in radius of pool + int poolUnitsArray = getUnitsWithinRadius(_vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X), _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y), 50); + int enemyPool = 0; + int j = 1; + int thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + + while (thisPoolUnit) { + if (getBuildingOwner(thisPoolUnit) != currentPlayer) + enemyPool = 1; + + j++; + thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + } + + _vm->nukeArray(poolUnitsArray); + + // if enemy collector, put at bottom + if (enemyPool) { + selectionValues[i] = 1; + } else if (numPoolSpots < getMaxCollectors(i)) { + selectionValues[i] = 3; + } else { + // this pool is filled + selectionValues[i] = 0; + } + } + + if (selectionValues[i] > selectionValues[selection]) { + selection = i; + } else if (selectionValues[i] == selectionValues[selection]) { + int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); + + int closestHub = getClosestUnit(poolX, poolY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 100); + int thisDist = getDistance(poolX, poolY, getHubX(closestHub), getHubY(closestHub)); + + if (thisDist < selectionDist) { + selection = i; + selectionDist = thisDist; + } + } + + } + + warning("Pool selected: %d dist: %d", selection, selectionDist); + return selection; + } + + if (behavior1 == OFFENSE_MODE) { + int returnBuilding = 0; + int attackableArray[500]; + int nearAttackableArray[500]; + int attackableIndex = 0; + int nearAttackableIndex = 0; + + int enemyArray = getEnemyUnitsVisible(currentPlayer); + + for (int i = 500; i >= 1; i--) { + int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i - 1, 0); + + if (thisBuilding) { + if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { + if ((getTerrain(getHubX(thisBuilding), getHubY(thisBuilding)) != TERRAIN_TYPE_WATER) || (getPlayerEnergy() > 6)) { + if (getClosestUnit(getHubX(thisBuilding), getHubY(thisBuilding), 380, currentPlayer, 1, BUILDING_MAIN_BASE, 1)) { + returnBuilding = thisBuilding; + _vm->nukeArray(enemyArray); + return returnBuilding; + } + } else { + continue; + } + } + + int enemyX = getHubX(thisBuilding); + int enemyY = getHubY(thisBuilding); + int closestHub = getClosestUnit(enemyX, enemyY, 930, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + int dist = getDistance(enemyX, enemyY, getHubX(closestHub), getHubY(closestHub)); + + if (getBuildingType(thisBuilding) != BUILDING_BALLOON) { + if (dist < 470) { + attackableArray[attackableIndex] = thisBuilding; + attackableIndex++; + } else { + nearAttackableArray[nearAttackableIndex] = thisBuilding; + nearAttackableIndex++; + } + } + } + } + + _vm->nukeArray(enemyArray); + + if (attackableIndex) { + int thisWorth = 0; + int savedWorth = 1; + int closestSavedShield = 0; + int closestSavedAntiAir = 0; + + for (int i = 0; i < attackableIndex; i++) { + thisWorth = getBuildingWorth(attackableArray[i]); + + if (thisWorth == savedWorth) { + int closestShield = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); + int closestAntiAir = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); + + if (closestSavedShield > closestShield) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = attackableArray[i]; + } else { + if (closestSavedAntiAir > closestAntiAir) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = attackableArray[i]; + } + } + } + + if (thisWorth > savedWorth) { + savedWorth = thisWorth; + returnBuilding = attackableArray[i]; + } + } + } else { + if (nearAttackableIndex) { + int thisWorth = 0; + int savedWorth = 1; + int closestSavedShield = 0; + int closestSavedAntiAir = 0; + + for (int i = 0; i < nearAttackableIndex; i++) { + thisWorth = getBuildingWorth(nearAttackableArray[i]); + + if (thisWorth == savedWorth) { + int closestShield = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); + int closestAntiAir = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); + + if (closestSavedShield > closestShield) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = nearAttackableArray[i]; + } else { + if (closestSavedAntiAir > closestAntiAir) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = nearAttackableArray[i]; + } + } + } + + if (thisWorth > savedWorth) { + savedWorth = thisWorth; + returnBuilding = nearAttackableArray[i]; + } + } + } + } + + if (!returnBuilding) { + for (int i = 500; i > 496; i--) { + if (getBuildingOwner(i)) { + if (getBuildingTeam(i) != getPlayerTeam(currentPlayer)) { + returnBuilding = i; + i = 0; + } + } + } + } + + warning("Attack target: %d", returnBuilding); + + assert(returnBuilding); + return returnBuilding; + } + + if (behavior1 == DEFENSE_MODE) { + int returnBuilding = 0; + + int savedTally = 0; + int savedDamage; + float savedNumDefenses = 0; + int savedWorth = 0; + + float numDefenses = 0; + int tally = 0; + int attackable = 0; + int attacked = 0; + int damage = 0; + + int type = 0; + int worth = 0; + + + int attackedX = 0; + int attackedY = 0; + int attackedUnit = 0; + + if (getLastAttacked(attackedX, attackedY)) { + (void)getClosestUnit(attackedX + 10, attackedY, 50, currentPlayer, 1, 0, 0); // Unused? + } + + // loop through own units + for (int i = 1; i <= 500; i++) { + numDefenses = 0; + attackable = 0; + attacked = 0; + damage = 0; + type = 0; + worth = 0; + + int owner = getBuildingOwner(i); + + if (owner == currentPlayer) { + type = getBuildingType(i); + + // if current unit in [hub, offense, energy, tower] + if ((type == BUILDING_MAIN_BASE) || (type == BUILDING_ENERGY_COLLECTOR) || (type == BUILDING_OFFENSIVE_LAUNCHER) || (type == BUILDING_TOWER)) { + worth = getBuildingWorth(i); + + // Calculate current defenses + int x = getHubX(i); + int y = getHubY(i); + assert(x >= 0); + assert(y >= 0); + + int defenseArray = getUnitsWithinRadius(x, y, 180); + int j = 0; + // cycle through the defense units close to the target building + int defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); + + // loop on all defenses w/in 180 + while (defenseBuilding != 0) { + int defenseType = getBuildingType(defenseBuilding); + + // sub for each + if ((defenseType == BUILDING_ANTI_AIR) || (defenseType == BUILDING_SHIELD)) { + if (getBuildingState(defenseBuilding) == 0) + numDefenses += 1; + else + numDefenses += .5; + } + + j++; + defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); + } + + _vm->nukeArray(defenseArray); + + // Calculate if this unit is attackable + // get dist to nearest enemy hub, offense + int closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_MAIN_BASE, 0); + int numStridesToHub = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 480; + closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_OFFENSIVE_LAUNCHER, 0); + int numStridesToOL = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 800; + + // sub for each stride away + if (!numStridesToOL || !numStridesToHub) + attackable = 1; + + // Check if this unit was just attacked + if (attackedUnit == i) + attacked = 1; + + if (!numDefenses) { + tally = 1; + + if (attackable) { + tally = 4; + + if (attacked) { + tally = 5; + } + } + } else { + if (attackable) { + tally = 2; + + if (attacked) { + tally = 3; + } + } + } + + // Check if this unit is damaged + int saveFlag = 0; + + if (tally > savedTally) { + saveFlag = 1; + } else { + if (tally == savedTally) { + if (worth > savedWorth) { + saveFlag = 1; + + if (numDefenses > savedNumDefenses) { + saveFlag = 0; + } + } + + if (damage > savedDamage) { + saveFlag = 1; + + if (numDefenses > savedNumDefenses) { + saveFlag = 0; + } + } + + if (numDefenses < savedNumDefenses) { + saveFlag = 1; + } + + if (numDefenses >= 3) { + saveFlag = 0; + } + } + } + + if (saveFlag) { + savedTally = tally; + savedWorth = worth; + savedDamage = damage; + savedNumDefenses = numDefenses; + returnBuilding = i; + } + } + } + } + + return returnBuilding; + } + + return 0; +} + +Tree *initApproachTarget(int targetX, int targetY, Node **retNode) { + int sourceHub = 0; + + if (behavior == 2) + sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); + else + sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); + + Traveller *myTraveller = new Traveller(getHubX(sourceHub), getHubY(sourceHub)); + myTraveller->setSourceHub(sourceHub); + + //target adjustment so that room is allowed for the appropriate shot + int tempAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + int adjX = -120 * cos(degToRad(tempAngle)); + int adjY = -120 * sin(degToRad(tempAngle)); + + Traveller::setTargetPosX(targetX + adjX); + Traveller::setTargetPosY(targetY + adjY); + Traveller::setMaxDist(340); + + Tree *myTree = new Tree(myTraveller, TREE_DEPTH); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + +int *approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { + int *retVal = NULL; + + *currentNode = NULL; + Node *retNode = myTree->aStarSearch_singlePass(); + + if (*currentNode != NULL) + warning("########################################### Got a possible solution"); + + if (myTree->IsBaseNode(retNode)) { + warning("########################################### Returning Base Node"); + retVal = new int[4]; + retVal[0] = -1; + return retVal; + } + + if (retNode == NULL) { + return retVal; + } else { + retVal = new int[4]; + + Traveller *retTraveller = reinterpret_cast<Traveller *>(retNode->getFirstStep()->getContainedObject()); + + // set launching hub, item to launch, angle of launch, power of launch + // if water flag is set, launch bridge instead of hub + retVal[0] = static_cast<Traveller *>(myTree->getBaseNode()->getContainedObject())->getSourceHub(); + + if (retTraveller->getWaterFlag()) { + int powAngle = getPowerAngleFromPoint(retTraveller->getWaterSourceX(), + retTraveller->getWaterSourceY(), + retTraveller->getWaterDestX(), + retTraveller->getWaterDestY(), + 15); + + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + retVal[0] = getClosestUnit(retTraveller->getWaterSourceX() + 10, retTraveller->getWaterSourceY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + + retVal[1] = ITEM_BRIDGE; + retVal[2] = angle; + retVal[3] = power; + + warning("Target Bridge Coords: <%d, %d> ", retTraveller->getWaterDestX(), retTraveller->getWaterDestY()); + } else { + retVal[1] = ITEM_HUB; + retVal[2] = retTraveller->getAngleTo(); + retVal[3] = retTraveller->getPowerTo(); + } + + + int whoseTurn = getCurrentPlayer(); + + if ((lastXCoord[whoseTurn]).size() >= MAX_MEMORY) { + (lastXCoord[whoseTurn]).erase((lastXCoord[whoseTurn]).begin()); + (lastYCoord[whoseTurn]).erase((lastYCoord[whoseTurn]).begin()); + } + + (lastXCoord[whoseTurn]).push_back(retTraveller->getPosX()); + (lastYCoord[whoseTurn]).push_back(retTraveller->getPosY()); + + int temp = static_cast<int>(retTraveller->calcT()); + int temp2 = static_cast<int>(retTraveller->getValueG()); + int x = static_cast<int>(retTraveller->getPosX()); + int y = static_cast<int>(retTraveller->getPosY()); + warning("Target Coords: <%d, %d> G-value: %d T-value: %d", x, y, temp2, temp); + xTarget = x; + yTarget = y; + } + + return retVal; +} + +Tree *initAcquireTarget(int targetX, int targetY, Node **retNode) { + int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); + warning("My coords (%d): %d %d", sourceHub, getHubX(sourceHub), getHubY(sourceHub)); + + Sortie::setSourcePos(getHubX(sourceHub), getHubY(sourceHub)); + Sortie::setTargetPos(targetX, targetY); + Sortie *myBaseTarget = new Sortie(); + myBaseTarget->setValueG(0); + + myBaseTarget->setUnitType(ITEM_BOMB); + myBaseTarget->setShotPos(-1, -1); + + int unitsArray = getUnitsWithinRadius(targetX + 7, targetY, 211); + + warning("Target Coords: <%d, %d> Source Coords: <%d, %d>", targetX, targetY, getHubX(sourceHub) , getHubY(sourceHub)); + + myBaseTarget->setEnemyDefenses(unitsArray, targetX, targetY); + + int thisElement = _vm->_moonbase->readFromArray(unitsArray, 0, 0); + + _vm->nukeArray(unitsArray); + + if (!thisElement) { + delete myBaseTarget; + return NULL; + } + + Tree *myTree = new Tree(myBaseTarget, 4); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + + +int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { + int currentPlayer = getCurrentPlayer(); + int *retVal = NULL; + + Node *retNode = myTree->aStarSearch_singlePass(); + + if (myTree->IsBaseNode(retNode)) + return acquireTarget(targetX, targetY); + + if (retNode == NULL) { + errorCode = 0; + return retVal; + } + + Sortie *thisSortie = static_cast<Sortie *>(retNode->getFirstStep()->getContainedObject()); + int unitToShoot = thisSortie->getUnitType(); + + if (unitToShoot < 0) { + errorCode = 1; + return retVal; + } + + if (unitToShoot == ITEM_CRAWLER) { + warning("target acquisition is launching a crawler"); + } + + int shotTargetX = thisSortie->getShotPosX(); + int shotTargetY = thisSortie->getShotPosY(); + int theTarget = getClosestUnit(shotTargetX + 5, shotTargetY, getMaxX(), 0, 0, 0, 0, 0); + + + int sourceOL = 0; + int sourceX = thisSortie->getSourcePosX(); + int sourceY = thisSortie->getSourcePosY(); + + int sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + + sourceOL = getClosestUnit(sourceX, sourceY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); + + if (sourceOL) { + sourceHub = sourceOL; + sourceX = getHubX(sourceOL); + sourceY = getHubY(sourceOL); + } + + if (!sourceHub) sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + + int powAngle = getPowerAngleFromPoint(sourceX, sourceY, shotTargetX, shotTargetY, 15, sourceOL); + warning("The source (%d: <%d, %d>) The target (%d: <%d, %d>)", sourceHub, sourceX, sourceY, theTarget, shotTargetX, shotTargetY); + + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + retVal = new int[4]; + + retVal[0] = sourceHub; + retVal[1] = unitToShoot; + retVal[2] = angle; + retVal[3] = power; + + warning("Unit to shoot: %d", unitToShoot); + + return retVal; +} + +int *acquireTarget(int targetX, int targetY) { + int *retVal = new int[4]; + int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + + if (!sourceHub) + sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + + int directAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + int directDistance = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + + retVal[0] = sourceHub; + retVal[1] = ITEM_OFFENSE; + retVal[2] = directAngle; + retVal[3] = MAX(MIN(getMaxPower() * directDistance / 500, getMaxPower()), getMinPower()); + + return retVal; +} + +int *energizeTarget(int &targetX, int &targetY, int index) { + int n = 10; + static int currentPlayer = 0; + static int pool = 0; + static int radius = 0; + static int poolUnitsArray = 0; + static int j = 0; + static int k = 0; + static int sameUnit = 0; + static int nextUnit = 0; + static int attempt = 0; + + if (!index) { + warning("index is 0!"); + currentPlayer = getCurrentPlayer(); + pool = 0; + + // get the pool that's closest to the target coords + for (int i = 1; i <= getNumberOfPools(); i++) { + int poolX = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_Y); + + if ((poolX == targetX) && (poolY == targetY)) { + pool = i; + } + } + + // calculate the appropriate radius + radius = energyPoolSize(pool) / 2; + + // create test points + k = 0; + j = 0; + nextUnit = 0; + sameUnit = 0; + attempt = 0; + } + + if (poolUnitsArray) + _vm->nukeArray(poolUnitsArray); + + poolUnitsArray = getUnitsWithinRadius(targetX, targetY, 450); + assert(poolUnitsArray); + + // 0 is for energy collectors, 1 is for circumnavigating hubs + if (k < 2) { + if (!sameUnit) { + sameUnit = 1; + attempt = 0; + nextUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + j++; + } + + if (nextUnit) { + if ((getBuildingType(nextUnit) == BUILDING_MAIN_BASE) && (getBuildingOwner(nextUnit) == currentPlayer)) { + int minAngle = 0; + int hubToPoolAngle = 0; + int testAngle = 0; + int testDist = 0; + static int xPos = 0; + static int yPos = 0; + static int newAttempt = 1; + + if (sameUnit) { + if (k == 0) { + int poolToHubAngle = calcAngle(targetX, targetY, getHubX(nextUnit), getHubY(nextUnit)); + minAngle = poolToHubAngle - 45; + } else { + hubToPoolAngle = calcAngle(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY); + } + } + + if (attempt < n) { + static int power = 0; + static int angle = 0; + + if (newAttempt) { + newAttempt = 0; + + if (k == 0) { + testAngle = (_vm->_rnd.getRandomNumber(89) + minAngle) % 360; + testDist = radius; + + xPos = targetX + testDist * cos(degToRad(testAngle)); + yPos = targetY + testDist * sin(degToRad(testAngle)); + } else { + switch (_vm->_rnd.getRandomNumber(1)) { + case 0: + testAngle = (hubToPoolAngle + (45 + _vm->_rnd.getRandomNumber(19))) % 360; + break; + + default: + testAngle = (hubToPoolAngle + (315 - _vm->_rnd.getRandomNumber(19))) % 360; + break; + } + + testDist = ((((n - attempt) / n) * .5) + .5) * (getDistance(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY) / .8); + xPos = getHubX(nextUnit) + testDist * cos(degToRad(testAngle)); + yPos = getHubY(nextUnit) + testDist * sin(degToRad(testAngle)); + } + + // check if points are good + int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); + powAngle = abs(powAngle); + + power = powAngle / 360; + angle = powAngle - (power * 360); + } + + int result = 0; + result = simulateBuildingLaunch(getHubX(nextUnit), getHubY(nextUnit), power, angle, 10, 1); + + if (result) { + newAttempt = 1; + + if (result > 0) { + xPos = (xPos + getMaxX()) % getMaxX(); + yPos = (yPos + getMaxY()) % getMaxY(); + + result = 1; + } else { + // Drop a bridge for the cord + int yCoord = -result / getMaxX(); + int xCoord = -result - (yCoord * getMaxX()); + + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - xPos; + int yDist = yCoord - yPos; + xPos = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); + yPos = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); + + nextUnit = getClosestUnit(xPos, yPos, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); + int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); + + powAngle = abs(powAngle); + power = powAngle / 360; + angle = powAngle - (power * 360); + + int *retVal = new int[4]; + + retVal[0] = nextUnit; + retVal[1] = ITEM_BRIDGE; + retVal[2] = angle; + retVal[3] = power; + + if (nextUnit <= 0) + retVal[0] = 0; + + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + return retVal; + } + } + + if (result > 0) { + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + + targetX = xPos; + targetY = yPos; + + int *retVal = new int[4]; + + retVal[0] = nextUnit; + + if (k == 0) { + retVal[1] = ITEM_ENERGY; + } else { + retVal[1] = ITEM_HUB; + } + + retVal[2] = angle; + retVal[3] = power; + return retVal; + } + } else { + int *retVal = new int[4]; + retVal[0] = 0; + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + + return retVal; + } + + attempt++; + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + k++; + j = 0; + } + } else { + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + return NULL; + } + + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + int *retVal = new int[4]; + retVal[0] = 0; + + return retVal; +} + +int *offendTarget(int &targetX, int &targetY, int index) { + int *retVal = NULL; + + int target = getClosestUnit(targetX + 10, targetY, 20, 0, 0, 0, 0); + + if (!target) + target = getClosestUnit(targetX + 10, targetY, 0, 0, 0, 0, 0); + + warning("The target inside the offendTarget routine is: %d", target); + int type = getBuildingType(target); + int unit = 0; + + DefenseUnit *thisUnit; + + switch (type) { + case BUILDING_OFFENSIVE_LAUNCHER: + thisUnit = new OffenseUnit(); + break; + + case BUILDING_TOWER: + thisUnit = new TowerUnit(); + break; + + case BUILDING_MAIN_BASE: + thisUnit = new HubUnit(); + break; + + case BUILDING_ENERGY_COLLECTOR: + thisUnit = new EnergyUnit(); + break; + + case BUILDING_CRAWLER: + thisUnit = new CrawlerUnit(); + break; + + case BUILDING_BRIDGE: + thisUnit = new BridgeUnit(); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(); + break; + + default: + thisUnit = new HubUnit(); + break; + } + + thisUnit->setPos(targetX, targetY); + thisUnit->setID(target); + + int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + int sourceOL = 0; + sourceOL = getClosestUnit(targetX, targetY, 900, getCurrentPlayer(), 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); + + unit = thisUnit->selectWeapon(_vm->_rnd.getRandomNumber(4)); + + if (sourceOL) { + if ((unit == ITEM_BOMB) || (unit == ITEM_CLUSTER) || (unit == ITEM_GUIDED) || (unit == ITEM_EMP) || (unit == ITEM_SPIKE) || (unit == ITEM_CRAWLER) || (unit == ITEM_VIRUS)) { + sourceHub = sourceOL; + } + } + + if (!sourceHub) { + retVal = new int[4]; + + retVal[1] = SKIP_TURN; + return retVal; + } + + + if ((thisUnit->getType() == BUILDING_CRAWLER) && (unit == SKIP_TURN)) { + retVal = new int[4]; + retVal[1] = unit; + delete thisUnit; + return retVal; + } + + if (unit == ITEM_CRAWLER) { + warning("******** offense is launching a crawler ********"); + warning("The defensive unit is: %d", unit); + } + + Common::Point *targetCoords; + int dist = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + targetCoords = thisUnit->createTargetPos(0, dist, unit, getHubX(sourceHub), getHubY(sourceHub)); + + int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetCoords->x, targetCoords->y, 15, sourceOL); + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle % 360; + + if (unit == ITEM_MINE) + power -= 30; + + targetX = targetCoords->x; + targetY = targetCoords->y; + + if (targetX < 0) + targetX = (targetX + getMaxX()) % getMaxX(); + + if (targetY < 0) + targetY = (targetY + getMaxY()) % getMaxY(); + + assert(targetX >= 0 && targetY >= 0); + delete targetCoords; + delete thisUnit; + + retVal = new int[4]; + + retVal[0] = sourceHub; + retVal[1] = unit; + retVal[2] = angle; + retVal[3] = power; + + return retVal; +} + +int *defendTarget(int &targetX, int &targetY, int index) { + int *retVal = NULL; + Defender *thisDefender = new Defender; + int defStatus = thisDefender->calculateDefenseUnitPosition(targetX, targetY, index); + + if (defStatus > 0) { + targetX = thisDefender->getTargetX(); + targetY = thisDefender->getTargetY(); + retVal = new int[4]; + + retVal[0] = thisDefender->getSourceUnit(); + retVal[1] = thisDefender->getUnit(); + retVal[2] = thisDefender->getAngle(); + retVal[3] = thisDefender->getPower(); + } + + if (defStatus == 0) { + retVal = new int[4]; + retVal[0] = 0; + } + + if (defStatus == -1) { + if (thisDefender->getTargetX() || thisDefender->getTargetY()) { + targetX = thisDefender->getTargetX(); + targetY = thisDefender->getTargetY(); + } + + retVal = new int[4]; + retVal[0] = thisDefender->getSourceUnit(); + retVal[1] = thisDefender->getUnit(); + retVal[2] = thisDefender->getAngle(); + retVal[3] = thisDefender->getPower(); + } + + if (defStatus == -3) { + retVal = new int[4]; + retVal[0] = 0; + retVal[1] = SKIP_TURN; + retVal[2] = 0; + retVal[3] = 0; + } + + assert(targetX >= 0 && targetY >= 0); + + if (retVal[1] == ITEM_CRAWLER) { + warning("defend target is launching a crawler"); + } + + delete thisDefender; + return retVal; +} + +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled) { + assert((unitType >= 0) && (unitType <= 12)); + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); + return retVal; +} + +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist) { + assert((unitType >= 0) && (unitType <= 12)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); + return retVal; +} + +int getDistance(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_DIST], 4, originX, originY, endX, endY); + return retVal; +} + +int calcAngle(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, 0); + return retVal; +} + +int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, noWrapFlag); + return retVal; +} + +int getTerrain(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_TERRAIN_TYPE], 2, x, y); + return retVal; +} + +int estimateNextRoundEnergy(int player) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); + return retVal / 10; +} + +int getHubX(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); + return retVal; +} + +int getHubY(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); + return retVal; +} + +int getMaxX() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); + return retVal; +} + +int getMaxY() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); + return retVal; +} + +int getCurrentPlayer() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); + assert(retVal != 0); + return retVal; +} + +int getMaxPower() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); + return retVal; +} + +int getMinPower() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); + return retVal; +} + +int getTerrainSquareSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); + return retVal; +} + +int getBuildingOwner(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); + return retVal; +} + +int getBuildingState(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); + return retVal; +} + +int getBuildingType(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); + return retVal; +} + +int getBuildingArmor(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); + return retVal; +} + +int getBuildingWorth(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); + return retVal; +} + +int getEnergyPoolsArray() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); + return retVal; +} + +int getCoordinateVisibility(int x, int y, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 4, D_GET_COORDINATE_VISIBILITY, x, y, playerNum); + return retVal; +} + +int getUnitVisibility(int unit, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_UNIT_VISIBILITY, unit, playerNum); + return retVal; +} + +int getEnergyPoolVisibility(int pool, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_ENERGY_POOL_VISIBILITY, pool, playerNum); + return retVal; +} + +int getNumberOfPools() { + int retVal = 0; + + if (AItype[getCurrentPlayer()]->getID() == ENERGY_HOG) { + retVal = 1; + } else { + retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); + } + + return retVal; +} + +int getNumberOfPlayers() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); + return retVal; +} + +int getPlayerEnergy() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_ENERGY); + return static_cast<int>(static_cast<float>(retVal) / 10.0); +} + +int getPlayerMaxTime() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); + return retVal; +} + +int getWindXSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); + return retVal; +} + +int getWindYSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); + return retVal; +} + +int getTotalWindSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); + return retVal; +} + +int getWindXSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); + return retVal; +} + +int getWindYSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); + return retVal; +} + +int getBigXSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); + return retVal; +} + +int getBigYSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); + return retVal; +} + +int getEnergyPoolWidth(int pool) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_ENERGY_POOL_WIDTH, pool); + return retVal; +} + +int getBuildingMaxArmor(int building) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); + return retVal; +} + +int getTimerValue(int timerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); + return retVal; +} + +int getLastAttacked(int &x, int &y) { + int currentPlayer = getCurrentPlayer(); + x = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_X, currentPlayer); + y = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_Y, currentPlayer); + + if (x || y) return 1; + + return 0; +} + +int getPlayerTeam(int player) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); + return retVal; +} + +int getBuildingTeam(int building) { + assert((building >= 1) && (building <= 500)); + + if (getBuildingOwner(building) == 0) return 0; + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); + return retVal; +} + +int getFOW() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_FOW); + return retVal; +} + +int getAnimSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); + return retVal; +} + +int getBuildingStackPtr() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); + return retVal; +} + +int getTurnCounter() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); + return retVal; +} + +int getGroundAltitude(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_GROUND_ALTITUDE], 2, x, y); + return retVal; +} + +int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_CORD_OVERLAP], 4, xStart, yStart, affectRadius, simulateFlag); + return retVal; +} + +int checkForAngleOverlap(int unit, int angle) { + assert(angle > -721); + assert(angle < 721); + + if (!unit) return 0; + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); + return retVal; +} + +int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_UNIT_OVERLAP], 4, x, y, radius, ignoredUnit); + return retVal; +} + +int checkForEnergySquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); + return retVal; +} + +int aiChat() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_AI_CHAT], 0); + return retVal; +} + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 6, originX, originY, endX, endY, threshold, olFlag); + return retVal; +} + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 5, originX, originY, endX, endY, threshold); + return retVal; +} + +int checkIfWaterState(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_STATE], 2, x, y); + return retVal; +} + +int checkIfWaterSquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_SQUARE], 2, x, y); + return retVal; +} + +int getUnitsWithinRadius(int x, int y, int radius) { + assert(x >= 0); + assert(y >= 0); + assert(radius >= 0); + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_UNITS_WITHIN_RADIUS], 3, x, y, radius); + return retVal; +} + +int getLandingPoint(int x, int y, int power, int angle) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_LANDING_POINT], 4, x, y, power, angle); + return retVal; +} + +int getEnemyUnitsVisible(int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); + return retVal; +} + +float degToRad(float degrees) { + return degrees * M_PI / 180.; +} + +void limitLocation(int &a, int &b, int c, int d) { + if (a >= 0) { + a = (a % c); + } else { + a = (c - (abs(a) % c)); + } + + if (b >= 0) { + b = (b % d); + } else { + b = (d - (abs(b) % d)); + } +} + +int energyPoolSize(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 115; + + case 116: + return 100; + + case 63: + return 60; + } + + return 0; +} + +int getMaxCollectors(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 4; + + case 116: + return 3; + + case 63: + return 2; + } + + return 0; +} + +int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy) { + static int sXSpeed = 0; + static int sYSpeed = 0; + static int sZSpeed = 0; + static int sXLoc = 0; + static int sYLoc = 0; + static int sZLoc = 0; + static int sFrictionCount = 0; + static int sWhichRadius = 0; + static int sWhichUnit = 0; + + int gWindXSpeed = getWindXSpeed(); + int gWindYSpeed = getWindYSpeed(); + int gTotalWindSpeed = getTotalWindSpeed(); + int gWindXSpeedMax = getWindXSpeedMax(); + int gWindYSpeedMax = getWindYSpeedMax(); + int bigXSize = getBigXSize(); + int bigYSize = getBigYSize(); + + int groundAltitude = 0; + int totalSpeed = 0; + int resultingPoint = 0; + int unscaledXLoc = 0; + int unscaledYLoc = 0; + int terrainType = 0; + int passedBeyondUnit = 0; + int currentDist = 0; + + + if (!numSteps) + numSteps = 1; + + if (!sXSpeed && !sYSpeed) { + sZSpeed = (static_cast<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + + sZSpeed *= SCALE_Z; + + sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; + + sXLoc = x * SCALE_X; + sYLoc = y * SCALE_Y; + + sFrictionCount = 0; + sWhichRadius = NODE_DETECT_RADIUS + 1; + + sWhichUnit = getClosestUnit(x + 10, y, 30, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 0, 0); + } + + for (int i = 1; i <= numSteps; i++) { + unscaledXLoc = sXLoc / SCALE_X; + unscaledYLoc = sYLoc / SCALE_Y; + + groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); + groundAltitude *= SCALE_Z; + + sZLoc += sZSpeed / SCALE_Z; + + resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); + + if (sZLoc <= groundAltitude) { + terrainType = getTerrain(unscaledXLoc, unscaledYLoc); + + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + if (terrainType == TERRAIN_TYPE_GOOD) + return resultingPoint; + else + return 0 - resultingPoint; + } else { + if (checkIfWaterState(unscaledXLoc, unscaledYLoc)) { + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + return 0 - resultingPoint; + } else { + int cfco = 0; + int cfuo = 0; + int cfes = 0; + int cfao = 0; + cfao = checkForAngleOverlap(sWhichUnit, angle); + + cfco = checkForCordOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, 1); + cfuo = checkForUnitOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, sWhichUnit); + + if (!isEnergy) + cfes = checkForEnergySquare(unscaledXLoc, unscaledYLoc); + + if (cfco || cfuo || cfes || cfao) { + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + return 0 - resultingPoint; + } else { + sFrictionCount++; + + if (sFrictionCount == 10) { + sFrictionCount = 0; + + if (!gWindXSpeed) + sXSpeed = sXSpeed * .95; + + if (!gWindYSpeed) + sYSpeed = sYSpeed * .95; + } + + if (passedBeyondUnit) { + totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); + + if (totalSpeed > gTotalWindSpeed) { + if (gWindXSpeed > 0) { + if (sXSpeed < gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } else { + if (sXSpeed > gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } + + if (gWindYSpeed > 0) { + if (sYSpeed < gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } else { + if (sYSpeed > gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } + } + } else { + currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); + + if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) + passedBeyondUnit = 1; + } + + sXLoc += sXSpeed; + sYLoc += sYSpeed; + + limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); + + sZSpeed -= GRAVITY_CONSTANT; + } + } + } + } + + return 0; +} + +int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { + static int sXSpeed = 0; + static int sYSpeed = 0; + static int sZSpeed = 0; + static int sXLoc = 0; + static int sYLoc = 0; + static int sZLoc = 0; + static int sFrictionCount = 0; + + int gWindXSpeed = getWindXSpeed(); + int gWindYSpeed = getWindYSpeed(); + int gTotalWindSpeed = getTotalWindSpeed(); + int gWindXSpeedMax = getWindXSpeedMax(); + int gWindYSpeedMax = getWindYSpeedMax(); + int bigXSize = getBigXSize(); + int bigYSize = getBigYSize(); + + int groundAltitude = 0; + int totalSpeed = 0; + int resultingPoint = 0; + int unscaledXLoc = 0; + int unscaledYLoc = 0; + int terrainType = 0; + int passedBeyondUnit = 0; + int currentDist = 0; + + if (!numSteps) numSteps = 1; + + if (!sXSpeed && !sYSpeed) { + sZSpeed = (static_cast<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + + sZSpeed *= SCALE_Z; + + sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; + + sXLoc = x * SCALE_X; + sYLoc = y * SCALE_Y; + + sFrictionCount = 0; + } + + for (int i = 1; i <= numSteps; i++) { + unscaledXLoc = sXLoc / SCALE_X; + unscaledYLoc = sYLoc / SCALE_Y; + + groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); + groundAltitude *= SCALE_Z; + sZLoc += sZSpeed / SCALE_Z; + resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); + + if (sZLoc <= groundAltitude) { + terrainType = getTerrain(unscaledXLoc, unscaledYLoc); + + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + if (terrainType == TERRAIN_TYPE_GOOD) + return resultingPoint; + else + return 0 - resultingPoint; + } else { + sFrictionCount++; + + if (sFrictionCount == 10) { + sFrictionCount = 0; + + if (!gWindXSpeed) + sXSpeed = sXSpeed * .95; + + if (!gWindYSpeed) + sYSpeed = sYSpeed * .95; + } + + if (passedBeyondUnit) { + totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); + + if (totalSpeed > gTotalWindSpeed) { + if (gWindXSpeed > 0) { + if (sXSpeed < gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } else { + if (sXSpeed > gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } + + if (gWindYSpeed > 0) { + if (sYSpeed < gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } else { + if (sYSpeed > gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } + } + } else { + currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); + + if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) + passedBeyondUnit = 1; + } + + sXLoc += sXSpeed; + sYLoc += sYSpeed; + + limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); + + sZSpeed -= GRAVITY_CONSTANT; + } + } + + return 0; +} + +int fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { + int distance = power * 480 / getMaxPower(); + float radAngle = degToRad(angle); + int maxX = getMaxX(); + int maxY = getMaxY(); + + x += distance * cos(radAngle); + y += distance * sin(radAngle); + + x = (x + maxX) % maxX; + y = (y + maxY) % maxY; + + return MAX(1, x + y * maxX); +} + +int getEnergyHogType() { + return energyHogType; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_main.h b/engines/scumm/he/moonbase/ai_main.h new file mode 100644 index 0000000000..8c58a70b28 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.h @@ -0,0 +1,182 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_MAIN_H +#define SCUMM_HE_MOONBASE_AI_MAIN_H + +#include "scumm/he/moonbase/ai_tree.h" + +namespace Scumm { + +class ScummEngine_v90he; + +extern ScummEngine_v90he *_vm; + +typedef Common::Array<int>::iterator intVecItr; + +enum { + TERRAIN_TYPE_GOOD = 0, + TERRAIN_TYPE_SLOPE = 1, + TERRAIN_TYPE_WATER = 2, + MAX_MEMORY = 3 +}; + +enum { + ITEM_BOMB = 0, + ITEM_CLUSTER = 1, + ITEM_REPAIR = 2, + ITEM_ANTIAIR = 3, + ITEM_BRIDGE = 4, + ITEM_TOWER = 5, + ITEM_GUIDED = 6, + ITEM_EMP = 7, + ITEM_SPIKE = 8, + ITEM_RECLAIMER = 9, + ITEM_BALLOON = 10, + ITEM_MINE = 11, + ITEM_CRAWLER = 12, + ITEM_VIRUS = 13, + ITEM_ENERGY = 14, + ITEM_SHIELD = 15, + ITEM_OFFENSE = 16, + ITEM_HUB = 17, + ITEM_TIME_EXPIRED = 18, + SKIP_TURN = -999 +}; + +enum BuildingTypes { + BUILDING_ENERGY_COLLECTOR = 3, + BUILDING_MAIN_BASE = 4, + BUILDING_BRIDGE = 5, + BUILDING_TOWER = 6, + BUILDING_EXPLOSIVE_MINE = 7, + BUILDING_SHIELD = 8, + BUILDING_ANTI_AIR = 9, + BUILDING_OFFENSIVE_LAUNCHER = 10, + BUILDING_BALLOON = 11, + BUILDING_CRAWLER = 12 +}; + +enum { + ENERGY_POOL_X = 45, + ENERGY_POOL_Y = 46, + ENERGY_POOL_UNITS_ON = 47, + + MIN_DIST = 108 +}; + +void resetAI(ScummEngine_v90he *vm); +void cleanUpAI(); +void setAIType(const int paramCount, const int32 *params); +int masterControlProgram(const int paramCount, const int32 *params); + +int chooseBehavior(); +int chooseTarget(int behavior); + +Tree *initApproachTarget(int targetX, int targetY, Node **retNode); +int *approachTarget(Tree *myTree, int &x, int &y, Node **currentNode); +Tree *initAcquireTarget(int targetX, int targetY, Node **retNode); +int *acquireTarget(int targetX, int targetY); +int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode); +int *offendTarget(int &targetX, int &targetY, int index); +int *defendTarget(int &targetX, int &targetY, int index); +int *energizeTarget(int &targetX, int &targetY, int index); + +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled); +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist); + +int getDistance(int originX, int originY, int endX, int endY); +int calcAngle(int originX, int originY, int endX, int endY); +int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag); +int getTerrain(int x, int y); +int getHubX(int hub); +int getHubY(int hub); +int getMaxX(); +int getMaxY(); +int getCurrentPlayer(); +int getMaxPower(); +int getMinPower(); +int getTerrainSquareSize(); +int getBuildingOwner(int building); +int getBuildingState(int building); +int getBuildingType(int building); +int getBuildingArmor(int building); +int getBuildingWorth(int building); +int getEnergyPoolsArray(); +int getCoordinateVisibility(int x, int y, int playerNum); +int getUnitVisibility(int unit, int playerNum); +int getEnergyPoolVisibility(int pool, int playerNum); +int getNumberOfPools(); +int getNumberOfPlayers(); +int getPlayerEnergy(); +int getPlayerMaxTime(); +int getWindXSpeed(); +int getWindYSpeed(); +int getTotalWindSpeed(); +int getWindXSpeedMax(); +int getWindYSpeedMax(); +int getBigXSize(); +int getBigYSize(); +int getEnergyPoolWidth(int pool); +int getBuildingMaxArmor(int building); +int getTimerValue(int timerNum); +int getLastAttacked(int &x, int &y); +int getPlayerTeam(int player); +int getBuildingTeam(int building); +int getFOW(); +int getAnimSpeed(); +int getBuildingStackPtr(); +int getTurnCounter(); + +int getGroundAltitude(int x, int y); +int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag); +int checkForAngleOverlap(int unit, int angle); +int estimateNextRoundEnergy(int player); +int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit); +int checkForEnergySquare(int x, int y); +int aiChat(); + +int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy); +int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps); +int fakeSimulateWeaponLaunch(int x, int y, int power, int angle); + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag); +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold); +int checkIfWaterState(int x, int y); +int checkIfWaterSquare(int x, int y); +int getUnitsWithinRadius(int x, int y, int radius); +int getLandingPoint(int x, int y, int power, int angle); +int getEnemyUnitsVisible(int playerNum); + +float degToRad(float degrees); +void limitLocation(int &a, int &b, int c, int d); +int energyPoolSize(int pool); +int getMaxCollectors(int pool); + +int getEnergyHogType(); + +extern Common::Array<int> lastXCoord[]; +extern Common::Array<int> lastYCoord[]; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_node.cpp b/engines/scumm/he/moonbase/ai_node.cpp new file mode 100644 index 0000000000..083a156a31 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.cpp @@ -0,0 +1,153 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +IContainedObject::IContainedObject(IContainedObject &sourceContainedObject) { + _objID = sourceContainedObject.getObjID(); + _valueG = sourceContainedObject.getG(); +} + +int Node::_nodeCount = 0; + +Node::Node() { + _parent = NULL; + _depth = 0; + _nodeCount++; + _contents = NULL; +} + +Node::Node(Node *sourceNode) { + _parent = NULL; + _children = sourceNode->getChildren(); + + _depth = sourceNode->getDepth(); + + _contents = sourceNode->getContainedObject()->duplicate(); +} + +Node::~Node() { + if (_contents != NULL) { + delete _contents; + _contents = NULL; + } + + _nodeCount--; +} + +int Node::generateChildren() { + int numChildren = _contents->numChildrenToGen(); + + int numChildrenGenerated = numChildren; + int errorCode = -1; + static int i = 0; + + while (i < numChildren) { + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int completionFlag; + + IContainedObject *thisContObj = _contents->createChildObj(i, completionFlag); + assert(!(thisContObj != NULL && completionFlag == 0)); + + if (!completionFlag) { + _children.pop_back(); + delete tempNode; + return 0; + } + + i++; + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + numChildrenGenerated--; + } + } + + i = 0; + + if (numChildrenGenerated > 0) + return numChildrenGenerated; + + return errorCode; +} + + +int Node::generateNextChild() { + int numChildren = _contents->numChildrenToGen(); + + static int i = 0; + + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int compFlag; + IContainedObject *thisContObj = _contents->createChildObj(i, compFlag); + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + } + + ++i; + + if (i > numChildren) + i = 0; + + return i; +} + +Node *Node::popChild() { + Node *temp; + + temp = _children.back(); + _children.pop_back(); + return temp; +} + +Node *Node::getFirstStep() { + Node *currentNode = this; + + if (currentNode->getParent() == NULL) + return currentNode; + + while (currentNode->getParent()->getParent() != NULL) + currentNode = currentNode->getParent(); + + assert(currentNode->getDepth() == 1); + + return currentNode; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_node.h b/engines/scumm/he/moonbase/ai_node.h new file mode 100644 index 0000000000..0c3ef2f023 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.h @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_NODE_H +#define SCUMM_HE_MOONBASE_AI_NODE_H + +#include "common/array.h" + +namespace Scumm { + +const float SUCCESS = -1; +const float FAILURE = 1e20f; + +class IContainedObject { +private: + int _objID; + float _valueG; + +protected: + virtual float getG() const { return _valueG; } + virtual float calcH() { return 0; } + +public: + IContainedObject() { _valueG = 0; _objID = -1; } + IContainedObject(float inG) { _valueG = inG; _objID = -1; } + IContainedObject(IContainedObject &sourceContainedObject); + virtual ~IContainedObject() {} + + virtual IContainedObject *duplicate() = 0; + + void setValueG(float inG) { _valueG = inG; } + float getValueG() { return _valueG; } + + int getObjID() const { return _objID; } + void setObjID(int inputObjID) { _objID = inputObjID; } + + virtual int numChildrenToGen() = 0; + virtual IContainedObject *createChildObj(int index, int &completionFlag) = 0; + + virtual int checkSuccess() = 0; + virtual float calcT() { return getG(); } + + float returnG() const { return getG(); } +}; + +class Node { +private: + Node *_parent; + Common::Array<Node *> _children; + + int _depth; + static int _nodeCount; + + IContainedObject *_contents; + +public: + Node(); + Node(Node *sourceNode); + ~Node(); + + void setParent(Node *parentPtr) { _parent = parentPtr; } + Node *getParent() const { return _parent; } + + void setDepth(int depth) { _depth = depth; } + int getDepth() const { return _depth; } + + static int getNodeCount() { return _nodeCount; } + + void setContainedObject(IContainedObject *value) { _contents = value; } + IContainedObject *getContainedObject() { return _contents; } + + Common::Array<Node *> getChildren() const { return _children; } + int generateChildren(); + int generateNextChild(); + Node *popChild(); + + float getObjectT() { return _contents->calcT(); } + + Node *getFirstStep(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_pattern.h b/engines/scumm/he/moonbase/ai_pattern.h new file mode 100644 index 0000000000..ae1fa6b55e --- /dev/null +++ b/engines/scumm/he/moonbase/ai_pattern.h @@ -0,0 +1,162 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_PATTERN_H +#define SCUMM_HE_MOONBASE_AI_PATTERN_H + +namespace Scumm { + +const int NO_PATTERN = 0; +const int PATTERN_FOUND = 1; + +class patternInstance { +private: + int _sourceHub; + int _unit; + int _power; + int _angle; + +public: + patternInstance() { + _sourceHub = 0; + _unit = 0; + _power = 0; + _angle = 0; + } + + patternInstance(int sh, int unit, int power, int angle) { + setSourceHub(sh); + setUnit(unit); + setPower(power); + setAngle(angle); + } + + void setSourceHub(int sh) { _sourceHub = sh; } + void setUnit(int unit) { _unit = unit; } + + void setPower(int power) { + if (power < 300) + _power = 1; + else if (power < 480) + _power = 2; + else + _power = 3; + } + + void setAngle(int angle) { + int tempAngle = angle % 360; + + if ((tempAngle >= 0) && (tempAngle < 90)) + _angle = 1; + + if ((tempAngle >= 90) && (tempAngle < 180)) + _angle = 2; + + if ((tempAngle >= 180) && (tempAngle < 270)) + _angle = 3; + + if ((tempAngle >= 270)) + _angle = 4; + } + + int getSourceHub() const { return _sourceHub; } + int getUnit() const { return _unit; } + int getPowerIndex() const { return _power; } + int getAngleIndex() const { return _angle; } + + static int comparePatterns(patternInstance *p1, patternInstance *p2) { + if (p1->getSourceHub() != p2->getSourceHub()) + return 0; + + if (p1->getUnit() != p2->getUnit()) + return 0; + + if (p1->getUnit() == -999) + return 0; + + int temp = abs(p1->getPowerIndex() - p2->getPowerIndex()); + + if (temp > 1) + return 0; + + temp = abs(p1->getAngleIndex() - p2->getAngleIndex()); + + if (temp > 1 && temp < 3) + return 0; + + return 1; + } +}; + +class patternList { +private: + patternInstance *theList[10]; + int listIndex; + +public: + patternList() { + for (int i = 0; i < 10; i++) { + theList[i] = new patternInstance(); + } + + listIndex = 0; + } + ~patternList() { + for (int i = 0; i < 10; i++) { + delete theList[i]; + } + } + + void addPattern(int sh, int unit, int power, int angle) { + theList[listIndex]->setSourceHub(sh); + theList[listIndex]->setUnit(unit); + theList[listIndex]->setPower(power); + theList[listIndex]->setAngle(angle); + + listIndex++; + + if (listIndex > 9) + listIndex = 0; + } + + int evaluatePattern(int sh, int unit, int power, int angle) { + patternInstance *patternToMatch = new patternInstance(sh, unit, power, angle); + int matchCount = 0; + + for (int i = 0; i < 9; i++) { + if (patternInstance::comparePatterns(theList[i], patternToMatch)) { + matchCount++; + } + } + + delete patternToMatch; + + if (matchCount > 2) + return PATTERN_FOUND; + + return NO_PATTERN; + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.cpp b/engines/scumm/he/moonbase/ai_targetacquisition.cpp new file mode 100644 index 0000000000..fb74d8ae3c --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.cpp @@ -0,0 +1,556 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" + +#include "scumm/he/moonbase/ai_targetacquisition.h" +#include "scumm/he/moonbase/ai_main.h" +#include "scumm/he/moonbase/ai_weapon.h" + +namespace Scumm { + +int Sortie::_sSourceX = 0; +int Sortie::_sSourceY = 0; + +int Sortie::_sTargetX = 0; +int Sortie::_sTargetY = 0; + +Sortie::~Sortie() { + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + delete *k; + } +} + +void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY) { + DefenseUnit *thisUnit; + int currentPlayer = getCurrentPlayer(); + + for (int i = 0; i < 200; i++) { + int thisElement = _vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i); + + if (thisElement) { + if (getBuildingOwner(thisElement)) { + if (getPlayerTeam(currentPlayer) != getBuildingTeam(thisElement)) { + int type = getBuildingType(thisElement); + + switch (type) { + case BUILDING_ANTI_AIR: + thisUnit = new AntiAirUnit(); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(); + break; + + case BUILDING_EXPLOSIVE_MINE: + if (getDistance(getHubX(thisElement), getHubY(thisElement), defendX, defendY) < 90) + thisUnit = new MineUnit(); + else + thisUnit = NULL; + + break; + + case BUILDING_CRAWLER: + thisUnit = NULL; + break; + + default: + thisUnit = NULL; + break; + } + + if (thisUnit != NULL) { + thisUnit->setID(thisElement); + thisUnit->setPos(getHubX(thisElement), getHubY(thisElement)); + + if (getBuildingState(thisElement)) thisUnit->setState(DUS_OFF); + + _enemyDefenses.push_back(thisUnit); + } + } + } + } else { + i = 200; + } + } +} + +int *Sortie::getShotPos() const { + int *retVal = new int[2]; + + retVal[0] = _shotPosX; + retVal[1] = _shotPosY; + + return retVal; +} + +int Sortie::numChildrenToGen() { + int retVal = MAX<uint>(_enemyDefenses.size(), 1) * NUM_SHOT_POSITIONS * NUM_WEAPONS; + return retVal; +} + +IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { + float thisDamage; + Sortie *retSortie = new Sortie; + int activeDefenses = 0; + + Common::Array<DefenseUnit *> thisEnemyDefenses; + + // Copy the defensive unit list from the parent + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + DefenseUnit *temp; + + switch ((*k)->getType()) { + case DUT_ANTI_AIR: + temp = new AntiAirUnit(*k); + break; + + case DUT_SHIELD: + temp = new ShieldUnit(*k); + break; + + case DUT_MINE: + temp = new MineUnit(*k); + break; + + case DUT_CRAWLER: + temp = new CrawlerUnit(*k); + break; + + default: + temp = new ShieldUnit(*k); + break; + } + + thisEnemyDefenses.push_back(temp); + } + + // Calculate the current target from the index + DefenseUnit *currentTarget = *(thisEnemyDefenses.begin() + static_cast<int>(index / (NUM_WEAPONS * NUM_SHOT_POSITIONS))); + + assert(currentTarget); + + // Pick correct weapon according to index + Weapon *currentWeapon = new Weapon(currentTarget->selectWeapon(index % NUM_WEAPONS)); + retSortie->setUnitType(currentWeapon->getTypeID()); + + // Calculate distance from target to source hub + int distance = getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY()); + + // Pick correct shot position according to index + Common::Point *targetCoords; + targetCoords = currentTarget->createTargetPos((static_cast<int>(index / NUM_WEAPONS) % NUM_SHOT_POSITIONS), distance, currentWeapon->getTypeID(), getSourcePosX(), getSourcePosY()); + retSortie->setShotPos(targetCoords->x, targetCoords->y); + + // Set the g value based on cost of the weapon + retSortie->setValueG(getG() + currentWeapon->getCost()); + + int AAcounter = 3; + + // Loop through defensive units, toggling anti-air units and deciding if this weapon will land safely + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + + // Check to see if we're within an active defense's radius + if ((distance < (*i)->getRadius()) && ((*i)->getState() == DUS_ON)) { + activeDefenses++; + + // Turn off this anti-air and drop the coverage count + if (((*i)->getType() == DUT_ANTI_AIR)) { + (*i)->setState(DUS_OFF); + + if (currentWeapon->getTypeID() == ITEM_CLUSTER) + AAcounter--; + else + AAcounter = 0; + } + + // Essentially disable this weapon choice, due to its impact with a shield, or untriggered anti-air + if (((*i)->getType() == DUT_SHIELD) || !AAcounter) { + retSortie->setValueG(1000); + i = thisEnemyDefenses.end() - 1; + } + } else { + // Turn on any anti-airs that were off the previous turn + if (((*i)->getType() == DUT_ANTI_AIR) && ((*i)->getState() == DUS_OFF)) + (*i)->setState(DUS_ON); + } + } + + // Turn on all the non-anti-air units in preparation for emp's and the next turn + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getType() != DUT_ANTI_AIR) { + (*i)->setState(DUS_ON); + } + } + + // If this weapon is still valid + if (retSortie->getValueG() < 1000) { + // Apply emp effects and damage to all units in range of weapon + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); ) { + // Special simulated crawler detonation location used, since it walks a bit + if (currentWeapon->getTypeID() == ITEM_CRAWLER) + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY()); + // Normal detonation location used here + else { + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + } + + if (distance < currentWeapon->getRadius()) { + // Apply damage + thisDamage = currentWeapon->getDamage(); + + if ((AAcounter != 3) && (currentWeapon->getTypeID() == ITEM_CLUSTER)) + thisDamage = 0; + + if (!_vm->_rnd.getRandomNumber(4)) + currentWeapon->setTypeID(ITEM_MINE); + + (*i)->setDamage(thisDamage); + + // Apply emp effect + if (currentWeapon->getTypeID() == ITEM_EMP) { + (*i)->setState(DUS_OFF); + } + + // Remove destroyed defenses + if ((*i)->getArmor() <= 0) { + delete *i; + i = thisEnemyDefenses.erase(i); + } else { + i++; + } + } else { + i++; + } + } + } + + retSortie->setEnemyDefenses(thisEnemyDefenses); + + delete targetCoords; + delete currentWeapon; + return retSortie; +} + +float Sortie::calcH() { + float retValue = 0; + Common::Array<DefenseUnit *> thisEnemyDefenses = getEnemyDefenses(); + + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getState() == DUS_ON) { + switch ((*i)->getType()) { + case DUT_ANTI_AIR: + retValue += 1; + + case DUT_MINE: + retValue += 1; + break; + + case DUT_SHIELD: + retValue += 1; + break; + } + } + } + + return retValue; +} + +int Sortie::checkSuccess() { + if (!_enemyDefenses.size()) return SUCCESS; + + int targetX = getTargetPosX(); + int targetY = getTargetPosY(); + + int targetCheck = 0; + + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + if (((*i)->getState() == DUS_ON) && ((*i)->getType() != DUT_HUB)) { + return 0; + } + + if (((*i)->getPosX() == targetX) && ((*i)->getPosY() == targetY)) targetCheck = 1; + } + + if (!targetCheck) + return SUCCESS; + + // If shot pos == target pos return SUCCESS; + if ((targetX == getShotPosX()) && (getTargetPosY() == getShotPosY())) { + return SUCCESS; + } + + return 0; +} + +float Sortie::calcT() { + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +IContainedObject *Sortie::duplicate() { + return this; +} + + +void Sortie::printEnemyDefenses() { + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + warning("Unit %d - Type: %d, Armor: %d, Status: %d", (*i)->getID(), (*i)->getType(), static_cast<int>((*i)->getArmor()), (*i)->getState()); + } +} + +int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) { + int currentPlayer = getCurrentPlayer(); + + //get list of near hubs + int unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 480); + + const int NUM_HUBS = 10; + //Order on dist + int hubArray[NUM_HUBS] = { 0 }; + int hubIndex = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + if (((getBuildingType(thisUnit) == BUILDING_MAIN_BASE) || (getBuildingType(thisUnit) == BUILDING_OFFENSIVE_LAUNCHER)) && (getBuildingOwner(thisUnit) == currentPlayer)) { + for (int j = 0; j < NUM_HUBS; j++) { + if (hubArray[j]) { + int distCurrent = getDistance(targetX, targetY, getHubX(thisUnit), getHubY(thisUnit)); + int distSaved = getDistance(targetX, targetY, getHubX(hubArray[j]), getHubY(hubArray[j])); + + if (distCurrent < distSaved) { + hubArray[hubIndex] = hubArray[j]; + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } else { + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } + } + } + + if (hubIndex >= NUM_HUBS) { + hubIndex = NUM_HUBS; + i = 200; + } + } + + _vm->nukeArray(unitsArray); + + //Check if repair is needed + int targetUnit = getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0); + + if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (getBuildingTeam(targetUnit) == getPlayerTeam(currentPlayer))) { + int armor = getBuildingArmor(targetUnit); + + if (armor < getBuildingMaxArmor(targetUnit)) { + unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 170); + int defCount = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + if (((getBuildingType(thisUnit) == BUILDING_SHIELD) || (getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (getBuildingOwner(thisUnit) == currentPlayer) && (getBuildingState(thisUnit) == 0)) { + defCount++; + i = 200; + } + } + } + + _vm->nukeArray(unitsArray); + + if (defCount) { + //repair + int hubUnit = getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110); + + if (hubUnit && (hubUnit != targetUnit)) { + int powAngle = abs(getPowerAngleFromPoint(getHubX(hubUnit), getHubY(hubUnit), targetX, targetY, 20)); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + setTargetX(targetX); + setTargetY(targetY); + + setSourceUnit(hubUnit); + setUnit(ITEM_REPAIR); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + //For each hub + for (int i = 0; i < MIN(NUM_HUBS, hubIndex); i++) { + int hubX = getHubX(hubArray[i]); + int hubY = getHubY(hubArray[i]); + //get angle to hub + int directAngleToHub = 0; + + //If this hub is the target + if ((hubX == targetX) && (hubY == targetY)) { + //make the angle seed point at the closest enemy + int enemyUnit = getClosestUnit(hubX, hubY, getMaxX(), currentPlayer, 0, 0, 0); + directAngleToHub = calcAngle(targetX, targetY, getHubX(enemyUnit), getHubY(enemyUnit)); + } else { + directAngleToHub = calcAngle(targetX, targetY, hubX, hubY); + } + + //Number of random chances to land + for (int j = 0; j < 3; j++) { + //Pick random angle and dist within semicircle (-90 to +90) and (40 to 150) + int randAngle = directAngleToHub + _vm->_rnd.getRandomNumber(179) - 90; + int randDist = _vm->_rnd.getRandomNumber(109) + 40; + + int x = targetX + randDist * cos(degToRad(randAngle)); + int y = targetY + randDist * sin(degToRad(randAngle)); + + int powAngle = getPowerAngleFromPoint(hubX, hubY, x, y, 20); + + if (powAngle < 0) + continue; + + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + int coords = 0; + coords = simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0); + + //if valid, return + if (coords > 0) { + //warning("The prospective launching hub for this defensive unit is: %d", hubArray[i]); + + setSourceX(hubX); + setSourceY(hubY); + setTargetX((x + getMaxX()) % getMaxX()); + setTargetY((y + getMaxY()) % getMaxY()); + setSourceUnit(hubArray[i]); + + int unitsArray2 = getUnitsWithinRadius(targetX + 5, targetY, 200); + int shieldCount = 0; + + for (int k = 0; k < 200; k++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray2, 0, k); + + if (thisUnit) { + if ((getBuildingType(thisUnit) == BUILDING_SHIELD) && (getBuildingOwner(thisUnit) == currentPlayer)) + shieldCount++; + + if ((getBuildingType(thisUnit) == BUILDING_BRIDGE) && (getBuildingOwner(thisUnit) == currentPlayer)) { + shieldCount--; + shieldCount = MAX(-1, shieldCount); + } + } + } + + if ((_vm->_rnd.getRandomNumber((int)pow(3.0f, shieldCount + 1) - 1) == 0) && (getPlayerEnergy() > 6)) + setUnit(ITEM_SHIELD); + else + setUnit(ITEM_ANTIAIR); + + setPower(power); + setAngle(angle); + + _vm->nukeArray(unitsArray2); + return 1; + } + + if (coords < 0) { + //drop a bridge for the cord + int yCoord = -coords / getMaxX(); + int xCoord = -coords - (yCoord * getMaxX()); + + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - x; + int yDist = yCoord - y; + x = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); + y = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); + + setTargetX(x); + setTargetY(y); + + int nextUnit = getClosestUnit(x, y, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); + powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), x, y, 15); + + powAngle = abs(powAngle); + power = powAngle / 360; + angle = powAngle - (power * 360); + + setSourceUnit(nextUnit); + setUnit(ITEM_BRIDGE); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + // Else create new hub + int count = 0; + int coords = 0; + + if (hubIndex == 0) return -3; + + do { + int sourceHub = hubArray[_vm->_rnd.getRandomNumber(hubIndex - 1)]; + + setSourceX(getHubX(sourceHub)); + setSourceY(getHubY(sourceHub)); + setSourceUnit(sourceHub); + setUnit(ITEM_HUB); + setPower(_vm->_rnd.getRandomNumber(299) + 200); + setAngle(_vm->_rnd.getRandomNumber(359)); + count++; + + if (count > (NUM_HUBS * 3)) break; + + coords = simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0); + } while (coords <= 0); + + if (coords > 0) { + setTargetX(coords % getMaxX()); + setTargetY(coords / getMaxX()); + } else { + setTargetX(0); + setTargetY(0); + } + + return -1; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.h b/engines/scumm/he/moonbase/ai_targetacquisition.h new file mode 100644 index 0000000000..6ac9e34751 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.h @@ -0,0 +1,150 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H +#define SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H + +#include "scumm/he/moonbase/ai_defenseunit.h" +#include "scumm/he/moonbase/ai_node.h" +#include "scumm/he/moonbase/ai_tree.h" + +namespace Scumm { + +const int NUM_IMPT_UNITS = 3; +const int NUM_SHOT_POSITIONS = 1; +const int NUM_WEAPONS = 3; + +class Sortie : public IContainedObject { +private: + static int _sSourceX; + static int _sSourceY; + + static int _sTargetX; + static int _sTargetY; + + int _unitType; + int _shotPosX, _shotPosY; + Common::Array<DefenseUnit *> _enemyDefenses; + + +public: + Sortie() { _unitType = 0; _shotPosX = _shotPosY = 0; } + virtual ~Sortie(); + + static void setSourcePos(int x, int y) { + _sSourceX = x; + _sSourceY = y; + } + static void setTargetPos(int x, int y) { + _sTargetX = x; + _sTargetY = y; + } + + void setUnitType(int unitType) { _unitType = unitType; } + + void setShotPosX(int shotPosX) { _shotPosX = shotPosX; } + void setShotPosY(int shotPosY) { _shotPosY = shotPosY; } + void setShotPos(int shotPosX, int shotPosY) { + _shotPosX = shotPosX; + _shotPosY = shotPosY; + } + + void setEnemyDefenses(Common::Array<DefenseUnit *> enemyDefenses) { + _enemyDefenses = enemyDefenses; + } + void setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY); + + void printEnemyDefenses(); + + static int getSourcePosX() { return _sSourceX; } + static int getSourcePosY() { return _sSourceY; } + static int getTargetPosX() { return _sTargetX; } + static int getTargetPosY() { return _sTargetY; } + + int getUnitType() const { return _unitType; } + + int getShotPosX() const { return _shotPosX; } + int getShotPosY() const { return _shotPosY; } + int *getShotPos() const; + + Common::Array<DefenseUnit *> getEnemyDefenses() const { return _enemyDefenses; } + + virtual IContainedObject *duplicate(); + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &completionFlag); + + + virtual float calcH(); + virtual int checkSuccess(); + virtual float calcT(); +}; + +class Defender { +private: + int _sourceX; + int _sourceY; + int _targetX; + int _targetY; + int _sourceUnit; + int _power; + int _angle; + int _unit; + +public: + void setSourceX(int sourceX) { _sourceX = sourceX; } + void setSourceY(int sourceY) { _sourceY = sourceY; } + void setTargetX(int targetX) { _targetX = targetX; } + void setTargetY(int targetY) { _targetY = targetY; } + void setSourceUnit(int sourceUnit) { _sourceUnit = sourceUnit; } + void setPower(int power) { _power = power; } + void setAngle(int angle) { _angle = angle; } + void setUnit(int unit) { _unit = unit; } + + int getSourceX() const { return _sourceX; } + int getSourceY() const { return _sourceY; } + int getTargetX() const { return _targetX; } + int getTargetY() const { return _targetY; } + int getSourceUnit() const { return _sourceUnit; } + int getPower() const { return _power; } + int getAngle() const { return _angle; } + int getUnit() const { return _unit; } + + int calculateDefenseUnitPosition(int targetX, int targetY, int index); +}; + +class defenseUnitCompare { +public: + bool operator()(DefenseUnit *x, DefenseUnit *y) { + //disabled units go at the end + if (x->getState() == DUS_OFF) { + warning("OFF"); + return 0; + } + + return x->getDistanceTo() < y->getDistanceTo(); + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_traveller.cpp b/engines/scumm/he/moonbase/ai_traveller.cpp new file mode 100644 index 0000000000..755ae36779 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.cpp @@ -0,0 +1,275 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/moonbase/ai_traveller.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +int Traveller::_targetPosX = 0; +int Traveller::_targetPosY = 0; +int Traveller::_maxDist = 0; + +int Traveller::_numToGen = 0; +int Traveller::_sizeAngleStep = 0; + +Traveller::Traveller() { + _waterFlag = 0; + setValueG(0); + unsetDisabled(); + + _sourceHub = 0; + _angleTo = 0; + _powerTo = 0; + _waterSourceX = 0; + _waterSourceY = 0; + _waterDestX = 0; + _waterDestY = 0; +} + +Traveller::Traveller(int originX, int originY) { + _waterFlag = 0; + setValueG(0); + unsetDisabled(); + + _posX = originX; + _posY = originY; + + _sourceHub = 0; + _angleTo = 0; + _powerTo = 0; + _waterSourceX = 0; + _waterSourceY = 0; + _waterDestX = 0; + _waterDestY = 0; +} + +void Traveller::adjustPosX(int offsetX) { + int maxX = getMaxX(); + int deltaX = _posX + offsetX; + + if (deltaX < 0) _posX = maxX + deltaX; + else if (deltaX > maxX) _posX = deltaX - maxX; + else _posX = deltaX; +} + +void Traveller::adjustPosY(int offsetY) { + int maxY = getMaxX(); + int deltaY = _posY + offsetY; + + if (deltaY < 0) _posY = maxY + deltaY; + else if (deltaY > maxY) _posY = deltaY - maxY; + else _posY = deltaY; +} + +void Traveller::adjustXY(int offsetX, int offsetY) { + adjustPosX(offsetX); + adjustPosY(offsetY); +} + +float Traveller::calcH() { + float retVal = 0; + // Calc dist from here to target + retVal = getDistance(_posX, _posY, _targetPosX, _targetPosY); + // Divide by _maxDist to get minimum number of jumps to goal + retVal /= static_cast<float>(_maxDist); + + return retVal * 2.0; +} + +int Traveller::numChildrenToGen() { + if (!_numToGen) + _numToGen = getAnimSpeed() + 2; + + return _numToGen; +} + +IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { + static int nodeCount = 0; + static int completionState = 1; + + if (!index) nodeCount = 0; + + nodeCount++; + + Traveller *retTraveller = new Traveller; + + static int dir, angle, power; + + if (completionState) { + // Calculate angle between here and target + int directAngle = 0; + + if (getEnergyHogType()) + directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1); + else + directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY); + + // Calculate the offset angle for this index + if (!_sizeAngleStep) + _sizeAngleStep = 52 - (getAnimSpeed() * 7); + + dir = _sizeAngleStep * ((static_cast<int>(index / NUM_POWER_STEPS) + 1) >> 1); + // Calculate the sign value for the offset for this index + int orientation = dir * (((static_cast<int>(index / NUM_POWER_STEPS) % 2) << 1) - 1); + // Add the offset angle to the direct angle to target + angle = orientation + directAngle; + + // Calculate power for this index + int maxPower = 0; + int directDist = getDistance(_posX, _posY, _targetPosX, _targetPosY); + + if (directDist > _maxDist + 120) + maxPower = getMaxPower(); + else + maxPower = (static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * getMaxPower(); + + maxPower -= 70; + power = maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP)); + } + + retTraveller->setAngleTo(angle); + retTraveller->setPowerTo(power); + + // Set this object's position to the new one determined by the power and angle from above + static int lastSuccessful = 0; + int coords = 0; + + if (!(index % NUM_POWER_STEPS) || (!lastSuccessful)) { + coords = simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0); + lastSuccessful = 0; + } else { + completionState = 1; + lastSuccessful = 0; + } + + if (!coords) { + completionFlag = 0; + completionState = 0; + delete retTraveller; + return NULL; + } else { + completionFlag = 1; + completionState = 1; + } + + int whoseTurn = getCurrentPlayer(); + int maxX = getMaxX(); + + // Check new position to see if landing is clear + if (coords > 0) { + int yCoord = coords / maxX; + int xCoord = coords - (yCoord * maxX); + + int terrain = getTerrain(xCoord, yCoord); + assert(terrain == TERRAIN_TYPE_GOOD); + + float pwr = getMinPower() * .3; + float cosine = cos((static_cast<float>(angle) / 360) * (2 * M_PI)); + float sine = sin((static_cast<float>(angle) / 360) * (2 * M_PI)); + int xParam = xCoord + (pwr * cosine); + int yParam = yCoord + (pwr * sine); + + if (xParam < 0) + xParam += getMaxX(); + else if (xParam > getMaxX()) + xParam -= getMaxX(); + + if (yParam < 0) + yParam += getMaxY(); + else if (yParam > getMaxY()) + yParam -= getMaxY(); + + if (checkIfWaterState(xParam, yParam)) { + delete retTraveller; + return NULL; + } + + retTraveller->setPosY(yCoord); + retTraveller->setPosX(xCoord); + + // Iterate through the previous action list, making sure this one isn't on it + for (intVecItr i = (lastXCoord[whoseTurn]).begin(), j = (lastYCoord[whoseTurn]).begin(); i != (lastXCoord[whoseTurn]).end(); i++, j++) { + // Check if this shot is the same as the last time we tried + if ((*i == retTraveller->getPosX()) && (*j == retTraveller->getPosY())) { + retTraveller->setDisabled(); + delete retTraveller; + return NULL; + } + } + + retTraveller->setValueG(getG() + 7 + (dir * DIRECTION_WEIGHT)); + lastSuccessful = 1; + } else { + int yCoord = -coords / maxX; + int xCoord = -coords - (yCoord * maxX); + + // If landing fault is because of water, add 1 extra to g and turn on water flag. Also set coords, and adjust power to water fault location + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - _posX; + int yDist = yCoord - _posY; + retTraveller->setPosX(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1)))); + retTraveller->setPosY(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1)))); + + int closestHub = getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + + retTraveller->setWaterSourceX(getHubX(closestHub)); + retTraveller->setWaterSourceY(getHubY(closestHub)); + retTraveller->setWaterDestX(retTraveller->getPosX()); + retTraveller->setWaterDestY(retTraveller->getPosY()); + + retTraveller->setPowerTo(power); + retTraveller->setAngleTo(angle); + + retTraveller->setValueG(getG() + 10 + (dir * DIRECTION_WEIGHT)); + retTraveller->enableWaterFlag(); + } else { + // If not, set G to highest value + retTraveller->setDisabled(); + delete retTraveller; + return NULL; + } + } + + return retTraveller; +} + +int Traveller::checkSuccess() { + if (getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist) + return SUCCESS; + + return 0; +} + +float Traveller::calcT() { + assert(!_disabled); + + if (_disabled) return FAILURE; + + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_traveller.h b/engines/scumm/he/moonbase/ai_traveller.h new file mode 100644 index 0000000000..63a56d77af --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.h @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TRAVELER_H +#define SCUMM_HE_MOONBASE_AI_TRAVELER_H + +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +const int NUM_TO_GEN = 9; + +const int NUM_POWER_STEPS = 3; +const double SIZE_POWER_STEP = .15; +const int SIZE_ANGLE_STEP = 45; +const int VARIATION_EXTENT = 3; +const int DIRECTION_WEIGHT = 5; + +class Traveller : public IContainedObject { +private: + static int _targetPosX; + static int _targetPosY; + static int _maxDist; + + static int _numToGen; + static int _sizeAngleStep; + + int _sourceHub; + + int _posX; + int _posY; + int _angleTo; + int _powerTo; + + int _disabled; + int _waterFlag; + int _waterSourceX; + int _waterSourceY; + int _waterDestX; + int _waterDestY; + + +protected: + virtual float calcH(); + +public: + Traveller(); + Traveller(int originX, int originY); + ~Traveller() {} + + IContainedObject *duplicate() { return this; } + + static void setTargetPosX(int posX) { _targetPosX = posX; } + static void setTargetPosY(int posY) { _targetPosY = posY; } + static void setMaxDist(int maxDist) { _maxDist = maxDist; } + + void setSourceHub(int sourceHub) { _sourceHub = sourceHub; } + + void setPosX(int posX) { _posX = posX; } + void setPosY(int posY) { _posY = posY; } + void setAngleTo(int angleTo) { _angleTo = angleTo; } + void setPowerTo(int powerTo) { _powerTo = powerTo; } + + void setWaterSourceX(int waterSourceX) { _waterSourceX = waterSourceX; } + void setWaterSourceY(int waterSourceY) { _waterSourceY = waterSourceY; } + + void setWaterDestX(int waterDestX) { _waterDestX = waterDestX; } + void setWaterDestY(int waterDestY) { _waterDestY = waterDestY; } + + int getSourceHub() const { return _sourceHub; } + + int getPosX() const { return _posX; } + int getPosY() const { return _posY; } + int getAngleTo() const { return _angleTo; } + int getPowerTo() const { return _powerTo; } + + int getWaterSourceX() const { return _waterSourceX; } + int getWaterSourceY() const { return _waterSourceY; } + int getWaterDestX() const { return _waterDestX; } + int getWaterDestY() const { return _waterDestY; } + + void setDisabled() { _disabled = 1; } + void unsetDisabled() { _disabled = 0; } + int getDisabled() { return _disabled; } + + void adjustPosX(int offsetX); + void adjustPosY(int offsetY); + void adjustXY(int offsetX, int offsetY); + + void enableWaterFlag() { _waterFlag = 1; } + void disableWaterFlag() { _waterFlag = 0; } + int getWaterFlag() const { return _waterFlag; } + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &); + + virtual int checkSuccess(); + virtual float calcT(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_tree.cpp b/engines/scumm/he/moonbase/ai_tree.cpp new file mode 100644 index 0000000000..2571928074 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.cpp @@ -0,0 +1,237 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/moonbase/ai_tree.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +static int compareTreeNodes(const void *a, const void *b) { + return ((const TreeNode *)a)->value - ((const TreeNode *)b)->value; +} + +Tree::Tree() { + pBaseNode = new Node; + _maxDepth = MAX_DEPTH; + _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); +} + +Tree::Tree(IContainedObject *contents) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = MAX_DEPTH; + _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); +} + +Tree::Tree(IContainedObject *contents, int maxDepth) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = maxDepth; + _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); +} + +Tree::Tree(IContainedObject *contents, int maxDepth, int maxNodes) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = maxDepth; + _maxNodes = maxNodes; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); +} + +void Tree::duplicateTree(Node *sourceNode, Node *destNode) { + Common::Array<Node *> vUnvisited = sourceNode->getChildren(); + + while (vUnvisited.size()) { + Node *newNode = new Node(*(vUnvisited.end())); + newNode->setParent(destNode); + (destNode->getChildren()).push_back(newNode); + duplicateTree(*(vUnvisited.end()), newNode); + vUnvisited.pop_back(); + } +} + +Tree::Tree(const Tree *sourceTree) { + pBaseNode = new Node(sourceTree->getBaseNode()); + _maxDepth = sourceTree->getMaxDepth(); + _maxNodes = sourceTree->getMaxNodes(); + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); + _currentNode = 0; + _currentChildIndex = 0; + + duplicateTree(sourceTree->getBaseNode(), pBaseNode); +} + +Tree::~Tree() { + // Delete all nodes + Node *pNodeItr = pBaseNode; + + // Depth first traversal of nodes to delete them + while (pNodeItr != NULL) { + // If any children are left, move to one of them + if (!(pNodeItr->getChildren().empty())) { + pNodeItr = pNodeItr->popChild(); + } else { + // Delete this node, and move up to the parent for further processing + Node *pTemp = pNodeItr; + pNodeItr = pNodeItr->getParent(); + delete pTemp; + pTemp = NULL; + } + } + + delete _currentMap; +} + +Node *Tree::aStarSearch() { + Common::SortedArray<TreeNode *> mmfpOpen(compareTreeNodes); + + Node *currentNode = NULL; + float currentT; + + Node *retNode = NULL; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + mmfpOpen.insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); + + while (mmfpOpen.size() && (retNode == NULL)) { + currentNode = mmfpOpen.front()->node; + mmfpOpen.erase(mmfpOpen.begin()); + + if ((currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes)) { + // Generate nodes + Common::Array<Node *> vChildren = currentNode->getChildren(); + + for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { + IContainedObject *pTemp = (*i)->getContainedObject(); + currentT = pTemp->calcT(); + + if (currentT == SUCCESS) + retNode = *i; + else + mmfpOpen.insert(new TreeNode(currentT, (*i))); + } + } else { + retNode = currentNode; + } + } + } else { + retNode = pBaseNode; + } + + return retNode; +} + + +Node *Tree::aStarSearch_singlePassInit() { + Node *retNode = NULL; + + _currentChildIndex = 1; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + _currentMap->insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); + } else { + retNode = pBaseNode; + } + + return retNode; +} + +Node *Tree::aStarSearch_singlePass() { + float currentT = 0.0; + Node *retNode = NULL; + + static int maxTime = 0; + + if (_currentChildIndex == 1) { + maxTime = getPlayerMaxTime(); + } + + if (_currentChildIndex) { + if (!(_currentMap->size())) { + retNode = _currentNode; + return retNode; + } + + _currentNode = _currentMap->front()->node; + _currentMap->erase(_currentMap->begin()); + } + + if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (getTimerValue(3) < maxTime))) { + // Generate nodes + _currentChildIndex = _currentNode->generateChildren(); + + if (_currentChildIndex) { + Common::Array<Node *> vChildren = _currentNode->getChildren(); + + if (!vChildren.size() && !_currentMap->size()) { + _currentChildIndex = 0; + retNode = _currentNode; + } + + for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { + IContainedObject *pTemp = (*i)->getContainedObject(); + currentT = pTemp->calcT(); + + if (currentT == SUCCESS) { + retNode = *i; + i = vChildren.end() - 1; + } else { + _currentMap->insert(new TreeNode(currentT, (*i))); + } + } + + if (!(_currentMap->size()) && (currentT != SUCCESS)) { + assert(_currentNode != NULL); + retNode = _currentNode; + } + } + } else { + retNode = _currentNode; + } + + return retNode; +} + +int Tree::IsBaseNode(Node *thisNode) { + return (thisNode == pBaseNode); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_tree.h b/engines/scumm/he/moonbase/ai_tree.h new file mode 100644 index 0000000000..5da5b3a920 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TREE_H +#define SCUMM_HE_MOONBASE_AI_TREE_H + +#include "common/array.h" +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +const int MAX_DEPTH = 100; +const int MAX_NODES = 1000000; + +struct TreeNode { + float value; + Node *node; + + TreeNode(float v, Node *n) { value = v; node = n; } +}; + +class Tree { +private: + Node *pBaseNode; + + int _maxDepth; + int _maxNodes; + + int _currentChildIndex; + + Common::SortedArray<TreeNode *> *_currentMap; + Node *_currentNode; + +public: + Tree(); + Tree(IContainedObject *contents); + Tree(IContainedObject *contents, int maxDepth); + Tree(IContainedObject *contents, int maxDepth, int maxNodes); + Tree(const Tree *sourceTree); + ~Tree(); + + void duplicateTree(Node *sourceNode, Node *destNode); + + Node *getBaseNode() const { return pBaseNode; } + void setMaxDepth(int maxDepth) { _maxDepth = maxDepth; } + int getMaxDepth() const { return _maxDepth; } + + void setMaxNodes(int maxNodes) { _maxNodes = maxNodes; } + int getMaxNodes() const { return _maxNodes; } + + Node *aStarSearch(); + + Node *aStarSearch_singlePassInit(); + Node *aStarSearch_singlePass(); + + int IsBaseNode(Node *thisNode); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_types.cpp b/engines/scumm/he/moonbase/ai_types.cpp new file mode 100644 index 0000000000..e134f5ee12 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.cpp @@ -0,0 +1,176 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/textconsole.h" +#include "scumm/he/moonbase/ai_types.h" + +namespace Scumm { + +AIEntity::AIEntity(int id) { + switch (id) { + default: + case BRUTAKAS: + warning("BRUTAKAS"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "BRUTAKAS"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case AGI: + warning("Agi"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Agi"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case EL_GATO: + warning("El Gato de la Noche"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "El Gato de la Noche"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_MEDIUM; + break; + + case PIXELAHT: + warning("Pixelaht"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Pixelaht"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_SMALL; + break; + + case CYBALL: + warning("cYbaLL"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "cYbaLL"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case NEEP: + warning("Neep! Neep!"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Neep! Neep!"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_LARGE; + break; + + case WARCUPINE: + warning("WARcupine"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "WARcupine"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_LARGE; + _powerVariation = AI_VAR_MEDIUM; + break; + + case AONE: + warning("aone"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "aone"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_MEDIUM; + break; + + case SPANDO: + warning("S p a n d o"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "S p a n d o"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case ORBNU_LUNATEK: + warning("Bonur J Lunatek"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Bonur J Lunatek"); + _behaviorVariation = AI_VAR_HUGE; + _targetVariation = AI_VAR_HUGE; + _angleVariation = AI_VAR_HUGE; + _powerVariation = AI_VAR_HUGE; + break; + + case CRAWLER_CHUCKER: + warning("Le Chuckre des Crawlres"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Le Chuckre des Crawlres"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case ENERGY_HOG: + warning("Energy Hog"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Energy Hog\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case RANGER: + warning("Ranger"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Ranger\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_types.h b/engines/scumm/he/moonbase/ai_types.h new file mode 100644 index 0000000000..e2de87d653 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TYPES_H +#define SCUMM_HE_MOONBASE_AI_TYPES_H + +namespace Scumm { + +enum { + AGI = 1, + AONE = 2, + BRUTAKAS = 3, + CYBALL = 4, + EL_GATO = 5, + NEEP = 6, + ORBNU_LUNATEK = 7, + PIXELAHT = 8, + SPANDO = 9, + WARCUPINE = 10 +}; + +enum { + CRAWLER_CHUCKER = 11, + ENERGY_HOG = 12, + RANGER = 13 +}; + +enum { + AI_VAR_NONE = -1, + AI_VAR_SMALL = 0, + AI_VAR_MEDIUM = 1, + AI_VAR_LARGE = 2, + AI_VAR_HUGE = 5 +}; + +enum { + AI_VAR_BASE_BEHAVIOR = 10, + AI_VAR_BASE_TARGET = 10, + AI_VAR_BASE_ANGLE = 2, + AI_VAR_BASE_POWER = 5 +}; + +class AIEntity { +private: + int _id; + char *_nameString; + int _behaviorVariation; + int _targetVariation; + int _angleVariation; + int _powerVariation; + +public: + AIEntity(int id); + ~AIEntity() { + if (_nameString) { + delete _nameString; + _nameString = 0; + } + } + + int getID() const { return _id; } + char *getNameString() const { return _nameString; } + int getBehaviorVariation() const { return _behaviorVariation; } + int getTargetVariation() const { return _targetVariation; } + int getAngleVariation() const { return _angleVariation; } + int getPowerVariation() const { return _powerVariation; } + + void setID(int id) { _id = id; } + void setNameString(char *nameString) { _nameString = nameString; } + void setBehaviorVariation(int behaviorVariation) { _behaviorVariation = behaviorVariation; } + void setTargetVariation(int targetVariation) { _targetVariation = targetVariation; } + void setAngleVariation(int angleVariation) { _angleVariation = angleVariation; } + void setPowerVariation(int powerVariation) { _powerVariation = powerVariation; } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_weapon.cpp b/engines/scumm/he/moonbase/ai_weapon.cpp new file mode 100644 index 0000000000..ba50aae4d1 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.cpp @@ -0,0 +1,88 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "scumm/he/moonbase/ai_weapon.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +Weapon::Weapon(int typeID) { //, float damage, int radius) + switch (typeID) { + default: + case ITEM_BOMB: + becomeBomb(); + break; + + case ITEM_CLUSTER: + becomeCluster(); + break; + + case ITEM_CRAWLER: + becomeCrawler(); + break; + + case ITEM_EMP: + becomeEMP(); + break; + + case ITEM_SPIKE: + becomeSpike(); + break; + } +} + +void Weapon::becomeBomb() { + _typeID = ITEM_BOMB; + _damage = 3; + _radius = 30; + _cost = 1; +} + +void Weapon::becomeCluster() { + _typeID = ITEM_CLUSTER; + _damage = 1.5; + _radius = 20; + _cost = 1; +} + +void Weapon::becomeCrawler() { + _typeID = ITEM_CRAWLER; + _damage = 4; + _radius = 180; + _cost = 7; +} + +void Weapon::becomeEMP() { + _typeID = ITEM_EMP; + _damage = .1f; + _radius = 215; + _cost = 3; +} + +void Weapon::becomeSpike() { + _typeID = ITEM_SPIKE; + _damage = 6; + _radius = 180; + _cost = 3; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_weapon.h b/engines/scumm/he/moonbase/ai_weapon.h new file mode 100644 index 0000000000..55c710ccdf --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.h @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_WEAPON_H +#define SCUMM_HE_MOONBASE_AI_WEAPON_H + +namespace Scumm { + +class Weapon { +private: + int _typeID; + float _damage; + int _radius; + int _cost; + +public: + Weapon() {} + Weapon(int typeID); + virtual ~Weapon() {} + + void setTypeID(int typeID) { _typeID = typeID; } + void setDamage(float damage) { _damage = damage; } + void setRadius(int radius) { _radius = radius; } + void setCost(int cost) { _cost = cost; } + + int getTypeID() { return _typeID; } + float getDamage() { return _damage; } + int getRadius() { return _radius; } + int getCost() { return _cost; } + + void becomeBomb(); + void becomeCluster(); + void becomeCrawler(); + void becomeEMP(); + void becomeSpike(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp index 1f9d843bb9..cc25c270e4 100644 --- a/engines/scumm/he/moonbase/moonbase.cpp +++ b/engines/scumm/he/moonbase/moonbase.cpp @@ -21,24 +21,45 @@ */ #include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" namespace Scumm { -Moonbase::Moonbase() { - _fowSentinelImage = -1; - _fowSentinelState = -1; - _fowSentinelConditionBits = 0; +Moonbase::Moonbase(ScummEngine_v71he *vm) : _vm(vm) { + initFOW(); } Moonbase::~Moonbase() { } -void Moonbase::renderFOW() { - warning("STUB: renderFOW()"); +int Moonbase::readFromArray(int array, int y, int x) { + _vm->VAR(((ScummEngine_v90he *)_vm)->VAR_U32_ARRAY_UNK) = array; + + return _vm->readArray(116, y, x); } +int Moonbase::callScummFunction(int scriptNumber, int paramCount,...) { + va_list va_params; + va_start(va_params, paramCount); + int args[25]; + + for (int i = 0; i < paramCount; i++) + args[i] = va_arg(va_params, int); + + va_end(va_params); + + _vm->runScript(scriptNumber, 0, 1, args); + + return _vm->pop(); +} + + void Moonbase::blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, uint8 *wizd, int x, int y, int rawROP, int paramROP) { + bool premulAlpa = false; + + if (rawROP == 1) + premulAlpa = true; Common::Rect clippedDstRect(dstw, dsth); if (clipBox) { @@ -89,22 +110,41 @@ void Moonbase::blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, con int code = *codes - 2; codes++; - if (code == 0) { // quad - for (int c = 0; c < 4; c++) { + if (code <= 0) { // quad or single + uint8 *src; + int cnt; + if (code == 0) { // quad + src = quadsOffset; + quadsOffset += 8; + cnt = 4; // 4 pixels + } else { // single + src = singlesOffset; + singlesOffset += 2; + cnt = 1; + } + + for (int c = 0; c < cnt; c++) { if (pixels >= sx) { - WRITE_LE_UINT16(dst1, READ_LE_UINT16(quadsOffset)); + if (rawROP == 1) { // MMX_PREMUL_ALPHA_COPY + WRITE_LE_UINT16(dst1, READ_LE_UINT16(src)); + } else if (rawROP == 2) { // MMX_ADDITIVE + uint16 color = READ_LE_UINT16(src); + uint16 orig = READ_LE_UINT16(dst1); + + uint32 r = MIN<uint32>(0x7c00, (orig & 0x7c00) + (color & 0x7c00)); + uint32 g = MIN<uint32>(0x03e0, (orig & 0x03e0) + (color & 0x03e0)); + uint32 b = MIN<uint32>(0x001f, (orig & 0x001f) + (color & 0x001f)); + WRITE_LE_UINT16(dst1, (r | g | b)); + } else if (rawROP == 5) { // MMX_CHEAP_50_50 + uint16 color = (READ_LE_UINT16(src) >> 1) & 0x3DEF; + uint16 orig = (READ_LE_UINT16(dst1) >> 1) & 0x3DEF; + WRITE_LE_UINT16(dst1, (color + orig)); + } dst1 += 2; } - quadsOffset += 2; + src += 2; pixels++; } - } else if (code < 0) { // single - if (pixels >= sx) { - WRITE_LE_UINT16(dst1, READ_LE_UINT16(singlesOffset)); - dst1 += 2; - } - singlesOffset += 2; - pixels++; } else { // skip if ((code & 1) == 0) { code >>= 1; @@ -120,25 +160,26 @@ void Moonbase::blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, con uint16 color = READ_LE_UINT16(singlesOffset); uint32 orig = READ_LE_UINT16(dst1); - //WRITE_LE_UINT16(dst1, color); // ENABLE_PREMUL_ALPHA = 0 - // ENABLE_PREMUL_ALPHA = 2 - if (alpha > 32) { - alpha -= 32; - - uint32 oR = orig & 0x7c00; - uint32 oG = orig & 0x03e0; - uint32 oB = orig & 0x1f; - uint32 dR = ((((color & 0x7c00) - oR) * alpha) >> 5) + oR; - uint32 dG = ((((color & 0x3e0) - oG) * alpha) >> 5) + oG; - uint32 dB = ((((color & 0x1f) - oB) * alpha) >> 5) + oB; - - WRITE_LE_UINT16(dst1, (dR & 0x7c00) | (dG & 0x3e0) | (dB & 0x1f)); + if (!premulAlpa) { + WRITE_LE_UINT16(dst1, color); // ENABLE_PREMUL_ALPHA = 0 } else { - uint32 pix = ((orig << 16) | orig) & 0x3e07c1f; - pix = (((pix * alpha) & 0xffffffff) >> 5) & 0x3e07c1f; - pix = ((pix << 16) + pix + color) & 0xffff; - - WRITE_LE_UINT16(dst1, pix); + if (alpha > 32) { + alpha -= 32; + + uint32 oR = orig & 0x7c00; + uint32 oG = orig & 0x03e0; + uint32 oB = orig & 0x1f; + uint32 dR = ((((color & 0x7c00) - oR) * alpha) >> 5) + oR; + uint32 dG = ((((color & 0x3e0) - oG) * alpha) >> 5) + oG; + uint32 dB = ((((color & 0x1f) - oB) * alpha) >> 5) + oB; + + WRITE_LE_UINT16(dst1, (dR & 0x7c00) | (dG & 0x3e0) | (dB & 0x1f)); + } else { + uint32 pix = ((orig << 16) | orig) & 0x3e07c1f; + pix = (((pix * alpha) & 0xffffffff) >> 5) & 0x3e07c1f; + pix = ((pix >> 16) + pix + color) & 0xffff; + WRITE_LE_UINT16(dst1, pix); + } } dst1 += 2; diff --git a/engines/scumm/he/moonbase/moonbase.h b/engines/scumm/he/moonbase/moonbase.h index 7ff2f17070..7d93661dec 100644 --- a/engines/scumm/he/moonbase/moonbase.h +++ b/engines/scumm/he/moonbase/moonbase.h @@ -20,31 +20,87 @@ * */ -#ifndef SCUMM_HE_MOONBASE_H -#define SCUMM_HE_MOONBASE_H +#ifndef SCUMM_HE_MOONBASE_MOONBASE_H +#define SCUMM_HE_MOONBASE_MOONBASE_H #ifdef ENABLE_HE +#include "common/winexe_pe.h" + namespace Scumm { class Moonbase { public: - Moonbase(); + Moonbase(ScummEngine_v71he *vm); ~Moonbase(); - void renderFOW(); + int readFromArray(int array, int y, int x); + int callScummFunction(int scriptNumber, int paramCount,...); void blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, uint8 *wizd, int srcx, int srcy, int rawROP, int paramROP); + // FOW Stuff + bool isFOW(int resNum, int state, uint32 conditionBits) { + return resNum == _fowSentinelImage && state == _fowSentinelState && conditionBits == _fowSentinelConditionBits; + } + + void initFOW(); + void releaseFOWResources(); + + bool setFOWImage(int id); + + void setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1, + int clipY1, int clipX2, int clipY2, int technique, int nFrame); + + + void renderFOW(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int flags); + +private: + int readFOWVisibilityArray(int array, int y, int x); + void renderFOWState(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int x, int y, int srcw, int srch, int state, int flags); + public: int _fowSentinelImage; int _fowSentinelState; - uint16 _fowSentinelConditionBits; -}; + uint32 _fowSentinelConditionBits; + +private: + ScummEngine_v71he *_vm; -#endif + int _fowFrameBaseNumber; + int _fowAnimationFrames; + int _fowCurrentFOWFrame; + + int32 _fowTileW; + int32 _fowTileH; + + uint8 *_fowImage; + int _fowClipX1; + int _fowClipY1; + int _fowClipX2; + int _fowClipY2; + + int _fowDrawX; + int _fowDrawY; + + int _fowVtx1; + int _fowVty1; + int _fowMvx; + int _fowMvy; + int _fowVw; + int _fowVh; + + bool _fowBlackMode; + + int _fowRenderTable[32768]; + + Common::PEResources _exe; + Common::String _fileName; +}; } // End of namespace Scumm -#endif +#endif // ENABLE_HE + +#endif // SCUMM_HE_MOONBASE_H diff --git a/engines/scumm/he/moonbase/moonbase_fow.cpp b/engines/scumm/he/moonbase/moonbase_fow.cpp new file mode 100644 index 0000000000..48c2219926 --- /dev/null +++ b/engines/scumm/he/moonbase/moonbase_fow.cpp @@ -0,0 +1,415 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/config-manager.h" + +#include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" + +namespace Scumm { + +#define FOW_ANIM_FRAME_COUNT 38 + +void Moonbase::initFOW() { + _fowSentinelImage = -1; + _fowSentinelState = -1; + _fowSentinelConditionBits = 0; + + _fowFrameBaseNumber = 0; + _fowAnimationFrames = 1; + _fowCurrentFOWFrame = 0; + + _fowTileW = 0; + _fowTileH = 0; + + _fowImage = nullptr; + _fowClipX1 = 0; + _fowClipY1 = 0; + _fowClipX2 = 0; + _fowClipY2 = 0; + + _fowDrawX = 0; + _fowDrawY = 0; + + _fowVtx1 = 0; + _fowVty1 = 0; + _fowMvx = 0; + _fowMvy = 0; + _fowVw = 0; + _fowVh = 0; + + _fowBlackMode = true; + + memset(_fowRenderTable, 0, 32768); +} + +void Moonbase::releaseFOWResources() { + if (_fowImage) { + free(_fowImage); + _fowImage = 0; + } +} + +bool Moonbase::setFOWImage(int image) { + releaseFOWResources(); + + if (!_fowImage) { + Common::String fowImageFilename(ConfMan.get("MOONX_FOWImageFilename").c_str()); + +#if 0 // TODO + if (!fowImageFilename.empty()) { + void *wiz = loadWizFromFilename(fowImageFilename); + + if (wiz) { + captureFOWImageFromLocation(wiz, file.size()); + free(wiz); + } + } +#endif + + if (!_fowImage && image < 0) { + int resId; + + // PIECES BUBBLES CIRCLES SIMPLE* WEDGEY BUBBLE2 + // WEDGE2 SPIKEY ANGLES SMOOTHED WUZZY SYS7-BEVELED + if (image >= -12 && image <= -1) + resId = 210 - image; // 211-222 range + else + resId = 214; // default, SIMPLE + + if (_fileName.empty()) { // We are running for the first time + _fileName = _vm->generateFilename(-3); + + if (!_exe.loadFromEXE(_fileName)) + error("Cannot open file %s", _fileName.c_str()); + } + + Common::SeekableReadStream *stream = _exe.getResource(Common::kPERCData, resId); + + if (stream->size()) { + _fowImage = (uint8 *)malloc(stream->size()); + + stream->read(_fowImage, stream->size()); + } + + delete stream; + } + + if (!_fowImage && image > 0) + _fowImage = _vm->getResourceAddress(rtImage, image); + + if (!_fowImage) + return false; + } + + int nStates = _vm->_wiz->getWizImageStates(_fowImage); + + if (nStates > FOW_ANIM_FRAME_COUNT) { + releaseFOWResources(); + return false; + } + + _fowAnimationFrames = (nStates + FOW_ANIM_FRAME_COUNT - 1) / FOW_ANIM_FRAME_COUNT; + + _vm->_wiz->getWizImageDim(_fowImage, (nStates - 1), _fowTileW, _fowTileH); + _fowBlackMode = !_vm->_wiz->isWizPixelNonTransparent(_fowImage, nStates - 1, 0, 0, 0); + + if (ConfMan.hasKey("EnableFOWRects")) + _fowBlackMode = (ConfMan.getInt("EnableFOWRects") == 1); + + return true; +} + +enum FOWElement { + FOW_EMPTY = 0, + FOW_SOLID = 1, + + FF_L = 0x01, + FF_R = 0x02, + FF_T = 0x04, + FF_B = 0x08, + FF_T_L = 0x10, + FF_T_R = 0x20, + FF_B_L = 0x40, + FF_B_R = 0x80, + FF_Q_A = (FF_L | FF_T | FF_T_L), + FF_Q_B = (FF_R | FF_T | FF_T_R), + FF_Q_C = (FF_L | FF_B | FF_B_L), + FF_Q_D = (FF_R | FF_B | FF_B_R) +}; + +int Moonbase::readFOWVisibilityArray(int array, int y, int x) { + if (readFromArray(array, y, x) > 0) + return FOW_EMPTY; + + return FOW_SOLID; +} + +void Moonbase::setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1, + int clipY1, int clipX2, int clipY2, int technique, int nFrame) { + if (!_fowImage) + return; + + _fowDrawX = clipX1; + _fowDrawY = clipY1; + + _fowClipX1 = clipX1; + _fowClipY1 = clipY1; + _fowClipX2 = clipX2; + _fowClipY2 = clipY2; + + // Figure out the number of tiles are involved + int view_W = (clipX2 - clipX1) + 1; + int view_H = (clipY2 - clipY1) + 1; + + int tw = _fowTileW; + int th = _fowTileH; + + int dw = acrossDim; + int dh = downDim; + + int dlw = dw * tw; + int dlh = dh * th; + + _fowMvx = (0 <= viewX) ? (viewX % dlw) : (dlw - (-viewX % dlw)); + _fowMvy = (0 <= viewY) ? (viewY % dlh) : (dlh - (-viewY % dlh)); + + _fowVtx1 = _fowMvx / tw; + _fowVty1 = _fowMvy / th; + + _fowVw = (((_fowMvx + view_W + tw - 1) / tw) - _fowVtx1) + 1; + _fowVh = (((_fowMvy + view_H + th - 1) / th) - _fowVty1) + 1; + + // Build the connectivity table + int t = (_fowVty1 - 1); if (t >= dh) { t = 0; } else if (t < 0) { t = (dh - 1); } + int m = (_fowVty1 + 0); if (m >= dh) { m = 0; } else if (m < 0) { m = (dh - 1); } + int b = (_fowVty1 + 1); if (b >= dh) { b = 0; } else if (b < 0) { b = (dh - 1); } + + int il = (_fowVtx1 - 1); if (il >= dh) { il = 0; } else if (il < 0) { il = (dw - 1); } + int ic = (_fowVtx1 + 0); if (ic >= dh) { ic = 0; } else if (ic < 0) { ic = (dw - 1); } + int ir = (_fowVtx1 + 1); if (ir >= dh) { ir = 0; } else if (ir < 0) { ir = (dw - 1); } + + int dataOffset = (_fowVw * 3); + int dataOffset2 = (dataOffset * 2); + int *pOutterRenderTableA = _fowRenderTable; + int *pOutterRenderTableB = pOutterRenderTableA + dataOffset; + + for (int ay = 0; ay < _fowVh; ay++) { + int l = il; + int c = ic; + int r = ir; + + int *pRenderTableA = pOutterRenderTableA; + int *pRenderTableB = pOutterRenderTableB; + + pOutterRenderTableA += dataOffset2; + pOutterRenderTableB += dataOffset2; + + for (int ax = 0; ax < _fowVw; ax++) { + int visibility = readFOWVisibilityArray(fowInfoArray, m, c); + + if (visibility == FOW_EMPTY) { + int bits = 0; + + if (readFOWVisibilityArray(fowInfoArray, t, l) != 0) bits |= FF_T_L; + if (readFOWVisibilityArray(fowInfoArray, t, c) != 0) bits |= FF_T; + if (readFOWVisibilityArray(fowInfoArray, t, r) != 0) bits |= FF_T_R; + if (readFOWVisibilityArray(fowInfoArray, m, l) != 0) bits |= FF_L; + if (readFOWVisibilityArray(fowInfoArray, m, r) != 0) bits |= FF_R; + if (readFOWVisibilityArray(fowInfoArray, b, l) != 0) bits |= FF_B_L; + if (readFOWVisibilityArray(fowInfoArray, b, c) != 0) bits |= FF_B; + if (readFOWVisibilityArray(fowInfoArray, b, r) != 0) bits |= FF_B_R; + + if (bits) { + *pRenderTableA++ = 1; + *pRenderTableB++ = 1; + + // Quadrant (A) + if (bits & FF_Q_A) { + *pRenderTableA++ = ( + ((FF_L & bits) ? 1 : 0) | + ((FF_T & bits) ? 2 : 0) | + ((FF_T_L & bits) ? 4 : 0) + ) + 0; + } else { + *pRenderTableA++ = 0; + } + + // Quadrant (B) + if (bits & FF_Q_B) { + *pRenderTableA++ = ( + ((FF_R & bits) ? 1 : 0) | + ((FF_T & bits) ? 2 : 0) | + ((FF_T_R & bits) ? 4 : 0) + ) + 8; + } else { + *pRenderTableA++ = 0; + } + + // Quadrant (C) + if (bits & FF_Q_C) { + *pRenderTableB++ = ( + ((FF_L & bits) ? 1 : 0) | + ((FF_B & bits) ? 2 : 0) | + ((FF_B_L & bits) ? 4 : 0) + ) + 16; + } else { + *pRenderTableB++ = 0; + } + + // Quadrant (D) + if (bits & FF_Q_D) { + *pRenderTableB++ = ( + ((FF_R & bits) ? 1 : 0) | + ((FF_B & bits) ? 2 : 0) | + ((FF_B_R & bits) ? 4 : 0) + ) + 24; + } else { + *pRenderTableB++ = 0; + } + } else { + *pRenderTableA++ = 0; + *pRenderTableB++ = 0; + } + } else { + if (_fowBlackMode) { + *pRenderTableA++ = 2; + *pRenderTableB++ = 2; + } else { + *pRenderTableA++ = 1; + *pRenderTableA++ = 33; + *pRenderTableA++ = 34; + + *pRenderTableB++ = 1; + *pRenderTableB++ = 35; + *pRenderTableB++ = 36; + } + } + + if (++l >= dw) { l = 0; } + if (++c >= dw) { c = 0; } + if (++r >= dw) { r = 0; } + } + + if (++t >= dh) { t = 0; } + if (++m >= dh) { m = 0; } + if (++b >= dh) { b = 0; } + } + + _fowCurrentFOWFrame = (nFrame >= 0) ? (nFrame % _fowAnimationFrames) : ((-nFrame) % _fowAnimationFrames); + _fowFrameBaseNumber = (_fowCurrentFOWFrame * FOW_ANIM_FRAME_COUNT); +} + +void Moonbase::renderFOWState(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int x, int y, int srcw, int srch, int state, int flags) { + int32 spotx, spoty; + + _vm->_wiz->getWizImageSpot(_fowImage, state, spotx, spoty); + Common::Rect r(_fowClipX1, _fowClipY1, _fowClipX2, _fowClipY2); + + _vm->_wiz->drawWizImageEx(destSurface, _fowImage, 0, dstPitch, dstType, dstw, dsth, x - spotx, y - spoty, srcw, srch, state, &r, flags, 0, 0, 16, 0, 0); +} + +static void blackRect_16bpp(uint8 *destSurface, int dstPitch, int dstw, int dsth, int x1, int y1, int x2, int y2) { + byte *dst = destSurface + dstPitch * y1 + x1 * 2; + int h = y2 - y1; + int w = ((x2 - x1) + 1) * 2; + + while ( --h >= 0 ) { + memset(dst, 0, w); + dst += dstPitch; + } +} + +void Moonbase::renderFOW(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int flags) { + if (!_fowImage) + return; + + const int *pOutterRenderTable = _fowRenderTable; + int ixPos = ((_fowVtx1 * _fowTileW) - _fowMvx) + _fowDrawX; + int yPos = ((_fowVty1 * _fowTileH) - _fowMvy) + _fowDrawY; + int dataOffset = _fowVw * 3; + int halfTileHeight = _fowTileH / 2; + int cx2 = MIN(_fowClipX2, (dstw - 1)); + int cy2 = MIN(_fowClipY2, (dsth - 1)); + + for (int ry = 0; ry < _fowVh; ry++) { + int real_yPos = yPos; + + for (int i = 0; i < 2; i++) { + const int *pRenderTable = pOutterRenderTable; + pOutterRenderTable += dataOffset; + + int xPos = ixPos; + + for (int rx = 0; rx < _fowVw; rx++) { + int nState = *pRenderTable++; + + if (nState != 0) { + if (nState == 2) { + int countLeft = (_fowVw - rx); + int count = 0; + + for (; count < countLeft; count++) { + if (*(pRenderTable + count) != 2) + break; + + pRenderTable++; + rx++; + } + count++; + + int x1 = xPos; + int y1 = real_yPos; + + xPos += _fowTileW * count; + int x2 = (xPos - 1); + int y2 = ((y1 + halfTileHeight) - 1); + + x1 = MAX(0, x1); + y1 = MAX(0, y1); + x2 = MIN(x2, cx2); + y2 = MIN(y2, cy2); + + if ((x2 >= x1) && (y2 >= y1) && (x1 <= _fowClipX2) && (y1 <= _fowClipY2)) + blackRect_16bpp(destSurface, dstPitch, dstw, dsth, x1, y1, x2, y2); + } else { + int subState; + + if ((subState = *pRenderTable++) != 0) + renderFOWState(destSurface, dstPitch, dstType, dstw, dsth, xPos, yPos, _fowTileW, _fowTileH, (subState + _fowFrameBaseNumber), flags); + + if ((subState = *pRenderTable++) != 0) + renderFOWState(destSurface, dstPitch, dstType, dstw, dsth, xPos, yPos, _fowTileW, _fowTileH, (subState + _fowFrameBaseNumber), flags); + + xPos += _fowTileW; + } + } else { + xPos += _fowTileW; + } + } + real_yPos += halfTileHeight; + } + yPos += _fowTileH; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index 7d56138247..cd807f06b8 100644 --- a/engines/scumm/he/script_v100he.cpp +++ b/engines/scumm/he/script_v100he.cpp @@ -549,20 +549,20 @@ void ScummEngine_v100he::o100_arrayOps() { debug(9,"o100_arrayOps: array %d case %d", array, subOp); switch (subOp) { - case 35: + case 35: // SO_FORMATTED_STRING decodeScriptString(string); len = resStrLen(string); data = defineArray(array, kStringArray, 0, 0, 0, len); memcpy(data, string, len); break; - case 77: // SO_ASSIGN_STRING + case 77: // SO_STRING copyScriptString(string, sizeof(string)); len = resStrLen(string); data = defineArray(array, kStringArray, 0, 0, 0, len); memcpy(data, string, len); break; - case 128: // SO_ASSIGN_2DIM_LIST + case 128: // SO_ASSIGN_2DIM_LIST len = getStackList(list, ARRAYSIZE(list)); id = readVar(array); if (id == 0) @@ -572,7 +572,7 @@ void ScummEngine_v100he::o100_arrayOps() { writeArray(array, c, len, list[len]); } break; - case 129: // SO_ASSIGN_INT_LIST + case 129: // SO_ASSIGN_INT_LIST b = pop(); c = pop(); id = readVar(array); @@ -583,7 +583,7 @@ void ScummEngine_v100he::o100_arrayOps() { writeArray(array, 0, b + c, pop()); } break; - case 130: + case 130: // len = getStackList(list, ARRAYSIZE(list)); dim1end = pop(); dim1start = pop(); @@ -607,7 +607,7 @@ void ScummEngine_v100he::o100_arrayOps() { dim2start++; } break; - case 131: + case 131: // SO_COMPLEX_ARRAY_COPY_OPERATION { int a2_dim1end = pop(); int a2_dim1start = pop(); @@ -624,44 +624,76 @@ void ScummEngine_v100he::o100_arrayOps() { copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end); } break; - case 132: - // TODO: Used by room 2 script 2180 in Moonbase Commander - fetchScriptWord(); - fetchScriptWord(); - type = pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - dim1end = pop(); - dim1start = pop(); - dim2end = pop(); - dim2start = pop(); - id = readVar(array); - if (id == 0) { - defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end); - } - switch (type) { - case 1: - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 5: + case 132: // SO_COMPLEX_ARRAY_MATH_OPERATION + { + // Used by room 2 script 2180 in Moonbase Commander (modify-line-of-sight) + int array2 = fetchScriptWord(); + int array1 = fetchScriptWord(); + type = pop(); + int a1_dim1end = pop(); + int a1_dim1start = pop(); + int a1_dim2end = pop(); + int a1_dim2start = pop(); + int a2_dim1end = pop(); + int a2_dim1start = pop(); + int a2_dim2end = pop(); + int a2_dim2start = pop(); + dim1end = pop(); + dim1start = pop(); + dim2end = pop(); + dim2start = pop(); + + int a12_num = a1_dim2end - a1_dim2start + 1; + int a11_num = a1_dim1end - a1_dim1start + 1; + int a22_num = a2_dim2end - a2_dim2start + 1; + int a21_num = a2_dim1end - a2_dim1start + 1; + int d12_num = dim2end - dim2start + 1; + int d11_num = dim1end - dim1start + 1; + + id = readVar(array); + if (id == 0) { + defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end); + } + if (a12_num != a22_num || a12_num != d12_num || a11_num != a21_num || a11_num != d11_num) { + error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num); + } + + for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start, ++dim2start) { + int a2dim1 = a2_dim1start; + int a1dim1 = a1_dim1start; + int dim1 = dim1start; + for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1, ++dim1) { + int val1 = readArray(array1, a1_dim2start, a1dim1); + int val2 = readArray(array2, a2_dim2start, a2dim1); + int res; + + switch (type) { + case 1: // Addition + res = val2 + val1; + break; + case 2: // Subtraction + res = val2 - val1; + break; + case 3: // Binary AND + res = val2 & val1; + break; + case 4: // Binary OR + res = val2 | val1; + break; + case 5: // Binary XOR + res = val2 ^ val1; + break; + default: + error("o100_arrayOps: case 132 unknown type %d)", type); + } + writeArray(array, dim2start, dim1, res); + } + } + + warning("STUB: o100_arrayOps: case 132 type %d", type); break; - default: - error("o100_arrayOps: case 132 unknown type %d)", type); } - debug(0, "o100_arrayOps: case 132 type %d", type); - break; - case 133: + case 133: // SO_RANGE_ARRAY_ASSIGNMENT b = pop(); c = pop(); dim1end = pop(); @@ -910,10 +942,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT _curSpriteGroupId = pop(); break; - case 6: + case 6: // SO_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -921,7 +953,7 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupPosition(_curSpriteGroupId, value1, value2); break; - case 18: + case 18: // SO_CLIPPED value4 = pop(); value3 = pop(); value2 = pop(); @@ -931,10 +963,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4); break; - case 38: + case 38: // SO_GROUP type = pop() - 1; switch (type) { - case 0: + case 0: // SPRGRPOP_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -942,48 +974,48 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2); break; - case 1: + case 1: // SPRGRPOP_ORDER value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersPriority(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPOP_NEW_GROUP value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersGroup(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPOP_UPDATE_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1); break; - case 4: + case 4: // SPRGRPOP_NEW if (!_curSpriteGroupId) break; _sprite->setGroupMembersResetSprite(_curSpriteGroupId); break; - case 5: + case 5: // SPRGRPOP_ANIMATION_SPEED value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1); break; - case 6: + case 6: // SPRGRPOP_ANIMATION_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1); break; - case 7: + case 7: // SPRGRPOP_SHADOW value1 = pop(); if (!_curSpriteGroupId) break; @@ -994,14 +1026,14 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { error("o100_setSpriteGroupInfo subOp 38: Unknown case %d", subOp); } break; - case 40: + case 40: // SO_IMAGE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupImage(_curSpriteGroupId, value1); break; - case 49: + case 49: // SO_AT value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -1009,51 +1041,51 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroup(_curSpriteGroupId, value1, value2); break; - case 52: + case 52: // SO_NAME copyScriptString(string, sizeof(string)); break; - case 53: + case 53: // SO_NEW if (!_curSpriteGroupId) break; _sprite->resetGroup(_curSpriteGroupId); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY // dummy case pop(); pop(); break; - case 59: + case 59: // SO_PRIORITY value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupPriority(_curSpriteGroupId, value1); break; - case 60: + case 60: // SO_PROPERTY type = pop(); value1 = pop(); if (!_curSpriteGroupId) break; switch (type) { - case 0: + case 0: // SPRGRPPROP_XMUL _sprite->setGroupXMul(_curSpriteGroupId, value1); break; - case 1: + case 1: // SPRGRPPROP_XDIV _sprite->setGroupXDiv(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPPROP_YMUL _sprite->setGroupYMul(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPPROP_YDIV _sprite->setGroupYDiv(_curSpriteGroupId, value1); break; default: error("o100_setSpriteGroupInfo subOp 60: Unknown case %d", subOp); } break; - case 89: + case 89: // SO_NEVER_ZCLIP if (!_curSpriteGroupId) break; @@ -1110,6 +1142,7 @@ void ScummEngine_v100he::o100_resourceRoutines() { break; case 128: // TODO: Clear Heap + warning("STUB: o100_resourceRoutines: clear Heap"); break; case 129: // Dummy case @@ -1781,14 +1814,14 @@ void ScummEngine_v100he::o100_setSpriteInfo() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT _curMaxSpriteId = pop(); _curSpriteId = pop(); if (_curSpriteId > _curMaxSpriteId) SWAP(_curSpriteId, _curMaxSpriteId); break; - case 2: + case 2: // SO_ANGLE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1799,7 +1832,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteAngle(spriteId, args[0]); break; - case 3: + case 3: // SO_ANIMATION args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1810,7 +1843,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagAutoAnim(spriteId, args[0]); break; - case 4: + case 4: // SO_ANIMATION_SPEED args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1821,7 +1854,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteAnimSpeed(spriteId, args[0]); break; - case 6: + case 6: // SO_AT args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1833,7 +1866,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePosition(spriteId, args[0], args[1]); break; - case 7: + case 7: // SO_AT_IMAGE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1844,7 +1877,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteSourceImage(spriteId, args[0]); break; - case 16: + case 16: // SO_CLASS n = getStackList(args, ARRAYSIZE(args)); if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) { int *p = &args[n - 1]; @@ -1867,7 +1900,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { } while (--n); } break; - case 32: + case 32: // SO_ERASE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1878,7 +1911,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagEraseType(spriteId, args[0]); break; - case 38: + case 38: // SO_GROUP args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1889,7 +1922,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteGroup(spriteId, args[0]); break; - case 40: + case 40: // SO_IMAGE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1900,7 +1933,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteImage(spriteId, args[0]); break; - case 48: + case 48: // SO_MASK args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1911,7 +1944,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteMaskImage(spriteId, args[0]); break; - case 49: + case 49: // SO_MOVE args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1923,10 +1956,10 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->moveSprite(spriteId, args[0], args[1]); break; - case 52: + case 52: // SO_NAME copyScriptString(string, sizeof(string)); break; - case 53: + case 53: // SO_NEW if (_curSpriteId > _curMaxSpriteId) break; spriteId = _curSpriteId; @@ -1936,7 +1969,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->resetSprite(spriteId); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1948,7 +1981,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]); break; - case 57: + case 57: // SO_PALETTE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1959,7 +1992,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePalette(spriteId, args[0]); break; - case 59: + case 59: // SO_PRIORITY args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1970,7 +2003,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePriority(spriteId, args[0]); break; - case 60: + case 60: // SO_PROPERTY args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -2001,10 +2034,10 @@ void ScummEngine_v100he::o100_setSpriteInfo() { break; } break; - case 61: + case 61: // SO_RESTART _sprite->resetTables(true); break; - case 65: + case 65: // SO_SCALE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2015,7 +2048,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteScale(spriteId, args[0]); break; - case 70: + case 70: // SO_SHADOW args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2026,7 +2059,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteShadow(spriteId, args[0]); break; - case 73: + case 73: // SO_STATE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2037,7 +2070,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteImageState(spriteId, args[0]); break; - case 74: + case 74: // SO_STEP_DIST args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -2049,7 +2082,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteDist(spriteId, args[0], args[1]); break; - case 75: + case 75: // SO_STEP_DIST_X args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2062,7 +2095,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { _sprite->setSpriteDist(spriteId, args[0], tmp[1]); } break; - case 76: + case 76: // SO_STEP_DIST_Y args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2075,7 +2108,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { _sprite->setSpriteDist(spriteId, tmp[0], args[0]); } break; - case 82: + case 82: // SO_UPDATE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2086,7 +2119,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagUpdateType(spriteId, args[0]); break; - case 83: + case 83: // SO_VARIABLE args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -2098,7 +2131,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteUserValue(spriteId, args[0], args[1]); break; - case 88: + case 88: // SO_IMAGE_ZCLIP args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2107,9 +2140,9 @@ void ScummEngine_v100he::o100_setSpriteInfo() { spriteId++; for (; spriteId <= _curMaxSpriteId; spriteId++) - _sprite->setSpriteField84(spriteId, args[0]); + _sprite->setSpriteZBuffer(spriteId, args[0]); break; - case 89: + case 89: // SO_NEVER_ZCLIP if (_curSpriteId > _curMaxSpriteId) break; spriteId = _curSpriteId; @@ -2117,7 +2150,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { spriteId++; for (; spriteId <= _curMaxSpriteId; spriteId++) - _sprite->setSpriteField84(spriteId, 0); + _sprite->setSpriteZBuffer(spriteId, 0); break; default: error("o100_setSpriteInfo: Unknown case %d", subOp); @@ -2236,40 +2269,43 @@ void ScummEngine_v100he::o100_videoOps() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = pop(); + _videoParams.number = pop(); _videoParams.wizResNum = 0; + + if (_videoParams.number != 1 && _videoParams.number != -1) + warning("o100_videoOps: number: %d", _videoParams.number); break; - case 19: + case 19: // SO_CLOSE _videoParams.status = 19; break; - case 40: + case 40: // SO_IMAGE _videoParams.wizResNum = pop(); if (_videoParams.wizResNum) _videoParams.flags |= 2; break; - case 47: + case 47: // SO_LOAD copyScriptString(_videoParams.filename, sizeof(_videoParams.filename)); _videoParams.status = 47; break; - case 67: + case 67: // SO_SET_FLAGS _videoParams.flags |= pop(); break; - case 92: - if (_videoParams.status == 47) { + case 92: // SO_END + if (_videoParams.status == 47) { // SO_LOAD // Start video if (_videoParams.flags == 0) _videoParams.flags = 4; - if (_videoParams.flags == 2) { + if (_videoParams.flags & 2) { VAR(119) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags, _videoParams.wizResNum); } else { VAR(119) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags); } - } else if (_videoParams.status == 19) { + } else if (_videoParams.status == 19) { // SO_CLOSE // Stop video _moviePlay->close(); } @@ -2449,49 +2485,52 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { byte subOp = fetchScriptByte(); + warning("o100_getSpriteGroupInfo, subop %d", subOp); + switch (subOp) { - case 5: + case 5: // SO_ARRAY spriteGroupId = pop(); if (spriteGroupId) push(getGroupSpriteArray(spriteGroupId)); else push(0); break; - case 40: + case 40: // SO_IMAGE spriteGroupId = pop(); if (spriteGroupId) push(_sprite->getGroupDstResNum(spriteGroupId)); else push(0); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY // TODO: U32 related pop(); pop(); push(0); + warning("STUB: o100_getSpriteGroupInfo, subop 54"); break; - case 59: + case 59: // SO_PRIORITY spriteGroupId = pop(); if (spriteGroupId) push(_sprite->getGroupPriority(spriteGroupId)); else push(0); break; - case 60: + case 60: // SO_PROPERTY type = pop(); spriteGroupId = pop(); if (spriteGroupId) { switch (type) { - case 0: + case 0: // SPRGRPPROP_XMUL push(_sprite->getGroupXMul(spriteGroupId)); break; - case 1: + case 1: // SPRGRPPROP_XDIV push(_sprite->getGroupXDiv(spriteGroupId)); break; - case 2: + case 2: // SPRGRPPROP_YMUL push(_sprite->getGroupYMul(spriteGroupId)); break; - case 3: + case 3: // SPRGRPPROP_YDIV push(_sprite->getGroupYDiv(spriteGroupId)); break; default: @@ -2501,7 +2540,7 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { push(0); } break; - case 85: + case 85: // SO_XPOS spriteGroupId = pop(); if (spriteGroupId) { _sprite->getGroupPosition(spriteGroupId, tx, ty); @@ -2510,7 +2549,7 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { push(0); } break; - case 86: + case 86: // SO_YPOS spriteGroupId = pop(); if (spriteGroupId) { _sprite->getGroupPosition(spriteGroupId, tx, ty); diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp index a911487738..b91943c685 100644 --- a/engines/scumm/he/script_v70he.cpp +++ b/engines/scumm/he/script_v70he.cpp @@ -71,6 +71,7 @@ void ScummEngine_v70he::o70_startSound() { value = pop(); _heSndSoundId = pop(); _sound->addSoundToQueue(_heSndSoundId, 0, 0, 8); + break; case 56: _heSndFlags |= 16; break; diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp index f2d92bc2ca..f63973e3f1 100644 --- a/engines/scumm/he/script_v90he.cpp +++ b/engines/scumm/he/script_v90he.cpp @@ -1412,7 +1412,7 @@ void ScummEngine_v90he::o90_videoOps() { memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = pop(); + _videoParams.number = pop(); _videoParams.wizResNum = 0; break; case 14: diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 2e0a03af7f..8670116c68 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -51,10 +51,12 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer) _heMusicTracks(0) { memset(_heChannel, 0, sizeof(_heChannel)); + _heSoundChannels = new Audio::SoundHandle[8](); } SoundHE::~SoundHE() { free(_heMusic); + delete[] _heSoundChannels; } void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h index 323858a7c9..e0324d0753 100644 --- a/engines/scumm/he/sound_he.h +++ b/engines/scumm/he/sound_he.h @@ -44,7 +44,7 @@ protected: HEMusic *_heMusic; int16 _heMusicTracks; - Audio::SoundHandle _heSoundChannels[8]; + Audio::SoundHandle *_heSoundChannels; public: // Used by createSound() struct { diff --git a/engines/scumm/he/sprite_he.cpp b/engines/scumm/he/sprite_he.cpp index b192fab233..e3f04dfcf0 100644 --- a/engines/scumm/he/sprite_he.cpp +++ b/engines/scumm/he/sprite_he.cpp @@ -739,19 +739,17 @@ void Sprite::setSpriteResetClass(int spriteId) { _spriteTable[spriteId].classFlags = 0; } -void Sprite::setSpriteField84(int spriteId, int value) { +void Sprite::setSpriteZBuffer(int spriteId, int value) { assertRange(1, spriteId, _varNumSprites, "sprite"); - _spriteTable[spriteId].field_84 = value; + _spriteTable[spriteId].zbufferImage = value; } void Sprite::setSpriteGeneralProperty(int spriteId, int type, int value) { - debug(0, "setSpriteGeneralProperty: spriteId %d type 0x%x value 0x%x", spriteId, type, value); + debug(6, "setSpriteGeneralProperty: spriteId %d type 0x%x value 0x%x", spriteId, type, value); assertRange(1, spriteId, _varNumSprites, "sprite"); int32 delay; - // XXX U32 related check - switch (type) { case 0x7B: _spriteTable[spriteId].imgFlags = value; @@ -797,7 +795,7 @@ void Sprite::resetSprite(int spriteId) { _spriteTable[spriteId].sourceImage = 0; _spriteTable[spriteId].maskImage = 0; _spriteTable[spriteId].priority = 0; - _spriteTable[spriteId].field_84 = 0; + _spriteTable[spriteId].zbufferImage = 0; _spriteTable[spriteId].imgFlags = 0; _spriteTable[spriteId].conditionBits = 0; @@ -816,7 +814,7 @@ void Sprite::setSpriteImage(int spriteId, int imageNum) { origResWizStates = _spriteTable[spriteId].imageStateCount; _spriteTable[spriteId].image = imageNum; - _spriteTable[spriteId].field_74 = 0; + _spriteTable[spriteId].animIndex = 0; _spriteTable[spriteId].imageState = 0; if (_spriteTable[spriteId].image) { @@ -1339,9 +1337,9 @@ void Sprite::processImages(bool arg) { } if (spr_flags & kSFRemapPalette) wiz.img.flags |= kWIFRemapPalette; - if (spi->field_84) { + if (spi->zbufferImage) { wiz.processFlags |= 0x200000; - wiz.img.zbuffer = spi->field_84; + wiz.img.zbuffer = spi->zbufferImage; wiz.img.zorder = spi->priority; } if (spi->sourceImage) { @@ -1419,11 +1417,11 @@ void Sprite::saveOrLoadSpriteData(Serializer *s) { MKLINE(SpriteInfo, curAngle, sleInt32, VER(48)), MKLINE(SpriteInfo, curScale, sleInt32, VER(48)), MKLINE(SpriteInfo, curImgFlags, sleInt32, VER(48)), - MKLINE(SpriteInfo, field_74, sleInt32, VER(48)), + MKLINE(SpriteInfo, animIndex, sleInt32, VER(48)), MKLINE(SpriteInfo, animSpeed, sleInt32, VER(48)), MKLINE(SpriteInfo, sourceImage, sleInt32, VER(48)), MKLINE(SpriteInfo, maskImage, sleInt32, VER(48)), - MKLINE(SpriteInfo, field_84, sleInt32, VER(48)), + MKLINE(SpriteInfo, zbufferImage, sleInt32, VER(48)), MKLINE(SpriteInfo, classFlags, sleInt32, VER(48)), MKLINE(SpriteInfo, imgFlags, sleInt32, VER(48)), MKLINE(SpriteInfo, conditionBits, sleInt32, VER(48)), diff --git a/engines/scumm/he/sprite_he.h b/engines/scumm/he/sprite_he.h index b1a7641fcc..3ea6bb9f84 100644 --- a/engines/scumm/he/sprite_he.h +++ b/engines/scumm/he/sprite_he.h @@ -72,11 +72,11 @@ struct SpriteInfo { int32 curAngle; int32 curScale; int32 curImgFlags; - int32 field_74; + int32 animIndex; int32 animSpeed; int32 sourceImage; int32 maskImage; - int32 field_84; + int32 zbufferImage; int32 classFlags; int32 imgFlags; int32 conditionBits; @@ -182,7 +182,7 @@ public: void setSpriteAnimSpeed(int spriteId, int value); void setSpriteSetClass(int spriteId, int classId, int toggle); void setSpriteResetClass(int spriteId); - void setSpriteField84(int spriteId, int value); + void setSpriteZBuffer(int spriteId, int value); void setSpriteGeneralProperty(int spriteId, int type, int value); void moveGroupMembers(int spriteGroupId, int value1, int value2); diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 19e9dbb5b6..0ebfe67245 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -31,6 +31,7 @@ #include "scumm/scumm.h" #include "scumm/util.h" #include "scumm/he/wiz_he.h" +#include "scumm/he/moonbase/moonbase.h" namespace Scumm { @@ -976,7 +977,7 @@ void Wiz::decompressRawWizImage(uint8 *dst, int dstPitch, int dstType, const uin } } -int Wiz::isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth) { +int Wiz::isPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth) { if (x < 0 || x >= w || y < 0 || y >= h) { return 0; } @@ -1434,7 +1435,7 @@ void Wiz::displayWizImage(WizImage *pwi) { } uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int x1, int y1, int zorder, int shadow, int zbuffer, const Common::Rect *clipBox, int flags, int dstResNum, const uint8 *palPtr, uint32 conditionBits) { - debug(3, "drawWizImage(resNum %d, state %d maskNum %d maskState %d x1 %d y1 %d flags 0x%X zorder %d shadow %d zbuffer %d dstResNum %d conditionBits: 0x%x)", resNum, state, maskNum, maskState, x1, y1, flags, zorder, shadow, zbuffer, dstResNum, conditionBits); + debug(7, "drawWizImage(resNum %d, state %d maskNum %d maskState %d x1 %d y1 %d flags 0x%X zorder %d shadow %d zbuffer %d dstResNum %d conditionBits: 0x%x)", resNum, state, maskNum, maskState, x1, y1, flags, zorder, shadow, zbuffer, dstResNum, conditionBits); uint8 *dataPtr; uint8 *dst = NULL; @@ -1454,7 +1455,7 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int uint32 comp = READ_LE_UINT32(wizh + 0x0); uint32 width = READ_LE_UINT32(wizh + 0x4); uint32 height = READ_LE_UINT32(wizh + 0x8); - debug(3, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); + debug(7, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); uint8 *mask = NULL; if (maskNum) { @@ -1571,8 +1572,17 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int transColor = (trns == NULL) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : -1; } - drawWizImageEx(dst, dataPtr, mask, dstPitch, dstType, cw, ch, x1, y1, width, height, - state, &rScreen, flags, palPtr, transColor, _vm->_bytesPerPixel, xmapPtr, conditionBits); + if (_vm->_game.id == GID_MOONBASE && + ((ScummEngine_v90he *)_vm)->_moonbase->isFOW(resNum, state, conditionBits)) { + ((ScummEngine_v90he *)_vm)->_moonbase->renderFOW(dst, dstPitch, dstType, cw, ch, flags); + x1 = 0; + y1 = 0; + width = rScreen.width(); + height = rScreen.height(); + } else { + drawWizImageEx(dst, dataPtr, mask, dstPitch, dstType, cw, ch, x1, y1, width, height, + state, &rScreen, flags, palPtr, transColor, _vm->_bytesPerPixel, xmapPtr, conditionBits); + } if (!(flags & kWIFBlitToMemBuffer) && dstResNum == 0) { Common::Rect rImage(x1, y1, x1 + width, y1 + height); @@ -1598,7 +1608,7 @@ void Wiz::drawWizImageEx(uint8 *dst, uint8 *dataPtr, uint8 *maskPtr, int dstPitc uint32 comp = READ_LE_UINT32(wizh + 0x0); uint32 width = READ_LE_UINT32(wizh + 0x4); uint32 height = READ_LE_UINT32(wizh + 0x8); - debug(3, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); + debug(7, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); uint8 *wizd = _vm->findWrappedBlock(MKTAG('W','I','Z','D'), dataPtr, state, 0); assert(wizd); @@ -1755,8 +1765,6 @@ void Wiz::copyCompositeWizImage(uint8 *dst, uint8 *wizPtr, uint8 *compositeInfoB if (layerCmdDataBits & kWCFSubConditionBits) { subConditionBits = READ_LE_UINT32(cmdPtr); cmdPtr += 4; - } else { - subConditionBits = 0; } drawWizImageEx(dst, nestedWizHeader, maskPtr, dstPitch, dstType, dstw, dsth, srcx + xPos, srcy + yPos, srcw, srch, @@ -1773,11 +1781,12 @@ void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, switch (rawROP) { default: case 1: + rawROP = 1; // MMX_PREMUL_ALPHA_COPY break; case 2: - warning("T14: MMX_ADDITIVE"); + //warning("T14: MMX_ADDITIVE"); break; case 3: @@ -1789,7 +1798,7 @@ void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, break; case 5: - warning("T14: MMX_CHEAP_50_50"); + //warning("T14: MMX_CHEAP_50_50"); break; case 6: @@ -1805,7 +1814,7 @@ void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, uint32 compID = READ_LE_UINT32(wizd); if (compID == 0x12340102) { - _vm->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); + ((ScummEngine_v90he *)_vm)->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); } else if (compID == 0x12340802) { warning("Distorion codec"); } else if (compID == 0x12340902) { @@ -2306,14 +2315,6 @@ void Wiz::displayWizComplexImage(const WizParameters *params) { if (flags & kWIFIsPolygon) { drawWizPolygon(params->img.resNum, state, po_x, flags, shadow, dstResNum, palette); } else { - if (_vm->_game.id == GID_MOONBASE) { - if (params->img.resNum == _vm->_moonbase->_fowSentinelImage && - state == _vm->_moonbase->_fowSentinelState && - params->conditionBits == _vm->_moonbase->_fowSentinelConditionBits) - _vm->_moonbase->renderFOW(); - } - - drawWizImage(params->img.resNum, state, 0, 0, po_x, po_y, params->img.zorder, shadow, zbuffer, r, flags, dstResNum, _vm->getHEPaletteSlot(palette), params->conditionBits); } } @@ -2699,6 +2700,10 @@ void Wiz::processWizImage(const WizParameters *params) { void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) { uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); assert(dataPtr); + getWizImageDim(dataPtr, state, w, h); +} + +void Wiz::getWizImageDim(uint8 *dataPtr, int state, int32 &w, int32 &h) { uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), dataPtr, state, 0); assert(wizh); w = READ_LE_UINT32(wizh + 0x4); @@ -2708,6 +2713,10 @@ void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) { void Wiz::getWizImageSpot(int resId, int state, int32 &x, int32 &y) { uint8 *dataPtr = _vm->getResourceAddress(rtImage, resId); assert(dataPtr); + getWizImageSpot(dataPtr, state, x, y); +} + +void Wiz::getWizImageSpot(uint8 *dataPtr, int state, int32 &x, int32 &y) { uint8 *spotPtr = _vm->findWrappedBlock(MKTAG('S','P','O','T'), dataPtr, state, 0); if (spotPtr) { x = READ_LE_UINT32(spotPtr + 0); @@ -2745,6 +2754,11 @@ int Wiz::getWizImageData(int resNum, int state, int type) { int Wiz::getWizImageStates(int resNum) { const uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); assert(dataPtr); + + return getWizImageStates(dataPtr); +} + +int Wiz::getWizImageStates(const uint8 *dataPtr) { if (READ_BE_UINT32(dataPtr) == MKTAG('M','U','L','T')) { const byte *offs, *wrap; @@ -2763,9 +2777,14 @@ int Wiz::getWizImageStates(int resNum) { } int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags) { - int ret = 0; uint8 *data = _vm->getResourceAddress(rtImage, resNum); assert(data); + + return isWizPixelNonTransparent(data, state, x, y, flags); +} + +int Wiz::isWizPixelNonTransparent(uint8 *data, int state, int x, int y, int flags) { + int ret = 0; uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), data, state, 0); assert(wizh); int c = READ_LE_UINT32(wizh + 0x0); @@ -2789,7 +2808,7 @@ int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags } break; case 1: - ret = isWizPixelNonTransparent(wizd, x, y, w, h, 1); + ret = isPixelNonTransparent(wizd, x, y, w, h, 1); break; #ifdef USE_RGB_COLOR case 2: @@ -2802,7 +2821,7 @@ int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags break; } case 5: - ret = isWizPixelNonTransparent(wizd, x, y, w, h, 2); + ret = isPixelNonTransparent(wizd, x, y, w, h, 2); break; #endif default: diff --git a/engines/scumm/he/wiz_he.h b/engines/scumm/he/wiz_he.h index 202f774560..692ad76db5 100644 --- a/engines/scumm/he/wiz_he.h +++ b/engines/scumm/he/wiz_he.h @@ -220,14 +220,19 @@ public: void remapWizImagePal(const WizParameters *params); void getWizImageDim(int resNum, int state, int32 &w, int32 &h); + void getWizImageDim(uint8 *dataPtr, int state, int32 &w, int32 &h); int getWizImageStates(int resnum); + int getWizImageStates(const uint8 *ptr); int isWizPixelNonTransparent(int resnum, int state, int x, int y, int flags); + int isWizPixelNonTransparent(uint8 *data, int state, int x, int y, int flags); + int isPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitdepth); uint16 getWizPixelColor(int resnum, int state, int x, int y); int getWizImageData(int resNum, int state, int type); void flushWizBuffer(); void getWizImageSpot(int resId, int state, int32 &x, int32 &y); + void getWizImageSpot(uint8 *data, int state, int32 &x, int32 &y); void loadWizCursor(int resId, int palette); void captureWizImage(int resNum, const Common::Rect& r, bool frontBuffer, int compType); @@ -272,7 +277,6 @@ public: template<int type> static void write8BitColor(uint8 *dst, const uint8 *src, int dstType, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth); static void writeColor(uint8 *dstPtr, int dstType, uint16 color); - int isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitdepth); uint16 getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth, uint16 color); uint16 getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth, uint16 color); void computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect& rCapt); diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 12047635a0..6ef7e4d7f4 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -24,6 +24,7 @@ #include "common/events.h" #include "common/system.h" #include "common/translation.h" +#include "audio/mixer.h" #include "scumm/debugger.h" #include "scumm/dialogs.h" diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 5462ad3090..04611ba1b1 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -139,7 +139,16 @@ MODULE_OBJS += \ he/logic/moonbase_logic.o \ he/logic/puttrace.o \ he/logic/soccer.o \ - he/moonbase/moonbase.o + he/moonbase/ai_defenseunit.o \ + he/moonbase/ai_main.o \ + he/moonbase/ai_node.o \ + he/moonbase/ai_targetacquisition.o \ + he/moonbase/ai_traveller.o \ + he/moonbase/ai_tree.o \ + he/moonbase/ai_types.o \ + he/moonbase/ai_weapon.o \ + he/moonbase/moonbase.o \ + he/moonbase/moonbase_fow.o endif # This module can be built as a plugin diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index e986ae4b47..68e4887b00 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Mon Mar 28 09:52:54 2016 + This file was generated by the md5table tool on Sat Apr 30 14:24:41 2016 DO NOT EDIT MANUALLY! */ @@ -532,6 +532,7 @@ static const MD5Table md5table[] = { { "bf8b52fdd9a69c67f34e8e9fec72661c", "farm", "HE 71", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "bfdf584b01503f0762baded581f6a0a2", "SoccerMLS", "", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "c0039ad982999c92d0de81910d640fa0", "freddi", "HE 71", "", 26159, Common::NL_NLD, Common::kPlatformWindows }, + { "c0c9de81fb965e6cbe77f6e5631ca705", "monkey", "SE Talkie", "Unofficial SE Talkie v1.02", 9135, Common::EN_ANY, Common::kPlatformDOS }, { "c13225cb1bbd3bc9fe578301696d8021", "monkey", "SEGA", "", -1, Common::EN_ANY, Common::kPlatformSegaCD }, { "c20848f53c2d48bfacdc840993843765", "freddi2", "HE 80", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "c225bec1b6c0798a2b8c89ac226dc793", "pajama", "HE 101", "", -1, Common::EN_ANY, Common::kPlatformWii }, @@ -672,6 +673,7 @@ static const MD5Table md5table[] = { { "f3c5d9bf3f091bd1f18dc1013fba5396", "atlantis", "Steam", "Steam", 638976, Common::EN_ANY, Common::kPlatformWindows }, { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows }, { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, + { "f4d20ab4ce19743a646cb48bd93aee72", "monkey2", "SE Talkie", "Unofficial SE Talkie v0.2", 10835, Common::EN_ANY, Common::kPlatformDOS }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh }, { "f7635a0e2ab82c9a0f9ace5f232a488f", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 746e74d33d..4adf0c5066 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -66,6 +66,7 @@ #include "scumm/players/player_v5m.h" #include "scumm/resource.h" #include "scumm/he/resource_he.h" +#include "scumm/he/moonbase/moonbase.h" #include "scumm/scumm_v0.h" #include "scumm/scumm_v8.h" #include "scumm/sound.h" @@ -832,16 +833,9 @@ ScummEngine_v71he::ScummEngine_v71he(OSystem *syst, const DetectorResult &dr) _skipProcessActors = 0; VAR_WIZ_TCOLOR = 0xFF; - - /* Moonbase stuff */ - _moonbase = 0; - - if (_game.id == GID_MOONBASE) - _moonbase = new Moonbase(); } ScummEngine_v71he::~ScummEngine_v71he() { - delete _moonbase; delete _wiz; } @@ -881,7 +875,7 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = 0; + _videoParams.number = 0; _videoParams.wizResNum = 0; VAR_NUM_SPRITE_GROUPS = 0xFF; @@ -891,6 +885,19 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) VAR_U32_VERSION = 0xFF; VAR_U32_ARRAY_UNK = 0xFF; + + /* Moonbase stuff */ + _moonbase = 0; + + if (_game.id == GID_MOONBASE) + _moonbase = new Moonbase(this); + + VAR_U32_USER_VAR_A = 0xFF; + VAR_U32_USER_VAR_B = 0xFF; + VAR_U32_USER_VAR_C = 0xFF; + VAR_U32_USER_VAR_D = 0xFF; + VAR_U32_USER_VAR_E = 0xFF; + VAR_U32_USER_VAR_F = 0xFF; } ScummEngine_v90he::~ScummEngine_v90he() { @@ -902,6 +909,7 @@ ScummEngine_v90he::~ScummEngine_v90he() { if (_game.heversion >= 99) { free(_hePalettes); } + delete _moonbase; } ScummEngine_vCUPhe::ScummEngine_vCUPhe(OSystem *syst, const DetectorResult &dr) : Engine(syst){ diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 6e0adc3ff3..a3fa329728 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -20,8 +20,8 @@ * */ -#ifndef SCUMM_H -#define SCUMM_H +#ifndef SCUMM_SCUMM_H +#define SCUMM_SCUMM_H #include "engines/engine.h" @@ -700,10 +700,13 @@ protected: void ignoreScriptWord() { fetchScriptWord(); } void ignoreScriptByte() { fetchScriptByte(); } void push(int a); + +public: // TODO. FIXME should be protected. Used by Moonbase int pop(); virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: void beginCutscene(int *args); void endCutscene(); void abortCutscene(); diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index e438008c1e..763bbc061c 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -77,9 +77,12 @@ protected: void getResultPosIndirect(); virtual void getResultPos(); + +public: // TODO. FIXME. Should be protected. Used by Moonbase virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: virtual int getActiveObject(); void ifStateCommon(byte type); void ifNotStateCommon(byte type); diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h index 73268f6f33..83b9f2f4f8 100644 --- a/engines/scumm/scumm_v6.h +++ b/engines/scumm/scumm_v6.h @@ -119,7 +119,10 @@ protected: ArrayHeader *getArray(int array); byte *defineArray(int array, int type, int dim2, int dim1); int findFreeArrayId(); +public: // FIXME. TODO void nukeArray(int array); + +protected: virtual int readArray(int array, int index, int base); virtual void writeArray(int array, int index, int base, int value); void shuffleArray(int num, int minIdx, int maxIdx); diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp index 2ca2579b54..42ee0115c7 100644 --- a/engines/scumm/smush/smush_player.cpp +++ b/engines/scumm/smush/smush_player.cpp @@ -25,6 +25,8 @@ #include "common/system.h" #include "common/util.h" +#include "audio/mixer.h" + #include "graphics/cursorman.h" #include "graphics/palette.h" @@ -242,9 +244,15 @@ SmushPlayer::SmushPlayer(ScummEngine_v7 *scumm) { _paused = false; _pauseStartTime = 0; _pauseTime = 0; + + + _IACTchannel = new Audio::SoundHandle(); + _compressedFileSoundHandle = new Audio::SoundHandle(); } SmushPlayer::~SmushPlayer() { + delete _IACTchannel; + delete _compressedFileSoundHandle; } void SmushPlayer::init(int32 speed) { @@ -271,8 +279,8 @@ void SmushPlayer::init(int32 speed) { vs->pitch = vs->w; _vm->_gdi->_numStrips = vs->w / 8; - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; _vm->_smixer->stop(); } @@ -470,7 +478,7 @@ void SmushPlayer::handleIACT(int32 subSize, Common::SeekableReadStream &b) { if (!_IACTstream) { _IACTstream = Audio::makeQueuingAudioStream(22050, true); - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _IACTchannel, _IACTstream); } _IACTstream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS); @@ -1091,7 +1099,7 @@ void SmushPlayer::seekSan(const char *file, int32 pos, int32 contFrame) { } void SmushPlayer::tryCmpFile(const char *filename) { - _vm->_mixer->stopHandle(_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); _compressedFileMode = false; const char *i = strrchr(filename, '.'); @@ -1110,7 +1118,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".ogg"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); return; } #endif @@ -1119,7 +1127,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".mp3"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); return; } #endif @@ -1185,12 +1193,12 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st // the sound. Synt to time instead. now = _vm->_system->getMillis() - _pauseTime; elapsed = now - _startTime; - } else if (_vm->_mixer->isSoundHandleActive(_compressedFileSoundHandle)) { + } else if (_vm->_mixer->isSoundHandleActive(*_compressedFileSoundHandle)) { // Compressed SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_compressedFileSoundHandle); - } else if (_vm->_mixer->isSoundHandleActive(_IACTchannel)) { + elapsed = _vm->_mixer->getSoundElapsedTime(*_compressedFileSoundHandle); + } else if (_vm->_mixer->isSoundHandleActive(*_IACTchannel)) { // Curse of Monkey Island SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_IACTchannel); + elapsed = _vm->_mixer->getSoundElapsedTime(*_IACTchannel); } else { // For other SMUSH files, we don't necessarily have any // one channel to sync against, so we have to use @@ -1245,8 +1253,8 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st break; if (_vm->shouldQuit() || _vm->_saveLoadFlag || _vm->_smushVideoShouldFinish) { _smixer->stop(); - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; break; } diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h index b0d6e6a9f0..f1dffef7c7 100644 --- a/engines/scumm/smush/smush_player.h +++ b/engines/scumm/smush/smush_player.h @@ -24,9 +24,9 @@ #define SCUMM_SMUSH_PLAYER_H #include "common/util.h" -#include "scumm/sound.h" namespace Audio { +class SoundHandle; class QueuingAudioStream; } @@ -65,10 +65,10 @@ private: bool _skipNext; uint32 _frame; - Audio::SoundHandle _IACTchannel; + Audio::SoundHandle *_IACTchannel; Audio::QueuingAudioStream *_IACTstream; - Audio::SoundHandle _compressedFileSoundHandle; + Audio::SoundHandle *_compressedFileSoundHandle; bool _compressedFileMode; byte _IACToutput[4096]; int32 _IACTpos; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index f66452e99c..33b7c3108d 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -97,12 +97,17 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) _loomSteamCD.balance = 0; _isLoomSteam = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU"); + + _loomSteamCDAudioHandle = new Audio::SoundHandle(); + _talkChannelHandle = new Audio::SoundHandle(); } Sound::~Sound() { stopCDTimer(); stopCD(); free(_offsetTable); + delete _loomSteamCDAudioHandle; + delete _talkChannelHandle; } void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { @@ -425,7 +430,7 @@ void Sound::processSfxQueues() { if (_talk_sound_mode & 1) startTalkSound(_talk_sound_a1, _talk_sound_b1, 1); if (_talk_sound_mode & 2) - startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, &_talkChannelHandle); + startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, _talkChannelHandle); _talk_sound_mode = 0; } @@ -439,7 +444,7 @@ void Sound::processSfxQueues() { } else if (_vm->_game.heversion >= 60) { finished = !isSoundRunning(1); } else { - finished = !_mixer->isSoundHandleActive(_talkChannelHandle); + finished = !_mixer->isSoundHandleActive(*_talkChannelHandle); } if ((uint) act < 0x80 && ((_vm->_game.version == 8) || (_vm->_game.version <= 7 && !_vm->_string[0].no_talk_anim))) { @@ -675,7 +680,7 @@ void Sound::stopTalkSound() { } else if (_vm->_game.heversion >= 60) { stopSound(1); } else { - _mixer->stopHandle(_talkChannelHandle); + _mixer->stopHandle(*_talkChannelHandle); } _sfxMode &= ~2; } @@ -1060,7 +1065,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration); } else { // Stop any currently playing track - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); Common::File *cddaFile = new Common::File(); if (cddaFile->open("CDDA.SOU")) { @@ -1068,7 +1073,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle, + _mixer->playStream(Audio::Mixer::kMusicSoundType, _loomSteamCDAudioHandle, Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); } else { delete cddaFile; @@ -1080,14 +1085,14 @@ void Sound::stopCD() { if (!_isLoomSteam) g_system->getAudioCDManager()->stop(); else - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); } int Sound::pollCD() const { if (!_isLoomSteam) return g_system->getAudioCDManager()->isPlaying(); else - return _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + return _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); } void Sound::updateCD() { @@ -1100,7 +1105,7 @@ AudioCDManager::Status Sound::getCDStatus() { return g_system->getAudioCDManager()->getStatus(); else { AudioCDManager::Status info = _loomSteamCD; - info.playing = _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + info.playing = _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); return info; } } diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h index 8c11c7b5b2..7fdb16371c 100644 --- a/engines/scumm/sound.h +++ b/engines/scumm/sound.h @@ -26,10 +26,14 @@ #include "common/scummsys.h" #include "common/str.h" #include "audio/mididrv.h" -#include "audio/mixer.h" #include "backends/audiocd/audiocd.h" #include "scumm/saveload.h" +namespace Audio { +class Mixer; +class SoundHandle; +} + namespace Scumm { class ScummEngine; @@ -81,12 +85,12 @@ protected: int16 _currentCDSound; int16 _currentMusic; - Audio::SoundHandle _loomSteamCDAudioHandle; + Audio::SoundHandle *_loomSteamCDAudioHandle; bool _isLoomSteam; AudioCDManager::Status _loomSteamCD; public: - Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on + Audio::SoundHandle *_talkChannelHandle; // Handle of mixer channel actor is talking on bool _soundsPaused; byte _sfxMode; diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 3049fbcf62..e6054918fa 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -23,6 +23,7 @@ #include "common/config-manager.h" +#include "audio/mixer.h" #include "scumm/actor.h" #include "scumm/charset.h" @@ -662,7 +663,7 @@ void ScummEngine::CHARSET_1() { // Special case for HE games } else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) { // Special case for Loom (CD), since it only uses CD audio.for sound - } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(_sound->_talkChannelHandle))) { + } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) { // Subtitles are turned off, and there is a voice version // of this message -> don't print it. } else { diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index a6be5c3f3a..5254aa4af2 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -340,6 +340,14 @@ void ScummEngine_v90he::setupScummVars() { VAR_NUM_PALETTES = 130; VAR_NUM_UNK = 131; } + if (_game.id == GID_MOONBASE) { + VAR_U32_USER_VAR_A = 108; + VAR_U32_USER_VAR_B = 109; + VAR_U32_USER_VAR_C = 110; + VAR_U32_USER_VAR_D = 111; + VAR_U32_USER_VAR_E = 112; + VAR_U32_USER_VAR_F = 113; + } } #endif diff --git a/engines/sherlock/image_file.cpp b/engines/sherlock/image_file.cpp index 1247a7f8d0..c53e537bb8 100644 --- a/engines/sherlock/image_file.cpp +++ b/engines/sherlock/image_file.cpp @@ -302,12 +302,6 @@ ImageFile3DO::ImageFile3DO(Common::SeekableReadStream &stream, bool isRoomData) } } -ImageFile3DO::~ImageFile3DO() { - // already done in ImageFile destructor - //for (uint idx = 0; idx < size(); ++idx) - // (*this)[idx]._frame.free(); -} - void ImageFile3DO::load(Common::SeekableReadStream &stream, bool isRoomData) { uint32 headerId = 0; @@ -380,7 +374,7 @@ void ImageFile3DO::loadAnimationFile(Common::SeekableReadStream &stream) { if (streamLeft < celDataSize) error("load3DOAnimationFile: expected cel data, not enough bytes"); - // + // // Load data for frame and decompress it byte *data = new byte[celDataSize]; stream.read(data, celDataSize); @@ -683,7 +677,7 @@ void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) { if (ccbFlags & 0x200) // bit 9 ccbFlags_compressed = true; - + // PRE0 first 3 bits define how many bits per encoded pixel are used ccbPRE0_bitsPerPixel = imagefile3DO_cel_bitsPerPixelLookupTable[ccbPRE0 & 0x07]; if (!ccbPRE0_bitsPerPixel) @@ -713,7 +707,7 @@ void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) { stream.read(celDataPtr, celDataSize); streamLeft -= celDataSize; - + // Set up frame { ImageFrame imageFrame; diff --git a/engines/sherlock/image_file.h b/engines/sherlock/image_file.h index 778332b726..0ae7cc1dcb 100644 --- a/engines/sherlock/image_file.h +++ b/engines/sherlock/image_file.h @@ -95,7 +95,7 @@ public: ImageFile(); ImageFile(const Common::String &name, bool skipPal = false, bool animImages = false); ImageFile(Common::SeekableReadStream &stream, bool skipPal = false); - ~ImageFile(); + virtual ~ImageFile(); static void setVm(SherlockEngine *vm); }; @@ -155,7 +155,6 @@ private: public: ImageFile3DO(const Common::String &name, ImageFile3DOType imageFile3DOType); ImageFile3DO(Common::SeekableReadStream &stream, bool isRoomData = false); - ~ImageFile3DO(); static void setVm(SherlockEngine *vm); }; diff --git a/engines/sherlock/sherlock.h b/engines/sherlock/sherlock.h index d3b2d0cac8..0b4333ea3a 100644 --- a/engines/sherlock/sherlock.h +++ b/engines/sherlock/sherlock.h @@ -20,8 +20,8 @@ * */ -#ifndef SHERLOCK_HOLMES_H -#define SHERLOCK_HOLMES_H +#ifndef SHERLOCK_SHERLOCK_H +#define SHERLOCK_SHERLOCK_H #include "common/scummsys.h" #include "common/array.h" diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp index 9f78234aba..9f4b6c21c6 100644 --- a/engines/sky/control.cpp +++ b/engines/sky/control.cpp @@ -167,7 +167,7 @@ ControlStatus::~ControlStatus() { void ControlStatus::setToText(const char *newText) { char tmpLine[256]; - strcpy(tmpLine, newText); + Common::strlcpy(tmpLine, newText, 256); if (_textData) { _statusText->flushForRedraw(); free(_textData); @@ -402,7 +402,8 @@ void Control::animClick(ConResource *pButton) { void Control::drawMainPanel() { memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT); _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT); - _controlPanel->drawToScreen(NO_MASK); + if (_controlPanel) + _controlPanel->drawToScreen(NO_MASK); _exitButton->drawToScreen(NO_MASK); _savePanButton->drawToScreen(NO_MASK); _restorePanButton->drawToScreen(NO_MASK); diff --git a/engines/sky/sky.h b/engines/sky/sky.h index 56833004ac..374302b8d3 100644 --- a/engines/sky/sky.h +++ b/engines/sky/sky.h @@ -20,8 +20,8 @@ * */ -#ifndef SKY_H -#define SKY_H +#ifndef SKY_SKY_H +#define SKY_SKY_H #include "common/error.h" diff --git a/engines/sword1/resman.cpp b/engines/sword1/resman.cpp index 0a0324a67c..2f8b37d21c 100644 --- a/engines/sword1/resman.cpp +++ b/engines/sword1/resman.cpp @@ -327,13 +327,12 @@ Common::File *ResMan::resFile(uint32 id) { Clu *closeClu = _openCluStart; _openCluStart = _openCluStart->nextOpen; - if (closeClu) { - if (closeClu->file) - closeClu->file->close(); - delete closeClu->file; - closeClu->file = NULL; - closeClu->nextOpen = NULL; - } + if (closeClu->file) + closeClu->file->close(); + delete closeClu->file; + closeClu->file = NULL; + closeClu->nextOpen = NULL; + _openClus--; } } diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index eb24ef70fa..8dab522fd0 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -20,8 +20,8 @@ * */ -#ifndef SWORD1_H -#define SWORD1_H +#ifndef SWORD1_SWORD1_H +#define SWORD1_SWORD1_H #include "engines/engine.h" #include "common/error.h" @@ -150,4 +150,4 @@ private: } // End of namespace Sword1 -#endif //BSSWORD1_H +#endif // SWORD1_SWORD1_H diff --git a/engines/sword2/screen.cpp b/engines/sword2/screen.cpp index 0cb951fdfc..40baf67e46 100644 --- a/engines/sword2/screen.cpp +++ b/engines/sword2/screen.cpp @@ -1296,7 +1296,7 @@ void Screen::setPsxScrCache(byte *psxScrCache, uint8 level) { } byte *Screen::getPsxScrCache(uint8 level) { - if (level > 3) { + if (level > 2) { level = 0; } @@ -1307,7 +1307,7 @@ byte *Screen::getPsxScrCache(uint8 level) { } bool Screen::getPsxScrCacheStatus(uint8 level) { - if (level > 3) { + if (level > 2) { level = 0; } diff --git a/engines/sword2/sword2.h b/engines/sword2/sword2.h index ef5c2b215e..65d1a955fb 100644 --- a/engines/sword2/sword2.h +++ b/engines/sword2/sword2.h @@ -22,8 +22,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef SWORD2_H -#define SWORD2_H +#ifndef SWORD2_SWORD2_H +#define SWORD2_SWORD2_H #define FRAMES_PER_SECOND 12 diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp index 457dda6268..87bedcc9c1 100644 --- a/engines/sword25/package/packagemanager.cpp +++ b/engines/sword25/package/packagemanager.cpp @@ -82,7 +82,7 @@ Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) return fileName; Common::String newFileName = "/speech/en"; - int fileIdx = 9; + uint fileIdx = 9; while (fileIdx < fileName.size() && fileName[fileIdx] != '/') ++fileIdx; if (fileIdx < fileName.size()) diff --git a/engines/sword25/sword25.h b/engines/sword25/sword25.h index 72391cf9d8..c08f20e3b3 100644 --- a/engines/sword25/sword25.h +++ b/engines/sword25/sword25.h @@ -20,8 +20,8 @@ * */ -#ifndef SWORD25_H -#define SWORD25_H +#ifndef SWORD25_SWORD25_H +#define SWORD25_SWORD25_H #include "common/scummsys.h" #include "engines/engine.h" diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h index 234bfb1c57..438f06d189 100644 --- a/engines/teenagent/teenagent.h +++ b/engines/teenagent/teenagent.h @@ -20,8 +20,8 @@ * */ -#ifndef TEENAGENT_ENGINE_H -#define TEENAGENT_ENGINE_H +#ifndef TEENAGENT_TEENAGENT_H +#define TEENAGENT_TEENAGENT_H #include "engines/engine.h" diff --git a/engines/testbed/testbed.h b/engines/testbed/testbed.h index 0f70e1191f..5ea05feba2 100644 --- a/engines/testbed/testbed.h +++ b/engines/testbed/testbed.h @@ -20,8 +20,8 @@ * */ -#ifndef TESTBED_H -#define TESTBED_H +#ifndef TESTBED_TESTBED_H +#define TESTBED_TESTBED_H #include "common/array.h" @@ -72,4 +72,4 @@ private: } // End of namespace Testbed -#endif // TESTBED_H +#endif // TESTBED_TESTBED_H diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp index 22c3798fc6..cfe97e6ec1 100644 --- a/engines/tinsel/bmv.cpp +++ b/engines/tinsel/bmv.cpp @@ -567,8 +567,8 @@ void BMVPlayer::PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) { assert(!bMovieOn); - strcpy(szMovieFile, (char *)LockMem(hFileStem)); - strcat(szMovieFile, BMOVIE_EXTENSION); + Common::strlcpy(szMovieFile, (char *)LockMem(hFileStem), 14); + Common::strlcat(szMovieFile, BMOVIE_EXTENSION, 14); assert(strlen(szMovieFile) <= 12); diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index 2fde6e788a..c44f1f4ef3 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -235,7 +235,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; - strcpy(tempFilename, fileDesc->fileName); + Common::strlcpy(tempFilename, fileDesc->fileName, 50); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { @@ -275,7 +275,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; - strcpy(tempFilename, fileDesc->fileName); + Common::strlcpy(tempFilename, fileDesc->fileName, 50); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp index a84dad942c..b5d090ec15 100644 --- a/engines/tinsel/dialogs.cpp +++ b/engines/tinsel/dialogs.cpp @@ -1671,10 +1671,10 @@ static void Select(int i, bool force) { #else // Current description with cursor appended if (cd.box[i].boxText != NULL) { - strcpy(g_sedit, cd.box[i].boxText); - strcat(g_sedit, sCursor); + Common::strlcpy(g_sedit, cd.box[i].boxText, SG_DESC_LEN+2); + Common::strlcat(g_sedit, sCursor, SG_DESC_LEN+2); } else { - strcpy(g_sedit, sCursor); + Common::strlcpy(g_sedit, sCursor, SG_DESC_LEN+2); } #endif @@ -3676,13 +3676,13 @@ extern void HideConversation(bool bHide) { ConstructInventory(FULL); else { // Move it all back on-screen - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiAdjustXY(g_objArray[i], -2 * SCREEN_WIDTH, 0); } // Don't flash if items changed. If they have, will be redrawn anyway. if (TinselV2 || !g_ItemsChanged) { - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiAdjustXY(g_iconArray[i], -2*SCREEN_WIDTH, 0); } } @@ -3739,10 +3739,10 @@ extern void HideConversation(bool bHide) { deltay = g_InvD[INV_CONV].inventoryY - deltay; // Move it all - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], x - center, deltay); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], x - center, deltay); } g_InvD[INV_CONV].inventoryX += x - center; @@ -3771,10 +3771,10 @@ extern void HideConversation(bool bHide) { y = 0; if (x || y) { - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], x, y); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], x, y); } g_InvD[INV_CONV].inventoryX += x; @@ -3786,10 +3786,10 @@ extern void HideConversation(bool bHide) { */ if (MultiLowest(g_RectObject) > SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) { y = (SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) - MultiLowest(g_RectObject); - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], 0, y); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], 0, y); } g_InvD[INV_CONV].inventoryY += y; @@ -4617,9 +4617,9 @@ extern void Xmovement(int x) { GetAniPosition(g_objArray[0], &g_InvD[g_ino].inventoryX, &aniY); g_InvD[g_ino].inventoryX +=x; MultiSetAniX(g_objArray[0], g_InvD[g_ino].inventoryX); - for (i = 1; g_objArray[i] && i < MAX_WCOMP; i++) + for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++) MultiMoveRelXY(g_objArray[i], x, 0); - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) MultiMoveRelXY(g_iconArray[i], x, 0); break; @@ -4665,9 +4665,9 @@ extern void Ymovement(int y) { GetAniPosition(g_objArray[0], &aniX, &g_InvD[g_ino].inventoryY); g_InvD[g_ino].inventoryY +=y; MultiSetAniY(g_objArray[0], g_InvD[g_ino].inventoryY); - for (i = 1; g_objArray[i] && i < MAX_WCOMP; i++) + for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++) MultiMoveRelXY(g_objArray[i], 0, y); - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) MultiMoveRelXY(g_iconArray[i], 0, y); break; diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index 62d244e449..9ffd477c4a 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -258,7 +258,7 @@ void LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) { } void SetCdPlaySceneDetails(int fileNum, const char *fileName) { - strcpy(g_szCdPlayFile, fileName); + Common::strlcpy(g_szCdPlayFile, fileName, 100); } void SetCdPlayHandle(int fileNum) { diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index 88cd80b78a..226cbb51c0 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -563,7 +563,7 @@ static void DoSave() { while (1) { Common::String fname = _vm->getSavegameFilename(ano); - strcpy(tmpName, fname.c_str()); + Common::strlcpy(tmpName, fname.c_str(), FNAMELEN); for (i = 0; i < g_numSfiles; i++) if (!strcmp(g_savedFiles[i].name, tmpName)) diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h index c83bc80ead..9e4ce61b04 100644 --- a/engines/tinsel/tinsel.h +++ b/engines/tinsel/tinsel.h @@ -20,8 +20,8 @@ * */ -#ifndef TINSEL_H -#define TINSEL_H +#ifndef TINSEL_TINSEL_H +#define TINSEL_TINSEL_H #include "common/scummsys.h" #include "common/system.h" @@ -251,4 +251,4 @@ void CdHasChanged(); } // End of namespace Tinsel -#endif /* TINSEL_H */ +#endif /* TINSEL_TINSEL_H */ diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index 7a04f6e8da..ece82f4a1a 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -20,8 +20,8 @@ * */ -#ifndef TOLTECS_H -#define TOLTECS_H +#ifndef TOLTECS_TOLTECS_H +#define TOLTECS_TOLTECS_H #include "common/scummsys.h" #include "common/endian.h" @@ -231,4 +231,4 @@ public: } // End of namespace Toltecs -#endif /* TOLTECS_H */ +#endif /* TOLTECS_TOLTECS_H */ diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp index 076f1ee91c..27145d7c4b 100644 --- a/engines/tony/gfxcore.cpp +++ b/engines/tony/gfxcore.cpp @@ -1767,13 +1767,6 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 6; b /= 6; - if (r > 0x1f) - r = 0x1f; - if (g > 0x3f) - g = 0x3f; - if (b > 0x1f) - b = 0x1f; - mybuf[0] = (r << 11) | (g << 5) | b; } } diff --git a/engines/tony/tony.h b/engines/tony/tony.h index 40bace8db8..fc47e1a1bf 100644 --- a/engines/tony/tony.h +++ b/engines/tony/tony.h @@ -20,8 +20,8 @@ * */ -#ifndef TONY_H -#define TONY_H +#ifndef TONY_TONY_H +#define TONY_TONY_H #include "common/scummsys.h" #include "common/system.h" @@ -242,4 +242,4 @@ extern TonyEngine *g_vm; } // End of namespace Tony -#endif /* TONY_H */ +#endif /* TONY_TONY_H */ diff --git a/engines/touche/touche.h b/engines/touche/touche.h index 33c415d9dc..c76532b302 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -20,8 +20,8 @@ * */ -#ifndef TOUCHE_ENGINE_H -#define TOUCHE_ENGINE_H +#ifndef TOUCHE_TOUCHE_H +#define TOUCHE_TOUCHE_H #include "common/array.h" #include "common/endian.h" diff --git a/engines/tsage/tsage.h b/engines/tsage/tsage.h index dd077e526f..ca29d68243 100644 --- a/engines/tsage/tsage.h +++ b/engines/tsage/tsage.h @@ -20,8 +20,8 @@ * */ -#ifndef TSAGE_H -#define TSAGE_H +#ifndef TSAGE_TSAGE_H +#define TSAGE_TSAGE_H #include "engines/engine.h" #include "gui/debugger.h" diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h index 2ab94dedbc..1be4682508 100644 --- a/engines/tucker/tucker.h +++ b/engines/tucker/tucker.h @@ -20,8 +20,8 @@ * */ -#ifndef TUCKER_ENGINE_H -#define TUCKER_ENGINE_H +#ifndef TUCKER_TUCKER_H +#define TUCKER_TUCKER_H #include "common/file.h" #include "common/util.h" diff --git a/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp index 9f3cdeaaa3..34341d1db2 100644 --- a/engines/wintermute/ui/ui_window.cpp +++ b/engines/wintermute/ui/ui_window.cpp @@ -139,13 +139,12 @@ bool UIWindow::display(int offsetX, int offsetY) { _shieldButton->setListener(this, _shieldButton, 0); _shieldButton->_parent = this; } - if (_shieldButton) { - _shieldButton->_posX = _shieldButton->_posY = 0; - _shieldButton->setWidth(_gameRef->_renderer->getWidth()); - _shieldButton->setHeight(_gameRef->_renderer->getHeight()); - _shieldButton->display(); - } + _shieldButton->_posX = _shieldButton->_posY = 0; + _shieldButton->setWidth(_gameRef->_renderer->getWidth()); + _shieldButton->setHeight(_gameRef->_renderer->getHeight()); + + _shieldButton->display(); } if (!_visible) { diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h index f8f5fc7deb..f61a809b22 100644 --- a/engines/wintermute/wintermute.h +++ b/engines/wintermute/wintermute.h @@ -20,8 +20,8 @@ * */ -#ifndef WINTERMUTE_H -#define WINTERMUTE_H +#ifndef WINTERMUTE_WINTERMUTE_H +#define WINTERMUTE_WINTERMUTE_H #include "engines/engine.h" #include "engines/advancedDetector.h" diff --git a/gui/credits.h b/gui/credits.h index d1d3bb2147..79f4516370 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -58,6 +58,7 @@ static const char *credits[] = { "C1""AGI", "C0""Stuart George", "C0""Matthew Hoops", +"C2""(retired)", "C0""Filippos Karapetis", "C0""Martin Kiewitz", "C0""Pawel Kolodziejski", @@ -173,6 +174,7 @@ static const char *credits[] = { "", "C1""Lastexpress", "C0""Matthew Hoops", +"C2""(retired)", "C0""Jordi Vilalta Prat", "C0""Julien Templier", "", @@ -192,6 +194,7 @@ static const char *credits[] = { "C1""Mohawk", "C0""Bastien Bouclet", "C0""Matthew Hoops", +"C2""(retired)", "C0""Filippos Karapetis", "C0""Alyssa Milburn", "C0""Eugene Sandulenko", @@ -211,6 +214,7 @@ static const char *credits[] = { "", "C1""Pegasus", "C0""Matthew Hoops", +"C2""(retired)", "", "C1""Queen", "C0""David Eriksson", @@ -343,6 +347,7 @@ static const char *credits[] = { "C1""Android", "C0""Andre Heider", "C0""Angus Lees", +"C0""Lubomyr Lisen", "", "C1""Dreamcast", "C0""Marcus Comstedt", @@ -366,6 +371,9 @@ static const char *credits[] = { "C2""(retired)", "C0""Tarek Soliman", "", +"C1""Nintendo 3DS", +"C0""Thomas Edvalson", +"", "C1""Nintendo 64", "C0""Fabio Battaglia", "", @@ -477,7 +485,7 @@ static const char *credits[] = { "C0""Joachim Eberhard", "C2""Numerous contributions to documentation (retired)", "C0""Matthew Hoops", -"C2""Wiki editor", +"C2""Numerous contributions to documentation (retired)", "", "C1""Retired Team Members", "C0""Chris Apers", diff --git a/gui/updates-dialog.cpp b/gui/updates-dialog.cpp index 960b3f397a..b82ecc0402 100644 --- a/gui/updates-dialog.cpp +++ b/gui/updates-dialog.cpp @@ -76,13 +76,13 @@ UpdatesDialog::UpdatesDialog() : Dialog(30, 20, 260, 124) { _y = (screenH - _h) / 2; // Each line is represented by one static text item. - int y = 10; - for (int i = 0; i < lines.size(); i++) { + uint y = 10; + for (uint i = 0; i < lines.size(); i++) { new StaticTextWidget(this, 10, y, maxlineWidth, kLineHeight, lines[i], Graphics::kTextAlignCenter); y += kLineHeight; } - for (int i = 0; i < lines2.size(); i++) { + for (uint i = 0; i < lines2.size(); i++) { new StaticTextWidget(this, 10, y, maxlineWidth2, kLineHeight, lines2[i], Graphics::kTextAlignCenter, 0, ThemeEngine::kFontStyleTooltip); y += kLineHeight; diff --git a/gui/widget.cpp b/gui/widget.cpp index 03540f7b91..dda44604c8 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -265,9 +265,17 @@ void StaticTextWidget::setLabel(const Common::String &label) { } void StaticTextWidget::setAlign(Graphics::TextAlign align) { - _align = align; - // TODO: We should automatically redraw when the alignment is changed. - // See setLabel() for more insights. + if (_align != align){ + _align = align; + + // same as setLabel() actually, the text + // would be redrawn on top of the old one so + // we add the CLEARBG flag + setFlags(WIDGET_CLEARBG); + draw(); + clearFlags(WIDGET_CLEARBG); + } + } diff --git a/image/codecs/bmp_raw.cpp b/image/codecs/bmp_raw.cpp index 83aedc84e6..b47d5a178d 100644 --- a/image/codecs/bmp_raw.cpp +++ b/image/codecs/bmp_raw.cpp @@ -48,7 +48,24 @@ const Graphics::Surface *BitmapRawDecoder::decodeFrame(Common::SeekableReadStrea int srcPitch = _width * (_bitsPerPixel >> 3); const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0; - if (_bitsPerPixel == 8) { + if (_bitsPerPixel == 4) { + for (int i = 0; i < _height; i++) { + byte *dst = (byte *)_surface->getBasePtr(0, _height - i - 1); + for (int j = 0; j < _width; j++) { + byte color = stream.readByte(); + + *dst++ = (color & 0xf0) >> 4; + j++; + + if (j ==_width) + break; + + *dst++ = color & 0x0f; + } + + stream.skip(extraDataLength); + } + } else if (_bitsPerPixel == 8) { byte *dst = (byte *)_surface->getPixels(); for (int i = 0; i < _height; i++) { @@ -100,6 +117,7 @@ const Graphics::Surface *BitmapRawDecoder::decodeFrame(Common::SeekableReadStrea Graphics::PixelFormat BitmapRawDecoder::getPixelFormat() const { switch (_bitsPerPixel) { + case 4: case 8: return Graphics::PixelFormat::createFormatCLUT8(); case 24: diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp index 398e9c562c..910fcc18cb 100644 --- a/image/codecs/codec.cpp +++ b/image/codecs/codec.cpp @@ -34,6 +34,7 @@ #include "image/codecs/mpeg.h" #include "image/codecs/msvideo1.h" #include "image/codecs/msrle.h" +#include "image/codecs/msrle4.h" #include "image/codecs/qtrle.h" #include "image/codecs/rpza.h" #include "image/codecs/smc.h" @@ -198,6 +199,8 @@ Codec *createBitmapCodec(uint32 tag, int width, int height, int bitsPerPixel) { return new BitmapRawDecoder(width, height, bitsPerPixel); case SWAP_CONSTANT_32(1): return new MSRLEDecoder(width, height, bitsPerPixel); + case SWAP_CONSTANT_32(2): + return new MSRLE4Decoder(width, height, bitsPerPixel); case MKTAG('C','R','A','M'): case MKTAG('m','s','v','c'): case MKTAG('W','H','A','M'): diff --git a/image/codecs/msrle4.cpp b/image/codecs/msrle4.cpp new file mode 100644 index 0000000000..cd9a8717bc --- /dev/null +++ b/image/codecs/msrle4.cpp @@ -0,0 +1,140 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// Based off ffmpeg's msrledec.c +#include "common/debug.h" +#include "image/codecs/msrle4.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/util.h" +namespace Image { + +MSRLE4Decoder::MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel) { + _surface = new Graphics::Surface(); + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + _bitsPerPixel = bitsPerPixel; +} + +MSRLE4Decoder::~MSRLE4Decoder() { + _surface->free(); + delete _surface; +} + +const Graphics::Surface *MSRLE4Decoder::decodeFrame(Common::SeekableReadStream &stream) { + if (_bitsPerPixel == 4) { + decode4(stream); + } else + error("Unhandled %d bit Microsoft RLE encoding", _bitsPerPixel); + + return _surface; +} + +void MSRLE4Decoder::decode4(Common::SeekableReadStream &stream) { + int x = 0; + int y = _surface->h - 1; + + byte *output = (byte *)_surface->getBasePtr(x, y); + byte *output_end = (byte *)_surface->getBasePtr(_surface->w, y); + + while (!stream.eos()) { + byte count = stream.readByte(); + + if (count == 0) { + byte value = stream.readByte(); + + if (value == 0) { + // End of line + + x = 0; + y--; + output = (byte *)_surface->getBasePtr(x, y); + } else if (value == 1) { + // End of image + + return; + } else if (value == 2) { + // Skip + + count = stream.readByte(); + value = stream.readByte(); + + x += count; + y -= value; + + if (y < 0) { + warning("MS RLE Codec: Skip beyond picture bounds"); + return; + } + + output = (byte *)_surface->getBasePtr(x, y);; + + } else { + // Copy data + + int odd_pixel = value & 1; + int rle_code = (value + 1) / 2; + int extra_byte = rle_code & 0x01; + + if (output + value > output_end) { + stream.skip(rle_code + extra_byte); + continue; + } + + for (int i = 0; i < rle_code; i++) { + byte color = stream.readByte(); + *output++ = (color & 0xf0) >> 4; + if (i + 1 == rle_code && odd_pixel) { + break; + } + *output++ = color & 0x0f; + } + + if (extra_byte) + stream.skip(1); + + x += value; + } + + } else { + // Run data + + if (output + count > output_end) + continue; + + byte color = stream.readByte(); + + for (int i = 0; i < count; i++, x++) { + *output++ = (color & 0xf0) >> 4; + i++; + x++; + if (i == count) + break; + *output++ = color & 0x0f; + } + } + + } + + warning("MS RLE Codec: No end-of-picture code"); +} + +} // End of namespace Image diff --git a/image/codecs/msrle4.h b/image/codecs/msrle4.h new file mode 100644 index 0000000000..26f52b651d --- /dev/null +++ b/image/codecs/msrle4.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef IMAGE_CODECS_MSRLE4_H +#define IMAGE_CODECS_MSRLE4_H + +#include "image/codecs/codec.h" + +namespace Image { + +/** + * Microsoft Run-Length Encoding decoder. + * + * Used by BMP/AVI. + */ +class MSRLE4Decoder : public Codec { +public: + MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel); + ~MSRLE4Decoder(); + + const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + +private: + byte _bitsPerPixel; + + Graphics::Surface *_surface; + + void decode4(Common::SeekableReadStream &stream); +}; + +} // End of namespace Image + +#endif diff --git a/image/module.mk b/image/module.mk index fdf52ec09f..3742fb909f 100644 --- a/image/module.mk +++ b/image/module.mk @@ -15,6 +15,7 @@ MODULE_OBJS := \ codecs/indeo3.o \ codecs/mjpeg.o \ codecs/msrle.o \ + codecs/msrle4.o \ codecs/msvideo1.o \ codecs/qtrle.o \ codecs/rpza.o \ diff --git a/po/be_BY.po b/po/be_BY.po index 20bc062b5e..84a4435946 100644 --- a/po/be_BY.po +++ b/po/be_BY.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.8.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-04-07 08:55+0100\n" -"PO-Revision-Date: 2016-02-21 23:32+0300\n" +"PO-Revision-Date: 2016-05-22 17:05+0300\n" "Last-Translator: Ivan Lukyanov <greencis@mail.ru>\n" "Language-Team: Ivan Lukyanov <greencis@mail.ru>\n" "Language: Belarusian\n" @@ -1054,15 +1054,15 @@ msgstr "Мова графічнага інтэрфейсу ScummVM" #: gui/options.cpp:1235 msgid "Update check:" -msgstr "" +msgstr "Правяраць абнаўленні:" #: gui/options.cpp:1235 msgid "How often to check ScummVM updates" -msgstr "" +msgstr "Як часта правяраць абнаўленні ScummVM" #: gui/options.cpp:1247 msgid "Check now" -msgstr "" +msgstr "Праверыць цяпер" #: gui/options.cpp:1382 msgid "You have to restart ScummVM before your changes will take effect." @@ -1264,19 +1264,22 @@ msgid "" "\n" "Would you like to enable this feature?" msgstr "" +"ScummVM стаў падтрымліваць аўтаматычную праверку\n" +"абнаўленняў, якая патрабуе доступу ў Інтэрнэт.\n" +"\n" +"Ці жадаеце вы ўключыць гэту опцыю?" #: gui/updates-dialog.cpp:55 msgid "(You can always enable it in the options dialog on the Misc tab)" -msgstr "" +msgstr "(Вы заўсёды можаце ўключыць яе ў наладах на закладцы \"Рознае\")" #: gui/updates-dialog.cpp:92 -#, fuzzy msgid "Check for updates automatically" -msgstr "Правяраю абнаўленні..." +msgstr "Аўтаматычна правяраць абнаўленні" #: gui/updates-dialog.cpp:111 msgid "Proceed" -msgstr "" +msgstr "Працягнуць" #: gui/widget.cpp:327 gui/widget.cpp:329 gui/widget.cpp:335 gui/widget.cpp:337 msgid "Clear value" @@ -1407,20 +1410,19 @@ msgstr "Hercules Бурштынавы" #: common/updates.cpp:58 msgid "Daily" -msgstr "" +msgstr "Штодзень" #: common/updates.cpp:60 msgid "Weekly" -msgstr "" +msgstr "Штотыдзень" #: common/updates.cpp:62 msgid "Monthly" -msgstr "" +msgstr "Штомесяц" #: common/updates.cpp:64 -#, fuzzy msgid "<Bad value>" -msgstr "Ачысціць значэнне" +msgstr "<Хібнае значэнне>" #: engines/advancedDetector.cpp:334 #, c-format @@ -2404,23 +2406,26 @@ msgstr "" "меню гульні." #: engines/agi/detection.cpp:177 -#, fuzzy msgid "Use Hercules hires font" -msgstr "Hercules Зялёны" +msgstr "Выкарыстоўваць шрыфт Hercules" #: engines/agi/detection.cpp:178 msgid "Uses Hercules hires font, when font file is available." msgstr "" +"Выкарыстоўвае шрыфт высокага адрознення Hercules, калі даступны файл са " +"шрыфтам." #: engines/agi/detection.cpp:187 msgid "Pause when entering commands" -msgstr "" +msgstr "Паўза падчас уводу каманд" #: engines/agi/detection.cpp:188 msgid "" "Shows a command prompt window and pauses the game (like in SCI) instead of a " "real-time prompt." msgstr "" +"Паказвае акно з радком уводу каманды і ставіць гульню на паўзу (як у SCI) " +"замест уводу ў рэальным часе." #: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 #: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 @@ -2712,11 +2717,11 @@ msgstr "" #: engines/mohawk/detection.cpp:168 msgid "Play the Myst fly by movie" -msgstr "" +msgstr "Прайграваць ролік пралёту над Myst" #: engines/mohawk/detection.cpp:169 msgid "The Myst fly by movie was not played by the original engine." -msgstr "" +msgstr "Ролік пралёту над Myst не прайграваўся арыгінальным рухавічком." #. I18N: Option for fast scene switching #: engines/mohawk/dialogs.cpp:96 engines/mohawk/dialogs.cpp:239 @@ -2733,14 +2738,12 @@ msgid "~D~rop Page" msgstr "~В~ыкінуць старонку" #: engines/mohawk/dialogs.cpp:103 -#, fuzzy msgid "Show ~M~ap" -msgstr "П~а~казаць карту" +msgstr "Паказаць ~к~арту" #: engines/mohawk/dialogs.cpp:109 -#, fuzzy msgid "Main Men~u~" -msgstr "~Г~алоўнае меню" +msgstr "Галоўнае мен~ю~" #: engines/mohawk/dialogs.cpp:240 msgid "~W~ater Effect Enabled" @@ -2933,13 +2936,12 @@ msgstr "" "Выкарыстоўваць альтэрнатыўны набор срэбных курсораў замест звычайных залатых" #: engines/scumm/detection.cpp:1335 -#, fuzzy msgid "Show Object Line" -msgstr "Паказваць назвы аб'ектаў" +msgstr "Паказваць радок аб'ектаў" #: engines/scumm/detection.cpp:1336 msgid "Show the names of objects at the bottom of the screen" -msgstr "" +msgstr "Паказваць назвы аб'ектаў унізе экрана" #: engines/scumm/dialogs.cpp:176 #, c-format @@ -3800,12 +3802,14 @@ msgstr "Паказвае назвы аб'ектаў пры навядзенні курсора мышы" #: engines/sword25/detection.cpp:46 msgid "Use English speech" -msgstr "" +msgstr "Выкарыстоўваць англійскую агучку" #: engines/sword25/detection.cpp:47 msgid "" "Use English speech instead of German for every language other than German" msgstr "" +"Выкарыстоўваць англійскую агучку замест нямецкай для ўсіх моў, акрамя " +"нямецкай" #: engines/teenagent/resources.cpp:95 msgid "" diff --git a/po/ru_RU.po b/po/ru_RU.po index 514d094e64..339bc7769b 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.8.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-04-07 08:55+0100\n" -"PO-Revision-Date: 2016-02-21 23:32+0300\n" +"PO-Revision-Date: 2016-05-22 17:04+0300\n" "Last-Translator: Eugene Sandulenko <sev@scummvm.org>\n" "Language-Team: Russian\n" "Language: Russian\n" @@ -1267,17 +1267,17 @@ msgid "" "Would you like to enable this feature?" msgstr "" "ScummVM стал поддерживать автоматическую проверку\n" -"обновлений, которая требует доступа в Интернет\n" +"обновлений, которая требует доступа в Интернет.\n" "\n" -"Хотели бы вы включить эту опцию?" +"Вы хотите включить эту опцию?" #: gui/updates-dialog.cpp:55 msgid "(You can always enable it in the options dialog on the Misc tab)" -msgstr "(Вы можете всегда её включыть в Опциях на закладку Другое)" +msgstr "(Вы всегда можете включить её в настройках на закладке \"Разное\")" #: gui/updates-dialog.cpp:92 msgid "Check for updates automatically" -msgstr "Автоматически проверить обновления" +msgstr "Автоматически проверять обновления" #: gui/updates-dialog.cpp:111 msgid "Proceed" @@ -2424,7 +2424,7 @@ msgid "" "real-time prompt." msgstr "" "Показывает окно со строкой ввода команды и ставит игру на паузу (как в SCI) " -"вместоввода в реальном времени." +"вместо ввода в реальном времени." #: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 #: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 @@ -2739,11 +2739,11 @@ msgstr "~В~ыбросить страницу" #: engines/mohawk/dialogs.cpp:103 msgid "Show ~M~ap" -msgstr "П~о~казать карту" +msgstr "Показать ~к~арту" #: engines/mohawk/dialogs.cpp:109 msgid "Main Men~u~" -msgstr "~Г~лавное меню" +msgstr "Главное мен~ю~" #: engines/mohawk/dialogs.cpp:240 msgid "~W~ater Effect Enabled" @@ -2939,7 +2939,7 @@ msgstr "" #: engines/scumm/detection.cpp:1335 msgid "Show Object Line" -msgstr "Показывать линии объектов" +msgstr "Показывать строку объектов" #: engines/scumm/detection.cpp:1336 msgid "Show the names of objects at the bottom of the screen" @@ -3808,7 +3808,7 @@ msgstr "Использовать английскую озвучку" msgid "" "Use English speech instead of German for every language other than German" msgstr "" -"Использовать английскую озвучку вместо немецкой для всех языков кроме " +"Использовать английскую озвучку вместо немецкой для всех языков, кроме " "немецкого" #: engines/teenagent/resources.cpp:95 diff --git a/test/common/algorithm.h b/test/common/algorithm.h index ccf6469f67..eeed59d821 100644 --- a/test/common/algorithm.h +++ b/test/common/algorithm.h @@ -25,6 +25,34 @@ class AlgorithmTestSuite : public CxxTest::TestSuite { return true; } + /** + * Auxiliary function to check the equality of two generic collections (A and B), from one_first to one_last. + * + * @note: It assumes that other has at least (one_last - one-first) lenght, starting from other_first. + * + * @param one_first: The first element of the first collection to be compared. + * @param one_last: The last element of the first collection to be compared. + * @param other_first: The first element of the collection to be compared. + * @return true if, for each index i in [one_first, one_last), A[i] == B[i], false otherwise. + */ + template<typename It> + bool checkEqual(It one_first, It one_last, It other_first) { + if (one_first == one_last) + return true; + + // Check whether two containers have the same items in the same order, + // starting from some iterators one_first and other_first + // + // It iterates through the containers, comparing the elements one by one. + // If it finds a discrepancy, it returns false. Otherwise, it returns true. + + for (; one_first != one_last; ++one_first, ++other_first) + if (*one_first != *other_first) + return false; + + return true; + } + struct Item { int value; Item(int v) : value(v) {} @@ -97,4 +125,32 @@ public: Common::sort(list.begin(), list.end()); TS_ASSERT_EQUALS(checkSort(list.begin(), list.end(), Common::Less<Item>()), true); } + + void test_string_replace() { + + Common::String original = "Hello World"; + Common::String expected = "Hells Wsrld"; + + Common::replace(original.begin(), original.end(), 'o', 's'); + + TS_ASSERT_EQUALS(original, expected); + } + + void test_container_replace() { + + Common::List<int> original; + Common::List<int> expected; + for (int i = 0; i < 6; ++i) { + original.push_back(i); + if (i == 3) { + expected.push_back(5); + } else { + expected.push_back(i); + } + } + + Common::replace(original.begin(), original.end(), 3, 5); + + TS_ASSERT_EQUALS(checkEqual(original.begin(), original.end(), expected.begin()), true); + } }; diff --git a/test/common/array.h b/test/common/array.h index 64354abc00..7473398058 100644 --- a/test/common/array.h +++ b/test/common/array.h @@ -354,3 +354,44 @@ class ArrayTestSuite : public CxxTest::TestSuite } }; + +struct ListElement { + int value; + + ListElement(int v) : value(v) {} +}; + +static int compareInts(const void *a, const void *b) { + return ((ListElement *)a)->value - ((ListElement *)a)->value; +} + +class SortedArrayTestSuite : public CxxTest::TestSuite { +public: + void test_insert() { + Common::SortedArray<ListElement *> container(compareInts); + Common::SortedArray<ListElement *>::iterator iter; + + // Fill the container with some random data + container.insert(new ListElement(1)); + return; + + container.insert(new ListElement(7)); + container.insert(new ListElement(8)); + container.insert(new ListElement(3)); + container.insert(new ListElement(5)); + container.insert(new ListElement(4)); + container.insert(new ListElement(9)); + container.insert(new ListElement(2)); + container.insert(new ListElement(6)); + + // Verify contents are correct + iter = container.begin(); + + for (int i = 1; i < 10; i++) { + TS_ASSERT_EQUALS((*iter)->value, i); + ++iter; + TS_ASSERT_DIFFERS(iter, container.end()); + } + } + +}; diff --git a/test/common/str.h b/test/common/str.h index 3ab5d828c1..461b26088a 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -419,4 +419,78 @@ class StringTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(scumm_strnicmp("abCd", "ABCde", 4), 0); TS_ASSERT_LESS_THAN(scumm_strnicmp("abCd", "ABCde", 5), 0); } + + void test_replace() { + // Tests created with the results of the STL std::string class + + // -------------------------- + // Tests without displacement + // -------------------------- + Common::String testString = Common::String("This is the original string."); + + // Positions and sizes as parameters, string as replacement + testString.replace(12, 8, Common::String("newnewne")); + TS_ASSERT_EQUALS(testString, Common::String("This is the newnewne string.")); + + // The same but with char* + testString.replace(0, 4, "That"); + TS_ASSERT_EQUALS(testString, Common::String("That is the newnewne string.")); + + // Using iterators (also a terribly useless program as a test). + testString.replace(testString.begin(), testString.end(), "That is the supernew string."); + TS_ASSERT_EQUALS(testString, Common::String("That is the supernew string.")); + + // With sub strings of character arrays. + testString.replace(21, 6, "That phrase is new.", 5, 6); + TS_ASSERT_EQUALS(testString, Common::String("That is the supernew phrase.")); + + // Now with substrings. + testString.replace(12, 2, Common::String("That hy is new."), 5, 2); + TS_ASSERT_EQUALS(testString, Common::String("That is the hypernew phrase.")); + + // -------------------------- + // Tests with displacement + // -------------------------- + testString = Common::String("Hello World"); + + // Positions and sizes as parameters, string as replacement + testString.replace(6, 5, Common::String("friends")); + TS_ASSERT_EQUALS(testString, Common::String("Hello friends")); + + // The same but with char* + testString.replace(0, 5, "Good"); + TS_ASSERT_EQUALS(testString, Common::String("Good friends")); + + // Using iterators (also a terribly useless program as a test) + testString.replace(testString.begin() + 4, testString.begin() + 5, " coffee "); + TS_ASSERT_EQUALS(testString, Common::String("Good coffee friends")); + + // With sub strings of character arrays + testString.replace(4, 0, "Lorem ipsum expresso dolor sit amet", 11, 9); + TS_ASSERT_EQUALS(testString, Common::String("Good expresso coffee friends")); + + // Now with substrings + testString.replace(5, 9, Common::String("Displaced ristretto string"), 10, 10); + TS_ASSERT_EQUALS(testString, Common::String("Good ristretto coffee friends")); + + // ----------------------- + // Deep copy compliance + // ----------------------- + + // Makes a deep copy without changing the length of the original + Common::String s1 = "TestTestTestTestTestTestTestTestTestTestTest"; + Common::String s2(s1); + TS_ASSERT_EQUALS(s1, "TestTestTestTestTestTestTestTestTestTestTest"); + TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest"); + s1.replace(0, 4, "TEST"); + TS_ASSERT_EQUALS(s1, "TESTTestTestTestTestTestTestTestTestTestTest"); + TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest"); + + // Makes a deep copy when we shorten the string + Common::String s3 = "TestTestTestTestTestTestTestTestTestTestTest"; + Common::String s4(s3); + s3.replace(0, 32, ""); + TS_ASSERT_EQUALS(s3, "TestTestTest"); + TS_ASSERT_EQUALS(s4, "TestTestTestTestTestTestTestTestTestTestTest"); + } }; diff --git a/test/module.mk b/test/module.mk index 11ee6bd200..e591854ace 100644 --- a/test/module.mk +++ b/test/module.mk @@ -26,6 +26,7 @@ endif #TEST_LDFLAGS += -L/usr/X11R6/lib -lX11 +test: CXXFLAGS += -DCOMPILING_TESTS=1 test: test/runner ./test/runner test/runner: test/runner.cpp $(TEST_LIBS) |